class obsidianmixer:
    def __init__(self, block_size=32):
        self._block_size = block_size
        self._mix_constants = self._generate_constants()
    
    def _generate_constants(self):
        constants = []
        phi = 0x9e3779b97f4a7c15
        for i in range(self._block_size):
            c = (phi * (i + 1)) & 0xffffffffffffffff
            c = self._mix64(c)
            constants.append(c & 0xff)
        return constants
    
    def _mix64(self, x):
        x ^= x >> 33
        x = (x * 0xff51afd7ed558ccd) & 0xffffffffffffffff
        x ^= x >> 33
        x = (x * 0xc4ceb9fe1a85ec53) & 0xffffffffffffffff
        x ^= x >> 33
        return x
    
    def _rotate_left(self, val, shift, bits=8):
        shift %= bits
        return ((val << shift) | (val >> (bits - shift))) & ((1 << bits) - 1)
    
    def _rotate_right(self, val, shift, bits=8):
        shift %= bits
        return ((val >> shift) | (val << (bits - shift))) & ((1 << bits) - 1)
    
    def mix_forward(self, data, round_key):
        if len(data) != self._block_size:
            raise ValueError(f"data must be {self._block_size} bytes")
        
        result = bytearray(data)
        
        for i in range(self._block_size):
            result[i] ^= self._mix_constants[i]
        
        for i in range(0, self._block_size - 1, 2):
            a, b = result[i], result[i + 1]
            a = self._rotate_left(a, 3)
            b = self._rotate_right(b, 5)
            a ^= b
            b = (b + a) & 0xff
            result[i], result[i + 1] = a, b
        
        for i in range(self._block_size):
            rk = round_key[i % len(round_key)]
            result[i] = (result[i] + rk) & 0xff
            result[i] = self._rotate_left(result[i], rk & 0x07)
        
        for i in range(self._block_size - 1):
            result[i + 1] ^= result[i]
        
        return bytes(result)
    
    def mix_inverse(self, data, round_key):
        if len(data) != self._block_size:
            raise ValueError(f"data must be {self._block_size} bytes")
        
        result = bytearray(data)
        
        for i in range(self._block_size - 2, -1, -1):
            result[i + 1] ^= result[i]
        
        for i in range(self._block_size - 1, -1, -1):
            rk = round_key[i % len(round_key)]
            result[i] = self._rotate_right(result[i], rk & 0x07)
            result[i] = (result[i] - rk) & 0xff
        
        for i in range(self._block_size - 2, -1, -2):
            a, b = result[i], result[i + 1]
            b = (b - a) & 0xff
            a ^= b
            b = self._rotate_left(b, 5)
            a = self._rotate_right(a, 3)
            result[i], result[i + 1] = a, b
        
        for i in range(self._block_size):
            result[i] ^= self._mix_constants[i]
        
        return bytes(result)
