Lunchbox  1.16.0
Multi-threaded C++ toolbox library for all application developers creating high-performance multi-threaded programs.
intervalSet.ipp
1 
2 /* Copyright (c) 2008-2016, Juan Hernando <jhernando@fi.upm.es>
3  * Daniel Nachbaur <danielnachbaur@epfl.ch>
4  *
5  * This library is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License version 2.1 as published
7  * by the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12  * details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include <lunchbox/debug.h>
20 
21 #include <boost/iterator/iterator_facade.hpp>
22 
24 namespace lunchbox
25 {
26 template <typename T>
27 class IntervalSet<T>::const_iterator
28  : public boost::iterator_facade<typename IntervalSet<T>::const_iterator, T,
29  std::forward_iterator_tag, T>
30 {
31 public:
32  // Default constructor to an end() iterator.
33  const_iterator() {}
34 private:
35  friend class boost::iterator_core_access;
36  friend class IntervalSet;
37 
38  typedef typename IntervalSet<T>::EdgeSet::const_iterator edge_iterator;
39 
40  const_iterator(const IntervalSet& set, const edge_iterator& interval)
41  : _intervalIteratorEnd(set._intervals.end())
42  , _startEdge(interval)
43  {
44  if (_startEdge != _intervalIteratorEnd)
45  _value = _startEdge->first;
46  }
47 
48  const_iterator(const IntervalSet& set, const edge_iterator& interval,
49  const T& current)
50  : _value(current)
51  , _intervalIteratorEnd(set._intervals.end())
52  , _startEdge(interval)
53  {
54  }
55 
56  void increment()
57  {
58  if (_startEdge == _intervalIteratorEnd)
59  return;
60 
61  edge_iterator endEdge = _startEdge;
62  ++endEdge;
63  // Next element is inside the current interval.
64  if (_value >= _startEdge->first && _value < endEdge->first)
65  ++_value;
66  else
67  {
68  ++_startEdge;
69  ++_startEdge;
70  if (_startEdge != _intervalIteratorEnd)
71  _value = _startEdge->first;
72  }
73  }
74 
75  bool equal(const const_iterator& rhs) const
76  {
77  return (_startEdge == _intervalIteratorEnd &&
78  rhs._startEdge == _intervalIteratorEnd) ||
79  (_startEdge == rhs._startEdge && _value == rhs._value);
80  }
81 
82  T dereference() const { return _value; }
83  T _value;
84  edge_iterator _intervalIteratorEnd;
85  edge_iterator _startEdge;
86 };
87 
88 template <typename T>
90  : _size(0)
91 {
92 }
93 
94 template <typename T>
95 void IntervalSet<T>::insert(const T& element)
96 {
97  insert(element, element);
98 }
99 
100 template <typename T>
101 void IntervalSet<T>::erase(const T& element)
102 {
103  erase(element, element);
104 }
105 
106 template <typename T>
107 void IntervalSet<T>::insert(const T& startElement, const T& endElement)
108 {
109  LBASSERT(startElement <= endElement);
110 
111  Edge startValue(startElement, true);
112  Edge endValue(endElement, false);
113 
114  if (_intervals.empty())
115  {
116  _intervals.insert(startValue);
117  _intervals.insert(endValue);
118  _size = endElement - startElement + 1;
119  return;
120  }
121 
122  // Finding the first edge whose value is less or equal than startElement.
123  typename EdgeSet::iterator previous_to_start =
124  _intervals.lower_bound(startValue);
125  if (previous_to_start != _intervals.end())
126  {
127  if (previous_to_start == _intervals.begin())
128  {
129  if (startValue.first < previous_to_start->first)
130  previous_to_start = _intervals.end();
131  }
132  else
133  previous_to_start--;
134  }
135  else
136  previous_to_start = (++_intervals.rbegin()).base();
137 
138  // Adding start edge as needed.
139  typename EdgeSet::iterator position;
140  bool fallsInside;
141  size_t overlappingPortion = 0;
142  T overlappingStart = T(); // initialized to silent a warning.
143 
144  if (previous_to_start == _intervals.end())
145  {
146  // Previous element doesn't exist and there is neither any of
147  // equal value.
148  // We have to insert start.
149  position = _intervals.insert(startValue);
150  fallsInside = false;
151  }
152  else if (!previous_to_start->second)
153  {
154  // Previous element is the end of one interval.
155  if (previous_to_start->first + 1 == startElement)
156  {
157  // The end of previous interval end is one unit less than the start
158  // of this interval. Removing the edge to fuse both.
159  position = previous_to_start;
160  position--;
161  _intervals.erase(previous_to_start);
162  }
163  else
164  // We have to insert start.
165  position = _intervals.insert(previous_to_start, startValue);
166  fallsInside = false;
167  }
168  else
169  {
170  // Start has fallen inside another interval we don't have to insert it.
171  position = previous_to_start;
172  fallsInside = true;
173  overlappingStart = startElement;
174  }
175 
176  // Now we have to check where the end goes.
177  ++position;
178  while (position != _intervals.end() && position->first <= endElement)
179  {
180  // Calculating the length of a possible interval overlapping the
181  // interval being inserted.
182  if (fallsInside)
183  {
184  // Previous position was the start of an overlapping interval.
185  LBASSERT(!position->second);
186  overlappingPortion += position->first - overlappingStart + 1;
187  }
188  else
189  overlappingStart = position->first;
190 
191  fallsInside = position->second;
192 
193  // Note that the post-increment is evaluated before the function call
194  // So position is actually pointing to the next one before the previous
195  // element is erased.
196  _intervals.erase(position++);
197  }
198 
199  if (position != _intervals.end() && position->second &&
200  position->first == endElement + 1)
201  {
202  // The end of the interval connects with the start of the next one.
203  // We remove the start of the following one and don't insert this
204  // edge.
205  _intervals.erase(position);
206  }
207  else if (!fallsInside)
208  {
209  // End edge is not inside a previously existing interval so we
210  // have to add it.
211  _intervals.insert(position, endValue);
212  }
213  else
214  overlappingPortion += endElement - overlappingStart + 1;
215 
216  _size += size_t(endElement - startElement + 1) - overlappingPortion;
217  LBASSERT(_intervals.size() % 2 == 0);
218 }
219 
220 template <typename T>
221 void IntervalSet<T>::erase(const T& startElement, const T& endElement)
222 {
223  LBASSERT(startElement <= endElement);
224 
225  if (_intervals.empty())
226  return;
227 
228  // Finding the first edge whose value is less or equal than startElement.
229  typename EdgeSet::iterator nextToStart =
230  _intervals.lower_bound(std::make_pair(startElement, true));
231  typename EdgeSet::iterator previousToStart = nextToStart;
232  if (nextToStart == _intervals.begin())
233  previousToStart = _intervals.end();
234  else if (nextToStart == _intervals.end())
235  // Nothing to remove here
236  return;
237  else
238  --previousToStart;
239 
240  typename EdgeSet::iterator position;
241  bool inside;
242  T overlappingStart = 0;
243  size_t overlapping_portion = 0;
244 
245  if (previousToStart != _intervals.end())
246  {
247  // startElement is greater or equal than some interval edge.
248  if (previousToStart->second)
249  {
250  // Inserting the new end of the interval starting at
251  // previous_to_start.
252  position =
253  _intervals.insert(std::make_pair(startElement - 1, false));
254  inside = true;
255  overlappingStart = startElement;
256  }
257  else
258  {
259  position = previousToStart;
260  inside = false;
261  }
262  ++position;
263  }
264  else
265  {
266  // startElement is less than the start of any interval.
267  inside = false;
268  position = _intervals.begin();
269  overlappingStart = position->first;
270  }
271  // Position has the next edge after the last interval before the removal
272  // interval.
273 
274  while (position != _intervals.end() && position->first <= endElement)
275  {
276  if (inside)
277  overlapping_portion += position->first - overlappingStart + 1;
278  else
279  overlappingStart = position->first;
280 
281  inside = position->second;
282 
283  // Note that the post-increment is evaluated before the function call
284  // So position is actually pointing to the next one before the previous
285  // element is erased.
286  _intervals.erase(position++);
287  }
288 
289  if (inside)
290  {
291  LBASSERT(position != _intervals.end());
292  // End edge is not inside a previously existing interval so we
293  // have to add it.
294  _intervals.insert(std::make_pair(endElement + 1, true));
295  overlapping_portion += endElement - overlappingStart + 1;
296  }
297 
298  _size -= overlapping_portion;
299  LBASSERT(_intervals.size() % 2 == 0);
300 }
301 
302 template <typename T>
303 void IntervalSet<T>::insert(const IntervalSet& rhs)
304 {
305  for (typename EdgeSet::const_iterator i = rhs._intervals.begin();
306  i != rhs._intervals.end(); ++i)
307  {
308  insert(i->first, i->second);
309  }
310 }
311 
312 template <typename T>
314 {
315  _intervals.clear();
316  _size = 0;
317 }
318 
319 template <typename T>
320 bool IntervalSet<T>::exists(const T& element) const
321 {
322  return find(element) != end();
323 }
324 
325 template <typename T>
326 typename IntervalSet<T>::const_iterator IntervalSet<T>::find(
327  const T& element) const
328 {
329  if (_intervals.empty())
330  return end();
331 
332  typename EdgeSet::const_iterator next =
333  _intervals.lower_bound(std::make_pair(element, false));
334  // Note that if x equals the start edge of any interval then
335  // next will be the end edge due to the use of (x, false) in the
336  // search.
337  if (next == _intervals.end() || next == _intervals.begin())
338  // x cannot be inside any interval.
339  return end();
340 
341  typename EdgeSet::const_iterator previous = next;
342  --previous;
343  if (previous->second)
344  return const_iterator(*this, previous, element);
345  return end();
346 }
347 
348 template <typename T>
349 typename IntervalSet<T>::const_iterator IntervalSet<T>::begin() const
350 {
351  if (_intervals.empty())
352  return end();
353  return const_iterator(*this, _intervals.begin());
354 }
355 
356 template <typename T>
357 typename IntervalSet<T>::const_iterator IntervalSet<T>::end() const
358 {
359  return const_iterator(*this, _intervals.end());
360 }
361 
362 template <typename T>
363 size_t IntervalSet<T>::size() const
364 {
365  return _size;
366 }
367 
368 template <typename T>
369 bool IntervalSet<T>::empty() const
370 {
371  return size() == 0;
372 }
373 
374 template <typename T>
376 {
377  _intervals.swap(rhs._intervals);
378 }
379 }
const_iterator find(const T &element) const
bool exists(const T &element) const
IntervalSet()
Construct a new interval set.
void erase(const T &element)
Remove the given element.
void swap(IntervalSet &rhs)
Swap this container with another one.
bool empty() const
void insert(const T &element)
Insert a new element.
const_iterator begin() const
Abstraction layer and common utilities for multi-threaded programming.
Definition: algorithm.h:29
const_iterator end() const
size_t size() const
void clear()
Remove all stored elements.