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.
Files changed (132) hide show
  1. exonware/__init__.py +14 -0
  2. exonware/xwnode/__init__.py +127 -0
  3. exonware/xwnode/base.py +676 -0
  4. exonware/xwnode/config.py +178 -0
  5. exonware/xwnode/contracts.py +730 -0
  6. exonware/xwnode/errors.py +503 -0
  7. exonware/xwnode/facade.py +460 -0
  8. exonware/xwnode/strategies/__init__.py +158 -0
  9. exonware/xwnode/strategies/advisor.py +463 -0
  10. exonware/xwnode/strategies/edges/__init__.py +32 -0
  11. exonware/xwnode/strategies/edges/adj_list.py +227 -0
  12. exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
  13. exonware/xwnode/strategies/edges/base.py +169 -0
  14. exonware/xwnode/strategies/flyweight.py +328 -0
  15. exonware/xwnode/strategies/impls/__init__.py +13 -0
  16. exonware/xwnode/strategies/impls/_base_edge.py +403 -0
  17. exonware/xwnode/strategies/impls/_base_node.py +307 -0
  18. exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
  19. exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
  20. exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
  21. exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
  22. exonware/xwnode/strategies/impls/edge_coo.py +533 -0
  23. exonware/xwnode/strategies/impls/edge_csc.py +447 -0
  24. exonware/xwnode/strategies/impls/edge_csr.py +492 -0
  25. exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
  26. exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
  27. exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
  28. exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
  29. exonware/xwnode/strategies/impls/edge_octree.py +574 -0
  30. exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
  31. exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
  32. exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
  33. exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
  34. exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
  35. exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
  36. exonware/xwnode/strategies/manager.py +775 -0
  37. exonware/xwnode/strategies/metrics.py +538 -0
  38. exonware/xwnode/strategies/migration.py +432 -0
  39. exonware/xwnode/strategies/nodes/__init__.py +50 -0
  40. exonware/xwnode/strategies/nodes/_base_node.py +307 -0
  41. exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
  42. exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
  43. exonware/xwnode/strategies/nodes/array_list.py +209 -0
  44. exonware/xwnode/strategies/nodes/base.py +247 -0
  45. exonware/xwnode/strategies/nodes/deque.py +200 -0
  46. exonware/xwnode/strategies/nodes/hash_map.py +135 -0
  47. exonware/xwnode/strategies/nodes/heap.py +307 -0
  48. exonware/xwnode/strategies/nodes/linked_list.py +232 -0
  49. exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
  50. exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
  51. exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
  52. exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
  53. exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
  54. exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
  55. exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
  56. exonware/xwnode/strategies/nodes/node_btree.py +357 -0
  57. exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
  58. exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
  59. exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
  60. exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
  61. exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
  62. exonware/xwnode/strategies/nodes/node_heap.py +191 -0
  63. exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
  64. exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
  65. exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
  66. exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
  67. exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
  68. exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
  69. exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
  70. exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
  71. exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
  72. exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
  73. exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
  74. exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
  75. exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
  76. exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
  77. exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
  78. exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
  79. exonware/xwnode/strategies/nodes/node_treap.py +387 -0
  80. exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
  81. exonware/xwnode/strategies/nodes/node_trie.py +252 -0
  82. exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
  83. exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
  84. exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
  85. exonware/xwnode/strategies/nodes/queue.py +161 -0
  86. exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
  87. exonware/xwnode/strategies/nodes/stack.py +152 -0
  88. exonware/xwnode/strategies/nodes/trie.py +274 -0
  89. exonware/xwnode/strategies/nodes/union_find.py +283 -0
  90. exonware/xwnode/strategies/pattern_detector.py +603 -0
  91. exonware/xwnode/strategies/performance_monitor.py +487 -0
  92. exonware/xwnode/strategies/queries/__init__.py +24 -0
  93. exonware/xwnode/strategies/queries/base.py +236 -0
  94. exonware/xwnode/strategies/queries/cql.py +201 -0
  95. exonware/xwnode/strategies/queries/cypher.py +181 -0
  96. exonware/xwnode/strategies/queries/datalog.py +70 -0
  97. exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
  98. exonware/xwnode/strategies/queries/eql.py +70 -0
  99. exonware/xwnode/strategies/queries/flux.py +70 -0
  100. exonware/xwnode/strategies/queries/gql.py +70 -0
  101. exonware/xwnode/strategies/queries/graphql.py +240 -0
  102. exonware/xwnode/strategies/queries/gremlin.py +181 -0
  103. exonware/xwnode/strategies/queries/hiveql.py +214 -0
  104. exonware/xwnode/strategies/queries/hql.py +70 -0
  105. exonware/xwnode/strategies/queries/jmespath.py +219 -0
  106. exonware/xwnode/strategies/queries/jq.py +66 -0
  107. exonware/xwnode/strategies/queries/json_query.py +66 -0
  108. exonware/xwnode/strategies/queries/jsoniq.py +248 -0
  109. exonware/xwnode/strategies/queries/kql.py +70 -0
  110. exonware/xwnode/strategies/queries/linq.py +238 -0
  111. exonware/xwnode/strategies/queries/logql.py +70 -0
  112. exonware/xwnode/strategies/queries/mql.py +68 -0
  113. exonware/xwnode/strategies/queries/n1ql.py +210 -0
  114. exonware/xwnode/strategies/queries/partiql.py +70 -0
  115. exonware/xwnode/strategies/queries/pig.py +215 -0
  116. exonware/xwnode/strategies/queries/promql.py +70 -0
  117. exonware/xwnode/strategies/queries/sparql.py +220 -0
  118. exonware/xwnode/strategies/queries/sql.py +275 -0
  119. exonware/xwnode/strategies/queries/xml_query.py +66 -0
  120. exonware/xwnode/strategies/queries/xpath.py +223 -0
  121. exonware/xwnode/strategies/queries/xquery.py +258 -0
  122. exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
  123. exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
  124. exonware/xwnode/strategies/registry.py +604 -0
  125. exonware/xwnode/strategies/simple.py +273 -0
  126. exonware/xwnode/strategies/utils.py +532 -0
  127. exonware/xwnode/types.py +912 -0
  128. exonware/xwnode/version.py +78 -0
  129. exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
  130. exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
  131. exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
  132. 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
+ }