Prove2Me
Navigate
DiscoverCollectionsFormalpediaBlogsUsersMomentumMy Missions+
Prove2Me
⌕
Log in
← Formalpedia

κ-improvement development, part 1: array model and networks

Definition
IntMul_Kappa_K_Defs1

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

complexity-theoryinteger-multiplicationkappa-improvement

Definitions (part 1): array-model and network definitions for the OpenAI-preprint formalization (mixed-radix arrays, pull maps, interchange networks, labelled networks, butterfly layers, synthesis, resampling, parameters).

Source modules (in order): Solutions.AModel, Solutions.AModelCost, Solutions.AModelRuns, Solutions.AModelShear, Solutions.AModelNet, Solutions.AModelProp11.

The file was generated from the development by deleting all theorems not needed by the definitions; it keeps all definitions plus the lemmas the definitions themselves need (proof fields of structures, decreasing_by facts) and simple @[simp]/rfl lemmas.

Definition code
-- OpenAI-preprint / κ-improvement development (integer-multiplication-kappa), generated by skeleton subtraction.

import Definitions.Def_IntMul_MultitapeModel
import Mathlib

set_option autoImplicit false
set_option relaxedAutoImplicit false
set_option maxHeartbeats 1000000

section
-- Source module: Solutions.AModel
/-!
# Array model: mixed-radix arrays, pull maps, and linear address maps via Bruhat factors

Arrays are lists in row-major lexicographic order over fields (first field most significant).
All denotations are plain `List` functions.
-/

namespace AModel

open Matrix

/-! ## 1. Mixed-radix basics -/

/-- A shape is the list of field sizes. -/
abbrev Shape := List ℕ

/-- Volume (number of entries) of a shape. -/
def vol (sh : Shape) : ℕ := sh.prod

/-- Mixed-radix decoding, first field most significant. -/
def decode : Shape → ℕ → List ℕ
  | [], _ => []
  | q :: sh, n => (n / vol sh) % q :: decode sh (n % vol sh)

/-- Mixed-radix encoding, first field most significant. -/
def encode : Shape → List ℕ → ℕ
  | [], _ => 0
  | _ :: sh, a => a.headD 0 * vol sh + encode sh a.tail

/-- A valid address: same length as the shape, each digit below its field size. -/
def Valid (sh : Shape) (a : List ℕ) : Prop := List.Forall₂ (· < ·) a sh

@[simp] lemma vol_nil : vol [] = 1 := rfl
@[simp] lemma vol_cons (q : ℕ) (sh : Shape) : vol (q :: sh) = q * vol sh := List.prod_cons

lemma valid_iff (sh : Shape) (a : List ℕ) :
    Valid sh a ↔ a.length = sh.length ∧
      ∀ (k : ℕ) (h₁ : k < a.length) (h₂ : k < sh.length), a[k] < sh[k] := by
  unfold Valid; rw [List.forall₂_iff_get]; rfl

lemma Valid.length_eq {sh : Shape} {a : List ℕ} (h : Valid sh a) : a.length = sh.length :=
  ((valid_iff sh a).1 h).1

lemma encode_lt {sh : Shape} {a : List ℕ} (h : Valid sh a) : encode sh a < vol sh := by
  induction h with
  | nil => simp [encode]
  | @cons x q as sh hx _ ih =>
    simp only [encode, List.headD_cons, List.tail_cons, vol_cons]
    have : (x + 1) * vol sh ≤ q * vol sh := Nat.mul_le_mul_right _ hx
    nlinarith

lemma valid_decode {sh : Shape} {m : ℕ} (h : m < vol sh) : Valid sh (decode sh m) := by
  induction sh generalizing m with
  | nil => exact List.Forall₂.nil
  | cons q sh ih =>
    simp only [vol_cons] at h
    have hV : 0 < vol sh := Nat.pos_of_ne_zero (by rintro h'; simp [h'] at h)
    have hq : 0 < q := Nat.pos_of_ne_zero (by rintro h'; simp [h'] at h)
    exact List.Forall₂.cons (Nat.mod_lt _ hq) (ih (Nat.mod_lt m hV))

/-! ## 2. Pull maps -/

/-- Array permutation by its pull map: output address `y` reads input address `g y`. -/
def pull {α : Type*} (sh sh' : Shape) (g : List ℕ → List ℕ) (d : α) (xs : List α) : List α :=
  (List.range (vol sh')).map (fun j => xs.getD (encode sh (g (decode sh' j))) d)

section Pull
variable {α : Type*}

end Pull

/-! ## 3. Active fields -/

/-- `n` active fields of size `Q` inside the shape `sh`, at strictly increasing positions. -/
structure Layout (sh : Shape) (n Q : ℕ) where
  pos : Fin n → Fin sh.length
  mono : StrictMono pos
  size : ∀ i, sh.get (pos i) = Q

variable {sh : Shape} {n Q : ℕ} (Λ : Layout sh n Q)

/-- The active field (if any) at list index `k`. -/
def Layout.actIdx (k : ℕ) : Option (Fin n) :=
  (List.finRange n).find? (fun i => (Λ.pos i : ℕ) == k)

/-- Active vector of an address. -/
def act (a : List ℕ) (i : Fin n) : ZMod Q := ((a.getD (Λ.pos i) 0 : ℕ) : ZMod Q)

/-- Replace the active digits of `a` by `(v i).val`. -/
def setAct (a : List ℕ) (v : Fin n → ZMod Q) : List ℕ :=
  a.mapIdx fun k x => match Λ.actIdx k with
    | some i => (v i).val
    | none => x

/-- Address map induced by a map on active vectors. -/
def vmap (F : (Fin n → ZMod Q) → (Fin n → ZMod Q)) (a : List ℕ) : List ℕ :=
  setAct Λ a (F (act Λ a))

/-- Address map of a matrix acting on the active vector. -/
def amap (L : Matrix (Fin n) (Fin n) (ZMod Q)) (a : List ℕ) : List ℕ :=
  setAct Λ a (L *ᵥ act Λ a)

namespace Layout

end Layout

/-! ## 4. Controlled shift -/

/-- Active-vector form of the controlled shift. -/
def shiftVec (i : Fin n) (u : ZMod Q) (c : Fin n → ZMod Q) (x : Fin n → ZMod Q) :
    Fin n → ZMod Q :=
  Function.update x i (u * x i + ∑ j, c j * x j)

/-- Inverse of `shiftVec` (when `u` is a unit and `c j = 0` for `j ≥ i`). -/
def shiftInvVec (i : Fin n) (u : ZMod Q) (c : Fin n → ZMod Q) (y : Fin n → ZMod Q) :
    Fin n → ZMod Q :=
  Function.update y i (u⁻¹ * (y i - ∑ j, c j * y j))

/-- Controlled shift address map: `x_i ↦ u x_i + Σ_j c_j x_j`. -/
def shiftMap (i : Fin n) (u : ZMod Q) (c : Fin n → ZMod Q) (a : List ℕ) : List ℕ :=
  setAct Λ a (Function.update (act Λ a) i (u * act Λ a i + ∑ j, c j * act Λ a j))

