forked from npshub/mantid
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TimeSplitter.cpp
268 lines (234 loc) · 9.16 KB
/
TimeSplitter.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidKernel/TimeSplitter.h"
namespace Mantid {
using namespace Types::Core;
namespace Kernel {
/// Default constructor
SplittingInterval::SplittingInterval() : m_start(), m_stop(), m_index(-1) {}
/// Constructor using DateAndTime
SplittingInterval::SplittingInterval(const Types::Core::DateAndTime &start, const Types::Core::DateAndTime &stop,
const int index)
: m_start(start), m_stop(stop), m_index(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));
}
/// @cond DOXYGEN_BUG
/// 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;
}
/// @endcond DOXYGEN_BUG
/// 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.empty()) || (b.empty()))
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.emplace_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;
out.reserve(a.size());
// Now we have to merge duplicate/overlapping intervals together
auto it = a.cbegin();
while (it != a.cend()) {
// 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.cend()) && (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.emplace_back(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.emplace_back(*it);
for (it = b.begin(); it != b.end(); ++it)
if (it->stop() > it->start())
temp.emplace_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.empty())) {
out.emplace_back(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.emplace_back(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.emplace_back(start, stop, 0);
}
}
return out;
}
} // namespace Kernel
} // namespace Mantid