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.
Files changed (72) hide show
  1. pkstruct/__init__.py +167 -0
  2. pkstruct/graphs/__init__.py +127 -0
  3. pkstruct/graphs/connectivity.py +157 -0
  4. pkstruct/graphs/directed.py +95 -0
  5. pkstruct/graphs/exceptions.py +63 -0
  6. pkstruct/graphs/graph.py +262 -0
  7. pkstruct/graphs/mst.py +118 -0
  8. pkstruct/graphs/scc.py +138 -0
  9. pkstruct/graphs/shortest_path.py +250 -0
  10. pkstruct/graphs/topo_sort.py +108 -0
  11. pkstruct/graphs/traversal.py +175 -0
  12. pkstruct/graphs/visualization.py +90 -0
  13. pkstruct/graphs/weighted.py +37 -0
  14. pkstruct/linear/__init__.py +95 -0
  15. pkstruct/linear/deques/__init__.py +33 -0
  16. pkstruct/linear/deques/deque.py +194 -0
  17. pkstruct/linear/deques/linked_deque.py +198 -0
  18. pkstruct/linear/exceptions.py +26 -0
  19. pkstruct/linear/linked_lists/__init__.py +5 -0
  20. pkstruct/linear/linked_lists/_base.py +608 -0
  21. pkstruct/linear/linked_lists/circular_linked_list.py +230 -0
  22. pkstruct/linear/linked_lists/doubly_linked_list.py +151 -0
  23. pkstruct/linear/linked_lists/nodes.py +68 -0
  24. pkstruct/linear/linked_lists/singly_linked_list.py +136 -0
  25. pkstruct/linear/queues/__init__.py +44 -0
  26. pkstruct/linear/queues/circular_queue.py +258 -0
  27. pkstruct/linear/queues/linked_queue.py +186 -0
  28. pkstruct/linear/queues/priority_queue.py +202 -0
  29. pkstruct/linear/queues/queue.py +174 -0
  30. pkstruct/linear/stacks/__init__.py +38 -0
  31. pkstruct/linear/stacks/array_stack.py +165 -0
  32. pkstruct/linear/stacks/linked_stack.py +168 -0
  33. pkstruct/linear/stacks/stack.py +158 -0
  34. pkstruct/linear/utils/__init__.py +18 -0
  35. pkstruct/linear/utils/benchmark.py +255 -0
  36. pkstruct/linear/utils/debug_tools.py +239 -0
  37. pkstruct/linear/utils/helpers.py +143 -0
  38. pkstruct/linear/utils/iterators.py +148 -0
  39. pkstruct/linear/visualization/__init__.py +0 -0
  40. pkstruct/linear/visualization/ascii_visualizer.py +114 -0
  41. pkstruct/linear/visualization/linked_list_visualizer.py +126 -0
  42. pkstruct/shared/__init__.py +67 -0
  43. pkstruct/shared/benchmarking/__init__.py +78 -0
  44. pkstruct/shared/debugging/__init__.py +69 -0
  45. pkstruct/shared/exceptions/__init__.py +59 -0
  46. pkstruct/shared/serializers/__init__.py +65 -0
  47. pkstruct/shared/threading/__init__.py +43 -0
  48. pkstruct/shared/validators/__init__.py +98 -0
  49. pkstruct/shared/visualization/__init__.py +21 -0
  50. pkstruct/trees/__init__.py +92 -0
  51. pkstruct/trees/avl.py +321 -0
  52. pkstruct/trees/balancing.py +253 -0
  53. pkstruct/trees/bplus.py +425 -0
  54. pkstruct/trees/bst.py +948 -0
  55. pkstruct/trees/btree.py +504 -0
  56. pkstruct/trees/exceptions.py +96 -0
  57. pkstruct/trees/fenwick_tree.py +312 -0
  58. pkstruct/trees/interval_tree.py +541 -0
  59. pkstruct/trees/node.py +356 -0
  60. pkstruct/trees/red_black.py +710 -0
  61. pkstruct/trees/segment_tree.py +398 -0
  62. pkstruct/trees/traversal.py +456 -0
  63. pkstruct/trees/tree_helpers.py +366 -0
  64. pkstruct/trees/utils/__init__.py +15 -0
  65. pkstruct/trees/utils/complexity_helpers.py +231 -0
  66. pkstruct/trees/visualization/__init__.py +0 -0
  67. pkstruct/trees/visualization/ascii_renderer.py +220 -0
  68. pkstruct/trees/visualization/tree_printer.py +129 -0
  69. pkstruct-0.1.0.dist-info/METADATA +482 -0
  70. pkstruct-0.1.0.dist-info/RECORD +72 -0
  71. pkstruct-0.1.0.dist-info/WHEEL +4 -0
  72. pkstruct-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,230 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, TypeVar, cast