/-- Its explicit inverse: `y_i ↦ u⁻¹ (y_i - Σ_j c_j y_j)`. -/
def shiftInv (i : Fin n) (u : ZMod Q) (c : Fin n → ZMod Q) (a : List ℕ) : List ℕ :=
  setAct Λ a (Function.update (act Λ a) i (u⁻¹ * (act Λ a i - ∑ j, c j * act Λ a j)))

/-! ## 5. Field transposition -/

/-- Exchange the digits at `pos i` and `pos j`. -/
def swapMap (i j : Fin n) (a : List ℕ) : List ℕ :=
  (a.set (Λ.pos i) (a.getD (Λ.pos j) 0)).set (Λ.pos j) (a.getD (Λ.pos i) 0)

section Shift

end Shift

section Swap

end Swap

/-! ## Primitive steps and their composition -/

/-- A primitive step on the active fields. -/
inductive Step (n Q : ℕ) where
  /-- Controlled shift (d): `x_i ↦ u x_i + Σ_j c_j x_j`. -/
  | shift (i : Fin n) (u : ZMod Q) (c : Fin n → ZMod Q)
  /-- Field transposition (e): exchange fields `i` and `j`. -/
  | swap (i j : Fin n)

namespace Step

/-- Well-formedness: shifts have unit coefficient and only read earlier fields. -/
def WF : Step n Q → Prop
  | shift i u c => IsUnit u ∧ ∀ j, i ≤ j → c j = 0
  | swap _ _ => True

/-- Action on active vectors. -/
def vec : Step n Q → (Fin n → ZMod Q) → (Fin n → ZMod Q)
  | shift i u c => shiftVec i u c
  | swap i j => fun x => x ∘ Equiv.swap i j

/-- Inverse action on active vectors. -/
def invVec : Step n Q → (Fin n → ZMod Q) → (Fin n → ZMod Q)
  | shift i u c => shiftInvVec i u c
  | swap i j => fun x => x ∘ Equiv.swap i j

/-- Address map of a step. -/
def addr (s : Step n Q) : List ℕ → List ℕ :=
  match s with
  | shift i u c => shiftMap Λ i u c
  | swap i j => swapMap Λ i j

/-- Explicit inverse address map of a step. -/
def inv (s : Step n Q) : List ℕ → List ℕ :=
  match s with
  | shift i u c => shiftInv Λ i u c
  | swap i j => swapMap Λ i j

/-- Array action of a step: `pull` with the inverse address map. -/
def arr {α : Type*} (d : α) (s : Step n Q) (xs : List α) : List α :=
  pull sh sh (s.inv Λ) d xs

variable [NeZero Q]

end Step

/-- Run a list of steps on an address (first step first). -/
def run (steps : List (Step n Q)) (a : List ℕ) : List ℕ :=
  steps.foldl (fun b s => s.addr Λ b) a

/-- Run a list of steps on an array (first step first). -/
def runArr {α : Type*} (d : α) (steps : List (Step n Q)) (xs : List α) : List α :=
  steps.foldl (fun ys s => s.arr Λ d ys) xs

/-- Run a list of steps on an active vector (first step first). -/
def runVec (steps : List (Step n Q)) (x : Fin n → ZMod Q) : Fin n → ZMod Q :=
  steps.foldl (fun y s => s.vec y) x

/-- Composition of the inverse address maps: `inv s₁ ∘ inv s₂ ∘ ⋯ ∘ inv sₖ`. -/
def invChain (steps : List (Step n Q)) : List ℕ → List ℕ :=
  steps.foldr (fun s g => s.inv Λ ∘ g) id

section Run
variable [NeZero Q]

end Run

/-! ## 6. Linear address maps through Bruhat factors -/

section Bruhat

/-- The controlled shift realizing row `i` of a lower triangular `E`:
`x_i ↦ E i i * x_i + Σ_{j < i} E i j * x_j`. -/
def shiftStep (E : Matrix (Fin n) (Fin n) (ZMod Q)) (i : Fin n) : Step n Q :=
  .shift i (E i i) (fun j => if j < i then E i j else 0)

/-- Shifts for `E`, in descending target order `n-1, …, 0`. -/
def shiftSteps (E : Matrix (Fin n) (Fin n) (ZMod Q)) : List (Step n Q) :=
  (List.finRange n).reverse.map (shiftStep E)

/-- Field transpositions, one per pair. -/
def swapSteps (ts : List (Fin n × Fin n)) : List (Step n Q) :=
  ts.map fun p => .swap p.1 p.2

/-- The full pipeline for `E₁ * w * E₂`: shifts for `E₂`, then swaps, then shifts for `E₁`. -/
def bruhatSteps (E₁ : Matrix (Fin n) (Fin n) (ZMod Q)) (ts : List (Fin n × Fin n))
    (E₂ : Matrix (Fin n) (Fin n) (ZMod Q)) : List (Step n Q) :=
  shiftSteps E₂ ++ swapSteps ts ++ shiftSteps E₁

variable {E : Matrix (Fin n) (Fin n) (ZMod Q)}

/-- The product of the transpositions of `ts`, leftmost first. -/
def swapProd (ts : List (Fin n × Fin n)) : Equiv.Perm (Fin n) :=
  (ts.map fun p => Equiv.swap p.1 p.2).prod

variable [NeZero Q]

end Bruhat

end AModel

end

section
-- Source module: Solutions.AModelCost
/-!
# Cost recurrences for the A-model (§4, Prop. 11 eq. (12) and Lemma 12)

* `power_width_recurrence`: `F (k+1) ≤ (s/W) F k + C` with `s/W < m^τ` gives
  `F k ≤ K (m^k)^τ`.
* `power_width_time`: the volume-scaled ("time") version
  `T (k+1) V ≤ C V + s T k (V/W)` gives `T k V ≤ K V (m^k)^τ`.
* `arbitrary_width_sum`: digit decomposition `Σ_j a_j (m^j)^τ ≤ K' e^τ`.
* `log_two_mul_le_rpow`: `log (2e) ≤ c e^τ`.
* `arbitrary_width_cost`: combination, total cost `≤ K'' V e^τ`.
-/

namespace AModel

open Finset

/-- The coefficient sequence `G 0 = C`, `G (k+1) = (s/W) G k + C`. -/
noncomputable def coeffSeq (s W : ℕ) (C : ℝ) : ℕ → ℝ
  | 0 => C
  | k + 1 => ((s : ℝ) / W) * coeffSeq s W C k + C

/-- The `j`-th base-`m` digit of `e` (zero beyond the length). -/
def digit (m e j : ℕ) : ℕ := (Nat.digits m e).getD j 0

end AModel

end

section
-- Source module: Solutions.AModelRuns
/-!
# Array model, Stage 2: runs of pivots as single interchanges of merged fields

