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,220 @@
1
+ """
2
+ pkstruct.trees.visualization.ascii_renderer
3
+ ============================================
4
+ Low-level ASCII rendering engine for binary trees.
5
+
6
+ Provides static methods that render a binary tree as a clean ASCII string.
7
+ Works with any node type that exposes ``.key``, ``.left``, and ``.right``
8
+ attributes (``TreeNode``, ``AVLNode``, ``RBNode``, ``IntervalNode``, etc.).
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections import deque
14
+ from collections.abc import Callable
15
+ from typing import Any
16
+
17
+
18
+ class AsciiRenderer:
19
+ """Static methods for rendering binary trees as ASCII art strings."""
20
+
21
+ _SPACE: str = " "
22
+ _FORWARD_SLASH: str = "/"
23
+ _BACKSLASH: str = "\\"
24
+
25
+ @staticmethod
26
+ def render(
27
+ root: Any,
28
+ key_repr: Callable[[Any], str] = str,
29
+ ) -> str:
30
+ """
31
+ Render a binary tree as an ASCII art string.
32
+
33
+ The output uses slashes to connect parent nodes to their children::
34
+
35
+ 10
36
+ / \\
37
+ 5 15
38
+ / \\
39
+ 3 7
40
+
41
+ Parameters
42
+ ----------
43
+ root:
44
+ Root node of the tree. Must have ``.key``, ``.left``, and
45
+ ``.right`` attributes.
46
+ key_repr:
47
+ Callable that converts a node's key to its string representation.
48
+ Defaults to ``str``.
49
+
50
+ Returns
51
+ -------
52
+ str
53
+ ASCII art representation, or ``"(empty)"`` if *root* is *None*.
54
+ """
55
+ if root is None:
56
+ return "(empty)"
57
+
58
+ height = AsciiRenderer._height(root)
59
+ if height == 0:
60
+ return key_repr(root.key)
61
+
62
+ tree_lines: list[str] = []
63
+ level: list[Any | None] = [root]
64
+ level_num = 0
65
+
66
+ while level and level_num <= height:
67
+ next_level: list[Any | None] = []
68
+ for node in level:
69
+ if node is not None:
70
+ next_level.append(node.left)
71
+ next_level.append(node.right)
72
+ else:
73
+ next_level.append(None)
74
+ next_level.append(None)
75
+
76
+ indent = 2 ** (height - level_num) - 1
77
+ between = 2 ** (height - level_num + 1) - 1
78
+
79
+ line: list[str] = []
80
+ for node in level:
81
+ if node is None:
82
+ line.append(" " * (max(1, indent + 1)))
83
+ else:
84
+ rep = key_repr(node.key)
85
+ padding = max(0, indent - len(rep) + 1)
86
+ line.append(" " * padding + rep + " " * (indent - padding))
87
+
88
+ tree_lines.append("".join(line).rstrip())
89
+
90
+ if level_num < height:
91
+ connector_line: list[str] = []
92
+ for _, node in enumerate(level):
93
+ if node is None:
94
+ connector_line.append(" " * (indent + between // 2 + 1))
95
+ else:
96
+ left_char = AsciiRenderer._FORWARD_SLASH if node.left else " "
97
+ right_char = AsciiRenderer._BACKSLASH if node.right else " "
98
+ left_pad = indent - 1
99
+ mid_pad = between - 2
100
+ if left_pad < 0:
101
+ left_pad = 0
102
+ if mid_pad < 0:
103
+ mid_pad = 0
104
+ segment = (
105
+ " " * left_pad
106
+ + left_char
107
+ + " " * max(0, mid_pad)
108
+ + right_char
109
+ + " " * (indent)
110
+ )
111
+ connector_line.append(segment)
112
+ tree_lines.append("".join(connector_line).rstrip())
113
+
114
+ level = next_level
115
+ level_num += 1
116
+
117
+ return "\n".join(tree_lines)
118
+
119
+ @staticmethod
120
+ def render_compact(
121
+ root: Any,
122
+ key_repr: Callable[[Any], str] = str,
123
+ ) -> str:
124
+ """
125
+ Render a compact single-line representation of the tree.
126
+
127
+ Produces output like: ``10 (5 (3, 7), 15)``.
128
+
129
+ Parameters
130
+ ----------
131
+ root:
132
+ Root node of the tree.
133
+ key_repr:
134
+ Callable that converts a node's key to a string.
135
+
136
+ Returns
137
+ -------
138
+ str
139
+ Compact representation, or ``"(empty)"`` if *root* is *None*.
140
+ """
141
+ if root is None:
142
+ return "(empty)"
143
+
144
+ def _build(node: Any | None) -> str:
145
+ if node is None:
146
+ return ""
147
+ left_str = _build(node.left)
148
+ right_str = _build(node.right)
149
+ rep = key_repr(node.key)
150
+ if left_str or right_str:
151
+ parts = []
152
+ if left_str:
153
+ parts.append(left_str)
154
+ if right_str:
155
+ parts.append(right_str)
156
+ return f"{rep} ({', '.join(parts)})"
157
+ return rep
158
+
159
+ return _build(root)
160
+
161
+ @staticmethod
162
+ def render_levels(
163
+ root: Any,
164
+ key_repr: Callable[[Any], str] = str,
165
+ ) -> str:
166
+ """
167
+ Render the tree level by level, one line per level.
168
+
169
+ Produces output like::
170
+
171
+ Level 0: 10
172
+ Level 1: 5 15
173
+ Level 2: 3 7
174
+
175
+ Parameters
176
+ ----------
177
+ root:
178
+ Root node of the tree.
179
+ key_repr:
180
+ Callable that converts a node's key to a string.
181
+
182
+ Returns
183
+ -------
184
+ str
185
+ Level-by-level representation.
186
+ """
187
+ if root is None:
188
+ return "(empty)"
189
+
190
+ lines: list[str] = []
191
+ queue: deque = deque([(root, 0)])
192
+ current_level = 0
193
+ level_nodes: list[str] = []
194
+
195
+ while queue:
196
+ node, level = queue.popleft()
197
+ if level != current_level:
198
+ lines.append(f"Level {current_level}: {' '.join(level_nodes)}")
199
+ level_nodes = []
200
+ current_level = level
201
+ level_nodes.append(key_repr(node.key))
202
+ if node.left:
203
+ queue.append((node.left, level + 1))
204
+ if node.right:
205
+ queue.append((node.right, level + 1))
206
+
207
+ if level_nodes:
208
+ lines.append(f"Level {current_level}: {' '.join(level_nodes)}")
209
+
210
+ return "\n".join(lines)
211
+
212
+ @staticmethod
213
+ def _height(node: Any | None) -> int:
214
+ """Compute the height of the tree rooted at *node*."""
215
+ if node is None:
216
+ return -1
217
+ return 1 + max(
218
+ AsciiRenderer._height(node.left),
219
+ AsciiRenderer._height(node.right),
220
+ )
@@ -0,0 +1,129 @@
1
+ """
2
+ pkstruct.trees.visualization.tree_printer
3
+ ==========================================
4
+ High-level tree visualizer that dispatches to AsciiRenderer based on tree type.
5
+
6
+ Provides a unified ``TreeVisualizer`` class with ``visualize()`` and
7
+ ``debug_view()`` methods, mirroring the architecture of
8
+ ``pkstruct.linear.visualization.linked_list_visualizer``.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any
14
+
15
+ from .ascii_renderer import AsciiRenderer
16
+
17
+
18
+ class TreeVisualizer:
19
+ """Provides ASCII visualization and debug views for any pkstruct tree."""
20
+
21
+ # Lazy imports to avoid circular dependencies at module load time.
22
+ @staticmethod
23
+ def _tree_types() -> tuple[type, ...]:
24
+ from pkstruct.trees.avl import AVLTree
25
+ from pkstruct.trees.bst import BinarySearchTree
26
+ from pkstruct.trees.interval_tree import IntervalTree
27
+ from pkstruct.trees.red_black import RedBlackTree
28
+
29
+ return (BinarySearchTree, AVLTree, RedBlackTree, IntervalTree)
30
+
31
+ def visualize(
32
+ self,
33
+ tree_instance: Any,
34
+ style: str = "ascii",
35
+ key_repr: Any = None,
36
+ ) -> str:
37
+ """
38
+ Return a string representation of *tree_instance*.
39
+
40
+ Parameters
41
+ ----------
42
+ tree_instance:
43
+ A supported tree instance (BST, AVL, RedBlack, IntervalTree).
44
+ style:
45
+ Rendering style. One of ``"ascii"``, ``"compact"``, or
46
+ ``"levels"``. Defaults to ``"ascii"``.
47
+ key_repr:
48
+ Optional callable to convert a node's key to a string.
49
+ Defaults to ``str``.
50
+
51
+ Returns
52
+ -------
53
+ str
54
+ ASCII art string describing the tree.
55
+
56
+ Raises
57
+ ------
58
+ TypeError
59
+ If *tree_instance* is not a recognised tree type.
60
+ ValueError
61
+ If *style* is not supported.
62
+ """
63
+ if key_repr is None:
64
+ key_repr = str
65
+
66
+ styles = {"ascii", "compact", "levels"}
67
+ if style not in styles:
68
+ raise ValueError(
69
+ f"Unsupported visualisation style {style!r}. "
70
+ f"Use one of: {sorted(styles)}."
71
+ )
72
+
73
+ tree_types = self._tree_types()
74
+ if not isinstance(tree_instance, tree_types):
75
+ raise TypeError(
76
+ f"Unsupported tree type: {type(tree_instance).__name__}. "
77
+ f"Expected one of: {[t.__name__ for t in tree_types]}."
78
+ )
79
+
80
+ root = getattr(tree_instance, "_root", None)
81
+
82
+ if style == "ascii":
83
+ return AsciiRenderer.render(root, key_repr=key_repr)
84
+ if style == "compact":
85
+ return AsciiRenderer.render_compact(root, key_repr=key_repr)
86
+ return AsciiRenderer.render_levels(root, key_repr=key_repr)
87
+
88
+ def debug_view(self, tree_instance: Any) -> dict[str, Any]:
89
+ """
90
+ Return a diagnostic dictionary for *tree_instance*.
91
+
92
+ The returned dict always contains:
93
+
94
+ - ``type``: class name string
95
+ - ``size``: integer node count
96
+ - ``height``: integer tree height
97
+ - ``is_empty``: boolean
98
+ - ``is_valid``: boolean (calls the tree's ``validate()``)
99
+ - ``keys``: list of all keys in in-order
100
+
101
+ Parameters
102
+ ----------
103
+ tree_instance:
104
+ Any supported tree instance.
105
+
106
+ Returns
107
+ -------
108
+ dict
109
+ Dictionary with debugging information.
110
+
111
+ Raises
112
+ ------
113
+ TypeError
114
+ If *tree_instance* is not a recognised tree type.
115
+ """
116
+ tree_types = self._tree_types()
117
+ if not isinstance(tree_instance, tree_types):
118
+ raise TypeError(
119
+ f"Unsupported tree type: {type(tree_instance).__name__}."
120
+ )
121
+
122
+ return {
123
+ "type": type(tree_instance).__name__,
124
+ "size": tree_instance.size(),
125
+ "height": tree_instance.height(),
126
+ "is_empty": tree_instance.is_empty(),
127
+ "is_valid": tree_instance.validate(),
128
+ "keys": list(tree_instance),
129
+ }