flexfloat 0.1.3__py3-none-any.whl → 0.1.5__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,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 Int64BitArray(BitArrayCommonMixin):
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, bits: list[bool] | None = None):
20
- """Initialize a BitArray.
29
+ def __init__(self, chunks: list[int] | None = None, length: int = 0):
30
+ """Initializes a ListInt64BitArray.
21
31
 
22
32
  Args:
23
- bits: Initial list of boolean values. Defaults to empty list.
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
- if bits is None:
26
- bits = []
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
- self._length = len(bits)
29
- # Pack bits into int64 chunks (64 bits per int)
30
- self._chunks: list[int] = []
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 << (63 - (j - i))
38
- self._chunks.append(chunk)
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) -> Int64BitArray:
42
- """Create a BitArray filled with zeros.
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
- Int64BitArray: A BitArray filled with False values.
80
+ ListInt64BitArray: A BitArray filled with False values.
48
81
  """
49
- instance = cls.__new__(cls)
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) -> Int64BitArray:
56
- """Create a BitArray filled with ones.
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
- Int64BitArray: A BitArray filled with True values.
92
+ ListInt64BitArray: A BitArray filled with True values.
62
93
  """
63
- instance = cls.__new__(cls)
64
- instance._length = length
65
- num_full_chunks = length // 64
66
- remaining_bits = length % 64
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
- # Add partial chunk if needed
75
- if remaining_bits > 0:
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
- return instance
103
+ Args:
104
+ index (int): The bit index (LSB-first).
81
105
 
82
- @staticmethod
83
- def parse_bitarray(bitstring: str) -> Int64BitArray:
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
- def _get_bit(self, index: int) -> bool:
89
- """Get a single bit at the specified index."""
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 = 63 - bit_index # Left-aligned
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
- """Set a single bit at the specified index."""
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
- def to_float(self) -> float:
114
- """Convert a 64-bit array to a floating-point number.
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
- AssertionError: If the bit array is not 64 bits long.
127
+ IndexError: If index is out of range.
120
128
  """
121
- assert self._length == 64, "Bit array must be 64 bits long."
122
-
123
- # Convert first chunk directly to bytes
124
- chunk = self._chunks[0]
125
- byte_values = bytearray()
126
- for i in range(8):
127
- byte = (chunk >> (56 - i * 8)) & 0xFF
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
- """Convert the bit array to an unsigned integer.
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 << (self._length - 1 - i)
146
+ result |= 1 << i
143
147
  return result
144
148
 
145
- def to_signed_int(self) -> int:
146
- """Convert a bit array into a signed integer using off-set binary
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
- int: The signed integer represented by the bit array.
153
+ float: The floating-point number represented by the bit array.
154
+
151
155
  Raises:
152
- AssertionError: If the bit array is empty.
156
+ AssertionError: If the bit array is not 64 bits long.
153
157
  """
154
- assert self._length > 0, "Bit array must not be empty."
155
-
156
- int_value = self.to_int()
157
- # Half of the maximum value
158
- bias = 1 << (self._length - 1)
159
- # If the sign bit is set, subtract the bias
160
- return int_value - bias
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
- This function shifts the bits in the array, filling in new bits with the
166
- specified fill value.
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
- Int64BitArray: A new BitArray with the bits shifted and filled.
171
+ ListInt64BitArray: A new BitArray with the same bits.
177
172
  """
178
- if shift_amount == 0:
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
- return Int64BitArray(new_bits)
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
- Int64BitArray: A new BitArray with the same bits.
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) -> Int64BitArray: ...
186
+ def __getitem__(self, index: slice) -> "ListInt64BitArray": ...
212
187
 
213
- def __getitem__(self, index: int | slice) -> bool | Int64BitArray:
214
- """Get an item or slice from the bit array."""
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 Int64BitArray(bits)
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
- """Set an item or slice in the bit array."""
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
- """Iterate over the bits in the array."""
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]) -> Int64BitArray:
259
- """Concatenate two bit arrays."""
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 Int64BitArray(list(self) + list(other))
262
- return Int64BitArray(list(self) + other)
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]) -> Int64BitArray:
265
- """Reverse concatenation with a list."""
266
- return Int64BitArray(other + list(self))
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
- """Check equality with another BitArray or list."""
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
- """Return True if any bit is set."""
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
- """Return a string representation of the BitArray."""
284
- return f"Int64BitArray({list(self)})"
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
- """Return True if any bit is set to True."""
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
- """Return True if all bits are set to True."""
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 = ((1 << remaining_bits) - 1) << (64 - remaining_bits)
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
- This mixin provides default implementations for methods that can be expressed
16
- in terms of the core BitArray protocol methods (__iter__, __len__, etc.).
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) -> 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
+ 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) -> 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
- """
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
- # Convert to unsigned integer representation
57
- unsigned_value = value - half
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
- bits = [(unsigned_value >> i) & 1 == 1 for i in range(length - 1, -1, -1)]
60
- return cls(bits)
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
- """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
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
- def __eq__(self, other: object) -> bool:
68
- """Check equality with another BitArray or list."""
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
- """Return True if any bit is set."""
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
- """Return True if any bit is set to True."""
81
- return any(self) # type: ignore
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
- """Return True if all bits are set to True."""
85
- return all(self) # type: ignore
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
- """Count the number of bits set to the specified value."""
89
- return sum(1 for bit in self if bit == value) # type: ignore
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
- def reverse(self) -> Any:
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)