from typing import TypeVar, Sequence, Optional


T = TypeVar('T')


class ReadOnlyList(Sequence[T]):

    def __init__(self, lst):
        self.lst = lst

    def __getitem__(self, key):
        return self.lst[key]

    def __len__(self) -> int:
        return len(self.lst)


def subclass_tuple(name, base):
    return type(name, (base,), {'__slots__': ()})


class ArithUint256:
    # https://github.com/bitcoin/bitcoin/blob/master/src/arith_uint256.cpp

    __slots__ = '_value', '_compact'

    def __init__(self, value: int) -> None:
        self._value = value
        self._compact: Optional[int] = None

    @classmethod
    def from_compact(cls, compact) -> 'ArithUint256':
        size = compact >> 24
        word = compact & 0x007fffff
        if size <= 3:
            return cls(word >> 8 * (3 - size))
        else:
            return cls(word << 8 * (size - 3))

    @property
    def value(self) -> int:
        return self._value

    @property
    def compact(self) -> int:
        if self._compact is None:
            self._compact = self._calculate_compact()
        return self._compact

    @property
    def negative(self) -> int:
        return self._calculate_compact(negative=True)

    @property
    def bits(self) -> int:
        """ Returns the position of the highest bit set plus one. """
        bits = bin(self._value)[2:]
        for i, d in enumerate(bits):
            if d:
                return (len(bits) - i) + 1
        return 0

    @property
    def low64(self) -> int:
        return self._value & 0xffffffffffffffff

    def _calculate_compact(self, negative=False) -> int:
        size = (self.bits + 7) // 8
        if size <= 3:
            compact = self.low64 << 8 * (3 - size)
        else:
            compact = ArithUint256(self._value >> 8 * (size - 3)).low64
        # The 0x00800000 bit denotes the sign.
        # Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
        if compact & 0x00800000:
            compact >>= 8
            size += 1
        assert (compact & ~0x007fffff) == 0
        assert size < 256
        compact |= size << 24
        if negative and compact & 0x007fffff:
            compact |= 0x00800000
        return compact

    def __mul__(self, x):
        # Take the mod because we are limited to an unsigned 256 bit number
        return ArithUint256((self._value * x) % 2 ** 256)

    def __truediv__(self, x):
        return ArithUint256(int(self._value / x))

    def __gt__(self, other):
        return self._value > other

    def __lt__(self, other):
        return self._value < other