diff --git a/LeanPool/PureNock.lean b/LeanPool/PureNock.lean new file mode 100644 index 0000000000..d7824eaab0 --- /dev/null +++ b/LeanPool/PureNock.lean @@ -0,0 +1,46 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.FingerprintLayer + +/-! +# Pure Nock semantics, trace certificates, and polynomial fingerprints + +Source: url:https://github.com/nockchain/pure-nock-lean +Authors: Pure Nock formalization contributors +Status: verified +Main declarations: `Nock.Verb.runProgram_adequate_complete`, `Nock.NPR.collision_prob_le` +Tags: operational-semantics, execution-traces, finite-fields, fingerprints +MSC: 68Q55, 68Q60, 11T71 +-/ + +/-! +# Pure Nock semantics, trace certificates, and polynomial fingerprints + +The source is `nockchain/pure-nock-lean` at Apache-2.0 commit +`4838863baa2d49e38573efa3474e77bf1154f300`, first publicly released on 26 August 2026. +The original release commit credits Akis (Assimakis A. Kattis); the upstream NOTICE attributes +copyright to the formalization contributors. This port retains that collective attribution. + +Upstream notice: Nock Pure (pure-nock-lean), copyright contributors to this formalization, +licensed under the Apache License, Version 2.0. Mathlib4 and other Lake dependencies retain +their own licenses. The repository LICENSE supplies the Apache license text. + +The imported scope is the 23-module fingerprint release closure: language semantics, +modular evaluation, guarded bridges, execution trace certificates, and polynomial fingerprints. +Upstream audit programs, differential test corpora, C extraction, branding, and patches are omitted. +The bundled manuscript draft v0.1.6 is the source for `main.tex` line references in the modules: +https://github.com/nockchain/pure-nock-lean/blob/4838863baa2d49e38573efa3474e77bf1154f300/paper/main.tex. + +The bridge identifies natural-number and modular evaluation only on guarded executions. +Fingerprint distinctness is equality of field-valued coefficient data and tree shape; casting +natural-number leaves can identify different natural-number nouns. The table estimate is a union +bound with shared randomness. These results cover the language and fingerprint layers of the +compilation framework; the zkVM, AIR, RAP, table security theorem, and concrete Goldilocks field +are outside this import. The opcode-11 hint extension is separate from the paper opcode domain. +-/ diff --git a/LeanPool/PureNock/Agreement.lean b/LeanPool/PureNock/Agreement.lean new file mode 100644 index 0000000000..1335011805 --- /dev/null +++ b/LeanPool/PureNock/Agreement.lean @@ -0,0 +1,1583 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Verb +public import LeanPool.PureNock.Semantics + +/-! +# Agreement with the standard evaluator + +`den` is a compositional Nat-level denotation of verbs using `evalN` at `*` +nodes. `den_step` proves one-step preservation; `runProgram_sound` is +small-step-to-big-step agreement. +-/ + +@[expose] public section + +namespace Nock +namespace Verb + +open Noun + +/-! ### The compositional denotation `den` of a verb -/ + +/-- Compositional denotation: `*` nodes use `evalN`; operator nodes apply the matching + `Nock.Noun` operator; `⊥`-nodes are cons; leaves are atoms. -/ +def den : Nat → Verb → Option Noun + | _, .leaf k => some (.atom k) + | 0, .node _ _ _ => none + | fuel + 1, .node act l r => + match act with + | .null => + match den fuel l, den fuel r with + | some a, some b => some (.cell a b) + | _, _ => none + | .star => + match den fuel l, den fuel r with + | some a, some b => evalN fuel a b + | _, _ => none + | .slot => + match den fuel l, den fuel r with + | some (.atom ax), some b => Noun.slot ax b + | _, _ => none + | .equal => + match den fuel l, den fuel r with + | some a, some b => some (Noun.tis a b) + | _, _ => none + | .minus => + match den fuel l, den fuel r with + | some (.atom 3), some b => some (Noun.wut b) + | some (.atom 4), some b => Noun.lus b + | _, _ => none + | .edit => + match r with + | .node _ nw od => + match den fuel l, den fuel nw, den fuel od with + | some (.atom ax), some n, some o => Noun.edit ax n o + | _, _, _ => none + | .leaf _ => none + | .wut => none -- unreachable (see Verb note 2a) + +/-! ### `den` is fuel-monotone (same shape as `evalN_succ`) -/ + +theorem den_succ {n : Nat} {v : Verb} {r : Noun} + (h : den n v = some r) : den (n + 1) v = some r := by + induction n, v using den.induct generalizing r with + | case1 k => simp only [den] at h ⊢; exact h + | case5 f l r' a b hrb hla ih2 ih1 => + have hla' : den (f + 1) l = some a := ih2 hla + have hrb' : den (f + 1) r' = some b := ih1 hrb + simp only [den, hla, hrb] at h + simp only [den, hla', hrb'] + exact evalN_succ h + | _ => simp_all only [den, Option.some.injEq, reduceCtorEq] + +theorem den_mono {n m : Nat} {v : Verb} {r : Noun} + (hle : n ≤ m) (h : den n v = some r) : den m v = some r := by + induction hle with + | refl => exact h + | step _ ih => exact den_succ ih + +/-! ### (A) `den` of a terminal noun-verb is the noun itself -/ + +theorem den_ofNoun : ∀ (n : Noun) (fuel : Nat), n.size ≤ fuel → den fuel (ofNoun n) = some n := by + intro n + induction n with + | atom k => intro fuel _; simp [ofNoun, den] + | cell l r ihl ihr => + intro fuel hf + simp only [Noun.size] at hf + obtain ⟨f, rfl⟩ : ∃ f, fuel = f + 1 := ⟨fuel - 1, by omega⟩ + have hfl : l.size ≤ f := by omega + have hfr : r.size ≤ f := by omega + simp only [ofNoun, den, ihl f hfl, ihr f hfr] + +/-- Terminal verbs denote their underlying noun (given adequate fuel). -/ +theorem den_terminal : ∀ (v : Verb) (fuel : Nat), + isTerminal v = true → v.noun.size ≤ fuel → den fuel v = some v.noun := by + intro v + induction v with + | leaf k => intro fuel _ _; simp [den, noun] + | node a l r ihl ihr => + intro fuel hterm hsize + simp only [isTerminal, Bool.and_eq_true] at hterm + obtain ⟨⟨hnull, hl⟩, hr⟩ := hterm + have ha : a = Action.null := by cases a <;> simp_all only [Action.isNull, reduceCtorEq] + subst ha + simp only [noun, Noun.size] at hsize ⊢ + obtain ⟨f, rfl⟩ : ∃ f, fuel = f + 1 := ⟨fuel - 1, by omega⟩ + have hfl : l.noun.size ≤ f := by omega + have hfr : r.noun.size ≤ f := by omega + simp only [den, ihl f hl hfl, ihr f hr hfr] + +/-! ### (B) The initial program verb denotes exactly what `evalN` computes -/ + +-- main.tex:1090-1094 (`F = *[subject, formula]`) +/-- `den` of the initial program verb `*[s, f]` equals `evalN s f`. -/ +theorem den_program (s f : Noun) (fuel : Nat) (hs : s.size ≤ fuel) (hf : f.size ≤ fuel) : + den (fuel + 1) (program s f) = evalN fuel s f := by + simp only [program, den, den_ofNoun s fuel hs, den_ofNoun f fuel hf] + +/-- `den` of `*[s, f]` and `evalN s f` define the same partial function. -/ +theorem den_iff_evalN (s f r : Noun) : + (∃ fuel, den fuel (program s f) = some r) ↔ (∃ fuel, evalN fuel s f = some r) := by + constructor + · rintro ⟨fuel, h⟩ + refine ⟨max fuel (max s.size f.size), ?_⟩ + have hm : fuel ≤ max fuel (max s.size f.size) + 1 := by omega + have hs : s.size ≤ max fuel (max s.size f.size) := by omega + have hf : f.size ≤ max fuel (max s.size f.size) := by omega + have := den_mono hm h + rwa [den_program s f _ hs hf] at this + · rintro ⟨fuel, h⟩ + refine ⟨max fuel (max s.size f.size) + 1, ?_⟩ + have hm : fuel ≤ max fuel (max s.size f.size) := by omega + have hs : s.size ≤ max fuel (max s.size f.size) := by omega + have hf : f.size ≤ max fuel (max s.size f.size) := by omega + rw [den_program s f _ hs hf] + exact evalN_mono hm h + +/-! ### Small-step to big-step simulation -/ + +/-- A verb has a denotation when its evaluator succeeds with some finite fuel. -/ +def Den (v : Verb) (x : Noun) : Prop := ∃ fuel, den fuel v = some x +/-- Natural-number Nock evaluation succeeds with some finite fuel. -/ +def Ev (a b x : Noun) : Prop := ∃ fuel, evalN fuel a b = some x + +theorem den_pair {l r a b} (hl : Den l a) (hr : Den r b) : + ∃ f, den f l = some a ∧ den f r = some b := by + obtain ⟨fl, hl⟩ := hl; obtain ⟨fr, hr⟩ := hr + exact ⟨max fl fr, den_mono (Nat.le_max_left _ _) hl, den_mono (Nat.le_max_right _ _) hr⟩ + +theorem den_triple {a b c va vb vc} (ha : Den a va) (hb : Den b vb) (hc : Den c vc) : + ∃ f, den f a = some va ∧ den f b = some vb ∧ den f c = some vc := by + obtain ⟨fa, ha⟩ := ha; obtain ⟨fb, hb⟩ := hb; obtain ⟨fc, hc⟩ := hc + refine ⟨max fa (max fb fc), den_mono (by omega) ha, den_mono (by omega) hb, den_mono (by omega) + hc⟩ + +theorem Den_leaf {k x} : Den (.leaf k) x ↔ x = .atom k := by + constructor + · rintro ⟨fuel, h⟩; simp only [den] at h; exact (Option.some.inj h).symm + · rintro rfl; exact ⟨0, by simp [den]⟩ + +theorem Den_null {l r x} : Den (.node .null l r) x ↔ ∃ a b, x = .cell a b ∧ Den l a ∧ Den r b := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [den] at h + | succ f => + simp only [den] at h + cases hl : den f l with + | none => rw [hl] at h; simp at h + | some a => + cases hr : den f r with + | none => rw [hl, hr] at h; simp at h + | some b => + rw [hl, hr] at h; simp only [Option.some.injEq] at h + exact ⟨a, b, h.symm, ⟨f, hl⟩, ⟨f, hr⟩⟩ + · rintro ⟨a, b, rfl, hl, hr⟩ + obtain ⟨f, hl, hr⟩ := den_pair hl hr + exact ⟨f + 1, by simp only [den, hl, hr]⟩ + +theorem Den_star {l r x} : Den (.node .star l r) x ↔ ∃ a b, Den l a ∧ Den r b ∧ Ev a b x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [den] at h + | succ f => + simp only [den] at h + cases hl : den f l with + | none => rw [hl] at h; simp at h + | some a => + cases hr : den f r with + | none => rw [hl, hr] at h; simp at h + | some b => + rw [hl, hr] at h + exact ⟨a, b, ⟨f, hl⟩, ⟨f, hr⟩, ⟨f, h⟩⟩ + · rintro ⟨a, b, hl, hr, hev⟩ + obtain ⟨f, hl, hr⟩ := den_pair hl hr + obtain ⟨fe, hev⟩ := hev + refine ⟨max f fe + 1, ?_⟩ + simp only [den, den_mono (show f ≤ max f fe by omega) hl, den_mono (show f ≤ max f fe by omega) + hr] + exact evalN_mono (by omega) hev + +theorem Den_slot {l r x} : Den (.node .slot l r) x ↔ + ∃ ax b, Den l (.atom ax) ∧ Den r b ∧ Noun.slot ax b = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [den] at h + | succ f => + simp only [den] at h + split at h + · rename_i ax b hl hr + exact ⟨ax, b, ⟨f, hl⟩, ⟨f, hr⟩, h⟩ + · simp at h + · rintro ⟨ax, b, hl, hr, hs⟩ + obtain ⟨f, hl, hr⟩ := den_pair hl hr + exact ⟨f + 1, by simp only [den, hl, hr]; exact hs⟩ + +theorem Den_equal {l r x} : Den (.node .equal l r) x ↔ + ∃ a b, Den l a ∧ Den r b ∧ x = Noun.tis a b := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [den] at h + | succ f => + simp only [den] at h + split at h + · rename_i a b hl hr + exact ⟨a, b, ⟨f, hl⟩, ⟨f, hr⟩, (Option.some.inj h).symm⟩ + · simp at h + · rintro ⟨a, b, hl, hr, rfl⟩ + obtain ⟨f, hl, hr⟩ := den_pair hl hr + exact ⟨f + 1, by simp only [den, hl, hr]⟩ + +theorem Den_minus {l r x} : Den (.node .minus l r) x ↔ + ∃ b, Den r b ∧ ((Den l (.atom 3) ∧ x = Noun.wut b) ∨ (Den l (.atom 4) ∧ Noun.lus b = some x)) := + by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [den] at h + | succ f => + simp only [den] at h + split at h + · rename_i b hl hr + exact ⟨b, ⟨f, hr⟩, Or.inl ⟨⟨f, hl⟩, (Option.some.inj h).symm⟩⟩ + · rename_i b hl hr + exact ⟨b, ⟨f, hr⟩, Or.inr ⟨⟨f, hl⟩, h⟩⟩ + · simp at h + · rintro ⟨b, hr, hor⟩ + rcases hor with ⟨hl, rfl⟩ | ⟨hl, hlus⟩ + · obtain ⟨f, hl, hr⟩ := den_pair hl hr + exact ⟨f + 1, by simp only [den, hl, hr]⟩ + · obtain ⟨f, hl, hr⟩ := den_pair hl hr + exact ⟨f + 1, by simp only [den, hl, hr]; exact hlus⟩ + +theorem Den_edit_node {l b nw od x} : Den (.node .edit l (.node b nw od)) x ↔ + ∃ ax n o, Den l (.atom ax) ∧ Den nw n ∧ Den od o ∧ Noun.edit ax n o = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [den] at h + | succ f => + simp only [den] at h + split at h + · rename_i ax n o hl hnw hod + exact ⟨ax, n, o, ⟨f, hl⟩, ⟨f, hnw⟩, ⟨f, hod⟩, h⟩ + · simp at h + · rintro ⟨ax, n, o, hl, hnw, hod, he⟩ + obtain ⟨f, hl, hnw, hod⟩ := den_triple hl hnw hod + exact ⟨f + 1, by simp only [den, hl, hnw, hod]; exact he⟩ + +theorem Den_edit_leaf {l k x} : ¬ Den (.node .edit l (.leaf k)) x := by + rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [den] at h + | succ f => simp [den] at h + +theorem Den_wut {l r x} : ¬ Den (.node .wut l r) x := by + rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [den] at h + | succ f => simp [den] at h + +theorem ev2 {s1 f1 x1 s2 f2 x2} (h1 : Ev s1 f1 x1) (h2 : Ev s2 f2 x2) : + ∃ f, evalN f s1 f1 = some x1 ∧ evalN f s2 f2 = some x2 := by + obtain ⟨a, h1⟩ := h1; obtain ⟨b, h2⟩ := h2 + exact ⟨max a b, evalN_mono (Nat.le_max_left _ _) h1, evalN_mono (Nat.le_max_right _ _) h2⟩ + +theorem ev3 {s1 f1 x1 s2 f2 x2 s3 f3 x3} (h1 : Ev s1 f1 x1) (h2 : Ev s2 f2 x2) (h3 : Ev s3 f3 x3) : + ∃ f, evalN f s1 f1 = some x1 ∧ evalN f s2 f2 = some x2 ∧ evalN f s3 f3 = some x3 := by + obtain ⟨a, h1⟩ := h1; obtain ⟨b, h2⟩ := h2; obtain ⟨c, h3⟩ := h3 + exact ⟨max a (max b c), evalN_mono (by omega) h1, evalN_mono (by omega) h2, evalN_mono (by omega) + h3⟩ + +theorem EvN_autocons {subj hb hc d x} : Ev subj (.cell (.cell hb hc) d) x ↔ + ∃ l r, x = .cell l r ∧ Ev subj (.cell hb hc) l ∧ Ev subj d r := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + split at h + · rename_i l r hl hr + exact ⟨l, r, (Option.some.inj h).symm, ⟨f, hl⟩, ⟨f, hr⟩⟩ + · simp at h + · rintro ⟨l, r, rfl, hl, hr⟩ + obtain ⟨f, hl, hr⟩ := ev2 hl hr + exact ⟨f + 1, by simp only [evalN, hl, hr]⟩ + +theorem EvN_op0 {subj tail x} : Ev subj (.cell (.atom 0) tail) x ↔ + ∃ ax, tail = .atom ax ∧ Noun.slot ax subj = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + cases tail with + | atom ax => exact ⟨ax, rfl, h⟩ + | cell => simp at h + · rintro ⟨ax, rfl, hs⟩ + exact ⟨1, by simp only [evalN]; exact hs⟩ + +theorem EvN_op1 {subj tail x} : Ev subj (.cell (.atom 1) tail) x ↔ x = tail := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => simp only [evalN] at h; exact (Option.some.inj h).symm + · rintro rfl; exact ⟨1, by simp only [evalN]⟩ + +theorem EvN_op2 {subj tail x} : Ev subj (.cell (.atom 2) tail) x ↔ + ∃ hb hc sb sc, tail = .cell hb hc ∧ Ev subj hb sb ∧ Ev subj hc sc ∧ Ev sb sc x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + cases tail with + | atom => simp at h + | cell hb hc => + simp only at h + split at h + · rename_i sb sc hsb hsc + exact ⟨hb, hc, sb, sc, rfl, ⟨f, hsb⟩, ⟨f, hsc⟩, ⟨f, h⟩⟩ + · simp at h + · rintro ⟨hb, hc, sb, sc, rfl, hsb, hsc, hx⟩ + obtain ⟨f, hsb, hsc, hx⟩ := ev3 hsb hsc hx + exact ⟨f + 1, by simp only [evalN, hsb, hsc]; exact hx⟩ + +theorem EvN_op3 {subj tail x} : Ev subj (.cell (.atom 3) tail) x ↔ + ∃ y, Ev subj tail y ∧ x = Noun.wut y := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN, Option.map_eq_some_iff] at h + obtain ⟨y, hy, hx⟩ := h + exact ⟨y, ⟨f, hy⟩, hx.symm⟩ + · rintro ⟨y, ⟨fy, hy⟩, rfl⟩ + exact ⟨fy + 1, by simp only [evalN, hy, Option.map_some]⟩ + +theorem EvN_op4 {subj tail x} : Ev subj (.cell (.atom 4) tail) x ↔ + ∃ y, Ev subj tail y ∧ Noun.lus y = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN, Option.bind_eq_some_iff] at h + obtain ⟨y, hy, hx⟩ := h + exact ⟨y, ⟨f, hy⟩, hx⟩ + · rintro ⟨y, ⟨fy, hy⟩, hx⟩ + exact ⟨fy + 1, by simp only [evalN, hy]; exact hx⟩ + +theorem EvN_op5 {subj tail x} : Ev subj (.cell (.atom 5) tail) x ↔ + ∃ hb hc sb sc, tail = .cell hb hc ∧ Ev subj hb sb ∧ Ev subj hc sc ∧ x = Noun.tis sb sc := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + cases tail with + | atom => simp at h + | cell hb hc => + simp only at h + split at h + · rename_i sb sc hsb hsc + exact ⟨hb, hc, sb, sc, rfl, ⟨f, hsb⟩, ⟨f, hsc⟩, (Option.some.inj h).symm⟩ + · simp at h + · rintro ⟨hb, hc, sb, sc, rfl, hsb, hsc, rfl⟩ + obtain ⟨f, hsb, hsc⟩ := ev2 hsb hsc + exact ⟨f + 1, by simp only [evalN, hsb, hsc]⟩ + +theorem EvN_op7 {subj tail x} : Ev subj (.cell (.atom 7) tail) x ↔ + ∃ hb hc sb, tail = .cell hb hc ∧ Ev subj hb sb ∧ Ev sb hc x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + cases tail with + | atom => simp at h + | cell hb hc => + simp only at h + cases hsb : evalN f subj hb with + | none => rw [hsb] at h; simp at h + | some sb => rw [hsb] at h; exact ⟨hb, hc, sb, rfl, ⟨f, hsb⟩, ⟨f, h⟩⟩ + · rintro ⟨hb, hc, sb, rfl, hsb, hx⟩ + obtain ⟨f, hsb, hx⟩ := ev2 hsb hx + exact ⟨f + 1, by simp only [evalN, hsb]; exact hx⟩ + +theorem EvN_op8 {subj tail x} : Ev subj (.cell (.atom 8) tail) x ↔ + ∃ hb hc sb, tail = .cell hb hc ∧ Ev subj hb sb ∧ Ev (.cell sb subj) hc x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + cases tail with + | atom => simp at h + | cell hb hc => + simp only at h + cases hsb : evalN f subj hb with + | none => rw [hsb] at h; simp at h + | some sb => rw [hsb] at h; exact ⟨hb, hc, sb, rfl, ⟨f, hsb⟩, ⟨f, h⟩⟩ + · rintro ⟨hb, hc, sb, rfl, hsb, hx⟩ + obtain ⟨f, hsb, hx⟩ := ev2 hsb hx + exact ⟨f + 1, by simp only [evalN, hsb]; exact hx⟩ + +theorem EvN_op10 {subj tail x} : Ev subj (.cell (.atom 10) tail) x ↔ + ∃ ax c d new old, tail = .cell (.cell (.atom ax) c) d ∧ + Ev subj c new ∧ Ev subj d old ∧ Noun.edit ax new old = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + split at h + · rename_i ax c d + split at h + · rename_i new old hnew hold + exact ⟨ax, c, d, new, old, rfl, ⟨f, hnew⟩, ⟨f, hold⟩, h⟩ + · simp at h + · simp at h + · rintro ⟨ax, c, d, new, old, rfl, hnew, hold, he⟩ + obtain ⟨f, hnew, hold⟩ := ev2 hnew hold + refine ⟨f + 1, ?_⟩ + rw [evalN, hnew, hold] + exact he + +theorem EvN_op6 {subj tail x} : Ev subj (.cell (.atom 6) tail) x ↔ + ∃ b c d cv, tail = .cell b (.cell c d) ∧ Ev subj b cv ∧ + ((cv = .atom 0 ∧ Ev subj c x) ∨ (cv = .atom 1 ∧ Ev subj d x)) := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + cases tail with + | atom => simp at h + | cell b rest => + cases rest with + | atom => simp at h + | cell c d => + simp only at h + cases hb : evalN f subj b with + | none => rw [hb] at h; simp at h + | some bv => + cases bv with + | cell => rw [hb] at h; simp at h + | atom k => + rw [hb] at h + cases k with + | zero => exact ⟨b, c, d, .atom 0, rfl, ⟨f, hb⟩, Or.inl ⟨rfl, ⟨f, h⟩⟩⟩ + | succ k => + cases k with + | zero => exact ⟨b, c, d, .atom 1, rfl, ⟨f, hb⟩, Or.inr ⟨rfl, ⟨f, h⟩⟩⟩ + | succ k => simp at h + · rintro ⟨b, c, d, cv, rfl, hcv, hor⟩ + rcases hor with ⟨rfl, hc⟩ | ⟨rfl, hd⟩ + · obtain ⟨f, hcv, hc⟩ := ev2 hcv hc + refine ⟨f + 1, ?_⟩; rw [evalN, hcv]; exact hc + · obtain ⟨f, hcv, hd⟩ := ev2 hcv hd + refine ⟨f + 1, ?_⟩; rw [evalN, hcv]; exact hd + +theorem EvN_op9 {subj tail x} : Ev subj (.cell (.atom 9) tail) x ↔ + ∃ ax c core arm, tail = .cell (.atom ax) c ∧ Ev subj c core ∧ + Noun.slot ax core = some arm ∧ Ev core arm x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + cases tail with + | atom => simp at h + | cell head c => + cases head with + | cell => simp at h + | atom ax => + simp only at h + cases hcore : evalN f subj c with + | none => rw [hcore] at h; simp at h + | some core => + rw [hcore] at h + simp only at h + cases hslot : Noun.slot ax core with + | none => rw [hslot] at h; simp at h + | some arm => + rw [hslot] at h + exact ⟨ax, c, core, arm, rfl, ⟨f, hcore⟩, hslot, ⟨f, h⟩⟩ + · rintro ⟨ax, c, core, arm, rfl, hcore, hslot, hx⟩ + obtain ⟨f, hcore, hx⟩ := ev2 hcore hx + refine ⟨f + 1, ?_⟩ + rw [evalN]; simp only [hcore, hslot]; exact hx + +/-! ### helpers: determinism of `den`, terminal denotation, `ofNoun` facts -/ + +theorem den_det {v r1 r2} (h1 : Den v r1) (h2 : Den v r2) : r1 = r2 := by + obtain ⟨f1, h1⟩ := h1; obtain ⟨f2, h2⟩ := h2 + have k1 := den_mono (Nat.le_max_left f1 f2) h1 + have k2 := den_mono (Nat.le_max_right f1 f2) h2 + exact Option.some.inj (k1.symm.trans k2) + +theorem Den_terminal_iff {v x} (ht : isTerminal v = true) : Den v x ↔ x = v.noun := by + constructor + · intro hd; exact den_det hd ⟨v.noun.size, den_terminal v v.noun.size ht (Nat.le_refl _)⟩ + · rintro rfl; exact ⟨v.noun.size, den_terminal v v.noun.size ht (Nat.le_refl _)⟩ + +theorem ofNoun_noun (n : Noun) : (ofNoun n).noun = n := by + induction n with + | atom k => rfl + | cell l r ihl ihr => simp only [ofNoun, noun, ihl, ihr] + +theorem ofNoun_terminal (n : Noun) : isTerminal (ofNoun n) = true := by + induction n with + | atom k => rfl + | cell l r ihl ihr => simp [ofNoun, isTerminal, Action.isNull, ihl, ihr] + +theorem Den_ofNoun_iff {n x} : Den (ofNoun n) x ↔ x = n := by + rw [Den_terminal_iff (ofNoun_terminal n), ofNoun_noun] + +/-! ### well-formedness: edit nodes never carry a pending r-child -/ + +/-- Every edit node has a non-pending right child, recursively throughout the verb. -/ +def editOk : Verb → Bool + | .leaf _ => true + | .node a l r => + (match a with | .edit => !r.isPending | _ => true) && editOk l && editOk r + +theorem editOk_ofNoun (n : Noun) : editOk (ofNoun n) = true := by + induction n with + | atom k => rfl + | cell l r ihl ihr => simp [ofNoun, editOk, ihl, ihr] + +theorem editOk_program (s f : Noun) : editOk (program s f) = true := by + simp [program, editOk, editOk_ofNoun] + +theorem editOk_null_eq {l r} : editOk (.node .null l r) = (editOk l && editOk r) := rfl +theorem editOk_star_eq {l r} : editOk (.node .star l r) = (editOk l && editOk r) := rfl + +theorem editOk_children {a l r} (h : editOk (.node a l r) = true) : + editOk l = true ∧ editOk r = true := by + simp only [editOk, Bool.and_eq_true] at h; exact ⟨h.1.2, h.2⟩ + +/-! ### `slot` equation lemmas -/ + +theorem slot_one {n : Noun} : Noun.slot 1 n = some n := by simp [Noun.slot] +theorem slot_two {a b : Noun} : Noun.slot 2 (.cell a b) = some a := by simp [Noun.slot] +theorem slot_three {a b : Noun} : Noun.slot 3 (.cell a b) = some b := by simp [Noun.slot] +theorem slot_two_atom {k : Nat} : Noun.slot 2 (.atom k) = none := by simp [Noun.slot] +theorem slot_three_atom {k : Nat} : Noun.slot 3 (.atom k) = none := by simp [Noun.slot] +theorem lus_atom {k : Nat} : Noun.lus (.atom k) = some (.atom (k+1)) := by simp [Noun.lus] + +/-- On a pair of atoms `[p,q]`, only axes `1,2,3` are addressable. -/ +theorem slot_pair_some {p q : Nat} : ∀ (m : Nat) (v : Noun), + Noun.slot m (.cell (.atom p) (.atom q)) = some v → m = 1 ∨ m = 2 ∨ m = 3 := by + intro m + induction m using Nat.strongRecOn with + | ind m ih => + intro v hv + match m with + | 0 => simp [Noun.slot] at hv + | 1 => exact Or.inl rfl + | 2 => exact Or.inr (Or.inl rfl) + | 3 => exact Or.inr (Or.inr rfl) + | (k+4) => + exfalso + rw [Noun.slot] at hv + cases hs : Noun.slot ((k+4)/2) (.cell (.atom p) (.atom q)) with + | none => rw [hs] at hv; simp at hv + | some w => + rw [hs] at hv + rcases ih ((k+4)/2) (by omega) w hs with h1 | h2 | h3 + · omega + · rw [h2, slot_two] at hs; obtain rfl := Option.some.inj hs; simp at hv + · rw [h3, slot_three] at hs; obtain rfl := Option.some.inj hs; simp at hv + +/-! ### STEP-LOCAL: a `reduce` on an operator-ready node preserves `Den` -/ + +theorem den_local_opReady {sub r₀ x} (hor : opReady sub = true) (hr : reduce sub = some r₀) : + (Den sub x ↔ Den r₀ x) := by + cases sub with + | leaf => simp [opReady] at hor + | node a l r => + simp only [opReady, Bool.and_eq_true] at hor + obtain ⟨⟨hao, htl⟩, htr⟩ := hor + have hDl : ∀ v, Den l v ↔ v = l.noun := fun v => Den_terminal_iff htl + have hDr : ∀ v, Den r v ↔ v = r.noun := fun v => Den_terminal_iff htr + have atominj : ∀ {ax' ax : Nat}, Den l (.atom ax') → l.noun = .atom ax → ax' = ax := by + intro ax' ax hd he; have := (hDl _).1 hd; rw [he] at this; exact Noun.atom.inj this + cases a with + | null => simp [Action.isOperator] at hao + | star => simp [Action.isOperator] at hao + | wut => simp [reduce] at hr + | equal => + rw [reduce] at hr; obtain rfl := Option.some.inj hr + rw [Den_equal, Den_ofNoun_iff] + constructor + · rintro ⟨va, vb, hva, hvb, rfl⟩; rw [(hDl va).1 hva, (hDr vb).1 hvb] + · rintro rfl; exact ⟨l.noun, r.noun, (hDl _).2 rfl, (hDr _).2 rfl, rfl⟩ + | slot => + rw [reduce] at hr + rw [Den_slot] + split at hr + · rename_i ax heq + rw [Option.map_eq_some_iff] at hr + obtain ⟨sr, hslot, rfl⟩ := hr + rw [Den_ofNoun_iff] + constructor + · rintro ⟨ax', b, hax', hb, hs⟩ + obtain rfl := atominj hax' heq + rw [(hDr _).1 hb] at hs; rw [hs] at hslot; exact (Option.some.inj hslot) + · rintro rfl + exact ⟨ax, r.noun, (hDl _).2 heq.symm, (hDr _).2 rfl, hslot⟩ + · simp at hr + | minus => + rw [reduce] at hr + rw [Den_minus] + split at hr + · rename_i heq + obtain rfl := Option.some.inj hr + rw [Den_ofNoun_iff] + constructor + · rintro ⟨b, hb, hor2⟩ + rcases hor2 with ⟨_, hx⟩ | ⟨h4, _⟩ + · rw [(hDr _).1 hb] at hx; exact hx + · exact absurd (atominj h4 heq) (by decide) + · rintro rfl; exact ⟨r.noun, (hDr _).2 rfl, Or.inl ⟨(hDl _).2 heq.symm, rfl⟩⟩ + · rename_i heq + rw [Option.map_eq_some_iff] at hr + obtain ⟨sr, hlus, rfl⟩ := hr + rw [Den_ofNoun_iff] + constructor + · rintro ⟨b, hb, hor2⟩ + rcases hor2 with ⟨h3, _⟩ | ⟨_, hlus2⟩ + · exact absurd (atominj h3 heq) (by decide) + · rw [(hDr _).1 hb] at hlus2; rw [hlus2] at hlus; exact (Option.some.inj hlus) + · rintro rfl; exact ⟨r.noun, (hDr _).2 rfl, Or.inr ⟨(hDl _).2 heq.symm, hlus⟩⟩ + · simp at hr + | edit => + cases hln : l.noun with + | cell => simp [reduce, hln] at hr + | atom ax => + cases hr2 : r with + | leaf => simp [reduce, hln, hr2] at hr + | node b nw od => + simp only [reduce, hln, hr2, Option.map_eq_some_iff] at hr + obtain ⟨er, hedit, rfl⟩ := hr + have htr2 : isTerminal (.node b nw od) = true := hr2 ▸ htr + simp only [isTerminal, Bool.and_eq_true] at htr2 + obtain ⟨⟨_, htnw⟩, htod⟩ := htr2 + have hDnw : ∀ v, Den nw v ↔ v = nw.noun := fun v => Den_terminal_iff htnw + have hDod : ∀ v, Den od v ↔ v = od.noun := fun v => Den_terminal_iff htod + rw [Den_edit_node, Den_ofNoun_iff] + constructor + · rintro ⟨ax', n, o, hax', hn, ho, he⟩ + obtain rfl := atominj hax' hln + rw [(hDnw _).1 hn, (hDod _).1 ho] at he + rw [he] at hedit; exact (Option.some.inj hedit) + · rintro rfl + exact ⟨ax, nw.noun, od.noun, (hDl _).2 hln.symm, (hDnw _).2 rfl, (hDod _).2 rfl, + hedit⟩ + +/-! ### STEP-LOCAL: a `reduce` (opcode `redex`) on an in-`D` node preserves `Den` -/ + +-- main.tex:1060-1076 (STEP-LOCAL: each opcode redex `reduce` preserves the denotation) +private theorem denotation_conditional_reduction {r₀ : Verb} {x : Noun} (n1 n2 : Verb) (htn2 : + n2.isTerminal = true) + (hn1 : ∀ (v : Noun), n1.Den v ↔ v = n1.noun) + (star_term : ∀ (W : Verb) (p : Noun), W.isTerminal = true → ((node Action.star n1 W).Den p ↔ Ev + n1.noun W.noun p)) + (hr : + (match (generalizing := false) n2 with + | node _ b c => + some + (node Action.star n1 + (node Action.star c + (node Action.null (leaf 0) + (node Action.star (node Action.null (leaf 2) (leaf 3)) + (node Action.null (leaf 0) + (node Action.star n1 (node Action.null (leaf 4) (node Action.null (leaf 4) + b)))))))) + | _ => none) = + some r₀) : + Ev n1.noun ((atom 6).cell n2.noun) x ↔ r₀.Den x := by + cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + have dStar : ∀ (W : Verb) y, + (Den (Verb.node .star n1 W) y ↔ ∃ q, Den W q ∧ Ev n1.noun q y) := by + intro W y; rw [Den_star] + exact ⟨fun ⟨p,q,hp,hq,hev⟩ => ⟨q, hq, (hn1 p).1 hp ▸ hev⟩, + fun ⟨q,hq,hev⟩ => ⟨n1.noun, q, (hn1 _).2 rfl, hq, hev⟩⟩ + have dStarC : ∀ (W : Verb) y, + (Den (Verb.node .star c W) y ↔ ∃ q, Den W q ∧ Ev c.noun q y) := by + intro W y; rw [Den_star] + exact ⟨fun ⟨p,q,hp,hq,hev⟩ => ⟨q, hq, (Den_terminal_iff htc).1 hp ▸ hev⟩, + fun ⟨q,hq,hev⟩ => ⟨c.noun, q, (Den_terminal_iff htc).2 rfl, hq, hev⟩⟩ + have hIarg : isTerminal (Verb.node .null (.leaf 4) (.node .null (.leaf 4) b)) = true := by + simp [isTerminal, Action.isNull, htb] + rw [EvN_op6] + constructor + · -- EvN_op6 ⇒ Den (macro) + rintro ⟨B, C, D, cv, heq, hcv, hor⟩ + obtain ⟨hBeq, hCDeq⟩ := Noun.cell.inj heq + subst hBeq + rcases hor with ⟨rfl, hCx⟩ | ⟨rfl, hDx⟩ + · -- cv = 0 → head arm + refine (dStar _ _).2 ⟨C, ?_, hCx⟩ + refine (dStarC _ _).2 ⟨Noun.cell (.atom 0) (.atom 2), ?_, + (EvN_op0).2 ⟨2, rfl, hCDeq ▸ slot_two⟩⟩ + refine (Den_null).2 ⟨.atom 0, .atom 2, rfl, Den_leaf.2 rfl, ?_⟩ + refine (Den_star).2 ⟨Noun.cell (.atom 2) (.atom 3), Noun.cell (.atom 0) (.atom 2), + (Den_null).2 ⟨.atom 2, .atom 3, rfl, Den_leaf.2 rfl, Den_leaf.2 rfl⟩, ?_, + (EvN_op0).2 ⟨2, rfl, slot_two⟩⟩ + refine (Den_null).2 ⟨.atom 0, .atom 2, rfl, Den_leaf.2 rfl, ?_⟩ + refine (dStar _ _).2 ⟨_, (Den_terminal_iff hIarg).2 rfl, ?_⟩ + exact (EvN_op4).2 ⟨.atom 1, (EvN_op4).2 ⟨.atom 0, hcv, lus_atom⟩, lus_atom⟩ + · -- cv = 1 → tail arm + refine (dStar _ _).2 ⟨D, ?_, hDx⟩ + refine (dStarC _ _).2 ⟨Noun.cell (.atom 0) (.atom 3), ?_, + (EvN_op0).2 ⟨3, rfl, hCDeq ▸ slot_three⟩⟩ + refine (Den_null).2 ⟨.atom 0, .atom 3, rfl, Den_leaf.2 rfl, ?_⟩ + refine (Den_star).2 ⟨Noun.cell (.atom 2) (.atom 3), Noun.cell (.atom 0) (.atom 3), + (Den_null).2 ⟨.atom 2, .atom 3, rfl, Den_leaf.2 rfl, Den_leaf.2 rfl⟩, ?_, + (EvN_op0).2 ⟨3, rfl, slot_three⟩⟩ + refine (Den_null).2 ⟨.atom 0, .atom 3, rfl, Den_leaf.2 rfl, ?_⟩ + refine (dStar _ _).2 ⟨_, (Den_terminal_iff hIarg).2 rfl, ?_⟩ + exact (EvN_op4).2 ⟨.atom 2, (EvN_op4).2 ⟨.atom 1, hcv, lus_atom⟩, lus_atom⟩ + · -- Den (macro) ⇒ EvN_op6 + intro hmacro + obtain ⟨qa0, ha0, hC4⟩ := (dStar _ _).1 hmacro + obtain ⟨qb0, hb0, hC3⟩ := (dStarC _ _).1 ha0 + rw [Den_null] at hb0; obtain ⟨v0, vc0, rfl, hv0, hc0⟩ := hb0 + rw [Den_leaf] at hv0; subst hv0 + rw [Den_star] at hc0; obtain ⟨p23, qd0, hp23, hd0, hC2⟩ := hc0 + rw [Den_null] at hp23; obtain ⟨w2, w3, rfl, hw2, hw3⟩ := hp23 + rw [Den_leaf] at hw2 hw3; subst hw2; subst hw3 + rw [Den_null] at hd0; obtain ⟨u0, vinner, rfl, hu0, hinner⟩ := hd0 + rw [Den_leaf] at hu0; subst hu0 + rw [star_term _ _ hIarg] at hinner + obtain ⟨y, hy1, hly⟩ := (EvN_op4).1 hinner + obtain ⟨z, hz1, hlz⟩ := (EvN_op4).1 hy1 + cases z with + | cell => simp [Noun.lus] at hlz + | atom k => + rw [lus_atom] at hlz; obtain rfl := Option.some.inj hlz + rw [lus_atom] at hly; obtain rfl := Option.some.inj hly + obtain ⟨ax, hax, hs23⟩ := (EvN_op0).1 hC2 + have haxv : ax = k + 2 := by injection hax with h; omega + subst haxv + rcases slot_pair_some _ _ hs23 with h1 | h2 | h3 + · omega + · -- k = 0 + have hk : k = 0 := by omega + subst hk + rw [slot_two] at hs23; obtain rfl := Option.some.inj hs23 + obtain ⟨ax', hax', hs3⟩ := (EvN_op0).1 hC3 + have : ax' = 2 := by injection hax' with h; omega + subst this + cases hcn : c.noun with + | atom => rw [hcn, slot_two_atom] at hs3; simp at hs3 + | cell C D => + rw [hcn, slot_two] at hs3; obtain rfl := Option.some.inj hs3 + exact ⟨b.noun, C, D, .atom 0, rfl, hz1, Or.inl ⟨rfl, hC4⟩⟩ + · -- k = 1 + have hk : k = 1 := by omega + subst hk + rw [slot_three] at hs23; obtain rfl := Option.some.inj hs23 + obtain ⟨ax', hax', hs3⟩ := (EvN_op0).1 hC3 + have : ax' = 3 := by injection hax' with h; omega + subst this + cases hcn : c.noun with + | atom => rw [hcn, slot_three_atom] at hs3; simp at hs3 + | cell C D => + rw [hcn, slot_three] at hs3; obtain rfl := Option.some.inj hs3 + exact ⟨b.noun, C, D, .atom 1, rfl, hz1, Or.inr ⟨rfl, hC4⟩⟩ + +private theorem denotation_composition_reduction {r₀ : Verb} {x : Noun} (n1 n2 : Verb) (htn2 : + n2.isTerminal = true) + (star_term : ∀ (W : Verb) (p : Noun), W.isTerminal = true → ((node Action.star n1 W).Den p ↔ Ev + n1.noun W.noun p)) + (hr : + (match (generalizing := false) n2 with + | node _ b c => some (node Action.star (node Action.star n1 b) (node Action.star n1 c)) + | _ => none) = + some r₀) : + Ev n1.noun ((atom 2).cell n2.noun) x ↔ r₀.Den x := by + cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + rw [EvN_op2, Den_star] + constructor + · rintro ⟨hb, hc, sb, sc, heq, hsb, hsc, hev⟩ + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq + exact ⟨sb, sc, (star_term _ _ htb).2 hsb, (star_term _ _ htc).2 hsc, hev⟩ + · rintro ⟨sb, sc, hsb, hsc, hev⟩ + exact ⟨b.noun, c.noun, sb, sc, rfl, (star_term _ _ htb).1 hsb, + (star_term _ _ htc).1 hsc, hev⟩ + +theorem den_local_opcode {sub r₀ x} (hd : inOpcodeDomain sub = true) (hr : reduce sub = some r₀) : + (Den sub x ↔ Den r₀ x) := by + cases sub with + | leaf => simp [inOpcodeDomain] at hd + | node a n1 t => + cases t with + | leaf => cases a <;> simp [inOpcodeDomain] at hd + | node ra i n2 => + cases a with + | null => simp [inOpcodeDomain] at hd + | wut => simp [inOpcodeDomain] at hd + | equal => simp [inOpcodeDomain] at hd + | slot => simp [inOpcodeDomain] at hd + | edit => simp [inOpcodeDomain] at hd + | minus => simp [inOpcodeDomain] at hd + | star => + simp only [inOpcodeDomain, Bool.and_eq_true] at hd + obtain ⟨⟨⟨⟨htn1, hra⟩, hti⟩, htn2⟩, hhead⟩ := hd + have htt : isTerminal (Verb.node ra i n2) = true := by + simp only [isTerminal, Bool.and_eq_true]; exact ⟨⟨hra, hti⟩, htn2⟩ + have hn1 : ∀ v, Den n1 v ↔ v = n1.noun := fun v => Den_terminal_iff htn1 + have hn2 : ∀ v, Den n2 v ↔ v = n2.noun := fun v => Den_terminal_iff htn2 + have star_term : ∀ (W : Verb) (p : Noun), isTerminal W = true → + (Den (Verb.node .star n1 W) p ↔ Ev n1.noun W.noun p) := by + intro W p htW + rw [Den_star] + constructor + · rintro ⟨va, vb, hva, hvb, hev⟩ + rw [(Den_terminal_iff htn1).1 hva, (Den_terminal_iff htW).1 hvb] at hev; exact hev + · intro hev + exact ⟨n1.noun, W.noun, (Den_terminal_iff htn1).2 rfl, (Den_terminal_iff htW).2 rfl, + hev⟩ + have hsub : (Den (Verb.node .star n1 (.node ra i n2)) x + ↔ Ev n1.noun (Noun.cell i.noun n2.noun) x) := by + rw [star_term (Verb.node ra i n2) x htt]; simp only [noun] + rw [hsub] + rw [reduce] at hr + cases i with + | node bi ic id => + rw [redex] at hr + obtain rfl := Option.some.inj hr + simp only [noun] + rw [Den_null, EvN_autocons] + constructor + · rintro ⟨l, r, rfl, hl, hr⟩ + refine ⟨l, r, rfl, (star_term _ _ hti).2 ?_, (star_term _ _ htn2).2 hr⟩ + simpa [noun] using hl + · rintro ⟨p, q, rfl, hp, hq⟩ + refine ⟨p, q, rfl, ?_, (star_term _ _ htn2).1 hq⟩ + have := (star_term (Verb.node bi ic id) p hti).1 hp + simpa [noun] using this + | leaf op => + simp only [noun] + simp only [redex] at hr + split at hr + · -- op 0 + obtain rfl := Option.some.inj hr + rw [EvN_op0, Den_slot] + constructor + · rintro ⟨ax, hax, hs⟩; exact ⟨ax, n1.noun, (hn2 _).2 hax.symm, (hn1 _).2 rfl, hs⟩ + · rintro ⟨ax, b, hax, hb, hs⟩ + rw [(hn1 _).1 hb] at hs; exact ⟨ax, ((hn2 _).1 hax).symm, hs⟩ + · -- op 1 + obtain rfl := Option.some.inj hr + rw [EvN_op1]; exact (hn2 x).symm + · -- op 2 + exact denotation_composition_reduction n1 n2 htn2 star_term hr + · -- op 3 + obtain rfl := Option.some.inj hr + rw [EvN_op3, Den_minus] + constructor + · rintro ⟨y, hy, rfl⟩ + exact ⟨y, (star_term _ _ htn2).2 hy, Or.inl ⟨(Den_leaf).2 rfl, rfl⟩⟩ + · rintro ⟨b, hb, hor⟩ + rcases hor with ⟨_, rfl⟩ | ⟨h4, _⟩ + · exact ⟨b, (star_term _ _ htn2).1 hb, rfl⟩ + · exact absurd ((Den_leaf).1 h4) (by decide) + · -- op 4 + obtain rfl := Option.some.inj hr + rw [EvN_op4, Den_minus] + constructor + · rintro ⟨y, hy, hlus⟩ + exact ⟨y, (star_term _ _ htn2).2 hy, Or.inr ⟨(Den_leaf).2 rfl, hlus⟩⟩ + · rintro ⟨b, hb, hor⟩ + rcases hor with ⟨h3, _⟩ | ⟨_, hlus⟩ + · exact absurd ((Den_leaf).1 h3) (by decide) + · exact ⟨b, (star_term _ _ htn2).1 hb, hlus⟩ + · -- op 5 + cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + rw [EvN_op5, Den_equal] + constructor + · rintro ⟨hb, hc, sb, sc, heq, hsb, hsc, rfl⟩ + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq + exact ⟨sb, sc, (star_term _ _ htb).2 hsb, (star_term _ _ htc).2 hsc, rfl⟩ + · rintro ⟨sb, sc, hsb, hsc, rfl⟩ + exact ⟨b.noun, c.noun, sb, sc, rfl, (star_term _ _ htb).1 hsb, + (star_term _ _ htc).1 hsc, rfl⟩ + · -- op 6 (if-then-else macro; main.tex:1067) + exact denotation_conditional_reduction n1 n2 htn2 hn1 star_term hr + · -- op 7 + cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + rw [EvN_op7, Den_star] + constructor + · rintro ⟨hb, hc, sb, heq, hsb, hev⟩ + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq + exact ⟨sb, c.noun, (star_term _ _ htb).2 hsb, (Den_terminal_iff htc).2 rfl, hev⟩ + · rintro ⟨p, q, hp, hq, hev⟩ + rw [(Den_terminal_iff htc).1 hq] at hev + exact ⟨b.noun, c.noun, p, rfl, (star_term _ _ htb).1 hp, hev⟩ + · -- op 8 + cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + rw [EvN_op8, Den_star] + constructor + · rintro ⟨hb, hc, sb, heq, hsb, hev⟩ + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq + refine ⟨Noun.cell sb n1.noun, c.noun, ?_, (Den_terminal_iff htc).2 rfl, hev⟩ + rw [Den_null] + exact ⟨sb, n1.noun, rfl, (star_term _ _ htb).2 hsb, (hn1 _).2 rfl⟩ + · rintro ⟨p, q, hp, hq, hev⟩ + rw [Den_null] at hp + obtain ⟨pb, pn, rfl, hpb, hpn⟩ := hp + rw [(hn1 _).1 hpn] at hev + rw [(Den_terminal_iff htc).1 hq] at hev + exact ⟨b.noun, c.noun, pb, rfl, (star_term _ _ htb).1 hpb, hev⟩ + · -- op 9 + cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + have htform : isTerminal (Verb.node .null (.leaf 2) + (.node .null (.node .null (.leaf 0) (.leaf 1)) (.node .null (.leaf 0) b))) = + true := by + simp [isTerminal, Action.isNull, htb] + rw [EvN_op9, Den_star] + constructor + · rintro ⟨ax, c', core, arm, heq, hcore, hslot, hev⟩ + obtain ⟨hbn, rfl⟩ := Noun.cell.inj heq + refine ⟨core, _, (star_term c core htc).2 hcore, + (Den_terminal_iff htform).2 rfl, ?_⟩ + refine (EvN_op2).2 ⟨_, _, core, arm, rfl, ?_, ?_, hev⟩ + · exact (EvN_op0).2 ⟨1, rfl, slot_one⟩ + · exact (EvN_op0).2 ⟨ax, hbn, hslot⟩ + · rintro ⟨p, q, hp, hq, hev⟩ + have hp' := (star_term c p htc).1 hp + rw [(Den_terminal_iff htform).1 hq] at hev + obtain ⟨hb2, hc2, sb, sc, heq2, hsb, hsc, hev2⟩ := (EvN_op2).1 hev + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq2 + obtain ⟨ax1, hax1, hs1⟩ := (EvN_op0).1 hsb + obtain ⟨ax2, hax2, hs2⟩ := (EvN_op0).1 hsc + have hax1' : ax1 = 1 := by injection hax1 with h; omega + rw [hax1', slot_one] at hs1; obtain rfl := Option.some.inj hs1 + exact ⟨ax2, c.noun, p, sc, by rw [hax2], hp', hs2, hev2⟩ + · -- op 10 + cases n2 with + | leaf => simp at hr + | node bn hd d => + cases hd with + | leaf => simp at hr + | node bhd ax c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, ⟨_, htax⟩, htc⟩, htd⟩ := htn2 + have hax : ∀ v, Den ax v ↔ v = ax.noun := fun v => Den_terminal_iff htax + rw [EvN_op10, Den_edit_node] + constructor + · rintro ⟨axv, cc, dd, new, old, heq, hnew, hold, hedit⟩ + obtain ⟨heq1, rfl⟩ := Noun.cell.inj heq + obtain ⟨haxn, rfl⟩ := Noun.cell.inj heq1 + exact ⟨axv, new, old, (hax _).2 haxn.symm, (star_term _ _ htc).2 hnew, + (star_term _ _ htd).2 hold, hedit⟩ + · rintro ⟨axv, n, o, haxd, hn, ho, hedit⟩ + refine ⟨axv, c.noun, d.noun, n, o, ?_, (star_term _ _ htc).1 hn, + (star_term _ _ htd).1 ho, hedit⟩ + rw [(hax _).1 haxd] + · -- default + simp at hr + +/-! ### STEP-CONG: `den`-congruence for splicing a den-equivalent redex back via `replaceAt` -/ + +/-- Structural relation: `w` is `v` with the subtree at one *pending* node replaced by a + `Den`-equivalent verb. `base` is restricted to pending nodes so it can never sit at an + `edit` node's `⊥` r-child (where `den` reads structure), the one place congruence needs care. -/ +inductive Rel : Verb → Verb → Prop where + | leaf {k} : Rel (.leaf k) (.leaf k) + | base {v w} : v.isPending = true → (∀ x, Den v x ↔ Den w x) → Rel v w + | cong {a l l' r r'} : Rel l l' → Rel r r' → Rel (.node a l r) (.node a l' r') + +theorem Rel.refl : ∀ v, Rel v v + | .leaf _ => .leaf + | .node _ l r => .cong (Rel.refl l) (Rel.refl r) + +theorem replaceAux_counter (i : Nat) (new : Verb) : ∀ (v : Verb) (c : Nat), + (replaceAux i new v c).1 = (enum v c).1 := by + intro v + induction v with + | leaf k => intro c; simp [replaceAux, enum] + | node a l r ihl ihr => + intro c + simp only [replaceAux, enum] + rw [ihl (c+1), ihr] + +theorem replaceAux_rel (i : Nat) (r₀ : Verb) : + ∀ (v : Verb) (c : Nat), + (∀ sub, findAt i (enum v c).2 = some sub → + sub.isPending = true ∧ (∀ x, Den sub x ↔ Den r₀ x)) → + Rel v (replaceAux i r₀ v c).2 := by + intro v + induction v with + | leaf k => intro c _; exact Rel.leaf + | node a l r ihl ihr => + intro c hyp + simp only [replaceAux] + by_cases hci : c = i + · subst hci + have hf : findAt c (enum (.node a l r) c).2 = some (.node a l r) := by + simp [enum, findAt] + obtain ⟨hp, hd⟩ := hyp _ hf + rw [ite_eq_left rfl] + exact Rel.base hp hd + · simp only [ite_eq_right hci] + have hine : ¬ (i = c) := fun h => hci h.symm + have hcL : (replaceAux i r₀ l (c+1)).1 = (enum l (c+1)).1 := replaceAux_counter i r₀ l (c+1) + refine Rel.cong (ihl (c+1) ?_) ?_ + · intro sub hsub + apply hyp sub + simp only [enum, findAt, ite_eq_right hine] + rw [findAt_append, hsub] + · rw [hcL] + apply ihr + intro sub hsub + apply hyp sub + simp only [enum, findAt, ite_eq_right hine] + rw [findAt_append] + have hnone : findAt i (enum l (c+1)).2 = none := by + apply findAt_eq_none + intro q hq + have := enum_index_range l (c+1) q hq + have := enum_index_range r (enum l (c+1)).1 (i, sub) (findAt_some_mem hsub) + omega + rw [hnone, hsub] + +/-- **STEP-CONG (den-preservation of `Rel`).** If `Rel v w` and `v` is edit-well-formed, + then `v` and `w` have the same denotation. -/ +theorem Rel_den : ∀ (n : Nat) (v w : Verb), v.noun.size ≤ n → Rel v w → editOk v = true → + ∀ x, (Den v x ↔ Den w x) := by + intro n + induction n with + | zero => intro v w hn _ _ x; cases v <;> simp [Noun.size, Verb.noun] at hn + | succ n ih => + intro v w hn hrel hok x + cases hrel with + | leaf => exact Iff.rfl + | base _ hd => exact hd x + | @cong a l l' r r' hl hr => + simp only [Verb.noun, Noun.size] at hn + simp only [editOk, Bool.and_eq_true] at hok + obtain ⟨⟨hcond, hokl⟩, hokr⟩ := hok + have El : ∀ y, Den l y ↔ Den l' y := fun y => ih l l' (by omega) hl hokl y + have Er : ∀ y, Den r y ↔ Den r' y := fun y => ih r r' (by omega) hr hokr y + cases a with + | null => + rw [Den_null, Den_null] + exact ⟨fun ⟨p,q,he,hp,hq⟩ => ⟨p,q,he,(El p).1 hp,(Er q).1 hq⟩, + fun ⟨p,q,he,hp,hq⟩ => ⟨p,q,he,(El p).2 hp,(Er q).2 hq⟩⟩ + | star => + rw [Den_star, Den_star] + exact ⟨fun ⟨p,q,hp,hq,he⟩ => ⟨p,q,(El p).1 hp,(Er q).1 hq,he⟩, + fun ⟨p,q,hp,hq,he⟩ => ⟨p,q,(El p).2 hp,(Er q).2 hq,he⟩⟩ + | slot => + rw [Den_slot, Den_slot] + exact ⟨fun ⟨ax,b,hp,hq,hs⟩ => ⟨ax,b,(El _).1 hp,(Er _).1 hq,hs⟩, + fun ⟨ax,b,hp,hq,hs⟩ => ⟨ax,b,(El _).2 hp,(Er _).2 hq,hs⟩⟩ + | equal => + rw [Den_equal, Den_equal] + exact ⟨fun ⟨p,q,hp,hq,he⟩ => ⟨p,q,(El p).1 hp,(Er q).1 hq,he⟩, + fun ⟨p,q,hp,hq,he⟩ => ⟨p,q,(El p).2 hp,(Er q).2 hq,he⟩⟩ + | minus => + rw [Den_minus, Den_minus] + exact ⟨fun ⟨b,hq,hor⟩ => ⟨b,(Er _).1 hq, + hor.imp (fun ⟨h,e⟩ => ⟨(El _).1 h,e⟩) (fun ⟨h,e⟩ => ⟨(El _).1 h,e⟩)⟩, + fun ⟨b,hq,hor⟩ => ⟨b,(Er _).2 hq, + hor.imp (fun ⟨h,e⟩ => ⟨(El _).2 h,e⟩) (fun ⟨h,e⟩ => ⟨(El _).2 h,e⟩)⟩⟩ + | wut => + exact ⟨fun h => (Den_wut h).elim, fun h => (Den_wut h).elim⟩ + | edit => + cases hr with + | leaf => exact ⟨fun h => (Den_edit_leaf h).elim, fun h => (Den_edit_leaf h).elim⟩ + | base hp _ => + rw [hp] at hcond; simp at hcond + | @cong b nw nw' od od' hnw hod => + simp only [Verb.noun, Noun.size] at hn + simp only [editOk, Bool.and_eq_true] at hokr + obtain ⟨⟨_, hoknw⟩, hokod⟩ := hokr + have Enw : ∀ y, Den nw y ↔ Den nw' y := fun y => ih nw nw' (by omega) hnw hoknw y + have Eod : ∀ y, Den od y ↔ Den od' y := fun y => ih od od' (by omega) hod hokod y + rw [Den_edit_node, Den_edit_node] + exact ⟨fun ⟨ax,nn,oo,ha,hn2,ho,he⟩ => + ⟨ax,nn,oo,(El _).1 ha,(Enw _).1 hn2,(Eod _).1 ho,he⟩, + fun ⟨ax,nn,oo,ha,hn2,ho,he⟩ => + ⟨ax,nn,oo,(El _).2 ha,(Enw _).2 hn2,(Eod _).2 ho,he⟩⟩ + +/-! ### `editOk` is preserved by `next` (the invariant discharging `den_step`'s hypothesis) -/ + +theorem editOk_reduce {sub r₀} (hr : reduce sub = some r₀) (hok : editOk sub = true) : + editOk r₀ = true := by + cases sub with + | leaf => simp [reduce] at hr + | node a n1 t => + simp only [editOk, Bool.and_eq_true] at hok + obtain ⟨⟨_, hokn1⟩, hokt⟩ := hok + cases a with + | null => simp [reduce] at hr + | wut => simp [reduce] at hr + | equal => rw [reduce] at hr; obtain rfl := Option.some.inj hr; exact editOk_ofNoun _ + | slot => + rw [reduce] at hr + cases hln : n1.noun with + | cell => simp [hln] at hr + | atom ax => + rw [hln, Option.map_eq_some_iff] at hr + obtain ⟨sr, _, rfl⟩ := hr; exact editOk_ofNoun _ + | minus => + rw [reduce] at hr + split at hr + · obtain rfl := Option.some.inj hr; exact editOk_ofNoun _ + · rw [Option.map_eq_some_iff] at hr; obtain ⟨sr, _, rfl⟩ := hr; exact editOk_ofNoun _ + · simp at hr + | edit => + cases hln : n1.noun with + | cell => simp [reduce, hln] at hr + | atom ax => + cases t with + | leaf => simp [reduce, hln] at hr + | node b nw od => + simp only [reduce, hln, Option.map_eq_some_iff] at hr + obtain ⟨sr, _, rfl⟩ := hr; exact editOk_ofNoun _ + | star => + -- redex + cases t with + | leaf => simp [reduce, redex] at hr + | node ra i n2 => + simp only [editOk, Bool.and_eq_true] at hokt + obtain ⟨⟨_, hoki⟩, hokn2⟩ := hokt + rw [reduce] at hr + cases i with + | node bi ic id => + rw [redex] at hr + obtain rfl := Option.some.inj hr + simp only [editOk_null_eq, editOk_star_eq, hokn1, hoki, hokn2, Bool.and_self] + | leaf op => + simp only [redex] at hr + have ecell : ∀ (bn b c : _), n2 = Verb.node bn b c → + editOk b = true ∧ editOk c = true := by + intro bn b c he; subst he; exact editOk_children hokn2 + split at hr + · obtain rfl := Option.some.inj hr; simp [editOk, hokn1, hokn2] + · obtain rfl := Option.some.inj hr; exact hokn2 + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain ⟨hokb, hokc⟩ := ecell bn b c rfl + obtain rfl := Option.some.inj hr; simp [editOk, hokn1, hokb, hokc] + · obtain rfl := Option.some.inj hr; simp [editOk, hokn1, hokn2] + · obtain rfl := Option.some.inj hr; simp [editOk, hokn1, hokn2] + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain ⟨hokb, hokc⟩ := ecell bn b c rfl + obtain rfl := Option.some.inj hr; simp [editOk, hokn1, hokb, hokc] + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain ⟨hokb, hokc⟩ := ecell bn b c rfl + obtain rfl := Option.some.inj hr + simp [editOk, hokn1, hokb, hokc] + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain ⟨hokb, hokc⟩ := ecell bn b c rfl + obtain rfl := Option.some.inj hr; simp [editOk, hokn1, hokb, hokc] + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain ⟨hokb, hokc⟩ := ecell bn b c rfl + obtain rfl := Option.some.inj hr + simp [editOk, hokn1, hokb, hokc] + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain ⟨hokb, hokc⟩ := ecell bn b c rfl + obtain rfl := Option.some.inj hr + simp [editOk, hokn1, hokb, hokc] + · cases n2 with + | leaf => simp at hr + | node bn hd d => + cases hd with + | leaf => simp at hr + | node bhd ax c => + obtain ⟨hokhd, hokd⟩ := editOk_children hokn2 + obtain ⟨hokax, hokc⟩ := editOk_children hokhd + obtain rfl := Option.some.inj hr + simp [editOk, Verb.isPending, Action.isNull, hokn1, hokax, hokc, hokd] + · simp at hr + +theorem editOk_edit_cond {l r} (h : editOk (.node .edit l r) = true) : Verb.isPending r = false := + by + simp only [editOk, Bool.and_eq_true] at h + have := h.1.1 + simpa using this + +theorem editOk_editN_eq {l r} : editOk (.node .edit l r) = (!Verb.isPending r && editOk l && editOk + r) := rfl + +theorem isPending_replaceAux (i : Nat) (r₀ : Verb) : ∀ (v : Verb) (c : Nat), c ≠ i → + Verb.isPending (replaceAux i r₀ v c).2 = Verb.isPending v := by + intro v c hci; cases v with + | leaf => simp [replaceAux, Verb.isPending] + | node a l r => simp [replaceAux, ite_eq_right hci, Verb.isPending] + +theorem editOk_replaceAux (i : Nat) (r₀ : Verb) (hr0 : editOk r₀ = true) : + ∀ (v : Verb) (c : Nat), editOk v = true → + (∀ s, findAt i (enum v c).2 = some s → s.isPending = true) → + editOk (replaceAux i r₀ v c).2 = true := by + intro v + induction v with + | leaf k => intro c _ _; simp [replaceAux, editOk] + | node a l r ihl ihr => + intro c hok hpend + obtain ⟨hokl, hokr⟩ := editOk_children hok + simp only [replaceAux] + by_cases hci : c = i + · subst hci; rw [ite_eq_left rfl]; exact hr0 + · simp only [ite_eq_right hci] + have hine : ¬ (i = c) := fun h => hci h.symm + have hcL : (replaceAux i r₀ l (c+1)).1 = (enum l (c+1)).1 := replaceAux_counter i r₀ l (c+1) + have hpl : ∀ s, findAt i (enum l (c+1)).2 = some s → s.isPending = true := by + intro s hs; apply hpend s; simp only [enum, findAt, ite_eq_right hine]; rw [findAt_append, + hs] + have hokl' : editOk (replaceAux i r₀ l (c+1)).2 = true := ihl (c+1) hokl hpl + have hleftNone : ∀ s, findAt i (enum r (enum l (c+1)).1).2 = some s → + findAt i (enum l (c+1)).2 = none := by + intro s hs + apply findAt_eq_none; intro q hq + have := enum_index_range l (c+1) q hq + have := enum_index_range r (enum l (c+1)).1 (i, s) (findAt_some_mem hs) + omega + have hpr : ∀ s, findAt i (enum r (enum l (c+1)).1).2 = some s → s.isPending = true := by + intro s hs; apply hpend s + simp only [enum, findAt, ite_eq_right hine]; rw [findAt_append, hleftNone s hs, hs] + have hokr' : editOk (replaceAux i r₀ r (enum l (c+1)).1).2 = true := + ihr (enum l (c+1)).1 hokr hpr + rw [hcL] + cases a with + | edit => + have hcond : Verb.isPending r = false := editOk_edit_cond hok + have hnp : Verb.isPending (replaceAux i r₀ r (enum l (c+1)).1).2 = false := by + by_cases hcLi : (enum l (c+1)).1 = i + · cases r with + | leaf => simp [replaceAux, Verb.isPending] + | node b nw od => + exfalso + have hn : findAt i (enum l (c+1)).2 = none := by + apply findAt_eq_none; intro q hq + have := enum_index_range l (c+1) q hq; omega + have hf : findAt i (enum (Verb.node .edit l (Verb.node b nw od)) c).2 + = some (Verb.node b nw od) := by + simp only [enum, findAt, ite_eq_right hine, findAt_append, hn] + rw [← hcLi]; simp + have := hpend _ hf + rw [hcond] at this; simp at this + · rw [isPending_replaceAux i r₀ r _ hcLi, hcond] + simp only [editOk_editN_eq, hnp, hokl', hokr', Bool.not_false, Bool.and_self] + | null => simp only [editOk_null_eq, hokl', hokr', Bool.and_self] + | star => simp only [editOk_star_eq, hokl', hokr', Bool.and_self] + | wut => simp only [editOk, hokl', hokr', Bool.and_self] + | slot => simp only [editOk, hokl', hokr', Bool.and_self] + | equal => simp only [editOk, hokl', hokr', Bool.and_self] + | minus => simp only [editOk, hokl', hokr', Bool.and_self] + +theorem editOk_enum : ∀ (v : Verb) (c : Nat), editOk v = true → + ∀ p ∈ (enum v c).2, editOk p.2 = true := by + intro v + induction v with + | leaf k => intro c _ p hp; simp [enum] at hp + | node a l r ihl ihr => + intro c hok p hp + obtain ⟨hokl, hokr⟩ := editOk_children hok + simp only [enum, List.mem_cons, List.mem_append] at hp + rcases hp with rfl | hpl | hpr + · exact hok + · exact ihl (c+1) hokl p hpl + · exact ihr _ hokr p hpr + +/-! ### STEP-LOCAL combined, `den_step`, and the `editOk` invariant threaded through -/ + +theorem den_local {sub r₀ x} (hd : inDomain sub = true) (hr : reduce sub = some r₀) : + (Den sub x ↔ Den r₀ x) := by + rw [inDomain, Bool.or_eq_true] at hd + rcases hd with h | h + · exact den_local_opcode h hr + · exact den_local_opReady h hr + +theorem editOk_next {v v'} (h : next v = some v') (hok : editOk v = true) : editOk v' = true := by + obtain ⟨i, sub, r₀, hmax, hmap, hdom, hred, hv'⟩ := next_eq_some_iff.1 h + have hoksub : editOk sub = true := editOk_enum v 0 hok (i, sub) (mem_nodes_of_map hmap) + have hokr0 : editOk r₀ = true := editOk_reduce hred hoksub + obtain ⟨w, hmapw, hpw⟩ := hmax.1 + have hpsub : Verb.isPending sub = true := (Option.some.inj (hmapw.symm.trans hmap)) ▸ hpw + rw [hv', replaceAt] + apply editOk_replaceAux i r₀ hokr0 v 0 hok + intro s hs + have hms : map i v = some s := hs + exact (Option.some.inj (hms.symm.trans hmap)) ▸ hpsub + +-- main.tex:1052,1156 (one `next`-step preserves `den`: small-step ↦ big-step adequacy) +theorem den_step {v v' : Verb} (hwf : editOk v = true) (h : next v = some v') {r : Noun} : + (∃ fuel, den fuel v = some r) ↔ (∃ fuel, den fuel v' = some r) := by + obtain ⟨i, sub, r₀, hmax, hmap, hdom, hred, hv'⟩ := next_eq_some_iff.1 h + obtain ⟨w, hmapw, hpw⟩ := hmax.1 + have hpsub : Verb.isPending sub = true := (Option.some.inj (hmapw.symm.trans hmap)) ▸ hpw + have hrel : Rel v v' := by + rw [hv', replaceAt] + apply replaceAux_rel i r₀ v 0 + intro s hs + have hms : map i v = some s := hs + have hss : s = sub := Option.some.inj (hms.symm.trans hmap) + subst hss + exact ⟨hpsub, fun x => den_local hdom hred⟩ + exact Rel_den v.noun.size v v' (Nat.le_refl _) hrel hwf r + +theorem run_sound {v : Verb} {r : Noun} : + ∀ fuel, editOk v = true → run fuel v = some r → (∃ f, den f v = some r) := by + intro fuel + induction fuel generalizing v with + | zero => intro _ h; simp [run] at h + | succ fuel ih => + intro hwf h + rw [run] at h + cases hnext : next v with + | some v' => + rw [hnext] at h + exact (den_step hwf hnext).2 (ih (editOk_next hnext hwf) h) + | none => + rw [hnext] at h + simp only [result] at h + split at h + · rename_i hterm + have hr : v.noun = r := Option.some.inj h + subst hr + exact ⟨v.noun.size, den_terminal v v.noun.size hterm (Nat.le_refl _)⟩ + · simp at h + +theorem runProgram_sound {s f r : Noun} {fuel : Nat} + (h : runProgram fuel s f = some r) : ∃ fuel', evalN fuel' s f = some r := by + have : ∃ f', den f' (program s f) = some r := run_sound fuel (editOk_program s f) h + exact (den_iff_evalN s f r).1 this + +/-! ### `edit`/`slot` characterization — the `%10` soundness relation + + `Noun.edit ax new old` (the `#` operator, opcode 10's structural write, `main.tex:1071`, + `1587–1607`) replaces the subtree of `old` at axis `ax` with `new`. The paper's + `NockVMROM` excludes `%10` entirely, so this relation is stated nowhere in the manuscript; + it is the noun-level soundness relation an `%edit`-table arithmetization must prove + against. Two **necessary** properties an `%edit`-table arithmetization must discharge + (sufficiency / uniqueness of `res` is not proved here): + + * `slot_edit_eq` — **read-back**: on a successful edit, reading axis `ax` of the + result yields `new`. + * `slot_edit_sibling` — **sibling carry**: every off-path sibling encountered from `ax` + to the root is carried unchanged from `old` (the property whose + absence lets a fraudulent proof mutate an unrelated subtree). + + Both are over the fuel-free `Noun.slot`/`Noun.edit` (`Algorithm:Noun_Operators`, + `main.tex:1537–1607`). `Noun.edit` recurses bottom-up — it rebuilds each parent by + pairing `new` with the sibling read from the *original* `old`, then edits the halved + (parent) axis. Read-back uses strong induction on `ax`; full sibling carry then follows by + induction over the siblings encountered by that same `ax / 2` walk. -/ + +/-- The sibling axis of `ax = a+2`: its co-child under the shared parent `ax / 2`. + Even axes are left children (sibling `ax+1`); odd axes are right children (sibling + `ax-1`). This is exactly the `sibAxis` local of `Noun.edit`'s `a+2` branch. -/ +def siblingAxis (ax : Nat) : Nat := if ax % 2 == 0 then ax + 1 else ax - 1 + +/-- `SiblingOf ax sib` says that `sib` is an off-path sibling encountered while following + `ax` toward the root: either the immediate sibling of `ax`, or a sibling of one of `ax`'s + proper ancestors. For example, both axes `5` and `3` are siblings of target axis `4`. -/ +inductive SiblingOf : Nat → Nat → Prop where + | immediate (a : Nat) : SiblingOf (a + 2) (siblingAxis (a + 2)) + | ancestor (a : Nat) {sib : Nat} : SiblingOf ((a + 2) / 2) sib → SiblingOf (a + 2) sib + +/-- A sibling shares its parent: `siblingAxis (a+2) / 2 = (a+2) / 2`. -/ +theorem siblingAxis_half (a : Nat) : siblingAxis (a + 2) / 2 = (a + 2) / 2 := by + unfold siblingAxis + by_cases hb : ((a + 2) % 2 == 0) = true + · rw [ite_eq_left hb] + have hbn : (a + 2) % 2 = 0 := by simpa using hb + omega + · rw [ite_eq_right hb] + have hbn : (a + 2) % 2 ≠ 0 := by simpa using hb + omega + +/-- A sibling has the opposite parity to its axis. -/ +theorem siblingAxis_parity (a : Nat) : + ((siblingAxis (a + 2)) % 2 == 0) = !((a + 2) % 2 == 0) := by + unfold siblingAxis + by_cases hb : ((a + 2) % 2 == 0) = true + · have hbn : (a + 2) % 2 = 0 := by simpa using hb + rw [ite_eq_left hb, hb] + have h1 : (a + 2 + 1) % 2 = 1 := by omega + rw [h1]; decide + · have hbf : ((a + 2) % 2 == 0) = false := by + cases hh : (a + 2) % 2 == 0 with + | true => exact absurd hh hb + | false => rfl + have hbn : (a + 2) % 2 ≠ 0 := by simpa using hb + rw [ite_eq_right hb, hbf] + have h0 : (a + 2 - 1) % 2 = 0 := by omega + rw [h0]; decide + +/-- **Read-back (`#`/`/` round-trip).** If editing `old` at axis `ax` with `new` succeeds, + reading axis `ax` of the result `res` returns `new`: `slot ax res = some new` + (conditional on `edit ax new old = some res`). + The "terminal replaced node equals the patch" half of the `%10` relation. -/ +theorem slot_edit_eq : + ∀ (ax : Nat) {new old res : Noun}, + Noun.edit ax new old = some res → Noun.slot ax res = some new := by + intro ax + induction ax using Nat.strongRecOn with + | ind ax ih => + intro new old res h + match ax with + | 0 => rw [Noun.edit] at h; exact absurd h (by simp) + | 1 => + rw [Noun.edit] at h + obtain rfl := Option.some.inj h + exact slot_one + | (a + 2) => + rw [Noun.edit] at h + simp only at h + cases hs : Noun.slot (if (a + 2) % 2 == 0 then a + 2 + 1 else a + 2 - 1) old with + | none => rw [hs] at h; exact absurd h (by simp) + | some sib => + rw [hs] at h + have hres : + Noun.slot ((a + 2) / 2) + res + = some (if (a + 2) % 2 == 0 then Noun.cell new sib else Noun.cell sib new) := + ih ((a + 2) / 2) (by omega) h + rw [Noun.slot, hres] + by_cases hpar : ((a + 2) % 2 == 0) = true + · simp only [hpar, ite_true] + · simp only [Bool.not_eq_true] at hpar + simp only [hpar, ite_false, Bool.false_eq_true] + +/-- `slot` at any axis `≥ 2` in terms of the parent read `slot (m/2)` and the parity pick — + the `slot` equation stated for a general `m` rather than the `a+2` constructor pattern. -/ +theorem slot_ge_two (m : Nat) (hm : 2 ≤ m) (n : Noun) : + Noun.slot m n + = (match Noun.slot (m / 2) n with + | some (.cell l r) => some (if m % 2 == 0 then l else r) + | _ => none) := by + obtain ⟨k, rfl⟩ : ∃ k, m = k + 2 := ⟨m - 2, by omega⟩ + simp only [Noun.slot] + rfl + +/-- The immediate sibling written into the rebuilt parent by one recursive `edit` step is + carried unchanged from `old`. -/ +private theorem slot_edit_immediate_sibling : + ∀ (a : Nat) {new old res : Noun}, + Noun.edit (a + 2) new old = some res → + Noun.slot (siblingAxis (a + 2)) res = Noun.slot (siblingAxis (a + 2)) old := by + intro a new old res h + rw [Noun.edit] at h + simp only at h + cases hs : Noun.slot (if (a + 2) % 2 == 0 then a + 2 + 1 else a + 2 - 1) old with + | none => rw [hs] at h; exact absurd h (by simp) + | some sib => + rw [hs] at h + have hres : Noun.slot ((a + 2) / 2) res + = some (if (a + 2) % 2 == 0 then Noun.cell new sib else Noun.cell sib new) := + slot_edit_eq ((a + 2) / 2) h + have hsib_old : Noun.slot (siblingAxis (a + 2)) old = some sib := by + unfold siblingAxis; exact hs + have hge : 2 ≤ siblingAxis (a + 2) := by + unfold siblingAxis + by_cases hb : ((a + 2) % 2 == 0) = true + · rw [ite_eq_left hb]; omega + · rw [ite_eq_right hb] + have hbn : (a + 2) % 2 ≠ 0 := by simpa using hb + omega + rw [hsib_old, slot_ge_two _ hge, siblingAxis_half, hres, siblingAxis_parity] + by_cases hb : ((a + 2) % 2 == 0) = true + · simp only [hb, ite_true, Bool.not_true, ite_false, Bool.false_eq_true] + · have hbf : ((a + 2) % 2 == 0) = false := by + cases hh : (a + 2) % 2 == 0 with + | true => exact absurd hh hb + | false => rfl + simp only [hbf, ite_false, Bool.not_false, ite_true, Bool.false_eq_true] + +/-- **Sibling carry.** If editing `old` at axis `ax` succeeds, every off-path sibling subtree + encountered from `ax` to the root is unchanged in the result. Thus an edit cannot mutate + either the target's immediate sibling or a sibling branching from any proper ancestor of the + target. -/ +theorem slot_edit_sibling {ax sib : Nat} (hsib : SiblingOf ax sib) : + ∀ {new old res : Noun}, + Noun.edit ax new old = some res → Noun.slot sib res = Noun.slot sib old := by + induction hsib with + | immediate a => + intro new old res h + exact slot_edit_immediate_sibling a h + | ancestor a hsib ih => + intro new old res h + rw [Noun.edit] at h + simp only at h + cases hs : Noun.slot (if (a + 2) % 2 == 0 then a + 2 + 1 else a + 2 - 1) old with + | none => rw [hs] at h; exact absurd h (by simp) + | some sibling => + rw [hs] at h + exact ih h + +end Verb +end Nock diff --git a/LeanPool/PureNock/Determinism.lean b/LeanPool/PureNock/Determinism.lean new file mode 100644 index 0000000000..d6598005f2 --- /dev/null +++ b/LeanPool/PureNock/Determinism.lean @@ -0,0 +1,61 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.PureCompleteHint +public import LeanPool.PureNock.PaperReferenceMod + +/-! +# Output uniqueness for the executable machines + +Public capstones stating that the small-step Nock machines compute *partial functions*: a +successful run's result is independent of the fuel budget. Each composes the machine's +unconditional adequacy with the corresponding oracle's output uniqueness: + +* `runProgram_output_unique` via `runProgram_adequate_complete` + `evalPaper_deterministic`; +* `runHintProgram_output_unique` via `runHint_adequate_complete` + `evalN_deterministic`; +* `evalPaperMod_deterministic` directly from `evalPaperMod_mono`. + +Fuel affects only whether a run *finishes*, never a finished run's *value*. These are Lean +consistency theorems (a fuel-indexed shadow of `Thm:deterministic_Trace`, `main.tex:1156–1158`), +not numbered paper theorems. In particular there is deliberately no claim that the fuel *numbers* +of `runProgram` and `evalPaper` coincide — the two count different quantities. +-/ + +@[expose] public section + +namespace Nock +namespace Verb + +/-- **`runProgram` output uniqueness.** Any two successful small-step runs of `*[s f]`, at any + fuel budgets, return the same noun. -/ +theorem runProgram_output_unique {s f r₁ r₂ : Noun} + (h₁ : ∃ fuel, runProgram fuel s f = some r₁) + (h₂ : ∃ fuel, runProgram fuel s f = some r₂) : r₁ = r₂ := by + obtain ⟨n₁, hp₁⟩ := (runProgram_adequate_complete s f r₁).1 h₁ + obtain ⟨n₂, hp₂⟩ := (runProgram_adequate_complete s f r₂).1 h₂ + exact evalPaper_deterministic hp₁ hp₂ + +/-- **`runHintProgram` output uniqueness.** Any two successful OP₁₁-extended runs of `*[s f]`, at + any fuel budgets, return the same noun. -/ +theorem runHintProgram_output_unique {s f r₁ r₂ : Noun} + (h₁ : ∃ fuel, runHintProgram fuel s f = some r₁) + (h₂ : ∃ fuel, runHintProgram fuel s f = some r₂) : r₁ = r₂ := by + obtain ⟨n₁, hp₁⟩ := (runHint_adequate_complete s f r₁).1 h₁ + obtain ⟨n₂, hp₂⟩ := (runHint_adequate_complete s f r₂).1 h₂ + exact evalN_deterministic hp₁ hp₂ + +end Verb + +/-- **`evalPaperMod q` is a partial function (output uniqueness).** Two successful modular + fragment evaluations of the same `subject`/`formula`, at any fuel budgets, agree. -/ +theorem evalPaperMod_deterministic {q n m : Nat} {s form r₁ r₂ : Noun} + (h₁ : evalPaperMod q n s form = some r₁) (h₂ : evalPaperMod q m s form = some r₂) : r₁ = r₂ := + Option.some.inj ((evalPaperMod_mono (Nat.le_max_left n m) h₁).symm.trans + (evalPaperMod_mono (Nat.le_max_right n m) h₂)) + +end Nock diff --git a/LeanPool/PureNock/Dyck.lean b/LeanPool/PureNock/Dyck.lean new file mode 100644 index 0000000000..f834d00d23 --- /dev/null +++ b/LeanPool/PureNock/Dyck.lean @@ -0,0 +1,352 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Tree +public import Mathlib.Combinatorics.Enumerative.Catalan.Tree + +/-! +# Dyck words and proper binary trees + +Formalizes the grammar, tree encoding, graded correspondence, and Catalan +cardinality in `Lem:optimal_dyck` (`main.tex:337–342`). +-/ + +@[expose] public section + +namespace Nock + +/-! ### The Dyck language (main.tex:337) -/ + +-- main.tex:337 (prelude of Lem:optimal_dyck — L_dk) +/-- Prelude of **`Lem:optimal_dyck`** (`main.tex:337`). + `L_dk = { w ∈ {0,1}^* | w=ε or ∃ w₁,w₂ ∈ L_dk s.t. w = 0 w₁ 1 w₂ }`. -/ +inductive Dyck : List Bool → Prop + | eps : Dyck [] + | cat {w₁ w₂ : List Bool} : Dyck w₁ → Dyck w₂ → Dyck (false :: w₁ ++ true :: w₂) + +/-! ### Proper binary tree shapes = `L_full_BT` (main.tex:318–327) -/ + +-- main.tex:318–327 (Def:Proper_BT) +/-- **`Def:Proper_BT`** (`main.tex:318–327`). + `L_full_BT = { t ∈ T | (size(t)=1) ∪ (left(t) ∈ L_full_BT ∩ right(t) ∈ L_full_BT ∩ (left(t) + ∩ right(t)=∅)) }`; + `L^λ_full_BT = { t ∈ L_full_BT | size(t)=λ }`. -/ +inductive BTree where + | leaf : BTree + | node : BTree → BTree → BTree +deriving Repr, DecidableEq, Inhabited + +namespace BTree + +/-- Total node count of a shape (matches `Noun.size`). -/ +def size : BTree → Nat + | .leaf => 1 + | .node l r => 1 + l.size + r.size + +/-- The Dyck encoding of a proper binary tree (main.tex:337): `leaf ↦ ε`, + `node l r ↦ 0 · encode l · 1 · encode r`. -/ +def encode : BTree → List Bool + | .leaf => [] + | .node l r => false :: encode l ++ true :: encode r + +/-- A Dyck word encoding a tree has length `size − 1` (each internal node contributes the + pair `0…1`). Stated additively to avoid truncated subtraction. -/ +theorem encode_length_add_one (t : BTree) : (encode t).length + 1 = t.size := by + induction t with + | leaf => rfl + | node l r ihl ihr => + simp only [encode, size, List.length_cons, List.length_append] + omega + +/-! ### `encode` lands in the Dyck language, and is onto it (main.tex:340) -/ + +-- main.tex:340 (⇐ direction of `L_dk ≡ L_full_BT`: every tree's encoding is Dyck) +/-- Every tree encodes to a Dyck word. -/ +theorem dyck_encode (t : BTree) : Dyck (encode t) := by + induction t with + | leaf => exact Dyck.eps + | node l r ihl ihr => exact Dyck.cat ihl ihr + +-- main.tex:340 (⇒ direction of `L_dk ≡ L_full_BT`: every Dyck word is some tree's encoding) +/-- Every Dyck word is the encoding of some tree (surjectivity of `encode` onto `L_dk`). -/ +theorem exists_encode_of_dyck {w : List Bool} (h : Dyck w) : ∃ t : BTree, encode t = w := by + induction h with + | eps => exact ⟨.leaf, rfl⟩ + | @cat w₁ w₂ _ _ ih₁ ih₂ => + obtain ⟨t₁, ht₁⟩ := ih₁ + obtain ⟨t₂, ht₂⟩ := ih₂ + exact ⟨.node t₁ t₂, by simp only [encode, ht₁, ht₂]⟩ + +/-! ### A greedy parser, to pin down injectivity of `encode` + + `decode fuel w` parses exactly one tree off the front of `w`, returning the tree and the + unconsumed suffix. A leading `0` (`false`) starts a `node`; anything else (a leading `1` + or the empty word) is a `leaf` consuming nothing. This is the algorithmic inverse of + `encode`; `decode_encode` proves the round-trip, which yields injectivity. -/ +/-- Parse one binary tree from a Dyck prefix, returning the tree and unconsumed suffix. -/ +def decode : Nat → List Bool → Option (BTree × List Bool) + | 0, _ => none + | fuel + 1, (false :: rest) => + match decode fuel rest with + | some (l, true :: rest₁) => + match decode fuel rest₁ with + | some (r, rest₂) => some (.node l r, rest₂) + | none => none + | _ => none + | _ + 1, rest => some (.leaf, rest) -- leading `1`, or `[]` + +end BTree +end Nock + +namespace Nock +namespace BTree + +/-! ### Round-trip: `decode` inverts `encode`, hence `encode` is injective -/ + +/-- The greedy parser recovers the encoded tree and returns the untouched suffix, provided + the fuel covers the tree's size and the suffix is not "open"-led (which is exactly the + invariant maintained by the recursion: a left subtree is followed by its closing `1`, a + right subtree by the parent's suffix). -/ +theorem decode_encode : + ∀ (t : BTree) (fuel : Nat) (rest : List Bool), + t.size ≤ fuel → (rest = [] ∨ rest.head? = some true) → + decode fuel (encode t ++ rest) = some (t, rest) := by + intro t + induction t with + | leaf => + intro fuel rest _ hrest + cases fuel with + | zero => simp [size] at * + | succ f => + simp only [encode, List.nil_append] + rcases hrest with h | h + · subst h; rfl + · cases rest with + | nil => rfl + | cons b rs => + simp only [List.head?_cons, Option.some.injEq] at h + subst h; rfl + | node l r ihl ihr => + intro fuel rest hsize hrest + cases fuel with + | zero => simp [size] at hsize + | succ f => + -- fuel = f+1, f ≥ size l + size r ≥ size l, size r + simp only [size] at hsize + have hfl : l.size ≤ f := by omega + have hfr : r.size ≤ f := by omega + -- left subtree: continuation starts with `true` + have hL := ihl f (true :: (encode r ++ rest)) hfl (Or.inr rfl) + -- right subtree: continuation is the (open-free) `rest` + have hR := ihr f rest hfr hrest + simp only [encode, List.cons_append, List.append_assoc, decode, hL, hR] + +/-- `encode` is injective: distinct shapes have distinct Dyck words. -/ +theorem encode_injective {t₁ t₂ : BTree} (h : encode t₁ = encode t₂) : t₁ = t₂ := by + have hlen : t₁.size = t₂.size := by + have e₁ := encode_length_add_one t₁ + have e₂ := encode_length_add_one t₂ + rw [h] at e₁; omega + have d₁ : decode t₁.size (encode t₁ ++ []) = some (t₁, []) := + decode_encode t₁ t₁.size [] (Nat.le_refl _) (Or.inl rfl) + have d₂ : decode t₁.size (encode t₂ ++ []) = some (t₂, []) := + decode_encode t₂ t₁.size [] (by omega) (Or.inl rfl) + rw [h] at d₁ + have := d₁.symm.trans d₂ + simp only [Option.some.injEq, Prod.mk.injEq] at this + exact this.1 + +/-! ### `Lem:optimal_dyck` (main.tex:340–342) -/ + +-- main.tex:340–342 (Lem:optimal_dyck) +/-- **`Lem:optimal_dyck`** (`main.tex:340–342`). + `L_dk ≡ L_full_BT`. -/ +theorem dyck_iff_encode {w : List Bool} : Dyck w ↔ ∃ t : BTree, encode t = w := + ⟨exists_encode_of_dyck, fun ⟨t, ht⟩ => ht ▸ dyck_encode t⟩ + +-- main.tex:340–342 (Lem:optimal_dyck) +/-- **`Lem:optimal_dyck`** (`main.tex:340–342`). + `∀λ, L^{2λ}_dk ≡ L^{2λ+1}_full_BT`. -/ +theorem dyck_length_graded {w : List Bool} (t : BTree) (ht : encode t = w) (lam : Nat) : + w.length = 2 * lam ↔ t.size = 2 * lam + 1 := by + have := encode_length_add_one t + rw [ht] at this + omega + +end BTree + +/-! ### Connection to `Noun`: a noun is a shape (`BTree`) plus leaf data (main.tex:347) -/ + +namespace Noun + +-- main.tex:347,1164 (a noun `n = (t, dt)` = a Dyck word/tree shape `t` + leaf array `dt`) +/-- The **shape** of a noun: erase the leaf data, leaving a proper binary tree (`L_full_BT`). + The paper's noun `(t, dt)` pairs this shape (encoded as the Dyck word `t`) with the leaf + array `dt`; the Dyck bijection above governs the `t` component. -/ +def shape : Noun → Nock.BTree + | .atom _ => .leaf + | .cell l r => .node l.shape r.shape + +/-- The shape preserves leaf and node counts (`Noun.size = BTree.size ∘ shape`). -/ +theorem shape_size (n : Noun) : n.shape.size = n.size := by + induction n with + | atom k => rfl + | cell l r ihl ihr => simp only [shape, Nock.BTree.size, size]; omega + +/-- The Dyck word of a noun: the encoding of its shape. By `Lem:pbt_size` (main.tex:333) a + noun with `λ` leaves has size `2λ − 1`, so its Dyck word has length `2λ − 2 = 2(λ−1)`, + matching the paper's `L^{2(λ−1)}_dk` component of a noun (main.tex:1164). -/ +def dyckWord (n : Noun) : List Bool := n.shape.encode + +/-- A noun's Dyck word has length `2·leaves − 2 = 2(λ−1)` (main.tex:1164). -/ +theorem dyckWord_length (n : Noun) : n.dyckWord.length + 2 = 2 * n.leaves := by + have h1 := Nock.BTree.encode_length_add_one n.shape + have h2 := shape_size n + have h3 := size_add_one n + simp only [dyckWord] + omega + +/-! ### Leaf array + Def "noun" uniqueness (main.tex:347) + + A noun is `(t, dt)` = Dyck shape + DFS leaf array. These live here (not in NPR) so the + Pure Nock language closure can talk about uniqueness without importing Field/Goldilocks. -/ + +/-- The leaf values of a noun in left-to-right (DFS) order — the paper's leaf array `dt` + (main.tex:1164), here over `Nat` (cast into the field at the NPR layer). -/ +def leafList : Noun → List Nat + | .atom k => [k] + | .cell l r => l.leafList ++ r.leafList + +@[simp] theorem leafList_length (n : Noun) : n.leafList.length = n.leaves := by + induction n with + | atom k => rfl + | cell l r ihl ihr => simp [leafList, leaves, ihl, ihr] + +-- main.tex:333,347 (a noun's shape fixes its leaf count `λ`) +/-- Equal shape ⇒ equal leaf count: the Dyck shape already determines `λ` (`Lem:pbt_size`, + main.tex:333), so it constrains how the leaf array splits between the two subtrees. -/ +theorem shape_leaves_eq {n₁ n₂ : Noun} (h : n₁.shape = n₂.shape) : + n₁.leaves = n₂.leaves := by + have hs : n₁.shape.size = n₂.shape.size := by rw [h] + rw [shape_size, shape_size] at hs + have e₁ := size_add_one n₁ + have e₂ := size_add_one n₂ + omega + +-- main.tex:347 (shape + leaf array reconstruct the noun uniquely) +/-- **Reconstruction.** If two nouns have the same shape (Dyck word) and the same leaf + array, they are the *same* noun: the shape fixes the tree structure and the leaf list + fills the leaves in DFS order, leaving no ambiguity. -/ +theorem eq_of_shape_leafList : + ∀ {n₁ n₂ : Noun}, n₁.shape = n₂.shape → n₁.leafList = n₂.leafList → n₁ = n₂ := by + intro n₁ + induction n₁ with + | atom k₁ => + intro n₂ hshape hleaf + cases n₂ with + | atom k₂ => + simp only [leafList, List.cons.injEq, and_true] at hleaf; rw [hleaf] + | cell l r => simp [shape] at hshape + | cell l₁ r₁ ihl ihr => + intro n₂ hshape hleaf + cases n₂ with + | atom k₂ => simp [shape] at hshape + | cell l₂ r₂ => + simp only [shape, BTree.node.injEq] at hshape + obtain ⟨hsl, hsr⟩ := hshape + simp only [leafList] at hleaf + have hlen : l₁.leafList.length = l₂.leafList.length := by + rw [leafList_length, leafList_length, shape_leaves_eq hsl] + obtain ⟨hll, hrl⟩ := List.append_inj hleaf hlen + rw [ihl hsl hll, ihr hsr hrl] + +-- main.tex:347 (Def "noun": the tuple `(t, dt)` corresponds to a UNIQUE noun) +/-- **Def "noun" uniqueness (main.tex:347).** The map sending a noun to its Dyck word + + leaf array, `n ↦ (n.dyckWord, n.leafList)`, is injective. This is the paper's claim + that the tuple `(t, dt)` "corresponds to a unique noun" (main.tex:345,347). -/ +theorem noun_dyck_leaf_injective : + Function.Injective (fun n : Noun => (n.dyckWord, n.leafList)) := by + intro n₁ n₂ h + simp only [Prod.mk.injEq] at h + obtain ⟨hd, hl⟩ := h + have hshape : n₁.shape = n₂.shape := + BTree.encode_injective (by simpa only [dyckWord] using hd) + exact eq_of_shape_leafList hshape hl + +end Noun + +/-! ### Catalan cardinality `|L^{2λ}_dk| = C_λ` (`Lem:optimal_dyck`, main.tex:341) -/ + +namespace BTree + +/-- The shape isomorphism to Mathlib's `BinaryTree`: a proper-binary-tree `leaf` is Mathlib's + empty leaf `nil`; a `node` is a Mathlib `node` carrying the unit value. -/ +def toBin : BTree → BinaryTree Unit + | .leaf => .nil + | .node l r => .node () (toBin l) (toBin r) + +/-- Inverse of `toBin`. -/ +def ofBin : BinaryTree Unit → BTree + | .nil => .leaf + | .node _ l r => .node (ofBin l) (ofBin r) + +@[simp] theorem ofBin_toBin (t : BTree) : ofBin (toBin t) = t := by + induction t <;> simp_all [toBin, ofBin] + +@[simp] theorem toBin_ofBin (t : BinaryTree Unit) : toBin (ofBin t) = t := by + induction t <;> simp_all [toBin, ofBin] + +/-- Under the shape isomorphism, `BTree.size` is `2·numNodes + 1`: a proper binary tree with + `λ` internal (Mathlib) nodes has `2λ+1` total nodes (`Lem:pbt_size`, main.tex:333). -/ +theorem size_eq (t : BTree) : t.size = 2 * (toBin t).numNodes + 1 := by + induction t with + | leaf => rfl + | node l r ihl ihr => + simp only [size, toBin, BinaryTree.numNodes, ihl, ihr] + omega + +/-- **`|L^{2λ}_dk|` as a finset (main.tex:337–341).** The Dyck words of length `2λ`, obtained + as the image of Mathlib's `treesOfNumNodesEq λ` under `encode ∘ ofBin`. -/ +noncomputable def dyckWordsOfLen (lam : ℕ) : Finset (List Bool) := + (BinaryTree.treesOfNumNodesEq lam).map + ⟨fun t => encode (ofBin t), by + intro a b h + have := encode_injective h + have := congrArg toBin this + simpa using this⟩ + +/-- A word lies in `dyckWordsOfLen λ` iff it is a Dyck word of length `2λ` — i.e. this finset is + exactly the paper's `L^{2λ}_dk`. -/ +theorem mem_dyckWordsOfLen {lam : ℕ} {w : List Bool} : + w ∈ dyckWordsOfLen lam ↔ Dyck w ∧ w.length = 2 * lam := by + simp only [dyckWordsOfLen, Finset.mem_map, Function.Embedding.coeFn_mk, + BinaryTree.mem_treesOfNumNodesEq] + constructor + · rintro ⟨t, ht, rfl⟩ + refine ⟨dyck_encode _, ?_⟩ + have hlen := encode_length_add_one (ofBin t) + have hsz := size_eq (ofBin t) + rw [toBin_ofBin, ht] at hsz + omega + · rintro ⟨hd, hlen⟩ + obtain ⟨s, hs⟩ := exists_encode_of_dyck hd + refine ⟨toBin s, ?_, ?_⟩ + · have hlen' := encode_length_add_one s + have hsz := size_eq s + rw [hs, hlen] at hlen' + omega + · rw [ofBin_toBin, hs] + +-- main.tex:340–342 (Lem:optimal_dyck) +/-- **`Lem:optimal_dyck`** (`main.tex:340–342`). + `|L^{2λ}_dk| = C_λ`. -/ +theorem dyckWordsOfLen_card (lam : ℕ) : (dyckWordsOfLen lam).card = catalan lam := by + rw [dyckWordsOfLen, Finset.card_map, BinaryTree.treesOfNumNodesEq_card_eq_catalan] + +end BTree +end Nock diff --git a/LeanPool/PureNock/Fingerprint.lean b/LeanPool/PureNock/Fingerprint.lean new file mode 100644 index 0000000000..2b60080434 --- /dev/null +++ b/LeanPool/PureNock/Fingerprint.lean @@ -0,0 +1,63 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.NPR + +/-! +# Computable noun fingerprint + +`NPR.fn` (`Nock/NPR.lean`, `def:ion_univariate`, `main.tex:1180–1184`) evaluates the noun +polynomials with Mathlib's `Polynomial`, which is `noncomputable`. This module gives the +extractable Horner evaluation `fnHorner` and proves it equals `NPR.fn` +(`fnHorner_eq_fn`), so the collision-soundness bounds of `Nock/NPR.lean` apply to the code that +actually runs. Instantiated at a concrete prime field `ZMod q` (with `[Fact (Nat.Prime q)]` +supplied by the caller — never as an axiom), `fnHorner` compiles and evaluates. +-/ + +@[expose] public section + +namespace Nock +namespace NPR + +open Polynomial + +/-- Horner evaluation of a coefficient list: `hornerEval r [a₀,…,aₙ] = a₀ + r·(a₁ + r·(…))`, + i.e. `Σᵢ aᵢ rⁱ`. Computable (no `Polynomial`); the extractable core of the fingerprint. -/ +def hornerEval {K : Type*} [Semiring K] (r : K) : List K → K + | [] => 0 + | a :: l => a + r * hornerEval r l + +section Horner +variable {K : Type*} [Field K] + +/-- **Horner = polynomial evaluation.** `hornerEval r l = (listPoly l).eval r`: the computable + Horner scheme agrees with evaluating the coefficient polynomial `Σᵢ lᵢ Xⁱ` at `r`. -/ +theorem hornerEval_eq_listPoly_eval (r : K) (l : List K) : + hornerEval r l = (listPoly l).eval r := by + induction l with + | nil => simp [hornerEval, listPoly_nil] + | cons x l ih => + simp only [hornerEval, listPoly_cons, eval_add, eval_C, eval_mul, eval_X, ih] + +/-- **The computable fingerprint (`def:ion_univariate`, `main.tex:1180–1184`).** `fnHorner r n = + (r^{|dt|}, Σ dtᵢ rⁱ, Σ wᵢ rⁱ)` via Horner, with no `Polynomial`. Equal to `NPR.fn` + (`fnHorner_eq_fn`), so it inherits the collision bounds. -/ +def fnHorner (r : K) (n : Noun) : K × K × K := + (r ^ n.leaves, hornerEval r (leafF n), hornerEval r (dyckBits n)) + +/-- **`fnHorner` computes `NPR.fn`.** The extractable Horner fingerprint equals the polynomial + fingerprint `fn` (`main.tex:1180–1184`), so `lem:ion_univariate_sec` / + `corr:ion_single_security` + (`Nock/NPR.lean`) transfer verbatim to the running code. -/ +theorem fnHorner_eq_fn (n : Noun) (r : K) : fnHorner r n = fn n r := by + simp only [fnHorner, fn, hornerEval_eq_listPoly_eval] + +end Horner + +end NPR +end Nock diff --git a/LeanPool/PureNock/FingerprintLayer.lean b/LeanPool/PureNock/FingerprintLayer.lean new file mode 100644 index 0000000000..93c9a8c794 --- /dev/null +++ b/LeanPool/PureNock/FingerprintLayer.lean @@ -0,0 +1,28 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Modular +public import LeanPool.PureNock.NPR +public import LeanPool.PureNock.Fingerprint + +/-! +# Pure Nock — Fingerprint layer (L2) + +Extends the modular layer `NockPure.Modular` (L1) with the noun polynomial representation and its +computable fingerprint: + +* the NPR `Φ` and fingerprint `fn`, decomposition `NPR_decomp`, and collision-soundness bounds + over an arbitrary finite field (`Nock/NPR.lean`, `main.tex:1170–1226`), and +* the extractable Horner fingerprint `fnHorner` with `fnHorner_eq_fn` (`Nock/Fingerprint.lean`). + +This root imports `NockPure.Modular`, so L0 ⊂ L1 ⊂ L2 as mechanically verified sub-closures. +No project-declared axioms. +-/ + +@[expose] public section + diff --git a/LeanPool/PureNock/Imports.lean b/LeanPool/PureNock/Imports.lean new file mode 100644 index 0000000000..112e78cb61 --- /dev/null +++ b/LeanPool/PureNock/Imports.lean @@ -0,0 +1,32 @@ +/- +Copyright (c) 2026 Lean Pool contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Lean Pool contributors +-/ +module -- shake: keep-all --deprecated_module: ignore + +-- Generated project imports; run `lake exe mk_all`. +public import LeanPool.PureNock +public import LeanPool.PureNock.Agreement +public import LeanPool.PureNock.Determinism +public import LeanPool.PureNock.Dyck +public import LeanPool.PureNock.Fingerprint +public import LeanPool.PureNock.FingerprintLayer +public import LeanPool.PureNock.Language +public import LeanPool.PureNock.Modular +public import LeanPool.PureNock.ModularEval +public import LeanPool.PureNock.NPR +public import LeanPool.PureNock.NounValidation +public import LeanPool.PureNock.PaperFieldBridge +public import LeanPool.PureNock.PaperNames +public import LeanPool.PureNock.PaperReference +public import LeanPool.PureNock.PaperReferenceMod +public import LeanPool.PureNock.PureAgreement +public import LeanPool.PureNock.PureComplete +public import LeanPool.PureNock.PureCompleteHint +public import LeanPool.PureNock.Reference +public import LeanPool.PureNock.Semantics +public import LeanPool.PureNock.Trace +public import LeanPool.PureNock.TraceCertificate +public import LeanPool.PureNock.Tree +public import LeanPool.PureNock.Verb diff --git a/LeanPool/PureNock/Language.lean b/LeanPool/PureNock/Language.lean new file mode 100644 index 0000000000..713b61cd28 --- /dev/null +++ b/LeanPool/PureNock/Language.lean @@ -0,0 +1,31 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Tree +public import LeanPool.PureNock.Reference +public import LeanPool.PureNock.Verb +public import LeanPool.PureNock.Trace +public import LeanPool.PureNock.Semantics +public import LeanPool.PureNock.PaperReference +public import LeanPool.PureNock.Agreement +public import LeanPool.PureNock.PureAgreement +public import LeanPool.PureNock.PureComplete +public import LeanPool.PureNock.PureCompleteHint +public import LeanPool.PureNock.Dyck +public import LeanPool.PureNock.NounValidation +public import LeanPool.PureNock.PaperNames + +/-! +# Pure Nock + +Standalone Nat-level Nock language formalization (`main.tex:295–1666`), +excluding the paper's §5 zkVM/AIR/RAP/table stack. +-/ + +@[expose] public section + diff --git a/LeanPool/PureNock/Modular.lean b/LeanPool/PureNock/Modular.lean new file mode 100644 index 0000000000..435f508a0a --- /dev/null +++ b/LeanPool/PureNock/Modular.lean @@ -0,0 +1,32 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Language +public import LeanPool.PureNock.ModularEval +public import LeanPool.PureNock.PaperReferenceMod +public import LeanPool.PureNock.PaperFieldBridge +public import LeanPool.PureNock.Determinism +public import LeanPool.PureNock.TraceCertificate + +/-! +# Pure Nock — Modular layer (L1) + +Extends the language core `NockPure` (L0) with the executable modular/field layer: + +* modular field-noun evaluation `evalFBy` / guarded `evalGBy` (`Nock/ModularEval.lean`), +* the strict modular paper oracle `evalPaperMod q` (`main.tex:1531–1534`), +* the paper-fragment ↔ finite-field bridge `paper_field_bridge` (no-overflow sublanguage), +* output-uniqueness capstones for the machines, and +* executable evaluation-trace certificates. + +This root imports `NockPure`, so the L0 closure is a mechanically verified sub-closure of L1. +No project-declared axioms; capstones stay within `{propext, Classical.choice, Quot.sound}`. +-/ + +@[expose] public section + diff --git a/LeanPool/PureNock/ModularEval.lean b/LeanPool/PureNock/ModularEval.lean new file mode 100644 index 0000000000..a8ca1eb37c --- /dev/null +++ b/LeanPool/PureNock/ModularEval.lean @@ -0,0 +1,746 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Reference +public import Mathlib.Data.ZMod.Basic + +/-! +# Modular (field-noun) evaluation + +The paper's nouns are field-valued: leaves carry elements of a field `𝔽` +(`main.tex:345–349`, `1023`) and the increment operator reduces modulo the +characteristic, `+(n) = (1 + n) mod p` (`main.tex:1531–1534`). The Nat-level +reference `evalN` (`Nock/Reference.lean`) instead uses the unbounded successor +`n ↦ n + 1`. + +This module makes the modulus-generic embedding precise over `Nat`-atom nouns, +parameterized by an arbitrary modulus `q` (so it never needs a concrete prime and +never declares a project axiom). It defines: + +* `BoundedBy q n` — every atom of `n` is `< q` (a `q`-bounded / canonical field noun); +* `evalFBy q` — the **modular** evaluator (OP₄ increment `mod q`); +* `evalGBy q` — the **guarded** evaluator that *crashes* rather than reaching an + atom `≥ q`; a successful `evalGBy q` run witnesses no-overflow; +* `agreeBy` — on `q`-bounded inputs, a successful guarded run makes the Nat + reference `evalN` and the modular `evalFBy q` return the same + (`q`-bounded) noun. + +Everything is `q`-generic; the ZMod injectivity fact `zmod_natCast_inj_of_lt` +requires no primality assumption. Axioms stay within `{propext, Classical.choice, Quot.sound}`. +-/ + +@[expose] public section + +namespace Nock.FieldEmbedding + +open Nock Nock.Noun + +/-! ## Field nouns: bounded leaf labels -/ + +/-- `BoundedBy q n` means that every atom (leaf) of `n` is in the numeric range `[0,q)`. + This is the bound needed when the operative modulus varies with a security parameter. -/ +def BoundedBy (q : ℕ) : Noun → Prop + | .atom n => n < q + | .cell l r => BoundedBy q l ∧ BoundedBy q r + +@[simp] theorem BoundedBy_atom {q n : ℕ} : BoundedBy q (.atom n) ↔ n < q := Iff.rfl +@[simp] theorem BoundedBy_cell {q : ℕ} {l r : Noun} : + BoundedBy q (.cell l r) ↔ BoundedBy q l ∧ BoundedBy q r := Iff.rfl + +/-! ## The field increment `+(n) = (1 + n) mod q` (`main.tex:1531–1534`) -/ + +/-- Increment modulo `q` on an atom, crash on a cell. -/ +def lusFBy (q : ℕ) : Noun → Option Noun + | .atom n => some (.atom ((n + 1) % q)) + | .cell _ _ => none + +/-- Guarded Nat increment: like `Noun.lus` (`n ↦ n+1`) but **crashes** instead of overflowing + the selected range, i.e. it refuses to produce an atom `≥ q`. A successful guarded run + witnesses that no increment reached `q` — this is how the no-overflow side condition is + encoded honestly. -/ +def lusGBy (q : ℕ) : Noun → Option Noun + | .atom n => if n + 1 < q then some (.atom (n + 1)) else none + | .cell _ _ => none + +/-! ## The field-valued evaluator `evalFBy` + +`evalFBy q` is `Nock.evalN` verbatim EXCEPT that OP₄ (`+`) uses increment modulo `q`. +All other operators are structural (they only rearrange existing leaves) or produce the +constants `0`/`1`, so over field nouns they coincide with the Nat versions; hence `evalFBy` +reuses `Noun.slot`, `Noun.edit`, `Noun.wut`, `Noun.tis` from `Nock/Reference.lean`. Equality +(OP₅, `main.tex:1525`) compares nouns structurally; over `q`-bounded nouns this is field +equality (distinct `< q` atoms are distinct in `ZMod q`). -/ +/-- Fuel-indexed Nock evaluation with increment reduced modulo the supplied modulus. -/ +def evalFBy (q : ℕ) : Nat → Noun → Noun → Option Noun + | 0, _, _ => none + | _ + 1, _, .atom _ => none + | fuel + 1, subj, .cell (.cell b c) d => + match evalFBy q fuel subj (.cell b c), evalFBy q fuel subj d with + | some l, some r => some (.cell l r) + | _, _ => none + | fuel + 1, subj, .cell (.atom op) tail => + match op with + | 0 => match tail with + | .atom ax => slot ax subj + | _ => none + | 1 => some tail + | 2 => match tail with + | .cell b c => + match evalFBy q fuel subj b, evalFBy q fuel subj c with + | some sb, some sc => evalFBy q fuel sb sc + | _, _ => none + | _ => none + | 3 => (evalFBy q fuel subj tail).map wut + | 4 => (evalFBy q fuel subj tail).bind (lusFBy q) + | 5 => match tail with + | .cell b c => + match evalFBy q fuel subj b, evalFBy q fuel subj c with + | some sb, some sc => some (tis sb sc) + | _, _ => none + | _ => none + | 6 => match tail with + | .cell b (.cell c d) => + match evalFBy q fuel subj b with + | some (.atom 0) => evalFBy q fuel subj c + | some (.atom 1) => evalFBy q fuel subj d + | _ => none + | _ => none + | 7 => match tail with + | .cell b c => + match evalFBy q fuel subj b with + | some sb => evalFBy q fuel sb c + | none => none + | _ => none + | 8 => match tail with + | .cell b c => + match evalFBy q fuel subj b with + | some sb => evalFBy q fuel (.cell sb subj) c + | none => none + | _ => none + | 9 => match tail with + | .cell (.atom ax) c => + match evalFBy q fuel subj c with + | some core => + match slot ax core with + | some arm => evalFBy q fuel core arm + | none => none + | none => none + | _ => none + | 10 => match tail with + | .cell (.cell (.atom ax) c) d => + match evalFBy q fuel subj c, evalFBy q fuel subj d with + | some new, some old => edit ax new old + | _, _ => none + | _ => none + | 11 => match tail with + | .cell (.cell _ clue) body => + match evalFBy q fuel subj clue with + | some _ => evalFBy q fuel subj body + | none => none + | .cell (.atom _) body => evalFBy q fuel subj body + | _ => none + | _ => none + +/-! ## The guarded (no-overflow) evaluator `evalGBy` + +`evalGBy q` is `Nock.evalN` verbatim EXCEPT that OP₄ uses `lusGBy q`, which crashes on overflow. -/ +/-- Fuel-indexed Nock evaluation that rejects increments reaching the supplied modulus. -/ +def evalGBy (q : ℕ) : Nat → Noun → Noun → Option Noun + | 0, _, _ => none + | _ + 1, _, .atom _ => none + | fuel + 1, subj, .cell (.cell b c) d => + match evalGBy q fuel subj (.cell b c), evalGBy q fuel subj d with + | some l, some r => some (.cell l r) + | _, _ => none + | fuel + 1, subj, .cell (.atom op) tail => + match op with + | 0 => match tail with + | .atom ax => slot ax subj + | _ => none + | 1 => some tail + | 2 => match tail with + | .cell b c => + match evalGBy q fuel subj b, evalGBy q fuel subj c with + | some sb, some sc => evalGBy q fuel sb sc + | _, _ => none + | _ => none + | 3 => (evalGBy q fuel subj tail).map wut + | 4 => (evalGBy q fuel subj tail).bind (lusGBy q) + | 5 => match tail with + | .cell b c => + match evalGBy q fuel subj b, evalGBy q fuel subj c with + | some sb, some sc => some (tis sb sc) + | _, _ => none + | _ => none + | 6 => match tail with + | .cell b (.cell c d) => + match evalGBy q fuel subj b with + | some (.atom 0) => evalGBy q fuel subj c + | some (.atom 1) => evalGBy q fuel subj d + | _ => none + | _ => none + | 7 => match tail with + | .cell b c => + match evalGBy q fuel subj b with + | some sb => evalGBy q fuel sb c + | none => none + | _ => none + | 8 => match tail with + | .cell b c => + match evalGBy q fuel subj b with + | some sb => evalGBy q fuel (.cell sb subj) c + | none => none + | _ => none + | 9 => match tail with + | .cell (.atom ax) c => + match evalGBy q fuel subj c with + | some core => + match slot ax core with + | some arm => evalGBy q fuel core arm + | none => none + | none => none + | _ => none + | 10 => match tail with + | .cell (.cell (.atom ax) c) d => + match evalGBy q fuel subj c, evalGBy q fuel subj d with + | some new, some old => edit ax new old + | _, _ => none + | _ => none + | 11 => match tail with + | .cell (.cell _ clue) body => + match evalGBy q fuel subj clue with + | some _ => evalGBy q fuel subj body + | none => none + | .cell (.atom _) body => evalGBy q fuel subj body + | _ => none + | _ => none + +/-! ## `BoundedBy` is preserved by the structural operators -/ + +/-- `slot` returns a subtree of `n`, so it preserves every numeric leaf bound. -/ +theorem slot_boundedBy (q : ℕ) (ax : Nat) (n : Noun) : + BoundedBy q n → ∀ {m : Noun}, slot ax n = some m → BoundedBy q m := by + induction ax, n using Nock.Noun.slot.induct with + | case1 x => intro _ m h; simp [slot] at h + | case2 n => intro hn m h; simp only [slot] at h; obtain rfl := Option.some.inj h; exact hn + | case3 a n l r hrec ih => + intro hn m h + have hlr : BoundedBy q (Noun.cell l r) := ih hn hrec + simp only [slot, hrec] at h + split at h + · obtain rfl := Option.some.inj h; exact hlr.1 + · obtain rfl := Option.some.inj h; exact hlr.2 + | case4 a n hno ih => + intro hn m h + simp only [slot] at h + cases hs : slot ((a + 2) / 2) n with + | none => simp at h + | some w => + cases w with + | atom k => simp at h + | cell l r => exact (hno l r hs).elim + +/-- `edit` grafts `new` into `old`, so it preserves every numeric leaf bound. -/ +theorem edit_boundedBy (q : ℕ) (ax : Nat) (new old : Noun) : + BoundedBy q new → BoundedBy q old → + ∀ {m : Noun}, edit ax new old = some m → BoundedBy q m := by + induction ax, new, old using Nock.Noun.edit.induct with + | case1 x y => intro _ _ m h; simp [edit] at h + | case2 new x => intro hnew _ m h; simp only [edit] at h; obtain rfl := Option.some.inj h; exact + hnew + | case3 a new old sibAxis sib hsib paired ih => + intro hnew hold m h + have hsibB : BoundedBy q sib := slot_boundedBy q _ _ hold hsib + have hpair : ∀ b : Bool, BoundedBy q + (if b = true then new.cell sib else sib.cell new) := by + intro b; cases b <;> exact ⟨by assumption, by assumption⟩ + simp only [edit] at h + rw [show slot (if ((a + 2) % 2 == 0) = true then a + 2 + 1 else a + 2 - 1) old = some sib + from hsib] at h + exact ih (hpair _) hold h + | case4 a new old sibAxis hnone => + intro _ _ m h + simp only [edit] at h + rw [show slot (if ((a + 2) % 2 == 0) = true then a + 2 + 1 else a + 2 - 1) old = none + from hnone] at h + simp at h + +/-- Increment modulo a positive `q` always produces an atom `< q`. -/ +theorem lusFBy_bounded {q : ℕ} (hq : 0 < q) {x m : Noun} + (h : lusFBy q x = some m) : BoundedBy q m := by + cases x with + | atom n => simp only [lusFBy] at h; obtain rfl := Option.some.inj h; exact Nat.mod_lt _ hq + | cell l r => simp [lusFBy] at h + +theorem wut_boundedBy {q : ℕ} (hq : 1 < q) (x : Noun) : BoundedBy q (wut x) := by + cases x with + | cell _ _ => + simp only [wut, BoundedBy_atom] + omega + | atom _ => exact hq + +theorem tis_boundedBy {q : ℕ} (hq : 1 < q) (a b : Noun) : BoundedBy q (tis a b) := by + unfold tis; split + · simp only [BoundedBy_atom] + omega + · exact hq + +/-! ## Main agreement lemma (the no-overflow simulation) + +`agreeBy` is proved by induction on `fuel`. Its conclusion bundles the three facts that must +be carried through the recursion simultaneously: +* `evalN … = some r` — the Nat reference reproduces the guarded result; +* `evalFBy q … = some r` — the modular evaluator reproduces it too; and +* `BoundedBy q r` — the result is again a field noun (needed to feed nested calls). + +The ONLY place the three evaluators differ is OP₄ (`+`): there `evalGBy q` succeeds exactly when +`n + 1 < q`, and under that guard the Nat increment (`n+1`) and the modular increment +(`(n+1) mod q`) coincide — this is where no-overflow discharges the divergence. -/ +private theorem guarded_hint_agreement (q fuel : ℕ) + (ih : + ∀ (s f r : Noun), + BoundedBy q s → + BoundedBy q f → + evalGBy q fuel s f = some r → evalN fuel s f = some r ∧ evalFBy q fuel s f = some r ∧ + BoundedBy q r) + (s r : Noun) (hs : BoundedBy q s) (tail : Noun) (hft : BoundedBy q tail) + (hG : evalGBy q (fuel + 1) s ((atom 11).cell tail) = some r) : + evalN (fuel + 1) s ((atom 11).cell tail) = some r ∧ + evalFBy q (fuel + 1) s ((atom 11).cell tail) = some r ∧ BoundedBy q r := by + match tail with + | .cell (.cell hd clue) body => + obtain ⟨⟨-, hfclue⟩, hfbody⟩ := hft + simp only [evalGBy] at hG + cases hcl : evalGBy q fuel s clue with + | none => rw [hcl] at hG; simp at hG + | some vc => + rw [hcl] at hG + obtain ⟨hNcl, hFcl, _⟩ := ih s clue vc hs hfclue hcl + obtain ⟨hNb, hFb, hBr⟩ := ih s body r hs hfbody hG + exact ⟨by simp only [evalN, hNcl, hNb], + by simp only [evalFBy, hFcl, hFb], hBr⟩ + | .cell (.atom hint) body => + obtain ⟨-, hfbody⟩ := hft + simp only [evalGBy] at hG + obtain ⟨hNb, hFb, hBr⟩ := ih s body r hs hfbody hG + exact ⟨by simp only [evalN, hNb], + by simp only [evalFBy, hFb], hBr⟩ + | .atom _ => simp [evalGBy] at hG + +private theorem guarded_conditional_agreement (q fuel : ℕ) + (ih : + ∀ (s f r : Noun), + BoundedBy q s → + BoundedBy q f → + evalGBy q fuel s f = some r → evalN fuel s f = some r ∧ evalFBy q fuel s f = some r ∧ + BoundedBy q r) + (s r : Noun) (hs : BoundedBy q s) (tail : Noun) (hft : BoundedBy q tail) + (hG : evalGBy q (fuel + 1) s ((atom 6).cell tail) = some r) : + evalN (fuel + 1) s ((atom 6).cell tail) = some r ∧ + evalFBy q (fuel + 1) s ((atom 6).cell tail) = some r ∧ BoundedBy q r := by + match tail with + | .cell b (.cell c d) => + obtain ⟨hfb, hfc, hfd⟩ := hft + simp only [evalGBy] at hG + cases hb : evalGBy q fuel s b with + | none => rw [hb] at hG; simp at hG + | some vb => + obtain ⟨hNb, hFb, _⟩ := ih s b vb hs hfb hb + rw [hb] at hG + match vb, hNb, hFb, hG with + | .atom 0, hNb, hFb, hG => + obtain ⟨hNc, hFc, hBr⟩ := ih s c r hs hfc hG + exact ⟨by simp only [evalN, hNb, hNc], + by simp only [evalFBy, hFb, hFc], hBr⟩ + | .atom 1, hNb, hFb, hG => + obtain ⟨hNd, hFd, hBr⟩ := ih s d r hs hfd hG + exact ⟨by simp only [evalN, hNb, hNd], + by simp only [evalFBy, hFb, hFd], hBr⟩ + | .atom (n+2), _, _, hG => simp at hG + | .cell _ _, _, _, hG => simp at hG + | .cell b (.atom _) => simp [evalGBy] at hG + | .atom _ => simp [evalGBy] at hG + +theorem agreeBy (q : ℕ) (hq : 1 < q) : + ∀ (fuel : Nat) (s f r : Noun), BoundedBy q s → BoundedBy q f → + evalGBy q fuel s f = some r → + evalN fuel s f = some r ∧ evalFBy q fuel s f = some r ∧ BoundedBy q r := by + intro fuel + induction fuel with + | zero => intro s f r _ _ hG; simp [evalGBy] at hG + | succ fuel ih => + intro s f r hs hf hG + match f with + | .atom a => simp [evalGBy] at hG + | .cell (.cell b c) d => + obtain ⟨hfbc, hfd⟩ := hf + simp only [evalGBy] at hG + cases hbc : evalGBy q fuel s (.cell b c) with + | none => rw [hbc] at hG; simp at hG + | some l => + cases hd : evalGBy q fuel s d with + | none => rw [hbc, hd] at hG; simp at hG + | some r' => + rw [hbc, hd] at hG + simp only [Option.some.injEq] at hG + obtain ⟨hNbc, hFbc, hBl⟩ := ih s (.cell b c) l hs hfbc hbc + obtain ⟨hNd, hFd, hBr⟩ := ih s d r' hs hfd hd + subst hG + refine ⟨?_, ?_, ⟨hBl, hBr⟩⟩ + · simp only [evalN, hNbc, hNd] + · simp only [evalFBy, hFbc, hFd] + | .cell (.atom op) tail => + obtain ⟨-, hft⟩ := hf + match op with + | 0 => + simp only [evalGBy] at hG + match tail with + | .atom ax => + refine ⟨?_, ?_, slot_boundedBy q ax s hs hG⟩ + · simpa only [evalN] using hG + · simpa only [evalFBy] using hG + | .cell _ _ => simp at hG + | 1 => + simp only [evalGBy] at hG + obtain rfl := Option.some.inj hG + exact ⟨by simp only [evalN], by simp only [evalFBy], hft⟩ + | 2 => + match tail with + | .cell b c => + obtain ⟨hfb, hfc⟩ := hft + simp only [evalGBy] at hG + cases hb : evalGBy q fuel s b with + | none => rw [hb] at hG; simp at hG + | some sb => + cases hc : evalGBy q fuel s c with + | none => rw [hb, hc] at hG; simp at hG + | some sc => + rw [hb, hc] at hG + obtain ⟨hNb, hFb, hBb⟩ := ih s b sb hs hfb hb + obtain ⟨hNc, hFc, hBc⟩ := ih s c sc hs hfc hc + obtain ⟨hN2, hF2, hBr⟩ := ih sb sc r hBb hBc hG + exact ⟨by simp only [evalN, hNb, hNc, hN2], + by simp only [evalFBy, hFb, hFc, hF2], hBr⟩ + | .atom _ => simp [evalGBy] at hG + | 3 => + simp only [evalGBy] at hG + cases ht : evalGBy q fuel s tail with + | none => rw [ht] at hG; simp at hG + | some v => + rw [ht] at hG + simp only [Option.map_some, Option.some.injEq] at hG + obtain ⟨hN, hF, _⟩ := ih s tail v hs hft ht + subst hG + exact ⟨by simp only [evalN, hN, Option.map_some], + by simp only [evalFBy, hF, Option.map_some], wut_boundedBy hq v⟩ + | 4 => + simp only [evalGBy] at hG + cases ht : evalGBy q fuel s tail with + | none => rw [ht] at hG; simp at hG + | some v => + rw [ht] at hG + obtain ⟨hN, hF, hBv⟩ := ih s tail v hs hft ht + cases v with + | cell _ _ => simp [lusGBy] at hG + | atom n => + simp only [Option.bind_some, lusGBy] at hG + split at hG + · rename_i hlt + obtain rfl := Option.some.inj hG + refine ⟨?_, ?_, ?_⟩ + · simp only [evalN, hN, Option.bind_some, Noun.lus] + · simp only [evalFBy, hF, Option.bind_some, lusFBy, Nat.mod_eq_of_lt hlt] + · exact hlt + · simp at hG + | 5 => + match tail with + | .cell b c => + obtain ⟨hfb, hfc⟩ := hft + simp only [evalGBy] at hG + cases hb : evalGBy q fuel s b with + | none => rw [hb] at hG; simp at hG + | some sb => + cases hc : evalGBy q fuel s c with + | none => rw [hb, hc] at hG; simp at hG + | some sc => + rw [hb, hc] at hG + simp only [Option.some.injEq] at hG + obtain ⟨hNb, hFb, _⟩ := ih s b sb hs hfb hb + obtain ⟨hNc, hFc, _⟩ := ih s c sc hs hfc hc + subst hG + exact ⟨by simp only [evalN, hNb, hNc], + by simp only [evalFBy, hFb, hFc], tis_boundedBy hq sb sc⟩ + | .atom _ => simp [evalGBy] at hG + | 6 => + exact guarded_conditional_agreement q fuel ih s r hs tail hft hG + | 7 => + match tail with + | .cell b c => + obtain ⟨hfb, hfc⟩ := hft + simp only [evalGBy] at hG + cases hb : evalGBy q fuel s b with + | none => rw [hb] at hG; simp at hG + | some sb => + rw [hb] at hG + obtain ⟨hNb, hFb, hBb⟩ := ih s b sb hs hfb hb + obtain ⟨hN7, hF7, hBr⟩ := ih sb c r hBb hfc hG + exact ⟨by simp only [evalN, hNb, hN7], + by simp only [evalFBy, hFb, hF7], hBr⟩ + | .atom _ => simp [evalGBy] at hG + | 8 => + match tail with + | .cell b c => + obtain ⟨hfb, hfc⟩ := hft + simp only [evalGBy] at hG + cases hb : evalGBy q fuel s b with + | none => rw [hb] at hG; simp at hG + | some sb => + rw [hb] at hG + obtain ⟨hNb, hFb, hBb⟩ := ih s b sb hs hfb hb + obtain ⟨hN8, hF8, hBr⟩ := ih (.cell sb s) c r ⟨hBb, hs⟩ hfc hG + exact ⟨by simp only [evalN, hNb, hN8], + by simp only [evalFBy, hFb, hF8], hBr⟩ + | .atom _ => simp [evalGBy] at hG + | 9 => + match tail with + | .cell (.atom ax) c => + obtain ⟨-, hfc⟩ := hft + simp only [evalGBy] at hG + cases hc : evalGBy q fuel s c with + | none => rw [hc] at hG; simp at hG + | some core => + rw [hc] at hG + dsimp only at hG + obtain ⟨hNc, hFc, hBcore⟩ := ih s c core hs hfc hc + cases harm : slot ax core with + | none => rw [harm] at hG; simp at hG + | some arm => + rw [harm] at hG + dsimp only at hG + have hBarm : BoundedBy q arm := slot_boundedBy q ax core hBcore harm + obtain ⟨hN9, hF9, hBr⟩ := ih core arm r hBcore hBarm hG + exact ⟨by simp only [evalN, hNc, harm, hN9], + by simp only [evalFBy, hFc, harm, hF9], hBr⟩ + | .cell (.cell _ _) _ => simp [evalGBy] at hG + | .atom _ => simp [evalGBy] at hG + | 10 => + match tail with + | .cell (.cell (.atom ax) c) d => + obtain ⟨⟨-, hfc⟩, hfd⟩ := hft + simp only [evalGBy] at hG + cases hc : evalGBy q fuel s c with + | none => rw [hc] at hG; simp at hG + | some new => + cases hd : evalGBy q fuel s d with + | none => rw [hc, hd] at hG; simp at hG + | some old => + rw [hc, hd] at hG + obtain ⟨hNc, hFc, hBnew⟩ := ih s c new hs hfc hc + obtain ⟨hNd, hFd, hBold⟩ := ih s d old hs hfd hd + refine ⟨?_, ?_, edit_boundedBy q ax new old hBnew hBold hG⟩ + · simp only [evalN, hNc, hNd]; exact hG + · simp only [evalFBy, hFc, hFd]; exact hG + | .cell (.cell (.cell _ _) _) _ => simp [evalGBy] at hG + | .cell (.atom _) _ => simp [evalGBy] at hG + | .atom _ => simp [evalGBy] at hG + | 11 => + exact guarded_hint_agreement q fuel ih s r hs tail hft hG + | (n+12) => simp [evalGBy] at hG + +/-! ## The guarded evaluator refines `evalN`: the guard only removes behaviours -/ + +theorem evalGBy_le_evalN {q : ℕ} (hq : 1 < q) {fuel : Nat} {s f r : Noun} + (hs : BoundedBy q s) (hf : BoundedBy q f) (h : evalGBy q fuel s f = some r) : + evalN fuel s f = some r := + (agreeBy q hq fuel s f r hs hf h).1 + +/-! ## The no-overflow agreement theorem -/ + +/-- **No-overflow agreement.** For `q`-bounded inputs, a successful guarded run contains no + modular wrap at `q`, so the Nat and modulo-`q` evaluators return the same `q`-bounded noun. -/ +theorem noOverflow_agreeBy {q : ℕ} (hq : 1 < q) {fuel : Nat} {s f r : Noun} + (hs : BoundedBy q s) (hf : BoundedBy q f) (hno : evalGBy q fuel s f = some r) : + evalN fuel s f = some r ∧ evalFBy q fuel s f = some r ∧ BoundedBy q r := + agreeBy q hq fuel s f r hs hf hno + +/-! ## The modular evaluator is a field-noun endomorphism + +Unconditionally (no guard): on `q`-bounded inputs, every successful modular run returns a +`q`-bounded noun. `evalFBy q` maps canonical field nouns to canonical field nouns — its OP₄ +increment is `(n+1) mod q < q`, and every other opcode only rearranges existing (bounded) leaves +or produces `0`/`1`. -/ +theorem evalFBy_boundedBy {q : ℕ} (hq : 1 < q) : + ∀ (fuel : Nat) (s f r : Noun), BoundedBy q s → BoundedBy q f → + evalFBy q fuel s f = some r → BoundedBy q r := by + intro fuel + induction fuel with + | zero => intro s f r _ _ h; simp [evalFBy] at h + | succ fuel ih => + intro s f r hs hf h + match f with + | .atom a => simp [evalFBy] at h + | .cell (.cell b c) d => + obtain ⟨hfbc, hfd⟩ := hf + simp only [evalFBy] at h + cases hbc : evalFBy q fuel s (.cell b c) with + | none => rw [hbc] at h; simp at h + | some l => + cases hd : evalFBy q fuel s d with + | none => rw [hbc, hd] at h; simp at h + | some r' => + rw [hbc, hd] at h; simp only [Option.some.injEq] at h; subst h + exact ⟨ih s (.cell b c) l hs hfbc hbc, ih s d r' hs hfd hd⟩ + | .cell (.atom op) tail => + obtain ⟨-, hft⟩ := hf + match op with + | 0 => + simp only [evalFBy] at h + match tail with + | .atom ax => exact slot_boundedBy q ax s hs h + | .cell _ _ => simp at h + | 1 => simp only [evalFBy] at h; obtain rfl := Option.some.inj h; exact hft + | 2 => + match tail with + | .cell b c => + obtain ⟨hfb, hfc⟩ := hft + simp only [evalFBy] at h + cases hb : evalFBy q fuel s b with + | none => rw [hb] at h; simp at h + | some sb => + cases hc : evalFBy q fuel s c with + | none => rw [hb, hc] at h; simp at h + | some sc => + rw [hb, hc] at h + exact ih sb sc r (ih s b sb hs hfb hb) (ih s c sc hs hfc hc) h + | .atom _ => simp [evalFBy] at h + | 3 => + simp only [evalFBy] at h + cases ht : evalFBy q fuel s tail with + | none => rw [ht] at h; simp at h + | some v => + rw [ht] at h; simp only [Option.map_some, Option.some.injEq] at h + subst h; exact wut_boundedBy hq v + | 4 => + simp only [evalFBy] at h + cases ht : evalFBy q fuel s tail with + | none => rw [ht] at h; simp at h + | some v => rw [ht] at h; exact lusFBy_bounded (by omega) h + | 5 => + match tail with + | .cell b c => + obtain ⟨hfb, hfc⟩ := hft + simp only [evalFBy] at h + cases hb : evalFBy q fuel s b with + | none => rw [hb] at h; simp at h + | some sb => + cases hc : evalFBy q fuel s c with + | none => rw [hb, hc] at h; simp at h + | some sc => + rw [hb, hc] at h; simp only [Option.some.injEq] at h; subst h + exact tis_boundedBy hq sb sc + | .atom _ => simp [evalFBy] at h + | 6 => + match tail with + | .cell b (.cell c d) => + obtain ⟨hfb, hfc, hfd⟩ := hft + simp only [evalFBy] at h + cases hb : evalFBy q fuel s b with + | none => rw [hb] at h; simp at h + | some vb => + rw [hb] at h + match vb, h with + | .atom 0, h => exact ih s c r hs hfc h + | .atom 1, h => exact ih s d r hs hfd h + | .atom (n+2), h => simp at h + | .cell _ _, h => simp at h + | .cell b (.atom _) => simp [evalFBy] at h + | .atom _ => simp [evalFBy] at h + | 7 => + match tail with + | .cell b c => + obtain ⟨hfb, hfc⟩ := hft + simp only [evalFBy] at h + cases hb : evalFBy q fuel s b with + | none => rw [hb] at h; simp at h + | some sb => + rw [hb] at h + exact ih sb c r (ih s b sb hs hfb hb) hfc h + | .atom _ => simp [evalFBy] at h + | 8 => + match tail with + | .cell b c => + obtain ⟨hfb, hfc⟩ := hft + simp only [evalFBy] at h + cases hb : evalFBy q fuel s b with + | none => rw [hb] at h; simp at h + | some sb => + rw [hb] at h + exact ih (.cell sb s) c r ⟨ih s b sb hs hfb hb, hs⟩ hfc h + | .atom _ => simp [evalFBy] at h + | 9 => + match tail with + | .cell (.atom ax) c => + obtain ⟨-, hfc⟩ := hft + simp only [evalFBy] at h + cases hc : evalFBy q fuel s c with + | none => rw [hc] at h; simp at h + | some core => + rw [hc] at h; dsimp only at h + cases harm : slot ax core with + | none => rw [harm] at h; simp at h + | some arm => + rw [harm] at h; dsimp only at h + have hBcore := ih s c core hs hfc hc + exact ih core arm r hBcore (slot_boundedBy q ax core hBcore harm) h + | .cell (.cell _ _) _ => simp [evalFBy] at h + | .atom _ => simp [evalFBy] at h + | 10 => + match tail with + | .cell (.cell (.atom ax) c) d => + obtain ⟨⟨-, hfc⟩, hfd⟩ := hft + simp only [evalFBy] at h + cases hc : evalFBy q fuel s c with + | none => rw [hc] at h; simp at h + | some new => + cases hd : evalFBy q fuel s d with + | none => rw [hc, hd] at h; simp at h + | some old => + rw [hc, hd] at h + exact edit_boundedBy q ax new old (ih s c new hs hfc hc) + (ih s d old hs hfd hd) h + | .cell (.cell (.cell _ _) _) _ => simp [evalFBy] at h + | .cell (.atom _) _ => simp [evalFBy] at h + | .atom _ => simp [evalFBy] at h + | 11 => + match tail with + | .cell (.cell hd clue) body => + obtain ⟨⟨-, -⟩, hfbody⟩ := hft + simp only [evalFBy] at h + cases hcl : evalFBy q fuel s clue with + | none => rw [hcl] at h; simp at h + | some vc => rw [hcl] at h; exact ih s body r hs hfbody h + | .cell (.atom hint) body => + obtain ⟨-, hfbody⟩ := hft + simp only [evalFBy] at h + exact ih s body r hs hfbody h + | .atom _ => simp [evalFBy] at h + | (n+12) => simp [evalFBy] at h + +/-! ## `ZMod q` representative-level injectivity + +On `q`-bounded nouns the structural Nock equality used by OP₅ (`main.tex:1525`) coincides with +field equality in `ZMod q`: distinct canonical residues `< q` stay distinct. Primality is not +needed for this representative-level fact. -/ + +/-- The `Nat` cast into `ZMod q` is injective on canonical representatives `[0,q)`. -/ +theorem zmod_natCast_inj_of_lt {q m n : ℕ} (hm : m < q) (hn : n < q) + (h : (m : ZMod q) = (n : ZMod q)) : m = n := by + have := congrArg ZMod.val h + rwa [ZMod.val_natCast_of_lt hm, ZMod.val_natCast_of_lt hn] at this + +end Nock.FieldEmbedding diff --git a/LeanPool/PureNock/NPR.lean b/LeanPool/PureNock/NPR.lean new file mode 100644 index 0000000000..76c8345f4f --- /dev/null +++ b/LeanPool/PureNock/NPR.lean @@ -0,0 +1,703 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Dyck +public import Mathlib.Algebra.Polynomial.Roots +public import Mathlib.Algebra.Polynomial.Degree.Lemmas +public import Mathlib.Algebra.Order.Ring.Pow +public import Mathlib.Data.List.GetD +public import Mathlib.Tactic.FieldSimp + +/-! +# Noun Polynomial Representation (NPR) + +The paper's succinct polynomial representation of nouns and its collision-soundness bounds +(`main.tex:1162–1226`). A noun `n = (dt, w)` (`main.tex:1164`) is a leaf array `dt ∈ 𝔽^λ` +together with a Dyck word `w ∈ {0,1}^{2(λ−1)}` fixing the tree shape. The leaf array is +`Noun.leafList` and the Dyck word `Noun.dyckWord` (both from `Nock/Dyck.lean`), cast into a +field `K` at this layer. + +Contents: +* `Φ` (`main.tex:1170–1173`) — the NPR: a noun ↦ triple of univariate polynomials + `Φ(dt,w) = ( X^{|dt|}, Σ dtᵢ X^{i−1}, Σ wᵢ X^{i−1} )`. +* `fn` (`def:ion_univariate`, `main.tex:1180–1184`) — the fingerprint + `fn(n; r) = Φ(dt,w)[r,r,r] = (r^{|dt|}, F_d(r), F_w(r))`, `r ∈ K`. +* `NPR_decomp` (`Lem:NPR_decomp`, `main.tex:1218–1226`) — the three `cons` concatenation + identities (with the corrected Φ₃ exponents; see the section header). +* `collision_card_le` / `collision_prob_le` (`lem:ion_univariate_sec`, `main.tex:1188`) — + distinct nouns collide under `fn` for at most `2(2Λ−1)` values of `r`. +* `collision_table_card_le` / `collision_table_prob_le` (`corr:ion_single_security`, + `main.tex:1197`) — union bound over `k` nouns: `Pr ≤ ε · k(k−1)/2`. + +Everything is proved over an ARBITRARY finite field `K` (`[Field K] [Fintype K] +[DecidableEq K]`): no concrete field, no primality axiom. "`Pr_{r∈K}[…]`" is the counting +measure (satisfying `r` count divided by `|K| = Fintype.card K`); the core results are the +exact cardinality bounds (`…_card_le`), with `…_prob_le` dividing by `|K|` in `ℚ`. +Schwartz–Zippel is `Polynomial.card_roots'` via `card_eval_zero_le`. +-/ + +@[expose] public section + +open Polynomial + +namespace Nock + +namespace NPR + +/-! ### Coefficient-vector polynomials -/ + +section CPoly +variable {K : Type*} [Field K] + +/-- `cpoly f n = Σ_{i f i - g i) n := by + unfold cpoly + rw [← Finset.sum_sub_distrib] + refine Finset.sum_congr rfl ?_ + intro i _ + rw [← sub_mul, ← C_sub] + +theorem cpoly_natDegree_le (f : ℕ → K) (n : ℕ) : (cpoly f n).natDegree ≤ n - 1 := by + rw [natDegree_le_iff_coeff_eq_zero] + intro N hN + rw [cpoly_coeff] + have : ¬ N < n := by omega + simp [this] + +theorem cpoly_ne_zero_of_coeff {f g : ℕ → K} {n j : ℕ} (hj : j < n) (h : f j ≠ g j) : + cpoly f n - cpoly g n ≠ 0 := by + rw [cpoly_sub] + intro hzero + apply h + have hc : (cpoly (fun i => f i - g i) n).coeff j = f j - g j := by + rw [cpoly_coeff]; simp [hj] + rw [hzero, coeff_zero] at hc + exact sub_eq_zero.mp hc.symm + +end CPoly + +/-! ### List-indexed polynomials -/ + +section ListPoly +variable {K : Type*} [Field K] + +/-- The polynomial whose coefficient vector is the list `l` (default `0` past the end). -/ +noncomputable def listPoly (l : List K) : K[X] := + cpoly (fun i => l.getD i 0) l.length + +theorem listPoly_natDegree_le (l : List K) : (listPoly l).natDegree ≤ l.length - 1 := + cpoly_natDegree_le _ _ + +/-- Two equal-length lists that differ have distinct coefficient polynomials. -/ +theorem listPoly_sub_ne_zero {l₁ l₂ : List K} (hlen : l₁.length = l₂.length) (hne : l₁ ≠ l₂) : + listPoly l₁ - listPoly l₂ ≠ 0 := by + -- find a position where they differ + have hex : ∃ j, j < l₁.length ∧ l₁.getD j 0 ≠ l₂.getD j 0 := by + by_contra h + simp only [not_exists, not_and, ne_eq, not_not] at h + apply hne + apply List.ext_getElem hlen + intro j hj hj2 + have hjeq := h j hj + rwa [List.getD_eq_getElem l₁ 0 hj, List.getD_eq_getElem l₂ 0 (hlen ▸ hj)] at hjeq + obtain ⟨j, hj, hval⟩ := hex + unfold listPoly + rw [hlen] + exact cpoly_ne_zero_of_coeff (hlen ▸ hj) hval + +theorem listPoly_sub_natDegree_le {l₁ l₂ : List K} (hlen : l₁.length = l₂.length) : + (listPoly l₁ - listPoly l₂).natDegree ≤ l₁.length - 1 := by + refine le_trans (natDegree_sub_le _ _) ?_ + rw [max_le_iff] + exact ⟨listPoly_natDegree_le l₁, by rw [hlen]; exact listPoly_natDegree_le l₂⟩ + +/-! #### Concatenation identities for `listPoly` (the algebraic backbone of `Lem:NPR_decomp`). + + These express how a list-indexed polynomial factors when its coefficient list is split. + They are what turn the DFS/Dyck concatenation of nouns under `cell` into the polynomial + identities of `Lem:NPR_decomp` (main.tex:1218). -/ + +theorem listPoly_coeff (l : List K) (j : ℕ) : + (listPoly l).coeff j = if j < l.length then l.getD j 0 else 0 := by + unfold listPoly; rw [cpoly_coeff] + +@[simp] theorem listPoly_nil : listPoly ([] : List K) = 0 := by + unfold listPoly cpoly; simp + +/-- `listPoly (x :: l) = C x + X · listPoly l`: prepending a coefficient shifts by one. -/ +theorem listPoly_cons (x : K) (l : List K) : + listPoly (x :: l) = C x + X * listPoly l := by + ext j + rw [coeff_add, listPoly_coeff] + match j with + | 0 => + rw [mul_coeff_zero, coeff_X_zero, zero_mul, add_zero, coeff_C_zero, + List.getD_cons_zero] + simp + | k + 1 => + rw [coeff_X_mul, listPoly_coeff, coeff_C, List.getD_cons_succ, List.length_cons] + simp + +/-- **`listPoly (a ++ b) = listPoly a + X^{|a|} · listPoly b`** — the paper's stated + concatenation rule (main.tex:1218 discussion): a coefficient list `a ++ b` gives the + polynomial of `a` plus the polynomial of `b` shifted up by `|a|` places. -/ +theorem listPoly_append (a b : List K) : + listPoly (a ++ b) = listPoly a + X ^ a.length * listPoly b := by + induction a with + | nil => simp + | cons x a' ih => + rw [List.cons_append, listPoly_cons, ih, listPoly_cons, List.length_cons, pow_succ] + ring + +end ListPoly + +/-! ### The NPR and the fingerprint -/ + +section FnLayer +variable {K : Type*} [Field K] + +/-- Leaf array `dt` cast into the field `K` (ATOM MODEL (a): `Nat → K`), main.tex:1164. -/ +def leafF (n : Noun) : List K := n.leafList.map (fun k : ℕ => (k : K)) + +/-- Dyck word `w ∈ {0,1}*` cast into the field `K` (`0 ↦ 0`, `1 ↦ 1`), main.tex:1166. -/ +def dyckBits (n : Noun) : List K := n.dyckWord.map (fun b => if b then (1 : K) else 0) + +@[simp] theorem leafF_length (n : Noun) : (leafF (K := K) n).length = n.leaves := by + simp [leafF] + +theorem dyckBits_length (n : Noun) : (dyckBits (K := K) n).length + 2 = 2 * n.leaves := by + simp only [dyckBits, List.length_map] + exact n.dyckWord_length + +/-- **The Noun Polynomial Representation (main.tex:1170–1173).** + `Φ(dt,w) = ( X₁^{|dt|}, Σ dtᵢ X₂^{i−1}, Σ wᵢ X₃^{i−1} )` — represented as a triple of + univariate polynomials (each in its own indeterminate `X`). -/ +noncomputable def Φ (n : Noun) : K[X] × K[X] × K[X] := + (X ^ n.leaves, listPoly (leafF n), listPoly (dyckBits n)) + +/-- **The fingerprint (def:ion_univariate, main.tex:1180–1184).** + `fn(n; r) = Φ(dt,w)[r,r,r] = (r^{|dt|}, F_d(r), F_w(r))`, evaluated at `r ∈ K`. -/ +noncomputable def fn (n : Noun) (r : K) : K × K × K := + (r ^ n.leaves, (listPoly (leafF n)).eval r, (listPoly (dyckBits n)).eval r) + +/-- The fingerprint is `Φ` evaluated at `(r,r,r)` (main.tex:1183). -/ +theorem fn_eq_Φ_eval (n : Noun) (r : K) : + fn n r = ((Φ n).1.eval r, (Φ (K := K) n).2.1.eval r, (Φ n).2.2.eval r) := by + simp [fn, Φ] + +@[simp] theorem Φ_fst (n : Noun) : (Φ (K := K) n).1 = X ^ n.leaves := rfl +@[simp] theorem Φ_snd_fst (n : Noun) : (Φ (K := K) n).2.1 = listPoly (leafF n) := rfl +@[simp] theorem Φ_snd_snd (n : Noun) : (Φ (K := K) n).2.2 = listPoly (dyckBits n) := rfl + +end FnLayer + +/-! ### `Lem:NPR_decomp` — the memory-table backbone (main.tex:1218) + + How the three NPR polynomials concatenate under `cons`. We first record the two + structural concatenation lemmas (leaf array in DFS order; Dyck word by the grammar + `w = 0·w_L·1·w_R`), then read off the three `Φ`-identities. + + ⚠ **FAITHFULNESS — the Φ₃ (Dyck) exponents (main.tex:1223).** The paper prints + `Φ(n)₃ = Φ(nL)₃ + X^{2(λL−1)} + X^{2λL−1}·Φ(nR)₃`. + Derived carefully against `Dyck.encode`'s grammar `w = 0·w_L·1·w_R` and the paper's own + `F_w(X) = Σ_i w_i X^{i−1}` (`listPoly`), the CORRECT identity is + `Φ(n)₃ = X·Φ(nL)₃ + X^{2λL−1} + X^{2λL}·Φ(nR)₃`. + The paper's form is off by one global factor of `X` (equivalently: it drops the leading + open-delimiter bit's `X`-shift), i.e. paper-RHS `= X⁻¹·(correct RHS)`. Concretely, for + `n = cons(atom,atom)` the Dyck word is `[0,1]`, so `F_w(n) = X`, which the correct + identity yields (`X·0 + X^{2·1−1} + X^{2·1}·0 = X`) but the paper's yields `1` + (`0 + X^{2(1−1)} + X^{2·1−1}·0`). We prove and use the correct identity `Phi3_cons` + and flag this deviation. -/ + +section Decomp +variable {K : Type*} [Field K] + +/-- Leaf array concatenates under `cell` (DFS order), main.tex:1218. -/ +@[simp] theorem leafF_cell (l r : Noun) : + leafF (K := K) (Noun.cell l r) = leafF l ++ leafF r := by + simp [leafF, Noun.leafList] + +/-- Dyck word under `cell`: `w = 0 · w_L · 1 · w_R` (the grammar of `Dyck.encode`, + main.tex:337), cast to field bits (`false ↦ 0`, `true ↦ 1`). -/ +theorem dyckBits_cell (l r : Noun) : + dyckBits (K := K) (Noun.cell l r) + = (0 : K) :: (dyckBits l ++ (1 : K) :: dyckBits r) := by + simp only [dyckBits, Noun.dyckWord, Noun.shape, Nock.BTree.encode, List.map_cons, + List.map_append] + norm_num + +/-- **`Lem:NPR_decomp`, Φ₁ (main.tex:1221):** `Φ(cons(l,r))₁ = Φ(l)₁ · Φ(r)₁`, + i.e. `X^{|dt|} = X^{λL}·X^{λR}`. -/ +theorem Phi1_cons (l r : Noun) : + (Φ (K := K) (Noun.cell l r)).1 = (Φ l).1 * (Φ r).1 := by + simp only [Φ_fst, Noun.leaves, pow_add] + +/-- **`Lem:NPR_decomp`, Φ₂ (main.tex:1222):** `Φ(cons(l,r))₂ = Φ(l)₂ + X^{λL}·Φ(r)₂`. -/ +theorem Phi2_cons (l r : Noun) : + (Φ (K := K) (Noun.cell l r)).2.1 = (Φ l).2.1 + X ^ l.leaves * (Φ r).2.1 := by + simp only [Φ_snd_fst, leafF_cell, listPoly_append, leafF_length] + +/-- **`Lem:NPR_decomp`, Φ₃ (main.tex:1223), CORRECTED exponents (see the section header):** + `Φ(cons(l,r))₃ = X·Φ(l)₃ + X^{2λL−1} + X^{2λL}·Φ(r)₃`. -/ +theorem Phi3_cons (l r : Noun) : + (Φ (K := K) (Noun.cell l r)).2.2 + = X * (Φ l).2.2 + X ^ (2 * l.leaves - 1) + X ^ (2 * l.leaves) * (Φ r).2.2 := by + have hd : (dyckBits (K := K) l).length + 2 = 2 * l.leaves := dyckBits_length l + have h1 : (dyckBits (K := K) l).length + 1 = 2 * l.leaves - 1 := by omega + simp only [Φ_snd_snd, dyckBits_cell, listPoly_cons, listPoly_append, C_0, C_1] + rw [← h1, ← hd] + ring + +/-- **`Lem:NPR_decomp` (main.tex:1218).** The three algebraic identities characterizing + `cons`. We state the paper's `↔ n = cons(nL,nR)` as `Φ n = Φ (cons nL nR)` — *field-noun* + equality: `Φ n = Φ n'` is equality of the pair (leaf-count, leaf polynomial, Dyck polynomial), + i.e. of the `(dyck word, 𝔽-valued leaf array)`, which is the paper's notion of noun equality + over `𝔽` (the paper's nouns are field-valued, `main.tex:347–349`, `1023`). (Structural + `n = cell nL nR` implies it via `congrArg`, `NPR_decomp_of_cons`; the converse to *structural* + `Nat` equality does NOT hold — distinct `Nat` leaves congruent mod the characteristic give the + same field-noun (compiled counterexample `RedTeam.Batch5.b5_npr_decomp_field_not_structural` + over `ZMod 5`), the "same field-noun" + phenomenon also underlying `collision_card_le`. This module does not prove `Φ` injective on + field-nouns; that is the paper's ion property, whose soundness error is bounded by + `collision_prob_le`.) -/ +theorem NPR_decomp (n nL nR : Noun) : + Φ (K := K) n = Φ (K := K) (Noun.cell nL nR) + ↔ ( (Φ (K := K) n).1 = (Φ (K := K) nL).1 * (Φ (K := K) nR).1 + ∧ (Φ (K := K) n).2.1 = (Φ (K := K) nL).2.1 + X ^ nL.leaves * (Φ (K := K) nR).2.1 + ∧ (Φ (K := K) n).2.2 = X * (Φ (K := K) nL).2.2 + X ^ (2 * nL.leaves - 1) + + X ^ (2 * nL.leaves) * (Φ (K := K) nR).2.2 ) := by + constructor + · intro h + rw [h] + exact ⟨Phi1_cons nL nR, Phi2_cons nL nR, Phi3_cons nL nR⟩ + · rintro ⟨h1, h2, h3⟩ + have e1 : (Φ (K := K) n).1 = (Φ (K := K) (Noun.cell nL nR)).1 := by + rw [h1, Phi1_cons] + have e2 : (Φ (K := K) n).2.1 = (Φ (K := K) (Noun.cell nL nR)).2.1 := by + rw [h2, Phi2_cons] + have e3 : (Φ (K := K) n).2.2 = (Φ (K := K) (Noun.cell nL nR)).2.2 := by + rw [h3, Phi3_cons] + exact Prod.ext_iff.mpr ⟨e1, Prod.ext_iff.mpr ⟨e2, e3⟩⟩ + +/-- Structural `←` direction of `Lem:NPR_decomp`: if `n` is literally `cons(nL,nR)` then its + NPR satisfies the three identities. -/ +theorem NPR_decomp_of_cons {n nL nR : Noun} (h : n = Noun.cell nL nR) : + (Φ (K := K) n).1 = (Φ (K := K) nL).1 * (Φ (K := K) nR).1 + ∧ (Φ (K := K) n).2.1 = (Φ (K := K) nL).2.1 + X ^ nL.leaves * (Φ (K := K) nR).2.1 + ∧ (Φ (K := K) n).2.2 = X * (Φ (K := K) nL).2.2 + X ^ (2 * nL.leaves - 1) + + X ^ (2 * nL.leaves) * (Φ (K := K) nR).2.2 := + (NPR_decomp n nL nR).mp (by rw [h]) + +/-! #### Fingerprint-level `cons` composition (the constraint form) + + `Lem:NPR_decomp` characterizes `cons` at the polynomial level. A zkVM AIR never carries + the polynomials — a trace row carries only the *evaluated* fingerprints `fn(·; x) ∈ K³` — + so a cons gadget states the same identities as field arithmetic on fingerprint components. + These are the evaluations of `Phi1_cons`/`Phi2_cons`/`Phi3_cons` at `x`, plus an + inverse-free multiplied-through form of the Dyck identity (`fn_cons_dyck_constraint`). + That form is a **completeness** identity for honest `fn` values (no `x⁻¹` in the + statement). It is **not** a proved soundness theorem for committed columns, and at + `x = 0` it degenerates to `0 = 0` (every noun has ≥1 leaf, so `fn₁(l;0) = 0`), constraining + nothing — useful as an AIR-style equation only for nonzero challenges. + + ⚠ **Orientation.** This layer's `fn` / `fnHorner` evaluate with the FIRST leaf/Dyck + symbol at the constant coefficient (`listPoly`). A different Horner convention that + places the first symbol at the highest power would need mirrored cons identities; those + mirrored equations are **not** theorems in this file. `fnHorner_eq_fn` ties the running + code to the non-mirrored orientation proved here. -/ + +/-- `fn₁` composes multiplicatively under `cons` (evaluation of `Phi1_cons`): + `fn₁(cons(l,r)) = fn₁(l)·fn₁(r)`. -/ +theorem fn_cons_size (l r : Noun) (x : K) : + (fn (Noun.cell l r) x).1 = (fn l x).1 * (fn r x).1 := by + simp only [fn, Noun.leaves, pow_add] + +/-- `fn₂` (leaf fingerprint) composes affinely under `cons` (evaluation of `Phi2_cons`): + `fn₂(cons(l,r)) = fn₂(l) + fn₁(l)·fn₂(r)`. -/ +theorem fn_cons_leaf (l r : Noun) (x : K) : + (fn (Noun.cell l r) x).2.1 = (fn l x).2.1 + (fn l x).1 * (fn r x).2.1 := by + simp only [fn, leafF_cell, listPoly_append, eval_add, eval_mul, eval_pow, eval_X, + leafF_length] + +/-- `fn₃` (Dyck fingerprint) composes under `cons` with the delimiter power made explicit + (evaluation of the corrected `Phi3_cons`): + `fn₃(cons(l,r)) = x·fn₃(l) + x^{2λL−1} + x^{2λL}·fn₃(r)`. -/ +theorem fn_cons_dyck (l r : Noun) (x : K) : + (fn (Noun.cell l r) x).2.2 + = x * (fn l x).2.2 + x ^ (2 * l.leaves - 1) + + x ^ (2 * l.leaves) * (fn r x).2.2 := by + have h := congrArg (Polynomial.eval x) (Phi3_cons (K := K) l r) + simpa only [fn, Φ_snd_snd, eval_add, eval_mul, eval_pow, eval_X] using h + +/-- **Fingerprint `cons` composition (`fn_cons`)** — `fn(cons(l,r); x)` as field arithmetic + over the children's fingerprints, with the Dyck delimiter power explicit. -/ +theorem fn_cons (l r : Noun) (x : K) : + fn (Noun.cell l r) x + = ( (fn l x).1 * (fn r x).1 + , (fn l x).2.1 + (fn l x).1 * (fn r x).2.1 + , x * (fn l x).2.2 + x ^ (2 * l.leaves - 1) + + x ^ (2 * l.leaves) * (fn r x).2.2 ) := + Prod.ext_iff.mpr ⟨fn_cons_size l r x, + Prod.ext_iff.mpr ⟨fn_cons_leaf l r x, fn_cons_dyck l r x⟩⟩ + +/-- **Inverse-free (AIR-constraint) form of the Dyck composition.** Multiplying + `fn_cons_dyck` through by `x` replaces the loose power with fingerprint components — + `x·x^{2λL−1} = x^{2λL} = fn₁(l)²` — so every term is a product of components and `x`: + + `x·fn₃(cons(l,r)) = x²·fn₃(l) + fn₁(l)² + x·fn₁(l)²·fn₃(r)`. + + Completeness identity for honest fingerprints (no `x⁻¹` in the statement). Not a + soundness theorem for committed columns; at `x = 0` both sides are 0, so the equation + constrains nothing there. -/ +theorem fn_cons_dyck_constraint (l r : Noun) (x : K) : + x * (fn (Noun.cell l r) x).2.2 + = x ^ 2 * (fn l x).2.2 + (fn l x).1 ^ 2 + x * (fn l x).1 ^ 2 * (fn r x).2.2 := by + have hlam : 1 ≤ l.leaves := l.one_le_leaves + have hsz : ((fn l x).1 : K) = x ^ l.leaves := rfl + have hpow : x * x ^ (2 * l.leaves - 1) = (x ^ l.leaves) ^ 2 := by + rw [← pow_mul, ← pow_succ'] + congr 1 + omega + rw [fn_cons_dyck, hsz, mul_add, mul_add, hpow] + ring + +/-- Division form of the Dyck composition, for witness generators that carry a committed + `x⁻¹` (`fn₃(cons(l,r)) = x·fn₃(l) + fn₁(l)²·x⁻¹ + fn₁(l)²·fn₃(r)`), valid for `x ≠ 0`. -/ +theorem fn_cons_dyck_inv (l r : Noun) {x : K} (hx : x ≠ 0) : + (fn (Noun.cell l r) x).2.2 + = x * (fn l x).2.2 + (fn l x).1 ^ 2 * x⁻¹ + (fn l x).1 ^ 2 * (fn r x).2.2 := by + have h := fn_cons_dyck_constraint l r x + field_simp at h ⊢ + exact h + +end Decomp + +/-! ### Schwartz–Zippel / root counting -/ + +section SchwartzZippel +variable {K : Type*} [Field K] [Fintype K] [DecidableEq K] + +/-- A nonzero polynomial of natDegree `d` vanishes at at most `d` points of `K`. -/ +theorem card_eval_zero_le (q : K[X]) (hq : q ≠ 0) : + (Finset.univ.filter (fun r : K => q.eval r = 0)).card ≤ q.natDegree := by + have hset : (Finset.univ.filter (fun r : K => q.eval r = 0)) = q.roots.toFinset := by + ext r + simp only [Finset.mem_filter, Finset.mem_univ, true_and, Multiset.mem_toFinset, + mem_roots hq, IsRoot.def] + rw [hset] + calc q.roots.toFinset.card ≤ Multiset.card q.roots := Multiset.toFinset_card_le _ + _ ≤ q.natDegree := card_roots' q + +end SchwartzZippel + +/-! ### The collision-soundness bound (lem:ion_univariate_sec, main.tex:1188) -/ + +section Collision +variable {K : Type*} [Field K] [Fintype K] [DecidableEq K] + +/-- **`ε_col^{Λ,𝔽}` — the single-instance collision-soundness error (main.tex:1204).** + The paper names the collision error `ε^{Λ,𝔽}_col := ε(Λ,K)` (main.tex:1204) where + `ε(Λ,K) = 2·(2Λ−1)/|K|` is the bound of `Lem:ion_univariate_sec` (main.tex:1192). + We name it here as a standalone constant. (`2·Λ−1` uses `ℕ` truncated subtraction; over + the operative range `Λ ≥ 1` — always forced by `Noun.one_le_leaves` in every use below — + it agrees with the paper's `2(2Λ−1)`; see `epsCol_eq` for the real-arithmetic form.) -/ +def epsCol (Λ : ℕ) : ℚ := ((2 * (2 * Λ - 1) : ℕ) : ℚ) / (Fintype.card K) + +omit [Field K] [DecidableEq K] in +/-- `ε_col` in the paper's real (non-truncated) arithmetic form `2(2Λ−1)/|K|`, valid for + `Λ ≥ 1` (main.tex:1204/1192). -/ +theorem epsCol_eq {Λ : ℕ} (hΛ : 1 ≤ Λ) : + epsCol (K := K) Λ = 2 * (2 * (Λ : ℚ) - 1) / (Fintype.card K) := by + unfold epsCol + have h1 : (1 : ℕ) ≤ 2 * Λ := by omega + rw [Nat.cast_mul] + congr 1 + push_cast [Nat.cast_sub h1] + ring + +/-- **lem:ion_univariate_sec (main.tex:1188), cardinality form — tight bound.** + For distinct field-nouns of length `≤ Λ`, the fingerprint `fn` collides for at most + `2Λ − 1` values of `r ∈ K`. + + "distinct field-nouns" (the paper's `n₁ ≠ n₂` in `𝒩^{λ≤Λ}(𝔽)`, main.tex:1181) is the + hypothesis `hne`: the NPR data `(dt, w) = (leafF, dyckBits)` differ. (Operationally + distinct `Noun`s that agree after casting leaves into `K` are the *same* field-noun and + genuinely have equal fingerprints, so this is exactly the right premise.) + + Proof = univariate Schwartz–Zippel: at least one of the three difference polynomials + `X^{λ₁}−X^{λ₂}`, `F_{d₁}−F_{d₂}`, `F_{w₁}−F_{w₂}` is nonzero of natDegree `≤ 2Λ−1`, and a + collision forces `r` to be its root. -/ +theorem collision_card_le_tight (Λ : ℕ) (n₁ n₂ : Noun) + (h₁ : n₁.leaves ≤ Λ) (h₂ : n₂.leaves ≤ Λ) + (hne : (NPR.leafF (K := K) n₁, NPR.dyckBits (K := K) n₁) + ≠ (NPR.leafF (K := K) n₂, NPR.dyckBits (K := K) n₂)) : + (Finset.univ.filter (fun r : K => NPR.fn n₁ r = NPR.fn n₂ r)).card ≤ 2 * Λ - 1 := by + have hΛ : 1 ≤ Λ := le_trans n₁.one_le_leaves h₁ + -- reduce to: some difference polynomial `P` is nonzero, deg ≤ 2Λ−1, and collision ⟹ root + suffices h : ∃ P : K[X], P ≠ 0 ∧ P.natDegree ≤ 2 * Λ - 1 ∧ + (Finset.univ.filter (fun r : K => NPR.fn n₁ r = NPR.fn n₂ r)) ⊆ + (Finset.univ.filter (fun r : K => P.eval r = 0)) by + obtain ⟨P, hPne, hPdeg, hPsub⟩ := h + calc (Finset.univ.filter (fun r : K => NPR.fn n₁ r = NPR.fn n₂ r)).card + ≤ (Finset.univ.filter (fun r : K => P.eval r = 0)).card := Finset.card_le_card hPsub + _ ≤ P.natDegree := card_eval_zero_le P hPne + _ ≤ 2 * Λ - 1 := hPdeg + by_cases hlam : n₁.leaves = n₂.leaves + · -- equal length λ: distinguished by the leaf poly or the dyck poly + simp only [ne_eq, Prod.mk.injEq, not_and_or] at hne + rcases hne with hd | hd + · -- leaf arrays differ (equal length λ) + refine ⟨listPoly (NPR.leafF n₁) - listPoly (NPR.leafF n₂), ?_, ?_, ?_⟩ + · exact NPR.listPoly_sub_ne_zero (by simp [hlam]) hd + · refine le_trans (NPR.listPoly_sub_natDegree_le (by simp [hlam])) ?_ + rw [NPR.leafF_length]; omega + · intro r hr + rw [Finset.mem_filter] at hr ⊢ + refine ⟨hr.1, ?_⟩ + have := congrArg (fun x => x.2.1) hr.2 + simp only [NPR.fn] at this + rw [eval_sub, sub_eq_zero]; exact this + · -- dyck words differ (equal length 2(λ−1)) + refine ⟨listPoly (NPR.dyckBits n₁) - listPoly (NPR.dyckBits n₂), ?_, ?_, ?_⟩ + · refine NPR.listPoly_sub_ne_zero ?_ hd + have e1 := NPR.dyckBits_length (K := K) n₁ + have e2 := NPR.dyckBits_length (K := K) n₂ + omega + · refine le_trans (NPR.listPoly_sub_natDegree_le ?_) ?_ + · have e1 := NPR.dyckBits_length (K := K) n₁ + have e2 := NPR.dyckBits_length (K := K) n₂ + omega + · have e1 := NPR.dyckBits_length (K := K) n₁ + omega + · intro r hr + rw [Finset.mem_filter] at hr ⊢ + refine ⟨hr.1, ?_⟩ + have := congrArg (fun x => x.2.2) hr.2 + simp only [NPR.fn] at this + rw [eval_sub, sub_eq_zero]; exact this + · -- lengths differ: distinguished by the first component X^{λ} + refine ⟨X ^ n₁.leaves - X ^ n₂.leaves, ?_, ?_, ?_⟩ + · intro h + have hcoeff := congrArg (fun q => Polynomial.coeff q n₁.leaves) h + simp only [coeff_sub, coeff_X_pow, coeff_zero, ite_eq_right hlam, sub_zero] at hcoeff + exact one_ne_zero hcoeff + · refine le_trans (natDegree_sub_le _ _) ?_ + rw [natDegree_X_pow, natDegree_X_pow, max_le_iff]; omega + · intro r hr + rw [Finset.mem_filter] at hr ⊢ + refine ⟨hr.1, ?_⟩ + have := congrArg (fun x => x.1) hr.2 + simp only [NPR.fn] at this + rw [eval_sub, eval_X_pow, eval_X_pow, sub_eq_zero]; exact this + +/-- **lem:ion_univariate_sec (main.tex:1188), cardinality form — paper's stated bound.** + The collision set has size `≤ 2(2Λ−1)` (the constant printed in the paper; the tight + bound `collision_card_le_tight` gives `2Λ−1`). -/ +theorem collision_card_le (Λ : ℕ) (n₁ n₂ : Noun) + (h₁ : n₁.leaves ≤ Λ) (h₂ : n₂.leaves ≤ Λ) + (hne : (NPR.leafF (K := K) n₁, NPR.dyckBits (K := K) n₁) + ≠ (NPR.leafF (K := K) n₂, NPR.dyckBits (K := K) n₂)) : + (Finset.univ.filter (fun r : K => NPR.fn n₁ r = NPR.fn n₂ r)).card ≤ 2 * (2 * Λ - 1) := + le_trans (collision_card_le_tight Λ n₁ n₂ h₁ h₂ hne) (by omega) + +/-- **lem:ion_univariate_sec (main.tex:1188), probability form.** With the counting-measure + convention, `Pr_{r∈K}[fn n₁ = fn n₂] ≤ 2(2Λ−1)/|K| = ε(Λ,K)`. -/ +theorem collision_prob_le (Λ : ℕ) (n₁ n₂ : Noun) + (h₁ : n₁.leaves ≤ Λ) (h₂ : n₂.leaves ≤ Λ) + (hne : (NPR.leafF (K := K) n₁, NPR.dyckBits (K := K) n₁) + ≠ (NPR.leafF (K := K) n₂, NPR.dyckBits (K := K) n₂)) : + ((Finset.univ.filter (fun r : K => NPR.fn n₁ r = NPR.fn n₂ r)).card : ℚ) + / (Fintype.card K) + ≤ ((2 * (2 * Λ - 1) : ℕ) : ℚ) / (Fintype.card K) := by + have hpos : (0 : ℚ) < Fintype.card K := by + have : Nonempty K := ⟨0⟩ + exact_mod_cast Fintype.card_pos + rw [div_le_div_iff_of_pos_right hpos] + exact_mod_cast collision_card_le Λ n₁ n₂ h₁ h₂ hne + +/-- **lem:ion_univariate_sec (main.tex:1192), probability form in terms of `ε_col`.** + Restatement of `collision_prob_le` against the named constant `epsCol` (main.tex:1204): + `Pr_{r∈K}[fn n₁ = fn n₂; n₁ ≠ n₂] ≤ ε_col^{Λ,𝔽}`. -/ +theorem collision_prob_le_epsCol (Λ : ℕ) (n₁ n₂ : Noun) + (h₁ : n₁.leaves ≤ Λ) (h₂ : n₂.leaves ≤ Λ) + (hne : (NPR.leafF (K := K) n₁, NPR.dyckBits (K := K) n₁) + ≠ (NPR.leafF (K := K) n₂, NPR.dyckBits (K := K) n₂)) : + ((Finset.univ.filter (fun r : K => NPR.fn n₁ r = NPR.fn n₂ r)).card : ℚ) + / (Fintype.card K) + ≤ epsCol (K := K) Λ := + collision_prob_le Λ n₁ n₂ h₁ h₂ hne + +end Collision + +/-! ### Union bound over a table of nouns (corr:ion_single_security, main.tex:1197) -/ + +section Table +variable {K : Type*} [Field K] [Fintype K] [DecidableEq K] + +/-- `k² − k = k(k−1)` (Nat, robust to truncated subtraction). -/ +private theorem sq_sub_self (k : ℕ) : k * k - k = k * (k - 1) := by + cases k with + | zero => rfl + | succ n => rw [Nat.succ_sub_one, Nat.mul_succ]; omega + +/-- **corr:ion_single_security (main.tex:1197), cardinality form.** For a table of `k = |s|` + nouns of length `≤ Λ`, the number of `r ∈ K` for which *some* pair of distinct nouns + collides is `≤ k(k−1)·(2Λ−1)`. (Union bound over ordered distinct index pairs, each + contributing `≤ 2Λ−1` by `collision_card_le_tight`.) -/ +theorem collision_table_card_le {ι : Type*} (Λ : ℕ) + (s : Finset ι) (f : ι → Noun) (hf : ∀ i ∈ s, (f i).leaves ≤ Λ) : + (Finset.univ.filter (fun r : K => ∃ ij ∈ s.offDiag, + NPR.fn (f ij.1) r = NPR.fn (f ij.2) r ∧ + (NPR.leafF (K := K) (f ij.1), NPR.dyckBits (K := K) (f ij.1)) + ≠ (NPR.leafF (K := K) (f ij.2), NPR.dyckBits (K := K) (f ij.2)))).card + ≤ s.card * (s.card - 1) * (2 * Λ - 1) := by + classical + set g : ι × ι → Finset K := fun ij => + Finset.univ.filter (fun r : K => NPR.fn (f ij.1) r = NPR.fn (f ij.2) r ∧ + (NPR.leafF (K := K) (f ij.1), NPR.dyckBits (K := K) (f ij.1)) + ≠ (NPR.leafF (K := K) (f ij.2), NPR.dyckBits (K := K) (f ij.2))) with hg + have hsub : (Finset.univ.filter (fun r : K => ∃ ij ∈ s.offDiag, + NPR.fn (f ij.1) r = NPR.fn (f ij.2) r ∧ + (NPR.leafF (K := K) (f ij.1), NPR.dyckBits (K := K) (f ij.1)) + ≠ (NPR.leafF (K := K) (f ij.2), NPR.dyckBits (K := K) (f ij.2)))) + ⊆ s.offDiag.biUnion g := by + intro r hr + rw [Finset.mem_filter] at hr + obtain ⟨ij, hij, hcol, hdi⟩ := hr.2 + refine Finset.mem_biUnion.mpr ⟨ij, hij, ?_⟩ + simp only [hg, Finset.mem_filter, Finset.mem_univ, true_and] + exact ⟨hcol, hdi⟩ + have hcard : ∀ ij ∈ s.offDiag, (g ij).card ≤ 2 * Λ - 1 := by + intro ij hij + obtain ⟨hi, hj, _⟩ := Finset.mem_offDiag.mp hij + by_cases hd : (NPR.leafF (K := K) (f ij.1), NPR.dyckBits (K := K) (f ij.1)) + ≠ (NPR.leafF (K := K) (f ij.2), NPR.dyckBits (K := K) (f ij.2)) + · -- distinct: bound by the single-pair collision count + refine le_trans (Finset.card_le_card ?_) + (collision_card_le_tight Λ (f ij.1) (f ij.2) (hf _ hi) (hf _ hj) hd) + intro r hr + simp only [hg, Finset.mem_filter, Finset.mem_univ, true_and] at hr ⊢ + exact hr.1 + · -- equal field-nouns: the filtered set is empty + have hempty : g ij = ∅ := by + simp only [hg, Finset.filter_eq_empty_iff] + intro r _ + rintro ⟨_, hdiff⟩ + exact hd hdiff + rw [hempty]; simp + calc (Finset.univ.filter (fun r : K => ∃ ij ∈ s.offDiag, + NPR.fn (f ij.1) r = NPR.fn (f ij.2) r ∧ + (NPR.leafF (K := K) (f ij.1), NPR.dyckBits (K := K) (f ij.1)) + ≠ (NPR.leafF (K := K) (f ij.2), NPR.dyckBits (K := K) (f ij.2)))).card + ≤ (s.offDiag.biUnion g).card := Finset.card_le_card hsub + _ ≤ ∑ _ij ∈ s.offDiag, (2 * Λ - 1) := + le_trans Finset.card_biUnion_le (Finset.sum_le_sum hcard) + _ = s.offDiag.card * (2 * Λ - 1) := by rw [Finset.sum_const, smul_eq_mul] + _ = s.card * (s.card - 1) * (2 * Λ - 1) := by + rw [Finset.offDiag_card, sq_sub_self] + +/-- Casting helper: `↑(k(k−1)) = k(k−1)` bridging `ℕ` truncated subtraction into `ℚ`. -/ +private theorem natCast_mul_pred (k : ℕ) : ((k * (k - 1) : ℕ) : ℚ) = (k : ℚ) * ((k : ℚ) - 1) := by + rcases k with _ | k + · simp + · push_cast; ring + +/-- **corr:ion_single_security (main.tex:1197), probability form — union bound.** With + `ε := ε_col = 2(2Λ−1)/|K|`, the probability that some pair among `k = |s|` nouns collides + is `≤ ε · k(k−1)/2`. This is the paper's second (union-bound) inequality, main.tex:1200. + + ⚠ **The paper's FIRST (tight) inequality `Pr ≤ 1 − (1−ε)^{k(k−1)/2}` is NOT proven here, + and is not soundly provable in this model.** The paper's proof (main.tex:2361) obtains it + by asserting "the probability of no table collisions is `(1−p)^{k(k−1)/2}`", i.e. by + treating the `k(k−1)/2` pairwise collision events as *independent*. In our (and the + paper's real) probability model those events all share the SAME random `r ∈ K`, so they + are NOT independent, and a product/independence bound is unavailable: the only estimate the + root-counting machinery yields is the ADDITIVE union bound (`card_biUnion_le`), giving the + `≤ ε·k(k−1)/2` below. The product form is moreover genuinely FALSE in the worst case + admitted by our per-pair root-count bounds: for `m := k(k−1)/2` pairwise collision sets + that are pairwise disjoint (each of the maximal `2Λ−1` roots) and together partition `K`, + the collision probability is `1`, which exceeds `1−(1−ε)^m < 1`. Consistently, our proven + union bound `ε·m` is `≥` the tight `1−(1−ε)^m` (Bernoulli, `one_sub_one_sub_pow_le`; equality + only in the degenerate `ε = 0` / `m ≤ 1` cases, strict for `m ≥ 2, ε > 0`), so the tight + expression is a stronger claim that this model cannot license. The union bound below is the + rigorous, model-faithful claim; the paper describes a "non-optimal but easier to show" bound + (main.tex:2361) which is what we prove here. -/ +theorem collision_table_prob_le {ι : Type*} (Λ : ℕ) + (s : Finset ι) (f : ι → Noun) (hf : ∀ i ∈ s, (f i).leaves ≤ Λ) : + ((Finset.univ.filter (fun r : K => ∃ ij ∈ s.offDiag, + NPR.fn (f ij.1) r = NPR.fn (f ij.2) r ∧ + (NPR.leafF (K := K) (f ij.1), NPR.dyckBits (K := K) (f ij.1)) + ≠ (NPR.leafF (K := K) (f ij.2), NPR.dyckBits (K := K) (f ij.2)))).card : ℚ) + / (Fintype.card K) + ≤ (((2 * (2 * Λ - 1) : ℕ) : ℚ) / (Fintype.card K)) + * ((s.card : ℚ) * ((s.card : ℚ) - 1) / 2) := by + have hpos : (0 : ℚ) < Fintype.card K := by + have : Nonempty K := ⟨0⟩ + exact_mod_cast Fintype.card_pos + have hcard := collision_table_card_le (K := K) Λ s f hf + have hne0 : (Fintype.card K : ℚ) ≠ 0 := ne_of_gt hpos + rw [div_le_iff₀ hpos] + -- the two `|K|`s cancel; both sides reduce to `k(k−1)·(2Λ−1)` + have hrhs : (((2 * (2 * Λ - 1) : ℕ) : ℚ) / (Fintype.card K)) + * ((s.card : ℚ) * ((s.card : ℚ) - 1) / 2) * (Fintype.card K) + = (s.card : ℚ) * ((s.card : ℚ) - 1) * ((2 * Λ - 1 : ℕ) : ℚ) := by + field_simp + push_cast + ring + rw [hrhs] + calc ((_ : ℕ) : ℚ) ≤ ((s.card * (s.card - 1) * (2 * Λ - 1) : ℕ) : ℚ) := by exact_mod_cast hcard + _ = (s.card : ℚ) * ((s.card : ℚ) - 1) * ((2 * Λ - 1 : ℕ) : ℚ) := by + rw [Nat.cast_mul, natCast_mul_pred] + +/-- **corr:ion_single_security (main.tex:1197), union bound in terms of `ε_col`.** + Restatement of `collision_table_prob_le` against the named constant `epsCol` + (main.tex:1204): `Pr[∃ colliding pair] ≤ ε_col^{Λ,𝔽} · k(k−1)/2`. -/ +theorem collision_table_prob_le_epsCol {ι : Type*} (Λ : ℕ) + (s : Finset ι) (f : ι → Noun) (hf : ∀ i ∈ s, (f i).leaves ≤ Λ) : + ((Finset.univ.filter (fun r : K => ∃ ij ∈ s.offDiag, + NPR.fn (f ij.1) r = NPR.fn (f ij.2) r ∧ + (NPR.leafF (K := K) (f ij.1), NPR.dyckBits (K := K) (f ij.1)) + ≠ (NPR.leafF (K := K) (f ij.2), NPR.dyckBits (K := K) (f ij.2)))).card : ℚ) + / (Fintype.card K) + ≤ epsCol (K := K) Λ * ((s.card : ℚ) * ((s.card : ℚ) - 1) / 2) := + collision_table_prob_le Λ s f hf + +/-- **Bernoulli's inequality (the paper's `1 − (1−ε)^m < ε·m` step, main.tex:1200/2361).** + For any `ε ≤ 2` and `m : ℕ`, `1 − (1−ε)^m ≤ m·ε`. This is the pure real inequality + relating the paper's two collision-error expressions: the tight + `1 − (1−ε)^{k(k−1)/2}` and the union bound `ε·k(k−1)/2` (`m = k(k−1)/2`). + + NOTE: this lemma establishes only the *arithmetic* relation `tight ≤ union`. It does + NOT (and cannot, in this shared-randomness model) establish that the collision + probability is bounded by the tight expression — see the ⚠ note on + `collision_table_prob_le`. It is exactly what shows the union bound we DO prove is the + weaker (larger) of the paper's two stated bounds, faithfully reproducing the paper's + `<` chain. -/ +theorem one_sub_one_sub_pow_le (ε : ℚ) (hε : ε ≤ 2) (m : ℕ) : + 1 - (1 - ε) ^ m ≤ (m : ℚ) * ε := by + have hb := one_add_mul_le_pow (a := -ε) (by linarith) m + have hmul : (m : ℚ) * (-ε) = -((m : ℚ) * ε) := by ring + have h1 : (1 : ℚ) + -ε = 1 - ε := by ring + rw [hmul, h1] at hb + linarith + +end Table + +end NPR +end Nock diff --git a/LeanPool/PureNock/NounValidation.lean b/LeanPool/PureNock/NounValidation.lean new file mode 100644 index 0000000000..b6f9eb4428 --- /dev/null +++ b/LeanPool/PureNock/NounValidation.lean @@ -0,0 +1,257 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Tree +public import Mathlib.Data.Multiset.Basic +public import Mathlib.Data.Multiset.AddSub +public import Mathlib.Data.Multiset.MapFold +public import Mathlib.Algebra.BigOperators.Group.Multiset.Basic +public import Mathlib.Algebra.Ring.Nat + +/-! +# Structural noun validation + +`Lem:decomp`, `Algorithm:Valid_Noun`, and `Thm:Noun_Validation` +(`main.tex:1206–1270`) over Nat-labelled nouns. +-/ + +@[expose] public section + +namespace Nock + +/-! ### `Lem:decomp` (`main.tex:1210–1213`) -/ + +namespace Noun + +-- main.tex:1210–1213 +/-- **`Lem:decomp`** (`main.tex:1210–1213`). + ∃! λ_L, λ_R ∈ ℕ₊ s.t. λ = λ_L + λ_R, n_L ∈ N^{λ_L}(F), n_R ∈ N^{λ_R}(F) + ⇔ n = cons(n_L,n_R), n ∈ N^λ(F). + (Lean ∃ also includes `n = cons(n_L,n_R)` per CORRESPONDENCE note 3.) -/ +theorem decomp (n nL nR : Noun) (lam : ℕ) : + (∃! p : ℕ × ℕ, 0 < p.1 ∧ 0 < p.2 ∧ lam = p.1 + p.2 ∧ + nL.leaves = p.1 ∧ nR.leaves = p.2 ∧ n = Noun.cell nL nR) + ↔ (n = Noun.cell nL nR ∧ n.leaves = lam) := by + constructor + · rintro ⟨p, ⟨_, _, hsum, hL, hR, hcons⟩, _⟩ + refine ⟨hcons, ?_⟩ + subst hcons + simp only [Noun.leaves] + omega + · rintro ⟨hcons, hlen⟩ + refine ⟨(nL.leaves, nR.leaves), + ⟨nL.one_le_leaves, nR.one_le_leaves, ?_, rfl, rfl, hcons⟩, ?_⟩ + · subst hcons; simp only [Noun.leaves] at hlen; omega + · rintro q ⟨_, _, _, hqL, hqR, _⟩ + exact Prod.ext_iff.mpr ⟨hqL.symm, hqR.symm⟩ + +/-- The structural core of `Lem:decomp`: the children of a `cons` are unique — `cons` is + injective, so the decomposition `n = cons(nL,nR)` is well-defined (main.tex:1208). -/ +theorem cons_children_unique {l r l' r' : Noun} (h : Noun.cell l r = Noun.cell l' r') : + l = l' ∧ r = r' := by + injection h with h₁ h₂; exact ⟨h₁, h₂⟩ + +end Noun + +/-! ### `Algorithm:Valid_Noun` (`main.tex:1244–1260`) -/ + +/-- A transcript entry `(n, n_L?/n_R?)` — the paper's `t_i = (n, n_L, n_R)`. -/ +abbrev Transcript := List (Noun × Option (Noun × Noun)) + +/-- **`Algorithm:Valid_Noun`** (`main.tex:1244–1260`), one step of `CheckValidNoun`: + parse `(n,n_L,n_R) ← t_i`; if `n ∉ mset` Reject; erase `n`; if atom require + `n_L,n_R = ⊥` else require `n = cons(n_L,n_R)` and push `{n_L,n_R}`. `none` = Reject. -/ +def stepMset (m : Multiset Noun) (n : Noun) (ch : Option (Noun × Noun)) : + Option (Multiset Noun) := + match n, ch with + | Noun.atom k, none => if Noun.atom k ∈ m then some (m.erase (Noun.atom k)) else none + | Noun.cell a b, some (a', b') => + if Noun.cell a b ∈ m ∧ a = a' ∧ b = b' then + some (a ::ₘ b ::ₘ m.erase (Noun.cell a b)) else none + | _, _ => none + +/-- Run the whole transcript, threading the multiset; `none` on any `Reject`. -/ +def runMset : Multiset Noun → Transcript → Option (Multiset Noun) + | m, [] => some m + | m, (n, ch) :: ts => + match stepMset m n ch with + | some m' => runMset m' ts + | none => none + +theorem runMset_cons {m m' : Multiset Noun} {n : Noun} {ch : Option (Noun × Noun)} + {ts : Transcript} (h : stepMset m n ch = some m') : + runMset m ((n, ch) :: ts) = runMset m' ts := by + simp only [runMset, h] + +/-- Accept / Reject, the output of `CheckValidNoun`. -/ +inductive Result where + | accept : Result + | reject : Result +deriving DecidableEq, Repr + +/-- **`Algorithm:Valid_Noun`** (`main.tex:1244–1260`). + `CheckValidNoun(n₀;T)`: init `mset = {n₀}`; while `mset ≠ ∅` process `t_i` as in + `stepMset`; if `mset = ∅` return Accept else Reject. -/ +def checkValidNoun (n₀ : Noun) (T : Transcript) : Result := + match runMset {n₀} T with + | some m => if m = 0 then Result.accept else Result.reject + | none => Result.reject + +theorem checkValidNoun_accept_iff (n₀ : Noun) (T : Transcript) : + checkValidNoun n₀ T = Result.accept ↔ runMset {n₀} T = some 0 := by + unfold checkValidNoun + cases hr : runMset {n₀} T with + | none => simp + | some m => + by_cases hz : m = 0 + · subst hz; simp + · simp [hz] + +/-! ### Total node count of the multiset, and its decrease under each step -/ + +/-- Total node count across a multiset of nouns (`Σ size`). -/ +def msize (m : Multiset Noun) : ℕ := (m.map Noun.size).sum + +@[simp] theorem msize_zero : msize (0 : Multiset Noun) = 0 := by simp [msize] + +@[simp] theorem msize_cons (a : Noun) (m : Multiset Noun) : + msize (a ::ₘ m) = a.size + msize m := by + simp [msize] + +theorem msize_singleton (a : Noun) : msize ({a} : Multiset Noun) = a.size := by + simp [msize] + +theorem msize_erase {n : Noun} {m : Multiset Noun} (h : n ∈ m) : + msize m = n.size + msize (m.erase n) := by + conv_lhs => rw [← Multiset.cons_erase h] + rw [msize_cons] + +/-- Each successful step of the walk removes exactly one node from the total (`Lem:pbt_size` + bookkeeping): a leaf pops `1` node; a cell pops `1 + |l| + |r|` and pushes `|l| + |r|`. -/ +theorem step_msize {m m' : Multiset Noun} {n : Noun} {ch : Option (Noun × Noun)} + (h : stepMset m n ch = some m') : msize m = msize m' + 1 := by + cases n with + | atom k => + cases ch with + | none => + simp only [stepMset] at h + split at h + · rename_i hmem + simp only [Option.some.injEq] at h + subst h + rw [msize_erase hmem]; simp only [Noun.size]; omega + · simp at h + | some c => simp only [stepMset] at h; simp at h + | cell a b => + cases ch with + | none => simp only [stepMset] at h; simp at h + | some c => + obtain ⟨a', b'⟩ := c + simp only [stepMset] at h + split at h + · rename_i hcond + obtain ⟨hmem, -, -⟩ := hcond + simp only [Option.some.injEq] at h + subst h + rw [msize_erase hmem] + simp only [msize_cons, Noun.size] + omega + · simp at h + +/-- An accepting run consumes a transcript of length exactly `msize m` (the total node count + of the initial multiset). This is the necessity half of `Thm:Noun_Validation`. -/ +theorem run_length : ∀ {m : Multiset Noun} {T : Transcript}, + runMset m T = some 0 → T.length = msize m := by + intro m T + induction T generalizing m with + | nil => + intro h + simp only [runMset, Option.some.injEq] at h + subst h; simp + | cons t ts ih => + intro h + obtain ⟨n, ch⟩ := t + simp only [runMset] at h + cases hstep : stepMset m n ch with + | none => rw [hstep] at h; simp at h + | some m' => + rw [hstep] at h + have hlen := ih h + have hms := step_msize hstep + simp only [List.length_cons] + omega + +/-! ### The canonical accepting transcript (a preorder DFS walk) -/ + +/-- The canonical validity transcript of a noun: a preorder DFS walk. A cell is decomposed + (checking `cons`) before its subtrees are validated; an atom is a single no-children step. -/ +def transcript : Noun → Transcript + | Noun.atom k => [(Noun.atom k, none)] + | Noun.cell l r => (Noun.cell l r, some (l, r)) :: (transcript l ++ transcript r) + +@[simp] theorem transcript_length (n : Noun) : (transcript n).length = n.size := by + induction n with + | atom k => rfl + | cell l r ihl ihr => + simp only [transcript, List.length_cons, List.length_append, ihl, ihr, Noun.size] + omega + +/-- Processing `transcript n` off the front of a multiset containing `n` validates `n` and + removes exactly `n`, leaving the rest of the walk untouched. (Order-independence of the + walk, main.tex:1266: we use preorder.) -/ +theorem run_transcript : ∀ (n : Noun) (m : Multiset Noun) (rest : Transcript), + runMset (n ::ₘ m) (transcript n ++ rest) = runMset m rest := by + intro n + induction n with + | atom k => + intro m rest + rw [show transcript (Noun.atom k) ++ rest = (Noun.atom k, none) :: rest from rfl] + have hstep : stepMset (Noun.atom k ::ₘ m) (Noun.atom k) none = some m := by + simp [stepMset, Multiset.erase_cons_head] + rw [runMset_cons hstep] + | cell l r ihl ihr => + intro m rest + rw [show transcript (Noun.cell l r) ++ rest + = (Noun.cell l r, some (l, r)) :: (transcript l ++ transcript r ++ rest) from rfl] + have hstep : stepMset (Noun.cell l r ::ₘ m) (Noun.cell l r) (some (l, r)) + = some (l ::ₘ r ::ₘ m) := by + simp [stepMset, Multiset.erase_cons_head] + rw [runMset_cons hstep, List.append_assoc, + ihl (r ::ₘ m) (transcript r ++ rest), ihr m rest] + +/-- The canonical transcript accepts. -/ +theorem run_transcript_accept (n : Noun) : runMset {n} (transcript n) = some 0 := by + have h := run_transcript n 0 [] + rw [List.append_nil] at h + rw [show ({n} : Multiset Noun) = n ::ₘ 0 from rfl, h] + rfl + +/-! ### `Thm:Noun_Validation` (`main.tex:1268–1269`) -/ + +-- main.tex:1268–1269 +/-- **`Thm:Noun_Validation`** (`main.tex:1268–1269`). + n ∈ N^λ(F) ⇔ ∃ T. CheckValidNoun(n;T)=Accept ∧ |T|=2λ−1 -/ +theorem Noun_Validation (n : Noun) (lam : ℕ) : + n.leaves = lam ↔ + ∃ T : Transcript, checkValidNoun n T = Result.accept ∧ T.length = 2 * lam - 1 := by + constructor + · intro hlen + refine ⟨transcript n, ?_, ?_⟩ + · rw [checkValidNoun_accept_iff]; exact run_transcript_accept n + · rw [transcript_length] + have := n.size_add_one + omega + · rintro ⟨T, hacc, hlenT⟩ + rw [checkValidNoun_accept_iff] at hacc + have hlenrun := run_length hacc + rw [msize_singleton] at hlenrun + have hsz := n.size_add_one + have hpos := n.one_le_leaves + omega + +end Nock diff --git a/LeanPool/PureNock/PaperFieldBridge.lean b/LeanPool/PureNock/PaperFieldBridge.lean new file mode 100644 index 0000000000..965a60e4da --- /dev/null +++ b/LeanPool/PureNock/PaperFieldBridge.lean @@ -0,0 +1,362 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.PaperReferenceMod +public import LeanPool.PureNock.PureComplete + +/-! +# Paper-fragment ↔ finite-field bridge + +`FieldEmbedding.agreeBy` (`Nock/ModularEval.lean`) identifies the *full* evaluators — `evalN` +(unbounded Nat increment) and `evalFBy q` (modular increment, `main.tex:1531–1534`) — conditional +on a successful run of the *guarded* evaluator `evalGBy q`, which crashes rather than letting any +increment reach `q`. But all three of those evaluators include OP₁₁, while + +* the small-step machine `Verb.runProgram` and its unconditional adequacy partner `evalPaper` + (`Verb.runProgram_adequate_complete`), and +* the strict modular paper oracle `evalPaperMod q` (`Nock/PaperReferenceMod.lean`) + +live in the paper's OP₀–OP₁₀ fragment (`main.tex:1054–1076`). + +This module supplies the guarded paper-fragment evaluator `evalGPaper q` (`evalPaperMod q` with the +crashing increment `lusGBy q`) and threads one conditional through every layer, ending in +`paper_field_bridge`: on `q`-bounded inputs, a successful guarded fragment run makes the small-step +machine, `evalPaper`, `evalPaperMod q`, `evalFBy q`, and `evalN` all return the *same* noun, itself +`q`-bounded. + +The conditional is no-overflow; injectivity is the payoff, not a second hypothesis: the `BoundedBy +q` +conclusion makes the leaf cast `ℕ → ZMod q` injective (`embBy_inj_of_boundedBy`), so the structural +noun equality used by OP₅ (`main.tex:1525`) *is* field equality on bridged results. Primality is +never used — `1 < q` suffices — so nothing here declares or pulls a project axiom. + +The conditional is STRICTLY STRONGER than the paper's own total modular language: `evalGPaper q` +crashes where the paper's modular OP₄ merely wraps, so this identifies the five evaluators on the +*no-overflow sublanguage* only (`evalGPaper_fails_where_evalPaperMod_wraps`). +-/ + +@[expose] public section + +namespace Nock + +open Noun + +/-! ## The guarded paper-fragment evaluator -/ + +-- main.tex:1054-1076 (fragment OP₀..OP₁₀ + `cons`), main.tex:1531-1534 (the OP₄ increment) +/-- `*[subject formula]`, fuel-indexed, **guarded paper fragment**: the paper's OP₀–OP₁₀ fragment + with the OP₄ increment `lusGBy q`, which *crashes* rather than reaching an atom `≥ q`. + + `evalGPaper q fuel s f = some r` is therefore exactly the statement "this paper-fragment + computation runs without any **OP₄** modular wrap at `q`" — the honest no-overflow side + condition, in the fragment where `Verb.runProgram` and `evalPaperMod q` live. (OP₄ is the only + opcode these evaluators reduce with a modular increment; see the OP₆ note below.) + + OP₆ NOTE. These modular evaluators reduce OP₆ by the standard Nock loobean if/else on the + condition (`main.tex:1067` computes the same thing over ℕ via an increment macro). Over ℕ they + coincide (`runProgram_adequate_complete`), and under the bridge's `BoundedBy q` hypotheses for + `q ≥ 4` every intermediate atom is `< q` so no OP₆ increment could wrap either; hence the guard + is complete on the bridge's domain. The paper's *literal* OP₆ macro, whose two increments are + modular, diverges from the if/else only for an unbounded (`≥ q`) or small-modulus (`q ∈ {2,3}`) + condition — inputs outside the bounded domain — so `paper_field_bridge` is unaffected at + `q ≥ 4`. This is a representation choice (if/else vs increment-macro), documented, not an + OP₄-style wrap. -/ +def evalGPaper (q : Nat) : Nat → Noun → Noun → Option Noun + | 0, _, _ => none + | _ + 1, _, .atom _ => none + | fuel + 1, subj, .cell (.cell b c) d => + match evalGPaper q fuel subj (.cell b c), evalGPaper q fuel subj d with + | some l, some r => some (.cell l r) + | _, _ => none + | fuel + 1, subj, .cell (.atom op) tail => + match op with + | 0 => match tail with + | .atom ax => slot ax subj + | _ => none + | 1 => some tail + | 2 => match tail with + | .cell b c => + match evalGPaper q fuel subj b, evalGPaper q fuel subj c with + | some sb, some sc => evalGPaper q fuel sb sc + | _, _ => none + | _ => none + | 3 => (evalGPaper q fuel subj tail).map wut + | 4 => (evalGPaper q fuel subj tail).bind (FieldEmbedding.lusGBy q) + | 5 => match tail with + | .cell b c => + match evalGPaper q fuel subj b, evalGPaper q fuel subj c with + | some sb, some sc => some (tis sb sc) + | _, _ => none + | _ => none + | 6 => match tail with + | .cell b (.cell c d) => + match evalGPaper q fuel subj b with + | some (.atom 0) => evalGPaper q fuel subj c + | some (.atom 1) => evalGPaper q fuel subj d + | _ => none + | _ => none + | 7 => match tail with + | .cell b c => + match evalGPaper q fuel subj b with + | some sb => evalGPaper q fuel sb c + | none => none + | _ => none + | 8 => match tail with + | .cell b c => + match evalGPaper q fuel subj b with + | some sb => evalGPaper q fuel (.cell sb subj) c + | none => none + | _ => none + | 9 => match tail with + | .cell (.atom ax) c => + match evalGPaper q fuel subj c with + | some core => + match slot ax core with + | some arm => evalGPaper q fuel core arm + | none => none + | none => none + | _ => none + | 10 => match tail with + | .cell (.cell (.atom ax) c) d => + match evalGPaper q fuel subj c, evalGPaper q fuel subj d with + | some new, some old => edit ax new old + | _, _ => none + | _ => none + | _ => none -- op ≥ 11 (incl. OP₁₁ hints) ⇒ crash (paper fragment) + +/-! ## The increment agrees under the guard + +The guard is the only place the three fragment evaluators can differ: a successful `lusGBy q` +pins `n + 1 < q`, and there the unbounded increment `Noun.lus` and the modular increment +`lusFBy q` produce the same atom. -/ + +/-- A successful guarded increment is the unbounded increment (`main.tex:1531–1534` vs `Noun.lus`). +-/ +theorem lusGBy_le_lus {q : Nat} {x m : Noun} + (h : FieldEmbedding.lusGBy q x = some m) : Noun.lus x = some m := by + cases x with + | atom n => + by_cases hlt : n + 1 < q + · simp only [FieldEmbedding.lusGBy, ite_eq_left hlt, Option.some.injEq] at h + simp [Noun.lus, ← h] + · simp [FieldEmbedding.lusGBy, hlt] at h + | cell _ _ => simp [FieldEmbedding.lusGBy] at h + +/-- A successful guarded increment is the paper's modular increment (`main.tex:1531–1534`). -/ +theorem lusGBy_le_lusFBy {q : Nat} {x m : Noun} + (h : FieldEmbedding.lusGBy q x = some m) : FieldEmbedding.lusFBy q x = some m := by + cases x with + | atom n => + by_cases hlt : n + 1 < q + · simp only [FieldEmbedding.lusGBy, ite_eq_left hlt, Option.some.injEq] at h + simp [FieldEmbedding.lusFBy, ← h, Nat.mod_eq_of_lt hlt] + · simp [FieldEmbedding.lusGBy, hlt] at h + | cell _ _ => simp [FieldEmbedding.lusGBy] at h + +/-! ## The three refinements out of the guarded fragment -/ + +/-- **`evalGPaper q` refines `evalGBy q`.** The guarded fragment run is a guarded full run with + the same value; they differ only where `evalGBy` fires OP₁₁, which the fragment crashes on. + This is what makes `FieldEmbedding.agreeBy` reusable inside the fragment. -/ +theorem evalGPaper_le_evalGBy {q n : Nat} {s form r : Noun} + (h : evalGPaper q n s form = some r) : FieldEmbedding.evalGBy q n s form = some r := by + induction n, s, form using evalGPaper.induct q generalizing r <;> + simp_all only [evalGPaper, FieldEmbedding.evalGBy, Option.map_eq_some_iff, + Option.bind_eq_some_iff, Option.some.injEq, reduceCtorEq] <;> + grind + +/-- **`evalGPaper q` refines `evalPaper`.** Under the guard the fragment's modular-range OP₄ and + the unbounded Nat OP₄ coincide, so a successful guarded run is a `evalPaper` run with the same + value — the entry point into the unconditional `runProgram ↔ evalPaper` adequacy. -/ +theorem evalGPaper_le_evalPaper {q n : Nat} {s form r : Noun} + (h : evalGPaper q n s form = some r) : evalPaper n s form = some r := by + induction n, s, form using evalGPaper.induct q generalizing r <;> + simp_all only [evalGPaper, evalPaper, Option.map_eq_some_iff, + Option.bind_eq_some_iff, Option.some.injEq, reduceCtorEq] <;> + grind [lusGBy_le_lus] + +/-- **`evalGPaper q` refines `evalPaperMod q`.** A successful guarded run is a strict-paper + modular run with the same value: the guard rules out the wrap that is the only difference. -/ +theorem evalGPaper_le_evalPaperMod {q n : Nat} {s form r : Noun} + (h : evalGPaper q n s form = some r) : evalPaperMod q n s form = some r := by + induction n, s, form using evalGPaper.induct q generalizing r <;> + simp_all only [evalGPaper, evalPaperMod, Option.map_eq_some_iff, + Option.bind_eq_some_iff, Option.some.injEq, reduceCtorEq] <;> + grind [lusGBy_le_lusFBy] + +/-! ## The injectivity carry + +`BoundedBy q` is precisely the condition under which the leaf cast `ℕ → ZMod q` is injective, so on +bridged results the *structural* noun equality used by OP₅ (`main.tex:1525`) coincides with equality +of field images. Injectivity is thus a consequence of the bridge's conclusion, not an extra +hypothesis. These do not use any project axiom. -/ + +namespace FieldEmbedding + +-- main.tex:345-348 +/-- Field nouns at a supplied modulus: proper binary trees with leaves in `ZMod q`. -/ +inductive FNounBy (q : Nat) where + | atom : ZMod q → FNounBy q + | cell : FNounBy q → FNounBy q → FNounBy q + +-- main.tex:345 +/-- The leaf map `n ↦ (n : ZMod q)`, structural on cells. -/ +def embBy (q : Nat) : Noun → FNounBy q + | .atom n => .atom (n : ZMod q) + | .cell l r => .cell (embBy q l) (embBy q r) + +@[simp] theorem embBy_atom {q n : Nat} : embBy q (.atom n) = .atom (n : ZMod q) := rfl + +/-- **`embBy q` is injective on `q`-bounded nouns.** Distinct canonical residues `< q` stay + distinct in `ZMod q`, so structural Nock equality is field equality there (`main.tex:1525`). -/ +theorem embBy_inj_of_boundedBy {q : Nat} : + ∀ {a b : Noun}, BoundedBy q a → BoundedBy q b → embBy q a = embBy q b → a = b := by + intro a + induction a with + | atom m => + intro b hm hb h + cases b with + | atom n => + simp only [embBy, FNounBy.atom.injEq] at h + rw [zmod_natCast_inj_of_lt hm hb h] + | cell _ _ => simp [embBy] at h + | cell la ra ihl ihr => + intro b ha hb h + cases b with + | atom _ => simp [embBy] at h + | cell lb rb => + simp only [embBy, FNounBy.cell.injEq] at h + rw [ihl ha.1 hb.1 h.1, ihr ha.2 hb.2 h.2] + +end FieldEmbedding + +/-! ## The capstone bridge -/ + +/-- **Paper ↔ finite-field bridge.** On `q`-bounded inputs (every atom is a canonical residue + `< q`), a successful *guarded* paper-fragment run makes all five formalizations of the paper's + evaluation agree on one noun, which is again `q`-bounded: + + * the small-step machine `Verb.runProgram` (`main.tex:1052–1083`), + * the Nat paper oracle `evalPaper`, + * the strict modular paper oracle `evalPaperMod q` (`main.tex:1531–1534`), + * the full modular evaluator `FieldEmbedding.evalFBy q`, and + * the Nat reference `evalN`. + + The single hypothesis `evalGPaper q fuel s f = some r` is no-overflow: it is discharged by + *running* the guarded evaluator, not assumed. The `BoundedBy q r` conclusion then delivers + leaf-cast injectivity for free (`bridge_reflects_field_equality`). -/ +theorem paper_field_bridge (q : Nat) (hq : 1 < q) {fuel : Nat} {s f r : Noun} + (hs : FieldEmbedding.BoundedBy q s) (hf : FieldEmbedding.BoundedBy q f) + (hG : evalGPaper q fuel s f = some r) : + (∃ n, Verb.runProgram n s f = some r) ∧ + evalPaper fuel s f = some r ∧ + evalPaperMod q fuel s f = some r ∧ + FieldEmbedding.evalFBy q fuel s f = some r ∧ + evalN fuel s f = some r ∧ + FieldEmbedding.BoundedBy q r := by + have hP : evalPaper fuel s f = some r := evalGPaper_le_evalPaper hG + have hM : evalPaperMod q fuel s f = some r := evalGPaper_le_evalPaperMod hG + have hB : FieldEmbedding.BoundedBy q r := + (FieldEmbedding.agreeBy q hq fuel s f r hs hf (evalGPaper_le_evalGBy hG)).2.2 + exact ⟨(Verb.runProgram_adequate_complete s f r).2 ⟨fuel, hP⟩, hP, hM, + evalPaperMod_le_evalFBy hM, evalPaper_le_evalN hP, hB⟩ + +/-- The bridge's small-step half in the form the machine consumes. -/ +theorem runProgram_of_evalGPaper {q fuel : Nat} {s f r : Noun} + (hG : evalGPaper q fuel s f = some r) : ∃ n, Verb.runProgram n s f = some r := + (Verb.runProgram_adequate_complete s f r).2 ⟨fuel, evalGPaper_le_evalPaper hG⟩ + +/-- The bridge's field half: a successful guarded fragment run is a full modular run. -/ +theorem evalFBy_of_evalGPaper {q fuel : Nat} {s f r : Noun} + (hG : evalGPaper q fuel s f = some r) : + FieldEmbedding.evalFBy q fuel s f = some r := + evalPaperMod_le_evalFBy (evalGPaper_le_evalPaperMod hG) + +/-- **The bridge carries the equivalence through the field embedding.** Two guarded fragment runs + on `q`-bounded inputs whose results have the same field image have the *same result noun*: the + no-overflow conditional yields boundedness, and boundedness yields injectivity. -/ +theorem bridge_reflects_field_equality (q : Nat) (hq : 1 < q) + {fuel₁ fuel₂ : Nat} {s₁ f₁ s₂ f₂ r₁ r₂ : Noun} + (hs₁ : FieldEmbedding.BoundedBy q s₁) (hf₁ : FieldEmbedding.BoundedBy q f₁) + (hs₂ : FieldEmbedding.BoundedBy q s₂) (hf₂ : FieldEmbedding.BoundedBy q f₂) + (hG₁ : evalGPaper q fuel₁ s₁ f₁ = some r₁) + (hG₂ : evalGPaper q fuel₂ s₂ f₂ = some r₂) + (himg : FieldEmbedding.embBy q r₁ = FieldEmbedding.embBy q r₂) : r₁ = r₂ := + FieldEmbedding.embBy_inj_of_boundedBy + (paper_field_bridge q hq hs₁ hf₁ hG₁).2.2.2.2.2 + (paper_field_bridge q hq hs₂ hf₂ hG₂).2.2.2.2.2 himg + +/-! ## Non-vacuity, and the guard cuts exactly at the wrap + +The witnesses instantiate the full six-fold bridge conclusion at a genuine prime and at the paper's +own characteristic (`main.tex:158`), given as a *literal* modulus so no primality axiom is needed +(the bridge never uses primality). -/ + +/-- The Goldilocks modulus `p = 2^64 − 2^32 + 1` (`main.tex:158`), as a literal `Nat` constant. + Used only to instantiate a bridge witness at the paper's own characteristic; primality is not + assumed anywhere. -/ +def goldilocksModulus : Nat := 2 ^ 64 - 2 ^ 32 + 1 + +theorem one_lt_goldilocksModulus : 1 < goldilocksModulus := by + norm_num [goldilocksModulus] + +/-- **Non-vacuity at a genuine prime.** `*[0 [4 [1 3]]] = 4` at `q = 5`, where `ZMod q` really is a + field, with the full six-fold bridge conclusion. -/ +theorem bridge_nonvacuous_at_prime_5 : + (∃ n, Verb.runProgram n (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 3))) + = some (.atom 4)) ∧ + evalPaper 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 3))) = some (.atom 4) ∧ + evalPaperMod 5 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 3))) + = some (.atom 4) ∧ + FieldEmbedding.evalFBy 5 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 3))) + = some (.atom 4) ∧ + evalN 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 3))) = some (.atom 4) ∧ + FieldEmbedding.BoundedBy 5 (.atom 4) := + paper_field_bridge 5 (by norm_num) (by norm_num) (by norm_num) (by decide) + +-- main.tex:158 +/-- **Non-vacuity at the paper's own characteristic** `goldilocksModulus = 2^64 − 2^32 + 1`. + Uses the literal modulus; no primality axiom. -/ +theorem bridge_nonvacuous_at_goldilocks : + (∃ n, Verb.runProgram n (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 42))) + = some (.atom 43)) ∧ + evalPaper 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 42))) = some (.atom 43) ∧ + evalPaperMod goldilocksModulus 5 (.atom 0) + (.cell (.atom 4) (.cell (.atom 1) (.atom 42))) = some (.atom 43) ∧ + FieldEmbedding.evalFBy goldilocksModulus 5 (.atom 0) + (.cell (.atom 4) (.cell (.atom 1) (.atom 42))) = some (.atom 43) ∧ + evalN 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 42))) = some (.atom 43) ∧ + FieldEmbedding.BoundedBy goldilocksModulus (.atom 43) := + paper_field_bridge goldilocksModulus one_lt_goldilocksModulus + (by norm_num [goldilocksModulus]) (by norm_num [goldilocksModulus]) + (by have hgm : goldilocksModulus = 18446744069414584321 := by norm_num [goldilocksModulus] + rw [hgm]; decide) + +/-- **The guard rejects exactly the divergent computation.** At the top residue `q−1 = 4` the + strict modular oracle wraps to `0` while the unbounded oracle produces `5` + (`evalPaperMod_ne_evalPaper_at_top`); the guarded fragment evaluator crashes there, so the + bridge never claims agreement across the wrap. -/ +theorem evalGPaper_fails_where_evalPaperMod_wraps : + evalGPaper 5 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 4))) = none ∧ + evalPaperMod 5 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 4))) + = some (.atom 0) ∧ + evalPaper 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 4))) + = some (.atom 5) := by + refine ⟨by decide, by decide, by decide⟩ + +/-- One residue below the top the guard holds and all three fragment oracles agree. -/ +theorem evalGPaper_succeeds_below_top : + evalGPaper 5 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 3))) + = some (.atom 4) ∧ + evalPaperMod 5 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 3))) + = some (.atom 4) ∧ + evalPaper 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 3))) + = some (.atom 4) := by + refine ⟨by decide, by decide, by decide⟩ + +end Nock diff --git a/LeanPool/PureNock/PaperNames.lean b/LeanPool/PureNock/PaperNames.lean new file mode 100644 index 0000000000..3c5310c469 --- /dev/null +++ b/LeanPool/PureNock/PaperNames.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import Batteries.Tactic.Alias +public import LeanPool.PureNock.PureComplete +public import LeanPool.PureNock.PureCompleteHint +public import LeanPool.PureNock.NounValidation +public import LeanPool.PureNock.Dyck + +/-! +# Pure paper-name aliases + +Aliases to labelled objects from the same authorial line as ePrint 2025/1110 +(https://eprint.iacr.org/2025/1110). `paper/main.tex` is the bundled draft (different +revision/title). +-/ + +@[expose] public section + +namespace NockPure + +/-! ## Proper trees and nouns (`main.tex:318–349`) -/ + +/-- Def:Proper_BT (main.tex:318–327): L_full_BT = {t | size=1 ∨ (left,right ∈ L_full_BT ∧ left ∩ +right = ∅)}; L^λ_full_BT = {t ∈ L_full_BT | size=λ}. -/ +alias «Def:Proper_BT» := Nock.BTree +/-- Def:tree_nodes (main.tex:329–331): leaf = no children; internal = not a leaf. -/ +alias «Def:tree_nodes» := Nock.Noun.internal +/-- Lem:pbt_size (main.tex:333–335): t ∈ L_full_BT^{2λ−1} has λ−1 internal and λ leaf nodes. -/ +alias «Lem:pbt_size» := Nock.Noun.pbt_size_of_size +/-- Lem:optimal_dyck (main.tex:340–342): L_dk ≡ L_full_BT; ∀λ, L^{2λ}_dk ≡ L^{2λ+1}_full_BT; +|L^{2λ}_dk| = C_λ. -/ +alias «Lem:optimal_dyck» := Nock.BTree.dyck_iff_encode +/-- Def:noun (main.tex:347–349): n := (t,dt) ∈ L_dk^{2(λ−1)} × DT(F)^λ := N^λ(F) is a noun of size λ +over F. -/ +alias «Def:noun» := Nock.Noun + +/-! ## Formalizing Nock (`main.tex:1034–1270`) -/ + +/-- Def:verb (main.tex:1034–1041): verb := (actions,noun) ∈ (X ∪ {*,⊥})^{λ−1} × N^λ(F); terminal iff +actions are all ⊥. -/ +alias «Def:verb» := Nock.Verb +/-- Algorithm:Opcode_Updates_verb (main.tex:1054–1078): OP_i : D(V) → V ∪ {⊥} for i∈[11], plus cons +on cell opcode. -/ +alias «Algorithm:Opcode_Updates_verb» := Nock.Verb.redex +/-- Def:NockProgram (main.tex:1090–1099): F = *[subject,formula] with subject,formula terminal; +NockProgram^Λ = {F | λ≤Λ}. -/ +alias «Def:NockProgram» := Nock.Verb.IsNockProgram +/-- Def:eval_trace (main.tex:1114–1127): T=(t_0,…,t_n) consistent iff t_0=F and ∀i +Nock(t_i,t_{i+1})=0; complete/valid/invalid/continuation. -/ +alias «Def:eval_trace» := Nock.Verb.Consistent +/-- Def:Nock (main.tex:1130–1135): NockVM^Λ = {F | ∃ complete trace T with t_0=F and λ≤Λ}. -/ +alias «Def:Nock» := Nock.Verb.FiniteΛ +/-- Corr:valid_invalid_finite (main.tex:1137–1139): all valid and invalid verbs are finite. -/ +alias «Corr:valid_invalid_finite» := Nock.Verb.finite_of_valid_or_invalid +/-- Lem:domain (main.tex:1141–1143): non-null continuation c ⇒ map(c,getIndex(c)) ∈ D(V). -/ +alias «Lem:domain» := Nock.Verb.domain +/-- lemma:equiv_continuations (main.tex:1150–1154): NockVM^Λ ≡ FiniteTransitions^Λ. -/ +alias «lemma:equiv_continuations» := Nock.Verb.equiv_continuationsΛ +/-- Thm:deterministic_Trace (main.tex:1156–1158): strict map ⇒ ≤1 consistent evaluation trace of +size n. -/ +alias «Thm:deterministic_Trace» := Nock.Verb.deterministic_Trace_strict +/-- Lem:decomp (main.tex:1210–1213): ∃! λ_L,λ_R>0 with λ=λ_L+λ_R, n_L∈N^{λ_L}, n_R∈N^{λ_R} ⇔ +n=cons(n_L,n_R), n∈N^λ. -/ +alias «Lem:decomp» := Nock.Noun.decomp +/-- Algorithm:Valid_Noun (main.tex:1244–1260): CheckValidNoun(n₀;T) walks mset via transcript; +Accept iff mset empties. -/ +alias «Algorithm:Valid_Noun» := Nock.checkValidNoun +/-- Thm:Noun_Validation (main.tex:1268–1269): n∈N^λ(F) ⇔ ∃T. CheckValidNoun(n;T)=Accept ∧ |T|=2λ−1. +-/ +alias «Thm:Noun_Validation» := Nock.Noun_Validation + +/-! ## Appendix A (`main.tex:1514–1666`) -/ + +/-- Algorithm:Noun_Operators (main.tex:1519–1607): ?,=,+,/,# over nouns (Appendix A). -/ +alias «Algorithm:Noun_Operators» := Nock.Verb.reduce +/-- Algorithm:Unroll_Operator (main.tex:1616–1626): −(i,n): i=3 ↦ ?(n); i=4 ↦ +(n); else ⊥. -/ +alias «Algorithm:Unroll_Operator» := Nock.Verb.reduce +/-- Algorithm:Transition_Function (main.tex:1637–1662): Nock(T_i,T_{i+1}) checks local OP_i/cons +step at getIndex. -/ +alias «Algorithm:Transition_Function» := Nock.Verb.NockCheck + +end NockPure diff --git a/LeanPool/PureNock/PaperReference.lean b/LeanPool/PureNock/PaperReference.lean new file mode 100644 index 0000000000..6a4407c02e --- /dev/null +++ b/LeanPool/PureNock/PaperReference.lean @@ -0,0 +1,131 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Reference + +/-! +# Derived OP₀–OP₁₀ evaluator + +Fuel-indexed `evalPaper` from the opcode equations at `main.tex:1054–1076` +(rejects OP₁₁; refines `evalN`). +-/ + +@[expose] public section + +namespace Nock + +open Noun + +/-- `*[subject formula]`, fuel-indexed, **paper fragment only** (OP₀..OP₁₀ + `cons`). + Identical to `evalN` except an OP₁₁ head (`op = 11`) is an unknown opcode and crashes, + matching the paper's `opHeadOk` (`op < 11`) and the small-step machine `runProgram`. -/ +def evalPaper : Nat → Noun → Noun → Option Noun + | 0, _, _ => none + | _ + 1, _, .atom _ => none + | fuel + 1, subj, .cell (.cell b c) d => + match evalPaper fuel subj (.cell b c), evalPaper fuel subj d with + | some l, some r => some (.cell l r) + | _, _ => none + | fuel + 1, subj, .cell (.atom op) tail => + match op with + | 0 => match tail with + | .atom ax => slot ax subj + | _ => none + | 1 => some tail + | 2 => match tail with + | .cell b c => + match evalPaper fuel subj b, evalPaper fuel subj c with + | some sb, some sc => evalPaper fuel sb sc + | _, _ => none + | _ => none + | 3 => (evalPaper fuel subj tail).map wut + | 4 => (evalPaper fuel subj tail).bind lus + | 5 => match tail with + | .cell b c => + match evalPaper fuel subj b, evalPaper fuel subj c with + | some sb, some sc => some (tis sb sc) + | _, _ => none + | _ => none + | 6 => match tail with + | .cell b (.cell c d) => + match evalPaper fuel subj b with + | some (.atom 0) => evalPaper fuel subj c + | some (.atom 1) => evalPaper fuel subj d + | _ => none + | _ => none + | 7 => match tail with + | .cell b c => + match evalPaper fuel subj b with + | some sb => evalPaper fuel sb c + | none => none + | _ => none + | 8 => match tail with + | .cell b c => + match evalPaper fuel subj b with + | some sb => evalPaper fuel (.cell sb subj) c + | none => none + | _ => none + | 9 => match tail with + | .cell (.atom ax) c => + match evalPaper fuel subj c with + | some core => + match slot ax core with + | some arm => evalPaper fuel core arm + | none => none + | none => none + | _ => none + | 10 => match tail with + | .cell (.cell (.atom ax) c) d => + match evalPaper fuel subj c, evalPaper fuel subj d with + | some new, some old => edit ax new old + | _, _ => none + | _ => none + | _ => none -- op ≥ 11 (incl. OP₁₁ hints) ⇒ crash (paper fragment) + +/-- **Fuel monotonicity (single step)** for `evalPaper` (same shape as `evalN_succ`). -/ +theorem evalPaper_succ {n : Nat} {s form r : Noun} + (h : evalPaper n s form = some r) : evalPaper (n + 1) s form = some r := by + induction n, s, form using evalPaper.induct generalizing r with + | case11 fuel subj tail ih => + simp only [evalPaper, Option.map_eq_some_iff] at h ⊢ + obtain ⟨a, ha, hp⟩ := h + exact ⟨a, ih ha, hp⟩ + | case12 fuel subj tail ih => + simp only [evalPaper, Option.bind_eq_some_iff] at h ⊢ + obtain ⟨a, ha, hp⟩ := h + exact ⟨a, ih ha, hp⟩ + | _ => + simp_all [evalPaper] + +/-- **Fuel monotonicity** for `evalPaper`. -/ +theorem evalPaper_mono {n m : Nat} {s form r : Noun} + (hle : n ≤ m) (h : evalPaper n s form = some r) : evalPaper m s form = some r := by + induction hle with + | refl => exact h + | step _ ih => exact evalPaper_succ ih + +-- main.tex:1156–1158 (fuel-indexed shadow of `Thm:deterministic_Trace`, paper fragment) +/-- **`evalPaper` is a partial function (output uniqueness).** Two successful paper-fragment + evaluations of the same `subject`/`formula`, at any fuel budgets, return the same noun. -/ +theorem evalPaper_deterministic {n m : Nat} {s form r₁ r₂ : Noun} + (h₁ : evalPaper n s form = some r₁) (h₂ : evalPaper m s form = some r₂) : r₁ = r₂ := + Option.some.inj ((evalPaper_mono (Nat.le_max_left n m) h₁).symm.trans + (evalPaper_mono (Nat.le_max_right n m) h₂)) + +/-- **`evalPaper` refines `evalN`.** Every paper-fragment evaluation is a standard Nock + evaluation with the same result: `evalPaper` and `evalN` differ only where `evalN` + fires OP₁₁ (which `evalPaper` crashes on). Proof: functional induction; every clause + is identical except OP₁₁, where `evalPaper` returns `none` so the hypothesis is absurd. -/ +theorem evalPaper_le_evalN {n : Nat} {s form r : Noun} + (h : evalPaper n s form = some r) : evalN n s form = some r := by + induction n, s, form using evalPaper.induct generalizing r <;> + simp_all only [evalPaper, evalN, Option.map_eq_some_iff, Option.bind_eq_some_iff, + Option.some.injEq, reduceCtorEq] <;> + grind + +end Nock diff --git a/LeanPool/PureNock/PaperReferenceMod.lean b/LeanPool/PureNock/PaperReferenceMod.lean new file mode 100644 index 0000000000..f0735985a1 --- /dev/null +++ b/LeanPool/PureNock/PaperReferenceMod.lean @@ -0,0 +1,164 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.PaperReference +public import LeanPool.PureNock.ModularEval + +/-! +# Strict modular paper oracle + +`evalPaperMod q`: the paper's OP₀–OP₁₀ fragment (`main.tex:1054–1076`) over +`Nat`-atom nouns, with the OP₄ increment taken **modulo `q`**, exactly as the paper +specifies `+(n) = (1+n) mod p` (`main.tex:1531–1534`). + +It is the strict-language counterpart of `evalPaper`: + +* `evalPaper` = OP₀–OP₁₀ fragment with the *unbounded* Nat increment `n ↦ n+1`; +* `evalPaperMod q` = the same fragment with the *modular* increment `n ↦ (n+1) mod q`. + +Two facts must not be conflated: + +1. `evalPaperMod q` refines the full modular evaluator `FieldEmbedding.evalFBy q` + (`evalPaperMod_le_evalFBy`): they agree on the whole OP₀–OP₁₀ fragment and differ only + where `evalFBy` would fire OP₁₁, which the paper fragment crashes on. +2. `evalPaperMod q` is **not** `evalPaper`: at the top atom `q−1` the modular OP₄ wraps to + `0` while the unbounded `evalPaper` produces `q` (`evalPaperMod_ne_evalPaper_at_top`). + This is the compiled no-overflow distinction, not a claim that the two coincide. +-/ + +@[expose] public section + +namespace Nock + +open Noun + +/-- `*[subject formula]`, fuel-indexed, **strict paper fragment** (OP₀–OP₁₀ + `cons`) with the + paper's **modular** OP₄ increment `+(n) = (1+n) mod q` (`main.tex:1531–1534`). + + Identical to `FieldEmbedding.evalFBy q` except an OP₁₁ head (`op = 11`) is an unknown opcode + and crashes (`none`), matching the paper's `opHeadOk` (`op < 11`) and the small-step machine + `runProgram`. -/ +def evalPaperMod (q : Nat) : Nat → Noun → Noun → Option Noun + | 0, _, _ => none + | _ + 1, _, .atom _ => none + | fuel + 1, subj, .cell (.cell b c) d => + match evalPaperMod q fuel subj (.cell b c), evalPaperMod q fuel subj d with + | some l, some r => some (.cell l r) + | _, _ => none + | fuel + 1, subj, .cell (.atom op) tail => + match op with + | 0 => match tail with + | .atom ax => slot ax subj + | _ => none + | 1 => some tail + | 2 => match tail with + | .cell b c => + match evalPaperMod q fuel subj b, evalPaperMod q fuel subj c with + | some sb, some sc => evalPaperMod q fuel sb sc + | _, _ => none + | _ => none + | 3 => (evalPaperMod q fuel subj tail).map wut + | 4 => (evalPaperMod q fuel subj tail).bind (FieldEmbedding.lusFBy q) + | 5 => match tail with + | .cell b c => + match evalPaperMod q fuel subj b, evalPaperMod q fuel subj c with + | some sb, some sc => some (tis sb sc) + | _, _ => none + | _ => none + | 6 => match tail with + | .cell b (.cell c d) => + match evalPaperMod q fuel subj b with + | some (.atom 0) => evalPaperMod q fuel subj c + | some (.atom 1) => evalPaperMod q fuel subj d + | _ => none + | _ => none + | 7 => match tail with + | .cell b c => + match evalPaperMod q fuel subj b with + | some sb => evalPaperMod q fuel sb c + | none => none + | _ => none + | 8 => match tail with + | .cell b c => + match evalPaperMod q fuel subj b with + | some sb => evalPaperMod q fuel (.cell sb subj) c + | none => none + | _ => none + | 9 => match tail with + | .cell (.atom ax) c => + match evalPaperMod q fuel subj c with + | some core => + match slot ax core with + | some arm => evalPaperMod q fuel core arm + | none => none + | none => none + | _ => none + | 10 => match tail with + | .cell (.cell (.atom ax) c) d => + match evalPaperMod q fuel subj c, evalPaperMod q fuel subj d with + | some new, some old => edit ax new old + | _, _ => none + | _ => none + | _ => none -- op ≥ 11 (incl. OP₁₁ hints) ⇒ crash (paper fragment) + +/-- Convenience wrapper with a large fuel budget, for `#eval` sanity checks. -/ +def evalPaperModTop (q : Nat) (subj formula : Noun) : Option Noun := evalPaperMod q 100000 subj + formula + +/-- **Fuel monotonicity (single step)** for `evalPaperMod`. -/ +theorem evalPaperMod_succ {q n : Nat} {s form r : Noun} + (h : evalPaperMod q n s form = some r) : evalPaperMod q (n + 1) s form = some r := by + induction n, s, form using evalPaperMod.induct q generalizing r with + | case11 fuel subj tail ih => + simp only [evalPaperMod, Option.map_eq_some_iff] at h ⊢ + obtain ⟨a, ha, hp⟩ := h + exact ⟨a, ih ha, hp⟩ + | case12 fuel subj tail ih => + simp only [evalPaperMod, Option.bind_eq_some_iff] at h ⊢ + obtain ⟨a, ha, hp⟩ := h + exact ⟨a, ih ha, hp⟩ + | _ => + simp_all [evalPaperMod] + +/-- **Fuel monotonicity** for `evalPaperMod`. -/ +theorem evalPaperMod_mono {q n m : Nat} {s form r : Noun} + (hle : n ≤ m) (h : evalPaperMod q n s form = some r) : evalPaperMod q m s form = some r := by + induction hle with + | refl => exact h + | step _ ih => exact evalPaperMod_succ ih + +/-- **`evalPaperMod q` refines `evalFBy q`.** Every strict-paper modular evaluation is a full + modular evaluation with the same result: they differ only where `evalFBy` fires OP₁₁, which + `evalPaperMod` crashes on. -/ +theorem evalPaperMod_le_evalFBy {q n : Nat} {s form r : Noun} + (h : evalPaperMod q n s form = some r) : FieldEmbedding.evalFBy q n s form = some r := by + induction n, s, form using evalPaperMod.induct q generalizing r <;> + simp_all only [evalPaperMod, FieldEmbedding.evalFBy, Option.map_eq_some_iff, + Option.bind_eq_some_iff, Option.some.injEq, reduceCtorEq] <;> + grind + +-- main.tex:345–349, 1531–1534 (the strict modular oracle is a field-noun endomorphism) +/-- **`evalPaperMod q` preserves `q`-boundedness (unconditionally).** On canonical field-noun + inputs (`BoundedBy q`), every successful modular run returns a canonical field noun — the + modular oracle maps `𝒩(𝔽)` into `𝒩(𝔽)`, handling the wrap exactly (no guard needed). -/ +theorem evalPaperMod_boundedBy {q fuel : Nat} {s f r : Noun} (hq : 1 < q) + (hs : FieldEmbedding.BoundedBy q s) (hf : FieldEmbedding.BoundedBy q f) + (h : evalPaperMod q fuel s f = some r) : FieldEmbedding.BoundedBy q r := + FieldEmbedding.evalFBy_boundedBy hq fuel s f r hs hf (evalPaperMod_le_evalFBy h) + +-- main.tex:1531–1534 (modular OP₄ ≠ unbounded increment at the top atom) +/-- **Modular OP₄ differs from the unbounded increment at the top atom.** On `+(2)` at `q = 3` + the modular oracle wraps (`(2+1) mod 3 = 0`) while the unbounded `evalPaper` produces `3`. + This is the compiled distinction: the strict paper language is the modular one, not the + unbounded `evalPaper`. -/ +theorem evalPaperMod_ne_evalPaper_at_top : + evalPaperMod 3 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 2))) + ≠ evalPaper 5 (.atom 0) (.cell (.atom 4) (.cell (.atom 1) (.atom 2))) := by + decide + +end Nock diff --git a/LeanPool/PureNock/PureAgreement.lean b/LeanPool/PureNock/PureAgreement.lean new file mode 100644 index 0000000000..48876fe8a9 --- /dev/null +++ b/LeanPool/PureNock/PureAgreement.lean @@ -0,0 +1,1943 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Agreement +public import LeanPool.PureNock.Trace +public import LeanPool.PureNock.PaperReference +public import Mathlib.Tactic.IntervalCases + +/-! +# OP₀–OP₁₀ consistency and OP₁₁ extension + +`denP` is `den` with `evalPaper` at `*` nodes. Also defines the OP₁₁ hint +machine (`nextHint`/`runHint`) by expanding hints at reduction time. +-/ + +@[expose] public section + +namespace Nock +namespace Verb + +open Noun + +/-! ### OP₀–OP₁₀ denotation -/ + +/-- Like `den`, but `*` nodes use `evalPaper` (OP₀–OP₁₀; crashes on OP₁₁). -/ +def denP : Nat → Verb → Option Noun + | _, .leaf k => some (.atom k) + | 0, .node _ _ _ => none + | fuel + 1, .node act l r => + match act with + | .null => + match denP fuel l, denP fuel r with + | some a, some b => some (.cell a b) + | _, _ => none + | .star => + match denP fuel l, denP fuel r with + | some a, some b => evalPaper fuel a b + | _, _ => none + | .slot => + match denP fuel l, denP fuel r with + | some (.atom ax), some b => Noun.slot ax b + | _, _ => none + | .equal => + match denP fuel l, denP fuel r with + | some a, some b => some (Noun.tis a b) + | _, _ => none + | .minus => + match denP fuel l, denP fuel r with + | some (.atom 3), some b => some (Noun.wut b) + | some (.atom 4), some b => Noun.lus b + | _, _ => none + | .edit => + match r with + | .node _ nw od => + match denP fuel l, denP fuel nw, denP fuel od with + | some (.atom ax), some n, some o => Noun.edit ax n o + | _, _, _ => none + | .leaf _ => none + | .wut => none + +theorem denP_succ {n : Nat} {v : Verb} {r : Noun} + (h : denP n v = some r) : denP (n + 1) v = some r := by + induction n, v using denP.induct generalizing r with + | case1 k => simp only [denP] at h ⊢; exact h + | case5 f l r' a b hrb hla ih2 ih1 => + have hla' : denP (f + 1) l = some a := ih2 hla + have hrb' : denP (f + 1) r' = some b := ih1 hrb + simp only [denP, hla, hrb] at h + simp only [denP, hla', hrb'] + exact evalPaper_succ h + | _ => simp_all only [denP, Option.some.injEq, reduceCtorEq] + +theorem denP_mono {n m : Nat} {v : Verb} {r : Noun} + (hle : n ≤ m) (h : denP n v = some r) : denP m v = some r := by + induction hle with + | refl => exact h + | step _ ih => exact denP_succ ih + +/-- `denP` of any terminal verb is its underlying noun (given adequate fuel). Identical to + `den_terminal`: a terminal verb has no `*`-node, so `evalPaper`/`evalN` never enter. -/ +theorem denP_terminal : ∀ (v : Verb) (fuel : Nat), + isTerminal v = true → v.noun.size ≤ fuel → denP fuel v = some v.noun := by + intro v + induction v with + | leaf k => intro fuel _ _; simp [denP, noun] + | node a l r ihl ihr => + intro fuel hterm hsize + simp only [isTerminal, Bool.and_eq_true] at hterm + obtain ⟨⟨hnull, hl⟩, hr⟩ := hterm + have ha : a = Action.null := by cases a <;> simp_all only [Action.isNull, reduceCtorEq] + subst ha + simp only [noun, Noun.size] at hsize ⊢ + obtain ⟨f, rfl⟩ : ∃ f, fuel = f + 1 := ⟨fuel - 1, by omega⟩ + have hfl : l.noun.size ≤ f := by omega + have hfr : r.noun.size ≤ f := by omega + simp only [denP, ihl f hl hfl, ihr f hr hfr] + +/-- `denP` of the terminal verb `ofNoun n` is `n` (given adequate fuel; cf. `den_ofNoun`). -/ +theorem denP_ofNoun : ∀ (n : Noun) (fuel : Nat), n.size ≤ fuel → denP fuel (ofNoun n) = some n := by + intro n + induction n with + | atom k => intro fuel _; simp [ofNoun, denP] + | cell l r ihl ihr => + intro fuel hf + simp only [Noun.size] at hf + obtain ⟨f, rfl⟩ : ∃ f, fuel = f + 1 := ⟨fuel - 1, by omega⟩ + have hfl : l.size ≤ f := by omega + have hfr : r.size ≤ f := by omega + simp only [ofNoun, denP, ihl f hfl, ihr f hfr] + +/-- `denP` of the initial program verb `*[s, f]` is `evalPaper s f` (cf. `den_program`). -/ +theorem denP_program (s f : Noun) (fuel : Nat) (hs : s.size ≤ fuel) (hf : f.size ≤ fuel) : + denP (fuel + 1) (program s f) = evalPaper fuel s f := by + simp only [program, denP, denP_ofNoun s fuel hs, denP_ofNoun f fuel hf] + +/-- **`denP` ≡ `evalPaper` on program verbs, as partial functions** (cf. `den_iff_evalN`). -/ +theorem denP_iff_evalPaper (s f r : Noun) : + (∃ fuel, denP fuel (program s f) = some r) ↔ (∃ fuel, evalPaper fuel s f = some r) := by + constructor + · rintro ⟨fuel, h⟩ + refine ⟨max fuel (max s.size f.size), ?_⟩ + have hm : fuel ≤ max fuel (max s.size f.size) + 1 := by omega + have hs : s.size ≤ max fuel (max s.size f.size) := by omega + have hf : f.size ≤ max fuel (max s.size f.size) := by omega + have := denP_mono hm h + rwa [denP_program s f _ hs hf] at this + · rintro ⟨fuel, h⟩ + refine ⟨max fuel (max s.size f.size) + 1, ?_⟩ + have hm : fuel ≤ max fuel (max s.size f.size) := by omega + have hs : s.size ≤ max fuel (max s.size f.size) := by omega + have hf : f.size ≤ max fuel (max s.size f.size) := by omega + rw [denP_program s f _ hs hf] + exact evalPaper_mono hm h + +/-- A verb has a paper-fragment denotation at some finite fuel. -/ +def DenP (v : Verb) (x : Noun) : Prop := ∃ fuel, denP fuel v = some x + +/-- Paper-fragment Nock evaluation succeeds with some finite fuel. -/ +def EvP (a b x : Noun) : Prop := ∃ fuel, evalPaper fuel a b = some x + +theorem denP_pair {l r a b} (hl : DenP l a) (hr : DenP r b) : + ∃ f, denP f l = some a ∧ denP f r = some b := by + obtain ⟨fl, hl⟩ := hl; obtain ⟨fr, hr⟩ := hr + exact ⟨max fl fr, denP_mono (Nat.le_max_left _ _) hl, denP_mono (Nat.le_max_right _ _) hr⟩ + +theorem denP_triple {a b c va vb vc} (ha : DenP a va) (hb : DenP b vb) (hc : DenP c vc) : + ∃ f, denP f a = some va ∧ denP f b = some vb ∧ denP f c = some vc := by + obtain ⟨fa, ha⟩ := ha; obtain ⟨fb, hb⟩ := hb; obtain ⟨fc, hc⟩ := hc + refine ⟨max fa (max fb fc), denP_mono (by omega) ha, denP_mono (by omega) hb, + denP_mono (by omega) hc⟩ + +theorem denP_det {v r1 r2} (h1 : DenP v r1) (h2 : DenP v r2) : r1 = r2 := by + obtain ⟨f1, h1⟩ := h1; obtain ⟨f2, h2⟩ := h2 + have k1 := denP_mono (Nat.le_max_left f1 f2) h1 + have k2 := denP_mono (Nat.le_max_right f1 f2) h2 + exact Option.some.inj (k1.symm.trans k2) + +theorem ev2P {s1 f1 x1 s2 f2 x2} (h1 : EvP s1 f1 x1) (h2 : EvP s2 f2 x2) : + ∃ f, evalPaper f s1 f1 = some x1 ∧ evalPaper f s2 f2 = some x2 := by + obtain ⟨a, h1⟩ := h1; obtain ⟨b, h2⟩ := h2 + exact ⟨max a b, evalPaper_mono (Nat.le_max_left _ _) h1, evalPaper_mono (Nat.le_max_right _ _) h2⟩ + +theorem ev3P {s1 f1 x1 s2 f2 x2 s3 f3 x3} + (h1 : EvP s1 f1 x1) (h2 : EvP s2 f2 x2) (h3 : EvP s3 f3 x3) : + ∃ f, evalPaper f s1 f1 = some x1 ∧ evalPaper f s2 f2 = some x2 ∧ evalPaper f s3 f3 = some x3 := + by + obtain ⟨a, h1⟩ := h1; obtain ⟨b, h2⟩ := h2; obtain ⟨c, h3⟩ := h3 + exact ⟨max a (max b c), evalPaper_mono (by omega) h1, evalPaper_mono (by omega) h2, + evalPaper_mono (by omega) h3⟩ + +/-! ### `EvP` inversions (evalPaper analogues of the `EvN_*` family) -/ + +theorem EvP_autocons {subj hb hc d x} : EvP subj (.cell (.cell hb hc) d) x ↔ + ∃ l r, x = .cell l r ∧ EvP subj (.cell hb hc) l ∧ EvP subj d r := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper] at h + split at h + · rename_i l r hl hr + exact ⟨l, r, (Option.some.inj h).symm, ⟨f, hl⟩, ⟨f, hr⟩⟩ + · simp at h + · rintro ⟨l, r, rfl, hl, hr⟩ + obtain ⟨f, hl, hr⟩ := ev2P hl hr + exact ⟨f + 1, by simp only [evalPaper, hl, hr]⟩ + +theorem EvP_op0 {subj tail x} : EvP subj (.cell (.atom 0) tail) x ↔ + ∃ ax, tail = .atom ax ∧ Noun.slot ax subj = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper] at h + cases tail with + | atom ax => exact ⟨ax, rfl, h⟩ + | cell => simp at h + · rintro ⟨ax, rfl, hs⟩ + exact ⟨1, by simp only [evalPaper]; exact hs⟩ + +theorem EvP_op1 {subj tail x} : EvP subj (.cell (.atom 1) tail) x ↔ x = tail := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => simp only [evalPaper] at h; exact (Option.some.inj h).symm + · rintro rfl; exact ⟨1, by simp only [evalPaper]⟩ + +theorem EvP_op2 {subj tail x} : EvP subj (.cell (.atom 2) tail) x ↔ + ∃ hb hc sb sc, tail = .cell hb hc ∧ EvP subj hb sb ∧ EvP subj hc sc ∧ EvP sb sc x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper] at h + cases tail with + | atom => simp at h + | cell hb hc => + simp only at h + split at h + · rename_i sb sc hsb hsc + exact ⟨hb, hc, sb, sc, rfl, ⟨f, hsb⟩, ⟨f, hsc⟩, ⟨f, h⟩⟩ + · simp at h + · rintro ⟨hb, hc, sb, sc, rfl, hsb, hsc, hx⟩ + obtain ⟨f, hsb, hsc, hx⟩ := ev3P hsb hsc hx + exact ⟨f + 1, by simp only [evalPaper, hsb, hsc]; exact hx⟩ + +theorem EvP_op3 {subj tail x} : EvP subj (.cell (.atom 3) tail) x ↔ + ∃ y, EvP subj tail y ∧ x = Noun.wut y := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper, Option.map_eq_some_iff] at h + obtain ⟨y, hy, hx⟩ := h + exact ⟨y, ⟨f, hy⟩, hx.symm⟩ + · rintro ⟨y, ⟨fy, hy⟩, rfl⟩ + exact ⟨fy + 1, by simp only [evalPaper, hy, Option.map_some]⟩ + +theorem EvP_op4 {subj tail x} : EvP subj (.cell (.atom 4) tail) x ↔ + ∃ y, EvP subj tail y ∧ Noun.lus y = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper, Option.bind_eq_some_iff] at h + obtain ⟨y, hy, hx⟩ := h + exact ⟨y, ⟨f, hy⟩, hx⟩ + · rintro ⟨y, ⟨fy, hy⟩, hx⟩ + exact ⟨fy + 1, by simp only [evalPaper, hy]; exact hx⟩ + +theorem EvP_op5 {subj tail x} : EvP subj (.cell (.atom 5) tail) x ↔ + ∃ hb hc sb sc, tail = .cell hb hc ∧ EvP subj hb sb ∧ EvP subj hc sc ∧ x = Noun.tis sb sc := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper] at h + cases tail with + | atom => simp at h + | cell hb hc => + simp only at h + split at h + · rename_i sb sc hsb hsc + exact ⟨hb, hc, sb, sc, rfl, ⟨f, hsb⟩, ⟨f, hsc⟩, (Option.some.inj h).symm⟩ + · simp at h + · rintro ⟨hb, hc, sb, sc, rfl, hsb, hsc, rfl⟩ + obtain ⟨f, hsb, hsc⟩ := ev2P hsb hsc + exact ⟨f + 1, by simp only [evalPaper, hsb, hsc]⟩ + +theorem EvP_op6 {subj tail x} : EvP subj (.cell (.atom 6) tail) x ↔ + ∃ b c d cv, tail = .cell b (.cell c d) ∧ EvP subj b cv ∧ + ((cv = .atom 0 ∧ EvP subj c x) ∨ (cv = .atom 1 ∧ EvP subj d x)) := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper] at h + cases tail with + | atom => simp at h + | cell b rest => + cases rest with + | atom => simp at h + | cell c d => + simp only at h + cases hb : evalPaper f subj b with + | none => rw [hb] at h; simp at h + | some bv => + cases bv with + | cell => rw [hb] at h; simp at h + | atom k => + rw [hb] at h + cases k with + | zero => exact ⟨b, c, d, .atom 0, rfl, ⟨f, hb⟩, Or.inl ⟨rfl, ⟨f, h⟩⟩⟩ + | succ k => + cases k with + | zero => exact ⟨b, c, d, .atom 1, rfl, ⟨f, hb⟩, Or.inr ⟨rfl, ⟨f, h⟩⟩⟩ + | succ k => simp at h + · rintro ⟨b, c, d, cv, rfl, hcv, hor⟩ + rcases hor with ⟨rfl, hc⟩ | ⟨rfl, hd⟩ + · obtain ⟨f, hcv, hc⟩ := ev2P hcv hc + refine ⟨f + 1, ?_⟩; rw [evalPaper, hcv]; exact hc + · obtain ⟨f, hcv, hd⟩ := ev2P hcv hd + refine ⟨f + 1, ?_⟩; rw [evalPaper, hcv]; exact hd + +theorem EvP_op7 {subj tail x} : EvP subj (.cell (.atom 7) tail) x ↔ + ∃ hb hc sb, tail = .cell hb hc ∧ EvP subj hb sb ∧ EvP sb hc x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper] at h + cases tail with + | atom => simp at h + | cell hb hc => + simp only at h + cases hsb : evalPaper f subj hb with + | none => rw [hsb] at h; simp at h + | some sb => rw [hsb] at h; exact ⟨hb, hc, sb, rfl, ⟨f, hsb⟩, ⟨f, h⟩⟩ + · rintro ⟨hb, hc, sb, rfl, hsb, hx⟩ + obtain ⟨f, hsb, hx⟩ := ev2P hsb hx + exact ⟨f + 1, by simp only [evalPaper, hsb]; exact hx⟩ + +theorem EvP_op8 {subj tail x} : EvP subj (.cell (.atom 8) tail) x ↔ + ∃ hb hc sb, tail = .cell hb hc ∧ EvP subj hb sb ∧ EvP (.cell sb subj) hc x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper] at h + cases tail with + | atom => simp at h + | cell hb hc => + simp only at h + cases hsb : evalPaper f subj hb with + | none => rw [hsb] at h; simp at h + | some sb => rw [hsb] at h; exact ⟨hb, hc, sb, rfl, ⟨f, hsb⟩, ⟨f, h⟩⟩ + · rintro ⟨hb, hc, sb, rfl, hsb, hx⟩ + obtain ⟨f, hsb, hx⟩ := ev2P hsb hx + exact ⟨f + 1, by simp only [evalPaper, hsb]; exact hx⟩ + +theorem EvP_op9 {subj tail x} : EvP subj (.cell (.atom 9) tail) x ↔ + ∃ ax c core arm, tail = .cell (.atom ax) c ∧ EvP subj c core ∧ + Noun.slot ax core = some arm ∧ EvP core arm x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper] at h + cases tail with + | atom => simp at h + | cell head c => + cases head with + | cell => simp at h + | atom ax => + simp only at h + cases hcore : evalPaper f subj c with + | none => rw [hcore] at h; simp at h + | some core => + rw [hcore] at h + simp only at h + cases hslot : Noun.slot ax core with + | none => rw [hslot] at h; simp at h + | some arm => + rw [hslot] at h + exact ⟨ax, c, core, arm, rfl, ⟨f, hcore⟩, hslot, ⟨f, h⟩⟩ + · rintro ⟨ax, c, core, arm, rfl, hcore, hslot, hx⟩ + obtain ⟨f, hcore, hx⟩ := ev2P hcore hx + refine ⟨f + 1, ?_⟩ + rw [evalPaper]; simp only [hcore, hslot]; exact hx + +theorem EvP_op10 {subj tail x} : EvP subj (.cell (.atom 10) tail) x ↔ + ∃ ax c d new old, tail = .cell (.cell (.atom ax) c) d ∧ + EvP subj c new ∧ EvP subj d old ∧ Noun.edit ax new old = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalPaper] at h + | succ f => + simp only [evalPaper] at h + split at h + · rename_i ax c d + split at h + · rename_i new old hnew hold + exact ⟨ax, c, d, new, old, rfl, ⟨f, hnew⟩, ⟨f, hold⟩, h⟩ + · simp at h + · simp at h + · rintro ⟨ax, c, d, new, old, rfl, hnew, hold, he⟩ + obtain ⟨f, hnew, hold⟩ := ev2P hnew hold + refine ⟨f + 1, ?_⟩ + rw [evalPaper, hnew, hold] + exact he + +/-! ### `DenP` node characterizations (paper-denotation analogues of the `Den_*` family) -/ + +theorem DenP_leaf {k x} : DenP (.leaf k) x ↔ x = .atom k := by + constructor + · rintro ⟨fuel, h⟩; simp only [denP] at h; exact (Option.some.inj h).symm + · rintro rfl; exact ⟨0, by simp [denP]⟩ + +theorem DenP_null {l r x} : DenP (.node .null l r) x ↔ ∃ a b, x = .cell a b ∧ DenP l a ∧ DenP r b := + by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [denP] at h + | succ f => + simp only [denP] at h + cases hl : denP f l with + | none => rw [hl] at h; simp at h + | some a => + cases hr : denP f r with + | none => rw [hl, hr] at h; simp at h + | some b => + rw [hl, hr] at h; simp only [Option.some.injEq] at h + exact ⟨a, b, h.symm, ⟨f, hl⟩, ⟨f, hr⟩⟩ + · rintro ⟨a, b, rfl, hl, hr⟩ + obtain ⟨f, hl, hr⟩ := denP_pair hl hr + exact ⟨f + 1, by simp only [denP, hl, hr]⟩ + +theorem DenP_star {l r x} : DenP (.node .star l r) x ↔ ∃ a b, DenP l a ∧ DenP r b ∧ EvP a b x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [denP] at h + | succ f => + simp only [denP] at h + cases hl : denP f l with + | none => rw [hl] at h; simp at h + | some a => + cases hr : denP f r with + | none => rw [hl, hr] at h; simp at h + | some b => + rw [hl, hr] at h + exact ⟨a, b, ⟨f, hl⟩, ⟨f, hr⟩, ⟨f, h⟩⟩ + · rintro ⟨a, b, hl, hr, hev⟩ + obtain ⟨f, hl, hr⟩ := denP_pair hl hr + obtain ⟨fe, hev⟩ := hev + refine ⟨max f fe + 1, ?_⟩ + simp only [denP, denP_mono (show f ≤ max f fe by omega) hl, denP_mono (show f ≤ max f fe by + omega) hr] + exact evalPaper_mono (by omega) hev + +theorem DenP_slot {l r x} : DenP (.node .slot l r) x ↔ + ∃ ax b, DenP l (.atom ax) ∧ DenP r b ∧ Noun.slot ax b = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [denP] at h + | succ f => + simp only [denP] at h + split at h + · rename_i ax b hl hr + exact ⟨ax, b, ⟨f, hl⟩, ⟨f, hr⟩, h⟩ + · simp at h + · rintro ⟨ax, b, hl, hr, hs⟩ + obtain ⟨f, hl, hr⟩ := denP_pair hl hr + exact ⟨f + 1, by simp only [denP, hl, hr]; exact hs⟩ + +theorem DenP_equal {l r x} : DenP (.node .equal l r) x ↔ + ∃ a b, DenP l a ∧ DenP r b ∧ x = Noun.tis a b := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [denP] at h + | succ f => + simp only [denP] at h + split at h + · rename_i a b hl hr + exact ⟨a, b, ⟨f, hl⟩, ⟨f, hr⟩, (Option.some.inj h).symm⟩ + · simp at h + · rintro ⟨a, b, hl, hr, rfl⟩ + obtain ⟨f, hl, hr⟩ := denP_pair hl hr + exact ⟨f + 1, by simp only [denP, hl, hr]⟩ + +theorem DenP_minus {l r x} : DenP (.node .minus l r) x ↔ + ∃ b, DenP r b ∧ ((DenP l (.atom 3) ∧ x = Noun.wut b) ∨ (DenP l (.atom 4) ∧ Noun.lus b = some x)) + := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [denP] at h + | succ f => + simp only [denP] at h + split at h + · rename_i b hl hr + exact ⟨b, ⟨f, hr⟩, Or.inl ⟨⟨f, hl⟩, (Option.some.inj h).symm⟩⟩ + · rename_i b hl hr + exact ⟨b, ⟨f, hr⟩, Or.inr ⟨⟨f, hl⟩, h⟩⟩ + · simp at h + · rintro ⟨b, hr, hor⟩ + rcases hor with ⟨hl, rfl⟩ | ⟨hl, hlus⟩ + · obtain ⟨f, hl, hr⟩ := denP_pair hl hr + exact ⟨f + 1, by simp only [denP, hl, hr]⟩ + · obtain ⟨f, hl, hr⟩ := denP_pair hl hr + exact ⟨f + 1, by simp only [denP, hl, hr]; exact hlus⟩ + +theorem DenP_edit_node {l b nw od x} : DenP (.node .edit l (.node b nw od)) x ↔ + ∃ ax n o, DenP l (.atom ax) ∧ DenP nw n ∧ DenP od o ∧ Noun.edit ax n o = some x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [denP] at h + | succ f => + simp only [denP] at h + split at h + · rename_i ax n o hl hnw hod + exact ⟨ax, n, o, ⟨f, hl⟩, ⟨f, hnw⟩, ⟨f, hod⟩, h⟩ + · simp at h + · rintro ⟨ax, n, o, hl, hnw, hod, he⟩ + obtain ⟨f, hl, hnw, hod⟩ := denP_triple hl hnw hod + exact ⟨f + 1, by simp only [denP, hl, hnw, hod]; exact he⟩ + +theorem DenP_edit_leaf {l k x} : ¬ DenP (.node .edit l (.leaf k)) x := by + rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [denP] at h + | succ f => simp [denP] at h + +theorem DenP_wut {l r x} : ¬ DenP (.node .wut l r) x := by + rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [denP] at h + | succ f => simp [denP] at h + +theorem DenP_terminal_iff {v x} (ht : isTerminal v = true) : DenP v x ↔ x = v.noun := by + constructor + · intro hd; exact denP_det hd ⟨v.noun.size, denP_terminal v v.noun.size ht (Nat.le_refl _)⟩ + · rintro rfl; exact ⟨v.noun.size, denP_terminal v v.noun.size ht (Nat.le_refl _)⟩ + +/-! ### The OP₁₁ hint macro (reduction-time desugaring into OP₇/OP₈) -/ + +/-- Is `v` an OP₁₁ hint redex? A `*`-node whose formula head is the atom `11`, with subject + and formula-tail in terminal state, and whose tail `n2` is a *cell* (`[t d]` — a hint with a + body `d`). This is the OP₁₁ analogue of `inOpcodeDomain` (which rejects `11` via `opHeadOk`, + `op < 11`), and is aligned exactly with `hintExpand`'s success set: `isHintRedex v = true` + iff `hintExpand v` is `some`. -/ +def isHintRedex : Verb → Bool + | .node .star n1 (.node ra (.leaf 11) (.node rn h b)) => + isTerminal n1 && ra.isNull && isTerminal (.node rn h b) + | _ => false + +/-- **Reduction-time hint expansion.** Rewrite an OP₁₁ redex into its paper-opcode macro: + a dynamic hint `*[n1 11 [t c] d]` becomes `*[n1 8 c [7 [0 3] d]]` (evaluate the clue `c` + against `n1`, pair with `n1`, project `n1` back out via `/[3]`, then run the body `d`); a + static hint `*[n1 11 t d]` (atom tag `t`) becomes `*[n1 d]`. Non-OP₁₁ verbs map to `none`. -/ +def hintExpand : Verb → Option Verb + | .node .star n1 (.node ra (.leaf 11) n2) => + if isTerminal n1 && ra.isNull && isTerminal n2 then + match n2 with + | .node _ (.node _ _ clue) body => -- dynamic: [11 [t c] d] + some (.node .star n1 + (.node .null (.leaf 8) + (.node .null clue + (.node .null (.leaf 7) + (.node .null (.node .null (.leaf 0) (.leaf 3)) body))))) + | .node _ (.leaf _) body => -- static: [11 t d] + some (.node .star n1 body) + | _ => none + else none + | _ => none + +/-- The extended local rewrite: expand an OP₁₁ hint redex (`hintExpand`), otherwise the + standard paper local rewrite `reduce` (opcode `redex` on a `*`-node, or an + operator-application step). Additive: `redex`/`reduce` themselves are untouched. -/ +def redexHint (v : Verb) : Option Verb := + match hintExpand v with + | some v' => some v' + | none => reduce v + +/-- The extended transition domain: `inDomain` plus OP₁₁ hint redexes. -/ +def inDomainHint (v : Verb) : Bool := inDomain v || isHintRedex v + +-- main.tex:1052 + Urbit OP₁₁ (one step of the OP₁₁-extended machine) +/-- One step of the OP₁₁-extended Nock machine: exactly `next`, but with the extended domain + `inDomainHint` and the extended local rewrite `redexHint` (so a selected OP₁₁ hint redex + expands to its OP₇/OP₈ macro instead of crashing). -/ +def nextHint (v : Verb) : Option Verb := + match getIndex v with + | none => none + | some i => + match map i v with + | none => none + | some sub => + if inDomainHint sub then + match redexHint sub with + | some r => some (replaceAt i v r) + | none => none + else none + +/-- Fuel-indexed iteration of `nextHint` to a terminal/⊥ state (the OP₁₁-extended `run`). -/ +def runHint : Nat → Verb → Option Noun + | 0, _ => none + | fuel + 1, v => + match nextHint v with + | some v' => runHint fuel v' + | none => result v + +/-- Run a Nock program `*[subject, formula]` through the OP₁₁-extended machine. -/ +def runHintProgram (fuel : Nat) (subject formula : Noun) : Option Noun := + runHint fuel (program subject formula) + +/-! ### OP₁₁ soundness -/ + +/-- **A hint redex is not a paper redex: `reduce` crashes on it.** An OP₁₁ head fails + `opHeadOk` (`op < 11`), so the paper local rewrite `redex`/`reduce` returns `⊥`. This is + what forces the extended domain to be `inDomain ∨ isHintRedex`, disjointly. -/ +theorem reduce_none_of_isHintRedex {sub} (h : isHintRedex sub = true) : reduce sub = none := by + unfold isHintRedex at h + split at h + · rename_i n1 ra rn hh b + simp [reduce, redex] + · simp at h + +/-- Big-step denotation of a `*`-node with *both* children terminal is `evalN` of the two + underlying nouns. -/ +theorem Den_star_terminal {n1 t x} (h1 : isTerminal n1 = true) (ht : isTerminal t = true) : + Den (.node .star n1 t) x ↔ Ev n1.noun t.noun x := by + rw [Den_star] + constructor + · rintro ⟨a, b, ha, hb, hev⟩ + rw [(Den_terminal_iff h1).1 ha] at hev + rw [(Den_terminal_iff ht).1 hb] at hev + exact hev + · intro hev + exact ⟨n1.noun, t.noun, (Den_terminal_iff h1).2 rfl, (Den_terminal_iff ht).2 rfl, hev⟩ + +/-- **`evalN` OP₁₁ dynamic clause**, as a big-step equivalence: `*[a 11 [t c] d]` succeeds iff + the clue `c` converges and the body `d` yields the result (the clue value is discarded). -/ +theorem EvN_op11_dyn {subj tb clue body x} : + Ev subj (.cell (.atom 11) (.cell (.cell tb clue) body)) x ↔ + (∃ cv, Ev subj clue cv) ∧ Ev subj body x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => + simp only [evalN] at h + cases hcv : evalN f subj clue with + | none => rw [hcv] at h; simp at h + | some cv => rw [hcv] at h; exact ⟨⟨cv, f, hcv⟩, f, h⟩ + · rintro ⟨⟨cv, hcv⟩, hbody⟩ + obtain ⟨f, hcv, hbody⟩ := ev2 hcv hbody + exact ⟨f + 1, by simp only [evalN, hcv]; exact hbody⟩ + +/-- **`evalN` OP₁₁ static clause**, as a big-step equivalence: `*[a 11 t d] = *[a d]`. -/ +theorem EvN_op11_static {subj t body x} : + Ev subj (.cell (.atom 11) (.cell (.atom t) body)) x ↔ Ev subj body x := by + constructor + · rintro ⟨fuel, h⟩ + cases fuel with + | zero => simp [evalN] at h + | succ f => simp only [evalN] at h; exact ⟨f, h⟩ + · rintro ⟨fuel, h⟩; exact ⟨fuel + 1, by simp only [evalN]; exact h⟩ + +/-- **The dynamic-hint macro `*[a 8 c [7 [0 3] d]]` has the same big-step value as OP₁₁**: + evaluate the clue `c`, keep the original subject `a` via `/[3]`, then run the body `d`. -/ +theorem EvN_macro_dyn {subj clue body x} : + Ev subj (.cell (.atom 8) + (.cell clue (.cell (.atom 7) (.cell (.cell (.atom 0) (.atom 3)) body)))) x ↔ + (∃ cv, Ev subj clue cv) ∧ Ev subj body x := by + rw [EvN_op8] + constructor + · rintro ⟨hb, hc, sb, heq, hclue, hrest⟩ + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq + rw [EvN_op7] at hrest + obtain ⟨hb2, hc2, sb2, heq2, hslot, hbody⟩ := hrest + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq2 + rw [EvN_op0] at hslot + obtain ⟨ax, hax, hs⟩ := hslot + obtain rfl := Noun.atom.inj hax + rw [slot_three] at hs + obtain rfl := Option.some.inj hs + exact ⟨⟨sb, hclue⟩, hbody⟩ + · rintro ⟨⟨cv, hclue⟩, hbody⟩ + refine ⟨clue, _, cv, rfl, hclue, ?_⟩ + rw [EvN_op7] + refine ⟨.cell (.atom 0) (.atom 3), body, subj, rfl, ?_, hbody⟩ + rw [EvN_op0] + exact ⟨3, rfl, slot_three⟩ + +/-- Hint expansion preserves `den`: `Den sub x ↔ Den e x` (OP₁₁ analogue of `den_local`). -/ +theorem denHint_expand {sub e x} (he : hintExpand sub = some e) : Den sub x ↔ Den e x := by + unfold hintExpand at he + split at he + · rename_i n1 ra n2 + split at he + · rename_i hguard + simp only [Bool.and_eq_true] at hguard + obtain ⟨⟨h1, hra⟩, hn2⟩ := hguard + have hraN : ra = Action.null := by cases ra <;> simp_all only [Action.isNull, reduceCtorEq] + subst hraN + have hform : isTerminal (Verb.node .null (.leaf 11) n2) = true := by + simp [isTerminal, Action.isNull, hn2] + split at he + · -- dynamic hint n2 = [[tb clue] body] + rename_i rn2 innerA tb clue body + obtain rfl := Option.some.inj he + simp only [isTerminal, Bool.and_eq_true] at hn2 + obtain ⟨⟨_, ⟨_, hclue⟩⟩, hbody⟩ := hn2 + have hE : isTerminal (Verb.node .null (.leaf 8) + (.node .null clue (.node .null (.leaf 7) + (.node .null (.node .null (.leaf 0) (.leaf 3)) body)))) = true := by + simp [isTerminal, Action.isNull, hclue, hbody] + rw [Den_star_terminal h1 hform, Den_star_terminal h1 hE] + simp only [noun] + rw [EvN_op11_dyn, EvN_macro_dyn] + · -- static hint n2 = [t body] (t an atom) + rename_i rn2 t body + obtain rfl := Option.some.inj he + simp only [isTerminal, Bool.and_eq_true] at hn2 + obtain ⟨_, hbody⟩ := hn2 + rw [Den_star_terminal h1 hform, Den_star_terminal h1 hbody] + simp only [noun] + rw [EvN_op11_static] + · simp at he + · simp at he + · simp at he + +/-- A terminal verb (all actions `⊥`) is trivially edit-well-formed. -/ +theorem editOk_of_terminal : ∀ {v : Verb}, isTerminal v = true → editOk v = true := by + intro v + induction v with + | leaf => intro _; rfl + | node a l r ihl ihr => + intro h + simp only [isTerminal, Bool.and_eq_true] at h + obtain ⟨⟨ha, hl⟩, hr⟩ := h + have haN : a = Action.null := by cases a <;> simp_all only [Action.isNull, reduceCtorEq] + subst haN + simp [editOk_null_eq, ihl hl, ihr hr] + +/-- `hintExpand` produces an edit-well-formed verb (its pieces `n1`, clue, body are terminal). -/ +theorem editOk_hintExpand {sub e} (he : hintExpand sub = some e) : editOk e = true := by + unfold hintExpand at he + split at he + · rename_i n1 ra n2 + split at he + · rename_i hguard + simp only [Bool.and_eq_true] at hguard + obtain ⟨⟨h1, _⟩, hn2⟩ := hguard + split at he + · rename_i rn2 innerA tb clue body + obtain rfl := Option.some.inj he + simp only [isTerminal, Bool.and_eq_true] at hn2 + obtain ⟨⟨_, ⟨_, hclue⟩⟩, hbody⟩ := hn2 + simp [editOk, editOk_of_terminal h1, + editOk_of_terminal hclue, editOk_of_terminal hbody] + · rename_i rn2 t body + obtain rfl := Option.some.inj he + simp only [isTerminal, Bool.and_eq_true] at hn2 + obtain ⟨_, hbody⟩ := hn2 + simp [editOk, editOk_of_terminal h1, + editOk_of_terminal hbody] + · simp at he + · simp at he + · simp at he + +/-- `redexHint` (hint expand, else `reduce`) preserves `den`. -/ +theorem denHint_local {sub r₀ x} (hd : inDomainHint sub = true) (hr : redexHint sub = some r₀) : + (Den sub x ↔ Den r₀ x) := by + cases he : hintExpand sub with + | some e => + have hre : r₀ = e := by + unfold redexHint at hr; rw [he] at hr; exact (Option.some.inj hr).symm + subst hre; exact denHint_expand he + | none => + have hr' : reduce sub = some r₀ := by unfold redexHint at hr; rw [he] at hr; exact hr + have hin : inDomain sub = true := by + rw [inDomainHint, Bool.or_eq_true] at hd + rcases hd with h | h + · exact h + · have hnone := reduce_none_of_isHintRedex h + rw [hnone] at hr'; exact absurd hr' (by simp) + exact den_local hin hr' + +/-- `editOk` is preserved by the extended local rewrite. -/ +theorem editOk_redexHint {sub r₀} (hr : redexHint sub = some r₀) (hok : editOk sub = true) : + editOk r₀ = true := by + cases he : hintExpand sub with + | some e => + have hre : r₀ = e := by + unfold redexHint at hr; rw [he] at hr; exact (Option.some.inj hr).symm + subst hre; exact editOk_hintExpand he + | none => + have hr' : reduce sub = some r₀ := by unfold redexHint at hr; rw [he] at hr; exact hr + exact editOk_reduce hr' hok + +/-- `nextHint` realizes the strict DFS selection step over `inDomainHint`/`redexHint` + (OP₁₁ analogue of `next_eq_some_iff`). -/ +theorem nextHint_eq_some_iff {a b : Verb} : nextHint a = some b ↔ + ∃ i sub r, IsMaxPending dfsLt a i ∧ map i a = some sub ∧ inDomainHint sub = true + ∧ redexHint sub = some r ∧ b = replaceAt i a r := by + constructor + · intro h + unfold nextHint at h + split at h + · simp at h + · rename_i i hgi + split at h + · simp at h + · rename_i sub hmap + split at h + · rename_i hdom + split at h + · rename_i r hred + exact ⟨i, sub, r, getIndex_eq_some_iff.1 hgi, hmap, hdom, hred, (Option.some.inj + h).symm⟩ + · simp at h + · simp at h + · rintro ⟨i, sub, r, hmax, hmap, hdom, hred, hb⟩ + have hgi : getIndex a = some i := getIndex_eq_some_iff.2 hmax + unfold nextHint + simp only [hgi, hmap, hdom, hred, ite_true] + rw [hb] + +/-- `editOk` is preserved by `nextHint` (the invariant discharging `denHint_step`). -/ +theorem editOk_nextHint {v v'} (h : nextHint v = some v') (hok : editOk v = true) : + editOk v' = true := by + obtain ⟨i, sub, r₀, hmax, hmap, hdom, hred, hv'⟩ := nextHint_eq_some_iff.1 h + have hoksub : editOk sub = true := editOk_enum v 0 hok (i, sub) (mem_nodes_of_map hmap) + have hokr0 : editOk r₀ = true := editOk_redexHint hred hoksub + obtain ⟨w, hmapw, hpw⟩ := hmax.1 + have hpsub : Verb.isPending sub = true := (Option.some.inj (hmapw.symm.trans hmap)) ▸ hpw + rw [hv', replaceAt] + apply editOk_replaceAux i r₀ hokr0 v 0 hok + intro s hs + have hms : map i v = some s := hs + exact (Option.some.inj (hms.symm.trans hmap)) ▸ hpsub + +/-- One `nextHint` step preserves `den`. -/ +theorem denHint_step {v v' : Verb} (hwf : editOk v = true) (h : nextHint v = some v') {r : Noun} : + (∃ fuel, den fuel v = some r) ↔ (∃ fuel, den fuel v' = some r) := by + obtain ⟨i, sub, r₀, hmax, hmap, hdom, hred, hv'⟩ := nextHint_eq_some_iff.1 h + obtain ⟨w, hmapw, hpw⟩ := hmax.1 + have hpsub : Verb.isPending sub = true := (Option.some.inj (hmapw.symm.trans hmap)) ▸ hpw + have hrel : Rel v v' := by + rw [hv', replaceAt] + apply replaceAux_rel i r₀ v 0 + intro s hs + have hms : map i v = some s := hs + have hss : s = sub := Option.some.inj (hms.symm.trans hmap) + subst hss + exact ⟨hpsub, fun x => denHint_local hdom hred⟩ + exact Rel_den v.noun.size v v' (Nat.le_refl _) hrel hwf r + +/-- Iterating `nextHint` to a terminal state computes `den`. -/ +theorem runHint_sound {v : Verb} {r : Noun} : + ∀ fuel, editOk v = true → runHint fuel v = some r → (∃ f, den f v = some r) := by + intro fuel + induction fuel generalizing v with + | zero => intro _ h; simp [runHint] at h + | succ fuel ih => + intro hwf h + rw [runHint] at h + cases hnext : nextHint v with + | some v' => + rw [hnext] at h + exact (denHint_step hwf hnext).2 (ih (editOk_nextHint hnext hwf) h) + | none => + rw [hnext] at h + simp only [result] at h + split at h + · rename_i hterm + have hr : v.noun = r := Option.some.inj h + subst hr + exact ⟨v.noun.size, den_terminal v v.noun.size hterm (Nat.le_refl _)⟩ + · simp at h + +-- main.tex:1090-1094 + Urbit OP₁₁ +/-- Whenever `runHintProgram` returns a value, `evalN` agrees. -/ +theorem runHintProgram_sound {s f r : Noun} {fuel : Nat} + (h : runHintProgram fuel s f = some r) : ∃ fuel', evalN fuel' s f = some r := by + have : ∃ f', den f' (program s f) = some r := runHint_sound fuel (editOk_program s f) h + exact (den_iff_evalN s f r).1 this + +/-! ### Concrete OP₁₁ boundary witnesses -/ + +/-- Static hint `*[42 11 33 0 1] = 42` (evaluate-and-discard atom tag `33`, then `/[1] = 42`). -/ +def hintProgStatic : Noun := .cell (.atom 11) (.cell (.atom 33) (.cell (.atom 0) (.atom 1))) + +/-- Dynamic hint `*[42 11 [1 1 33] 0 1] = 42` (clue `[1 33]` computed then discarded). -/ +def hintProgDyn : Noun := + .cell (.atom 11) + (.cell (.cell (.atom 1) (.cell (.atom 1) (.atom 33))) + (.cell (.atom 0) (.atom 1))) + +/-- Dynamic hint over an increment: `*[42 11 [1 1 33] 4 0 1] = 43`. -/ +def hintProgInc : Noun := + .cell (.atom 11) + (.cell (.cell (.atom 1) (.cell (.atom 1) (.atom 33))) + (.cell (.atom 4) (.cell (.atom 0) (.atom 1)))) + +/-- **A `*`-node with terminal children has only its root pending.** If `map i` retrieves a + pending node from `*[L, R]` with `L`, `R` terminal, that node is the root itself. -/ +theorem star_pending_is_root {L R : Verb} + (hL : isTerminal L = true) (hR : isTerminal R = true) + {i : Nat} {sub : Verb} + (hmap : map i (.node .star L R) = some sub) (hp : sub.isPending = true) : + sub = .node .star L R := by + have hmem : (i, sub) ∈ nodes (.node .star L R) := mem_nodes_of_map hmap + simp only [nodes, enum, List.mem_cons, List.mem_append] at hmem + rcases hmem with hz | hl | hr + · exact (Prod.mk.injEq .. ▸ hz).2 + · exact absurd (enum_nonpending_of_terminal L hL 1 (i, sub) hl) (by rw [hp]; simp) + · exact absurd (enum_nonpending_of_terminal R hR _ (i, sub) hr) (by rw [hp]; simp) + +/-- The paper machine crashes at the root OP₁₁ redex of a hint program: `next` of the + program verb is `⊥`, because an OP₁₁ head is out of the paper domain (`opHeadOk`, + `op < 11`) and the root `*`-node carries no operator. -/ +theorem next_hintProg_none (subj n2 : Noun) : + next (program subj (.cell (.atom 11) n2)) = none := by + cases hb : next (program subj (.cell (.atom 11) n2)) with + | none => rfl + | some b => + exfalso + obtain ⟨i, sub, r, hmax, hmap, hdom, _, _⟩ := next_eq_some_iff.1 hb + obtain ⟨w, hmapw, hpw⟩ := hmax.1 + have hsw : sub = w := Option.some.inj (hmap.symm.trans hmapw) + have hpsub : sub.isPending = true := hsw ▸ hpw + have hL : isTerminal (ofNoun subj) = true := ofNoun_terminal subj + have hR : isTerminal (ofNoun (.cell (.atom 11) n2)) = true := ofNoun_terminal _ + have hroot : sub = .node .star (ofNoun subj) (ofNoun (.cell (.atom 11) n2)) := + star_pending_is_root hL hR (by simpa [program] using hmap) hpsub + rw [hroot] at hdom + simp [inDomain, inOpcodeDomain, opReady, ofNoun, opHeadOk, Action.isOperator, + Action.isNull, isTerminal] at hdom + +/-- **Paper machine crashes on hints (all fuel).** For every fuel budget, `runProgram` of a + hint program `*[subj 11 …]` is `⊥`: the very first step crashes (OP₁₁ ∉ paper domain), + and the crashed root `*`-verb is not terminal, so `result` is `⊥`. -/ +theorem runProgram_hint_crash (subj n2 : Noun) : + ∀ fuel, runProgram fuel subj (.cell (.atom 11) n2) = none := by + intro fuel + cases fuel with + | zero => rfl + | succ f => + unfold runProgram + rw [run, next_hintProg_none subj n2] + simp [result, program, isTerminal, ofNoun, Action.isNull] + +theorem runHint_hintStatic : runHintProgram 32 (Noun.atom 42) hintProgStatic = some (Noun.atom 42) + := by + decide +kernel + +theorem runHint_hintDyn : runHintProgram 64 (Noun.atom 42) hintProgDyn = some (Noun.atom 42) := by + decide +kernel + +theorem runHint_hintInc : runHintProgram 64 (Noun.atom 42) hintProgInc = some (Noun.atom 43) := by + decide +kernel + +/-- On `*[42 11 33 0 1]`, `runProgram` crashes at every fuel while `runHint` yields `42`. -/ +theorem paper_hint_separation : + (∀ fuel, runProgram fuel (Noun.atom 42) hintProgStatic = none) + ∧ (∃ fuel, runHintProgram fuel (Noun.atom 42) hintProgStatic = some (Noun.atom 42)) := + ⟨runProgram_hint_crash _ _, 32, runHint_hintStatic⟩ + +/-- **Positive verifiable crash.** A `*[subj 11 …]` program — a non-opcode head atom `11` + (`11 ∉ [11] = {0,…,10}`, `main.tex:1052,1083`), outside the paper's opcode domain — reports + `Outcome.crash` under `eval`, not merely `none` under `run`: the first step is stuck + (`next_hintProg_none`) and the crashed `*`-root is non-terminal, so `haltOutcome` is + `crash`. By + `eval_crash_invalid` the evaluated program is `Invalid` (the paper's ⊥, `main.tex:1124`), + not fuel + exhaustion. -/ +theorem evalProgram_hint_crash (subj n2 : Noun) : + evalProgram 1 subj (.cell (.atom 11) n2) = .crash := by + unfold evalProgram + rw [eval_succ, next_hintProg_none subj n2] + exact haltOutcome_crash rfl + +/-- Sharpened separation: `eval` reports a positive `crash` on the non-opcode-head program while + `runHint` yields `42` — "provably crashes", not just "produces no value". -/ +theorem paper_hint_separation_crash : + evalProgram 1 (Noun.atom 42) hintProgStatic = .crash + ∧ (∃ fuel, runHintProgram fuel (Noun.atom 42) hintProgStatic = some (Noun.atom 42)) := + ⟨evalProgram_hint_crash _ _, 32, runHint_hintStatic⟩ + +/-! ### Soundness and completeness interfaces -/ + +/-- Every successful `evalN` evaluation is realized by `runHintProgram`. -/ +def HintComplete : Prop := + ∀ s f r : Noun, (∃ fuel, evalN fuel s f = some r) → (∃ fuel, runHintProgram fuel s f = some r) + +/-- Every successful `evalPaper` evaluation is realized by `runProgram`. -/ +def PaperComplete : Prop := + ∀ s f r : Noun, (∃ fuel, evalPaper fuel s f = some r) → (∃ fuel, runProgram fuel s f = some r) + +/-- Every successful `runProgram` evaluation is realized by `evalPaper`. -/ +def PaperSound : Prop := + ∀ s f r : Noun, (∃ fuel, runProgram fuel s f = some r) → (∃ fuel, evalPaper fuel s f = some r) + +/-! ### OP₀–OP₁₀ soundness replay -/ + +/-- Paper-denotation analogue of `Den_ofNoun_iff` (helper). -/ +theorem DenP_ofNoun_iff {n x} : DenP (ofNoun n) x ↔ x = n := by + rw [DenP_terminal_iff (ofNoun_terminal n), ofNoun_noun] + +theorem den_localP_opReady {sub r₀ x} (hor : opReady sub = true) (hr : reduce sub = some r₀) : + (DenP sub x ↔ DenP r₀ x) := by + cases sub with + | leaf => simp [opReady] at hor + | node a l r => + simp only [opReady, Bool.and_eq_true] at hor + obtain ⟨⟨hao, htl⟩, htr⟩ := hor + have hDl : ∀ v, DenP l v ↔ v = l.noun := fun v => DenP_terminal_iff htl + have hDr : ∀ v, DenP r v ↔ v = r.noun := fun v => DenP_terminal_iff htr + have atominj : ∀ {ax' ax : Nat}, DenP l (.atom ax') → l.noun = .atom ax → ax' = ax := by + intro ax' ax hd he; have := (hDl _).1 hd; rw [he] at this; exact Noun.atom.inj this + cases a with + | null => simp [Action.isOperator] at hao + | star => simp [Action.isOperator] at hao + | wut => simp [reduce] at hr + | equal => + rw [reduce] at hr; obtain rfl := Option.some.inj hr + rw [DenP_equal, DenP_ofNoun_iff] + constructor + · rintro ⟨va, vb, hva, hvb, rfl⟩; rw [(hDl va).1 hva, (hDr vb).1 hvb] + · rintro rfl; exact ⟨l.noun, r.noun, (hDl _).2 rfl, (hDr _).2 rfl, rfl⟩ + | slot => + rw [reduce] at hr + rw [DenP_slot] + split at hr + · rename_i ax heq + rw [Option.map_eq_some_iff] at hr + obtain ⟨sr, hslot, rfl⟩ := hr + rw [DenP_ofNoun_iff] + constructor + · rintro ⟨ax', b, hax', hb, hs⟩ + obtain rfl := atominj hax' heq + rw [(hDr _).1 hb] at hs; rw [hs] at hslot; exact (Option.some.inj hslot) + · rintro rfl + exact ⟨ax, r.noun, (hDl _).2 heq.symm, (hDr _).2 rfl, hslot⟩ + · simp at hr + | minus => + rw [reduce] at hr + rw [DenP_minus] + split at hr + · rename_i heq + obtain rfl := Option.some.inj hr + rw [DenP_ofNoun_iff] + constructor + · rintro ⟨b, hb, hor2⟩ + rcases hor2 with ⟨_, hx⟩ | ⟨h4, _⟩ + · rw [(hDr _).1 hb] at hx; exact hx + · exact absurd (atominj h4 heq) (by decide) + · rintro rfl; exact ⟨r.noun, (hDr _).2 rfl, Or.inl ⟨(hDl _).2 heq.symm, rfl⟩⟩ + · rename_i heq + rw [Option.map_eq_some_iff] at hr + obtain ⟨sr, hlus, rfl⟩ := hr + rw [DenP_ofNoun_iff] + constructor + · rintro ⟨b, hb, hor2⟩ + rcases hor2 with ⟨h3, _⟩ | ⟨_, hlus2⟩ + · exact absurd (atominj h3 heq) (by decide) + · rw [(hDr _).1 hb] at hlus2; rw [hlus2] at hlus; exact (Option.some.inj hlus) + · rintro rfl; exact ⟨r.noun, (hDr _).2 rfl, Or.inr ⟨(hDl _).2 heq.symm, hlus⟩⟩ + · simp at hr + | edit => + cases hln : l.noun with + | cell => simp [reduce, hln] at hr + | atom ax => + cases hr2 : r with + | leaf => simp [reduce, hln, hr2] at hr + | node b nw od => + simp only [reduce, hln, hr2, Option.map_eq_some_iff] at hr + obtain ⟨er, hedit, rfl⟩ := hr + have htr2 : isTerminal (.node b nw od) = true := hr2 ▸ htr + simp only [isTerminal, Bool.and_eq_true] at htr2 + obtain ⟨⟨_, htnw⟩, htod⟩ := htr2 + have hDnw : ∀ v, DenP nw v ↔ v = nw.noun := fun v => DenP_terminal_iff htnw + have hDod : ∀ v, DenP od v ↔ v = od.noun := fun v => DenP_terminal_iff htod + rw [DenP_edit_node, DenP_ofNoun_iff] + constructor + · rintro ⟨ax', n, o, hax', hn, ho, he⟩ + obtain rfl := atominj hax' hln + rw [(hDnw _).1 hn, (hDod _).1 ho] at he + rw [he] at hedit; exact (Option.some.inj hedit) + · rintro rfl + exact ⟨ax, nw.noun, od.noun, (hDl _).2 hln.symm, (hDnw _).2 rfl, (hDod _).2 rfl, + hedit⟩ + +private theorem paper_denotation_conditional_reduction {r₀ : Verb} {x : Noun} (n1 n2 : Verb) (htn2 : + n2.isTerminal = true) + (hn1 : ∀ (v : Noun), n1.DenP v ↔ v = n1.noun) + (star_term : ∀ (W : Verb) (p : Noun), W.isTerminal = true → ((node Action.star n1 W).DenP p ↔ EvP + n1.noun W.noun p)) + (hr : + (match (generalizing := false) n2 with + | node _ b c => + some + (node Action.star n1 + (node Action.star c + (node Action.null (leaf 0) + (node Action.star (node Action.null (leaf 2) (leaf 3)) + (node Action.null (leaf 0) + (node Action.star n1 (node Action.null (leaf 4) (node Action.null (leaf 4) + b)))))))) + | _ => none) = + some r₀) : + EvP n1.noun ((atom 6).cell n2.noun) x ↔ r₀.DenP x := by + cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + have dStar : ∀ (W : Verb) y, + (DenP (Verb.node .star n1 W) y ↔ ∃ q, DenP W q ∧ EvP n1.noun q y) := by + intro W y; rw [DenP_star] + exact ⟨fun ⟨p,q,hp,hq,hev⟩ => ⟨q, hq, (hn1 p).1 hp ▸ hev⟩, + fun ⟨q,hq,hev⟩ => ⟨n1.noun, q, (hn1 _).2 rfl, hq, hev⟩⟩ + have dStarC : ∀ (W : Verb) y, + (DenP (Verb.node .star c W) y ↔ ∃ q, DenP W q ∧ EvP c.noun q y) := by + intro W y; rw [DenP_star] + exact ⟨fun ⟨p,q,hp,hq,hev⟩ => ⟨q, hq, (DenP_terminal_iff htc).1 hp ▸ hev⟩, + fun ⟨q,hq,hev⟩ => ⟨c.noun, q, (DenP_terminal_iff htc).2 rfl, hq, hev⟩⟩ + have hIarg : isTerminal (Verb.node .null (.leaf 4) (.node .null (.leaf 4) b)) = true := by + simp [isTerminal, Action.isNull, htb] + rw [EvP_op6] + constructor + · rintro ⟨B, C, D, cv, heq, hcv, hor⟩ + obtain ⟨hBeq, hCDeq⟩ := Noun.cell.inj heq + subst hBeq + rcases hor with ⟨rfl, hCx⟩ | ⟨rfl, hDx⟩ + · refine (dStar _ _).2 ⟨C, ?_, hCx⟩ + refine (dStarC _ _).2 ⟨Noun.cell (.atom 0) (.atom 2), ?_, + (EvP_op0).2 ⟨2, rfl, hCDeq ▸ slot_two⟩⟩ + refine (DenP_null).2 ⟨.atom 0, .atom 2, rfl, DenP_leaf.2 rfl, ?_⟩ + refine (DenP_star).2 ⟨Noun.cell (.atom 2) (.atom 3), Noun.cell (.atom 0) (.atom 2), + (DenP_null).2 ⟨.atom 2, .atom 3, rfl, DenP_leaf.2 rfl, DenP_leaf.2 rfl⟩, ?_, + (EvP_op0).2 ⟨2, rfl, slot_two⟩⟩ + refine (DenP_null).2 ⟨.atom 0, .atom 2, rfl, DenP_leaf.2 rfl, ?_⟩ + refine (dStar _ _).2 ⟨_, (DenP_terminal_iff hIarg).2 rfl, ?_⟩ + exact (EvP_op4).2 ⟨.atom 1, (EvP_op4).2 ⟨.atom 0, hcv, lus_atom⟩, lus_atom⟩ + · refine (dStar _ _).2 ⟨D, ?_, hDx⟩ + refine (dStarC _ _).2 ⟨Noun.cell (.atom 0) (.atom 3), ?_, + (EvP_op0).2 ⟨3, rfl, hCDeq ▸ slot_three⟩⟩ + refine (DenP_null).2 ⟨.atom 0, .atom 3, rfl, DenP_leaf.2 rfl, ?_⟩ + refine (DenP_star).2 ⟨Noun.cell (.atom 2) (.atom 3), Noun.cell (.atom 0) (.atom 3), + (DenP_null).2 ⟨.atom 2, .atom 3, rfl, DenP_leaf.2 rfl, DenP_leaf.2 rfl⟩, ?_, + (EvP_op0).2 ⟨3, rfl, slot_three⟩⟩ + refine (DenP_null).2 ⟨.atom 0, .atom 3, rfl, DenP_leaf.2 rfl, ?_⟩ + refine (dStar _ _).2 ⟨_, (DenP_terminal_iff hIarg).2 rfl, ?_⟩ + exact (EvP_op4).2 ⟨.atom 2, (EvP_op4).2 ⟨.atom 1, hcv, lus_atom⟩, lus_atom⟩ + · intro hmacro + obtain ⟨qa0, ha0, hC4⟩ := (dStar _ _).1 hmacro + obtain ⟨qb0, hb0, hC3⟩ := (dStarC _ _).1 ha0 + rw [DenP_null] at hb0; obtain ⟨v0, vc0, rfl, hv0, hc0⟩ := hb0 + rw [DenP_leaf] at hv0; subst hv0 + rw [DenP_star] at hc0; obtain ⟨p23, qd0, hp23, hd0, hC2⟩ := hc0 + rw [DenP_null] at hp23; obtain ⟨w2, w3, rfl, hw2, hw3⟩ := hp23 + rw [DenP_leaf] at hw2 hw3; subst hw2; subst hw3 + rw [DenP_null] at hd0; obtain ⟨u0, vinner, rfl, hu0, hinner⟩ := hd0 + rw [DenP_leaf] at hu0; subst hu0 + rw [star_term _ _ hIarg] at hinner + obtain ⟨y, hy1, hly⟩ := (EvP_op4).1 hinner + obtain ⟨z, hz1, hlz⟩ := (EvP_op4).1 hy1 + cases z with + | cell => simp [Noun.lus] at hlz + | atom k => + rw [lus_atom] at hlz; obtain rfl := Option.some.inj hlz + rw [lus_atom] at hly; obtain rfl := Option.some.inj hly + obtain ⟨ax, hax, hs23⟩ := (EvP_op0).1 hC2 + have haxv : ax = k + 2 := by injection hax with h; omega + subst haxv + rcases slot_pair_some _ _ hs23 with h1 | h2 | h3 + · omega + · have hk : k = 0 := by omega + subst hk + rw [slot_two] at hs23; obtain rfl := Option.some.inj hs23 + obtain ⟨ax', hax', hs3⟩ := (EvP_op0).1 hC3 + have : ax' = 2 := by injection hax' with h; omega + subst this + cases hcn : c.noun with + | atom => rw [hcn, slot_two_atom] at hs3; simp at hs3 + | cell C D => + rw [hcn, slot_two] at hs3; obtain rfl := Option.some.inj hs3 + exact ⟨b.noun, C, D, .atom 0, rfl, hz1, Or.inl ⟨rfl, hC4⟩⟩ + · have hk : k = 1 := by omega + subst hk + rw [slot_three] at hs23; obtain rfl := Option.some.inj hs23 + obtain ⟨ax', hax', hs3⟩ := (EvP_op0).1 hC3 + have : ax' = 3 := by injection hax' with h; omega + subst this + cases hcn : c.noun with + | atom => rw [hcn, slot_three_atom] at hs3; simp at hs3 + | cell C D => + rw [hcn, slot_three] at hs3; obtain rfl := Option.some.inj hs3 + exact ⟨b.noun, C, D, .atom 1, rfl, hz1, Or.inr ⟨rfl, hC4⟩⟩ + +theorem den_localP_opcode {sub r₀ x} (hd : inOpcodeDomain sub = true) (hr : reduce sub = some r₀) : + (DenP sub x ↔ DenP r₀ x) := by + cases sub with + | leaf => simp [inOpcodeDomain] at hd + | node a n1 t => + cases t with + | leaf => cases a <;> simp [inOpcodeDomain] at hd + | node ra i n2 => + cases a with + | null => simp [inOpcodeDomain] at hd + | wut => simp [inOpcodeDomain] at hd + | equal => simp [inOpcodeDomain] at hd + | slot => simp [inOpcodeDomain] at hd + | edit => simp [inOpcodeDomain] at hd + | minus => simp [inOpcodeDomain] at hd + | star => + simp only [inOpcodeDomain, Bool.and_eq_true] at hd + obtain ⟨⟨⟨⟨htn1, hra⟩, hti⟩, htn2⟩, hhead⟩ := hd + have htt : isTerminal (Verb.node ra i n2) = true := by + simp only [isTerminal, Bool.and_eq_true]; exact ⟨⟨hra, hti⟩, htn2⟩ + have hn1 : ∀ v, DenP n1 v ↔ v = n1.noun := fun v => DenP_terminal_iff htn1 + have hn2 : ∀ v, DenP n2 v ↔ v = n2.noun := fun v => DenP_terminal_iff htn2 + have star_term : ∀ (W : Verb) (p : Noun), isTerminal W = true → + (DenP (Verb.node .star n1 W) p ↔ EvP n1.noun W.noun p) := by + intro W p htW + rw [DenP_star] + constructor + · rintro ⟨va, vb, hva, hvb, hev⟩ + rw [(DenP_terminal_iff htn1).1 hva, (DenP_terminal_iff htW).1 hvb] at hev; exact hev + · intro hev + exact ⟨n1.noun, W.noun, (DenP_terminal_iff htn1).2 rfl, (DenP_terminal_iff htW).2 rfl, + hev⟩ + have hsub : (DenP (Verb.node .star n1 (.node ra i n2)) x + ↔ EvP n1.noun (Noun.cell i.noun n2.noun) x) := by + rw [star_term (Verb.node ra i n2) x htt]; simp only [noun] + rw [hsub] + rw [reduce] at hr + cases i with + | node bi ic id => + rw [redex] at hr + obtain rfl := Option.some.inj hr + simp only [noun] + rw [DenP_null, EvP_autocons] + constructor + · rintro ⟨l, r, rfl, hl, hr⟩ + refine ⟨l, r, rfl, (star_term _ _ hti).2 ?_, (star_term _ _ htn2).2 hr⟩ + simpa [noun] using hl + · rintro ⟨p, q, rfl, hp, hq⟩ + refine ⟨p, q, rfl, ?_, (star_term _ _ htn2).1 hq⟩ + have := (star_term (Verb.node bi ic id) p hti).1 hp + simpa [noun] using this + | leaf op => + simp only [noun] + simp only [redex] at hr + split at hr + · obtain rfl := Option.some.inj hr + rw [EvP_op0, DenP_slot] + constructor + · rintro ⟨ax, hax, hs⟩; exact ⟨ax, n1.noun, (hn2 _).2 hax.symm, (hn1 _).2 rfl, hs⟩ + · rintro ⟨ax, b, hax, hb, hs⟩ + rw [(hn1 _).1 hb] at hs; exact ⟨ax, ((hn2 _).1 hax).symm, hs⟩ + · obtain rfl := Option.some.inj hr + rw [EvP_op1]; exact (hn2 x).symm + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + rw [EvP_op2, DenP_star] + constructor + · rintro ⟨hb, hc, sb, sc, heq, hsb, hsc, hev⟩ + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq + exact ⟨sb, sc, (star_term _ _ htb).2 hsb, (star_term _ _ htc).2 hsc, hev⟩ + · rintro ⟨sb, sc, hsb, hsc, hev⟩ + exact ⟨b.noun, c.noun, sb, sc, rfl, (star_term _ _ htb).1 hsb, + (star_term _ _ htc).1 hsc, hev⟩ + · obtain rfl := Option.some.inj hr + rw [EvP_op3, DenP_minus] + constructor + · rintro ⟨y, hy, rfl⟩ + exact ⟨y, (star_term _ _ htn2).2 hy, Or.inl ⟨(DenP_leaf).2 rfl, rfl⟩⟩ + · rintro ⟨b, hb, hor⟩ + rcases hor with ⟨_, rfl⟩ | ⟨h4, _⟩ + · exact ⟨b, (star_term _ _ htn2).1 hb, rfl⟩ + · exact absurd ((DenP_leaf).1 h4) (by decide) + · obtain rfl := Option.some.inj hr + rw [EvP_op4, DenP_minus] + constructor + · rintro ⟨y, hy, hlus⟩ + exact ⟨y, (star_term _ _ htn2).2 hy, Or.inr ⟨(DenP_leaf).2 rfl, hlus⟩⟩ + · rintro ⟨b, hb, hor⟩ + rcases hor with ⟨h3, _⟩ | ⟨_, hlus⟩ + · exact absurd ((DenP_leaf).1 h3) (by decide) + · exact ⟨b, (star_term _ _ htn2).1 hb, hlus⟩ + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + rw [EvP_op5, DenP_equal] + constructor + · rintro ⟨hb, hc, sb, sc, heq, hsb, hsc, rfl⟩ + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq + exact ⟨sb, sc, (star_term _ _ htb).2 hsb, (star_term _ _ htc).2 hsc, rfl⟩ + · rintro ⟨sb, sc, hsb, hsc, rfl⟩ + exact ⟨b.noun, c.noun, sb, sc, rfl, (star_term _ _ htb).1 hsb, + (star_term _ _ htc).1 hsc, rfl⟩ + · -- op6 + exact paper_denotation_conditional_reduction n1 n2 htn2 hn1 star_term hr + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + rw [EvP_op7, DenP_star] + constructor + · rintro ⟨hb, hc, sb, heq, hsb, hev⟩ + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq + exact ⟨sb, c.noun, (star_term _ _ htb).2 hsb, (DenP_terminal_iff htc).2 rfl, + hev⟩ + · rintro ⟨p, q, hp, hq, hev⟩ + rw [(DenP_terminal_iff htc).1 hq] at hev + exact ⟨b.noun, c.noun, p, rfl, (star_term _ _ htb).1 hp, hev⟩ + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + rw [EvP_op8, DenP_star] + constructor + · rintro ⟨hb, hc, sb, heq, hsb, hev⟩ + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq + refine ⟨Noun.cell sb n1.noun, c.noun, ?_, (DenP_terminal_iff htc).2 rfl, hev⟩ + rw [DenP_null] + exact ⟨sb, n1.noun, rfl, (star_term _ _ htb).2 hsb, (hn1 _).2 rfl⟩ + · rintro ⟨p, q, hp, hq, hev⟩ + rw [DenP_null] at hp + obtain ⟨pb, pn, rfl, hpb, hpn⟩ := hp + rw [(hn1 _).1 hpn] at hev + rw [(DenP_terminal_iff htc).1 hq] at hev + exact ⟨b.noun, c.noun, pb, rfl, (star_term _ _ htb).1 hpb, hev⟩ + · cases n2 with + | leaf => simp at hr + | node bn b c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, htb⟩, htc⟩ := htn2 + have htform : isTerminal (Verb.node .null (.leaf 2) + (.node .null (.node .null (.leaf 0) (.leaf 1)) (.node .null (.leaf 0) b))) = + true := by + simp [isTerminal, Action.isNull, htb] + rw [EvP_op9, DenP_star] + constructor + · rintro ⟨ax, c', core, arm, heq, hcore, hslot, hev⟩ + obtain ⟨hbn, rfl⟩ := Noun.cell.inj heq + refine ⟨core, _, (star_term c core htc).2 hcore, + (DenP_terminal_iff htform).2 rfl, ?_⟩ + refine (EvP_op2).2 ⟨_, _, core, arm, rfl, ?_, ?_, hev⟩ + · exact (EvP_op0).2 ⟨1, rfl, slot_one⟩ + · exact (EvP_op0).2 ⟨ax, hbn, hslot⟩ + · rintro ⟨p, q, hp, hq, hev⟩ + have hp' := (star_term c p htc).1 hp + rw [(DenP_terminal_iff htform).1 hq] at hev + obtain ⟨hb2, hc2, sb, sc, heq2, hsb, hsc, hev2⟩ := (EvP_op2).1 hev + obtain ⟨rfl, rfl⟩ := Noun.cell.inj heq2 + obtain ⟨ax1, hax1, hs1⟩ := (EvP_op0).1 hsb + obtain ⟨ax2, hax2, hs2⟩ := (EvP_op0).1 hsc + have hax1' : ax1 = 1 := by injection hax1 with h; omega + rw [hax1', slot_one] at hs1; obtain rfl := Option.some.inj hs1 + exact ⟨ax2, c.noun, p, sc, by rw [hax2], hp', hs2, hev2⟩ + · cases n2 with + | leaf => simp at hr + | node bn hd d => + cases hd with + | leaf => simp at hr + | node bhd ax c => + obtain rfl := Option.some.inj hr + simp only [noun, isTerminal, Bool.and_eq_true] at htn2 ⊢ + obtain ⟨⟨_, ⟨_, htax⟩, htc⟩, htd⟩ := htn2 + have hax : ∀ v, DenP ax v ↔ v = ax.noun := fun v => DenP_terminal_iff htax + rw [EvP_op10, DenP_edit_node] + constructor + · rintro ⟨axv, cc, dd, new, old, heq, hnew, hold, hedit⟩ + obtain ⟨heq1, rfl⟩ := Noun.cell.inj heq + obtain ⟨haxn, rfl⟩ := Noun.cell.inj heq1 + exact ⟨axv, new, old, (hax _).2 haxn.symm, (star_term _ _ htc).2 hnew, + (star_term _ _ htd).2 hold, hedit⟩ + · rintro ⟨axv, n, o, haxd, hn, ho, hedit⟩ + refine ⟨axv, c.noun, d.noun, n, o, ?_, (star_term _ _ htc).1 hn, + (star_term _ _ htd).1 ho, hedit⟩ + rw [(hax _).1 haxd] + · simp at hr + +/-- Congruence generated by equal denotations of pending subverbs in the paper fragment. -/ +inductive RelP : Verb → Verb → Prop where + | leaf {k} : RelP (.leaf k) (.leaf k) + | base {v w} : v.isPending = true → (∀ x, DenP v x ↔ DenP w x) → RelP v w + | cong {a l l' r r'} : RelP l l' → RelP r r' → RelP (.node a l r) (.node a l' r') + +theorem RelP.refl : ∀ v, RelP v v + | .leaf _ => .leaf + | .node _ l r => .cong (RelP.refl l) (RelP.refl r) + +theorem replaceAux_relP (i : Nat) (r₀ : Verb) : + ∀ (v : Verb) (c : Nat), + (∀ sub, findAt i (enum v c).2 = some sub → + sub.isPending = true ∧ (∀ x, DenP sub x ↔ DenP r₀ x)) → + RelP v (replaceAux i r₀ v c).2 := by + intro v + induction v with + | leaf k => intro c _; exact RelP.leaf + | node a l r ihl ihr => + intro c hyp + simp only [replaceAux] + by_cases hci : c = i + · subst hci + have hf : findAt c (enum (.node a l r) c).2 = some (.node a l r) := by + simp [enum, findAt] + obtain ⟨hp, hd⟩ := hyp _ hf + rw [ite_eq_left rfl] + exact RelP.base hp hd + · simp only [ite_eq_right hci] + have hine : ¬ (i = c) := fun h => hci h.symm + have hcL : (replaceAux i r₀ l (c+1)).1 = (enum l (c+1)).1 := replaceAux_counter i r₀ l (c+1) + refine RelP.cong (ihl (c+1) ?_) ?_ + · intro sub hsub + apply hyp sub + simp only [enum, findAt, ite_eq_right hine] + rw [findAt_append, hsub] + · rw [hcL] + apply ihr + intro sub hsub + apply hyp sub + simp only [enum, findAt, ite_eq_right hine] + rw [findAt_append] + have hnone : findAt i (enum l (c+1)).2 = none := by + apply findAt_eq_none + intro q hq + have := enum_index_range l (c+1) q hq + have := enum_index_range r (enum l (c+1)).1 (i, sub) (findAt_some_mem hsub) + omega + rw [hnone, hsub] + +theorem Rel_denP : ∀ (n : Nat) (v w : Verb), v.noun.size ≤ n → RelP v w → editOk v = true → + ∀ x, (DenP v x ↔ DenP w x) := by + intro n + induction n with + | zero => intro v w hn _ _ x; cases v <;> simp [Noun.size, Verb.noun] at hn + | succ n ih => + intro v w hn hrel hok x + cases hrel with + | leaf => exact Iff.rfl + | base _ hd => exact hd x + | @cong a l l' r r' hl hr => + simp only [Verb.noun, Noun.size] at hn + simp only [editOk, Bool.and_eq_true] at hok + obtain ⟨⟨hcond, hokl⟩, hokr⟩ := hok + have El : ∀ y, DenP l y ↔ DenP l' y := fun y => ih l l' (by omega) hl hokl y + have Er : ∀ y, DenP r y ↔ DenP r' y := fun y => ih r r' (by omega) hr hokr y + cases a with + | null => + rw [DenP_null, DenP_null] + exact ⟨fun ⟨p,q,he,hp,hq⟩ => ⟨p,q,he,(El p).1 hp,(Er q).1 hq⟩, + fun ⟨p,q,he,hp,hq⟩ => ⟨p,q,he,(El p).2 hp,(Er q).2 hq⟩⟩ + | star => + rw [DenP_star, DenP_star] + exact ⟨fun ⟨p,q,hp,hq,he⟩ => ⟨p,q,(El p).1 hp,(Er q).1 hq,he⟩, + fun ⟨p,q,hp,hq,he⟩ => ⟨p,q,(El p).2 hp,(Er q).2 hq,he⟩⟩ + | slot => + rw [DenP_slot, DenP_slot] + exact ⟨fun ⟨ax,b,hp,hq,hs⟩ => ⟨ax,b,(El _).1 hp,(Er _).1 hq,hs⟩, + fun ⟨ax,b,hp,hq,hs⟩ => ⟨ax,b,(El _).2 hp,(Er _).2 hq,hs⟩⟩ + | equal => + rw [DenP_equal, DenP_equal] + exact ⟨fun ⟨p,q,hp,hq,he⟩ => ⟨p,q,(El p).1 hp,(Er q).1 hq,he⟩, + fun ⟨p,q,hp,hq,he⟩ => ⟨p,q,(El p).2 hp,(Er q).2 hq,he⟩⟩ + | minus => + rw [DenP_minus, DenP_minus] + exact ⟨fun ⟨b,hq,hor⟩ => ⟨b,(Er _).1 hq, + hor.imp (fun ⟨h,e⟩ => ⟨(El _).1 h,e⟩) (fun ⟨h,e⟩ => ⟨(El _).1 h,e⟩)⟩, + fun ⟨b,hq,hor⟩ => ⟨b,(Er _).2 hq, + hor.imp (fun ⟨h,e⟩ => ⟨(El _).2 h,e⟩) (fun ⟨h,e⟩ => ⟨(El _).2 h,e⟩)⟩⟩ + | wut => + exact ⟨fun h => (DenP_wut h).elim, fun h => (DenP_wut h).elim⟩ + | edit => + cases hr with + | leaf => exact ⟨fun h => (DenP_edit_leaf h).elim, fun h => (DenP_edit_leaf h).elim⟩ + | base hp _ => + rw [hp] at hcond; simp at hcond + | @cong b nw nw' od od' hnw hod => + simp only [Verb.noun, Noun.size] at hn + simp only [editOk, Bool.and_eq_true] at hokr + obtain ⟨⟨_, hoknw⟩, hokod⟩ := hokr + have Enw : ∀ y, DenP nw y ↔ DenP nw' y := fun y => ih nw nw' (by omega) hnw hoknw + y + have Eod : ∀ y, DenP od y ↔ DenP od' y := fun y => ih od od' (by omega) hod hokod + y + rw [DenP_edit_node, DenP_edit_node] + exact ⟨fun ⟨ax,nn,oo,ha,hn2,ho,he⟩ => + ⟨ax,nn,oo,(El _).1 ha,(Enw _).1 hn2,(Eod _).1 ho,he⟩, + fun ⟨ax,nn,oo,ha,hn2,ho,he⟩ => + ⟨ax,nn,oo,(El _).2 ha,(Enw _).2 hn2,(Eod _).2 ho,he⟩⟩ + +theorem den_localP {sub r₀ x} (hd : inDomain sub = true) (hr : reduce sub = some r₀) : + (DenP sub x ↔ DenP r₀ x) := by + rw [inDomain, Bool.or_eq_true] at hd + rcases hd with h | h + · exact den_localP_opcode h hr + · exact den_localP_opReady h hr + +theorem den_stepP {v v' : Verb} (hwf : editOk v = true) (h : next v = some v') {r : Noun} : + (∃ fuel, denP fuel v = some r) ↔ (∃ fuel, denP fuel v' = some r) := by + obtain ⟨i, sub, r₀, hmax, hmap, hdom, hred, hv'⟩ := next_eq_some_iff.1 h + obtain ⟨w, hmapw, hpw⟩ := hmax.1 + have hpsub : Verb.isPending sub = true := (Option.some.inj (hmapw.symm.trans hmap)) ▸ hpw + have hrel : RelP v v' := by + rw [hv', replaceAt] + apply replaceAux_relP i r₀ v 0 + intro s hs + have hms : map i v = some s := hs + have hss : s = sub := Option.some.inj (hms.symm.trans hmap) + subst hss + exact ⟨hpsub, fun x => den_localP hdom hred⟩ + exact Rel_denP v.noun.size v v' (Nat.le_refl _) hrel hwf r + +theorem run_soundP {v : Verb} {r : Noun} : + ∀ fuel, editOk v = true → run fuel v = some r → (∃ f, denP f v = some r) := by + intro fuel + induction fuel generalizing v with + | zero => intro _ h; simp [run] at h + | succ fuel ih => + intro hwf h + rw [run] at h + cases hnext : next v with + | some v' => + rw [hnext] at h + exact (den_stepP hwf hnext).2 (ih (editOk_next hnext hwf) h) + | none => + rw [hnext] at h + simp only [result] at h + split at h + · rename_i hterm + have hr : v.noun = r := Option.some.inj h + subst hr + exact ⟨v.noun.size, denP_terminal v v.noun.size hterm (Nat.le_refl _)⟩ + · simp at h + +/-- `runProgram` soundness against the derived OP₀–OP₁₀ oracle. -/ +theorem paperSound_holds : PaperSound := by + intro s f r hrun + obtain ⟨fuel, h⟩ := hrun + have hd : ∃ f', denP f' (program s f) = some r := run_soundP fuel (editOk_program s f) h + exact (denP_iff_evalPaper s f r).1 hd + +-- main.tex:1090-1094 + Urbit OP₁₁ +/-- Conditional wrapper for OP₁₁ adequacy (see `runHint_adequate_complete`). -/ +theorem runHint_adequate (hc : HintComplete) (s f r : Noun) : + (∃ fuel, runHintProgram fuel s f = some r) ↔ (∃ fuel, evalN fuel s f = some r) := + ⟨fun ⟨_, h⟩ => runHintProgram_sound h, hc s f r⟩ + +/-! ### Progress (`denP`-defined + non-terminal ⇒ `next` fires) -/ + +/-- `denP` of a `*`/operator node forces the LEFT child to be `denP`-defined. -/ +theorem denP_left_defined {a : Action} {l r : Verb} {f : Nat} {x : Noun} + (h : denP (f + 1) (Verb.node a l r) = some x) : ∃ y, denP f l = some y := by + cases hl : denP f l with + | some y => exact ⟨y, rfl⟩ + | none => + exfalso + cases a with + | edit => + cases r with + | leaf k => simp [denP] at h + | node rb nw od => simp [denP, hl] at h + | wut => simp [denP] at h + | _ => simp [denP, hl] at h + +/-- `denP` of an `editOk` node forces the RIGHT child to be `denP`-defined (the `edit` case uses + `editOk`: its right child is non-pending, hence a `⊥`-cons of its two `denP`-defined args). -/ +theorem denP_right_defined_of_editOk {a : Action} {l r : Verb} {f : Nat} {x : Noun} + (hok : editOk (Verb.node a l r) = true) (h : denP (f + 1) (Verb.node a l r) = some x) : + ∃ g y, denP g r = some y := by + cases a with + | wut => simp [denP] at h + | edit => + have hrp : Verb.isPending r = false := editOk_edit_cond hok + cases r with + | leaf k => simp [denP] at h + | node rb nw od => + have hrb : rb = Action.null := by + simp only [isPending] at hrp + cases rb <;> simp_all only [Action.isNull, Bool.not_false, reduceCtorEq] + subst hrb + simp only [denP] at h + cases hnw : denP f nw with + | none => rw [hnw] at h; simp at h + | some n => + cases hod : denP f od with + | none => rw [hnw, hod] at h; simp at h + | some o => exact ⟨f + 1, .cell n o, by simp [denP, hnw, hod]⟩ + | null => cases hr : denP f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [denP, hr] + at h + | star => cases hr : denP f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [denP, hr] + at h + | slot => cases hr : denP f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [denP, hr] + at h + | equal => cases hr : denP f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [denP, + hr] at h + | minus => cases hr : denP f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [denP, + hr] at h + +/-- **`denP`-definedness descends to every enumerated subnode** (given `editOk`). -/ +theorem enum_denP_defined : + ∀ (v : Verb), editOk v = true → ∀ (c f : Nat) (x : Noun), + denP f v = some x → ∀ p ∈ (enum v c).2, ∃ g y, denP g p.2 = some y := by + intro v + induction v with + | leaf k => intro _ c f x _ p hp; simp [enum] at hp + | node a l r ihl ihr => + intro hok c f x hdenP p hp + obtain ⟨f, rfl⟩ : ∃ f', f = f' + 1 := by + cases f with + | zero => simp [denP] at hdenP + | succ n => exact ⟨n, rfl⟩ + have hokl := (editOk_children hok).1 + have hokr := (editOk_children hok).2 + simp only [enum, List.mem_cons, List.mem_append] at hp + rcases hp with hroot | hleft | hright + · subst hroot; exact ⟨f + 1, x, hdenP⟩ + · obtain ⟨yl, hyl⟩ := denP_left_defined hdenP + exact ihl hokl (c + 1) f yl hyl p hleft + · obtain ⟨g, yr, hyr⟩ := denP_right_defined_of_editOk hok hdenP + exact ihr hokr _ g yr hyr p hright + +/-- The subnode `map i v = some sub` of a `denP`-defined `editOk` verb is itself `denP`-defined. -/ +theorem DenP_subnode {v : Verb} {x : Noun} (hok : editOk v = true) (hv : DenP v x) + {i : Nat} {sub : Verb} (hmap : map i v = some sub) : ∃ y, DenP sub y := by + obtain ⟨f, hf⟩ := hv + have hmem : (i, sub) ∈ nodes v := mem_nodes_of_map hmap + obtain ⟨g, y, hg⟩ := enum_denP_defined v hok 0 f x hf (i, sub) hmem + exact ⟨y, g, hg⟩ + +/-- `evalPaper` crashes when the formula is an atom (bare noun, no opcode). -/ +theorem evalPaper_atom_none (f : Nat) (s : Noun) (k : Nat) : + evalPaper f s (.atom k) = none := by + cases f <;> simp [evalPaper] + +/-- `evalPaper` crashes on an out-of-fragment opcode head (`op ≥ 11`). -/ +theorem evalPaper_op_ge11 (f : Nat) (s tail : Noun) (op : Nat) (h : 11 ≤ op) : + evalPaper f s (.cell (.atom op) tail) = none := by + cases f with + | zero => rfl + | succ ff => + simp only [evalPaper] + split <;> first | rfl | omega + +/-- A `*`-redex with terminal children and a valid opcode head is in the transition domain. -/ +theorem inDomain_star_of {L I n2 : Verb} {rb : Action} + (hL : isTerminal L = true) (hrb : rb.isNull = true) (hI : isTerminal I = true) + (hn2 : isTerminal n2 = true) (hhead : opHeadOk I = true) : + inDomain (Verb.node .star L (Verb.node rb I n2)) = true := by + simp [inDomain, inOpcodeDomain, hL, hrb, hI, hn2, hhead] + +private theorem paper_progress_for_evaluation {v : Verb} (L R : Verb) (hL + : L.isTerminal = true) + (hR : R.isTerminal = true) (y : Noun) (hsuby : (node Action.star L R).DenP y) + (hstep : + (node Action.star L R).inDomain = true → (node Action.star L R).reduce.isSome = true → ∃ v', + v.next = some v') : + ∃ v', v.next = some v' := by + obtain ⟨a', b', hLa, hRb, hev⟩ := DenP_star.1 hsuby + have haL : a' = L.noun := (DenP_terminal_iff hL).1 hLa + have hbR : b' = R.noun := (DenP_terminal_iff hR).1 hRb + subst haL; subst hbR + cases R with + | leaf k => + exfalso; obtain ⟨f, hf⟩ := hev + simp [noun, evalPaper_atom_none] at hf + | node rb fi n2 => + simp only [isTerminal, Bool.and_eq_true] at hR + obtain ⟨⟨hrb, hfi_t⟩, hn2⟩ := hR + simp only [noun] at hev + cases fi with + | node bi ic id => + exact hstep (inDomain_star_of hL hrb hfi_t hn2 rfl) (by simp [reduce, redex]) + | leaf op => + simp only [noun] at hev + have hop : op < 11 := by + rcases Nat.lt_or_ge op 11 with h | h + · exact h + · exfalso + obtain ⟨f, hf⟩ := hev + rw [evalPaper_op_ge11 f L.noun n2.noun op h] at hf + exact absurd hf (by simp) + interval_cases op + · exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · obtain ⟨hb, hc, _, _, hn2eq, _⟩ := EvP_op2.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · obtain ⟨hb, hc, _, _, hn2eq, _⟩ := EvP_op5.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · obtain ⟨b, c, d, cv, hn2eq, _⟩ := EvP_op6.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · obtain ⟨hb, hc, _, hn2eq, _⟩ := EvP_op7.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · obtain ⟨hb, hc, _, hn2eq, _⟩ := EvP_op8.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · obtain ⟨ax, c, _, _, hn2eq, _⟩ := EvP_op9.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, redex]) + · obtain ⟨ax, c, d, new, old, hn2eq, _⟩ := EvP_op10.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 hd d2 => + rw [noun] at hn2eq + have hhd : hd.noun = Noun.cell (Noun.atom ax) c := (Noun.cell.inj hn2eq).1 + cases hd with + | leaf k => rw [noun] at hhd; exact absurd hhd (by simp) + | node bhd axv cc => + exact hstep (inDomain_star_of hL hrb rfl hn2 (by decide)) (by simp [reduce, + redex]) + +/-- A `denP`-defined, `editOk`, non-terminal verb has a `next` successor. -/ +theorem progressP {v : Verb} {x : Noun} (hwf : editOk v = true) (hd : DenP v x) + (hnt : isTerminal v = false) : ∃ v', next v = some v' := by + have hne : getIndex v ≠ none := by + intro h; rw [getIndex_none_iff_terminal] at h; rw [h] at hnt; simp at hnt + obtain ⟨i, hi⟩ := Option.ne_none_iff_exists'.mp hne + obtain ⟨sub, hmap⟩ := map_some_of_getIndex hi + have hmax : IsMaxPending dfsLt v i := getIndex_eq_some_iff.1 hi + have hsubp : sub.isPending = true := by + obtain ⟨w, hw, hwp⟩ := hmax.1 + rw [hmap] at hw + have heq : sub = w := Option.some.inj hw + rw [heq]; exact hwp + cases sub with + | leaf k => simp [isPending] at hsubp + | node a L R => + obtain ⟨hL, hR⟩ := children_terminal_of_getIndex hi hmap + obtain ⟨y, hsuby⟩ := DenP_subnode hwf hd hmap + have hstep : inDomain (Verb.node a L R) = true → + (reduce (Verb.node a L R)).isSome = true → ∃ v', next v = some v' := by + intro hdom hred + cases hrr : reduce (Verb.node a L R) with + | none => rw [hrr] at hred; simp at hred + | some rr => + exact ⟨replaceAt i v rr, + next_eq_some_iff.2 ⟨i, Verb.node a L R, rr, hmax, hmap, hdom, hrr, rfl⟩⟩ + cases a with + | null => simp [isPending, Action.isNull] at hsubp + | wut => exact absurd hsuby DenP_wut + | equal => + have hdom : inDomain (Verb.node .equal L R) = true := by + simp [inDomain, inOpcodeDomain, opReady, Action.isOperator, hL, hR] + exact hstep hdom (by simp [reduce]) + | slot => + obtain ⟨ax, b, hLax, hRb, hslot⟩ := DenP_slot.1 hsuby + have hLn : L.noun = Noun.atom ax := ((DenP_terminal_iff hL).1 hLax).symm + have hbR : b = R.noun := (DenP_terminal_iff hR).1 hRb + subst hbR + have hdom : inDomain (Verb.node .slot L R) = true := by + simp [inDomain, inOpcodeDomain, opReady, Action.isOperator, hL, hR] + exact hstep hdom (by simp [reduce, hLn, hslot]) + | minus => + obtain ⟨b, hRb, hcase⟩ := DenP_minus.1 hsuby + have hbR : b = R.noun := (DenP_terminal_iff hR).1 hRb + subst hbR + have hdom : inDomain (Verb.node .minus L R) = true := by + simp [inDomain, inOpcodeDomain, opReady, Action.isOperator, hL, hR] + rcases hcase with ⟨hL3, _⟩ | ⟨hL4, hlus⟩ + · have hLn : L.noun = Noun.atom 3 := ((DenP_terminal_iff hL).1 hL3).symm + exact hstep hdom (by simp [reduce, hLn]) + · have hLn : L.noun = Noun.atom 4 := ((DenP_terminal_iff hL).1 hL4).symm + exact hstep hdom (by simp [reduce, hLn, hlus]) + | edit => + cases R with + | leaf k => exact absurd hsuby DenP_edit_leaf + | node rb nw od => + simp only [isTerminal, Bool.and_eq_true] at hR + obtain ⟨⟨hrb, hnw⟩, hod⟩ := hR + obtain ⟨ax, n, o, hLax, hnwn, hodo, hedit⟩ := DenP_edit_node.1 hsuby + have hLn : L.noun = Noun.atom ax := ((DenP_terminal_iff hL).1 hLax).symm + have hnwn' : n = nw.noun := (DenP_terminal_iff hnw).1 hnwn + have hodo' : o = od.noun := (DenP_terminal_iff hod).1 hodo + subst hnwn'; subst hodo' + have hdom : inDomain (Verb.node .edit L (Verb.node rb nw od)) = true := by + simp [inDomain, inOpcodeDomain, opReady, Action.isOperator, hL, + isTerminal, hrb, hnw, hod] + exact hstep hdom (by simp [reduce, hLn, hedit]) + | star => + exact paper_progress_for_evaluation L R hL hR y hsuby hstep +/-! ### Denotation-keyed cost measure -/ + +/-- Weighted `evalPaper`-derivation cost: mirrors `evalPaper` clause-for-clause (same recursive + calls), each opcode contributing a positive weight. Returns 0 where `evalPaper` crashes. -/ +def pcost : Nat → Noun → Noun → Nat + | 0, _, _ => 0 + | _ + 1, _, .atom _ => 0 + | f + 1, subj, .cell (.cell b c) d => + 1 + pcost f subj (.cell b c) + pcost f subj d + | f + 1, subj, .cell (.atom op) tail => + match op with + | 0 => 2 + | 1 => 1 + | 2 => + match tail with + | .cell b c => + let inner := match evalPaper f subj b, evalPaper f subj c with + | some sb, some sc => pcost f sb sc + | _, _ => 0 + 1 + pcost f subj b + pcost f subj c + inner + | _ => 0 + | 3 => 2 + pcost f subj tail + | 4 => 2 + pcost f subj tail + | 5 => + match tail with + | .cell b c => 2 + pcost f subj b + pcost f subj c + | _ => 0 + | 6 => + match tail with + | .cell b (.cell c d) => + let inner := match evalPaper f subj b with + | some (.atom 0) => pcost f subj c + | some (.atom 1) => pcost f subj d + | _ => 0 + 100 + pcost f subj b + inner + | _ => 0 + | 7 => + match tail with + | .cell b c => + let inner := match evalPaper f subj b with + | some sb => pcost f sb c + | _ => 0 + 1 + pcost f subj b + inner + | _ => 0 + | 8 => + match tail with + | .cell b c => + let inner := match evalPaper f subj b with + | some sb => pcost f (.cell sb subj) c + | _ => 0 + 1 + pcost f subj b + inner + | _ => 0 + | 9 => + match tail with + | .cell (.atom ax) c => + let inner := match evalPaper f subj c with + | some core => + match Noun.slot ax core with + | some arm => pcost f core arm + | _ => 0 + | _ => 0 + 20 + pcost f subj c + inner + | _ => 0 + | 10 => + match tail with + | .cell (.cell (.atom _) c) d => 2 + pcost f subj c + pcost f subj d + | _ => 0 + | _ => 0 + +/-- Verb cost (structural in the verb; `F` is a fixed lookup-fuel for denotations). A `*`-node + charges `pcost` of its two denotations (the firing cost); an operator node charges `1`; + `⊥`/leaf charge only their children. Being structural (not fuel-recursive), `replaceAt` + swaps exactly one subtree's cost, so the step-congruence is immediate. -/ +def vcost (F : Nat) : Verb → Nat + | .leaf _ => 0 + | .node a l r => + (match a with + | .star => match denP F l, denP F r with + | some sa, some fo => pcost F sa fo + | _, _ => 0 + | .slot => 1 + | .equal => 1 + | .minus => 1 + | .edit => 1 + | .null => 0 + | .wut => 0) + vcost F l + vcost F r + +/-- `pcost` is unchanged by extra fuel once `evalPaper` succeeds (mirrors `evalPaper_succ`). -/ +theorem pcost_succ {n : Nat} {a b : Noun} {r : Noun} + (hb : evalPaper n a b = some r) : pcost (n + 1) a b = pcost n a b := by + induction n, a, b using evalPaper.induct generalizing r <;> + simp_all only [evalPaper, pcost, Option.map_eq_some_iff, Option.bind_eq_some_iff, + Option.some.injEq, reduceCtorEq] <;> + grind only [evalPaper_succ] + +/-- `pcost` stabilizes for any fuel `≥ n` once `evalPaper n` succeeds. -/ +theorem pcost_mono {n m : Nat} {a b r : Noun} (hle : n ≤ m) (hb : evalPaper n a b = some r) : + pcost m a b = pcost n a b := by + induction hle with + | refl => rfl + | step hk ih => rw [pcost_succ (evalPaper_mono hk hb)]; exact ih + +/-- `vcost` of a terminal verb is `0`. -/ +theorem vcost_terminal : ∀ (v : Verb), isTerminal v = true → ∀ F, vcost F v = 0 := by + intro v + induction v with + | leaf k => intro _ F; rfl + | node a l r ihl ihr => + intro hterm F + simp only [isTerminal, Bool.and_eq_true] at hterm + obtain ⟨⟨hnull, hl⟩, hr⟩ := hterm + have ha : a = Action.null := by cases a <;> simp_all only [Action.isNull, reduceCtorEq] + subst ha + simp only [vcost]; rw [ihl hl F, ihr hr F] + +-- main.tex:1052-1083 (`i ∈ [11]` = {0,…,10}) +/-- Conditional wrapper for OP₀–OP₁₀ consistency (see `runProgram_adequate_complete`). -/ +theorem runProgram_adequate (hc : PaperComplete) (s f r : Noun) : + (∃ fuel, runProgram fuel s f = some r) ↔ (∃ fuel, evalPaper fuel s f = some r) := + ⟨paperSound_holds s f r, hc s f r⟩ + +end Verb +end Nock diff --git a/LeanPool/PureNock/PureComplete.lean b/LeanPool/PureNock/PureComplete.lean new file mode 100644 index 0000000000..37ebf7f185 --- /dev/null +++ b/LeanPool/PureNock/PureComplete.lean @@ -0,0 +1,1161 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.PureAgreement +public import Mathlib.Tactic.IntervalCases + +/-! +# Completeness for the derived OP₀–OP₁₀ oracle + +`progressP` and the denotation-keyed `vcost`/`pcost` measure show every +successful `evalPaper` evaluation is realized by `runProgram`. +-/ + +@[expose] public section + +namespace Nock +namespace Verb + +open Noun + +/-! ### Helper: right child of a denP-defined editOk node is denP-defined at the SAME fuel -/ + +theorem denP_right_F {a : Action} {l r : Verb} {f : Nat} {x : Noun} + (hok : editOk (Verb.node a l r) = true) (h : denP (f + 1) (Verb.node a l r) = some x) : + ∃ yr, denP (f + 1) r = some yr := by + cases a with + | wut => simp [denP] at h + | edit => + have hrp : Verb.isPending r = false := editOk_edit_cond hok + cases r with + | leaf k => simp [denP] at h + | node rb nw od => + have hrb : rb = Action.null := by + simp only [isPending] at hrp + cases rb <;> simp_all [Action.isNull] + subst hrb + simp only [denP] at h + cases hnw : denP f nw with + | none => rw [hnw] at h; simp at h + | some n => + cases hod : denP f od with + | none => rw [hnw, hod] at h; simp at h + | some o => exact ⟨.cell n o, by simp [denP, hnw, hod]⟩ + | null => cases hr : denP f r with | some y => exact ⟨y, denP_succ hr⟩ | none => simp [denP, hr] + at h + | star => cases hr : denP f r with | some y => exact ⟨y, denP_succ hr⟩ | none => simp [denP, hr] + at h + | slot => cases hr : denP f r with | some y => exact ⟨y, denP_succ hr⟩ | none => simp [denP, hr] + at h + | equal => cases hr : denP f r with | some y => exact ⟨y, denP_succ hr⟩ | none => simp [denP, hr] + at h + | minus => cases hr : denP f r with | some y => exact ⟨y, denP_succ hr⟩ | none => simp [denP, hr] + at h + +theorem denP_children_F {a : Action} {l r : Verb} {F : Nat} {x : Noun} + (hok : editOk (Verb.node a l r) = true) (h : denP F (Verb.node a l r) = some x) : + (∃ yl, denP F l = some yl) ∧ (∃ yr, denP F r = some yr) := by + obtain ⟨f, rfl⟩ : ∃ f, F = f + 1 := by + cases F with + | zero => simp [denP] at h + | succ n => exact ⟨n, rfl⟩ + refine ⟨?_, denP_right_F hok h⟩ + obtain ⟨yl, hyl⟩ := denP_left_defined h + exact ⟨yl, denP_succ hyl⟩ + +theorem isTerminal_node {a : Action} {l r : Verb} : + isTerminal (Verb.node a l r) = (a.isNull && isTerminal l && isTerminal r) := rfl + +theorem denP_terminal_val {v : Verb} {F : Nat} {y : Noun} + (ht : isTerminal v = true) (h : denP F v = some y) : y = v.noun := + (DenP_terminal_iff ht).1 ⟨F, h⟩ + +theorem denP_term_children {a : Action} {l r : Verb} {F : Nat} {x : Noun} + (hok : editOk (Verb.node a l r) = true) (h : denP F (Verb.node a l r) = some x) + (htl : isTerminal l = true) (htr : isTerminal r = true) : + denP F l = some l.noun ∧ denP F r = some r.noun := by + obtain ⟨⟨yl, hyl⟩, ⟨yr, hyr⟩⟩ := denP_children_F hok h + rw [denP_terminal_val htl hyl] at hyl + rw [denP_terminal_val htr hyr] at hyr + exact ⟨hyl, hyr⟩ + +theorem denP_term_split {l r : Verb} {f : Nat} {x : Noun} + (htl : isTerminal l = true) (htr : isTerminal r = true) + (h : denP (f + 1) (Verb.node .null l r) = some x) : + denP f l = some l.noun ∧ denP f r = some r.noun := by + simp only [denP] at h + cases hl : denP f l with + | none => rw [hl] at h; simp at h + | some yl => + cases hr : denP f r with + | none => rw [hl, hr] at h; simp at h + | some yr => + refine ⟨?_, ?_⟩ + · rw [denP_terminal_val htl hl] + · rw [denP_terminal_val htr hr] + +theorem denP_star_val {A B : Verb} {f : Nat} {a b w : Noun} + (hA : denP f A = some a) (hB : denP f B = some b) (he : evalPaper f a b = some w) : + denP (f + 1) (Verb.node .star A B) = some w := by + simp only [denP, hA, hB]; exact he + +theorem denP_null_val {A B : Verb} {f : Nat} {a b : Noun} + (hA : denP f A = some a) (hB : denP f B = some b) : + denP (f + 1) (Verb.node .null A B) = some (Noun.cell a b) := by + simp only [denP, hA, hB] + +theorem denP_null_none {A B : Verb} {f : Nat} (hA : denP f A = none) : + denP (f + 1) (Verb.node .null A B) = none := by + simp only [denP, hA] + +theorem isTerminal_of_map_ofNoun {o : Option Noun} {r₀ : Verb} + (h : o.map ofNoun = some r₀) : isTerminal r₀ = true := by + rw [Option.map_eq_some_iff] at h; obtain ⟨w, _, rfl⟩ := h; exact ofNoun_terminal w + +theorem pcost_align {n m k : Nat} {a b r : Noun} (h : evalPaper n a b = some r) + (hm : n ≤ m) (hk : n ≤ k) : pcost m a b = pcost k a b := + (pcost_mono hm h).trans (pcost_mono hk h).symm + +/-! ### Controlled `vcost` node equations (avoid `simp [vcost]` over-unfolding literals) -/ + +theorem vcost_star_eq {F : Nat} {l r : Verb} : vcost F (Verb.node .star l r) = + (match denP F l, denP F r with | some sa, some fo => pcost F sa fo | _, _ => 0) + + vcost F l + vcost F r := rfl +theorem vcost_slot_eq {F : Nat} {l r : Verb} : + vcost F (Verb.node .slot l r) = 1 + vcost F l + vcost F r := rfl +theorem vcost_equal_eq {F : Nat} {l r : Verb} : + vcost F (Verb.node .equal l r) = 1 + vcost F l + vcost F r := rfl +theorem vcost_minus_eq {F : Nat} {l r : Verb} : + vcost F (Verb.node .minus l r) = 1 + vcost F l + vcost F r := rfl +theorem vcost_edit_eq {F : Nat} {l r : Verb} : + vcost F (Verb.node .edit l r) = 1 + vcost F l + vcost F r := rfl +theorem vcost_null_eq {F : Nat} {l r : Verb} : + vcost F (Verb.node .null l r) = vcost F l + vcost F r := by + simp only [vcost]; omega +theorem vcost_wut_eq {F : Nat} {l r : Verb} : + vcost F (Verb.node .wut l r) = vcost F l + vcost F r := by + simp only [vcost]; omega + +/-- `vcost` of a `*`-node with terminal, denP-defined children is the firing `pcost`. -/ +theorem vcost_star_terminal {F : Nat} {A B : Verb} + (hA : isTerminal A = true) (hB : isTerminal B = true) + (hdA : denP F A = some A.noun) (hdB : denP F B = some B.noun) : + vcost F (Verb.node .star A B) = pcost F A.noun B.noun := by + simp only [vcost_star_eq, hdA, hdB, vcost_terminal A hA F, vcost_terminal B hB F, Nat.add_zero] + +/-! ### Lemma 1 — `vcost_succ` -/ + +theorem vcost_succ {F : Nat} {v : Verb} {x : Noun} + (hok : editOk v = true) (hv : denP F v = some x) : vcost (F + 1) v = vcost F v := by + induction v generalizing F x with + | leaf k => rfl + | node a l r ihl ihr => + obtain ⟨f, rfl⟩ : ∃ f, F = f + 1 := by + cases F with + | zero => simp [denP] at hv + | succ n => exact ⟨n, rfl⟩ + obtain ⟨hokl, hokr⟩ := editOk_children hok + obtain ⟨⟨yl, hyl⟩, ⟨yr, hyr⟩⟩ := denP_children_F hok hv + have ihl' : vcost (f + 1 + 1) l = vcost (f + 1) l := ihl hokl hyl + have ihr' : vcost (f + 1 + 1) r = vcost (f + 1) r := ihr hokr hyr + cases a with + | null => simp only [vcost, ihl', ihr'] + | wut => simp [denP] at hv + | slot => simp only [vcost, ihl', ihr'] + | equal => simp only [vcost, ihl', ihr'] + | minus => simp only [vcost, ihl', ihr'] + | edit => simp only [vcost, ihl', ihr'] + | star => + -- unfold star clause; both fuels pick the same denP values sa, fo + simp only [denP] at hv + -- hv : (match denP f l, denP f r with ...) = some x + cases hfl : denP f l with + | none => rw [hfl] at hv; simp at hv + | some sa => + cases hfr : denP f r with + | none => rw [hfl, hfr] at hv; simp at hv + | some fo => + rw [hfl, hfr] at hv + -- hv : evalPaper f sa fo = some x + have hdl1 : denP (f + 1) l = some sa := denP_succ hfl + have hdr1 : denP (f + 1) r = some fo := denP_succ hfr + have hdl2 : denP (f + 1 + 1) l = some sa := denP_succ hdl1 + have hdr2 : denP (f + 1 + 1) r = some fo := denP_succ hdr1 + have hev1 : evalPaper (f + 1) sa fo = some x := evalPaper_succ hv + have hpc : pcost (f + 1 + 1) sa fo = pcost (f + 1) sa fo := pcost_succ hev1 + simp only [vcost, hdl1, hdr1, hdl2, hdr2, hpc, ihl', ihr'] + +/-! ### Lemma 2 — `vcost_mono` -/ + +theorem vcost_mono {F G : Nat} {v : Verb} {x : Noun} + (hle : F ≤ G) (hok : editOk v = true) (hv : denP F v = some x) : vcost G v = vcost F v := by + induction hle with + | refl => rfl + | @step m h ih => rw [vcost_succ hok (denP_mono h hv)]; exact ih + +theorem EvP_det {a b y1 y2 : Noun} (h1 : EvP a b y1) (h2 : EvP a b y2) : y1 = y2 := by + obtain ⟨f1, h1⟩ := h1; obtain ⟨f2, h2⟩ := h2 + exact Option.some.inj ((evalPaper_mono (Nat.le_max_left f1 f2) h1).symm.trans + (evalPaper_mono (Nat.le_max_right f1 f2) h2)) + +theorem denP_null_leaf0 {inner : Verb} {f : Nat} {dv : Noun} + (h : denP (f + 1) (Verb.node .null (Verb.leaf 0) inner) = some dv) : + ∃ w, dv = Noun.cell (Noun.atom 0) w := by + simp only [denP] at h + cases hi : denP f inner with + | none => rw [hi] at h; simp at h + | some iv => rw [hi] at h; exact ⟨iv, (Option.some.inj h).symm⟩ + +/-- **OP₆ strict local decrease** (if-then-else macro). The reduct is a 6-level nested `*`-tree; + its `vcost` is `SF(L,a0) + SF(c,b0) + SF([2,3],d0) + SF(L,[4[4b]])`, where the last is exactly + `4 + pcost g L b`, the middle two are `≤ 2` (op0), and the first is `0` or the branch cost + (matching the RHS branch by a full evaluation trace `b → *[L,b]∈{0,1} → +2 → slot → branch`). + The op₆ weight `100` dominates the macro skeleton (`8`). -/ +theorem op6_decrease {g : Nat} {L b thn els : Verb} {x : Noun} {rn2 rc : Action} + (hL : isTerminal L = true) (htc : isTerminal (Verb.node rc thn els) = true) + (hthn : isTerminal thn = true) (htels : isTerminal els = true) + (hgc : denP g (Verb.node rc thn els) = some (Verb.node rc thn els).noun) + (hdL : denP (g + 1 + 1) L = some L.noun) + (h4b : denP (g + 1 + 1) (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) + = some (Noun.cell (Noun.atom 4) (Noun.cell (Noun.atom 4) b.noun))) + (ht4 : isTerminal (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) = true) + (ht23 : isTerminal (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) = true) + (hvv : evalPaper (g + 1) L.noun + (Noun.cell (Noun.atom 6) (Verb.node rn2 b (Verb.node rc thn els)).noun) = some x) : + vcost (g + 1 + 1) + (Verb.node .star L + (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L + (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)))))))) + < pcost (g + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 6) (Verb.node rn2 b (Verb.node rc thn els))).noun := by + have hleaf0 : isTerminal (Verb.leaf 0) = true := rfl + have h23 : denP (g + 1 + 1) (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + = some (Noun.cell (Noun.atom 2) (Noun.atom 3)) := + denP_null_val (by simp only [denP]) (by simp only [denP]) + have hdc : denP (g + 1 + 1) (Verb.node rc thn els) = some (Verb.node rc thn els).noun := + denP_succ (denP_succ hgc) + -- op₆ evaluation of the guard `b` + simp only [noun, evalPaper] at hvv + cases hsb : evalPaper g L.noun b.noun with + | none => rw [hsb] at hvv; simp at hvv + | some sbv => + rw [hsb] at hvv + have hEvb : EvP L.noun b.noun sbv := ⟨g, hsb⟩ + have hsb1 : evalPaper (g + 1) L.noun b.noun = some sbv := evalPaper_succ hsb + have eqLb : pcost g L.noun b.noun = pcost (g + 1) L.noun b.noun := + pcost_align hsb (Nat.le_refl g) (by omega) + -- `[4 [4 b]]` cost is `4 + pcost g L b` (definitional, op4∘op4) + have hSFinner : pcost (g + 1 + 1) L.noun (Noun.cell (Noun.atom 4) (Noun.cell (Noun.atom 4) + b.noun)) + = 4 + pcost g L.noun b.noun := by simp only [pcost]; omega + -- The generic argument: whatever branch, the reduct cost < RHS. + -- key: SF(c,b0) ≤ 2, SF([2,3],d0) ≤ 2, SF(L,a0) is 0 or the branch pcost. + -- We prove it by decomposing `vcost` and bounding. + have hbound : ∀ (branchNoun : Verb), + (sbv = Noun.atom 0 ∧ branchNoun = thn ∨ sbv = Noun.atom 1 ∧ branchNoun = els) → + evalPaper g L.noun branchNoun.noun = some x → + vcost (g + 1 + 1) + (Verb.node .star L + (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L + (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)))))))) + < pcost (g + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 6) (Verb.node rn2 b (Verb.node rc thn els))).noun := by + intro branchNoun hbr hbev + -- align the branch cost + have eqBr : pcost (g + 1 + 1) L.noun branchNoun.noun = pcost (g + 1) L.noun branchNoun.noun := + pcost_align hbev (by omega) (by omega) + -- RHS = 100 + pcost(g+1) L b + pcost(g+1) L branch + have hRHS : pcost (g + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 6) (Verb.node rn2 b (Verb.node rc thn els))).noun + = 100 + pcost (g + 1) L.noun b.noun + pcost (g + 1) L.noun branchNoun.noun := by + rcases hbr with ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩ <;> + simp only [noun, pcost, hsb1] + rw [hRHS] + -- decompose vcost of the reduct + simp only [vcost_star_eq, vcost_null_eq, vcost_terminal L hL, + vcost_terminal (Verb.node rc thn els) htc, + vcost_terminal (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) ht23, + vcost_terminal (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) ht4, + vcost_terminal (Verb.leaf 0) hleaf0, hdL, h4b, Nat.add_zero, Nat.zero_add] + -- SF([2,3],d0) ≤ 2 + have hc0le : (match denP (g + 1 + 1) (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)), + denP (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)))) + with + | some sa, some fo => pcost (g + 1 + 1) sa fo | _, _ => 0) ≤ 2 := by + rw [h23] + cases hd0 : denP (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)))) + with + | none => simp + | some dv => obtain ⟨w, rfl⟩ := denP_null_leaf0 hd0; simp only [pcost]; omega + -- SF(c,b0) ≤ 2 + have ha0le : (match denP (g + 1 + 1) (Verb.node rc thn els), + denP (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) + b)))))) with + | some sa, some fo => pcost (g + 1 + 1) sa fo | _, _ => 0) ≤ 2 := by + rw [hdc] + cases hb0 : denP (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) + b)))))) with + | none => simp + | some bv => obtain ⟨w, rfl⟩ := denP_null_leaf0 hb0; simp only [pcost]; omega + -- SF(L,a0) ≤ pcost(g+1) L branch + have hrrle : (match some L.noun, + denP (g + 1 + 1) (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) + b))))))) with + | some sa, some fo => pcost (g + 1 + 1) sa fo | _, _ => 0) + ≤ pcost (g + 1) L.noun branchNoun.noun := by + cases haa : denP (g + 1 + 1) (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) + b))))))) with + | none => simp + | some aa => + simp only + -- trace: aa = branchNoun.noun + have hDa0 : DenP (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf + 4) b))))))) aa := + ⟨g + 1 + 1, haa⟩ + rw [DenP_star] at hDa0 + obtain ⟨cv, b0v, hDc0, hDb0, hEva0⟩ := hDa0 + have : cv = (Verb.node rc thn els).noun := (DenP_terminal_iff htc).1 hDc0 + subst this + rw [DenP_null] at hDb0 + obtain ⟨z, c0v, rfl, hDz, hDc0'⟩ := hDb0 + have : z = Noun.atom 0 := DenP_leaf.1 hDz + subst this + rw [EvP_op0] at hEva0 + obtain ⟨ax, rfl, hslotA⟩ := hEva0 + rw [DenP_star] at hDc0' + obtain ⟨p23, d0v, hD23, hDd0, hEvc0⟩ := hDc0' + have hp23 : p23 = Noun.cell (Noun.atom 2) (Noun.atom 3) := by + have := (DenP_terminal_iff ht23).1 hD23; simpa only [noun] using this + subst hp23 + rw [DenP_null] at hDd0 + obtain ⟨z2, iv, rfl, hDz2, hDinner⟩ := hDd0 + have : z2 = Noun.atom 0 := DenP_leaf.1 hDz2 + subst this + rw [EvP_op0] at hEvc0 + obtain ⟨ax2, rfl, hslotB⟩ := hEvc0 + rw [DenP_star] at hDinner + obtain ⟨Lv, fv, hDL', hD4, hEvinner⟩ := hDinner + have : Lv = L.noun := (DenP_terminal_iff hL).1 hDL' + subst this + have hfv : fv = Noun.cell (Noun.atom 4) (Noun.cell (Noun.atom 4) b.noun) := by + have := (DenP_terminal_iff ht4).1 hD4; simpa only [noun] using this + subst hfv + rw [EvP_op4] at hEvinner + obtain ⟨y, hEvy, hlusy⟩ := hEvinner + rw [EvP_op4] at hEvy + obtain ⟨y2, hEvy2, hlusy2⟩ := hEvy + have hy2 : y2 = sbv := EvP_det hEvy2 hEvb + subst hy2 + -- Compute each branch. + rcases hbr with ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩ + · -- sbv = 0 : y = 1, ax2 = 2, ax = 2, aa = slot 2 c.noun = thn.noun + have hy : y = Noun.atom 1 := by + rw [lus_atom] at hlusy2; exact (Option.some.inj hlusy2).symm + subst hy + have hax2 : ax2 = 2 := by + rw [lus_atom] at hlusy; exact (Noun.atom.inj (Option.some.inj hlusy)).symm + subst hax2 + rw [slot_two] at hslotB + have hax : ax = 2 := (Noun.atom.inj (Option.some.inj hslotB)).symm + subst hax + rw [noun, slot_two] at hslotA + obtain rfl := Option.some.inj hslotA + exact eqBr.le + · -- sbv = 1 : y = 2, ax2 = 3, ax = 3, aa = slot 3 c.noun = els.noun + have hy : y = Noun.atom 2 := by + rw [lus_atom] at hlusy2; exact (Option.some.inj hlusy2).symm + subst hy + have hax2 : ax2 = 3 := by + rw [lus_atom] at hlusy; exact (Noun.atom.inj (Option.some.inj hlusy)).symm + subst hax2 + rw [slot_three] at hslotB + have hax : ax = 3 := (Noun.atom.inj (Option.some.inj hslotB)).symm + subst hax + rw [noun, slot_three] at hslotA + obtain rfl := Option.some.inj hslotA + exact eqBr.le + -- combine + rw [hSFinner] + cases evaluatedZero : denP (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf + 4) b)))) + <;> cases evaluatedOne : denP (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null + (Verb.leaf 4) b)))))) + <;> cases evaluatedTwo : denP (g + 1 + 1) (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null + (Verb.leaf 4) b))))))) + <;> simp only [hdc, h23, evaluatedZero, evaluatedOne, evaluatedTwo] + at hc0le ha0le hrrle ⊢ + <;> omega + -- dispatch on the guard value + cases sbv with + | cell k1 k2 => simp at hvv + | atom av => + match av, hvv with + | 0, hvv => exact hbound thn (Or.inl ⟨rfl, rfl⟩) hvv + | 1, hvv => exact hbound els (Or.inr ⟨rfl, rfl⟩) hvv + | (k + 2), hvv => simp at hvv + +/-! ### Lemma 3 — `cost_local` (strict local decrease at a `*`-redex) -/ + +private theorem paper_composition_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) + (n2 : Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hfL : denP (g + 1) L = some L.noun) (hdL : denP (g + 1 + 1) + L = some L.noun) + (hgn2 : denP g n2 = some n2.noun) (hr : (node Action.star L (node Action.null (leaf 2) n2)).redex + = some rr) + (hvv : evalPaper (g + 1) L.noun ((atom 2).cell n2.noun) = some x) : + vcost (g + 1 + 1) rr < pcost (g + 1 + 1) L.noun (node Action.null (leaf 2) n2).noun := by + cases n2 with + | leaf k => simp [redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + simp only [redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨hg1b, hg1c⟩ := + denP_term_children (editOk_of_terminal hn2t) (denP_succ hgn2) htb htc + have hdb_f1 : denP (g + 1 + 1) b = some b.noun := denP_succ hg1b + have hdc_f1 : denP (g + 1 + 1) c = some c.noun := denP_succ hg1c + simp only [noun, evalPaper] at hvv + cases hb : evalPaper g L.noun b.noun with + | none => rw [hb] at hvv; simp at hvv + | some sb => + cases hc : evalPaper g L.noun c.noun with + | none => rw [hb, hc] at hvv; simp at hvv + | some sc => + rw [hb, hc] at hvv + have hb' : evalPaper (g + 1) L.noun b.noun = some sb := evalPaper_succ hb + have hc' : evalPaper (g + 1) L.noun c.noun = some sc := evalPaper_succ hc + have hd_Lb : denP (g + 1 + 1) (Verb.node .star L b) = some sb := + denP_star_val hfL hg1b hb' + have hd_Lc : denP (g + 1 + 1) (Verb.node .star L c) = some sc := + denP_star_val hfL hg1c hc' + have eq_b : pcost (g + 1 + 1) L.noun b.noun = pcost (g + 1) L.noun b.noun := + pcost_align hb (by omega) (by omega) + have eq_c : pcost (g + 1 + 1) L.noun c.noun = pcost (g + 1) L.noun c.noun := + pcost_align hc (by omega) (by omega) + have eq_sbsc : pcost (g + 1 + 1) sb sc = pcost (g + 1) sb sc := + pcost_align hvv (by omega) (by omega) + simp only [vcost_star_eq, hd_Lb, hd_Lc, + vcost_star_terminal hL htb hdL hdb_f1, + vcost_star_terminal hL htc hdL hdc_f1] + simp only [noun, pcost, hb', hc'] + rw [eq_b, eq_c, eq_sbsc] + omega + +private theorem paper_conditional_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) + (n2 : Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hdL : denP (g + 1 + 1) L = some L.noun) (hgn2 : denP g n2 = + some n2.noun) + (hr : (node Action.star L (node Action.null (leaf 6) n2)).redex = some rr) + (hvv : evalPaper (g + 1) L.noun ((atom 6).cell n2.noun) = some x) : + vcost (g + 1 + 1) rr < pcost (g + 1 + 1) L.noun (node Action.null (leaf 6) n2).noun := by + cases n2 with + | leaf k => simp [redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + have hct : isTerminal c = true := htc + simp only [redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨hgb, hgcv⟩ := + denP_term_children (editOk_of_terminal hn2t) hgn2 htb htc + have hdb_f1 : denP (g + 1 + 1) b = some b.noun := denP_succ (denP_succ hgb) + -- denP F of the terminal `[4 [4 b]]` cell + have h4a : denP (g + 1) (Verb.node .null (Verb.leaf 4) b) + = some (Noun.cell (Noun.atom 4) b.noun) := + denP_null_val (by simp only [denP]) hgb + have h4b : denP (g + 1 + 1) + (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) + = some (Noun.cell (Noun.atom 4) (Noun.cell (Noun.atom 4) b.noun)) := + denP_null_val (by simp only [denP]) h4a + have ht4 : isTerminal + (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) = true := by + simp [isTerminal, Action.isNull, htb] + have ht23 : isTerminal (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) = true := by + simp [isTerminal, Action.isNull] + -- c must be a cell for op6 to fire + cases c with + | leaf ck => exfalso; simp only [noun, evalPaper] at hvv; simp at hvv + | node rc thn els => + have hct2 : isTerminal (Verb.node rc thn els) = true := hct + rw [isTerminal_node] at hct; simp only [Bool.and_eq_true] at hct + obtain ⟨⟨hrc, hthn⟩, htels⟩ := hct + exact op6_decrease hL hct2 hthn htels hgcv hdL h4b ht4 ht23 hvv + +private theorem paper_arm_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) (n2 : + Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hfL : denP (g + 1) L = some L.noun) (hdL : denP (g + 1 + 1) + L = some L.noun) + (hgn2 : denP g n2 = some n2.noun) (hr : (node Action.star L (node Action.null (leaf 9) n2)).redex + = some rr) + (hvv : evalPaper (g + 1) L.noun ((atom 9).cell n2.noun) = some x) : + vcost (g + 1 + 1) rr < pcost (g + 1 + 1) L.noun (node Action.null (leaf 9) n2).noun := by + cases n2 with + | leaf k => simp [redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + have hrn2' : rn2 = Action.null := by cases rn2 <;> simp_all [Action.isNull] + subst hrn2' + simp only [redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨g', rfl⟩ : ∃ g', g = g' + 1 := by + cases g with | zero => simp [denP] at hgn2 | succ n => exact ⟨n, rfl⟩ + obtain ⟨hhb, hhc⟩ := denP_term_split htb htc hgn2 + cases b with + | node bb bc bd => simp [noun, evalPaper] at hvv + | leaf ax0 => + simp only [noun, evalPaper] at hvv + cases hcor : evalPaper (g' + 1) L.noun c.noun with + | none => simp [hcor] at hvv + | some cor => + simp only [hcor] at hvv + cases harm : Noun.slot ax0 cor with + | none => simp [harm] at hvv + | some arm => + simp only [harm] at hvv + have hg2c : denP (g' + 1 + 1) c = some c.noun := denP_succ (denP_succ hhc) + have hdc_f1 : denP (g' + 1 + 1 + 1) c = some c.noun := denP_succ hg2c + have hcor2 : evalPaper (g' + 2) L.noun c.noun = some cor := + evalPaper_succ hcor + have hd_core : denP (g' + 1 + 1 + 1) (Verb.node .star L c) = some cor := + denP_star_val hfL hg2c hcor2 + have hb0 : denP g' (Verb.leaf 0) = some (Noun.atom 0) := by simp only [denP] + have hb1 : denP g' (Verb.leaf 1) = some (Noun.atom 1) := by simp only [denP] + have hf0h : denP (g' + 1) (Verb.node .null (Verb.leaf 0) (Verb.leaf ax0)) + = some (Noun.cell (Noun.atom 0) (Noun.atom ax0)) := denP_null_val hb0 hhb + have hf01 : denP (g' + 1) (Verb.node .null (Verb.leaf 0) (Verb.leaf 1)) + = some (Noun.cell (Noun.atom 0) (Noun.atom 1)) := denP_null_val hb0 hb1 + have hM : denP (g' + 1 + 1) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 1)) + (Verb.node .null (Verb.leaf 0) (Verb.leaf ax0))) + = some (Noun.cell (Noun.cell (Noun.atom 0) (Noun.atom 1)) + (Noun.cell (Noun.atom 0) (Noun.atom ax0))) := denP_null_val hf01 hf0h + have h2 : denP (g' + 1 + 1) (Verb.leaf 2) = some (Noun.atom 2) := by + simp only [denP] + have hform : denP (g' + 1 + 1 + 1) + (Verb.node .null (Verb.leaf 2) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 1)) + (Verb.node .null (Verb.leaf 0) (Verb.leaf ax0)))) + = some (Noun.cell (Noun.atom 2) + (Noun.cell (Noun.cell (Noun.atom 0) (Noun.atom 1)) + (Noun.cell (Noun.atom 0) (Noun.atom ax0)))) := denP_null_val h2 hM + have hformT : isTerminal + (Verb.node .null (Verb.leaf 2) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 1)) + (Verb.node .null (Verb.leaf 0) (Verb.leaf ax0)))) = true := by + simp [isTerminal, Action.isNull] + have hev01 : evalPaper (g' + 2) cor + (Noun.cell (Noun.atom 0) (Noun.atom 1)) = some cor := by + simp only [evalPaper]; exact slot_one + have hev0h : evalPaper (g' + 2) cor + (Noun.cell (Noun.atom 0) (Noun.atom ax0)) = some arm := by + simp only [evalPaper]; exact harm + have eq_c1 : pcost (g' + 1 + 1 + 1) L.noun c.noun + = pcost (g' + 2) L.noun c.noun := pcost_align hcor (by omega) (by omega) + have eq_c2 : pcost (g' + 3) L.noun c.noun + = pcost (g' + 2) L.noun c.noun := pcost_align hcor (by omega) (by omega) + have hRHS : pcost (g' + 1 + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 9) (Verb.node .null (Verb.leaf ax0) c)).noun + = 20 + pcost (g' + 2) L.noun c.noun + pcost (g' + 2) cor arm := by + simp only [noun, pcost, hcor2, harm] + rw [hRHS] + simp only [vcost_star_eq, hd_core, hform, + vcost_star_terminal hL htc hdL hdc_f1, vcost_terminal _ hformT] + simp only [pcost, hev01, hev0h] + change 1 + 2 + 2 + pcost (g' + 2) cor arm + + pcost (g' + 1 + 1 + 1) L.noun c.noun + 0 + < 20 + pcost (g' + 2) L.noun c.noun + pcost (g' + 2) cor arm + rw [eq_c1] + omega + +private theorem paper_edit_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) (n2 : + Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hdL : denP (g + 1 + 1) L = some L.noun) (hgn2 : denP g n2 = + some n2.noun) + (hr : (node Action.star L (node Action.null (leaf 10) n2)).redex = some rr) + (hvv : evalPaper (g + 1) L.noun ((atom 10).cell n2.noun) = some x) : + vcost (g + 1 + 1) rr < pcost (g + 1 + 1) L.noun (node Action.null (leaf 10) n2).noun := by + cases n2 with + | leaf k => simp [redex] at hr + | node rn2 hd d => + cases hd with + | leaf k => simp [redex] at hr + | node rhd axv c => + have hn2t : isTerminal (Verb.node rn2 (Verb.node rhd axv c) d) = true := htn2 + rw [isTerminal_node] at htn2; simp only [Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, hthd⟩, htd⟩ := htn2 + have hhdt : isTerminal (Verb.node rhd axv c) = true := hthd + rw [isTerminal_node] at hthd; simp only [Bool.and_eq_true] at hthd + obtain ⟨⟨hrhd, htaxv⟩, htc⟩ := hthd + simp only [redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨hg1hd, hg1d⟩ := + denP_term_children (editOk_of_terminal hn2t) (denP_succ hgn2) hhdt htd + obtain ⟨hg1axv, hg1c⟩ := + denP_term_children (editOk_of_terminal hhdt) hg1hd htaxv htc + have hdc_f1 : denP (g + 1 + 1) c = some c.noun := denP_succ hg1c + have hdd_f1 : denP (g + 1 + 1) d = some d.noun := denP_succ hg1d + cases axv with + | node b0 c0 d0 => simp [noun, evalPaper] at hvv + | leaf a0 => + simp only [noun, evalPaper] at hvv + cases hnew : evalPaper g L.noun c.noun with + | none => rw [hnew] at hvv; simp at hvv + | some new => + cases hold : evalPaper g L.noun d.noun with + | none => rw [hnew, hold] at hvv; simp at hvv + | some old => + have eq_c : pcost (g + 1 + 1) L.noun c.noun + = pcost (g + 1) L.noun c.noun := pcost_align hnew (by omega) (by omega) + have eq_d : pcost (g + 1 + 1) L.noun d.noun + = pcost (g + 1) L.noun d.noun := pcost_align hold (by omega) (by omega) + rw [vcost_edit_eq, vcost_null_eq, + vcost_star_terminal hL htc hdL hdc_f1, + vcost_star_terminal hL htd hdL hdd_f1] + simp only [vcost, noun, pcost] + rw [eq_c, eq_d]; omega + +private theorem paper_equality_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) (n2 + : Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hdL : denP (g + 1 + 1) L = some L.noun) (hgn2 : denP g n2 = + some n2.noun) + (hr : (node Action.star L (node Action.null (leaf 5) n2)).redex = some rr) + (hvv : evalPaper (g + 1) L.noun ((atom 5).cell n2.noun) = some x) : + vcost (g + 1 + 1) rr < pcost (g + 1 + 1) L.noun (node Action.null (leaf 5) n2).noun := by + cases n2 with + | leaf k => simp [redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + simp only [redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨hg1b, hg1c⟩ := + denP_term_children (editOk_of_terminal hn2t) (denP_succ hgn2) htb htc + have hdb_f1 : denP (g + 1 + 1) b = some b.noun := denP_succ hg1b + have hdc_f1 : denP (g + 1 + 1) c = some c.noun := denP_succ hg1c + simp only [noun, evalPaper] at hvv + cases hb : evalPaper g L.noun b.noun with + | none => rw [hb] at hvv; simp at hvv + | some sb => + cases hc : evalPaper g L.noun c.noun with + | none => rw [hb, hc] at hvv; simp at hvv + | some sc => + have eq_b : pcost (g + 1 + 1) L.noun b.noun = pcost (g + 1) L.noun b.noun := + pcost_align hb (by omega) (by omega) + have eq_c : pcost (g + 1 + 1) L.noun c.noun = pcost (g + 1) L.noun c.noun := + pcost_align hc (by omega) (by omega) + rw [vcost_equal_eq, vcost_star_terminal hL htb hdL hdb_f1, + vcost_star_terminal hL htc hdL hdc_f1] + simp only [noun, pcost] + rw [eq_b, eq_c]; omega + +private theorem paper_push_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) (n2 : + Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hfL : denP (g + 1) L = some L.noun) (hdL : denP (g + 1 + 1) + L = some L.noun) + (hgn2 : denP g n2 = some n2.noun) (hr : (node Action.star L (node Action.null (leaf 8) n2)).redex + = some rr) + (hvv : evalPaper (g + 1) L.noun ((atom 8).cell n2.noun) = some x) : + vcost (g + 1 + 1) rr < pcost (g + 1 + 1) L.noun (node Action.null (leaf 8) n2).noun := by + cases n2 with + | leaf k => simp [redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + simp only [redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨hgb, hgc⟩ := + denP_term_children (editOk_of_terminal hn2t) hgn2 htb htc + have hg1b : denP (g + 1) b = some b.noun := denP_succ hgb + have hdb_f1 : denP (g + 1 + 1) b = some b.noun := denP_succ hg1b + have hg1c : denP (g + 1) c = some c.noun := denP_succ hgc + have hdc_f1 : denP (g + 1 + 1) c = some c.noun := denP_succ hg1c + simp only [noun, evalPaper] at hvv + cases hb : evalPaper g L.noun b.noun with + | none => rw [hb] at hvv; simp at hvv + | some sb => + rw [hb] at hvv + have hb' : evalPaper (g + 1) L.noun b.noun = some sb := evalPaper_succ hb + have eq_Lb : pcost (g + 1 + 1) L.noun b.noun = pcost (g + 1) L.noun b.noun + := + pcost_align hb (by omega) (by omega) + have eq_sbLc : pcost (g + 1 + 1) (Noun.cell sb L.noun) c.noun + = pcost (g + 1) (Noun.cell sb L.noun) c.noun := pcost_align hvv (by + omega) (by omega) + have hRHS : pcost (g + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 8) (Verb.node rn2 b c)).noun + = 1 + pcost (g + 1) L.noun b.noun + + pcost (g + 1) (Noun.cell sb L.noun) c.noun := by + simp only [noun, pcost, hb'] + rw [hRHS] + have hvcnull : vcost (g + 1 + 1) (Verb.node .null (Verb.node .star L b) L) + = pcost (g + 1 + 1) L.noun b.noun := by + rw [vcost_null_eq, vcost_star_terminal hL htb hdL hdb_f1, vcost_terminal L + hL, + Nat.add_zero] + cases hgL : denP g L with + | none => + have hnone : denP (g + 1) (Verb.node .star L b) = none := by + simp only [denP, hgL] + have hsubjn : denP (g + 1 + 1) (Verb.node .null (Verb.node .star L b) L) + = none := + denP_null_none hnone + rw [vcost_star_eq] + simp only [hsubjn, hvcnull, vcost_terminal c htc] + rw [eq_Lb] + omega + | some vL => + have hvL : vL = L.noun := denP_terminal_val hL hgL + subst hvL + have hd_Lb_g : denP (g + 1) (Verb.node .star L b) = some sb := + denP_star_val hgL hgb hb + have hd_subj : denP (g + 1 + 1) (Verb.node .null (Verb.node .star L b) + L) + = some (Noun.cell sb L.noun) := denP_null_val hd_Lb_g hfL + rw [vcost_star_eq] + simp only [hd_subj, hdc_f1, hvcnull, vcost_terminal c htc] + rw [eq_Lb, eq_sbLc] + omega + +theorem cost_local {F : Nat} {L R rr : Verb} {x : Noun} + (hL : isTerminal L = true) (hR : isTerminal R = true) + (hv : denP F (Verb.node .star L R) = some x) + (hr : reduce (Verb.node .star L R) = some rr) : + vcost F rr < vcost F (Verb.node .star L R) := by + obtain ⟨f, rfl⟩ : ∃ f, F = f + 1 := by + cases F with + | zero => simp [denP] at hv + | succ n => exact ⟨n, rfl⟩ + have hvv := hv + simp only [denP] at hvv + cases hfL : denP f L with + | none => rw [hfL] at hvv; simp at hvv + | some sL => + cases hfR : denP f R with + | none => rw [hfL, hfR] at hvv; simp at hvv + | some sR => + rw [hfL, hfR] at hvv + have hsL : sL = L.noun := denP_terminal_val hL hfL + have hsR : sR = R.noun := denP_terminal_val hR hfR + subst hsL; subst hsR + have hdL : denP (f + 1) L = some L.noun := denP_succ hfL + have hvcL : vcost (f + 1) L = 0 := vcost_terminal L hL (f + 1) + have hvcR : vcost (f + 1) R = 0 := vcost_terminal R hR (f + 1) + have hstar : vcost (f + 1) (Verb.node .star L R) = pcost (f + 1) L.noun R.noun := by + simp only [vcost, hdL, denP_succ hfR, hvcL, hvcR, Nat.add_zero] + rw [hstar] + cases R with + | leaf k => simp [reduce, redex] at hr + | node ra i n2 => + have hRt : isTerminal (Verb.node ra i n2) = true := hR + simp only [isTerminal, Bool.and_eq_true] at hR + obtain ⟨⟨hra, hti⟩, htn2⟩ := hR + have hra0 : ra = Action.null := by cases ra <;> simp_all [Action.isNull] + subst hra0 + obtain ⟨g, rfl⟩ : ∃ g, f = g + 1 := by + cases f with + | zero => simp [denP] at hfR + | succ n => exact ⟨n, rfl⟩ + obtain ⟨hgi, hgn2⟩ := denP_term_split hti htn2 hfR + rw [reduce] at hr + have hdi_f1 : denP (g + 1 + 1) i = some i.noun := denP_succ (denP_succ hgi) + have hdn2_f1 : denP (g + 1 + 1) n2 = some n2.noun := denP_succ (denP_succ hgn2) + have hvci : vcost (g + 1 + 1) i = 0 := vcost_terminal i hti _ + have hvcn2 : vcost (g + 1 + 1) n2 = 0 := vcost_terminal n2 htn2 _ + cases i with + | node bi ic id => + simp only [redex] at hr + obtain rfl := Option.some.inj hr + simp only [noun, evalPaper] at hvv + cases he1 : evalPaper g L.noun (Noun.cell ic.noun id.noun) with + | none => rw [he1] at hvv; simp at hvv + | some l' => + cases he2 : evalPaper g L.noun n2.noun with + | none => rw [he1, he2] at hvv; simp at hvv + | some r' => + have e_i : pcost (g + 1 + 1) L.noun (Noun.cell ic.noun id.noun) + = pcost (g + 1) L.noun (Noun.cell ic.noun id.noun) := + pcost_align he1 (by omega) (by omega) + have e_n2 : pcost (g + 1 + 1) L.noun n2.noun = pcost (g + 1) L.noun n2.noun := + pcost_align he2 (by omega) (by omega) + rw [vcost_null_eq, + vcost_star_terminal hL hti hdL hdi_f1, + vcost_star_terminal hL htn2 hdL hdn2_f1] + simp only [noun, pcost] + rw [e_i, e_n2] + omega + | leaf op => + have hop : op < 11 := by + rcases Nat.lt_or_ge op 11 with h | h + · exact h + · exfalso + simp only [noun] at hvv + rw [evalPaper_op_ge11 (g + 1) L.noun n2.noun op h] at hvv + simp at hvv + simp only [noun] at hvv + interval_cases op + · -- OP0 + simp only [redex] at hr; obtain rfl := Option.some.inj hr + rw [vcost_slot_eq, vcost_terminal n2 htn2, vcost_terminal L hL] + simp only [noun, pcost]; omega + · -- OP1 + simp only [redex] at hr; obtain rfl := Option.some.inj hr + rw [vcost_terminal n2 htn2] + simp only [noun, pcost]; omega + · -- OP2 + exact paper_composition_cost_decrease hL n2 htn2 g hfL hdL hgn2 hr hvv + · -- OP3 + simp only [redex] at hr; obtain rfl := Option.some.inj hr + simp only [evalPaper, Option.map_eq_some_iff] at hvv + obtain ⟨y, hy, _⟩ := hvv + have e_n2 : pcost (g + 1 + 1) L.noun n2.noun = pcost (g + 1) L.noun n2.noun := + pcost_align hy (by omega) (by omega) + rw [vcost_minus_eq, vcost_star_terminal hL htn2 hdL hdn2_f1] + simp only [vcost, noun, pcost] + rw [e_n2]; omega + · -- OP4 + simp only [redex] at hr; obtain rfl := Option.some.inj hr + simp only [evalPaper, Option.bind_eq_some_iff] at hvv + obtain ⟨y, hy, _⟩ := hvv + have e_n2 : pcost (g + 1 + 1) L.noun n2.noun = pcost (g + 1) L.noun n2.noun := + pcost_align hy (by omega) (by omega) + rw [vcost_minus_eq, vcost_star_terminal hL htn2 hdL hdn2_f1] + simp only [vcost, noun, pcost] + rw [e_n2]; omega + · -- OP5 + exact paper_equality_cost_decrease hL n2 htn2 g hdL hgn2 hr hvv + · -- OP6 + exact paper_conditional_cost_decrease hL n2 htn2 g hdL hgn2 hr hvv + · -- OP7 + cases n2 with + | leaf k => simp [redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + simp only [redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨hg1b, hg1c⟩ := + denP_term_children (editOk_of_terminal hn2t) (denP_succ hgn2) htb htc + have hdb_f1 : denP (g + 1 + 1) b = some b.noun := denP_succ hg1b + have hdc_f1 : denP (g + 1 + 1) c = some c.noun := denP_succ hg1c + simp only [noun, evalPaper] at hvv + cases hb : evalPaper g L.noun b.noun with + | none => rw [hb] at hvv; simp at hvv + | some sb => + rw [hb] at hvv + have hb' : evalPaper (g + 1) L.noun b.noun = some sb := evalPaper_succ hb + have hd_Lb : denP (g + 1 + 1) (Verb.node .star L b) = some sb := + denP_star_val hfL hg1b hb' + have eq_b : pcost (g + 1 + 1) L.noun b.noun = pcost (g + 1) L.noun b.noun := + pcost_align hb (by omega) (by omega) + have eq_sbc : pcost (g + 1 + 1) sb c.noun = pcost (g + 1) sb c.noun := + pcost_align hvv (by omega) (by omega) + simp only [vcost_star_eq, hd_Lb, hdc_f1, + vcost_star_terminal hL htb hdL hdb_f1, vcost_terminal c htc] + simp only [noun, pcost, hb'] + rw [eq_b, eq_sbc]; omega + · -- OP8 + exact paper_push_cost_decrease hL n2 htn2 g hfL hdL hgn2 hr hvv + · -- OP9 + exact paper_arm_cost_decrease hL n2 htn2 g hfL hdL hgn2 hr hvv + · -- OP10 + exact paper_edit_cost_decrease hL n2 htn2 g hdL hgn2 hr hvv +/-! ### Lemma 4 — step congruence and `cost_stepP` -/ + +theorem enum_denP_F {F : Nat} : ∀ (v : Verb), editOk v = true → ∀ (c : Nat) (x : Noun), + denP F v = some x → ∀ p ∈ (enum v c).2, ∃ y, denP F p.2 = some y := by + intro v + induction v with + | leaf k => intro _ c x _ p hp; simp [enum] at hp + | node a l r ihl ihr => + intro hok c x hd p hp + obtain ⟨hokl, hokr⟩ := editOk_children hok + obtain ⟨⟨yl, hyl⟩, ⟨yr, hyr⟩⟩ := denP_children_F hok hd + simp only [enum, List.mem_cons, List.mem_append] at hp + rcases hp with hroot | hleft | hright + · subst hroot; exact ⟨x, hd⟩ + · exact ihl hokl (c + 1) yl hyl p hleft + · exact ihr hokr _ yr hyr p hright + +theorem denP_F_subnode {F : Nat} {v : Verb} {x : Noun} (hok : editOk v = true) + (hv : denP F v = some x) {i : Nat} {sub : Verb} (hmap : map i v = some sub) : + ∃ y, denP F sub = some y := + enum_denP_F v hok 0 x hv (i, sub) (mem_nodes_of_map hmap) + +theorem replaceAux_notMem (i : Nat) (r₀ : Verb) : ∀ (v : Verb) (c : Nat), + findAt i (enum v c).2 = none → (replaceAux i r₀ v c).2 = v := by + intro v + induction v with + | leaf k => intro c _; rfl + | node a l r ihl ihr => + intro c hnone + have hci : i ≠ c := by + intro h; subst h; simp [enum, findAt] at hnone + have hine : ¬ (c = i) := fun h => hci h.symm + simp only [enum, findAt, ite_eq_right (fun h : i = c => hci h)] at hnone + rw [findAt_append] at hnone + have hln : findAt i (enum l (c + 1)).2 = none := by + cases hl : findAt i (enum l (c + 1)).2 with + | none => rfl + | some s => rw [hl] at hnone; simp at hnone + have hrn : findAt i (enum r (enum l (c + 1)).1).2 = none := by + rw [hln] at hnone; exact hnone + have hcL : (replaceAux i r₀ l (c + 1)).1 = (enum l (c + 1)).1 := replaceAux_counter i r₀ l (c + + 1) + simp only [replaceAux, ite_eq_right hine] + rw [ihl (c + 1) hln, hcL, ihr _ hrn] + +theorem denP_F_swap_eq {F : Nat} {w w' : Verb} (hok : editOk w = true) (hrel : RelP w w') + {a b : Noun} (ha : denP F w = some a) (hb : denP F w' = some b) : a = b := by + have hiff := Rel_denP w.noun.size w w' (Nat.le_refl _) hrel hok a + exact denP_det (hiff.1 ⟨F, ha⟩) ⟨F, hb⟩ + +/-- **Step congruence for `vcost`.** Swapping the (denotation-preserving, pending) subnode `sub` + for `r₀` changes `vcost F` by exactly `vcost F r₀ - vcost F sub` (additive form). -/ +theorem vcost_replace_cong (F i : Nat) (sub r₀ : Verb) (hpend : sub.isPending = true) + (hgraph : ∀ y, DenP sub y ↔ DenP r₀ y) (hr0 : editOk r₀ = true) : + ∀ (v : Verb) (c : Nat) (xv xv' : Noun), + editOk v = true → + findAt i (enum v c).2 = some sub → + denP F v = some xv → + denP F (replaceAux i r₀ v c).2 = some xv' → + vcost F (replaceAux i r₀ v c).2 + vcost F sub = vcost F v + vcost F r₀ := by + intro v + induction v with + | leaf k => intro c xv xv' _ hfind; simp [enum, findAt] at hfind + | node a l r ihl ihr => + intro c xv xv' hok hfind hdv hdv' + obtain ⟨hokl, hokr⟩ := editOk_children hok + by_cases hci : c = i + · -- the swapped node itself + subst hci + have hf : findAt c (enum (Verb.node a l r) c).2 = some (Verb.node a l r) := by + simp [enum, findAt] + have hsub : Verb.node a l r = sub := (Option.some.inj (hf.symm.trans hfind)) + have hrepl : (replaceAux c r₀ (Verb.node a l r) c).2 = r₀ := by simp [replaceAux] + rw [hrepl, ← hsub] + omega + · have hine : ¬ (i = c) := fun h => hci h.symm + have hcL : (replaceAux i r₀ l (c + 1)).1 = (enum l (c + 1)).1 := replaceAux_counter i r₀ l + (c + 1) + -- locate i in left/right + have hfind' : findAt i (enum l (c + 1)).2 = some sub ∧ + findAt i (enum r (enum l (c + 1)).1).2 = none + ∨ findAt i (enum l (c + 1)).2 = none ∧ + findAt i (enum r (enum l (c + 1)).1).2 = some sub := by + simp only [enum, findAt, ite_eq_right hine] at hfind + rw [findAt_append] at hfind + cases hl : findAt i (enum l (c + 1)).2 with + | some s => rw [hl] at hfind; exact Or.inl ⟨hfind, by + cases hr : findAt i (enum r (enum l (c + 1)).1).2 with + | none => rfl + | some s2 => + exfalso + have h1 := enum_index_range l (c + 1) (i, s) (findAt_some_mem hl) + have h2 := enum_index_range r (enum l (c + 1)).1 (i, s2) (findAt_some_mem hr) + omega⟩ + | none => rw [hl] at hfind; exact Or.inr ⟨rfl, hfind⟩ + -- denP-definedness of children at F (both v and v') + obtain ⟨⟨xl, hxl⟩, ⟨xr, hxr⟩⟩ := denP_children_F hok hdv + have hok' : editOk (replaceAux i r₀ (Verb.node a l r) c).2 = true := by + apply editOk_replaceAux i r₀ hr0 (Verb.node a l r) c hok + intro s hs; have : s = sub := Option.some.inj (hs.symm.trans hfind); rw [this]; exact + hpend + simp only [replaceAux, ite_eq_right hci] at hdv' hok' ⊢ + rw [hcL] at hdv' hok' ⊢ + obtain ⟨⟨xl', hxl'⟩, ⟨xr', hxr'⟩⟩ := denP_children_F hok' hdv' + -- head-cost preservation + rcases hfind' with ⟨hLsub, hRnone⟩ | ⟨hLnone, hRsub⟩ + · -- i in left + have hrIH := ihl (c + 1) xl xl' hokl hLsub hxl hxl' + -- right unchanged + have hreq : (replaceAux i r₀ r (enum l (c + 1)).1).2 = r := replaceAux_notMem i r₀ r _ + hRnone + rw [hreq] at hxr' ⊢ + have hrelL : RelP l (replaceAux i r₀ l (c + 1)).2 := by + apply replaceAux_relP i r₀ l (c + 1) + intro s hs + have : s = sub := Option.some.inj (hs.symm.trans hLsub) + rw [this]; exact ⟨hpend, hgraph⟩ + have hlEq : xl = xl' := denP_F_swap_eq hokl hrelL hxl hxl' + cases a <;> + simp only [vcost_star_eq, vcost_null_eq, vcost_wut_eq, vcost_slot_eq, vcost_equal_eq, + vcost_minus_eq, vcost_edit_eq, hxl, hxl', hxr, hlEq] <;> omega + · -- i in right + have hrIH := ihr (enum l (c + 1)).1 xr xr' hokr hRsub hxr hxr' + have hleq : (replaceAux i r₀ l (c + 1)).2 = l := replaceAux_notMem i r₀ l _ hLnone + rw [hleq] at hxl' ⊢ + have hrelR : RelP r (replaceAux i r₀ r (enum l (c + 1)).1).2 := by + apply replaceAux_relP i r₀ r (enum l (c + 1)).1 + intro s hs + have : s = sub := Option.some.inj (hs.symm.trans hRsub) + rw [this]; exact ⟨hpend, hgraph⟩ + have hrEq : xr = xr' := denP_F_swap_eq hokr hrelR hxr hxr' + cases a <;> + simp only [vcost_star_eq, vcost_null_eq, vcost_wut_eq, vcost_slot_eq, vcost_equal_eq, + vcost_minus_eq, vcost_edit_eq, hxr, hxr', hxl, hrEq] <;> omega + +/-- **A single machine step strictly decreases `vcost`** (at a common fuel `F`). -/ +theorem cost_stepP {F : Nat} {v v' : Verb} {x x' : Noun} + (hwf : editOk v = true) (hv : denP F v = some x) (hv' : denP F v' = some x') + (h : next v = some v') : vcost F v' < vcost F v := by + obtain ⟨i, sub, r₀, hmax, hmap, hdom, hred, hv'eq⟩ := next_eq_some_iff.1 h + have hgi : getIndex v = some i := getIndex_eq_some_iff.2 hmax + have hoksub : editOk sub = true := editOk_enum v 0 hwf (i, sub) (mem_nodes_of_map hmap) + have hokr0 : editOk r₀ = true := editOk_reduce hred hoksub + obtain ⟨w, hmapw, hpw⟩ := hmax.1 + have hpsub : Verb.isPending sub = true := (Option.some.inj (hmapw.symm.trans hmap)) ▸ hpw + have hgraph : ∀ y, DenP sub y ↔ DenP r₀ y := fun y => den_localP hdom hred + -- denP-definedness of sub at F + obtain ⟨xs, hxs⟩ := denP_F_subnode hwf hv hmap + -- strict local decrease at the redex + have hlocal : vcost F r₀ < vcost F sub := by + cases sub with + | leaf k => simp [isPending] at hpsub + | node a L R => + obtain ⟨hL, hR⟩ := children_terminal_of_getIndex hgi hmap + cases a with + | null => simp [isPending, Action.isNull] at hpsub + | wut => simp [reduce] at hred + | star => exact cost_local hL hR hxs hred + | slot => + have ht0 : isTerminal r₀ = true := by + cases L with + | leaf ax => rw [reduce] at hred; simp only [noun] at hred + exact isTerminal_of_map_ofNoun hred + | node a2 l2 r2 => rw [reduce] at hred; simp only [noun] at hred; simp at hred + rw [vcost_terminal r₀ ht0] + simp only [vcost_slot_eq, vcost_terminal L hL, vcost_terminal R hR]; omega + | equal => + have ht0 : isTerminal r₀ = true := by + rw [reduce] at hred; obtain rfl := Option.some.inj hred; exact ofNoun_terminal _ + rw [vcost_terminal r₀ ht0] + simp only [vcost_equal_eq, vcost_terminal L hL, vcost_terminal R hR]; omega + | minus => + have ht0 : isTerminal r₀ = true := by + cases L with + | leaf ax => + rw [reduce] at hred; simp only [noun] at hred + split at hred + · obtain rfl := Option.some.inj hred; exact ofNoun_terminal _ + · exact isTerminal_of_map_ofNoun hred + · simp at hred + | node a2 l2 r2 => rw [reduce] at hred; simp only [noun] at hred; simp at hred + rw [vcost_terminal r₀ ht0] + simp only [vcost_minus_eq, vcost_terminal L hL, vcost_terminal R hR]; omega + | edit => + have ht0 : isTerminal r₀ = true := by + cases L with + | leaf ax => + cases R with + | leaf k => simp [reduce, noun] at hred + | node rb nw od => + simp only [reduce, noun] at hred + exact isTerminal_of_map_ofNoun hred + | node a2 l2 r2 => simp [reduce, noun] at hred + rw [vcost_terminal r₀ ht0] + simp only [vcost_edit_eq, vcost_terminal L hL, vcost_terminal R hR]; omega + -- congruence + have hv'2 : denP F (replaceAt i v r₀) = some x' := hv'eq ▸ hv' + have hcong : vcost F (replaceAt i v r₀) + vcost F sub = vcost F v + vcost F r₀ := + vcost_replace_cong F i sub r₀ hpsub hgraph hokr0 v 0 x x' hwf hmap hv hv'2 + rw [hv'eq] + omega + +/-! ### Lemma 5 — `run_completeP` -/ + +theorem run_completeP {v : Verb} {x : Noun} (hok : editOk v = true) (hv : DenP v x) : + ∃ N, run N v = some x := by + suffices aux : ∀ (bound F : Nat) (v : Verb) (x : Noun), + editOk v = true → denP F v = some x → vcost F v < bound → ∃ N, run N v = some x by + obtain ⟨F, hF⟩ := hv + exact aux (vcost F v + 1) F v x hok hF (Nat.lt_succ_self _) + intro bound + induction bound with + | zero => intro F v x _ _ hlt; omega + | succ bound ih => + intro F v x hok hF hlt + by_cases hterm : isTerminal v = true + · refine ⟨1, ?_⟩ + have hx : x = v.noun := denP_terminal_val hterm hF + subst hx + simp only [run, next_none_of_terminal hterm, result, hterm, ite_true] + · have hntf : isTerminal v = false := by + cases hh : isTerminal v with + | true => exact absurd hh hterm + | false => rfl + obtain ⟨v', hnext⟩ := progressP hok ⟨F, hF⟩ hntf + obtain ⟨F', hF'⟩ := (den_stepP hok hnext).1 ⟨F, hF⟩ + have hokv' : editOk v' = true := editOk_next hnext hok + have hGv : denP (max F F') v = some x := denP_mono (Nat.le_max_left _ _) hF + have hGv' : denP (max F F') v' = some x := denP_mono (Nat.le_max_right _ _) hF' + have hvcv : vcost (max F F') v = vcost F v := vcost_mono (Nat.le_max_left _ _) hok hF + have hvcv' : vcost (max F F') v' = vcost F' v' := vcost_mono (Nat.le_max_right _ _) hokv' + hF' + have hdec : vcost (max F F') v' < vcost (max F F') v := cost_stepP hok hGv hGv' hnext + have hlt' : vcost F' v' < bound := by + rw [hvcv'] at hdec; rw [hvcv] at hdec; omega + obtain ⟨N, hN⟩ := ih F' v' x hokv' hF' hlt' + exact ⟨N + 1, by simp only [run, hnext]; exact hN⟩ + +/-! ### Lemma 6 — `paperComplete_holds` -/ + +theorem paperComplete_holds : PaperComplete := by + intro s f r hev + obtain ⟨g, hg⟩ := (denP_iff_evalPaper s f r).2 hev + obtain ⟨N, hN⟩ := run_completeP (editOk_program s f) ⟨g, hg⟩ + exact ⟨N, hN⟩ + +/-! ### OP₀–OP₁₀ consistency -/ + +-- main.tex:1052-1083 (`i ∈ [11]` = {0,…,10}) +/-- The paper-derived local machine and derived OP₀–OP₁₀ oracle compute the + same partial function. This is a Lean consistency theorem, not a theorem + stated in `main.tex`. -/ +theorem runProgram_adequate_complete (s f r : Noun) : + (∃ fuel, runProgram fuel s f = some r) ↔ (∃ fuel, evalPaper fuel s f = some r) := + runProgram_adequate paperComplete_holds s f r + +end Verb +end Nock diff --git a/LeanPool/PureNock/PureCompleteHint.lean b/LeanPool/PureNock/PureCompleteHint.lean new file mode 100644 index 0000000000..4640b568ec --- /dev/null +++ b/LeanPool/PureNock/PureCompleteHint.lean @@ -0,0 +1,1645 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.PureComplete +public import Mathlib.Tactic.IntervalCases + +/-! +# Completeness for the OP₁₁ extension + +Mirrors the OP₀–OP₁₀ completeness proof for `nextHint` and `evalN`. +`vcostN` accounts for hint expansion; `runHint_adequate_complete` closes it. +-/ + +@[expose] public section + +namespace Nock +namespace Verb + +open Noun + +/-- Arithmetic identity for the OP₁₁ dynamic-macro cost accounting (kept top-level so `omega` + runs in a clean context). -/ +theorem op11_arith (A B : Nat) : 1 + A + (1 + 2 + B) = 4 + A + B := by omega + +/-! ### Small machine / evalN helpers -/ + +theorem evalN_atom_none (f : Nat) (s : Noun) (k : Nat) : evalN f s (.atom k) = none := by + cases f <;> simp [evalN] + +theorem evalN_op_ge12_none (f : Nat) (s tail : Noun) (op : Nat) (h : 12 ≤ op) : + evalN f s (.cell (.atom op) tail) = none := by + cases f with + | zero => rfl + | succ ff => + simp only [evalN] + split <;> first | rfl | omega + +theorem inDomainHint_of_inDomain {v : Verb} (h : inDomain v = true) : inDomainHint v = true := by + simp only [inDomainHint, h, Bool.true_or] + +theorem nextHint_none_of_terminal {v : Verb} (h : isTerminal v = true) : nextHint v = none := by + unfold nextHint; rw [getIndex_none_of_terminal h] + +theorem redexHint_eq_reduce_of_hintExpand_none {v : Verb} (h : hintExpand v = none) : + redexHint v = reduce v := by + unfold redexHint; rw [h] + +theorem EvN_det {a b y1 y2 : Noun} (h1 : Ev a b y1) (h2 : Ev a b y2) : y1 = y2 := by + obtain ⟨f1, h1⟩ := h1; obtain ⟨f2, h2⟩ := h2 + exact Option.some.inj ((evalN_mono (Nat.le_max_left f1 f2) h1).symm.trans + (evalN_mono (Nat.le_max_right f1 f2) h2)) + +/-! ### (b) `den`-definedness descent -/ + +theorem den_left_defined {a : Action} {l r : Verb} {f : Nat} {x : Noun} + (h : den (f + 1) (Verb.node a l r) = some x) : ∃ y, den f l = some y := by + cases hl : den f l with + | some y => exact ⟨y, rfl⟩ + | none => + exfalso + cases a with + | edit => + cases r with + | leaf k => simp [den] at h + | node rb nw od => simp [den, hl] at h + | wut => simp [den] at h + | _ => simp [den, hl] at h + +theorem den_right_defined_of_editOk {a : Action} {l r : Verb} {f : Nat} {x : Noun} + (hok : editOk (Verb.node a l r) = true) (h : den (f + 1) (Verb.node a l r) = some x) : + ∃ g y, den g r = some y := by + cases a with + | wut => simp [den] at h + | edit => + have hrp : Verb.isPending r = false := editOk_edit_cond hok + cases r with + | leaf k => simp [den] at h + | node rb nw od => + have hrb : rb = Action.null := by + simp only [isPending] at hrp + cases rb <;> simp_all [Action.isNull] + subst hrb + simp only [den] at h + cases hnw : den f nw with + | none => rw [hnw] at h; simp at h + | some n => + cases hod : den f od with + | none => rw [hnw, hod] at h; simp at h + | some o => exact ⟨f + 1, .cell n o, by simp [den, hnw, hod]⟩ + | null => cases hr : den f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [den, hr] + at h + | star => cases hr : den f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [den, hr] + at h + | slot => cases hr : den f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [den, hr] + at h + | equal => cases hr : den f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [den, hr] + at h + | minus => cases hr : den f r with | some y => exact ⟨f, y, hr⟩ | none => exfalso; simp [den, hr] + at h + +theorem enum_den_defined : + ∀ (v : Verb), editOk v = true → ∀ (c f : Nat) (x : Noun), + den f v = some x → ∀ p ∈ (enum v c).2, ∃ g y, den g p.2 = some y := by + intro v + induction v with + | leaf k => intro _ c f x _ p hp; simp [enum] at hp + | node a l r ihl ihr => + intro hok c f x hden p hp + obtain ⟨f, rfl⟩ : ∃ f', f = f' + 1 := by + cases f with + | zero => simp [den] at hden + | succ n => exact ⟨n, rfl⟩ + have hokl := (editOk_children hok).1 + have hokr := (editOk_children hok).2 + simp only [enum, List.mem_cons, List.mem_append] at hp + rcases hp with hroot | hleft | hright + · subst hroot; exact ⟨f + 1, x, hden⟩ + · obtain ⟨yl, hyl⟩ := den_left_defined hden + exact ihl hokl (c + 1) f yl hyl p hleft + · obtain ⟨g, yr, hyr⟩ := den_right_defined_of_editOk hok hden + exact ihr hokr _ g yr hyr p hright + +theorem Den_subnode {v : Verb} {x : Noun} (hok : editOk v = true) (hv : Den v x) + {i : Nat} {sub : Verb} (hmap : map i v = some sub) : ∃ y, Den sub y := by + obtain ⟨f, hf⟩ := hv + have hmem : (i, sub) ∈ nodes v := mem_nodes_of_map hmap + obtain ⟨g, y, hg⟩ := enum_den_defined v hok 0 f x hf (i, sub) hmem + exact ⟨y, g, hg⟩ + +/-! ### `den`-definedness helpers at a FIXED fuel -/ + +theorem den_right_F {a : Action} {l r : Verb} {f : Nat} {x : Noun} + (hok : editOk (Verb.node a l r) = true) (h : den (f + 1) (Verb.node a l r) = some x) : + ∃ yr, den (f + 1) r = some yr := by + cases a with + | wut => simp [den] at h + | edit => + have hrp : Verb.isPending r = false := editOk_edit_cond hok + cases r with + | leaf k => simp [den] at h + | node rb nw od => + have hrb : rb = Action.null := by + simp only [isPending] at hrp + cases rb <;> simp_all [Action.isNull] + subst hrb + simp only [den] at h + cases hnw : den f nw with + | none => rw [hnw] at h; simp at h + | some n => + cases hod : den f od with + | none => rw [hnw, hod] at h; simp at h + | some o => exact ⟨.cell n o, by simp [den, hnw, hod]⟩ + | null => + cases hr : den f r with + | some y => exact ⟨y, den_succ hr⟩ + | none => simp [den, hr] at h + | star => + cases hr : den f r with + | some y => exact ⟨y, den_succ hr⟩ + | none => simp [den, hr] at h + | slot => + cases hr : den f r with + | some y => exact ⟨y, den_succ hr⟩ + | none => simp [den, hr] at h + | equal => + cases hr : den f r with + | some y => exact ⟨y, den_succ hr⟩ + | none => simp [den, hr] at h + | minus => + cases hr : den f r with + | some y => exact ⟨y, den_succ hr⟩ + | none => simp [den, hr] at h + +theorem den_children_F {a : Action} {l r : Verb} {F : Nat} {x : Noun} + (hok : editOk (Verb.node a l r) = true) (h : den F (Verb.node a l r) = some x) : + (∃ yl, den F l = some yl) ∧ (∃ yr, den F r = some yr) := by + obtain ⟨f, rfl⟩ : ∃ f, F = f + 1 := by + cases F with + | zero => simp [den] at h + | succ n => exact ⟨n, rfl⟩ + refine ⟨?_, den_right_F hok h⟩ + obtain ⟨yl, hyl⟩ := den_left_defined h + exact ⟨yl, den_succ hyl⟩ + +theorem den_terminal_val {v : Verb} {F : Nat} {y : Noun} + (ht : isTerminal v = true) (h : den F v = some y) : y = v.noun := + (Den_terminal_iff ht).1 ⟨F, h⟩ + +theorem den_term_children {a : Action} {l r : Verb} {F : Nat} {x : Noun} + (hok : editOk (Verb.node a l r) = true) (h : den F (Verb.node a l r) = some x) + (htl : isTerminal l = true) (htr : isTerminal r = true) : + den F l = some l.noun ∧ den F r = some r.noun := by + obtain ⟨⟨yl, hyl⟩, ⟨yr, hyr⟩⟩ := den_children_F hok h + rw [den_terminal_val htl hyl] at hyl + rw [den_terminal_val htr hyr] at hyr + exact ⟨hyl, hyr⟩ + +theorem den_term_split {l r : Verb} {f : Nat} {x : Noun} + (htl : isTerminal l = true) (htr : isTerminal r = true) + (h : den (f + 1) (Verb.node .null l r) = some x) : + den f l = some l.noun ∧ den f r = some r.noun := by + simp only [den] at h + cases hl : den f l with + | none => rw [hl] at h; simp at h + | some yl => + cases hr : den f r with + | none => rw [hl, hr] at h; simp at h + | some yr => + refine ⟨?_, ?_⟩ + · rw [den_terminal_val htl hl] + · rw [den_terminal_val htr hr] + +theorem den_star_val {A B : Verb} {f : Nat} {a b w : Noun} + (hA : den f A = some a) (hB : den f B = some b) (he : evalN f a b = some w) : + den (f + 1) (Verb.node .star A B) = some w := by + simp only [den, hA, hB]; exact he + +theorem den_null_val {A B : Verb} {f : Nat} {a b : Noun} + (hA : den f A = some a) (hB : den f B = some b) : + den (f + 1) (Verb.node .null A B) = some (Noun.cell a b) := by + simp only [den, hA, hB] + +theorem den_null_none {A B : Verb} {f : Nat} (hA : den f A = none) : + den (f + 1) (Verb.node .null A B) = none := by + simp only [den, hA] + +theorem den_null_leaf0 {inner : Verb} {f : Nat} {dv : Noun} + (h : den (f + 1) (Verb.node .null (Verb.leaf 0) inner) = some dv) : + ∃ w, dv = Noun.cell (Noun.atom 0) w := by + simp only [den] at h + cases hi : den f inner with + | none => rw [hi] at h; simp at h + | some iv => rw [hi] at h; exact ⟨iv, (Option.some.inj h).symm⟩ + +/-! ### (d) The cost measure over `evalN`/`den` -/ + +/-- Weighted `evalN`-derivation cost: mirrors `evalN` clause-for-clause (same recursive calls), + each opcode contributing a positive weight (identical to `pcost`, plus an OP₁₁ clause with + weight `50`). -/ +def ncost : Nat → Noun → Noun → Nat + | 0, _, _ => 0 + | _ + 1, _, .atom _ => 0 + | f + 1, subj, .cell (.cell b c) d => + 1 + ncost f subj (.cell b c) + ncost f subj d + | f + 1, subj, .cell (.atom op) tail => + match op with + | 0 => 2 + | 1 => 1 + | 2 => + match tail with + | .cell b c => + let inner := match evalN f subj b, evalN f subj c with + | some sb, some sc => ncost f sb sc + | _, _ => 0 + 1 + ncost f subj b + ncost f subj c + inner + | _ => 0 + | 3 => 2 + ncost f subj tail + | 4 => 2 + ncost f subj tail + | 5 => + match tail with + | .cell b c => 2 + ncost f subj b + ncost f subj c + | _ => 0 + | 6 => + match tail with + | .cell b (.cell c d) => + let inner := match evalN f subj b with + | some (.atom 0) => ncost f subj c + | some (.atom 1) => ncost f subj d + | _ => 0 + 100 + ncost f subj b + inner + | _ => 0 + | 7 => + match tail with + | .cell b c => + let inner := match evalN f subj b with + | some sb => ncost f sb c + | _ => 0 + 1 + ncost f subj b + inner + | _ => 0 + | 8 => + match tail with + | .cell b c => + let inner := match evalN f subj b with + | some sb => ncost f (.cell sb subj) c + | _ => 0 + 1 + ncost f subj b + inner + | _ => 0 + | 9 => + match tail with + | .cell (.atom ax) c => + let inner := match evalN f subj c with + | some core => + match Noun.slot ax core with + | some arm => ncost f core arm + | _ => 0 + | _ => 0 + 20 + ncost f subj c + inner + | _ => 0 + | 10 => + match tail with + | .cell (.cell (.atom _) c) d => 2 + ncost f subj c + ncost f subj d + | _ => 0 + | 11 => + match tail with + | .cell (.cell _ clue) body => + let inner := match evalN f subj clue with + | some _ => ncost f subj body + | _ => 0 + 50 + ncost f subj clue + inner + | .cell (.atom _) body => 50 + ncost f subj body + | _ => 0 + | _ => 0 + +/-- Verb cost keyed to `den`/`evalN` (structural in the verb; `F` a fixed lookup-fuel). -/ +def vcostN (F : Nat) : Verb → Nat + | .leaf _ => 0 + | .node a l r => + (match a with + | .star => match den F l, den F r with + | some sa, some fo => ncost F sa fo + | _, _ => 0 + | .slot => 1 + | .equal => 1 + | .minus => 1 + | .edit => 1 + | .null => 0 + | .wut => 0) + vcostN F l + vcostN F r + +theorem ncost_succ {n : Nat} {a b : Noun} {r : Noun} + (hb : evalN n a b = some r) : ncost (n + 1) a b = ncost n a b := by + induction n, a, b using evalN.induct generalizing r <;> + simp_all only [evalN, ncost, Option.map_eq_some_iff, Option.bind_eq_some_iff, + Option.some.injEq, reduceCtorEq] <;> + grind only [evalN_succ] + +theorem ncost_mono {n m : Nat} {a b r : Noun} (hle : n ≤ m) (hb : evalN n a b = some r) : + ncost m a b = ncost n a b := by + induction hle with + | refl => rfl + | step hk ih => rw [ncost_succ (evalN_mono hk hb)]; exact ih + +theorem ncost_align {n m k : Nat} {a b r : Noun} (h : evalN n a b = some r) + (hm : n ≤ m) (hk : n ≤ k) : ncost m a b = ncost k a b := + (ncost_mono hm h).trans (ncost_mono hk h).symm + +theorem vcostN_star_eq {F : Nat} {l r : Verb} : vcostN F (Verb.node .star l r) = + (match den F l, den F r with | some sa, some fo => ncost F sa fo | _, _ => 0) + + vcostN F l + vcostN F r := rfl +theorem vcostN_slot_eq {F : Nat} {l r : Verb} : + vcostN F (Verb.node .slot l r) = 1 + vcostN F l + vcostN F r := rfl +theorem vcostN_equal_eq {F : Nat} {l r : Verb} : + vcostN F (Verb.node .equal l r) = 1 + vcostN F l + vcostN F r := rfl +theorem vcostN_minus_eq {F : Nat} {l r : Verb} : + vcostN F (Verb.node .minus l r) = 1 + vcostN F l + vcostN F r := rfl +theorem vcostN_edit_eq {F : Nat} {l r : Verb} : + vcostN F (Verb.node .edit l r) = 1 + vcostN F l + vcostN F r := rfl +theorem vcostN_null_eq {F : Nat} {l r : Verb} : + vcostN F (Verb.node .null l r) = vcostN F l + vcostN F r := by + simp only [vcostN]; omega +theorem vcostN_wut_eq {F : Nat} {l r : Verb} : + vcostN F (Verb.node .wut l r) = vcostN F l + vcostN F r := by + simp only [vcostN]; omega + +theorem vcostN_terminal : ∀ (v : Verb), isTerminal v = true → ∀ F, vcostN F v = 0 := by + intro v + induction v with + | leaf k => intro _ F; rfl + | node a l r ihl ihr => + intro hterm F + simp only [isTerminal, Bool.and_eq_true] at hterm + obtain ⟨⟨hnull, hl⟩, hr⟩ := hterm + have ha : a = Action.null := by cases a <;> simp_all [Action.isNull] + subst ha + simp only [vcostN]; rw [ihl hl F, ihr hr F] + +theorem vcostN_star_terminal {F : Nat} {A B : Verb} + (hA : isTerminal A = true) (hB : isTerminal B = true) + (hdA : den F A = some A.noun) (hdB : den F B = some B.noun) : + vcostN F (Verb.node .star A B) = ncost F A.noun B.noun := by + simp only [vcostN_star_eq, hdA, hdB, vcostN_terminal A hA F, vcostN_terminal B hB F, Nat.add_zero] + +theorem vcostN_succ {F : Nat} {v : Verb} {x : Noun} + (hok : editOk v = true) (hv : den F v = some x) : vcostN (F + 1) v = vcostN F v := by + induction v generalizing F x with + | leaf k => rfl + | node a l r ihl ihr => + obtain ⟨f, rfl⟩ : ∃ f, F = f + 1 := by + cases F with + | zero => simp [den] at hv + | succ n => exact ⟨n, rfl⟩ + obtain ⟨hokl, hokr⟩ := editOk_children hok + obtain ⟨⟨yl, hyl⟩, ⟨yr, hyr⟩⟩ := den_children_F hok hv + have ihl' : vcostN (f + 1 + 1) l = vcostN (f + 1) l := ihl hokl hyl + have ihr' : vcostN (f + 1 + 1) r = vcostN (f + 1) r := ihr hokr hyr + cases a with + | null => simp only [vcostN, ihl', ihr'] + | wut => simp [den] at hv + | slot => simp only [vcostN, ihl', ihr'] + | equal => simp only [vcostN, ihl', ihr'] + | minus => simp only [vcostN, ihl', ihr'] + | edit => simp only [vcostN, ihl', ihr'] + | star => + simp only [den] at hv + cases hfl : den f l with + | none => rw [hfl] at hv; simp at hv + | some sa => + cases hfr : den f r with + | none => rw [hfl, hfr] at hv; simp at hv + | some fo => + rw [hfl, hfr] at hv + have hdl1 : den (f + 1) l = some sa := den_succ hfl + have hdr1 : den (f + 1) r = some fo := den_succ hfr + have hdl2 : den (f + 1 + 1) l = some sa := den_succ hdl1 + have hdr2 : den (f + 1 + 1) r = some fo := den_succ hdr1 + have hev1 : evalN (f + 1) sa fo = some x := evalN_succ hv + have hpc : ncost (f + 1 + 1) sa fo = ncost (f + 1) sa fo := ncost_succ hev1 + simp only [vcostN, hdl1, hdr1, hdl2, hdr2, hpc, ihl', ihr'] + +theorem vcostN_mono {F G : Nat} {v : Verb} {x : Noun} + (hle : F ≤ G) (hok : editOk v = true) (hv : den F v = some x) : vcostN G v = vcostN F v := by + induction hle with + | refl => rfl + | @step m h ih => rw [vcostN_succ hok (den_mono h hv)]; exact ih + +/-! ### OP₆ strict local decrease (ncost) -/ + +theorem op6_decreaseN {g : Nat} {L b thn els : Verb} {x : Noun} {rn2 rc : Action} + (hL : isTerminal L = true) (htc : isTerminal (Verb.node rc thn els) = true) + (hthn : isTerminal thn = true) (htels : isTerminal els = true) + (hgc : den g (Verb.node rc thn els) = some (Verb.node rc thn els).noun) + (hdL : den (g + 1 + 1) L = some L.noun) + (h4b : den (g + 1 + 1) (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) + = some (Noun.cell (Noun.atom 4) (Noun.cell (Noun.atom 4) b.noun))) + (ht4 : isTerminal (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) = true) + (ht23 : isTerminal (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) = true) + (hvv : evalN (g + 1) L.noun + (Noun.cell (Noun.atom 6) (Verb.node rn2 b (Verb.node rc thn els)).noun) = some x) : + vcostN (g + 1 + 1) + (Verb.node .star L + (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L + (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)))))))) + < ncost (g + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 6) (Verb.node rn2 b (Verb.node rc thn els))).noun := by + have hleaf0 : isTerminal (Verb.leaf 0) = true := rfl + have h23 : den (g + 1 + 1) (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + = some (Noun.cell (Noun.atom 2) (Noun.atom 3)) := + den_null_val (by simp only [den]) (by simp only [den]) + have hdc : den (g + 1 + 1) (Verb.node rc thn els) = some (Verb.node rc thn els).noun := + den_succ (den_succ hgc) + simp only [noun, evalN] at hvv + cases hsb : evalN g L.noun b.noun with + | none => rw [hsb] at hvv; simp at hvv + | some sbv => + rw [hsb] at hvv + have hEvb : Ev L.noun b.noun sbv := ⟨g, hsb⟩ + have hsb1 : evalN (g + 1) L.noun b.noun = some sbv := evalN_succ hsb + have eqLb : ncost g L.noun b.noun = ncost (g + 1) L.noun b.noun := + ncost_align hsb (Nat.le_refl g) (by omega) + have hSFinner : ncost (g + 1 + 1) L.noun (Noun.cell (Noun.atom 4) (Noun.cell (Noun.atom 4) + b.noun)) + = 4 + ncost g L.noun b.noun := by simp only [ncost]; omega + have hbound : ∀ (branchNoun : Verb), + (sbv = Noun.atom 0 ∧ branchNoun = thn ∨ sbv = Noun.atom 1 ∧ branchNoun = els) → + evalN g L.noun branchNoun.noun = some x → + vcostN (g + 1 + 1) + (Verb.node .star L + (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L + (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)))))))) + < ncost (g + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 6) (Verb.node rn2 b (Verb.node rc thn els))).noun := by + intro branchNoun hbr hbev + have eqBr : ncost (g + 1 + 1) L.noun branchNoun.noun = ncost (g + 1) L.noun branchNoun.noun := + ncost_align hbev (by omega) (by omega) + have hRHS : ncost (g + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 6) (Verb.node rn2 b (Verb.node rc thn els))).noun + = 100 + ncost (g + 1) L.noun b.noun + ncost (g + 1) L.noun branchNoun.noun := by + rcases hbr with ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩ <;> + simp only [noun, ncost, hsb1] + rw [hRHS] + simp only [vcostN_star_eq, vcostN_null_eq, vcostN_terminal L hL, + vcostN_terminal (Verb.node rc thn els) htc, + vcostN_terminal (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) ht23, + vcostN_terminal (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) ht4, + vcostN_terminal (Verb.leaf 0) hleaf0, hdL, h4b, Nat.add_zero, Nat.zero_add] + have hc0le : (match den (g + 1 + 1) (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)), + den (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)))) + with + | some sa, some fo => ncost (g + 1 + 1) sa fo | _, _ => 0) ≤ 2 := by + rw [h23] + cases hd0 : den (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)))) + with + | none => simp + | some dv => obtain ⟨w, rfl⟩ := den_null_leaf0 hd0; simp only [ncost]; omega + have ha0le : (match den (g + 1 + 1) (Verb.node rc thn els), + den (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) + b)))))) with + | some sa, some fo => ncost (g + 1 + 1) sa fo | _, _ => 0) ≤ 2 := by + rw [hdc] + cases hb0 : den (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) + b)))))) with + | none => simp + | some bv => obtain ⟨w, rfl⟩ := den_null_leaf0 hb0; simp only [ncost]; omega + have hrrle : (match some L.noun, + den (g + 1 + 1) (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) + b))))))) with + | some sa, some fo => ncost (g + 1 + 1) sa fo | _, _ => 0) + ≤ ncost (g + 1) L.noun branchNoun.noun := by + cases haa : den (g + 1 + 1) (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) + b))))))) with + | none => simp + | some aa => + simp only + have hDa0 : Den (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf + 4) b))))))) aa := + ⟨g + 1 + 1, haa⟩ + rw [Den_star] at hDa0 + obtain ⟨cv, b0v, hDc0, hDb0, hEva0⟩ := hDa0 + have : cv = (Verb.node rc thn els).noun := (Den_terminal_iff htc).1 hDc0 + subst this + rw [Den_null] at hDb0 + obtain ⟨z, c0v, rfl, hDz, hDc0'⟩ := hDb0 + have : z = Noun.atom 0 := Den_leaf.1 hDz + subst this + rw [EvN_op0] at hEva0 + obtain ⟨ax, rfl, hslotA⟩ := hEva0 + rw [Den_star] at hDc0' + obtain ⟨p23, d0v, hD23, hDd0, hEvc0⟩ := hDc0' + have hp23 : p23 = Noun.cell (Noun.atom 2) (Noun.atom 3) := by + have := (Den_terminal_iff ht23).1 hD23; simpa only [noun] using this + subst hp23 + rw [Den_null] at hDd0 + obtain ⟨z2, iv, rfl, hDz2, hDinner⟩ := hDd0 + have : z2 = Noun.atom 0 := Den_leaf.1 hDz2 + subst this + rw [EvN_op0] at hEvc0 + obtain ⟨ax2, rfl, hslotB⟩ := hEvc0 + rw [Den_star] at hDinner + obtain ⟨Lv, fv, hDL', hD4, hEvinner⟩ := hDinner + have : Lv = L.noun := (Den_terminal_iff hL).1 hDL' + subst this + have hfv : fv = Noun.cell (Noun.atom 4) (Noun.cell (Noun.atom 4) b.noun) := by + have := (Den_terminal_iff ht4).1 hD4; simpa only [noun] using this + subst hfv + rw [EvN_op4] at hEvinner + obtain ⟨y, hEvy, hlusy⟩ := hEvinner + rw [EvN_op4] at hEvy + obtain ⟨y2, hEvy2, hlusy2⟩ := hEvy + have hy2 : y2 = sbv := EvN_det hEvy2 hEvb + subst hy2 + rcases hbr with ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩ + · have hy : y = Noun.atom 1 := by + rw [lus_atom] at hlusy2; exact (Option.some.inj hlusy2).symm + subst hy + have hax2 : ax2 = 2 := by + rw [lus_atom] at hlusy; exact (Noun.atom.inj (Option.some.inj hlusy)).symm + subst hax2 + rw [slot_two] at hslotB + have hax : ax = 2 := (Noun.atom.inj (Option.some.inj hslotB)).symm + subst hax + rw [noun, slot_two] at hslotA + obtain rfl := Option.some.inj hslotA + exact eqBr.le + · have hy : y = Noun.atom 2 := by + rw [lus_atom] at hlusy2; exact (Option.some.inj hlusy2).symm + subst hy + have hax2 : ax2 = 3 := by + rw [lus_atom] at hlusy; exact (Noun.atom.inj (Option.some.inj hlusy)).symm + subst hax2 + rw [slot_three] at hslotB + have hax : ax = 3 := (Noun.atom.inj (Option.some.inj hslotB)).symm + subst hax + rw [noun, slot_three] at hslotA + obtain rfl := Option.some.inj hslotA + exact eqBr.le + rw [hSFinner] + cases evaluatedZero : den (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf + 4) b)))) + <;> cases evaluatedOne : den (g + 1 + 1) (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null + (Verb.leaf 4) b)))))) + <;> cases evaluatedTwo : den (g + 1 + 1) (Verb.node .star (Verb.node rc thn els) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) + (Verb.node .null (Verb.leaf 0) + (Verb.node .star L (Verb.node .null (Verb.leaf 4) (Verb.node .null + (Verb.leaf 4) b))))))) + <;> simp only [hdc, h23, evaluatedZero, evaluatedOne, evaluatedTwo] + at hc0le ha0le hrrle ⊢ + <;> omega + cases sbv with + | cell k1 k2 => simp at hvv + | atom av => + match av, hvv with + | 0, hvv => exact hbound thn (Or.inl ⟨rfl, rfl⟩) hvv + | 1, hvv => exact hbound els (Or.inr ⟨rfl, rfl⟩) hvv + | (k + 2), hvv => simp at hvv + +/-! ### (e) `cost_localN` — strict local decrease at a `redexHint` redex -/ + +private theorem hint_composition_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) + (n2 : Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hfL : den (g + 1) L = some L.noun) (hdL : den (g + 1 + 1) L + = some L.noun) + (hgn2 : den g n2 = some n2.noun) (hr : (node Action.star L (node Action.null (leaf 2) + n2)).redexHint = some rr) + (hvv : evalN (g + 1) L.noun ((atom 2).cell n2.noun) = some x) : + vcostN (g + 1 + 1) rr < ncost (g + 1 + 1) L.noun (node Action.null (leaf 2) n2).noun := by + cases n2 with + | leaf k => simp [redexHint, hintExpand, reduce, redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨hg1b, hg1c⟩ := + den_term_children (editOk_of_terminal hn2t) (den_succ hgn2) htb htc + have hdb_f1 : den (g + 1 + 1) b = some b.noun := den_succ hg1b + have hdc_f1 : den (g + 1 + 1) c = some c.noun := den_succ hg1c + simp only [noun, evalN] at hvv + cases hb : evalN g L.noun b.noun with + | none => rw [hb] at hvv; simp at hvv + | some sb => + cases hc : evalN g L.noun c.noun with + | none => rw [hb, hc] at hvv; simp at hvv + | some sc => + rw [hb, hc] at hvv + have hb' : evalN (g + 1) L.noun b.noun = some sb := evalN_succ hb + have hc' : evalN (g + 1) L.noun c.noun = some sc := evalN_succ hc + have hd_Lb : den (g + 1 + 1) (Verb.node .star L b) = some sb := + den_star_val hfL hg1b hb' + have hd_Lc : den (g + 1 + 1) (Verb.node .star L c) = some sc := + den_star_val hfL hg1c hc' + have eq_b : ncost (g + 1 + 1) L.noun b.noun = ncost (g + 1) L.noun b.noun := + ncost_align hb (by omega) (by omega) + have eq_c : ncost (g + 1 + 1) L.noun c.noun = ncost (g + 1) L.noun c.noun := + ncost_align hc (by omega) (by omega) + have eq_sbsc : ncost (g + 1 + 1) sb sc = ncost (g + 1) sb sc := + ncost_align hvv (by omega) (by omega) + simp only [vcostN_star_eq, hd_Lb, hd_Lc, + vcostN_star_terminal hL htb hdL hdb_f1, + vcostN_star_terminal hL htc hdL hdc_f1] + simp only [noun, ncost, hb', hc'] + rw [eq_b, eq_c, eq_sbsc] + omega + +private theorem hint_conditional_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) + (n2 : Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hdL : den (g + 1 + 1) L = some L.noun) (hgn2 : den g n2 = + some n2.noun) + (hr : (node Action.star L (node Action.null (leaf 6) n2)).redexHint = some rr) + (hvv : evalN (g + 1) L.noun ((atom 6).cell n2.noun) = some x) : + vcostN (g + 1 + 1) rr < ncost (g + 1 + 1) L.noun (node Action.null (leaf 6) n2).noun := by + cases n2 with + | leaf k => simp [redexHint, hintExpand, reduce, redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + have hct : isTerminal c = true := htc + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨hgb, hgcv⟩ := + den_term_children (editOk_of_terminal hn2t) hgn2 htb htc + have hdb_f1 : den (g + 1 + 1) b = some b.noun := den_succ (den_succ hgb) + have h4a : den (g + 1) (Verb.node .null (Verb.leaf 4) b) + = some (Noun.cell (Noun.atom 4) b.noun) := + den_null_val (by simp only [den]) hgb + have h4b : den (g + 1 + 1) + (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) + = some (Noun.cell (Noun.atom 4) (Noun.cell (Noun.atom 4) b.noun)) := + den_null_val (by simp only [den]) h4a + have ht4 : isTerminal + (Verb.node .null (Verb.leaf 4) (Verb.node .null (Verb.leaf 4) b)) = true := by + simp [isTerminal, Action.isNull, htb] + have ht23 : isTerminal (Verb.node .null (Verb.leaf 2) (Verb.leaf 3)) = true := by + simp [isTerminal, Action.isNull] + cases c with + | leaf ck => exfalso; simp only [noun, evalN] at hvv; simp at hvv + | node rc thn els => + have hct2 : isTerminal (Verb.node rc thn els) = true := hct + rw [isTerminal_node] at hct; simp only [Bool.and_eq_true] at hct + obtain ⟨⟨hrc, hthn⟩, htels⟩ := hct + exact op6_decreaseN hL hct2 hthn htels hgcv hdL h4b ht4 ht23 hvv + +private theorem hint_arm_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) (n2 : + Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hfL : den (g + 1) L = some L.noun) (hdL : den (g + 1 + 1) L + = some L.noun) + (hgn2 : den g n2 = some n2.noun) (hr : (node Action.star L (node Action.null (leaf 9) + n2)).redexHint = some rr) + (hvv : evalN (g + 1) L.noun ((atom 9).cell n2.noun) = some x) : + vcostN (g + 1 + 1) rr < ncost (g + 1 + 1) L.noun (node Action.null (leaf 9) n2).noun := by + cases n2 with + | leaf k => simp [redexHint, hintExpand, reduce, redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + have hrn2' : rn2 = Action.null := by cases rn2 <;> simp_all [Action.isNull] + subst hrn2' + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨g', rfl⟩ : ∃ g', g = g' + 1 := by + cases g with | zero => simp [den] at hgn2 | succ n => exact ⟨n, rfl⟩ + obtain ⟨hhb, hhc⟩ := den_term_split htb htc hgn2 + cases b with + | node bb bc bd => simp [noun, evalN] at hvv + | leaf ax0 => + simp only [noun, evalN] at hvv + cases hcor : evalN (g' + 1) L.noun c.noun with + | none => simp [hcor] at hvv + | some cor => + simp only [hcor] at hvv + cases harm : Noun.slot ax0 cor with + | none => simp [harm] at hvv + | some arm => + simp only [harm] at hvv + have hg2c : den (g' + 1 + 1) c = some c.noun := den_succ (den_succ hhc) + have hdc_f1 : den (g' + 1 + 1 + 1) c = some c.noun := den_succ hg2c + have hcor2 : evalN (g' + 2) L.noun c.noun = some cor := + evalN_succ hcor + have hd_core : den (g' + 1 + 1 + 1) (Verb.node .star L c) = some cor := + den_star_val hfL hg2c hcor2 + have hb0 : den g' (Verb.leaf 0) = some (Noun.atom 0) := by simp only [den] + have hb1 : den g' (Verb.leaf 1) = some (Noun.atom 1) := by simp only [den] + have hf0h : den (g' + 1) (Verb.node .null (Verb.leaf 0) (Verb.leaf ax0)) + = some (Noun.cell (Noun.atom 0) (Noun.atom ax0)) := den_null_val hb0 hhb + have hf01 : den (g' + 1) (Verb.node .null (Verb.leaf 0) (Verb.leaf 1)) + = some (Noun.cell (Noun.atom 0) (Noun.atom 1)) := den_null_val hb0 hb1 + have hM : den (g' + 1 + 1) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 1)) + (Verb.node .null (Verb.leaf 0) (Verb.leaf ax0))) + = some (Noun.cell (Noun.cell (Noun.atom 0) (Noun.atom 1)) + (Noun.cell (Noun.atom 0) (Noun.atom ax0))) := den_null_val hf01 hf0h + have h2 : den (g' + 1 + 1) (Verb.leaf 2) = some (Noun.atom 2) := by + simp only [den] + have hform : den (g' + 1 + 1 + 1) + (Verb.node .null (Verb.leaf 2) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 1)) + (Verb.node .null (Verb.leaf 0) (Verb.leaf ax0)))) + = some (Noun.cell (Noun.atom 2) + (Noun.cell (Noun.cell (Noun.atom 0) (Noun.atom 1)) + (Noun.cell (Noun.atom 0) (Noun.atom ax0)))) := den_null_val h2 hM + have hformT : isTerminal + (Verb.node .null (Verb.leaf 2) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 1)) + (Verb.node .null (Verb.leaf 0) (Verb.leaf ax0)))) = true := by + simp [isTerminal, Action.isNull] + have hev01 : evalN (g' + 2) cor + (Noun.cell (Noun.atom 0) (Noun.atom 1)) = some cor := by + simp only [evalN]; exact slot_one + have hev0h : evalN (g' + 2) cor + (Noun.cell (Noun.atom 0) (Noun.atom ax0)) = some arm := by + simp only [evalN]; exact harm + have eq_c1 : ncost (g' + 1 + 1 + 1) L.noun c.noun + = ncost (g' + 2) L.noun c.noun := ncost_align hcor (by omega) (by omega) + have hRHS : ncost (g' + 1 + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 9) (Verb.node .null (Verb.leaf ax0) c)).noun + = 20 + ncost (g' + 2) L.noun c.noun + ncost (g' + 2) cor arm := by + simp only [noun, ncost, hcor2, harm] + rw [hRHS] + simp only [vcostN_star_eq, hd_core, hform, + vcostN_star_terminal hL htc hdL hdc_f1, vcostN_terminal _ hformT] + simp only [ncost, hev01, hev0h] + change 1 + 2 + 2 + ncost (g' + 2) cor arm + + ncost (g' + 1 + 1 + 1) L.noun c.noun + 0 + < 20 + ncost (g' + 2) L.noun c.noun + ncost (g' + 2) cor arm + rw [eq_c1] + omega + +private theorem hint_edit_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) (n2 : + Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hdL : den (g + 1 + 1) L = some L.noun) (hgn2 : den g n2 = + some n2.noun) + (hr : (node Action.star L (node Action.null (leaf 10) n2)).redexHint = some rr) + (hvv : evalN (g + 1) L.noun ((atom 10).cell n2.noun) = some x) : + vcostN (g + 1 + 1) rr < ncost (g + 1 + 1) L.noun (node Action.null (leaf 10) n2).noun := by + cases n2 with + | leaf k => simp [redexHint, hintExpand, reduce, redex] at hr + | node rn2 hd d => + cases hd with + | leaf k => simp [redexHint, hintExpand, reduce, redex] at hr + | node rhd axv c => + have hn2t : isTerminal (Verb.node rn2 (Verb.node rhd axv c) d) = true := htn2 + rw [isTerminal_node] at htn2; simp only [Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, hthd⟩, htd⟩ := htn2 + have hhdt : isTerminal (Verb.node rhd axv c) = true := hthd + rw [isTerminal_node] at hthd; simp only [Bool.and_eq_true] at hthd + obtain ⟨⟨hrhd, htaxv⟩, htc⟩ := hthd + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := Option.some.inj hr + obtain ⟨hg1hd, hg1d⟩ := + den_term_children (editOk_of_terminal hn2t) (den_succ hgn2) hhdt htd + obtain ⟨hg1axv, hg1c⟩ := + den_term_children (editOk_of_terminal hhdt) hg1hd htaxv htc + have hdc_f1 : den (g + 1 + 1) c = some c.noun := den_succ hg1c + have hdd_f1 : den (g + 1 + 1) d = some d.noun := den_succ hg1d + cases axv with + | node b0 c0 d0 => simp [noun, evalN] at hvv + | leaf a0 => + simp only [noun, evalN] at hvv + cases hnew : evalN g L.noun c.noun with + | none => rw [hnew] at hvv; simp at hvv + | some new => + cases hold : evalN g L.noun d.noun with + | none => rw [hnew, hold] at hvv; simp at hvv + | some old => + have eq_c : ncost (g + 1 + 1) L.noun c.noun + = ncost (g + 1) L.noun c.noun := ncost_align hnew (by omega) (by omega) + have eq_d : ncost (g + 1 + 1) L.noun d.noun + = ncost (g + 1) L.noun d.noun := ncost_align hold (by omega) (by omega) + rw [vcostN_edit_eq, vcostN_null_eq, + vcostN_star_terminal hL htc hdL hdc_f1, + vcostN_star_terminal hL htd hdL hdd_f1] + simp only [vcostN, noun, ncost] + rw [eq_c, eq_d]; omega + +private theorem hint_opcode_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) (n2 : + Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hdL : den (g + 1 + 1) L = some L.noun) (hgn2 : den g n2 = + some n2.noun) + (hr : (node Action.star L (node Action.null (leaf 11) n2)).redexHint = some rr) + (hvv : evalN (g + 1) L.noun ((atom 11).cell n2.noun) = some x) : + vcostN (g + 1 + 1) rr < ncost (g + 1 + 1) L.noun (node Action.null (leaf 11) n2).noun := by + cases n2 with + | leaf k => + simp [noun, evalN] at hvv + | node rn2 h2 b2 => + have hn2t : isTerminal (Verb.node rn2 h2 b2) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, hth2⟩, htb2⟩ := htn2 + have hgb2 : den g b2 = some b2.noun := + (den_term_children (editOk_of_terminal hn2t) hgn2 hth2 htb2).2 + cases h2 with + | leaf t3 => + -- static hint: rr = *[L body] + have hrr : redexHint (Verb.node .star L + (Verb.node .null (Verb.leaf 11) (Verb.node rn2 (Verb.leaf t3) b2))) + = some (Verb.node .star L b2) := by + simp [redexHint, hintExpand, hL, hn2t, Action.isNull] + rw [hrr] at hr + obtain rfl := Option.some.inj hr + simp only [noun, evalN] at hvv + have hd2b2 : den (g + 1 + 1) b2 = some b2.noun := den_succ (den_succ hgb2) + have hLHS : vcostN (g + 1 + 1) (Verb.node .star L b2) + = ncost (g + 1 + 1) L.noun b2.noun := + vcostN_star_terminal hL htb2 hdL hd2b2 + have hRHS : ncost (g + 1 + 1) L.noun + (Noun.cell (Noun.atom 11) (Noun.cell (Noun.atom t3) b2.noun)) + = 50 + ncost (g + 1) L.noun b2.noun := by simp only [ncost] + have halign : ncost (g + 1 + 1) L.noun b2.noun = ncost (g + 1) L.noun b2.noun := + ncost_align hvv (by omega) (by omega) + rw [hLHS] + simp only [noun] + rw [hRHS, halign] + omega + | node r3 t3 clue => + -- dynamic hint: rr = *[L (8 clue (7 [0 3] body))] + have hh2t : isTerminal (Verb.node r3 t3 clue) = true := hth2 + simp only [isTerminal, Bool.and_eq_true] at hth2 + obtain ⟨⟨hr3, ht3⟩, htclue⟩ := hth2 + have hrr : redexHint (Verb.node .star L + (Verb.node .null (Verb.leaf 11) (Verb.node rn2 (Verb.node r3 t3 clue) b2))) + = some (Verb.node .star L + (Verb.node .null (Verb.leaf 8) + (Verb.node .null clue + (Verb.node .null (Verb.leaf 7) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 3)) b2))))) := by + simp [redexHint, hintExpand, hL, hn2t, Action.isNull] + rw [hrr] at hr + obtain rfl := Option.some.inj hr + simp only [noun, evalN] at hvv + cases hclue : evalN g L.noun clue.noun with + | none => rw [hclue] at hvv; simp at hvv + | some cv => + rw [hclue] at hvv + obtain ⟨g0, rfl⟩ : ∃ g0, g = g0 + 1 := by + cases g with + | zero => simp [evalN] at hclue + | succ n => exact ⟨n, rfl⟩ + have hclue1 : evalN (g0 + 2) L.noun clue.noun = some cv := evalN_succ hclue + have hslot3 : evalN (g0 + 1) (Noun.cell cv L.noun) + (Noun.cell (Noun.atom 0) (Noun.atom 3)) = some L.noun := by + simp [evalN, slot_three] + have hMt : isTerminal + (Verb.node .null (Verb.leaf 8) + (Verb.node .null clue + (Verb.node .null (Verb.leaf 7) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 3)) b2)))) = true + := by + simp [isTerminal, Action.isNull, htclue, htb2] + have hLHSle : vcostN (g0 + 1 + 1 + 1) + (Verb.node .star L + (Verb.node .null (Verb.leaf 8) + (Verb.node .null clue + (Verb.node .null (Verb.leaf 7) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 3)) b2))))) + ≤ ncost (g0 + 1 + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 8) + (Verb.node .null clue + (Verb.node .null (Verb.leaf 7) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 3)) + b2)))).noun := by + rw [vcostN_star_eq, vcostN_terminal L hL, vcostN_terminal _ hMt, + Nat.add_zero, Nat.add_zero] + cases hdL3 : den (g0 + 1 + 1 + 1) L with + | none => simp + | some sa => + cases hdM : den (g0 + 1 + 1 + 1) + (Verb.node .null (Verb.leaf 8) + (Verb.node .null clue + (Verb.node .null (Verb.leaf 7) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 3)) b2)))) + with + | none => simp + | some Mv => + have e1 : sa = L.noun := den_terminal_val hL hdL3 + have e2 : Mv = (Verb.node .null (Verb.leaf 8) + (Verb.node .null clue + (Verb.node .null (Verb.leaf 7) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 3)) + b2)))).noun := + den_terminal_val hMt hdM + subst e1; subst e2; simp + have hLHSval' : ncost (g0 + 1 + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 8) + (Verb.node .null clue + (Verb.node .null (Verb.leaf 7) + (Verb.node .null (Verb.node .null (Verb.leaf 0) (Verb.leaf 3)) b2)))).noun + = 4 + ncost (g0 + 2) L.noun clue.noun + ncost (g0 + 1) L.noun b2.noun := by + simp only [noun, ncost, hclue1, hslot3] + generalize ncost (g0 + 2) L.noun clue.noun = A + generalize ncost (g0 + 1) L.noun b2.noun = B + exact op11_arith A B + have hRHS' : ncost (g0 + 1 + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 11) (Verb.node rn2 (Verb.node r3 t3 clue) b2)).noun + = 50 + ncost (g0 + 2) L.noun clue.noun + ncost (g0 + 2) L.noun b2.noun := by + simp only [noun, ncost, hclue1] + have halign : ncost (g0 + 1) L.noun b2.noun = ncost (g0 + 2) L.noun b2.noun := + ncost_align hvv (by omega) (by omega) + rw [hRHS'] + have key := hLHSval' ▸ hLHSle + omega + +private theorem hint_equality_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) (n2 + : Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hdL : den (g + 1 + 1) L = some L.noun) (hgn2 : den g n2 = + some n2.noun) + (hr : (node Action.star L (node Action.null (leaf 5) n2)).redexHint = some rr) + (hvv : evalN (g + 1) L.noun ((atom 5).cell n2.noun) = some x) : + vcostN (g + 1 + 1) rr < ncost (g + 1 + 1) L.noun (node Action.null (leaf 5) n2).noun := by + cases n2 with + | leaf k => simp [redexHint, hintExpand, reduce, redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := + Option.some.inj hr + obtain ⟨hg1b, hg1c⟩ := + den_term_children (editOk_of_terminal hn2t) (den_succ hgn2) htb htc + have hdb_f1 : den (g + 1 + 1) b = some b.noun := den_succ hg1b + have hdc_f1 : den (g + 1 + 1) c = some c.noun := den_succ hg1c + simp only [noun, evalN] at hvv + cases hb : evalN g L.noun b.noun with + | none => rw [hb] at hvv; simp at hvv + | some sb => + cases hc : evalN g L.noun c.noun with + | none => rw [hb, hc] at hvv; simp at hvv + | some sc => + have eq_b : ncost (g + 1 + 1) L.noun b.noun = ncost (g + 1) L.noun b.noun := + ncost_align hb (by omega) (by omega) + have eq_c : ncost (g + 1 + 1) L.noun c.noun = ncost (g + 1) L.noun c.noun := + ncost_align hc (by omega) (by omega) + rw [vcostN_equal_eq, vcostN_star_terminal hL htb hdL hdb_f1, + vcostN_star_terminal hL htc hdL hdc_f1] + simp only [noun, ncost] + rw [eq_b, eq_c]; omega + +private theorem hint_push_cost_decrease {L rr : Verb} {x : Noun} (hL : L.isTerminal = true) (n2 : + Verb) + (htn2 : n2.isTerminal = true) (g : ℕ) (hfL : den (g + 1) L = some L.noun) (hdL : den (g + 1 + 1) L + = some L.noun) + (hgn2 : den g n2 = some n2.noun) (hr : (node Action.star L (node Action.null (leaf 8) + n2)).redexHint = some rr) + (hvv : evalN (g + 1) L.noun ((atom 8).cell n2.noun) = some x) : + vcostN (g + 1 + 1) rr < ncost (g + 1 + 1) L.noun (node Action.null (leaf 8) n2).noun := by + cases n2 with + | leaf k => simp [redexHint, hintExpand, reduce, redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := + Option.some.inj hr + obtain ⟨hgb, hgc⟩ := + den_term_children (editOk_of_terminal hn2t) hgn2 htb htc + have hg1b : den (g + 1) b = some b.noun := den_succ hgb + have hdb_f1 : den (g + 1 + 1) b = some b.noun := den_succ hg1b + have hg1c : den (g + 1) c = some c.noun := den_succ hgc + have hdc_f1 : den (g + 1 + 1) c = some c.noun := den_succ hg1c + simp only [noun, evalN] at hvv + cases hb : evalN g L.noun b.noun with + | none => rw [hb] at hvv; simp at hvv + | some sb => + rw [hb] at hvv + have hb' : evalN (g + 1) L.noun b.noun = some sb := evalN_succ hb + have eq_Lb : ncost (g + 1 + 1) L.noun b.noun = ncost (g + 1) L.noun b.noun + := + ncost_align hb (by omega) (by omega) + have eq_sbLc : ncost (g + 1 + 1) (Noun.cell sb L.noun) c.noun + = ncost (g + 1) (Noun.cell sb L.noun) c.noun := ncost_align hvv (by + omega) (by omega) + have hRHS : ncost (g + 1 + 1) L.noun + (Verb.node .null (Verb.leaf 8) (Verb.node rn2 b c)).noun + = 1 + ncost (g + 1) L.noun b.noun + + ncost (g + 1) (Noun.cell sb L.noun) c.noun := by + simp only [noun, ncost, hb'] + rw [hRHS] + have hvcnull : vcostN (g + 1 + 1) (Verb.node .null (Verb.node .star L b) L) + = ncost (g + 1 + 1) L.noun b.noun := by + rw [vcostN_null_eq, vcostN_star_terminal hL htb hdL hdb_f1, + vcostN_terminal L hL, + Nat.add_zero] + cases hgL : den g L with + | none => + have hnone : den (g + 1) (Verb.node .star L b) = none := by + simp only [den, hgL] + have hsubjn : den (g + 1 + 1) (Verb.node .null (Verb.node .star L b) L) + = none := + den_null_none hnone + rw [vcostN_star_eq] + simp only [hsubjn, hvcnull, vcostN_terminal c htc] + rw [eq_Lb] + omega + | some vL => + have hvL : vL = L.noun := den_terminal_val hL hgL + subst hvL + have hd_Lb_g : den (g + 1) (Verb.node .star L b) = some sb := + den_star_val hgL hgb hb + have hd_subj : den (g + 1 + 1) (Verb.node .null (Verb.node .star L b) L) + = some (Noun.cell sb L.noun) := den_null_val hd_Lb_g hfL + rw [vcostN_star_eq] + simp only [hd_subj, hdc_f1, hvcnull, vcostN_terminal c htc] + rw [eq_Lb, eq_sbLc] + omega + +theorem cost_localN {F : Nat} {L R rr : Verb} {x : Noun} + (hL : isTerminal L = true) (hR : isTerminal R = true) + (hv : den F (Verb.node .star L R) = some x) + (hr : redexHint (Verb.node .star L R) = some rr) : + vcostN F rr < vcostN F (Verb.node .star L R) := by + obtain ⟨f, rfl⟩ : ∃ f, F = f + 1 := by + cases F with + | zero => simp [den] at hv + | succ n => exact ⟨n, rfl⟩ + have hvv := hv + simp only [den] at hvv + cases hfL : den f L with + | none => rw [hfL] at hvv; simp at hvv + | some sL => + cases hfR : den f R with + | none => rw [hfL, hfR] at hvv; simp at hvv + | some sR => + rw [hfL, hfR] at hvv + have hsL : sL = L.noun := den_terminal_val hL hfL + have hsR : sR = R.noun := den_terminal_val hR hfR + subst hsL; subst hsR + have hdL : den (f + 1) L = some L.noun := den_succ hfL + have hvcL : vcostN (f + 1) L = 0 := vcostN_terminal L hL (f + 1) + have hvcR : vcostN (f + 1) R = 0 := vcostN_terminal R hR (f + 1) + have hstar : vcostN (f + 1) (Verb.node .star L R) = ncost (f + 1) L.noun R.noun := by + simp only [vcostN, hdL, den_succ hfR, hvcL, hvcR, Nat.add_zero] + rw [hstar] + cases R with + | leaf k => simp [redexHint, hintExpand, reduce, redex] at hr + | node ra i n2 => + have hRt : isTerminal (Verb.node ra i n2) = true := hR + simp only [isTerminal, Bool.and_eq_true] at hR + obtain ⟨⟨hra, hti⟩, htn2⟩ := hR + have hra0 : ra = Action.null := by cases ra <;> simp_all [Action.isNull] + subst hra0 + obtain ⟨g, rfl⟩ : ∃ g, f = g + 1 := by + cases f with + | zero => simp [den] at hfR + | succ n => exact ⟨n, rfl⟩ + obtain ⟨hgi, hgn2⟩ := den_term_split hti htn2 hfR + have hdi_f1 : den (g + 1 + 1) i = some i.noun := den_succ (den_succ hgi) + have hdn2_f1 : den (g + 1 + 1) n2 = some n2.noun := den_succ (den_succ hgn2) + have hvci : vcostN (g + 1 + 1) i = 0 := vcostN_terminal i hti _ + have hvcn2 : vcostN (g + 1 + 1) n2 = 0 := vcostN_terminal n2 htn2 _ + cases i with + | node bi ic id => + simp only [redexHint, hintExpand, reduce, redex] at hr + obtain rfl := Option.some.inj hr + simp only [noun, evalN] at hvv + cases he1 : evalN g L.noun (Noun.cell ic.noun id.noun) with + | none => rw [he1] at hvv; simp at hvv + | some l' => + cases he2 : evalN g L.noun n2.noun with + | none => rw [he1, he2] at hvv; simp at hvv + | some r' => + have e_i : ncost (g + 1 + 1) L.noun (Noun.cell ic.noun id.noun) + = ncost (g + 1) L.noun (Noun.cell ic.noun id.noun) := + ncost_align he1 (by omega) (by omega) + have e_n2 : ncost (g + 1 + 1) L.noun n2.noun = ncost (g + 1) L.noun n2.noun := + ncost_align he2 (by omega) (by omega) + rw [vcostN_null_eq, + vcostN_star_terminal hL hti hdL hdi_f1, + vcostN_star_terminal hL htn2 hdL hdn2_f1] + simp only [noun, ncost] + rw [e_i, e_n2] + omega + | leaf op => + have hop : op < 12 := by + rcases Nat.lt_or_ge op 12 with h | h + · exact h + · exfalso + simp only [noun] at hvv + rw [evalN_op_ge12_none (g + 1) L.noun n2.noun op h] at hvv + simp at hvv + simp only [noun] at hvv + interval_cases op + · -- OP0 + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := + Option.some.inj hr + rw [vcostN_slot_eq, vcostN_terminal n2 htn2, vcostN_terminal L hL] + simp only [noun, ncost]; omega + · -- OP1 + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := + Option.some.inj hr + rw [vcostN_terminal n2 htn2] + simp only [noun, ncost]; omega + · -- OP2 + exact hint_composition_cost_decrease hL n2 htn2 g hfL hdL hgn2 hr hvv + · -- OP3 + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := + Option.some.inj hr + simp only [evalN, Option.map_eq_some_iff] at hvv + obtain ⟨y, hy, _⟩ := hvv + have e_n2 : ncost (g + 1 + 1) L.noun n2.noun = ncost (g + 1) L.noun n2.noun := + ncost_align hy (by omega) (by omega) + rw [vcostN_minus_eq, vcostN_star_terminal hL htn2 hdL hdn2_f1] + simp only [vcostN, noun, ncost] + rw [e_n2]; omega + · -- OP4 + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := + Option.some.inj hr + simp only [evalN, Option.bind_eq_some_iff] at hvv + obtain ⟨y, hy, _⟩ := hvv + have e_n2 : ncost (g + 1 + 1) L.noun n2.noun = ncost (g + 1) L.noun n2.noun := + ncost_align hy (by omega) (by omega) + rw [vcostN_minus_eq, vcostN_star_terminal hL htn2 hdL hdn2_f1] + simp only [vcostN, noun, ncost] + rw [e_n2]; omega + · -- OP5 + exact hint_equality_cost_decrease hL n2 htn2 g hdL hgn2 hr hvv + · -- OP6 + exact hint_conditional_cost_decrease hL n2 htn2 g hdL hgn2 hr hvv + · -- OP7 + cases n2 with + | leaf k => simp [redexHint, hintExpand, reduce, redex] at hr + | node rn2 b c => + have hn2t : isTerminal (Verb.node rn2 b c) = true := htn2 + simp only [isTerminal, Bool.and_eq_true] at htn2 + obtain ⟨⟨hrn2, htb⟩, htc⟩ := htn2 + simp only [redexHint, hintExpand, reduce, redex] at hr; obtain rfl := + Option.some.inj hr + obtain ⟨hg1b, hg1c⟩ := + den_term_children (editOk_of_terminal hn2t) (den_succ hgn2) htb htc + have hdb_f1 : den (g + 1 + 1) b = some b.noun := den_succ hg1b + have hdc_f1 : den (g + 1 + 1) c = some c.noun := den_succ hg1c + simp only [noun, evalN] at hvv + cases hb : evalN g L.noun b.noun with + | none => rw [hb] at hvv; simp at hvv + | some sb => + rw [hb] at hvv + have hb' : evalN (g + 1) L.noun b.noun = some sb := evalN_succ hb + have hd_Lb : den (g + 1 + 1) (Verb.node .star L b) = some sb := + den_star_val hfL hg1b hb' + have eq_b : ncost (g + 1 + 1) L.noun b.noun = ncost (g + 1) L.noun b.noun := + ncost_align hb (by omega) (by omega) + have eq_sbc : ncost (g + 1 + 1) sb c.noun = ncost (g + 1) sb c.noun := + ncost_align hvv (by omega) (by omega) + simp only [vcostN_star_eq, hd_Lb, hdc_f1, + vcostN_star_terminal hL htb hdL hdb_f1, vcostN_terminal c htc] + simp only [noun, ncost, hb'] + rw [eq_b, eq_sbc]; omega + · -- OP8 + exact hint_push_cost_decrease hL n2 htn2 g hfL hdL hgn2 hr hvv + · -- OP9 + exact hint_arm_cost_decrease hL n2 htn2 g hfL hdL hgn2 hr hvv + · -- OP10 + exact hint_edit_cost_decrease hL n2 htn2 g hdL hgn2 hr hvv + · -- OP11 (hint expansion) + exact hint_opcode_cost_decrease hL n2 htn2 g hdL hgn2 hr hvv +/-! ### (f) step congruence and `cost_stepN` -/ + +theorem enum_den_F {F : Nat} : ∀ (v : Verb), editOk v = true → ∀ (c : Nat) (x : Noun), + den F v = some x → ∀ p ∈ (enum v c).2, ∃ y, den F p.2 = some y := by + intro v + induction v with + | leaf k => intro _ c x _ p hp; simp [enum] at hp + | node a l r ihl ihr => + intro hok c x hd p hp + obtain ⟨hokl, hokr⟩ := editOk_children hok + obtain ⟨⟨yl, hyl⟩, ⟨yr, hyr⟩⟩ := den_children_F hok hd + simp only [enum, List.mem_cons, List.mem_append] at hp + rcases hp with hroot | hleft | hright + · subst hroot; exact ⟨x, hd⟩ + · exact ihl hokl (c + 1) yl hyl p hleft + · exact ihr hokr _ yr hyr p hright + +theorem den_F_subnode {F : Nat} {v : Verb} {x : Noun} (hok : editOk v = true) + (hv : den F v = some x) {i : Nat} {sub : Verb} (hmap : map i v = some sub) : + ∃ y, den F sub = some y := + enum_den_F v hok 0 x hv (i, sub) (mem_nodes_of_map hmap) + +theorem den_F_swap_eq {F : Nat} {w w' : Verb} (hok : editOk w = true) (hrel : Rel w w') + {a b : Noun} (ha : den F w = some a) (hb : den F w' = some b) : a = b := by + have hiff := Rel_den w.noun.size w w' (Nat.le_refl _) hrel hok a + exact den_det (hiff.1 ⟨F, ha⟩) ⟨F, hb⟩ + +theorem vcostN_replace_cong (F i : Nat) (sub r₀ : Verb) (hpend : sub.isPending = true) + (hgraph : ∀ y, Den sub y ↔ Den r₀ y) (hr0 : editOk r₀ = true) : + ∀ (v : Verb) (c : Nat) (xv xv' : Noun), + editOk v = true → + findAt i (enum v c).2 = some sub → + den F v = some xv → + den F (replaceAux i r₀ v c).2 = some xv' → + vcostN F (replaceAux i r₀ v c).2 + vcostN F sub = vcostN F v + vcostN F r₀ := by + intro v + induction v with + | leaf k => intro c xv xv' _ hfind; simp [enum, findAt] at hfind + | node a l r ihl ihr => + intro c xv xv' hok hfind hdv hdv' + obtain ⟨hokl, hokr⟩ := editOk_children hok + by_cases hci : c = i + · subst hci + have hf : findAt c (enum (Verb.node a l r) c).2 = some (Verb.node a l r) := by + simp [enum, findAt] + have hsub : Verb.node a l r = sub := (Option.some.inj (hf.symm.trans hfind)) + have hrepl : (replaceAux c r₀ (Verb.node a l r) c).2 = r₀ := by simp [replaceAux] + rw [hrepl, ← hsub] + omega + · have hine : ¬ (i = c) := fun h => hci h.symm + have hcL : (replaceAux i r₀ l (c + 1)).1 = (enum l (c + 1)).1 := replaceAux_counter i r₀ l + (c + 1) + have hfind' : findAt i (enum l (c + 1)).2 = some sub ∧ + findAt i (enum r (enum l (c + 1)).1).2 = none + ∨ findAt i (enum l (c + 1)).2 = none ∧ + findAt i (enum r (enum l (c + 1)).1).2 = some sub := by + simp only [enum, findAt, ite_eq_right hine] at hfind + rw [findAt_append] at hfind + cases hl : findAt i (enum l (c + 1)).2 with + | some s => rw [hl] at hfind; exact Or.inl ⟨hfind, by + cases hr : findAt i (enum r (enum l (c + 1)).1).2 with + | none => rfl + | some s2 => + exfalso + have h1 := enum_index_range l (c + 1) (i, s) (findAt_some_mem hl) + have h2 := enum_index_range r (enum l (c + 1)).1 (i, s2) (findAt_some_mem hr) + omega⟩ + | none => rw [hl] at hfind; exact Or.inr ⟨rfl, hfind⟩ + obtain ⟨⟨xl, hxl⟩, ⟨xr, hxr⟩⟩ := den_children_F hok hdv + have hok' : editOk (replaceAux i r₀ (Verb.node a l r) c).2 = true := by + apply editOk_replaceAux i r₀ hr0 (Verb.node a l r) c hok + intro s hs; have : s = sub := Option.some.inj (hs.symm.trans hfind); rw [this]; exact + hpend + simp only [replaceAux, ite_eq_right hci] at hdv' hok' ⊢ + rw [hcL] at hdv' hok' ⊢ + obtain ⟨⟨xl', hxl'⟩, ⟨xr', hxr'⟩⟩ := den_children_F hok' hdv' + rcases hfind' with ⟨hLsub, hRnone⟩ | ⟨hLnone, hRsub⟩ + · have hrIH := ihl (c + 1) xl xl' hokl hLsub hxl hxl' + have hreq : (replaceAux i r₀ r (enum l (c + 1)).1).2 = r := replaceAux_notMem i r₀ r _ + hRnone + rw [hreq] at hxr' ⊢ + have hrelL : Rel l (replaceAux i r₀ l (c + 1)).2 := by + apply replaceAux_rel i r₀ l (c + 1) + intro s hs + have : s = sub := Option.some.inj (hs.symm.trans hLsub) + rw [this]; exact ⟨hpend, hgraph⟩ + have hlEq : xl = xl' := den_F_swap_eq hokl hrelL hxl hxl' + cases a <;> + simp only [vcostN_star_eq, vcostN_null_eq, vcostN_wut_eq, vcostN_slot_eq, + vcostN_equal_eq, + vcostN_minus_eq, vcostN_edit_eq, hxl, hxl', hxr, hlEq] <;> omega + · have hrIH := ihr (enum l (c + 1)).1 xr xr' hokr hRsub hxr hxr' + have hleq : (replaceAux i r₀ l (c + 1)).2 = l := replaceAux_notMem i r₀ l _ hLnone + rw [hleq] at hxl' ⊢ + have hrelR : Rel r (replaceAux i r₀ r (enum l (c + 1)).1).2 := by + apply replaceAux_rel i r₀ r (enum l (c + 1)).1 + intro s hs + have : s = sub := Option.some.inj (hs.symm.trans hRsub) + rw [this]; exact ⟨hpend, hgraph⟩ + have hrEq : xr = xr' := den_F_swap_eq hokr hrelR hxr hxr' + cases a <;> + simp only [vcostN_star_eq, vcostN_null_eq, vcostN_wut_eq, vcostN_slot_eq, + vcostN_equal_eq, + vcostN_minus_eq, vcostN_edit_eq, hxr, hxr', hxl, hrEq] <;> omega + +theorem cost_stepN {F : Nat} {v v' : Verb} {x x' : Noun} + (hwf : editOk v = true) (hv : den F v = some x) (hv' : den F v' = some x') + (h : nextHint v = some v') : vcostN F v' < vcostN F v := by + obtain ⟨i, sub, r₀, hmax, hmap, hdom, hred, hv'eq⟩ := nextHint_eq_some_iff.1 h + have hgi : getIndex v = some i := getIndex_eq_some_iff.2 hmax + have hoksub : editOk sub = true := editOk_enum v 0 hwf (i, sub) (mem_nodes_of_map hmap) + have hokr0 : editOk r₀ = true := editOk_redexHint hred hoksub + obtain ⟨w, hmapw, hpw⟩ := hmax.1 + have hpsub : Verb.isPending sub = true := (Option.some.inj (hmapw.symm.trans hmap)) ▸ hpw + have hgraph : ∀ y, Den sub y ↔ Den r₀ y := fun y => denHint_local hdom hred + obtain ⟨xs, hxs⟩ := den_F_subnode hwf hv hmap + have hlocal : vcostN F r₀ < vcostN F sub := by + cases sub with + | leaf k => simp [isPending] at hpsub + | node a L R => + obtain ⟨hL, hR⟩ := children_terminal_of_getIndex hgi hmap + cases a with + | null => simp [isPending, Action.isNull] at hpsub + | wut => simp [redexHint, hintExpand, reduce] at hred + | star => exact cost_localN hL hR hxs hred + | slot => + have hEXn : hintExpand (Verb.node .slot L R) = none := by simp [hintExpand] + rw [redexHint_eq_reduce_of_hintExpand_none hEXn] at hred + have ht0 : isTerminal r₀ = true := by + cases L with + | leaf ax => rw [reduce] at hred; simp only [noun] at hred + exact isTerminal_of_map_ofNoun hred + | node a2 l2 r2 => rw [reduce] at hred; simp only [noun] at hred; simp at hred + rw [vcostN_terminal r₀ ht0] + simp only [vcostN_slot_eq, vcostN_terminal L hL, vcostN_terminal R hR]; omega + | equal => + have hEXn : hintExpand (Verb.node .equal L R) = none := by simp [hintExpand] + rw [redexHint_eq_reduce_of_hintExpand_none hEXn] at hred + have ht0 : isTerminal r₀ = true := by + rw [reduce] at hred; obtain rfl := Option.some.inj hred; exact ofNoun_terminal _ + rw [vcostN_terminal r₀ ht0] + simp only [vcostN_equal_eq, vcostN_terminal L hL, vcostN_terminal R hR]; omega + | minus => + have hEXn : hintExpand (Verb.node .minus L R) = none := by simp [hintExpand] + rw [redexHint_eq_reduce_of_hintExpand_none hEXn] at hred + have ht0 : isTerminal r₀ = true := by + cases L with + | leaf ax => + rw [reduce] at hred; simp only [noun] at hred + split at hred + · obtain rfl := Option.some.inj hred; exact ofNoun_terminal _ + · exact isTerminal_of_map_ofNoun hred + · simp at hred + | node a2 l2 r2 => rw [reduce] at hred; simp only [noun] at hred; simp at hred + rw [vcostN_terminal r₀ ht0] + simp only [vcostN_minus_eq, vcostN_terminal L hL, vcostN_terminal R hR]; omega + | edit => + have hEXn : hintExpand (Verb.node .edit L R) = none := by simp [hintExpand] + rw [redexHint_eq_reduce_of_hintExpand_none hEXn] at hred + have ht0 : isTerminal r₀ = true := by + cases L with + | leaf ax => + cases R with + | leaf k => simp [reduce, noun] at hred + | node rb nw od => + simp only [reduce, noun] at hred + exact isTerminal_of_map_ofNoun hred + | node a2 l2 r2 => simp [reduce, noun] at hred + rw [vcostN_terminal r₀ ht0] + simp only [vcostN_edit_eq, vcostN_terminal L hL, vcostN_terminal R hR]; omega + have hv'2 : den F (replaceAt i v r₀) = some x' := hv'eq ▸ hv' + have hcong : vcostN F (replaceAt i v r₀) + vcostN F sub = vcostN F v + vcostN F r₀ := + vcostN_replace_cong F i sub r₀ hpsub hgraph hokr0 v 0 x x' hwf hmap hv hv'2 + rw [hv'eq] + omega + +/-! ### (c) `progressN` -/ + +/-- A `den`-defined, `editOk`, non-terminal verb has a `nextHint` successor. -/ +theorem progressN {v : Verb} {x : Noun} (hwf : editOk v = true) (hd : Den v x) + (hnt : isTerminal v = false) : ∃ v', nextHint v = some v' := by + have hne : getIndex v ≠ none := by + intro h; rw [getIndex_none_iff_terminal] at h; rw [h] at hnt; simp at hnt + obtain ⟨i, hi⟩ := Option.ne_none_iff_exists'.mp hne + obtain ⟨sub, hmap⟩ := map_some_of_getIndex hi + have hmax : IsMaxPending dfsLt v i := getIndex_eq_some_iff.1 hi + have hsubp : sub.isPending = true := by + obtain ⟨w, hw, hwp⟩ := hmax.1 + rw [hmap] at hw + have heq : sub = w := Option.some.inj hw + rw [heq]; exact hwp + cases sub with + | leaf k => simp [isPending] at hsubp + | node a L R => + obtain ⟨hL, hR⟩ := children_terminal_of_getIndex hi hmap + obtain ⟨y, hsuby⟩ := Den_subnode hwf hd hmap + have hstepN : inDomainHint (Verb.node a L R) = true → + (redexHint (Verb.node a L R)).isSome = true → ∃ v', nextHint v = some v' := by + intro hdom hred + cases hrr : redexHint (Verb.node a L R) with + | none => rw [hrr] at hred; simp at hred + | some rr => + exact ⟨replaceAt i v rr, + nextHint_eq_some_iff.2 ⟨i, Verb.node a L R, rr, hmax, hmap, hdom, hrr, rfl⟩⟩ + cases a with + | null => simp [isPending, Action.isNull] at hsubp + | wut => exact absurd hsuby Den_wut + | equal => + have hdom : inDomain (Verb.node .equal L R) = true := by + simp [inDomain, inOpcodeDomain, opReady, Action.isOperator, hL, hR] + exact hstepN (inDomainHint_of_inDomain hdom) (by simp [redexHint, hintExpand, reduce]) + | slot => + obtain ⟨ax, b, hLax, hRb, hslot⟩ := Den_slot.1 hsuby + have hLn : L.noun = Noun.atom ax := ((Den_terminal_iff hL).1 hLax).symm + have hbR : b = R.noun := (Den_terminal_iff hR).1 hRb + subst hbR + have hdom : inDomain (Verb.node .slot L R) = true := by + simp [inDomain, inOpcodeDomain, opReady, Action.isOperator, hL, hR] + exact hstepN (inDomainHint_of_inDomain hdom) + (by simp [redexHint, hintExpand, reduce, hLn, hslot]) + | minus => + obtain ⟨b, hRb, hcase⟩ := Den_minus.1 hsuby + have hbR : b = R.noun := (Den_terminal_iff hR).1 hRb + subst hbR + have hdom : inDomain (Verb.node .minus L R) = true := by + simp [inDomain, inOpcodeDomain, opReady, Action.isOperator, hL, hR] + rcases hcase with ⟨hL3, _⟩ | ⟨hL4, hlus⟩ + · have hLn : L.noun = Noun.atom 3 := ((Den_terminal_iff hL).1 hL3).symm + exact hstepN (inDomainHint_of_inDomain hdom) + (by simp [redexHint, hintExpand, reduce, hLn]) + · have hLn : L.noun = Noun.atom 4 := ((Den_terminal_iff hL).1 hL4).symm + exact hstepN (inDomainHint_of_inDomain hdom) + (by simp [redexHint, hintExpand, reduce, hLn, hlus]) + | edit => + cases R with + | leaf k => exact absurd hsuby Den_edit_leaf + | node rb nw od => + simp only [isTerminal, Bool.and_eq_true] at hR + obtain ⟨⟨hrb, hnw⟩, hod⟩ := hR + obtain ⟨ax, n, o, hLax, hnwn, hodo, hedit⟩ := Den_edit_node.1 hsuby + have hLn : L.noun = Noun.atom ax := ((Den_terminal_iff hL).1 hLax).symm + have hnwn' : n = nw.noun := (Den_terminal_iff hnw).1 hnwn + have hodo' : o = od.noun := (Den_terminal_iff hod).1 hodo + subst hnwn'; subst hodo' + have hdom : inDomain (Verb.node .edit L (Verb.node rb nw od)) = true := by + simp [inDomain, inOpcodeDomain, opReady, Action.isOperator, hL, + isTerminal, hrb, hnw, hod] + exact hstepN (inDomainHint_of_inDomain hdom) + (by simp [redexHint, hintExpand, reduce, hLn, hedit]) + | star => + obtain ⟨a', b', hLa, hRb, hev⟩ := Den_star.1 hsuby + have haL : a' = L.noun := (Den_terminal_iff hL).1 hLa + have hbR : b' = R.noun := (Den_terminal_iff hR).1 hRb + subst haL; subst hbR + cases R with + | leaf k => + exfalso; obtain ⟨f, hf⟩ := hev + simp [noun, evalN_atom_none] at hf + | node rb fi n2 => + simp only [isTerminal, Bool.and_eq_true] at hR + obtain ⟨⟨hrb, hfi_t⟩, hn2⟩ := hR + simp only [noun] at hev + cases fi with + | node bi ic id => + exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb hfi_t hn2 rfl)) + (by simp [redexHint, hintExpand, reduce, redex]) + | leaf op => + simp only [noun] at hev + have hop : op < 12 := by + rcases Nat.lt_or_ge op 12 with h | h + · exact h + · exfalso + obtain ⟨f, hf⟩ := hev + rw [evalN_op_ge12_none f L.noun n2.noun op h] at hf + exact absurd hf (by simp) + interval_cases op + · exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · obtain ⟨hb, hc, _, _, hn2eq, _⟩ := EvN_op2.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · obtain ⟨hb, hc, _, _, hn2eq, _⟩ := EvN_op5.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · obtain ⟨b, c, d, cv, hn2eq, _⟩ := EvN_op6.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · obtain ⟨hb, hc, _, hn2eq, _⟩ := EvN_op7.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · obtain ⟨hb, hc, _, hn2eq, _⟩ := EvN_op8.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · obtain ⟨ax, c, _, _, hn2eq, _⟩ := EvN_op9.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 c2 d2 => + exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 (by + decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · obtain ⟨ax, c, d, new, old, hn2eq, _⟩ := EvN_op10.1 hev + cases n2 with + | leaf k => rw [noun] at hn2eq; exact absurd hn2eq (by simp) + | node b2 hdd d2 => + rw [noun] at hn2eq + have hhd : hdd.noun = Noun.cell (Noun.atom ax) c := (Noun.cell.inj hn2eq).1 + cases hdd with + | leaf k => rw [noun] at hhd; exact absurd hhd (by simp) + | node bhd axv cc => + exact hstepN (inDomainHint_of_inDomain (inDomain_star_of hL hrb rfl hn2 + (by decide))) + (by simp [redexHint, hintExpand, reduce, redex]) + · -- OP₁₁ hint redex + cases n2 with + | leaf k => + exfalso; obtain ⟨f, hf⟩ := hev + simp only [noun] at hf + cases f with + | zero => simp [evalN] at hf + | succ ff => simp [evalN] at hf + | node rn2 h2 b2 => + have hdomN : inDomainHint + (Verb.node .star L (Verb.node rb (Verb.leaf 11) (Verb.node rn2 h2 b2))) + = true := by + rw [inDomainHint] + have hhr : isHintRedex + (Verb.node .star L (Verb.node rb (Verb.leaf 11) (Verb.node rn2 h2 + b2))) = true := by + simp [isHintRedex, hL, hrb, hn2] + rw [hhr]; simp + have hredN : (redexHint + (Verb.node .star L (Verb.node rb (Verb.leaf 11) (Verb.node rn2 h2 + b2)))).isSome = true := by + cases h2 <;> simp [redexHint, hintExpand, hL, hrb, hn2] + exact hstepN hdomN hredN + +/-! ### (g) `run_completeN` -/ + +theorem run_completeN {v : Verb} {x : Noun} (hok : editOk v = true) (hv : Den v x) : + ∃ N, runHint N v = some x := by + suffices aux : ∀ (bound F : Nat) (v : Verb) (x : Noun), + editOk v = true → den F v = some x → vcostN F v < bound → ∃ N, runHint N v = some x by + obtain ⟨F, hF⟩ := hv + exact aux (vcostN F v + 1) F v x hok hF (Nat.lt_succ_self _) + intro bound + induction bound with + | zero => intro F v x _ _ hlt; omega + | succ bound ih => + intro F v x hok hF hlt + by_cases hterm : isTerminal v = true + · refine ⟨1, ?_⟩ + have hx : x = v.noun := den_terminal_val hterm hF + subst hx + simp only [runHint, nextHint_none_of_terminal hterm, result, hterm, ite_true] + · have hntf : isTerminal v = false := by + cases hh : isTerminal v with + | true => exact absurd hh hterm + | false => rfl + obtain ⟨v', hnext⟩ := progressN hok ⟨F, hF⟩ hntf + obtain ⟨F', hF'⟩ := (denHint_step hok hnext).1 ⟨F, hF⟩ + have hokv' : editOk v' = true := editOk_nextHint hnext hok + have hGv : den (max F F') v = some x := den_mono (Nat.le_max_left _ _) hF + have hGv' : den (max F F') v' = some x := den_mono (Nat.le_max_right _ _) hF' + have hvcv : vcostN (max F F') v = vcostN F v := vcostN_mono (Nat.le_max_left _ _) hok hF + have hvcv' : vcostN (max F F') v' = vcostN F' v' := vcostN_mono (Nat.le_max_right _ _) hokv' + hF' + have hdec : vcostN (max F F') v' < vcostN (max F F') v := cost_stepN hok hGv hGv' hnext + have hlt' : vcostN F' v' < bound := by + rw [hvcv'] at hdec; rw [hvcv] at hdec; omega + obtain ⟨N, hN⟩ := ih F' v' x hokv' hF' hlt' + exact ⟨N + 1, by simp only [runHint, hnext]; exact hN⟩ + +/-! ### `hintComplete_holds` and OP₁₁ adequacy -/ + +theorem hintComplete_holds : HintComplete := by + intro s f r hev + obtain ⟨g, hg⟩ := (den_iff_evalN s f r).2 hev + obtain ⟨N, hN⟩ := run_completeN (editOk_program s f) ⟨g, hg⟩ + exact ⟨N, hN⟩ + +theorem runHint_adequate_complete (s f r : Noun) : + (∃ fuel, runHintProgram fuel s f = some r) ↔ (∃ fuel, evalN fuel s f = some r) := + runHint_adequate hintComplete_holds s f r + +end Verb +end Nock diff --git a/LeanPool/PureNock/Reference.lean b/LeanPool/PureNock/Reference.lean new file mode 100644 index 0000000000..bb160eb153 --- /dev/null +++ b/LeanPool/PureNock/Reference.lean @@ -0,0 +1,145 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Tree + +/-! +# Standard Nock reference evaluator + +Fuel-indexed Nat-level `evalN` for OP₀–OP₁₁ (`main.tex:1054–1076`; OP₁₁ extension). +-/ + +@[expose] public section + +namespace Nock +namespace Noun + +/-- **`?`** (`Algorithm:Noun_Operators`, `main.tex:1519–1523`). + type(n)=cell ↦ 0, else ↦ 1. -/ +def wut : Noun → Noun + | .cell _ _ => .atom 0 + | .atom _ => .atom 1 + +/-- **`+`** (`Algorithm:Noun_Operators`, `main.tex:1531–1535`). + atom ↦ (1+n) (paper mod p; Lean Nat successor); else ⊥. -/ +def lus : Noun → Option Noun + | .atom n => some (.atom (n + 1)) + | .cell _ _ => none + +/-- **`=`** (`Algorithm:Noun_Operators`, `main.tex:1525–1529`). + n₁=n₂ ↦ 0, else ↦ 1. -/ +def tis (a b : Noun) : Noun := + if a = b then .atom 0 else .atom 1 + +/-- **`/`** (`Algorithm:Noun_Operators`, `main.tex:1537–1557`). + axis=0 ↦ ⊥; =1 ↦ n; =2 ↦ head(n); =3 ↦ tail(n); + else b←axis mod 2, return /(2+b, /((axis−b)/2, n)). -/ +def slot : Nat → Noun → Option Noun + | 0, _ => none + | 1, n => some n + | (a+2), n => + match slot ((a+2) / 2) n with + | some (.cell l r) => some (if (a+2) % 2 == 0 then l else r) + | _ => none + termination_by a => a + decreasing_by omega + +/-- **`#`** (`Algorithm:Noun_Operators`, `main.tex:1587–1607`). + axis=0 ↦ ⊥; =1 ↦ new; else b←axis mod 2, a←(axis−b)/2, + edit head/tail of parent at a, recurse `#(a, parent, old)`. -/ +def edit : Nat → Noun → Noun → Option Noun + | 0, _, _ => none + | 1, new, _ => some new + | (a+2), new, old => + let sibAxis := if (a+2) % 2 == 0 then (a+2) + 1 else (a+2) - 1 + match slot sibAxis old with + | some sib => + let paired := if (a+2) % 2 == 0 then Noun.cell new sib else Noun.cell sib new + edit ((a+2) / 2) paired old + | none => none + termination_by a => a + decreasing_by omega + +end Noun + +open Noun + +/-- `*[subject formula]`, fuel-indexed. `evalN 0 _ _ = none`. -/ +def evalN : Nat → Noun → Noun → Option Noun + | 0, _, _ => none + | _ + 1, _, .atom _ => none + | fuel + 1, subj, .cell (.cell b c) d => + -- autocons: the head of the formula is itself a formula ⇒ distribute (Nock's [b c] rule) + match evalN fuel subj (.cell b c), evalN fuel subj d with + | some l, some r => some (.cell l r) + | _, _ => none + | fuel + 1, subj, .cell (.atom op) tail => + match op with + | 0 => match tail with -- *[a 0 b] = /[b a] + | .atom ax => slot ax subj + | _ => none + | 1 => some tail -- *[a 1 b] = b + | 2 => match tail with -- *[a 2 b c] = *[*[a b] *[a c]] + | .cell b c => + match evalN fuel subj b, evalN fuel subj c with + | some sb, some sc => evalN fuel sb sc + | _, _ => none + | _ => none + | 3 => (evalN fuel subj tail).map wut -- *[a 3 b] = ?*[a b] + | 4 => (evalN fuel subj tail).bind lus -- *[a 4 b] = +*[a b] + | 5 => match tail with -- *[a 5 b c] = =[*[a b] *[a c]] + | .cell b c => + match evalN fuel subj b, evalN fuel subj c with + | some sb, some sc => some (tis sb sc) + | _, _ => none + | _ => none + | 6 => match tail with -- *[a 6 b c d] (if b then c else d) + | .cell b (.cell c d) => + match evalN fuel subj b with + | some (.atom 0) => evalN fuel subj c + | some (.atom 1) => evalN fuel subj d + | _ => none + | _ => none + | 7 => match tail with -- *[a 7 b c] = *[*[a b] c] + | .cell b c => + match evalN fuel subj b with + | some sb => evalN fuel sb c + | none => none + | _ => none + | 8 => match tail with -- *[a 8 b c] = *[[*[a b] a] c] + | .cell b c => + match evalN fuel subj b with + | some sb => evalN fuel (.cell sb subj) c + | none => none + | _ => none + | 9 => match tail with -- *[a 9 b c] = *[*[a c] 2 [0 1] 0 b] + | .cell (.atom ax) c => -- ≡ fetch arm at axis b of core *[a c], run core against + -- it + match evalN fuel subj c with + | some core => + match slot ax core with + | some arm => evalN fuel core arm + | none => none + | none => none + | _ => none + | 10 => match tail with -- *[a 10 [b c] d] = #[b *[a c] *[a d]] + | .cell (.cell (.atom ax) c) d => + match evalN fuel subj c, evalN fuel subj d with + | some new, some old => edit ax new old + | _, _ => none + | _ => none + | 11 => match tail with -- *[a 11 [b c] d] (dynamic) / *[a 11 b c] (static) — hints + | .cell (.cell _ clue) body => + match evalN fuel subj clue with + | some _ => evalN fuel subj body + | none => none + | .cell (.atom _) body => evalN fuel subj body + | _ => none + | _ => none -- unknown opcode ⇒ crash + +end Nock diff --git a/LeanPool/PureNock/Semantics.lean b/LeanPool/PureNock/Semantics.lean new file mode 100644 index 0000000000..43b46e4e8d --- /dev/null +++ b/LeanPool/PureNock/Semantics.lean @@ -0,0 +1,51 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Reference + +/-! +# Executable semantic helpers + +Fuel monotonicity for the Nat-level `evalN` reference. +-/ + +@[expose] public section + +namespace Nock + +theorem evalN_succ {n : Nat} {s form r : Noun} + (h : evalN n s form = some r) : evalN (n + 1) s form = some r := by + induction n, s, form using evalN.induct generalizing r with + | case11 fuel subj tail ih => + simp only [evalN, Option.map_eq_some_iff] at h ⊢ + obtain ⟨a, ha, hp⟩ := h + exact ⟨a, ih ha, hp⟩ + | case12 fuel subj tail ih => + simp only [evalN, Option.bind_eq_some_iff] at h ⊢ + obtain ⟨a, ha, hp⟩ := h + exact ⟨a, ih ha, hp⟩ + | _ => + simp_all [evalN] + +theorem evalN_mono {n m : Nat} {s form r : Noun} + (hle : n ≤ m) (h : evalN n s form = some r) : evalN m s form = some r := by + induction hle with + | refl => exact h + | step _ ih => exact evalN_succ ih + +-- main.tex:1156–1158 (fuel-indexed shadow of `Thm:deterministic_Trace`) +/-- **`evalN` is a partial function (output uniqueness).** Two successful evaluations of the + same `subject`/`formula`, at any fuel budgets, return the same noun. Fuel controls only + whether evaluation finishes, never the result. Proof: bump both to the common fuel + `max n m` by `evalN_mono`, then `some` is injective. -/ +theorem evalN_deterministic {n m : Nat} {s form r₁ r₂ : Noun} + (h₁ : evalN n s form = some r₁) (h₂ : evalN m s form = some r₂) : r₁ = r₂ := + Option.some.inj ((evalN_mono (Nat.le_max_left n m) h₁).symm.trans + (evalN_mono (Nat.le_max_right n m) h₂)) + +end Nock diff --git a/LeanPool/PureNock/Trace.lean b/LeanPool/PureNock/Trace.lean new file mode 100644 index 0000000000..a0f564b8f1 --- /dev/null +++ b/LeanPool/PureNock/Trace.lean @@ -0,0 +1,529 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Verb + +/-! +# Evaluation traces + +`Def:eval_trace`–`Thm:deterministic_Trace` (`main.tex:1114–1157`). +-/ + +@[expose] public section + +namespace Nock +namespace Verb + +/-! ### Trace, consistency, completeness (`Def:eval_trace`, `main.tex:1114–1127`) -/ + +-- main.tex:1118 (`Nock(tᵢ, tᵢ₊₁) = 0`) +/-- **`Def:eval_trace`** (`main.tex:1118`). + Consistent step: `Nock(tᵢ, tᵢ₊₁) = 0`. -/ +def Step (a b : Verb) : Prop := next a = some b + +/-- The transition relation is deterministic: `next` is a function. -/ +theorem Step.det {a b b' : Verb} (h : Step a b) (h' : Step a b') : b = b' := + Option.some.inj (h.symm.trans h') + +-- main.tex:1115–1118 (consistent evaluation trace) +/-- **`Def:eval_trace`** (`main.tex:1114–1127`). + Consistent: `∀ i ∈ [n], Nock(tᵢ, tᵢ₊₁) = 0`. -/ +def Consistent : List Verb → Prop + | [] => True + | [_] => True + | a :: b :: rest => Step a b ∧ Consistent (b :: rest) + +-- main.tex:1122 (complete: `tₙ` terminal or null) +/-- **`Def:eval_trace`** (`main.tex:1114–1127`). + Complete: `tₙ` is in terminal state or null. -/ +def Complete (T : List Verb) : Prop := ∃ v, T.getLast? = some v ∧ next v = none + +-- main.tex:1115–1118 (`t₀ = F` with consistency) +/-- **`Def:eval_trace`** (`main.tex:1114–1127`). + Consistent trace of `F`: `t₀ = F` and `∀ i ∈ [n], Nock(tᵢ, tᵢ₊₁) = 0`. -/ +def TraceOf (F : Verb) (T : List Verb) : Prop := T.head? = some F ∧ Consistent T + +-- main.tex:1126 (continuation) +/-- **`Def:eval_trace`** (`main.tex:1114–1127`). + If `c ∈ T(F)`, `c` is a continuation (of `F`). -/ +def Continuation (F c : Verb) : Prop := ∃ T, TraceOf F T ∧ c ∈ T + +-- main.tex:1123 (valid) +/-- **`Def:eval_trace`** (`main.tex:1114–1127`). + Valid: `T` complete and `tₙ` is not null. -/ +def Valid (F : Verb) : Prop := + ∃ T v, TraceOf F T ∧ T.getLast? = some v ∧ next v = none ∧ isTerminal v = true + +-- main.tex:1124 (invalid) +/-- **`Def:eval_trace`** (`main.tex:1114–1127`). + Invalid: `T` complete and `tₙ` is null. -/ +def Invalid (F : Verb) : Prop := + ∃ T v, TraceOf F T ∧ T.getLast? = some v ∧ next v = none ∧ isTerminal v = false + +-- main.tex:1130–1134 (Def:Nock) +/-- **`Def:Nock`** (`main.tex:1130–1134`). + `F` terminates (is finite) if `F ∈ NockVM^Λ := {F | ∃ complete T=(t₀,…,tₙ) s.t. t₀=F, λ≤Λ}`. -/ +def Finite (F : Verb) : Prop := ∃ T, TraceOf F T ∧ Complete T + +-- main.tex:1147 (`FiniteTransitions^Λ`) +/-- **`FiniteTransitions^Λ`** (`main.tex:1147`). + `FiniteTransitions^Λ := {F | F is a continuation of a finite verb and λ ≤ Λ}`. -/ +def FiniteTransition (F : Verb) : Prop := + ∃ (F0 : Verb) (T : List Verb), TraceOf F0 T ∧ Complete T ∧ F ∈ T + +/-! ### Small list helpers (Lean core only) -/ + +private theorem head?_some_mem {F : Verb} {T : List Verb} + (h : T.head? = some F) : F ∈ T := by + cases T with + | nil => simp at h + | cons a t => + simp only [List.head?_cons, Option.some.injEq] at h + subst h; simp + +private theorem getLast?_cons₂ (x y : Verb) (t : List Verb) : + (x :: y :: t).getLast? = (y :: t).getLast? := by + simp [List.getLast?_cons_cons] + +/-- Any element of a consistent trace begins a consistent *suffix* with the same last verb. -/ +private theorem consistent_suffix_of_mem {F : Verb} : + ∀ (T : List Verb), Consistent T → F ∈ T → + ∃ S, S.head? = some F ∧ Consistent S ∧ S.getLast? = T.getLast? := by + intro T + induction T with + | nil => intro _ hmem; simp at hmem + | cons a t iht => + cases t with + | nil => + intro hc hmem + simp only [List.mem_singleton] at hmem + subst hmem + exact ⟨[F], by simp, hc, rfl⟩ + | cons b t' => + intro hc hmem + obtain ⟨hab, hrest⟩ := hc + rcases List.mem_cons.1 hmem with hFa | hmem' + · subst hFa + exact ⟨F :: b :: t', by simp, ⟨hab, hrest⟩, rfl⟩ + · obtain ⟨S, hS1, hS2, hS3⟩ := iht hrest hmem' + exact ⟨S, hS1, hS2, hS3.trans (getLast?_cons₂ a b t').symm⟩ + +/-! ### `Lem:domain` (`main.tex:1141–1142`) -/ + +-- main.tex:1141–1142 (Lem:domain) +/-- **`Lem:domain`** (`main.tex:1141–1142`). + Let `c` be a continuation not followed by a null operator in a consistent evaluation + trace. Then `map(c, getIndex(c)) ∈ D(V)`. -/ +theorem domain {c t : Verb} (h : Step c t) : + ∃ i sub, getIndex c = some i ∧ map i c = some sub ∧ inDomain sub = true := by + unfold Step next at h + split at h + · simp at h + · rename_i i hg + split at h + · simp at h + · rename_i sub hm + split at h + · rename_i hd + exact ⟨i, sub, hg, hm, hd⟩ + · simp at h + +/-! ### `lemma:equiv_continuations` (`main.tex:1150–1153`) -/ + +-- main.tex:1150–1153 (NockVM^Λ ≡ FiniteTransitions^Λ) +/-- **`lemma:equiv_continuations`** (`main.tex:1150–1153`). + `NockVM^Λ ≡ FiniteTransitions^Λ`. -/ +theorem equiv_continuations (F : Verb) : Finite F ↔ FiniteTransition F := by + constructor + · rintro ⟨T, hTof, hComp⟩ + exact ⟨F, T, hTof, hComp, head?_some_mem hTof.1⟩ + · rintro ⟨F0, T, hTof, ⟨v, hlast, hnext⟩, hmem⟩ + obtain ⟨S, hS1, hS2, hS3⟩ := consistent_suffix_of_mem T hTof.2 hmem + exact ⟨S, ⟨hS1, hS2⟩, ⟨v, hS3.trans hlast, hnext⟩⟩ + +/-! ### `Thm:deterministic_Trace` (`main.tex:1156–1157`) -/ + +/-- Inductive core of determinism: `Step` is functional, so equal heads and lengths force equality. +-/ +theorem trace_unique (T1 : List Verb) : ∀ (T2 : List Verb), + Consistent T1 → Consistent T2 → T1.head? = T2.head? → T1.length = T2.length → T1 = T2 := by + induction T1 with + | nil => + intro T2 _ _ _ hl + cases T2 with + | nil => rfl + | cons _ _ => simp at hl + | cons a t1 ih => + intro T2 h1 h2 hh hl + cases T2 with + | nil => simp at hl + | cons c t2 => + have hac : a = c := by + simp only [List.head?_cons, Option.some.injEq] at hh; exact hh + subst hac + cases t1 with + | nil => + cases t2 with + | nil => rfl + | cons _ _ => simp at hl + | cons b t1' => + cases t2 with + | nil => simp at hl + | cons d t2' => + simp only [Consistent] at h1 h2 + obtain ⟨hab, hbt⟩ := h1 + obtain ⟨had, hdt⟩ := h2 + have hbd : b = d := Step.det hab had + subst hbd + have hrec : (b :: t1') = (b :: t2') := + ih (b :: t2') hbt hdt (by simp) + (by simp only [List.length_cons] at hl ⊢; omega) + exact congrArg (a :: ·) hrec + +-- main.tex:1156–1157 (Thm:deterministic_Trace — functional form) +/-- **`Thm:deterministic_Trace`** (`main.tex:1156–1157`). + If `map` enforces a strict ordering on finite verbs, then for every finite verb `F` and + integer `n`, there exists at most one consistent evaluation trace of size `n` for `F`. -/ +theorem deterministic_Trace {F : Verb} {n : Nat} {T1 T2 : List Verb} + (h1 : TraceOf F T1) (h2 : TraceOf F T2) + (hn1 : T1.length = n + 1) (hn2 : T2.length = n + 1) : T1 = T2 := + trace_unique T1 T2 h1.2 h2.2 (h1.1.trans h2.1.symm) (by omega) + +/-! ### Conditional deterministic-trace theorem (`main.tex:1156–1157`) -/ + +/-- Consistency of a trace with respect to an arbitrary transition relation `R` + (`Consistent` is the special case `R = Step`). -/ +def ConsistentR (R : Verb → Verb → Prop) : List Verb → Prop + | [] => True + | [_] => True + | a :: b :: rest => R a b ∧ ConsistentR R (b :: rest) + +/-- Trace uniqueness for **any deterministic** transition relation. Determinism of the + relation is the only hypothesis; this is the inductive core shared by the functional and + the strict-order forms. -/ +theorem trace_unique_of_det {R : Verb → Verb → Prop} + (hdet : ∀ {a b b' : Verb}, R a b → R a b' → b = b') : + ∀ (T1 T2 : List Verb), ConsistentR R T1 → ConsistentR R T2 → + T1.head? = T2.head? → T1.length = T2.length → T1 = T2 := by + intro T1 + induction T1 with + | nil => + intro T2 _ _ _ hl + cases T2 with + | nil => rfl + | cons _ _ => simp at hl + | cons a t1 ih => + intro T2 h1 h2 hh hl + cases T2 with + | nil => simp at hl + | cons c t2 => + have hac : a = c := by + simp only [List.head?_cons, Option.some.injEq] at hh; exact hh + subst hac + cases t1 with + | nil => + cases t2 with + | nil => rfl + | cons _ _ => simp at hl + | cons b t1' => + cases t2 with + | nil => simp at hl + | cons d t2' => + simp only [ConsistentR] at h1 h2 + obtain ⟨hab, hbt⟩ := h1 + obtain ⟨had, hdt⟩ := h2 + have hbd : b = d := hdet hab had + subst hbd + have hrec : (b :: t1') = (b :: t2') := + ih (b :: t2') hbt hdt (by simp) + (by simp only [List.length_cons] at hl ⊢; omega) + exact congrArg (a :: ·) hrec + +/-- A trace w.r.t. an arbitrary transition relation `R` (generalizing `TraceOf`). -/ +def TraceOfR (R : Verb → Verb → Prop) (F : Verb) (T : List Verb) : Prop := + T.head? = some F ∧ ConsistentR R T + +-- main.tex:1156–1157 (Thm:deterministic_Trace — conditional form) +/-- **`Thm:deterministic_Trace`** (`main.tex:1156–1157`). + If `map` enforces a strict ordering on finite verbs, then for every finite verb `F` and + integer `n`, there exists at most one consistent evaluation trace of size `n` for `F`. -/ +theorem selStep_deterministic_Trace {lt : Nat → Nat → Prop} (hs : StrictSelect lt) + {F : Verb} {n : Nat} {T1 T2 : List Verb} + (h1 : TraceOfR (SelStep lt) F T1) (h2 : TraceOfR (SelStep lt) F T2) + (hn1 : T1.length = n + 1) (hn2 : T2.length = n + 1) : T1 = T2 := + trace_unique_of_det (R := SelStep lt) + (fun {a b b'} (hab : SelStep lt a b) (hab' : SelStep lt a b') => SelStep.det hs hab hab') + T1 T2 h1.2 h2.2 (h1.1.trans h2.1.symm) (by omega) + +/-- The concrete consistency (`Consistent`, via `next`) is exactly `SelStep dfsLt` + consistency, because `next` realizes the DFS selection step (`next_eq_some_iff`). -/ +theorem consistent_iff_selStep : ∀ (T : List Verb), + Consistent T ↔ ConsistentR (SelStep dfsLt) T + | [] => Iff.rfl + | [_] => Iff.rfl + | a :: b :: rest => by + simp only [Consistent, ConsistentR] + exact and_congr next_eq_some_iff (consistent_iff_selStep (b :: rest)) + +-- main.tex:1156–1157 (Thm:deterministic_Trace — DFS instance) +/-- **`Thm:deterministic_Trace`** (`main.tex:1156–1157`). + If `map` enforces a strict ordering on finite verbs, then for every finite verb `F` and + integer `n`, there exists at most one consistent evaluation trace of size `n` for `F`. -/ +theorem deterministic_Trace_strict {F : Verb} {n : Nat} {T1 T2 : List Verb} + (h1 : TraceOf F T1) (h2 : TraceOf F T2) + (hn1 : T1.length = n + 1) (hn2 : T2.length = n + 1) : T1 = T2 := + selStep_deterministic_Trace strictSelect_dfs + ⟨h1.1, (consistent_iff_selStep T1).1 h1.2⟩ + ⟨h2.1, (consistent_iff_selStep T2).1 h2.2⟩ hn1 hn2 + +/-! ### Corollary: valid and invalid verbs are finite (`main.tex:1137–1138`) -/ + +-- main.tex:1137–1138 (Corr) +/-- **`Corr`** (`main.tex:1137–1138`). + All valid and invalid verbs are finite. -/ +theorem finite_of_valid {F : Verb} (h : Valid F) : Finite F := by + obtain ⟨T, v, hT, hl, hn, _⟩ := h + exact ⟨T, hT, v, hl, hn⟩ + +-- main.tex:1137–1138 (Corr) +/-- **`Corr`** (`main.tex:1137–1138`). + All valid and invalid verbs are finite. -/ +theorem finite_of_invalid {F : Verb} (h : Invalid F) : Finite F := by + obtain ⟨T, v, hT, hl, hn, _⟩ := h + exact ⟨T, hT, v, hl, hn⟩ + +-- main.tex:1137–1138 (Corr) +/-- **`Corr`** (`main.tex:1137–1138`). + All valid and invalid verbs are finite. -/ +theorem finite_of_valid_or_invalid {F : Verb} (h : Valid F ∨ Invalid F) : Finite F := + h.elim finite_of_valid finite_of_invalid + +/-! ### Size bound `λ ≤ Λ` on paper languages (`main.tex:1130–1147`) -/ + +-- main.tex:1164 (`λ = len(n) = |d|`) +/-- The paper's length `λ` of a verb: the number of leaves of its noun. -/ +def size (F : Verb) : Nat := F.leaves + +-- main.tex:1130–1134 (`NockVM^Λ`) +/-- **`Def:Nock`** (`main.tex:1130–1134`). + `NockVM^Λ := {F ∈ V^λ | ∃ complete T with t₀=F, and λ ≤ Λ}`. -/ +def FiniteΛ (Λ : Nat) (F : Verb) : Prop := Finite F ∧ size F ≤ Λ + +-- main.tex:1147 (`FiniteTransitions^Λ`) +/-- **`FiniteTransitions^Λ`** (`main.tex:1147`). + `FiniteTransitions^Λ := {F | F is a continuation of a finite verb and λ ≤ Λ}`. -/ +def FiniteTransitionΛ (Λ : Nat) (F : Verb) : Prop := FiniteTransition F ∧ size F ≤ Λ + +-- main.tex:1150–1153 (`NockVM^Λ ≡ FiniteTransitions^Λ`) +/-- **`lemma:equiv_continuations`** (`main.tex:1150–1153`). + `NockVM^Λ ≡ FiniteTransitions^Λ`. -/ +theorem equiv_continuationsΛ (Λ : Nat) (F : Verb) : FiniteΛ Λ F ↔ FiniteTransitionΛ Λ F := + and_congr (equiv_continuations F) Iff.rfl + +/-! ### `NockProgram^Λ` / `NockValidProgram^Λ` (`main.tex:1090–1149`) -/ + +-- main.tex:1090–1098 (Def:NockProgram) +/-- **`Def:NockProgram`** (`main.tex:1090–1098`). + `F = *[subject, formula]` with subject, formula terminal; `NockProgram^Λ := {F | λ ≤ Λ}`. -/ +def IsNockProgram (Λ : Nat) (F : Verb) : Prop := + (∃ subject formula, F = .node .star subject formula + ∧ isTerminal subject = true ∧ isTerminal formula = true) + ∧ size F ≤ Λ + +-- main.tex:1145 (`NockValidProgram^Λ := {F ∈ NockProgram^Λ | F is a valid verb}`) +/-- **`F ∈ NockValidProgram^Λ`** (main.tex:1145): a Nock program that is a *valid* verb. -/ +def NockValidProgramΛ (Λ : Nat) (F : Verb) : Prop := IsNockProgram Λ F ∧ Valid F + +/-- Subset of verb-languages (`⊆`): every verb in `P` is in `Q`. -/ +def SubLang (P Q : Verb → Prop) : Prop := ∀ ⦃F⦄, P F → Q F + +/-- **Strict inclusion of verb-languages (`⊊`)**: `P ⊆ Q` together with a witness in `Q ∖ P`. + This is exactly the meaning of the set-theoretic `⊊` (proper subset = subset + a strictly + separating element), spelled out at the predicate level. -/ +def SSubLang (P Q : Verb → Prop) : Prop := SubLang P Q ∧ ∃ F, Q F ∧ ¬ P F + +/-- A terminal leaf is finite: its one-element trace `[leaf n]` is complete. -/ +theorem finite_leaf (n : Nat) : Finite (.leaf n) := + ⟨[.leaf n], ⟨rfl, trivial⟩, .leaf n, rfl, next_none_of_terminal rfl⟩ + +-- main.tex:1145 (`NockValidProgram^Λ ⊊ NockVM^Λ`) +/-- **Strict inclusion (main.tex:1145).** `NockValidProgram^Λ ⊊ NockVM^Λ`: every valid program + is finite of length `≤ Λ` (`⊆`, via `finite_of_valid`), and the containment is *strict* — + e.g. the terminal leaf `0` is a finite verb of length `1 ≤ Λ` that is **not** a Nock program + (it is not of the form `*[subject, formula]`; main.tex:1145 "not all elements of `NockVM^Λ` + are actually programs"). The side condition `1 ≤ Λ` is *necessary*: for `Λ = 0` both + languages are empty (every verb has `≥ 1` leaf), so `∅ ⊊ ∅` fails; the paper's `⊊` is meant + for the non-degenerate range. -/ +theorem nockValidProgram_ssubset_nockVM {Λ : Nat} (hΛ : 1 ≤ Λ) : + SSubLang (NockValidProgramΛ Λ) (FiniteΛ Λ) := by + refine ⟨?_, .leaf 0, ⟨finite_leaf 0, ?_⟩, ?_⟩ + · intro F hF + exact ⟨finite_of_valid hF.2, hF.1.2⟩ + · show size (.leaf 0) ≤ Λ + simpa [size, leaves] using hΛ + · rintro ⟨⟨⟨s, f, hEq, _, _⟩, _⟩, _⟩ + simp at hEq + +/-! ### `NockVMROM^Λ` at the verb level (main.tex:1160) -/ + +-- main.tex:1160 (a single consistent ROM transition) +/-- A single `NockVMROM` step (`next` restricted to exclude `OP₁₀`). -/ +def StepROM (a b : Verb) : Prop := nextROM a = some b + +/-- Every ROM step is a Nock step (`nextROM ⊆ next`; main.tex:1160). -/ +theorem step_of_stepROM {a b : Verb} (h : StepROM a b) : Step a b := next_of_nextROM h + +-- main.tex:1160 (ROM completeness: `tₙ` has no ROM successor) +/-- A trace is **ROM-complete** iff its last verb has no `nextROM`-successor. -/ +def CompleteROM (T : List Verb) : Prop := ∃ v, T.getLast? = some v ∧ nextROM v = none + +-- main.tex:1160 (a consistent ROM trace of `F`) +/-- `T` is a consistent ROM trace of `F` (uses `StepROM` for consistency). -/ +def TraceOfROM (F : Verb) (T : List Verb) : Prop := T.head? = some F ∧ ConsistentR StepROM T + +/-- Every consistent ROM trace is a consistent Nock trace (ROM steps are Nock steps). -/ +theorem consistent_of_consistentROM : ∀ (T : List Verb), ConsistentR StepROM T → Consistent T + | [], _ => trivial + | [_], _ => trivial + | _ :: _ :: rest, h => ⟨step_of_stepROM h.1, consistent_of_consistentROM (_ :: rest) h.2⟩ + +/-- Every ROM trace of `F` is a Nock trace of `F`. -/ +theorem traceOf_of_traceOfROM {F : Verb} {T : List Verb} (h : TraceOfROM F T) : TraceOf F T := + ⟨h.1, consistent_of_consistentROM T h.2⟩ + +-- main.tex:1160 (`NockVMROM`: verbs with a complete ROM trace) +/-- `F` is **ROM-finite** (`F ∈ NockVMROM`): it has a complete ROM trace. -/ +def FiniteROM (F : Verb) : Prop := ∃ T, TraceOfROM F T ∧ CompleteROM T + +-- main.tex:1160 + 1130-1133 (`NockVMROM^Λ`: ROM-finite verbs of length `λ ≤ Λ`) +/-- `F ∈ NockVMROM^Λ`: ROM-finite *and* of length `λ ≤ Λ` (Def:Nock without `OP₁₀`). -/ +def FiniteROMΛ (Λ : Nat) (F : Verb) : Prop := FiniteROM F ∧ size F ≤ Λ + +-- main.tex:1123,1160 (a ROM-*valid* verb: ROM-complete trace ending in terminal state) +/-- `F` is **ROM-valid**: it has a complete ROM trace ending in a terminal (non-crash) verb. -/ +def ValidROM (F : Verb) : Prop := + ∃ T v, TraceOfROM F T ∧ T.getLast? = some v ∧ nextROM v = none ∧ isTerminal v = true + +/-- **ROM-valid ⇒ finite.** A verb that terminates in a terminal state using only ROM steps + is Nock-finite: its ROM trace is a Nock trace (`traceOf_of_traceOfROM`), and its terminal + last verb has no Nock successor either (`next_none_of_terminal`), so the trace is + Nock-complete. -/ +theorem finite_of_validROM {F : Verb} (h : ValidROM F) : Finite F := by + obtain ⟨T, v, hT, hl, _, hterm⟩ := h + exact ⟨T, traceOf_of_traceOfROM hT, v, hl, next_none_of_terminal hterm⟩ + +/-! ### The two-verb checker `Nock` matches `Step` (App A, main.tex:1639) -/ + +-- main.tex:1118,1639 (`Nock(tᵢ,tᵢ₊₁)=0` ↔ `Step` ∨ crash: the checker is 1-1 with `Step`) +/-- **`Nock` checker ↔ trace consistency (`Step`) + crash.** `NockCheck a b = true` (paper's + accept `0`, main.tex:1118) iff `b` is a `Step`-successor of `a` (`Step a v`, i.e. a + consistent transition `Nock(a,v)=0`) or `a` crashes and `b = ⊥` (`b = none`). This is the + genuine biconditional making the two-verb checker 1-1 with the forward semantics `Step` + plus the crash branch (defeq to `Verb.nockCheck_eq_true_iff`, since `Step a v := next a = + some v`). -/ +theorem nockCheck_iff_step {a : Verb} {b : Option Verb} : + NockCheck a b = true ↔ (∃ v, b = some v ∧ Step a v) ∨ (b = none ∧ Crashes a) := + nockCheck_eq_true_iff + +/-! ### Verifiable crash ↔ the paper's ⊥ (`Invalid`, `main.tex:1124`) + +`Nock.Verb.eval` reports a three-way `Outcome`; a `crash` result is tied here to the paper's +program-level ⊥: the *evaluated* program `v` is `Invalid` — its trace is complete and ends at a +stuck, non-terminal state (`main.tex:1124`). Soundness (`eval_crash_invalid`), completeness +(`eval_crash_complete`), and their biconditional (`eval_crash_iff_invalid`) pin `eval`'s crash to +exactly that object; `eval_crash_checked` further exhibits the reached ⊥ state and its +`NockCheck`-accepted crash edge. -/ + +-- main.tex:1114–1127 (a crash trace: consistent, ending stuck non-terminal, length = fuel) +/-- If `S` is a consistent trace of `u` (head `u`, `Consistent S`) whose last state `w` is a stuck + non-terminal (`next w = none`, `¬terminal`), then `eval` reports a `crash` at fuel + `S.length`. -/ +theorem crash_of_trace : + ∀ (S : List Verb) (u w : Verb), + S.head? = some u → Consistent S → S.getLast? = some w → + next w = none → isTerminal w = false → eval S.length u = .crash + | [], u, w, h, _, _, _, _ => by simp at h + | [a], u, w, hh, _, hl, hnext, hnt => by + simp only [List.head?_cons, Option.some.injEq] at hh + simp only [List.getLast?_singleton, Option.some.injEq] at hl + subst hh; subst hl + show eval 1 a = .crash + rw [eval_succ, hnext, haltOutcome_crash hnt] + | a :: b :: rest, u, w, hh, hc, hl, hnext, hnt => by + simp only [List.head?_cons, Option.some.injEq] at hh + subst hh + obtain ⟨hstep, hcrest⟩ := hc + have hlrest : (b :: rest).getLast? = some w := by + rw [List.getLast?_cons_cons] at hl; exact hl + have ih := crash_of_trace (b :: rest) b w rfl hcrest hlrest hnext hnt + show eval ((b :: rest).length + 1) a = .crash + rw [eval_succ, (hstep : next a = some b)] + exact ih + +-- main.tex:1124 (`Invalid`: complete trace ending in the null state ⊥) +/-- **Crash soundness (program-tied).** A `crash` reported by `eval` for the program `v` means `v` + is `Invalid`: its evaluation trace is complete and ends at a stuck non-terminal — the paper's ⊥ + (`main.tex:1124`). Unlike a bare "some crashing verb exists", the witness is the trace *of + `v`*. -/ +theorem eval_crash_invalid {n : Nat} {v : Verb} (h : eval n v = .crash) : Invalid v := by + induction n generalizing v with + | zero => simp [eval] at h + | succ n ih => + rw [eval_succ] at h + cases hnv : next v with + | some v' => + simp only [hnv] at h + obtain ⟨T, w, hTr, hlast, hnext, hnt⟩ := ih h + refine ⟨v :: T, w, ?_, ?_, hnext, hnt⟩ + · refine ⟨rfl, ?_⟩ + obtain ⟨hhead, hcons⟩ := hTr + cases T with + | nil => simp at hhead + | cons a t => + simp only [List.head?_cons, Option.some.injEq] at hhead + subst hhead + exact ⟨hnv, hcons⟩ + · obtain ⟨hhead, _⟩ := hTr + cases T with + | nil => simp at hhead + | cons a t => simp [List.getLast?_cons_cons] at hlast ⊢; exact hlast + | none => + simp only [hnv, haltOutcome] at h + have hnt : v.isTerminal = false := by + by_cases ht : v.isTerminal + · rw [ite_eq_left ht] at h; exact Outcome.noConfusion h + · simpa using ht + exact ⟨[v], v, ⟨rfl, trivial⟩, rfl, hnv, hnt⟩ + +-- main.tex:1124 (converse: every `Invalid` program is reported as a crash) +/-- **Crash completeness.** Every `Invalid` program (complete trace ending stuck non-terminal, + `main.tex:1124`) is eventually reported as a `crash` by `eval` — at fuel = its trace length. -/ +theorem eval_crash_complete {v : Verb} (h : Invalid v) : ∃ n, eval n v = .crash := by + obtain ⟨T, w, ⟨hhead, hcons⟩, hlast, hnext, hnt⟩ := h + exact ⟨T.length, crash_of_trace T v w hhead hcons hlast hnext hnt⟩ + +-- main.tex:1124 (`eval` crash ↔ the paper's ⊥ object) +/-- **Verifiable crash ↔ ⊥.** `eval` crashes on `v` (at some fuel) iff `v` is `Invalid` — the + clean soundness/completeness biconditional at the paper's ⊥ (`main.tex:1124`). -/ +theorem eval_crash_iff_invalid {v : Verb} : (∃ n, eval n v = .crash) ↔ Invalid v := + ⟨fun ⟨_, h⟩ => eval_crash_invalid h, eval_crash_complete⟩ + +-- main.tex:1124,1642 (the reached ⊥ state passes the transition checker) +/-- **The reported crash is a checked ⊥ (program-tied).** A `crash` for `v` exhibits the trace of + `v` reaching a state `w` that genuinely `Crashes` and whose ⊥-edge is accepted by the two-verb + checker (`NockCheck w none = true`) — verifiability tied to the *evaluated* program. -/ +theorem eval_crash_checked {n : Nat} {v : Verb} (h : eval n v = .crash) : + ∃ T w, TraceOf v T ∧ T.getLast? = some w ∧ Crashes w ∧ NockCheck w none = true := by + obtain ⟨T, w, hTr, hlast, hnext, hnt⟩ := eval_crash_invalid h + have hgi : getIndex w ≠ none := fun hg => + absurd (getIndex_none_iff_terminal.mp hg) (by simp [hnt]) + have hcr : Crashes w := ⟨hgi, hnext⟩ + exact ⟨T, w, hTr, hlast, hcr, nockCheck_eq_true_iff.mpr (Or.inr ⟨rfl, hcr⟩)⟩ + +end Verb +end Nock diff --git a/LeanPool/PureNock/TraceCertificate.lean b/LeanPool/PureNock/TraceCertificate.lean new file mode 100644 index 0000000000..c0a9ceb42b --- /dev/null +++ b/LeanPool/PureNock/TraceCertificate.lean @@ -0,0 +1,232 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Trace + +/-! +# Executable evaluation-trace certificates + +`Nock.Verb.run` (`Nock/Verb.lean`) iterates the small-step `next` to a result noun but discards +the intermediate states. This module adds `runTrace`, which returns the full list of visited +verbs, and proves it is a *certificate* of the paper's evaluation trace (`Def:eval_trace`, +`main.tex:1114–1128`; transition checker `Algorithm:Transition_Function`, `main.tex:1633–1666`): + +* `runTrace_traceOf` — the emitted list is a consistent trace of the input (`TraceOf`); +* `runTrace_complete` — it is complete (ends at a terminal/⊥ verb); +* `runTrace_adjacent_nockCheck` — every adjacent pair is accepted by the two-verb checker `Nock` + (`NockCheck _ (some _) = true`); +* `runTrace_run` — its last verb carries exactly the `run` result; +* `runTrace_unique` — the emitted trace is independent of the fuel budget. + +`runTrace_unique` is the executable realization of `Thm:deterministic_Trace` (`main.tex:1156–1158`): +because `next` is a function, the trace from a verb is forced, so any two successful runs coincide. +These are Lean consistency theorems, not numbered paper theorems. +-/ + +@[expose] public section + +namespace Nock +namespace Verb + +/-- Fuel-indexed iteration of `next` that *records* every visited verb. `some T` iff evaluation + reached a terminal/⊥ state within the budget (so `T` ends there); `none` iff fuel ran out. -/ +def runTrace : Nat → Verb → Option (List Verb) + | 0, _ => none + | fuel + 1, v => + match next v with + | some v' => (runTrace fuel v').map (fun T => v :: T) + | none => some [v] + +/-- Run a Nock program `*[subject, formula]`, recording the full evaluation trace. -/ +def runTraceProgram (fuel : Nat) (subject formula : Noun) : Option (List Verb) := + runTrace fuel (program subject formula) + +/-! ### The trace is a consistent, complete trace of the input -/ + +/-- **`runTrace` produces a consistent, complete trace headed at the input.** Bundles the three + facts carried through the fuel recursion: head `= v`, `Consistent`, and `Complete`. -/ +theorem runTrace_spec : + ∀ (fuel : Nat) (v : Verb) (T : List Verb), runTrace fuel v = some T → + TraceOf v T ∧ Complete T := by + intro fuel + induction fuel with + | zero => intro v T h; simp [runTrace] at h + | succ fuel ih => + intro v T h + simp only [runTrace] at h + cases hn : next v with + | none => + rw [hn] at h + simp only [Option.some.injEq] at h + subst h + exact ⟨⟨rfl, trivial⟩, ⟨v, rfl, hn⟩⟩ + | some v' => + rw [hn] at h + simp only [Option.map_eq_some_iff] at h + obtain ⟨T', hT', rfl⟩ := h + obtain ⟨⟨hhead, hcons⟩, hcomp⟩ := ih v' T' hT' + obtain ⟨rest, rfl⟩ : ∃ rest, T' = v' :: rest := by + cases T' with + | nil => simp at hhead + | cons a t => + simp only [List.head?_cons, Option.some.injEq] at hhead + exact ⟨t, by rw [hhead]⟩ + refine ⟨⟨rfl, ⟨hn, hcons⟩⟩, ?_⟩ + obtain ⟨w, hw, hwn⟩ := hcomp + exact ⟨w, by rw [List.getLast?_cons_cons, hw], hwn⟩ + +/-- **The emitted list is a consistent trace of the input** (`TraceOf`, `main.tex:1114–1118`). -/ +theorem runTrace_traceOf {fuel : Nat} {v : Verb} {T : List Verb} + (h : runTrace fuel v = some T) : TraceOf v T := (runTrace_spec fuel v T h).1 + +/-- **The emitted trace is complete** (`main.tex:1122`): it ends at a terminal or ⊥ verb. -/ +theorem runTrace_complete {fuel : Nat} {v : Verb} {T : List Verb} + (h : runTrace fuel v = some T) : Complete T := (runTrace_spec fuel v T h).2 + +/-! ### Every adjacent pair is accepted by the transition checker `Nock` -/ + +/-- Every consecutive pair of a list is accepted by the paper's two-verb checker `Nock` + (`NockCheck _ (some _) = true`). Mirrors the shape of `Consistent`. -/ +def AdjNockCheck : List Verb → Prop + | [] => True + | [_] => True + | a :: b :: rest => NockCheck a (some b) = true ∧ AdjNockCheck (b :: rest) + +/-- A consistent trace is accepted pairwise by the transition checker: every `Step a b` + (`next a = some b`) makes `NockCheck a (some b) = true` (`nockCheck_eq_true_iff`). -/ +theorem adjNockCheck_of_consistent : ∀ (T : List Verb), Consistent T → AdjNockCheck T + | [], _ => trivial + | [_], _ => trivial + | a :: b :: rest, h => by + obtain ⟨hstep, hcons⟩ := h + exact ⟨nockCheck_eq_true_iff.mpr (Or.inl ⟨b, rfl, hstep⟩), + adjNockCheck_of_consistent (b :: rest) hcons⟩ + +/-- **Executable** two-verb transition checker over a whole trace: `Bool`-valued reflection of + `AdjNockCheck`, so the certificate can actually be *run* (and extracted). -/ +def adjNockCheckB : List Verb → Bool + | [] => true + | [_] => true + | a :: b :: rest => NockCheck a (some b) && adjNockCheckB (b :: rest) + +/-- `adjNockCheckB` decides `AdjNockCheck`: the executable checker returns `true` iff every + adjacent pair passes the paper's transition checker `Nock`. -/ +theorem adjNockCheckB_iff : ∀ (T : List Verb), adjNockCheckB T = true ↔ AdjNockCheck T + | [] => by simp [adjNockCheckB, AdjNockCheck] + | [_] => by simp [adjNockCheckB, AdjNockCheck] + | _ :: b :: rest => by + simp only [adjNockCheckB, AdjNockCheck, Bool.and_eq_true] + exact and_congr Iff.rfl (adjNockCheckB_iff (b :: rest)) + +-- main.tex:1633–1666 (Algorithm:Transition_Function accepts every emitted step) +/-- **Every adjacent pair of the emitted trace passes the transition checker `Nock`.** The + certificate is checkable by the paper's `Algorithm:Transition_Function`. -/ +theorem runTrace_adjacent_nockCheck {fuel : Nat} {v : Verb} {T : List Verb} + (h : runTrace fuel v = some T) : AdjNockCheck T := + adjNockCheck_of_consistent T (runTrace_traceOf h).2 + +/-- **The executable checker accepts every emitted trace.** Running `adjNockCheckB` on the + output of `runTrace` returns `true` — the certificate is machine-checkable, not just a + `Prop`. -/ +theorem runTrace_adjNockCheckB {fuel : Nat} {v : Verb} {T : List Verb} + (h : runTrace fuel v = some T) : adjNockCheckB T = true := + (adjNockCheckB_iff T).mpr (runTrace_adjacent_nockCheck h) + +/-! ### The last verb carries the `run` result -/ + +/-- **The emitted trace's last verb carries exactly the `run` result.** `run fuel v` is the + `result` (terminal noun, or `none` on crash) of the trace's final verb. -/ +theorem runTrace_run : + ∀ (fuel : Nat) (v : Verb) (T : List Verb), runTrace fuel v = some T → + ∃ w, T.getLast? = some w ∧ run fuel v = result w := by + intro fuel + induction fuel with + | zero => intro v T h; simp [runTrace] at h + | succ fuel ih => + intro v T h + simp only [runTrace] at h + cases hn : next v with + | none => + rw [hn] at h + simp only [Option.some.injEq] at h + subst h + exact ⟨v, rfl, by simp only [run, hn]⟩ + | some v' => + rw [hn] at h + simp only [Option.map_eq_some_iff] at h + obtain ⟨T', hT', rfl⟩ := h + obtain ⟨w, hw, hrun⟩ := ih v' T' hT' + obtain ⟨rest, rfl⟩ : ∃ rest, T' = v' :: rest := by + have hhead := (runTrace_spec fuel v' T' hT').1.1 + cases T' with + | nil => simp at hhead + | cons a t => + simp only [List.head?_cons, Option.some.injEq] at hhead + exact ⟨t, by rw [hhead]⟩ + refine ⟨w, ?_, ?_⟩ + · rw [List.getLast?_cons_cons]; exact hw + · simp only [run, hn]; exact hrun + +/-! ### The emitted trace is independent of the fuel budget -/ + +-- main.tex:1156–1158 (executable realization of Thm:deterministic_Trace) +/-- **The emitted trace is unique.** Because `next` is a function, any two successful runs of + `runTrace` on the same verb — at any fuel budgets — produce the same trace. This is the + executable form of `Thm:deterministic_Trace` (`main.tex:1156–1158`). -/ +theorem runTrace_unique : + ∀ (fuel₁ fuel₂ : Nat) (v : Verb) (T₁ T₂ : List Verb), + runTrace fuel₁ v = some T₁ → runTrace fuel₂ v = some T₂ → T₁ = T₂ := by + intro fuel₁ + induction fuel₁ with + | zero => intro fuel₂ v T₁ T₂ h₁ _; simp [runTrace] at h₁ + | succ f₁ ih => + intro fuel₂ v T₁ T₂ h₁ h₂ + cases fuel₂ with + | zero => simp [runTrace] at h₂ + | succ f₂ => + simp only [runTrace] at h₁ h₂ + cases hn : next v with + | none => + rw [hn] at h₁ h₂ + simp only [Option.some.injEq] at h₁ h₂ + rw [← h₁, ← h₂] + | some v' => + rw [hn] at h₁ h₂ + simp only [Option.map_eq_some_iff] at h₁ h₂ + obtain ⟨T₁', hT₁', rfl⟩ := h₁ + obtain ⟨T₂', hT₂', rfl⟩ := h₂ + rw [ih f₂ v' T₁' T₂' hT₁' hT₂'] + +-- main.tex:1122–1124, 1633–1666 (the completion edge: valid terminal, or a checked crash to ⊥) +/-- **The completion edge is accounted for.** The emitted trace's last verb is either terminal + (a *valid* completion, `main.tex:1123`) or crashes, and in the crash case the transition to `⊥` + passes the checker (`NockCheck w none = true`, `main.tex:1124`, `1644`, `1650`). Together with + `runTrace_adjacent_nockCheck`, every edge of the certificate — including the final ⊥ edge on a + crash — is `Nock`-checked; there is no unchecked step. -/ +theorem runTrace_final_edge {fuel : Nat} {v : Verb} {T : List Verb} + (h : runTrace fuel v = some T) : + ∃ w, T.getLast? = some w ∧ (isTerminal w = true ∨ NockCheck w none = true) := by + obtain ⟨w, hw, hwn⟩ := runTrace_complete h + refine ⟨w, hw, ?_⟩ + by_cases ht : isTerminal w = true + · exact Or.inl ht + · refine Or.inr (nockCheck_eq_true_iff.mpr (Or.inr ⟨rfl, ?_⟩)) + have hgi : getIndex w ≠ none := fun hg => ht (getIndex_none_iff_terminal.mp hg) + exact ⟨hgi, hwn⟩ + +/-- **`runTraceProgram` emits a certified trace of the program.** Its head is the program verb, + it is consistent and complete, every adjacent step passes the checker `Nock`, and the final + state is terminal or has a crash edge accepted by the checker. -/ +theorem runTraceProgram_certificate {fuel : Nat} {subject formula : Noun} {T : List Verb} + (h : runTraceProgram fuel subject formula = some T) : + TraceOf (program subject formula) T ∧ Complete T ∧ AdjNockCheck T ∧ + (∃ w, T.getLast? = some w ∧ (isTerminal w = true ∨ NockCheck w none = true)) := + ⟨runTrace_traceOf h, runTrace_complete h, runTrace_adjacent_nockCheck h, runTrace_final_edge h⟩ + +end Verb +end Nock diff --git a/LeanPool/PureNock/Tree.lean b/LeanPool/PureNock/Tree.lean new file mode 100644 index 0000000000..b6bdeac974 --- /dev/null +++ b/LeanPool/PureNock/Tree.lean @@ -0,0 +1,130 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +/-! +# Nouns + +Inductive proper binary trees with `Nat` atoms (`main.tex:318–349`). +-/ + +@[expose] public section + +namespace Nock + +-- main.tex:347–349 (Def:noun) +/-- **`Def:noun`** (`main.tex:347–349`). + `n := (t, dt) ∈ L_dk^{2·(λ−1)} × DT(F)^λ := N^λ(F)` is a noun of size `λ` over field `F`. + (paper: field F; Lean: Nat leaves.) -/ +inductive Noun where + | atom : Nat → Noun + | cell : Noun → Noun → Noun +deriving Repr, DecidableEq, Inhabited + +namespace Noun + +/-- `?` in Nock terms: is this noun a cell? -/ +def isCell : Noun → Bool + | .cell _ _ => true + | .atom _ => false + +/-- Test whether a noun is an atomic value rather than a cell. -/ +def isAtom (n : Noun) : Bool := !n.isCell + +-- main.tex:329–331 (Def:tree_nodes) +/-- **`Def:tree_nodes`** (`main.tex:329–331`). + Given `t ∈ L_full_BT`, a node with no children is a leaf; a non-leaf is an internal node. -/ +def leaves : Noun → Nat + | .atom _ => 1 + | .cell l r => l.leaves + r.leaves + +/-- Total node count. -/ +def size : Noun → Nat + | .atom _ => 1 + | .cell l r => 1 + l.size + r.size + +-- main.tex:329–331 (Def:tree_nodes) +/-- **`Def:tree_nodes`** (`main.tex:329–331`). + Given `t ∈ L_full_BT`, a node with no children is a leaf; a non-leaf is an internal node. -/ +def internal : Noun → Nat + | .atom _ => 0 + | .cell l r => 1 + l.internal + r.internal + +/-! ### `Lem:pbt_size` (main.tex:333–335) -/ + +-- main.tex:333–335 (Lem:pbt_size — additive form) +theorem size_add_one (n : Noun) : n.size + 1 = 2 * n.leaves := by + induction n with + | atom k => rfl + | cell l r ihl ihr => simp only [size, leaves]; omega + +-- main.tex:333–335 (Lem:pbt_size) +/-- **`Lem:pbt_size`** (`main.tex:333–335`). + A tree `t ∈ L_full_BT^{2λ−1}` has `λ−1` internal and `λ` leaf nodes. -/ +theorem pbt_size (n : Noun) : + n.size = 2 * n.leaves - 1 ∧ n.internal = n.leaves - 1 := by + have hs : n.size + 1 = 2 * n.leaves := size_add_one n + refine ⟨by omega, ?_⟩ + induction n with + | atom k => rfl + | cell l r ihl ihr => + have hl := size_add_one l + have hr := size_add_one r + simp only [internal, leaves]; omega + +-- main.tex:333–335 (Lem:pbt_size) +/-- **`Lem:pbt_size`** (`main.tex:333–335`). + A tree `t ∈ L_full_BT^{2λ−1}` has `λ−1` internal and `λ` leaf nodes. -/ +theorem pbt_size_of_size {n : Noun} {lam : Nat} (hlam : 1 ≤ lam) + (h : n.size = 2 * lam - 1) : n.leaves = lam ∧ n.internal = lam - 1 := by + have hs : n.size + 1 = 2 * n.leaves := size_add_one n + have hi : n.internal = n.leaves - 1 := (pbt_size n).2 + refine ⟨by omega, by omega⟩ + +/-- Every node of a proper binary tree is a leaf or an internal node: + `size = leaves + internal`. -/ +theorem size_eq_leaves_add_internal (n : Noun) : n.size = n.leaves + n.internal := by + induction n with + | atom k => rfl + | cell l r ihl ihr => simp only [size, leaves, internal]; omega + +/-- Every noun has at least one leaf (atoms contribute 1; cells sum positive leaf counts). -/ +theorem one_le_leaves (n : Noun) : 1 ≤ n.leaves := by + induction n with + | atom k => simp [leaves] + | cell l r ihl ihr => simp only [leaves]; omega + +end Noun + +/-! The circular S-expression object at `main.tex:305–312` is out of scope: +the paper restricts subsequent use to proper binary trees. -/ + +/-! ### Proper n-ary trees (`main.tex:351–364`) + +The paper includes this unused generalization for completeness. -/ + +-- main.tex:355–363 (Def: Proper n-ary Tree) +/-- **`Def` proper n-ary** (`main.tex:355–363`). + `L_full_T,n = { t ∈ T^n | (size(t)=1) ∪ ((⋂_{1≤i≤n} child_i(t) ∈ L_full_T^n) ∩ (∅ = + ⋂_{1≤i≤n} child_i(t))) }`; + `L^λ_full_T,n = { t ∈ L_full_T^n | size(t)=λ }`. + Unused in paper body. -/ +inductive NAryTree (k : Nat) where + | leaf : NAryTree k + | node : (Fin k → NAryTree k) → NAryTree k + +namespace NAryTree + +-- main.tex:353 (`child_i : T^n → T^n`) +/-- Return the indexed child of an internal n-ary tree, or none at a leaf. -/ +def childi {k : Nat} (i : Fin k) : NAryTree k → Option (NAryTree k) + | .leaf => none + | .node f => some (f i) + +end NAryTree + +end Nock diff --git a/LeanPool/PureNock/Verb.lean b/LeanPool/PureNock/Verb.lean new file mode 100644 index 0000000000..703dde9f0d --- /dev/null +++ b/LeanPool/PureNock/Verb.lean @@ -0,0 +1,1081 @@ +/- +Copyright (c) 2026 Pure Nock formalization contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Pure Nock formalization contributors +-/ + +module + +public import LeanPool.PureNock.Tree +public import LeanPool.PureNock.Reference + +/-! +# Verbs and local transitions + +`Def:verb`–selection (`main.tex:1034–1112`); `D`/OP (`1055–1076`); +`Def:NockProgram` (`1090–1098`); operators (`1514–1626`); `Nock` (`1639–1661`). +-/ + +@[expose] public section + +namespace Nock + +-- main.tex:1034–1040 (Def:verb — alphabet) +/-- **`Def:verb`** (`main.tex:1034–1040`). + Alphabet `X := ⟨?,=,/,#,−⟩`; actions from `X ∪ {*,⊥}`. -/ +inductive Action where + | null -- ⊥ + | star -- * + | wut -- ? + | equal -- = + | slot -- / + | edit -- # + | minus -- - +deriving Repr, DecidableEq, Inhabited + +/-- Is an action the null element `⊥`? -/ +def Action.isNull : Action → Bool + | .null => true + | _ => false + +/-- Is an action one of the operators in `X` (as opposed to `⊥` or `*`)? -/ +def Action.isOperator : Action → Bool + | .null => false + | .star => false + | _ => true + +-- main.tex:1034–1040 (Def:verb) +/-- **`Def:verb`** (`main.tex:1034–1040`). + `verb := (actions, noun) ∈ (X ∪ {*,⊥})^{λ−1} × N^λ(F)`; terminal iff actions all `⊥`. -/ +inductive Verb where + | leaf : Nat → Verb + | node : Action → Verb → Verb → Verb +deriving Repr, DecidableEq, Inhabited + +namespace Verb + +/-- The underlying noun (`snd` of the verb pair): forget the action labels. -/ +def noun : Verb → Noun + | .leaf n => .atom n + | .node _ l r => .cell l.noun r.noun + +/-- Number of leaves `λ` of the verb's noun. -/ +def leaves : Verb → Nat + | .leaf _ => 1 + | .node _ l r => l.leaves + r.leaves + +/-- `type` (main.tex:1045): is the underlying noun a cell? -/ +def isCell : Verb → Bool + | .node _ _ _ => true + | .leaf _ => false + +/-- Root action, if the verb is a cell (`none` at a leaf — leaves carry no action). -/ +def rootAction : Verb → Option Action + | .node a _ _ => some a + | .leaf _ => none + +/-- Is the root action a pending `*`? -/ +def isStar : Verb → Bool + | .node .star _ _ => true + | _ => false + +/-- Does the root node carry a *pending* (non-`⊥`) action? `*` or any operator counts; + a leaf or a `⊥`-node does not. -/ +def isPending : Verb → Bool + | .node a _ _ => !a.isNull + | .leaf _ => false + +-- main.tex:1039 (Def:verb — terminal clause) +/-- **`Def:verb`** (`main.tex:1039`). + `verb` is (fully) executed or in terminal state if its `actions` array consists only of `⊥`. -/ +def isTerminal : Verb → Bool + | .leaf _ => true + | .node a l r => a.isNull && isTerminal l && isTerminal r + +/-! ### Selection (`main.tex:1103–1112`) -/ + +-- main.tex:1103 (DFS enumeration of internal nodes) +/-- **Selection** (`main.tex:1103–1112`). + `map := DFS`; index of each internal node is the order a DFS from the root first explores + it, excluding leaves. -/ +def enum : Verb → Nat → Nat × List (Nat × Verb) + | .leaf _, c => (c, []) + | .node a l r, c => + let e1 := enum l (c + 1) + let e2 := enum r e1.1 + (e2.1, (c, .node a l r) :: (e1.2 ++ e2.2)) + +/-- All `(index, subverb)` pairs of internal nodes, in DFS pre-order. -/ +def nodes (v : Verb) : List (Nat × Verb) := (enum v 0).2 + +/-- Find the verb at an index in a list of enumerated internal nodes. -/ +def findAt : Nat → List (Nat × Verb) → Option Verb + | _, [] => none + | i, (j, v) :: t => if i = j then some v else findAt i t + +-- main.tex:1103 (`map := DFS`) +/-- **Selection** (`main.tex:1103–1112`). + `map := DFS`: returns the verb at internal-node index `i ∈ [λ−1]`. -/ +def map (i : Nat) (v : Verb) : Option Verb := findAt i (nodes v) + +/-- Greatest index of a pending verb in an enumerated list of internal nodes. -/ +def maximumPendingIndex : List (Nat × Verb) → Option Nat + | [] => none + | (i, v) :: t => + let rest := maximumPendingIndex t + if v.isPending then + some (match rest with | none => i | some j => Nat.max i j) + else rest + +-- main.tex:1109–1112 (`getIndex^Λ(F) = argmax …`; `F₁ >_F F₂ ⇔ i₁ > i₂`) +/-- **Selection** (`main.tex:1103–1112`). + `getIndex^Λ(F) = argmax_{map(i,F) ∈ NockProgram^Λ}(map(i,F))`; `F₁ >_F F₂ ⇔ i₁ > i₂`. -/ +def getIndex (v : Verb) : Option Nat := maximumPendingIndex (nodes v) + +/-- Rebuild `v`, replacing the internal node at DFS index `i` by `new`. -/ +def replaceAux (i : Nat) (new : Verb) : Verb → Nat → Nat × Verb + | .leaf n, c => (c, .leaf n) + | .node a l r, c => + let e1 := replaceAux i new l (c + 1) + let e2 := replaceAux i new r e1.1 + (e2.1, if c = i then new else .node a e1.2 e2.2) + +/-- Splice: replace the subverb at DFS index `i` with `new`. -/ +def replaceAt (i : Nat) (v new : Verb) : Verb := (replaceAux i new v 0).2 + +/-! ### Computable opcode domain `D` (`main.tex:1055–1083`) -/ + +/-- `type(i) = atom ∧ i ∈ [11]`, or `type(i) = cell` (⇒ the `cons` opcode). -/ +def opHeadOk : Verb → Bool + | .leaf k => k < 11 + | .node _ _ _ => true + +-- main.tex:1055–1058, 1078–1083 (computable opcode domain `D`) +/-- **`Algorithm:Opcode_Updates_verb`** domain `D` (`main.tex:1055–1058`, `1078–1083`). + `D := {v := (a,n) | n := [n₁,[i,n₂]], (i∈[11]) ∪ (type(i)=cell), a₁=⟨*⟩, ∀j≠1 aⱼ=⟨⊥⟩}`; + (1) `v := *[n₁,[i,n₂]]`; (2) `n₁,[i,n₂]` terminal; (3) `i` atom in `[11]` or cell. -/ +def inOpcodeDomain : Verb → Bool + | .node .star n1 (.node ra i n2) => + isTerminal n1 + && ra.isNull + && isTerminal i && isTerminal n2 && opHeadOk i + | _ => false + +/-- A node ready for an **operator-application step**: it carries an operator from `X` + and both of its argument subtrees are already in terminal state (note 2b). These are + the operator redexes that the paper's App A transition algorithm elides. -/ +def opReady : Verb → Bool + | .node a l r => a.isOperator && isTerminal l && isTerminal r + | .leaf _ => false + +/-- The full domain of the transition function `next`: opcode redexes (`inOpcodeDomain`, + = the paper's `D`) together with ready operator nodes (`opReady`). See note 2c; + `Trace.domain` (`Lem:domain`) concludes membership here. -/ +def inDomain (v : Verb) : Bool := inOpcodeDomain v || opReady v + +/-! ### Opcode local rewrite (`main.tex:1060–1076`) -/ +-- main.tex:1060–1076 (Algorithm:Opcode_Updates_verb) +/-- **`Algorithm:Opcode_Updates_verb`** (`main.tex:1060–1076`). + `OP₀: *[n₁,[0,n₂]] → /(n₂,n₁)`; `OP₁ → n₂`; `OP₂ → *[*[n₁,h],*[n₁,t]]`; + `OP₃ → -[3,*[n₁,n₂]]`; `OP₄ → -[4,*[n₁,n₂]]`; `OP₅ → =(*[n₁,h],*[n₁,t])`; + `OP₆ → *[n₁,*[t,[0,*[[2,3],[0,*[n₁,[4,[4,h]]]]]]]]`; `OP₇ → *[*[n₁,h],t]`; + `OP₈ → *[[*[n₁,h],n₁],t]`; `OP₉ → *[*[n₁,t],2,[[0,1],0,h]]`; + `OP₁₀ → #[hh,*[n₁,th],*[n₁,t]]`; `cons: *[n₁,[i,n₂]] → [*[n₁,i],*[n₁,n₂]]`. -/ +def redex : Verb → Option Verb + | .node .star n1 (.node _ i n2) => + match i with + | .node _ _ _ => -- main.tex:1075 cons (type(i)=cell) + some (.node .null (.node .star n1 i) (.node .star n1 n2)) + | .leaf op => + match op with + | 0 => -- main.tex:1061 OP₀ : → /(n₂,n₁) + some (.node .slot n2 n1) + | 1 => some n2 -- main.tex:1062 OP₁ : → n₂ + | 2 => match n2 with -- main.tex:1063 OP₂ : → *[*[n₁,h],*[n₁,t]] + | .node _ b c => + some (.node .star (.node .star n1 b) (.node .star n1 c)) + | _ => none + | 3 => -- main.tex:1064 OP₃ : → -[3, *[n₁,n₂]] = + -- ?(*[n₁,n₂]) + some (.node .minus (.leaf 3) (.node .star n1 n2)) + | 4 => -- main.tex:1065 OP₄ : → -[4, *[n₁,n₂]] = + -- +(*[n₁,n₂]) + some (.node .minus (.leaf 4) (.node .star n1 n2)) + | 5 => match n2 with -- main.tex:1066 OP₅ : → =(*[n₁,h],*[n₁,t]) + | .node _ b c => + some (.node .equal (.node .star n1 b) (.node .star n1 c)) + | _ => none + | 6 => match n2 with -- main.tex:1067 OP₆ (if-then-else macro) + | .node _ b c => + -- b = head(n₂), c = tail(n₂); + -- → *[n₁, *[c, [0, *[[2,3], [0, *[n₁,[4,[4,b]]]]]]]] + let inner := Verb.node .star n1 + (.node .null (.leaf 4) (.node .null (.leaf 4) b)) + let d0 := Verb.node .null (.leaf 0) inner + let c0 := Verb.node .star (.node .null (.leaf 2) (.leaf 3)) d0 + let b0 := Verb.node .null (.leaf 0) c0 + let a0 := Verb.node .star c b0 + some (.node .star n1 a0) + | _ => none + | 7 => match n2 with -- main.tex:1068 OP₇ : → *[*[n₁,h],t] + | .node _ b c => some (.node .star (.node .star n1 b) c) + | _ => none + | 8 => match n2 with -- main.tex:1069 OP₈ : → *[[*[n₁,h],n₁],t] + | .node _ b c => + some (.node .star (.node .null (.node .star n1 b) n1) c) + | _ => none + | 9 => match n2 with -- main.tex:1070 OP₉ : → *[*[n₁,t], 2, + -- [[0,1],0,h]] + | .node _ b c => + -- b = head(n₂), c = tail(n₂) + let core := Verb.node .star n1 c + let f01 := Verb.node .null (.leaf 0) (.leaf 1) + let f0h := Verb.node .null (.leaf 0) b + let form := Verb.node .null (.leaf 2) (.node .null f01 f0h) + some (.node .star core form) + | _ => none + | 10 => match n2 with -- main.tex:1071 OP₁₀ : → #[hh(n₂), + -- *[n₁,th(n₂)], *[n₁,t(n₂)]] + | .node _ hd d => + match hd with + | .node _ ax c => + some (.node .edit ax + (.node .null (.node .star n1 c) (.node .star n1 d))) + | _ => none + | _ => none + | _ => none -- i ∉ [11] ⇒ crash + | _ => none + +/-! ### Operator-application steps (`Algorithm:Noun_Operators` / `Unroll_Operator`) -/ + +/-- Convert a plain noun into a *terminal* verb (all actions `⊥`). -/ +def ofNoun : Noun → Verb + | .atom k => .leaf k + | .cell l r => .node .null (ofNoun l) (ofNoun r) + +/-! ### Local rewrite (`main.tex:1060–1076`, `1514–1626`) -/ + +-- main.tex:1514–1626 (Algorithm:Noun_Operators / Unroll_Operator; opcodes via redex) +/-- **`Algorithm:Noun_Operators`** / **`Unroll_Operator`** (`main.tex:1514–1626`). + Local rewrite: `*` via opcodes, or `=`, `/`, `#`, `-` (`-[3,·]=?`, `-[4,·]=+`) on terminal + args. -/ +def reduce : Verb → Option Verb + | .node .star n1 t => redex (.node .star n1 t) -- opcode step + | .node .equal l r => some (ofNoun (Noun.tis l.noun r.noun)) -- main.tex:1525 = + | .node .slot l r => -- main.tex:1537 / + match l.noun with + | .atom ax => (Noun.slot ax r.noun).map ofNoun + | _ => none + | .node .edit l r => -- main.tex:1587 # + match l.noun, r with + | .atom ax, .node _ nw od => (Noun.edit ax nw.noun od.noun).map ofNoun + | _, _ => none + | .node .minus l r => -- main.tex:1618 - + match l.noun with + | .atom 3 => some (ofNoun (Noun.wut r.noun)) -- -[3,·] = ?(·) + | .atom 4 => (Noun.lus r.noun).map ofNoun -- -[4,·] = +(·) + | _ => none + | .node .wut _ _ => none -- unreachable, see note 2a + | .node .null _ _ => none + | .leaf _ => none + +-- main.tex:1052, 1109 (`* : V → V ∪ {⊥}`, driving the next redex chosen by `getIndex`) +/-- One Nock step. Locate the pending redex of largest DFS index via `getIndex`/`map`; if it + lies in the transition domain `D` (opcode redex or ready operator), apply the local + rewrite (`reduce`) and splice the result back; otherwise crash (`none`). + `next v = none` also when `v` is terminal (no pending action). Being a Lean function, + `next` is deterministic — the concrete realization of the paper's "`*` is a + deterministic mapping between program states". -/ +def next (v : Verb) : Option Verb := + match getIndex v with + | none => none + | some i => + match map i v with + | none => none + | some sub => + if inDomain sub then + match reduce sub with + | some r => some (replaceAt i v r) + | none => none + else none + +/-! ### Executable paper interpreter — iterate `next` to a result (note 2, PLAN §2) -/ + +-- main.tex:1090–1098 (Def:NockProgram) +/-- **`Def:NockProgram`** (`main.tex:1090–1098`). + `F = *[subject, formula]` with subject, formula terminal; `NockProgram^Λ := {F | λ ≤ Λ}`. -/ +def program (subject formula : Noun) : Verb := + .node .star (ofNoun subject) (ofNoun formula) + +/-- Extract the observable output of a stuck verb: its underlying noun if it is in terminal + state (a *valid* completion, main.tex:1123), else `none` (a crash / *invalid* + completion, main.tex:1124). -/ +def result (v : Verb) : Option Noun := + if v.isTerminal then some v.noun else none + +/-- Fuel-indexed iteration of `next` from `v` to a terminal/⊥ state, returning the result + noun (`none` on crash or fuel exhaustion). Nock is Turing-complete, so fuel is + required for totality; `none` conflates a genuine crash with fuel exhaustion, exactly + as `evalN` does. -/ +def run : Nat → Verb → Option Noun + | 0, _ => none + | fuel + 1, v => + match next v with + | some v' => run fuel v' + | none => result v + +/-- Run a Nock program `*[subject, formula]` through the paper's small-step interpreter. -/ +def runProgram (fuel : Nat) (subject formula : Noun) : Option Noun := + run fuel (program subject formula) + +/-! ### Strictness of the DFS order (main.tex:1107) -/ + +/-- The DFS counter never decreases: `(enum v c).1 ≥ c`. -/ +theorem enum_ge_start (v : Verb) (c : Nat) : c ≤ (enum v c).1 := by + induction v generalizing c with + | leaf n => simp [enum] + | node a l r ihl ihr => + simp only [enum] + have h1 : c + 1 ≤ (enum l (c + 1)).1 := ihl (c + 1) + have h2 : (enum l (c + 1)).1 ≤ (enum r (enum l (c + 1)).1).1 := ihr _ + omega + +/-- Every internal-node index produced by `enum v c` lies in the half-open counter range + `[c, (enum v c).1)`. Distinct nodes therefore receive distinct indices: the DFS + `map` enforces a *strict* ordering on subtrees (main.tex:1107). -/ +theorem enum_index_range (v : Verb) (c : Nat) : + ∀ p ∈ (enum v c).2, c ≤ p.1 ∧ p.1 < (enum v c).1 := by + induction v generalizing c with + | leaf n => intro p hp; simp [enum] at hp + | node a l r ihl ihr => + intro p hp + simp only [enum, List.mem_cons, List.mem_append] at hp + have hlge : c + 1 ≤ (enum l (c + 1)).1 := enum_ge_start l (c + 1) + have hrge : (enum l (c + 1)).1 ≤ (enum r (enum l (c + 1)).1).1 := enum_ge_start r _ + rcases hp with hroot | hleft | hright + · subst hroot; simp only [enum]; omega + · have := ihl (c + 1) p hleft + simp only [enum]; omega + · have := ihr (enum l (c + 1)).1 p hright + simp only [enum]; omega + +/-! ### DFS indices are distinct (main.tex:1107) -/ + +/-- `findAt` over an append: the left list wins if it has a match. -/ +theorem findAt_append (i : Nat) : + ∀ (L1 L2 : List (Nat × Verb)), + findAt i (L1 ++ L2) = match findAt i L1 with + | some v => some v + | none => findAt i L2 + | [], L2 => by simp [findAt] + | (j, w) :: t, L2 => by + simp only [List.cons_append, findAt] + by_cases h : i = j + · simp [h] + · simp only [ite_eq_right h]; exact findAt_append i t L2 + +/-- If no pair in `L` carries index `i`, then `findAt i L = none`. -/ +theorem findAt_eq_none {i : Nat} : + ∀ {L : List (Nat × Verb)}, (∀ p ∈ L, p.1 ≠ i) → findAt i L = none + | [], _ => rfl + | (j, w) :: t, h => by + have hj : ¬ (i = j) := fun heq => (h (j, w) (by simp)) heq.symm + simp only [findAt, ite_eq_right hj] + exact findAt_eq_none (fun p hp => h p (List.mem_cons_of_mem _ hp)) + +/-- `findAt i L = some v` witnesses membership `(i, v) ∈ L`. -/ +theorem findAt_some_mem {i : Nat} {v : Verb} : + ∀ {L : List (Nat × Verb)}, findAt i L = some v → (i, v) ∈ L + | [], h => by simp [findAt] at h + | (j, w) :: t, h => by + simp only [findAt] at h + split at h + · rename_i hij + subst hij + obtain rfl := Option.some.inj h + simp + · exact List.mem_cons_of_mem _ (findAt_some_mem h) + +/-- **DFS distinctness (core).** Every pair produced by `enum` is recovered by `findAt` on + its own index: the index ranges of the two subtrees (`enum_index_range`) are disjoint + from each other and from the root counter, so no two pairs share an index. -/ +private theorem enum_findAt (v : Verb) (c : Nat) : + ∀ p ∈ (enum v c).2, findAt p.1 (enum v c).2 = some p.2 := by + induction v generalizing c with + | leaf n => intro p hp; simp [enum] at hp + | node a l r ihl ihr => + intro p hp + simp only [enum, List.mem_cons, List.mem_append] at hp + have hlge : c + 1 ≤ (enum l (c + 1)).1 := enum_ge_start l (c + 1) + simp only [enum, findAt] + rcases hp with hroot | hleft | hright + · subst hroot; simp + · have hb := enum_index_range l (c + 1) p hleft + have hpc : p.1 ≠ c := by omega + rw [ite_eq_right hpc, findAt_append] + rw [ihl (c + 1) p hleft] + · have hb := enum_index_range r (enum l (c + 1)).1 p hright + have hpc : p.1 ≠ c := by omega + rw [ite_eq_right hpc, findAt_append] + have hnone : findAt p.1 (enum l (c + 1)).2 = none := by + apply findAt_eq_none + intro q hq + have := enum_index_range l (c + 1) q hq + omega + rw [hnone] + exact ihr (enum l (c + 1)).1 p hright + +/-- **`map` retrieves the unique node at an index.** If `(i, v)` is one of the DFS + internal-node pairs of `a`, then `map i a = some v`. (Uses DFS distinctness, + `enum_findAt`.) -/ +theorem map_of_mem_nodes {a : Verb} {i : Nat} {v : Verb} + (h : (i, v) ∈ nodes a) : map i a = some v := + enum_findAt a 0 (i, v) h + +/-- Membership of the retrieved node: `map i a = some v ⇒ (i, v) ∈ nodes a`. -/ +theorem mem_nodes_of_map {a : Verb} {i : Nat} {v : Verb} + (h : map i a = some v) : (i, v) ∈ nodes a := + findAt_some_mem h + +/-! ### `getIndex` = numeric argmax of pending indices (spec of `maximumPendingIndex`) -/ + +/-- `maximumPendingIndex L = none` iff no pair in `L` has a pending action. -/ +private theorem maximumPendingIndex_eq_none : + ∀ {L : List (Nat × Verb)}, + maximumPendingIndex L = none ↔ ∀ p ∈ L, p.2.isPending = false + | [] => by simp [maximumPendingIndex] + | (i, w) :: t => by + simp only [maximumPendingIndex] + by_cases hw : w.isPending = true + · rw [ite_eq_left hw] + constructor + · intro h; cases maximumPendingIndex t <;> simp at h + · intro h; exact absurd (h (i, w) (by simp)) (by simp [hw]) + · rw [ite_eq_right hw] + rw [maximumPendingIndex_eq_none] + constructor + · intro h p hp + rcases List.mem_cons.1 hp with heq | hmem + · subst heq; simpa using hw + · exact h p hmem + · intro h p hp; exact h p (List.mem_cons_of_mem _ hp) + +/-- The numeric argmax is achieved: `maximumPendingIndex L = some i` names a pending pair at `i`. -/ +private theorem maximumPendingIndex_mem : + ∀ {L : List (Nat × Verb)} {i : Nat}, + maximumPendingIndex L = some i → ∃ v, (i, v) ∈ L ∧ v.isPending = true + | [], i, h => by simp [maximumPendingIndex] at h + | (j, w) :: t, i, h => by + simp only [maximumPendingIndex] at h + by_cases hw : w.isPending = true + · rw [ite_eq_left hw] at h + cases hr : maximumPendingIndex t with + | none => rw [hr] at h; simp only [Option.some.injEq] at h; subst h; exact ⟨w, by simp, hw⟩ + | some k => + rw [hr] at h; simp only [Option.some.injEq] at h + -- h : Nat.max j k = i + rcases Nat.le_total j k with hjk | hkj + · have hmx : Nat.max j k = k := Nat.max_eq_right hjk + have hik : i = k := hmx ▸ h.symm + subst hik + obtain ⟨v, hv, hvp⟩ := maximumPendingIndex_mem hr + exact ⟨v, List.mem_cons_of_mem _ hv, hvp⟩ + · have hmx : Nat.max j k = j := Nat.max_eq_left hkj + have hij : i = j := hmx ▸ h.symm + subst hij + exact ⟨w, by simp, hw⟩ + · rw [ite_eq_right hw] at h + obtain ⟨v, hv, hvp⟩ := maximumPendingIndex_mem h + exact ⟨v, List.mem_cons_of_mem _ hv, hvp⟩ + +/-- Every pending index is `≤` the numeric argmax. -/ +private theorem maximumPendingIndex_ge : + ∀ {L : List (Nat × Verb)} {i : Nat}, + maximumPendingIndex L = some i → ∀ j v, (j, v) ∈ L → v.isPending = true → j ≤ i + | [], i, h => by simp [maximumPendingIndex] at h + | (p, w) :: t, i, h => by + intro j v hmem hvp + simp only [maximumPendingIndex] at h + by_cases hw : w.isPending = true + · rw [ite_eq_left hw] at h + cases hr : maximumPendingIndex t with + | none => + rw [hr] at h; simp only [Option.some.injEq] at h + -- h : p = i + rcases List.mem_cons.1 hmem with heq | hmem' + · simp only [Prod.mk.injEq] at heq; have hjp := heq.1; omega + · have hnone := (maximumPendingIndex_eq_none).1 hr + exact absurd (hnone (j, v) hmem') (by simp [hvp]) + | some k => + rw [hr] at h; simp only [Option.some.injEq] at h + -- h : Nat.max p k = i (kept as an opaque atom for omega) + rcases List.mem_cons.1 hmem with heq | hmem' + · simp only [Prod.mk.injEq] at heq + have hjp : j = p := heq.1 + have hle : p ≤ Nat.max p k := Nat.le_max_left p k; omega + · have hjk := maximumPendingIndex_ge hr j v hmem' hvp + have hle : k ≤ Nat.max p k := Nat.le_max_right p k; omega + · rw [ite_eq_right hw] at h + rcases List.mem_cons.1 hmem with heq | hmem' + · simp only [Prod.mk.injEq] at heq + obtain ⟨_, rfl⟩ := heq; simp [hvp] at hw + · exact maximumPendingIndex_ge h j v hmem' hvp + +/-- Characterization of the numeric argmax: if `(i, v)` is pending and no pending index + exceeds `i`, then `maximumPendingIndex L = some i`. -/ +private theorem maximumPendingIndex_of {L : List (Nat × Verb)} {i : Nat} + (hmem : ∃ v, (i, v) ∈ L ∧ v.isPending = true) + (hmax : ∀ j v, (j, v) ∈ L → v.isPending = true → j ≤ i) : + maximumPendingIndex L = some i := by + obtain ⟨v, hv, hvp⟩ := hmem + cases hm : maximumPendingIndex L with + | none => + have hnone := (maximumPendingIndex_eq_none).1 hm + exact absurd (hnone (i, v) hv) (by simp [hvp]) + | some m => + obtain ⟨w, hw, hwp⟩ := maximumPendingIndex_mem hm + have h1 : m ≤ i := hmax m w hw hwp + have h2 : i ≤ m := maximumPendingIndex_ge hm i v hv hvp + have : m = i := by omega + rw [this] + +/-! ### Strict redex-selection order (main.tex:1105-1109, 1156) -/ + +-- main.tex:1105-1107 (`>_F` a strict ordering on subtrees) +/-- A **strict selection order** on internal-node indices: a strict total order (the paper's + "strict ordering", main.tex:1107). Irreflexive + transitive ⇒ asymmetric; `total` + (connectedness) is what forbids ties, hence forces a *unique* maximum. -/ +structure StrictSelect (lt : Nat → Nat → Prop) : Prop where + irrefl : ∀ i, ¬ lt i i + trans : ∀ {i j k}, lt i j → lt j k → lt i k + total : ∀ {i j}, i ≠ j → lt i j ∨ lt j i + +/-- `i` is a *pending* internal-node index of `a`: `map i a` is a node with a pending + (non-⊥) action. -/ +def IsPendingIdx (a : Verb) (i : Nat) : Prop := + ∃ v, map i a = some v ∧ v.isPending = true + +-- main.tex:1109,1111 (`getIndex = argmax`) +/-- `i` is a **maximal** pending index of `a` under the order `lt`: a pending index that no + other pending index exceeds. This is the paper's `argmax` characterization of + `getIndex`. -/ +def IsMaxPending (lt : Nat → Nat → Prop) (a : Verb) (i : Nat) : Prop := + IsPendingIdx a i ∧ ∀ j, IsPendingIdx a j → ¬ lt i j + +-- main.tex:1052,1109 (one `*`-step: rewrite the redex chosen by the order) +/-- An **abstract redex-selection step**: pick a maximal pending index `i` under `lt`, and + if the selected subverb is in the transition domain, apply the local rewrite and splice + it back. With no strictness assumption this relation can be multi-valued (several + maximal redexes); `SelStep.det` shows a *strict* order collapses it to a function. -/ +def SelStep (lt : Nat → Nat → Prop) (a b : Verb) : Prop := + ∃ i sub r, IsMaxPending lt a i ∧ map i a = some sub ∧ inDomain sub = true + ∧ reduce sub = some r ∧ b = replaceAt i a r + +-- main.tex:2325 (uniqueness of the argmax from strictness) +/-- **Determinism from strictness.** If `lt` is a strict total order, a redex-selection + step is deterministic: `hs.total` forces the two selected indices to coincide; the rest + is functionality of `map`/`reduce`/`replaceAt`. -/ +theorem SelStep.det {lt : Nat → Nat → Prop} (hs : StrictSelect lt) {a b b' : Verb} + (h : SelStep lt a b) (h' : SelStep lt a b') : b = b' := by + obtain ⟨i, sub, r, ⟨hip, hmax⟩, hmap, _, hred, hb⟩ := h + obtain ⟨i', sub', r', ⟨hip', hmax'⟩, hmap', _, hred', hb'⟩ := h' + have hii : i = i' := by + rcases Classical.em (i = i') with heq | hne + · exact heq + · rcases hs.total hne with hlt | hlt + · exact absurd hlt (hmax i' hip') + · exact absurd hlt (hmax' i hip) + subst hii + have hsub : sub = sub' := Option.some.inj (hmap.symm.trans hmap') + subst hsub + have hr : r = r' := Option.some.inj (hred.symm.trans hred') + subst hr + exact hb.trans hb'.symm + +/-! ### DFS instantiation: the concrete order is strict, and `next` realizes `SelStep` -/ + +/-- The concrete DFS order on pre-order indices is the usual strict order on `Nat`. -/ +abbrev dfsLt : Nat → Nat → Prop := (· < ·) + +-- main.tex:1103,1107 (DFS enforces a strict ordering) +/-- The DFS index order is a strict total order. This is *why* `map = DFS` "enforces a + strict ordering on subtrees" (main.tex:1107): distinct internal nodes get distinct `Nat` + indices (`enum_index_range`/`map_of_mem_nodes`), and `Nat.lt` is a strict total order. -/ +theorem strictSelect_dfs : StrictSelect dfsLt where + irrefl i := Nat.lt_irrefl i + trans h h' := Nat.lt_trans h h' + total hne := Nat.lt_or_gt_of_ne hne + +/-- Pending-index membership, in list form. The `←` direction uses DFS distinctness + (`map_of_mem_nodes`): a pending node in the `nodes` list is retrieved by `map` on its + (unique) index. -/ +theorem isPendingIdx_iff_mem {a : Verb} {i : Nat} : + IsPendingIdx a i ↔ ∃ v, (i, v) ∈ nodes a ∧ v.isPending = true := by + constructor + · rintro ⟨v, hmap, hp⟩; exact ⟨v, mem_nodes_of_map hmap, hp⟩ + · rintro ⟨v, hmem, hp⟩; exact ⟨v, map_of_mem_nodes hmem, hp⟩ + +/-- `getIndex` is exactly the DFS argmax: `getIndex a = some i ↔ i` is the maximal pending + index under `dfsLt`. (Combines the `maximumPendingIndex` spec with DFS distinctness.) -/ +theorem getIndex_eq_some_iff {a : Verb} {i : Nat} : + getIndex a = some i ↔ IsMaxPending dfsLt a i := by + unfold getIndex IsMaxPending dfsLt + constructor + · intro h + refine ⟨?_, ?_⟩ + · obtain ⟨v, hv, hvp⟩ := maximumPendingIndex_mem h + exact isPendingIdx_iff_mem.2 ⟨v, hv, hvp⟩ + · intro j hj + rw [Nat.not_lt] + obtain ⟨w, hw, hwp⟩ := isPendingIdx_iff_mem.1 hj + exact maximumPendingIndex_ge h j w hw hwp + · rintro ⟨hip, hmax⟩ + obtain ⟨v, hv, hvp⟩ := isPendingIdx_iff_mem.1 hip + apply maximumPendingIndex_of ⟨v, hv, hvp⟩ + intro j w hjw hwp + have hj : IsPendingIdx a j := isPendingIdx_iff_mem.2 ⟨w, hjw, hwp⟩ + have := hmax j hj + omega + +-- main.tex:1052,1156 (`next` = the DFS-ordered selection step) +/-- **`next` faithfully realizes the strict DFS selection step.** This is the bridge that + makes `deterministic_Trace` an instance of the paper's conditional theorem: the concrete + interpreter's step is `SelStep` for the (strict) DFS order. -/ +theorem next_eq_some_iff {a b : Verb} : next a = some b ↔ SelStep dfsLt a b := by + constructor + · intro h + unfold next at h + split at h + · simp at h + · rename_i i hgi + split at h + · simp at h + · rename_i sub hmap + split at h + · rename_i hdom + split at h + · rename_i r hred + exact ⟨i, sub, r, getIndex_eq_some_iff.1 hgi, hmap, hdom, hred, (Option.some.inj + h).symm⟩ + · simp at h + · simp at h + · rintro ⟨i, sub, r, hmax, hmap, hdom, hred, hb⟩ + have hgi : getIndex a = some i := getIndex_eq_some_iff.2 hmax + unfold next + simp only [hgi, hmap, hdom, hred, ite_true] + rw [hb] + +/-! ### Non-strict selection witness (main.tex:2325) -/ + +/-- The empty order — *not* total, hence not a `StrictSelect`. Every index is vacuously + maximal under it. -/ +def emptyLt : Nat → Nat → Prop := fun _ _ => False + +/-- A verb with two independent, equally-maximal redexes: two copies of `*[0, [1, 5]]` + (each an `OP₁` redex reducing to `5`), spliced under a `⊥` root. -/ +def twoRedex : Verb := + .node .null + (.node .star (.leaf 0) (.node .null (.leaf 1) (.leaf 5))) + (.node .star (.leaf 0) (.node .null (.leaf 1) (.leaf 5))) + +-- main.tex:2325 (strictness essential: no ties ⇒ unique max) +/-- **Non-vacuity of strictness.** Under a non-strict order, `SelStep` is not a function: + it relates `twoRedex` to two distinct successors. Contrast `SelStep.det`, which uses + `StrictSelect.total` to collapse exactly this ambiguity. -/ +theorem selStep_multivalued_without_strictness : + ∃ (a b b' : Verb), b ≠ b' ∧ SelStep emptyLt a b ∧ SelStep emptyLt a b' := by + refine ⟨twoRedex, replaceAt 1 twoRedex (.leaf 5), replaceAt 3 twoRedex (.leaf 5), by decide, + ⟨1, _, .leaf 5, ⟨⟨_, rfl, rfl⟩, fun _ _ hf => hf⟩, rfl, by decide, rfl, rfl⟩, + ⟨3, _, .leaf 5, ⟨⟨_, rfl, rfl⟩, fun _ _ hf => hf⟩, rfl, by decide, rfl, rfl⟩⟩ + +/-! ### Terminal verbs cannot step (main.tex:1039, 1122) -/ + +/-- Every internal node produced by `enum` of a *terminal* verb is non-pending. -/ +theorem enum_nonpending_of_terminal : ∀ (v : Verb), isTerminal v = true → + ∀ (c : Nat), ∀ p ∈ (enum v c).2, p.2.isPending = false := by + intro v + induction v with + | leaf n => intro _ c p hp; simp [enum] at hp + | node a l r ihl ihr => + intro h c p hp + simp only [isTerminal, Bool.and_eq_true] at h + obtain ⟨⟨ha, hl⟩, hr⟩ := h + simp only [enum, List.mem_cons, List.mem_append] at hp + rcases hp with hroot | hleft | hright + · subst hroot; simp [isPending, ha] + · exact ihl hl (c + 1) p hleft + · exact ihr hr _ p hright + +-- main.tex:1039,1122 (a terminal verb has no pending redex to select) +/-- `getIndex` of a terminal verb is `none`: no internal node carries a pending action. -/ +theorem getIndex_none_of_terminal {v : Verb} (h : isTerminal v = true) : getIndex v = none := by + unfold getIndex nodes + rw [maximumPendingIndex_eq_none] + exact enum_nonpending_of_terminal v h 0 + +-- main.tex:1122 (`tₙ` terminal ⇒ the trace is complete: `next` bottoms out) +/-- A terminal verb cannot take a Nock step. -/ +theorem next_none_of_terminal {v : Verb} (h : isTerminal v = true) : next v = none := by + unfold next; rw [getIndex_none_of_terminal h] + +/-! ### Converses: no pending redex ⇒ terminal (main.tex:1122) -/ + +/-- **Converse of `enum_nonpending_of_terminal`.** If every internal node enumerated from `v` + is non-pending, then `v` is terminal. -/ +theorem terminal_of_enum_nonpending : + ∀ (v : Verb) (c : Nat), + (∀ p ∈ (enum v c).2, p.2.isPending = false) → isTerminal v = true := by + intro v + induction v with + | leaf k => intro c _; rfl + | node a l r ihl ihr => + intro c h + have hroot : ((c, Verb.node a l r) : Nat × Verb) ∈ (enum (Verb.node a l r) c).2 := by + simp [enum] + have hpc := h _ hroot + have ha : a.isNull = true := by + simp only [isPending] at hpc + cases hh : a.isNull with + | true => rfl + | false => rw [hh] at hpc; simp at hpc + have hl : isTerminal l = true := by + apply ihl (c + 1) + intro p hp + apply h + simp only [enum, List.mem_cons, List.mem_append] + exact Or.inr (Or.inl hp) + have hr : isTerminal r = true := by + apply ihr (enum l (c + 1)).1 + intro p hp + apply h + simp only [enum, List.mem_cons, List.mem_append] + exact Or.inr (Or.inr hp) + simp only [isTerminal, ha, hl, hr, Bool.and_self] + +-- main.tex:1039,1122 (halting condition: `getIndex F = ⊥ ⇔ F` terminal) +/-- **`getIndex` is `none` iff terminal.** The `←` is `getIndex_none_of_terminal`; the `→` + is `terminal_of_enum_nonpending` fed the `maximumPendingIndex` emptiness spec. -/ +theorem getIndex_none_iff_terminal {v : Verb} : getIndex v = none ↔ isTerminal v = true := by + constructor + · intro h + apply terminal_of_enum_nonpending v 0 + unfold getIndex nodes at h + exact maximumPendingIndex_eq_none.1 h + · exact getIndex_none_of_terminal + +/-- **Subtree enumeration embeds.** If `sub` is enumerated at index `i` within `v` (counter + `c`), then every internal node of `sub`'s own enumeration (counter `i`) also appears in + `v`'s enumeration. This is what lets a pending node *inside* the selected redex be seen as + a pending index of the whole verb. -/ +theorem enum_subtree_mem : + ∀ (v : Verb) (c i : Nat) (sub : Verb), + (i, sub) ∈ (enum v c).2 → ∀ p ∈ (enum sub i).2, p ∈ (enum v c).2 := by + intro v + induction v with + | leaf k => intro c i sub hmem; simp [enum] at hmem + | node a l r ihl ihr => + intro c i sub hmem p hp + simp only [enum, List.mem_cons, List.mem_append] at hmem + rcases hmem with hroot | hleft | hright + · simp only [Prod.mk.injEq] at hroot + obtain ⟨rfl, rfl⟩ := hroot + simpa only [enum] using hp + · have := ihl (c + 1) i sub hleft p hp + simp only [enum, List.mem_cons, List.mem_append] + exact Or.inr (Or.inl this) + · have := ihr (enum l (c + 1)).1 i sub hright p hp + simp only [enum, List.mem_cons, List.mem_append] + exact Or.inr (Or.inr this) + +-- main.tex:1109 (the argmax redex has no pending descendant ⇒ its arguments are terminal) +/-- **The selected redex has terminal children.** If `getIndex v = some i` picks the argmax + pending node `map i v = node a l r`, then `l` and `r` are terminal: any pending node below + them would have a strictly larger DFS index (`enum_index_range` + `enum_subtree_mem`), + contradicting maximality (`getIndex_eq_some_iff`). This is the shape `inDomain`/`reduce` + need at the redex. -/ +theorem children_terminal_of_getIndex {v : Verb} {i : Nat} {a : Action} {l r : Verb} + (hidx : getIndex v = some i) (hmap : map i v = some (Verb.node a l r)) : + isTerminal l = true ∧ isTerminal r = true := by + have hmax : IsMaxPending dfsLt v i := getIndex_eq_some_iff.1 hidx + have hmem : (i, Verb.node a l r) ∈ nodes v := mem_nodes_of_map hmap + have hsub : ∀ p ∈ (enum (Verb.node a l r) i).2, p ∈ nodes v := by + intro p hp; exact enum_subtree_mem v 0 i (Verb.node a l r) hmem p hp + have hnopend : ∀ p ∈ (enum (Verb.node a l r) i).2, i < p.1 → p.2.isPending = false := by + intro p hp hlt + obtain ⟨j, w⟩ := p + simp only at hlt ⊢ + cases hw : w.isPending with + | false => rfl + | true => + have hpid : IsPendingIdx v j := isPendingIdx_iff_mem.2 ⟨w, hsub (j, w) hp, hw⟩ + exact absurd hlt (hmax.2 j hpid) + refine ⟨?_, ?_⟩ + · apply terminal_of_enum_nonpending l (i + 1) + intro p hp + apply hnopend p + · simp only [enum, List.mem_cons, List.mem_append]; exact Or.inr (Or.inl hp) + · have := enum_index_range l (i + 1) p hp; omega + · apply terminal_of_enum_nonpending r (enum l (i + 1)).1 + intro p hp + apply hnopend p + · simp only [enum, List.mem_cons, List.mem_append]; exact Or.inr (Or.inr hp) + · have hge : i + 1 ≤ (enum l (i + 1)).1 := enum_ge_start l (i + 1) + have := enum_index_range r (enum l (i + 1)).1 p hp; omega + +/-! ### `NockVMROM` without OP₁₀ (main.tex:1160) -/ + +-- main.tex:1160 (the local rewrite with `OP₁₀` excluded) +/-- The ROM local rewrite: identical to `reduce` except every part of `OP₁₀` (edit, `#` — + main.tex:1071,1587) is *rejected* (`none` = crash), removing the ability to write to memory. + Two things are excluded, exactly the two halves of `OP₁₀` in our two-step encoding + (note 2b): (i) the `OP₁₀` opcode redex `*[n₁,[10,n₂]]` (`i = 10`), and (ii) the `edit` + operator node it produces (`#`, the actual memory write). No other opcode produces `edit`, + so this removes `OP₁₀` and nothing else. -/ +def reduceROM : Verb → Option Verb + | .node .star _ (.node _ (.leaf 10) _) => none -- (i) OP₁₀ opcode excluded (main.tex:1160) + | .node .edit _ _ => none -- (ii) the `#`/edit write step excluded + | v => reduce v + +-- main.tex:1160 (the transition function of `NockVMROM`) +/-- One `NockVMROM` step: exactly `next`, but using `reduceROM` for the local rewrite, so a + selected `OP₁₀` redex (or a stray `edit` node) crashes. Every non-crashing ROM step is a + genuine Nock step (`next_of_nextROM`). -/ +def nextROM (v : Verb) : Option Verb := + match getIndex v with + | none => none + | some i => + match map i v with + | none => none + | some sub => + if inDomain sub then + match reduceROM sub with + | some r => some (replaceAt i v r) + | none => none + else none + +/-- A terminal verb cannot take a ROM step either. -/ +theorem nextROM_none_of_terminal {v : Verb} (h : isTerminal v = true) : nextROM v = none := by + unfold nextROM; rw [getIndex_none_of_terminal h] + +/-- `reduceROM` refines `reduce`: whenever the ROM rewrite succeeds, the full Nock rewrite agrees + (they differ only on `OP₁₀`, where `reduceROM` returns `none`). -/ +theorem reduce_of_reduceROM {v r : Verb} (h : reduceROM v = some r) : reduce v = some r := by + unfold reduceROM at h + split at h <;> first | exact h | simp at h + +-- main.tex:1160 (NockVMROM ⊆ NockVM at the transition level: ROM steps are Nock steps) +/-- **Every ROM transition is a Nock transition.** `nextROM` is a *restriction* of `next` + (`OP₁₀` removed): if `nextROM v = some w` then `next v = some w`. This is the verb-level + sense in which `NockVMROM` is `NockVM` without `OP₁₀`. -/ +theorem next_of_nextROM {v w : Verb} (h : nextROM v = some w) : next v = some w := by + unfold nextROM at h + unfold next + cases hg : getIndex v with + | none => simp only [hg] at h ⊢; exact h + | some i => + simp only [hg] at h ⊢ + cases hm : map i v with + | none => simp only [hm] at h ⊢; exact h + | some sub => + simp only [hm] at h ⊢ + by_cases hd : inDomain sub = true + · simp only [ite_eq_left hd] at h ⊢ + cases hr : reduceROM sub with + | none => simp only [hr] at h; simp at h + | some r => + simp only [hr] at h + simp only [reduce_of_reduceROM hr] + exact h + · simp only [ite_eq_right hd] at h ⊢; exact h + +/-! ### Two-verb transition checker `Nock` (`main.tex:1639–1661`) -/ +-- main.tex:1639–1661 (Algorithm:Transition_Function) +/-- **`Algorithm:Transition_Function`** (`main.tex:1639–1661`). + `Nock(Tᵢ,Tᵢ₊₁)`: `Fᵢ ← map(Tᵢ,getIndex(Tᵢ))`; frame `Tᵢ\Fᵢ = Tᵢ₊₁\Fᵢ₊₁`; + accept (`0`) iff `Fᵢ₊₁ = OP:Fᵢ` / `cons:Fᵢ`, or `Fᵢ₊₁=⊥` when out of domain. -/ +def NockCheck (Ti : Verb) (Tnext : Option Verb) : Bool := + match getIndex Ti with + | none => false -- terminal `Ti`: no transition + | some i => + match map i Ti with + | none => false -- (unreachable: `getIndex ⇒ node`) + | some Fi => + match inDomain Fi, reduce Fi with + -- main.tex:1642,1653,1657 genuine step: frame-preserving splice of the rewrite + | true, some Fnext => decide (Tnext = some (replaceAt i Ti Fnext)) + -- main.tex:1644,1650 crash: selected redex reduces to ⊥ + | true, none => decide (Tnext = none) + -- main.tex:1643,1649 crash: selected `Fᵢ` is a non-redex / out of domain + | false, _ => decide (Tnext = none) + +-- main.tex:1643-1659 (the crash branches: selected redex has no consistent non-⊥ successor) +/-- **`Ti` crashes.** `Ti` selects a pending redex (`getIndex Ti ≠ none`, so `Ti` is not + terminal, main.tex:1039) but that redex is out of the transition domain / does not reduce, + so its only consistent successor is `⊥` (main.tex:1644,1650). Equivalently `next Ti = none` + together with a pending redex. -/ +def Crashes (Ti : Verb) : Prop := getIndex Ti ≠ none ∧ next Ti = none + +/-- `getIndex a = some i` names an actual internal node: `map i a` succeeds. (The argmax is + achieved at a pending node, which `map` retrieves by DFS distinctness.) -/ +theorem map_some_of_getIndex {a : Verb} {i : Nat} (h : getIndex a = some i) : + ∃ sub, map i a = some sub := by + obtain ⟨⟨v, hmap, _⟩, _⟩ := getIndex_eq_some_iff.1 h + exact ⟨v, hmap⟩ + +-- main.tex:1639 (the checker is 1-1 with the forward semantics `next` plus the crash branch) +/-- **`NockCheck` ↔ forward semantics + crash.** The two-verb checker accepts `(a, b)` + (returns `true` = the paper's `0`) iff `b` is a genuine one-step successor of `a` + (`next a = some v`, i.e. `Step a v`) OR `a` crashes and `b = ⊥` (`b = none`). This is a + genuine biconditional: `NockCheck` is 1-1 with `next` (on non-⊥ successors) together with + the crash branch — the checker `Nock(Tᵢ,Tᵢ₊₁)=0` and the forward map coincide exactly. -/ +theorem nockCheck_eq_true_iff {a : Verb} {b : Option Verb} : + NockCheck a b = true ↔ + (∃ v, b = some v ∧ next a = some v) ∨ (b = none ∧ Crashes a) := by + unfold NockCheck Crashes + cases hgi : getIndex a with + | none => + have hn : next a = none := by unfold next; rw [hgi] + rw [hn]; simp + | some i => + obtain ⟨Fi, hmap⟩ := map_some_of_getIndex hgi + simp only [hmap] + by_cases hd : inDomain Fi = true + · cases hr : reduce Fi with + | some Fnext => + have hnext : next a = some (replaceAt i a Fnext) := by + unfold next; simp only [hgi, hmap, ite_eq_left hd, hr] + simp only [hd] + constructor + · intro h + have hb : b = some (replaceAt i a Fnext) := by simpa using h + exact Or.inl ⟨_, hb, hnext⟩ + · rintro (⟨v, hb, hv⟩ | ⟨_, _, hcr⟩) + · rw [hnext] at hv + have : v = replaceAt i a Fnext := (Option.some.inj hv).symm + subst this; simp [hb] + · rw [hnext] at hcr; simp at hcr + | none => + have hnext : next a = none := by + unfold next; simp only [hgi, hmap, ite_eq_left hd, hr] + simp only [hd] + constructor + · intro h + have hb : b = none := by simpa using h + exact Or.inr ⟨hb, by simp, hnext⟩ + · rintro (⟨v, hb, hv⟩ | ⟨hb, _, _⟩) + · rw [hnext] at hv; simp at hv + · simp [hb] + · have hd' : inDomain Fi = false := by simpa using hd + have hnext : next a = none := by + unfold next; simp only [hgi, hmap, ite_eq_right hd] + simp only [hd'] + constructor + · intro h + have hb : b = none := by simpa using h + exact Or.inr ⟨hb, by simp, hnext⟩ + · rintro (⟨v, hb, hv⟩ | ⟨hb, _, _⟩) + · rw [hnext] at hv; simp at hv + · simp [hb] + +/-! ### Frame-invariance gate (main.tex:1642) -/ + +-- main.tex:1642 (`Tᵢ \ Fᵢ = Tᵢ₊₁ \ Fᵢ₊₁`: equal contexts outside the selected index) +/-- **Frame agreement at index `i`**: `Tj` is `Ti` with the subverb at DFS index `i` replaced + by some `Fj` — so `Ti` and `Tj` share the same frame (context) outside `i`. This is the + map-complement equality `Ti \ Fᵢ = Tj \ Fⱼ` (main.tex:1642). -/ +def FrameAgree (i : Nat) (Ti Tj : Verb) : Prop := ∃ Fj, Tj = replaceAt i Ti Fj + +-- main.tex:1642 (an accepted non-⊥ transition satisfies the frame gate) +/-- **Accepted steps pass the frame gate (main.tex:1642).** If `NockCheck` accepts a non-⊥ + successor `b` of `a`, then `b` agrees with `a` outside the selected index: `FrameAgree`. + (The genuine-step branch of `NockCheck` is exactly the frame-preserving splice.) -/ +theorem nockCheck_frameAgree {a b : Verb} (h : NockCheck a (some b) = true) : + ∃ i, getIndex a = some i ∧ FrameAgree i a b := by + rcases nockCheck_eq_true_iff.1 h with ⟨v, hbv, hnext⟩ | ⟨hb, _⟩ + · have hvb : v = b := (Option.some.inj hbv).symm + subst hvb + obtain ⟨i, sub, r, hmax, _, _, _, hb⟩ := next_eq_some_iff.1 hnext + exact ⟨i, getIndex_eq_some_iff.2 hmax, r, hb⟩ + · exact absurd hb (by simp) + +/-! ### Verifiable crash — a three-way outcome (value / crash / timeout) + +`run`/`runProgram` return `Option Noun`, collapsing a genuine crash (a stuck, non-terminal ⊥ state, +`main.tex:1124`) and fuel exhaustion into the same `none`. `eval` refines that: it mirrors `run`'s +fuel structure exactly, but at a halt (`next v = none`) it reports `value` (a terminal completion) +or +`crash` (a *checked* ⊥) instead of `some`/`none`; only a genuinely-steppable state can `timeout`. A +crash thus becomes a positive, kernel-checkable outcome, not merely the absence of a value. -/ + +/-- Result of running a verb to a normal form. -/ +inductive Outcome + | value (n : Noun) -- terminal reached: the paper's `*[s f] = n` + | crash -- stuck, non-terminal reached: the paper's `*[s f] = ⊥` (main.tex:1124) + | timeout -- fuel exhausted while the state was still steppable + deriving Repr, DecidableEq, Inhabited + +/-- The halting outcome of a stopped verb (`next v = none`): its noun if terminal (a *valid* + completion), else a `crash` (an *invalid* completion, main.tex:1124). -/ +def haltOutcome (v : Verb) : Outcome := + if v.isTerminal then .value v.noun else .crash + +/-- A non-terminal halt is a `crash`. -/ +theorem haltOutcome_crash {v : Verb} (h : v.isTerminal = false) : haltOutcome v = .crash := by + simp [haltOutcome, h] + +/-- Fuel-indexed evaluation distinguishing a crash from fuel exhaustion. Structurally identical to + `run`, so `eval` and `run` agree on values at the *same* fuel (`eval_value_iff_run`); the crash + branch turns `run`'s conflated `none` into a positive `crash`. -/ +def eval : Nat → Verb → Outcome + | 0, _ => .timeout + | fuel + 1, v => + match next v with + | some v' => eval fuel v' + | none => haltOutcome v + +/-- Run a Nock program `*[subject, formula]`, reporting value / crash / timeout. -/ +def evalProgram (fuel : Nat) (subject formula : Noun) : Outcome := + eval fuel (program subject formula) + +/-- One-step unfolding of `eval` (definitional), to avoid over-unfolding nested `eval` calls. -/ +theorem eval_succ (n : Nat) (v : Verb) : + eval (n + 1) v = match next v with | some v' => eval n v' | none => haltOutcome v := rfl + +/-- **Values are preserved exactly.** `eval` yields `value r` at fuel `n` iff the `Option`-valued + `run` yields `some r` at the same fuel — successful evaluation is unchanged. -/ +theorem eval_value_iff_run {n : Nat} {v : Verb} {r : Noun} : + eval n v = .value r ↔ run n v = some r := by + induction n generalizing v with + | zero => simp [eval, run] + | succ n ih => + rw [eval_succ, run] + cases hnv : next v with + | some v' => exact ih + | none => + simp only [haltOutcome, result] + by_cases ht : v.isTerminal + · simp [ht] + · simp [ht] + +/-- **Crash is stable one step on**: if `eval` reports `crash` at fuel `n`, it reports `crash` at + `n + 1`. (The general "any larger fuel" form is `eval_crash_mono_le`.) -/ +theorem eval_crash_mono {n : Nat} {v : Verb} (h : eval n v = .crash) : + eval (n + 1) v = .crash := by + induction n generalizing v with + | zero => simp [eval] at h + | succ n ih => + rw [eval_succ] at h ⊢ + cases hnv : next v with + | some v' => simp only [hnv] at h; exact ih h + | none => simp only [hnv] at h; exact h + +/-- **Crash is permanent** (so a single successful `eval` certifies it *at every fuel*): once `eval` + reports `crash` at fuel `n`, it reports `crash` at every `m ≥ n`. -/ +theorem eval_crash_mono_le {n m : Nat} {v : Verb} (hnm : n ≤ m) (h : eval n v = .crash) : + eval m v = .crash := by + induction hnm with + | refl => exact h + | step _ ih => exact eval_crash_mono ih + +end Verb +end Nock diff --git a/LeanPool/projects/pure-nock.yaml b/LeanPool/projects/pure-nock.yaml new file mode 100644 index 0000000000..78f480fdc1 --- /dev/null +++ b/LeanPool/projects/pure-nock.yaml @@ -0,0 +1,66 @@ +slug: pure-nock +title: Pure Nock semantics, trace certificates, and polynomial fingerprints +summary: >- + Formalizes Pure Nock nouns with natural-number leaves, operational semantics, + evaluator soundness and completeness, verifiable crash behavior, modular + evaluation and guarded + natural-number bridges, execution trace certificates, and polynomial + fingerprints over arbitrary finite fields. Distinct field-valued noun data + have bounded fingerprint collision probability, with a union bound for + tables sharing one random evaluation point. A computable Horner evaluator + agrees with the polynomial fingerprint. The language and fingerprint + closure excludes the framework's zkVM, AIR, RAP, table security theorem, + and concrete Goldilocks field. Manuscript line references concern the + upstream bundled draft v0.1.6, rather than the posted ePrint revision. +branch: computer science +entry_module: LeanPool.PureNock +authors: +- Pure Nock formalization contributors +contributors: +- Assimakis A. Kattis +source: + title: A Generic Compilation Framework for Efficiently Verifiable Virtual Machine Architectures (draft v0.1.6) + authors: + - Assimakis A. Kattis + - Brian Klatt + - Philip Quirk + - Logan Allen + url: https://github.com/nockchain/pure-nock-lean + github_repo: nockchain/pure-nock-lean + commit: 4838863baa2d49e38573efa3474e77bf1154f300 + manuscript: https://github.com/nockchain/pure-nock-lean/blob/4838863baa2d49e38573efa3474e77bf1154f300/paper/main.tex + announcement: https://github.com/nockchain/pure-nock-lean/commit/4838863baa2d49e38573efa3474e77bf1154f300 + announcement_date: '2026-08-26T15:48:50Z' +license: Apache-2.0 +status: verified +# Classified by the repository's evidence-based rubric: the original release +# credits Akis; commits, source, README, manuscript and publisher supply no AI disclosure. +# Absence of such evidence does not establish exclusively human authorship. +provenance: human +main_declarations: +- Nock.Verb.runProgram_adequate_complete +- Nock.NPR.collision_prob_le +main_results: +- declaration: Nock.Verb.runProgram_adequate_complete + informal: A subject and formula evaluate to a result under the small-step program machine with some finite fuel if and only if the derived paper evaluator produces that result with some finite fuel. +- declaration: Nock.Verb.runTraceProgram_certificate + informal: Every successfully emitted program trace is consistent and complete, every adjacent transition passes the Nock checker, and the final state is either terminal or has a crash edge accepted by the checker. +- declaration: Nock.paper_field_bridge + informal: For a modulus greater than one and inputs whose atoms are below the modulus, successful guarded evaluation produces the same bounded result under natural-number paper evaluation, modular paper evaluation, the full modular evaluator, the natural-number reference evaluator, and the small-step program machine with some finite fuel. +- declaration: Nock.NPR.collision_prob_le + informal: For nouns with at most Lambda leaves and distinct field-valued coefficient data or tree shapes, a uniformly selected point of a finite field makes their polynomial fingerprints coincide with probability at most 2 times (2 Lambda minus 1) divided by the field cardinality. +- declaration: Nock.NPR.fnHorner_eq_fn + informal: The computable Horner fingerprint of every noun at every field evaluation point equals its polynomial fingerprint. +- declaration: Nock.Verb.eval_crash_iff_invalid + informal: The three-valued evaluator reports a crash at some finite fuel exactly when the initial verb has a complete trace ending at a stuck nonterminal state. +- declaration: Nock.NPR.collision_table_prob_le + informal: For a finite table of bounded-length nouns evaluated at one shared random field point, the probability that some pair with distinct field-valued data collides is at most the single-pair error times k times (k minus 1) divided by 2. +tags: +- operational-semantics +- execution-traces +- finite-fields +- fingerprints +msc: +- 68Q55 +- 68Q60 +- 11T71