"""
`binteger` is a toolkit for manipulating integers in their binary
(fixed-width) representation (big endian - most significant bits go first).
**Documentation**: https://binteger.readthedocs.io/
**Code**: https://github.com/hellman/binteger
**Installation**::
pip install binteger
This module is quite similar to `bitstring` (at PyPI):
https://github.com/scott-griffiths/bitstring
which is pretty awesome.
The difference is that the API is a bit more compact
to aid simple conversions, such as:
>>> from binteger import Bin
>>> Bin(0x4142).bytes
b'AB'
>>> Bin(b'AB').int == 0x4142
True
>>> Bin(14, n=6).tuple
(0, 0, 1, 1, 1, 0)
>>> Bin(0x123, n=16).rol(8).hex
'2301'
.. warning:: `binteger` is optimized for *convenience of use*,
rather than for performance.
"""
from pprint import pprint
from random import randrange
from functools import reduce
from itertools import product
[docs]class Bin:
"""An integer with a fixed-width binary representation.
Provides tools for working on binary representation.
Parameters
----------
spec:
Specifier for the integer:
int,
or list/tuple/str of 0/1 (binary representation),
or bytes (to be unpacked).
n: int, optional
Width of the binary representation.
Attributes
----------
int: int
Integer representation.
n: int
Width of the binary representation.
Examples
--------
Creation:
>>> Bin(16).n
5
>>> Bin(16).int
16
>>> Bin(16, n=3)
Traceback (most recent call last):
ValueError: integer out of range
>>> Bin(b"AB") # bytes are unpacked
Bin(0b0100000101000010, n=16)
>>> Bin("010") # strings must be the binary representation
Bin(0b010, n=3)
>>> Bin("AB") # strings must be 01-formed
Traceback (most recent call last):
ValueError: invalid literal for int() with base 10: 'A'
Outputting (formatting):
>>> str(Bin(16))
'10000'
>>> repr(Bin(16))
'Bin(0b10000, n=5)'
>>> str(Bin(16, 8))
'00010000'
>>> Bin(16).tuple
(1, 0, 0, 0, 0)
>>> Bin(16).list
[1, 0, 0, 0, 0]
>>> Bin(0x4142).bytes
b'AB'
>>> Bin("1101").int
13
>>> Bin("1110001101").hex
'38d'
>>> Bin("1101") == Bin((1, 1, 0, 1)) == Bin([1, 1, 0, 1]) == Bin(13)
True
>>> Bin([0, 1, 2])
Traceback (most recent call last):
ValueError: integer 2 is not binary
"""
__slots__ = "int", "n"
def __init__(self, spec, n=None):
"""
>>> Bin(1, 4).tuple
(0, 0, 0, 1)
>>> Bin(8, 4).tuple
(1, 0, 0, 0)
>>> Bin(16, 4).tuple
Traceback (most recent call last):
ValueError: integer out of range
>>> Bin((1, 0, 0, 0)).int
8
>>> Bin((0, 0, 0, 1)).int
1
>>> Bin(Bin(123123, 32).tuple).int
123123
>>> Bin({1, 4, 5, 6}, 8)
Bin(0b01001110, n=8)
"""
# support Sage integers (ZZ) and IntegerMod etc.
if isinstance(spec, int) or type(spec).__name__.startswith("Integer"):
self.int = int(spec)
self.n = n if n is not None else self.int.bit_length()
elif isinstance(spec, Bin):
self.int = spec.int
self.n = n if n is not None else spec.n
elif isinstance(spec, bytes):
self.int = int.from_bytes(spec, "big")
self.n = len(spec) * 8
elif isinstance(spec, (set, frozenset)):
if n is None:
raise ValueError("n must be specified for set-of-indexes spec")
self.int = sum(2**(n-1-i) for i in spec)
self.n = n if n is not None else self.int.bit_length()
else:
# vector / tuple / list
assert n is None or n == len(spec)
self.n = len(spec)
self.int = sum(
_int01(b) << (self.n - 1 - i)
for i, b in enumerate(spec)
)
if not (0 <= self.int < (1 << self.n)):
raise ValueError("integer out of range")
[docs] @classmethod
def empty(self, n=0):
"""Create empty :class:`Bin`.
>>> Bin.empty(0)
Bin(0b0, n=0)
>>> Bin.empty(4)
Bin(0b0000, n=4)
>>> Bin.zero(4)
Bin(0b0000, n=4)
"""
n = _check_n(n)
return self._new(0, n)
zero = empty
[docs] @classmethod
def full(self, n):
"""Create full :class:`Bin` of width `n`.
>>> Bin.full(0)
Bin(0b0, n=0)
>>> Bin.full(4)
Bin(0b1111, n=4)
>>> Bin.ones(4)
Bin(0b1111, n=4)
"""
n = _check_n(n)
return self._new(2**n-1, n)
ones = full
[docs] @classmethod
def iter(self, *ns):
"""Iterate over all `n`-bit :class:`Bin`s.
Multiple arguments yield product of single iters,
i.e., all tuples of :class:`Bin`s of given sizes `ns`.
>>> list(Bin.iter(2))
[Bin(0b00, n=2), Bin(0b01, n=2), Bin(0b10, n=2), Bin(0b11, n=2)]
>>> pprint(list(Bin.iter(1, 2)))
[(Bin(0b0, n=1), Bin(0b00, n=2)),
(Bin(0b0, n=1), Bin(0b01, n=2)),
(Bin(0b0, n=1), Bin(0b10, n=2)),
(Bin(0b0, n=1), Bin(0b11, n=2)),
(Bin(0b1, n=1), Bin(0b00, n=2)),
(Bin(0b1, n=1), Bin(0b01, n=2)),
(Bin(0b1, n=1), Bin(0b10, n=2)),
(Bin(0b1, n=1), Bin(0b11, n=2))]
"""
if len(ns) == 1:
n = ns[0]
n = _check_n(n)
for x in range(2**n):
yield Bin(x, n)
else:
yield from product(*[self.iter(n) for n in ns])
[docs] @classmethod
def unit(cls, i, n):
r""":class:`Bin` integer only with bit at index `i` set
(equals :math:`2^{n-1-(i\mod n)}`)
Parameters
----------
i : int
The index, requires :math:`-n \le i < n`.
n : int
The width.
Examples
--------
>>> Bin.unit(0, 5)
Bin(0b10000, n=5)
>>> Bin.unit(3, 5)
Bin(0b00010, n=5)
>>> Bin.unit(4, 5)
Bin(0b00001, n=5)
>>> Bin.unit(-5, 5)
Bin(0b10000, n=5)
>>> Bin.unit(-1, 5)
Bin(0b00001, n=5)
>>> Bin.unit(5, 5)
Traceback (most recent call last):
ValueError: integer out of range
>>> Bin.unit(-6, 5)
Traceback (most recent call last):
ValueError: integer out of range
"""
if not (-n <= i < n):
raise ValueError("integer out of range")
return cls(1 << (n - 1 - i % n), n)
[docs] @classmethod
def random(cls, n, nonzero=False):
r"""Random `n`-bit Bin.
Parameters
----------
n : int
The width.
nonzero : bool = False
Force nonzero integer?
Examples
--------
>>> Bin.random(0)
Bin(0b0, n=0)
>>> Bin.random(10) # doctest: +SKIP
Bin(0b0110110011, n=10)
>>> Bin.random(10) # doctest: +SKIP
Bin(0b1010000111, n=10)
>>> all(Bin.random(3) for _ in range(1000))
False
>>> all(Bin.random(3, nonzero=True) for _ in range(1000))
True
"""
return cls(randrange(1 if nonzero else 0, 2**n), n)
@property
def mask(self):
"""Bin: Binary mask for the full width."""
return self._new((1 << (self.n)) - 1, self.n)
# =========================================================
# Creation
# =========================================================
@classmethod
def _new(cls, x, n):
self = object.__new__(cls)
self.int = int(x)
self.n = n
return self
[docs] def copy(self):
return self._new(self.int, self.n)
@classmethod
def _coerce_hint_n(self, other, n):
if not isinstance(other, Bin):
return Bin(other, n=n)
return other
@classmethod
def _coerce_force_n(self, other, n):
if not isinstance(other, Bin):
return Bin(other, n=n)
return other.resize(n)
def _coerce_hint_same_n(self, other):
if not isinstance(other, Bin):
return Bin(other, n=self.n)
return other
def _coerce_same_n(self, other):
if not isinstance(other, Bin):
return Bin(other, n=self.n)
return other.resize(self.n)
[docs] @classmethod
def array(self, *args, n=None, ns=None):
"""Convert a list of objects to a list of :class:`Bin`
with the same `n`, or an iterable `ns` of sizes.
>>> Bin.array(1, 2, 3, 4, n=4)
[Bin(0b0001, n=4), Bin(0b0010, n=4), Bin(0b0011, n=4), Bin(0b0100, n=4)]
>>> Bin.array(1, 2, 3, 4, ns=(1,2,3,4))
[Bin(0b1, n=1), Bin(0b10, n=2), Bin(0b011, n=3), Bin(0b0100, n=4)]
>>> Bin.concat(*Bin.array(1, 2, 3, 4, n=4)).hex
'1234'
>>> Bin.concat(*Bin.array(1, 2, 3, 4, ns=(4, 8, 4, 16))).hex
'10230004'
"""
assert (n is None) or (ns is None)
if n:
return [Bin(arg, n) for arg in args]
if ns:
return [Bin(arg, n) for arg, n in zip(args, ns)]
return [Bin(arg) for arg in args]
[docs] def resize(self, n, truncate=False):
"""Change the width of the integer
(`truncate` must be set to True to force truncation,
otherwise an Exceptino will be raised if doesn't fit).
Examples
--------
>>> Bin(3).resize(10)
Bin(0b0000000011, n=10)
>>> Bin(3).resize(1)
Traceback (most recent call last):
ValueError: integer out of range
>>> Bin(3).resize(1, truncate=True)
Bin(0b1, n=1)
"""
n = _check_n(n)
if truncate:
mask = (1 << n) - 1
return Bin(self.int & mask, n)
else:
return Bin(self.int, n)
# =========================================================
# Output something
# =========================================================
def __index__(self):
"""
Whenever Python needs to losslessly convert the numeric object
to an integer object (such as in slicing, or in the built-in bin(),
hex() and oct() functions).
>>> [0, 10, 20, 30, 40][Bin(3)]
30
"""
return self.int
def __int__(self):
"""
>>> int(Bin(123))
123
"""
return self.int
def __len__(self):
return self.n
@property
def tuple(self):
"""Tuple of the binary representation.
Examples
--------
>>> Bin(0x123).tuple
(1, 0, 0, 1, 0, 0, 0, 1, 1)
>>> Bin(0x123, 10).tuple
(0, 1, 0, 0, 1, 0, 0, 0, 1, 1)
"""
return tuple(map(int, self.bin))
@property
def list(self):
"""List of the binary representation.
Examples
--------
>>> Bin(0x123).list
[1, 0, 0, 1, 0, 0, 0, 1, 1]
>>> Bin(0x123, 10).list
[0, 1, 0, 0, 1, 0, 0, 0, 1, 1]
"""
return list(map(int, self.bin))
@property
def vector(self):
"""GF(2)-vector of the binary representation.
.. warning: Only in sage mode. TBD: better imports
"""
from sage.all import vector, GF
return vector(GF(2), self.tuple)
@property
def support(self):
"""Tuple of indices `i` such that bits with index `i` is set.
>>> Bin(0x1234, 16).support
(3, 6, 10, 11, 13)
>>> Bin(0x1234).support
(0, 3, 7, 8, 10)
"""
return tuple(i for i, c in enumerate(self.tuple) if c)
def __iter__(self):
return iter(self.tuple)
def __str__(self):
"""01-string binary representation."""
return "".join("%d" % v for v in self.tuple)
str = property(__str__)
def __repr__(self):
return f"Bin(0b{str(self)}, n={self.n})"
@property
def bytes(self):
r"""
Bytes representation (big-endian).
>>> Bin(0x4142, 24).bytes
b'\x00AB'
"""
return self.int.to_bytes((self.n + 7) // 8, "big")
@property
def hex(self):
"""Hexadecimal representation.
>>> Bin(0xabc, 12).hex
'abc'
>>> Bin(0xabc, 13).hex
'0abc'
>>> Bin(0xabc, 16).hex
'0abc'
"""
return f"{self.int:x}".zfill((self.n + 3) // 4)
@property
def bin(self):
"""
>>> Bin(0xabc, 12).bin
'101010111100'
>>> Bin(0xabc, 13).bin
'0101010111100'
>>> Bin(0xabc, 16).bin
'0000101010111100'
"""
return f"{self.int:b}".zfill(self.n)
def __getitem__(self, idx):
"""
>>> Bin(0x1234, 16)[0:4]
Bin(0b0001, n=4)
>>> Bin(0x1234, 16)[4:12] # 0x23 == 35
Bin(0b00100011, n=8)
>>> Bin(0x1234, 16)[-4:]
Bin(0b0100, n=4)
>>> Bin("101010")[::2]
Bin(0b111, n=3)
>>> Bin("101010")[0]
1
>>> Bin("101010")[1]
0
>>> Bin("101010")[2]
1
>>> Bin("101010")[5]
0
>>> Bin("101010")[6]
Traceback (most recent call last):
IndexError: index 6 out of range (-6,5)
>>> Bin("101010")[-6]
1
>>> Bin("101010")[-7]
Traceback (most recent call last):
IndexError: index -7 out of range (-6,5)
"""
if isinstance(idx, slice):
# todo: optimize simple substring slices?
# easy to mess up with out of bounds, negative indices, etc. ...
return Bin(self.tuple[idx])
else:
idx = int(idx)
if idx < -self.n or idx >= self.n:
raise IndexError(f"index {idx} out of range ({-self.n},{self.n-1})")
if idx < 0:
idx += self.n
assert 0 <= idx < self.n
return 1 & (self.int >> (self.n - 1 - idx))
# __setitem__ maybe ?
# do we want to keep this immutable?
# =========================================================
# Comparison
# =========================================================
def __eq__(self, other):
other = self._coerce_hint_same_n(other)
if other.n != self.n:
raise ValueError("Can not compare Bin's with different n")
return self.int == other.int
def __lt__(self, other):
"""
>>> Bin(3, 5) < Bin(4, 5)
True
>>> Bin(3, 5) < Bin(3, 5)
False
>>> Bin(4, 5) < Bin(3, 5)
False
>>> Bin(3, 5) < 4
True
>>> Bin(3, 5) < 3
False
>>> Bin(4, 5) < 3
False
"""
other = self._coerce_same_n(other)
return self.int < other.int
def __le__(self, other):
other = self._coerce_same_n(other)
return self.int <= other.int
def __gt__(self, other):
other = self._coerce_same_n(other)
return self.int > other.int
def __ge__(self, other):
other = self._coerce_same_n(other)
return self.int >= other.int
def __bool__(self):
"""
>>> bool(Bin("000"))
False
>>> bool(Bin("100"))
True
>>> bool(Bin("101") & 1)
True
>>> bool(Bin("100") & 1)
False
"""
return self.int != 0
def __hash__(self):
"""
Hash matches that of underlying int to allow reusing Bin as dict keys
>>> d = {10: 123}; d[Bin(10, 5)] == d[Bin(10, 100)] == 123
True
>>> hash(100) == hash(Bin(100, 7)) == hash(Bin(100, 10))
True
"""
return hash(self.int)
[docs] def is_prec(self, other):
return self.int & other == self.int and self.int != other
[docs] def is_preceq(self, other):
return self.int & other == self.int
[docs] def is_succ(self, other):
return self.int & other == other and self.int != other
[docs] def is_succeq(self, other):
return self.int & other == other
# =========================================================
# Properties
# =========================================================
@property
def weight(self):
"""Hamming weight.
(Aliases: :attr:`hw`, :attr:`wt`).
>>> Bin(0).weight
0
>>> Bin(1).weight
1
>>> Bin(0xffffffff).weight
32
>>> Bin(2**64-1).weight
64
>>> Bin(int("10" * 999, 2)).weight
999
"""
return sum(self.tuple)
wt = hw = weight
@property
def parity(self):
"""
Parity of all bits.
>>> Bin(0).parity
0
>>> Bin(1).parity
1
>>> Bin(4).parity
1
>>> Bin(6).parity
0
>>> Bin(2**100).parity
1
>>> Bin(2**100 + 1).parity
0
>>> Bin(2**100 ^ 7).parity
0
>>> Bin(2**100 ^ 3).parity
1
"""
return self.weight & 1
# =========================================================
# Transformations / operations
# =========================================================
[docs] def rol(self, n):
"""
Rotate left by @n bits
>>> hex( Bin(0x1234, 16).rol(4).int )
'0x2341'
>>> hex( Bin(0x1234, 16).rol(12).int )
'0x4123'
"""
n %= self.n
x = self.int
y = ((x << n) | (x >> (self.n - n))) & self.mask
return self._new(y, n=self.n)
[docs] def ror(self, n):
"""
Rotate right by @n bits
>>> hex( Bin(0x1234, 16).ror(4).int )
'0x4123'
>>> hex( Bin(0x1234, 16).ror(12).int )
'0x2341'
>>> Bin(1, 16).rol(1).n
16
"""
return self.rol(-n)
def __and__(self, other):
other = Bin(other)
n = max(self.n, other.n)
return self._new(self.int & other.int, n=n)
__rand__ = __and__
def __xor__(self, other):
other = Bin(other, n=self.n)
n = max(self.n, other.n)
return self._new(self.int ^ other.int, n=n)
__rxor__ = __xor__
def __or__(self, other):
other = Bin(other, n=self.n)
n = max(self.n, other.n)
return self._new(self.int | other.int, n=n)
__ror__ = __or__
def __lshift__(self, n):
y = (self.int << n) & self.mask
return self._new(y, n=self.n)
def __rshift__(self, n):
y = (self.int >> n) & self.mask
return self._new(y, n=self.n)
def __invert__(self):
y = self.int ^ self.mask
return self._new(y, n=self.n)
def __neg__(self):
"""
>>> -Bin(1, 4)
Bin(0b1111, n=4)
>>> -Bin(0, 4)
Bin(0b0000, n=4)
"""
mod = 1 << self.n
v = mod - self.int
if v >= mod:
v -= mod
return self._new(v, n=self.n)
def __add__(self, other):
"""
>>> Bin(31, 5) + 2
Bin(0b00001, n=5)
>>> Bin(31, 5) + Bin(1, 5)
Bin(0b00000, n=5)
>>> Bin(31, 5) + Bin(1, 6)
Traceback (most recent call last):
...
ValueError: add/sub Bin with different n? 5 vs 6
"""
if isinstance(other, Bin):
if self.n != other.n:
raise ValueError(f"add/sub Bin with different n? {self.n} vs {other.n}")
other = other.int
mask = (1 << self.n) - 1
v = int(self.int + other) & mask
return self._new(v, n=self.n)
__radd__ = __add__
def __sub__(self, other):
"""
>>> Bin(1, 5) - 2
Bin(0b11111, n=5)
>>> Bin(17, 5) - Bin(1, 5)
Bin(0b10000, n=5)
>>> Bin(1, 5) + Bin(31, 6)
Traceback (most recent call last):
...
ValueError: add/sub Bin with different n? 5 vs 6
"""
if isinstance(other, Bin):
if self.n != other.n:
raise ValueError(f"add/sub Bin with different n? {self.n} vs {other.n}")
other = other.int
mask = (1 << self.n) - 1
v = int(self.int - other) & mask
return self._new(v, n=self.n)
def __rsub__(self, other):
"""
>>> Bin(1, 5) - 2
Bin(0b11111, n=5)
>>> Bin(17, 5) - Bin(1, 5)
Bin(0b10000, n=5)
>>> Bin(1, 5) + Bin(31, 6)
Traceback (most recent call last):
...
ValueError: add/sub Bin with different n? 5 vs 6
"""
if isinstance(other, Bin):
if self.n != other.n:
raise ValueError(f"add/sub Bin with different n? {self.n} vs {other.n}")
other = other.int
mask = (1 << self.n) - 1
v = int(other - self.int) & mask
return self._new(v, n=self.n)
[docs] def scalar_bin(self, other):
"""Dot product in GF(2).
>>> Bin(0).scalar_bin(0)
0
>>> Bin(1).scalar_bin(1)
1
>>> Bin(0xf731).scalar_bin(0xffffff)
0
>>> Bin(1).scalar_bin(3)
1
>>> Bin(7).scalar_bin(15)
1
Same as:
>>> (Bin(7) @ 15) & 1
1
>>> (Bin(7) & 15).parity
1
"""
return (self & other).parity
[docs] def scalar_int(self, other):
"""Dot product in integers. Aliased as overloaded @,
similarly to .dot = @ in numpy.
>>> Bin(0) @ 0
0
>>> Bin(1) @ 1
1
>>> Bin(0xf731) @ 0xffffff
10
>>> Bin(1) @ 3
1
>>> Bin(7) @ 15
3
Same as:
>>> (Bin(7) & 15).weight
3
"""
return (self & other).weight
# as .dot() = @ in numpy
__matmul__ = __rmatmul__ = scalar_int
[docs] def bit_product(self, mask):
"""
Multiply bits selected by mask.
(better method name?)
>>> Bin("101").bit_product("101")
1
>>> Bin("101").bit_product("100")
1
>>> Bin("101").bit_product("001")
1
>>> Bin("101").bit_product("111")
0
>>> Bin("1111").bit_product("1111")
1
>>> Bin("1111").bit_product("1110")
1
>>> Bin("1111").bit_product("0111")
1
>>> Bin("0111").bit_product("1111")
0
>>> Bin("1110").bit_product("1111")
0
"""
mask = self._coerce_same_n(mask)
return int(self.int & mask.int == mask.int)
[docs] @classmethod
def concat(cls, *args, n=None, ns=None):
"""
Concatenate bitstrings. Classmethod, varargs.
:param:
>>> Bin.concat(Bin(128), Bin(255), Bin(1, n=8)).str
'100000001111111100000001'
>>> Bin.concat(1, 2, 3, 4).str
'11011100'
>>> Bin.concat(1, 2, 3, 4, n=3).str
'001010011100'
>>> Bin.concat(1, 2, 3, 15, n=4).str
'0001001000111111'
>>> Bin.concat(1, 2, 3, 16, n=4).str
Traceback (most recent call last):
ValueError: integer out of range
"""
if not args:
return Bin(0, n=0)
args = cls.array(*args, n=n, ns=ns)
return reduce(lambda a, b: a._concat1(b), args)
def _concat1(self, other, n=None):
y = (self.int << other.n) | other.int
return self._new(y, n=self.n + other.n)
[docs] def halves(self):
"""
>>> Bin(0x79, 8).halves()
(Bin(0b0111, n=4), Bin(0b1001, n=4))
"""
return self.split(parts=2)
[docs] def swap_halves(self):
"""
>>> Bin(0x79, 8).swap_halves().hex
'97'
"""
l, r = self.halves()
return Bin.concat(r, l)
[docs] def split(self, parts=None, n=None, ns=None):
"""
Split the bitstring into several parts.
Either:
- into @parts same-sized chunks
- into parts with sizes @n each or given by @ns
>>> Bin(0x123, 12).split(3) == Bin(0x123, 12).split(parts=3)
True
>>> Bin(0x123, 12).split(parts=3)
(Bin(0b0001, n=4), Bin(0b0010, n=4), Bin(0b0011, n=4))
>>> Bin(0x9821, 16).split(ns=(4, 4, 8)) # 0x21 == 33
(Bin(0b1001, n=4), Bin(0b1000, n=4), Bin(0b00100001, n=8))
>>> Bin(0x9821, 16).split(n=4)
(Bin(0b1001, n=4), Bin(0b1000, n=4), Bin(0b0010, n=4), Bin(0b0001, n=4))
"""
assert 1 == (parts is not None) + (ns is not None) + (n is not None)
if parts or n:
if parts:
assert self.n % parts == 0
n = self.n // parts
else:
assert self.n % n == 0
parts = self.n // n
ret = []
mask = (1 << n) - 1
x = self.int
for i in range(parts):
ret.append(self._new(x & mask, n=n))
x >>= n
return tuple(ret[::-1])
if ns:
assert sum(ns) == self.n
ret = []
x = self.int
for n in reversed(ns):
mask = (1 << n) - 1
ret.append(self._new(x & mask, n=n))
x >>= n
return tuple(ret[::-1])
[docs] def squeeze_by_mask(self, mask):
"""
Keep bits selected by mask and delete the others.
Parameters
----------
mask: :class:`Bin` or int or str
Mask for squeeze, will be converted to ``Bin(..., self.n)``
Examples
--------
>>> Bin("10101").squeeze_by_mask("10101").str
'111'
>>> Bin("10101").squeeze_by_mask("01111").str
'0101'
"""
mask = self._coerce_same_n(mask).int
res = 0
n = 0
x = self.int
while mask:
if mask & 1:
res |= (x & 1) << n
n += 1
mask >>= 1
x >>= 1
return self._new(res, n=n)
[docs]def Bin8(x):
"""Fixed-width 8-bit :class:`Bin` (shortcut to ``Bin(x, 8)``) ."""
return Bin(x, n=8)
[docs]def Bin16(x):
"""Fixed-width 16-bit :class:`Bin` (shortcut to ``Bin(x, 16)``) ."""
return Bin(x, n=16)
[docs]def Bin32(x):
"""Fixed-width 32-bit :class:`Bin` (shortcut to ``Bin(x, 32)``) ."""
return Bin(x, n=32)
[docs]def Bin64(x):
"""Fixed-width 64-bit :class:`Bin` (shortcut to ``Bin(x, 64)``) ."""
return Bin(x, n=64)
def _int01(v):
w = int(v)
if not 0 <= w <= 1:
raise ValueError("integer %d is not binary" % w)
return w
def _check_n(n):
n = int(n)
if n < 0:
raise ValueError("Negative width?")
return n
def _test_halves():
"""
>>> Bin.concat(Bin(0x7, n=4), Bin(0xa, 4)).hex
'7a'
>>> Bin.concat(*Bin.array(1, 2, 3, 4, 5, 6, 10, n=4)).hex
'123456a'
>>> Bin.concat(1, 2, 3, 4, 5, 6, 10, n=4).hex
'123456a'
"""
pass