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,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
+ }