Skip to content

Instantly share code, notes, and snippets.

@Techcable
Created February 25, 2017 20:32
Show Gist options
  • Select an option

  • Save Techcable/491256f738bc4df3cc8e764ee6dc794e to your computer and use it in GitHub Desktop.

Select an option

Save Techcable/491256f738bc4df3cc8e764ee6dc794e to your computer and use it in GitHub Desktop.
Python Bitvector
from array import array
from collections import Iterable
def _chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
class BitVector:
__slots__ = "_words", "size"
def __init__(self, initial_values=None):
words = array('Q')
if initial_values is not None:
if type(initial_values) is str:
assert not initial_values.startswith("0b")
value = int(initial_values, 2)
size = len(initial_values)
words.frombytes(value.to_bytes(((size + 63) // 64) * 8, byteorder='little'))
elif isinstance(initial_values, Iterable):
initial_values = list(initial_values)
size = len(initial_values)
# Type-check
for value in initial_values:
if type(value) != bool:
raise TypeError(f"Value isn't a boolean: {type(value)}")
# Split into sections of 64
for section in _chunks(initial_values, 64):
word = 0
for index, value in enumerate(section):
if value:
word |= 1<<index
words.append(word)
else:
raise TypeError(f"Invalid type for initial_values: {type(initial_values)}")
assert size <= len(words) * 64, f"Size {size} greater than max {len(words) * 64}"
object.__setattr__(self, '_words', words)
object.__setattr__(self, 'size', size)
def __getitem__(self, index):
if type(index) != int:
raise TypeError(f"Can't index bitvector with {type(index)}")
if index >= self.size:
raise IndexError(f"Index out of bounds: {index}")
word = self._words[index // 64]
return (word & (1<<(index%64))) != 0
def __setitem__(self, index, value):
if type(index) != int:
raise TypeError(f"Can't index bitvector with {type(index)}")
if type(value) != bool:
raise TypeError(f"Can't place {type(value)} in a bitvector")
word = self._words[index // 64]
if value:
word |= (1<<(index%64))
else:
word &= ~(1<<(index%64))
self._words[index // 64] = word
def __iter__(self):
for i in range(len(self)):
yield self[i]
def __len__(self):
return self.size
def to_array(self):
return list(self)
@property
def words(self):
return list(self._words)
@property
def bytes(self):
return int(self).to_bytes(self.size + 7 // 8, byteorder='little')
def __int__(self):
return int.from_bytes(self._words.tobytes(), byteorder='little')
def __str__(self):
value = int(self)
s = bin(value)
s = s.lstrip("0b")
s = s.rjust(self.size, '0') # Add trailing zeros
return s
def __repr__(self):
return f"BitVector({str(self)})"
def __eq__(self, other):
return type(other) is BitVector and str(self) == str(other)
def __setattr__(self, key, value):
raise AttributeError(f"BitVector's {key} can't be modified!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment