exonware-xwnode 0.0.1.12__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 +14 -0
- exonware/xwnode/__init__.py +127 -0
- exonware/xwnode/base.py +676 -0
- exonware/xwnode/config.py +178 -0
- exonware/xwnode/contracts.py +730 -0
- exonware/xwnode/errors.py +503 -0
- exonware/xwnode/facade.py +460 -0
- exonware/xwnode/strategies/__init__.py +158 -0
- exonware/xwnode/strategies/advisor.py +463 -0
- exonware/xwnode/strategies/edges/__init__.py +32 -0
- exonware/xwnode/strategies/edges/adj_list.py +227 -0
- exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
- exonware/xwnode/strategies/edges/base.py +169 -0
- exonware/xwnode/strategies/flyweight.py +328 -0
- exonware/xwnode/strategies/impls/__init__.py +13 -0
- exonware/xwnode/strategies/impls/_base_edge.py +403 -0
- exonware/xwnode/strategies/impls/_base_node.py +307 -0
- exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
- exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
- exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
- exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
- exonware/xwnode/strategies/impls/edge_coo.py +533 -0
- exonware/xwnode/strategies/impls/edge_csc.py +447 -0
- exonware/xwnode/strategies/impls/edge_csr.py +492 -0
- exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
- exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
- exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
- exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
- exonware/xwnode/strategies/impls/edge_octree.py +574 -0
- exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
- exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
- exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
- exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
- exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
- exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
- exonware/xwnode/strategies/manager.py +775 -0
- exonware/xwnode/strategies/metrics.py +538 -0
- exonware/xwnode/strategies/migration.py +432 -0
- exonware/xwnode/strategies/nodes/__init__.py +50 -0
- exonware/xwnode/strategies/nodes/_base_node.py +307 -0
- exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
- exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
- exonware/xwnode/strategies/nodes/array_list.py +209 -0
- exonware/xwnode/strategies/nodes/base.py +247 -0
- exonware/xwnode/strategies/nodes/deque.py +200 -0
- exonware/xwnode/strategies/nodes/hash_map.py +135 -0
- exonware/xwnode/strategies/nodes/heap.py +307 -0
- exonware/xwnode/strategies/nodes/linked_list.py +232 -0
- exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
- exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
- exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
- exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
- exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
- exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
- exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
- exonware/xwnode/strategies/nodes/node_btree.py +357 -0
- exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
- exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
- exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
- exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
- exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
- exonware/xwnode/strategies/nodes/node_heap.py +191 -0
- exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
- exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
- exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
- exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
- exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
- exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
- exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
- exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
- exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
- exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
- exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
- exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
- exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
- exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
- exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
- exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
- exonware/xwnode/strategies/nodes/node_treap.py +387 -0
- exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
- exonware/xwnode/strategies/nodes/node_trie.py +252 -0
- exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
- exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
- exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
- exonware/xwnode/strategies/nodes/queue.py +161 -0
- exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
- exonware/xwnode/strategies/nodes/stack.py +152 -0
- exonware/xwnode/strategies/nodes/trie.py +274 -0
- exonware/xwnode/strategies/nodes/union_find.py +283 -0
- exonware/xwnode/strategies/pattern_detector.py +603 -0
- exonware/xwnode/strategies/performance_monitor.py +487 -0
- exonware/xwnode/strategies/queries/__init__.py +24 -0
- exonware/xwnode/strategies/queries/base.py +236 -0
- exonware/xwnode/strategies/queries/cql.py +201 -0
- exonware/xwnode/strategies/queries/cypher.py +181 -0
- exonware/xwnode/strategies/queries/datalog.py +70 -0
- exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
- exonware/xwnode/strategies/queries/eql.py +70 -0
- exonware/xwnode/strategies/queries/flux.py +70 -0
- exonware/xwnode/strategies/queries/gql.py +70 -0
- exonware/xwnode/strategies/queries/graphql.py +240 -0
- exonware/xwnode/strategies/queries/gremlin.py +181 -0
- exonware/xwnode/strategies/queries/hiveql.py +214 -0
- exonware/xwnode/strategies/queries/hql.py +70 -0
- exonware/xwnode/strategies/queries/jmespath.py +219 -0
- exonware/xwnode/strategies/queries/jq.py +66 -0
- exonware/xwnode/strategies/queries/json_query.py +66 -0
- exonware/xwnode/strategies/queries/jsoniq.py +248 -0
- exonware/xwnode/strategies/queries/kql.py +70 -0
- exonware/xwnode/strategies/queries/linq.py +238 -0
- exonware/xwnode/strategies/queries/logql.py +70 -0
- exonware/xwnode/strategies/queries/mql.py +68 -0
- exonware/xwnode/strategies/queries/n1ql.py +210 -0
- exonware/xwnode/strategies/queries/partiql.py +70 -0
- exonware/xwnode/strategies/queries/pig.py +215 -0
- exonware/xwnode/strategies/queries/promql.py +70 -0
- exonware/xwnode/strategies/queries/sparql.py +220 -0
- exonware/xwnode/strategies/queries/sql.py +275 -0
- exonware/xwnode/strategies/queries/xml_query.py +66 -0
- exonware/xwnode/strategies/queries/xpath.py +223 -0
- exonware/xwnode/strategies/queries/xquery.py +258 -0
- exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
- exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
- exonware/xwnode/strategies/registry.py +604 -0
- exonware/xwnode/strategies/simple.py +273 -0
- exonware/xwnode/strategies/utils.py +532 -0
- exonware/xwnode/types.py +912 -0
- exonware/xwnode/version.py +78 -0
- exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
- exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
- exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
- exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,513 @@
|
|
1
|
+
"""
|
2
|
+
Dynamic Bitset Node Strategy Implementation
|
3
|
+
|
4
|
+
This module implements the BITSET_DYNAMIC strategy for dynamic bitset
|
5
|
+
operations with automatic resizing and bit manipulation capabilities.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Iterator, List, Dict, Optional, Tuple, Union
|
9
|
+
from .base import ANodeMatrixStrategy
|
10
|
+
from ...types import NodeMode, NodeTrait
|
11
|
+
|
12
|
+
|
13
|
+
class BitsetDynamicStrategy(ANodeMatrixStrategy):
|
14
|
+
"""
|
15
|
+
Dynamic Bitset node strategy for bit manipulation operations.
|
16
|
+
|
17
|
+
Provides efficient set operations, bit manipulation, and automatic
|
18
|
+
resizing for large-scale boolean data processing.
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
22
|
+
"""Initialize the Dynamic Bitset strategy."""
|
23
|
+
super().__init__(NodeMode.BITSET_DYNAMIC, traits, **options)
|
24
|
+
|
25
|
+
self.initial_capacity = options.get('initial_capacity', 64)
|
26
|
+
self.growth_factor = options.get('growth_factor', 2.0)
|
27
|
+
self.auto_trim = options.get('auto_trim', True)
|
28
|
+
|
29
|
+
# Core dynamic bitset
|
30
|
+
self._bits: List[int] = [0] * ((self.initial_capacity + 63) // 64) # 64-bit chunks
|
31
|
+
self._capacity = len(self._bits) * 64
|
32
|
+
self._size = 0 # Number of set bits
|
33
|
+
self._highest_bit = -1 # Highest set bit index
|
34
|
+
|
35
|
+
# Statistics
|
36
|
+
self._total_operations = 0
|
37
|
+
self._resize_count = 0
|
38
|
+
self._trim_count = 0
|
39
|
+
self._bit_flips = 0
|
40
|
+
|
41
|
+
def get_supported_traits(self) -> NodeTrait:
|
42
|
+
"""Get the traits supported by the dynamic bitset strategy."""
|
43
|
+
return (NodeTrait.INDEXED | NodeTrait.COMPRESSED)
|
44
|
+
|
45
|
+
def _ensure_capacity(self, bit_index: int) -> None:
|
46
|
+
"""Ensure bitset can accommodate the given bit index."""
|
47
|
+
required_capacity = bit_index + 1
|
48
|
+
|
49
|
+
if required_capacity > self._capacity:
|
50
|
+
# Calculate new capacity
|
51
|
+
new_capacity = self._capacity
|
52
|
+
while new_capacity < required_capacity:
|
53
|
+
new_capacity = int(new_capacity * self.growth_factor)
|
54
|
+
|
55
|
+
# Extend bits array
|
56
|
+
old_chunks = len(self._bits)
|
57
|
+
new_chunks = (new_capacity + 63) // 64
|
58
|
+
self._bits.extend([0] * (new_chunks - old_chunks))
|
59
|
+
self._capacity = new_chunks * 64
|
60
|
+
self._resize_count += 1
|
61
|
+
|
62
|
+
def _trim_if_needed(self) -> None:
|
63
|
+
"""Trim unused capacity if auto_trim is enabled."""
|
64
|
+
if not self.auto_trim or self._highest_bit == -1:
|
65
|
+
return
|
66
|
+
|
67
|
+
required_chunks = (self._highest_bit // 64) + 1
|
68
|
+
current_chunks = len(self._bits)
|
69
|
+
|
70
|
+
# Trim if we have more than 2x the required chunks
|
71
|
+
if current_chunks > required_chunks * 2:
|
72
|
+
self._bits = self._bits[:required_chunks]
|
73
|
+
self._capacity = len(self._bits) * 64
|
74
|
+
self._trim_count += 1
|
75
|
+
|
76
|
+
def _update_highest_bit(self) -> None:
|
77
|
+
"""Update the highest set bit index."""
|
78
|
+
self._highest_bit = -1
|
79
|
+
for i in range(len(self._bits) - 1, -1, -1):
|
80
|
+
if self._bits[i] != 0:
|
81
|
+
# Find highest bit in this chunk
|
82
|
+
chunk = self._bits[i]
|
83
|
+
bit_pos = i * 64
|
84
|
+
while chunk > 0:
|
85
|
+
if chunk & 1:
|
86
|
+
self._highest_bit = bit_pos
|
87
|
+
chunk >>= 1
|
88
|
+
bit_pos += 1
|
89
|
+
break
|
90
|
+
|
91
|
+
def _set_bit(self, bit_index: int, value: bool) -> bool:
|
92
|
+
"""Set bit at index to value. Returns True if bit changed."""
|
93
|
+
self._ensure_capacity(bit_index)
|
94
|
+
|
95
|
+
chunk_index = bit_index // 64
|
96
|
+
bit_position = bit_index % 64
|
97
|
+
mask = 1 << bit_position
|
98
|
+
|
99
|
+
old_value = bool(self._bits[chunk_index] & mask)
|
100
|
+
|
101
|
+
if value:
|
102
|
+
if not old_value:
|
103
|
+
self._bits[chunk_index] |= mask
|
104
|
+
self._size += 1
|
105
|
+
self._highest_bit = max(self._highest_bit, bit_index)
|
106
|
+
self._bit_flips += 1
|
107
|
+
return True
|
108
|
+
else:
|
109
|
+
if old_value:
|
110
|
+
self._bits[chunk_index] &= ~mask
|
111
|
+
self._size -= 1
|
112
|
+
if bit_index == self._highest_bit:
|
113
|
+
self._update_highest_bit()
|
114
|
+
self._bit_flips += 1
|
115
|
+
return True
|
116
|
+
|
117
|
+
return False
|
118
|
+
|
119
|
+
def _get_bit(self, bit_index: int) -> bool:
|
120
|
+
"""Get bit value at index."""
|
121
|
+
if bit_index < 0 or bit_index >= self._capacity:
|
122
|
+
return False
|
123
|
+
|
124
|
+
chunk_index = bit_index // 64
|
125
|
+
if chunk_index >= len(self._bits):
|
126
|
+
return False
|
127
|
+
|
128
|
+
bit_position = bit_index % 64
|
129
|
+
mask = 1 << bit_position
|
130
|
+
return bool(self._bits[chunk_index] & mask)
|
131
|
+
|
132
|
+
def _find_next_set_bit(self, start_index: int = 0) -> int:
|
133
|
+
"""Find next set bit starting from start_index."""
|
134
|
+
for i in range(start_index, self._highest_bit + 1):
|
135
|
+
if self._get_bit(i):
|
136
|
+
return i
|
137
|
+
return -1
|
138
|
+
|
139
|
+
def _find_next_clear_bit(self, start_index: int = 0) -> int:
|
140
|
+
"""Find next clear bit starting from start_index."""
|
141
|
+
i = start_index
|
142
|
+
while i <= self._highest_bit + 64: # Search a bit beyond current range
|
143
|
+
if not self._get_bit(i):
|
144
|
+
return i
|
145
|
+
i += 1
|
146
|
+
return i
|
147
|
+
|
148
|
+
# ============================================================================
|
149
|
+
# CORE OPERATIONS
|
150
|
+
# ============================================================================
|
151
|
+
|
152
|
+
def put(self, key: Any, value: Any = None) -> None:
|
153
|
+
"""Set bit at key index."""
|
154
|
+
self._total_operations += 1
|
155
|
+
|
156
|
+
if isinstance(key, str) and key.isdigit():
|
157
|
+
bit_index = int(key)
|
158
|
+
elif isinstance(key, int):
|
159
|
+
bit_index = key
|
160
|
+
else:
|
161
|
+
bit_index = hash(str(key)) % (2**20) # Limit to reasonable range
|
162
|
+
|
163
|
+
# Interpret value as boolean
|
164
|
+
if value is None:
|
165
|
+
bit_value = True # Default to setting the bit
|
166
|
+
elif isinstance(value, bool):
|
167
|
+
bit_value = value
|
168
|
+
elif isinstance(value, (int, float)):
|
169
|
+
bit_value = bool(value)
|
170
|
+
else:
|
171
|
+
bit_value = bool(value)
|
172
|
+
|
173
|
+
self._set_bit(abs(bit_index), bit_value)
|
174
|
+
self._trim_if_needed()
|
175
|
+
|
176
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
177
|
+
"""Get bit value at key index."""
|
178
|
+
key_str = str(key)
|
179
|
+
|
180
|
+
if key_str == "bitset_info":
|
181
|
+
return {
|
182
|
+
'size': self._size,
|
183
|
+
'capacity': self._capacity,
|
184
|
+
'highest_bit': self._highest_bit,
|
185
|
+
'total_operations': self._total_operations,
|
186
|
+
'resize_count': self._resize_count,
|
187
|
+
'bit_flips': self._bit_flips,
|
188
|
+
'utilization': self._size / max(1, self._capacity) * 100
|
189
|
+
}
|
190
|
+
elif key_str == "bit_count":
|
191
|
+
return self._size
|
192
|
+
elif key_str == "all_set_bits":
|
193
|
+
return self.get_set_bits()
|
194
|
+
elif key_str == "bit_pattern":
|
195
|
+
return self.to_bit_string()
|
196
|
+
|
197
|
+
if key_str.isdigit():
|
198
|
+
bit_index = int(key_str)
|
199
|
+
return self._get_bit(bit_index)
|
200
|
+
elif isinstance(key, int):
|
201
|
+
return self._get_bit(key)
|
202
|
+
else:
|
203
|
+
bit_index = hash(str(key)) % (2**20)
|
204
|
+
return self._get_bit(abs(bit_index))
|
205
|
+
|
206
|
+
def has(self, key: Any) -> bool:
|
207
|
+
"""Check if bit at key index is set."""
|
208
|
+
key_str = str(key)
|
209
|
+
|
210
|
+
if key_str in ["bitset_info", "bit_count", "all_set_bits", "bit_pattern"]:
|
211
|
+
return True
|
212
|
+
|
213
|
+
if key_str.isdigit():
|
214
|
+
bit_index = int(key_str)
|
215
|
+
return self._get_bit(bit_index)
|
216
|
+
elif isinstance(key, int):
|
217
|
+
return self._get_bit(key)
|
218
|
+
else:
|
219
|
+
bit_index = hash(str(key)) % (2**20)
|
220
|
+
return self._get_bit(abs(bit_index))
|
221
|
+
|
222
|
+
def remove(self, key: Any) -> bool:
|
223
|
+
"""Clear bit at key index."""
|
224
|
+
if isinstance(key, str) and key.isdigit():
|
225
|
+
bit_index = int(key)
|
226
|
+
elif isinstance(key, int):
|
227
|
+
bit_index = key
|
228
|
+
else:
|
229
|
+
bit_index = hash(str(key)) % (2**20)
|
230
|
+
|
231
|
+
return self._set_bit(abs(bit_index), False)
|
232
|
+
|
233
|
+
def delete(self, key: Any) -> bool:
|
234
|
+
"""Clear bit at key index (alias for remove)."""
|
235
|
+
return self.remove(key)
|
236
|
+
|
237
|
+
def clear(self) -> None:
|
238
|
+
"""Clear all bits."""
|
239
|
+
self._bits = [0] * ((self.initial_capacity + 63) // 64)
|
240
|
+
self._capacity = len(self._bits) * 64
|
241
|
+
self._size = 0
|
242
|
+
self._highest_bit = -1
|
243
|
+
self._total_operations = 0
|
244
|
+
self._resize_count = 0
|
245
|
+
self._trim_count = 0
|
246
|
+
self._bit_flips = 0
|
247
|
+
|
248
|
+
def keys(self) -> Iterator[str]:
|
249
|
+
"""Get all set bit indices as strings."""
|
250
|
+
for i in range(self._highest_bit + 1):
|
251
|
+
if self._get_bit(i):
|
252
|
+
yield str(i)
|
253
|
+
|
254
|
+
def values(self) -> Iterator[Any]:
|
255
|
+
"""Get all set bit values (always True)."""
|
256
|
+
for i in range(self._highest_bit + 1):
|
257
|
+
if self._get_bit(i):
|
258
|
+
yield True
|
259
|
+
|
260
|
+
def items(self) -> Iterator[tuple[str, Any]]:
|
261
|
+
"""Get all set bit indices and values."""
|
262
|
+
for i in range(self._highest_bit + 1):
|
263
|
+
if self._get_bit(i):
|
264
|
+
yield (str(i), True)
|
265
|
+
|
266
|
+
def __len__(self) -> int:
|
267
|
+
"""Get number of set bits."""
|
268
|
+
return self._size
|
269
|
+
|
270
|
+
def to_native(self) -> Dict[str, bool]:
|
271
|
+
"""Convert to native Python dict of set bits."""
|
272
|
+
return {str(i): True for i in range(self._highest_bit + 1) if self._get_bit(i)}
|
273
|
+
|
274
|
+
@property
|
275
|
+
def is_list(self) -> bool:
|
276
|
+
"""This can behave like a list for indexed access."""
|
277
|
+
return True
|
278
|
+
|
279
|
+
@property
|
280
|
+
def is_dict(self) -> bool:
|
281
|
+
"""This behaves like a dict."""
|
282
|
+
return True
|
283
|
+
|
284
|
+
# ============================================================================
|
285
|
+
# BITSET SPECIFIC OPERATIONS
|
286
|
+
# ============================================================================
|
287
|
+
|
288
|
+
def set_bit(self, index: int, value: bool = True) -> None:
|
289
|
+
"""Set specific bit to value."""
|
290
|
+
self._set_bit(abs(index), value)
|
291
|
+
self._trim_if_needed()
|
292
|
+
|
293
|
+
def clear_bit(self, index: int) -> None:
|
294
|
+
"""Clear specific bit."""
|
295
|
+
self._set_bit(abs(index), False)
|
296
|
+
self._trim_if_needed()
|
297
|
+
|
298
|
+
def flip_bit(self, index: int) -> None:
|
299
|
+
"""Flip specific bit."""
|
300
|
+
current_value = self._get_bit(abs(index))
|
301
|
+
self._set_bit(abs(index), not current_value)
|
302
|
+
self._trim_if_needed()
|
303
|
+
|
304
|
+
def get_set_bits(self) -> List[int]:
|
305
|
+
"""Get list of all set bit indices."""
|
306
|
+
result = []
|
307
|
+
for i in range(self._highest_bit + 1):
|
308
|
+
if self._get_bit(i):
|
309
|
+
result.append(i)
|
310
|
+
return result
|
311
|
+
|
312
|
+
def get_clear_bits(self, max_index: Optional[int] = None) -> List[int]:
|
313
|
+
"""Get list of clear bit indices up to max_index."""
|
314
|
+
if max_index is None:
|
315
|
+
max_index = self._highest_bit + 10 # Some reasonable limit
|
316
|
+
|
317
|
+
result = []
|
318
|
+
for i in range(min(max_index + 1, self._capacity)):
|
319
|
+
if not self._get_bit(i):
|
320
|
+
result.append(i)
|
321
|
+
return result
|
322
|
+
|
323
|
+
def count_bits(self, start: int = 0, end: Optional[int] = None) -> int:
|
324
|
+
"""Count set bits in range [start, end)."""
|
325
|
+
if end is None:
|
326
|
+
end = self._highest_bit + 1
|
327
|
+
|
328
|
+
count = 0
|
329
|
+
for i in range(start, min(end, self._highest_bit + 1)):
|
330
|
+
if self._get_bit(i):
|
331
|
+
count += 1
|
332
|
+
return count
|
333
|
+
|
334
|
+
def find_first_set(self) -> int:
|
335
|
+
"""Find index of first set bit (-1 if none)."""
|
336
|
+
return self._find_next_set_bit(0)
|
337
|
+
|
338
|
+
def find_last_set(self) -> int:
|
339
|
+
"""Find index of last set bit (-1 if none)."""
|
340
|
+
return self._highest_bit if self._size > 0 else -1
|
341
|
+
|
342
|
+
def find_next_set(self, start: int) -> int:
|
343
|
+
"""Find next set bit after start index."""
|
344
|
+
return self._find_next_set_bit(start + 1)
|
345
|
+
|
346
|
+
def find_next_clear(self, start: int) -> int:
|
347
|
+
"""Find next clear bit after start index."""
|
348
|
+
return self._find_next_clear_bit(start)
|
349
|
+
|
350
|
+
def set_range(self, start: int, end: int, value: bool = True) -> None:
|
351
|
+
"""Set range of bits [start, end) to value."""
|
352
|
+
for i in range(start, end):
|
353
|
+
self._set_bit(i, value)
|
354
|
+
self._trim_if_needed()
|
355
|
+
|
356
|
+
def flip_range(self, start: int, end: int) -> None:
|
357
|
+
"""Flip range of bits [start, end)."""
|
358
|
+
for i in range(start, end):
|
359
|
+
current = self._get_bit(i)
|
360
|
+
self._set_bit(i, not current)
|
361
|
+
self._trim_if_needed()
|
362
|
+
|
363
|
+
def logical_and(self, other: 'xBitsetDynamicStrategy') -> 'xBitsetDynamicStrategy':
|
364
|
+
"""Perform logical AND with another bitset."""
|
365
|
+
result = xBitsetDynamicStrategy()
|
366
|
+
max_index = max(self._highest_bit, other._highest_bit)
|
367
|
+
|
368
|
+
for i in range(max_index + 1):
|
369
|
+
if self._get_bit(i) and other._get_bit(i):
|
370
|
+
result._set_bit(i, True)
|
371
|
+
|
372
|
+
return result
|
373
|
+
|
374
|
+
def logical_or(self, other: 'xBitsetDynamicStrategy') -> 'xBitsetDynamicStrategy':
|
375
|
+
"""Perform logical OR with another bitset."""
|
376
|
+
result = xBitsetDynamicStrategy()
|
377
|
+
max_index = max(self._highest_bit, other._highest_bit)
|
378
|
+
|
379
|
+
for i in range(max_index + 1):
|
380
|
+
if self._get_bit(i) or other._get_bit(i):
|
381
|
+
result._set_bit(i, True)
|
382
|
+
|
383
|
+
return result
|
384
|
+
|
385
|
+
def logical_xor(self, other: 'xBitsetDynamicStrategy') -> 'xBitsetDynamicStrategy':
|
386
|
+
"""Perform logical XOR with another bitset."""
|
387
|
+
result = xBitsetDynamicStrategy()
|
388
|
+
max_index = max(self._highest_bit, other._highest_bit)
|
389
|
+
|
390
|
+
for i in range(max_index + 1):
|
391
|
+
if self._get_bit(i) != other._get_bit(i):
|
392
|
+
result._set_bit(i, True)
|
393
|
+
|
394
|
+
return result
|
395
|
+
|
396
|
+
def logical_not(self, max_index: Optional[int] = None) -> 'xBitsetDynamicStrategy':
|
397
|
+
"""Perform logical NOT (up to max_index)."""
|
398
|
+
if max_index is None:
|
399
|
+
max_index = self._highest_bit + 64 # Reasonable extension
|
400
|
+
|
401
|
+
result = xBitsetDynamicStrategy()
|
402
|
+
for i in range(max_index + 1):
|
403
|
+
if not self._get_bit(i):
|
404
|
+
result._set_bit(i, True)
|
405
|
+
|
406
|
+
return result
|
407
|
+
|
408
|
+
def is_subset_of(self, other: 'xBitsetDynamicStrategy') -> bool:
|
409
|
+
"""Check if this bitset is a subset of another."""
|
410
|
+
for i in range(self._highest_bit + 1):
|
411
|
+
if self._get_bit(i) and not other._get_bit(i):
|
412
|
+
return False
|
413
|
+
return True
|
414
|
+
|
415
|
+
def is_superset_of(self, other: 'xBitsetDynamicStrategy') -> bool:
|
416
|
+
"""Check if this bitset is a superset of another."""
|
417
|
+
return other.is_subset_of(self)
|
418
|
+
|
419
|
+
def intersects(self, other: 'xBitsetDynamicStrategy') -> bool:
|
420
|
+
"""Check if this bitset intersects with another."""
|
421
|
+
max_index = min(self._highest_bit, other._highest_bit)
|
422
|
+
for i in range(max_index + 1):
|
423
|
+
if self._get_bit(i) and other._get_bit(i):
|
424
|
+
return True
|
425
|
+
return False
|
426
|
+
|
427
|
+
def to_bit_string(self, max_length: int = 64) -> str:
|
428
|
+
"""Convert to binary string representation."""
|
429
|
+
if self._highest_bit == -1:
|
430
|
+
return "0"
|
431
|
+
|
432
|
+
length = min(self._highest_bit + 1, max_length)
|
433
|
+
bits = []
|
434
|
+
for i in range(length - 1, -1, -1): # MSB first
|
435
|
+
bits.append('1' if self._get_bit(i) else '0')
|
436
|
+
|
437
|
+
result = ''.join(bits)
|
438
|
+
if length < self._highest_bit + 1:
|
439
|
+
result = "..." + result # Indicate truncation
|
440
|
+
|
441
|
+
return result
|
442
|
+
|
443
|
+
def from_bit_string(self, bit_string: str) -> None:
|
444
|
+
"""Load from binary string representation."""
|
445
|
+
self.clear()
|
446
|
+
|
447
|
+
# Remove any truncation indicator
|
448
|
+
if bit_string.startswith("..."):
|
449
|
+
bit_string = bit_string[3:]
|
450
|
+
|
451
|
+
for i, bit_char in enumerate(reversed(bit_string)):
|
452
|
+
if bit_char == '1':
|
453
|
+
self._set_bit(i, True)
|
454
|
+
|
455
|
+
def get_statistics(self) -> Dict[str, Any]:
|
456
|
+
"""Get comprehensive bitset statistics."""
|
457
|
+
chunks_used = (self._highest_bit // 64) + 1 if self._highest_bit >= 0 else 0
|
458
|
+
|
459
|
+
return {
|
460
|
+
'size': self._size,
|
461
|
+
'capacity': self._capacity,
|
462
|
+
'highest_bit': self._highest_bit,
|
463
|
+
'chunks_allocated': len(self._bits),
|
464
|
+
'chunks_used': chunks_used,
|
465
|
+
'utilization': self._size / max(1, self._capacity) * 100,
|
466
|
+
'density': self._size / max(1, self._highest_bit + 1) * 100 if self._highest_bit >= 0 else 0,
|
467
|
+
'total_operations': self._total_operations,
|
468
|
+
'resize_count': self._resize_count,
|
469
|
+
'trim_count': self._trim_count,
|
470
|
+
'bit_flips': self._bit_flips,
|
471
|
+
'memory_efficiency': chunks_used / max(1, len(self._bits)) * 100
|
472
|
+
}
|
473
|
+
|
474
|
+
# ============================================================================
|
475
|
+
# PERFORMANCE CHARACTERISTICS
|
476
|
+
# ============================================================================
|
477
|
+
|
478
|
+
@property
|
479
|
+
def backend_info(self) -> Dict[str, Any]:
|
480
|
+
"""Get backend implementation info."""
|
481
|
+
return {
|
482
|
+
'strategy': 'BITSET_DYNAMIC',
|
483
|
+
'backend': 'Dynamic bitset with 64-bit chunks',
|
484
|
+
'initial_capacity': self.initial_capacity,
|
485
|
+
'growth_factor': self.growth_factor,
|
486
|
+
'auto_trim': self.auto_trim,
|
487
|
+
'complexity': {
|
488
|
+
'set_bit': 'O(1) amortized',
|
489
|
+
'get_bit': 'O(1)',
|
490
|
+
'clear_bit': 'O(1)',
|
491
|
+
'flip_bit': 'O(1)',
|
492
|
+
'find_next': 'O(n)', # n = bit range
|
493
|
+
'logical_ops': 'O(max(m,n))', # m,n = highest bits
|
494
|
+
'space': 'O(capacity/64)',
|
495
|
+
'resize': 'O(old_capacity) when triggered'
|
496
|
+
}
|
497
|
+
}
|
498
|
+
|
499
|
+
@property
|
500
|
+
def metrics(self) -> Dict[str, Any]:
|
501
|
+
"""Get performance metrics."""
|
502
|
+
stats = self.get_statistics()
|
503
|
+
|
504
|
+
return {
|
505
|
+
'size': stats['size'],
|
506
|
+
'capacity': stats['capacity'],
|
507
|
+
'highest_bit': stats['highest_bit'],
|
508
|
+
'utilization': f"{stats['utilization']:.1f}%",
|
509
|
+
'density': f"{stats['density']:.1f}%",
|
510
|
+
'total_operations': stats['total_operations'],
|
511
|
+
'bit_flips': stats['bit_flips'],
|
512
|
+
'memory_usage': f"{len(self._bits) * 8} bytes"
|
513
|
+
}
|