exonware-xwnode 0.0.1.21__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 +8 -1
- exonware/xwnode/__init__.py +18 -5
- exonware/xwnode/add_strategy_types.py +165 -0
- exonware/xwnode/base.py +7 -5
- 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 +9 -9
- exonware/xwnode/common/management/migration.py +6 -6
- exonware/xwnode/common/monitoring/__init__.py +3 -5
- exonware/xwnode/common/monitoring/metrics.py +7 -3
- exonware/xwnode/common/monitoring/pattern_detector.py +2 -2
- exonware/xwnode/common/monitoring/performance_monitor.py +6 -2
- exonware/xwnode/common/patterns/__init__.py +3 -5
- exonware/xwnode/common/patterns/advisor.py +1 -1
- exonware/xwnode/common/patterns/flyweight.py +6 -2
- exonware/xwnode/common/patterns/registry.py +203 -184
- 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.21.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.21.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.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/WHEEL +0 -0
- {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/licenses/LICENSE +0 -0
@@ -13,31 +13,82 @@ from ...defs import NodeMode, NodeTrait
|
|
13
13
|
|
14
14
|
class SegmentTreeStrategy(ANodeTreeStrategy):
|
15
15
|
"""
|
16
|
-
Segment Tree
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
Segment Tree strategy for range query operations.
|
17
|
+
|
18
|
+
WHY Segment Tree:
|
19
|
+
- Supports arbitrary range queries (sum, min, max, GCD, etc.)
|
20
|
+
- O(log n) queries and updates (in optimized implementation)
|
21
|
+
- More flexible than Fenwick Tree (supports non-invertible operations)
|
22
|
+
- Industry standard for range query problems
|
23
|
+
- Handles both point and range updates
|
24
|
+
|
25
|
+
WHY this implementation:
|
26
|
+
- Simplified O(n) range query for correctness (not O(log n) tree structure)
|
27
|
+
- Supports multiple operations (sum, min, max, etc.)
|
28
|
+
- Combiner pattern allows pluggable operations
|
29
|
+
- Identity elements enable proper initialization
|
30
|
+
- Clear semantics over performance optimization
|
31
|
+
|
32
|
+
Time Complexity (Current Simplified Implementation):
|
33
|
+
- Update: O(1) - direct array access
|
34
|
+
- Range Query: O(n) - scans range (simplified for correctness)
|
35
|
+
- Note: Full O(log n) segment tree requires tree rebuild logic
|
36
|
+
|
37
|
+
Space Complexity: O(n) - simple array storage
|
38
|
+
|
39
|
+
Trade-offs:
|
40
|
+
- Advantage: Supports non-invertible operations (min, max, GCD)
|
41
|
+
- Advantage: More flexible than Fenwick Tree
|
42
|
+
- Limitation: Current implementation is O(n) query (not O(log n))
|
43
|
+
- Limitation: Full segment tree is more complex than Fenwick
|
44
|
+
- Compared to Fenwick: More flexible, but current impl slower
|
45
|
+
- Compared to naive: Same complexity, but extensible
|
46
|
+
|
47
|
+
Best for:
|
48
|
+
- Range queries with non-invertible operations (min, max)
|
49
|
+
- When operation flexibility is needed
|
50
|
+
- Competitive programming (with proper O(log n) impl)
|
51
|
+
- When understanding segment tree concepts
|
52
|
+
|
53
|
+
Not recommended for:
|
54
|
+
- Prefix sums only (use Fenwick Tree - simpler)
|
55
|
+
- When O(log n) is critical (current impl is O(n))
|
56
|
+
- Production systems (needs full tree implementation)
|
57
|
+
|
58
|
+
Following eXonware Priorities:
|
59
|
+
1. Usability: Clear API for range operations
|
60
|
+
2. Maintainability: Simplified for correctness over complexity
|
61
|
+
3. Performance: O(1) updates, O(n) queries (acceptable for moderate n)
|
62
|
+
4. Extensibility: Easy to swap combiner functions
|
63
|
+
5. Security: Input validation, bounds checking
|
64
|
+
|
65
|
+
Implementation Note:
|
66
|
+
This is a SIMPLIFIED segment tree using O(n) range queries for CORRECTNESS.
|
67
|
+
A full O(log n) segment tree requires proper tree structure with:
|
68
|
+
- Recursive tree building
|
69
|
+
- Lazy propagation for range updates
|
70
|
+
- Complex index calculations
|
71
|
+
|
72
|
+
For production O(log n) performance, this needs enhancement.
|
73
|
+
Current implementation prioritizes CORRECTNESS and SIMPLICITY per
|
74
|
+
GUIDELINES_DEV.md principle: "prefer simple, maintainable solutions."
|
75
|
+
"""
|
20
76
|
|
21
77
|
# Strategy type classification
|
22
78
|
STRATEGY_TYPE = NodeType.TREE
|
23
|
-
like sum, min, max, etc.
|
24
|
-
"""
|
25
79
|
|
26
80
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
27
81
|
"""Initialize the Segment Tree strategy."""
|
28
82
|
super().__init__(NodeMode.SEGMENT_TREE, traits, **options)
|
29
83
|
|
30
|
-
self.
|
31
|
-
self._operation = options.get('operation', 'sum')
|
84
|
+
self._max_size = options.get('initial_size', 1000)
|
85
|
+
self._operation = options.get('operation', 'sum')
|
32
86
|
self._identity = self._get_identity(self._operation)
|
33
87
|
self._combiner = self._get_combiner(self._operation)
|
34
88
|
|
35
|
-
#
|
36
|
-
self.
|
89
|
+
# Simple array to store values (0-indexed for queries)
|
90
|
+
self._data: List[Any] = [self._identity] * self._max_size
|
37
91
|
self._values: Dict[str, Any] = {} # Key-value storage for compatibility
|
38
|
-
self._indices: Dict[str, int] = {} # Map keys to tree indices
|
39
|
-
self._reverse_indices: Dict[int, str] = {} # Map indices to keys
|
40
|
-
self._next_index = 0
|
41
92
|
|
42
93
|
def get_supported_traits(self) -> NodeTrait:
|
43
94
|
"""Get the traits supported by the segment tree strategy."""
|
@@ -83,27 +134,15 @@ like sum, min, max, etc.
|
|
83
134
|
"""Store a value at the given key."""
|
84
135
|
key_str = str(key)
|
85
136
|
|
86
|
-
#
|
137
|
+
# Try to convert to index
|
87
138
|
try:
|
88
|
-
|
139
|
+
idx = int(key_str)
|
140
|
+
if 0 <= idx < self._max_size:
|
141
|
+
self.update(idx, value)
|
89
142
|
except (ValueError, TypeError):
|
90
|
-
|
91
|
-
|
92
|
-
if key_str in self._indices:
|
93
|
-
# Update existing
|
94
|
-
idx = self._indices[key_str]
|
95
|
-
self._update_point(idx, numeric_value)
|
96
|
-
else:
|
97
|
-
# Add new
|
98
|
-
if self._next_index >= len(self._tree) // 4:
|
99
|
-
self._resize_tree()
|
100
|
-
|
101
|
-
idx = self._next_index
|
102
|
-
self._indices[key_str] = idx
|
103
|
-
self._reverse_indices[idx] = key_str
|
104
|
-
self._next_index += 1
|
105
|
-
self._update_point(idx, numeric_value)
|
143
|
+
pass
|
106
144
|
|
145
|
+
# Always store in values dict
|
107
146
|
self._values[key_str] = value
|
108
147
|
|
109
148
|
def get(self, key: Any, default: Any = None) -> Any:
|
@@ -118,16 +157,18 @@ like sum, min, max, etc.
|
|
118
157
|
def remove(self, key: Any) -> bool:
|
119
158
|
"""Remove value by key."""
|
120
159
|
key_str = str(key)
|
121
|
-
if key_str not in self.
|
160
|
+
if key_str not in self._values:
|
122
161
|
return False
|
123
162
|
|
124
|
-
|
125
|
-
|
163
|
+
# Try to clear from array
|
164
|
+
try:
|
165
|
+
idx = int(key_str)
|
166
|
+
if 0 <= idx < self._max_size:
|
167
|
+
self._data[idx] = self._identity
|
168
|
+
except (ValueError, TypeError):
|
169
|
+
pass
|
126
170
|
|
127
|
-
del self._indices[key_str]
|
128
|
-
del self._reverse_indices[idx]
|
129
171
|
del self._values[key_str]
|
130
|
-
|
131
172
|
return True
|
132
173
|
|
133
174
|
def delete(self, key: Any) -> bool:
|
@@ -136,11 +177,8 @@ like sum, min, max, etc.
|
|
136
177
|
|
137
178
|
def clear(self) -> None:
|
138
179
|
"""Clear all data."""
|
139
|
-
self.
|
180
|
+
self._data = [self._identity] * self._max_size
|
140
181
|
self._values.clear()
|
141
|
-
self._indices.clear()
|
142
|
-
self._reverse_indices.clear()
|
143
|
-
self._next_index = 0
|
144
182
|
|
145
183
|
def keys(self) -> Iterator[str]:
|
146
184
|
"""Get all keys."""
|
@@ -176,85 +214,47 @@ like sum, min, max, etc.
|
|
176
214
|
# SEGMENT TREE SPECIFIC OPERATIONS
|
177
215
|
# ============================================================================
|
178
216
|
|
179
|
-
def
|
180
|
-
"""
|
181
|
-
|
182
|
-
new_size = old_size * 2
|
183
|
-
self._tree.extend([self._identity] * (new_size - old_size))
|
184
|
-
|
185
|
-
def _update_point(self, idx: int, value: Any) -> None:
|
186
|
-
"""Update a single point in the segment tree."""
|
187
|
-
# Convert to 1-based indexing for tree
|
188
|
-
tree_idx = idx + len(self._tree) // 4
|
189
|
-
|
190
|
-
# Ensure tree is large enough
|
191
|
-
while tree_idx >= len(self._tree):
|
192
|
-
self._resize_tree()
|
193
|
-
tree_idx = idx + len(self._tree) // 4
|
217
|
+
def range_query(self, left: int, right: int) -> Any:
|
218
|
+
"""
|
219
|
+
Query range [left, right] inclusive (0-indexed).
|
194
220
|
|
195
|
-
|
196
|
-
|
221
|
+
Uses simple O(n) scan for correctness.
|
222
|
+
For true O(log n), segment tree needs proper rebuild after updates.
|
223
|
+
"""
|
224
|
+
if left < 0 or right >= len(self._data) or left > right:
|
225
|
+
return self._identity
|
197
226
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
right_child = self._tree[tree_idx * 2 + 1] if tree_idx * 2 + 1 < len(self._tree) else self._identity
|
203
|
-
self._tree[tree_idx] = self._combiner(left_child, right_child)
|
204
|
-
|
205
|
-
def range_query(self, left: int, right: int) -> Any:
|
206
|
-
"""Query range [left, right] inclusive."""
|
207
|
-
return self._range_query_recursive(1, 0, len(self._tree) // 4 - 1, left, right)
|
227
|
+
result = self._identity
|
228
|
+
for i in range(left, right + 1):
|
229
|
+
result = self._combiner(result, self._data[i])
|
230
|
+
return result
|
208
231
|
|
209
|
-
def
|
210
|
-
|
211
|
-
|
212
|
-
if query_right < start or query_left > end:
|
213
|
-
return self._identity
|
232
|
+
def update(self, index: int, value: Any) -> None:
|
233
|
+
"""
|
234
|
+
Update value at specific index (0-indexed).
|
214
235
|
|
215
|
-
|
216
|
-
|
236
|
+
Simple O(1) update for correctness.
|
237
|
+
"""
|
238
|
+
if index < 0 or index >= self._max_size:
|
239
|
+
return
|
217
240
|
|
218
|
-
|
219
|
-
|
220
|
-
right_result = self._range_query_recursive(2 * node + 1, mid + 1, end, query_left, query_right)
|
241
|
+
# Store value
|
242
|
+
self._data[index] = value
|
221
243
|
|
222
|
-
|
244
|
+
# Track in values dict
|
245
|
+
key = str(index)
|
246
|
+
self._values[key] = value
|
223
247
|
|
224
248
|
def range_update(self, left: int, right: int, value: Any) -> None:
|
225
|
-
"""Update range [left, right] with value
|
249
|
+
"""Update range [left, right] with value."""
|
226
250
|
for i in range(left, right + 1):
|
227
|
-
|
228
|
-
key = self._reverse_indices[i]
|
229
|
-
self.put(key, value)
|
230
|
-
|
231
|
-
def prefix_query(self, index: int) -> Any:
|
232
|
-
"""Query prefix [0, index]."""
|
233
|
-
return self.range_query(0, index)
|
234
|
-
|
235
|
-
def suffix_query(self, index: int) -> Any:
|
236
|
-
"""Query suffix [index, size-1]."""
|
237
|
-
return self.range_query(index, len(self._values) - 1)
|
238
|
-
|
239
|
-
def find_first_greater(self, value: Any) -> Optional[int]:
|
240
|
-
"""Find first index where tree value > given value."""
|
241
|
-
for i in range(len(self._values)):
|
242
|
-
if i in self._reverse_indices:
|
243
|
-
key = self._reverse_indices[i]
|
244
|
-
if key in self._values:
|
245
|
-
try:
|
246
|
-
if float(self._values[key]) > float(value):
|
247
|
-
return i
|
248
|
-
except (ValueError, TypeError):
|
249
|
-
continue
|
250
|
-
return None
|
251
|
+
self.update(i, value)
|
251
252
|
|
252
253
|
def get_operation_info(self) -> Dict[str, Any]:
|
253
254
|
"""Get information about the current operation."""
|
254
255
|
return {
|
255
256
|
'operation': self._operation,
|
256
|
-
'identity': self._identity
|
257
|
-
'total_result': self.range_query(0, max(0, len(self._values) - 1))
|
257
|
+
'identity': self._identity
|
258
258
|
}
|
259
259
|
|
260
260
|
# ============================================================================
|
@@ -7,11 +7,12 @@ with O(1) average-case membership testing and insertion.
|
|
7
7
|
|
8
8
|
from typing import Any, Iterator, Set, Dict, Union, List
|
9
9
|
import hashlib
|
10
|
-
from .
|
10
|
+
from .base import ANodeStrategy
|
11
11
|
from ...defs import NodeMode, NodeTrait
|
12
|
+
from .contracts import NodeType
|
12
13
|
|
13
14
|
|
14
|
-
class
|
15
|
+
class SetHashStrategy(ANodeStrategy):
|
15
16
|
"""
|
16
17
|
Hash Set node strategy for efficient set operations.
|
17
18
|
|
@@ -20,7 +21,7 @@ class xSetHashStrategy(aNodeStrategy):
|
|
20
21
|
"""
|
21
22
|
|
22
23
|
# Strategy type classification
|
23
|
-
STRATEGY_TYPE = NodeType.
|
24
|
+
STRATEGY_TYPE = NodeType.HYBRID # Hash-based set operations
|
24
25
|
|
25
26
|
|
26
27
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
@@ -171,10 +172,10 @@ class xSetHashStrategy(aNodeStrategy):
|
|
171
172
|
self._size -= 1
|
172
173
|
return value
|
173
174
|
|
174
|
-
def union(self, other: Union['
|
175
|
+
def union(self, other: Union['SetHashStrategy', Set, List]) -> 'SetHashStrategy':
|
175
176
|
"""Return union of this set with another."""
|
176
|
-
result =
|
177
|
-
traits=self.
|
177
|
+
result = SetHashStrategy(
|
178
|
+
traits=self.traits,
|
178
179
|
case_sensitive=self.case_sensitive,
|
179
180
|
allow_none=self.allow_none
|
180
181
|
)
|
@@ -184,7 +185,7 @@ class xSetHashStrategy(aNodeStrategy):
|
|
184
185
|
result.add(self._values[element])
|
185
186
|
|
186
187
|
# Add elements from other
|
187
|
-
if isinstance(other,
|
188
|
+
if isinstance(other, SetHashStrategy):
|
188
189
|
for element in other._set:
|
189
190
|
result.add(other._values[element])
|
190
191
|
elif isinstance(other, (set, list)):
|
@@ -193,15 +194,15 @@ class xSetHashStrategy(aNodeStrategy):
|
|
193
194
|
|
194
195
|
return result
|
195
196
|
|
196
|
-
def intersection(self, other: Union['
|
197
|
+
def intersection(self, other: Union['SetHashStrategy', Set, List]) -> 'SetHashStrategy':
|
197
198
|
"""Return intersection of this set with another."""
|
198
|
-
result =
|
199
|
-
traits=self.
|
199
|
+
result = SetHashStrategy(
|
200
|
+
traits=self.traits,
|
200
201
|
case_sensitive=self.case_sensitive,
|
201
202
|
allow_none=self.allow_none
|
202
203
|
)
|
203
204
|
|
204
|
-
if isinstance(other,
|
205
|
+
if isinstance(other, SetHashStrategy):
|
205
206
|
common = self._set.intersection(other._set)
|
206
207
|
for element in common:
|
207
208
|
result.add(self._values[element])
|
@@ -213,15 +214,15 @@ class xSetHashStrategy(aNodeStrategy):
|
|
213
214
|
|
214
215
|
return result
|
215
216
|
|
216
|
-
def difference(self, other: Union['
|
217
|
+
def difference(self, other: Union['SetHashStrategy', Set, List]) -> 'SetHashStrategy':
|
217
218
|
"""Return difference of this set with another."""
|
218
|
-
result =
|
219
|
-
traits=self.
|
219
|
+
result = SetHashStrategy(
|
220
|
+
traits=self.traits,
|
220
221
|
case_sensitive=self.case_sensitive,
|
221
222
|
allow_none=self.allow_none
|
222
223
|
)
|
223
224
|
|
224
|
-
if isinstance(other,
|
225
|
+
if isinstance(other, SetHashStrategy):
|
225
226
|
diff = self._set.difference(other._set)
|
226
227
|
elif isinstance(other, (set, list)):
|
227
228
|
other_set = {str(x).lower() if not self.case_sensitive else str(x) for x in other}
|
@@ -234,15 +235,15 @@ class xSetHashStrategy(aNodeStrategy):
|
|
234
235
|
|
235
236
|
return result
|
236
237
|
|
237
|
-
def symmetric_difference(self, other: Union['
|
238
|
+
def symmetric_difference(self, other: Union['SetHashStrategy', Set, List]) -> 'SetHashStrategy':
|
238
239
|
"""Return symmetric difference of this set with another."""
|
239
|
-
result =
|
240
|
-
traits=self.
|
240
|
+
result = SetHashStrategy(
|
241
|
+
traits=self.traits,
|
241
242
|
case_sensitive=self.case_sensitive,
|
242
243
|
allow_none=self.allow_none
|
243
244
|
)
|
244
245
|
|
245
|
-
if isinstance(other,
|
246
|
+
if isinstance(other, SetHashStrategy):
|
246
247
|
sym_diff = self._set.symmetric_difference(other._set)
|
247
248
|
for element in sym_diff:
|
248
249
|
if element in self._values:
|
@@ -264,37 +265,37 @@ class xSetHashStrategy(aNodeStrategy):
|
|
264
265
|
|
265
266
|
return result
|
266
267
|
|
267
|
-
def is_subset(self, other: Union['
|
268
|
+
def is_subset(self, other: Union['SetHashStrategy', Set, List]) -> bool:
|
268
269
|
"""Check if this set is a subset of another."""
|
269
|
-
if isinstance(other,
|
270
|
+
if isinstance(other, SetHashStrategy):
|
270
271
|
return self._set.issubset(other._set)
|
271
272
|
elif isinstance(other, (set, list)):
|
272
273
|
other_set = {str(x).lower() if not self.case_sensitive else str(x) for x in other}
|
273
274
|
return self._set.issubset(other_set)
|
274
275
|
return False
|
275
276
|
|
276
|
-
def is_superset(self, other: Union['
|
277
|
+
def is_superset(self, other: Union['SetHashStrategy', Set, List]) -> bool:
|
277
278
|
"""Check if this set is a superset of another."""
|
278
|
-
if isinstance(other,
|
279
|
+
if isinstance(other, SetHashStrategy):
|
279
280
|
return self._set.issuperset(other._set)
|
280
281
|
elif isinstance(other, (set, list)):
|
281
282
|
other_set = {str(x).lower() if not self.case_sensitive else str(x) for x in other}
|
282
283
|
return self._set.issuperset(other_set)
|
283
284
|
return False
|
284
285
|
|
285
|
-
def is_disjoint(self, other: Union['
|
286
|
+
def is_disjoint(self, other: Union['SetHashStrategy', Set, List]) -> bool:
|
286
287
|
"""Check if this set has no elements in common with another."""
|
287
|
-
if isinstance(other,
|
288
|
+
if isinstance(other, SetHashStrategy):
|
288
289
|
return self._set.isdisjoint(other._set)
|
289
290
|
elif isinstance(other, (set, list)):
|
290
291
|
other_set = {str(x).lower() if not self.case_sensitive else str(x) for x in other}
|
291
292
|
return self._set.isdisjoint(other_set)
|
292
293
|
return True
|
293
294
|
|
294
|
-
def copy(self) -> '
|
295
|
+
def copy(self) -> 'SetHashStrategy':
|
295
296
|
"""Create a shallow copy of the set."""
|
296
|
-
result =
|
297
|
-
traits=self.
|
297
|
+
result = SetHashStrategy(
|
298
|
+
traits=self.traits,
|
298
299
|
case_sensitive=self.case_sensitive,
|
299
300
|
allow_none=self.allow_none
|
300
301
|
)
|
@@ -307,7 +308,7 @@ class xSetHashStrategy(aNodeStrategy):
|
|
307
308
|
def update(self, *others) -> None:
|
308
309
|
"""Update the set with elements from other iterables."""
|
309
310
|
for other in others:
|
310
|
-
if isinstance(other,
|
311
|
+
if isinstance(other, SetHashStrategy):
|
311
312
|
for element in other._set:
|
312
313
|
self.add(other._values[element])
|
313
314
|
elif isinstance(other, (set, list, tuple)):
|
@@ -38,15 +38,75 @@ class SkipListNode:
|
|
38
38
|
|
39
39
|
class SkipListStrategy(ANodeTreeStrategy):
|
40
40
|
"""
|
41
|
-
Skip
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
Skip List strategy for probabilistic sorted data structures.
|
42
|
+
|
43
|
+
WHY Skip List:
|
44
|
+
- Simpler implementation than balanced trees (no complex rotations)
|
45
|
+
- Lock-free concurrent access possible with careful implementation
|
46
|
+
- O(log n) expected time for search, insert, delete (probabilistic)
|
47
|
+
- Memory-efficient alternative to balanced trees for dynamic datasets
|
48
|
+
- Excellent for distributed systems (natural partitioning by levels)
|
49
|
+
|
50
|
+
WHY this implementation:
|
51
|
+
- Probabilistic level promotion (p=0.5) ensures balanced height distribution
|
52
|
+
- Header node sentinel pattern eliminates special-case handling
|
53
|
+
- Path caching during search amortizes traversal cost
|
54
|
+
- Forward pointers list enables efficient level-by-level traversal
|
55
|
+
- Statistics tracking for monitoring performance characteristics
|
56
|
+
|
57
|
+
Time Complexity:
|
58
|
+
- Insert: O(log n) expected, O(n) worst case (probabilistic)
|
59
|
+
- Search: O(log n) expected, O(n) worst case (probabilistic)
|
60
|
+
- Delete: O(log n) expected, O(n) worst case (probabilistic)
|
61
|
+
- Range Query: O(k + log n) where k is result size
|
62
|
+
- Iteration: O(n) sorted order at bottom level
|
63
|
+
|
64
|
+
Space Complexity: O(n log n) expected (multiple forward pointers per node)
|
65
|
+
|
66
|
+
Trade-offs:
|
67
|
+
- Advantage: Simpler than balanced trees, good concurrent properties
|
68
|
+
- Advantage: No rebalancing overhead (probabilistic balance)
|
69
|
+
- Limitation: Worst-case O(n) if unlucky with random levels
|
70
|
+
- Limitation: Higher memory overhead than plain linked list
|
71
|
+
- Compared to B-Tree: Simpler, better concurrency, but less deterministic
|
72
|
+
- Compared to AVL: Easier to implement, but higher memory usage
|
73
|
+
|
74
|
+
Best for:
|
75
|
+
- Concurrent sorted data structures (lock-free variants)
|
76
|
+
- When simplicity is more important than worst-case guarantees
|
77
|
+
- Distributed systems requiring sorted indices
|
78
|
+
- Real-time systems (no unpredictable rebalancing pauses)
|
79
|
+
- In-memory caches with sorted key access
|
80
|
+
|
81
|
+
Not recommended for:
|
82
|
+
- Hard real-time systems (probabilistic behavior)
|
83
|
+
- Memory-constrained environments (O(n log n) space)
|
84
|
+
- When worst-case guarantees are critical (use balanced tree)
|
85
|
+
- Disk-based storage (use B-Tree for locality)
|
86
|
+
- Write-once, read-many scenarios (plain sorted array better)
|
87
|
+
|
88
|
+
Following eXonware Priorities:
|
89
|
+
1. Usability: Simple API, no complex rebalancing logic to debug
|
90
|
+
2. Maintainability: Clean probabilistic design, easy to understand
|
91
|
+
3. Performance: O(log n) expected time, efficient range queries
|
92
|
+
4. Extensibility: Easy to add concurrent variants or custom level logic
|
93
|
+
5. Security: Input validation on all operations, prevents malformed structure
|
94
|
+
|
95
|
+
Industry Best Practices:
|
96
|
+
- Follows Pugh's original skip list paper (1990)
|
97
|
+
- Uses p=0.5 probability (optimal for most workloads)
|
98
|
+
- Implements header sentinel for cleaner code
|
99
|
+
- Provides statistics for monitoring probabilistic behavior
|
100
|
+
- Supports both case-sensitive and case-insensitive keys
|
101
|
+
|
102
|
+
Performance Note:
|
103
|
+
Skip list is probabilistic - O(log n) is EXPECTED, not worst-case.
|
104
|
+
For guaranteed O(log n), use RED_BLACK_TREE or AVL_TREE instead.
|
105
|
+
The trade-off is simplicity and concurrency vs determinism.
|
106
|
+
"""
|
45
107
|
|
46
108
|
# Strategy type classification
|
47
109
|
STRATEGY_TYPE = NodeType.TREE
|
48
|
-
rrent access properties.
|
49
|
-
"""
|
50
110
|
|
51
111
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
52
112
|
"""Initialize the skip list strategy."""
|
@@ -227,6 +287,14 @@ rrent access properties.
|
|
227
287
|
"""Get number of key-value pairs."""
|
228
288
|
return self._size
|
229
289
|
|
290
|
+
def __len__(self) -> int:
|
291
|
+
"""Get number of key-value pairs."""
|
292
|
+
return self._size
|
293
|
+
|
294
|
+
def to_native(self) -> Dict[str, Any]:
|
295
|
+
"""Convert to native Python dict."""
|
296
|
+
return dict(self.items())
|
297
|
+
|
230
298
|
def is_empty(self) -> bool:
|
231
299
|
"""Check if skip list is empty."""
|
232
300
|
return self._size == 0
|