Builds on `Solutions/AModel.lean` (whose definitions are unchanged).

* §1 Reshape (`mergeShape`, `mergeAddr`, `splitAddr`): merging adjacent fields is free —
  `pull_merge`, `pull_split`, general `pull_reshape`.
* §2 Block swap (`blockSwap`): it is the composition of the single position swaps
  (`chain_runPairs`), and after merging both runs it is one swap of two merged fields
  (`merge2_blockSwap`, `pull_blockSwap`).
* §3 A run of `r` consecutive pivots on contiguous active fields acts on arrays as that block swap
  (`runArr_pivotRun`), hence as one merged-field interchange (`runArr_pivotRun_merged`).
* §4 A pivot list splits into `numRuns` maximal runs (`groupRuns`, `flatMap_groupRuns`), and
  `runArr` over its swaps is the composition of one merged-field swap per run
  (`runArr_swapSteps_runs`).
-/

namespace AModel

/-! ## 1. Reshape -/

section Reshape

/-- Merge the fields `[i, i+r)` of `sh` into one field. -/
def mergeShape (sh : Shape) (i r : ℕ) : Shape :=
  sh.take i ++ [vol ((sh.drop i).take r)] ++ sh.drop (i + r)

/-- Merge the digits `[i, i+r)` of an address into one digit. -/
def mergeAddr (sh : Shape) (i r : ℕ) (a : List ℕ) : List ℕ :=
  a.take i ++ [encode ((sh.drop i).take r) ((a.drop i).take r)] ++ a.drop (i + r)

/-- Split digit `i` of a merged address back into `r` digits. -/
def splitAddr (sh : Shape) (i r : ℕ) (b : List ℕ) : List ℕ :=
  b.take i ++ decode ((sh.drop i).take r) (b.getD i 0) ++ b.drop (i + 1)

end Reshape

/-! ## 2. Block swaps -/

section BlockSwap

/-- Gather digits by an index map: digit `k` of the result is digit `f k` of `a`. -/
def permIdx (f : ℕ → ℕ) (a : List ℕ) : List ℕ :=
  (List.range a.length).map fun k => a.getD (f k) 0

/-- Exchange the digits at list positions `p` and `q` (same formula as `swapMap`). -/
def swapPos (p q : ℕ) (a : List ℕ) : List ℕ :=
  (a.set p (a.getD q 0)).set q (a.getD p 0)

/-- Index map of `swapPos`. -/
def swapIdx (p q k : ℕ) : ℕ := if k = q then p else if k = p then q else k

/-- Index map of `blockSwap`. -/
def bsIdx (i j r k : ℕ) : ℕ :=
  if i ≤ k ∧ k < i + r then k + j - i else if j ≤ k ∧ k < j + r then k + i - j else k

/-- Exchange the digit runs `[i, i+r)` and `[j, j+r)`. -/
def blockSwap (i j r : ℕ) (a : List ℕ) : List ℕ := permIdx (bsIdx i j r) a

/-! ### Segments of a block-swapped address -/

section Segments
variable {i j r : ℕ} {a : List ℕ} (hij : i + r ≤ j) (hj : j + r ≤ a.length)
include hij hj

end Segments

/-! ### Merging both runs -/

/-- Both runs merged (the later run first). -/
def merge2 (sh : Shape) (i j r : ℕ) (a : List ℕ) : List ℕ :=
  mergeAddr (mergeShape sh j r) i r (mergeAddr sh j r a)

/-- The shape with both runs merged. -/
def mergeShape2 (sh : Shape) (i j r : ℕ) : Shape := mergeShape (mergeShape sh j r) i r

/-- Inverse of `merge2`. -/
def split2 (sh : Shape) (i j r : ℕ) (b : List ℕ) : List ℕ :=
  splitAddr sh j r (splitAddr (mergeShape sh j r) i r b)

end BlockSwap

/-! ## 3. A run of consecutive pivots is one merged-field interchange -/

section PivotRun

variable {sh : Shape} {n Q : ℕ} (Λ : Layout sh n Q)

variable [NeZero Q]

end PivotRun

/-! ## 4. Counting runs -/

section Runs

variable {sh : Shape} {n Q : ℕ} (Λ : Layout sh n Q) [NeZero Q]

end Runs

end AModel

end

section
-- Source module: Solutions.AModelShear
/-!
# Shears and interchanges in the array model (Lemma 10 / end of Prop. 11)

Active fields are indexed by `Fin (m + m)`: the `H` fields are `Fin.castAdd m i` (the first `m`),
the `D` fields are `Fin.natAdd m j` (the last `m`). With a `Layout` (strictly increasing
positions) every `H` field therefore precedes every `D` field.
-/

namespace AModel

open Matrix

section Blocks

variable {m Q : ℕ}

/-- `H` part of an active vector. -/
def hv {R : Type*} (x : Fin (m + m) → R) : Fin m → R := fun i => x (Fin.castAdd m i)

/-- `D` part of an active vector. -/
def dv {R : Type*} (x : Fin (m + m) → R) : Fin m → R := fun j => x (Fin.natAdd m j)

/-- A `2 × 2` block matrix over `Fin m ⊕ Fin m`, reindexed to `Fin (m + m)`. -/
def blk (P B C D : Matrix (Fin m) (Fin m) (ZMod Q)) : Matrix (Fin (m + m)) (Fin (m + m)) (ZMod Q) :=
  reindex finSumFinEquiv finSumFinEquiv (fromBlocks P B C D)

/-- The shear `(H, D) ↦ (H + A D, D)`. -/
def shearMat (A : Matrix (Fin m) (Fin m) (ZMod Q)) : Matrix (Fin (m + m)) (Fin (m + m)) (ZMod Q) :=
  AModel.blk 1 A 0 1

/-- The block interchange `(H, D) ↦ (D, H)`. -/
def swapMat : Matrix (Fin (m + m)) (Fin (m + m)) (ZMod Q) := AModel.blk 0 1 1 0

section AmapShear

variable {sh : Shape} {m Q : ℕ} (Λ : Layout sh (m + m) Q) [NeZero Q]

end AmapShear

end Blocks

/-! ## Inverse steps -/

section Rev

variable {n Q : ℕ}

/-- The explicit inverse of a step, as a step: a shift by `u⁻¹` with controls `-u⁻¹ c`. -/
def Step.rev : Step n Q → Step n Q
  | .shift i u c => .shift i u⁻¹ (fun j => -(u⁻¹ * c j))
  | .swap i j => .swap i j

/-- Is the step a field transposition? -/
def Step.isSwap : Step n Q → Bool
  | .shift _ _ _ => false
  | .swap _ _ => true

/-- Inverse of a step list: inverted steps in reverse order. -/
def revSteps (l : List (Step n Q)) : List (Step n Q) := (l.map Step.rev).reverse

end Rev

/-! ## Lower triangular transforms of one block -/

section Tri

variable {m Q : ℕ}

/-- `H` field indices, descending. -/
def hIdx (m : ℕ) : List (Fin (m + m)) := (List.finRange m).reverse.map (Fin.castAdd m)

