LevelS C++ support library  3.82
crangeset.h
Go to the documentation of this file.
1 /*
2  * This file is part of libcxxsupport.
3  *
4  * libcxxsupport is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * libcxxsupport is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with libcxxsupport; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 /*
20  * libcxxsupport is being developed at the Max-Planck-Institut fuer Astrophysik
21  * and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt
22  * (DLR).
23  */
24 
25 /*! \file crangeset.h
26  * Class for storing sets of ranges of integer numbers
27  *
28  * Copyright (C) 2015-2021 Max-Planck-Society
29  * \author Martin Reinecke
30  */
31 
32 #ifndef PLANCK_CRANGESET_H
33 #define PLANCK_CRANGESET_H
34 
35 #include <algorithm>
36 #include <vector>
37 #include <utility>
38 #include <iostream>
39 #include "datatypes.h"
40 #include "error_handling.h"
41 #include "math_utils.h"
42 
43 /*
44 compact rangeset (CRS)
45 
46 The underlying type T must be signed.
47 BUT: All "on" ranges must lie entirely in N_0.
48 
49 A nonnegative number indicates the start of an "on" range at this number.
50 A negative number indicates the start of an "off" range at the absolute value
51 of this number.
52 
53 All numbers r are sorted in a way that |r_i| < |r_{i+1}|.
54 
55 If two consecutive numbers are both nonnegative or negative, it means that the
56 interval between them contains in fact _two_ ranges: one of length 1 and the
57 other filling the remainder.
58 
59 Consequences:
60 
61 - The first number in CRS must be nonnegative
62 - numbers r_i and r_{i+1} in the CRS have the property
63  |r_{i+1}| > |r_i| + 1 (because of the special treatment of short intervals)
64 
65 Example:
66 The range set
67 [5;7[; [10;15[; [16;22[; [23;24[; [25;30[; [35;36[; [40;45[
68 
69 would be encoded as
70 5 -7 10 -15 -22 -24 -30 35 40 -45
71 */
72 
73 /*! Class for storing sets of ranges of integer numbers
74  T must be a signed integer type, but all numbers entered into the range set
75  must be nonnegative! */
76 template<typename T> class crangeset
77  {
78  public:
79  typedef std::vector<T> rtype;
80 
81  private:
82  rtype r;
83 
84  struct abscomp
85  {
86  bool operator()(const T &a, const T &b)
87  {
88  using namespace std;
89  return abs(a)<abs(b);
90  }
91  };
92 
93  class RsIter
94  {
95  private:
96  tsize idx;
97  bool extra;
98  const crangeset &ref;
99  T val;
100 
101  bool singleVal() const
102  {
103  if (idx==ref.r.size()-1)
104  return (ref.r[idx]>=0);
105  else
106  return ((ref.r[idx]^ref.r[idx+1])>=0);
107  }
108 
109  public:
110  RsIter(const crangeset &ref_) : idx(0), extra(false), ref(ref_), val(0)
111  {
112  using namespace std;
113  if (!atEnd())
114  val=abs(ref.r[0]);
115  }
116  bool atEnd() const { return idx>=ref.r.size(); }
117  T operator*() const
118  {
119  return val;
120  }
121  RsIter &operator++ ()
122  {
123  using namespace std;
124  if (extra)
125  {
126  ++idx;
127  extra=false;
128  if (!atEnd()) val=abs(ref.r[idx]);
129  }
130  else
131  {
132  if (singleVal())
133  {
134  extra=true;
135  ++val;
136  }
137  else
138  {
139  ++idx;
140  if (!atEnd()) val=abs(ref.r[idx]);
141  }
142  }
143  return *this;
144  }
145  void advance_up_to(T goal)
146  {
147  using namespace std;
148  tdiff idx2=ref.iiv(goal-1);
149  if (idx2>tdiff(idx))
150  {
151  idx=idx2;
152  extra=false;
153  val=abs(ref.r[idx]);
154  }
155  }
156  bool onoff() const
157  { return (ref.r[idx]>=0)^extra; }
158  };
159  public:
160  class IvIter
161  {
162  private:
163  RsIter rsi;
164  T b,e;
165 
166  public:
167  IvIter(const crangeset &ref_) : rsi(ref_)
168  {
169  if (rsi.atEnd()) return;
170  b=*rsi;
171  ++rsi;
172  e=*rsi;
173  }
174  bool atEnd() const { return rsi.atEnd(); }
175  T ivbegin() const { return b; }
176  T ivend() const { return e; }
177  tdiff ivlen() const { return e-b; }
178  IvIter &operator++ ()
179  {
180  ++rsi;
181  if (rsi.atEnd()) return *this;
182  b=*rsi;
183  ++rsi;
184  e=*rsi;
185  return *this;
186  }
187  };
188  class RsOutputIter
189  {
190  private:
191  T val;
192  bool val_set;
193  crangeset &ref;
194 
195  public:
196  RsOutputIter(crangeset &ref_) : val(T(0)), val_set(false), ref(ref_)
197  { ref.clear(); }
198  RsOutputIter &operator*() { return *this; }
199  RsOutputIter &operator++ () { return *this; }
200  RsOutputIter &operator++ (int) { return *this; }
201  RsOutputIter &operator= (const T &v)
202  {
203  if (val_set)
204  ref.append(val,v);
205  else
206  val=v;
207  val_set=!val_set;
208  return *this;
209  }
210  };
211  /*! Returns the index of the last number in \c r whose absolute value
212  is <= \a val
213  If \a val is smaller than all absolute values in \c r, returns -1. */
214  tdiff iiv (const T &val) const
215  {
216  return tdiff(std::upper_bound(r.begin(),r.end(),val,abscomp())
217  -r.begin())-1;
218  }
219 
220  /*! Estimate a good strategy for set operations involving two rangesets. */
221  static int strategy (tsize sza, tsize szb)
222  {
223  const double fct1 = 1.;
224  const double fct2 = 1.;
225  tsize slo = sza<szb ? sza : szb,
226  shi = sza<szb ? szb : sza;
227  double cost1 = fct1 * (sza+szb);
228  double cost2 = fct2 * slo * std::max(1,ilog2(shi));
229  return (cost1<=cost2) ? 1 : (slo==sza) ? 2 : 3;
230  }
231 
232  static crangeset generalUnion1 (const crangeset &a, const crangeset &b,
233  bool flip_a, bool flip_b)
234  {
235  crangeset res;
236  bool state_a=flip_a, state_b=flip_b, state_res=state_a||state_b;
237  RsIter ia(a), ib(b);
238  RsOutputIter io(res);
239  bool runa = !ia.atEnd(), runb = !ib.atEnd();
240  while(runa||runb)
241  {
242  T va = runa ? *ia : T(0),
243  vb = runb ? *ib : T(0);
244  bool adv_a = runa && (!runb || (va<=vb)),
245  adv_b = runb && (!runa || (vb<=va));
246  if (adv_a) { state_a=!state_a; ++ia; runa = !ia.atEnd(); }
247  if (adv_b) { state_b=!state_b; ++ib; runb = !ib.atEnd(); }
248  if ((state_a||state_b)!=state_res)
249  { *io++ = (adv_a ? va : vb); state_res = !state_res; }
250  }
251  return res;
252  }
253  static crangeset generalUnion2 (const crangeset &a, const crangeset &b,
254  bool flip_a, bool flip_b)
255  {
256  crangeset res;
257  bool state_a=flip_a, state_b=flip_b, state_res=state_a||state_b;
258  RsIter ia(a), ib(b);
259  RsOutputIter io(res);
260  bool runa = !ia.atEnd(), runb = !ib.atEnd();
261  while(runa||runb)
262  {
263  T va = runa ? *ia : T(0);
264  if (state_a && runb) // changes in b are irrelevant while state_a is true
265  {
266  if (!runa) break; // we are at the end
267  if (*ib<va)
268  {
269  ib.advance_up_to (va);
270  state_b=!(flip_b^ib.onoff());
271  }
272  }
273  T vb = runb ? *ib : T(0);
274  bool adv_a = runa && (!runb || (va<=vb)),
275  adv_b = runb && (!runa || (vb<=va));
276  if (adv_a) { state_a=!state_a; ++ia; runa = !ia.atEnd(); }
277  if (adv_b) { state_b=!state_b; ++ib; runb = !ib.atEnd(); }
278  if ((state_a||state_b)!=state_res)
279  { *io++ = (adv_a ? va : vb); state_res = !state_res; }
280  }
281  return res;
282  }
283  static crangeset generalUnion (const crangeset &a, const crangeset &b,
284  bool flip_a, bool flip_b)
285  {
286  if (a.r.empty())
287  return flip_a ? crangeset() : b;
288  if (b.r.empty())
289  return flip_b ? crangeset() : a;
290 
291  int strat = strategy (a.r.size(), b.r.size());
292  return (strat==1) ? generalUnion1(a,b,flip_a,flip_b) :
293  ((strat==2) ? generalUnion2(a,b,flip_a,flip_b)
294  : generalUnion2(b,a,flip_b,flip_a));
295  }
296  static crangeset generalXor (const crangeset &a, const crangeset &b)
297  {
298  crangeset res;
299  bool state_a=false, state_b=false, state_res=state_a||state_b;
300  RsIter ia(a), ib(b);
301  RsOutputIter io(res);
302  bool runa = !ia.atEnd(), runb = !ib.atEnd();
303  while(runa||runb)
304  {
305  T va = runa ? *ia : T(0),
306  vb = runb ? *ib : T(0);
307  bool adv_a = runa && (!runb || (va<=vb)),
308  adv_b = runb && (!runa || (vb<=va));
309  if (adv_a) { state_a=!state_a; ++ia; runa = !ia.atEnd(); }
310  if (adv_b) { state_b=!state_b; ++ib; runb = !ib.atEnd(); }
311  if ((state_a^state_b)!=state_res)
312  { *io++ = (adv_a ? va : vb); state_res = !state_res; }
313  }
314  return res;
315  }
316 
317  static bool generalAllOrNothing1 (const crangeset &a, const crangeset &b,
318  bool flip_a, bool flip_b)
319  {
320  bool state_a=flip_a, state_b=flip_b, state_res=state_a||state_b;
321  RsIter ia(a), ib(b);
322  bool runa = !ia.atEnd(), runb = !ib.atEnd();
323  while(runa||runb)
324  {
325  using namespace std;
326  T va = runa ? *ia : T(0),
327  vb = runb ? *ib : T(0);
328  bool adv_a = runa && (!runb || (va<=vb)),
329  adv_b = runb && (!runa || (vb<=va));
330  if (adv_a) { state_a=!state_a; ++ia; runa = !ia.atEnd(); }
331  if (adv_b) { state_b=!state_b; ++ib; runb = !ib.atEnd(); }
332  if ((state_a||state_b)!=state_res)
333  return false;
334  }
335  return true;
336  }
337  static bool generalAllOrNothing2 (const crangeset &a, const crangeset &b,
338  bool flip_a, bool flip_b)
339  {
340  bool state_a=flip_a, state_b=flip_b, state_res=state_a||state_b;
341  RsIter ia(a), ib(b);
342  bool runa = !ia.atEnd(), runb = !ib.atEnd();
343  while(runa||runb)
344  {
345  T va = runa ? *ia : T(0);
346  if (state_a && runb) // changes in b are irrelevant while state_a is true
347  {
348  if (!runa) break; // we are at the end
349  if (*ib<va)
350  {
351  ib.advance_up_to (va);
352  state_b=!(flip_b^ib.onoff());
353  }
354  }
355  T vb = runb ? *ib : T(0);
356  bool adv_a = runa && (!runb || (va<=vb)),
357  adv_b = runb && (!runa || (vb<=va));
358  if (adv_a) { state_a=!state_a; ++ia; runa = !ia.atEnd(); }
359  if (adv_b) { state_b=!state_b; ++ib; runb = !ib.atEnd(); }
360  if ((state_a||state_b)!=state_res)
361  return false;
362  }
363  return true;
364  }
365 
366  static bool generalAllOrNothing (const crangeset &a, const crangeset &b,
367  bool flip_a, bool flip_b)
368  {
369  if (a.r.empty())
370  return flip_a ? true : b.r.empty();
371  if (b.r.empty())
372  return flip_b ? true : a.r.empty();
373  int strat = strategy (a.r.size(), b.r.size());
374  return (strat==1) ? generalAllOrNothing1(a,b,flip_a,flip_b) :
375  ((strat==2) ? generalAllOrNothing2(a,b,flip_a,flip_b)
376  : generalAllOrNothing2(b,a,flip_b,flip_a));
377  }
378  public:
379  /*! Removes all rangeset entries. */
380  void clear() { r.clear(); }
381  bool empty() const { return r.empty(); }
382  const rtype &data() const { return r; }
383  void checkConsistency() const
384  {
385  using namespace std;
386  if (r.size()==0) return;
387  planck_assert(r[0]>=0,"incorrect first element in range set");
388  for (tsize i=1; i<r.size(); ++i)
389  planck_assert(abs(r[i])>abs(r[i-1]+1),"inconsistent entries");
390  }
391  void setData (const rtype &inp)
392  {
393  r=inp;
394  checkConsistency();
395  }
396  /*! Appends \a [v1;v2[ to the rangeset. \a v1 must be larger
397  than the minimum of the last range in the rangeset. */
398  void append(const T &v1, const T &v2)
399  {
400  using namespace std;
401  if (v2<=v1) return;
402  T le = -100;
403  if (!empty())
404  le=(r.back()<0) ? -r.back() : r.back()+1;
405 
406  if (v1>le) // clean append
407  {
408  if ((v1>le+1)||(r.back()>0))
409  {
410  r.push_back(v1);
411  if (v2-v1>1) r.push_back(-v2);
412  }
413  else // short off interval
414  r.push_back(-v2);
415  return;
416  }
417  T lastbegin=-200;
418  if (!r.empty())
419  {
420  if (r.back()>=0) lastbegin= r.back();
421  else if (r[r.size()-2]<0) lastbegin = -r[r.size()-2]+1;
422  else lastbegin = r[r.size()-2];
423  }
424  planck_assert(v1>=lastbegin,"bad append operation");
425  // merge with the last interval
426  T endval=max(abs(r.back()),v2);
427  if (r.back()<0)
428  r.back()=-endval;
429  else
430  if (endval>r.back()+1) r.push_back(-endval);
431  }
432  /*! Appends \a [v;v+1[ to the rangeset. \a v must be larger
433  than the minimum of the last range in the rangeset. */
434  void append(const T &v)
435  { append(v,v+1); }
436 
437  /*! Appends \a other to the rangeset. All values in \a other must be larger
438  than the minimum of the last range in the rangeset. */
439  void append (const crangeset &other)
440  {
441  typename crangeset<T>::IvIter iter(other);
442  while (!iter.atEnd())
443  {
444  append(iter.ivbegin(),iter.ivend());
445  ++iter;
446  }
447  }
448  /*! Returns the total number of elements in the rangeset. */
449  T nval() const
450  {
451  T res=0;
452  typename crangeset<T>::IvIter iter(*this);
453  while (!iter.atEnd())
454  {res+=iter.ivlen(); ++iter;}
455  return res;
456  }
457 
458  crangeset op_or (const crangeset &other) const
459  { return generalUnion (*this,other,false,false); }
460  crangeset op_and (const crangeset &other) const
461  { return generalUnion (*this,other,true,true); }
462  crangeset op_andnot (const crangeset &other) const
463  { return generalUnion (*this,other,true,false); }
464  crangeset op_xor (const crangeset &other) const
465  { return generalXor (*this,other); }
466 
467  /*! Returns \a true if the rangeset is identical to \a other, else \a false.
468  */
469  bool operator== (const crangeset &other) const
470  { return r==other.r; }
471 
472  /*! Returns \a true if the rangeset contains all values in the range
473  \a [a;b[, else \a false. */
474  bool contains (T a,T b) const
475  {
476  tdiff res=iiv(a);
477  if (res<0) return false;
478  if (r[res]<0)
479  {
480  if (res==tdiff(r.size())-1) return false; // beyond end
481  if (r[res+1]>=0) return false; // long "off" range
482  return ((a>-r[res])&&(b<=-r[res+1])); // mixed range
483  }
484  // r[res]>=0
485  if ((res==tdiff(r.size())-1) || (r[res+1]>=0)) // short interval
486  return ((a==r[res])&&(b==a+1));
487  return b<=-r[res+1];
488  }
489  /*! Returns \a true if the rangeset contains the value \a v,
490  else \a false. */
491  bool contains (T v) const
492  {
493  tdiff res=iiv(v);
494  if (res<0) return false;
495  if (r[res]<0)
496  {
497  if (res==tdiff(r.size())-1) return false; // beyond end
498  if (r[res+1]>=0) return false; // long "off" range
499  return (v>-r[res]); // mixed range
500  }
501  if ((res<tdiff(r.size())-1) && (r[res+1]<0))
502  return true;
503  return (r[res]==v);
504  }
505  /*! Returns \a true if the rangeset contains all values stored in \a other,
506  else \a false. */
507  bool contains (const crangeset &other) const
508  { return generalAllOrNothing(*this,other,false,true); }
509 
510  /** Returns true if any of the numbers [a;b[ are contained in the set,
511  else false. */
512  bool overlaps (T a,T b) const
513  {
514  using namespace std;
515  if (empty()) return false;
516  tdiff res=iiv(a);
517  if (res<tdiff(r.size())-1)
518  if (b>abs(r[res+1])) return true; // at least one switch
519  // now we know that [a;b[ lies entirely inside an interval,
520  // but it may still contain a short sub-interval
521  if (res==-1) return false; // no sub-intervals in that one
522  if (res==tdiff(r.size())-1)
523  return a==r[res]; // only overlaps if r[res]>0 and a==abs(r[res])
524  // we are somewhere in the middle
525  if (r[res]>=0)
526  return (a==r[res]) || (r[res+1]<0);
527  return (r[res+1]<0) && (b>-r[res]+1);
528  }
529 
530  /** Returns true if there is overlap between the set and "other",
531  else false. */
532  bool overlaps (const crangeset &other) const
533  { return !generalAllOrNothing(*this,other,true,true); }
534  };
535 
536 template<typename T> inline std::ostream &operator<< (std::ostream &os,
537  const crangeset<T> &rs)
538  {
539  os << "{ ";
540  typename crangeset<T>::IvIter iter(rs);
541  while (!iter.atEnd())
542  {
543  os << "["<<iter.ivbegin()<<";"<<iter.ivend()<<"[ ";
544  ++iter;
545  }
546  return os << "}";
547  }
548 
549 #endif
void append(const T &v1, const T &v2)
Definition: crangeset.h:398
void append(const T &v)
Definition: crangeset.h:434
bool overlaps(T a, T b) const
Definition: crangeset.h:512
bool contains(const crangeset &other) const
Definition: crangeset.h:507
T nval() const
Definition: crangeset.h:449
void clear()
Definition: crangeset.h:380
static int strategy(tsize sza, tsize szb)
Definition: crangeset.h:221
std::size_t tsize
Definition: datatypes.h:116
int ilog2(I arg)
Definition: math_utils.h:125
std::ptrdiff_t tdiff
Definition: datatypes.h:118
bool overlaps(const crangeset &other) const
Definition: crangeset.h:532
tdiff iiv(const T &val) const
Definition: crangeset.h:214
bool operator==(const crangeset &other) const
Definition: crangeset.h:469
bool contains(T a, T b) const
Definition: crangeset.h:474
#define planck_assert(testval, msg)
void append(const crangeset &other)
Definition: crangeset.h:439
bool contains(T v) const
Definition: crangeset.h:491

Generated on Thu Jul 28 2022 17:32:06 for LevelS C++ support library