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,420 @@
|
|
1
|
+
"""
|
2
|
+
Bitmap Node Strategy Implementation
|
3
|
+
|
4
|
+
This module implements the BITMAP strategy for efficient bit manipulation
|
5
|
+
and boolean operations with compressed storage.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Iterator, List, Dict, Optional, Union
|
9
|
+
import array
|
10
|
+
from .base import ANodeMatrixStrategy
|
11
|
+
from ...types import NodeMode, NodeTrait
|
12
|
+
|
13
|
+
|
14
|
+
class BitmapStrategy(ANodeMatrixStrategy):
|
15
|
+
"""
|
16
|
+
Bitmap node strategy for efficient bit manipulation and boolean operations.
|
17
|
+
|
18
|
+
Provides space-efficient storage for boolean flags and supports
|
19
|
+
fast bitwise operations with compressed representation.
|
20
|
+
"""
|
21
|
+
|
22
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
23
|
+
"""Initialize the Bitmap strategy."""
|
24
|
+
super().__init__(NodeMode.BITMAP, traits, **options)
|
25
|
+
|
26
|
+
self.initial_size = options.get('initial_size', 1024)
|
27
|
+
self.auto_resize = options.get('auto_resize', True)
|
28
|
+
self.compression_threshold = options.get('compression_threshold', 0.1) # 10% fill rate
|
29
|
+
|
30
|
+
# Core storage using Python array for efficiency
|
31
|
+
self._bits = array.array('B') # Byte array for bit storage
|
32
|
+
self._capacity_bits = 0
|
33
|
+
self._size = 0 # Number of set bits
|
34
|
+
self._max_index = -1 # Highest index with a bit set
|
35
|
+
|
36
|
+
# Key-value mapping for compatibility
|
37
|
+
self._key_to_index: Dict[str, int] = {}
|
38
|
+
self._index_to_key: Dict[int, str] = {}
|
39
|
+
self._values: Dict[str, Any] = {} # Associated values
|
40
|
+
self._next_index = 0
|
41
|
+
|
42
|
+
# Initialize with initial capacity
|
43
|
+
self._resize_to(self.initial_size)
|
44
|
+
|
45
|
+
def get_supported_traits(self) -> NodeTrait:
|
46
|
+
"""Get the traits supported by the bitmap strategy."""
|
47
|
+
return (NodeTrait.COMPRESSED | NodeTrait.INDEXED | NodeTrait.STREAMING)
|
48
|
+
|
49
|
+
def _resize_to(self, new_bit_capacity: int) -> None:
|
50
|
+
"""Resize bitmap to new bit capacity."""
|
51
|
+
new_byte_capacity = (new_bit_capacity + 7) // 8 # Round up to nearest byte
|
52
|
+
|
53
|
+
if new_byte_capacity > len(self._bits):
|
54
|
+
# Expand
|
55
|
+
self._bits.extend([0] * (new_byte_capacity - len(self._bits)))
|
56
|
+
elif new_byte_capacity < len(self._bits):
|
57
|
+
# Shrink
|
58
|
+
self._bits = self._bits[:new_byte_capacity]
|
59
|
+
|
60
|
+
self._capacity_bits = new_bit_capacity
|
61
|
+
|
62
|
+
def _ensure_capacity(self, bit_index: int) -> None:
|
63
|
+
"""Ensure bitmap has capacity for the given bit index."""
|
64
|
+
if bit_index >= self._capacity_bits:
|
65
|
+
if self.auto_resize:
|
66
|
+
new_capacity = max(bit_index + 1, self._capacity_bits * 2)
|
67
|
+
self._resize_to(new_capacity)
|
68
|
+
else:
|
69
|
+
raise IndexError(f"Bit index {bit_index} exceeds capacity {self._capacity_bits}")
|
70
|
+
|
71
|
+
def _get_bit(self, bit_index: int) -> bool:
|
72
|
+
"""Get bit value at index."""
|
73
|
+
if bit_index >= self._capacity_bits or bit_index < 0:
|
74
|
+
return False
|
75
|
+
|
76
|
+
byte_index = bit_index // 8
|
77
|
+
bit_offset = bit_index % 8
|
78
|
+
|
79
|
+
if byte_index >= len(self._bits):
|
80
|
+
return False
|
81
|
+
|
82
|
+
return bool(self._bits[byte_index] & (1 << bit_offset))
|
83
|
+
|
84
|
+
def _set_bit(self, bit_index: int, value: bool) -> bool:
|
85
|
+
"""Set bit value at index. Returns True if bit was changed."""
|
86
|
+
self._ensure_capacity(bit_index)
|
87
|
+
|
88
|
+
byte_index = bit_index // 8
|
89
|
+
bit_offset = bit_index % 8
|
90
|
+
|
91
|
+
old_value = bool(self._bits[byte_index] & (1 << bit_offset))
|
92
|
+
|
93
|
+
if value:
|
94
|
+
self._bits[byte_index] |= (1 << bit_offset)
|
95
|
+
if not old_value:
|
96
|
+
self._size += 1
|
97
|
+
self._max_index = max(self._max_index, bit_index)
|
98
|
+
else:
|
99
|
+
self._bits[byte_index] &= ~(1 << bit_offset)
|
100
|
+
if old_value:
|
101
|
+
self._size -= 1
|
102
|
+
if bit_index == self._max_index:
|
103
|
+
self._update_max_index()
|
104
|
+
|
105
|
+
return old_value != value
|
106
|
+
|
107
|
+
def _update_max_index(self) -> None:
|
108
|
+
"""Update the maximum set bit index."""
|
109
|
+
self._max_index = -1
|
110
|
+
for i in range(self._capacity_bits - 1, -1, -1):
|
111
|
+
if self._get_bit(i):
|
112
|
+
self._max_index = i
|
113
|
+
break
|
114
|
+
|
115
|
+
# ============================================================================
|
116
|
+
# CORE OPERATIONS (Key-based interface for compatibility)
|
117
|
+
# ============================================================================
|
118
|
+
|
119
|
+
def put(self, key: Any, value: Any = None) -> None:
|
120
|
+
"""Set a bit and associate a value."""
|
121
|
+
key_str = str(key)
|
122
|
+
|
123
|
+
# Get or assign bit index for this key
|
124
|
+
if key_str in self._key_to_index:
|
125
|
+
bit_index = self._key_to_index[key_str]
|
126
|
+
else:
|
127
|
+
# Try to parse key as integer index
|
128
|
+
try:
|
129
|
+
bit_index = int(key_str)
|
130
|
+
if bit_index < 0:
|
131
|
+
bit_index = self._next_index
|
132
|
+
self._next_index += 1
|
133
|
+
except ValueError:
|
134
|
+
bit_index = self._next_index
|
135
|
+
self._next_index += 1
|
136
|
+
|
137
|
+
self._key_to_index[key_str] = bit_index
|
138
|
+
self._index_to_key[bit_index] = key_str
|
139
|
+
|
140
|
+
# Set the bit (treat None as False, everything else as True)
|
141
|
+
bit_value = value is not None and value is not False
|
142
|
+
self._set_bit(bit_index, bit_value)
|
143
|
+
|
144
|
+
# Store associated value
|
145
|
+
if bit_value:
|
146
|
+
self._values[key_str] = value if value is not None else True
|
147
|
+
else:
|
148
|
+
self._values.pop(key_str, None)
|
149
|
+
|
150
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
151
|
+
"""Get value associated with key."""
|
152
|
+
key_str = str(key)
|
153
|
+
|
154
|
+
if key_str in self._key_to_index:
|
155
|
+
bit_index = self._key_to_index[key_str]
|
156
|
+
if self._get_bit(bit_index):
|
157
|
+
return self._values.get(key_str, True)
|
158
|
+
|
159
|
+
return default
|
160
|
+
|
161
|
+
def has(self, key: Any) -> bool:
|
162
|
+
"""Check if bit is set for key."""
|
163
|
+
key_str = str(key)
|
164
|
+
|
165
|
+
if key_str in self._key_to_index:
|
166
|
+
bit_index = self._key_to_index[key_str]
|
167
|
+
return self._get_bit(bit_index)
|
168
|
+
|
169
|
+
return False
|
170
|
+
|
171
|
+
def remove(self, key: Any) -> bool:
|
172
|
+
"""Clear bit for key."""
|
173
|
+
key_str = str(key)
|
174
|
+
|
175
|
+
if key_str in self._key_to_index:
|
176
|
+
bit_index = self._key_to_index[key_str]
|
177
|
+
was_set = self._get_bit(bit_index)
|
178
|
+
|
179
|
+
if was_set:
|
180
|
+
self._set_bit(bit_index, False)
|
181
|
+
self._values.pop(key_str, None)
|
182
|
+
return True
|
183
|
+
|
184
|
+
return False
|
185
|
+
|
186
|
+
def delete(self, key: Any) -> bool:
|
187
|
+
"""Clear bit for key (alias for remove)."""
|
188
|
+
return self.remove(key)
|
189
|
+
|
190
|
+
def clear(self) -> None:
|
191
|
+
"""Clear all bits."""
|
192
|
+
self._bits = array.array('B', [0] * len(self._bits))
|
193
|
+
self._size = 0
|
194
|
+
self._max_index = -1
|
195
|
+
self._values.clear()
|
196
|
+
# Keep key mappings for consistency
|
197
|
+
|
198
|
+
def keys(self) -> Iterator[str]:
|
199
|
+
"""Get all keys with set bits."""
|
200
|
+
for key_str, bit_index in self._key_to_index.items():
|
201
|
+
if self._get_bit(bit_index):
|
202
|
+
yield key_str
|
203
|
+
|
204
|
+
def values(self) -> Iterator[Any]:
|
205
|
+
"""Get all values for set bits."""
|
206
|
+
return iter(self._values.values())
|
207
|
+
|
208
|
+
def items(self) -> Iterator[tuple[str, Any]]:
|
209
|
+
"""Get all key-value pairs for set bits."""
|
210
|
+
for key_str, bit_index in self._key_to_index.items():
|
211
|
+
if self._get_bit(bit_index):
|
212
|
+
yield (key_str, self._values.get(key_str, True))
|
213
|
+
|
214
|
+
def __len__(self) -> int:
|
215
|
+
"""Get the number of set bits."""
|
216
|
+
return self._size
|
217
|
+
|
218
|
+
def to_native(self) -> Dict[str, bool]:
|
219
|
+
"""Convert to native Python dict of boolean values."""
|
220
|
+
result = {}
|
221
|
+
for key_str, bit_index in self._key_to_index.items():
|
222
|
+
result[key_str] = self._get_bit(bit_index)
|
223
|
+
return result
|
224
|
+
|
225
|
+
@property
|
226
|
+
def is_list(self) -> bool:
|
227
|
+
"""This can behave like a list for indexed access."""
|
228
|
+
return True
|
229
|
+
|
230
|
+
@property
|
231
|
+
def is_dict(self) -> bool:
|
232
|
+
"""This can behave like a dict."""
|
233
|
+
return True
|
234
|
+
|
235
|
+
# ============================================================================
|
236
|
+
# BITMAP-SPECIFIC OPERATIONS
|
237
|
+
# ============================================================================
|
238
|
+
|
239
|
+
def set_bit(self, index: int, value: bool = True) -> bool:
|
240
|
+
"""Set bit at index. Returns previous value."""
|
241
|
+
old_value = self._get_bit(index)
|
242
|
+
self._set_bit(index, value)
|
243
|
+
return old_value
|
244
|
+
|
245
|
+
def get_bit(self, index: int) -> bool:
|
246
|
+
"""Get bit value at index."""
|
247
|
+
return self._get_bit(index)
|
248
|
+
|
249
|
+
def flip_bit(self, index: int) -> bool:
|
250
|
+
"""Flip bit at index. Returns new value."""
|
251
|
+
current_value = self._get_bit(index)
|
252
|
+
new_value = not current_value
|
253
|
+
self._set_bit(index, new_value)
|
254
|
+
return new_value
|
255
|
+
|
256
|
+
def count_set_bits(self, start: int = 0, end: Optional[int] = None) -> int:
|
257
|
+
"""Count set bits in range [start, end)."""
|
258
|
+
if end is None:
|
259
|
+
end = self._capacity_bits
|
260
|
+
|
261
|
+
count = 0
|
262
|
+
for i in range(start, min(end, self._capacity_bits)):
|
263
|
+
if self._get_bit(i):
|
264
|
+
count += 1
|
265
|
+
|
266
|
+
return count
|
267
|
+
|
268
|
+
def find_first_set(self, start: int = 0) -> Optional[int]:
|
269
|
+
"""Find first set bit starting from index."""
|
270
|
+
for i in range(start, self._capacity_bits):
|
271
|
+
if self._get_bit(i):
|
272
|
+
return i
|
273
|
+
return None
|
274
|
+
|
275
|
+
def find_first_clear(self, start: int = 0) -> Optional[int]:
|
276
|
+
"""Find first clear bit starting from index."""
|
277
|
+
for i in range(start, self._capacity_bits):
|
278
|
+
if not self._get_bit(i):
|
279
|
+
return i
|
280
|
+
return None
|
281
|
+
|
282
|
+
def bitwise_and(self, other: 'xBitmapStrategy') -> 'xBitmapStrategy':
|
283
|
+
"""Bitwise AND with another bitmap."""
|
284
|
+
result = xBitmapStrategy(
|
285
|
+
traits=self._traits,
|
286
|
+
initial_size=max(self._capacity_bits, other._capacity_bits)
|
287
|
+
)
|
288
|
+
|
289
|
+
max_bits = min(self._capacity_bits, other._capacity_bits)
|
290
|
+
for i in range(max_bits):
|
291
|
+
if self._get_bit(i) and other._get_bit(i):
|
292
|
+
result._set_bit(i, True)
|
293
|
+
|
294
|
+
return result
|
295
|
+
|
296
|
+
def bitwise_or(self, other: 'xBitmapStrategy') -> 'xBitmapStrategy':
|
297
|
+
"""Bitwise OR with another bitmap."""
|
298
|
+
result = xBitmapStrategy(
|
299
|
+
traits=self._traits,
|
300
|
+
initial_size=max(self._capacity_bits, other._capacity_bits)
|
301
|
+
)
|
302
|
+
|
303
|
+
max_bits = max(self._capacity_bits, other._capacity_bits)
|
304
|
+
for i in range(max_bits):
|
305
|
+
if self._get_bit(i) or other._get_bit(i):
|
306
|
+
result._set_bit(i, True)
|
307
|
+
|
308
|
+
return result
|
309
|
+
|
310
|
+
def bitwise_xor(self, other: 'xBitmapStrategy') -> 'xBitmapStrategy':
|
311
|
+
"""Bitwise XOR with another bitmap."""
|
312
|
+
result = xBitmapStrategy(
|
313
|
+
traits=self._traits,
|
314
|
+
initial_size=max(self._capacity_bits, other._capacity_bits)
|
315
|
+
)
|
316
|
+
|
317
|
+
max_bits = max(self._capacity_bits, other._capacity_bits)
|
318
|
+
for i in range(max_bits):
|
319
|
+
if self._get_bit(i) != other._get_bit(i):
|
320
|
+
result._set_bit(i, True)
|
321
|
+
|
322
|
+
return result
|
323
|
+
|
324
|
+
def bitwise_not(self) -> 'xBitmapStrategy':
|
325
|
+
"""Bitwise NOT (invert all bits)."""
|
326
|
+
result = xBitmapStrategy(
|
327
|
+
traits=self._traits,
|
328
|
+
initial_size=self._capacity_bits
|
329
|
+
)
|
330
|
+
|
331
|
+
for i in range(self._capacity_bits):
|
332
|
+
if not self._get_bit(i):
|
333
|
+
result._set_bit(i, True)
|
334
|
+
|
335
|
+
return result
|
336
|
+
|
337
|
+
def compress(self) -> None:
|
338
|
+
"""Compress bitmap by removing trailing zero bytes."""
|
339
|
+
if self._max_index < 0:
|
340
|
+
# No bits set, minimize to 1 byte
|
341
|
+
self._resize_to(8)
|
342
|
+
return
|
343
|
+
|
344
|
+
# Resize to just fit the highest set bit
|
345
|
+
new_capacity = ((self._max_index + 8) // 8) * 8
|
346
|
+
self._resize_to(new_capacity)
|
347
|
+
|
348
|
+
def get_compression_ratio(self) -> float:
|
349
|
+
"""Get compression ratio (set bits / total capacity)."""
|
350
|
+
if self._capacity_bits == 0:
|
351
|
+
return 0.0
|
352
|
+
return self._size / self._capacity_bits
|
353
|
+
|
354
|
+
def to_bytes(self) -> bytes:
|
355
|
+
"""Export bitmap as bytes."""
|
356
|
+
return self._bits.tobytes()
|
357
|
+
|
358
|
+
def from_bytes(self, data: bytes) -> None:
|
359
|
+
"""Import bitmap from bytes."""
|
360
|
+
self._bits = array.array('B', data)
|
361
|
+
self._capacity_bits = len(self._bits) * 8
|
362
|
+
|
363
|
+
# Recalculate size and max_index
|
364
|
+
self._size = 0
|
365
|
+
self._max_index = -1
|
366
|
+
|
367
|
+
for i in range(self._capacity_bits):
|
368
|
+
if self._get_bit(i):
|
369
|
+
self._size += 1
|
370
|
+
self._max_index = i
|
371
|
+
|
372
|
+
def get_bit_pattern(self, start: int = 0, length: int = 64) -> str:
|
373
|
+
"""Get bit pattern as string for debugging."""
|
374
|
+
pattern = ""
|
375
|
+
end = min(start + length, self._capacity_bits)
|
376
|
+
|
377
|
+
for i in range(start, end):
|
378
|
+
pattern += "1" if self._get_bit(i) else "0"
|
379
|
+
if (i - start + 1) % 8 == 0 and i < end - 1:
|
380
|
+
pattern += " " # Byte separator
|
381
|
+
|
382
|
+
return pattern
|
383
|
+
|
384
|
+
# ============================================================================
|
385
|
+
# PERFORMANCE CHARACTERISTICS
|
386
|
+
# ============================================================================
|
387
|
+
|
388
|
+
@property
|
389
|
+
def backend_info(self) -> Dict[str, Any]:
|
390
|
+
"""Get backend implementation info."""
|
391
|
+
return {
|
392
|
+
'strategy': 'BITMAP',
|
393
|
+
'backend': 'Python array (byte-based)',
|
394
|
+
'capacity_bits': self._capacity_bits,
|
395
|
+
'capacity_bytes': len(self._bits),
|
396
|
+
'auto_resize': self.auto_resize,
|
397
|
+
'complexity': {
|
398
|
+
'set_bit': 'O(1)',
|
399
|
+
'get_bit': 'O(1)',
|
400
|
+
'count_bits': 'O(n)',
|
401
|
+
'bitwise_ops': 'O(n)',
|
402
|
+
'space': 'O(n/8) bytes'
|
403
|
+
}
|
404
|
+
}
|
405
|
+
|
406
|
+
@property
|
407
|
+
def metrics(self) -> Dict[str, Any]:
|
408
|
+
"""Get performance metrics."""
|
409
|
+
compression_ratio = self.get_compression_ratio()
|
410
|
+
memory_efficiency = (self._size * 8) / max(1, len(self._bits)) * 100
|
411
|
+
|
412
|
+
return {
|
413
|
+
'set_bits': self._size,
|
414
|
+
'total_capacity_bits': self._capacity_bits,
|
415
|
+
'capacity_bytes': len(self._bits),
|
416
|
+
'max_set_index': self._max_index,
|
417
|
+
'compression_ratio': f"{compression_ratio:.3f}",
|
418
|
+
'memory_efficiency': f"{memory_efficiency:.1f}%",
|
419
|
+
'memory_usage': f"{len(self._bits)} bytes (bits) + {len(self._values) * 24} bytes (values)"
|
420
|
+
}
|