import os
import time
import hashlib
import struct
import platform
import sys


class quantumstate:
    phi = 0x9e3779b97f4a7c15
    sqrt2 = 0x6a09e667bb67ae85
    sqrt3 = 0xb7e151628aed2a6a
    pi = 0x243f6a8885a308d3
    e = 0x2b7e151628aed2a6
    
    def __init__(self):
        self.state = [
            self.phi, self.sqrt2, self.sqrt3, self.pi,
            self.e, 0xc4ceb9fe1a85ec53, 0xff51afd7ed558ccd, 0x62a9d9ed799705f5
        ]
    
    def _rotl(self, x, k):
        k &= 63
        return ((x << k) | (x >> (64 - k))) & 0xffffffffffffffff
    
    def _rotr(self, x, k):
        k &= 63
        return ((x >> k) | (x << (64 - k))) & 0xffffffffffffffff
    
    def absorb(self, data):
        if isinstance(data, str):
            data = data.encode()
        for i, b in enumerate(data):
            self.state[i % 8] ^= b << ((i % 8) * 8)
            self.state[i % 8] &= 0xffffffffffffffff
            self.permute()
    
    def permute(self):
        for r in range(24):
            t = 0
            for i in range(8):
                t ^= self.state[i]
            
            for i in range(8):
                self.state[i] ^= self._rotl(t, 7 + i)
                self.state[i] = (self.state[i] + self.state[(i + 1) % 8]) & 0xffffffffffffffff
                self.state[i] = self._rotl(self.state[i], 17 + r)
                self.state[i] = (self.state[i] * self.phi) & 0xffffffffffffffff
                self.state[i] ^= self._rotr(self.state[i], 29)
            
            theta = 0
            for i in range(8):
                theta ^= self._rotl(self.state[i], i * 8)
            for i in range(8):
                self.state[i] ^= theta
            
            for i in range(4):
                a = self.state[i * 2]
                b = self.state[i * 2 + 1]
                self.state[i * 2] = a ^ ((~b) & self.state[(i * 2 + 2) % 8])
                self.state[i * 2 + 1] = b ^ ((~self.state[(i * 2 + 2) % 8]) & self.state[(i * 2 + 3) % 8])
                self.state[i * 2] &= 0xffffffffffffffff
                self.state[i * 2 + 1] &= 0xffffffffffffffff
            
            self.state[0] ^= r ^ (r << 8) ^ (r << 16)
            self.state[0] &= 0xffffffffffffffff
    
    def squeeze(self, length):
        output = bytearray(length)
        for i in range(length):
            if i % 64 == 0 and i > 0:
                self.permute()
            output[i] = (self.state[(i // 8) % 8] >> ((i % 8) * 8)) & 0xff
        return bytes(output)
    
    def get_state(self, idx):
        return self.state[idx % 8]


class chaosengine:
    def __init__(self, seed):
        self.x = ((seed & 0xffff) / 65535.0) * 20.0 - 10.0
        self.y = (((seed >> 16) & 0xffff) / 65535.0) * 20.0 - 10.0
        self.z = (((seed >> 32) & 0xffff) / 65535.0) * 30.0 + 10.0
        self.sigma = 10.0
        self.rho = 28.0
        self.beta = 8.0 / 3.0
        self.dt = 0.001
    
    def step(self):
        dx = self.sigma * (self.y - self.x)
        dy = self.x * (self.rho - self.z) - self.y
        dz = self.x * self.y - self.beta * self.z
        
        self.x += dx * self.dt
        self.y += dy * self.dt
        self.z += dz * self.dt
    
    def generate(self):
        result = 0
        for i in range(64):
            self.step()
            bit = int((self.x + self.y + self.z) * 1000000.0) & 1
            result |= bit << i
        return result
    
    def generate_byte(self):
        result = 0
        for i in range(8):
            for _ in range(10):
                self.step()
            bit = int((self.x * self.y * self.z) * 1000000.0) & 1
            result |= bit << i
        return result


class nonlinearfeedback:
    def __init__(self, seed1, seed2, seed3):
        self.lfsr1 = seed1 | 1
        self.lfsr2 = seed2 | 1
        self.lfsr3 = seed3 | 1
        self.nlfsr = seed1 ^ seed2 ^ seed3
    
    def clock(self):
        bit1 = ((self.lfsr1 >> 0) ^ (self.lfsr1 >> 2) ^ (self.lfsr1 >> 3) ^ (self.lfsr1 >> 5)) & 1
        self.lfsr1 = ((self.lfsr1 >> 1) | (bit1 << 63)) & 0xffffffffffffffff
        
        bit2 = ((self.lfsr2 >> 0) ^ (self.lfsr2 >> 1) ^ (self.lfsr2 >> 2) ^ (self.lfsr2 >> 7)) & 1
        self.lfsr2 = ((self.lfsr2 >> 1) | (bit2 << 63)) & 0xffffffffffffffff
        
        bit3 = ((self.lfsr3 >> 0) ^ (self.lfsr3 >> 1) ^ (self.lfsr3 >> 5) ^ (self.lfsr3 >> 6)) & 1
        self.lfsr3 = ((self.lfsr3 >> 1) | (bit3 << 63)) & 0xffffffffffffffff
        
        maj = (self.lfsr1 & self.lfsr2) ^ (self.lfsr2 & self.lfsr3) ^ (self.lfsr1 & self.lfsr3)
        
        self.nlfsr ^= (self.lfsr1 & self.lfsr2 & self.lfsr3) | ((~self.lfsr1) & (self.lfsr2 ^ self.lfsr3))
        self.nlfsr = (((self.nlfsr << 1) | (self.nlfsr >> 63)) ^ maj) & 0xffffffffffffffff
        
        return (self.nlfsr ^ self.lfsr1 ^ self.lfsr2 ^ self.lfsr3) & 0xffffffffffffffff


class dynamicsbox:
    def __init__(self, key):
        self.forward = list(range(256))
        self.inverse = list(range(256))
        self._generate(key)
    
    def _generate(self, key):
        qs = quantumstate()
        qs.absorb(key)
        
        ce = chaosengine(qs.get_state(0))
        nf = nonlinearfeedback(qs.get_state(1), qs.get_state(2), qs.get_state(3))
        
        for _ in range(32):
            for i in range(255, 0, -1):
                r1 = ce.generate()
                r2 = nf.clock()
                j = (r1 ^ r2) % (i + 1)
                self.forward[i], self.forward[j] = self.forward[j], self.forward[i]
            qs.permute()
        
        for i in range(256):
            self.inverse[self.forward[i]] = i
    
    def substitute(self, data):
        return bytes([self.forward[b] for b in data])
    
    def inverse_substitute(self, data):
        return bytes([self.inverse[b] for b in data])


class contextboundkey:
    def __init__(self):
        pass
    
    def get_context(self):
        context = bytearray()
        
        context.extend(struct.pack("<Q", int(time.time() * 1000000)))
        
        context.extend(platform.system().encode()[:8].ljust(8, b'\x00'))
        
        context.extend(platform.machine().encode()[:8].ljust(8, b'\x00'))
        
        context.extend(struct.pack("<Q", sys.maxsize))
        
        context.extend(struct.pack("<I", os.getpid()))
        
        context.extend(os.urandom(16))
        
        return bytes(context)
    
    def derive_bound_key(self, master_key, context=None):
        if context is None:
            context = self.get_context()
        
        qs = quantumstate()
        qs.absorb(master_key)
        qs.absorb(context)
        
        ce = chaosengine(qs.get_state(0) ^ qs.get_state(4))
        nf = nonlinearfeedback(qs.get_state(1), qs.get_state(2), qs.get_state(3))
        
        output = bytearray(len(master_key))
        for i in range(len(master_key)):
            qs.permute()
            q_byte = qs.get_state(i % 8) & 0xff
            c_byte = ce.generate_byte()
            n_byte = nf.clock() & 0xff
            output[i] = q_byte ^ c_byte ^ n_byte ^ master_key[i % len(master_key)]
        
        return bytes(output), context


class obsidian3300:
    block_bits = 3296
    block_size = block_bits // 8
    key_size = block_size
    default_rounds = 32
    
    def __init__(self, key=None, context_bound=False):
        if key is None:
            key = os.urandom(self.key_size)
        
        if len(key) < self.key_size:
            key = self._expand_key(key, self.key_size)
        
        self._master_key = key[:self.key_size]
        self._context_bound = context_bound
        self._context = None
        
        if context_bound:
            cb = contextboundkey()
            self._master_key, self._context = cb.derive_bound_key(self._master_key)
        
        self._sbox = dynamicsbox(self._master_key)
        self._iv = os.urandom(self.block_size)
        self._round_keys = self._generate_round_keys()
    
    def _expand_key(self, key, target_len):
        qs = quantumstate()
        qs.absorb(key)
        return qs.squeeze(target_len)
    
    def _generate_round_keys(self):
        round_keys = []
        qs = quantumstate()
        qs.absorb(self._master_key)
        
        for r in range(self.default_rounds):
            qs.permute()
            rk = qs.squeeze(self.key_size)
            
            ce = chaosengine(qs.get_state(r % 8))
            nf = nonlinearfeedback(qs.get_state(0), qs.get_state(1), qs.get_state(2))
            
            mixed = bytearray(self.key_size)
            for i in range(self.key_size):
                mixed[i] = rk[i] ^ ce.generate_byte() ^ (nf.clock() & 0xff)
            
            round_keys.append(bytes(mixed))
        
        return round_keys
    
    def _feistel_round(self, left, right, round_key):
        qs = quantumstate()
        qs.absorb(right)
        qs.absorb(round_key)
        
        f = qs.squeeze(len(left))
        f = self._sbox.substitute(f)
        
        new_left = right
        new_right = bytes([l ^ fv for l, fv in zip(left, f)])
        
        return new_left, new_right
    
    def _inverse_feistel_round(self, left, right, round_key):
        qs = quantumstate()
        qs.absorb(left)
        qs.absorb(round_key)
        
        f = qs.squeeze(len(right))
        f = self._sbox.substitute(f)
        
        new_right = left
        new_left = bytes([r ^ fv for r, fv in zip(right, f)])
        
        return new_left, new_right
    
    def _mixing_layer(self, data, round_key):
        result = bytearray(data)
        
        for i in range(len(result)):
            result[i] ^= round_key[i % len(round_key)]
        
        for i in range(len(result) - 1):
            result[i + 1] ^= result[i]
        
        return bytes(result)
    
    def _inverse_mixing_layer(self, data, round_key):
        result = bytearray(data)
        
        for i in range(len(result) - 2, -1, -1):
            result[i + 1] ^= result[i]
        
        for i in range(len(result)):
            result[i] ^= round_key[i % len(round_key)]
        
        return bytes(result)
    
    def _encrypt_block(self, block):
        half = len(block) // 2
        left = block[:half]
        right = block[half:]
        
        for r in range(self.default_rounds):
            left = self._sbox.substitute(left)
            right = self._sbox.substitute(right)
            
            left, right = self._feistel_round(left, right, self._round_keys[r])
            
            combined = bytes(left) + bytes(right)
            combined = self._mixing_layer(combined, self._round_keys[r])
            left = combined[:half]
            right = combined[half:]
        
        return bytes(left) + bytes(right)
    
    def _decrypt_block(self, block):
        half = len(block) // 2
        left = block[:half]
        right = block[half:]
        
        for r in range(self.default_rounds - 1, -1, -1):
            combined = bytes(left) + bytes(right)
            combined = self._inverse_mixing_layer(combined, self._round_keys[r])
            left = combined[:half]
            right = combined[half:]
            
            left, right = self._inverse_feistel_round(left, right, self._round_keys[r])
            
            left = self._sbox.inverse_substitute(left)
            right = self._sbox.inverse_substitute(right)
        
        return bytes(left) + bytes(right)
    
    def _pad(self, data):
        pad_len = self.block_size - (len(data) % self.block_size)
        if pad_len == 0:
            pad_len = self.block_size
        
        if pad_len < 4:
            pad_len += self.block_size
        
        length_bytes = struct.pack(">I", pad_len)
        pad_byte = (pad_len & 0xff) if pad_len > 4 else 0x80
        padding = bytes([pad_byte] * (pad_len - 4)) + length_bytes
        return data + padding
    
    def _unpad(self, data):
        length_bytes = data[-4:]
        pad_len = struct.unpack(">I", length_bytes)[0]
        return data[:-pad_len]
    
    def _xor_bytes(self, a, b):
        return bytes([x ^ y for x, y in zip(a, b)])
    
    def encrypt(self, plaintext):
        if isinstance(plaintext, str):
            plaintext = plaintext.encode()
        
        padded = self._pad(plaintext)
        blocks = [padded[i:i + self.block_size] for i in range(0, len(padded), self.block_size)]
        
        encrypted = []
        prev = self._iv
        
        for block in blocks:
            xored = self._xor_bytes(block, prev)
            enc = self._encrypt_block(xored)
            encrypted.append(enc)
            prev = enc
        
        return b"".join(encrypted)
    
    def decrypt(self, ciphertext):
        blocks = [ciphertext[i:i + self.block_size] for i in range(0, len(ciphertext), self.block_size)]
        
        decrypted = []
        prev = self._iv
        
        for block in blocks:
            dec = self._decrypt_block(block)
            xored = self._xor_bytes(dec, prev)
            decrypted.append(xored)
            prev = block
        
        padded = b"".join(decrypted)
        return self._unpad(padded)
    
    def encrypt_with_iv(self, plaintext):
        ciphertext = self.encrypt(plaintext)
        return self._iv + ciphertext
    
    def decrypt_with_iv(self, data):
        self._iv = data[:self.block_size]
        ciphertext = data[self.block_size:]
        return self.decrypt(ciphertext)
    
    def get_key(self):
        return self._master_key
    
    def get_iv(self):
        return self._iv
    
    def get_context(self):
        return self._context
