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.
Files changed (250) hide show
  1. exonware/__init__.py +8 -1
  2. exonware/xwnode/__init__.py +18 -5
  3. exonware/xwnode/add_strategy_types.py +165 -0
  4. exonware/xwnode/base.py +7 -5
  5. exonware/xwnode/common/__init__.py +1 -1
  6. exonware/xwnode/common/graph/__init__.py +30 -0
  7. exonware/xwnode/common/graph/caching.py +131 -0
  8. exonware/xwnode/common/graph/contracts.py +100 -0
  9. exonware/xwnode/common/graph/errors.py +44 -0
  10. exonware/xwnode/common/graph/indexing.py +260 -0
  11. exonware/xwnode/common/graph/manager.py +568 -0
  12. exonware/xwnode/common/management/__init__.py +3 -5
  13. exonware/xwnode/common/management/manager.py +9 -9
  14. exonware/xwnode/common/management/migration.py +6 -6
  15. exonware/xwnode/common/monitoring/__init__.py +3 -5
  16. exonware/xwnode/common/monitoring/metrics.py +7 -3
  17. exonware/xwnode/common/monitoring/pattern_detector.py +2 -2
  18. exonware/xwnode/common/monitoring/performance_monitor.py +6 -2
  19. exonware/xwnode/common/patterns/__init__.py +3 -5
  20. exonware/xwnode/common/patterns/advisor.py +1 -1
  21. exonware/xwnode/common/patterns/flyweight.py +6 -2
  22. exonware/xwnode/common/patterns/registry.py +203 -184
  23. exonware/xwnode/common/utils/__init__.py +25 -11
  24. exonware/xwnode/common/utils/simple.py +1 -1
  25. exonware/xwnode/config.py +3 -8
  26. exonware/xwnode/contracts.py +4 -105
  27. exonware/xwnode/defs.py +413 -159
  28. exonware/xwnode/edges/strategies/__init__.py +86 -4
  29. exonware/xwnode/edges/strategies/_base_edge.py +2 -2
  30. exonware/xwnode/edges/strategies/adj_list.py +287 -121
  31. exonware/xwnode/edges/strategies/adj_matrix.py +316 -222
  32. exonware/xwnode/edges/strategies/base.py +1 -1
  33. exonware/xwnode/edges/strategies/{edge_bidir_wrapper.py → bidir_wrapper.py} +45 -4
  34. exonware/xwnode/edges/strategies/bitemporal.py +520 -0
  35. exonware/xwnode/edges/strategies/{edge_block_adj_matrix.py → block_adj_matrix.py} +77 -6
  36. exonware/xwnode/edges/strategies/bv_graph.py +664 -0
  37. exonware/xwnode/edges/strategies/compressed_graph.py +217 -0
  38. exonware/xwnode/edges/strategies/{edge_coo.py → coo.py} +46 -4
  39. exonware/xwnode/edges/strategies/{edge_csc.py → csc.py} +45 -4
  40. exonware/xwnode/edges/strategies/{edge_csr.py → csr.py} +94 -12
  41. exonware/xwnode/edges/strategies/{edge_dynamic_adj_list.py → dynamic_adj_list.py} +46 -4
  42. exonware/xwnode/edges/strategies/edge_list.py +168 -0
  43. exonware/xwnode/edges/strategies/edge_property_store.py +2 -2
  44. exonware/xwnode/edges/strategies/euler_tour.py +560 -0
  45. exonware/xwnode/edges/strategies/{edge_flow_network.py → flow_network.py} +2 -2
  46. exonware/xwnode/edges/strategies/graphblas.py +449 -0
  47. exonware/xwnode/edges/strategies/hnsw.py +637 -0
  48. exonware/xwnode/edges/strategies/hop2_labels.py +467 -0
  49. exonware/xwnode/edges/strategies/{edge_hyperedge_set.py → hyperedge_set.py} +2 -2
  50. exonware/xwnode/edges/strategies/incidence_matrix.py +250 -0
  51. exonware/xwnode/edges/strategies/k2_tree.py +613 -0
  52. exonware/xwnode/edges/strategies/link_cut.py +626 -0
  53. exonware/xwnode/edges/strategies/multiplex.py +532 -0
  54. exonware/xwnode/edges/strategies/{edge_neural_graph.py → neural_graph.py} +2 -2
  55. exonware/xwnode/edges/strategies/{edge_octree.py → octree.py} +69 -11
  56. exonware/xwnode/edges/strategies/{edge_quadtree.py → quadtree.py} +66 -10
  57. exonware/xwnode/edges/strategies/roaring_adj.py +438 -0
  58. exonware/xwnode/edges/strategies/{edge_rtree.py → rtree.py} +43 -5
  59. exonware/xwnode/edges/strategies/{edge_temporal_edgeset.py → temporal_edgeset.py} +24 -5
  60. exonware/xwnode/edges/strategies/{edge_tree_graph_basic.py → tree_graph_basic.py} +78 -7
  61. exonware/xwnode/edges/strategies/{edge_weighted_graph.py → weighted_graph.py} +188 -10
  62. exonware/xwnode/errors.py +3 -6
  63. exonware/xwnode/facade.py +20 -20
  64. exonware/xwnode/nodes/strategies/__init__.py +29 -9
  65. exonware/xwnode/nodes/strategies/adjacency_list.py +650 -177
  66. exonware/xwnode/nodes/strategies/aho_corasick.py +358 -183
  67. exonware/xwnode/nodes/strategies/array_list.py +36 -3
  68. exonware/xwnode/nodes/strategies/art.py +581 -0
  69. exonware/xwnode/nodes/strategies/{node_avl_tree.py → avl_tree.py} +77 -6
  70. exonware/xwnode/nodes/strategies/{node_b_plus_tree.py → b_plus_tree.py} +81 -40
  71. exonware/xwnode/nodes/strategies/{node_btree.py → b_tree.py} +79 -9
  72. exonware/xwnode/nodes/strategies/base.py +469 -98
  73. exonware/xwnode/nodes/strategies/{node_bitmap.py → bitmap.py} +12 -12
  74. exonware/xwnode/nodes/strategies/{node_bitset_dynamic.py → bitset_dynamic.py} +11 -11
  75. exonware/xwnode/nodes/strategies/{node_bloom_filter.py → bloom_filter.py} +15 -2
  76. exonware/xwnode/nodes/strategies/bloomier_filter.py +519 -0
  77. exonware/xwnode/nodes/strategies/bw_tree.py +531 -0
  78. exonware/xwnode/nodes/strategies/contracts.py +1 -1
  79. exonware/xwnode/nodes/strategies/{node_count_min_sketch.py → count_min_sketch.py} +3 -2
  80. exonware/xwnode/nodes/strategies/{node_cow_tree.py → cow_tree.py} +135 -13
  81. exonware/xwnode/nodes/strategies/crdt_map.py +629 -0
  82. exonware/xwnode/nodes/strategies/{node_cuckoo_hash.py → cuckoo_hash.py} +2 -2
  83. exonware/xwnode/nodes/strategies/{node_xdata_optimized.py → data_interchange_optimized.py} +21 -4
  84. exonware/xwnode/nodes/strategies/dawg.py +876 -0
  85. exonware/xwnode/nodes/strategies/deque.py +321 -153
  86. exonware/xwnode/nodes/strategies/extendible_hash.py +93 -0
  87. exonware/xwnode/nodes/strategies/{node_fenwick_tree.py → fenwick_tree.py} +111 -19
  88. exonware/xwnode/nodes/strategies/hamt.py +403 -0
  89. exonware/xwnode/nodes/strategies/hash_map.py +354 -67
  90. exonware/xwnode/nodes/strategies/heap.py +105 -5
  91. exonware/xwnode/nodes/strategies/hopscotch_hash.py +525 -0
  92. exonware/xwnode/nodes/strategies/{node_hyperloglog.py → hyperloglog.py} +6 -5
  93. exonware/xwnode/nodes/strategies/interval_tree.py +742 -0
  94. exonware/xwnode/nodes/strategies/kd_tree.py +703 -0
  95. exonware/xwnode/nodes/strategies/learned_index.py +533 -0
  96. exonware/xwnode/nodes/strategies/linear_hash.py +93 -0
  97. exonware/xwnode/nodes/strategies/linked_list.py +316 -119
  98. exonware/xwnode/nodes/strategies/{node_lsm_tree.py → lsm_tree.py} +219 -15
  99. exonware/xwnode/nodes/strategies/masstree.py +130 -0
  100. exonware/xwnode/nodes/strategies/{node_persistent_tree.py → persistent_tree.py} +149 -9
  101. exonware/xwnode/nodes/strategies/priority_queue.py +544 -132
  102. exonware/xwnode/nodes/strategies/queue.py +249 -120
  103. exonware/xwnode/nodes/strategies/{node_red_black_tree.py → red_black_tree.py} +183 -72
  104. exonware/xwnode/nodes/strategies/{node_roaring_bitmap.py → roaring_bitmap.py} +19 -6
  105. exonware/xwnode/nodes/strategies/rope.py +717 -0
  106. exonware/xwnode/nodes/strategies/{node_segment_tree.py → segment_tree.py} +106 -106
  107. exonware/xwnode/nodes/strategies/{node_set_hash.py → set_hash.py} +30 -29
  108. exonware/xwnode/nodes/strategies/{node_skip_list.py → skip_list.py} +74 -6
  109. exonware/xwnode/nodes/strategies/sparse_matrix.py +427 -131
  110. exonware/xwnode/nodes/strategies/{node_splay_tree.py → splay_tree.py} +55 -6
  111. exonware/xwnode/nodes/strategies/stack.py +244 -112
  112. exonware/xwnode/nodes/strategies/{node_suffix_array.py → suffix_array.py} +5 -1
  113. exonware/xwnode/nodes/strategies/t_tree.py +94 -0
  114. exonware/xwnode/nodes/strategies/{node_treap.py → treap.py} +75 -6
  115. exonware/xwnode/nodes/strategies/{node_tree_graph_hybrid.py → tree_graph_hybrid.py} +46 -5
  116. exonware/xwnode/nodes/strategies/trie.py +153 -9
  117. exonware/xwnode/nodes/strategies/union_find.py +111 -5
  118. exonware/xwnode/nodes/strategies/veb_tree.py +856 -0
  119. exonware/xwnode/strategies/__init__.py +5 -51
  120. exonware/xwnode/version.py +3 -3
  121. {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/METADATA +23 -3
  122. exonware_xwnode-0.0.1.23.dist-info/RECORD +130 -0
  123. exonware/xwnode/edges/strategies/edge_adj_list.py +0 -353
  124. exonware/xwnode/edges/strategies/edge_adj_matrix.py +0 -445
  125. exonware/xwnode/nodes/strategies/_base_node.py +0 -307
  126. exonware/xwnode/nodes/strategies/node_aho_corasick.py +0 -525
  127. exonware/xwnode/nodes/strategies/node_array_list.py +0 -179
  128. exonware/xwnode/nodes/strategies/node_hash_map.py +0 -273
  129. exonware/xwnode/nodes/strategies/node_heap.py +0 -196
  130. exonware/xwnode/nodes/strategies/node_linked_list.py +0 -413
  131. exonware/xwnode/nodes/strategies/node_trie.py +0 -257
  132. exonware/xwnode/nodes/strategies/node_union_find.py +0 -192
  133. exonware/xwnode/queries/executors/__init__.py +0 -47
  134. exonware/xwnode/queries/executors/advanced/__init__.py +0 -37
  135. exonware/xwnode/queries/executors/advanced/aggregate_executor.py +0 -50
  136. exonware/xwnode/queries/executors/advanced/ask_executor.py +0 -50
  137. exonware/xwnode/queries/executors/advanced/construct_executor.py +0 -50
  138. exonware/xwnode/queries/executors/advanced/describe_executor.py +0 -50
  139. exonware/xwnode/queries/executors/advanced/for_loop_executor.py +0 -50
  140. exonware/xwnode/queries/executors/advanced/foreach_executor.py +0 -50
  141. exonware/xwnode/queries/executors/advanced/join_executor.py +0 -50
  142. exonware/xwnode/queries/executors/advanced/let_executor.py +0 -50
  143. exonware/xwnode/queries/executors/advanced/mutation_executor.py +0 -50
  144. exonware/xwnode/queries/executors/advanced/options_executor.py +0 -50
  145. exonware/xwnode/queries/executors/advanced/pipe_executor.py +0 -50
  146. exonware/xwnode/queries/executors/advanced/subscribe_executor.py +0 -50
  147. exonware/xwnode/queries/executors/advanced/subscription_executor.py +0 -50
  148. exonware/xwnode/queries/executors/advanced/union_executor.py +0 -50
  149. exonware/xwnode/queries/executors/advanced/window_executor.py +0 -51
  150. exonware/xwnode/queries/executors/advanced/with_cte_executor.py +0 -50
  151. exonware/xwnode/queries/executors/aggregation/__init__.py +0 -21
  152. exonware/xwnode/queries/executors/aggregation/avg_executor.py +0 -50
  153. exonware/xwnode/queries/executors/aggregation/count_executor.py +0 -38
  154. exonware/xwnode/queries/executors/aggregation/distinct_executor.py +0 -50
  155. exonware/xwnode/queries/executors/aggregation/group_executor.py +0 -50
  156. exonware/xwnode/queries/executors/aggregation/having_executor.py +0 -50
  157. exonware/xwnode/queries/executors/aggregation/max_executor.py +0 -50
  158. exonware/xwnode/queries/executors/aggregation/min_executor.py +0 -50
  159. exonware/xwnode/queries/executors/aggregation/sum_executor.py +0 -50
  160. exonware/xwnode/queries/executors/aggregation/summarize_executor.py +0 -50
  161. exonware/xwnode/queries/executors/array/__init__.py +0 -9
  162. exonware/xwnode/queries/executors/array/indexing_executor.py +0 -51
  163. exonware/xwnode/queries/executors/array/slicing_executor.py +0 -51
  164. exonware/xwnode/queries/executors/base.py +0 -257
  165. exonware/xwnode/queries/executors/capability_checker.py +0 -204
  166. exonware/xwnode/queries/executors/contracts.py +0 -166
  167. exonware/xwnode/queries/executors/core/__init__.py +0 -17
  168. exonware/xwnode/queries/executors/core/create_executor.py +0 -96
  169. exonware/xwnode/queries/executors/core/delete_executor.py +0 -99
  170. exonware/xwnode/queries/executors/core/drop_executor.py +0 -100
  171. exonware/xwnode/queries/executors/core/insert_executor.py +0 -39
  172. exonware/xwnode/queries/executors/core/select_executor.py +0 -152
  173. exonware/xwnode/queries/executors/core/update_executor.py +0 -102
  174. exonware/xwnode/queries/executors/data/__init__.py +0 -13
  175. exonware/xwnode/queries/executors/data/alter_executor.py +0 -50
  176. exonware/xwnode/queries/executors/data/load_executor.py +0 -50
  177. exonware/xwnode/queries/executors/data/merge_executor.py +0 -50
  178. exonware/xwnode/queries/executors/data/store_executor.py +0 -50
  179. exonware/xwnode/queries/executors/defs.py +0 -93
  180. exonware/xwnode/queries/executors/engine.py +0 -221
  181. exonware/xwnode/queries/executors/errors.py +0 -68
  182. exonware/xwnode/queries/executors/filtering/__init__.py +0 -25
  183. exonware/xwnode/queries/executors/filtering/between_executor.py +0 -80
  184. exonware/xwnode/queries/executors/filtering/filter_executor.py +0 -79
  185. exonware/xwnode/queries/executors/filtering/has_executor.py +0 -70
  186. exonware/xwnode/queries/executors/filtering/in_executor.py +0 -70
  187. exonware/xwnode/queries/executors/filtering/like_executor.py +0 -76
  188. exonware/xwnode/queries/executors/filtering/optional_executor.py +0 -76
  189. exonware/xwnode/queries/executors/filtering/range_executor.py +0 -80
  190. exonware/xwnode/queries/executors/filtering/term_executor.py +0 -77
  191. exonware/xwnode/queries/executors/filtering/values_executor.py +0 -71
  192. exonware/xwnode/queries/executors/filtering/where_executor.py +0 -44
  193. exonware/xwnode/queries/executors/graph/__init__.py +0 -15
  194. exonware/xwnode/queries/executors/graph/in_traverse_executor.py +0 -51
  195. exonware/xwnode/queries/executors/graph/match_executor.py +0 -51
  196. exonware/xwnode/queries/executors/graph/out_executor.py +0 -51
  197. exonware/xwnode/queries/executors/graph/path_executor.py +0 -51
  198. exonware/xwnode/queries/executors/graph/return_executor.py +0 -51
  199. exonware/xwnode/queries/executors/ordering/__init__.py +0 -9
  200. exonware/xwnode/queries/executors/ordering/by_executor.py +0 -50
  201. exonware/xwnode/queries/executors/ordering/order_executor.py +0 -51
  202. exonware/xwnode/queries/executors/projection/__init__.py +0 -9
  203. exonware/xwnode/queries/executors/projection/extend_executor.py +0 -50
  204. exonware/xwnode/queries/executors/projection/project_executor.py +0 -50
  205. exonware/xwnode/queries/executors/registry.py +0 -173
  206. exonware/xwnode/queries/parsers/__init__.py +0 -26
  207. exonware/xwnode/queries/parsers/base.py +0 -86
  208. exonware/xwnode/queries/parsers/contracts.py +0 -46
  209. exonware/xwnode/queries/parsers/errors.py +0 -53
  210. exonware/xwnode/queries/parsers/sql_param_extractor.py +0 -318
  211. exonware/xwnode/queries/strategies/__init__.py +0 -24
  212. exonware/xwnode/queries/strategies/base.py +0 -236
  213. exonware/xwnode/queries/strategies/cql.py +0 -201
  214. exonware/xwnode/queries/strategies/cypher.py +0 -181
  215. exonware/xwnode/queries/strategies/datalog.py +0 -70
  216. exonware/xwnode/queries/strategies/elastic_dsl.py +0 -70
  217. exonware/xwnode/queries/strategies/eql.py +0 -70
  218. exonware/xwnode/queries/strategies/flux.py +0 -70
  219. exonware/xwnode/queries/strategies/gql.py +0 -70
  220. exonware/xwnode/queries/strategies/graphql.py +0 -240
  221. exonware/xwnode/queries/strategies/gremlin.py +0 -181
  222. exonware/xwnode/queries/strategies/hiveql.py +0 -214
  223. exonware/xwnode/queries/strategies/hql.py +0 -70
  224. exonware/xwnode/queries/strategies/jmespath.py +0 -219
  225. exonware/xwnode/queries/strategies/jq.py +0 -66
  226. exonware/xwnode/queries/strategies/json_query.py +0 -66
  227. exonware/xwnode/queries/strategies/jsoniq.py +0 -248
  228. exonware/xwnode/queries/strategies/kql.py +0 -70
  229. exonware/xwnode/queries/strategies/linq.py +0 -238
  230. exonware/xwnode/queries/strategies/logql.py +0 -70
  231. exonware/xwnode/queries/strategies/mql.py +0 -68
  232. exonware/xwnode/queries/strategies/n1ql.py +0 -210
  233. exonware/xwnode/queries/strategies/partiql.py +0 -70
  234. exonware/xwnode/queries/strategies/pig.py +0 -215
  235. exonware/xwnode/queries/strategies/promql.py +0 -70
  236. exonware/xwnode/queries/strategies/sparql.py +0 -220
  237. exonware/xwnode/queries/strategies/sql.py +0 -275
  238. exonware/xwnode/queries/strategies/xml_query.py +0 -66
  239. exonware/xwnode/queries/strategies/xpath.py +0 -223
  240. exonware/xwnode/queries/strategies/xquery.py +0 -258
  241. exonware/xwnode/queries/strategies/xwnode_executor.py +0 -332
  242. exonware/xwnode/queries/strategies/xwquery.py +0 -456
  243. exonware_xwnode-0.0.1.21.dist-info/RECORD +0 -214
  244. /exonware/xwnode/nodes/strategies/{node_ordered_map.py → ordered_map.py} +0 -0
  245. /exonware/xwnode/nodes/strategies/{node_ordered_map_balanced.py → ordered_map_balanced.py} +0 -0
  246. /exonware/xwnode/nodes/strategies/{node_patricia.py → patricia.py} +0 -0
  247. /exonware/xwnode/nodes/strategies/{node_radix_trie.py → radix_trie.py} +0 -0
  248. /exonware/xwnode/nodes/strategies/{node_set_tree.py → set_tree.py} +0 -0
  249. {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/WHEEL +0 -0
  250. {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/licenses/LICENSE +0 -0
@@ -13,15 +13,72 @@ from ...defs import NodeMode, NodeTrait
13
13
 
14
14
  class FenwickTreeStrategy(ANodeTreeStrategy):
15
15
  """
16
- Fenwick Tree node strategy for efficient prefix sum operations.
17
-
18
- Also known as Binary Indexed Tree (BIT), provides O(log n) prefix sum
19
- queries and point updates with
16
+ Fenwick Tree (Binary Indexed Tree) strategy for efficient prefix sums.
17
+
18
+ WHY Fenwick Tree:
19
+ - Simpler than Segment Tree for prefix sum queries
20
+ - O(log n) for both query and update (vs O(n) for naive)
21
+ - Minimal memory overhead: just O(n) array
22
+ - Elegant bit manipulation (lowest set bit operations)
23
+ - Industry standard for competitive programming
24
+
25
+ WHY this implementation:
26
+ - 1-indexed array (standard for Fenwick/BIT - simplifies bit ops)
27
+ - Lowest set bit technique: `idx & -idx` for parent/child navigation
28
+ - Delta-based updates (add/subtract differences)
29
+ - Supports cumulative frequency applications
30
+
31
+ Time Complexity:
32
+ - Prefix Sum: O(log n) - traverse parents via bit operations
33
+ - Range Sum: O(log n) - difference of two prefix sums
34
+ - Point Update: O(log n) - update ancestors via bit operations
35
+ - Build: O(n log n) - n updates
36
+
37
+ Space Complexity: O(n) - single array, very memory efficient
38
+
39
+ Trade-offs:
40
+ - Advantage: Simpler than Segment Tree (fewer lines of code)
41
+ - Advantage: Lower memory overhead than Segment Tree
42
+ - Limitation: Only supports PREFIX operations (not arbitrary ranges as efficiently)
43
+ - Limitation: Requires associative, invertible operations (sum, XOR work; min/max don't)
44
+ - Compared to Segment Tree: Simpler and faster for prefix sums, less flexible
45
+ - Compared to prefix sum array: Dynamic updates O(log n) vs O(n)
46
+
47
+ Best for:
48
+ - Prefix sum queries (cumulative frequencies)
49
+ - Competitive programming problems
50
+ - When space is limited (O(n) vs O(4n) for Segment Tree)
51
+ - Invertible operations (sum, XOR, but NOT min/max)
52
+ - Dynamic arrays requiring frequent sum queries
53
+
54
+ Not recommended for:
55
+ - Non-invertible operations (min, max, GCD) - use Segment Tree
56
+ - When arbitrary range operations are primary need
57
+ - When simpler solutions suffice (e.g., static prefix array)
58
+ - 2D/multidimensional ranges (use 2D Fenwick or Segment Tree)
59
+
60
+ Following eXonware Priorities:
61
+ 1. Usability: Clean API for prefix/range sums
62
+ 2. Maintainability: Simple bit manipulation logic, well-documented
63
+ 3. Performance: O(log n) operations with low constants
64
+ 4. Extensibility: Can extend to 2D Fenwick Tree
65
+ 5. Security: Input validation on all operations
66
+
67
+ Industry Best Practices:
68
+ - Follows Peter Fenwick's original paper (1994)
69
+ - Uses 1-indexed array (standard for BIT)
70
+ - Lowest set bit operations: `i & -i`
71
+ - Parent navigation: `i += i & -i`
72
+ - Child navigation: `i -= i & -i`
73
+
74
+ Performance Note:
75
+ Fenwick Trees excel at prefix sums with O(log n) query and update.
76
+ For arbitrary range operations (especially min/max), use Segment Tree.
77
+ Trade-off: Simplicity (Fenwick) vs Flexibility (Segment Tree).
78
+ """
20
79
 
21
80
  # Strategy type classification
22
81
  STRATEGY_TYPE = NodeType.TREE
23
- minimal memory overhead.
24
- """
25
82
 
26
83
  def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
27
84
  """Initialize the Fenwick Tree strategy."""
@@ -159,6 +216,34 @@ minimal memory overhead.
159
216
  new_size = old_size * 2
160
217
  self._tree.extend([0.0] * (new_size - old_size))
161
218
 
219
+ def update(self, index: int, value: float) -> None:
220
+ """
221
+ Update value at specific index (1-indexed).
222
+
223
+ Sets the value at index to the new value by calculating delta.
224
+ """
225
+ if index < 1:
226
+ raise ValueError(f"Fenwick Tree indices must be >= 1, got {index}")
227
+
228
+ # Ensure tree is large enough
229
+ while index >= len(self._tree):
230
+ self._resize_tree()
231
+
232
+ # Calculate delta from current value
233
+ current = self._get_point_value(index)
234
+ delta = value - current
235
+
236
+ # Update tree with delta
237
+ self._update_point(index, delta)
238
+
239
+ # Store for retrieval
240
+ key = str(index)
241
+ if key not in self._indices:
242
+ self._indices[key] = index
243
+ self._reverse_indices[index] = key
244
+ self._size += 1
245
+ self._values[key] = value
246
+
162
247
  def _update_point(self, idx: int, delta: float) -> None:
163
248
  """Add delta to position idx (1-indexed)."""
164
249
  while idx < len(self._tree):
@@ -184,25 +269,32 @@ minimal memory overhead.
184
269
  return result
185
270
 
186
271
  def prefix_sum(self, index: int) -> float:
187
- """Public method: get prefix sum from start to index."""
188
- if index < 0 or index >= self._size:
272
+ """
273
+ Get prefix sum from 1 to index (1-indexed, inclusive).
274
+
275
+ For Fenwick Tree, index 1 is the first element.
276
+ To query sum of first 3 elements, use prefix_sum(3).
277
+ """
278
+ if index < 1:
189
279
  return 0.0
190
280
 
191
- # Convert 0-indexed to 1-indexed
192
- return self._prefix_sum(index + 1)
281
+ # Ensure index is within bounds
282
+ if index >= len(self._tree):
283
+ index = len(self._tree) - 1
284
+
285
+ return self._prefix_sum(index)
193
286
 
194
287
  def range_sum(self, left: int, right: int) -> float:
195
- """Get sum of elements in range [left, right] (0-indexed)."""
196
- if left > right or right < 0 or left >= self._size:
197
- return 0.0
288
+ """
289
+ Get sum of elements in range [left, right] (1-indexed, inclusive).
198
290
 
199
- left = max(0, left)
200
- right = min(self._size - 1, right)
291
+ For Fenwick Tree, indices start at 1 (standard for BIT).
292
+ To query sum of elements 2-5, use range_sum(2, 5).
293
+ """
294
+ if left > right or left < 1:
295
+ return 0.0
201
296
 
202
- if left == 0:
203
- return self._prefix_sum(right + 1)
204
- else:
205
- return self._prefix_sum(right + 1) - self._prefix_sum(left)
297
+ return self._prefix_sum(right) - self._prefix_sum(left - 1)
206
298
 
207
299
  def point_update(self, index: int, new_value: float) -> None:
208
300
  """Update value at index (0-indexed)."""
@@ -0,0 +1,403 @@
1
+ """
2
+ #exonware/xwnode/src/exonware/xwnode/nodes/strategies/node_hamt.py
3
+
4
+ HAMT (Hash Array Mapped Trie) Node Strategy Implementation
5
+
6
+ This module implements the HAMT strategy for persistent data structures
7
+ with structural sharing and efficient immutable operations.
8
+
9
+ Company: eXonware.com
10
+ Author: Eng. Muhammad AlShehri
11
+ Email: connect@exonware.com
12
+ Version: 0.0.1.23
13
+ Generation Date: 11-Oct-2025
14
+ """
15
+
16
+ from typing import Any, Iterator, Dict, List, Optional, Union
17
+ from .base import ANodeStrategy
18
+ from ...defs import NodeMode, NodeTrait
19
+ from .contracts import NodeType
20
+ from ...common.utils import (
21
+ safe_to_native_conversion,
22
+ create_basic_metrics,
23
+ create_basic_backend_info,
24
+ create_size_tracker,
25
+ create_access_tracker,
26
+ update_size_tracker,
27
+ record_access,
28
+ get_access_metrics
29
+ )
30
+
31
+
32
+ class HAMTNode:
33
+ """
34
+ HAMT Node with bitmap-based indexing.
35
+
36
+ Each node uses a 32-bit bitmap to track which slots are occupied,
37
+ and stores only the occupied slots in a compact array.
38
+ """
39
+
40
+ def __init__(self):
41
+ self.bitmap: int = 0 # 32-bit bitmap for tracking occupied slots
42
+ self.children: List[Any] = [] # Compact array of children/values
43
+
44
+ def index_for_bit(self, bit_pos: int) -> int:
45
+ """Calculate array index for given bit position using popcount."""
46
+ # Count number of 1s before bit_pos
47
+ mask = (1 << bit_pos) - 1
48
+ return bin(self.bitmap & mask).count('1')
49
+
50
+ def has_child(self, bit_pos: int) -> bool:
51
+ """Check if child exists at bit position."""
52
+ return (self.bitmap & (1 << bit_pos)) != 0
53
+
54
+ def get_child(self, bit_pos: int) -> Optional[Any]:
55
+ """Get child at bit position."""
56
+ if not self.has_child(bit_pos):
57
+ return None
58
+ idx = self.index_for_bit(bit_pos)
59
+ return self.children[idx]
60
+
61
+ def set_child(self, bit_pos: int, value: Any) -> 'HAMTNode':
62
+ """
63
+ Set child at bit position (immutable - returns new node).
64
+
65
+ This creates a new node with structural sharing.
66
+ """
67
+ new_node = HAMTNode()
68
+ new_node.bitmap = self.bitmap
69
+ new_node.children = self.children.copy()
70
+
71
+ if self.has_child(bit_pos):
72
+ # Update existing child
73
+ idx = self.index_for_bit(bit_pos)
74
+ new_node.children[idx] = value
75
+ else:
76
+ # Insert new child
77
+ new_node.bitmap |= (1 << bit_pos)
78
+ idx = self.index_for_bit(bit_pos)
79
+ new_node.children.insert(idx, value)
80
+
81
+ return new_node
82
+
83
+ def remove_child(self, bit_pos: int) -> 'HAMTNode':
84
+ """Remove child at bit position (immutable - returns new node)."""
85
+ if not self.has_child(bit_pos):
86
+ return self # No change
87
+
88
+ new_node = HAMTNode()
89
+ new_node.bitmap = self.bitmap & ~(1 << bit_pos)
90
+ new_node.children = self.children.copy()
91
+ idx = self.index_for_bit(bit_pos)
92
+ new_node.children.pop(idx)
93
+
94
+ return new_node
95
+
96
+
97
+ class HAMTLeaf:
98
+ """Leaf node storing key-value pair."""
99
+
100
+ def __init__(self, key: Any, value: Any):
101
+ self.key = key
102
+ self.value = value
103
+
104
+
105
+ class HAMTStrategy(ANodeStrategy):
106
+ """
107
+ HAMT (Hash Array Mapped Trie) - Persistent data structure.
108
+
109
+ HAMT is a persistent hash table that uses structural sharing
110
+ for efficient immutable operations. Popular in functional
111
+ programming languages like Clojure, Scala, and Haskell.
112
+
113
+ Features:
114
+ - Persistent/immutable operations
115
+ - Structural sharing (memory efficient)
116
+ - O(log32 n) operations (5-bit chunks)
117
+ - Cache-friendly bitmap indexing
118
+ - No rehashing needed
119
+
120
+ Best for:
121
+ - Functional programming
122
+ - Immutable data structures
123
+ - Version control systems
124
+ - Undo/redo functionality
125
+ - Concurrent read-heavy workloads
126
+ """
127
+
128
+ # Strategy type classification
129
+ STRATEGY_TYPE = NodeType.TREE
130
+
131
+ # HAMT constants
132
+ BITS_PER_LEVEL = 5 # 2^5 = 32 children per node
133
+ LEVEL_MASK = 0x1F # 0b11111
134
+
135
+ def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
136
+ """Initialize the HAMT strategy."""
137
+ super().__init__(NodeMode.HAMT, traits, **options)
138
+ self._root: HAMTNode = HAMTNode()
139
+ self._size = 0
140
+ self._size_tracker = create_size_tracker()
141
+ self._access_tracker = create_access_tracker()
142
+
143
+ def get_supported_traits(self) -> NodeTrait:
144
+ """Get the traits supported by HAMT strategy."""
145
+ return NodeTrait.INDEXED | NodeTrait.PERSISTENT
146
+
147
+ # ============================================================================
148
+ # CORE OPERATIONS
149
+ # ============================================================================
150
+
151
+ def _hash_key(self, key: Any) -> int:
152
+ """Compute hash for key."""
153
+ return hash(str(key)) & 0xFFFFFFFF # 32-bit hash
154
+
155
+ def _get_chunk(self, hash_val: int, level: int) -> int:
156
+ """Extract 5-bit chunk at given level."""
157
+ shift = level * self.BITS_PER_LEVEL
158
+ return (hash_val >> shift) & self.LEVEL_MASK
159
+
160
+ def _search(self, node: HAMTNode, key: Any, hash_val: int, level: int) -> Optional[Any]:
161
+ """Recursively search for key in HAMT."""
162
+ chunk = self._get_chunk(hash_val, level)
163
+
164
+ if not node.has_child(chunk):
165
+ return None
166
+
167
+ child = node.get_child(chunk)
168
+
169
+ if isinstance(child, HAMTLeaf):
170
+ if child.key == key:
171
+ return child.value
172
+ return None
173
+ elif isinstance(child, HAMTNode):
174
+ return self._search(child, key, hash_val, level + 1)
175
+
176
+ return None
177
+
178
+ def get(self, path: str, default: Any = None) -> Any:
179
+ """Retrieve a value by path."""
180
+ record_access(self._access_tracker, 'get_count')
181
+
182
+ if '.' in path:
183
+ # Handle nested paths
184
+ parts = path.split('.')
185
+ current = self.get(parts[0])
186
+ for part in parts[1:]:
187
+ if isinstance(current, dict) and part in current:
188
+ current = current[part]
189
+ else:
190
+ return default
191
+ return current
192
+
193
+ hash_val = self._hash_key(path)
194
+ result = self._search(self._root, path, hash_val, 0)
195
+ return result if result is not None else default
196
+
197
+ def _insert(self, node: HAMTNode, key: Any, value: Any,
198
+ hash_val: int, level: int) -> HAMTNode:
199
+ """
200
+ Recursively insert key-value pair (immutable).
201
+
202
+ Returns new node with structural sharing.
203
+ """
204
+ chunk = self._get_chunk(hash_val, level)
205
+
206
+ if not node.has_child(chunk):
207
+ # Empty slot - create leaf
208
+ leaf = HAMTLeaf(key, value)
209
+ return node.set_child(chunk, leaf)
210
+
211
+ child = node.get_child(chunk)
212
+
213
+ if isinstance(child, HAMTLeaf):
214
+ if child.key == key:
215
+ # Update existing key
216
+ new_leaf = HAMTLeaf(key, value)
217
+ return node.set_child(chunk, new_leaf)
218
+ else:
219
+ # Hash collision - create sub-node
220
+ new_subnode = HAMTNode()
221
+
222
+ # Insert existing leaf
223
+ existing_hash = self._hash_key(child.key)
224
+ existing_chunk = self._get_chunk(existing_hash, level + 1)
225
+ new_subnode = new_subnode.set_child(existing_chunk, child)
226
+
227
+ # Insert new leaf
228
+ new_chunk = self._get_chunk(hash_val, level + 1)
229
+ new_leaf = HAMTLeaf(key, value)
230
+ new_subnode = new_subnode.set_child(new_chunk, new_leaf)
231
+
232
+ return node.set_child(chunk, new_subnode)
233
+
234
+ elif isinstance(child, HAMTNode):
235
+ # Recurse into sub-node
236
+ new_child = self._insert(child, key, value, hash_val, level + 1)
237
+ return node.set_child(chunk, new_child)
238
+
239
+ return node
240
+
241
+ def put(self, path: str, value: Any = None) -> 'HAMTStrategy':
242
+ """Set a value at path (immutable operation)."""
243
+ record_access(self._access_tracker, 'put_count')
244
+
245
+ if '.' in path:
246
+ # Handle nested paths
247
+ parts = path.split('.')
248
+ root = self.get(parts[0])
249
+ if root is None:
250
+ root = {}
251
+ elif not isinstance(root, dict):
252
+ root = {parts[0]: root}
253
+
254
+ current = root
255
+ for part in parts[1:-1]:
256
+ if part not in current:
257
+ current[part] = {}
258
+ current = current[part]
259
+ current[parts[-1]] = value
260
+
261
+ # Create new root (structural sharing)
262
+ hash_val = self._hash_key(parts[0])
263
+ self._root = self._insert(self._root, parts[0], root, hash_val, 0)
264
+ else:
265
+ exists = self.exists(path)
266
+ hash_val = self._hash_key(path)
267
+ self._root = self._insert(self._root, path, value, hash_val, 0)
268
+
269
+ if not exists:
270
+ update_size_tracker(self._size_tracker, 1)
271
+ self._size += 1
272
+
273
+ return self
274
+
275
+ def _remove(self, node: HAMTNode, key: Any, hash_val: int, level: int) -> Optional[HAMTNode]:
276
+ """Recursively remove key (immutable)."""
277
+ chunk = self._get_chunk(hash_val, level)
278
+
279
+ if not node.has_child(chunk):
280
+ return node # Key not found
281
+
282
+ child = node.get_child(chunk)
283
+
284
+ if isinstance(child, HAMTLeaf):
285
+ if child.key == key:
286
+ # Remove leaf
287
+ return node.remove_child(chunk)
288
+ return node # Different key
289
+
290
+ elif isinstance(child, HAMTNode):
291
+ # Recurse
292
+ new_child = self._remove(child, key, hash_val, level + 1)
293
+ if new_child is None or (new_child.bitmap == 0):
294
+ # Child is now empty, remove it
295
+ return node.remove_child(chunk)
296
+ else:
297
+ return node.set_child(chunk, new_child)
298
+
299
+ return node
300
+
301
+ def delete(self, key: Any) -> bool:
302
+ """Remove a key-value pair (immutable operation)."""
303
+ key_str = str(key)
304
+ if self.exists(key_str):
305
+ hash_val = self._hash_key(key_str)
306
+ self._root = self._remove(self._root, key_str, hash_val, 0)
307
+ update_size_tracker(self._size_tracker, -1)
308
+ record_access(self._access_tracker, 'delete_count')
309
+ self._size -= 1
310
+ return True
311
+ return False
312
+
313
+ def remove(self, key: Any) -> bool:
314
+ """Remove a key-value pair (alias for delete)."""
315
+ return self.delete(key)
316
+
317
+ def has(self, key: Any) -> bool:
318
+ """Check if key exists."""
319
+ return self.get(str(key)) is not None
320
+
321
+ def exists(self, path: str) -> bool:
322
+ """Check if path exists."""
323
+ return self.get(path) is not None
324
+
325
+ # ============================================================================
326
+ # ITERATION METHODS
327
+ # ============================================================================
328
+
329
+ def _collect_all(self, node: HAMTNode) -> List[tuple[Any, Any]]:
330
+ """Collect all key-value pairs from HAMT."""
331
+ results = []
332
+
333
+ for child in node.children:
334
+ if isinstance(child, HAMTLeaf):
335
+ results.append((child.key, child.value))
336
+ elif isinstance(child, HAMTNode):
337
+ results.extend(self._collect_all(child))
338
+
339
+ return results
340
+
341
+ def keys(self) -> Iterator[Any]:
342
+ """Get an iterator over all keys."""
343
+ for key, _ in self._collect_all(self._root):
344
+ yield key
345
+
346
+ def values(self) -> Iterator[Any]:
347
+ """Get an iterator over all values."""
348
+ for _, value in self._collect_all(self._root):
349
+ yield value
350
+
351
+ def items(self) -> Iterator[tuple[Any, Any]]:
352
+ """Get an iterator over all key-value pairs."""
353
+ for item in self._collect_all(self._root):
354
+ yield item
355
+
356
+ def __len__(self) -> int:
357
+ """Get the number of key-value pairs."""
358
+ return self._size
359
+
360
+ # ============================================================================
361
+ # ADVANCED FEATURES
362
+ # ============================================================================
363
+
364
+ def snapshot(self) -> 'HAMTStrategy':
365
+ """
366
+ Create immutable snapshot of current state.
367
+
368
+ Due to structural sharing, this is very efficient (O(1)).
369
+ """
370
+ new_strategy = HAMTStrategy(self.traits, **self.options)
371
+ new_strategy._root = self._root # Shared structure
372
+ new_strategy._size = self._size
373
+ return new_strategy
374
+
375
+ def get_tree_depth(self) -> int:
376
+ """Calculate maximum tree depth."""
377
+ def depth(node: HAMTNode, current: int) -> int:
378
+ max_d = current
379
+ for child in node.children:
380
+ if isinstance(child, HAMTNode):
381
+ max_d = max(max_d, depth(child, current + 1))
382
+ return max_d
383
+
384
+ return depth(self._root, 0)
385
+
386
+ def to_native(self) -> Dict[str, Any]:
387
+ """Convert to native Python dictionary."""
388
+ result = {}
389
+ for key, value in self.items():
390
+ result[str(key)] = safe_to_native_conversion(value)
391
+ return result
392
+
393
+ def get_backend_info(self) -> Dict[str, Any]:
394
+ """Get backend information."""
395
+ return {
396
+ **create_basic_backend_info('HAMT', 'Hash Array Mapped Trie'),
397
+ 'total_keys': self._size,
398
+ 'tree_depth': self.get_tree_depth(),
399
+ 'bits_per_level': self.BITS_PER_LEVEL,
400
+ **self._size_tracker,
401
+ **get_access_metrics(self._access_tracker)
402
+ }
403
+