Created
February 25, 2017 20:32
-
-
Save Techcable/491256f738bc4df3cc8e764ee6dc794e to your computer and use it in GitHub Desktop.
Python Bitvector
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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