const { sha256, merkleRoot, merkleProof } = require('./hash');
const { Rng } = require('./rng');
const { readJson } = require('./allocator');
const { answerBudgetForStep, allowedStructuresForStep } = require('./budget');
const { applyOutputPipeline, applyOutputPipelineToExpressions } = require('./output_pipeline');

// Legacy module name: this file evaluates current Step challenge behavior.
// Public protocol language should use "challenge behavior", not "blackbox".

function mod(value, n) {
  return ((Math.trunc(value) % n) + n) % n;
}

function tableAt(index, values) {
  return values[mod(index, values.length)];
}

function invMod(a, m) {
  const aa = mod(a, m);
  for (let x = 1; x < m; x += 1) {
    if (mod(aa * x, m) === 1) return x;
  }
  return 1;
}

function isPrimeSmall(value) {
  const n = Math.trunc(Math.abs(value));
  if (n < 2) return 0;
  for (let i = 2; i * i <= n; i += 1) {
    if (n % i === 0) return 0;
  }
  return 1;
}

function fibTable(length, seed) {
  const values = [mod(seed || 1, 97), mod((seed || 1) + 1, 97)];
  while (values.length < length) values.push(mod(values[values.length - 1] + values[values.length - 2], 97));
  return values.slice(0, length);
}

function knightLegal(x, y, tx, ty) {
  const dx = Math.abs(x - tx);
  const dy = Math.abs(y - ty);
  return (dx === 1 && dy === 2) || (dx === 2 && dy === 1);
}

function valuesList(values) {
  return values.join(',');
}

function nonZero(rng, min = 1, max = 9) {
  return rng.nonZero(min, max);
}

const GENERIC_BUILT_INS = new Set([
  'x',
  'y',
  'N',
  'if',
  'iff',
  'abs',
  'floor',
  'ceil',
  'round',
  'sqrt',
  'min',
  'max',
  'sin',
  'cos',
  'tan',
  'log',
  'mod',
  'table',
  'PI',
  'E',
]);
const GENERIC_EVALUATOR_CACHE = new Map();

function identifiersIn(text) {
  return Array.from(new Set(String(text || '').match(/[A-Za-z_][A-Za-z0-9_]*/g) || []));
}

function genericVariableNames(step) {
  const template = step.formula_template || {};
  return Array.from(
    new Set([
      ...(Array.isArray(step.variables) ? step.variables : []),
      ...identifiersIn(template.u),
      ...identifiersIn(template.v),
    ]),
  ).filter((name) => !GENERIC_BUILT_INS.has(name));
}

function genericParamValue(name, rng, n) {
  const lower = String(name).toLowerCase();
  const coeff = () => nonZero(rng, 1, 9);
  const bias = () => rng.int(0, n - 1);
  const prime = () => rng.pick([5, 7, 11, 13, 17, 19, 23]);
  const center = () => rng.int(Math.max(0, Math.floor(n * 0.15)), Math.max(1, Math.floor(n * 0.85)));

  if (lower.includes('table')) return Array.from({ length: rng.pick([7, 11, 13]) }, () => rng.int(0, n - 1));
  if (lower.includes('prime_a')) return rng.pick([5, 7, 11]);
  if (lower.includes('prime_b')) return rng.pick([13, 17, 19]);
  if (lower === 'p' || lower === 'q' || lower === 'm' || lower.includes('period') || lower.includes('degree')) return prime();
  if (lower.includes('scale')) return rng.pick([17, 23, 31, 37]);
  if (lower.includes('capacity')) return rng.pick([17, 23, 29, 31]);
  if (lower.includes('width') || lower === 'w' || lower.includes('height') || lower === 'h') return rng.pick([3, 4, 5, 8, 10]);
  if (lower === 'cols') return rng.int(3, 12);
  if (lower.includes('threshold')) return rng.int(2, 10);
  if (lower.includes('half_life')) return rng.int(3, 9);
  if (lower === 'a' || lower === 'b' || lower === 'd' || lower === 'e' || lower === 'g' || lower === 'k') return coeff();
  if (lower.includes('alpha') || lower.includes('beta') || lower.includes('gamma') || lower.includes('delta')) return coeff();
  if (lower.includes('lambda') || lower.includes('rate') || lower.includes('enzyme') || lower.includes('substrate')) return coeff();
  if (lower.includes('viscosity') || lower.includes('charge') || lower.includes('field')) return rng.int(1, 5);
  if (lower.includes('target') || lower === 'cx' || lower === 'cy' || lower === 'ax' || lower === 'ay' || lower === 'bx' || lower === 'by') return center();
  if (lower.includes('last')) return center();
  if (lower.includes('seed') || lower.includes('history') || lower.includes('mix')) return bias();
  if (lower === 'c' || lower === 'f' || lower === 'r' || lower === 's' || lower === 't' || lower.endsWith('0')) return bias();
  if (/^c\d+$|^f\d+$|^r\d+$|^s\d+$/.test(lower)) return bias();
  return coeff();
}

function genericParamsForStep(step, rng, n) {
  return Object.fromEntries(genericVariableNames(step).map((name) => [name, genericParamValue(name, rng, n)]));
}

