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,390 @@
|
|
1
|
+
"""
|
2
|
+
Ordered Map Node Strategy Implementation
|
3
|
+
|
4
|
+
This module implements the ORDERED_MAP strategy for sorted key-value
|
5
|
+
operations with efficient range queries and ordered iteration.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Iterator, List, Dict, Optional, Tuple
|
9
|
+
import bisect
|
10
|
+
from .base import ANodeTreeStrategy
|
11
|
+
from ...types import NodeMode, NodeTrait
|
12
|
+
|
13
|
+
|
14
|
+
class OrderedMapStrategy(ANodeTreeStrategy):
|
15
|
+
"""
|
16
|
+
Ordered Map node strategy for sorted key-value operations.
|
17
|
+
|
18
|
+
Maintains keys in sorted order for efficient range queries,
|
19
|
+
ordered iteration, and logarithmic search operations.
|
20
|
+
"""
|
21
|
+
|
22
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
23
|
+
"""Initialize the Ordered Map strategy."""
|
24
|
+
super().__init__(NodeMode.ORDERED_MAP, traits, **options)
|
25
|
+
|
26
|
+
self.case_sensitive = options.get('case_sensitive', True)
|
27
|
+
self.allow_duplicates = options.get('allow_duplicates', False)
|
28
|
+
|
29
|
+
# Core storage: parallel sorted arrays
|
30
|
+
self._keys: List[str] = []
|
31
|
+
self._values: List[Any] = []
|
32
|
+
self._size = 0
|
33
|
+
|
34
|
+
def get_supported_traits(self) -> NodeTrait:
|
35
|
+
"""Get the traits supported by the ordered map strategy."""
|
36
|
+
return (NodeTrait.ORDERED | NodeTrait.INDEXED | NodeTrait.HIERARCHICAL)
|
37
|
+
|
38
|
+
def _normalize_key(self, key: str) -> str:
|
39
|
+
"""Normalize key based on case sensitivity."""
|
40
|
+
return key if self.case_sensitive else key.lower()
|
41
|
+
|
42
|
+
def _find_key_position(self, key: str) -> int:
|
43
|
+
"""Find position where key should be inserted (or exists)."""
|
44
|
+
normalized_key = self._normalize_key(key)
|
45
|
+
return bisect.bisect_left(self._keys, normalized_key)
|
46
|
+
|
47
|
+
def _insert_at_position(self, position: int, key: str, value: Any) -> None:
|
48
|
+
"""Insert key-value pair at specific position."""
|
49
|
+
normalized_key = self._normalize_key(key)
|
50
|
+
self._keys.insert(position, normalized_key)
|
51
|
+
self._values.insert(position, value)
|
52
|
+
self._size += 1
|
53
|
+
|
54
|
+
def _remove_at_position(self, position: int) -> Any:
|
55
|
+
"""Remove key-value pair at specific position."""
|
56
|
+
if 0 <= position < self._size:
|
57
|
+
self._keys.pop(position)
|
58
|
+
value = self._values.pop(position)
|
59
|
+
self._size -= 1
|
60
|
+
return value
|
61
|
+
return None
|
62
|
+
|
63
|
+
# ============================================================================
|
64
|
+
# CORE OPERATIONS
|
65
|
+
# ============================================================================
|
66
|
+
|
67
|
+
def put(self, key: Any, value: Any = None) -> None:
|
68
|
+
"""Add/update key-value pair in sorted order."""
|
69
|
+
key_str = str(key)
|
70
|
+
normalized_key = self._normalize_key(key_str)
|
71
|
+
position = self._find_key_position(key_str)
|
72
|
+
|
73
|
+
# Check if key already exists
|
74
|
+
if (position < self._size and
|
75
|
+
self._keys[position] == normalized_key):
|
76
|
+
if not self.allow_duplicates:
|
77
|
+
# Update existing key
|
78
|
+
self._values[position] = value
|
79
|
+
return
|
80
|
+
|
81
|
+
# Insert new key-value pair
|
82
|
+
self._insert_at_position(position, key_str, value)
|
83
|
+
|
84
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
85
|
+
"""Get value by key."""
|
86
|
+
key_str = str(key)
|
87
|
+
|
88
|
+
if key_str == "sorted_keys":
|
89
|
+
return self._keys.copy()
|
90
|
+
elif key_str == "sorted_values":
|
91
|
+
return self._values.copy()
|
92
|
+
elif key_str == "map_info":
|
93
|
+
return {
|
94
|
+
'size': self._size,
|
95
|
+
'case_sensitive': self.case_sensitive,
|
96
|
+
'allow_duplicates': self.allow_duplicates,
|
97
|
+
'first_key': self.first_key(),
|
98
|
+
'last_key': self.last_key()
|
99
|
+
}
|
100
|
+
elif key_str.isdigit():
|
101
|
+
# Numeric access by index
|
102
|
+
index = int(key_str)
|
103
|
+
if 0 <= index < self._size:
|
104
|
+
return self._values[index]
|
105
|
+
return default
|
106
|
+
|
107
|
+
normalized_key = self._normalize_key(key_str)
|
108
|
+
position = self._find_key_position(key_str)
|
109
|
+
|
110
|
+
if (position < self._size and
|
111
|
+
self._keys[position] == normalized_key):
|
112
|
+
return self._values[position]
|
113
|
+
|
114
|
+
return default
|
115
|
+
|
116
|
+
def has(self, key: Any) -> bool:
|
117
|
+
"""Check if key exists."""
|
118
|
+
key_str = str(key)
|
119
|
+
|
120
|
+
if key_str in ["sorted_keys", "sorted_values", "map_info"]:
|
121
|
+
return True
|
122
|
+
elif key_str.isdigit():
|
123
|
+
index = int(key_str)
|
124
|
+
return 0 <= index < self._size
|
125
|
+
|
126
|
+
normalized_key = self._normalize_key(key_str)
|
127
|
+
position = self._find_key_position(key_str)
|
128
|
+
|
129
|
+
return (position < self._size and
|
130
|
+
self._keys[position] == normalized_key)
|
131
|
+
|
132
|
+
def remove(self, key: Any) -> bool:
|
133
|
+
"""Remove key from map."""
|
134
|
+
key_str = str(key)
|
135
|
+
|
136
|
+
if key_str.isdigit():
|
137
|
+
# Remove by index
|
138
|
+
index = int(key_str)
|
139
|
+
if 0 <= index < self._size:
|
140
|
+
self._remove_at_position(index)
|
141
|
+
return True
|
142
|
+
return False
|
143
|
+
|
144
|
+
normalized_key = self._normalize_key(key_str)
|
145
|
+
position = self._find_key_position(key_str)
|
146
|
+
|
147
|
+
if (position < self._size and
|
148
|
+
self._keys[position] == normalized_key):
|
149
|
+
self._remove_at_position(position)
|
150
|
+
return True
|
151
|
+
|
152
|
+
return False
|
153
|
+
|
154
|
+
def delete(self, key: Any) -> bool:
|
155
|
+
"""Remove key from map (alias for remove)."""
|
156
|
+
return self.remove(key)
|
157
|
+
|
158
|
+
def clear(self) -> None:
|
159
|
+
"""Clear all data."""
|
160
|
+
self._keys.clear()
|
161
|
+
self._values.clear()
|
162
|
+
self._size = 0
|
163
|
+
|
164
|
+
def keys(self) -> Iterator[str]:
|
165
|
+
"""Get all keys in sorted order."""
|
166
|
+
for key in self._keys:
|
167
|
+
yield key
|
168
|
+
|
169
|
+
def values(self) -> Iterator[Any]:
|
170
|
+
"""Get all values in key order."""
|
171
|
+
for value in self._values:
|
172
|
+
yield value
|
173
|
+
|
174
|
+
def items(self) -> Iterator[tuple[str, Any]]:
|
175
|
+
"""Get all key-value pairs in sorted order."""
|
176
|
+
for key, value in zip(self._keys, self._values):
|
177
|
+
yield (key, value)
|
178
|
+
|
179
|
+
def __len__(self) -> int:
|
180
|
+
"""Get number of key-value pairs."""
|
181
|
+
return self._size
|
182
|
+
|
183
|
+
def to_native(self) -> Dict[str, Any]:
|
184
|
+
"""Convert to native Python dict (preserves insertion order in Python 3.7+)."""
|
185
|
+
return dict(zip(self._keys, self._values))
|
186
|
+
|
187
|
+
@property
|
188
|
+
def is_list(self) -> bool:
|
189
|
+
"""This can behave like a list for indexed access."""
|
190
|
+
return True
|
191
|
+
|
192
|
+
@property
|
193
|
+
def is_dict(self) -> bool:
|
194
|
+
"""This is a dict-like structure."""
|
195
|
+
return True
|
196
|
+
|
197
|
+
# ============================================================================
|
198
|
+
# ORDERED MAP SPECIFIC OPERATIONS
|
199
|
+
# ============================================================================
|
200
|
+
|
201
|
+
def first_key(self) -> Optional[str]:
|
202
|
+
"""Get first (smallest) key."""
|
203
|
+
return self._keys[0] if self._size > 0 else None
|
204
|
+
|
205
|
+
def last_key(self) -> Optional[str]:
|
206
|
+
"""Get last (largest) key."""
|
207
|
+
return self._keys[-1] if self._size > 0 else None
|
208
|
+
|
209
|
+
def first_value(self) -> Any:
|
210
|
+
"""Get value of first key."""
|
211
|
+
return self._values[0] if self._size > 0 else None
|
212
|
+
|
213
|
+
def last_value(self) -> Any:
|
214
|
+
"""Get value of last key."""
|
215
|
+
return self._values[-1] if self._size > 0 else None
|
216
|
+
|
217
|
+
def get_range(self, start_key: str, end_key: str, inclusive: bool = True) -> List[Tuple[str, Any]]:
|
218
|
+
"""Get key-value pairs in range [start_key, end_key]."""
|
219
|
+
start_norm = self._normalize_key(start_key)
|
220
|
+
end_norm = self._normalize_key(end_key)
|
221
|
+
|
222
|
+
result = []
|
223
|
+
for key, value in zip(self._keys, self._values):
|
224
|
+
if inclusive:
|
225
|
+
if start_norm <= key <= end_norm:
|
226
|
+
result.append((key, value))
|
227
|
+
else:
|
228
|
+
if start_norm < key < end_norm:
|
229
|
+
result.append((key, value))
|
230
|
+
|
231
|
+
return result
|
232
|
+
|
233
|
+
def get_keys_range(self, start_key: str, end_key: str, inclusive: bool = True) -> List[str]:
|
234
|
+
"""Get keys in range."""
|
235
|
+
range_items = self.get_range(start_key, end_key, inclusive)
|
236
|
+
return [key for key, _ in range_items]
|
237
|
+
|
238
|
+
def get_values_range(self, start_key: str, end_key: str, inclusive: bool = True) -> List[Any]:
|
239
|
+
"""Get values in key range."""
|
240
|
+
range_items = self.get_range(start_key, end_key, inclusive)
|
241
|
+
return [value for _, value in range_items]
|
242
|
+
|
243
|
+
def lower_bound(self, key: str) -> Optional[str]:
|
244
|
+
"""Find first key >= given key."""
|
245
|
+
normalized_key = self._normalize_key(key)
|
246
|
+
position = bisect.bisect_left(self._keys, normalized_key)
|
247
|
+
|
248
|
+
return self._keys[position] if position < self._size else None
|
249
|
+
|
250
|
+
def upper_bound(self, key: str) -> Optional[str]:
|
251
|
+
"""Find first key > given key."""
|
252
|
+
normalized_key = self._normalize_key(key)
|
253
|
+
position = bisect.bisect_right(self._keys, normalized_key)
|
254
|
+
|
255
|
+
return self._keys[position] if position < self._size else None
|
256
|
+
|
257
|
+
def floor(self, key: str) -> Optional[str]:
|
258
|
+
"""Find largest key <= given key."""
|
259
|
+
normalized_key = self._normalize_key(key)
|
260
|
+
position = bisect.bisect_right(self._keys, normalized_key) - 1
|
261
|
+
|
262
|
+
return self._keys[position] if position >= 0 else None
|
263
|
+
|
264
|
+
def ceiling(self, key: str) -> Optional[str]:
|
265
|
+
"""Find smallest key >= given key."""
|
266
|
+
return self.lower_bound(key)
|
267
|
+
|
268
|
+
def get_at_index(self, index: int) -> Optional[Tuple[str, Any]]:
|
269
|
+
"""Get key-value pair at specific index."""
|
270
|
+
if 0 <= index < self._size:
|
271
|
+
return (self._keys[index], self._values[index])
|
272
|
+
return None
|
273
|
+
|
274
|
+
def index_of(self, key: str) -> int:
|
275
|
+
"""Get index of key (-1 if not found)."""
|
276
|
+
normalized_key = self._normalize_key(key)
|
277
|
+
position = self._find_key_position(key)
|
278
|
+
|
279
|
+
if (position < self._size and
|
280
|
+
self._keys[position] == normalized_key):
|
281
|
+
return position
|
282
|
+
|
283
|
+
return -1
|
284
|
+
|
285
|
+
def pop_first(self) -> Optional[Tuple[str, Any]]:
|
286
|
+
"""Remove and return first key-value pair."""
|
287
|
+
if self._size > 0:
|
288
|
+
key = self._keys[0]
|
289
|
+
value = self._remove_at_position(0)
|
290
|
+
return (key, value)
|
291
|
+
return None
|
292
|
+
|
293
|
+
def pop_last(self) -> Optional[Tuple[str, Any]]:
|
294
|
+
"""Remove and return last key-value pair."""
|
295
|
+
if self._size > 0:
|
296
|
+
key = self._keys[-1]
|
297
|
+
value = self._remove_at_position(self._size - 1)
|
298
|
+
return (key, value)
|
299
|
+
return None
|
300
|
+
|
301
|
+
def reverse_keys(self) -> Iterator[str]:
|
302
|
+
"""Get keys in reverse order."""
|
303
|
+
for i in range(self._size - 1, -1, -1):
|
304
|
+
yield self._keys[i]
|
305
|
+
|
306
|
+
def reverse_values(self) -> Iterator[Any]:
|
307
|
+
"""Get values in reverse key order."""
|
308
|
+
for i in range(self._size - 1, -1, -1):
|
309
|
+
yield self._values[i]
|
310
|
+
|
311
|
+
def reverse_items(self) -> Iterator[Tuple[str, Any]]:
|
312
|
+
"""Get key-value pairs in reverse order."""
|
313
|
+
for i in range(self._size - 1, -1, -1):
|
314
|
+
yield (self._keys[i], self._values[i])
|
315
|
+
|
316
|
+
def find_prefix_keys(self, prefix: str) -> List[str]:
|
317
|
+
"""Find all keys starting with given prefix."""
|
318
|
+
normalized_prefix = self._normalize_key(prefix)
|
319
|
+
result = []
|
320
|
+
|
321
|
+
for key in self._keys:
|
322
|
+
if key.startswith(normalized_prefix):
|
323
|
+
result.append(key)
|
324
|
+
elif key > normalized_prefix and not key.startswith(normalized_prefix):
|
325
|
+
break # Keys are sorted, no more matches possible
|
326
|
+
|
327
|
+
return result
|
328
|
+
|
329
|
+
def count_range(self, start_key: str, end_key: str, inclusive: bool = True) -> int:
|
330
|
+
"""Count keys in range."""
|
331
|
+
return len(self.get_keys_range(start_key, end_key, inclusive))
|
332
|
+
|
333
|
+
def get_statistics(self) -> Dict[str, Any]:
|
334
|
+
"""Get comprehensive ordered map statistics."""
|
335
|
+
if self._size == 0:
|
336
|
+
return {'size': 0, 'first_key': None, 'last_key': None}
|
337
|
+
|
338
|
+
# Calculate key length statistics
|
339
|
+
key_lengths = [len(key) for key in self._keys]
|
340
|
+
avg_key_length = sum(key_lengths) / len(key_lengths)
|
341
|
+
min_key_length = min(key_lengths)
|
342
|
+
max_key_length = max(key_lengths)
|
343
|
+
|
344
|
+
return {
|
345
|
+
'size': self._size,
|
346
|
+
'first_key': self.first_key(),
|
347
|
+
'last_key': self.last_key(),
|
348
|
+
'case_sensitive': self.case_sensitive,
|
349
|
+
'allow_duplicates': self.allow_duplicates,
|
350
|
+
'avg_key_length': avg_key_length,
|
351
|
+
'min_key_length': min_key_length,
|
352
|
+
'max_key_length': max_key_length,
|
353
|
+
'memory_usage': self._size * 50 # Estimated
|
354
|
+
}
|
355
|
+
|
356
|
+
# ============================================================================
|
357
|
+
# PERFORMANCE CHARACTERISTICS
|
358
|
+
# ============================================================================
|
359
|
+
|
360
|
+
@property
|
361
|
+
def backend_info(self) -> Dict[str, Any]:
|
362
|
+
"""Get backend implementation info."""
|
363
|
+
return {
|
364
|
+
'strategy': 'ORDERED_MAP',
|
365
|
+
'backend': 'Parallel sorted arrays with binary search',
|
366
|
+
'case_sensitive': self.case_sensitive,
|
367
|
+
'allow_duplicates': self.allow_duplicates,
|
368
|
+
'complexity': {
|
369
|
+
'put': 'O(n)', # Due to array insertion
|
370
|
+
'get': 'O(log n)', # Binary search
|
371
|
+
'remove': 'O(n)', # Due to array removal
|
372
|
+
'range_query': 'O(log n + k)', # k = result size
|
373
|
+
'iteration': 'O(n)',
|
374
|
+
'space': 'O(n)'
|
375
|
+
}
|
376
|
+
}
|
377
|
+
|
378
|
+
@property
|
379
|
+
def metrics(self) -> Dict[str, Any]:
|
380
|
+
"""Get performance metrics."""
|
381
|
+
stats = self.get_statistics()
|
382
|
+
|
383
|
+
return {
|
384
|
+
'size': stats['size'],
|
385
|
+
'first_key': str(stats['first_key']) if stats['first_key'] else 'None',
|
386
|
+
'last_key': str(stats['last_key']) if stats['last_key'] else 'None',
|
387
|
+
'avg_key_length': f"{stats['avg_key_length']:.1f}" if stats.get('avg_key_length') else '0',
|
388
|
+
'case_sensitive': stats['case_sensitive'],
|
389
|
+
'memory_usage': f"{stats['memory_usage']} bytes (estimated)"
|
390
|
+
}
|