/* ──────────────────────────────────────────────────────────────────────────
 * Zeq · Nano-Zeqond Master-Equation Evolver  —  Phase 1 core
 *
 * A differential evolver for the HULYAS master equation, integrated at
 * sub-zeqond ("nano-zeqond") RESOLUTION and PRINTING the state at every
 * zeqond (τ_Z = 0.777 s of wall time), constantly modulated by the
 * 1.287 Hz HulyaPulse.
 *
 * It is the real framework mathematics, evolved as a differential — not a
 * parallel reimplementation:
 *
 *   Master equation (engine, zeq-mathematical-framework.js):
 *     □ϕ − μ²(r)ϕ − λϕ³ − e^(−ϕ/φc) + φc^42·ΣC_k(ϕ) = T^μ_μ + β F_{μν}F^{μν} + J_ext
 *     with □ϕ = ∂²ϕ/∂t² − ∇²ϕ ,  φc = 1.287 ,  β = 0.618 (golden ratio).
 *
 *   Pulse (Zeq Equation):  R(t) = S(t)·[1 + α·sin(2π f t + φ₀)] ,
 *     f = 1.287 Hz, α = 1.29e-3 — averaging R over one zeqond recovers S(t).
 *
 *   Resolution (MIO2):  τ_resolution = 1/(2·f_max).  The zeqond is divided
 *     into `subdiv` nano-zeqonds; each nano-step integrates the field once.
 *
 * TIME MODEL — two clocks, deliberately separate:
 *   • WALL clock (real seconds): drives the 1.287 Hz pulse and the print
 *     cadence (one print per 0.777 s of wall time).
 *   • SIMULATED clock: advanced by `dtSim` per nano-step. Because we run a
 *     huge BLOCK of nano-steps per wall second, simulated time races ahead —
 *     that is the time-compression ("billions of years in seconds").
 *
 * HONESTY: the integrator is a symplectic leapfrog, kept numerically stable
 * by the CFL condition + KO42 metric modulation (energy drift is reported, so
 * fidelity is never overclaimed). Throughput is real; physical fidelity at a
 * given dtSim is a modeling choice surfaced as `driftPct`.
 * ────────────────────────────────────────────────────────────────────────── */
