# hdgl_16M3_lattice.py
import math
import random
import hashlib

# -------------------------------
# Constants
# -------------------------------
phi = 1.6180339887
sqrt_phi = math.sqrt(phi)
MAX_VECTORS = 16_777_216  # 2^24

FIBS = [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610]
PRIMES = [2,3,5,7,11,13,17,19,23,29,31]

# -------------------------------
# Vector index & HDGL state
# -------------------------------
def vector_index(primary:int, secondary:int, tertiary:int, block_hash:str):
    hash_int = int(block_hash[:16], 16)
    return (primary + secondary*MAX_VECTORS + tertiary*MAX_VECTORS**2 + hash_int) % (MAX_VECTORS**3)

def vector_to_hdgl_state(primary:int, secondary:int, tertiary:int, block_hash:str):
    sub_index = vector_index(primary, secondary, tertiary, block_hash)
    inst_index = primary % 8
    fib_val = FIBS[(primary + secondary + tertiary) % len(FIBS)]
    phi_scale = phi ** ((primary % 16)/16)
    r_dim = 0.3 + 0.7*((fib_val*phi_scale) % 1)
    slot_scale = [(FIBS[(primary+i+secondary+tertiary)%len(FIBS)] / PRIMES[i%len(PRIMES)])*phi_scale for i in range(4)]
    
    seed = (primary << 48) | (secondary << 24) | tertiary
    random.seed(seed)
    program = [
        {'op':'LOAD','args':[0, random.uniform(0,2)]},
        {'op':'ADD','args':[2,0,1]},
        {'op':'JMP','args':[2,0]},
        {'op':'NOP'}
    ]
    return inst_index, program, r_dim, slot_scale

# -------------------------------
# Minimal-memory vector execution
# -------------------------------
def execute_vector(primary:int, secondary:int, tertiary:int, block_hash:str, ticks:int=1):
    inst_index, program, r_dim, slot_scale = vector_to_hdgl_state(primary, secondary, tertiary, block_hash)
    D = [0.0]*4
    binary = [0]*4
    for tick in range(ticks):
        for instr in program:
            op = instr['op']
            args = instr.get('args', [])
            if op=='LOAD':
                D[args[0]] = args[1]
            elif op=='ADD':
                D[args[0]] = D[args[1]] + D[args[2]]
            elif op=='JMP' and D[args[0]] >= sqrt_phi:
                pass  # minimal jump logic
        for i in range(4):
            D[i] += 0.05*r_dim*slot_scale[i]
        binary = [1 if d >= sqrt_phi else 0 for d in D]
    return inst_index, D, binary

# -------------------------------
# Vector lattice executor
# -------------------------------
class VectorLatticeExecutor:
    def __init__(self, block_hash:str):
        self.block_hash = block_hash
        self.active_vectors = []  # list of (primary,secondary,tertiary)
    
    def add_vector(self, primary:int, secondary:int, tertiary:int):
        self.active_vectors.append((primary,secondary,tertiary))
    
    def step(self, ticks:int=1):
        lattice_bin = []
        for p,s,t in self.active_vectors:
            _, _, binary = execute_vector(p,s,t,self.block_hash,ticks)
            lattice_bin.extend(binary)
        lattice_str = ''.join(str(b) for b in reversed(lattice_bin))
        lattice_hex = hex(int(lattice_str,2))
        return lattice_str, lattice_hex

# -------------------------------
# Example usage
# -------------------------------
if __name__=="__main__":
    block_hash = hashlib.sha256(b"example_block").hexdigest()
    lattice_executor = VectorLatticeExecutor(block_hash)
    
    # Add multiple vectors (on-demand, minimal memory)
    lattice_executor.add_vector(42,17,99)
    lattice_executor.add_vector(1234,5678,9012)
    lattice_executor.add_vector(999,888,777)
    
    lattice_str, lattice_hex = lattice_executor.step(ticks=5)
    
    print(f"Lattice binary: {lattice_str}")
    print(f"Lattice hex: {lattice_hex}")
