class SortedListWithKey(MutableSequence): def __init__(self, iterable=None, key=lambda val: val, value_orderable=True, load=1000): self._key = key self._list = SortedList(load=load) self._ordered = value_orderable if value_orderable: self._pair = lambda key, value: (key, value) else: self._pair = Pair if iterable is not None: self.update(iterable) def clear(self): self._list.clear() def add(self, value): pair = self._pair(self._key(value), value) self._list.add(pair) def update(self, iterable): _key, _pair = self._key, self._pair self._list.update(_pair(_key(val), val) for val in iterable) def __contains__(self, value): _list = self._list _key = self._key(value) _pair = self._pair(_key, value) if self._ordered: return _pair in _list _maxes = _list._maxes if _maxes is None: return False pos = bisect_left(_maxes, _pair) if pos == len(_maxes): return False _lists = _list._lists idx = bisect_left(_lists[pos], _pair) len_lists = len(_lists) len_sublist = len(_lists[pos]) while True: pair = _lists[pos][idx] if _key != pair.key: return False if value == pair.value: return True idx += 1 if idx == len_sublist: pos += 1 if pos == len_lists: return False len_sublist = len(_lists[pos]) idx = 0 def discard(self, value): _list = self._list _key = self._key(value) _pair = self._pair(_key, value) if self._ordered: _list.discard(_pair) return _maxes = _list._maxes if _maxes is None: return pos = bisect_left(_maxes, _pair) if pos == len(_maxes): return _lists = _list._lists idx = bisect_left(_lists[pos], _pair) len_lists = len(_lists) len_sublist = len(_lists[pos]) while True: pair = _lists[pos][idx] if _key != pair.key: return if value == pair.value: _list._delete(pos, idx) return idx += 1 if idx == len_sublist: pos += 1 if pos == len_lists: return len_sublist = len(_lists[pos]) idx = 0 def remove(self, value): _list = self._list _key = self._key(value) _pair = self._pair(_key, value) if self._ordered: _list.remove(_pair) return _maxes = _list._maxes if _maxes is None: raise ValueError pos = bisect_left(_maxes, _pair) if pos == len(_maxes): raise ValueError _lists = _list._lists idx = bisect_left(_lists[pos], _pair) len_lists = len(_lists) len_sublist = len(_lists[pos]) while True: pair = _lists[pos][idx] if _key != pair.key: raise ValueError if value == pair.value: _list._delete(pos, idx) return idx += 1 if idx == len_sublist: pos += 1 if pos == len_lists: raise ValueError len_sublist = len(_lists[pos]) idx = 0 def __delitem__(self, index): del self._list[index] def __getitem__(self, index): if isinstance(index, slice): return list(tup[1] for tup in self._list[index]) else: return self._list[index][1] def __setitem__(self, index, value): _key, _pair = self._key, self._pair if isinstance(index, slice): self._list[index] = list(_pair(_key(val), val) for val in value) else: self._list[index] = _pair(_key(value), value) def __iter__(self): return iter(tup[1] for tup in iter(self._list)) def __reversed__(self): return iter(tup[1] for tup in reversed(self._list)) def __len__(self): return len(self._list) def bisect_left(self, value): pair = self._pair(self._key(value), value) return self._list.bisect_left(pair) def bisect(self, value): pair = self._pair(self._key(value), value) return self._list.bisect(pair) def bisect_right(self, value): pair = self._pair(self._key(value), value) return self._list.bisect_right(pair) def count(self, value): _list = self._list _key = self._key(value) _pair = self._pair(_key, value) if self._ordered: return _list.count(_pair) _maxes = _list._maxes if _maxes is None: return 0 pos = bisect_left(_maxes, _pair) if pos == len(_maxes): return 0 _lists = _list._lists idx = bisect_left(_lists[pos], _pair) total = 0 len_lists = len(_lists) len_sublist = len(_lists[pos]) while True: pair = _lists[pos][idx] if _key != pair.key: return total if value == pair.value: total += 1 idx += 1 if idx == len_sublist: pos += 1 if pos == len_lists: return total len_sublist = len(_lists[pos]) idx = 0 def copy(self): _key, _ordered, _load = self._key, self._ordered, self._list._load kwargs = dict(key=_key, value_orderable=_ordered, load=_load) return SortedListWithKey(self, **kwargs) def __copy__(self): return self.copy() def append(self, value): pair = self._pair(self._key(value), value) self._list.append(pair) def extend(self, iterable): _key, _pair = self._key, self._pair self._list.extend(_pair(_key(val), val) for val in iterable) def insert(self, index, value): pair = self._pair(self._key(value), value) self._list.insert(index, pair) def pop(self, index=-1): return self._list.pop(index)[1] def index(self, value, start=None, stop=None): _list = self._list _key = self._key(value) _pair = self._pair(_key, value) if self._ordered: return _list.index(_pair, start, stop) _len = _list._len if start == None: start = 0 if start < 0: start += _len if start < 0: start = 0 if stop == None: stop = _len if stop < 0: stop += _len if stop > _len: stop = _len if stop <= start: raise ValueError _maxes = _list._maxes pos = bisect_left(_maxes, _pair) if pos == len(_maxes): raise ValueError _lists = _list._lists idx = bisect_left(_lists[pos], _pair) len_lists = len(_lists) len_sublist = len(_lists[pos]) while True: pair = _lists[pos][idx] if _key != pair.key: raise ValueError if value == pair.value: loc = _list._loc(pos, idx) if start <= loc < stop: return loc idx += 1 if idx == len_sublist: pos += 1 if pos == len_lists: raise ValueError len_sublist = len(_lists[pos]) idx = 0 def as_list(self): return list(tup[1] for tup in self._list.as_list()) def __add__(self, that): result = SortedListWithKey( key=self._key, value_orderable=self._ordered, load=self._list._load) values = self.as_list() values.extend(that) result.update(values) return result def __iadd__(self, that): self.update(that) return self def __mul__(self, that): values = self.as_list() * that return SortedListWithKey( values, key=self._key, value_orderable=self._ordered, load=self._list._load) def __imul__(self, that): values = self.as_list() * that self.clear() self.update(values) return self def __eq__(self, that): return ((len(self) == len(that)) and all(lhs == rhs for lhs, rhs in zip(self, that))) def __ne__(self, that): return ((len(self) != len(that)) or any(lhs != rhs for lhs, rhs in zip(self, that))) def __lt__(self, that): return ((len(self) <= len(that)) and all(lhs < rhs for lhs, rhs in zip(self, that))) def __le__(self, that): return ((len(self) <= len(that)) and all(lhs <= rhs for lhs, rhs in zip(self, that))) def __gt__(self, that): return ((len(self) >= len(that)) and all(lhs > rhs for lhs, rhs in zip(self, that))) def __ge__(self, that): return ((len(self) >= len(that)) and all(lhs >= rhs for lhs, rhs in zip(self, that))) @recursive_repr def __repr__(self): return '%s(%s, key=%r, value_orderable=%r, load=%r)' % ( self.__class__.__name__, self.as_list(), self._key, self._ordered, self._list._load)
class SortedListTestCase(unittest.TestCase): def setUp(self): self.sorted_list = SortedList() @property def is_sorted(self): for i, item in enumerate(self.sorted_list): if i == 0: current = item else: if current > item: return False return True def test_append_is_sorted(self): items = [] for _ in xrange(200): num = random.uniform(-100, 100) self.sorted_list.append(num) self.assertTrue(self.is_sorted) def test_append_keeps_items(self): items = [] for _ in xrange(200): num = random.uniform(-100, 100) self.sorted_list.append(num) items.append(num) items.sort() for i, j in zip(items, self.sorted_list): self.assertEqual(i, j) def test_extend_is_sorted(self): nums = range(-100, 100) random.shuffle(nums) self.sorted_list.extend(nums) self.assertTrue(self.is_sorted) def test_extend_keeps_items(self): nums = range(-100, 100) random.shuffle(nums) self.sorted_list.extend(nums) nums.sort() for i, j in zip(nums, self.sorted_list): self.assertEqual(i, j) def test_setitem_is_sorted(self): nums = xrange(-100, 100) self.sorted_list.extend(nums) for _ in xrange(200): self.sorted_list[random.randint(0, len(self.sorted_list) - 1)] = random.uniform(-100, 100) self.assertTrue(self.is_sorted) def test_setitem_keeps_items(self): nums = range(-100, 100) self.sorted_list.extend(nums) for _ in xrange(200): index = random.randint(0, len(self.sorted_list) - 1) num = random.uniform(-100, 100) self.sorted_list[index] = num nums[index] = num nums.sort() for i, j in zip(self.sorted_list, nums): self.assertEqual(i, j)