/-- `D` field indices, descending. -/
def dIdx (m : ℕ) : List (Fin (m + m)) := (List.finRange m).reverse.map (Fin.natAdd m)

/-- Shifts realizing `H ← E H` (one per `H` field, descending targets). -/
def hSteps (E : Matrix (Fin m) (Fin m) (ZMod Q)) : List (Step (m + m) Q) :=
  (hIdx m).map (shiftStep (AModel.blk E 0 0 1))

/-- Shifts realizing `D ← E D` (one per `D` field, descending targets). -/
def dSteps (E : Matrix (Fin m) (Fin m) (ZMod Q)) : List (Step (m + m) Q) :=
  (dIdx m).map (shiftStep (AModel.blk 1 0 0 E))

end Tri

/-! ## The pivot gadget: `H_i ← H_i + D_j` with one interchange -/

section Pivot

variable {m Q : ℕ}

/-- `D_j ← D_j + H_i`, `(H_i, D_j) ← (D_j, H_i)`, `D_j ← H_i − D_j`. -/
def pivotSteps (i j : Fin m) : List (Step (m + m) Q) :=
  [.shift (Fin.natAdd m j) 1 (Pi.single (Fin.castAdd m i) 1),
   .swap (Fin.castAdd m i) (Fin.natAdd m j),
   .shift (Fin.natAdd m j) (-1) (Pi.single (Fin.castAdd m i) 1)]

/-- One gadget per pivot, in list order. -/
def addSteps (ps : List (Fin m × Fin m)) : List (Step (m + m) Q) :=
  ps.flatMap fun p => pivotSteps p.1 p.2

/-- The pivots (positions of the ones) of `W`, in lexicographic order. -/
def pivots (W : Matrix (Fin m) (Fin m) (ZMod Q)) : List (Fin m × Fin m) :=
  (List.finRange m ×ˢ List.finRange m).filter fun p => W p.1 p.2 = 1

end Pivot

/-! ## Lemma 10: the shear through Bruhat-type factors -/

section Shear

variable {m Q : ℕ}

/-- Steps for the shear by `A = E₁ W E₂`: `D ← E₂ D`, `H ← E₁⁻¹ H` (the inverse shifts of
`H ← E₁ H`), `H ← H + W D` (one pivot gadget per one of `W`), `H ← E₁ H`, `D ← E₂⁻¹ D`. -/
def shearSteps (E₁ W E₂ : Matrix (Fin m) (Fin m) (ZMod Q)) : List (Step (m + m) Q) :=
  dSteps E₂ ++ revSteps (hSteps E₁) ++ addSteps (pivots W) ++ hSteps E₁ ++ revSteps (dSteps E₂)

variable {E₁ W E₂ : Matrix (Fin m) (Fin m) (ZMod Q)}

variable {sh : Shape} (Λ : Layout sh (m + m) Q) [NeZero Q]

end Shear

/-! ## Interchange from a shear (end of Prop. 11) -/

section Interchange

variable {m Q : ℕ}

/-- The shift `D_j ← u D_j + c H_j`. -/
def dhStep (u c : ZMod Q) (j : Fin m) : Step (m + m) Q :=
  .shift (Fin.natAdd m j) u (Pi.single (Fin.castAdd m j) c)

/-- `D ← D − H`, one shift per `D` field. -/
def subSteps (m : ℕ) : List (Step (m + m) Q) := (List.finRange m).map (dhStep 1 (-1))

/-- `D ← H − D`, one shift per `D` field. -/
def flipSteps (m : ℕ) : List (Step (m + m) Q) := (List.finRange m).map (dhStep (-1) 1)

variable {sh : Shape} (Λ : Layout sh (m + m) Q) [NeZero Q]

end Interchange

end AModel

end

section
-- Source module: Solutions.AModelNet
/-!
# Row streams and the gate network (Stage 3 of the interchange procedure)

1. Cyclic row split / merge (primitive (b)) on shapes `P :: (W * L) :: rest`:
   row `r = W * g + w` of the big array becomes row `g` of stream `w`.
2. The abstract network theorem: a circuit of pointwise linear gates, executed physically with
   frame changes `Φ (M_g - label)` on every wire, realizes `Φ I0` on every wire, routed by `ρ`.
3. The combination: split, run the network with `Φ_M = pull` by `π_M` on the `rest` digits, merge
   back with `ρ` undone; this equals the `pull` of the whole array by `π_{I0}` on the `rest` digits.
-/

namespace AModel

open Matrix

/-! ## 1. Cyclic row split and merge -/

section RowSplit

variable {α : Type*}

/-- Stream address `(p, g, x)` ↦ big address `(p, W g + w, x)`. -/
def rowSplitAddr (W w : ℕ) (a : List ℕ) : List ℕ :=
  a.headD 0 :: (W * a.tail.headD 0 + w) :: a.tail.tail

/-- Big address `(p, r, x)` ↦ stream address `(p, r / W, x)`. -/
def rowMergeAddr (W : ℕ) (a : List ℕ) : List ℕ :=
  a.headD 0 :: a.tail.headD 0 / W :: a.tail.tail

/-- The stream (role) of a big address `(p, r, x)`: `r % W`. -/
def rowIdx (W : ℕ) [NeZero W] (a : List ℕ) : Fin W :=
  ⟨a.tail.headD 0 % W, Nat.mod_lt _ (NeZero.pos W)⟩

/-- Lift an address map on the `rest` digits to `(p, r, x) ↦ (p, r, g x)`. -/
def rowLift (g : List ℕ → List ℕ) (a : List ℕ) : List ℕ :=
  a.headD 0 :: a.tail.headD 0 :: g a.tail.tail

/-- Stream `w` of the cyclic row split: rows `r` with `r % W = w`, shape `P :: L :: rest`. -/
def rowSplit (P W L : ℕ) (rest : Shape) (w : Fin W) (d : α) (xs : List α) : List α :=
  pull (P :: W * L :: rest) (P :: L :: rest) (rowSplitAddr W w) d xs

/-- Merge `W` streams of shape `P :: L :: rest` back into shape `P :: (W * L) :: rest`. -/
def rowMerge (P W L : ℕ) [NeZero W] (rest : Shape) (d : α) (s : Fin W → List α) : List α :=
  (List.range (vol (P :: W * L :: rest))).map fun j =>
    (s (rowIdx W (decode (P :: W * L :: rest) j))).getD
      (encode (P :: L :: rest) (rowMergeAddr W (decode (P :: W * L :: rest) j))) d

lemma valid_cons2 {P Q : ℕ} {sh : Shape} {a : List ℕ} (h : Valid (P :: Q :: sh) a) :
    ∃ p q x, a = p :: q :: x ∧ p < P ∧ q < Q ∧ Valid sh x := by
  unfold Valid at h
  rcases h with _ | ⟨hp, h'⟩
  rcases h' with _ | ⟨hq, hx⟩
  exact ⟨_, _, _, rfl, hp, hq, hx⟩

