Coverage for casanova/casanova/contiguous_range_set.py: 93%

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

56 statements  

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 

13 

14 

15class ContiguousRangeSet(object): 

16 """ 

17 Class representing contiguous ranges of integers as a sorted list of 

18 intervals. 

19 """ 

20 

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 

26 

27 def count(self): 

28 count = 0 

29 

30 for start, end in self.intervals: 

31 count += (end + 1 - start) 

32 

33 return count 

34 

35 def __len__(self): 

36 return self.length 

37 

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 """ 

43 

44 # NOTE: this would break if you add the same point twice 

45 self.length += 1 

46 

47 interval = (point, point) 

48 N = len(self.intervals) 

49 

50 # Set is empty 

51 if N == 0: 

52 self.intervals.append(interval) 

53 return 

54 

55 # Finding insertion point 

56 index = bisect_left(self.intervals, interval) 

57 

58 if index >= N: 

59 last_interval = self.intervals[-1] 

60 

61 if point == last_interval[1] + 1: 

62 self.intervals[-1] = (last_interval[0], point) 

63 return 

64 

65 self.intervals.append(interval) 

66 return 

67 

68 matched_interval = self.intervals[index] 

69 previous_interval = self.intervals[index - 1] if index != 0 else None 

70 

71 if point == matched_interval[0] - 1: 

72 

73 self.intervals[index] = (point, matched_interval[1]) 

74 

75 if previous_interval is not None: 

76 

77 if point == previous_interval[1] + 1: 

78 self.intervals[index] = (previous_interval[0], matched_interval[1]) 

79 self.intervals.pop(index - 1) 

80 

81 return 

82 

83 elif previous_interval is not None and point == previous_interval[1] + 1: 

84 

85 self.intervals[index - 1] = (previous_interval[0], point) 

86 return 

87 

88 if point < matched_interval[0]: 

89 self.intervals.insert(index, interval) 

90 return 

91 

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 """ 

98 

99 if self.length == 0: 

100 return False 

101 

102 N = len(self.intervals) 

103 

104 if N == 0 or self.current_stateful_interval >= N: 

105 return False 

106 

107 I = self.intervals[self.current_stateful_interval] 

108 

109 if point < I[0]: 

110 return False 

111 

112 if point > I[1]: 

113 self.current_stateful_interval += 1 

114 return False 

115 

116 return True