flexfloat 0.1.1__py3-none-any.whl → 0.1.3__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.
@@ -1,257 +1,187 @@
1
- """BitArray implementation for the flexfloat package."""
2
-
3
- from __future__ import annotations
4
-
5
- import struct
6
- from typing import Iterator, overload
7
-
8
-
9
- class BitArray:
10
- """A bit array class that encapsulates a list of booleans with utility methods.
11
-
12
- This class provides all the functionality previously available through utility
13
- functions, now encapsulated as methods for better object-oriented design.
14
- """
15
-
16
- def __init__(self, bits: list[bool] | None = None):
17
- """Initialize a BitArray.
18
-
19
- Args:
20
- bits: Initial list of boolean values. Defaults to empty list.
21
- """
22
- self._bits = bits if bits is not None else []
23
-
24
- @classmethod
25
- def from_float(cls, value: float) -> BitArray:
26
- """Convert a floating-point number to a bit array.
27
-
28
- Args:
29
- value (float): The floating-point number to convert.
30
- Returns:
31
- BitArray: A BitArray representing the bits of the floating-point number.
32
- """
33
- # Pack as double precision (64 bits)
34
- packed = struct.pack("!d", value)
35
- # Convert to boolean list
36
- bits = [bool((byte >> bit) & 1) for byte in packed for bit in range(7, -1, -1)]
37
- return cls(bits)
38
-
39
- @classmethod
40
- def from_signed_int(cls, value: int, length: int) -> BitArray:
41
- """Convert a signed integer to a bit array using off-set binary representation.
42
-
43
- Args:
44
- value (int): The signed integer to convert.
45
- length (int): The length of the resulting bit array.
46
- Returns:
47
- BitArray: A BitArray representing the bits of the signed integer.
48
- Raises:
49
- AssertionError: If the value is out of range for the specified length.
50
- """
51
- half = 1 << (length - 1)
52
- max_value = half - 1
53
- min_value = -half
54
-
55
- assert (
56
- min_value <= value <= max_value
57
- ), "Value out of range for specified length."
58
-
59
- # Convert to unsigned integer representation
60
- unsigned_value = value - half
61
-
62
- bits = [(unsigned_value >> i) & 1 == 1 for i in range(length - 1, -1, -1)]
63
- return cls(bits)
64
-
65
- @classmethod
66
- def zeros(cls, length: int) -> BitArray:
67
- """Create a BitArray filled with zeros.
68
-
69
- Args:
70
- length: The length of the bit array.
71
- Returns:
72
- BitArray: A BitArray filled with False values.
73
- """
74
- return cls([False] * length)
75
-
76
- @classmethod
77
- def ones(cls, length: int) -> BitArray:
78
- """Create a BitArray filled with ones.
79
-
80
- Args:
81
- length: The length of the bit array.
82
- Returns:
83
- BitArray: A BitArray filled with True values.
84
- """
85
- return cls([True] * length)
86
-
87
- @staticmethod
88
- def parse_bitarray(bitstring: str) -> BitArray:
89
- """Parse a string of bits (with optional spaces) into a BitArray instance."""
90
- bits = [c == "1" for c in bitstring if c in "01"]
91
- return BitArray(bits)
92
-
93
- def to_float(self) -> float:
94
- """Convert a 64-bit array to a floating-point number.
95
-
96
- Returns:
97
- float: The floating-point number represented by the bit array.
98
- Raises:
99
- AssertionError: If the bit array is not 64 bits long.
100
- """
101
- assert len(self._bits) == 64, "Bit array must be 64 bits long."
102
-
103
- byte_values = bytearray()
104
- for i in range(0, 64, 8):
105
- byte = 0
106
- for j in range(8):
107
- if self._bits[i + j]:
108
- byte |= 1 << (7 - j)
109
- byte_values.append(byte)
110
- # Unpack as double precision (64 bits)
111
- return struct.unpack("!d", bytes(byte_values))[0] # type: ignore
112
-
113
- def to_int(self) -> int:
114
- """Convert the bit array to an unsigned integer.
115
-
116
- Returns:
117
- int: The integer represented by the bit array.
118
- """
119
- return sum((1 << i) for i, bit in enumerate(reversed(self._bits)) if bit)
120
-
121
- def to_signed_int(self) -> int:
122
- """Convert a bit array into a signed integer using off-set binary
123
- representation.
124
-
125
- Returns:
126
- int: The signed integer represented by the bit array.
127
- Raises:
128
- AssertionError: If the bit array is empty.
129
- """
130
- assert len(self._bits) > 0, "Bit array must not be empty."
131
-
132
- int_value = self.to_int()
133
- # Half of the maximum value
134
- bias = 1 << (len(self._bits) - 1)
135
- # If the sign bit is set, subtract the bias
136
- return int_value - bias
137
-
138
- def shift(self, shift_amount: int, fill: bool = False) -> BitArray:
139
- """Shift the bit array left or right by a specified number of bits.
140
-
141
- This function shifts the bits in the array, filling in new bits with the
142
- specified fill value.
143
- If the value is positive, it shifts left; if negative, it shifts right.
144
- Fills the new bits with the specified fill value (default is False).
145
-
146
- Args:
147
- shift_amount (int): The number of bits to shift. Positive for left shift,
148
- negative for right shift.
149
- fill (bool): The value to fill in the new bits created by the shift.
150
- Defaults to False.
151
- Returns:
152
- BitArray: A new BitArray with the bits shifted and filled.
153
- """
154
- if shift_amount == 0:
155
- return self.copy()
156
- if abs(shift_amount) > len(self._bits):
157
- new_bits = [fill] * len(self._bits)
158
- elif shift_amount > 0:
159
- new_bits = [fill] * shift_amount + self._bits[:-shift_amount]
160
- else:
161
- new_bits = self._bits[-shift_amount:] + [fill] * (-shift_amount)
162
- return BitArray(new_bits)
163
-
164
- def copy(self) -> BitArray:
165
- """Create a copy of the bit array.
166
-
167
- Returns:
168
- BitArray: A new BitArray with the same bits.
169
- """
170
- return BitArray(self._bits.copy())
171
-
172
- def __len__(self) -> int:
173
- """Return the length of the bit array."""
174
- return len(self._bits)
175
-
176
- @overload
177
- def __getitem__(self, index: int) -> bool: ...
178
- @overload
179
- def __getitem__(self, index: slice) -> BitArray: ...
180
-
181
- def __getitem__(self, index: int | slice) -> bool | BitArray:
182
- """Get an item or slice from the bit array."""
183
- if isinstance(index, slice):
184
- return BitArray(self._bits[index])
185
- return self._bits[index]
186
-
187
- @overload
188
- def __setitem__(self, index: int, value: bool) -> None: ...
189
- @overload
190
- def __setitem__(self, index: slice, value: BitArray | list[bool]) -> None: ...
191
-
192
- def __setitem__(
193
- self, index: int | slice, value: bool | list[bool] | BitArray
194
- ) -> None:
195
- """Set an item or slice in the bit array."""
196
- if isinstance(index, slice):
197
- if isinstance(value, BitArray):
198
- self._bits[index] = value._bits
199
- elif isinstance(value, list):
200
- self._bits[index] = value
201
- else:
202
- raise TypeError("Cannot assign a single bool to a slice")
203
- return
204
- if isinstance(value, bool):
205
- self._bits[index] = value
206
- else:
207
- raise TypeError("Cannot assign a list or BitArray to a single index")
208
-
209
- def __iter__(self) -> Iterator[bool]:
210
- """Iterate over the bits in the array."""
211
- return iter(self._bits)
212
-
213
- def __add__(self, other: BitArray | list[bool]) -> BitArray:
214
- """Concatenate two bit arrays."""
215
- if isinstance(other, BitArray):
216
- return BitArray(self._bits + other._bits)
217
- return BitArray(self._bits + other)
218
-
219
- def __radd__(self, other: list[bool]) -> BitArray:
220
- """Reverse concatenation with a list."""
221
- return BitArray(other + self._bits)
222
-
223
- def __eq__(self, other: object) -> bool:
224
- """Check equality with another BitArray or list."""
225
- if isinstance(other, BitArray):
226
- return self._bits == other._bits
227
- if isinstance(other, list):
228
- return self._bits == other
229
- return False
230
-
231
- def __bool__(self) -> bool:
232
- """Return True if any bit is set."""
233
- return any(self._bits)
234
-
235
- def __repr__(self) -> str:
236
- """Return a string representation of the BitArray."""
237
- return f"BitArray({self._bits})"
238
-
239
- def __str__(self) -> str:
240
- """Return a string representation of the bits."""
241
- return "".join("1" if bit else "0" for bit in self._bits)
242
-
243
- def any(self) -> bool:
244
- """Return True if any bit is set to True."""
245
- return any(self._bits)
246
-
247
- def all(self) -> bool:
248
- """Return True if all bits are set to True."""
249
- return all(self._bits)
250
-
251
- def count(self, value: bool = True) -> int:
252
- """Count the number of bits set to the specified value."""
253
- return self._bits.count(value)
254
-
255
- def reverse(self) -> BitArray:
256
- """Return a new BitArray with the bits in reverse order."""
257
- return BitArray(self._bits[::-1])
1
+ """List-based BitArray implementation for the flexfloat package."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import struct
6
+ from typing import Iterator, overload
7
+
8
+ from .bitarray import BitArray
9
+ from .bitarray_mixins import BitArrayCommonMixin
10
+
11
+
12
+ class ListBitArray(BitArrayCommonMixin):
13
+ """A bit array class that encapsulates a list of booleans with utility methods.
14
+
15
+ This class provides all the functionality previously available through utility
16
+ functions, now encapsulated as methods for better object-oriented design.
17
+ """
18
+
19
+ def __init__(self, bits: list[bool] | None = None):
20
+ """Initialize a BitArray.
21
+
22
+ Args:
23
+ bits: Initial list of boolean values. Defaults to empty list.
24
+ """
25
+ self._bits = bits if bits is not None else []
26
+
27
+ @classmethod
28
+ def zeros(cls, length: int) -> ListBitArray:
29
+ """Create a BitArray filled with zeros.
30
+
31
+ Args:
32
+ length: The length of the bit array.
33
+ Returns:
34
+ ListBitArray: A BitArray filled with False values.
35
+ """
36
+ return cls([False] * length)
37
+
38
+ @classmethod
39
+ def ones(cls, length: int) -> ListBitArray:
40
+ """Create a BitArray filled with ones.
41
+
42
+ Args:
43
+ length: The length of the bit array.
44
+ Returns:
45
+ ListBitArray: A BitArray filled with True values.
46
+ """
47
+ return cls([True] * length)
48
+
49
+ @staticmethod
50
+ def parse_bitarray(bitstring: str) -> ListBitArray:
51
+ """Parse a string of bits (with optional spaces) into a BitArray instance."""
52
+ bits = [c == "1" for c in bitstring if c in "01"]
53
+ return ListBitArray(bits)
54
+
55
+ def to_float(self) -> float:
56
+ """Convert a 64-bit array to a floating-point number.
57
+
58
+ Returns:
59
+ float: The floating-point number represented by the bit array.
60
+ Raises:
61
+ AssertionError: If the bit array is not 64 bits long.
62
+ """
63
+ assert len(self._bits) == 64, "Bit array must be 64 bits long."
64
+
65
+ byte_values = bytearray()
66
+ for i in range(0, 64, 8):
67
+ byte = 0
68
+ for j in range(8):
69
+ if self._bits[i + j]:
70
+ byte |= 1 << (7 - j)
71
+ byte_values.append(byte)
72
+ # Unpack as double precision (64 bits)
73
+ return struct.unpack("!d", bytes(byte_values))[0] # type: ignore
74
+
75
+ def to_int(self) -> int:
76
+ """Convert the bit array to an unsigned integer.
77
+
78
+ Returns:
79
+ int: The integer represented by the bit array.
80
+ """
81
+ return sum((1 << i) for i, bit in enumerate(reversed(self._bits)) if bit)
82
+
83
+ def to_signed_int(self) -> int:
84
+ """Convert a bit array into a signed integer using off-set binary
85
+ representation.
86
+
87
+ Returns:
88
+ int: The signed integer represented by the bit array.
89
+ Raises:
90
+ AssertionError: If the bit array is empty.
91
+ """
92
+ assert len(self._bits) > 0, "Bit array must not be empty."
93
+
94
+ int_value = self.to_int()
95
+ # Half of the maximum value
96
+ bias = 1 << (len(self._bits) - 1)
97
+ # If the sign bit is set, subtract the bias
98
+ return int_value - bias
99
+
100
+ def shift(self, shift_amount: int, fill: bool = False) -> ListBitArray:
101
+ """Shift the bit array left or right by a specified number of bits.
102
+
103
+ This function shifts the bits in the array, filling in new bits with the
104
+ specified fill value.
105
+ If the value is positive, it shifts left; if negative, it shifts right.
106
+ Fills the new bits with the specified fill value (default is False).
107
+
108
+ Args:
109
+ shift_amount (int): The number of bits to shift. Positive for left shift,
110
+ negative for right shift.
111
+ fill (bool): The value to fill in the new bits created by the shift.
112
+ Defaults to False.
113
+ Returns:
114
+ ListBitArray: A new BitArray with the bits shifted and filled.
115
+ """
116
+ if shift_amount == 0:
117
+ return self.copy()
118
+ if abs(shift_amount) > len(self._bits):
119
+ new_bits = [fill] * len(self._bits)
120
+ elif shift_amount > 0:
121
+ new_bits = [fill] * shift_amount + self._bits[:-shift_amount]
122
+ else:
123
+ new_bits = self._bits[-shift_amount:] + [fill] * (-shift_amount)
124
+ return ListBitArray(new_bits)
125
+
126
+ def copy(self) -> ListBitArray:
127
+ """Create a copy of the bit array.
128
+
129
+ Returns:
130
+ ListBitArray: A new BitArray with the same bits.
131
+ """
132
+ return ListBitArray(self._bits.copy())
133
+
134
+ def __len__(self) -> int:
135
+ """Return the length of the bit array."""
136
+ return len(self._bits)
137
+
138
+ @overload
139
+ def __getitem__(self, index: int) -> bool: ...
140
+ @overload
141
+ def __getitem__(self, index: slice) -> ListBitArray: ...
142
+
143
+ def __getitem__(self, index: int | slice) -> bool | ListBitArray:
144
+ """Get an item or slice from the bit array."""
145
+ if isinstance(index, slice):
146
+ return ListBitArray(self._bits[index])
147
+ return self._bits[index]
148
+
149
+ @overload
150
+ def __setitem__(self, index: int, value: bool) -> None: ...
151
+ @overload
152
+ def __setitem__(self, index: slice, value: BitArray | list[bool]) -> None: ...
153
+
154
+ def __setitem__(
155
+ self, index: int | slice, value: bool | list[bool] | BitArray
156
+ ) -> None:
157
+ """Set an item or slice in the bit array."""
158
+ if isinstance(index, slice):
159
+ if isinstance(value, BitArray):
160
+ self._bits[index] = list(value)
161
+ elif isinstance(value, list):
162
+ self._bits[index] = value
163
+ else:
164
+ raise TypeError("Cannot assign a single bool to a slice")
165
+ return
166
+ if isinstance(value, bool):
167
+ self._bits[index] = value
168
+ else:
169
+ raise TypeError("Cannot assign a list or BitArray to a single index")
170
+
171
+ def __iter__(self) -> Iterator[bool]:
172
+ """Iterate over the bits in the array."""
173
+ return iter(self._bits)
174
+
175
+ def __add__(self, other: BitArray | list[bool]) -> ListBitArray:
176
+ """Concatenate two bit arrays."""
177
+ if isinstance(other, BitArray):
178
+ return ListBitArray(self._bits + list(other))
179
+ return ListBitArray(self._bits + other)
180
+
181
+ def __radd__(self, other: list[bool]) -> ListBitArray:
182
+ """Reverse concatenation with a list."""
183
+ return ListBitArray(other + self._bits)
184
+
185
+ def __repr__(self) -> str:
186
+ """Return a string representation of the BitArray."""
187
+ return f"ListBitArray({self._bits})"
@@ -0,0 +1,93 @@
1
+ """Mixin classes providing common BitArray functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import struct
6
+ from typing import Any
7
+
8
+ from .bitarray import BitArray
9
+
10
+
11
+ class BitArrayCommonMixin(BitArray):
12
+ """Mixin providing common methods that can be implemented using the BitArray
13
+ protocol.
14
+
15
+ This mixin provides default implementations for methods that can be expressed
16
+ in terms of the core BitArray protocol methods (__iter__, __len__, etc.).
17
+
18
+ Classes using this mixin must implement the BitArray protocol.
19
+ """
20
+
21
+ @classmethod
22
+ def from_float(cls, value: float) -> Any:
23
+ """Convert a floating-point number to a bit array.
24
+
25
+ Args:
26
+ value (float): The floating-point number to convert.
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
+
36
+ @classmethod
37
+ def from_signed_int(cls, value: int, length: int) -> Any:
38
+ """Convert a signed integer to a bit array using off-set binary representation.
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
+ """
48
+ half = 1 << (length - 1)
49
+ max_value = half - 1
50
+ min_value = -half
51
+
52
+ assert (
53
+ min_value <= value <= max_value
54
+ ), "Value out of range for specified length."
55
+
56
+ # Convert to unsigned integer representation
57
+ unsigned_value = value - half
58
+
59
+ bits = [(unsigned_value >> i) & 1 == 1 for i in range(length - 1, -1, -1)]
60
+ return cls(bits)
61
+
62
+ def __str__(self) -> str:
63
+ """Return a string representation of the bits."""
64
+ # This assumes self implements __iter__ as per the BitArray protocol
65
+ return "".join("1" if bit else "0" for bit in self) # type: ignore
66
+
67
+ def __eq__(self, other: object) -> bool:
68
+ """Check equality with another BitArray or list."""
69
+ if hasattr(other, "__iter__") and hasattr(other, "__len__"):
70
+ if len(self) != len(other): # type: ignore
71
+ return False
72
+ return all(a == b for a, b in zip(self, other)) # type: ignore
73
+ return False
74
+
75
+ def __bool__(self) -> bool:
76
+ """Return True if any bit is set."""
77
+ return self.any()
78
+
79
+ def any(self) -> bool:
80
+ """Return True if any bit is set to True."""
81
+ return any(self) # type: ignore
82
+
83
+ def all(self) -> bool:
84
+ """Return True if all bits are set to True."""
85
+ return all(self) # type: ignore
86
+
87
+ def count(self, value: bool = True) -> int:
88
+ """Count the number of bits set to the specified value."""
89
+ return sum(1 for bit in self if bit == value) # type: ignore
90
+
91
+ def reverse(self) -> Any:
92
+ """Return a new BitArray with the bits in reverse order."""
93
+ return self.__class__(list(self)[::-1]) # type: ignore