import math

class BhashException(Exception): pass

class Bhash:
    """
    Hash manipulation class.

    The hash is internally treated as a big-endinan integer. This allows for
    easy implementation of operations such as modulo.

    Note: The consumed bits are calculated using math.log() so the exact number
    may be inprecise.
    """
    def __init__(self):
        self.bhash = None
        self.bits_avail = 0
        self.bits_used = 0.0

    def from_bytes(self, buf: bytes):
        """
        Initialize a Bhash from a bytes object
        """
        self.bhash = int.from_bytes(buf, byteorder='big')
        self.bits_avail = len(buf) * 8
        self.bits_used = 0.0

    def modulo(self, mod: int) -> int:
        """
        Treat the hash as a bigint and divide it by mod. The hash is updated
        with the division result while the module value is returned.
        """
        self.bits_used += math.log(mod) / math.log(2)
        if self.bits_used > self.bits_avail:
            raise BhashException("Consumed all bits in hash")

        r = self.bhash % mod
        self.bhash //= mod

        return r
