exonware-xwnode 0.0.1.12__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.
- exonware/__init__.py +14 -0
- exonware/xwnode/__init__.py +127 -0
- exonware/xwnode/base.py +676 -0
- exonware/xwnode/config.py +178 -0
- exonware/xwnode/contracts.py +730 -0
- exonware/xwnode/errors.py +503 -0
- exonware/xwnode/facade.py +460 -0
- exonware/xwnode/strategies/__init__.py +158 -0
- exonware/xwnode/strategies/advisor.py +463 -0
- exonware/xwnode/strategies/edges/__init__.py +32 -0
- exonware/xwnode/strategies/edges/adj_list.py +227 -0
- exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
- exonware/xwnode/strategies/edges/base.py +169 -0
- exonware/xwnode/strategies/flyweight.py +328 -0
- exonware/xwnode/strategies/impls/__init__.py +13 -0
- exonware/xwnode/strategies/impls/_base_edge.py +403 -0
- exonware/xwnode/strategies/impls/_base_node.py +307 -0
- exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
- exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
- exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
- exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
- exonware/xwnode/strategies/impls/edge_coo.py +533 -0
- exonware/xwnode/strategies/impls/edge_csc.py +447 -0
- exonware/xwnode/strategies/impls/edge_csr.py +492 -0
- exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
- exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
- exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
- exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
- exonware/xwnode/strategies/impls/edge_octree.py +574 -0
- exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
- exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
- exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
- exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
- exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
- exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
- exonware/xwnode/strategies/manager.py +775 -0
- exonware/xwnode/strategies/metrics.py +538 -0
- exonware/xwnode/strategies/migration.py +432 -0
- exonware/xwnode/strategies/nodes/__init__.py +50 -0
- exonware/xwnode/strategies/nodes/_base_node.py +307 -0
- exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
- exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
- exonware/xwnode/strategies/nodes/array_list.py +209 -0
- exonware/xwnode/strategies/nodes/base.py +247 -0
- exonware/xwnode/strategies/nodes/deque.py +200 -0
- exonware/xwnode/strategies/nodes/hash_map.py +135 -0
- exonware/xwnode/strategies/nodes/heap.py +307 -0
- exonware/xwnode/strategies/nodes/linked_list.py +232 -0
- exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
- exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
- exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
- exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
- exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
- exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
- exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
- exonware/xwnode/strategies/nodes/node_btree.py +357 -0
- exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
- exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
- exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
- exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
- exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
- exonware/xwnode/strategies/nodes/node_heap.py +191 -0
- exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
- exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
- exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
- exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
- exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
- exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
- exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
- exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
- exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
- exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
- exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
- exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
- exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
- exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
- exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
- exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
- exonware/xwnode/strategies/nodes/node_treap.py +387 -0
- exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
- exonware/xwnode/strategies/nodes/node_trie.py +252 -0
- exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
- exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
- exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
- exonware/xwnode/strategies/nodes/queue.py +161 -0
- exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
- exonware/xwnode/strategies/nodes/stack.py +152 -0
- exonware/xwnode/strategies/nodes/trie.py +274 -0
- exonware/xwnode/strategies/nodes/union_find.py +283 -0
- exonware/xwnode/strategies/pattern_detector.py +603 -0
- exonware/xwnode/strategies/performance_monitor.py +487 -0
- exonware/xwnode/strategies/queries/__init__.py +24 -0
- exonware/xwnode/strategies/queries/base.py +236 -0
- exonware/xwnode/strategies/queries/cql.py +201 -0
- exonware/xwnode/strategies/queries/cypher.py +181 -0
- exonware/xwnode/strategies/queries/datalog.py +70 -0
- exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
- exonware/xwnode/strategies/queries/eql.py +70 -0
- exonware/xwnode/strategies/queries/flux.py +70 -0
- exonware/xwnode/strategies/queries/gql.py +70 -0
- exonware/xwnode/strategies/queries/graphql.py +240 -0
- exonware/xwnode/strategies/queries/gremlin.py +181 -0
- exonware/xwnode/strategies/queries/hiveql.py +214 -0
- exonware/xwnode/strategies/queries/hql.py +70 -0
- exonware/xwnode/strategies/queries/jmespath.py +219 -0
- exonware/xwnode/strategies/queries/jq.py +66 -0
- exonware/xwnode/strategies/queries/json_query.py +66 -0
- exonware/xwnode/strategies/queries/jsoniq.py +248 -0
- exonware/xwnode/strategies/queries/kql.py +70 -0
- exonware/xwnode/strategies/queries/linq.py +238 -0
- exonware/xwnode/strategies/queries/logql.py +70 -0
- exonware/xwnode/strategies/queries/mql.py +68 -0
- exonware/xwnode/strategies/queries/n1ql.py +210 -0
- exonware/xwnode/strategies/queries/partiql.py +70 -0
- exonware/xwnode/strategies/queries/pig.py +215 -0
- exonware/xwnode/strategies/queries/promql.py +70 -0
- exonware/xwnode/strategies/queries/sparql.py +220 -0
- exonware/xwnode/strategies/queries/sql.py +275 -0
- exonware/xwnode/strategies/queries/xml_query.py +66 -0
- exonware/xwnode/strategies/queries/xpath.py +223 -0
- exonware/xwnode/strategies/queries/xquery.py +258 -0
- exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
- exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
- exonware/xwnode/strategies/registry.py +604 -0
- exonware/xwnode/strategies/simple.py +273 -0
- exonware/xwnode/strategies/utils.py +532 -0
- exonware/xwnode/types.py +912 -0
- exonware/xwnode/version.py +78 -0
- exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
- exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
- exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
- exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,480 @@
|
|
1
|
+
"""
|
2
|
+
Tree Set Node Strategy Implementation
|
3
|
+
|
4
|
+
This module implements the SET_TREE strategy for ordered set operations
|
5
|
+
using a balanced binary search tree with efficient range queries.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Iterator, List, Dict, Optional, Tuple
|
9
|
+
from .base import ANodeTreeStrategy
|
10
|
+
from ...types import NodeMode, NodeTrait
|
11
|
+
|
12
|
+
|
13
|
+
class TreeNode:
|
14
|
+
"""Node in the balanced binary search tree."""
|
15
|
+
|
16
|
+
def __init__(self, key: str, value: Any = None):
|
17
|
+
self.key = key
|
18
|
+
self.value = value
|
19
|
+
self.left: Optional['TreeNode'] = None
|
20
|
+
self.right: Optional['TreeNode'] = None
|
21
|
+
self.height = 1
|
22
|
+
self.size = 1 # Size of subtree
|
23
|
+
|
24
|
+
|
25
|
+
class SetTreeStrategy(ANodeTreeStrategy):
|
26
|
+
"""
|
27
|
+
Tree Set node strategy for ordered set operations.
|
28
|
+
|
29
|
+
Provides efficient ordered set operations with logarithmic complexity
|
30
|
+
for insertions, deletions, and range queries.
|
31
|
+
"""
|
32
|
+
|
33
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
34
|
+
"""Initialize the Tree Set strategy."""
|
35
|
+
super().__init__(NodeMode.SET_TREE, traits, **options)
|
36
|
+
|
37
|
+
self.allow_duplicates = options.get('allow_duplicates', False)
|
38
|
+
self.case_sensitive = options.get('case_sensitive', True)
|
39
|
+
|
40
|
+
# Core AVL tree
|
41
|
+
self._root: Optional[TreeNode] = None
|
42
|
+
self._size = 0
|
43
|
+
|
44
|
+
# Key-value mapping for compatibility
|
45
|
+
self._values: Dict[str, Any] = {}
|
46
|
+
|
47
|
+
def get_supported_traits(self) -> NodeTrait:
|
48
|
+
"""Get the traits supported by the tree set strategy."""
|
49
|
+
return (NodeTrait.ORDERED | NodeTrait.INDEXED | NodeTrait.HIERARCHICAL)
|
50
|
+
|
51
|
+
def _normalize_key(self, key: str) -> str:
|
52
|
+
"""Normalize key based on case sensitivity."""
|
53
|
+
return key if self.case_sensitive else key.lower()
|
54
|
+
|
55
|
+
def _get_height(self, node: Optional[TreeNode]) -> int:
|
56
|
+
"""Get height of node."""
|
57
|
+
return node.height if node else 0
|
58
|
+
|
59
|
+
def _get_size(self, node: Optional[TreeNode]) -> int:
|
60
|
+
"""Get size of subtree."""
|
61
|
+
return node.size if node else 0
|
62
|
+
|
63
|
+
def _update_node(self, node: TreeNode) -> None:
|
64
|
+
"""Update height and size of node."""
|
65
|
+
node.height = max(self._get_height(node.left), self._get_height(node.right)) + 1
|
66
|
+
node.size = self._get_size(node.left) + self._get_size(node.right) + 1
|
67
|
+
|
68
|
+
def _get_balance(self, node: Optional[TreeNode]) -> int:
|
69
|
+
"""Get balance factor of node."""
|
70
|
+
return self._get_height(node.left) - self._get_height(node.right) if node else 0
|
71
|
+
|
72
|
+
def _rotate_right(self, y: TreeNode) -> TreeNode:
|
73
|
+
"""Right rotation for AVL balancing."""
|
74
|
+
x = y.left
|
75
|
+
t2 = x.right
|
76
|
+
|
77
|
+
# Perform rotation
|
78
|
+
x.right = y
|
79
|
+
y.left = t2
|
80
|
+
|
81
|
+
# Update heights and sizes
|
82
|
+
self._update_node(y)
|
83
|
+
self._update_node(x)
|
84
|
+
|
85
|
+
return x
|
86
|
+
|
87
|
+
def _rotate_left(self, x: TreeNode) -> TreeNode:
|
88
|
+
"""Left rotation for AVL balancing."""
|
89
|
+
y = x.right
|
90
|
+
t2 = y.left
|
91
|
+
|
92
|
+
# Perform rotation
|
93
|
+
y.left = x
|
94
|
+
x.right = t2
|
95
|
+
|
96
|
+
# Update heights and sizes
|
97
|
+
self._update_node(x)
|
98
|
+
self._update_node(y)
|
99
|
+
|
100
|
+
return y
|
101
|
+
|
102
|
+
def _insert_node(self, node: Optional[TreeNode], key: str, value: Any) -> TreeNode:
|
103
|
+
"""Insert node with AVL balancing."""
|
104
|
+
# Standard BST insertion
|
105
|
+
if not node:
|
106
|
+
return TreeNode(key, value)
|
107
|
+
|
108
|
+
if key < node.key:
|
109
|
+
node.left = self._insert_node(node.left, key, value)
|
110
|
+
elif key > node.key:
|
111
|
+
node.right = self._insert_node(node.right, key, value)
|
112
|
+
else:
|
113
|
+
# Key exists
|
114
|
+
if self.allow_duplicates:
|
115
|
+
# Insert as right child for duplicates
|
116
|
+
node.right = self._insert_node(node.right, key, value)
|
117
|
+
else:
|
118
|
+
# Update value
|
119
|
+
node.value = value
|
120
|
+
return node
|
121
|
+
|
122
|
+
# Update height and size
|
123
|
+
self._update_node(node)
|
124
|
+
|
125
|
+
# Get balance factor
|
126
|
+
balance = self._get_balance(node)
|
127
|
+
|
128
|
+
# Left heavy
|
129
|
+
if balance > 1:
|
130
|
+
if key < node.left.key:
|
131
|
+
# Left-Left case
|
132
|
+
return self._rotate_right(node)
|
133
|
+
else:
|
134
|
+
# Left-Right case
|
135
|
+
node.left = self._rotate_left(node.left)
|
136
|
+
return self._rotate_right(node)
|
137
|
+
|
138
|
+
# Right heavy
|
139
|
+
if balance < -1:
|
140
|
+
if key > node.right.key:
|
141
|
+
# Right-Right case
|
142
|
+
return self._rotate_left(node)
|
143
|
+
else:
|
144
|
+
# Right-Left case
|
145
|
+
node.right = self._rotate_right(node.right)
|
146
|
+
return self._rotate_left(node)
|
147
|
+
|
148
|
+
return node
|
149
|
+
|
150
|
+
def _find_min(self, node: TreeNode) -> TreeNode:
|
151
|
+
"""Find minimum node in subtree."""
|
152
|
+
while node.left:
|
153
|
+
node = node.left
|
154
|
+
return node
|
155
|
+
|
156
|
+
def _delete_node(self, node: Optional[TreeNode], key: str) -> Optional[TreeNode]:
|
157
|
+
"""Delete node with AVL balancing."""
|
158
|
+
if not node:
|
159
|
+
return node
|
160
|
+
|
161
|
+
if key < node.key:
|
162
|
+
node.left = self._delete_node(node.left, key)
|
163
|
+
elif key > node.key:
|
164
|
+
node.right = self._delete_node(node.right, key)
|
165
|
+
else:
|
166
|
+
# Node to delete found
|
167
|
+
if not node.left or not node.right:
|
168
|
+
# Node with only one child or no child
|
169
|
+
temp = node.left if node.left else node.right
|
170
|
+
if not temp:
|
171
|
+
# No child case
|
172
|
+
temp = node
|
173
|
+
node = None
|
174
|
+
else:
|
175
|
+
# One child case
|
176
|
+
node = temp
|
177
|
+
else:
|
178
|
+
# Node with two children
|
179
|
+
temp = self._find_min(node.right)
|
180
|
+
node.key = temp.key
|
181
|
+
node.value = temp.value
|
182
|
+
node.right = self._delete_node(node.right, temp.key)
|
183
|
+
|
184
|
+
if not node:
|
185
|
+
return node
|
186
|
+
|
187
|
+
# Update height and size
|
188
|
+
self._update_node(node)
|
189
|
+
|
190
|
+
# Get balance factor
|
191
|
+
balance = self._get_balance(node)
|
192
|
+
|
193
|
+
# Left heavy
|
194
|
+
if balance > 1:
|
195
|
+
if self._get_balance(node.left) >= 0:
|
196
|
+
return self._rotate_right(node)
|
197
|
+
else:
|
198
|
+
node.left = self._rotate_left(node.left)
|
199
|
+
return self._rotate_right(node)
|
200
|
+
|
201
|
+
# Right heavy
|
202
|
+
if balance < -1:
|
203
|
+
if self._get_balance(node.right) <= 0:
|
204
|
+
return self._rotate_left(node)
|
205
|
+
else:
|
206
|
+
node.right = self._rotate_right(node.right)
|
207
|
+
return self._rotate_left(node)
|
208
|
+
|
209
|
+
return node
|
210
|
+
|
211
|
+
def _search_node(self, node: Optional[TreeNode], key: str) -> Optional[TreeNode]:
|
212
|
+
"""Search for node with given key."""
|
213
|
+
if not node or node.key == key:
|
214
|
+
return node
|
215
|
+
|
216
|
+
if key < node.key:
|
217
|
+
return self._search_node(node.left, key)
|
218
|
+
else:
|
219
|
+
return self._search_node(node.right, key)
|
220
|
+
|
221
|
+
def _inorder_traversal(self, node: Optional[TreeNode], result: List[Tuple[str, Any]]) -> None:
|
222
|
+
"""Inorder traversal to get sorted keys."""
|
223
|
+
if node:
|
224
|
+
self._inorder_traversal(node.left, result)
|
225
|
+
result.append((node.key, node.value))
|
226
|
+
self._inorder_traversal(node.right, result)
|
227
|
+
|
228
|
+
# ============================================================================
|
229
|
+
# CORE OPERATIONS (Key-based interface for compatibility)
|
230
|
+
# ============================================================================
|
231
|
+
|
232
|
+
def put(self, key: Any, value: Any = None) -> None:
|
233
|
+
"""Add key to tree set."""
|
234
|
+
key_str = self._normalize_key(str(key))
|
235
|
+
|
236
|
+
old_size = self._get_size(self._root)
|
237
|
+
self._root = self._insert_node(self._root, key_str, value)
|
238
|
+
new_size = self._get_size(self._root)
|
239
|
+
|
240
|
+
if new_size > old_size:
|
241
|
+
self._size += 1
|
242
|
+
|
243
|
+
self._values[key_str] = value if value is not None else True
|
244
|
+
|
245
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
246
|
+
"""Get value by key."""
|
247
|
+
key_str = str(key)
|
248
|
+
|
249
|
+
if key_str == "sorted_keys":
|
250
|
+
return self.get_sorted_keys()
|
251
|
+
elif key_str == "tree_info":
|
252
|
+
return {
|
253
|
+
'height': self._get_height(self._root),
|
254
|
+
'size': self._size,
|
255
|
+
'balanced': self.is_balanced()
|
256
|
+
}
|
257
|
+
|
258
|
+
normalized_key = self._normalize_key(key_str)
|
259
|
+
node = self._search_node(self._root, normalized_key)
|
260
|
+
return node.value if node else default
|
261
|
+
|
262
|
+
def has(self, key: Any) -> bool:
|
263
|
+
"""Check if key exists in set."""
|
264
|
+
key_str = self._normalize_key(str(key))
|
265
|
+
return self._search_node(self._root, key_str) is not None
|
266
|
+
|
267
|
+
def remove(self, key: Any) -> bool:
|
268
|
+
"""Remove key from set."""
|
269
|
+
key_str = self._normalize_key(str(key))
|
270
|
+
|
271
|
+
if not self._search_node(self._root, key_str):
|
272
|
+
return False
|
273
|
+
|
274
|
+
self._root = self._delete_node(self._root, key_str)
|
275
|
+
self._size -= 1
|
276
|
+
self._values.pop(key_str, None)
|
277
|
+
return True
|
278
|
+
|
279
|
+
def delete(self, key: Any) -> bool:
|
280
|
+
"""Remove key from set (alias for remove)."""
|
281
|
+
return self.remove(key)
|
282
|
+
|
283
|
+
def clear(self) -> None:
|
284
|
+
"""Clear all data."""
|
285
|
+
self._root = None
|
286
|
+
self._size = 0
|
287
|
+
self._values.clear()
|
288
|
+
|
289
|
+
def keys(self) -> Iterator[str]:
|
290
|
+
"""Get all keys in sorted order."""
|
291
|
+
result = []
|
292
|
+
self._inorder_traversal(self._root, result)
|
293
|
+
for key, _ in result:
|
294
|
+
yield key
|
295
|
+
|
296
|
+
def values(self) -> Iterator[Any]:
|
297
|
+
"""Get all values in key order."""
|
298
|
+
result = []
|
299
|
+
self._inorder_traversal(self._root, result)
|
300
|
+
for _, value in result:
|
301
|
+
yield value
|
302
|
+
|
303
|
+
def items(self) -> Iterator[tuple[str, Any]]:
|
304
|
+
"""Get all key-value pairs in sorted order."""
|
305
|
+
result = []
|
306
|
+
self._inorder_traversal(self._root, result)
|
307
|
+
for key, value in result:
|
308
|
+
yield (key, value)
|
309
|
+
|
310
|
+
def __len__(self) -> int:
|
311
|
+
"""Get number of elements in set."""
|
312
|
+
return self._size
|
313
|
+
|
314
|
+
def to_native(self) -> List[str]:
|
315
|
+
"""Convert to native Python sorted list."""
|
316
|
+
return list(self.keys())
|
317
|
+
|
318
|
+
@property
|
319
|
+
def is_list(self) -> bool:
|
320
|
+
"""This can behave like a list for ordered access."""
|
321
|
+
return True
|
322
|
+
|
323
|
+
@property
|
324
|
+
def is_dict(self) -> bool:
|
325
|
+
"""This behaves like a dict."""
|
326
|
+
return True
|
327
|
+
|
328
|
+
# ============================================================================
|
329
|
+
# SET-SPECIFIC OPERATIONS
|
330
|
+
# ============================================================================
|
331
|
+
|
332
|
+
def add(self, key: str) -> bool:
|
333
|
+
"""Add element to set. Returns True if element was new."""
|
334
|
+
old_size = self._size
|
335
|
+
self.put(key)
|
336
|
+
return self._size > old_size
|
337
|
+
|
338
|
+
def discard(self, key: str) -> None:
|
339
|
+
"""Remove element if present (no error if not found)."""
|
340
|
+
self.remove(key)
|
341
|
+
|
342
|
+
def get_sorted_keys(self) -> List[str]:
|
343
|
+
"""Get all keys in sorted order."""
|
344
|
+
return list(self.keys())
|
345
|
+
|
346
|
+
def get_range(self, start_key: str, end_key: str, inclusive: bool = True) -> List[str]:
|
347
|
+
"""Get keys in range [start_key, end_key]."""
|
348
|
+
result = []
|
349
|
+
start_norm = self._normalize_key(start_key)
|
350
|
+
end_norm = self._normalize_key(end_key)
|
351
|
+
|
352
|
+
for key in self.keys():
|
353
|
+
if inclusive:
|
354
|
+
if start_norm <= key <= end_norm:
|
355
|
+
result.append(key)
|
356
|
+
else:
|
357
|
+
if start_norm < key < end_norm:
|
358
|
+
result.append(key)
|
359
|
+
|
360
|
+
return result
|
361
|
+
|
362
|
+
def lower_bound(self, key: str) -> Optional[str]:
|
363
|
+
"""Find first key >= given key."""
|
364
|
+
norm_key = self._normalize_key(key)
|
365
|
+
|
366
|
+
for k in self.keys():
|
367
|
+
if k >= norm_key:
|
368
|
+
return k
|
369
|
+
|
370
|
+
return None
|
371
|
+
|
372
|
+
def upper_bound(self, key: str) -> Optional[str]:
|
373
|
+
"""Find first key > given key."""
|
374
|
+
norm_key = self._normalize_key(key)
|
375
|
+
|
376
|
+
for k in self.keys():
|
377
|
+
if k > norm_key:
|
378
|
+
return k
|
379
|
+
|
380
|
+
return None
|
381
|
+
|
382
|
+
def floor(self, key: str) -> Optional[str]:
|
383
|
+
"""Find largest key <= given key."""
|
384
|
+
norm_key = self._normalize_key(key)
|
385
|
+
result = None
|
386
|
+
|
387
|
+
for k in self.keys():
|
388
|
+
if k <= norm_key:
|
389
|
+
result = k
|
390
|
+
else:
|
391
|
+
break
|
392
|
+
|
393
|
+
return result
|
394
|
+
|
395
|
+
def ceiling(self, key: str) -> Optional[str]:
|
396
|
+
"""Find smallest key >= given key."""
|
397
|
+
return self.lower_bound(key)
|
398
|
+
|
399
|
+
def first(self) -> Optional[str]:
|
400
|
+
"""Get first (smallest) key."""
|
401
|
+
if self._root:
|
402
|
+
node = self._root
|
403
|
+
while node.left:
|
404
|
+
node = node.left
|
405
|
+
return node.key
|
406
|
+
return None
|
407
|
+
|
408
|
+
def last(self) -> Optional[str]:
|
409
|
+
"""Get last (largest) key."""
|
410
|
+
if self._root:
|
411
|
+
node = self._root
|
412
|
+
while node.right:
|
413
|
+
node = node.right
|
414
|
+
return node.key
|
415
|
+
return None
|
416
|
+
|
417
|
+
def is_balanced(self) -> bool:
|
418
|
+
"""Check if tree is balanced."""
|
419
|
+
def _check_balance(node: Optional[TreeNode]) -> bool:
|
420
|
+
if not node:
|
421
|
+
return True
|
422
|
+
|
423
|
+
balance = self._get_balance(node)
|
424
|
+
if abs(balance) > 1:
|
425
|
+
return False
|
426
|
+
|
427
|
+
return _check_balance(node.left) and _check_balance(node.right)
|
428
|
+
|
429
|
+
return _check_balance(self._root)
|
430
|
+
|
431
|
+
def get_tree_statistics(self) -> Dict[str, Any]:
|
432
|
+
"""Get comprehensive tree statistics."""
|
433
|
+
if not self._root:
|
434
|
+
return {'size': 0, 'height': 0, 'balanced': True}
|
435
|
+
|
436
|
+
return {
|
437
|
+
'size': self._size,
|
438
|
+
'height': self._get_height(self._root),
|
439
|
+
'balanced': self.is_balanced(),
|
440
|
+
'first_key': self.first(),
|
441
|
+
'last_key': self.last(),
|
442
|
+
'case_sensitive': self.case_sensitive,
|
443
|
+
'allow_duplicates': self.allow_duplicates
|
444
|
+
}
|
445
|
+
|
446
|
+
# ============================================================================
|
447
|
+
# PERFORMANCE CHARACTERISTICS
|
448
|
+
# ============================================================================
|
449
|
+
|
450
|
+
@property
|
451
|
+
def backend_info(self) -> Dict[str, Any]:
|
452
|
+
"""Get backend implementation info."""
|
453
|
+
return {
|
454
|
+
'strategy': 'SET_TREE',
|
455
|
+
'backend': 'AVL balanced binary search tree',
|
456
|
+
'case_sensitive': self.case_sensitive,
|
457
|
+
'allow_duplicates': self.allow_duplicates,
|
458
|
+
'complexity': {
|
459
|
+
'insert': 'O(log n)',
|
460
|
+
'delete': 'O(log n)',
|
461
|
+
'search': 'O(log n)',
|
462
|
+
'range_query': 'O(log n + k)', # k = result size
|
463
|
+
'traversal': 'O(n)',
|
464
|
+
'space': 'O(n)'
|
465
|
+
}
|
466
|
+
}
|
467
|
+
|
468
|
+
@property
|
469
|
+
def metrics(self) -> Dict[str, Any]:
|
470
|
+
"""Get performance metrics."""
|
471
|
+
stats = self.get_tree_statistics()
|
472
|
+
|
473
|
+
return {
|
474
|
+
'size': stats['size'],
|
475
|
+
'height': stats['height'],
|
476
|
+
'balanced': stats['balanced'],
|
477
|
+
'first_key': stats.get('first_key', 'None'),
|
478
|
+
'last_key': stats.get('last_key', 'None'),
|
479
|
+
'memory_usage': f"{self._size * 80} bytes (estimated)"
|
480
|
+
}
|