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,497 @@
|
|
1
|
+
#exonware\xnode\strategies\impls\node_red_black_tree.py
|
2
|
+
"""
|
3
|
+
Red-Black Tree Node Strategy Implementation
|
4
|
+
|
5
|
+
This module implements the RED_BLACK_TREE strategy for self-balancing binary
|
6
|
+
search trees with guaranteed O(log n) height and operations.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Any, Iterator, List, Dict, Optional, Tuple
|
10
|
+
from .base import ANodeTreeStrategy
|
11
|
+
from ...types import NodeMode, NodeTrait
|
12
|
+
|
13
|
+
|
14
|
+
class RedBlackTreeNode:
|
15
|
+
"""Node in the red-black tree."""
|
16
|
+
|
17
|
+
def __init__(self, key: str, value: Any = None, color: str = 'RED'):
|
18
|
+
self.key = key
|
19
|
+
self.value = value
|
20
|
+
self.color = color # 'RED' or 'BLACK'
|
21
|
+
self.left: Optional['RedBlackTreeNode'] = None
|
22
|
+
self.right: Optional['RedBlackTreeNode'] = None
|
23
|
+
self.parent: Optional['RedBlackTreeNode'] = None
|
24
|
+
self._hash = None
|
25
|
+
|
26
|
+
def __hash__(self) -> int:
|
27
|
+
"""Cache hash for performance."""
|
28
|
+
if self._hash is None:
|
29
|
+
self._hash = hash((self.key, self.value, self.color))
|
30
|
+
return self._hash
|
31
|
+
|
32
|
+
def __eq__(self, other) -> bool:
|
33
|
+
"""Structural equality."""
|
34
|
+
if not isinstance(other, RedBlackTreeNode):
|
35
|
+
return False
|
36
|
+
return (self.key == other.key and
|
37
|
+
self.value == other.value and
|
38
|
+
self.color == other.color)
|
39
|
+
|
40
|
+
def is_red(self) -> bool:
|
41
|
+
"""Check if node is red."""
|
42
|
+
return self.color == 'RED'
|
43
|
+
|
44
|
+
def is_black(self) -> bool:
|
45
|
+
"""Check if node is black."""
|
46
|
+
return self.color == 'BLACK'
|
47
|
+
|
48
|
+
def set_red(self) -> None:
|
49
|
+
"""Set node color to red."""
|
50
|
+
self.color = 'RED'
|
51
|
+
|
52
|
+
def set_black(self) -> None:
|
53
|
+
"""Set node color to black."""
|
54
|
+
self.color = 'BLACK'
|
55
|
+
|
56
|
+
|
57
|
+
class RedBlackTreeStrategy(ANodeTreeStrategy):
|
58
|
+
"""
|
59
|
+
Red-black tree node strategy for self-balancing binary search trees.
|
60
|
+
|
61
|
+
Provides guaranteed O(log n) height and operations through color-based
|
62
|
+
balancing rules and rotations.
|
63
|
+
"""
|
64
|
+
|
65
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
66
|
+
"""Initialize the red-black tree strategy."""
|
67
|
+
super().__init__(NodeMode.RED_BLACK_TREE, traits, **options)
|
68
|
+
|
69
|
+
self.case_sensitive = options.get('case_sensitive', True)
|
70
|
+
|
71
|
+
# Core red-black tree
|
72
|
+
self._root: Optional[RedBlackTreeNode] = None
|
73
|
+
self._size = 0
|
74
|
+
|
75
|
+
# Statistics
|
76
|
+
self._total_insertions = 0
|
77
|
+
self._total_deletions = 0
|
78
|
+
self._total_rotations = 0
|
79
|
+
self._max_height = 0
|
80
|
+
|
81
|
+
def get_supported_traits(self) -> NodeTrait:
|
82
|
+
"""Get the traits supported by the red-black tree strategy."""
|
83
|
+
return (NodeTrait.ORDERED | NodeTrait.INDEXED)
|
84
|
+
|
85
|
+
def _normalize_key(self, key: str) -> str:
|
86
|
+
"""Normalize key based on case sensitivity."""
|
87
|
+
return key if self.case_sensitive else key.lower()
|
88
|
+
|
89
|
+
def _get_height(self, node: Optional[RedBlackTreeNode]) -> int:
|
90
|
+
"""Get height of node."""
|
91
|
+
if not node:
|
92
|
+
return 0
|
93
|
+
|
94
|
+
left_height = self._get_height(node.left)
|
95
|
+
right_height = self._get_height(node.right)
|
96
|
+
return 1 + max(left_height, right_height)
|
97
|
+
|
98
|
+
def _rotate_left(self, node: RedBlackTreeNode) -> None:
|
99
|
+
"""Left rotation around node."""
|
100
|
+
right_child = node.right
|
101
|
+
if not right_child:
|
102
|
+
return
|
103
|
+
|
104
|
+
# Update parent connections
|
105
|
+
node.right = right_child.left
|
106
|
+
if right_child.left:
|
107
|
+
right_child.left.parent = node
|
108
|
+
|
109
|
+
right_child.parent = node.parent
|
110
|
+
if not node.parent:
|
111
|
+
self._root = right_child
|
112
|
+
elif node == node.parent.left:
|
113
|
+
node.parent.left = right_child
|
114
|
+
else:
|
115
|
+
node.parent.right = right_child
|
116
|
+
|
117
|
+
# Update rotation
|
118
|
+
right_child.left = node
|
119
|
+
node.parent = right_child
|
120
|
+
|
121
|
+
self._total_rotations += 1
|
122
|
+
|
123
|
+
def _rotate_right(self, node: RedBlackTreeNode) -> None:
|
124
|
+
"""Right rotation around node."""
|
125
|
+
left_child = node.left
|
126
|
+
if not left_child:
|
127
|
+
return
|
128
|
+
|
129
|
+
# Update parent connections
|
130
|
+
node.left = left_child.right
|
131
|
+
if left_child.right:
|
132
|
+
left_child.right.parent = node
|
133
|
+
|
134
|
+
left_child.parent = node.parent
|
135
|
+
if not node.parent:
|
136
|
+
self._root = left_child
|
137
|
+
elif node == node.parent.right:
|
138
|
+
node.parent.right = left_child
|
139
|
+
else:
|
140
|
+
node.parent.left = left_child
|
141
|
+
|
142
|
+
# Update rotation
|
143
|
+
left_child.right = node
|
144
|
+
node.parent = left_child
|
145
|
+
|
146
|
+
self._total_rotations += 1
|
147
|
+
|
148
|
+
def _fix_insertion(self, node: RedBlackTreeNode) -> None:
|
149
|
+
"""Fix red-black tree properties after insertion."""
|
150
|
+
while node.parent and node.parent.is_red():
|
151
|
+
if node.parent == node.parent.parent.left:
|
152
|
+
uncle = node.parent.parent.right
|
153
|
+
if uncle and uncle.is_red():
|
154
|
+
# Case 1: Uncle is red
|
155
|
+
node.parent.set_black()
|
156
|
+
uncle.set_black()
|
157
|
+
node.parent.parent.set_red()
|
158
|
+
node = node.parent.parent
|
159
|
+
else:
|
160
|
+
# Case 2: Uncle is black and node is right child
|
161
|
+
if node == node.parent.right:
|
162
|
+
node = node.parent
|
163
|
+
self._rotate_left(node)
|
164
|
+
|
165
|
+
# Case 3: Uncle is black and node is left child
|
166
|
+
node.parent.set_black()
|
167
|
+
node.parent.parent.set_red()
|
168
|
+
self._rotate_right(node.parent.parent)
|
169
|
+
else:
|
170
|
+
# Mirror cases for right side
|
171
|
+
uncle = node.parent.parent.left
|
172
|
+
if uncle and uncle.is_red():
|
173
|
+
# Case 1: Uncle is red
|
174
|
+
node.parent.set_black()
|
175
|
+
uncle.set_black()
|
176
|
+
node.parent.parent.set_red()
|
177
|
+
node = node.parent.parent
|
178
|
+
else:
|
179
|
+
# Case 2: Uncle is black and node is left child
|
180
|
+
if node == node.parent.left:
|
181
|
+
node = node.parent
|
182
|
+
self._rotate_right(node)
|
183
|
+
|
184
|
+
# Case 3: Uncle is black and node is right child
|
185
|
+
node.parent.set_black()
|
186
|
+
node.parent.parent.set_red()
|
187
|
+
self._rotate_left(node.parent.parent)
|
188
|
+
|
189
|
+
self._root.set_black()
|
190
|
+
|
191
|
+
def _insert_node(self, key: str, value: Any) -> bool:
|
192
|
+
"""Insert node with given key and value."""
|
193
|
+
normalized_key = self._normalize_key(key)
|
194
|
+
|
195
|
+
# Create new node
|
196
|
+
new_node = RedBlackTreeNode(key, value, 'RED')
|
197
|
+
|
198
|
+
# Find insertion point
|
199
|
+
current = self._root
|
200
|
+
parent = None
|
201
|
+
|
202
|
+
while current:
|
203
|
+
parent = current
|
204
|
+
current_key = self._normalize_key(current.key)
|
205
|
+
if normalized_key < current_key:
|
206
|
+
current = current.left
|
207
|
+
elif normalized_key > current_key:
|
208
|
+
current = current.right
|
209
|
+
else:
|
210
|
+
# Key already exists, update value
|
211
|
+
current.value = value
|
212
|
+
return False
|
213
|
+
|
214
|
+
# Insert new node
|
215
|
+
new_node.parent = parent
|
216
|
+
if not parent:
|
217
|
+
self._root = new_node
|
218
|
+
elif normalized_key < self._normalize_key(parent.key):
|
219
|
+
parent.left = new_node
|
220
|
+
else:
|
221
|
+
parent.right = new_node
|
222
|
+
|
223
|
+
# Fix red-black tree properties
|
224
|
+
self._fix_insertion(new_node)
|
225
|
+
|
226
|
+
self._size += 1
|
227
|
+
self._total_insertions += 1
|
228
|
+
self._max_height = max(self._max_height, self._get_height(self._root))
|
229
|
+
return True
|
230
|
+
|
231
|
+
def _find_node(self, key: str) -> Optional[RedBlackTreeNode]:
|
232
|
+
"""Find node with given key."""
|
233
|
+
normalized_key = self._normalize_key(key)
|
234
|
+
current = self._root
|
235
|
+
|
236
|
+
while current:
|
237
|
+
current_key = self._normalize_key(current.key)
|
238
|
+
if normalized_key < current_key:
|
239
|
+
current = current.left
|
240
|
+
elif normalized_key > current_key:
|
241
|
+
current = current.right
|
242
|
+
else:
|
243
|
+
return current
|
244
|
+
|
245
|
+
return None
|
246
|
+
|
247
|
+
def _find_min(self, node: RedBlackTreeNode) -> RedBlackTreeNode:
|
248
|
+
"""Find minimum node in subtree."""
|
249
|
+
while node.left:
|
250
|
+
node = node.left
|
251
|
+
return node
|
252
|
+
|
253
|
+
def _find_max(self, node: RedBlackTreeNode) -> RedBlackTreeNode:
|
254
|
+
"""Find maximum node in subtree."""
|
255
|
+
while node.right:
|
256
|
+
node = node.right
|
257
|
+
return node
|
258
|
+
|
259
|
+
def _delete_node(self, key: str) -> bool:
|
260
|
+
"""Delete node with given key."""
|
261
|
+
node = self._find_node(key)
|
262
|
+
if not node:
|
263
|
+
return False
|
264
|
+
|
265
|
+
# Find replacement node
|
266
|
+
if not node.left:
|
267
|
+
replacement = node.right
|
268
|
+
elif not node.right:
|
269
|
+
replacement = node.left
|
270
|
+
else:
|
271
|
+
replacement = self._find_min(node.right)
|
272
|
+
node.key = replacement.key
|
273
|
+
node.value = replacement.value
|
274
|
+
node = replacement
|
275
|
+
|
276
|
+
# Remove node
|
277
|
+
if replacement:
|
278
|
+
replacement.parent = node.parent
|
279
|
+
|
280
|
+
if not node.parent:
|
281
|
+
self._root = replacement
|
282
|
+
elif node == node.parent.left:
|
283
|
+
node.parent.left = replacement
|
284
|
+
else:
|
285
|
+
node.parent.right = replacement
|
286
|
+
|
287
|
+
# Fix red-black tree properties if needed
|
288
|
+
if node.is_black() and replacement:
|
289
|
+
self._fix_deletion(replacement)
|
290
|
+
elif node.is_black():
|
291
|
+
self._fix_deletion(None)
|
292
|
+
|
293
|
+
self._size -= 1
|
294
|
+
self._total_deletions += 1
|
295
|
+
return True
|
296
|
+
|
297
|
+
def _fix_deletion(self, node: Optional[RedBlackTreeNode]) -> None:
|
298
|
+
"""Fix red-black tree properties after deletion."""
|
299
|
+
while node != self._root and (not node or node.is_black()):
|
300
|
+
if node == node.parent.left:
|
301
|
+
sibling = node.parent.right
|
302
|
+
if sibling and sibling.is_red():
|
303
|
+
# Case 1: Sibling is red
|
304
|
+
sibling.set_black()
|
305
|
+
node.parent.set_red()
|
306
|
+
self._rotate_left(node.parent)
|
307
|
+
sibling = node.parent.right
|
308
|
+
|
309
|
+
if (not sibling.left or sibling.left.is_black()) and \
|
310
|
+
(not sibling.right or sibling.right.is_black()):
|
311
|
+
# Case 2: Sibling and its children are black
|
312
|
+
sibling.set_red()
|
313
|
+
node = node.parent
|
314
|
+
else:
|
315
|
+
if not sibling.right or sibling.right.is_black():
|
316
|
+
# Case 3: Sibling's right child is black
|
317
|
+
sibling.left.set_black()
|
318
|
+
sibling.set_red()
|
319
|
+
self._rotate_right(sibling)
|
320
|
+
sibling = node.parent.right
|
321
|
+
|
322
|
+
# Case 4: Sibling's right child is red
|
323
|
+
sibling.color = node.parent.color
|
324
|
+
node.parent.set_black()
|
325
|
+
sibling.right.set_black()
|
326
|
+
self._rotate_left(node.parent)
|
327
|
+
node = self._root
|
328
|
+
else:
|
329
|
+
# Mirror cases for right side
|
330
|
+
sibling = node.parent.left
|
331
|
+
if sibling and sibling.is_red():
|
332
|
+
# Case 1: Sibling is red
|
333
|
+
sibling.set_black()
|
334
|
+
node.parent.set_red()
|
335
|
+
self._rotate_right(node.parent)
|
336
|
+
sibling = node.parent.left
|
337
|
+
|
338
|
+
if (not sibling.right or sibling.right.is_black()) and \
|
339
|
+
(not sibling.left or sibling.left.is_black()):
|
340
|
+
# Case 2: Sibling and its children are black
|
341
|
+
sibling.set_red()
|
342
|
+
node = node.parent
|
343
|
+
else:
|
344
|
+
if not sibling.left or sibling.left.is_black():
|
345
|
+
# Case 3: Sibling's left child is black
|
346
|
+
sibling.right.set_black()
|
347
|
+
sibling.set_red()
|
348
|
+
self._rotate_left(sibling)
|
349
|
+
sibling = node.parent.left
|
350
|
+
|
351
|
+
# Case 4: Sibling's left child is red
|
352
|
+
sibling.color = node.parent.color
|
353
|
+
node.parent.set_black()
|
354
|
+
sibling.left.set_black()
|
355
|
+
self._rotate_right(node.parent)
|
356
|
+
node = self._root
|
357
|
+
|
358
|
+
if node:
|
359
|
+
node.set_black()
|
360
|
+
|
361
|
+
def _inorder_traversal(self, node: Optional[RedBlackTreeNode]) -> Iterator[Tuple[str, Any]]:
|
362
|
+
"""In-order traversal of tree."""
|
363
|
+
if node:
|
364
|
+
yield from self._inorder_traversal(node.left)
|
365
|
+
yield (node.key, node.value)
|
366
|
+
yield from self._inorder_traversal(node.right)
|
367
|
+
|
368
|
+
# ============================================================================
|
369
|
+
# CORE OPERATIONS
|
370
|
+
# ============================================================================
|
371
|
+
|
372
|
+
def put(self, key: Any, value: Any = None) -> None:
|
373
|
+
"""Store a key-value pair."""
|
374
|
+
if not isinstance(key, str):
|
375
|
+
key = str(key)
|
376
|
+
|
377
|
+
self._insert_node(key, value)
|
378
|
+
|
379
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
380
|
+
"""Retrieve a value by key."""
|
381
|
+
if not isinstance(key, str):
|
382
|
+
key = str(key)
|
383
|
+
|
384
|
+
node = self._find_node(key)
|
385
|
+
return node.value if node else default
|
386
|
+
|
387
|
+
def delete(self, key: Any) -> bool:
|
388
|
+
"""Remove a key-value pair."""
|
389
|
+
if not isinstance(key, str):
|
390
|
+
key = str(key)
|
391
|
+
|
392
|
+
return self._delete_node(key)
|
393
|
+
|
394
|
+
def has(self, key: Any) -> bool:
|
395
|
+
"""Check if key exists."""
|
396
|
+
if not isinstance(key, str):
|
397
|
+
key = str(key)
|
398
|
+
|
399
|
+
return self._find_node(key) is not None
|
400
|
+
|
401
|
+
def clear(self) -> None:
|
402
|
+
"""Clear all data."""
|
403
|
+
self._root = None
|
404
|
+
self._size = 0
|
405
|
+
|
406
|
+
def size(self) -> int:
|
407
|
+
"""Get number of key-value pairs."""
|
408
|
+
return self._size
|
409
|
+
|
410
|
+
def is_empty(self) -> bool:
|
411
|
+
"""Check if tree is empty."""
|
412
|
+
return self._root is None
|
413
|
+
|
414
|
+
# ============================================================================
|
415
|
+
# ITERATION
|
416
|
+
# ============================================================================
|
417
|
+
|
418
|
+
def keys(self) -> Iterator[str]:
|
419
|
+
"""Iterate over keys in sorted order."""
|
420
|
+
for key, _ in self._inorder_traversal(self._root):
|
421
|
+
yield key
|
422
|
+
|
423
|
+
def values(self) -> Iterator[Any]:
|
424
|
+
"""Iterate over values in key order."""
|
425
|
+
for _, value in self._inorder_traversal(self._root):
|
426
|
+
yield value
|
427
|
+
|
428
|
+
def items(self) -> Iterator[Tuple[str, Any]]:
|
429
|
+
"""Iterate over key-value pairs in sorted order."""
|
430
|
+
yield from self._inorder_traversal(self._root)
|
431
|
+
|
432
|
+
def __iter__(self) -> Iterator[str]:
|
433
|
+
"""Iterate over keys."""
|
434
|
+
yield from self.keys()
|
435
|
+
|
436
|
+
# ============================================================================
|
437
|
+
# RED-BLACK TREE SPECIFIC OPERATIONS
|
438
|
+
# ============================================================================
|
439
|
+
|
440
|
+
def get_min(self) -> Optional[Tuple[str, Any]]:
|
441
|
+
"""Get the minimum key-value pair."""
|
442
|
+
if not self._root:
|
443
|
+
return None
|
444
|
+
|
445
|
+
min_node = self._find_min(self._root)
|
446
|
+
return (min_node.key, min_node.value)
|
447
|
+
|
448
|
+
def get_max(self) -> Optional[Tuple[str, Any]]:
|
449
|
+
"""Get the maximum key-value pair."""
|
450
|
+
if not self._root:
|
451
|
+
return None
|
452
|
+
|
453
|
+
max_node = self._find_max(self._root)
|
454
|
+
return (max_node.key, max_node.value)
|
455
|
+
|
456
|
+
def get_height(self) -> int:
|
457
|
+
"""Get the height of the tree."""
|
458
|
+
return self._get_height(self._root)
|
459
|
+
|
460
|
+
def is_valid_rb_tree(self) -> bool:
|
461
|
+
"""Check if tree satisfies red-black tree properties."""
|
462
|
+
if not self._root:
|
463
|
+
return True
|
464
|
+
|
465
|
+
# Check if root is black
|
466
|
+
if self._root.is_red():
|
467
|
+
return False
|
468
|
+
|
469
|
+
# Check all paths have same number of black nodes
|
470
|
+
def check_black_height(node: Optional[RedBlackTreeNode]) -> int:
|
471
|
+
if not node:
|
472
|
+
return 1
|
473
|
+
|
474
|
+
left_height = check_black_height(node.left)
|
475
|
+
right_height = check_black_height(node.right)
|
476
|
+
|
477
|
+
if left_height != right_height:
|
478
|
+
return -1
|
479
|
+
|
480
|
+
return left_height + (1 if node.is_black() else 0)
|
481
|
+
|
482
|
+
return check_black_height(self._root) != -1
|
483
|
+
|
484
|
+
def get_stats(self) -> Dict[str, Any]:
|
485
|
+
"""Get performance statistics."""
|
486
|
+
return {
|
487
|
+
'size': self._size,
|
488
|
+
'height': self._get_height(self._root),
|
489
|
+
'max_height': self._max_height,
|
490
|
+
'total_insertions': self._total_insertions,
|
491
|
+
'total_deletions': self._total_deletions,
|
492
|
+
'total_rotations': self._total_rotations,
|
493
|
+
'is_valid_rb_tree': self.is_valid_rb_tree(),
|
494
|
+
'strategy': 'RED_BLACK_TREE',
|
495
|
+
'backend': 'Self-balancing red-black tree with guaranteed O(log n) height',
|
496
|
+
'traits': [trait.name for trait in NodeTrait if self.has_trait(trait)]
|
497
|
+
}
|