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
@@ -40,15 +40,78 @@ class AVLTreeNode:
|
|
40
40
|
|
41
41
|
class AVLTreeStrategy(ANodeTreeStrategy):
|
42
42
|
"""
|
43
|
-
AVL
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
AVL Tree strategy for strictly balanced binary search trees.
|
44
|
+
|
45
|
+
WHY AVL Tree:
|
46
|
+
- Strictest height guarantee: height ≤ 1.44 * log₂(n)
|
47
|
+
- Fastest searches among all balanced trees
|
48
|
+
- Guaranteed O(log n) worst-case for all operations
|
49
|
+
- Height-based balancing (simpler than weight-based)
|
50
|
+
- Maintains sorted order with optimal search performance
|
51
|
+
|
52
|
+
WHY this implementation:
|
53
|
+
- Height-based balance factor (simpler than red-black color rules)
|
54
|
+
- Balance factor ∈ {-1, 0, 1} maintained after every operation
|
55
|
+
- Four rotation types (LL, LR, RR, RL) handle all imbalances
|
56
|
+
- Height stored in each node for O(1) balance computation
|
57
|
+
- Parent-free implementation (simpler, less memory overhead)
|
58
|
+
|
59
|
+
Time Complexity:
|
60
|
+
- Insert: O(log n) guaranteed (at most 2 rotations, O(log n) retracing)
|
61
|
+
- Search: O(log n) guaranteed (strictly balanced tree)
|
62
|
+
- Delete: O(log n) guaranteed (at most log n rotations)
|
63
|
+
- Min/Max: O(log n) - leftmost/rightmost traversal
|
64
|
+
- Iteration: O(n) in sorted order
|
65
|
+
|
66
|
+
Space Complexity: O(n) - one node per key + height field
|
67
|
+
|
68
|
+
Trade-offs:
|
69
|
+
- Advantage: Fastest searches (stricter height balance than RB-tree)
|
70
|
+
- Advantage: Simpler than Red-Black (only heights, no colors)
|
71
|
+
- Limitation: More rotations on insert/delete than Red-Black Tree
|
72
|
+
- Limitation: Delete can trigger O(log n) rotations (RB-tree has O(1))
|
73
|
+
- Compared to Red-Black: Better searches, slower writes
|
74
|
+
- Compared to Skip List: Deterministic (no randomness), but more complex
|
75
|
+
|
76
|
+
Best for:
|
77
|
+
- Read-heavy workloads (95%+ reads)
|
78
|
+
- When search performance is critical
|
79
|
+
- Sorted data structures requiring fast lookups
|
80
|
+
- Databases and indices optimized for queries
|
81
|
+
- When strict height guarantees are needed
|
82
|
+
|
83
|
+
Not recommended for:
|
84
|
+
- Write-heavy workloads (Red-Black Tree is better)
|
85
|
+
- Disk-based storage (B-Tree is better)
|
86
|
+
- Simple, unsorted data (Hash Map is faster)
|
87
|
+
- When insert/delete performance matters more than search
|
88
|
+
- Memory-constrained environments (height overhead)
|
89
|
+
|
90
|
+
Following eXonware Priorities:
|
91
|
+
1. Usability: Strictest balancing ensures predictable O(log n) performance
|
92
|
+
2. Maintainability: Height-based balancing is easier to understand than colors
|
93
|
+
3. Performance: Optimal search performance among all balanced trees
|
94
|
+
4. Extensibility: Can be enhanced with augmentation (order statistics, intervals)
|
95
|
+
5. Security: Input validation, bounded height prevents worst-case attacks
|
96
|
+
|
97
|
+
Industry Best Practices:
|
98
|
+
- Follows Adelson-Velsky and Landis original paper (1962)
|
99
|
+
- Maintains balance factor invariant: |bf| ≤ 1
|
100
|
+
- Uses four rotation types: LL (single right), RR (single left),
|
101
|
+
LR (left-right double), RL (right-left double)
|
102
|
+
- Height updated after every structural change
|
103
|
+
- Tracks performance statistics
|
104
|
+
|
105
|
+
Performance Note:
|
106
|
+
AVL Trees provide the STRICTEST height guarantee (≤ 1.44 * log₂(n))
|
107
|
+
among all balanced binary search trees. This makes them ideal for
|
108
|
+
read-heavy workloads where search performance is paramount.
|
109
|
+
For write-heavy workloads, Red-Black Trees are preferred due to
|
110
|
+
fewer rotations (at most 2 for insert vs O(log n) for AVL delete).
|
111
|
+
"""
|
47
112
|
|
48
113
|
# Strategy type classification
|
49
114
|
STRATEGY_TYPE = NodeType.TREE
|
50
|
-
ing rules and rotations.
|
51
|
-
"""
|
52
115
|
|
53
116
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
54
117
|
"""Initialize the AVL tree strategy."""
|
@@ -291,6 +354,14 @@ ing rules and rotations.
|
|
291
354
|
"""Get number of key-value pairs."""
|
292
355
|
return self._size
|
293
356
|
|
357
|
+
def __len__(self) -> int:
|
358
|
+
"""Get number of key-value pairs."""
|
359
|
+
return self._size
|
360
|
+
|
361
|
+
def to_native(self) -> Dict[str, Any]:
|
362
|
+
"""Convert to native Python dict."""
|
363
|
+
return dict(self.items())
|
364
|
+
|
294
365
|
def is_empty(self) -> bool:
|
295
366
|
"""Check if tree is empty."""
|
296
367
|
return self._root is None
|
@@ -42,15 +42,70 @@ class BPlusTreeNode:
|
|
42
42
|
|
43
43
|
class BPlusTreeStrategy(ANodeTreeStrategy):
|
44
44
|
"""
|
45
|
-
B+ Tree
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
B+ Tree strategy for database-friendly operations and range queries.
|
46
|
+
|
47
|
+
WHY B+ Tree:
|
48
|
+
- All data stored in leaves (efficient sequential scans)
|
49
|
+
- Leaf nodes linked for O(k) range queries after O(log n) seek
|
50
|
+
- Higher fanout than B-Tree (more keys per internal node)
|
51
|
+
- Optimal for disk-based databases (fewer disk reads)
|
52
|
+
|
53
|
+
WHY this implementation:
|
54
|
+
- Maintains linked list of leaves for fast range scans
|
55
|
+
- Internal nodes store only keys (no values)
|
56
|
+
- All values at leaf level for consistent access
|
57
|
+
- Standard B+ Tree algorithm used in most databases
|
58
|
+
|
59
|
+
Time Complexity:
|
60
|
+
- Insert: O(log n) - Tree traversal to leaf
|
61
|
+
- Search: O(log n) - Guaranteed by tree height
|
62
|
+
- Delete: O(log n) - May require merging
|
63
|
+
- Range query: O(log n + k) where k = results (B+ Tree strength)
|
64
|
+
- Sequential scan: O(n) via linked leaves (very fast)
|
65
|
+
|
66
|
+
Space Complexity: O(n)
|
67
|
+
|
68
|
+
WHY O(log n) and not O(1):
|
69
|
+
- Sorted order maintenance requires tree traversal
|
70
|
+
- Balanced structure is essential for consistency
|
71
|
+
- Trade-off enables extremely fast range queries
|
72
|
+
|
73
|
+
Trade-offs:
|
74
|
+
- Advantage: Exceptional range query performance
|
75
|
+
- Advantage: Sequential access via linked leaves
|
76
|
+
- Advantage: Higher fanout = shorter tree
|
77
|
+
- Limitation: O(log n) for single-key operations
|
78
|
+
- Compared to B-Tree: Better for range scans, similar point queries
|
79
|
+
- Compared to Hash Map: Slower lookups, but supports ranges
|
80
|
+
|
81
|
+
Best for:
|
82
|
+
- Database indexes (PostgreSQL, MySQL use B+ Trees)
|
83
|
+
- File system indexes
|
84
|
+
- Range queries and interval lookups
|
85
|
+
- Sequential scans of large datasets
|
86
|
+
- Disk-based storage systems
|
87
|
+
|
88
|
+
Not recommended for:
|
89
|
+
- Random single-key lookups only (use HASH_MAP)
|
90
|
+
- Small datasets (< 100 items)
|
91
|
+
- Write-heavy workloads (use LSM_TREE)
|
92
|
+
- In-memory with no range queries (use HASH_MAP)
|
93
|
+
|
94
|
+
Following eXonware Priorities:
|
95
|
+
1. Security: Predictable O(log n) performance
|
96
|
+
2. Usability: Sorted iteration, range queries
|
97
|
+
3. Maintainability: Well-known database algorithm
|
98
|
+
4. Performance: Optimal for range queries and scans
|
99
|
+
5. Extensibility: Can add bulk loading, concurrent access
|
100
|
+
|
101
|
+
Performance Note:
|
102
|
+
B+ Tree O(log n) is CORRECT and NECESSARY for sorted order.
|
103
|
+
The algorithm's strength is range queries, not point lookups.
|
104
|
+
For O(1) operations, use HASH_MAP.
|
105
|
+
"""
|
49
106
|
|
50
107
|
# Strategy type classification
|
51
108
|
STRATEGY_TYPE = NodeType.TREE
|
52
|
-
k-based storage systems.
|
53
|
-
"""
|
54
109
|
|
55
110
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
56
111
|
"""Initialize the B+ Tree strategy."""
|
@@ -97,7 +152,17 @@ k-based storage systems.
|
|
97
152
|
|
98
153
|
current = self._root
|
99
154
|
while not current.is_leaf:
|
100
|
-
child_index =
|
155
|
+
child_index = 0
|
156
|
+
# Find correct child to traverse
|
157
|
+
for i, k in enumerate(current.keys):
|
158
|
+
if key < k:
|
159
|
+
break
|
160
|
+
child_index = i + 1
|
161
|
+
|
162
|
+
# Bounds check
|
163
|
+
if child_index >= len(current.children):
|
164
|
+
child_index = len(current.children) - 1
|
165
|
+
|
101
166
|
current = current.children[child_index]
|
102
167
|
|
103
168
|
return current
|
@@ -230,50 +295,26 @@ k-based storage systems.
|
|
230
295
|
# CORE OPERATIONS
|
231
296
|
# ============================================================================
|
232
297
|
|
298
|
+
def find(self, key: Any) -> Optional[Any]:
|
299
|
+
"""Find value by key (implements base class abstract method)."""
|
300
|
+
key_str = str(key)
|
301
|
+
return self._search_key(key_str)
|
302
|
+
|
233
303
|
def put(self, key: Any, value: Any = None) -> None:
|
234
304
|
"""Add key-value pair to B+ tree."""
|
235
305
|
key_str = str(key)
|
236
|
-
|
237
|
-
self._insert_key(
|
306
|
+
# Store original key, not normalized (for case-sensitive mode)
|
307
|
+
self._insert_key(key_str, value)
|
238
308
|
|
239
309
|
def get(self, key: Any, default: Any = None) -> Any:
|
240
310
|
"""Get value by key."""
|
241
311
|
key_str = str(key)
|
242
|
-
|
243
|
-
if key_str == "tree_info":
|
244
|
-
return {
|
245
|
-
'size': self._size,
|
246
|
-
'height': self._height,
|
247
|
-
'total_nodes': self._total_nodes,
|
248
|
-
'order': self.order,
|
249
|
-
'case_sensitive': self.case_sensitive,
|
250
|
-
'total_splits': self._total_splits
|
251
|
-
}
|
252
|
-
elif key_str == "first_key":
|
253
|
-
return self.first_key()
|
254
|
-
elif key_str == "last_key":
|
255
|
-
return self.last_key()
|
256
|
-
elif key_str.isdigit():
|
257
|
-
# Access by index
|
258
|
-
index = int(key_str)
|
259
|
-
return self.get_at_index(index)
|
260
|
-
|
261
|
-
normalized_key = self._normalize_key(key_str)
|
262
|
-
result = self._search_key(normalized_key)
|
312
|
+
result = self.find(key_str)
|
263
313
|
return result if result is not None else default
|
264
314
|
|
265
315
|
def has(self, key: Any) -> bool:
|
266
316
|
"""Check if key exists."""
|
267
|
-
|
268
|
-
|
269
|
-
if key_str in ["tree_info", "first_key", "last_key"]:
|
270
|
-
return True
|
271
|
-
elif key_str.isdigit():
|
272
|
-
index = int(key_str)
|
273
|
-
return 0 <= index < self._size
|
274
|
-
|
275
|
-
normalized_key = self._normalize_key(key_str)
|
276
|
-
return self._search_key(normalized_key) is not None
|
317
|
+
return self.find(str(key)) is not None
|
277
318
|
|
278
319
|
def remove(self, key: Any) -> bool:
|
279
320
|
"""Remove key from tree (simplified implementation)."""
|
@@ -71,10 +71,18 @@ class BTreeNode:
|
|
71
71
|
full_child = self.children[i]
|
72
72
|
new_child = BTreeNode(full_child.degree, full_child.is_leaf)
|
73
73
|
|
74
|
-
#
|
74
|
+
# Calculate middle index
|
75
75
|
mid = self.degree - 1
|
76
|
+
|
77
|
+
# Store middle key and value to promote
|
78
|
+
mid_key = full_child.keys[mid]
|
79
|
+
mid_value = full_child.values[mid]
|
80
|
+
|
81
|
+
# Move right half to new child (excluding middle)
|
76
82
|
new_child.keys = full_child.keys[mid + 1:]
|
77
83
|
new_child.values = full_child.values[mid + 1:]
|
84
|
+
|
85
|
+
# Keep left half in full_child (excluding middle)
|
78
86
|
full_child.keys = full_child.keys[:mid]
|
79
87
|
full_child.values = full_child.values[:mid]
|
80
88
|
|
@@ -85,21 +93,77 @@ class BTreeNode:
|
|
85
93
|
|
86
94
|
# Insert new child and promote middle key
|
87
95
|
self.children.insert(i + 1, new_child)
|
88
|
-
self.keys.insert(i,
|
89
|
-
self.values.insert(i,
|
96
|
+
self.keys.insert(i, mid_key)
|
97
|
+
self.values.insert(i, mid_value)
|
90
98
|
|
91
99
|
|
92
100
|
class BTreeStrategy(ANodeTreeStrategy):
|
93
101
|
"""
|
94
|
-
B-Tree
|
95
|
-
|
96
|
-
|
97
|
-
|
102
|
+
B-Tree strategy for efficient sorted operations and range queries.
|
103
|
+
|
104
|
+
WHY B-Tree:
|
105
|
+
- Self-balancing ensures consistent O(log n) performance
|
106
|
+
- High branching factor reduces tree height (better than binary trees)
|
107
|
+
- Minimizes disk I/O by grouping keys in pages/blocks
|
108
|
+
- Excellent for range queries and sequential scans
|
109
|
+
|
110
|
+
WHY this implementation:
|
111
|
+
- Uses standard B-Tree algorithm (Knuth, TAOCP Vol 3)
|
112
|
+
- Proactive node splitting (split-on-insert strategy)
|
113
|
+
- Maintains 2t-1 max keys per node (t = degree)
|
114
|
+
- Keeps all leaves at same depth (perfect balance)
|
115
|
+
|
116
|
+
Time Complexity:
|
117
|
+
- Insert: O(log n) - REQUIRED for maintaining sorted order
|
118
|
+
- Search: O(log n) - Tree height determines this
|
119
|
+
- Delete: O(log n) - May require merging/borrowing
|
120
|
+
- Range query: O(k + log n) where k = results
|
121
|
+
- Iteration: O(n) sorted order guaranteed
|
122
|
+
|
123
|
+
Space Complexity: O(n)
|
124
|
+
|
125
|
+
WHY O(log n) and not O(1):
|
126
|
+
- Maintaining sorted order requires tree traversal
|
127
|
+
- Balancing operations are essential to B-Tree correctness
|
128
|
+
- This trade-off enables efficient range queries
|
129
|
+
- Self-balancing prevents degradation to O(n)
|
130
|
+
|
131
|
+
Trade-offs:
|
132
|
+
- Advantage: Excellent for range queries (k + log n)
|
133
|
+
- Advantage: Guaranteed balanced (no worst-case degradation)
|
134
|
+
- Limitation: Higher memory per node vs binary trees
|
135
|
+
- Limitation: More complex insertion/deletion logic
|
136
|
+
- Compared to Hash Map: Slower single lookups, but supports ranges
|
137
|
+
- Compared to Binary Tree: Better cache performance, lower height
|
138
|
+
|
139
|
+
Best for:
|
140
|
+
- Database indexes (primary use case)
|
141
|
+
- File systems (directory structures)
|
142
|
+
- Range queries and sorted iteration
|
143
|
+
- Disk-based storage (page-oriented)
|
144
|
+
- Large datasets with sorted access
|
145
|
+
|
146
|
+
Not recommended for:
|
147
|
+
- Random single-key lookups only (use HASH_MAP)
|
148
|
+
- Small datasets (< 100 items, overhead not worth it)
|
149
|
+
- Write-heavy workloads (use LSM_TREE)
|
150
|
+
- In-memory only with no range queries (use HASH_MAP)
|
151
|
+
|
152
|
+
Following eXonware Priorities:
|
153
|
+
1. Security: Predictable performance (no worst-case degradation)
|
154
|
+
2. Usability: Sorted iteration, range queries, familiar tree semantics
|
155
|
+
3. Maintainability: Well-established algorithm, clear split/merge logic
|
156
|
+
4. Performance: O(log n) guaranteed, optimal for disk I/O
|
157
|
+
5. Extensibility: Can add concurrent access, bulk loading
|
158
|
+
|
159
|
+
Performance Note:
|
160
|
+
This is the CORRECT complexity for a B-Tree. The O(log n) overhead
|
161
|
+
enables sorted order and range queries. If you need O(1) operations
|
162
|
+
without ordering, use HASH_MAP instead.
|
163
|
+
"""
|
98
164
|
|
99
165
|
# Strategy type classification
|
100
166
|
STRATEGY_TYPE = NodeType.TREE
|
101
|
-
mance for range queries.
|
102
|
-
"""
|
103
167
|
|
104
168
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
105
169
|
"""Initialize the B-tree strategy."""
|
@@ -241,6 +305,11 @@ mance for range queries.
|
|
241
305
|
"""Get the number of items."""
|
242
306
|
return self._size
|
243
307
|
|
308
|
+
@property
|
309
|
+
def is_empty(self) -> bool:
|
310
|
+
"""Check if B-Tree is empty."""
|
311
|
+
return self._size == 0
|
312
|
+
|
244
313
|
def to_native(self) -> Dict[str, Any]:
|
245
314
|
"""Convert to native Python dict."""
|
246
315
|
return dict(self.items())
|
@@ -360,3 +429,4 @@ mance for range queries.
|
|
360
429
|
'memory_usage': f"{self._size * (24 + 16)} bytes (estimated)",
|
361
430
|
'is_sorted': True
|
362
431
|
}
|
432
|
+
|