lemma valid_cons2_mk {P Q : ℕ} {sh : Shape} {p q : ℕ} {x : List ℕ} (hp : p < P) (hq : q < Q)
    (hx : Valid sh x) : Valid (P :: Q :: sh) (p :: q :: x) :=
  List.Forall₂.cons hp (List.Forall₂.cons hq hx)

lemma valid_rowLift {P L : ℕ} {rest : Shape} {g : List ℕ → List ℕ}
    (hg : ∀ x, Valid rest x → Valid rest (g x)) {a : List ℕ}
    (ha : Valid (P :: L :: rest) a) : Valid (P :: L :: rest) (rowLift g a) := by
  obtain ⟨p, q, x, rfl, hp, hq, hx⟩ := valid_cons2 ha
  exact valid_cons2_mk hp hq (hg x hx)

end RowSplit

/-! ## 2. The abstract network theorem -/

section Network

variable {Wr Ω R M : Type*} [CommRing R] [Fintype Wr] [DecidableEq Wr] [AddGroup M]

/-- A gate: matrix over `R` on the wires, the set of touched wires, and the gate's label. -/
abbrev NGate (Wr R M : Type*) := Matrix Wr Wr R × Finset Wr × M

/-- A gate acts as the identity off its touched set and touched rows only read touched wires. -/
def NGate.OK (g : NGate Wr R M) : Prop :=
  ∀ w w', (w ∉ g.2.1 → g.1 w w' = if w = w' then 1 else 0) ∧
    (w ∈ g.2.1 → w' ∉ g.2.1 → g.1 w w' = 0)

/-- Physical execution of one gate: on each touched wire apply the frame change
`Φ (M_g - current label)`, then the gate pointwise; touched wires get label `M_g`. -/
def physStep (Φ : M → (Ω → R) →ₗ[R] (Ω → R)) (st : (Wr → M) × (Wr → Ω → R))
    (g : NGate Wr R M) : (Wr → M) × (Wr → Ω → R) :=
  (fun w => if w ∈ g.2.1 then g.2.2 else st.1 w,
   fun w => if w ∈ g.2.1 then ∑ w' ∈ g.2.1, g.1 w w' • Φ (g.2.2 - st.1 w') (st.2 w')
     else st.2 w)

/-- The physical network: start with labels `Min`, run all gates, finish with `Φ (Mout - label)`. -/
def netRun (Φ : M → (Ω → R) →ₗ[R] (Ω → R)) (gates : List (NGate Wr R M)) (Min Mout : Wr → M)
    (f : Wr → Ω → R) : Wr → Ω → R :=
  fun w => Φ (Mout w - (gates.foldl (physStep Φ) (Min, f)).1 w)
    ((gates.foldl (physStep Φ) (Min, f)).2 w)

/-- Logical (frame-free) gate on streams. -/
def logStep (h : Wr → Ω → R) (g : NGate Wr R M) : Wr → Ω → R :=
  fun w => ∑ w', g.1 w w' • h w'

/-- The scalar circuit. -/
def scalarRun (gates : List (NGate Wr R M)) (v : Wr → R) : Wr → R :=
  gates.foldl (fun v g => g.1 *ᵥ v) v

