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
@@ -41,15 +41,76 @@ class TreapNode:
|
|
41
41
|
|
42
42
|
class TreapStrategy(ANodeTreeStrategy):
|
43
43
|
"""
|
44
|
-
Treap
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
Treap strategy for randomized balanced binary search trees.
|
45
|
+
|
46
|
+
WHY Treap (Tree + Heap):
|
47
|
+
- Simpler than AVL/Red-Black (only rotations, no complex rules)
|
48
|
+
- Probabilistic O(log n) expected height (randomized balancing)
|
49
|
+
- Combines BST property (keys) with max-heap property (priorities)
|
50
|
+
- No explicit height/color tracking needed (priorities handle balancing)
|
51
|
+
- Excellent for dynamic scenarios (frequent insert/delete)
|
52
|
+
|
53
|
+
WHY this implementation:
|
54
|
+
- Random priorities assigned at insertion (1 to 1,000,000 range)
|
55
|
+
- BST property maintained by keys (sorted order)
|
56
|
+
- Max-heap property maintained by priorities (parent > children)
|
57
|
+
- Rotations restore heap property after insertions/deletions
|
58
|
+
- Parent-free implementation (simpler, less memory)
|
59
|
+
|
60
|
+
Time Complexity:
|
61
|
+
- Insert: O(log n) expected, O(n) worst case (probabilistic)
|
62
|
+
- Search: O(log n) expected, O(n) worst case (probabilistic)
|
63
|
+
- Delete: O(log n) expected, O(n) worst case (probabilistic)
|
64
|
+
- Min/Max: O(log n) - leftmost/rightmost traversal
|
65
|
+
- Iteration: O(n) in sorted order (BST property)
|
66
|
+
|
67
|
+
Space Complexity: O(n) - one node per key + priority integer
|
68
|
+
|
69
|
+
Trade-offs:
|
70
|
+
- Advantage: Simpler than AVL/Red-Black (no complex rebalancing rules)
|
71
|
+
- Advantage: Good performance in practice (expected O(log n))
|
72
|
+
- Limitation: Probabilistic (no worst-case guarantees like AVL/RB)
|
73
|
+
- Limitation: Randomness may complicate debugging/testing
|
74
|
+
- Compared to Skip List: More memory efficient, but rotations overhead
|
75
|
+
- Compared to AVL: Simpler, but probabilistic vs deterministic
|
76
|
+
|
77
|
+
Best for:
|
78
|
+
- When simplicity is valued over worst-case guarantees
|
79
|
+
- Dynamic scenarios with frequent insert/delete
|
80
|
+
- When deterministic balancing is not required
|
81
|
+
- Educational purposes (elegant combination of BST + heap)
|
82
|
+
- Randomized algorithms and Monte Carlo simulations
|
83
|
+
|
84
|
+
Not recommended for:
|
85
|
+
- Hard real-time systems (probabilistic behavior)
|
86
|
+
- When worst-case guarantees are critical (use AVL/Red-Black)
|
87
|
+
- Adversarial inputs (can be exploited with known priorities)
|
88
|
+
- Deterministic testing environments (randomness complicates tests)
|
89
|
+
- Production systems requiring predictable performance
|
90
|
+
|
91
|
+
Following eXonware Priorities:
|
92
|
+
1. Usability: Simplest self-balancing tree (no complex rules)
|
93
|
+
2. Maintainability: Clean randomized design, easy to understand
|
94
|
+
3. Performance: Expected O(log n) with low constant factors
|
95
|
+
4. Extensibility: Can support split/merge operations efficiently
|
96
|
+
5. Security: Input validation, random priorities prevent some attacks
|
97
|
+
|
98
|
+
Industry Best Practices:
|
99
|
+
- Follows Seidel and Aragon original paper (1989)
|
100
|
+
- Maintains BST invariant: left < node < right (keys)
|
101
|
+
- Maintains max-heap invariant: parent.priority > child.priority
|
102
|
+
- Uses rotations to restore heap property after modifications
|
103
|
+
- Provides split/merge operations for advanced use cases
|
104
|
+
|
105
|
+
Performance Note:
|
106
|
+
Treaps offer EXPECTED O(log n) performance, not worst-case.
|
107
|
+
The randomized priorities ensure good balance with high probability.
|
108
|
+
For guaranteed O(log n), use AVL_TREE or RED_BLACK_TREE instead.
|
109
|
+
Trade-off: Simplicity (Treap) vs Determinism (AVL/Red-Black).
|
110
|
+
"""
|
48
111
|
|
49
112
|
# Strategy type classification
|
50
113
|
STRATEGY_TYPE = NodeType.TREE
|
51
|
-
self-balancing behavior.
|
52
|
-
"""
|
53
114
|
|
54
115
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
55
116
|
"""Initialize the treap strategy."""
|
@@ -267,6 +328,14 @@ self-balancing behavior.
|
|
267
328
|
"""Get number of key-value pairs."""
|
268
329
|
return self._size
|
269
330
|
|
331
|
+
def __len__(self) -> int:
|
332
|
+
"""Get number of key-value pairs."""
|
333
|
+
return self._size
|
334
|
+
|
335
|
+
def to_native(self) -> Dict[str, Any]:
|
336
|
+
"""Convert to native Python dict."""
|
337
|
+
return dict(self.items())
|
338
|
+
|
270
339
|
def is_empty(self) -> bool:
|
271
340
|
"""Check if tree is empty."""
|
272
341
|
return self._root is None
|
@@ -1,5 +1,5 @@
|
|
1
1
|
"""
|
2
|
-
#exonware/xwnode/src/exonware/xwnode/strategies/
|
2
|
+
#exonware/xwnode/src/exonware/xwnode/nodes/strategies/tree_graph_hybrid.py
|
3
3
|
|
4
4
|
Tree Graph Hybrid Strategy Implementation
|
5
5
|
|
@@ -15,14 +15,17 @@ from typing import Any, Union, List, Dict, Optional, Iterator, Tuple, Callable
|
|
15
15
|
from collections import OrderedDict
|
16
16
|
|
17
17
|
from ...defs import NodeMode, NodeTrait
|
18
|
-
from ...
|
19
|
-
from ...errors import
|
18
|
+
from ...contracts import iNodeStrategy
|
19
|
+
from ...errors import XWNodePathError
|
20
20
|
from exonware.xwsystem import get_logger
|
21
21
|
|
22
22
|
logger = get_logger(__name__)
|
23
23
|
|
24
|
+
# Import contracts
|
25
|
+
from .contracts import NodeType
|
26
|
+
|
24
27
|
# Import shared utilities
|
25
|
-
from
|
28
|
+
from ...common.utils import (
|
26
29
|
PathParser, TrieNode, UnionFind, MinHeap,
|
27
30
|
create_path_parser, create_performance_tracker
|
28
31
|
)
|
@@ -533,10 +536,12 @@ class TreeGraphHybridStrategy(iNodeStrategy):
|
|
533
536
|
STRATEGY_TYPE = NodeType.HYBRID
|
534
537
|
|
535
538
|
|
536
|
-
def __init__(self):
|
539
|
+
def __init__(self, mode=None, traits=None, **options):
|
537
540
|
"""Initialize the TreeGraphHybrid strategy."""
|
541
|
+
# Accept standard parameters but don't use them (for compatibility)
|
538
542
|
self._root: Optional[TreeGraphNode] = None
|
539
543
|
self._node_count = 0
|
544
|
+
self._options = options
|
540
545
|
self._edge_count = 0
|
541
546
|
self._cache = {}
|
542
547
|
self._size = 0
|
@@ -581,6 +586,9 @@ class TreeGraphHybridStrategy(iNodeStrategy):
|
|
581
586
|
def _calculate_size(self, node: TreeGraphNode) -> int:
|
582
587
|
"""Calculate the size of the tree."""
|
583
588
|
if isinstance(node, TreeGraphValueNode):
|
589
|
+
# Treat None as empty (size 0)
|
590
|
+
if node._value is None:
|
591
|
+
return 0
|
584
592
|
return 1
|
585
593
|
elif isinstance(node, TreeGraphListNode):
|
586
594
|
# For lists, return the number of items
|
@@ -1107,6 +1115,39 @@ class TreeGraphHybridStrategy(iNodeStrategy):
|
|
1107
1115
|
return {}
|
1108
1116
|
return self._root._to_native()
|
1109
1117
|
|
1118
|
+
def size(self) -> int:
|
1119
|
+
"""Get the size/count of items in the tree."""
|
1120
|
+
if self._root is None:
|
1121
|
+
return 0
|
1122
|
+
# For dict root, return number of top-level keys
|
1123
|
+
if isinstance(self._root, TreeGraphDictNode):
|
1124
|
+
return len(self._root._keys) if hasattr(self._root, '_keys') else len(self._root._children)
|
1125
|
+
# For list root, return list length
|
1126
|
+
elif isinstance(self._root, TreeGraphListNode):
|
1127
|
+
# Check lazy-loaded source data first, then loaded children
|
1128
|
+
if hasattr(self._root, '_source_data') and self._root._source_data is not None:
|
1129
|
+
return len(self._root._source_data)
|
1130
|
+
return len(self._root._children)
|
1131
|
+
# For value node, check if it's None (empty)
|
1132
|
+
elif isinstance(self._root, TreeGraphValueNode):
|
1133
|
+
if hasattr(self._root, '_value') and self._root._value is None:
|
1134
|
+
return 0
|
1135
|
+
return 1
|
1136
|
+
# Default: return 1
|
1137
|
+
else:
|
1138
|
+
return 1
|
1139
|
+
|
1140
|
+
def is_empty(self) -> bool:
|
1141
|
+
"""Check if the structure is empty."""
|
1142
|
+
return self.size() == 0
|
1143
|
+
|
1144
|
+
def find(self, path: str) -> Optional[Any]:
|
1145
|
+
"""Find a value by path (facade compatibility)."""
|
1146
|
+
result = self.get(path, default=None)
|
1147
|
+
if result is not None and hasattr(result, 'to_native'):
|
1148
|
+
return result.to_native()
|
1149
|
+
return result
|
1150
|
+
|
1110
1151
|
def get(self, path: str, default: Any = None) -> Optional['TreeGraphHybridStrategy']:
|
1111
1152
|
"""Get a child node by path."""
|
1112
1153
|
if self._root is None:
|
@@ -19,22 +19,63 @@ class TrieNode:
|
|
19
19
|
self.value: Any = None
|
20
20
|
|
21
21
|
|
22
|
-
class
|
22
|
+
class TrieStrategy(ANodeTreeStrategy):
|
23
|
+
"""
|
24
|
+
Trie (Prefix Tree) strategy for efficient prefix-based string operations.
|
25
|
+
|
26
|
+
WHY Trie:
|
27
|
+
- O(k) operations where k = key length (independent of dataset size)
|
28
|
+
- Exceptional prefix matching and autocomplete
|
29
|
+
- Natural string organization by shared prefixes
|
30
|
+
- Memory-efficient for datasets with common prefixes
|
31
|
+
|
32
|
+
WHY this implementation:
|
33
|
+
- Standard trie algorithm with character-by-character nodes
|
34
|
+
- Dictionary-based children for flexible character sets
|
35
|
+
- End-of-word markers for distinguishing prefixes from complete words
|
36
|
+
- Supports full Unicode character set
|
37
|
+
|
38
|
+
Time Complexity:
|
39
|
+
- Insert: O(k) where k = key length
|
40
|
+
- Search: O(k) - traverse k characters
|
41
|
+
- Delete: O(k) - traverse and cleanup
|
42
|
+
- Prefix search: O(k + m) where m = matching words
|
43
|
+
- Autocomplete: O(k + m) where m = suggestions
|
44
|
+
|
45
|
+
Space Complexity: O(ALPHABET_SIZE * N * K) worst case, often much better
|
46
|
+
|
47
|
+
Trade-offs:
|
48
|
+
- Advantage: Time independent of dataset size
|
49
|
+
- Advantage: Natural prefix operations
|
50
|
+
- Advantage: Memory sharing for common prefixes
|
51
|
+
- Limitation: Higher memory than hash for unique keys
|
52
|
+
- Compared to Hash Map: Slower exact match, better for prefixes
|
53
|
+
|
54
|
+
Best for:
|
55
|
+
- Autocomplete systems
|
56
|
+
- Dictionary/spell check
|
57
|
+
- IP routing tables
|
58
|
+
- String prefix matching
|
59
|
+
|
60
|
+
Not recommended for:
|
61
|
+
- Non-string keys (use HASH_MAP)
|
62
|
+
- Exact match only (use HASH_MAP)
|
63
|
+
- Numeric keys (use B_TREE)
|
64
|
+
|
65
|
+
Following eXonware Priorities:
|
66
|
+
1. Security: Bounded by key length
|
67
|
+
2. Usability: Intuitive for string operations
|
68
|
+
3. Maintainability: Well-known algorithm
|
69
|
+
4. Performance: O(k) guaranteed
|
70
|
+
5. Extensibility: Can add compression (Radix)
|
23
71
|
"""
|
24
|
-
Trie node strategy for efficient string prefix operations.
|
25
|
-
|
26
|
-
Optimized for prefix matching, autocomplet
|
27
72
|
|
28
73
|
# Strategy type classification
|
29
74
|
STRATEGY_TYPE = NodeType.TREE
|
30
|
-
e, and string searching.
|
31
|
-
"""
|
32
75
|
|
33
76
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
34
77
|
"""Initialize the trie strategy."""
|
35
|
-
super().__init__(
|
36
|
-
self._mode = NodeMode.TRIE
|
37
|
-
self._traits = traits
|
78
|
+
super().__init__(NodeMode.TRIE, traits, **options)
|
38
79
|
self._root = TrieNode()
|
39
80
|
self._size = 0
|
40
81
|
|
@@ -277,3 +318,106 @@ e, and string searching.
|
|
277
318
|
if not node.children:
|
278
319
|
return 0
|
279
320
|
return 1 + max(self._height_helper(child) for child in node.children.values())
|
321
|
+
|
322
|
+
# ============================================================================
|
323
|
+
# REQUIRED INTERFACE METHODS (iNodeStrategy)
|
324
|
+
# ============================================================================
|
325
|
+
|
326
|
+
def create_from_data(self, data: Any) -> 'TrieStrategy':
|
327
|
+
"""Create strategy instance from data."""
|
328
|
+
new_strategy = TrieStrategy(self._traits)
|
329
|
+
if isinstance(data, dict):
|
330
|
+
for key, value in data.items():
|
331
|
+
new_strategy.insert(key, value)
|
332
|
+
elif isinstance(data, list):
|
333
|
+
for item in data:
|
334
|
+
new_strategy.insert(item, item)
|
335
|
+
return new_strategy
|
336
|
+
|
337
|
+
def get(self, path: str, default: Any = None) -> Any:
|
338
|
+
"""Get value by path (trie uses find)."""
|
339
|
+
result = self.find(path)
|
340
|
+
return result if result is not None else default
|
341
|
+
|
342
|
+
def has(self, key: Any) -> bool:
|
343
|
+
"""Check if key exists."""
|
344
|
+
return self.find(str(key)) is not None
|
345
|
+
|
346
|
+
def put(self, path: str, value: Any) -> 'TrieStrategy':
|
347
|
+
"""Put value at path."""
|
348
|
+
self.insert(path, value)
|
349
|
+
return self
|
350
|
+
|
351
|
+
def exists(self, path: str) -> bool:
|
352
|
+
"""Check if path exists."""
|
353
|
+
return self.find(path) is not None
|
354
|
+
|
355
|
+
# Container protocol
|
356
|
+
def __len__(self) -> int:
|
357
|
+
"""Get length."""
|
358
|
+
return self._size
|
359
|
+
|
360
|
+
def __iter__(self) -> Iterator[Any]:
|
361
|
+
"""Iterate over values."""
|
362
|
+
return self.values()
|
363
|
+
|
364
|
+
def __getitem__(self, key: Any) -> Any:
|
365
|
+
"""Get item by key."""
|
366
|
+
result = self.find(key)
|
367
|
+
if result is None:
|
368
|
+
raise KeyError(str(key))
|
369
|
+
return result
|
370
|
+
|
371
|
+
def __setitem__(self, key: Any, value: Any) -> None:
|
372
|
+
"""Set item by key."""
|
373
|
+
self.insert(key, value)
|
374
|
+
|
375
|
+
def __contains__(self, key: Any) -> bool:
|
376
|
+
"""Check if key exists."""
|
377
|
+
return self.find(key) is not None
|
378
|
+
|
379
|
+
# Type checking properties
|
380
|
+
@property
|
381
|
+
def is_leaf(self) -> bool:
|
382
|
+
"""Check if this is a leaf node."""
|
383
|
+
return self._size == 0
|
384
|
+
|
385
|
+
@property
|
386
|
+
def is_list(self) -> bool:
|
387
|
+
"""Check if this is a list node."""
|
388
|
+
return False
|
389
|
+
|
390
|
+
@property
|
391
|
+
def is_dict(self) -> bool:
|
392
|
+
"""Check if this is a dict node."""
|
393
|
+
return True # Trie is dict-like (maps strings to values)
|
394
|
+
|
395
|
+
@property
|
396
|
+
def is_reference(self) -> bool:
|
397
|
+
"""Check if this is a reference node."""
|
398
|
+
return False
|
399
|
+
|
400
|
+
@property
|
401
|
+
def is_object(self) -> bool:
|
402
|
+
"""Check if this is an object node."""
|
403
|
+
return False
|
404
|
+
|
405
|
+
@property
|
406
|
+
def type(self) -> str:
|
407
|
+
"""Get the type of this node."""
|
408
|
+
return "trie"
|
409
|
+
|
410
|
+
@property
|
411
|
+
def value(self) -> Any:
|
412
|
+
"""Get the value of this node."""
|
413
|
+
return self.to_native()
|
414
|
+
|
415
|
+
@property
|
416
|
+
def strategy_name(self) -> str:
|
417
|
+
"""Get strategy name."""
|
418
|
+
return "TRIE"
|
419
|
+
|
420
|
+
@property
|
421
|
+
def supported_traits(self) -> NodeTrait:
|
422
|
+
"""Get supported traits."""
|
423
|
+
return self.get_supported_traits()
|
@@ -77,7 +77,7 @@ class UnionFind:
|
|
77
77
|
return len(self.get_set_members(x))
|
78
78
|
|
79
79
|
|
80
|
-
class
|
80
|
+
class UnionFindStrategy(ANodeGraphStrategy):
|
81
81
|
"""
|
82
82
|
Union-Find node strategy for efficient set operations.
|
83
83
|
|
@@ -90,15 +90,13 @@ ations on disjoint sets.
|
|
90
90
|
|
91
91
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
92
92
|
"""Initialize the union-find strategy."""
|
93
|
-
super().__init__(
|
94
|
-
self._mode = NodeMode.UNION_FIND
|
95
|
-
self._traits = traits
|
93
|
+
super().__init__(NodeMode.UNION_FIND, traits, **options)
|
96
94
|
self._union_find = UnionFind()
|
97
95
|
self._size = 0
|
98
96
|
|
99
97
|
def get_supported_traits(self) -> NodeTrait:
|
100
98
|
"""Get the traits supported by the union-find strategy."""
|
101
|
-
return (NodeTrait.
|
99
|
+
return (NodeTrait.GRAPH | NodeTrait.HIERARCHICAL | NodeTrait.UNION_FIND)
|
102
100
|
|
103
101
|
# ============================================================================
|
104
102
|
# CORE OPERATIONS
|
@@ -122,6 +120,11 @@ ations on disjoint sets.
|
|
122
120
|
# Union-Find doesn't support deletion efficiently
|
123
121
|
return False
|
124
122
|
|
123
|
+
def clear(self) -> None:
|
124
|
+
"""Clear all data."""
|
125
|
+
self._union_find = UnionFind()
|
126
|
+
self._size = 0
|
127
|
+
|
125
128
|
def size(self) -> int:
|
126
129
|
"""Get the number of elements."""
|
127
130
|
return self._size
|
@@ -286,3 +289,106 @@ ations on disjoint sets.
|
|
286
289
|
'sets': self._union_find.get_set_count(),
|
287
290
|
'memory_usage': f"{self._size * 32} bytes (estimated)"
|
288
291
|
}
|
292
|
+
|
293
|
+
# ============================================================================
|
294
|
+
# REQUIRED INTERFACE METHODS (iNodeStrategy)
|
295
|
+
# ============================================================================
|
296
|
+
|
297
|
+
def create_from_data(self, data: Any) -> 'UnionFindStrategy':
|
298
|
+
"""Create strategy instance from data."""
|
299
|
+
new_strategy = UnionFindStrategy(self._traits)
|
300
|
+
if isinstance(data, dict):
|
301
|
+
for key, value in data.items():
|
302
|
+
new_strategy.insert(key, value)
|
303
|
+
elif isinstance(data, list):
|
304
|
+
for item in data:
|
305
|
+
new_strategy.insert(item, item)
|
306
|
+
return new_strategy
|
307
|
+
|
308
|
+
def get(self, path: str, default: Any = None) -> Any:
|
309
|
+
"""Get value by path."""
|
310
|
+
result = self.find(path)
|
311
|
+
return result if result is not None else default
|
312
|
+
|
313
|
+
def has(self, key: Any) -> bool:
|
314
|
+
"""Check if key exists."""
|
315
|
+
return str(key) in self._parent
|
316
|
+
|
317
|
+
def put(self, path: str, value: Any) -> 'UnionFindStrategy':
|
318
|
+
"""Put value at path."""
|
319
|
+
self.insert(path, value)
|
320
|
+
return self
|
321
|
+
|
322
|
+
def exists(self, path: str) -> bool:
|
323
|
+
"""Check if path exists."""
|
324
|
+
return path in self._union_find.parent
|
325
|
+
|
326
|
+
# Container protocol
|
327
|
+
def __len__(self) -> int:
|
328
|
+
"""Get length."""
|
329
|
+
return self._size
|
330
|
+
|
331
|
+
def __iter__(self) -> Iterator[Any]:
|
332
|
+
"""Iterate over elements."""
|
333
|
+
return self.keys()
|
334
|
+
|
335
|
+
def __getitem__(self, key: Any) -> Any:
|
336
|
+
"""Get item by key."""
|
337
|
+
result = self.find(key)
|
338
|
+
if result is None:
|
339
|
+
raise KeyError(str(key))
|
340
|
+
return result
|
341
|
+
|
342
|
+
def __setitem__(self, key: Any, value: Any) -> None:
|
343
|
+
"""Set item by key."""
|
344
|
+
self.insert(key, value)
|
345
|
+
|
346
|
+
def __contains__(self, key: Any) -> bool:
|
347
|
+
"""Check if key exists."""
|
348
|
+
return str(key) in self._union_find.parent
|
349
|
+
|
350
|
+
# Type checking properties
|
351
|
+
@property
|
352
|
+
def is_leaf(self) -> bool:
|
353
|
+
"""Check if this is a leaf node."""
|
354
|
+
return self._size == 0
|
355
|
+
|
356
|
+
@property
|
357
|
+
def is_list(self) -> bool:
|
358
|
+
"""Check if this is a list node."""
|
359
|
+
return False
|
360
|
+
|
361
|
+
@property
|
362
|
+
def is_dict(self) -> bool:
|
363
|
+
"""Check if this is a dict node."""
|
364
|
+
return True # Union-find is dict-like (maps elements to sets)
|
365
|
+
|
366
|
+
@property
|
367
|
+
def is_reference(self) -> bool:
|
368
|
+
"""Check if this is a reference node."""
|
369
|
+
return False
|
370
|
+
|
371
|
+
@property
|
372
|
+
def is_object(self) -> bool:
|
373
|
+
"""Check if this is an object node."""
|
374
|
+
return False
|
375
|
+
|
376
|
+
@property
|
377
|
+
def type(self) -> str:
|
378
|
+
"""Get the type of this node."""
|
379
|
+
return "union_find"
|
380
|
+
|
381
|
+
@property
|
382
|
+
def value(self) -> Any:
|
383
|
+
"""Get the value of this node."""
|
384
|
+
return self.to_native()
|
385
|
+
|
386
|
+
@property
|
387
|
+
def strategy_name(self) -> str:
|
388
|
+
"""Get strategy name."""
|
389
|
+
return "UNION_FIND"
|
390
|
+
|
391
|
+
@property
|
392
|
+
def supported_traits(self) -> NodeTrait:
|
393
|
+
"""Get supported traits."""
|
394
|
+
return self.get_supported_traits()
|