function toGenericJsExpression(expr, params) {
  let text = String(expr || '').trim();
  if (!text) throw new Error('empty formula template expression');
  text = text.replace(/\bmod\b/gi, '%').replace(/\^/g, '**').replace(/\bif\s*\(/g, 'iff(');
  if (!/^[0-9a-zA-Z_+\-*/%().,<>=!&| \t]*$/.test(text)) {
    throw new Error(`unsupported formula template characters: ${expr}`);
  }
  const allowed = new Set(['x', 'y', 'N', 'iff', 'table', 'abs', 'floor', 'ceil', 'round', 'sqrt', 'min', 'max', 'sin', 'cos', 'tan', 'log', 'PI', 'E']);
  for (const name of Object.keys(params || {})) allowed.add(name);
  for (const id of identifiersIn(text)) {
    if (!allowed.has(id)) throw new Error(`unsupported formula template identifier: ${id}`);
  }
  return text
    .replace(/\babs\b/g, 'Math.abs')
    .replace(/\bfloor\b/g, 'Math.floor')
    .replace(/\bceil\b/g, 'Math.ceil')
    .replace(/\bround\b/g, 'Math.round')
    .replace(/\bsqrt\b/g, 'Math.sqrt')
    .replace(/\bmin\b/g, 'Math.min')
    .replace(/\bmax\b/g, 'Math.max')
    .replace(/\bsin\b/g, 'Math.sin')
    .replace(/\bcos\b/g, 'Math.cos')
    .replace(/\btan\b/g, 'Math.tan')
    .replace(/\blog\b/g, 'Math.log')
    .replace(/\bPI\b/g, 'Math.PI')
    .replace(/\bE\b/g, 'Math.E');
}

function compileGenericExpression(expr, params) {
  const paramNames = Object.keys(params || {}).sort();
  const cacheKey = `${expr}::${paramNames.join(',')}`;
  if (GENERIC_EVALUATOR_CACHE.has(cacheKey)) return GENERIC_EVALUATOR_CACHE.get(cacheKey);
  const js = toGenericJsExpression(expr, params);
  const names = ['x', 'y', 'N', 'iff', 'table', ...paramNames];
  // Formula templates are generated by the local asset builder and are restricted above.
  // eslint-disable-next-line no-new-func
  const fn = new Function(...names, `"use strict"; const value = (${js}); if (!Number.isFinite(Number(value))) throw new Error("non-finite formula result"); return Number(value);`);
  const compiled = { fn, paramNames };
  GENERIC_EVALUATOR_CACHE.set(cacheKey, compiled);
  return compiled;
}

function evaluateGenericExpression(expr, params, x, y, n) {
  const { fn, paramNames } = compileGenericExpression(expr, params);
  const values = [
    x,
    y,
    n,
    (condition, a, b) => (condition ? a : b),
    (index, ...items) => items[mod(index, items.length)],
    ...paramNames.map((name) => params[name]),
  ];
  return mod(Math.round(fn(...values)), n);
}

function evaluateFormulaTemplate(step, params, x, y, n) {
  const template = step.formula_template || {};
  if (typeof template.u !== 'string' || typeof template.v !== 'string') return null;
  return {
    u: evaluateGenericExpression(template.u, params, x, y, n),
    v: evaluateGenericExpression(template.v, params, x, y, n),
  };
}

function materializeTemplateExpression(expr, params) {
  let text = String(expr || '');
  for (const [name, value] of Object.entries(params || {}).sort((a, b) => b[0].length - a[0].length)) {
    const replacement = Array.isArray(value) ? valuesList(value) : String(value);
    text = text.replace(new RegExp(`\\b${name}\\b`, 'g'), replacement);
  }
  return text;
}

function materializeFormulaTemplate(step, params) {
  const template = step.formula_template || {};
  if (typeof template.u !== 'string' || typeof template.v !== 'string') return null;
  return {
    u: materializeTemplateExpression(template.u, params),
    v: materializeTemplateExpression(template.v, params),
  };
}

function makeParams(step, n = 100) {
  const rng = new Rng(step.parameter_seed);
  const p = {};
  const id = step.asset_id;

  const coeff = () => nonZero(rng, 1, 9);
  const bias = () => rng.int(0, n - 1);
  const period = () => rng.int(3, 17);
  const bucket = () => rng.pick([5, 10, 20, 25]);
  const center = () => rng.int(10, n - 11);
  const smallTable = (length, max = 31) => Array.from({ length }, () => rng.int(0, max));
  const branchParams = (prefix) => ({
    [`${prefix}a`]: coeff(),
    [`${prefix}b`]: coeff(),
    [`${prefix}c`]: bias(),
    [`${prefix}d`]: coeff(),
    [`${prefix}e`]: coeff(),
    [`${prefix}f`]: bias(),
  });
  const family = step.family_id;

  if (Array.isArray(step.components) && step.components.length) {
    Object.assign(p, {
      composition_mode: step.combiner_mode || step.formula_template?.composition || 'weighted_sum',
      composition_bias_u: bias(),
      composition_bias_v: bias(),
      composition_gate: bias(),
      component_weights: Object.fromEntries(
        step.components.map((component) => [component.component_id, { u: coeff(), v: coeff() }]),
      ),
      component_weights_alt: Object.fromEntries(
        step.components.map((component) => [component.component_id, { u: coeff(), v: coeff() }]),
      ),
      components: Object.fromEntries(step.components.map((component) => [component.component_id, makeParams(component, n)])),
    });
  } else if (id === 'MXF-S01-MATH-001') Object.assign(p, { c: bias(), f: bias() });
  else if (id === 'MXF-S01-MAP-001') Object.assign(p, { east_shift: bias(), north_shift: bias() });
  else if (id === 'MXF-S02-MATH-001') Object.assign(p, { a: coeff(), b: coeff(), c: bias(), d: coeff(), e: coeff(), f: bias() });
  else if (id === 'MXF-S02-MATH-002') Object.assign(p, { a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S02-PHYS-001') Object.assign(p, { slope: coeff(), refract: coeff(), offset: bias(), offset2: bias() });
  else if (id === 'MXF-S02-MAP-001') Object.assign(p, { scale_x: coeff(), scale_y: coeff(), skew_x: coeff(), skew_y: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S02-ENG-001') Object.assign(p, { ratio_a: coeff(), ratio_b: coeff(), ratio_c: coeff(), ratio_d: coeff(), bias: bias(), bias2: bias() });
  else if (id === 'MXF-S03-VISION-001') Object.assign(p, { c: bias(), f: bias() });
  else if (id === 'MXF-S03-MATH-001') Object.assign(p, { c: bias(), f: bias() });
  else if (id === 'MXF-S03-MATH-002') Object.assign(p, { shear: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S03-PHYS-001') Object.assign(p, { prism_a: coeff(), prism_b: coeff(), prism_c: coeff(), prism_d: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S04-MATH-001') Object.assign(p, { a: coeff(), b: coeff(), c: bias(), d: coeff(), e: coeff(), f: bias() });
  else if (id === 'MXF-S04-MATH-002') Object.assign(p, { a: coeff(), b: coeff(), c: coeff(), d: coeff(), e: coeff(), f: coeff(), seed: bias(), seed2: bias() });
  else if (id === 'MXF-S04-PHYS-001') Object.assign(p, { a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S04-MAP-001') Object.assign(p, { a: coeff(), b: coeff(), c: coeff(), seed: bias(), seed2: bias() });
  else if (id === 'MXF-S05-MATH-001') Object.assign(p, { a: coeff(), b: coeff(), c: bias(), f: bias(), p: period(), q: period() });
  else if (id === 'MXF-S05-MATH-002') Object.assign(p, { a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S05-INFO-001') Object.assign(p, { p: period(), key: rng.int(1, 12), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S05-MUSIC-001') Object.assign(p, { p: period(), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S05-ENG-001') Object.assign(p, { gear_a: coeff(), gear_b: coeff(), teeth: period(), ratio: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S06-MATH-001') Object.assign(p, { a: coeff(), b: coeff(), c: bias(), d: coeff(), e: coeff(), f: bias(), a2: coeff(), b2: coeff(), c2: bias(), d2: coeff(), e2: coeff(), f2: bias() });
  else if (id === 'MXF-S06-MATH-002') Object.assign(p, { threshold: rng.int(40, 140), a: coeff(), b: coeff(), c: bias(), c2: bias(), d: coeff(), e: coeff(), f: bias(), f2: bias() });
  else if (id === 'MXF-S06-GAME-001') Object.assign(p, { w: bucket(), h: bucket(), a: coeff(), b: coeff(), c: bias(), c2: bias(), d: coeff(), e: coeff(), f: bias(), f2: bias() });
  else if (id === 'MXF-S06-ECON-001') Object.assign(p, { p: coeff(), q: coeff(), threshold: rng.int(-100, 500), a: coeff(), b: coeff(), c: bias(), c2: bias(), d: coeff(), e: coeff(), f: bias(), f2: bias() });
  else if (id === 'MXF-S07-MATH-001') Object.assign(p, { cx: center(), cy: center(), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S07-MAP-001') Object.assign(p, { beacon_x: center(), beacon_y: center(), c: bias(), f: bias() });
  else if (id === 'MXF-S07-PHYS-001') Object.assign(p, { cx: center(), cy: center(), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S08-MATH-001') Object.assign(p, { w: bucket(), h: bucket(), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S08-VISION-001') Object.assign(p, { w: bucket(), h: bucket(), cols: Math.ceil(n / bucket()), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (id === 'MXF-S10-PHYS-001') {
    const pPeriod = period();
    const qPeriod = period();
    const length = Math.max(pPeriod, qPeriod);
    Object.assign(p, {
      p: pPeriod,
      q: qPeriod,
      wave_table: Array.from({ length }, () => rng.int(0, 20)),
      c: bias(),
      f: bias(),
    });
  } else if (family === 'math.poly.quadratic_mix_mod') Object.assign(p, { a: coeff(), b: coeff(), c: coeff(), d: coeff(), e: coeff(), f: coeff(), seed: bias(), seed2: bias() });
  else if (family === 'info.permutation.sbox_small') {
    const pSize = rng.pick([7, 11, 13, 17]);
    Object.assign(p, { a: coeff(), b: coeff(), seed: bias(), p: pSize, table: smallTable(pSize, n - 1), offset: rng.int(1, pSize - 1), c: bias(), f: bias() });
  } else if (family === 'time.decay.half_life_step') Object.assign(p, { half_life: rng.int(3, 9), A: rng.int(16, 96), k: coeff(), a: coeff(), b: coeff(), c: bias(), f: bias(), t_seed: bias() });
  else if (family === 'graph.path.shortest_distance') Object.assign(p, { target_x: center(), target_y: center(), graph_seed: bias(), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (family === 'game.board.knight_move_gate') Object.assign(p, { target_x: rng.int(0, n - 1), target_y: rng.int(0, n - 1), a: coeff(), b: coeff(), c: bias(), c2: bias(), f: bias(), f2: bias() });
  else if (family === 'info.checksum.weighted_sum') Object.assign(p, { a: coeff(), b: coeff(), checksum_seed: bias(), p: rng.pick([17, 19, 23]), q: rng.pick([5, 7, 11]), c: bias(), f: bias() });
  else if (family === 'prob.seeded.random_walk_bucket') Object.assign(p, { seed: bias(), width: rng.pick([3, 5, 7]), a: coeff(), b: coeff(), c: bias(), f: bias(), q: rng.pick([5, 7, 11]) });
  else if (family === 'state.click.last_point_memory') Object.assign(p, { last_x: rng.int(0, n - 1), last_y: rng.int(0, n - 1), click_seed: rng.int(1, 31), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (family === 'fsm.phase.three_gate') Object.assign(p, { state_seed: bias(), ...branchParams('p0'), ...branchParams('p1'), ...branchParams('p2') });
  else if (family === 'sequence.recurrence.fibonacci_mod') {
    const periodLength = rng.pick([8, 13, 21]);
    Object.assign(p, { period: periodLength, seq_table: fibTable(periodLength, rng.int(1, 20)), a: coeff(), b: coeff(), c: bias(), f: bias(), t_seed: bias() });
  } else if (family === 'vision.kernel.edge_stencil') Object.assign(p, { kx: coeff(), ky: coeff(), edge_seed: bias(), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (family === 'opt.cost.minimum_choice') Object.assign(p, { ax: center(), ay: center(), bx: center(), by: center(), p: coeff(), q: coeff(), r: coeff(), s: coeff(), c: bias(), c2: bias(), f: bias(), f2: bias() });
  else if (family === 'symbol.grammar.token_bucket') {
    const pSize = rng.pick([7, 11, 13]);
    Object.assign(p, { symbol_table: smallTable(pSize, 24), a: coeff(), b: coeff(), seed: bias(), p: pSize, c: bias(), f: bias() });
  } else if (family === 'bio.growth.logistic_bucket') Object.assign(p, { rate: rng.int(2, 6), capacity: rng.int(12, 28), scale: rng.int(7, 17), a: coeff(), b: coeff(), c: bias(), f: bias(), q: rng.pick([5, 7, 11]), t_seed: bias() });
  else if (family === 'econ.payoff.threshold_gate') Object.assign(p, { p: coeff(), q: coeff(), threshold: rng.int(-100, 500), a: coeff(), b: coeff(), c: bias(), c2: bias(), d: coeff(), e: coeff(), f: bias(), f2: bias() });
  else if (family === 'boss.composite.structure_chain') Object.assign(p, { a: coeff(), b: coeff(), c: bias(), d: coeff(), e: coeff(), f: bias(), p: rng.pick([5, 7, 11]), q: coeff(), r: bias(), s: bias() });
  else if (family === 'chain.history.previous_hash_mix') Object.assign(p, { history_mix: bias(), k: coeff(), a: coeff(), b: coeff(), c: bias(), f: bias() });
  else if (family === 'math.constants.pi_e_phi_scaled') Object.assign(p, { PI_SCALE: 314, E_SCALE: 271, PHI_SCALE: 161, scale: rng.pick([31, 37, 41]), a: coeff(), b: coeff(), c: coeff(), d: coeff(), seed: bias(), seed2: bias(), seed3: bias(), r: bias(), s: bias() });
  else if (family === 'math.number.prime_crt_euler_phi') {
    const primeA = rng.pick([5, 7, 11]);
    const primeB = rng.pick([13, 17, 19]);
    Object.assign(p, { prime_a: primeA, prime_b: primeB, inv_a: invMod(primeB, primeA), inv_b: invMod(primeA, primeB), a: coeff(), b: coeff(), c: bias(), d: coeff(), e: coeff(), f: bias(), phi_offset: rng.int(1, 9), alpha: coeff(), beta: coeff(), gx: rng.int(1, 9), gy: rng.int(1, 9), r: bias(), s: bias() });
  } else if (family === 'math.number.prime_mask_quadratic_residue') {
    const m = 17;
    const qp = 11;
    Object.assign(p, { M: m, p: qp, prime_table: Array.from({ length: m }, (_, i) => isPrimeSmall(i)), qr_table: Array.from({ length: qp }, (_, i) => mod(i * i, qp)), a: coeff(), b: coeff(), c: bias(), seed: bias(), q1: coeff(), q2: coeff(), r: bias(), r2: bias(), s: bias(), s2: bias() });
  } else if (family === 'math.spectral.fourier_phase_integer') {
    const periodLength = rng.pick([8, 12, 16]);
    Object.assign(p, { kx: coeff(), ky: coeff(), seed: bias(), period: periodLength, fourier_table: smallTable(periodLength, 20), h: rng.int(2, 5), offset: rng.int(1, periodLength - 1), a: coeff(), b: coeff(), c: bias(), d: coeff(), e: coeff(), f: bias() });
  } else if (family === 'math.spectral.laplace_decay_kernel') {
    const periodLength = rng.pick([8, 12, 16]);
    Object.assign(p, { cx: center(), cy: center(), A: rng.int(20, 120), lambda: rng.int(1, 5), seed: bias(), period: periodLength, wave_table: smallTable(periodLength, 20), shift: rng.int(1, periodLength - 1), c: bias(), f: bias() });
  } else if (family === 'math.group.symmetry_orbit_action') Object.assign(p, { group_order: 4, a: coeff(), b: coeff(), seed: bias(), p: coeff(), q: coeff(), r: bias(), s: bias() });
  else if (family === 'math.complex.mobius_discrete') Object.assign(p, { a: coeff(), b: coeff(), cx: coeff(), cy: coeff(), d0: rng.int(1, 9), p: bias(), q: bias(), r: bias(), s: bias() });
  else if (family === 'math.tensor.curvature_bucket') Object.assign(p, { a: coeff(), b: coeff(), c: bias(), d: coeff(), e: coeff(), f: bias(), p: coeff(), q: coeff(), r: bias(), M: rng.pick([17, 23, 31]), K: rng.pick([19, 29, 37]), trace_scale: coeff(), alpha: coeff(), beta: coeff(), s: bias(), t: bias() });
  else if (family === 'physics.variational.hamiltonian_symplectic_step') Object.assign(p, { px: coeff(), py: coeff(), a: coeff(), b: coeff(), c: coeff(), seed: bias(), r: bias(), s: bias() });
  else if (family === 'physics.field.maxwell_lattice_flux') Object.assign(p, { a: coeff(), b: coeff(), c: coeff(), d: coeff(), p: coeff(), q: coeff(), r: coeff(), s: coeff(), seedE: bias(), seedE2: bias(), seedB: bias(), seedB2: bias(), M: rng.pick([17, 23, 31]), alpha: coeff(), beta: coeff(), gamma: coeff(), delta: coeff(), c0: bias(), f0: bias() });
  else if (family === 'physics.quantum.schrodinger_lattice_phase') Object.assign(p, { a: coeff(), b: coeff(), c: coeff(), d: coeff(), p: coeff(), q: coeff(), seedR: bias(), seedI: bias(), v0: bias(), M: rng.pick([17, 23, 31]), alpha: coeff(), beta: coeff(), r: bias(), s: bias() });
  else if (family === 'physics.fluid.navier_stokes_lattice_step') Object.assign(p, { a: coeff(), b: coeff(), c: coeff(), d: coeff(), p: rng.pick([5, 7, 11]), q: rng.pick([5, 7, 11]), seedX: bias(), seedY: bias(), M: rng.pick([17, 23, 31]), px: coeff(), py: coeff(), pxy: coeff(), p0: bias(), viscosity: rng.int(1, 4), r: bias(), s: bias() });
  else if (family === 'physics.statmech.boltzmann_entropy_bucket') {
    const periodLength = rng.pick([7, 11, 13]);
    Object.assign(p, { a: coeff(), b: coeff(), seed: bias(), m: rng.pick([7, 11, 13]), threshold: rng.int(2, 6), J: coeff(), field: coeff(), temp_offset: bias(), temp_period: periodLength, boltzmann_table: smallTable(periodLength, 20), alpha: coeff(), beta: coeff(), r: bias(), s: bias() });
  } else if (family === 'physics.gauge.lattice_plaquette_phase') {
    const pSize = rng.pick([7, 11, 13]);
    Object.assign(p, { link_table: smallTable(pSize, pSize - 1), seedX: bias(), seedY: bias(), seedXY: bias(), seedYX: bias(), p: pSize, charge: coeff(), seed: bias(), a: coeff(), b: coeff(), r: bias(), s: bias() });
  } else {
    Object.assign(p, genericParamsForStep(step, rng, n));
    if (Object.keys(p).length === 0) Object.assign(p, { c: bias(), f: bias() });
  }

  return p;
}

function evaluateWeightedComponents(step, params, x, y, n, { alternate = false, cross = false, include = null, bias = true } = {}) {
  let u = bias ? params.composition_bias_u || 0 : 0;
  let v = bias ? params.composition_bias_v || 0 : 0;
  const weightsKey = alternate ? 'component_weights_alt' : 'component_weights';
  const components = include ? step.components.filter(include) : step.components;
  for (const component of components) {
    const output = evaluateBaseStep(component, params.components?.[component.component_id] || {}, x, y, n);
    const weights = params[weightsKey]?.[component.component_id] || { u: 1, v: 1 };
    if (cross) {
      u += weights.u * output.u + weights.v * output.v;
      v += weights.v * output.u - weights.u * output.v;
    } else {
      u += weights.u * output.u;
      v += weights.v * output.v;
    }
  }
  return { u: mod(u, n), v: mod(v, n) };
}

function evaluateCompositeStep(step, params, x, y, n) {
  const mode = params.composition_mode || step.combiner_mode || step.formula_template?.composition || 'weighted_sum';
  if (mode === 'nested_chain') {
    const first = step.components[0];
    const second = step.components[1] || step.components[0];
    const firstOutput = evaluateBaseStep(first, params.components?.[first.component_id] || {}, x, y, n);
    const secondOutput = evaluateBaseStep(second, params.components?.[second.component_id] || {}, firstOutput.u, firstOutput.v, n);
    let u = secondOutput.u + (params.composition_bias_u || 0);
    let v = secondOutput.v + (params.composition_bias_v || 0);
    if (step.components.length > 2) {
      const rest = evaluateWeightedComponents(step, params, x, y, n, {
        include: (_, index) => index >= 2,
        bias: false,
      });
      u += rest.u;
      v += rest.v;
    }
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (mode === 'conditional_gate') {
    const index = mod(x + 2 * y + (params.composition_gate || 0), step.components.length);
    const component = step.components[index];
    const output = evaluateBaseStep(component, params.components?.[component.component_id] || {}, x, y, n);
    return { u: mod(output.u + (params.composition_bias_u || 0), n), v: mod(output.v + (params.composition_bias_v || 0), n) };
  }
  if (mode === 'cross_weighted_mix') {
    return evaluateWeightedComponents(step, params, x, y, n, { cross: true });
  }
  if (mode === 'gated_weighted_sum') {
    const gate = mod(x + y + (params.composition_gate || 0), 2);
    return evaluateWeightedComponents(step, params, x, y, n, {
      alternate: gate === 1,
      include: (_, index) => (gate === 0 ? index % 2 === 0 : index % 2 === 1),
    });
  }
  return evaluateWeightedComponents(step, params, x, y, n);
}

function evaluateBaseStep(step, params, x, y, n = 100) {
  if (Array.isArray(step.components) && step.components.length) {
    return evaluateCompositeStep(step, params, x, y, n);
  }

  const id = step.asset_id;
  const p = params;
  const family = step.family_id;
  let u;
  let v;

  if (family === 'math.poly.quadratic_mix_mod') {
    u = p.a * x * x + p.b * y + p.c * x * y + p.seed;
    v = p.d * y * y + p.e * x + p.f * x * y + p.seed2;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'info.permutation.sbox_small') {
    const idx = mod(p.a * x + p.b * y + p.seed, p.p);
    const s = tableAt(idx, p.table);
    u = x + s + p.c;
    v = y + tableAt(idx + p.offset, p.table) + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'time.decay.half_life_step') {
    const age = mod(x + y + p.t_seed, n);
    const h = Math.floor(age / p.half_life);
    const decay = Math.floor(p.A / 2 ** Math.min(h, 12));
    u = p.a * x + p.b * y + decay + p.c;
    v = y + p.k * h + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'graph.path.shortest_distance') {
    const dist = Math.abs(x - p.target_x) + Math.abs(y - p.target_y);
    const degree = 2 + mod(x + y + p.graph_seed, 4);
    u = x + p.a * dist + p.c;
    v = y + p.b * degree + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'game.board.knight_move_gate') {
    const legal = knightLegal(x, y, p.target_x, p.target_y);
    u = legal ? x + p.a * y + p.c : n - 1 - x + p.c2;
    v = legal ? y + p.b * x + p.f : n - 1 - y + p.f2;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'info.checksum.weighted_sum') {
    const code = mod(p.a * x + p.b * y + p.checksum_seed, p.p);
    const check = mod(code + x + 2 * y, p.q);
    u = x + code + p.c;
    v = y + check + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'prob.seeded.random_walk_bucket') {
    const r = mod(p.seed + p.a * x * 17 + p.b * y * 31 + x * y, 997);
    const bucketValue = Math.floor(r / p.width);
    u = x + p.a * bucketValue + p.c;
    v = y + p.b * mod(bucketValue, p.q) + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'state.click.last_point_memory') {
    const memory = Math.abs(x - p.last_x) + Math.abs(y - p.last_y) + mod(x + y + p.click_seed, 17);
    u = x + p.a * memory + p.c;
    v = y + p.b * memory + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'fsm.phase.three_gate') {
    const gate = mod(x + 2 * y + p.state_seed, 3);
    const branch = gate === 0 ? 'p0' : gate === 1 ? 'p1' : 'p2';
    u = p[`${branch}a`] * x + p[`${branch}b`] * y + p[`${branch}c`];
    v = p[`${branch}d`] * x + p[`${branch}e`] * y + p[`${branch}f`];
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'sequence.recurrence.fibonacci_mod') {
    const idx = mod(x + y + p.t_seed, p.period);
    const s = tableAt(idx, p.seq_table);
    u = x + p.a * s + p.c;
    v = y + p.b * s + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'vision.kernel.edge_stencil') {
    const k = mod(p.kx * (x - y) + p.ky * (x + y) + p.edge_seed, n);
    u = x + p.a * k + p.c;
    v = y + p.b * Math.abs(k) + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'opt.cost.minimum_choice') {
    const costA = Math.abs(x - p.ax) + Math.abs(y - p.ay);
    const costB = Math.abs(x - p.bx) + Math.abs(y - p.by);
    const choiceA = costA <= costB;
    u = choiceA ? x + p.p * costA + p.c : x + p.q * costB + p.c2;
    v = choiceA ? y + p.r * costA + p.f : y + p.s * costB + p.f2;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'symbol.grammar.token_bucket') {
    const idx = mod(p.a * x + p.b * y + p.seed, p.p);
    const token = tableAt(idx, p.symbol_table);
    const score = mod(token * token + 3 * token + 7, n);
    u = x + score + p.c;
    v = y + token + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'bio.growth.logistic_bucket') {
    const t = mod(x + y + p.t_seed, p.capacity);
    const growth = Math.floor((p.rate * t * (p.capacity - t)) / p.scale);
    u = x + p.a * growth + p.c;
    v = y + p.b * mod(growth, p.q) + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'econ.payoff.threshold_gate') {
    const payoff = p.p * x - p.q * y;
    const cond = payoff >= p.threshold;
    u = cond ? x + p.a * y + p.c : x + p.b * y + p.c2;
    v = cond ? y + p.d * x + p.f : y + p.e * x + p.f2;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'boss.composite.structure_chain') {
    const r1u = p.a * x + p.b * y + p.c;
    const r1v = p.d * x + p.e * y + p.f;
    const r2 = mod(r1u + r1v, p.p);
    const cond = mod(r2 + x + y, 2) === 0;
    u = cond ? r1u + p.q * r2 + p.r : n - 1 - mod(r1u, n) + p.r;
    v = cond ? r1v + p.q * r2 + p.s : n - 1 - mod(r1v, n) + p.s;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'chain.history.previous_hash_mix') {
    const r = mod(p.history_mix + Number(step.layer_no || 0) * 31 + Number(step.step_no || 0) * 17, n);
    u = p.a * x + p.b * y + p.c + r;
    v = y + p.k * r + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'math.constants.pi_e_phi_scaled') {
    const piK = Math.floor((p.PI_SCALE * mod(p.a * x + p.b * y + p.seed, p.scale)) / p.scale);
    const eK = Math.floor((p.E_SCALE * mod(p.c * x + p.d * y + p.seed2, p.scale)) / p.scale);
    const phiK = Math.floor((p.PHI_SCALE * mod(x + y + p.seed3, p.scale)) / p.scale);
    u = x + piK + phiK + p.r;
    v = y + eK + phiK + p.s;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'math.number.prime_crt_euler_phi') {
    const r1 = mod(p.a * x + p.b * y + p.c, p.prime_a);
    const r2 = mod(p.d * x + p.e * y + p.f, p.prime_b);
    const modulus = p.prime_a * p.prime_b;
    const z = mod(r1 * p.prime_b * p.inv_a + r2 * p.prime_a * p.inv_b, modulus);
    const phiZ = mod(z + p.phi_offset, n);
    u = z + p.alpha * phiZ + p.r;
    v = z + p.beta * (mod(x + p.gx, 7) + mod(y + p.gy, 5)) + p.s;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'math.number.prime_mask_quadratic_residue') {
    const z = mod(p.a * x + p.b * y + p.c, p.M);
    const primeGate = tableAt(z, p.prime_table) === 1;
    const qr = tableAt(mod(x + p.seed, p.p), p.qr_table);
    u = primeGate ? x + p.q1 * qr + p.r : n - 1 - x + p.r2;
    v = primeGate ? y + p.q2 * z + p.s : n - 1 - y + p.s2;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'math.spectral.fourier_phase_integer') {
    const phase = mod(p.kx * x + p.ky * y + p.seed, p.period);
    const amp = tableAt(phase, p.fourier_table);
    const harm = tableAt(p.h * phase + p.offset, p.fourier_table);
    u = x + p.a * amp + p.b * harm + p.c;
    v = y + p.d * amp - p.e * harm + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'math.spectral.laplace_decay_kernel') {
    const r = Math.abs(x - p.cx) + Math.abs(y - p.cy);
    const kernel = Math.floor(p.A / (1 + p.lambda * r));
    const phase = mod(x + y + p.seed, p.period);
    u = x + kernel + tableAt(phase, p.wave_table) + p.c;
    v = y + p.lambda * kernel + tableAt(phase + p.shift, p.wave_table) + p.f;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'math.group.symmetry_orbit_action') {
    const g = mod(p.a * x + p.b * y + p.seed, p.group_order);
    const actionU = g === 0 ? x : g === 1 ? y : g === 2 ? n - 1 - x : n - 1 - y;
    const actionV = g === 0 ? y : g === 1 ? n - 1 - x : g === 2 ? n - 1 - y : x;
    const orbit = mod(actionU + actionV, p.group_order);
    const stabilizer = g === 0 ? 2 : 1;
    u = actionU + p.p * orbit + p.r;
    v = actionV + p.q * stabilizer + p.s;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'math.complex.mobius_discrete') {
    const den = Math.abs(p.cx * x - p.cy * y + p.d0) + 1;
    const real = Math.floor((p.a * x - p.b * y + p.p) / den);
    const imag = Math.floor((p.b * x + p.a * y + p.q) / den);
    return { u: mod(real + p.r, n), v: mod(imag + p.s, n) };
  }
  if (family === 'math.tensor.curvature_bucket') {
    const g11 = mod(p.a * x + p.b * y + p.c, p.M);
    const g12 = mod(p.d * x + p.e * y + p.f, p.M);
    const g22 = mod(p.p * x + p.q * y + p.r, p.M);
    const detG = g11 * g22 - g12 * g12;
    const curv = mod(Math.abs(detG + p.trace_scale * (g11 + g22)), p.K);
    u = x + p.alpha * curv + p.s;
    v = y + p.beta * detG + p.t;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'physics.variational.hamiltonian_symplectic_step') {
    const dHx = mod(2 * p.a * x + p.c * y, n);
    const dHy = mod(2 * p.b * y + p.c * x, n);
    u = x + dHy + p.r;
    v = y - dHx + p.s;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'physics.field.maxwell_lattice_flux') {
    const ex = mod(p.a * x + p.b * y + p.seedE, p.M);
    const ey = mod(p.c * x - p.d * y + p.seedE2, p.M);
    const bx = mod(p.p * x + p.q * y + p.seedB, p.M);
    const by = mod(p.r * x - p.s * y + p.seedB2, p.M);
    const divE = mod(ex + ey + x - y, p.M);
    const curlB = mod(by - bx + y - x, p.M);
    u = x + p.alpha * divE + p.beta * curlB + p.c0;
    v = y + p.gamma * curlB - p.delta * divE + p.f0;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'physics.quantum.schrodinger_lattice_phase') {
    const psiR = mod(p.a * x + p.b * y + p.seedR, p.M);
    const psiI = mod(p.c * x - p.d * y + p.seedI, p.M);
    const potential = mod(p.p * x * x + p.q * y * y + p.v0, p.M);
    const lap = mod(psiR + psiI + x + y, p.M);
    const measure = mod(psiR * psiR + psiI * psiI + potential + lap, n);
    u = x + p.alpha * measure + p.r;
    v = y + p.beta * mod(measure + potential, n) + p.s;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'physics.fluid.navier_stokes_lattice_step') {
    const vx = mod(p.a * y + p.b * mod(x, p.p) + p.seedX, p.M);
    const vy = mod(p.c * x + p.d * mod(y, p.q) + p.seedY, p.M);
    const pressure = mod(p.px * x * x + p.py * y * y + p.pxy * x * y + p.p0, p.M);
    const advect = mod(vx * mod(vx + x, p.M) + vy * mod(vy + y, p.M), p.M);
    const diffuse = mod(p.viscosity * (vx + vy + x + y), p.M);
    u = x + vx - advect + diffuse - pressure + p.r;
    v = y + vy - advect + diffuse - pressure + p.s;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'physics.statmech.boltzmann_entropy_bucket') {
    const spin = mod(p.a * x + p.b * y + p.seed, p.m) < p.threshold ? 1 : -1;
    const neighborEnergy = spin * (mod(x + y, 3) - 1);
    const energy = p.J * neighborEnergy + p.field * spin;
    const bucketValue = tableAt(energy + p.temp_offset, p.boltzmann_table);
    u = x + p.alpha * bucketValue + p.r;
    v = y + p.beta * Math.abs(energy) + p.s;
    return { u: mod(u, n), v: mod(v, n) };
  }
  if (family === 'physics.gauge.lattice_plaquette_phase') {
    const ux = tableAt(x + p.seedX, p.link_table);
    const uy = tableAt(y + p.seedY, p.link_table);
    const uxY = tableAt(x + y + p.seedXY, p.link_table);
    const uyX = tableAt(y + x + p.seedYX, p.link_table);
    const plaquette = mod(ux + uyX - uxY - uy, p.p);
    const chargePhase = mod(p.charge * plaquette + p.seed + x * 7 + y * 11, p.p);
    u = x + p.a * chargePhase + p.r;
    v = y + p.b * plaquette + p.s;
    return { u: mod(u, n), v: mod(v, n) };
  }

  switch (id) {
    case 'MXF-S01-MATH-001':
      u = x + p.c; v = y + p.f; break;
    case 'MXF-S01-MAP-001':
      u = x + p.east_shift; v = y + p.north_shift; break;
    case 'MXF-S02-MATH-001':
      u = p.a * x + p.b * y + p.c; v = p.d * x + p.e * y + p.f; break;
    case 'MXF-S02-MATH-002':
      u = x + p.a * y + p.c; v = y + p.b * x + p.f; break;
    case 'MXF-S02-PHYS-001':
      u = x + p.slope * y + p.offset; v = y + p.refract * x + p.offset2; break;
    case 'MXF-S02-MAP-001':
      u = p.scale_x * x + p.skew_y * y + p.c; v = p.skew_x * x + p.scale_y * y + p.f; break;
    case 'MXF-S02-ENG-001':
      u = p.ratio_a * x + p.ratio_b * y + p.bias; v = p.ratio_c * x + p.ratio_d * y + p.bias2; break;
    case 'MXF-S03-VISION-001':
      u = n - 1 - x + p.c; v = y + p.f; break;
    case 'MXF-S03-MATH-001':
      u = n - 1 - y + p.c; v = x + p.f; break;
    case 'MXF-S03-MATH-002':
      u = x + p.shear * y + p.c; v = y + p.f; break;
    case 'MXF-S03-PHYS-001':
      u = p.prism_a * x + p.prism_b * y + p.c; v = p.prism_c * x + p.prism_d * y + p.f; break;
    case 'MXF-S04-MATH-001':
      u = p.a * x * x + p.b * y + p.c; v = p.d * y * y + p.e * x + p.f; break;
    case 'MXF-S04-MATH-002':
      u = p.a * x + p.b * y + p.c * x * y + p.seed; v = p.d * y + p.e * x + p.f * x * y + p.seed2; break;
    case 'MXF-S04-PHYS-001': {
      const energy = mod(x * x + y * y, n);
      u = x + p.a * energy + p.c; v = y + p.b * energy + p.f; break;
    }
    case 'MXF-S04-MAP-001': {
      const height = mod(p.a * x * x + p.b * y * y + p.c * x * y, n);
      u = x + height + p.seed; v = y + 2 * height + p.seed2; break;
    }
    case 'MXF-S05-MATH-001':
      u = x + p.a * mod(y, p.p) + p.c; v = y + p.b * mod(x, p.q) + p.f; break;
    case 'MXF-S05-MATH-002': {
      const parity = mod(x + y, 2);
      u = x + p.a * parity + p.c; v = y + p.b * parity + p.f; break;
    }
    case 'MXF-S05-INFO-001': {
      const shift = mod(x + p.key, p.p);
      u = shift + p.a * y + p.c; v = y + p.b * shift + p.f; break;
    }
    case 'MXF-S05-MUSIC-001': {
      const beat = mod(x + 2 * y, p.p);
      u = x + p.a * beat + p.c; v = y + p.b * beat + p.f; break;
    }
    case 'MXF-S05-ENG-001': {
      const gear = mod(p.gear_a * x + p.gear_b * y, p.teeth);
      u = x + gear + p.c; v = y + p.ratio * gear + p.f; break;
    }
    case 'MXF-S06-MATH-001': {
      const cond = mod(x + y, 2) === 0;
      u = cond ? p.a * x + p.b * y + p.c : p.a2 * x + p.b2 * y + p.c2;
      v = cond ? p.d * x + p.e * y + p.f : p.d2 * x + p.e2 * y + p.f2;
      break;
    }
    case 'MXF-S06-MATH-002': {
      const cond = x + y >= p.threshold;
      u = cond ? x + p.a * y + p.c : x + p.b * y + p.c2;
      v = cond ? y + p.d * x + p.f : y + p.e * x + p.f2;
      break;
    }
    case 'MXF-S06-GAME-001': {
      const region = Math.floor(x / p.w) + Math.floor(y / p.h);
      const cond = mod(region, 2) === 0;
      u = cond ? x + p.a * y + p.c : n - 1 - x + p.b * y + p.c2;
      v = cond ? y + p.d * x + p.f : n - 1 - y + p.e * x + p.f2;
      break;
    }
    case 'MXF-S06-ECON-001': {
      const profit = p.p * x - p.q * y;
      const cond = profit >= p.threshold;
      u = cond ? x + p.a * y + p.c : x + p.b * y + p.c2;
      v = cond ? y + p.d * x + p.f : y + p.e * x + p.f2;
      break;
    }
    case 'MXF-S07-MATH-001': {
      const dist = Math.abs(x - p.cx) + Math.abs(y - p.cy);
      u = x + p.a * dist + p.c; v = y + p.b * dist + p.f; break;
    }
    case 'MXF-S07-MAP-001': {
      const range = Math.abs(x - p.beacon_x) + Math.abs(y - p.beacon_y);
      u = x + range + p.c; v = y + 2 * range + p.f; break;
    }
    case 'MXF-S07-PHYS-001': {
      const r2 = (x - p.cx) * (x - p.cx) + (y - p.cy) * (y - p.cy);
      u = x + p.a * r2 + p.c; v = y + p.b * r2 + p.f; break;
    }
    case 'MXF-S08-MATH-001': {
      const bucketX = Math.floor(x / p.w);
      const bucketY = Math.floor(y / p.h);
      u = x + p.a * bucketY + p.c; v = y + p.b * bucketX + p.f; break;
    }
    case 'MXF-S08-VISION-001': {
      const tile = Math.floor(x / p.w) + p.cols * Math.floor(y / p.h);
      u = x + p.a * tile + p.c; v = y + p.b * tile + p.f; break;
    }
    case 'MXF-S10-PHYS-001': {
      const phaseX = mod(x, p.p);
      const phaseY = mod(y, p.q);
      u = x + p.wave_table[phaseX] + p.c; v = y + p.wave_table[phaseY] + p.f; break;
    }
    default: {
      const generic = evaluateFormulaTemplate(step, p, x, y, n);
      if (generic) return generic;
      u = x + (p.c || 0); v = y + (p.f || 0);
      break;
    }
  }

  return { u: mod(u, n), v: mod(v, n) };
}

function evaluateStep(step, params, x, y, n = 100, context = {}) {
  const baseOutput = evaluateBaseStep(step, params, x, y, n);
  return applyOutputPipeline({
    output: baseOutput,
    step,
    x,
    y,
    N: n,
    context: {
      layerNo: step.layer_no,
      stepNo: step.step_no,
      ...context,
    },
  });
}

function formulaHash(step, params) {
  return sha256({
    asset_id: step.asset_id,
    components: step.components || [],
    formula_template: step.formula_template,
    output_pipeline: step.output_pipeline || null,
    parameters: params,
    formula_seed: step.formula_seed,
  });
}

function verificationHash(step) {
  const rules = readJson('MazeX_validator_rules.json');
  return sha256({
    verification_seed: step.verification_seed,
    verification_policy_version: rules.version,
    verification_mode: rules.verification_mode,
    step_verification_mode: step.verification_mode,
    verification_point_count: step.verification_point_count,
    verification_strength: step.verification_strength,
    difficulty_model_version: step.difficulty_model_version,
    difficulty_score: step.difficulty_score,
    work_score: step.work_score,
    solver_complexity_score: step.solver_complexity_score,
    layer_pressure: step.layer_pressure,
    behavior_policy: rules.behavior_policy,
    answer_budget: answerBudgetForStep(step, rules),
    allowed_structures: allowedStructuresForStep(step),
  });
}

function buildCommitments(steps, paramsByStep) {
  const publicSteps = steps.map((step) => {
    const params = paramsByStep[String(step.step_no)];
    const payload = {
      layer_no: step.layer_no,
      step_no: step.step_no,
      step_count: step.step_count,
      progress_band: step.progress_band,
      target_difficulty: step.target_difficulty,
      readable_difficulty: step.readable_difficulty,
      difficulty_model_version: step.difficulty_model_version,
      difficulty_score: step.difficulty_score,
      work_score: step.work_score,
      solver_complexity_score: step.solver_complexity_score,
      grid_size: step.grid_size,
      verification_mode: step.verification_mode,
      verification_point_count: step.verification_point_count,
      verification_strength: step.verification_strength,
      answer_budget_pressure: step.answer_budget_pressure,
      layer_pressure: step.layer_pressure,
      dimension_score: step.dimension_score,
      dimension_profile: step.dimension_profile,
      target_dimension_profile: step.target_dimension_profile,
      dominant_dimensions: step.dominant_dimensions,
      composition_count: step.composition_count,
      combiner_mode: step.combiner_mode,
      combiner_score: step.combiner_score,
      static_parameter_score: step.static_parameter_score,
      dynamic_parameter_score: step.dynamic_parameter_score,
      output_pipeline_score: step.output_pipeline_score,
      final_step_score: step.final_step_score,
      structure_bundle: step.structure_bundle,
      theme_hint: step.theme_hint,
      sample_cost_gas: step.sample_cost_gas,
      submit_cost_gas: step.submit_cost_gas,
      reward_weight: step.reward_weight,
      boss_zone: step.boss_zone,
      allocator_hash: sha256({ allocator_seed: step.allocator_seed }),
      formula_hash: formulaHash(step, params),
      verification_hash: verificationHash(step),
      answer_budget_hash: sha256(answerBudgetForStep(step, readJson('MazeX_validator_rules.json'))),
      allowed_structures: allowedStructuresForStep(step),
    };
    return { ...payload, step_commit: sha256(payload) };
  });
  const leaves = publicSteps.map((step) => step.step_commit);
  const root = merkleRoot(leaves);
  return publicSteps.map((step, index) => ({
    ...step,
    layer_root: root,
    merkle_proof: merkleProof(leaves, index),
  }));
}

function revealForStep(step, params) {
  return {
    layer_no: step.layer_no,
    step_no: step.step_no,
    asset_id: step.asset_id,
    pool_id: step.pool_id,
    family_id: step.family_id,
    primary_structure: step.primary_structure,
    composition_count: step.composition_count,
    combiner_mode: step.combiner_mode,
    combiner_score: step.combiner_score,
    static_parameter_score: step.static_parameter_score,
    dynamic_parameter_score: step.dynamic_parameter_score,
    output_pipeline_score: step.output_pipeline_score,
    final_step_score: step.final_step_score,
    structure_bundle: step.structure_bundle,
    components: step.components || [],
    formula_template: step.formula_template,
    output_pipeline: step.output_pipeline || null,
    parameters: params,
    allocator_seed: step.allocator_seed,
    formula_seed: step.formula_seed,
    parameter_seed: step.parameter_seed,
    verification_seed: step.verification_seed,
    difficulty_model_version: step.difficulty_model_version,
    difficulty_score: step.difficulty_score,
    work_score: step.work_score,
    solver_complexity_score: step.solver_complexity_score,
    readable_difficulty: step.readable_difficulty,
    grid_size: step.grid_size,
    verification_mode: step.verification_mode,
    verification_point_count: step.verification_point_count,
    verification_strength: step.verification_strength,
    answer_budget_pressure: step.answer_budget_pressure,
    layer_pressure: step.layer_pressure,
    dimension_score: step.dimension_score,
    dimension_profile: step.dimension_profile,
    target_dimension_profile: step.target_dimension_profile,
    dominant_dimensions: step.dominant_dimensions,
    difficulty_components: step.difficulty_components,
    formula_hash: formulaHash(step, params),
    verification_hash: verificationHash(step),
    answer_formula: answerExpressions(step, params),
  };
}

function substituteXY(expression, xExpression, yExpression) {
  return String(expression)
    .replace(/\bx\b/g, '__MX_X__')
    .replace(/\by\b/g, '__MX_Y__')
    .replace(/__MX_X__/g, `(${xExpression})`)
    .replace(/__MX_Y__/g, `(${yExpression})`);
}

function weightedExpressionTerms(step, p, { alternate = false, cross = false, include = null } = {}) {
  const weightsKey = alternate ? 'component_weights_alt' : 'component_weights';
  const termsU = [];
  const termsV = [];
  const components = include ? step.components.filter(include) : step.components;
  for (const component of components) {
    const componentParams = p.components?.[component.component_id] || {};
    const weights = p[weightsKey]?.[component.component_id] || { u: 1, v: 1 };
    const expressions = baseAnswerExpressions(component, componentParams);
    if (cross) {
      termsU.push(`${weights.u}*(${expressions.u}) + ${weights.v}*(${expressions.v})`);
      termsV.push(`${weights.v}*(${expressions.u}) - ${weights.u}*(${expressions.v})`);
    } else {
      termsU.push(`${weights.u}*(${expressions.u})`);
      termsV.push(`${weights.v}*(${expressions.v})`);
    }
  }
  return {
    u: termsU.length ? termsU.join(' + ') : '0',
    v: termsV.length ? termsV.join(' + ') : '0',
  };
}

function buildIndexedIf(gate, expressions, axis) {
  let result = `(${expressions[expressions.length - 1][axis]})`;
  for (let index = expressions.length - 2; index >= 0; index -= 1) {
    result = `if(${gate} == ${index}, (${expressions[index][axis]}), ${result})`;
  }
  return result;
}

function compositeAnswerExpressions(step, p) {
  const mode = p.composition_mode || step.combiner_mode || step.formula_template?.composition || 'weighted_sum';
  if (mode === 'nested_chain') {
    const first = step.components[0];
    const second = step.components[1] || step.components[0];
    const firstExpressions = baseAnswerExpressions(first, p.components?.[first.component_id] || {});
    const secondExpressions = baseAnswerExpressions(second, p.components?.[second.component_id] || {});
    const nested = {
      u: substituteXY(secondExpressions.u, firstExpressions.u, firstExpressions.v),
      v: substituteXY(secondExpressions.v, firstExpressions.u, firstExpressions.v),
    };
    const rest = weightedExpressionTerms(step, p, { include: (_, index) => index >= 2 });
    return {
      u: `(${p.composition_bias_u || 0} + (${nested.u}) + ${rest.u}) mod N`,
      v: `(${p.composition_bias_v || 0} + (${nested.v}) + ${rest.v}) mod N`,
    };
  }
  if (mode === 'conditional_gate') {
    const gate = `((x + 2*y + ${p.composition_gate || 0}) mod ${step.components.length})`;
    const expressions = step.components.map((component) =>
      baseAnswerExpressions(component, p.components?.[component.component_id] || {}),
    );
    return {
      u: `(${buildIndexedIf(gate, expressions, 'u')} + ${p.composition_bias_u || 0}) mod N`,
      v: `(${buildIndexedIf(gate, expressions, 'v')} + ${p.composition_bias_v || 0}) mod N`,
    };
  }
  if (mode === 'cross_weighted_mix') {
    const terms = weightedExpressionTerms(step, p, { cross: true });
    return {
      u: `(${p.composition_bias_u || 0} + ${terms.u}) mod N`,
      v: `(${p.composition_bias_v || 0} + ${terms.v}) mod N`,
    };
  }
  if (mode === 'gated_weighted_sum') {
    const gate = `((x + y + ${p.composition_gate || 0}) mod 2)`;
    const even = weightedExpressionTerms(step, p, { include: (_, index) => index % 2 === 0 });
    const odd = weightedExpressionTerms(step, p, { alternate: true, include: (_, index) => index % 2 === 1 });
    return {
      u: `if(${gate} == 0, (${p.composition_bias_u || 0} + ${even.u}) mod N, (${p.composition_bias_u || 0} + ${odd.u}) mod N)`,
      v: `if(${gate} == 0, (${p.composition_bias_v || 0} + ${even.v}) mod N, (${p.composition_bias_v || 0} + ${odd.v}) mod N)`,
    };
  }
  const terms = weightedExpressionTerms(step, p);
  return {
    u: `(${p.composition_bias_u || 0} + ${terms.u}) mod N`,
    v: `(${p.composition_bias_v || 0} + ${terms.v}) mod N`,
  };
}

function baseAnswerExpressions(step, p) {
  if (Array.isArray(step.components) && step.components.length) {
    return compositeAnswerExpressions(step, p);
  }

  const id = step.asset_id;
  const family = step.family_id;
  const m = (value) => String(value);
  if (family === 'math.poly.quadratic_mix_mod') {
    return { u: `(${m(p.a)}*x*x + ${m(p.b)}*y + ${m(p.c)}*x*y + ${m(p.seed)}) mod N`, v: `(${m(p.d)}*y*y + ${m(p.e)}*x + ${m(p.f)}*x*y + ${m(p.seed2)}) mod N` };
  }
  if (family === 'info.permutation.sbox_small') {
    const values = valuesList(p.table);
    return { u: `(x + table((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.seed)}) mod ${m(p.p)}, ${values}) + ${m(p.c)}) mod N`, v: `(y + table(((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.seed)}) mod ${m(p.p)}) + ${m(p.offset)}, ${values}) + ${m(p.f)}) mod N` };
  }
  if (family === 'time.decay.half_life_step') {
    const age = `((x + y + ${m(p.t_seed)}) mod N)`;
    const h = `floor(${age}/${m(p.half_life)})`;
    const decay = `floor(${m(p.A)} / (2^min(${h}, 12)))`;
    return { u: `(${m(p.a)}*x + ${m(p.b)}*y + ${decay} + ${m(p.c)}) mod N`, v: `(y + ${m(p.k)}*${h} + ${m(p.f)}) mod N` };
  }
  if (family === 'graph.path.shortest_distance') {
    const dist = `(abs(x - ${m(p.target_x)}) + abs(y - ${m(p.target_y)}))`;
    const degree = `(2 + ((x + y + ${m(p.graph_seed)}) mod 4))`;
    return { u: `(x + ${m(p.a)}*${dist} + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*${degree} + ${m(p.f)}) mod N` };
  }
  if (family === 'game.board.knight_move_gate') {
    const legal = `((abs(x - ${m(p.target_x)}) == 1 && abs(y - ${m(p.target_y)}) == 2) || (abs(x - ${m(p.target_x)}) == 2 && abs(y - ${m(p.target_y)}) == 1))`;
    return { u: `if(${legal}, (x + ${m(p.a)}*y + ${m(p.c)}) mod N, (N - 1 - x + ${m(p.c2)}) mod N)`, v: `if(${legal}, (y + ${m(p.b)}*x + ${m(p.f)}) mod N, (N - 1 - y + ${m(p.f2)}) mod N)` };
  }
  if (family === 'info.checksum.weighted_sum') {
    const code = `((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.checksum_seed)}) mod ${m(p.p)})`;
    const check = `((${code} + x + 2*y) mod ${m(p.q)})`;
    return { u: `(x + ${code} + ${m(p.c)}) mod N`, v: `(y + ${check} + ${m(p.f)}) mod N` };
  }
  if (family === 'prob.seeded.random_walk_bucket') {
    const bucketValue = `floor(((${m(p.seed)} + ${m(p.a)}*x*17 + ${m(p.b)}*y*31 + x*y) mod 997) / ${m(p.width)})`;
    return { u: `(x + ${m(p.a)}*${bucketValue} + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*(${bucketValue} mod ${m(p.q)}) + ${m(p.f)}) mod N` };
  }
  if (family === 'state.click.last_point_memory') {
    const memory = `(abs(x - ${m(p.last_x)}) + abs(y - ${m(p.last_y)}) + ((x + y + ${m(p.click_seed)}) mod 17))`;
    return { u: `(x + ${m(p.a)}*${memory} + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*${memory} + ${m(p.f)}) mod N` };
  }
  if (family === 'fsm.phase.three_gate') {
    const gate = `((x + 2*y + ${m(p.state_seed)}) mod 3)`;
    const bu = (prefix) => `(${m(p[`${prefix}a`])}*x + ${m(p[`${prefix}b`])}*y + ${m(p[`${prefix}c`])}) mod N`;
    const bv = (prefix) => `(${m(p[`${prefix}d`])}*x + ${m(p[`${prefix}e`])}*y + ${m(p[`${prefix}f`])}) mod N`;
    return { u: `if(${gate} == 0, ${bu('p0')}, if(${gate} == 1, ${bu('p1')}, ${bu('p2')}))`, v: `if(${gate} == 0, ${bv('p0')}, if(${gate} == 1, ${bv('p1')}, ${bv('p2')}))` };
  }
  if (family === 'sequence.recurrence.fibonacci_mod') {
    const values = valuesList(p.seq_table);
    const s = `table((x + y + ${m(p.t_seed)}) mod ${m(p.period)}, ${values})`;
    return { u: `(x + ${m(p.a)}*${s} + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*${s} + ${m(p.f)}) mod N` };
  }
  if (family === 'vision.kernel.edge_stencil') {
    const k = `((${m(p.kx)}*(x - y) + ${m(p.ky)}*(x + y) + ${m(p.edge_seed)} + N*1000) mod N)`;
    return { u: `(x + ${m(p.a)}*${k} + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*abs(${k}) + ${m(p.f)}) mod N` };
  }
  if (family === 'opt.cost.minimum_choice') {
    const costA = `(abs(x - ${m(p.ax)}) + abs(y - ${m(p.ay)}))`;
    const costB = `(abs(x - ${m(p.bx)}) + abs(y - ${m(p.by)}))`;
    return { u: `if(${costA} <= ${costB}, (x + ${m(p.p)}*${costA} + ${m(p.c)}) mod N, (x + ${m(p.q)}*${costB} + ${m(p.c2)}) mod N)`, v: `if(${costA} <= ${costB}, (y + ${m(p.r)}*${costA} + ${m(p.f)}) mod N, (y + ${m(p.s)}*${costB} + ${m(p.f2)}) mod N)` };
  }
  if (family === 'symbol.grammar.token_bucket') {
    const token = `table((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.seed)}) mod ${m(p.p)}, ${valuesList(p.symbol_table)})`;
    const score = `((${token}*${token} + 3*${token} + 7) mod N)`;
    return { u: `(x + ${score} + ${m(p.c)}) mod N`, v: `(y + ${token} + ${m(p.f)}) mod N` };
  }
  if (family === 'bio.growth.logistic_bucket') {
    const t = `((x + y + ${m(p.t_seed)}) mod ${m(p.capacity)})`;
    const growth = `floor(${m(p.rate)}*${t}*(${m(p.capacity)} - ${t})/${m(p.scale)})`;
    return { u: `(x + ${m(p.a)}*${growth} + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*(${growth} mod ${m(p.q)}) + ${m(p.f)}) mod N` };
  }
  if (family === 'econ.payoff.threshold_gate') {
    return { u: `if(${m(p.p)}*x - ${m(p.q)}*y >= ${m(p.threshold)}, (x + ${m(p.a)}*y + ${m(p.c)}) mod N, (x + ${m(p.b)}*y + ${m(p.c2)}) mod N)`, v: `if(${m(p.p)}*x - ${m(p.q)}*y >= ${m(p.threshold)}, (y + ${m(p.d)}*x + ${m(p.f)}) mod N, (y + ${m(p.e)}*x + ${m(p.f2)}) mod N)` };
  }
  if (family === 'boss.composite.structure_chain') {
    const r1u = `(${m(p.a)}*x + ${m(p.b)}*y + ${m(p.c)})`;
    const r1v = `(${m(p.d)}*x + ${m(p.e)}*y + ${m(p.f)})`;
    const r2 = `((${r1u} + ${r1v}) mod ${m(p.p)})`;
    const cond = `((${r2} + x + y) mod 2 == 0)`;
    return { u: `if(${cond}, (${r1u} + ${m(p.q)}*${r2} + ${m(p.r)}) mod N, (N - 1 - (${r1u} mod N) + ${m(p.r)}) mod N)`, v: `if(${cond}, (${r1v} + ${m(p.q)}*${r2} + ${m(p.s)}) mod N, (N - 1 - (${r1v} mod N) + ${m(p.s)}) mod N)` };
  }
  if (family === 'chain.history.previous_hash_mix') {
    const r = `((${m(p.history_mix)} + ${m(Number(step.layer_no || 0))}*31 + ${m(Number(step.step_no || 0))}*17) mod N)`;
    return { u: `(${m(p.a)}*x + ${m(p.b)}*y + ${m(p.c)} + ${r}) mod N`, v: `(y + ${m(p.k)}*${r} + ${m(p.f)}) mod N` };
  }
  if (family === 'math.constants.pi_e_phi_scaled') {
    const piK = `floor(${m(p.PI_SCALE)}*((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.seed)}) mod ${m(p.scale)})/${m(p.scale)})`;
    const eK = `floor(${m(p.E_SCALE)}*((${m(p.c)}*x + ${m(p.d)}*y + ${m(p.seed2)}) mod ${m(p.scale)})/${m(p.scale)})`;
    const phiK = `floor(${m(p.PHI_SCALE)}*((x + y + ${m(p.seed3)}) mod ${m(p.scale)})/${m(p.scale)})`;
    return { u: `(x + ${piK} + ${phiK} + ${m(p.r)}) mod N`, v: `(y + ${eK} + ${phiK} + ${m(p.s)}) mod N` };
  }
  if (family === 'math.number.prime_crt_euler_phi') {
    const r1 = `((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.c)}) mod ${m(p.prime_a)})`;
    const r2 = `((${m(p.d)}*x + ${m(p.e)}*y + ${m(p.f)}) mod ${m(p.prime_b)})`;
    const modulus = p.prime_a * p.prime_b;
    const z = `((${r1}*${m(p.prime_b)}*${m(p.inv_a)} + ${r2}*${m(p.prime_a)}*${m(p.inv_b)}) mod ${m(modulus)})`;
    const phiZ = `((${z} + ${m(p.phi_offset)}) mod N)`;
    return { u: `(${z} + ${m(p.alpha)}*${phiZ} + ${m(p.r)}) mod N`, v: `(${z} + ${m(p.beta)}*(((x + ${m(p.gx)}) mod 7) + ((y + ${m(p.gy)}) mod 5)) + ${m(p.s)}) mod N` };
  }
  if (family === 'math.number.prime_mask_quadratic_residue') {
    const z = `((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.c)}) mod ${m(p.M)})`;
    const primeGate = `(table(${z}, ${valuesList(p.prime_table)}) == 1)`;
    const qr = `table((x + ${m(p.seed)}) mod ${m(p.p)}, ${valuesList(p.qr_table)})`;
    return { u: `if(${primeGate}, (x + ${m(p.q1)}*${qr} + ${m(p.r)}) mod N, (N - 1 - x + ${m(p.r2)}) mod N)`, v: `if(${primeGate}, (y + ${m(p.q2)}*${z} + ${m(p.s)}) mod N, (N - 1 - y + ${m(p.s2)}) mod N)` };
  }
  if (family === 'math.spectral.fourier_phase_integer') {
    const phase = `((${m(p.kx)}*x + ${m(p.ky)}*y + ${m(p.seed)}) mod ${m(p.period)})`;
    const values = valuesList(p.fourier_table);
    const amp = `table(${phase}, ${values})`;
    const harm = `table(${m(p.h)}*${phase} + ${m(p.offset)}, ${values})`;
    return { u: `(x + ${m(p.a)}*${amp} + ${m(p.b)}*${harm} + ${m(p.c)}) mod N`, v: `(y + ${m(p.d)}*${amp} - ${m(p.e)}*${harm} + ${m(p.f)}) mod N` };
  }
  if (family === 'math.spectral.laplace_decay_kernel') {
    const r = `(abs(x - ${m(p.cx)}) + abs(y - ${m(p.cy)}))`;
    const kernel = `floor(${m(p.A)} / (1 + ${m(p.lambda)}*${r}))`;
    const phase = `((x + y + ${m(p.seed)}) mod ${m(p.period)})`;
    const values = valuesList(p.wave_table);
    return { u: `(x + ${kernel} + table(${phase}, ${values}) + ${m(p.c)}) mod N`, v: `(y + ${m(p.lambda)}*${kernel} + table(${phase} + ${m(p.shift)}, ${values}) + ${m(p.f)}) mod N` };
  }
  if (family === 'math.group.symmetry_orbit_action') {
    const g = `((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.seed)}) mod ${m(p.group_order)})`;
    const actionU = `if(${g} == 0, x, if(${g} == 1, y, if(${g} == 2, N - 1 - x, N - 1 - y)))`;
    const actionV = `if(${g} == 0, y, if(${g} == 1, N - 1 - x, if(${g} == 2, N - 1 - y, x)))`;
    const orbit = `((${actionU} + ${actionV}) mod ${m(p.group_order)})`;
    const stabilizer = `if(${g} == 0, 2, 1)`;
    return { u: `(${actionU} + ${m(p.p)}*${orbit} + ${m(p.r)}) mod N`, v: `(${actionV} + ${m(p.q)}*${stabilizer} + ${m(p.s)}) mod N` };
  }
  if (family === 'math.complex.mobius_discrete') {
    const den = `(abs(${m(p.cx)}*x - ${m(p.cy)}*y + ${m(p.d0)}) + 1)`;
    return { u: `(floor((${m(p.a)}*x - ${m(p.b)}*y + ${m(p.p)})/${den}) + ${m(p.r)}) mod N`, v: `(floor((${m(p.b)}*x + ${m(p.a)}*y + ${m(p.q)})/${den}) + ${m(p.s)}) mod N` };
  }
  if (family === 'math.tensor.curvature_bucket') {
    const g11 = `((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.c)}) mod ${m(p.M)})`;
    const g12 = `((${m(p.d)}*x + ${m(p.e)}*y + ${m(p.f)}) mod ${m(p.M)})`;
    const g22 = `((${m(p.p)}*x + ${m(p.q)}*y + ${m(p.r)}) mod ${m(p.M)})`;
    const det = `(${g11}*${g22} - ${g12}*${g12})`;
    const curv = `(abs(${det} + ${m(p.trace_scale)}*(${g11} + ${g22})) mod ${m(p.K)})`;
    return { u: `(x + ${m(p.alpha)}*${curv} + ${m(p.s)}) mod N`, v: `(y + ${m(p.beta)}*${det} + ${m(p.t)}) mod N` };
  }
  if (family === 'physics.variational.hamiltonian_symplectic_step') {
    const dHx = `((2*${m(p.a)}*x + ${m(p.c)}*y) mod N)`;
    const dHy = `((2*${m(p.b)}*y + ${m(p.c)}*x) mod N)`;
    return { u: `(x + ${dHy} + ${m(p.r)}) mod N`, v: `(y - ${dHx} + ${m(p.s)}) mod N` };
  }
  if (family === 'physics.field.maxwell_lattice_flux') {
    const guard = `${m(p.M)}*1000`;
    const ex = `((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.seedE)}) mod ${m(p.M)})`;
    const ey = `((${m(p.c)}*x - ${m(p.d)}*y + ${m(p.seedE2)} + ${guard}) mod ${m(p.M)})`;
    const bx = `((${m(p.p)}*x + ${m(p.q)}*y + ${m(p.seedB)}) mod ${m(p.M)})`;
    const by = `((${m(p.r)}*x - ${m(p.s)}*y + ${m(p.seedB2)} + ${guard}) mod ${m(p.M)})`;
    const divE = `((${ex} + ${ey} + x - y + ${guard}) mod ${m(p.M)})`;
    const curlB = `((${by} - ${bx} + y - x + ${guard}) mod ${m(p.M)})`;
    return { u: `(x + ${m(p.alpha)}*${divE} + ${m(p.beta)}*${curlB} + ${m(p.c0)}) mod N`, v: `(y + ${m(p.gamma)}*${curlB} - ${m(p.delta)}*${divE} + ${m(p.f0)}) mod N` };
  }
  if (family === 'physics.quantum.schrodinger_lattice_phase') {
    const guard = `${m(p.M)}*1000`;
    const psiR = `((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.seedR)}) mod ${m(p.M)})`;
    const psiI = `((${m(p.c)}*x - ${m(p.d)}*y + ${m(p.seedI)} + ${guard}) mod ${m(p.M)})`;
    const potential = `((${m(p.p)}*x*x + ${m(p.q)}*y*y + ${m(p.v0)}) mod ${m(p.M)})`;
    const lap = `((${psiR} + ${psiI} + x + y) mod ${m(p.M)})`;
    const measure = `((${psiR}*${psiR} + ${psiI}*${psiI} + ${potential} + ${lap}) mod N)`;
    return { u: `(x + ${m(p.alpha)}*${measure} + ${m(p.r)}) mod N`, v: `(y + ${m(p.beta)}*((${measure} + ${potential}) mod N) + ${m(p.s)}) mod N` };
  }
  if (family === 'physics.fluid.navier_stokes_lattice_step') {
    const vx = `((${m(p.a)}*y + ${m(p.b)}*(x mod ${m(p.p)}) + ${m(p.seedX)}) mod ${m(p.M)})`;
    const vy = `((${m(p.c)}*x + ${m(p.d)}*(y mod ${m(p.q)}) + ${m(p.seedY)}) mod ${m(p.M)})`;
    const pressure = `((${m(p.px)}*x*x + ${m(p.py)}*y*y + ${m(p.pxy)}*x*y + ${m(p.p0)}) mod ${m(p.M)})`;
    const advect = `((${vx}*((${vx} + x) mod ${m(p.M)}) + ${vy}*((${vy} + y) mod ${m(p.M)})) mod ${m(p.M)})`;
    const diffuse = `((${m(p.viscosity)}*(${vx} + ${vy} + x + y)) mod ${m(p.M)})`;
    return { u: `(x + ${vx} - ${advect} + ${diffuse} - ${pressure} + ${m(p.r)}) mod N`, v: `(y + ${vy} - ${advect} + ${diffuse} - ${pressure} + ${m(p.s)}) mod N` };
  }
  if (family === 'physics.statmech.boltzmann_entropy_bucket') {
    const spin = `if(((${m(p.a)}*x + ${m(p.b)}*y + ${m(p.seed)}) mod ${m(p.m)}) < ${m(p.threshold)}, 1, -1)`;
    const energy = `(${m(p.J)}*(${spin}*((x + y) mod 3 - 1)) + ${m(p.field)}*${spin})`;
    const bucketValue = `table(${energy} + ${m(p.temp_offset)}, ${valuesList(p.boltzmann_table)})`;
    return { u: `(x + ${m(p.alpha)}*${bucketValue} + ${m(p.r)}) mod N`, v: `(y + ${m(p.beta)}*abs(${energy}) + ${m(p.s)}) mod N` };
  }
  if (family === 'physics.gauge.lattice_plaquette_phase') {
    const values = valuesList(p.link_table);
    const guard = `${m(p.p)}*1000`;
    const ux = `table(x + ${m(p.seedX)}, ${values})`;
    const uy = `table(y + ${m(p.seedY)}, ${values})`;
    const uxY = `table(x + y + ${m(p.seedXY)}, ${values})`;
    const uyX = `table(y + x + ${m(p.seedYX)}, ${values})`;
    const plaquette = `((${ux} + ${uyX} - ${uxY} - ${uy} + ${guard}) mod ${m(p.p)})`;
    const chargePhase = `((${m(p.charge)}*${plaquette} + ${m(p.seed)} + x*7 + y*11 + ${guard}) mod ${m(p.p)})`;
    return { u: `(x + ${m(p.a)}*${chargePhase} + ${m(p.r)}) mod N`, v: `(y + ${m(p.b)}*${plaquette} + ${m(p.s)}) mod N` };
  }
  switch (id) {
    case 'MXF-S01-MATH-001':
      return { u: `(x + ${m(p.c)}) mod N`, v: `(y + ${m(p.f)}) mod N` };
    case 'MXF-S01-MAP-001':
      return { u: `(x + ${m(p.east_shift)}) mod N`, v: `(y + ${m(p.north_shift)}) mod N` };
    case 'MXF-S02-MATH-001':
      return { u: `(${m(p.a)}*x + ${m(p.b)}*y + ${m(p.c)}) mod N`, v: `(${m(p.d)}*x + ${m(p.e)}*y + ${m(p.f)}) mod N` };
    case 'MXF-S02-MATH-002':
      return { u: `(x + ${m(p.a)}*y + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*x + ${m(p.f)}) mod N` };
    case 'MXF-S02-PHYS-001':
      return { u: `(x + ${m(p.slope)}*y + ${m(p.offset)}) mod N`, v: `(y + ${m(p.refract)}*x + ${m(p.offset2)}) mod N` };
    case 'MXF-S02-MAP-001':
      return { u: `(${m(p.scale_x)}*x + ${m(p.skew_y)}*y + ${m(p.c)}) mod N`, v: `(${m(p.skew_x)}*x + ${m(p.scale_y)}*y + ${m(p.f)}) mod N` };
    case 'MXF-S02-ENG-001':
      return { u: `(${m(p.ratio_a)}*x + ${m(p.ratio_b)}*y + ${m(p.bias)}) mod N`, v: `(${m(p.ratio_c)}*x + ${m(p.ratio_d)}*y + ${m(p.bias2)}) mod N` };
    case 'MXF-S03-VISION-001':
      return { u: `(N - 1 - x + ${m(p.c)}) mod N`, v: `(y + ${m(p.f)}) mod N` };
    case 'MXF-S03-MATH-001':
      return { u: `(N - 1 - y + ${m(p.c)}) mod N`, v: `(x + ${m(p.f)}) mod N` };
    case 'MXF-S03-MATH-002':
      return { u: `(x + ${m(p.shear)}*y + ${m(p.c)}) mod N`, v: `(y + ${m(p.f)}) mod N` };
    case 'MXF-S03-PHYS-001':
      return { u: `(${m(p.prism_a)}*x + ${m(p.prism_b)}*y + ${m(p.c)}) mod N`, v: `(${m(p.prism_c)}*x + ${m(p.prism_d)}*y + ${m(p.f)}) mod N` };
    case 'MXF-S04-MATH-001':
      return { u: `(${m(p.a)}*x*x + ${m(p.b)}*y + ${m(p.c)}) mod N`, v: `(${m(p.d)}*y*y + ${m(p.e)}*x + ${m(p.f)}) mod N` };
    case 'MXF-S04-MATH-002':
      return { u: `(${m(p.a)}*x + ${m(p.b)}*y + ${m(p.c)}*x*y + ${m(p.seed)}) mod N`, v: `(${m(p.d)}*y + ${m(p.e)}*x + ${m(p.f)}*x*y + ${m(p.seed2)}) mod N` };
    case 'MXF-S04-PHYS-001':
      return { u: `(x + ${m(p.a)}*((x*x + y*y) mod N) + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*((x*x + y*y) mod N) + ${m(p.f)}) mod N` };
    case 'MXF-S04-MAP-001':
      return { u: `(x + ((${m(p.a)}*x*x + ${m(p.b)}*y*y + ${m(p.c)}*x*y) mod N) + ${m(p.seed)}) mod N`, v: `(y + 2*((${m(p.a)}*x*x + ${m(p.b)}*y*y + ${m(p.c)}*x*y) mod N) + ${m(p.seed2)}) mod N` };
    case 'MXF-S05-MATH-001':
      return { u: `(x + ${m(p.a)}*(y mod ${m(p.p)}) + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*(x mod ${m(p.q)}) + ${m(p.f)}) mod N` };
    case 'MXF-S05-MATH-002':
      return { u: `(x + ${m(p.a)}*((x + y) mod 2) + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*((x + y) mod 2) + ${m(p.f)}) mod N` };
    case 'MXF-S05-INFO-001':
      return { u: `(((x + ${m(p.key)}) mod ${m(p.p)}) + ${m(p.a)}*y + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*((x + ${m(p.key)}) mod ${m(p.p)}) + ${m(p.f)}) mod N` };
    case 'MXF-S05-MUSIC-001':
      return { u: `(x + ${m(p.a)}*((x + 2*y) mod ${m(p.p)}) + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*((x + 2*y) mod ${m(p.p)}) + ${m(p.f)}) mod N` };
    case 'MXF-S05-ENG-001':
      return { u: `(x + ((${m(p.gear_a)}*x + ${m(p.gear_b)}*y) mod ${m(p.teeth)}) + ${m(p.c)}) mod N`, v: `(y + ${m(p.ratio)}*((${m(p.gear_a)}*x + ${m(p.gear_b)}*y) mod ${m(p.teeth)}) + ${m(p.f)}) mod N` };
    case 'MXF-S06-MATH-001':
      return { u: `if((x + y) mod 2 == 0, (${m(p.a)}*x + ${m(p.b)}*y + ${m(p.c)}) mod N, (${m(p.a2)}*x + ${m(p.b2)}*y + ${m(p.c2)}) mod N)`, v: `if((x + y) mod 2 == 0, (${m(p.d)}*x + ${m(p.e)}*y + ${m(p.f)}) mod N, (${m(p.d2)}*x + ${m(p.e2)}*y + ${m(p.f2)}) mod N)` };
    case 'MXF-S06-MATH-002':
      return { u: `if(x + y >= ${m(p.threshold)}, (x + ${m(p.a)}*y + ${m(p.c)}) mod N, (x + ${m(p.b)}*y + ${m(p.c2)}) mod N)`, v: `if(x + y >= ${m(p.threshold)}, (y + ${m(p.d)}*x + ${m(p.f)}) mod N, (y + ${m(p.e)}*x + ${m(p.f2)}) mod N)` };
    case 'MXF-S06-GAME-001':
      return { u: `if((floor(x/${m(p.w)}) + floor(y/${m(p.h)})) mod 2 == 0, (x + ${m(p.a)}*y + ${m(p.c)}) mod N, (N - 1 - x + ${m(p.b)}*y + ${m(p.c2)}) mod N)`, v: `if((floor(x/${m(p.w)}) + floor(y/${m(p.h)})) mod 2 == 0, (y + ${m(p.d)}*x + ${m(p.f)}) mod N, (N - 1 - y + ${m(p.e)}*x + ${m(p.f2)}) mod N)` };
    case 'MXF-S06-ECON-001':
      return { u: `if(${m(p.p)}*x - ${m(p.q)}*y >= ${m(p.threshold)}, (x + ${m(p.a)}*y + ${m(p.c)}) mod N, (x + ${m(p.b)}*y + ${m(p.c2)}) mod N)`, v: `if(${m(p.p)}*x - ${m(p.q)}*y >= ${m(p.threshold)}, (y + ${m(p.d)}*x + ${m(p.f)}) mod N, (y + ${m(p.e)}*x + ${m(p.f2)}) mod N)` };
    case 'MXF-S07-MATH-001':
      return { u: `(x + ${m(p.a)}*(abs(x - ${m(p.cx)}) + abs(y - ${m(p.cy)})) + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*(abs(x - ${m(p.cx)}) + abs(y - ${m(p.cy)})) + ${m(p.f)}) mod N` };
    case 'MXF-S07-MAP-001':
      return { u: `(x + abs(x - ${m(p.beacon_x)}) + abs(y - ${m(p.beacon_y)}) + ${m(p.c)}) mod N`, v: `(y + 2*(abs(x - ${m(p.beacon_x)}) + abs(y - ${m(p.beacon_y)})) + ${m(p.f)}) mod N` };
    case 'MXF-S07-PHYS-001':
      return { u: `(x + ${m(p.a)}*((x - ${m(p.cx)})*(x - ${m(p.cx)}) + (y - ${m(p.cy)})*(y - ${m(p.cy)})) + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*((x - ${m(p.cx)})*(x - ${m(p.cx)}) + (y - ${m(p.cy)})*(y - ${m(p.cy)})) + ${m(p.f)}) mod N` };
    case 'MXF-S08-MATH-001':
      return { u: `(x + ${m(p.a)}*floor(y/${m(p.h)}) + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*floor(x/${m(p.w)}) + ${m(p.f)}) mod N` };
    case 'MXF-S08-VISION-001':
      return { u: `(x + ${m(p.a)}*(floor(x/${m(p.w)}) + ${m(p.cols)}*floor(y/${m(p.h)})) + ${m(p.c)}) mod N`, v: `(y + ${m(p.b)}*(floor(x/${m(p.w)}) + ${m(p.cols)}*floor(y/${m(p.h)})) + ${m(p.f)}) mod N` };
    case 'MXF-S10-PHYS-001':
      const values = p.wave_table.join(',');
      return {
        u: `(x + table(x mod ${m(p.p)}, ${values}) + ${m(p.c)}) mod N`,
        v: `(y + table(y mod ${m(p.q)}, ${values}) + ${m(p.f)}) mod N`,
      };
    default: {
      const generic = materializeFormulaTemplate(step, p);
      if (generic) return generic;
      return { u: `(x + ${m(p.c || 0)}) mod N`, v: `(y + ${m(p.f || 0)}) mod N` };
    }
  }
}

function answerExpressions(step, p, context = {}) {
  const base = baseAnswerExpressions(step, p);
  return applyOutputPipelineToExpressions({
    expressions: base,
    step,
    context: {
      layerNo: step.layer_no,
      stepNo: step.step_no,
      ...context,
    },
    N: step.grid_size || 100,
  });
}

module.exports = {
  mod,
  makeParams,
  evaluateStep,
  formulaHash,
  verificationHash,
  buildCommitments,
  revealForStep,
  answerExpressions,
};
