exonware-xwnode 0.0.1.22__py3-none-any.whl → 0.0.1.23__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 +1 -1
- exonware/xwnode/__init__.py +18 -5
- exonware/xwnode/add_strategy_types.py +165 -0
- exonware/xwnode/common/__init__.py +1 -1
- exonware/xwnode/common/graph/__init__.py +30 -0
- exonware/xwnode/common/graph/caching.py +131 -0
- exonware/xwnode/common/graph/contracts.py +100 -0
- exonware/xwnode/common/graph/errors.py +44 -0
- exonware/xwnode/common/graph/indexing.py +260 -0
- exonware/xwnode/common/graph/manager.py +568 -0
- exonware/xwnode/common/management/__init__.py +3 -5
- exonware/xwnode/common/management/manager.py +2 -2
- exonware/xwnode/common/management/migration.py +3 -3
- exonware/xwnode/common/monitoring/__init__.py +3 -5
- exonware/xwnode/common/monitoring/metrics.py +6 -2
- exonware/xwnode/common/monitoring/pattern_detector.py +1 -1
- exonware/xwnode/common/monitoring/performance_monitor.py +5 -1
- exonware/xwnode/common/patterns/__init__.py +3 -5
- exonware/xwnode/common/patterns/flyweight.py +5 -1
- exonware/xwnode/common/patterns/registry.py +202 -183
- exonware/xwnode/common/utils/__init__.py +25 -11
- exonware/xwnode/common/utils/simple.py +1 -1
- exonware/xwnode/config.py +3 -8
- exonware/xwnode/contracts.py +4 -105
- exonware/xwnode/defs.py +413 -159
- exonware/xwnode/edges/strategies/__init__.py +86 -4
- exonware/xwnode/edges/strategies/_base_edge.py +2 -2
- exonware/xwnode/edges/strategies/adj_list.py +287 -121
- exonware/xwnode/edges/strategies/adj_matrix.py +316 -222
- exonware/xwnode/edges/strategies/base.py +1 -1
- exonware/xwnode/edges/strategies/{edge_bidir_wrapper.py → bidir_wrapper.py} +45 -4
- exonware/xwnode/edges/strategies/bitemporal.py +520 -0
- exonware/xwnode/edges/strategies/{edge_block_adj_matrix.py → block_adj_matrix.py} +77 -6
- exonware/xwnode/edges/strategies/bv_graph.py +664 -0
- exonware/xwnode/edges/strategies/compressed_graph.py +217 -0
- exonware/xwnode/edges/strategies/{edge_coo.py → coo.py} +46 -4
- exonware/xwnode/edges/strategies/{edge_csc.py → csc.py} +45 -4
- exonware/xwnode/edges/strategies/{edge_csr.py → csr.py} +94 -12
- exonware/xwnode/edges/strategies/{edge_dynamic_adj_list.py → dynamic_adj_list.py} +46 -4
- exonware/xwnode/edges/strategies/edge_list.py +168 -0
- exonware/xwnode/edges/strategies/edge_property_store.py +2 -2
- exonware/xwnode/edges/strategies/euler_tour.py +560 -0
- exonware/xwnode/edges/strategies/{edge_flow_network.py → flow_network.py} +2 -2
- exonware/xwnode/edges/strategies/graphblas.py +449 -0
- exonware/xwnode/edges/strategies/hnsw.py +637 -0
- exonware/xwnode/edges/strategies/hop2_labels.py +467 -0
- exonware/xwnode/edges/strategies/{edge_hyperedge_set.py → hyperedge_set.py} +2 -2
- exonware/xwnode/edges/strategies/incidence_matrix.py +250 -0
- exonware/xwnode/edges/strategies/k2_tree.py +613 -0
- exonware/xwnode/edges/strategies/link_cut.py +626 -0
- exonware/xwnode/edges/strategies/multiplex.py +532 -0
- exonware/xwnode/edges/strategies/{edge_neural_graph.py → neural_graph.py} +2 -2
- exonware/xwnode/edges/strategies/{edge_octree.py → octree.py} +69 -11
- exonware/xwnode/edges/strategies/{edge_quadtree.py → quadtree.py} +66 -10
- exonware/xwnode/edges/strategies/roaring_adj.py +438 -0
- exonware/xwnode/edges/strategies/{edge_rtree.py → rtree.py} +43 -5
- exonware/xwnode/edges/strategies/{edge_temporal_edgeset.py → temporal_edgeset.py} +24 -5
- exonware/xwnode/edges/strategies/{edge_tree_graph_basic.py → tree_graph_basic.py} +78 -7
- exonware/xwnode/edges/strategies/{edge_weighted_graph.py → weighted_graph.py} +188 -10
- exonware/xwnode/errors.py +3 -6
- exonware/xwnode/facade.py +20 -20
- exonware/xwnode/nodes/strategies/__init__.py +29 -9
- exonware/xwnode/nodes/strategies/adjacency_list.py +650 -177
- exonware/xwnode/nodes/strategies/aho_corasick.py +358 -183
- exonware/xwnode/nodes/strategies/array_list.py +36 -3
- exonware/xwnode/nodes/strategies/art.py +581 -0
- exonware/xwnode/nodes/strategies/{node_avl_tree.py → avl_tree.py} +77 -6
- exonware/xwnode/nodes/strategies/{node_b_plus_tree.py → b_plus_tree.py} +81 -40
- exonware/xwnode/nodes/strategies/{node_btree.py → b_tree.py} +79 -9
- exonware/xwnode/nodes/strategies/base.py +469 -98
- exonware/xwnode/nodes/strategies/{node_bitmap.py → bitmap.py} +12 -12
- exonware/xwnode/nodes/strategies/{node_bitset_dynamic.py → bitset_dynamic.py} +11 -11
- exonware/xwnode/nodes/strategies/{node_bloom_filter.py → bloom_filter.py} +15 -2
- exonware/xwnode/nodes/strategies/bloomier_filter.py +519 -0
- exonware/xwnode/nodes/strategies/bw_tree.py +531 -0
- exonware/xwnode/nodes/strategies/contracts.py +1 -1
- exonware/xwnode/nodes/strategies/{node_count_min_sketch.py → count_min_sketch.py} +3 -2
- exonware/xwnode/nodes/strategies/{node_cow_tree.py → cow_tree.py} +135 -13
- exonware/xwnode/nodes/strategies/crdt_map.py +629 -0
- exonware/xwnode/nodes/strategies/{node_cuckoo_hash.py → cuckoo_hash.py} +2 -2
- exonware/xwnode/nodes/strategies/{node_xdata_optimized.py → data_interchange_optimized.py} +21 -4
- exonware/xwnode/nodes/strategies/dawg.py +876 -0
- exonware/xwnode/nodes/strategies/deque.py +321 -153
- exonware/xwnode/nodes/strategies/extendible_hash.py +93 -0
- exonware/xwnode/nodes/strategies/{node_fenwick_tree.py → fenwick_tree.py} +111 -19
- exonware/xwnode/nodes/strategies/hamt.py +403 -0
- exonware/xwnode/nodes/strategies/hash_map.py +354 -67
- exonware/xwnode/nodes/strategies/heap.py +105 -5
- exonware/xwnode/nodes/strategies/hopscotch_hash.py +525 -0
- exonware/xwnode/nodes/strategies/{node_hyperloglog.py → hyperloglog.py} +6 -5
- exonware/xwnode/nodes/strategies/interval_tree.py +742 -0
- exonware/xwnode/nodes/strategies/kd_tree.py +703 -0
- exonware/xwnode/nodes/strategies/learned_index.py +533 -0
- exonware/xwnode/nodes/strategies/linear_hash.py +93 -0
- exonware/xwnode/nodes/strategies/linked_list.py +316 -119
- exonware/xwnode/nodes/strategies/{node_lsm_tree.py → lsm_tree.py} +219 -15
- exonware/xwnode/nodes/strategies/masstree.py +130 -0
- exonware/xwnode/nodes/strategies/{node_persistent_tree.py → persistent_tree.py} +149 -9
- exonware/xwnode/nodes/strategies/priority_queue.py +544 -132
- exonware/xwnode/nodes/strategies/queue.py +249 -120
- exonware/xwnode/nodes/strategies/{node_red_black_tree.py → red_black_tree.py} +183 -72
- exonware/xwnode/nodes/strategies/{node_roaring_bitmap.py → roaring_bitmap.py} +19 -6
- exonware/xwnode/nodes/strategies/rope.py +717 -0
- exonware/xwnode/nodes/strategies/{node_segment_tree.py → segment_tree.py} +106 -106
- exonware/xwnode/nodes/strategies/{node_set_hash.py → set_hash.py} +30 -29
- exonware/xwnode/nodes/strategies/{node_skip_list.py → skip_list.py} +74 -6
- exonware/xwnode/nodes/strategies/sparse_matrix.py +427 -131
- exonware/xwnode/nodes/strategies/{node_splay_tree.py → splay_tree.py} +55 -6
- exonware/xwnode/nodes/strategies/stack.py +244 -112
- exonware/xwnode/nodes/strategies/{node_suffix_array.py → suffix_array.py} +5 -1
- exonware/xwnode/nodes/strategies/t_tree.py +94 -0
- exonware/xwnode/nodes/strategies/{node_treap.py → treap.py} +75 -6
- exonware/xwnode/nodes/strategies/{node_tree_graph_hybrid.py → tree_graph_hybrid.py} +46 -5
- exonware/xwnode/nodes/strategies/trie.py +153 -9
- exonware/xwnode/nodes/strategies/union_find.py +111 -5
- exonware/xwnode/nodes/strategies/veb_tree.py +856 -0
- exonware/xwnode/strategies/__init__.py +5 -51
- exonware/xwnode/version.py +3 -3
- {exonware_xwnode-0.0.1.22.dist-info → exonware_xwnode-0.0.1.23.dist-info}/METADATA +23 -3
- exonware_xwnode-0.0.1.23.dist-info/RECORD +130 -0
- exonware/xwnode/edges/strategies/edge_adj_list.py +0 -353
- exonware/xwnode/edges/strategies/edge_adj_matrix.py +0 -445
- exonware/xwnode/nodes/strategies/_base_node.py +0 -307
- exonware/xwnode/nodes/strategies/node_aho_corasick.py +0 -525
- exonware/xwnode/nodes/strategies/node_array_list.py +0 -179
- exonware/xwnode/nodes/strategies/node_hash_map.py +0 -273
- exonware/xwnode/nodes/strategies/node_heap.py +0 -196
- exonware/xwnode/nodes/strategies/node_linked_list.py +0 -413
- exonware/xwnode/nodes/strategies/node_trie.py +0 -257
- exonware/xwnode/nodes/strategies/node_union_find.py +0 -192
- exonware/xwnode/queries/executors/__init__.py +0 -47
- exonware/xwnode/queries/executors/advanced/__init__.py +0 -37
- exonware/xwnode/queries/executors/advanced/aggregate_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/ask_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/construct_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/describe_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/for_loop_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/foreach_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/join_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/let_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/mutation_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/options_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/pipe_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/subscribe_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/subscription_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/union_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/window_executor.py +0 -51
- exonware/xwnode/queries/executors/advanced/with_cte_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/__init__.py +0 -21
- exonware/xwnode/queries/executors/aggregation/avg_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/count_executor.py +0 -38
- exonware/xwnode/queries/executors/aggregation/distinct_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/group_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/having_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/max_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/min_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/sum_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/summarize_executor.py +0 -50
- exonware/xwnode/queries/executors/array/__init__.py +0 -9
- exonware/xwnode/queries/executors/array/indexing_executor.py +0 -51
- exonware/xwnode/queries/executors/array/slicing_executor.py +0 -51
- exonware/xwnode/queries/executors/base.py +0 -257
- exonware/xwnode/queries/executors/capability_checker.py +0 -204
- exonware/xwnode/queries/executors/contracts.py +0 -166
- exonware/xwnode/queries/executors/core/__init__.py +0 -17
- exonware/xwnode/queries/executors/core/create_executor.py +0 -96
- exonware/xwnode/queries/executors/core/delete_executor.py +0 -99
- exonware/xwnode/queries/executors/core/drop_executor.py +0 -100
- exonware/xwnode/queries/executors/core/insert_executor.py +0 -39
- exonware/xwnode/queries/executors/core/select_executor.py +0 -152
- exonware/xwnode/queries/executors/core/update_executor.py +0 -102
- exonware/xwnode/queries/executors/data/__init__.py +0 -13
- exonware/xwnode/queries/executors/data/alter_executor.py +0 -50
- exonware/xwnode/queries/executors/data/load_executor.py +0 -50
- exonware/xwnode/queries/executors/data/merge_executor.py +0 -50
- exonware/xwnode/queries/executors/data/store_executor.py +0 -50
- exonware/xwnode/queries/executors/defs.py +0 -93
- exonware/xwnode/queries/executors/engine.py +0 -221
- exonware/xwnode/queries/executors/errors.py +0 -68
- exonware/xwnode/queries/executors/filtering/__init__.py +0 -25
- exonware/xwnode/queries/executors/filtering/between_executor.py +0 -80
- exonware/xwnode/queries/executors/filtering/filter_executor.py +0 -79
- exonware/xwnode/queries/executors/filtering/has_executor.py +0 -70
- exonware/xwnode/queries/executors/filtering/in_executor.py +0 -70
- exonware/xwnode/queries/executors/filtering/like_executor.py +0 -76
- exonware/xwnode/queries/executors/filtering/optional_executor.py +0 -76
- exonware/xwnode/queries/executors/filtering/range_executor.py +0 -80
- exonware/xwnode/queries/executors/filtering/term_executor.py +0 -77
- exonware/xwnode/queries/executors/filtering/values_executor.py +0 -71
- exonware/xwnode/queries/executors/filtering/where_executor.py +0 -44
- exonware/xwnode/queries/executors/graph/__init__.py +0 -15
- exonware/xwnode/queries/executors/graph/in_traverse_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/match_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/out_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/path_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/return_executor.py +0 -51
- exonware/xwnode/queries/executors/ordering/__init__.py +0 -9
- exonware/xwnode/queries/executors/ordering/by_executor.py +0 -50
- exonware/xwnode/queries/executors/ordering/order_executor.py +0 -51
- exonware/xwnode/queries/executors/projection/__init__.py +0 -9
- exonware/xwnode/queries/executors/projection/extend_executor.py +0 -50
- exonware/xwnode/queries/executors/projection/project_executor.py +0 -50
- exonware/xwnode/queries/executors/registry.py +0 -173
- exonware/xwnode/queries/parsers/__init__.py +0 -26
- exonware/xwnode/queries/parsers/base.py +0 -86
- exonware/xwnode/queries/parsers/contracts.py +0 -46
- exonware/xwnode/queries/parsers/errors.py +0 -53
- exonware/xwnode/queries/parsers/sql_param_extractor.py +0 -318
- exonware/xwnode/queries/strategies/__init__.py +0 -24
- exonware/xwnode/queries/strategies/base.py +0 -236
- exonware/xwnode/queries/strategies/cql.py +0 -201
- exonware/xwnode/queries/strategies/cypher.py +0 -181
- exonware/xwnode/queries/strategies/datalog.py +0 -70
- exonware/xwnode/queries/strategies/elastic_dsl.py +0 -70
- exonware/xwnode/queries/strategies/eql.py +0 -70
- exonware/xwnode/queries/strategies/flux.py +0 -70
- exonware/xwnode/queries/strategies/gql.py +0 -70
- exonware/xwnode/queries/strategies/graphql.py +0 -240
- exonware/xwnode/queries/strategies/gremlin.py +0 -181
- exonware/xwnode/queries/strategies/hiveql.py +0 -214
- exonware/xwnode/queries/strategies/hql.py +0 -70
- exonware/xwnode/queries/strategies/jmespath.py +0 -219
- exonware/xwnode/queries/strategies/jq.py +0 -66
- exonware/xwnode/queries/strategies/json_query.py +0 -66
- exonware/xwnode/queries/strategies/jsoniq.py +0 -248
- exonware/xwnode/queries/strategies/kql.py +0 -70
- exonware/xwnode/queries/strategies/linq.py +0 -238
- exonware/xwnode/queries/strategies/logql.py +0 -70
- exonware/xwnode/queries/strategies/mql.py +0 -68
- exonware/xwnode/queries/strategies/n1ql.py +0 -210
- exonware/xwnode/queries/strategies/partiql.py +0 -70
- exonware/xwnode/queries/strategies/pig.py +0 -215
- exonware/xwnode/queries/strategies/promql.py +0 -70
- exonware/xwnode/queries/strategies/sparql.py +0 -220
- exonware/xwnode/queries/strategies/sql.py +0 -275
- exonware/xwnode/queries/strategies/xml_query.py +0 -66
- exonware/xwnode/queries/strategies/xpath.py +0 -223
- exonware/xwnode/queries/strategies/xquery.py +0 -258
- exonware/xwnode/queries/strategies/xwnode_executor.py +0 -332
- exonware/xwnode/queries/strategies/xwquery.py +0 -456
- exonware_xwnode-0.0.1.22.dist-info/RECORD +0 -214
- /exonware/xwnode/nodes/strategies/{node_ordered_map.py → ordered_map.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_ordered_map_balanced.py → ordered_map_balanced.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_patricia.py → patricia.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_radix_trie.py → radix_trie.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_set_tree.py → set_tree.py} +0 -0
- {exonware_xwnode-0.0.1.22.dist-info → exonware_xwnode-0.0.1.23.dist-info}/WHEEL +0 -0
- {exonware_xwnode-0.0.1.22.dist-info → exonware_xwnode-0.0.1.23.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,856 @@
|
|
1
|
+
"""
|
2
|
+
#exonware/xwnode/src/exonware/xwnode/nodes/strategies/veb_tree.py
|
3
|
+
|
4
|
+
van Emde Boas Tree Node Strategy Implementation
|
5
|
+
|
6
|
+
This module implements the VEB_TREE strategy for O(log log U) operations
|
7
|
+
on fixed-universe integer keys.
|
8
|
+
|
9
|
+
Company: eXonware.com
|
10
|
+
Author: Eng. Muhammad AlShehri
|
11
|
+
Email: connect@exonware.com
|
12
|
+
Version: 0.0.1.23
|
13
|
+
Generation Date: 12-Oct-2025
|
14
|
+
"""
|
15
|
+
|
16
|
+
import math
|
17
|
+
from typing import Any, Iterator, List, Dict, Optional, Set
|
18
|
+
from .base import ANodeTreeStrategy
|
19
|
+
from .contracts import NodeType
|
20
|
+
from ...defs import NodeMode, NodeTrait
|
21
|
+
from ...errors import XWNodeError, XWNodeValueError
|
22
|
+
|
23
|
+
|
24
|
+
class VebNode:
|
25
|
+
"""
|
26
|
+
Node in the van Emde Boas tree structure.
|
27
|
+
|
28
|
+
WHY recursive structure:
|
29
|
+
- Enables O(log log U) time complexity through universe splitting
|
30
|
+
- Each cluster handles √U elements recursively
|
31
|
+
- Summary structure provides constant-time minimum/maximum
|
32
|
+
"""
|
33
|
+
|
34
|
+
def __init__(self, universe_size: int):
|
35
|
+
"""
|
36
|
+
Initialize vEB node.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
universe_size: Size of universe (must be power of 2)
|
40
|
+
"""
|
41
|
+
self.universe_size = universe_size
|
42
|
+
self.min = None
|
43
|
+
self.max = None
|
44
|
+
|
45
|
+
# Base case: universe size 2
|
46
|
+
if universe_size <= 2:
|
47
|
+
self.summary = None
|
48
|
+
self.clusters = None
|
49
|
+
else:
|
50
|
+
# Recursive case - for power of 2, use exact sqrt
|
51
|
+
# For universe_size = 2^k, sqrt = 2^(k/2)
|
52
|
+
lower_sqrt = 1 << (universe_size.bit_length() // 2) # Power of 2 sqrt
|
53
|
+
upper_sqrt = (universe_size + lower_sqrt - 1) // lower_sqrt # Ceiling division
|
54
|
+
|
55
|
+
# Summary structure for non-empty clusters
|
56
|
+
self.summary = VebNode(upper_sqrt)
|
57
|
+
|
58
|
+
# Array of cluster nodes (one per possible cluster)
|
59
|
+
self.clusters = [None] * upper_sqrt
|
60
|
+
|
61
|
+
def is_empty(self) -> bool:
|
62
|
+
"""Check if tree is empty."""
|
63
|
+
return self.min is None
|
64
|
+
|
65
|
+
def high(self, x: int) -> int:
|
66
|
+
"""Get high-order bits (cluster index)."""
|
67
|
+
lower_sqrt = 1 << (self.universe_size.bit_length() // 2)
|
68
|
+
return x // lower_sqrt
|
69
|
+
|
70
|
+
def low(self, x: int) -> int:
|
71
|
+
"""Get low-order bits (position in cluster)."""
|
72
|
+
lower_sqrt = 1 << (self.universe_size.bit_length() // 2)
|
73
|
+
return x % lower_sqrt
|
74
|
+
|
75
|
+
def index(self, high: int, low: int) -> int:
|
76
|
+
"""Combine high and low to get original index."""
|
77
|
+
lower_sqrt = 1 << (self.universe_size.bit_length() // 2)
|
78
|
+
return high * lower_sqrt + low
|
79
|
+
|
80
|
+
|
81
|
+
class VebTreeStrategy(ANodeTreeStrategy):
|
82
|
+
"""
|
83
|
+
van Emde Boas Tree strategy for O(log log U) integer operations.
|
84
|
+
|
85
|
+
WHY van Emde Boas Tree:
|
86
|
+
- Asymptotically faster than balanced BSTs: O(log log U) vs O(log n)
|
87
|
+
- Predictable performance for fixed-universe integers
|
88
|
+
- Excellent for routing tables, IP address lookups, priority queues
|
89
|
+
- Constant-time min/max queries through summary caching
|
90
|
+
- Ideal for small-universe scenarios (16-bit, 32-bit integers)
|
91
|
+
|
92
|
+
WHY this implementation:
|
93
|
+
- Recursive cluster decomposition enables logarithmic speedup
|
94
|
+
- Min/max caching at each level for O(1) extreme queries
|
95
|
+
- Summary structure enables fast successor/predecessor
|
96
|
+
- Lazy cluster initialization saves memory for sparse data
|
97
|
+
- Direct indexing eliminates pointer indirection overhead
|
98
|
+
|
99
|
+
Time Complexity:
|
100
|
+
- Insert: O(log log U) where U is universe size
|
101
|
+
- Delete: O(log log U)
|
102
|
+
- Search: O(log log U)
|
103
|
+
- Min/Max: O(1) through caching
|
104
|
+
- Successor: O(log log U)
|
105
|
+
- Predecessor: O(log log U)
|
106
|
+
|
107
|
+
Space Complexity: O(U) worst case, O(n) for n elements with lazy allocation
|
108
|
+
|
109
|
+
Trade-offs:
|
110
|
+
- Advantage: Faster than BST for small universes (log log U << log n)
|
111
|
+
- Advantage: Constant-time min/max queries
|
112
|
+
- Advantage: Excellent for dense integer keys
|
113
|
+
- Limitation: High space overhead O(U) for sparse data
|
114
|
+
- Limitation: Universe size must be power of 2
|
115
|
+
- Limitation: Integer keys only (no strings, floats)
|
116
|
+
- Compared to HashMap: Better ordered operations, worse space
|
117
|
+
- Compared to B-Tree: Faster for small universes, worse for large
|
118
|
+
|
119
|
+
Best for:
|
120
|
+
- Routing tables with IP addresses (32-bit universe)
|
121
|
+
- Priority queues with small integer priorities
|
122
|
+
- Network switches and routers
|
123
|
+
- Small-universe integer key-value stores
|
124
|
+
- Real-time systems requiring predictable performance
|
125
|
+
- Dense integer key distributions
|
126
|
+
|
127
|
+
Not recommended for:
|
128
|
+
- Large universes (2^64) - excessive memory
|
129
|
+
- Sparse data (few elements, large universe)
|
130
|
+
- String or floating-point keys
|
131
|
+
- Dynamic universe size requirements
|
132
|
+
- Memory-constrained environments
|
133
|
+
- Non-integer key types
|
134
|
+
|
135
|
+
Following eXonware Priorities:
|
136
|
+
1. Security: Validates universe bounds, prevents overflow attacks
|
137
|
+
2. Usability: Simple API for integer operations, clear errors
|
138
|
+
3. Maintainability: Clean recursive structure, well-documented
|
139
|
+
4. Performance: O(log log U) operations, optimal for use case
|
140
|
+
5. Extensibility: Easy to add weighted variants or range queries
|
141
|
+
|
142
|
+
Industry Best Practices:
|
143
|
+
- Follows van Emde Boas original paper (1975)
|
144
|
+
- Implements lazy cluster allocation for memory efficiency
|
145
|
+
- Uses power-of-2 universe sizes for optimal splitting
|
146
|
+
- Provides min/max caching for constant-time queries
|
147
|
+
- Supports both membership and key-value storage
|
148
|
+
"""
|
149
|
+
|
150
|
+
# Tree node type for classification
|
151
|
+
STRATEGY_TYPE: NodeType = NodeType.TREE
|
152
|
+
|
153
|
+
def __init__(self, mode: NodeMode = NodeMode.VEB_TREE,
|
154
|
+
traits: NodeTrait = NodeTrait.NONE,
|
155
|
+
universe_size: int = 65536, **options):
|
156
|
+
"""
|
157
|
+
Initialize van Emde Boas tree strategy.
|
158
|
+
|
159
|
+
Args:
|
160
|
+
mode: Node mode (VEB_TREE)
|
161
|
+
traits: Node traits
|
162
|
+
universe_size: Maximum key value (must be power of 2)
|
163
|
+
**options: Additional options
|
164
|
+
|
165
|
+
Raises:
|
166
|
+
XWNodeValueError: If universe_size is not power of 2
|
167
|
+
"""
|
168
|
+
# Validate universe size is power of 2
|
169
|
+
if universe_size <= 0 or (universe_size & (universe_size - 1)) != 0:
|
170
|
+
raise XWNodeValueError(
|
171
|
+
f"Universe size must be a power of 2, got {universe_size}"
|
172
|
+
)
|
173
|
+
|
174
|
+
super().__init__(mode, traits, **options)
|
175
|
+
|
176
|
+
self.universe_size = universe_size
|
177
|
+
self._root = VebNode(universe_size)
|
178
|
+
self._values: Dict[int, Any] = {} # Store actual values
|
179
|
+
self._size = 0
|
180
|
+
|
181
|
+
def get_supported_traits(self) -> NodeTrait:
|
182
|
+
"""Get supported traits."""
|
183
|
+
return NodeTrait.ORDERED | NodeTrait.INDEXED | NodeTrait.FAST_INSERT | NodeTrait.FAST_DELETE
|
184
|
+
|
185
|
+
# ============================================================================
|
186
|
+
# CORE VEB OPERATIONS
|
187
|
+
# ============================================================================
|
188
|
+
|
189
|
+
def _veb_insert(self, node: VebNode, x: int) -> None:
|
190
|
+
"""
|
191
|
+
Insert element into vEB tree.
|
192
|
+
|
193
|
+
Args:
|
194
|
+
node: Current vEB node
|
195
|
+
x: Integer key to insert
|
196
|
+
"""
|
197
|
+
# Security: Validate bounds
|
198
|
+
if x < 0 or x >= node.universe_size:
|
199
|
+
raise XWNodeValueError(
|
200
|
+
f"Key {x} out of bounds [0, {node.universe_size})"
|
201
|
+
)
|
202
|
+
|
203
|
+
# Empty tree case
|
204
|
+
if node.is_empty():
|
205
|
+
node.min = node.max = x
|
206
|
+
return
|
207
|
+
|
208
|
+
# Ensure x is not already min or max
|
209
|
+
if x == node.min or x == node.max:
|
210
|
+
return # Already present
|
211
|
+
|
212
|
+
# Swap to ensure min is minimum
|
213
|
+
if x < node.min:
|
214
|
+
x, node.min = node.min, x
|
215
|
+
|
216
|
+
# Base case
|
217
|
+
if node.universe_size == 2:
|
218
|
+
node.max = max(x, node.max) if node.max is not None else x
|
219
|
+
return
|
220
|
+
|
221
|
+
# Recursive case
|
222
|
+
high = node.high(x)
|
223
|
+
low = node.low(x)
|
224
|
+
|
225
|
+
# Lazy cluster creation
|
226
|
+
if node.clusters[high] is None:
|
227
|
+
lower_sqrt = 1 << (node.universe_size.bit_length() // 2)
|
228
|
+
node.clusters[high] = VebNode(lower_sqrt)
|
229
|
+
|
230
|
+
# If cluster is empty, update summary
|
231
|
+
if node.clusters[high].is_empty():
|
232
|
+
self._veb_insert(node.summary, high)
|
233
|
+
|
234
|
+
# Insert into cluster
|
235
|
+
self._veb_insert(node.clusters[high], low)
|
236
|
+
|
237
|
+
# Update max if necessary
|
238
|
+
if x > node.max:
|
239
|
+
node.max = x
|
240
|
+
|
241
|
+
def _veb_delete(self, node: VebNode, x: int) -> bool:
|
242
|
+
"""
|
243
|
+
Delete element from vEB tree.
|
244
|
+
|
245
|
+
Args:
|
246
|
+
node: Current vEB node
|
247
|
+
x: Integer key to delete
|
248
|
+
|
249
|
+
Returns:
|
250
|
+
True if deleted, False if not found
|
251
|
+
"""
|
252
|
+
# Security: Validate bounds
|
253
|
+
if x < 0 or x >= node.universe_size:
|
254
|
+
return False
|
255
|
+
|
256
|
+
# Element not in tree
|
257
|
+
if node.is_empty() or x < node.min or x > node.max:
|
258
|
+
return False
|
259
|
+
|
260
|
+
# Single element case
|
261
|
+
if node.min == node.max:
|
262
|
+
if x == node.min:
|
263
|
+
node.min = node.max = None
|
264
|
+
return True
|
265
|
+
return False
|
266
|
+
|
267
|
+
# Base case: universe size 2
|
268
|
+
if node.universe_size == 2:
|
269
|
+
if x == 0:
|
270
|
+
node.min = 1
|
271
|
+
else:
|
272
|
+
node.max = 0
|
273
|
+
return True
|
274
|
+
|
275
|
+
# Recursive case
|
276
|
+
# If deleting min, replace with successor
|
277
|
+
if x == node.min:
|
278
|
+
first_cluster = node.summary.min
|
279
|
+
if first_cluster is None:
|
280
|
+
# No other elements
|
281
|
+
node.min = node.max
|
282
|
+
return True
|
283
|
+
|
284
|
+
x = node.index(first_cluster, node.clusters[first_cluster].min)
|
285
|
+
node.min = x
|
286
|
+
|
287
|
+
# Delete from appropriate cluster
|
288
|
+
high = node.high(x)
|
289
|
+
low = node.low(x)
|
290
|
+
|
291
|
+
if node.clusters[high] is None:
|
292
|
+
return False
|
293
|
+
|
294
|
+
deleted = self._veb_delete(node.clusters[high], low)
|
295
|
+
|
296
|
+
if not deleted:
|
297
|
+
return False
|
298
|
+
|
299
|
+
# Update summary if cluster becomes empty
|
300
|
+
if node.clusters[high].is_empty():
|
301
|
+
self._veb_delete(node.summary, high)
|
302
|
+
|
303
|
+
# Update max if we deleted it
|
304
|
+
if x == node.max:
|
305
|
+
if node.summary.is_empty():
|
306
|
+
node.max = node.min
|
307
|
+
else:
|
308
|
+
max_cluster = node.summary.max
|
309
|
+
node.max = node.index(max_cluster, node.clusters[max_cluster].max)
|
310
|
+
elif x == node.max:
|
311
|
+
# Update max within same cluster
|
312
|
+
node.max = node.index(high, node.clusters[high].max)
|
313
|
+
|
314
|
+
return True
|
315
|
+
|
316
|
+
def _veb_member(self, node: VebNode, x: int) -> bool:
|
317
|
+
"""
|
318
|
+
Check membership in vEB tree.
|
319
|
+
|
320
|
+
Args:
|
321
|
+
node: Current vEB node
|
322
|
+
x: Integer key to check
|
323
|
+
|
324
|
+
Returns:
|
325
|
+
True if present, False otherwise
|
326
|
+
"""
|
327
|
+
# Security: Validate bounds
|
328
|
+
if x < 0 or x >= node.universe_size:
|
329
|
+
return False
|
330
|
+
|
331
|
+
# Check cached min/max
|
332
|
+
if x == node.min or x == node.max:
|
333
|
+
return True
|
334
|
+
|
335
|
+
# Empty or out of range
|
336
|
+
if node.is_empty() or x < node.min or x > node.max:
|
337
|
+
return False
|
338
|
+
|
339
|
+
# Base case
|
340
|
+
if node.universe_size == 2:
|
341
|
+
return False
|
342
|
+
|
343
|
+
# Recursive case
|
344
|
+
high = node.high(x)
|
345
|
+
low = node.low(x)
|
346
|
+
|
347
|
+
if node.clusters[high] is None:
|
348
|
+
return False
|
349
|
+
|
350
|
+
return self._veb_member(node.clusters[high], low)
|
351
|
+
|
352
|
+
def _veb_successor(self, node: VebNode, x: int) -> Optional[int]:
|
353
|
+
"""
|
354
|
+
Find successor of x (smallest element > x).
|
355
|
+
|
356
|
+
Args:
|
357
|
+
node: Current vEB node
|
358
|
+
x: Integer key
|
359
|
+
|
360
|
+
Returns:
|
361
|
+
Successor key or None if no successor exists
|
362
|
+
"""
|
363
|
+
# Security: Validate bounds
|
364
|
+
if x < 0 or x >= node.universe_size:
|
365
|
+
return None
|
366
|
+
|
367
|
+
# Base case
|
368
|
+
if node.universe_size == 2:
|
369
|
+
if x == 0 and node.max == 1:
|
370
|
+
return 1
|
371
|
+
return None
|
372
|
+
|
373
|
+
# If x < min, min is successor
|
374
|
+
if not node.is_empty() and x < node.min:
|
375
|
+
return node.min
|
376
|
+
|
377
|
+
# Check within same cluster
|
378
|
+
high = node.high(x)
|
379
|
+
low = node.low(x)
|
380
|
+
|
381
|
+
if node.clusters[high] is not None and not node.clusters[high].is_empty():
|
382
|
+
if low < node.clusters[high].max:
|
383
|
+
offset = self._veb_successor(node.clusters[high], low)
|
384
|
+
if offset is not None:
|
385
|
+
return node.index(high, offset)
|
386
|
+
|
387
|
+
# Find next non-empty cluster
|
388
|
+
succ_cluster = self._veb_successor(node.summary, high)
|
389
|
+
if succ_cluster is None:
|
390
|
+
return None
|
391
|
+
|
392
|
+
if node.clusters[succ_cluster] is None:
|
393
|
+
return None
|
394
|
+
|
395
|
+
offset = node.clusters[succ_cluster].min
|
396
|
+
return node.index(succ_cluster, offset)
|
397
|
+
|
398
|
+
def _veb_predecessor(self, node: VebNode, x: int) -> Optional[int]:
|
399
|
+
"""
|
400
|
+
Find predecessor of x (largest element < x).
|
401
|
+
|
402
|
+
Args:
|
403
|
+
node: Current vEB node
|
404
|
+
x: Integer key
|
405
|
+
|
406
|
+
Returns:
|
407
|
+
Predecessor key or None if no predecessor exists
|
408
|
+
"""
|
409
|
+
# Security: Validate bounds
|
410
|
+
if x < 0 or x >= node.universe_size:
|
411
|
+
return None
|
412
|
+
|
413
|
+
# Base case
|
414
|
+
if node.universe_size == 2:
|
415
|
+
if x == 1 and node.min == 0:
|
416
|
+
return 0
|
417
|
+
return None
|
418
|
+
|
419
|
+
# If x > max, max is predecessor
|
420
|
+
if not node.is_empty() and x > node.max:
|
421
|
+
return node.max
|
422
|
+
|
423
|
+
# Check within same cluster
|
424
|
+
high = node.high(x)
|
425
|
+
low = node.low(x)
|
426
|
+
|
427
|
+
if node.clusters[high] is not None and not node.clusters[high].is_empty():
|
428
|
+
if low > node.clusters[high].min:
|
429
|
+
offset = self._veb_predecessor(node.clusters[high], low)
|
430
|
+
if offset is not None:
|
431
|
+
return node.index(high, offset)
|
432
|
+
|
433
|
+
# Find previous non-empty cluster
|
434
|
+
pred_cluster = self._veb_predecessor(node.summary, high)
|
435
|
+
if pred_cluster is None:
|
436
|
+
# Check if min is predecessor
|
437
|
+
if not node.is_empty() and x > node.min:
|
438
|
+
return node.min
|
439
|
+
return None
|
440
|
+
|
441
|
+
if node.clusters[pred_cluster] is None:
|
442
|
+
return None
|
443
|
+
|
444
|
+
offset = node.clusters[pred_cluster].max
|
445
|
+
return node.index(pred_cluster, offset)
|
446
|
+
|
447
|
+
# ============================================================================
|
448
|
+
# STRATEGY INTERFACE IMPLEMENTATION
|
449
|
+
# ============================================================================
|
450
|
+
|
451
|
+
def put(self, key: Any, value: Any = None) -> None:
|
452
|
+
"""
|
453
|
+
Store key-value pair.
|
454
|
+
|
455
|
+
Args:
|
456
|
+
key: Integer key (0 to universe_size-1)
|
457
|
+
value: Associated value
|
458
|
+
|
459
|
+
Raises:
|
460
|
+
XWNodeValueError: If key is not an integer or out of bounds
|
461
|
+
"""
|
462
|
+
# Security: Type validation
|
463
|
+
if not isinstance(key, int):
|
464
|
+
raise XWNodeValueError(
|
465
|
+
f"vEB tree requires integer keys, got {type(key).__name__}"
|
466
|
+
)
|
467
|
+
|
468
|
+
# Security: Bounds validation
|
469
|
+
if key < 0 or key >= self.universe_size:
|
470
|
+
raise XWNodeValueError(
|
471
|
+
f"Key {key} out of universe bounds [0, {self.universe_size})"
|
472
|
+
)
|
473
|
+
|
474
|
+
# Insert into vEB structure
|
475
|
+
was_present = self._veb_member(self._root, key)
|
476
|
+
self._veb_insert(self._root, key)
|
477
|
+
|
478
|
+
# Store value
|
479
|
+
self._values[key] = value
|
480
|
+
|
481
|
+
if not was_present:
|
482
|
+
self._size += 1
|
483
|
+
|
484
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
485
|
+
"""
|
486
|
+
Retrieve value by key.
|
487
|
+
|
488
|
+
Args:
|
489
|
+
key: Integer key
|
490
|
+
default: Default value if not found
|
491
|
+
|
492
|
+
Returns:
|
493
|
+
Value or default
|
494
|
+
"""
|
495
|
+
# Type check
|
496
|
+
if not isinstance(key, int):
|
497
|
+
return default
|
498
|
+
|
499
|
+
# Bounds check
|
500
|
+
if key < 0 or key >= self.universe_size:
|
501
|
+
return default
|
502
|
+
|
503
|
+
# Check membership
|
504
|
+
if not self._veb_member(self._root, key):
|
505
|
+
return default
|
506
|
+
|
507
|
+
return self._values.get(key, default)
|
508
|
+
|
509
|
+
def has(self, key: Any) -> bool:
|
510
|
+
"""
|
511
|
+
Check if key exists.
|
512
|
+
|
513
|
+
Args:
|
514
|
+
key: Integer key
|
515
|
+
|
516
|
+
Returns:
|
517
|
+
True if exists, False otherwise
|
518
|
+
"""
|
519
|
+
if not isinstance(key, int):
|
520
|
+
return False
|
521
|
+
|
522
|
+
if key < 0 or key >= self.universe_size:
|
523
|
+
return False
|
524
|
+
|
525
|
+
return self._veb_member(self._root, key)
|
526
|
+
|
527
|
+
def delete(self, key: Any) -> bool:
|
528
|
+
"""
|
529
|
+
Remove key-value pair.
|
530
|
+
|
531
|
+
Args:
|
532
|
+
key: Integer key
|
533
|
+
|
534
|
+
Returns:
|
535
|
+
True if deleted, False if not found
|
536
|
+
"""
|
537
|
+
if not isinstance(key, int):
|
538
|
+
return False
|
539
|
+
|
540
|
+
if key < 0 or key >= self.universe_size:
|
541
|
+
return False
|
542
|
+
|
543
|
+
# Delete from vEB structure
|
544
|
+
deleted = self._veb_delete(self._root, key)
|
545
|
+
|
546
|
+
if deleted:
|
547
|
+
if key in self._values:
|
548
|
+
del self._values[key]
|
549
|
+
self._size -= 1
|
550
|
+
|
551
|
+
return deleted
|
552
|
+
|
553
|
+
def keys(self) -> Iterator[Any]:
|
554
|
+
"""
|
555
|
+
Get iterator over all keys in sorted order.
|
556
|
+
|
557
|
+
Returns:
|
558
|
+
Iterator of keys
|
559
|
+
"""
|
560
|
+
if self._root.is_empty():
|
561
|
+
return
|
562
|
+
|
563
|
+
# Start with minimum
|
564
|
+
current = self._root.min
|
565
|
+
while current is not None:
|
566
|
+
yield current
|
567
|
+
current = self._veb_successor(self._root, current)
|
568
|
+
|
569
|
+
def values(self) -> Iterator[Any]:
|
570
|
+
"""
|
571
|
+
Get iterator over all values in key-sorted order.
|
572
|
+
|
573
|
+
Returns:
|
574
|
+
Iterator of values
|
575
|
+
"""
|
576
|
+
for key in self.keys():
|
577
|
+
yield self._values.get(key)
|
578
|
+
|
579
|
+
def items(self) -> Iterator[tuple[Any, Any]]:
|
580
|
+
"""
|
581
|
+
Get iterator over all key-value pairs in sorted order.
|
582
|
+
|
583
|
+
Returns:
|
584
|
+
Iterator of (key, value) tuples
|
585
|
+
"""
|
586
|
+
for key in self.keys():
|
587
|
+
yield (key, self._values.get(key))
|
588
|
+
|
589
|
+
def __len__(self) -> int:
|
590
|
+
"""Get number of elements."""
|
591
|
+
return self._size
|
592
|
+
|
593
|
+
def to_native(self) -> Any:
|
594
|
+
"""
|
595
|
+
Convert to native Python dict.
|
596
|
+
|
597
|
+
Returns:
|
598
|
+
Dictionary representation
|
599
|
+
"""
|
600
|
+
return dict(self.items())
|
601
|
+
|
602
|
+
# ============================================================================
|
603
|
+
# VEB-SPECIFIC OPERATIONS
|
604
|
+
# ============================================================================
|
605
|
+
|
606
|
+
def get_min(self) -> Optional[int]:
|
607
|
+
"""
|
608
|
+
Get minimum key in O(1) time.
|
609
|
+
|
610
|
+
Returns:
|
611
|
+
Minimum key or None if empty
|
612
|
+
"""
|
613
|
+
return self._root.min
|
614
|
+
|
615
|
+
def get_max(self) -> Optional[int]:
|
616
|
+
"""
|
617
|
+
Get maximum key in O(1) time.
|
618
|
+
|
619
|
+
Returns:
|
620
|
+
Maximum key or None if empty
|
621
|
+
"""
|
622
|
+
return self._root.max
|
623
|
+
|
624
|
+
def successor(self, key: int) -> Optional[int]:
|
625
|
+
"""
|
626
|
+
Find successor of key (smallest key > given key).
|
627
|
+
|
628
|
+
Args:
|
629
|
+
key: Integer key
|
630
|
+
|
631
|
+
Returns:
|
632
|
+
Successor key or None
|
633
|
+
|
634
|
+
Raises:
|
635
|
+
XWNodeValueError: If key is invalid
|
636
|
+
"""
|
637
|
+
if not isinstance(key, int):
|
638
|
+
raise XWNodeValueError(f"vEB tree requires integer keys")
|
639
|
+
|
640
|
+
if key < 0 or key >= self.universe_size:
|
641
|
+
raise XWNodeValueError(
|
642
|
+
f"Key {key} out of universe bounds [0, {self.universe_size})"
|
643
|
+
)
|
644
|
+
|
645
|
+
return self._veb_successor(self._root, key)
|
646
|
+
|
647
|
+
def predecessor(self, key: int) -> Optional[int]:
|
648
|
+
"""
|
649
|
+
Find predecessor of key (largest key < given key).
|
650
|
+
|
651
|
+
Args:
|
652
|
+
key: Integer key
|
653
|
+
|
654
|
+
Returns:
|
655
|
+
Predecessor key or None
|
656
|
+
|
657
|
+
Raises:
|
658
|
+
XWNodeValueError: If key is invalid
|
659
|
+
"""
|
660
|
+
if not isinstance(key, int):
|
661
|
+
raise XWNodeValueError(f"vEB tree requires integer keys")
|
662
|
+
|
663
|
+
if key < 0 or key >= self.universe_size:
|
664
|
+
raise XWNodeValueError(
|
665
|
+
f"Key {key} out of universe bounds [0, {self.universe_size})"
|
666
|
+
)
|
667
|
+
|
668
|
+
return self._veb_predecessor(self._root, key)
|
669
|
+
|
670
|
+
def range_query(self, low: int, high: int) -> List[tuple[int, Any]]:
|
671
|
+
"""
|
672
|
+
Find all keys in range [low, high].
|
673
|
+
|
674
|
+
Args:
|
675
|
+
low: Lower bound (inclusive)
|
676
|
+
high: Upper bound (inclusive)
|
677
|
+
|
678
|
+
Returns:
|
679
|
+
List of (key, value) pairs in range
|
680
|
+
|
681
|
+
Raises:
|
682
|
+
XWNodeValueError: If bounds are invalid
|
683
|
+
"""
|
684
|
+
# Security: Validate inputs
|
685
|
+
if not isinstance(low, int) or not isinstance(high, int):
|
686
|
+
raise XWNodeValueError("Range bounds must be integers")
|
687
|
+
|
688
|
+
if low < 0 or high >= self.universe_size or low > high:
|
689
|
+
raise XWNodeValueError(
|
690
|
+
f"Invalid range [{low}, {high}] for universe [0, {self.universe_size})"
|
691
|
+
)
|
692
|
+
|
693
|
+
result = []
|
694
|
+
|
695
|
+
# Find first key >= low
|
696
|
+
if self._veb_member(self._root, low):
|
697
|
+
current = low
|
698
|
+
else:
|
699
|
+
current = self._veb_successor(self._root, low)
|
700
|
+
|
701
|
+
# Collect all keys up to high
|
702
|
+
while current is not None and current <= high:
|
703
|
+
result.append((current, self._values.get(current)))
|
704
|
+
current = self._veb_successor(self._root, current)
|
705
|
+
|
706
|
+
return result
|
707
|
+
|
708
|
+
# ============================================================================
|
709
|
+
# PERFORMANCE METHODS
|
710
|
+
# ============================================================================
|
711
|
+
|
712
|
+
def get_depth(self) -> int:
|
713
|
+
"""
|
714
|
+
Get tree depth.
|
715
|
+
|
716
|
+
Returns:
|
717
|
+
Depth of recursion (log log U)
|
718
|
+
"""
|
719
|
+
depth = 0
|
720
|
+
u = self.universe_size
|
721
|
+
while u > 2:
|
722
|
+
u = int(math.sqrt(u))
|
723
|
+
depth += 1
|
724
|
+
return depth
|
725
|
+
|
726
|
+
def get_memory_usage(self) -> Dict[str, Any]:
|
727
|
+
"""
|
728
|
+
Estimate memory usage.
|
729
|
+
|
730
|
+
Returns:
|
731
|
+
Memory usage statistics
|
732
|
+
"""
|
733
|
+
def count_nodes(node: VebNode) -> int:
|
734
|
+
"""Recursively count allocated nodes."""
|
735
|
+
if node is None:
|
736
|
+
return 0
|
737
|
+
|
738
|
+
count = 1
|
739
|
+
if node.summary is not None:
|
740
|
+
count += count_nodes(node.summary)
|
741
|
+
|
742
|
+
if node.clusters is not None:
|
743
|
+
for cluster in node.clusters:
|
744
|
+
if cluster is not None:
|
745
|
+
count += count_nodes(cluster)
|
746
|
+
|
747
|
+
return count
|
748
|
+
|
749
|
+
node_count = count_nodes(self._root)
|
750
|
+
|
751
|
+
return {
|
752
|
+
'allocated_nodes': node_count,
|
753
|
+
'universe_size': self.universe_size,
|
754
|
+
'stored_values': len(self._values),
|
755
|
+
'depth': self.get_depth(),
|
756
|
+
'memory_efficiency': len(self._values) / max(node_count, 1)
|
757
|
+
}
|
758
|
+
|
759
|
+
# ============================================================================
|
760
|
+
# ADDITIONAL HELPER METHODS
|
761
|
+
# ============================================================================
|
762
|
+
|
763
|
+
def clear(self) -> None:
|
764
|
+
"""Clear all elements."""
|
765
|
+
self._root = VebNode(self.universe_size)
|
766
|
+
self._values.clear()
|
767
|
+
self._size = 0
|
768
|
+
|
769
|
+
def is_empty(self) -> bool:
|
770
|
+
"""Check if tree is empty."""
|
771
|
+
return self._root.is_empty()
|
772
|
+
|
773
|
+
def size(self) -> int:
|
774
|
+
"""Get number of elements."""
|
775
|
+
return self._size
|
776
|
+
|
777
|
+
def get_mode(self) -> NodeMode:
|
778
|
+
"""Get strategy mode."""
|
779
|
+
return self.mode
|
780
|
+
|
781
|
+
def get_traits(self) -> NodeTrait:
|
782
|
+
"""Get strategy traits."""
|
783
|
+
return self.traits
|
784
|
+
|
785
|
+
# ============================================================================
|
786
|
+
# COMPATIBILITY METHODS
|
787
|
+
# ============================================================================
|
788
|
+
|
789
|
+
def find(self, key: Any) -> Optional[Any]:
|
790
|
+
"""
|
791
|
+
Find value by key (alias for get).
|
792
|
+
|
793
|
+
Args:
|
794
|
+
key: Integer key
|
795
|
+
|
796
|
+
Returns:
|
797
|
+
Value or None
|
798
|
+
"""
|
799
|
+
return self.get(key)
|
800
|
+
|
801
|
+
def insert(self, key: Any, value: Any = None) -> None:
|
802
|
+
"""
|
803
|
+
Insert key-value pair (alias for put).
|
804
|
+
|
805
|
+
Args:
|
806
|
+
key: Integer key
|
807
|
+
value: Associated value
|
808
|
+
"""
|
809
|
+
self.put(key, value)
|
810
|
+
|
811
|
+
def __str__(self) -> str:
|
812
|
+
"""String representation."""
|
813
|
+
return f"VebTreeStrategy(universe={self.universe_size}, size={self._size}, depth={self.get_depth()})"
|
814
|
+
|
815
|
+
def __repr__(self) -> str:
|
816
|
+
"""Detailed representation."""
|
817
|
+
return (f"VebTreeStrategy(mode={self.mode.name}, universe={self.universe_size}, "
|
818
|
+
f"size={self._size}, traits={self.traits})")
|
819
|
+
|
820
|
+
# ============================================================================
|
821
|
+
# FACTORY METHOD
|
822
|
+
# ============================================================================
|
823
|
+
|
824
|
+
@classmethod
|
825
|
+
def create_from_data(cls, data: Any, universe_size: int = 65536) -> 'VebTreeStrategy':
|
826
|
+
"""
|
827
|
+
Create vEB tree from data.
|
828
|
+
|
829
|
+
Args:
|
830
|
+
data: Dictionary with integer keys
|
831
|
+
universe_size: Maximum key value
|
832
|
+
|
833
|
+
Returns:
|
834
|
+
New VebTreeStrategy instance
|
835
|
+
|
836
|
+
Raises:
|
837
|
+
XWNodeValueError: If data contains non-integer keys
|
838
|
+
"""
|
839
|
+
instance = cls(universe_size=universe_size)
|
840
|
+
|
841
|
+
if isinstance(data, dict):
|
842
|
+
for key, value in data.items():
|
843
|
+
if not isinstance(key, int):
|
844
|
+
raise XWNodeValueError(
|
845
|
+
f"vEB tree requires integer keys, found {type(key).__name__}"
|
846
|
+
)
|
847
|
+
instance.put(key, value)
|
848
|
+
elif isinstance(data, (list, tuple)):
|
849
|
+
for i, value in enumerate(data):
|
850
|
+
instance.put(i, value)
|
851
|
+
else:
|
852
|
+
# Store scalar as single element
|
853
|
+
instance.put(0, data)
|
854
|
+
|
855
|
+
return instance
|
856
|
+
|