flexfloat 0.1.3__py3-none-any.whl → 0.3.0__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.
- flexfloat/__init__.py +21 -17
- flexfloat/bitarray/__init__.py +21 -80
- flexfloat/bitarray/bitarray.py +154 -54
- flexfloat/bitarray/bitarray_bigint.py +305 -0
- flexfloat/bitarray/bitarray_bool.py +202 -0
- flexfloat/bitarray/bitarray_int64.py +174 -149
- flexfloat/bitarray/bitarray_mixins.py +110 -47
- flexfloat/core.py +215 -113
- flexfloat/types.py +7 -1
- {flexfloat-0.1.3.dist-info → flexfloat-0.3.0.dist-info}/METADATA +48 -25
- flexfloat-0.3.0.dist-info/RECORD +15 -0
- flexfloat/bitarray/bitarray_list.py +0 -187
- flexfloat-0.1.3.dist-info/RECORD +0 -14
- {flexfloat-0.1.3.dist-info → flexfloat-0.3.0.dist-info}/WHEEL +0 -0
- {flexfloat-0.1.3.dist-info → flexfloat-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {flexfloat-0.1.3.dist-info → flexfloat-0.3.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,14 @@
|
|
1
|
-
"""Memory-efficient int64-based BitArray implementation for the flexfloat package.
|
1
|
+
"""Memory-efficient int64-based BitArray implementation for the flexfloat package.
|
2
|
+
|
3
|
+
This implementation is ideal for large bit arrays, as it packs 64 bits per integer.
|
4
|
+
Bit order: LSB-first (least significant bit at index 0, increasing to MSB).
|
5
|
+
|
6
|
+
Example:
|
7
|
+
from flexfloat.bitarray import ListInt64BitArray
|
8
|
+
ba = ListInt64BitArray([0b10101010], length=8)
|
9
|
+
print(list(ba))
|
10
|
+
# Output: [False, True, False, True, False, True, False, True]
|
11
|
+
"""
|
2
12
|
|
3
13
|
from __future__ import annotations
|
4
14
|
|
@@ -9,129 +19,123 @@ from .bitarray import BitArray
|
|
9
19
|
from .bitarray_mixins import BitArrayCommonMixin
|
10
20
|
|
11
21
|
|
12
|
-
class
|
22
|
+
class ListInt64BitArray(BitArrayCommonMixin):
|
13
23
|
"""A memory-efficient bit array class using a list of int64 values.
|
14
24
|
|
15
25
|
This implementation packs 64 bits per integer, making it more memory efficient
|
16
26
|
for large bit arrays compared to the boolean list implementation.
|
17
27
|
"""
|
18
28
|
|
19
|
-
def __init__(self,
|
20
|
-
"""
|
29
|
+
def __init__(self, chunks: list[int] | None = None, length: int = 0):
|
30
|
+
"""Initializes a ListInt64BitArray.
|
21
31
|
|
22
32
|
Args:
|
23
|
-
|
33
|
+
chunks (list[int] | None, optional): Initial list of int64 chunks. Defaults
|
34
|
+
to empty list.
|
35
|
+
length (int, optional): The amount of bits in the array. Defaults to 0.
|
36
|
+
|
37
|
+
Raises:
|
38
|
+
ValueError: If length is negative.
|
24
39
|
"""
|
25
|
-
|
26
|
-
|
40
|
+
super().__init__()
|
41
|
+
chunks = chunks or []
|
42
|
+
if length < 0:
|
43
|
+
raise ValueError("Length must be non-negative")
|
44
|
+
self._length: int = length
|
45
|
+
self._chunks: list[int] = chunks
|
27
46
|
|
28
|
-
|
29
|
-
|
30
|
-
|
47
|
+
@classmethod
|
48
|
+
def from_bits(cls, bits: list[bool] | None = None) -> "ListInt64BitArray":
|
49
|
+
"""Creates a BitArray from a list of boolean values.
|
50
|
+
|
51
|
+
Args:
|
52
|
+
bits (list[bool] | None, optional): List of boolean values in LSB-first
|
53
|
+
order. Defaults to None, which creates an empty BitArray.
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
ListInt64BitArray: A BitArray created from the bits.
|
57
|
+
"""
|
58
|
+
if bits is None:
|
59
|
+
return cls()
|
60
|
+
chunks: list[int] = []
|
31
61
|
|
32
62
|
for i in range(0, len(bits), 64):
|
33
63
|
chunk = 0
|
34
64
|
chunk_end = min(i + 64, len(bits))
|
35
65
|
for j in range(i, chunk_end):
|
36
66
|
if bits[j]:
|
37
|
-
chunk |= 1 << (
|
38
|
-
|
67
|
+
chunk |= 1 << (j - i)
|
68
|
+
chunks.append(chunk)
|
69
|
+
|
70
|
+
return cls(chunks, len(bits))
|
39
71
|
|
40
72
|
@classmethod
|
41
|
-
def zeros(cls, length: int) ->
|
42
|
-
"""
|
73
|
+
def zeros(cls, length: int) -> "ListInt64BitArray":
|
74
|
+
"""Creates a BitArray filled with zeros.
|
43
75
|
|
44
76
|
Args:
|
45
|
-
length: The length of the bit array.
|
77
|
+
length (int): The length of the bit array.
|
78
|
+
|
46
79
|
Returns:
|
47
|
-
|
80
|
+
ListInt64BitArray: A BitArray filled with False values.
|
48
81
|
"""
|
49
|
-
|
50
|
-
instance._length = length
|
51
|
-
instance._chunks = [0] * ((length + 63) // 64)
|
52
|
-
return instance
|
82
|
+
return cls([0] * ((length + 63) // 64), length)
|
53
83
|
|
54
84
|
@classmethod
|
55
|
-
def ones(cls, length: int) ->
|
56
|
-
"""
|
85
|
+
def ones(cls, length: int) -> "ListInt64BitArray":
|
86
|
+
"""Creates a BitArray filled with ones.
|
57
87
|
|
58
88
|
Args:
|
59
|
-
length: The length of the bit array.
|
89
|
+
length (int): The length of the bit array.
|
90
|
+
|
60
91
|
Returns:
|
61
|
-
|
92
|
+
ListInt64BitArray: A BitArray filled with True values.
|
62
93
|
"""
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
instance._chunks = []
|
69
|
-
|
70
|
-
# Add full chunks of all 1s
|
71
|
-
for _ in range(num_full_chunks):
|
72
|
-
instance._chunks.append(0xFFFFFFFFFFFFFFFF) # All 64 bits set
|
94
|
+
chunks = [0xFFFFFFFFFFFFFFFF] * (length // 64)
|
95
|
+
if length % 64 > 0:
|
96
|
+
partial_chunk = (1 << (length % 64)) - 1
|
97
|
+
chunks.append(partial_chunk)
|
98
|
+
return cls(chunks, length)
|
73
99
|
|
74
|
-
|
75
|
-
|
76
|
-
partial_chunk = (1 << remaining_bits) - 1
|
77
|
-
partial_chunk <<= 64 - remaining_bits # Left-align the bits
|
78
|
-
instance._chunks.append(partial_chunk)
|
100
|
+
def _get_bit(self, index: int) -> bool:
|
101
|
+
"""Gets a single bit at the specified index (LSB-first).
|
79
102
|
|
80
|
-
|
103
|
+
Args:
|
104
|
+
index (int): The bit index (LSB-first).
|
81
105
|
|
82
|
-
|
83
|
-
|
84
|
-
"""Parse a string of bits (with optional spaces) into a BitArray instance."""
|
85
|
-
bits = [c == "1" for c in bitstring if c in "01"]
|
86
|
-
return Int64BitArray(bits)
|
106
|
+
Returns:
|
107
|
+
bool: The value of the bit at the specified index.
|
87
108
|
|
88
|
-
|
89
|
-
|
109
|
+
Raises:
|
110
|
+
IndexError: If index is out of range.
|
111
|
+
"""
|
90
112
|
if index < 0 or index >= self._length:
|
91
113
|
raise IndexError("Bit index out of range")
|
92
|
-
|
93
114
|
chunk_index = index // 64
|
94
115
|
bit_index = index % 64
|
95
|
-
bit_position =
|
96
|
-
|
116
|
+
bit_position = bit_index # LSB-first
|
97
117
|
return bool(self._chunks[chunk_index] & (1 << bit_position))
|
98
118
|
|
99
119
|
def _set_bit(self, index: int, value: bool) -> None:
|
100
|
-
"""
|
101
|
-
if index < 0 or index >= self._length:
|
102
|
-
raise IndexError("Bit index out of range")
|
103
|
-
|
104
|
-
chunk_index = index // 64
|
105
|
-
bit_index = index % 64
|
106
|
-
bit_position = 63 - bit_index # Left-aligned
|
107
|
-
|
108
|
-
if value:
|
109
|
-
self._chunks[chunk_index] |= 1 << bit_position
|
110
|
-
else:
|
111
|
-
self._chunks[chunk_index] &= ~(1 << bit_position)
|
120
|
+
"""Sets a single bit at the specified index (LSB-first).
|
112
121
|
|
113
|
-
|
114
|
-
|
122
|
+
Args:
|
123
|
+
index (int): The bit index (LSB-first).
|
124
|
+
value (bool): The value to set.
|
115
125
|
|
116
|
-
Returns:
|
117
|
-
float: The floating-point number represented by the bit array.
|
118
126
|
Raises:
|
119
|
-
|
127
|
+
IndexError: If index is out of range.
|
120
128
|
"""
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
byte_values.append(byte)
|
129
|
-
|
130
|
-
# Unpack as double precision (64 bits)
|
131
|
-
return struct.unpack("!d", bytes(byte_values))[0] # type: ignore
|
129
|
+
if index < 0 or index >= self._length:
|
130
|
+
raise IndexError("Bit index out of range")
|
131
|
+
chunk_index = index // 64
|
132
|
+
bit_index = index % 64
|
133
|
+
bit_position = bit_index # LSB-first
|
134
|
+
mask = 1 << bit_position
|
135
|
+
self._chunks[chunk_index] ^= (-value ^ self._chunks[chunk_index]) & mask
|
132
136
|
|
133
137
|
def to_int(self) -> int:
|
134
|
-
"""
|
138
|
+
"""Converts the bit array to an unsigned integer (LSB-first).
|
135
139
|
|
136
140
|
Returns:
|
137
141
|
int: The integer represented by the bit array.
|
@@ -139,83 +143,54 @@ class Int64BitArray(BitArrayCommonMixin):
|
|
139
143
|
result = 0
|
140
144
|
for i in range(self._length):
|
141
145
|
if self._get_bit(i):
|
142
|
-
result |= 1 <<
|
146
|
+
result |= 1 << i
|
143
147
|
return result
|
144
148
|
|
145
|
-
def
|
146
|
-
"""
|
147
|
-
representation.
|
149
|
+
def to_float(self) -> float:
|
150
|
+
"""Converts a 64-bit array to a floating-point number (LSB-first).
|
148
151
|
|
149
152
|
Returns:
|
150
|
-
|
153
|
+
float: The floating-point number represented by the bit array.
|
154
|
+
|
151
155
|
Raises:
|
152
|
-
AssertionError: If the bit array is
|
156
|
+
AssertionError: If the bit array is not 64 bits long.
|
153
157
|
"""
|
154
|
-
assert self._length
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
def shift(self, shift_amount: int, fill: bool = False) -> Int64BitArray:
|
163
|
-
"""Shift the bit array left or right by a specified number of bits.
|
158
|
+
assert self._length == 64, "Bit array must be 64 bits long."
|
159
|
+
chunk = self._chunks[0]
|
160
|
+
byte_values = bytearray()
|
161
|
+
for i in range(8):
|
162
|
+
byte = (chunk >> (i * 8)) & 0xFF # LSB-first
|
163
|
+
byte_values.append(byte)
|
164
|
+
float_value = struct.unpack("<d", bytes(byte_values))[0]
|
165
|
+
return float_value # type: ignore
|
164
166
|
|
165
|
-
|
166
|
-
|
167
|
-
If the value is positive, it shifts left; if negative, it shifts right.
|
168
|
-
Fills the new bits with the specified fill value (default is False).
|
167
|
+
def copy(self) -> "ListInt64BitArray":
|
168
|
+
"""Creates a copy of the bit array.
|
169
169
|
|
170
|
-
Args:
|
171
|
-
shift_amount (int): The number of bits to shift. Positive for left shift,
|
172
|
-
negative for right shift.
|
173
|
-
fill (bool): The value to fill in the new bits created by the shift.
|
174
|
-
Defaults to False.
|
175
170
|
Returns:
|
176
|
-
|
171
|
+
ListInt64BitArray: A new BitArray with the same bits.
|
177
172
|
"""
|
178
|
-
|
179
|
-
return self.copy()
|
180
|
-
|
181
|
-
# Convert to bit list for simplicity (can be optimized later)
|
182
|
-
bits = list(self)
|
183
|
-
|
184
|
-
if abs(shift_amount) > len(bits):
|
185
|
-
new_bits = [fill] * len(bits)
|
186
|
-
elif shift_amount > 0:
|
187
|
-
new_bits = [fill] * shift_amount + bits[:-shift_amount]
|
188
|
-
else:
|
189
|
-
new_bits = bits[-shift_amount:] + [fill] * (-shift_amount)
|
173
|
+
return ListInt64BitArray(self._chunks.copy(), self._length)
|
190
174
|
|
191
|
-
|
192
|
-
|
193
|
-
def copy(self) -> Int64BitArray:
|
194
|
-
"""Create a copy of the bit array.
|
175
|
+
def __len__(self) -> int:
|
176
|
+
"""Returns the length of the bit array.
|
195
177
|
|
196
178
|
Returns:
|
197
|
-
|
179
|
+
int: The number of bits in the array.
|
198
180
|
"""
|
199
|
-
instance = Int64BitArray.__new__(Int64BitArray)
|
200
|
-
instance._length = self._length
|
201
|
-
instance._chunks = self._chunks.copy() # This creates a shallow copy
|
202
|
-
return instance
|
203
|
-
|
204
|
-
def __len__(self) -> int:
|
205
|
-
"""Return the length of the bit array."""
|
206
181
|
return self._length
|
207
182
|
|
208
183
|
@overload
|
209
184
|
def __getitem__(self, index: int) -> bool: ...
|
210
185
|
@overload
|
211
|
-
def __getitem__(self, index: slice) ->
|
186
|
+
def __getitem__(self, index: slice) -> "ListInt64BitArray": ...
|
212
187
|
|
213
|
-
def __getitem__(self, index: int | slice) -> bool |
|
214
|
-
"""Get
|
188
|
+
def __getitem__(self, index: int | slice) -> bool | ListInt64BitArray:
|
189
|
+
"""Get a bit or a slice of bits as a new ListInt64BitArray."""
|
215
190
|
if isinstance(index, slice):
|
216
191
|
start, stop, step = index.indices(self._length)
|
217
192
|
bits = [self._get_bit(i) for i in range(start, stop, step)]
|
218
|
-
return
|
193
|
+
return ListInt64BitArray.from_bits(bits)
|
219
194
|
return self._get_bit(index)
|
220
195
|
|
221
196
|
@overload
|
@@ -226,7 +201,16 @@ class Int64BitArray(BitArrayCommonMixin):
|
|
226
201
|
def __setitem__(
|
227
202
|
self, index: int | slice, value: bool | list[bool] | BitArray
|
228
203
|
) -> None:
|
229
|
-
"""
|
204
|
+
"""Sets an item or slice in the bit array.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
index (int or slice): The index or slice to set.
|
208
|
+
value (bool or list[bool] or BitArray): The value(s) to assign.
|
209
|
+
|
210
|
+
Raises:
|
211
|
+
TypeError: If value type does not match index type.
|
212
|
+
ValueError: If value length does not match slice length.
|
213
|
+
"""
|
230
214
|
if isinstance(index, slice):
|
231
215
|
start, stop, step = index.indices(self._length)
|
232
216
|
indices = list(range(start, stop, step))
|
@@ -251,22 +235,47 @@ class Int64BitArray(BitArrayCommonMixin):
|
|
251
235
|
raise TypeError("Cannot assign a list or BitArray to a single index")
|
252
236
|
|
253
237
|
def __iter__(self) -> Iterator[bool]:
|
254
|
-
"""
|
238
|
+
"""Iterates over the bits in the array.
|
239
|
+
|
240
|
+
Yields:
|
241
|
+
bool: The next bit in the array.
|
242
|
+
"""
|
255
243
|
for i in range(self._length):
|
256
244
|
yield self._get_bit(i)
|
257
245
|
|
258
|
-
def __add__(self, other: BitArray | list[bool]) ->
|
259
|
-
"""
|
246
|
+
def __add__(self, other: BitArray | list[bool]) -> "ListInt64BitArray":
|
247
|
+
"""Concatenates two bit arrays.
|
248
|
+
|
249
|
+
Args:
|
250
|
+
other (BitArray or list[bool]): The other bit array or list to concatenate.
|
251
|
+
|
252
|
+
Returns:
|
253
|
+
ListInt64BitArray: The concatenated bit array.
|
254
|
+
"""
|
260
255
|
if isinstance(other, BitArray):
|
261
|
-
return
|
262
|
-
return
|
256
|
+
return ListInt64BitArray.from_bits(list(self) + list(other))
|
257
|
+
return ListInt64BitArray.from_bits(list(self) + other)
|
263
258
|
|
264
|
-
def __radd__(self, other: list[bool]) ->
|
265
|
-
"""Reverse concatenation with a list.
|
266
|
-
|
259
|
+
def __radd__(self, other: list[bool]) -> "ListInt64BitArray":
|
260
|
+
"""Reverse concatenation with a list.
|
261
|
+
|
262
|
+
Args:
|
263
|
+
other (list[bool]): The list to concatenate before this bit array.
|
264
|
+
|
265
|
+
Returns:
|
266
|
+
ListInt64BitArray: The concatenated bit array.
|
267
|
+
"""
|
268
|
+
return ListInt64BitArray.from_bits(other + list(self))
|
267
269
|
|
268
270
|
def __eq__(self, other: object) -> bool:
|
269
|
-
"""
|
271
|
+
"""Checks equality with another BitArray or list.
|
272
|
+
|
273
|
+
Args:
|
274
|
+
other (object): The object to compare with.
|
275
|
+
|
276
|
+
Returns:
|
277
|
+
bool: True if equal, False otherwise.
|
278
|
+
"""
|
270
279
|
if isinstance(other, BitArray):
|
271
280
|
if len(self) != len(other):
|
272
281
|
return False
|
@@ -276,19 +285,35 @@ class Int64BitArray(BitArrayCommonMixin):
|
|
276
285
|
return False
|
277
286
|
|
278
287
|
def __bool__(self) -> bool:
|
279
|
-
"""
|
288
|
+
"""Returns True if any bit is set.
|
289
|
+
|
290
|
+
Returns:
|
291
|
+
bool: True if any bit is set, False otherwise.
|
292
|
+
"""
|
280
293
|
return any(chunk != 0 for chunk in self._chunks)
|
281
294
|
|
282
295
|
def __repr__(self) -> str:
|
283
|
-
"""
|
284
|
-
|
296
|
+
"""Returns a string representation of the BitArray.
|
297
|
+
|
298
|
+
Returns:
|
299
|
+
str: String representation of the BitArray.
|
300
|
+
"""
|
301
|
+
return f"ListInt64BitArray({list(self)})"
|
285
302
|
|
286
303
|
def any(self) -> bool:
|
287
|
-
"""
|
304
|
+
"""Returns True if any bit is set to True.
|
305
|
+
|
306
|
+
Returns:
|
307
|
+
bool: True if any bit is set to True, False otherwise.
|
308
|
+
"""
|
288
309
|
return any(chunk != 0 for chunk in self._chunks)
|
289
310
|
|
290
311
|
def all(self) -> bool:
|
291
|
-
"""
|
312
|
+
"""Returns True if all bits are set to True.
|
313
|
+
|
314
|
+
Returns:
|
315
|
+
bool: True if all bits are set to True, False otherwise.
|
316
|
+
"""
|
292
317
|
if self._length == 0:
|
293
318
|
return True
|
294
319
|
|
@@ -301,7 +326,7 @@ class Int64BitArray(BitArrayCommonMixin):
|
|
301
326
|
# Check partial chunk if exists
|
302
327
|
remaining_bits = self._length % 64
|
303
328
|
if remaining_bits > 0:
|
304
|
-
expected_pattern = (
|
329
|
+
expected_pattern = (1 << remaining_bits) - 1
|
305
330
|
if self._chunks[-1] != expected_pattern:
|
306
331
|
return False
|
307
332
|
|
@@ -1,9 +1,20 @@
|
|
1
|
-
"""Mixin classes providing common BitArray functionality.
|
1
|
+
"""Mixin classes providing common BitArray functionality.
|
2
|
+
|
3
|
+
This module provides mixins for BitArray implementations, offering default methods that
|
4
|
+
rely on the BitArray protocol. Bit order is always LSB-first.
|
5
|
+
|
6
|
+
Example:
|
7
|
+
class MyBitArray(BitArrayCommonMixin):
|
8
|
+
... # implement BitArray protocol methods
|
9
|
+
ba = MyBitArray.from_bits([True, False])
|
10
|
+
print(list(ba))
|
11
|
+
# Output: [True, False]
|
12
|
+
"""
|
2
13
|
|
3
14
|
from __future__ import annotations
|
4
15
|
|
5
16
|
import struct
|
6
|
-
from typing import Any
|
17
|
+
from typing import Any, Iterable
|
7
18
|
|
8
19
|
from .bitarray import BitArray
|
9
20
|
|
@@ -12,39 +23,24 @@ class BitArrayCommonMixin(BitArray):
|
|
12
23
|
"""Mixin providing common methods that can be implemented using the BitArray
|
13
24
|
protocol.
|
14
25
|
|
15
|
-
|
16
|
-
|
26
|
+
Bit order: LSB-first (least significant bit at index 0, increasing to MSB).
|
27
|
+
|
28
|
+
This mixin provides default implementations for methods that can be expressed in
|
29
|
+
terms of the core BitArray protocol methods (__iter__, __len__, etc.).
|
17
30
|
|
18
31
|
Classes using this mixin must implement the BitArray protocol.
|
19
32
|
"""
|
20
33
|
|
21
34
|
@classmethod
|
22
|
-
def from_float(cls, value: float) ->
|
23
|
-
"""
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
Returns:
|
28
|
-
BitArray: A BitArray representing the bits of the floating-point number.
|
29
|
-
"""
|
30
|
-
# Pack as double precision (64 bits)
|
31
|
-
packed = struct.pack("!d", value)
|
32
|
-
# Convert to boolean list
|
33
|
-
bits = [bool((byte >> bit) & 1) for byte in packed for bit in range(7, -1, -1)]
|
34
|
-
return cls(bits)
|
35
|
+
def from_float(cls, value: float) -> BitArray:
|
36
|
+
"""Create a BitArray from a float value."""
|
37
|
+
packed = struct.pack("<d", value)
|
38
|
+
bits = [bool((byte >> bit) & 1) for byte in packed for bit in range(8)]
|
39
|
+
return cls.from_bits(bits)
|
35
40
|
|
36
41
|
@classmethod
|
37
|
-
def from_signed_int(cls, value: int, length: int) ->
|
38
|
-
"""
|
39
|
-
|
40
|
-
Args:
|
41
|
-
value (int): The signed integer to convert.
|
42
|
-
length (int): The length of the resulting bit array.
|
43
|
-
Returns:
|
44
|
-
BitArray: A BitArray representing the bits of the signed integer.
|
45
|
-
Raises:
|
46
|
-
AssertionError: If the value is out of range for the specified length.
|
47
|
-
"""
|
42
|
+
def from_signed_int(cls, value: int, length: int) -> BitArray:
|
43
|
+
"""Create a BitArray from a signed integer value."""
|
48
44
|
half = 1 << (length - 1)
|
49
45
|
max_value = half - 1
|
50
46
|
min_value = -half
|
@@ -53,19 +49,33 @@ class BitArrayCommonMixin(BitArray):
|
|
53
49
|
min_value <= value <= max_value
|
54
50
|
), "Value out of range for specified length."
|
55
51
|
|
56
|
-
|
57
|
-
|
52
|
+
unsigned_value = value + half
|
53
|
+
bits = [(unsigned_value >> i) & 1 == 1 for i in range(length)]
|
54
|
+
return cls.from_bits(bits)
|
58
55
|
|
59
|
-
|
60
|
-
|
56
|
+
@classmethod
|
57
|
+
def parse_bitarray(cls, bitstring: Iterable[str]) -> BitArray:
|
58
|
+
"""Parses a string of bits (with optional spaces) into a BitArray instance."""
|
59
|
+
return cls.from_bits([c == "1" for c in bitstring if c in "01"])
|
61
60
|
|
62
61
|
def __str__(self) -> str:
|
63
|
-
"""
|
64
|
-
|
65
|
-
|
62
|
+
"""Returns a string representation of the bits (LSB-first, index 0 is
|
63
|
+
rightmost).
|
64
|
+
|
65
|
+
Returns:
|
66
|
+
str: String representation of the bits.
|
67
|
+
"""
|
68
|
+
return "".join("1" if bit else "0" for bit in reversed(list(self)))
|
69
|
+
|
70
|
+
def __eq__(self, other: Any) -> bool:
|
71
|
+
"""Checks equality with another BitArray or list.
|
66
72
|
|
67
|
-
|
68
|
-
|
73
|
+
Args:
|
74
|
+
other (Any): The object to compare with.
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
bool: True if equal, False otherwise.
|
78
|
+
"""
|
69
79
|
if hasattr(other, "__iter__") and hasattr(other, "__len__"):
|
70
80
|
if len(self) != len(other): # type: ignore
|
71
81
|
return False
|
@@ -73,21 +83,74 @@ class BitArrayCommonMixin(BitArray):
|
|
73
83
|
return False
|
74
84
|
|
75
85
|
def __bool__(self) -> bool:
|
76
|
-
"""
|
86
|
+
"""Returns True if any bit is set.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
bool: True if any bit is set, False otherwise.
|
90
|
+
"""
|
77
91
|
return self.any()
|
78
92
|
|
79
93
|
def any(self) -> bool:
|
80
|
-
"""
|
81
|
-
|
94
|
+
"""Returns True if any bit is set to True.
|
95
|
+
|
96
|
+
Returns:
|
97
|
+
bool: True if any bit is set to True, False otherwise.
|
98
|
+
"""
|
99
|
+
return any(self)
|
82
100
|
|
83
101
|
def all(self) -> bool:
|
84
|
-
"""
|
85
|
-
|
102
|
+
"""Returns True if all bits are set to True.
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
bool: True if all bits are set to True, False otherwise.
|
106
|
+
"""
|
107
|
+
return all(self)
|
86
108
|
|
87
109
|
def count(self, value: bool = True) -> int:
|
88
|
-
"""
|
89
|
-
|
110
|
+
"""Counts the number of bits set to the specified value.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
value (bool, optional): The value to count. Defaults to True.
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
int: The number of bits set to the specified value.
|
117
|
+
"""
|
118
|
+
return sum(1 for bit in self if bit == value)
|
119
|
+
|
120
|
+
def reverse(self) -> BitArray:
|
121
|
+
"""Returns a new BitArray with the bits in reverse order."""
|
122
|
+
return self.from_bits(list(reversed(self)))
|
123
|
+
|
124
|
+
def to_signed_int(self) -> int:
|
125
|
+
"""Converts a bit array into a signed integer using off-set binary
|
126
|
+
representation.
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
int: The signed integer represented by the bit array.
|
130
|
+
|
131
|
+
Raises:
|
132
|
+
AssertionError: If the bit array is empty.
|
133
|
+
"""
|
134
|
+
assert len(self) > 0, "Bit array must not be empty."
|
135
|
+
|
136
|
+
int_value: int = self.to_int()
|
137
|
+
# Half of the maximum value
|
138
|
+
bias = 1 << (len(self) - 1)
|
139
|
+
# Subtract the bias to get the signed value
|
140
|
+
return int_value - bias
|
141
|
+
|
142
|
+
def shift(self, shift_amount: int, fill: bool = False) -> BitArray:
|
143
|
+
"""Shifts the bit array left or right by a specified number of bits."""
|
144
|
+
if shift_amount == 0:
|
145
|
+
return self.copy()
|
146
|
+
|
147
|
+
bits = list(self)
|
148
|
+
|
149
|
+
if abs(shift_amount) > len(bits):
|
150
|
+
new_bits = [fill] * len(bits)
|
151
|
+
elif shift_amount > 0: # Right shift
|
152
|
+
new_bits = bits[shift_amount:] + [fill] * shift_amount
|
153
|
+
else: # Left shift
|
154
|
+
new_bits = [fill] * (-shift_amount) + bits[:shift_amount]
|
90
155
|
|
91
|
-
|
92
|
-
"""Return a new BitArray with the bits in reverse order."""
|
93
|
-
return self.__class__(list(self)[::-1]) # type: ignore
|
156
|
+
return self.from_bits(new_bits)
|