/machines/1RB0RD_0RC0LB_0RD1RD_1LE1RA_1RC1LF_---1LA/check_proof.py · raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
#!/usr/bin/env python3
"""check_proof.py for W5-8c = 1RB0RD_0RC0LB_0RD1RD_1LE1RA_1RC1LF_---1LA (740 line 636); proof.md in this directory.

proof.md is a delta proof from the parent W5-8 = 1RB1LD_0RC1RC_1LA1RE_---1LE_1RF0RC_0RB0LF (740 line 214, killed):
  * the relation: this table is the parent's table with states renamed by
    sigma = A->E, B->C, C->D, D->F, E->A, F->B (no mirror); it starts in sigma(E), so its run is sigma of the
    parent's table run from 0^inf E> 0^inf (Lemma R), on the same tape;
  * the opening: 6,950,830 direct steps from blank to K(2583,70,76,43,18) (head state sigma(F) = B), then 18 macro
    steps by the parent's rules (S^8 T6 S^7 TE S^6 T6 S^13 T8 S^10 TO S^18 T8 S^14 T6 S^23 TE S^17 T6) to
    Even(13299,220) at step 163,638,058;
  * from there the parent's double cycle Even(n,x) --> Even(n+12x+808, x+32) runs forever (via Lemma R).

Sections (proof.md section in brackets):
  T    [header]  the string; line 636 of the 740 list byte for byte and exactly once (parent: line 214); only F0
                 undefined, F entered only by E1; sheet rows (class only, no claim); the only Discord file that
                 lists it is mxdys's UBRRBA to-do list; busycoq index: absent; upstream tree (if present): none of the
                 1,440 variants of the table
  REL  [1]   sigma maps the parent's table onto this one (12/12 cells, no mirror), the only map of 720 x 2; same
             canonical form; start = sigma(E); lockstep with the parent's table from E (Python, every step);
             tapes equal after 163,638,058 steps (C runner)
  OPEN [2]   blank --(6,950,830)--> K(2583,70,76,43,18) for this machine (state B) and the parent's table from E
             (state F); the 18 rows: every application in its rule's domain, the step sums; the K-shaped left records
             of both runs up to 163,638,058 (C scanner, any state) = exactly the predicted points (start, every single
             application, end), the C scanner agreeing with a regex scan; no K-shaped record before 6,950,830
             (tools/sim.py events); every row by direct simulation; one unbroken run to Even(13299,220)
  INV  [3]   Even(13299,220) is in the domain of the double cycle; the orbit's first members
  DC   [4]   whole double cycles of this machine from sigma Even(n,x) (x = 52..252), exact configuration and steps;
             the orbit from step 163,638,058: every predicted even and odd run start by direct simulation (chained,
             and one unbroken run from blank), to 2e9 steps (5e8 --quick, 1e10 --full); every K-shaped left record
             of this machine in that range = the next point predicted by S, TE, TO
  PLAN [6]   the half-length start / end and the 18 follow arguments against the parent's lemma statements (read
             from the parent's kill file)
  REM  [5]   the three list entries of the table (lines, starts, mirror flags) and the Even steps of lines 214, 418;
             starts B and F reach the blank start-C configuration after 1 and 2 steps
  PAR  [4]   the parent's own check_proof.py (same mode) exits 0 with every check PASS (its rules S, TE, TO, T6, T8
             by symbolic execution, the double cycle, the halting cell); skipped with --no-parent
  HALT [4]   no halt of this machine in its first 163,638,058 steps; sigma(D) = F, D0 / F0 the only undefined cells
  DOC  [*]   proof.md quotes the relation table, sigma, the opening rows, the domain rows and bounds, the chain and the
             follow arguments as checked; proof.md before section 7 passes tools/writeup_lint.py with 0 errors
Usage: python3 -B check_proof.py [--seed S] [--quick|--full] [--only T,REL,...] [--no-parent]
"""
import importlib.util
import itertools
import os
import random
import re
import subprocess
import sys
import time

HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.abspath(os.path.join(HERE, '..', '..'))
TM = '1RB0RD_0RC0LB_0RD1RD_1LE1RA_1RC1LF_---1LA'
PARENT = '1RB1LD_0RC1RC_1LA1RE_---1LE_1RF0RC_0RB0LF'
SIB418 = '1RB1LE_1LC1RD_0LA1LA_---1RE_1LF0LA_0LC0RF'
PDIR = os.path.join(ROOT, 'machines', PARENT)
UPSTREAM = '/tmp/claude-1000/-home-dennis/dbf3e2a9-21e7-4eb2-b481-38a0113aca31/scratchpad/pr3/busycoq/verify'

SEED = int(sys.argv[sys.argv.index('--seed') + 1]) if '--seed' in sys.argv else 1
QUICK = '--quick' in sys.argv
FULL = '--full' in sys.argv
ONLY = set(sys.argv[sys.argv.index('--only') + 1].split(',')) if '--only' in sys.argv else None
NO_PARENT = '--no-parent' in sys.argv
RNG = random.Random(SEED)
RES, ORDER, INFO = {}, [], {}
T0 = time.time()

