from decimal import Decimal
import numpy as np
import hashlib

class PhiCompressionTuner:
    def __init__(self, phi=1.6180339887498948, max_value=256, saturate_slot=9223372036854775):
        self.PHI = Decimal(str(phi))
        self.MAX_VALUE = max_value
        self.SATURATE_SLOT = Decimal(str(saturate_slot))

    # -------------------------------
    # Synthetic tuner mapping: frequency → nodal parameters
    # Replace with learned model for production
    # -------------------------------
    def predict_params(self, f):
        alpha = 3.0 + 0.01*f
        beta  = 6.0 + 0.02*f
        eta   = 2.0 + 0.005*f
        zeta  = 3.0 + 0.007*f
        return dict(alpha=alpha, beta=beta, eta=eta, zeta=zeta)

    # -------------------------------
    # Nodal hash computation
    # -------------------------------
    def nodal_hash(self, alpha, beta, eta, zeta, Nx=32, Ny=32):
        x = np.linspace(0, 1, Nx)
        y = np.linspace(0, 1, Ny)
        Xg, Yg = np.meshgrid(x, y)
        Z = np.sin(alpha*np.pi*Xg) * np.sin(beta*np.pi*Yg) + eta*np.cos(zeta*np.pi*(Xg+Yg))
        flat = Z.flatten()
        flat_bytes = (np.round(flat*1e5)).astype(np.int64).tobytes()
        h = hashlib.sha256(flat_bytes).hexdigest()
        h_num = int(h[:16], 16)
        return Decimal(h_num)

    # -------------------------------
    # Encoding
    # -------------------------------
    def encode(self, sequence):
        seq_phi = sum(self.PHI**Decimal(x) for x in sequence)
        f_synth = float(seq_phi % 1000) + 100
        params = self.predict_params(f_synth)
        h = self.nodal_hash(**params)
        encoded = (seq_phi + h) % self.SATURATE_SLOT
        return encoded

    # -------------------------------
    # Decoding with tuner-guided gradient descent
    # -------------------------------
    def decode(self, encoded, seq_length, max_iter=2000, lr=0.05):
        guesses = np.full(seq_length, self.MAX_VALUE // 2, dtype=np.float64)
        for _ in range(max_iter):
            seq_phi = sum(self.PHI**Decimal(x) for x in guesses)
            f_synth = float(seq_phi % 1000) + 100
            params = self.predict_params(f_synth)
            h = self.nodal_hash(**params)
            pred = (seq_phi + h) % self.SATURATE_SLOT
            error = float(encoded - pred)
            if abs(error) < 1e-6:
                break
            gradient = np.array([float(self.PHI**Decimal(x)) for x in guesses])
            guesses += lr * error / (gradient + 1e-12)
            guesses = np.clip(guesses, 0, self.MAX_VALUE-1)
        return [int(round(x)) for x in guesses]

# -------------------------------
# Example usage
# -------------------------------
if __name__ == "__main__":
    sequence = [35, 33, 10, 97, 98]
    compressor = PhiCompressionTuner()
    encoded = compressor.encode(sequence)
    decoded = compressor.decode(encoded, len(sequence))
    print("Original sequence:", sequence)
    print("Encoded value:   ", encoded)
    print("Decoded sequence:", decoded)