dtools.circular-array 3.12.1__py3-none-any.whl → 3.14.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.
- dtools/{circular_array/ca.py → circular_array.py} +59 -47
- {dtools_circular_array-3.12.1.dist-info → dtools_circular_array-3.14.0.dist-info}/METADATA +27 -24
- dtools_circular_array-3.14.0.dist-info/RECORD +5 -0
- dtools/circular_array/__init__.py +0 -33
- dtools/circular_array/py.typed +0 -0
- dtools_circular_array-3.12.1.dist-info/RECORD +0 -7
- {dtools_circular_array-3.12.1.dist-info → dtools_circular_array-3.14.0.dist-info}/WHEEL +0 -0
- {dtools_circular_array-3.12.1.dist-info → dtools_circular_array-3.14.0.dist-info}/licenses/LICENSE +0 -0
@@ -12,41 +12,51 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
"""###
|
15
|
+
"""### An indexable circular array data structure.
|
16
|
+
|
17
|
+
An indexable, sliceable, auto-resizing circular array
|
18
|
+
data structure with amortized O(1) pushes and pops either end.
|
19
|
+
|
20
|
+
"""
|
16
21
|
|
17
22
|
from __future__ import annotations
|
23
|
+
|
24
|
+
__author__ = 'Geoffrey R. Scheller'
|
25
|
+
__copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
|
26
|
+
__license__ = 'Apache License 2.0'
|
27
|
+
|
18
28
|
from collections.abc import Callable, Iterable, Iterator
|
19
29
|
from typing import cast, Never, overload, TypeVar
|
20
30
|
|
21
31
|
__all__ = ['CA', 'ca']
|
22
32
|
|
23
|
-
D = TypeVar('D')
|
24
|
-
L = TypeVar('L') # ignored by both MyPy and Python. Makes linters unhappy
|
25
|
-
R = TypeVar('R') # when these are used on function and method signatures due
|
26
|
-
U = TypeVar('U') # to "redefined-outer-name" warnings. Function and method
|
27
|
-
T = TypeVar('T') # signatures do not support variance and bounds constraints.
|
33
|
+
D = TypeVar('D')
|
28
34
|
|
29
35
|
|
30
|
-
class CA[D]
|
36
|
+
class CA[D]:
|
31
37
|
"""Indexable circular array data structure
|
32
38
|
|
33
|
-
- generic, stateful data structure
|
34
|
-
- lowercase class name chosen to match nomenclature for builtins
|
35
|
-
- like `list` and `tuple`
|
39
|
+
- generic, stateful, invariant data structure
|
36
40
|
- amortized O(1) pushing and popping from either end
|
37
41
|
- O(1) random access any element
|
38
42
|
- will resize itself as needed
|
39
43
|
- sliceable
|
40
44
|
- makes defensive copies of contents for the purposes of iteration
|
41
|
-
- in boolean context returns
|
42
|
-
|
45
|
+
- in boolean context returns
|
46
|
+
- `True` when not empty
|
47
|
+
- `False` when empty
|
48
|
+
- in comparisons compare identity before equality, like builtins do
|
43
49
|
- raises `IndexError` for out-of-bounds indexing
|
44
|
-
- raises `ValueError` for popping from or folding an empty `
|
50
|
+
- raises `ValueError` for popping from or folding an empty `CA`
|
45
51
|
|
46
52
|
"""
|
47
53
|
|
48
54
|
__slots__ = '_data', '_cnt', '_cap', '_front', '_rear'
|
49
55
|
|
56
|
+
L = TypeVar('L')
|
57
|
+
R = TypeVar('R')
|
58
|
+
U = TypeVar('U')
|
59
|
+
|
50
60
|
def __init__(self, *dss: Iterable[D]) -> None:
|
51
61
|
if len(dss) < 2:
|
52
62
|
self._data: list[D | None] = (
|
@@ -77,7 +87,6 @@ class CA[D]():
|
|
77
87
|
self._front, self._cap = self._front + self._cap, 2 * self._cap
|
78
88
|
|
79
89
|
def _compact_storage_capacity(self) -> None:
|
80
|
-
"""Compact the CA."""
|
81
90
|
match self._cnt:
|
82
91
|
case 0:
|
83
92
|
self._cap, self._front, self._rear, self._data = 2, 0, 1, [None, None]
|
@@ -211,11 +220,11 @@ class CA[D]():
|
|
211
220
|
raise IndexError(msg1 + msg2 + msg3)
|
212
221
|
|
213
222
|
@overload
|
214
|
-
def __delitem__(self, idx: int) -> None: ...
|
223
|
+
def __delitem__(self, idx: int, /) -> None: ...
|
215
224
|
@overload
|
216
|
-
def __delitem__(self, idx: slice) -> None: ...
|
225
|
+
def __delitem__(self, idx: slice, /) -> None: ...
|
217
226
|
|
218
|
-
def __delitem__(self, idx: int | slice) -> None:
|
227
|
+
def __delitem__(self, idx: int | slice, /) -> None:
|
219
228
|
data = list(self)
|
220
229
|
del data[idx]
|
221
230
|
_ca = CA(data)
|
@@ -259,7 +268,11 @@ class CA[D]():
|
|
259
268
|
return True
|
260
269
|
|
261
270
|
def pushl(self, *ds: D) -> None:
|
262
|
-
"""Push
|
271
|
+
"""Push left.
|
272
|
+
|
273
|
+
- push data from the left onto the CA
|
274
|
+
|
275
|
+
"""
|
263
276
|
for d in ds:
|
264
277
|
if self._cnt == self._cap:
|
265
278
|
self._double_storage_capacity()
|
@@ -267,7 +280,11 @@ class CA[D]():
|
|
267
280
|
self._data[self._front], self._cnt = d, self._cnt + 1
|
268
281
|
|
269
282
|
def pushr(self, *ds: D) -> None:
|
270
|
-
"""Push
|
283
|
+
"""Push right.
|
284
|
+
|
285
|
+
- push data from the right onto the CA
|
286
|
+
|
287
|
+
"""
|
271
288
|
for d in ds:
|
272
289
|
if self._cnt == self._cap:
|
273
290
|
self._double_storage_capacity()
|
@@ -275,9 +292,10 @@ class CA[D]():
|
|
275
292
|
self._data[self._rear], self._cnt = d, self._cnt + 1
|
276
293
|
|
277
294
|
def popl(self) -> D | Never:
|
278
|
-
"""Pop
|
295
|
+
"""Pop left.
|
279
296
|
|
280
|
-
|
297
|
+
- pop one value off the left side of the `CA`
|
298
|
+
- raises `ValueError` when called on an empty `CA`
|
281
299
|
|
282
300
|
"""
|
283
301
|
if self._cnt > 1:
|
@@ -301,9 +319,10 @@ class CA[D]():
|
|
301
319
|
return cast(D, d)
|
302
320
|
|
303
321
|
def popr(self) -> D | Never:
|
304
|
-
"""Pop
|
322
|
+
"""Pop right
|
305
323
|
|
306
|
-
|
324
|
+
- pop one value off the right side of the `CA`
|
325
|
+
- raises `ValueError` when called on an empty `CA`
|
307
326
|
|
308
327
|
"""
|
309
328
|
if self._cnt > 1:
|
@@ -330,7 +349,7 @@ class CA[D]():
|
|
330
349
|
"""Pop one value from left, provide a mandatory default value.
|
331
350
|
|
332
351
|
- safe version of popl
|
333
|
-
- returns
|
352
|
+
- returns the default value if `CA` is empty
|
334
353
|
|
335
354
|
"""
|
336
355
|
try:
|
@@ -342,7 +361,7 @@ class CA[D]():
|
|
342
361
|
"""Pop one value from right, provide a mandatory default value.
|
343
362
|
|
344
363
|
- safe version of popr
|
345
|
-
- returns
|
364
|
+
- returns the default value if `CA` is empty
|
346
365
|
|
347
366
|
"""
|
348
367
|
try:
|
@@ -353,9 +372,8 @@ class CA[D]():
|
|
353
372
|
def poplt(self, maximum: int, /) -> tuple[D, ...]:
|
354
373
|
"""Pop multiple values from left side of `CA`.
|
355
374
|
|
356
|
-
- returns the results in a tuple of type
|
357
|
-
-
|
358
|
-
- pop no more that `max` values
|
375
|
+
- returns the results in a tuple of type
|
376
|
+
- pop no more that `maximum` values
|
359
377
|
- will pop less if `CA` becomes empty
|
360
378
|
|
361
379
|
"""
|
@@ -374,9 +392,8 @@ class CA[D]():
|
|
374
392
|
def poprt(self, maximum: int, /) -> tuple[D, ...]:
|
375
393
|
"""Pop multiple values from right side of `CA`.
|
376
394
|
|
377
|
-
- returns the results in a tuple
|
378
|
-
-
|
379
|
-
- pop no more that `max` values
|
395
|
+
- returns the results in a tuple
|
396
|
+
- pop no more that `maximum` values
|
380
397
|
- will pop less if `CA` becomes empty
|
381
398
|
|
382
399
|
"""
|
@@ -392,34 +409,31 @@ class CA[D]():
|
|
392
409
|
return tuple(ds)
|
393
410
|
|
394
411
|
def rotl(self, n: int = 1, /) -> None:
|
395
|
-
"""Rotate `CA`
|
412
|
+
"""Rotate `CA` components to the left n times."""
|
396
413
|
if self._cnt < 2:
|
397
414
|
return
|
398
415
|
for _ in range(n, 0, -1):
|
399
416
|
self.pushr(self.popl())
|
400
417
|
|
401
418
|
def rotr(self, n: int = 1, /) -> None:
|
402
|
-
"""Rotate `CA`
|
419
|
+
"""Rotate `CA` components to the right n times."""
|
403
420
|
if self._cnt < 2:
|
404
421
|
return
|
405
422
|
for _ in range(n, 0, -1):
|
406
423
|
self.pushl(self.popr())
|
407
424
|
|
408
425
|
def map[U](self, f: Callable[[D], U], /) -> CA[U]:
|
409
|
-
"""Apply function f over
|
426
|
+
"""Apply function `f` over the `CA` contents,
|
410
427
|
|
411
|
-
-
|
412
|
-
- returns a new instance of type `CA[~U]`
|
428
|
+
- returns a new `CA` instance
|
413
429
|
|
414
430
|
"""
|
415
431
|
return CA(map(f, self))
|
416
432
|
|
417
|
-
def foldl[L](self, f: Callable[[L, D], L],
|
418
|
-
"""Left fold `CA`
|
433
|
+
def foldl[L](self, f: Callable[[L, D], L], initial: L | None = None, /) -> L:
|
434
|
+
"""Left fold `CA` with function `f` and an optional `initial` value.
|
419
435
|
|
420
|
-
-
|
421
|
-
- the first argument to `f` is for the accumulated value.
|
422
|
-
- parameter `initial` is an optional initial value
|
436
|
+
- first argument to `f` is for the accumulated value
|
423
437
|
- returns the reduced value of type `~L`
|
424
438
|
- note that `~L` and `~D` can be the same type
|
425
439
|
- if an initial value is not given then by necessity `~L = ~D`
|
@@ -443,12 +457,10 @@ class CA[D]():
|
|
443
457
|
acc = f(acc, d)
|
444
458
|
return acc
|
445
459
|
|
446
|
-
def foldr[R](self, f: Callable[[D, R], R],
|
447
|
-
"""Right fold `CA`
|
460
|
+
def foldr[R](self, f: Callable[[D, R], R], initial: R | None = None, /) -> R:
|
461
|
+
"""Right fold `CA` with function `f` and an optional `initial` value.
|
448
462
|
|
449
|
-
-
|
450
|
-
- the second argument to f is for the accumulated value
|
451
|
-
- parameter `initial` is an optional initial value
|
463
|
+
- second argument to f is for the accumulated value
|
452
464
|
- returns the reduced value of type `~R`
|
453
465
|
- note that `~R` and `~D` can be the same type
|
454
466
|
- if an initial value is not given then by necessity `~R = ~D`
|
@@ -497,6 +509,6 @@ class CA[D]():
|
|
497
509
|
self._front, self._rear = 0, self._cap - 1
|
498
510
|
|
499
511
|
|
500
|
-
def ca[
|
512
|
+
def ca[D](*ds: D) -> CA[D]:
|
501
513
|
"""Function to produce a `CA` array from a variable number of arguments."""
|
502
514
|
return CA(ds)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dtools.circular-array
|
3
|
-
Version: 3.
|
4
|
-
Summary: ###
|
5
|
-
Keywords: circular array,
|
3
|
+
Version: 3.14.0
|
4
|
+
Summary: ### An indexable circular array data structure.
|
5
|
+
Keywords: circular array,dequeue,pop,push,indexable,auto resizing
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
7
7
|
Requires-Python: >=3.12
|
8
8
|
Description-Content-Type: text/markdown
|
@@ -16,7 +16,8 @@ Classifier: Typing :: Typed
|
|
16
16
|
License-File: LICENSE
|
17
17
|
Requires-Dist: pytest >=8.3.5 ; extra == "test"
|
18
18
|
Project-URL: Changelog, https://github.com/grscheller/dtools-circular-array/blob/main/CHANGELOG.md
|
19
|
-
Project-URL: Documentation, https://grscheller.github.io/dtools-
|
19
|
+
Project-URL: Documentation, https://grscheller.github.io/dtools-namespace-projects/circular-array
|
20
|
+
Project-URL: Homepage, https://github.com/grscheller/dtools-namespace-projects/blob/main/README.md
|
20
21
|
Project-URL: Source, https://github.com/grscheller/dtools-circular-array
|
21
22
|
Provides-Extra: test
|
22
23
|
|
@@ -31,37 +32,41 @@ structure,
|
|
31
32
|
- **Detailed documentation**
|
32
33
|
- [Detailed API documentation][3] on *GH-Pages*
|
33
34
|
|
34
|
-
This project is part of the [Developer Tools for Python][4] **dtools
|
35
|
-
namespace
|
35
|
+
This project is part of the [Developer Tools for Python][4] **dtools**
|
36
|
+
namespace projects.
|
36
37
|
|
37
38
|
## Overview
|
38
39
|
|
39
40
|
- O(1) amortized pushes and pops either end.
|
40
41
|
- O(1) indexing
|
41
42
|
- fully supports slicing
|
42
|
-
- safely mutates
|
43
|
+
- safely mutates over previous state
|
43
44
|
|
44
45
|
### Module circular_array
|
45
46
|
|
46
|
-
A full featured
|
47
|
-
|
48
|
-
|
47
|
+
A full featured auto resizing circular array data structure. Double
|
48
|
+
sided, indexable, sliceable, and iterable. When iterated, uses cached
|
49
|
+
copies of its present state so that the circular array itself can safely
|
50
|
+
be mutated.
|
49
51
|
|
50
|
-
Useful either if used directly
|
51
|
-
|
52
|
+
Useful either if used directly like a Python list, or in a "has-a"
|
53
|
+
relationship when implementing other data structures.
|
52
54
|
|
53
55
|
- *module* dtools.circular_array
|
54
|
-
- *class*
|
55
|
-
|
56
|
+
- *class* `CA:` circular array data structure
|
57
|
+
- initializer takes 1 or 0 iterators
|
58
|
+
- like `list` or `set`
|
59
|
+
- *function* `ca`: produces a `CA` from function's arguments
|
60
|
+
- similar use case as syntactic constructs `[]` or `{}`
|
56
61
|
|
57
|
-
Above nomenclature modeled after
|
58
|
-
`
|
59
|
-
|
62
|
+
Above nomenclature modeled after builtin data types like `list`, where
|
63
|
+
`CA` and `ca` correspond respectfully to `list` and `[]` in their use
|
64
|
+
cases.
|
60
65
|
|
61
66
|
#### Usage
|
62
67
|
|
63
68
|
```python
|
64
|
-
from dtools.circular_array
|
69
|
+
from dtools.circular_array import CA, ca
|
65
70
|
|
66
71
|
ca1 = ca(1, 2, 3)
|
67
72
|
assert ca1.popl() == 1
|
@@ -79,8 +84,8 @@ syntactic sugar like `[]` or `{}`.
|
|
79
84
|
tup4 = ca2.poprt(4)
|
80
85
|
assert tup3 == (1, 2, 3)
|
81
86
|
assert tup4 == (10, 9, 8, 7)
|
82
|
-
assert ca2 ==
|
83
|
-
four, *rest =
|
87
|
+
assert ca2 == ca(4, 5, 6)
|
88
|
+
four, *rest = ca2.poplt(1000)
|
84
89
|
assert four == 4
|
85
90
|
assert rest == [5, 6]
|
86
91
|
assert len(ca2) == 0
|
@@ -94,10 +99,8 @@ syntactic sugar like `[]` or `{}`.
|
|
94
99
|
assert len(ca2) == 0
|
95
100
|
```
|
96
101
|
|
97
|
-
______________________________________________________________________
|
98
|
-
|
99
102
|
[1]: https://pypi.org/project/dtools.circular-array
|
100
103
|
[2]: https://github.com/grscheller/dtools-circular-array
|
101
|
-
[3]: https://grscheller.github.io/dtools-
|
102
|
-
[4]: https://github.com/grscheller/dtools-
|
104
|
+
[3]: https://grscheller.github.io/dtools-namespace-projects/circular-array
|
105
|
+
[4]: https://github.com/grscheller/dtools-namespace-projects/blob/main/README.md
|
103
106
|
|
@@ -0,0 +1,5 @@
|
|
1
|
+
dtools/circular_array.py,sha256=GJn30Xub6oSd2Ltiy76ccUytiNpVS43RJNU4Oax6EkM,16184
|
2
|
+
dtools_circular_array-3.14.0.dist-info/licenses/LICENSE,sha256=csqbZRvA3Nyuav1aszWvswE8CZtaKr-hMjjjcKqms7w,10774
|
3
|
+
dtools_circular_array-3.14.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
4
|
+
dtools_circular_array-3.14.0.dist-info/METADATA,sha256=PPfMaC0yPeiMRUp9rsXxFtEmw7mUVkJK-DAtffjKjhI,3597
|
5
|
+
dtools_circular_array-3.14.0.dist-info/RECORD,,
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# Copyright 2023-2025 Geoffrey R. Scheller
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
|
15
|
-
"""### Developer Tools - Circular Array Data Structure
|
16
|
-
|
17
|
-
Package for an indexable, sliceable, auto-resizing circular array
|
18
|
-
data structure with amortized O(1) pushes and pops either end.
|
19
|
-
|
20
|
-
Circular array data structure.
|
21
|
-
|
22
|
-
- *module* dtools.circular_array
|
23
|
-
- *class* dtools.circular_array.ca.CA
|
24
|
-
- initializer takes up to 1 iterable
|
25
|
-
- *function* dtools.circular_array.ca.ca
|
26
|
-
- factory function taking a variable number of arguments
|
27
|
-
|
28
|
-
"""
|
29
|
-
|
30
|
-
__version__ = '3.12.1'
|
31
|
-
__author__ = 'Geoffrey R. Scheller'
|
32
|
-
__copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
|
33
|
-
__license__ = 'Apache License 2.0'
|
dtools/circular_array/py.typed
DELETED
File without changes
|
@@ -1,7 +0,0 @@
|
|
1
|
-
dtools/circular_array/__init__.py,sha256=2CvFEORjHTaRYTteVH-Ifo7THhbttgif71VigBM-3-Y,1189
|
2
|
-
dtools/circular_array/ca.py,sha256=PRt-AOvR9WqwS6EdKFWXde7VxBc1yOD6kuCK_lXuWFQ,16683
|
3
|
-
dtools/circular_array/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
dtools_circular_array-3.12.1.dist-info/licenses/LICENSE,sha256=csqbZRvA3Nyuav1aszWvswE8CZtaKr-hMjjjcKqms7w,10774
|
5
|
-
dtools_circular_array-3.12.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
6
|
-
dtools_circular_array-3.12.1.dist-info/METADATA,sha256=9HGyzkYTReGc9LUbP_X1qbWEK-EQKVwA_gAaZdI_5cI,3495
|
7
|
-
dtools_circular_array-3.12.1.dist-info/RECORD,,
|
File without changes
|
{dtools_circular_array-3.12.1.dist-info → dtools_circular_array-3.14.0.dist-info}/licenses/LICENSE
RENAMED
File without changes
|