# the parent's check module: its rules (domains, updates, step formulas), its K word and its executor's table parser
_spec = importlib.util.spec_from_file_location('w58_check', os.path.join(PDIR, 'check_proof.py'))
P = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(P)
sx = P.sx
VARS = P.VARS
RULES = P.RULES
G = P.G
PTABLE = sx.parse_table(PARENT)
TABLE = sx.parse_table(TM)

SIGMA = {'A': 'E', 'B': 'C', 'C': 'D', 'D': 'F', 'E': 'A', 'F': 'B'}     # parent state -> this machine's state
KSTATE = SIGMA['F']                                                       # = 'B'

OPEN_STEP = 6950830
OPEN_START = (OPEN_STEP, {'n': 2583, 'a': 70, 'b': 76, 'c': 43, 'd': 18})
OPEN_CHAIN = [('S', 8), ('T6', 1), ('S', 7), ('TE', 1), ('S', 6), ('T6', 1), ('S', 13), ('T8', 1), ('S', 10), ('TO', 1),
              ('S', 18), ('T8', 1), ('S', 14), ('T6', 1), ('S', 23), ('TE', 1), ('S', 17), ('T6', 1)]
CHAIN_STR = 'S^8 T6 S^7 TE S^6 T6 S^13 T8 S^10 TO S^18 T8 S^14 T6 S^23 TE S^17 T6'
END = (163638058, (13299, 220))


def rec(cid, ok, info=None):
    if cid not in RES:
        RES[cid] = {'n': 0, 'pass': 0, 'fail': []}
        ORDER.append(cid)
    r = RES[cid]
    r['n'] += 1
    r['pass'] += bool(ok)
    if not ok and len(r['fail']) < 3:
        r['fail'].append(str(info)[:300])


def scale(q, d, f):
    return q if QUICK else (f if FULL else d)


# ------------------------------------------------------------------ C tools: batch runner and K scanner
KRUN = os.path.join(HERE, 'work', 'krun')
KSCAN = os.path.join(HERE, 'work', 'kscan')


def build():
    for exe, src in ((KRUN, 'krun.c'), (KSCAN, 'kscan.c')):
        src = os.path.join(HERE, 'scripts', src)
        if not os.path.exists(exe) or os.path.getmtime(exe) < os.path.getmtime(src):
            os.makedirs(os.path.dirname(exe), exist_ok=True)
            subprocess.run(['gcc', '-O2', '-o', exe, src], check=True)


def krun(tm, side, jobs):
    """jobs: (state, headidx, nrec, maxsteps, tape) -> (kind, steps, state, headoff, word); records on side L/R"""
    if not jobs:
        return []
    inp = ''.join('%s %d %d %d %s\n' % j for j in jobs)
    out = subprocess.run(['nice', '-n', '10', KRUN, tm, side], input=inp, capture_output=True, text=True,
                         check=True).stdout.split('\n')
    res = []
    for line in out:
        if not line:
            continue
        k, s, q, h, w = line.split()
        res.append((k, int(s), q, int(h), w))
    assert len(res) == len(jobs)
    return res


def kscan(tm, start, fstate, frm, maxs):
    """K-shaped left records: list of (step, state, val); and the END line"""
    out = subprocess.run(['nice', '-n', '10', KSCAN, tm, start, fstate, str(frm), str(maxs)], capture_output=True,
                         text=True, check=True).stdout.split('\n')
    pts, end = [], None
    for line in out:
        p = line.split()
        if len(p) == 7:
            pts.append((int(p[0]), p[1], dict(zip(VARS, map(int, p[2:])))))
        elif p:
            end = p
    return pts, end


# ------------------------------------------------------------------ the family K
def Kw(val):
    """the parent's K word (head = cell 0)"""
    return P.Kval(val)


def is_K(res, val, state, kind='MAX'):
    """res of a run ends at K(val) with the head in `state`: nothing but 0 left of the head, the word from the head on
    = the K word (trailing 0s ignored)"""
    k, s, q, h, w = res
    return k == kind and q == state and '1' not in w[:h] and w[h:].rstrip('0') == Kw(val).rstrip('0')


def even(n, x):
    return P.even(n, x)


def Kfmt(v):
    return 'K(%s)' % ','.join(str(v[x]) for x in VARS)


def open_points():
    """(row, rule, k, start step, start val, end step, end val, ok, singles) for the 18 rows; singles = the
    (step, val) after every single application"""
    step, val = OPEN_START
    rows = []
    for i, (r, k) in enumerate(OPEN_CHAIN, 1):
        s0, v0, ok, singles = step, dict(val), True, []
        for _ in range(k):
            ok &= P.in_dom(RULES[r]['dom'], val)
            step += P.rule_steps(r, val)
            val = P.apply_rule(r, val)
            singles.append((step, dict(val)))
        rows.append((i, r, k, s0, v0, step, dict(val), ok, singles))
    return rows


