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,252 @@
|
|
1
|
+
"""
|
2
|
+
Trie Node Strategy Implementation
|
3
|
+
|
4
|
+
This module implements the TRIE strategy for efficient string prefix operations.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Any, Iterator, Dict, List, Optional
|
8
|
+
from .base import ANodeTreeStrategy
|
9
|
+
from ...types import NodeMode, NodeTrait
|
10
|
+
from ..utils import (
|
11
|
+
TrieNode,
|
12
|
+
safe_to_native_conversion,
|
13
|
+
create_basic_metrics,
|
14
|
+
create_basic_backend_info,
|
15
|
+
create_size_tracker,
|
16
|
+
create_access_tracker,
|
17
|
+
update_size_tracker,
|
18
|
+
record_access,
|
19
|
+
get_access_metrics
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
class TrieStrategy(ANodeTreeStrategy):
|
24
|
+
"""
|
25
|
+
Trie node strategy for efficient string prefix operations.
|
26
|
+
|
27
|
+
Optimized for prefix matching, autocomplete, and string searching.
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
31
|
+
"""Initialize the trie strategy."""
|
32
|
+
super().__init__(NodeMode.TRIE, traits, **options)
|
33
|
+
self._root = TrieNode()
|
34
|
+
self._size_tracker = create_size_tracker()
|
35
|
+
self._access_tracker = create_access_tracker()
|
36
|
+
|
37
|
+
def get_supported_traits(self) -> NodeTrait:
|
38
|
+
"""Get the traits supported by the trie strategy."""
|
39
|
+
return (NodeTrait.ORDERED | NodeTrait.HIERARCHICAL | NodeTrait.INDEXED)
|
40
|
+
|
41
|
+
# ============================================================================
|
42
|
+
# CORE OPERATIONS
|
43
|
+
# ============================================================================
|
44
|
+
|
45
|
+
def put(self, key: Any, value: Any = None) -> None:
|
46
|
+
"""Store a key-value pair (key should be string-like)."""
|
47
|
+
word = str(key)
|
48
|
+
node = self._root
|
49
|
+
|
50
|
+
# Traverse/create path
|
51
|
+
for char in word:
|
52
|
+
if char not in node.children:
|
53
|
+
node.children[char] = TrieNode()
|
54
|
+
node = node.children[char]
|
55
|
+
|
56
|
+
# Mark end and store value
|
57
|
+
if not node.is_end_word:
|
58
|
+
update_size_tracker(self._size_tracker, 1)
|
59
|
+
node.is_end_word = True
|
60
|
+
node.value = value
|
61
|
+
record_access(self._access_tracker, 'put_count')
|
62
|
+
|
63
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
64
|
+
"""Retrieve a value by key."""
|
65
|
+
word = str(key)
|
66
|
+
node = self._find_node(word)
|
67
|
+
record_access(self._access_tracker, 'get_count')
|
68
|
+
if node and node.is_end_word:
|
69
|
+
return node.value
|
70
|
+
return default
|
71
|
+
|
72
|
+
def has(self, key: Any) -> bool:
|
73
|
+
"""Check if key exists."""
|
74
|
+
word = str(key)
|
75
|
+
node = self._find_node(word)
|
76
|
+
return node is not None and node.is_end_word
|
77
|
+
|
78
|
+
def remove(self, key: Any) -> bool:
|
79
|
+
"""Remove a key-value pair."""
|
80
|
+
word = str(key)
|
81
|
+
result = self._remove_recursive(self._root, word, 0)
|
82
|
+
if result:
|
83
|
+
update_size_tracker(self._size_tracker, -1)
|
84
|
+
record_access(self._access_tracker, 'delete_count')
|
85
|
+
return result
|
86
|
+
|
87
|
+
def delete(self, key: Any) -> bool:
|
88
|
+
"""Remove a key-value pair (alias for remove)."""
|
89
|
+
return self.remove(key)
|
90
|
+
|
91
|
+
def clear(self) -> None:
|
92
|
+
"""Clear all data."""
|
93
|
+
self._root = TrieNode()
|
94
|
+
self._size_tracker['size'] = 0
|
95
|
+
|
96
|
+
def keys(self) -> Iterator[str]:
|
97
|
+
"""Get all keys."""
|
98
|
+
return iter(self._collect_words())
|
99
|
+
|
100
|
+
def values(self) -> Iterator[Any]:
|
101
|
+
"""Get all values."""
|
102
|
+
words = self._collect_words()
|
103
|
+
for word in words:
|
104
|
+
node = self._find_node(word)
|
105
|
+
if node:
|
106
|
+
yield node.value
|
107
|
+
|
108
|
+
def items(self) -> Iterator[tuple[str, Any]]:
|
109
|
+
"""Get all key-value pairs."""
|
110
|
+
words = self._collect_words()
|
111
|
+
for word in words:
|
112
|
+
node = self._find_node(word)
|
113
|
+
if node:
|
114
|
+
yield (word, node.value)
|
115
|
+
|
116
|
+
def __len__(self) -> int:
|
117
|
+
"""Get the number of items."""
|
118
|
+
return self._size_tracker['size']
|
119
|
+
|
120
|
+
def to_native(self) -> Dict[str, Any]:
|
121
|
+
"""Convert to native Python dictionary."""
|
122
|
+
result = {}
|
123
|
+
for word in self._collect_words():
|
124
|
+
node = self._find_node(word)
|
125
|
+
if node:
|
126
|
+
result[word] = safe_to_native_conversion(node.value)
|
127
|
+
return result
|
128
|
+
|
129
|
+
@property
|
130
|
+
def is_list(self) -> bool:
|
131
|
+
"""This is not a list strategy."""
|
132
|
+
return False
|
133
|
+
|
134
|
+
@property
|
135
|
+
def is_dict(self) -> bool:
|
136
|
+
"""This behaves like a dict with string keys."""
|
137
|
+
return True
|
138
|
+
|
139
|
+
# ============================================================================
|
140
|
+
# TRIE-SPECIFIC OPERATIONS
|
141
|
+
# ============================================================================
|
142
|
+
|
143
|
+
def starts_with(self, prefix: str) -> List[str]:
|
144
|
+
"""Get all keys that start with the given prefix."""
|
145
|
+
node = self._find_node(prefix)
|
146
|
+
if not node:
|
147
|
+
return []
|
148
|
+
|
149
|
+
words = []
|
150
|
+
self._collect_words_from_node(node, prefix, words)
|
151
|
+
return words
|
152
|
+
|
153
|
+
def longest_common_prefix(self) -> str:
|
154
|
+
"""Find the longest common prefix of all stored keys."""
|
155
|
+
if not self._root.children:
|
156
|
+
return ""
|
157
|
+
|
158
|
+
prefix = ""
|
159
|
+
node = self._root
|
160
|
+
|
161
|
+
while len(node.children) == 1 and not node.is_end_word:
|
162
|
+
char = next(iter(node.children.keys()))
|
163
|
+
prefix += char
|
164
|
+
node = node.children[char]
|
165
|
+
|
166
|
+
return prefix
|
167
|
+
|
168
|
+
def get_all_prefixes(self, word: str) -> List[str]:
|
169
|
+
"""Get all prefixes of the given word that exist in the trie."""
|
170
|
+
prefixes = []
|
171
|
+
node = self._root
|
172
|
+
|
173
|
+
for i, char in enumerate(word):
|
174
|
+
if char not in node.children:
|
175
|
+
break
|
176
|
+
node = node.children[char]
|
177
|
+
if node.is_end_word:
|
178
|
+
prefixes.append(word[:i+1])
|
179
|
+
|
180
|
+
return prefixes
|
181
|
+
|
182
|
+
# ============================================================================
|
183
|
+
# HELPER METHODS
|
184
|
+
# ============================================================================
|
185
|
+
|
186
|
+
def _find_node(self, word: str) -> Optional[TrieNode]:
|
187
|
+
"""Find the node corresponding to the given word."""
|
188
|
+
node = self._root
|
189
|
+
for char in word:
|
190
|
+
if char not in node.children:
|
191
|
+
return None
|
192
|
+
node = node.children[char]
|
193
|
+
return node
|
194
|
+
|
195
|
+
def _remove_recursive(self, node: TrieNode, word: str, index: int) -> bool:
|
196
|
+
"""Recursively remove a word from the trie."""
|
197
|
+
if index == len(word):
|
198
|
+
if node.is_end_word:
|
199
|
+
node.is_end_word = False
|
200
|
+
node.value = None
|
201
|
+
return True
|
202
|
+
return False
|
203
|
+
|
204
|
+
char = word[index]
|
205
|
+
if char not in node.children:
|
206
|
+
return False
|
207
|
+
|
208
|
+
child = node.children[char]
|
209
|
+
should_delete_child = self._remove_recursive(child, word, index + 1)
|
210
|
+
|
211
|
+
if should_delete_child and not child.is_end_word and not child.children:
|
212
|
+
del node.children[char]
|
213
|
+
|
214
|
+
return should_delete_child
|
215
|
+
|
216
|
+
def _collect_words(self) -> List[str]:
|
217
|
+
"""Collect all words stored in the trie."""
|
218
|
+
words = []
|
219
|
+
self._collect_words_from_node(self._root, "", words)
|
220
|
+
return words
|
221
|
+
|
222
|
+
def _collect_words_from_node(self, node: TrieNode, prefix: str, words: List[str]) -> None:
|
223
|
+
"""Collect all words from a given node."""
|
224
|
+
if node.is_end_word:
|
225
|
+
words.append(prefix)
|
226
|
+
|
227
|
+
for char, child in node.children.items():
|
228
|
+
self._collect_words_from_node(child, prefix + char, words)
|
229
|
+
|
230
|
+
# ============================================================================
|
231
|
+
# PERFORMANCE CHARACTERISTICS
|
232
|
+
# ============================================================================
|
233
|
+
|
234
|
+
def backend_info(self) -> Dict[str, Any]:
|
235
|
+
"""Get backend implementation info."""
|
236
|
+
return create_basic_backend_info(
|
237
|
+
'TRIE',
|
238
|
+
'TrieNode tree',
|
239
|
+
complexity={
|
240
|
+
'get': 'O(m) where m is key length',
|
241
|
+
'put': 'O(m) where m is key length',
|
242
|
+
'has': 'O(m) where m is key length',
|
243
|
+
'starts_with': 'O(m + k) where m is prefix length, k is number of matches'
|
244
|
+
}
|
245
|
+
)
|
246
|
+
|
247
|
+
def metrics(self) -> Dict[str, Any]:
|
248
|
+
"""Get performance metrics."""
|
249
|
+
base_metrics = create_basic_metrics('TRIE', self._size_tracker['size'])
|
250
|
+
access_metrics = get_access_metrics(self._access_tracker)
|
251
|
+
base_metrics.update(access_metrics)
|
252
|
+
return base_metrics
|
@@ -0,0 +1,187 @@
|
|
1
|
+
"""
|
2
|
+
Union-Find Node Strategy Implementation
|
3
|
+
|
4
|
+
This module implements the UNION_FIND strategy for efficient set operations.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Any, Iterator, Dict, List, Set
|
8
|
+
from .base import ANodeGraphStrategy
|
9
|
+
from ...types import NodeMode, NodeTrait
|
10
|
+
from ..utils import (
|
11
|
+
UnionFind,
|
12
|
+
safe_to_native_conversion,
|
13
|
+
create_basic_metrics,
|
14
|
+
create_basic_backend_info,
|
15
|
+
create_size_tracker,
|
16
|
+
create_access_tracker,
|
17
|
+
update_size_tracker,
|
18
|
+
record_access,
|
19
|
+
get_access_metrics
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
class UnionFindStrategy(ANodeGraphStrategy):
|
24
|
+
"""
|
25
|
+
Union-Find node strategy for efficient set operations.
|
26
|
+
|
27
|
+
Optimized for union, find, and connected operations on disjoint sets.
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
31
|
+
"""Initialize the union-find strategy."""
|
32
|
+
super().__init__(NodeMode.UNION_FIND, traits, **options)
|
33
|
+
self._union_find = UnionFind()
|
34
|
+
self._size_tracker = create_size_tracker()
|
35
|
+
self._access_tracker = create_access_tracker()
|
36
|
+
|
37
|
+
def get_supported_traits(self) -> NodeTrait:
|
38
|
+
"""Get the traits supported by the union-find strategy."""
|
39
|
+
return (NodeTrait.SET_OPERATIONS | NodeTrait.HIERARCHICAL)
|
40
|
+
|
41
|
+
# ============================================================================
|
42
|
+
# CORE OPERATIONS
|
43
|
+
# ============================================================================
|
44
|
+
|
45
|
+
def put(self, key: Any, value: Any = None) -> None:
|
46
|
+
"""Store a key-value pair (creates a new set)."""
|
47
|
+
str_key = str(key)
|
48
|
+
if str_key not in self._union_find.parent:
|
49
|
+
update_size_tracker(self._size_tracker, 1)
|
50
|
+
self._union_find.make_set(str_key, value)
|
51
|
+
record_access(self._access_tracker, 'put_count')
|
52
|
+
|
53
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
54
|
+
"""Retrieve a value by key."""
|
55
|
+
str_key = str(key)
|
56
|
+
record_access(self._access_tracker, 'get_count')
|
57
|
+
return self._union_find.values.get(str_key, default)
|
58
|
+
|
59
|
+
def has(self, key: Any) -> bool:
|
60
|
+
"""Check if key exists."""
|
61
|
+
return str(key) in self._union_find.parent
|
62
|
+
|
63
|
+
def remove(self, key: Any) -> bool:
|
64
|
+
"""Remove a key-value pair."""
|
65
|
+
str_key = str(key)
|
66
|
+
if str_key in self._union_find.parent:
|
67
|
+
# Remove from all data structures
|
68
|
+
del self._union_find.parent[str_key]
|
69
|
+
if str_key in self._union_find.rank:
|
70
|
+
del self._union_find.rank[str_key]
|
71
|
+
if str_key in self._union_find.values:
|
72
|
+
del self._union_find.values[str_key]
|
73
|
+
update_size_tracker(self._size_tracker, -1)
|
74
|
+
record_access(self._access_tracker, 'delete_count')
|
75
|
+
return True
|
76
|
+
return False
|
77
|
+
|
78
|
+
def delete(self, key: Any) -> bool:
|
79
|
+
"""Remove a key-value pair (alias for remove)."""
|
80
|
+
return self.remove(key)
|
81
|
+
|
82
|
+
def clear(self) -> None:
|
83
|
+
"""Clear all data."""
|
84
|
+
self._union_find = UnionFind()
|
85
|
+
self._size_tracker['size'] = 0
|
86
|
+
|
87
|
+
def keys(self) -> Iterator[str]:
|
88
|
+
"""Get all keys."""
|
89
|
+
return iter(self._union_find.parent.keys())
|
90
|
+
|
91
|
+
def values(self) -> Iterator[Any]:
|
92
|
+
"""Get all values."""
|
93
|
+
return iter(self._union_find.values.values())
|
94
|
+
|
95
|
+
def items(self) -> Iterator[tuple[str, Any]]:
|
96
|
+
"""Get all key-value pairs."""
|
97
|
+
return iter(self._union_find.values.items())
|
98
|
+
|
99
|
+
def __len__(self) -> int:
|
100
|
+
"""Get the number of items."""
|
101
|
+
return self._size_tracker['size']
|
102
|
+
|
103
|
+
def to_native(self) -> Dict[str, Any]:
|
104
|
+
"""Convert to native Python dict with set structure info."""
|
105
|
+
result = {}
|
106
|
+
for key in self._union_find.parent.keys():
|
107
|
+
result[key] = {
|
108
|
+
'value': safe_to_native_conversion(self._union_find.values.get(key)),
|
109
|
+
'root': self._union_find.find(key),
|
110
|
+
'set_id': self._union_find.find(key)
|
111
|
+
}
|
112
|
+
return result
|
113
|
+
|
114
|
+
@property
|
115
|
+
def is_list(self) -> bool:
|
116
|
+
"""This is not a list strategy."""
|
117
|
+
return False
|
118
|
+
|
119
|
+
@property
|
120
|
+
def is_dict(self) -> bool:
|
121
|
+
"""This behaves like a dict but with set semantics."""
|
122
|
+
return True
|
123
|
+
|
124
|
+
# ============================================================================
|
125
|
+
# UNION-FIND SPECIFIC OPERATIONS
|
126
|
+
# ============================================================================
|
127
|
+
|
128
|
+
def make_set(self, element: str, value: Any = None) -> None:
|
129
|
+
"""Create a new set containing only the given element."""
|
130
|
+
if element not in self._union_find.parent:
|
131
|
+
update_size_tracker(self._size_tracker, 1)
|
132
|
+
self._union_find.make_set(element, value)
|
133
|
+
|
134
|
+
def find(self, element: str) -> str:
|
135
|
+
"""Find the root of the set containing element (with path compression)."""
|
136
|
+
return self._union_find.find(element)
|
137
|
+
|
138
|
+
def union(self, element1: str, element2: str) -> bool:
|
139
|
+
"""Union the sets containing element1 and element2."""
|
140
|
+
return self._union_find.union(element1, element2)
|
141
|
+
|
142
|
+
def connected(self, element1: str, element2: str) -> bool:
|
143
|
+
"""Check if two elements are in the same set."""
|
144
|
+
return self._union_find.connected(element1, element2)
|
145
|
+
|
146
|
+
def get_set_members(self, element: str) -> Set[str]:
|
147
|
+
"""Get all members of the set containing the given element."""
|
148
|
+
return self._union_find.get_set_members(element)
|
149
|
+
|
150
|
+
def get_all_sets(self) -> List[Set[str]]:
|
151
|
+
"""Get all disjoint sets."""
|
152
|
+
return self._union_find.get_all_sets()
|
153
|
+
|
154
|
+
def get_set_count(self) -> int:
|
155
|
+
"""Get the number of disjoint sets."""
|
156
|
+
return self._union_find.get_set_count()
|
157
|
+
|
158
|
+
def get_set_size(self, element: str) -> int:
|
159
|
+
"""Get the size of the set containing the given element."""
|
160
|
+
return self._union_find.get_set_size(element)
|
161
|
+
|
162
|
+
# ============================================================================
|
163
|
+
# PERFORMANCE CHARACTERISTICS
|
164
|
+
# ============================================================================
|
165
|
+
|
166
|
+
def backend_info(self) -> Dict[str, Any]:
|
167
|
+
"""Get backend implementation info."""
|
168
|
+
return create_basic_backend_info(
|
169
|
+
'UNION_FIND',
|
170
|
+
'UnionFind with path compression and union by rank',
|
171
|
+
complexity={
|
172
|
+
'find': 'O(α(n)) amortized where α is inverse Ackermann',
|
173
|
+
'union': 'O(α(n)) amortized where α is inverse Ackermann',
|
174
|
+
'connected': 'O(α(n)) amortized where α is inverse Ackermann'
|
175
|
+
}
|
176
|
+
)
|
177
|
+
|
178
|
+
def metrics(self) -> Dict[str, Any]:
|
179
|
+
"""Get performance metrics."""
|
180
|
+
base_metrics = create_basic_metrics('UNION_FIND', self._size_tracker['size'])
|
181
|
+
access_metrics = get_access_metrics(self._access_tracker)
|
182
|
+
base_metrics.update(access_metrics)
|
183
|
+
base_metrics.update({
|
184
|
+
'set_count': self._union_find.get_set_count(),
|
185
|
+
'avg_set_size': self._size_tracker['size'] / max(1, self._union_find.get_set_count())
|
186
|
+
})
|
187
|
+
return base_metrics
|