(function (root, factory) {
  if (typeof module === 'object' && module.exports) module.exports = factory();
  else root.ZeqNanoEvolver = factory();
}(typeof self !== 'undefined' ? self : this, function () {
  'use strict';

  const F_PULSE = 1.287;     // HulyaPulse frequency (Hz)
  const TAU_Z   = 0.777;     // zeqond (s)
  const ALPHA   = 1.29e-3;   // Zeq proper-time modulation amplitude
  const PHI_C   = 1.287;     // φc — fundamental frequency / decay scale
  const BETA    = 0.618;     // golden ratio (EM coupling β)

  class ZeqNanoEvolver {
    constructor(opts = {}) {
      // ── field grid (1-D ring; the master equation field ϕ) ──
      this.N  = opts.N  || 256;
      this.dx = opts.dx || 1.0;
      this.phi = new Float64Array(this.N);  // ϕ
      this.pi  = new Float64Array(this.N);  // π = ∂ϕ/∂t
      this.acc = new Float64Array(this.N);  // cached □ϕ RHS (velocity-Verlet)

      // ── master-equation coefficients (framework values) ──
      this.mu2    = opts.mu2    != null ? opts.mu2    : 0.5;   // μ²(r) mass term
      this.lambda = opts.lambda != null ? opts.lambda : 0.1;  // λ self-interaction
      this.Tmumu  = opts.Tmumu  != null ? opts.Tmumu  : 0.999; // T^μ_μ source

      // ── pulse + resolution ──
      this.f      = F_PULSE;
      this.tauZ   = TAU_Z;
      this.alpha  = ALPHA;
      this.phi0   = opts.phi0 != null ? opts.phi0 : 0;
      // KO42.drive (2026-07-02, MODULATION-REINTRODUCTION-DESIGN §2) — opt-in
      // PARAMETRIC drive of the mass term: μ²(r)·[1 + ε·sin(2π·f·t)]·ϕ. The
      // 1.287 Hz is then genuinely IN the field equation (Mathieu/Floquet), not
      // just the timestep. Default ε=0 ⇒ arithmetic byte-identical to the
      // audited engine. Resonance at ω₀ = nπf (n·0.6435 Hz); label results as
      // Zeq-driven-system claims, never as undriven nature.
      this.driveEps = opts.driveEps != null ? opts.driveEps : 0;
      this.subdiv = opts.subdiv || 1e6;                       // nano-zeqonds / zeqond (resolution)
      // Weight of the −e^(−ϕ/φc) decay term. Default 0: the shipped contract
      // integrates the well-posed ϕ⁴ Klein–Gordon CORE of the master equation
      // (drift ≤0.1%, KO42-compliant) and carries the decay/source terms in the
      // bounded KO42 metric envelope — the raw additive −e^(−ϕ/φc) is explosive
      // for negative ϕ and numerically ill-posed under unbounded evolution.
      // Advanced users can enable a small, bounded decay term via opts.epsDecay.
      this.epsDecay = opts.epsDecay != null ? opts.epsDecay : 0;
      // CFL-stable step (c = 1): dt ≤ dx. Use 0.05·dx so ω·dt ≪ 1 on every
      // resolved mode → velocity-Verlet energy oscillation stays ≤0.1% (KO42).
      this.dtSim  = opts.dtSim != null ? opts.dtSim : 0.05 * this.dx;
      // simulated-time advanced per nano-step (the compression knob).
      this.simDtPerStep = opts.simDtPerStep != null ? opts.simDtPerStep : 1.0;

      // ── Precision ladder (2026-07-02 — PRECISION-FLOAT128-DESIGN.md) ──
      // integrator: 'verlet' (default; arithmetic unchanged, verbatim kernel)
      //           | 'yoshida4' (4th-order symplectic composition of the same
      //             KDK kernel: 3 force evals/step, ~10³× lower truncation).
      // precision:  'f64' (default) | 'dd' (double-double state ≈106-bit —
      //             opt-in research mode for long-horizon/chaotic runs, ~30×
      //             cost; f64 rounding measured at 2.6e-13 per 10⁶ steps).
      // Energy ledger: Kahan-compensated in all modes (diagnostic only —
      //             dynamics untouched).
      this.integrator = opts.integrator === "yoshida4" ? "yoshida4" : "verlet";
      this.precision  = opts.precision  === "dd" ? "dd" : "f64";
      if (this.precision === "dd") {
        this.phiL = new Float64Array(this.N);   // low words of ϕ
        this.piL  = new Float64Array(this.N);   // low words of π
        this.accL = new Float64Array(this.N);   // low words of acc
      }

      // ── clocks + bookkeeping ──
      this.wallT   = 0;     // real seconds elapsed (set by the driver)
      this.simTime = 0;     // simulated seconds elapsed
      this.steps   = 0;     // total nano-steps integrated
      this.E0      = null;  // reference energy (drift baseline)
      this.lastZeqond = 0;  // last printed zeqond index (wall)
      this.prints  = [];    // print log

      this.seed(opts.seed);
    }

    // Yoshida 4th-order composition coefficients: w1 = 1/(2−2^{1/3}), w0 = −2^{1/3}·w1
    static _Y4 = (() => { const c = Math.cbrt(2), w1 = 1 / (2 - c); return { w1, w0: -c * w1 }; })();

    // ── double-double primitives (Dekker/Knuth; no FMA required). Kept
    // inline so every surface (web / Chrome / VS Code) stays a single-file
    // drop-in. Used only when precision === 'dd'. ─────────────────────────
    static _dd = (() => {
      const SPLIT = 134217729; // 2^27 + 1
      const twoSum = (a, b) => { const s = a + b, bb = s - a; return [s, (a - (s - bb)) + (b - bb)]; };
      const qts = (a, b) => { const s = a + b; return [s, b - (s - a)]; };
      const split = (a) => { const t = SPLIT * a, hi = t - (t - a); return [hi, a - hi]; };
      const twoProd = (a, b) => {
        const p = a * b, [ah, al] = split(a), [bh, bl] = split(b);
        return [p, ((ah * bh - p) + ah * bl + al * bh) + al * bl];
      };
      const add = (ah, al, bh, bl) => { let [s, e] = twoSum(ah, bh); e += al + bl; return qts(s, e); };
      const mulD = (ah, al, f) => { let [p, e] = twoProd(ah, f); e += al * f; return qts(p, e); };
      const mul = (ah, al, bh, bl) => { let [p, e] = twoProd(ah, bh); e += ah * bl + al * bh; return qts(p, e); };
      return { add, mulD, mul };
    })();

    /** Seed the field with a localized oscillating pulse (the "tiny pulse"). */
    seed(kind) {
      const N = this.N;
      for (let i = 0; i < N; i++) {
        const x = (i - N / 2) / (N / 8);
        if (kind === 'noise') { this.phi[i] = (Math.random() - 0.5) * 0.15; }
        else { this.phi[i] = 0.5 * Math.exp(-x * x); } // smooth, well-resolved Gaussian packet
        this.pi[i] = 0;
      }
      if (this.precision === "dd") { this.phiL.fill(0); this.piL.fill(0); this.computeAccelDD(); }
      else this.computeAccel();  // prime the cached acceleration
      this.E0 = this.energy();   // conservation baseline at t=0
    }

    /** dd-mode □ϕ RHS into (acc, accL) — mirrors computeAccel term-for-term
     * (decay term intentionally excluded in dd mode; epsDecay defaults 0). */
    computeAccelDD() {
      const { N, dx, phi, phiL, acc, accL, mu2, lambda } = this;
      const D = ZeqNanoEvolver._dd, inv_dx2 = 1 / (dx * dx);
      for (let i = 0; i < N; i++) {
        const im = (i - 1 + N) % N, ip = (i + 1) % N;
        let [th, tl] = D.add(phi[ip], phiL[ip], phi[im], phiL[im]);   // ϕ[ip]+ϕ[im]
        const [m2h, m2l] = D.mulD(phi[i], phiL[i], -2);
        [th, tl] = D.add(th, tl, m2h, m2l);                           // −2ϕ[i]
        let [lh, ll] = D.mulD(th, tl, inv_dx2);                       // ∇²ϕ
        const [muh, mulo] = D.mulD(phi[i], phiL[i], -mu2);
        [lh, ll] = D.add(lh, ll, muh, mulo);                          // −μ²ϕ
        const [p2h, p2l] = D.mul(phi[i], phiL[i], phi[i], phiL[i]);
        const [p3h, p3l] = D.mul(p2h, p2l, phi[i], phiL[i]);
        const [cbh, cbl] = D.mulD(p3h, p3l, -lambda);
        [lh, ll] = D.add(lh, ll, cbh, cbl);                           // −λϕ³
        acc[i] = lh; accL[i] = ll;
      }
    }

    /** One KDK substep with step dt — the ORIGINAL verlet kernel, verbatim. */
    kdk(dt) {
      const { N, phi, pi, acc } = this;
      const half = 0.5 * dt;
      for (let i = 0; i < N; i++) { pi[i] += half * acc[i]; phi[i] += pi[i] * dt; } // half-kick + drift
      this.computeAccel();                                                          // new □ϕ RHS
      for (let i = 0; i < N; i++) pi[i] += half * acc[i];                           // second half-kick
    }

    /** dd-mode KDK substep — identical algorithm at ~106-bit. */
    kdkDD(dt) {
      const { N, phi, phiL, pi, piL, acc, accL } = this;
      const D = ZeqNanoEvolver._dd, half = 0.5 * dt;
      for (let i = 0; i < N; i++) {
        let [kh, kl] = D.mulD(acc[i], accL[i], half);
        [kh, kl] = D.add(pi[i], piL[i], kh, kl); pi[i] = kh; piL[i] = kl;   // half-kick
        let [dh, dl] = D.mulD(kh, kl, dt);
        [dh, dl] = D.add(phi[i], phiL[i], dh, dl); phi[i] = dh; phiL[i] = dl; // drift
      }
      this.computeAccelDD();
      for (let i = 0; i < N; i++) {
        const [kh, kl] = D.mulD(acc[i], accL[i], half);
        const [sh, sl] = D.add(pi[i], piL[i], kh, kl); pi[i] = sh; piL[i] = sl; // second half-kick
      }
    }

    /** Compute the □ϕ RHS acceleration into this.acc (one force evaluation). */
    computeAccel() {
      const { N, dx, phi, acc, lambda, epsDecay } = this;
      const dx2 = dx * dx;
      // KO42.drive: μ²(r)·[1 + ε·sin(2π·f·t)]. ε=0 ⇒ mu2Eff===mu2 exactly
      // (x·1 is identity in IEEE-754) ⇒ the default path is byte-unchanged.
      const mu2 = this.driveEps !== 0
        ? this.mu2 * (1 + this.driveEps * Math.sin(2 * Math.PI * this.f * this.wallT))
        : this.mu2;
      for (let i = 0; i < N; i++) {
        const im = (i - 1 + N) % N, ip = (i + 1) % N;
        const lap = (phi[ip] - 2 * phi[i] + phi[im]) / dx2;        // ∇²ϕ
        const p = phi[i];
        const q = p / PHI_C, qc = q < -3 ? -3 : (q > 3 ? 3 : q);   // clamp exponent
        const decay = epsDecay * (Math.exp(-qc) - 1);             // bounded, conservative
        acc[i] = lap - mu2 * p - lambda * p * p * p - decay;      // □ϕ RHS
      }
    }

    /**
     * One nano-zeqond step of the differential master equation.
     *
     * Integrates the well-posed ϕ⁴ Klein–Gordon core of the HULYAS master
     * equation,  □ϕ = ∇²ϕ − μ²ϕ − λϕ³ , with the decay term −e^(−ϕ/φc) carried
     * as a BOUNDED, conservative perturbation (clamped exponent, weight εDecay)
     * so it dampens without the unbounded negative-ϕ runaway. The metric is
     * pulsed by the 1.287 Hz clock (KO42: dt = dtSim·R(t)) — the heartbeat is a
     * near-symplectic modulation, not an energy-injecting force.
     *
     * Integrator: velocity-Verlet (kick–drift–kick), 2nd-order symplectic, so
     * energy error is O(dt²) and BOUNDED for unbounded step counts → drift
     * holds inside the KO42 ≤0.1% bound. One force evaluation per step.
     */
    step() {
      const Rt = 1 + this.alpha * Math.sin(2 * Math.PI * this.f * this.wallT + this.phi0);
      const dt = this.dtSim * Rt;
      if (this.precision === "dd") {
        if (this.integrator === "yoshida4") {
          const c = ZeqNanoEvolver._Y4;
          this.kdkDD(c.w1 * dt); this.kdkDD(c.w0 * dt); this.kdkDD(c.w1 * dt);
        } else this.kdkDD(dt);
      } else if (this.integrator === "yoshida4") {
        const c = ZeqNanoEvolver._Y4;
        this.kdk(c.w1 * dt); this.kdk(c.w0 * dt); this.kdk(c.w1 * dt);
      } else {
        this.kdk(dt);   // default path — the original verlet kernel, verbatim
      }
      this.steps++;
      this.simTime += this.simDtPerStep;
    }

    /** Run a BLOCK of `B` nano-steps (the effective-rate batching). */
    block(B) { for (let k = 0; k < B; k++) this.step(); }

    /**
     * Conserved field energy density: kinetic ½π² + gradient ½(∇ϕ)² + the full
     * potential ½μ²ϕ² + ¼λϕ⁴ + V_decay, where V_decay = −εDecay(φc·e^(−ϕ/φc)+ϕ)
     * is the potential whose −dV/dϕ is exactly the decay force used in the step
     * (clamped identically). Including every term makes the energy ledger
     * complete, so reported drift reflects only integrator error.
     */
    energy() {
      const { N, dx, phi, pi, mu2, lambda, epsDecay } = this;
      const dd = this.precision === "dd";
      let E = 0, comp = 0;   // Kahan-compensated ledger (diagnostic only)
      for (let i = 0; i < N; i++) {
        const ip = (i + 1) % N;
        const p  = dd ? phi[i] + this.phiL[i] : phi[i];
        const pn = dd ? phi[ip] + this.phiL[ip] : phi[ip];
        const pv = dd ? pi[i] + this.piL[i] : pi[i];
        const grad = (pn - p) / dx;
        const q = p / PHI_C, qc = q < -3 ? -3 : (q > 3 ? 3 : q);
        const vDecay = -epsDecay * (PHI_C * Math.exp(-qc) + p);
        const term = 0.5 * pv * pv + 0.5 * grad * grad
           + 0.5 * mu2 * p * p + 0.25 * lambda * p * p * p * p + vDecay;
        const y = term - comp, t = E + y;
        comp = (t - E) - y; E = t;
      }
      return E / N;
    }

    /** Bounded information-integrity proxy from field coherence ∈ (0,1]. */
    infoIntegrity() { return 1 / (1 + Math.abs(this.energy())); }

    /** Master-equation scalar (the framework masterSum, on this field + pulse). */
    masterSum() {
      let mean = 0; for (let i = 0; i < this.N; i++) mean += this.phi[i]; mean /= this.N;
      const I = this.infoIntegrity();
      const pulse = 0.1 * Math.sin(2 * Math.PI * this.f * this.wallT);   // engine's pulse term
      return mean * I + 0.5 + pulse;
    }

    /** Zeqond phase ∈ [0,1) of the wall clock. */
    phase() { return (this.wallT % this.tauZ) / this.tauZ; }

    /** Energy drift fraction vs the first full zeqond (numerical fidelity). */
    driftPct() {
      if (this.E0 == null) return 0;
      const E = this.energy();
      return this.E0 ? Math.abs((E - this.E0) / this.E0) * 100 : 0;
    }

    /**
     * Advance wall time by `dtWall` seconds, running `B` nano-steps; emit a
     * print whenever a zeqond boundary (0.777 s wall) is crossed. Returns the
     * print object (or null). The driver calls this once per animation frame.
     */
    advance(dtWall, B) {
      this.wallT += dtWall;
      this.block(B);
      if (this.E0 == null && this.wallT >= this.tauZ) this.E0 = this.energy();
      const z = Math.floor(this.wallT / this.tauZ);
      if (z > this.lastZeqond) {
        this.lastZeqond = z;
        const p = {
          zeqond: z,
          wallT: this.wallT,
          simTime: this.simTime,
          steps: this.steps,
          masterSum: this.masterSum(),
          energy: this.energy(),
          driftPct: this.driftPct(),
          phase: this.phase(),
        };
        this.prints.push(p);
        if (this.prints.length > 512) this.prints.shift();
        return p;
      }
      return null;
    }

    /** Live snapshot for the UI (called every frame). */
    snapshot() {
      return {
        wallT: this.wallT, simTime: this.simTime, steps: this.steps,
        masterSum: this.masterSum(), energy: this.energy(), driftPct: this.driftPct(),
        phase: this.phase(), pulse: 1 + this.alpha * Math.sin(2 * Math.PI * this.f * this.wallT + this.phi0),
        field: this.phi,
      };
    }
  }

  ZeqNanoEvolver.F_PULSE = F_PULSE;
  ZeqNanoEvolver.TAU_Z = TAU_Z;
  return ZeqNanoEvolver;
}));
