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.
- 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.24.dist-info/METADATA +900 -0
- exonware_xwnode-0.0.1.24.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/METADATA +0 -168
- 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.24.dist-info}/WHEEL +0 -0
- {exonware_xwnode-0.0.1.22.dist-info → exonware_xwnode-0.0.1.24.dist-info}/licenses/LICENSE +0 -0
@@ -1,12 +1,24 @@
|
|
1
|
-
#exonware
|
1
|
+
#exonware/xwnode/src/exonware/xwnode/nodes/strategies/persistent_tree.py
|
2
2
|
"""
|
3
3
|
Persistent Tree Node Strategy Implementation
|
4
4
|
|
5
|
+
Status: Production Ready
|
6
|
+
True Purpose: Immutable functional tree with structural sharing and versioning
|
7
|
+
Complexity: O(log n) operations with structural sharing
|
8
|
+
Production Features: ✓ Immutability, ✓ Structural Sharing, ✓ Version Management, ✓ Snapshots
|
9
|
+
|
5
10
|
This module implements the PERSISTENT_TREE strategy for immutable functional
|
6
11
|
trees with structural sharing and lock-free concurrency.
|
12
|
+
|
13
|
+
Company: eXonware.com
|
14
|
+
Author: Eng. Muhammad AlShehri
|
15
|
+
Email: connect@exonware.com
|
16
|
+
Version: 0.0.1.24
|
17
|
+
Generation Date: October 12, 2025
|
7
18
|
"""
|
8
19
|
|
9
20
|
from typing import Any, Iterator, List, Dict, Optional, Tuple
|
21
|
+
import time
|
10
22
|
from .base import ANodeTreeStrategy
|
11
23
|
from .contracts import NodeType
|
12
24
|
from ...defs import NodeMode, NodeTrait
|
@@ -53,7 +65,7 @@ sharing unchanged nodes.
|
|
53
65
|
"""
|
54
66
|
|
55
67
|
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
56
|
-
"""Initialize the persistent tree strategy."""
|
68
|
+
"""Initialize the persistent tree strategy with version management."""
|
57
69
|
super().__init__(NodeMode.PERSISTENT_TREE, traits, **options)
|
58
70
|
|
59
71
|
self.case_sensitive = options.get('case_sensitive', True)
|
@@ -64,6 +76,11 @@ sharing unchanged nodes.
|
|
64
76
|
self._size = 0
|
65
77
|
self._version = 0
|
66
78
|
|
79
|
+
# Version management
|
80
|
+
self._version_history: List[Tuple[int, PersistentTreeNode, float]] = [] # version, root, timestamp
|
81
|
+
self._max_versions = options.get('max_versions', 100) # Retention limit
|
82
|
+
self._version_retention_policy = options.get('retention_policy', 'keep_recent') # or 'keep_all'
|
83
|
+
|
67
84
|
# Statistics
|
68
85
|
self._total_allocations = 0
|
69
86
|
self._total_shares = 0
|
@@ -261,10 +278,14 @@ sharing unchanged nodes.
|
|
261
278
|
# ============================================================================
|
262
279
|
|
263
280
|
def put(self, key: Any, value: Any = None) -> None:
|
264
|
-
"""Store a key-value pair,
|
281
|
+
"""Store a key-value pair, creating new version."""
|
265
282
|
if not isinstance(key, str):
|
266
283
|
key = str(key)
|
267
284
|
|
285
|
+
# Save current version to history before modification
|
286
|
+
if self._root is not None:
|
287
|
+
self._save_version()
|
288
|
+
|
268
289
|
new_root, inserted = self._insert_node(self._root, key, value)
|
269
290
|
self._root = new_root
|
270
291
|
|
@@ -313,6 +334,14 @@ sharing unchanged nodes.
|
|
313
334
|
"""Get number of key-value pairs."""
|
314
335
|
return self._size
|
315
336
|
|
337
|
+
def __len__(self) -> int:
|
338
|
+
"""Get number of key-value pairs."""
|
339
|
+
return self._size
|
340
|
+
|
341
|
+
def to_native(self) -> Dict[str, Any]:
|
342
|
+
"""Convert to native Python dict."""
|
343
|
+
return dict(self.items())
|
344
|
+
|
316
345
|
def is_empty(self) -> bool:
|
317
346
|
"""Check if tree is empty."""
|
318
347
|
return self._root is None
|
@@ -343,17 +372,17 @@ sharing unchanged nodes.
|
|
343
372
|
# PERSISTENT TREE SPECIFIC OPERATIONS
|
344
373
|
# ============================================================================
|
345
374
|
|
346
|
-
def snapshot(self) -> '
|
375
|
+
def snapshot(self) -> 'PersistentTreeStrategy':
|
347
376
|
"""Create a snapshot of the current tree."""
|
348
|
-
snapshot =
|
377
|
+
snapshot = PersistentTreeStrategy(self.traits, **self.options)
|
349
378
|
snapshot._root = self._root # Share root (structural sharing)
|
350
379
|
snapshot._size = self._size
|
351
380
|
snapshot._version = self._version
|
352
381
|
return snapshot
|
353
382
|
|
354
|
-
def merge(self, other: '
|
383
|
+
def merge(self, other: 'PersistentTreeStrategy') -> 'PersistentTreeStrategy':
|
355
384
|
"""Merge with another persistent tree."""
|
356
|
-
result =
|
385
|
+
result = PersistentTreeStrategy(self.traits, **self.options)
|
357
386
|
|
358
387
|
# Copy all items from both trees
|
359
388
|
for key, value in self.items():
|
@@ -363,21 +392,132 @@ sharing unchanged nodes.
|
|
363
392
|
|
364
393
|
return result
|
365
394
|
|
395
|
+
# ============================================================================
|
396
|
+
# VERSION MANAGEMENT (Production Feature)
|
397
|
+
# ============================================================================
|
398
|
+
|
399
|
+
def _save_version(self) -> None:
|
400
|
+
"""Save current version to history."""
|
401
|
+
if self._root is None:
|
402
|
+
return
|
403
|
+
|
404
|
+
# Add current state to version history
|
405
|
+
self._version_history.append((
|
406
|
+
self._version,
|
407
|
+
self._root,
|
408
|
+
time.time()
|
409
|
+
))
|
410
|
+
|
411
|
+
# Apply retention policy
|
412
|
+
if self._version_retention_policy == 'keep_recent':
|
413
|
+
# Keep only last N versions
|
414
|
+
if len(self._version_history) > self._max_versions:
|
415
|
+
self._version_history = self._version_history[-self._max_versions:]
|
416
|
+
|
366
417
|
def get_version(self) -> int:
|
367
418
|
"""Get current version number."""
|
368
419
|
return self._version
|
369
420
|
|
421
|
+
def get_version_history(self) -> List[Tuple[int, float]]:
|
422
|
+
"""Get list of available versions with timestamps."""
|
423
|
+
return [(v, ts) for v, _, ts in self._version_history]
|
424
|
+
|
425
|
+
def restore_version(self, version: int) -> bool:
|
426
|
+
"""
|
427
|
+
Restore to a specific version.
|
428
|
+
|
429
|
+
Args:
|
430
|
+
version: Version number to restore
|
431
|
+
|
432
|
+
Returns:
|
433
|
+
True if version was found and restored
|
434
|
+
"""
|
435
|
+
for v, root, _ in self._version_history:
|
436
|
+
if v == version:
|
437
|
+
self._root = root # Structural sharing
|
438
|
+
self._version = version
|
439
|
+
self._size = len(list(self._inorder_traversal(root)))
|
440
|
+
return True
|
441
|
+
return False
|
442
|
+
|
443
|
+
def compare_versions(self, version1: int, version2: int) -> Dict[str, Any]:
|
444
|
+
"""
|
445
|
+
Compare two versions and return differences.
|
446
|
+
|
447
|
+
Args:
|
448
|
+
version1: First version number
|
449
|
+
version2: Second version number
|
450
|
+
|
451
|
+
Returns:
|
452
|
+
Dict with added, removed, and modified keys
|
453
|
+
"""
|
454
|
+
# Find roots for both versions
|
455
|
+
root1 = None
|
456
|
+
root2 = None
|
457
|
+
|
458
|
+
for v, root, _ in self._version_history:
|
459
|
+
if v == version1:
|
460
|
+
root1 = root
|
461
|
+
if v == version2:
|
462
|
+
root2 = root
|
463
|
+
|
464
|
+
if root1 is None or root2 is None:
|
465
|
+
return {'error': 'Version not found'}
|
466
|
+
|
467
|
+
# Get items from both versions
|
468
|
+
items1 = dict(self._inorder_traversal(root1))
|
469
|
+
items2 = dict(self._inorder_traversal(root2))
|
470
|
+
|
471
|
+
# Calculate differences
|
472
|
+
keys1 = set(items1.keys())
|
473
|
+
keys2 = set(items2.keys())
|
474
|
+
|
475
|
+
return {
|
476
|
+
'added': list(keys2 - keys1),
|
477
|
+
'removed': list(keys1 - keys2),
|
478
|
+
'modified': [k for k in (keys1 & keys2) if items1[k] != items2[k]],
|
479
|
+
'unchanged': [k for k in (keys1 & keys2) if items1[k] == items2[k]]
|
480
|
+
}
|
481
|
+
|
482
|
+
def cleanup_old_versions(self, keep_count: int = 10) -> int:
|
483
|
+
"""
|
484
|
+
Clean up old versions, keeping only most recent.
|
485
|
+
|
486
|
+
Args:
|
487
|
+
keep_count: Number of versions to keep
|
488
|
+
|
489
|
+
Returns:
|
490
|
+
Number of versions removed
|
491
|
+
"""
|
492
|
+
if len(self._version_history) <= keep_count:
|
493
|
+
return 0
|
494
|
+
|
495
|
+
removed = len(self._version_history) - keep_count
|
496
|
+
self._version_history = self._version_history[-keep_count:]
|
497
|
+
return removed
|
498
|
+
|
370
499
|
def get_stats(self) -> Dict[str, Any]:
|
371
|
-
"""Get performance statistics."""
|
500
|
+
"""Get performance statistics with version management."""
|
372
501
|
return {
|
373
502
|
'size': self._size,
|
374
503
|
'height': self._get_height(self._root),
|
375
504
|
'max_height': self._max_height,
|
376
505
|
'version': self._version,
|
506
|
+
'version_history_size': len(self._version_history),
|
507
|
+
'max_versions': self._max_versions,
|
508
|
+
'retention_policy': self._version_retention_policy,
|
377
509
|
'total_allocations': self._total_allocations,
|
378
510
|
'total_shares': self._total_shares,
|
379
511
|
'sharing_ratio': self._total_shares / max(1, self._total_allocations),
|
380
512
|
'strategy': 'PERSISTENT_TREE',
|
381
|
-
'backend': 'Immutable AVL tree with structural sharing',
|
513
|
+
'backend': 'Immutable AVL tree with structural sharing and version management',
|
514
|
+
'production_features': [
|
515
|
+
'Immutability',
|
516
|
+
'Structural Sharing',
|
517
|
+
'Version History',
|
518
|
+
'Version Comparison',
|
519
|
+
'Version Restoration',
|
520
|
+
'Automatic Retention Policy'
|
521
|
+
],
|
382
522
|
'traits': [trait.name for trait in NodeTrait if self.has_trait(trait)]
|
383
523
|
}
|