import math
import random

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  # bias grows across cores
        self.Ω = 1 / (phi ** (n_base * 7))  # analog clock tension
        self.status = "IDLE"
        self.pc = 0  # program counter
        self.program = []  # loaded instructions

    # HDGL-ISA instructions
    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":  # resonance send
            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 (8-core multiprocessor) ---
class HDGLLattice:
    def __init__(self, n_base=1):
        self.cores = [HDGLCore(i, n_base) for i in range(8)]
        self.shared_bus = {}  # resonance IPC

    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  # always lands in D₄

    def aggregate_state(self):
        bits = []
        for core in self.cores:
            for val in core.registers:
                bits.append(val)
        # combine to binary string + hex
        bitstring = "".join(str(b) for b in bits)
        hexval = hex(int(bitstring, 2))
        return bitstring, hexval

    def step(self):
        for core in self.cores:
            # scheduler uses recursion bias
            if core.r_dim < 0.5 or random.random() < core.r_dim:
                core.step(self)


# --- Example Usage ---
if __name__ == "__main__":
    # Programs for each core
    programs = [
        [["LOAD", 0, 1], ["HLT"]],                   # Core A: set D₁=1
        [["LOAD", 1, 1], ["HLT"]],                   # Core B: set D₂=1
        [["LOAD", 2, 1], ["HLT"]],                   # Core C: set D₃=1
        [["LOAD", 3, 1], ["HLT"]],                   # Core D: set D₄=1
        [["LOAD", 0, 1], ["RES", 0, 0], ["HLT"]],    # Core E: send to A
        [["LOAD", 1, 1], ["RES", 1, 1], ["HLT"]],    # Core F: send to B
        [["LOAD", 2, 1], ["RES", 2, 2], ["HLT"]],    # Core G: send to C
        [["LOAD", 3, 1], ["RES", 3, 3], ["HLT"]]     # Core H: send to D
    ]

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

    # Run for a few cycles
    for cycle in range(10):
        lattice.step()
        bits, hexval = lattice.aggregate_state()
        print(f"Cycle {cycle}: {bits} | {hexval}")
