gdp-wired 0.12.1 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -100,7 +100,12 @@ export function solve(constraints, maxRows = 20_000) {
100
100
  const neg = byKey.get(negKey(r));
101
101
  if (neg === undefined || neg === r)
102
102
  continue;
103
- const x = vars(lins[r]).find(name => Math.abs(lins[r][name]) === 1);
103
+ let x;
104
+ for (const name in lins[r])
105
+ if (name !== '1' && Math.abs(lins[r][name]) === 1) {
106
+ x = name;
107
+ break;
108
+ }
104
109
  if (x === undefined)
105
110
  continue;
106
111
  // up: the row of the pair with +x, down: with −x.
@@ -139,9 +144,10 @@ export function solve(constraints, maxRows = 20_000) {
139
144
  // Each variable's rows by sign, in one pass (variables in the order they first appear).
140
145
  const index = new Map();
141
146
  for (const r of rows)
142
- for (const [x, c] of Object.entries(lins[r])) {
147
+ for (const x in lins[r]) {
143
148
  if (x === '1')
144
149
  continue;
150
+ const c = lins[r][x];
145
151
  let e = index.get(x);
146
152
  if (!e)
147
153
  index.set(x, (e = { pos: [], neg: [], posUnit: true, negUnit: true }));
@@ -1120,7 +1120,7 @@ export function stepLawObligations(machine, laws, instances, sl) {
1120
1120
  const m = machine.history ? historyMachine(machine).machine : machine;
1121
1121
  scope(m, laws);
1122
1122
  const k = sl.steps ?? 1;
1123
- // Past 64 the cost is the bound (proving grows about as k³: 64 steps in about 17 s on Modal; Lean checks 32 in about 25 s, 64 in about 3 minutes).
1123
+ // Past 64 the cost is the bound (proving grows about as k³: 64 steps in about 5 s on Modal; Lean checks 32 in about 14 s, 64 in about 47 s).
1124
1124
  if (!Number.isInteger(k) || k < 1 || k > 64)
1125
1125
  throw new Error(`step law ${sl.name}: steps is a whole number from 1 to 64`);
1126
1126
  const whens = sl.when === undefined ? [] : Array.isArray(sl.when) ? sl.when : [sl.when];
@@ -412,15 +412,25 @@ function growFocused(goal, path, focus, budget, near = focus) {
412
412
  }
413
413
  // A refutation from the rows near some facts (the rows reachable from their variables in two hops, each row joining the
414
414
  // variables it reads), its inputs renumbered to the whole leaf's. A cut about one step of a long run needs that step's
415
- // rows, not the run's; a cut it misses is only a cut not made (the plain search still follows).
415
+ // rows, not the run's; a cut it misses is only a cut not made (the plain search still follows). A variable most rows
416
+ // read (a param every step reads) joins no rows: through it every step is near every other.
416
417
  function solveNear(rows, seeds) {
418
+ const uses = new Map();
419
+ for (const r of rows)
420
+ for (const key in r.lin)
421
+ if (key !== '1')
422
+ uses.set(key, (uses.get(key) ?? 0) + 1);
423
+ const hub = (key) => (uses.get(key) ?? 0) > Math.max(16, rows.length / 8);
417
424
  const reach = new Set(seeds.flatMap(l => Object.keys(l).filter(key => key !== '1')));
418
- for (let hop = 0; hop < 2; hop++)
425
+ for (let hop = 0; hop < 2; hop++) {
426
+ // One hop: the rows reading what was reached before it (not what this hop adds, or one pass runs along the run).
427
+ const from = new Set(reach);
419
428
  for (const r of rows) {
420
429
  const keys = Object.keys(r.lin).filter(key => key !== '1');
421
- if (keys.some(key => reach.has(key)))
430
+ if (keys.some(key => from.has(key) && !hub(key)))
422
431
  keys.forEach(key => reach.add(key));
423
432
  }
433
+ }
424
434
  const picked = rows.flatMap((r, i) => (Object.keys(r.lin).every(key => key === '1' || reach.has(key)) ? [i] : []));
425
435
  const near = solve(picked.map(i => rows[i]));
426
436
  return near.kind === 'refuted' ? { kind: 'refuted', steps: near.steps.map(st => (st.kind === 'input' ? { ...st, index: picked[st.index] } : st)) } : near;
@@ -145,6 +145,7 @@ export type LeanItem = {
145
145
  };
146
146
  export declare function toLean(items: LeanItem[], opts?: {
147
147
  title?: string;
148
+ native?: boolean;
148
149
  }): string;
149
150
  export declare function leanProject(file: string, opts?: {
150
151
  name?: string;
@@ -214,11 +214,11 @@ def ${name}_tree : Tree :=
214
214
  return tamper
215
215
  ? `-- TAMPERED: ${ex.name}${dropStep ? ", the first leaf's derivation without its last step" : ''}. Lean computes the check to false.
216
216
  ${body}
217
- example : checkGoal ${name}_goal ${name}_tree = false := by decide +kernel`
217
+ example : checkGoal ${name}_goal ${name}_tree = false := by ${checkBy}`
218
218
  : `-- ${ex.name} (${cols.length} columns)
219
219
  ${body}
220
220
  theorem ${name} : ∀ env maps, ¬ ∀ c ∈ ${name}_goal.hyps, evalC env maps c = true :=
221
- sound ${name}_goal ${name}_tree (by decide +kernel)`;
221
+ sound ${name}_goal ${name}_tree (by ${checkBy})`;
222
222
  }
223
223
  // Whole machines through Kern.machine_sound: Lean builds the goals from the machine; the trees are the kernel's.
224
224
  export function machineToLean(id, m, laws, corollary) {
@@ -255,15 +255,17 @@ export function machineToLean(id, m, laws, corollary) {
255
255
  def ${id}_machine : FMachine := ${lit(m)}
256
256
  def ${id}_proof : MachineProof := ${list(proof)}
257
257
  theorem ${id}_laws : ∀ maps ρ, FReach ${id}_machine maps ρ → ∀ l ∈ ${id}_proof.map (·.1), evalC ρ maps l = true :=
258
- fmachine_sound ${id}_machine ${id}_proof (by decide +kernel)${corollary ? `\n${corollary}` : ''}`;
258
+ fmachine_sound ${id}_machine ${id}_proof (by ${checkBy})${corollary ? `\n${corollary}` : ''}`;
259
259
  }
260
260
  return `-- ${m.name}: ${laws.map(l => l.name).join('; ')} (variables ${vars.map((v2, j) => `${j} ${v2}`).join(', ')})
261
261
  def ${id}_machine : Machine := ${machine}
262
262
  def ${id}_proof : MachineProof := ${list(proof)}
263
263
  theorem ${id}_laws : ∀ maps ρ, Reach ${id}_machine maps ρ → ∀ l ∈ ${id}_proof.map (·.1), evalC ρ maps l = true :=
264
- machine_sound ${id}_machine ${id}_proof (by decide +kernel)${corollary ? `\n${corollary}` : ''}`;
264
+ machine_sound ${id}_machine ${id}_proof (by ${checkBy})${corollary ? `\n${corollary}` : ''}`;
265
265
  }
266
266
  export const TRUE = '.le (.const 0) (.const 0)';
267
+ // The tactic each check is closed by: `decide +kernel`, or `native_decide` for toLean's `native`.
268
+ let checkBy = 'decide +kernel';
267
269
  export const grown = (g) => { const o = grow(g); if (!('tree' in o))
268
270
  throw new Error(`${g.name}: stuck`); return o.tree; };
269
271
  // Loops through Kern.loop_halts_of: the loop's machine (Live.lean's Loop.machine) with its invariants as laws, and its
@@ -295,7 +297,7 @@ def ${id}_fold : Fold := ${fold}
295
297
  def ${id}_proof : FoldProof := ${list(proof)}
296
298
  theorem ${id}_laws : ∀ maps ρ, evalC ρ maps ${id}_fold.pre = true → ∀ t, Allowed ${id}_fold ρ maps t →
297
299
  ∀ l ∈ ${id}_proof.map (·.1), evalC (foldT ${id}_fold ρ maps t) maps l = true :=
298
- fold_sound ${id}_fold ${id}_proof (by decide +kernel)${extra}`;
300
+ fold_sound ${id}_fold ${id}_proof (by ${checkBy})${extra}`;
299
301
  }
300
302
  // Machines with maps (and flags) through Kern.fmmachine_sound: Lean inlines the flags, then builds the start and step
301
303
  // goals (read-over-write, ∀-laws at the proof's keys and at a skolem); the trees and the instance keys are the kernel's.
@@ -433,10 +435,10 @@ export function mmachineToLean(id, m, laws, opts = {}) {
433
435
  return `-- Progress: ${pc.name}
434
436
  def ${pc.id} : Progress := ${record(pc.when)}
435
437
  theorem ${pc.id}_holds : ProgressHolds ${id}_machine ${pc.id} :=
436
- progress_sound ${id}_machine ${id}_proof ${pc.id} ${id}_check (by decide +kernel)${pc.tamper ? `
438
+ progress_sound ${id}_machine ${id}_proof ${pc.id} ${id}_check (by ${checkBy})${pc.tamper ? `
437
439
  -- TAMPERED: ${pc.tamper.what}: the proof above offered for it. Lean computes the check to false.
438
440
  def ${pc.id}_tampered : Progress := ${record(pc.tamper.when)}
439
- example : checkProgress ${id}_machine ${id}_proof ${pc.id}_tampered = false := by decide +kernel` : ''}`;
441
+ example : checkProgress ${id}_machine ${id}_proof ${pc.id}_tampered = false := by ${checkBy}` : ''}`;
440
442
  });
441
443
  // Step laws through Kern.steplaw_sound: `when` and `then` sent with the flags inlined (as kernel.ts's goal has them);
442
444
  // a law over several steps goes through smachineToLean.
@@ -461,20 +463,20 @@ def ${id}_step${j} : StepLaw := ⟨${when ? any0.c(inlineFlags(when)) : TRUE},
461
463
  ${list(insts)},
462
464
  (${pr.tab}, ${pr.tree})⟩
463
465
  theorem ${id}_step${j}_holds : StepLawHolds ${id}_machine ${id}_step${j} :=
464
- steplaw_sound ${id}_machine ${id}_proof ${id}_step${j} ${id}_check (by decide +kernel)`;
466
+ steplaw_sound ${id}_machine ${id}_proof ${id}_step${j} ${id}_check (by ${checkBy})`;
465
467
  });
466
468
  const machineDef = `def ${id}_machine : FMMachine := ${lit(m)}`;
467
469
  return `-- ${m.name}: ${laws.map(l => l.name).join('; ')}${flagNames.length ? `, with flags ${flagNames.join(', ')}` : ''} (variables ${vars.map((v2, j) => `${j} ${v2}`).join(', ')}; maps ${mapNames.map((v2, j) => `${j} ${v2}`).join(', ')})
468
470
  ${machineDef}
469
471
  ${entries.join('\n')}
470
472
  def ${id}_proof : List MEntry := ${list(laws.map((_, j) => `${id}_e${j}`))}
471
- theorem ${id}_check : checkFMMachine ${id}_machine ${id}_proof = true := by decide +kernel
473
+ theorem ${id}_check : checkFMMachine ${id}_machine ${id}_proof = true := by ${checkBy}
472
474
  theorem ${id}_laws : ∀ ρ M, FMReach ${id}_machine ρ M → ∀ e ∈ ${id}_proof, e.law.Holds ρ M :=
473
475
  fmmachine_sound ${id}_machine ${id}_proof ${id}_check
474
476
  ${readable(id, laws, vars, mapNames, { binders: '(ρ : Nat → Nat) (μ : Nat → Nat → Nat)', hyp: `FMReach ${id}_machine ρ μ`, call: `${id}_laws ρ μ h` })}${opts.corollary ? `\n${opts.corollary}` : ''}${opts.tamper ? `
475
477
  -- TAMPERED: ${opts.tamper.what}. Lean computes the check to false.
476
478
  def ${id}_tampered : FMMachine := ${lit(opts.tamper.machine)}
477
- example : checkFMMachine ${id}_tampered ${id}_proof = false := by decide +kernel` : ''}${progress.length ? `\n\n${progress.join('\n\n')}` : ''}${stepRecords.length ? `\n\n${stepRecords.join('\n\n')}` : ''}`;
479
+ example : checkFMMachine ${id}_tampered ${id}_proof = false := by ${checkBy}` : ''}${progress.length ? `\n\n${progress.join('\n\n')}` : ''}${stepRecords.length ? `\n\n${stepRecords.join('\n\n')}` : ''}`;
478
480
  }
479
481
  // Machines with sets through Kern.smachine_sound and Kern.skstep_sound (Sets.lean): maps a request carries, flags that
480
482
  // hold at every key (each its 0/1 variable ?f, closed at the proof's keys and at a witness w?f) and writes at every
@@ -523,7 +525,7 @@ def ${id}_step${j} : SKStep := ⟨⟨${list(Array.from({ length: kk }, (_, s2) =
523
525
  ${pr.hyps},
524
526
  ${list(Array.from({ length: kk - 1 }, (_, s2) => list(Object.keys(m.state).map(n2 => `(${ix(n2)}, ${ix(stateName(n2, s2 + 1))})`))))}⟩
525
527
  theorem ${id}_step${j}_holds : SKStepHolds ${id}_machine ${id}_step${j} :=
526
- skstep_sound ${id}_machine ${id}_proof ${id}_step${j} ${id}_check (by decide +kernel)`;
528
+ skstep_sound ${id}_machine ${id}_proof ${id}_step${j} ${id}_check (by ${checkBy})`;
527
529
  } }));
528
530
  // A refinement (Refine.lean's SRefined): its step law, that the spec stutters or allows the request it is sent; srefines
529
531
  // says what the whole means.
@@ -531,11 +533,11 @@ theorem ${id}_step${j}_holds : SKStepHolds ${id}_machine ${id}_step${j} :=
531
533
  claims.push({ steps: [stepLaws[0]], allows: [], render: h => {
532
534
  const primesL = list(Object.keys(m.state).map(s2 => `(${ix(s2)}, ${ix(`${s2}'`)})`)), primeMapsL = list(Object.keys(m.maps ?? {}).map(mp => `(${mx(mp)}, ${mx(`${mp}'`)})`));
533
535
  const proofL = h.sproof(stepLaws[0]);
534
- return `theorem ${id}_allowed : checkSKStep ${id}_machine ${id}_proof (srStep ${id}_refined ${primesL} ${primeMapsL} ${proofL}) = true := by decide +kernel
536
+ return `theorem ${id}_allowed : checkSKStep ${id}_machine ${id}_proof (srStep ${id}_refined ${primesL} ${primeMapsL} ${proofL}) = true := by ${checkBy}
535
537
  theorem ${id}_refines : ∀ ρ M, SReach ${id}_refined.impl ρ M → ∃ ρc Mc σ Ms, SReach ${id}_refined.full ρc Mc ∧ Agree ${id}_refined.SV ρc ρ
536
538
  ∧ (∀ mp, mp ∉ ${id}_refined.SM → Mc mp = M mp) ∧ SReach ${id}_refined.spec σ Ms ∧ Agree ${id}_refined.X σ ρc
537
539
  ∧ (∀ mp, mp ∉ ${id}_refined.IM → mp ∉ ${id}_refined.spec.carried → Ms mp = Mc mp) ∧ ∀ e ∈ ${id}_proof, e.Holds ρc Mc :=
538
- srefines ${id}_refined (by decide +kernel) (by decide +kernel) ${id}_proof ${id}_check ${rf.specPre} ⟨rfl, rfl⟩ ${primesL} ${primeMapsL} ${proofL} ${id}_allowed (by decide +kernel) (by decide +kernel) (by decide +kernel)`;
540
+ srefines ${id}_refined (by ${checkBy}) (by ${checkBy}) ${id}_proof ${id}_check ${rf.specPre} ⟨rfl, rfl⟩ ${primesL} ${primeMapsL} ${proofL} ${id}_allowed (by ${checkBy}) (by ${checkBy}) (by ${checkBy})`;
539
541
  } });
540
542
  // Liveness (Live.lean's sleads_sound): its step laws (stay, cover, one per helpful request), and where each helpful
541
543
  // request is proven allowed in its region, the same under weak fairness (sleads_wf_sound).
@@ -546,7 +548,7 @@ theorem ${id}_refines : ∀ ρ M, SReach ${id}_refined.impl ρ M → ∃ ρc Mc
546
548
  const al = allows.map(h.allow);
547
549
  const wf = al.every(a => a !== undefined) ? `
548
550
  def ${lid}_ap : List (List (Nat × E) × SProof × List E) := ${list(al)}
549
- theorem ${lid}_allows : sliveAllowsOk ${id}_machine ${id}_proof ${lid} ${lid}_ap = true := by decide +kernel
551
+ theorem ${lid}_allows : sliveAllowsOk ${id}_machine ${id}_proof ${lid} ${lid}_ap = true := by ${checkBy}
550
552
  /-- ${l.name}, under weak fairness: along every run that takes each helpful request whenever, from some point on, it
551
553
  stays allowed, \`from\` is followed by \`to\`. -/
552
554
  theorem ${lid}_holds_wf (run : SRun ${id}_machine)
@@ -560,10 +562,10 @@ def ${lid} : Live := ${liveLit(l, { e, c }, ix, list(Object.keys(m.state).map(s2
560
562
  def ${lid}_sp : SProof := ${h.sproof(stay)}
561
563
  def ${lid}_cp : SProof := ${h.sproof(cover)}
562
564
  def ${lid}_hp : List SProof := ${list(helps.map(h.sproof))}
563
- theorem ${lid}_ok : sliveOk ${id}_machine ${lid} = true := by decide +kernel
564
- theorem ${lid}_stay : checkSKStep ${id}_machine ${id}_proof (sliveStep ${lid} ${lid}.pend (liveStayC ${lid}) ${lid}_sp) = true := by decide +kernel
565
- theorem ${lid}_cover : checkSKStep ${id}_machine ${id}_proof (sliveStep ${lid} ${lid}.pend (liveCoverC ${lid}) ${lid}_cp) = true := by decide +kernel
566
- theorem ${lid}_helps : sliveHelpsOk ${id}_machine ${id}_proof ${lid} ${lid}_hp = true := by decide +kernel
565
+ theorem ${lid}_ok : sliveOk ${id}_machine ${lid} = true := by ${checkBy}
566
+ theorem ${lid}_stay : checkSKStep ${id}_machine ${id}_proof (sliveStep ${lid} ${lid}.pend (liveStayC ${lid}) ${lid}_sp) = true := by ${checkBy}
567
+ theorem ${lid}_cover : checkSKStep ${id}_machine ${id}_proof (sliveStep ${lid} ${lid}.pend (liveCoverC ${lid}) ${lid}_cp) = true := by ${checkBy}
568
+ theorem ${lid}_helps : sliveHelpsOk ${id}_machine ${id}_proof ${lid} ${lid}_hp = true := by ${checkBy}
567
569
  /-- ${l.name}: along every run that takes each helpful request infinitely often, \`from\` is followed by \`to\`. -/
568
570
  theorem ${lid}_holds (run : SRun ${id}_machine)
569
571
  (fair : ∀ h ∈ ${lid}.helps, ∀ n, ∃ m, n ≤ m ∧ ∀ p ∈ h.2, (run.req m).1 p.1 = evalE (run.st m).1 (run.st m).2 p.2) :
@@ -577,13 +579,13 @@ theorem ${lid}_holds (run : SRun ${id}_machine)
577
579
  claims.push({ steps: [], allows: [], render: () => `/-- ${lp0.loop.name} halts: for params meeting its precondition, no run of turns goes on forever. -/
578
580
  theorem ${id}_halts : ∀ maps ρ0, evalC ρ0 maps ${id}_loop.pre = true →
579
581
  ¬ ∃ f : Nat → Nat → Nat, f 0 = applyS ${id}_loop.init ρ0 maps ∧ ∀ n, Turn ${id}_loop maps (f n) (f (n + 1)) :=
580
- loop_halts_of ${id}_loop ${id}_live0 rfl rfl (by intro h hh; simp only [${id}_live0, List.mem_singleton] at hh; subst hh; rfl) (by decide +kernel) ${id}_live0_holds${lp0.tamper ? `
582
+ loop_halts_of ${id}_loop ${id}_live0 rfl rfl (by intro h hh; simp only [${id}_live0, List.mem_singleton] at hh; subst hh; rfl) (by ${checkBy}) ${id}_live0_holds${lp0.tamper ? `
581
583
  -- TAMPERED: ${lp0.tamper.what}. Lean computes the checks to false.
582
584
  def ${id}_tampered : SMachine := Loop.machine ${loopLit(lp0.tamper.loop)}
583
585
  example : (checkSMachine ${id}_tampered ${id}_proof && sliveOk ${id}_tampered ${id}_live0
584
586
  && checkSKStep ${id}_tampered ${id}_proof (sliveStep ${id}_live0 ${id}_live0.pend (liveStayC ${id}_live0) ${id}_live0_sp)
585
587
  && checkSKStep ${id}_tampered ${id}_proof (sliveStep ${id}_live0 ${id}_live0.pend (liveCoverC ${id}_live0) ${id}_live0_cp)
586
- && sliveHelpsOk ${id}_tampered ${id}_proof ${id}_live0 ${id}_live0_hp) = false := by decide +kernel` : ''}` });
588
+ && sliveHelpsOk ${id}_tampered ${id}_proof ${id}_live0 ${id}_live0_hp) = false := by ${checkBy}` : ''}` });
587
589
  // Ghosts (Ghost.lean's sghost_laws): the laws that read no ghost hold of the machine without its ghosts.
