-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathSequence.py
127 lines (109 loc) · 3.74 KB
/
Sequence.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""Sequence.py
Doubly-linked circular list for maintaining a sequence of items
subject to insertions and deletions.
D. Eppstein, November 2003.
"""
import math
import sys
class SequenceError(Exception): pass
class Sequence:
"""Maintain a sequence of items subject to insertions and removals.
All sequence operations take constant time except indexing, which
takes time proportional to the index.
"""
def __init__(self, iterable=[], key=None):
"""We represent the sequence as a doubly-linked circular linked list,
stored in two dictionaries, self._next and self._prev. We also store
a pointer self._first to the first item in the sequence. If key is
supplied, key(x) is used in place of x to look up item positions;
e.g. using key=id allows sequences of lists or sets.
"""
self._key = key
self._items = {}
self._next = {}
self._prev = {}
self._first = None
for x in iterable:
self.append(x)
def __iter__(self):
"""Iterate through the objects in the sequence.
May give unpredictable results if sequence changes mid-iteration.
"""
item = self._first
while self._next:
yield self._items.get(item,item)
item = self._next[item]
if item == self._first:
return
def __getitem__(self,i):
"""Return the ith item in the sequence."""
item = self._first
while i:
item = self._next[item]
if item == self._first:
raise IndexError("Index out of range")
i -= 1
return self._items.get(item,item)
def __len__(self):
"""Number of items in the sequence."""
return len(self._next)
def __repr__(self):
"""Printable representation of the sequence."""
output = []
for x in self:
output.append(repr(x))
return 'Sequence([' + ','.join(output) + '])'
def key(self,x):
"""Apply supplied key function."""
if not self._key:
return x
key = self._key(x)
self._items[key] = x
return key
def _insafter(self,x,y):
"""Unkeyed version of insertAfter."""
if y in self._next:
raise SequenceError("Item already in sequence: "+repr(y))
self._next[y] = z = self._next[x]
self._next[x] = self._prev[z] = y
self._prev[y] = x
def append(self,x):
"""Add x to the end of the sequence."""
x = self.key(x)
if not self._next: # add to empty sequence
self._next = {x:x}
self._prev = {x:x}
self._first = x
else:
self._insafter(self._prev[self._first],x)
def remove(self,x):
"""Remove x from the sequence."""
x = self.key(x)
prev = self._prev[x]
self._next[prev] = next = self._next[x]
self._prev[next] = prev
if x == self._first:
self._first = next
del self._next[x], self._prev[x]
def insertAfter(self,x,y):
"""Add y after x in the sequence."""
y = self.key(y)
x = self.key(x)
self._insafter(x,y)
def insertBefore(self,x,y):
"""Add y before x in the sequence."""
y = self.key(y)
x = self.key(x)
self._insafter(self._prev[x],y)
if self._first == x:
self._first = y
def predecessor(self,x):
"""Find the previous element in the sequence."""
x = self.key(x)
prev = self._prev[x]
return self._items.get(prev,prev)
def successor(self,x):
"""Find the next element in the sequence."""
x = self.key(x)
next = self._next[x]
return self._items.get(next,next)