-
Campbell, Stuart authoredCampbell, Stuart authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
TimeSplitter.cpp 9.10 KiB
#include "MantidKernel/DateAndTime.h"
#include "MantidKernel/TimeSplitter.h"
namespace Mantid {
namespace Kernel {
/// Default constructor
SplittingInterval::SplittingInterval() : m_start(), m_stop(), m_index(-1) {}
/// Constructor using DateAndTime
SplittingInterval::SplittingInterval(const DateAndTime &start,
const DateAndTime &stop, const int index)
: m_start(start), m_stop(stop), m_index(index) {}
/// Copy Constructor
SplittingInterval::SplittingInterval(const SplittingInterval &other)
: m_start(other.m_start), m_stop(other.m_stop), m_index(other.m_index) {}
/// Return the start time
DateAndTime SplittingInterval::start() const { return m_start; }
/// Return the stop time
DateAndTime SplittingInterval::stop() const { return m_stop; }
/// Returns the duration in seconds
double SplittingInterval::duration() const {
return DateAndTime::secondsFromDuration(m_stop - m_start);
}
/// Return the index (destination of this split time block)
int SplittingInterval::index() const { return m_index; }
/// Return true if the b SplittingInterval overlaps with this one.
bool SplittingInterval::overlaps(const SplittingInterval &b) const {
return ((b.m_start < this->m_stop) && (b.m_start >= this->m_start)) ||
((b.m_stop < this->m_stop) && (b.m_stop >= this->m_start)) ||
((this->m_start < b.m_stop) && (this->m_start >= b.m_start)) ||
((this->m_stop < b.m_stop) && (this->m_stop >= b.m_start));
}
/// And operator. Return the smallest time interval where both intervals are
/// TRUE.
SplittingInterval SplittingInterval::
operator&(const SplittingInterval &b) const {
SplittingInterval out(*this);
if (b.m_start > this->m_start)
out.m_start = b.m_start;
if (b.m_stop < this->m_stop)
out.m_stop = b.m_stop;
return out;
}
/// Or operator. Return the largest time interval.
SplittingInterval SplittingInterval::
operator|(const SplittingInterval &b) const {
SplittingInterval out(*this);
if (!this->overlaps(b))
throw std::invalid_argument("SplittingInterval: cannot apply the OR (|) "
"operator to non-overlapping "
"SplittingInterval's.");
if (b.m_start < this->m_start)
out.m_start = b.m_start;
if (b.m_stop > this->m_stop)
out.m_stop = b.m_stop;
return out;
}
/// Compare two splitter by the begin time
bool SplittingInterval::operator<(const SplittingInterval &b) const {
return (this->m_start < b.m_start);
}
/// Compare two splitter by the begin time
bool SplittingInterval::operator>(const SplittingInterval &b) const {
return (this->m_start > b.m_start);
}
/** Comparator for sorting lists of SplittingInterval */
bool compareSplittingInterval(const SplittingInterval &si1,
const SplittingInterval &si2) {
return (si1.start() < si2.start());
}
//------------------------------------------------------------------------------------------------
/** Return true if the TimeSplitterType provided is a filter,
* meaning that it only has an output index of 0.
*/
bool isFilter(const TimeSplitterType &a) {
int max = -1;
TimeSplitterType::const_iterator it;
for (it = a.begin(); it != a.end(); ++it)
if (it->index() > max)
max = it->index();
return (max <= 0);
}
//------------------------------------------------------------------------------------------------
/** Plus operator for TimeSplitterType.
* Combines a filter and a splitter by removing entries that are filtered out
*from the splitter.
* Also, will combine two filters together by "and"ing them
*
* @param a :: TimeSplitterType splitter OR filter
* @param b :: TimeSplitterType splitter OR filter.
* @throw std::invalid_argument if two splitters are given.
*/
TimeSplitterType operator+(const TimeSplitterType &a,
const TimeSplitterType &b) {
bool a_filter, b_filter;
a_filter = isFilter(a);
b_filter = isFilter(b);
if (a_filter && b_filter) {
return a & b;
} else if (a_filter && !b_filter) {
return b & a;
} else if (!a_filter && b_filter) {
return a & b;
} else // (!a_filter && !b_filter)
{
// Both are splitters.
throw std::invalid_argument("Cannot combine two splitters together, as the "
"output is undefined. Try splitting each "
"output workspace by b after the a split has "
"been done.");
}
}
//------------------------------------------------------------------------------------------------
/** AND operator for TimeSplitterType
* Works on Filters - combines them to only keep times where both Filters are
*TRUE.
* Works on splitter + filter if (a) is a splitter and b is a filter.
* In general, use the + operator since it will resolve the order for you.
*
* @param a :: TimeSplitterType filter or Splitter.
* @param b :: TimeSplitterType filter.
* @return the ANDed filter
*/
TimeSplitterType operator&(const TimeSplitterType &a,
const TimeSplitterType &b) {
TimeSplitterType out;
// If either is empty, then no entries in the filter (aka everything is
// removed)
if ((a.size() == 0) || (b.size() == 0))
return out;
TimeSplitterType::const_iterator ait;
TimeSplitterType::const_iterator bit;
// For now, a simple double iteration. Can be made smarter if a and b are
// sorted.
for (ait = a.begin(); ait != a.end(); ++ait) {
for (bit = b.begin(); bit != b.end(); ++bit) {
if (ait->overlaps(*bit)) {
// The & operator for SplittingInterval keeps the index of the
// left-hand-side (ait in this case)
// meaning that a has to be the splitter because the b index is
// ignored.
out.push_back(*ait & *bit);
}
}
}
return out;
}
//------------------------------------------------------------------------------------------------
/** Remove any overlap in a filter (will not work properly on a splitter)
*
* @param a :: TimeSplitterType filter.
*/
TimeSplitterType removeFilterOverlap(const TimeSplitterType &a) {
TimeSplitterType out;
// Now we have to merge duplicate/overlapping intervals together
TimeSplitterType::const_iterator it = a.begin();
while (it != a.end()) {
// All following intervals will start at or after this one
DateAndTime start = it->start();
DateAndTime stop = it->stop();
// Keep looking for the next interval where there is a gap (start > old
// stop);
while ((it != a.end()) && (it->start() <= stop)) {
// Extend the stop point (the start cannot be extended since the list is
// sorted)
if (it->stop() > stop)
stop = it->stop();
++it;
}
// We've reached a gap point. Output this merged interval and move on.
out.push_back(SplittingInterval(start, stop, 0));
}
return out;
}
//------------------------------------------------------------------------------------------------
/** OR operator for TimeSplitterType
* Only works on Filters, not splitters. Combines the splitters
* to only keep times where EITHER Filter is TRUE.
*
* @param a :: TimeSplitterType filter.
* @param b :: TimeSplitterType filter.
* @return the ORed filter
*/
TimeSplitterType operator|(const TimeSplitterType &a,
const TimeSplitterType &b) {
TimeSplitterType out;
// Concatenate the two lists
TimeSplitterType temp;
// temp.insert(temp.end(), b.begin(), b.end());
// Add the intervals, but don't add any invalid (empty) ranges
TimeSplitterType::const_iterator it;
;
for (it = a.begin(); it != a.end(); ++it)
if (it->stop() > it->start())
temp.push_back(*it);
for (it = b.begin(); it != b.end(); ++it)
if (it->stop() > it->start())
temp.push_back(*it);
// Sort by start time
std::sort(temp.begin(), temp.end(), compareSplittingInterval);
out = removeFilterOverlap(temp);
return out;
}
//------------------------------------------------------------------------------------------------
/** NOT operator for TimeSplitterType
* Only works on Filters. Returns a filter with the reversed
* time intervals as the incoming filter.
*
* @param a :: TimeSplitterType filter.
*/
TimeSplitterType operator~(const TimeSplitterType &a) {
TimeSplitterType out, temp;
// First, you must remove any overlapping intervals, otherwise the output is
// stupid.
temp = removeFilterOverlap(a);
// No entries: then make a "filter" that keeps everything
if ((temp.size() == 0)) {
out.push_back(
SplittingInterval(DateAndTime::minimum(), DateAndTime::maximum(), 0));
return out;
}
TimeSplitterType::const_iterator ait;
ait = temp.begin();
if (ait != temp.end()) {
// First entry; start at -infinite time
out.push_back(SplittingInterval(DateAndTime::minimum(), ait->start(), 0));
// Now start at the second entry
while (ait != temp.end()) {
DateAndTime start, stop;
start = ait->stop();
++ait;
if (ait == temp.end()) { // Reached the end - go to inf
stop = DateAndTime::maximum();
} else { // Stop at the start of the next entry
stop = ait->start();
}
out.push_back(SplittingInterval(start, stop, 0));
}
}
return out;
}
}
}