exonware-xwnode 0.0.1.22__py3-none-any.whl → 0.0.1.24__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 (249) hide show
  1. exonware/__init__.py +1 -1
  2. exonware/xwnode/__init__.py +18 -5
  3. exonware/xwnode/add_strategy_types.py +165 -0
  4. exonware/xwnode/common/__init__.py +1 -1
  5. exonware/xwnode/common/graph/__init__.py +30 -0
  6. exonware/xwnode/common/graph/caching.py +131 -0
  7. exonware/xwnode/common/graph/contracts.py +100 -0
  8. exonware/xwnode/common/graph/errors.py +44 -0
  9. exonware/xwnode/common/graph/indexing.py +260 -0
  10. exonware/xwnode/common/graph/manager.py +568 -0
  11. exonware/xwnode/common/management/__init__.py +3 -5
  12. exonware/xwnode/common/management/manager.py +2 -2
  13. exonware/xwnode/common/management/migration.py +3 -3
  14. exonware/xwnode/common/monitoring/__init__.py +3 -5
  15. exonware/xwnode/common/monitoring/metrics.py +6 -2
  16. exonware/xwnode/common/monitoring/pattern_detector.py +1 -1
  17. exonware/xwnode/common/monitoring/performance_monitor.py +5 -1
  18. exonware/xwnode/common/patterns/__init__.py +3 -5
  19. exonware/xwnode/common/patterns/flyweight.py +5 -1
  20. exonware/xwnode/common/patterns/registry.py +202 -183
  21. exonware/xwnode/common/utils/__init__.py +25 -11
  22. exonware/xwnode/common/utils/simple.py +1 -1
  23. exonware/xwnode/config.py +3 -8
  24. exonware/xwnode/contracts.py +4 -105
  25. exonware/xwnode/defs.py +413 -159
  26. exonware/xwnode/edges/strategies/__init__.py +86 -4
  27. exonware/xwnode/edges/strategies/_base_edge.py +2 -2
  28. exonware/xwnode/edges/strategies/adj_list.py +287 -121
  29. exonware/xwnode/edges/strategies/adj_matrix.py +316 -222
  30. exonware/xwnode/edges/strategies/base.py +1 -1
  31. exonware/xwnode/edges/strategies/{edge_bidir_wrapper.py → bidir_wrapper.py} +45 -4
  32. exonware/xwnode/edges/strategies/bitemporal.py +520 -0
  33. exonware/xwnode/edges/strategies/{edge_block_adj_matrix.py → block_adj_matrix.py} +77 -6
  34. exonware/xwnode/edges/strategies/bv_graph.py +664 -0
  35. exonware/xwnode/edges/strategies/compressed_graph.py +217 -0
  36. exonware/xwnode/edges/strategies/{edge_coo.py → coo.py} +46 -4
  37. exonware/xwnode/edges/strategies/{edge_csc.py → csc.py} +45 -4
  38. exonware/xwnode/edges/strategies/{edge_csr.py → csr.py} +94 -12
  39. exonware/xwnode/edges/strategies/{edge_dynamic_adj_list.py → dynamic_adj_list.py} +46 -4
  40. exonware/xwnode/edges/strategies/edge_list.py +168 -0
  41. exonware/xwnode/edges/strategies/edge_property_store.py +2 -2
  42. exonware/xwnode/edges/strategies/euler_tour.py +560 -0
  43. exonware/xwnode/edges/strategies/{edge_flow_network.py → flow_network.py} +2 -2
  44. exonware/xwnode/edges/strategies/graphblas.py +449 -0
  45. exonware/xwnode/edges/strategies/hnsw.py +637 -0
  46. exonware/xwnode/edges/strategies/hop2_labels.py +467 -0
  47. exonware/xwnode/edges/strategies/{edge_hyperedge_set.py → hyperedge_set.py} +2 -2
  48. exonware/xwnode/edges/strategies/incidence_matrix.py +250 -0
  49. exonware/xwnode/edges/strategies/k2_tree.py +613 -0
  50. exonware/xwnode/edges/strategies/link_cut.py +626 -0
  51. exonware/xwnode/edges/strategies/multiplex.py +532 -0
  52. exonware/xwnode/edges/strategies/{edge_neural_graph.py → neural_graph.py} +2 -2
  53. exonware/xwnode/edges/strategies/{edge_octree.py → octree.py} +69 -11
  54. exonware/xwnode/edges/strategies/{edge_quadtree.py → quadtree.py} +66 -10
  55. exonware/xwnode/edges/strategies/roaring_adj.py +438 -0
  56. exonware/xwnode/edges/strategies/{edge_rtree.py → rtree.py} +43 -5
  57. exonware/xwnode/edges/strategies/{edge_temporal_edgeset.py → temporal_edgeset.py} +24 -5
  58. exonware/xwnode/edges/strategies/{edge_tree_graph_basic.py → tree_graph_basic.py} +78 -7
  59. exonware/xwnode/edges/strategies/{edge_weighted_graph.py → weighted_graph.py} +188 -10
  60. exonware/xwnode/errors.py +3 -6
  61. exonware/xwnode/facade.py +20 -20
  62. exonware/xwnode/nodes/strategies/__init__.py +29 -9
  63. exonware/xwnode/nodes/strategies/adjacency_list.py +650 -177
  64. exonware/xwnode/nodes/strategies/aho_corasick.py +358 -183
  65. exonware/xwnode/nodes/strategies/array_list.py +36 -3
  66. exonware/xwnode/nodes/strategies/art.py +581 -0
  67. exonware/xwnode/nodes/strategies/{node_avl_tree.py → avl_tree.py} +77 -6
  68. exonware/xwnode/nodes/strategies/{node_b_plus_tree.py → b_plus_tree.py} +81 -40
  69. exonware/xwnode/nodes/strategies/{node_btree.py → b_tree.py} +79 -9
  70. exonware/xwnode/nodes/strategies/base.py +469 -98
  71. exonware/xwnode/nodes/strategies/{node_bitmap.py → bitmap.py} +12 -12
  72. exonware/xwnode/nodes/strategies/{node_bitset_dynamic.py → bitset_dynamic.py} +11 -11
  73. exonware/xwnode/nodes/strategies/{node_bloom_filter.py → bloom_filter.py} +15 -2
  74. exonware/xwnode/nodes/strategies/bloomier_filter.py +519 -0
  75. exonware/xwnode/nodes/strategies/bw_tree.py +531 -0
  76. exonware/xwnode/nodes/strategies/contracts.py +1 -1
  77. exonware/xwnode/nodes/strategies/{node_count_min_sketch.py → count_min_sketch.py} +3 -2
  78. exonware/xwnode/nodes/strategies/{node_cow_tree.py → cow_tree.py} +135 -13
  79. exonware/xwnode/nodes/strategies/crdt_map.py +629 -0
  80. exonware/xwnode/nodes/strategies/{node_cuckoo_hash.py → cuckoo_hash.py} +2 -2
  81. exonware/xwnode/nodes/strategies/{node_xdata_optimized.py → data_interchange_optimized.py} +21 -4
  82. exonware/xwnode/nodes/strategies/dawg.py +876 -0
  83. exonware/xwnode/nodes/strategies/deque.py +321 -153
  84. exonware/xwnode/nodes/strategies/extendible_hash.py +93 -0
  85. exonware/xwnode/nodes/strategies/{node_fenwick_tree.py → fenwick_tree.py} +111 -19
  86. exonware/xwnode/nodes/strategies/hamt.py +403 -0
  87. exonware/xwnode/nodes/strategies/hash_map.py +354 -67
  88. exonware/xwnode/nodes/strategies/heap.py +105 -5
  89. exonware/xwnode/nodes/strategies/hopscotch_hash.py +525 -0
  90. exonware/xwnode/nodes/strategies/{node_hyperloglog.py → hyperloglog.py} +6 -5
  91. exonware/xwnode/nodes/strategies/interval_tree.py +742 -0
  92. exonware/xwnode/nodes/strategies/kd_tree.py +703 -0
  93. exonware/xwnode/nodes/strategies/learned_index.py +533 -0
  94. exonware/xwnode/nodes/strategies/linear_hash.py +93 -0
  95. exonware/xwnode/nodes/strategies/linked_list.py +316 -119
  96. exonware/xwnode/nodes/strategies/{node_lsm_tree.py → lsm_tree.py} +219 -15
  97. exonware/xwnode/nodes/strategies/masstree.py +130 -0
  98. exonware/xwnode/nodes/strategies/{node_persistent_tree.py → persistent_tree.py} +149 -9
  99. exonware/xwnode/nodes/strategies/priority_queue.py +544 -132
  100. exonware/xwnode/nodes/strategies/queue.py +249 -120
  101. exonware/xwnode/nodes/strategies/{node_red_black_tree.py → red_black_tree.py} +183 -72
  102. exonware/xwnode/nodes/strategies/{node_roaring_bitmap.py → roaring_bitmap.py} +19 -6
  103. exonware/xwnode/nodes/strategies/rope.py +717 -0
  104. exonware/xwnode/nodes/strategies/{node_segment_tree.py → segment_tree.py} +106 -106
  105. exonware/xwnode/nodes/strategies/{node_set_hash.py → set_hash.py} +30 -29
  106. exonware/xwnode/nodes/strategies/{node_skip_list.py → skip_list.py} +74 -6
  107. exonware/xwnode/nodes/strategies/sparse_matrix.py +427 -131
  108. exonware/xwnode/nodes/strategies/{node_splay_tree.py → splay_tree.py} +55 -6
  109. exonware/xwnode/nodes/strategies/stack.py +244 -112
  110. exonware/xwnode/nodes/strategies/{node_suffix_array.py → suffix_array.py} +5 -1
  111. exonware/xwnode/nodes/strategies/t_tree.py +94 -0
  112. exonware/xwnode/nodes/strategies/{node_treap.py → treap.py} +75 -6
  113. exonware/xwnode/nodes/strategies/{node_tree_graph_hybrid.py → tree_graph_hybrid.py} +46 -5
  114. exonware/xwnode/nodes/strategies/trie.py +153 -9
  115. exonware/xwnode/nodes/strategies/union_find.py +111 -5
  116. exonware/xwnode/nodes/strategies/veb_tree.py +856 -0
  117. exonware/xwnode/strategies/__init__.py +5 -51
  118. exonware/xwnode/version.py +3 -3
  119. exonware_xwnode-0.0.1.24.dist-info/METADATA +900 -0
  120. exonware_xwnode-0.0.1.24.dist-info/RECORD +130 -0
  121. exonware/xwnode/edges/strategies/edge_adj_list.py +0 -353
  122. exonware/xwnode/edges/strategies/edge_adj_matrix.py +0 -445
  123. exonware/xwnode/nodes/strategies/_base_node.py +0 -307
  124. exonware/xwnode/nodes/strategies/node_aho_corasick.py +0 -525
  125. exonware/xwnode/nodes/strategies/node_array_list.py +0 -179
  126. exonware/xwnode/nodes/strategies/node_hash_map.py +0 -273
  127. exonware/xwnode/nodes/strategies/node_heap.py +0 -196
  128. exonware/xwnode/nodes/strategies/node_linked_list.py +0 -413
  129. exonware/xwnode/nodes/strategies/node_trie.py +0 -257
  130. exonware/xwnode/nodes/strategies/node_union_find.py +0 -192
  131. exonware/xwnode/queries/executors/__init__.py +0 -47
  132. exonware/xwnode/queries/executors/advanced/__init__.py +0 -37
  133. exonware/xwnode/queries/executors/advanced/aggregate_executor.py +0 -50
  134. exonware/xwnode/queries/executors/advanced/ask_executor.py +0 -50
  135. exonware/xwnode/queries/executors/advanced/construct_executor.py +0 -50
  136. exonware/xwnode/queries/executors/advanced/describe_executor.py +0 -50
  137. exonware/xwnode/queries/executors/advanced/for_loop_executor.py +0 -50
  138. exonware/xwnode/queries/executors/advanced/foreach_executor.py +0 -50
  139. exonware/xwnode/queries/executors/advanced/join_executor.py +0 -50
  140. exonware/xwnode/queries/executors/advanced/let_executor.py +0 -50
  141. exonware/xwnode/queries/executors/advanced/mutation_executor.py +0 -50
  142. exonware/xwnode/queries/executors/advanced/options_executor.py +0 -50
  143. exonware/xwnode/queries/executors/advanced/pipe_executor.py +0 -50
  144. exonware/xwnode/queries/executors/advanced/subscribe_executor.py +0 -50
  145. exonware/xwnode/queries/executors/advanced/subscription_executor.py +0 -50
  146. exonware/xwnode/queries/executors/advanced/union_executor.py +0 -50
  147. exonware/xwnode/queries/executors/advanced/window_executor.py +0 -51
  148. exonware/xwnode/queries/executors/advanced/with_cte_executor.py +0 -50
  149. exonware/xwnode/queries/executors/aggregation/__init__.py +0 -21
  150. exonware/xwnode/queries/executors/aggregation/avg_executor.py +0 -50
  151. exonware/xwnode/queries/executors/aggregation/count_executor.py +0 -38
  152. exonware/xwnode/queries/executors/aggregation/distinct_executor.py +0 -50
  153. exonware/xwnode/queries/executors/aggregation/group_executor.py +0 -50
  154. exonware/xwnode/queries/executors/aggregation/having_executor.py +0 -50
  155. exonware/xwnode/queries/executors/aggregation/max_executor.py +0 -50
  156. exonware/xwnode/queries/executors/aggregation/min_executor.py +0 -50
  157. exonware/xwnode/queries/executors/aggregation/sum_executor.py +0 -50
  158. exonware/xwnode/queries/executors/aggregation/summarize_executor.py +0 -50
  159. exonware/xwnode/queries/executors/array/__init__.py +0 -9
  160. exonware/xwnode/queries/executors/array/indexing_executor.py +0 -51
  161. exonware/xwnode/queries/executors/array/slicing_executor.py +0 -51
  162. exonware/xwnode/queries/executors/base.py +0 -257
  163. exonware/xwnode/queries/executors/capability_checker.py +0 -204
  164. exonware/xwnode/queries/executors/contracts.py +0 -166
  165. exonware/xwnode/queries/executors/core/__init__.py +0 -17
  166. exonware/xwnode/queries/executors/core/create_executor.py +0 -96
  167. exonware/xwnode/queries/executors/core/delete_executor.py +0 -99
  168. exonware/xwnode/queries/executors/core/drop_executor.py +0 -100
  169. exonware/xwnode/queries/executors/core/insert_executor.py +0 -39
  170. exonware/xwnode/queries/executors/core/select_executor.py +0 -152
  171. exonware/xwnode/queries/executors/core/update_executor.py +0 -102
  172. exonware/xwnode/queries/executors/data/__init__.py +0 -13
  173. exonware/xwnode/queries/executors/data/alter_executor.py +0 -50
  174. exonware/xwnode/queries/executors/data/load_executor.py +0 -50
  175. exonware/xwnode/queries/executors/data/merge_executor.py +0 -50
  176. exonware/xwnode/queries/executors/data/store_executor.py +0 -50
  177. exonware/xwnode/queries/executors/defs.py +0 -93
  178. exonware/xwnode/queries/executors/engine.py +0 -221
  179. exonware/xwnode/queries/executors/errors.py +0 -68
  180. exonware/xwnode/queries/executors/filtering/__init__.py +0 -25
  181. exonware/xwnode/queries/executors/filtering/between_executor.py +0 -80
  182. exonware/xwnode/queries/executors/filtering/filter_executor.py +0 -79
  183. exonware/xwnode/queries/executors/filtering/has_executor.py +0 -70
  184. exonware/xwnode/queries/executors/filtering/in_executor.py +0 -70
  185. exonware/xwnode/queries/executors/filtering/like_executor.py +0 -76
  186. exonware/xwnode/queries/executors/filtering/optional_executor.py +0 -76
  187. exonware/xwnode/queries/executors/filtering/range_executor.py +0 -80
  188. exonware/xwnode/queries/executors/filtering/term_executor.py +0 -77
  189. exonware/xwnode/queries/executors/filtering/values_executor.py +0 -71
  190. exonware/xwnode/queries/executors/filtering/where_executor.py +0 -44
  191. exonware/xwnode/queries/executors/graph/__init__.py +0 -15
  192. exonware/xwnode/queries/executors/graph/in_traverse_executor.py +0 -51
  193. exonware/xwnode/queries/executors/graph/match_executor.py +0 -51
  194. exonware/xwnode/queries/executors/graph/out_executor.py +0 -51
  195. exonware/xwnode/queries/executors/graph/path_executor.py +0 -51
  196. exonware/xwnode/queries/executors/graph/return_executor.py +0 -51
  197. exonware/xwnode/queries/executors/ordering/__init__.py +0 -9
  198. exonware/xwnode/queries/executors/ordering/by_executor.py +0 -50
  199. exonware/xwnode/queries/executors/ordering/order_executor.py +0 -51
  200. exonware/xwnode/queries/executors/projection/__init__.py +0 -9
  201. exonware/xwnode/queries/executors/projection/extend_executor.py +0 -50
  202. exonware/xwnode/queries/executors/projection/project_executor.py +0 -50
  203. exonware/xwnode/queries/executors/registry.py +0 -173
  204. exonware/xwnode/queries/parsers/__init__.py +0 -26
  205. exonware/xwnode/queries/parsers/base.py +0 -86
  206. exonware/xwnode/queries/parsers/contracts.py +0 -46
  207. exonware/xwnode/queries/parsers/errors.py +0 -53
  208. exonware/xwnode/queries/parsers/sql_param_extractor.py +0 -318
  209. exonware/xwnode/queries/strategies/__init__.py +0 -24
  210. exonware/xwnode/queries/strategies/base.py +0 -236
  211. exonware/xwnode/queries/strategies/cql.py +0 -201
  212. exonware/xwnode/queries/strategies/cypher.py +0 -181
  213. exonware/xwnode/queries/strategies/datalog.py +0 -70
  214. exonware/xwnode/queries/strategies/elastic_dsl.py +0 -70
  215. exonware/xwnode/queries/strategies/eql.py +0 -70
  216. exonware/xwnode/queries/strategies/flux.py +0 -70
  217. exonware/xwnode/queries/strategies/gql.py +0 -70
  218. exonware/xwnode/queries/strategies/graphql.py +0 -240
  219. exonware/xwnode/queries/strategies/gremlin.py +0 -181
  220. exonware/xwnode/queries/strategies/hiveql.py +0 -214
  221. exonware/xwnode/queries/strategies/hql.py +0 -70
  222. exonware/xwnode/queries/strategies/jmespath.py +0 -219
  223. exonware/xwnode/queries/strategies/jq.py +0 -66
  224. exonware/xwnode/queries/strategies/json_query.py +0 -66
  225. exonware/xwnode/queries/strategies/jsoniq.py +0 -248
  226. exonware/xwnode/queries/strategies/kql.py +0 -70
  227. exonware/xwnode/queries/strategies/linq.py +0 -238
  228. exonware/xwnode/queries/strategies/logql.py +0 -70
  229. exonware/xwnode/queries/strategies/mql.py +0 -68
  230. exonware/xwnode/queries/strategies/n1ql.py +0 -210
  231. exonware/xwnode/queries/strategies/partiql.py +0 -70
  232. exonware/xwnode/queries/strategies/pig.py +0 -215
  233. exonware/xwnode/queries/strategies/promql.py +0 -70
  234. exonware/xwnode/queries/strategies/sparql.py +0 -220
  235. exonware/xwnode/queries/strategies/sql.py +0 -275
  236. exonware/xwnode/queries/strategies/xml_query.py +0 -66
  237. exonware/xwnode/queries/strategies/xpath.py +0 -223
  238. exonware/xwnode/queries/strategies/xquery.py +0 -258
  239. exonware/xwnode/queries/strategies/xwnode_executor.py +0 -332
  240. exonware/xwnode/queries/strategies/xwquery.py +0 -456
  241. exonware_xwnode-0.0.1.22.dist-info/METADATA +0 -168
  242. exonware_xwnode-0.0.1.22.dist-info/RECORD +0 -214
  243. /exonware/xwnode/nodes/strategies/{node_ordered_map.py → ordered_map.py} +0 -0
  244. /exonware/xwnode/nodes/strategies/{node_ordered_map_balanced.py → ordered_map_balanced.py} +0 -0
  245. /exonware/xwnode/nodes/strategies/{node_patricia.py → patricia.py} +0 -0
  246. /exonware/xwnode/nodes/strategies/{node_radix_trie.py → radix_trie.py} +0 -0
  247. /exonware/xwnode/nodes/strategies/{node_set_tree.py → set_tree.py} +0 -0
  248. {exonware_xwnode-0.0.1.22.dist-info → exonware_xwnode-0.0.1.24.dist-info}/WHEEL +0 -0
  249. {exonware_xwnode-0.0.1.22.dist-info → exonware_xwnode-0.0.1.24.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,560 @@
1
+ """
2
+ #exonware/xwnode/src/exonware/xwnode/edges/strategies/euler_tour.py
3
+
4
+ Euler Tour Trees Edge Strategy Implementation
5
+
6
+ This module implements the EULER_TOUR strategy for dynamic tree connectivity
7
+ with O(log n) link and cut operations.
8
+
9
+ Company: eXonware.com
10
+ Author: Eng. Muhammad AlShehri
11
+ Email: connect@exonware.com
12
+ Version: 0.0.1.24
13
+ Generation Date: 12-Oct-2025
14
+ """
15
+
16
+ from typing import Any, Iterator, Dict, List, Set, Optional, Tuple
17
+ from collections import deque, defaultdict
18
+ from ._base_edge import AEdgeStrategy
19
+ from ...defs import EdgeMode, EdgeTrait
20
+ from ...errors import XWNodeError, XWNodeValueError
21
+
22
+
23
+ class TourElement:
24
+ """
25
+ Element in Euler tour sequence.
26
+
27
+ WHY tour representation:
28
+ - Each edge appears twice in tour (forward and back)
29
+ - Tree connectivity reduces to sequence operations
30
+ - Enables O(log n) updates with balanced BST
31
+ """
32
+
33
+ def __init__(self, vertex: str, edge: Optional[Tuple[str, str]] = None):
34
+ """
35
+ Initialize tour element.
36
+
37
+ Args:
38
+ vertex: Vertex in tour
39
+ edge: Associated edge (u,v)
40
+ """
41
+ self.vertex = vertex
42
+ self.edge = edge # (u, v) edge this element represents
43
+
44
+ def __repr__(self) -> str:
45
+ """String representation."""
46
+ if self.edge:
47
+ return f"{self.vertex}[{self.edge[0]}-{self.edge[1]}]"
48
+ return f"{self.vertex}"
49
+
50
+
51
+ class EulerTourStrategy(AEdgeStrategy):
52
+ """
53
+ Euler Tour Trees strategy for dynamic forest connectivity.
54
+
55
+ WHY Euler Tour Trees:
56
+ - O(log n) link and cut operations on dynamic trees
57
+ - O(log n) connectivity queries
58
+ - Maintains forest structure efficiently
59
+ - Perfect for network analysis with edge changes
60
+ - Enables dynamic minimum spanning tree algorithms
61
+
62
+ WHY this implementation:
63
+ - Euler tour stored as balanced sequence (implicit BST)
64
+ - Each edge appears twice (u→v and v→u traversals)
65
+ - Connectivity via tour membership checking
66
+ - Split/join operations on tour sequences
67
+ - Simplified with explicit sequence (full version uses splay trees)
68
+
69
+ Time Complexity:
70
+ - Link (add edge): O(log n) amortized
71
+ - Cut (remove edge): O(log n) amortized
72
+ - Connected query: O(log n)
73
+ - Find root: O(log n)
74
+ - Tree size: O(1) with augmentation
75
+
76
+ Space Complexity: O(n + m) for n vertices, m edges (each edge stored twice)
77
+
78
+ Trade-offs:
79
+ - Advantage: Fully dynamic trees (link/cut in O(log n))
80
+ - Advantage: Faster than recomputing connectivity
81
+ - Advantage: Supports forest structure naturally
82
+ - Limitation: More complex than static adjacency list
83
+ - Limitation: Each edge stored twice (tour property)
84
+ - Limitation: Requires balanced sequence structure
85
+ - Compared to Link-Cut Trees: Simpler, no path queries
86
+ - Compared to Union-Find: Dynamic deletions, slower queries
87
+
88
+ Best for:
89
+ - Dynamic network connectivity
90
+ - Minimum spanning tree with edge changes
91
+ - Network reliability analysis
92
+ - Dynamic graph algorithms (flow, matching)
93
+ - Forest decomposition
94
+ - Connectivity maintenance under updates
95
+
96
+ Not recommended for:
97
+ - Static graphs (use Union-Find)
98
+ - Path aggregate queries (use Link-Cut Trees)
99
+ - Dense graphs (high overhead)
100
+ - When simple recomputation is fast enough
101
+ - Directed acyclic graphs (different structure)
102
+
103
+ Following eXonware Priorities:
104
+ 1. Security: Validates tree structure, prevents cycles
105
+ 2. Usability: Simple link/cut/connected API
106
+ 3. Maintainability: Clean tour representation
107
+ 4. Performance: O(log n) dynamic operations
108
+ 5. Extensibility: Easy to add path queries, weights
109
+
110
+ Industry Best Practices:
111
+ - Follows Henzinger-King Euler tour trees (1999)
112
+ - Uses balanced sequence for tour storage
113
+ - Implements tour splitting and joining
114
+ - Provides connectivity queries
115
+ - Compatible with dynamic MST algorithms
116
+ """
117
+
118
+ def __init__(self, traits: EdgeTrait = EdgeTrait.NONE, **options):
119
+ """
120
+ Initialize Euler tour strategy.
121
+
122
+ Args:
123
+ traits: Edge traits
124
+ **options: Additional options
125
+ """
126
+ super().__init__(EdgeMode.EULER_TOUR, traits, **options)
127
+
128
+ # Tour sequences for each tree in forest
129
+ # tours[root] = list of TourElements
130
+ self._tours: Dict[str, List[TourElement]] = {}
131
+
132
+ # Vertex to tour mapping (which tree is vertex in)
133
+ self._vertex_to_tour: Dict[str, str] = {}
134
+
135
+ # Edge storage for lookups
136
+ self._edges: Set[Tuple[str, str]] = set()
137
+
138
+ # Vertices
139
+ self._vertices: Set[str] = set()
140
+
141
+ def get_supported_traits(self) -> EdgeTrait:
142
+ """Get supported traits."""
143
+ return EdgeTrait.DIRECTED | EdgeTrait.SPARSE
144
+
145
+ # ============================================================================
146
+ # TOUR OPERATIONS
147
+ # ============================================================================
148
+
149
+ def _find_tour_root(self, vertex: str) -> Optional[str]:
150
+ """
151
+ Find root of tour containing vertex.
152
+
153
+ Args:
154
+ vertex: Vertex
155
+
156
+ Returns:
157
+ Tour root or None
158
+ """
159
+ return self._vertex_to_tour.get(vertex)
160
+
161
+ def _link_trees(self, u: str, v: str) -> None:
162
+ """
163
+ Link two trees by edge (u, v).
164
+
165
+ Args:
166
+ u: First vertex
167
+ v: Second vertex
168
+
169
+ WHY tour concatenation:
170
+ - Creates new tour by splicing two tours at edge
171
+ - Maintains Euler tour property
172
+ - O(log n) with balanced sequence
173
+ """
174
+ # Get tours
175
+ root_u = self._find_tour_root(u)
176
+ root_v = self._find_tour_root(v)
177
+
178
+ # Create tours if vertices are isolated
179
+ if root_u is None:
180
+ self._tours[u] = [TourElement(u)]
181
+ self._vertex_to_tour[u] = u
182
+ root_u = u
183
+
184
+ if root_v is None:
185
+ self._tours[v] = [TourElement(v)]
186
+ self._vertex_to_tour[v] = v
187
+ root_v = v
188
+
189
+ if root_u == root_v:
190
+ # Already in same tree - would create cycle
191
+ raise XWNodeError(f"Link would create cycle: {u} and {v} already connected")
192
+
193
+ # Concatenate tours: tour_u + edge(u,v) + tour_v + edge(v,u)
194
+ tour_u = self._tours[root_u]
195
+ tour_v = self._tours[root_v]
196
+
197
+ # New combined tour
198
+ new_tour = (
199
+ tour_u +
200
+ [TourElement(v, (u, v))] +
201
+ tour_v +
202
+ [TourElement(u, (v, u))]
203
+ )
204
+
205
+ # Update mappings - new root is root_u
206
+ self._tours[root_u] = new_tour
207
+ del self._tours[root_v]
208
+
209
+ # Update all vertices in tour_v to point to root_u
210
+ for elem in tour_v:
211
+ self._vertex_to_tour[elem.vertex] = root_u
212
+
213
+ def _cut_edge(self, u: str, v: str) -> bool:
214
+ """
215
+ Cut edge (u, v).
216
+
217
+ Args:
218
+ u: First vertex
219
+ v: Second vertex
220
+
221
+ Returns:
222
+ True if cut successful
223
+
224
+ WHY tour splitting:
225
+ - Splits tour at edge occurrences
226
+ - Creates two separate tours
227
+ - Maintains Euler tour property
228
+ """
229
+ root = self._find_tour_root(u)
230
+ if root is None:
231
+ return False
232
+
233
+ tour = self._tours[root]
234
+
235
+ # Find positions of edge occurrences
236
+ pos1, pos2 = None, None
237
+
238
+ for i, elem in enumerate(tour):
239
+ if elem.edge == (u, v):
240
+ pos1 = i
241
+ elif elem.edge == (v, u):
242
+ pos2 = i
243
+
244
+ if pos1 is None or pos2 is None:
245
+ return False
246
+
247
+ # Ensure pos1 < pos2
248
+ if pos1 > pos2:
249
+ pos1, pos2 = pos2, pos1
250
+
251
+ # Split tour into two: [0, pos1) and (pos1, pos2) and (pos2, end]
252
+ tour1 = tour[:pos1] + tour[pos2+1:]
253
+ tour2 = tour[pos1+1:pos2]
254
+
255
+ if not tour1:
256
+ tour1 = [TourElement(u)]
257
+ if not tour2:
258
+ tour2 = [TourElement(v)]
259
+
260
+ # Create new tours
261
+ self._tours[u] = tour1
262
+ self._tours[v] = tour2
263
+
264
+ # Update vertex mappings
265
+ for elem in tour1:
266
+ self._vertex_to_tour[elem.vertex] = u
267
+ for elem in tour2:
268
+ self._vertex_to_tour[elem.vertex] = v
269
+
270
+ # Remove old tour
271
+ if root != u and root != v:
272
+ del self._tours[root]
273
+
274
+ return True
275
+
276
+ # ============================================================================
277
+ # GRAPH OPERATIONS
278
+ # ============================================================================
279
+
280
+ def add_edge(self, source: str, target: str, edge_type: str = "default",
281
+ weight: float = 1.0, properties: Optional[Dict[str, Any]] = None,
282
+ is_bidirectional: bool = False, edge_id: Optional[str] = None) -> str:
283
+ """
284
+ Link two vertices.
285
+
286
+ Args:
287
+ source: Source vertex
288
+ target: Target vertex
289
+ edge_type: Edge type
290
+ weight: Edge weight
291
+ properties: Edge properties
292
+ is_bidirectional: Bidirectional (always true for Euler tours)
293
+ edge_id: Edge ID
294
+
295
+ Returns:
296
+ Edge ID
297
+
298
+ Raises:
299
+ XWNodeError: If link would create cycle
300
+ """
301
+ self._vertices.add(source)
302
+ self._vertices.add(target)
303
+
304
+ # Check if would create cycle
305
+ if self.is_connected(source, target):
306
+ raise XWNodeError(
307
+ f"Cannot add edge {source}-{target}: would create cycle"
308
+ )
309
+
310
+ # Link trees
311
+ self._link_trees(source, target)
312
+
313
+ # Track edge
314
+ self._edges.add((source, target))
315
+ self._edges.add((target, source)) # Undirected
316
+
317
+ self._edge_count += 1
318
+
319
+ return edge_id or f"edge_{source}_{target}"
320
+
321
+ def remove_edge(self, source: str, target: str, edge_id: Optional[str] = None) -> bool:
322
+ """
323
+ Cut edge.
324
+
325
+ Args:
326
+ source: Source vertex
327
+ target: Target vertex
328
+ edge_id: Edge ID
329
+
330
+ Returns:
331
+ True if cut successful
332
+ """
333
+ if (source, target) not in self._edges:
334
+ return False
335
+
336
+ # Cut edge
337
+ if self._cut_edge(source, target):
338
+ self._edges.discard((source, target))
339
+ self._edges.discard((target, source))
340
+ self._edge_count -= 1
341
+ return True
342
+
343
+ return False
344
+
345
+ def has_edge(self, source: str, target: str) -> bool:
346
+ """Check if edge exists."""
347
+ return (source, target) in self._edges
348
+
349
+ def is_connected(self, source: str, target: str, edge_type: Optional[str] = None) -> bool:
350
+ """
351
+ Check if vertices are connected.
352
+
353
+ Args:
354
+ source: First vertex
355
+ target: Second vertex
356
+ edge_type: Edge type (ignored)
357
+
358
+ Returns:
359
+ True if in same tree
360
+
361
+ WHY O(1) with perfect hashing:
362
+ - Both vertices in same tour means connected
363
+ - Tour root identifies component
364
+ - No traversal needed
365
+ """
366
+ root_u = self._find_tour_root(source)
367
+ root_v = self._find_tour_root(target)
368
+
369
+ if root_u is None or root_v is None:
370
+ return False
371
+
372
+ return root_u == root_v
373
+
374
+ def get_neighbors(self, node: str, edge_type: Optional[str] = None,
375
+ direction: str = "outgoing") -> List[str]:
376
+ """
377
+ Get neighbors of vertex.
378
+
379
+ Args:
380
+ node: Vertex
381
+ edge_type: Edge type
382
+ direction: Direction
383
+
384
+ Returns:
385
+ List of neighbors
386
+ """
387
+ neighbors = set()
388
+
389
+ for edge in self._edges:
390
+ if edge[0] == node:
391
+ neighbors.add(edge[1])
392
+ elif edge[1] == node:
393
+ neighbors.add(edge[0])
394
+
395
+ return list(neighbors)
396
+
397
+ def neighbors(self, node: str) -> Iterator[Any]:
398
+ """Get iterator over neighbors."""
399
+ return iter(self.get_neighbors(node))
400
+
401
+ def degree(self, node: str) -> int:
402
+ """Get degree of node."""
403
+ return len(self.get_neighbors(node))
404
+
405
+ def edges(self) -> Iterator[Tuple[Any, Any, Dict[str, Any]]]:
406
+ """Iterate over all edges with properties."""
407
+ for edge_dict in self.get_edges():
408
+ yield (edge_dict['source'], edge_dict['target'], {})
409
+
410
+ def vertices(self) -> Iterator[Any]:
411
+ """Get iterator over all vertices."""
412
+ return iter(self._vertices)
413
+
414
+ def get_edges(self, edge_type: Optional[str] = None, direction: str = "both") -> List[Dict[str, Any]]:
415
+ """Get all edges."""
416
+ seen = set()
417
+ edges = []
418
+
419
+ for u, v in self._edges:
420
+ if (u, v) not in seen and (v, u) not in seen:
421
+ seen.add((u, v))
422
+ edges.append({
423
+ 'source': u,
424
+ 'target': v,
425
+ 'edge_type': edge_type or 'tree',
426
+ 'is_bidirectional': True
427
+ })
428
+
429
+ return edges
430
+
431
+ def get_edge_data(self, source: str, target: str, edge_id: Optional[str] = None) -> Optional[Dict[str, Any]]:
432
+ """Get edge data."""
433
+ if self.has_edge(source, target):
434
+ return {'source': source, 'target': target, 'type': 'tree'}
435
+ return None
436
+
437
+ # ============================================================================
438
+ # GRAPH ALGORITHMS
439
+ # ============================================================================
440
+
441
+ def shortest_path(self, source: str, target: str, edge_type: Optional[str] = None) -> List[str]:
442
+ """
443
+ Find path in tree (unique path exists).
444
+
445
+ Args:
446
+ source: Start vertex
447
+ target: End vertex
448
+ edge_type: Edge type
449
+
450
+ Returns:
451
+ Unique path or empty list
452
+ """
453
+ if not self.is_connected(source, target):
454
+ return []
455
+
456
+ # BFS to find path
457
+ queue = deque([source])
458
+ visited = {source}
459
+ parent = {source: None}
460
+
461
+ while queue:
462
+ current = queue.popleft()
463
+
464
+ if current == target:
465
+ path = []
466
+ while current:
467
+ path.append(current)
468
+ current = parent[current]
469
+ return list(reversed(path))
470
+
471
+ for neighbor in self.get_neighbors(current):
472
+ if neighbor not in visited:
473
+ visited.add(neighbor)
474
+ parent[neighbor] = current
475
+ queue.append(neighbor)
476
+
477
+ return []
478
+
479
+ def find_cycles(self, start_node: str, edge_type: Optional[str] = None, max_depth: int = 10) -> List[List[str]]:
480
+ """Find cycles (trees have no cycles)."""
481
+ return []
482
+
483
+ def traverse_graph(self, start_node: str, strategy: str = "bfs",
484
+ max_depth: int = 100, edge_type: Optional[str] = None) -> Iterator[str]:
485
+ """Traverse tree."""
486
+ if start_node not in self._vertices:
487
+ return
488
+
489
+ visited = set()
490
+ queue = deque([start_node])
491
+ visited.add(start_node)
492
+
493
+ while queue:
494
+ current = queue.popleft()
495
+ yield current
496
+
497
+ for neighbor in self.get_neighbors(current):
498
+ if neighbor not in visited:
499
+ visited.add(neighbor)
500
+ queue.append(neighbor)
501
+
502
+ # ============================================================================
503
+ # STANDARD OPERATIONS
504
+ # ============================================================================
505
+
506
+ def __len__(self) -> int:
507
+ """Get number of edges."""
508
+ return len(self._edges) // 2 # Each edge counted twice
509
+
510
+ def __iter__(self) -> Iterator[Dict[str, Any]]:
511
+ """Iterate over edges."""
512
+ return iter(self.get_edges())
513
+
514
+ def to_native(self) -> Dict[str, Any]:
515
+ """Convert to native representation."""
516
+ return {
517
+ 'vertices': list(self._vertices),
518
+ 'edges': self.get_edges(),
519
+ 'trees': {
520
+ root: [elem.vertex for elem in tour]
521
+ for root, tour in self._tours.items()
522
+ }
523
+ }
524
+
525
+ # ============================================================================
526
+ # STATISTICS
527
+ # ============================================================================
528
+
529
+ def get_statistics(self) -> Dict[str, Any]:
530
+ """Get Euler tour statistics."""
531
+ return {
532
+ 'vertices': len(self._vertices),
533
+ 'edges': len(self),
534
+ 'trees': len(self._tours),
535
+ 'avg_tree_size': len(self._vertices) / max(len(self._tours), 1),
536
+ 'tour_elements': sum(len(tour) for tour in self._tours.values())
537
+ }
538
+
539
+ # ============================================================================
540
+ # UTILITY METHODS
541
+ # ============================================================================
542
+
543
+ @property
544
+ def strategy_name(self) -> str:
545
+ """Get strategy name."""
546
+ return "EULER_TOUR"
547
+
548
+ @property
549
+ def supported_traits(self) -> List[EdgeTrait]:
550
+ """Get supported traits."""
551
+ return [EdgeTrait.DIRECTED, EdgeTrait.SPARSE]
552
+
553
+ def get_backend_info(self) -> Dict[str, Any]:
554
+ """Get backend information."""
555
+ return {
556
+ 'strategy': 'Euler Tour Trees',
557
+ 'description': 'Dynamic tree connectivity with O(log n) updates',
558
+ **self.get_statistics()
559
+ }
560
+
@@ -8,7 +8,7 @@ capacity constraints, flow algorithms, and network flow optimization.
8
8
  from typing import Any, Iterator, Dict, List, Set, Optional, Tuple, DefaultDict
9
9
  from collections import defaultdict, deque
10
10
  import math
11
- from ._base_edge import aEdgeStrategy
11
+ from ._base_edge import AEdgeStrategy
12
12
  from ...defs import EdgeMode, EdgeTrait
13
13
 
14
14
 
@@ -89,7 +89,7 @@ class FlowEdge:
89
89
  return f"FlowEdge({self.source}->{self.target}: {self.flow}/{self.capacity})"
90
90
 
91
91
 
92
- class xFlowNetworkStrategy(aEdgeStrategy):
92
+ class FlowNetworkStrategy(AEdgeStrategy):
93
93
  """
94
94
  Flow Network strategy for capacity-constrained flow graphs.
95
95