# ================================================================== T
def check_T():
    rec('T.machine_string', TM == '1RB0RD_0RC0LB_0RD1RD_1LE1RA_1RC1LF_---1LA')
    raw = open(os.path.join(ROOT, 'data', 'holdouts', 'BB6_holdouts_740.txt'), 'rb').read()
    lines = raw.split(b'\n')
    rec('T.holdout_740_line636_byte_exact', lines[635] == TM.encode(), lines[635])
    rec('T.holdout_740_exactly_once', lines.count(TM.encode()) == 1, lines.count(TM.encode()))
    rec('T.holdout_740_no_CR', b'\r' not in raw)
    rec('T.parent_is_740_line214', lines[213] == PARENT.encode(), lines[213])
    und = [k for k, v in TABLE.items() if v is None]
    rec('T.only_F0_undefined', und == [('F', 0)], und)
    into_F = [k for k, v in TABLE.items() if v and v[2] == 'F']
    rec('T.F_entered_only_by_E1', into_F == [('E', 1)], into_F)
    import csv
    sheet = os.path.join(ROOT, 'data', 'sheet')
    rows = [r for r in csv.reader(open(os.path.join(sheet, '2026-10-09_results.csv'))) if r and r[0] == TM]
    rec('T.sheet_results_row_class_only', len(rows) == 1 and rows[0][2] == 'Bouncer + Chaotic Edge' and
        not any(x.strip() for i, x in enumerate(rows[0]) if i not in (0, 2)), rows)
    rows = [r for r in csv.reader(open(os.path.join(sheet, '2026-10-09_level-0.csv'))) if r and r[0] == TM]
    rec('T.sheet_level0_row_no_status', len(rows) == 1 and rows[0][1] == '' and rows[0][2] in ('', '-') and
        not any(x.strip() for x in rows[0][3:]), rows)
    cl = list(csv.reader(open(os.path.join(sheet, '2026-10-09_classifications.csv'))))
    cols = sorted({cl[0][i] for r in cl[1:] for i, x in enumerate(r) if x == TM})
    rec('T.sheet_classifications_only_class_column', cols == ['Bouncer + Chaotic Edge'], cols)
    files = sorted(f for f in os.listdir(sheet) if f.endswith('.csv') and TM in open(os.path.join(sheet, f)).read())
    rec('T.sheet_files_naming_it', files == ['2026-10-09_classifications.csv', '2026-10-09_level-0.csv',
                                             '2026-10-09_results.csv'], files)
    disc = os.path.join(ROOT, 'data', 'discord')
    files = sorted(f for f in os.listdir(disc) if TM in open(os.path.join(disc, f)).read())
    rec('T.discord_only_in_UBRRBA_todo', files == ['2026-08-24_mxdys_ubrrba_86.txt'], files)
    r = subprocess.run([sys.executable, '-B', os.path.join(ROOT, 'tools', 'holdouts.py'), 'solved', TM],
                       capture_output=True, text=True)
    rec('T.busycoq_index_absent', r.returncode != 0 and 'absent from verify/' in r.stdout and
        'equivalent (L/R mirror and/or renamed states): none' in r.stdout, r.stdout[-300:])
    if os.path.isdir(UPSTREAM):
        variants = all_variants(PTABLE)
        hits = []
        for dp, _, fs in os.walk(UPSTREAM):
            for f in fs:
                if f.endswith('.v'):
                    t = open(os.path.join(dp, f), errors='ignore').read()
                    hits += [(f, v) for v in variants if v in t]
        rec('T.upstream_tree_has_no_variant_of_the_table', not hits and len(variants) == 1440 and TM in variants,
            hits[:3])
    else:
        INFO['upstream'] = 'upstream tree not present; T.upstream check skipped'


# ================================================================== REL
def emit(T):
    out = []
    for s in 'ABCDEF':
        row = ''
        for b in (0, 1):
            t = T[(s, b)]
            row += '---' if t is None else '%d%s%s' % (t[0], 'R' if t[1] == 1 else 'L', t[2])
        out.append(row)
    return '_'.join(out)


def image(T, perm, mirror):
    """the table T with states renamed by perm (dict) and, if mirror, L and R swapped"""
    U = {}
    for (s, b), t in T.items():
        U[(perm[s], b)] = None if t is None else (t[0], -t[1] if mirror else t[1], perm[t[2]])
    return emit(U)


def canon(T, start):
    """the least string over all renamings that send `start` to A, with and without the mirror"""
    rest = [q for q in 'ABCDEF' if q != start]
    return min(image(T, dict(zip([start] + rest, ('A',) + p)), m)
               for p in itertools.permutations('BCDEF') for m in (False, True))


def all_variants(T):
    out = set()
    for p in itertools.permutations('ABCDEF'):
        perm = dict(zip('ABCDEF', p))
        for m in (False, True):
            out.add(image(T, perm, m))
    return out


