Lunchbox  1.8.0
unorderedIntervalSet.ipp
1 
2 /* Copyright (c) 2008, Juan Hernando <jhernando@fi.upm.es>
3  * 2013, 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 
27 template< typename T >
28 class UnorderedIntervalSet< T >::const_iterator :
29  public boost::iterator_facade<
30  typename UnorderedIntervalSet< T >::const_iterator,
31  T, std::forward_iterator_tag, T >
32 {
33 public:
34  // Default constructor to an end() iterator.
35  const_iterator()
36  {}
37 
38 private:
39  friend class boost::iterator_core_access;
40  friend class UnorderedIntervalSet;
41 
42  typedef typename UnorderedIntervalSet< T >::EdgeSet::const_iterator
43  edge_iterator;
44 
45  const_iterator( const UnorderedIntervalSet& set,
46  const edge_iterator& interval)
47  : _intervalIteratorEnd( set._intervals.end( ))
48  , _startEdge( interval )
49  {
50  if (_startEdge != _intervalIteratorEnd )
51  _value = _startEdge->first;
52  }
53 
54  const_iterator( const UnorderedIntervalSet & set,
55  const edge_iterator & interval, const T& current )
56  : _value( current )
57  , _intervalIteratorEnd( set._intervals.end( ))
58  , _startEdge( interval )
59  {
60  }
61 
62  void increment()
63  {
64  if( _startEdge == _intervalIteratorEnd )
65  return;
66 
67  edge_iterator endEdge = _startEdge;
68  ++endEdge;
69  // Next element is inside the current interval.
70  if( _value >= _startEdge->first && _value < endEdge->first )
71  ++_value;
72  else
73  {
74  ++_startEdge;
75  ++_startEdge;
76  if( _startEdge != _intervalIteratorEnd )
77  _value = _startEdge->first;
78  }
79  }
80 
81  bool equal( const const_iterator& rhs ) const
82  {
83  return (_startEdge == _intervalIteratorEnd &&
84  rhs._startEdge == _intervalIteratorEnd) ||
85  (_startEdge == rhs._startEdge && _value == rhs._value );
86  }
87 
88  T dereference() const
89  {
90  return _value;
91  }
92 
93  T _value;
94  edge_iterator _intervalIteratorEnd;
95  edge_iterator _startEdge;
96 };
97 
98 template < typename T >
100  : _size( 0 )
101 {}
102 
103 template < typename T >
104 void UnorderedIntervalSet< T >::insert( const T& element )
105 {
106  insert( element, element );
107 }
108 
109 template < typename T >
110 void UnorderedIntervalSet< T >::erase( const T& element )
111 {
112  erase( element, element );
113 }
114 
115 template < typename T >
116 void UnorderedIntervalSet< T >::insert( const T& startElement,
117  const T& endElement )
118 {
119  LBASSERT( startElement <= endElement );
120 
121  Edge startValue( startElement, true );
122  Edge endValue( endElement, false );
123 
124  if( _intervals.empty( ))
125  {
126  _intervals.insert( startValue );
127  _intervals.insert( endValue );
128  _size = endElement - startElement + 1;
129  return;
130  }
131 
132  // Finding the first edge whose value is less or equal than startElement.
133  typename EdgeSet::iterator previous_to_start =
134  _intervals.lower_bound( startValue );
135  if( previous_to_start != _intervals.end( ))
136  {
137  if (previous_to_start == _intervals.begin( ))
138  previous_to_start = _intervals.end();
139  else
140  previous_to_start--;
141  }
142  else
143  previous_to_start = (++_intervals.rbegin()).base();
144 
145  // Adding start edge as needed.
146  typename EdgeSet::iterator position;
147  bool fallsInside;
148  size_t overlappingPortion = 0;
149  T overlappingStart = T(); // initialized to silent a warning.
150 
151  if( previous_to_start == _intervals.end( ))
152  {
153  // Previous element doesn't exist and there is neither any of
154  // equal value.
155  // We have to insert start.
156  position = _intervals.insert( startValue );
157  fallsInside = false;
158  }
159  else if( !previous_to_start->second )
160  {
161  // Previous element is the end of one interval.
162  if( previous_to_start->first + 1 == startElement )
163  {
164  // The end of previous interval end is one unit less than the start
165  // of this interval. Removing the edge to fuse both.
166  position = previous_to_start;
167  position--;
168  _intervals.erase( previous_to_start );
169  }
170  else
171  // We have to insert start.
172  position = _intervals.insert( previous_to_start, startValue );
173  fallsInside = false;
174  }
175  else
176  {
177  // Start has fallen inside another interval we don't have to insert it.
178  position = previous_to_start;
179  fallsInside = true;
180  overlappingStart = startElement;
181  }
182 
183  // Now we have to check where the end goes.
184  ++position;
185  while( position != _intervals.end() && position->first <= endElement )
186  {
187  // Calculating the length of a possible interval overlapping the
188  // interval being inserted.
189  if( fallsInside )
190  {
191  // Previous position was the start of an overlapping interval.
192  LBASSERT( !position->second );
193  overlappingPortion += position->first - overlappingStart + 1;
194  }
195  else
196  overlappingStart = position->first;
197 
198  fallsInside = position->second;
199 
200  // Note that the post-increment is evaluated before the function call
201  // So position is actually pointing to the next one before the previous
202  // element is erased.
203  _intervals.erase( position++ );
204  }
205 
206  if( position != _intervals.end() &&
207  position->second && position->first == endElement + 1 )
208  {
209  // The end of the interval connects with the start of the next one.
210  // We remove the start of the following one and don't insert this
211  // edge.
212  _intervals.erase(position);
213  }
214  else if( !fallsInside )
215  {
216  // End edge is not inside a previously existing interval so we
217  // have to add it.
218  _intervals.insert(position, endValue);
219  }
220  else
221  overlappingPortion += endElement - overlappingStart + 1;
222 
223  _size += size_t(endElement - startElement + 1) - overlappingPortion;
224  LBASSERT( _intervals.size() % 2 == 0 );
225 }
226 
227 template < typename T >
228 void UnorderedIntervalSet< T >::erase( const T& startElement,
229  const T& endElement )
230 {
231  LBASSERT( startElement <= endElement );
232 
233  if( _intervals.empty( ))
234  return;
235 
236  // Finding the first edge whose value is less or equal than startElement.
237  typename EdgeSet::iterator nextToStart =
238  _intervals.lower_bound( std::make_pair( startElement, true ));
239  typename EdgeSet::iterator previousToStart = nextToStart;
240  if( nextToStart == _intervals.begin( ))
241  previousToStart = _intervals.end();
242  else if( nextToStart == _intervals.end( ))
243  // Nothing to remove here
244  return;
245  else
246  --previousToStart;
247 
248  typename EdgeSet::iterator position;
249  bool inside;
250  T overlappingStart = 0;
251  size_t overlapping_portion = 0;
252 
253  if( previousToStart != _intervals.end( ))
254  {
255  // startElement is greater or equal than some interval edge.
256  if( previousToStart->second )
257  {
258  // Inserting the new end of the interval starting at
259  // previous_to_start.
260  position =
261  _intervals.insert( std::make_pair( startElement - 1, false ));
262  inside = true;
263  overlappingStart = startElement;
264  }
265  else
266  {
267  position = previousToStart;
268  inside = false;
269  }
270  ++position;
271  }
272  else
273  {
274  // startElement is less than the start of any interval.
275  inside = false;
276  position = _intervals.begin();
277  overlappingStart = position->first;
278  }
279  // Position has the next edge after the last interval before the removal
280  // interval.
281 
282  while( position != _intervals.end() && position->first <= endElement )
283  {
284  if (inside)
285  overlapping_portion += position->first - overlappingStart + 1;
286  else
287  overlappingStart = position->first;
288 
289  inside = position->second;
290 
291  // Note that the post-increment is evaluated before the function call
292  // So position is actually pointing to the next one before the previous
293  // element is erased.
294  _intervals.erase( position++ );
295  }
296 
297  if( inside )
298  {
299  LBASSERT( position != _intervals.end( ));
300  // End edge is not inside a previously existing interval so we
301  // have to add it.
302  _intervals.insert (std::make_pair( endElement + 1, true ));
303  overlapping_portion += endElement - overlappingStart + 1;
304  }
305 
306  _size -= overlapping_portion;
307  LBASSERT( _intervals.size() % 2 == 0 );
308 }
309 
310 template < typename T >
311 void UnorderedIntervalSet< T >::insert( const UnorderedIntervalSet& rhs )
312 {
313  for( typename EdgeSet::const_iterator i = rhs._intervals.begin();
314  i != rhs._intervals.end(); ++i )
315  {
316  insert( i->first, i->second );
317  }
318 }
319 
320 template < typename T >
322 {
323  _intervals.clear();
324  _size = 0;
325 }
326 
327 template < typename T >
328 bool UnorderedIntervalSet< T >::exists( const T& element ) const
329 {
330  return find( element ) != end();
331 }
332 
333 template < typename T >
334 typename UnorderedIntervalSet< T >::const_iterator
335 UnorderedIntervalSet< T >::find( const T& element ) const
336 {
337  if( _intervals.empty( ))
338  return end();
339 
340  typename EdgeSet::const_iterator next =
341  _intervals.lower_bound( std::make_pair( element, false ));
342  // Note that if x equals the start edge of any interval then
343  // next will be the end edge due to the use of (x, false) in the
344  // search.
345  if( next == _intervals.end() || next == _intervals.begin( ))
346  // x cannot be inside any interval.
347  return end();
348 
349  typename EdgeSet::const_iterator previous = next;
350  --previous;
351  if( previous->second )
352  return const_iterator( *this, previous, element );
353  return end();
354 }
355 
356 template < typename T >
357 typename UnorderedIntervalSet< T >::const_iterator
359 {
360  if( _intervals.empty( ))
361  return end();
362  return const_iterator( *this, _intervals.begin( ));
363 }
364 
365 template < typename T >
366 typename UnorderedIntervalSet< T >::const_iterator
368 {
369  return const_iterator( *this, _intervals.end());
370 }
371 
372 template < typename T >
373 size_t UnorderedIntervalSet< T >::size() const
374 {
375  return _size;
376 }
377 
378 template < typename T >
380 {
381  return size() == 0;
382 }
383 
384 template < typename T >
385 void UnorderedIntervalSet< T >::swap( UnorderedIntervalSet& rhs )
386 {
387  _intervals.swap( rhs._intervals );
388 }
389 
390 }