Rendered from rocq-plan.md (raw file). Formalization prep, written before the domain fix in proof.md; its 3-sweep plan (§3.5) was not used.
SOMDCv6.v lines 1–17)1.2 Module TM7 (lines 1627–1870), item by item2. Standalone TM7 copy (template validation)3. What changes for #23.1 Measured: what breaks when only tm changes3.2 New relation Inc and Inc_spec (segment facts verified)3.3 The two RIncs clauses that change3.4 RIncs_spec: the two new bullets3.5 The domain argument (main risk)3.6 BigStep and nonhalt3.7 Size estimate4. Tactics to reuse, and pitfalls5. FilesMachine #2 = 1RB0RF_1LC0RE_0LC0LD_1RD1LB_0RF1RA_0RB---. Template: vendor/busycoq/verify/SOMDCv6.v, Module TM7, whose machine differs from #2 only in A1 (TM7 0RC, #2 0RF). Written 2026-10-10. This is preparation only. No proof of #2 has been written; the only Rocq for #2 is the segment probe in §3.2.
Closed under the global context, and tools/check_kill.sh gives RESULT: PASS on it in 5 s (coqchk passes in 0.8 s).
RIncs_spec: an eater entering an L, and a drain pass entering an L. BigStep, the start-up from blank, and all non-L cases go through unchanged.
h visit (enter as B, leave as C), and both L-entry segments emit h ++ h' ++ h^^(1+k) to the right. All 8 probe lemmas compile in 6.5 s (rocq-scratch/probe_segments_2.v).
P r := exists r', RIncs hx 0 r r' ("r is in the domain"). For #2 that set is not closed: LLLL10LL1L1 is in the domain, but its image LLLLL1LLLLLLL111 is not.
image(eat) ∩ dom(eat) is closed in every test so far: exhaustive over inputs of length ≤ 12, plus about 62k random inputs of length ≤ 40.
.v work.
SOMDCv6.v lines 1–17) #| Item | Statement / body | Role |
|---|---|---|
| imports | From BusyCoq Require Import Individual62 Longitudinal DivModCases., then ZifyNat Lia ZArith String List | Individual62 provides the machine and the segment machinery. Longitudinal provides segRLs_addmul_v2 and sideRLs_1. DivModCases provides mod2 and sub. All three are on check_kill's allowlist and built (.vo + native). |
Ltac esc | tries BoundedConfig.segRLs_c_spec (T:=10^3), then BoundedConfig.sideRLs_c_spec (T:=10^3), then sideRLs_c_spec (T:=10^6) with vm_compute | Proves a concrete segRLs/sideRLs goal by computation. Used for the right-end base case and inside am. |
Ltac ec | econstructor | Builds RIncs derivations. |
Ltac am a a' k b b' | applys_eq (segRLs_addmul_v2 a a' k b b'); unfold DH0; flia; esc | Proves segRLs tm (h^^(k*a+b)) (h^^(k*a'+b')) w1 w2 from two concrete facts: b visits give b' out, and every further a visits give a' out. Used for the binary-counter digits (a 0-digit absorbing 1+2k or 2+2k passes). flia comes from the import chain. The header does not define it, but it resolves. |
Framework pieces TM7 relies on (from Individual.v / Individual62.v / Longitudinal.v):
sideRLs tm ls r1 r2. The right half of the tape, initially r1, receives the visits in ls from the left and ends as r2. Each visit (h1,h2) enters moving right in state/prefix h1 and leaves moving left as h2.
segRLs tm ls1 ls2 w1 w2. A finite word w1 (becoming w2) turns incoming visits ls1 into outgoing visits ls2 to its right.
segRLs_sideRLs_concat glues a segment onto a side.
segRLs_trans, sideRLs_trans concatenate visit lists.
segRLs_wall''. segRLs h1 h2 w w -> segRLs (h1^^n) (h2^^n) w w.
sideRLs_1 turns a one-visit sideRLs into -->+.
esx dispatches to solve_segRLs, solve_sideRLs, solve_init or es by goal shape.
follow10.
Module TM7 (lines 1627–1870), item by item #| Item | Statement | Role |
|---|---|---|
tm | Eval compute in (TM_from_str "1RB0RC_…") | The machine. This is the only line that names A1. |
| notations | ld:=[1;1;0] (L), d0:=[0;0;0], d1:=[1;0;0]; hR:=(B,[]), hR':=(D,[1]), hL:=(C,[]); h:=[(hR,hL)], h':=[(hR',hL)] | Digits, and the two visit kinds. h is the drain-pass visit and, for #2, also the +1 pass. h' is the eater (state D with a 1 on its left). |
RD, H | LD \| D0 \| D1; hx \| h1 | Digit names. hx is the eater; h1 is count mode, which has a stray 1 in front, so the alignment shifts by one cell. |
toRC | list RD -> side, digits then 0inf | Converts the digit list to the tape side. |
toH', toH | toH' hx=[], toH' h1=[1]; toH hx=h', toH h1=[] | The extra cell in front of the side, and the first visit of the mode. |
RIncs | 10 clauses, H -> nat -> list RD -> list RD -> Prop (mode, pass count k, input, output) | The sweep as a relation. hx_ld, hx_d0: eat, k+1. hx_d1: switch to h1 with k. h1_ld: back to hx with the same k. h1_d1: absorb one pass. h1_d0_0/1: halve. h1_rh_0/1/2: write k at the right end (bijective binary followed by a terminating D1). |
cat3, tr1, wal, tr2 | Ltacs | cat3 peels a 3-cell digit off the side. tr1 splits h^^(1+k) and proves the first visit by esx. wal handles k identical visits through a wall. tr2 handles a leading sideRLs step. |
RIncs_spec | RIncs tp k r r' -> sideRLs tm (toH tp++h^^k) (toH' tp*>toRC r) (toRC r') | Soundness: the relation is what the machine does. Proved by induction on the derivation, one bullet per clause. |
RIncs_nxt_1 | exists r, RIncs h1 k [] r | The right end always succeeds in count mode. Proved by well-founded induction on k with mod2. |
solve_v2, solve_v1 | Ltacs | Automation tailored to the case list of RIncs_nxt. |
RIncs_nxt | RIncs tp k r r' -> cond(tp,k,tp',k') -> exists r'', RIncs tp' k' r' r'', where cond is: hx,hx: k<=k'; h1,h1: 2k+1<=k'; h1,hx: k+1<=k'; otherwise False | The domain lemma: the output of a sweep is again in the domain. Proved by induction on r, with a nested lt_wf_ind on k for []. It does not mention tm. |
lh, S' | lh := 0inf<*<[1]; S' r := lh {{{(hR',R)}}} toH' hx *> toRC r | The boundary configuration 0^inf 1 1 D> r. |
BigStep | RIncs hx 0 r r' -> S' r -->+ S' r' | One sweep: RIncs_spec, then sideRLs_1 and follow10, then es steps back from the left wall. |
nonhalt | ~halts tm c0 | multistep_nonhalt to S' [D1] (esx, 5 steps), then progress_nonhalt_cond with P r := exists r', RIncs hx 0 r r', closed by RIncs_nxt (hx,0)->(hx,0). |
TM8 (line 1874, A1 = 0LC) reuses the same proof text.
rocq-scratch/TM7_toplevel.v, 261 lines, sha256 642086b1…20e59b5.
{ sed -n '1,17p' SOMDCv6.v; sed -n '1627,1870p' SOMDCv6.v; } > TM7_toplevel.v, i.e. the header plus the module body without its Module TM7./End TM7. lines. The body is byte-identical to upstream (checked by diff).
vendor/: coqc -Q vendor/busycoq/verify BusyCoq -Q vendor/busycoq/verify/BigInt BigInt -native-compiler yes, run under ulimit -s unlimited.
Print Assumptions nonhalt. gives Closed under the global context.
tools/check_kill.sh rocq-scratch/TM7_toplevel.v 1RB0RC_1LC0RE_0LC0LD_1RD1LB_0RF1RA_0RB--- gives RESULT: PASS in 5.0 s total (compile 3.1 s, coqchk pass in 0.8 s, 0 assumptions). This validates the route end to end: imports allowlisted, framework fresh, top-level tm/nonhalt, and no forbidden words in upstream's text.
tm changes #I swapped the string in the TM7 copy and compiled it (scratchpad only, not kept):
RIncs_spec, bullet 1 (RIncs_hx_ld). The eater's visit over ld now emits a different visit list.
RIncs_spec, RIncs_nxt_1, RIncs_nxt, BigStep, and the c0 -->* S' [D1] start.
es/ind lemmas. Every low-level fact goes through the segment automation (esx/esc/am/wal), so no other low-level lemma touches A1.
Inc and Inc_spec (segment facts verified) #Inductive Inc: list RD -> list RD -> Prop := | Inc_nil: Inc [] [D1] | Inc_d0 r: Inc (D0::r) (D1::r) | Inc_d1 r r': Inc r r' -> Inc (D1::r) (D0::r') | Inc_ld r r': Inc r r' -> Inc (LD::r) (LD::r'). Lemma Inc_spec r r': Inc r r' -> sideRLs tm h (toRC r) (toRC r').
Each case is cat3 plus one segment fact. The segment facts are proved in rocq-scratch/probe_segments_2.v (compiles in 6.5 s):
segRLs tm h [] d0 d1, by esx;
segRLs tm h h d1 d0, by esx;
segRLs tm h h ld ld, by esx;
sideRLs tm h 0inf (d1 *> 0inf), by esc.
Planned helper lemmas:
Inc_total: forall r, exists r', Inc r r'.
Inc_fun: Inc is deterministic.
Inc_D1: the output always contains D1.
RIncs clauses that change #| RIncs_hx_ld k r r1 r': Inc r r1 -> RIncs hx (1+k) r1 r' -> RIncs hx k (LD::r) (LD::r') | RIncs_h1_ld k r r1 r': Inc r r1 -> RIncs hx (1+k) r1 r' -> RIncs h1 (1+k) (LD::r) (LD::r')
The other 8 clauses are TM7's, verbatim. Simulation agrees with this relation:
rocq-scratch/py/rincs.py mirrors these exact clauses;
out(hx,0,·) equals check_rules.eat on all 29,523 words of length ≤ 9;
check_rules.py matches 208/208 real sweeps.
RIncs_spec: the two new bullets #Segment facts, verified in the probe:
segRLs tm (h' ++ h^^k) (h ++ h' ++ h^^(1+k)) ld ld. TM7's is h' ++ h^^(1+k).
segRLs tm (h^^1 ++ h^^k) (h ++ h' ++ h^^(1+k)) [1;1;1;0] ld.
Both proofs are:
rewrite lpow_add;
change (h ++ h' ++ h^^1 ++ h^^k) with ((h ++ h' ++ h^^1) ++ h^^k);
eapply segRLs_trans; [esx | wal].
Bullet shape: eapply segRLs_sideRLs_concat with the segment fact, then eapply (sideRLs_trans (ls1:=h)); [apply Inc_spec; eauto | eauto].
TM7's cat3 ends in [|eauto 1], which cannot find the sideRLs_trans split, so these bullets are written out (about 4 lines each).
TM7's invariant fails for #2. Script: rocq-scratch/py/domain_facts.py.
LLLL10LL1L1 → LLLLL1LLLLLLL111. On the second word, the eater reaches the tail 10001 with k = 13. The 1 becomes L, and the drain of 0001 then needs 15 passes but has 12.
So RIncs_nxt has no #2 analogue with any condition on (tp, k, tp', k'). I derived this by hand and the measurements back it: the constraints forced on a bound g(k) are contradictory (g(k+2) ≤ g(k)+1 versus g(k) ≥ k−1).
What holds instead (tested, not proved):
RIncs_Inc: RIncs tp k r r' -> Inc r' r'' -> RIncs tp (1+k) r r''. In words, a +1 applied to a sweep's output is the same as one more pass in that sweep.
D0=1, D1=2, terminated by D1), and Inc is exactly bijective +1 on that code.
RIncs derivation, inverting Inc. Every case closes with one constructor and the IH; the D0 cases need mod2 arithmetic. Estimate 40–60 lines.
image(eat) ∩ dom(eat) is closed under eat.
S(1) is an image.
Proposed proof shape:
P r := (exists r0, RIncs hx 0 r0 r) /\ (exists r', RIncs hx 0 r r').
S' [LD;D1], reached at step 16 from blank (by simulation; esx should do it as for TM7). P [LD;D1] holds via r0 = [D1], with r' = [LD;LD;LD;D1;D1], which matches the step-91 config.
RIncs tpA a s r -> RIncs tpB b r r' -> Cond(tpA,a,tpB,b,tpC,c) -> exists r'', RIncs tpC c r' r''. It describes three sweeps A, B, C side by side, digit by digit.
a+1, by RIncs_Inc together with Inc_fun and RIncs determinism. A +1 fired by C becomes b+1.
r, e.g. length r + 2*(count of LD in r). This strictly drops every step even after B's Inc. TM7's structural induction on r fails here, because the sub-derivation runs on Inc r.
Cond. The harness rocq-scratch/py/joint.py replays the three sweeps jointly; it agrees with direct composition on 1678/1678 samples. Its data, over all inputs of length ≤ 11 where A and B succeed (71,695 inputs, C always succeeds):
xxx, 1xx, 11x, 111 occur (A's mode ≥ B's ≥ C's);
b >= a and 2c >= b hold at every position;
111 occurs only after A has finished (a = 0).
2c >= b nor a TM7-style pairwise condition is inductive on its own. Hand-checking the 111 transitions breaks both, so Cond must use a and the phase (A still reading, at the right end, or finished). Finding it is the first job of proof.md.
rocq-scratch/py/margin.py.
eat.
…1 L^a 111 pattern of the counterexample above, at larger sizes.
hx count k is [0,9,1,10,6,11,7,12,8] for k = 0..8: not monotone, and parity-driven (margin_cond.py).
BigStep and nonhalt #BigStep is unchanged, verified by compile in §3.1.
nonhalt changes only in P, the start (S' [LD;D1]) and the closing step. Three facts close it: the new key lemma, RIncs_Inc, and the witness r itself for the image half of P r'.
| Piece | Lines | Risk |
|---|---|---|
TM7 text kept (notations, toRC, 8 RIncs clauses, 8 RIncs_spec bullets, RIncs_nxt_1, BigStep) | ~150 | none (compiles today) |
Inc, Inc_spec, Inc_total, Inc_fun | 40–60 | low (segments verified) |
2 new RIncs_spec bullets | ~10 | low (verified) |
RIncs determinism, RIncs_Inc | 60–90 | low–medium |
3-sweep domain lemma (replaces RIncs_nxt) | 150–400 | high: invariant unknown |
nonhalt | ~25 | low |
| Total | ~450–700 | Compile should stay under 30 s: the computation is local segments only. |
Reuse:
cat3 / tr1 / wal / tr2 / am / esc unchanged.
rewrite lpow_add + change (… ++ h^^k) + segRLs_trans; [esx | wal] pattern for any visit list of the form "fixed prefix, then k copies".
mod2 / sub from DivModCases for parity and predecessor case splits, as in TM7's RIncs_nxt.
gen + inverts (LibTactics) for derivation inversion.
Pitfalls (each hit while probing):
Open Scope nat turns list literals into list nat. [1;1;1;0] fails as a list Sym. Declare word notations (e.g. Notation w1ld := [1;1;1;0].) before Open Scope nat, as TM7 does for ld/d0/d1.
0inf, not const 0, for the blank side. const 0 is read as Stream nat.
cat3's [|eauto 1] will not combine Inc_spec with the IH. Write the sideRLs_trans split explicitly.
solve_v1/solve_v2 are tied to its case order. The new domain lemma needs its own automation.
Inc premise. That is fine for RIncs_spec. For the domain lemma, use the measure induction described in §3.5.
ulimit change in practice; it is set anyway.
esc's vm_compute fallback (T:=10^6) is never reached by the probes; BoundedConfig with T:=10^3 suffices.
rocq-scratch/TM7_toplevel.v: standalone TM7 (check_kill PASS with TM7's string).
rocq-scratch/probe_segments_2.v: #2 segment probes (8 lemmas, all Qed).
rocq-scratch/py/rincs.py: #2's RIncs as a function. It checks equality with check_rules.eat and the commutation lemma.
rocq-scratch/py/domain_facts.py: the closure counterexample and the image∩dom closure test.
rocq-scratch/py/joint.py: the 3-sweep joint harness, for testing candidate Conds.
rocq-scratch/py/margin.py, margin_cond.py: the ruled-out fixed-margin invariants (§3.5).
Reproduce the data: cd rocq-scratch/py, then run:
python3 -B rincs.py (about 1 min);
python3 -B domain_facts.py 10 (about 2 min);
python3 -B joint.py (about 5 min);
python3 -u -B margin.py 2 11 40000 300 (about 15 min).