def check_REL():
    rec('REL.sigma_maps_parent_onto_this_no_mirror', image(PTABLE, SIGMA, False) == TM, image(PTABLE, SIGMA, False))
    cells = 0
    for (s, b), t in PTABLE.items():
        u = TABLE[(SIGMA[s], b)]
        cells += (t is None and u is None) or (t is not None and u == (t[0], t[1], SIGMA[t[2]]))
    rec('REL.cells_12_of_12', cells == 12, cells)
    maps = [(p, m) for p in itertools.permutations('ABCDEF') for m in (False, True)
            if image(PTABLE, dict(zip('ABCDEF', p)), m) == TM]
    rec('REL.unique_map_of_720x2', len(maps) == 1 and maps[0] == (tuple(SIGMA[s] for s in 'ABCDEF'), False), maps)
    rec('REL.start_is_sigma(E)', SIGMA['E'] == 'A')
    rec('REL.canon(this,A)=canon(parent,E)', canon(TABLE, 'A') == canon(PTABLE, 'E'))
    # lockstep: this machine from blank vs the parent's table from 0^inf E> 0^inf, every step (Python)
    N = scale(200000, 2000000, 10000000)
    ta, pa, sa = {}, 0, 'A'
    tp, pp, sp = {}, 0, 'E'
    ok = True
    for i in range(N):
        u = TABLE[(sa, ta.get(pa, 0))]
        t = PTABLE[(sp, tp.get(pp, 0))]
        if u is None or t is None:
            ok = False
            break
        ta[pa] = u[0]
        pa += u[1]
        sa = u[2]
        tp[pp] = t[0]
        pp += t[1]
        sp = t[2]
        if sa != SIGMA[sp] or pa != pp:
            ok = False
            break
    ok = ok and ta == tp
    rec('REL.lockstep_with_parent_table_from_E_python', ok, i)
    INFO['lockstep_steps'] = N
    ra = krun(TM, 'L', [('A', 0, 0, END[0], '0')])[0]
    rp = krun(PARENT, 'L', [('E', 0, 0, END[0], '0')])[0]
    rec('REL.equal_tapes_after_163638058_steps_C_runner',
        ra[0] == rp[0] == 'MAX' and ra[2] == SIGMA[rp[2]] and ra[4] == rp[4] and ra[3] == rp[3], (ra[:4], rp[:4]))


# ================================================================== OPEN
def check_OPEN():
    r = krun(TM, 'L', [('A', 0, 0, OPEN_STEP, '0')])[0]
    rec('OPEN.blank_to_K(2583,70,76,43,18)_state_B_at_6950830', is_K(r, OPEN_START[1], KSTATE), r[:4])
    r = krun(PARENT, 'L', [('E', 0, 0, OPEN_STEP, '0')])[0]
    rec('OPEN.parent_table_from_E_to_K(2583,70,76,43,18)_at_6950830', is_K(r, OPEN_START[1], 'F'), r[:4])
    rows = open_points()
    rec('OPEN.18_rows', len(rows) == 18)
    rec('OPEN.every_application_in_domain', all(x[7] for x in rows), [x[0] for x in rows if not x[7]])
    rec('OPEN.rows_chain', all(rows[i][3] == rows[i - 1][5] and rows[i][4] == rows[i - 1][6]
                               for i in range(1, len(rows))) and rows[0][3:5] == OPEN_START)
    rec('OPEN.chain_string', ' '.join(r + ('^%d' % k if k > 1 else '') for r, k in OPEN_CHAIN) == CHAIN_STR)
    rec('OPEN.end_is_Even(13299,220)_at_163638058', rows[-1][5] == END[0] and rows[-1][6] == even(*END[1]), rows[-1])
    # every K-shaped left record of both runs up to END = the predicted points (start, every single application, end)
    pred = [OPEN_START] + [s for x in rows for s in x[8]]
    for tm, st, ks in ((TM, 'A', KSTATE), (PARENT, 'E', 'F')):
        pts, end = kscan(tm, st, '*', 0, END[0])
        rec('OPEN.K_shaped_left_records_to_163638058_are_the_predicted_points_%s' % ('this' if tm == TM else 'parentE'),
            [(s, v) for s, _, v in pts] == pred and all(q == ks for _, q, _ in pts) and end[0] == 'END',
            (len(pts), len(pred), pts[:2], end))
        rec('OPEN.first_K_shaped_left_record_any_state_is_6950830', pts and pts[0][0] == OPEN_STEP, pts[:1])
    INFO['K_records_opening'] = len(pred)
    # independent: the regex of the parent's check (tools/sim.py event iterator) up to the opening start
    try:
        sys.path.insert(0, os.path.join(ROOT, 'tools'))
        import sim
        pat = re.compile('^0' + G + r'(1+)0(1+)0110(1+)000000(1+)01101111000000(1+)01100110111(0*)$')
        found = []
        for ev in sim.events(sim.parse(TM), max_steps=OPEN_STEP + 1, on_record=True, snapshot=True):
            c = ev.config
            if ev.side == 'L' and c.head == 0 and pat.match(c.tape_str()):
                found.append((ev.step, c.state))
        rec('OPEN.sim_py_first_K_shaped_left_record_is_6950830', found == [(OPEN_STEP, KSTATE)], found)
    except ImportError as e:
        rec('OPEN.sim_py_first_K_shaped_left_record_is_6950830', False, e)
    # every row by direct simulation of this machine, checkpoint to checkpoint
    for i, r, k, s0, v0, s1, v1, _, _ in rows:
        res = krun(TM, 'L', [(KSTATE, 0, 0, s1 - s0, Kw(v0))])[0]
        rec('OPEN.direct_row_this_machine', is_K(res, v1, KSTATE), (i, r, s1, v1, res[:4]))
    r = krun(TM, 'L', [('A', 0, 0, END[0], '0')])[0]
    rec('OPEN.blank_to_Even(13299,220)_at_163638058_unbroken', is_K(r, even(*END[1]), KSTATE), r[:4])
    INFO['OPEN'] = [(x[0], x[1], x[2], x[5], Kfmt(x[6])) for x in rows]


