Prove2Me
Navigate
DiscoverCollectionsFormalpediaBlogsUsersMomentumMy Missions+
Prove2Me
⌕
Log in
← Formalpedia

Round-six κ development, definitions part 10

Definition
IntMul_Kappa_R6_Defs10

by xuanji · Oct 20, 2026 · Mathlib 0df444a (Lean v4.33.1)

complexity-theoryinteger-multiplicationkappa-improvement

Definition bundle IntMul_Kappa_R6_Defs10 of the round-six κ-improvement development. It collects the definitions (data types, parameter maps, network and program structures, and the few lemmas that these definitions need) of the Lean modules NLogN.Logic, NLogN.Move, NLogN.Add, NLogN.Item, NLogN.Arith, NLogN.Basic, NLogN.IntOps2, NLogN.Shift, NLogN.Flat, NLogN.Walk, NLogN.TM, NLogN.Prims, NLogN.ILang, NLogN.IRuns, NLogN.Costs, NLogN.ISchool, NLogN.IEval, NLogN.SSBase, NLogN.HExpr, extracted by skeleton subtraction from the source files with their namespaces, variable declarations and opens unchanged.

The theorems about these objects are published as separate platform nodes that import this bundle.

Formalization Note. Part of the Lean formalization of the round-six κ-improvement (integer-multiplication-kappa campaign, target IntMul.Kappa.jain_round6_framework). The statement is copied verbatim from the Lean development; the platform node carries the same declaration name. Definitions it mentions live in the IntMul_Kappa_R6_Defs* bundles (on top of the published κ bundles IntMul_Kappa_K_Defs*).

Definition code
-- Round-six κ development (integer-multiplication-kappa), generated by skeleton subtraction.

import Definitions.Def_IntMul_Kappa_K_Defs33
import Definitions.Def_IntMul_Kappa_R6_Defs1
import Mathlib
import Definitions.Def_IntMul_HvdH_M_Defs8
import Definitions.Def_IntMul_MultitapeModel
import Definitions.Def_IntMul_HvdH_M_Defs1
import Definitions.Def_IntMul_HvdH_M_Defs2

set_option autoImplicit false
set_option relaxedAutoImplicit false
set_option maxHeartbeats 1000000

section
-- Source module: NLogN.Logic
/-!
# Program logic for stack programs

`Runs I prog s σ σ' c`: `s` takes `σ` to `σ'` with cost at most `c`.  Besides the rules for each
statement this file provides

* `caseTops`: branch on the tops of several stacks (constant cost);
* `opsStmt`: straight-line lists of primitive operations;
* `machine`: a finite-state transducer whose state lives on a dedicated stack; its correctness
  reduces to iterating a plain Lean function `mstep`.
-/

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

namespace Runs

theorem mono {s : Stmt S P} {σ σ' : St S} {c c' : ℕ} (h : Runs I prog s σ σ' c) (hc : c ≤ c') :
    Runs I prog s σ σ' c' := by
  obtain ⟨d, hd, h⟩ := h; exact ⟨d, by omega, h⟩

theorem mono' {s : Stmt S P} {σ σ' σ'' : St S} {c c' : ℕ} (h : Runs I prog s σ σ' c)
    (he : σ' = σ'') (hc : c ≤ c') : Runs I prog s σ σ'' c' := he ▸ h.mono hc

theorem skip (σ : St S) : Runs I prog .skip σ σ 1 := ⟨1, le_rfl, .skip σ⟩
theorem push (σ : St S) (i a) : Runs I prog (.push i a) σ (σ.push i a) 1 := ⟨1, le_rfl, .push σ i a⟩
theorem pop (σ : St S) (i) : Runs I prog (.pop i) σ (σ.pop i) 1 := ⟨1, le_rfl, .pop σ i⟩
theorem inRd (σ : St S) (i) : Runs I prog (.inRd i) σ (σ.inRd I i) 1 := ⟨1, le_rfl, .inRd σ i⟩
theorem out (σ : St S) (b) : Runs I prog (.out b) σ (σ.outB b) 1 := ⟨1, le_rfl, .out σ b⟩

theorem seq {p q : Stmt S P} {σ σ₁ σ₂ : St S} {c₁ c₂ : ℕ} (h₁ : Runs I prog p σ σ₁ c₁)
    (h₂ : Runs I prog q σ₁ σ₂ c₂) : Runs I prog (.seq p q) σ σ₂ (c₁ + c₂) := by
  obtain ⟨d₁, hd₁, h₁⟩ := h₁; obtain ⟨d₂, hd₂, h₂⟩ := h₂
  exact ⟨d₁ + d₂, by omega, .seq h₁ h₂⟩

