diff --git a/ERRATA.md b/ERRATA.md
index 4c9edfa..624c5ff 100644
--- a/ERRATA.md
+++ b/ERRATA.md
@@ -1062,3 +1062,113 @@ the two versions cannot drift. Magma now passes all three.
basis-aware either, so a posReduced run wrote over the negReduced run's log. Worse
than untidy: `--inherit-from` reads those logs, so a negReduced arb could have been
handed posReduced cases. Found by noticing a POS log where a NEG one belonged.
+
+## E23: the gates mutate formula files, so they cannot be run concurrently
+
+**Found 2026-08-26 during C4, by making the mistake.** `verification/selftest.py`
+provokes its own guards by editing formula files in place and restoring them:
+`blocks` drops a live `ExactQuotient` from `arb_splitG3_ADD.mag`, `dominance`
+deletes a live `k3` assignment, `adjugate` removes a ledger comment. Each run
+restores what it touched, and each is correct on its own.
+
+**Two runs at once are not.** A second `selftest` restored a file while the first
+still held it mutated, and the restore was lost. The tree was left with
+`g3/ramifiedModel/g3Formulas/arb_ramifiedG3_ADD.mag` missing the six-line
+`// top: 15m 0s 9a` ledger comment that `adjugate.py` anchors on -- a file
+neither run was testing and no one had edited.
+
+**It surfaced as a gate failure naming an untouched file:** `adjugate` reporting
+`arb_ramifiedG3_ADD.mag no longer carries its 'top' op-count comment, so
+shipped_7 is measured against nothing`, while a standalone `adjugate` run passed.
+That is a confusing signal, and the natural first hypothesis -- that the edit
+under test broke something -- is wrong.
+
+**The same hazard applies to Magma.** `./test_all.sh` loads formula files as each
+tester starts, so a `selftest` running alongside it can feed a deliberately
+broken file to a tester, and the suite's verdict then describes neither the
+committed code nor the code under test.
+
+**Not fixed, and the fix is not obvious.** Restoring via a temporary copy rather
+than in place would remove the hazard, but the provocations must edit the real
+path because the gates locate their targets by content in the real file. A lock
+file would serialise them at the cost of silent waiting. Recorded so the next
+person recognises the symptom rather than debugging a phantom formula defect.
+
+**Meanwhile: run the gates serially, and never alongside Magma.** If a gate fails
+naming a file the current work did not touch, check `git status` before believing
+it.
+
+## E24: a proved saving in the split genus-3 addition that cannot yet be applied
+
+**Registered 2026-08-26, when C5 was dropped from the C4 pull request.** Not a
+defect in the formulas -- a saving that is mathematically established and whose
+implementation is blocked on a question about the code, recorded per the standing
+rule that a result proved before its oracle exists is written down rather than
+applied.
+
+**The setting.** In `F_q[x]`, one Euclidean division step on `(up, dw2)` gives
+`up = q*dw2 + dw3` with `deg dw3 < deg dw2`. That is the first step of the
+extended Euclidean algorithm on the pair, and the extended algorithm maintains
+Bezout cofactors alongside the remainders: with `s_i*up + t_i*dw2 = r_i` and the
+recurrences `s_{i+1} = s_{i-1} - q_i*s_i`, `t_{i+1} = t_{i-1} - q_i*t_i`, after
+one step `t_1 = -q`. So the cofactor of `dw2` is, up to sign, exactly the
+quotient the division already computed. Reducing modulo `up` gives
+`t_1*dw2 = dw3 (mod up)`, hence `dw2^{-1} = t_1*dw3^{-1} (mod up)` whenever
+`dw3` is a unit there.
+
+**The saving.** In the leaf of `Deg3ADD` where the guards force `d = 0`,
+`t7 = 0` and `t4 = 0`, the formulas need `b2 = S*(dw2^{-1} mod up)` with `S` the
+monic `dw3`, and spend **11M 6A** on a `2x2` Cramer solve, its own comment naming
+the method: `//b2 := S*R!((Q!a)^-1) mod up; //2x2 system`. By the above the
+polynomial part is free, and the intermediate it needs is already in hand:
+`t0 := w2*up2 - w1` is computed twelve lines earlier as part of
+`//dw3 := up mod dw2;`.
+
+**What is proved.** Constructing inputs that satisfy the leaf's precondition --
+`S1 = x + a`, `dw2 = S1*(bx + c)`, `q = (1/b)x + g`, `up = q*dw2 + lam*S1`, so
+`S1 | up` and `up mod dw2 = lam*S1` -- and computing the true
+`S*dw2^{-1} mod unp` independently in the quotient ring: the closed form
+`(-w2, -t0)` is proportional to it in **400 of 400** trials over GF(1000003).
+Constructed rather than sampled because rejection sampling reaches this leaf
+about once in ten thousand random inputs.
+
+**What blocks it.** The file's `(b1, b0)` is **not** that quantity up to any
+scalar, 0 of 400. So the substitution is not `b1 := -w2; b0 := -t0` at some
+weight, and the file's normalisation of `b2` is unknown. Two leads:
+`b1 := m1 + w4*m2` and `b0 := m3 + w4*m4` form `M*(1,w4)^T` for
+`M = [[m1,m2],[m3,m4]]`, which is not a Cramer solve for an inverse; and the
+comment `//a := dw2 mod up;` does not say whether the modulus is the original
+degree-3 `up` or the degree-2 quotient built two lines above, with the file's
+`up` still holding the degree-3 one at that point.
+
+**Do not settle this by re-implementing the block in Python.** That was tried.
+The transcription is precisely what is in doubt, so the experiment cannot
+distinguish its own error from a real difference. Observe the real execution
+instead: a recursive copy of `maginterp`'s statement loop in the manner of
+`detect.py`, snapshotting the environment where `b0` lands, over the whitebox
+cases reaching `ADD281`, `ADD282` and `ADD283`. Note `opcount.py` cannot verify
+such an edit -- the leaf is not a priced row -- so `whitebox.py` and Magma would
+carry it alone. The corpus **can** see the leaf: breaking `b1` deliberately gives
+9 whitebox mismatches, checked 2026-08-26.
+
+**Worth little in expectation, which is why it was dropped rather than finished.**
+Three zero-tests deep and reached about once in ten thousand inputs, so the
+11M 6A is a raw count on a path almost never taken, and no published cell moves.
+
+**Nine sibling sites are unexamined**, each a `b2` computation of the same family:
+`arb_splitG3_ADD.mag:8113/8249/9312`, `nch2_splitG3_ADD.mag:7834/7970/9031`,
+`ch2_splitG3_ADD.mag:7696/7832/8871` (numbering predates C4; locate by content).
+Each needs a division producing the remainder just above it and needs `u = up`.
+Genus-2 split has no `b2` site at all, and no doubling has one.
+
+**The transferable rule, which outlives this leaf.** Wherever an explicit formula
+computes a remainder `r = a mod b` and later requires the Bezout cofactor of `b`
+modulo `a` -- equivalently an inverse of `b` in `F_q[x]/(a)` -- the quotient
+discarded by that division already is that cofactor, up to the scalar `lc(r)`.
+The ramified model exploits this and the split model does not, and the reason is
+inversion scheduling rather than oversight: ramified inverts early, so `S1`
+becomes an exact scalar and everything downstream is unweighted, while split
+inverts late because its `f` is non-monic of degree `2g+2` and `upp` must be
+normalised after `upp` is known, which is how it holds to a single inversion.
+Late inversion means every upstream quantity is carried with a weight, and a
+weighted cofactor is not a drop-in for an exact one.
diff --git a/NEW_WORK.md b/NEW_WORK.md
index 2f0d471..c222405 100644
--- a/NEW_WORK.md
+++ b/NEW_WORK.md
@@ -2958,3 +2958,117 @@ Two standing rules, both learned the hard way here:
alternative is that the next person re-runs the experiment.
- **State honest limits in the entry, not in a footnote.** E-T5 is not measured
and says so; the doubling composite is approximate and says so.
+
+## N33 — The adjugate is nearly free, and a discarded quotient is a Bezout cofactor
+
+**Status** — established, C4. **Where** — `g3/splitModel/negReduced/g3Formulas/`,
+all six `Deg3ADD` and `Deg3DBL` files; the published tables are
+`Thesis/chapter6.tex` `tab:g3splitfcosts{ADD,DBL}`, corrected as `E-T10`.
+
+**What was there.** Composing two degree-3 divisors needs `s = vt*q mod up`,
+where `q = d/w mod up` is a quadratic whose coefficient vector is the first
+column of the adjugate of the `3x3` matrix `T`. The split formulas built exactly
+that column -- three `2x2` minors `m1`, `m4`, `m7` -- took the determinant by
+expanding along `T`'s first row, and then reduced the product `vt*q` modulo `up`
+using Karatsuba twice.
+
+**The finding, and it is not the one the plan predicted.** Carrying the *whole*
+adjugate and applying it as a matrix-vector product is `+1M -12A` on the generic
+path of all six operations. The plan expected the extra multiplication to be paid
+at the `T` block, on the reasoning that nine entries must cost more than three.
+Measured, the block costs **`15M 0S 9A` either way**.
+
+The reason is structural rather than a happy accident, and it is the part worth
+publishing. Column 3 of `T` is `x` times column 2, reduced modulo `up`. `adj(T)`
+is itself a multiplication matrix -- by `q`, up to `det(T)` -- so it inherits that
+shift structure. Concretely, the bottom row `(m7, m8, m9)` is the cross product
+of columns 1 and 2 of `T` and needs no third column at all, and the remaining six
+entries are then **shifts of that row costing one multiplication each** instead
+of a `2x2` minor costing two:
+
+ m5 = m9 + w2*m8 m2 = -w0*m7 m1 = m5 + w1*m7
+ m4 = m8 + w2*m7 m6 = m2 - w1*m8 m3 = -w0*m8
+
+with `(w0, w1, w2)` the modulus coefficients. Three minors at `2M` and six shifts
+at `1M` is `12M`, exactly what three minors at `2M` plus the three now-unneeded
+`T` entries `t3`, `t6`, `t9` at `1M` used to cost. The nine-entry adjugate is
+free relative to the three-entry column.
+
+A further multiplication falls out of `t7*m3 = (-w0*t7)*m8 = t2*m8`, the signs
+cancelling because `t2` and `m3` carry the same `-w0` factor. So the determinant
+can be expanded along `T`'s first *column* reading `m8` in place of `m3`, and
+`m3` is left wanted only by the generic path, which computes it there -- no
+degenerate leaf pays for an entry it never reads.
+
+**Where the cost actually moves.** Downstream. Applying the matrix is
+`9M 6A` plus the three deferred entries at `3M 2A`, so `12M 8A`, against
+Karatsuba's `11M 20A`. One multiplication on, twelve additions off, and no
+reduction step because applying the multiplication matrix *is* the reduction.
+
+**Measured, six sites, `+1M -12A` at every one:**
+
+| | addition | doubling |
+|---|---|---|
+| arb | 65/3/87/12 -> **66/3/75/12** | 73/3/101/19 -> **74/3/89/19** |
+| nch2 | 65/3/85/0 -> **66/3/73/0** | 72/4/97/0 -> **73/4/85/0** |
+| ch2 | 65/3/80/0 -> **66/3/68/0** | 71/4/86/1 -> **72/4/74/1** |
+
+`+6M -72A` in total, accepted comfortably by the thesis's own `1M : 3A` rule. No
+other shape moved in any of the twelve families, which is the acceptance test:
+the trade is confined to the generic path it targets.
+
+**Scope, and why it is exactly six.** The `3x3` multiplication matrix exists only
+when reducing modulo a degree-3 modulus, so the lower-degree additions and
+doublings carry a smaller system with nothing to trade, and genus-2 split has no
+such matrix at all -- `m7` occurs zero times in all six of its formula files.
+
+**For the paper.** State the shift structure as the result, not the `+1M -12A`.
+The operation count is an artefact of one model at one genus; the statement that
+*the adjugate of a multiplication matrix inherits the shift structure of the
+matrix, so all nine entries cost barely more than one column* is what transfers,
+and it is what makes the matrix-vector form cheaper than forming the polynomial
+and reducing.
+
+### The refutation this replaced
+
+The plan carried a hand count saying the conversion **loses `2M 1A`** at the
+block and that the `+1M` is paid there. That count is wrong, and it was wrong in
+a way no amount of re-reading would have caught: it priced the ramified route's
+seven entries against split's three without noticing both arrangements spend the
+same fifteen multiplications. `verification/adjugate.py` settles it by executing
+both from their real `.mag` text -- `split_q_col1` at `15M 0S 9A` for four
+entries, `split_q` at `15M 0S 9A` for seven -- and the pre-C4 route is kept as a
+candidate rather than deleted so the comparison keeps scoring it.
+
+**The methodological point, which this project keeps relearning:** state what a
+change *removes*, and measure the total. A hand count of two arrangements is a
+prediction, not a result, and here the prediction had the right bottom line for
+the wrong reason. My own independent hand count also said `0M` before the
+measurement said `+1M`.
+
+### A related result, proved and deliberately not applied
+
+The same leaf structure exposed that split spends `11M 6A` reconstructing a
+Bezout cofactor it has already computed. The mathematics is settled -- `400 of
+400` constructed trials against ground truth in the quotient ring -- and the
+implementation is blocked on the file's normalisation of `b2`, so it is recorded
+in `ERRATA.md` **E24** rather than applied. The general statement belongs here
+because it outlives the leaf:
+
+**Wherever an explicit formula computes a remainder `r = a mod b` and later needs
+the Bezout cofactor of `b` modulo `a`, equivalently an inverse of `b` in
+`F_q[x]/(a)`, the quotient discarded by that division already is that cofactor,
+up to the scalar `lc(r)`.** One Euclidean step gives `t_1 = -q` in the extended
+algorithm's recurrence `t_{i+1} = t_{i-1} - q_i*t_i`, and reducing
+`s_1*a + t_1*b = r` modulo `a` gives `b^{-1} = t_1*r^{-1} (mod a)`. The cost of
+the modular inverse collapses to the cost of inverting a leading coefficient.
+
+The ramified model exploits this; the split model does not. The reason is
+**inversion scheduling**, and that is the publishable observation: ramified
+inverts early, so the monic-making scalar is exact and everything downstream is
+unweighted, while split must invert late because its `f` is non-monic of degree
+`2g+2` and `upp` needs normalising only after `upp` is known -- batching that
+normalisation with weight removal is precisely how the split formulas hold to a
+single inversion. Late inversion means every upstream quantity is carried
+projectively, and a weighted cofactor is not a drop-in for an exact one. The
+omission is a consequence of a deliberate design choice, not an oversight.
diff --git a/README.md b/README.md
index 6dee0b4..8190eca 100644
--- a/README.md
+++ b/README.md
@@ -31,15 +31,15 @@ The frequent case of the highest-degree operation in each family. "Frequent" me
| g3 ramified arb | 53 | 3 | 71 | 1 | 54 | 4 | 80 | 4 |
| g3 ramified nch2 | 53 | 3 | 59 | 0 | 53 | 5 | 61 | 0 |
| g3 ramified ch2 | 51 | 3 | 62 | 0 | 51 | 4 | 55 | 2 |
-| g3 split arb | 65 | 3 | 87 | 12 | 73 | 3 | 101 | 19 |
-| g3 split nch2 | 65 | 3 | 85 | 0 | 72 | 4 | 97 | 0 |
-| g3 split ch2 | 65 | 3 | 80 | 0 | 71 | 4 | 86 | 1 |
+| g3 split arb | 66 | 3 | 75 | 12 | 74 | 3 | 89 | 19 |
+| g3 split nch2 | 66 | 3 | 73 | 0 | 73 | 4 | 85 | 0 |
+| g3 split ch2 | 66 | 3 | 68 | 0 | 72 | 4 | 74 | 1 |
Every figure is measured. `python3 verification/opcount.py --family ` executes the formulas over a real field, identifies the frequent case by observing which branch is taken, and cross-checks each contributing call against the Cantor reference implementation. All fifteen families measure, the twelve above plus genus-2 split negative reduced.
-Where the thesis publishes a cell, measurement reproduces it exactly: the genus-2 ramified rows against `tab:ramfcosts`, and 168 split shapes against `tab:splitfcosts` and `tab:g3splitfcosts{ADD,DBL}`, every one at exactly one inversion. One systematic divergence turned up during that check and was the tool's rather than the thesis's, a flat `+2A` on every split row, because a divisor's balancing weight is a small integer and `n := n1 + n2 - 2` is bookkeeping rather than field arithmetic. See [NEW_WORK.md](NEW_WORK.md) N31.
+Where the thesis publishes a cell, measurement reproduces it exactly: the genus-2 ramified rows against `tab:ramfcosts`, and 168 split shapes against `tab:splitfcosts` and `tab:g3splitfcosts{ADD,DBL}`, every one at exactly one inversion. Two of those cells have since moved by design: the genus-3 split `33ADD n=0,0` and `3DBL n=0` each trade one multiplication for twelve additions, so measurement and the published table differ there deliberately, recorded in [Thesis/ERRATA.md](Thesis/ERRATA.md). One systematic divergence turned up during the original check and was the tool's rather than the thesis's, a flat `+2A` on every split row, because a divisor's balancing weight is a small integer and `n := n1 + n2 - 2` is bookkeeping rather than field arithmetic. See [NEW_WORK.md](NEW_WORK.md) N31.
The genus-2 split figures are positive reduced, the basis of record. Negative reduced is a different algorithm and differs by an operation or two on several rows.
@@ -119,10 +119,10 @@ Against Rezai Rad et al. 2019 and Sutherland 2019.
| M | S | A | C | M | S | A | C | M | S | A | C |
-| 3DBL odd | 85 | 2 | 163 | 0 | 74 | 8 | 127 | 0 | 72 | 4 | 97 | 0 |
-| 3ADD odd | 75 | 2 | 138 | 0 | 73 | 6 | 127 | 0 | 65 | 3 | 85 | 0 |
-| 3DBL char 2 | 89 | 1 | 116 | 0 | n/a | 71 | 4 | 86 | 1 |
-| 3ADD char 2 | 81 | 0 | 118 | 0 | n/a | 65 | 3 | 80 | 0 |
+| 3DBL odd | 85 | 2 | 163 | 0 | 74 | 8 | 127 | 0 | 73 | 4 | 85 | 0 |
+| 3ADD odd | 75 | 2 | 138 | 0 | 73 | 6 | 127 | 0 | 66 | 3 | 73 | 0 |
+| 3DBL char 2 | 89 | 1 | 116 | 0 | n/a | 72 | 4 | 74 | 1 |
+| 3ADD char 2 | 81 | 0 | 118 | 0 | n/a | 66 | 3 | 68 | 0 |
@@ -544,12 +544,12 @@ Operation costs for every non-degenerate function, measured as in [Typical Case
| 22ADD n=1,1 | 37 | 1 | 56 | 7 | 36 | 2 | 57 | 0 | 33 | 4 | 52 | 0 |
| 23ADD n=0,0 | 75 | 3 | 89 | 18 | 72 | 5 | 78 | 3 | 72 | 4 | 76 | 7 |
| 23ADD n=1,0 | 41 | 1 | 59 | 3 | 41 | 1 | 57 | 0 | 41 | 1 | 55 | 0 |
-| 33ADD n=0,0 | 65 | 3 | 87 | 12 | 65 | 3 | 85 | 0 | 65 | 3 | 80 | 0 |
+| 33ADD n=0,0 | 66 | 3 | 75 | 12 | 66 | 3 | 73 | 0 | 66 | 3 | 68 | 0 |
| 1DBL n=0 | 42 | 5 | 66 | 27 | 40 | 6 | 57 | 7 | 38 | 7 | 44 | 10 |
| 1DBL n=1 | 7 | 1 | 19 | 6 | 7 | 1 | 15 | 3 | 7 | 1 | 14 | 3 |
| 1DBL n=2 | 14 | 3 | 25 | 8 | 14 | 3 | 24 | 3 | 12 | 3 | 19 | 4 |
| 2DBL n=0 | 62 | 5 | 95 | 31 | 60 | 7 | 89 | 4 | 60 | 7 | 80 | 7 |
| 2DBL n=1 | 37 | 0 | 56 | 13 | 35 | 2 | 54 | 3 | 36 | 1 | 48 | 3 |
-| 3DBL n=0 | 73 | 3 | 101 | 19 | 72 | 4 | 97 | 0 | 71 | 4 | 86 | 1 |
+| 3DBL n=0 | 74 | 3 | 89 | 19 | 73 | 4 | 85 | 0 | 72 | 4 | 74 | 1 |
diff --git a/Thesis/ERRATA.md b/Thesis/ERRATA.md
index f9f9500..598b7e5 100644
--- a/Thesis/ERRATA.md
+++ b/Thesis/ERRATA.md
@@ -343,3 +343,41 @@ from the `f4`-free code, which is why the contradiction is with the sentence and
the numbers.
---
+
+## E-T10 — the genus-3 split Degree-3 rows trade one multiplication for twelve additions
+
+**`chapter6.tex:2389` in `tab:g3splitfcostsDBL` and `:2491` in `tab:g3splitfcostsADD`.**
+**Measured, and hand-counted independently.**
+
+**This is not an error in the thesis.** The published counts were correct for the
+formulas as published; the formulas have since changed under them. The entry
+exists because `Thesis/` must not quote a cost the code no longer has.
+
+ :2389 &73&3&101&19 &72&4&97&0 &71&4&86&1 becomes
+ &74&3& 89&19 &73&4&85&0 &72&4&74&1
+
+ :2491 &65&3& 87&12 &65&3&85&0 &65&3&80&0 becomes
+ &66&3& 75&12 &66&3&73&0 &66&3&68&0
+
+Six cells, `+1M -12A` in each, across the `arb`, `nch2` and `ch2` columns of both
+tables. S and C are unchanged everywhere, and no other row in either table moves.
+
+**What changed.** The addition and doubling now carry the full adjugate of the
+`3x3` matrix `T` and apply it as a matrix-vector product, where they previously
+built only its first column and reduced `vt*q mod up` by Karatsuba twice. The
+trade is `+1M` for `-12A`, which the thesis's own `1M : 3A` rule accepts
+comfortably.
+
+**Where the cost actually moves, which the derivation should state.** Not at the
+`T` block: that costs `15M 0S 9A` either way, yielding three adjugate entries in
+the old arrangement and seven in the new, because column 3 of `T` is `x` times
+column 2 reduced modulo the modulus, so six of the nine entries are one
+multiplication each rather than a `2x2` minor. The whole `+1M` and the whole
+`-12A` are downstream, where applying the matrix costs `12M 8A` against
+Karatsuba's `11M 20A`.
+
+**Adjudicated per the standing rule.** `verification/opcount.py` measures the new
+figures by execution and a hand count of the changed region reproduces the same
+`+1M -12A`, two methods sharing no code. `verification/selftest.py`'s published
+pins for `33ADD n=0,0` and `3DBL n=0` are updated with the reason recorded
+inline, and every other pinned cell still matches its published value.
diff --git a/Thesis/chapter6.tex b/Thesis/chapter6.tex
index ad8e4f3..2323845 100644
--- a/Thesis/chapter6.tex
+++ b/Thesis/chapter6.tex
@@ -2386,7 +2386,7 @@ \subsection{Field Operation Costs and Comparisons}
&62&5&95&31 &60&7&89&4 &60&7&80&7\TS\\
\hline
Degree 3
-&73&3&101&19 &72&4&97&0 &71&4&86&1\TS\\
+&74&3&89&19 &73&4&85&0 &72&4&74&1\TS\\
\hline
\end{tabular}
\end{table}
@@ -2488,7 +2488,7 @@ \subsection{Field Operation Costs and Comparisons}
&75&3&89&18 &72&5&78&3 &72&4&76&7\TS\\
\hline
Degree 3
-&65&3&87&12 &65&3&85&0 &65&3&80&0\TS\\
+&66&3&75&12 &66&3&73&0 &66&3&68&0\TS\\
\hline
\end{tabular}
\end{table}
diff --git a/g3/splitModel/negReduced/g3Formulas/arb_splitG3_ADD.mag b/g3/splitModel/negReduced/g3Formulas/arb_splitG3_ADD.mag
index 1bbd95e..5ea92fc 100644
--- a/g3/splitModel/negReduced/g3Formulas/arb_splitG3_ADD.mag
+++ b/g3/splitModel/negReduced/g3Formulas/arb_splitG3_ADD.mag
@@ -8391,14 +8391,26 @@ Deg3ADD:= function(u2,u1,u0,v2,v1,v0,up2,up1,up0,vp2,vp1,vp0,ccs)
t2 := -up0*t7;
t5 := t1 - up1*t7;
t8 := t4 - up2*t7;
- t3 := -up0*t8;
- t6 := t2 - up1*t8;
- t9 := t5 - up2*t8;
+ //t3 := -up0*t8;
+ //t6 := t2 - up1*t8;
+ //t9 := t5 - up2*t8;
- m1 := t5*t9 - t6*t8;
- m4 := t6*t7 - t4*t9;
+ // Bottom row of the adjugate needs only columns 1 and 2 of T.
+ m9 := t1*t5 - t2*t4;
+ m8 := t2*t7 - t1*t8;
m7 := t4*t8 - t5*t7;
- d := t1*m1 + t2*m4 + t3*m7; //Determinant of T = t1*m1 + t2*m4 + t3*m7
+
+ // Column 3 of T is x*(column 2) reduced mod up, so the remaining six
+ // adjugate entries are shifts of the bottom row with one multiplication
+ // each rather than a 2x2 minor. m3, m4 and m6 are the three the
+ // determinant does not read, so they wait until the generic path below.
+ m5 := m9 + up2*m8;
+ m2 := -up0*m7;
+ m1 := m5 + up1*m7;
+
+ // t7*m3 = t7*(-up0*m8) = (-up0*t7)*m8 = t2*m8, the signs cancelling because
+ // t2 and m3 carry the same -up0 factor. So the determinant needs m8, not m3.
+ d := t1*m1 + t4*m2 + t2*m8; //Determinant of T
if IsZero(d) then
//dw1 := u mod up; // = (a1^-1)*S for S,a1,b1 = XGCD(u,up);
@@ -10042,16 +10054,17 @@ Deg3ADD:= function(u2,u1,u0,v2,v1,v0,up2,up1,up0,vp2,vp1,vp0,ccs)
vt1:= vp1 - v1;
vt0:= vp0 - v0;
- //Compute s = vt*q mod u2 using Karatsuba twice
- t0 := vt0*m1;
- t1 := vt1*m4;
- t2 := vt2*m7;
- t3 := (m4 + m7)*(vt1 + vt2) - t2 - t1 - up2*t2;
- t4 := up1*t2;
- t5 := t4 - t1;
- sp0 := t0 - up0*t3;
- sp1 := (m1 + m4)*(vt0 + vt1) - sp0 - (up0 + up1)*(t2 + t3) + t5;
- sp2 := (m1 + m7)*(vt0 + vt2) - t0 - t2 - t5 - up2*t3;
+ // the three adjugate entries the determinant did not need
+ m4 := m8 + up2*m7;
+ m6 := m2 - up1*m8;
+ m3 := -up0*m8;
+
+ //Compute s = vt*q mod up as the matrix-vector product sp = M*vt. M is
+ //multiplication by q = d/u mod up, so this is the same map as vt*q mod up,
+ //and applying the matrix needs no reduction step.
+ sp0 := vt0*m1 + vt1*m2 + vt2*m3;
+ sp1 := vt0*m4 + vt1*m5 + vt2*m6;
+ sp2 := vt0*m7 + vt1*m8 + vt2*m9;
if IsZero(sp2) then
if IsZero(sp1) then
diff --git a/g3/splitModel/negReduced/g3Formulas/arb_splitG3_DBL.mag b/g3/splitModel/negReduced/g3Formulas/arb_splitG3_DBL.mag
index dce8d44..bdb2d82 100644
--- a/g3/splitModel/negReduced/g3Formulas/arb_splitG3_DBL.mag
+++ b/g3/splitModel/negReduced/g3Formulas/arb_splitG3_DBL.mag
@@ -1163,16 +1163,26 @@ Deg3DBL:= function(u2,u1,u0,v2,v1,v0,ccs)
t2 := -u0*t7;
t5 := t1 - u1*t7;
t8 := t4 - u2*t7;
- t3 := -u0*t8;
- t6 := t2 - u1*t8;
- t9 := t5 - u2*t8;
-
- //Determinant of T = t1*m1 + t2*m4 + t3*m7
- //First column of M corresponds to polynomial q = m7x^2 + m4x + m1 = d/u1 mod u2.
- m1 := t5*t9 - t6*t8;
- m4 := t6*t7 - t4*t9;
+ //t3 := -u0*t8;
+ //t6 := t2 - u1*t8;
+ //t9 := t5 - u2*t8;
+
+ // Bottom row of the adjugate needs only columns 1 and 2 of T.
+ m9 := t1*t5 - t2*t4;
+ m8 := t2*t7 - t1*t8;
m7 := t4*t8 - t5*t7;
- d := t1*m1 + t2*m4 + t3*m7;
+
+ // Column 3 of T is x*(column 2) reduced mod u, so the remaining six
+ // adjugate entries are shifts of the bottom row with one multiplication
+ // each rather than a 2x2 minor. m3, m4 and m6 are the three the
+ // determinant does not read, so they wait until the generic path below.
+ m5 := m9 + u2*m8;
+ m2 := -u0*m7;
+ m1 := m5 + u1*m7;
+
+ // t7*m3 = t7*(-u0*m8) = (-u0*t7)*m8 = t2*m8, the signs cancelling because
+ // t2 and m3 carry the same -u0 factor. So the determinant needs m8, not m3.
+ d := t1*m1 + t4*m2 + t2*m8;
if IsZero(d) then
//gcd(h + 2*v1, u1) = (x + xp1)
@@ -1418,16 +1428,17 @@ Deg3DBL:= function(u2,u1,u0,v2,v1,v0,ccs)
kp0 := dn5 + c3*v0 - h2*v1 - v2*vh1 - u1*k2 - u2*k1 - t12 - t12;
- //Compute s = k*q mod u using Karatsuba twice
- t0 := kp0*m1;
- t1 := kp1*m4;
- t2 := kp2*m7;
- t3 := (m4 + m7)*(kp1 + kp2) - t2 - t1 - u2*t2;
- t4 := u1*t2;
- t5 := t4 - t1;
- sp0 := t0 - u0*t3;
- sp1 := (m1 + m4)*(kp0 + kp1) - sp0 - (u0 + u1)*(t2 + t3) + t5;
- sp2 := (m1 + m7)*(kp0 + kp2) - t0 - t2 - t5 - u2*t3;
+ // the three adjugate entries the determinant did not need
+ m4 := m8 + u2*m7;
+ m6 := m2 - u1*m8;
+ m3 := -u0*m8;
+
+ //Compute s = k*q mod u as the matrix-vector product sp = M*kp. M is
+ //multiplication by q = d/w mod u, so this is the same map as kp*q mod u,
+ //and applying the matrix needs no reduction step.
+ sp0 := kp0*m1 + kp1*m2 + kp2*m3;
+ sp1 := kp0*m4 + kp1*m5 + kp2*m6;
+ sp2 := kp0*m7 + kp1*m8 + kp2*m9;
if IsZero(sp2) then
if IsZero(sp1) then
diff --git a/g3/splitModel/negReduced/g3Formulas/ch2_splitG3_ADD.mag b/g3/splitModel/negReduced/g3Formulas/ch2_splitG3_ADD.mag
index 46f20cf..83feb5c 100644
--- a/g3/splitModel/negReduced/g3Formulas/ch2_splitG3_ADD.mag
+++ b/g3/splitModel/negReduced/g3Formulas/ch2_splitG3_ADD.mag
@@ -7964,14 +7964,27 @@ Deg3ADD:= function(u2,u1,u0,v2,v1,v0,up2,up1,up0,vp2,vp1,vp0,ccs)
t2 := up0*t7;
t5 := t1 + up1*t7;
t8 := t4 + up2*t7;
- //t3 := +u20*t8;
- t6 := t2 + up1*t8;
- t9 := t5 + up2*t8;
+ //t3 := up0*t8;
+ //t6 := t2 + up1*t8;
+ //t9 := t5 + up2*t8;
- m1 := t5*t9 + t6*t8;
- m4 := t6*t7 + t4*t9;
+ // Bottom row of the adjugate needs only columns 1 and 2 of T.
+ m9 := t1*t5 + t2*t4;
+ m8 := t2*t7 + t1*t8;
m7 := t4*t8 + t5*t7;
- d := t1*m1 + t2*m4 + up0*t8*m7; //Determinant of T = t1*m1 + t2*m4 + t3*m7
+
+ // Column 3 of T is x*(column 2) reduced mod up, so the remaining six
+ // adjugate entries are shifts of the bottom row with one multiplication
+ // each rather than a 2x2 minor. m3, m4 and m6 are the three the
+ // determinant does not read, so they wait until the generic path below.
+ m5 := m9 + up2*m8;
+ m2 := up0*m7;
+ m1 := m5 + up1*m7;
+
+ // t7*m3 = t7*(up0*m8) = (up0*t7)*m8 = t2*m8. So the determinant needs m8,
+ // not m3, and the inlined up0*t8*m7 it used to spend two multiplications
+ // on is gone.
+ d := t1*m1 + t4*m2 + t2*m8; //Determinant of T
if IsZero(d) then
//dw1 := u mod up; // = (a1^-1)*S for S,a1,b1 = XGCD(u,up);
@@ -9584,16 +9597,17 @@ Deg3ADD:= function(u2,u1,u0,v2,v1,v0,up2,up1,up0,vp2,vp1,vp0,ccs)
vt1:= vp1 + v1;
vt0:= vp0 + v0;
- //Compute s = vt*q mod u2 using Karatsuba twice
- t0 := vt0*m1;
- t1 := vt1*m4;
- t2 := vt2*m7;
- t3 := (m4 + m7)*(vt1 + vt2) + t2 + t1 + up2*t2;
- t4 := up1*t2;
- t5 := t4 + t1;
- sp0 := t0 + up0*t3;
- sp1 := (m1 + m4)*(vt0 + vt1) + sp0 + (up0 + up1)*(t2 + t3) + t5;
- sp2 := (m1 + m7)*(vt0 + vt2) + t0 + t2 + t5 + up2*t3;
+ // the three adjugate entries the determinant did not need
+ m4 := m8 + up2*m7;
+ m6 := m2 + up1*m8;
+ m3 := up0*m8;
+
+ //Compute s = vt*q mod up as the matrix-vector product sp = M*vt. M is
+ //multiplication by q = d/u mod up, so this is the same map as vt*q mod up,
+ //and applying the matrix needs no reduction step.
+ sp0 := vt0*m1 + vt1*m2 + vt2*m3;
+ sp1 := vt0*m4 + vt1*m5 + vt2*m6;
+ sp2 := vt0*m7 + vt1*m8 + vt2*m9;
if IsZero(sp2) then
if IsZero(sp1) then
diff --git a/g3/splitModel/negReduced/g3Formulas/ch2_splitG3_DBL.mag b/g3/splitModel/negReduced/g3Formulas/ch2_splitG3_DBL.mag
index f9022c2..3a51868 100644
--- a/g3/splitModel/negReduced/g3Formulas/ch2_splitG3_DBL.mag
+++ b/g3/splitModel/negReduced/g3Formulas/ch2_splitG3_DBL.mag
@@ -1090,16 +1090,26 @@ Deg3DBL:= function(u2,u1,u0,v2,v1,v0,ccs)
t2 := u0*t7;
t5 := t1 + u1*t7;
t8 := t4 + u2*t7;
- t3 := u0*t8;
- t6 := t2 + u1*t8;
- t9 := t5 + u2*t8;
-
- //Determinant of T = t1*m1 + t2*m4 + t3*m7
- //First column of M corresponds to polynomial q = m7x^2 + m4x + m1 = d/u1 mod u2.
- m1 := t5*t9 + t6*t8;
- m4 := t6*t7 + t4*t9;
+ //t3 := u0*t8;
+ //t6 := t2 + u1*t8;
+ //t9 := t5 + u2*t8;
+
+ // Bottom row of the adjugate needs only columns 1 and 2 of T.
+ m9 := t1*t5 + t2*t4;
+ m8 := t2*t7 + t1*t8;
m7 := t4*t8 + t5*t7;
- d := t1*m1 + t2*m4 + t3*m7;
+
+ // Column 3 of T is x*(column 2) reduced mod u, so the remaining six
+ // adjugate entries are shifts of the bottom row with one multiplication
+ // each rather than a 2x2 minor. m3, m4 and m6 are the three the
+ // determinant does not read, so they wait until the generic path below.
+ m5 := m9 + u2*m8;
+ m2 := u0*m7;
+ m1 := m5 + u1*m7;
+
+ // t7*m3 = t7*(u0*m8) = (u0*t7)*m8 = t2*m8. So the determinant needs m8,
+ // not m3.
+ d := t1*m1 + t4*m2 + t2*m8;
if IsZero(d) then
//gcd(h + 2*v1, u1) = (x + xp1)
@@ -1327,16 +1337,17 @@ Deg3DBL:= function(u2,u1,u0,v2,v1,v0,ccs)
kp1 := v0 + yn0 + v2*vn2 + yy2 + u2*k2;
kp0 := f3 + h2*v1 + v2*vh1 + u1*kp2 + u2*kp1;
- //Compute s = k*q mod u using Karatsuba twice
- t0 := kp0*m1;
- t1 := kp1*m4;
- t2 := kp2*m7;
- t3 := (m4 + m7)*(kp1 + kp2) + t2 + t1 + u2*t2;
- t4 := u1*t2;
- t5 := t4 + t1;
- sp0 := t0 + u0*t3;
- sp1 := (m1 + m4)*(kp0 + kp1) + sp0 + (u0 + u1)*(t2 + t3) + t5;
- sp2 := (m1 + m7)*(kp0 + kp2) + t0 + t2 + t5 + u2*t3;
+ // the three adjugate entries the determinant did not need
+ m4 := m8 + u2*m7;
+ m6 := m2 + u1*m8;
+ m3 := u0*m8;
+
+ //Compute s = k*q mod u as the matrix-vector product sp = M*kp. M is
+ //multiplication by q = d/w mod u, so this is the same map as kp*q mod u,
+ //and applying the matrix needs no reduction step.
+ sp0 := kp0*m1 + kp1*m2 + kp2*m3;
+ sp1 := kp0*m4 + kp1*m5 + kp2*m6;
+ sp2 := kp0*m7 + kp1*m8 + kp2*m9;
if IsZero(sp2) then
diff --git a/g3/splitModel/negReduced/g3Formulas/nch2_splitG3_ADD.mag b/g3/splitModel/negReduced/g3Formulas/nch2_splitG3_ADD.mag
index 3e5118a..a99554f 100644
--- a/g3/splitModel/negReduced/g3Formulas/nch2_splitG3_ADD.mag
+++ b/g3/splitModel/negReduced/g3Formulas/nch2_splitG3_ADD.mag
@@ -8099,13 +8099,26 @@ Deg3ADD:= function(u2,u1,u0,v2,v1,v0,up2,up1,up0,vp2,vp1,vp0,ccs)
t5 := t1 - up1*t7;
t8 := t4 - up2*t7;
//t3 := -up0*t8;
- t6 := t2 - up1*t8;
- t9 := t5 - up2*t8;
+ //t6 := t2 - up1*t8;
+ //t9 := t5 - up2*t8;
- m1 := t5*t9 - t6*t8;
- m4 := t6*t7 - t4*t9;
+ // Bottom row of the adjugate needs only columns 1 and 2 of T.
+ m9 := t1*t5 - t2*t4;
+ m8 := t2*t7 - t1*t8;
m7 := t4*t8 - t5*t7;
- d := t1*m1 + t2*m4 - up0*t8*m7; //Determinant of T = t1*m1 + t2*m4 + t3*m7
+
+ // Column 3 of T is x*(column 2) reduced mod up, so the remaining six
+ // adjugate entries are shifts of the bottom row with one multiplication
+ // each rather than a 2x2 minor. m3, m4 and m6 are the three the
+ // determinant does not read, so they wait until the generic path below.
+ m5 := m9 + up2*m8;
+ m2 := -up0*m7;
+ m1 := m5 + up1*m7;
+
+ // t7*m3 = t7*(-up0*m8) = (-up0*t7)*m8 = t2*m8, the signs cancelling because
+ // t2 and m3 carry the same -up0 factor. So the determinant needs m8, not m3,
+ // and the inlined -up0*t8*m7 it used to spend two multiplications on is gone.
+ d := t1*m1 + t4*m2 + t2*m8; //Determinant of T
if IsZero(d) then
//dw1 := u mod up; // = (a1^-1)*S for S,a1,b1 = XGCD(u,up);
@@ -9769,16 +9782,17 @@ Deg3ADD:= function(u2,u1,u0,v2,v1,v0,up2,up1,up0,vp2,vp1,vp0,ccs)
vt1:= vp1 - v1;
vt0:= vp0 - v0;
- //Compute s = vt*q mod u2 using Karatsuba twice
- t0 := vt0*m1;
- t1 := vt1*m4;
- t2 := vt2*m7;
- t3 := (m4 + m7)*(vt1 + vt2) - t2 - t1 - up2*t2;
- t4 := up1*t2;
- t5 := t4 - t1;
- sp0 := t0 - up0*t3;
- sp1 := (m1 + m4)*(vt0 + vt1) - sp0 - (up0 + up1)*(t2 + t3) + t5;
- sp2 := (m1 + m7)*(vt0 + vt2) - t0 - t2 - t5 - up2*t3;
+ // the three adjugate entries the determinant did not need
+ m4 := m8 + up2*m7;
+ m6 := m2 - up1*m8;
+ m3 := -up0*m8;
+
+ //Compute s = vt*q mod up as the matrix-vector product sp = M*vt. M is
+ //multiplication by q = d/u mod up, so this is the same map as vt*q mod up,
+ //and applying the matrix needs no reduction step.
+ sp0 := vt0*m1 + vt1*m2 + vt2*m3;
+ sp1 := vt0*m4 + vt1*m5 + vt2*m6;
+ sp2 := vt0*m7 + vt1*m8 + vt2*m9;
if IsZero(sp2) then
if IsZero(sp1) then
diff --git a/g3/splitModel/negReduced/g3Formulas/nch2_splitG3_DBL.mag b/g3/splitModel/negReduced/g3Formulas/nch2_splitG3_DBL.mag
index 6caf2ec..72748e4 100644
--- a/g3/splitModel/negReduced/g3Formulas/nch2_splitG3_DBL.mag
+++ b/g3/splitModel/negReduced/g3Formulas/nch2_splitG3_DBL.mag
@@ -1111,16 +1111,26 @@ Deg3DBL:= function(u2,u1,u0,v2,v1,v0,ccs)
t2 := -u0*t7;
t5 := t1 - u1*t7;
t8 := t4 - u2*t7;
- t3 := -u0*t8;
- t6 := t2 - u1*t8;
- t9 := t5 - u2*t8;
-
- //Determinant of T = t1*m1 + t2*m4 + t3*m7
- //First column of M corresponds to polynomial q = m7x^2 + m4x + m1 = d/u1 mod u2.
- m1 := t5*t9 - t6*t8;
- m4 := t6*t7 - t4*t9;
+ //t3 := -u0*t8;
+ //t6 := t2 - u1*t8;
+ //t9 := t5 - u2*t8;
+
+ // Bottom row of the adjugate needs only columns 1 and 2 of T.
+ m9 := t1*t5 - t2*t4;
+ m8 := t2*t7 - t1*t8;
m7 := t4*t8 - t5*t7;
- d := t1*m1 + t2*m4 + t3*m7;
+
+ // Column 3 of T is x*(column 2) reduced mod u, so the remaining six
+ // adjugate entries are shifts of the bottom row with one multiplication
+ // each rather than a 2x2 minor. m3, m4 and m6 are the three the
+ // determinant does not read, so they wait until the generic path below.
+ m5 := m9 + u2*m8;
+ m2 := -u0*m7;
+ m1 := m5 + u1*m7;
+
+ // t7*m3 = t7*(-u0*m8) = (-u0*t7)*m8 = t2*m8, the signs cancelling because
+ // t2 and m3 carry the same -u0 factor. So the determinant needs m8, not m3.
+ d := t1*m1 + t4*m2 + t2*m8;
if IsZero(d) then
//gcd(h + 2*v1, u1) = (x + xp1)
@@ -1368,16 +1378,17 @@ Deg3DBL:= function(u2,u1,u0,v2,v1,v0,ccs)
kp0 := f3 - v2*vh1 - u1*k2 - u2*k1 - t12 - t12;
- //Compute s = k*q mod u using Karatsuba twice
- t0 := kp0*m1;
- t1 := kp1*m4;
- t2 := kp2*m7;
- t3 := (m4 + m7)*(kp1 + kp2) - t2 - t1 - u2*t2;
- t4 := u1*t2;
- t5 := t4 - t1;
- sp0 := t0 - u0*t3;
- sp1 := (m1 + m4)*(kp0 + kp1) - sp0 - (u0 + u1)*(t2 + t3) + t5;
- sp2 := (m1 + m7)*(kp0 + kp2) - t0 - t2 - t5 - u2*t3;
+ // the three adjugate entries the determinant did not need
+ m4 := m8 + u2*m7;
+ m6 := m2 - u1*m8;
+ m3 := -u0*m8;
+
+ //Compute s = k*q mod u as the matrix-vector product sp = M*kp. M is
+ //multiplication by q = d/w mod u, so this is the same map as kp*q mod u,
+ //and applying the matrix needs no reduction step.
+ sp0 := kp0*m1 + kp1*m2 + kp2*m3;
+ sp1 := kp0*m4 + kp1*m5 + kp2*m6;
+ sp2 := kp0*m7 + kp1*m8 + kp2*m9;
if IsZero(sp2) then
k3 := T4 + T4;
diff --git a/verification/adjugate.py b/verification/adjugate.py
index aefb89d..ab85d0b 100644
--- a/verification/adjugate.py
+++ b/verification/adjugate.py
@@ -1032,13 +1032,17 @@ def shipped_9(t1, t4, t7, up0, up1, up2):
m9=m9, d=d)
-def split_q(t1, t4, t7, up0, up1, up2):
- """arb_splitG3_ADD.mag, `t1 := u0 - up0;` .. `d := t1*m1 + t2*m4 + t3*m7;`.
+def split_q_col1(t1, t4, t7, up0, up1, up2):
+ """The split route as shipped BEFORE C4: `d := t1*m1 + t2*m4 + t3*m7;`.
The whole first column, and d.
- The split model wants only column 1 of the adjugate (it is the coefficient
- vector of q = d/w mod up), and pays for all nine T entries to get it.
+ It wants only column 1 of the adjugate (the coefficient vector of
+ q = d/w mod up) and pays for all nine T entries to get it. Kept as a
+ candidate rather than deleted: it is a valid route and the comparison should
+ keep scoring it, and it is the thing C4 measured itself against. Note it
+ costs the SAME 15M 0S 9A as the shift route below -- the block conversion
+ alone wins nothing, which is why C4's unit of work is the generic path.
"""
t2 = -(up0 * t7)
t5 = t1 - up1 * t7
@@ -1053,6 +1057,32 @@ def split_q(t1, t4, t7, up0, up1, up2):
return dict(m1=m1, m4=m4, m7=m7, d=d)
+def split_q(t1, t4, t7, up0, up1, up2):
+ """arb_splitG3_ADD.mag, `t2 := -up0*t7;` .. `d := t1*m1 + t4*m2 + t2*m8;`.
+
+ The split route as shipped SINCE C4, and textually the ramified block with
+ `up` for `u`: bottom row of the adjugate from columns 1 and 2 of T, then six
+ shifts at one multiplication each, then the column expansion that reads m8
+ rather than m3 because t7*m3 = t2*m8.
+
+ Column 3 of T is x*(column 2) reduced mod up, which is what makes the six
+ remaining entries shifts rather than 2x2 minors. m3, m4 and m6 are the three
+ the determinant does not read, so the file defers them to the generic path
+ and no degenerate leaf pays for them.
+ """
+ t2 = -(up0 * t7)
+ t5 = t1 - up1 * t7
+ t8 = t4 - up2 * t7
+ m9 = t1 * t5 - t2 * t4
+ m8 = t2 * t7 - t1 * t8
+ m7 = t4 * t8 - t5 * t7
+ m5 = m9 + up2 * m8
+ m2 = -(up0 * m7)
+ m1 = m5 + up1 * m7
+ d = t1 * m1 + t4 * m2 + t2 * m8
+ return dict(m1=m1, m2=m2, m5=m5, m7=m7, m8=m8, m9=m9, d=d)
+
+
def _rank5_row3(t1, t4, t7, up0, up1, up2):
"""The five-product bottom row, and column 2 of T. Shared by the variants.
@@ -1175,6 +1205,7 @@ def __init__(self, name, fn, nargs, expect=None, annot=None, group="entries"):
("arb_ramifiedG3_DBL.mag", "block")),
Candidate("shipped_9", shipped_9, 6, (18, 0, 11)),
Candidate("split_q", split_q, 6, (15, 0, 9)),
+ Candidate("split_q_col1", split_q_col1, 6, (15, 0, 9)),
Candidate("rank5_7_d", rank5_7_d, 6, (15, 0, 14)),
Candidate("rank5_7", rank5_7, 6, (12, 0, 12)),
Candidate("rank5_9_d", rank5_9_d, 6, (17, 0, 16)),
@@ -1932,7 +1963,7 @@ def section_bound(primes=(2, 3, 5, 7, 11), verbose=False):
"t2 := -u0*t7;", "d := t1*m1 + t4*m2 + t2*m8;",
{"u0": "up0", "u1": "up1", "u2": "up2"}),
("split_q", "g3/splitModel/negReduced/g3Formulas/arb_splitG3_ADD.mag",
- "t2 := -up0*t7;", "d := t1*m1 + t2*m4 + t3*m7;", {}),
+ "t2 := -up0*t7;", "d := t1*m1 + t4*m2 + t2*m8;", {}),
)
# The three `t1 = u0 - up0` differences are deliberately outside every anchor
diff --git a/verification/selftest.py b/verification/selftest.py
index 11a12e5..3ae6d33 100644
--- a/verification/selftest.py
+++ b/verification/selftest.py
@@ -1874,9 +1874,20 @@ def section_split_counts(rep, quick):
# every tuple a published cell and every delta measured.
PINS = [
("splitneg/g3/arb", 31, {
- "33ADD n=0,0": ((65, 3, 87, 12, 1), 2), # Degree 3
+ # SUPERSEDED BY C4, not a disagreement with the thesis. The published
+ # cell is (65, 3, 87, 12, 1); the formula changed under it, trading one
+ # multiplication for twelve additions by carrying the full adjugate and
+ # applying it as a matrix-vector product. Hand-counted and measured
+ # independently, both giving +1M -12A. `Thesis/ERRATA.md` records the
+ # divergence; nothing here says the thesis was wrong.
+ "33ADD n=0,0": ((66, 3, 75, 12, 1), 2), # Degree 3, post-C4
"13ADD n=1,0": ((22, 2, 43, 7, 1), 2), # Degree 1 and 3, Up Adjust
- "3DBL n=0": ((73, 3, 101, 19, 1), 2), # Degree 3
+ # SUPERSEDED BY C4, same trade as 33ADD above and for the same
+ # reason. Published cell (73, 3, 101, 19, 1). Hand-counted and
+ # measured independently, both giving +1M -12A: the generic path's
+ # s computation goes 11M 20A -> 12M 8A while the T-block holds at
+ # 15M 9A. `Thesis/ERRATA.md` records the divergence.
+ "3DBL n=0": ((74, 3, 89, 19, 1), 2), # Degree 3, post-C4
"1DBL n=1": ((7, 1, 19, 6, 1), 2), # Degree 1
"1DBL n=2": ((14, 3, 25, 8, 1), 2), # Degree 1 with Down Adjust
}),