The core module lean/FrcCore/Theme/Packed.lean — the substrate from first principles, no Mathlib, no axioms; one of the core's 137 modules (the index); it rests on 1 module: Nat: 60 theorems (28 definitions): 60 with no axioms, 0 at tier 1 (propext, Quot.sound), 0 at tier 2 (Classical.choice), 0 red; the mark's colour is the library's (green, the core; red a proof the gate rejects), each declaration's axioms on hover; the small plain glyph marks a definition (no proof, no axioms; on the ledger pages the plain glyph is instead a module cited as a whole). Each declaration is anchored; the ledger predicates cite them by name. ▶ open in Lean Live · axioms.log.
1import FrcCore.Nat23/-!4# The packed arithmetic of the kernel-decided witnesses — rows as one natural number, no axioms56The first file of the operator theme (6 November 2026). A kernel-decided witness on a cycle of `p` sites or on an7`n × n` matrix over the coefficient field costs the kernel one reduction step per field operation, and the kernel keeps8every intermediate term: the powers of the translation on the 173-cycle, evaluated on lists of field elements, exhausted9six gigabytes (`reports/core-proofs-20261105/execution.md`). Here a row of residues is packed into one natural number with10slots of `W = 48` bits, `pack [a₀, a₁, …] = a₀ + B a₁ + B² a₂ + …` with `B = 2^W`; the product of two packed rows is one11multiplication of natural numbers, which the kernel performs in one step on its big-number representation; the acyclic12convolution of the rows is read off the product's slots, and the cyclic one by adding the high half of the slots to the13low half, one addition more; the residues modulo `p` are taken on unpacking, slot by slot. A product that costs the kernel14`n²` field multiplications on lists costs it about `4n` operations on packed rows, and the memory follows.1516The file states the arithmetic on lists (`aconv`, the acyclic convolution; `cfold`, the cyclic fold; `combo`, a linear17combination of rows — the convolution of a cycle and the rows of a matrix product), the same arithmetic on packed numbers18(`packFold`, `unpack`), and proves them equal: `pack` is linear (`pack_smul`, `pack_addL`), turns the acyclic convolution19into the product (`pack_aconv`) and the cyclic fold into the fold of the number (`packFold_pack`), and `unpack` inverts20`pack` on bounded rows (`unpack_pack`) — the slot bound is the one hypothesis, and the bound of a convolution of residues21is computed (`aconv_bound`, `cfold_bound`). The division algorithm for `/` is proved from the definition of `Nat.div`, as22the base theme proves it for `%`, so that nothing here needs the standard library's division lemmas. No axioms.23-/2425namespace FRC.Dirac.Packed2627/-! ## The division algorithm for `/`, from the definition of `Nat.div` -/28section div2930theorem div_go_fuel {y : Nat} (hy : 0 < y) : ∀ (f1 f2 x : Nat) (h1 : x < f1) (h2 : x < f2),31 Nat.div.go y hy f1 x h1 = Nat.div.go y hy f2 x h2 := by32 intro f133 induction f1 with34 | zero => intro f2 x h1; exact absurd h1 (Nat.not_lt_zero x)35 | succ f1 ih =>36 intro f2 x h1 h237 cases f2 with38 | zero => exact absurd h2 (Nat.not_lt_zero x)39 | succ f2 =>40 show (if h : y ≤ x then Nat.div.go y hy f1 (x - y) _ + 1 else 0)41 = (if h : y ≤ x then Nat.div.go y hy f2 (x - y) _ + 1 else 0)42 exact match Nat.decLe y x with43 | .isTrue h => by rw [dite_eq_left h, dite_eq_left h, ih f2 (x - y) _ _]44 | .isFalse h => by rw [dite_eq_right h, dite_eq_right h]4546
theorem div_eq' (x y : Nat) (hy : 0 < y) : x / y = if y ≤ x then (x - y) / y + 1 else 0 := by47 show Nat.div x y = _48 unfold Nat.div49 rw [dite_eq_left hy]50 show (if h : y ≤ x then Nat.div.go y hy x (x - y) _ + 1 else 0) = _51 exact match Nat.decLe y x with52 | .isTrue h => by53 rw [dite_eq_left h, ite_eq_left h]54 show _ = Nat.div (x - y) y + 155 unfold Nat.div56 rw [dite_eq_left hy, div_go_fuel hy x (x - y + 1) (x - y) _ _]57 | .isFalse h => by rw [dite_eq_right h, ite_eq_right h]5859/-- The division algorithm: `x = y (x / y) + x % y`. -/60
theorem div_add_mod' (y : Nat) (hy : 0 < y) : ∀ x : Nat, x = y * (x / y) + x % y := by61 intro x62 induction x using Nat.strongRecOn with63 | _ x ih =>64 exact match Nat.decLe y x with65 | .isTrue h => by66 rw [div_eq' x y hy, ite_eq_left h, FRC.Nat.mod_eq_sub_mod hy h, Nat.mul_succ, Nat.add_assoc,67 Nat.add_comm y, ← Nat.add_assoc, ← ih (x - y) (Nat.sub_lt (Nat.lt_of_lt_of_le hy h) hy),68 FRC.Nat.sub_add_cancel h]69 | .isFalse h => by70 rw [div_eq' x y hy, ite_eq_right h, FRC.Nat.mod_eq_of_lt (Nat.lt_of_not_le h), Nat.mul_zero, Nat.zero_add]7172/-- The quotient is determined: `x = y q + r` with `r < y` gives `x / y = q`. -/73
theorem div_unique {y x q r : Nat} (hr : r < y) (h : x = y * q + r) : x / y = q :=74 FRC.Nat.quot_unique (Nat.mod_lt x (FRC.Nat.pos_of_lt hr)) hr (by rw [← div_add_mod' y (FRC.Nat.pos_of_lt hr) x, ← h])7576
theorem div_eq_of_lt {x y : Nat} (h : x < y) : x / y = 0 :=77 div_unique h (by rw [Nat.mul_zero, Nat.zero_add])7879/-- `(r + y q) / y = q` and `(r + y q) % y = r` for `r < y`. -/80
theorem add_mul_div {y q r : Nat} (hr : r < y) : (r + y * q) / y = q :=81 div_unique hr (Nat.add_comm _ _)8283
theorem add_mul_mod {y q r : Nat} (hr : r < y) : (r + y * q) % y = r :=84 FRC.Nat.mod_unique hr (Nat.add_comm _ _)8586end div8788/-! ## The slots -/8990/-- The slot width, in bits, and the base `B = 2^W`: a slot holds every residue sum the witnesses form (the bound91`cfold_bound`: `2 n p³ < B` on every standard shell, `2 · 1549⁴ < 2⁴⁸`). -/92
def W : Nat := 4893
def B : Nat := 2 ^ W9495
theorem B_pos : 0 < B := Nat.pow_pos (by decide)9697/-- A row packed into one natural number: `pack [a₀, a₁, …] = a₀ + B (a₁ + B (…))`. -/98
def pack : List Nat → Nat99 | [] => 0100 | x :: l => x + B * pack l101102/-- The `k` slots of a natural number, each reduced modulo `p`. -/103
def unpack (p : Nat) : Nat → Nat → List Nat104 | 0, _ => []105 | k + 1, P => P % B % p :: unpack p k (P / B)106107/-- The entries of a row are below `B`. -/108
def Bounded : List Nat → Prop109 | [] => True110 | x :: l => x < B ∧ Bounded l111112/-- `pack` inverts `unpack` on bounded rows, up to the residues: the slots are the entries modulo `p`. -/113
theorem unpack_pack (p : Nat) : ∀ l : List Nat, Bounded l → unpack p l.length (pack l) = l.map (· % p)114 | [], _ => rfl115 | x :: l, ⟨hx, hl⟩ => by116 show (x + B * pack l) % B % p :: unpack p l.length ((x + B * pack l) / B) = x % p :: l.map (· % p)117 rw [add_mul_mod hx, add_mul_div hx, unpack_pack p l hl]118119
theorem pack_lt : ∀ l : List Nat, Bounded l → pack l < B ^ l.length120 | [], _ => Nat.lt_succ_self 0121 | x :: l, ⟨hx, hl⟩ => by122 show x + B * pack l < B ^ (l.length + 1)123 have h1 : pack l + 1 ≤ B ^ l.length := pack_lt l hl124 calc x + B * pack l < B + B * pack l := Nat.add_lt_add_right hx _125 _ = B * (pack l + 1) := by rw [Nat.mul_succ, Nat.add_comm]126 _ ≤ B * B ^ l.length := Nat.mul_le_mul_left B h1127 _ = B ^ (l.length + 1) := by rw [FRC.Nat.pow_succ', Nat.mul_comm]128129/-! ## Linearity: scalar multiples, sums and the acyclic convolution -/130131/-- `c · l`, entrywise. -/132
def smul (c : Nat) (l : List Nat) : List Nat := l.map (c * ·)133134/-- `u + v`, entrywise, the shorter row padded with zeros. -/135
def addL : List Nat → List Nat → List Nat136 | [], v => v137 | u, [] => u138 | x :: u, y :: v => (x + y) :: addL u v139140/-- The acyclic convolution (the product of the rows as polynomials): `(x :: a) * b = x b + (0 :: a * b)`. -/141
def aconv : List Nat → List Nat → List Nat142 | [], _ => []143 | x :: a, b => addL (smul x b) (0 :: aconv a b)144145
theorem pack_smul (c : Nat) : ∀ l : List Nat, pack (smul c l) = c * pack l146 | [] => by show 0 = c * 0; rw [Nat.mul_zero]147 | x :: l => by148 show c * x + B * pack (smul c l) = c * (x + B * pack l)149 rw [pack_smul c l, Nat.mul_add, FRC.Nat.mul_left_comm]150151
theorem pack_addL : ∀ u v : List Nat, pack (addL u v) = pack u + pack v152 | [], v => by show pack v = 0 + pack v; rw [Nat.zero_add]153 | x :: u, [] => by show pack (x :: u) = pack (x :: u) + 0; rw [Nat.add_zero]154 | x :: u, y :: v => by155 show x + y + B * pack (addL u v) = x + B * pack u + (y + B * pack v)156 rw [pack_addL u v, Nat.mul_add, Nat.add_assoc, Nat.add_assoc, Nat.add_left_comm y]157158/-- The product of the packed rows is the packed acyclic convolution. -/159
theorem pack_aconv : ∀ a b : List Nat, pack (aconv a b) = pack a * pack b160 | [], b => by show 0 = 0 * pack b; rw [Nat.zero_mul]161 | x :: a, b => by162 show pack (addL (smul x b) (0 :: aconv a b)) = (x + B * pack a) * pack b163 rw [pack_addL, pack_smul]164 show x * pack b + (0 + B * pack (aconv a b)) = _165 rw [Nat.zero_add, pack_aconv a b, Nat.add_mul, Nat.mul_assoc]166167/-! ## The cyclic fold -/168169/-- The cyclic fold of a row of `2n − 1` (or `2n`) entries to `n`: the high half added to the low half, the170convolution on the cycle of `n` sites from the acyclic one. -/171
def cfold (n : Nat) (c : List Nat) : List Nat := addL (c.take n) (c.drop n)172173/-- The same on a packed row: the high slots are `P / Bⁿ`, the low ones `P % Bⁿ`. -/174
def packFold (n P : Nat) : Nat := P % B ^ n + P / B ^ n175176
theorem pack_take_drop : ∀ (n : Nat) (c : List Nat), pack c = pack (c.take n) + B ^ n * pack (c.drop n)177 | 0, c => by show pack c = 0 + 1 * pack c; rw [Nat.zero_add, Nat.one_mul]178 | n + 1, [] => by show 0 = 0 + B ^ (n + 1) * 0; rw [Nat.mul_zero]179 | n + 1, x :: c => by180 show x + B * pack c = x + B * pack (c.take n) + B ^ (n + 1) * pack (c.drop n)181 rw [pack_take_drop n c, Nat.mul_add, Nat.add_assoc, FRC.Nat.pow_succ', Nat.mul_comm (B ^ n) B, Nat.mul_assoc]182183
theorem bounded_take : ∀ (n : Nat) (c : List Nat), Bounded c → Bounded (c.take n)184 | 0, _, _ => trivial185 | _ + 1, [], _ => trivial186 | n + 1, _ :: c, ⟨hx, hc⟩ => ⟨hx, bounded_take n c hc⟩187188
theorem length_take_le : ∀ (n : Nat) (c : List Nat), (c.take n).length ≤ n189 | 0, _ => Nat.le_refl 0190 | _ + 1, [] => Nat.zero_le _191 | n + 1, _ :: c => Nat.succ_le_succ (length_take_le n c)192193
theorem pow_le_pow_of_le {m n : Nat} (h : m ≤ n) : B ^ m ≤ B ^ n := by194 obtain ⟨k, rfl⟩ : ∃ k, n = m + k := ⟨n - m, by rw [Nat.add_comm, FRC.Nat.sub_add_cancel h]⟩195 rw [FRC.Nat.pow_add]196 exact Nat.le_mul_of_pos_right _ (Nat.pow_pos B_pos)197198/-- The fold of the packed row is the packed fold, for a bounded row. -/199
theorem packFold_pack (n : Nat) (c : List Nat) (hc : Bounded c) : packFold n (pack c) = pack (cfold n c) := by200 have hlt : pack (c.take n) < B ^ n :=201 Nat.lt_of_lt_of_le (pack_lt _ (bounded_take n c hc)) (pow_le_pow_of_le (length_take_le n c))202 show pack c % B ^ n + pack c / B ^ n = pack (addL (c.take n) (c.drop n))203 rw [pack_addL, pack_take_drop n c, add_mul_mod hlt, add_mul_div hlt]204205/-! ## The combination of rows (the rows of a matrix product) -/206207/-- `Σ_k c_k · r_k`: a row of the product of a matrix with the rows `r` is the combination of the rows by the entries. -/208
def combo : List Nat → List (List Nat) → List Nat209 | [], _ => []210 | _, [] => []211 | c :: cs, r :: rs => addL (smul c r) (combo cs rs)212213/-- The same on packed rows. -/214
def packCombo : List Nat → List Nat → Nat215 | [], _ => 0216 | _, [] => 0217 | c :: cs, P :: Ps => c * P + packCombo cs Ps218219
theorem pack_combo : ∀ (cs : List Nat) (rs : List (List Nat)), pack (combo cs rs) = packCombo cs (rs.map pack)220 | [], _ => rfl221 | _ :: _, [] => rfl222 | c :: cs, r :: rs => by223 show pack (addL (smul c r) (combo cs rs)) = c * pack r + packCombo cs (rs.map pack)224 rw [pack_addL, pack_smul, pack_combo cs rs]225226/-! ## The bounds -/227228/-- The entries of a row are below `m`. -/229
def Below (m : Nat) : List Nat → Prop230 | [] => True231 | x :: l => x < m ∧ Below m l232233
theorem below_mono {m m' : Nat} (h : m ≤ m') : ∀ l, Below m l → Below m' l234 | [], _ => trivial235 | _ :: l, ⟨hx, hl⟩ => ⟨Nat.lt_of_lt_of_le hx h, below_mono h l hl⟩236237/-- The entries of `u + v` are below `m + m'` when those of `u` are below `m` and those of `v` below `m'`238(`0 < m`, `0 < m'` for the padded tails). -/239
theorem below_addL {m m' : Nat} (hm : 0 < m) (hm' : 0 < m') :240 ∀ u v, Below m u → Below m' v → Below (m + m') (addL u v)241 | [], v, _, hv => below_mono (Nat.le_add_left m' m) v hv242 | _ :: _, [], hu, _ => below_mono (Nat.le_add_right m m') _ hu243 | _ :: u, _ :: v, ⟨hx, hu⟩, ⟨hy, hv⟩ => ⟨Nat.add_lt_add hx hy, below_addL hm hm' u v hu hv⟩244245
theorem below_smul {c m m' : Nat} (hc : c < m) : ∀ l, Below m' l → Below (m * m') (smul c l)246 | [], _ => trivial247 | _ :: l, ⟨hx, hl⟩ => ⟨Nat.mul_lt_mul_of_lt_of_lt hc hx, below_smul hc l hl⟩248249
theorem below_zero_cons {m : Nat} (hm : 0 < m) {l : List Nat} (h : Below m l) : Below m (0 :: l) := ⟨hm, h⟩250251/-- The acyclic convolution of two rows of residues below `p`, the first of `k` entries, has entries below `k p²`. -/252
theorem aconv_bound {p : Nat} (hp : 0 < p) : ∀ (a b : List Nat), Below p a → Below p b →253 Below (a.length * (p * p) + 1) (aconv a b)254 | [], _, _, _ => trivial255 | x :: a, b, ⟨hx, ha⟩, hb => by256 show Below ((a.length + 1) * (p * p) + 1) (addL (smul x b) (0 :: aconv a b))257 have h1 : Below (p * p) (smul x b) := below_smul hx b hb258 have h2 : Below (a.length * (p * p) + 1) (0 :: aconv a b) :=259 below_zero_cons (Nat.succ_pos _) (aconv_bound hp a b ha hb)260 have := below_addL (Nat.mul_pos hp hp) (Nat.succ_pos _) _ _ h1 h2261 rw [Nat.succ_mul, Nat.add_comm (a.length * (p * p)) (p * p), Nat.add_assoc]262 exact this263264
theorem below_take {m : Nat} : ∀ (n : Nat) (c : List Nat), Below m c → Below m (c.take n)265 | 0, _, _ => trivial266 | _ + 1, [], _ => trivial267 | n + 1, _ :: c, ⟨hx, hc⟩ => ⟨hx, below_take n c hc⟩268269
theorem below_drop {m : Nat} : ∀ (n : Nat) (c : List Nat), Below m c → Below m (c.drop n)270 | 0, _, h => h271 | _ + 1, [], _ => trivial272 | n + 1, _ :: c, ⟨_, hc⟩ => below_drop n c hc273274/-- The cyclic fold of a row with entries below `m` has entries below `2m`. -/275
theorem cfold_bound {m : Nat} (hm : 0 < m) (n : Nat) (c : List Nat) (h : Below m c) : Below (m + m) (cfold n c) :=276 below_addL hm hm _ _ (below_take n c h) (below_drop n c h)277278
theorem bounded_of_below {m : Nat} (hm : m ≤ B) : ∀ l, Below m l → Bounded l279 | [], _ => trivial280 | _ :: l, ⟨hx, hl⟩ => ⟨Nat.lt_of_lt_of_le hx hm, bounded_of_below hm l hl⟩281282/-! ## The cyclic convolution of residues: the list form, the packed form, and their equality -/283284/-- The cyclic convolution of two rows of residues on the cycle of `n` sites, reduced modulo `p`: the list form. -/285
def cycList (p n : Nat) (a b : List Nat) : List Nat := (cfold n (aconv a b)).map (· % p)286287/-- The packed form: one product, one fold, `n` slots. -/288
def cycPacked (p n : Nat) (A C : Nat) : List Nat := unpack p n (packFold n (A * C))289290
theorem length_addL_left : ∀ u v : List Nat, v.length ≤ u.length → (addL u v).length = u.length291 | [], [], _ => rfl292 | [], _ :: _, h => absurd h (Nat.not_succ_le_zero _)293 | _ :: _, [], _ => rfl294 | _ :: u, _ :: v, h => by295 show (addL u v).length + 1 = u.length + 1296 rw [length_addL_left u v (Nat.le_of_succ_le_succ h)]297298
theorem length_addL_right : ∀ u v : List Nat, u.length ≤ v.length → (addL u v).length = v.length299 | [], _, _ => rfl300 | _ :: _, [], h => absurd h (Nat.not_succ_le_zero _)301 | _ :: u, _ :: v, h => by302 show (addL u v).length + 1 = v.length + 1303 rw [length_addL_right u v (Nat.le_of_succ_le_succ h)]304305
theorem length_smul (c : Nat) : ∀ l : List Nat, (smul c l).length = l.length306 | [] => rfl307 | _ :: l => by show (smul c l).length + 1 = l.length + 1; rw [length_smul c l]308309/-- `|a * b| + 1 = |a| + |b|` for nonempty rows. -/310
theorem length_aconv : ∀ a b : List Nat, 0 < a.length → 0 < b.length → (aconv a b).length + 1 = a.length + b.length311 | [], _, h, _ => absurd h (Nat.lt_irrefl 0)312 | [x], b, _, hb => by313 show (addL (smul x b) (0 :: aconv [] b)).length + 1 = 1 + b.length314 rw [length_addL_left _ _ (by rw [length_smul]; exact hb), length_smul, Nat.add_comm]315 | x :: y :: a, b, _, hb => by316 show (addL (smul x b) (0 :: aconv (y :: a) b)).length + 1 = a.length + 1 + 1 + b.length317 have ih := length_aconv (y :: a) b (Nat.succ_pos _) hb318 have hle : (smul x b).length ≤ (0 :: aconv (y :: a) b).length := by319 show (smul x b).length ≤ (aconv (y :: a) b).length + 1320 rw [length_smul, ih]; exact Nat.le_add_left _ _321 rw [length_addL_right _ _ hle]322 show (aconv (y :: a) b).length + 1 + 1 = a.length + 1 + 1 + b.length323 rw [ih]; show a.length + 1 + b.length + 1 = _; rw [Nat.add_right_comm]324325
theorem length_take_of_le : ∀ (n : Nat) (c : List Nat), n ≤ c.length → (c.take n).length = n326 | 0, _, _ => rfl327 | _ + 1, [], h => absurd h (Nat.not_succ_le_zero _)328 | n + 1, _ :: c, h => by show (c.take n).length + 1 = n + 1; rw [length_take_of_le n c (Nat.le_of_succ_le_succ h)]329330
theorem length_drop : ∀ (n : Nat) (c : List Nat), (c.drop n).length = c.length - n331 | 0, _ => rfl332 | n + 1, [] => by show 0 = 0 - (n + 1); rw [Nat.zero_sub]333 | n + 1, _ :: c => by show (c.drop n).length = c.length + 1 - (n + 1); rw [length_drop n c, Nat.succ_sub_succ]334335/-- The fold of a row of `2n − 1` entries has `n` entries. -/336
theorem length_cfold (n : Nat) (c : List Nat) (hc : c.length + 1 = n + n) : (cfold n c).length = n := by337 have hn : n ≤ c.length := by338 have : n + 1 ≤ c.length + 1 := by339 rw [hc]340 cases n with341 | zero => exact absurd hc (by rw [Nat.add_zero]; exact Nat.succ_ne_zero _)342 | succ n => exact Nat.succ_le_succ (Nat.le_add_right (n + 1) n)343 exact Nat.le_of_succ_le_succ this344 have hd : (c.drop n).length ≤ (c.take n).length := by345 rw [length_drop, length_take_of_le n c hn]346 have e : c.length = n + (n - 1) := by347 cases n with348 | zero => exact absurd hc (by rw [Nat.add_zero]; exact Nat.succ_ne_zero _)349 | succ n =>350 have : c.length + 1 = n + 1 + (n + 1 - 1) + 1 := by rw [hc, Nat.add_sub_cancel]; rfl351 exact Nat.succ.inj this352 rw [e, FRC.Nat.add_sub_cancel_left]353 exact Nat.sub_le n 1354 show (addL (c.take n) (c.drop n)).length = n355 rw [length_addL_left _ _ hd, length_take_of_le n c hn]356357/-- The packed cyclic convolution of rows of residues below `p`, each of `n` entries, is the list form, when358`2 n p² ≤ B`: the one bound of the method. -/359
theorem cycPacked_eq (p n : Nat) (hp : 0 < p) (hn : 0 < n) (a b : List Nat) (ha : Below p a) (hb : Below p b)360 (la : a.length = n) (lb : b.length = n) (hB : 2 * (n * (p * p) + 1) ≤ B) :361 cycPacked p n (pack a) (pack b) = cycList p n a b := by362 have hc : Below (n * (p * p) + 1) (aconv a b) := la ▸ aconv_bound hp a b ha hb363 have hf : Below (n * (p * p) + 1 + (n * (p * p) + 1)) (cfold n (aconv a b)) :=364 cfold_bound (Nat.succ_pos _) n _ hc365 have hB' : n * (p * p) + 1 + (n * (p * p) + 1) ≤ B := by rw [← Nat.two_mul]; exact hB366 have hbnd : Bounded (cfold n (aconv a b)) := bounded_of_below hB' _ hf367 have hbndc : Bounded (aconv a b) :=368 bounded_of_below (Nat.le_trans (Nat.le_add_right _ _) hB') _ hc369 have hlen : (cfold n (aconv a b)).length = n :=370 length_cfold n _ (by rw [length_aconv a b (la ▸ hn) (lb ▸ hn), la, lb])371 show unpack p n (packFold n (pack a * pack b)) = (cfold n (aconv a b)).map (· % p)372 rw [← pack_aconv, packFold_pack n _ hbndc, ← unpack_pack p _ hbnd, hlen]373374/-! ## Rows over the quadratic extension: pairs of rows of residues -/375376/-- The cyclic convolution over `K = 𝔽_p(√ν)` on the cycle of `n` sites, the list form: with `a = a₁ + a₂ w` and377`b = b₁ + b₂ w` (rows of residues), `ab = (a₁b₁ + ν a₂b₂) + (a₁b₂ + a₂b₁) w`. -/378
def cycExtList (p ν n : Nat) (a b : List Nat × List Nat) : List Nat × List Nat :=379 ((cfold n (addL (aconv a.1 b.1) (smul ν (aconv a.2 b.2)))).map (· % p),380 (cfold n (addL (aconv a.1 b.2) (aconv a.2 b.1))).map (· % p))381382/-- The packed form: four products of natural numbers, two folds, `2n` slots. -/383
def cycExtPacked (p ν n : Nat) (A C : Nat × Nat) : List Nat × List Nat :=384 (unpack p n (packFold n (A.1 * C.1 + ν * (A.2 * C.2))), unpack p n (packFold n (A.1 * C.2 + A.2 * C.1)))385386/-- A pair of rows of residues below `p`, each of `n` entries. -/387
def Row (p n : Nat) (a : List Nat × List Nat) : Prop :=388 Below p a.1 ∧ Below p a.2 ∧ a.1.length = n ∧ a.2.length = n389390
theorem length_aconv' {n : Nat} (hn : 0 < n) {a b : List Nat} (la : a.length = n) (lb : b.length = n) :391 (aconv a b).length + 1 = n + n := by rw [length_aconv a b (la ▸ hn) (lb ▸ hn), la, lb]392393
theorem length_smul_aconv {n : Nat} (hn : 0 < n) (c : Nat) {a b : List Nat} (la : a.length = n) (lb : b.length = n) :394 (smul c (aconv a b)).length + 1 = n + n := by rw [length_smul, length_aconv' hn la lb]395396
theorem length_addL_eq {u v : List Nat} {k : Nat} (hu : u.length = k) (hv : v.length = k) : (addL u v).length = k := by397 rw [length_addL_left u v (by rw [hu, hv]; exact Nat.le_refl k), hu]398399
theorem length_addL_eq' {u v : List Nat} {m : Nat} (hu : u.length + 1 = m) (hv : v.length + 1 = m) :400 (addL u v).length + 1 = m := by401 have e : v.length = u.length := Nat.succ.inj (hv.trans hu.symm)402 rw [length_addL_left u v (e ▸ Nat.le_refl _), hu]403404/-- The bound of a term `aconv` or `ν · aconv`: below `p (n p² + 1)`. -/405
theorem below_aconv_term {p ν n : Nat} (hp : 0 < p) (hν : ν < p) {a b : List Nat} (ha : Below p a) (hb : Below p b)406 (la : a.length = n) : Below (p * (n * (p * p) + 1)) (smul ν (aconv a b)) :=407 below_smul hν _ (la ▸ aconv_bound hp a b ha hb)408409
theorem below_aconv_one {p n : Nat} (hp : 0 < p) {a b : List Nat} (ha : Below p a) (hb : Below p b)410 (la : a.length = n) : Below (p * (n * (p * p) + 1)) (aconv a b) :=411 below_mono (Nat.le_mul_of_pos_left _ hp) _ (la ▸ aconv_bound hp a b ha hb)412413/-- The packed cyclic convolution over `K` of two rows is the list form, when `4 p (n p² + 1) ≤ B`: the one bound of414the method on the extension. -/415
theorem cycExtPacked_eq (p ν n : Nat) (hp : 0 < p) (hν : ν < p) (hn : 0 < n) (a b : List Nat × List Nat)416 (ha : Row p n a) (hb : Row p n b) (hB : 4 * (p * (n * (p * p) + 1)) ≤ B) :417 cycExtPacked p ν n (pack a.1, pack a.2) (pack b.1, pack b.2) = cycExtList p ν n a b := by418 obtain ⟨ha1, ha2, la1, la2⟩ := ha419 obtain ⟨hb1, hb2, lb1, lb2⟩ := hb420 have hm : 0 < p * (n * (p * p) + 1) := Nat.mul_pos hp (Nat.succ_pos _)421 have h2 : p * (n * (p * p) + 1) + p * (n * (p * p) + 1) + (p * (n * (p * p) + 1) + p * (n * (p * p) + 1)) ≤ B := by422 have e : 4 * (p * (n * (p * p) + 1)) =423 p * (n * (p * p) + 1) + p * (n * (p * p) + 1) + (p * (n * (p * p) + 1) + p * (n * (p * p) + 1)) := by424 rw [show (4 : Nat) = 2 + 2 from rfl, Nat.add_mul, Nat.two_mul]425 rw [← e]; exact hB426 have hhalf : p * (n * (p * p) + 1) + p * (n * (p * p) + 1) ≤ B :=427 Nat.le_trans (Nat.le_add_right _ _) h2428 -- the real part429 have hre : Below (p * (n * (p * p) + 1) + p * (n * (p * p) + 1))430 (addL (aconv a.1 b.1) (smul ν (aconv a.2 b.2))) :=431 below_addL hm hm _ _ (below_aconv_one hp ha1 hb1 la1) (below_aconv_term hp hν ha2 hb2 la2)432 have lre : (addL (aconv a.1 b.1) (smul ν (aconv a.2 b.2))).length + 1 = n + n :=433 length_addL_eq' (length_aconv' hn la1 lb1) (length_smul_aconv hn ν la2 lb2)434 have him : Below (p * (n * (p * p) + 1) + p * (n * (p * p) + 1)) (addL (aconv a.1 b.2) (aconv a.2 b.1)) :=435 below_addL hm hm _ _ (below_aconv_one hp ha1 hb2 la1) (below_aconv_one hp ha2 hb1 la2)436 have lim : (addL (aconv a.1 b.2) (aconv a.2 b.1)).length + 1 = n + n :=437 length_addL_eq' (length_aconv' hn la1 lb2) (length_aconv' hn la2 lb1)438 show (unpack p n (packFold n (pack a.1 * pack b.1 + ν * (pack a.2 * pack b.2))),439 unpack p n (packFold n (pack a.1 * pack b.2 + pack a.2 * pack b.1))) =440 ((cfold n (addL (aconv a.1 b.1) (smul ν (aconv a.2 b.2)))).map (· % p),441 (cfold n (addL (aconv a.1 b.2) (aconv a.2 b.1))).map (· % p))442 rw [← pack_aconv, ← pack_aconv, ← pack_aconv, ← pack_aconv, ← pack_smul, ← pack_addL, ← pack_addL,443 packFold_pack n _ (bounded_of_below hhalf _ hre), packFold_pack n _ (bounded_of_below hhalf _ him),444 ← unpack_pack p _ (bounded_of_below h2 _ (cfold_bound (Nat.add_pos_left hm _) n _ hre)), length_cfold n _ lre,445 ← unpack_pack p _ (bounded_of_below h2 _ (cfold_bound (Nat.add_pos_left hm _) n _ him)), length_cfold n _ lim]446447/-! ## The rows kept packed: the slot-wise reduction by halving -/448449/-- The slot-wise reduction modulo `p` of a packed row of at most sixteen slots, written out: the kernel evaluates450these sixteen quotients and residues without unfolding a recursion (`leaf16_eq`: it is `pack (unpack p 16 P)`). -/451
def leaf16 (p P : Nat) : Nat :=452 let P1 := P / B453 let P2 := P1 / B454 let P3 := P2 / B455 let P4 := P3 / B456 let P5 := P4 / B457 let P6 := P5 / B458 let P7 := P6 / B459 let P8 := P7 / B460 let P9 := P8 / B461 let P10 := P9 / B462 let P11 := P10 / B463 let P12 := P11 / B464 let P13 := P12 / B465 let P14 := P13 / B466 let P15 := P14 / B467 P % B % p + B * (P1 % B % p + B * (P2 % B % p + B * (P3 % B % p + B * (P4 % B % p + B * (P5 % B % p + B * (P6 % B % p + B * (P7 % B % p + B * (P8 % B % p + B * (P9 % B % p + B * (P10 % B % p + B * (P11 % B % p + B * (P12 % B % p + B * (P13 % B % p + B * (P14 % B % p + B * (P15 % B % p + B * 0)))))))))))))))468469
theorem leaf16_eq (p P : Nat) : leaf16 p P = pack (unpack p 16 P) := rfl470471/-- The slot-wise reduction modulo `p` of a packed row of at most `16 · 2^k` slots, by halving down to the leaves of472sixteen slots: the low and the high halves reduced and reassembled. A row stays packed between the products this473way; the slot-by-slot chain of `unpack` and `pack` would hold `n` growing numbers at a time, and the kernel, which474keeps every reduction step, would hold them all. -/475
def repack (p : Nat) : Nat → Nat → Nat476 | 0, P => leaf16 p P477 | k + 1, P => repack p k (P % B ^ (16 * 2 ^ k)) + B ^ (16 * 2 ^ k) * repack p k (P / B ^ (16 * 2 ^ k))478479
theorem bounded_drop : ∀ (n : Nat) (c : List Nat), Bounded c → Bounded (c.drop n)480 | 0, _, h => h481 | _ + 1, [], _ => trivial482 | n + 1, _ :: c, ⟨_, hc⟩ => bounded_drop n c hc483484
theorem map_take (f : Nat → Nat) : ∀ (n : Nat) (l : List Nat), (l.take n).map f = (l.map f).take n485 | 0, _ => rfl486 | _ + 1, [] => rfl487 | n + 1, x :: l => by show f x :: (l.take n).map f = f x :: (l.map f).take n; rw [map_take f n l]488489
theorem map_drop (f : Nat → Nat) : ∀ (n : Nat) (l : List Nat), (l.drop n).map f = (l.map f).drop n490 | 0, _ => rfl491 | _ + 1, [] => rfl492 | n + 1, _ :: l => map_drop f n l493494
theorem pack_unpack_zero (p : Nat) : ∀ k : Nat, pack (unpack p k 0) = 0495 | 0 => rfl496 | k + 1 => by497 show 0 % B % p + B * pack (unpack p k (0 / B)) = 0498 rw [Nat.zero_mod, Nat.zero_mod, div_eq_of_lt B_pos, pack_unpack_zero p k, Nat.mul_zero]499500/-- `unpack_pack` with spare slots: the slots past the row are zero and do not change the packed value. -/501
theorem pack_unpack_pack (p : Nat) : ∀ (l : List Nat) (k : Nat), Bounded l → l.length ≤ k →502 pack (unpack p k (pack l)) = pack (l.map (· % p))503 | [], k, _, _ => pack_unpack_zero p k504 | _ :: _, 0, _, h => absurd h (Nat.not_succ_le_zero _)505 | x :: l, k + 1, ⟨hx, hl⟩, h => by506 show (x + B * pack l) % B % p + B * pack (unpack p k ((x + B * pack l) / B)) = x % p + B * pack (l.map (· % p))507 rw [add_mul_mod hx, add_mul_div hx, pack_unpack_pack p l k hl (Nat.le_of_succ_le_succ h)]508509/-- `repack` is the packed slot-wise reduction: on a bounded row of at most `16 · 2^k` entries, the packed row of510the residues. -/511
theorem repack_pack (p : Nat) : ∀ (k : Nat) (l : List Nat), Bounded l → l.length ≤ 16 * 2 ^ k →512 repack p k (pack l) = pack (l.map (· % p))513 | 0, l, hl, hlen => by514 show leaf16 p (pack l) = _515 rw [leaf16_eq]; exact pack_unpack_pack p l 16 hl hlen516 | k + 1, l, hl, hlen => by517 have hm : pack (l.take (16 * 2 ^ k)) < B ^ (16 * 2 ^ k) :=518 Nat.lt_of_lt_of_le (pack_lt _ (bounded_take _ l hl)) (pow_le_pow_of_le (length_take_le _ l))519 have h2 : (l.drop (16 * 2 ^ k)).length ≤ 16 * 2 ^ k := by520 rw [length_drop]521 rw [Nat.pow_succ, ← Nat.mul_assoc, Nat.mul_two] at hlen522 exact Nat.le_trans (Nat.sub_le_sub_right hlen _) (by rw [FRC.Nat.add_sub_cancel]; exact Nat.le_refl _)523 show repack p k (pack l % B ^ (16 * 2 ^ k)) + B ^ (16 * 2 ^ k) * repack p k (pack l / B ^ (16 * 2 ^ k)) =524 pack (l.map (· % p))525 rw [pack_take_drop (16 * 2 ^ k) l, add_mul_mod hm, add_mul_div hm,526 repack_pack p k _ (bounded_take _ l hl) (length_take_le _ l), repack_pack p k _ (bounded_drop _ l hl) h2,527 pack_take_drop (16 * 2 ^ k) (l.map (· % p)), map_take, map_drop]528529/-- The product on the cycle of two packed real rows (rows of residues), the result reduced slot-wise on530`16 · 2^k ≥ n` slots: one product, one fold, one reduction. -/531
def rmul (p n k : Nat) (A C : Nat) : Nat := repack p k (packFold n (A * C))532533/-- `rmul` is the packed `cycList`, under the bound of `cycPacked_eq` and `n ≤ 16 · 2^k`. -/534
theorem rmul_eq (p n k : Nat) (hp : 0 < p) (hn : 0 < n) (a b : List Nat) (ha : Below p a) (hb : Below p b)535 (la : a.length = n) (lb : b.length = n) (hB : 2 * (n * (p * p) + 1) ≤ B) (hk : n ≤ 16 * 2 ^ k) :536 rmul p n k (pack a) (pack b) = pack (cycList p n a b) := by537 have hc : Below (n * (p * p) + 1) (aconv a b) := la ▸ aconv_bound hp a b ha hb538 have hf : Below (n * (p * p) + 1 + (n * (p * p) + 1)) (cfold n (aconv a b)) :=539 cfold_bound (Nat.succ_pos _) n _ hc540 have hB' : n * (p * p) + 1 + (n * (p * p) + 1) ≤ B := by rw [← Nat.two_mul]; exact hB541 have hbndc : Bounded (aconv a b) :=542 bounded_of_below (Nat.le_trans (Nat.le_add_right _ _) hB') _ hc543 have hlen : (cfold n (aconv a b)).length = n :=544 length_cfold n _ (by rw [length_aconv a b (la ▸ hn) (lb ▸ hn), la, lb])545 show repack p k (packFold n (pack a * pack b)) = pack ((cfold n (aconv a b)).map (· % p))546 rw [← pack_aconv, packFold_pack n _ hbndc]547 exact repack_pack p k _ (bounded_of_below hB' _ hf) (by rw [hlen]; exact hk)548549/-- The product on the cycle over `K` of two packed rows, the result reduced slot-wise on `16 · 2^k ≥ n` slots: four550products, two folds, two reductions, and the rows stay packed. -/551
def pmul (p ν n k : Nat) (A C : Nat × Nat) : Nat × Nat :=552 (repack p k (packFold n (A.1 * C.1 + ν * (A.2 * C.2))), repack p k (packFold n (A.1 * C.2 + A.2 * C.1)))553554/-- `pmul` is the packed `cycExtList`, under the bound of `cycExtPacked_eq` and `n ≤ 16 · 2^k`. -/555
theorem pmul_eq (p ν n k : Nat) (hp : 0 < p) (hν : ν < p) (hn : 0 < n) (a b : List Nat × List Nat)556 (ha : Row p n a) (hb : Row p n b) (hB : 4 * (p * (n * (p * p) + 1)) ≤ B) (hk : n ≤ 16 * 2 ^ k) :557 pmul p ν n k (pack a.1, pack a.2) (pack b.1, pack b.2) =558 (pack (cycExtList p ν n a b).1, pack (cycExtList p ν n a b).2) := by559 obtain ⟨ha1, ha2, la1, la2⟩ := ha560 obtain ⟨hb1, hb2, lb1, lb2⟩ := hb561 have hm : 0 < p * (n * (p * p) + 1) := Nat.mul_pos hp (Nat.succ_pos _)562 have h2 : p * (n * (p * p) + 1) + p * (n * (p * p) + 1) + (p * (n * (p * p) + 1) + p * (n * (p * p) + 1)) ≤ B := by563 have e : 4 * (p * (n * (p * p) + 1)) =564 p * (n * (p * p) + 1) + p * (n * (p * p) + 1) + (p * (n * (p * p) + 1) + p * (n * (p * p) + 1)) := by565 rw [show (4 : Nat) = 2 + 2 from rfl, Nat.add_mul, Nat.two_mul]566 rw [← e]; exact hB567 have hhalf : p * (n * (p * p) + 1) + p * (n * (p * p) + 1) ≤ B :=568 Nat.le_trans (Nat.le_add_right _ _) h2569 have hre : Below (p * (n * (p * p) + 1) + p * (n * (p * p) + 1))570 (addL (aconv a.1 b.1) (smul ν (aconv a.2 b.2))) :=571 below_addL hm hm _ _ (below_aconv_one hp ha1 hb1 la1) (below_aconv_term hp hν ha2 hb2 la2)572 have lre : (addL (aconv a.1 b.1) (smul ν (aconv a.2 b.2))).length + 1 = n + n :=573 length_addL_eq' (length_aconv' hn la1 lb1) (length_smul_aconv hn ν la2 lb2)574 have him : Below (p * (n * (p * p) + 1) + p * (n * (p * p) + 1)) (addL (aconv a.1 b.2) (aconv a.2 b.1)) :=575 below_addL hm hm _ _ (below_aconv_one hp ha1 hb2 la1) (below_aconv_one hp ha2 hb1 la2)576 have lim : (addL (aconv a.1 b.2) (aconv a.2 b.1)).length + 1 = n + n :=577 length_addL_eq' (length_aconv' hn la1 lb2) (length_aconv' hn la2 lb1)578 show (repack p k (packFold n (pack a.1 * pack b.1 + ν * (pack a.2 * pack b.2))),579 repack p k (packFold n (pack a.1 * pack b.2 + pack a.2 * pack b.1))) =580 (pack ((cfold n (addL (aconv a.1 b.1) (smul ν (aconv a.2 b.2)))).map (· % p)),581 pack ((cfold n (addL (aconv a.1 b.2) (aconv a.2 b.1))).map (· % p)))582 rw [← pack_aconv, ← pack_aconv, ← pack_aconv, ← pack_aconv, ← pack_smul, ← pack_addL, ← pack_addL,583 packFold_pack n _ (bounded_of_below hhalf _ hre), packFold_pack n _ (bounded_of_below hhalf _ him),584 repack_pack p k _ (bounded_of_below h2 _ (cfold_bound (Nat.add_pos_left hm _) n _ hre))585 (by rw [length_cfold n _ lre]; exact hk),586 repack_pack p k _ (bounded_of_below h2 _ (cfold_bound (Nat.add_pos_left hm _) n _ him))587 (by rw [length_cfold n _ lim]; exact hk)]588589/-- The sum of two packed rows, reduced slot-wise. -/590
def padd (p k : Nat) (A C : Nat) : Nat := repack p k (A + C)591592
theorem padd_eq (p k : Nat) (hp : 0 < p) (a b : List Nat) (ha : Below p a) (hb : Below p b) (la : a.length ≤ 16 * 2 ^ k)593 (hab : b.length ≤ a.length) (hB : p + p ≤ B) : padd p k (pack a) (pack b) = pack ((addL a b).map (· % p)) := by594 show repack p k (pack a + pack b) = _595 rw [← pack_addL]596 exact repack_pack p k _ (bounded_of_below hB _ (below_addL hp hp a b ha hb))597 (by rw [length_addL_left a b hab]; exact la)598599/-- The number of halvings that cover `n` slots: the least `k` with `n ≤ 2^k`, by halving `n` (its own fuel), and600the halvings above the leaves of sixteen slots, `16 · 2^(slots n) ≥ n`. -/601
def slotsAux : Nat → Nat → Nat602 | 0, _ => 0603 | f + 1, n => if n ≤ 1 then 0 else slotsAux f ((n + 1) / 2) + 1604
def slots (n : Nat) : Nat := slotsAux ((n + 15) / 16) ((n + 15) / 16)605606
theorem le_two_pow_slotsAux : ∀ (f n : Nat), n ≤ f → n ≤ 2 ^ slotsAux f n607 | 0, n, h => Nat.le_trans h (Nat.zero_le _)608 | f + 1, n, h => by609 show n ≤ 2 ^ (if n ≤ 1 then 0 else slotsAux f ((n + 1) / 2) + 1)610 match Nat.decLe n 1 with611 | .isTrue h1 => rw [ite_eq_left h1]; exact h1612 | .isFalse h1 =>613 rw [ite_eq_right h1]614 have hn : 1 < n := Nat.lt_of_not_le h1615 have e := div_add_mod' 2 (by decide) (n + 1)616 have hq : 2 * ((n + 1) / 2) ≤ n + 1 := Nat.le_trans (Nat.le_add_right _ _) (Nat.le_of_eq e.symm)617 have h2 : 2 * ((n + 1) / 2) < 2 * n :=618 Nat.lt_of_le_of_lt hq (by rw [Nat.two_mul]; exact Nat.add_lt_add_left hn n)619 have hlt : (n + 1) / 2 < n :=620 Nat.lt_of_not_le fun hle => Nat.lt_irrefl _ (Nat.lt_of_lt_of_le h2 (Nat.mul_le_mul_left 2 hle))621 have ih := le_two_pow_slotsAux f ((n + 1) / 2) (Nat.le_of_lt_succ (Nat.lt_of_lt_of_le hlt h))622 have hdiv : n ≤ 2 * ((n + 1) / 2) := by623 have hr : (n + 1) % 2 ≤ 1 := Nat.le_of_lt_succ (Nat.mod_lt _ (by decide))624 have : n + 1 ≤ 2 * ((n + 1) / 2) + 1 := Nat.le_trans (Nat.le_of_eq e) (Nat.add_le_add_left hr _)625 exact Nat.le_of_succ_le_succ this626 rw [Nat.pow_succ, Nat.mul_comm]627 exact Nat.le_trans hdiv (Nat.mul_le_mul_left 2 ih)628629
theorem le_two_pow_slots (n : Nat) : n ≤ 16 * 2 ^ slots n := by630 have e := div_add_mod' 16 (by decide) (n + 15)631 have hr : (n + 15) % 16 ≤ 15 := Nat.le_of_lt_succ (Nat.mod_lt _ (by decide))632 have h1 : n ≤ 16 * ((n + 15) / 16) :=633 Nat.le_of_add_le_add_right (Nat.le_trans (Nat.le_of_eq e) (Nat.add_le_add_left hr _))634 exact Nat.le_trans h1 (Nat.mul_le_mul_left 16 (le_two_pow_slotsAux _ _ (Nat.le_refl _)))635636/-! ## Matrices over the extension: the rows of a product as combinations of rows -/637638/-- The product of two matrices over `K` given as pairs of matrices of residues (the real and the `w` parts, rows as639lists): the row `i` of `AB` is the combination of the rows of `B` by the entries of the row `i` of `A`, the list form. -/640
def mulExtList (p ν : Nat) (A B : List (List Nat) × List (List Nat)) : List (List Nat) × List (List Nat) :=641 (List.zipWith (fun ar ai => (addL (combo ar B.1) (smul ν (combo ai B.2))).map (· % p)) A.1 A.2,642 List.zipWith (fun ar ai => (addL (combo ar B.2) (combo ai B.1)).map (· % p)) A.1 A.2)643644/-- The packed form: the rows of `B` packed once, each row of the product as sums of scalar multiples of them. -/645
def mulExtPacked (p ν n : Nat) (A : List (List Nat) × List (List Nat)) (P : List Nat × List Nat) :646 List (List Nat) × List (List Nat) :=647 (List.zipWith (fun ar ai => unpack p n (packCombo ar P.1 + ν * packCombo ai P.2)) A.1 A.2,648 List.zipWith (fun ar ai => unpack p n (packCombo ar P.2 + packCombo ai P.1)) A.1 A.2)649650/-- A matrix of residues below `p`: every row below `p`, of `n` entries. -/651
def Mtx (p n : Nat) : List (List Nat) → Prop652 | [] => True653 | r :: rs => Below p r ∧ r.length = n ∧ Mtx p n rs654655
theorem length_combo {p n : Nat} : ∀ (cs : List Nat) (rs : List (List Nat)), 0 < cs.length → Mtx p n rs →656 cs.length ≤ rs.length → (combo cs rs).length = n657 | [], _, h, _, _ => absurd h (Nat.lt_irrefl 0)658 | _ :: _, [], _, _, hl => absurd hl (Nat.not_succ_le_zero _)659 | [c], r :: _, _, ⟨_, hr, _⟩, _ => by660 show (addL (smul c r) []).length = n661 rw [length_addL_left _ _ (Nat.zero_le _), length_smul, hr]662 | c :: c' :: cs, r :: rs, _, ⟨_, hr, hrs⟩, hl => by663 show (addL (smul c r) (combo (c' :: cs) rs)).length = n664 have ih := length_combo (c' :: cs) rs (Nat.succ_pos _) hrs (Nat.le_of_succ_le_succ hl)665 rw [length_addL_left _ _ (by rw [ih, length_smul, hr]; exact Nat.le_refl n), length_smul, hr]666667/-- The combination of rows of residues below `p` by `k` coefficients below `p` has entries below `k p² + 1`. -/668
theorem combo_bound {p n : Nat} (hp : 0 < p) : ∀ (cs : List Nat) (rs : List (List Nat)), Below p cs → Mtx p n rs →669 Below (cs.length * (p * p) + 1) (combo cs rs)670 | [], _, _, _ => trivial671 | _ :: _, [], _, _ => trivial672 | c :: cs, r :: rs, ⟨hc, hcs⟩, ⟨hr, _, hrs⟩ => by673 show Below ((cs.length + 1) * (p * p) + 1) (addL (smul c r) (combo cs rs))674 have := below_addL (Nat.mul_pos hp hp) (Nat.succ_pos _) _ _ (below_smul hc r hr) (combo_bound hp cs rs hcs hrs)675 rw [Nat.succ_mul, Nat.add_comm (cs.length * (p * p)) (p * p), Nat.add_assoc]676 exact this677678
theorem map_pack_length : ∀ rs : List (List Nat), (rs.map pack).length = rs.length679 | [] => rfl680 | _ :: rs => by show (rs.map pack).length + 1 = rs.length + 1; rw [map_pack_length rs]681682/-- One row of the product: the packed combination is the list combination. -/683
theorem row_eq (p ν n : Nat) (hp : 0 < p) (hν : ν < p) (hn : 0 < n) (ar ai : List Nat) (M : List (List Nat) × List (List Nat))684 (har : Below p ar) (hai : Below p ai) (lar : ar.length = n) (lai : ai.length = n)685 (hM1 : Mtx p n M.1) (hM2 : Mtx p n M.2) (lM1 : M.1.length = n) (lM2 : M.2.length = n)686 (hB : p * (n * (p * p) + 1) + p * (n * (p * p) + 1) ≤ B) :687 unpack p n (packCombo ar (M.1.map pack) + ν * packCombo ai (M.2.map pack)) =688 (addL (combo ar M.1) (smul ν (combo ai M.2))).map (· % p) ∧689 unpack p n (packCombo ar (M.2.map pack) + packCombo ai (M.1.map pack)) =690 (addL (combo ar M.2) (combo ai M.1)).map (· % p) := by691 have hm : 0 < p * (n * (p * p) + 1) := Nat.mul_pos hp (Nat.succ_pos _)692 have c1 : Below (p * (n * (p * p) + 1)) (combo ar M.1) :=693 below_mono (Nat.le_mul_of_pos_left _ hp) _ (lar ▸ combo_bound hp ar M.1 har hM1)694 have c2 : Below (p * (n * (p * p) + 1)) (smul ν (combo ai M.2)) :=695 below_smul hν _ (lai ▸ combo_bound hp ai M.2 hai hM2)696 have c3 : Below (p * (n * (p * p) + 1)) (combo ar M.2) :=697 below_mono (Nat.le_mul_of_pos_left _ hp) _ (lar ▸ combo_bound hp ar M.2 har hM2)698 have c4 : Below (p * (n * (p * p) + 1)) (combo ai M.1) :=699 below_mono (Nat.le_mul_of_pos_left _ hp) _ (lai ▸ combo_bound hp ai M.1 hai hM1)700 have l1 : (combo ar M.1).length = n := length_combo ar M.1 (lar ▸ hn) hM1 (by rw [lar, lM1]; exact Nat.le_refl n)701 have l2 : (combo ai M.2).length = n := length_combo ai M.2 (lai ▸ hn) hM2 (by rw [lai, lM2]; exact Nat.le_refl n)702 have l3 : (combo ar M.2).length = n := length_combo ar M.2 (lar ▸ hn) hM2 (by rw [lar, lM2]; exact Nat.le_refl n)703 have l4 : (combo ai M.1).length = n := length_combo ai M.1 (lai ▸ hn) hM1 (by rw [lai, lM1]; exact Nat.le_refl n)704 constructor705 · rw [← pack_combo, ← pack_combo, ← pack_smul, ← pack_addL,706 ← unpack_pack p _ (bounded_of_below hB _ (below_addL hm hm _ _ c1 c2)),707 length_addL_eq l1 (by rw [length_smul, l2])]708 · rw [← pack_combo, ← pack_combo, ← pack_addL,709 ← unpack_pack p _ (bounded_of_below hB _ (below_addL hm hm _ _ c3 c4)), length_addL_eq l3 l4]710711/-- The packed product of two matrices over `K` is the list form, when `2 p (n p² + 1) ≤ B`. -/712
theorem mulExtPacked_eq (p ν n : Nat) (hp : 0 < p) (hν : ν < p) (hn : 0 < n)713 (A M : List (List Nat) × List (List Nat)) (hA1 : Mtx p n A.1) (hA2 : Mtx p n A.2)714 (hM1 : Mtx p n M.1) (hM2 : Mtx p n M.2) (lM1 : M.1.length = n) (lM2 : M.2.length = n)715 (hB : p * (n * (p * p) + 1) + p * (n * (p * p) + 1) ≤ B) :716 mulExtPacked p ν n A (M.1.map pack, M.2.map pack) = mulExtList p ν A M := by717 obtain ⟨A1, A2⟩ := A718 unfold mulExtPacked mulExtList719 induction A1 generalizing A2 with720 | nil => cases A2 <;> rfl721 | cons ar A1 ih =>722 cases A2 with723 | nil => rfl724 | cons ai A2 =>725 obtain ⟨har, lar, hA1'⟩ := hA1726 obtain ⟨hai, lai, hA2'⟩ := hA2727 have h := row_eq p ν n hp hν hn ar ai M har hai lar lai hM1 hM2 lM1 lM2 hB728 have ih' := ih A2 hA1' hA2'729 show (unpack p n (packCombo ar (M.1.map pack) + ν * packCombo ai (M.2.map pack)) :: List.zipWith _ A1 A2,730 unpack p n (packCombo ar (M.2.map pack) + packCombo ai (M.1.map pack)) :: List.zipWith _ A1 A2) =731 ((addL (combo ar M.1) (smul ν (combo ai M.2))).map (· % p) :: List.zipWith _ A1 A2,732 (addL (combo ar M.2) (combo ai M.1)).map (· % p) :: List.zipWith _ A1 A2)733 rw [h.1, h.2, (Prod.mk.inj ih').1, (Prod.mk.inj ih').2]734735end FRC.Dirac.Packed736