# ================================================================== INV
def check_INV():
    n, x = END[1]
    rec('INV.n_odd', n % 2 == 1)
    rec('INV.x=4mod8_>=52', x % 8 == 4 and x >= 52)
    rec('INV.b=(x+76)/2_c=35_d=18', even(n, x) == {'n': 13299, 'a': 220, 'b': 148, 'c': 35, 'd': 18})
    v1, _, ok1, _ = P.double_cycle(13299, 220)
    v2, _, ok2, _ = P.double_cycle(v1['n'], v1['a'])
    rec('INV.orbit_first_members', ok1 and ok2 and v1 == even(16747, 252) and v2 == even(20579, 284), (v1, v2))


# ================================================================== DC
def check_DC():
    xs = list(range(52, scale(100, 252, 404) + 1, 8))
    jobs, exp = [], []
    for x in xs:
        n = 2 * RNG.randrange(0, 60) + 1
        val, tot, ok, _ = P.double_cycle(n, x)
        rec('DC.every_application_in_domain', ok, x)
        trips = 3 * x + 201
        jobs.append((KSTATE, 0, 2 * trips, 10 ** 11, Kw(even(n, x))))
        exp.append((val, tot))
    for x, r, (val, tot) in zip(xs, krun(TM, 'L', jobs), exp):
        rec('DC.direct_double_cycle_this_machine', is_K(r, val, KSTATE, 'REC') and r[1] == tot, (x, r[:4]))
    # the orbit from step 163,638,058: predicted even and odd run starts, and every single application
    limit = scale(5 * 10 ** 8, 2 * 10 ** 9, 10 ** 10)
    step, val = END[0], even(*END[1])
    pts, singles = [], []
    done = False
    while not done:
        x = val['a']
        nv, tot, ok, _ = P.double_cycle(val['n'], x)
        rec('DC.orbit_every_application_in_domain', ok, (step, val))
        v2, s2 = dict(val), step
        for i, r in enumerate(['S'] * ((x - 4) // 8) + ['TE'] + ['S'] * ((x - 52) // 8) + ['TO']):
            s2 += P.rule_steps(r, v2)
            v2 = P.apply_rule(r, v2)
            if s2 > limit:
                done = True
                break
            singles.append((s2, dict(v2)))
            if r == 'TE':
                pts.append(('odd', s2, dict(v2)))
        if not done:
            rec('DC.orbit_cycle_end_is_Even', s2 == step + tot and v2 == nv, (step, val))
            pts.append(('even', s2, dict(v2)))
            step, val = s2, v2
    cfg, prev = Kw(even(*END[1])), END[0]
    for kind, s, v in pts:
        r = krun(TM, 'L', [(KSTATE, 0, 0, s - prev, cfg)])[0]
        rec('DC.orbit_%s_start_direct' % kind, is_K(r, v, KSTATE), (s, v, r[:4]))
        cfg, prev = Kw(v), s
    r = krun(TM, 'L', [('A', 0, 0, pts[-1][1], '0')])[0]
    rec('DC.orbit_last_point_unbroken_run_from_blank', is_K(r, pts[-1][2], KSTATE), (pts[-1][:2], r[:4]))
    # every K-shaped left record of this machine in (END, limit] is the next predicted single application
    found, end = kscan(TM, 'A', '*', END[0] + 1, limit)
    rec('DC.K_shaped_left_records_to_limit_are_the_predicted_points',
        [(s, v) for s, _, v in found] == singles and all(q == KSTATE for _, q, _ in found) and end[0] == 'END',
        (len(found), len(singles), end))
    INFO['ORBIT'] = '%d even/odd run starts, %d K records to %d steps' % (len(pts), len(singles), limit)


# ================================================================== PLAN
def Kr(N, a, b, c, d):
    """proof.md 6: Kr N a b c d = K(2N+1, 2a, 2b, 2c+1, 2d)"""
    return {'n': 2 * N + 1, 'a': 2 * a, 'b': 2 * b, 'c': 2 * c + 1, 'd': 2 * d}


def Ev(N, X):
    return Kr(N, 4 * X + 26, 2 * X + 32, 17, 9)


# the parent .v's lemma statements, as (name, arity, lhs, rhs) on the arguments
LEMMAS = {
    'S_iter': (6, lambda k, n, a, b, c, d: Kr(n, 4 * k + 1 + a, 3 + b, 3 + c, 1 + d),
               lambda k, n, a, b, c, d: Kr(24 * k + n, 1 + a, 2 * k + 3 + b, 3 + c, 2 * k + 1 + d)),
    "TE'": (3, lambda n, b, d: Kr(n, 2, 44 + b, 17, 10 + d), lambda n, b, d: Kr(278 + n, 1 + b, 41, 35 + d, 9)),
    "TO'": (4, lambda n, b, c, d: Kr(n, 1, 41 + b, 18 + c, 9 + d),
            lambda n, b, c, d: Kr(294 + n, 14 + b + c, 40 + d, 17, 9)),
    "T6'": (4, lambda n, b, c, d: Kr(n, 3, 41 + b, 17 + c, 9 + d),
            lambda n, b, c, d: Kr(296 + n, 13 + b + c, 40 + d, 17, 9)),
    "T8'": (3, lambda n, b, d: Kr(n, 4, 44 + b, 17, 10 + d), lambda n, b, d: Kr(270 + n, 7 + b, 43, 33 + d, 9)),
}
# the same statements as Rocq text in the parent's kill file
LEMMA_TEXT = {
    'S_iter': 'Lemma S_iter k: forall n a b c d,\n  Kr n (4*k+1+a) (3+b) (3+c) (1+d) -->* '
              'Kr (24*k+n) (1+a) (2*k+3+b) (3+c) (2*k+1+d).',
    "TE'": "Lemma TE' n b d:\n  Kr n 2 (44+b) 17 (10+d) -->* Kr (278+n) (1+b) 41 (35+d) 9.",
    "TO'": "Lemma TO' n b c d:\n  Kr n 1 (41+b) (18+c) (9+d) -->* Kr (294+n) (14+b+c) (40+d) 17 9.",
    "T6'": "Lemma T6' n b c d:\n  Kr n 3 (41+b) (17+c) (9+d) -->* Kr (296+n) (13+b+c) (40+d) 17 9.",
    "T8'": "Lemma T8' n b d:\n  Kr n 4 (44+b) 17 (10+d) -->* Kr (270+n) (7+b) 43 (33+d) 9.",
}
FOLLOWS = ['(S_iter 8 1291 2 35 18 8)', "(T6' 1483 13 4 16)", '(S_iter 7 1779 1 53 14 8)', "(TE' 1947 26 13)",
           '(S_iter 6 2225 2 38 45 8)', "(T6' 2369 12 31 12)", '(S_iter 13 2665 3 49 14 8)', "(T8' 2977 34 25)",
           '(S_iter 10 3247 0 40 55 8)', "(TO' 3487 22 40 20)", '(S_iter 18 3781 3 57 14 8)', "(T8' 4213 52 35)",
           '(S_iter 14 4483 2 40 65 8)', "(T6' 4819 30 51 28)", '(S_iter 23 5115 1 65 14 8)', "(TE' 5667 70 45)",
           '(S_iter 17 5945 2 38 77 8)', "(T6' 6353 34 63 34)"]
RULE_OF = {'S_iter': 'S', "TE'": 'TE', "TO'": 'TO', "T6'": 'T6', "T8'": 'T8'}


def check_PLAN():
    vfile = os.path.join(PDIR, PARENT + '.v')
    vt = open(vfile).read()
    for name, txt in LEMMA_TEXT.items():
        rec('PLAN.lemma_statement_in_parent_v', txt in vt, name)
    rec('PLAN.Kr_Ev_definitions_in_parent_v', 'Definition Ev N X := Kr N (4*X+26) (2*X+32) 17 9.' in vt and
        'Lemma DC N X:\n  Ev N X -->+ Ev (48*X+716+N) (4+X).' in vt)
    rec('PLAN.start_Kr_1291_35_38_21_9', Kr(1291, 35, 38, 21, 9) == OPEN_START[1])
    rec('PLAN.end_Ev_6649_21', Ev(6649, 21) == even(*END[1]))
    rows = open_points()
    rec('PLAN.18_follows', len(FOLLOWS) == len(rows) == 18)
    for f, (i, r, k, s0, v0, s1, v1, _, _) in zip(FOLLOWS, rows):
        p = f.strip('()').split()
        name, xs = p[0], [int(t) for t in p[1:]]
        ar, lhs, rhs = LEMMAS[name]
        ok = len(xs) == ar and all(t >= 0 for t in xs) and RULE_OF[name] == r and lhs(*xs) == v0 and rhs(*xs) == v1
        if name == 'S_iter':
            ok = ok and xs[0] == k
        rec('PLAN.follow_args_match_row', ok, (i, f, v0, v1))
    # the Rocq DC step agrees with the double cycle
    for N, X in ((6649, 21), (RNG.randrange(10 ** 6), RNG.randrange(10 ** 3))):
        v, _, ok, _ = P.double_cycle(2 * N + 1, 8 * X + 52)
        rec('PLAN.DC_Ev_step', ok and v == Ev(48 * X + 716 + N, 4 + X), (N, X))


# ================================================================== REM
def check_REM():
    lines = open(os.path.join(ROOT, 'data', 'holdouts', 'BB6_holdouts_740.txt')).read().split('\n')
    for ln, tm, start, mirror in ((214, PARENT, 'A', False), (418, SIB418, 'C', True), (636, TM, 'E', False)):
        t = sx.parse_table(tm)
        maps = [(p, m) for p in itertools.permutations('ABCDEF') for m in (False, True)
                if image(PTABLE, dict(zip('ABCDEF', p)), m) == tm]
        rec('REM.line_start_mirror', lines[ln - 1] == tm and canon(t, 'A') == canon(PTABLE, start) and
            len(maps) == 1 and maps[0][1] == mirror and maps[0][0]['ABCDEF'.index(start)] == 'A', (ln, maps))
    r = krun(PARENT, 'L', [('A', 0, 0, 153126993, '0')])[0]
    rec('REM.line214_Even(12863,212)_at_153126993', is_K(r, even(12863, 212), 'F'), r[:4])
    r = krun(PARENT, 'L', [('C', 0, 0, 153013640, '0')])[0]
    rec('REM.parent_table_from_C_Even(12857,212)_at_153013640', is_K(r, even(12857, 212), 'F'), r[:4])
    # starts B and F: the tape stays blank and the run enters C after 1 / 2 steps
    for q, k in (('B', 1), ('F', 2)):
        tape, pos, st = {}, 0, q
        for _ in range(k):
            w, d, st2 = PTABLE[(st, tape.get(pos, 0))]
            tape[pos] = w
            pos += d
            st = st2
        rec('REM.start_%s_blank_C_after_%d' % (q, k), st == 'C' and not any(tape.values()), (st, tape))
    # the first two lines of the table have killed proofs (status.json)
    import json
    for tm in (PARENT, SIB418):
        s = json.load(open(os.path.join(ROOT, 'machines', tm, 'status.json')))
        rec('REM.sibling_status_killed', s.get('status') == 'KILLED_NONHALT', (tm, s.get('status')))


# ================================================================== PAR
def check_PAR():
    if NO_PARENT:
        INFO['PAR'] = 'skipped (--no-parent)'
        return
    mode = ['--quick'] if QUICK else (['--full'] if FULL else [])
    t = time.time()
    r = subprocess.run(['nice', '-n', '10', sys.executable, '-B', os.path.join(PDIR, 'check_proof.py'),
                        '--seed', str(SEED)] + mode, capture_output=True, text=True, cwd=PDIR)
    last = [l for l in r.stdout.split('\n') if l.startswith('seed ')]
    m = re.search(r'(\d+)/(\d+) checks PASS', last[-1]) if last else None
    rec('PAR.parent_check_proof_exit_0_all_PASS', r.returncode == 0 and m and m.group(1) == m.group(2),
        (r.returncode, last[-1:] or r.stderr[-300:]))
    for sec in ('MR.', 'DC.', 'HALT.'):
        lines = [l for l in r.stdout.split('\n') if re.match(r'(PASS|FAIL) +' + re.escape(sec), l)]
        rec('PAR.parent_%s_checks_present_and_PASS' % sec.strip('.'), lines and all(l.startswith('PASS')
                                                                                   for l in lines), lines[:3])
    INFO['PAR'] = '%s (%.0f s)' % (last[-1] if last else '?', time.time() - t)
    os.makedirs(os.path.join(HERE, 'work'), exist_ok=True)
    with open(os.path.join(HERE, 'work', 'parent_check_proof_%s_seed%d.log' % (
            'quick' if QUICK else ('full' if FULL else 'default'), SEED)), 'w') as f:
        f.write(r.stdout)


# ================================================================== HALT
def check_HALT():
    rec('HALT.sigma(D)=F', SIGMA['D'] == 'F')
    pund = [k for k, v in PTABLE.items() if v is None]
    rec('HALT.parent_only_D0_undefined', pund == [('D', 0)], pund)
    rec('HALT.halting_cells_correspond', [(SIGMA[s], b) for s, b in pund] ==
        [k for k, v in TABLE.items() if v is None])
    rec('HALT.E1_is_sigma_of_parent_A1', TABLE[('E', 1)] == (1, -1, 'F') and PTABLE[('A', 1)] == (1, -1, 'D'))
    r = krun(TM, 'L', [('A', 0, 0, END[0], '0')])[0]
    rec('HALT.no_halt_in_first_163638058_steps', r[0] == 'MAX', r[:4])


# ================================================================== DOC
def dom_rows():
    out = []
    for i, r, k, s0, v0, s1, v1, _, _ in open_points():
        a = '%d, …, %d' % (v0['a'], v0['a'] - 8 * (k - 1)) if r == 'S' else str(v0['a'])
        out.append('| %d | %s | %s | %s | %d | %d | %d |' % (i, r + ('^%d' % k if k > 1 else ''), Kfmt(v0), a,
                                                            v0['b'], v0['c'], v0['d']))
    return out


def bound_lines():
    rows = open_points()
    S = [x for x in rows if x[1] == 'S']
    amin = min(x[4]['a'] - 8 * (x[2] - 1) for x in S)
    arow = [x[0] for x in S if x[4]['a'] - 8 * (x[2] - 1) == amin]
    # the bounds also hold for every single application (b, d grow, c fixed, a falls)
    ok = all(P.in_dom(RULES[x[1]]['dom'], x[4]) for x in rows)
    by = lambda r: [x[4] for x in rows if x[1] == r]
    TE, TO, T6, T8 = by('TE'), by('TO'), by('T6'), by('T8')
    lines = [
        '- In the S rows the smallest a is %d (row %s), the smallest b is %d, every c is at least %d and every d is '
        '%s.' % (amin, ','.join(map(str, arow)), min(x[4]['b'] for x in S), min(x[4]['c'] for x in S),
                 '/'.join(str(d) for d in sorted({x[4]['d'] for x in S}))),
        '- The TE rows have c = %s, b ≥ %d and d ≥ %d.' % ('/'.join(sorted({str(v['c']) for v in TE})),
                                                          min(v['b'] for v in TE), min(v['d'] for v in TE)),
        '- The TO row has b = %d, c = %d and d = %d.' % (TO[0]['b'], TO[0]['c'], TO[0]['d']) if len(TO) == 1 else '?',
        '- The T6 rows have b ≥ %d, c ≥ %d and d ≥ %d.' % (min(v['b'] for v in T6), min(v['c'] for v in T6),
                                                          min(v['d'] for v in T6)),
        '- The T8 rows have c = %s, b ≥ %d and d ≥ %d.' % ('/'.join(sorted({str(v['c']) for v in T8})),
                                                          min(v['b'] for v in T8), min(v['d'] for v in T8)),
    ]
    return lines, ok


def check_DOC():
    p = os.path.join(HERE, 'proof.md')
    if not os.path.exists(p):
        rec('DOC.proof_md_exists', False)
        return
    t = open(p).read()
    inv = {v: k for k, v in SIGMA.items()}
    fmt = lambda tr: '---' if tr is None else '%d%s%s' % (tr[0], 'R' if tr[1] == 1 else 'L', tr[2])
    for s in 'ABCDEF':
        q = inv[s]
        row = '| %s | %s | %s | σ(%s) | %s | %s |' % (s, fmt(TABLE[(s, 0)]), fmt(TABLE[(s, 1)]), q,
                                                     fmt(PTABLE[(q, 0)]), fmt(PTABLE[(q, 1)]))
        rec('DOC.relation_row_quoted', row in t, row)
    rec('DOC.sigma_quoted', 'A -> E,  B -> C,  C -> D,  D -> F,  E -> A,  F -> B' in t)
    rec('DOC.opening_start_quoted', '| opening | (direct run) | 6,950,830 | K(2583,70,76,43,18) |' in t and
        'K(2583, 70, 76, 43, 18)' in t)
    for i, r, k, s0, v0, s1, v1, _, _ in open_points():
        row = '| %d | %s | %s | %s |' % (i, r + ('^%d' % k if k > 1 else ''), '{:,}'.format(s1), Kfmt(v1))
        rec('DOC.opening_row_quoted', row in t, row)
    for row in dom_rows():
        rec('DOC.domain_row_quoted', row in t, row)
    lines, ok = bound_lines()
    rec('DOC.domain_rows_in_domain', ok)
    for l in lines:
        rec('DOC.bound_line_quoted', l in t, l)
    for name, dom in RULES.items():
        d = dom['dom']
        cell = lambda v: str(d[v]) if isinstance(d[v], int) else '%s, ≥ %d' % ('odd' if d[v][0] else 'even', d[v][1])
        row = '| %s | %s | %s | %s | %s |' % (name, cell('a'), cell('b'), cell('c'), cell('d'))
        rec('DOC.domain_definition_row_quoted', row in t and d['n'] == (1, 1), row)
    rec('DOC.chain_quoted', CHAIN_STR in t)
    v = {'n': 1, 'a': 0, 'b': 0, 'c': 0, 'd': 0}
    rec('DOC.n_increments_quoted', sorted(P.apply_rule(r, v)['n'] - 1 for r in RULES) == [48, 540, 556, 588, 592]
        and '(48, 556, 588, 592 or 540)' in t)
    rec('DOC.records_count_quoted', 'these 126 configurations' in t and
        1 + sum(k for _, k in OPEN_CHAIN) == 126)
    for f in FOLLOWS:
        rec('DOC.follow_quoted', 'follow %s.' % f in t, f)
    rec('DOC.start_end_half_quoted', 'Kr 1291 35 38 21 9' in t and 'Ev 6649 21' in t and '(6649, 21)' in t)
    rec('DOC.end_quoted', 'Even(13299, 220)' in t and '163,638,058' in t and
        'Even(13299, 220) --> Even(16747, 252) --> Even(20579, 284)' in t)
    try:
        sys.path.insert(0, os.path.join(ROOT, 'tools'))
        import writeup_lint
        hits = writeup_lint.lint_text(t[:t.index('\n## 7.')])
        errs = [(h['line'], h['rule']) for h in hits if h['level'] == 'ERROR']
        rec('DOC.sections_before_7_lint_0_errors', not errs, errs[:5])
        INFO['LINT'] = {'errors': errs, 'warnings': [(h['line'], h['rule']) for h in hits if h['level'] == 'WARN']}
    except (ImportError, ValueError) as e:
        rec('DOC.sections_before_7_lint_0_errors', False, e)


SECTIONS = [('T', check_T), ('REL', check_REL), ('OPEN', check_OPEN), ('INV', check_INV), ('DC', check_DC),
            ('PLAN', check_PLAN), ('REM', check_REM), ('PAR', check_PAR), ('HALT', check_HALT), ('DOC', check_DOC)]


def main():
    build()
    for name, fn in SECTIONS:
        if ONLY and name not in ONLY:
            continue
        t = time.time()
        fn()
        print('[%s] %.1fs' % (name, time.time() - t), flush=True)
    npass = sum(1 for c in ORDER if RES[c]['pass'] == RES[c]['n'])
    for c in ORDER:
        r = RES[c]
        st = 'PASS' if r['pass'] == r['n'] else 'FAIL'
        print('%-4s %-72s %d/%d %s' % (st, c, r['pass'], r['n'], ('' if st == 'PASS' else r['fail'])))
    for k, v in INFO.items():
        print('info', k, v)
    print('seed %d, %s mode: %d/%d checks PASS, %.1fs' % (SEED, 'quick' if QUICK else ('full' if FULL else 'default'),
                                                         npass, len(ORDER), time.time() - T0))
    sys.exit(0 if npass == len(ORDER) else 1)


if __name__ == '__main__':
    main()