import math
import random
import matplotlib.pyplot as plt
import matplotlib.animation as animation

phi = (1 + math.sqrt(5)) / 2

# --- HDGL Core ---
class HDGLCore:
    def __init__(self, core_id, n_base):
        self.id = core_id
        self.registers = [0, 0, 0, 0]  # D₁–D₄
        self.r_dim = 0.3 + 0.1 * core_id  # recursion bias
        self.Ω = 1 / (phi ** (n_base * 7))
        self.status = "IDLE"
        self.pc = 0
        self.program = []

    def exec_instr(self, instr, lattice):
        op, *args = instr
        if op == "NOP":
            pass
        elif op == "LOAD":
            slot, val = args
            self.registers[slot % 4] = val
        elif op == "ADD":
            a, b, dst = args
            self.registers[dst % 4] = (self.registers[a % 4] + self.registers[b % 4]) % 2
        elif op == "JMP":
            cond, target = args
            if self.registers[cond % 4] == 1:
                self.pc = target % len(self.program)
                return
        elif op == "RES":
            dst, slot = args
            val = self.registers[slot % 4]
            lattice.send_resonance(self.id, dst, val)
        elif op == "HLT":
            self.status = "HALT"
        self.pc = (self.pc + 1) % len(self.program)

    def step(self, lattice):
        if self.status != "HALT" and self.program:
            instr = self.program[self.pc]
            self.exec_instr(instr, lattice)


# --- Lattice Kernel ---
class HDGLLattice:
    def __init__(self, n_base=1):
        self.cores = [HDGLCore(i, n_base) for i in range(8)]
        self.shared_bus = {}

    def load_programs(self, programs):
        for i, prog in enumerate(programs):
            self.cores[i].program = prog

    def send_resonance(self, src, dst, value):
        self.shared_bus[(src, dst)] = value
        self.cores[dst].registers[3] = value

    def aggregate_state(self):
        bits = []
        for core in self.cores:
            for val in core.registers:
                bits.append(val)
        return bits

    def step(self):
        for core in self.cores:
            if core.r_dim < 0.5 or random.random() < core.r_dim:
                core.step(self)


# --- Setup Programs ---
programs = [
    [["LOAD", 0, 1], ["HLT"]],
    [["LOAD", 1, 1], ["HLT"]],
    [["LOAD", 2, 1], ["HLT"]],
    [["LOAD", 3, 1], ["HLT"]],
    [["LOAD", 0, 1], ["RES", 0, 0], ["HLT"]],
    [["LOAD", 1, 1], ["RES", 1, 1], ["HLT"]],
    [["LOAD", 2, 1], ["RES", 2, 2], ["HLT"]],
    [["LOAD", 3, 1], ["RES", 3, 3], ["HLT"]],
]

lattice = HDGLLattice(n_base=1)
lattice.load_programs(programs)

# --- Visualization ---
fig, ax = plt.subplots(figsize=(12, 3))
im = ax.imshow([lattice.aggregate_state()], cmap="binary", aspect="auto", vmin=0, vmax=1)
ax.set_title("HDGL 8-Core Lattice (32-bit)")
ax.set_xlabel("Registers D₁–D₄ × 8 cores")
ax.set_ylabel("Time steps")

history = []

def update(frame):
    lattice.step()
    bits = lattice.aggregate_state()
    history.append(bits)
    im.set_data(history)
    ax.set_ylim(len(history), 0)  # scroll downward
    return [im]

ani = animation.FuncAnimation(fig, update, frames=100, interval=300, blit=True)
plt.show()
