Here is the current code for the BitArray
class needing optimization (implemented on big integers):
import itertools
################################################################################
class BitArray:
def __init__(self, values=[], max_size=128):
self.__data = []
self.__size = 0
self.__bits = max_size << 3 # Bytes -> Bits
self.extend(values)
########################################################################
# String Operators
def __repr__(self):
return '{}({}, {})'.format(self.__class__.__name__,
self, self.__bits >> 3)
def __str__(self):
return repr(list(self))
########################################################################
# Iteration Methods
def __iter__(self):
size = len(self)
for index in range(size):
if len(self) != size:
raise RuntimeError()
yield self[index]
def __reversed__(self):
size = len(self)
for index in range(size - 1, -1, -1):
if len(self) != size:
raise RuntimeError()
yield self[index]
########################################################################
# Container Methods
def __len__(self):
return self.__size
def __contains__(self, item):
return any(value == item for value in self)
def __getitem__(self, key):
div, mod = divmod(self.__get_index(key), self.__bits)
return bool(self.__data[div] >> mod & 1)
def __setitem__(self, key, value):
div, mod = divmod(self.__get_index(key), self.__bits)
if value:
self.__data[div] |= 1 << mod
else:
self.__data[div] &= ~(1 << mod)
def __delitem__(self, key):
div, mod = divmod(self.__get_index(key), self.__bits)
value = self.__data[div]
self.__data[div] = value >> 1 & -(1 << mod) | value & (1 << mod) - 1
set_mask = 1 << (self.__bits - 1)
for div in range(div + 1, len(self.__data)):
value = self.__data[div]
if value & 1:
self.__data[div - 1] |= set_mask
self.__data[div] = value >> 1
self.__size -= 1
if not len(self) % self.__bits:
del self.__data[-1]
########################################################################
# Comparison Operators
def __eq__(self, other):
return self.__cmp__(other) == 0
def __gt__(self, other):
return self.__cmp__(other) > 0
def __lt__(self, other):
return self.__cmp__(other) < 0
def __ne__(self, other):
return self.__cmp__(other) != 0
def __ge__(self, other):
return self.__cmp__(other) >= 0
def __le__(self, other):
return self.__cmp__(other) <= 0
########################################################################
# Math Operators
def __add__(self, other):
value = self.__class__(self, self.__bits >> 3)
value.extend(other)
return value
def __mul__(self, other):
value = self.__class__([], self.__bits >> 3)
for _ in range(other):
value.extend(self)
return value
def __rmul__(self, other):
return self * other
def __iadd__(self, other):
self.extend(other)
return self
def __imul__(self, other):
size = len(self)
for _ in range(other - 1):
for index in range(size):
self.append(self[index])
return self
########################################################################
# List Methods
def append(self, item):
index = len(self) % self.__bits
if not index:
self.__data.append(0)
if item:
self.__data[-1] |= 1 << index
self.__size += 1
def extend(self, iterable):
for item in iterable:
self.append(item)
def count(self, item):
return sum(1 for value in self if value == item)
def index(self, item, start=None, stop=None):
for index in range(
0 if start is None else self.__get_index(start),
len(self) if stop is None else self.__get_index(stop)):
if self[index] == item:
return index
raise ValueError()
def reverse(self):
for index in range(len(self) >> 1):
self[index], self[~index] = self[~index], self[index]
def pop(self, index=None):
index = len(self) - 1 if index is None else self.__get_index(index)
value = self[index]
del self[index]
return value
def remove(self, item):
for index, value in enumerate(self):
if value == item:
del self[index]
return
raise ValueError()
def sort(self, *, key=lambda item: item, reverse=False):
reverse, lo, hi = bool(reverse), 0, len(self) - 1
while lo < hi:
if bool(key(self[lo])) is not reverse:
while lo < hi:
if bool(key(self[hi])) is reverse:
break
hi -= 1
else:
break
self[lo], self[hi] = self[hi], self[lo]
lo += 1
def insert(self, index, item):
for index in range(self.__get_index(index), len(self)):
temp = item
item = self[index]
self[index] = temp
self.append(item)
########################################################################
# Helper Methods
def __cmp__(self, other):
for a, b in itertools.zip_longest(self, map(bool, other)):
if a != b:
if None in {a, b}:
return (b is None) - (a is None)
return a - b
return 0
def __get_index(self, key):
size = len(self)
if key < 0:
key += size
if not 0 <= key < size:
raise IndexError()
return key
These are some tests used to make sure that the code is still working after making various changes to it:
# Test Range Check
x = BitArray([], 1)
x.append(True)
try:
x[5] = True
except IndexError:
pass
else:
raise RuntimeError()
try:
y = x[5]
except IndexError:
pass
else:
del y
raise RuntimeError()
# Test Append, Set, Get
x = BitArray([], 1)
for _ in range(7):
x.append(False)
x.append(True)
assert x[7]
assert not x[6]
x[6] = True
x[7] = False
assert x[6]
assert not x[7]
# Test Extend and Compare
x = BitArray([False, False])
y = BitArray([False, False])
assert x.__cmp__(y) == 0
x[0] = True
assert x.__cmp__(y) == +1
y[0] = y[1] = True
assert x.__cmp__(y) == -1
x[1] = True
x.append(False)
assert x.__cmp__(y) == +1
x[2] = True
assert x.__cmp__(y) == +1
y.append(True)
y.append(False)
assert x.__cmp__(y) == -1
y[3] = True
assert x.__cmp__(y) == -1
# Test Addition, Multiplication
x = BitArray([False, True])
y = BitArray([True, False])
assert x + y == BitArray([False, True, True, False])
assert x * 3 == x + x + x
assert y * 3 == y + y + y
assert 3 * x == x + x + x
z = BitArray(x)
x += y
assert x == z + y
z = BitArray(y)
y *= 3
assert y == z + z + z
# Test Reverse
x = BitArray([False, True])
y = BitArray([True, False])
x.reverse()
assert x == y
# Test Remove
x = BitArray([False, True])
x.remove(False)
x.remove(True)
try:
x.remove(False)
except ValueError:
pass
else:
raise RuntimeError()
try:
x.remove(True)
except ValueError:
pass
else:
raise RuntimeError()
# Test Delete, Pop
x = 0b1110011111100111
y = BitArray((x >> shift & 1 for shift in range(x.bit_length())), 1)
del y[15], y[2], y[-1]
assert len(y) == 13
del y[-1]
assert len(y) == 12
assert not y[11] and not y[10] and y[8]
y[8] = False
del y[6]
assert y._BitArray__data[0] == 0b1110011
assert y.pop(0)
assert not y.pop()
Edit: Sometimes I write code for fun and the benefit of others. It was disappointing to not find a module like this in the standard library. This is an exercise of writing a good BitArray
class that anyone can use. As a result, this code was created to:
- act similarly to the
list
data type - have an efficient implementation
- use
int
array with maximum bit length