theorem iteT {i f} {p q : Stmt S P} {σ σ' : St S} {c : ℕ} (hf : f (σ.top i) = true)
    (h : Runs I prog p σ σ' c) : Runs I prog (.ite i f p q) σ σ' (c + 1) := by
  obtain ⟨d, hd, h⟩ := h; exact ⟨d + 1, by omega, .iteT hf h⟩

theorem iteF {i f} {p q : Stmt S P} {σ σ' : St S} {c : ℕ} (hf : f (σ.top i) = false)
    (h : Runs I prog q σ σ' c) : Runs I prog (.ite i f p q) σ σ' (c + 1) := by
  obtain ⟨d, hd, h⟩ := h; exact ⟨d + 1, by omega, .iteF hf h⟩

theorem call {j : Fin P} {σ σ' : St S} {c : ℕ} (h : Runs I prog (prog j) σ σ' c) :
    Runs I prog (.call j) σ σ' (c + 2) := by
  obtain ⟨d, hd, h⟩ := h; exact ⟨d + 2, by omega, .call h⟩

theorem loopF {i f} {p : Stmt S P} {σ : St S} (hf : f (σ.top i) = false) :
    Runs I prog (.loop i f p) σ σ 1 := ⟨1, le_rfl, .loopF hf⟩

/-- A loop running through the states `σs 0, σs 1, …, σs m`. -/
theorem loop {i f} {p : Stmt S P} (σs : ℕ → St S) (cs : ℕ → ℕ) (m : ℕ)
    (hT : ∀ k < m, f ((σs k).top i) = true) (hF : f ((σs m).top i) = false)
    (hb : ∀ k < m, Runs I prog p (σs k) (σs (k + 1)) (cs k)) :
    Runs I prog (.loop i f p) (σs 0) (σs m) ((∑ k ∈ Finset.range m, (cs k + 1)) + 1) := by
  suffices H : ∀ r j, j + r = m → Runs I prog (.loop i f p) (σs j) (σs m)
      ((∑ k ∈ Finset.Ico j m, (cs k + 1)) + 1) by
    have := H m 0 (by omega)
    rwa [Finset.range_eq_Ico]
  intro r
  induction r with
  | zero => intro j hj; subst hj; simpa using loopF hF
  | succ r ih =>
    intro j hj
    have hjm : j < m := by omega
    obtain ⟨d₁, hd₁, h₁⟩ := hb j hjm
    obtain ⟨d₂, hd₂, h₂⟩ := ih (j + 1) (by omega)
    refine ⟨d₁ + d₂ + 1, ?_, .loopT (hT j hjm) h₁ h₂⟩
    rw [Finset.sum_eq_sum_Ico_succ_bot hjm]
    omega

/-- Loop with a uniform bound on the cost of each iteration. -/
theorem loop' {i f} {p : Stmt S P} (σs : ℕ → St S) (c m : ℕ)
    (hT : ∀ k < m, f ((σs k).top i) = true) (hF : f ((σs m).top i) = false)
    (hb : ∀ k < m, Runs I prog p (σs k) (σs (k + 1)) c) :
    Runs I prog (.loop i f p) (σs 0) (σs m) (m * (c + 1) + 1) := by
  have := loop σs (fun _ => c) m hT hF hb
  simpa [Finset.sum_const, Finset.card_range, smul_eq_mul] using this

end Runs

/-! ## Branching on stack tops -/

theorem Runs.caseList {i : Fin S} {k : Option Sym → Stmt S P} {σ σ' : St S} {c : ℕ} {a : Sym}
    (ha : σ.top i = some a) (h : Runs I prog (k (some a)) σ σ' c) :
    ∀ l : List Sym, a ∈ l → Runs I prog (NLogN.caseList i k l) σ σ' (c + l.length) := by
  intro l hl
  induction l with
  | nil => simp at hl
  | cons b l ih =>
    simp only [NLogN.caseList, List.length_cons]
    by_cases hb : a = b
    · subst hb
      exact (Runs.iteT (by simp [ha]) h).mono (by omega)
    · have hl' : a ∈ l := by simpa [hb] using hl
      exact (Runs.iteF (by simp [ha, hb]) (ih hl')).mono (by omega)

theorem Runs.caseTop {i : Fin S} {k : Option Sym → Stmt S P} {σ σ' : St S} {c : ℕ}
    (h : Runs I prog (k (σ.top i)) σ σ' c) : Runs I prog (NLogN.caseTop i k) σ σ' (c + 17) := by
  unfold NLogN.caseTop
  rcases ht : σ.top i with _ | a
  · rw [ht] at h; exact (Runs.iteT (by simp [ht]) h).mono (by omega)
  · rw [ht] at h
    have := Runs.caseList ht h (List.finRange 16) (List.mem_finRange a)
    exact (Runs.iteF (by simp [ht]) this).mono (by simp)

theorem Runs.caseTops {σ σ' : St S} {c : ℕ} :
    ∀ (w : List (Fin S)) (k : List (Option Sym) → Stmt S P),
      Runs I prog (k (σ.tops w)) σ σ' c →
        Runs I prog (NLogN.caseTops w k) σ σ' (c + 17 * w.length)
  | [], k, h => by simpa [NLogN.caseTops, St.tops] using h
  | i :: w, k, h => by
    simp only [NLogN.caseTops, List.length_cons]
    have := Runs.caseTops w (fun os => k (σ.top i :: os)) (by simpa [St.tops] using h)
    exact (Runs.caseTop this).mono (by ring_nf; omega)

/-! ## Straight-line operations -/

theorem Runs.op (o : Op S) (σ : St S) : Runs I prog (o.stmt : Stmt S P) σ (o.run I σ) 1 := by
  cases o <;> simp only [Op.stmt, Op.run]
  · exact Runs.push _ _ _
  · exact Runs.pop _ _
  · exact Runs.inRd _ _
  · exact Runs.out _ _

theorem Runs.ops : ∀ (l : List (Op S)) (σ : St S),
    Runs I prog (opsStmt l : Stmt S P) σ (runOps I l σ) (l.length + 1)
  | [], σ => by simpa [opsStmt, runOps] using Runs.skip σ
  | o :: l, σ => by
    simp only [opsStmt, runOps, List.length_cons]
    exact (Runs.seq (Runs.op o σ) (Runs.ops l _)).mono (by omega)

/-! ## Finite-state transducers -/

theorem Runs.machine {q : Fin S} {w : List (Fin S)} {q0 : Sym}
    {δ : List (Option Sym) → List (Op S)} (B : ℕ) (hB : ∀ os, (δ os).length ≤ B)
    (σ : St S) (m : ℕ)
    (hrun : ∀ k < m, ((mstep I q w δ)^[k] (σ.push q q0)).top q ≠ some hlt)
    (hstop : ((mstep I q w δ)^[m] (σ.push q q0)).top q = some hlt) :
    Runs I prog (NLogN.machine q w q0 δ : Stmt S P) σ
      (((mstep I q w δ)^[m] (σ.push q q0)).pop q) (m * (B + 17 * w.length + 19) + 3) := by
  unfold NLogN.machine
  have hl := Runs.loop' (I := I) (prog := prog) (i := q) (f := fun o => decide (o ≠ some hlt))
    (p := NLogN.caseTops (q :: w) fun os => opsStmt (δ os))
    (fun k => (mstep I q w δ)^[k] (σ.push q q0)) (B + 17 * w.length + 18) m
    (fun k hk => by simpa using hrun k hk) (by simpa using hstop) (fun k _ => by
      rw [Function.iterate_succ_apply']
      have := Runs.ops (I := I) (prog := prog) (δ (((mstep I q w δ)^[k] (σ.push q q0)).tops
        (q :: w))) ((mstep I q w δ)^[k] (σ.push q q0))
      have h2 := Runs.caseTops (I := I) (prog := prog) (q :: w) (fun os => opsStmt (δ os)) this
      refine h2.mono ?_
      have := hB (((mstep I q w δ)^[k] (σ.push q q0)).tops (q :: w))
      simp only [List.length_cons]; omega)
  simp only [Function.iterate_zero, id] at hl
  have := Runs.seq (Runs.push (I := I) (prog := prog) σ q q0)
    (Runs.seq hl (Runs.pop (I := I) (prog := prog) _ q))
  exact this.mono (by ring_nf; omega)

end NLogN

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

end NLogN

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

namespace Reaches

variable {q : Fin S} {w : List (Fin S)} {δ : List (Option Sym) → List (Op S)}

theorem refl (σ : St S) : Reaches I q w δ σ σ 0 := ⟨rfl, fun _ h => absurd h (by omega)⟩

theorem step {σ σf : St S} {m : ℕ} (h : σ.top q ≠ some hlt)
    (h' : Reaches I q w δ (mstep I q w δ σ) σf m) : Reaches I q w δ σ σf (m + 1) := by
  refine ⟨by rw [Function.iterate_succ_apply]; exact h'.1, fun k hk => ?_⟩
  rcases k with _ | k
  · simpa using h
  · rw [Function.iterate_succ_apply]; exact h'.2 k (by omega)

theorem step' {σ σ₁ σf : St S} {m : ℕ} (h : σ.top q ≠ some hlt) (e : mstep I q w δ σ = σ₁)
    (h' : Reaches I q w δ σ₁ σf m) : Reaches I q w δ σ σf (m + 1) := step h (e ▸ h')

theorem trans {σ σ₁ σf : St S} {m₁ m₂ : ℕ} (h₁ : Reaches I q w δ σ σ₁ m₁)
    (h₂ : Reaches I q w δ σ₁ σf m₂) : Reaches I q w δ σ σf (m₁ + m₂) := by
  refine ⟨by rw [add_comm, Function.iterate_add_apply, h₁.1, h₂.1], fun k hk => ?_⟩
  by_cases hk1 : k < m₁
  · exact h₁.2 k hk1
  · obtain ⟨j, rfl⟩ := Nat.exists_eq_add_of_le (not_lt.mp hk1)
    rw [add_comm, Function.iterate_add_apply, h₁.1]; exact h₂.2 j (by omega)

end Reaches

theorem Runs.machine_reach {q : Fin S} {w : List (Fin S)} {q0 : Sym}
    {δ : List (Option Sym) → List (Op S)} (B : ℕ) (hB : ∀ os, (δ os).length ≤ B)
    (σ σf : St S) (m : ℕ) (hR : Reaches I q w δ (σ.push q q0) σf m)
    (hstop : σf.top q = some hlt) :
    Runs I prog (NLogN.machine q w q0 δ : Stmt S P) σ (σf.pop q)
      (m * (B + 17 * w.length + 19) + 3) := by
  have := Runs.machine (I := I) (prog := prog) B hB σ m hR.2 (by rw [hR.1]; exact hstop)
  rwa [hR.1] at this

end NLogN

end

section
-- Source module: NLogN.Move
/-!
# Basic transducers: moving stack contents
-/

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

@[simp] theorem St.set_stk (σ : St S) (i j : Fin S) (l : List Sym) :
    (σ.set i l).stk j = if j = i then l else σ.stk j := by
  simp [St.set, Function.update_apply]

/-! ### Normalising chains of `set` -/

@[simp] theorem St.push_set_same (σ : St S) (i : Fin S) (l : List Sym) (a : Sym) :
    (σ.set i l).push i a = σ.set i (a :: l) := by
  simp [St.push, St.set]

theorem St.push_set_ne (σ : St S) {i j : Fin S} (l : List Sym) (a : Sym) (h : j ≠ i) :
    (σ.set i l).push j a = (σ.push j a).set i l := by
  simp only [St.push, St.set, St.mk.injEq, and_true]
  funext k; simp only [Function.update_apply]; split_ifs <;> simp_all

@[simp] theorem St.pop_set_same (σ : St S) (i : Fin S) (l : List Sym) :
    (σ.set i l).pop i = σ.set i l.tail := by
  simp [St.pop, St.set]

theorem St.pop_set_ne (σ : St S) {i j : Fin S} (l : List Sym) (h : j ≠ i) :
    (σ.set i l).pop j = (σ.pop j).set i l := by
  simp only [St.pop, St.set, St.mk.injEq, and_true]
  funext k; simp only [Function.update_apply]; split_ifs <;> simp_all

theorem St.top_set (σ : St S) (i j : Fin S) (l : List Sym) :
    (σ.set i l).top j = if j = i then l.head? else σ.top j := by
  simp [St.top, St.set, Function.update_apply]; split_ifs <;> rfl

@[simp] theorem St.top_set_same (σ : St S) (i : Fin S) (l : List Sym) :
    (σ.set i l).top i = l.head? := by simp [St.top_set]

/-- Simp set for stepping transducers; add the needed `≠` facts. -/
theorem St.push_eq_set (σ : St S) (i : Fin S) (a : Sym) : σ.push i a = σ.set i (a :: σ.stk i) :=
  rfl

theorem St.set_of_eq (σ : St S) (i : Fin S) (l : List Sym) (h : σ.stk i = l) : σ.set i l = σ := by
  subst h; simp [St.set]

@[simp] theorem St.set_self (σ : St S) (i : Fin S) : σ.set i (σ.stk i) = σ := St.set_of_eq _ _ _ rfl

/-! ## Moving symbols from one stack to another -/

end NLogN

end

section
-- Source module: NLogN.Add
/-!
# Binary words on stacks; addition

A word `A : List Bool` (least significant bit first) sits on a stack as `A.map codeBit`, so its
least significant bit is on top.  A word ends where the stack stops showing bits.
-/

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

@[simp] theorem bitOf_codeBit (b : Bool) : bitOf (some (codeBit b)) = some b := by
  cases b <;> rfl

theorem codeBit_ne_hlt (b : Bool) : codeBit b ≠ hlt := by cases b <;> decide

/-- Ripple-carry addition with carry-in `c`. -/
def addL : Bool → List Bool → List Bool → List Bool
  | c, [], [] => if c then [true] else []
  | c, x :: A, [] => (x ^^ c) :: addL (x && c) A []
  | c, [], y :: B => (y ^^ c) :: addL (y && c) [] B
  | c, x :: A, y :: B => (x ^^ y ^^ c) :: addL ((x && y) || (c && (x ^^ y))) A B

/-- Transition of `add`: the state is the carry (`codeBit`). -/
def addδ (q a b d e : Fin S) : List (Option Sym) → List (Op S)
  | [some c, ta, tb] =>
    if bitOf ta = none ∧ bitOf tb = none then
      (if c = 1 then [.push d 1] else []) ++ [.pop q, .push q hlt]
    else
      let x := (bitOf ta).getD false
      let y := (bitOf tb).getD false
      let cb := decide (c = 1)
      (if (bitOf ta).isSome then [.pop a] else []) ++
        (if (bitOf tb).isSome then [.pop b, .push e (codeBit y)] else []) ++
        [.push d (codeBit (x ^^ y ^^ cb)), .pop q,
          .push q (codeBit ((x && y) || (cb && (x ^^ y))))]
  | _ => [.pop q, .push q hlt]

theorem addδ_length (q a b d e : Fin S) (os) : (addδ q a b d e os).length ≤ 6 := by
  unfold addδ; split
  · split_ifs <;> simp
  · simp

/-- `add`: pops the words on `a` and `b`, pushes their sum on `d` (most significant bit on top)
and the word of `b` on `e` (reversed). -/
def add (q a b d e : Fin S) : Stmt S P := machine q [a, b] 0 (addδ q a b d e)

def addCost (m : ℕ) : ℕ := (m + 1) * 59 + 3

section
variable {q a b d e : Fin S} (hqa : q ≠ a) (hqb : q ≠ b) (hqd : q ≠ d) (hqe : q ≠ e)
  (hab : a ≠ b) (had : a ≠ d) (hae : a ≠ e) (hbd : b ≠ d) (hbe : b ≠ e) (hde : d ≠ e)
  (ra rb Q : List Sym) (hra : bitOf ra.head? = none) (hrb : bitOf rb.head? = none)

/-- Configuration of `add` with carry `c`, remaining words `A`, `B`. -/
def addCfg (σ : St S) (c : Sym) (A B : List Bool) (D E : List Sym) : St S :=
  ((((σ.set a (A.map codeBit ++ ra)).set b (B.map codeBit ++ rb)).set d D).set e E).set q (c :: Q)

include hqa hqb hqd hqe hab had hae hbd hbe hde hra hrb in
theorem add_reaches (σ : St S) : ∀ (A B : List Bool) (c : Bool) (D E : List Sym),
    Reaches I q [a, b] (addδ q a b d e)
      (addCfg (q := q) (a := a) (b := b) (d := d) (e := e) ra rb Q σ (codeBit c) A B D E)
      (addCfg (q := q) (a := a) (b := b) (d := d) (e := e) ra rb Q σ hlt [] []
        (((addL c A B).map codeBit).reverse ++ D) ((B.map codeBit).reverse ++ E))
      (max A.length B.length + 1) := by
  have hqa' := Ne.symm hqa; have hqb' := Ne.symm hqb; have hqd' := Ne.symm hqd
  have hqe' := Ne.symm hqe; have hab' := Ne.symm hab; have had' := Ne.symm had
  have hae' := Ne.symm hae; have hbd' := Ne.symm hbd; have hbe' := Ne.symm hbe
  have hde' := Ne.symm hde
  have htop : ∀ (c : Bool) A B D E, (addCfg (q := q) (a := a) (b := b) (d := d) (e := e) ra rb Q
      σ (codeBit c) A B D E).top q ≠ some hlt := by
    intro c A B D E
    simp only [addCfg, St.top_set_same, List.head?_cons, ne_eq, Option.some.injEq]
    exact codeBit_ne_hlt c
  -- the tops seen by one step
  have htops : ∀ (c : Sym) A B D E, (addCfg (q := q) (a := a) (b := b) (d := d) (e := e) ra rb Q
      σ c A B D E).tops [q, a, b] = [some c, (A.map codeBit ++ ra).head?,
        (B.map codeBit ++ rb).head?] := by
    intro c A B D E
    simp only [St.tops, addCfg, List.map_cons, List.map_nil, St.top_set, hqa, hqb, hqd, hqe, hab,
      had, hae, hbd, hbe, hde, hqa', hqb', hqd', hqe', hab', had', hae', hbd', hbe', hde',
      if_true, if_false, List.head?_cons]
  intro A
  induction A with
  | nil =>
    intro B
    induction B with
    | nil =>
      intro c D E
      refine Reaches.step' (htop c [] [] D E) ?_ (Reaches.refl _)
      rw [mstep, htops]
      simp only [List.map_nil, List.nil_append]
      rw [addδ, if_pos ⟨hra, hrb⟩]
      cases c <;> simp [codeBit, addL, runOps, Op.run, addCfg, St.push_set_ne, St.pop_set_ne,
        hqa, hqb, hqd, hqe, hab, had, hae, hbd, hbe, hde, hqa', hqb', hqd', hqe', hab', had',
        hae', hbd', hbe', hde'] <;> rfl
    | cons y B ih =>
      intro c D E
      refine Reaches.step' (σ₁ := addCfg (q := q) (a := a) (b := b) (d := d) (e := e) ra rb Q σ
        (codeBit (y && c)) [] B (codeBit (y ^^ c) :: D) (codeBit y :: E))
        (htop c [] (y :: B) D E) ?_ ?_
      · rw [mstep, htops]
        simp only [List.map_nil, List.nil_append, List.map_cons, List.cons_append,
          List.head?_cons]
        rw [addδ, if_neg (by simp)]
        cases c <;> cases y <;> simp [codeBit, runOps, Op.run, addCfg, St.push_set_ne,
          St.pop_set_ne, hra, hqa, hqb, hqd, hqe, hab, had, hae, hbd, hbe, hde, hqa', hqb', hqd',
          hqe', hab', had', hae', hbd', hbe', hde']
      · have := ih (y && c) (codeBit (y ^^ c) :: D) (codeBit y :: E)
        simp only [List.length_nil, List.length_cons, addL, List.map_cons, List.reverse_cons,
          List.append_assoc, List.singleton_append] at this ⊢
        simpa using this
  | cons x A ih =>
    intro B
    cases B with
    | nil =>
      intro c D E
      refine Reaches.step' (σ₁ := addCfg (q := q) (a := a) (b := b) (d := d) (e := e) ra rb Q σ
        (codeBit (x && c)) A [] (codeBit (x ^^ c) :: D) E) (htop c (x :: A) [] D E) ?_ ?_
      · rw [mstep, htops]
        simp only [List.map_nil, List.nil_append, List.map_cons, List.cons_append,
          List.head?_cons]
        rw [addδ, if_neg (by simp)]
        cases c <;> cases x <;> simp [codeBit, runOps, Op.run, addCfg, St.push_set_ne,
          St.pop_set_ne, hrb, hqa, hqb, hqd, hqe, hab, had, hae, hbd, hbe, hde, hqa', hqb', hqd',
          hqe', hab', had', hae', hbd', hbe', hde']
      · have := ih [] (x && c) (codeBit (x ^^ c) :: D) E
        simp only [List.length_nil, List.length_cons, addL, List.map_cons, List.reverse_cons,
          List.append_assoc, List.singleton_append, List.map_nil, List.reverse_nil,
          List.nil_append] at this ⊢
        simpa using this
    | cons y B =>
      intro c D E
      refine Reaches.step' (σ₁ := addCfg (q := q) (a := a) (b := b) (d := d) (e := e) ra rb Q σ
          (codeBit ((x && y) || (c && (x ^^ y)))) A B (codeBit (x ^^ y ^^ c) :: D)
          (codeBit y :: E)) (htop c (x :: A) (y :: B) D E) ?_ ?_
      · rw [mstep, htops]
        simp only [List.map_cons, List.cons_append, List.head?_cons]
        rw [addδ, if_neg (by simp)]
        cases c <;> cases x <;> cases y <;> simp [codeBit, runOps, Op.run, addCfg,
          St.push_set_ne, St.pop_set_ne, hqa, hqb, hqd, hqe, hab, had, hae, hbd, hbe, hde, hqa',
          hqb', hqd', hqe', hab', had', hae', hbd', hbe', hde']
      · have := ih B ((x && y) || (c && (x ^^ y))) (codeBit (x ^^ y ^^ c) :: D) (codeBit y :: E)
        simp only [List.length_cons, addL, List.map_cons, List.reverse_cons,
          List.append_assoc, List.singleton_append] at this ⊢
        simpa [Nat.succ_max_succ] using this

include hqa hqb hqd hqe hab had hae hbd hbe hde hra hrb in
theorem Runs.add (σ : St S) (A B : List Bool) (hA : σ.stk a = A.map codeBit ++ ra)
    (hB : σ.stk b = B.map codeBit ++ rb) :
    Runs I prog (NLogN.add q a b d e : Stmt S P) σ
      ((((σ.set a ra).set b rb).set d (((addL false A B).map codeBit).reverse ++ σ.stk d)).set e
        ((B.map codeBit).reverse ++ σ.stk e)) (addCost (max A.length B.length)) := by
  have h := add_reaches (I := I) hqa hqb hqd hqe hab had hae hbd hbe hde ra rb (σ.stk q) hra hrb σ
    A B false (σ.stk d) (σ.stk e)
  have e0 : σ.push q 0 = addCfg (q := q) (a := a) (b := b) (d := d) (e := e) ra rb (σ.stk q) σ
      (codeBit false) A B (σ.stk d) (σ.stk e) := by
    simp only [addCfg, ← hA, ← hB, St.set_self, St.push_eq_set]; rfl
  rw [← e0] at h
  have := Runs.machine_reach (prog := prog) 6 (addδ_length q a b d e) σ _ _ h
    (by simp [addCfg, St.top_set_same])
  have e1 : (addCfg (q := q) (a := a) (b := b) (d := d) (e := e) ra rb (σ.stk q) σ hlt [] []
      (((addL false A B).map codeBit).reverse ++ σ.stk d) ((B.map codeBit).reverse ++ σ.stk e)).pop q
      = ((((σ.set a ra).set b rb).set d (((addL false A B).map codeBit).reverse ++ σ.stk d)).set e
        ((B.map codeBit).reverse ++ σ.stk e)) := by
    simp only [addCfg, St.pop_set_same, List.tail_cons, List.map_nil, List.nil_append]
    apply St.set_of_eq
    simp [hqa, hqb, hqd, hqe]
  rw [e1] at this
  exact this.mono (by simp [addCost])

end

end NLogN

end

section
-- Source module: NLogN.Item
/-!
# Integer items on stacks

An item is an integer (sign–magnitude, minimal binary, least significant bit nearest the header)
or a mark.  On a stack an item is its header symbol followed by its magnitude bits; the next
item's header ends the bits.
-/

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

/-! ## Minimal binary -/

theorem nbits_zero : nbits 0 = [] := by rw [nbits]; simp

theorem nbits_pos {m : ℕ} (h : m ≠ 0) : nbits m = decide (m % 2 = 1) :: nbits (m / 2) := by
  rw [nbits]; simp [h]

theorem wval_nbits (m : ℕ) : wval (nbits m) = m := by
  induction m using Nat.strong_induction_on with
  | _ m ih =>
    rcases Nat.eq_zero_or_pos m with rfl | hm
    · simp [nbits_zero, wval]
    · rw [nbits_pos hm.ne', wval, ih _ (Nat.div_lt_self hm (by norm_num))]
      by_cases h : m % 2 = 1 <;> simp [h] <;> omega

/-! ## Items -/

end NLogN

namespace NLogN

/-! ## Bit-level functions for `zip` -/

theorem wval_lt_pow (A : List Bool) : wval A < 2 ^ A.length := by
  induction A with
  | nil => simp [wval]
  | cons b A ih => simp only [wval, List.length_cons, pow_succ]; cases b <;> simp <;> omega

end NLogN

end

section
-- Source module: NLogN.Arith
/-!
# Signed addition of integer items
-/

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

end NLogN

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

theorem wval_zbits (z : ℤ) : wval (zbits z) = z.natAbs := wval_nbits _

end NLogN

end

section
-- Source module: NLogN.Basic
/-!
# A relaxed machine layer over `IntMul.MultitapeTM`

`Prog Γ k` is a deterministic `k`-tape machine over an arbitrary alphabet whose transition
function returns `none` to halt.  Its step semantics silently enforce the platform rules
(`▷` is kept and never written elsewhere, no left move from `▷`, tape `0` is read-only), so
`compile` turns any program into a `MultitapeTM` with one extra step.
-/

namespace IntMul.Mach

open IntMul

variable {Γ : Type} {k : ℕ}

namespace Tapes

end Tapes

attribute [instance] Prog.fintypeQ

section Sem

variable [DecidableEq Γ] (st : Γ)

variable {st}

/-- Transport along a state map that commutes with the transition function. -/
theorem Halts.map {P R : Prog Γ k} (f : P.Q → R.Q)
    (hf : ∀ q a, R.δ (f q) a = (P.δ q a).map (fun r => (f r.1, r.2)))
    {q : P.Q} {T T' : Tapes Γ k} {t : ℕ} (h : Halts st P q T t T') :
    Halts st R (f q) T t T' := by
  induction h with
  | done h0 => exact Halts.done (by rw [hf, h0]; rfl)
  | step h0 _ ih => exact Halts.step (by rw [hf, h0]; rfl) ih

end Sem

/-! ## Combinators -/

/-- Run `P₁`, then `P₂` (no extra step at the junction). -/
def seq (P₁ P₂ : Prog Γ k) : Prog Γ k where
  Q := P₁.Q ⊕ P₂.Q
  q0 := Sum.inl P₁.q0
  δ q a := match q with
    | Sum.inl q => match P₁.δ q a with
      | some r => some (Sum.inl r.1, r.2)
      | none => (P₂.δ P₂.q0 a).map (fun r => (Sum.inr r.1, r.2))
    | Sum.inr q => (P₂.δ q a).map (fun r => (Sum.inr r.1, r.2))

/-- Branch on the scanned symbols, then run `P₁` or `P₂`. -/
def ite (c : (Fin k → Γ) → Bool) (P₁ P₂ : Prog Γ k) : Prog Γ k where
  Q := Option (P₁.Q ⊕ P₂.Q)
  q0 := none
  δ q a := match q with
    | none => if c a then (P₁.δ P₁.q0 a).map (fun r => (some (Sum.inl r.1), r.2))
        else (P₂.δ P₂.q0 a).map (fun r => (some (Sum.inr r.1), r.2))
    | some (Sum.inl q) => (P₁.δ q a).map (fun r => (some (Sum.inl r.1), r.2))
    | some (Sum.inr q) => (P₂.δ q a).map (fun r => (some (Sum.inr r.1), r.2))

/-- `while c do B`: the test is made on the scanned symbols before every pass. -/
def loop (c : (Fin k → Γ) → Bool) (B : Prog Γ k) : Prog Γ k where
  Q := Option B.Q
  q0 := none
  δ q a := match q with
    | none => if c a then (B.δ B.q0 a).map (fun r => (some r.1, r.2)) else none
    | some q => match B.δ q a with
      | some r => some (some r.1, r.2)
      | none => if c a then (B.δ B.q0 a).map (fun r => (some r.1, r.2)) else none

/-- A program with a single step. -/
def act (f : (Fin k → Γ) → Fin k → Γ × Move) : Prog Γ k where
  Q := Bool
  q0 := false
  δ q a := if q then none else some (true, f a)

/-- The empty program. -/
def skip : Prog Γ k where
  Q := Unit
  q0 := ()
  δ _ _ := none

/-! ## Compilation to the platform model -/

variable [DecidableEq Γ] [Fintype Γ]

end IntMul.Mach

end

section
-- Source module: NLogN.IntOps2
/-!
# More integer item operations: negate, duplicate, shifts
-/

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

theorem zbits_neg (z : ℤ) : zbits (-z) = zbits z := by simp [zbits]

/-! ## Negation -/

end NLogN

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

/-! ## Duplication -/

end NLogN

end

section
-- Source module: NLogN.Shift
/-!
# Shifting and splitting integers by a power of two given as an item

A "ruler" is the item `int (2 ^ m)`; its bits are `m` zeros and a one.
-/

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

/-! ## Bit lemmas -/

theorem nbits_two_pow (m : ℕ) : nbits (2 ^ m) = List.replicate m false ++ [true] := by
  induction m with
  | zero => rw [nbits_pos (by norm_num)]; simp [nbits_zero]
  | succ m ih =>
    rw [nbits_pos (by positivity), pow_succ, List.replicate_succ]
    simp [ih, Nat.mul_mod_left]

theorem zbits_two_pow (m : ℕ) : zbits (2 ^ m : ℕ) = List.replicate m false ++ [true] := by
  simp [zbits, nbits_two_pow]

theorem nbits_mul_two_pow {x : ℕ} (hx : x ≠ 0) (m : ℕ) :
    nbits (x * 2 ^ m) = List.replicate m false ++ nbits x := by
  induction m with
  | zero => simp
  | succ m ih =>
    rw [nbits_pos (by positivity), List.replicate_succ, pow_succ, ← mul_assoc]
    simp [Nat.mul_mod_left, ih]

theorem nbits_drop (x m : ℕ) : (nbits x).drop m = nbits (x / 2 ^ m) := by
  induction m generalizing x with
  | zero => simp
  | succ m ih =>
    rcases Nat.eq_zero_or_pos x with rfl | hx
    · simp [nbits_zero]
    · rw [nbits_pos hx.ne', List.drop_succ_cons, ih, pow_succ, Nat.div_div_eq_div_mul,
        mul_comm 2]

theorem natAbs_mul_two_pow (z : ℤ) (m : ℕ) : (z * 2 ^ m).natAbs = z.natAbs * 2 ^ m := by
  rw [Int.natAbs_mul]; simp

theorem zbits_mul_two_pow {z : ℤ} (hz : z ≠ 0) (m : ℕ) :
    zbits (z * 2 ^ m) = List.replicate m false ++ zbits z := by
  rw [zbits, natAbs_mul_two_pow, nbits_mul_two_pow (by omega)]; rfl

/-! ## Moving the zeros of a ruler -/

end NLogN

namespace NLogN

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

/-! ## Shift left by a ruler -/

/-! ## Split by a ruler -/

end NLogN

end

section
-- Source module: NLogN.Flat
/-!
# Flat code with jumps, and compilation of structured programs into it

Instructions carry absolute addresses. `ret` with an empty control stack halts.
`exec_flat`: a structured run of cost `c` is `c` flat steps.
-/

namespace NLogN

variable {S P : ℕ}

theorem Steps.trans {I : List Sym} {code : ℕ → Instr S} {c₁ c₂ c₃ : Cfg S} {m n : ℕ}
    (h₁ : Steps I code c₁ m c₂) (h₂ : Steps I code c₂ n c₃) : Steps I code c₁ (m + n) c₃ := by
  induction h₁ with
  | refl => simpa using h₂
  | step h _ ih => rw [Nat.add_right_comm]; exact Steps.step h (ih h₂)

/-! ## Layout -/

/-! ## A concrete layout -/

end NLogN

end

section
-- Source module: NLogN.Walk
/-!
# Scans, copies and finite-state walks

Generic single-loop programs and their exact running times.
-/

namespace IntMul.Mach

open IntMul

set_option linter.unusedSectionVars false

variable {Γ : Type} {k : ℕ} [DecidableEq Γ]

/-! ## Tape lemmas -/

/-! ## Writing with `fix` -/

/-! ## Scans -/

/-! ## Copy -/

/-! ## Finite-state walks to the left -/

/-! ## Single moves -/

/-- Move head `i` one cell. -/
def mv (i : Fin k) (m : Move) : Prog Γ k := act (fun a => wr1 a i (a i) m)

end IntMul.Mach

end

section
-- Source module: NLogN.TM
/-!
# Running flat code on the platform's Turing machines

Tapes: `0` input, `1` output, `2` control stack (return addresses), `3 + i` stack `i`.
On every stack tape the head rests on the top symbol (on `▷` when empty), so `push` is two
steps, `pop` and tests are one step, and a call is two steps.  Each flat step is at most two
machine steps (`flat_tm`).
-/

namespace NLogN

open IntMul IntMul.Mach

variable {S L : ℕ}

/-! ## Encoding of configurations -/

end NLogN

namespace NLogN

open IntMul IntMul.Mach

variable {S L : ℕ}

/-! ## Partial runs -/

theorem Reach.trans {Γ' : Type} [DecidableEq Γ'] {k : ℕ} {st : Γ'} {P : Prog Γ' k}
    {q₁ q₂ q₃ : P.Q} {T₁ T₂ T₃ : Tapes Γ' k} {m n : ℕ}
    (h₁ : Reach st P q₁ T₁ m q₂ T₂) (h₂ : Reach st P q₂ T₂ n q₃ T₃) : Reach st P q₁ T₁ (m + n) q₃ T₃ := by
  induction h₁ with
  | refl => simpa using h₂
  | step h _ ih => rw [Nat.add_right_comm]; exact Reach.step h (ih h₂)

attribute [simp] tst_ne_tin tst_ne_tout tst_ne_tctl tin_ne_tout tin_ne_tctl tout_ne_tctl

/-! ## One flat step -/

end NLogN

end

section
-- Source module: NLogN.Prims
/-!
# Constants, input and output of integer items
-/

namespace NLogN

open IntMul

variable {S P : ℕ} {I : List Sym} {prog : Fin P → Stmt S P}

/-! ## Constants -/

/-! ## Output -/

theorem length_nbits_le {n m : ℕ} (h : n < 2 ^ m) : (nbits n).length ≤ m := by
  have := nbits_drop n m
  rw [Nat.div_eq_of_lt h, nbits_zero, List.drop_eq_nil_iff] at this
  exact this

/-! ## Input -/

end NLogN

end

section
-- Source module: NLogN.ILang
/-!
# The item language

Programs over `S` stacks of items (integers and marks), compiled to stack programs over `S + 4`
stacks (the last four are scratch).  `compile_correct` turns item-level executions into
`Runs` of the compiled program, with the same cost.
-/

namespace NLogN

open IntMul

variable {S P : ℕ}

/-! ## Stacks of the compiled program -/

/-! ## Operations -/

variable {I : List Sym} {prog : Fin P → Stmt (S + 4) P}

theorem addIntCost_mono {L L' : ℕ} (h : L ≤ L') : addIntCost L ≤ addIntCost L' := by
  simp only [addIntCost, addMagCost, subMagCost, zipCost, moveCost]; nlinarith

end NLogN

end

section
-- Source module: NLogN.IRuns
/-!
# Reasoning about item-language programs
-/

namespace NLogN

open IntMul

variable {S P : ℕ} {I : List Sym} {iprog : Fin P → IStmt S P}

namespace IRuns

theorem mono_r6c {p : IStmt S P} {s s' : IState S} {c c' : ℕ} (h : IRuns I iprog p s s' c)
    (hc : c ≤ c') : IRuns I iprog p s s' c' := by
  obtain ⟨d, hd, h⟩ := h; exact ⟨d, by omega, h⟩

theorem mono' {p : IStmt S P} {s s' s'' : IState S} {c c' : ℕ} (h : IRuns I iprog p s s' c)
    (he : s' = s'') (hc : c ≤ c') : IRuns I iprog p s s'' c' := he ▸ h.mono_r6c hc

theorem skip_r6c (s : IState S) : IRuns I iprog .skip s s 1 := ⟨1, le_rfl, .skip s⟩

theorem op {o : IOp S} {s s' : IState S} {c : ℕ} (h : o.Step I s s' c) :
    IRuns I iprog (.op o) s s' c := ⟨c, le_rfl, .op h⟩

theorem seq_r6c {p q : IStmt S P} {s s₁ s₂ : IState S} {c₁ c₂ : ℕ} (h₁ : IRuns I iprog p s s₁ c₁)
    (h₂ : IRuns I iprog q s₁ s₂ c₂) : IRuns I iprog (.seq p q) s s₂ (c₁ + c₂) := by
  obtain ⟨d₁, hd₁, h₁⟩ := h₁; obtain ⟨d₂, hd₂, h₂⟩ := h₂
  exact ⟨d₁ + d₂, by omega, .seq h₁ h₂⟩

theorem iteT {i f} {p q : IStmt S P} {s s' : IState S} {c : ℕ} (hf : f (s.cl i) = true)
    (h : IRuns I iprog p s s' c) : IRuns I iprog (.ite i f p q) s s' (c + 25) := by
  obtain ⟨d, hd, h⟩ := h; exact ⟨d + 25, by omega, .iteT hf h⟩

theorem iteF {i f} {p q : IStmt S P} {s s' : IState S} {c : ℕ} (hf : f (s.cl i) = false)
    (h : IRuns I iprog q s s' c) : IRuns I iprog (.ite i f p q) s s' (c + 25) := by
  obtain ⟨d, hd, h⟩ := h; exact ⟨d + 25, by omega, .iteF hf h⟩

theorem ite {i f} {p q : IStmt S P} {s s' : IState S} {c : ℕ}
    (h : IRuns I iprog (if f (s.cl i) then p else q) s s' c) :
    IRuns I iprog (.ite i f p q) s s' (c + 25) := by
  by_cases hf : f (s.cl i)
  · rw [if_pos hf] at h; exact iteT hf h
  · rw [if_neg hf] at h; exact iteF (by simpa using hf) h

theorem call {j : Fin P} {s s' : IState S} {c : ℕ} (h : IRuns I iprog (iprog j) s s' c) :
    IRuns I iprog (.call j) s s' (c + 2) := by
  obtain ⟨d, hd, h⟩ := h; exact ⟨d + 2, by omega, .call h⟩

theorem loopF {i f} {p : IStmt S P} {s : IState S} (hf : f (s.cl i) = false) :
    IRuns I iprog (.loop i f p) s s 30 := ⟨30, le_rfl, .loopF hf⟩

theorem loopT {i f} {p : IStmt S P} {s s₁ s₂ : IState S} {c₁ c₂ : ℕ} (hf : f (s.cl i) = true)
    (h₁ : IRuns I iprog p s s₁ c₁) (h₂ : IRuns I iprog (.loop i f p) s₁ s₂ c₂) :
    IRuns I iprog (.loop i f p) s s₂ (c₁ + c₂ + 30) := by
  obtain ⟨d₁, hd₁, h₁⟩ := h₁; obtain ⟨d₂, hd₂, h₂⟩ := h₂
  exact ⟨d₁ + d₂ + 30, by omega, .loopT hf h₁ h₂⟩

/-- A loop running through the states `ss 0, …, ss m`. -/
theorem loop {i f} {p : IStmt S P} (ss : ℕ → IState S) (cs : ℕ → ℕ) (m : ℕ)
    (hT : ∀ k < m, f ((ss k).cl i) = true) (hF : f ((ss m).cl i) = false)
    (hb : ∀ k < m, IRuns I iprog p (ss k) (ss (k + 1)) (cs k)) :
    IRuns I iprog (.loop i f p) (ss 0) (ss m) ((∑ k ∈ Finset.range m, (cs k + 30)) + 30) := by
  suffices H : ∀ r j, j + r = m → IRuns I iprog (.loop i f p) (ss j) (ss m)
      ((∑ k ∈ Finset.Ico j m, (cs k + 30)) + 30) by
    have := H m 0 (by omega)
    rwa [Finset.range_eq_Ico]
  intro r
  induction r with
  | zero => intro j hj; subst hj; simpa using loopF hF
  | succ r ih =>
    intro j hj
    have hjm : j < m := by omega
    have := loopT (hT j hjm) (hb j hjm) (ih (j + 1) (by omega))
    refine this.mono_r6c ?_
    rw [Finset.sum_eq_sum_Ico_succ_bot hjm]
    omega

/-- Loop with a uniform bound on the cost of each iteration. -/
theorem loop' {i f} {p : IStmt S P} (ss : ℕ → IState S) (c m : ℕ)
    (hT : ∀ k < m, f ((ss k).cl i) = true) (hF : f ((ss m).cl i) = false)
    (hb : ∀ k < m, IRuns I iprog p (ss k) (ss (k + 1)) c) :
    IRuns I iprog (.loop i f p) (ss 0) (ss m) (m * (c + 30) + 30) := by
  have := loop ss (fun _ => c) m hT hF hb
  simpa [Finset.sum_const, Finset.card_range, smul_eq_mul] using this

end IRuns

/-! ## Operations as `IRuns` -/

namespace IRuns

theorem add {s : IState S} {i j k : Fin S} {a b : ℤ} {Li Lj : List Item} (hij : i ≠ j)
    (hi : s.G i = .int a :: Li) (hj : s.G j = .int b :: Lj) :
    IRuns I iprog (.op (.add i j k)) s
      (((s.upd i Li).upd j Lj).upd k (.int (a + b) :: ((s.upd i Li).upd j Lj).G k))
      (addIntCost (zsz a + zsz b)) := op (.add hij hi hj)

theorem neg {s : IState S} {i : Fin S} {a : ℤ} {L : List Item} (hi : s.G i = .int a :: L) :
    IRuns I iprog (.op (.neg i)) s (s.upd i (.int (-a) :: L)) 20 := op (.neg hi)

theorem dup_r6c {s : IState S} {i j : Fin S} {a : ℤ} {L : List Item} (hij : i ≠ j)
    (hi : s.G i = .int a :: L) :
    IRuns I iprog (.op (.dup i j)) s (s.upd j (.int a :: s.G j)) (dupCost (zsz a) + 17) :=
  op (.dup hij hi)

theorem drop_r6c {s : IState S} {i : Fin S} {a : ℤ} {L : List Item} (hi : s.G i = .int a :: L) :
    IRuns I iprog (.op (.drop i)) s (s.upd i L) (2 * zsz a + 2) := op (.drop hi)

theorem mv_r6c {s : IState S} {i j : Fin S} {a : ℤ} {L : List Item} (hij : i ≠ j)
    (hi : s.G i = .int a :: L) :
    IRuns I iprog (.op (.mv i j)) s ((s.upd i L).upd j (.int a :: s.G j))
      (2 * moveCost (zsz a) + 19) := op (.mv hij hi)

theorem const_r6c {s : IState S} (i : Fin S) (c : ℤ) :
    IRuns I iprog (.op (.const i c)) s (s.upd i (.int c :: s.G i)) (zsz c + 2) := op (.const c)

theorem mark {s : IState S} (i : Fin S) :
    IRuns I iprog (.op (.mark i)) s (s.upd i (.mark :: s.G i)) 1 := op .mark

theorem unmark {s : IState S} {i : Fin S} {L : List Item} (hi : s.G i = .mark :: L) :
    IRuns I iprog (.op (.unmark i)) s (s.upd i L) 1 := op (.unmark hi)

theorem shl {s : IState S} {i j : Fin S} {a : ℤ} {m : ℕ} {Li Lj : List Item} (hij : i ≠ j)
    (hi : s.G i = .int a :: Li) (hj : s.G j = .int (2 ^ m) :: Lj) :
    IRuns I iprog (.op (.shl i j)) s (s.upd i (.int (a * 2 ^ m) :: Li)) (shlCost m) :=
  op (.shl hij hi hj)

theorem split {s : IState S} {i j k : Fin S} {a : ℤ} {m : ℕ} {Li Lj : List Item} (hij : i ≠ j)
    (hik : i ≠ k) (hjk : j ≠ k) (hi : s.G i = .int a :: Li) (hj : s.G j = .int (2 ^ m) :: Lj) :
    IRuns I iprog (.op (.split i j k)) s
      ((s.upd i (.int (sg a (a.natAbs / 2 ^ m)) :: Li)).upd k
        (.int (sg a (a.natAbs % 2 ^ m)) :: s.G k)) (splitCost m + 17) :=
  op (.split hij hik hjk hi hj)

theorem cat {s : IState S} {i j k : Fin S} {a : ℤ} {b m : ℕ} {Li Lj Lk : List Item} (hij : i ≠ j)
    (hik : i ≠ k) (hjk : j ≠ k) (ha : a ≠ 0) (hb : b < 2 ^ m) (hi : s.G i = .int a :: Li)
    (hj : s.G j = .int (2 ^ m) :: Lj) (hk : s.G k = .int (b : ℤ) :: Lk) :
    IRuns I iprog (.op (.cat i j k)) s
      ((s.upd i (.int (sg a (a.natAbs * 2 ^ m + b)) :: Li)).upd k Lk) (catCost m) :=
  op (.cat hij hik hjk ha hb hi hj hk)

end IRuns

/-! ## State lemmas -/

@[simp] theorem IState.upd_upd_same (s : IState S) (i : Fin S) (L L' : List Item) :
    (s.upd i L).upd i L' = s.upd i L' := by
  simp [IState.upd]

@[simp] theorem IState.upd_self (s : IState S) (i : Fin S) : s.upd i (s.G i) = s := by
  simp [IState.upd]

theorem IState.ext' {s t : IState S} (hG : ∀ x, s.G x = t.G x) (hip : s.ip = t.ip)
    (hout : s.out = t.out) : s = t := by
  cases s; cases t; simp only [IState.mk.injEq]; exact ⟨funext hG, hip, hout⟩

@[simp] theorem cl_int (s : IState S) (i : Fin S) (z : ℤ) (L : List Item) (h : s.G i = .int z :: L) :
    s.cl i = if z = 0 then .zero else if z < 0 then .neg else .pos := by
  simp [IState.cl, h, cls]

end NLogN

end

section
-- Source module: NLogN.Costs
/-! # Linear bounds on the costs of item operations -/

namespace NLogN

theorem addIntCost_le (L : ℕ) : addIntCost L ≤ 400 * (L + 1) := by
  simp only [addIntCost, addMagCost, subMagCost, zipCost, moveCost]; omega
theorem dupCost_le (L : ℕ) : dupCost L + 17 ≤ 300 * (L + 1) := by
  simp only [dupCost, zipCost, moveCost]; ring_nf; omega
theorem moveIntCost_le (L : ℕ) : 2 * moveCost L + 19 ≤ 110 * (L + 1) := by
  simp only [moveCost]; omega
theorem shlCost_le (m : ℕ) : shlCost m ≤ 200 * (m + 1) := by
  simp only [shlCost, zipCost, moveCost]; omega
theorem splitCost_le (m : ℕ) : splitCost m + 17 ≤ 200 * (m + 1) := by
  simp only [splitCost, takeCost, moveCost]; omega

/-! ## Sizes -/

theorem zsz_le_of_lt {z : ℤ} {m : ℕ} (h : z.natAbs < 2 ^ m) : zsz z ≤ m := length_nbits_le h

theorem natAbs_lt_zsz (z : ℤ) : z.natAbs < 2 ^ zsz z := by
  have := wval_lt_pow (zbits z); rwa [wval_zbits] at this

theorem zsz_le_iff {z : ℤ} {m : ℕ} : zsz z ≤ m ↔ z.natAbs < 2 ^ m :=
  ⟨fun h => lt_of_lt_of_le (natAbs_lt_zsz z) (Nat.pow_le_pow_right (by norm_num) h),
    zsz_le_of_lt⟩

theorem zsz_mul_le (a b : ℤ) : zsz (a * b) ≤ zsz a + zsz b := by
  apply zsz_le_of_lt
  rw [Int.natAbs_mul, pow_add]
  exact Nat.mul_lt_mul'' (natAbs_lt_zsz a) (natAbs_lt_zsz b)

theorem zsz_add_le (a b : ℤ) : zsz (a + b) ≤ max (zsz a) (zsz b) + 1 := by
  apply zsz_le_of_lt
  have ha := natAbs_lt_zsz a; have hb := natAbs_lt_zsz b
  have ha' : 2 ^ zsz a ≤ 2 ^ max (zsz a) (zsz b) := Nat.pow_le_pow_right (by norm_num) (le_max_left _ _)
  have hb' : 2 ^ zsz b ≤ 2 ^ max (zsz a) (zsz b) := Nat.pow_le_pow_right (by norm_num) (le_max_right _ _)
  have := Int.natAbs_add_le a b
  rw [pow_succ]; omega

theorem zsz_two_pow_r6c (m : ℕ) : zsz ((2 : ℤ) ^ m) = m + 1 := by
  have : ((2 : ℤ) ^ m) = ((2 ^ m : ℕ) : ℤ) := by push_cast; rfl
  rw [this, zsz, zbits_two_pow]; simp

theorem zsz_neg_r6c (z : ℤ) : zsz (-z) = zsz z := by simp [zsz, zbits_neg]

theorem zsz_zero_r6c : zsz 0 = 0 := by simp [zsz, zbits, nbits_zero]

theorem zsz_one_r6c : zsz 1 = 1 := by
  simp [zsz, zbits]; rw [nbits_pos (by norm_num)]; simp [nbits_zero]

theorem zsz_mono_r6c {a b : ℤ} (h : a.natAbs ≤ b.natAbs) : zsz a ≤ zsz b :=
  zsz_le_of_lt (lt_of_le_of_lt h (natAbs_lt_zsz b))

theorem natAbs_sg (z : ℤ) (n : ℕ) : (sg z n).natAbs = n := by
  unfold sg; split_ifs <;> simp

theorem sg_add (z : ℤ) (a b : ℕ) : sg z (a + b) = sg z a + sg z b := by
  unfold sg; split_ifs <;> push_cast <;> ring

theorem sg_mul (z : ℤ) (c a : ℕ) : sg z (c * a) = c * sg z a := by
  unfold sg; split_ifs <;> push_cast <;> ring

theorem sg_sg {z : ℤ} {n : ℕ} (hn : n ≠ 0) (m : ℕ) : sg (sg z n) m = sg z m := by
  unfold sg; split_ifs <;> first | rfl | omega

theorem sg_natAbs (z : ℤ) : sg z z.natAbs = z := by unfold sg; split_ifs <;> omega

theorem sg_zero (z : ℤ) : sg z 0 = 0 := by unfold sg; split_ifs <;> simp

theorem sg_eq_zero {z : ℤ} {n : ℕ} : sg z n = 0 ↔ n = 0 := by
  unfold sg; split_ifs <;> omega

end NLogN

end

section
-- Source module: NLogN.ISchool
/-!
# Schoolbook multiplication in the item language (base case)
-/

namespace NLogN

open IntMul

variable {S P : ℕ} {I : List Sym} {iprog : Fin P → IStmt S P}

theorem nat_mod_two_pow_succ (n k : ℕ) : n % 2 ^ (k + 1) = n % 2 ^ k + 2 ^ k * (n / 2 ^ k % 2) := by
  rw [pow_succ, Nat.mod_mul, mul_comm]

theorem div_two_pow_ne_zero {n k : ℕ} (hk : k < (nbits n).length) : n / 2 ^ k ≠ 0 := by
  intro h
  have := nbits_drop n k
  rw [h, nbits_zero, List.drop_eq_nil_iff] at this; omega

theorem div_two_pow_zsz (n : ℕ) : n / 2 ^ (nbits n).length = 0 := by
  have := wval_lt_pow (nbits n); rw [wval_nbits] at this
  exact Nat.div_eq_of_lt this

theorem smulBody_runs_r6c {A X Y W Q R T : Fin S} (hd : [A, X, Y, W, Q, R, T].Nodup)
    (s : IState S) (L : List Item) (a b c : ℤ) (hb : b ≠ 0) (N : ℕ)
    (ha : zsz a ≤ N) (hc : zsz c ≤ N) :
    IRuns I iprog (smulBody X Y W Q R T) (schoolSt s A X Y W Q L a b c)
      (schoolSt s A X Y W Q L (a * 2) (sg b (b.natAbs / 2))
        (c + a * sg b (b.natAbs % 2))) (schoolIter N) := by
  simp only [List.nodup_cons, List.mem_cons, List.not_mem_nil, not_or,
    not_false_eq_true, List.nodup_nil, and_true, or_false] at hd
  obtain ⟨⟨hAX, hAY, hAW, hAQ, hAR, hAT⟩, ⟨hXY, hXW, hXQ, hXR, hXT⟩, ⟨hYW, hYQ, hYR, hYT⟩,
    ⟨hWQ, hWR, hWT⟩, ⟨hQR, hQT⟩, hRT⟩ := hd
  have hAX' := Ne.symm hAX; have hAY' := Ne.symm hAY; have hAW' := Ne.symm hAW; have hAQ' := Ne.symm hAQ; have hAR' := Ne.symm hAR; have hAT' := Ne.symm hAT; have hXY' := Ne.symm hXY; have hXW' := Ne.symm hXW; have hXQ' := Ne.symm hXQ; have hXR' := Ne.symm hXR; have hXT' := Ne.symm hXT; have hYW' := Ne.symm hYW; have hYQ' := Ne.symm hYQ; have hYR' := Ne.symm hYR; have hYT' := Ne.symm hYT; have hWQ' := Ne.symm hWQ; have hWR' := Ne.symm hWR; have hWT' := Ne.symm hWT; have hQR' := Ne.symm hQR; have hQT' := Ne.symm hQT; have hRT' := Ne.symm hRT
  set s0 := schoolSt s A X Y W Q L a b c
  have hY : s0.G Y = .int b :: s.G Y := by simp [s0, schoolSt, hYQ, hYW]
  have hQ : s0.G Q = .int (2 ^ 1) :: s.G Q := by simp [s0, schoolSt]
  have h1 := IRuns.split (I := I) (iprog := iprog) (s := s0) (k := R) hYQ hYR hQR hY hQ
  set r := sg b (b.natAbs % 2 ^ 1)
  set s1 := (s0.upd Y (.int (sg b (b.natAbs / 2 ^ 1)) :: s.G Y)).upd R (.int r :: s0.G R)
  have hR1 : s1.G R = .int r :: s.G R := by
    simp [s1, s0, schoolSt, hYR', hWR', hQR', hXR', hAR']
  have hX1 : s1.G X = .int a :: s.G X := by simp [s1, s0, schoolSt, hXR, hXY, hXW, hXQ]
  have hW1 : s1.G W = .int c :: s.G W := by simp [s1, s0, schoolSt, hWR, hYW', hWQ]
  have hr : r = 0 ∨ r = 1 ∨ r = -1 := by
    simp only [r, sg]; split_ifs <;> omega
  -- the branch
  have hbr : IRuns I iprog (.ite R (fun c => c == .zero) .skip
      (.ite R (fun c => c == .pos) (.seq (.op (.dup X T)) (.op (.add T W W)))
        (.seq (.op (.dup X T)) (.seq (.op (.neg T)) (.op (.add T W W)))))) s1
      (s1.upd W (.int (c + a * r) :: s.G W)) (50 + 20 + 300 * (N + 1) + 400 * (2 * N + 2)) := by
    rcases hr with h0 | h1 | hm
    · refine (IRuns.iteT (by rw [cl_int _ _ _ _ hR1]; simp [h0]) (IRuns.skip_r6c _)).mono' ?_ (by omega)
      rw [h0, mul_zero, add_zero, ← hW1, IState.upd_self]
    · have hcl1 : s1.cl R = .pos := by rw [cl_int _ _ _ _ hR1]; simp [h1]
      have e1 := IRuns.dup_r6c (I := I) (iprog := iprog) (s := s1) (j := T) hXT hX1
      have e2 := IRuns.add (I := I) (iprog := iprog) (s := s1.upd T (.int a :: s1.G T))
        (i := T) (j := W) (k := W) (a := a) (b := c) (Li := s1.G T) (Lj := s.G W)
        hWT' (by simp) (by simp [hW1, hWT])
      refine (IRuns.iteF (by rw [hcl1]; rfl) (IRuns.iteT (by rw [hcl1]; rfl) (IRuns.seq_r6c e1 e2))).mono' ?_ ?_
      · apply IState.ext' <;> intros <;> simp [h1] <;> split_ifs <;> simp_all <;> ring
      · have := dupCost_le (zsz a)
        have := addIntCost_mono (show zsz a + zsz c ≤ 2 * N + 1 by omega)
        have := addIntCost_le (2 * N + 1)
        omega
    · have hcl1 : s1.cl R = .neg := by rw [cl_int _ _ _ _ hR1]; simp [hm]
      have e1 := IRuns.dup_r6c (I := I) (iprog := iprog) (s := s1) (j := T) hXT hX1
      have e2 := IRuns.neg (I := I) (iprog := iprog) (s := s1.upd T (.int a :: s1.G T)) (i := T)
        (a := a) (L := s1.G T) (by simp)
      have e3 := IRuns.add (I := I) (iprog := iprog)
        (s := (s1.upd T (.int a :: s1.G T)).upd T (.int (-a) :: s1.G T))
        (i := T) (j := W) (k := W) (a := -a) (b := c) (Li := s1.G T) (Lj := s.G W)
        hWT' (by simp) (by simp [hW1, hWT])
      refine (IRuns.iteF (by rw [hcl1]; rfl) (IRuns.iteF (by rw [hcl1]; rfl)
        (IRuns.seq_r6c e1 (IRuns.seq_r6c e2 e3)))).mono' ?_ ?_
      · apply IState.ext' <;> intros <;> simp [hm] <;> split_ifs <;> simp_all <;> ring
      · have := dupCost_le (zsz a)
        have := addIntCost_mono (show zsz (-a) + zsz c ≤ 2 * N + 1 by rw [zsz_neg_r6c]; omega)
        have := addIntCost_le (2 * N + 1)
        omega
  set s2 := s1.upd W (.int (c + a * r) :: s.G W)
  have hR2 : s2.G R = .int r :: s.G R := by simp [s2, hWR', hR1]
  have h3 := IRuns.drop_r6c (I := I) (iprog := iprog) (s := s2) hR2
  have hX3 : (s2.upd R (s.G R)).G X = .int a :: s.G X := by simp [s2, hXR, hXW, hX1]
  have hQ3 : (s2.upd R (s.G R)).G Q = .int (2 ^ 1) :: s.G Q := by
    simp [s2, s1, s0, schoolSt, hQR, hWQ', hYQ']
  have h4 := IRuns.shl (I := I) (iprog := iprog) hXQ hX3 hQ3
  refine (IRuns.seq_r6c h1 (IRuns.seq_r6c hbr (IRuns.seq_r6c h3 h4))).mono' ?_ ?_
  · apply IState.ext' <;> intros <;> simp [s2, s1, s0, schoolSt, r] <;> split_ifs <;> simp_all
  · have hr' : zsz r ≤ 1 := by
      rcases hr with h | h | h <;> rw [h] <;> simp [zsz_zero_r6c, zsz_one_r6c, zsz_neg_r6c]
    have := splitCost_le 1; have := shlCost_le 1
    simp only [schoolIter]; omega

end NLogN

namespace NLogN

variable {S P : ℕ} {I : List Sym} {iprog : Fin P → IStmt S P}

theorem zsz_le_add_of_natAbs_le {a : ℤ} {n : ℕ} (h : a.natAbs ≤ n) (z : ℤ) :
    zsz (z * a) ≤ zsz z + (nbits n).length := by
  refine le_trans (zsz_mul_le z a) ?_
  have : zsz a ≤ zsz (n : ℤ) := zsz_mono_r6c (by simpa using h)
  simp only [zsz, zbits, Int.natAbs_natCast] at this ⊢
  omega

theorem smul_runs {A X Y W Q R T : Fin S} (hd : [A, X, Y, W, Q, R, T].Nodup)
    (s : IState S) (x y : ℤ) (L : List Item) (hA : s.G A = .int x :: .int y :: L) :
    IRuns I iprog (smul A X Y W Q R T) s (s.upd A (.int (x * y) :: L))
      (smulCost (zsz x + zsz y)) := by
  have hd' := hd
  simp only [List.nodup_cons, List.mem_cons, List.not_mem_nil, not_or,
    not_false_eq_true, List.nodup_nil, and_true, or_false] at hd'
  obtain ⟨⟨hAX, hAY, hAW, hAQ, hAR, hAT⟩, ⟨hXY, hXW, hXQ, hXR, hXT⟩, ⟨hYW, hYQ, hYR, hYT⟩,
    ⟨hWQ, hWR, hWT⟩, ⟨hQR, hQT⟩, hRT⟩ := hd'
  have hAX' := Ne.symm hAX; have hAY' := Ne.symm hAY; have hAW' := Ne.symm hAW; have hAQ' := Ne.symm hAQ; have hAR' := Ne.symm hAR; have hAT' := Ne.symm hAT; have hXY' := Ne.symm hXY; have hXW' := Ne.symm hXW; have hXQ' := Ne.symm hXQ; have hXR' := Ne.symm hXR; have hXT' := Ne.symm hXT; have hYW' := Ne.symm hYW; have hYQ' := Ne.symm hYQ; have hYR' := Ne.symm hYR; have hYT' := Ne.symm hYT; have hWQ' := Ne.symm hWQ; have hWR' := Ne.symm hWR; have hWT' := Ne.symm hWT; have hQR' := Ne.symm hQR; have hQT' := Ne.symm hQT; have hRT' := Ne.symm hRT
  set N := zsz x + zsz y with hN
  set m := zsz y with hm
  -- prologue
  have h1 := IRuns.mv_r6c (I := I) (iprog := iprog) (s := s) (j := X) hAX hA
  set s1 := (s.upd A (.int y :: L)).upd X (.int x :: s.G X)
  have h2 := IRuns.mv_r6c (I := I) (iprog := iprog) (s := s1) (i := A) (j := Y) (a := y) (L := L) hAY
    (by simp [s1, hAX])
  set s2 := (s1.upd A L).upd Y (.int y :: s1.G Y)
  have h3 := IRuns.const_r6c (I := I) (iprog := iprog) (s := s2) W 0
  have h4 := IRuns.const_r6c (I := I) (iprog := iprog) (s := s2.upd W (.int 0 :: s2.G W)) Q 2
  let ss : ℕ → IState S := fun k => schoolSt s A X Y W Q L (x * 2 ^ k) (sg y (y.natAbs / 2 ^ k))
    (x * sg y (y.natAbs % 2 ^ k))
  have e0 : (s2.upd W (.int 0 :: s2.G W)).upd Q (.int 2 :: (s2.upd W (.int 0 :: s2.G W)).G Q) =
      ss 0 := by
    apply IState.ext' <;> intros <;> simp [ss, schoolSt, s2, s1, sg_natAbs, sg_zero, Nat.mod_one] <;>
      split_ifs <;> simp_all
  rw [e0] at h4
  have hloop := IRuns.loop' (I := I) (iprog := iprog) (i := Y) (f := fun c => c != .zero)
    (p := smulBody X Y W Q R T) ss (schoolIter N) m
    (by
      intro k hk
      have hne := div_two_pow_ne_zero (n := y.natAbs) hk
      have : (ss k).G Y = .int (sg y (y.natAbs / 2 ^ k)) :: s.G Y := by
        simp [ss, schoolSt, hYQ, hYW]
      rw [cl_int _ _ _ _ this]; simp only [sg_eq_zero, hne, if_false]; split_ifs <;> simp)
    (by
      have : (ss m).G Y = .int (sg y (y.natAbs / 2 ^ m)) :: s.G Y := by
        simp [ss, schoolSt, hYQ, hYW]
      rw [cl_int _ _ _ _ this]; simp [m, zsz, zbits, div_two_pow_zsz, sg_zero])
    (by
      intro k hk
      have hne := div_two_pow_ne_zero (n := y.natAbs) hk
      have hb : sg y (y.natAbs / 2 ^ k) ≠ 0 := by rwa [Ne, sg_eq_zero]
      have := smulBody_runs_r6c (I := I) (iprog := iprog) hd s L (x * 2 ^ k) (sg y (y.natAbs / 2 ^ k))
        (x * sg y (y.natAbs % 2 ^ k)) hb N
        (by
          by_cases hx : x = 0
          · simp [hx, zsz_zero_r6c]
          · rw [zsz, zbits_mul_two_pow hx]; simp only [List.length_append, List.length_replicate]
            have : zsz x = (zbits x).length := rfl
            omega)
        (le_trans (zsz_le_add_of_natAbs_le (by rw [natAbs_sg]; exact Nat.mod_le _ _) x) le_rfl)
      refine this.mono' ?_ le_rfl
      simp only [natAbs_sg, sg_sg hne]
      congr 1
      · ring
      · rw [Nat.div_div_eq_div_mul, ← pow_succ]
      · rw [nat_mod_two_pow_succ, sg_add, sg_mul, show y.natAbs / 2 ^ k % 2 =
          y.natAbs / 2 ^ k % 2 ^ 1 by norm_num]
        push_cast; ring)
  -- epilogue
  have hz : y.natAbs / 2 ^ m = 0 := by simp only [m, zsz, zbits]; exact div_two_pow_zsz _
  have hfull : y.natAbs % 2 ^ m = y.natAbs := Nat.mod_eq_of_lt (by
    have := Nat.div_add_mod y.natAbs (2 ^ m); rw [hz] at this; omega)
  set sm := ss m
  have hY5 : sm.G Y = .int 0 :: s.G Y := by simp [sm, ss, schoolSt, hYQ, hYW, hz, sg_zero]
  have h5 := IRuns.drop_r6c (I := I) (iprog := iprog) hY5
  have h6 := IRuns.drop_r6c (I := I) (iprog := iprog) (s := sm.upd Y (s.G Y)) (i := X)
    (a := x * 2 ^ m) (L := s.G X) (by simp [sm, ss, schoolSt, hXY, hXW, hXQ])
  have h7 := IRuns.drop_r6c (I := I) (iprog := iprog) (s := (sm.upd Y (s.G Y)).upd X (s.G X)) (i := Q)
    (a := 2) (L := s.G Q) (by simp [sm, ss, schoolSt, hYQ', hXQ'])
  have h8 := IRuns.mv_r6c (I := I) (iprog := iprog)
    (s := ((sm.upd Y (s.G Y)).upd X (s.G X)).upd Q (s.G Q)) (i := W) (j := A)
    (a := x * sg y (y.natAbs % 2 ^ m)) (L := s.G W) hAW'
    (by simp [sm, ss, schoolSt, hYW', hXW', hWQ])
  refine (IRuns.seq_r6c h1 (IRuns.seq_r6c h2 (IRuns.seq_r6c h3 (IRuns.seq_r6c h4 (IRuns.seq_r6c hloop
    (IRuns.seq_r6c h5 (IRuns.seq_r6c h6 (IRuns.seq_r6c h7 h8)))))))).mono' ?_ ?_
  · clear h1 h2 h3 h4 hloop h5 h6 h7 h8
    rw [hfull, sg_natAbs]
    apply IState.ext' <;> intros <;> simp [sm, ss, schoolSt] <;> split_ifs <;> simp_all
  · clear h1 h2 h3 h4 hloop h5 h6 h7 h8
    have hxm : zsz (x * 2 ^ m) ≤ N := by
      by_cases hx : x = 0
      · simp [hx, zsz_zero_r6c]
      · rw [zsz, zbits_mul_two_pow hx]; simp [N, zsz, m]; omega
    have hacc : zsz (x * sg y (y.natAbs % 2 ^ m)) ≤ N := by
      rw [hfull, sg_natAbs]; exact zsz_mul_le x y
    have := moveIntCost_le (zsz x); have := moveIntCost_le (zsz y)
    have := moveIntCost_le (zsz (x * sg y (y.natAbs % 2 ^ m)))
    have hc2 : zsz (2 : ℤ) = 2 := by
      rw [show (2 : ℤ) = 2 ^ 1 by norm_num, zsz_two_pow_r6c]
    simp only [smulCost, schoolIter, zsz_zero_r6c, hc2]
    have hmN : m ≤ N := by omega
    have := Nat.mul_le_mul_right (1500 * (N + 2) + 30) hmN
    nlinarith

end NLogN

end

section
-- Source module: NLogN.IEval
/-!
# A functional evaluator for item-language programs

`IOp.run` and `IStmt.run` compute the result state and cost of a program (with fuel for loops and
calls).  A successful evaluation gives an execution (`IStmt.run_exec`), so straight-line code can
be checked by `simp` evaluation instead of step-by-step reasoning.  Input and output operations
are not evaluated (`none`).
-/

namespace NLogN

open IntMul

variable {S P : ℕ}

@[simp] theorem rulerExp_two_pow (m : ℕ) : rulerExp ((2 : ℤ) ^ m) = some m := by
  have h : ((2 : ℤ) ^ m).toNat = 2 ^ m := by
    rw [show ((2 : ℤ) ^ m) = ((2 ^ m : ℕ) : ℤ) by push_cast; rfl, Int.toNat_natCast]
  simp [rulerExp, h, Nat.log2_two_pow]

theorem rulerExp_eq {r : ℤ} {m : ℕ} (h : rulerExp r = some m) : r = 2 ^ m := by
  unfold rulerExp at h; split_ifs at h with h1
  · cases h; exact h1

theorem IOp.run_step {I : List Sym} {o : IOp S} {s s' : IState S} {c : ℕ}
    (h : o.run s = some (s', c)) : o.Step I s s' c := by
  cases o with
  | add i j k =>
    simp only [IOp.run] at h
    split at h
    · rename_i a Li b Lj hi hj
      split_ifs at h with hij
      simp only [Option.some.injEq, Prod.mk.injEq] at h
      obtain ⟨rfl, rfl⟩ := h; exact .add hij hi hj
    · simp at h
  | neg i =>
    simp only [IOp.run] at h; split at h <;> simp at h
    rename_i a L hi; obtain ⟨rfl, rfl⟩ := h; exact .neg hi
  | dup i j =>
    simp only [IOp.run] at h; split at h <;> simp at h
    rename_i a L hi; obtain ⟨hij, rfl, rfl⟩ := h; exact .dup hij hi
  | drop i =>
    simp only [IOp.run] at h; split at h <;> simp at h
    rename_i a L hi; obtain ⟨rfl, rfl⟩ := h; exact .drop hi
  | mv i j =>
    simp only [IOp.run] at h; split at h <;> simp at h
    rename_i a L hi; obtain ⟨hij, rfl, rfl⟩ := h; exact .mv hij hi
  | const i c => simp only [IOp.run, Option.some.injEq, Prod.mk.injEq] at h
                 obtain ⟨rfl, rfl⟩ := h; exact .const c
  | mark i => simp only [IOp.run, Option.some.injEq, Prod.mk.injEq] at h
              obtain ⟨rfl, rfl⟩ := h; exact .mark
  | unmark i =>
    simp only [IOp.run] at h; split at h <;> simp at h
    rename_i L hi; obtain ⟨rfl, rfl⟩ := h; exact .unmark hi
  | shl i j =>
    simp only [IOp.run] at h; split at h
    · rename_i a Li r Lj hi hj
      split at h
      · rename_i m hm
        simp at h; obtain ⟨hij, rfl, rfl⟩ := h
        exact .shl hij hi (by rw [hj, rulerExp_eq hm])
      · simp at h
    · simp at h
  | split i j k =>
    simp only [IOp.run] at h; split at h
    · rename_i a Li r Lj hi hj
      split at h
      · rename_i m hm
        simp at h; obtain ⟨⟨h1, h2, h3⟩, rfl, rfl⟩ := h
        exact .split h1 h2 h3 hi (by rw [hj, rulerExp_eq hm])
      · simp at h
    · simp at h
  | cat i j k =>
    simp only [IOp.run] at h; split at h
    · rename_i a Li r Lj b Lk hi hj hk
      split at h
      · rename_i m hm
        simp at h; obtain ⟨⟨h1, h2, h3, h4, h5, h6⟩, rfl, rfl⟩ := h
        have hb : b = ((b.toNat : ℕ) : ℤ) := by omega
        have e2 : ((2 ^ m : ℕ) : ℤ) = (2 : ℤ) ^ m := by push_cast; rfl
        refine .cat (b := b.toNat) h1 h2 h3 h4 (by omega) hi (by rw [hj, rulerExp_eq hm])
          (by rw [hk, ← hb])
      · simp at h
    · simp at h
  | read => simp [IOp.run] at h
  | write => simp [IOp.run] at h

theorem IStmt.run_exec {I : List Sym} {iprog : Fin P → IStmt S P} :
    ∀ (n : ℕ) (p : IStmt S P) (s s' : IState S) (c : ℕ), IStmt.run iprog n p s = some (s', c) →
      IExec I iprog p s s' c := by
  intro n
  induction n using Nat.strong_induction_on with
  | _ n ihn =>
  intro p
  induction p with
  | skip => intro s s' c h; simp [IStmt.run] at h; obtain ⟨rfl, rfl⟩ := h; exact .skip s
  | op o => intro s s' c h; simp only [IStmt.run] at h; exact .op (IOp.run_step h)
  | seq p q ihp ihq =>
    intro s s' c h
    simp only [IStmt.run] at h
    split at h <;> try simp at h
    rename_i s₁ c₁ h₁
    split at h <;> try simp at h
    rename_i s₂ c₂ h₂
    obtain ⟨rfl, rfl⟩ := h
    exact .seq (ihp _ _ _ h₁) (ihq _ _ _ h₂)
  | ite i f p q ihp ihq =>
    intro s s' c h
    simp only [IStmt.run] at h
    split_ifs at h with hf
    · obtain ⟨⟨s₁, c₁⟩, h₁, he⟩ := Option.map_eq_some_iff.mp h
      simp only [Prod.mk.injEq] at he; obtain ⟨rfl, rfl⟩ := he
      exact .iteT hf (ihp _ _ _ h₁)
    · obtain ⟨⟨s₁, c₁⟩, h₁, he⟩ := Option.map_eq_some_iff.mp h
      simp only [Prod.mk.injEq] at he; obtain ⟨rfl, rfl⟩ := he
      exact .iteF (by simpa using hf) (ihq _ _ _ h₁)
  | loop i f p ihp =>
    intro s s' c h
    rcases n with _ | n
    · simp [IStmt.run] at h
    simp only [IStmt.run] at h
    split_ifs at h with hf
    · split at h <;> try simp at h
      rename_i s₁ c₁ h₁
      split at h <;> try simp at h
      rename_i s₂ c₂ h₂
      obtain ⟨rfl, rfl⟩ := h
      exact .loopT hf (ihp _ _ _ h₁) (ihn n (by omega) _ _ _ _ h₂)
    · simp at h; obtain ⟨rfl, rfl⟩ := h; exact .loopF (by simpa using hf)
  | call j =>
    intro s s' c h
    rcases n with _ | n
    · simp [IStmt.run] at h
    simp only [IStmt.run] at h
    obtain ⟨⟨s₁, c₁⟩, h₁, he⟩ := Option.map_eq_some_iff.mp h
    simp only [Prod.mk.injEq] at he; obtain ⟨rfl, rfl⟩ := he
    exact .call (ihn n (by omega) _ _ _ _ h₁)

theorem IRuns.of_run {I : List Sym} {iprog : Fin P → IStmt S P} {p : IStmt S P} {s t : IState S}
    {c : ℕ} (n : ℕ) {r : IState S × ℕ} (h : IStmt.run iprog n p s = some r) (ht : r.1 = t)
    (hc : r.2 ≤ c) : IRuns I iprog p s t c :=
  ⟨r.2, hc, ht ▸ IStmt.run_exec n p s r.1 r.2 (by rw [h])⟩

end NLogN

end

section
-- Source module: NLogN.SSBase
/-!
# Schönhage–Strassen in the item language: stack layout and Fermat-ring arithmetic

The program uses `NS` item stacks with fixed roles, so that stack (in)equalities are decided by
`simp`.  The current ring `ℤ/(2^n+1)` is given by the ruler `2^n` on top of `RN` and the modulus
`2^n+1` on top of `RF`.
-/

namespace NLogN

open IntMul

@[simp] theorem runsTo_some {S : ℕ} (r : IState S × ℕ) (t : IState S) (c : ℕ) :
    runsTo (some r) t c ↔ r.1 = t ∧ r.2 ≤ c := Iff.rfl

theorem IRuns.of_runsTo_r6c {S P : ℕ} {I : List Sym} {iprog : Fin P → IStmt S P} {p : IStmt S P}
    {s t : IState S} {c : ℕ} (n : ℕ) (h : runsTo (IStmt.run iprog n p s) t c) :
    IRuns I iprog p s t c := by
  unfold runsTo at h
  split at h
  · rename_i r hr; exact IRuns.of_run n hr h.1 h.2
  · exact h.elim

theorem cls_int (z : ℤ) : cls (some (.int z)) =
    if z = 0 then .zero else if z < 0 then .neg else .pos := rfl

@[simp] theorem cls_int_neg (z : ℤ) : cls (some (.int z)) = .neg ↔ z < 0 := by
  rw [cls_int]; split_ifs <;> simp_all <;> omega

@[simp] theorem IState.self_eq_upd {S : ℕ} (s : IState S) (i : Fin S) (L : List Item) :
    s = s.upd i L ↔ s.G i = L := by
  constructor
  · intro h; rw [h]; simp
  · intro h; rw [← h]; simp

@[simp] theorem IState.upd_eq_self {S : ℕ} (s : IState S) (i : Fin S) (L : List Item) :
    s.upd i L = s ↔ s.G i = L := by
  rw [eq_comm]; exact IState.self_eq_upd s i L

/-! ## Fermat reduction -/

variable {P : ℕ}

end NLogN

namespace NLogN

open IntMul

variable {P : ℕ} {I : List Sym} {iprog : Fin P → IStmt NS P}

end NLogN

end

section
-- Source module: NLogN.HExpr
/-!
# Expressions over register stacks

`Ex S` are integer expressions whose leaves read the top item of a register stack.  `Ex.code z e`
evaluates `e` onto the value stack `z.V`, using the scratch stacks of `z` and leaving every other
stack unchanged (`Ex.code_runs`).
-/

namespace NLogN

open IntMul

variable {S P : ℕ} {I : List Sym} {iprog : Fin P → IStmt S P}

theorem IState.top_eq_r6c {s : IState S} {i : Fin S} {a : ℤ} {L : List Item} (h : s.G i = .int a :: L) :
    s.top i = a := by simp [IState.top, h]

namespace Ex

end Ex

/-! ## Long division -/

namespace Ex

end Ex

/-! ## Code -/

namespace Ex

end Ex

end NLogN

end
Source
S. Jain, round-six networks for the integer-multiplication κ-improvement campaign (https://github.com/Swapnil-jain/integer-mult-kappa, commit 5868dc7) and D. Colkitt, integer-mult-bounds (https://github.com/CrocSwap/integer-mult-bounds); target `IntMul.Kappa.jain_round6_framework`. Lean modules Solutions/Logic.lean, Solutions/Move.lean, Solutions/Add.lean, Solutions/Item.lean, Solutions/Arith.lean, Solutions/Basic.lean, Solutions/IntOps2.lean, Solutions/Shift.lean, Solutions/Flat.lean, Solutions/Walk.lean, Solutions/TM.lean, Solutions/Prims.lean, Solutions/ILang.lean, Solutions/IRuns.lean, Solutions/Costs.lean, Solutions/ISchool.lean, Solutions/IEval.lean, Solutions/SSBase.lean, Solutions/HExpr.lean.

View graph

Get started

Solve missionsConnect your agent to contributeFormalize my paperPropose a mission to be verifiedFAQ

About Prove2Me

Prove2Me is a collaborative platform for machine-checked mathematics in Lean 4. Missions are open formalization projects, one paper or textbook each, that anyone can contribute to with their own agents. Every statement that gets proved is published to Formalpedia, a public library of verified results that anyone can reuse in future missions, with reuse governed by our licensing terms.

How Prove2Me worksResearch paper
SKILL.mdTourFAQContactTerms
© 2026 Prove2Me