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,174 @@
1
+ """
2
+ pkstruct.linear.queues.queue
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ Abstract base class for all queue implementations.
5
+
6
+ Provides a formal interface contract. Every concrete queue 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, TypeVar
15
+
16
+ T = TypeVar("T")
17
+
18
+
19
+ class Queue(ABC, Generic[T]):
20
+ """
21
+ Abstract base class for FIFO (first-in, first-out) queue structures.
22
+
23
+ Subclasses must implement all abstract methods. The interface is
24
+ consistent across array-backed, linked-list-backed, and heap-backed
25
+ implementations.
26
+
27
+ Parameters
28
+ ----------
29
+ No parameters required at the abstract level.
30
+ """
31
+
32
+ __slots__ = ()
33
+
34
+ # ------------------------------------------------------------------ #
35
+ # Required operations #
36
+ # ------------------------------------------------------------------ #
37
+
38
+ @abstractmethod
39
+ def enqueue(self, value: T) -> None:
40
+ """
41
+ Add *value* to the rear of the queue.
42
+
43
+ Parameters
44
+ ----------
45
+ value:
46
+ The element to enqueue.
47
+ """
48
+ ...
49
+
50
+ @abstractmethod
51
+ def dequeue(self) -> T:
52
+ """
53
+ Remove and return the front element.
54
+
55
+ Returns
56
+ -------
57
+ T
58
+ The element at the front of the queue.
59
+
60
+ Raises
61
+ ------
62
+ EmptyStructureError
63
+ If the queue is empty.
64
+ """
65
+ ...
66
+
67
+ @abstractmethod
68
+ def front(self) -> T:
69
+ """
70
+ Return the front element without removing it.
71
+
72
+ Returns
73
+ -------
74
+ T
75
+
76
+ Raises
77
+ ------
78
+ EmptyStructureError
79
+ If the queue is empty.
80
+ """
81
+ ...
82
+
83
+ @abstractmethod
84
+ def rear(self) -> T:
85
+ """
86
+ Return the rear element without removing it.
87
+
88
+ Returns
89
+ -------
90
+ T
91
+
92
+ Raises
93
+ ------
94
+ EmptyStructureError
95
+ If the queue is empty.
96
+ """
97
+ ...
98
+
99
+ # ------------------------------------------------------------------ #
100
+ # Query operations #
101
+ # ------------------------------------------------------------------ #
102
+
103
+ @abstractmethod
104
+ def is_empty(self) -> bool:
105
+ """
106
+ Return ``True`` if the queue contains no elements.
107
+
108
+ Returns
109
+ -------
110
+ bool
111
+ """
112
+ ...
113
+
114
+ @abstractmethod
115
+ def size(self) -> int:
116
+ """
117
+ Return the number of elements in the queue.
118
+
119
+ Returns
120
+ -------
121
+ int
122
+ """
123
+ ...
124
+
125
+ @abstractmethod
126
+ def clear(self) -> None:
127
+ """Remove all elements from the queue."""
128
+ ...
129
+
130
+ @abstractmethod
131
+ def copy(self) -> Queue[T]:
132
+ """
133
+ Return a shallow copy of this queue.
134
+
135
+ Returns
136
+ -------
137
+ Queue[T]
138
+ """
139
+ ...
140
+
141
+ @abstractmethod
142
+ def to_list(self) -> list[T]:
143
+ """
144
+ Return a list of all elements, from front to rear.
145
+
146
+ Returns
147
+ -------
148
+ list[T]
149
+ """
150
+ ...
151
+
152
+ # ------------------------------------------------------------------ #
153
+ # Dunder methods #
154
+ # ------------------------------------------------------------------ #
155
+
156
+ @abstractmethod
157
+ def __iter__(self) -> Iterator[T]:
158
+ ...
159
+
160
+ @abstractmethod
161
+ def __len__(self) -> int:
162
+ ...
163
+
164
+ @abstractmethod
165
+ def __bool__(self) -> bool:
166
+ ...
167
+
168
+ @abstractmethod
169
+ def __repr__(self) -> str:
170
+ ...
171
+
172
+ @abstractmethod
173
+ def __eq__(self, other: object) -> bool:
174
+ ...
@@ -0,0 +1,38 @@
1
+ """
2
+ pkstruct.linear.stacks
3
+ ======================
4
+
5
+ LIFO (last-in, first-out) stack data structures for the pkstruct ecosystem.
6
+
7
+ Provides two concrete stack implementations and a shared abstract base.
8
+
9
+ Classes
10
+ -------
11
+ Stack
12
+ Abstract base class defining the stack interface.
13
+ LinkedStack
14
+ Stack backed by ``SinglyLinkedList`` (node-based, thread-safe).
15
+ ArrayStack
16
+ Stack backed by a Python ``list`` (compact, cache-local).
17
+
18
+ Example
19
+ -------
20
+ >>> from pkstruct.linear.stacks import LinkedStack
21
+ >>> s = LinkedStack([1, 2, 3])
22
+ >>> s.push(4)
23
+ >>> s.pop()
24
+ 4
25
+ >>> list(s)
26
+ [3, 2, 1]
27
+ """
28
+
29
+ from pkstruct.linear.stacks.array_stack import ArrayStack
30
+ from pkstruct.linear.stacks.linked_stack import LinkedStack
31
+ from pkstruct.linear.stacks.stack import Stack
32
+
33
+ __all__ = [
34
+ # Stack classes
35
+ "ArrayStack",
36
+ "LinkedStack",
37
+ "Stack",
38
+ ]
@@ -0,0 +1,165 @@
1
+ """
2
+ pkstruct.linear.stacks.array_stack
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ A LIFO stack backed by a dynamic Python ``list``.
5
+
6
+ ``ArrayStack`` offers compact memory (no per-node overhead) and excellent
7
+ cache locality. All operations are O(1) amortised.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import Iterator
13
+ from typing import TypeVar
14
+
15
+ from pkstruct.linear.exceptions import EmptyStructureError
16
+ from pkstruct.linear.stacks.stack import Stack
17
+ from pkstruct.shared.threading import StructureLock
18
+
19
+ T = TypeVar("T")
20
+
21
+
22
+ class ArrayStack(Stack[T]):
23
+ """
24
+ A thread-safe stack backed by a Python ``list``.
25
+
26
+ The underlying list grows and shrinks dynamically. Push and pop at the
27
+ end of the list are O(1) amortised.
28
+
29
+ Parameters
30
+ ----------
31
+ items:
32
+ Optional iterable of initial values (bottom-to-top order).
33
+ If omitted the stack starts empty.
34
+
35
+ Examples
36
+ --------
37
+ >>> s = ArrayStack([1, 2, 3])
38
+ >>> s.push(4)
39
+ >>> s.pop()
40
+ 4
41
+ >>> s.peek()
42
+ 3
43
+ """
44
+
45
+ __slots__ = ("_data", "_lock")
46
+
47
+ def __init__(self, items: list[T] | None = None) -> None:
48
+ self._data: list[T] = list(items) if items is not None else []
49
+ self._lock: StructureLock = StructureLock()
50
+
51
+ # ------------------------------------------------------------------ #
52
+ # Required operations #
53
+ # ------------------------------------------------------------------ #
54
+
55
+ def push(self, value: T) -> None:
56
+ """
57
+ Push *value* onto the top of the stack.
58
+
59
+ Parameters
60
+ ----------
61
+ value:
62
+ The element to push.
63
+ """
64
+ with self._lock:
65
+ self._data.append(value)
66
+
67
+ def pop(self) -> T:
68
+ """
69
+ Remove and return the top element.
70
+
71
+ Returns
72
+ -------
73
+ T
74
+
75
+ Raises
76
+ ------
77
+ EmptyStructureError
78
+ If the stack is empty.
79
+ """
80
+ with self._lock:
81
+ if not self._data:
82
+ raise EmptyStructureError("pop from an empty stack")
83
+ return self._data.pop()
84
+
85
+ def peek(self) -> T:
86
+ """
87
+ Return the top element without removing it.
88
+
89
+ Returns
90
+ -------
91
+ T
92
+
93
+ Raises
94
+ ------
95
+ EmptyStructureError
96
+ If the stack is empty.
97
+ """
98
+ with self._lock:
99
+ if not self._data:
100
+ raise EmptyStructureError("peek at an empty stack")
101
+ return self._data[-1]
102
+
103
+ # ------------------------------------------------------------------ #
104
+ # Query operations #
105
+ # ------------------------------------------------------------------ #
106
+
107
+ def is_empty(self) -> bool:
108
+ """Return ``True`` if the stack contains no elements."""
109
+ return len(self._data) == 0
110
+
111
+ def size(self) -> int:
112
+ """Return the number of elements in the stack."""
113
+ return len(self._data)
114
+
115
+ def clear(self) -> None:
116
+ """Remove all elements from the stack."""
117
+ with self._lock:
118
+ self._data.clear()
119
+
120
+ def copy(self) -> ArrayStack[T]:
121
+ """
122
+ Return a shallow copy of this stack.
123
+
124
+ Returns
125
+ -------
126
+ ArrayStack[T]
127
+ """
128
+ with self._lock:
129
+ new = ArrayStack[T]()
130
+ new._data = self._data.copy()
131
+ return new
132
+
133
+ def to_list(self) -> list[T]:
134
+ """
135
+ Return a list of all elements, from top to bottom.
136
+
137
+ Returns
138
+ -------
139
+ list[T]
140
+ """
141
+ with self._lock:
142
+ return list(reversed(self._data))
143
+
144
+ # ------------------------------------------------------------------ #
145
+ # Dunder methods #
146
+ # ------------------------------------------------------------------ #
147
+
148
+ def __iter__(self) -> Iterator[T]:
149
+ with self._lock:
150
+ snapshot = self.to_list()
151
+ return iter(snapshot)
152
+
153
+ def __len__(self) -> int:
154
+ return self.size()
155
+
156
+ def __bool__(self) -> bool:
157
+ return not self.is_empty()
158
+
159
+ def __repr__(self) -> str:
160
+ return f"ArrayStack({self.to_list()!r})"
161
+
162
+ def __eq__(self, other: object) -> bool:
163
+ if not isinstance(other, ArrayStack):
164
+ return NotImplemented
165
+ return self.to_list() == other.to_list()
@@ -0,0 +1,168 @@
1
+ """
2
+ pkstruct.linear.stacks.linked_stack
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ A LIFO stack backed by a ``SinglyLinkedList``.
5
+
6
+ ``LinkedStack`` composes the existing production-grade linked list rather than
7
+ reimplementing node management. Push is O(1) amortised via head insertion;
8
+ pop and peek are O(1).
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections.abc import Iterator
14
+ from typing import TypeVar, cast
15
+
16
+ from pkstruct.linear.exceptions import EmptyStructureError
17
+ from pkstruct.linear.linked_lists.singly_linked_list import SinglyLinkedList
18
+ from pkstruct.linear.stacks.stack import Stack
19
+ from pkstruct.shared.threading import StructureLock
20
+
21
+ T = TypeVar("T")
22
+
23
+
24
+ class LinkedStack(Stack[T]):
25
+ """
26
+ A thread-safe stack implemented on top of ``SinglyLinkedList``.
27
+
28
+ The internal linked list stores elements with the top of the stack at
29
+ head position, so ``push``, ``pop``, and ``peek`` all operate at index 0.
30
+
31
+ Parameters
32
+ ----------
33
+ items:
34
+ Optional iterable of initial values (bottom-to-top order).
35
+ If omitted the stack starts empty.
36
+
37
+ Examples
38
+ --------
39
+ >>> s = LinkedStack([1, 2, 3])
40
+ >>> s.push(4)
41
+ >>> s.pop()
42
+ 4
43
+ >>> s.peek()
44
+ 3
45
+ """
46
+
47
+ __slots__ = ("_list", "_lock")
48
+
49
+ def __init__(self, items: list[T] | None = None) -> None:
50
+ self._list: SinglyLinkedList[T] = SinglyLinkedList()
51
+ self._lock: StructureLock = StructureLock()
52
+ if items is not None:
53
+ for item in items:
54
+ self._list.insert(item, position=0)
55
+
56
+ # ------------------------------------------------------------------ #
57
+ # Required operations #
58
+ # ------------------------------------------------------------------ #
59
+
60
+ def push(self, value: T) -> None:
61
+ """
62
+ Push *value* onto the top of the stack (head of the list).
63
+
64
+ Parameters
65
+ ----------
66
+ value:
67
+ The element to push.
68
+ """
69
+ with self._lock:
70
+ self._list.insert(value, position=0)
71
+
72
+ def pop(self) -> T:
73
+ """
74
+ Remove and return the top element.
75
+
76
+ Returns
77
+ -------
78
+ T
79
+
80
+ Raises
81
+ ------
82
+ EmptyStructureError
83
+ If the stack is empty.
84
+ """
85
+ with self._lock:
86
+ if self._list.is_empty():
87
+ raise EmptyStructureError("pop from an empty stack")
88
+ return cast(T, self._list.delete(position=0))
89
+
90
+ def peek(self) -> T:
91
+ """
92
+ Return the top element without removing it.
93
+
94
+ Returns
95
+ -------
96
+ T
97
+
98
+ Raises
99
+ ------
100
+ EmptyStructureError
101
+ If the stack is empty.
102
+ """
103
+ with self._lock:
104
+ if self._list.is_empty():
105
+ raise EmptyStructureError("peek at an empty stack")
106
+ return self._list.get(0)
107
+
108
+ # ------------------------------------------------------------------ #
109
+ # Query operations #
110
+ # ------------------------------------------------------------------ #
111
+
112
+ def is_empty(self) -> bool:
113
+ """Return ``True`` if the stack contains no elements."""
114
+ return self._list.is_empty()
115
+
116
+ def size(self) -> int:
117
+ """Return the number of elements in the stack."""
118
+ return self._list.size()
119
+
120
+ def clear(self) -> None:
121
+ """Remove all elements from the stack."""
122
+ self._list.clear()
123
+
124
+ def copy(self) -> LinkedStack[T]:
125
+ """
126
+ Return a shallow copy of this stack.
127
+
128
+ Returns
129
+ -------
130
+ LinkedStack[T]
131
+ """
132
+ with self._lock:
133
+ new = LinkedStack[T]()
134
+ new._list = cast(SinglyLinkedList[T], self._list.copy())
135
+ return new
136
+
137
+ def to_list(self) -> list[T]:
138
+ """
139
+ Return a list of all elements, from top to bottom.
140
+
141
+ Returns
142
+ -------
143
+ list[T]
144
+ """
145
+ return self._list.to_list()
146
+
147
+ # ------------------------------------------------------------------ #
148
+ # Dunder methods #
149
+ # ------------------------------------------------------------------ #
150
+
151
+ def __iter__(self) -> Iterator[T]:
152
+ with self._lock:
153
+ snapshot = self.to_list()
154
+ return iter(snapshot)
155
+
156
+ def __len__(self) -> int:
157
+ return self.size()
158
+
159
+ def __bool__(self) -> bool:
160
+ return not self.is_empty()
161
+
162
+ def __repr__(self) -> str:
163
+ return f"LinkedStack({self.to_list()!r})"
164
+
165
+ def __eq__(self, other: object) -> bool:
166
+ if not isinstance(other, LinkedStack):
167
+ return NotImplemented
168
+ return self.to_list() == other.to_list()
@@ -0,0 +1,158 @@
1
+ """
2
+ pkstruct.linear.stacks.stack
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ Abstract base class for all stack implementations.
5
+
6
+ Provides a formal interface contract. Every concrete stack 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, TypeVar
15
+
16
+ T = TypeVar("T")
17
+
18
+
19
+ class Stack(ABC, Generic[T]):
20
+ """
21
+ Abstract base class for LIFO (last-in, first-out) stack structures.
22
+
23
+ Subclasses must implement all abstract methods. The interface is designed
24
+ to be consistent across both array-backed and linked-list-backed stacks.
25
+
26
+ Parameters
27
+ ----------
28
+ No parameters required at the abstract level.
29
+ """
30
+
31
+ __slots__ = ()
32
+
33
+ # ------------------------------------------------------------------ #
34
+ # Required operations #
35
+ # ------------------------------------------------------------------ #
36
+
37
+ @abstractmethod
38
+ def push(self, value: T) -> None:
39
+ """
40
+ Push *value* onto the top of the stack.
41
+
42
+ Parameters
43
+ ----------
44
+ value:
45
+ The element to push.
46
+ """
47
+ ...
48
+
49
+ @abstractmethod
50
+ def pop(self) -> T:
51
+ """
52
+ Remove and return the top element.
53
+
54
+ Returns
55
+ -------
56
+ T
57
+ The element at the top of the stack.
58
+
59
+ Raises
60
+ ------
61
+ EmptyStructureError
62
+ If the stack is empty.
63
+ """
64
+ ...
65
+
66
+ @abstractmethod
67
+ def peek(self) -> T:
68
+ """
69
+ Return the top element without removing it.
70
+
71
+ Returns
72
+ -------
73
+ T
74
+ The element at the top of the stack.
75
+
76
+ Raises
77
+ ------
78
+ EmptyStructureError
79
+ If the stack is empty.
80
+ """
81
+ ...
82
+
83
+ # ------------------------------------------------------------------ #
84
+ # Query operations #
85
+ # ------------------------------------------------------------------ #
86
+
87
+ @abstractmethod
88
+ def is_empty(self) -> bool:
89
+ """
90
+ Return ``True`` if the stack contains no elements.
91
+
92
+ Returns
93
+ -------
94
+ bool
95
+ """
96
+ ...
97
+
98
+ @abstractmethod
99
+ def size(self) -> int:
100
+ """
101
+ Return the number of elements in the stack.
102
+
103
+ Returns
104
+ -------
105
+ int
106
+ """
107
+ ...
108
+
109
+ @abstractmethod
110
+ def clear(self) -> None:
111
+ """Remove all elements from the stack."""
112
+ ...
113
+
114
+ @abstractmethod
115
+ def copy(self) -> Stack[T]:
116
+ """
117
+ Return a shallow copy of this stack.
118
+
119
+ Returns
120
+ -------
121
+ Stack[T]
122
+ """
123
+ ...
124
+
125
+ @abstractmethod
126
+ def to_list(self) -> list[T]:
127
+ """
128
+ Return a list of all elements, from top to bottom.
129
+
130
+ Returns
131
+ -------
132
+ list[T]
133
+ """
134
+ ...
135
+
136
+ # ------------------------------------------------------------------ #
137
+ # Dunder methods #
138
+ # ------------------------------------------------------------------ #
139
+
140
+ @abstractmethod
141
+ def __iter__(self) -> Iterator[T]:
142
+ ...
143
+
144
+ @abstractmethod
145
+ def __len__(self) -> int:
146
+ ...
147
+
148
+ @abstractmethod
149
+ def __bool__(self) -> bool:
150
+ ...
151
+
152
+ @abstractmethod
153
+ def __repr__(self) -> str:
154
+ ...
155
+
156
+ @abstractmethod
157
+ def __eq__(self, other: object) -> bool:
158
+ ...