pythonic-fp-circulararray 5.3.2__py3-none-any.whl → 5.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pythonic_fp/circulararray/__init__.py +1 -2
- pythonic_fp/circulararray/__init__.pyi +0 -0
- pythonic_fp/circulararray/auto.py +275 -287
- pythonic_fp/circulararray/auto.pyi +12 -3
- pythonic_fp/circulararray/fixed.py +207 -219
- pythonic_fp/circulararray/fixed.pyi +15 -3
- {pythonic_fp_circulararray-5.3.2.dist-info → pythonic_fp_circulararray-5.4.0.dist-info}/METADATA +28 -13
- pythonic_fp_circulararray-5.4.0.dist-info/RECORD +11 -0
- pythonic_fp_circulararray-5.3.2.dist-info/RECORD +0 -10
- {pythonic_fp_circulararray-5.3.2.dist-info → pythonic_fp_circulararray-5.4.0.dist-info}/WHEEL +0 -0
- {pythonic_fp_circulararray-5.3.2.dist-info → pythonic_fp_circulararray-5.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -16,44 +16,46 @@
|
|
|
16
16
|
Variable Storage Capacity
|
|
17
17
|
=========================
|
|
18
18
|
|
|
19
|
-
Circular array with variable storage capacity
|
|
19
|
+
**Circular array with variable storage capacity.**
|
|
20
20
|
|
|
21
|
-
- O(1) pops either end
|
|
22
|
-
- O(1) amortized pushes either end
|
|
21
|
+
- O(1) pops either end
|
|
22
|
+
- O(1) amortized pushes either end
|
|
23
23
|
- O(1) indexing, fully supports slicing
|
|
24
24
|
- auto-resizing more storage capacity when necessary, manually compatible
|
|
25
|
-
- iterable,
|
|
25
|
+
- iterable, safely mutates while iterators iterating over previous state
|
|
26
26
|
- comparisons compare identity before equality, like builtins
|
|
27
27
|
- in boolean context, falsy when empty, otherwise truthy
|
|
28
|
-
-
|
|
28
|
+
- function ``ca`` produces auto-resizing circular array from arguments
|
|
29
29
|
|
|
30
30
|
"""
|
|
31
|
+
|
|
31
32
|
from collections.abc import Callable, Iterable, Iterator
|
|
32
|
-
from typing import cast, overload
|
|
33
|
+
from typing import cast, Final, overload
|
|
34
|
+
from pythonic_fp.gadgets.sentinels.novalue import NoValue
|
|
33
35
|
|
|
34
36
|
__all__ = ['CA', 'ca']
|
|
35
37
|
|
|
38
|
+
nada: Final[NoValue] = NoValue()
|
|
36
39
|
|
|
37
|
-
class CA[I]():
|
|
38
40
|
|
|
41
|
+
class CA[I]:
|
|
39
42
|
__slots__ = '_items', '_cnt', '_cap', '_front', '_rear'
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"""
|
|
46
|
-
Basically a list that can be grown from both ends
|
|
47
|
-
in O(1) time and space complexity.
|
|
48
|
-
|
|
49
|
-
:param items: optional iterable to initial populate circular array
|
|
50
|
-
:raises TypeError: if items is not Iterable
|
|
44
|
+
@overload
|
|
45
|
+
def __init__(self) -> None: ...
|
|
46
|
+
@overload
|
|
47
|
+
def __init__(self, items: Iterable[I]) -> None: ...
|
|
51
48
|
|
|
49
|
+
def __init__(self, items: Iterable[I | NoValue] | NoValue = nada) -> None:
|
|
50
|
+
"""
|
|
51
|
+
:param items: "Optional" iterable to initial populate circular array.
|
|
52
|
+
:raises TypeError: When ``items`` not Iterable.
|
|
52
53
|
"""
|
|
53
|
-
if items is
|
|
54
|
-
self._items: list[I |
|
|
54
|
+
if items is nada:
|
|
55
|
+
self._items: list[I | NoValue] = [nada, nada]
|
|
55
56
|
else:
|
|
56
|
-
|
|
57
|
+
values: list[I | NoValue] = list(cast(Iterable[I | NoValue], items))
|
|
58
|
+
self._items = [nada] + values + [nada]
|
|
57
59
|
self._cap = cap = len(self._items)
|
|
58
60
|
self._cnt = cap - 2
|
|
59
61
|
if cap == 2:
|
|
@@ -66,88 +68,93 @@ class CA[I]():
|
|
|
66
68
|
def _double_storage_capacity(self) -> None:
|
|
67
69
|
if self._front <= self._rear:
|
|
68
70
|
(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
self._items,
|
|
72
|
+
self._cap,
|
|
71
73
|
) = (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
self._items + [nada] * self._cap,
|
|
75
|
+
self._cap * 2,
|
|
76
|
+
)
|
|
75
77
|
else:
|
|
76
78
|
(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
self._items,
|
|
80
|
+
self._front,
|
|
81
|
+
self._cap,
|
|
80
82
|
) = (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
self._items[: self._front]
|
|
84
|
+
+ [nada] * self._cap
|
|
85
|
+
+ self._items[self._front :],
|
|
86
|
+
self._front + self._cap,
|
|
87
|
+
2 * self._cap,
|
|
88
|
+
)
|
|
85
89
|
|
|
86
90
|
def _compact_storage_capacity(self) -> None:
|
|
87
91
|
match self._cnt:
|
|
88
92
|
case 0:
|
|
89
93
|
(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
self._cap,
|
|
95
|
+
self._front,
|
|
96
|
+
self._rear,
|
|
97
|
+
self._items,
|
|
94
98
|
) = (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
2,
|
|
100
|
+
0,
|
|
101
|
+
1,
|
|
102
|
+
[nada, nada],
|
|
103
|
+
)
|
|
100
104
|
case 1:
|
|
101
105
|
(
|
|
106
|
+
self._cap,
|
|
107
|
+
self._front,
|
|
108
|
+
self._rear,
|
|
109
|
+
self._items,
|
|
110
|
+
) = (
|
|
111
|
+
3,
|
|
112
|
+
1,
|
|
113
|
+
1,
|
|
114
|
+
[nada, self._items[self._front], nada],
|
|
115
|
+
)
|
|
116
|
+
case _:
|
|
117
|
+
if self._front <= self._rear:
|
|
118
|
+
(
|
|
102
119
|
self._cap,
|
|
103
120
|
self._front,
|
|
104
121
|
self._rear,
|
|
105
122
|
self._items,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
1,
|
|
123
|
+
) = (
|
|
124
|
+
self._cnt + 2,
|
|
109
125
|
1,
|
|
110
|
-
|
|
126
|
+
self._cnt,
|
|
127
|
+
[nada] + self._items[self._front : self._rear + 1] + [nada],
|
|
111
128
|
)
|
|
112
|
-
case _:
|
|
113
|
-
if self._front <= self._rear:
|
|
114
|
-
(
|
|
115
|
-
self._cap,
|
|
116
|
-
self._front,
|
|
117
|
-
self._rear,
|
|
118
|
-
self._items,
|
|
119
|
-
) = (
|
|
120
|
-
self._cnt + 2,
|
|
121
|
-
1,
|
|
122
|
-
self._cnt,
|
|
123
|
-
[None] + self._items[self._front : self._rear + 1] + [None],
|
|
124
|
-
)
|
|
125
129
|
else:
|
|
126
130
|
(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
self._cap,
|
|
132
|
+
self._front,
|
|
133
|
+
self._rear,
|
|
134
|
+
self._items,
|
|
131
135
|
) = (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
self._cnt + 2,
|
|
137
|
+
1,
|
|
138
|
+
self._cnt,
|
|
139
|
+
[nada]
|
|
140
|
+
+ self._items[self._front :]
|
|
141
|
+
+ self._items[: self._rear + 1]
|
|
142
|
+
+ [nada],
|
|
143
|
+
)
|
|
137
144
|
|
|
138
145
|
def __iter__(self) -> Iterator[I]:
|
|
139
146
|
if self._cnt > 0:
|
|
140
147
|
(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
148
|
+
capacity,
|
|
149
|
+
rear,
|
|
150
|
+
position,
|
|
151
|
+
current_state,
|
|
145
152
|
) = (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
self._cap,
|
|
154
|
+
self._rear,
|
|
155
|
+
self._front,
|
|
156
|
+
self._items.copy(),
|
|
157
|
+
)
|
|
151
158
|
|
|
152
159
|
while position != rear:
|
|
153
160
|
yield cast(I, current_state[position])
|
|
@@ -157,16 +164,16 @@ class CA[I]():
|
|
|
157
164
|
def __reversed__(self) -> Iterator[I]:
|
|
158
165
|
if self._cnt > 0:
|
|
159
166
|
(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
167
|
+
capacity,
|
|
168
|
+
front,
|
|
169
|
+
position,
|
|
170
|
+
current_state,
|
|
164
171
|
) = (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
172
|
+
self._cap,
|
|
173
|
+
self._front,
|
|
174
|
+
self._rear,
|
|
175
|
+
self._items.copy(),
|
|
176
|
+
)
|
|
170
177
|
|
|
171
178
|
while position != front:
|
|
172
179
|
yield cast(I, current_state[position])
|
|
@@ -188,9 +195,9 @@ class CA[I]():
|
|
|
188
195
|
@overload
|
|
189
196
|
def __getitem__(self, idx: int) -> I: ...
|
|
190
197
|
@overload
|
|
191
|
-
def __getitem__(self, idx: slice) ->
|
|
198
|
+
def __getitem__(self, idx: slice) -> 'CA[I]': ...
|
|
192
199
|
|
|
193
|
-
def __getitem__(self, idx: int | slice) -> I |
|
|
200
|
+
def __getitem__(self, idx: int | slice) -> I | 'CA[I]':
|
|
194
201
|
if isinstance(idx, slice):
|
|
195
202
|
return CA(list(self)[idx])
|
|
196
203
|
|
|
@@ -222,18 +229,18 @@ class CA[I]():
|
|
|
222
229
|
item_list[idx] = vals
|
|
223
230
|
_ca = CA(item_list)
|
|
224
231
|
(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
self._items,
|
|
233
|
+
self._cnt,
|
|
234
|
+
self._cap,
|
|
235
|
+
self._front,
|
|
236
|
+
self._rear,
|
|
230
237
|
) = (
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
_ca._items,
|
|
239
|
+
_ca._cnt,
|
|
240
|
+
_ca._cap,
|
|
241
|
+
_ca._front,
|
|
242
|
+
_ca._rear,
|
|
243
|
+
)
|
|
237
244
|
return
|
|
238
245
|
|
|
239
246
|
msg = 'must assign iterable to extended slice'
|
|
@@ -263,18 +270,18 @@ class CA[I]():
|
|
|
263
270
|
del item_list[idx]
|
|
264
271
|
_ca = CA(item_list)
|
|
265
272
|
(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
273
|
+
self._items,
|
|
274
|
+
self._cnt,
|
|
275
|
+
self._cap,
|
|
276
|
+
self._front,
|
|
277
|
+
self._rear,
|
|
271
278
|
) = (
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
279
|
+
_ca._items,
|
|
280
|
+
_ca._cnt,
|
|
281
|
+
_ca._cap,
|
|
282
|
+
_ca._front,
|
|
283
|
+
_ca._rear,
|
|
284
|
+
)
|
|
278
285
|
del _ca
|
|
279
286
|
|
|
280
287
|
def __eq__(self, other: object) -> bool:
|
|
@@ -284,157 +291,153 @@ class CA[I]():
|
|
|
284
291
|
return False
|
|
285
292
|
|
|
286
293
|
(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
294
|
+
front1,
|
|
295
|
+
count1,
|
|
296
|
+
capacity1,
|
|
297
|
+
front2,
|
|
298
|
+
count2,
|
|
299
|
+
capacity2,
|
|
293
300
|
) = (
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
+
self._front,
|
|
302
|
+
self._cnt,
|
|
303
|
+
self._cap,
|
|
304
|
+
other._front,
|
|
305
|
+
other._cnt,
|
|
306
|
+
other._cap,
|
|
307
|
+
)
|
|
301
308
|
|
|
302
309
|
if count1 != count2:
|
|
303
310
|
return False
|
|
304
311
|
|
|
305
312
|
for nn in range(count1):
|
|
306
|
-
if
|
|
313
|
+
if (
|
|
314
|
+
self._items[(front1 + nn) % capacity1]
|
|
315
|
+
is other._items[(front2 + nn) % capacity2]
|
|
316
|
+
):
|
|
307
317
|
continue
|
|
308
|
-
if
|
|
318
|
+
if (
|
|
319
|
+
self._items[(front1 + nn) % capacity1]
|
|
320
|
+
!= other._items[(front2 + nn) % capacity2]
|
|
321
|
+
):
|
|
309
322
|
return False
|
|
310
323
|
return True
|
|
311
324
|
|
|
312
325
|
def pushl(self, *items: I) -> None:
|
|
313
|
-
"""
|
|
314
|
-
Push items onto left side (front) of circular array.
|
|
315
|
-
|
|
316
|
-
:param items: items pushed onto circular array from left
|
|
326
|
+
"""Push ``items`` on from left.
|
|
317
327
|
|
|
328
|
+
:param items: Items pushed onto circular array from left (front).
|
|
318
329
|
"""
|
|
319
330
|
for item in items:
|
|
320
331
|
if self._cnt == self._cap:
|
|
321
332
|
self._double_storage_capacity()
|
|
322
333
|
(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
334
|
+
self._front,
|
|
335
|
+
self._items[self._front],
|
|
336
|
+
self._cnt,
|
|
326
337
|
) = (
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
338
|
+
(self._front - 1) % self._cap,
|
|
339
|
+
item,
|
|
340
|
+
self._cnt + 1,
|
|
341
|
+
)
|
|
331
342
|
|
|
332
343
|
def pushr(self, *items: I) -> None:
|
|
333
|
-
"""
|
|
334
|
-
Push items onto right side (rear) of circular array.
|
|
335
|
-
|
|
336
|
-
:param items: items pushed onto circular array from right
|
|
344
|
+
"""Push ``items`` on from right.
|
|
337
345
|
|
|
346
|
+
:param items: Items pushed onto circular array from right (rear).
|
|
338
347
|
"""
|
|
339
348
|
for item in items:
|
|
340
349
|
if self._cnt == self._cap:
|
|
341
350
|
self._double_storage_capacity()
|
|
342
351
|
(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
352
|
+
self._rear,
|
|
353
|
+
self._items[self._rear],
|
|
354
|
+
self._cnt,
|
|
346
355
|
) = (
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
356
|
+
(self._rear + 1) % self._cap,
|
|
357
|
+
item,
|
|
358
|
+
self._cnt + 1,
|
|
359
|
+
)
|
|
351
360
|
|
|
352
361
|
def popl(self) -> I:
|
|
353
|
-
"""
|
|
354
|
-
Pop item off left side (front) of circular array.
|
|
355
|
-
|
|
356
|
-
:returns: item popped from left side of circular array
|
|
357
|
-
:raises ValueError: when called on an empty circular array
|
|
362
|
+
"""Pop single item off from left side.
|
|
358
363
|
|
|
364
|
+
:returns: Item popped from left side (front) of circular array.
|
|
365
|
+
:raises ValueError: When called on an empty circular array.
|
|
359
366
|
"""
|
|
360
367
|
if self._cnt > 1:
|
|
361
368
|
(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
369
|
+
d,
|
|
370
|
+
self._items[self._front],
|
|
371
|
+
self._front,
|
|
372
|
+
self._cnt,
|
|
366
373
|
) = (
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
374
|
+
self._items[self._front],
|
|
375
|
+
nada,
|
|
376
|
+
(self._front + 1) % self._cap,
|
|
377
|
+
self._cnt - 1,
|
|
378
|
+
)
|
|
372
379
|
elif self._cnt == 1:
|
|
373
380
|
(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
381
|
+
d,
|
|
382
|
+
self._items[self._front],
|
|
383
|
+
self._cnt,
|
|
384
|
+
self._front,
|
|
385
|
+
self._rear,
|
|
379
386
|
) = (
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
387
|
+
self._items[self._front],
|
|
388
|
+
nada,
|
|
389
|
+
0,
|
|
390
|
+
0,
|
|
391
|
+
self._cap - 1,
|
|
392
|
+
)
|
|
386
393
|
else:
|
|
387
394
|
msg = 'Method popl called on an empty CA'
|
|
388
395
|
raise ValueError(msg)
|
|
389
396
|
return cast(I, d)
|
|
390
397
|
|
|
391
398
|
def popr(self) -> I:
|
|
392
|
-
"""
|
|
393
|
-
Pop item off right side (rear) of circular array.
|
|
394
|
-
|
|
395
|
-
:return: item popped from right side of circular array
|
|
396
|
-
:raises ValueError: when called on an empty circular array
|
|
399
|
+
"""Pop single item off from right.
|
|
397
400
|
|
|
401
|
+
:returns: Item popped from right side (rear) of circular array.
|
|
402
|
+
:raises ValueError: When called on an empty circular array.
|
|
398
403
|
"""
|
|
399
404
|
if self._cnt > 1:
|
|
400
405
|
(
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
406
|
+
d,
|
|
407
|
+
self._items[self._rear],
|
|
408
|
+
self._rear,
|
|
409
|
+
self._cnt,
|
|
405
410
|
) = (
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
+
self._items[self._rear],
|
|
412
|
+
nada,
|
|
413
|
+
(self._rear - 1) % self._cap,
|
|
414
|
+
self._cnt - 1,
|
|
415
|
+
)
|
|
411
416
|
elif self._cnt == 1:
|
|
412
417
|
(
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
+
d,
|
|
419
|
+
self._items[self._front],
|
|
420
|
+
self._cnt,
|
|
421
|
+
self._front,
|
|
422
|
+
self._rear,
|
|
418
423
|
) = (
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
424
|
+
self._items[self._front],
|
|
425
|
+
nada,
|
|
426
|
+
0,
|
|
427
|
+
0,
|
|
428
|
+
self._cap - 1,
|
|
429
|
+
)
|
|
425
430
|
else:
|
|
426
431
|
msg = 'Method popr called on an empty CA'
|
|
427
432
|
raise ValueError(msg)
|
|
428
433
|
return cast(I, d)
|
|
429
434
|
|
|
430
435
|
def popld(self, default: I) -> I:
|
|
431
|
-
"""
|
|
432
|
-
|
|
433
|
-
a mandatory default value. "Safe" version of popl.
|
|
434
|
-
|
|
435
|
-
:param default: item returned if circular array is empty
|
|
436
|
-
:return: item popped from left side or default item if empty
|
|
436
|
+
"""Pop one item from left side of the circular array, provide
|
|
437
|
+
a mandatory default value. "Safe" version of ``popl``.
|
|
437
438
|
|
|
439
|
+
:param default: Item returned if circular array is empty.
|
|
440
|
+
:returns: Item popped from left side or default item if empty.
|
|
438
441
|
"""
|
|
439
442
|
try:
|
|
440
443
|
return self.popl()
|
|
@@ -442,13 +445,11 @@ class CA[I]():
|
|
|
442
445
|
return default
|
|
443
446
|
|
|
444
447
|
def poprd(self, default: I) -> I:
|
|
445
|
-
"""
|
|
446
|
-
|
|
447
|
-
a mandatory default value. "Safe" version of popr.
|
|
448
|
-
|
|
449
|
-
:param default: item returned if circular array is empty
|
|
450
|
-
:return: item popped from right side or default item if empty
|
|
448
|
+
"""Pop one item from right side of the circular array, provide
|
|
449
|
+
a mandatory default value. "Safe" version of ``popr``.
|
|
451
450
|
|
|
451
|
+
:param default: Item returned if circular array is empty.
|
|
452
|
+
:returns: Item popped from right side or default item if empty.
|
|
452
453
|
"""
|
|
453
454
|
try:
|
|
454
455
|
return self.popr()
|
|
@@ -456,12 +457,10 @@ class CA[I]():
|
|
|
456
457
|
return default
|
|
457
458
|
|
|
458
459
|
def poplt(self, maximum: int) -> tuple[I, ...]:
|
|
459
|
-
"""
|
|
460
|
-
Pop multiple items from left side of circular array.
|
|
461
|
-
|
|
462
|
-
:param maximum: maximum number of items to pop, may pop less if not enough items
|
|
463
|
-
:return: items in the order popped, left to right
|
|
460
|
+
"""Pop multiple items from left side of circular array.
|
|
464
461
|
|
|
462
|
+
:param maximum: Maximum number of items to pop, may pop less if not enough items.
|
|
463
|
+
:returns: Tuple of items in the order popped, left to right.
|
|
465
464
|
"""
|
|
466
465
|
item_list: list[I] = []
|
|
467
466
|
|
|
@@ -476,12 +475,10 @@ class CA[I]():
|
|
|
476
475
|
return tuple(item_list)
|
|
477
476
|
|
|
478
477
|
def poprt(self, maximum: int) -> tuple[I, ...]:
|
|
479
|
-
"""
|
|
480
|
-
Pop multiple items from right side of circular array.
|
|
481
|
-
|
|
482
|
-
:param maximum: maximum number of items to pop, may pop less if not enough items
|
|
483
|
-
:return: items in the order popped, right to left
|
|
478
|
+
"""Pop multiple items from right side of circular array.
|
|
484
479
|
|
|
480
|
+
:param maximum: Maximum number of items to pop, may pop less if not enough items.
|
|
481
|
+
:returns: Tuple of items in the order popped, right to left.
|
|
485
482
|
"""
|
|
486
483
|
item_list: list[I] = []
|
|
487
484
|
while maximum > 0:
|
|
@@ -494,11 +491,9 @@ class CA[I]():
|
|
|
494
491
|
return tuple(item_list)
|
|
495
492
|
|
|
496
493
|
def rotl(self, n: int = 1) -> None:
|
|
497
|
-
"""
|
|
498
|
-
Rotate items to the left.
|
|
499
|
-
|
|
500
|
-
:param n: number of times to shift elements to the left
|
|
494
|
+
"""Rotate items to the left.
|
|
501
495
|
|
|
496
|
+
:param n: Number of times to shift elements to the left.
|
|
502
497
|
"""
|
|
503
498
|
if self._cnt < 2:
|
|
504
499
|
return
|
|
@@ -506,142 +501,135 @@ class CA[I]():
|
|
|
506
501
|
self.pushr(self.popl())
|
|
507
502
|
|
|
508
503
|
def rotr(self, n: int = 1) -> None:
|
|
509
|
-
"""
|
|
510
|
-
Rotate items to the right.
|
|
511
|
-
|
|
512
|
-
:param n: number of times to shift elements to the right
|
|
504
|
+
"""Rotate items to the right.
|
|
513
505
|
|
|
506
|
+
:param n: Number of times to shift elements to the right.
|
|
514
507
|
"""
|
|
515
508
|
if self._cnt < 2:
|
|
516
509
|
return
|
|
517
510
|
for _ in range(n, 0, -1):
|
|
518
511
|
self.pushl(self.popr())
|
|
519
512
|
|
|
520
|
-
def map[U](self, f: Callable[[I], U]) ->
|
|
521
|
-
"""
|
|
522
|
-
Apply function f over the circular array's contents,
|
|
523
|
-
|
|
524
|
-
:param f: callable from type I to type U
|
|
525
|
-
:returns: new circular array instance
|
|
513
|
+
def map[U](self, f: Callable[[I], U]) -> 'CA[U]':
|
|
514
|
+
"""Apply function ``f`` over the circular array's contents.
|
|
526
515
|
|
|
516
|
+
:param f: Callable from type ``I`` to type ``U``.
|
|
517
|
+
:returns: New auto-resizing circular array instance.
|
|
527
518
|
"""
|
|
528
519
|
return CA(map(f, self))
|
|
529
520
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
521
|
+
@overload
|
|
522
|
+
def foldl[L](self, f: Callable[[I, I], I]) -> I: ...
|
|
523
|
+
@overload
|
|
524
|
+
def foldl[L](self, f: Callable[[L, I], L], start: L) -> L: ...
|
|
533
525
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
:returns: reduced value produced by the left fold
|
|
537
|
-
:raises ValueError: when circular array empty and no starting item given
|
|
526
|
+
def foldl[L](self, f: Callable[[L, I], L], start: L | NoValue = NoValue()) -> L:
|
|
527
|
+
"""Fold left with a function and optional starting item.
|
|
538
528
|
|
|
529
|
+
:param f: Folding function, first argument to ``f`` is for the accumulator.
|
|
530
|
+
:param start: "Optional" starting item.
|
|
531
|
+
:returns: Reduced value produced by the left fold.
|
|
532
|
+
:raises ValueError: When circular array empty and no starting item given.
|
|
539
533
|
"""
|
|
540
534
|
if self._cnt == 0:
|
|
541
|
-
if start is
|
|
535
|
+
if start is nada:
|
|
542
536
|
msg = 'Method foldl called on an empty CA without a start item.'
|
|
543
537
|
raise ValueError(msg)
|
|
544
|
-
return start
|
|
538
|
+
return cast(L, start)
|
|
545
539
|
|
|
546
|
-
if start is
|
|
540
|
+
if start is nada:
|
|
547
541
|
acc = cast(L, self[0]) # in this case D = L
|
|
548
542
|
for idx in range(1, self._cnt):
|
|
549
543
|
acc = f(acc, self[idx])
|
|
550
544
|
return acc
|
|
551
545
|
|
|
552
|
-
acc = start
|
|
546
|
+
acc = cast(L, start)
|
|
553
547
|
for d in self:
|
|
554
548
|
acc = f(acc, d)
|
|
555
549
|
return acc
|
|
556
550
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
551
|
+
@overload
|
|
552
|
+
def foldr[R](self, f: Callable[[I, I], I]) -> I: ...
|
|
553
|
+
@overload
|
|
554
|
+
def foldr[R](self, f: Callable[[I, R], R], start: R) -> R: ...
|
|
560
555
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
:returns: reduced value produced by the right fold
|
|
564
|
-
:raises ValueError: when circular array empty and no starting item given
|
|
556
|
+
def foldr[R](self, f: Callable[[I, R], R], start: R | NoValue = nada) -> R:
|
|
557
|
+
"""Fold right with a function and an optional starting item.
|
|
565
558
|
|
|
559
|
+
:param f: Folding function, second argument to ``f`` is for the accumulator.
|
|
560
|
+
:param start: "Optional" starting item.
|
|
561
|
+
:returns: Reduced value produced by the right fold.
|
|
562
|
+
:raises ValueError: When circular array empty and no starting item given.
|
|
566
563
|
"""
|
|
567
564
|
if self._cnt == 0:
|
|
568
|
-
if start is
|
|
565
|
+
if start is nada:
|
|
569
566
|
msg = 'Method foldr called on empty CA without initial value.'
|
|
570
567
|
raise ValueError(msg)
|
|
571
|
-
return start
|
|
568
|
+
return cast(R, start)
|
|
572
569
|
|
|
573
|
-
if start is
|
|
570
|
+
if start is nada:
|
|
574
571
|
acc = cast(R, self[-1]) # in this case D = R
|
|
575
572
|
for idx in range(self._cnt - 2, -1, -1):
|
|
576
573
|
acc = f(self[idx], acc)
|
|
577
574
|
return acc
|
|
578
575
|
|
|
579
|
-
acc = start
|
|
576
|
+
acc = cast(R, start)
|
|
580
577
|
for d in reversed(self):
|
|
581
578
|
acc = f(d, acc)
|
|
582
579
|
return acc
|
|
583
580
|
|
|
584
581
|
def capacity(self) -> int:
|
|
585
|
-
"""
|
|
586
|
-
Return current storage capacity of the circular array.
|
|
587
|
-
|
|
588
|
-
:returns: current storage capacity
|
|
582
|
+
"""Return current storage capacity of the circular array.
|
|
589
583
|
|
|
584
|
+
:returns: Current storage capacity.
|
|
590
585
|
"""
|
|
591
586
|
return self._cap
|
|
592
587
|
|
|
593
588
|
def empty(self) -> None:
|
|
594
|
-
"""
|
|
595
|
-
Empty the circular array, keep current storage capacity."""
|
|
589
|
+
"""Empty the circular array, keep current storage capacity."""
|
|
596
590
|
(
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
591
|
+
self._items,
|
|
592
|
+
self._front,
|
|
593
|
+
self._rear,
|
|
594
|
+
self._cnt,
|
|
601
595
|
) = (
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
596
|
+
[nada] * self._cap,
|
|
597
|
+
0,
|
|
598
|
+
self._cap - 1,
|
|
599
|
+
0,
|
|
600
|
+
)
|
|
607
601
|
|
|
608
602
|
def fraction_filled(self) -> float:
|
|
609
|
-
"""
|
|
610
|
-
Find fraction of the storage capacity which is filled.
|
|
611
|
-
|
|
612
|
-
:returns: the ratio count/capacity
|
|
603
|
+
"""Find fraction of the storage capacity which is filled.
|
|
613
604
|
|
|
605
|
+
:returns: The ratio count/capacity.
|
|
614
606
|
"""
|
|
615
607
|
return self._cnt / self._cap
|
|
616
608
|
|
|
617
609
|
def resize(self, minimum_capacity: int = 2) -> None:
|
|
618
|
-
"""
|
|
619
|
-
Compact circular array and, if necessary, resize to a minimum
|
|
610
|
+
"""Compact circular array and, if necessary, resize to a minimum
|
|
620
611
|
storage capacity. To just compact the circular array, do not
|
|
621
612
|
provide ``minimum_capacity``.
|
|
622
613
|
|
|
623
|
-
:param minimum_capacity:
|
|
624
|
-
|
|
614
|
+
:param minimum_capacity: Minimum storage capacity to compact the circular array.
|
|
625
615
|
"""
|
|
626
616
|
self._compact_storage_capacity()
|
|
627
617
|
if (min_cap := minimum_capacity) > self._cap:
|
|
628
618
|
(
|
|
629
|
-
|
|
630
|
-
|
|
619
|
+
self._cap,
|
|
620
|
+
self._items,
|
|
631
621
|
) = (
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
622
|
+
min_cap,
|
|
623
|
+
self._items + [nada] * (min_cap - self._cap),
|
|
624
|
+
)
|
|
635
625
|
if self._cnt == 0:
|
|
636
626
|
self._front, self._rear = 0, self._cap - 1
|
|
637
627
|
|
|
638
628
|
|
|
639
629
|
def ca[T](*items: T) -> CA[T]:
|
|
640
|
-
"""
|
|
641
|
-
Produce circular array from a variable number of arguments.
|
|
642
|
-
|
|
643
|
-
:param items: initial items for a new circular array
|
|
644
|
-
:returns: new variable storage capacity circular array
|
|
630
|
+
"""Produce circular array from a variable number of arguments.
|
|
645
631
|
|
|
632
|
+
:param items: Initial items for a new auto-resizing circular array.
|
|
633
|
+
:returns: New variable storage capacity circular array.
|
|
646
634
|
"""
|
|
647
635
|
return CA(items)
|