Coverage for casanova/casanova/contiguous_range_set.py: 12%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# =============================================================================
2# Casanova Contiguous Range Set Collection
3# =============================================================================
4#
5# Simplistic implementation of a contiguous range set in python relying on a
6# sorted list of contiguous intervals. It is very useful to represent a set of
7# dense intervals using very little memory & is used across the task-resuming
8# schemes of the CLI tool.
9#
10# It takes a lot of inspiration from the inversion list data structure.
11#
12from bisect import bisect_left
15class ContiguousRangeSet(object):
16 """
17 Class representing contiguous ranges of integers as a sorted list of
18 intervals.
19 """
21 def __init__(self):
22 # NOTE: replace this by `blist` if not performany enough
23 self.intervals = []
24 self.current_stateful_interval = 0
25 self.length = 0
27 def count(self):
28 count = 0
30 for start, end in self.intervals:
31 count += (end + 1 - start)
33 return count
35 def __len__(self):
36 return self.length
38 def add(self, point):
39 """
40 Method adding a single point to the set. We assume that the point
41 cannot yet be in the set.
42 """
44 # NOTE: this would break if you add the same point twice
45 self.length += 1
47 interval = (point, point)
48 N = len(self.intervals)
50 # Set is empty
51 if N == 0:
52 self.intervals.append(interval)
53 return
55 # Finding insertion point
56 index = bisect_left(self.intervals, interval)
58 if index >= N:
59 last_interval = self.intervals[-1]
61 if point == last_interval[1] + 1:
62 self.intervals[-1] = (last_interval[0], point)
63 return
65 self.intervals.append(interval)
66 return
68 matched_interval = self.intervals[index]
69 previous_interval = self.intervals[index - 1] if index != 0 else None
71 if point == matched_interval[0] - 1:
73 self.intervals[index] = (point, matched_interval[1])
75 if previous_interval is not None:
77 if point == previous_interval[1] + 1:
78 self.intervals[index] = (previous_interval[0], matched_interval[1])
79 self.intervals.pop(index - 1)
81 return
83 elif previous_interval is not None and point == previous_interval[1] + 1:
85 self.intervals[index - 1] = (previous_interval[0], point)
86 return
88 if point < matched_interval[0]:
89 self.intervals.insert(index, interval)
90 return
92 def stateful_contains(self, point):
93 """
94 Method returning whether the given point is found in the set. This
95 method is stateful and assumes we will ask for points in a monotonic
96 increasing order.
97 """
99 if self.length == 0:
100 return False
102 N = len(self.intervals)
104 if N == 0 or self.current_stateful_interval >= N:
105 return False
107 I = self.intervals[self.current_stateful_interval]
109 if point < I[0]:
110 return False
112 if point > I[1]:
113 self.current_stateful_interval += 1
114 return False
116 return True