4
+
5
+ from pkstruct.linear.exceptions import (
6
+ EmptyStructureError,
7
+ ValidationError,
8
+ )
9
+ from pkstruct.linear.linked_lists._base import _LinkedListBase
10
+ from pkstruct.linear.linked_lists.nodes import CircularNode
11
+ from pkstruct.shared.validators import validate_index
12
+
13
+ T = TypeVar("T")
14
+
15
+
16
+ class CircularLinkedList(_LinkedListBase[T]):
17
+ __slots__ = ()
18
+
19
+ def __init__(self) -> None:
20
+ super().__init__()
21
+
22
+ def _append(self, value: T) -> None:
23
+ new_node: CircularNode[T] = CircularNode(value)
24
+ if self._tail is None:
25
+ new_node.next = new_node
26
+ self._head = new_node
27
+ self._tail = new_node
28
+ else:
29
+ new_node.next = self._head
30
+ self._tail.next = new_node
31
+ self._tail = new_node
32
+ self._size += 1
33
+
34
+ def _prepend(self, value: T) -> None:
35
+ new_node: CircularNode[T] = CircularNode(value)
36
+ if self._tail is None:
37
+ new_node.next = new_node
38
+ self._head = new_node
39
+ self._tail = new_node
40
+ else:
41
+ new_node.next = self._head
42
+ self._tail.next = new_node
43
+ self._head = new_node
44
+ self._size += 1
45
+
46
+ def _node_at(self, index: int) -> CircularNode[T]:
47
+ validate_index(index, self._size)
48
+ node: CircularNode[T] | None = self._head
49
+ for _ in range(index):
50
+ if node is None:
51
+ raise RuntimeError("internal invariant violated: unexpected None in linked list chain")
52
+ node = node.next
53
+ if node is None:
54
+ raise RuntimeError("internal invariant violated: unexpected None in linked list chain")
55
+ return node
56
+
57
+ def _node_before(self, target: CircularNode[T]) -> CircularNode[T]:
58
+ node: CircularNode[T] | None = self._tail
59
+ while node is not None and node.next is not target:
60
+ node = node.next
61
+ if node is None:
62
+ raise RuntimeError("internal invariant violated: unexpected None in linked list chain")
63
+ return node
64
+
65
+ def _find_node(self, value: object) -> CircularNode[T] | None:
66
+ if self._head is None:
67
+ return None
68
+ node: CircularNode[T] | None = self._head
69
+ for _ in range(self._size):
70
+ if node is None:
71
+ return None
72
+ if node.value == value:
73
+ return node
74
+ node = node.next
75
+ return None
76
+
77
+ def _remove_node(self, node: CircularNode[T]) -> T:
78
+ if self._size == 1:
79
+ self._head = None
80
+ self._tail = None
81
+ node.next = None
82
+ self._size -= 1
83
+ return node.value
84
+ prev = self._node_before(node)
85
+ prev.next = node.next
86
+ if node is self._head:
87
+ self._head = node.next
88
+ if node is self._tail:
89
+ self._tail = prev
90
+ node.next = None
91
+ self._size -= 1
92
+ return node.value
93
+
94
+ def _insert_before_node(self, node: CircularNode[T], value: T) -> None:
95
+ if node is self._head:
96
+ self._prepend(value)
97
+ else:
98
+ prev = self._node_before(node)
99
+ new_node: CircularNode[T] = CircularNode(value)
100
+ new_node.next = node
101
+ prev.next = new_node
102
+ self._size += 1
103
+
104
+ def _insert_after_node(self, node: CircularNode[T], value: T) -> None:
105
+ new_node: CircularNode[T] = CircularNode(value)
106
+ new_node.next = node.next
107
+ node.next = new_node
108
+ if node is self._tail:
109
+ self._tail = new_node
110
+ self._size += 1
111
+
112
+ def _to_list_unsafe(self) -> list[T]:
113
+ if self._head is None:
114
+ return []
115
+ result: list[T] = []
116
+ node = self._head
117
+ for _ in range(self._size):
118
+ result.append(node.value)
119
+ node = node.next
120
+ return result
121
+
122
+ # ------------------------------------------------------------------ #
123
+ # Rotation overrides (pointer-based for full list) #
124
+ # ------------------------------------------------------------------ #
125
+
126
+ def _rotate_full_list(self, shift: int) -> None:
127
+ if shift == 0 or self._size <= 1:
128
+ return
129
+ steps = (self._size - shift) % self._size
130
+ for _ in range(steps):
131
+ self._head = self._head.next
132
+ self._tail = self._tail.next
133
+
134
+ def rotate_head(self, steps: int = 1, direction: bool = True) -> None:
135
+ with self._lock:
136
+ if self._size == 0:
137
+ raise EmptyStructureError("rotate_head on an empty list")
138
+ self._tracer.record("rotate_head", steps=steps, direction=direction)
139
+ steps = steps % self._size
140
+ if steps == 0:
141
+ return
142
+ if not direction:
143
+ steps = self._size - steps
144
+ for _ in range(steps):
145
+ self._tail = self._head
146
+ self._head = self._head.next
147
+
148
+ # ------------------------------------------------------------------ #
149
+ # Josephus problem (circular-unique) #
150
+ # ------------------------------------------------------------------ #
151
+
152
+ def josephus(self, step: int) -> T:
153
+ with self._lock:
154
+ if self._size == 0:
155
+ raise EmptyStructureError("josephus on an empty list")
156
+ if step < 1:
157
+ raise ValidationError("'step' must be >= 1.")
158
+ self._tracer.record("josephus", step=step)
159
+ current = self._head
160
+ while self._size > 1:
161
+ for _ in range(step - 1):
162
+ current = current.next
163
+ next_node = current.next
164
+ self._remove_node(current)
165
+ current = next_node
166
+ survivor = cast(T, self._head.value)
167
+ self.clear()
168
+ return survivor
169
+
170
+ # ------------------------------------------------------------------ #
171
+ # Visualization & Debug #
172
+ # ------------------------------------------------------------------ #
173
+
174
+ def visualize(self, style: str = "ascii") -> str:
175
+ with self._lock:
176
+ if self._size == 0:
177
+ return "(empty circular list)"
178
+ values = self._to_list_unsafe()
179
+ if style == "simple":
180
+ return ", ".join(str(v) for v in values)
181
+ if style == "ring":
182
+ nodes_str = " -> ".join(f"[{v}]" for v in values)
183
+ return f"{nodes_str} -> \u21ba"
184
+ inner = " -> ".join(str(v) for v in values)
185
+ return f"{inner} -> (back to head)"
186
+
187
+ def debug(self) -> dict[str, Any]:
188
+ with self._lock:
189
+ invariant = (
190
+ (self._tail is not None and self._tail.next is self._head)
191
+ if self._size > 0
192
+ else True
193
+ )
194
+ return {
195
+ "type": "CircularLinkedList",
196
+ "length": self._size,
197
+ "size": self._size,
198
+ "head": self._head.value if self._head else None,
199
+ "tail": self._tail.value if self._tail else None,
200
+ "tail_next": (self._tail.next.value if self._tail and self._tail.next else None),
201
+ "invariant_ok": invariant,
202
+ "values": self._to_list_unsafe(),
203
+ "events": self._tracer.get_events(),
204
+ }
205
+
206
+ def detect_cycle(self, return_start: bool = False) -> bool | tuple[bool, Any]:
207
+ with self._lock:
208
+ if self._head is None:
209
+ return (False, None) if return_start else False
210
+ slow = self._head
211
+ fast = self._head
212
+ has_cycle = False
213
+ max_steps = self._size * 2
214
+ for _ in range(max_steps):
215
+ if fast.next is None or fast.next.next is None:
216
+ break
217
+ slow = slow.next
218
+ fast = fast.next.next
219
+ if slow is fast:
220
+ has_cycle = True
221
+ break
222
+ if not return_start:
223
+ return has_cycle
224
+ if not has_cycle:
225
+ return False, None
226
+ slow = self._head
227
+ while slow is not fast:
228
+ slow = slow.next
229
+ fast = fast.next
230
+ return True, slow.value
@@ -0,0 +1,151 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, TypeVar, cast
4
+
5
+ from pkstruct.linear.exceptions import EmptyStructureError
6
+ from pkstruct.linear.linked_lists._base import _LinkedListBase
7
+ from pkstruct.linear.linked_lists.nodes import DoublyNode
8
+ from pkstruct.shared.validators import validate_range
9
+
10
+ T = TypeVar("T")
11
+
12
+
13
+ class DoublyLinkedList(_LinkedListBase[T]):
14
+ __slots__ = ()
15
+
16
+ def __init__(self) -> None:
17
+ super().__init__()
18
+
19
+ def _append(self, value: T) -> None:
20
+ new_node: DoublyNode[T] = DoublyNode(value)
21
+ if self._tail is None:
22
+ self._head = new_node
23
+ self._tail = new_node
24
+ else:
25
+ new_node.prev = self._tail
26
+ self._tail.next = new_node
27
+ self._tail = new_node
28
+ self._size += 1
29
+
30
+ def _prepend(self, value: T) -> None:
31
+ new_node: DoublyNode[T] = DoublyNode(value)
32
+ if self._head is None:
33
+ self._head = new_node
34
+ self._tail = new_node
35
+ else:
36
+ new_node.next = self._head
37
+ self._head.prev = new_node
38
+ self._head = new_node
39
+ self._size += 1
40
+
41
+ def _node_at(self, index: int) -> DoublyNode[T]:
42
+ if index <= self._size // 2:
43
+ node: DoublyNode[T] | None = self._head
44
+ for _ in range(index):
45
+ if node is None:
46
+ raise RuntimeError("internal invariant violated: unexpected None in linked list chain")
47
+ node = node.next
48
+ else:
49
+ node = self._tail
50
+ for _ in range(self._size - 1 - index):
51
+ if node is None:
52
+ raise RuntimeError("internal invariant violated: unexpected None in linked list chain")
53
+ node = node.prev
54
+ if node is None:
55
+ raise RuntimeError("internal invariant violated: unexpected None in linked list chain")
56
+ return node
57
+
58
+ def _find_node(self, value: object) -> DoublyNode[T] | None:
59
+ node: DoublyNode[T] | None = self._head
60
+ while node is not None:
61
+ if node.value == value:
62
+ return node
63
+ node = node.next
64
+ return None
65
+
66
+ def _remove_node(self, node: DoublyNode[T]) -> T:
67
+ prev_node = node.prev
68
+ next_node = node.next
69
+ if prev_node is not None:
70
+ prev_node.next = next_node
71
+ else:
72
+ self._head = next_node
73
+ if next_node is not None:
74
+ next_node.prev = prev_node
75
+ else:
76
+ self._tail = prev_node
77
+ node.next = None
78
+ node.prev = None
79
+ self._size -= 1
80
+ return node.value
81
+
82
+ def _insert_before_node(self, node: DoublyNode[T], value: T) -> None:
83
+ new_node: DoublyNode[T] = DoublyNode(value)
84
+ prev_node = node.prev
85
+ new_node.next = node
86
+ new_node.prev = prev_node
87
+ node.prev = new_node
88
+ if prev_node is None:
89
+ self._head = new_node
90
+ else:
91
+ prev_node.next = new_node
92
+ self._size += 1
93
+
94
+ def _insert_after_node(self, node: DoublyNode[T], value: T) -> None:
95
+ new_node: DoublyNode[T] = DoublyNode(value)
96
+ next_node = node.next
97
+ new_node.prev = node
98
+ new_node.next = next_node
99
+ node.next = new_node
100
+ if next_node is None:
101
+ self._tail = new_node
102
+ else:
103
+ next_node.prev = new_node
104
+ self._size += 1
105
+
106
+ def _to_list_unsafe(self) -> list[T]:
107
+ result: list[T] = []
108
+ node = self._head
109
+ while node is not None:
110
+ result.append(node.value)
111
+ node = node.next
112
+ return result
113
+
114
+ def reverse(self, start: int | None = None, end: int | None = None) -> None:
115
+ with self._lock:
116
+ if self._size == 0:
117
+ raise EmptyStructureError("reverse an empty list")
118
+ lo = 0 if start is None else start
119
+ hi = self._size - 1 if end is None else end
120
+ if lo >= hi:
121
+ return
122
+ validate_range(lo, hi, self._size)
123
+ self._tracer.record("reverse", start=lo, end=hi)
124
+ left = self._node_at(lo)
125
+ right = self._node_at(hi)
126
+ while left is not right and left is not cast(DoublyNode[T], right.next):
127
+ left.value, right.value = right.value, left.value
128
+ left = cast(DoublyNode[T], left.next)
129
+ right = cast(DoublyNode[T], right.prev)
130
+
131
+ def visualize(self, style: str = "ascii") -> str:
132
+ with self._lock:
133
+ if self._size == 0:
134
+ return "None <- (empty) -> None"
135
+ values = self._to_list_unsafe()
136
+ if style == "simple":
137
+ return ", ".join(str(v) for v in values)
138
+ inner = " <-> ".join(str(v) for v in values)
139
+ return f"None <- {inner} -> None"
140
+
141
+ def debug(self) -> dict[str, Any]:
142
+ with self._lock:
143
+ return {
144
+ "type": "DoublyLinkedList",
145
+ "length": self._size,
146
+ "size": self._size,
147
+ "head": self._head.value if self._head else None,
148
+ "tail": self._tail.value if self._tail else None,
149
+ "values": self._to_list_unsafe(),
150
+ "events": self._tracer.get_events(),
151
+ }
@@ -0,0 +1,68 @@
1
+ """
2
+ pkstruct.linear.linked_lists.nodes
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ Node definitions for all linked-list variants.
5
+
6
+ All nodes use ``__slots__`` to minimise per-object memory overhead.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from typing import Generic, TypeVar
11
+
12
+ T = TypeVar("T")
13
+
14
+
15
+ class SinglyNode(Generic[T]):
16
+ """A node in a singly-linked list.
17
+
18
+ Attributes:
19
+ value: The stored element.
20
+ next: Reference to the next node, or ``None`` if tail.
21
+ """
22
+
23
+ __slots__ = ("value", "next")
24
+
25
+ def __init__(self, value: T) -> None:
26
+ self.value: T = value
27
+ self.next: SinglyNode[T] | None = None
28
+
29
+ def __repr__(self) -> str:
30
+ return f"SinglyNode({self.value!r})"
31
+
32
+
33
+ class DoublyNode(Generic[T]):
34
+ """A node in a doubly-linked list.
35
+
36
+ Attributes:
37
+ value: The stored element.
38
+ next: Reference to the next node, or ``None`` if tail.
39
+ prev: Reference to the previous node, or ``None`` if head.
40
+ """
41
+
42
+ __slots__ = ("value", "next", "prev")
43
+
44
+ def __init__(self, value: T) -> None:
45
+ self.value: T = value
46
+ self.next: DoublyNode[T] | None = None
47
+ self.prev: DoublyNode[T] | None = None
48
+
49
+ def __repr__(self) -> str:
50
+ return f"DoublyNode({self.value!r})"
51
+
52
+
53
+ class CircularNode(Generic[T]):
54
+ """A node in a circular singly-linked list.
55
+
56
+ Attributes:
57
+ value: The stored element.
58
+ next: Always points to a valid node (wraps to head at tail).
59
+ """
60
+
61
+ __slots__ = ("value", "next")
62
+
63
+ def __init__(self, value: T) -> None:
64
+ self.value: T = value
65
+ self.next: CircularNode[T] | None = None # set to self or next during build
66
+
67
+ def __repr__(self) -> str:
68
+ return f"CircularNode({self.value!r})"
@@ -0,0 +1,136 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, TypeVar
4
+
5
+ from pkstruct.linear.linked_lists._base import _LinkedListBase
6
+ from pkstruct.linear.linked_lists.nodes import SinglyNode
7
+
8
+ T = TypeVar("T")
9
+
10
+
11
+ class SinglyLinkedList(_LinkedListBase[T]):
12
+ def __init__(self) -> None:
13
+ super().__init__()
14
+
15
+ def _append(self, value: T) -> None:
16
+ node: SinglyNode[T] = SinglyNode(value)
17
+ if self._head is None:
18
+ self._head = node
19
+ self._tail = node
20
+ else:
21
+ self._tail.next = node
22
+ self._tail = node
23
+ self._size += 1
24
+
25
+ def _prepend(self, value: T) -> None:
26
+ node: SinglyNode[T] = SinglyNode(value)
27
+ node.next = self._head
28
+ self._head = node
29
+ if self._tail is None:
30
+ self._tail = node
31
+ self._size += 1
32
+
33
+ def _node_at(self, index: int) -> SinglyNode[T]:
34
+ cur: SinglyNode[T] | None = self._head
35
+ for _ in range(index):
36
+ if cur is None:
37
+ raise RuntimeError("internal invariant violated: unexpected None in linked list chain")
38
+ cur = cur.next
39
+ if cur is None:
40
+ raise RuntimeError("internal invariant violated: unexpected None in linked list chain")
41
+ return cur
42
+
43
+ def _find_node(self, value: object) -> SinglyNode[T] | None:
44
+ cur: SinglyNode[T] | None = self._head
45
+ while cur is not None:
46
+ if cur.value == value:
47
+ return cur
48
+ cur = cur.next
49
+ return None
50
+
51
+ def _remove_node(self, node: SinglyNode[T]) -> T:
52
+ if node is self._head:
53
+ self._head = node.next
54
+ if self._head is None:
55
+ self._tail = None
56
+ else:
57
+ prev = self._head
58
+ while prev is not None and prev.next is not node:
59
+ prev = prev.next
60
+ if prev is not None:
61
+ prev.next = node.next
62
+ if prev.next is None:
63
+ self._tail = prev
64
+ node.next = None
65
+ self._size -= 1
66
+ return node.value
67
+
68
+ def _insert_before_node(self, node: SinglyNode[T], value: T) -> None:
69
+ new_node: SinglyNode[T] = SinglyNode(value)
70
+ if node is self._head:
71
+ new_node.next = self._head
72
+ self._head = new_node
73
+ else:
74
+ prev = self._head
75
+ while prev is not None and prev.next is not node:
76
+ prev = prev.next
77
+ if prev is not None:
78
+ new_node.next = node
79
+ prev.next = new_node
80
+ self._size += 1
81
+
82
+ def _insert_after_node(self, node: SinglyNode[T], value: T) -> None:
83
+ new_node: SinglyNode[T] = SinglyNode(value)
84
+ new_node.next = node.next
85
+ node.next = new_node
86
+ if node is self._tail:
87
+ self._tail = new_node
88
+ self._size += 1
89
+
90
+ def _to_list_unsafe(self) -> list[T]:
91
+ result: list[T] = []
92
+ cur = self._head
93
+ while cur is not None:
94
+ result.append(cur.value)
95
+ cur = cur.next
96
+ return result
97
+
98
+ def intersection_node(self, other: SinglyLinkedList[T]) -> T | None:
99
+ with self._lock:
100
+ len_a = self._size
101
+ len_b = other._size
102
+ cur_a: SinglyNode[T] | None = self._head
103
+ cur_b: SinglyNode[T] | None = other._head
104
+ while len_a > len_b:
105
+ cur_a = cur_a.next if cur_a else None
106
+ len_a -= 1
107
+ while len_b > len_a:
108
+ cur_b = cur_b.next if cur_b else None
109
+ len_b -= 1
110
+ while cur_a is not cur_b:
111
+ cur_a = cur_a.next if cur_a else None
112
+ cur_b = cur_b.next if cur_b else None
113
+ return cur_a.value if cur_a is not None else None
114
+
115
+ def visualize(self, style: str = "ascii") -> str:
116
+ with self._lock:
117
+ items = self._to_list_unsafe()
118
+ if style != "ascii":
119
+ from pkstruct.linear.exceptions import ValidationError
120
+ raise ValidationError(f"Unknown style {style!r}. Only 'ascii' is supported.")
121
+ if not items:
122
+ return "NULL (empty)"
123
+ parts = [f"[{v!r}]" for v in items]
124
+ return " -> ".join(parts) + " -> NULL"
125
+
126
+ def debug(self) -> dict[str, Any]:
127
+ with self._lock:
128
+ return {
129
+ "type": "SinglyLinkedList",
130
+ "length": self._size,
131
+ "size": self._size,
132
+ "head": repr(self._head),
133
+ "tail": repr(self._node_at(self._size - 1)) if self._size > 0 else None,
134
+ "values": self._to_list_unsafe(),
135
+ "tracer": self._tracer.summary(),
136
+ }
@@ -0,0 +1,44 @@
1
+ """
2
+ pkstruct.linear.queues
3
+ ======================
4
+
5
+ FIFO (first-in, first-out) queue data structures for the pkstruct ecosystem.
6
+
7
+ Provides multiple concrete queue implementations and a shared abstract base.
8
+
9
+ Classes
10
+ -------
11
+ Queue
12
+ Abstract base class defining the queue interface.
13
+ LinkedQueue
14
+ Queue backed by ``SinglyLinkedList`` (node-based, thread-safe).
15
+ CircularQueue
16
+ Fixed-capacity queue backed by a ring buffer.
17
+ PriorityQueue
18
+ Min-heap priority queue backed by ``heapq``.
19
+
20
+ Example
21
+ -------
22
+ >>> from pkstruct.linear.queues import LinkedQueue
23
+ >>> q = LinkedQueue([1, 2, 3])
24
+ >>> q.enqueue(4)
25
+ >>> q.dequeue()
26
+ 1
27
+ >>> list(q)
28
+ [2, 3, 4]
29
+ """
30
+
31
+ from pkstruct.linear.queues.circular_queue import CircularQueue, QueueFullError
32
+ from pkstruct.linear.queues.linked_queue import LinkedQueue
33
+ from pkstruct.linear.queues.priority_queue import PriorityQueue
34
+ from pkstruct.linear.queues.queue import Queue
35
+
36
+ __all__ = [
37
+ # Queue classes
38
+ "CircularQueue",
39
+ "LinkedQueue",
40
+ "PriorityQueue",
41
+ "Queue",
42
+ # Exceptions
43
+ "QueueFullError",
44
+ ]