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
@@ -0,0 +1,305 @@
|
|
1
|
+
"""Infinite-size int-based BitArray implementation for the flexfloat package.
|
2
|
+
|
3
|
+
This implementation is suitable for extremely large bit arrays, leveraging Python's
|
4
|
+
arbitrary-precision integers. Bit order: LSB-first (least significant bit at index 0).
|
5
|
+
|
6
|
+
Example:
|
7
|
+
from flexfloat.bitarray import BigIntBitArray
|
8
|
+
ba = BigIntBitArray(0b1011, length=4)
|
9
|
+
print(list(ba))
|
10
|
+
# Output: [True, True, False, True]
|
11
|
+
"""
|
12
|
+
|
13
|
+
from __future__ import annotations
|
14
|
+
|
15
|
+
import struct
|
16
|
+
from typing import Iterator, overload
|
17
|
+
|
18
|
+
from .bitarray import BitArray
|
19
|
+
from .bitarray_mixins import BitArrayCommonMixin
|
20
|
+
|
21
|
+
|
22
|
+
class BigIntBitArray(BitArrayCommonMixin):
|
23
|
+
"""A memory-efficient bit array class using Python's infinite-size int.
|
24
|
+
|
25
|
+
This implementation stores all bits as a single Python integer, leveraging
|
26
|
+
Python's arbitrary precision arithmetic for potentially unlimited size.
|
27
|
+
Since Python integers are arbitrary precision, this can handle bit arrays
|
28
|
+
of any size limited only by available memory.
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(self, value: int = 0, length: int = 0):
|
32
|
+
"""Initializes a BigIntBitArray.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
value (int, optional): Initial integer value representing the bits. Defaults
|
36
|
+
to 0.
|
37
|
+
length (int, optional): The number of bits in the array. Defaults to 0.
|
38
|
+
|
39
|
+
Raises:
|
40
|
+
ValueError: If length is negative.
|
41
|
+
"""
|
42
|
+
super().__init__()
|
43
|
+
if length < 0:
|
44
|
+
raise ValueError("Length must be non-negative")
|
45
|
+
self._length: int = length
|
46
|
+
self._value: int = value
|
47
|
+
|
48
|
+
@classmethod
|
49
|
+
def from_bits(cls, bits: list[bool] | None = None) -> "BigIntBitArray":
|
50
|
+
"""Creates a BitArray from a list of boolean values.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
bits (list[bool] | None, optional): List of boolean values. Defaults to
|
54
|
+
None, which creates an empty BitArray.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
BigIntBitArray: A BitArray created from the bits.
|
58
|
+
"""
|
59
|
+
if bits is None:
|
60
|
+
return cls()
|
61
|
+
value = 0
|
62
|
+
|
63
|
+
# Pack bits into a single integer (LSB-first)
|
64
|
+
# Least significant bit is at index 0
|
65
|
+
for i, bit in enumerate(bits):
|
66
|
+
if bit:
|
67
|
+
value |= 1 << i
|
68
|
+
|
69
|
+
return cls(value, len(bits))
|
70
|
+
|
71
|
+
@classmethod
|
72
|
+
def zeros(cls, length: int) -> "BigIntBitArray":
|
73
|
+
"""Creates a BitArray filled with zeros.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
length (int): The length of the bit array.
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
BigIntBitArray: A BitArray filled with False values.
|
80
|
+
"""
|
81
|
+
return cls(0, length)
|
82
|
+
|
83
|
+
@classmethod
|
84
|
+
def ones(cls, length: int) -> "BigIntBitArray":
|
85
|
+
"""Creates a BitArray filled with ones.
|
86
|
+
|
87
|
+
Args:
|
88
|
+
length (int): The length of the bit array.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
BigIntBitArray: A BitArray filled with True values.
|
92
|
+
"""
|
93
|
+
return cls((1 << length) - 1 if length > 0 else 0, length)
|
94
|
+
|
95
|
+
def _get_bit(self, index: int) -> bool:
|
96
|
+
"""Gets a single bit at the specified index (LSB-first).
|
97
|
+
|
98
|
+
Args:
|
99
|
+
index (int): The bit index (LSB-first).
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
bool: The value of the bit at the specified index.
|
103
|
+
|
104
|
+
Raises:
|
105
|
+
IndexError: If index is out of range.
|
106
|
+
"""
|
107
|
+
if index < 0 or index >= self._length:
|
108
|
+
raise IndexError("Bit index out of range")
|
109
|
+
bit_position = index # LSB-first
|
110
|
+
return bool(self._value & (1 << bit_position))
|
111
|
+
|
112
|
+
def _set_bit(self, index: int, value: bool) -> None:
|
113
|
+
"""Sets a single bit at the specified index (LSB-first).
|
114
|
+
|
115
|
+
Args:
|
116
|
+
index (int): The bit index (LSB-first).
|
117
|
+
value (bool): The value to set.
|
118
|
+
|
119
|
+
Raises:
|
120
|
+
IndexError: If index is out of range.
|
121
|
+
"""
|
122
|
+
if index < 0 or index >= self._length:
|
123
|
+
raise IndexError("Bit index out of range")
|
124
|
+
bit_position = index # LSB-first
|
125
|
+
|
126
|
+
if value:
|
127
|
+
self._value |= 1 << bit_position
|
128
|
+
else:
|
129
|
+
self._value &= ~(1 << bit_position)
|
130
|
+
|
131
|
+
def to_float(self) -> float:
|
132
|
+
"""Converts a 64-bit array to a floating-point number (LSB-first).
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
float: The floating-point number represented by the bit array.
|
136
|
+
|
137
|
+
Raises:
|
138
|
+
AssertionError: If the bit array is not 64 bits long.
|
139
|
+
"""
|
140
|
+
assert self._length == 64, "Bit array must be 64 bits long."
|
141
|
+
|
142
|
+
# Convert integer to bytes (big-endian)
|
143
|
+
byte_values = bytearray()
|
144
|
+
value = self._value
|
145
|
+
for i in range(8):
|
146
|
+
byte = (value >> (i * 8)) & 0xFF # LSB-first
|
147
|
+
byte_values.append(byte)
|
148
|
+
|
149
|
+
# Unpack as double precision (64 bits)
|
150
|
+
float_value = struct.unpack("<d", bytes(byte_values))[0]
|
151
|
+
return float_value # type: ignore
|
152
|
+
|
153
|
+
def to_int(self) -> int:
|
154
|
+
"""Converts the bit array to an unsigned integer (LSB-first).
|
155
|
+
|
156
|
+
Returns:
|
157
|
+
int: The integer represented by the bit array.
|
158
|
+
"""
|
159
|
+
return self._value
|
160
|
+
|
161
|
+
def copy(self) -> "BigIntBitArray":
|
162
|
+
"""Creates a copy of the bit array.
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
BigIntBitArray: A new BitArray with the same bits.
|
166
|
+
"""
|
167
|
+
return BigIntBitArray(self._value, self._length)
|
168
|
+
|
169
|
+
def __len__(self) -> int:
|
170
|
+
"""Returns the length of the bit array.
|
171
|
+
|
172
|
+
Returns:
|
173
|
+
int: The number of bits in the array.
|
174
|
+
"""
|
175
|
+
return self._length
|
176
|
+
|
177
|
+
@overload
|
178
|
+
def __getitem__(self, index: int) -> bool: ...
|
179
|
+
@overload
|
180
|
+
def __getitem__(self, index: slice) -> BigIntBitArray: ...
|
181
|
+
|
182
|
+
def __getitem__(self, index: int | slice) -> bool | BigIntBitArray:
|
183
|
+
"""Gets an item or slice from the bit array.
|
184
|
+
|
185
|
+
Args:
|
186
|
+
index (int or slice): The index or slice to retrieve.
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
bool or BigIntBitArray: The bit value or a new BitArray for the slice.
|
190
|
+
"""
|
191
|
+
if isinstance(index, int):
|
192
|
+
return self._get_bit(index)
|
193
|
+
start, stop, step = index.indices(self._length)
|
194
|
+
if step != 1:
|
195
|
+
# Handle step != 1 by extracting individual bits
|
196
|
+
bits: list[bool] = [self._get_bit(i) for i in range(start, stop, step)]
|
197
|
+
else:
|
198
|
+
# Efficient slice extraction for step == 1
|
199
|
+
bits = []
|
200
|
+
for i in range(start, stop):
|
201
|
+
bits.append(self._get_bit(i))
|
202
|
+
return BigIntBitArray.from_bits(bits)
|
203
|
+
|
204
|
+
@overload
|
205
|
+
def __setitem__(self, index: int, value: bool) -> None: ...
|
206
|
+
@overload
|
207
|
+
def __setitem__(self, index: slice, value: BitArray | list[bool]) -> None: ...
|
208
|
+
|
209
|
+
def __setitem__(
|
210
|
+
self, index: int | slice, value: bool | list[bool] | BitArray
|
211
|
+
) -> None:
|
212
|
+
"""Sets an item or slice in the bit array.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
index (int or slice): The index or slice to set.
|
216
|
+
value (bool or list[bool] or BitArray): The value(s) to assign.
|
217
|
+
|
218
|
+
Raises:
|
219
|
+
TypeError: If value type does not match index type.
|
220
|
+
ValueError: If value length does not match slice length.
|
221
|
+
"""
|
222
|
+
if isinstance(index, int):
|
223
|
+
if not isinstance(value, bool):
|
224
|
+
raise TypeError("Value must be bool for single index")
|
225
|
+
self._set_bit(index, value)
|
226
|
+
else: # slice
|
227
|
+
start, stop, step = index.indices(self._length)
|
228
|
+
|
229
|
+
# Convert value to list of bools
|
230
|
+
if isinstance(value, bool):
|
231
|
+
raise TypeError("Cannot assign bool to slice")
|
232
|
+
if not hasattr(value, "__iter__"):
|
233
|
+
raise TypeError("Value must be iterable for slice assignment")
|
234
|
+
value_list = list(value)
|
235
|
+
|
236
|
+
if step != 1:
|
237
|
+
# Handle step != 1
|
238
|
+
indices = list(range(start, stop, step))
|
239
|
+
if len(value_list) != len(indices):
|
240
|
+
raise ValueError("Value length doesn't match slice length")
|
241
|
+
for i, v in zip(indices, value_list):
|
242
|
+
self._set_bit(i, bool(v))
|
243
|
+
else:
|
244
|
+
# Handle step == 1
|
245
|
+
if len(value_list) != (stop - start):
|
246
|
+
raise ValueError("Value length doesn't match slice length")
|
247
|
+
for i, v in enumerate(value_list):
|
248
|
+
self._set_bit(start + i, bool(v))
|
249
|
+
|
250
|
+
def __iter__(self) -> Iterator[bool]:
|
251
|
+
"""Iterates over the bits in the array.
|
252
|
+
|
253
|
+
Yields:
|
254
|
+
bool: The next bit in the array.
|
255
|
+
"""
|
256
|
+
for i in range(self._length):
|
257
|
+
yield self._get_bit(i)
|
258
|
+
|
259
|
+
def __add__(self, other: BitArray | list[bool]) -> "BigIntBitArray":
|
260
|
+
"""Concatenates two bit arrays.
|
261
|
+
|
262
|
+
Args:
|
263
|
+
other (BitArray or list[bool]): The other bit array or list to concatenate.
|
264
|
+
|
265
|
+
Returns:
|
266
|
+
BigIntBitArray: The concatenated bit array.
|
267
|
+
|
268
|
+
Raises:
|
269
|
+
TypeError: If other is not iterable.
|
270
|
+
"""
|
271
|
+
if hasattr(other, "__iter__"):
|
272
|
+
other_bits = list(other)
|
273
|
+
else:
|
274
|
+
raise TypeError("Can only concatenate with iterable")
|
275
|
+
|
276
|
+
all_bits = list(self) + other_bits
|
277
|
+
return BigIntBitArray.from_bits(all_bits)
|
278
|
+
|
279
|
+
def __radd__(self, other: list[bool]) -> "BigIntBitArray":
|
280
|
+
"""Reverse concatenation with a list.
|
281
|
+
|
282
|
+
Args:
|
283
|
+
other (list[bool]): The list to concatenate before this bit array.
|
284
|
+
|
285
|
+
Returns:
|
286
|
+
BigIntBitArray: The concatenated bit array.
|
287
|
+
|
288
|
+
Raises:
|
289
|
+
TypeError: If other is not iterable.
|
290
|
+
"""
|
291
|
+
if hasattr(other, "__iter__"):
|
292
|
+
other_bits = list(other)
|
293
|
+
else:
|
294
|
+
raise TypeError("Can only concatenate with iterable")
|
295
|
+
|
296
|
+
all_bits = other_bits + list(self)
|
297
|
+
return BigIntBitArray.from_bits(all_bits)
|
298
|
+
|
299
|
+
def __repr__(self) -> str:
|
300
|
+
"""Returns a string representation of the BitArray.
|
301
|
+
|
302
|
+
Returns:
|
303
|
+
str: String representation of the BitArray.
|
304
|
+
"""
|
305
|
+
return f"BigIntBitArray({list(self)})"
|
@@ -0,0 +1,202 @@
|
|
1
|
+
"""List-based BitArray implementation for the flexfloat package.
|
2
|
+
|
3
|
+
This implementation is best for small or dynamically sized bit arrays.
|
4
|
+
Bit order: LSB-first (least significant bit at index 0, increasing to MSB).
|
5
|
+
|
6
|
+
Example:
|
7
|
+
from flexfloat.bitarray import ListBoolBitArray
|
8
|
+
ba = ListBoolBitArray([True, False, True])
|
9
|
+
print(list(ba))
|
10
|
+
# Output: [True, False, True]
|
11
|
+
"""
|
12
|
+
|
13
|
+
from __future__ import annotations
|
14
|
+
|
15
|
+
import struct
|
16
|
+
from typing import Iterator, overload
|
17
|
+
|
18
|
+
from .bitarray import BitArray
|
19
|
+
from .bitarray_mixins import BitArrayCommonMixin
|
20
|
+
|
21
|
+
|
22
|
+
class ListBoolBitArray(BitArrayCommonMixin):
|
23
|
+
"""A bit array class that encapsulates a list of booleans with utility methods.
|
24
|
+
|
25
|
+
This implementation uses a list of boolean values to represent the bits,
|
26
|
+
allowing for dynamic resizing and easy manipulation of individual bits.
|
27
|
+
"""
|
28
|
+
|
29
|
+
def __init__(self, bits: list[bool] | None = None):
|
30
|
+
"""Initializes a ListBoolBitArray.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
bits (list[bool] | None, optional): Initial list of boolean values. Defaults
|
34
|
+
to empty list.
|
35
|
+
"""
|
36
|
+
super().__init__()
|
37
|
+
if bits is not None:
|
38
|
+
self._bits = bits
|
39
|
+
else:
|
40
|
+
self._bits = []
|
41
|
+
|
42
|
+
@classmethod
|
43
|
+
def from_bits(cls, bits: list[bool] | None = None) -> "ListBoolBitArray":
|
44
|
+
"""Creates a BitArray from a list of boolean values.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
bits (list[bool] | None, optional): List of boolean values. Defaults to
|
48
|
+
None, which creates an empty BitArray.
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
ListBoolBitArray: A BitArray created from the bits.
|
52
|
+
"""
|
53
|
+
return cls(bits)
|
54
|
+
|
55
|
+
@classmethod
|
56
|
+
def zeros(cls, length: int) -> "ListBoolBitArray":
|
57
|
+
"""Creates a BitArray filled with zeros.
|
58
|
+
|
59
|
+
Args:
|
60
|
+
length (int): The length of the bit array.
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
ListBoolBitArray: A BitArray filled with False values.
|
64
|
+
"""
|
65
|
+
return cls([False] * length)
|
66
|
+
|
67
|
+
@classmethod
|
68
|
+
def ones(cls, length: int) -> "ListBoolBitArray":
|
69
|
+
"""Creates a BitArray filled with ones.
|
70
|
+
|
71
|
+
Args:
|
72
|
+
length (int): The length of the bit array.
|
73
|
+
|
74
|
+
Returns:
|
75
|
+
ListBoolBitArray: A BitArray filled with True values.
|
76
|
+
"""
|
77
|
+
return cls([True] * length)
|
78
|
+
|
79
|
+
def to_int(self) -> int:
|
80
|
+
"""Converts the bit array to an unsigned integer (LSB-first).
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
int: The integer represented by the bit array.
|
84
|
+
"""
|
85
|
+
return sum((1 << i) for i, bit in enumerate(self._bits) if bit)
|
86
|
+
|
87
|
+
def copy(self) -> "ListBoolBitArray":
|
88
|
+
"""Creates a copy of the bit array.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
ListBoolBitArray: A new BitArray with the same bits.
|
92
|
+
"""
|
93
|
+
return ListBoolBitArray(self._bits.copy())
|
94
|
+
|
95
|
+
def to_float(self) -> float:
|
96
|
+
"""Converts a 64-bit array to a floating-point number (LSB-first).
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
float: The floating-point number represented by the bit array.
|
100
|
+
|
101
|
+
Raises:
|
102
|
+
AssertionError: If the bit array is not 64 bits long.
|
103
|
+
"""
|
104
|
+
assert len(self._bits) == 64, "Bit array must be 64 bits long."
|
105
|
+
byte_values = bytearray()
|
106
|
+
for i in range(0, 64, 8):
|
107
|
+
byte = 0
|
108
|
+
for j in range(8):
|
109
|
+
if self._bits[i + j]:
|
110
|
+
byte |= 1 << j # LSB-first
|
111
|
+
byte_values.append(byte)
|
112
|
+
float_value = struct.unpack("<d", bytes(byte_values))[0]
|
113
|
+
return float_value # type: ignore
|
114
|
+
|
115
|
+
def __len__(self) -> int:
|
116
|
+
"""Returns the length of the bit array.
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
int: The number of bits in the array.
|
120
|
+
"""
|
121
|
+
return len(self._bits)
|
122
|
+
|
123
|
+
@overload
|
124
|
+
def __getitem__(self, index: int) -> bool: ...
|
125
|
+
@overload
|
126
|
+
def __getitem__(self, index: slice) -> ListBoolBitArray: ...
|
127
|
+
|
128
|
+
def __getitem__(self, index: int | slice) -> bool | ListBoolBitArray:
|
129
|
+
"""Get a bit or a slice of bits as a new ListBoolBitArray."""
|
130
|
+
if isinstance(index, slice):
|
131
|
+
return ListBoolBitArray.from_bits(self._bits[index])
|
132
|
+
return self._bits[index]
|
133
|
+
|
134
|
+
@overload
|
135
|
+
def __setitem__(self, index: int, value: bool) -> None: ...
|
136
|
+
@overload
|
137
|
+
def __setitem__(self, index: slice, value: BitArray | list[bool]) -> None: ...
|
138
|
+
|
139
|
+
def __setitem__(
|
140
|
+
self, index: int | slice, value: bool | list[bool] | BitArray
|
141
|
+
) -> None:
|
142
|
+
"""Sets an item or slice in the bit array.
|
143
|
+
|
144
|
+
Args:
|
145
|
+
index (int or slice): The index or slice to set.
|
146
|
+
value (bool or list[bool] or BitArray): The value(s) to assign.
|
147
|
+
|
148
|
+
Raises:
|
149
|
+
TypeError: If value type does not match index type.
|
150
|
+
"""
|
151
|
+
if isinstance(index, slice):
|
152
|
+
if isinstance(value, BitArray):
|
153
|
+
self._bits[index] = list(value)
|
154
|
+
elif isinstance(value, list):
|
155
|
+
self._bits[index] = value
|
156
|
+
else:
|
157
|
+
raise TypeError("Cannot assign a single bool to a slice")
|
158
|
+
return
|
159
|
+
if isinstance(value, bool):
|
160
|
+
self._bits[index] = value
|
161
|
+
else:
|
162
|
+
raise TypeError("Cannot assign a list or BitArray to a single index")
|
163
|
+
|
164
|
+
def __iter__(self) -> Iterator[bool]:
|
165
|
+
"""Iterates over the bits in the array.
|
166
|
+
|
167
|
+
Yields:
|
168
|
+
bool: The next bit in the array.
|
169
|
+
"""
|
170
|
+
return iter(self._bits)
|
171
|
+
|
172
|
+
def __add__(self, other: BitArray | list[bool]) -> "ListBoolBitArray":
|
173
|
+
"""Concatenates two bit arrays.
|
174
|
+
|
175
|
+
Args:
|
176
|
+
other (BitArray or list[bool]): The other bit array or list to concatenate.
|
177
|
+
|
178
|
+
Returns:
|
179
|
+
ListBoolBitArray: The concatenated bit array.
|
180
|
+
"""
|
181
|
+
if isinstance(other, BitArray):
|
182
|
+
return ListBoolBitArray(self._bits + list(other))
|
183
|
+
return ListBoolBitArray(self._bits + other)
|
184
|
+
|
185
|
+
def __radd__(self, other: list[bool]) -> "ListBoolBitArray":
|
186
|
+
"""Reverse concatenation with a list.
|
187
|
+
|
188
|
+
Args:
|
189
|
+
other (list[bool]): The list to concatenate before this bit array.
|
190
|
+
|
191
|
+
Returns:
|
192
|
+
ListBoolBitArray: The concatenated bit array.
|
193
|
+
"""
|
194
|
+
return ListBoolBitArray(other + self._bits)
|
195
|
+
|
196
|
+
def __repr__(self) -> str:
|
197
|
+
"""Returns a string representation of the BitArray.
|
198
|
+
|
199
|
+
Returns:
|
200
|
+
str: String representation of the BitArray.
|
201
|
+
"""
|
202
|
+
return f"ListBoolBitArray({self._bits})"
|