11"""core representation for binary sequences"""
2+
23from __future__ import annotations
3- from collections .abc import Sequence
4+
5+ import math
6+ from collections .abc import Iterator , Sequence
47from enum import StrEnum
58from itertools import groupby
6- import math
7- from typing import Iterator , TYPE_CHECKING , Any , Self
9+ from typing import TYPE_CHECKING , Any , Literal , Self
10+
811__all__ = ("Direction" , "BinarySequence" )
912
1013
@@ -25,19 +28,15 @@ def __init__(self, bits: Sequence[int | str] | str) -> None:
2528 self .bits : tuple [int , ...] = self ._validate_bits (bits )
2629
2730 @staticmethod
28- def _validate_bits (
29- input_bits : Sequence [int | str ] | str
30- ) -> tuple [int , ...]:
31+ def _validate_bits (input_bits : Sequence [int | str ] | str ) -> tuple [int , ...]:
3132 """Validate input bit sequences."""
3233 if (input_bits is None ) or (not input_bits ):
3334 raise ValueError ("Input bits cannot be None or empty." )
3435
3536 try :
3637 bits_list : tuple [int , ...] = tuple (int (bit ) for bit in input_bits )
3738 except (TypeError , ValueError ) as e :
38- raise TypeError (
39- "Bits must be an iterable of 0 and 1 values."
40- ) from e
39+ raise TypeError ("Bits must be an iterable of 0 and 1 values." ) from e
4140
4241 if any (bit not in (0 , 1 ) for bit in bits_list ):
4342 raise ValueError ("Bit sequence must only contain 0 or 1." )
@@ -64,12 +63,7 @@ def __str__(self) -> str:
6463 return self .bit_string
6564
6665 def __repr__ (self ) -> str :
67- return (
68- f"{ type (self ).__name__ } ("
69- f"length={ self .length } , "
70- f"preview='{ str (self )} '"
71- f")"
72- )
66+ return f"{ type (self ).__name__ } (length={ self .length } , preview='{ str (self )} ')"
7367
7468 def __eq__ (self , other : object ) -> bool :
7569 if not isinstance (other , BinarySequence ):
@@ -135,7 +129,7 @@ def as_bytes(self) -> bytes:
135129 zero_padding_len : int = (- len (bit_str )) % 8
136130 bit_str_padded : str = ("0" * zero_padding_len ) + bit_str
137131 byte_conversion : bytes = int (bit_str_padded , 2 ).to_bytes (
138- len (bit_str_padded )// 8 , "big"
132+ len (bit_str_padded ) // 8 , "big"
139133 )
140134
141135 return byte_conversion
@@ -177,7 +171,7 @@ def entropy(self) -> float:
177171 if self .length == 0 :
178172 return 0.0
179173
180- p1 = self .ones / self .length
174+ p1 = self .ones / self .length
181175 p0 = 1.0 - p1
182176
183177 entropy = 0.0
@@ -188,7 +182,7 @@ def entropy(self) -> float:
188182 return round (entropy , 5 )
189183
190184 @property
191- def run_lengths (self ):
185+ def run_lengths (self ) -> list [ tuple [ int , int ]] :
192186 """Return list of run lengths [(digit, run count)].
193187
194188 E.g. 111001 >> [(1, 3), (0, 2), (1, 1)]
@@ -212,15 +206,15 @@ def to_length(self, n: int) -> BinarySequence:
212206 return BinarySequence (bits )
213207
214208 def shift (
215- self ,
216- n : int ,
217- direction : Direction = Direction .LEFT
218- ) -> " BinarySequence" :
219- """Shift sequence (circular)
209+ self ,
210+ n : int ,
211+ direction : Direction | Literal [ "left" , "right" ] = Direction .LEFT ,
212+ ) -> BinarySequence :
213+ """Shift sequence (circular).
220214
221215 Args:
222216 n (int): How many bits to shift by.
223- direction (Direction, optional): 'left' or 'right'.
217+ direction (Direction|Literal['left', 'right'] , optional): 'left' or 'right'.
224218 Defaults to Direction.LEFT.
225219
226220 Returns:
@@ -236,8 +230,7 @@ def shift(
236230 if n < 0 :
237231 n = - n
238232 direction = (
239- Direction .RIGHT if direction == Direction .LEFT
240- else Direction .LEFT
233+ Direction .RIGHT if direction == Direction .LEFT else Direction .LEFT
241234 )
242235
243236 match direction :
@@ -248,13 +241,13 @@ def shift(
248241
249242 raise ValueError (f"{ direction } not a valid direction; 'left', 'right'" )
250243
251- def autocorr (self ):
244+ def autocorr (self ) -> Any :
252245 raise NotImplementedError ("Auto-correlation coming soon." )
253246
254- def crosscorr (self ):
247+ def crosscorr (self ) -> Any :
255248 raise NotImplementedError ("Cross-correlation coming soon." )
256249
257- def to_numpy (self , dtype : " NpDTypeInt | None" = None ) -> " NpNDArrayInt" :
250+ def to_numpy (self , dtype : NpDTypeInt | None = None ) -> NpNDArrayInt :
258251 """Convert BinarySequence to 1D NumPy array.
259252
260253 NumPy is an optional dependency, only required when calling the to_numpy
@@ -280,7 +273,7 @@ def to_numpy(self, dtype: "NpDTypeInt | None" = None) -> "NpNDArrayInt":
280273 return np .array (self .bits , dtype = out_dtype )
281274
282275 @classmethod
283- def from_numpy (cls , np_array : " NpNDArrayInt" ) -> Self :
276+ def from_numpy (cls , np_array : NpNDArrayInt ) -> Self :
284277 """Convert 1D NumPy array to BinarySequence.
285278
286279 Args:
@@ -329,7 +322,7 @@ def xor(self, other: BinarySequence) -> BinarySequence:
329322 """
330323 other_sequence = self ._compatible_bits (other , "xor" )
331324 return BinarySequence (
332- tuple (a ^ b for a , b in zip (self .bits , other_sequence ))
325+ tuple (a ^ b for a , b in zip (self .bits , other_sequence , strict = True ))
333326 )
334327
335328 def bitwise_and (self , other : BinarySequence ) -> BinarySequence :
@@ -343,7 +336,7 @@ def bitwise_and(self, other: BinarySequence) -> BinarySequence:
343336 """
344337 other_sequence = self ._compatible_bits (other , "and" )
345338 return BinarySequence (
346- tuple (a & b for a , b in zip (self .bits , other_sequence ))
339+ tuple (a & b for a , b in zip (self .bits , other_sequence , strict = True ))
347340 )
348341
349342 def bitwise_or (self , other : BinarySequence ) -> BinarySequence :
@@ -357,8 +350,8 @@ def bitwise_or(self, other: BinarySequence) -> BinarySequence:
357350 """
358351 other_sequence = self ._compatible_bits (other , "or" )
359352 return BinarySequence (
360- tuple (a | b for a , b in zip (self .bits , other_sequence ))
353+ tuple (a | b for a , b in zip (self .bits , other_sequence , strict = True ))
361354 )
362355
363- def hamming_distance (self ):
356+ def hamming_distance (self ) -> Any :
364357 raise NotImplementedError ("Hamming distance coming soon" )
0 commit comments