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,345 @@
|
|
1
|
+
"""
|
2
|
+
Aho-Corasick Node Strategy Implementation
|
3
|
+
|
4
|
+
This module implements the AHO_CORASICK strategy for efficient multi-pattern
|
5
|
+
string matching using the Aho-Corasick automaton algorithm.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Iterator, List, Dict, Set, Optional, Tuple
|
9
|
+
from collections import deque, defaultdict
|
10
|
+
from .base import ANodeTreeStrategy
|
11
|
+
from ...types import NodeMode, NodeTrait
|
12
|
+
|
13
|
+
|
14
|
+
class ACNode:
|
15
|
+
"""Node in the Aho-Corasick trie."""
|
16
|
+
|
17
|
+
def __init__(self):
|
18
|
+
self.children: Dict[str, 'ACNode'] = {}
|
19
|
+
self.failure: Optional['ACNode'] = None
|
20
|
+
self.output: Set[str] = set() # Patterns that end at this node
|
21
|
+
self.pattern_indices: Set[int] = set() # Indices of patterns
|
22
|
+
self.depth = 0
|
23
|
+
|
24
|
+
def is_leaf(self) -> bool:
|
25
|
+
"""Check if this is a leaf node."""
|
26
|
+
return len(self.children) == 0
|
27
|
+
|
28
|
+
|
29
|
+
class xAhoCorasickStrategy(ANodeTreeStrategy):
|
30
|
+
"""
|
31
|
+
Aho-Corasick node strategy for multi-pattern string matching.
|
32
|
+
|
33
|
+
Efficiently searches for multiple patterns simultaneously in a text
|
34
|
+
using a finite automaton with failure links for linear-time matching.
|
35
|
+
"""
|
36
|
+
|
37
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
38
|
+
"""Initialize the Aho-Corasick strategy."""
|
39
|
+
super().__init__(data=None, **options)
|
40
|
+
self._mode = NodeMode.AHO_CORASICK
|
41
|
+
self._traits = traits
|
42
|
+
|
43
|
+
self.case_sensitive = options.get('case_sensitive', True)
|
44
|
+
self.enable_overlapping = options.get('enable_overlapping', True)
|
45
|
+
self.max_pattern_length = options.get('max_pattern_length', 1000)
|
46
|
+
|
47
|
+
# Core automaton
|
48
|
+
self._root = ACNode()
|
49
|
+
self._patterns: List[str] = []
|
50
|
+
self._pattern_to_index: Dict[str, int] = {}
|
51
|
+
self._automaton_built = False
|
52
|
+
|
53
|
+
# Performance tracking
|
54
|
+
self._size_tracker = 0
|
55
|
+
self._access_tracker = 0
|
56
|
+
|
57
|
+
def get_supported_traits(self) -> NodeTrait:
|
58
|
+
"""Get the traits supported by the Aho-Corasick strategy."""
|
59
|
+
return (NodeTrait.ORDERED | NodeTrait.HIERARCHICAL | NodeTrait.INDEXED)
|
60
|
+
|
61
|
+
# ============================================================================
|
62
|
+
# CORE OPERATIONS
|
63
|
+
# ============================================================================
|
64
|
+
|
65
|
+
def insert(self, key: Any, value: Any) -> None:
|
66
|
+
"""Store a pattern (key should be string-like)."""
|
67
|
+
pattern = str(key)
|
68
|
+
if not self.case_sensitive:
|
69
|
+
pattern = pattern.lower()
|
70
|
+
|
71
|
+
if len(pattern) > self.max_pattern_length:
|
72
|
+
raise ValueError(f"Pattern too long: {len(pattern)} > {self.max_pattern_length}")
|
73
|
+
|
74
|
+
if pattern not in self._pattern_to_index:
|
75
|
+
self._patterns.append(pattern)
|
76
|
+
self._pattern_to_index[pattern] = len(self._patterns) - 1
|
77
|
+
self._automaton_built = False
|
78
|
+
self._size_tracker += 1
|
79
|
+
|
80
|
+
def find(self, key: Any) -> Any:
|
81
|
+
"""Find pattern index."""
|
82
|
+
pattern = str(key)
|
83
|
+
if not self.case_sensitive:
|
84
|
+
pattern = pattern.lower()
|
85
|
+
return self._pattern_to_index.get(pattern)
|
86
|
+
|
87
|
+
def delete(self, key: Any) -> bool:
|
88
|
+
"""Remove a pattern."""
|
89
|
+
pattern = str(key)
|
90
|
+
if not self.case_sensitive:
|
91
|
+
pattern = pattern.lower()
|
92
|
+
|
93
|
+
if pattern in self._pattern_to_index:
|
94
|
+
index = self._pattern_to_index[pattern]
|
95
|
+
del self._patterns[index]
|
96
|
+
del self._pattern_to_index[pattern]
|
97
|
+
self._automaton_built = False
|
98
|
+
self._size_tracker -= 1
|
99
|
+
return True
|
100
|
+
return False
|
101
|
+
|
102
|
+
def size(self) -> int:
|
103
|
+
"""Get the number of patterns."""
|
104
|
+
return self._size_tracker
|
105
|
+
|
106
|
+
def is_empty(self) -> bool:
|
107
|
+
"""Check if the structure is empty."""
|
108
|
+
return self._size_tracker == 0
|
109
|
+
|
110
|
+
def to_native(self) -> Dict[str, Any]:
|
111
|
+
"""Convert to native Python dictionary."""
|
112
|
+
return {pattern: index for pattern, index in self._pattern_to_index.items()}
|
113
|
+
|
114
|
+
# ============================================================================
|
115
|
+
# TREE STRATEGY METHODS
|
116
|
+
# ============================================================================
|
117
|
+
|
118
|
+
def traverse(self, order: str = 'inorder') -> List[Any]:
|
119
|
+
"""Traverse patterns in specified order."""
|
120
|
+
return self._patterns.copy()
|
121
|
+
|
122
|
+
def get_min(self) -> Any:
|
123
|
+
"""Get minimum pattern."""
|
124
|
+
return min(self._patterns) if self._patterns else None
|
125
|
+
|
126
|
+
def get_max(self) -> Any:
|
127
|
+
"""Get maximum pattern."""
|
128
|
+
return max(self._patterns) if self._patterns else None
|
129
|
+
|
130
|
+
# ============================================================================
|
131
|
+
# AUTO-3 Phase 2 methods
|
132
|
+
# ============================================================================
|
133
|
+
|
134
|
+
def as_trie(self):
|
135
|
+
"""Provide Trie behavioral view."""
|
136
|
+
return self
|
137
|
+
|
138
|
+
def as_heap(self):
|
139
|
+
"""Provide Heap behavioral view."""
|
140
|
+
# TODO: Implement Heap view
|
141
|
+
return self
|
142
|
+
|
143
|
+
def as_skip_list(self):
|
144
|
+
"""Provide SkipList behavioral view."""
|
145
|
+
# TODO: Implement SkipList view
|
146
|
+
return self
|
147
|
+
|
148
|
+
# ============================================================================
|
149
|
+
# AHO-CORASICK SPECIFIC OPERATIONS
|
150
|
+
# ============================================================================
|
151
|
+
|
152
|
+
def add_pattern(self, pattern: str, metadata: Any = None) -> None:
|
153
|
+
"""Add a pattern to the automaton."""
|
154
|
+
self.insert(pattern, metadata)
|
155
|
+
|
156
|
+
def search_text(self, text: str) -> List[Tuple[str, int, Any]]:
|
157
|
+
"""Search for all patterns in the given text."""
|
158
|
+
if not self._automaton_built:
|
159
|
+
self._build_automaton()
|
160
|
+
|
161
|
+
results = []
|
162
|
+
current = self._root
|
163
|
+
|
164
|
+
for i, char in enumerate(text):
|
165
|
+
if not self.case_sensitive:
|
166
|
+
char = char.lower()
|
167
|
+
|
168
|
+
# Follow failure links if needed
|
169
|
+
while current != self._root and char not in current.children:
|
170
|
+
current = current.failure
|
171
|
+
|
172
|
+
# Move to next state
|
173
|
+
if char in current.children:
|
174
|
+
current = current.children[char]
|
175
|
+
|
176
|
+
# Check for matches
|
177
|
+
for pattern in current.output:
|
178
|
+
pattern_index = self._pattern_to_index[pattern]
|
179
|
+
results.append((pattern, i - len(pattern) + 1, pattern_index))
|
180
|
+
|
181
|
+
return results
|
182
|
+
|
183
|
+
def find_all_matches(self, text: str) -> Dict[str, List[int]]:
|
184
|
+
"""Find all matches grouped by pattern."""
|
185
|
+
matches = self.search_text(text)
|
186
|
+
result = defaultdict(list)
|
187
|
+
|
188
|
+
for pattern, position, _ in matches:
|
189
|
+
result[pattern].append(position)
|
190
|
+
|
191
|
+
return dict(result)
|
192
|
+
|
193
|
+
def count_matches(self, text: str) -> Dict[str, int]:
|
194
|
+
"""Count matches for each pattern."""
|
195
|
+
all_matches = self.find_all_matches(text)
|
196
|
+
return {pattern: len(positions) for pattern, positions in all_matches.items()}
|
197
|
+
|
198
|
+
def has_any_match(self, text: str) -> bool:
|
199
|
+
"""Check if any pattern matches in the text."""
|
200
|
+
if not self._automaton_built:
|
201
|
+
self._build_automaton()
|
202
|
+
|
203
|
+
current = self._root
|
204
|
+
|
205
|
+
for char in text:
|
206
|
+
if not self.case_sensitive:
|
207
|
+
char = char.lower()
|
208
|
+
|
209
|
+
while current != self._root and char not in current.children:
|
210
|
+
current = current.failure
|
211
|
+
|
212
|
+
if char in current.children:
|
213
|
+
current = current.children[char]
|
214
|
+
|
215
|
+
if current.output:
|
216
|
+
return True
|
217
|
+
|
218
|
+
return False
|
219
|
+
|
220
|
+
def find_longest_match(self, text: str) -> Optional[Tuple[str, int, int]]:
|
221
|
+
"""Find the longest matching pattern."""
|
222
|
+
matches = self.search_text(text)
|
223
|
+
if not matches:
|
224
|
+
return None
|
225
|
+
|
226
|
+
# Find the longest match
|
227
|
+
longest = max(matches, key=lambda x: len(x[0]))
|
228
|
+
return (longest[0], longest[1], longest[1] + len(longest[0]) - 1)
|
229
|
+
|
230
|
+
def replace_patterns(self, text: str, replacement_func: callable = None) -> str:
|
231
|
+
"""Replace all pattern matches in text."""
|
232
|
+
matches = self.search_text(text)
|
233
|
+
if not matches:
|
234
|
+
return text
|
235
|
+
|
236
|
+
# Sort matches by position (descending) to replace from end to start
|
237
|
+
matches.sort(key=lambda x: x[1], reverse=True)
|
238
|
+
|
239
|
+
result = text
|
240
|
+
for pattern, position, _ in matches:
|
241
|
+
if replacement_func:
|
242
|
+
replacement = replacement_func(pattern, position)
|
243
|
+
else:
|
244
|
+
replacement = f"[{pattern}]"
|
245
|
+
|
246
|
+
result = result[:position] + replacement + result[position + len(pattern):]
|
247
|
+
|
248
|
+
return result
|
249
|
+
|
250
|
+
def _build_automaton(self) -> None:
|
251
|
+
"""Build the Aho-Corasick automaton."""
|
252
|
+
# Build trie
|
253
|
+
for pattern in self._patterns:
|
254
|
+
self._add_pattern_to_trie(pattern)
|
255
|
+
|
256
|
+
# Build failure links
|
257
|
+
self._build_failure_links()
|
258
|
+
|
259
|
+
self._automaton_built = True
|
260
|
+
|
261
|
+
def _add_pattern_to_trie(self, pattern: str, pattern_index: int) -> None:
|
262
|
+
"""Add a pattern to the trie."""
|
263
|
+
current = self._root
|
264
|
+
|
265
|
+
for char in pattern:
|
266
|
+
if char not in current.children:
|
267
|
+
current.children[char] = ACNode()
|
268
|
+
current.children[char].depth = current.depth + 1
|
269
|
+
|
270
|
+
current = current.children[char]
|
271
|
+
|
272
|
+
current.output.add(pattern)
|
273
|
+
current.pattern_indices.add(pattern_index)
|
274
|
+
|
275
|
+
def _build_failure_links(self) -> None:
|
276
|
+
"""Build failure links using BFS."""
|
277
|
+
queue = deque()
|
278
|
+
|
279
|
+
# Initialize failure links for root's children
|
280
|
+
for child in self._root.children.values():
|
281
|
+
child.failure = self._root
|
282
|
+
queue.append(child)
|
283
|
+
|
284
|
+
# Build failure links for remaining nodes
|
285
|
+
while queue:
|
286
|
+
current = queue.popleft()
|
287
|
+
|
288
|
+
for char, child in current.children.items():
|
289
|
+
queue.append(child)
|
290
|
+
|
291
|
+
# Find failure link
|
292
|
+
failure = current.failure
|
293
|
+
while failure != self._root and char not in failure.children:
|
294
|
+
failure = failure.failure
|
295
|
+
|
296
|
+
if char in failure.children:
|
297
|
+
child.failure = failure.children[char]
|
298
|
+
else:
|
299
|
+
child.failure = self._root
|
300
|
+
|
301
|
+
# Merge output sets
|
302
|
+
child.output.update(child.failure.output)
|
303
|
+
|
304
|
+
# ============================================================================
|
305
|
+
# ITERATION
|
306
|
+
# ============================================================================
|
307
|
+
|
308
|
+
def keys(self) -> Iterator[str]:
|
309
|
+
"""Get all patterns."""
|
310
|
+
return iter(self._patterns)
|
311
|
+
|
312
|
+
def values(self) -> Iterator[Any]:
|
313
|
+
"""Get all pattern indices."""
|
314
|
+
return iter(range(len(self._patterns)))
|
315
|
+
|
316
|
+
def items(self) -> Iterator[tuple[str, Any]]:
|
317
|
+
"""Get all pattern-index pairs."""
|
318
|
+
return ((pattern, index) for pattern, index in self._pattern_to_index.items())
|
319
|
+
|
320
|
+
# ============================================================================
|
321
|
+
# PERFORMANCE CHARACTERISTICS
|
322
|
+
# ============================================================================
|
323
|
+
|
324
|
+
@property
|
325
|
+
def backend_info(self) -> Dict[str, Any]:
|
326
|
+
"""Get backend implementation info."""
|
327
|
+
return {
|
328
|
+
'strategy': 'AHO_CORASICK',
|
329
|
+
'backend': 'Aho-Corasick automaton',
|
330
|
+
'complexity': {
|
331
|
+
'build': 'O(sum of pattern lengths)',
|
332
|
+
'search': 'O(text length + number of matches)',
|
333
|
+
'space': 'O(sum of pattern lengths)'
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
@property
|
338
|
+
def metrics(self) -> Dict[str, Any]:
|
339
|
+
"""Get performance metrics."""
|
340
|
+
return {
|
341
|
+
'patterns': len(self._patterns),
|
342
|
+
'automaton_built': self._automaton_built,
|
343
|
+
'case_sensitive': self.case_sensitive,
|
344
|
+
'max_pattern_length': self.max_pattern_length
|
345
|
+
}
|
@@ -0,0 +1,209 @@
|
|
1
|
+
"""
|
2
|
+
Array List Node Strategy Implementation
|
3
|
+
|
4
|
+
This module implements the ARRAY_LIST strategy for sequential data
|
5
|
+
with fast indexed access.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Iterator, List, Union, Dict
|
9
|
+
from .base import ANodeLinearStrategy
|
10
|
+
from ...types import NodeMode, NodeTrait
|
11
|
+
|
12
|
+
|
13
|
+
class ArrayListStrategy(ANodeLinearStrategy):
|
14
|
+
"""
|
15
|
+
Array List node strategy for sequential data with O(1) indexed access.
|
16
|
+
|
17
|
+
Uses Python's built-in list for optimal performance with indexed operations.
|
18
|
+
"""
|
19
|
+
|
20
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
21
|
+
"""Initialize the array list strategy."""
|
22
|
+
super().__init__(data=None, **options)
|
23
|
+
self._mode = NodeMode.ARRAY_LIST
|
24
|
+
self._traits = traits
|
25
|
+
self._data: List[Any] = []
|
26
|
+
self._size = 0
|
27
|
+
|
28
|
+
def get_supported_traits(self) -> NodeTrait:
|
29
|
+
"""Get the traits supported by the array list strategy."""
|
30
|
+
return (NodeTrait.ORDERED | NodeTrait.INDEXED)
|
31
|
+
|
32
|
+
# ============================================================================
|
33
|
+
# CORE OPERATIONS (Key-based interface for compatibility)
|
34
|
+
# ============================================================================
|
35
|
+
|
36
|
+
def insert(self, key: Any, value: Any) -> None:
|
37
|
+
"""Store a value at index (key must be numeric)."""
|
38
|
+
try:
|
39
|
+
index = int(key)
|
40
|
+
except (ValueError, TypeError):
|
41
|
+
raise TypeError(f"Array list requires numeric indices, got {type(key).__name__}")
|
42
|
+
|
43
|
+
# Extend list if necessary
|
44
|
+
while len(self._data) <= index:
|
45
|
+
self._data.append(None)
|
46
|
+
|
47
|
+
if self._data[index] is None:
|
48
|
+
self._size += 1
|
49
|
+
self._data[index] = value
|
50
|
+
|
51
|
+
def find(self, key: Any) -> Any:
|
52
|
+
"""Retrieve a value by index."""
|
53
|
+
try:
|
54
|
+
index = int(key)
|
55
|
+
if 0 <= index < len(self._data):
|
56
|
+
value = self._data[index]
|
57
|
+
return value if value is not None else None
|
58
|
+
return None
|
59
|
+
except (ValueError, TypeError):
|
60
|
+
return None
|
61
|
+
|
62
|
+
def delete(self, key: Any) -> bool:
|
63
|
+
"""Remove value at index."""
|
64
|
+
try:
|
65
|
+
index = int(key)
|
66
|
+
if 0 <= index < len(self._data) and self._data[index] is not None:
|
67
|
+
self._data[index] = None
|
68
|
+
self._size -= 1
|
69
|
+
return True
|
70
|
+
return False
|
71
|
+
except (ValueError, TypeError):
|
72
|
+
return False
|
73
|
+
|
74
|
+
def size(self) -> int:
|
75
|
+
"""Get the number of non-None items."""
|
76
|
+
return self._size
|
77
|
+
|
78
|
+
def is_empty(self) -> bool:
|
79
|
+
"""Check if structure is empty."""
|
80
|
+
return self._size == 0
|
81
|
+
|
82
|
+
def to_native(self) -> List[Any]:
|
83
|
+
"""Convert to native Python list."""
|
84
|
+
# Return only non-None values in order
|
85
|
+
return [value for value in self._data if value is not None]
|
86
|
+
|
87
|
+
# ============================================================================
|
88
|
+
# LINEAR STRATEGY METHODS
|
89
|
+
# ============================================================================
|
90
|
+
|
91
|
+
def push_front(self, value: Any) -> None:
|
92
|
+
"""Add element to front."""
|
93
|
+
self._data.insert(0, value)
|
94
|
+
self._size += 1
|
95
|
+
|
96
|
+
def push_back(self, value: Any) -> None:
|
97
|
+
"""Add element to back."""
|
98
|
+
self._data.append(value)
|
99
|
+
self._size += 1
|
100
|
+
|
101
|
+
def pop_front(self) -> Any:
|
102
|
+
"""Remove element from front."""
|
103
|
+
if not self._data:
|
104
|
+
raise IndexError("pop from empty list")
|
105
|
+
value = self._data.pop(0)
|
106
|
+
self._size -= 1
|
107
|
+
return value
|
108
|
+
|
109
|
+
def pop_back(self) -> Any:
|
110
|
+
"""Remove element from back."""
|
111
|
+
if not self._data:
|
112
|
+
raise IndexError("pop from empty list")
|
113
|
+
value = self._data.pop()
|
114
|
+
self._size -= 1
|
115
|
+
return value
|
116
|
+
|
117
|
+
def get_at_index(self, index: int) -> Any:
|
118
|
+
"""Get element at index."""
|
119
|
+
if 0 <= index < len(self._data):
|
120
|
+
return self._data[index]
|
121
|
+
raise IndexError("list index out of range")
|
122
|
+
|
123
|
+
def set_at_index(self, index: int, value: Any) -> None:
|
124
|
+
"""Set element at index."""
|
125
|
+
if 0 <= index < len(self._data):
|
126
|
+
self._data[index] = value
|
127
|
+
else:
|
128
|
+
raise IndexError("list index out of range")
|
129
|
+
|
130
|
+
# ============================================================================
|
131
|
+
# AUTO-3 Phase 1 methods
|
132
|
+
# ============================================================================
|
133
|
+
|
134
|
+
def as_linked_list(self):
|
135
|
+
"""Provide LinkedList behavioral view."""
|
136
|
+
# TODO: Implement LinkedList view
|
137
|
+
return self
|
138
|
+
|
139
|
+
def as_stack(self):
|
140
|
+
"""Provide Stack behavioral view."""
|
141
|
+
# TODO: Implement Stack view
|
142
|
+
return self
|
143
|
+
|
144
|
+
def as_queue(self):
|
145
|
+
"""Provide Queue behavioral view."""
|
146
|
+
# TODO: Implement Queue view
|
147
|
+
return self
|
148
|
+
|
149
|
+
def as_deque(self):
|
150
|
+
"""Provide Deque behavioral view."""
|
151
|
+
# TODO: Implement Deque view
|
152
|
+
return self
|
153
|
+
|
154
|
+
# ============================================================================
|
155
|
+
# ARRAY-SPECIFIC OPERATIONS
|
156
|
+
# ============================================================================
|
157
|
+
|
158
|
+
def append(self, value: Any) -> None:
|
159
|
+
"""Append a value to the end."""
|
160
|
+
self._data.append(value)
|
161
|
+
self._size += 1
|
162
|
+
|
163
|
+
def insert_at(self, index: int, value: Any) -> None:
|
164
|
+
"""Insert a value at the specified index."""
|
165
|
+
self._data.insert(index, value)
|
166
|
+
self._size += 1
|
167
|
+
|
168
|
+
def pop_at(self, index: int = -1) -> Any:
|
169
|
+
"""Remove and return value at index."""
|
170
|
+
if not self._data:
|
171
|
+
raise IndexError("pop from empty list")
|
172
|
+
value = self._data.pop(index)
|
173
|
+
if value is not None:
|
174
|
+
self._size -= 1
|
175
|
+
return value
|
176
|
+
|
177
|
+
def extend(self, values: List[Any]) -> None:
|
178
|
+
"""Extend with multiple values."""
|
179
|
+
self._data.extend(values)
|
180
|
+
self._size += len(values)
|
181
|
+
|
182
|
+
# ============================================================================
|
183
|
+
# PERFORMANCE CHARACTERISTICS
|
184
|
+
# ============================================================================
|
185
|
+
|
186
|
+
@property
|
187
|
+
def backend_info(self) -> Dict[str, Any]:
|
188
|
+
"""Get backend implementation info."""
|
189
|
+
return {
|
190
|
+
'strategy': 'ARRAY_LIST',
|
191
|
+
'backend': 'Python list',
|
192
|
+
'complexity': {
|
193
|
+
'get': 'O(1)',
|
194
|
+
'put': 'O(1) amortized',
|
195
|
+
'append': 'O(1) amortized',
|
196
|
+
'insert': 'O(n)',
|
197
|
+
'pop': 'O(1) end, O(n) middle'
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
@property
|
202
|
+
def metrics(self) -> Dict[str, Any]:
|
203
|
+
"""Get performance metrics."""
|
204
|
+
return {
|
205
|
+
'size': self._size,
|
206
|
+
'capacity': len(self._data),
|
207
|
+
'memory_usage': f"{len(self._data) * 8} bytes (estimated)",
|
208
|
+
'utilization': f"{(self._size / max(1, len(self._data))) * 100:.1f}%"
|
209
|
+
}
|