588
590
  if (gv.length)
589
591
  claims.push({ steps: [], allows: [], render: () => `-- The laws that read no ghost hold of the machine without its ghosts.
@@ -724,9 +726,9 @@ theorem ${id}_orig_laws : ∀ ρ M, SReach ${id}_ghosted.orig ρ M → ∀ e ∈
724
726
  throw new Error(`${id}: ${g.name} not proven`); const x2 = enc(g, out.tree); return `(${x2.tab}, ${x2.tree})`; });
725
727
  const tampered = (opts.historyTampers ?? []).map((t, j) => `-- TAMPERED: ${t.what}. Lean computes the check to false.
726
728
  def ${id}_tampered${j} : HMachine := ${hlit(t.relations)}
727
- example : checkHMachine ${id}_tampered${j} ${id}_proof ${t.withRecordTrees ? `${id}_records` : '[]'} = false := by decide +kernel`);
729
+ example : checkHMachine ${id}_tampered${j} ${id}_proof ${t.withRecordTrees ? `${id}_records` : '[]'} = false := by ${checkBy}`);
728
730
  return `def ${id}_records : List (List VDef × Tree) := ${list(records)}
729
- theorem ${id}_hcheck : checkHMachine ${id}_hist ${id}_proof ${id}_records = true := by decide +kernel
731
+ theorem ${id}_hcheck : checkHMachine ${id}_hist ${id}_proof ${id}_records = true := by ${checkBy}
730
732
  theorem ${id}_hlaws : ∀ ρ M hist, HReach ${id}_hist ρ M hist → ∀ e ∈ ${id}_proof, e.Holds ρ M :=
731
733
  hmachine_sound ${id}_hist ${id}_proof ${id}_records ${id}_hcheck
732
734
  ${readable(id, laws, vars, mapNames, { binders: '(ρ : Nat → Nat) (μ : Nat → Nat → Nat) (hist : List (Nat → Nat))', hyp: `HReach ${id}_hist ρ μ hist`, call: `${id}_hlaws ρ μ hist h` })}${tampered.length ? `\n${tampered.join('\n')}` : ''}`;
@@ -784,18 +786,18 @@ ${rf ? `def ${id}_refined : SRefined := ⟨${lit(rf.impl)},
784
786
  def ${id}_machine : SMachine := ${id}_refined.full` : gv.length ? `def ${id}_ghosted : SGhosted := ⟨${lit(orig)},
785
787
  ${list(gv.map(v2 => `(${ix(v2)}, ${e(m.state[v2])}, ${e(m.step.state?.[v2] ?? { op: 'var', name: v2 })})`))}⟩
786
788
  def ${id}_machine : SMachine := ${id}_ghosted.full
787
- theorem ${id}_ghostOk : sghostOk ${id}_ghosted = true := by decide +kernel` : hm ? `def ${id}_hist : HMachine := ${hlit(hm.history.relations ?? [])}
789
+ theorem ${id}_ghostOk : sghostOk ${id}_ghosted = true := by ${checkBy}` : hm ? `def ${id}_hist : HMachine := ${hlit(hm.history.relations ?? [])}
788
790
  def ${id}_machine : SMachine := ${id}_hist.plain` : lp0 ? `def ${id}_loop : Loop := ${loopLit(lp0.loop)}
789
791
  def ${id}_machine : SMachine := ${id}_loop.machine` : `def ${id}_machine : SMachine := ${lit(m)}`}
790
792
  ${entries.join('\n')}
791
793
  def ${id}_proof : List SEntry := ${list(laws.map((_, j) => `${id}_e${j}`))}
792
- theorem ${id}_check : checkSMachine ${id}_machine ${id}_proof = true := by decide +kernel
794
+ theorem ${id}_check : checkSMachine ${id}_machine ${id}_proof = true := by ${checkBy}
793
795
  theorem ${id}_laws : ∀ ρ M, SReach ${id}_machine ρ M → ∀ e ∈ ${id}_proof, e.Holds ρ M :=
794
796
  smachine_sound ${id}_machine ${id}_proof ${id}_check
795
797
  ${hm ? histBlock() : readable(id, laws, vars, mapNames, { binders: '(ρ : Nat → Nat) (μ : Nat → Nat → Nat)', hyp: `SReach ${id}_machine ρ μ`, call: `${id}_laws ρ μ h` })}${opts.noFacts ? '' : factsProof(id, m, mapNames, vars, opts.simp)}${opts.tamper ? `
796
798
  -- TAMPERED: ${opts.tamper.what}. Lean computes the check to false.
797
799
  def ${id}_tampered : SMachine := ${lit(opts.tamper.machine)}
798
- example : checkSMachine ${id}_tampered ${id}_proof = false := by decide +kernel` : ''}${records.length ? `\n\n${records.join('\n\n')}` : ''}${opts.after ? `\n\n${opts.after(ix)}` : ''}`;
800
+ example : checkSMachine ${id}_tampered ${id}_proof = false := by ${checkBy}` : ''}${records.length ? `\n\n${records.join('\n\n')}` : ''}${opts.after ? `\n\n${opts.after(ix)}` : ''}`;
799
801
  }
800
802
  // A function defined by a recurrence (core/dsl.ts recur): its Lean definition by the same recurrence, and its lemmas
801
803
  // proven together by induction (each case by omega, its min, max and ifs split first), and its laws between two points
@@ -1030,7 +1032,23 @@ export function hmachineToLean(id, m, laws, tampers = [], corollary) {
1030
1032
  // over named variables); step laws with `<id>_stepN_holds` (on the plain machine, `<id>s`, for a machine with a history);
1031
1033
  // loops with `<id>_halts`; decisions with `<id>`. A machine with sets has its step laws as `<id>_stepN_holds`.
1032
1034
  // Throws if the kernel can't prove one.
1035
+ //
1036
+ // `native`: each check by `native_decide` (compiled Lean, several times faster on a large certificate), not by
1037
+ // `decide +kernel` (Lean's kernel alone). Every such theorem then also rests on an axiom of its own that `#print axioms`
1038
+ // names (`…_native.native_decide.ax_…`): it trusts Lean's compiler, its runtime and the C compiler. Defaults to off, or
1039
+ // to GDP_LEAN_NATIVE=1 (the suite's quick runs); a release's proofs stay on Lean's kernel and its core axioms.
1033
1040
  export function toLean(items, opts = {}) {
1041
+ const outer = checkBy;
1042
+ const env = globalThis.process?.env;
1043
+ checkBy = (opts.native ?? env?.GDP_LEAN_NATIVE === '1') ? 'native_decide' : 'decide +kernel';
1044
+ try {
1045
+ return toLeanWith(items, opts);
1046
+ }
1047
+ finally {
1048
+ checkBy = outer;
1049
+ }
1050
+ }
1051
+ function toLeanWith(items, opts) {
1034
1052
  const ident = (id) => { const s = id.replace(/[^A-Za-z0-9_]/g, '_'); return /^[A-Za-z]/.test(s) ? s : `x_${s}`; };
1035
1053
  const parts = [], theorems = [];
1036
1054
  let goals = 0;