Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pyrsistent/_pdeque.py: 28%
167 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
1from collections.abc import Sequence, Hashable
2from itertools import islice, chain
3from numbers import Integral
4from pyrsistent._plist import plist
7class PDeque(object):
8 """
9 Persistent double ended queue (deque). Allows quick appends and pops in both ends. Implemented
10 using two persistent lists.
12 A maximum length can be specified to create a bounded queue.
14 Fully supports the Sequence and Hashable protocols including indexing and slicing but
15 if you need fast random access go for the PVector instead.
17 Do not instantiate directly, instead use the factory functions :py:func:`dq` or :py:func:`pdeque` to
18 create an instance.
20 Some examples:
22 >>> x = pdeque([1, 2, 3])
23 >>> x.left
24 1
25 >>> x.right
26 3
27 >>> x[0] == x.left
28 True
29 >>> x[-1] == x.right
30 True
31 >>> x.pop()
32 pdeque([1, 2])
33 >>> x.pop() == x[:-1]
34 True
35 >>> x.popleft()
36 pdeque([2, 3])
37 >>> x.append(4)
38 pdeque([1, 2, 3, 4])
39 >>> x.appendleft(4)
40 pdeque([4, 1, 2, 3])
42 >>> y = pdeque([1, 2, 3], maxlen=3)
43 >>> y.append(4)
44 pdeque([2, 3, 4], maxlen=3)
45 >>> y.appendleft(4)
46 pdeque([4, 1, 2], maxlen=3)
47 """
48 __slots__ = ('_left_list', '_right_list', '_length', '_maxlen', '__weakref__')
50 def __new__(cls, left_list, right_list, length, maxlen=None):
51 instance = super(PDeque, cls).__new__(cls)
52 instance._left_list = left_list
53 instance._right_list = right_list
54 instance._length = length
56 if maxlen is not None:
57 if not isinstance(maxlen, Integral):
58 raise TypeError('An integer is required as maxlen')
60 if maxlen < 0:
61 raise ValueError("maxlen must be non-negative")
63 instance._maxlen = maxlen
64 return instance
66 @property
67 def right(self):
68 """
69 Rightmost element in dqueue.
70 """
71 return PDeque._tip_from_lists(self._right_list, self._left_list)
73 @property
74 def left(self):
75 """
76 Leftmost element in dqueue.
77 """
78 return PDeque._tip_from_lists(self._left_list, self._right_list)
80 @staticmethod
81 def _tip_from_lists(primary_list, secondary_list):
82 if primary_list:
83 return primary_list.first
85 if secondary_list:
86 return secondary_list[-1]
88 raise IndexError('No elements in empty deque')
90 def __iter__(self):
91 return chain(self._left_list, self._right_list.reverse())
93 def __repr__(self):
94 return "pdeque({0}{1})".format(list(self),
95 ', maxlen={0}'.format(self._maxlen) if self._maxlen is not None else '')
96 __str__ = __repr__
98 @property
99 def maxlen(self):
100 """
101 Maximum length of the queue.
102 """
103 return self._maxlen
105 def pop(self, count=1):
106 """
107 Return new deque with rightmost element removed. Popping the empty queue
108 will return the empty queue. A optional count can be given to indicate the
109 number of elements to pop. Popping with a negative index is the same as
110 popleft. Executes in amortized O(k) where k is the number of elements to pop.
112 >>> pdeque([1, 2]).pop()
113 pdeque([1])
114 >>> pdeque([1, 2]).pop(2)
115 pdeque([])
116 >>> pdeque([1, 2]).pop(-1)
117 pdeque([2])
118 """
119 if count < 0:
120 return self.popleft(-count)
122 new_right_list, new_left_list = PDeque._pop_lists(self._right_list, self._left_list, count)
123 return PDeque(new_left_list, new_right_list, max(self._length - count, 0), self._maxlen)
125 def popleft(self, count=1):
126 """
127 Return new deque with leftmost element removed. Otherwise functionally
128 equivalent to pop().
130 >>> pdeque([1, 2]).popleft()
131 pdeque([2])
132 """
133 if count < 0:
134 return self.pop(-count)
136 new_left_list, new_right_list = PDeque._pop_lists(self._left_list, self._right_list, count)
137 return PDeque(new_left_list, new_right_list, max(self._length - count, 0), self._maxlen)
139 @staticmethod
140 def _pop_lists(primary_list, secondary_list, count):
141 new_primary_list = primary_list
142 new_secondary_list = secondary_list
144 while count > 0 and (new_primary_list or new_secondary_list):
145 count -= 1
146 if new_primary_list.rest:
147 new_primary_list = new_primary_list.rest
148 elif new_primary_list:
149 new_primary_list = new_secondary_list.reverse()
150 new_secondary_list = plist()
151 else:
152 new_primary_list = new_secondary_list.reverse().rest
153 new_secondary_list = plist()
155 return new_primary_list, new_secondary_list
157 def _is_empty(self):
158 return not self._left_list and not self._right_list
160 def __lt__(self, other):
161 if not isinstance(other, PDeque):
162 return NotImplemented
164 return tuple(self) < tuple(other)
166 def __eq__(self, other):
167 if not isinstance(other, PDeque):
168 return NotImplemented
170 if tuple(self) == tuple(other):
171 # Sanity check of the length value since it is redundant (there for performance)
172 assert len(self) == len(other)
173 return True
175 return False
177 def __hash__(self):
178 return hash(tuple(self))
180 def __len__(self):
181 return self._length
183 def append(self, elem):
184 """
185 Return new deque with elem as the rightmost element.
187 >>> pdeque([1, 2]).append(3)
188 pdeque([1, 2, 3])
189 """
190 new_left_list, new_right_list, new_length = self._append(self._left_list, self._right_list, elem)
191 return PDeque(new_left_list, new_right_list, new_length, self._maxlen)
193 def appendleft(self, elem):
194 """
195 Return new deque with elem as the leftmost element.
197 >>> pdeque([1, 2]).appendleft(3)
198 pdeque([3, 1, 2])
199 """
200 new_right_list, new_left_list, new_length = self._append(self._right_list, self._left_list, elem)
201 return PDeque(new_left_list, new_right_list, new_length, self._maxlen)
203 def _append(self, primary_list, secondary_list, elem):
204 if self._maxlen is not None and self._length == self._maxlen:
205 if self._maxlen == 0:
206 return primary_list, secondary_list, 0
207 new_primary_list, new_secondary_list = PDeque._pop_lists(primary_list, secondary_list, 1)
208 return new_primary_list, new_secondary_list.cons(elem), self._length
210 return primary_list, secondary_list.cons(elem), self._length + 1
212 @staticmethod
213 def _extend_list(the_list, iterable):
214 count = 0
215 for elem in iterable:
216 the_list = the_list.cons(elem)
217 count += 1
219 return the_list, count
221 def _extend(self, primary_list, secondary_list, iterable):
222 new_primary_list, extend_count = PDeque._extend_list(primary_list, iterable)
223 new_secondary_list = secondary_list
224 current_len = self._length + extend_count
225 if self._maxlen is not None and current_len > self._maxlen:
226 pop_len = current_len - self._maxlen
227 new_secondary_list, new_primary_list = PDeque._pop_lists(new_secondary_list, new_primary_list, pop_len)
228 extend_count -= pop_len
230 return new_primary_list, new_secondary_list, extend_count
232 def extend(self, iterable):
233 """
234 Return new deque with all elements of iterable appended to the right.
236 >>> pdeque([1, 2]).extend([3, 4])
237 pdeque([1, 2, 3, 4])
238 """
239 new_right_list, new_left_list, extend_count = self._extend(self._right_list, self._left_list, iterable)
240 return PDeque(new_left_list, new_right_list, self._length + extend_count, self._maxlen)
242 def extendleft(self, iterable):
243 """
244 Return new deque with all elements of iterable appended to the left.
246 NB! The elements will be inserted in reverse order compared to the order in the iterable.
248 >>> pdeque([1, 2]).extendleft([3, 4])
249 pdeque([4, 3, 1, 2])
250 """
251 new_left_list, new_right_list, extend_count = self._extend(self._left_list, self._right_list, iterable)
252 return PDeque(new_left_list, new_right_list, self._length + extend_count, self._maxlen)
254 def count(self, elem):
255 """
256 Return the number of elements equal to elem present in the queue
258 >>> pdeque([1, 2, 1]).count(1)
259 2
260 """
261 return self._left_list.count(elem) + self._right_list.count(elem)
263 def remove(self, elem):
264 """
265 Return new deque with first element from left equal to elem removed. If no such element is found
266 a ValueError is raised.
268 >>> pdeque([2, 1, 2]).remove(2)
269 pdeque([1, 2])
270 """
271 try:
272 return PDeque(self._left_list.remove(elem), self._right_list, self._length - 1)
273 except ValueError:
274 # Value not found in left list, try the right list
275 try:
276 # This is severely inefficient with a double reverse, should perhaps implement a remove_last()?
277 return PDeque(self._left_list,
278 self._right_list.reverse().remove(elem).reverse(), self._length - 1)
279 except ValueError as e:
280 raise ValueError('{0} not found in PDeque'.format(elem)) from e
282 def reverse(self):
283 """
284 Return reversed deque.
286 >>> pdeque([1, 2, 3]).reverse()
287 pdeque([3, 2, 1])
289 Also supports the standard python reverse function.
291 >>> reversed(pdeque([1, 2, 3]))
292 pdeque([3, 2, 1])
293 """
294 return PDeque(self._right_list, self._left_list, self._length)
295 __reversed__ = reverse
297 def rotate(self, steps):
298 """
299 Return deque with elements rotated steps steps.
301 >>> x = pdeque([1, 2, 3])
302 >>> x.rotate(1)
303 pdeque([3, 1, 2])
304 >>> x.rotate(-2)
305 pdeque([3, 1, 2])
306 """
307 popped_deque = self.pop(steps)
308 if steps >= 0:
309 return popped_deque.extendleft(islice(self.reverse(), steps))
311 return popped_deque.extend(islice(self, -steps))
313 def __reduce__(self):
314 # Pickling support
315 return pdeque, (list(self), self._maxlen)
317 def __getitem__(self, index):
318 if isinstance(index, slice):
319 if index.step is not None and index.step != 1:
320 # Too difficult, no structural sharing possible
321 return pdeque(tuple(self)[index], maxlen=self._maxlen)
323 result = self
324 if index.start is not None:
325 result = result.popleft(index.start % self._length)
326 if index.stop is not None:
327 result = result.pop(self._length - (index.stop % self._length))
329 return result
331 if not isinstance(index, Integral):
332 raise TypeError("'%s' object cannot be interpreted as an index" % type(index).__name__)
334 if index >= 0:
335 return self.popleft(index).left
337 shifted = len(self) + index
338 if shifted < 0:
339 raise IndexError(
340 "pdeque index {0} out of range {1}".format(index, len(self)),
341 )
342 return self.popleft(shifted).left
344 index = Sequence.index
346Sequence.register(PDeque)
347Hashable.register(PDeque)
350def pdeque(iterable=(), maxlen=None):
351 """
352 Return deque containing the elements of iterable. If maxlen is specified then
353 len(iterable) - maxlen elements are discarded from the left to if len(iterable) > maxlen.
355 >>> pdeque([1, 2, 3])
356 pdeque([1, 2, 3])
357 >>> pdeque([1, 2, 3, 4], maxlen=2)
358 pdeque([3, 4], maxlen=2)
359 """
360 t = tuple(iterable)
361 if maxlen is not None:
362 t = t[-maxlen:]
363 length = len(t)
364 pivot = int(length / 2)
365 left = plist(t[:pivot])
366 right = plist(t[pivot:], reverse=True)
367 return PDeque(left, right, length, maxlen)
369def dq(*elements):
370 """
371 Return deque containing all arguments.
373 >>> dq(1, 2, 3)
374 pdeque([1, 2, 3])
375 """
376 return pdeque(elements)