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.
- flexfloat/__init__.py +32 -13
- flexfloat/bitarray/__init__.py +90 -0
- flexfloat/bitarray/bitarray.py +198 -0
- flexfloat/bitarray/bitarray_int64.py +308 -0
- flexfloat/{bitarray.py → bitarray/bitarray_list.py} +187 -257
- flexfloat/bitarray/bitarray_mixins.py +93 -0
- flexfloat/core.py +1014 -728
- flexfloat/types.py +5 -5
- flexfloat-0.1.3.dist-info/METADATA +340 -0
- flexfloat-0.1.3.dist-info/RECORD +14 -0
- flexfloat-0.1.3.dist-info/licenses/LICENSE +21 -0
- flexfloat-0.1.1.dist-info/METADATA +0 -82
- flexfloat-0.1.1.dist-info/RECORD +0 -9
- {flexfloat-0.1.1.dist-info → flexfloat-0.1.3.dist-info}/WHEEL +0 -0
- {flexfloat-0.1.1.dist-info → flexfloat-0.1.3.dist-info}/top_level.txt +0 -0
flexfloat/__init__.py
CHANGED
@@ -1,13 +1,32 @@
|
|
1
|
-
"""FlexFloat - A library for arbitrary precision floating point arithmetic.
|
2
|
-
|
3
|
-
This package provides the FlexFloat class for handling floating-point numbers
|
4
|
-
with growable exponents and fixed-size fractions.
|
5
|
-
"""
|
6
|
-
|
7
|
-
from .bitarray import
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
"""FlexFloat - A library for arbitrary precision floating point arithmetic.
|
2
|
+
|
3
|
+
This package provides the FlexFloat class for handling floating-point numbers
|
4
|
+
with growable exponents and fixed-size fractions.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .bitarray import (
|
8
|
+
BitArray,
|
9
|
+
BitArrayType,
|
10
|
+
Int64BitArray,
|
11
|
+
ListBitArray,
|
12
|
+
create_bitarray,
|
13
|
+
get_available_implementations,
|
14
|
+
parse_bitarray,
|
15
|
+
set_default_implementation,
|
16
|
+
)
|
17
|
+
from .core import FlexFloat
|
18
|
+
|
19
|
+
__version__ = "0.1.3"
|
20
|
+
__author__ = "Ferran Sanchez Llado"
|
21
|
+
|
22
|
+
__all__ = [
|
23
|
+
"FlexFloat",
|
24
|
+
"BitArrayType",
|
25
|
+
"BitArray",
|
26
|
+
"ListBitArray",
|
27
|
+
"Int64BitArray",
|
28
|
+
"create_bitarray",
|
29
|
+
"set_default_implementation",
|
30
|
+
"get_available_implementations",
|
31
|
+
"parse_bitarray",
|
32
|
+
]
|
@@ -0,0 +1,90 @@
|
|
1
|
+
"""BitArray implementation for the flexfloat package."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import Dict, Type
|
6
|
+
|
7
|
+
from .bitarray import BitArray
|
8
|
+
from .bitarray_int64 import Int64BitArray
|
9
|
+
from .bitarray_list import ListBitArray
|
10
|
+
from .bitarray_mixins import BitArrayCommonMixin
|
11
|
+
|
12
|
+
# Type alias for the default BitArray implementation
|
13
|
+
BitArrayType: Type[BitArray] = ListBitArray
|
14
|
+
|
15
|
+
# Available implementations
|
16
|
+
IMPLEMENTATIONS: Dict[str, Type[BitArray]] = {
|
17
|
+
"list": ListBitArray,
|
18
|
+
"int64": Int64BitArray,
|
19
|
+
}
|
20
|
+
|
21
|
+
|
22
|
+
def create_bitarray(
|
23
|
+
implementation: str = "list", bits: list[bool] | None = None
|
24
|
+
) -> BitArray:
|
25
|
+
"""Factory function to create a BitArray with the specified implementation.
|
26
|
+
|
27
|
+
Args:
|
28
|
+
implementation: The implementation to use ("list" or "int64")
|
29
|
+
bits: Initial list of boolean values
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
BitArray: A BitArray instance using the specified implementation
|
33
|
+
|
34
|
+
Raises:
|
35
|
+
ValueError: If the implementation is not supported
|
36
|
+
"""
|
37
|
+
if implementation not in IMPLEMENTATIONS:
|
38
|
+
raise ValueError(
|
39
|
+
f"Unknown implementation '{implementation}'. "
|
40
|
+
f"Available: {list(IMPLEMENTATIONS.keys())}"
|
41
|
+
)
|
42
|
+
|
43
|
+
return IMPLEMENTATIONS[implementation](bits)
|
44
|
+
|
45
|
+
|
46
|
+
def set_default_implementation(implementation: str) -> None:
|
47
|
+
"""Set the default BitArray implementation.
|
48
|
+
|
49
|
+
Args:
|
50
|
+
implementation: The implementation to use as default ("list" or "int64")
|
51
|
+
|
52
|
+
Raises:
|
53
|
+
ValueError: If the implementation is not supported
|
54
|
+
"""
|
55
|
+
global BitArrayType
|
56
|
+
|
57
|
+
if implementation not in IMPLEMENTATIONS:
|
58
|
+
raise ValueError(
|
59
|
+
f"Unknown implementation '{implementation}'. "
|
60
|
+
f"Available: {list(IMPLEMENTATIONS.keys())}"
|
61
|
+
)
|
62
|
+
|
63
|
+
BitArrayType = IMPLEMENTATIONS[implementation]
|
64
|
+
|
65
|
+
|
66
|
+
def get_available_implementations() -> list[str]:
|
67
|
+
"""Get the list of available BitArray implementations.
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
list[str]: List of available implementation names
|
71
|
+
"""
|
72
|
+
return list(IMPLEMENTATIONS.keys())
|
73
|
+
|
74
|
+
|
75
|
+
# Maintain backward compatibility by exposing the methods as module-level functions
|
76
|
+
def parse_bitarray(bitstring: str) -> BitArray:
|
77
|
+
"""Parse a string of bits (with optional spaces) into a BitArray instance."""
|
78
|
+
return BitArrayType.parse_bitarray(bitstring)
|
79
|
+
|
80
|
+
|
81
|
+
__all__ = [
|
82
|
+
"BitArray",
|
83
|
+
"ListBitArray",
|
84
|
+
"Int64BitArray",
|
85
|
+
"BitArrayCommonMixin",
|
86
|
+
"create_bitarray",
|
87
|
+
"set_default_implementation",
|
88
|
+
"get_available_implementations",
|
89
|
+
"parse_bitarray",
|
90
|
+
]
|
@@ -0,0 +1,198 @@
|
|
1
|
+
"""BitArray protocol definition for the flexfloat package."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import Iterator, Protocol, overload, runtime_checkable
|
6
|
+
|
7
|
+
|
8
|
+
@runtime_checkable
|
9
|
+
class BitArray(Protocol):
|
10
|
+
"""Protocol defining the interface for BitArray implementations.
|
11
|
+
|
12
|
+
This protocol defines all the methods and properties that a BitArray
|
13
|
+
implementation must provide.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def __init__(self, bits: list[bool] | None = None) -> None:
|
17
|
+
"""Initialize a BitArray.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
bits: Initial list of boolean values. Defaults to empty list.
|
21
|
+
"""
|
22
|
+
...
|
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
|
+
BitArrayProtocol: A BitArray representing the bits of the floating-point
|
32
|
+
number.
|
33
|
+
"""
|
34
|
+
...
|
35
|
+
|
36
|
+
@classmethod
|
37
|
+
def from_signed_int(cls, value: int, length: int) -> BitArray:
|
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
|
+
BitArrayProtocol: 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
|
+
...
|
49
|
+
|
50
|
+
@classmethod
|
51
|
+
def zeros(cls, length: int) -> BitArray:
|
52
|
+
"""Create a BitArray filled with zeros.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
length: The length of the bit array.
|
56
|
+
Returns:
|
57
|
+
BitArrayProtocol: A BitArray filled with False values.
|
58
|
+
"""
|
59
|
+
...
|
60
|
+
|
61
|
+
@classmethod
|
62
|
+
def ones(cls, length: int) -> BitArray:
|
63
|
+
"""Create a BitArray filled with ones.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
length: The length of the bit array.
|
67
|
+
Returns:
|
68
|
+
BitArrayProtocol: A BitArray filled with True values.
|
69
|
+
"""
|
70
|
+
...
|
71
|
+
|
72
|
+
@staticmethod
|
73
|
+
def parse_bitarray(bitstring: str) -> BitArray:
|
74
|
+
"""Parse a string of bits (with optional spaces) into a BitArray instance."""
|
75
|
+
...
|
76
|
+
|
77
|
+
def to_float(self) -> float:
|
78
|
+
"""Convert a 64-bit array to a floating-point number.
|
79
|
+
|
80
|
+
Returns:
|
81
|
+
float: The floating-point number represented by the bit array.
|
82
|
+
Raises:
|
83
|
+
AssertionError: If the bit array is not 64 bits long.
|
84
|
+
"""
|
85
|
+
...
|
86
|
+
|
87
|
+
def to_int(self) -> int:
|
88
|
+
"""Convert the bit array to an unsigned integer.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
int: The integer represented by the bit array.
|
92
|
+
"""
|
93
|
+
...
|
94
|
+
|
95
|
+
def to_signed_int(self) -> int:
|
96
|
+
"""Convert a bit array into a signed integer using off-set binary
|
97
|
+
representation.
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
int: The signed integer represented by the bit array.
|
101
|
+
Raises:
|
102
|
+
AssertionError: If the bit array is empty.
|
103
|
+
"""
|
104
|
+
...
|
105
|
+
|
106
|
+
def shift(self, shift_amount: int, fill: bool = False) -> BitArray:
|
107
|
+
"""Shift the bit array left or right by a specified number of bits.
|
108
|
+
|
109
|
+
This function shifts the bits in the array, filling in new bits with the
|
110
|
+
specified fill value.
|
111
|
+
If the value is positive, it shifts left; if negative, it shifts right.
|
112
|
+
Fills the new bits with the specified fill value (default is False).
|
113
|
+
|
114
|
+
Args:
|
115
|
+
shift_amount (int): The number of bits to shift. Positive for left shift,
|
116
|
+
negative for right shift.
|
117
|
+
fill (bool): The value to fill in the new bits created by the shift.
|
118
|
+
Defaults to False.
|
119
|
+
Returns:
|
120
|
+
BitArrayProtocol: A new BitArray with the bits shifted and filled.
|
121
|
+
"""
|
122
|
+
...
|
123
|
+
|
124
|
+
def copy(self) -> BitArray:
|
125
|
+
"""Create a copy of the bit array.
|
126
|
+
|
127
|
+
Returns:
|
128
|
+
BitArrayProtocol: A new BitArray with the same bits.
|
129
|
+
"""
|
130
|
+
...
|
131
|
+
|
132
|
+
def __len__(self) -> int:
|
133
|
+
"""Return the length of the bit array."""
|
134
|
+
...
|
135
|
+
|
136
|
+
@overload
|
137
|
+
def __getitem__(self, index: int) -> bool: ...
|
138
|
+
@overload
|
139
|
+
def __getitem__(self, index: slice) -> BitArray: ...
|
140
|
+
|
141
|
+
def __getitem__(self, index: int | slice) -> bool | BitArray:
|
142
|
+
"""Get an item or slice from the bit array."""
|
143
|
+
...
|
144
|
+
|
145
|
+
@overload
|
146
|
+
def __setitem__(self, index: int, value: bool) -> None: ...
|
147
|
+
@overload
|
148
|
+
def __setitem__(self, index: slice, value: BitArray | list[bool]) -> None: ...
|
149
|
+
|
150
|
+
def __setitem__(
|
151
|
+
self, index: int | slice, value: bool | list[bool] | BitArray
|
152
|
+
) -> None:
|
153
|
+
"""Set an item or slice in the bit array."""
|
154
|
+
...
|
155
|
+
|
156
|
+
def __iter__(self) -> Iterator[bool]:
|
157
|
+
"""Iterate over the bits in the array."""
|
158
|
+
...
|
159
|
+
|
160
|
+
def __add__(self, other: BitArray | list[bool]) -> BitArray:
|
161
|
+
"""Concatenate two bit arrays."""
|
162
|
+
...
|
163
|
+
|
164
|
+
def __radd__(self, other: list[bool]) -> BitArray:
|
165
|
+
"""Reverse concatenation with a list."""
|
166
|
+
...
|
167
|
+
|
168
|
+
def __eq__(self, other: object) -> bool:
|
169
|
+
"""Check equality with another BitArray or list."""
|
170
|
+
...
|
171
|
+
|
172
|
+
def __bool__(self) -> bool:
|
173
|
+
"""Return True if any bit is set."""
|
174
|
+
...
|
175
|
+
|
176
|
+
def __repr__(self) -> str:
|
177
|
+
"""Return a string representation of the BitArray."""
|
178
|
+
...
|
179
|
+
|
180
|
+
def __str__(self) -> str:
|
181
|
+
"""Return a string representation of the bits."""
|
182
|
+
...
|
183
|
+
|
184
|
+
def any(self) -> bool:
|
185
|
+
"""Return True if any bit is set to True."""
|
186
|
+
...
|
187
|
+
|
188
|
+
def all(self) -> bool:
|
189
|
+
"""Return True if all bits are set to True."""
|
190
|
+
...
|
191
|
+
|
192
|
+
def count(self, value: bool = True) -> int:
|
193
|
+
"""Count the number of bits set to the specified value."""
|
194
|
+
...
|
195
|
+
|
196
|
+
def reverse(self) -> BitArray:
|
197
|
+
"""Return a new BitArray with the bits in reverse order."""
|
198
|
+
...
|
@@ -0,0 +1,308 @@
|
|
1
|
+
"""Memory-efficient int64-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 Int64BitArray(BitArrayCommonMixin):
|
13
|
+
"""A memory-efficient bit array class using a list of int64 values.
|
14
|
+
|
15
|
+
This implementation packs 64 bits per integer, making it more memory efficient
|
16
|
+
for large bit arrays compared to the boolean list implementation.
|
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
|
+
if bits is None:
|
26
|
+
bits = []
|
27
|
+
|
28
|
+
self._length = len(bits)
|
29
|
+
# Pack bits into int64 chunks (64 bits per int)
|
30
|
+
self._chunks: list[int] = []
|
31
|
+
|
32
|
+
for i in range(0, len(bits), 64):
|
33
|
+
chunk = 0
|
34
|
+
chunk_end = min(i + 64, len(bits))
|
35
|
+
for j in range(i, chunk_end):
|
36
|
+
if bits[j]:
|
37
|
+
chunk |= 1 << (63 - (j - i))
|
38
|
+
self._chunks.append(chunk)
|
39
|
+
|
40
|
+
@classmethod
|
41
|
+
def zeros(cls, length: int) -> Int64BitArray:
|
42
|
+
"""Create a BitArray filled with zeros.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
length: The length of the bit array.
|
46
|
+
Returns:
|
47
|
+
Int64BitArray: A BitArray filled with False values.
|
48
|
+
"""
|
49
|
+
instance = cls.__new__(cls)
|
50
|
+
instance._length = length
|
51
|
+
instance._chunks = [0] * ((length + 63) // 64)
|
52
|
+
return instance
|
53
|
+
|
54
|
+
@classmethod
|
55
|
+
def ones(cls, length: int) -> Int64BitArray:
|
56
|
+
"""Create a BitArray filled with ones.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
length: The length of the bit array.
|
60
|
+
Returns:
|
61
|
+
Int64BitArray: A BitArray filled with True values.
|
62
|
+
"""
|
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
|
73
|
+
|
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)
|
79
|
+
|
80
|
+
return instance
|
81
|
+
|
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)
|
87
|
+
|
88
|
+
def _get_bit(self, index: int) -> bool:
|
89
|
+
"""Get a single bit at the specified index."""
|
90
|
+
if index < 0 or index >= self._length:
|
91
|
+
raise IndexError("Bit index out of range")
|
92
|
+
|
93
|
+
chunk_index = index // 64
|
94
|
+
bit_index = index % 64
|
95
|
+
bit_position = 63 - bit_index # Left-aligned
|
96
|
+
|
97
|
+
return bool(self._chunks[chunk_index] & (1 << bit_position))
|
98
|
+
|
99
|
+
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)
|
112
|
+
|
113
|
+
def to_float(self) -> float:
|
114
|
+
"""Convert a 64-bit array to a floating-point number.
|
115
|
+
|
116
|
+
Returns:
|
117
|
+
float: The floating-point number represented by the bit array.
|
118
|
+
Raises:
|
119
|
+
AssertionError: If the bit array is not 64 bits long.
|
120
|
+
"""
|
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
|
132
|
+
|
133
|
+
def to_int(self) -> int:
|
134
|
+
"""Convert the bit array to an unsigned integer.
|
135
|
+
|
136
|
+
Returns:
|
137
|
+
int: The integer represented by the bit array.
|
138
|
+
"""
|
139
|
+
result = 0
|
140
|
+
for i in range(self._length):
|
141
|
+
if self._get_bit(i):
|
142
|
+
result |= 1 << (self._length - 1 - i)
|
143
|
+
return result
|
144
|
+
|
145
|
+
def to_signed_int(self) -> int:
|
146
|
+
"""Convert a bit array into a signed integer using off-set binary
|
147
|
+
representation.
|
148
|
+
|
149
|
+
Returns:
|
150
|
+
int: The signed integer represented by the bit array.
|
151
|
+
Raises:
|
152
|
+
AssertionError: If the bit array is empty.
|
153
|
+
"""
|
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.
|
164
|
+
|
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).
|
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
|
+
Returns:
|
176
|
+
Int64BitArray: A new BitArray with the bits shifted and filled.
|
177
|
+
"""
|
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)
|
190
|
+
|
191
|
+
return Int64BitArray(new_bits)
|
192
|
+
|
193
|
+
def copy(self) -> Int64BitArray:
|
194
|
+
"""Create a copy of the bit array.
|
195
|
+
|
196
|
+
Returns:
|
197
|
+
Int64BitArray: A new BitArray with the same bits.
|
198
|
+
"""
|
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
|
+
return self._length
|
207
|
+
|
208
|
+
@overload
|
209
|
+
def __getitem__(self, index: int) -> bool: ...
|
210
|
+
@overload
|
211
|
+
def __getitem__(self, index: slice) -> Int64BitArray: ...
|
212
|
+
|
213
|
+
def __getitem__(self, index: int | slice) -> bool | Int64BitArray:
|
214
|
+
"""Get an item or slice from the bit array."""
|
215
|
+
if isinstance(index, slice):
|
216
|
+
start, stop, step = index.indices(self._length)
|
217
|
+
bits = [self._get_bit(i) for i in range(start, stop, step)]
|
218
|
+
return Int64BitArray(bits)
|
219
|
+
return self._get_bit(index)
|
220
|
+
|
221
|
+
@overload
|
222
|
+
def __setitem__(self, index: int, value: bool) -> None: ...
|
223
|
+
@overload
|
224
|
+
def __setitem__(self, index: slice, value: BitArray | list[bool]) -> None: ...
|
225
|
+
|
226
|
+
def __setitem__(
|
227
|
+
self, index: int | slice, value: bool | list[bool] | BitArray
|
228
|
+
) -> None:
|
229
|
+
"""Set an item or slice in the bit array."""
|
230
|
+
if isinstance(index, slice):
|
231
|
+
start, stop, step = index.indices(self._length)
|
232
|
+
indices = list(range(start, stop, step))
|
233
|
+
|
234
|
+
if isinstance(value, BitArray):
|
235
|
+
values = list(value)
|
236
|
+
elif isinstance(value, list):
|
237
|
+
values = value
|
238
|
+
else:
|
239
|
+
raise TypeError("Cannot assign a single bool to a slice")
|
240
|
+
|
241
|
+
if len(indices) != len(values):
|
242
|
+
raise ValueError("Length mismatch in slice assignment")
|
243
|
+
|
244
|
+
for i, v in zip(indices, values):
|
245
|
+
self._set_bit(i, v)
|
246
|
+
return
|
247
|
+
|
248
|
+
if isinstance(value, bool):
|
249
|
+
self._set_bit(index, value)
|
250
|
+
else:
|
251
|
+
raise TypeError("Cannot assign a list or BitArray to a single index")
|
252
|
+
|
253
|
+
def __iter__(self) -> Iterator[bool]:
|
254
|
+
"""Iterate over the bits in the array."""
|
255
|
+
for i in range(self._length):
|
256
|
+
yield self._get_bit(i)
|
257
|
+
|
258
|
+
def __add__(self, other: BitArray | list[bool]) -> Int64BitArray:
|
259
|
+
"""Concatenate two bit arrays."""
|
260
|
+
if isinstance(other, BitArray):
|
261
|
+
return Int64BitArray(list(self) + list(other))
|
262
|
+
return Int64BitArray(list(self) + other)
|
263
|
+
|
264
|
+
def __radd__(self, other: list[bool]) -> Int64BitArray:
|
265
|
+
"""Reverse concatenation with a list."""
|
266
|
+
return Int64BitArray(other + list(self))
|
267
|
+
|
268
|
+
def __eq__(self, other: object) -> bool:
|
269
|
+
"""Check equality with another BitArray or list."""
|
270
|
+
if isinstance(other, BitArray):
|
271
|
+
if len(self) != len(other):
|
272
|
+
return False
|
273
|
+
return all(a == b for a, b in zip(self, other))
|
274
|
+
if isinstance(other, list):
|
275
|
+
return list(self) == other
|
276
|
+
return False
|
277
|
+
|
278
|
+
def __bool__(self) -> bool:
|
279
|
+
"""Return True if any bit is set."""
|
280
|
+
return any(chunk != 0 for chunk in self._chunks)
|
281
|
+
|
282
|
+
def __repr__(self) -> str:
|
283
|
+
"""Return a string representation of the BitArray."""
|
284
|
+
return f"Int64BitArray({list(self)})"
|
285
|
+
|
286
|
+
def any(self) -> bool:
|
287
|
+
"""Return True if any bit is set to True."""
|
288
|
+
return any(chunk != 0 for chunk in self._chunks)
|
289
|
+
|
290
|
+
def all(self) -> bool:
|
291
|
+
"""Return True if all bits are set to True."""
|
292
|
+
if self._length == 0:
|
293
|
+
return True
|
294
|
+
|
295
|
+
# Check full chunks
|
296
|
+
num_full_chunks = self._length // 64
|
297
|
+
for i in range(num_full_chunks):
|
298
|
+
if self._chunks[i] != 0xFFFFFFFFFFFFFFFF:
|
299
|
+
return False
|
300
|
+
|
301
|
+
# Check partial chunk if exists
|
302
|
+
remaining_bits = self._length % 64
|
303
|
+
if remaining_bits > 0:
|
304
|
+
expected_pattern = ((1 << remaining_bits) - 1) << (64 - remaining_bits)
|
305
|
+
if self._chunks[-1] != expected_pattern:
|
306
|
+
return False
|
307
|
+
|
308
|
+
return True
|