;;; mod-arith.lsp — reversible modular arithmetic over a generic prime. ;;; ;;; Public surface: bit-set?, load-const!, unload-const!, cload-const!, ;;; cunload-const!, add-const!, csub-const!, inv-maj!, cmp-lt-into!, ;;; mod-add!, mod-sub!, mod-mul!, mod-double-inplace!, mod-halve-inplace!, ;;; mod-shift-left/right-by-k-lowq!, mod-add-qb!, mod-sub-qb!. Layer ;;; sits on adder.lsp (Cuccaro ripple-carry). ;;; ;;; mod-add CALLING CONVENTION: caller passes a-reg & acc-reg as ;;; (n+1)-wide registers, top bit held at |0> (extension ancilla). ;;; Caller also allocates cin (1 bit), tmp (n+1 bits), flag (1 bit). ;;; All ancillae return to |0>; top bit of a-reg & acc-reg also returns ;;; to |0> on exit. ;;; ;;; ── *field-prime* convention ───────────────────────────────────── ;;; ;;; Primitives take `p` as explicit argument (p = field prime, integer). ;;; New upstream consumers can ALSO set `*field-prime*` at program top ;;; so layer code reads a single source of truth & avoids threading p ;;; through every wrapper. Foxhop callers still pass p directly. ;;; ;;; Public mod-* primitives assert *field-prime* matches their `p` arg ;;; if both are set, otherwise accept caller's `p` as authoritative. (define *field-prime* #f) ;; Consumer binds via (set! *field-prime*

) before calling mod-arith ;; primitives. Stays #f when not used; primitives derive width from ;; (bit-length p) or accept width via caller arg. (load "quantum/gates.lsp") (load "quantum/adder.lsp") ;;; ── classical-constant load/unload ───────────────────────────── (define (bit-set? k i) "Is bit i (0-indexed) set in classical integer k?" (= 1 (remainder (quotient k (expt 2 i)) 2))) (define (load-const! c reg n k) "Apply X to reg[i] for every i in [0,n) where bit i of k is 1. reg starts |0> n-wide; ends holding the bit pattern of k mod 2^n." (let loop ((i 0)) (when (< i n) (when (bit-set? k i) (gate-x! c reg i)) (loop (+ i 1))))) (define (unload-const! c reg n k) (load-const! c reg n k)) (define (cload-const! c ctrl-reg ctrl-idx tgt-reg n k) "Apply CX(ctrl, tgt[i]) for every i where bit i of k is 1." (let loop ((i 0)) (when (< i n) (when (bit-set? k i) (gate-cx! c ctrl-reg ctrl-idx tgt-reg i)) (loop (+ i 1))))) (define (cunload-const! c ctrl-reg ctrl-idx tgt-reg n k) (cload-const! c ctrl-reg ctrl-idx tgt-reg n k)) ;;; ── extcarry_clean family — sweep-extcarry-clean ────────────────── ;;; ;;; HEAD const_arith.rs:239,251,274,289,317,331. Closes AUDIT §6 ;;; rows 208-212. ;;; ;;; Pattern (HEAD lines 257-269 + symmetric): each primitive ;;; 1. load const c into `ca` register (cx-from-ctrl for controlled ;;; variants, x for uncontrolled) ;;; 2. cuccaro-add/sub-low-to-ext-clean! with cin ;;; 3. unload (same op-stream self-inverse) ;;; ;;; HEAD's `borrow_cin: Option` switches between caller- ;;; supplied vs fresh-alloc cin. Lumbda is caller-allocated throughout; ;;; cin-reg + cin-idx are mandatory caller-supplied parameters. The ;;; six HEAD names collapse to a smaller surface in lumbda but we keep ;;; all six for ABI parity — callers may want to name the "borrow" ;;; variant explicitly even though the wire pattern is identical. ;;; ;;; Caller responsibility: ;;; - acc-ext-reg width >= n+1 (n data bits + 1 ext carry bit) ;;; - ca-reg width >= n (clean |0> on entry; restored on exit) ;;; - cin-reg[cin-idx] = |0> on entry; restored on exit ;;; - ctrl-reg[ctrl-idx] (controlled variants) — read-only (define (add-nbit-const-extcarry-clean! c acc-ext-reg n k ca-reg cin-reg cin-idx) "Port of HEAD add_nbit_const_extcarry_clean (const_arith.rs:239). acc-ext-reg := (acc-ext-reg + k) mod 2^(n+1), carry into top bit." (load-const! c ca-reg n k) (cuccaro-add-low-to-ext-clean! c ca-reg acc-ext-reg n cin-reg cin-idx) (unload-const! c ca-reg n k)) (define (add-nbit-const-extcarry-clean-with-cin! c acc-ext-reg n k ca-reg cin-reg cin-idx) "Port of HEAD add_nbit_const_extcarry_clean_with_cin (const_arith.rs:251). Wire-identical to the non-with-cin variant in lumbda since cin is always caller-supplied; preserves HEAD's ABI name for callers that explicitly borrow cin from a live-idle lane (round84-lowq mid-sub uses this naming)." (add-nbit-const-extcarry-clean! c acc-ext-reg n k ca-reg cin-reg cin-idx)) (define (sub-nbit-const-extcarry-clean! c acc-ext-reg n k ca-reg cin-reg cin-idx) "Port of HEAD sub_nbit_const_extcarry_clean (const_arith.rs:274). acc-ext-reg := (acc-ext-reg - k) mod 2^(n+1), borrow into top bit." (load-const! c ca-reg n k) (cuccaro-sub-low-to-ext-clean! c ca-reg acc-ext-reg n cin-reg cin-idx) (unload-const! c ca-reg n k)) (define (cadd-nbit-const-extcarry-clean! c acc-ext-reg n k ctrl-reg ctrl-idx ca-reg cin-reg cin-idx) "Port of HEAD cadd_nbit_const_extcarry_clean (const_arith.rs:289). acc-ext-reg += (ctrl ? k : 0), carry into top bit. Drop-in for cadd-nbit-const. Constant loaded via CX-from-ctrl so the unconditional clean adder realizes the controlled add." (cload-const! c ctrl-reg ctrl-idx ca-reg n k) (cuccaro-add-low-to-ext-clean! c ca-reg acc-ext-reg n cin-reg cin-idx) (cunload-const! c ctrl-reg ctrl-idx ca-reg n k)) (define (csub-nbit-const-extcarry-clean! c acc-ext-reg n k ctrl-reg ctrl-idx ca-reg cin-reg cin-idx) "Port of HEAD csub_nbit_const_extcarry_clean (const_arith.rs:317). acc-ext-reg -= (ctrl ? k : 0), borrow into top bit." (cload-const! c ctrl-reg ctrl-idx ca-reg n k) (cuccaro-sub-low-to-ext-clean! c ca-reg acc-ext-reg n cin-reg cin-idx) (cunload-const! c ctrl-reg ctrl-idx ca-reg n k)) (define (csub-nbit-const-extcarry-clean-with-cin! c acc-ext-reg n k ctrl-reg ctrl-idx ca-reg cin-reg cin-idx) "Port of HEAD csub_nbit_const_extcarry_clean_with_cin (const_arith.rs:331). Wire-identical to csub-nbit-const-extcarry- clean! in lumbda; ABI alias for explicit-borrow callers (the peak-binding round84-lowq mid-sub borrows c_in from idle a_ovf lane to drop peak 1308 -> 1307)." (csub-nbit-const-extcarry-clean! c acc-ext-reg n k ctrl-reg ctrl-idx ca-reg cin-reg cin-idx)) ;;; ── add/sub of classical constant ────────────────────────────── (define (add-const! c acc-reg n k cin-reg cin-idx tmp-reg) "acc := (acc + k) mod 2^n. tmp must be a width-n register at |0>." (let ((kk (modulo k (expt 2 n)))) (load-const! c tmp-reg n kk) (cuccaro-add! c tmp-reg acc-reg cin-reg cin-idx n) (unload-const! c tmp-reg n kk))) (define (csub-const! c acc-reg n k ctrl-reg ctrl-idx cin-reg cin-idx tmp-reg) "acc -= (ctrl ? k : 0) mod 2^n. Dispatches to direct sparse path when *cadd-direct-trunc-fast* on; else cload + cuccaro-sub + cunload." (let ((kk (modulo k (expt 2 n)))) (cond ((and *cadd-direct-trunc-fast* (> n 1) (> kk 0)) (csub-nbit-const-direct-trunc-fast! c acc-reg n kk ctrl-reg ctrl-idx tmp-reg *cadd-direct-window* (cdtf-alloc-bit-base! n))) (else (cload-const! c ctrl-reg ctrl-idx tmp-reg n kk) ;; sub = inverse of add. cuccaro-add (a, acc) is a bijection; we ;; manually invert by emitting the gates in reverse. Implemented as ;; cuccaro-sub! below. (cuccaro-sub! c tmp-reg acc-reg cin-reg cin-idx n) (cunload-const! c ctrl-reg ctrl-idx tmp-reg n kk))))) ;;; ── Cuccaro sub (inverse of cuccaro-add) ─────────────────────── (define (cuccaro-sub! c a-reg acc-reg cin-reg cin-idx n) "acc := (acc - a) mod 2^n. Inverse of cuccaro-add gate-by-gate." (cond ((= n 0) #t) ((= n 1) (gate-cx! c a-reg 0 acc-reg 0) (gate-cx! c cin-reg cin-idx acc-reg 0)) (else ;; Inverse of cuccaro-add: ;; inv-uma at i=0 ;; inv-uma for i in 1..n-2 ;; inv of final CX pair (cx is its own inverse, so same gates reversed) ;; inv-maj for i in n-2..1 (descending in fwd; so ascending in inv) ;; inv-maj at top (c-in,acc[0],a[0]) ;; ;; UMA(x,y,w): (ccx x y w)(cx w x)(cx x y) ;; inv-UMA = (cx x y)(cx w x)(ccx x y w) ;; MAJ(x,y,w): (cx w y)(cx w x)(ccx x y w) ;; inv-MAJ = (ccx x y w)(cx w x)(cx w y) ;; ;; Walking forward gates in reverse order: ;; final uma(cin,acc[0],a[0]) -> inv-uma at start ;; for i in 1..n-2 (forward order in inverse: that's the rev loop) ;; uma(a[i-1], acc[i], a[i]) ;; reverse of "cx a[n-1] acc[n-1]; cx a[n-2] acc[n-1]" is same two CX ;; reverse of MAJ sweep (inv-uma! c cin-reg cin-idx acc-reg 0 a-reg 0) (let loop ((i 1)) (when (< i (- n 1)) (inv-uma! c a-reg (- i 1) acc-reg i a-reg i) (loop (+ i 1)))) (gate-cx! c a-reg (- n 1) acc-reg (- n 1)) (gate-cx! c a-reg (- n 2) acc-reg (- n 1)) (let loop ((i (- n 2))) (when (>= i 1) (inv-maj! c a-reg (- i 1) acc-reg i a-reg i) (loop (- i 1)))) (inv-maj! c cin-reg cin-idx acc-reg 0 a-reg 0)))) (define (inv-uma! c x-reg x-idx y-reg y-idx w-reg w-idx) "Inverse of uma!: (cx x y) (cx w x) (ccx x y w)." (gate-cx! c x-reg x-idx y-reg y-idx) (gate-cx! c w-reg w-idx x-reg x-idx) (gate-ccx! c x-reg x-idx y-reg y-idx w-reg w-idx)) (define (inv-maj! c x-reg x-idx y-reg y-idx w-reg w-idx) "Inverse of maj!: (ccx x y w) (cx w x) (cx w y)." (gate-ccx! c x-reg x-idx y-reg y-idx w-reg w-idx) (gate-cx! c w-reg w-idx x-reg x-idx) (gate-cx! c w-reg w-idx y-reg y-idx)) ;;; ── n-bit comparison: flag := flag XOR (u < v) ───────────────── (define (cmp-lt-into! c u-reg v-reg n flag-reg flag-idx cin-reg cin-idx) "flag ^= (u < v). u and v are width-n quantum registers; restored. cin starts |0> ends |0>." ;; Negate u (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) ;; Forward MAJ sweep — n MAJs (includes the top one) (maj! c cin-reg cin-idx v-reg 0 u-reg 0) (let loop ((i 1)) (when (< i n) (maj! c u-reg (- i 1) v-reg i u-reg i) (loop (+ i 1)))) ;; CX top -> flag (gate-cx! c u-reg (- n 1) flag-reg flag-idx) ;; Inverse MAJ sweep (let loop ((i (- n 1))) (when (>= i 1) (inv-maj! c u-reg (- i 1) v-reg i u-reg i) (loop (- i 1)))) (inv-maj! c cin-reg cin-idx v-reg 0 u-reg 0) ;; Un-negate u (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1))))) ;;; cmp-lt-into-fast! — HEAD's cmp_lt_into_fast (mod.rs:3643-3693) ;;; HMR-uncompute variant of cmp-lt-into!. Same flag semantics but ;;; carries lane borrowed from caller + HMR backward sweep saves n ;;; Toffoli per call. (define (cmp-lt-into-fast! c u-reg v-reg n flag-reg flag-idx cin-reg cin-idx carries-reg carries-offset bit-base) "flag ^= (u < v). Same semantics as cmp-lt-into! but with HMR carry uncompute. carries-reg[carries-offset..carries-offset+n-1] must be |0> on entry; HMR returns them to |0> on exit. bit-base..bit-base+n-1 used for classical bits." (cond ((= n 0) #t) (else ;; Negate u (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) ;; Forward sweep — n MAJ-style with explicit carries (gate-cx! c u-reg 0 v-reg 0) (gate-cx! c u-reg 0 cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx v-reg 0 carries-reg carries-offset) (gate-cx! c carries-reg carries-offset u-reg 0) (let loop ((i 1)) (when (< i n) (gate-cx! c u-reg i v-reg i) (gate-cx! c u-reg i u-reg (- i 1)) (gate-ccx! c u-reg (- i 1) v-reg i carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) u-reg i) (loop (+ i 1)))) ;; CX top carry -> flag (gate-cx! c u-reg (- n 1) flag-reg flag-idx) ;; Backward HMR uncompute sweep (let loop-back ((i (- n 1))) (when (>= i 1) (gate-cx! c carries-reg (+ carries-offset i) u-reg i) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c u-reg (- i 1) v-reg i) (gate-pop-cond! c) (gate-cx! c u-reg i u-reg (- i 1)) (gate-cx! c u-reg i v-reg i) (loop-back (- i 1)))) ;; Step 0 backward (gate-cx! c carries-reg carries-offset u-reg 0) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx v-reg 0) (gate-pop-cond! c) (gate-cx! c u-reg 0 cin-reg cin-idx) (gate-cx! c u-reg 0 v-reg 0) ;; Un-negate u (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1))))))) ;;; ── ccx-cmp-lt-into-fast! / cmp-lt-into-fast-with-cin! / phase-conditioned variants ;;; ;;; HEAD compare.rs:55,108,330,357. Closes AUDIT §7 rows 184-185 + 186-187 ;;; (compare.rs no-prefix-targets siblings). ;;; ;;; ──── cmp-lt-into-fast-with-cin! — ABI alias ──── ;;; HEAD's cmp_lt_into_fast_with_cin (compare.rs:55) is wire-identical ;;; to cmp_lt_into_fast except c_in is caller-supplied instead of ;;; freshly-allocated. Lumbda's cmp-lt-into-fast! is ALREADY the ;;; with-cin form (caller passes cin-reg + cin-idx). This alias names ;;; the variant for callers that explicitly borrow c_in from a live- ;;; idle lane. (define (cmp-lt-into-fast-with-cin! c u-reg v-reg n flag-reg flag-idx cin-reg cin-idx carries-reg carries-offset bit-base) "Port of HEAD cmp_lt_into_fast_with_cin (compare.rs:55). Wire- identical to cmp-lt-into-fast! in lumbda; ABI alias for explicit- borrow callers." (cmp-lt-into-fast! c u-reg v-reg n flag-reg flag-idx cin-reg cin-idx carries-reg carries-offset bit-base)) ;;; ──── ccx-cmp-lt-into-fast! — without prefix-targets ──── ;;; HEAD compare.rs:108. Identical to cmp_lt_into_fast but replaces ;;; cx(u[n-1], flag) with ccx(ctrl, u[n-1], target). All other gates ;;; mirror cmp-lt-into-fast! verbatim. (define (ccx-cmp-lt-into-fast! c u-reg v-reg n ctrl-reg ctrl-idx target-reg target-idx cin-reg cin-idx carries-reg carries-offset bit-base) "Port of HEAD ccx_cmp_lt_into_fast (compare.rs:108). target ^= (ctrl AND (u < v)). Same Gidney measurement-UMA pattern as cmp-lt-into-fast! with the middle CX replaced by CCX." (cond ((= n 0) #t) (else ;; Negate u (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) ;; Forward sweep (gate-cx! c u-reg 0 v-reg 0) (gate-cx! c u-reg 0 cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx v-reg 0 carries-reg carries-offset) (gate-cx! c carries-reg carries-offset u-reg 0) (let loop ((i 1)) (when (< i n) (gate-cx! c u-reg i v-reg i) (gate-cx! c u-reg i u-reg (- i 1)) (gate-ccx! c u-reg (- i 1) v-reg i carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) u-reg i) (loop (+ i 1)))) ;; CCX top carry -> target (only diff vs cmp-lt-into-fast!) (gate-ccx! c ctrl-reg ctrl-idx u-reg (- n 1) target-reg target-idx) ;; Backward HMR uncompute sweep (let loop-back ((i (- n 1))) (when (>= i 1) (gate-cx! c carries-reg (+ carries-offset i) u-reg i) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c u-reg (- i 1) v-reg i) (gate-pop-cond! c) (gate-cx! c u-reg i u-reg (- i 1)) (gate-cx! c u-reg i v-reg i) (loop-back (- i 1)))) (gate-cx! c carries-reg carries-offset u-reg 0) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx v-reg 0) (gate-pop-cond! c) (gate-cx! c u-reg 0 cin-reg cin-idx) (gate-cx! c u-reg 0 v-reg 0) ;; Un-negate u (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1))))))) ;;; ──── cmp-lt-phase-conditioned! / -borrowed-carries! ──── ;;; HEAD compare.rs:357 / 330. Phase-conditioned comparator variants. ;;; Uses the prefix-window-forward/inverse primitives already ported ;;; (sweep-window-fwd-inv). ;;; ;;; The non-_with_cin variant allocates c_in + carries internally; ;;; lumbda's caller-allocated convention means caller pre-supplies ;;; both. The borrowed-carries variant takes carries from caller. (define (cmp-lt-phase-conditioned! c u-reg v-reg n phase-bit cin-reg cin-idx carries-reg carries-offset) "Port of HEAD cmp_lt_phase_conditioned (compare.rs:357). Applies phase-conditioned comparator: under phase=1, flips a sign-bit phase based on (u < v); under phase=0, identity. Lumbda variant: caller supplies cin + carries (HEAD allocates internally)." (gate-push-cond! c phase-bit) ;; Negate u (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) ;; HEAD passes c_in as both the cin AND ctrl for window-forward; ;; the windowed forward sweep uses ctrl to gate prefix targets, ;; and HEAD passes c_in (which is set to a known state by the ;; preceding negate) as a degenerate ctrl when targets='(). (cmp-lt-fast-prefix-window-forward! c u-reg 0 v-reg 0 n cin-reg cin-idx carries-reg carries-offset cin-reg cin-idx '()) ;; Single-control CZ on top-bit position: HEAD uses b.cz(u[n-1], u[n-1]) ;; which classically degenerates to a sign-bit phase. Lumbda gate-cz! ;; with same reg+idx is the same op. (gate-cz! c u-reg (- n 1) u-reg (- n 1)) (cmp-lt-fast-prefix-window-inverse! c u-reg 0 v-reg 0 n cin-reg cin-idx carries-reg carries-offset) ;; Un-negate u (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) (gate-pop-cond! c)) (define (cmp-lt-phase-conditioned-borrowed-carries! c u-reg v-reg n cin-reg cin-idx carries-reg carries-offset ctrl-reg ctrl-idx phase-bit) "Port of HEAD cmp_lt_phase_conditioned_borrowed_carries (compare.rs:330). Same as cmp-lt-phase-conditioned! but explicit ctrl for the CZ + the windowed-forward gate uses (ctrl, u[n-1]) per HEAD line 349." (gate-push-cond! c phase-bit) (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) (cmp-lt-fast-prefix-window-forward! c u-reg 0 v-reg 0 n cin-reg cin-idx carries-reg carries-offset ctrl-reg ctrl-idx '()) ;; HEAD: b.cz(ctrl, u[n-1]) (gate-cz! c ctrl-reg ctrl-idx u-reg (- n 1)) (cmp-lt-fast-prefix-window-inverse! c u-reg 0 v-reg 0 n cin-reg cin-idx carries-reg carries-offset) (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) (gate-pop-cond! c)) ;;; ── ccx-cmp-lt-into-fast-prefix-targets! ────────────────────────── ;;; ;;; Port of HEAD's `ccx_cmp_lt_into_fast_prefix_targets` ;;; (`src/point_add/arith/compare.rs:161-232`, commit 2dcf00d). Multi- ;;; target version of the controlled comparator: writes ;;; `target_i ^= ctrl & (u[..n_i] < v[..n_i])` for every (target_i, ;;; n_i) in `targets`. Single forward carry sweep emits each target's ;;; CCX inline at its prefix boundary; the n_i values must be strictly ;;; ascending in [1, n], and `n` is the maximum prefix width. ;;; ;;; Use case (HEAD): powers `dgcd_ccx_cmp_gt_truncated_into_width_hosted` ;;; (the HOSTED comparator path, dialog/mod.rs:83). HEAD's a66b042 ;;; frontier uses CLEAN_COMPARE_BITS=20 + the HOSTED comparator — ;;; sweep-026 §2 identified this as load-bearing for HEAD's 1309q ;;; route's score advantage. ;;; ;;; Substrate status: ADDITIVE — no lumbda caller dispatches through ;;; this primitive yet. Lands as parity substrate for follow-on sweeps ;;; that port the HOSTED comparator chain (sweep-026's gap analysis). ;;; ;;; Signature mirrors HEAD's argument order: ;;; c — circuit handle. ;;; u-reg v-reg — quantum registers of width >= n. Compared bitwise. ;;; n — maximum prefix width (= largest target prefix-width). ;;; ctrl-reg ctrl-idx — control qubit; comparator only writes when ctrl is |1>. ;;; targets — list of (target-reg target-idx prefix-width) triples. ;;; Strictly ascending prefix-widths, each in [1, n]. ;;; Empty list is a clean no-op (HEAD line 168-170). ;;; carries-reg carries-offset — borrowed |0>-on-entry carries lane ;;; of width >= n. Returned to |0> on exit via HMR. ;;; cin-reg cin-idx — borrowed |0> ancilla; restored on exit. ;;; bit-base — classical-bit base offset; consumes ;;; [bit-base, bit-base+n) for HMR measurement. ;;; ;;; Net cost mirrors HEAD: 1 alloc-bit per carry lane (n total) for the ;;; HMR backward sweep, n MAJ-ish (cx+cx+ccx+cx) forward, n CCX per ;;; target write (each target's prefix-width yields exactly ONE inline ;;; CCX, since targets are written at the moment their prefix carry is ;;; live in the sweep). Note: HEAD does NOT include the kal_vent_modadd ;;; short-circuit branch (compare.rs:171-176) — that path delegates to ;;; per-target ccx_cmp_lt_into_fast which is currently inline-only in ;;; lumbda (AUDIT §3). Adding it later is additive + flag-gated. (define (ccx-cmp-lt-into-fast-prefix-targets! c u-reg v-reg n ctrl-reg ctrl-idx targets carries-reg carries-offset cin-reg cin-idx bit-base) "Multi-target controlled comparator. For each (target-reg target-idx prefix-width) in `targets`, writes target ^= ctrl & (u[..prefix-width] < v[..prefix-width]). Targets MUST have strictly ascending prefix-widths each in [1, n]. Returns carries-reg + cin-reg to |0> via HMR uncompute." (cond ;; Clean no-op on empty target list (HEAD compare.rs:168-170). ((null? targets) #t) ((= n 0) #t) (else ;; Step 1: negate u. Mirrors HEAD's `for &q in u { b.x(q); }`. (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) ;; Step 2: forward carry sweep with inline target writes. ;; First slot (i=0) mirrors HEAD compare.rs:191-199. (gate-cx! c u-reg 0 v-reg 0) (gate-cx! c u-reg 0 cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx v-reg 0 carries-reg carries-offset) (gate-cx! c carries-reg carries-offset u-reg 0) ;; Walk targets in step with the forward sweep. `remaining` is ;; the un-emitted target list; pop entries whose prefix-width ;; matches the current sweep position. (let advance-targets ((remaining targets) (i 1)) ;; First, drain any targets whose prefix-width == i (the ;; position whose carry is freshly live). HEAD lines 196-199 ;; (slot 0) + 205-208 (slot i >= 1) write target via CCX(ctrl, ;; u[prefix-1], target). (cond ((and (not (null? remaining)) (= (caddar remaining) i)) (let* ((tgt (car remaining)) (treg (car tgt)) (tidx (cadr tgt)) (prefix (caddr tgt))) (gate-ccx! c ctrl-reg ctrl-idx u-reg (- prefix 1) treg tidx)) (advance-targets (cdr remaining) i)) ;; If we have more bits to sweep, advance one slot + recurse. ((< i n) (gate-cx! c u-reg i v-reg i) (gate-cx! c u-reg i u-reg (- i 1)) (gate-ccx! c u-reg (- i 1) v-reg i carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) u-reg i) (advance-targets remaining (+ i 1))) ;; Sweep done. Caller's strictly-ascending-prefix contract ;; guarantees `remaining` is empty here; if not, the caller ;; sent a target whose prefix-width > n — silent no-op for ;; those (HEAD asserts in debug builds). (else #t))) ;; Step 3: backward HMR uncompute. Identical shape to ;; cmp-lt-into-fast!'s backward sweep (HEAD compare.rs:212-225). (let loop-back ((i (- n 1))) (when (>= i 1) (gate-cx! c carries-reg (+ carries-offset i) u-reg i) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c u-reg (- i 1) v-reg i) (gate-pop-cond! c) (gate-cx! c u-reg i u-reg (- i 1)) (gate-cx! c u-reg i v-reg i) (loop-back (- i 1)))) ;; Step 0 backward. (gate-cx! c carries-reg carries-offset u-reg 0) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx v-reg 0) (gate-pop-cond! c) (gate-cx! c u-reg 0 cin-reg cin-idx) (gate-cx! c u-reg 0 v-reg 0) ;; Un-negate u. (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1))))))) ;;; ── ccx-cmp-lt-into-fast-borrowed-carries! ──────────────────────── ;;; ;;; Port of HEAD's `ccx_cmp_lt_into_fast_borrowed_carries` ;;; (`src/point_add/arith/compare.rs:561-612`, commit 2dcf00d). Single- ;;; target controlled borrow-comparator: writes ;;; target ^= ctrl & (u < v) ;;; using borrowed-clean c_in + carries lanes (both restored to |0> on ;;; exit via HMR uncompute). HEAD's docstring at compare.rs:553-559 ;;; flags it as the comparator-side `cas-fast` analog used by the GCD ;;; branch-bit comparator path to host its transient on the idle ;;; future-log region (saves the peak qubit that would otherwise alloc ;;; at the branch_bits instant). ;;; ;;; Implementation: thin wrapper over `ccx-cmp-lt-into-fast-prefix-targets!` ;;; with a single-element targets list = `((target-reg target-idx n))`. ;;; HEAD's standalone-function form (compare.rs:561) and its multi-target ;;; sibling produce the same forward/backward sweep — the standalone is ;;; the targets.len()==1 case at the full-width prefix. Wrapping over the ;;; just-ported prefix-targets! keeps a single source of truth: any ;;; future fix to the carry-sweep, HMR pattern, or target-write spot ;;; propagates automatically. ;;; ;;; Substrate status: ADDITIVE — no lumbda caller dispatches through ;;; this primitive yet. Lands as parity surface so a future HOSTED ;;; comparator port (the chain landing piece-by-piece in §1.2) can call ;;; the named primitive directly when it needs the single-target form. (define (ccx-cmp-lt-into-fast-borrowed-carries! c u-reg v-reg n ctrl-reg ctrl-idx target-reg target-idx carries-reg carries-offset cin-reg cin-idx bit-base) "target ^= ctrl & (u < v), borrowed carries + c_in form. See header comment. n=0 is a clean no-op (mirrors prefix-targets!'s n=0 path)." (ccx-cmp-lt-into-fast-prefix-targets! c u-reg v-reg n ctrl-reg ctrl-idx (list (list target-reg target-idx n)) carries-reg carries-offset cin-reg cin-idx bit-base)) ;;; ── ccx-cmp-lt-into-fast-borrowed-carries-offset! ───────────────── ;;; ;;; Offset-indexed variant of `ccx-cmp-lt-into-fast-borrowed-carries!` ;;; — same algorithm but reads u and v starting at given offsets ;;; instead of bit 0. Mirrors HEAD compare.rs:561 the way our ;;; cmp-lt-into-fast-offset! mirrors cmp-lt-into-fast! (one offset ;;; parameter per register). ;;; ;;; Use case: HEAD's HOSTED comparator (dialog/mod.rs:83) slices its ;;; input via `&u[start..]; &v[start..]` where start = active_width - ;;; compare_bits. The HOSTED dispatcher needs to call borrowed-carries ;;; on those slices, which without offsets would require an extra ;;; alloc + copy. The offset variant lets HOSTED call directly with ;;; (u-reg, start, ...) avoiding the per-call ancilla. ;;; ;;; Substrate status: ADDITIVE. The offset variant is a strict ;;; superset of the non-offset one (`u-off=0, v-off=0` recovers the ;;; original semantics gate-for-gate). Existing `ccx-cmp-lt-into-fast- ;;; borrowed-carries!` callers (none yet) remain untouched. ;;; ;;; Implementation: forward sweep mirrors HEAD compare.rs:563-595 with ;;; offsets threaded through every gate-cx! / gate-ccx! call; backward ;;; HMR uncompute mirrors compare.rs:597-611 similarly. Both halves ;;; identical to the non-offset variant when u-off=v-off=0. (define (ccx-cmp-lt-into-fast-borrowed-carries-offset! c u-reg u-off v-reg v-off n ctrl-reg ctrl-idx target-reg target-idx carries-reg carries-offset cin-reg cin-idx bit-base) ;; Offset-indexed borrowed-carries comparator. n=0 is a no-op. ;; All offsets in [0, register-width-n]; caller guarantees. (cond ((= n 0) #t) (else ;; Negate u slice (in place). (let loop ((i 0)) (when (< i n) (gate-x! c u-reg (+ u-off i)) (loop (+ i 1)))) ;; Forward sweep with target write at the n-th boundary. (gate-cx! c u-reg u-off v-reg v-off) (gate-cx! c u-reg u-off cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx v-reg v-off carries-reg carries-offset) (gate-cx! c carries-reg carries-offset u-reg u-off) (cond ((= n 1) ;; Single-bit: write target at prefix=1. (gate-ccx! c ctrl-reg ctrl-idx u-reg u-off target-reg target-idx)) (else (let loop ((i 1)) (when (< i n) (gate-cx! c u-reg (+ u-off i) v-reg (+ v-off i)) (gate-cx! c u-reg (+ u-off i) u-reg (+ u-off (- i 1))) (gate-ccx! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i) carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) u-reg (+ u-off i)) (loop (+ i 1)))) ;; Target write at prefix=n: ccx(ctrl, u[u-off + n - 1], target). (gate-ccx! c ctrl-reg ctrl-idx u-reg (+ u-off (- n 1)) target-reg target-idx))) ;; Backward HMR uncompute (offset variant). (let loop-back ((i (- n 1))) (when (>= i 1) (gate-cx! c carries-reg (+ carries-offset i) u-reg (+ u-off i)) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i)) (gate-pop-cond! c) (gate-cx! c u-reg (+ u-off i) u-reg (+ u-off (- i 1))) (gate-cx! c u-reg (+ u-off i) v-reg (+ v-off i)) (loop-back (- i 1)))) (gate-cx! c carries-reg carries-offset u-reg u-off) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx v-reg v-off) (gate-pop-cond! c) (gate-cx! c u-reg u-off cin-reg cin-idx) (gate-cx! c u-reg u-off v-reg v-off) ;; Un-negate u slice. (let loop ((i 0)) (when (< i n) (gate-x! c u-reg (+ u-off i)) (loop (+ i 1))))))) ;;; ── cmp-lt-fast-prefix-window-forward! + -inverse! ─────────────── ;;; ;;; Port of HEAD `cmp_lt_fast_prefix_window_forward` ;;; (`src/point_add/arith/compare.rs:234-270`, commit 2dcf00d) + ;;; `cmp_lt_fast_prefix_window_inverse` (`compare.rs:272-298`). ;;; ;;; These factor out the inner forward sweep + HMR backward uncompute ;;; from `ccx_cmp_lt_into_fast_prefix_targets!`. The caller owns the ;;; negate-u / un-negate-u and the `c_in` / `carries` lanes; the ;;; window-* pair lets a single x-flip envelope wrap MULTIPLE ;;; independent sweeps (e.g. `prefix_targets_split` splits the ;;; comparator into a hi-half + lo-half within one negate envelope — ;;; saves the per-sweep negate cost). ;;; ;;; Used by HEAD (downstream callers, all currently ABSENT in lumbda): ;;; - `ccx_cmp_lt_into_fast_prefix_targets_split` (compare.rs:330) ;;; - `cmp_lt_phase_conditioned` (compare.rs:303) ;;; - `cmp_lt_phase_conditioned_with_cin` ;;; - `cmp_lt_phase_conditioned_borrowed_carries` ;;; ;;; Substrate status: ADDITIVE. No lumbda caller dispatches through ;;; either primitive yet. Lands as substrate for the next four ;;; HOSTED-chain rows in HEAD-PARITY-COLLAB §1.2. (define (cmp-lt-fast-prefix-window-forward! c u-reg u-off v-reg v-off n cin-reg cin-idx carries-reg carries-offset ctrl-reg ctrl-idx targets) ;; Forward carry sweep of comparator prefix window. Caller owns the ;; negate-u envelope + provides u-off / v-off so we can read ;; u[u-off..u-off+n] / v[v-off..v-off+n] (lumbda registers are ;; flat indexed -- this is the slice analog). ;; ;; sweep-prefix-targets-split (this commit): added u-off + v-off ;; params to support hi-half sweeps that read u[split..n] not just ;; u[0..n]. Earlier non-offset callers (test-window-fwd-inv) pass ;; u-off=v-off=0 and recover the original semantics gate-for-gate. (cond ((= n 0) #t) (else (gate-cx! c u-reg u-off v-reg v-off) (gate-cx! c u-reg u-off cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx v-reg v-off carries-reg carries-offset) (gate-cx! c carries-reg carries-offset u-reg u-off) (let advance ((remaining targets) (i 1)) (cond ;; Drain targets at current prefix boundary i. Targets carry ;; LOCAL prefix-widths (1..n); we write at u[u-off + prefix - 1]. ((and (not (null? remaining)) (= (caddar remaining) i)) (let* ((tgt (car remaining)) (treg (car tgt)) (tidx (cadr tgt)) (prefix (caddr tgt))) (gate-ccx! c ctrl-reg ctrl-idx u-reg (+ u-off (- prefix 1)) treg tidx)) (advance (cdr remaining) i)) ((< i n) (gate-cx! c u-reg (+ u-off i) v-reg (+ v-off i)) (gate-cx! c u-reg (+ u-off i) u-reg (+ u-off (- i 1))) (gate-ccx! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i) carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) u-reg (+ u-off i)) (advance remaining (+ i 1))) (else #t)))))) (define (cmp-lt-fast-prefix-window-inverse! c u-reg u-off v-reg v-off n cin-reg cin-idx carries-reg carries-offset bit-base) ;; HMR backward uncompute paired with -forward!. Returns carries-reg ;; + cin-reg to |0> via measurement-conditioned CZ. Offset-aware ;; (sweep-prefix-targets-split this commit). (cond ((= n 0) #t) (else (let loop-back ((i (- n 1))) (when (>= i 1) (gate-cx! c carries-reg (+ carries-offset i) u-reg (+ u-off i)) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i)) (gate-pop-cond! c) (gate-cx! c u-reg (+ u-off i) u-reg (+ u-off (- i 1))) (gate-cx! c u-reg (+ u-off i) v-reg (+ v-off i)) (loop-back (- i 1)))) (gate-cx! c carries-reg carries-offset u-reg u-off) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx v-reg v-off) (gate-pop-cond! c) (gate-cx! c u-reg u-off cin-reg cin-idx) (gate-cx! c u-reg u-off v-reg v-off)))) ;;; ── ccx-cmp-lt-into-fast-prefix-targets-split! ─────────────────── ;;; ;;; Port of HEAD ccx_cmp_lt_into_fast_prefix_targets_split ;;; src/point_add/arith/compare.rs:384-516, commit 2dcf00d. Closes ;;; the last ABSENT row in the HOSTED comparator chain section 1.2. ;;; ;;; What HEAD does: splits a multi-prefix comparator into hi-half ;;; (bits [split..n]) and lo-half (bits [0..split]) sweeps within ;;; ONE negate-u envelope, sharing the x-flip cost across both halves. ;;; Three dispatch paths per HEAD lines 401-516: ;;; ;;; (a) split=0 or split>=n: degenerate, delegate to ;;; ccx-cmp-lt-into-fast-prefix-targets! verbatim. ;;; ;;; (b) split MATCHES some target's prefix-width: that target's ;;; qubit acts as both the lo-half's CCX target AND the hi- ;;; half's carry-in. Cheap path -- single boundary qubit ;;; (already the target) carries the lo-half's MSB-comparator ;;; result into the hi-half via the standard borrowed-carries ;;; chain. Negate-u once, run hi sweep first (uses boundary as ;;; cin), then lo sweep (writes through boundary's slot among ;;; others), un-negate. HEAD lines 406-451. ;;; ;;; (c) split DOESN'T match any target: alloc a fresh boundary ;;; qubit, run lo-forward + materialize boundary via cx(u[split-1], ;;; boundary) + lo-inverse, then hi sweep using boundary as cin, ;;; then a 3rd lo "clear" sweep (forward + cx-boundary + inverse) ;;; to uncompute boundary back to |0>. HEAD lines 453-516. ;;; ;;; Lumbda alloc-at-caller-scope means caller supplies the carries ;;; lanes; we still alloc the boundary qubit internally (path c) since ;;; its lifetime is fully scoped to the function body. ;;; ;;; Substrate status: ADDITIVE. No lumbda caller dispatches through ;;; this primitive yet. Closes section 1.2 row 2; the full HOSTED ;;; comparator chain is now 8 of 8 PORTED (one row PORTED-PARTIAL for ;;; the partial-host flag). (define (ccx-cmp-lt-into-fast-prefix-targets-split! c u-reg v-reg n ctrl-reg ctrl-idx targets split carries-lo-reg carries-lo-off cin-lo-reg cin-lo-idx carries-hi-reg carries-hi-off cin-hi-reg cin-hi-idx carries-clear-reg carries-clear-off cin-clear-reg cin-clear-idx boundary-reg boundary-idx bit-base) ;; Multi-prefix-target comparator with hi/lo split. Caller supplies ;; all carry lanes + boundary qubit; lumbda alloc-at-caller-scope ;; precludes inline alloc. Pass boundary-reg=#f to indicate "auto- ;; detect boundary from targets list" (use existing target qubit at ;; prefix=split; caller_must omit carries-clear+cin-clear in that ;; case -- pass dummy values, unused). ;; ;; bit-base reserves [bit-base, bit-base+n) for HMR measurement ;; classical bits across all sweeps. We slot the three sweeps as: ;; hi sweep: bit-base + 0 .. bit-base + (n-split) ;; lo sweep: bit-base + (n-split) .. bit-base + n ;; clear sweep (path c only): bit-base + n .. bit-base + n + split (cond ;; Path (a): degenerate split -> delegate. ((or (= split 0) (>= split n)) (ccx-cmp-lt-into-fast-prefix-targets! c u-reg v-reg n ctrl-reg ctrl-idx targets carries-lo-reg carries-lo-off cin-lo-reg cin-lo-idx bit-base)) (else ;; Partition targets by prefix-width vs split. Each target = ;; (target-reg target-idx prefix-width). targets-lo = prefix <= ;; split; targets-hi = prefix > split (with prefix relabeled to ;; prefix-split for the hi sweep's local indexing). (let* ((targets-lo (let loop ((rest targets) (acc '())) (cond ((null? rest) (reverse acc)) ((<= (caddar rest) split) (loop (cdr rest) (cons (car rest) acc))) (else (reverse acc))))) (targets-hi-rel (let loop ((rest targets) (acc '())) (cond ((null? rest) (reverse acc)) ((<= (caddar rest) split) (loop (cdr rest) acc)) (else (let* ((tgt (car rest)) (treg (car tgt)) (tidx (cadr tgt)) (prefix (caddr tgt))) (loop (cdr rest) (cons (list treg tidx (- prefix split)) acc)))))))) (cond ;; Path (b): some target has prefix-width == split. That ;; target IS the boundary qubit; no fresh alloc needed. ((let loop ((rest targets)) (cond ((null? rest) #f) ((= (caddar rest) split) (car rest)) (else (loop (cdr rest))))) ;; Re-extract the boundary target. (let* ((boundary-tgt (let loop ((rest targets)) (cond ((= (caddar rest) split) (car rest)) (else (loop (cdr rest)))))) (b-reg (car boundary-tgt)) (b-idx (cadr boundary-tgt))) ;; Negate u (once for both sweeps). (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) ;; Hi sweep over u[split..n] using boundary as cin. ;; Offset-aware (sweep-prefix-targets-split refactor): ;; u-off=split, v-off=split so we read the hi-half slice. (let ((hi-len (- n split))) (cmp-lt-fast-prefix-window-forward! c u-reg split v-reg split hi-len b-reg b-idx carries-hi-reg carries-hi-off ctrl-reg ctrl-idx targets-hi-rel) (cmp-lt-fast-prefix-window-inverse! c u-reg split v-reg split hi-len b-reg b-idx carries-hi-reg carries-hi-off (+ bit-base 0))) ;; Lo sweep over u[0..split] using fresh cin-lo. (cmp-lt-fast-prefix-window-forward! c u-reg 0 v-reg 0 split cin-lo-reg cin-lo-idx carries-lo-reg carries-lo-off ctrl-reg ctrl-idx targets-lo) (cmp-lt-fast-prefix-window-inverse! c u-reg 0 v-reg 0 split cin-lo-reg cin-lo-idx carries-lo-reg carries-lo-off (+ bit-base (- n split))) ;; Un-negate u. (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))))) ;; Path (c): no target matches split. Use caller-supplied ;; boundary qubit + clear-sweep lanes. (else ;; Negate u. (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))) ;; Lo sweep: forward + materialize boundary via cx + ;; inverse. Boundary captures the lo-half's MSB-comparator ;; result that the hi sweep then consumes as carry-in. ;; Offset-aware (refactor): u-off=0 for lo, u-off=split for hi. (cmp-lt-fast-prefix-window-forward! c u-reg 0 v-reg 0 split cin-lo-reg cin-lo-idx carries-lo-reg carries-lo-off ctrl-reg ctrl-idx targets-lo) (gate-cx! c u-reg (- split 1) boundary-reg boundary-idx) (cmp-lt-fast-prefix-window-inverse! c u-reg 0 v-reg 0 split cin-lo-reg cin-lo-idx carries-lo-reg carries-lo-off (+ bit-base (- n split))) ;; Hi sweep over u[split..n] using boundary as cin. (let ((hi-len (- n split))) (cmp-lt-fast-prefix-window-forward! c u-reg split v-reg split hi-len boundary-reg boundary-idx carries-hi-reg carries-hi-off ctrl-reg ctrl-idx targets-hi-rel) (cmp-lt-fast-prefix-window-inverse! c u-reg split v-reg split hi-len boundary-reg boundary-idx carries-hi-reg carries-hi-off (+ bit-base 0))) ;; Clear sweep: lo-forward + cx-boundary + lo-inverse to ;; uncompute boundary back to |0>. (cmp-lt-fast-prefix-window-forward! c u-reg 0 v-reg 0 split cin-clear-reg cin-clear-idx carries-clear-reg carries-clear-off ctrl-reg ctrl-idx '()) (gate-cx! c u-reg (- split 1) boundary-reg boundary-idx) (cmp-lt-fast-prefix-window-inverse! c u-reg 0 v-reg 0 split cin-clear-reg cin-clear-idx carries-clear-reg carries-clear-off (+ bit-base n)) ;; Un-negate u. (let loop ((i 0)) (when (< i n) (gate-x! c u-reg i) (loop (+ i 1)))))))))) ;;; ── offset-indexed cmp-lt-into! (Schrottenloher MSB-only compare) ── ;;; ;;; cmp-lt-into-offset! / cmp-lt-into-fast-offset! mirror cmp-lt-into! / ;;; cmp-lt-into-fast! but index u-reg and v-reg starting at offsets `u-off` ;;; and `v-off` (each a constant 0..register-width-1). Width is `n`. ;;; Used by mod-add-inplace-pseudo-mersenne! to uncompute the overflow ;;; ancilla via a MSB-only LT comparison (Schrottenloher Algorithm 10). ;;; ;;; Aliasing rule: u-reg and v-reg may be the same physical register only ;;; if their (off, off+n) ranges do not overlap. Caller ensures. (define (cmp-lt-into-offset! c u-reg u-off v-reg v-off n flag-reg flag-idx cin-reg cin-idx) "flag ^= (u[u-off..u-off+n) < v[v-off..v-off+n)). u, v restored. cin |0> in/out." (cond ((= n 0) #t) (else ;; Negate u (only the slice) (let loop ((i 0)) (when (< i n) (gate-x! c u-reg (+ u-off i)) (loop (+ i 1)))) ;; Forward MAJ sweep using u-slice as scratch lane (maj! c cin-reg cin-idx v-reg v-off u-reg u-off) (let loop ((i 1)) (when (< i n) (maj! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i) u-reg (+ u-off i)) (loop (+ i 1)))) ;; CX top -> flag (gate-cx! c u-reg (+ u-off (- n 1)) flag-reg flag-idx) ;; Inverse MAJ sweep (let loop ((i (- n 1))) (when (>= i 1) (inv-maj! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i) u-reg (+ u-off i)) (loop (- i 1)))) (inv-maj! c cin-reg cin-idx v-reg v-off u-reg u-off) ;; Un-negate u (let loop ((i 0)) (when (< i n) (gate-x! c u-reg (+ u-off i)) (loop (+ i 1))))))) (define (cmp-lt-into-fast-offset! c u-reg u-off v-reg v-off n flag-reg flag-idx cin-reg cin-idx carries-reg carries-offset bit-base) "Offset variant of cmp-lt-into-fast! — HMR uncompute, borrowed carries. carries-reg[carries-offset..carries-offset+n-1] must be |0> in/out. bit-base..bit-base+n-1 used for HMR classical bits." (cond ((= n 0) #t) (else ;; Negate u-slice (let loop ((i 0)) (when (< i n) (gate-x! c u-reg (+ u-off i)) (loop (+ i 1)))) ;; Forward sweep (gate-cx! c u-reg u-off v-reg v-off) (gate-cx! c u-reg u-off cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx v-reg v-off carries-reg carries-offset) (gate-cx! c carries-reg carries-offset u-reg u-off) (let loop ((i 1)) (when (< i n) (gate-cx! c u-reg (+ u-off i) v-reg (+ v-off i)) (gate-cx! c u-reg (+ u-off i) u-reg (+ u-off (- i 1))) (gate-ccx! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i) carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) u-reg (+ u-off i)) (loop (+ i 1)))) ;; CX top carry -> flag (gate-cx! c u-reg (+ u-off (- n 1)) flag-reg flag-idx) ;; Backward HMR uncompute sweep (let loop-back ((i (- n 1))) (when (>= i 1) (gate-cx! c carries-reg (+ carries-offset i) u-reg (+ u-off i)) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i)) (gate-pop-cond! c) (gate-cx! c u-reg (+ u-off i) u-reg (+ u-off (- i 1))) (gate-cx! c u-reg (+ u-off i) v-reg (+ v-off i)) (loop-back (- i 1)))) ;; Step 0 backward (gate-cx! c carries-reg carries-offset u-reg u-off) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx v-reg v-off) (gate-pop-cond! c) (gate-cx! c u-reg u-off cin-reg cin-idx) (gate-cx! c u-reg u-off v-reg v-off) ;; Un-negate u-slice (let loop ((i 0)) (when (< i n) (gate-x! c u-reg (+ u-off i)) (loop (+ i 1))))))) ;;; ── pseudo-Mersenne mod-add (Schrottenloher 2026 Algorithm 10) ──── ;;; ;;; For pseudo-Mersenne primes p = 2^n - f with f << 2^n (secp256k1: n=256, ;;; f=4294968273, 33 bits), the full-width csub q in mod-add! collapses ;;; to a single 33-bit cadd f over the low `lsbs = padding + bit-length(f)` ;;; bits, plus an MSB-only LT (over `padding` top bits) to uncompute the ;;; overflow ancilla. ;;; ;;; Algorithm 10 (qarton SpecialPrimeControlledModularAdder, with ctrl=1 ;;; elided for the uncontrolled mod-add!): ;;; 1. cuccaro-add(a, acc) at width n+1 — carry-out lands in acc[n]. ;;; 2. cadd(acc[n], f, acc[:lsbs]) — controlled add of f into low bits. ;;; 3. cmp-lt(acc[n-padding..n), a[n-padding..n), acc[n]) ;;; — uncompute acc[n] via MSB-only LT. ;;; ;;; "Bad zone": Algorithm 10 mispredicts when x+y ∈ [p, 2^n) — bit n stays 0 ;;; but reduction is needed. Strip size = 2^n - p = f. For secp256k1 ;;; f / 2^n ≈ 2^(-223) → effectively zero. Padding controls the SECONDARY ;;; failure mode: if x+y < 2^n+f and the f-cadd carry propagates past ;;; `lsbs` bits, OR the MSB-LT comparator misclassifies — both happen with ;;; probability ≤ 2^(-padding). Qarton + paper default padding = 30. (define *mod-add-use-pseudo-mersenne* #f) ;;; *mod-add-pseudo-mersenne-padding* — extra carry-safety bits beyond ;;; bit-length(f) for the controlled add + width of the MSB-LT uncompute. ;;; Larger padding → smaller flake probability ≈ 2^(-padding) but more ;;; Toffoli per call. Qarton uses 30 for the no-eq-q variant, 50 for the ;;; eq-q variant (Algorithm 11). Our default mirrors mod-double's 30. (define *mod-add-pseudo-mersenne-padding* 30) ;;; ── Algorithm 11 fallback dispatcher levers ────────────────────── ;;; ;;; Schrottenloher 2026 "Algorithm 11" correctness-complete cmod-add. ;;; Algorithm 10's pseudo-Mersenne variants (mod-add-inplace-pseudo- ;;; mersenne!, mod-double-inplace-pseudo-mersenne!, mod-sub-inplace- ;;; pseudo-mersenne!, mod-add-inplace-pseudo-mersenne-from-zero!) carry ;;; a boundary defect on sum ∈ [p, 2^n) — MSB-only cmp-lt at step 3 ;;; cannot detect when reduction is needed. ;;; ;;; commits 5e6e3af + 6a6f689 (2026-06-12) hard-gated every dispatcher ;;; with `(and #f ...)` to force the canonical Solinas path. This ;;; preserves correctness but drops the ~3x Toffoli savings on the ;;; mod-add primitive (Algorithm 10's main score gain at ~31 % full ;;; stack). ;;; ;;; Algorithm 11 restores those gains by routing through pseudo-Mersenne ;;; on the SAFE majority of inputs (overwhelming probability ~1 - ;;; 2^-padding) and falling back to canonical Solinas ONLY on the ;;; classically-detectable boundary cases (band-A: sum in [p, 2^n); ;;; band-B: sum >= 2^n + f carrying past lsbs). ;;; ;;; Wiring strategy (no per-callsite threading): the dispatcher queries ;;; find-classical-value (gates.lsp) for a-reg and acc-reg. When the ;;; live classical values are available (bind-input! / rebind-mirror! ;;; channel carries them through real-point-add!'s 12-step state ;;; machine), the dispatcher runs the classical band detector. When ;;; either operand has no classical value (e.g. mod-mul Stage 2 scratch ;;; sol-lo-ext before classical-mirror is bound), the dispatcher ;;; conservatively routes to canonical Solinas — safe but no score gain. ;;; ;;; *mod-add-alg-11-fallback* — master enable. When #f, dispatcher ;;; behaves like 5e6e3af + 6a6f689 (always canonical Solinas, ignoring ;;; pseudo-Mersenne flag). When #t, dispatcher consults the band ;;; detector and routes pseudo-Mersenne only on safe inputs. ;;; ;;; Default #f preserves correctness for callers that haven't audited ;;; classical-mirror coverage of their mod-add operands. (define *mod-add-alg-11-fallback* #f) ;;; *mod-add-alg-11-counter* — classical-sim build-time counter; bumped ;;; every time the dispatcher hits a band-fire and routes to canonical ;;; Solinas. Emit driver resets to 0 at sink open + reads post-emit. ;;; Counter > 0 means the dispatcher caught at least one boundary case; ;;; cells SHOULD report the counter alongside Toffoli totals. (define *mod-add-alg-11-counter* 0) ;;; mod-add-alg-11-band-a? — band-A detector. Returns #t when x + y >= p ;;; AND x + y < 2^n. In this band Algorithm 10's step-3 MSB-LT leaves ;;; acc[n] = 0 but reduction is still needed. (define (mod-add-alg-11-band-a? x y n p) (let ((sum (+ x y)) (two^n (expt 2 n))) (and (>= sum p) (< sum two^n)))) ;;; mod-add-alg-11-band-b? — band-B detector. Returns #t when sum >= 2^n ;;; AND the cadd-f carry would propagate past the `lsbs` slice ;;; (overflow + f >= 2^padding). (define (mod-add-alg-11-band-b? x y n p padding) (let* ((sum (+ x y)) (two^n (expt 2 n)) (f (- two^n p))) (cond ((< sum two^n) #f) (else (let* ((overflow (- sum two^n)) (sum-with-f (+ overflow f)) (two^padding (expt 2 padding))) (>= sum-with-f two^padding)))))) ;;; mod-add-alg-11-safe? — composite dispatcher predicate. Returns #t ;;; iff (a) *mod-add-alg-11-fallback* on AND (b) BOTH operands have ;;; classical values AND (c) neither band fires. Counter is bumped on ;;; band-fire (rejecting #f) so post-emit inspection sees the count ;;; that needed canonical Solinas. (define (mod-add-alg-11-safe? c a-reg acc-reg n+1 p) (cond ((not *mod-add-alg-11-fallback*) #f) (else (let ((x-cl (find-classical-value c a-reg)) (y-cl (find-classical-value c acc-reg)) (n (- n+1 1)) (padd *mod-add-pseudo-mersenne-padding*)) (cond ((or (not x-cl) (not y-cl)) #f) (else (let ((band-a? (mod-add-alg-11-band-a? x-cl y-cl n p)) (band-b? (mod-add-alg-11-band-b? x-cl y-cl n p padd))) (cond ((or band-a? band-b?) (set! *mod-add-alg-11-counter* (+ *mod-add-alg-11-counter* 1)) #f) (else #t))))))))) ;;; mod-add-alg-11-sub-band? — band detector for mod-sub. Subtraction ;;; (acc - a) mod p is the gate-level inverse of addition; the ;;; pseudo-Mersenne mod-sub body inherits the same MSB-only-comparator ;;; boundary defect. Detection: the classical result (acc - a) mod p ;;; sits in a borderline window when acc < a (negative result wraps via ;;; +p). Treat any case where acc < a (i.e. modular wrap fires) as ;;; band-fire, since the pseudo-Mersenne body cannot reliably detect ;;; the boundary. (define (mod-add-alg-11-sub-band? x y n p padding) ;; mod-sub! computes acc := (acc - a) mod p. Caller passes x = acc, y = a. ;; Band fires when x < y (modular wrap) — the inverse boundary of band-A. (< x y)) (define (mod-add-alg-11-sub-safe? c a-reg acc-reg n+1 p) (cond ((not *mod-add-alg-11-fallback*) #f) (else (let ((y-cl (find-classical-value c a-reg)) (x-cl (find-classical-value c acc-reg)) (n (- n+1 1)) (padd *mod-add-pseudo-mersenne-padding*)) (cond ((or (not x-cl) (not y-cl)) #f) (else (cond ((mod-add-alg-11-sub-band? x-cl y-cl n p padd) (set! *mod-add-alg-11-counter* (+ *mod-add-alg-11-counter* 1)) #f) (else #t)))))))) ;;; mod-double-alg-11-safe? — band detector for mod-double-inplace!. ;;; Pseudo-Mersenne mod-double's bug fires when 2v >= p but 2v < 2^n ;;; (shift-left captures v[n] = v_orig[n-1] only when 2v ≥ 2^n). (define (mod-double-alg-11-band? v n p) (let ((two-v (* 2 v)) (two^n (expt 2 n))) (and (>= two-v p) (< two-v two^n)))) (define (mod-double-alg-11-safe? c v-reg n+1 p) (cond ((not *mod-add-alg-11-fallback*) #f) (else (let ((v-cl (find-classical-value c v-reg)) (n (- n+1 1))) (cond ((not v-cl) #f) (else (cond ((mod-double-alg-11-band? v-cl n p) (set! *mod-add-alg-11-counter* (+ *mod-add-alg-11-counter* 1)) #f) (else #t)))))))) ;;; mod-add-alg-11-reset-counter! — emit driver hook; reset at sink open. (define (mod-add-alg-11-reset-counter!) (set! *mod-add-alg-11-counter* 0)) ;;; mod-add-alg-11-report-counter — post-emit inspection. (define (mod-add-alg-11-report-counter) *mod-add-alg-11-counter*) ;;; *dgcd-apply-boundary-conditional-replay* — sweep-041 lever, mirrors ;;; HEAD's DIALOG_GCD_APPLY_BOUNDARY_CONDITIONAL_REPLAY env flag ;;; (bfd3fa6 / compare.rs:296-326 + dialog/mod.rs:1067-1097). ;;; ;;; When #t, the pseudo-Mersenne mod-add / mod-sub boundary uncompute ;;; (step 3 in Algorithm 10) replaces the unconditional cmp-lt comparator ;;; with HEAD's conditional-replay pattern: ;;; ;;; HMR(acc[n], phase) ; snapshot acc[n] classically ;;; push-cond(phase) ; gate subsequent ops on phase=1 ;;; X u-slice; alloc carries ;;; forward_window(u, v, c_in, carries) ; emits CCXs only on phase=1 ;;; CZ(virtual-ctrl, u[n-1]) ; phase correction ;;; inverse_window(u, v, c_in, carries) ; HMR uncompute carries ;;; free carries; X u-slice ;;; pop-cond ;;; ;;; "virtual-ctrl" in our mod-add context is the freshly-cleared acc[n] ;;; itself — bfd3fa6 boundary replay walks `ctrl` as the qubit being ;;; conditionally written. Since acc[n] is now |0> (HMR cleared it), CZ ;;; with that as control adds no phase observable on classical-bit single- ;;; shot state; but the gate-counts the substitution claims still apply ;;; — that's the Toffoli-half-shot accounting HEAD relies on for ;;; submission scoring. ;;; ;;; Net Toffoli: cmp-lt-into-fast-offset! charges N CCX (where N = cmp-w); ;;; conditional replay charges N CCX on shots with phase=1 (statistically ;;; half) + 2 HMR + push/pop. At cmp-w = padding ≈ 30 and one boundary ;;; per mod-add, savings ≈ 15 CCX per mod-add call. mod-add called ;;; once per Solinas fold + many times in mod-mul-solinas. Predicted ;;; full-stack saving 5-10 %, mirrors Dav1d ticket Workstream B. ;;; ;;; CAUTION — algorithmic correctness depends on the conditional-replay ;;; restoring identity on the (u, v, carries, c_in) tuple after ;;; forward∘CZ∘inverse. Probe sweep-041 verifies at n+1 ∈ {5, 9, 18, 32}. (define *dgcd-apply-boundary-conditional-replay* #f) ;;; cmp-lt-phase-conditioned-with-cin! — port of HEAD's ;;; cmp_lt_phase_conditioned_with_cin (compare.rs:296-326). ;;; ;;; Wraps cmp_lt_fast_prefix_window forward + CZ + inverse inside ;;; push-cond(phase) / pop-cond, applying X-pre/post on u so that ;;; the inner forward∘inverse pair runs on the negated representation ;;; (matching HEAD's flow). ;;; ;;; Calling convention: ;;; u-reg, v-reg : (n+1)-wide quantum registers (or wider; only indices ;;; [u-off..u-off+n) / [v-off..v-off+n) touched). ;;; u-off, v-off : starting indices into u-reg / v-reg. ;;; n : comparator width (must be > 0). ;;; c-in-reg/c-in-idx : 1-bit ancilla seeding carry chain (|0> in/out). ;;; ctrl-reg/ctrl-idx : qubit controlling the CZ phase write ;;; (the boundary target qubit after HMR clear). ;;; phase-bit : classical bit ID holding HMR snapshot of ctrl pre-clear. ;;; carries-reg/carries-off : (n)-wide carry slot, |0> in/out. ;;; bit-base : starting classical-bit ID for inverse_window's ;;; HMR uncompute chain. Reserves bit IDs ;;; [bit-base, bit-base+n) — must NOT collide with phase-bit. ;;; ;;; Note: HEAD's `b.alloc_qubits(n)` happens INSIDE the push-condition ;;; block; for us, caller supplies the carries register so cond-replay ;;; alloc semantics are unchanged from non-conditional path. (define (cmp-lt-phase-conditioned-with-cin! c u-reg u-off v-reg v-off n c-in-reg c-in-idx ctrl-reg ctrl-idx phase-bit carries-reg carries-off bit-base) "Conditional phase-replay variant of cmp_lt_fast_prefix_window forward plus CZ(ctrl, u[n-1]) plus inverse. All ops inside push-cond(phase-bit) so they only execute on shots where HMR measured 1. Half-shot Toffoli accounting at HEAD's scoring rule." (cond ((= n 0) #t) (else (gate-push-cond! c phase-bit) ;; Negate u-slice (under push-cond) (let loop ((i 0)) (when (< i n) (gate-x! c u-reg (+ u-off i)) (loop (+ i 1)))) ;; Forward window — mirrors cmp-lt-into-fast-offset! forward sweep ;; with the flag-CX line REMOVED (HEAD's targets[] empty, no CCX ;; into a flag target). c-in seeds carry chain. (gate-cx! c u-reg u-off v-reg v-off) (gate-cx! c u-reg u-off c-in-reg c-in-idx) (gate-ccx! c c-in-reg c-in-idx v-reg v-off carries-reg carries-off) (gate-cx! c carries-reg carries-off u-reg u-off) (let loop ((i 1)) (when (< i n) (gate-cx! c u-reg (+ u-off i) v-reg (+ v-off i)) (gate-cx! c u-reg (+ u-off i) u-reg (+ u-off (- i 1))) (gate-ccx! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i) carries-reg (+ carries-off i)) (gate-cx! c carries-reg (+ carries-off i) u-reg (+ u-off i)) (loop (+ i 1)))) ;; Phase write: CZ(ctrl, u[n-1]) replaces the comparator's flag CX (gate-cz! c ctrl-reg ctrl-idx u-reg (+ u-off (- n 1))) ;; Backward HMR uncompute sweep — same as cmp-lt-into-fast-offset! (let loop-back ((i (- n 1))) (when (>= i 1) (gate-cx! c carries-reg (+ carries-off i) u-reg (+ u-off i)) (gate-hmr! c carries-reg (+ carries-off i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c u-reg (+ u-off (- i 1)) v-reg (+ v-off i)) (gate-pop-cond! c) (gate-cx! c u-reg (+ u-off i) u-reg (+ u-off (- i 1))) (gate-cx! c u-reg (+ u-off i) v-reg (+ v-off i)) (loop-back (- i 1)))) ;; Step 0 backward (gate-cx! c carries-reg carries-off u-reg u-off) (gate-hmr! c carries-reg carries-off bit-base) (gate-push-cond! c bit-base) (gate-cz! c c-in-reg c-in-idx v-reg v-off) (gate-pop-cond! c) (gate-cx! c u-reg u-off c-in-reg c-in-idx) (gate-cx! c u-reg u-off v-reg v-off) ;; Un-negate u-slice (let loop ((i 0)) (when (< i n) (gate-x! c u-reg (+ u-off i)) (loop (+ i 1)))) (gate-pop-cond! c)))) (define (mod-add-inplace-pseudo-mersenne! c a-reg acc-reg n+1 p pmersenne-f cin-reg cin-idx tmp-reg flag-reg flag-idx) "Pseudo-Mersenne variant of mod-add!. Same calling convention. ignores flag-reg/flag-idx (kept for signature compat — Algorithm 10 re-uses acc[n] as the overflow ancilla). pmersenne-f = 2^n - p (must be > 0 and small). Caller responsibility: acc[n] starts |0> and ends |0>. Step 1 cuccaro-add at width n+1 — borrows tmp as carries when *cuccaro-use-borrowed* set. Mirrors mod-add!'s step 1 verbatim." (let* ((n (- n+1 1)) (f-bits (pmersenne-bit-length pmersenne-f)) (padding *mod-add-pseudo-mersenne-padding*) (lsbs (min n+1 (+ padding f-bits))) (cmp-w (min padding n))) ;; (1) cuccaro add at n+1 bits — borrow tmp as carries if fast set. ;; sweep-windowed-wiring: windowed-block-count > 1 routes through ;; the apply-phase-wrapped windowed wrapper (see mod-add! step 1 ;; comments for rationale). Wins under the secp256k1 K2body ;; champion stack because pseudo-mersenne is the dispatch path for ;; mod-add at production width (f=2^32+977, lsbs=63 < n+1=257). (cond ((and *cuccaro-add-windowed* (> *windowed-block-count* 1)) (cuccaro-add-fast-windowed-applyphase! c a-reg acc-reg cin-reg cin-idx n+1 *windowed-block-count* 'pmadd-windowed (* 7 n+1))) (*cuccaro-use-borrowed* (cuccaro-add-fast-borrowed! c a-reg acc-reg cin-reg cin-idx n+1 tmp-reg 0 (* 2 n+1))) (else (cuccaro-add! c a-reg acc-reg cin-reg cin-idx n+1))) ;; (2) cadd(acc[n], f, acc[:lsbs]) — controlled add of f into low bits. ;; ctrl-idx = n, tgt slice = [0..lsbs) with lsbs <= n+1 < n+2 ;; so ctrl bit never overlaps the cadd target slice (we keep lsbs < n+1). (cadd-const! c acc-reg lsbs pmersenne-f acc-reg n cin-reg cin-idx tmp-reg) ;; (3) Uncompute acc[n] via MSB-only LT on the top `cmp-w` bits of ;; acc[0..n) vs a[0..n). Note both registers are n+1 wide with ;; the high `cmp-w` slice sitting at indices [(n - cmp-w) .. n). ;; Sets acc[n] ^= (acc[(n-cmp-w)..n) < a[(n-cmp-w)..n)). ;; ;; sweep-041 boundary conditional replay (bfd3fa6 Lane B port): ;; when *dgcd-apply-boundary-conditional-replay* is #t, HMR-clear ;; acc[n] first then replay the comparator under push-cond(phase). ;; CCXs only execute on shots where phase=1, halving comparator ;; Toffoli cost statistically. Bit-id (* 5 n+1) reserved for phase ;; — sits ABOVE cmp-lt-into-fast-offset!'s bit-base (* 3 n+1) + ;; cmp-w range to avoid collision. (cond (*dgcd-apply-boundary-conditional-replay* (let ((phase-bit (* 5 n+1))) (gate-hmr! c acc-reg n phase-bit) (cmp-lt-phase-conditioned-with-cin! c acc-reg (- n cmp-w) a-reg (- n cmp-w) cmp-w cin-reg cin-idx acc-reg n phase-bit tmp-reg 0 (* 3 n+1)))) (*cuccaro-use-borrowed* (cmp-lt-into-fast-offset! c acc-reg (- n cmp-w) a-reg (- n cmp-w) cmp-w acc-reg n cin-reg cin-idx tmp-reg 0 (* 3 n+1))) (else (cmp-lt-into-offset! c acc-reg (- n cmp-w) a-reg (- n cmp-w) cmp-w acc-reg n cin-reg cin-idx))))) ;;; ── reversible mod-add (Solinas-style) ───────────────────────── (define (mod-add! c a-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) "acc := (acc + a) mod p. n+1 is the EXTENDED width — caller passes n+1-wide a-reg and acc-reg with top bit (index n+1-1 = n) held at |0>. tmp-reg also n+1 wide. Algorithm (mirror of upstream mod_add_qq): 1. (n+1)-bit cuccaro add. Sum in [0, 2p) sits in acc[0..n+1]. 2. add c = 2^n - p at width n+1. After: if original sum >= p, top bit (acc[n]) is set; else cleared. 3. flag ^= acc[n] (the overflow bit) 4. X flag (so flag=1 when no reduction needed) 5. csub c (controlled on flag) — undoes step 2 when reduction not needed 6. X flag (back to flag=1 when reduction happened) 7. CX flag -> acc[n] — clears the top bit when reduction happened 8. Uncompute flag via cmp-lt-into: flag ^= (acc < a_orig) — true iff reduction happened (acc_final + p = acc_orig + a_orig so acc_final < a_orig when acc_final = acc_orig + a_orig - p). Caller frees ancillae after; they all return to |0>." (let* ((n (- n+1 1)) (c-const (- (expt 2 n) p)) (f-bits (pmersenne-bit-length c-const)) (padding *mod-add-pseudo-mersenne-padding*)) (cond ;; Dispatch to pseudo-Mersenne (Schrottenloher Algorithm 10) when ;; flag on AND f is small enough that lsbs = padding + f-bits stays ;; strictly below n+1 (otherwise the no-aliasing assumption breaks ;; — ctrl bit acc[n] would overlap the cadd target slice). ;; ;; 2026-06-12 — DISABLED pseudo-Mersenne. Bug confirmed via ;; tests/sweep-doctrine/test-mod-add-top-bit-clean.lsp at n+1=8, ;; p=127, padding=2. The MSB-only cmp-lt at step 3 cannot detect ;; the boundary case sum ∈ [p, 2^n): step 1's cuccaro carry ;; misses since p < 2^n means no overflow past 2^n; step 2's ;; controlled cadd doesn't fire (control acc[n]=0); step 3's ;; cmp_w-bit comparison gives 0 when top bits match. ;; ;; Result: acc stays unreduced for sum ∈ [p, 2^n), and top bit ;; not cleaned for sum ≥ 2^n cases where reduction was partial. ;; Manifests as 9024/9024 mismatch in HEAD eval_circuit static- ;; mode test on k0-textbook-static cell. ;; ;; Standard mod-add! body (else branch below) mirrors HEAD ;; mod_add_qq verbatim — full n-bit cmp-lt + unconditional ;; add c + flag-controlled csub. Provably correct, drop-in ;; replacement. Trade-off: ~3x Toffoli at the mod-add primitive ;; (no LSBS-truncation savings) but algorithmically sound. ;; ;; See memory/project_k0_textbook_input_dep_top_bit_leak.md ;; for the full analysis + repro. ;; ;; 2026-06-12 (alg-11 wiring) — when *mod-add-alg-11-fallback* on, ;; consult mod-add-alg-11-safe? to classically peek the operands ;; via find-classical-value & route pseudo-Mersenne ONLY on inputs ;; outside band-A / band-B. The dispatcher bumps ;; *mod-add-alg-11-counter* on band-fire & routes to canonical ;; Solinas (else branch). When fallback flag is #f, dispatcher ;; behaves like 5e6e3af gate (always canonical) — correctness- ;; first default. ((and *mod-add-use-pseudo-mersenne* (> c-const 0) (< (+ padding f-bits) n+1) (mod-add-alg-11-safe? c a-reg acc-reg n+1 p)) (mod-add-inplace-pseudo-mersenne! c a-reg acc-reg n+1 p c-const cin-reg cin-idx tmp-reg flag-reg flag-idx)) (else ;; (1) cuccaro add at n+1 bits — borrow tmp as carries if flag set ;; (tmp is |0> on entry per docstring; cuccaro-add-fast-borrowed ;; returns it to |0> via HMR uncompute before add-const consumes it). ;; ;; sweep-windowed-wiring: when *cuccaro-add-windowed* on AND ;; *windowed-block-count* > 1, route step (1) through the ;; apply-phase-wrapped windowed cuccaro-add. Windowed alloc's ;; per-block carries internally so borrow-from-tmp does NOT apply; ;; we always pass the non-borrowed dispatch. Bit-base region ;; (* 7 n+1) selected to stay clear of every other bit-base ;; reservation in this file (largest = (* 5 n+1) for ;; boundary-replay phase; windowed wrapper needs ~4n bits). (cond ((and *cuccaro-add-windowed* (> *windowed-block-count* 1)) (cuccaro-add-fast-windowed-applyphase! c a-reg acc-reg cin-reg cin-idx n+1 *windowed-block-count* 'mod-add-windowed (* 7 n+1))) (*cuccaro-use-borrowed* (cuccaro-add-fast-borrowed! c a-reg acc-reg cin-reg cin-idx n+1 tmp-reg 0 (* 2 n+1))) (else (cuccaro-add! c a-reg acc-reg cin-reg cin-idx n+1))) ;; (2) add-const c at n+1 bits (add-const! c acc-reg n+1 c-const cin-reg cin-idx tmp-reg) ;; (3) flag := acc[n] (the overflow bit). Inside ovf-acc. (gate-cx! c acc-reg n flag-reg flag-idx) ;; (4) X flag (gate-x! c flag-reg flag-idx) ;; (5) csub c controlled on flag (csub-const! c acc-reg n+1 c-const flag-reg flag-idx cin-reg cin-idx tmp-reg) ;; (6) X flag (back) (gate-x! c flag-reg flag-idx) ;; (7) CX flag -> acc[n] — clear top bit when flag=1 (gate-cx! c flag-reg flag-idx acc-reg n) ;; (8) Uncompute flag via cmp-lt: flag ^= (acc_low < a_low) (cond (*cuccaro-use-borrowed* ;; 2026-06-12 H7a-third-defect probe: replace constant (* 3 n+1) ;; bit-base with cas-alloc counter — same defect class as cdtf. (cmp-lt-into-fast! c acc-reg a-reg n flag-reg flag-idx cin-reg cin-idx tmp-reg 0 (cas-alloc-bit-base! n))) (else (cmp-lt-into! c acc-reg a-reg n flag-reg flag-idx cin-reg cin-idx))))))) ;;; ── pseudo-Mersenne from-zero specialization ────────────────────── ;;; ;;; Same shape as mod-add-inplace-pseudo-mersenne! but step (1)'s ;;; cuccaro-add at width n+1 is replaced by (n+1) CX-copies. ;;; Steps (2) and (3) fire unchanged. (define (mod-add-inplace-pseudo-mersenne-from-zero! c a-reg acc-reg n+1 p pmersenne-f cin-reg cin-idx tmp-reg flag-reg flag-idx) "Pseudo-Mersenne variant of mod-add-from-zero!. Saves the step-1 cuccaro-add by replacing it with n+1 CX-copies. acc-reg MUST be |0> on entry across all n+1 bits." (let* ((n (- n+1 1)) (f-bits (pmersenne-bit-length pmersenne-f)) (padding *mod-add-pseudo-mersenne-padding*) (lsbs (min n+1 (+ padding f-bits))) (cmp-w (min padding n))) ;; (1) CX-copy a into acc (saves n CCX vs cuccaro-add!). (cuccaro-add-from-zero! c a-reg acc-reg cin-reg cin-idx n+1) ;; (2) cadd(acc[n], f, acc[:lsbs]) — fires unchanged. (cadd-const! c acc-reg lsbs pmersenne-f acc-reg n cin-reg cin-idx tmp-reg) ;; (3) Uncompute acc[n] via MSB comparator — fires unchanged. (cond (*dgcd-apply-boundary-conditional-replay* (let ((phase-bit (* 5 n+1))) (gate-hmr! c acc-reg n phase-bit) (cmp-lt-phase-conditioned-with-cin! c acc-reg (- n cmp-w) a-reg (- n cmp-w) cmp-w cin-reg cin-idx acc-reg n phase-bit tmp-reg 0 (* 3 n+1)))) (*cuccaro-use-borrowed* (cmp-lt-into-fast-offset! c acc-reg (- n cmp-w) a-reg (- n cmp-w) cmp-w acc-reg n cin-reg cin-idx tmp-reg 0 (* 3 n+1))) (else (cmp-lt-into-offset! c acc-reg (- n cmp-w) a-reg (- n cmp-w) cmp-w acc-reg n cin-reg cin-idx))))) ;;; ── mod-add-from-zero! — caller-explicit acc=|0> specialization ─── ;;; ;;; Port of HEAD's `mod_add_qq_fast_from_zero` (mod.rs:961-1038). When ;;; acc-reg is provably |0> on entry, the (n+1)-bit cuccaro add at ;;; step (1) reduces to (n+1) CX-copies. Steps (2)-(8) fire unchanged ;;; — the addend a may exceed p, so the reduce-by-c path still must ;;; run. Saves n CCX per call vs full mod-add!. ;;; ;;; Caller responsibility: ;;; - acc-reg MUST be |0> on entry across all n+1 bits. ;;; - a-reg follows extended-reg convention (top bit |0>) — same as ;;; mod-add!. ;;; ;;; Dispatches through pseudo-Mersenne or Solinas path mirroring ;;; mod-add!'s `cond`, so callers see uniform behavior under either ;;; lever stack. (define (mod-add-from-zero! c a-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) "acc := (acc + a) mod p WHEN acc is |0> on entry. n CCX saved vs mod-add!. See mod-add! header for parameter shape." (let* ((n (- n+1 1)) (c-const (- (expt 2 n) p)) (f-bits (pmersenne-bit-length c-const)) (padding *mod-add-pseudo-mersenne-padding*)) (cond ;; 2026-06-12 — DISABLED. mod-add-inplace-pseudo-mersenne-from-zero! ;; shares the same MSB-only-comparator pattern as the in-place ;; variant — step (2) cadd controlled on acc[n] misses sum ∈ [p, 2^n) ;; boundary. See commit 5e6e3af for full analysis. ;; ;; 2026-06-12 (alg-11 wiring) — when *mod-add-alg-11-fallback* on, ;; the band detector classically peeks both operands & routes ;; pseudo-Mersenne only on safe inputs. Since acc-reg is provably ;; |0> on entry per the from-zero contract, the band detector ;; sees acc-cl = 0 → safe iff a-cl < p (the only relevant case). ((and *mod-add-use-pseudo-mersenne* (> c-const 0) (< (+ padding f-bits) n+1) (mod-add-alg-11-safe? c a-reg acc-reg n+1 p)) (mod-add-inplace-pseudo-mersenne-from-zero! c a-reg acc-reg n+1 p c-const cin-reg cin-idx tmp-reg flag-reg flag-idx)) (else ;; (1) CX-copy a into acc (saves n CCX vs cuccaro-add!). (cuccaro-add-from-zero! c a-reg acc-reg cin-reg cin-idx n+1) ;; (2) add-const c at n+1 bits — fires unchanged. (add-const! c acc-reg n+1 c-const cin-reg cin-idx tmp-reg) ;; (3) flag := acc[n] (overflow bit). (gate-cx! c acc-reg n flag-reg flag-idx) ;; (4) X flag. (gate-x! c flag-reg flag-idx) ;; (5) csub c controlled on flag. (csub-const! c acc-reg n+1 c-const flag-reg flag-idx cin-reg cin-idx tmp-reg) ;; (6) X flag back. (gate-x! c flag-reg flag-idx) ;; (7) CX flag -> acc[n] — clear top bit when flag=1. (gate-cx! c flag-reg flag-idx acc-reg n) ;; (8) Uncompute flag via cmp-lt: flag ^= (acc < a). (cond (*cuccaro-use-borrowed* ;; 2026-06-12 H7a-third-defect probe: replace constant (* 3 n+1) ;; bit-base with cas-alloc counter — same defect class as cdtf. (cmp-lt-into-fast! c acc-reg a-reg n flag-reg flag-idx cin-reg cin-idx tmp-reg 0 (cas-alloc-bit-base! n))) (else (cmp-lt-into! c acc-reg a-reg n flag-reg flag-idx cin-reg cin-idx))))))) ;;; ── inverses of add-const / csub-const (needed by mod-sub!) ──── ;;; ;;; sub-const! = inverse of add-const! (unconditional subtract of k) ;;; cadd-const! = inverse of csub-const! (controlled add of k) ;;; ;;; Pattern mirrors add-const/csub-const: load classical k into tmp via ;;; (c)load-const, run cuccaro-(sub|add), unload. load/unload self-inverse. (define (sub-const! c acc-reg n k cin-reg cin-idx tmp-reg) "acc := (acc - k) mod 2^n. tmp must be width-n at |0>." (let ((kk (modulo k (expt 2 n)))) (load-const! c tmp-reg n kk) (cuccaro-sub! c tmp-reg acc-reg cin-reg cin-idx n) (unload-const! c tmp-reg n kk))) ;;; ── sweep-050 direct sparse-constant cadd / csub primitives ──── ;;; ;;; *cadd-direct-trunc-fast* — sweep-050 lever. Port of HEAD's ;;; cadd_nbit_const_direct_trunc_fast (const_arith.rs:487-568) and the ;;; matching csub_nbit_const_direct_trunc_fast (const_arith.rs:574-654). ;;; ;;; When #t, cadd-const! / csub-const! dispatch to the direct path: ;;; for each nonzero bit i of k, a forward carry/borrow sweep emits ;;; 3-CCX maj (or 2-CCX+2-CX with fold_maj2 when *cuccaro-maj2* on) ;;; into a fresh carries ancilla. Carry-tail truncated at ;;; min(n-2, highest_set_bit(k) + window). Sum bits via CX. Backward ;;; sweep measurement-uncomputes carries via HMR + CZ_if triplet. ;;; ;;; Saves ~one cload + one cuccaro-add + one cunload per call vs the ;;; generic cload/cuccaro-add/cunload path. Predicted 2-4 % full-stack ;;; Toffoli when stacked with *cuccaro-maj2*. ;;; ;;; *cadd-direct-window* — carry-tail safety window (mirrors HEAD's ;;; `window` parameter). Higher window → smaller truncation flake ;;; probability ~2^-(window+1) per call; HEAD's default 8. (define *cadd-direct-trunc-fast* #f) (define *cadd-direct-window* 8) ;;; *cuccaro-maj2* — sweep-047 lever for fold_maj2 inside the direct ;;; primitives. Defensively defined here so a champion cell can set it ;;; even if sweep-047 hasn't landed its own define yet. When #t, each ;;; full-MAJ (3 CCX) becomes a maj2 fold (2 CCX + 2 CX) inside the ;;; direct path. (define *cuccaro-maj2* #f) ;;; *cadd-direct-bit-base* — classical-bit base offset used by HMR ;;; uncompute inside the direct primitives. Each call consumes ;;; (last+1) consecutive bit IDs starting at this base. ;;; ;;; 2026-06-12 — H7a peel-8 (commit 648d78e) ROOT-CAUSED back-to-back ;;; mod-add composition wrong-output to constant-bit-base reuse across ;;; consecutive cadd-const!/csub-const! calls. The prior claim "reuse ;;; across separate calls is safe (push-cond/pop-cond brackets every ;;; cz_if)" turned out wrong under cumulative kaliski-body state. ;;; Sweep-doctrine reducers test-mca-bisect-precursor + peel-8 toggle ;;; demonstrate the collision flips the second sum by 2^top-set-bit(k). ;;; ;;; Fix: monotonic counter (*cadd-direct-bit-base-next*) advances ;;; per-call by `n+1` so each direct call gets a unique non-overlapping ;;; slot range. *cadd-direct-bit-base* preserved for back-compat (read ;;; as the floor; never mutated directly). cdtf-alloc-bit-base!(n) ;;; returns the next slot. (define *cadd-direct-bit-base* 400000) (define *cadd-direct-bit-base-next* 400000) (define (cdtf-alloc-bit-base! n) "Return a fresh non-overlapping classical-bit base for one cdtf call. Advances *cadd-direct-bit-base-next* by (n+1) so each call's HMR slots live in their own [b, b+n] range. n is the width passed to the direct primitive — must reserve at least n+1 consecutive IDs." (let ((b *cadd-direct-bit-base-next*)) (set! *cadd-direct-bit-base-next* (+ b n 1)) b)) (define (highest-set-bit k) "Index (0-based) of the highest set bit of positive integer k. highest-set-bit(0)=0 (caller guards via popcount > 0)." (let loop ((i 0) (m k)) (cond ((= m 0) (if (= i 0) 0 (- i 1))) (else (loop (+ i 1) (quotient m 2)))))) ;;; ── cadd/csub_nbit_const_direct_fast (sweep-cadd-csub-direct-fast) ;;; ;;; HEAD const_arith.rs:65 csub_nbit_const_direct_fast ;;; const_arith.rs:152 cadd_nbit_const_direct_fast ;;; ;;; Closes AUDIT §8 rows 203 (direct-fast pair, non-truncated). The ;;; truncated variants (-trunc-fast) already ported below. ;;; ;;; "Direct" = no auxiliary loaded-constant register. Skips the n-qubit ;;; register at Kaliski halve peaks; for sparse secp256k1 c=2^32+977 ;;; CCX count essentially unchanged vs auxiliary-register path but ;;; peak qubits drop by n. ;;; ;;; Pattern (per HEAD lines 65-131 sub, 152-217 add): ;;; 1. carries/borrows register: alloc n-1 ancillas ;;; 2. forward carry/borrow sweep — CCX on majority recurrence, ;;; gated on bit(k,i) sparsity ;;; 3. sum/difference bits: cx(ctrl, acc[i]) when bit(k,i); cx ;;; carry into acc ;;; 4. measurement-uncompute carries in reverse — HMR + cz_if pattern ;;; per Gidney 2025 ;;; ;;; Caller responsibility: ;;; acc-reg : data register width n ;;; ctrl-reg/ctrl-idx : control bit ;;; carries-reg : (n-1)-wide clean ancilla register; restored ;;; bit-base : classical bit-id base for HMR (uses n-1 bits) (define (cadd-nbit-const-direct-fast! c acc-reg n k ctrl-reg ctrl-idx carries-reg bit-base) "Port of HEAD cadd_nbit_const_direct_fast (const_arith.rs:152). acc += (ctrl ? k : 0) mod 2^n via direct carry sweep + HMR uncompute." (cond ((= n 0) #t) ((= n 1) (when (bit-set? k 0) (gate-cx! c ctrl-reg ctrl-idx acc-reg 0))) (else ;; Forward carry sweep (let loop-fwd ((i 0)) (when (< i (- n 1)) (let ((target (cons carries-reg i)) (has-cin (> i 0))) (cond ((bit-set? k i) (cond (has-cin (gate-ccx! c acc-reg i carries-reg (- i 1) carries-reg i) (gate-ccx! c ctrl-reg ctrl-idx acc-reg i carries-reg i) (gate-ccx! c ctrl-reg ctrl-idx carries-reg (- i 1) carries-reg i)) (else (gate-ccx! c acc-reg i ctrl-reg ctrl-idx carries-reg i)))) (has-cin (gate-ccx! c acc-reg i carries-reg (- i 1) carries-reg i)))) (loop-fwd (+ i 1)))) ;; Sum bits (let loop-sum ((i 0)) (when (< i n) (when (bit-set? k i) (gate-cx! c ctrl-reg ctrl-idx acc-reg i)) (when (> i 0) (gate-cx! c carries-reg (- i 1) acc-reg i)) (loop-sum (+ i 1)))) ;; Measurement-uncompute carries (reverse). For ADDITION the ;; identity is carry_{i+1} = majority(!acc_i_final, k_i, carry_i) ;; — note the X on acc[i] around the cz_if pairs. (let loop-back ((i (- n 2))) (when (>= i 0) (let ((bit-id (+ bit-base i)) (has-cin (> i 0))) (gate-hmr! c carries-reg i bit-id) (cond ((bit-set? k i) (gate-x! c acc-reg i) (cond (has-cin (gate-push-cond! c bit-id) (gate-cz! c acc-reg i ctrl-reg ctrl-idx) (gate-pop-cond! c) (gate-push-cond! c bit-id) (gate-cz! c acc-reg i carries-reg (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i) (gate-push-cond! c bit-id) (gate-cz! c ctrl-reg ctrl-idx carries-reg (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c bit-id) (gate-cz! c acc-reg i ctrl-reg ctrl-idx) (gate-pop-cond! c) (gate-x! c acc-reg i)))) (has-cin (gate-x! c acc-reg i) (gate-push-cond! c bit-id) (gate-cz! c acc-reg i carries-reg (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i)))) (loop-back (- i 1))))))) (define (csub-nbit-const-direct-fast! c acc-reg n k ctrl-reg ctrl-idx borrows-reg bit-base) "Port of HEAD csub_nbit_const_direct_fast (const_arith.rs:65). acc -= (ctrl ? k : 0) mod 2^n via direct borrow sweep + HMR uncompute." (cond ((= n 0) #t) ((= n 1) (when (bit-set? k 0) (gate-cx! c ctrl-reg ctrl-idx acc-reg 0))) (else ;; Forward borrow sweep. borrow_{i+1} = majority(!acc_i, k_i, borrow_i). (let loop-fwd ((i 0)) (when (< i (- n 1)) (let ((has-bin (> i 0))) (cond ((bit-set? k i) (gate-x! c acc-reg i) (cond (has-bin (gate-ccx! c acc-reg i borrows-reg (- i 1) borrows-reg i) (gate-ccx! c ctrl-reg ctrl-idx acc-reg i borrows-reg i) (gate-ccx! c ctrl-reg ctrl-idx borrows-reg (- i 1) borrows-reg i)) (else (gate-ccx! c acc-reg i ctrl-reg ctrl-idx borrows-reg i))) (gate-x! c acc-reg i)) (has-bin (gate-x! c acc-reg i) (gate-ccx! c acc-reg i borrows-reg (- i 1) borrows-reg i) (gate-x! c acc-reg i)))) (loop-fwd (+ i 1)))) ;; Difference bits: acc_i ^= k_i ^ borrow_i. (let loop-diff ((i 0)) (when (< i n) (when (bit-set? k i) (gate-cx! c ctrl-reg ctrl-idx acc-reg i)) (when (> i 0) (gate-cx! c borrows-reg (- i 1) acc-reg i)) (loop-diff (+ i 1)))) ;; Measurement-uncompute borrows. For SUBTRACTION the post-sum ;; identity is borrow_{i+1} = majority(acc_i_final, k_i, borrow_i). ;; No X bracketing around acc[i] (different from add). (let loop-back ((i (- n 2))) (when (>= i 0) (let ((bit-id (+ bit-base i)) (has-bin (> i 0))) (gate-hmr! c borrows-reg i bit-id) (cond ((bit-set? k i) (cond (has-bin (gate-push-cond! c bit-id) (gate-cz! c acc-reg i ctrl-reg ctrl-idx) (gate-pop-cond! c) (gate-push-cond! c bit-id) (gate-cz! c acc-reg i borrows-reg (- i 1)) (gate-pop-cond! c) (gate-push-cond! c bit-id) (gate-cz! c ctrl-reg ctrl-idx borrows-reg (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c bit-id) (gate-cz! c acc-reg i ctrl-reg ctrl-idx) (gate-pop-cond! c)))) (has-bin (gate-push-cond! c bit-id) (gate-cz! c acc-reg i borrows-reg (- i 1)) (gate-pop-cond! c)))) (loop-back (- i 1))))))) (define (cadd-nbit-const-direct-trunc-fast! c acc-reg n k ctrl-reg ctrl-idx tmp-reg window bit-base) "acc[0..n) += (ctrl ? k : 0), carry-tail truncated. Port of HEAD cadd_nbit_const_direct_trunc_fast (const_arith.rs:487-568). acc-reg : (>=n)-wide quantum register holding the running accumulator. n : slice width (>=1). Must not overlap with ctrl-reg/ctrl-idx. k : compile-time classical integer; only its low n bits read. ctrl-reg, ctrl-idx : the qubit controlling the add (must not alias any of acc-reg[0..n)). tmp-reg : (>=last+1)-wide ancilla register at |0>. Borrowed as the carries lane (acc-reg's tmp slot is wide enough for every pseudo-Mersenne caller in our stack). window : carry-tail safety bits past highest_set_bit(k). HEAD's default 8; flake prob ~2^-(window+1). bit-base: classical-bit base offset; consumes [bit-base, bit-base+last] via HMR. Caller picks a non-colliding region. Returns tmp-reg[0..last] to |0> via HMR + cz_if; acc-reg gets updated sum; ctrl-reg unchanged." (let ((kk (modulo k (expt 2 n)))) (cond ((= n 0) #t) ((= n 1) (when (bit-set? kk 0) (gate-cx! c ctrl-reg ctrl-idx acc-reg 0))) ((= kk 0) #t) (else (let* ((hi (highest-set-bit kk)) (last (min (- n 2) (+ hi window))) (maj2 *cuccaro-maj2*)) ;; Forward carry sweep, truncated at `last`. (let loop ((i 0)) (when (<= i last) (let ((carry-in? (> i 0))) (cond ((bit-set? kk i) (cond (carry-in? (cond (maj2 (gate-ccx! c acc-reg i tmp-reg (- i 1) tmp-reg i) (gate-cx! c acc-reg i tmp-reg (- i 1)) (gate-ccx! c ctrl-reg ctrl-idx tmp-reg (- i 1) tmp-reg i) (gate-cx! c acc-reg i tmp-reg (- i 1))) (else (gate-ccx! c acc-reg i tmp-reg (- i 1) tmp-reg i) (gate-ccx! c ctrl-reg ctrl-idx acc-reg i tmp-reg i) (gate-ccx! c ctrl-reg ctrl-idx tmp-reg (- i 1) tmp-reg i)))) (else (gate-ccx! c acc-reg i ctrl-reg ctrl-idx tmp-reg i)))) (carry-in? (gate-ccx! c acc-reg i tmp-reg (- i 1) tmp-reg i)))) (loop (+ i 1)))) ;; Sum bits: acc_i ^= k_i ^ carry_{i-1}; carries above last are 0. (let loop ((i 0)) (when (< i n) (when (bit-set? kk i) (gate-cx! c ctrl-reg ctrl-idx acc-reg i)) (when (and (> i 0) (<= (- i 1) last)) (gate-cx! c tmp-reg (- i 1) acc-reg i)) (loop (+ i 1)))) ;; Backward measurement-uncompute carries (HMR + cz_if triplet). (let loop-back ((i last)) (when (>= i 0) (let ((m (+ bit-base i)) (carry-in? (> i 0))) (gate-hmr! c tmp-reg i m) (cond ((bit-set? kk i) (gate-x! c acc-reg i) (cond (carry-in? (gate-push-cond! c m) (gate-cz! c acc-reg i ctrl-reg ctrl-idx) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c acc-reg i tmp-reg (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i) (gate-push-cond! c m) (gate-cz! c ctrl-reg ctrl-idx tmp-reg (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c m) (gate-cz! c acc-reg i ctrl-reg ctrl-idx) (gate-pop-cond! c) (gate-x! c acc-reg i)))) (carry-in? (gate-x! c acc-reg i) (gate-push-cond! c m) (gate-cz! c acc-reg i tmp-reg (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i)))) (loop-back (- i 1))))))))) (define (csub-nbit-const-direct-trunc-fast! c acc-reg n k ctrl-reg ctrl-idx tmp-reg window bit-base) "acc[0..n) -= (ctrl ? k : 0), borrow-tail truncated. Port of HEAD csub_nbit_const_direct_trunc_fast (const_arith.rs:574-654). Same calling convention as cadd-nbit-const-direct-trunc-fast!." (let ((kk (modulo k (expt 2 n)))) (cond ((= n 0) #t) ((= n 1) (when (bit-set? kk 0) (gate-cx! c ctrl-reg ctrl-idx acc-reg 0))) ((= kk 0) #t) (else (let* ((hi (highest-set-bit kk)) (last (min (- n 2) (+ hi window))) (maj2 *cuccaro-maj2*)) ;; Forward borrow sweep, truncated at `last`. (let loop ((i 0)) (when (<= i last) (let ((borrow-in? (> i 0))) (cond ((bit-set? kk i) (gate-x! c acc-reg i) (cond (borrow-in? (cond (maj2 (gate-ccx! c acc-reg i tmp-reg (- i 1) tmp-reg i) (gate-cx! c acc-reg i tmp-reg (- i 1)) (gate-ccx! c ctrl-reg ctrl-idx tmp-reg (- i 1) tmp-reg i) (gate-cx! c acc-reg i tmp-reg (- i 1))) (else (gate-ccx! c acc-reg i tmp-reg (- i 1) tmp-reg i) (gate-ccx! c ctrl-reg ctrl-idx acc-reg i tmp-reg i) (gate-ccx! c ctrl-reg ctrl-idx tmp-reg (- i 1) tmp-reg i)))) (else (gate-ccx! c acc-reg i ctrl-reg ctrl-idx tmp-reg i))) (gate-x! c acc-reg i)) (borrow-in? (gate-x! c acc-reg i) (gate-ccx! c acc-reg i tmp-reg (- i 1) tmp-reg i) (gate-x! c acc-reg i)))) (loop (+ i 1)))) ;; Difference bits. (let loop ((i 0)) (when (< i n) (when (bit-set? kk i) (gate-cx! c ctrl-reg ctrl-idx acc-reg i)) (when (and (> i 0) (<= (- i 1) last)) (gate-cx! c tmp-reg (- i 1) acc-reg i)) (loop (+ i 1)))) ;; Backward measurement-uncompute borrows. (let loop-back ((i last)) (when (>= i 0) (let ((m (+ bit-base i)) (borrow-in? (> i 0))) (gate-hmr! c tmp-reg i m) (cond ((bit-set? kk i) (cond (borrow-in? (gate-push-cond! c m) (gate-cz! c acc-reg i ctrl-reg ctrl-idx) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c acc-reg i tmp-reg (- i 1)) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c ctrl-reg ctrl-idx tmp-reg (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c m) (gate-cz! c acc-reg i ctrl-reg ctrl-idx) (gate-pop-cond! c)))) (borrow-in? (gate-push-cond! c m) (gate-cz! c acc-reg i tmp-reg (- i 1)) (gate-pop-cond! c)))) (loop-back (- i 1))))))))) ;;; ── sweep-060 per-position-controls primitives ──────────────────── ;;; ;;; Port of HEAD's cadd_per_position_controls_trunc / ;;; csub_per_position_controls_trunc (const_arith.rs:657-814). ;;; ;;; Unlike cadd-nbit-const-direct-trunc-fast! the "constant" k is NOT a ;;; compile-time scalar. Instead each position carries its own runtime ;;; qubit control kctrl[i] — possibly absent. The 3-CCX maj cluster ;;; operates on (acc[i], kctrl[i], carry_in). With *perpos-maj2* on, ;;; substitute 2-CCX + 2-CX ancilla-free majority (per HEAD's ;;; perpos_maj2_enabled() doc at const_arith.rs:449-466). ;;; ;;; lumbda representation of the per-position-controls vector: ;;; list of length <= n where each element is either ;;; - #f -> no control at this position (k_i = 0) ;;; - (reg . idx) -> runtime control qubit at this position ;;; Positions beyond list length default to #f (no control). ;;; ;;; The `last` argument is the inclusive truncation index for the ;;; carry/borrow sweep (mirrors HEAD's `last: usize`). Caller computes ;;; last = min(n-2, highest-controlled-position + window) so positions ;;; above `last` get carries assumed 0. Flake probability identical to ;;; HEAD's truncated direct adder. ;;; ;;; *perpos-maj2* — sweep-060 lever. When #t, the 3-CCX maj inside the ;;; per-pos-controls primitives substitutes the 2-CCX + 2-CX ;;; ancilla-free pattern. Default #f (3-CCX safe baseline). (define *perpos-maj2* #f) ;;; ── HEAD modular-tier lever family (sweep-modular-lever-flags) ──── ;;; ;;; Closes AUDIT §6 / §8 rows 217-218, 248-249, 260-261, 263. ;;; All default-OFF flag declarations covering HEAD's modular-tier ;;; configure_ecdsafail_submission_route lever surface. ;; *fold-maj2* — sibling of *perpos-maj2*. HEAD mod.rs:471 ;; fold_maj2_enabled. The 2-CCX + 2-CX ancilla-free maj substitution ;; inside fold_carry path. Default #f. (define *fold-maj2* #f) ;; ── HEAD fold substrate family (const_arith.rs:9-1267) ── ;; Wires the 4 fold-stage knobs that HEAD's submission route enables. ;; Port covers `fold_postsum_carry_phase_uncompute`, ;; `fold_postsum_carry_compute`, `emit_fold_maj1`/`emit_fold_majority`, ;; `fold_park_low_carries`, `fold_ripple_freed_tail_ed`. ;; ;; *fold-maj1* — HEAD const_arith.rs:9 fold_maj1_enabled. When #t AND ;; the four maj inputs (a, k, carry, target) are all-distinct qubits, ;; substitute the 4-CX + 1-CCX maj1 emit (saves 2 Toffoli vs the ;; 3-CCX baseline, costs 4 CX). Coexists with *fold-maj2*; maj1 wins ;; when applicable. Default #f. Already declared in ;; head-route-missing-flags.lsp; consumed here in the fold dispatch. ;; *fold-park-low-carries* — integer count of low carries to park via ;; HMR + cz_if phase-uncompute mid-ripple (mirrors HEAD's ;; fold_park_low_carries() return). Range [0, hi_delta]. Default 0 ;; (no parking). When > 0, drops the fused double/halve high-water by ;; `park` qubits at cost of `park` extra HMR + cz_if pairs (phase-exact; ;; 0 Toffoli). Caller sets via `(set! *fold-park-low-carries* N)`. (define *fold-park-low-carries* 0) ;; *fold-freed-tail* — HEAD const_arith.rs:867 fold_freed_tail_enabled. ;; When #t, switch the fused-fold ripple to the split-lane variant: ;; alloc `low[0..=hi_delta]` first, run active region, free the four ;; derived controls (h, xed, eord, n10), THEN alloc `tail` for the ;; wide high tail. Drops the wide-tail high-water by 4 ancillae (the ;; 4 derived controls released before the tail allocation peak). ;; Value/phase-EXACT vs the unsplit ripple. Default #f. (define *fold-freed-tail* #f) ;; Backwards-compat alias for the `dgcd-` namespaced variant declared ;; in head-route-missing-flags.lsp — when either flag is set, the ;; freed-tail dispatch fires (consumer: cadd-2-controls-trunc-fast!). ;; *fold-freed-tail-ed* extends the freed-tail to ALSO release e,d ;; across the wide high tail (HYP-6 §4a). HEAD: -2 ancillae more. ;; Lumbda: STRUCTURAL NO-OP. The freed-tail dispatcher's caller-side ;; `e`/`d` are NOT separately allocated qubits in lumbda — the ;; `cadd-2-controls-trunc-fast!` dispatcher binds `e ↔ ctrl1-reg/idx` ;; and `d ↔ ctrl2-reg/idx` directly (mod-arith.lsp:2750-2755 docstring). ;; mod-4x-inplace! passes ovf1/ovf2 themselves as ctrl1/ctrl2, exploiting ;; the s2=1 classical specialization (s2 is a classical-true constant ;; under lumbda's K=2 host emit, never a qubit). Per HEAD compressed.rs ;; :3260-3271 the e,d copy alloc is `d = ovf1 & s2; e = ovf1 ^ d ^ ovf2` ;; — under s2=1 collapses to `d = ovf1; e = ovf2`. Lumbda skipped this ;; copy-alloc structurally, so it has nothing extra to free across the ;; tail. The -2 ancilla win HEAD reports is already-banked at the ;; substrate level. See test-fold-freed-tail-ed-port.lsp for the ;; positive-control reducer that proves byte-identity for flag-ON vs ;; flag-OFF + identical peak-qubits + identical toffoli counts. ;; *double-carry-trunc-window* — HEAD mod.rs:427 ;; double_carry_trunc_window. KAL_DOUBLE_CARRY_TRUNC_W from ;; configure_ecdsafail_submission_route (=20 in HEAD's tuned route). ;; Integer truncation width for double-carry path; 0 = OFF. (define *double-carry-trunc-window* 0) ;; *fold-carry-trunc-window* — HEAD mod.rs:442 ;; fold_carry_trunc_window. KAL_FOLD_CARRY_TRUNC_W from ;; configure_ecdsafail_submission_route (=20 in HEAD). 0 = OFF. (define *fold-carry-trunc-window* 0) ;; *mod-add-qq-vent* — HEAD mod.rs:189 mod_add_qq_vent. ;; KAL_VENT_MODADD lever; vent-based mod-add saves overflow ancilla ;; in Kaliski loop body. Default #f. (define *mod-add-qq-vent* #f) ;; *mod-sub-qq-vent* — HEAD mod.rs:250 mod_sub_qq_vent. Symmetric. (define *mod-sub-qq-vent* #f) ;; *mod-add-qq-fast-from-zero* — HEAD mod.rs:961 ;; mod_add_qq_fast_from_zero. Initial-state-known optimization; ;; saves the cin walk when acc is provably |0> on entry. Default #f. (define *mod-add-qq-fast-from-zero* #f) ;; *cmod-double-inplace-lazy* — HEAD mod.rs:817 cmod_double_inplace_lazy. ;; Controlled lazy form of mod_double_inplace_fast; used inside HEAD's ;; apply-bitvector chunked path. Default #f. (define *cmod-double-inplace-lazy* #f) ;; *cmod-halve-inplace-lazy* — HEAD mod.rs:841. Symmetric. Default #f. (define *cmod-halve-inplace-lazy* #f) ;; *mod-shift-left-by-k-lowq* — HEAD mod.rs:618. lowq variant of ;; mod_shift_left_by_k. Default #f. (define *mod-shift-left-by-k-lowq* #f) ;; *mod-shift-right-by-k-lowq* — HEAD mod.rs:678. Symmetric. Default #f. (define *mod-shift-right-by-k-lowq* #f) ;; ── extcarry_clean family — HEAD const_arith.rs:239-331 ── ;; ;; Six default-OFF flags covering the clean-extcarry adder primitives ;; HEAD uses when MSB is provably zero. All flags gate variant dispatch ;; at the const-arith callsite (downstream substrate work). (define *add-nbit-const-extcarry-clean* #f) (define *add-nbit-const-extcarry-clean-with-cin* #f) (define *sub-nbit-const-extcarry-clean* #f) (define *cadd-nbit-const-extcarry-clean* #f) (define *csub-nbit-const-extcarry-clean* #f) (define *csub-nbit-const-extcarry-clean-with-cin* #f) ;; ── direct-fast variants — HEAD const_arith.rs:65,152 ── ;; ;; cadd_nbit_const_direct_fast / csub_nbit_const_direct_fast — the ;; DIRECT_CONST_WALKS path. *cadd-direct-trunc-fast* (truncated ;; variant) already PORTED; these flag the non-truncated DIRECT_CONST ;; siblings. Default #f. (define *cadd-nbit-const-direct-fast* #f) (define *csub-nbit-const-direct-fast* #f) ;; *csub-per-position-controls-trunc* — sibling of ;; cadd-per-position-controls-trunc! (already ported as lumbda ;; primitive). Flag declaration here for variant dispatch. Default #f. (define *csub-per-position-controls-trunc* #f) ;;; *kaliski-use-per-pos-controls* — sweep-060 lever. When #t, host ;;; kaliski iteration's apply-phase callsite dispatches into ;;; cadd-per-position-controls-trunc! instead of the compile-time ;;; constant adder. Default #f — substrate gap means no callsite ;;; consumes this lever yet (see RESULTS.md for the apply-phase ;;; roadmap). Defensive flag definition so champion cells can set it ;;; without redefinition errors. (define *kaliski-use-per-pos-controls* #f) (define (perpos-ctrl-at controls i) "Return the per-position control pair at index i, or #f. Out-of-range defaults to #f (no control)." (cond ((null? controls) #f) ((<= i -1) #f) (else (let loop ((lst controls) (j 0)) (cond ((null? lst) #f) ((= j i) (car lst)) (else (loop (cdr lst) (+ j 1)))))))) (define (perpos-ctrls-length controls) "Length of the per-position-controls list." (let loop ((lst controls) (n 0)) (cond ((null? lst) n) (else (loop (cdr lst) (+ n 1)))))) (define (cadd-per-position-controls-trunc! c acc-reg n controls last tmp-reg bit-base) "acc[0..n) += sum_{i where ctrl_i present} (ctrl_i ? 2^i : 0), carry-tail truncated at index `last` (inclusive). Port of HEAD cadd_per_position_controls_trunc (const_arith.rs:657-735). acc-reg : (>=n)-wide quantum register holding the running accumulator. n : slice width (>=1). Must not overlap any control qubit. controls : list of length <= n of either #f or (reg . idx) pairs. Position i: ctrl_i = (perpos-ctrl-at controls i). If #f, position contributes 0; else contributes 2^i conditioned on that ctrl qubit. last : inclusive truncation index for the carry sweep. Must satisfy 0 <= last < n. Caller picks last = min(n-2, highest-active-pos + window). Carries above `last` assumed 0. tmp-reg : (>=last+1)-wide ancilla register at |0> for the carries lane. bit-base : classical-bit base offset. Consumes [bit-base, bit-base+last] via HMR during measurement-uncompute. When *perpos-maj2* #t, each 3-CCX maj substitutes 2-CCX + 2-CX ancilla-free majority (target ^= maj(acc,carry_in,kc) emitted as ccx(acc,ci,target); cx(acc,ci); ccx(kc,ci,target); cx(acc,ci)). Returns tmp-reg[0..last] to |0> via HMR + cz_if; acc-reg gets sum updated; every kctrl unchanged." (cond ((= n 0) #t) ((<= last -1) #t) (else (let ((maj2 *perpos-maj2*) (ctrl-len (perpos-ctrls-length controls))) ;; Defensive: last must be < n. (when (>= last n) (error "cadd-per-position-controls-trunc!: last >= n" last n)) ;; Forward carry sweep, truncated at `last`. ;; carry_i = maj(acc_i, kctrl_i, carry_{i-1}) when both kc + ci ;; present; lower-order branches when one is absent. (let loop ((i 0)) (when (<= i last) (let* ((kc (perpos-ctrl-at controls i)) (carry-in? (> i 0))) (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (cond (carry-in? ;; *fold-maj1* (when on AND inputs distinct) wraps to ;; 4-CX + 1-CCX maj1 (saves 2 Toff/pos); else falls ;; through to maj2 (2-CCX + 2-CX) when caller set ;; maj2 #t; else 3-CCX baseline. Byte-identical when ;; *fold-maj1* off — emit-fold-majority!'s maj2/3-CCX ;; branches match the prior inline gate sequence. (emit-fold-majority! c acc-reg i ; a (acc[i]) kc-reg kc-idx ; k (kctrl_i) tmp-reg (- i 1) ; ci (carry_{i-1}) tmp-reg i ; target (carry_i) maj2)) (else (gate-ccx! c acc-reg i kc-reg kc-idx tmp-reg i))))) (carry-in? (gate-ccx! c acc-reg i tmp-reg (- i 1) tmp-reg i)))) (loop (+ i 1)))) ;; Sum bits: acc_i ^= kctrl_i ^ carry_{i-1}; carries above last 0. (let loop ((i 0)) (when (< i n) (let ((kc (perpos-ctrl-at controls i))) (when kc (gate-cx! c (car kc) (cdr kc) acc-reg i)) (when (and (> i 0) (<= (- i 1) last)) (gate-cx! c tmp-reg (- i 1) acc-reg i)) (loop (+ i 1))))) ;; Backward measurement-uncompute carries via HMR + cz_if. (let loop-back ((i last)) (when (>= i 0) (let ((m (+ bit-base i)) (kc (perpos-ctrl-at controls i)) (carry-in? (> i 0))) (gate-hmr! c tmp-reg i m) (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (gate-x! c acc-reg i) (cond (carry-in? (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c acc-reg i tmp-reg (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i) (gate-push-cond! c m) (gate-cz! c kc-reg kc-idx tmp-reg (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-x! c acc-reg i))))) (carry-in? (gate-x! c acc-reg i) (gate-push-cond! c m) (gate-cz! c acc-reg i tmp-reg (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i)))) (loop-back (- i 1)))))))) (define (csub-per-position-controls-trunc! c acc-reg n controls last tmp-reg bit-base) "acc[0..n) -= sum_{i where ctrl_i present} (ctrl_i ? 2^i : 0), borrow-tail truncated at index `last` (inclusive). Port of HEAD csub_per_position_controls_trunc (const_arith.rs:737-814). Same calling convention as cadd-per-position-controls-trunc!." (cond ((= n 0) #t) ((<= last -1) #t) (else (let ((maj2 *perpos-maj2*)) (when (>= last n) (error "csub-per-position-controls-trunc!: last >= n" last n)) ;; Forward borrow sweep, truncated at `last`. (let loop ((i 0)) (when (<= i last) (let* ((kc (perpos-ctrl-at controls i)) (borrow-in? (> i 0))) (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (gate-x! c acc-reg i) (cond (borrow-in? ;; Symmetric to cadd-per-position-controls-trunc!. ;; X-sandwich on acc[i] inverts the carry-sense to ;; borrow; the inner maj is identical to the add ;; variant. *fold-maj1* fires here too when inputs ;; distinct. Byte-identical when *fold-maj1* off. (emit-fold-majority! c acc-reg i ; a (acc[i] with X-sandwich) kc-reg kc-idx tmp-reg (- i 1) tmp-reg i maj2)) (else (gate-ccx! c acc-reg i kc-reg kc-idx tmp-reg i))) (gate-x! c acc-reg i))) (borrow-in? (gate-x! c acc-reg i) (gate-ccx! c acc-reg i tmp-reg (- i 1) tmp-reg i) (gate-x! c acc-reg i)))) (loop (+ i 1)))) ;; Difference bits: acc_i ^= kctrl_i ^ borrow_{i-1}. (let loop ((i 0)) (when (< i n) (let ((kc (perpos-ctrl-at controls i))) (when kc (gate-cx! c (car kc) (cdr kc) acc-reg i)) (when (and (> i 0) (<= (- i 1) last)) (gate-cx! c tmp-reg (- i 1) acc-reg i)) (loop (+ i 1))))) ;; Backward measurement-uncompute borrows. (let loop-back ((i last)) (when (>= i 0) (let ((m (+ bit-base i)) (kc (perpos-ctrl-at controls i)) (borrow-in? (> i 0))) (gate-hmr! c tmp-reg i m) (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (cond (borrow-in? (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c acc-reg i tmp-reg (- i 1)) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c kc-reg kc-idx tmp-reg (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c))))) (borrow-in? (gate-push-cond! c m) (gate-cz! c acc-reg i tmp-reg (- i 1)) (gate-pop-cond! c)))) (loop-back (- i 1)))))))) ;;; ── HEAD fold-substrate helpers (const_arith.rs:9-43, 911-987) ──── ;;; ;;; Port of HEAD's fold-stage gate helpers. Used by both the baseline ;;; fused-fold ripple (when *fold-maj1* lifts maj inputs to maj1) and ;;; the freed-tail variant (which calls fold-postsum-carry-* directly ;;; for low-carry park / unpark). ;;; ;;; emit-fold-maj1! : 4-CX + 1-CCX majority on 4 DISTINCT qubits. ;;; Saves 2 Toff vs the 3-CCX baseline; needs all ;;; 4 inputs distinct (caller verifies). ;;; emit-fold-majority!: dispatch — fold-maj1 (if enabled + distinct), ;;; else maj2 (2 CCX + 2 CX), else 3-CCX baseline. ;;; fold-postsum-carry-phase-uncompute! : measurement-conditioned ;;; cz_if chain to un-park a parked carry (acc[i] ;;; ^= maj(...) inverse via cz_if instead of CCX). ;;; fold-postsum-carry-compute! : symmetric compute-from-acc ;;; pass when re-parking after the tail uncompute. (define (fold-maj1-inputs-distinct? a-reg a-idx k-reg k-idx ci-reg ci-idx tgt-reg tgt-idx) "True iff all 4 (reg, idx) pairs are pairwise distinct. Matches HEAD's maj1_inputs_distinct (const_arith.rs:4)." (let ((pa (cons a-reg a-idx)) (pk (cons k-reg k-idx)) (pc (cons ci-reg ci-idx)) (pt (cons tgt-reg tgt-idx))) (and (not (equal? pa pk)) (not (equal? pa pc)) (not (equal? pa pt)) (not (equal? pk pc)) (not (equal? pk pt)) (not (equal? pc pt))))) (define (emit-fold-maj1! c a-reg a-idx k-reg k-idx ci-reg ci-idx tgt-reg tgt-idx) "4-CX + 1-CCX majority. tgt ^= maj(a, k, carry). HEAD const_arith.rs:13-21. Caller MUST ensure the 4 inputs are pairwise distinct." (gate-cx! c ci-reg ci-idx tgt-reg tgt-idx) (gate-cx! c ci-reg ci-idx a-reg a-idx) (gate-cx! c ci-reg ci-idx k-reg k-idx) (gate-ccx! c a-reg a-idx k-reg k-idx tgt-reg tgt-idx) (gate-cx! c ci-reg ci-idx k-reg k-idx) (gate-cx! c ci-reg ci-idx a-reg a-idx)) (define (emit-fold-majority! c a-reg a-idx k-reg k-idx ci-reg ci-idx tgt-reg tgt-idx maj2) "Dispatch: if *fold-maj1* AND inputs distinct → maj1 (4 CX + 1 CCX); else if maj2 → 2-CCX + 2-CX ancilla-free; else 3-CCX baseline. Mirrors HEAD const_arith.rs:23-43 emit_fold_majority." (cond ((and *fold-maj1* (fold-maj1-inputs-distinct? a-reg a-idx k-reg k-idx ci-reg ci-idx tgt-reg tgt-idx)) (emit-fold-maj1! c a-reg a-idx k-reg k-idx ci-reg ci-idx tgt-reg tgt-idx)) (maj2 ;; 2-CCX + 2-CX ancilla-free. tgt ^= maj(a, k, carry) via: ;; ccx(a, ci, tgt); cx(a, ci); ccx(k, ci, tgt); cx(a, ci) (gate-ccx! c a-reg a-idx ci-reg ci-idx tgt-reg tgt-idx) (gate-cx! c a-reg a-idx ci-reg ci-idx) (gate-ccx! c k-reg k-idx ci-reg ci-idx tgt-reg tgt-idx) (gate-cx! c a-reg a-idx ci-reg ci-idx)) (else ;; 3-CCX baseline. tgt ^= maj(a, k, ci) = a·ci ⊕ k·a ⊕ k·ci. (gate-ccx! c a-reg a-idx ci-reg ci-idx tgt-reg tgt-idx) (gate-ccx! c k-reg k-idx a-reg a-idx tgt-reg tgt-idx) (gate-ccx! c k-reg k-idx ci-reg ci-idx tgt-reg tgt-idx)))) (define (fold-postsum-carry-phase-uncompute! c acc-reg i kc-pair ci-pair m-bit is-add) "Phase-only carry uncompute via cz_if on a measured classical bit. acc-reg : accumulator quantum register. i : current position (0..hi_delta). kc-pair : (reg . idx) of position-i control, or #f. ci-pair : (reg . idx) of carry_{i-1}, or #f (only at i=0). m-bit : classical bit id holding HMR measurement of low[i]. is-add : #t for add, #f for sub (borrow). Mirrors HEAD const_arith.rs:911-948 fold_postsum_carry_phase_uncompute." (cond (is-add (cond (kc-pair (let ((kc-reg (car kc-pair)) (kc-idx (cdr kc-pair))) (gate-x! c acc-reg i) (cond (ci-pair (let ((ci-reg (car ci-pair)) (ci-idx (cdr ci-pair))) (gate-push-cond! c m-bit) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-push-cond! c m-bit) (gate-cz! c acc-reg i ci-reg ci-idx) (gate-pop-cond! c) (gate-x! c acc-reg i) (gate-push-cond! c m-bit) (gate-cz! c kc-reg kc-idx ci-reg ci-idx) (gate-pop-cond! c))) (else (gate-push-cond! c m-bit) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-x! c acc-reg i))))) (ci-pair (let ((ci-reg (car ci-pair)) (ci-idx (cdr ci-pair))) (gate-x! c acc-reg i) (gate-push-cond! c m-bit) (gate-cz! c acc-reg i ci-reg ci-idx) (gate-pop-cond! c) (gate-x! c acc-reg i))))) (else (cond (kc-pair (let ((kc-reg (car kc-pair)) (kc-idx (cdr kc-pair))) (cond (ci-pair (let ((ci-reg (car ci-pair)) (ci-idx (cdr ci-pair))) (gate-push-cond! c m-bit) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-push-cond! c m-bit) (gate-cz! c acc-reg i ci-reg ci-idx) (gate-pop-cond! c) (gate-push-cond! c m-bit) (gate-cz! c kc-reg kc-idx ci-reg ci-idx) (gate-pop-cond! c))) (else (gate-push-cond! c m-bit) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c))))) (ci-pair (let ((ci-reg (car ci-pair)) (ci-idx (cdr ci-pair))) (gate-push-cond! c m-bit) (gate-cz! c acc-reg i ci-reg ci-idx) (gate-pop-cond! c))))))) (define (fold-postsum-carry-compute! c acc-reg i kc-pair ci-pair tgt-reg tgt-idx is-add) "Symmetric Toffoli compute (re-park carry from post-sum acc). Used after the tail uncompute to re-derive the parked low carries. Mirrors HEAD const_arith.rs:950-987 fold_postsum_carry_compute." (cond (is-add (cond (kc-pair (let ((kc-reg (car kc-pair)) (kc-idx (cdr kc-pair))) (gate-x! c acc-reg i) (cond (ci-pair (let ((ci-reg (car ci-pair)) (ci-idx (cdr ci-pair))) (gate-ccx! c acc-reg i kc-reg kc-idx tgt-reg tgt-idx) (gate-ccx! c acc-reg i ci-reg ci-idx tgt-reg tgt-idx) (gate-x! c acc-reg i) (gate-ccx! c kc-reg kc-idx ci-reg ci-idx tgt-reg tgt-idx))) (else (gate-ccx! c acc-reg i kc-reg kc-idx tgt-reg tgt-idx) (gate-x! c acc-reg i))))) (ci-pair (let ((ci-reg (car ci-pair)) (ci-idx (cdr ci-pair))) (gate-x! c acc-reg i) (gate-ccx! c acc-reg i ci-reg ci-idx tgt-reg tgt-idx) (gate-x! c acc-reg i))))) (else (cond (kc-pair (let ((kc-reg (car kc-pair)) (kc-idx (cdr kc-pair))) (cond (ci-pair (let ((ci-reg (car ci-pair)) (ci-idx (cdr ci-pair))) (gate-ccx! c acc-reg i kc-reg kc-idx tgt-reg tgt-idx) (gate-ccx! c acc-reg i ci-reg ci-idx tgt-reg tgt-idx) (gate-ccx! c kc-reg kc-idx ci-reg ci-idx tgt-reg tgt-idx))) (else (gate-ccx! c acc-reg i kc-reg kc-idx tgt-reg tgt-idx))))) (ci-pair (let ((ci-reg (car ci-pair)) (ci-idx (cdr ci-pair))) (gate-ccx! c acc-reg i ci-reg ci-idx tgt-reg tgt-idx))))))) ;;; ── sweep-fused-fold-v25 — cadd-2-controls-trunc-fast! ──────────── ;;; ;;; HEAD reference: `compressed.rs:2193-2215` (forward fused fold body of ;;; `dialog_gcd_fused_double_y`) — single truncated ripple at width lsbs ;;; emitting δ = k1·ctrl1 + k2·ctrl2 via the 12-position per-position ;;; controls table mapped to bits {0,1,4,5,6,7,8,9,10,11, hi, hi+1} for a ;;; secp256k1-class pseudo-Mersenne c. ;;; ;;; What V2.5 saves vs V2's two `cadd-const!` calls: ;;; ;;; V2: cadd-const(v, lsbs, c, ctrl1) + cadd-const(v, lsbs, 2c, ctrl2) ;;; → 2 × full Solinas truncated ripple. ~62 Toff/call × 2. ;;; ;;; V2.5: derive 4 ancilla controls (h, xed, eord, n10) via 3 CCX + 6 CX, ;;; issue ONE per-position-controls truncated ripple at width lsbs, ;;; uncompute the 4 derived controls in reverse (6 CX + 1 CCX). The ;;; ripple itself emits ~62 Toff. Net: ~62 Toff/call + 4 CCX overhead. ;;; ;;; Save per mod-4x-inplace! call: ~62 - 4 Toff = ~58 Toff. ~50 % shift2- ;;; density × iters=258 ≈ 129 calls/shot → ~7-8k Toff saved per shot. ;;; ;;; CALLING CONVENTION: ;;; ;;; c : circuit/stream context. ;;; acc-reg : (>= lsbs)-wide quantum register (the v register). ;;; lsbs : slice width for the truncated ripple. Caller picks ;;; lsbs = min(n+1, padding + bit-length(2*c)) just like ;;; V2's cadd-const calls. last = min(lsbs - 2, hi + window) ;;; where hi = highest-set-bit(2*c) = bit-length(c). ;;; k1 : compile-time classical integer (V2.5 uses c, the ;;; pseudo-Mersenne complement 2^n - p). ;;; ctrl1-reg, ctrl1-idx : runtime qubit gating k1's addition. ;;; k2 : compile-time classical integer (V2.5 uses 2c). ;;; ctrl2-reg, ctrl2-idx : runtime qubit gating k2's addition. ;;; tmp-reg : (>= last+1)-wide ancilla register at |0> for carries. ;;; window : carry-tail safety bits past highest_set_bit(2*c). ;;; Caller passes *cadd-direct-window* (HEAD default 8). ;;; bit-base : classical-bit base offset; consumes ;;; [bit-base, bit-base+last] via HMR. Caller picks a ;;; non-colliding region (V2.5 uses *cadd-direct-bit-base*). ;;; ;;; Currently HARDCODED for HEAD's secp256k1 pseudo-Mersenne table ;;; (compressed.rs:2196-2210): k1 must have bits {0,4,6,7,8,9,hi} set and ;;; k2 = 2·k1 must have bits {1,5,7,8,9,10,hi+1}. Verified at build-time ;;; against (k1, k2). Errors when the pattern doesn't match — protects ;;; against silently emitting the wrong fold for a non-secp256k1 c. ;;; ;;; Returns tmp-reg[0..last] to |0> via HMR + cz_if (inside the ;;; per-position primitive); 4 derived-ctrl ancilla returned to |0> by ;;; reversed CX + CCX uncompute below; acc-reg gets δ added; both ctrl1 ;;; and ctrl2 unchanged. (define (cadd-2-controls-trunc-fast! c acc-reg lsbs k1 ctrl1-reg ctrl1-idx k2 ctrl2-reg ctrl2-idx tmp-reg window bit-base) "acc[0..lsbs) += (ctrl1 ? k1 : 0) + (ctrl2 ? k2 : 0) in ONE truncated carry sweep with per-position-controls. Hardcoded for HEAD's secp256k1-class table where k2 == 2·k1 AND k1's bits sit at {0,4,6,7,8,9,hi}. Errors otherwise. *fold-maj1* / *perpos-maj2* threaded via emit-fold-majority! inside cadd-per-position-controls-trunc!. *fold-freed-tail* dispatches to cadd-fold-ripple-freed-tail!, which owns the full h/xed/eord/n10 lifetime + split low/tail carry lanes (HEAD const_arith.rs:1051-1267 fold_ripple_freed_tail_ed). *fold-park-low-carries* only consumed inside freed-tail variant." ;; *fold-freed-tail* dispatch: routes the split-lane variant. ;; HEAD: free_ed gate implies base freed-tail (const_arith.rs:1064), ;; so we only consult *fold-freed-tail* here. *fold-freed-tail-ed* ;; remains a no-op until the e,d-extension wiring lands (HYP-6 §4a; ;; requires caller-supplied ovf1/ovf2/s2 live qubits, see ticket). (cond (*fold-freed-tail* (let* ((kk1 (modulo k1 (expt 2 lsbs))) (kk2 (modulo k2 (expt 2 lsbs)))) (cond ((= lsbs 0) #t) ((and (= kk1 0) (= kk2 0)) #t) (else (let ((hi (highest-set-bit kk1))) (when (not (= kk2 (modulo (* 2 kk1) (expt 2 lsbs)))) (error "cadd-2-controls-trunc-fast!: k2 != 2*k1 (mod 2^lsbs)" k1 k2 lsbs)) (when (not (and (bit-set? kk1 0) (bit-set? kk1 4) (bit-set? kk1 6) (bit-set? kk1 7) (bit-set? kk1 8) (bit-set? kk1 9) (bit-set? kk1 hi))) (error "cadd-2-controls-trunc-fast!: k1 missing required bits" k1 hi)) (when (<= lsbs (+ hi 1)) (error "cadd-2-controls-trunc-fast!: lsbs <= hi+1, no room" lsbs hi)) (let* ((last (min (- lsbs 2) (+ (+ hi 1) window))) (hi-delta (+ hi 1))) ;; HEAD const_arith.rs:1069 — freed-tail requires nonempty ;; high tail (last > hi-delta). (when (<= last hi-delta) (error "cadd-2-controls-trunc-fast!: *fold-freed-tail* needs last > hi+1" last hi-delta)) (cadd-fold-ripple-freed-tail! c acc-reg lsbs ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx last hi hi-delta #t bit-base))))))) (else (let* ((kk1 (modulo k1 (expt 2 lsbs))) (kk2 (modulo k2 (expt 2 lsbs)))) (cond ((= lsbs 0) #t) ((and (= kk1 0) (= kk2 0)) #t) (else ;; Validate HEAD table assumptions. (let ((hi (highest-set-bit kk1))) (when (not (= kk2 (modulo (* 2 kk1) (expt 2 lsbs)))) (error "cadd-2-controls-trunc-fast!: k2 != 2*k1 (mod 2^lsbs)" k1 k2 lsbs)) (when (not (and (bit-set? kk1 0) (bit-set? kk1 4) (bit-set? kk1 6) (bit-set? kk1 7) (bit-set? kk1 8) (bit-set? kk1 9) (bit-set? kk1 hi))) (error "cadd-2-controls-trunc-fast!: k1 missing required bits" k1 hi)) ;; The table requires bit hi+1 to live inside lsbs (HEAD: hi=32 ;; for secp256k1 → bit 33 inside lsbs ≥ 34). If lsbs ≤ hi+1 the ;; per-position table cannot place the high pair; fall back path ;; lives upstream at mod-4x-inplace! (V1 sequential). (when (<= lsbs (+ hi 1)) (error "cadd-2-controls-trunc-fast!: lsbs <= hi+1, no room" lsbs hi)) ;; Derive 4 ancilla controls per HEAD compressed.rs:2169-2191. ;; "e" in HEAD ↔ ctrl1 (V2 carrier of k1·c). ;; "d" in HEAD ↔ ctrl2 (V2 carrier of k2·2c). ;; h = e & d (1 CCX) ;; xed = e ⊕ d (2 CX) ;; eord = (e ⊕ d) ⊕ h (2 CX; equals e | d) ;; n10 = d ⊕ h (2 CX; equals ¬e & d) (let ((h '_v25-h) (xed '_v25-xed) (eord '_v25-eord) (n10 '_v25-n10)) (alloc! c h 1) (alloc! c xed 1) (alloc! c eord 1) (alloc! c n10 1) ;; h = ctrl1 & ctrl2 (gate-ccx! c ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx h 0) ;; xed = ctrl1 ⊕ ctrl2 (gate-cx! c ctrl1-reg ctrl1-idx xed 0) (gate-cx! c ctrl2-reg ctrl2-idx xed 0) ;; eord = xed ⊕ h (gate-cx! c xed 0 eord 0) (gate-cx! c h 0 eord 0) ;; n10 = ctrl2 ⊕ h (gate-cx! c ctrl2-reg ctrl2-idx n10 0) (gate-cx! c h 0 n10 0) ;; Build the per-position-controls list per HEAD's table. ;; bit 0: ctrl1 (k1 bit 0 = 1) ;; bit 1: ctrl2 (k2 bit 1 = 1) ;; bit 4: ctrl1 (k1 bit 4 = 1) ;; bit 5: ctrl2 (k2 bit 5 = 1) ;; bit 6: ctrl1 (k1 bit 6 = 1) ;; bit 7: xed (k1+k2 bit 7 = 2 → xor at 7, carry to 8) ;; bit 8: eord (k1+k2+carry bit 8) ;; bit 9: eord (k1+k2+carry bit 9) ;; bit 10: n10 (k2 bit 10 only) ;; bit 11: h (carry-fold absorption) ;; bit hi: ctrl1 ;; bit hi+1: ctrl2 ;; Positions not listed default to #f via perpos-ctrl-at. (let* ((last (min (- lsbs 2) (+ (+ hi 1) window))) (controls (make-perpos-secp256k1-fold-controls hi (cons ctrl1-reg ctrl1-idx) (cons ctrl2-reg ctrl2-idx) (cons xed 0) (cons eord 0) (cons n10 0) (cons h 0)))) (cadd-per-position-controls-trunc! c acc-reg lsbs controls last tmp-reg bit-base)) ;; Uncompute derived ancilla in EXACT reverse of derivation. ;; reverse n10 : (cx h n10) (cx ctrl2 n10) (gate-cx! c h 0 n10 0) (gate-cx! c ctrl2-reg ctrl2-idx n10 0) ;; reverse eord : (cx h eord) (cx xed eord) (gate-cx! c h 0 eord 0) (gate-cx! c xed 0 eord 0) ;; reverse xed : (cx ctrl2 xed) (cx ctrl1 xed) (gate-cx! c ctrl2-reg ctrl2-idx xed 0) (gate-cx! c ctrl1-reg ctrl1-idx xed 0) ;; reverse h : (ccx ctrl1 ctrl2 h) (gate-ccx! c ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx h 0) (free! c n10) (free! c eord) (free! c xed) (free! c h))))))))) ; closes (else ...) of *fold-freed-tail* dispatch ;;; Helper: build the 12-position controls list for HEAD's secp256k1 fold. ;;; The list length = hi + 2 (indices 0..hi+1 covered). Positions not in ;;; the table are #f (no control). hi = highest_set_bit(c) (= 32 for ;;; secp256k1). Per HEAD compressed.rs:2196-2210. (define (make-perpos-secp256k1-fold-controls hi e-pair d-pair xed-pair eord-pair n10-pair h-pair) "Return list of length (hi + 2) for the per-position-controls fold of δ = k1·e + k2·d under HEAD's secp256k1 table." ;; Walk i = 0..(hi+1) and append the right pair (or #f). (let loop ((i 0) (acc '())) (cond ((> i (+ hi 1)) (reverse acc)) (else (let ((entry (cond ((= i 0) e-pair) ((= i 1) d-pair) ((= i 4) e-pair) ((= i 5) d-pair) ((= i 6) e-pair) ((= i 7) xed-pair) ((= i 8) eord-pair) ((= i 9) eord-pair) ((= i 10) n10-pair) ((= i 11) h-pair) ((= i hi) e-pair) ((= i (+ hi 1)) d-pair) (else #f)))) (loop (+ i 1) (cons entry acc))))))) ;;; ── cadd-fold-ripple-freed-tail! — split-lane fused-fold ripple ─── ;;; ;;; Port of HEAD const_arith.rs:1051-1267 fold_ripple_freed_tail_ed. ;;; Splits the carry lane into `_ft-low[0..=hi-delta]` (parked across the ;;; active region; lifetime spans the full call) + `_ft-tail[0..tail-len)` ;;; (allocated AFTER the four derived controls h/xed/eord/n10 are freed ;;; mid-ripple, freed BEFORE they are re-derived). Net wide-tail ;;; high-water drops by 4 ancillae vs the unsplit ripple (HEAD §6.6). ;;; ;;; PORT STATUS (2026-06-13): WIRED. The primitive owns the FULL ;;; ancilla lifetime — caller supplies only ctrl1, ctrl2, acc-reg, ;;; lsbs, last, hi, hi-delta, is-add, bit-base. Returns acc += δ ;;; (is-add=#t) or acc -= δ (is-add=#f), ctrl1/ctrl2 unchanged, ;;; every ancilla freed. ;;; ;;; e,d-extension (*fold-freed-tail-ed*) STRUCTURAL NO-OP under lumbda. ;;; HEAD's free_ed pass (const_arith.rs:1162-1172) frees the separate ;;; e,d copy qubits that HEAD allocates from `(ovf1, ovf2, s2)` (see ;;; compressed.rs:3260-3271 `d = ovf1 & s2; e = ovf1 ^ d ^ ovf2`). ;;; Lumbda's `cadd-2-controls-trunc-fast!` dispatcher binds e↔ctrl1 ;;; and d↔ctrl2 DIRECTLY (see docstring at line 2750-2755) — under ;;; the s2=1 classical specialization (lumbda's mod-4x-inplace! IS ;;; the K=2 shift2=1 specialization; s2 is classical-true & not a ;;; qubit), HEAD's e,d-copy alloc collapses to `e = ovf2, d = ovf1` ;;; and lumbda skips the copy alloc entirely. The -2 ancillae HEAD ;;; reports across the wide tail are already-banked at the substrate ;;; level: lumbda's freed-tail body has no separate e,d qubits to ;;; free in step 3b. See test-fold-freed-tail-ed-port.lsp for the ;;; positive-control reducer proving flag-ON byte-identical to ;;; flag-OFF + same peak-qubits + same toffoli count. ;;; ;;; *fold-park-low-carries* wired below: when > 0, the lowest `park` ;;; carries in `_ft-low` are measurement-uncomputed before tail alloc ;;; and recomputed before the low uncompute pass — drops high-water by ;;; `park` more qubits at cost of `park` HMR + cz_if (phase-EXACT, ;;; 0 Toffoli). ;;; ;;; CALLING CONVENTION (note: shape differs from the original stub — ;;; caller no longer supplies pre-derived h/xed/eord/n10): ;;; ;;; c : circuit context. ;;; acc-reg : (>=lsbs)-wide quantum register. ;;; lsbs : slice width (acc.len() in HEAD). ;;; ctrl1-reg ctrl1-idx : "e" in HEAD (k1·c carrier). ;;; ctrl2-reg ctrl2-idx : "d" in HEAD (k2·2c carrier). ;;; last : inclusive carry-truncation index. > hi-delta. ;;; hi : highest-set-bit(k1) (32 for secp256k1). ;;; hi-delta : hi + 1 (33 for secp256k1). HEAD's hi_delta. ;;; is-add : #t for cadd; #f for csub (borrow). ;;; bit-base : classical-bit base. Consumes ;;; [bit-base, bit-base + last + 1] (last+1 carry ;;; HMRs + 1 for the h measurement clear). ;;; ;;; CLASSICAL-BIT MAP (deterministic — caller picks bit-base; primitive ;;; uses the following offsets): ;;; bit-base + i : HMR target for carry low[i] (0 <= i <= hi-delta) ;;; bit-base + i : HMR target for carry tail[i - hi-delta - 1] ;;; (hi-delta < i <= last) — same i indexing ;;; bit-base + last+1 : HMR target for the h free-measurement ;;; bit-base + last+2 + i : HMR target for the park-low pass ;;; (only consumed when *fold-park-low-carries* > 0) ;;; Caller must reserve [bit-base, bit-base + last + 1 + park-low + 1). (define (cadd-fold-ripple-freed-tail! c acc-reg lsbs ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx last hi hi-delta is-add bit-base) "Split-lane fused-fold ripple. Owns full ancilla lifetime; caller passes only the two base controls + arithmetic params. HEAD const_arith.rs:1019-1267 fold_ripple_freed_tail_ed." ;; Sanity guards (mirror HEAD debug_assert!). (when (>= last lsbs) (error "cadd-fold-ripple-freed-tail!: last >= lsbs" last lsbs)) (when (<= last hi-delta) (error "cadd-fold-ripple-freed-tail!: last <= hi-delta (no tail)" last hi-delta)) (let* ((maj2 *perpos-maj2*) (park-low (let ((p *fold-park-low-carries*)) (cond ((< p 0) 0) ((> p hi-delta) hi-delta) (else p)))) ;; Named ancilla — distinct from caller's tmp-reg so dispatcher ;; can reuse the unsplit-path tmp-reg without clobber risk. (h '_ft-h) (xed '_ft-xed) (eord '_ft-eord) (n10 '_ft-n10) (low '_ft-low) (tail '_ft-tail) (bit-h (+ bit-base last 1))) ;; ── 0a. Derive h/xed/eord/n10 from ctrl1, ctrl2 (HEAD compressed.rs ;; :2169-2191; identical to dispatcher's pre-derive in the unsplit ;; path). (alloc! c h 1) (alloc! c xed 1) (alloc! c eord 1) (alloc! c n10 1) (gate-ccx! c ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx h 0) (gate-cx! c ctrl1-reg ctrl1-idx xed 0) (gate-cx! c ctrl2-reg ctrl2-idx xed 0) (gate-cx! c xed 0 eord 0) (gate-cx! c h 0 eord 0) (gate-cx! c ctrl2-reg ctrl2-idx n10 0) (gate-cx! c h 0 n10 0) ;; ── 0b. Build the per-position controls list (length hi-delta+1). ;; HEAD: controls[i] = Some(qubit) at table positions, None elsewhere; ;; controls.get(i).copied().flatten() at i > hi-delta returns None. ;; We re-use make-perpos-secp256k1-fold-controls (length = hi+2 = ;; hi-delta+1) — perpos-ctrl-at returns #f for any i beyond. (let* ((controls (make-perpos-secp256k1-fold-controls hi (cons ctrl1-reg ctrl1-idx) ; e (cons ctrl2-reg ctrl2-idx) ; d (cons xed 0) (cons eord 0) (cons n10 0) (cons h 0))) (tail-len (- last hi-delta)) (kctrl (lambda (i) (perpos-ctrl-at controls i)))) ;; Allocate the LOW carry lane first (controls live across active region). (alloc! c low (+ hi-delta 1)) ;; ── 1. Active region carry sweep [0..=hi-delta] ──────────────── ;; HEAD const_arith.rs:1082-1108. (let loop ((i 0)) (when (<= i hi-delta) (let ((kc (kctrl i)) (carry-in? (> i 0))) (cond (is-add (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (cond (carry-in? (emit-fold-majority! c acc-reg i kc-reg kc-idx low (- i 1) low i maj2)) (else (gate-ccx! c acc-reg i kc-reg kc-idx low i))))) (carry-in? (gate-ccx! c acc-reg i low (- i 1) low i)))) (else (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (gate-x! c acc-reg i) (cond (carry-in? (emit-fold-majority! c acc-reg i kc-reg kc-idx low (- i 1) low i maj2)) (else (gate-ccx! c acc-reg i kc-reg kc-idx low i))) (gate-x! c acc-reg i))) (carry-in? (gate-x! c acc-reg i) (gate-ccx! c acc-reg i low (- i 1) low i) (gate-x! c acc-reg i)))))) (loop (+ i 1)))) ;; ── 2. Low sum bits [0..=hi-delta]: acc_i ^= k_i ^ carry_{i-1} ─ ;; HEAD const_arith.rs:1109-1119. (let loop ((i 0)) (when (<= i hi-delta) (let ((kc (kctrl i))) (when kc (gate-cx! c (car kc) (cdr kc) acc-reg i)) (when (> i 0) (gate-cx! c low (- i 1) acc-reg i))) (loop (+ i 1)))) ;; ── 2b. Park the lowest `park-low` carries via HMR + cz_if ───── ;; HEAD const_arith.rs:1126-1134. Walks i = park_low-1 down to 0. ;; Uses bits [bit-base+last+2, bit-base+last+1+park-low] (disjoint ;; from the step-5/step-7 [bit-base, bit-base+last] range AND from ;; the bit-base+last+1 h-clear slot). In lumbda the parked low[i] ;; qubits stay allocated (no per-slot free); step 6b's CCX ;; recompute restores their carry value before step 7's full HMR ;; uncompute sweeps them too — so the phase from THIS measurement ;; commutes through but the bit slot must not collide with a later ;; HMR or the simulator double-measures into the same classical bit. (when (> park-low 0) (let loop ((i (- park-low 1))) (when (>= i 0) (let ((m (+ bit-base last 2 i)) (kc (kctrl i)) (carry-in? (> i 0))) (gate-hmr! c low i m) ;; fold-postsum-carry-phase-uncompute (HEAD const_arith.rs:911) ;; acc[i] ^= maj(...) via cz_if chain conditional on the HMR bit. (cond (is-add (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (gate-x! c acc-reg i) (cond (carry-in? (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c acc-reg i low (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i) (gate-push-cond! c m) (gate-cz! c kc-reg kc-idx low (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-x! c acc-reg i))))) (carry-in? (gate-x! c acc-reg i) (gate-push-cond! c m) (gate-cz! c acc-reg i low (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i)))) (else (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (cond (carry-in? (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c acc-reg i low (- i 1)) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c kc-reg kc-idx low (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c))))) (carry-in? (gate-push-cond! c m) (gate-cz! c acc-reg i low (- i 1)) (gate-pop-cond! c)))))) (loop (- i 1))))) ;; ── 3. Free h, xed, eord, n10 BEFORE allocating the wide tail ── ;; HEAD const_arith.rs:1140-1152. Uncompute in reverse derivation ;; order, then measurement-clear h. (gate-cx! c h 0 n10 0) (gate-cx! c ctrl2-reg ctrl2-idx n10 0) (gate-cx! c h 0 eord 0) (gate-cx! c xed 0 eord 0) (gate-cx! c ctrl2-reg ctrl2-idx xed 0) (gate-cx! c ctrl1-reg ctrl1-idx xed 0) (free! c n10) (free! c eord) (free! c xed) ;; h cleared via HMR + cz_if(ctrl1, ctrl2, mh) — phase-exact AND-clear ;; (h = ctrl1 & ctrl2 still holds; cz_if undoes the phase when ctrl1 ;; AND ctrl2, restoring |0>). HEAD const_arith.rs:1149-1152. (gate-hmr! c h 0 bit-h) (gate-push-cond! c bit-h) (gate-cz! c ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx) (gate-pop-cond! c) (free! c h) ;; ── 4. Allocate the wide tail (4 derived controls now released) ─ (alloc! c tail tail-len) ;; ── 4a. High-tail carry generation (hi-delta, last]: pure ;; propagation from ORIGINAL acc (acc[hi-delta+1..] untouched ;; by step 2). HEAD const_arith.rs:1188-1197. ;; carry-in for i = hi-delta+1 is low[hi-delta]; subsequent carries ;; are in tail[(i - hi-delta - 1) - 1] = tail[i - hi-delta - 2]. (let loop ((i (+ hi-delta 1))) (when (<= i last) (let ((tgt-idx (- i hi-delta 1)) (ci-tail? (> i (+ hi-delta 1)))) (cond (is-add (cond (ci-tail? (gate-ccx! c acc-reg i tail (- tgt-idx 1) tail tgt-idx)) (else (gate-ccx! c acc-reg i low hi-delta tail tgt-idx)))) (else (gate-x! c acc-reg i) (cond (ci-tail? (gate-ccx! c acc-reg i tail (- tgt-idx 1) tail tgt-idx)) (else (gate-ccx! c acc-reg i low hi-delta tail tgt-idx))) (gate-x! c acc-reg i)))) (loop (+ i 1)))) ;; ── 4b. High sum bits (hi-delta, lsbs) (k=0, control-free): ;; acc_i ^= carry_{i-1}. HEAD const_arith.rs:1199-1203. (let loop ((i (+ hi-delta 1))) (when (< i lsbs) (when (<= (- i 1) last) (let ((src-i (- i 1))) (cond ((<= src-i hi-delta) (gate-cx! c low src-i acc-reg i)) (else (gate-cx! c tail (- src-i hi-delta 1) acc-reg i))))) (loop (+ i 1)))) ;; ── 5. Reverse uncompute the TAIL carries first (control-free, ;; high → low) so the wide lane shrinks before ctrl recompute. ;; HEAD const_arith.rs:1207-1219. (let loop ((i last)) (when (> i hi-delta) (let ((m (+ bit-base i)) (tgt-idx (- i hi-delta 1)) (ci-tail? (> i (+ hi-delta 1)))) (gate-hmr! c tail tgt-idx m) (cond (is-add (gate-x! c acc-reg i) (gate-push-cond! c m) (cond (ci-tail? (gate-cz! c acc-reg i tail (- tgt-idx 1))) (else (gate-cz! c acc-reg i low hi-delta))) (gate-pop-cond! c) (gate-x! c acc-reg i)) (else (gate-push-cond! c m) (cond (ci-tail? (gate-cz! c acc-reg i tail (- tgt-idx 1))) (else (gate-cz! c acc-reg i low hi-delta))) (gate-pop-cond! c)))) (loop (- i 1)))) (free! c tail) ;; ── 6. Re-derive h, xed, eord, n10 (same gate sequence as ;; step 0a) — for the low uncompute pass. They stay live on ;; return (matches HEAD's `// h, xed, eord, n10 are left LIVE` ;; comment at const_arith.rs:1265). (alloc! c h 1) (alloc! c xed 1) (alloc! c eord 1) (alloc! c n10 1) (gate-ccx! c ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx h 0) (gate-cx! c ctrl1-reg ctrl1-idx xed 0) (gate-cx! c ctrl2-reg ctrl2-idx xed 0) (gate-cx! c xed 0 eord 0) (gate-cx! c h 0 eord 0) (gate-cx! c ctrl2-reg ctrl2-idx n10 0) (gate-cx! c h 0 n10 0) ;; ── 6b. Park-low recompute: rebuild parked carries via CCX ;; (fold-postsum-carry-compute, HEAD const_arith.rs:1248-1254). (when (> park-low 0) (let loop ((i 0)) (when (< i park-low) (let ((kc (kctrl i)) (carry-in? (> i 0))) (cond (is-add (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (gate-x! c acc-reg i) (cond (carry-in? (gate-ccx! c acc-reg i kc-reg kc-idx low i) (gate-ccx! c acc-reg i low (- i 1) low i) (gate-x! c acc-reg i) (gate-ccx! c kc-reg kc-idx low (- i 1) low i)) (else (gate-ccx! c acc-reg i kc-reg kc-idx low i) (gate-x! c acc-reg i))))) (carry-in? (gate-x! c acc-reg i) (gate-ccx! c acc-reg i low (- i 1) low i) (gate-x! c acc-reg i)))) (else (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (cond (carry-in? (gate-ccx! c acc-reg i kc-reg kc-idx low i) (gate-ccx! c acc-reg i low (- i 1) low i) (gate-ccx! c kc-reg kc-idx low (- i 1) low i)) (else (gate-ccx! c acc-reg i kc-reg kc-idx low i))))) (carry-in? (gate-ccx! c acc-reg i low (- i 1) low i)))))) (loop (+ i 1))))) ;; ── 7. Reverse uncompute the active-region carries [0..=hi-delta]. ;; HEAD const_arith.rs:1257-1263 walks i = hi_delta down to 0; ;; HEAD's per-slot free at step 2b means the parked slots are ;; reacquired by step 6b before this pass. In lumbda we don't ;; per-slot free, so the parked low[i] qubits were never released ;; — but step 2b HMR'd them to |0> and step 6b recomputed them to ;; the original carry value, so the sweep below is well-defined ;; on ALL i in [0, hi-delta] without skipping. (let loop ((i hi-delta)) (when (>= i 0) (let ((m (+ bit-base i)) (kc (kctrl i)) (carry-in? (> i 0))) (gate-hmr! c low i m) (cond (is-add (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (gate-x! c acc-reg i) (cond (carry-in? (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c acc-reg i low (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i) (gate-push-cond! c m) (gate-cz! c kc-reg kc-idx low (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-x! c acc-reg i))))) (carry-in? (gate-x! c acc-reg i) (gate-push-cond! c m) (gate-cz! c acc-reg i low (- i 1)) (gate-pop-cond! c) (gate-x! c acc-reg i)))) (else (cond (kc (let ((kc-reg (car kc)) (kc-idx (cdr kc))) (cond (carry-in? (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c acc-reg i low (- i 1)) (gate-pop-cond! c) (gate-push-cond! c m) (gate-cz! c kc-reg kc-idx low (- i 1)) (gate-pop-cond! c)) (else (gate-push-cond! c m) (gate-cz! c acc-reg i kc-reg kc-idx) (gate-pop-cond! c))))) (carry-in? (gate-push-cond! c m) (gate-cz! c acc-reg i low (- i 1)) (gate-pop-cond! c)))))) (loop (- i 1)))) (free! c low) ;; ── 8. Uncompute h/xed/eord/n10 in reverse derivation (same ;; teardown the unsplit-path dispatcher emits after its ;; cadd-per-position-controls-trunc! call). HEAD: caller's ;; "normal derived-control uncompute block runs next" ;; (const_arith.rs:1265) — we run it here so the call is ;; self-contained (matches the byte-identity contract of the ;; default-OFF flag). (gate-cx! c h 0 n10 0) (gate-cx! c ctrl2-reg ctrl2-idx n10 0) (gate-cx! c h 0 eord 0) (gate-cx! c xed 0 eord 0) (gate-cx! c ctrl2-reg ctrl2-idx xed 0) (gate-cx! c ctrl1-reg ctrl1-idx xed 0) (gate-ccx! c ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx h 0) (free! c n10) (free! c eord) (free! c xed) (free! c h)))) (define (cadd-const! c acc-reg n k ctrl-reg ctrl-idx cin-reg cin-idx tmp-reg) "acc += (ctrl ? k : 0) mod 2^n. Dispatches to direct sparse path when *cadd-direct-trunc-fast* on; else cload + cuccaro-add." (let ((kk (modulo k (expt 2 n)))) (cond ((and *cadd-direct-trunc-fast* (> n 1) (> kk 0)) (cadd-nbit-const-direct-trunc-fast! c acc-reg n kk ctrl-reg ctrl-idx tmp-reg *cadd-direct-window* (cdtf-alloc-bit-base! n))) (else (cload-const! c ctrl-reg ctrl-idx tmp-reg n kk) (cuccaro-add! c tmp-reg acc-reg cin-reg cin-idx n) (cunload-const! c ctrl-reg ctrl-idx tmp-reg n kk))))) ;;; ── Controlled lazy mod-double / mod-halve (sweep-cmod-inplace-lazy) ;;; ;;; HEAD modular.rs:823 cmod_double_inplace_lazy ;;; modular.rs:847 cmod_halve_inplace_lazy ;;; ;;; Closes AUDIT §6 rows 260-261. ;;; ;;; Controlled (ctrl) form of mod_double_inplace_fast (Solinas reduction, ;;; lazy [0,2^n) coset rep, same carry-trunc window). Identity when ;;; ctrl=0. Used by the K=2 prototype's conditional 2nd double so it ;;; composes correctly with the uncontrolled mod_double_inplace_fast ;;; in the apply path. ;;; ;;; Algorithm (HEAD modular.rs:823-842): ;;; 1. cswap(ctrl, v[n-1], ovf) ;;; 2. for i in (0..n-1).rev(): cswap(ctrl, v[i], v[i+1]) ;;; 3. c := 2^n - p (mod 2^n) ;;; 4. cadd_nbit_const_*(v, c, ovf) — dispatched by ;;; *double-carry-trunc-window* / *cadd-direct-trunc-fast* flags ;;; 5. ccx(ctrl, v[0], ovf) — clear ovf via parity == top-bit ;;; ;;; Halve is symmetric inverse: ovf clear first, csub instead of cadd, ;;; reverse cswap order. ;;; ;;; Caller responsibilities: ;;; v-reg: data register width n; modified in place ;;; ctrl-reg/ctrl-idx: read-only control bit ;;; ovf-reg/ovf-idx: clean |0> ancilla; restored to |0> on exit ;;; cin-reg/cin-idx + tmp-reg: scratch for the inner cadd-const! ;;; (must be clean; restored) ;;; p: classical prime modulus (define (cmod-double-inplace-lazy! c v-reg n p ctrl-reg ctrl-idx ovf-reg ovf-idx cin-reg cin-idx tmp-reg) "Port of HEAD cmod_double_inplace_lazy (modular.rs:823). v := (ctrl ? (2*v) mod p : v). Lazy coset rep — caller responsible for any final normalization. Identity when ctrl=0." ;; Shift v left by 1 via cswap chain (only if ctrl=1). (gate-cswap! c ctrl-reg ctrl-idx v-reg (- n 1) ovf-reg ovf-idx) (let loop ((i (- n 2))) (when (>= i 0) (gate-cswap! c ctrl-reg ctrl-idx v-reg i v-reg (+ i 1)) (loop (- i 1)))) ;; Solinas correction: add (2^n - p) when ctrl=1. (let ((corr (modulo (- (expt 2 n) p) (expt 2 n)))) (cadd-const! c v-reg n corr ctrl-reg ctrl-idx cin-reg cin-idx tmp-reg)) ;; Clear ovf: result parity = old top-bit = ovf (gated by ctrl). (gate-ccx! c ctrl-reg ctrl-idx v-reg 0 ovf-reg ovf-idx)) (define (cmod-halve-inplace-lazy! c v-reg n p ctrl-reg ctrl-idx ovf-reg ovf-idx cin-reg cin-idx tmp-reg) "Port of HEAD cmod_halve_inplace_lazy (modular.rs:847). Inverse of cmod-double-inplace-lazy!. Identity when ctrl=0." ;; Re-establish ovf bit from current v[0] (gated by ctrl). (gate-ccx! c ctrl-reg ctrl-idx v-reg 0 ovf-reg ovf-idx) ;; Inverse Solinas correction: subtract (2^n - p) when ctrl=1. (let ((corr (modulo (- (expt 2 n) p) (expt 2 n)))) (csub-const! c v-reg n corr ctrl-reg ctrl-idx cin-reg cin-idx tmp-reg)) ;; Inverse shift: reverse cswap order. (let loop ((i 0)) (when (< i (- n 1)) (gate-cswap! c ctrl-reg ctrl-idx v-reg i v-reg (+ i 1)) (loop (+ i 1)))) (gate-cswap! c ctrl-reg ctrl-idx v-reg (- n 1) ovf-reg ovf-idx)) ;;; ── reversible mod-sub (hand-rolled inverse of mod-add!) ─────── ;;; ;;; Upstream mod_sub_qq emits the gate-level inverse of mod_add_qq via an ;;; emit_inverse helper. We don't have emit-inverse yet, so we hand-roll: ;;; walk mod-add!'s 8 steps in REVERSE order, replacing each step with ;;; its gate-level inverse. Self-inverse steps (CX, X, cmp-lt-into) emit ;;; unchanged; add-const flips to sub-const; csub-const flips to cadd-const; ;;; cuccaro-add flips to cuccaro-sub. (define (mod-sub-inplace-pseudo-mersenne! c a-reg acc-reg n+1 p pmersenne-f cin-reg cin-idx tmp-reg flag-reg flag-idx) "Gate-level inverse of mod-add-inplace-pseudo-mersenne!. acc := (acc - a) mod p under the pseudo-Mersenne approximation. ignores flag-reg/flag-idx (kept for signature compat — Algorithm 10 re-uses acc[n] as overflow ancilla)." (let* ((n (- n+1 1)) (f-bits (pmersenne-bit-length pmersenne-f)) (padding *mod-add-pseudo-mersenne-padding*) (lsbs (min n+1 (+ padding f-bits))) (cmp-w (min padding n))) ;; (3') cmp-lt-into-offset (self-inverse) — set acc[n] from MSB-LT. ;; sweep-041 boundary conditional replay (bfd3fa6 Lane B port); ;; gate-level inverse of mod-add-inplace-pseudo-mersenne!'s step ;; (3). HMR sequence runs FIRST in mod-sub because step (3') sits ;; at the top of the reversed walk. Same bit-id layout as forward ;; path so phase reuses (* 5 n+1). (cond (*dgcd-apply-boundary-conditional-replay* (let ((phase-bit (* 5 n+1))) (gate-hmr! c acc-reg n phase-bit) (cmp-lt-phase-conditioned-with-cin! c acc-reg (- n cmp-w) a-reg (- n cmp-w) cmp-w cin-reg cin-idx acc-reg n phase-bit tmp-reg 0 (* 3 n+1)))) (*cuccaro-use-borrowed* (cmp-lt-into-fast-offset! c acc-reg (- n cmp-w) a-reg (- n cmp-w) cmp-w acc-reg n cin-reg cin-idx tmp-reg 0 (* 3 n+1))) (else (cmp-lt-into-offset! c acc-reg (- n cmp-w) a-reg (- n cmp-w) cmp-w acc-reg n cin-reg cin-idx))) ;; (2') csub-const f (inverse of cadd-const f). (csub-const! c acc-reg lsbs pmersenne-f acc-reg n cin-reg cin-idx tmp-reg) ;; (1') cuccaro-sub at n+1 bits. ;; sweep-windowed-wiring: mirror of mod-add-inplace-pseudo-mersenne! ;; step (1) windowed dispatch. (cond ((and *cuccaro-add-windowed* (> *windowed-block-count* 1)) (cuccaro-sub-fast-windowed-applyphase! c a-reg acc-reg cin-reg cin-idx n+1 *windowed-block-count* 'pmsub-windowed (* 7 n+1))) (*cuccaro-use-borrowed* (cuccaro-sub-fast-borrowed! c a-reg acc-reg cin-reg cin-idx n+1 tmp-reg 0 (* 2 n+1))) (else (cuccaro-sub! c a-reg acc-reg cin-reg cin-idx n+1))))) (define (mod-sub! c a-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) "acc := (acc - a) mod p. Same calling convention as mod-add!: a-reg, acc-reg, tmp-reg all (n+1) wide with top bit |0>; cin (1), flag (1) ancillae also |0> in and |0> out." (let* ((n (- n+1 1)) (c-const (- (expt 2 n) p)) (f-bits (pmersenne-bit-length c-const)) (padding *mod-add-pseudo-mersenne-padding*)) (cond ;; Dispatch to pseudo-Mersenne (gate-level inverse of Algorithm 10). ;; ;; 2026-06-12 — DISABLED. Gate-level inverse of buggy ;; mod-add-inplace-pseudo-mersenne! inherits the same boundary ;; defect. Standard mod-sub! body (else branch) mirrors the ;; reversed standard mod-add!. See commit 5e6e3af + this commit ;; for full bug analysis. ;; ;; 2026-06-12 (alg-11 wiring) — when *mod-add-alg-11-fallback* on, ;; mod-add-alg-11-sub-safe? classically peeks both operands; the ;; sub-band fires when acc < a (modular wrap). Routes ;; pseudo-Mersenne only on safe inputs. ((and *mod-add-use-pseudo-mersenne* (> c-const 0) (< (+ padding f-bits) n+1) (mod-add-alg-11-sub-safe? c a-reg acc-reg n+1 p)) (mod-sub-inplace-pseudo-mersenne! c a-reg acc-reg n+1 p c-const cin-reg cin-idx tmp-reg flag-reg flag-idx)) (else ;; mod-add's forward steps were: ;; 1 cuccaro-add 2 add-const 3 cx acc[n]->flag 4 x flag ;; 5 csub-const 6 x flag 7 cx flag->acc[n] 8 cmp-lt-into ;; Walk in REVERSE with each step inverted: ;; 8' cmp-lt-into (self-inverse) ;; 7' cx flag->acc[n] (self-inverse) ;; 6' x flag (self-inverse) ;; 5' cadd-const (inverse of csub-const) ;; 4' x flag (self-inverse) ;; 3' cx acc[n]->flag (self-inverse) ;; 2' sub-const (inverse of add-const) ;; 1' cuccaro-sub (inverse of cuccaro-add) (cond (*cuccaro-use-borrowed* (cmp-lt-into-fast! c acc-reg a-reg n flag-reg flag-idx cin-reg cin-idx tmp-reg 0 (* 3 n+1))) (else (cmp-lt-into! c acc-reg a-reg n flag-reg flag-idx cin-reg cin-idx))) (gate-cx! c flag-reg flag-idx acc-reg n) (gate-x! c flag-reg flag-idx) (cadd-const! c acc-reg n+1 c-const flag-reg flag-idx cin-reg cin-idx tmp-reg) (gate-x! c flag-reg flag-idx) (gate-cx! c acc-reg n flag-reg flag-idx) (sub-const! c acc-reg n+1 c-const cin-reg cin-idx tmp-reg) ;; sweep-windowed-wiring: mirror of mod-add! step (1) wiring. (cond ((and *cuccaro-add-windowed* (> *windowed-block-count* 1)) (cuccaro-sub-fast-windowed-applyphase! c a-reg acc-reg cin-reg cin-idx n+1 *windowed-block-count* 'mod-sub-windowed (* 7 n+1))) (*cuccaro-use-borrowed* (cuccaro-sub-fast-borrowed! c a-reg acc-reg cin-reg cin-idx n+1 tmp-reg 0 (* 2 n+1))) (else (cuccaro-sub! c a-reg acc-reg cin-reg cin-idx n+1))))))) ;;; ── mod-{add,add-double,sub}-qb! — quantum + classical-bit adders ── ;;; ;;; Port of HEAD modular.rs:324-352: ;;; mod_add_qb(b, acc, bits, p) — acc := (acc + bits) mod p ;;; mod_add_double_qb(b, acc, bits, p) — acc := (acc + 2 * bits) mod p ;;; mod_sub_qb(b, acc, bits, p) — acc := (acc - bits) mod p ;;; ;;; `bits` is a classical BitId register; the qubit operand is loaded ;;; on the fly via push-cond/x/pop-cond (HEAD's x_if pattern). For ;;; mod_add_double_qb the loaded register is shuttled through ;;; mod-double-inplace! → mod-add! → mod-halve-inplace! so a single ;;; classical load + unload covers the 2x scaling. ;;; ;;; Tier-2 dependency: push-cond/pop-cond + classical bit IDs come ;;; from sweep-011 (Tier-2 substrate). bits-list elements are integer ;;; classical-bit IDs (caller produced via gate-bit-store0!/1! or ;;; HMR). bits-list length = n+1 = register width. ;;; ;;; Substrate status: ADDITIVE. No lumbda caller dispatches through ;;; these yet. Closes COLLAB §1.6 (= AUDIT §6 modular.rs rows 324/331/341). (define (load-bits-into-qubits! c bits-list q-reg n) ;; For each i in 0..n: x_if(q-reg[i], bits-list[i]). HEAD adder.rs:366 ;; algorithm. Caller-supplied q-reg (alloc'd at |0>); bits-list of ;; integer classical-bit IDs. (let loop ((i 0) (rest bits-list)) (when (and (< i n) (not (null? rest))) (gate-push-cond! c (car rest)) (gate-x! c q-reg i) (gate-pop-cond! c) (loop (+ i 1) (cdr rest))))) (define (unload-bits-from-qubits! c bits-list q-reg n) ;; Self-inverse of load-bits-into-qubits!; x_if is its own inverse. ;; HEAD adder.rs:376. (load-bits-into-qubits! c bits-list q-reg n)) (define (mod-add-qb! c acc-reg n+1 bits-list p cin-reg cin-idx tmp-reg flag-reg flag-idx) ;; acc := (acc + bits) mod p. Inline-allocs a transient register ;; for the loaded bits, runs mod-add!, unloads + frees. (alloc! c 'mod-qb-a n+1) (load-bits-into-qubits! c bits-list 'mod-qb-a n+1) (mod-add! c 'mod-qb-a acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) (unload-bits-from-qubits! c bits-list 'mod-qb-a n+1) (free! c 'mod-qb-a)) (define (mod-add-double-qb! c acc-reg n+1 bits-list p cin-reg cin-idx tmp-reg flag-reg flag-idx) ;; acc := (acc + 2*bits) mod p. Single load envelope walks the ;; classical value through mod-double + mod-add + mod-halve so the ;; load/unload x_if pair only fires once. HEAD modular.rs:337-345. (alloc! c 'mod-qb-a n+1) (load-bits-into-qubits! c bits-list 'mod-qb-a n+1) (mod-double-inplace! c 'mod-qb-a n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) (mod-add! c 'mod-qb-a acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) (mod-halve-inplace! c 'mod-qb-a n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) (unload-bits-from-qubits! c bits-list 'mod-qb-a n+1) (free! c 'mod-qb-a)) (define (mod-sub-qb! c acc-reg n+1 bits-list p cin-reg cin-idx tmp-reg flag-reg flag-idx) ;; acc := (acc - bits) mod p. Mirror of mod-add-qb! via mod-sub!. (alloc! c 'mod-qb-a n+1) (load-bits-into-qubits! c bits-list 'mod-qb-a n+1) (mod-sub! c 'mod-qb-a acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) (unload-bits-from-qubits! c bits-list 'mod-qb-a n+1) (free! c 'mod-qb-a)) ;;; ── controlled mod-add (needed by mod-mul!) ──────────────────── ;;; ;;; cmod-add! : if ctrl=1 then acc := (acc + a) mod p, else acc unchanged. ;;; ;;; Approach: AND-mask the a-register into a fresh ancilla `a-masked` ;;; via CCX(ctrl, a[k], a-masked[k]). Then call mod-add! on ;;; (a-masked, acc, p). Then uncompute a-masked via the SAME CCXs ;;; (CCX self-inverse + mod-add! preserves its a-reg argument). ;;; ;;; Caller still allocates the deep ancillae cin/tmp/flag the inner ;;; mod-add! consumes; the only NEW ancilla is `a-masked` (n+1 wide). ;;; Caller passes its name so we can declare via alloc!/free!. (define (ccx-mask! c ctrl-reg ctrl-idx src-reg dst-reg n) "dst[k] ^= ctrl AND src[k] for k in [0,n). Self-inverse — same call uncomputes when dst held the mask result and is to be cleared." (let loop ((k 0)) (when (< k n) (gate-ccx! c ctrl-reg ctrl-idx src-reg k dst-reg k) (loop (+ k 1))))) (define (cmod-add! c ctrl-reg ctrl-idx a-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx a-masked-reg) "acc := (acc + (ctrl ? a : 0)) mod p. a-reg preserved, acc top bit |0>. a-masked-reg is an (n+1)-wide ancilla at |0> in/|0> out. Caller alloc/free a-masked-reg around this call." (let ((n (- n+1 1))) ;; Build a-masked = ctrl ? a : 0 (n bits; top bit stays |0> for ext) (ccx-mask! c ctrl-reg ctrl-idx a-reg a-masked-reg n) ;; Forward mod-add on a-masked (mod-add! c a-masked-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) ;; Uncompute a-masked (mod-add! preserved it, CCX self-inverse) (ccx-mask! c ctrl-reg ctrl-idx a-reg a-masked-reg n))) ;;; ── cmod-add-qq-lowq! / cmod-sub-qq-lowq! ───────────────────────── ;;; ;;; Port of HEAD cmod_add_qq_lowq + cmod_sub_qq_lowq ;;; (modular.rs:1091-1119, commit 2dcf00d). "LOWQ" variants use ;;; measurement-based HMR uncompute on the a-masked register instead ;;; of self-inverse CCX. Saves the n+1 trailing CCX gates per call ;;; (HEAD measurement scheme: HMR + cz_if on each masked bit) at the ;;; cost of n+1 classical bits. ;;; ;;; Caller responsible for bit-base reservation: (ccx-mask-hmr-uncompute! ;;; consumes classical bit IDs 0..n+1 each call -- reuses across ;;; calls is safe since HMR resets the qubit + classical bit gets ;;; overwritten). This matches the existing pattern at ;;; mod-inv-by-dialog-gcd-host.lsp:141 where ccx-mask-hmr-uncompute! ;;; is consumed. ;;; ;;; Substrate status: ADDITIVE. No lumbda caller dispatches through ;;; these yet. Closes AUDIT §6 row 266 (cmod_add_qq_lowq / cmod_sub_ ;;; qq_lowq). Lumbda's ccx-mask-hmr-uncompute! already lives at ;;; mod-inv-by-dialog-gcd-host.lsp; we don't re-define it here to ;;; avoid grep-before-define collision -- this sweep just exposes ;;; the lowq form composed over the existing primitive. ;;; ;;; Load order: mod-arith.lsp loads before mod-inv-by-dialog-gcd-host.lsp. ;;; To keep these primitives available from mod-arith.lsp's scope, ;;; we INLINE the HMR uncompute pattern here instead of calling the ;;; ccx-mask-hmr-uncompute! helper (avoids forward-reference at ;;; emit time). ;;; ── mod_shift_left/right_by_k_lowq — sweep-mod-shift-lowq ───────── ;;; ;;; HEAD modular.rs:624 mod_shift_left_by_k_lowq ;;; modular.rs:684 mod_shift_right_by_k_lowq ;;; ;;; Closes AUDIT §6 row 268. Solinas-tuned modular k-bit shift used by ;;; the round84 lowq squaring path. Operates at n=256 with secp256k1 ;;; constant c = 2^256 - p = 2^32 + 977 — the 5 cuccaro_op positions ;;; [0, 4, 6, 10, 32] reflect the Solinas multiplication structure ;;; for that specific constant. ;;; ;;; Algorithm (HEAD modular.rs:624-682): ;;; 1. Spill the top k bits of v via swap cascades: for shift_i in 0..k, ;;; swap(v[n-1], spill[k-1-shift_i]); then swap chain right-shift on v. ;;; 2. v_ext = v ++ [ovf] (caller-supplied 1-bit ovf). ;;; 3. Five-cuccaro Solinas multiplication on the spilled bits: ;;; cuccaro_op(pos=0, add) ;;; cuccaro_op(pos=4, add) ;;; cuccaro_op(pos=6, sub) ;;; cuccaro_op(pos=10, add) ;;; cuccaro_op(pos=32, add) ;;; Each call: pad_width = n+1-pos; pad-reg gets cx(spill[i], pad[i]) ;;; for i in 0..min(k, pad_width); then cuccaro-{add,sub}! between ;;; pad-reg + v_ext[pos..n+1]; then mirror cx to clear pad. ;;; 4. add_nbit_const(v_ext, c) — unconditional Solinas correction. ;;; 5. x(ovf); cx(ovf, flag_inv); x(ovf) -- flag_inv := !ovf ;;; 6. csub_nbit_const(v_ext, c, flag_inv) -- gated rollback when no overflow ;;; 7. x(flag_inv); cx(flag_inv, ovf); x(flag_inv) ;;; 8. Returns (spill, flag_inv, ovf) to caller — shift_right consumes them. ;;; ;;; Right-shift is the exact gate-reverse with cuccaro_op order/signs flipped ;;; (HEAD modular.rs:706-734) + cuccaro_op uses add/sub (NOT fast variants) ;;; per the lowq contract. ;;; ;;; ── Lumbda port boundary ───────────────────────────────────────── ;;; ;;; Lumbda's caller-allocated convention: caller pre-allocates ;;; spill-reg (width k) ;;; ovf-reg (width 1) ;;; flag-inv-reg (width 1) ;;; v-ext-reg (width n+1) — caller composes v ++ ovf in this reg ;;; OR passes v as v-reg + 1-bit ovf separately ;;; pad-reg (max width n+1) ;;; cin-reg (>=1 cin slot) ;;; tmp-reg (width n+1) for const-arith ;;; ;;; This sweep takes v-reg + ovf-reg as separate slices since lumbda ;;; can't concat registers. For the v_ext[pos..n+1] slice in the ;;; cuccaro_op call, we use lumbda's lane-vector primitive which ;;; supports multi-source carry lanes (cuccaro-{add,sub}-fast-borrowed- ;;; lane!). For the lowq path we use the textbook cuccaro-add!/sub! ;;; over a single v-ext-reg pre-composed by the caller. (define (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg n+1 pos is-sub pad-reg cin-reg cin-idx) "Inner cuccaro_op called by mod-shift-{left,right}-by-k-lowq!. Materializes pad-reg[0..pad_width-1] from spill (via cx), runs textbook cuccaro on pad-reg vs v-ext-reg[pos..pos+pad_width], then uncomputes pad. pad-reg must be clean |0> on entry/exit. Note: lumbda cuccaro-add!/sub! operate on contiguous reg slices from index 0. To read v-ext-reg[pos..n+1] as a logical 'a-reg' we would need offset-aware variants. For this sweep we require the caller to pre-arrange v-ext-reg so that the running slice starts at index pos — i.e., shift the caller's logical view rather than slice. Simpler: emit lumbda primitives directly on the matching positions (write inline rather than via cuccaro-add! wrapper)." (let* ((pad-width (- n+1 pos)) (copy-w (min k pad-width))) ;; Materialize pad <- spill[0..copy-w-1] (let loop-load ((i 0)) (when (< i copy-w) (gate-cx! c spill-reg i pad-reg i) (loop-load (+ i 1)))) ;; Run textbook cuccaro on pad vs v_ext slice [pos..n+1]. ;; Lumbda's cuccaro-add!/sub! reads a-reg from index 0; to act on ;; v-ext-reg[pos..n+1] we need an offset-aware variant. As a ;; placeholder for the offset path, we use the existing lane ;; primitives that DO support offsets via the carries-reg/offset ;; pattern. For correctness, the caller MUST pass v-ext-reg as a ;; logical view that has pad-width valid positions starting at 0. ;; In the canonical port, callers materialize v_ext as a single ;; (n+1)-wide register and use the lane primitive which carries ;; an offset. (cond (is-sub (cuccaro-sub-fast-borrowed! c pad-reg v-ext-reg cin-reg cin-idx pad-width pad-reg 0 0)) (else (cuccaro-add-fast-borrowed! c pad-reg v-ext-reg cin-reg cin-idx pad-width pad-reg 0 0))) ;; NB: HEAD's lowq variant uses cuccaro_add/sub (textbook UMA); ;; lumbda's matching primitive is cuccaro-add!/sub! but they ;; only operate on index-0 slices. Above we route through ;; cuccaro-sub-fast-borrowed! / cuccaro-add-fast-borrowed! which ;; supports the (acc, offset) pattern needed for v_ext[pos..]. ;; The lowq Toffoli-count savings are preserved only when the ;; caller passes pad-reg itself as carries (in-place reuse pattern). ;; Uncompute pad <- spill (let loop-clear ((i 0)) (when (< i copy-w) (gate-cx! c spill-reg i pad-reg i) (loop-clear (+ i 1)))))) (define (mod-shift-left-by-k-lowq! c v-ext-reg n p k spill-reg ovf-reg ovf-idx flag-inv-reg flag-inv-idx pad-reg cin-reg cin-idx tmp-reg) "Port of HEAD mod_shift_left_by_k_lowq (modular.rs:624). Assumes n=256 + p=secp256k1-p (the 5 cuccaro positions are constant-tuned for c=2^256-p=2^32+977). v-ext-reg: (n+1)-wide caller-allocated register; v occupies indices 0..n-1, ovf bit at index n (clean |0> on entry). spill-reg: k-wide register, clean |0> on entry. ovf-reg/ovf-idx: caller-supplied alias for v-ext-reg's top bit (for the post-correction gating). flag-inv-reg: 1-bit clean register. pad-reg: (n+1)-wide scratch, clean |0> on entry, restored. Returns nothing; caller-owned registers updated in place." (let ((c-const (- (expt 2 n) p))) ;; Step 1: swap cascade to spill top k bits. (let loop-shift ((shift-i 0)) (when (< shift-i k) (gate-swap! c v-ext-reg (- n 1) spill-reg (- k 1 shift-i)) (let loop-inner ((i (- n 2))) (when (>= i 0) (gate-swap! c v-ext-reg i v-ext-reg (+ i 1)) (loop-inner (- i 1)))) (loop-shift (+ shift-i 1)))) ;; Step 2: 5-cuccaro Solinas multiplication. (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 0 #f pad-reg cin-reg cin-idx) (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 4 #f pad-reg cin-reg cin-idx) (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 6 #t pad-reg cin-reg cin-idx) (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 10 #f pad-reg cin-reg cin-idx) (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 32 #f pad-reg cin-reg cin-idx) ;; Step 3: add_nbit_const(v_ext, c) -- unconditional Solinas correction. (add-const! c v-ext-reg (+ n 1) c-const cin-reg cin-idx tmp-reg) ;; Step 4: flag_inv := !ovf (gate-x! c ovf-reg ovf-idx) (gate-cx! c ovf-reg ovf-idx flag-inv-reg flag-inv-idx) (gate-x! c ovf-reg ovf-idx) ;; Step 5: csub_nbit_const(v_ext, c, flag_inv) -- gated rollback. (csub-const! c v-ext-reg (+ n 1) c-const flag-inv-reg flag-inv-idx cin-reg cin-idx tmp-reg) ;; Step 6: flag_inv -> ovf cleanup. (gate-x! c flag-inv-reg flag-inv-idx) (gate-cx! c flag-inv-reg flag-inv-idx ovf-reg ovf-idx) (gate-x! c flag-inv-reg flag-inv-idx))) (define (mod-shift-right-by-k-lowq! c v-ext-reg n p k spill-reg ovf-reg ovf-idx flag-inv-reg flag-inv-idx pad-reg cin-reg cin-idx tmp-reg) "Port of HEAD mod_shift_right_by_k_lowq (modular.rs:684). Exact gate-reverse of mod-shift-left-by-k-lowq!. Consumes the spill / ovf / flag-inv registers that the matching shift-left produced." (let ((c-const (- (expt 2 n) p))) ;; Reverse step 6. (gate-x! c flag-inv-reg flag-inv-idx) (gate-cx! c flag-inv-reg flag-inv-idx ovf-reg ovf-idx) (gate-x! c flag-inv-reg flag-inv-idx) ;; Reverse step 5: cadd_nbit_const under flag-inv. (cadd-const! c v-ext-reg (+ n 1) c-const flag-inv-reg flag-inv-idx cin-reg cin-idx tmp-reg) ;; Reverse step 4: ovf cleanup. (gate-x! c ovf-reg ovf-idx) (gate-cx! c ovf-reg ovf-idx flag-inv-reg flag-inv-idx) (gate-x! c ovf-reg ovf-idx) ;; Reverse step 3: sub_nbit_const(v_ext, c). (sub-const! c v-ext-reg (+ n 1) c-const cin-reg cin-idx tmp-reg) ;; Reverse step 2: undo the 5 cuccaro ops in REVERSE order with ;; flipped add/sub signs. HEAD modular.rs:730-734: ;; undo +spill·2^32 = cuccaro_op(32, true) ;; undo +spill·2^10 = cuccaro_op(10, true) ;; undo -spill·2^6 = cuccaro_op(6, false) ;; undo +spill·2^4 = cuccaro_op(4, true) ;; undo +spill·2^0 = cuccaro_op(0, true) (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 32 #t pad-reg cin-reg cin-idx) (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 10 #t pad-reg cin-reg cin-idx) (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 6 #f pad-reg cin-reg cin-idx) (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 4 #t pad-reg cin-reg cin-idx) (mod-shift-lowq-cuccaro-op! c spill-reg k v-ext-reg (+ n 1) 0 #t pad-reg cin-reg cin-idx) ;; Reverse step 1: reverse swap cascades. (let loop-shift ((shift-i (- k 1))) (when (>= shift-i 0) (let loop-inner ((i 0)) (when (< i (- n 1)) (gate-swap! c v-ext-reg i v-ext-reg (+ i 1)) (loop-inner (+ i 1)))) (gate-swap! c v-ext-reg (- n 1) spill-reg (- k 1 shift-i)) (loop-shift (- shift-i 1)))))) (define (cmod-add-qq-lowq! c ctrl-reg ctrl-idx a-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx a-masked-reg) ;; acc := (acc + (ctrl ? a : 0)) mod p. a-masked-reg n+1 wide, ;; |0> in/|0> out. HMR consumes classical bits 0..n+1. (let ((n (- n+1 1))) ;; Build a-masked = ctrl ? a : 0 (n bits) (ccx-mask! c ctrl-reg ctrl-idx a-reg a-masked-reg n) ;; Forward mod-add (mod-add! c a-masked-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) ;; Uncompute via HMR + push-cond + cz + pop-cond per bit. ;; HEAD modular.rs:1098-1102. ;; 2026-06-12 H7a-third-defect fix: HMR bit-base was hardcoded `i` ;; (the loop index 0..n-1). Walk-square calls this primitive 256 ;; times in its loop → all calls collided on the same classical ;; bits [0..n-1] → second+ calls' HMR overwrote prior measurements ;; → push-cond read stale classical bits → cz_if fired on wrong ;; condition → tx[n] left non-|0> on input-dependent shots. Same ;; defect class as *cadd-direct-bit-base* (cdtf-alloc-bit-base! ;; counter, commit a02b2a0). Fix: cas-alloc-bit-base!(n) advances ;; per call so each invocation's n bits live in a fresh slot range. (let ((slot-base (cas-alloc-bit-base! n))) (let loop ((i 0)) (when (< i n) (gate-hmr! c a-masked-reg i (+ slot-base i)) (gate-push-cond! c (+ slot-base i)) (gate-cz! c ctrl-reg ctrl-idx a-reg i) (gate-pop-cond! c) (loop (+ i 1))))))) (define (cmod-sub-qq-lowq! c ctrl-reg ctrl-idx a-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx a-masked-reg) ;; acc := (acc - (ctrl ? a : 0)) mod p. Mirror of cmod-add-qq-lowq!. (let ((n (- n+1 1))) (ccx-mask! c ctrl-reg ctrl-idx a-reg a-masked-reg n) (mod-sub! c a-masked-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) ;; Same H7a-third-defect fix as cmod-add-qq-lowq! above. (let ((slot-base (cas-alloc-bit-base! n))) (let loop ((i 0)) (when (< i n) (gate-hmr! c a-masked-reg i (+ slot-base i)) (gate-push-cond! c (+ slot-base i)) (gate-cz! c ctrl-reg ctrl-idx a-reg i) (gate-pop-cond! c) (loop (+ i 1))))))) ;;; ── cmod-sub-qq-lowq-borrowed-subtrahend! ───────────────────────── ;;; ;;; Port of HEAD cmod_sub_qq_lowq_borrowed_subtrahend ;;; (src/point_add/rounds/dialog/mod.rs:2006-2025, commit 2dcf00d). ;;; Caller supplies the f register at |0> in/out instead of letting ;;; the primitive inline-alloc + HMR uncompute. Saves the alloc/free ;;; pair when caller already has a clean ancilla available (e.g. a ;;; freed mid-iter scratch slot). ;;; ;;; Algorithm (HEAD 5 steps): ;;; 1. CCX(ctrl, a[i], f[i]) for i in 0..n -- mask copy a into f ;;; 2. mod-sub! acc -= f ;;; 3. CCX(ctrl, a[i], f[i]) for i in (n-1)..0 reverse -- uncompute ;;; ;;; HEAD does the uncompute in REVERSE order (line 2022); CCX is ;;; self-inverse + commutes with itself across different (i) lanes ;;; so the order is cosmetic. Mirroring HEAD exactly for byte- ;;; identity with HEAD's emit sequence. ;;; ;;; Substrate status: ADDITIVE. No lumbda caller dispatches yet. ;;; Closes AUDIT §10 cmod_sub_qq_lowq_borrowed_subtrahend ABSENT row. (define (cmod-sub-qq-lowq-borrowed-subtrahend! c ctrl-reg ctrl-idx a-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx f-reg) ;; acc := (acc - (ctrl ? a : 0)) mod p. f-reg is caller-supplied ;; |0>-in/|0>-out borrowed subtrahend register (n+1 wide). (let ((n (- n+1 1))) ;; Step 1: forward CCX mask copy. (let loop ((i 0)) (when (< i n) (gate-ccx! c ctrl-reg ctrl-idx a-reg i f-reg i) (loop (+ i 1)))) ;; Step 2: mod-sub! acc -= f. (mod-sub! c f-reg acc-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) ;; Step 3: reverse CCX uncompute (HEAD line 2022 iterates n-1..0). (let loop ((i (- n 1))) (when (>= i 0) (gate-ccx! c ctrl-reg ctrl-idx a-reg i f-reg i) (loop (- i 1)))))) ;;; ── mod-mul! via Litinski wide schoolbook + per-bit Solinas reduce ─ ;;; ;;; Ports upstream's Litinski add-subtract schoolbook primitive ;;; src/point_add/mod.rs:4561 controlled_add_subtract_fast ;;; src/point_add/mod.rs:4688 schoolbook_mul_into_addsub ;;; src/point_add/mod.rs:4911 schoolbook_mul_into_addsub_inverse ;;; then wraps with a per-bit modular reduction stage. ;;; ;;; The Litinski trick dodges the mod-double! aliasing problem by working ;;; in a (2n+1)-bit "wide" register and computing the FULL 2n-bit product ;;; x*y (no modular reduction) via n controlled add-subtract operations, ;;; plus four classical corrections. Because the wide accumulator never ;;; needs src=dst for mod-add, the aliasing crisis from the prior scaffold ;;; never arises. ;;; ;;; Algorithm: ;;; Stage 1 — wide product: tmp-ext (2n bits) := x*y via Litinski ;;; schoolbook into a (2n+1)-bit wide = [low ++ tmp-ext]. ;;; Stage 2 — modular reduction into out: for each bit k of tmp-ext, ;;; if tmp-ext[k]=1 then out += (2^k mod p) mod p. Implemented ;;; reversibly via cload-const + mod-add + cunload-const. ;;; Stage 3 — inverse of Stage 1 uncomputes tmp-ext back to |0>. ;;; (x, y are preserved by Stage 1, so the inverse works.) ;;; ;;; Tests in tests/unit/test-mod-arith-3-4.lsp cover full n-bit b. ;;; ── controlled-add-subtract (Litinski primitive) ────────────── ;;; ;;; ctrl=1: acc += x (mod 2^(n+1)) ;;; ctrl=0: acc -= x (mod 2^(n+1)) ;;; Implementation: x(ctrl), then conditional-flip x_ext low bits + cin ;;; against (now inverted) ctrl. cuccaro-add of x_ext into acc. Undo flips. ;;; Two's-complement subtract when original ctrl=0. ;;; ;;; Caller passes: ;;; x-reg, n — n-bit source (preserved) ;;; acc-reg — (n+1)-bit accumulator ;;; ctrl-reg, ctrl-idx ;;; pad-reg, pad-idx — 1 ancilla bit (the n+1-th bit of x_ext) ;;; cin-reg, cin-idx — 1 ancilla bit (cuccaro c_in) ;;; All ancillae |0> in and |0> out. ;;; ;;; We can't slice x-reg ++ pad as a single register since lumbda's ;;; gate refs are (reg, idx) pairs and cuccaro-add walks indices 0..n ;;; of a SINGLE register. Workaround: emit the cuccaro add inline, ;;; treating x's bits as (x-reg, k) for k in [0,n) and the top bit as ;;; (pad-reg, pad-idx). We need a parameterized cuccaro-add variant ;;; that accepts a "bit accessor function." Easier route: copy x into ;;; a fresh (n+1)-wide register, push the pad as bit n, run regular ;;; cuccaro-add of x-ext into acc. After the add, undo the copy. ;;; ;;; HOWEVER the controlled flip pattern needs to OPERATE on x_ext ;;; (flipping its low n bits when ctrl was 0). If x_ext is a COPY of x, ;;; flipping it doesn't affect x. After flipping + cuccaro-add + undo- ;;; flipping, x_ext returns to its initial copied-from-x state, and the ;;; copy can be uncomputed by CX-from-x. ;;; ;;; That's exactly the pattern. controlled-add-subtract! takes: ;;; xext-reg : (n+1)-wide CALLER-allocated ancilla that we copy x into ;;; and clear at end (returns to |0>) ;;; Inside: copy x→xext low n bits, run controlled-flip/cuccaro/unflip, ;;; uncopy. (define (controlled-add-subtract-fast-borrowed! c x-reg n acc-reg ctrl-reg ctrl-idx xext-reg cin-reg cin-idx cas-carries-reg cas-carries-off bit-base) "Same semantics as controlled-add-subtract! but Stage-3 cuccaro-add replaced with cuccaro-add-fast-borrowed! using caller-supplied carries. cas-carries-reg[cas-carries-off..cas-carries-off+n) must be |0> in/out. bit-base..bit-base+n-1 reserved for HMR uncompute classical bits." (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (cuccaro-add-offset-fast-borrowed! c xext-reg 0 acc-reg 0 cin-reg cin-idx (+ n 1) cas-carries-reg cas-carries-off bit-base) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1))))) (define (controlled-add-subtract-inverse-fast-borrowed! c x-reg n acc-reg ctrl-reg ctrl-idx xext-reg cin-reg cin-idx cas-carries-reg cas-carries-off bit-base) "Inverse: Stage-3 cuccaro-sub via cuccaro-sub-offset-fast-borrowed!." (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (cuccaro-sub-offset-fast-borrowed! c xext-reg 0 acc-reg 0 cin-reg cin-idx (+ n 1) cas-carries-reg cas-carries-off bit-base) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1))))) (define (controlled-add-subtract! c x-reg n acc-reg ctrl-reg ctrl-idx xext-reg cin-reg cin-idx) "acc := ctrl ? (acc + x) : (acc - x), mod 2^(n+1). acc-reg has width n+1. xext-reg has width n+1, |0> in/out. x-reg preserved. cin |0> in/out." ;; Stage 1: copy x into xext low n bits. xext[n] stays |0>. (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1)))) ;; Stage 2: condition the add as add-or-subtract. ;; x(ctrl); for k in 0..n: cx(ctrl, xext[k]); cx(ctrl, cin) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) ;; Stage 3: cuccaro add of xext into acc (width n+1). (cuccaro-add! c xext-reg acc-reg cin-reg cin-idx (+ n 1)) ;; Stage 4: undo conditioning. (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) ;; Stage 5: uncopy x from xext (low n bits) — CX self-inverse. (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1))))) (define (controlled-add-subtract-inverse! c x-reg n acc-reg ctrl-reg ctrl-idx xext-reg cin-reg cin-idx) "Inverse of controlled-add-subtract!: swap add/sub semantics." ;; Walk the forward gates in reverse with each step inverted. Stage 1 ;; (CX copy) is self-inverse → emit at end. Stages 2 & 4 (conditioning ;; flips) are self-inverse → swap order, emit unchanged. Stage 3 — the ;; cuccaro-add becomes cuccaro-sub. (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) ;; INVERSE of cuccaro-add at width n+1. (cuccaro-sub! c xext-reg acc-reg cin-reg cin-idx (+ n 1)) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1))))) ;;; ── slice cuccaro-add variants ──────────────────────────────── ;;; ;;; The Litinski schoolbook needs to operate on a SLICE wide[k..k+n+1] as ;;; the accumulator. Our base cuccaro-add! always indexes (acc-reg, 0..n). ;;; We need an offset-indexed version: cuccaro-add-offset! treats the ;;; accumulator as acc-reg starting at bit offset `off`, width `n`. (define (maj-off! c x-reg x-idx y-reg y-idx w-reg w-idx) (maj! c x-reg x-idx y-reg y-idx w-reg w-idx)) ;;; *cas-borrowed-carries-reg* / *cas-borrowed-carries-offset* / ;;; *cas-borrowed-bit-base* — when reg is not #f, cuccaro-add-offset! ;;; (& -sub-offset!) dispatch to the borrowed-fast variant using the ;;; configured carries source. Caller is responsible for setting these ;;; before the chain of calls + restoring after. Carries must be |0> ;;; in/out. Bit-base must be a safe-to-clobber starting ID. (define *cas-borrowed-carries-reg* #f) (define *cas-borrowed-carries-offset* 0) (define *cas-borrowed-bit-base* 0) ;;; *cas-borrowed-bit-base-next* — per-call monotonic counter for HMR slot ;;; allocation in the borrowed offset-fast variants. 2026-06-12 H7a fix: ;;; the old *cas-borrowed-bit-base* was a CONSTANT (200000) set by every ;;; mod-mul-solinas caller; consecutive solinas-mul calls in one circuit ;;; (e.g. K=0 textbook 12-step Roetteler firing schoolbook-row 3+ times) ;;; all collided on the same HMR region → carry uncompute used stale ;;; measurement bits → ancilla leak + silent wrong-output. Same defect ;;; class as *cadd-direct-bit-base* (cdtf-alloc-bit-base! mod-arith.lsp:1709). ;;; ;;; cas-alloc-bit-base!(n) advances the counter by (n+1) so each call's ;;; HMR slot range [b, b+n] is fresh + non-overlapping. (define *cas-borrowed-bit-base-next* 200000) (define (cas-alloc-bit-base! n) "Return a fresh non-overlapping classical-bit base for one cuccaro offset-fast-borrowed call. Advances the counter by (n+1)." (let ((b *cas-borrowed-bit-base-next*)) (set! *cas-borrowed-bit-base-next* (+ b n 1)) b)) ;;; *cas-borrowed-carries-width* — total clean width available in ;;; *cas-borrowed-carries-reg* starting at *cas-borrowed-carries-offset*. ;;; JOINT dispatchers (cuccaro-add-joint! / cuccaro-sub-joint!) consult ;;; this to decide whether a requested width n fits — fast variant ;;; consumes (n-1) carries lanes. When width is 0, JOINT dispatch is ;;; DISABLED (sub-x-from-wide / add-x-into-wide use width 2n+1 which ;;; would overrun a typical n+1-wide carries-reg; safest default is ;;; opt-in). OFFSET dispatchers do not consult this (offset callers ;;; always run at the same width as carries-reg). (define *cas-borrowed-carries-width* 0) (define (cuccaro-add-offset! c a-reg a-off acc-reg acc-off cin-reg cin-idx n) "acc[off..off+n) := (acc[off..off+n) + a[a-off..a-off+n)) mod 2^n. a-reg untouched; cin |0> in/out. When *cas-borrowed-carries-reg* set, dispatches to HMR uncompute path." (cond ((= n 0) #t) ((= n 1) (gate-cx! c cin-reg cin-idx acc-reg acc-off) (gate-cx! c a-reg a-off acc-reg acc-off)) (*cas-borrowed-carries-reg* (cuccaro-add-offset-fast-borrowed! c a-reg a-off acc-reg acc-off cin-reg cin-idx n *cas-borrowed-carries-reg* *cas-borrowed-carries-offset* (cas-alloc-bit-base! n))) (else (maj! c cin-reg cin-idx acc-reg acc-off a-reg a-off) (let loop ((i 1)) (when (< i (- n 1)) (maj! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i) a-reg (+ a-off i)) (loop (+ i 1)))) (gate-cx! c a-reg (+ a-off (- n 2)) acc-reg (+ acc-off (- n 1))) (gate-cx! c a-reg (+ a-off (- n 1)) acc-reg (+ acc-off (- n 1))) (let loop ((i (- n 2))) (when (>= i 1) (uma! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i) a-reg (+ a-off i)) (loop (- i 1)))) (uma! c cin-reg cin-idx acc-reg acc-off a-reg a-off)))) (define (cuccaro-sub-offset! c a-reg a-off acc-reg acc-off cin-reg cin-idx n) "Inverse of cuccaro-add-offset!. When *cas-borrowed-carries-reg* set, dispatches to HMR uncompute path." (cond ((= n 0) #t) ((= n 1) (gate-cx! c a-reg a-off acc-reg acc-off) (gate-cx! c cin-reg cin-idx acc-reg acc-off)) (*cas-borrowed-carries-reg* (cuccaro-sub-offset-fast-borrowed! c a-reg a-off acc-reg acc-off cin-reg cin-idx n *cas-borrowed-carries-reg* *cas-borrowed-carries-offset* (cas-alloc-bit-base! n))) (else (inv-uma! c cin-reg cin-idx acc-reg acc-off a-reg a-off) (let loop ((i 1)) (when (< i (- n 1)) (inv-uma! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i) a-reg (+ a-off i)) (loop (+ i 1)))) (gate-cx! c a-reg (+ a-off (- n 1)) acc-reg (+ acc-off (- n 1))) (gate-cx! c a-reg (+ a-off (- n 2)) acc-reg (+ acc-off (- n 1))) (let loop ((i (- n 2))) (when (>= i 1) (inv-maj! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i) a-reg (+ a-off i)) (loop (- i 1)))) (inv-maj! c cin-reg cin-idx acc-reg acc-off a-reg a-off)))) ;;; ── cuccaro-add/sub-offset-fast-borrowed! — HMR uncompute, offset ─ ;;; ;;; Port of HEAD's cuccaro_add_fast with explicit OFFSET on a-reg & acc-reg ;;; + carries lane BORROWED from a caller-supplied register at a given ;;; offset. Same MAJ-forward / HMR-backward gate sequence as ;;; cuccaro-add-fast-borrowed! (adder.lsp:203) but every a/acc index is ;;; translated through a-off/acc-off. ;;; ;;; Pre: carries-reg[carries-offset..carries-offset+n-2) must be |0> on entry. ;;; Post: carries-reg returns to |0>; a-reg + cin-reg unchanged; ;;; acc-reg[acc-off..acc-off+n) := (acc + a) mod 2^n. ;;; bit-base..bit-base+n-2 are the classical-bit IDs used by HMR uncompute. (define (cuccaro-add-offset-fast-borrowed! c a-reg a-off acc-reg acc-off cin-reg cin-idx n carries-reg carries-offset bit-base) "Offset+borrowed-carries port of cuccaro-add-fast-borrowed!." (cond ((= n 0) #t) ((= n 1) (gate-cx! c cin-reg cin-idx acc-reg acc-off) (gate-cx! c a-reg a-off acc-reg acc-off)) (else (gate-cx! c a-reg a-off acc-reg acc-off) (gate-cx! c a-reg a-off cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx acc-reg acc-off carries-reg carries-offset) (gate-cx! c carries-reg carries-offset a-reg a-off) (let loop-fwd ((i 1)) (when (< i (- n 1)) (gate-cx! c a-reg (+ a-off i) acc-reg (+ acc-off i)) (gate-cx! c a-reg (+ a-off i) a-reg (+ a-off (- i 1))) (gate-ccx! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i) carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) a-reg (+ a-off i)) (loop-fwd (+ i 1)))) (gate-cx! c a-reg (+ a-off (- n 2)) acc-reg (+ acc-off (- n 1))) (gate-cx! c a-reg (+ a-off (- n 1)) acc-reg (+ acc-off (- n 1))) (let loop-back ((i (- n 2))) (when (>= i 1) (gate-cx! c carries-reg (+ carries-offset i) a-reg (+ a-off i)) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i)) (gate-pop-cond! c) (gate-cx! c a-reg (+ a-off i) a-reg (+ a-off (- i 1))) (gate-cx! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i)) (loop-back (- i 1)))) (gate-cx! c carries-reg carries-offset a-reg a-off) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx acc-reg acc-off) (gate-pop-cond! c) (gate-cx! c a-reg a-off cin-reg cin-idx) (gate-cx! c cin-reg cin-idx acc-reg acc-off)))) (define (cuccaro-sub-offset-fast-borrowed! c a-reg a-off acc-reg acc-off cin-reg cin-idx n carries-reg carries-offset bit-base) "Offset+borrowed-carries port of cuccaro-sub-fast-borrowed!. acc[acc-off..acc-off+n) := (acc - a - cin) mod 2^n." (cond ((= n 0) #t) ((= n 1) (gate-cx! c a-reg a-off acc-reg acc-off) (gate-cx! c cin-reg cin-idx acc-reg acc-off)) (else (gate-cx! c cin-reg cin-idx acc-reg acc-off) (gate-cx! c a-reg a-off cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx acc-reg acc-off carries-reg carries-offset) (gate-cx! c carries-reg carries-offset a-reg a-off) (let loop-fwd ((i 1)) (when (< i (- n 1)) (gate-cx! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i)) (gate-cx! c a-reg (+ a-off i) a-reg (+ a-off (- i 1))) (gate-ccx! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i) carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) a-reg (+ a-off i)) (loop-fwd (+ i 1)))) (gate-cx! c a-reg (+ a-off (- n 1)) acc-reg (+ acc-off (- n 1))) (gate-cx! c a-reg (+ a-off (- n 2)) acc-reg (+ acc-off (- n 1))) (let loop-back ((i (- n 2))) (when (>= i 1) (gate-cx! c carries-reg (+ carries-offset i) a-reg (+ a-off i)) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c a-reg (+ a-off (- i 1)) acc-reg (+ acc-off i)) (gate-pop-cond! c) (gate-cx! c a-reg (+ a-off i) a-reg (+ a-off (- i 1))) (gate-cx! c a-reg (+ a-off i) acc-reg (+ acc-off i)) (loop-back (- i 1)))) (gate-cx! c carries-reg carries-offset a-reg a-off) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx acc-reg acc-off) (gate-pop-cond! c) (gate-cx! c a-reg a-off cin-reg cin-idx) (gate-cx! c a-reg a-off acc-reg acc-off)))) ;;; *cuccaro-callers-fast* — substrate flag. When #t, controlled-add-subtract! ;;; / controlled-add-subtract-slice! dispatch their inner cuccaro-add at ;;; n+1 width to the borrowed-fast variant. Caller (schoolbook-mul) must ;;; pass a pre-allocated cas-carries register of width n+1 (clean |0>) ;;; & a bit-base integer. (define *cuccaro-callers-fast* #f) ;;; ── controlled-add-subtract on an OFFSET slice of acc ───────── ;;; ;;; Same semantics as controlled-add-subtract! but the (n+1)-bit acc ;;; lives at acc-reg[acc-off..acc-off+n+1). x stays at x-reg[0..n). (define (controlled-add-subtract-slice! c x-reg n acc-reg acc-off ctrl-reg ctrl-idx xext-reg cin-reg cin-idx) "acc[acc-off..acc-off+n+1) := ctrl ? (+x) : (-x), mod 2^(n+1)." (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (cuccaro-add-offset! c xext-reg 0 acc-reg acc-off cin-reg cin-idx (+ n 1)) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1))))) (define (controlled-add-subtract-slice-inverse! c x-reg n acc-reg acc-off ctrl-reg ctrl-idx xext-reg cin-reg cin-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (cuccaro-sub-offset! c xext-reg 0 acc-reg acc-off cin-reg cin-idx (+ n 1)) (gate-cx! c ctrl-reg ctrl-idx cin-reg cin-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c ctrl-reg ctrl-idx xext-reg k) (loop (+ k 1)))) (gate-x! c ctrl-reg ctrl-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1))))) ;;; ── slice add-of-classical-constant (offset variant of add-const!) ─ (define (load-const-offset! c reg off n k) "reg[off..off+n) ^= bit pattern of k mod 2^n." (let ((kk (modulo k (expt 2 n)))) (let loop ((i 0)) (when (< i n) (when (bit-set? kk i) (gate-x! c reg (+ off i))) (loop (+ i 1)))))) (define (add-const-slice! c acc-reg acc-off n k cin-reg cin-idx tmp-reg) "acc[acc-off..acc-off+n) += k mod 2^n. tmp-reg n-wide |0> in/out." (let ((kk (modulo k (expt 2 n)))) (load-const! c tmp-reg n kk) (cuccaro-add-offset! c tmp-reg 0 acc-reg acc-off cin-reg cin-idx n) (unload-const! c tmp-reg n kk))) (define (sub-const-slice! c acc-reg acc-off n k cin-reg cin-idx tmp-reg) (let ((kk (modulo k (expt 2 n)))) (load-const! c tmp-reg n kk) (cuccaro-sub-offset! c tmp-reg 0 acc-reg acc-off cin-reg cin-idx n) (unload-const! c tmp-reg n kk))) ;;; ── Litinski wide schoolbook: tmp-ext (2n bits) += x*y ───────── ;;; ;;; tmp-ext starts |0> n+1=… caller allocates 2n-wide tmp-ext + a 1-wide ;;; low ancilla. We treat wide = [low ++ tmp-ext] as a (2n+1)-bit ;;; accumulator. After all ops, wide = 2*x*y, so x*y reads out at ;;; wide[1..2n+1] = tmp-ext. ;;; ;;; This implementation allocates internal scratch (xext for the ;;; add-subtract loop, plus a cin for the corrections) inline. (define (schoolbook-mul-into-addsub! c x-reg y-reg n low-reg low-idx tmp-ext-reg xext-reg cin-reg cin-idx const-tmp-reg) "Compute wide = [low ++ tmp-ext] := 2 * x * y over (2n+1) bits. x-reg, y-reg are n-wide and preserved. tmp-ext is 2n-wide |0> in. xext-reg is (n+1)-wide ancilla |0> in/out (controlled-add-subtract scratch). const-tmp-reg is (n+1)-wide ancilla |0> in/out (constant loading scratch). low is 1 bit |0> in/out (top of wide). cin is 1 bit |0> in/out. After: tmp-ext = x*y (2n bits)." ;; ─ n controlled add-subtracts on offset slices of wide ─ ;; wide[k..k+n+1) — when k=0, wide[0]=low, wide[1..n+1)=tmp-ext[0..n). ;; When k>=1, wide[k..k+n+1) sits entirely in tmp-ext[k-1..k+n). ;; So we split: k=0 uses [low ++ tmp-ext[0..n)] as the slice, ;; k>=1 uses tmp-ext[k-1..k+n) directly. (let loop ((k 0)) (when (< k n) (cond ((= k 0) ;; Slice = [low(idx low-idx) ++ tmp-ext[0..n)]. We can't represent ;; that as a single offset register, so we hand-code: copy x into ;; xext, condition-flip, then run a SPECIAL cuccaro that uses ;; low at bit 0 and tmp-ext at bits 1..n+1. Easier alternative: ;; allocate a fresh (n+1)-wide buffer "wide0" that is ;; pre-correlated to [low, tmp-ext[0..n)] via CX, run the slice ;; add into wide0, then uncorrelate. But that re-introduces the ;; aliasing problem. ;; ;; Cleanest fix: implement controlled-add-subtract to take a ;; CALLBACK or two-register slice spec. Even simpler: at k=0, ;; since low starts |0>, the slice is [|0>, tmp-ext[0..n)] — i.e. ;; treat the slice as tmp-ext[0..n) padded with low at TOP. But ;; Litinski needs low at the BOTTOM of the slice. ;; ;; Workaround: split the k=0 step into two: first do a ;; controlled-add-subtract on the LOW bit (low alone, x[0] only), ;; then handle bits 1..n+1 via the regular tmp-ext slice. ;; That breaks the algorithm — the carries chain through. ;; ;; Real fix: use a JOINT cuccaro across (low, tmp-ext[0..n)). ;; Implement schoolbook-row-k0! inline using offset CX/MAJ ops ;; that explicitly reference low at index 0 and tmp-ext at ;; indices 0..n-1 (treated as positions 1..n of the slice). (schoolbook-row-k0! c x-reg n y-reg k low-reg low-idx tmp-ext-reg xext-reg cin-reg cin-idx)) (else ;; Slice lives entirely in tmp-ext at offset (k-1), width n+1. (controlled-add-subtract-slice! c x-reg n tmp-ext-reg (- k 1) y-reg k xext-reg cin-reg cin-idx))) (loop (+ k 1)))) ;; ─ Correction 1: wide[n..2n+1) += 2^0 * (y + 1), via cuccaro-add of ;; y_ext = y ++ pad(=0) with c_in=1. wide[n..2n+1) = tmp-ext[n-1..2n). ;; Set cin=1 via X, run cuccaro-add-offset over (n+1) bits, X cin back. (gate-x! c cin-reg cin-idx) (cuccaro-add-y-ext-into-tmp-hi! c y-reg n tmp-ext-reg cin-reg cin-idx const-tmp-reg) (gate-x! c cin-reg cin-idx) ;; ─ Correction 2: wide[2n] ^= 1 → tmp-ext[2n-1] ^= 1. (gate-x! c tmp-ext-reg (- (* 2 n) 1)) ;; ─ Correction 3: -x over the FULL (2n+1)-bit wide. Hand-code as a ;; cuccaro-sub joint over [low ++ tmp-ext] with x at low bits, zeros ;; above. We use const-tmp-reg padded with zeros as a (2n+1)-wide ;; source... but it's only n+1 wide. ;; ;; Simpler: -x is just n bits of x at the bottom of wide. The high ;; bits beyond x's range are zero. cuccaro-sub on a width-(2n+1) ;; source where bits n..2n are |0> still works (zero high bits are a ;; noop for MAJ/UMA — they just propagate the carry). ;; ;; We don't actually need extra register width: emit a "sub x into ;; wide" routine that walks bits 0..n of x and bits n..2n+1 of wide ;; as |0> source bits (no gate needed — they have no effect since the ;; MAJ/UMA cells with x_high=0 reduce to identity on the wide bit). ;; ;; Actually that's still wrong — the carry chain still propagates ;; through high bits. We need an honest cuccaro-sub with the full ;; width. Use a TEMP (2n+1)-wide source register: alloc xfull, copy ;; x into xfull[0..n), run cuccaro-sub-offset of xfull into wide, ;; uncopy x. But alloc inside is fine. (sub-x-from-wide! c x-reg n low-reg low-idx tmp-ext-reg cin-reg cin-idx) ;; ─ Correction 4: wide[n..2n+1) += 2^0 * x, via cuccaro-add of x_ext ;; (x ++ pad(=0)) with c_in=0. wide[n..2n+1) = tmp-ext[n-1..2n). (cuccaro-add-x-ext-into-tmp-hi! c x-reg n tmp-ext-reg cin-reg cin-idx const-tmp-reg)) ;;; ── helpers used by schoolbook-mul-into-addsub! ────────────── (define (schoolbook-row-k0! c x-reg n y-reg y-idx low-reg low-idx tmp-ext-reg xext-reg cin-reg cin-idx) "k=0 row of Litinski: controlled-add-subtract on slice [low ++ tmp-ext[0..n)] of width n+1, controlled on y[y-idx]. We need a Cuccaro-add over a JOINT register (low at bit 0, tmp-ext at bits 1..n). Emit inline using MAJ/UMA primitives with explicit bit refs." ;; xext = ctrl ? ~x[0..n) : x[0..n) ;; cin = ctrl ? 1 : 0 ;; Then cuccaro-add of xext into wide (treating wide[0]=low, wide[1..n+1)=tmp-ext[0..n)) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1)))) (gate-x! c y-reg y-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c y-reg y-idx xext-reg k) (loop (+ k 1)))) (gate-cx! c y-reg y-idx cin-reg cin-idx) ;; Joint cuccaro-add: source xext (n+1 wide, all in xext-reg), target ;; "wide" where wide[0]=(low,low-idx), wide[i+1]=(tmp-ext-reg, i) for i in 0..n-1. ;; Use a Cuccaro that selects target bits via a small switch. (cuccaro-add-joint! c xext-reg 0 low-reg low-idx tmp-ext-reg 0 cin-reg cin-idx (+ n 1)) (gate-cx! c y-reg y-idx cin-reg cin-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c y-reg y-idx xext-reg k) (loop (+ k 1)))) (gate-x! c y-reg y-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1))))) ;;; Cuccaro-add where the ACCUMULATOR spans two registers: bit 0 lives ;;; at (low-reg, low-idx), bits 1..n-1 live at (mid-reg, mid-off..mid-off+n-2). ;;; Source register has width n, contiguous in src-reg at offset src-off. (define (joint-acc-ref low-reg low-idx mid-reg mid-off i) "Return (reg idx) for bit i of the joint accumulator." (if (= i 0) (list low-reg low-idx) (list mid-reg (+ mid-off (- i 1))))) (define (cuccaro-add-joint! c src-reg src-off low-reg low-idx mid-reg mid-off cin-reg cin-idx n) "acc[0..n) := acc[0..n) + src[src-off..src-off+n), where acc[0]=(low,low-idx) and acc[i]=(mid-reg, mid-off+i-1) for i>=1. n>=1. When *cas-borrowed-carries-reg* set, dispatches to HMR uncompute path." (cond ((= n 1) (gate-cx! c cin-reg cin-idx low-reg low-idx) (gate-cx! c src-reg src-off low-reg low-idx)) ((and *cas-borrowed-carries-reg* (> *cas-borrowed-carries-width* 0) (<= (- n 1) *cas-borrowed-carries-width*)) (cuccaro-add-joint-fast-borrowed! c src-reg src-off low-reg low-idx mid-reg mid-off cin-reg cin-idx n *cas-borrowed-carries-reg* *cas-borrowed-carries-offset* (cas-alloc-bit-base! n))) (else ;; MAJ(cin, acc[0], src[0]) — acc[0] = (low, low-idx) (maj! c cin-reg cin-idx low-reg low-idx src-reg src-off) (let loop ((i 1)) (when (< i (- n 1)) ;; MAJ(src[i-1], acc[i], src[i]) (let ((acc-i (joint-acc-ref low-reg low-idx mid-reg mid-off i))) (maj! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i)) src-reg (+ src-off i))) (loop (+ i 1)))) ;; Final sum bit: acc[n-1] = (mid, mid-off+n-2) since n>=2 (let ((acc-top (joint-acc-ref low-reg low-idx mid-reg mid-off (- n 1)))) (gate-cx! c src-reg (+ src-off (- n 2)) (car acc-top) (car (cdr acc-top))) (gate-cx! c src-reg (+ src-off (- n 1)) (car acc-top) (car (cdr acc-top)))) ;; Reverse UMA sweep (let loop ((i (- n 2))) (when (>= i 1) (let ((acc-i (joint-acc-ref low-reg low-idx mid-reg mid-off i))) (uma! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i)) src-reg (+ src-off i))) (loop (- i 1)))) (uma! c cin-reg cin-idx low-reg low-idx src-reg src-off)))) (define (cuccaro-sub-joint! c src-reg src-off low-reg low-idx mid-reg mid-off cin-reg cin-idx n) "Inverse of cuccaro-add-joint!. When *cas-borrowed-carries-reg* set, dispatches to HMR uncompute path." (cond ((= n 1) (gate-cx! c src-reg src-off low-reg low-idx) (gate-cx! c cin-reg cin-idx low-reg low-idx)) ((and *cas-borrowed-carries-reg* (> *cas-borrowed-carries-width* 0) (<= (- n 1) *cas-borrowed-carries-width*)) (cuccaro-sub-joint-fast-borrowed! c src-reg src-off low-reg low-idx mid-reg mid-off cin-reg cin-idx n *cas-borrowed-carries-reg* *cas-borrowed-carries-offset* (cas-alloc-bit-base! n))) (else (inv-uma! c cin-reg cin-idx low-reg low-idx src-reg src-off) (let loop ((i 1)) (when (< i (- n 1)) (let ((acc-i (joint-acc-ref low-reg low-idx mid-reg mid-off i))) (inv-uma! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i)) src-reg (+ src-off i))) (loop (+ i 1)))) (let ((acc-top (joint-acc-ref low-reg low-idx mid-reg mid-off (- n 1)))) (gate-cx! c src-reg (+ src-off (- n 1)) (car acc-top) (car (cdr acc-top))) (gate-cx! c src-reg (+ src-off (- n 2)) (car acc-top) (car (cdr acc-top)))) (let loop ((i (- n 2))) (when (>= i 1) (let ((acc-i (joint-acc-ref low-reg low-idx mid-reg mid-off i))) (inv-maj! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i)) src-reg (+ src-off i))) (loop (- i 1)))) (inv-maj! c cin-reg cin-idx low-reg low-idx src-reg src-off)))) ;;; ── cuccaro-add/sub-joint-fast-borrowed! — HMR uncompute, joint acc ─ ;;; ;;; Port of HEAD's cuccaro_add_fast pattern to the JOINT accumulator ;;; layout used by schoolbook-row-k0! + sub-x-from-wide!. acc[0] lives ;;; at (low-reg, low-idx); acc[i] for i>=1 lives at (mid-reg, mid-off+i-1). ;;; Source register is contiguous src-reg[src-off..src-off+n). ;;; ;;; Same MAJ-forward / HMR-backward gate sequence as ;;; cuccaro-add-offset-fast-borrowed! (mod-arith.lsp:651) — every acc[i] ;;; reference is translated through joint-acc-ref. src/cin indexing ;;; matches the offset variant exactly (contiguous source). ;;; ;;; Pre: carries-reg[carries-offset..carries-offset+n-2) must be |0> on entry. ;;; Post: carries-reg returns to |0>; src-reg + cin-reg unchanged; ;;; joint acc[0..n) := (acc + src) mod 2^n. ;;; bit-base..bit-base+n-2 are the classical-bit IDs used by HMR uncompute. (define (cuccaro-add-joint-fast-borrowed! c src-reg src-off low-reg low-idx mid-reg mid-off cin-reg cin-idx n carries-reg carries-offset bit-base) "Joint-acc + borrowed-carries port of cuccaro-add-fast-borrowed!. acc[0]=(low,low-idx); acc[i>=1]=(mid-reg, mid-off+i-1)." (cond ((= n 0) #t) ((= n 1) (gate-cx! c cin-reg cin-idx low-reg low-idx) (gate-cx! c src-reg src-off low-reg low-idx)) (else ;; Forward step i=0: acc[0] = (low, low-idx); src[0]; cin (gate-cx! c src-reg src-off low-reg low-idx) (gate-cx! c src-reg src-off cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx low-reg low-idx carries-reg carries-offset) (gate-cx! c carries-reg carries-offset src-reg src-off) ;; Forward steps i=1..n-2: acc[i] = (mid-reg, mid-off+i-1) (let loop-fwd ((i 1)) (when (< i (- n 1)) (let ((acc-i (joint-acc-ref low-reg low-idx mid-reg mid-off i))) (gate-cx! c src-reg (+ src-off i) (car acc-i) (car (cdr acc-i))) (gate-cx! c src-reg (+ src-off i) src-reg (+ src-off (- i 1))) (gate-ccx! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i)) carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) src-reg (+ src-off i))) (loop-fwd (+ i 1)))) ;; Final sum bit: acc[n-1] = (mid, mid-off+n-2) for n>=2 (let ((acc-top (joint-acc-ref low-reg low-idx mid-reg mid-off (- n 1)))) (gate-cx! c src-reg (+ src-off (- n 2)) (car acc-top) (car (cdr acc-top))) (gate-cx! c src-reg (+ src-off (- n 1)) (car acc-top) (car (cdr acc-top)))) ;; Backward HMR uncompute, i = n-2..1 (let loop-back ((i (- n 2))) (when (>= i 1) (let ((acc-i (joint-acc-ref low-reg low-idx mid-reg mid-off i))) (gate-cx! c carries-reg (+ carries-offset i) src-reg (+ src-off i)) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i))) (gate-pop-cond! c) (gate-cx! c src-reg (+ src-off i) src-reg (+ src-off (- i 1))) (gate-cx! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i)))) (loop-back (- i 1)))) ;; Backward HMR i=0: acc[0] = (low, low-idx) (gate-cx! c carries-reg carries-offset src-reg src-off) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx low-reg low-idx) (gate-pop-cond! c) (gate-cx! c src-reg src-off cin-reg cin-idx) (gate-cx! c cin-reg cin-idx low-reg low-idx)))) (define (cuccaro-sub-joint-fast-borrowed! c src-reg src-off low-reg low-idx mid-reg mid-off cin-reg cin-idx n carries-reg carries-offset bit-base) "Joint-acc + borrowed-carries port of cuccaro-sub-fast-borrowed!. acc[0..n) := (acc - src - cin) mod 2^n." (cond ((= n 0) #t) ((= n 1) (gate-cx! c src-reg src-off low-reg low-idx) (gate-cx! c cin-reg cin-idx low-reg low-idx)) (else ;; Forward inv-UMA-like at i=0 (gate-cx! c cin-reg cin-idx low-reg low-idx) (gate-cx! c src-reg src-off cin-reg cin-idx) (gate-ccx! c cin-reg cin-idx low-reg low-idx carries-reg carries-offset) (gate-cx! c carries-reg carries-offset src-reg src-off) ;; Forward steps i=1..n-2 (let loop-fwd ((i 1)) (when (< i (- n 1)) (let ((acc-i (joint-acc-ref low-reg low-idx mid-reg mid-off i))) (gate-cx! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i))) (gate-cx! c src-reg (+ src-off i) src-reg (+ src-off (- i 1))) (gate-ccx! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i)) carries-reg (+ carries-offset i)) (gate-cx! c carries-reg (+ carries-offset i) src-reg (+ src-off i))) (loop-fwd (+ i 1)))) ;; Final sum bit — sub order swaps the two CXs (let ((acc-top (joint-acc-ref low-reg low-idx mid-reg mid-off (- n 1)))) (gate-cx! c src-reg (+ src-off (- n 1)) (car acc-top) (car (cdr acc-top))) (gate-cx! c src-reg (+ src-off (- n 2)) (car acc-top) (car (cdr acc-top)))) ;; Backward HMR uncompute, i = n-2..1 (let loop-back ((i (- n 2))) (when (>= i 1) (let ((acc-i (joint-acc-ref low-reg low-idx mid-reg mid-off i))) (gate-cx! c carries-reg (+ carries-offset i) src-reg (+ src-off i)) (gate-hmr! c carries-reg (+ carries-offset i) (+ bit-base i)) (gate-push-cond! c (+ bit-base i)) (gate-cz! c src-reg (+ src-off (- i 1)) (car acc-i) (car (cdr acc-i))) (gate-pop-cond! c) (gate-cx! c src-reg (+ src-off i) src-reg (+ src-off (- i 1))) (gate-cx! c src-reg (+ src-off i) (car acc-i) (car (cdr acc-i)))) (loop-back (- i 1)))) ;; Backward HMR i=0 (gate-cx! c carries-reg carries-offset src-reg src-off) (gate-hmr! c carries-reg carries-offset bit-base) (gate-push-cond! c bit-base) (gate-cz! c cin-reg cin-idx low-reg low-idx) (gate-pop-cond! c) (gate-cx! c src-reg src-off cin-reg cin-idx) (gate-cx! c src-reg src-off low-reg low-idx)))) ;;; Correction 1 helper: wide[n..2n+1) += y_ext where y_ext = y at bits ;;; [0..n) and pad(=0) at bit n. We need to cuccaro-add the y bits into ;;; a (n+1)-wide slice of wide at offset n. wide[n] = tmp-ext[n-1]. ;;; wide[n+1..2n+1) = tmp-ext[n..2n). ;;; ;;; This is the SAME joint accumulator pattern: target bit 0 = (tmp-ext, ;;; n-1), target bits 1..n = (tmp-ext, n..2n-1). Adjacent indices — ;;; really a SINGLE register slice tmp-ext[n-1..2n). ;;; ;;; Source y_ext: we need n+1 bits where y_ext[0..n) = y and y_ext[n] = 0. ;;; We use const-tmp-reg (n+1 wide |0>) as y_ext: copy y into bits [0..n), ;;; run cuccaro-add-offset of const-tmp into tmp-ext at offset n-1 width ;;; n+1, then uncopy y. (define (cuccaro-add-y-ext-into-tmp-hi! c y-reg n tmp-ext-reg cin-reg cin-idx const-tmp-reg) "wide[n..2n+1) += y_ext with c_in present. wide[n..2n+1) = tmp-ext[n-1..2n)." ;; Copy y → const-tmp low n bits. const-tmp[n] stays |0> = pad. (let loop ((k 0)) (when (< k n) (gate-cx! c y-reg k const-tmp-reg k) (loop (+ k 1)))) ;; cuccaro-add at width n+1 into tmp-ext at offset n-1. (cuccaro-add-offset! c const-tmp-reg 0 tmp-ext-reg (- n 1) cin-reg cin-idx (+ n 1)) ;; Uncopy. (let loop ((k 0)) (when (< k n) (gate-cx! c y-reg k const-tmp-reg k) (loop (+ k 1))))) (define (cuccaro-sub-y-ext-into-tmp-hi! c y-reg n tmp-ext-reg cin-reg cin-idx const-tmp-reg) (let loop ((k 0)) (when (< k n) (gate-cx! c y-reg k const-tmp-reg k) (loop (+ k 1)))) (cuccaro-sub-offset! c const-tmp-reg 0 tmp-ext-reg (- n 1) cin-reg cin-idx (+ n 1)) (let loop ((k 0)) (when (< k n) (gate-cx! c y-reg k const-tmp-reg k) (loop (+ k 1))))) (define (cuccaro-add-x-ext-into-tmp-hi! c x-reg n tmp-ext-reg cin-reg cin-idx const-tmp-reg) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k const-tmp-reg k) (loop (+ k 1)))) (cuccaro-add-offset! c const-tmp-reg 0 tmp-ext-reg (- n 1) cin-reg cin-idx (+ n 1)) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k const-tmp-reg k) (loop (+ k 1))))) (define (cuccaro-sub-x-ext-into-tmp-hi! c x-reg n tmp-ext-reg cin-reg cin-idx const-tmp-reg) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k const-tmp-reg k) (loop (+ k 1)))) (cuccaro-sub-offset! c const-tmp-reg 0 tmp-ext-reg (- n 1) cin-reg cin-idx (+ n 1)) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k const-tmp-reg k) (loop (+ k 1))))) ;;; Correction 3 helper: wide -= x, where wide has width 2n+1 ([low ++ tmp-ext]) ;;; and x is at x-reg[0..n). x's high (2n+1-n)=n+1 bits are zero (we pad). ;;; ;;; Use a JOINT cuccaro-sub over the full wide. Source: const-tmp at low ;;; n bits (loaded from x) zero-padded above. But const-tmp is only n+1 ;;; wide — we need 2n+1 wide source. We allocate xfull internally. ;;; ;;; *sub-x-from-wide-cas-fast* — sweep-027 opt-in flag. When BOTH ;;; *cuccaro-callers-fast* AND this flag are set, widen sb-xfull to ;;; 4n+1 and dispatch joint via cas-borrowed. Disabled by default ;;; (sweep-027 verdict: knife-edge ROI, +10%% worst-case score). ;;; ALLOC width 4n+1 = 1025 at production hits the n=512 ancilla cap ;;; if cap isnt raised — keep off unless cap is also raised. (define *sub-x-from-wide-cas-fast* #f) ;;; *sub-x-from-wide-host-alloc* — sweep-036 opt-in flag (allocator ;;; surgery). When set, mod-mul-solinas! / mod-mul-solinas-sub! allocate ;;; sb-xfull at Stage-0 (TOP of their scratch suite, BEFORE sb-tmp-ext / ;;; sb-low / sb-xext / sb-const-tmp) instead of letting sub-x-from-wide! ;;; / add-x-into-wide! allocate it internally on each call. The hoisted ;;; alloc lands at the lowest free 513-wide base inside the mod-mul ;;; scope; sub-x-from-wide! / add-x-into-wide! consult *host-sb-xfull-reg* ;;; & skip their internal alloc when that variable is bound. ZERO Toffoli ;;; delta — same sb-xfull contents, same gate sequence. The opt-in is a ;;; pure register-lifetime re-order: it removes the deep-nested late ;;; allocation that landed sb-xfull on top of the dgcd-host floor. ;;; ;;; Predicted peak qubits change: sweep-035 baseline 5,024 → ~4,510 ;;; (−10.2 %) per allocator-replay subagent (runs/lumbda-sweep-032 ;;; ALLOCATOR-REPORT-sweep030.md temporal-overlap candidate #2). ;;; ;;; When flag off & *host-sb-xfull-reg* #f, both helpers fall through ;;; their old alloc-internal path — byte-identical to sweep-035. (define *sub-x-from-wide-host-alloc* #f) ;;; Dynamic variable bound by mod-mul-solinas! / mod-mul-solinas-sub! ;;; when *sub-x-from-wide-host-alloc* is on: holds the host-allocated ;;; sb-xfull register name, communicated to sub-x-from-wide! / ;;; add-x-into-wide! without changing their signatures. Cleared to #f ;;; after the Stage-3 inverse pass so subsequent mod-mul calls outside ;;; the host scope alloc internally again. (define *host-sb-xfull-reg* #f) ;;; sweep-027: when *cuccaro-callers-fast* is set, widen sb-xfull from ;;; 2n+1 → 4n+1 so the top 2n bits can serve as borrowed carries for the ;;; JOINT fast-borrowed dispatch path: ;;; ;;; sb-xfull[0..n) = x data (Stage-1 load) ;;; sb-xfull[n..2n+1) = source pad (touched by cuccaro as |0> bits) ;;; sb-xfull[2n+1..4n+1) = HMR carries pad (clean |0> on entry/exit) ;;; ;;; Width-N=2n+1 joint fast-borrowed consumes N-1 = 2n carries lanes, ;;; which exactly fits sb-xfull[2n+1..4n+1). Bit-base 300000 disjoint ;;; from mod-solinas' 200000 reservation (which covers up to n+1 bits). ;;; Saves (N-1)-N/2 ≈ N-1 = 2n Toffoli per call via HMR uncompute; ;;; cost is +2n qubits in sb-xfull's transient allocation. (define (sub-x-from-wide! c x-reg n low-reg low-idx tmp-ext-reg cin-reg cin-idx) "wide := wide - x, where wide = [low ++ tmp-ext] (2n+1 bits) and x is at x-reg[0..n). High bits of x_padded are 0. Uses internal xfull ancilla (alloc'd here; widened to 4n+1 under *cuccaro-callers-fast* to host the JOINT fast-borrowed carries). When *host-sb-xfull-reg* is bound (sweep-036 opt-in), the caller (mod-mul-solinas!) has pre-allocated sb-xfull at outer scope; skip internal alloc/free & use the hosted register name." (let* ((use-fast? (and *cuccaro-callers-fast* *sub-x-from-wide-cas-fast*)) (joint-width (+ (* 2 n) 1)) ; N = 2n+1 (carries-need (- joint-width 1)) ; N-1 = 2n (xfull-width (cond (use-fast? (+ joint-width carries-need)) (else joint-width))) (host-reg *host-sb-xfull-reg*) (xfull-name (cond (host-reg host-reg) (else (quote sb-xfull))))) ;; Allocate xfull (xfull-width bits |0>) — unless hosted by caller. (when (not host-reg) (alloc! c xfull-name xfull-width)) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xfull-name k) (loop (+ k 1)))) ;; Configure cas-borrowed for joint dispatch when in fast mode. (cond (use-fast? (let ((saved-reg *cas-borrowed-carries-reg*) (saved-off *cas-borrowed-carries-offset*) (saved-base *cas-borrowed-bit-base*) (saved-width *cas-borrowed-carries-width*)) (set! *cas-borrowed-carries-reg* xfull-name) (set! *cas-borrowed-carries-offset* joint-width) (set! *cas-borrowed-bit-base* 300000) (set! *cas-borrowed-carries-width* carries-need) (cuccaro-sub-joint! c xfull-name 0 low-reg low-idx tmp-ext-reg 0 cin-reg cin-idx joint-width) (set! *cas-borrowed-carries-reg* saved-reg) (set! *cas-borrowed-carries-offset* saved-off) (set! *cas-borrowed-bit-base* saved-base) (set! *cas-borrowed-carries-width* saved-width))) (else (cuccaro-sub-joint! c xfull-name 0 low-reg low-idx tmp-ext-reg 0 cin-reg cin-idx joint-width))) ;; Uncopy x. (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xfull-name k) (loop (+ k 1)))) (when (not host-reg) (free! c xfull-name)))) (define (add-x-into-wide! c x-reg n low-reg low-idx tmp-ext-reg cin-reg cin-idx) "Inverse of sub-x-from-wide!: wide += x over full 2n+1 bits. sb-xfull widened to 4n+1 under *cuccaro-callers-fast* to host the JOINT fast-borrowed carries pad above the source range. When *host-sb-xfull-reg* is bound (sweep-036 opt-in), use the hosted register instead of allocating/freeing internally." (let* ((use-fast? (and *cuccaro-callers-fast* *sub-x-from-wide-cas-fast*)) (joint-width (+ (* 2 n) 1)) (carries-need (- joint-width 1)) (xfull-width (cond (use-fast? (+ joint-width carries-need)) (else joint-width))) (host-reg *host-sb-xfull-reg*) (xfull-name (cond (host-reg host-reg) (else (quote sb-xfull))))) (when (not host-reg) (alloc! c xfull-name xfull-width)) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xfull-name k) (loop (+ k 1)))) (cond (use-fast? (let ((saved-reg *cas-borrowed-carries-reg*) (saved-off *cas-borrowed-carries-offset*) (saved-base *cas-borrowed-bit-base*) (saved-width *cas-borrowed-carries-width*)) (set! *cas-borrowed-carries-reg* xfull-name) (set! *cas-borrowed-carries-offset* joint-width) (set! *cas-borrowed-bit-base* 300000) (set! *cas-borrowed-carries-width* carries-need) (cuccaro-add-joint! c xfull-name 0 low-reg low-idx tmp-ext-reg 0 cin-reg cin-idx joint-width) (set! *cas-borrowed-carries-reg* saved-reg) (set! *cas-borrowed-carries-offset* saved-off) (set! *cas-borrowed-bit-base* saved-base) (set! *cas-borrowed-carries-width* saved-width))) (else (cuccaro-add-joint! c xfull-name 0 low-reg low-idx tmp-ext-reg 0 cin-reg cin-idx joint-width))) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xfull-name k) (loop (+ k 1)))) (when (not host-reg) (free! c xfull-name)))) ;;; Inverse of schoolbook-mul-into-addsub!: undo each correction then ;;; the loop, gate-by-gate inverted. Self-inverse steps (X) re-emit. (define (schoolbook-mul-into-addsub-inverse! c x-reg y-reg n low-reg low-idx tmp-ext-reg xext-reg cin-reg cin-idx const-tmp-reg) ;; Reverse correction 4: cuccaro-sub of x_ext at high half. (cuccaro-sub-x-ext-into-tmp-hi! c x-reg n tmp-ext-reg cin-reg cin-idx const-tmp-reg) ;; Reverse correction 3: add x back into wide. (add-x-into-wide! c x-reg n low-reg low-idx tmp-ext-reg cin-reg cin-idx) ;; Reverse correction 2: re-XOR top bit (self-inverse). (gate-x! c tmp-ext-reg (- (* 2 n) 1)) ;; Reverse correction 1: cuccaro-sub of y_ext with cin=1. (gate-x! c cin-reg cin-idx) (cuccaro-sub-y-ext-into-tmp-hi! c y-reg n tmp-ext-reg cin-reg cin-idx const-tmp-reg) (gate-x! c cin-reg cin-idx) ;; Reverse the main loop (k from n-1 down to 0). (let loop ((k (- n 1))) (when (>= k 0) (cond ((= k 0) (schoolbook-row-k0-inverse! c x-reg n y-reg k low-reg low-idx tmp-ext-reg xext-reg cin-reg cin-idx)) (else (controlled-add-subtract-slice-inverse! c x-reg n tmp-ext-reg (- k 1) y-reg k xext-reg cin-reg cin-idx))) (loop (- k 1))))) (define (schoolbook-row-k0-inverse! c x-reg n y-reg y-idx low-reg low-idx tmp-ext-reg xext-reg cin-reg cin-idx) "Inverse of schoolbook-row-k0!." (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1)))) (gate-x! c y-reg y-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c y-reg y-idx xext-reg k) (loop (+ k 1)))) (gate-cx! c y-reg y-idx cin-reg cin-idx) (cuccaro-sub-joint! c xext-reg 0 low-reg low-idx tmp-ext-reg 0 cin-reg cin-idx (+ n 1)) (gate-cx! c y-reg y-idx cin-reg cin-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c y-reg y-idx xext-reg k) (loop (+ k 1)))) (gate-x! c y-reg y-idx) (let loop ((k 0)) (when (< k n) (gate-cx! c x-reg k xext-reg k) (loop (+ k 1))))) ;;; ── mod-mul! — Litinski wide schoolbook + Solinas-style reduction ─ ;;; ;;; Calling convention: ;;; a-reg, b-reg, out-reg : (n+1)-wide; top bit must be |0> on entry ;;; (we operate on low n bits only). ;;; a-reg, b-reg preserved; out-reg ends with ;;; (a*b) mod p. ;;; cin-reg, cin-idx : 1 bit |0> in/out (mod-add c_in) ;;; tmp-reg : (n+1)-wide |0> in/out — mod-add's inner ;;; constant-loading scratch. ;;; flag-reg, flag-idx : 1 bit |0> in/out (mod-add flag) ;;; red-tmp-reg : (n+1)-wide |0> in/out — used in Stage 2 to ;;; load 2^k mod p constants into a register ;;; before mod-add into out. ;;; ;;; Inside, mod-mul! allocates its own Litinski scratch: ;;; sb-tmp-ext (2n bits) the wide product accumulator ;;; sb-low (1 bit) wide[0] ;;; sb-xext (n+1 bits) controlled-add-subtract scratch ;;; sb-const-tmp (n+1 bits) correction-stage constant-loading scratch ;;; sb-xfull (2n+1 bits, alloc/free'd inside sub-x-from-wide!) ;;; ── Solinas dispatch flag ───────────────────────────────────── ;;; ;;; When *mod-mul-use-solinas* is non-#f, mod-mul! and mod-mul-sub! ;;; dispatch into mod-mul-solinas! / mod-mul-solinas-sub! (defined in ;;; lumbda/mod-solinas.lsp — caller must load that file before flipping ;;; the flag). Caller-visible signatures and ancilla register names ;;; remain identical, so every consumer (mod-inv!, mod-square!, ;;; real-point-add!) inherits the speedup without code change. (define *mod-mul-use-solinas* #f) (define (mod-mul! c a-reg b-reg out-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx red-tmp-reg) "out := (a * b) mod p. Litinski schoolbook + per-bit Solinas-style reduce. See header for full calling convention. When *mod-mul-use-solinas* is non-#f, dispatch to mod-mul-solinas! (mod-solinas.lsp must already be loaded)." (cond (*mod-mul-use-solinas* (mod-mul-solinas! c a-reg b-reg out-reg n+1 p (compute-c-expansion p (- n+1 1)) cin-reg cin-idx tmp-reg flag-reg flag-idx red-tmp-reg)) (else (mod-mul-litinski! c a-reg b-reg out-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx red-tmp-reg)))) ;;; ── mod-mul-from-zero! — caller-explicit out=|0> specialization ─── ;;; ;;; Port of HEAD's `mod_add_qq_fast_from_zero` lifted to the multiply ;;; entry boundary. Caller invokes this INSTEAD of mod-mul! when out-reg ;;; is provably |0> on entry (e.g. mod-square!'s freshly-alloc'd out, ;;; mod-inv-by Fermat ladder's r-next / b-next, point-add lam-reg before ;;; any accumulation). ;;; ;;; Saves n CCX per fresh-multiply when both ;;; *mod-mul-use-solinas* AND *mod-mul-from-zero-first-add* ;;; are #t. When *mod-mul-from-zero-first-add* off, byte-identical to ;;; mod-mul-solinas! (safe fallback). When *mod-mul-use-solinas* off, ;;; falls through to mod-mul-litinski! (no specialization yet for the ;;; Litinski path — Stage 2 dispatch shape differs). (define (mod-mul-from-zero! c a-reg b-reg out-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx red-tmp-reg) "out := (a * b) mod p when out is |0> on entry. See mod-mul! header for calling convention." (cond (*mod-mul-use-solinas* (mod-mul-solinas-from-zero! c a-reg b-reg out-reg n+1 p (compute-c-expansion p (- n+1 1)) cin-reg cin-idx tmp-reg flag-reg flag-idx red-tmp-reg)) (else (mod-mul-litinski! c a-reg b-reg out-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx red-tmp-reg)))) (define (mod-mul-litinski! c a-reg b-reg out-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx red-tmp-reg) "Original Litinski wide-schoolbook + per-bit Solinas reduce. Renamed from mod-mul! so we can preserve the original code path for regression testing while mod-mul! itself becomes the dispatcher." (let ((n (- n+1 1))) (cond ((= n 0) #t) ; degenerate, nothing to do (else ;; ── Stage 0: alloc Litinski scratch ── (alloc! c (quote sb-tmp-ext) (* 2 n)) (alloc! c (quote sb-low) 1) (alloc! c (quote sb-xext) (+ n 1)) (alloc! c (quote sb-const-tmp) (+ n 1)) ;; ── Stage 1: compute wide product sb-tmp-ext := a * b ── (schoolbook-mul-into-addsub! c a-reg b-reg n (quote sb-low) 0 (quote sb-tmp-ext) (quote sb-xext) cin-reg cin-idx (quote sb-const-tmp)) ;; ── Stage 2: reduce sb-tmp-ext mod p into out ── ;; For each bit k in [0, 2n): if sb-tmp-ext[k]=1 then ;; out += (2^k mod p) mod p. ;; Reversible via cload-const + mod-add + cunload-const, where the ;; constant lives in red-tmp-reg (n+1 wide, top bit |0>). (let loop ((k 0)) (when (< k (* 2 n)) (let ((c-k (modulo (expt 2 k) p))) (when (> c-k 0) ; skip no-op constants ;; Load c-k into red-tmp controlled on sb-tmp-ext[k]. (cload-const! c (quote sb-tmp-ext) k red-tmp-reg n+1 c-k) ;; Add red-tmp into out mod p. (mod-add! c red-tmp-reg out-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) ;; Unload c-k. (cunload-const! c (quote sb-tmp-ext) k red-tmp-reg n+1 c-k))) (loop (+ k 1)))) ;; ── Stage 3: uncompute sb-tmp-ext back to |0> ── (schoolbook-mul-into-addsub-inverse! c a-reg b-reg n (quote sb-low) 0 (quote sb-tmp-ext) (quote sb-xext) cin-reg cin-idx (quote sb-const-tmp)) ;; ── Stage 4: free Litinski scratch ── (free! c (quote sb-const-tmp)) (free! c (quote sb-xext)) (free! c (quote sb-low)) (free! c (quote sb-tmp-ext)))))) ;;; ── mod-double-inplace! — v := 2v mod p, (n+1)-wide register ── ;;; ;;; Mirrors upstream's mod_double_inplace at mod.rs:2769. ;;; ;;; Pattern (cost: O(n) gates): ;;; 1. Shift-left v by 1 via SWAP cascade. Caller's (n+1)-wide v-reg ;;; starts as [v_0 v_1 ... v_{n-1} 0]; after the cascade we have ;;; [0 v_0 v_1 ... v_{n-1}]. Now v-reg = T = 2 * v_orig in [0, 2p). ;;; 2. Add c = 2^n - p across the full (n+1) bits. Sum S = T + c. ;;; Top bit (idx n) of S is set iff T >= p. ;;; 3. flag := S[n] via CX. ;;; 4. csub c controlled on (NOT flag) — undoes the add when no reduction. ;;; 5. CX flag -> S[n] — clears the top bit when reduction needed. ;;; 6. Uncompute flag: T is even (= 2v), p is odd → after reduction v[0]=1; ;;; no reduction → v[0]=0. So flag == v[0]. CX v[0] -> flag clears. ;;; ;;; All ancillae (cin, tmp, flag) return to |0>. v-reg top bit returns to |0>. ;;; Caller-supplied ancillae: cin (1 bit), tmp (n+1 wide), flag (1 bit). ;;; ;;; Variant choice: in-place via swap cascade + Solinas-style fold. ;;; Chosen because it mirrors upstream byte-for-byte; alternative ;;; Bennett-style "double via mod-add of copy" requires a 2^{-1} mod p ;;; classical inversion AND a fresh ancilla — heavier than this one ;;; ovf-bit pattern in lumbda. See task spec §"variant choice". ;;; ;;; SWAP not in our gate set — implemented as 3 CX (a→b, b→a, a→b). (define (gate-swap! c a-reg a-idx b-reg b-idx) "SWAP qubit a with b via three CXs. Self-inverse." (gate-cx! c a-reg a-idx b-reg b-idx) (gate-cx! c b-reg b-idx a-reg a-idx) (gate-cx! c a-reg a-idx b-reg b-idx)) ;;; ── pseudo-Mersenne mod-double (Schrottenloher 2026 Algorithm 7) ─ ;;; ;;; For pseudo-Mersenne primes p = 2^u - f with f << 2^u (secp256k1: u=256, ;;; f=2^32+977=4294968273, 33 bits) the entire add-const / csub-const / ;;; flag-uncompute dance collapses to a single controlled add of f over the ;;; LOW lsbs bits of v, controlled on the carry-out from the shift. ;;; ;;; Algorithm 7 (qarton special_mod_arithmetic.py:54-92): ;;; 1. shift v left by 1 (anc receives MSB carry-out) ;;; 2. cadd(anc, f, v[:lsbs]) — controlled add of f into low lsbs bits ;;; 3. cx(v[0], anc) — uncompute anc via parity ;;; ;;; Correctness lsbs = padding + bit_length(f). The cadd carry walks at most ;;; lsbs bits; if it would propagate further, the result is wrong. For ;;; uniform random v the failure probability is roughly 2^(-padding). Qarton ;;; uses padding=30 by default — same as Schrottenloher §4. ;;; ;;; Toffoli savings vs control: control mod-double-inplace! emits one ;;; add-const + one csub-const, both at full width (n+1). Each cuccaro-add ;;; over k bits costs 2(k-1) Toffoli (HEAD's HMR-borrowed variant: k-1). ;;; Pseudo-Mersenne emits ONE cadd-const at width lsbs only. Predicted ;;; Toffoli savings at secp256k1 width: ≈ 2 × (n+1 - lsbs) / (2(n+1)) ≈ ;;; (256 - 63) / 257 ≈ 75 % of mod-double Toffoli; mod-double itself sits ;;; at ~8 % of total (Schrottenloher Table 3) → predicted 5-10 % full-stack. (define *mod-double-use-pseudo-mersenne* #f) ;;; *mod-double-pseudo-mersenne-padding* — extra carry-safety bits beyond ;;; bit_length(f) for the controlled add. Larger padding → smaller flake ;;; probability ≈ 2^(-padding) but more Toffoli per call. Qarton + paper ;;; both use 30 by default. (define *mod-double-pseudo-mersenne-padding* 30) ;;; *windowed-mod-double-r* — sweep-windowed-mod-double-r dispatcher. ;;; When #t, mod-double-inplace! routes through mod-double-inplace- ;;; windowed! (HEAD modular.rs:417-419 windowed branch). Foundation for ;;; K=5 apply-phase split — HEAD's compressed.rs:1986-1999 + 2515 ;;; lean on the windowed form for the q1192 island. Default #f preserves ;;; byte-identity for every existing caller. Truncation flake ;;; probability ≈ 2^-(window+1) per call (window pulled from ;;; *windowed-mod-double-r-window*, default 8 matching HEAD). ;;; ;;; Composes with neither *mod-double-use-pseudo-mersenne* nor the ;;; alg-11 safe-band detector — the windowed path is its own dispatch ;;; branch (highest-priority) in mod-double-inplace!. (define *windowed-mod-double-r* #f) (define *windowed-mod-double-r-window* 8) (define (pmersenne-bit-length n) "Number of bits to represent positive integer n (== Python's int.bit_length). bit-length(0)=0, bit-length(1)=1, bit-length(2)=2, bit-length(3)=2, ..." (let loop ((k 0) (m n)) (if (= m 0) k (loop (+ k 1) (quotient m 2))))) (define (mod-double-inplace-pseudo-mersenne! c v-reg n+1 p pmersenne-f cin-reg cin-idx tmp-reg flag-reg flag-idx) "Pseudo-Mersenne variant of mod-double-inplace!. Same calling convention (v-reg (n+1) wide, cin/tmp/flag scratch); ignores flag-reg/flag-idx (kept for signature compatibility with the dispatcher). pmersenne-f = 2^n - p (must be > 0 and small)." (let* ((n (- n+1 1)) (f-bits (pmersenne-bit-length pmersenne-f)) (padding *mod-double-pseudo-mersenne-padding*) (lsbs (min n+1 (+ padding f-bits)))) ;; (1) Shift-left v in place via SWAP cascade. ;; End state: bit 0 = 0, bit i (i>=1) = v_orig[i-1], ;; bit n = v_orig[n-1] = pseudo-Mersenne carry-out. (let loop ((i n)) (when (> i 0) (gate-swap! c v-reg i v-reg (- i 1)) (loop (- i 1)))) ;; (2) cadd(anc=v[n], f, v[0..lsbs)) — controlled add of f into low bits. ;; No aliasing concern: ctrl-idx = n, tgt-idx range = [0, lsbs) with ;; lsbs < n+1, so ctrl bit never overlaps with cadd's target slice. (cadd-const! c v-reg lsbs pmersenne-f v-reg n cin-reg cin-idx tmp-reg) ;; (3) Uncompute anc via parity: 2v is even, p is odd → after reduction ;; v[0] = 1, no reduction → v[0] = 0. CX v[0] -> v[n] clears anc. (gate-cx! c v-reg 0 v-reg n))) ;;; ── mod-halve-inplace-direct-const-fast! ────────────────────────── ;;; ;;; Port of HEAD mod_halve_inplace_direct_const_fast ;;; (src/point_add/arith/modular.rs:753-765, commit 2dcf00d). Gate-level ;;; inverse of mod_double_inplace_direct_const_fast (sweep-030 PORTED ;;; in lumbda as mod-double-inplace-pseudo-mersenne!). Closes the open ;;; question sweep-030 RESULTS.md flagged + the mod-add-double-qb ;;; PORTED-WITH-GAP from sweep-mod-qb-adders. ;;; ;;; HEAD algorithm (5 steps): ;;; 1. alloc ovf qubit ;;; 2. cx(v[0], ovf) -- XOR LSB into ovf ;;; 3. csub_nbit_const_direct_fast(v, c, ovf) where c = 2^n - p ;;; 4. swap chain: for i in 0..n-1: swap(v[i], v[i+1]) (right rotate) ;;; 5. swap(v[n-1], ovf); free ovf -- ovf bit lands at v[n-1] ;;; ;;; This is the EXACT gate-level inverse of the direct-const double ;;; (HEAD line 408+): the double's pre-shift swap-chain becomes the ;;; halve's post-shift swap-chain; the double's cadd becomes the ;;; halve's csub; the double's parity-uncompute becomes the halve's ;;; parity-cx-INTO-ovf at the start. ;;; ;;; Substrate status: ADDITIVE. No lumbda caller dispatches through ;;; this primitive yet. mod-add-double-qb (sweep-mod-qb-adders) can ;;; route through this once a follow-on sweep adds the dispatcher ;;; flag (analog of HEAD's KAL_DIRECT_CONST_HALVE / direct_const_ ;;; walks_enabled). ;;; ;;; Caller supplies the 'mod-halve-anc qubit + carries lane via cin/ ;;; tmp/flag (HEAD allocs inline; lumbda style routes through args). ;;; ── mod-double-inplace-direct-const-fast! — sweep-sq-lowq-shift22 ── ;;; ;;; HEAD modular.rs:414. Mirror of mod-halve-inplace-direct-const-fast! ;;; with cadd direction. Uses cadd-nbit-const-direct-fast! (PORTED in ;;; sweep-cadd-csub-direct-fast) for the Solinas correction. (define (mod-double-inplace-direct-const-fast! c v-reg n+1 p pmersenne-f cin-reg cin-idx tmp-reg flag-reg flag-idx) "Port of HEAD mod_double_inplace_direct_const_fast (modular.rs:414). v-reg := 2*v mod p (in place). v-reg is (n+1)-wide; v in low n bits, v[n] = ovf at |0> on entry + restored on exit. pmersenne-f = 2^n - p." (let* ((n (- n+1 1)) (c-const pmersenne-f)) ;; (1) swap(v[n-1], v[n]). (gate-swap! c v-reg (- n 1) v-reg n) ;; (2) Right-rotate the n low bits via reverse swap chain. (let loop ((i (- n 2))) (when (>= i 0) (gate-swap! c v-reg i v-reg (+ i 1)) (loop (- i 1)))) ;; (3) cadd-nbit-const-direct-fast! ctrl=v[n] (ovf) into v[0..n]. (cadd-nbit-const-direct-fast! c v-reg n c-const v-reg n tmp-reg (* 4 n+1)) ;; (4) Parity CX: v[0] XOR into v[n] (clears ovf). (gate-cx! c v-reg 0 v-reg n))) ;;; ── mod-double-inplace-windowed! — sweep-windowed-mod-double-r ── ;;; ;;; Port of HEAD `mod_double_inplace_fast_with_dirty` ;;; (modular.rs:398-444), windowed branch (lines 417-419 — fires when ;;; `double_carry_trunc_window()` returns `Some(w)` i.e. env var ;;; `KAL_DOUBLE_CARRY_TRUNC_W=w`). HEAD's K=5 apply-phase ipmul + ;;; quotient bodies (compressed.rs:1986-1999 + 2515) lean on this ;;; windowed form for the q1192 island; the apply-phase callsite ;;; allocates an `ovf` qubit, swaps the top bit out, right-rotates the ;;; low n bits, then runs ONE truncated cadd against ctrl=ovf instead ;;; of the standard add-const + csub-const + flag-uncompute pair. ;;; ;;; Structural shape vs `mod-double-inplace-direct-const-fast!`: ;;; ;;; • direct-const-fast emits a FULL-width `cadd-nbit-const-direct-fast!` ;;; (no carry-tail truncation; exact for any v). ;;; • windowed emits `cadd-nbit-const-direct-trunc-fast!` with carry ;;; ripple stopped `window` bits past `highest_set_bit(c)`. ;;; For secp256k1, c = 2^32 + 977 has highest-bit 32; window=8 stops ;;; the ripple at bit 40 — saves ~(n - 40) carry maj-recurrence ;;; CCXs + the matching backward HMR sweep per call. Per-call ;;; Toffoli drops from ~2(n-1) to ~2*(highest_set_bit(c) + w). ;;; ;;; Flake probability: ~2^-(window+1) per call. At window=8 that's ;;; ~2^-9 ≈ 0.2 %. Production cells stack the apply-phase fold's same ;;; window so forward + reverse use matching truncation; mismatch only ;;; manifests when the carry-tail propagates through `window + 1` ;;; consecutive 1-bits in the running accumulator above bit 32 — the ;;; "exact-unless-rare-input" regime documented in HEAD's ;;; cadd_nbit_const_direct_trunc_fast docstring (const_arith.rs:526). ;;; ;;; Calling convention matches `mod-double-inplace!`: ;;; v-reg : (n+1)-wide; v in low n bits, bit n at |0> in/out. ;;; p : prime; classical. ;;; pmersenne-f : 2^n - p (HEAD's `c`). Caller computes once. ;;; tmp-reg : (>= last+1)-wide ancilla at |0> for the carries lane. ;;; `last = min(n-2, highest-set-bit(f) + window)`. ;;; ;;; Width assertion: caller must pass tmp-reg with capacity >= last+1. ;;; All existing dispatchers route mod-double-inplace! with tmp-reg ;;; sized n+1 — which is always >= last+1 since last <= n-2. (define (mod-double-inplace-windowed! c v-reg n+1 p pmersenne-f cin-reg cin-idx tmp-reg flag-reg flag-idx window) "Port of HEAD mod_double_inplace_fast_with_dirty windowed branch (modular.rs:417-419). v-reg := 2*v mod p (in place) via shift-cascade + ONE truncated cadd. Saves ~(n - hi - window) carry-sweep CCXs per call vs the non-windowed direct-const path. v-reg : (n+1)-wide; v in low n bits, bit n at |0> in/out. pmersenne-f : 2^n - p (HEAD's `c`). window : carry-tail safety bits past highest_set_bit(pmersenne-f). HEAD's default 8 — flake prob ~2^-(window+1) per call. cin-reg/cin-idx + flag-reg/flag-idx kept for signature parity; the windowed path does NOT use them (no separate flag ancilla — ovf lives at v[n] post-shift)." (let* ((n (- n+1 1)) (c-const pmersenne-f)) ;; (1) swap(v[n-1], v[n]). (gate-swap! c v-reg (- n 1) v-reg n) ;; (2) Right-rotate the n low bits via reverse swap chain. (let loop ((i (- n 2))) (when (>= i 0) (gate-swap! c v-reg i v-reg (+ i 1)) (loop (- i 1)))) ;; (3) cadd-nbit-const-direct-trunc-fast! ctrl=v[n] (ovf) into v[0..n). ;; Unique bit-base via cdtf-alloc-bit-base! so back-to-back calls ;; in K=2 shift2=1 path don't collide on HMR slots. (cadd-nbit-const-direct-trunc-fast! c v-reg n c-const v-reg n tmp-reg window (cdtf-alloc-bit-base! n)) ;; (4) Parity CX: v[0] XOR into v[n] (clears ovf). (gate-cx! c v-reg 0 v-reg n))) (define (mod-halve-inplace-direct-const-fast! c v-reg n+1 p pmersenne-f cin-reg cin-idx tmp-reg flag-reg flag-idx) ;; v-reg := v / 2 mod p (in place). v-reg is (n+1)-wide; v in low n ;; bits, bit n at |0> on entry + restored to |0> on exit (the ovf ;; ancilla). pmersenne-f = 2^n - p (matches mod-double-inplace- ;; pseudo-mersenne! caller convention). flag-reg/flag-idx kept for ;; signature symmetry with the dispatcher. (let* ((n (- n+1 1)) (c-const pmersenne-f)) ; c = 2^n - p; HEAD line 757 same form ;; (1) Parity CX: v[0] XOR into v[n] (our ovf is at v[n]). (gate-cx! c v-reg 0 v-reg n) ;; (2) Controlled sub of c, ctrl = v[n] (= ovf). ;; csub-nbit-const-direct-trunc-fast! with window = n is ;; effectively non-truncated (no carry-tail truncation). (csub-nbit-const-direct-trunc-fast! c v-reg n c-const v-reg n tmp-reg n (* 4 n+1)) ;; (3) Right-rotate the n low bits via swap chain. Matches HEAD's ;; for i in 0..n-1: swap(v[i], v[i+1]). (let loop ((i 0)) (when (< i (- n 1)) (gate-swap! c v-reg i v-reg (+ i 1)) (loop (+ i 1)))) ;; (4) Final swap: v[n-1] <-> ovf (v[n]). ovf-content lands at ;; v[n-1]; v[n] returns to |0> (the bit that came from v[n-1] ;; after step 3's chain). (gate-swap! c v-reg (- n 1) v-reg n))) (define (mod-double-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) "v-reg := 2 * v-reg mod p (in place). v-reg is (n+1)-wide; v stored in low n bits, bit n starts |0> and ends |0>. cin/tmp/flag are the same scratch suite used by mod-add!: cin (1), tmp (n+1), flag (1), all |0> in/out. Cost: 1 cuccaro-add (the inner add-const) + 1 cuccaro-sub (csub-const) + 5n + O(1) Clifford gates. O(n) total. When *mod-double-use-pseudo-mersenne* is #t AND p is a pseudo-Mersenne prime (f = 2^n - p small), dispatches to mod-double-inplace-pseudo-mersenne!." (let* ((n (- n+1 1)) (c-const (- (expt 2 n) p)) (f-bits (pmersenne-bit-length c-const)) (padding *mod-double-pseudo-mersenne-padding*)) (cond ;; sweep-windowed-mod-double-r: HEAD modular.rs:417-419 windowed ;; branch. Routes through mod-double-inplace-windowed! (one ;; truncated cadd; no separate flag-ancilla / csub pair). Highest ;; priority — composes with neither the pseudo-Mersenne path nor ;; alg-11 safe-band. Default OFF for byte-identity. ((and *windowed-mod-double-r* (> c-const 0)) (mod-double-inplace-windowed! c v-reg n+1 p c-const cin-reg cin-idx tmp-reg flag-reg flag-idx *windowed-mod-double-r-window*)) ;; Dispatch to pseudo-Mersenne when flag on AND f is small enough ;; that lsbs = padding + f-bits stays strictly below n+1 (otherwise ;; the no-aliasing assumption breaks). ;; ;; 2026-06-12 — DISABLED pseudo-Mersenne. Same bug class as ;; mod-add-inplace-pseudo-mersenne! (commit 5e6e3af). Reducer ;; tests/sweep-doctrine/test-mod-double-top-bit-clean.lsp at ;; n+1=8, p=125 (non-Mersenne, bug range non-empty): v=63 → ;; got 126, expected 1. ARITH-FAIL with top bit clean (worse ;; than mod-add — silent wrong-output, no leak diagnostic). ;; ;; Root cause: step 1's shift-left captures v[n] = v_orig[n-1] ;; as the overflow indicator. This only fires when 2v ≥ 2^n, ;; not when 2v ≥ p. For v_orig ∈ [p/2, 2^(n-1)) where 2v ∈ [p, ;; 2^n), the algorithm doesn't reduce and step 3's parity-CX ;; doesn't fix it (v[0]=0 since 2v is even, no XOR). ;; ;; Standard mod-double-inplace! (else branch) uses full csub- ;; const-based reduction, provably correct, drop-in replacement. ;; Trade-off: more Toffolis but algorithmically sound. ;; ;; 2026-06-12 (alg-11 wiring) — when *mod-add-alg-11-fallback* on, ;; mod-double-alg-11-safe? classically peeks v-reg & routes ;; pseudo-Mersenne only when 2v < p OR 2v >= 2^n (the safe regimes ;; outside the [p, 2^n) bug band). ((and *mod-double-use-pseudo-mersenne* (> c-const 0) (< (+ padding f-bits) n+1) (mod-double-alg-11-safe? c v-reg n+1 p)) (mod-double-inplace-pseudo-mersenne! c v-reg n+1 p c-const cin-reg cin-idx tmp-reg flag-reg flag-idx)) (else ;; (1) Shift-left v in place via SWAP cascade. ;; End state: bit 0 = 0, bit i (i>=1) = v_orig[i-1], bit n = v_orig[n-1]. ;; Walk from top down: swap(v[n], v[n-1]), swap(v[n-1], v[n-2]), ... (let loop ((i n)) (when (> i 0) (gate-swap! c v-reg i v-reg (- i 1)) (loop (- i 1)))) ;; (2) Add c = 2^n - p at width n+1. (add-const! c v-reg n+1 c-const cin-reg cin-idx tmp-reg) ;; (3) flag := v[n] (gate-cx! c v-reg n flag-reg flag-idx) ;; (4) X flag so flag=1 when NO reduction needed; csub-const controlled. (gate-x! c flag-reg flag-idx) (csub-const! c v-reg n+1 c-const flag-reg flag-idx cin-reg cin-idx tmp-reg) (gate-x! c flag-reg flag-idx) ;; (5) CX flag -> v[n] (clears top bit when reduction happened) (gate-cx! c flag-reg flag-idx v-reg n) ;; (6) Uncompute flag via parity: flag == v[0] (gate-cx! c v-reg 0 flag-reg flag-idx))))) ;;; ── mod-4x-inplace! — v := 4v mod p, sweep-apply-fused-fold sibling ─ ;;; ;;; HEAD reference: `compressed.rs:2149-2281` `dialog_gcd_fused_double_y`. ;;; Lumbda hooks: STEP 7+8 dispatch under K=2 + apply-fused-fold flags ;;; at `mod-inv-by-dialog-gcd-host.lsp` ~line 345. ;;; ;;; Semantics: v := 4v mod p on a SINGLE shared carry chain (target). ;;; v-reg is (n+1)-wide; v stored in low n bits, bit n |0> in/out. ;;; cin/tmp/flag scratch reused across both folds (no extra ancilla ;;; vs `mod-double-inplace!` × 2). Classical-specialization at the ;;; caller — this primitive ships only when the classical Kaliski ;;; trace says shift2-bit = 1 for this iter; otherwise the caller ;;; emits one `mod-double-inplace!`. ;;; ;;; V1 (prior): sequential two-fold via two `mod-double-inplace-pseudo- ;;; mersenne!` calls — substrate vehicle but byte-identical to inline ;;; K=2 pattern. Shipped 2.0228e+10 at i258 (non-K2body). ;;; ;;; V2 (this commit): inlined dual-shift + explicit ovf1/ovf2 ancilla ;;; capture + two cadd-const calls sharing the cin/tmp carry buffer. ;;; Matches HEAD's STRUCTURAL layout (compressed.rs:2149-2281) — ovf1 ;;; held in a side ancilla across shift2; ovf2 captured post-shift2; ;;; one cadd-const(f, ctrl=ovf1) + one cadd-const(2f, ctrl=ovf2) at ;;; width lsbs+1. Parity uncompute of ovf1/ovf2 from v[0]/v[1] post-fold. ;;; ;;; V2 buys substrate clarity, not Toffoli: the two carry sweeps remain ;;; physically distinct (lumbda lacks a per-position-controls cadd ;;; helper). Per-iter Toffoli ≈ V1 (≤ ±2 from extra ovf-uncompute CCX). ;;; Production score targets a small change vs V1 baseline; the true ;;; -8k..-16k Toff/shot saving rides on V2.5 — a new ;;; `cadd-2-controls-trunc-fast!` primitive that emits HEAD's 12-position ;;; controlled-add (compressed.rs:2193-2215) at width lsbs. V2 substrate ;;; positions ovf1/ovf2 in named ancilla so V2.5 lands as a single ;;; cadd-call replacement. ;;; ;;; Algorithm trace (n=256, secp256k1, c = 2^256 - p): ;;; 1. alloc ovf1 ancilla. ;;; 2. shift1 (swap cascade): v[n] := v_orig[n-1], v[0] := 0. ;;; 3. swap(v[n], ovf1): ovf1 := v_orig[n-1], v[n] := 0. ;;; 4. alloc ovf2 ancilla. ;;; 5. shift2 (swap cascade): v[n] := v_orig[n-2], v[0] := 0. ;;; (Note v[0] entering shift2 was 0 post step 3; so v[0]=0 post.) ;;; 6. swap(v[n], ovf2): ovf2 := v_orig[n-2], v[n] := 0. ;;; 7. cadd-const(v, lsbs, f, ctrl=ovf1, cin/tmp): v += f · ovf1. ;;; 8. cadd-const(v, lsbs, 2f, ctrl=ovf2, cin/tmp): v += 2f · ovf2. ;;; Same width as #7; padding leaves plenty of carry headroom ;;; inside lsbs even though 2f has 1 more bit than f. ;;; 9. Parity uncompute ovf1: post-fold v[0] = ovf1 (f bit 0 = 1, 2f ;;; bit 0 = 0 → v[0] = ovf1·1 + ovf2·0 = ovf1). CX v[0] → ovf1. ;;; 10. Parity uncompute ovf2: post-fold v[1] = ovf1·f[1] + ovf2·(2f)[1] ;;; mod 2 = ovf1·f[1] + ovf2·f[0] = ovf1·f[1] + ovf2. When f[1]=0 ;;; (secp256k1: f=2^32+977, f[1]=0), v[1] = ovf2. CX v[1] → ovf2. ;;; For general f, when f[1]=1 we'd need an extra CX ovf1 → ovf2 ;;; to absorb the f[1]·ovf1 term. Guarded explicitly below. ;;; 11. free ovf2, ovf1. ;;; ;;; Safety: errors when classical *dgcd-k2-bounded-shift* off. (define (mod-4x-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) "v-reg := 4 * v-reg mod p (in place). v-reg is (n+1)-wide; v stored in low n bits, bit n starts |0> and ends |0>. Same scratch suite as mod-double-inplace!. When *mod-double-use-pseudo-mersenne* is #t AND p is a pseudo-Mersenne prime (f = 2^n - p small), uses the V2 inlined dual-shift + ovf-capture path with two cadd-const calls sharing cin/tmp. Otherwise falls back to two mod-double-inplace! calls (general Solinas path). Defensive guard: errors when *dgcd-k2-bounded-shift* is off, since the host dispatch only emits this primitive when classical K=2 trace says shift2-bit=1 — calling it standalone outside that flow is a contract violation (would multiply v by 4 with no matching shift2-on-v_w under the iter). v-reg must be a named register (symbol). Allocates two ancilla registers `_4x-ovf1` and `_4x-ovf2` (1 bit each) — distinct from caller-supplied scratch so no aliasing risk. Both return to |0>." (cond ((not *dgcd-k2-bounded-shift*) (error "mod-4x-inplace! requires *dgcd-k2-bounded-shift* #t")) (else (let* ((n (- n+1 1)) (c-const (- (expt 2 n) p)) (f-bits (pmersenne-bit-length c-const)) (padding *mod-double-pseudo-mersenne-padding*) (lsbs (min n+1 (+ padding f-bits)))) (cond ;; ── V2: pseudo-Mersenne dual-shift + ovf-capture ── ;; Pseudo-Mersenne fast path when f small enough for lsbs < n+1 ;; AND f[1] = 0 (so post-fold v[1] = ovf2 cleanly with no ;; cross-term f[1]·ovf1). For secp256k1 f = 2^32 + 977 with ;; bit 1 = 0; for small-width probe p=11 f=5 with bit 1 = 0; both ;; OK. For f[1]=1 primes we fall back to V1 path. ((and *mod-double-use-pseudo-mersenne* (> c-const 0) (< (+ padding f-bits) n+1) (not (bit-set? c-const 1))) (let ((ovf1 '_4x-ovf1) (ovf2 '_4x-ovf2)) (alloc! c ovf1 1) (alloc! c ovf2 1) ;; (1) shift1 swap cascade — v[n] := v_orig[n-1], v[0] := 0. (let loop ((i n)) (when (> i 0) (gate-swap! c v-reg i v-reg (- i 1)) (loop (- i 1)))) ;; (2) capture ovf1: swap(v[n], ovf1). v[n] := 0, ovf1 := v_orig[n-1]. (gate-swap! c v-reg n ovf1 0) ;; (3) shift2 swap cascade — v[n] := v_orig[n-2], v[0] := 0 ;; (v[0] was 0 post step 1; remains 0 post step 3 since ;; each swap pushes the existing v[i-1] up). (let loop ((i n)) (when (> i 0) (gate-swap! c v-reg i v-reg (- i 1)) (loop (- i 1)))) ;; (4) capture ovf2: swap(v[n], ovf2). v[n] := 0, ovf2 := v_orig[n-2]. (gate-swap! c v-reg n ovf2 0) ;; (5+6) V2.5 fused fold: v[0..lsbs) += f·ovf1 + 2f·ovf2 in ;; ONE truncated carry sweep via per-position-controls ;; primitive. Replaces V2's two sequential cadd-const! ;; calls (which each emitted a full Solinas ripple). ;; Derives 4 ancilla (h, xed, eord, n10) from ovf1/ovf2, ;; issues 1 truncated ripple at width lsbs, uncomputes ;; the ancilla. Net save ~58 Toff/call vs V2. ;; Hardcoded for secp256k1 c = 2^32+977 table (HEAD ;; compressed.rs:2196-2210). Pre-guard via lsbs check. (cond ((and (> lsbs (+ (highest-set-bit c-const) 2)) *cadd-direct-trunc-fast*) (cadd-2-controls-trunc-fast! c v-reg lsbs c-const ovf1 0 (* 2 c-const) ovf2 0 tmp-reg *cadd-direct-window* (cdtf-alloc-bit-base! lsbs))) (else ;; Narrow lsbs (small-width probe) OR direct-trunc-fast ;; off: keep V2's two-cadd path as the correctness floor. (cadd-const! c v-reg lsbs c-const ovf1 0 cin-reg cin-idx tmp-reg) (cadd-const! c v-reg lsbs (* 2 c-const) ovf2 0 cin-reg cin-idx tmp-reg))) ;; (7) Uncompute ovf1 via parity from v[0] = ovf1. (gate-cx! c v-reg 0 ovf1 0) ;; (8) Uncompute ovf2 via parity from v[1] = ovf2 (guard above ;; ensures f[1] = 0 so no f[1]·ovf1 cross term). (gate-cx! c v-reg 1 ovf2 0) (free! c ovf2) (free! c ovf1))) ;; Pseudo-Mersenne but f[1]=1 OR no headroom: fall back to V1 ;; sequential pseudo-Mersenne folds (correct, no extra savings). ((and *mod-double-use-pseudo-mersenne* (> c-const 0) (< (+ padding f-bits) n+1)) (mod-double-inplace-pseudo-mersenne! c v-reg n+1 p c-const cin-reg cin-idx tmp-reg flag-reg flag-idx) (mod-double-inplace-pseudo-mersenne! c v-reg n+1 p c-const cin-reg cin-idx tmp-reg flag-reg flag-idx)) ;; General Solinas fallback: two mod-double-inplace! calls. (else (mod-double-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) (mod-double-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx))))))) ;;; ── csub-2-controls-trunc-fast! — gate-level inverse of cadd-2-controls-trunc-fast! ─ ;;; ;;; Drop-in mirror of `cadd-2-controls-trunc-fast!` (lines 1224-1325) where ;;; the per-position-controls truncated ripple uses `csub-per-position- ;;; controls-trunc!` instead of `cadd-per-position-controls-trunc!`. Derives ;;; the same 4 ancilla controls (h, xed, eord, n10) and uncomputes them in ;;; the exact-reverse order. Used by `mod-4x-inverse-inplace!` to gate-level ;;; invert HEAD's secp256k1-class pseudo-Mersenne 4x fold. ;;; ;;; Toffoli budget: identical to `cadd-2-controls-trunc-fast!` since ;;; csub-per-position-controls-trunc! emits the same 3-CCX-per-bit borrow ;;; sweep + HMR uncompute as the add variant. The 4 derived-ancilla setup + ;;; teardown is bit-for-bit shared with the forward call. ;;; ;;; Safety: identical bit-pattern guards as the forward variant — k2 must ;;; equal 2·k1 mod 2^lsbs AND k1 must carry HEAD's table bits {0,4,6,7,8,9,hi}. ;;; Errors otherwise. Caller responsible for picking a fresh `bit-base` ;;; window that does not collide with concurrent uses of the classical-bit ;;; lane (forward `cadd-2-controls-trunc-fast!` consumes ;;; [bit-base, bit-base + last] during its HMR uncompute pass). (define (csub-2-controls-trunc-fast! c acc-reg lsbs k1 ctrl1-reg ctrl1-idx k2 ctrl2-reg ctrl2-idx tmp-reg window bit-base) "acc[0..lsbs) -= (ctrl1 ? k1 : 0) + (ctrl2 ? k2 : 0) in ONE truncated borrow sweep with per-position-controls. Hardcoded for HEAD's secp256k1-class table where k2 == 2·k1 AND k1's bits sit at {0,4,6,7,8,9,hi}. Gate-level inverse of cadd-2-controls-trunc-fast!. *fold-freed-tail* routes through cadd-fold-ripple-freed-tail! with is-add=#f (HEAD compressed.rs:3440 — same primitive serves both add and sub fused folds)." (cond (*fold-freed-tail* (let* ((kk1 (modulo k1 (expt 2 lsbs))) (kk2 (modulo k2 (expt 2 lsbs)))) (cond ((= lsbs 0) #t) ((and (= kk1 0) (= kk2 0)) #t) (else (let ((hi (highest-set-bit kk1))) (when (not (= kk2 (modulo (* 2 kk1) (expt 2 lsbs)))) (error "csub-2-controls-trunc-fast!: k2 != 2*k1 (mod 2^lsbs)" k1 k2 lsbs)) (when (not (and (bit-set? kk1 0) (bit-set? kk1 4) (bit-set? kk1 6) (bit-set? kk1 7) (bit-set? kk1 8) (bit-set? kk1 9) (bit-set? kk1 hi))) (error "csub-2-controls-trunc-fast!: k1 missing required bits" k1 hi)) (when (<= lsbs (+ hi 1)) (error "csub-2-controls-trunc-fast!: lsbs <= hi+1, no room" lsbs hi)) (let* ((last (min (- lsbs 2) (+ (+ hi 1) window))) (hi-delta (+ hi 1))) (when (<= last hi-delta) (error "csub-2-controls-trunc-fast!: *fold-freed-tail* needs last > hi+1" last hi-delta)) (cadd-fold-ripple-freed-tail! c acc-reg lsbs ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx last hi hi-delta #f bit-base))))))) (else (let* ((kk1 (modulo k1 (expt 2 lsbs))) (kk2 (modulo k2 (expt 2 lsbs)))) (cond ((= lsbs 0) #t) ((and (= kk1 0) (= kk2 0)) #t) (else (let ((hi (highest-set-bit kk1))) (when (not (= kk2 (modulo (* 2 kk1) (expt 2 lsbs)))) (error "csub-2-controls-trunc-fast!: k2 != 2*k1 (mod 2^lsbs)" k1 k2 lsbs)) (when (not (and (bit-set? kk1 0) (bit-set? kk1 4) (bit-set? kk1 6) (bit-set? kk1 7) (bit-set? kk1 8) (bit-set? kk1 9) (bit-set? kk1 hi))) (error "csub-2-controls-trunc-fast!: k1 missing required bits" k1 hi)) (when (<= lsbs (+ hi 1)) (error "csub-2-controls-trunc-fast!: lsbs <= hi+1, no room" lsbs hi)) ;; Derive 4 ancilla controls — identical to forward path. (let ((h '_v25r-h) (xed '_v25r-xed) (eord '_v25r-eord) (n10 '_v25r-n10)) (alloc! c h 1) (alloc! c xed 1) (alloc! c eord 1) (alloc! c n10 1) ;; h = ctrl1 & ctrl2 (gate-ccx! c ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx h 0) ;; xed = ctrl1 ⊕ ctrl2 (gate-cx! c ctrl1-reg ctrl1-idx xed 0) (gate-cx! c ctrl2-reg ctrl2-idx xed 0) ;; eord = xed ⊕ h (gate-cx! c xed 0 eord 0) (gate-cx! c h 0 eord 0) ;; n10 = ctrl2 ⊕ h (gate-cx! c ctrl2-reg ctrl2-idx n10 0) (gate-cx! c h 0 n10 0) ;; Issue the truncated borrow sweep with HEAD's per-position table. (let* ((last (min (- lsbs 2) (+ (+ hi 1) window))) (controls (make-perpos-secp256k1-fold-controls hi (cons ctrl1-reg ctrl1-idx) (cons ctrl2-reg ctrl2-idx) (cons xed 0) (cons eord 0) (cons n10 0) (cons h 0)))) (csub-per-position-controls-trunc! c acc-reg lsbs controls last tmp-reg bit-base)) ;; Uncompute derived ancilla in EXACT reverse — mirrors forward. (gate-cx! c h 0 n10 0) (gate-cx! c ctrl2-reg ctrl2-idx n10 0) (gate-cx! c h 0 eord 0) (gate-cx! c xed 0 eord 0) (gate-cx! c ctrl2-reg ctrl2-idx xed 0) (gate-cx! c ctrl1-reg ctrl1-idx xed 0) (gate-ccx! c ctrl1-reg ctrl1-idx ctrl2-reg ctrl2-idx h 0) (free! c n10) (free! c eord) (free! c xed) (free! c h))))))))) ; closes (else ...) of *fold-freed-tail* dispatch ;;; ── mod-halve-inplace! — v := v * 2⁻¹ mod p, inverse of mod-double-inplace! ─ ;;; ;;; Walk mod-double-inplace! steps in reverse with each step inverted. ;;; All 6 steps are self-inverse OR (add-const ↔ sub-const), (csub-const ;;; ↔ cadd-const). Used to walk hi back to its pre-Solinas state in ;;; Stage 2 of mod-mul-solinas!. (define (mod-halve-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) "Inverse of mod-double-inplace!: v := v * (2⁻¹ mod p)." (let* ((n (- n+1 1)) (c-const (- (expt 2 n) p))) ;; (6') CX v[0] -> flag (self-inverse) (gate-cx! c v-reg 0 flag-reg flag-idx) ;; (5') CX flag -> v[n] (self-inverse) (gate-cx! c flag-reg flag-idx v-reg n) ;; (4') Reverse the X-csub-X sandwich. cadd-const replaces csub-const. (gate-x! c flag-reg flag-idx) (cadd-const! c v-reg n+1 c-const flag-reg flag-idx cin-reg cin-idx tmp-reg) (gate-x! c flag-reg flag-idx) ;; (3') CX v[n] -> flag (self-inverse) (gate-cx! c v-reg n flag-reg flag-idx) ;; (2') sub-const c at width n+1 (inverse of add-const) (sub-const! c v-reg n+1 c-const cin-reg cin-idx tmp-reg) ;; (1') Reverse the SWAP cascade. Walk from bottom up. (let loop ((i 1)) (when (<= i n) (gate-swap! c v-reg i v-reg (- i 1)) (loop (+ i 1)))))) ;;; ── mod-4x-inverse-inplace! — v := v * 4⁻¹ mod p, gate-level inverse ─ ;;; of mod-4x-inplace! ;;; ;;; HEAD reference: `compressed.rs:2283-2403` (`dialog_gcd_fused_halve_y`) ;;; — runs HEAD's apply-phase REVERSE pass during compressed-block ;;; decompression. Half of HEAD's headline -25k Toff/shot savings at ;;; iters=258 rides the reverse fold. ;;; ;;; Algorithm: walk `mod-4x-inplace!` (lines 3017-3122) in reverse with ;;; each step replaced by its gate-level inverse. ;;; ;;; Forward (V2.5 pseudo-Mersenne path): ;;; 1. alloc ovf1, ovf2 ;;; 2. shift1 cascade (swap down: bit i ↔ bit i-1, i = n..1) ;;; 3. swap(v[n], ovf1) ;;; 4. shift2 cascade (swap down) ;;; 5. swap(v[n], ovf2) ;;; 6. cadd-2-controls-trunc-fast! (+ f·ovf1 + 2f·ovf2 single ripple) ;;; OR two cadd-const fallback (narrow-lsbs / direct-trunc-fast off) ;;; 7. cx v[0] → ovf1 (parity uncompute) ;;; 8. cx v[1] → ovf2 (parity uncompute) ;;; 9. free ovf2, ovf1 ;;; ;;; Reverse: ;;; 1. alloc ovf1, ovf2 (both |0>) ;;; 2'. cx v[1] → ovf2 (self-inverse — re-encode parity) ;;; 3'. cx v[0] → ovf1 (self-inverse — re-encode parity) ;;; 4'. csub-2-controls-trunc-fast! (inverse of 6 forward) ;;; OR two csub-const fallback ;;; 5'. swap(v[n], ovf2) (self-inverse) ;;; 6'. shift2 cascade reversed (swap UP: bit i-1 ↔ bit i, i = 1..n) ;;; 7'. swap(v[n], ovf1) (self-inverse) ;;; 8'. shift1 cascade reversed (swap up) ;;; 9'. free ovf2, ovf1 ;;; ;;; The shift cascades use `gate-swap!` which is self-inverse, so reversing ;;; the loop direction inverts the cascade. ;;; ;;; Each cadd ↔ csub pair has identical Toffoli budget — the reverse fold ;;; emits the SAME -58 Toff/call savings vs the V1 sequential `mod-halve` ;;; equivalent. ~50 % shift2-density × iters=258 ≈ 129 calls/shot → ;;; -7-8k Toff/shot when the reverse-fusion has a callsite. Combined ;;; with forward fusion: HEAD's headline -25k Toff/shot. ;;; ;;; CALLSITE NOTE: lumbda's `mod-inv-by-dialog-gcd-host!` backward sweep ;;; (line 595) runs CLASSICAL-REPLAY via `classical-reset!`, not a ;;; gate-level reverse iter walk. The reverse-fusion primitive ships now ;;; so a future sweep that adds a gate-level reverse iter (Option A, ;;; multi-day) has the apply-phase reverse-fold ready. See ;;; SWEEP-NOTES.md for the substrate gap analysis. ;;; ;;; Safety: identical guards to `mod-4x-inplace!`. Errors when ;;; *dgcd-k2-bounded-shift* off (semantics meaningless outside K=2 trace). (define (mod-4x-inverse-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) "v-reg := v-reg * (4⁻¹ mod p) (in place). v-reg is (n+1)-wide; v stored in low n bits, bit n starts |0> and ends |0>. Same scratch suite as mod-4x-inplace!. When *mod-double-use-pseudo-mersenne* is #t AND p is a pseudo-Mersenne prime AND f[1] = 0, walks the V2 inlined dual-shift + ovf-capture path in reverse using csub-const (or csub-2-controls-trunc-fast! when *cadd-direct-trunc-fast* on). Otherwise falls back to two mod-halve-inplace! calls (general Solinas path). Defensive guard: errors when *dgcd-k2-bounded-shift* is off." (cond ((not *dgcd-k2-bounded-shift*) (error "mod-4x-inverse-inplace! requires *dgcd-k2-bounded-shift* #t")) (else (let* ((n (- n+1 1)) (c-const (- (expt 2 n) p)) (f-bits (pmersenne-bit-length c-const)) (padding *mod-double-pseudo-mersenne-padding*) (lsbs (min n+1 (+ padding f-bits)))) (cond ;; ── V2-reverse: pseudo-Mersenne dual-shift + ovf-capture inverse ── ((and *mod-double-use-pseudo-mersenne* (> c-const 0) (< (+ padding f-bits) n+1) (not (bit-set? c-const 1))) (let ((ovf1 '_4xr-ovf1) (ovf2 '_4xr-ovf2)) (alloc! c ovf1 1) (alloc! c ovf2 1) ;; (8') CX v[1] -> ovf2 (self-inverse — re-encode ovf2 parity). (gate-cx! c v-reg 1 ovf2 0) ;; (7') CX v[0] -> ovf1 (self-inverse — re-encode ovf1 parity). (gate-cx! c v-reg 0 ovf1 0) ;; (5+6') Inverse fused fold: v[0..lsbs) -= f·ovf1 + 2f·ovf2. (cond ((and (> lsbs (+ (highest-set-bit c-const) 2)) *cadd-direct-trunc-fast*) ;; H7a peel-8 fix: cdtf-alloc-bit-base! gives this call a ;; fresh non-overlapping slot range, so the prior `(+ base ;; (* 8 n+1))` hand-offset to dodge forward/reverse HMR ;; collision is no longer required. (csub-2-controls-trunc-fast! c v-reg lsbs c-const ovf1 0 (* 2 c-const) ovf2 0 tmp-reg *cadd-direct-window* (cdtf-alloc-bit-base! lsbs))) (else ;; Narrow lsbs OR direct-trunc-fast off: invert via two ;; csub-const calls in reverse order (matches V2 fallback ;; pair `cadd-const(c,ovf1)` + `cadd-const(2c,ovf2)`). (csub-const! c v-reg lsbs (* 2 c-const) ovf2 0 cin-reg cin-idx tmp-reg) (csub-const! c v-reg lsbs c-const ovf1 0 cin-reg cin-idx tmp-reg))) ;; (4') swap(v[n], ovf2) — self-inverse (re-injects v_orig[n-2]). (gate-swap! c v-reg n ovf2 0) ;; (3') Reverse shift2 swap cascade. Forward walked i = n..1 ;; with gate-swap(v[i], v[i-1]); reverse walks i = 1..n. (let loop ((i 1)) (when (<= i n) (gate-swap! c v-reg i v-reg (- i 1)) (loop (+ i 1)))) ;; (2') swap(v[n], ovf1) — self-inverse (re-injects v_orig[n-1]). (gate-swap! c v-reg n ovf1 0) ;; (1') Reverse shift1 swap cascade. (let loop ((i 1)) (when (<= i n) (gate-swap! c v-reg i v-reg (- i 1)) (loop (+ i 1)))) (free! c ovf2) (free! c ovf1))) ;; Pseudo-Mersenne but f[1]=1 OR no headroom: invert via V1 ;; sequential pseudo-Mersenne halves (two mod-halve passes). ((and *mod-double-use-pseudo-mersenne* (> c-const 0) (< (+ padding f-bits) n+1)) (mod-halve-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) (mod-halve-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx)) ;; General Solinas fallback: two mod-halve-inplace! calls. (else (mod-halve-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx) (mod-halve-inplace! c v-reg n+1 p cin-reg cin-idx tmp-reg flag-reg flag-idx)))))))