intervals.cpp
1 /********************************************************************************
2  * FARSA Utilities Library *
3  * Copyright (C) 2007-2011 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "intervals.h"
21 #include <QRegExp>
22 
23 namespace farsa {
24 
26 {
27  SimpleInterval ret;
28 
29  // If not NULL ok is set to false (will be set to true at the end of the function
30  // if everything goes well)
31  if (ok != NULL) {
32  *ok = false;
33  }
34 
35  // The regular expression the string should match
36  QRegExp re("\\s*\\[(.+),\\s*(.+)\\]\\s*");
37  if (!re.exactMatch(str)) {
38  return SimpleInterval();
39  }
40 
41  // The string matched, now we have to convert the strings for start and end
42  bool numConv;
43  ret.start = re.cap(1).toDouble(&numConv);
44  if (!numConv) {
45  return SimpleInterval();
46  }
47  ret.end = re.cap(2).toDouble(&numConv);
48  if (!numConv) {
49  return SimpleInterval();
50  }
51 
52  // The string is a valid interval, setting ok to true if we have to
53  if (ok != NULL) {
54  *ok = true;
55  }
56  return ret;
57 }
58 
59 QString SimpleInterval::vectorOfSimpleIntervalsToString(QVector<SimpleInterval> v)
60 {
61  QString str;
62 
63  for (int i = 0; i < v.size(); i++) {
64  if (i != 0) {
65  str += ", ";
66  }
67  str += v[i];
68  }
69 
70  return str;
71 }
72 
73 QVector<SimpleInterval> SimpleInterval::vectorOfSimpleIntervalsFromString(QString s, bool* ok)
74 {
75  QVector<SimpleInterval> intervals;
76 
77  const QString str = s.simplified();
78  int curPos = 0;
79  bool conversionOk = true;
80 
81  // We look for ] and exit when there is an error or when the position of the ] is the end
82  // of the string (the string is simplified(), so there should be no character after the last ])
83  do {
84  const int lastPos = curPos + 1;
85  curPos = str.indexOf(']', lastPos);
86 
87  // No ] found but not at the end of the string, there is an error
88  if (curPos == -1) {
89  conversionOk = false;
90  break;
91  }
92 
93  // Looking for [
94  int startInterval = str.lastIndexOf('[', curPos);
95  if (startInterval == -1) {
96  // Cannot find the opening parenthesis, exiting
97  conversionOk = false;
98  break;
99  }
100 
101  // Checking that the only characters between the previous interval and the current one are
102  // one comma and spaces. Of course this check is skipped if this is the first interval
103  if ((lastPos != 1) && (str.mid(lastPos, (startInterval - lastPos)).simplified() != ",")) {
104  // Invalid format
105  conversionOk = false;
106  break;
107  }
108 
109  intervals.append(SimpleInterval::fromString(str.mid(startInterval, (curPos - startInterval + 1)), &conversionOk));
110  } while ((curPos != (str.size() - 1)) && conversionOk);
111 
112  if (ok != NULL) {
113  *ok = conversionOk;
114  }
115 
116  return (conversionOk ? intervals : QVector<SimpleInterval>());
117 }
118 
120  m_length(0.0),
121  m_intervals()
122 {
123  // We have to check if start == end or start > end
124  if (interval.start == interval.end) {
125  // Nothing to do, this is an empty interval
126  return;
127  } else if (interval.start > interval.end) {
128  SimpleInterval i1(-std::numeric_limits<real>::infinity(), interval.end);
129  SimpleInterval i2(interval.start, std::numeric_limits<real>::infinity());
130 
131  m_intervals << i1 << i2;
132  } else {
133  m_intervals << interval;
134  }
135 
136  recomputeLength();
137 }
138 
140 {
141  if (&other == this) {
142  return *this;
143  }
144 
145  m_length = other.m_length;
146  m_intervals = other.m_intervals;
147 
148  return *this;
149 }
150 
151 Intervals::operator QString() const
152 {
153  if (isEmpty()) {
154  return QString("[]");
155  }
156 
157  QString s;
158 
159  for (const_iterator it = begin(); it != end(); it++) {
160  if (it != begin()) {
161  s += ", ";
162  }
163  s += *it;
164  }
165 
166  return s;
167 }
168 
170 {
171  m_length = 0.0;
172  m_intervals.clear();
173 }
174 
175 bool Intervals::operator==(const Intervals& other) const
176 {
177  const_iterator it1 = begin();
178  const_iterator it2 = other.begin();
179  while (true) {
180  if ((it1 == end()) && (it2 == other.end())) {
181  // Both ended, if we get here the intervals are equal
182  break;
183  } else if ((it1 == end()) || (it2 == other.end())) {
184  // Different length, so intervals are different
185  return false;
186  } else if (!(it1->equals(*it2))) {
187  // Different intervals
188  return false;
189  }
190 
191  ++it1;
192  ++it2;
193  }
194 
195  return true;
196 }
197 
199 {
200  for (const_iterator it = begin(); it != end(); ++it) {
201  if ((it->start <= v) && (it->end >= v)) {
202  return true;
203  }
204  }
205 
206  return false;
207 }
208 
210 {
211  m_length = 0.0;
212  for (const_iterator it = begin(); it != end(); ++it) {
213  m_length += it->length();
214  }
215 }
216 
217 template <class OtherIterator_t>
218 void Intervals::intersect(OtherIterator_t otherBegin, OtherIterator_t otherEnd)
219 {
220  // We modify m_intervals in place, so it1 needs to be a non-const interator
221  QLinkedList<SimpleInterval>::iterator it1 = m_intervals.begin();
222  OtherIterator_t it2 = otherBegin;
223  while (it1 != m_intervals.end()) {
224  // Possible relationships between the segments represented by it1 and it2:
225  // a)
226  // it1 -----
227  // it2 ---
228  // No intersection, move it2 forward
229  //
230  // b)
231  // it1 -----
232  // it2 ---
233  // No intersection, remove current it1 and move to the next one
234  //
235  // c)
236  // it1 ----
237  // it2 ----
238  // Intersection, move both it1 and it2 forward (this condition could be handled in
239  // one of the following ones, but we explicitate it to avoid creating zero-length
240  // intervals)
241  //
242  // d)
243  // it1 -----
244  // it2 ---
245  // Intersection, split it1 in two, set it1 to the second part of the original it1
246  // and move it2 forward
247  //
248  // e)
249  // it1 -----
250  // it2 ---
251  // Intersection, restrict it1 to the intersecting part and move it1 forward
252  //
253  // f)
254  // it1 -----
255  // it2 ----------
256  // Intersection, keep the current it1 as is and move it1 forward
257  //
258  // g)
259  // it1 --------
260  // it2 ----
261  // Intersection, split it1 in three parts: remove the leftmost, keep the central one
262  // and set it1 to the rightmost; then increment it2
263  //
264  // The conditions above are written near the corresponding if branch
265  if (it2 == otherEnd) {
266  // We have to remove all intervals from it1 to the end
267  it1 = m_intervals.erase(it1, m_intervals.end());
268  } else if (it1->start >= it2->end) { // Condition a
269  ++it2;
270  } else if (it2->start >= it1->end) { // Condition b
271  it1 = m_intervals.erase(it1);
272  } else { // All other conditions
273  // There is an intersection, checking what kind
274  if ((it1->start == it2->start) && (it1->end == it2->end)) { // Condition c
275  ++it1;
276  ++it2;
277  } else if ((it1->start >= it2->start) && (it1->start <= it2->end) && (it1->end >= it2->end)) { // Condition d
278  const real origEnd = it1->end;
279  it1->end = it2->end;
280  ++it1;
281  it1 = m_intervals.insert(it1, SimpleInterval(it2->end, origEnd));
282  } else if ((it1->start <= it2->start) && (it1->end >= it1->start) && (it1->end <= it2->end)) { // Condition e
283  it1->start = it2->start;
284  ++it1;
285  } else if ((it1->start >= it2->start) && (it1->end <= it2->end)) { // Condition f
286  ++it1;
287  } else if ((it1->start <= it2->start) && (it2->end >= it2->end)) { // Condition g
288  const real origEnd = it1->end;
289  it1->start = it2->start;
290  it1->end = it2->end;
291  ++it1;
292  it1 = m_intervals.insert(it1, SimpleInterval(it2->end, origEnd));
293  } else { // We should never get here
294  qFatal("Unexpected condition in %s at line %d (segments intersection). it1->start = %f, it2->start = %f, it1->end = %f, it2->end = %f", __FILE__, __LINE__, it1->start, it2->start, it1->end, it2->end);
295  }
296  }
297  }
298 
299  // Recomputing length
300  recomputeLength();
301 }
302 
303 template <class OtherIterator_t>
304 void Intervals::unite(OtherIterator_t otherBegin, OtherIterator_t otherEnd)
305 {
306  // We modify m_intervals in place, so it1 needs to be a non-const interator
307  QLinkedList<SimpleInterval>::iterator it1 = m_intervals.begin();
308  OtherIterator_t it2 = otherBegin;
309  while (it2 != otherEnd) {
310  // Possible relationships between the segments represented by it1 and it2:
311  // a)
312  // it1 -----
313  // it2 ---
314  // No overlapping, add it2 to the list and move it2 forward
315  //
316  // b)
317  // it1 -----
318  // it2 ---
319  // No overlapping, simply move it1 forward (because the current it2 could be
320  // overlapping with the next it1; if it is not, we will fall in condition a and
321  // add the interval)
322  //
323  // c)
324  // In all other cases there is an overlap: the resulting segment has
325  // start = min(it1->start, it2->start), while for the end:
326  // - if it1->end == it2->end, the end is it1->end; then move both it1 and it2
327  // forward
328  // - if it1->end > it2->end, the end is it1->end; then move it2 forward until
329  // you get it2->end > it1->end
330  // - if it1->end < it2->end, move it1 forward until you get it1->start > it2->end;
331  // remove all it1 that do not satisfy this condition and set the end to the max
332  // between it2->end and the end of the last removed it1. Then move it1 and it2
333  // forward
334  //
335  // The conditions above are written near the corresponding if branch
336  if (it1 == m_intervals.end()) {
337  // We have to add all intervals still in it2 to the list
338  for (; it2 != otherEnd; ++it2) {
339  m_intervals.push_back(*it2);
340  }
341  } else if (it1->start > it2->end) { // Condition a
342  m_intervals.insert(it1, *it2); // it1 is not incremented
343  ++it2;
344  } else if (it2->start >= it1->end) { // Condition b
345  ++it1;
346  } else { // Condition c
347  it1->start = min(it1->start, it2->start);
348  if (it1->end == it2->end) {
349  ++it1;
350  ++it2;
351  } else if (it1->end > it2->end) {
352  // it1->end is ok as it is, moving it2 forward
353  for (; (it2 != otherEnd) && (it2->end <= it1->end); ++it2);
354  } else { // it1->end < it2->end
355  real prevIt1End = it1->end;
356  QLinkedList<SimpleInterval>::iterator origIt1 = it1;
357  ++it1;
358  while ((it1 != m_intervals.end()) && (it1->start <= it2->end)) {
359  prevIt1End = it1->end;
360 
361  it1 = m_intervals.erase(it1);
362  }
363  origIt1->end = max(prevIt1End, it2->end);
364  ++it2;
365  }
366  }
367  }
368 
369  // Recomputing length
370  recomputeLength();
371 }
372 
373 template <class OtherIterator_t>
374 void Intervals::subtract(OtherIterator_t otherBegin, OtherIterator_t otherEnd)
375 {
376  // We modify m_intervals in place, so it1 needs to be a non-const interator
377  QLinkedList<SimpleInterval>::iterator it1 = m_intervals.begin();
378  OtherIterator_t it2 = otherBegin;
379  while ((it1 != m_intervals.end()) && (it2 != otherEnd)){
380  // Possible relationships between the segments represented by it1 and it2:
381  // a)
382  // it1 -----
383  // it2 ---
384  // No intersection, move it2 forward
385  //
386  // b)
387  // it1 -----
388  // it2 ---
389  // No intersection, move it1 forward
390  //
391  // c)
392  // it1 -----
393  // it2 ----------
394  // Intersection, remove it1 and move to the next one (this case also
395  // comprises it1 equal to it2)
396  //
397  // d)
398  // it1 -----
399  // it2 ---
400  // Intersection, set it1->start to it2->end and move it2 forward
401  //
402  // e)
403  // it1 -----
404  // it2 ---
405  // Intersection, set it1->end to it2->start and move it1 forward
406  //
407  // f)
408  // it1 --------
409  // it2 ----
410  // Intersection, split it1 in three parts and remove the central one;
411  // then move it1 to the rightmost part and it2 forward
412  //
413  // The conditions above are written near the corresponding if branch
414  if (it1->start >= it2->end) { // Condition a
415  ++it2;
416  } else if (it2->start >= it1->end) { // Condition b
417  ++it1;
418  } else { // All other conditions
419  // There is an intersection, checking what kind
420  if ((it1->start >= it2->start) && (it1->end <= it2->end)) { // Condition c
421  it1 = m_intervals.erase(it1);
422  } else if ((it1->start >= it2->start) && (it1->start < it2->end) && (it1->end > it2->end)) { // Condition d
423  it1->start = it2->end;
424  ++it2;
425  } else if ((it1->start < it2->start) && (it1->end > it1->start) && (it1->end <= it2->end)) { // Condition e
426  it1->end = it2->start;
427  ++it1;
428  } else if ((it1->start < it2->start) && (it1->end > it2->end)) { // Condition f
429  const real origEnd = it1->end;
430  it1->end = it2->start;
431  ++it1;
432  it1 = m_intervals.insert(it1, SimpleInterval(it2->end, origEnd));
433  ++it2;
434  } else { // We should never get here (if we get here, try to check whether the equality is checked correctly)
435  qFatal("Unexpected condition in %s at line %d (segments subtraction). it1->start = %f, it2->start = %f, it1->end = %f, it2->end = %f", __FILE__, __LINE__, it1->start, it2->start, it1->end, it2->end);
436  }
437  }
438  }
439 
440  // Recomputing length
441  recomputeLength();
442 }
443 
444 // Explicit template instantiation of the functions above
445 template void Intervals::intersect<QLinkedList<farsa::SimpleInterval>::const_iterator>(QLinkedList<farsa::SimpleInterval>::const_iterator otherBegin, QLinkedList<farsa::SimpleInterval>::const_iterator otherEnd);
446 template void Intervals::intersect<const farsa::SimpleInterval*>(const farsa::SimpleInterval* otherBegin, const farsa::SimpleInterval* otherEnd);
447 template void Intervals::unite<QLinkedList<farsa::SimpleInterval>::const_iterator>(QLinkedList<farsa::SimpleInterval>::const_iterator otherBegin, QLinkedList<farsa::SimpleInterval>::const_iterator otherEnd);
448 template void Intervals::unite<const farsa::SimpleInterval*>(const farsa::SimpleInterval* otherBegin, const farsa::SimpleInterval* otherEnd);
449 template void Intervals::subtract<QLinkedList<farsa::SimpleInterval>::const_iterator>(QLinkedList<farsa::SimpleInterval>::const_iterator otherBegin, QLinkedList<farsa::SimpleInterval>::const_iterator otherEnd);
450 template void Intervals::subtract<const farsa::SimpleInterval*>(const farsa::SimpleInterval* otherBegin, const farsa::SimpleInterval* otherEnd);
451 
452 } // end namespace farsa
void clear()
Sets this to an empty interval.
Definition: intervals.cpp:169
QLinkedList< SimpleInterval > m_intervals
The list of simple intervals.
Definition: intervals.h:746
real m_length
The total length of intervals.
Definition: intervals.h:738
Intervals & operator=(const Intervals &other)
Copy operator.
Definition: intervals.cpp:139
A macro to deprecate functions.
FARSA_UTIL_TEMPLATE const T max(const T &t1, const U &t2)
Template for max calculation.
Definition: mathutils.h:154
bool operator==(const Intervals &other) const
Returns true if this and other are the same.
Definition: intervals.cpp:175
Intervals()
Constructor.
Definition: intervals.h:232
const_iterator begin() const
Returns a const iterator to the beginning of the list of simple intervals.
Definition: intervals.h:324
A class modelling a range of real values.
Definition: intervals.h:44
Intervals & unite(const Intervals &other)
Unites intervals in this with other.
Definition: intervals.h:477
SimpleInterval()
Constructor.
Definition: intervals.h:52
float real
Abstraction on the type of real numbers.
Definition: mathutils.h:45
QLinkedList< SimpleInterval >::const_iterator const_iterator
The const iterator on simple intervals.
Definition: intervals.h:224
real end
The ending value of the interval.
Definition: intervals.h:202
static QVector< SimpleInterval > vectorOfSimpleIntervalsFromString(QString s, bool *ok=NULL)
Converts the given string to a vector of simple intervals.
Definition: intervals.cpp:73
real start
The starting value of the interval.
Definition: intervals.h:197
The class modelling intervals of floating point values.
Definition: intervals.h:218
FARSA_UTIL_TEMPLATE const T min(const T &t1, const U &t2)
Template for min calculation.
Definition: mathutils.h:136
const_iterator end() const
Returns a const iterator to the end of the list of simple intervals.
Definition: intervals.h:349
static SimpleInterval fromString(QString str, bool *ok=NULL)
Converts the given string to a SimpleInterval.
Definition: intervals.cpp:25
bool valueIn(real v) const
Returns true if the given value belongs to these intervals.
Definition: intervals.cpp:198
void recomputeLength()
Recomputes the length of the interval.
Definition: intervals.cpp:209
static QString vectorOfSimpleIntervalsToString(QVector< SimpleInterval > v)
Converts a vector of simple intervals to string.
Definition: intervals.cpp:59
Intervals & subtract(const Intervals &other)
Subtracts intervals from other to these ones.
Definition: intervals.h:604
Intervals & intersect(const Intervals &other)
Intersects intervals in this with other.
Definition: intervals.h:407