import math
from sympy import primerange
import hashlib

# -------------------------------
# Constants
# -------------------------------
PHI = 1.6180339887
INV_PHI = 1.0 / PHI
SQRT_PHI = math.sqrt(PHI)
PI = math.pi

# -------------------------------
# Fibonacci & Primes
# -------------------------------
def fibonacci(n):
    f = [0, 1]
    for i in range(2, n+1):
        f.append(f[-1] + f[-2])
    return f

def primes(n):
    return list(primerange(2, n+1))

FIBS = fibonacci(40)
PRIMES = primes(40)

# -------------------------------
# Deterministic Slot Compute
# -------------------------------
def compute_D(n, r=1, k=1, omega=None):
    F_n = FIBS[n] if n < len(FIBS) else fibonacci(n)[-1]
    P_n = PRIMES[n-1] if n-1 < len(PRIMES) else list(primerange(2, n*10))[-1]
    if omega is None:
        omega = 1 / (PHI ** n) ** 7
    return math.sqrt(PHI * F_n * (2 ** n) * P_n * omega) * (r ** k)

def discretize(value, threshold=SQRT_PHI):
    return 1 if value >= threshold else 0

# -------------------------------
# Opcode Table
# -------------------------------
OPCODES = {
    "PI": "PI_OPERATOR",
    "PHI+": "PHI_PHI_ADD",
    "FIB*": "FIB_MULT",
    "PRIME*": "PRIME_MULT",
    "DYADIC*": "DYADIC_MULT",
    "SIN": "SIN_WAVE",
    "COS": "COS_WAVE",
    "EXP": "EXP_OPERATOR",
    "LOG": "LOG_OPERATOR",
    "SQRT": "SQRT_OPERATOR",
    "POW": "POW_OPERATOR",
    "MOD": "MOD_OPERATOR",
    "AND": "AND_GATE",
    "OR": "OR_GATE",
    "XOR": "XOR_GATE",
    "NOT": "NOT_GATE",
    "SHA256": "SHA256_HASH",
}

# -------------------------------
# BTC-style HDGL VM
# -------------------------------
class BTC_HDGL_VM:
    def __init__(self, slots=32, r=1, k=1):
        self.stack = []
        self.slots = {}
        self.bits = {}
        self.slots_count = slots
        self.r = r
        self.k = k

    def provision_lattice(self):
        """Initialize slots like UTXOs"""
        for n in range(1, self.slots_count + 1):
            val = compute_D(n, self.r, self.k)
            self.slots[n] = val
            self.bits[n] = discretize(val)

    def execute_stream(self, program):
        for cmd in program:
            if isinstance(cmd, (int, float)):
                self.stack.append(cmd)
            elif cmd in OPCODES:
                self._dispatch(OPCODES[cmd])
            else:
                raise ValueError(f"Unknown opcode: {cmd}")

    def _dispatch(self, opcode):
        # Stack-based BTC-like operations
        if opcode == "PI_OPERATOR":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(math.sin(a) * b)

        elif opcode == "PHI_PHI_ADD":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a + b * PHI)

        elif opcode == "FIB_MULT":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a * FIBS[int(b) % len(FIBS)])

        elif opcode == "PRIME_MULT":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a * PRIMES[int(b) % len(PRIMES)])

        elif opcode == "DYADIC_MULT":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a * (2 ** int(b)))

        elif opcode == "SIN_WAVE":
            if self.stack:
                a = self.stack.pop()
                self.stack.append(math.sin(a))

        elif opcode == "COS_WAVE":
            if self.stack:
                a = self.stack.pop()
                self.stack.append(math.cos(a))

        elif opcode == "EXP_OPERATOR":
            if self.stack:
                a = self.stack.pop()
                self.stack.append(math.exp(a))

        elif opcode == "LOG_OPERATOR":
            if self.stack:
                a = self.stack.pop()
                self.stack.append(math.log(abs(a) + 1))

        elif opcode == "SQRT_OPERATOR":
            if self.stack:
                a = self.stack.pop()
                self.stack.append(math.sqrt(abs(a)))

        elif opcode == "POW_OPERATOR":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a ** b)

        elif opcode == "MOD_OPERATOR":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a % int(b))

        elif opcode == "AND_GATE":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(int(bool(a) and bool(b)))

        elif opcode == "OR_GATE":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(int(bool(a) or bool(b)))

        elif opcode == "XOR_GATE":
            if len(self.stack) >= 2:
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(int(bool(a) ^ bool(b)))

        elif opcode == "NOT_GATE":
            if self.stack:
                a = self.stack.pop()
                self.stack.append(int(not bool(a)))

        elif opcode == "SHA256_HASH":
            """Compute BTC-style deterministic hash of stack and slots"""
            state_bytes = "".join(str(v) for v in self.stack + list(self.slots.values())).encode()
            digest = hashlib.sha256(state_bytes).hexdigest()
            self.stack.append(int(digest, 16) % (2 ** 32))

        # Update slots like BTC UTXOs
        for n, val in self.slots.items():
            self.bits[n] = discretize(val if not math.isinf(val) else 1e9)

    def report(self):
        print("\n[Final Slots State]")
        for n, val in sorted(self.slots.items()):
            print(f"D_{n}(r) = {val} → {self.bits[n]}")
        bitstring = ''.join(str(self.bits[n]) for n in sorted(self.bits.keys(), reverse=True))
        print("Binary:", bitstring)
        print("Hex   :", hex(int(bitstring, 2)) if bitstring else "0x0")
        print("Stack :", self.stack)

# -------------------------------
# Example BTC-style Program
# -------------------------------
if __name__ == "__main__":
    vm = BTC_HDGL_VM()
    vm.provision_lattice()

    program = [
        5, 3,        # push 5, 3
        "POW",       # 5^3
        "MOD",       # mod next value (example)
        "SIN",       # sin(result)
        "SHA256"     # BTC-style hash of stack+slots
    ]

    vm.execute_stream(program)
    vm.report()