lemma physStep_inv (Φ : M → (Ω → R) →ₗ[R] (Ω → R))
    (hadd : ∀ a b x, Φ (a + b) x = Φ a (Φ b x))
    (st : (Wr → M) × (Wr → Ω → R)) (h : Wr → Ω → R) (hst : ∀ w, st.2 w = Φ (st.1 w) (h w))
    (g : NGate Wr R M) (hg : g.OK) :
    ∀ w, (physStep Φ st g).2 w = Φ ((physStep Φ st g).1 w) (logStep h g w) := by
  intro w
  simp only [physStep, logStep]
  by_cases hw : w ∈ g.2.1
  · simp only [hw, if_true, map_sum, map_smul]
    rw [← Finset.sum_subset (Finset.subset_univ g.2.1)]
    · refine Finset.sum_congr rfl fun w' _ => ?_
      rw [hst w', ← hadd, sub_add_cancel]
    · intro w' _ hw'
      simp [(hg w w').2 hw hw']
  · simp only [hw, if_false]
    rw [Finset.sum_eq_single w, hst w]
    · simp [(hg w w).1 hw]
    · intro b _ hb; simp [(hg w b).1 hw, Ne.symm hb]
    · simp

lemma physRun_inv (Φ : M → (Ω → R) →ₗ[R] (Ω → R))
    (hadd : ∀ a b x, Φ (a + b) x = Φ a (Φ b x))
    (gates : List (NGate Wr R M)) (hg : ∀ g ∈ gates, g.OK) :
    ∀ (st : (Wr → M) × (Wr → Ω → R)) (h : Wr → Ω → R), (∀ w, st.2 w = Φ (st.1 w) (h w)) →
      ∀ w, (gates.foldl (physStep Φ) st).2 w =
        Φ ((gates.foldl (physStep Φ) st).1 w) (gates.foldl logStep h w) := by
  induction gates with
  | nil => intro st h hst; simpa using hst
  | cons g gs ih =>
    intro st h hst
    simp only [List.foldl_cons]
    exact ih (fun g' hg' => hg g' (List.mem_cons_of_mem _ hg')) _ _
      (physStep_inv Φ hadd st h hst g (hg g List.mem_cons_self))

omit [DecidableEq Wr] [AddGroup M] in
lemma logRun_apply (gates : List (NGate Wr R M)) :
    ∀ (h : Wr → Ω → R) (w : Wr) (ω : Ω),
      gates.foldl logStep h w ω = scalarRun gates (fun w => h w ω) w := by
  induction gates with
  | nil => intro h w ω; rfl
  | cons g gs ih =>
    intro h w ω
    simp only [List.foldl_cons, scalarRun] at ih ⊢
    rw [ih]
    have : (fun w => logStep h g w ω) = g.1 *ᵥ fun w => h w ω := by
      funext w'
      simp [logStep, Matrix.mulVec, dotProduct, Finset.sum_apply]
    rw [this]

end Network

/-! ## 3. The full procedure: split, network with pull frames, merge with `ρ` undone -/

section Combine

/-- The address bijection candidate on `Fin (vol sh)` induced by a valid-preserving address map. -/
def addrPerm (sh : Shape) (G : List ℕ → List ℕ) (hG : ∀ a, Valid sh a → Valid sh (G a)) :
    Fin (vol sh) → Fin (vol sh) :=
  fun j => ⟨encode sh (G (decode sh j)), encode_lt (hG _ (valid_decode j.isLt))⟩

/-- The stream frame action: `Φ_m` = pull by `π m` on the `rest` digits, as a linear map on
streams `Fin (vol (P :: L :: rest)) → R`. -/
def streamΦ {R M : Type*} [CommRing R] (P L : ℕ) (rest : Shape) (π : M → List ℕ → List ℕ)
    (hπv : ∀ m x, Valid rest x → Valid rest (π m x)) (m : M) :
    (Fin (vol (P :: L :: rest)) → R) →ₗ[R] (Fin (vol (P :: L :: rest)) → R) :=
  LinearMap.funLeft R R (addrPerm (P :: L :: rest) (rowLift (π m))
    (fun _ ha => valid_rowLift (hπv m) ha))

end Combine

end AModel

end

section
-- Source module: Solutions.AModelProp11
/-!
# Proposition 11 (power-width interchange with rows) in the array model

1. `Layout.cons2`: a layout on `rest` lifted to `P :: L :: rest`; `amap_cons2`.
2. The concrete frame action `shearπ` (shear address maps on `rest`), its hypotheses, and
   `shear_network`: the row-split network realizes `H ← H + D` on every row.
3. `netRunF`: the network with implemented frame changes; `netRunF_congr` (agreement on the frame
   changes that actually occur, `FramesOK`, suffices).
4. `runC`: Lemma 10 steps with every field interchange delegated to a child.
5. Chunk shapes `hdShape` (chunks split into `m` fields) / `swShape` (chunks merged).
6. `amap_swapMat_eq_blockSwap`.
7. One level (`levelR`) with an oracle child: `levelR_correct_amap`, `levelR_correct`.
8. The recursive program `prog` and `prog_correct` (induction on the level).
9. Cost: `levelCost_eq` (`T_{k+1}(V) = C₀ V + s T_k(V/W)`), `prog_cost`.
10. `prop11`: denotation (block swap of the two `m^k`-digit chunks) and cost `O(V (m^k)^τ)`.
-/

namespace AModel

open Matrix

/-! ## 1. Lifting a layout past the prefix and row fields -/

section Cons2

variable {sh : Shape} {n Q : ℕ}

/-- A layout on `sh` viewed on `P :: L :: sh` (positions shifted by two). -/
def Layout.cons2 (Λ : Layout sh n Q) (P L : ℕ) : Layout (P :: L :: sh) n Q where
  pos i := ⟨Λ.pos i + 2, by simp⟩
  mono a b h := by
    have := Λ.mono h
    rw [Fin.lt_def] at this ⊢
    simp only
    omega
  size i := by simpa using Λ.size i

variable (Λ : Layout sh n Q)

@[simp] lemma Layout.cons2_pos (P L : ℕ) (i : Fin n) :
    ((Λ.cons2 P L).pos i : ℕ) = Λ.pos i + 2 := rfl

end Cons2

/-! ## 2. The concrete frame action: shears on the `rest` digits -/

section ShearAction

variable {rest : Shape} {m Q : ℕ} {M : Type*} [AddCommGroup M]

/-- The frame action of a label `a : M` (through an additive map `φ` to `m × m` matrices over
`ZMod Q`): the address map of the shear by `-φ a` on the `rest` digits. Pull by it is the array
permutation of the address map `(H, D) ↦ (H + φ a · D, D)`. -/
def shearπ (Λ : Layout rest (m + m) Q) (φ : M →+ Matrix (Fin m) (Fin m) (ZMod Q)) (a : M) :
    List ℕ → List ℕ :=
  amap Λ (shearMat (-(φ a)))

variable (Λ : Layout rest (m + m) Q) (φ : M →+ Matrix (Fin m) (Fin m) (ZMod Q))

end ShearAction

/-! ## 3. Networks with implemented frame changes -/

section Frames

variable {Wr Ω R M : Type*} [CommRing R] [Fintype Wr] [DecidableEq Wr] [AddGroup M]

/-- `physStep` with a plain function family (an implementation of the frame changes). -/
def physStepF (Φ : M → (Ω → R) → (Ω → R)) (st : (Wr → M) × (Wr → Ω → R))
    (g : NGate Wr R M) : (Wr → M) × (Wr → Ω → R) :=
  (fun w => if w ∈ g.2.1 then g.2.2 else st.1 w,
   fun w => if w ∈ g.2.1 then ∑ w' ∈ g.2.1, g.1 w w' • Φ (g.2.2 - st.1 w') (st.2 w')
     else st.2 w)

/-- `netRun` with a plain function family. -/
def netRunF (Φ : M → (Ω → R) → (Ω → R)) (gates : List (NGate Wr R M)) (Min Mout : Wr → M)
    (f : Wr → Ω → R) : Wr → Ω → R :=
  fun w => Φ (Mout w - (gates.foldl (physStepF Φ) (Min, f)).1 w)
    ((gates.foldl (physStepF Φ) (Min, f)).2 w)

/-- Labels after a gate. -/
def labStep (lab : Wr → M) (g : NGate Wr R M) : Wr → M :=
  fun w => if w ∈ g.2.1 then g.2.2 else lab w

/-- Every frame change of the run (each touched wire of each gate, and the final frames)
satisfies `Pr`. -/
def FramesOK (Pr : M → Prop) : List (NGate Wr R M) → (Wr → M) → (Wr → M) → Prop
  | [], lab, Mout => ∀ w, Pr (Mout w - lab w)
  | g :: gs, lab, Mout => (∀ w ∈ g.2.1, Pr (g.2.2 - lab w)) ∧ FramesOK Pr gs (labStep lab g) Mout

end Frames

/-! ## 4. Running steps with the interchanges delegated to a child procedure -/

section RunChild

variable {sh : Shape} {n Q : ℕ} (Λ : Layout sh n Q) {α : Type*}

/-- One step, with field interchanges delegated to `child`. -/
def stepC (child : Fin n → Fin n → List α → List α) (d : α) (ys : List α) :
    Step n Q → List α
  | .swap a b => child a b ys
  | .shift i u c => (Step.shift i u c).arr Λ d ys

/-- Run steps, delegating every field interchange to `child`. -/
def runC (child : Fin n → Fin n → List α → List α) (d : α) (steps : List (Step n Q))
    (xs : List α) : List α :=
  steps.foldl (stepC Λ child d) xs

end RunChild

/-! ## 5. Chunk shapes: two chunks split into `m` fields, and merged -/

section Chunks

/-- `rest` with two chunks, each split into `m` fields of size `N`:
`A ++ [N]*m ++ B ++ [N]*m ++ C`. -/
def hdShape (A B C : Shape) (m N : ℕ) : Shape :=
  A ++ (List.replicate m N ++ (B ++ (List.replicate m N ++ C)))

/-- `rest` with two single fields of size `F`: `A ++ [F] ++ B ++ [F] ++ C`. -/
def swShape (A B C : Shape) (F : ℕ) : Shape := A ++ (F :: (B ++ (F :: C)))

/-- Position of active field `t` (`H_t` for `t < m`, `D_{t-m}` otherwise) in `hdShape`. -/
def hdPos (A B : Shape) (m t : ℕ) : ℕ := if t < m then A.length + t else A.length + B.length + t

lemma length_hdShape (A B C : Shape) (m N : ℕ) :
    (hdShape A B C m N).length = A.length + m + B.length + m + C.length := by
  simp [hdShape]; omega

lemma hdShape_getD (A B C : Shape) (m N t : ℕ) (ht : t < m + m) :
    (hdShape A B C m N).getD (hdPos A B m t) 0 = N := by
  unfold hdShape hdPos
  split_ifs with h
  · rw [List.getD_append_right _ _ _ _ (by omega), Nat.add_sub_cancel_left,
      List.getD_append _ _ _ _ (by simp; omega)]
    simp [h]
  · rw [List.getD_append_right _ _ _ _ (by omega),
      show A.length + B.length + t - A.length = m + (B.length + (t - m)) by omega,
      List.getD_append_right _ _ _ _ (by simp), List.length_replicate,
      Nat.add_sub_cancel_left, List.getD_append_right _ _ _ _ (by omega),
      Nat.add_sub_cancel_left, List.getD_append _ _ _ _ (by simp; omega),
      List.getD_eq_getElem _ _ (by simp; omega), List.getElem_replicate]

lemma hdPos_lt (A B C : Shape) (m N t : ℕ) (ht : t < m + m) :
    hdPos A B m t < (hdShape A B C m N).length := by
  rw [length_hdShape]; unfold hdPos; split_ifs <;> omega

/-- The layout of the `2m` active fields of `hdShape`. -/
def hdLayout (A B C : Shape) (m N : ℕ) : Layout (hdShape A B C m N) (m + m) N where
  pos t := ⟨hdPos A B m t, hdPos_lt A B C m N t t.2⟩
  mono a b h := by
    rw [Fin.lt_def] at h ⊢
    simp only [hdPos]
    split_ifs <;> omega
  size t := by
    have := hdShape_getD A B C m N t t.2
    rw [List.getD_eq_getElem _ _ (hdPos_lt A B C m N t t.2)] at this
    simpa using this

@[simp] lemma hdLayout_pos (A B C : Shape) (m N : ℕ) (t : Fin (m + m)) :
    ((hdLayout A B C m N).pos t : ℕ) = hdPos A B m t := rfl

end Chunks

/-! ## 6. The chunk interchange `amap swapMat` is a block swap -/

section SwapBlock

variable {sh : Shape} {m Q : ℕ} [NeZero Q] (Λ : Layout sh (m + m) Q)

end SwapBlock

/-! ## 7. One level of Prop. 11, with the children as an oracle -/

section Level

/-- Integer `m × m` matrices: the labels of the circuit. -/
abbrev IMat (m : ℕ) := Matrix (Fin m) (Fin m) ℤ

/-- Reduction of an integer matrix mod `N`. -/
def castM {m : ℕ} (N : ℕ) (X : IMat m) : Matrix (Fin m) (Fin m) (ZMod N) :=
  X.map (Int.castRingHom (ZMod N))

/-- Reduction mod `N` as an additive map (the `φ` of `shear_network`). -/
def castHom (m N : ℕ) : IMat m →+ Matrix (Fin m) (Fin m) (ZMod N) :=
  (Int.castRingHom (ZMod N)).mapMatrix.toAddMonoidHom

variable {R : Type*} [CommRing R] {m W : ℕ}

/-- The data of the circuit: gates on `W` wires with integer matrix labels, input/output labels,
the routing `ρ`, and for every label difference `A` a factorization `A = E₁ Π E₂` (Lemma 10). -/
structure Circuit (R : Type*) [CommRing R] (m W : ℕ) where
  gates : List (NGate (Fin W) R (IMat m))
  Min : Fin W → IMat m
  Mout : Fin W → IMat m
  ρ : Fin W → Fin W
  fac : IMat m → IMat m × IMat m × IMat m

/-- `fac A = (E₁, Π, E₂)` is a Lemma 10 factorization of `A` usable mod `N`: `E₁, E₂` lower
triangular with diagonal units mod `N`, `Π` a `0/1` matrix, `A = E₁ Π E₂` over `ℤ`. -/
def FacAt (N : ℕ) (fac : IMat m → IMat m × IMat m × IMat m) (A : IMat m) : Prop :=
  (∀ i j, i < j → (fac A).1 i j = 0) ∧ (∀ i j, i < j → (fac A).2.2 i j = 0) ∧
  (∀ i, IsUnit (((fac A).1 i i : ℤ) : ZMod N)) ∧ (∀ i, IsUnit (((fac A).2.2 i i : ℤ) : ZMod N)) ∧
  (∀ i j, (fac A).2.1 i j = 0 ∨ (fac A).2.1 i j = 1) ∧ A = (fac A).1 * (fac A).2.1 * (fac A).2.2

/-- The Lemma 10 steps of the frame change `Φ_A` (fields of size `N`). -/
def edgeSteps (C : Circuit R m W) (N : ℕ) (A : IMat m) : List (Step (m + m) N) :=
  shearSteps (castM N (C.fac A).1) (castM N (C.fac A).2.1) (castM N (C.fac A).2.2)

/-- The circuit hypotheses of Prop. 11 at field size `N`. -/
structure Circuit.OK (C : Circuit R m W) (N : ℕ) : Prop where
  gatesOK : ∀ g ∈ C.gates, g.OK
  route : ∀ (v : Fin W → R) w, scalarRun C.gates v (C.ρ w) = v w
  unit : ∀ w, C.Mout (C.ρ w) - C.Min w = 1
  frames : FramesOK (FacAt N C.fac) C.gates C.Min C.Mout

/-- The implemented frame change `Φ_A` on a stream: the Lemma 10 steps of `A`, every field
interchange delegated to `child`. -/
def implΦ (C : Circuit R m W) (N P L' : ℕ) (A B Cs : Shape)
    (child : Fin (m + m) → Fin (m + m) → List R → List R) (X : IMat m)
    (f : Fin (vol (P :: L' :: hdShape A B Cs m N)) → R) :
    Fin (vol (P :: L' :: hdShape A B Cs m N)) → R :=
  fun i => (runC ((hdLayout A B Cs m N).cons2 P L') child 0 (AModel.edgeSteps C N X) (List.ofFn f)).getD
    (i : ℕ) 0

/-- **One level of the procedure** on `P :: (W * L') :: hdShape A B Cs m N` (two chunks of `m`
fields of size `N`): `D ← D − H`; row split into `W` streams; the network (gates pointwise,
frame changes `Φ_A` by `implΦ`); merge undoing `ρ`; `D ← H − D`. -/
def levelR [NeZero W] (C : Circuit R m W) (N P L' : ℕ) (A B Cs : Shape)
    (child : Fin (m + m) → Fin (m + m) → List R → List R) (xs : List R) : List R :=
  AModel.runArr ((hdLayout A B Cs m N).cons2 P (W * L')) 0 (flipSteps m)
    (rowMerge P W L' (hdShape A B Cs m N) 0 (fun w => List.ofFn
      (netRunF (implΦ C N P L' A B Cs child) C.gates C.Min C.Mout
        (fun w' i => (rowSplit P W L' (hdShape A B Cs m N) w' 0
          (AModel.runArr ((hdLayout A B Cs m N).cons2 P (W * L')) 0 (subSteps m) xs)).getD (i : ℕ) 0)
        (C.ρ w))))

variable [NeZero W] (C : Circuit R m W) {N : ℕ} [NeZero N] (P L' : ℕ) (A B Cs : Shape)

/-- The oracle hypothesis on the children: `child H_i D_j` interchanges the two size-`N` fields
`H_i`, `D_j` of a stream (single fields, i.e. a Prop. 11 instance one level down). -/
def ChildOK (child : Fin (m + m) → Fin (m + m) → List R → List R) : Prop :=
  ∀ (i j : Fin m) (ys : List R), ys.length = vol (P :: L' :: hdShape A B Cs m N) →
    child (Fin.castAdd m i) (Fin.natAdd m j) ys =
      pull (P :: L' :: hdShape A B Cs m N) (P :: L' :: hdShape A B Cs m N)
        (swapPos (A.length + i + 2) (A.length + B.length + m + j + 2)) 0 ys

end Level

/-! ## 8. The recursive procedure and Prop. 11 by induction on the level -/

section Recursion

variable {R : Type*} [CommRing R] {m W : ℕ} [NeZero W]

/-- **The interchange program.** `prog C q k P L A B Cs` interchanges the two fields of size
`q ^ m ^ k` (chunks of `m ^ k` radix-`q` digits) of `P :: L :: swShape A B Cs (q ^ m ^ k)`.
Level `0`: a single digit swap (primitive). Level `k + 1`: view each chunk as `m` fields of size
`N = q ^ m ^ k` (reshape, free) and run `levelR` with `L / W` stream rows, every field
interchange `H_a ↔ D_b` of every edge's Lemma 10 steps being a level-`k` call on the stream. -/
def prog (C : Circuit R m W) (q : ℕ) : ℕ → ℕ → ℕ → Shape → Shape → Shape → List R → List R
  | 0, P, L, A, B, Cs, xs =>
    pull (P :: L :: swShape A B Cs q) (P :: L :: swShape A B Cs q)
      (swapPos (A.length + 2) (A.length + B.length + 3)) 0 xs
  | k + 1, P, L, A, B, Cs, xs =>
    levelR C (q ^ m ^ k) P (L / W) A B Cs
      (fun a b => prog C q k P (L / W) (A ++ List.replicate a (q ^ m ^ k))
        (List.replicate (m - 1 - a) (q ^ m ^ k) ++ (B ++ List.replicate (b - m) (q ^ m ^ k)))
        (List.replicate (m + m - 1 - b) (q ^ m ^ k) ++ Cs)) xs

end Recursion

/-! ## 9. Cost -/

section CostDefs

variable {Wr R M : Type*} [Fintype Wr] [DecidableEq Wr] [AddGroup M]

/-- Sum of `f` over all frame changes of the run (as in `FramesOK`). -/
def frameSum {β : Type*} [AddCommMonoid β] (f : M → β) :
    List (NGate Wr R M) → (Wr → M) → (Wr → M) → β
  | [], lab, Mout => ∑ w, f (Mout w - lab w)
  | g :: gs, lab, Mout => (∑ w ∈ g.2.1, f (g.2.2 - lab w)) + frameSum f gs (labStep lab g) Mout

/-- Total number of (gate, touched wire) pairs: the pointwise gate passes on streams. -/
def touched (gates : List (NGate Wr R M)) : ℕ := (gates.map fun g => g.2.1.card).sum

/-- Cost of the network on streams of volume `v`: each frame change costs `fc` of it, each
touched wire of each gate one pass `v` for the pointwise gate. -/
noncomputable def netCost (fc : M → ℝ) (v : ℝ) : List (NGate Wr R M) → (Wr → M) → (Wr → M) → ℝ
  | [], lab, Mout => ∑ w, fc (Mout w - lab w)
  | g :: gs, lab, Mout => (∑ w ∈ g.2.1, (fc (g.2.2 - lab w) + v)) + netCost fc v gs (labStep lab g) Mout

end CostDefs

section StepCost

variable {n Q : ℕ}

/-- Cost of running steps on an array of volume `v`: a shift is one pass (`v`), a field
interchange is a child call (`T v`). -/
noncomputable def runCost (T : ℝ → ℝ) (v : ℝ) (steps : List (Step n Q)) : ℝ :=
  (steps.map fun s => if s.isSwap then T v else v).sum

variable {m : ℕ}

end StepCost

section ProgCost

variable {R : Type*} [CommRing R] {m W : ℕ}

/-- `r_A`: the number of ones of the `Π` of the factorization of `A` (its rank). -/
def pivRank (C : Circuit R m W) (A : IMat m) : ℕ :=
  (Finset.univ.filter fun p : Fin m × Fin m => (C.fac A).2.1 p.1 p.2 = 1).card

/-- `s = Σ_e r_e`, summed over all frame changes of the network: the number of child calls. -/
def rankSum (C : Circuit R m W) : ℕ := frameSum (pivRank C) C.gates C.Min C.Mout

/-- The constant `C₀` of the linear part: `2m + 2` full passes (`D ← D − H`, split, merge,
`D ← H − D`) plus, per stream (volume `V / W`), the shift passes `4m + 2 r_e` of each frame
change and one pass per touched wire of each gate. -/
noncomputable def costConst (C : Circuit R m W) : ℝ :=
  2 * m + 2 + (frameSum (fun A => (4 * m + 2 * pivRank C A : ℝ)) C.gates C.Min C.Mout +
    touched C.gates) / W

/-- Cost of one level with child cost `T`, on input volume `V` (per the structure of `levelR`). -/
noncomputable def levelCost (C : Circuit R m W) (N : ℕ) (T : ℝ → ℝ) (V : ℝ) : ℝ :=
  ((subSteps (Q := N) m).length : ℝ) * V + V +
    netCost (fun X => runCost T (V / W) (AModel.edgeSteps C N X)) (V / W) C.gates C.Min C.Mout + V +
    ((flipSteps (Q := N) m).length : ℝ) * V

/-- Cost of `prog` at level `k` on volume `V` (level `0`: one digit-swap pass). -/
noncomputable def progCost (C : Circuit R m W) (q : ℕ) : ℕ → ℝ → ℝ
  | 0, V => V
  | k + 1, V => levelCost C (q ^ m ^ k) (progCost C q k) V

end ProgCost

/-! ## 10. Proposition 11, assembled -/

section Assembled

variable {R : Type*} [CommRing R] {m W : ℕ} [NeZero W]

end Assembled

end AModel

end
Source
OpenAI, Integer multiplication below n log n (preprint, 23 Sep 2026), https://github.com/openai/math/tree/adc7f1241b42e322a6451854ab7e4b4c146bf78a/preprints/Integer-multiplication-below-n-log-n-September-23-2026. Lean development of the integer-multiplication-kappa campaign.

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