pkstruct 0.1.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.
- pkstruct/__init__.py +167 -0
- pkstruct/graphs/__init__.py +127 -0
- pkstruct/graphs/connectivity.py +157 -0
- pkstruct/graphs/directed.py +95 -0
- pkstruct/graphs/exceptions.py +63 -0
- pkstruct/graphs/graph.py +262 -0
- pkstruct/graphs/mst.py +118 -0
- pkstruct/graphs/scc.py +138 -0
- pkstruct/graphs/shortest_path.py +250 -0
- pkstruct/graphs/topo_sort.py +108 -0
- pkstruct/graphs/traversal.py +175 -0
- pkstruct/graphs/visualization.py +90 -0
- pkstruct/graphs/weighted.py +37 -0
- pkstruct/linear/__init__.py +95 -0
- pkstruct/linear/deques/__init__.py +33 -0
- pkstruct/linear/deques/deque.py +194 -0
- pkstruct/linear/deques/linked_deque.py +198 -0
- pkstruct/linear/exceptions.py +26 -0
- pkstruct/linear/linked_lists/__init__.py +5 -0
- pkstruct/linear/linked_lists/_base.py +608 -0
- pkstruct/linear/linked_lists/circular_linked_list.py +230 -0
- pkstruct/linear/linked_lists/doubly_linked_list.py +151 -0
- pkstruct/linear/linked_lists/nodes.py +68 -0
- pkstruct/linear/linked_lists/singly_linked_list.py +136 -0
- pkstruct/linear/queues/__init__.py +44 -0
- pkstruct/linear/queues/circular_queue.py +258 -0
- pkstruct/linear/queues/linked_queue.py +186 -0
- pkstruct/linear/queues/priority_queue.py +202 -0
- pkstruct/linear/queues/queue.py +174 -0
- pkstruct/linear/stacks/__init__.py +38 -0
- pkstruct/linear/stacks/array_stack.py +165 -0
- pkstruct/linear/stacks/linked_stack.py +168 -0
- pkstruct/linear/stacks/stack.py +158 -0
- pkstruct/linear/utils/__init__.py +18 -0
- pkstruct/linear/utils/benchmark.py +255 -0
- pkstruct/linear/utils/debug_tools.py +239 -0
- pkstruct/linear/utils/helpers.py +143 -0
- pkstruct/linear/utils/iterators.py +148 -0
- pkstruct/linear/visualization/__init__.py +0 -0
- pkstruct/linear/visualization/ascii_visualizer.py +114 -0
- pkstruct/linear/visualization/linked_list_visualizer.py +126 -0
- pkstruct/shared/__init__.py +67 -0
- pkstruct/shared/benchmarking/__init__.py +78 -0
- pkstruct/shared/debugging/__init__.py +69 -0
- pkstruct/shared/exceptions/__init__.py +59 -0
- pkstruct/shared/serializers/__init__.py +65 -0
- pkstruct/shared/threading/__init__.py +43 -0
- pkstruct/shared/validators/__init__.py +98 -0
- pkstruct/shared/visualization/__init__.py +21 -0
- pkstruct/trees/__init__.py +92 -0
- pkstruct/trees/avl.py +321 -0
- pkstruct/trees/balancing.py +253 -0
- pkstruct/trees/bplus.py +425 -0
- pkstruct/trees/bst.py +948 -0
- pkstruct/trees/btree.py +504 -0
- pkstruct/trees/exceptions.py +96 -0
- pkstruct/trees/fenwick_tree.py +312 -0
- pkstruct/trees/interval_tree.py +541 -0
- pkstruct/trees/node.py +356 -0
- pkstruct/trees/red_black.py +710 -0
- pkstruct/trees/segment_tree.py +398 -0
- pkstruct/trees/traversal.py +456 -0
- pkstruct/trees/tree_helpers.py +366 -0
- pkstruct/trees/utils/__init__.py +15 -0
- pkstruct/trees/utils/complexity_helpers.py +231 -0
- pkstruct/trees/visualization/__init__.py +0 -0
- pkstruct/trees/visualization/ascii_renderer.py +220 -0
- pkstruct/trees/visualization/tree_printer.py +129 -0
- pkstruct-0.1.0.dist-info/METADATA +482 -0
- pkstruct-0.1.0.dist-info/RECORD +72 -0
- pkstruct-0.1.0.dist-info/WHEEL +4 -0
- pkstruct-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pkstruct.linear
|
|
3
|
+
===============
|
|
4
|
+
|
|
5
|
+
Linear data structures module for the pkstruct ecosystem.
|
|
6
|
+
|
|
7
|
+
Provides production-grade implementations of linked lists, stacks, queues,
|
|
8
|
+
and deques with full thread safety, debug tracing, benchmarking, and
|
|
9
|
+
ASCII visualization support.
|
|
10
|
+
|
|
11
|
+
Classes
|
|
12
|
+
-------
|
|
13
|
+
SinglyLinkedList
|
|
14
|
+
A singly linked list with forward traversal.
|
|
15
|
+
DoublyLinkedList
|
|
16
|
+
A doubly linked list with both forward and backward traversal.
|
|
17
|
+
CircularLinkedList
|
|
18
|
+
A circular linked list maintaining the circular invariant.
|
|
19
|
+
ArrayStack
|
|
20
|
+
LIFO stack backed by a dynamic array.
|
|
21
|
+
LinkedStack
|
|
22
|
+
LIFO stack backed by ``SinglyLinkedList``.
|
|
23
|
+
LinkedQueue
|
|
24
|
+
FIFO queue backed by ``SinglyLinkedList``.
|
|
25
|
+
CircularQueue
|
|
26
|
+
Fixed-capacity FIFO queue backed by a ring buffer.
|
|
27
|
+
PriorityQueue
|
|
28
|
+
Min-heap priority queue.
|
|
29
|
+
LinkedDeque
|
|
30
|
+
Double-ended queue backed by ``DoublyLinkedList``.
|
|
31
|
+
|
|
32
|
+
Exceptions
|
|
33
|
+
----------
|
|
34
|
+
PkstructError, ValidationError, IndexOutOfRangeError, ValueNotFoundError,
|
|
35
|
+
EmptyStructureError, SerializationError, ConcurrencyError, InvalidRangeError,
|
|
36
|
+
QueueFullError
|
|
37
|
+
|
|
38
|
+
Example
|
|
39
|
+
-------
|
|
40
|
+
>>> from pkstruct.linear import SinglyLinkedList, LinkedStack
|
|
41
|
+
>>> ll = SinglyLinkedList()
|
|
42
|
+
>>> ll.insert(10)
|
|
43
|
+
>>> s = LinkedStack([1, 2, 3])
|
|
44
|
+
>>> s.pop()
|
|
45
|
+
3
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from pkstruct.linear.deques.linked_deque import LinkedDeque
|
|
49
|
+
from pkstruct.linear.exceptions import (
|
|
50
|
+
ConcurrencyError,
|
|
51
|
+
EmptyStructureError,
|
|
52
|
+
IndexOutOfRangeError,
|
|
53
|
+
InvalidRangeError,
|
|
54
|
+
PkstructError,
|
|
55
|
+
SerializationError,
|
|
56
|
+
ValidationError,
|
|
57
|
+
ValueNotFoundError,
|
|
58
|
+
)
|
|
59
|
+
from pkstruct.linear.linked_lists.circular_linked_list import CircularLinkedList
|
|
60
|
+
from pkstruct.linear.linked_lists.doubly_linked_list import DoublyLinkedList
|
|
61
|
+
from pkstruct.linear.linked_lists.singly_linked_list import SinglyLinkedList
|
|
62
|
+
from pkstruct.linear.queues.circular_queue import CircularQueue, QueueFullError
|
|
63
|
+
from pkstruct.linear.queues.linked_queue import LinkedQueue
|
|
64
|
+
from pkstruct.linear.queues.priority_queue import PriorityQueue
|
|
65
|
+
from pkstruct.linear.stacks.array_stack import ArrayStack
|
|
66
|
+
from pkstruct.linear.stacks.linked_stack import LinkedStack
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
# Linked list classes
|
|
70
|
+
"SinglyLinkedList",
|
|
71
|
+
"DoublyLinkedList",
|
|
72
|
+
"CircularLinkedList",
|
|
73
|
+
# Stack classes
|
|
74
|
+
"ArrayStack",
|
|
75
|
+
"LinkedStack",
|
|
76
|
+
# Queue classes
|
|
77
|
+
"LinkedQueue",
|
|
78
|
+
"CircularQueue",
|
|
79
|
+
"PriorityQueue",
|
|
80
|
+
# Deque classes
|
|
81
|
+
"LinkedDeque",
|
|
82
|
+
# Exceptions
|
|
83
|
+
"PkstructError",
|
|
84
|
+
"ValidationError",
|
|
85
|
+
"IndexOutOfRangeError",
|
|
86
|
+
"ValueNotFoundError",
|
|
87
|
+
"EmptyStructureError",
|
|
88
|
+
"SerializationError",
|
|
89
|
+
"ConcurrencyError",
|
|
90
|
+
"InvalidRangeError",
|
|
91
|
+
"QueueFullError",
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
__version__ = "0.1.0"
|
|
95
|
+
__author__ = "pkstruct contributors"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pkstruct.linear.deques
|
|
3
|
+
======================
|
|
4
|
+
|
|
5
|
+
Double-ended queue (deque) data structures for the pkstruct ecosystem.
|
|
6
|
+
|
|
7
|
+
Provides a concrete doubly-linked-list-backed deque and a shared abstract base.
|
|
8
|
+
|
|
9
|
+
Classes
|
|
10
|
+
-------
|
|
11
|
+
Deque
|
|
12
|
+
Abstract base class defining the deque interface.
|
|
13
|
+
LinkedDeque
|
|
14
|
+
Deque backed by ``DoublyLinkedList`` (O(1) at both ends, thread-safe).
|
|
15
|
+
|
|
16
|
+
Example
|
|
17
|
+
-------
|
|
18
|
+
>>> from pkstruct.linear.deques import LinkedDeque
|
|
19
|
+
>>> d = LinkedDeque([1, 2, 3])
|
|
20
|
+
>>> d.append(0, side="left")
|
|
21
|
+
>>> d.append(4, side="right")
|
|
22
|
+
>>> list(d)
|
|
23
|
+
[0, 1, 2, 3, 4]
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from pkstruct.linear.deques.deque import Deque
|
|
27
|
+
from pkstruct.linear.deques.linked_deque import LinkedDeque
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
# Deque classes
|
|
31
|
+
"Deque",
|
|
32
|
+
"LinkedDeque",
|
|
33
|
+
]
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pkstruct.linear.deques.deque
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
Abstract base class for all double-ended queue implementations.
|
|
5
|
+
|
|
6
|
+
Provides a formal interface contract. Every concrete deque in ``pkstruct``
|
|
7
|
+
derives from this class and implements all abstract methods.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from collections.abc import Iterator
|
|
14
|
+
from typing import Generic, Literal, TypeVar
|
|
15
|
+
|
|
16
|
+
T = TypeVar("T")
|
|
17
|
+
|
|
18
|
+
_Side = Literal["left", "right"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Deque(ABC, Generic[T]):
|
|
22
|
+
"""
|
|
23
|
+
Abstract base class for double-ended queue (deque) structures.
|
|
24
|
+
|
|
25
|
+
Subclasses must implement all abstract methods. The interface supports
|
|
26
|
+
O(1) append and pop at both ends, plus rotation and reversal.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
No parameters required at the abstract level.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
__slots__ = ()
|
|
34
|
+
|
|
35
|
+
# ------------------------------------------------------------------ #
|
|
36
|
+
# Required operations #
|
|
37
|
+
# ------------------------------------------------------------------ #
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def append(self, value: T, side: _Side = "right") -> None:
|
|
41
|
+
"""
|
|
42
|
+
Add *value* to one end of the deque.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
value:
|
|
47
|
+
The element to add.
|
|
48
|
+
side:
|
|
49
|
+
``"left"`` to prepend (front), ``"right"`` to append (rear).
|
|
50
|
+
"""
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def pop(self, side: _Side = "right") -> T:
|
|
55
|
+
"""
|
|
56
|
+
Remove and return an element from one end.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
side:
|
|
61
|
+
``"left"`` to remove from the front, ``"right"`` to remove from
|
|
62
|
+
the rear.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
T
|
|
67
|
+
|
|
68
|
+
Raises
|
|
69
|
+
------
|
|
70
|
+
EmptyStructureError
|
|
71
|
+
If the deque is empty.
|
|
72
|
+
"""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
@abstractmethod
|
|
76
|
+
def peek(self, side: _Side = "left") -> T:
|
|
77
|
+
"""
|
|
78
|
+
Return an element from one end without removing it.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
side:
|
|
83
|
+
``"left"`` to inspect the front, ``"right"`` to inspect the rear.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
T
|
|
88
|
+
|
|
89
|
+
Raises
|
|
90
|
+
------
|
|
91
|
+
EmptyStructureError
|
|
92
|
+
If the deque is empty.
|
|
93
|
+
"""
|
|
94
|
+
...
|
|
95
|
+
|
|
96
|
+
# ------------------------------------------------------------------ #
|
|
97
|
+
# Bulk operations #
|
|
98
|
+
# ------------------------------------------------------------------ #
|
|
99
|
+
|
|
100
|
+
@abstractmethod
|
|
101
|
+
def rotate(self, steps: int = 1) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Rotate the deque *steps* positions to the right.
|
|
104
|
+
|
|
105
|
+
Negative *steps* rotates to the left.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
steps:
|
|
110
|
+
Number of positions to rotate (default 1).
|
|
111
|
+
"""
|
|
112
|
+
...
|
|
113
|
+
|
|
114
|
+
@abstractmethod
|
|
115
|
+
def reverse(self) -> None:
|
|
116
|
+
"""Reverse the order of all elements in the deque."""
|
|
117
|
+
...
|
|
118
|
+
|
|
119
|
+
# ------------------------------------------------------------------ #
|
|
120
|
+
# Query operations #
|
|
121
|
+
# ------------------------------------------------------------------ #
|
|
122
|
+
|
|
123
|
+
@abstractmethod
|
|
124
|
+
def is_empty(self) -> bool:
|
|
125
|
+
"""
|
|
126
|
+
Return ``True`` if the deque contains no elements.
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
bool
|
|
131
|
+
"""
|
|
132
|
+
...
|
|
133
|
+
|
|
134
|
+
@abstractmethod
|
|
135
|
+
def size(self) -> int:
|
|
136
|
+
"""
|
|
137
|
+
Return the number of elements in the deque.
|
|
138
|
+
|
|
139
|
+
Returns
|
|
140
|
+
-------
|
|
141
|
+
int
|
|
142
|
+
"""
|
|
143
|
+
...
|
|
144
|
+
|
|
145
|
+
@abstractmethod
|
|
146
|
+
def clear(self) -> None:
|
|
147
|
+
"""Remove all elements from the deque."""
|
|
148
|
+
...
|
|
149
|
+
|
|
150
|
+
@abstractmethod
|
|
151
|
+
def copy(self) -> Deque[T]:
|
|
152
|
+
"""
|
|
153
|
+
Return a shallow copy of this deque.
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
Deque[T]
|
|
158
|
+
"""
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
@abstractmethod
|
|
162
|
+
def to_list(self) -> list[T]:
|
|
163
|
+
"""
|
|
164
|
+
Return a list of all elements, from left to right.
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
list[T]
|
|
169
|
+
"""
|
|
170
|
+
...
|
|
171
|
+
|
|
172
|
+
# ------------------------------------------------------------------ #
|
|
173
|
+
# Dunder methods #
|
|
174
|
+
# ------------------------------------------------------------------ #
|
|
175
|
+
|
|
176
|
+
@abstractmethod
|
|
177
|
+
def __iter__(self) -> Iterator[T]:
|
|
178
|
+
...
|
|
179
|
+
|
|
180
|
+
@abstractmethod
|
|
181
|
+
def __len__(self) -> int:
|
|
182
|
+
...
|
|
183
|
+
|
|
184
|
+
@abstractmethod
|
|
185
|
+
def __bool__(self) -> bool:
|
|
186
|
+
...
|
|
187
|
+
|
|
188
|
+
@abstractmethod
|
|
189
|
+
def __repr__(self) -> str:
|
|
190
|
+
...
|
|
191
|
+
|
|
192
|
+
@abstractmethod
|
|
193
|
+
def __eq__(self, other: object) -> bool:
|
|
194
|
+
...
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pkstruct.linear.deques.linked_deque
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
A double-ended queue backed by a ``DoublyLinkedList``.
|
|
5
|
+
|
|
6
|
+
``LinkedDeque`` composes the existing production-grade doubly-linked list,
|
|
7
|
+
giving O(1) append and pop at both ends without any node-management code.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from collections.abc import Iterator
|
|
13
|
+
from typing import Literal, TypeVar, cast
|
|
14
|
+
|
|
15
|
+
from pkstruct.linear.deques.deque import Deque
|
|
16
|
+
from pkstruct.linear.exceptions import EmptyStructureError
|
|
17
|
+
from pkstruct.linear.linked_lists.doubly_linked_list import DoublyLinkedList
|
|
18
|
+
from pkstruct.shared.threading import StructureLock
|
|
19
|
+
|
|
20
|
+
T = TypeVar("T")
|
|
21
|
+
|
|
22
|
+
_Side = Literal["left", "right"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class LinkedDeque(Deque[T]):
|
|
26
|
+
"""
|
|
27
|
+
A thread-safe double-ended queue implemented on top of ``DoublyLinkedList``.
|
|
28
|
+
|
|
29
|
+
The internal list stores elements from left to right. ``append(side="left")``
|
|
30
|
+
inserts at the head, ``append(side="right")`` appends at the tail — both O(1).
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
items:
|
|
35
|
+
Optional iterable of initial values (left-to-right order).
|
|
36
|
+
If omitted the deque starts empty.
|
|
37
|
+
|
|
38
|
+
Examples
|
|
39
|
+
--------
|
|
40
|
+
>>> d = LinkedDeque([1, 2, 3])
|
|
41
|
+
>>> d.append(0, side="left")
|
|
42
|
+
>>> d.append(4, side="right")
|
|
43
|
+
>>> d.pop(side="left")
|
|
44
|
+
0
|
|
45
|
+
>>> d.pop(side="right")
|
|
46
|
+
4
|
|
47
|
+
>>> list(d)
|
|
48
|
+
[1, 2, 3]
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
__slots__ = ("_list", "_lock")
|
|
52
|
+
|
|
53
|
+
def __init__(self, items: list[T] | None = None) -> None:
|
|
54
|
+
self._list: DoublyLinkedList[T] = DoublyLinkedList()
|
|
55
|
+
self._lock: StructureLock = StructureLock()
|
|
56
|
+
if items is not None:
|
|
57
|
+
for item in items:
|
|
58
|
+
self._list.insert(item)
|
|
59
|
+
|
|
60
|
+
# ------------------------------------------------------------------ #
|
|
61
|
+
# Core operations #
|
|
62
|
+
# ------------------------------------------------------------------ #
|
|
63
|
+
|
|
64
|
+
def append(self, value: T, side: _Side = "right") -> None:
|
|
65
|
+
"""
|
|
66
|
+
Add *value* to one end of the deque.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
value:
|
|
71
|
+
The element to add.
|
|
72
|
+
side:
|
|
73
|
+
``"left"`` to prepend, ``"right"`` to append (default).
|
|
74
|
+
"""
|
|
75
|
+
with self._lock:
|
|
76
|
+
if side == "left":
|
|
77
|
+
self._list.insert(value, position=0)
|
|
78
|
+
else:
|
|
79
|
+
self._list.insert(value)
|
|
80
|
+
|
|
81
|
+
def pop(self, side: _Side = "right") -> T:
|
|
82
|
+
"""
|
|
83
|
+
Remove and return an element from one end.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
side:
|
|
88
|
+
``"left"`` to remove from the front, ``"right"`` to remove from
|
|
89
|
+
the rear (default).
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
T
|
|
94
|
+
|
|
95
|
+
Raises
|
|
96
|
+
------
|
|
97
|
+
EmptyStructureError
|
|
98
|
+
If the deque is empty.
|
|
99
|
+
"""
|
|
100
|
+
with self._lock:
|
|
101
|
+
if self._list.is_empty():
|
|
102
|
+
raise EmptyStructureError("pop from an empty deque")
|
|
103
|
+
if side == "left":
|
|
104
|
+
return cast(T, self._list.delete(position=0))
|
|
105
|
+
sz = self._list.size()
|
|
106
|
+
return cast(T, self._list.delete(position=sz - 1))
|
|
107
|
+
|
|
108
|
+
def peek(self, side: _Side = "left") -> T:
|
|
109
|
+
"""
|
|
110
|
+
Return an element from one end without removing it.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
side:
|
|
115
|
+
``"left"`` to inspect the front (default), ``"right"`` to inspect
|
|
116
|
+
the rear.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
T
|
|
121
|
+
|
|
122
|
+
Raises
|
|
123
|
+
------
|
|
124
|
+
EmptyStructureError
|
|
125
|
+
If the deque is empty.
|
|
126
|
+
"""
|
|
127
|
+
with self._lock:
|
|
128
|
+
if self._list.is_empty():
|
|
129
|
+
raise EmptyStructureError("peek at an empty deque")
|
|
130
|
+
if side == "left":
|
|
131
|
+
return self._list.get(0)
|
|
132
|
+
sz = self._list.size()
|
|
133
|
+
return self._list.get(sz - 1)
|
|
134
|
+
|
|
135
|
+
# ------------------------------------------------------------------ #
|
|
136
|
+
# Bulk operations #
|
|
137
|
+
# ------------------------------------------------------------------ #
|
|
138
|
+
|
|
139
|
+
def rotate(self, steps: int = 1) -> None:
|
|
140
|
+
with self._lock:
|
|
141
|
+
sz = self._list.size()
|
|
142
|
+
if sz <= 1:
|
|
143
|
+
return
|
|
144
|
+
steps = steps % sz
|
|
145
|
+
if steps == 0:
|
|
146
|
+
return
|
|
147
|
+
for _ in range(steps):
|
|
148
|
+
val: T = cast(T, self._list.delete(position=sz - 1))
|
|
149
|
+
self._list.insert(val, position=0)
|
|
150
|
+
|
|
151
|
+
def reverse(self) -> None:
|
|
152
|
+
with self._lock:
|
|
153
|
+
self._list.reverse()
|
|
154
|
+
|
|
155
|
+
# ------------------------------------------------------------------ #
|
|
156
|
+
# Query operations #
|
|
157
|
+
# ------------------------------------------------------------------ #
|
|
158
|
+
|
|
159
|
+
def is_empty(self) -> bool:
|
|
160
|
+
return self._list.is_empty()
|
|
161
|
+
|
|
162
|
+
def size(self) -> int:
|
|
163
|
+
return self._list.size()
|
|
164
|
+
|
|
165
|
+
def clear(self) -> None:
|
|
166
|
+
self._list.clear()
|
|
167
|
+
|
|
168
|
+
def copy(self) -> LinkedDeque[T]:
|
|
169
|
+
with self._lock:
|
|
170
|
+
new = LinkedDeque[T]()
|
|
171
|
+
new._list = cast(DoublyLinkedList[T], self._list.copy())
|
|
172
|
+
return new
|
|
173
|
+
|
|
174
|
+
def to_list(self) -> list[T]:
|
|
175
|
+
return self._list.to_list()
|
|
176
|
+
|
|
177
|
+
# ------------------------------------------------------------------ #
|
|
178
|
+
# Dunder methods #
|
|
179
|
+
# ------------------------------------------------------------------ #
|
|
180
|
+
|
|
181
|
+
def __iter__(self) -> Iterator[T]:
|
|
182
|
+
with self._lock:
|
|
183
|
+
snapshot = self.to_list()
|
|
184
|
+
return iter(snapshot)
|
|
185
|
+
|
|
186
|
+
def __len__(self) -> int:
|
|
187
|
+
return self.size()
|
|
188
|
+
|
|
189
|
+
def __bool__(self) -> bool:
|
|
190
|
+
return not self.is_empty()
|
|
191
|
+
|
|
192
|
+
def __repr__(self) -> str:
|
|
193
|
+
return f"LinkedDeque({self.to_list()!r})"
|
|
194
|
+
|
|
195
|
+
def __eq__(self, other: object) -> bool:
|
|
196
|
+
if not isinstance(other, LinkedDeque):
|
|
197
|
+
return NotImplemented
|
|
198
|
+
return self.to_list() == other.to_list()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pkstruct.linear.exceptions
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
Re-exports the shared exception hierarchy with linear-specific aliases.
|
|
5
|
+
"""
|
|
6
|
+
from pkstruct.shared.exceptions import (
|
|
7
|
+
ConcurrencyError,
|
|
8
|
+
EmptyStructureError,
|
|
9
|
+
IndexOutOfRangeError,
|
|
10
|
+
InvalidRangeError,
|
|
11
|
+
PkstructError,
|
|
12
|
+
SerializationError,
|
|
13
|
+
ValidationError,
|
|
14
|
+
ValueNotFoundError,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"PkstructError",
|
|
19
|
+
"ValidationError",
|
|
20
|
+
"IndexOutOfRangeError",
|
|
21
|
+
"ValueNotFoundError",
|
|
22
|
+
"EmptyStructureError",
|
|
23
|
+
"SerializationError",
|
|
24
|
+
"ConcurrencyError",
|
|
25
|
+
"InvalidRangeError",
|
|
26
|
+
]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
from pkstruct.linear.linked_lists.circular_linked_list import CircularLinkedList
|
|
2
|
+
from pkstruct.linear.linked_lists.doubly_linked_list import DoublyLinkedList
|
|
3
|
+
from pkstruct.linear.linked_lists.singly_linked_list import SinglyLinkedList
|
|
4
|
+
|
|
5
|
+
__all__ = ["SinglyLinkedList", "DoublyLinkedList", "CircularLinkedList"]
|