Linux ip-172-26-7-228 5.4.0-1103-aws #111~18.04.1-Ubuntu SMP Tue May 23 20:04:10 UTC 2023 x86_64
Your IP : 18.118.166.45
from twisted.python.compat import xrange
class SequenceError(Exception):
"""Raised when the sequence isn't proper for translation to ranges."""
class SequenceRanges(object):
"""High level interface to ranges.
A ranges list represent a sequence of ordered and non-repeating
elements into a more compact format, by representing 3 or more
consecutive entries by a range.
This means that a sequence such as
[1, 2, 4, 5, 6, 8, 10, 11, 12, 14]
becomes
[1, 2, (4, 6), 8, (10, 12), 14]
"""
def __init__(self):
self._ranges = []
@classmethod
def from_sequence(cls, sequence):
obj = cls()
obj._ranges[:] = sequence_to_ranges(sequence)
return obj
@classmethod
def from_ranges(cls, ranges):
obj = cls()
obj._ranges[:] = ranges
return obj
def to_sequence(self):
return list(ranges_to_sequence(self._ranges))
def to_ranges(self):
return list(self._ranges)
def __iter__(self):
return ranges_to_sequence(self._ranges)
def __contains__(self, item):
index = find_ranges_index(self._ranges, item)
if index < len(self._ranges):
test = self._ranges[index]
if isinstance(test, tuple):
return (test[0] <= item <= test[1])
return (test == item)
return False
def add(self, item):
add_to_ranges(self._ranges, item)
def remove(self, item):
remove_from_ranges(self._ranges, item)
def sequence_to_ranges(sequence):
"""Iterate over range items that compose the given sequence."""
iterator = iter(sequence)
try:
range_start = range_stop = next(iterator)
except StopIteration:
return
while range_start is not None:
try:
item = next(iterator)
except StopIteration:
item = None
if item == range_stop + 1:
range_stop += 1
else:
if item is not None and item <= range_stop:
if item < range_stop:
raise SequenceError("Sequence is unordered (%r < %r)" %
(item, range_stop))
else:
raise SequenceError("Found duplicated item (%r)" % (item,))
if range_stop == range_start:
yield range_start
elif range_stop == range_start + 1:
yield range_start
yield range_stop
else:
yield (range_start, range_stop)
range_start = range_stop = item
def ranges_to_sequence(ranges):
"""Iterate over individual items represented in a ranges list."""
for item in ranges:
if isinstance(item, tuple):
start, end = item
if start > end:
raise ValueError("Range error %d > %d", start, end)
for item in xrange(start, end + 1):
yield item
else:
yield item
def find_ranges_index(ranges, item):
"""Find the index where an entry *may* be."""
lo = 0
hi = len(ranges)
while lo < hi:
mid = (lo + hi) // 2
test = ranges[mid]
try:
test = test[1]
except TypeError:
pass
if item > test:
lo = mid + 1
else:
hi = mid
return lo
def add_to_ranges(ranges, item):
"""Insert item in ranges, reorganizing as needed."""
index_start = index_stop = index = find_ranges_index(ranges, item)
range_start = range_stop = item
ranges_len = len(ranges)
# Look for duplicates.
if index < ranges_len:
test = ranges[index]
if isinstance(test, tuple):
if test[0] <= item <= test[1]:
return
elif test == item:
return
# Merge to the left side.
while index_start > 0:
test = ranges[index_start - 1]
if isinstance(test, tuple):
if test[1] != range_start - 1:
break
range_start = test[0]
else:
if test != range_start - 1:
break
range_start -= 1
index_start -= 1
# Merge to the right side.
while index_stop < ranges_len:
test = ranges[index_stop]
if isinstance(test, tuple):
if test[0] != range_stop + 1:
break
range_stop = test[1]
else:
if test != range_stop + 1:
break
range_stop += 1
index_stop += 1
if range_stop - range_start < 2:
ranges.insert(index, item)
else:
ranges[index_start:index_stop] = ((range_start, range_stop),)
def remove_from_ranges(ranges, item):
"""Remove item from ranges, reorganizing as needed."""
index = find_ranges_index(ranges, item)
ranges_len = len(ranges)
if index < ranges_len:
test = ranges[index]
if isinstance(test, tuple):
range_start, range_stop = test
if item >= range_start:
# Handle right side of the range (and replace original item).
if range_stop < item + 3:
ranges[index:index + 1] = range(item + 1, range_stop + 1)
else:
ranges[index:index + 1] = ((item + 1, range_stop),)
# Handle left side of the range.
if range_start > item - 3:
if range_start != item:
ranges[index:index] = range(range_start, item)
else:
ranges[index:index] = ((range_start, item - 1),)
elif item == test:
del ranges[index]
|