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,316 @@
1
+ #exonware\xnode\strategies\impls\node_skip_list.py
2
+ """
3
+ Skip List Node Strategy Implementation
4
+
5
+ This module implements the SKIP_LIST strategy for probabilistic data structures
6
+ with O(log n) expected performance for search, insertion, and deletion.
7
+ """
8
+
9
+ import random
10
+ from typing import Any, Iterator, List, Dict, Optional, Tuple
11
+ from .base import ANodeTreeStrategy
12
+ from ...types import NodeMode, NodeTrait
13
+
14
+
15
+ class SkipListNode:
16
+ """Node in the skip list."""
17
+
18
+ def __init__(self, key: str, value: Any = None, level: int = 0):
19
+ self.key = key
20
+ self.value = value
21
+ self.level = level
22
+ self.forward: List[Optional['SkipListNode']] = [None] * (level + 1)
23
+ self._hash = None
24
+
25
+ def __hash__(self) -> int:
26
+ """Cache hash for performance."""
27
+ if self._hash is None:
28
+ self._hash = hash((self.key, self.value))
29
+ return self._hash
30
+
31
+ def __eq__(self, other) -> bool:
32
+ """Structural equality."""
33
+ if not isinstance(other, SkipListNode):
34
+ return False
35
+ return self.key == other.key and self.value == other.value
36
+
37
+
38
+ class SkipListStrategy(ANodeTreeStrategy):
39
+ """
40
+ Skip list node strategy for probabilistic data structures.
41
+
42
+ Provides O(log n) expected performance for search, insertion, and deletion
43
+ with simple implementation and good concurrent access properties.
44
+ """
45
+
46
+ def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
47
+ """Initialize the skip list strategy."""
48
+ super().__init__(NodeMode.SKIP_LIST, traits, **options)
49
+
50
+ self.case_sensitive = options.get('case_sensitive', True)
51
+ self.max_level = options.get('max_level', 16) # Maximum level for skip list
52
+ self.probability = options.get('probability', 0.5) # Probability for level promotion
53
+
54
+ # Core skip list
55
+ self._header = SkipListNode("", None, self.max_level) # Header node
56
+ self._level = 0 # Current maximum level
57
+ self._size = 0
58
+
59
+ # Statistics
60
+ self._total_insertions = 0
61
+ self._total_deletions = 0
62
+ self._total_searches = 0
63
+ self._max_level_reached = 0
64
+
65
+ def get_supported_traits(self) -> NodeTrait:
66
+ """Get the traits supported by the skip list strategy."""
67
+ return (NodeTrait.ORDERED | NodeTrait.INDEXED)
68
+
69
+ def _normalize_key(self, key: str) -> str:
70
+ """Normalize key based on case sensitivity."""
71
+ return key if self.case_sensitive else key.lower()
72
+
73
+ def _random_level(self) -> int:
74
+ """Generate random level for new node."""
75
+ level = 0
76
+ while random.random() < self.probability and level < self.max_level:
77
+ level += 1
78
+ self._max_level_reached = max(self._max_level_reached, level)
79
+ return level
80
+
81
+ def _search_path(self, key: str) -> List[Optional[SkipListNode]]:
82
+ """Find the path to the node with given key."""
83
+ normalized_key = self._normalize_key(key)
84
+ current = self._header
85
+ path = [None] * (self._level + 1)
86
+
87
+ # Start from highest level and work down
88
+ for i in range(self._level, -1, -1):
89
+ while (current.forward[i] is not None and
90
+ self._normalize_key(current.forward[i].key) < normalized_key):
91
+ current = current.forward[i]
92
+ path[i] = current
93
+
94
+ return path
95
+
96
+ def _find_node(self, key: str) -> Optional[SkipListNode]:
97
+ """Find node with given key."""
98
+ normalized_key = self._normalize_key(key)
99
+ current = self._header
100
+
101
+ # Start from highest level and work down
102
+ for i in range(self._level, -1, -1):
103
+ while (current.forward[i] is not None and
104
+ self._normalize_key(current.forward[i].key) < normalized_key):
105
+ current = current.forward[i]
106
+
107
+ # Check if we found the exact key
108
+ current = current.forward[0]
109
+ if current is not None and self._normalize_key(current.key) == normalized_key:
110
+ return current
111
+
112
+ return None
113
+
114
+ def _insert_node(self, key: str, value: Any) -> bool:
115
+ """Insert node with given key and value."""
116
+ normalized_key = self._normalize_key(key)
117
+
118
+ # Find the path to insertion point
119
+ path = self._search_path(key)
120
+ current = path[0].forward[0]
121
+
122
+ # Check if key already exists
123
+ if current is not None and self._normalize_key(current.key) == normalized_key:
124
+ # Update existing node
125
+ current.value = value
126
+ return False
127
+
128
+ # Create new node
129
+ new_level = self._random_level()
130
+ new_node = SkipListNode(key, value, new_level)
131
+
132
+ # If new level is higher than current level, update header
133
+ if new_level > self._level:
134
+ for i in range(self._level + 1, new_level + 1):
135
+ path.append(self._header)
136
+ self._level = new_level
137
+
138
+ # Insert node into skip list
139
+ for i in range(new_level + 1):
140
+ new_node.forward[i] = path[i].forward[i]
141
+ path[i].forward[i] = new_node
142
+
143
+ self._size += 1
144
+ self._total_insertions += 1
145
+ return True
146
+
147
+ def _delete_node(self, key: str) -> bool:
148
+ """Delete node with given key."""
149
+ normalized_key = self._normalize_key(key)
150
+
151
+ # Find the path to deletion point
152
+ path = self._search_path(key)
153
+ current = path[0].forward[0]
154
+
155
+ # Check if key exists
156
+ if current is None or self._normalize_key(current.key) != normalized_key:
157
+ return False
158
+
159
+ # Remove node from skip list
160
+ for i in range(self._level + 1):
161
+ if path[i].forward[i] != current:
162
+ break
163
+ path[i].forward[i] = current.forward[i]
164
+
165
+ # Update level if necessary
166
+ while (self._level > 0 and
167
+ self._header.forward[self._level] is None):
168
+ self._level -= 1
169
+
170
+ self._size -= 1
171
+ self._total_deletions += 1
172
+ return True
173
+
174
+ def _inorder_traversal(self) -> Iterator[Tuple[str, Any]]:
175
+ """In-order traversal of skip list."""
176
+ current = self._header.forward[0]
177
+ while current is not None:
178
+ yield (current.key, current.value)
179
+ current = current.forward[0]
180
+
181
+ # ============================================================================
182
+ # CORE OPERATIONS
183
+ # ============================================================================
184
+
185
+ def put(self, key: Any, value: Any = None) -> None:
186
+ """Store a key-value pair."""
187
+ if not isinstance(key, str):
188
+ key = str(key)
189
+
190
+ self._insert_node(key, value)
191
+
192
+ def get(self, key: Any, default: Any = None) -> Any:
193
+ """Retrieve a value by key."""
194
+ if not isinstance(key, str):
195
+ key = str(key)
196
+
197
+ self._total_searches += 1
198
+ node = self._find_node(key)
199
+ return node.value if node else default
200
+
201
+ def delete(self, key: Any) -> bool:
202
+ """Remove a key-value pair."""
203
+ if not isinstance(key, str):
204
+ key = str(key)
205
+
206
+ return self._delete_node(key)
207
+
208
+ def has(self, key: Any) -> bool:
209
+ """Check if key exists."""
210
+ if not isinstance(key, str):
211
+ key = str(key)
212
+
213
+ return self._find_node(key) is not None
214
+
215
+ def clear(self) -> None:
216
+ """Clear all data."""
217
+ self._header = SkipListNode("", None, self.max_level)
218
+ self._level = 0
219
+ self._size = 0
220
+
221
+ def size(self) -> int:
222
+ """Get number of key-value pairs."""
223
+ return self._size
224
+
225
+ def is_empty(self) -> bool:
226
+ """Check if skip list is empty."""
227
+ return self._size == 0
228
+
229
+ # ============================================================================
230
+ # ITERATION
231
+ # ============================================================================
232
+
233
+ def keys(self) -> Iterator[str]:
234
+ """Iterate over keys in sorted order."""
235
+ for key, _ in self._inorder_traversal():
236
+ yield key
237
+
238
+ def values(self) -> Iterator[Any]:
239
+ """Iterate over values in key order."""
240
+ for _, value in self._inorder_traversal():
241
+ yield value
242
+
243
+ def items(self) -> Iterator[Tuple[str, Any]]:
244
+ """Iterate over key-value pairs in sorted order."""
245
+ yield from self._inorder_traversal()
246
+
247
+ def __iter__(self) -> Iterator[str]:
248
+ """Iterate over keys."""
249
+ yield from self.keys()
250
+
251
+ # ============================================================================
252
+ # SKIP LIST SPECIFIC OPERATIONS
253
+ # ============================================================================
254
+
255
+ def get_min(self) -> Optional[Tuple[str, Any]]:
256
+ """Get the minimum key-value pair."""
257
+ if self._size == 0:
258
+ return None
259
+
260
+ current = self._header.forward[0]
261
+ return (current.key, current.value) if current else None
262
+
263
+ def get_max(self) -> Optional[Tuple[str, Any]]:
264
+ """Get the maximum key-value pair."""
265
+ if self._size == 0:
266
+ return None
267
+
268
+ current = self._header
269
+ for i in range(self._level, -1, -1):
270
+ while current.forward[i] is not None:
271
+ current = current.forward[i]
272
+
273
+ return (current.key, current.value) if current != self._header else None
274
+
275
+ def range_query(self, start_key: str, end_key: str) -> Iterator[Tuple[str, Any]]:
276
+ """Get all key-value pairs in range [start_key, end_key]."""
277
+ if not isinstance(start_key, str) or not isinstance(end_key, str):
278
+ return
279
+
280
+ normalized_start = self._normalize_key(start_key)
281
+ normalized_end = self._normalize_key(end_key)
282
+
283
+ # Find starting position
284
+ path = self._search_path(start_key)
285
+ current = path[0].forward[0]
286
+
287
+ # Iterate through range
288
+ while (current is not None and
289
+ self._normalize_key(current.key) <= normalized_end):
290
+ if self._normalize_key(current.key) >= normalized_start:
291
+ yield (current.key, current.value)
292
+ current = current.forward[0]
293
+
294
+ def get_level(self) -> int:
295
+ """Get current maximum level."""
296
+ return self._level
297
+
298
+ def get_max_level(self) -> int:
299
+ """Get maximum level reached."""
300
+ return self._max_level_reached
301
+
302
+ def get_stats(self) -> Dict[str, Any]:
303
+ """Get performance statistics."""
304
+ return {
305
+ 'size': self._size,
306
+ 'level': self._level,
307
+ 'max_level_reached': self._max_level_reached,
308
+ 'total_insertions': self._total_insertions,
309
+ 'total_deletions': self._total_deletions,
310
+ 'total_searches': self._total_searches,
311
+ 'probability': self.probability,
312
+ 'max_level': self.max_level,
313
+ 'strategy': 'SKIP_LIST',
314
+ 'backend': 'Probabilistic skip list with O(log n) expected performance',
315
+ 'traits': [trait.name for trait in NodeTrait if self.has_trait(trait)]
316
+ }
@@ -0,0 +1,393 @@
1
+ #exonware\xnode\strategies\impls\node_splay_tree.py
2
+ """
3
+ Splay Tree Node Strategy Implementation
4
+
5
+ This module implements the SPLAY_TREE strategy for self-adjusting binary
6
+ search trees with amortized O(log n) performance.
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 SplayTreeNode:
15
+ """Node in the splay tree."""
16
+
17
+ def __init__(self, key: str, value: Any = None):
18
+ self.key = key
19
+ self.value = value
20
+ self.left: Optional['SplayTreeNode'] = None
21
+ self.right: Optional['SplayTreeNode'] = None
22
+ self.parent: Optional['SplayTreeNode'] = None
23
+ self._hash = None
24
+
25
+ def __hash__(self) -> int:
26
+ """Cache hash for performance."""
27
+ if self._hash is None:
28
+ self._hash = hash((self.key, self.value))
29
+ return self._hash
30
+
31
+ def __eq__(self, other) -> bool:
32
+ """Structural equality."""
33
+ if not isinstance(other, SplayTreeNode):
34
+ return False
35
+ return self.key == other.key and self.value == other.value
36
+
37
+
38
+ class SplayTreeStrategy(ANodeTreeStrategy):
39
+ """
40
+ Splay tree node strategy for self-adjusting binary search trees.
41
+
42
+ Provides amortized O(log n) performance by moving accessed nodes
43
+ to the root through splaying operations.
44
+ """
45
+
46
+ def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
47
+ """Initialize the splay tree strategy."""
48
+ super().__init__(NodeMode.SPLAY_TREE, traits, **options)
49
+
50
+ self.case_sensitive = options.get('case_sensitive', True)
51
+
52
+ # Core splay tree
53
+ self._root: Optional[SplayTreeNode] = None
54
+ self._size = 0
55
+
56
+ # Statistics
57
+ self._total_insertions = 0
58
+ self._total_deletions = 0
59
+ self._total_splays = 0
60
+ self._max_height = 0
61
+
62
+ def get_supported_traits(self) -> NodeTrait:
63
+ """Get the traits supported by the splay tree strategy."""
64
+ return (NodeTrait.ORDERED | NodeTrait.INDEXED)
65
+
66
+ def _normalize_key(self, key: str) -> str:
67
+ """Normalize key based on case sensitivity."""
68
+ return key if self.case_sensitive else key.lower()
69
+
70
+ def _get_height(self, node: Optional[SplayTreeNode]) -> int:
71
+ """Get height of node."""
72
+ if not node:
73
+ return 0
74
+
75
+ left_height = self._get_height(node.left)
76
+ right_height = self._get_height(node.right)
77
+ return 1 + max(left_height, right_height)
78
+
79
+ def _rotate_right(self, node: SplayTreeNode) -> None:
80
+ """Right rotation around node."""
81
+ left_child = node.left
82
+ if not left_child:
83
+ return
84
+
85
+ # Update parent connections
86
+ node.left = left_child.right
87
+ if left_child.right:
88
+ left_child.right.parent = node
89
+
90
+ left_child.parent = node.parent
91
+ if not node.parent:
92
+ self._root = left_child
93
+ elif node == node.parent.left:
94
+ node.parent.left = left_child
95
+ else:
96
+ node.parent.right = left_child
97
+
98
+ # Update rotation
99
+ left_child.right = node
100
+ node.parent = left_child
101
+
102
+ def _rotate_left(self, node: SplayTreeNode) -> None:
103
+ """Left rotation around node."""
104
+ right_child = node.right
105
+ if not right_child:
106
+ return
107
+
108
+ # Update parent connections
109
+ node.right = right_child.left
110
+ if right_child.left:
111
+ right_child.left.parent = node
112
+
113
+ right_child.parent = node.parent
114
+ if not node.parent:
115
+ self._root = right_child
116
+ elif node == node.parent.right:
117
+ node.parent.right = right_child
118
+ else:
119
+ node.parent.left = right_child
120
+
121
+ # Update rotation
122
+ right_child.left = node
123
+ node.parent = right_child
124
+
125
+ def _splay(self, node: SplayTreeNode) -> None:
126
+ """Splay node to root."""
127
+ while node.parent:
128
+ parent = node.parent
129
+ grandparent = parent.parent
130
+
131
+ if not grandparent:
132
+ # Zig case
133
+ if node == parent.left:
134
+ self._rotate_right(parent)
135
+ else:
136
+ self._rotate_left(parent)
137
+ elif node == parent.left and parent == grandparent.left:
138
+ # Zig-zig case (left-left)
139
+ self._rotate_right(grandparent)
140
+ self._rotate_right(parent)
141
+ elif node == parent.right and parent == grandparent.right:
142
+ # Zig-zig case (right-right)
143
+ self._rotate_left(grandparent)
144
+ self._rotate_left(parent)
145
+ elif node == parent.right and parent == grandparent.left:
146
+ # Zig-zag case (left-right)
147
+ self._rotate_left(parent)
148
+ self._rotate_right(grandparent)
149
+ else:
150
+ # Zig-zag case (right-left)
151
+ self._rotate_right(parent)
152
+ self._rotate_left(grandparent)
153
+
154
+ self._total_splays += 1
155
+
156
+ def _find_node(self, key: str) -> Optional[SplayTreeNode]:
157
+ """Find node with given key and splay it to root."""
158
+ normalized_key = self._normalize_key(key)
159
+ current = self._root
160
+
161
+ while current:
162
+ current_key = self._normalize_key(current.key)
163
+ if normalized_key < current_key:
164
+ current = current.left
165
+ elif normalized_key > current_key:
166
+ current = current.right
167
+ else:
168
+ # Found the node, splay it to root
169
+ self._splay(current)
170
+ return current
171
+
172
+ return None
173
+
174
+ def _insert_node(self, key: str, value: Any) -> bool:
175
+ """Insert node with given key and value."""
176
+ normalized_key = self._normalize_key(key)
177
+
178
+ # Create new node
179
+ new_node = SplayTreeNode(key, value)
180
+
181
+ # Find insertion point
182
+ current = self._root
183
+ parent = None
184
+
185
+ while current:
186
+ parent = current
187
+ current_key = self._normalize_key(current.key)
188
+ if normalized_key < current_key:
189
+ current = current.left
190
+ elif normalized_key > current_key:
191
+ current = current.right
192
+ else:
193
+ # Key already exists, update value and splay
194
+ current.value = value
195
+ self._splay(current)
196
+ return False
197
+
198
+ # Insert new node
199
+ new_node.parent = parent
200
+ if not parent:
201
+ self._root = new_node
202
+ elif normalized_key < self._normalize_key(parent.key):
203
+ parent.left = new_node
204
+ else:
205
+ parent.right = new_node
206
+
207
+ # Splay new node to root
208
+ self._splay(new_node)
209
+
210
+ self._size += 1
211
+ self._total_insertions += 1
212
+ self._max_height = max(self._max_height, self._get_height(self._root))
213
+ return True
214
+
215
+ def _delete_node(self, key: str) -> bool:
216
+ """Delete node with given key."""
217
+ node = self._find_node(key)
218
+ if not node:
219
+ return False
220
+
221
+ # Splay node to root
222
+ self._splay(node)
223
+
224
+ # If node has no children, just remove it
225
+ if not node.left and not node.right:
226
+ self._root = None
227
+ elif not node.left:
228
+ # Only right child
229
+ self._root = node.right
230
+ self._root.parent = None
231
+ elif not node.right:
232
+ # Only left child
233
+ self._root = node.left
234
+ self._root.parent = None
235
+ else:
236
+ # Both children exist
237
+ # Find maximum in left subtree
238
+ max_left = node.left
239
+ while max_left.right:
240
+ max_left = max_left.right
241
+
242
+ # Splay max_left to root of left subtree
243
+ self._splay(max_left)
244
+
245
+ # Attach right subtree to max_left
246
+ max_left.right = node.right
247
+ if node.right:
248
+ node.right.parent = max_left
249
+
250
+ # Make max_left the new root
251
+ self._root = max_left
252
+ self._root.parent = None
253
+
254
+ self._size -= 1
255
+ self._total_deletions += 1
256
+ return True
257
+
258
+ def _inorder_traversal(self, node: Optional[SplayTreeNode]) -> Iterator[Tuple[str, Any]]:
259
+ """In-order traversal of tree."""
260
+ if node:
261
+ yield from self._inorder_traversal(node.left)
262
+ yield (node.key, node.value)
263
+ yield from self._inorder_traversal(node.right)
264
+
265
+ # ============================================================================
266
+ # CORE OPERATIONS
267
+ # ============================================================================
268
+
269
+ def put(self, key: Any, value: Any = None) -> None:
270
+ """Store a key-value pair."""
271
+ if not isinstance(key, str):
272
+ key = str(key)
273
+
274
+ self._insert_node(key, value)
275
+
276
+ def get(self, key: Any, default: Any = None) -> Any:
277
+ """Retrieve a value by key."""
278
+ if not isinstance(key, str):
279
+ key = str(key)
280
+
281
+ node = self._find_node(key)
282
+ return node.value if node else default
283
+
284
+ def delete(self, key: Any) -> bool:
285
+ """Remove a key-value pair."""
286
+ if not isinstance(key, str):
287
+ key = str(key)
288
+
289
+ return self._delete_node(key)
290
+
291
+ def has(self, key: Any) -> bool:
292
+ """Check if key exists."""
293
+ if not isinstance(key, str):
294
+ key = str(key)
295
+
296
+ return self._find_node(key) is not None
297
+
298
+ def clear(self) -> None:
299
+ """Clear all data."""
300
+ self._root = None
301
+ self._size = 0
302
+
303
+ def size(self) -> int:
304
+ """Get number of key-value pairs."""
305
+ return self._size
306
+
307
+ def is_empty(self) -> bool:
308
+ """Check if tree is empty."""
309
+ return self._root is None
310
+
311
+ # ============================================================================
312
+ # ITERATION
313
+ # ============================================================================
314
+
315
+ def keys(self) -> Iterator[str]:
316
+ """Iterate over keys in sorted order."""
317
+ for key, _ in self._inorder_traversal(self._root):
318
+ yield key
319
+
320
+ def values(self) -> Iterator[Any]:
321
+ """Iterate over values in key order."""
322
+ for _, value in self._inorder_traversal(self._root):
323
+ yield value
324
+
325
+ def items(self) -> Iterator[Tuple[str, Any]]:
326
+ """Iterate over key-value pairs in sorted order."""
327
+ yield from self._inorder_traversal(self._root)
328
+
329
+ def __iter__(self) -> Iterator[str]:
330
+ """Iterate over keys."""
331
+ yield from self.keys()
332
+
333
+ # ============================================================================
334
+ # SPLAY TREE SPECIFIC OPERATIONS
335
+ # ============================================================================
336
+
337
+ def get_min(self) -> Optional[Tuple[str, Any]]:
338
+ """Get the minimum key-value pair."""
339
+ if not self._root:
340
+ return None
341
+
342
+ # Find minimum and splay it to root
343
+ current = self._root
344
+ while current.left:
345
+ current = current.left
346
+
347
+ self._splay(current)
348
+ return (current.key, current.value)
349
+
350
+ def get_max(self) -> Optional[Tuple[str, Any]]:
351
+ """Get the maximum key-value pair."""
352
+ if not self._root:
353
+ return None
354
+
355
+ # Find maximum and splay it to root
356
+ current = self._root
357
+ while current.right:
358
+ current = current.right
359
+
360
+ self._splay(current)
361
+ return (current.key, current.value)
362
+
363
+ def get_height(self) -> int:
364
+ """Get the height of the tree."""
365
+ return self._get_height(self._root)
366
+
367
+ def splay_to_root(self, key: str) -> bool:
368
+ """Splay node with given key to root."""
369
+ node = self._find_node(key)
370
+ return node is not None
371
+
372
+ def get_root_key(self) -> Optional[str]:
373
+ """Get the key of the root node."""
374
+ return self._root.key if self._root else None
375
+
376
+ def get_root_value(self) -> Optional[Any]:
377
+ """Get the value of the root node."""
378
+ return self._root.value if self._root else None
379
+
380
+ def get_stats(self) -> Dict[str, Any]:
381
+ """Get performance statistics."""
382
+ return {
383
+ 'size': self._size,
384
+ 'height': self._get_height(self._root),
385
+ 'max_height': self._max_height,
386
+ 'total_insertions': self._total_insertions,
387
+ 'total_deletions': self._total_deletions,
388
+ 'total_splays': self._total_splays,
389
+ 'root_key': self.get_root_key(),
390
+ 'strategy': 'SPLAY_TREE',
391
+ 'backend': 'Self-adjusting splay tree with amortized O(log n) performance',
392
+ 'traits': [trait.name for trait in NodeTrait if self.has_trait(trait)]
393
+ }