From 192d0942181dec497dc9b6204bb42e5380f01dac Mon Sep 17 00:00:00 2001 From: Georgios Raikos <57717352+graikos@users.noreply.github.com> Date: Tue, 23 Jun 2026 09:34:55 +0000 Subject: [PATCH 01/31] feat(fields): Fast BabyBear impl, mirroring KB --- CompPoly.lean | 5 + CompPoly/Fields/BabyBear.lean | 247 +------ CompPoly/Fields/BabyBear/Basic.lean | 264 ++++++++ CompPoly/Fields/BabyBear/Fast.lean | 629 ++++++++++++++++++ CompPoly/Fields/BabyBear/Fast/Convert.lean | 251 +++++++ CompPoly/Fields/BabyBear/Fast/Montgomery.lean | 322 +++++++++ CompPoly/Fields/BabyBear/Fast/Prelude.lean | 81 +++ 7 files changed, 1558 insertions(+), 241 deletions(-) create mode 100644 CompPoly/Fields/BabyBear/Basic.lean create mode 100644 CompPoly/Fields/BabyBear/Fast.lean create mode 100644 CompPoly/Fields/BabyBear/Fast/Convert.lean create mode 100644 CompPoly/Fields/BabyBear/Fast/Montgomery.lean create mode 100644 CompPoly/Fields/BabyBear/Fast/Prelude.lean diff --git a/CompPoly.lean b/CompPoly.lean index 58d6f008..385fc1b5 100644 --- a/CompPoly.lean +++ b/CompPoly.lean @@ -66,6 +66,11 @@ import CompPoly.Fields.BLS12_377 import CompPoly.Fields.BLS12_381 import CompPoly.Fields.BN254 import CompPoly.Fields.BabyBear +import CompPoly.Fields.BabyBear.Basic +import CompPoly.Fields.BabyBear.Fast +import CompPoly.Fields.BabyBear.Fast.Convert +import CompPoly.Fields.BabyBear.Fast.Montgomery +import CompPoly.Fields.BabyBear.Fast.Prelude import CompPoly.Fields.Basic import CompPoly.Fields.Binary.AdditiveNTT.AdditiveNTT import CompPoly.Fields.Binary.AdditiveNTT.Algorithm diff --git a/CompPoly/Fields/BabyBear.lean b/CompPoly/Fields/BabyBear.lean index e0bf3e60..bcc0bb89 100644 --- a/CompPoly/Fields/BabyBear.lean +++ b/CompPoly/Fields/BabyBear.lean @@ -4,248 +4,13 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Quang Dao, Valerii Huhnin -/ -import CompPoly.Fields.Basic -import CompPoly.Fields.PrattCertificate -import Mathlib.Algebra.Order.Ring.Star -import Mathlib.RingTheory.RootsOfUnity.PrimitiveRoots -/-! - # BabyBear Field `2^{31} - 2^{27} + 1` - - This is the field used by Risc Zero. --/ - -namespace BabyBear - -/-- The BabyBear field modulus, `2^31 - 2^27 + 1`. -/ -@[reducible] -def fieldSize : Nat := 2 ^ 31 - 2 ^ 27 + 1 - -/-- The BabyBear prime field as a `ZMod`. -/ -abbrev Field := ZMod fieldSize - -/-- The BabyBear modulus is prime. -/ -theorem is_prime : Nat.Prime fieldSize := by - unfold fieldSize - pratt - -/-! - ## Constants - - These are convenience constants for the BabyBear field: - - `pBits = 31` - - `twoAdicity = 27` with `fieldSize - 1 = 2^27 * 15` --/ - -/-- Bit width of the BabyBear modulus. -/ -@[reducible] -def pBits : Nat := 31 - -/-- The largest supported power-of-two root-of-unity exponent for BabyBear. -/ -@[reducible] -def twoAdicity : Nat := 27 - -instance : Fact (Nat.Prime fieldSize) := ⟨is_prime⟩ - -instance : _root_.Field Field := ZMod.instField fieldSize - -instance : NonBinaryField Field where - char_neq_2 := by - simpa [Field, fieldSize] using - (by decide : (2 : ZMod (2 ^ 31 - 2 ^ 27 + 1)) ≠ 0) - -/-! - ## Two-adicity and roots of unity table - - We record the factorization of `fieldSize - 1` and provide a precomputed table - of `2^n`-th roots of unity for `0 ≤ n ≤ twoAdicity`. --/ - -/-- The factorization `fieldSize - 1 = 2^twoAdicity * 15`. -/ -lemma fieldSize_sub_one_factorization : fieldSize - 1 = 2 ^ twoAdicity * 15 := by - unfold fieldSize twoAdicity - decide +import CompPoly.Fields.BabyBear.Basic +import CompPoly.Fields.BabyBear.Fast /-! - A table of `2^n`-th roots of unity. The element at index `n` generates the - multiplicative subgroup of order `2^n`. +# BabyBear Field - The first entry n = 0 is 1. +Facade module for the BabyBear field. It re-exports the canonical `ZMod` model +from `CompPoly.Fields.BabyBear.Basic` and the native-word implementation from +`CompPoly.Fields.BabyBear.Fast`. -/ -/-- Precomputed generators for the BabyBear two-adic subgroups. -/ -def twoAdicGenerators : List Field := - [ - (0x1 : Field), - (0x78000000 : Field), - (0x67055C21 : Field), - (0x5EE99486 : Field), - (0xBB4C4E4 : Field), - (0x2D4CC4DA : Field), - (0x669D6090 : Field), - (0x17B56C64 : Field), - (0x67456167 : Field), - (0x688442F9 : Field), - (0x145E952D : Field), - (0x4FE61226 : Field), - (0x4C734715 : Field), - (0x11C33E2A : Field), - (0x62C3D2B1 : Field), - (0x77CAD399 : Field), - (0x54C131F4 : Field), - (0x4CABD6A6 : Field), - (0x5CF5713F : Field), - (0x3E9430E8 : Field), - (0xBA067A3 : Field), - (0x18ADC27D : Field), - (0x21FD55BC : Field), - (0x4B859B3D : Field), - (0x3BD57996 : Field), - (0x4483D85A : Field), - (0x3A26EEF8 : Field), - (0x1A427A41 : Field) - ] - -/-- The BabyBear two-adic generator table has one entry for each supported exponent. -/ -@[simp] lemma twoAdicGenerators_length : twoAdicGenerators.length = twoAdicity + 1 := by - decide - -/-- Consecutive BabyBear two-adic generators square to the previous generator. -/ -@[simp] lemma twoAdicGenerators_succ_square_eq' (idx : Fin twoAdicity) : - twoAdicGenerators[idx.val + 1] ^ 2 = twoAdicGenerators[idx] := by - fin_cases idx - <;> simp [twoAdicGenerators] <;> decide - -/-- Consecutive BabyBear two-adic generators square to the previous generator. -/ -@[simp] lemma twoAdicGenerators_succ_square_eq (idx : Nat) (h : idx < twoAdicity) : - haveI : idx + 1 < twoAdicGenerators.length := by simp [twoAdicGenerators_length, h] - twoAdicGenerators[idx + 1] ^ 2 = twoAdicGenerators[idx] := - twoAdicGenerators_succ_square_eq' ⟨idx, h⟩ - -/-- `twoAdicity` is maximal: `2^(twoAdicity+1)` does not divide `fieldSize - 1`. -/ -lemma twoAdicity_maximal : ¬ (2 ^ (twoAdicity + 1)) ∣ (fieldSize - 1) := by - decide - -/-- Repeated squaring: `sqChain g n = g ^ (2^n)`. - Does `n` multiplications instead of `2^n`, making it kernel-friendly. -/ -private def sqChain (g : Field) : Nat → Field - | 0 => g - | n + 1 => let h := sqChain g n; h * h - -/-- Repeated squaring agrees with exponentiation by a power of two. -/ -private theorem sqChain_eq_pow_two_pow (g : Field) (n : Nat) : - sqChain g n = g ^ (2 ^ n) := by - induction n with - | zero => simp [sqChain] - | succ n ih => simp [sqChain, ih, pow_succ, pow_mul] - -/-- Repeated squaring walks backward through the precomputed two-adic generator table. -/ -private theorem sqChain_twoAdicGenerators_shift (k n : Nat) (hkn : k + n ≤ twoAdicity) : - sqChain twoAdicGenerators[(⟨k + n, by omega⟩ : Fin (twoAdicity + 1))] n = - twoAdicGenerators[(⟨k, by omega⟩ : Fin (twoAdicity + 1))] := by - induction n generalizing k with - | zero => - simp [sqChain] - | succ n ih => - calc - sqChain twoAdicGenerators[(⟨k + (n + 1), by omega⟩ : Fin (twoAdicity + 1))] - (n + 1) - = sqChain twoAdicGenerators[(⟨k + (n + 1), by omega⟩ : - Fin (twoAdicity + 1))] n * - sqChain twoAdicGenerators[(⟨k + (n + 1), by omega⟩ : - Fin (twoAdicity + 1))] n := by - simp [sqChain] - _ = twoAdicGenerators[(⟨k + 1, by omega⟩ : Fin (twoAdicity + 1))] * - twoAdicGenerators[(⟨k + 1, by omega⟩ : Fin (twoAdicity + 1))] := by - have hshift : - sqChain twoAdicGenerators[(⟨k + (n + 1), by omega⟩ : - Fin (twoAdicity + 1))] n = - twoAdicGenerators[(⟨k + 1, by omega⟩ : Fin (twoAdicity + 1))] := by - simpa [Nat.add_assoc, Nat.add_left_comm, Nat.add_comm] using - (ih (k := k + 1) (by omega)) - exact congrArg (fun x ↦ x * x) hshift - _ = twoAdicGenerators[(⟨k + 1, by omega⟩ : Fin (twoAdicity + 1))] ^ 2 := by - simp [pow_two] - _ = twoAdicGenerators[(⟨k, by omega⟩ : Fin (twoAdicity + 1))] := by - simpa [Nat.succ_eq_add_one, Nat.add_assoc, Nat.add_left_comm, Nat.add_comm] - using (twoAdicGenerators_succ_square_eq k (by omega)) - -/-- Every nonzero index in the precomputed table is genuinely nontrivial. -/ -private lemma twoAdicGenerators_ne_one_of_pos (n : Fin (twoAdicity + 1)) (hn : 0 < n) : - twoAdicGenerators[n] ≠ (1 : Field) := by - fin_cases n <;> simp_all [twoAdicGenerators] <;> decide - -/-- The power `(twoAdicGenerators[bits])^(2^bits) = 1`. -/ -lemma twoAdicGenerators_pow_twoPow_eq_one (bits : Fin (twoAdicity + 1)) : - twoAdicGenerators[bits] ^ (2 ^ (bits : Nat)) = 1 := by - rw [← sqChain_eq_pow_two_pow] - rcases bits with ⟨n, hn⟩ - have hshift : - sqChain twoAdicGenerators[(⟨n, hn⟩ : Fin (twoAdicity + 1))] n = - twoAdicGenerators[(⟨0, by omega⟩ : Fin (twoAdicity + 1))] := by - simpa using sqChain_twoAdicGenerators_shift 0 n (by omega) - simpa [twoAdicGenerators] using hshift - -/-- Helper: Fin-indexed version for computational verification of non-triviality. -/ -private lemma twoAdicGenerators_pow_ne_one_aux (n : Fin 28) (m : Fin 28) - (hm : m.val < n.val) : - twoAdicGenerators[n] ^ (2 ^ m.val) ≠ (1 : Field) := by - rw [← sqChain_eq_pow_two_pow] - let nMinus : Fin (twoAdicity + 1) := - ⟨n.val - m.val, lt_of_le_of_lt (Nat.sub_le _ _) n.isLt⟩ - have hshift : - sqChain twoAdicGenerators[n] m.val = twoAdicGenerators[nMinus] := by - have hn_le : n.val ≤ twoAdicity := by - simpa [twoAdicity] using Nat.le_of_lt_succ n.isLt - have hbound : (n.val - m.val) + m.val ≤ twoAdicity := by - simpa [Nat.sub_add_cancel (Nat.le_of_lt hm)] using hn_le - simpa [Nat.sub_add_cancel (Nat.le_of_lt hm)] using - (sqChain_twoAdicGenerators_shift (n.val - m.val) m.val hbound) - rw [hshift] - exact twoAdicGenerators_ne_one_of_pos nMinus (by - change 0 < n.val - m.val - exact Nat.sub_pos_of_lt hm) - -/-- If `m < bits`, then `(twoAdicGenerators[bits])^(2^m) ≠ 1`. -/ -lemma twoAdicGenerators_pow_twoPow_ne_one_of_lt - {bits : Fin (twoAdicity + 1)} {m : Nat} (hm : m < bits) : - (twoAdicGenerators[bits]) ^ (2 ^ m) ≠ (1 : Field) := by - have hm_lt : m < 28 := Nat.lt_trans hm bits.isLt - exact twoAdicGenerators_pow_ne_one_aux bits ⟨m, hm_lt⟩ hm - -/-- The precomputed element at index `bits` is a primitive `2^bits`-th root of unity. -/ -lemma isPrimitiveRoot_twoAdicGenerator (n : Fin (twoAdicity + 1)) : - IsPrimitiveRoot (twoAdicGenerators[n]) (2 ^ (n : Nat)) := by - rw [IsPrimitiveRoot.iff_def] - rcases n with ⟨_ | k, hb⟩ - · simp [twoAdicGenerators] - · constructor - · exact twoAdicGenerators_pow_twoPow_eq_one ⟨k + 1, hb⟩ - · intro m hm - by_contra h - have hord := orderOf_eq_prime_pow - (twoAdicGenerators_pow_twoPow_ne_one_of_lt (bits := ⟨k + 1, hb⟩) (m := k) - (by simp)) - (twoAdicGenerators_pow_twoPow_eq_one ⟨k + 1, hb⟩) - have hdvd := orderOf_dvd_of_pow_eq_one hm - rw [hord] at hdvd - exact h hdvd - -/-- As a unit, the precomputed element is a member of `rootsOfUnity (2^bits)`. -/ -lemma twoAdicGenerator_unit_mem_rootsOfUnity - (bits : Fin (twoAdicity + 1)) (h : twoAdicGenerators[bits] ≠ 0) : - Units.mk0 (twoAdicGenerators[bits]) h ∈ rootsOfUnity (2 ^ (bits : Nat)) Field := by - rw [mem_rootsOfUnity] - rw [Units.ext_iff] - simp only [Units.val_pow_eq_pow_val, Units.val_mk0, Units.val_one] - exact twoAdicGenerators_pow_twoPow_eq_one bits - -/-- The order of `twoAdicGenerators[bits]` equals `2^bits`. -/ -lemma twoAdicGenerators_order (bits : Fin (twoAdicity + 1)) : - orderOf (twoAdicGenerators[bits]) = 2 ^ (bits : Nat) := by - rcases bits with ⟨_ | n, hb⟩ - · simp [twoAdicGenerators, orderOf_one] - · exact orderOf_eq_prime_pow - (twoAdicGenerators_pow_twoPow_ne_one_of_lt (bits := ⟨n + 1, hb⟩) (m := n) - (by simp)) - (twoAdicGenerators_pow_twoPow_eq_one ⟨n + 1, hb⟩) - -end BabyBear diff --git a/CompPoly/Fields/BabyBear/Basic.lean b/CompPoly/Fields/BabyBear/Basic.lean new file mode 100644 index 00000000..37a0729a --- /dev/null +++ b/CompPoly/Fields/BabyBear/Basic.lean @@ -0,0 +1,264 @@ +/- +Copyright (c) 2024 ArkLib Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Quang Dao, Valerii Huhnin +-/ + +import CompPoly.Fields.Basic +import CompPoly.Fields.PrattCertificate +import Mathlib.Algebra.Order.Ring.Star +import Mathlib.RingTheory.RootsOfUnity.PrimitiveRoots +import Mathlib.FieldTheory.Finite.Basic +/-! + # BabyBear Field `2^{31} - 2^{27} + 1` + + This is the field used by Risc Zero. +-/ + +namespace BabyBear + +/-- The BabyBear field modulus, `2^31 - 2^27 + 1`. -/ +@[reducible] +def fieldSize : Nat := 2 ^ 31 - 2 ^ 27 + 1 + +/-- The BabyBear prime field as a `ZMod`. -/ +abbrev Field := ZMod fieldSize + +/-- The BabyBear modulus is prime. -/ +theorem is_prime : Nat.Prime fieldSize := by + unfold fieldSize + pratt + +/-! + ## Constants + + These are convenience constants for the BabyBear field: + - `pBits = 31` + - `twoAdicity = 27` with `fieldSize - 1 = 2^27 * 15` +-/ + +/-- Bit width of the BabyBear modulus. -/ +@[reducible] +def pBits : Nat := 31 + +/-- The largest supported power-of-two root-of-unity exponent for BabyBear. -/ +@[reducible] +def twoAdicity : Nat := 27 + +instance : Fact (Nat.Prime fieldSize) := ⟨is_prime⟩ + +instance : _root_.Field Field := ZMod.instField fieldSize + +instance : NonBinaryField Field where + char_neq_2 := by + simpa [Field, fieldSize] using + (by decide : (2 : ZMod (2 ^ 31 - 2 ^ 27 + 1)) ≠ 0) + +/-- Fermat-style inversion in `ZMod fieldSize`. -/ +lemma inv_eq_pow (a : Field) (ha : a ≠ 0) : a⁻¹ = a ^ (fieldSize - 2) := by + have hcard : Fintype.card Field = fieldSize := ZMod.card fieldSize + have h1 : a ^ (fieldSize - 1) = 1 := by + have h := FiniteField.pow_card_sub_one_eq_one a ha + rw [hcard] at h; exact h + have hmul : a * a ^ (fieldSize - 2) = 1 := by + rw [← pow_succ']; show a ^ (fieldSize - 2 + 1) = 1 + have : fieldSize - 2 + 1 = fieldSize - 1 := by unfold fieldSize; omega + rw [this]; exact h1 + exact (eq_inv_of_mul_eq_one_left (by rwa [mul_comm])).symm + +/-! + ## Two-adicity and roots of unity table + + We record the factorization of `fieldSize - 1` and provide a precomputed table + of `2^n`-th roots of unity for `0 ≤ n ≤ twoAdicity`. +-/ + +/-- The factorization `fieldSize - 1 = 2^twoAdicity * 15`. -/ +lemma fieldSize_sub_one_factorization : fieldSize - 1 = 2 ^ twoAdicity * 15 := by + unfold fieldSize twoAdicity + decide + +/-! + A table of `2^n`-th roots of unity. The element at index `n` generates the + multiplicative subgroup of order `2^n`. + + The first entry n = 0 is 1. +-/ +/-- Precomputed generators for the BabyBear two-adic subgroups. -/ +def twoAdicGenerators : List Field := + [ + (0x1 : Field), + (0x78000000 : Field), + (0x67055C21 : Field), + (0x5EE99486 : Field), + (0xBB4C4E4 : Field), + (0x2D4CC4DA : Field), + (0x669D6090 : Field), + (0x17B56C64 : Field), + (0x67456167 : Field), + (0x688442F9 : Field), + (0x145E952D : Field), + (0x4FE61226 : Field), + (0x4C734715 : Field), + (0x11C33E2A : Field), + (0x62C3D2B1 : Field), + (0x77CAD399 : Field), + (0x54C131F4 : Field), + (0x4CABD6A6 : Field), + (0x5CF5713F : Field), + (0x3E9430E8 : Field), + (0xBA067A3 : Field), + (0x18ADC27D : Field), + (0x21FD55BC : Field), + (0x4B859B3D : Field), + (0x3BD57996 : Field), + (0x4483D85A : Field), + (0x3A26EEF8 : Field), + (0x1A427A41 : Field) + ] + +/-- The BabyBear two-adic generator table has one entry for each supported exponent. -/ +@[simp] lemma twoAdicGenerators_length : twoAdicGenerators.length = twoAdicity + 1 := by + decide + +/-- Consecutive BabyBear two-adic generators square to the previous generator. -/ +@[simp] lemma twoAdicGenerators_succ_square_eq' (idx : Fin twoAdicity) : + twoAdicGenerators[idx.val + 1] ^ 2 = twoAdicGenerators[idx] := by + fin_cases idx + <;> simp [twoAdicGenerators] <;> decide + +/-- Consecutive BabyBear two-adic generators square to the previous generator. -/ +@[simp] lemma twoAdicGenerators_succ_square_eq (idx : Nat) (h : idx < twoAdicity) : + haveI : idx + 1 < twoAdicGenerators.length := by simp [twoAdicGenerators_length, h] + twoAdicGenerators[idx + 1] ^ 2 = twoAdicGenerators[idx] := + twoAdicGenerators_succ_square_eq' ⟨idx, h⟩ + +/-- `twoAdicity` is maximal: `2^(twoAdicity+1)` does not divide `fieldSize - 1`. -/ +lemma twoAdicity_maximal : ¬ (2 ^ (twoAdicity + 1)) ∣ (fieldSize - 1) := by + decide + +/-- Repeated squaring: `sqChain g n = g ^ (2^n)`. + Does `n` multiplications instead of `2^n`, making it kernel-friendly. -/ +private def sqChain (g : Field) : Nat → Field + | 0 => g + | n + 1 => let h := sqChain g n; h * h + +/-- Repeated squaring agrees with exponentiation by a power of two. -/ +private theorem sqChain_eq_pow_two_pow (g : Field) (n : Nat) : + sqChain g n = g ^ (2 ^ n) := by + induction n with + | zero => simp [sqChain] + | succ n ih => simp [sqChain, ih, pow_succ, pow_mul] + +/-- Repeated squaring walks backward through the precomputed two-adic generator table. -/ +private theorem sqChain_twoAdicGenerators_shift (k n : Nat) (hkn : k + n ≤ twoAdicity) : + sqChain twoAdicGenerators[(⟨k + n, by omega⟩ : Fin (twoAdicity + 1))] n = + twoAdicGenerators[(⟨k, by omega⟩ : Fin (twoAdicity + 1))] := by + induction n generalizing k with + | zero => + simp [sqChain] + | succ n ih => + calc + sqChain twoAdicGenerators[(⟨k + (n + 1), by omega⟩ : Fin (twoAdicity + 1))] + (n + 1) + = sqChain twoAdicGenerators[(⟨k + (n + 1), by omega⟩ : + Fin (twoAdicity + 1))] n * + sqChain twoAdicGenerators[(⟨k + (n + 1), by omega⟩ : + Fin (twoAdicity + 1))] n := by + simp [sqChain] + _ = twoAdicGenerators[(⟨k + 1, by omega⟩ : Fin (twoAdicity + 1))] * + twoAdicGenerators[(⟨k + 1, by omega⟩ : Fin (twoAdicity + 1))] := by + have hshift : + sqChain twoAdicGenerators[(⟨k + (n + 1), by omega⟩ : + Fin (twoAdicity + 1))] n = + twoAdicGenerators[(⟨k + 1, by omega⟩ : Fin (twoAdicity + 1))] := by + simpa [Nat.add_assoc, Nat.add_left_comm, Nat.add_comm] using + (ih (k := k + 1) (by omega)) + exact congrArg (fun x ↦ x * x) hshift + _ = twoAdicGenerators[(⟨k + 1, by omega⟩ : Fin (twoAdicity + 1))] ^ 2 := by + simp [pow_two] + _ = twoAdicGenerators[(⟨k, by omega⟩ : Fin (twoAdicity + 1))] := by + simpa [Nat.succ_eq_add_one, Nat.add_assoc, Nat.add_left_comm, Nat.add_comm] + using (twoAdicGenerators_succ_square_eq k (by omega)) + +/-- Every nonzero index in the precomputed table is genuinely nontrivial. -/ +private lemma twoAdicGenerators_ne_one_of_pos (n : Fin (twoAdicity + 1)) (hn : 0 < n) : + twoAdicGenerators[n] ≠ (1 : Field) := by + fin_cases n <;> simp_all [twoAdicGenerators] <;> decide + +/-- The power `(twoAdicGenerators[bits])^(2^bits) = 1`. -/ +lemma twoAdicGenerators_pow_twoPow_eq_one (bits : Fin (twoAdicity + 1)) : + twoAdicGenerators[bits] ^ (2 ^ (bits : Nat)) = 1 := by + rw [← sqChain_eq_pow_two_pow] + rcases bits with ⟨n, hn⟩ + have hshift : + sqChain twoAdicGenerators[(⟨n, hn⟩ : Fin (twoAdicity + 1))] n = + twoAdicGenerators[(⟨0, by omega⟩ : Fin (twoAdicity + 1))] := by + simpa using sqChain_twoAdicGenerators_shift 0 n (by omega) + simpa [twoAdicGenerators] using hshift + +/-- Helper: Fin-indexed version for computational verification of non-triviality. -/ +private lemma twoAdicGenerators_pow_ne_one_aux (n : Fin 28) (m : Fin 28) + (hm : m.val < n.val) : + twoAdicGenerators[n] ^ (2 ^ m.val) ≠ (1 : Field) := by + rw [← sqChain_eq_pow_two_pow] + let nMinus : Fin (twoAdicity + 1) := + ⟨n.val - m.val, lt_of_le_of_lt (Nat.sub_le _ _) n.isLt⟩ + have hshift : + sqChain twoAdicGenerators[n] m.val = twoAdicGenerators[nMinus] := by + have hn_le : n.val ≤ twoAdicity := by + simpa [twoAdicity] using Nat.le_of_lt_succ n.isLt + have hbound : (n.val - m.val) + m.val ≤ twoAdicity := by + simpa [Nat.sub_add_cancel (Nat.le_of_lt hm)] using hn_le + simpa [Nat.sub_add_cancel (Nat.le_of_lt hm)] using + (sqChain_twoAdicGenerators_shift (n.val - m.val) m.val hbound) + rw [hshift] + exact twoAdicGenerators_ne_one_of_pos nMinus (by + change 0 < n.val - m.val + exact Nat.sub_pos_of_lt hm) + +/-- If `m < bits`, then `(twoAdicGenerators[bits])^(2^m) ≠ 1`. -/ +lemma twoAdicGenerators_pow_twoPow_ne_one_of_lt + {bits : Fin (twoAdicity + 1)} {m : Nat} (hm : m < bits) : + (twoAdicGenerators[bits]) ^ (2 ^ m) ≠ (1 : Field) := by + have hm_lt : m < 28 := Nat.lt_trans hm bits.isLt + exact twoAdicGenerators_pow_ne_one_aux bits ⟨m, hm_lt⟩ hm + +/-- The precomputed element at index `bits` is a primitive `2^bits`-th root of unity. -/ +lemma isPrimitiveRoot_twoAdicGenerator (n : Fin (twoAdicity + 1)) : + IsPrimitiveRoot (twoAdicGenerators[n]) (2 ^ (n : Nat)) := by + rw [IsPrimitiveRoot.iff_def] + rcases n with ⟨_ | k, hb⟩ + · simp [twoAdicGenerators] + · constructor + · exact twoAdicGenerators_pow_twoPow_eq_one ⟨k + 1, hb⟩ + · intro m hm + by_contra h + have hord := orderOf_eq_prime_pow + (twoAdicGenerators_pow_twoPow_ne_one_of_lt (bits := ⟨k + 1, hb⟩) (m := k) + (by simp)) + (twoAdicGenerators_pow_twoPow_eq_one ⟨k + 1, hb⟩) + have hdvd := orderOf_dvd_of_pow_eq_one hm + rw [hord] at hdvd + exact h hdvd + +/-- As a unit, the precomputed element is a member of `rootsOfUnity (2^bits)`. -/ +lemma twoAdicGenerator_unit_mem_rootsOfUnity + (bits : Fin (twoAdicity + 1)) (h : twoAdicGenerators[bits] ≠ 0) : + Units.mk0 (twoAdicGenerators[bits]) h ∈ rootsOfUnity (2 ^ (bits : Nat)) Field := by + rw [mem_rootsOfUnity] + rw [Units.ext_iff] + simp only [Units.val_pow_eq_pow_val, Units.val_mk0, Units.val_one] + exact twoAdicGenerators_pow_twoPow_eq_one bits + +/-- The order of `twoAdicGenerators[bits]` equals `2^bits`. -/ +lemma twoAdicGenerators_order (bits : Fin (twoAdicity + 1)) : + orderOf (twoAdicGenerators[bits]) = 2 ^ (bits : Nat) := by + rcases bits with ⟨_ | n, hb⟩ + · simp [twoAdicGenerators, orderOf_one] + · exact orderOf_eq_prime_pow + (twoAdicGenerators_pow_twoPow_ne_one_of_lt (bits := ⟨n + 1, hb⟩) (m := n) + (by simp)) + (twoAdicGenerators_pow_twoPow_eq_one ⟨n + 1, hb⟩) + +end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean new file mode 100644 index 00000000..4671cc56 --- /dev/null +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -0,0 +1,629 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin +-/ + +import CompPoly.Fields.BabyBear.Fast.Convert +import Mathlib.Algebra.Field.TransferInstance + +/-! +# Fast BabyBear Field Operations + +This module provides a native-word implementation of BabyBear arithmetic as a sidecar +to the canonical `BabyBear.Field := ZMod BabyBear.fieldSize` model. + +Fast field values are stored as Montgomery `UInt32` residues below `BabyBear.fieldSize`, +representing `x * 2^32` modulo the BabyBear prime. Addition, subtraction, and +negation operate directly on Montgomery words; multiplication uses Montgomery +reduction on a native `UInt64` product. + +Supporting layers live in sibling modules: field basics in +`CompPoly.Fields.BabyBear.Fast.Prelude`, Montgomery reduction in +`CompPoly.Fields.BabyBear.Fast.Montgomery`, and conversions/casts in +`CompPoly.Fields.BabyBear.Fast.Convert`. This module defines the field operations, +their instances, and the `toField` bridge to `BabyBear.Field`. +-/ + +namespace BabyBear +namespace Fast + +/-- Fast modular addition in Montgomery form. -/ +@[inline] +def add (x y : Field) : Field := + reduceUInt32Lt2Modulus (x.val + y.val) (by + rw [UInt32.toNat_add] + exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by omega)) + +/-- Fast modular negation in Montgomery form. -/ +@[inline] +def neg (x : Field) : Field := + if hx : x.val = 0 then + zero + else + ⟨modulus - x.val, by + have hle : x.val ≤ modulus := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + exact Nat.le_of_lt x.property + rw [UInt32.toNat_sub_of_le _ _ hle, modulus_toNat] + have hxpos : 0 < x.val.toNat := by + apply Nat.pos_of_ne_zero + intro hzero + apply hx + apply UInt32.toNat_inj.mp + simpa using hzero + omega⟩ + +/-- Fast modular subtraction in Montgomery form. -/ +@[inline] +def sub (x y : Field) : Field := + if hyx : y.val ≤ x.val then + ⟨x.val - y.val, by + rw [UInt32.toNat_sub_of_le _ _ hyx] + omega⟩ + else + ⟨x.val + modulus - y.val, by + have hsum_lt : x.val.toNat + BabyBear.fieldSize < UInt32.size := by + have htwo := fieldSize_add_fieldSize_lt_uint32Size + omega + have hsum_eq : (x.val + modulus).toNat = x.val.toNat + BabyBear.fieldSize := by + rw [UInt32.toNat_add, modulus_toNat, Nat.mod_eq_of_lt hsum_lt] + have hyle : y.val ≤ x.val + modulus := by + rw [UInt32.le_iff_toNat_le, hsum_eq] + omega + rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] + have hyxNat : ¬y.val.toNat ≤ x.val.toNat := by + intro hle + apply hyx + rw [UInt32.le_iff_toNat_le] + exact hle + omega⟩ + +/-- Fast modular multiplication in Montgomery form. -/ +@[inline] +def mul (x y : Field) : Field := + montgomeryReduceBounded (x.val.toUInt64 * y.val.toUInt64) (by + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by + nlinarith [x.property, y.property, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [x.property, y.property, fieldSize_lt_uint32Size, fieldSize_pos]) + +/-- Fast squaring. -/ +@[inline] +def square (x : Field) : Field := + mul x x + +/-- Exponentiation over the fast representation using repeated squaring. -/ +@[inline] +def pow (x : Field) (n : Nat) : Field := + @npowBinRec Field ⟨one⟩ ⟨mul⟩ n x + +/-- Fermat exponent used for inversion in the BabyBear prime field. -/ +def invExponent : Nat := BabyBear.fieldSize - 2 + +/-- Four squarings followed by multiplication by the next 4-bit exponent digit. -/ +@[inline] +private def shift4Mul (acc digit : Field) : Field := + mul (square (square (square (square acc)))) digit + +/-- Inversion in Montgomery form by a fixed 4-bit Fermat chain. -/ +@[inline] +def inv (x : Field) : Field := + let x2 := square x + let x3 := mul x2 x + let x5 := mul x3 x2 + let x7 := mul x5 x2 + let x14 := square x7 + let x15 := mul x14 x + let acc := shift4Mul x7 x7 + let acc := shift4Mul acc x15 + let acc := shift4Mul acc x15 + let acc := shift4Mul acc x15 + let acc := shift4Mul acc x15 + let acc := shift4Mul acc x15 + shift4Mul acc x15 + +/-- Division through inversion and fast multiplication. -/ +@[inline] +def div (x y : Field) : Field := + mul x (inv y) + +instance instZeroField : Zero Field where + zero := zero + +instance instOneField : One Field where + one := one + +instance instAddField : Add Field where + add := add + +instance instNegField : Neg Field where + neg := neg + +instance instSubField : Sub Field where + sub := sub + +instance instMulField : Mul Field where + mul := mul + +instance instInvField : Inv Field where + inv := inv + +instance instDivField : Div Field where + div := div + +instance instNatCastField : NatCast Field where + natCast := ofNat + +instance instIntCastField : IntCast Field where + intCast := ofInt + +instance instNatSMulField : SMul Nat Field where + smul n x := ofNat n * x + +instance instIntSMulField : SMul Int Field where + smul n x := ofInt n * x + +instance instPowFieldNat : Pow Field Nat where + pow := pow + +instance instPowFieldInt : Pow Field Int where + pow x n := + match n with + | Int.ofNat k => pow x k + | Int.negSucc k => pow (inv x) (k + 1) + +instance instNNRatCastField : NNRatCast Field where + nnratCast q := ofField (q : BabyBear.Field) + +instance instRatCastField : RatCast Field where + ratCast q := ofField (q : BabyBear.Field) + +instance instNNRatSMulField : SMul ℚ≥0 Field where + smul q x := ofField (q • toField x) + +instance instRatSMulField : SMul ℚ Field where + smul q x := ofField (q • toField x) + +/-- Converting from the canonical field to fast form and back is the identity. -/ +@[simp] +theorem toField_ofField (x : BabyBear.Field) : toField (ofField x) = x := by + unfold ofField + rw [toField_ofCanonicalNat] + exact ZMod.natCast_zmod_val x + +/-- Converting from fast form to the canonical field and back is the identity. -/ +@[simp] +theorem ofField_toField (x : Field) : ofField (toField x) = x := by + apply Subtype.ext + apply UInt32.toNat_inj.mp + apply nat_eq_of_field_eq + · exact (ofField (toField x)).property + · exact x.property + · rw [raw_cast_eq_toField_mul] + rw [toField_ofField] + rw [raw_cast_eq_toField_mul] + +/-- The canonical-field interpretation distinguishes fast BabyBear values. -/ +theorem toField_injective : Function.Injective toField := + Function.LeftInverse.injective ofField_toField + +/-- `toField` maps fast zero to canonical zero. -/ +@[simp] +theorem toField_zero : toField (0 : Field) = 0 := by + rw [toField_eq_raw_mul_inv] + change ((0 : Nat) : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ = 0 + exact zero_mul _ + +/-- `toField` maps fast one to canonical one. -/ +@[simp] +theorem toField_one : toField (1 : Field) = 1 := by + rw [toField_eq_raw_mul_inv] + change (rModModulus.toNat : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹ = 1 + rw [rModModulus_cast] + exact mul_inv_cancel₀ uint32Size_ne_zero_in_field + +/-- Fast addition agrees with addition in the canonical BabyBear field. -/ +@[simp] +theorem toField_add (x y : Field) : toField (x + y) = toField x + toField y := by + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] + unfold instAddField add + have hred := reduceUInt32Lt2Modulus_cast (x.val + y.val) (by + rw [UInt32.toNat_add] + exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by omega)) + change ((reduceUInt32Lt2ModulusRaw (x.val + y.val)).toNat : BabyBear.Field) = + ((x.val + y.val).toNat : BabyBear.Field) at hred + change ((reduceUInt32Lt2ModulusRaw (x.val + y.val)).toNat : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹ = + (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + + (y.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + rw [hred] + rw [UInt32.toNat_add] + have hsum_lt : x.val.toNat + y.val.toNat < UInt32.size := by + nlinarith [x.property, y.property, fieldSize_add_fieldSize_lt_uint32Size] + rw [Nat.mod_eq_of_lt hsum_lt] + rw [Nat.cast_add] + ring + +/-- Fast subtraction agrees with subtraction in the canonical BabyBear field. -/ +@[simp] +theorem toField_sub (x y : Field) : toField (x - y) = toField x - toField y := by + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] + by_cases hyx : y.val ≤ x.val + · have hsubval : (x - y : Field).val = x.val - y.val := by + change (sub x y).val = x.val - y.val + unfold sub + rw [dif_pos hyx] + rw [hsubval] + change (((x.val - y.val).toNat : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹) = + (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - + (y.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + rw [UInt32.toNat_sub_of_le _ _ hyx] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le] at hyx + exact hyx)] + ring + · have hsum_lt : x.val.toNat + BabyBear.fieldSize < UInt32.size := by + have htwo := fieldSize_add_fieldSize_lt_uint32Size + omega + have hsum_eq : (x.val + modulus).toNat = x.val.toNat + BabyBear.fieldSize := by + rw [UInt32.toNat_add, modulus_toNat, Nat.mod_eq_of_lt hsum_lt] + have hyle : y.val ≤ x.val + modulus := by + rw [UInt32.le_iff_toNat_le, hsum_eq] + omega + have hsubval : (x - y : Field).val = x.val + modulus - y.val := by + change (sub x y).val = x.val + modulus - y.val + unfold sub + rw [dif_neg hyx] + rw [hsubval] + change (((x.val + modulus - y.val).toNat : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹) = + (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - + (y.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le, hsum_eq] at hyle + exact hyle)] + rw [Nat.cast_add, ZMod.natCast_self] + ring + +/-- Fast negation agrees with negation in the canonical BabyBear field. -/ +@[simp] +theorem toField_neg (x : Field) : toField (-x) = -toField x := by + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x] + by_cases hx : x.val = 0 + · have hnegval : (-x : Field).val = zero.val := by + change (neg x).val = zero.val + unfold neg + rw [dif_pos hx] + rw [hnegval] + change ((zero.val.toNat : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹) = + -((x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹) + have hxNat : x.val.toNat = 0 := by + simpa using congrArg UInt32.toNat hx + rw [hxNat] + change ((0 : Nat) : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ = + -(((0 : Nat) : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹) + simp + · have hle : x.val ≤ modulus := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + exact Nat.le_of_lt x.property + have hnegval : (-x : Field).val = modulus - x.val := by + change (neg x).val = modulus - x.val + unfold neg + rw [dif_neg hx] + rw [hnegval] + change (((modulus - x.val).toNat : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹) = + -((x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹) + rw [UInt32.toNat_sub_of_le _ _ hle, modulus_toNat] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le, modulus_toNat] at hle + exact hle)] + rw [ZMod.natCast_self] + ring + +/-- Fast multiplication agrees with multiplication in the canonical BabyBear field. -/ +@[simp] +theorem toField_mul (x y : Field) : toField (x * y) = toField x * toField y := by + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] + unfold instMulField mul + have hred := montgomeryReduceBounded_cast (x.val.toUInt64 * y.val.toUInt64) (by + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by + nlinarith [x.property, y.property, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [x.property, y.property, fieldSize_lt_uint32Size, fieldSize_pos]) + change ((montgomeryReduceBoundedRaw (x.val.toUInt64 * y.val.toUInt64)).toNat : + BabyBear.Field) = + ((x.val.toUInt64 * y.val.toUInt64).toNat : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹ at hred + change ((montgomeryReduceBoundedRaw (x.val.toUInt64 * y.val.toUInt64)).toNat : + BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ = + (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ * + ((y.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹) + rw [hred] + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by + nlinarith [x.property, y.property, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + rw [Nat.cast_mul] + ring + +/-- Ring equivalence between the fast Montgomery representation and canonical `BabyBear.Field`. -/ +def ringEquiv : Field ≃+* BabyBear.Field where + toFun := toField + invFun := ofField + left_inv := ofField_toField + right_inv := toField_ofField + map_add' := toField_add + map_mul' := toField_mul + +/-- Applying `ringEquiv` is the same as interpreting a fast value canonically. -/ +@[simp] +theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := rfl + +/-- Applying the inverse `ringEquiv` is conversion into fast Montgomery form. -/ +@[simp] +theorem ringEquiv_symm_apply (x : BabyBear.Field) : ringEquiv.symm x = ofField x := rfl + +private theorem mul_assoc_field (x y z : Field) : (x * y) * z = x * (y * z) := by + apply toField_injective + rw [toField_mul, toField_mul, toField_mul, toField_mul] + ring + +private theorem pow_succ (x : Field) (n : Nat) : pow x (n + 1) = pow x n * x := by + unfold pow + letI : Semigroup Field := { + mul := (· * ·) + mul_assoc := mul_assoc_field + } + exact npowBinRec_succ n x + +/-- Fast squaring agrees with multiplication by itself in the canonical field. -/ +@[simp] +theorem toField_square (x : Field) : toField (square x) = toField x * toField x := by + change toField (x * x) = toField x * toField x + rw [toField_mul] + +/-- Fast natural-power computation agrees with powers in the canonical field. -/ +@[simp] +theorem toField_pow (x : Field) (n : Nat) : toField (pow x n) = toField x ^ n := by + induction n with + | zero => + unfold pow + rw [npowBinRec_zero] + rw [toField_one] + simp + | succ n ih => + rw [pow_succ, toField_mul, ih, _root_.pow_succ] + +private theorem toField_mul_pow (base x y : Field) (m n : Nat) + (hx : toField x = toField base ^ m) (hy : toField y = toField base ^ n) : + toField (mul x y) = toField base ^ (m + n) := by + change toField (x * y) = toField base ^ (m + n) + rw [toField_mul, hx, hy, ← pow_add] + +private theorem toField_shift4Mul (acc digit : Field) : + toField (shift4Mul acc digit) = toField acc ^ 16 * toField digit := by + unfold shift4Mul + change toField (square (square (square (square acc))) * digit) = + toField acc ^ 16 * toField digit + rw [toField_mul] + repeat rw [toField_square] + ring + +private theorem toField_shift4Mul_pow (base acc digit : Field) (e d : Nat) + (hacc : toField acc = toField base ^ e) (hdigit : toField digit = toField base ^ d) : + toField (shift4Mul acc digit) = toField base ^ (16 * e + d) := by + rw [toField_shift4Mul, hacc, hdigit] + rw [← pow_mul, ← pow_add] + congr 1 + omega + +private theorem toField_inv_pow (x : Field) : + toField (inv x) = toField x ^ invExponent := by + unfold inv + let x2 := square x + let x3 := mul x2 x + let x5 := mul x3 x2 + let x7 := mul x5 x2 + let x14 := square x7 + let x15 := mul x14 x + let acc1 := shift4Mul x7 x7 + let acc2 := shift4Mul acc1 x15 + let acc3 := shift4Mul acc2 x15 + let acc4 := shift4Mul acc3 x15 + let acc5 := shift4Mul acc4 x15 + let acc6 := shift4Mul acc5 x15 + have hx1 : toField x = toField x ^ 1 := by simp + have hx2 : toField x2 = toField x ^ 2 := by + dsimp [x2] + rw [toField_square, pow_two] + have hx3 : toField x3 = toField x ^ 3 := by + dsimp [x3] + simpa using toField_mul_pow x x2 x 2 1 hx2 hx1 + have hx5 : toField x5 = toField x ^ 5 := by + dsimp [x5] + simpa using toField_mul_pow x x3 x2 3 2 hx3 hx2 + have hx7 : toField x7 = toField x ^ 7 := by + dsimp [x7] + simpa using toField_mul_pow x x5 x2 5 2 hx5 hx2 + have hx14 : toField x14 = toField x ^ 14 := by + dsimp [x14] + rw [toField_square, hx7, ← pow_add] + have hx15 : toField x15 = toField x ^ 15 := by + dsimp [x15] + simpa using toField_mul_pow x x14 x 14 1 hx14 hx1 + have hacc1 : toField acc1 = toField x ^ 119 := by + dsimp [acc1] + simpa using toField_shift4Mul_pow x x7 x7 7 7 hx7 hx7 + have hacc2 : toField acc2 = toField x ^ 1919 := by + dsimp [acc2] + simpa using toField_shift4Mul_pow x acc1 x15 119 15 hacc1 hx15 + have hacc3 : toField acc3 = toField x ^ 30719 := by + dsimp [acc3] + simpa using toField_shift4Mul_pow x acc2 x15 1919 15 hacc2 hx15 + have hacc4 : toField acc4 = toField x ^ 491519 := by + dsimp [acc4] + simpa using toField_shift4Mul_pow x acc3 x15 30719 15 hacc3 hx15 + have hacc5 : toField acc5 = toField x ^ 7864319 := by + dsimp [acc5] + simpa using toField_shift4Mul_pow x acc4 x15 491519 15 hacc4 hx15 + have hacc6 : toField acc6 = toField x ^ 125829119 := by + dsimp [acc6] + simpa using toField_shift4Mul_pow x acc5 x15 7864319 15 hacc5 hx15 + have hfinal := toField_shift4Mul_pow x acc6 x15 125829119 15 hacc6 hx15 + simpa [invExponent, BabyBear.fieldSize] using hfinal + +private theorem toField_inv_raw (x : Field) : toField (inv x) = (toField x)⁻¹ := by + rw [toField_inv_pow] + by_cases hx : toField x = 0 + · rw [hx] + simp [invExponent, BabyBear.fieldSize] + · simpa [invExponent] using (BabyBear.inv_eq_pow (toField x) hx).symm + +/-- Fast inversion agrees with inversion in the canonical BabyBear field. -/ +@[simp] +theorem toField_inv (x : Field) : toField x⁻¹ = (toField x)⁻¹ := by + change toField (inv x) = (toField x)⁻¹ + exact toField_inv_raw x + +private theorem toField_mul_raw (x y : Field) : toField (mul x y) = toField x * toField y := by + change toField (x * y) = toField x * toField y + exact toField_mul x y + +private theorem toField_div_mul_inv (x y : Field) : + toField (div x y) = toField x * toField (inv y) := by + unfold div + exact toField_mul_raw x (inv y) + +/-- Fast division agrees with division in the canonical BabyBear field. -/ +@[simp] +theorem toField_div (x y : Field) : toField (x / y) = toField x / toField y := by + change toField (div x y) = toField x / toField y + have h : ∀ a b c : BabyBear.Field, c = b⁻¹ → a * c = a / b := by + intro a b c hc + rw [hc] + rfl + exact (toField_div_mul_inv x y).trans + (h (toField x) (toField y) (toField (inv y)) (toField_inv_raw y)) + +/-- Natural casts into fast form agree with natural casts into the canonical field. -/ +@[simp] +theorem toField_natCast (n : Nat) : toField (n : Field) = (n : BabyBear.Field) := by + change toField (ofNat n) = (n : BabyBear.Field) + unfold ofNat + rw [toField_ofCanonicalNat] + rw [ZMod.natCast_eq_natCast_iff] + exact Nat.mod_modEq _ _ + +/-- Integer casts into fast form agree with integer casts into the canonical field. -/ +@[simp] +theorem toField_intCast (n : Int) : toField (n : Field) = (n : BabyBear.Field) := by + change toField (ofInt n) = (n : BabyBear.Field) + unfold ofInt + rw [toField_ofField] + +/-- Natural scalar multiplication is preserved by `toField`. -/ +@[simp] +theorem toField_nsmul (n : Nat) (x : Field) : toField (n • x) = n • toField x := by + change toField ((n : Field) * x) = n • toField x + rw [toField_mul, toField_natCast] + rw [nsmul_eq_mul] + +/-- Integer scalar multiplication is preserved by `toField`. -/ +@[simp] +theorem toField_zsmul (n : Int) (x : Field) : toField (n • x) = n • toField x := by + change toField ((n : Field) * x) = n • toField x + rw [toField_mul, toField_intCast] + rw [zsmul_eq_mul] + +/-- Natural powers through the `Pow` instance are preserved by `toField`. -/ +@[simp] +theorem toField_npow (x : Field) (n : Nat) : toField (x ^ n) = toField x ^ n := by + change toField (pow x n) = toField x ^ n + rw [toField_pow] + +/-- Integer powers through the `Pow` instance are preserved by `toField`. -/ +@[simp] +theorem toField_zpow (x : Field) (n : Int) : toField (x ^ n) = toField x ^ n := by + cases n with + | ofNat n => + change toField (pow x n) = toField x ^ (Int.ofNat n) + rw [toField_pow] + exact (zpow_natCast (toField x) n).symm + | negSucc n => + change toField (pow (inv x) (n + 1)) = toField x ^ (Int.negSucc n) + have hinv : toField (inv x) = (toField x)⁻¹ := by + change toField x⁻¹ = (toField x)⁻¹ + rw [toField_inv] + rw [toField_pow, hinv, zpow_negSucc, inv_pow] + +/-- Nonnegative rational casts into fast form agree with canonical-field casts. -/ +@[simp] +theorem toField_nnratCast (q : ℚ≥0) : toField (q : Field) = (q : BabyBear.Field) := by + change toField (ofField (q : BabyBear.Field)) = (q : BabyBear.Field) + rw [toField_ofField] + +/-- Rational casts into fast form agree with canonical-field casts. -/ +@[simp] +theorem toField_ratCast (q : ℚ) : toField (q : Field) = (q : BabyBear.Field) := by + change toField (ofField (q : BabyBear.Field)) = (q : BabyBear.Field) + rw [toField_ofField] + +/-- Nonnegative rational scalar multiplication is preserved by `toField`. -/ +@[simp] +theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • toField x := by + change toField (ofField (q • toField x)) = q • toField x + rw [toField_ofField] + +/-- Rational scalar multiplication is preserved by `toField`. -/ +@[simp] +theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := by + change toField (ofField (q • toField x)) = q • toField x + rw [toField_ofField] + +/-- Field instance transferred from canonical BabyBear through `toField`. -/ +instance (priority := low) instField : _root_.Field Field := + toField_injective.field toField + toField_zero + toField_one + toField_add + toField_mul + toField_neg + toField_sub + toField_inv + toField_div + toField_nsmul + toField_zsmul + toField_nnqsmul + toField_qsmul + toField_npow + toField_zpow + toField_natCast + toField_intCast + toField_nnratCast + toField_ratCast + +/-- Commutative-ring instance inherited from the transferred field structure. -/ +instance (priority := low) instCommRing : CommRing Field := by + infer_instance + +/-- Fast BabyBear is a non-binary field. -/ +instance (priority := low) instNonBinaryField : NonBinaryField Field where + char_neq_2 := by + change ((2 : Nat) : Field) ≠ 0 + intro h + exact NonBinaryField.char_neq_2 (F := BabyBear.Field) (by + calc + (2 : BabyBear.Field) = toField ((2 : Nat) : Field) := (toField_natCast 2).symm + _ = toField (0 : Field) := congrArg toField h + _ = 0 := toField_zero) + +end Fast +end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean new file mode 100644 index 00000000..f3b6e96d --- /dev/null +++ b/CompPoly/Fields/BabyBear/Fast/Convert.lean @@ -0,0 +1,251 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin +-/ + +import CompPoly.Fields.BabyBear.Fast.Montgomery + +/-! +# Fast BabyBear Field — Conversions + +Conversions between the fast Montgomery representation and the canonical +`BabyBear.Field` / `Nat` views (`ofField`, `toField`, `ofNat`, `toNat`, …), the +`zero`/`one` constants, and the round-trip cast lemmas relating them. The field +operations and ring/field instances build on this module. +-/ + +namespace BabyBear +namespace Fast + +/-- Build a fast element from a canonical natural representative. -/ +@[inline] +def ofCanonicalNat (n : Nat) (_h : n < BabyBear.fieldSize) : Field := + montgomeryReduceBounded (UInt64.ofNat n * r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans _h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) + rw [hnmod] + have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [r2ModModulus_lt_fieldSize]) + +/-- Reduce a `UInt64` modulo the BabyBear prime and return a Montgomery fast element. -/ +@[inline] +def reduceUInt64 (x : UInt64) : Field := + let y := x % modulus64 + montgomeryReduceBounded (y * r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hy_lt : (x % modulus64).toNat < BabyBear.fieldSize := by + rw [UInt64.toNat_mod, modulus64_toNat] + exact Nat.mod_lt _ fieldSize_pos + have hprod : (x % modulus64).toNat * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [r2ModModulus_lt_fieldSize]) + +/-- The zero fast BabyBear element. -/ +def zero : Field := ⟨0, by decide⟩ + +/-- The one fast BabyBear element. -/ +def one : Field := ⟨rModModulus, by decide⟩ + +/-- Convert a natural number into fast Montgomery representation. -/ +@[inline] +def ofNat (n : Nat) : Field := + ofCanonicalNat (n % BabyBear.fieldSize) (Nat.mod_lt _ fieldSize_pos) + +/-- Convert a 32-bit word into fast Montgomery representation. -/ +@[inline] +def ofUInt32 (x : UInt32) : Field := + reduceUInt64 x.toUInt64 + +/-- Convert from the canonical `ZMod` BabyBear field into fast Montgomery form. -/ +@[inline] +def ofField (x : BabyBear.Field) : Field := + ofCanonicalNat x.val (ZMod.val_lt x) + +/-- Convert an integer into fast Montgomery representation. -/ +@[inline] +def ofInt (n : Int) : Field := + ofField (n : BabyBear.Field) + +/-- Convert a fast element to its canonical native-word representative. -/ +@[inline] +def toCanonicalUInt32 (x : Field) : UInt32 := + raw (montgomeryReduceBounded x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, fieldSize_pos])) + +/-- Convert a fast BabyBear element to its canonical natural representative. -/ +@[inline] +def toNat (x : Field) : Nat := + (toCanonicalUInt32 x).toNat + +/-- Convert a fast BabyBear element to the canonical `ZMod` BabyBear field. -/ +@[inline] +def toField (x : Field) : BabyBear.Field := + (toNat x : BabyBear.Field) + +theorem toNat_lt_fieldSize (x : Field) : toNat x < BabyBear.fieldSize := by + unfold toNat toCanonicalUInt32 raw + change (montgomeryReduceBoundedRaw x.val.toUInt64).toNat < BabyBear.fieldSize + exact montgomeryReduceBoundedRaw_lt x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, fieldSize_pos]) + +theorem toField_eq_raw_mul_inv (x : Field) : + toField x = + (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by + unfold toField toNat toCanonicalUInt32 raw + have hred := montgomeryReduceBounded_cast x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, fieldSize_pos]) + change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : BabyBear.Field) = + (x.val.toUInt64.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ at hred + change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : BabyBear.Field) = + (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + rw [hred] + rw [UInt32.toNat_toUInt64] + +theorem raw_cast_eq_toField_mul (x : Field) : + (x.val.toNat : BabyBear.Field) = + toField x * (UInt32.size : BabyBear.Field) := by + rw [toField_eq_raw_mul_inv] + rw [mul_assoc] + rw [inv_mul_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +theorem nat_eq_of_field_eq {a b : Nat} (ha : a < BabyBear.fieldSize) + (hb : b < BabyBear.fieldSize) (h : (a : BabyBear.Field) = (b : BabyBear.Field)) : + a = b := by + rw [ZMod.natCast_eq_natCast_iff] at h + exact Nat.ModEq.eq_of_lt_of_lt h ha hb + +theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < BabyBear.fieldSize) : + ((ofCanonicalNat n h).val.toNat : BabyBear.Field) = + (n : BabyBear.Field) * (UInt32.size : BabyBear.Field) := by + unfold ofCanonicalNat + have hred := montgomeryReduceBounded_cast + (UInt64.ofNat n * r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) + rw [hnmod] + have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [r2ModModulus_lt_fieldSize]) + change ((montgomeryReduceBoundedRaw + (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : BabyBear.Field) = + ((UInt64.ofNat n * r2ModModulus.toUInt64).toNat : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹ at hred + change ((montgomeryReduceBoundedRaw + (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : BabyBear.Field) = + (n : BabyBear.Field) * (UInt32.size : BabyBear.Field) + rw [hred] + simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) + rw [hnmod] + have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + rw [Nat.cast_mul, r2ModModulus_cast] + rw [pow_two] + rw [mul_assoc (n : BabyBear.Field) ((UInt32.size : BabyBear.Field) * + (UInt32.size : BabyBear.Field)) ((UInt32.size : BabyBear.Field)⁻¹)] + rw [mul_assoc (UInt32.size : BabyBear.Field) (UInt32.size : BabyBear.Field) + ((UInt32.size : BabyBear.Field)⁻¹)] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +theorem toField_ofCanonicalNat_aux (n : Nat) (h : n < BabyBear.fieldSize) : + toField (ofCanonicalNat n h) = (n : BabyBear.Field) := by + rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +theorem reduceUInt64_raw_cast (x : UInt64) : + ((reduceUInt64 x).val.toNat : BabyBear.Field) = + (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field) := by + unfold reduceUInt64 + let y := x % modulus64 + have hred := montgomeryReduceBounded_cast (y * r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hy_lt : y.toNat < BabyBear.fieldSize := by + rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] + exact Nat.mod_lt _ fieldSize_pos + have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [r2ModModulus_lt_fieldSize]) + change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : + BabyBear.Field) = + ((y * r2ModModulus.toUInt64).toNat : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹ at hred + change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : + BabyBear.Field) = + (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field) + rw [hred] + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hy_lt : y.toNat < BabyBear.fieldSize := by + rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] + exact Nat.mod_lt _ fieldSize_pos + have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + have hy_cast : (y.toNat : BabyBear.Field) = (x.toNat : BabyBear.Field) := by + rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] + rw [ZMod.natCast_eq_natCast_iff] + exact Nat.mod_modEq _ _ + rw [Nat.cast_mul, r2ModModulus_cast, hy_cast] + rw [pow_two] + rw [mul_assoc (x.toNat : BabyBear.Field) ((UInt32.size : BabyBear.Field) * + (UInt32.size : BabyBear.Field)) ((UInt32.size : BabyBear.Field)⁻¹)] + rw [mul_assoc (UInt32.size : BabyBear.Field) (UInt32.size : BabyBear.Field) + ((UInt32.size : BabyBear.Field)⁻¹)] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +/-- Converting a canonical natural representative to fast form preserves its value. -/ +@[simp] +theorem toNat_ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : + toNat (ofCanonicalNat n h) = n := by + exact nat_eq_of_field_eq (toNat_lt_fieldSize _) h (toField_ofCanonicalNat_aux n h) + +/-- `ofCanonicalNat` embeds a canonical representative into the canonical field. -/ +@[simp] +theorem toField_ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : + toField (ofCanonicalNat n h) = (n : BabyBear.Field) := by + exact toField_ofCanonicalNat_aux n h + +/-- Reducing a `UInt64` gives the canonical natural residue modulo BabyBear. -/ +@[simp] +theorem toNat_reduceUInt64 (x : UInt64) : + toNat (reduceUInt64 x) = x.toNat % BabyBear.fieldSize := by + apply nat_eq_of_field_eq (toNat_lt_fieldSize _) (Nat.mod_lt _ fieldSize_pos) + change toField (reduceUInt64 x) = ((x.toNat % BabyBear.fieldSize : Nat) : BabyBear.Field) + rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + rw [ZMod.natCast_eq_natCast_iff] + exact (Nat.mod_modEq _ _).symm + +/-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ +@[simp] +theorem toField_reduceUInt64 (x : UInt64) : + toField (reduceUInt64 x) = (x.toNat : BabyBear.Field) := by + rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +end Fast +end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean new file mode 100644 index 00000000..cd4b1151 --- /dev/null +++ b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean @@ -0,0 +1,322 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin +-/ + +import CompPoly.Fields.BabyBear.Fast.Prelude +import Mathlib.Data.Nat.ModEq + +/-! +# Fast BabyBear Field — Montgomery Reduction + +Montgomery-form constants (`montgomeryNegInv`, `rModModulus`, `r2ModModulus`) and the +native-word Montgomery reduction used by the fast BabyBear field. Field basics live +in `CompPoly.Fields.BabyBear.Fast.Prelude`; conversions and field operations build +on this module. +-/ + +namespace BabyBear +namespace Fast + +/-- `2^32 mod BabyBear.fieldSize`. This is the Montgomery representation of one. -/ +def rModModulus : UInt32 := 268435454 + +/-- `(2^32)^2 mod BabyBear.fieldSize`, used to enter Montgomery representation. -/ +def r2ModModulus : UInt32 := 1172168163 + +/-- `-BabyBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ +def montgomeryNegInv : UInt32 := 0x77FFFFFF + +theorem r2ModModulus_lt_fieldSize : r2ModModulus.toNat < BabyBear.fieldSize := by + decide + +theorem rModModulus_lt_fieldSize : rModModulus.toNat < BabyBear.fieldSize := by + decide + +theorem rModModulus_cast : + (rModModulus.toNat : BabyBear.Field) = (UInt32.size : BabyBear.Field) := by + decide + +theorem r2ModModulus_cast : + (r2ModModulus.toNat : BabyBear.Field) = (UInt32.size : BabyBear.Field) ^ 2 := by + decide + +theorem montgomery_sum_dvd (x : Nat) : + UInt32.size ∣ + x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * BabyBear.fieldSize := by + rw [← Nat.modEq_zero_iff_dvd] + have hx : x ≡ x % UInt32.size [MOD UInt32.size] := + (Nat.mod_modEq x UInt32.size).symm + have hm : + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * BabyBear.fieldSize ≡ + (x % UInt32.size) * (UInt32.size - 1) [MOD UInt32.size] := by + have hmi : + (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size ≡ + x % UInt32.size * montgomeryNegInv.toNat [MOD UInt32.size] := + Nat.mod_modEq _ _ + have hp : montgomeryNegInv.toNat * BabyBear.fieldSize ≡ UInt32.size - 1 + [MOD UInt32.size] := by + decide + calc + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * BabyBear.fieldSize + ≡ (x % UInt32.size * montgomeryNegInv.toNat) * BabyBear.fieldSize + [MOD UInt32.size] := hmi.mul_right _ + _ = x % UInt32.size * (montgomeryNegInv.toNat * BabyBear.fieldSize) := by ring + _ ≡ x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := hp.mul_left _ + calc + x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * BabyBear.fieldSize + ≡ x % UInt32.size + x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := + hx.add hm + _ = x % UInt32.size * UInt32.size := by + rw [add_comm, ← Nat.mul_succ] + have hsucc : (UInt32.size - 1).succ = UInt32.size := by decide + rw [hsucc] + _ ≡ 0 [MOD UInt32.size] := by + rw [Nat.modEq_zero_iff_dvd] + exact ⟨x % UInt32.size, by rw [mul_comm]⟩ + +/-- Nat-level Montgomery reduction used to specify and prove the native-word reducer. -/ +def montgomeryReduceNat (x : Nat) : Nat := + let m := (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size + let u := (x + m * BabyBear.fieldSize) / UInt32.size + if u < BabyBear.fieldSize then u else u - BabyBear.fieldSize + +theorem montgomeryReduceNat_cast (x : Nat) : + (montgomeryReduceNat x : BabyBear.Field) = + (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by + let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size + let u := (x + m * BabyBear.fieldSize) / UInt32.size + have hdiv : UInt32.size ∣ x + m * BabyBear.fieldSize := by + simpa [m] using montgomery_sum_dvd x + have hu_mul : u * UInt32.size = x + m * BabyBear.fieldSize := by + exact Nat.div_mul_cancel hdiv + have hcast_mul : (u : BabyBear.Field) * (UInt32.size : BabyBear.Field) = + (x : BabyBear.Field) := by + rw [← Nat.cast_mul, hu_mul, Nat.cast_add, Nat.cast_mul] + simp + have hR := uint32Size_ne_zero_in_field + by_cases hu : u < BabyBear.fieldSize + · have hmain : + (u : BabyBear.Field) = (x : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹ := by + calc + (u : BabyBear.Field) = + (u : BabyBear.Field) * (UInt32.size : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹ := by + rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] + _ = (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by + rw [hcast_mul] + dsimp only [montgomeryReduceNat] + rw [if_pos hu] + exact hmain + · have hfield : + ((u - BabyBear.fieldSize : Nat) : BabyBear.Field) = (u : BabyBear.Field) := by + rw [Nat.cast_sub (Nat.le_of_not_gt hu)] + simp + have hmain : + ((u - BabyBear.fieldSize : Nat) : BabyBear.Field) = + (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by + rw [hfield] + calc + (u : BabyBear.Field) = + (u : BabyBear.Field) * (UInt32.size : BabyBear.Field) * + (UInt32.size : BabyBear.Field)⁻¹ := by + rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] + _ = (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by + rw [hcast_mul] + dsimp only [montgomeryReduceNat] + rw [if_neg hu] + exact hmain + +theorem montgomeryQuotient_cast (x : Nat) : + (let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size + let u := (x + m * BabyBear.fieldSize) / UInt32.size + (u : BabyBear.Field)) = + (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by + let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size + let u := (x + m * BabyBear.fieldSize) / UInt32.size + have hmr := montgomeryReduceNat_cast x + unfold montgomeryReduceNat at hmr + change (u : BabyBear.Field) = + (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + by_cases hu : u < BabyBear.fieldSize + · simpa only [m, u, hu, if_true] using hmr + · have hfield : ((u - BabyBear.fieldSize : Nat) : BabyBear.Field) = + (u : BabyBear.Field) := by + rw [Nat.cast_sub (Nat.le_of_not_gt hu)] + simp + rw [← hfield] + simpa only [m, u, hu, if_false] using hmr + +theorem montgomery_u_eq_nat (x : UInt64) + (h : x.toNat < BabyBear.fieldSize * UInt32.size) : + (let m : UInt32 := x.toUInt32 * montgomeryNegInv + (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) = + (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * + BabyBear.fieldSize) / UInt32.size := by + simp only [UInt64.toNat_shiftRight, UInt64.toNat_toUInt32, UInt64.toNat_add, + UInt64.toNat_mul, UInt32.toNat_toUInt64, UInt32.toNat_mul, UInt64.toNat_ofNat, + modulus64_toNat, Nat.shiftRight_eq_div_pow] + norm_num [BabyBear.fieldSize, UInt32.size] at h ⊢ + let mNat := x.toNat * montgomeryNegInv.toNat % 4294967296 + have hm_lt : mNat < 4294967296 := Nat.mod_lt _ (by decide) + have hsum_lt : x.toNat + mNat * 2013265921 < 18446744073709551616 := by + have hprod_lt : mNat * 2013265921 < 2013265921 * 4294967296 := by nlinarith + nlinarith + change ((x.toNat + mNat * 2013265921) % 18446744073709551616 / 4294967296) % + 4294967296 = (x.toNat + mNat * 2013265921) / 4294967296 + rw [Nat.mod_eq_of_lt hsum_lt] + rw [Nat.mod_eq_of_lt] + rw [Nat.div_lt_iff_lt_mul] + · exact hsum_lt + · decide + +theorem montgomery_u_lt_two_fieldSize (x : UInt64) + (h : x.toNat < BabyBear.fieldSize * UInt32.size) : + (let m : UInt32 := x.toUInt32 * montgomeryNegInv + (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) < + 2 * BabyBear.fieldSize := by + rw [montgomery_u_eq_nat x h] + let mNat := x.toNat % UInt32.size * montgomeryNegInv.toNat % UInt32.size + have hm_lt : mNat < UInt32.size := Nat.mod_lt _ (by decide) + rw [Nat.div_lt_iff_lt_mul] + · have hprod_lt : mNat * BabyBear.fieldSize < UInt32.size * BabyBear.fieldSize := by + exact Nat.mul_lt_mul_of_pos_right hm_lt fieldSize_pos + have hprod_lt' : + mNat * BabyBear.fieldSize < BabyBear.fieldSize * UInt32.size := by + simpa [Nat.mul_comm] using hprod_lt + change x.toNat + mNat * BabyBear.fieldSize < 2 * BabyBear.fieldSize * UInt32.size + nlinarith + · decide + +/-- Reduce a native word known to be below twice the BabyBear prime. -/ +@[inline] +def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := + if x < modulus then x else x - modulus + +theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) + (h : x.toNat < 2 * BabyBear.fieldSize) : + (reduceUInt32Lt2ModulusRaw x).toNat < BabyBear.fieldSize := by + unfold reduceUInt32Lt2ModulusRaw + by_cases hx : x < modulus + · rw [if_pos hx] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact hx + · rw [if_neg hx] + have hmod_le_x : modulus ≤ x := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact Nat.le_of_not_gt hx + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] + omega + +/-- Reduce a native word known to be below twice the BabyBear prime. -/ +@[inline] +def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : + Field := + ⟨reduceUInt32Lt2ModulusRaw x, reduceUInt32Lt2ModulusRaw_lt x h⟩ + +theorem reduceUInt32Lt2Modulus_cast (x : UInt32) + (h : x.toNat < 2 * BabyBear.fieldSize) : + ((reduceUInt32Lt2Modulus x h).val.toNat : BabyBear.Field) = + (x.toNat : BabyBear.Field) := by + change ((reduceUInt32Lt2ModulusRaw x).toNat : BabyBear.Field) = + (x.toNat : BabyBear.Field) + unfold reduceUInt32Lt2ModulusRaw + by_cases hx : x < modulus + · rw [if_pos hx] + · have hmod_le_x : modulus ≤ x := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact Nat.le_of_not_gt hx + rw [if_neg hx] + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le, modulus_toNat] at hmod_le_x + exact hmod_le_x)] + simp + +/-- Reduce a native word below `2^32` modulo the BabyBear prime. -/ +@[inline] +def reduceUInt32 (x : UInt32) : Field := + if hx : x < modulus then + ⟨x, by + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact hx⟩ + else + let y := x - modulus + if hy : y < modulus then + ⟨y, by + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy + exact hy⟩ + else + ⟨y - modulus, by + have hmod_le_x : modulus ≤ x := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact Nat.le_of_not_gt hx + have hy_eq : y.toNat = x.toNat - BabyBear.fieldSize := by + change (x - modulus).toNat = x.toNat - BabyBear.fieldSize + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] + have hmod_le_y : modulus ≤ y := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy + exact Nat.le_of_not_gt hy + rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, modulus_toNat, hy_eq] + have hx_lt := UInt32.toNat_lt_size x + have hthree := uint32Size_lt_three_fieldSize + omega⟩ + +/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ +@[inline] +def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := + let m : UInt32 := x.toUInt32 * montgomeryNegInv + let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 + reduceUInt32Lt2ModulusRaw u + +theorem montgomeryReduceBoundedRaw_lt (x : UInt64) + (h : x.toNat < BabyBear.fieldSize * UInt32.size) : + (montgomeryReduceBoundedRaw x).toNat < BabyBear.fieldSize := by + unfold montgomeryReduceBoundedRaw + exact reduceUInt32Lt2ModulusRaw_lt _ + (montgomery_u_lt_two_fieldSize x h) + +/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ +@[inline] +def montgomeryReduceBounded (x : UInt64) + (h : x.toNat < BabyBear.fieldSize * UInt32.size) : Field := + ⟨montgomeryReduceBoundedRaw x, montgomeryReduceBoundedRaw_lt x h⟩ + +theorem montgomeryReduceBounded_cast (x : UInt64) + (h : x.toNat < BabyBear.fieldSize * UInt32.size) : + ((montgomeryReduceBounded x h).val.toNat : BabyBear.Field) = + (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by + change ((montgomeryReduceBoundedRaw x).toNat : BabyBear.Field) = + (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + unfold montgomeryReduceBoundedRaw + let m : UInt32 := x.toUInt32 * montgomeryNegInv + let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 + change ((reduceUInt32Lt2ModulusRaw u).toNat : BabyBear.Field) = + (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + have hred := reduceUInt32Lt2Modulus_cast u (montgomery_u_lt_two_fieldSize x h) + change ((reduceUInt32Lt2ModulusRaw u).toNat : BabyBear.Field) = + (u.toNat : BabyBear.Field) at hred + rw [hred] + change (u.toNat : BabyBear.Field) = + (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + rw [show u.toNat = + (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * + BabyBear.fieldSize) / UInt32.size by + exact montgomery_u_eq_nat x h] + exact montgomeryQuotient_cast x.toNat + +/-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ +@[inline] +def montgomeryReduce (x : UInt64) : Field := + let m : UInt32 := x.toUInt32 * montgomeryNegInv + let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 + reduceUInt32 u + +end Fast +end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Prelude.lean new file mode 100644 index 00000000..88e8f3f4 --- /dev/null +++ b/CompPoly/Fields/BabyBear/Fast/Prelude.lean @@ -0,0 +1,81 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin +-/ + +import CompPoly.Fields.BabyBear.Basic + +/-! +# Fast BabyBear Field — Basics + +The native-word modulus and the `Field` carrier type for the fast BabyBear field, +plus the elementary numeric bounds reused throughout the Montgomery reduction layer. + +The Montgomery-form constants and reduction machinery live in +`CompPoly.Fields.BabyBear.Fast.Montgomery`; conversions in `...Fast.Convert`; the +field operations and instances in `CompPoly.Fields.BabyBear.Fast`. +-/ + +namespace BabyBear +namespace Fast + +/-- BabyBear modulus as a native word. -/ +def modulus : UInt32 := 2013265921 + +/-- BabyBear modulus as a 64-bit word for modular reduction. -/ +def modulus64 : UInt64 := 2013265921 + +/-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ +abbrev Field : Type := { x : UInt32 // x.toNat < BabyBear.fieldSize } + +instance : DecidableEq Field := inferInstance + +/-- The raw Montgomery word backing a fast BabyBear element. -/ +@[inline] +def raw (x : Field) : UInt32 := x.val + +/-- The native `UInt32` modulus agrees with the mathematical BabyBear modulus. -/ +@[simp] theorem modulus_toNat : modulus.toNat = BabyBear.fieldSize := by + decide + +/-- The native `UInt64` modulus agrees with the mathematical BabyBear modulus. -/ +@[simp] theorem modulus64_toNat : modulus64.toNat = BabyBear.fieldSize := by + decide + +theorem fieldSize_pos : 0 < BabyBear.fieldSize := by + decide + +theorem fieldSize_lt_uint32Size : BabyBear.fieldSize < UInt32.size := by + decide + +theorem fieldSize_add_fieldSize_lt_two64 : + BabyBear.fieldSize + BabyBear.fieldSize < 2 ^ 64 := by + decide + +theorem fieldSize_add_fieldSize_lt_uint32Size : + BabyBear.fieldSize + BabyBear.fieldSize < UInt32.size := by + decide + +theorem fieldSize_mul_fieldSize_lt_two64 : + BabyBear.fieldSize * BabyBear.fieldSize < 2 ^ 64 := by + decide + +theorem uint32Size_lt_three_fieldSize : + UInt32.size < BabyBear.fieldSize + BabyBear.fieldSize + BabyBear.fieldSize := by + decide + +theorem fieldSize_mul_uint32Size_lt_two64 : + BabyBear.fieldSize * UInt32.size < 2 ^ 64 := by + decide + +theorem two_fieldSize_mul_uint32Size_lt_two64 : + 2 * BabyBear.fieldSize * UInt32.size < 2 ^ 64 := by + decide + +theorem uint32Size_ne_zero_in_field : + (UInt32.size : BabyBear.Field) ≠ 0 := by + decide + +end Fast +end BabyBear From c7198bbd44c5d8fda26e9d6afdde1baca8001238 Mon Sep 17 00:00:00 2001 From: Georgios Raikos <57717352+graikos@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:44:54 +0000 Subject: [PATCH 02/31] feat(fields): minor changes and test --- CompPoly/Fields/BabyBear/Fast.lean | 2 +- CompPoly/Fields/BabyBear/Fast/Convert.lean | 2 +- CompPoly/Fields/BabyBear/Fast/Montgomery.lean | 6 +-- CompPoly/Fields/BabyBear/Fast/Prelude.lean | 6 +-- tests/CompPolyTests.lean | 1 + tests/CompPolyTests/Fields/BabyBear/Fast.lean | 41 +++++++++++++++++++ 6 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 tests/CompPolyTests/Fields/BabyBear/Fast.lean diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index 4671cc56..58dc2839 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2026 CompPoly Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin +Authors: Valerii Huhnin, Georgios Raikos -/ import CompPoly.Fields.BabyBear.Fast.Convert diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean index f3b6e96d..df3c2daf 100644 --- a/CompPoly/Fields/BabyBear/Fast/Convert.lean +++ b/CompPoly/Fields/BabyBear/Fast/Convert.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2026 CompPoly Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin +Authors: Valerii Huhnin, Georgios Raikos -/ import CompPoly.Fields.BabyBear.Fast.Montgomery diff --git a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean index cd4b1151..cfadc39e 100644 --- a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean +++ b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2026 CompPoly Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin +Authors: Valerii Huhnin, Georgios Raikos -/ import CompPoly.Fields.BabyBear.Fast.Prelude @@ -20,10 +20,10 @@ namespace BabyBear namespace Fast /-- `2^32 mod BabyBear.fieldSize`. This is the Montgomery representation of one. -/ -def rModModulus : UInt32 := 268435454 +def rModModulus : UInt32 := 0x0FFFFFFE /-- `(2^32)^2 mod BabyBear.fieldSize`, used to enter Montgomery representation. -/ -def r2ModModulus : UInt32 := 1172168163 +def r2ModModulus : UInt32 := 0x45DDDDE3 /-- `-BabyBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ def montgomeryNegInv : UInt32 := 0x77FFFFFF diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Prelude.lean index 88e8f3f4..3079201f 100644 --- a/CompPoly/Fields/BabyBear/Fast/Prelude.lean +++ b/CompPoly/Fields/BabyBear/Fast/Prelude.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2026 CompPoly Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin +Authors: Valerii Huhnin, Georgios Raikos -/ import CompPoly.Fields.BabyBear.Basic @@ -21,10 +21,10 @@ namespace BabyBear namespace Fast /-- BabyBear modulus as a native word. -/ -def modulus : UInt32 := 2013265921 +def modulus : UInt32 := 0x78000001 /-- BabyBear modulus as a 64-bit word for modular reduction. -/ -def modulus64 : UInt64 := 2013265921 +def modulus64 : UInt64 := 0x78000001 /-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ abbrev Field : Type := { x : UInt32 // x.toNat < BabyBear.fieldSize } diff --git a/tests/CompPolyTests.lean b/tests/CompPolyTests.lean index 0321cad5..1c627ad7 100644 --- a/tests/CompPolyTests.lean +++ b/tests/CompPolyTests.lean @@ -21,6 +21,7 @@ import CompPolyTests.Bivariate.GuruswamiSudan.Root.RothRuckenstein import CompPolyTests.Bivariate.Multiplicity import CompPolyTests.Bivariate.WeightedDegree import CompPolyTests.Data.MvPolynomial.Notation +import CompPolyTests.Fields.BabyBear.Fast import CompPolyTests.Fields.Binary.AdditiveNTT.NovelPolynomialBasis import CompPolyTests.Fields.Binary.BF128Ghash.Prelude import CompPolyTests.Fields.KoalaBear.Fast diff --git a/tests/CompPolyTests/Fields/BabyBear/Fast.lean b/tests/CompPolyTests/Fields/BabyBear/Fast.lean new file mode 100644 index 00000000..02b25071 --- /dev/null +++ b/tests/CompPolyTests/Fields/BabyBear/Fast.lean @@ -0,0 +1,41 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin, Georgios Raikos +-/ + +import CompPoly.Fields.BabyBear.Fast + +/-! +# Fast BabyBear Field Tests + +Regression checks for the executable Montgomery representation. +-/ + +namespace BabyBear.Fast + +#guard raw (0 : Field) = 0 +#guard raw (1 : Field) = rModModulus +#guard toCanonicalUInt32 (ofNat 37) = (37 : UInt32) +#guard toNat (ofNat BabyBear.fieldSize) = 0 +#guard toNat (ofNat (BabyBear.fieldSize + 37)) = 37 +#guard toNat ((ofNat (BabyBear.fieldSize - 1)) + (2 : Field)) = 1 +#guard toNat ((ofNat (BabyBear.fieldSize - 1)) + (ofNat (BabyBear.fieldSize - 1))) = + BabyBear.fieldSize - 2 +#guard toNat ((9 : Field) - (5 : Field)) = 4 +#guard toNat ((5 : Field) - (9 : Field)) = BabyBear.fieldSize - 4 +#guard toNat (-(0 : Field)) = 0 +#guard toNat (-(1 : Field)) = BabyBear.fieldSize - 1 +#guard toNat ((ofNat (BabyBear.fieldSize - 1)) * (ofNat (BabyBear.fieldSize - 1))) = 1 +#guard toNat (square (12345 : Field)) = 152399025 +#guard toNat ((37 : Field) ^ 0) = 1 +#guard toNat ((37 : Field) ^ 1) = 37 +#guard toField ((123456789 : Field) ^ 17) = ((123456789 : BabyBear.Field) ^ 17) +#guard toField ((123456789 : Field) ^ 255) = ((123456789 : BabyBear.Field) ^ 255) +#guard toNat ((0 : Field)⁻¹) = 0 +#guard toNat ((37 : Field)⁻¹ * (37 : Field)) = 1 +#guard toNat ((37 : Field) / (37 : Field)) = 1 +#guard toField ((37 : Field)⁻¹) = ((37 : BabyBear.Field)⁻¹) +#guard toField ((37 : Field) ^ (-3 : Int)) = ((37 : BabyBear.Field) ^ (-3 : Int)) + +end BabyBear.Fast From c0607d8273037a74d5ec3545d1ab54e483464581 Mon Sep 17 00:00:00 2001 From: Georgios Raikos <57717352+graikos@users.noreply.github.com> Date: Tue, 23 Jun 2026 08:18:34 +0000 Subject: [PATCH 03/31] feat(fields): Split KoalaBear fast impl --- CompPoly.lean | 3 + CompPoly/Fields/KoalaBear/Fast.lean | 659 +----------------- CompPoly/Fields/KoalaBear/Fast/Convert.lean | 251 +++++++ .../Fields/KoalaBear/Fast/Montgomery.lean | 322 +++++++++ CompPoly/Fields/KoalaBear/Fast/Prelude.lean | 81 +++ 5 files changed, 664 insertions(+), 652 deletions(-) create mode 100644 CompPoly/Fields/KoalaBear/Fast/Convert.lean create mode 100644 CompPoly/Fields/KoalaBear/Fast/Montgomery.lean create mode 100644 CompPoly/Fields/KoalaBear/Fast/Prelude.lean diff --git a/CompPoly.lean b/CompPoly.lean index 385fc1b5..65cfe53a 100644 --- a/CompPoly.lean +++ b/CompPoly.lean @@ -107,6 +107,9 @@ import CompPoly.Fields.Goldilocks import CompPoly.Fields.KoalaBear import CompPoly.Fields.KoalaBear.Basic import CompPoly.Fields.KoalaBear.Fast +import CompPoly.Fields.KoalaBear.Fast.Convert +import CompPoly.Fields.KoalaBear.Fast.Montgomery +import CompPoly.Fields.KoalaBear.Fast.Prelude import CompPoly.Fields.Mersenne import CompPoly.Fields.PrattCertificate import CompPoly.Fields.Secp256k1 diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index e35308d0..a4259175 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -4,9 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Valerii Huhnin -/ -import CompPoly.Fields.KoalaBear.Basic +import CompPoly.Fields.KoalaBear.Fast.Convert import Mathlib.Algebra.Field.TransferInstance -import Mathlib.Data.Nat.ModEq /-! # Fast KoalaBear Field Operations @@ -18,661 +17,17 @@ Fast field values are stored as Montgomery `UInt32` residues below `KoalaBear.fi representing `x * 2^32` modulo the KoalaBear prime. Addition, subtraction, and negation operate directly on Montgomery words; multiplication uses Montgomery reduction on a native `UInt64` product. + +Supporting layers live in sibling modules: field basics in +`CompPoly.Fields.KoalaBear.Fast.Prelude`, Montgomery reduction in +`CompPoly.Fields.KoalaBear.Fast.Montgomery`, and conversions/casts in +`CompPoly.Fields.KoalaBear.Fast.Convert`. This module defines the field operations, +their instances, and the `toField` bridge to `KoalaBear.Field`. -/ namespace KoalaBear namespace Fast -/-- KoalaBear modulus as a native word. -/ -def modulus : UInt32 := 0x7F000001 - -/-- KoalaBear modulus as a 64-bit word for modular reduction. -/ -def modulus64 : UInt64 := 0x7F000001 - -/-- `2^32 mod KoalaBear.fieldSize`. This is the Montgomery representation of one. -/ -def rModModulus : UInt32 := 0x01FFFFFE - -/-- `(2^32)^2 mod KoalaBear.fieldSize`, used to enter Montgomery representation. -/ -def r2ModModulus : UInt32 := 0x17F7EFE4 - -/-- `-KoalaBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ -def montgomeryNegInv : UInt32 := 0x7EFFFFFF - -/-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ -abbrev Field : Type := { x : UInt32 // x.toNat < KoalaBear.fieldSize } - -instance : DecidableEq Field := inferInstance - -/-- The raw Montgomery word backing a fast KoalaBear element. -/ -@[inline] -def raw (x : Field) : UInt32 := x.val - -/-- The native `UInt32` modulus agrees with the mathematical KoalaBear modulus. -/ -@[simp] theorem modulus_toNat : modulus.toNat = KoalaBear.fieldSize := by - decide - -/-- The native `UInt64` modulus agrees with the mathematical KoalaBear modulus. -/ -@[simp] theorem modulus64_toNat : modulus64.toNat = KoalaBear.fieldSize := by - decide - -private theorem fieldSize_pos : 0 < KoalaBear.fieldSize := by - decide - -private theorem fieldSize_lt_uint32Size : KoalaBear.fieldSize < UInt32.size := by - decide - -private theorem fieldSize_add_fieldSize_lt_two64 : - KoalaBear.fieldSize + KoalaBear.fieldSize < 2 ^ 64 := by - decide - -private theorem fieldSize_add_fieldSize_lt_uint32Size : - KoalaBear.fieldSize + KoalaBear.fieldSize < UInt32.size := by - decide - -private theorem fieldSize_mul_fieldSize_lt_two64 : - KoalaBear.fieldSize * KoalaBear.fieldSize < 2 ^ 64 := by - decide - -private theorem uint32Size_lt_three_fieldSize : - UInt32.size < KoalaBear.fieldSize + KoalaBear.fieldSize + KoalaBear.fieldSize := by - decide - -private theorem fieldSize_mul_uint32Size_lt_two64 : - KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by - decide - -private theorem two_fieldSize_mul_uint32Size_lt_two64 : - 2 * KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by - decide - -private theorem r2ModModulus_lt_fieldSize : r2ModModulus.toNat < KoalaBear.fieldSize := by - decide - -private theorem rModModulus_lt_fieldSize : rModModulus.toNat < KoalaBear.fieldSize := by - decide - -private theorem rModModulus_cast : - (rModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) := by - decide - -private theorem r2ModModulus_cast : - (r2ModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) ^ 2 := by - decide - -private theorem uint32Size_ne_zero_in_field : - (UInt32.size : KoalaBear.Field) ≠ 0 := by - decide - -private theorem montgomery_sum_dvd (x : Nat) : - UInt32.size ∣ - x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize := by - rw [← Nat.modEq_zero_iff_dvd] - have hx : x ≡ x % UInt32.size [MOD UInt32.size] := - (Nat.mod_modEq x UInt32.size).symm - have hm : - ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize ≡ - (x % UInt32.size) * (UInt32.size - 1) [MOD UInt32.size] := by - have hmi : - (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size ≡ - x % UInt32.size * montgomeryNegInv.toNat [MOD UInt32.size] := - Nat.mod_modEq _ _ - have hp : montgomeryNegInv.toNat * KoalaBear.fieldSize ≡ UInt32.size - 1 - [MOD UInt32.size] := by - decide - calc - ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize - ≡ (x % UInt32.size * montgomeryNegInv.toNat) * KoalaBear.fieldSize - [MOD UInt32.size] := hmi.mul_right _ - _ = x % UInt32.size * (montgomeryNegInv.toNat * KoalaBear.fieldSize) := by ring - _ ≡ x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := hp.mul_left _ - calc - x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize - ≡ x % UInt32.size + x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := - hx.add hm - _ = x % UInt32.size * UInt32.size := by - rw [add_comm, ← Nat.mul_succ] - have hsucc : (UInt32.size - 1).succ = UInt32.size := by decide - rw [hsucc] - _ ≡ 0 [MOD UInt32.size] := by - rw [Nat.modEq_zero_iff_dvd] - exact ⟨x % UInt32.size, by rw [mul_comm]⟩ - -/-- Nat-level Montgomery reduction used to specify and prove the native-word reducer. -/ -private def montgomeryReduceNat (x : Nat) : Nat := - let m := (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size - let u := (x + m * KoalaBear.fieldSize) / UInt32.size - if u < KoalaBear.fieldSize then u else u - KoalaBear.fieldSize - -private theorem montgomeryReduceNat_lt (x : Nat) - (h : x < KoalaBear.fieldSize * UInt32.size) : - montgomeryReduceNat x < KoalaBear.fieldSize := by - let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * KoalaBear.fieldSize) / UInt32.size - have hm_lt : m < UInt32.size := Nat.mod_lt _ (by decide) - have hu_lt : u < 2 * KoalaBear.fieldSize := by - rw [Nat.div_lt_iff_lt_mul] - · have hprod_lt : m * KoalaBear.fieldSize < UInt32.size * KoalaBear.fieldSize := by - exact Nat.mul_lt_mul_of_pos_right hm_lt fieldSize_pos - have hprod_lt' : m * KoalaBear.fieldSize < KoalaBear.fieldSize * UInt32.size := by - simpa [Nat.mul_comm] using hprod_lt - nlinarith - · decide - by_cases hu : u < KoalaBear.fieldSize - · unfold montgomeryReduceNat - change (if u < KoalaBear.fieldSize then u else u - KoalaBear.fieldSize) < - KoalaBear.fieldSize - rw [if_pos hu] - exact hu - · have hsub : u - KoalaBear.fieldSize < KoalaBear.fieldSize := by omega - unfold montgomeryReduceNat - change (if u < KoalaBear.fieldSize then u else u - KoalaBear.fieldSize) < - KoalaBear.fieldSize - rw [if_neg hu] - exact hsub - -private theorem montgomeryReduceNat_cast (x : Nat) : - (montgomeryReduceNat x : KoalaBear.Field) = - (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * KoalaBear.fieldSize) / UInt32.size - have hdiv : UInt32.size ∣ x + m * KoalaBear.fieldSize := by - simpa [m] using montgomery_sum_dvd x - have hu_mul : u * UInt32.size = x + m * KoalaBear.fieldSize := by - exact Nat.div_mul_cancel hdiv - have hcast_mul : (u : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) = - (x : KoalaBear.Field) := by - rw [← Nat.cast_mul, hu_mul, Nat.cast_add, Nat.cast_mul] - simp - have hR := uint32Size_ne_zero_in_field - by_cases hu : u < KoalaBear.fieldSize - · have hmain : - (u : KoalaBear.Field) = (x : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ := by - calc - (u : KoalaBear.Field) = - (u : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] - _ = (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [hcast_mul] - dsimp only [montgomeryReduceNat] - rw [if_pos hu] - exact hmain - · have hfield : - ((u - KoalaBear.fieldSize : Nat) : KoalaBear.Field) = (u : KoalaBear.Field) := by - rw [Nat.cast_sub (Nat.le_of_not_gt hu)] - simp - have hmain : - ((u - KoalaBear.fieldSize : Nat) : KoalaBear.Field) = - (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [hfield] - calc - (u : KoalaBear.Field) = - (u : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] - _ = (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [hcast_mul] - dsimp only [montgomeryReduceNat] - rw [if_neg hu] - exact hmain - -private theorem montgomeryQuotient_cast (x : Nat) : - (let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * KoalaBear.fieldSize) / UInt32.size - (u : KoalaBear.Field)) = - (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * KoalaBear.fieldSize) / UInt32.size - have hmr := montgomeryReduceNat_cast x - unfold montgomeryReduceNat at hmr - change (u : KoalaBear.Field) = - (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - by_cases hu : u < KoalaBear.fieldSize - · simpa only [m, u, hu, if_true] using hmr - · have hfield : ((u - KoalaBear.fieldSize : Nat) : KoalaBear.Field) = - (u : KoalaBear.Field) := by - rw [Nat.cast_sub (Nat.le_of_not_gt hu)] - simp - rw [← hfield] - simpa only [m, u, hu, if_false] using hmr - -private theorem montgomery_u_eq_nat (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - (let m : UInt32 := x.toUInt32 * montgomeryNegInv - (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) = - (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * - KoalaBear.fieldSize) / UInt32.size := by - simp only [UInt64.toNat_shiftRight, UInt64.toNat_toUInt32, UInt64.toNat_add, - UInt64.toNat_mul, UInt32.toNat_toUInt64, UInt32.toNat_mul, UInt64.toNat_ofNat, - modulus64_toNat, Nat.shiftRight_eq_div_pow] - norm_num [KoalaBear.fieldSize, UInt32.size] at h ⊢ - let mNat := x.toNat * montgomeryNegInv.toNat % 4294967296 - have hm_lt : mNat < 4294967296 := Nat.mod_lt _ (by decide) - have hsum_lt : x.toNat + mNat * 2130706433 < 18446744073709551616 := by - have hprod_lt : mNat * 2130706433 < 2130706433 * 4294967296 := by nlinarith - nlinarith - change ((x.toNat + mNat * 2130706433) % 18446744073709551616 / 4294967296) % - 4294967296 = (x.toNat + mNat * 2130706433) / 4294967296 - rw [Nat.mod_eq_of_lt hsum_lt] - rw [Nat.mod_eq_of_lt] - rw [Nat.div_lt_iff_lt_mul] - · exact hsum_lt - · decide - -private theorem montgomery_u_lt_two_fieldSize (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - (let m : UInt32 := x.toUInt32 * montgomeryNegInv - (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) < - 2 * KoalaBear.fieldSize := by - rw [montgomery_u_eq_nat x h] - let mNat := x.toNat % UInt32.size * montgomeryNegInv.toNat % UInt32.size - have hm_lt : mNat < UInt32.size := Nat.mod_lt _ (by decide) - rw [Nat.div_lt_iff_lt_mul] - · have hprod_lt : mNat * KoalaBear.fieldSize < UInt32.size * KoalaBear.fieldSize := by - exact Nat.mul_lt_mul_of_pos_right hm_lt fieldSize_pos - have hprod_lt' : - mNat * KoalaBear.fieldSize < KoalaBear.fieldSize * UInt32.size := by - simpa [Nat.mul_comm] using hprod_lt - change x.toNat + mNat * KoalaBear.fieldSize < 2 * KoalaBear.fieldSize * UInt32.size - nlinarith - · decide - -/-- Reduce a native word known to be below twice the KoalaBear prime. -/ -@[inline] -private def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := - if x < modulus then x else x - modulus - -private theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) - (h : x.toNat < 2 * KoalaBear.fieldSize) : - (reduceUInt32Lt2ModulusRaw x).toNat < KoalaBear.fieldSize := by - unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < modulus - · rw [if_pos hx] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx - · rw [if_neg hx] - have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - omega - -/-- Reduce a native word known to be below twice the KoalaBear prime. -/ -@[inline] -private def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : - Field := - ⟨reduceUInt32Lt2ModulusRaw x, reduceUInt32Lt2ModulusRaw_lt x h⟩ - -private theorem reduceUInt32Lt2Modulus_cast (x : UInt32) - (h : x.toNat < 2 * KoalaBear.fieldSize) : - ((reduceUInt32Lt2Modulus x h).val.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) := by - change ((reduceUInt32Lt2ModulusRaw x).toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) - unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < modulus - · rw [if_pos hx] - · have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - rw [if_neg hx] - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, modulus_toNat] at hmod_le_x - exact hmod_le_x)] - simp - -private theorem reduceUInt32Lt2Modulus_val_eq_nat (x : UInt32) - (h : x.toNat < 2 * KoalaBear.fieldSize) : - (reduceUInt32Lt2Modulus x h).val.toNat = montgomeryReduceNat (x.toNat * UInt32.size) := by - change (reduceUInt32Lt2ModulusRaw x).toNat = montgomeryReduceNat (x.toNat * UInt32.size) - unfold reduceUInt32Lt2ModulusRaw montgomeryReduceNat - have hm_zero : - (x.toNat * UInt32.size % UInt32.size * montgomeryNegInv.toNat) % UInt32.size = 0 := by - simp - rw [hm_zero] - simp only [zero_mul, add_zero] - have hdiv : x.toNat * UInt32.size / UInt32.size = x.toNat := by - rw [Nat.mul_div_cancel _ (by decide : 0 < UInt32.size)] - rw [hdiv] - by_cases hx : x < modulus - · rw [if_pos] - · have hxNat : x.toNat < KoalaBear.fieldSize := by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx - rw [if_pos hxNat] - · have hxNat : x.toNat < KoalaBear.fieldSize := by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx - exact hxNat - · rw [if_neg] - · have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - have hxNat : ¬x.toNat < KoalaBear.fieldSize := by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx - rw [if_neg hxNat] - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - · have hxNat : ¬x.toNat < KoalaBear.fieldSize := by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx - exact hxNat - -/-- Reduce a native word below `2^32` modulo the KoalaBear prime. -/ -@[inline] -private def reduceUInt32 (x : UInt32) : Field := - if hx : x < modulus then - ⟨x, by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx⟩ - else - let y := x - modulus - if hy : y < modulus then - ⟨y, by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy - exact hy⟩ - else - ⟨y - modulus, by - have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - have hy_eq : y.toNat = x.toNat - KoalaBear.fieldSize := by - change (x - modulus).toNat = x.toNat - KoalaBear.fieldSize - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - have hmod_le_y : modulus ≤ y := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy - exact Nat.le_of_not_gt hy - rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, modulus_toNat, hy_eq] - have hx_lt := UInt32.toNat_lt_size x - have hthree := uint32Size_lt_three_fieldSize - omega⟩ - -/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ -@[inline] -private def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := - let m : UInt32 := x.toUInt32 * montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 - reduceUInt32Lt2ModulusRaw u - -private theorem montgomeryReduceBoundedRaw_lt (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - (montgomeryReduceBoundedRaw x).toNat < KoalaBear.fieldSize := by - unfold montgomeryReduceBoundedRaw - exact reduceUInt32Lt2ModulusRaw_lt _ - (montgomery_u_lt_two_fieldSize x h) - -/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ -@[inline] -private def montgomeryReduceBounded (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : Field := - ⟨montgomeryReduceBoundedRaw x, montgomeryReduceBoundedRaw_lt x h⟩ - -private theorem montgomeryReduceBounded_cast (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - ((montgomeryReduceBounded x h).val.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - change ((montgomeryReduceBoundedRaw x).toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - unfold montgomeryReduceBoundedRaw - let m : UInt32 := x.toUInt32 * montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 - change ((reduceUInt32Lt2ModulusRaw u).toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - have hred := reduceUInt32Lt2Modulus_cast u (montgomery_u_lt_two_fieldSize x h) - change ((reduceUInt32Lt2ModulusRaw u).toNat : KoalaBear.Field) = - (u.toNat : KoalaBear.Field) at hred - rw [hred] - change (u.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - rw [show u.toNat = - (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * - KoalaBear.fieldSize) / UInt32.size by - exact montgomery_u_eq_nat x h] - exact montgomeryQuotient_cast x.toNat - -/-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ -@[inline] -def montgomeryReduce (x : UInt64) : Field := - let m : UInt32 := x.toUInt32 * montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 - reduceUInt32 u - -/-- Build a fast element from a canonical natural representative. -/ -@[inline] -def ofCanonicalNat (n : Nat) (_h : n < KoalaBear.fieldSize) : Field := - montgomeryReduceBounded (UInt64.ofNat n * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans _h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) - rw [hnmod] - have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) - -/-- Reduce a `UInt64` modulo the KoalaBear prime and return a Montgomery fast element. -/ -@[inline] -def reduceUInt64 (x : UInt64) : Field := - let y := x % modulus64 - montgomeryReduceBounded (y * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : (x % modulus64).toNat < KoalaBear.fieldSize := by - rw [UInt64.toNat_mod, modulus64_toNat] - exact Nat.mod_lt _ fieldSize_pos - have hprod : (x % modulus64).toNat * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) - -/-- The zero fast KoalaBear element. -/ -def zero : Field := ⟨0, by decide⟩ - -/-- The one fast KoalaBear element. -/ -def one : Field := ⟨rModModulus, by decide⟩ - -/-- Convert a natural number into fast Montgomery representation. -/ -@[inline] -def ofNat (n : Nat) : Field := - ofCanonicalNat (n % KoalaBear.fieldSize) (Nat.mod_lt _ fieldSize_pos) - -/-- Convert a 32-bit word into fast Montgomery representation. -/ -@[inline] -def ofUInt32 (x : UInt32) : Field := - reduceUInt64 x.toUInt64 - -/-- Convert from the canonical `ZMod` KoalaBear field into fast Montgomery form. -/ -@[inline] -def ofField (x : KoalaBear.Field) : Field := - ofCanonicalNat x.val (ZMod.val_lt x) - -/-- Convert an integer into fast Montgomery representation. -/ -@[inline] -def ofInt (n : Int) : Field := - ofField (n : KoalaBear.Field) - -/-- Convert a fast element to its canonical native-word representative. -/ -@[inline] -def toCanonicalUInt32 (x : Field) : UInt32 := - raw (montgomeryReduceBounded x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, fieldSize_pos])) - -/-- Convert a fast KoalaBear element to its canonical natural representative. -/ -@[inline] -def toNat (x : Field) : Nat := - (toCanonicalUInt32 x).toNat - -/-- Convert a fast KoalaBear element to the canonical `ZMod` KoalaBear field. -/ -@[inline] -def toField (x : Field) : KoalaBear.Field := - (toNat x : KoalaBear.Field) - -private theorem toNat_lt_fieldSize (x : Field) : toNat x < KoalaBear.fieldSize := by - unfold toNat toCanonicalUInt32 raw - change (montgomeryReduceBoundedRaw x.val.toUInt64).toNat < KoalaBear.fieldSize - exact montgomeryReduceBoundedRaw_lt x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, fieldSize_pos]) - -private theorem toField_eq_raw_mul_inv (x : Field) : - toField x = - (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - unfold toField toNat toCanonicalUInt32 raw - have hred := montgomeryReduceBounded_cast x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, fieldSize_pos]) - change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : KoalaBear.Field) = - (x.val.toUInt64.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : KoalaBear.Field) = - (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - rw [hred] - rw [UInt32.toNat_toUInt64] - -private theorem raw_cast_eq_toField_mul (x : Field) : - (x.val.toNat : KoalaBear.Field) = - toField x * (UInt32.size : KoalaBear.Field) := by - rw [toField_eq_raw_mul_inv] - rw [mul_assoc] - rw [inv_mul_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - -private theorem nat_eq_of_field_eq {a b : Nat} (ha : a < KoalaBear.fieldSize) - (hb : b < KoalaBear.fieldSize) (h : (a : KoalaBear.Field) = (b : KoalaBear.Field)) : - a = b := by - rw [ZMod.natCast_eq_natCast_iff] at h - exact Nat.ModEq.eq_of_lt_of_lt h ha hb - -private theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < KoalaBear.fieldSize) : - ((ofCanonicalNat n h).val.toNat : KoalaBear.Field) = - (n : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := by - unfold ofCanonicalNat - have hred := montgomeryReduceBounded_cast - (UInt64.ofNat n * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) - rw [hnmod] - have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) - change ((montgomeryReduceBoundedRaw - (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : KoalaBear.Field) = - ((UInt64.ofNat n * r2ModModulus.toUInt64).toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw - (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : KoalaBear.Field) = - (n : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) - rw [hred] - simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) - rw [hnmod] - have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - rw [Nat.cast_mul, r2ModModulus_cast] - rw [pow_two] - rw [mul_assoc (n : KoalaBear.Field) ((UInt32.size : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)) ((UInt32.size : KoalaBear.Field)⁻¹)] - rw [mul_assoc (UInt32.size : KoalaBear.Field) (UInt32.size : KoalaBear.Field) - ((UInt32.size : KoalaBear.Field)⁻¹)] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - -private theorem toField_ofCanonicalNat_aux (n : Nat) (h : n < KoalaBear.fieldSize) : - toField (ofCanonicalNat n h) = (n : KoalaBear.Field) := by - rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - -private theorem reduceUInt64_raw_cast (x : UInt64) : - ((reduceUInt64 x).val.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := by - unfold reduceUInt64 - let y := x % modulus64 - have hred := montgomeryReduceBounded_cast (y * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < KoalaBear.fieldSize := by - rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] - exact Nat.mod_lt _ fieldSize_pos - have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) - change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : - KoalaBear.Field) = - ((y * r2ModModulus.toUInt64).toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : - KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) - rw [hred] - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < KoalaBear.fieldSize := by - rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] - exact Nat.mod_lt _ fieldSize_pos - have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - have hy_cast : (y.toNat : KoalaBear.Field) = (x.toNat : KoalaBear.Field) := by - rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] - rw [ZMod.natCast_eq_natCast_iff] - exact Nat.mod_modEq _ _ - rw [Nat.cast_mul, r2ModModulus_cast, hy_cast] - rw [pow_two] - rw [mul_assoc (x.toNat : KoalaBear.Field) ((UInt32.size : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)) ((UInt32.size : KoalaBear.Field)⁻¹)] - rw [mul_assoc (UInt32.size : KoalaBear.Field) (UInt32.size : KoalaBear.Field) - ((UInt32.size : KoalaBear.Field)⁻¹)] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - -/-- Converting a canonical natural representative to fast form preserves its value. -/ -@[simp] -theorem toNat_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : - toNat (ofCanonicalNat n h) = n := by - exact nat_eq_of_field_eq (toNat_lt_fieldSize _) h (toField_ofCanonicalNat_aux n h) - -/-- `ofCanonicalNat` embeds a canonical representative into the canonical field. -/ -@[simp] -theorem toField_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : - toField (ofCanonicalNat n h) = (n : KoalaBear.Field) := by - exact toField_ofCanonicalNat_aux n h - -/-- Reducing a `UInt64` gives the canonical natural residue modulo KoalaBear. -/ -@[simp] -theorem toNat_reduceUInt64 (x : UInt64) : - toNat (reduceUInt64 x) = x.toNat % KoalaBear.fieldSize := by - apply nat_eq_of_field_eq (toNat_lt_fieldSize _) (Nat.mod_lt _ fieldSize_pos) - change toField (reduceUInt64 x) = ((x.toNat % KoalaBear.fieldSize : Nat) : KoalaBear.Field) - rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - rw [ZMod.natCast_eq_natCast_iff] - exact (Nat.mod_modEq _ _).symm - -/-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ -@[simp] -theorem toField_reduceUInt64 (x : UInt64) : - toField (reduceUInt64 x) = (x.toNat : KoalaBear.Field) := by - rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - /-- Fast modular addition in Montgomery form. -/ @[inline] def add (x y : Field) : Field := diff --git a/CompPoly/Fields/KoalaBear/Fast/Convert.lean b/CompPoly/Fields/KoalaBear/Fast/Convert.lean new file mode 100644 index 00000000..27e538cb --- /dev/null +++ b/CompPoly/Fields/KoalaBear/Fast/Convert.lean @@ -0,0 +1,251 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin +-/ + +import CompPoly.Fields.KoalaBear.Fast.Montgomery + +/-! +# Fast KoalaBear Field — Conversions + +Conversions between the fast Montgomery representation and the canonical +`KoalaBear.Field` / `Nat` views (`ofField`, `toField`, `ofNat`, `toNat`, …), the +`zero`/`one` constants, and the round-trip cast lemmas relating them. The field +operations and ring/field instances build on this module. +-/ + +namespace KoalaBear +namespace Fast + +/-- Build a fast element from a canonical natural representative. -/ +@[inline] +def ofCanonicalNat (n : Nat) (_h : n < KoalaBear.fieldSize) : Field := + montgomeryReduceBounded (UInt64.ofNat n * r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans _h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) + rw [hnmod] + have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [r2ModModulus_lt_fieldSize]) + +/-- Reduce a `UInt64` modulo the KoalaBear prime and return a Montgomery fast element. -/ +@[inline] +def reduceUInt64 (x : UInt64) : Field := + let y := x % modulus64 + montgomeryReduceBounded (y * r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hy_lt : (x % modulus64).toNat < KoalaBear.fieldSize := by + rw [UInt64.toNat_mod, modulus64_toNat] + exact Nat.mod_lt _ fieldSize_pos + have hprod : (x % modulus64).toNat * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [r2ModModulus_lt_fieldSize]) + +/-- The zero fast KoalaBear element. -/ +def zero : Field := ⟨0, by decide⟩ + +/-- The one fast KoalaBear element. -/ +def one : Field := ⟨rModModulus, by decide⟩ + +/-- Convert a natural number into fast Montgomery representation. -/ +@[inline] +def ofNat (n : Nat) : Field := + ofCanonicalNat (n % KoalaBear.fieldSize) (Nat.mod_lt _ fieldSize_pos) + +/-- Convert a 32-bit word into fast Montgomery representation. -/ +@[inline] +def ofUInt32 (x : UInt32) : Field := + reduceUInt64 x.toUInt64 + +/-- Convert from the canonical `ZMod` KoalaBear field into fast Montgomery form. -/ +@[inline] +def ofField (x : KoalaBear.Field) : Field := + ofCanonicalNat x.val (ZMod.val_lt x) + +/-- Convert an integer into fast Montgomery representation. -/ +@[inline] +def ofInt (n : Int) : Field := + ofField (n : KoalaBear.Field) + +/-- Convert a fast element to its canonical native-word representative. -/ +@[inline] +def toCanonicalUInt32 (x : Field) : UInt32 := + raw (montgomeryReduceBounded x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, fieldSize_pos])) + +/-- Convert a fast KoalaBear element to its canonical natural representative. -/ +@[inline] +def toNat (x : Field) : Nat := + (toCanonicalUInt32 x).toNat + +/-- Convert a fast KoalaBear element to the canonical `ZMod` KoalaBear field. -/ +@[inline] +def toField (x : Field) : KoalaBear.Field := + (toNat x : KoalaBear.Field) + +theorem toNat_lt_fieldSize (x : Field) : toNat x < KoalaBear.fieldSize := by + unfold toNat toCanonicalUInt32 raw + change (montgomeryReduceBoundedRaw x.val.toUInt64).toNat < KoalaBear.fieldSize + exact montgomeryReduceBoundedRaw_lt x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, fieldSize_pos]) + +theorem toField_eq_raw_mul_inv (x : Field) : + toField x = + (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by + unfold toField toNat toCanonicalUInt32 raw + have hred := montgomeryReduceBounded_cast x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, fieldSize_pos]) + change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : KoalaBear.Field) = + (x.val.toUInt64.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ at hred + change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : KoalaBear.Field) = + (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ + rw [hred] + rw [UInt32.toNat_toUInt64] + +theorem raw_cast_eq_toField_mul (x : Field) : + (x.val.toNat : KoalaBear.Field) = + toField x * (UInt32.size : KoalaBear.Field) := by + rw [toField_eq_raw_mul_inv] + rw [mul_assoc] + rw [inv_mul_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +theorem nat_eq_of_field_eq {a b : Nat} (ha : a < KoalaBear.fieldSize) + (hb : b < KoalaBear.fieldSize) (h : (a : KoalaBear.Field) = (b : KoalaBear.Field)) : + a = b := by + rw [ZMod.natCast_eq_natCast_iff] at h + exact Nat.ModEq.eq_of_lt_of_lt h ha hb + +theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < KoalaBear.fieldSize) : + ((ofCanonicalNat n h).val.toNat : KoalaBear.Field) = + (n : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := by + unfold ofCanonicalNat + have hred := montgomeryReduceBounded_cast + (UInt64.ofNat n * r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) + rw [hnmod] + have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [r2ModModulus_lt_fieldSize]) + change ((montgomeryReduceBoundedRaw + (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : KoalaBear.Field) = + ((UInt64.ofNat n * r2ModModulus.toUInt64).toNat : KoalaBear.Field) * + (UInt32.size : KoalaBear.Field)⁻¹ at hred + change ((montgomeryReduceBoundedRaw + (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : KoalaBear.Field) = + (n : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) + rw [hred] + simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) + rw [hnmod] + have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + rw [Nat.cast_mul, r2ModModulus_cast] + rw [pow_two] + rw [mul_assoc (n : KoalaBear.Field) ((UInt32.size : KoalaBear.Field) * + (UInt32.size : KoalaBear.Field)) ((UInt32.size : KoalaBear.Field)⁻¹)] + rw [mul_assoc (UInt32.size : KoalaBear.Field) (UInt32.size : KoalaBear.Field) + ((UInt32.size : KoalaBear.Field)⁻¹)] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +theorem toField_ofCanonicalNat_aux (n : Nat) (h : n < KoalaBear.fieldSize) : + toField (ofCanonicalNat n h) = (n : KoalaBear.Field) := by + rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +theorem reduceUInt64_raw_cast (x : UInt64) : + ((reduceUInt64 x).val.toNat : KoalaBear.Field) = + (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := by + unfold reduceUInt64 + let y := x % modulus64 + have hred := montgomeryReduceBounded_cast (y * r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hy_lt : y.toNat < KoalaBear.fieldSize := by + rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] + exact Nat.mod_lt _ fieldSize_pos + have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [r2ModModulus_lt_fieldSize]) + change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : + KoalaBear.Field) = + ((y * r2ModModulus.toUInt64).toNat : KoalaBear.Field) * + (UInt32.size : KoalaBear.Field)⁻¹ at hred + change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : + KoalaBear.Field) = + (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) + rw [hred] + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hy_lt : y.toNat < KoalaBear.fieldSize := by + rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] + exact Nat.mod_lt _ fieldSize_pos + have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + have hy_cast : (y.toNat : KoalaBear.Field) = (x.toNat : KoalaBear.Field) := by + rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] + rw [ZMod.natCast_eq_natCast_iff] + exact Nat.mod_modEq _ _ + rw [Nat.cast_mul, r2ModModulus_cast, hy_cast] + rw [pow_two] + rw [mul_assoc (x.toNat : KoalaBear.Field) ((UInt32.size : KoalaBear.Field) * + (UInt32.size : KoalaBear.Field)) ((UInt32.size : KoalaBear.Field)⁻¹)] + rw [mul_assoc (UInt32.size : KoalaBear.Field) (UInt32.size : KoalaBear.Field) + ((UInt32.size : KoalaBear.Field)⁻¹)] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +/-- Converting a canonical natural representative to fast form preserves its value. -/ +@[simp] +theorem toNat_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : + toNat (ofCanonicalNat n h) = n := by + exact nat_eq_of_field_eq (toNat_lt_fieldSize _) h (toField_ofCanonicalNat_aux n h) + +/-- `ofCanonicalNat` embeds a canonical representative into the canonical field. -/ +@[simp] +theorem toField_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : + toField (ofCanonicalNat n h) = (n : KoalaBear.Field) := by + exact toField_ofCanonicalNat_aux n h + +/-- Reducing a `UInt64` gives the canonical natural residue modulo KoalaBear. -/ +@[simp] +theorem toNat_reduceUInt64 (x : UInt64) : + toNat (reduceUInt64 x) = x.toNat % KoalaBear.fieldSize := by + apply nat_eq_of_field_eq (toNat_lt_fieldSize _) (Nat.mod_lt _ fieldSize_pos) + change toField (reduceUInt64 x) = ((x.toNat % KoalaBear.fieldSize : Nat) : KoalaBear.Field) + rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + rw [ZMod.natCast_eq_natCast_iff] + exact (Nat.mod_modEq _ _).symm + +/-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ +@[simp] +theorem toField_reduceUInt64 (x : UInt64) : + toField (reduceUInt64 x) = (x.toNat : KoalaBear.Field) := by + rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] + rw [mul_one] + +end Fast +end KoalaBear diff --git a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean new file mode 100644 index 00000000..3d3a7ab2 --- /dev/null +++ b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean @@ -0,0 +1,322 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin +-/ + +import CompPoly.Fields.KoalaBear.Fast.Prelude +import Mathlib.Data.Nat.ModEq + +/-! +# Fast KoalaBear Field — Montgomery Reduction + +Montgomery-form constants (`montgomeryNegInv`, `rModModulus`, `r2ModModulus`) and the +native-word Montgomery reduction used by the fast KoalaBear field. Field basics live +in `CompPoly.Fields.KoalaBear.Fast.Prelude`; conversions and field operations build +on this module. +-/ + +namespace KoalaBear +namespace Fast + +/-- `2^32 mod KoalaBear.fieldSize`. This is the Montgomery representation of one. -/ +def rModModulus : UInt32 := 0x01FFFFFE + +/-- `(2^32)^2 mod KoalaBear.fieldSize`, used to enter Montgomery representation. -/ +def r2ModModulus : UInt32 := 0x17F7EFE4 + +/-- `-KoalaBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ +def montgomeryNegInv : UInt32 := 0x7EFFFFFF + +theorem r2ModModulus_lt_fieldSize : r2ModModulus.toNat < KoalaBear.fieldSize := by + decide + +theorem rModModulus_lt_fieldSize : rModModulus.toNat < KoalaBear.fieldSize := by + decide + +theorem rModModulus_cast : + (rModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) := by + decide + +theorem r2ModModulus_cast : + (r2ModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) ^ 2 := by + decide + +theorem montgomery_sum_dvd (x : Nat) : + UInt32.size ∣ + x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize := by + rw [← Nat.modEq_zero_iff_dvd] + have hx : x ≡ x % UInt32.size [MOD UInt32.size] := + (Nat.mod_modEq x UInt32.size).symm + have hm : + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize ≡ + (x % UInt32.size) * (UInt32.size - 1) [MOD UInt32.size] := by + have hmi : + (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size ≡ + x % UInt32.size * montgomeryNegInv.toNat [MOD UInt32.size] := + Nat.mod_modEq _ _ + have hp : montgomeryNegInv.toNat * KoalaBear.fieldSize ≡ UInt32.size - 1 + [MOD UInt32.size] := by + decide + calc + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize + ≡ (x % UInt32.size * montgomeryNegInv.toNat) * KoalaBear.fieldSize + [MOD UInt32.size] := hmi.mul_right _ + _ = x % UInt32.size * (montgomeryNegInv.toNat * KoalaBear.fieldSize) := by ring + _ ≡ x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := hp.mul_left _ + calc + x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize + ≡ x % UInt32.size + x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := + hx.add hm + _ = x % UInt32.size * UInt32.size := by + rw [add_comm, ← Nat.mul_succ] + have hsucc : (UInt32.size - 1).succ = UInt32.size := by decide + rw [hsucc] + _ ≡ 0 [MOD UInt32.size] := by + rw [Nat.modEq_zero_iff_dvd] + exact ⟨x % UInt32.size, by rw [mul_comm]⟩ + +/-- Nat-level Montgomery reduction used to specify and prove the native-word reducer. -/ +def montgomeryReduceNat (x : Nat) : Nat := + let m := (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size + let u := (x + m * KoalaBear.fieldSize) / UInt32.size + if u < KoalaBear.fieldSize then u else u - KoalaBear.fieldSize + +theorem montgomeryReduceNat_cast (x : Nat) : + (montgomeryReduceNat x : KoalaBear.Field) = + (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by + let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size + let u := (x + m * KoalaBear.fieldSize) / UInt32.size + have hdiv : UInt32.size ∣ x + m * KoalaBear.fieldSize := by + simpa [m] using montgomery_sum_dvd x + have hu_mul : u * UInt32.size = x + m * KoalaBear.fieldSize := by + exact Nat.div_mul_cancel hdiv + have hcast_mul : (u : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) = + (x : KoalaBear.Field) := by + rw [← Nat.cast_mul, hu_mul, Nat.cast_add, Nat.cast_mul] + simp + have hR := uint32Size_ne_zero_in_field + by_cases hu : u < KoalaBear.fieldSize + · have hmain : + (u : KoalaBear.Field) = (x : KoalaBear.Field) * + (UInt32.size : KoalaBear.Field)⁻¹ := by + calc + (u : KoalaBear.Field) = + (u : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) * + (UInt32.size : KoalaBear.Field)⁻¹ := by + rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] + _ = (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by + rw [hcast_mul] + dsimp only [montgomeryReduceNat] + rw [if_pos hu] + exact hmain + · have hfield : + ((u - KoalaBear.fieldSize : Nat) : KoalaBear.Field) = (u : KoalaBear.Field) := by + rw [Nat.cast_sub (Nat.le_of_not_gt hu)] + simp + have hmain : + ((u - KoalaBear.fieldSize : Nat) : KoalaBear.Field) = + (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by + rw [hfield] + calc + (u : KoalaBear.Field) = + (u : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) * + (UInt32.size : KoalaBear.Field)⁻¹ := by + rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] + _ = (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by + rw [hcast_mul] + dsimp only [montgomeryReduceNat] + rw [if_neg hu] + exact hmain + +theorem montgomeryQuotient_cast (x : Nat) : + (let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size + let u := (x + m * KoalaBear.fieldSize) / UInt32.size + (u : KoalaBear.Field)) = + (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by + let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size + let u := (x + m * KoalaBear.fieldSize) / UInt32.size + have hmr := montgomeryReduceNat_cast x + unfold montgomeryReduceNat at hmr + change (u : KoalaBear.Field) = + (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ + by_cases hu : u < KoalaBear.fieldSize + · simpa only [m, u, hu, if_true] using hmr + · have hfield : ((u - KoalaBear.fieldSize : Nat) : KoalaBear.Field) = + (u : KoalaBear.Field) := by + rw [Nat.cast_sub (Nat.le_of_not_gt hu)] + simp + rw [← hfield] + simpa only [m, u, hu, if_false] using hmr + +theorem montgomery_u_eq_nat (x : UInt64) + (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : + (let m : UInt32 := x.toUInt32 * montgomeryNegInv + (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) = + (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * + KoalaBear.fieldSize) / UInt32.size := by + simp only [UInt64.toNat_shiftRight, UInt64.toNat_toUInt32, UInt64.toNat_add, + UInt64.toNat_mul, UInt32.toNat_toUInt64, UInt32.toNat_mul, UInt64.toNat_ofNat, + modulus64_toNat, Nat.shiftRight_eq_div_pow] + norm_num [KoalaBear.fieldSize, UInt32.size] at h ⊢ + let mNat := x.toNat * montgomeryNegInv.toNat % 4294967296 + have hm_lt : mNat < 4294967296 := Nat.mod_lt _ (by decide) + have hsum_lt : x.toNat + mNat * 2130706433 < 18446744073709551616 := by + have hprod_lt : mNat * 2130706433 < 2130706433 * 4294967296 := by nlinarith + nlinarith + change ((x.toNat + mNat * 2130706433) % 18446744073709551616 / 4294967296) % + 4294967296 = (x.toNat + mNat * 2130706433) / 4294967296 + rw [Nat.mod_eq_of_lt hsum_lt] + rw [Nat.mod_eq_of_lt] + rw [Nat.div_lt_iff_lt_mul] + · exact hsum_lt + · decide + +theorem montgomery_u_lt_two_fieldSize (x : UInt64) + (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : + (let m : UInt32 := x.toUInt32 * montgomeryNegInv + (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) < + 2 * KoalaBear.fieldSize := by + rw [montgomery_u_eq_nat x h] + let mNat := x.toNat % UInt32.size * montgomeryNegInv.toNat % UInt32.size + have hm_lt : mNat < UInt32.size := Nat.mod_lt _ (by decide) + rw [Nat.div_lt_iff_lt_mul] + · have hprod_lt : mNat * KoalaBear.fieldSize < UInt32.size * KoalaBear.fieldSize := by + exact Nat.mul_lt_mul_of_pos_right hm_lt fieldSize_pos + have hprod_lt' : + mNat * KoalaBear.fieldSize < KoalaBear.fieldSize * UInt32.size := by + simpa [Nat.mul_comm] using hprod_lt + change x.toNat + mNat * KoalaBear.fieldSize < 2 * KoalaBear.fieldSize * UInt32.size + nlinarith + · decide + +/-- Reduce a native word known to be below twice the KoalaBear prime. -/ +@[inline] +def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := + if x < modulus then x else x - modulus + +theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) + (h : x.toNat < 2 * KoalaBear.fieldSize) : + (reduceUInt32Lt2ModulusRaw x).toNat < KoalaBear.fieldSize := by + unfold reduceUInt32Lt2ModulusRaw + by_cases hx : x < modulus + · rw [if_pos hx] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact hx + · rw [if_neg hx] + have hmod_le_x : modulus ≤ x := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact Nat.le_of_not_gt hx + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] + omega + +/-- Reduce a native word known to be below twice the KoalaBear prime. -/ +@[inline] +def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : + Field := + ⟨reduceUInt32Lt2ModulusRaw x, reduceUInt32Lt2ModulusRaw_lt x h⟩ + +theorem reduceUInt32Lt2Modulus_cast (x : UInt32) + (h : x.toNat < 2 * KoalaBear.fieldSize) : + ((reduceUInt32Lt2Modulus x h).val.toNat : KoalaBear.Field) = + (x.toNat : KoalaBear.Field) := by + change ((reduceUInt32Lt2ModulusRaw x).toNat : KoalaBear.Field) = + (x.toNat : KoalaBear.Field) + unfold reduceUInt32Lt2ModulusRaw + by_cases hx : x < modulus + · rw [if_pos hx] + · have hmod_le_x : modulus ≤ x := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact Nat.le_of_not_gt hx + rw [if_neg hx] + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le, modulus_toNat] at hmod_le_x + exact hmod_le_x)] + simp + +/-- Reduce a native word below `2^32` modulo the KoalaBear prime. -/ +@[inline] +def reduceUInt32 (x : UInt32) : Field := + if hx : x < modulus then + ⟨x, by + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact hx⟩ + else + let y := x - modulus + if hy : y < modulus then + ⟨y, by + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy + exact hy⟩ + else + ⟨y - modulus, by + have hmod_le_x : modulus ≤ x := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx + exact Nat.le_of_not_gt hx + have hy_eq : y.toNat = x.toNat - KoalaBear.fieldSize := by + change (x - modulus).toNat = x.toNat - KoalaBear.fieldSize + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] + have hmod_le_y : modulus ≤ y := by + rw [UInt32.le_iff_toNat_le, modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy + exact Nat.le_of_not_gt hy + rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, modulus_toNat, hy_eq] + have hx_lt := UInt32.toNat_lt_size x + have hthree := uint32Size_lt_three_fieldSize + omega⟩ + +/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ +@[inline] +def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := + let m : UInt32 := x.toUInt32 * montgomeryNegInv + let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 + reduceUInt32Lt2ModulusRaw u + +theorem montgomeryReduceBoundedRaw_lt (x : UInt64) + (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : + (montgomeryReduceBoundedRaw x).toNat < KoalaBear.fieldSize := by + unfold montgomeryReduceBoundedRaw + exact reduceUInt32Lt2ModulusRaw_lt _ + (montgomery_u_lt_two_fieldSize x h) + +/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ +@[inline] +def montgomeryReduceBounded (x : UInt64) + (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : Field := + ⟨montgomeryReduceBoundedRaw x, montgomeryReduceBoundedRaw_lt x h⟩ + +theorem montgomeryReduceBounded_cast (x : UInt64) + (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : + ((montgomeryReduceBounded x h).val.toNat : KoalaBear.Field) = + (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by + change ((montgomeryReduceBoundedRaw x).toNat : KoalaBear.Field) = + (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ + unfold montgomeryReduceBoundedRaw + let m : UInt32 := x.toUInt32 * montgomeryNegInv + let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 + change ((reduceUInt32Lt2ModulusRaw u).toNat : KoalaBear.Field) = + (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ + have hred := reduceUInt32Lt2Modulus_cast u (montgomery_u_lt_two_fieldSize x h) + change ((reduceUInt32Lt2ModulusRaw u).toNat : KoalaBear.Field) = + (u.toNat : KoalaBear.Field) at hred + rw [hred] + change (u.toNat : KoalaBear.Field) = + (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ + rw [show u.toNat = + (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * + KoalaBear.fieldSize) / UInt32.size by + exact montgomery_u_eq_nat x h] + exact montgomeryQuotient_cast x.toNat + +/-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ +@[inline] +def montgomeryReduce (x : UInt64) : Field := + let m : UInt32 := x.toUInt32 * montgomeryNegInv + let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 + reduceUInt32 u + +end Fast +end KoalaBear diff --git a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean new file mode 100644 index 00000000..82772643 --- /dev/null +++ b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean @@ -0,0 +1,81 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin +-/ + +import CompPoly.Fields.KoalaBear.Basic + +/-! +# Fast KoalaBear Field — Basics + +The native-word modulus and the `Field` carrier type for the fast KoalaBear field, +plus the elementary numeric bounds reused throughout the Montgomery reduction layer. + +The Montgomery-form constants and reduction machinery live in +`CompPoly.Fields.KoalaBear.Fast.Montgomery`; conversions in `...Fast.Convert`; the +field operations and instances in `CompPoly.Fields.KoalaBear.Fast`. +-/ + +namespace KoalaBear +namespace Fast + +/-- KoalaBear modulus as a native word. -/ +def modulus : UInt32 := 0x7F000001 + +/-- KoalaBear modulus as a 64-bit word for modular reduction. -/ +def modulus64 : UInt64 := 0x7F000001 + +/-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ +abbrev Field : Type := { x : UInt32 // x.toNat < KoalaBear.fieldSize } + +instance : DecidableEq Field := inferInstance + +/-- The raw Montgomery word backing a fast KoalaBear element. -/ +@[inline] +def raw (x : Field) : UInt32 := x.val + +/-- The native `UInt32` modulus agrees with the mathematical KoalaBear modulus. -/ +@[simp] theorem modulus_toNat : modulus.toNat = KoalaBear.fieldSize := by + decide + +/-- The native `UInt64` modulus agrees with the mathematical KoalaBear modulus. -/ +@[simp] theorem modulus64_toNat : modulus64.toNat = KoalaBear.fieldSize := by + decide + +theorem fieldSize_pos : 0 < KoalaBear.fieldSize := by + decide + +theorem fieldSize_lt_uint32Size : KoalaBear.fieldSize < UInt32.size := by + decide + +theorem fieldSize_add_fieldSize_lt_two64 : + KoalaBear.fieldSize + KoalaBear.fieldSize < 2 ^ 64 := by + decide + +theorem fieldSize_add_fieldSize_lt_uint32Size : + KoalaBear.fieldSize + KoalaBear.fieldSize < UInt32.size := by + decide + +theorem fieldSize_mul_fieldSize_lt_two64 : + KoalaBear.fieldSize * KoalaBear.fieldSize < 2 ^ 64 := by + decide + +theorem uint32Size_lt_three_fieldSize : + UInt32.size < KoalaBear.fieldSize + KoalaBear.fieldSize + KoalaBear.fieldSize := by + decide + +theorem fieldSize_mul_uint32Size_lt_two64 : + KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by + decide + +theorem two_fieldSize_mul_uint32Size_lt_two64 : + 2 * KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by + decide + +theorem uint32Size_ne_zero_in_field : + (UInt32.size : KoalaBear.Field) ≠ 0 := by + decide + +end Fast +end KoalaBear From 2c58e808c19ce4c99dacc5dbeca5fcc8c2398083 Mon Sep 17 00:00:00 2001 From: Georgios Raikos <57717352+graikos@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:00:00 +0000 Subject: [PATCH 04/31] feat(fields): Dedup KB/BB with shared Montgomery proof logic --- CompPoly.lean | 2 + CompPoly/Fields/BabyBear/Fast/Convert.lean | 5 +- CompPoly/Fields/BabyBear/Fast/Montgomery.lean | 171 ++---------------- CompPoly/Fields/KoalaBear/Fast/Convert.lean | 5 +- .../Fields/KoalaBear/Fast/Montgomery.lean | 171 ++---------------- CompPoly/Fields/Montgomery/Basic.lean | 120 ++++++++++++ CompPoly/Fields/Montgomery/Native32.lean | 74 ++++++++ 7 files changed, 230 insertions(+), 318 deletions(-) create mode 100644 CompPoly/Fields/Montgomery/Basic.lean create mode 100644 CompPoly/Fields/Montgomery/Native32.lean diff --git a/CompPoly.lean b/CompPoly.lean index 65cfe53a..327c1123 100644 --- a/CompPoly.lean +++ b/CompPoly.lean @@ -111,6 +111,8 @@ import CompPoly.Fields.KoalaBear.Fast.Convert import CompPoly.Fields.KoalaBear.Fast.Montgomery import CompPoly.Fields.KoalaBear.Fast.Prelude import CompPoly.Fields.Mersenne +import CompPoly.Fields.Montgomery.Basic +import CompPoly.Fields.Montgomery.Native32 import CompPoly.Fields.PrattCertificate import CompPoly.Fields.Secp256k1 import CompPoly.LinearAlgebra.Dense diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean index df3c2daf..2233e54c 100644 --- a/CompPoly/Fields/BabyBear/Fast/Convert.lean +++ b/CompPoly/Fields/BabyBear/Fast/Convert.lean @@ -120,9 +120,8 @@ theorem raw_cast_eq_toField_mul (x : Field) : theorem nat_eq_of_field_eq {a b : Nat} (ha : a < BabyBear.fieldSize) (hb : b < BabyBear.fieldSize) (h : (a : BabyBear.Field) = (b : BabyBear.Field)) : - a = b := by - rw [ZMod.natCast_eq_natCast_iff] at h - exact Nat.ModEq.eq_of_lt_of_lt h ha hb + a = b := + Montgomery.natCast_inj ha hb h theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < BabyBear.fieldSize) : ((ofCanonicalNat n h).val.toNat : BabyBear.Field) = diff --git a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean index cfadc39e..45b6958f 100644 --- a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean +++ b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean @@ -5,15 +5,17 @@ Authors: Valerii Huhnin, Georgios Raikos -/ import CompPoly.Fields.BabyBear.Fast.Prelude -import Mathlib.Data.Nat.ModEq +import CompPoly.Fields.Montgomery.Native32 /-! # Fast BabyBear Field — Montgomery Reduction Montgomery-form constants (`montgomeryNegInv`, `rModModulus`, `r2ModModulus`) and the -native-word Montgomery reduction used by the fast BabyBear field. Field basics live -in `CompPoly.Fields.BabyBear.Fast.Prelude`; conversions and field operations build -on this module. +native-word Montgomery reduction used by the fast BabyBear field. The radix-generic +number theory lives in `CompPoly.Fields.Montgomery.Basic` and the `R = 2^32` word bridge +in `CompPoly.Fields.Montgomery.Native32`; field basics live in +`CompPoly.Fields.BabyBear.Fast.Prelude`; conversions and field operations build on this +module. -/ namespace BabyBear @@ -42,154 +44,6 @@ theorem r2ModModulus_cast : (r2ModModulus.toNat : BabyBear.Field) = (UInt32.size : BabyBear.Field) ^ 2 := by decide -theorem montgomery_sum_dvd (x : Nat) : - UInt32.size ∣ - x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * BabyBear.fieldSize := by - rw [← Nat.modEq_zero_iff_dvd] - have hx : x ≡ x % UInt32.size [MOD UInt32.size] := - (Nat.mod_modEq x UInt32.size).symm - have hm : - ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * BabyBear.fieldSize ≡ - (x % UInt32.size) * (UInt32.size - 1) [MOD UInt32.size] := by - have hmi : - (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size ≡ - x % UInt32.size * montgomeryNegInv.toNat [MOD UInt32.size] := - Nat.mod_modEq _ _ - have hp : montgomeryNegInv.toNat * BabyBear.fieldSize ≡ UInt32.size - 1 - [MOD UInt32.size] := by - decide - calc - ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * BabyBear.fieldSize - ≡ (x % UInt32.size * montgomeryNegInv.toNat) * BabyBear.fieldSize - [MOD UInt32.size] := hmi.mul_right _ - _ = x % UInt32.size * (montgomeryNegInv.toNat * BabyBear.fieldSize) := by ring - _ ≡ x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := hp.mul_left _ - calc - x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * BabyBear.fieldSize - ≡ x % UInt32.size + x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := - hx.add hm - _ = x % UInt32.size * UInt32.size := by - rw [add_comm, ← Nat.mul_succ] - have hsucc : (UInt32.size - 1).succ = UInt32.size := by decide - rw [hsucc] - _ ≡ 0 [MOD UInt32.size] := by - rw [Nat.modEq_zero_iff_dvd] - exact ⟨x % UInt32.size, by rw [mul_comm]⟩ - -/-- Nat-level Montgomery reduction used to specify and prove the native-word reducer. -/ -def montgomeryReduceNat (x : Nat) : Nat := - let m := (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size - let u := (x + m * BabyBear.fieldSize) / UInt32.size - if u < BabyBear.fieldSize then u else u - BabyBear.fieldSize - -theorem montgomeryReduceNat_cast (x : Nat) : - (montgomeryReduceNat x : BabyBear.Field) = - (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by - let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * BabyBear.fieldSize) / UInt32.size - have hdiv : UInt32.size ∣ x + m * BabyBear.fieldSize := by - simpa [m] using montgomery_sum_dvd x - have hu_mul : u * UInt32.size = x + m * BabyBear.fieldSize := by - exact Nat.div_mul_cancel hdiv - have hcast_mul : (u : BabyBear.Field) * (UInt32.size : BabyBear.Field) = - (x : BabyBear.Field) := by - rw [← Nat.cast_mul, hu_mul, Nat.cast_add, Nat.cast_mul] - simp - have hR := uint32Size_ne_zero_in_field - by_cases hu : u < BabyBear.fieldSize - · have hmain : - (u : BabyBear.Field) = (x : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹ := by - calc - (u : BabyBear.Field) = - (u : BabyBear.Field) * (UInt32.size : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹ := by - rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] - _ = (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by - rw [hcast_mul] - dsimp only [montgomeryReduceNat] - rw [if_pos hu] - exact hmain - · have hfield : - ((u - BabyBear.fieldSize : Nat) : BabyBear.Field) = (u : BabyBear.Field) := by - rw [Nat.cast_sub (Nat.le_of_not_gt hu)] - simp - have hmain : - ((u - BabyBear.fieldSize : Nat) : BabyBear.Field) = - (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by - rw [hfield] - calc - (u : BabyBear.Field) = - (u : BabyBear.Field) * (UInt32.size : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹ := by - rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] - _ = (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by - rw [hcast_mul] - dsimp only [montgomeryReduceNat] - rw [if_neg hu] - exact hmain - -theorem montgomeryQuotient_cast (x : Nat) : - (let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * BabyBear.fieldSize) / UInt32.size - (u : BabyBear.Field)) = - (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by - let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * BabyBear.fieldSize) / UInt32.size - have hmr := montgomeryReduceNat_cast x - unfold montgomeryReduceNat at hmr - change (u : BabyBear.Field) = - (x : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - by_cases hu : u < BabyBear.fieldSize - · simpa only [m, u, hu, if_true] using hmr - · have hfield : ((u - BabyBear.fieldSize : Nat) : BabyBear.Field) = - (u : BabyBear.Field) := by - rw [Nat.cast_sub (Nat.le_of_not_gt hu)] - simp - rw [← hfield] - simpa only [m, u, hu, if_false] using hmr - -theorem montgomery_u_eq_nat (x : UInt64) - (h : x.toNat < BabyBear.fieldSize * UInt32.size) : - (let m : UInt32 := x.toUInt32 * montgomeryNegInv - (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) = - (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * - BabyBear.fieldSize) / UInt32.size := by - simp only [UInt64.toNat_shiftRight, UInt64.toNat_toUInt32, UInt64.toNat_add, - UInt64.toNat_mul, UInt32.toNat_toUInt64, UInt32.toNat_mul, UInt64.toNat_ofNat, - modulus64_toNat, Nat.shiftRight_eq_div_pow] - norm_num [BabyBear.fieldSize, UInt32.size] at h ⊢ - let mNat := x.toNat * montgomeryNegInv.toNat % 4294967296 - have hm_lt : mNat < 4294967296 := Nat.mod_lt _ (by decide) - have hsum_lt : x.toNat + mNat * 2013265921 < 18446744073709551616 := by - have hprod_lt : mNat * 2013265921 < 2013265921 * 4294967296 := by nlinarith - nlinarith - change ((x.toNat + mNat * 2013265921) % 18446744073709551616 / 4294967296) % - 4294967296 = (x.toNat + mNat * 2013265921) / 4294967296 - rw [Nat.mod_eq_of_lt hsum_lt] - rw [Nat.mod_eq_of_lt] - rw [Nat.div_lt_iff_lt_mul] - · exact hsum_lt - · decide - -theorem montgomery_u_lt_two_fieldSize (x : UInt64) - (h : x.toNat < BabyBear.fieldSize * UInt32.size) : - (let m : UInt32 := x.toUInt32 * montgomeryNegInv - (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) < - 2 * BabyBear.fieldSize := by - rw [montgomery_u_eq_nat x h] - let mNat := x.toNat % UInt32.size * montgomeryNegInv.toNat % UInt32.size - have hm_lt : mNat < UInt32.size := Nat.mod_lt _ (by decide) - rw [Nat.div_lt_iff_lt_mul] - · have hprod_lt : mNat * BabyBear.fieldSize < UInt32.size * BabyBear.fieldSize := by - exact Nat.mul_lt_mul_of_pos_right hm_lt fieldSize_pos - have hprod_lt' : - mNat * BabyBear.fieldSize < BabyBear.fieldSize * UInt32.size := by - simpa [Nat.mul_comm] using hprod_lt - change x.toNat + mNat * BabyBear.fieldSize < 2 * BabyBear.fieldSize * UInt32.size - nlinarith - · decide - /-- Reduce a native word known to be below twice the BabyBear prime. -/ @[inline] def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := @@ -280,7 +134,8 @@ theorem montgomeryReduceBoundedRaw_lt (x : UInt64) (montgomeryReduceBoundedRaw x).toNat < BabyBear.fieldSize := by unfold montgomeryReduceBoundedRaw exact reduceUInt32Lt2ModulusRaw_lt _ - (montgomery_u_lt_two_fieldSize x h) + (Montgomery.Native32.u_lt_two_mul montgomeryNegInv modulus64 BabyBear.fieldSize + modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h) /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] @@ -299,7 +154,9 @@ theorem montgomeryReduceBounded_cast (x : UInt64) let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 change ((reduceUInt32Lt2ModulusRaw u).toNat : BabyBear.Field) = (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - have hred := reduceUInt32Lt2Modulus_cast u (montgomery_u_lt_two_fieldSize x h) + have hred := reduceUInt32Lt2Modulus_cast u + (Montgomery.Native32.u_lt_two_mul montgomeryNegInv modulus64 BabyBear.fieldSize + modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h) change ((reduceUInt32Lt2ModulusRaw u).toNat : BabyBear.Field) = (u.toNat : BabyBear.Field) at hred rw [hred] @@ -308,8 +165,10 @@ theorem montgomeryReduceBounded_cast (x : UInt64) rw [show u.toNat = (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * BabyBear.fieldSize) / UInt32.size by - exact montgomery_u_eq_nat x h] - exact montgomeryQuotient_cast x.toNat + exact Montgomery.Native32.u_eq_nat montgomeryNegInv modulus64 BabyBear.fieldSize + modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h] + exact Montgomery.quotient_cast UInt32.size BabyBear.fieldSize montgomeryNegInv.toNat + (by decide) (by decide) uint32Size_ne_zero_in_field x.toNat /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] diff --git a/CompPoly/Fields/KoalaBear/Fast/Convert.lean b/CompPoly/Fields/KoalaBear/Fast/Convert.lean index 27e538cb..6abc9b46 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Convert.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Convert.lean @@ -120,9 +120,8 @@ theorem raw_cast_eq_toField_mul (x : Field) : theorem nat_eq_of_field_eq {a b : Nat} (ha : a < KoalaBear.fieldSize) (hb : b < KoalaBear.fieldSize) (h : (a : KoalaBear.Field) = (b : KoalaBear.Field)) : - a = b := by - rw [ZMod.natCast_eq_natCast_iff] at h - exact Nat.ModEq.eq_of_lt_of_lt h ha hb + a = b := + Montgomery.natCast_inj ha hb h theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < KoalaBear.fieldSize) : ((ofCanonicalNat n h).val.toNat : KoalaBear.Field) = diff --git a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean index 3d3a7ab2..310c1976 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean @@ -5,15 +5,17 @@ Authors: Valerii Huhnin -/ import CompPoly.Fields.KoalaBear.Fast.Prelude -import Mathlib.Data.Nat.ModEq +import CompPoly.Fields.Montgomery.Native32 /-! # Fast KoalaBear Field — Montgomery Reduction Montgomery-form constants (`montgomeryNegInv`, `rModModulus`, `r2ModModulus`) and the -native-word Montgomery reduction used by the fast KoalaBear field. Field basics live -in `CompPoly.Fields.KoalaBear.Fast.Prelude`; conversions and field operations build -on this module. +native-word Montgomery reduction used by the fast KoalaBear field. The radix-generic +number theory lives in `CompPoly.Fields.Montgomery.Basic` and the `R = 2^32` word bridge +in `CompPoly.Fields.Montgomery.Native32`; field basics live in +`CompPoly.Fields.KoalaBear.Fast.Prelude`; conversions and field operations build on this +module. -/ namespace KoalaBear @@ -42,154 +44,6 @@ theorem r2ModModulus_cast : (r2ModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) ^ 2 := by decide -theorem montgomery_sum_dvd (x : Nat) : - UInt32.size ∣ - x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize := by - rw [← Nat.modEq_zero_iff_dvd] - have hx : x ≡ x % UInt32.size [MOD UInt32.size] := - (Nat.mod_modEq x UInt32.size).symm - have hm : - ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize ≡ - (x % UInt32.size) * (UInt32.size - 1) [MOD UInt32.size] := by - have hmi : - (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size ≡ - x % UInt32.size * montgomeryNegInv.toNat [MOD UInt32.size] := - Nat.mod_modEq _ _ - have hp : montgomeryNegInv.toNat * KoalaBear.fieldSize ≡ UInt32.size - 1 - [MOD UInt32.size] := by - decide - calc - ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize - ≡ (x % UInt32.size * montgomeryNegInv.toNat) * KoalaBear.fieldSize - [MOD UInt32.size] := hmi.mul_right _ - _ = x % UInt32.size * (montgomeryNegInv.toNat * KoalaBear.fieldSize) := by ring - _ ≡ x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := hp.mul_left _ - calc - x + ((x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize - ≡ x % UInt32.size + x % UInt32.size * (UInt32.size - 1) [MOD UInt32.size] := - hx.add hm - _ = x % UInt32.size * UInt32.size := by - rw [add_comm, ← Nat.mul_succ] - have hsucc : (UInt32.size - 1).succ = UInt32.size := by decide - rw [hsucc] - _ ≡ 0 [MOD UInt32.size] := by - rw [Nat.modEq_zero_iff_dvd] - exact ⟨x % UInt32.size, by rw [mul_comm]⟩ - -/-- Nat-level Montgomery reduction used to specify and prove the native-word reducer. -/ -def montgomeryReduceNat (x : Nat) : Nat := - let m := (x % UInt32.size * montgomeryNegInv.toNat) % UInt32.size - let u := (x + m * KoalaBear.fieldSize) / UInt32.size - if u < KoalaBear.fieldSize then u else u - KoalaBear.fieldSize - -theorem montgomeryReduceNat_cast (x : Nat) : - (montgomeryReduceNat x : KoalaBear.Field) = - (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * KoalaBear.fieldSize) / UInt32.size - have hdiv : UInt32.size ∣ x + m * KoalaBear.fieldSize := by - simpa [m] using montgomery_sum_dvd x - have hu_mul : u * UInt32.size = x + m * KoalaBear.fieldSize := by - exact Nat.div_mul_cancel hdiv - have hcast_mul : (u : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) = - (x : KoalaBear.Field) := by - rw [← Nat.cast_mul, hu_mul, Nat.cast_add, Nat.cast_mul] - simp - have hR := uint32Size_ne_zero_in_field - by_cases hu : u < KoalaBear.fieldSize - · have hmain : - (u : KoalaBear.Field) = (x : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ := by - calc - (u : KoalaBear.Field) = - (u : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] - _ = (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [hcast_mul] - dsimp only [montgomeryReduceNat] - rw [if_pos hu] - exact hmain - · have hfield : - ((u - KoalaBear.fieldSize : Nat) : KoalaBear.Field) = (u : KoalaBear.Field) := by - rw [Nat.cast_sub (Nat.le_of_not_gt hu)] - simp - have hmain : - ((u - KoalaBear.fieldSize : Nat) : KoalaBear.Field) = - (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [hfield] - calc - (u : KoalaBear.Field) = - (u : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [mul_assoc, mul_inv_cancel₀ hR, mul_one] - _ = (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - rw [hcast_mul] - dsimp only [montgomeryReduceNat] - rw [if_neg hu] - exact hmain - -theorem montgomeryQuotient_cast (x : Nat) : - (let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * KoalaBear.fieldSize) / UInt32.size - (u : KoalaBear.Field)) = - (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - let m := x % UInt32.size * montgomeryNegInv.toNat % UInt32.size - let u := (x + m * KoalaBear.fieldSize) / UInt32.size - have hmr := montgomeryReduceNat_cast x - unfold montgomeryReduceNat at hmr - change (u : KoalaBear.Field) = - (x : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - by_cases hu : u < KoalaBear.fieldSize - · simpa only [m, u, hu, if_true] using hmr - · have hfield : ((u - KoalaBear.fieldSize : Nat) : KoalaBear.Field) = - (u : KoalaBear.Field) := by - rw [Nat.cast_sub (Nat.le_of_not_gt hu)] - simp - rw [← hfield] - simpa only [m, u, hu, if_false] using hmr - -theorem montgomery_u_eq_nat (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - (let m : UInt32 := x.toUInt32 * montgomeryNegInv - (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) = - (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * - KoalaBear.fieldSize) / UInt32.size := by - simp only [UInt64.toNat_shiftRight, UInt64.toNat_toUInt32, UInt64.toNat_add, - UInt64.toNat_mul, UInt32.toNat_toUInt64, UInt32.toNat_mul, UInt64.toNat_ofNat, - modulus64_toNat, Nat.shiftRight_eq_div_pow] - norm_num [KoalaBear.fieldSize, UInt32.size] at h ⊢ - let mNat := x.toNat * montgomeryNegInv.toNat % 4294967296 - have hm_lt : mNat < 4294967296 := Nat.mod_lt _ (by decide) - have hsum_lt : x.toNat + mNat * 2130706433 < 18446744073709551616 := by - have hprod_lt : mNat * 2130706433 < 2130706433 * 4294967296 := by nlinarith - nlinarith - change ((x.toNat + mNat * 2130706433) % 18446744073709551616 / 4294967296) % - 4294967296 = (x.toNat + mNat * 2130706433) / 4294967296 - rw [Nat.mod_eq_of_lt hsum_lt] - rw [Nat.mod_eq_of_lt] - rw [Nat.div_lt_iff_lt_mul] - · exact hsum_lt - · decide - -theorem montgomery_u_lt_two_fieldSize (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - (let m : UInt32 := x.toUInt32 * montgomeryNegInv - (((x + m.toUInt64 * modulus64) >>> 32).toUInt32).toNat) < - 2 * KoalaBear.fieldSize := by - rw [montgomery_u_eq_nat x h] - let mNat := x.toNat % UInt32.size * montgomeryNegInv.toNat % UInt32.size - have hm_lt : mNat < UInt32.size := Nat.mod_lt _ (by decide) - rw [Nat.div_lt_iff_lt_mul] - · have hprod_lt : mNat * KoalaBear.fieldSize < UInt32.size * KoalaBear.fieldSize := by - exact Nat.mul_lt_mul_of_pos_right hm_lt fieldSize_pos - have hprod_lt' : - mNat * KoalaBear.fieldSize < KoalaBear.fieldSize * UInt32.size := by - simpa [Nat.mul_comm] using hprod_lt - change x.toNat + mNat * KoalaBear.fieldSize < 2 * KoalaBear.fieldSize * UInt32.size - nlinarith - · decide - /-- Reduce a native word known to be below twice the KoalaBear prime. -/ @[inline] def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := @@ -280,7 +134,8 @@ theorem montgomeryReduceBoundedRaw_lt (x : UInt64) (montgomeryReduceBoundedRaw x).toNat < KoalaBear.fieldSize := by unfold montgomeryReduceBoundedRaw exact reduceUInt32Lt2ModulusRaw_lt _ - (montgomery_u_lt_two_fieldSize x h) + (Montgomery.Native32.u_lt_two_mul montgomeryNegInv modulus64 KoalaBear.fieldSize + modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h) /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] @@ -299,7 +154,9 @@ theorem montgomeryReduceBounded_cast (x : UInt64) let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 change ((reduceUInt32Lt2ModulusRaw u).toNat : KoalaBear.Field) = (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - have hred := reduceUInt32Lt2Modulus_cast u (montgomery_u_lt_two_fieldSize x h) + have hred := reduceUInt32Lt2Modulus_cast u + (Montgomery.Native32.u_lt_two_mul montgomeryNegInv modulus64 KoalaBear.fieldSize + modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h) change ((reduceUInt32Lt2ModulusRaw u).toNat : KoalaBear.Field) = (u.toNat : KoalaBear.Field) at hred rw [hred] @@ -308,8 +165,10 @@ theorem montgomeryReduceBounded_cast (x : UInt64) rw [show u.toNat = (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * KoalaBear.fieldSize) / UInt32.size by - exact montgomery_u_eq_nat x h] - exact montgomeryQuotient_cast x.toNat + exact Montgomery.Native32.u_eq_nat montgomeryNegInv modulus64 KoalaBear.fieldSize + modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h] + exact Montgomery.quotient_cast UInt32.size KoalaBear.fieldSize montgomeryNegInv.toNat + (by decide) (by decide) uint32Size_ne_zero_in_field x.toNat /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] diff --git a/CompPoly/Fields/Montgomery/Basic.lean b/CompPoly/Fields/Montgomery/Basic.lean new file mode 100644 index 00000000..e6a8bce1 --- /dev/null +++ b/CompPoly/Fields/Montgomery/Basic.lean @@ -0,0 +1,120 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin, Georgios Raikos +-/ + +import Mathlib.Data.Nat.ModEq +import Mathlib.Data.ZMod.Basic +import Mathlib.Algebra.Field.ZMod +import Mathlib.Tactic.Ring + +/-! +# Montgomery Reduction — radix-generic core + +Field-agnostic number theory behind native-word Montgomery reduction, shared by every +fast prime field. Everything here is parameterized by an abstract radix `R : Nat` (the +Montgomery modulus, e.g. `2^32` for 32-bit-word fields) and the prime `p`; the proofs use +only that `gcd(R, p) = 1` — supplied as the congruence `negInv * p ≡ R - 1 [MOD R]` — and +that `0 < R`, so nothing here is tied to a particular word size. + +The concrete word-level bridges (e.g. the `UInt32 × UInt64`, `R = 2^32` reduction) live in +sibling modules such as `CompPoly.Fields.Montgomery.Native32`, which instantiate `R` and +call into this core. These lemmas are all `Prop`/spec level and erased at codegen, so +sharing them carries no runtime cost. +-/ + +namespace Montgomery + +/-- The Montgomery divisibility identity: if `negInv * p ≡ R - 1 [MOD R]` (i.e. +`negInv = -p⁻¹ mod R`), then `R ∣ x + ((x mod R)·negInv mod R)·p` for every `x`. This is +what makes Montgomery reduction integer-valued. -/ +theorem sum_dvd (R p negInv : Nat) (hR : 0 < R) + (hp : negInv * p ≡ R - 1 [MOD R]) (x : Nat) : + R ∣ x + ((x % R * negInv) % R) * p := by + rw [← Nat.modEq_zero_iff_dvd] + have hx : x ≡ x % R [MOD R] := (Nat.mod_modEq x R).symm + have hm : + ((x % R * negInv) % R) * p ≡ (x % R) * (R - 1) [MOD R] := by + have hmi : + (x % R * negInv) % R ≡ x % R * negInv [MOD R] := Nat.mod_modEq _ _ + calc + ((x % R * negInv) % R) * p + ≡ (x % R * negInv) * p [MOD R] := hmi.mul_right _ + _ = x % R * (negInv * p) := by ring + _ ≡ x % R * (R - 1) [MOD R] := hp.mul_left _ + calc + x + ((x % R * negInv) % R) * p + ≡ x % R + x % R * (R - 1) [MOD R] := hx.add hm + _ = x % R * R := by + rw [add_comm, ← Nat.mul_succ] + have hsucc : (R - 1).succ = R := by omega + rw [hsucc] + _ ≡ 0 [MOD R] := by + rw [Nat.modEq_zero_iff_dvd] + exact ⟨x % R, by rw [mul_comm]⟩ + +/-- Nat-level Montgomery reduction: specifies and is used to prove the native-word reducer. -/ +def reduceNat (R p negInv x : Nat) : Nat := + let m := (x % R * negInv) % R + let u := (x + m * p) / R + if u < p then u else u - p + +/-- Montgomery reduction descends to "multiply by `R⁻¹`" in `ZMod p`. -/ +theorem reduceNat_cast (R p negInv : Nat) [Fact (Nat.Prime p)] (hR : 0 < R) + (hp : negInv * p ≡ R - 1 [MOD R]) (hRne : (R : ZMod p) ≠ 0) (x : Nat) : + (reduceNat R p negInv x : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by + let m := x % R * negInv % R + let u := (x + m * p) / R + have hdiv : R ∣ x + m * p := by + simpa [m] using sum_dvd R p negInv hR hp x + have hu_mul : u * R = x + m * p := Nat.div_mul_cancel hdiv + have hcast_mul : (u : ZMod p) * (R : ZMod p) = (x : ZMod p) := by + rw [← Nat.cast_mul, hu_mul, Nat.cast_add, Nat.cast_mul] + simp + by_cases hu : u < p + · have hmain : (u : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by + rw [← hcast_mul] + refine (mul_inv_cancel_right₀ ?_ (u : ZMod p)).symm + exact hRne + dsimp only [reduceNat] + rw [if_pos hu] + exact hmain + · have hfield : ((u - p : Nat) : ZMod p) = (u : ZMod p) := by + rw [Nat.cast_sub (Nat.le_of_not_gt hu)] + simp + have hmain : ((u - p : Nat) : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by + rw [hfield, ← hcast_mul] + refine (mul_inv_cancel_right₀ ?_ (u : ZMod p)).symm + exact hRne + dsimp only [reduceNat] + rw [if_neg hu] + exact hmain + +/-- The pre-conditional-subtract Montgomery quotient already casts to "multiply by `R⁻¹`" +in `ZMod p`. -/ +theorem quotient_cast (R p negInv : Nat) [Fact (Nat.Prime p)] (hR : 0 < R) + (hp : negInv * p ≡ R - 1 [MOD R]) (hRne : (R : ZMod p) ≠ 0) (x : Nat) : + (let m := x % R * negInv % R + let u := (x + m * p) / R + (u : ZMod p)) = (x : ZMod p) * (R : ZMod p)⁻¹ := by + let m := x % R * negInv % R + let u := (x + m * p) / R + have hmr := reduceNat_cast R p negInv hR hp hRne x + unfold reduceNat at hmr + change (u : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ + by_cases hu : u < p + · simpa only [m, u, hu, if_true] using hmr + · have hfield : ((u - p : Nat) : ZMod p) = (u : ZMod p) := by + rw [Nat.cast_sub (Nat.le_of_not_gt hu)] + simp + rw [← hfield] + simpa only [m, u, hu, if_false] using hmr + +/-- Two naturals below `p` are equal once their `ZMod p` casts agree. -/ +theorem natCast_inj {p a b : Nat} (ha : a < p) (hb : b < p) + (h : (a : ZMod p) = (b : ZMod p)) : a = b := by + rw [ZMod.natCast_eq_natCast_iff] at h + exact Nat.ModEq.eq_of_lt_of_lt h ha hb + +end Montgomery diff --git a/CompPoly/Fields/Montgomery/Native32.lean b/CompPoly/Fields/Montgomery/Native32.lean new file mode 100644 index 00000000..9cd9df29 --- /dev/null +++ b/CompPoly/Fields/Montgomery/Native32.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin, Georgios Raikos +-/ + +import CompPoly.Fields.Montgomery.Basic + +/-! +# Montgomery Reduction — native `UInt32 × UInt64`, radix `R = 2^32` + +The word-level bridge specialising `Montgomery.Basic` to 32-bit-word prime fields: the +Montgomery product fits in a `UInt64`, the radix is `R = UInt32.size = 2^32`, and the +high word is extracted with `>>> 32`. These lemmas relate the native-word computation to +the `Nat` formula proved generically in `Montgomery.Basic`, parameterized by the prime +`p`, the `UInt32` negated inverse `negInv`, and the `UInt64` modulus `modN`. + +A future 64-bit family would add a parallel `Native64` bridge (`>>> 64`, +`UInt64 × UInt128`) over the same `Montgomery.Basic` core. +-/ + +namespace Montgomery +namespace Native32 + +/-- The native high-word extraction agrees with the `Nat`-level Montgomery quotient. -/ +theorem u_eq_nat (negInv : UInt32) (modN : UInt64) (p : Nat) + (hmod : modN.toNat = p) (hp_pos : 0 < p) + (hbound : 2 * p * UInt32.size < 2 ^ 64) + (x : UInt64) (h : x.toNat < p * UInt32.size) : + ((((x + (x.toUInt32 * negInv).toUInt64 * modN) >>> 32).toUInt32).toNat) = + (x.toNat + ((x.toNat % UInt32.size * negInv.toNat) % UInt32.size) * p) / UInt32.size := by + simp only [UInt64.toNat_shiftRight, UInt64.toNat_toUInt32, UInt64.toNat_add, + UInt64.toNat_mul, UInt32.toNat_toUInt64, UInt32.toNat_mul, UInt64.toNat_ofNat, + hmod, Nat.shiftRight_eq_div_pow] + norm_num [UInt32.size] at h hbound ⊢ + let mNat := x.toNat * negInv.toNat % 4294967296 + have hm_lt : mNat < 4294967296 := Nat.mod_lt _ (by decide) + have hsum_lt : x.toNat + mNat * p < 18446744073709551616 := by + have hprod_lt : mNat * p < p * 4294967296 := by + have := Nat.mul_lt_mul_of_pos_right hm_lt hp_pos + simpa [Nat.mul_comm] using this + calc x.toNat + mNat * p < p * 4294967296 + p * 4294967296 := Nat.add_lt_add h hprod_lt + _ = 2 * p * 4294967296 := by ring + _ < 18446744073709551616 := hbound + change ((x.toNat + mNat * p) % 18446744073709551616 / 4294967296) % 4294967296 = + (x.toNat + mNat * p) / 4294967296 + rw [Nat.mod_eq_of_lt hsum_lt] + rw [Nat.mod_eq_of_lt] + rw [Nat.div_lt_iff_lt_mul] + · exact hsum_lt + · decide + +/-- The native Montgomery quotient stays below `2 * p`, so a single conditional subtract +canonicalises it. -/ +theorem u_lt_two_mul (negInv : UInt32) (modN : UInt64) (p : Nat) + (hmod : modN.toNat = p) (hp_pos : 0 < p) + (hbound : 2 * p * UInt32.size < 2 ^ 64) + (x : UInt64) (h : x.toNat < p * UInt32.size) : + ((((x + (x.toUInt32 * negInv).toUInt64 * modN) >>> 32).toUInt32).toNat) < 2 * p := by + rw [u_eq_nat negInv modN p hmod hp_pos hbound x h] + let mNat := x.toNat % UInt32.size * negInv.toNat % UInt32.size + have hm_lt : mNat < UInt32.size := Nat.mod_lt _ (by decide) + rw [Nat.div_lt_iff_lt_mul] + · have hprod_lt : mNat * p < UInt32.size * p := Nat.mul_lt_mul_of_pos_right hm_lt hp_pos + have hprod_lt' : mNat * p < p * UInt32.size := by + simpa [Nat.mul_comm] using hprod_lt + change x.toNat + mNat * p < 2 * p * UInt32.size + calc x.toNat + mNat * p < p * UInt32.size + p * UInt32.size := + Nat.add_lt_add h hprod_lt' + _ = 2 * p * UInt32.size := by ring + · decide + +end Native32 +end Montgomery From 6b87a34e6c31a3237e35831304a7944522476428 Mon Sep 17 00:00:00 2001 From: Georgios Raikos <57717352+graikos@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:10:44 +0000 Subject: [PATCH 05/31] feat(fields): Changed inv to pow instead of chain --- CompPoly/Fields/BabyBear/Fast.lean | 97 ++--------------------------- CompPoly/Fields/KoalaBear/Fast.lean | 97 ++--------------------------- 2 files changed, 8 insertions(+), 186 deletions(-) diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index 58dc2839..a35854c7 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -102,27 +102,11 @@ def pow (x : Field) (n : Nat) : Field := /-- Fermat exponent used for inversion in the BabyBear prime field. -/ def invExponent : Nat := BabyBear.fieldSize - 2 -/-- Four squarings followed by multiplication by the next 4-bit exponent digit. -/ -@[inline] -private def shift4Mul (acc digit : Field) : Field := - mul (square (square (square (square acc)))) digit - -/-- Inversion in Montgomery form by a fixed 4-bit Fermat chain. -/ +/-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`), +by binary exponentiation (`pow`). -/ @[inline] def inv (x : Field) : Field := - let x2 := square x - let x3 := mul x2 x - let x5 := mul x3 x2 - let x7 := mul x5 x2 - let x14 := square x7 - let x15 := mul x14 x - let acc := shift4Mul x7 x7 - let acc := shift4Mul acc x15 - let acc := shift4Mul acc x15 - let acc := shift4Mul acc x15 - let acc := shift4Mul acc x15 - let acc := shift4Mul acc x15 - shift4Mul acc x15 + pow x invExponent /-- Division through inversion and fast multiplication. -/ @[inline] @@ -402,83 +386,10 @@ theorem toField_pow (x : Field) (n : Nat) : toField (pow x n) = toField x ^ n := | succ n ih => rw [pow_succ, toField_mul, ih, _root_.pow_succ] -private theorem toField_mul_pow (base x y : Field) (m n : Nat) - (hx : toField x = toField base ^ m) (hy : toField y = toField base ^ n) : - toField (mul x y) = toField base ^ (m + n) := by - change toField (x * y) = toField base ^ (m + n) - rw [toField_mul, hx, hy, ← pow_add] - -private theorem toField_shift4Mul (acc digit : Field) : - toField (shift4Mul acc digit) = toField acc ^ 16 * toField digit := by - unfold shift4Mul - change toField (square (square (square (square acc))) * digit) = - toField acc ^ 16 * toField digit - rw [toField_mul] - repeat rw [toField_square] - ring - -private theorem toField_shift4Mul_pow (base acc digit : Field) (e d : Nat) - (hacc : toField acc = toField base ^ e) (hdigit : toField digit = toField base ^ d) : - toField (shift4Mul acc digit) = toField base ^ (16 * e + d) := by - rw [toField_shift4Mul, hacc, hdigit] - rw [← pow_mul, ← pow_add] - congr 1 - omega - private theorem toField_inv_pow (x : Field) : toField (inv x) = toField x ^ invExponent := by unfold inv - let x2 := square x - let x3 := mul x2 x - let x5 := mul x3 x2 - let x7 := mul x5 x2 - let x14 := square x7 - let x15 := mul x14 x - let acc1 := shift4Mul x7 x7 - let acc2 := shift4Mul acc1 x15 - let acc3 := shift4Mul acc2 x15 - let acc4 := shift4Mul acc3 x15 - let acc5 := shift4Mul acc4 x15 - let acc6 := shift4Mul acc5 x15 - have hx1 : toField x = toField x ^ 1 := by simp - have hx2 : toField x2 = toField x ^ 2 := by - dsimp [x2] - rw [toField_square, pow_two] - have hx3 : toField x3 = toField x ^ 3 := by - dsimp [x3] - simpa using toField_mul_pow x x2 x 2 1 hx2 hx1 - have hx5 : toField x5 = toField x ^ 5 := by - dsimp [x5] - simpa using toField_mul_pow x x3 x2 3 2 hx3 hx2 - have hx7 : toField x7 = toField x ^ 7 := by - dsimp [x7] - simpa using toField_mul_pow x x5 x2 5 2 hx5 hx2 - have hx14 : toField x14 = toField x ^ 14 := by - dsimp [x14] - rw [toField_square, hx7, ← pow_add] - have hx15 : toField x15 = toField x ^ 15 := by - dsimp [x15] - simpa using toField_mul_pow x x14 x 14 1 hx14 hx1 - have hacc1 : toField acc1 = toField x ^ 119 := by - dsimp [acc1] - simpa using toField_shift4Mul_pow x x7 x7 7 7 hx7 hx7 - have hacc2 : toField acc2 = toField x ^ 1919 := by - dsimp [acc2] - simpa using toField_shift4Mul_pow x acc1 x15 119 15 hacc1 hx15 - have hacc3 : toField acc3 = toField x ^ 30719 := by - dsimp [acc3] - simpa using toField_shift4Mul_pow x acc2 x15 1919 15 hacc2 hx15 - have hacc4 : toField acc4 = toField x ^ 491519 := by - dsimp [acc4] - simpa using toField_shift4Mul_pow x acc3 x15 30719 15 hacc3 hx15 - have hacc5 : toField acc5 = toField x ^ 7864319 := by - dsimp [acc5] - simpa using toField_shift4Mul_pow x acc4 x15 491519 15 hacc4 hx15 - have hacc6 : toField acc6 = toField x ^ 125829119 := by - dsimp [acc6] - simpa using toField_shift4Mul_pow x acc5 x15 7864319 15 hacc5 hx15 - have hfinal := toField_shift4Mul_pow x acc6 x15 125829119 15 hacc6 hx15 - simpa [invExponent, BabyBear.fieldSize] using hfinal + exact toField_pow x invExponent private theorem toField_inv_raw (x : Field) : toField (inv x) = (toField x)⁻¹ := by rw [toField_inv_pow] diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index a4259175..04da53b5 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -102,27 +102,11 @@ def pow (x : Field) (n : Nat) : Field := /-- Fermat exponent used for inversion in the KoalaBear prime field. -/ def invExponent : Nat := KoalaBear.fieldSize - 2 -/-- Four squarings followed by multiplication by the next 4-bit exponent digit. -/ -@[inline] -private def shift4Mul (acc digit : Field) : Field := - mul (square (square (square (square acc)))) digit - -/-- Inversion in Montgomery form by a fixed 4-bit Fermat chain. -/ +/-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`), +by binary exponentiation (`pow`). -/ @[inline] def inv (x : Field) : Field := - let x2 := square x - let x3 := mul x2 x - let x5 := mul x3 x2 - let x7 := mul x5 x2 - let x14 := square x7 - let x15 := mul x14 x - let acc := shift4Mul x7 x14 - let acc := shift4Mul acc x15 - let acc := shift4Mul acc x15 - let acc := shift4Mul acc x15 - let acc := shift4Mul acc x15 - let acc := shift4Mul acc x15 - shift4Mul acc x15 + pow x invExponent /-- Division through inversion and fast multiplication. -/ @[inline] @@ -402,83 +386,10 @@ theorem toField_pow (x : Field) (n : Nat) : toField (pow x n) = toField x ^ n := | succ n ih => rw [pow_succ, toField_mul, ih, _root_.pow_succ] -private theorem toField_mul_pow (base x y : Field) (m n : Nat) - (hx : toField x = toField base ^ m) (hy : toField y = toField base ^ n) : - toField (mul x y) = toField base ^ (m + n) := by - change toField (x * y) = toField base ^ (m + n) - rw [toField_mul, hx, hy, ← pow_add] - -private theorem toField_shift4Mul (acc digit : Field) : - toField (shift4Mul acc digit) = toField acc ^ 16 * toField digit := by - unfold shift4Mul - change toField (square (square (square (square acc))) * digit) = - toField acc ^ 16 * toField digit - rw [toField_mul] - repeat rw [toField_square] - ring - -private theorem toField_shift4Mul_pow (base acc digit : Field) (e d : Nat) - (hacc : toField acc = toField base ^ e) (hdigit : toField digit = toField base ^ d) : - toField (shift4Mul acc digit) = toField base ^ (16 * e + d) := by - rw [toField_shift4Mul, hacc, hdigit] - rw [← pow_mul, ← pow_add] - congr 1 - omega - private theorem toField_inv_pow (x : Field) : toField (inv x) = toField x ^ invExponent := by unfold inv - let x2 := square x - let x3 := mul x2 x - let x5 := mul x3 x2 - let x7 := mul x5 x2 - let x14 := square x7 - let x15 := mul x14 x - let acc1 := shift4Mul x7 x14 - let acc2 := shift4Mul acc1 x15 - let acc3 := shift4Mul acc2 x15 - let acc4 := shift4Mul acc3 x15 - let acc5 := shift4Mul acc4 x15 - let acc6 := shift4Mul acc5 x15 - have hx1 : toField x = toField x ^ 1 := by simp - have hx2 : toField x2 = toField x ^ 2 := by - dsimp [x2] - rw [toField_square, pow_two] - have hx3 : toField x3 = toField x ^ 3 := by - dsimp [x3] - simpa using toField_mul_pow x x2 x 2 1 hx2 hx1 - have hx5 : toField x5 = toField x ^ 5 := by - dsimp [x5] - simpa using toField_mul_pow x x3 x2 3 2 hx3 hx2 - have hx7 : toField x7 = toField x ^ 7 := by - dsimp [x7] - simpa using toField_mul_pow x x5 x2 5 2 hx5 hx2 - have hx14 : toField x14 = toField x ^ 14 := by - dsimp [x14] - rw [toField_square, hx7, ← pow_add] - have hx15 : toField x15 = toField x ^ 15 := by - dsimp [x15] - simpa using toField_mul_pow x x14 x 14 1 hx14 hx1 - have hacc1 : toField acc1 = toField x ^ 126 := by - dsimp [acc1] - simpa using toField_shift4Mul_pow x x7 x14 7 14 hx7 hx14 - have hacc2 : toField acc2 = toField x ^ 2031 := by - dsimp [acc2] - simpa using toField_shift4Mul_pow x acc1 x15 126 15 hacc1 hx15 - have hacc3 : toField acc3 = toField x ^ 32511 := by - dsimp [acc3] - simpa using toField_shift4Mul_pow x acc2 x15 2031 15 hacc2 hx15 - have hacc4 : toField acc4 = toField x ^ 520191 := by - dsimp [acc4] - simpa using toField_shift4Mul_pow x acc3 x15 32511 15 hacc3 hx15 - have hacc5 : toField acc5 = toField x ^ 8323071 := by - dsimp [acc5] - simpa using toField_shift4Mul_pow x acc4 x15 520191 15 hacc4 hx15 - have hacc6 : toField acc6 = toField x ^ 133169151 := by - dsimp [acc6] - simpa using toField_shift4Mul_pow x acc5 x15 8323071 15 hacc5 hx15 - have hfinal := toField_shift4Mul_pow x acc6 x15 133169151 15 hacc6 hx15 - simpa [invExponent, KoalaBear.fieldSize] using hfinal + exact toField_pow x invExponent private theorem toField_inv_raw (x : Field) : toField (inv x) = (toField x)⁻¹ := by rw [toField_inv_pow] From 8d554437785c167135726d69e1eebadda05154d0 Mon Sep 17 00:00:00 2001 From: Georgios Raikos <57717352+graikos@users.noreply.github.com> Date: Wed, 24 Jun 2026 13:33:18 +0000 Subject: [PATCH 06/31] chore(fields): doc changes --- CompPoly/Fields/README.md | 6 +++++- docs/wiki/binary-fields-and-ntt.md | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CompPoly/Fields/README.md b/CompPoly/Fields/README.md index ef884b31..93b3c868 100644 --- a/CompPoly/Fields/README.md +++ b/CompPoly/Fields/README.md @@ -8,7 +8,9 @@ This directory contains formally verified field infrastructure used in zero-know |--------|-------------| | **Basic.lean** | `NonBinaryField` type class (char ≠ 2), polynomial composition lemmas (`coeffs_of_comp_minus_x`, `comp_x_square_coeff`). | | **PrattCertificate.lean** | Lucas test for primality and Pratt certificate infrastructure (`PrattCertificate`, `PrattCertificate'`) for proving concrete primality goals. | -| **BabyBear.lean** | \(2^{31} - 2^{27} + 1\) — Risc Zero. | +| **BabyBear.lean** | Facade for BabyBear modules, re-exporting the canonical field and fast native-word implementation. | +| **BabyBear/Basic.lean** | \(2^{31} - 2^{27} + 1\) — Risc Zero. | +| **BabyBear/Fast.lean** | Native `UInt32` Montgomery-residue operations for BabyBear, with conversion and operation equivalence statements against `BabyBear.Field`. | | **BLS12_377.lean** | Scalar field of BLS12-377 (253-bit, 2-adicity 47) — Zexe. | | **BLS12_381.lean** | Scalar field of BLS12-381 (253-bit, 2-adicity 47). | | **BN254.lean** | Scalar field of BN254 curve. | @@ -17,6 +19,8 @@ This directory contains formally verified field infrastructure used in zero-know | **KoalaBear/Basic.lean** | \(2^{31} - 2^{24} + 1\) — lean Ethereum spec. | | **KoalaBear/Fast.lean** | Native `UInt32` Montgomery-residue operations for KoalaBear, with conversion and operation equivalence statements against `KoalaBear.Field`. | | **Mersenne.lean** | \(2^{31} - 1\) — Circle STARKs. | +| **Montgomery/Basic.lean** | Radix-generic Montgomery reduction, field-agnostic number theory shared by the fast prime fields. | +| **Montgomery/Native32.lean** | `UInt32 × UInt64`, radix `R = 2^32` word-level Montgomery bridge over the generic core. | | **Secp256k1.lean** | Base and scalar fields for the Secp256k1 curve (used in Bitcoin/Ethereum). | ## Binary-field modules diff --git a/docs/wiki/binary-fields-and-ntt.md b/docs/wiki/binary-fields-and-ntt.md index 958477d6..a0e4f0dc 100644 --- a/docs/wiki/binary-fields-and-ntt.md +++ b/docs/wiki/binary-fields-and-ntt.md @@ -12,9 +12,14 @@ CompPoly/Fields/ BabyBear/ Basic.lean Fast.lean + Fast/ (Prelude.lean, Montgomery.lean, Convert.lean) KoalaBear/ Basic.lean Fast.lean + Fast/ (Prelude.lean, Montgomery.lean, Convert.lean) + Montgomery/ + Basic.lean + Native32.lean Binary/ Common.lean BF128Ghash/ From f5eb442b5934428edef50b1c7986b8420f2a5565 Mon Sep 17 00:00:00 2001 From: Georgios Raikos <57717352+graikos@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:45:00 +0000 Subject: [PATCH 07/31] feat(fields): dedup & shared code for u32 fields (KoalaBear/BabyBear) --- CompPoly.lean | 1 + CompPoly/Fields/BabyBear/Fast.lean | 496 ++------- CompPoly/Fields/BabyBear/Fast/Convert.lean | 196 +--- CompPoly/Fields/BabyBear/Fast/Montgomery.lean | 144 +-- CompPoly/Fields/BabyBear/Fast/Prelude.lean | 109 +- CompPoly/Fields/KoalaBear/Fast.lean | 496 ++------- CompPoly/Fields/KoalaBear/Fast/Convert.lean | 196 +--- .../Fields/KoalaBear/Fast/Montgomery.lean | 144 +-- CompPoly/Fields/KoalaBear/Fast/Prelude.lean | 109 +- CompPoly/Fields/Montgomery/Native32Field.lean | 996 ++++++++++++++++++ CompPoly/Fields/README.md | 5 +- docs/wiki/binary-fields-and-ntt.md | 1 + 12 files changed, 1411 insertions(+), 1482 deletions(-) create mode 100644 CompPoly/Fields/Montgomery/Native32Field.lean diff --git a/CompPoly.lean b/CompPoly.lean index 327c1123..61e7a4cd 100644 --- a/CompPoly.lean +++ b/CompPoly.lean @@ -113,6 +113,7 @@ import CompPoly.Fields.KoalaBear.Fast.Prelude import CompPoly.Fields.Mersenne import CompPoly.Fields.Montgomery.Basic import CompPoly.Fields.Montgomery.Native32 +import CompPoly.Fields.Montgomery.Native32Field import CompPoly.Fields.PrattCertificate import CompPoly.Fields.Secp256k1 import CompPoly.LinearAlgebra.Dense diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index a35854c7..2a313148 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -5,536 +5,184 @@ Authors: Valerii Huhnin, Georgios Raikos -/ import CompPoly.Fields.BabyBear.Fast.Convert -import Mathlib.Algebra.Field.TransferInstance /-! # Fast BabyBear Field Operations -This module provides a native-word implementation of BabyBear arithmetic as a sidecar -to the canonical `BabyBear.Field := ZMod BabyBear.fieldSize` model. +A native-word implementation of BabyBear arithmetic as a sidecar to the canonical +`BabyBear.Field := ZMod BabyBear.fieldSize` model. Fast values are stored as Montgomery +`UInt32` residues below `BabyBear.fieldSize`, representing `x * 2^32` modulo the prime. -Fast field values are stored as Montgomery `UInt32` residues below `BabyBear.fieldSize`, -representing `x * 2^32` modulo the BabyBear prime. Addition, subtraction, and -negation operate directly on Montgomery words; multiplication uses Montgomery -reduction on a native `UInt64` product. - -Supporting layers live in sibling modules: field basics in -`CompPoly.Fields.BabyBear.Fast.Prelude`, Montgomery reduction in -`CompPoly.Fields.BabyBear.Fast.Montgomery`, and conversions/casts in -`CompPoly.Fields.BabyBear.Fast.Convert`. This module defines the field operations, -their instances, and the `toField` bridge to `BabyBear.Field`. +The operations, their `Field`/`CommRing`/`NonBinaryField` instances, the `toField` bridge, +and all correctness theorems are shared across every fast 32-bit-word field; they live once +in `CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance +in `CompPoly.Fields.BabyBear.Fast.Prelude`. Because `Field := Native32.FastField BabyBear.Field`, +the generic algebraic instances resolve here automatically. This module re-exports the +named operations and `simp` lemmas at the BabyBear instance. -/ namespace BabyBear namespace Fast +open Montgomery.Native32 + /-- Fast modular addition in Montgomery form. -/ @[inline] -def add (x y : Field) : Field := - reduceUInt32Lt2Modulus (x.val + y.val) (by - rw [UInt32.toNat_add] - exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by omega)) +def add (x y : Field) : Field := Montgomery.Native32.add x y /-- Fast modular negation in Montgomery form. -/ @[inline] -def neg (x : Field) : Field := - if hx : x.val = 0 then - zero - else - ⟨modulus - x.val, by - have hle : x.val ≤ modulus := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - exact Nat.le_of_lt x.property - rw [UInt32.toNat_sub_of_le _ _ hle, modulus_toNat] - have hxpos : 0 < x.val.toNat := by - apply Nat.pos_of_ne_zero - intro hzero - apply hx - apply UInt32.toNat_inj.mp - simpa using hzero - omega⟩ +def neg (x : Field) : Field := Montgomery.Native32.neg x /-- Fast modular subtraction in Montgomery form. -/ @[inline] -def sub (x y : Field) : Field := - if hyx : y.val ≤ x.val then - ⟨x.val - y.val, by - rw [UInt32.toNat_sub_of_le _ _ hyx] - omega⟩ - else - ⟨x.val + modulus - y.val, by - have hsum_lt : x.val.toNat + BabyBear.fieldSize < UInt32.size := by - have htwo := fieldSize_add_fieldSize_lt_uint32Size - omega - have hsum_eq : (x.val + modulus).toNat = x.val.toNat + BabyBear.fieldSize := by - rw [UInt32.toNat_add, modulus_toNat, Nat.mod_eq_of_lt hsum_lt] - have hyle : y.val ≤ x.val + modulus := by - rw [UInt32.le_iff_toNat_le, hsum_eq] - omega - rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] - have hyxNat : ¬y.val.toNat ≤ x.val.toNat := by - intro hle - apply hyx - rw [UInt32.le_iff_toNat_le] - exact hle - omega⟩ +def sub (x y : Field) : Field := Montgomery.Native32.sub x y /-- Fast modular multiplication in Montgomery form. -/ @[inline] -def mul (x y : Field) : Field := - montgomeryReduceBounded (x.val.toUInt64 * y.val.toUInt64) (by - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, fieldSize_lt_uint32Size, fieldSize_pos]) +def mul (x y : Field) : Field := Montgomery.Native32.mul x y /-- Fast squaring. -/ @[inline] -def square (x : Field) : Field := - mul x x +def square (x : Field) : Field := Montgomery.Native32.square x /-- Exponentiation over the fast representation using repeated squaring. -/ @[inline] -def pow (x : Field) (n : Nat) : Field := - @npowBinRec Field ⟨one⟩ ⟨mul⟩ n x +def pow (x : Field) (n : Nat) : Field := Montgomery.Native32.pow x n /-- Fermat exponent used for inversion in the BabyBear prime field. -/ -def invExponent : Nat := BabyBear.fieldSize - 2 +def invExponent : Nat := Montgomery.Native32.invExponent (F := BabyBear.Field) -/-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`), -by binary exponentiation (`pow`). -/ +/-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ @[inline] -def inv (x : Field) : Field := - pow x invExponent +def inv (x : Field) : Field := Montgomery.Native32.inv x /-- Division through inversion and fast multiplication. -/ @[inline] -def div (x y : Field) : Field := - mul x (inv y) - -instance instZeroField : Zero Field where - zero := zero - -instance instOneField : One Field where - one := one - -instance instAddField : Add Field where - add := add - -instance instNegField : Neg Field where - neg := neg - -instance instSubField : Sub Field where - sub := sub - -instance instMulField : Mul Field where - mul := mul - -instance instInvField : Inv Field where - inv := inv - -instance instDivField : Div Field where - div := div - -instance instNatCastField : NatCast Field where - natCast := ofNat - -instance instIntCastField : IntCast Field where - intCast := ofInt - -instance instNatSMulField : SMul Nat Field where - smul n x := ofNat n * x - -instance instIntSMulField : SMul Int Field where - smul n x := ofInt n * x +def div (x y : Field) : Field := Montgomery.Native32.div x y -instance instPowFieldNat : Pow Field Nat where - pow := pow - -instance instPowFieldInt : Pow Field Int where - pow x n := - match n with - | Int.ofNat k => pow x k - | Int.negSucc k => pow (inv x) (k + 1) - -instance instNNRatCastField : NNRatCast Field where - nnratCast q := ofField (q : BabyBear.Field) - -instance instRatCastField : RatCast Field where - ratCast q := ofField (q : BabyBear.Field) - -instance instNNRatSMulField : SMul ℚ≥0 Field where - smul q x := ofField (q • toField x) - -instance instRatSMulField : SMul ℚ Field where - smul q x := ofField (q • toField x) +/-- Ring equivalence between the fast Montgomery representation and canonical `BabyBear.Field`. -/ +def ringEquiv : Field ≃+* BabyBear.Field := + Montgomery.Native32.ringEquiv (F := BabyBear.Field) /-- Converting from the canonical field to fast form and back is the identity. -/ @[simp] -theorem toField_ofField (x : BabyBear.Field) : toField (ofField x) = x := by - unfold ofField - rw [toField_ofCanonicalNat] - exact ZMod.natCast_zmod_val x +theorem toField_ofField (x : BabyBear.Field) : toField (ofField x) = x := + Montgomery.Native32.toField_ofField (F := BabyBear.Field) x /-- Converting from fast form to the canonical field and back is the identity. -/ @[simp] -theorem ofField_toField (x : Field) : ofField (toField x) = x := by - apply Subtype.ext - apply UInt32.toNat_inj.mp - apply nat_eq_of_field_eq - · exact (ofField (toField x)).property - · exact x.property - · rw [raw_cast_eq_toField_mul] - rw [toField_ofField] - rw [raw_cast_eq_toField_mul] +theorem ofField_toField (x : Field) : ofField (toField x) = x := + Montgomery.Native32.ofField_toField x /-- The canonical-field interpretation distinguishes fast BabyBear values. -/ -theorem toField_injective : Function.Injective toField := - Function.LeftInverse.injective ofField_toField +theorem toField_injective : Function.Injective (toField : Field → BabyBear.Field) := + Montgomery.Native32.toField_injective (F := BabyBear.Field) /-- `toField` maps fast zero to canonical zero. -/ @[simp] -theorem toField_zero : toField (0 : Field) = 0 := by - rw [toField_eq_raw_mul_inv] - change ((0 : Nat) : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ = 0 - exact zero_mul _ +theorem toField_zero : toField (0 : Field) = 0 := + Montgomery.Native32.toField_zero (F := BabyBear.Field) /-- `toField` maps fast one to canonical one. -/ @[simp] -theorem toField_one : toField (1 : Field) = 1 := by - rw [toField_eq_raw_mul_inv] - change (rModModulus.toNat : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹ = 1 - rw [rModModulus_cast] - exact mul_inv_cancel₀ uint32Size_ne_zero_in_field +theorem toField_one : toField (1 : Field) = 1 := + Montgomery.Native32.toField_one (F := BabyBear.Field) /-- Fast addition agrees with addition in the canonical BabyBear field. -/ @[simp] -theorem toField_add (x y : Field) : toField (x + y) = toField x + toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] - unfold instAddField add - have hred := reduceUInt32Lt2Modulus_cast (x.val + y.val) (by - rw [UInt32.toNat_add] - exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by omega)) - change ((reduceUInt32Lt2ModulusRaw (x.val + y.val)).toNat : BabyBear.Field) = - ((x.val + y.val).toNat : BabyBear.Field) at hred - change ((reduceUInt32Lt2ModulusRaw (x.val + y.val)).toNat : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹ = - (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ + - (y.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - rw [hred] - rw [UInt32.toNat_add] - have hsum_lt : x.val.toNat + y.val.toNat < UInt32.size := by - nlinarith [x.property, y.property, fieldSize_add_fieldSize_lt_uint32Size] - rw [Nat.mod_eq_of_lt hsum_lt] - rw [Nat.cast_add] - ring +theorem toField_add (x y : Field) : toField (x + y) = toField x + toField y := + Montgomery.Native32.toField_add x y /-- Fast subtraction agrees with subtraction in the canonical BabyBear field. -/ @[simp] -theorem toField_sub (x y : Field) : toField (x - y) = toField x - toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] - by_cases hyx : y.val ≤ x.val - · have hsubval : (x - y : Field).val = x.val - y.val := by - change (sub x y).val = x.val - y.val - unfold sub - rw [dif_pos hyx] - rw [hsubval] - change (((x.val - y.val).toNat : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹) = - (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - - (y.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - rw [UInt32.toNat_sub_of_le _ _ hyx] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le] at hyx - exact hyx)] - ring - · have hsum_lt : x.val.toNat + BabyBear.fieldSize < UInt32.size := by - have htwo := fieldSize_add_fieldSize_lt_uint32Size - omega - have hsum_eq : (x.val + modulus).toNat = x.val.toNat + BabyBear.fieldSize := by - rw [UInt32.toNat_add, modulus_toNat, Nat.mod_eq_of_lt hsum_lt] - have hyle : y.val ≤ x.val + modulus := by - rw [UInt32.le_iff_toNat_le, hsum_eq] - omega - have hsubval : (x - y : Field).val = x.val + modulus - y.val := by - change (sub x y).val = x.val + modulus - y.val - unfold sub - rw [dif_neg hyx] - rw [hsubval] - change (((x.val + modulus - y.val).toNat : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹) = - (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - - (y.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, hsum_eq] at hyle - exact hyle)] - rw [Nat.cast_add, ZMod.natCast_self] - ring +theorem toField_sub (x y : Field) : toField (x - y) = toField x - toField y := + Montgomery.Native32.toField_sub x y /-- Fast negation agrees with negation in the canonical BabyBear field. -/ @[simp] -theorem toField_neg (x : Field) : toField (-x) = -toField x := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x] - by_cases hx : x.val = 0 - · have hnegval : (-x : Field).val = zero.val := by - change (neg x).val = zero.val - unfold neg - rw [dif_pos hx] - rw [hnegval] - change ((zero.val.toNat : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹) = - -((x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹) - have hxNat : x.val.toNat = 0 := by - simpa using congrArg UInt32.toNat hx - rw [hxNat] - change ((0 : Nat) : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ = - -(((0 : Nat) : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹) - simp - · have hle : x.val ≤ modulus := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - exact Nat.le_of_lt x.property - have hnegval : (-x : Field).val = modulus - x.val := by - change (neg x).val = modulus - x.val - unfold neg - rw [dif_neg hx] - rw [hnegval] - change (((modulus - x.val).toNat : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹) = - -((x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹) - rw [UInt32.toNat_sub_of_le _ _ hle, modulus_toNat] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, modulus_toNat] at hle - exact hle)] - rw [ZMod.natCast_self] - ring +theorem toField_neg (x : Field) : toField (-x) = -toField x := + Montgomery.Native32.toField_neg x /-- Fast multiplication agrees with multiplication in the canonical BabyBear field. -/ @[simp] -theorem toField_mul (x y : Field) : toField (x * y) = toField x * toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] - unfold instMulField mul - have hred := montgomeryReduceBounded_cast (x.val.toUInt64 * y.val.toUInt64) (by - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, fieldSize_lt_uint32Size, fieldSize_pos]) - change ((montgomeryReduceBoundedRaw (x.val.toUInt64 * y.val.toUInt64)).toNat : - BabyBear.Field) = - ((x.val.toUInt64 * y.val.toUInt64).toNat : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (x.val.toUInt64 * y.val.toUInt64)).toNat : - BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ = - (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ * - ((y.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹) - rw [hred] - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - rw [Nat.cast_mul] - ring - -/-- Ring equivalence between the fast Montgomery representation and canonical `BabyBear.Field`. -/ -def ringEquiv : Field ≃+* BabyBear.Field where - toFun := toField - invFun := ofField - left_inv := ofField_toField - right_inv := toField_ofField - map_add' := toField_add - map_mul' := toField_mul +theorem toField_mul (x y : Field) : toField (x * y) = toField x * toField y := + Montgomery.Native32.toField_mul x y /-- Applying `ringEquiv` is the same as interpreting a fast value canonically. -/ @[simp] -theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := rfl +theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := + Montgomery.Native32.ringEquiv_apply x /-- Applying the inverse `ringEquiv` is conversion into fast Montgomery form. -/ @[simp] -theorem ringEquiv_symm_apply (x : BabyBear.Field) : ringEquiv.symm x = ofField x := rfl - -private theorem mul_assoc_field (x y z : Field) : (x * y) * z = x * (y * z) := by - apply toField_injective - rw [toField_mul, toField_mul, toField_mul, toField_mul] - ring - -private theorem pow_succ (x : Field) (n : Nat) : pow x (n + 1) = pow x n * x := by - unfold pow - letI : Semigroup Field := { - mul := (· * ·) - mul_assoc := mul_assoc_field - } - exact npowBinRec_succ n x +theorem ringEquiv_symm_apply (x : BabyBear.Field) : ringEquiv.symm x = ofField x := + Montgomery.Native32.ringEquiv_symm_apply (F := BabyBear.Field) x /-- Fast squaring agrees with multiplication by itself in the canonical field. -/ @[simp] -theorem toField_square (x : Field) : toField (square x) = toField x * toField x := by - change toField (x * x) = toField x * toField x - rw [toField_mul] - -/-- Fast natural-power computation agrees with powers in the canonical field. -/ -@[simp] -theorem toField_pow (x : Field) (n : Nat) : toField (pow x n) = toField x ^ n := by - induction n with - | zero => - unfold pow - rw [npowBinRec_zero] - rw [toField_one] - simp - | succ n ih => - rw [pow_succ, toField_mul, ih, _root_.pow_succ] - -private theorem toField_inv_pow (x : Field) : - toField (inv x) = toField x ^ invExponent := by - unfold inv - exact toField_pow x invExponent - -private theorem toField_inv_raw (x : Field) : toField (inv x) = (toField x)⁻¹ := by - rw [toField_inv_pow] - by_cases hx : toField x = 0 - · rw [hx] - simp [invExponent, BabyBear.fieldSize] - · simpa [invExponent] using (BabyBear.inv_eq_pow (toField x) hx).symm +theorem toField_square (x : Field) : toField (square x) = toField x * toField x := + Montgomery.Native32.toField_square x /-- Fast inversion agrees with inversion in the canonical BabyBear field. -/ @[simp] -theorem toField_inv (x : Field) : toField x⁻¹ = (toField x)⁻¹ := by - change toField (inv x) = (toField x)⁻¹ - exact toField_inv_raw x - -private theorem toField_mul_raw (x y : Field) : toField (mul x y) = toField x * toField y := by - change toField (x * y) = toField x * toField y - exact toField_mul x y - -private theorem toField_div_mul_inv (x y : Field) : - toField (div x y) = toField x * toField (inv y) := by - unfold div - exact toField_mul_raw x (inv y) +theorem toField_inv (x : Field) : toField x⁻¹ = (toField x)⁻¹ := + Montgomery.Native32.toField_inv x /-- Fast division agrees with division in the canonical BabyBear field. -/ @[simp] -theorem toField_div (x y : Field) : toField (x / y) = toField x / toField y := by - change toField (div x y) = toField x / toField y - have h : ∀ a b c : BabyBear.Field, c = b⁻¹ → a * c = a / b := by - intro a b c hc - rw [hc] - rfl - exact (toField_div_mul_inv x y).trans - (h (toField x) (toField y) (toField (inv y)) (toField_inv_raw y)) +theorem toField_div (x y : Field) : toField (x / y) = toField x / toField y := + Montgomery.Native32.toField_div x y /-- Natural casts into fast form agree with natural casts into the canonical field. -/ @[simp] -theorem toField_natCast (n : Nat) : toField (n : Field) = (n : BabyBear.Field) := by - change toField (ofNat n) = (n : BabyBear.Field) - unfold ofNat - rw [toField_ofCanonicalNat] - rw [ZMod.natCast_eq_natCast_iff] - exact Nat.mod_modEq _ _ +theorem toField_natCast (n : Nat) : toField (n : Field) = (n : BabyBear.Field) := + Montgomery.Native32.toField_natCast n /-- Integer casts into fast form agree with integer casts into the canonical field. -/ @[simp] -theorem toField_intCast (n : Int) : toField (n : Field) = (n : BabyBear.Field) := by - change toField (ofInt n) = (n : BabyBear.Field) - unfold ofInt - rw [toField_ofField] +theorem toField_intCast (n : Int) : toField (n : Field) = (n : BabyBear.Field) := + Montgomery.Native32.toField_intCast n /-- Natural scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_nsmul (n : Nat) (x : Field) : toField (n • x) = n • toField x := by - change toField ((n : Field) * x) = n • toField x - rw [toField_mul, toField_natCast] - rw [nsmul_eq_mul] +theorem toField_nsmul (n : Nat) (x : Field) : toField (n • x) = n • toField x := + Montgomery.Native32.toField_nsmul n x /-- Integer scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_zsmul (n : Int) (x : Field) : toField (n • x) = n • toField x := by - change toField ((n : Field) * x) = n • toField x - rw [toField_mul, toField_intCast] - rw [zsmul_eq_mul] +theorem toField_zsmul (n : Int) (x : Field) : toField (n • x) = n • toField x := + Montgomery.Native32.toField_zsmul n x /-- Natural powers through the `Pow` instance are preserved by `toField`. -/ @[simp] -theorem toField_npow (x : Field) (n : Nat) : toField (x ^ n) = toField x ^ n := by - change toField (pow x n) = toField x ^ n - rw [toField_pow] +theorem toField_npow (x : Field) (n : Nat) : toField (x ^ n) = toField x ^ n := + Montgomery.Native32.toField_npow x n /-- Integer powers through the `Pow` instance are preserved by `toField`. -/ @[simp] -theorem toField_zpow (x : Field) (n : Int) : toField (x ^ n) = toField x ^ n := by - cases n with - | ofNat n => - change toField (pow x n) = toField x ^ (Int.ofNat n) - rw [toField_pow] - exact (zpow_natCast (toField x) n).symm - | negSucc n => - change toField (pow (inv x) (n + 1)) = toField x ^ (Int.negSucc n) - have hinv : toField (inv x) = (toField x)⁻¹ := by - change toField x⁻¹ = (toField x)⁻¹ - rw [toField_inv] - rw [toField_pow, hinv, zpow_negSucc, inv_pow] +theorem toField_zpow (x : Field) (n : Int) : toField (x ^ n) = toField x ^ n := + Montgomery.Native32.toField_zpow x n /-- Nonnegative rational casts into fast form agree with canonical-field casts. -/ @[simp] -theorem toField_nnratCast (q : ℚ≥0) : toField (q : Field) = (q : BabyBear.Field) := by - change toField (ofField (q : BabyBear.Field)) = (q : BabyBear.Field) - rw [toField_ofField] +theorem toField_nnratCast (q : ℚ≥0) : toField (q : Field) = (q : BabyBear.Field) := + Montgomery.Native32.toField_nnratCast q /-- Rational casts into fast form agree with canonical-field casts. -/ @[simp] -theorem toField_ratCast (q : ℚ) : toField (q : Field) = (q : BabyBear.Field) := by - change toField (ofField (q : BabyBear.Field)) = (q : BabyBear.Field) - rw [toField_ofField] +theorem toField_ratCast (q : ℚ) : toField (q : Field) = (q : BabyBear.Field) := + Montgomery.Native32.toField_ratCast q /-- Nonnegative rational scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • toField x := by - change toField (ofField (q • toField x)) = q • toField x - rw [toField_ofField] +theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • toField x := + Montgomery.Native32.toField_nnqsmul q x /-- Rational scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := by - change toField (ofField (q • toField x)) = q • toField x - rw [toField_ofField] - -/-- Field instance transferred from canonical BabyBear through `toField`. -/ -instance (priority := low) instField : _root_.Field Field := - toField_injective.field toField - toField_zero - toField_one - toField_add - toField_mul - toField_neg - toField_sub - toField_inv - toField_div - toField_nsmul - toField_zsmul - toField_nnqsmul - toField_qsmul - toField_npow - toField_zpow - toField_natCast - toField_intCast - toField_nnratCast - toField_ratCast - -/-- Commutative-ring instance inherited from the transferred field structure. -/ -instance (priority := low) instCommRing : CommRing Field := by - infer_instance - -/-- Fast BabyBear is a non-binary field. -/ -instance (priority := low) instNonBinaryField : NonBinaryField Field where - char_neq_2 := by - change ((2 : Nat) : Field) ≠ 0 - intro h - exact NonBinaryField.char_neq_2 (F := BabyBear.Field) (by - calc - (2 : BabyBear.Field) = toField ((2 : Nat) : Field) := (toField_natCast 2).symm - _ = toField (0 : Field) := congrArg toField h - _ = 0 := toField_zero) +theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := + Montgomery.Native32.toField_qsmul q x end Fast end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean index 2233e54c..f02556e1 100644 --- a/CompPoly/Fields/BabyBear/Fast/Convert.lean +++ b/CompPoly/Fields/BabyBear/Fast/Convert.lean @@ -10,241 +10,115 @@ import CompPoly.Fields.BabyBear.Fast.Montgomery # Fast BabyBear Field — Conversions Conversions between the fast Montgomery representation and the canonical -`BabyBear.Field` / `Nat` views (`ofField`, `toField`, `ofNat`, `toNat`, …), the -`zero`/`one` constants, and the round-trip cast lemmas relating them. The field -operations and ring/field instances build on this module. +`BabyBear.Field` / `Nat` views, re-exported from the shared implementation in +`CompPoly.Fields.Montgomery.Native32Field` at the BabyBear instance. -/ namespace BabyBear namespace Fast +open Montgomery.Native32 + /-- Build a fast element from a canonical natural representative. -/ @[inline] -def ofCanonicalNat (n : Nat) (_h : n < BabyBear.fieldSize) : Field := - montgomeryReduceBounded (UInt64.ofNat n * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans _h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) - rw [hnmod] - have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) +def ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : Field := + Montgomery.Native32.ofCanonicalNat (F := BabyBear.Field) n h /-- Reduce a `UInt64` modulo the BabyBear prime and return a Montgomery fast element. -/ @[inline] def reduceUInt64 (x : UInt64) : Field := - let y := x % modulus64 - montgomeryReduceBounded (y * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : (x % modulus64).toNat < BabyBear.fieldSize := by - rw [UInt64.toNat_mod, modulus64_toNat] - exact Nat.mod_lt _ fieldSize_pos - have hprod : (x % modulus64).toNat * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) + Montgomery.Native32.reduceUInt64 (F := BabyBear.Field) x /-- The zero fast BabyBear element. -/ -def zero : Field := ⟨0, by decide⟩ +def zero : Field := Montgomery.Native32.zero (F := BabyBear.Field) /-- The one fast BabyBear element. -/ -def one : Field := ⟨rModModulus, by decide⟩ +def one : Field := Montgomery.Native32.one (F := BabyBear.Field) /-- Convert a natural number into fast Montgomery representation. -/ @[inline] def ofNat (n : Nat) : Field := - ofCanonicalNat (n % BabyBear.fieldSize) (Nat.mod_lt _ fieldSize_pos) + Montgomery.Native32.ofNat (F := BabyBear.Field) n /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (x : UInt32) : Field := - reduceUInt64 x.toUInt64 + Montgomery.Native32.ofUInt32 (F := BabyBear.Field) x /-- Convert from the canonical `ZMod` BabyBear field into fast Montgomery form. -/ @[inline] def ofField (x : BabyBear.Field) : Field := - ofCanonicalNat x.val (ZMod.val_lt x) + Montgomery.Native32.ofField (F := BabyBear.Field) x /-- Convert an integer into fast Montgomery representation. -/ @[inline] def ofInt (n : Int) : Field := - ofField (n : BabyBear.Field) + Montgomery.Native32.ofInt (F := BabyBear.Field) n /-- Convert a fast element to its canonical native-word representative. -/ @[inline] def toCanonicalUInt32 (x : Field) : UInt32 := - raw (montgomeryReduceBounded x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, fieldSize_pos])) + Montgomery.Native32.toCanonicalUInt32 x /-- Convert a fast BabyBear element to its canonical natural representative. -/ @[inline] def toNat (x : Field) : Nat := - (toCanonicalUInt32 x).toNat + Montgomery.Native32.toNat x /-- Convert a fast BabyBear element to the canonical `ZMod` BabyBear field. -/ @[inline] def toField (x : Field) : BabyBear.Field := - (toNat x : BabyBear.Field) + Montgomery.Native32.toField x -theorem toNat_lt_fieldSize (x : Field) : toNat x < BabyBear.fieldSize := by - unfold toNat toCanonicalUInt32 raw - change (montgomeryReduceBoundedRaw x.val.toUInt64).toNat < BabyBear.fieldSize - exact montgomeryReduceBoundedRaw_lt x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, fieldSize_pos]) +theorem toNat_lt_fieldSize (x : Field) : toNat x < BabyBear.fieldSize := + Montgomery.Native32.toNat_lt_fieldSize x theorem toField_eq_raw_mul_inv (x : Field) : - toField x = - (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by - unfold toField toNat toCanonicalUInt32 raw - have hred := montgomeryReduceBounded_cast x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, fieldSize_pos]) - change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : BabyBear.Field) = - (x.val.toUInt64.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : BabyBear.Field) = - (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - rw [hred] - rw [UInt32.toNat_toUInt64] + toField x = (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := + Montgomery.Native32.toField_eq_raw_mul_inv x theorem raw_cast_eq_toField_mul (x : Field) : - (x.val.toNat : BabyBear.Field) = - toField x * (UInt32.size : BabyBear.Field) := by - rw [toField_eq_raw_mul_inv] - rw [mul_assoc] - rw [inv_mul_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] + (x.val.toNat : BabyBear.Field) = toField x * (UInt32.size : BabyBear.Field) := + Montgomery.Native32.raw_cast_eq_toField_mul x theorem nat_eq_of_field_eq {a b : Nat} (ha : a < BabyBear.fieldSize) (hb : b < BabyBear.fieldSize) (h : (a : BabyBear.Field) = (b : BabyBear.Field)) : a = b := - Montgomery.natCast_inj ha hb h + Montgomery.Native32.nat_eq_of_field_eq (F := BabyBear.Field) ha hb h theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < BabyBear.fieldSize) : ((ofCanonicalNat n h).val.toNat : BabyBear.Field) = - (n : BabyBear.Field) * (UInt32.size : BabyBear.Field) := by - unfold ofCanonicalNat - have hred := montgomeryReduceBounded_cast - (UInt64.ofNat n * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) - rw [hnmod] - have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) - change ((montgomeryReduceBoundedRaw - (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : BabyBear.Field) = - ((UInt64.ofNat n * r2ModModulus.toUInt64).toNat : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw - (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : BabyBear.Field) = - (n : BabyBear.Field) * (UInt32.size : BabyBear.Field) - rw [hred] - simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) - rw [hnmod] - have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - rw [Nat.cast_mul, r2ModModulus_cast] - rw [pow_two] - rw [mul_assoc (n : BabyBear.Field) ((UInt32.size : BabyBear.Field) * - (UInt32.size : BabyBear.Field)) ((UInt32.size : BabyBear.Field)⁻¹)] - rw [mul_assoc (UInt32.size : BabyBear.Field) (UInt32.size : BabyBear.Field) - ((UInt32.size : BabyBear.Field)⁻¹)] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - -theorem toField_ofCanonicalNat_aux (n : Nat) (h : n < BabyBear.fieldSize) : - toField (ofCanonicalNat n h) = (n : BabyBear.Field) := by - rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] + (n : BabyBear.Field) * (UInt32.size : BabyBear.Field) := + Montgomery.Native32.ofCanonicalNat_raw_cast (F := BabyBear.Field) n h theorem reduceUInt64_raw_cast (x : UInt64) : ((reduceUInt64 x).val.toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field) := by - unfold reduceUInt64 - let y := x % modulus64 - have hred := montgomeryReduceBounded_cast (y * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < BabyBear.fieldSize := by - rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] - exact Nat.mod_lt _ fieldSize_pos - have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) - change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : - BabyBear.Field) = - ((y * r2ModModulus.toUInt64).toNat : BabyBear.Field) * - (UInt32.size : BabyBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : - BabyBear.Field) = - (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field) - rw [hred] - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < BabyBear.fieldSize := by - rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] - exact Nat.mod_lt _ fieldSize_pos - have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - have hy_cast : (y.toNat : BabyBear.Field) = (x.toNat : BabyBear.Field) := by - rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] - rw [ZMod.natCast_eq_natCast_iff] - exact Nat.mod_modEq _ _ - rw [Nat.cast_mul, r2ModModulus_cast, hy_cast] - rw [pow_two] - rw [mul_assoc (x.toNat : BabyBear.Field) ((UInt32.size : BabyBear.Field) * - (UInt32.size : BabyBear.Field)) ((UInt32.size : BabyBear.Field)⁻¹)] - rw [mul_assoc (UInt32.size : BabyBear.Field) (UInt32.size : BabyBear.Field) - ((UInt32.size : BabyBear.Field)⁻¹)] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] + (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field) := + Montgomery.Native32.reduceUInt64_raw_cast x /-- Converting a canonical natural representative to fast form preserves its value. -/ @[simp] theorem toNat_ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : - toNat (ofCanonicalNat n h) = n := by - exact nat_eq_of_field_eq (toNat_lt_fieldSize _) h (toField_ofCanonicalNat_aux n h) + toNat (ofCanonicalNat n h) = n := + Montgomery.Native32.toNat_ofCanonicalNat n h /-- `ofCanonicalNat` embeds a canonical representative into the canonical field. -/ @[simp] theorem toField_ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : - toField (ofCanonicalNat n h) = (n : BabyBear.Field) := by - exact toField_ofCanonicalNat_aux n h + toField (ofCanonicalNat n h) = (n : BabyBear.Field) := + Montgomery.Native32.toField_ofCanonicalNat n h /-- Reducing a `UInt64` gives the canonical natural residue modulo BabyBear. -/ @[simp] theorem toNat_reduceUInt64 (x : UInt64) : - toNat (reduceUInt64 x) = x.toNat % BabyBear.fieldSize := by - apply nat_eq_of_field_eq (toNat_lt_fieldSize _) (Nat.mod_lt _ fieldSize_pos) - change toField (reduceUInt64 x) = ((x.toNat % BabyBear.fieldSize : Nat) : BabyBear.Field) - rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - rw [ZMod.natCast_eq_natCast_iff] - exact (Nat.mod_modEq _ _).symm + toNat (reduceUInt64 x) = x.toNat % BabyBear.fieldSize := + Montgomery.Native32.toNat_reduceUInt64 x /-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ @[simp] theorem toField_reduceUInt64 (x : UInt64) : - toField (reduceUInt64 x) = (x.toNat : BabyBear.Field) := by - rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] + toField (reduceUInt64 x) = (x.toNat : BabyBear.Field) := + Montgomery.Native32.toField_reduceUInt64 x end Fast end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean index 45b6958f..f798d171 100644 --- a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean +++ b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean @@ -5,177 +5,77 @@ Authors: Valerii Huhnin, Georgios Raikos -/ import CompPoly.Fields.BabyBear.Fast.Prelude -import CompPoly.Fields.Montgomery.Native32 /-! # Fast BabyBear Field — Montgomery Reduction -Montgomery-form constants (`montgomeryNegInv`, `rModModulus`, `r2ModModulus`) and the -native-word Montgomery reduction used by the fast BabyBear field. The radix-generic -number theory lives in `CompPoly.Fields.Montgomery.Basic` and the `R = 2^32` word bridge -in `CompPoly.Fields.Montgomery.Native32`; field basics live in -`CompPoly.Fields.BabyBear.Fast.Prelude`; conversions and field operations build on this -module. +The native-word Montgomery reducers specialized to BabyBear. The definitions and their +correctness proofs are shared across every fast 32-bit-word field; they live once in +`CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance +supplied in `CompPoly.Fields.BabyBear.Fast.Prelude`. This module just re-exports them at +the BabyBear instance. Because the shared definitions are `@[inline]`, fixing the instance +folds the constants to literals: the compiled code is identical to a hand-written +monomorphic reducer, with no `Mont32Field` dictionary at runtime. -/ namespace BabyBear namespace Fast -/-- `2^32 mod BabyBear.fieldSize`. This is the Montgomery representation of one. -/ -def rModModulus : UInt32 := 0x0FFFFFFE - -/-- `(2^32)^2 mod BabyBear.fieldSize`, used to enter Montgomery representation. -/ -def r2ModModulus : UInt32 := 0x45DDDDE3 - -/-- `-BabyBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ -def montgomeryNegInv : UInt32 := 0x77FFFFFF - -theorem r2ModModulus_lt_fieldSize : r2ModModulus.toNat < BabyBear.fieldSize := by - decide - -theorem rModModulus_lt_fieldSize : rModModulus.toNat < BabyBear.fieldSize := by - decide - -theorem rModModulus_cast : - (rModModulus.toNat : BabyBear.Field) = (UInt32.size : BabyBear.Field) := by - decide - -theorem r2ModModulus_cast : - (r2ModModulus.toNat : BabyBear.Field) = (UInt32.size : BabyBear.Field) ^ 2 := by - decide +open Montgomery.Native32 /-- Reduce a native word known to be below twice the BabyBear prime. -/ @[inline] def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := - if x < modulus then x else x - modulus + Montgomery.Native32.reduceUInt32Lt2ModulusRaw (F := BabyBear.Field) x theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : - (reduceUInt32Lt2ModulusRaw x).toNat < BabyBear.fieldSize := by - unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < modulus - · rw [if_pos hx] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx - · rw [if_neg hx] - have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - omega + (reduceUInt32Lt2ModulusRaw x).toNat < BabyBear.fieldSize := + Montgomery.Native32.reduceUInt32Lt2ModulusRaw_lt (F := BabyBear.Field) x h /-- Reduce a native word known to be below twice the BabyBear prime. -/ @[inline] def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : Field := - ⟨reduceUInt32Lt2ModulusRaw x, reduceUInt32Lt2ModulusRaw_lt x h⟩ + Montgomery.Native32.reduceUInt32Lt2Modulus (F := BabyBear.Field) x h theorem reduceUInt32Lt2Modulus_cast (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : ((reduceUInt32Lt2Modulus x h).val.toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) := by - change ((reduceUInt32Lt2ModulusRaw x).toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) - unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < modulus - · rw [if_pos hx] - · have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - rw [if_neg hx] - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, modulus_toNat] at hmod_le_x - exact hmod_le_x)] - simp + (x.toNat : BabyBear.Field) := + Montgomery.Native32.reduceUInt32Lt2Modulus_cast (F := BabyBear.Field) x h /-- Reduce a native word below `2^32` modulo the BabyBear prime. -/ @[inline] def reduceUInt32 (x : UInt32) : Field := - if hx : x < modulus then - ⟨x, by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx⟩ - else - let y := x - modulus - if hy : y < modulus then - ⟨y, by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy - exact hy⟩ - else - ⟨y - modulus, by - have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - have hy_eq : y.toNat = x.toNat - BabyBear.fieldSize := by - change (x - modulus).toNat = x.toNat - BabyBear.fieldSize - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - have hmod_le_y : modulus ≤ y := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy - exact Nat.le_of_not_gt hy - rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, modulus_toNat, hy_eq] - have hx_lt := UInt32.toNat_lt_size x - have hthree := uint32Size_lt_three_fieldSize - omega⟩ + Montgomery.Native32.reduceUInt32 (F := BabyBear.Field) x /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := - let m : UInt32 := x.toUInt32 * montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 - reduceUInt32Lt2ModulusRaw u + Montgomery.Native32.montgomeryReduceBoundedRaw (F := BabyBear.Field) x theorem montgomeryReduceBoundedRaw_lt (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : - (montgomeryReduceBoundedRaw x).toNat < BabyBear.fieldSize := by - unfold montgomeryReduceBoundedRaw - exact reduceUInt32Lt2ModulusRaw_lt _ - (Montgomery.Native32.u_lt_two_mul montgomeryNegInv modulus64 BabyBear.fieldSize - modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h) + (montgomeryReduceBoundedRaw x).toNat < BabyBear.fieldSize := + Montgomery.Native32.montgomeryReduceBoundedRaw_lt (F := BabyBear.Field) x h /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBounded (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : Field := - ⟨montgomeryReduceBoundedRaw x, montgomeryReduceBoundedRaw_lt x h⟩ + Montgomery.Native32.montgomeryReduceBounded (F := BabyBear.Field) x h theorem montgomeryReduceBounded_cast (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : ((montgomeryReduceBounded x h).val.toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := by - change ((montgomeryReduceBoundedRaw x).toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - unfold montgomeryReduceBoundedRaw - let m : UInt32 := x.toUInt32 * montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 - change ((reduceUInt32Lt2ModulusRaw u).toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - have hred := reduceUInt32Lt2Modulus_cast u - (Montgomery.Native32.u_lt_two_mul montgomeryNegInv modulus64 BabyBear.fieldSize - modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h) - change ((reduceUInt32Lt2ModulusRaw u).toNat : BabyBear.Field) = - (u.toNat : BabyBear.Field) at hred - rw [hred] - change (u.toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ - rw [show u.toNat = - (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * - BabyBear.fieldSize) / UInt32.size by - exact Montgomery.Native32.u_eq_nat montgomeryNegInv modulus64 BabyBear.fieldSize - modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h] - exact Montgomery.quotient_cast UInt32.size BabyBear.fieldSize montgomeryNegInv.toNat - (by decide) (by decide) uint32Size_ne_zero_in_field x.toNat + (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := + Montgomery.Native32.montgomeryReduceBounded_cast (F := BabyBear.Field) x h /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] def montgomeryReduce (x : UInt64) : Field := - let m : UInt32 := x.toUInt32 * montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 - reduceUInt32 u + Montgomery.Native32.montgomeryReduce (F := BabyBear.Field) x end Fast end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Prelude.lean index 3079201f..4ed200a1 100644 --- a/CompPoly/Fields/BabyBear/Fast/Prelude.lean +++ b/CompPoly/Fields/BabyBear/Fast/Prelude.lean @@ -5,77 +5,120 @@ Authors: Valerii Huhnin, Georgios Raikos -/ import CompPoly.Fields.BabyBear.Basic +import CompPoly.Fields.Montgomery.Native32Field /-! # Fast BabyBear Field — Basics -The native-word modulus and the `Field` carrier type for the fast BabyBear field, -plus the elementary numeric bounds reused throughout the Montgomery reduction layer. +The native-word constants and the `Field` carrier type for the fast BabyBear field. The +shared implementation lives in `CompPoly.Fields.Montgomery.Native32Field`; this module +supplies the per-field `Mont32Field` instance (the five word constants plus the +`decide`-checked numeric facts) and pins `Field := Native32.FastField BabyBear.Field`, so +the generic definitions, proofs, and algebraic instances specialize to BabyBear. -The Montgomery-form constants and reduction machinery live in -`CompPoly.Fields.BabyBear.Fast.Montgomery`; conversions in `...Fast.Convert`; the -field operations and instances in `CompPoly.Fields.BabyBear.Fast`. +The Montgomery reducers are re-exported in `CompPoly.Fields.BabyBear.Fast.Montgomery`; +conversions in `...Fast.Convert`; the field operations and instances in +`CompPoly.Fields.BabyBear.Fast`. -/ namespace BabyBear namespace Fast +open Montgomery.Native32 (Mont32Field FastField) + /-- BabyBear modulus as a native word. -/ def modulus : UInt32 := 0x78000001 /-- BabyBear modulus as a 64-bit word for modular reduction. -/ def modulus64 : UInt64 := 0x78000001 -/-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ -abbrev Field : Type := { x : UInt32 // x.toNat < BabyBear.fieldSize } +/-- `2^32 mod BabyBear.fieldSize`. This is the Montgomery representation of one. -/ +def rModModulus : UInt32 := 0x0FFFFFFE -instance : DecidableEq Field := inferInstance +/-- `(2^32)^2 mod BabyBear.fieldSize`, used to enter Montgomery representation. -/ +def r2ModModulus : UInt32 := 0x45DDDDE3 -/-- The raw Montgomery word backing a fast BabyBear element. -/ -@[inline] -def raw (x : Field) : UInt32 := x.val +/-- `-BabyBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ +def montgomeryNegInv : UInt32 := 0x77FFFFFF /-- The native `UInt32` modulus agrees with the mathematical BabyBear modulus. -/ -@[simp] theorem modulus_toNat : modulus.toNat = BabyBear.fieldSize := by - decide +@[simp] theorem modulus_toNat : modulus.toNat = BabyBear.fieldSize := by decide /-- The native `UInt64` modulus agrees with the mathematical BabyBear modulus. -/ -@[simp] theorem modulus64_toNat : modulus64.toNat = BabyBear.fieldSize := by - decide +@[simp] theorem modulus64_toNat : modulus64.toNat = BabyBear.fieldSize := by decide + +theorem fieldSize_pos : 0 < BabyBear.fieldSize := by decide -theorem fieldSize_pos : 0 < BabyBear.fieldSize := by - decide +theorem two_lt_fieldSize : 2 < BabyBear.fieldSize := by decide -theorem fieldSize_lt_uint32Size : BabyBear.fieldSize < UInt32.size := by - decide +theorem fieldSize_lt_uint32Size : BabyBear.fieldSize < UInt32.size := by decide theorem fieldSize_add_fieldSize_lt_two64 : - BabyBear.fieldSize + BabyBear.fieldSize < 2 ^ 64 := by - decide + BabyBear.fieldSize + BabyBear.fieldSize < 2 ^ 64 := by decide theorem fieldSize_add_fieldSize_lt_uint32Size : - BabyBear.fieldSize + BabyBear.fieldSize < UInt32.size := by - decide + BabyBear.fieldSize + BabyBear.fieldSize < UInt32.size := by decide theorem fieldSize_mul_fieldSize_lt_two64 : - BabyBear.fieldSize * BabyBear.fieldSize < 2 ^ 64 := by - decide + BabyBear.fieldSize * BabyBear.fieldSize < 2 ^ 64 := by decide theorem uint32Size_lt_three_fieldSize : - UInt32.size < BabyBear.fieldSize + BabyBear.fieldSize + BabyBear.fieldSize := by - decide + UInt32.size < BabyBear.fieldSize + BabyBear.fieldSize + BabyBear.fieldSize := by decide theorem fieldSize_mul_uint32Size_lt_two64 : - BabyBear.fieldSize * UInt32.size < 2 ^ 64 := by - decide + BabyBear.fieldSize * UInt32.size < 2 ^ 64 := by decide theorem two_fieldSize_mul_uint32Size_lt_two64 : - 2 * BabyBear.fieldSize * UInt32.size < 2 ^ 64 := by - decide + 2 * BabyBear.fieldSize * UInt32.size < 2 ^ 64 := by decide theorem uint32Size_ne_zero_in_field : - (UInt32.size : BabyBear.Field) ≠ 0 := by - decide + (UInt32.size : BabyBear.Field) ≠ 0 := by decide + +theorem rModModulus_lt_fieldSize : rModModulus.toNat < BabyBear.fieldSize := by decide + +theorem r2ModModulus_lt_fieldSize : r2ModModulus.toNat < BabyBear.fieldSize := by decide + +theorem rModModulus_cast : + (rModModulus.toNat : BabyBear.Field) = (UInt32.size : BabyBear.Field) := by decide + +theorem r2ModModulus_cast : + (r2ModModulus.toNat : BabyBear.Field) = (UInt32.size : BabyBear.Field) ^ 2 := by decide + +/-- The per-field data realizing BabyBear as a fast 32-bit-word Montgomery field. The five +word constants are the only runtime data; every other field is a `decide`-checked fact. -/ +instance instMont32Field : Mont32Field BabyBear.Field where + fieldSize := BabyBear.fieldSize + prime := inferInstance + modulus := modulus + modulus64 := modulus64 + rModModulus := rModModulus + r2ModModulus := r2ModModulus + montgomeryNegInv := montgomeryNegInv + modulus_toNat := modulus_toNat + modulus64_toNat := modulus64_toNat + fieldSize_pos := fieldSize_pos + two_lt_fieldSize := two_lt_fieldSize + fieldSize_lt_uint32Size := fieldSize_lt_uint32Size + fieldSize_add_fieldSize_lt_two64 := fieldSize_add_fieldSize_lt_two64 + fieldSize_add_fieldSize_lt_uint32Size := fieldSize_add_fieldSize_lt_uint32Size + fieldSize_mul_fieldSize_lt_two64 := fieldSize_mul_fieldSize_lt_two64 + uint32Size_lt_three_fieldSize := uint32Size_lt_three_fieldSize + fieldSize_mul_uint32Size_lt_two64 := fieldSize_mul_uint32Size_lt_two64 + two_fieldSize_mul_uint32Size_lt_two64 := two_fieldSize_mul_uint32Size_lt_two64 + uint32Size_ne_zero_in_field := uint32Size_ne_zero_in_field + rModModulus_lt_fieldSize := rModModulus_lt_fieldSize + r2ModModulus_lt_fieldSize := r2ModModulus_lt_fieldSize + rModModulus_cast := rModModulus_cast + r2ModModulus_cast := r2ModModulus_cast + negInv_congr := by decide + two_ne_zero_in_field := by decide + +/-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ +abbrev Field : Type := FastField BabyBear.Field + +/-- The raw Montgomery word backing a fast BabyBear element. -/ +@[inline] +def raw (x : Field) : UInt32 := x.val end Fast end BabyBear diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index 04da53b5..ea0f844b 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -5,536 +5,184 @@ Authors: Valerii Huhnin -/ import CompPoly.Fields.KoalaBear.Fast.Convert -import Mathlib.Algebra.Field.TransferInstance /-! # Fast KoalaBear Field Operations -This module provides a native-word implementation of KoalaBear arithmetic as a sidecar -to the canonical `KoalaBear.Field := ZMod KoalaBear.fieldSize` model. +A native-word implementation of KoalaBear arithmetic as a sidecar to the canonical +`KoalaBear.Field := ZMod KoalaBear.fieldSize` model. Fast values are stored as Montgomery +`UInt32` residues below `KoalaBear.fieldSize`, representing `x * 2^32` modulo the prime. -Fast field values are stored as Montgomery `UInt32` residues below `KoalaBear.fieldSize`, -representing `x * 2^32` modulo the KoalaBear prime. Addition, subtraction, and -negation operate directly on Montgomery words; multiplication uses Montgomery -reduction on a native `UInt64` product. - -Supporting layers live in sibling modules: field basics in -`CompPoly.Fields.KoalaBear.Fast.Prelude`, Montgomery reduction in -`CompPoly.Fields.KoalaBear.Fast.Montgomery`, and conversions/casts in -`CompPoly.Fields.KoalaBear.Fast.Convert`. This module defines the field operations, -their instances, and the `toField` bridge to `KoalaBear.Field`. +The operations, their `Field`/`CommRing`/`NonBinaryField` instances, the `toField` bridge, +and all correctness theorems are shared across every fast 32-bit-word field; they live once +in `CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance +in `CompPoly.Fields.KoalaBear.Fast.Prelude`. Because `Field := Native32.FastField KoalaBear.Field`, +the generic algebraic instances resolve here automatically. This module re-exports the +named operations and `simp` lemmas at the KoalaBear instance. -/ namespace KoalaBear namespace Fast +open Montgomery.Native32 + /-- Fast modular addition in Montgomery form. -/ @[inline] -def add (x y : Field) : Field := - reduceUInt32Lt2Modulus (x.val + y.val) (by - rw [UInt32.toNat_add] - exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by omega)) +def add (x y : Field) : Field := Montgomery.Native32.add x y /-- Fast modular negation in Montgomery form. -/ @[inline] -def neg (x : Field) : Field := - if hx : x.val = 0 then - zero - else - ⟨modulus - x.val, by - have hle : x.val ≤ modulus := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - exact Nat.le_of_lt x.property - rw [UInt32.toNat_sub_of_le _ _ hle, modulus_toNat] - have hxpos : 0 < x.val.toNat := by - apply Nat.pos_of_ne_zero - intro hzero - apply hx - apply UInt32.toNat_inj.mp - simpa using hzero - omega⟩ +def neg (x : Field) : Field := Montgomery.Native32.neg x /-- Fast modular subtraction in Montgomery form. -/ @[inline] -def sub (x y : Field) : Field := - if hyx : y.val ≤ x.val then - ⟨x.val - y.val, by - rw [UInt32.toNat_sub_of_le _ _ hyx] - omega⟩ - else - ⟨x.val + modulus - y.val, by - have hsum_lt : x.val.toNat + KoalaBear.fieldSize < UInt32.size := by - have htwo := fieldSize_add_fieldSize_lt_uint32Size - omega - have hsum_eq : (x.val + modulus).toNat = x.val.toNat + KoalaBear.fieldSize := by - rw [UInt32.toNat_add, modulus_toNat, Nat.mod_eq_of_lt hsum_lt] - have hyle : y.val ≤ x.val + modulus := by - rw [UInt32.le_iff_toNat_le, hsum_eq] - omega - rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] - have hyxNat : ¬y.val.toNat ≤ x.val.toNat := by - intro hle - apply hyx - rw [UInt32.le_iff_toNat_le] - exact hle - omega⟩ +def sub (x y : Field) : Field := Montgomery.Native32.sub x y /-- Fast modular multiplication in Montgomery form. -/ @[inline] -def mul (x y : Field) : Field := - montgomeryReduceBounded (x.val.toUInt64 * y.val.toUInt64) (by - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, fieldSize_lt_uint32Size, fieldSize_pos]) +def mul (x y : Field) : Field := Montgomery.Native32.mul x y /-- Fast squaring. -/ @[inline] -def square (x : Field) : Field := - mul x x +def square (x : Field) : Field := Montgomery.Native32.square x /-- Exponentiation over the fast representation using repeated squaring. -/ @[inline] -def pow (x : Field) (n : Nat) : Field := - @npowBinRec Field ⟨one⟩ ⟨mul⟩ n x +def pow (x : Field) (n : Nat) : Field := Montgomery.Native32.pow x n /-- Fermat exponent used for inversion in the KoalaBear prime field. -/ -def invExponent : Nat := KoalaBear.fieldSize - 2 +def invExponent : Nat := Montgomery.Native32.invExponent (F := KoalaBear.Field) -/-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`), -by binary exponentiation (`pow`). -/ +/-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ @[inline] -def inv (x : Field) : Field := - pow x invExponent +def inv (x : Field) : Field := Montgomery.Native32.inv x /-- Division through inversion and fast multiplication. -/ @[inline] -def div (x y : Field) : Field := - mul x (inv y) - -instance instZeroField : Zero Field where - zero := zero - -instance instOneField : One Field where - one := one - -instance instAddField : Add Field where - add := add - -instance instNegField : Neg Field where - neg := neg - -instance instSubField : Sub Field where - sub := sub - -instance instMulField : Mul Field where - mul := mul - -instance instInvField : Inv Field where - inv := inv - -instance instDivField : Div Field where - div := div - -instance instNatCastField : NatCast Field where - natCast := ofNat - -instance instIntCastField : IntCast Field where - intCast := ofInt - -instance instNatSMulField : SMul Nat Field where - smul n x := ofNat n * x - -instance instIntSMulField : SMul Int Field where - smul n x := ofInt n * x +def div (x y : Field) : Field := Montgomery.Native32.div x y -instance instPowFieldNat : Pow Field Nat where - pow := pow - -instance instPowFieldInt : Pow Field Int where - pow x n := - match n with - | Int.ofNat k => pow x k - | Int.negSucc k => pow (inv x) (k + 1) - -instance instNNRatCastField : NNRatCast Field where - nnratCast q := ofField (q : KoalaBear.Field) - -instance instRatCastField : RatCast Field where - ratCast q := ofField (q : KoalaBear.Field) - -instance instNNRatSMulField : SMul ℚ≥0 Field where - smul q x := ofField (q • toField x) - -instance instRatSMulField : SMul ℚ Field where - smul q x := ofField (q • toField x) +/-- Ring equivalence between the fast Montgomery representation and canonical `KoalaBear.Field`. -/ +def ringEquiv : Field ≃+* KoalaBear.Field := + Montgomery.Native32.ringEquiv (F := KoalaBear.Field) /-- Converting from the canonical field to fast form and back is the identity. -/ @[simp] -theorem toField_ofField (x : KoalaBear.Field) : toField (ofField x) = x := by - unfold ofField - rw [toField_ofCanonicalNat] - exact ZMod.natCast_zmod_val x +theorem toField_ofField (x : KoalaBear.Field) : toField (ofField x) = x := + Montgomery.Native32.toField_ofField (F := KoalaBear.Field) x /-- Converting from fast form to the canonical field and back is the identity. -/ @[simp] -theorem ofField_toField (x : Field) : ofField (toField x) = x := by - apply Subtype.ext - apply UInt32.toNat_inj.mp - apply nat_eq_of_field_eq - · exact (ofField (toField x)).property - · exact x.property - · rw [raw_cast_eq_toField_mul] - rw [toField_ofField] - rw [raw_cast_eq_toField_mul] +theorem ofField_toField (x : Field) : ofField (toField x) = x := + Montgomery.Native32.ofField_toField x /-- The canonical-field interpretation distinguishes fast KoalaBear values. -/ -theorem toField_injective : Function.Injective toField := - Function.LeftInverse.injective ofField_toField +theorem toField_injective : Function.Injective (toField : Field → KoalaBear.Field) := + Montgomery.Native32.toField_injective (F := KoalaBear.Field) /-- `toField` maps fast zero to canonical zero. -/ @[simp] -theorem toField_zero : toField (0 : Field) = 0 := by - rw [toField_eq_raw_mul_inv] - change ((0 : Nat) : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ = 0 - exact zero_mul _ +theorem toField_zero : toField (0 : Field) = 0 := + Montgomery.Native32.toField_zero (F := KoalaBear.Field) /-- `toField` maps fast one to canonical one. -/ @[simp] -theorem toField_one : toField (1 : Field) = 1 := by - rw [toField_eq_raw_mul_inv] - change (rModModulus.toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ = 1 - rw [rModModulus_cast] - exact mul_inv_cancel₀ uint32Size_ne_zero_in_field +theorem toField_one : toField (1 : Field) = 1 := + Montgomery.Native32.toField_one (F := KoalaBear.Field) /-- Fast addition agrees with addition in the canonical KoalaBear field. -/ @[simp] -theorem toField_add (x y : Field) : toField (x + y) = toField x + toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] - unfold instAddField add - have hred := reduceUInt32Lt2Modulus_cast (x.val + y.val) (by - rw [UInt32.toNat_add] - exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by omega)) - change ((reduceUInt32Lt2ModulusRaw (x.val + y.val)).toNat : KoalaBear.Field) = - ((x.val + y.val).toNat : KoalaBear.Field) at hred - change ((reduceUInt32Lt2ModulusRaw (x.val + y.val)).toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ = - (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ + - (y.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - rw [hred] - rw [UInt32.toNat_add] - have hsum_lt : x.val.toNat + y.val.toNat < UInt32.size := by - nlinarith [x.property, y.property, fieldSize_add_fieldSize_lt_uint32Size] - rw [Nat.mod_eq_of_lt hsum_lt] - rw [Nat.cast_add] - ring +theorem toField_add (x y : Field) : toField (x + y) = toField x + toField y := + Montgomery.Native32.toField_add x y /-- Fast subtraction agrees with subtraction in the canonical KoalaBear field. -/ @[simp] -theorem toField_sub (x y : Field) : toField (x - y) = toField x - toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] - by_cases hyx : y.val ≤ x.val - · have hsubval : (x - y : Field).val = x.val - y.val := by - change (sub x y).val = x.val - y.val - unfold sub - rw [dif_pos hyx] - rw [hsubval] - change (((x.val - y.val).toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹) = - (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - - (y.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - rw [UInt32.toNat_sub_of_le _ _ hyx] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le] at hyx - exact hyx)] - ring - · have hsum_lt : x.val.toNat + KoalaBear.fieldSize < UInt32.size := by - have htwo := fieldSize_add_fieldSize_lt_uint32Size - omega - have hsum_eq : (x.val + modulus).toNat = x.val.toNat + KoalaBear.fieldSize := by - rw [UInt32.toNat_add, modulus_toNat, Nat.mod_eq_of_lt hsum_lt] - have hyle : y.val ≤ x.val + modulus := by - rw [UInt32.le_iff_toNat_le, hsum_eq] - omega - have hsubval : (x - y : Field).val = x.val + modulus - y.val := by - change (sub x y).val = x.val + modulus - y.val - unfold sub - rw [dif_neg hyx] - rw [hsubval] - change (((x.val + modulus - y.val).toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹) = - (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - - (y.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, hsum_eq] at hyle - exact hyle)] - rw [Nat.cast_add, ZMod.natCast_self] - ring +theorem toField_sub (x y : Field) : toField (x - y) = toField x - toField y := + Montgomery.Native32.toField_sub x y /-- Fast negation agrees with negation in the canonical KoalaBear field. -/ @[simp] -theorem toField_neg (x : Field) : toField (-x) = -toField x := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x] - by_cases hx : x.val = 0 - · have hnegval : (-x : Field).val = zero.val := by - change (neg x).val = zero.val - unfold neg - rw [dif_pos hx] - rw [hnegval] - change ((zero.val.toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹) = - -((x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹) - have hxNat : x.val.toNat = 0 := by - simpa using congrArg UInt32.toNat hx - rw [hxNat] - change ((0 : Nat) : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ = - -(((0 : Nat) : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹) - simp - · have hle : x.val ≤ modulus := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - exact Nat.le_of_lt x.property - have hnegval : (-x : Field).val = modulus - x.val := by - change (neg x).val = modulus - x.val - unfold neg - rw [dif_neg hx] - rw [hnegval] - change (((modulus - x.val).toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹) = - -((x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹) - rw [UInt32.toNat_sub_of_le _ _ hle, modulus_toNat] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, modulus_toNat] at hle - exact hle)] - rw [ZMod.natCast_self] - ring +theorem toField_neg (x : Field) : toField (-x) = -toField x := + Montgomery.Native32.toField_neg x /-- Fast multiplication agrees with multiplication in the canonical KoalaBear field. -/ @[simp] -theorem toField_mul (x y : Field) : toField (x * y) = toField x * toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] - unfold instMulField mul - have hred := montgomeryReduceBounded_cast (x.val.toUInt64 * y.val.toUInt64) (by - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, fieldSize_lt_uint32Size, fieldSize_pos]) - change ((montgomeryReduceBoundedRaw (x.val.toUInt64 * y.val.toUInt64)).toNat : - KoalaBear.Field) = - ((x.val.toUInt64 * y.val.toUInt64).toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (x.val.toUInt64 * y.val.toUInt64)).toNat : - KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ = - (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ * - ((y.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹) - rw [hred] - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - rw [Nat.cast_mul] - ring - -/-- Ring equivalence between the fast Montgomery representation and canonical `KoalaBear.Field`. -/ -def ringEquiv : Field ≃+* KoalaBear.Field where - toFun := toField - invFun := ofField - left_inv := ofField_toField - right_inv := toField_ofField - map_add' := toField_add - map_mul' := toField_mul +theorem toField_mul (x y : Field) : toField (x * y) = toField x * toField y := + Montgomery.Native32.toField_mul x y /-- Applying `ringEquiv` is the same as interpreting a fast value canonically. -/ @[simp] -theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := rfl +theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := + Montgomery.Native32.ringEquiv_apply x /-- Applying the inverse `ringEquiv` is conversion into fast Montgomery form. -/ @[simp] -theorem ringEquiv_symm_apply (x : KoalaBear.Field) : ringEquiv.symm x = ofField x := rfl - -private theorem mul_assoc_field (x y z : Field) : (x * y) * z = x * (y * z) := by - apply toField_injective - rw [toField_mul, toField_mul, toField_mul, toField_mul] - ring - -private theorem pow_succ (x : Field) (n : Nat) : pow x (n + 1) = pow x n * x := by - unfold pow - letI : Semigroup Field := { - mul := (· * ·) - mul_assoc := mul_assoc_field - } - exact npowBinRec_succ n x +theorem ringEquiv_symm_apply (x : KoalaBear.Field) : ringEquiv.symm x = ofField x := + Montgomery.Native32.ringEquiv_symm_apply (F := KoalaBear.Field) x /-- Fast squaring agrees with multiplication by itself in the canonical field. -/ @[simp] -theorem toField_square (x : Field) : toField (square x) = toField x * toField x := by - change toField (x * x) = toField x * toField x - rw [toField_mul] - -/-- Fast natural-power computation agrees with powers in the canonical field. -/ -@[simp] -theorem toField_pow (x : Field) (n : Nat) : toField (pow x n) = toField x ^ n := by - induction n with - | zero => - unfold pow - rw [npowBinRec_zero] - rw [toField_one] - simp - | succ n ih => - rw [pow_succ, toField_mul, ih, _root_.pow_succ] - -private theorem toField_inv_pow (x : Field) : - toField (inv x) = toField x ^ invExponent := by - unfold inv - exact toField_pow x invExponent - -private theorem toField_inv_raw (x : Field) : toField (inv x) = (toField x)⁻¹ := by - rw [toField_inv_pow] - by_cases hx : toField x = 0 - · rw [hx] - simp [invExponent, KoalaBear.fieldSize] - · simpa [invExponent] using (KoalaBear.inv_eq_pow (toField x) hx).symm +theorem toField_square (x : Field) : toField (square x) = toField x * toField x := + Montgomery.Native32.toField_square x /-- Fast inversion agrees with inversion in the canonical KoalaBear field. -/ @[simp] -theorem toField_inv (x : Field) : toField x⁻¹ = (toField x)⁻¹ := by - change toField (inv x) = (toField x)⁻¹ - exact toField_inv_raw x - -private theorem toField_mul_raw (x y : Field) : toField (mul x y) = toField x * toField y := by - change toField (x * y) = toField x * toField y - exact toField_mul x y - -private theorem toField_div_mul_inv (x y : Field) : - toField (div x y) = toField x * toField (inv y) := by - unfold div - exact toField_mul_raw x (inv y) +theorem toField_inv (x : Field) : toField x⁻¹ = (toField x)⁻¹ := + Montgomery.Native32.toField_inv x /-- Fast division agrees with division in the canonical KoalaBear field. -/ @[simp] -theorem toField_div (x y : Field) : toField (x / y) = toField x / toField y := by - change toField (div x y) = toField x / toField y - have h : ∀ a b c : KoalaBear.Field, c = b⁻¹ → a * c = a / b := by - intro a b c hc - rw [hc] - rfl - exact (toField_div_mul_inv x y).trans - (h (toField x) (toField y) (toField (inv y)) (toField_inv_raw y)) +theorem toField_div (x y : Field) : toField (x / y) = toField x / toField y := + Montgomery.Native32.toField_div x y /-- Natural casts into fast form agree with natural casts into the canonical field. -/ @[simp] -theorem toField_natCast (n : Nat) : toField (n : Field) = (n : KoalaBear.Field) := by - change toField (ofNat n) = (n : KoalaBear.Field) - unfold ofNat - rw [toField_ofCanonicalNat] - rw [ZMod.natCast_eq_natCast_iff] - exact Nat.mod_modEq _ _ +theorem toField_natCast (n : Nat) : toField (n : Field) = (n : KoalaBear.Field) := + Montgomery.Native32.toField_natCast n /-- Integer casts into fast form agree with integer casts into the canonical field. -/ @[simp] -theorem toField_intCast (n : Int) : toField (n : Field) = (n : KoalaBear.Field) := by - change toField (ofInt n) = (n : KoalaBear.Field) - unfold ofInt - rw [toField_ofField] +theorem toField_intCast (n : Int) : toField (n : Field) = (n : KoalaBear.Field) := + Montgomery.Native32.toField_intCast n /-- Natural scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_nsmul (n : Nat) (x : Field) : toField (n • x) = n • toField x := by - change toField ((n : Field) * x) = n • toField x - rw [toField_mul, toField_natCast] - rw [nsmul_eq_mul] +theorem toField_nsmul (n : Nat) (x : Field) : toField (n • x) = n • toField x := + Montgomery.Native32.toField_nsmul n x /-- Integer scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_zsmul (n : Int) (x : Field) : toField (n • x) = n • toField x := by - change toField ((n : Field) * x) = n • toField x - rw [toField_mul, toField_intCast] - rw [zsmul_eq_mul] +theorem toField_zsmul (n : Int) (x : Field) : toField (n • x) = n • toField x := + Montgomery.Native32.toField_zsmul n x /-- Natural powers through the `Pow` instance are preserved by `toField`. -/ @[simp] -theorem toField_npow (x : Field) (n : Nat) : toField (x ^ n) = toField x ^ n := by - change toField (pow x n) = toField x ^ n - rw [toField_pow] +theorem toField_npow (x : Field) (n : Nat) : toField (x ^ n) = toField x ^ n := + Montgomery.Native32.toField_npow x n /-- Integer powers through the `Pow` instance are preserved by `toField`. -/ @[simp] -theorem toField_zpow (x : Field) (n : Int) : toField (x ^ n) = toField x ^ n := by - cases n with - | ofNat n => - change toField (pow x n) = toField x ^ (Int.ofNat n) - rw [toField_pow] - exact (zpow_natCast (toField x) n).symm - | negSucc n => - change toField (pow (inv x) (n + 1)) = toField x ^ (Int.negSucc n) - have hinv : toField (inv x) = (toField x)⁻¹ := by - change toField x⁻¹ = (toField x)⁻¹ - rw [toField_inv] - rw [toField_pow, hinv, zpow_negSucc, inv_pow] +theorem toField_zpow (x : Field) (n : Int) : toField (x ^ n) = toField x ^ n := + Montgomery.Native32.toField_zpow x n /-- Nonnegative rational casts into fast form agree with canonical-field casts. -/ @[simp] -theorem toField_nnratCast (q : ℚ≥0) : toField (q : Field) = (q : KoalaBear.Field) := by - change toField (ofField (q : KoalaBear.Field)) = (q : KoalaBear.Field) - rw [toField_ofField] +theorem toField_nnratCast (q : ℚ≥0) : toField (q : Field) = (q : KoalaBear.Field) := + Montgomery.Native32.toField_nnratCast q /-- Rational casts into fast form agree with canonical-field casts. -/ @[simp] -theorem toField_ratCast (q : ℚ) : toField (q : Field) = (q : KoalaBear.Field) := by - change toField (ofField (q : KoalaBear.Field)) = (q : KoalaBear.Field) - rw [toField_ofField] +theorem toField_ratCast (q : ℚ) : toField (q : Field) = (q : KoalaBear.Field) := + Montgomery.Native32.toField_ratCast q /-- Nonnegative rational scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • toField x := by - change toField (ofField (q • toField x)) = q • toField x - rw [toField_ofField] +theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • toField x := + Montgomery.Native32.toField_nnqsmul q x /-- Rational scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := by - change toField (ofField (q • toField x)) = q • toField x - rw [toField_ofField] - -/-- Field instance transferred from canonical KoalaBear through `toField`. -/ -instance (priority := low) instField : _root_.Field Field := - toField_injective.field toField - toField_zero - toField_one - toField_add - toField_mul - toField_neg - toField_sub - toField_inv - toField_div - toField_nsmul - toField_zsmul - toField_nnqsmul - toField_qsmul - toField_npow - toField_zpow - toField_natCast - toField_intCast - toField_nnratCast - toField_ratCast - -/-- Commutative-ring instance inherited from the transferred field structure. -/ -instance (priority := low) instCommRing : CommRing Field := by - infer_instance - -/-- Fast KoalaBear is a non-binary field. -/ -instance (priority := low) instNonBinaryField : NonBinaryField Field where - char_neq_2 := by - change ((2 : Nat) : Field) ≠ 0 - intro h - exact NonBinaryField.char_neq_2 (F := KoalaBear.Field) (by - calc - (2 : KoalaBear.Field) = toField ((2 : Nat) : Field) := (toField_natCast 2).symm - _ = toField (0 : Field) := congrArg toField h - _ = 0 := toField_zero) +theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := + Montgomery.Native32.toField_qsmul q x end Fast end KoalaBear diff --git a/CompPoly/Fields/KoalaBear/Fast/Convert.lean b/CompPoly/Fields/KoalaBear/Fast/Convert.lean index 6abc9b46..f2409532 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Convert.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Convert.lean @@ -10,241 +10,115 @@ import CompPoly.Fields.KoalaBear.Fast.Montgomery # Fast KoalaBear Field — Conversions Conversions between the fast Montgomery representation and the canonical -`KoalaBear.Field` / `Nat` views (`ofField`, `toField`, `ofNat`, `toNat`, …), the -`zero`/`one` constants, and the round-trip cast lemmas relating them. The field -operations and ring/field instances build on this module. +`KoalaBear.Field` / `Nat` views, re-exported from the shared implementation in +`CompPoly.Fields.Montgomery.Native32Field` at the KoalaBear instance. -/ namespace KoalaBear namespace Fast +open Montgomery.Native32 + /-- Build a fast element from a canonical natural representative. -/ @[inline] -def ofCanonicalNat (n : Nat) (_h : n < KoalaBear.fieldSize) : Field := - montgomeryReduceBounded (UInt64.ofNat n * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans _h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) - rw [hnmod] - have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) +def ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : Field := + Montgomery.Native32.ofCanonicalNat (F := KoalaBear.Field) n h /-- Reduce a `UInt64` modulo the KoalaBear prime and return a Montgomery fast element. -/ @[inline] def reduceUInt64 (x : UInt64) : Field := - let y := x % modulus64 - montgomeryReduceBounded (y * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : (x % modulus64).toNat < KoalaBear.fieldSize := by - rw [UInt64.toNat_mod, modulus64_toNat] - exact Nat.mod_lt _ fieldSize_pos - have hprod : (x % modulus64).toNat * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) + Montgomery.Native32.reduceUInt64 (F := KoalaBear.Field) x /-- The zero fast KoalaBear element. -/ -def zero : Field := ⟨0, by decide⟩ +def zero : Field := Montgomery.Native32.zero (F := KoalaBear.Field) /-- The one fast KoalaBear element. -/ -def one : Field := ⟨rModModulus, by decide⟩ +def one : Field := Montgomery.Native32.one (F := KoalaBear.Field) /-- Convert a natural number into fast Montgomery representation. -/ @[inline] def ofNat (n : Nat) : Field := - ofCanonicalNat (n % KoalaBear.fieldSize) (Nat.mod_lt _ fieldSize_pos) + Montgomery.Native32.ofNat (F := KoalaBear.Field) n /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (x : UInt32) : Field := - reduceUInt64 x.toUInt64 + Montgomery.Native32.ofUInt32 (F := KoalaBear.Field) x /-- Convert from the canonical `ZMod` KoalaBear field into fast Montgomery form. -/ @[inline] def ofField (x : KoalaBear.Field) : Field := - ofCanonicalNat x.val (ZMod.val_lt x) + Montgomery.Native32.ofField (F := KoalaBear.Field) x /-- Convert an integer into fast Montgomery representation. -/ @[inline] def ofInt (n : Int) : Field := - ofField (n : KoalaBear.Field) + Montgomery.Native32.ofInt (F := KoalaBear.Field) n /-- Convert a fast element to its canonical native-word representative. -/ @[inline] def toCanonicalUInt32 (x : Field) : UInt32 := - raw (montgomeryReduceBounded x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, fieldSize_pos])) + Montgomery.Native32.toCanonicalUInt32 x /-- Convert a fast KoalaBear element to its canonical natural representative. -/ @[inline] def toNat (x : Field) : Nat := - (toCanonicalUInt32 x).toNat + Montgomery.Native32.toNat x /-- Convert a fast KoalaBear element to the canonical `ZMod` KoalaBear field. -/ @[inline] def toField (x : Field) : KoalaBear.Field := - (toNat x : KoalaBear.Field) + Montgomery.Native32.toField x -theorem toNat_lt_fieldSize (x : Field) : toNat x < KoalaBear.fieldSize := by - unfold toNat toCanonicalUInt32 raw - change (montgomeryReduceBoundedRaw x.val.toUInt64).toNat < KoalaBear.fieldSize - exact montgomeryReduceBoundedRaw_lt x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, fieldSize_pos]) +theorem toNat_lt_fieldSize (x : Field) : toNat x < KoalaBear.fieldSize := + Montgomery.Native32.toNat_lt_fieldSize x theorem toField_eq_raw_mul_inv (x : Field) : - toField x = - (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - unfold toField toNat toCanonicalUInt32 raw - have hred := montgomeryReduceBounded_cast x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, fieldSize_pos]) - change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : KoalaBear.Field) = - (x.val.toUInt64.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw x.val.toUInt64).toNat : KoalaBear.Field) = - (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - rw [hred] - rw [UInt32.toNat_toUInt64] + toField x = (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := + Montgomery.Native32.toField_eq_raw_mul_inv x theorem raw_cast_eq_toField_mul (x : Field) : - (x.val.toNat : KoalaBear.Field) = - toField x * (UInt32.size : KoalaBear.Field) := by - rw [toField_eq_raw_mul_inv] - rw [mul_assoc] - rw [inv_mul_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] + (x.val.toNat : KoalaBear.Field) = toField x * (UInt32.size : KoalaBear.Field) := + Montgomery.Native32.raw_cast_eq_toField_mul x theorem nat_eq_of_field_eq {a b : Nat} (ha : a < KoalaBear.fieldSize) (hb : b < KoalaBear.fieldSize) (h : (a : KoalaBear.Field) = (b : KoalaBear.Field)) : a = b := - Montgomery.natCast_inj ha hb h + Montgomery.Native32.nat_eq_of_field_eq (F := KoalaBear.Field) ha hb h theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < KoalaBear.fieldSize) : ((ofCanonicalNat n h).val.toNat : KoalaBear.Field) = - (n : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := by - unfold ofCanonicalNat - have hred := montgomeryReduceBounded_cast - (UInt64.ofNat n * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) - rw [hnmod] - have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) - change ((montgomeryReduceBoundedRaw - (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : KoalaBear.Field) = - ((UInt64.ofNat n * r2ModModulus.toUInt64).toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw - (UInt64.ofNat n * r2ModModulus.toUInt64)).toNat : KoalaBear.Field) = - (n : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) - rw [hred] - simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans fieldSize_lt_uint32Size (by decide)) - rw [hnmod] - have hprod : n * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - rw [Nat.cast_mul, r2ModModulus_cast] - rw [pow_two] - rw [mul_assoc (n : KoalaBear.Field) ((UInt32.size : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)) ((UInt32.size : KoalaBear.Field)⁻¹)] - rw [mul_assoc (UInt32.size : KoalaBear.Field) (UInt32.size : KoalaBear.Field) - ((UInt32.size : KoalaBear.Field)⁻¹)] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - -theorem toField_ofCanonicalNat_aux (n : Nat) (h : n < KoalaBear.fieldSize) : - toField (ofCanonicalNat n h) = (n : KoalaBear.Field) := by - rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] + (n : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := + Montgomery.Native32.ofCanonicalNat_raw_cast (F := KoalaBear.Field) n h theorem reduceUInt64_raw_cast (x : UInt64) : ((reduceUInt64 x).val.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := by - unfold reduceUInt64 - let y := x % modulus64 - have hred := montgomeryReduceBounded_cast (y * r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < KoalaBear.fieldSize := by - rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] - exact Nat.mod_lt _ fieldSize_pos - have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [r2ModModulus_lt_fieldSize]) - change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : - KoalaBear.Field) = - ((y * r2ModModulus.toUInt64).toNat : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (y * r2ModModulus.toUInt64)).toNat : - KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) - rw [hred] - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < KoalaBear.fieldSize := by - rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] - exact Nat.mod_lt _ fieldSize_pos - have hprod : y.toNat * r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, r2ModModulus_lt_fieldSize, fieldSize_mul_fieldSize_lt_two64] - rw [Nat.mod_eq_of_lt hprod] - have hy_cast : (y.toNat : KoalaBear.Field) = (x.toNat : KoalaBear.Field) := by - rw [show y = x % modulus64 by rfl, UInt64.toNat_mod, modulus64_toNat] - rw [ZMod.natCast_eq_natCast_iff] - exact Nat.mod_modEq _ _ - rw [Nat.cast_mul, r2ModModulus_cast, hy_cast] - rw [pow_two] - rw [mul_assoc (x.toNat : KoalaBear.Field) ((UInt32.size : KoalaBear.Field) * - (UInt32.size : KoalaBear.Field)) ((UInt32.size : KoalaBear.Field)⁻¹)] - rw [mul_assoc (UInt32.size : KoalaBear.Field) (UInt32.size : KoalaBear.Field) - ((UInt32.size : KoalaBear.Field)⁻¹)] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] + (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := + Montgomery.Native32.reduceUInt64_raw_cast x /-- Converting a canonical natural representative to fast form preserves its value. -/ @[simp] theorem toNat_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : - toNat (ofCanonicalNat n h) = n := by - exact nat_eq_of_field_eq (toNat_lt_fieldSize _) h (toField_ofCanonicalNat_aux n h) + toNat (ofCanonicalNat n h) = n := + Montgomery.Native32.toNat_ofCanonicalNat n h /-- `ofCanonicalNat` embeds a canonical representative into the canonical field. -/ @[simp] theorem toField_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : - toField (ofCanonicalNat n h) = (n : KoalaBear.Field) := by - exact toField_ofCanonicalNat_aux n h + toField (ofCanonicalNat n h) = (n : KoalaBear.Field) := + Montgomery.Native32.toField_ofCanonicalNat n h /-- Reducing a `UInt64` gives the canonical natural residue modulo KoalaBear. -/ @[simp] theorem toNat_reduceUInt64 (x : UInt64) : - toNat (reduceUInt64 x) = x.toNat % KoalaBear.fieldSize := by - apply nat_eq_of_field_eq (toNat_lt_fieldSize _) (Nat.mod_lt _ fieldSize_pos) - change toField (reduceUInt64 x) = ((x.toNat % KoalaBear.fieldSize : Nat) : KoalaBear.Field) - rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] - rw [ZMod.natCast_eq_natCast_iff] - exact (Nat.mod_modEq _ _).symm + toNat (reduceUInt64 x) = x.toNat % KoalaBear.fieldSize := + Montgomery.Native32.toNat_reduceUInt64 x /-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ @[simp] theorem toField_reduceUInt64 (x : UInt64) : - toField (reduceUInt64 x) = (x.toNat : KoalaBear.Field) := by - rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ uint32Size_ne_zero_in_field] - rw [mul_one] + toField (reduceUInt64 x) = (x.toNat : KoalaBear.Field) := + Montgomery.Native32.toField_reduceUInt64 x end Fast end KoalaBear diff --git a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean index 310c1976..4f9c94ca 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean @@ -5,177 +5,77 @@ Authors: Valerii Huhnin -/ import CompPoly.Fields.KoalaBear.Fast.Prelude -import CompPoly.Fields.Montgomery.Native32 /-! # Fast KoalaBear Field — Montgomery Reduction -Montgomery-form constants (`montgomeryNegInv`, `rModModulus`, `r2ModModulus`) and the -native-word Montgomery reduction used by the fast KoalaBear field. The radix-generic -number theory lives in `CompPoly.Fields.Montgomery.Basic` and the `R = 2^32` word bridge -in `CompPoly.Fields.Montgomery.Native32`; field basics live in -`CompPoly.Fields.KoalaBear.Fast.Prelude`; conversions and field operations build on this -module. +The native-word Montgomery reducers specialized to KoalaBear. The definitions and their +correctness proofs are shared across every fast 32-bit-word field; they live once in +`CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance +supplied in `CompPoly.Fields.KoalaBear.Fast.Prelude`. This module just re-exports them at +the KoalaBear instance. Because the shared definitions are `@[inline]`, fixing the instance +folds the constants to literals: the compiled code is identical to a hand-written +monomorphic reducer, with no `Mont32Field` dictionary at runtime. -/ namespace KoalaBear namespace Fast -/-- `2^32 mod KoalaBear.fieldSize`. This is the Montgomery representation of one. -/ -def rModModulus : UInt32 := 0x01FFFFFE - -/-- `(2^32)^2 mod KoalaBear.fieldSize`, used to enter Montgomery representation. -/ -def r2ModModulus : UInt32 := 0x17F7EFE4 - -/-- `-KoalaBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ -def montgomeryNegInv : UInt32 := 0x7EFFFFFF - -theorem r2ModModulus_lt_fieldSize : r2ModModulus.toNat < KoalaBear.fieldSize := by - decide - -theorem rModModulus_lt_fieldSize : rModModulus.toNat < KoalaBear.fieldSize := by - decide - -theorem rModModulus_cast : - (rModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) := by - decide - -theorem r2ModModulus_cast : - (r2ModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) ^ 2 := by - decide +open Montgomery.Native32 /-- Reduce a native word known to be below twice the KoalaBear prime. -/ @[inline] def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := - if x < modulus then x else x - modulus + Montgomery.Native32.reduceUInt32Lt2ModulusRaw (F := KoalaBear.Field) x theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : - (reduceUInt32Lt2ModulusRaw x).toNat < KoalaBear.fieldSize := by - unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < modulus - · rw [if_pos hx] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx - · rw [if_neg hx] - have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - omega + (reduceUInt32Lt2ModulusRaw x).toNat < KoalaBear.fieldSize := + Montgomery.Native32.reduceUInt32Lt2ModulusRaw_lt (F := KoalaBear.Field) x h /-- Reduce a native word known to be below twice the KoalaBear prime. -/ @[inline] def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : Field := - ⟨reduceUInt32Lt2ModulusRaw x, reduceUInt32Lt2ModulusRaw_lt x h⟩ + Montgomery.Native32.reduceUInt32Lt2Modulus (F := KoalaBear.Field) x h theorem reduceUInt32Lt2Modulus_cast (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : ((reduceUInt32Lt2Modulus x h).val.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) := by - change ((reduceUInt32Lt2ModulusRaw x).toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) - unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < modulus - · rw [if_pos hx] - · have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - rw [if_neg hx] - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, modulus_toNat] at hmod_le_x - exact hmod_le_x)] - simp + (x.toNat : KoalaBear.Field) := + Montgomery.Native32.reduceUInt32Lt2Modulus_cast (F := KoalaBear.Field) x h /-- Reduce a native word below `2^32` modulo the KoalaBear prime. -/ @[inline] def reduceUInt32 (x : UInt32) : Field := - if hx : x < modulus then - ⟨x, by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact hx⟩ - else - let y := x - modulus - if hy : y < modulus then - ⟨y, by - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy - exact hy⟩ - else - ⟨y - modulus, by - have hmod_le_x : modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hx - exact Nat.le_of_not_gt hx - have hy_eq : y.toNat = x.toNat - KoalaBear.fieldSize := by - change (x - modulus).toNat = x.toNat - KoalaBear.fieldSize - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, modulus_toNat] - have hmod_le_y : modulus ≤ y := by - rw [UInt32.le_iff_toNat_le, modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, modulus_toNat] at hy - exact Nat.le_of_not_gt hy - rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, modulus_toNat, hy_eq] - have hx_lt := UInt32.toNat_lt_size x - have hthree := uint32Size_lt_three_fieldSize - omega⟩ + Montgomery.Native32.reduceUInt32 (F := KoalaBear.Field) x /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := - let m : UInt32 := x.toUInt32 * montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 - reduceUInt32Lt2ModulusRaw u + Montgomery.Native32.montgomeryReduceBoundedRaw (F := KoalaBear.Field) x theorem montgomeryReduceBoundedRaw_lt (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - (montgomeryReduceBoundedRaw x).toNat < KoalaBear.fieldSize := by - unfold montgomeryReduceBoundedRaw - exact reduceUInt32Lt2ModulusRaw_lt _ - (Montgomery.Native32.u_lt_two_mul montgomeryNegInv modulus64 KoalaBear.fieldSize - modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h) + (montgomeryReduceBoundedRaw x).toNat < KoalaBear.fieldSize := + Montgomery.Native32.montgomeryReduceBoundedRaw_lt (F := KoalaBear.Field) x h /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBounded (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : Field := - ⟨montgomeryReduceBoundedRaw x, montgomeryReduceBoundedRaw_lt x h⟩ + Montgomery.Native32.montgomeryReduceBounded (F := KoalaBear.Field) x h theorem montgomeryReduceBounded_cast (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : ((montgomeryReduceBounded x h).val.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := by - change ((montgomeryReduceBoundedRaw x).toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - unfold montgomeryReduceBoundedRaw - let m : UInt32 := x.toUInt32 * montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 - change ((reduceUInt32Lt2ModulusRaw u).toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - have hred := reduceUInt32Lt2Modulus_cast u - (Montgomery.Native32.u_lt_two_mul montgomeryNegInv modulus64 KoalaBear.fieldSize - modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h) - change ((reduceUInt32Lt2ModulusRaw u).toNat : KoalaBear.Field) = - (u.toNat : KoalaBear.Field) at hred - rw [hred] - change (u.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ - rw [show u.toNat = - (x.toNat + ((x.toNat % UInt32.size * montgomeryNegInv.toNat) % UInt32.size) * - KoalaBear.fieldSize) / UInt32.size by - exact Montgomery.Native32.u_eq_nat montgomeryNegInv modulus64 KoalaBear.fieldSize - modulus64_toNat fieldSize_pos two_fieldSize_mul_uint32Size_lt_two64 x h] - exact Montgomery.quotient_cast UInt32.size KoalaBear.fieldSize montgomeryNegInv.toNat - (by decide) (by decide) uint32Size_ne_zero_in_field x.toNat + (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := + Montgomery.Native32.montgomeryReduceBounded_cast (F := KoalaBear.Field) x h /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] def montgomeryReduce (x : UInt64) : Field := - let m : UInt32 := x.toUInt32 * montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * modulus64) >>> 32).toUInt32 - reduceUInt32 u + Montgomery.Native32.montgomeryReduce (F := KoalaBear.Field) x end Fast end KoalaBear diff --git a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean index 82772643..0317685d 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean @@ -5,77 +5,120 @@ Authors: Valerii Huhnin -/ import CompPoly.Fields.KoalaBear.Basic +import CompPoly.Fields.Montgomery.Native32Field /-! # Fast KoalaBear Field — Basics -The native-word modulus and the `Field` carrier type for the fast KoalaBear field, -plus the elementary numeric bounds reused throughout the Montgomery reduction layer. +The native-word constants and the `Field` carrier type for the fast KoalaBear field. The +shared implementation lives in `CompPoly.Fields.Montgomery.Native32Field`; this module +supplies the per-field `Mont32Field` instance (the five word constants plus the +`decide`-checked numeric facts) and pins `Field := Native32.FastField KoalaBear.Field`, so +the generic definitions, proofs, and algebraic instances specialize to KoalaBear. -The Montgomery-form constants and reduction machinery live in -`CompPoly.Fields.KoalaBear.Fast.Montgomery`; conversions in `...Fast.Convert`; the -field operations and instances in `CompPoly.Fields.KoalaBear.Fast`. +The Montgomery reducers are re-exported in `CompPoly.Fields.KoalaBear.Fast.Montgomery`; +conversions in `...Fast.Convert`; the field operations and instances in +`CompPoly.Fields.KoalaBear.Fast`. -/ namespace KoalaBear namespace Fast +open Montgomery.Native32 (Mont32Field FastField) + /-- KoalaBear modulus as a native word. -/ def modulus : UInt32 := 0x7F000001 /-- KoalaBear modulus as a 64-bit word for modular reduction. -/ def modulus64 : UInt64 := 0x7F000001 -/-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ -abbrev Field : Type := { x : UInt32 // x.toNat < KoalaBear.fieldSize } +/-- `2^32 mod KoalaBear.fieldSize`. This is the Montgomery representation of one. -/ +def rModModulus : UInt32 := 0x01FFFFFE -instance : DecidableEq Field := inferInstance +/-- `(2^32)^2 mod KoalaBear.fieldSize`, used to enter Montgomery representation. -/ +def r2ModModulus : UInt32 := 0x17F7EFE4 -/-- The raw Montgomery word backing a fast KoalaBear element. -/ -@[inline] -def raw (x : Field) : UInt32 := x.val +/-- `-KoalaBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ +def montgomeryNegInv : UInt32 := 0x7EFFFFFF /-- The native `UInt32` modulus agrees with the mathematical KoalaBear modulus. -/ -@[simp] theorem modulus_toNat : modulus.toNat = KoalaBear.fieldSize := by - decide +@[simp] theorem modulus_toNat : modulus.toNat = KoalaBear.fieldSize := by decide /-- The native `UInt64` modulus agrees with the mathematical KoalaBear modulus. -/ -@[simp] theorem modulus64_toNat : modulus64.toNat = KoalaBear.fieldSize := by - decide +@[simp] theorem modulus64_toNat : modulus64.toNat = KoalaBear.fieldSize := by decide + +theorem fieldSize_pos : 0 < KoalaBear.fieldSize := by decide -theorem fieldSize_pos : 0 < KoalaBear.fieldSize := by - decide +theorem two_lt_fieldSize : 2 < KoalaBear.fieldSize := by decide -theorem fieldSize_lt_uint32Size : KoalaBear.fieldSize < UInt32.size := by - decide +theorem fieldSize_lt_uint32Size : KoalaBear.fieldSize < UInt32.size := by decide theorem fieldSize_add_fieldSize_lt_two64 : - KoalaBear.fieldSize + KoalaBear.fieldSize < 2 ^ 64 := by - decide + KoalaBear.fieldSize + KoalaBear.fieldSize < 2 ^ 64 := by decide theorem fieldSize_add_fieldSize_lt_uint32Size : - KoalaBear.fieldSize + KoalaBear.fieldSize < UInt32.size := by - decide + KoalaBear.fieldSize + KoalaBear.fieldSize < UInt32.size := by decide theorem fieldSize_mul_fieldSize_lt_two64 : - KoalaBear.fieldSize * KoalaBear.fieldSize < 2 ^ 64 := by - decide + KoalaBear.fieldSize * KoalaBear.fieldSize < 2 ^ 64 := by decide theorem uint32Size_lt_three_fieldSize : - UInt32.size < KoalaBear.fieldSize + KoalaBear.fieldSize + KoalaBear.fieldSize := by - decide + UInt32.size < KoalaBear.fieldSize + KoalaBear.fieldSize + KoalaBear.fieldSize := by decide theorem fieldSize_mul_uint32Size_lt_two64 : - KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by - decide + KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by decide theorem two_fieldSize_mul_uint32Size_lt_two64 : - 2 * KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by - decide + 2 * KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by decide theorem uint32Size_ne_zero_in_field : - (UInt32.size : KoalaBear.Field) ≠ 0 := by - decide + (UInt32.size : KoalaBear.Field) ≠ 0 := by decide + +theorem rModModulus_lt_fieldSize : rModModulus.toNat < KoalaBear.fieldSize := by decide + +theorem r2ModModulus_lt_fieldSize : r2ModModulus.toNat < KoalaBear.fieldSize := by decide + +theorem rModModulus_cast : + (rModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) := by decide + +theorem r2ModModulus_cast : + (r2ModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) ^ 2 := by decide + +/-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. The five +word constants are the only runtime data; every other field is a `decide`-checked fact. -/ +instance instMont32Field : Mont32Field KoalaBear.Field where + fieldSize := KoalaBear.fieldSize + prime := inferInstance + modulus := modulus + modulus64 := modulus64 + rModModulus := rModModulus + r2ModModulus := r2ModModulus + montgomeryNegInv := montgomeryNegInv + modulus_toNat := modulus_toNat + modulus64_toNat := modulus64_toNat + fieldSize_pos := fieldSize_pos + two_lt_fieldSize := two_lt_fieldSize + fieldSize_lt_uint32Size := fieldSize_lt_uint32Size + fieldSize_add_fieldSize_lt_two64 := fieldSize_add_fieldSize_lt_two64 + fieldSize_add_fieldSize_lt_uint32Size := fieldSize_add_fieldSize_lt_uint32Size + fieldSize_mul_fieldSize_lt_two64 := fieldSize_mul_fieldSize_lt_two64 + uint32Size_lt_three_fieldSize := uint32Size_lt_three_fieldSize + fieldSize_mul_uint32Size_lt_two64 := fieldSize_mul_uint32Size_lt_two64 + two_fieldSize_mul_uint32Size_lt_two64 := two_fieldSize_mul_uint32Size_lt_two64 + uint32Size_ne_zero_in_field := uint32Size_ne_zero_in_field + rModModulus_lt_fieldSize := rModModulus_lt_fieldSize + r2ModModulus_lt_fieldSize := r2ModModulus_lt_fieldSize + rModModulus_cast := rModModulus_cast + r2ModModulus_cast := r2ModModulus_cast + negInv_congr := by decide + two_ne_zero_in_field := by decide + +/-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ +abbrev Field : Type := FastField KoalaBear.Field + +/-- The raw Montgomery word backing a fast KoalaBear element. -/ +@[inline] +def raw (x : Field) : UInt32 := x.val end Fast end KoalaBear diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean new file mode 100644 index 00000000..88f0e9a4 --- /dev/null +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -0,0 +1,996 @@ +/- +Copyright (c) 2026 CompPoly Contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Valerii Huhnin, Georgios Raikos +-/ + +import CompPoly.Fields.Basic +import CompPoly.Fields.Montgomery.Native32 +import Mathlib.Algebra.Field.TransferInstance +import Mathlib.FieldTheory.Finite.Basic + +/-! +# Fast 32-bit-word prime fields — shared implementation + +A `BabyBear`-style and `KoalaBear`-style fast field differ only in a handful of word +constants; their definitions, the proofs about them, and the resulting algebraic instances +are otherwise identical. This module captures that common content **once**, parameterized +by a `Mont32Field` instance that supplies the per-field data. + +* `Mont32Field F` bundles the prime (`fieldSize`), its native-word forms, the Montgomery + constants, and the small `decide`-checkable numeric/`ZMod` facts the proofs consume. + Everything except the five word constants is spec-level and erased at codegen. +* `FastField F` is the fast carrier `{ x : UInt32 // x.toNat < fieldSize }`, indexed by the + tag `F` so that the generic `Add`/`Mul`/`Field`/… instances below resolve for each + concrete field's `Field := FastField `. At runtime it erases to `UInt32`. +* The executable `def`s are `@[inline]`/`@[specialize]`, so once a concrete instance is + fixed the instance projections fold to literals and the compiled code is identical to a + hand-written monomorphic version — no `Mont32Field` dictionary survives to runtime. + +The radix-generic number theory lives in `CompPoly.Fields.Montgomery.Basic` and the +`R = 2^32` word bridge in `CompPoly.Fields.Montgomery.Native32`. A concrete fast field is +then just a `Mont32Field` instance plus thin re-export shims. +-/ + +namespace Montgomery +namespace Native32 + +/-- Per-field data for a fast 32-bit-word Montgomery prime field. + +The five word constants (`modulus`, `modulus64`, `rModModulus`, `r2ModModulus`, +`montgomeryNegInv`) are the only runtime data; the remaining fields are `Prop`s, all +dischargeable by `decide` for a concrete field, and are erased at codegen. `F` is a tag +(in practice the canonical `ZMod fieldSize` field) used for instance resolution. -/ +class Mont32Field (F : Type) where + /-- The field size / prime `p`. -/ + fieldSize : Nat + /-- `fieldSize` is prime — needed to reduce into `ZMod fieldSize` as a field. -/ + prime : Fact (Nat.Prime fieldSize) + /-- `fieldSize` as a 32-bit word. -/ + modulus : UInt32 + /-- `fieldSize` as a 64-bit word. -/ + modulus64 : UInt64 + /-- `2^32 mod fieldSize`, the Montgomery representation of one. -/ + rModModulus : UInt32 + /-- `(2^32)^2 mod fieldSize`, used to enter Montgomery form. -/ + r2ModModulus : UInt32 + /-- `-fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ + montgomeryNegInv : UInt32 + modulus_toNat : modulus.toNat = fieldSize + modulus64_toNat : modulus64.toNat = fieldSize + fieldSize_pos : 0 < fieldSize + two_lt_fieldSize : 2 < fieldSize + fieldSize_lt_uint32Size : fieldSize < UInt32.size + fieldSize_add_fieldSize_lt_two64 : fieldSize + fieldSize < 2 ^ 64 + fieldSize_add_fieldSize_lt_uint32Size : fieldSize + fieldSize < UInt32.size + fieldSize_mul_fieldSize_lt_two64 : fieldSize * fieldSize < 2 ^ 64 + uint32Size_lt_three_fieldSize : UInt32.size < fieldSize + fieldSize + fieldSize + fieldSize_mul_uint32Size_lt_two64 : fieldSize * UInt32.size < 2 ^ 64 + two_fieldSize_mul_uint32Size_lt_two64 : 2 * fieldSize * UInt32.size < 2 ^ 64 + uint32Size_ne_zero_in_field : (UInt32.size : ZMod fieldSize) ≠ 0 + rModModulus_lt_fieldSize : rModModulus.toNat < fieldSize + r2ModModulus_lt_fieldSize : r2ModModulus.toNat < fieldSize + rModModulus_cast : (rModModulus.toNat : ZMod fieldSize) = (UInt32.size : ZMod fieldSize) + r2ModModulus_cast : + (r2ModModulus.toNat : ZMod fieldSize) = (UInt32.size : ZMod fieldSize) ^ 2 + /-- The Montgomery inverse congruence `negInv * p ≡ 2^32 - 1 [MOD 2^32]`. -/ + negInv_congr : + montgomeryNegInv.toNat * fieldSize ≡ UInt32.size - 1 [MOD UInt32.size] + /-- Characteristic `≠ 2`, used to build the `NonBinaryField` instance. -/ + two_ne_zero_in_field : (2 : ZMod fieldSize) ≠ 0 + +attribute [instance] Mont32Field.prime + +/-- The fast carrier for the field tagged by `F`: a native word below `fieldSize`, +interpreted as a Montgomery residue. At runtime this erases to `UInt32`. -/ +def FastField (F : Type) [Mont32Field F] : Type := + { x : UInt32 // x.toNat < Mont32Field.fieldSize F } + +instance (F : Type) [Mont32Field F] : DecidableEq (FastField F) := + inferInstanceAs (DecidableEq { x : UInt32 // x.toNat < Mont32Field.fieldSize F }) + +section +variable {F : Type} [P : Mont32Field F] + +instance : NeZero P.fieldSize := ⟨P.fieldSize_pos.ne'⟩ + +/-- The raw Montgomery word backing a fast element. -/ +@[inline] +def raw (x : FastField F) : UInt32 := x.val + +/-! ## Montgomery reduction -/ + +/-- Reduce a native word known to be below twice the prime. -/ +@[inline] +def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := + if x < P.modulus then x else x - P.modulus + +theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) + (h : x.toNat < 2 * P.fieldSize) : + (reduceUInt32Lt2ModulusRaw (F := F) x).toNat < P.fieldSize := by + unfold reduceUInt32Lt2ModulusRaw + by_cases hx : x < P.modulus + · rw [if_pos hx] + rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + exact hx + · rw [if_neg hx] + have hmod_le_x : P.modulus ≤ x := by + rw [UInt32.le_iff_toNat_le, P.modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + exact Nat.le_of_not_gt hx + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus_toNat] + omega + +/-- Reduce a native word known to be below twice the prime. -/ +@[inline] +def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * P.fieldSize) : + FastField F := + ⟨reduceUInt32Lt2ModulusRaw (F := F) x, reduceUInt32Lt2ModulusRaw_lt x h⟩ + +theorem reduceUInt32Lt2Modulus_cast (x : UInt32) + (h : x.toNat < 2 * P.fieldSize) : + ((reduceUInt32Lt2Modulus (F := F) x h).val.toNat : ZMod P.fieldSize) = + (x.toNat : ZMod P.fieldSize) := by + change ((reduceUInt32Lt2ModulusRaw (F := F) x).toNat : ZMod P.fieldSize) = + (x.toNat : ZMod P.fieldSize) + unfold reduceUInt32Lt2ModulusRaw + by_cases hx : x < P.modulus + · rw [if_pos hx] + · have hmod_le_x : P.modulus ≤ x := by + rw [UInt32.le_iff_toNat_le, P.modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + exact Nat.le_of_not_gt hx + rw [if_neg hx] + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus_toNat] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le, P.modulus_toNat] at hmod_le_x + exact hmod_le_x)] + simp + +/-- Reduce a native word below `2^32` modulo the prime. -/ +@[inline] +def reduceUInt32 (x : UInt32) : FastField F := + if hx : x < P.modulus then + ⟨x, by + rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + exact hx⟩ + else + let y := x - P.modulus + if hy : y < P.modulus then + ⟨y, by + rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hy + exact hy⟩ + else + ⟨y - P.modulus, by + have hmod_le_x : P.modulus ≤ x := by + rw [UInt32.le_iff_toNat_le, P.modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + exact Nat.le_of_not_gt hx + have hy_eq : y.toNat = x.toNat - P.fieldSize := by + change (x - P.modulus).toNat = x.toNat - P.fieldSize + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus_toNat] + have hmod_le_y : P.modulus ≤ y := by + rw [UInt32.le_iff_toNat_le, P.modulus_toNat] + rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hy + exact Nat.le_of_not_gt hy + rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, P.modulus_toNat, hy_eq] + have hx_lt := UInt32.toNat_lt_size x + have hthree := P.uint32Size_lt_three_fieldSize + omega⟩ + +/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ +@[inline] +def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := + let m : UInt32 := x.toUInt32 * P.montgomeryNegInv + let u : UInt32 := ((x + m.toUInt64 * P.modulus64) >>> 32).toUInt32 + reduceUInt32Lt2ModulusRaw (F := F) u + +theorem montgomeryReduceBoundedRaw_lt (x : UInt64) + (h : x.toNat < P.fieldSize * UInt32.size) : + (montgomeryReduceBoundedRaw (F := F) x).toNat < P.fieldSize := by + unfold montgomeryReduceBoundedRaw + exact reduceUInt32Lt2ModulusRaw_lt _ + (Montgomery.Native32.u_lt_two_mul P.montgomeryNegInv P.modulus64 P.fieldSize + P.modulus64_toNat P.fieldSize_pos P.two_fieldSize_mul_uint32Size_lt_two64 x h) + +/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ +@[inline] +def montgomeryReduceBounded (x : UInt64) + (h : x.toNat < P.fieldSize * UInt32.size) : FastField F := + ⟨montgomeryReduceBoundedRaw (F := F) x, montgomeryReduceBoundedRaw_lt x h⟩ + +theorem montgomeryReduceBounded_cast (x : UInt64) + (h : x.toNat < P.fieldSize * UInt32.size) : + ((montgomeryReduceBounded (F := F) x h).val.toNat : ZMod P.fieldSize) = + (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ := by + change ((montgomeryReduceBoundedRaw (F := F) x).toNat : ZMod P.fieldSize) = + (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + unfold montgomeryReduceBoundedRaw + let m : UInt32 := x.toUInt32 * P.montgomeryNegInv + let u : UInt32 := ((x + m.toUInt64 * P.modulus64) >>> 32).toUInt32 + change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.fieldSize) = + (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + have hred := reduceUInt32Lt2Modulus_cast (F := F) u + (Montgomery.Native32.u_lt_two_mul P.montgomeryNegInv P.modulus64 P.fieldSize + P.modulus64_toNat P.fieldSize_pos P.two_fieldSize_mul_uint32Size_lt_two64 x h) + change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.fieldSize) = + (u.toNat : ZMod P.fieldSize) at hred + rw [hred] + change (u.toNat : ZMod P.fieldSize) = + (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + rw [show u.toNat = + (x.toNat + ((x.toNat % UInt32.size * P.montgomeryNegInv.toNat) % UInt32.size) * + P.fieldSize) / UInt32.size by + exact Montgomery.Native32.u_eq_nat P.montgomeryNegInv P.modulus64 P.fieldSize + P.modulus64_toNat P.fieldSize_pos P.two_fieldSize_mul_uint32Size_lt_two64 x h] + exact Montgomery.quotient_cast UInt32.size P.fieldSize P.montgomeryNegInv.toNat + (by decide) P.negInv_congr P.uint32Size_ne_zero_in_field x.toNat + +/-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ +@[inline] +def montgomeryReduce (x : UInt64) : FastField F := + let m : UInt32 := x.toUInt32 * P.montgomeryNegInv + let u : UInt32 := ((x + m.toUInt64 * P.modulus64) >>> 32).toUInt32 + reduceUInt32 (F := F) u + +/-! ## Conversions -/ + +/-- Build a fast element from a canonical natural representative. -/ +@[inline] +def ofCanonicalNat (n : Nat) (_h : n < P.fieldSize) : FastField F := + montgomeryReduceBounded (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans _h (Nat.lt_trans P.fieldSize_lt_uint32Size (by decide)) + rw [hnmod] + have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [P.r2ModModulus_lt_fieldSize]) + +/-- Reduce a `UInt64` modulo the prime and return a Montgomery fast element. -/ +@[inline] +def reduceUInt64 (x : UInt64) : FastField F := + let y := x % P.modulus64 + montgomeryReduceBounded (y * P.r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hy_lt : (x % P.modulus64).toNat < P.fieldSize := by + rw [UInt64.toNat_mod, P.modulus64_toNat] + exact Nat.mod_lt _ P.fieldSize_pos + have hprod : (x % P.modulus64).toNat * P.r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [P.r2ModModulus_lt_fieldSize]) + +/-- The zero fast element. -/ +def zero : FastField F := ⟨0, by + have h0 : (0 : UInt32).toNat = 0 := by decide + have hp := P.fieldSize_pos + omega⟩ + +/-- The one fast element. -/ +def one : FastField F := ⟨P.rModModulus, P.rModModulus_lt_fieldSize⟩ + +/-- Convert a natural number into fast Montgomery representation. -/ +@[inline] +def ofNat (n : Nat) : FastField F := + ofCanonicalNat (n % P.fieldSize) (Nat.mod_lt _ P.fieldSize_pos) + +/-- Convert a 32-bit word into fast Montgomery representation. -/ +@[inline] +def ofUInt32 (x : UInt32) : FastField F := + reduceUInt64 x.toUInt64 + +/-- Convert from the canonical `ZMod` field into fast Montgomery form. -/ +@[inline] +def ofField (x : ZMod P.fieldSize) : FastField F := + ofCanonicalNat x.val (ZMod.val_lt x) + +/-- Convert an integer into fast Montgomery representation. -/ +@[inline] +def ofInt (n : Int) : FastField F := + ofField (n : ZMod P.fieldSize) + +/-- Convert a fast element to its canonical native-word representative. -/ +@[inline] +def toCanonicalUInt32 (x : FastField F) : UInt32 := + raw (montgomeryReduceBounded x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, P.fieldSize_pos])) + +/-- Convert a fast element to its canonical natural representative. -/ +@[inline] +def toNat (x : FastField F) : Nat := + (toCanonicalUInt32 x).toNat + +/-- Convert a fast element to the canonical `ZMod` field. -/ +@[inline] +def toField (x : FastField F) : ZMod P.fieldSize := + (toNat x : ZMod P.fieldSize) + +theorem toNat_lt_fieldSize (x : FastField F) : toNat x < P.fieldSize := by + unfold toNat toCanonicalUInt32 raw + change (montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat < P.fieldSize + exact montgomeryReduceBoundedRaw_lt x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, P.fieldSize_pos]) + +theorem toField_eq_raw_mul_inv (x : FastField F) : + toField x = + (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ := by + unfold toField toNat toCanonicalUInt32 raw + have hred := montgomeryReduceBounded_cast x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, P.fieldSize_pos]) + change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.fieldSize) = + (x.val.toUInt64.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ at hred + change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.fieldSize) = + (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + rw [hred] + rw [UInt32.toNat_toUInt64] + +theorem raw_cast_eq_toField_mul (x : FastField F) : + (x.val.toNat : ZMod P.fieldSize) = + toField x * (UInt32.size : ZMod P.fieldSize) := by + rw [toField_eq_raw_mul_inv] + rw [mul_assoc] + rw [inv_mul_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_one] + +theorem nat_eq_of_field_eq {a b : Nat} (ha : a < P.fieldSize) + (hb : b < P.fieldSize) (h : (a : ZMod P.fieldSize) = (b : ZMod P.fieldSize)) : + a = b := + Montgomery.natCast_inj ha hb h + +theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < P.fieldSize) : + ((ofCanonicalNat (F := F) n h).val.toNat : ZMod P.fieldSize) = + (n : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize) := by + unfold ofCanonicalNat + have hred := montgomeryReduceBounded_cast + (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans h (Nat.lt_trans P.fieldSize_lt_uint32Size (by decide)) + rw [hnmod] + have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [P.r2ModModulus_lt_fieldSize]) + change ((montgomeryReduceBoundedRaw (F := F) + (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.fieldSize) = + ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)⁻¹ at hred + change ((montgomeryReduceBoundedRaw (F := F) + (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.fieldSize) = + (n : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize) + rw [hred] + simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans h (Nat.lt_trans P.fieldSize_lt_uint32Size (by decide)) + rw [hnmod] + have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + rw [Nat.cast_mul, P.r2ModModulus_cast] + rw [pow_two] + rw [mul_assoc (n : ZMod P.fieldSize) ((UInt32.size : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)) ((UInt32.size : ZMod P.fieldSize)⁻¹)] + rw [mul_assoc (UInt32.size : ZMod P.fieldSize) (UInt32.size : ZMod P.fieldSize) + ((UInt32.size : ZMod P.fieldSize)⁻¹)] + rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_one] + +theorem toField_ofCanonicalNat_aux (n : Nat) (h : n < P.fieldSize) : + toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.fieldSize) := by + rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_one] + +theorem reduceUInt64_raw_cast (x : UInt64) : + ((reduceUInt64 (F := F) x).val.toNat : ZMod P.fieldSize) = + (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize) := by + unfold reduceUInt64 + let y := x % P.modulus64 + have hred := montgomeryReduceBounded_cast (y * P.r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hy_lt : y.toNat < P.fieldSize := by + rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] + exact Nat.mod_lt _ P.fieldSize_pos + have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [P.r2ModModulus_lt_fieldSize]) + change ((montgomeryReduceBoundedRaw (F := F) (y * P.r2ModModulus.toUInt64)).toNat : + ZMod P.fieldSize) = + ((y * P.r2ModModulus.toUInt64).toNat : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)⁻¹ at hred + change ((montgomeryReduceBoundedRaw (F := F) (y * P.r2ModModulus.toUInt64)).toNat : + ZMod P.fieldSize) = + (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize) + rw [hred] + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hy_lt : y.toNat < P.fieldSize := by + rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] + exact Nat.mod_lt _ P.fieldSize_pos + have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + have hy_cast : (y.toNat : ZMod P.fieldSize) = (x.toNat : ZMod P.fieldSize) := by + rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] + rw [ZMod.natCast_eq_natCast_iff] + exact Nat.mod_modEq _ _ + rw [Nat.cast_mul, P.r2ModModulus_cast, hy_cast] + rw [pow_two] + rw [mul_assoc (x.toNat : ZMod P.fieldSize) ((UInt32.size : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)) ((UInt32.size : ZMod P.fieldSize)⁻¹)] + rw [mul_assoc (UInt32.size : ZMod P.fieldSize) (UInt32.size : ZMod P.fieldSize) + ((UInt32.size : ZMod P.fieldSize)⁻¹)] + rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_one] + +@[simp] +theorem toNat_ofCanonicalNat (n : Nat) (h : n < P.fieldSize) : + toNat (ofCanonicalNat (F := F) n h) = n := + nat_eq_of_field_eq (toNat_lt_fieldSize _) h (toField_ofCanonicalNat_aux n h) + +@[simp] +theorem toField_ofCanonicalNat (n : Nat) (h : n < P.fieldSize) : + toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.fieldSize) := + toField_ofCanonicalNat_aux n h + +@[simp] +theorem toNat_reduceUInt64 (x : UInt64) : + toNat (reduceUInt64 (F := F) x) = x.toNat % P.fieldSize := by + apply nat_eq_of_field_eq (toNat_lt_fieldSize _) (Nat.mod_lt _ P.fieldSize_pos) + change toField (reduceUInt64 (F := F) x) = ((x.toNat % P.fieldSize : Nat) : ZMod P.fieldSize) + rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_one] + rw [ZMod.natCast_eq_natCast_iff] + exact (Nat.mod_modEq _ _).symm + +@[simp] +theorem toField_reduceUInt64 (x : UInt64) : + toField (reduceUInt64 (F := F) x) = (x.toNat : ZMod P.fieldSize) := by + rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_one] + +/-! ## Field operations -/ + +/-- Fast modular addition in Montgomery form. -/ +@[inline] +def add (x y : FastField F) : FastField F := + reduceUInt32Lt2Modulus (x.val + y.val) (by + rw [UInt32.toNat_add] + exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by + have := x.property; have := y.property; omega)) + +/-- Fast modular negation in Montgomery form. -/ +@[inline] +def neg (x : FastField F) : FastField F := + if hx : x.val = 0 then + zero + else + ⟨P.modulus - x.val, by + have hle : x.val ≤ P.modulus := by + rw [UInt32.le_iff_toNat_le, P.modulus_toNat] + exact Nat.le_of_lt x.property + rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus_toNat] + have hxpos : 0 < x.val.toNat := by + apply Nat.pos_of_ne_zero + intro hzero + apply hx + apply UInt32.toNat_inj.mp + simpa using hzero + have hp := P.fieldSize_pos + omega⟩ + +/-- Fast modular subtraction in Montgomery form. -/ +@[inline] +def sub (x y : FastField F) : FastField F := + if hyx : y.val ≤ x.val then + ⟨x.val - y.val, by + rw [UInt32.toNat_sub_of_le _ _ hyx] + have := x.property; omega⟩ + else + ⟨x.val + P.modulus - y.val, by + have hsum_lt : x.val.toNat + P.fieldSize < UInt32.size := by + have htwo := P.fieldSize_add_fieldSize_lt_uint32Size + have := x.property; omega + have hsum_eq : (x.val + P.modulus).toNat = x.val.toNat + P.fieldSize := by + rw [UInt32.toNat_add, P.modulus_toNat, Nat.mod_eq_of_lt hsum_lt] + have hyle : y.val ≤ x.val + P.modulus := by + rw [UInt32.le_iff_toNat_le, hsum_eq] + have := y.property; omega + rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] + have hyxNat : ¬y.val.toNat ≤ x.val.toNat := by + intro hle + apply hyx + rw [UInt32.le_iff_toNat_le] + exact hle + have := y.property; omega⟩ + +/-- Fast modular multiplication in Montgomery form. -/ +@[inline] +def mul (x y : FastField F) : FastField F := + montgomeryReduceBounded (x.val.toUInt64 * y.val.toUInt64) (by + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by + nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [x.property, y.property, P.fieldSize_lt_uint32Size, P.fieldSize_pos]) + +/-- Fast squaring. -/ +@[inline] +def square (x : FastField F) : FastField F := + mul x x + +/-- Exponentiation over the fast representation using repeated squaring. -/ +@[specialize] +def pow (x : FastField F) (n : Nat) : FastField F := + @npowBinRec (FastField F) ⟨one⟩ ⟨mul⟩ n x + +/-- Fermat exponent used for inversion in the prime field. -/ +def invExponent : Nat := P.fieldSize - 2 + +/-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`), +by binary exponentiation (`pow`). -/ +@[inline] +def inv (x : FastField F) : FastField F := + pow x (invExponent (F := F)) + +/-- Division through inversion and fast multiplication. -/ +@[inline] +def div (x y : FastField F) : FastField F := + mul x (inv y) + +instance instZero : Zero (FastField F) where + zero := zero + +instance instOne : One (FastField F) where + one := one + +instance instAdd : Add (FastField F) where + add := add + +instance instNeg : Neg (FastField F) where + neg := neg + +instance instSub : Sub (FastField F) where + sub := sub + +instance instMul : Mul (FastField F) where + mul := mul + +instance instInv : Inv (FastField F) where + inv := inv + +instance instDiv : Div (FastField F) where + div := div + +instance instNatCast : NatCast (FastField F) where + natCast := ofNat + +instance instIntCast : IntCast (FastField F) where + intCast := ofInt + +instance instNatSMul : SMul Nat (FastField F) where + smul n x := ofNat n * x + +instance instIntSMul : SMul Int (FastField F) where + smul n x := ofInt n * x + +instance instPowNat : Pow (FastField F) Nat where + pow := pow + +instance instPowInt : Pow (FastField F) Int where + pow x n := + match n with + | Int.ofNat k => pow x k + | Int.negSucc k => pow (inv x) (k + 1) + +instance instNNRatCast : NNRatCast (FastField F) where + nnratCast q := ofField (q : ZMod P.fieldSize) + +instance instRatCast : RatCast (FastField F) where + ratCast q := ofField (q : ZMod P.fieldSize) + +instance instNNRatSMul : SMul ℚ≥0 (FastField F) where + smul q x := ofField (q • toField x) + +instance instRatSMul : SMul ℚ (FastField F) where + smul q x := ofField (q • toField x) + +/-- Fermat-style inversion in `ZMod fieldSize`. -/ +theorem inv_eq_pow_field (a : ZMod P.fieldSize) (ha : a ≠ 0) : + a⁻¹ = a ^ (P.fieldSize - 2) := by + have hcard : Fintype.card (ZMod P.fieldSize) = P.fieldSize := ZMod.card P.fieldSize + have h1 : a ^ (P.fieldSize - 1) = 1 := by + have h := FiniteField.pow_card_sub_one_eq_one a ha + rw [hcard] at h; exact h + have hmul : a * a ^ (P.fieldSize - 2) = 1 := by + rw [← pow_succ']; show a ^ (P.fieldSize - 2 + 1) = 1 + have : P.fieldSize - 2 + 1 = P.fieldSize - 1 := by + have := P.two_lt_fieldSize; omega + rw [this]; exact h1 + exact (eq_inv_of_mul_eq_one_left (by rwa [mul_comm])).symm + +/-- Converting from the canonical field to fast form and back is the identity. -/ +@[simp] +theorem toField_ofField (x : ZMod P.fieldSize) : toField (ofField (F := F) x) = x := by + unfold ofField + rw [toField_ofCanonicalNat] + exact ZMod.natCast_zmod_val x + +/-- Converting from fast form to the canonical field and back is the identity. -/ +@[simp] +theorem ofField_toField (x : FastField F) : ofField (toField x) = x := by + apply Subtype.ext + apply UInt32.toNat_inj.mp + apply nat_eq_of_field_eq (F := F) + · exact (ofField (toField x)).property + · exact x.property + · rw [raw_cast_eq_toField_mul] + rw [toField_ofField] + rw [raw_cast_eq_toField_mul] + +/-- The canonical-field interpretation distinguishes fast values. -/ +theorem toField_injective : Function.Injective (toField (F := F)) := + Function.LeftInverse.injective ofField_toField + +/-- `toField` maps fast zero to canonical zero. -/ +@[simp] +theorem toField_zero : toField (0 : FastField F) = 0 := by + rw [toField_eq_raw_mul_inv] + change ((0 : Nat) : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ = 0 + rw [Nat.cast_zero, zero_mul] + +/-- `toField` maps fast one to canonical one. -/ +@[simp] +theorem toField_one : toField (1 : FastField F) = 1 := by + rw [toField_eq_raw_mul_inv] + change (P.rModModulus.toNat : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)⁻¹ = 1 + rw [P.rModModulus_cast] + exact mul_inv_cancel₀ P.uint32Size_ne_zero_in_field + +/-- Fast addition agrees with addition in the canonical field. -/ +@[simp] +theorem toField_add (x y : FastField F) : toField (x + y) = toField x + toField y := by + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] + unfold instAdd add + have hred := reduceUInt32Lt2Modulus_cast (F := F) (x.val + y.val) (by + rw [UInt32.toNat_add] + exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by + have hx := x.property; have hy := y.property; omega)) + change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.fieldSize) = + ((x.val + y.val).toNat : ZMod P.fieldSize) at hred + change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)⁻¹ = + (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + + (y.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + rw [hred] + rw [UInt32.toNat_add] + have hsum_lt : x.val.toNat + y.val.toNat < UInt32.size := by + nlinarith [x.property, y.property, P.fieldSize_add_fieldSize_lt_uint32Size] + rw [Nat.mod_eq_of_lt hsum_lt] + rw [Nat.cast_add] + ring + +/-- Fast subtraction agrees with subtraction in the canonical field. -/ +@[simp] +theorem toField_sub (x y : FastField F) : toField (x - y) = toField x - toField y := by + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] + by_cases hyx : y.val ≤ x.val + · have hsubval : (x - y : FastField F).val = x.val - y.val := by + change (sub x y).val = x.val - y.val + unfold sub + rw [dif_pos hyx] + rw [hsubval] + change (((x.val - y.val).toNat : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)⁻¹) = + (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ - + (y.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + rw [UInt32.toNat_sub_of_le _ _ hyx] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le] at hyx + exact hyx)] + ring + · have hsum_lt : x.val.toNat + P.fieldSize < UInt32.size := by + have htwo := P.fieldSize_add_fieldSize_lt_uint32Size + have := x.property; omega + have hsum_eq : (x.val + P.modulus).toNat = x.val.toNat + P.fieldSize := by + rw [UInt32.toNat_add, P.modulus_toNat, Nat.mod_eq_of_lt hsum_lt] + have hyle : y.val ≤ x.val + P.modulus := by + rw [UInt32.le_iff_toNat_le, hsum_eq] + have := y.property; omega + have hsubval : (x - y : FastField F).val = x.val + P.modulus - y.val := by + change (sub x y).val = x.val + P.modulus - y.val + unfold sub + rw [dif_neg hyx] + rw [hsubval] + change (((x.val + P.modulus - y.val).toNat : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)⁻¹) = + (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ - + (y.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le, hsum_eq] at hyle + exact hyle)] + rw [Nat.cast_add, ZMod.natCast_self] + ring + +/-- Fast negation agrees with negation in the canonical field. -/ +@[simp] +theorem toField_neg (x : FastField F) : toField (-x) = -toField x := by + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x] + by_cases hx : x.val = 0 + · have hnegval : (-x : FastField F).val = (zero : FastField F).val := by + change (neg x).val = (zero : FastField F).val + unfold neg + rw [dif_pos hx] + rw [hnegval] + change ((zero : FastField F).val.toNat : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)⁻¹ = + -((x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹) + have hxNat : x.val.toNat = 0 := by + simpa using congrArg UInt32.toNat hx + rw [hxNat] + change ((0 : Nat) : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ = + -(((0 : Nat) : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹) + simp + · have hle : x.val ≤ P.modulus := by + rw [UInt32.le_iff_toNat_le, P.modulus_toNat] + exact Nat.le_of_lt x.property + have hnegval : (-x : FastField F).val = P.modulus - x.val := by + change (neg x).val = P.modulus - x.val + unfold neg + rw [dif_neg hx] + rw [hnegval] + change (((P.modulus - x.val).toNat : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)⁻¹) = + -((x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹) + rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus_toNat] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le, P.modulus_toNat] at hle + exact hle)] + rw [ZMod.natCast_self] + ring + +/-- Fast multiplication agrees with multiplication in the canonical field. -/ +@[simp] +theorem toField_mul (x y : FastField F) : toField (x * y) = toField x * toField y := by + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] + unfold instMul mul + have hred := montgomeryReduceBounded_cast (F := F) (x.val.toUInt64 * y.val.toUInt64) (by + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by + nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [x.property, y.property, P.fieldSize_lt_uint32Size, P.fieldSize_pos]) + change ((montgomeryReduceBoundedRaw (F := F) (x.val.toUInt64 * y.val.toUInt64)).toNat : + ZMod P.fieldSize) = + ((x.val.toUInt64 * y.val.toUInt64).toNat : ZMod P.fieldSize) * + (UInt32.size : ZMod P.fieldSize)⁻¹ at hred + change ((montgomeryReduceBoundedRaw (F := F) (x.val.toUInt64 * y.val.toUInt64)).toNat : + ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ = + (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ * + ((y.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹) + rw [hred] + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by + nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two64] + rw [Nat.mod_eq_of_lt hprod] + rw [Nat.cast_mul] + ring + +/-- Ring equivalence between the fast Montgomery representation and the canonical field. -/ +def ringEquiv : FastField F ≃+* ZMod P.fieldSize where + toFun := toField + invFun := ofField + left_inv := ofField_toField + right_inv := toField_ofField + map_add' := toField_add + map_mul' := toField_mul + +@[simp] +theorem ringEquiv_apply (x : FastField F) : ringEquiv x = toField x := rfl + +@[simp] +theorem ringEquiv_symm_apply (x : ZMod P.fieldSize) : + (ringEquiv (F := F)).symm x = ofField x := rfl + +private theorem mul_assoc_field (x y z : FastField F) : (x * y) * z = x * (y * z) := by + apply toField_injective + rw [toField_mul, toField_mul, toField_mul, toField_mul] + ring + +private theorem pow_succ_field (x : FastField F) (n : Nat) : pow x (n + 1) = pow x n * x := by + unfold pow + letI : Semigroup (FastField F) := { + mul := (· * ·) + mul_assoc := mul_assoc_field + } + exact npowBinRec_succ n x + +/-- Fast squaring agrees with multiplication by itself in the canonical field. -/ +@[simp] +theorem toField_square (x : FastField F) : toField (square x) = toField x * toField x := by + change toField (x * x) = toField x * toField x + rw [toField_mul] + +/-- Fast natural-power computation agrees with powers in the canonical field. -/ +@[simp] +theorem toField_pow (x : FastField F) (n : Nat) : toField (pow x n) = toField x ^ n := by + induction n with + | zero => + unfold pow + rw [npowBinRec_zero] + rw [toField_one] + simp + | succ n ih => + rw [pow_succ_field, toField_mul, ih, _root_.pow_succ] + +private theorem toField_inv_pow (x : FastField F) : + toField (inv x) = toField x ^ (invExponent (F := F)) := by + unfold inv + exact toField_pow x (invExponent (F := F)) + +private theorem toField_inv_raw (x : FastField F) : toField (inv x) = (toField x)⁻¹ := by + rw [toField_inv_pow] + by_cases hx : toField x = 0 + · rw [hx, inv_zero] + exact zero_pow (by unfold invExponent; have := P.two_lt_fieldSize; omega) + · simpa [invExponent] using (inv_eq_pow_field (toField x) hx).symm + +/-- Fast inversion agrees with inversion in the canonical field. -/ +@[simp] +theorem toField_inv (x : FastField F) : toField x⁻¹ = (toField x)⁻¹ := by + change toField (inv x) = (toField x)⁻¹ + exact toField_inv_raw x + +private theorem toField_mul_raw (x y : FastField F) : + toField (mul x y) = toField x * toField y := by + change toField (x * y) = toField x * toField y + exact toField_mul x y + +private theorem toField_div_mul_inv (x y : FastField F) : + toField (div x y) = toField x * toField (inv y) := by + unfold div + exact toField_mul_raw x (inv y) + +/-- Fast division agrees with division in the canonical field. -/ +@[simp] +theorem toField_div (x y : FastField F) : toField (x / y) = toField x / toField y := by + change toField (div x y) = toField x / toField y + have h : ∀ a b c : ZMod P.fieldSize, c = b⁻¹ → a * c = a / b := by + intro a b c hc + rw [hc] + rfl + exact (toField_div_mul_inv x y).trans + (h (toField x) (toField y) (toField (inv y)) (toField_inv_raw y)) + +/-- Natural casts into fast form agree with natural casts into the canonical field. -/ +@[simp] +theorem toField_natCast (n : Nat) : toField (n : FastField F) = (n : ZMod P.fieldSize) := by + change toField (ofNat n) = (n : ZMod P.fieldSize) + unfold ofNat + rw [toField_ofCanonicalNat] + rw [ZMod.natCast_eq_natCast_iff] + exact Nat.mod_modEq _ _ + +/-- Integer casts into fast form agree with integer casts into the canonical field. -/ +@[simp] +theorem toField_intCast (n : Int) : toField (n : FastField F) = (n : ZMod P.fieldSize) := by + change toField (ofInt n) = (n : ZMod P.fieldSize) + unfold ofInt + rw [toField_ofField] + +/-- Natural scalar multiplication is preserved by `toField`. -/ +@[simp] +theorem toField_nsmul (n : Nat) (x : FastField F) : toField (n • x) = n • toField x := by + change toField ((n : FastField F) * x) = n • toField x + rw [toField_mul, toField_natCast] + rw [nsmul_eq_mul] + +/-- Integer scalar multiplication is preserved by `toField`. -/ +@[simp] +theorem toField_zsmul (n : Int) (x : FastField F) : toField (n • x) = n • toField x := by + change toField ((n : FastField F) * x) = n • toField x + rw [toField_mul, toField_intCast] + rw [zsmul_eq_mul] + +/-- Natural powers through the `Pow` instance are preserved by `toField`. -/ +@[simp] +theorem toField_npow (x : FastField F) (n : Nat) : toField (x ^ n) = toField x ^ n := by + change toField (pow x n) = toField x ^ n + rw [toField_pow] + +/-- Integer powers through the `Pow` instance are preserved by `toField`. -/ +@[simp] +theorem toField_zpow (x : FastField F) (n : Int) : toField (x ^ n) = toField x ^ n := by + cases n with + | ofNat n => + change toField (pow x n) = toField x ^ (Int.ofNat n) + rw [toField_pow] + exact (zpow_natCast (toField x) n).symm + | negSucc n => + change toField (pow (inv x) (n + 1)) = toField x ^ (Int.negSucc n) + have hinv : toField (inv x) = (toField x)⁻¹ := by + change toField x⁻¹ = (toField x)⁻¹ + rw [toField_inv] + rw [toField_pow, hinv, zpow_negSucc, inv_pow] + +/-- Nonnegative rational casts into fast form agree with canonical-field casts. -/ +@[simp] +theorem toField_nnratCast (q : ℚ≥0) : toField (q : FastField F) = (q : ZMod P.fieldSize) := by + change toField (ofField (q : ZMod P.fieldSize)) = (q : ZMod P.fieldSize) + rw [toField_ofField] + +/-- Rational casts into fast form agree with canonical-field casts. -/ +@[simp] +theorem toField_ratCast (q : ℚ) : toField (q : FastField F) = (q : ZMod P.fieldSize) := by + change toField (ofField (q : ZMod P.fieldSize)) = (q : ZMod P.fieldSize) + rw [toField_ofField] + +/-- Nonnegative rational scalar multiplication is preserved by `toField`. -/ +@[simp] +theorem toField_nnqsmul (q : ℚ≥0) (x : FastField F) : toField (q • x) = q • toField x := by + change toField (ofField (q • toField x)) = q • toField x + rw [toField_ofField] + +/-- Rational scalar multiplication is preserved by `toField`. -/ +@[simp] +theorem toField_qsmul (q : ℚ) (x : FastField F) : toField (q • x) = q • toField x := by + change toField (ofField (q • toField x)) = q • toField x + rw [toField_ofField] + +/-- Field instance transferred from the canonical field through `toField`. -/ +instance (priority := low) instField : _root_.Field (FastField F) := + toField_injective.field toField + toField_zero + toField_one + toField_add + toField_mul + toField_neg + toField_sub + toField_inv + toField_div + toField_nsmul + toField_zsmul + toField_nnqsmul + toField_qsmul + toField_npow + toField_zpow + toField_natCast + toField_intCast + toField_nnratCast + toField_ratCast + +/-- Commutative-ring instance inherited from the transferred field structure. -/ +instance (priority := low) instCommRing : CommRing (FastField F) := by + infer_instance + +/-- A fast 32-bit-word field is non-binary. -/ +instance (priority := low) instNonBinaryField : NonBinaryField (FastField F) where + char_neq_2 := by + change ((2 : Nat) : FastField F) ≠ 0 + intro h + exact P.two_ne_zero_in_field (by + calc + (2 : ZMod P.fieldSize) = ((2 : Nat) : ZMod P.fieldSize) := by norm_cast + _ = toField ((2 : Nat) : FastField F) := (toField_natCast 2).symm + _ = toField (0 : FastField F) := congrArg toField h + _ = 0 := toField_zero) + +end + +end Native32 +end Montgomery diff --git a/CompPoly/Fields/README.md b/CompPoly/Fields/README.md index 93b3c868..f7718c2a 100644 --- a/CompPoly/Fields/README.md +++ b/CompPoly/Fields/README.md @@ -10,17 +10,18 @@ This directory contains formally verified field infrastructure used in zero-know | **PrattCertificate.lean** | Lucas test for primality and Pratt certificate infrastructure (`PrattCertificate`, `PrattCertificate'`) for proving concrete primality goals. | | **BabyBear.lean** | Facade for BabyBear modules, re-exporting the canonical field and fast native-word implementation. | | **BabyBear/Basic.lean** | \(2^{31} - 2^{27} + 1\) — Risc Zero. | -| **BabyBear/Fast.lean** | Native `UInt32` Montgomery-residue operations for BabyBear, with conversion and operation equivalence statements against `BabyBear.Field`. | +| **BabyBear/Fast.lean** | BabyBear-namespaced API over the shared fast-field implementation (`Montgomery/Native32Field.lean`): thin wrappers forwarding the native `UInt32` Montgomery-residue operations and their `BabyBear.Field` equivalence (`@[simp]`) lemmas. | | **BLS12_377.lean** | Scalar field of BLS12-377 (253-bit, 2-adicity 47) — Zexe. | | **BLS12_381.lean** | Scalar field of BLS12-381 (253-bit, 2-adicity 47). | | **BN254.lean** | Scalar field of BN254 curve. | | **Goldilocks.lean** | \(2^{64} - 2^{32} + 1\) — Plonky2/3. | | **KoalaBear.lean** | Facade for KoalaBear modules, re-exporting the canonical field and fast native-word implementation. | | **KoalaBear/Basic.lean** | \(2^{31} - 2^{24} + 1\) — lean Ethereum spec. | -| **KoalaBear/Fast.lean** | Native `UInt32` Montgomery-residue operations for KoalaBear, with conversion and operation equivalence statements against `KoalaBear.Field`. | +| **KoalaBear/Fast.lean** | KoalaBear-namespaced API over the shared fast-field implementation (`Montgomery/Native32Field.lean`): thin wrappers forwarding the native `UInt32` Montgomery-residue operations and their `KoalaBear.Field` equivalence (`@[simp]`) lemmas. | | **Mersenne.lean** | \(2^{31} - 1\) — Circle STARKs. | | **Montgomery/Basic.lean** | Radix-generic Montgomery reduction, field-agnostic number theory shared by the fast prime fields. | | **Montgomery/Native32.lean** | `UInt32 × UInt64`, radix `R = 2^32` word-level Montgomery bridge over the generic core. | +| **Montgomery/Native32Field.lean** | Shared 32-bit-word fast-field implementation over the word bridge: the `Mont32Field` per-field data class, the `FastField` carrier, every field operation, the algebraic instances, and the `toField`/`ofField` correctness bridge — written once and instantiated by both BabyBear and KoalaBear. | | **Secp256k1.lean** | Base and scalar fields for the Secp256k1 curve (used in Bitcoin/Ethereum). | ## Binary-field modules diff --git a/docs/wiki/binary-fields-and-ntt.md b/docs/wiki/binary-fields-and-ntt.md index a0e4f0dc..feedc80d 100644 --- a/docs/wiki/binary-fields-and-ntt.md +++ b/docs/wiki/binary-fields-and-ntt.md @@ -20,6 +20,7 @@ CompPoly/Fields/ Montgomery/ Basic.lean Native32.lean + Native32Field.lean Binary/ Common.lean BF128Ghash/ From 61ef5a807c6dd34e6078fee60e0ffc0c24e28b35 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 10:13:15 +0200 Subject: [PATCH 08/31] refactor(fields): clarify Montgomery core API --- CompPoly/Fields/Montgomery/Basic.lean | 94 ++++++++----------- CompPoly/Fields/Montgomery/Native32Field.lean | 4 +- 2 files changed, 41 insertions(+), 57 deletions(-) diff --git a/CompPoly/Fields/Montgomery/Basic.lean b/CompPoly/Fields/Montgomery/Basic.lean index e6a8bce1..532e99f5 100644 --- a/CompPoly/Fields/Montgomery/Basic.lean +++ b/CompPoly/Fields/Montgomery/Basic.lean @@ -10,18 +10,10 @@ import Mathlib.Algebra.Field.ZMod import Mathlib.Tactic.Ring /-! -# Montgomery Reduction — radix-generic core +# Montgomery Reduction -Field-agnostic number theory behind native-word Montgomery reduction, shared by every -fast prime field. Everything here is parameterized by an abstract radix `R : Nat` (the -Montgomery modulus, e.g. `2^32` for 32-bit-word fields) and the prime `p`; the proofs use -only that `gcd(R, p) = 1` — supplied as the congruence `negInv * p ≡ R - 1 [MOD R]` — and -that `0 < R`, so nothing here is tied to a particular word size. - -The concrete word-level bridges (e.g. the `UInt32 × UInt64`, `R = 2^32` reduction) live in -sibling modules such as `CompPoly.Fields.Montgomery.Native32`, which instantiate `R` and -call into this core. These lemmas are all `Prop`/spec level and erased at codegen, so -sharing them carries no runtime cost. +Radix-generic specification and correctness lemmas for single-word Montgomery reduction. +Word-specific implementations refine these results in sibling modules. -/ namespace Montgomery @@ -29,8 +21,8 @@ namespace Montgomery /-- The Montgomery divisibility identity: if `negInv * p ≡ R - 1 [MOD R]` (i.e. `negInv = -p⁻¹ mod R`), then `R ∣ x + ((x mod R)·negInv mod R)·p` for every `x`. This is what makes Montgomery reduction integer-valued. -/ -theorem sum_dvd (R p negInv : Nat) (hR : 0 < R) - (hp : negInv * p ≡ R - 1 [MOD R]) (x : Nat) : +theorem dvd_add (R p negInv : ℕ) (hR : 0 < R) + (hnegInv : negInv * p ≡ R - 1 [MOD R]) (x : ℕ) : R ∣ x + ((x % R * negInv) % R) * p := by rw [← Nat.modEq_zero_iff_dvd] have hx : x ≡ x % R [MOD R] := (Nat.mod_modEq x R).symm @@ -42,7 +34,7 @@ theorem sum_dvd (R p negInv : Nat) (hR : 0 < R) ((x % R * negInv) % R) * p ≡ (x % R * negInv) * p [MOD R] := hmi.mul_right _ _ = x % R * (negInv * p) := by ring - _ ≡ x % R * (R - 1) [MOD R] := hp.mul_left _ + _ ≡ x % R * (R - 1) [MOD R] := hnegInv.mul_left _ calc x + ((x % R * negInv) % R) * p ≡ x % R + x % R * (R - 1) [MOD R] := hx.add hm @@ -54,66 +46,58 @@ theorem sum_dvd (R p negInv : Nat) (hR : 0 < R) rw [Nat.modEq_zero_iff_dvd] exact ⟨x % R, by rw [mul_comm]⟩ -/-- Nat-level Montgomery reduction: specifies and is used to prove the native-word reducer. -/ -def reduceNat (R p negInv x : Nat) : Nat := +/-- The quotient before the final conditional subtraction in Montgomery reduction. -/ +def reduceNatQuotient (R p negInv x : ℕ) : ℕ := + let m := (x % R * negInv) % R + (x + m * p) / R + +/-- Natural-number Montgomery reduction used to specify the native-word reducer. -/ +def reduceNat (R p negInv x : ℕ) : ℕ := let m := (x % R * negInv) % R let u := (x + m * p) / R if u < p then u else u - p -/-- Montgomery reduction descends to "multiply by `R⁻¹`" in `ZMod p`. -/ -theorem reduceNat_cast (R p negInv : Nat) [Fact (Nat.Prime p)] (hR : 0 < R) - (hp : negInv * p ≡ R - 1 [MOD R]) (hRne : (R : ZMod p) ≠ 0) (x : Nat) : - (reduceNat R p negInv x : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by +/-- The pre-subtraction quotient represents multiplication by `R⁻¹` in `ZMod p`. -/ +theorem reduceNatQuotient_cast (R p negInv : ℕ) [Fact (Nat.Prime p)] (hR : 0 < R) + (hnegInv : negInv * p ≡ R - 1 [MOD R]) (hRne : (R : ZMod p) ≠ 0) (x : ℕ) : + (reduceNatQuotient R p negInv x : ZMod p) = + (x : ZMod p) * (R : ZMod p)⁻¹ := by let m := x % R * negInv % R let u := (x + m * p) / R + change (u : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ have hdiv : R ∣ x + m * p := by - simpa [m] using sum_dvd R p negInv hR hp x + simpa [m] using dvd_add R p negInv hR hnegInv x have hu_mul : u * R = x + m * p := Nat.div_mul_cancel hdiv have hcast_mul : (u : ZMod p) * (R : ZMod p) = (x : ZMod p) := by rw [← Nat.cast_mul, hu_mul, Nat.cast_add, Nat.cast_mul] simp - by_cases hu : u < p - · have hmain : (u : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by - rw [← hcast_mul] - refine (mul_inv_cancel_right₀ ?_ (u : ZMod p)).symm - exact hRne - dsimp only [reduceNat] - rw [if_pos hu] - exact hmain - · have hfield : ((u - p : Nat) : ZMod p) = (u : ZMod p) := by - rw [Nat.cast_sub (Nat.le_of_not_gt hu)] - simp - have hmain : ((u - p : Nat) : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by - rw [hfield, ← hcast_mul] - refine (mul_inv_cancel_right₀ ?_ (u : ZMod p)).symm - exact hRne - dsimp only [reduceNat] - rw [if_neg hu] - exact hmain + rw [← hcast_mul] + exact (mul_inv_cancel_right₀ hRne (u : ZMod p)).symm -/-- The pre-conditional-subtract Montgomery quotient already casts to "multiply by `R⁻¹`" -in `ZMod p`. -/ -theorem quotient_cast (R p negInv : Nat) [Fact (Nat.Prime p)] (hR : 0 < R) - (hp : negInv * p ≡ R - 1 [MOD R]) (hRne : (R : ZMod p) ≠ 0) (x : Nat) : - (let m := x % R * negInv % R - let u := (x + m * p) / R - (u : ZMod p)) = (x : ZMod p) * (R : ZMod p)⁻¹ := by +/-- Montgomery reduction represents multiplication by `R⁻¹` in `ZMod p`. -/ +theorem reduceNat_cast (R p negInv : ℕ) [Fact (Nat.Prime p)] (hR : 0 < R) + (hnegInv : negInv * p ≡ R - 1 [MOD R]) (hRne : (R : ZMod p) ≠ 0) (x : ℕ) : + (reduceNat R p negInv x : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by let m := x % R * negInv % R let u := (x + m * p) / R - have hmr := reduceNat_cast R p negInv hR hp hRne x - unfold reduceNat at hmr - change (u : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ + have hu_cast : (u : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by + simpa only [reduceNatQuotient, m, u] using + reduceNatQuotient_cast R p negInv hR hnegInv hRne x + change ((if u < p then u else u - p : ℕ) : ZMod p) = + (x : ZMod p) * (R : ZMod p)⁻¹ by_cases hu : u < p - · simpa only [m, u, hu, if_true] using hmr - · have hfield : ((u - p : Nat) : ZMod p) = (u : ZMod p) := by + · rw [if_pos hu] + exact hu_cast + · have hfield : ((u - p : ℕ) : ZMod p) = (u : ZMod p) := by rw [Nat.cast_sub (Nat.le_of_not_gt hu)] simp - rw [← hfield] - simpa only [m, u, hu, if_false] using hmr + rw [if_neg hu] + rw [hfield] + exact hu_cast /-- Two naturals below `p` are equal once their `ZMod p` casts agree. -/ -theorem natCast_inj {p a b : Nat} (ha : a < p) (hb : b < p) - (h : (a : ZMod p) = (b : ZMod p)) : a = b := by +theorem natCast_inj_of_lt {p a b : ℕ} (h : (a : ZMod p) = (b : ZMod p)) + (ha : a < p) (hb : b < p) : a = b := by rw [ZMod.natCast_eq_natCast_iff] at h exact Nat.ModEq.eq_of_lt_of_lt h ha hb diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index 88f0e9a4..64210bc2 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -223,7 +223,7 @@ theorem montgomeryReduceBounded_cast (x : UInt64) P.fieldSize) / UInt32.size by exact Montgomery.Native32.u_eq_nat P.montgomeryNegInv P.modulus64 P.fieldSize P.modulus64_toNat P.fieldSize_pos P.two_fieldSize_mul_uint32Size_lt_two64 x h] - exact Montgomery.quotient_cast UInt32.size P.fieldSize P.montgomeryNegInv.toNat + exact Montgomery.reduceNatQuotient_cast UInt32.size P.fieldSize P.montgomeryNegInv.toNat (by decide) P.negInv_congr P.uint32Size_ne_zero_in_field x.toNat /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @@ -341,7 +341,7 @@ theorem raw_cast_eq_toField_mul (x : FastField F) : theorem nat_eq_of_field_eq {a b : Nat} (ha : a < P.fieldSize) (hb : b < P.fieldSize) (h : (a : ZMod P.fieldSize) = (b : ZMod P.fieldSize)) : a = b := - Montgomery.natCast_inj ha hb h + Montgomery.natCast_inj_of_lt h ha hb theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < P.fieldSize) : ((ofCanonicalNat (F := F) n h).val.toNat : ZMod P.fieldSize) = From e7032ba7eab8ab798bfcfba0235759aad8ec3fe2 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 10:56:05 +0200 Subject: [PATCH 09/31] refactor(fields): name native Montgomery quotient --- CompPoly/Fields/Montgomery/Basic.lean | 19 +++++- CompPoly/Fields/Montgomery/Native32.lean | 67 +++++++++---------- CompPoly/Fields/Montgomery/Native32Field.lean | 50 +++++++++----- 3 files changed, 81 insertions(+), 55 deletions(-) diff --git a/CompPoly/Fields/Montgomery/Basic.lean b/CompPoly/Fields/Montgomery/Basic.lean index 532e99f5..8bb2be76 100644 --- a/CompPoly/Fields/Montgomery/Basic.lean +++ b/CompPoly/Fields/Montgomery/Basic.lean @@ -57,11 +57,26 @@ def reduceNat (R p negInv x : ℕ) : ℕ := let u := (x + m * p) / R if u < p then u else u - p +/-- The pre-subtraction quotient is below twice the modulus. -/ +theorem reduceNatQuotient_lt_two_mul (R p negInv x : ℕ) + (hR : 0 < R) (hp : 0 < p) (hx : x < p * R) : + reduceNatQuotient R p negInv x < 2 * p := by + let m := x % R * negInv % R + have hm_lt : m < R := Nat.mod_lt _ hR + change (x + m * p) / R < 2 * p + rw [Nat.div_lt_iff_lt_mul] + · have hprod_lt : m * p < R * p := Nat.mul_lt_mul_of_pos_right hm_lt hp + have hprod_lt' : m * p < p * R := by + simpa only [Nat.mul_comm] using hprod_lt + calc + x + m * p < p * R + p * R := Nat.add_lt_add hx hprod_lt' + _ = 2 * p * R := by ring + · exact hR + /-- The pre-subtraction quotient represents multiplication by `R⁻¹` in `ZMod p`. -/ theorem reduceNatQuotient_cast (R p negInv : ℕ) [Fact (Nat.Prime p)] (hR : 0 < R) (hnegInv : negInv * p ≡ R - 1 [MOD R]) (hRne : (R : ZMod p) ≠ 0) (x : ℕ) : - (reduceNatQuotient R p negInv x : ZMod p) = - (x : ZMod p) * (R : ZMod p)⁻¹ := by + (reduceNatQuotient R p negInv x : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by let m := x % R * negInv % R let u := (x + m * p) / R change (u : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ diff --git a/CompPoly/Fields/Montgomery/Native32.lean b/CompPoly/Fields/Montgomery/Native32.lean index 9cd9df29..879e1d54 100644 --- a/CompPoly/Fields/Montgomery/Native32.lean +++ b/CompPoly/Fields/Montgomery/Native32.lean @@ -22,53 +22,48 @@ A future 64-bit family would add a parallel `Native64` bridge (`>>> 64`, namespace Montgomery namespace Native32 -/-- The native high-word extraction agrees with the `Nat`-level Montgomery quotient. -/ -theorem u_eq_nat (negInv : UInt32) (modN : UInt64) (p : Nat) - (hmod : modN.toNat = p) (hp_pos : 0 < p) - (hbound : 2 * p * UInt32.size < 2 ^ 64) - (x : UInt64) (h : x.toNat < p * UInt32.size) : - ((((x + (x.toUInt32 * negInv).toUInt64 * modN) >>> 32).toUInt32).toNat) = - (x.toNat + ((x.toNat % UInt32.size * negInv.toNat) % UInt32.size) * p) / UInt32.size := by +/-- The native pre-subtraction quotient in 32-bit Montgomery reduction. -/ +@[inline] +def reduceQuotient (negInv : UInt32) (p x : UInt64) : UInt32 := + ((x + (x.toUInt32 * negInv).toUInt64 * p) >>> 32).toUInt32 + +/-- The native quotient agrees with the `Nat`-level Montgomery quotient. -/ +theorem reduceQuotient_toNat (negInv : UInt32) (p : UInt64) + (hp_pos : 0 < p.toNat) (hbound : p.toNat < 2 ^ 31) + (x : UInt64) (h : x.toNat < p.toNat * 2 ^ 32) : + (reduceQuotient negInv p x).toNat = + reduceNatQuotient (2 ^ 32) p.toNat negInv.toNat x.toNat := by simp only [UInt64.toNat_shiftRight, UInt64.toNat_toUInt32, UInt64.toNat_add, UInt64.toNat_mul, UInt32.toNat_toUInt64, UInt32.toNat_mul, UInt64.toNat_ofNat, - hmod, Nat.shiftRight_eq_div_pow] + reduceQuotient, reduceNatQuotient, Nat.shiftRight_eq_div_pow] norm_num [UInt32.size] at h hbound ⊢ - let mNat := x.toNat * negInv.toNat % 4294967296 - have hm_lt : mNat < 4294967296 := Nat.mod_lt _ (by decide) - have hsum_lt : x.toNat + mNat * p < 18446744073709551616 := by - have hprod_lt : mNat * p < p * 4294967296 := by + let mNat := x.toNat * negInv.toNat % 2 ^ 32 + have hm_lt : mNat < 2 ^ 32 := Nat.mod_lt _ (by decide) + have hsum_lt : x.toNat + mNat * p.toNat < 2 ^ 64 := by + have hprod_lt : mNat * p.toNat < p.toNat * 2 ^ 32 := by have := Nat.mul_lt_mul_of_pos_right hm_lt hp_pos simpa [Nat.mul_comm] using this - calc x.toNat + mNat * p < p * 4294967296 + p * 4294967296 := Nat.add_lt_add h hprod_lt - _ = 2 * p * 4294967296 := by ring - _ < 18446744073709551616 := hbound - change ((x.toNat + mNat * p) % 18446744073709551616 / 4294967296) % 4294967296 = - (x.toNat + mNat * p) / 4294967296 + calc + x.toNat + mNat * p.toNat < + p.toNat * 2 ^ 32 + p.toNat * 2 ^ 32 := Nat.add_lt_add h hprod_lt + _ = 2 * p.toNat * 2 ^ 32 := by ring + _ < 2 ^ 64 := by omega + change ((x.toNat + mNat * p.toNat) % 2 ^ 64 / 2 ^ 32) % 2 ^ 32 = + (x.toNat + mNat * p.toNat) / 2 ^ 32 rw [Nat.mod_eq_of_lt hsum_lt] rw [Nat.mod_eq_of_lt] rw [Nat.div_lt_iff_lt_mul] · exact hsum_lt · decide -/-- The native Montgomery quotient stays below `2 * p`, so a single conditional subtract -canonicalises it. -/ -theorem u_lt_two_mul (negInv : UInt32) (modN : UInt64) (p : Nat) - (hmod : modN.toNat = p) (hp_pos : 0 < p) - (hbound : 2 * p * UInt32.size < 2 ^ 64) - (x : UInt64) (h : x.toNat < p * UInt32.size) : - ((((x + (x.toUInt32 * negInv).toUInt64 * modN) >>> 32).toUInt32).toNat) < 2 * p := by - rw [u_eq_nat negInv modN p hmod hp_pos hbound x h] - let mNat := x.toNat % UInt32.size * negInv.toNat % UInt32.size - have hm_lt : mNat < UInt32.size := Nat.mod_lt _ (by decide) - rw [Nat.div_lt_iff_lt_mul] - · have hprod_lt : mNat * p < UInt32.size * p := Nat.mul_lt_mul_of_pos_right hm_lt hp_pos - have hprod_lt' : mNat * p < p * UInt32.size := by - simpa [Nat.mul_comm] using hprod_lt - change x.toNat + mNat * p < 2 * p * UInt32.size - calc x.toNat + mNat * p < p * UInt32.size + p * UInt32.size := - Nat.add_lt_add h hprod_lt' - _ = 2 * p * UInt32.size := by ring - · decide +/-- The native Montgomery quotient stays below twice the modulus, so one conditional +subtraction canonicalizes it. -/ +theorem reduceQuotient_toNat_lt_two_mul (negInv : UInt32) (p : UInt64) + (hp_pos : 0 < p.toNat) (hbound : p.toNat < 2 ^ 31) + (x : UInt64) (h : x.toNat < p.toNat * 2 ^ 32) : + (reduceQuotient negInv p x).toNat < 2 * p.toNat := by + rw [reduceQuotient_toNat negInv p hp_pos hbound x h] + apply reduceNatQuotient_lt_two_mul <;> simp_all end Native32 end Montgomery diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index 64210bc2..04f5f98b 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -181,17 +181,25 @@ def reduceUInt32 (x : UInt32) : FastField F := /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := - let m : UInt32 := x.toUInt32 * P.montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * P.modulus64) >>> 32).toUInt32 - reduceUInt32Lt2ModulusRaw (F := F) u + reduceUInt32Lt2ModulusRaw (F := F) + (Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x) theorem montgomeryReduceBoundedRaw_lt (x : UInt64) (h : x.toNat < P.fieldSize * UInt32.size) : (montgomeryReduceBoundedRaw (F := F) x).toNat < P.fieldSize := by + have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by + rw [P.modulus64_toNat] + have hp := P.fieldSize_add_fieldSize_lt_uint32Size + change P.fieldSize + P.fieldSize < 2 ^ 32 at hp + omega unfold montgomeryReduceBoundedRaw exact reduceUInt32Lt2ModulusRaw_lt _ - (Montgomery.Native32.u_lt_two_mul P.montgomeryNegInv P.modulus64 P.fieldSize - P.modulus64_toNat P.fieldSize_pos P.two_fieldSize_mul_uint32Size_lt_two64 x h) + (by + simpa only [P.modulus64_toNat] using + Montgomery.Native32.reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 + (by simpa only [P.modulus64_toNat] using P.fieldSize_pos) + hmodulus_bound + x (by simpa only [P.modulus64_toNat] using h)) /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] @@ -203,34 +211,42 @@ theorem montgomeryReduceBounded_cast (x : UInt64) (h : x.toNat < P.fieldSize * UInt32.size) : ((montgomeryReduceBounded (F := F) x h).val.toNat : ZMod P.fieldSize) = (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ := by + have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by + rw [P.modulus64_toNat] + have hp := P.fieldSize_add_fieldSize_lt_uint32Size + change P.fieldSize + P.fieldSize < 2 ^ 32 at hp + omega change ((montgomeryReduceBoundedRaw (F := F) x).toNat : ZMod P.fieldSize) = (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ unfold montgomeryReduceBoundedRaw - let m : UInt32 := x.toUInt32 * P.montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * P.modulus64) >>> 32).toUInt32 + let u := Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.fieldSize) = (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ have hred := reduceUInt32Lt2Modulus_cast (F := F) u - (Montgomery.Native32.u_lt_two_mul P.montgomeryNegInv P.modulus64 P.fieldSize - P.modulus64_toNat P.fieldSize_pos P.two_fieldSize_mul_uint32Size_lt_two64 x h) + (by + simpa only [P.modulus64_toNat] using + Montgomery.Native32.reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 + (by simpa only [P.modulus64_toNat] using P.fieldSize_pos) + hmodulus_bound + x (by simpa only [P.modulus64_toNat] using h)) change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.fieldSize) = (u.toNat : ZMod P.fieldSize) at hred rw [hred] change (u.toNat : ZMod P.fieldSize) = (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ - rw [show u.toNat = - (x.toNat + ((x.toNat % UInt32.size * P.montgomeryNegInv.toNat) % UInt32.size) * - P.fieldSize) / UInt32.size by - exact Montgomery.Native32.u_eq_nat P.montgomeryNegInv P.modulus64 P.fieldSize - P.modulus64_toNat P.fieldSize_pos P.two_fieldSize_mul_uint32Size_lt_two64 x h] - exact Montgomery.reduceNatQuotient_cast UInt32.size P.fieldSize P.montgomeryNegInv.toNat + rw [show u.toNat = reduceNatQuotient (2 ^ 32) P.fieldSize P.montgomeryNegInv.toNat x.toNat by + simpa only [u, P.modulus64_toNat] using + Montgomery.Native32.reduceQuotient_toNat P.montgomeryNegInv P.modulus64 + (by simpa only [P.modulus64_toNat] using P.fieldSize_pos) + hmodulus_bound + x (by simpa only [P.modulus64_toNat] using h)] + exact Montgomery.reduceNatQuotient_cast (2 ^ 32) P.fieldSize P.montgomeryNegInv.toNat (by decide) P.negInv_congr P.uint32Size_ne_zero_in_field x.toNat /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] def montgomeryReduce (x : UInt64) : FastField F := - let m : UInt32 := x.toUInt32 * P.montgomeryNegInv - let u : UInt32 := ((x + m.toUInt64 * P.modulus64) >>> 32).toUInt32 + let u := Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x reduceUInt32 (F := F) u /-! ## Conversions -/ From 40a831d020edc595f8ee1efe4fc4049ce4fa9b08 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 10:58:16 +0200 Subject: [PATCH 10/31] docs(fields): shorten Native32 module overview --- CompPoly/Fields/Montgomery/Native32.lean | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/CompPoly/Fields/Montgomery/Native32.lean b/CompPoly/Fields/Montgomery/Native32.lean index 879e1d54..a84dbfe2 100644 --- a/CompPoly/Fields/Montgomery/Native32.lean +++ b/CompPoly/Fields/Montgomery/Native32.lean @@ -7,16 +7,10 @@ Authors: Valerii Huhnin, Georgios Raikos import CompPoly.Fields.Montgomery.Basic /-! -# Montgomery Reduction — native `UInt32 × UInt64`, radix `R = 2^32` +# Native 32-bit Montgomery Reduction -The word-level bridge specialising `Montgomery.Basic` to 32-bit-word prime fields: the -Montgomery product fits in a `UInt64`, the radix is `R = UInt32.size = 2^32`, and the -high word is extracted with `>>> 32`. These lemmas relate the native-word computation to -the `Nat` formula proved generically in `Montgomery.Basic`, parameterized by the prime -`p`, the `UInt32` negated inverse `negInv`, and the `UInt64` modulus `modN`. - -A future 64-bit family would add a parallel `Native64` bridge (`>>> 64`, -`UInt64 × UInt128`) over the same `Montgomery.Basic` core. +Native `UInt32` Montgomery reduction with radix `2 ^ 32`, connected to the generic +natural-number specification in `Montgomery.Basic`. -/ namespace Montgomery From 8947f221e54ed1ff70e1788a9e7ff156943830f5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 11:35:53 +0200 Subject: [PATCH 11/31] refactor(fields): simplify Mont32Field assumptions --- CompPoly/Fields/BabyBear/Basic.lean | 6 +- CompPoly/Fields/BabyBear/Fast/Prelude.lean | 57 +--- CompPoly/Fields/KoalaBear/Fast/Prelude.lean | 57 +--- CompPoly/Fields/Montgomery/Native32.lean | 5 +- CompPoly/Fields/Montgomery/Native32Field.lean | 282 ++++++++++-------- 5 files changed, 179 insertions(+), 228 deletions(-) diff --git a/CompPoly/Fields/BabyBear/Basic.lean b/CompPoly/Fields/BabyBear/Basic.lean index 37a0729a..c0ed25de 100644 --- a/CompPoly/Fields/BabyBear/Basic.lean +++ b/CompPoly/Fields/BabyBear/Basic.lean @@ -47,12 +47,8 @@ def twoAdicity : Nat := 27 instance : Fact (Nat.Prime fieldSize) := ⟨is_prime⟩ -instance : _root_.Field Field := ZMod.instField fieldSize - instance : NonBinaryField Field where - char_neq_2 := by - simpa [Field, fieldSize] using - (by decide : (2 : ZMod (2 ^ 31 - 2 ^ 27 + 1)) ≠ 0) + char_neq_2 := by decide /-- Fermat-style inversion in `ZMod fieldSize`. -/ lemma inv_eq_pow (a : Field) (ha : a ≠ 0) : a⁻¹ = a ^ (fieldSize - 2) := by diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Prelude.lean index 4ed200a1..ded1b56d 100644 --- a/CompPoly/Fields/BabyBear/Fast/Prelude.lean +++ b/CompPoly/Fields/BabyBear/Fast/Prelude.lean @@ -47,42 +47,14 @@ def montgomeryNegInv : UInt32 := 0x77FFFFFF /-- The native `UInt64` modulus agrees with the mathematical BabyBear modulus. -/ @[simp] theorem modulus64_toNat : modulus64.toNat = BabyBear.fieldSize := by decide -theorem fieldSize_pos : 0 < BabyBear.fieldSize := by decide +theorem two_mul_fieldSize_lt_two_pow_32 : 2 * BabyBear.fieldSize < 2 ^ 32 := by decide -theorem two_lt_fieldSize : 2 < BabyBear.fieldSize := by decide +theorem two_pow_32_lt_three_mul_fieldSize : 2 ^ 32 < 3 * BabyBear.fieldSize := by decide -theorem fieldSize_lt_uint32Size : BabyBear.fieldSize < UInt32.size := by decide +theorem rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % BabyBear.fieldSize := by decide -theorem fieldSize_add_fieldSize_lt_two64 : - BabyBear.fieldSize + BabyBear.fieldSize < 2 ^ 64 := by decide - -theorem fieldSize_add_fieldSize_lt_uint32Size : - BabyBear.fieldSize + BabyBear.fieldSize < UInt32.size := by decide - -theorem fieldSize_mul_fieldSize_lt_two64 : - BabyBear.fieldSize * BabyBear.fieldSize < 2 ^ 64 := by decide - -theorem uint32Size_lt_three_fieldSize : - UInt32.size < BabyBear.fieldSize + BabyBear.fieldSize + BabyBear.fieldSize := by decide - -theorem fieldSize_mul_uint32Size_lt_two64 : - BabyBear.fieldSize * UInt32.size < 2 ^ 64 := by decide - -theorem two_fieldSize_mul_uint32Size_lt_two64 : - 2 * BabyBear.fieldSize * UInt32.size < 2 ^ 64 := by decide - -theorem uint32Size_ne_zero_in_field : - (UInt32.size : BabyBear.Field) ≠ 0 := by decide - -theorem rModModulus_lt_fieldSize : rModModulus.toNat < BabyBear.fieldSize := by decide - -theorem r2ModModulus_lt_fieldSize : r2ModModulus.toNat < BabyBear.fieldSize := by decide - -theorem rModModulus_cast : - (rModModulus.toNat : BabyBear.Field) = (UInt32.size : BabyBear.Field) := by decide - -theorem r2ModModulus_cast : - (r2ModModulus.toNat : BabyBear.Field) = (UInt32.size : BabyBear.Field) ^ 2 := by decide +theorem r2ModModulus_toNat : + r2ModModulus.toNat = (2 ^ 32) ^ 2 % BabyBear.fieldSize := by decide /-- The per-field data realizing BabyBear as a fast 32-bit-word Montgomery field. The five word constants are the only runtime data; every other field is a `decide`-checked fact. -/ @@ -96,22 +68,11 @@ instance instMont32Field : Mont32Field BabyBear.Field where montgomeryNegInv := montgomeryNegInv modulus_toNat := modulus_toNat modulus64_toNat := modulus64_toNat - fieldSize_pos := fieldSize_pos - two_lt_fieldSize := two_lt_fieldSize - fieldSize_lt_uint32Size := fieldSize_lt_uint32Size - fieldSize_add_fieldSize_lt_two64 := fieldSize_add_fieldSize_lt_two64 - fieldSize_add_fieldSize_lt_uint32Size := fieldSize_add_fieldSize_lt_uint32Size - fieldSize_mul_fieldSize_lt_two64 := fieldSize_mul_fieldSize_lt_two64 - uint32Size_lt_three_fieldSize := uint32Size_lt_three_fieldSize - fieldSize_mul_uint32Size_lt_two64 := fieldSize_mul_uint32Size_lt_two64 - two_fieldSize_mul_uint32Size_lt_two64 := two_fieldSize_mul_uint32Size_lt_two64 - uint32Size_ne_zero_in_field := uint32Size_ne_zero_in_field - rModModulus_lt_fieldSize := rModModulus_lt_fieldSize - r2ModModulus_lt_fieldSize := r2ModModulus_lt_fieldSize - rModModulus_cast := rModModulus_cast - r2ModModulus_cast := r2ModModulus_cast + two_mul_fieldSize_lt_two_pow_32 := two_mul_fieldSize_lt_two_pow_32 + two_pow_32_lt_three_mul_fieldSize := two_pow_32_lt_three_mul_fieldSize + rModModulus_toNat := rModModulus_toNat + r2ModModulus_toNat := r2ModModulus_toNat negInv_congr := by decide - two_ne_zero_in_field := by decide /-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ abbrev Field : Type := FastField BabyBear.Field diff --git a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean index 0317685d..4d34ca57 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean @@ -47,42 +47,14 @@ def montgomeryNegInv : UInt32 := 0x7EFFFFFF /-- The native `UInt64` modulus agrees with the mathematical KoalaBear modulus. -/ @[simp] theorem modulus64_toNat : modulus64.toNat = KoalaBear.fieldSize := by decide -theorem fieldSize_pos : 0 < KoalaBear.fieldSize := by decide +theorem two_mul_fieldSize_lt_two_pow_32 : 2 * KoalaBear.fieldSize < 2 ^ 32 := by decide -theorem two_lt_fieldSize : 2 < KoalaBear.fieldSize := by decide +theorem two_pow_32_lt_three_mul_fieldSize : 2 ^ 32 < 3 * KoalaBear.fieldSize := by decide -theorem fieldSize_lt_uint32Size : KoalaBear.fieldSize < UInt32.size := by decide +theorem rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % KoalaBear.fieldSize := by decide -theorem fieldSize_add_fieldSize_lt_two64 : - KoalaBear.fieldSize + KoalaBear.fieldSize < 2 ^ 64 := by decide - -theorem fieldSize_add_fieldSize_lt_uint32Size : - KoalaBear.fieldSize + KoalaBear.fieldSize < UInt32.size := by decide - -theorem fieldSize_mul_fieldSize_lt_two64 : - KoalaBear.fieldSize * KoalaBear.fieldSize < 2 ^ 64 := by decide - -theorem uint32Size_lt_three_fieldSize : - UInt32.size < KoalaBear.fieldSize + KoalaBear.fieldSize + KoalaBear.fieldSize := by decide - -theorem fieldSize_mul_uint32Size_lt_two64 : - KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by decide - -theorem two_fieldSize_mul_uint32Size_lt_two64 : - 2 * KoalaBear.fieldSize * UInt32.size < 2 ^ 64 := by decide - -theorem uint32Size_ne_zero_in_field : - (UInt32.size : KoalaBear.Field) ≠ 0 := by decide - -theorem rModModulus_lt_fieldSize : rModModulus.toNat < KoalaBear.fieldSize := by decide - -theorem r2ModModulus_lt_fieldSize : r2ModModulus.toNat < KoalaBear.fieldSize := by decide - -theorem rModModulus_cast : - (rModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) := by decide - -theorem r2ModModulus_cast : - (r2ModModulus.toNat : KoalaBear.Field) = (UInt32.size : KoalaBear.Field) ^ 2 := by decide +theorem r2ModModulus_toNat : + r2ModModulus.toNat = (2 ^ 32) ^ 2 % KoalaBear.fieldSize := by decide /-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. The five word constants are the only runtime data; every other field is a `decide`-checked fact. -/ @@ -96,22 +68,11 @@ instance instMont32Field : Mont32Field KoalaBear.Field where montgomeryNegInv := montgomeryNegInv modulus_toNat := modulus_toNat modulus64_toNat := modulus64_toNat - fieldSize_pos := fieldSize_pos - two_lt_fieldSize := two_lt_fieldSize - fieldSize_lt_uint32Size := fieldSize_lt_uint32Size - fieldSize_add_fieldSize_lt_two64 := fieldSize_add_fieldSize_lt_two64 - fieldSize_add_fieldSize_lt_uint32Size := fieldSize_add_fieldSize_lt_uint32Size - fieldSize_mul_fieldSize_lt_two64 := fieldSize_mul_fieldSize_lt_two64 - uint32Size_lt_three_fieldSize := uint32Size_lt_three_fieldSize - fieldSize_mul_uint32Size_lt_two64 := fieldSize_mul_uint32Size_lt_two64 - two_fieldSize_mul_uint32Size_lt_two64 := two_fieldSize_mul_uint32Size_lt_two64 - uint32Size_ne_zero_in_field := uint32Size_ne_zero_in_field - rModModulus_lt_fieldSize := rModModulus_lt_fieldSize - r2ModModulus_lt_fieldSize := r2ModModulus_lt_fieldSize - rModModulus_cast := rModModulus_cast - r2ModModulus_cast := r2ModModulus_cast + two_mul_fieldSize_lt_two_pow_32 := two_mul_fieldSize_lt_two_pow_32 + two_pow_32_lt_three_mul_fieldSize := two_pow_32_lt_three_mul_fieldSize + rModModulus_toNat := rModModulus_toNat + r2ModModulus_toNat := r2ModModulus_toNat negInv_congr := by decide - two_ne_zero_in_field := by decide /-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ abbrev Field : Type := FastField KoalaBear.Field diff --git a/CompPoly/Fields/Montgomery/Native32.lean b/CompPoly/Fields/Montgomery/Native32.lean index a84dbfe2..e8dbfea6 100644 --- a/CompPoly/Fields/Montgomery/Native32.lean +++ b/CompPoly/Fields/Montgomery/Native32.lean @@ -30,7 +30,6 @@ theorem reduceQuotient_toNat (negInv : UInt32) (p : UInt64) simp only [UInt64.toNat_shiftRight, UInt64.toNat_toUInt32, UInt64.toNat_add, UInt64.toNat_mul, UInt32.toNat_toUInt64, UInt32.toNat_mul, UInt64.toNat_ofNat, reduceQuotient, reduceNatQuotient, Nat.shiftRight_eq_div_pow] - norm_num [UInt32.size] at h hbound ⊢ let mNat := x.toNat * negInv.toNat % 2 ^ 32 have hm_lt : mNat < 2 ^ 32 := Nat.mod_lt _ (by decide) have hsum_lt : x.toNat + mNat * p.toNat < 2 ^ 64 := by @@ -42,10 +41,10 @@ theorem reduceQuotient_toNat (negInv : UInt32) (p : UInt64) p.toNat * 2 ^ 32 + p.toNat * 2 ^ 32 := Nat.add_lt_add h hprod_lt _ = 2 * p.toNat * 2 ^ 32 := by ring _ < 2 ^ 64 := by omega + norm_num [UInt32.size] change ((x.toNat + mNat * p.toNat) % 2 ^ 64 / 2 ^ 32) % 2 ^ 32 = (x.toNat + mNat * p.toNat) / 2 ^ 32 - rw [Nat.mod_eq_of_lt hsum_lt] - rw [Nat.mod_eq_of_lt] + rw [Nat.mod_eq_of_lt hsum_lt, Nat.mod_eq_of_lt] rw [Nat.div_lt_iff_lt_mul] · exact hsum_lt · decide diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index 04f5f98b..1f192b6f 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -38,12 +38,11 @@ namespace Native32 /-- Per-field data for a fast 32-bit-word Montgomery prime field. The five word constants (`modulus`, `modulus64`, `rModModulus`, `r2ModModulus`, -`montgomeryNegInv`) are the only runtime data; the remaining fields are `Prop`s, all -dischargeable by `decide` for a concrete field, and are erased at codegen. `F` is a tag -(in practice the canonical `ZMod fieldSize` field) used for instance resolution. -/ +`montgomeryNegInv`) are the only runtime data; the remaining fields are `Prop`s and +erased at codegen. -/ class Mont32Field (F : Type) where /-- The field size / prime `p`. -/ - fieldSize : Nat + fieldSize : ℕ /-- `fieldSize` is prime — needed to reduce into `ZMod fieldSize` as a field. -/ prime : Fact (Nat.Prime fieldSize) /-- `fieldSize` as a 32-bit word. -/ @@ -58,29 +57,63 @@ class Mont32Field (F : Type) where montgomeryNegInv : UInt32 modulus_toNat : modulus.toNat = fieldSize modulus64_toNat : modulus64.toNat = fieldSize - fieldSize_pos : 0 < fieldSize - two_lt_fieldSize : 2 < fieldSize - fieldSize_lt_uint32Size : fieldSize < UInt32.size - fieldSize_add_fieldSize_lt_two64 : fieldSize + fieldSize < 2 ^ 64 - fieldSize_add_fieldSize_lt_uint32Size : fieldSize + fieldSize < UInt32.size - fieldSize_mul_fieldSize_lt_two64 : fieldSize * fieldSize < 2 ^ 64 - uint32Size_lt_three_fieldSize : UInt32.size < fieldSize + fieldSize + fieldSize - fieldSize_mul_uint32Size_lt_two64 : fieldSize * UInt32.size < 2 ^ 64 - two_fieldSize_mul_uint32Size_lt_two64 : 2 * fieldSize * UInt32.size < 2 ^ 64 - uint32Size_ne_zero_in_field : (UInt32.size : ZMod fieldSize) ≠ 0 - rModModulus_lt_fieldSize : rModModulus.toNat < fieldSize - r2ModModulus_lt_fieldSize : r2ModModulus.toNat < fieldSize - rModModulus_cast : (rModModulus.toNat : ZMod fieldSize) = (UInt32.size : ZMod fieldSize) - r2ModModulus_cast : - (r2ModModulus.toNat : ZMod fieldSize) = (UInt32.size : ZMod fieldSize) ^ 2 + two_mul_fieldSize_lt_two_pow_32 : 2 * fieldSize < 2 ^ 32 + two_pow_32_lt_three_mul_fieldSize : 2 ^ 32 < 3 * fieldSize + rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % fieldSize + r2ModModulus_toNat : r2ModModulus.toNat = (2 ^ 32) ^ 2 % fieldSize /-- The Montgomery inverse congruence `negInv * p ≡ 2^32 - 1 [MOD 2^32]`. -/ negInv_congr : - montgomeryNegInv.toNat * fieldSize ≡ UInt32.size - 1 [MOD UInt32.size] - /-- Characteristic `≠ 2`, used to build the `NonBinaryField` instance. -/ - two_ne_zero_in_field : (2 : ZMod fieldSize) ≠ 0 + montgomeryNegInv.toNat * fieldSize ≡ 2 ^ 32 - 1 [MOD 2 ^ 32] attribute [instance] Mont32Field.prime +namespace Mont32Field + +theorem two_lt_fieldSize {F : Type} [P : Mont32Field F] : 2 < P.fieldSize := by + have h := P.two_pow_32_lt_three_mul_fieldSize + omega + +theorem fieldSize_pos {F : Type} [P : Mont32Field F] : 0 < P.fieldSize := by + exact Nat.zero_lt_of_lt P.two_lt_fieldSize + +theorem fieldSize_lt_two_pow_32 {F : Type} [P : Mont32Field F] : + P.fieldSize < 2 ^ 32 := by + have h := P.two_mul_fieldSize_lt_two_pow_32 + omega + +theorem fieldSize_mul_fieldSize_lt_two_pow_64 {F : Type} [P : Mont32Field F] : + P.fieldSize * P.fieldSize < 2 ^ 64 := by + nlinarith [P.fieldSize_lt_two_pow_32] + +theorem two_pow_32_ne_zero_in_field {F : Type} [P : Mont32Field F] : + ((2 ^ 32 : ℕ) : ZMod P.fieldSize) ≠ 0 := by + have htwo : (2 : ZMod P.fieldSize) ≠ 0 := by + intro h + have hdvd : P.fieldSize ∣ 2 := (ZMod.natCast_eq_zero_iff 2 P.fieldSize).mp h + exact (Nat.not_le_of_gt P.two_lt_fieldSize) (Nat.le_of_dvd (by decide) hdvd) + rw [Nat.cast_pow] + exact pow_ne_zero 32 htwo + +theorem rModModulus_lt_fieldSize {F : Type} [P : Mont32Field F] : + P.rModModulus.toNat < P.fieldSize := by + rw [P.rModModulus_toNat] + exact Nat.mod_lt _ P.fieldSize_pos + +theorem r2ModModulus_lt_fieldSize {F : Type} [P : Mont32Field F] : + P.r2ModModulus.toNat < P.fieldSize := by + rw [P.r2ModModulus_toNat] + exact Nat.mod_lt _ P.fieldSize_pos + +theorem rModModulus_cast {F : Type} [P : Mont32Field F] : + (P.rModModulus.toNat : ZMod P.fieldSize) = ((2 ^ 32 : ℕ) : ZMod P.fieldSize) := by + rw [P.rModModulus_toNat, ZMod.natCast_mod] + +theorem r2ModModulus_cast {F : Type} [P : Mont32Field F] : + (P.r2ModModulus.toNat : ZMod P.fieldSize) = ((2 ^ 32 : ℕ) : ZMod P.fieldSize) ^ 2 := by + rw [P.r2ModModulus_toNat, ZMod.natCast_mod, Nat.cast_pow] + +end Mont32Field + /-- The fast carrier for the field tagged by `F`: a native word below `fieldSize`, interpreted as a Montgomery residue. At runtime this erases to `UInt32`. -/ def FastField (F : Type) [Mont32Field F] : Type := @@ -175,7 +208,8 @@ def reduceUInt32 (x : UInt32) : FastField F := exact Nat.le_of_not_gt hy rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, P.modulus_toNat, hy_eq] have hx_lt := UInt32.toNat_lt_size x - have hthree := P.uint32Size_lt_three_fieldSize + change x.toNat < 2 ^ 32 at hx_lt + have hthree := P.two_pow_32_lt_three_mul_fieldSize omega⟩ /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @@ -185,12 +219,11 @@ def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := (Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x) theorem montgomeryReduceBoundedRaw_lt (x : UInt64) - (h : x.toNat < P.fieldSize * UInt32.size) : + (h : x.toNat < P.fieldSize * 2 ^ 32) : (montgomeryReduceBoundedRaw (F := F) x).toNat < P.fieldSize := by have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by rw [P.modulus64_toNat] - have hp := P.fieldSize_add_fieldSize_lt_uint32Size - change P.fieldSize + P.fieldSize < 2 ^ 32 at hp + have hp := P.two_mul_fieldSize_lt_two_pow_32 omega unfold montgomeryReduceBoundedRaw exact reduceUInt32Lt2ModulusRaw_lt _ @@ -204,24 +237,23 @@ theorem montgomeryReduceBoundedRaw_lt (x : UInt64) /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBounded (x : UInt64) - (h : x.toNat < P.fieldSize * UInt32.size) : FastField F := + (h : x.toNat < P.fieldSize * 2 ^ 32) : FastField F := ⟨montgomeryReduceBoundedRaw (F := F) x, montgomeryReduceBoundedRaw_lt x h⟩ theorem montgomeryReduceBounded_cast (x : UInt64) - (h : x.toNat < P.fieldSize * UInt32.size) : + (h : x.toNat < P.fieldSize * 2 ^ 32) : ((montgomeryReduceBounded (F := F) x h).val.toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ := by + (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ := by have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by rw [P.modulus64_toNat] - have hp := P.fieldSize_add_fieldSize_lt_uint32Size - change P.fieldSize + P.fieldSize < 2 ^ 32 at hp + have hp := P.two_mul_fieldSize_lt_two_pow_32 omega change ((montgomeryReduceBoundedRaw (F := F) x).toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ unfold montgomeryReduceBoundedRaw let u := Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ have hred := reduceUInt32Lt2Modulus_cast (F := F) u (by simpa only [P.modulus64_toNat] using @@ -233,7 +265,7 @@ theorem montgomeryReduceBounded_cast (x : UInt64) (u.toNat : ZMod P.fieldSize) at hred rw [hred] change (u.toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ rw [show u.toNat = reduceNatQuotient (2 ^ 32) P.fieldSize P.montgomeryNegInv.toNat x.toNat by simpa only [u, P.modulus64_toNat] using Montgomery.Native32.reduceQuotient_toNat P.montgomeryNegInv P.modulus64 @@ -241,7 +273,7 @@ theorem montgomeryReduceBounded_cast (x : UInt64) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)] exact Montgomery.reduceNatQuotient_cast (2 ^ 32) P.fieldSize P.montgomeryNegInv.toNat - (by decide) P.negInv_congr P.uint32Size_ne_zero_in_field x.toNat + (by decide) P.negInv_congr P.two_pow_32_ne_zero_in_field x.toNat /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] @@ -253,15 +285,15 @@ def montgomeryReduce (x : UInt64) : FastField F := /-- Build a fast element from a canonical natural representative. -/ @[inline] -def ofCanonicalNat (n : Nat) (_h : n < P.fieldSize) : FastField F := +def ofCanonicalNat (n : ℕ) (_h : n < P.fieldSize) : FastField F := montgomeryReduceBounded (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by apply Nat.mod_eq_of_lt - exact Nat.lt_trans _h (Nat.lt_trans P.fieldSize_lt_uint32Size (by decide)) + exact Nat.lt_trans _h (Nat.lt_trans P.fieldSize_lt_two_pow_32 (by decide)) rw [hnmod] have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_fieldSize]) @@ -275,7 +307,7 @@ def reduceUInt64 (x : UInt64) : FastField F := rw [UInt64.toNat_mod, P.modulus64_toNat] exact Nat.mod_lt _ P.fieldSize_pos have hprod : (x % P.modulus64).toNat * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_fieldSize]) @@ -290,7 +322,7 @@ def one : FastField F := ⟨P.rModModulus, P.rModModulus_lt_fieldSize⟩ /-- Convert a natural number into fast Montgomery representation. -/ @[inline] -def ofNat (n : Nat) : FastField F := +def ofNat (n : ℕ) : FastField F := ofCanonicalNat (n % P.fieldSize) (Nat.mod_lt _ P.fieldSize_pos) /-- Convert a 32-bit word into fast Montgomery representation. -/ @@ -317,7 +349,7 @@ def toCanonicalUInt32 (x : FastField F) : UInt32 := /-- Convert a fast element to its canonical natural representative. -/ @[inline] -def toNat (x : FastField F) : Nat := +def toNat (x : FastField F) : ℕ := (toCanonicalUInt32 x).toNat /-- Convert a fast element to the canonical `ZMod` field. -/ @@ -334,81 +366,81 @@ theorem toNat_lt_fieldSize (x : FastField F) : toNat x < P.fieldSize := by theorem toField_eq_raw_mul_inv (x : FastField F) : toField x = - (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ := by + (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ := by unfold toField toNat toCanonicalUInt32 raw have hred := montgomeryReduceBounded_cast x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] nlinarith [x.property, P.fieldSize_pos]) change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.fieldSize) = - (x.val.toUInt64.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ at hred + (x.val.toUInt64.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ at hred change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.fieldSize) = - (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ rw [hred] rw [UInt32.toNat_toUInt64] theorem raw_cast_eq_toField_mul (x : FastField F) : (x.val.toNat : ZMod P.fieldSize) = - toField x * (UInt32.size : ZMod P.fieldSize) := by + toField x * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) := by rw [toField_eq_raw_mul_inv] rw [mul_assoc] - rw [inv_mul_cancel₀ P.uint32Size_ne_zero_in_field] + rw [inv_mul_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] -theorem nat_eq_of_field_eq {a b : Nat} (ha : a < P.fieldSize) +theorem nat_eq_of_field_eq {a b : ℕ} (ha : a < P.fieldSize) (hb : b < P.fieldSize) (h : (a : ZMod P.fieldSize) = (b : ZMod P.fieldSize)) : a = b := Montgomery.natCast_inj_of_lt h ha hb -theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < P.fieldSize) : +theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < P.fieldSize) : ((ofCanonicalNat (F := F) n h).val.toNat : ZMod P.fieldSize) = - (n : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize) := by + (n : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) := by unfold ofCanonicalNat have hred := montgomeryReduceBounded_cast (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans P.fieldSize_lt_uint32Size (by decide)) + exact Nat.lt_trans h (Nat.lt_trans P.fieldSize_lt_two_pow_32 (by decide)) rw [hnmod] have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_fieldSize]) change ((montgomeryReduceBoundedRaw (F := F) (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.fieldSize) = ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)⁻¹ at hred + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ at hred change ((montgomeryReduceBoundedRaw (F := F) (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.fieldSize) = - (n : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize) + (n : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) rw [hred] simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans P.fieldSize_lt_uint32Size (by decide)) + exact Nat.lt_trans h (Nat.lt_trans P.fieldSize_lt_two_pow_32 (by decide)) rw [hnmod] have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] rw [Nat.cast_mul, P.r2ModModulus_cast] rw [pow_two] - rw [mul_assoc (n : ZMod P.fieldSize) ((UInt32.size : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)) ((UInt32.size : ZMod P.fieldSize)⁻¹)] - rw [mul_assoc (UInt32.size : ZMod P.fieldSize) (UInt32.size : ZMod P.fieldSize) - ((UInt32.size : ZMod P.fieldSize)⁻¹)] - rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_assoc (n : ZMod P.fieldSize) (((2 ^ 32 : ℕ) : ZMod P.fieldSize) * + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)) (((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹)] + rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod P.fieldSize) ((2 ^ 32 : ℕ) : ZMod P.fieldSize) + (((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹)] + rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] -theorem toField_ofCanonicalNat_aux (n : Nat) (h : n < P.fieldSize) : +theorem toField_ofCanonicalNat_aux (n : ℕ) (h : n < P.fieldSize) : toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.fieldSize) := by rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] rw [mul_assoc] - rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] theorem reduceUInt64_raw_cast (x : UInt64) : ((reduceUInt64 (F := F) x).val.toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize) := by + (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) := by unfold reduceUInt64 let y := x % P.modulus64 have hred := montgomeryReduceBounded_cast (y * P.r2ModModulus.toUInt64) (by @@ -417,23 +449,23 @@ theorem reduceUInt64_raw_cast (x : UInt64) : rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] exact Nat.mod_lt _ P.fieldSize_pos have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_fieldSize]) change ((montgomeryReduceBoundedRaw (F := F) (y * P.r2ModModulus.toUInt64)).toNat : ZMod P.fieldSize) = ((y * P.r2ModModulus.toUInt64).toNat : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)⁻¹ at hred + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ at hred change ((montgomeryReduceBoundedRaw (F := F) (y * P.r2ModModulus.toUInt64)).toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize) + (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) rw [hred] simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hy_lt : y.toNat < P.fieldSize := by rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] exact Nat.mod_lt _ P.fieldSize_pos have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two64] + nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] have hy_cast : (y.toNat : ZMod P.fieldSize) = (x.toNat : ZMod P.fieldSize) := by rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] @@ -441,20 +473,20 @@ theorem reduceUInt64_raw_cast (x : UInt64) : exact Nat.mod_modEq _ _ rw [Nat.cast_mul, P.r2ModModulus_cast, hy_cast] rw [pow_two] - rw [mul_assoc (x.toNat : ZMod P.fieldSize) ((UInt32.size : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)) ((UInt32.size : ZMod P.fieldSize)⁻¹)] - rw [mul_assoc (UInt32.size : ZMod P.fieldSize) (UInt32.size : ZMod P.fieldSize) - ((UInt32.size : ZMod P.fieldSize)⁻¹)] - rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_assoc (x.toNat : ZMod P.fieldSize) (((2 ^ 32 : ℕ) : ZMod P.fieldSize) * + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)) (((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹)] + rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod P.fieldSize) ((2 ^ 32 : ℕ) : ZMod P.fieldSize) + (((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹)] + rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] @[simp] -theorem toNat_ofCanonicalNat (n : Nat) (h : n < P.fieldSize) : +theorem toNat_ofCanonicalNat (n : ℕ) (h : n < P.fieldSize) : toNat (ofCanonicalNat (F := F) n h) = n := nat_eq_of_field_eq (toNat_lt_fieldSize _) h (toField_ofCanonicalNat_aux n h) @[simp] -theorem toField_ofCanonicalNat (n : Nat) (h : n < P.fieldSize) : +theorem toField_ofCanonicalNat (n : ℕ) (h : n < P.fieldSize) : toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.fieldSize) := toField_ofCanonicalNat_aux n h @@ -462,10 +494,10 @@ theorem toField_ofCanonicalNat (n : Nat) (h : n < P.fieldSize) : theorem toNat_reduceUInt64 (x : UInt64) : toNat (reduceUInt64 (F := F) x) = x.toNat % P.fieldSize := by apply nat_eq_of_field_eq (toNat_lt_fieldSize _) (Nat.mod_lt _ P.fieldSize_pos) - change toField (reduceUInt64 (F := F) x) = ((x.toNat % P.fieldSize : Nat) : ZMod P.fieldSize) + change toField (reduceUInt64 (F := F) x) = ((x.toNat % P.fieldSize : ℕ) : ZMod P.fieldSize) rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] rw [mul_assoc] - rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] rw [ZMod.natCast_eq_natCast_iff] exact (Nat.mod_modEq _ _).symm @@ -475,7 +507,7 @@ theorem toField_reduceUInt64 (x : UInt64) : toField (reduceUInt64 (F := F) x) = (x.toNat : ZMod P.fieldSize) := by rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] rw [mul_assoc] - rw [mul_inv_cancel₀ P.uint32Size_ne_zero_in_field] + rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] /-! ## Field operations -/ @@ -517,8 +549,8 @@ def sub (x y : FastField F) : FastField F := have := x.property; omega⟩ else ⟨x.val + P.modulus - y.val, by - have hsum_lt : x.val.toNat + P.fieldSize < UInt32.size := by - have htwo := P.fieldSize_add_fieldSize_lt_uint32Size + have hsum_lt : x.val.toNat + P.fieldSize < 2 ^ 32 := by + have htwo := P.two_mul_fieldSize_lt_two_pow_32 have := x.property; omega have hsum_eq : (x.val + P.modulus).toNat = x.val.toNat + P.fieldSize := by rw [UInt32.toNat_add, P.modulus_toNat, Nat.mod_eq_of_lt hsum_lt] @@ -539,9 +571,9 @@ def mul (x y : FastField F) : FastField F := montgomeryReduceBounded (x.val.toUInt64 * y.val.toUInt64) (by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two64] + nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, P.fieldSize_lt_uint32Size, P.fieldSize_pos]) + nlinarith [x.property, y.property, P.fieldSize_lt_two_pow_32, P.fieldSize_pos]) /-- Fast squaring. -/ @[inline] @@ -550,11 +582,11 @@ def square (x : FastField F) : FastField F := /-- Exponentiation over the fast representation using repeated squaring. -/ @[specialize] -def pow (x : FastField F) (n : Nat) : FastField F := +def pow (x : FastField F) (n : ℕ) : FastField F := @npowBinRec (FastField F) ⟨one⟩ ⟨mul⟩ n x /-- Fermat exponent used for inversion in the prime field. -/ -def invExponent : Nat := P.fieldSize - 2 +def invExponent : ℕ := P.fieldSize - 2 /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`), by binary exponentiation (`pow`). -/ @@ -597,13 +629,13 @@ instance instNatCast : NatCast (FastField F) where instance instIntCast : IntCast (FastField F) where intCast := ofInt -instance instNatSMul : SMul Nat (FastField F) where +instance instNatSMul : SMul ℕ (FastField F) where smul n x := ofNat n * x instance instIntSMul : SMul Int (FastField F) where smul n x := ofInt n * x -instance instPowNat : Pow (FastField F) Nat where +instance instPowNat : Pow (FastField F) ℕ where pow := pow instance instPowInt : Pow (FastField F) Int where @@ -665,7 +697,7 @@ theorem toField_injective : Function.Injective (toField (F := F)) := @[simp] theorem toField_zero : toField (0 : FastField F) = 0 := by rw [toField_eq_raw_mul_inv] - change ((0 : Nat) : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ = 0 + change ((0 : ℕ) : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = 0 rw [Nat.cast_zero, zero_mul] /-- `toField` maps fast one to canonical one. -/ @@ -673,9 +705,9 @@ theorem toField_zero : toField (0 : FastField F) = 0 := by theorem toField_one : toField (1 : FastField F) = 1 := by rw [toField_eq_raw_mul_inv] change (P.rModModulus.toNat : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)⁻¹ = 1 + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = 1 rw [P.rModModulus_cast] - exact mul_inv_cancel₀ P.uint32Size_ne_zero_in_field + exact mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field /-- Fast addition agrees with addition in the canonical field. -/ @[simp] @@ -689,13 +721,13 @@ theorem toField_add (x y : FastField F) : toField (x + y) = toField x + toField change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.fieldSize) = ((x.val + y.val).toNat : ZMod P.fieldSize) at hred change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)⁻¹ = - (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + - (y.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = + (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ + + (y.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ rw [hred] rw [UInt32.toNat_add] - have hsum_lt : x.val.toNat + y.val.toNat < UInt32.size := by - nlinarith [x.property, y.property, P.fieldSize_add_fieldSize_lt_uint32Size] + have hsum_lt : x.val.toNat + y.val.toNat < 2 ^ 32 := by + nlinarith [x.property, y.property, P.two_mul_fieldSize_lt_two_pow_32] rw [Nat.mod_eq_of_lt hsum_lt] rw [Nat.cast_add] ring @@ -711,16 +743,16 @@ theorem toField_sub (x y : FastField F) : toField (x - y) = toField x - toField rw [dif_pos hyx] rw [hsubval] change (((x.val - y.val).toNat : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)⁻¹) = - (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ - - (y.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) = + (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ - + (y.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ rw [UInt32.toNat_sub_of_le _ _ hyx] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le] at hyx exact hyx)] ring - · have hsum_lt : x.val.toNat + P.fieldSize < UInt32.size := by - have htwo := P.fieldSize_add_fieldSize_lt_uint32Size + · have hsum_lt : x.val.toNat + P.fieldSize < 2 ^ 32 := by + have htwo := P.two_mul_fieldSize_lt_two_pow_32 have := x.property; omega have hsum_eq : (x.val + P.modulus).toNat = x.val.toNat + P.fieldSize := by rw [UInt32.toNat_add, P.modulus_toNat, Nat.mod_eq_of_lt hsum_lt] @@ -733,9 +765,9 @@ theorem toField_sub (x y : FastField F) : toField (x - y) = toField x - toField rw [dif_neg hyx] rw [hsubval] change (((x.val + P.modulus - y.val).toNat : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)⁻¹) = - (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ - - (y.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) = + (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ - + (y.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le, hsum_eq] at hyle @@ -754,13 +786,13 @@ theorem toField_neg (x : FastField F) : toField (-x) = -toField x := by rw [dif_pos hx] rw [hnegval] change ((zero : FastField F).val.toNat : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)⁻¹ = - -((x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹) + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = + -((x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) have hxNat : x.val.toNat = 0 := by simpa using congrArg UInt32.toNat hx rw [hxNat] - change ((0 : Nat) : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ = - -(((0 : Nat) : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹) + change ((0 : ℕ) : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = + -(((0 : ℕ) : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) simp · have hle : x.val ≤ P.modulus := by rw [UInt32.le_iff_toNat_le, P.modulus_toNat] @@ -771,8 +803,8 @@ theorem toField_neg (x : FastField F) : toField (-x) = -toField x := by rw [dif_neg hx] rw [hnegval] change (((P.modulus - x.val).toNat : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)⁻¹) = - -((x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹) + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) = + -((x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus_toNat] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le, P.modulus_toNat] at hle @@ -788,21 +820,21 @@ theorem toField_mul (x y : FastField F) : toField (x * y) = toField x * toField have hred := montgomeryReduceBounded_cast (F := F) (x.val.toUInt64 * y.val.toUInt64) (by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two64] + nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, P.fieldSize_lt_uint32Size, P.fieldSize_pos]) + nlinarith [x.property, y.property, P.fieldSize_lt_two_pow_32, P.fieldSize_pos]) change ((montgomeryReduceBoundedRaw (F := F) (x.val.toUInt64 * y.val.toUInt64)).toNat : ZMod P.fieldSize) = ((x.val.toUInt64 * y.val.toUInt64).toNat : ZMod P.fieldSize) * - (UInt32.size : ZMod P.fieldSize)⁻¹ at hred + ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ at hred change ((montgomeryReduceBoundedRaw (F := F) (x.val.toUInt64 * y.val.toUInt64)).toNat : - ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ = - (x.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹ * - ((y.val.toNat : ZMod P.fieldSize) * (UInt32.size : ZMod P.fieldSize)⁻¹) + ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = + (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ * + ((y.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) rw [hred] simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two64] + nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] rw [Nat.cast_mul] ring @@ -828,7 +860,7 @@ private theorem mul_assoc_field (x y z : FastField F) : (x * y) * z = x * (y * z rw [toField_mul, toField_mul, toField_mul, toField_mul] ring -private theorem pow_succ_field (x : FastField F) (n : Nat) : pow x (n + 1) = pow x n * x := by +private theorem pow_succ_field (x : FastField F) (n : ℕ) : pow x (n + 1) = pow x n * x := by unfold pow letI : Semigroup (FastField F) := { mul := (· * ·) @@ -844,7 +876,7 @@ theorem toField_square (x : FastField F) : toField (square x) = toField x * toFi /-- Fast natural-power computation agrees with powers in the canonical field. -/ @[simp] -theorem toField_pow (x : FastField F) (n : Nat) : toField (pow x n) = toField x ^ n := by +theorem toField_pow (x : FastField F) (n : ℕ) : toField (pow x n) = toField x ^ n := by induction n with | zero => unfold pow @@ -895,7 +927,7 @@ theorem toField_div (x y : FastField F) : toField (x / y) = toField x / toField /-- Natural casts into fast form agree with natural casts into the canonical field. -/ @[simp] -theorem toField_natCast (n : Nat) : toField (n : FastField F) = (n : ZMod P.fieldSize) := by +theorem toField_natCast (n : ℕ) : toField (n : FastField F) = (n : ZMod P.fieldSize) := by change toField (ofNat n) = (n : ZMod P.fieldSize) unfold ofNat rw [toField_ofCanonicalNat] @@ -911,7 +943,7 @@ theorem toField_intCast (n : Int) : toField (n : FastField F) = (n : ZMod P.fiel /-- Natural scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_nsmul (n : Nat) (x : FastField F) : toField (n • x) = n • toField x := by +theorem toField_nsmul (n : ℕ) (x : FastField F) : toField (n • x) = n • toField x := by change toField ((n : FastField F) * x) = n • toField x rw [toField_mul, toField_natCast] rw [nsmul_eq_mul] @@ -925,7 +957,7 @@ theorem toField_zsmul (n : Int) (x : FastField F) : toField (n • x) = n • to /-- Natural powers through the `Pow` instance are preserved by `toField`. -/ @[simp] -theorem toField_npow (x : FastField F) (n : Nat) : toField (x ^ n) = toField x ^ n := by +theorem toField_npow (x : FastField F) (n : ℕ) : toField (x ^ n) = toField x ^ n := by change toField (pow x n) = toField x ^ n rw [toField_pow] @@ -997,14 +1029,16 @@ instance (priority := low) instCommRing : CommRing (FastField F) := by /-- A fast 32-bit-word field is non-binary. -/ instance (priority := low) instNonBinaryField : NonBinaryField (FastField F) where char_neq_2 := by - change ((2 : Nat) : FastField F) ≠ 0 + change ((2 : ℕ) : FastField F) ≠ 0 intro h - exact P.two_ne_zero_in_field (by + have htwo : (2 : ZMod P.fieldSize) = 0 := by calc - (2 : ZMod P.fieldSize) = ((2 : Nat) : ZMod P.fieldSize) := by norm_cast - _ = toField ((2 : Nat) : FastField F) := (toField_natCast 2).symm + (2 : ZMod P.fieldSize) = ((2 : ℕ) : ZMod P.fieldSize) := by norm_cast + _ = toField ((2 : ℕ) : FastField F) := (toField_natCast 2).symm _ = toField (0 : FastField F) := congrArg toField h - _ = 0 := toField_zero) + _ = 0 := toField_zero + have hdvd : P.fieldSize ∣ 2 := (ZMod.natCast_eq_zero_iff 2 P.fieldSize).mp htwo + exact (Nat.not_le_of_gt P.two_lt_fieldSize) (Nat.le_of_dvd (by decide) hdvd) end From 4ad9d9364937aaa4f623687a376e73c915f39d2d Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 13:50:55 +0200 Subject: [PATCH 12/31] refactor(fields): state Montgomery inverse by remainder --- CompPoly/Fields/BabyBear/Fast/Prelude.lean | 2 +- CompPoly/Fields/KoalaBear/Fast/Prelude.lean | 2 +- CompPoly/Fields/Montgomery/Basic.lean | 33 +++++-------------- CompPoly/Fields/Montgomery/Native32Field.lean | 8 ++--- 4 files changed, 14 insertions(+), 31 deletions(-) diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Prelude.lean index ded1b56d..f5ffcfff 100644 --- a/CompPoly/Fields/BabyBear/Fast/Prelude.lean +++ b/CompPoly/Fields/BabyBear/Fast/Prelude.lean @@ -72,7 +72,7 @@ instance instMont32Field : Mont32Field BabyBear.Field where two_pow_32_lt_three_mul_fieldSize := two_pow_32_lt_three_mul_fieldSize rModModulus_toNat := rModModulus_toNat r2ModModulus_toNat := r2ModModulus_toNat - negInv_congr := by decide + montgomeryNegInv_mul_fieldSize_mod_two_pow_32 := by decide /-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ abbrev Field : Type := FastField BabyBear.Field diff --git a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean index 4d34ca57..8160a9d7 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean @@ -72,7 +72,7 @@ instance instMont32Field : Mont32Field KoalaBear.Field where two_pow_32_lt_three_mul_fieldSize := two_pow_32_lt_three_mul_fieldSize rModModulus_toNat := rModModulus_toNat r2ModModulus_toNat := r2ModModulus_toNat - negInv_congr := by decide + montgomeryNegInv_mul_fieldSize_mod_two_pow_32 := by decide /-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ abbrev Field : Type := FastField KoalaBear.Field diff --git a/CompPoly/Fields/Montgomery/Basic.lean b/CompPoly/Fields/Montgomery/Basic.lean index 8bb2be76..53205cbd 100644 --- a/CompPoly/Fields/Montgomery/Basic.lean +++ b/CompPoly/Fields/Montgomery/Basic.lean @@ -18,33 +18,16 @@ Word-specific implementations refine these results in sibling modules. namespace Montgomery -/-- The Montgomery divisibility identity: if `negInv * p ≡ R - 1 [MOD R]` (i.e. +/-- The Montgomery divisibility identity: if `(negInv * p) % R = R - 1` (i.e. `negInv = -p⁻¹ mod R`), then `R ∣ x + ((x mod R)·negInv mod R)·p` for every `x`. This is what makes Montgomery reduction integer-valued. -/ theorem dvd_add (R p negInv : ℕ) (hR : 0 < R) - (hnegInv : negInv * p ≡ R - 1 [MOD R]) (x : ℕ) : + (hnegInv : negInv * p % R = R - 1) (x : ℕ) : R ∣ x + ((x % R * negInv) % R) * p := by - rw [← Nat.modEq_zero_iff_dvd] - have hx : x ≡ x % R [MOD R] := (Nat.mod_modEq x R).symm - have hm : - ((x % R * negInv) % R) * p ≡ (x % R) * (R - 1) [MOD R] := by - have hmi : - (x % R * negInv) % R ≡ x % R * negInv [MOD R] := Nat.mod_modEq _ _ - calc - ((x % R * negInv) % R) * p - ≡ (x % R * negInv) * p [MOD R] := hmi.mul_right _ - _ = x % R * (negInv * p) := by ring - _ ≡ x % R * (R - 1) [MOD R] := hnegInv.mul_left _ - calc - x + ((x % R * negInv) % R) * p - ≡ x % R + x % R * (R - 1) [MOD R] := hx.add hm - _ = x % R * R := by - rw [add_comm, ← Nat.mul_succ] - have hsucc : (R - 1).succ = R := by omega - rw [hsucc] - _ ≡ 0 [MOD R] := by - rw [Nat.modEq_zero_iff_dvd] - exact ⟨x % R, by rw [mul_comm]⟩ + rw [Nat.dvd_iff_mod_eq_zero] + rw [Nat.add_mod, Nat.mod_mul_mod (x % R * negInv) p R, Nat.mul_assoc] + rw [← Nat.mul_mod_mod, hnegInv, Nat.add_mod_mod, add_comm, ← Nat.mul_add_one] + rw [show R - 1 + 1 = R by omega, Nat.mul_mod_left] /-- The quotient before the final conditional subtraction in Montgomery reduction. -/ def reduceNatQuotient (R p negInv x : ℕ) : ℕ := @@ -75,7 +58,7 @@ theorem reduceNatQuotient_lt_two_mul (R p negInv x : ℕ) /-- The pre-subtraction quotient represents multiplication by `R⁻¹` in `ZMod p`. -/ theorem reduceNatQuotient_cast (R p negInv : ℕ) [Fact (Nat.Prime p)] (hR : 0 < R) - (hnegInv : negInv * p ≡ R - 1 [MOD R]) (hRne : (R : ZMod p) ≠ 0) (x : ℕ) : + (hnegInv : negInv * p % R = R - 1) (hRne : (R : ZMod p) ≠ 0) (x : ℕ) : (reduceNatQuotient R p negInv x : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by let m := x % R * negInv % R let u := (x + m * p) / R @@ -91,7 +74,7 @@ theorem reduceNatQuotient_cast (R p negInv : ℕ) [Fact (Nat.Prime p)] (hR : 0 < /-- Montgomery reduction represents multiplication by `R⁻¹` in `ZMod p`. -/ theorem reduceNat_cast (R p negInv : ℕ) [Fact (Nat.Prime p)] (hR : 0 < R) - (hnegInv : negInv * p ≡ R - 1 [MOD R]) (hRne : (R : ZMod p) ≠ 0) (x : ℕ) : + (hnegInv : negInv * p % R = R - 1) (hRne : (R : ZMod p) ≠ 0) (x : ℕ) : (reduceNat R p negInv x : ZMod p) = (x : ZMod p) * (R : ZMod p)⁻¹ := by let m := x % R * negInv % R let u := (x + m * p) / R diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index 1f192b6f..32352f8b 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -61,9 +61,8 @@ class Mont32Field (F : Type) where two_pow_32_lt_three_mul_fieldSize : 2 ^ 32 < 3 * fieldSize rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % fieldSize r2ModModulus_toNat : r2ModModulus.toNat = (2 ^ 32) ^ 2 % fieldSize - /-- The Montgomery inverse congruence `negInv * p ≡ 2^32 - 1 [MOD 2^32]`. -/ - negInv_congr : - montgomeryNegInv.toNat * fieldSize ≡ 2 ^ 32 - 1 [MOD 2 ^ 32] + montgomeryNegInv_mul_fieldSize_mod_two_pow_32 : + (montgomeryNegInv.toNat * fieldSize) % 2 ^ 32 = 2 ^ 32 - 1 attribute [instance] Mont32Field.prime @@ -273,7 +272,8 @@ theorem montgomeryReduceBounded_cast (x : UInt64) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)] exact Montgomery.reduceNatQuotient_cast (2 ^ 32) P.fieldSize P.montgomeryNegInv.toNat - (by decide) P.negInv_congr P.two_pow_32_ne_zero_in_field x.toNat + (by decide) P.montgomeryNegInv_mul_fieldSize_mod_two_pow_32 + P.two_pow_32_ne_zero_in_field x.toNat /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] From a2188740fa7cd3788b7c3758604bea2a45016bcc Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 14:04:16 +0200 Subject: [PATCH 13/31] rename modulus consistently --- CompPoly/Fields/BabyBear/Fast/Convert.lean | 2 +- CompPoly/Fields/BabyBear/Fast/Prelude.lean | 20 +- CompPoly/Fields/KoalaBear/Fast/Convert.lean | 2 +- CompPoly/Fields/KoalaBear/Fast/Prelude.lean | 20 +- CompPoly/Fields/Montgomery/Native32Field.lean | 544 +++++++++--------- 5 files changed, 294 insertions(+), 294 deletions(-) diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean index f02556e1..53a2f3c0 100644 --- a/CompPoly/Fields/BabyBear/Fast/Convert.lean +++ b/CompPoly/Fields/BabyBear/Fast/Convert.lean @@ -71,7 +71,7 @@ def toField (x : Field) : BabyBear.Field := Montgomery.Native32.toField x theorem toNat_lt_fieldSize (x : Field) : toNat x < BabyBear.fieldSize := - Montgomery.Native32.toNat_lt_fieldSize x + Montgomery.Native32.toNat_lt_modulus x theorem toField_eq_raw_mul_inv (x : Field) : toField x = (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Prelude.lean index f5ffcfff..e555556e 100644 --- a/CompPoly/Fields/BabyBear/Fast/Prelude.lean +++ b/CompPoly/Fields/BabyBear/Fast/Prelude.lean @@ -27,7 +27,7 @@ namespace Fast open Montgomery.Native32 (Mont32Field FastField) /-- BabyBear modulus as a native word. -/ -def modulus : UInt32 := 0x78000001 +def modulus32 : UInt32 := 0x78000001 /-- BabyBear modulus as a 64-bit word for modular reduction. -/ def modulus64 : UInt64 := 0x78000001 @@ -42,14 +42,14 @@ def r2ModModulus : UInt32 := 0x45DDDDE3 def montgomeryNegInv : UInt32 := 0x77FFFFFF /-- The native `UInt32` modulus agrees with the mathematical BabyBear modulus. -/ -@[simp] theorem modulus_toNat : modulus.toNat = BabyBear.fieldSize := by decide +@[simp] theorem modulus32_toNat : modulus32.toNat = BabyBear.fieldSize := by decide /-- The native `UInt64` modulus agrees with the mathematical BabyBear modulus. -/ @[simp] theorem modulus64_toNat : modulus64.toNat = BabyBear.fieldSize := by decide -theorem two_mul_fieldSize_lt_two_pow_32 : 2 * BabyBear.fieldSize < 2 ^ 32 := by decide +theorem two_mul_modulus_lt_two_pow_32 : 2 * BabyBear.fieldSize < 2 ^ 32 := by decide -theorem two_pow_32_lt_three_mul_fieldSize : 2 ^ 32 < 3 * BabyBear.fieldSize := by decide +theorem two_pow_32_lt_three_mul_modulus : 2 ^ 32 < 3 * BabyBear.fieldSize := by decide theorem rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % BabyBear.fieldSize := by decide @@ -59,20 +59,20 @@ theorem r2ModModulus_toNat : /-- The per-field data realizing BabyBear as a fast 32-bit-word Montgomery field. The five word constants are the only runtime data; every other field is a `decide`-checked fact. -/ instance instMont32Field : Mont32Field BabyBear.Field where - fieldSize := BabyBear.fieldSize + modulus := BabyBear.fieldSize prime := inferInstance - modulus := modulus + modulus32 := modulus32 modulus64 := modulus64 rModModulus := rModModulus r2ModModulus := r2ModModulus montgomeryNegInv := montgomeryNegInv - modulus_toNat := modulus_toNat + modulus32_toNat := modulus32_toNat modulus64_toNat := modulus64_toNat - two_mul_fieldSize_lt_two_pow_32 := two_mul_fieldSize_lt_two_pow_32 - two_pow_32_lt_three_mul_fieldSize := two_pow_32_lt_three_mul_fieldSize + two_mul_modulus_lt_two_pow_32 := two_mul_modulus_lt_two_pow_32 + two_pow_32_lt_three_mul_modulus := two_pow_32_lt_three_mul_modulus rModModulus_toNat := rModModulus_toNat r2ModModulus_toNat := r2ModModulus_toNat - montgomeryNegInv_mul_fieldSize_mod_two_pow_32 := by decide + montgomeryNegInv_mul_modulus_mod_two_pow_32 := by decide /-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ abbrev Field : Type := FastField BabyBear.Field diff --git a/CompPoly/Fields/KoalaBear/Fast/Convert.lean b/CompPoly/Fields/KoalaBear/Fast/Convert.lean index f2409532..e3b997b2 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Convert.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Convert.lean @@ -71,7 +71,7 @@ def toField (x : Field) : KoalaBear.Field := Montgomery.Native32.toField x theorem toNat_lt_fieldSize (x : Field) : toNat x < KoalaBear.fieldSize := - Montgomery.Native32.toNat_lt_fieldSize x + Montgomery.Native32.toNat_lt_modulus x theorem toField_eq_raw_mul_inv (x : Field) : toField x = (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := diff --git a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean index 8160a9d7..64731640 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean @@ -27,7 +27,7 @@ namespace Fast open Montgomery.Native32 (Mont32Field FastField) /-- KoalaBear modulus as a native word. -/ -def modulus : UInt32 := 0x7F000001 +def modulus32 : UInt32 := 0x7F000001 /-- KoalaBear modulus as a 64-bit word for modular reduction. -/ def modulus64 : UInt64 := 0x7F000001 @@ -42,14 +42,14 @@ def r2ModModulus : UInt32 := 0x17F7EFE4 def montgomeryNegInv : UInt32 := 0x7EFFFFFF /-- The native `UInt32` modulus agrees with the mathematical KoalaBear modulus. -/ -@[simp] theorem modulus_toNat : modulus.toNat = KoalaBear.fieldSize := by decide +@[simp] theorem modulus32_toNat : modulus32.toNat = KoalaBear.fieldSize := by decide /-- The native `UInt64` modulus agrees with the mathematical KoalaBear modulus. -/ @[simp] theorem modulus64_toNat : modulus64.toNat = KoalaBear.fieldSize := by decide -theorem two_mul_fieldSize_lt_two_pow_32 : 2 * KoalaBear.fieldSize < 2 ^ 32 := by decide +theorem two_mul_modulus_lt_two_pow_32 : 2 * KoalaBear.fieldSize < 2 ^ 32 := by decide -theorem two_pow_32_lt_three_mul_fieldSize : 2 ^ 32 < 3 * KoalaBear.fieldSize := by decide +theorem two_pow_32_lt_three_mul_modulus : 2 ^ 32 < 3 * KoalaBear.fieldSize := by decide theorem rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % KoalaBear.fieldSize := by decide @@ -59,20 +59,20 @@ theorem r2ModModulus_toNat : /-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. The five word constants are the only runtime data; every other field is a `decide`-checked fact. -/ instance instMont32Field : Mont32Field KoalaBear.Field where - fieldSize := KoalaBear.fieldSize + modulus := KoalaBear.fieldSize prime := inferInstance - modulus := modulus + modulus32 := modulus32 modulus64 := modulus64 rModModulus := rModModulus r2ModModulus := r2ModModulus montgomeryNegInv := montgomeryNegInv - modulus_toNat := modulus_toNat + modulus32_toNat := modulus32_toNat modulus64_toNat := modulus64_toNat - two_mul_fieldSize_lt_two_pow_32 := two_mul_fieldSize_lt_two_pow_32 - two_pow_32_lt_three_mul_fieldSize := two_pow_32_lt_three_mul_fieldSize + two_mul_modulus_lt_two_pow_32 := two_mul_modulus_lt_two_pow_32 + two_pow_32_lt_three_mul_modulus := two_pow_32_lt_three_mul_modulus rModModulus_toNat := rModModulus_toNat r2ModModulus_toNat := r2ModModulus_toNat - montgomeryNegInv_mul_fieldSize_mod_two_pow_32 := by decide + montgomeryNegInv_mul_modulus_mod_two_pow_32 := by decide /-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ abbrev Field : Type := FastField KoalaBear.Field diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index 32352f8b..12916624 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -17,10 +17,10 @@ constants; their definitions, the proofs about them, and the resulting algebraic are otherwise identical. This module captures that common content **once**, parameterized by a `Mont32Field` instance that supplies the per-field data. -* `Mont32Field F` bundles the prime (`fieldSize`), its native-word forms, the Montgomery +* `Mont32Field F` bundles the prime (`modulus`), its native-word forms, the Montgomery constants, and the small `decide`-checkable numeric/`ZMod` facts the proofs consume. Everything except the five word constants is spec-level and erased at codegen. -* `FastField F` is the fast carrier `{ x : UInt32 // x.toNat < fieldSize }`, indexed by the +* `FastField F` is the fast carrier `{ x : UInt32 // x.toNat < modulus }`, indexed by the tag `F` so that the generic `Add`/`Mul`/`Field`/… instances below resolve for each concrete field's `Field := FastField `. At runtime it erases to `UInt32`. * The executable `def`s are `@[inline]`/`@[specialize]`, so once a concrete instance is @@ -37,94 +37,94 @@ namespace Native32 /-- Per-field data for a fast 32-bit-word Montgomery prime field. -The five word constants (`modulus`, `modulus64`, `rModModulus`, `r2ModModulus`, +The five word constants (`modulus32`, `modulus64`, `rModModulus`, `r2ModModulus`, `montgomeryNegInv`) are the only runtime data; the remaining fields are `Prop`s and erased at codegen. -/ class Mont32Field (F : Type) where - /-- The field size / prime `p`. -/ - fieldSize : ℕ - /-- `fieldSize` is prime — needed to reduce into `ZMod fieldSize` as a field. -/ - prime : Fact (Nat.Prime fieldSize) - /-- `fieldSize` as a 32-bit word. -/ - modulus : UInt32 - /-- `fieldSize` as a 64-bit word. -/ + /-- The prime modulus `p`. -/ + modulus : ℕ + /-- `modulus` is prime — needed to reduce into `ZMod modulus` as a field. -/ + prime : Fact (Nat.Prime modulus) + /-- `modulus` as a 32-bit word. -/ + modulus32 : UInt32 + /-- `modulus` as a 64-bit word. -/ modulus64 : UInt64 - /-- `2^32 mod fieldSize`, the Montgomery representation of one. -/ + /-- `2^32 mod modulus`, the Montgomery representation of one. -/ rModModulus : UInt32 - /-- `(2^32)^2 mod fieldSize`, used to enter Montgomery form. -/ + /-- `(2^32)^2 mod modulus`, used to enter Montgomery form. -/ r2ModModulus : UInt32 - /-- `-fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ + /-- `-modulus⁻¹ mod 2^32`, used by Montgomery reduction. -/ montgomeryNegInv : UInt32 - modulus_toNat : modulus.toNat = fieldSize - modulus64_toNat : modulus64.toNat = fieldSize - two_mul_fieldSize_lt_two_pow_32 : 2 * fieldSize < 2 ^ 32 - two_pow_32_lt_three_mul_fieldSize : 2 ^ 32 < 3 * fieldSize - rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % fieldSize - r2ModModulus_toNat : r2ModModulus.toNat = (2 ^ 32) ^ 2 % fieldSize - montgomeryNegInv_mul_fieldSize_mod_two_pow_32 : - (montgomeryNegInv.toNat * fieldSize) % 2 ^ 32 = 2 ^ 32 - 1 + modulus32_toNat : modulus32.toNat = modulus + modulus64_toNat : modulus64.toNat = modulus + two_mul_modulus_lt_two_pow_32 : 2 * modulus < 2 ^ 32 + two_pow_32_lt_three_mul_modulus : 2 ^ 32 < 3 * modulus + rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % modulus + r2ModModulus_toNat : r2ModModulus.toNat = (2 ^ 32) ^ 2 % modulus + montgomeryNegInv_mul_modulus_mod_two_pow_32 : + (montgomeryNegInv.toNat * modulus) % 2 ^ 32 = 2 ^ 32 - 1 attribute [instance] Mont32Field.prime namespace Mont32Field -theorem two_lt_fieldSize {F : Type} [P : Mont32Field F] : 2 < P.fieldSize := by - have h := P.two_pow_32_lt_three_mul_fieldSize +theorem two_lt_modulus {F : Type} [P : Mont32Field F] : 2 < P.modulus := by + have h := P.two_pow_32_lt_three_mul_modulus omega -theorem fieldSize_pos {F : Type} [P : Mont32Field F] : 0 < P.fieldSize := by - exact Nat.zero_lt_of_lt P.two_lt_fieldSize +theorem modulus_pos {F : Type} [P : Mont32Field F] : 0 < P.modulus := by + exact Nat.zero_lt_of_lt P.two_lt_modulus -theorem fieldSize_lt_two_pow_32 {F : Type} [P : Mont32Field F] : - P.fieldSize < 2 ^ 32 := by - have h := P.two_mul_fieldSize_lt_two_pow_32 +theorem modulus_lt_two_pow_32 {F : Type} [P : Mont32Field F] : + P.modulus < 2 ^ 32 := by + have h := P.two_mul_modulus_lt_two_pow_32 omega -theorem fieldSize_mul_fieldSize_lt_two_pow_64 {F : Type} [P : Mont32Field F] : - P.fieldSize * P.fieldSize < 2 ^ 64 := by - nlinarith [P.fieldSize_lt_two_pow_32] +theorem modulus_sq_lt_two_pow_64 {F : Type} [P : Mont32Field F] : + P.modulus ^ 2 < 2 ^ 64 := by + nlinarith [P.modulus_lt_two_pow_32] theorem two_pow_32_ne_zero_in_field {F : Type} [P : Mont32Field F] : - ((2 ^ 32 : ℕ) : ZMod P.fieldSize) ≠ 0 := by - have htwo : (2 : ZMod P.fieldSize) ≠ 0 := by + ((2 ^ 32 : ℕ) : ZMod P.modulus) ≠ 0 := by + have htwo : (2 : ZMod P.modulus) ≠ 0 := by intro h - have hdvd : P.fieldSize ∣ 2 := (ZMod.natCast_eq_zero_iff 2 P.fieldSize).mp h - exact (Nat.not_le_of_gt P.two_lt_fieldSize) (Nat.le_of_dvd (by decide) hdvd) + have hdvd : P.modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 P.modulus).mp h + exact (Nat.not_le_of_gt P.two_lt_modulus) (Nat.le_of_dvd (by decide) hdvd) rw [Nat.cast_pow] exact pow_ne_zero 32 htwo -theorem rModModulus_lt_fieldSize {F : Type} [P : Mont32Field F] : - P.rModModulus.toNat < P.fieldSize := by +theorem rModModulus_lt_modulus {F : Type} [P : Mont32Field F] : + P.rModModulus.toNat < P.modulus := by rw [P.rModModulus_toNat] - exact Nat.mod_lt _ P.fieldSize_pos + exact Nat.mod_lt _ P.modulus_pos -theorem r2ModModulus_lt_fieldSize {F : Type} [P : Mont32Field F] : - P.r2ModModulus.toNat < P.fieldSize := by +theorem r2ModModulus_lt_modulus {F : Type} [P : Mont32Field F] : + P.r2ModModulus.toNat < P.modulus := by rw [P.r2ModModulus_toNat] - exact Nat.mod_lt _ P.fieldSize_pos + exact Nat.mod_lt _ P.modulus_pos theorem rModModulus_cast {F : Type} [P : Mont32Field F] : - (P.rModModulus.toNat : ZMod P.fieldSize) = ((2 ^ 32 : ℕ) : ZMod P.fieldSize) := by + (P.rModModulus.toNat : ZMod P.modulus) = ((2 ^ 32 : ℕ) : ZMod P.modulus) := by rw [P.rModModulus_toNat, ZMod.natCast_mod] theorem r2ModModulus_cast {F : Type} [P : Mont32Field F] : - (P.r2ModModulus.toNat : ZMod P.fieldSize) = ((2 ^ 32 : ℕ) : ZMod P.fieldSize) ^ 2 := by + (P.r2ModModulus.toNat : ZMod P.modulus) = ((2 ^ 32 : ℕ) : ZMod P.modulus) ^ 2 := by rw [P.r2ModModulus_toNat, ZMod.natCast_mod, Nat.cast_pow] end Mont32Field -/-- The fast carrier for the field tagged by `F`: a native word below `fieldSize`, +/-- The fast carrier for the field tagged by `F`: a native word below `modulus`, interpreted as a Montgomery residue. At runtime this erases to `UInt32`. -/ def FastField (F : Type) [Mont32Field F] : Type := - { x : UInt32 // x.toNat < Mont32Field.fieldSize F } + { x : UInt32 // x.toNat < Mont32Field.modulus F } instance (F : Type) [Mont32Field F] : DecidableEq (FastField F) := - inferInstanceAs (DecidableEq { x : UInt32 // x.toNat < Mont32Field.fieldSize F }) + inferInstanceAs (DecidableEq { x : UInt32 // x.toNat < Mont32Field.modulus F }) section variable {F : Type} [P : Mont32Field F] -instance : NeZero P.fieldSize := ⟨P.fieldSize_pos.ne'⟩ +instance : NeZero P.modulus := ⟨P.modulus_pos.ne'⟩ /-- The raw Montgomery word backing a fast element. -/ @[inline] @@ -135,80 +135,80 @@ def raw (x : FastField F) : UInt32 := x.val /-- Reduce a native word known to be below twice the prime. -/ @[inline] def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := - if x < P.modulus then x else x - P.modulus + if x < P.modulus32 then x else x - P.modulus32 theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) - (h : x.toNat < 2 * P.fieldSize) : - (reduceUInt32Lt2ModulusRaw (F := F) x).toNat < P.fieldSize := by + (h : x.toNat < 2 * P.modulus) : + (reduceUInt32Lt2ModulusRaw (F := F) x).toNat < P.modulus := by unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < P.modulus + by_cases hx : x < P.modulus32 · rw [if_pos hx] - rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx exact hx · rw [if_neg hx] - have hmod_le_x : P.modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, P.modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + have hmod_le_x : P.modulus32 ≤ x := by + rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] + rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx exact Nat.le_of_not_gt hx - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus_toNat] + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus32_toNat] omega /-- Reduce a native word known to be below twice the prime. -/ @[inline] -def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * P.fieldSize) : +def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * P.modulus) : FastField F := ⟨reduceUInt32Lt2ModulusRaw (F := F) x, reduceUInt32Lt2ModulusRaw_lt x h⟩ theorem reduceUInt32Lt2Modulus_cast (x : UInt32) - (h : x.toNat < 2 * P.fieldSize) : - ((reduceUInt32Lt2Modulus (F := F) x h).val.toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) := by - change ((reduceUInt32Lt2ModulusRaw (F := F) x).toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) + (h : x.toNat < 2 * P.modulus) : + ((reduceUInt32Lt2Modulus (F := F) x h).val.toNat : ZMod P.modulus) = + (x.toNat : ZMod P.modulus) := by + change ((reduceUInt32Lt2ModulusRaw (F := F) x).toNat : ZMod P.modulus) = + (x.toNat : ZMod P.modulus) unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < P.modulus + by_cases hx : x < P.modulus32 · rw [if_pos hx] - · have hmod_le_x : P.modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, P.modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + · have hmod_le_x : P.modulus32 ≤ x := by + rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] + rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx exact Nat.le_of_not_gt hx rw [if_neg hx] - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus_toNat] + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus32_toNat] rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, P.modulus_toNat] at hmod_le_x + rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] at hmod_le_x exact hmod_le_x)] simp /-- Reduce a native word below `2^32` modulo the prime. -/ @[inline] def reduceUInt32 (x : UInt32) : FastField F := - if hx : x < P.modulus then + if hx : x < P.modulus32 then ⟨x, by - rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx exact hx⟩ else - let y := x - P.modulus - if hy : y < P.modulus then + let y := x - P.modulus32 + if hy : y < P.modulus32 then ⟨y, by - rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hy + rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hy exact hy⟩ else - ⟨y - P.modulus, by - have hmod_le_x : P.modulus ≤ x := by - rw [UInt32.le_iff_toNat_le, P.modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hx + ⟨y - P.modulus32, by + have hmod_le_x : P.modulus32 ≤ x := by + rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] + rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx exact Nat.le_of_not_gt hx - have hy_eq : y.toNat = x.toNat - P.fieldSize := by - change (x - P.modulus).toNat = x.toNat - P.fieldSize - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus_toNat] - have hmod_le_y : P.modulus ≤ y := by - rw [UInt32.le_iff_toNat_le, P.modulus_toNat] - rw [UInt32.lt_iff_toNat_lt, P.modulus_toNat] at hy + have hy_eq : y.toNat = x.toNat - P.modulus := by + change (x - P.modulus32).toNat = x.toNat - P.modulus + rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus32_toNat] + have hmod_le_y : P.modulus32 ≤ y := by + rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] + rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hy exact Nat.le_of_not_gt hy - rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, P.modulus_toNat, hy_eq] + rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, P.modulus32_toNat, hy_eq] have hx_lt := UInt32.toNat_lt_size x change x.toNat < 2 ^ 32 at hx_lt - have hthree := P.two_pow_32_lt_three_mul_fieldSize + have hthree := P.two_pow_32_lt_three_mul_modulus omega⟩ /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @@ -218,61 +218,61 @@ def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := (Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x) theorem montgomeryReduceBoundedRaw_lt (x : UInt64) - (h : x.toNat < P.fieldSize * 2 ^ 32) : - (montgomeryReduceBoundedRaw (F := F) x).toNat < P.fieldSize := by + (h : x.toNat < P.modulus * 2 ^ 32) : + (montgomeryReduceBoundedRaw (F := F) x).toNat < P.modulus := by have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by rw [P.modulus64_toNat] - have hp := P.two_mul_fieldSize_lt_two_pow_32 + have hp := P.two_mul_modulus_lt_two_pow_32 omega unfold montgomeryReduceBoundedRaw exact reduceUInt32Lt2ModulusRaw_lt _ (by simpa only [P.modulus64_toNat] using Montgomery.Native32.reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 - (by simpa only [P.modulus64_toNat] using P.fieldSize_pos) + (by simpa only [P.modulus64_toNat] using P.modulus_pos) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)) /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBounded (x : UInt64) - (h : x.toNat < P.fieldSize * 2 ^ 32) : FastField F := + (h : x.toNat < P.modulus * 2 ^ 32) : FastField F := ⟨montgomeryReduceBoundedRaw (F := F) x, montgomeryReduceBoundedRaw_lt x h⟩ theorem montgomeryReduceBounded_cast (x : UInt64) - (h : x.toNat < P.fieldSize * 2 ^ 32) : - ((montgomeryReduceBounded (F := F) x h).val.toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ := by + (h : x.toNat < P.modulus * 2 ^ 32) : + ((montgomeryReduceBounded (F := F) x h).val.toNat : ZMod P.modulus) = + (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ := by have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by rw [P.modulus64_toNat] - have hp := P.two_mul_fieldSize_lt_two_pow_32 + have hp := P.two_mul_modulus_lt_two_pow_32 omega - change ((montgomeryReduceBoundedRaw (F := F) x).toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ + change ((montgomeryReduceBoundedRaw (F := F) x).toNat : ZMod P.modulus) = + (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ unfold montgomeryReduceBoundedRaw let u := Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x - change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ + change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.modulus) = + (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ have hred := reduceUInt32Lt2Modulus_cast (F := F) u (by simpa only [P.modulus64_toNat] using Montgomery.Native32.reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 - (by simpa only [P.modulus64_toNat] using P.fieldSize_pos) + (by simpa only [P.modulus64_toNat] using P.modulus_pos) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)) - change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.fieldSize) = - (u.toNat : ZMod P.fieldSize) at hred + change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.modulus) = + (u.toNat : ZMod P.modulus) at hred rw [hred] - change (u.toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ - rw [show u.toNat = reduceNatQuotient (2 ^ 32) P.fieldSize P.montgomeryNegInv.toNat x.toNat by + change (u.toNat : ZMod P.modulus) = + (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ + rw [show u.toNat = reduceNatQuotient (2 ^ 32) P.modulus P.montgomeryNegInv.toNat x.toNat by simpa only [u, P.modulus64_toNat] using Montgomery.Native32.reduceQuotient_toNat P.montgomeryNegInv P.modulus64 - (by simpa only [P.modulus64_toNat] using P.fieldSize_pos) + (by simpa only [P.modulus64_toNat] using P.modulus_pos) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)] - exact Montgomery.reduceNatQuotient_cast (2 ^ 32) P.fieldSize P.montgomeryNegInv.toNat - (by decide) P.montgomeryNegInv_mul_fieldSize_mod_two_pow_32 + exact Montgomery.reduceNatQuotient_cast (2 ^ 32) P.modulus P.montgomeryNegInv.toNat + (by decide) P.montgomeryNegInv_mul_modulus_mod_two_pow_32 P.two_pow_32_ne_zero_in_field x.toNat /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @@ -285,17 +285,17 @@ def montgomeryReduce (x : UInt64) : FastField F := /-- Build a fast element from a canonical natural representative. -/ @[inline] -def ofCanonicalNat (n : ℕ) (_h : n < P.fieldSize) : FastField F := +def ofCanonicalNat (n : ℕ) (_h : n < P.modulus) : FastField F := montgomeryReduceBounded (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by apply Nat.mod_eq_of_lt - exact Nat.lt_trans _h (Nat.lt_trans P.fieldSize_lt_two_pow_32 (by decide)) + exact Nat.lt_trans _h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) rw [hnmod] have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] + nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - nlinarith [P.r2ModModulus_lt_fieldSize]) + nlinarith [P.r2ModModulus_lt_modulus]) /-- Reduce a `UInt64` modulo the prime and return a Montgomery fast element. -/ @[inline] @@ -303,27 +303,27 @@ def reduceUInt64 (x : UInt64) : FastField F := let y := x % P.modulus64 montgomeryReduceBounded (y * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : (x % P.modulus64).toNat < P.fieldSize := by + have hy_lt : (x % P.modulus64).toNat < P.modulus := by rw [UInt64.toNat_mod, P.modulus64_toNat] - exact Nat.mod_lt _ P.fieldSize_pos + exact Nat.mod_lt _ P.modulus_pos have hprod : (x % P.modulus64).toNat * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] + nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - nlinarith [P.r2ModModulus_lt_fieldSize]) + nlinarith [P.r2ModModulus_lt_modulus]) /-- The zero fast element. -/ def zero : FastField F := ⟨0, by have h0 : (0 : UInt32).toNat = 0 := by decide - have hp := P.fieldSize_pos + have hp := P.modulus_pos omega⟩ /-- The one fast element. -/ -def one : FastField F := ⟨P.rModModulus, P.rModModulus_lt_fieldSize⟩ +def one : FastField F := ⟨P.rModModulus, P.rModModulus_lt_modulus⟩ /-- Convert a natural number into fast Montgomery representation. -/ @[inline] def ofNat (n : ℕ) : FastField F := - ofCanonicalNat (n % P.fieldSize) (Nat.mod_lt _ P.fieldSize_pos) + ofCanonicalNat (n % P.modulus) (Nat.mod_lt _ P.modulus_pos) /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] @@ -332,20 +332,20 @@ def ofUInt32 (x : UInt32) : FastField F := /-- Convert from the canonical `ZMod` field into fast Montgomery form. -/ @[inline] -def ofField (x : ZMod P.fieldSize) : FastField F := +def ofField (x : ZMod P.modulus) : FastField F := ofCanonicalNat x.val (ZMod.val_lt x) /-- Convert an integer into fast Montgomery representation. -/ @[inline] def ofInt (n : Int) : FastField F := - ofField (n : ZMod P.fieldSize) + ofField (n : ZMod P.modulus) /-- Convert a fast element to its canonical native-word representative. -/ @[inline] def toCanonicalUInt32 (x : FastField F) : UInt32 := raw (montgomeryReduceBounded x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.fieldSize_pos])) + nlinarith [x.property, P.modulus_pos])) /-- Convert a fast element to its canonical natural representative. -/ @[inline] @@ -354,147 +354,147 @@ def toNat (x : FastField F) : ℕ := /-- Convert a fast element to the canonical `ZMod` field. -/ @[inline] -def toField (x : FastField F) : ZMod P.fieldSize := - (toNat x : ZMod P.fieldSize) +def toField (x : FastField F) : ZMod P.modulus := + (toNat x : ZMod P.modulus) -theorem toNat_lt_fieldSize (x : FastField F) : toNat x < P.fieldSize := by +theorem toNat_lt_modulus (x : FastField F) : toNat x < P.modulus := by unfold toNat toCanonicalUInt32 raw - change (montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat < P.fieldSize + change (montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat < P.modulus exact montgomeryReduceBoundedRaw_lt x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.fieldSize_pos]) + nlinarith [x.property, P.modulus_pos]) theorem toField_eq_raw_mul_inv (x : FastField F) : toField x = - (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ := by + (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ := by unfold toField toNat toCanonicalUInt32 raw have hred := montgomeryReduceBounded_cast x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.fieldSize_pos]) - change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.fieldSize) = - (x.val.toUInt64.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.fieldSize) = - (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ + nlinarith [x.property, P.modulus_pos]) + change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.modulus) = + (x.val.toUInt64.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ at hred + change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.modulus) = + (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ rw [hred] rw [UInt32.toNat_toUInt64] theorem raw_cast_eq_toField_mul (x : FastField F) : - (x.val.toNat : ZMod P.fieldSize) = - toField x * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) := by + (x.val.toNat : ZMod P.modulus) = + toField x * ((2 ^ 32 : ℕ) : ZMod P.modulus) := by rw [toField_eq_raw_mul_inv] rw [mul_assoc] rw [inv_mul_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] -theorem nat_eq_of_field_eq {a b : ℕ} (ha : a < P.fieldSize) - (hb : b < P.fieldSize) (h : (a : ZMod P.fieldSize) = (b : ZMod P.fieldSize)) : +theorem nat_eq_of_field_eq {a b : ℕ} (ha : a < P.modulus) + (hb : b < P.modulus) (h : (a : ZMod P.modulus) = (b : ZMod P.modulus)) : a = b := Montgomery.natCast_inj_of_lt h ha hb -theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < P.fieldSize) : - ((ofCanonicalNat (F := F) n h).val.toNat : ZMod P.fieldSize) = - (n : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) := by +theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < P.modulus) : + ((ofCanonicalNat (F := F) n h).val.toNat : ZMod P.modulus) = + (n : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus) := by unfold ofCanonicalNat have hred := montgomeryReduceBounded_cast (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans P.fieldSize_lt_two_pow_32 (by decide)) + exact Nat.lt_trans h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) rw [hnmod] have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] + nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - nlinarith [P.r2ModModulus_lt_fieldSize]) + nlinarith [P.r2ModModulus_lt_modulus]) change ((montgomeryReduceBoundedRaw (F := F) - (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.fieldSize) = - ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ at hred + (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.modulus) = + ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ at hred change ((montgomeryReduceBoundedRaw (F := F) - (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.fieldSize) = - (n : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) + (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.modulus) = + (n : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus) rw [hred] simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans P.fieldSize_lt_two_pow_32 (by decide)) + exact Nat.lt_trans h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) rw [hnmod] have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] + nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] rw [Nat.cast_mul, P.r2ModModulus_cast] rw [pow_two] - rw [mul_assoc (n : ZMod P.fieldSize) (((2 ^ 32 : ℕ) : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)) (((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹)] - rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod P.fieldSize) ((2 ^ 32 : ℕ) : ZMod P.fieldSize) - (((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹)] + rw [mul_assoc (n : ZMod P.modulus) (((2 ^ 32 : ℕ) : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)) (((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹)] + rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod P.modulus) ((2 ^ 32 : ℕ) : ZMod P.modulus) + (((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹)] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] -theorem toField_ofCanonicalNat_aux (n : ℕ) (h : n < P.fieldSize) : - toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.fieldSize) := by +theorem toField_ofCanonicalNat_aux (n : ℕ) (h : n < P.modulus) : + toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.modulus) := by rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] rw [mul_assoc] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] theorem reduceUInt64_raw_cast (x : UInt64) : - ((reduceUInt64 (F := F) x).val.toNat : ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) := by + ((reduceUInt64 (F := F) x).val.toNat : ZMod P.modulus) = + (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus) := by unfold reduceUInt64 let y := x % P.modulus64 have hred := montgomeryReduceBounded_cast (y * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < P.fieldSize := by + have hy_lt : y.toNat < P.modulus := by rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] - exact Nat.mod_lt _ P.fieldSize_pos + exact Nat.mod_lt _ P.modulus_pos have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] + nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - nlinarith [P.r2ModModulus_lt_fieldSize]) + nlinarith [P.r2ModModulus_lt_modulus]) change ((montgomeryReduceBoundedRaw (F := F) (y * P.r2ModModulus.toUInt64)).toNat : - ZMod P.fieldSize) = - ((y * P.r2ModModulus.toUInt64).toNat : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ at hred + ZMod P.modulus) = + ((y * P.r2ModModulus.toUInt64).toNat : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ at hred change ((montgomeryReduceBoundedRaw (F := F) (y * P.r2ModModulus.toUInt64)).toNat : - ZMod P.fieldSize) = - (x.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize) + ZMod P.modulus) = + (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus) rw [hred] simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < P.fieldSize := by + have hy_lt : y.toNat < P.modulus := by rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] - exact Nat.mod_lt _ P.fieldSize_pos + exact Nat.mod_lt _ P.modulus_pos have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, P.r2ModModulus_lt_fieldSize, P.fieldSize_mul_fieldSize_lt_two_pow_64] + nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - have hy_cast : (y.toNat : ZMod P.fieldSize) = (x.toNat : ZMod P.fieldSize) := by + have hy_cast : (y.toNat : ZMod P.modulus) = (x.toNat : ZMod P.modulus) := by rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] rw [ZMod.natCast_eq_natCast_iff] exact Nat.mod_modEq _ _ rw [Nat.cast_mul, P.r2ModModulus_cast, hy_cast] rw [pow_two] - rw [mul_assoc (x.toNat : ZMod P.fieldSize) (((2 ^ 32 : ℕ) : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)) (((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹)] - rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod P.fieldSize) ((2 ^ 32 : ℕ) : ZMod P.fieldSize) - (((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹)] + rw [mul_assoc (x.toNat : ZMod P.modulus) (((2 ^ 32 : ℕ) : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)) (((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹)] + rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod P.modulus) ((2 ^ 32 : ℕ) : ZMod P.modulus) + (((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹)] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] @[simp] -theorem toNat_ofCanonicalNat (n : ℕ) (h : n < P.fieldSize) : +theorem toNat_ofCanonicalNat (n : ℕ) (h : n < P.modulus) : toNat (ofCanonicalNat (F := F) n h) = n := - nat_eq_of_field_eq (toNat_lt_fieldSize _) h (toField_ofCanonicalNat_aux n h) + nat_eq_of_field_eq (toNat_lt_modulus _) h (toField_ofCanonicalNat_aux n h) @[simp] -theorem toField_ofCanonicalNat (n : ℕ) (h : n < P.fieldSize) : - toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.fieldSize) := +theorem toField_ofCanonicalNat (n : ℕ) (h : n < P.modulus) : + toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.modulus) := toField_ofCanonicalNat_aux n h @[simp] theorem toNat_reduceUInt64 (x : UInt64) : - toNat (reduceUInt64 (F := F) x) = x.toNat % P.fieldSize := by - apply nat_eq_of_field_eq (toNat_lt_fieldSize _) (Nat.mod_lt _ P.fieldSize_pos) - change toField (reduceUInt64 (F := F) x) = ((x.toNat % P.fieldSize : ℕ) : ZMod P.fieldSize) + toNat (reduceUInt64 (F := F) x) = x.toNat % P.modulus := by + apply nat_eq_of_field_eq (toNat_lt_modulus _) (Nat.mod_lt _ P.modulus_pos) + change toField (reduceUInt64 (F := F) x) = ((x.toNat % P.modulus : ℕ) : ZMod P.modulus) rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] rw [mul_assoc] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] @@ -504,7 +504,7 @@ theorem toNat_reduceUInt64 (x : UInt64) : @[simp] theorem toField_reduceUInt64 (x : UInt64) : - toField (reduceUInt64 (F := F) x) = (x.toNat : ZMod P.fieldSize) := by + toField (reduceUInt64 (F := F) x) = (x.toNat : ZMod P.modulus) := by rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] rw [mul_assoc] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] @@ -526,18 +526,18 @@ def neg (x : FastField F) : FastField F := if hx : x.val = 0 then zero else - ⟨P.modulus - x.val, by - have hle : x.val ≤ P.modulus := by - rw [UInt32.le_iff_toNat_le, P.modulus_toNat] + ⟨P.modulus32 - x.val, by + have hle : x.val ≤ P.modulus32 := by + rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] exact Nat.le_of_lt x.property - rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus_toNat] + rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus32_toNat] have hxpos : 0 < x.val.toNat := by apply Nat.pos_of_ne_zero intro hzero apply hx apply UInt32.toNat_inj.mp simpa using hzero - have hp := P.fieldSize_pos + have hp := P.modulus_pos omega⟩ /-- Fast modular subtraction in Montgomery form. -/ @@ -548,13 +548,13 @@ def sub (x y : FastField F) : FastField F := rw [UInt32.toNat_sub_of_le _ _ hyx] have := x.property; omega⟩ else - ⟨x.val + P.modulus - y.val, by - have hsum_lt : x.val.toNat + P.fieldSize < 2 ^ 32 := by - have htwo := P.two_mul_fieldSize_lt_two_pow_32 + ⟨x.val + P.modulus32 - y.val, by + have hsum_lt : x.val.toNat + P.modulus < 2 ^ 32 := by + have htwo := P.two_mul_modulus_lt_two_pow_32 have := x.property; omega - have hsum_eq : (x.val + P.modulus).toNat = x.val.toNat + P.fieldSize := by - rw [UInt32.toNat_add, P.modulus_toNat, Nat.mod_eq_of_lt hsum_lt] - have hyle : y.val ≤ x.val + P.modulus := by + have hsum_eq : (x.val + P.modulus32).toNat = x.val.toNat + P.modulus := by + rw [UInt32.toNat_add, P.modulus32_toNat, Nat.mod_eq_of_lt hsum_lt] + have hyle : y.val ≤ x.val + P.modulus32 := by rw [UInt32.le_iff_toNat_le, hsum_eq] have := y.property; omega rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] @@ -571,9 +571,9 @@ def mul (x y : FastField F) : FastField F := montgomeryReduceBounded (x.val.toUInt64 * y.val.toUInt64) (by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two_pow_64] + nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, P.fieldSize_lt_two_pow_32, P.fieldSize_pos]) + nlinarith [x.property, y.property, P.modulus_lt_two_pow_32, P.modulus_pos]) /-- Fast squaring. -/ @[inline] @@ -586,7 +586,7 @@ def pow (x : FastField F) (n : ℕ) : FastField F := @npowBinRec (FastField F) ⟨one⟩ ⟨mul⟩ n x /-- Fermat exponent used for inversion in the prime field. -/ -def invExponent : ℕ := P.fieldSize - 2 +def invExponent : ℕ := P.modulus - 2 /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`), by binary exponentiation (`pow`). -/ @@ -645,10 +645,10 @@ instance instPowInt : Pow (FastField F) Int where | Int.negSucc k => pow (inv x) (k + 1) instance instNNRatCast : NNRatCast (FastField F) where - nnratCast q := ofField (q : ZMod P.fieldSize) + nnratCast q := ofField (q : ZMod P.modulus) instance instRatCast : RatCast (FastField F) where - ratCast q := ofField (q : ZMod P.fieldSize) + ratCast q := ofField (q : ZMod P.modulus) instance instNNRatSMul : SMul ℚ≥0 (FastField F) where smul q x := ofField (q • toField x) @@ -656,23 +656,23 @@ instance instNNRatSMul : SMul ℚ≥0 (FastField F) where instance instRatSMul : SMul ℚ (FastField F) where smul q x := ofField (q • toField x) -/-- Fermat-style inversion in `ZMod fieldSize`. -/ -theorem inv_eq_pow_field (a : ZMod P.fieldSize) (ha : a ≠ 0) : - a⁻¹ = a ^ (P.fieldSize - 2) := by - have hcard : Fintype.card (ZMod P.fieldSize) = P.fieldSize := ZMod.card P.fieldSize - have h1 : a ^ (P.fieldSize - 1) = 1 := by +/-- Fermat-style inversion in `ZMod modulus`. -/ +theorem inv_eq_pow_field (a : ZMod P.modulus) (ha : a ≠ 0) : + a⁻¹ = a ^ (P.modulus - 2) := by + have hcard : Fintype.card (ZMod P.modulus) = P.modulus := ZMod.card P.modulus + have h1 : a ^ (P.modulus - 1) = 1 := by have h := FiniteField.pow_card_sub_one_eq_one a ha rw [hcard] at h; exact h - have hmul : a * a ^ (P.fieldSize - 2) = 1 := by - rw [← pow_succ']; show a ^ (P.fieldSize - 2 + 1) = 1 - have : P.fieldSize - 2 + 1 = P.fieldSize - 1 := by - have := P.two_lt_fieldSize; omega + have hmul : a * a ^ (P.modulus - 2) = 1 := by + rw [← pow_succ']; show a ^ (P.modulus - 2 + 1) = 1 + have : P.modulus - 2 + 1 = P.modulus - 1 := by + have := P.two_lt_modulus; omega rw [this]; exact h1 exact (eq_inv_of_mul_eq_one_left (by rwa [mul_comm])).symm /-- Converting from the canonical field to fast form and back is the identity. -/ @[simp] -theorem toField_ofField (x : ZMod P.fieldSize) : toField (ofField (F := F) x) = x := by +theorem toField_ofField (x : ZMod P.modulus) : toField (ofField (F := F) x) = x := by unfold ofField rw [toField_ofCanonicalNat] exact ZMod.natCast_zmod_val x @@ -697,15 +697,15 @@ theorem toField_injective : Function.Injective (toField (F := F)) := @[simp] theorem toField_zero : toField (0 : FastField F) = 0 := by rw [toField_eq_raw_mul_inv] - change ((0 : ℕ) : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = 0 + change ((0 : ℕ) : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = 0 rw [Nat.cast_zero, zero_mul] /-- `toField` maps fast one to canonical one. -/ @[simp] theorem toField_one : toField (1 : FastField F) = 1 := by rw [toField_eq_raw_mul_inv] - change (P.rModModulus.toNat : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = 1 + change (P.rModModulus.toNat : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = 1 rw [P.rModModulus_cast] exact mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field @@ -718,16 +718,16 @@ theorem toField_add (x y : FastField F) : toField (x + y) = toField x + toField rw [UInt32.toNat_add] exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by have hx := x.property; have hy := y.property; omega)) - change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.fieldSize) = - ((x.val + y.val).toNat : ZMod P.fieldSize) at hred - change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = - (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ + - (y.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ + change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.modulus) = + ((x.val + y.val).toNat : ZMod P.modulus) at hred + change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = + (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ + + (y.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ rw [hred] rw [UInt32.toNat_add] have hsum_lt : x.val.toNat + y.val.toNat < 2 ^ 32 := by - nlinarith [x.property, y.property, P.two_mul_fieldSize_lt_two_pow_32] + nlinarith [x.property, y.property, P.two_mul_modulus_lt_two_pow_32] rw [Nat.mod_eq_of_lt hsum_lt] rw [Nat.cast_add] ring @@ -742,32 +742,32 @@ theorem toField_sub (x y : FastField F) : toField (x - y) = toField x - toField unfold sub rw [dif_pos hyx] rw [hsubval] - change (((x.val - y.val).toNat : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) = - (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ - - (y.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ + change (((x.val - y.val).toNat : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) = + (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ - + (y.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ rw [UInt32.toNat_sub_of_le _ _ hyx] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le] at hyx exact hyx)] ring - · have hsum_lt : x.val.toNat + P.fieldSize < 2 ^ 32 := by - have htwo := P.two_mul_fieldSize_lt_two_pow_32 + · have hsum_lt : x.val.toNat + P.modulus < 2 ^ 32 := by + have htwo := P.two_mul_modulus_lt_two_pow_32 have := x.property; omega - have hsum_eq : (x.val + P.modulus).toNat = x.val.toNat + P.fieldSize := by - rw [UInt32.toNat_add, P.modulus_toNat, Nat.mod_eq_of_lt hsum_lt] - have hyle : y.val ≤ x.val + P.modulus := by + have hsum_eq : (x.val + P.modulus32).toNat = x.val.toNat + P.modulus := by + rw [UInt32.toNat_add, P.modulus32_toNat, Nat.mod_eq_of_lt hsum_lt] + have hyle : y.val ≤ x.val + P.modulus32 := by rw [UInt32.le_iff_toNat_le, hsum_eq] have := y.property; omega - have hsubval : (x - y : FastField F).val = x.val + P.modulus - y.val := by - change (sub x y).val = x.val + P.modulus - y.val + have hsubval : (x - y : FastField F).val = x.val + P.modulus32 - y.val := by + change (sub x y).val = x.val + P.modulus32 - y.val unfold sub rw [dif_neg hyx] rw [hsubval] - change (((x.val + P.modulus - y.val).toNat : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) = - (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ - - (y.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ + change (((x.val + P.modulus32 - y.val).toNat : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) = + (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ - + (y.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le, hsum_eq] at hyle @@ -785,29 +785,29 @@ theorem toField_neg (x : FastField F) : toField (-x) = -toField x := by unfold neg rw [dif_pos hx] rw [hnegval] - change ((zero : FastField F).val.toNat : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = - -((x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) + change ((zero : FastField F).val.toNat : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = + -((x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) have hxNat : x.val.toNat = 0 := by simpa using congrArg UInt32.toNat hx rw [hxNat] - change ((0 : ℕ) : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = - -(((0 : ℕ) : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) + change ((0 : ℕ) : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = + -(((0 : ℕ) : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) simp - · have hle : x.val ≤ P.modulus := by - rw [UInt32.le_iff_toNat_le, P.modulus_toNat] + · have hle : x.val ≤ P.modulus32 := by + rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] exact Nat.le_of_lt x.property - have hnegval : (-x : FastField F).val = P.modulus - x.val := by - change (neg x).val = P.modulus - x.val + have hnegval : (-x : FastField F).val = P.modulus32 - x.val := by + change (neg x).val = P.modulus32 - x.val unfold neg rw [dif_neg hx] rw [hnegval] - change (((P.modulus - x.val).toNat : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) = - -((x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) - rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus_toNat] + change (((P.modulus32 - x.val).toNat : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) = + -((x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) + rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus32_toNat] rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, P.modulus_toNat] at hle + rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] at hle exact hle)] rw [ZMod.natCast_self] ring @@ -820,27 +820,27 @@ theorem toField_mul (x y : FastField F) : toField (x * y) = toField x * toField have hred := montgomeryReduceBounded_cast (F := F) (x.val.toUInt64 * y.val.toUInt64) (by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two_pow_64] + nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, P.fieldSize_lt_two_pow_32, P.fieldSize_pos]) + nlinarith [x.property, y.property, P.modulus_lt_two_pow_32, P.modulus_pos]) change ((montgomeryReduceBoundedRaw (F := F) (x.val.toUInt64 * y.val.toUInt64)).toNat : - ZMod P.fieldSize) = - ((x.val.toUInt64 * y.val.toUInt64).toNat : ZMod P.fieldSize) * - ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ at hred + ZMod P.modulus) = + ((x.val.toUInt64 * y.val.toUInt64).toNat : ZMod P.modulus) * + ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ at hred change ((montgomeryReduceBoundedRaw (F := F) (x.val.toUInt64 * y.val.toUInt64)).toNat : - ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ = - (x.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹ * - ((y.val.toNat : ZMod P.fieldSize) * ((2 ^ 32 : ℕ) : ZMod P.fieldSize)⁻¹) + ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = + (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ * + ((y.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) rw [hred] simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, P.fieldSize_mul_fieldSize_lt_two_pow_64] + nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] rw [Nat.cast_mul] ring /-- Ring equivalence between the fast Montgomery representation and the canonical field. -/ -def ringEquiv : FastField F ≃+* ZMod P.fieldSize where +def ringEquiv : FastField F ≃+* ZMod P.modulus where toFun := toField invFun := ofField left_inv := ofField_toField @@ -852,7 +852,7 @@ def ringEquiv : FastField F ≃+* ZMod P.fieldSize where theorem ringEquiv_apply (x : FastField F) : ringEquiv x = toField x := rfl @[simp] -theorem ringEquiv_symm_apply (x : ZMod P.fieldSize) : +theorem ringEquiv_symm_apply (x : ZMod P.modulus) : (ringEquiv (F := F)).symm x = ofField x := rfl private theorem mul_assoc_field (x y z : FastField F) : (x * y) * z = x * (y * z) := by @@ -895,7 +895,7 @@ private theorem toField_inv_raw (x : FastField F) : toField (inv x) = (toField x rw [toField_inv_pow] by_cases hx : toField x = 0 · rw [hx, inv_zero] - exact zero_pow (by unfold invExponent; have := P.two_lt_fieldSize; omega) + exact zero_pow (by unfold invExponent; have := P.two_lt_modulus; omega) · simpa [invExponent] using (inv_eq_pow_field (toField x) hx).symm /-- Fast inversion agrees with inversion in the canonical field. -/ @@ -918,7 +918,7 @@ private theorem toField_div_mul_inv (x y : FastField F) : @[simp] theorem toField_div (x y : FastField F) : toField (x / y) = toField x / toField y := by change toField (div x y) = toField x / toField y - have h : ∀ a b c : ZMod P.fieldSize, c = b⁻¹ → a * c = a / b := by + have h : ∀ a b c : ZMod P.modulus, c = b⁻¹ → a * c = a / b := by intro a b c hc rw [hc] rfl @@ -927,8 +927,8 @@ theorem toField_div (x y : FastField F) : toField (x / y) = toField x / toField /-- Natural casts into fast form agree with natural casts into the canonical field. -/ @[simp] -theorem toField_natCast (n : ℕ) : toField (n : FastField F) = (n : ZMod P.fieldSize) := by - change toField (ofNat n) = (n : ZMod P.fieldSize) +theorem toField_natCast (n : ℕ) : toField (n : FastField F) = (n : ZMod P.modulus) := by + change toField (ofNat n) = (n : ZMod P.modulus) unfold ofNat rw [toField_ofCanonicalNat] rw [ZMod.natCast_eq_natCast_iff] @@ -936,8 +936,8 @@ theorem toField_natCast (n : ℕ) : toField (n : FastField F) = (n : ZMod P.fiel /-- Integer casts into fast form agree with integer casts into the canonical field. -/ @[simp] -theorem toField_intCast (n : Int) : toField (n : FastField F) = (n : ZMod P.fieldSize) := by - change toField (ofInt n) = (n : ZMod P.fieldSize) +theorem toField_intCast (n : Int) : toField (n : FastField F) = (n : ZMod P.modulus) := by + change toField (ofInt n) = (n : ZMod P.modulus) unfold ofInt rw [toField_ofField] @@ -978,14 +978,14 @@ theorem toField_zpow (x : FastField F) (n : Int) : toField (x ^ n) = toField x ^ /-- Nonnegative rational casts into fast form agree with canonical-field casts. -/ @[simp] -theorem toField_nnratCast (q : ℚ≥0) : toField (q : FastField F) = (q : ZMod P.fieldSize) := by - change toField (ofField (q : ZMod P.fieldSize)) = (q : ZMod P.fieldSize) +theorem toField_nnratCast (q : ℚ≥0) : toField (q : FastField F) = (q : ZMod P.modulus) := by + change toField (ofField (q : ZMod P.modulus)) = (q : ZMod P.modulus) rw [toField_ofField] /-- Rational casts into fast form agree with canonical-field casts. -/ @[simp] -theorem toField_ratCast (q : ℚ) : toField (q : FastField F) = (q : ZMod P.fieldSize) := by - change toField (ofField (q : ZMod P.fieldSize)) = (q : ZMod P.fieldSize) +theorem toField_ratCast (q : ℚ) : toField (q : FastField F) = (q : ZMod P.modulus) := by + change toField (ofField (q : ZMod P.modulus)) = (q : ZMod P.modulus) rw [toField_ofField] /-- Nonnegative rational scalar multiplication is preserved by `toField`. -/ @@ -1031,14 +1031,14 @@ instance (priority := low) instNonBinaryField : NonBinaryField (FastField F) whe char_neq_2 := by change ((2 : ℕ) : FastField F) ≠ 0 intro h - have htwo : (2 : ZMod P.fieldSize) = 0 := by + have htwo : (2 : ZMod P.modulus) = 0 := by calc - (2 : ZMod P.fieldSize) = ((2 : ℕ) : ZMod P.fieldSize) := by norm_cast + (2 : ZMod P.modulus) = ((2 : ℕ) : ZMod P.modulus) := by norm_cast _ = toField ((2 : ℕ) : FastField F) := (toField_natCast 2).symm _ = toField (0 : FastField F) := congrArg toField h _ = 0 := toField_zero - have hdvd : P.fieldSize ∣ 2 := (ZMod.natCast_eq_zero_iff 2 P.fieldSize).mp htwo - exact (Nat.not_le_of_gt P.two_lt_fieldSize) (Nat.le_of_dvd (by decide) hdvd) + have hdvd : P.modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 P.modulus).mp htwo + exact (Nat.not_le_of_gt P.two_lt_modulus) (Nat.le_of_dvd (by decide) hdvd) end From 71957d546c5bd1e5d0d3cb524e895dba307fa07d Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 14:24:00 +0200 Subject: [PATCH 14/31] refactor(fields): index Mont32Field by modulus --- CompPoly/Fields/BabyBear/Fast.lean | 21 +- CompPoly/Fields/BabyBear/Fast/Convert.lean | 20 +- CompPoly/Fields/BabyBear/Fast/Montgomery.lean | 20 +- CompPoly/Fields/BabyBear/Fast/Prelude.lean | 7 +- CompPoly/Fields/KoalaBear/Fast.lean | 21 +- CompPoly/Fields/KoalaBear/Fast/Convert.lean | 20 +- .../Fields/KoalaBear/Fast/Montgomery.lean | 20 +- CompPoly/Fields/KoalaBear/Fast/Prelude.lean | 7 +- CompPoly/Fields/Montgomery/Native32Field.lean | 596 +++++++++--------- 9 files changed, 362 insertions(+), 370 deletions(-) diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index 2a313148..ea05e891 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -16,9 +16,10 @@ A native-word implementation of BabyBear arithmetic as a sidecar to the canonica The operations, their `Field`/`CommRing`/`NonBinaryField` instances, the `toField` bridge, and all correctness theorems are shared across every fast 32-bit-word field; they live once in `CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance -in `CompPoly.Fields.BabyBear.Fast.Prelude`. Because `Field := Native32.FastField BabyBear.Field`, -the generic algebraic instances resolve here automatically. This module re-exports the -named operations and `simp` lemmas at the BabyBear instance. +in `CompPoly.Fields.BabyBear.Fast.Prelude`. Because +`Field := Native32.FastField BabyBear.fieldSize`, the generic algebraic instances resolve +here automatically. This module re-exports the named operations and `simp` lemmas at the +BabyBear instance. -/ namespace BabyBear @@ -51,7 +52,7 @@ def square (x : Field) : Field := Montgomery.Native32.square x def pow (x : Field) (n : Nat) : Field := Montgomery.Native32.pow x n /-- Fermat exponent used for inversion in the BabyBear prime field. -/ -def invExponent : Nat := Montgomery.Native32.invExponent (F := BabyBear.Field) +def invExponent : Nat := Montgomery.Native32.invExponent BabyBear.fieldSize /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ @[inline] @@ -63,12 +64,12 @@ def div (x y : Field) : Field := Montgomery.Native32.div x y /-- Ring equivalence between the fast Montgomery representation and canonical `BabyBear.Field`. -/ def ringEquiv : Field ≃+* BabyBear.Field := - Montgomery.Native32.ringEquiv (F := BabyBear.Field) + Montgomery.Native32.ringEquiv BabyBear.fieldSize /-- Converting from the canonical field to fast form and back is the identity. -/ @[simp] theorem toField_ofField (x : BabyBear.Field) : toField (ofField x) = x := - Montgomery.Native32.toField_ofField (F := BabyBear.Field) x + Montgomery.Native32.toField_ofField x /-- Converting from fast form to the canonical field and back is the identity. -/ @[simp] @@ -77,17 +78,17 @@ theorem ofField_toField (x : Field) : ofField (toField x) = x := /-- The canonical-field interpretation distinguishes fast BabyBear values. -/ theorem toField_injective : Function.Injective (toField : Field → BabyBear.Field) := - Montgomery.Native32.toField_injective (F := BabyBear.Field) + Montgomery.Native32.toField_injective /-- `toField` maps fast zero to canonical zero. -/ @[simp] theorem toField_zero : toField (0 : Field) = 0 := - Montgomery.Native32.toField_zero (F := BabyBear.Field) + Montgomery.Native32.toField_zero /-- `toField` maps fast one to canonical one. -/ @[simp] theorem toField_one : toField (1 : Field) = 1 := - Montgomery.Native32.toField_one (F := BabyBear.Field) + Montgomery.Native32.toField_one /-- Fast addition agrees with addition in the canonical BabyBear field. -/ @[simp] @@ -117,7 +118,7 @@ theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := /-- Applying the inverse `ringEquiv` is conversion into fast Montgomery form. -/ @[simp] theorem ringEquiv_symm_apply (x : BabyBear.Field) : ringEquiv.symm x = ofField x := - Montgomery.Native32.ringEquiv_symm_apply (F := BabyBear.Field) x + Montgomery.Native32.ringEquiv_symm_apply x /-- Fast squaring agrees with multiplication by itself in the canonical field. -/ @[simp] diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean index 53a2f3c0..1f9602c4 100644 --- a/CompPoly/Fields/BabyBear/Fast/Convert.lean +++ b/CompPoly/Fields/BabyBear/Fast/Convert.lean @@ -22,38 +22,38 @@ open Montgomery.Native32 /-- Build a fast element from a canonical natural representative. -/ @[inline] def ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : Field := - Montgomery.Native32.ofCanonicalNat (F := BabyBear.Field) n h + Montgomery.Native32.ofCanonicalNat n h /-- Reduce a `UInt64` modulo the BabyBear prime and return a Montgomery fast element. -/ @[inline] def reduceUInt64 (x : UInt64) : Field := - Montgomery.Native32.reduceUInt64 (F := BabyBear.Field) x + Montgomery.Native32.reduceUInt64 BabyBear.fieldSize x /-- The zero fast BabyBear element. -/ -def zero : Field := Montgomery.Native32.zero (F := BabyBear.Field) +def zero : Field := Montgomery.Native32.zero BabyBear.fieldSize /-- The one fast BabyBear element. -/ -def one : Field := Montgomery.Native32.one (F := BabyBear.Field) +def one : Field := Montgomery.Native32.one BabyBear.fieldSize /-- Convert a natural number into fast Montgomery representation. -/ @[inline] def ofNat (n : Nat) : Field := - Montgomery.Native32.ofNat (F := BabyBear.Field) n + Montgomery.Native32.ofNat BabyBear.fieldSize n /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (x : UInt32) : Field := - Montgomery.Native32.ofUInt32 (F := BabyBear.Field) x + Montgomery.Native32.ofUInt32 BabyBear.fieldSize x /-- Convert from the canonical `ZMod` BabyBear field into fast Montgomery form. -/ @[inline] def ofField (x : BabyBear.Field) : Field := - Montgomery.Native32.ofField (F := BabyBear.Field) x + Montgomery.Native32.ofField x /-- Convert an integer into fast Montgomery representation. -/ @[inline] def ofInt (n : Int) : Field := - Montgomery.Native32.ofInt (F := BabyBear.Field) n + Montgomery.Native32.ofInt BabyBear.fieldSize n /-- Convert a fast element to its canonical native-word representative. -/ @[inline] @@ -84,12 +84,12 @@ theorem raw_cast_eq_toField_mul (x : Field) : theorem nat_eq_of_field_eq {a b : Nat} (ha : a < BabyBear.fieldSize) (hb : b < BabyBear.fieldSize) (h : (a : BabyBear.Field) = (b : BabyBear.Field)) : a = b := - Montgomery.Native32.nat_eq_of_field_eq (F := BabyBear.Field) ha hb h + Montgomery.Native32.nat_eq_of_field_eq ha hb h theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < BabyBear.fieldSize) : ((ofCanonicalNat n h).val.toNat : BabyBear.Field) = (n : BabyBear.Field) * (UInt32.size : BabyBear.Field) := - Montgomery.Native32.ofCanonicalNat_raw_cast (F := BabyBear.Field) n h + Montgomery.Native32.ofCanonicalNat_raw_cast n h theorem reduceUInt64_raw_cast (x : UInt64) : ((reduceUInt64 x).val.toNat : BabyBear.Field) = diff --git a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean index f798d171..585760b7 100644 --- a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean +++ b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean @@ -26,56 +26,56 @@ open Montgomery.Native32 /-- Reduce a native word known to be below twice the BabyBear prime. -/ @[inline] def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := - Montgomery.Native32.reduceUInt32Lt2ModulusRaw (F := BabyBear.Field) x + Montgomery.Native32.reduceUInt32Lt2ModulusRaw BabyBear.fieldSize x theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : (reduceUInt32Lt2ModulusRaw x).toNat < BabyBear.fieldSize := - Montgomery.Native32.reduceUInt32Lt2ModulusRaw_lt (F := BabyBear.Field) x h + Montgomery.Native32.reduceUInt32Lt2ModulusRaw_lt x h /-- Reduce a native word known to be below twice the BabyBear prime. -/ @[inline] def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : Field := - Montgomery.Native32.reduceUInt32Lt2Modulus (F := BabyBear.Field) x h + Montgomery.Native32.reduceUInt32Lt2Modulus x h theorem reduceUInt32Lt2Modulus_cast (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : ((reduceUInt32Lt2Modulus x h).val.toNat : BabyBear.Field) = (x.toNat : BabyBear.Field) := - Montgomery.Native32.reduceUInt32Lt2Modulus_cast (F := BabyBear.Field) x h + Montgomery.Native32.reduceUInt32Lt2Modulus_cast x h /-- Reduce a native word below `2^32` modulo the BabyBear prime. -/ @[inline] def reduceUInt32 (x : UInt32) : Field := - Montgomery.Native32.reduceUInt32 (F := BabyBear.Field) x + Montgomery.Native32.reduceUInt32 BabyBear.fieldSize x /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := - Montgomery.Native32.montgomeryReduceBoundedRaw (F := BabyBear.Field) x + Montgomery.Native32.montgomeryReduceBoundedRaw BabyBear.fieldSize x theorem montgomeryReduceBoundedRaw_lt (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : (montgomeryReduceBoundedRaw x).toNat < BabyBear.fieldSize := - Montgomery.Native32.montgomeryReduceBoundedRaw_lt (F := BabyBear.Field) x h + Montgomery.Native32.montgomeryReduceBoundedRaw_lt x h /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBounded (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : Field := - Montgomery.Native32.montgomeryReduceBounded (F := BabyBear.Field) x h + Montgomery.Native32.montgomeryReduceBounded x h theorem montgomeryReduceBounded_cast (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : ((montgomeryReduceBounded x h).val.toNat : BabyBear.Field) = (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := - Montgomery.Native32.montgomeryReduceBounded_cast (F := BabyBear.Field) x h + Montgomery.Native32.montgomeryReduceBounded_cast x h /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] def montgomeryReduce (x : UInt64) : Field := - Montgomery.Native32.montgomeryReduce (F := BabyBear.Field) x + Montgomery.Native32.montgomeryReduce BabyBear.fieldSize x end Fast end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Prelude.lean index e555556e..0ab8c383 100644 --- a/CompPoly/Fields/BabyBear/Fast/Prelude.lean +++ b/CompPoly/Fields/BabyBear/Fast/Prelude.lean @@ -13,7 +13,7 @@ import CompPoly.Fields.Montgomery.Native32Field The native-word constants and the `Field` carrier type for the fast BabyBear field. The shared implementation lives in `CompPoly.Fields.Montgomery.Native32Field`; this module supplies the per-field `Mont32Field` instance (the five word constants plus the -`decide`-checked numeric facts) and pins `Field := Native32.FastField BabyBear.Field`, so +`decide`-checked numeric facts) and pins `Field := Native32.FastField BabyBear.fieldSize`, so the generic definitions, proofs, and algebraic instances specialize to BabyBear. The Montgomery reducers are re-exported in `CompPoly.Fields.BabyBear.Fast.Montgomery`; @@ -58,8 +58,7 @@ theorem r2ModModulus_toNat : /-- The per-field data realizing BabyBear as a fast 32-bit-word Montgomery field. The five word constants are the only runtime data; every other field is a `decide`-checked fact. -/ -instance instMont32Field : Mont32Field BabyBear.Field where - modulus := BabyBear.fieldSize +instance instMont32Field : Mont32Field BabyBear.fieldSize where prime := inferInstance modulus32 := modulus32 modulus64 := modulus64 @@ -75,7 +74,7 @@ instance instMont32Field : Mont32Field BabyBear.Field where montgomeryNegInv_mul_modulus_mod_two_pow_32 := by decide /-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ -abbrev Field : Type := FastField BabyBear.Field +abbrev Field : Type := FastField BabyBear.fieldSize /-- The raw Montgomery word backing a fast BabyBear element. -/ @[inline] diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index ea0f844b..454d0b33 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -16,9 +16,10 @@ A native-word implementation of KoalaBear arithmetic as a sidecar to the canonic The operations, their `Field`/`CommRing`/`NonBinaryField` instances, the `toField` bridge, and all correctness theorems are shared across every fast 32-bit-word field; they live once in `CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance -in `CompPoly.Fields.KoalaBear.Fast.Prelude`. Because `Field := Native32.FastField KoalaBear.Field`, -the generic algebraic instances resolve here automatically. This module re-exports the -named operations and `simp` lemmas at the KoalaBear instance. +in `CompPoly.Fields.KoalaBear.Fast.Prelude`. Because +`Field := Native32.FastField KoalaBear.fieldSize`, the generic algebraic instances resolve +here automatically. This module re-exports the named operations and `simp` lemmas at the +KoalaBear instance. -/ namespace KoalaBear @@ -51,7 +52,7 @@ def square (x : Field) : Field := Montgomery.Native32.square x def pow (x : Field) (n : Nat) : Field := Montgomery.Native32.pow x n /-- Fermat exponent used for inversion in the KoalaBear prime field. -/ -def invExponent : Nat := Montgomery.Native32.invExponent (F := KoalaBear.Field) +def invExponent : Nat := Montgomery.Native32.invExponent KoalaBear.fieldSize /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ @[inline] @@ -63,12 +64,12 @@ def div (x y : Field) : Field := Montgomery.Native32.div x y /-- Ring equivalence between the fast Montgomery representation and canonical `KoalaBear.Field`. -/ def ringEquiv : Field ≃+* KoalaBear.Field := - Montgomery.Native32.ringEquiv (F := KoalaBear.Field) + Montgomery.Native32.ringEquiv KoalaBear.fieldSize /-- Converting from the canonical field to fast form and back is the identity. -/ @[simp] theorem toField_ofField (x : KoalaBear.Field) : toField (ofField x) = x := - Montgomery.Native32.toField_ofField (F := KoalaBear.Field) x + Montgomery.Native32.toField_ofField x /-- Converting from fast form to the canonical field and back is the identity. -/ @[simp] @@ -77,17 +78,17 @@ theorem ofField_toField (x : Field) : ofField (toField x) = x := /-- The canonical-field interpretation distinguishes fast KoalaBear values. -/ theorem toField_injective : Function.Injective (toField : Field → KoalaBear.Field) := - Montgomery.Native32.toField_injective (F := KoalaBear.Field) + Montgomery.Native32.toField_injective /-- `toField` maps fast zero to canonical zero. -/ @[simp] theorem toField_zero : toField (0 : Field) = 0 := - Montgomery.Native32.toField_zero (F := KoalaBear.Field) + Montgomery.Native32.toField_zero /-- `toField` maps fast one to canonical one. -/ @[simp] theorem toField_one : toField (1 : Field) = 1 := - Montgomery.Native32.toField_one (F := KoalaBear.Field) + Montgomery.Native32.toField_one /-- Fast addition agrees with addition in the canonical KoalaBear field. -/ @[simp] @@ -117,7 +118,7 @@ theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := /-- Applying the inverse `ringEquiv` is conversion into fast Montgomery form. -/ @[simp] theorem ringEquiv_symm_apply (x : KoalaBear.Field) : ringEquiv.symm x = ofField x := - Montgomery.Native32.ringEquiv_symm_apply (F := KoalaBear.Field) x + Montgomery.Native32.ringEquiv_symm_apply x /-- Fast squaring agrees with multiplication by itself in the canonical field. -/ @[simp] diff --git a/CompPoly/Fields/KoalaBear/Fast/Convert.lean b/CompPoly/Fields/KoalaBear/Fast/Convert.lean index e3b997b2..8afd9595 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Convert.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Convert.lean @@ -22,38 +22,38 @@ open Montgomery.Native32 /-- Build a fast element from a canonical natural representative. -/ @[inline] def ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : Field := - Montgomery.Native32.ofCanonicalNat (F := KoalaBear.Field) n h + Montgomery.Native32.ofCanonicalNat n h /-- Reduce a `UInt64` modulo the KoalaBear prime and return a Montgomery fast element. -/ @[inline] def reduceUInt64 (x : UInt64) : Field := - Montgomery.Native32.reduceUInt64 (F := KoalaBear.Field) x + Montgomery.Native32.reduceUInt64 KoalaBear.fieldSize x /-- The zero fast KoalaBear element. -/ -def zero : Field := Montgomery.Native32.zero (F := KoalaBear.Field) +def zero : Field := Montgomery.Native32.zero KoalaBear.fieldSize /-- The one fast KoalaBear element. -/ -def one : Field := Montgomery.Native32.one (F := KoalaBear.Field) +def one : Field := Montgomery.Native32.one KoalaBear.fieldSize /-- Convert a natural number into fast Montgomery representation. -/ @[inline] def ofNat (n : Nat) : Field := - Montgomery.Native32.ofNat (F := KoalaBear.Field) n + Montgomery.Native32.ofNat KoalaBear.fieldSize n /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (x : UInt32) : Field := - Montgomery.Native32.ofUInt32 (F := KoalaBear.Field) x + Montgomery.Native32.ofUInt32 KoalaBear.fieldSize x /-- Convert from the canonical `ZMod` KoalaBear field into fast Montgomery form. -/ @[inline] def ofField (x : KoalaBear.Field) : Field := - Montgomery.Native32.ofField (F := KoalaBear.Field) x + Montgomery.Native32.ofField x /-- Convert an integer into fast Montgomery representation. -/ @[inline] def ofInt (n : Int) : Field := - Montgomery.Native32.ofInt (F := KoalaBear.Field) n + Montgomery.Native32.ofInt KoalaBear.fieldSize n /-- Convert a fast element to its canonical native-word representative. -/ @[inline] @@ -84,12 +84,12 @@ theorem raw_cast_eq_toField_mul (x : Field) : theorem nat_eq_of_field_eq {a b : Nat} (ha : a < KoalaBear.fieldSize) (hb : b < KoalaBear.fieldSize) (h : (a : KoalaBear.Field) = (b : KoalaBear.Field)) : a = b := - Montgomery.Native32.nat_eq_of_field_eq (F := KoalaBear.Field) ha hb h + Montgomery.Native32.nat_eq_of_field_eq ha hb h theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < KoalaBear.fieldSize) : ((ofCanonicalNat n h).val.toNat : KoalaBear.Field) = (n : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := - Montgomery.Native32.ofCanonicalNat_raw_cast (F := KoalaBear.Field) n h + Montgomery.Native32.ofCanonicalNat_raw_cast n h theorem reduceUInt64_raw_cast (x : UInt64) : ((reduceUInt64 x).val.toNat : KoalaBear.Field) = diff --git a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean index 4f9c94ca..021b8741 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean @@ -26,56 +26,56 @@ open Montgomery.Native32 /-- Reduce a native word known to be below twice the KoalaBear prime. -/ @[inline] def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := - Montgomery.Native32.reduceUInt32Lt2ModulusRaw (F := KoalaBear.Field) x + Montgomery.Native32.reduceUInt32Lt2ModulusRaw KoalaBear.fieldSize x theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : (reduceUInt32Lt2ModulusRaw x).toNat < KoalaBear.fieldSize := - Montgomery.Native32.reduceUInt32Lt2ModulusRaw_lt (F := KoalaBear.Field) x h + Montgomery.Native32.reduceUInt32Lt2ModulusRaw_lt x h /-- Reduce a native word known to be below twice the KoalaBear prime. -/ @[inline] def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : Field := - Montgomery.Native32.reduceUInt32Lt2Modulus (F := KoalaBear.Field) x h + Montgomery.Native32.reduceUInt32Lt2Modulus x h theorem reduceUInt32Lt2Modulus_cast (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : ((reduceUInt32Lt2Modulus x h).val.toNat : KoalaBear.Field) = (x.toNat : KoalaBear.Field) := - Montgomery.Native32.reduceUInt32Lt2Modulus_cast (F := KoalaBear.Field) x h + Montgomery.Native32.reduceUInt32Lt2Modulus_cast x h /-- Reduce a native word below `2^32` modulo the KoalaBear prime. -/ @[inline] def reduceUInt32 (x : UInt32) : Field := - Montgomery.Native32.reduceUInt32 (F := KoalaBear.Field) x + Montgomery.Native32.reduceUInt32 KoalaBear.fieldSize x /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := - Montgomery.Native32.montgomeryReduceBoundedRaw (F := KoalaBear.Field) x + Montgomery.Native32.montgomeryReduceBoundedRaw KoalaBear.fieldSize x theorem montgomeryReduceBoundedRaw_lt (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : (montgomeryReduceBoundedRaw x).toNat < KoalaBear.fieldSize := - Montgomery.Native32.montgomeryReduceBoundedRaw_lt (F := KoalaBear.Field) x h + Montgomery.Native32.montgomeryReduceBoundedRaw_lt x h /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBounded (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : Field := - Montgomery.Native32.montgomeryReduceBounded (F := KoalaBear.Field) x h + Montgomery.Native32.montgomeryReduceBounded x h theorem montgomeryReduceBounded_cast (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : ((montgomeryReduceBounded x h).val.toNat : KoalaBear.Field) = (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := - Montgomery.Native32.montgomeryReduceBounded_cast (F := KoalaBear.Field) x h + Montgomery.Native32.montgomeryReduceBounded_cast x h /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] def montgomeryReduce (x : UInt64) : Field := - Montgomery.Native32.montgomeryReduce (F := KoalaBear.Field) x + Montgomery.Native32.montgomeryReduce KoalaBear.fieldSize x end Fast end KoalaBear diff --git a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean index 64731640..67af7bee 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean @@ -13,7 +13,7 @@ import CompPoly.Fields.Montgomery.Native32Field The native-word constants and the `Field` carrier type for the fast KoalaBear field. The shared implementation lives in `CompPoly.Fields.Montgomery.Native32Field`; this module supplies the per-field `Mont32Field` instance (the five word constants plus the -`decide`-checked numeric facts) and pins `Field := Native32.FastField KoalaBear.Field`, so +`decide`-checked numeric facts) and pins `Field := Native32.FastField KoalaBear.fieldSize`, so the generic definitions, proofs, and algebraic instances specialize to KoalaBear. The Montgomery reducers are re-exported in `CompPoly.Fields.KoalaBear.Fast.Montgomery`; @@ -58,8 +58,7 @@ theorem r2ModModulus_toNat : /-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. The five word constants are the only runtime data; every other field is a `decide`-checked fact. -/ -instance instMont32Field : Mont32Field KoalaBear.Field where - modulus := KoalaBear.fieldSize +instance instMont32Field : Mont32Field KoalaBear.fieldSize where prime := inferInstance modulus32 := modulus32 modulus64 := modulus64 @@ -75,7 +74,7 @@ instance instMont32Field : Mont32Field KoalaBear.Field where montgomeryNegInv_mul_modulus_mod_two_pow_32 := by decide /-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ -abbrev Field : Type := FastField KoalaBear.Field +abbrev Field : Type := FastField KoalaBear.fieldSize /-- The raw Montgomery word backing a fast KoalaBear element. -/ @[inline] diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index 12916624..0f4501a2 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -17,12 +17,11 @@ constants; their definitions, the proofs about them, and the resulting algebraic are otherwise identical. This module captures that common content **once**, parameterized by a `Mont32Field` instance that supplies the per-field data. -* `Mont32Field F` bundles the prime (`modulus`), its native-word forms, the Montgomery +* `Mont32Field modulus` bundles the native-word forms of the prime modulus, the Montgomery constants, and the small `decide`-checkable numeric/`ZMod` facts the proofs consume. Everything except the five word constants is spec-level and erased at codegen. -* `FastField F` is the fast carrier `{ x : UInt32 // x.toNat < modulus }`, indexed by the - tag `F` so that the generic `Add`/`Mul`/`Field`/… instances below resolve for each - concrete field's `Field := FastField `. At runtime it erases to `UInt32`. +* `FastField modulus` is the fast carrier `{ x : UInt32 // x.toNat < modulus }`. + At runtime it erases to `UInt32`. * The executable `def`s are `@[inline]`/`@[specialize]`, so once a concrete instance is fixed the instance projections fold to literals and the compiled code is identical to a hand-written monomorphic version — no `Mont32Field` dictionary survives to runtime. @@ -40,10 +39,8 @@ namespace Native32 The five word constants (`modulus32`, `modulus64`, `rModModulus`, `r2ModModulus`, `montgomeryNegInv`) are the only runtime data; the remaining fields are `Prop`s and erased at codegen. -/ -class Mont32Field (F : Type) where - /-- The prime modulus `p`. -/ - modulus : ℕ - /-- `modulus` is prime — needed to reduce into `ZMod modulus` as a field. -/ +class Mont32Field (modulus : ℕ) where + /-- `modulus` is prime. -/ prime : Fact (Nat.Prime modulus) /-- `modulus` as a 32-bit word. -/ modulus32 : UInt32 @@ -67,79 +64,65 @@ class Mont32Field (F : Type) where attribute [instance] Mont32Field.prime namespace Mont32Field - -theorem two_lt_modulus {F : Type} [P : Mont32Field F] : 2 < P.modulus := by +theorem two_lt_modulus {modulus : ℕ} [P : Mont32Field modulus] : 2 < modulus := by have h := P.two_pow_32_lt_three_mul_modulus omega -theorem modulus_pos {F : Type} [P : Mont32Field F] : 0 < P.modulus := by +theorem modulus_pos {modulus : ℕ} [P : Mont32Field modulus] : 0 < modulus := by exact Nat.zero_lt_of_lt P.two_lt_modulus -theorem modulus_lt_two_pow_32 {F : Type} [P : Mont32Field F] : - P.modulus < 2 ^ 32 := by +theorem modulus_lt_two_pow_32 {modulus : ℕ} [P : Mont32Field modulus] : + modulus < 2 ^ 32 := by have h := P.two_mul_modulus_lt_two_pow_32 omega -theorem modulus_sq_lt_two_pow_64 {F : Type} [P : Mont32Field F] : - P.modulus ^ 2 < 2 ^ 64 := by +theorem modulus_sq_lt_two_pow_64 {modulus : ℕ} [P : Mont32Field modulus] : + modulus ^ 2 < 2 ^ 64 := by nlinarith [P.modulus_lt_two_pow_32] -theorem two_pow_32_ne_zero_in_field {F : Type} [P : Mont32Field F] : - ((2 ^ 32 : ℕ) : ZMod P.modulus) ≠ 0 := by - have htwo : (2 : ZMod P.modulus) ≠ 0 := by +theorem two_pow_32_ne_zero_in_field {modulus : ℕ} [P : Mont32Field modulus] : + ((2 ^ 32 : ℕ) : ZMod modulus) ≠ 0 := by + have htwo : (2 : ZMod modulus) ≠ 0 := by intro h - have hdvd : P.modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 P.modulus).mp h + have hdvd : modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 modulus).mp h exact (Nat.not_le_of_gt P.two_lt_modulus) (Nat.le_of_dvd (by decide) hdvd) rw [Nat.cast_pow] exact pow_ne_zero 32 htwo -theorem rModModulus_lt_modulus {F : Type} [P : Mont32Field F] : - P.rModModulus.toNat < P.modulus := by - rw [P.rModModulus_toNat] - exact Nat.mod_lt _ P.modulus_pos - -theorem r2ModModulus_lt_modulus {F : Type} [P : Mont32Field F] : - P.r2ModModulus.toNat < P.modulus := by +theorem r2ModModulus_lt_modulus {modulus : ℕ} [P : Mont32Field modulus] : + P.r2ModModulus.toNat < modulus := by rw [P.r2ModModulus_toNat] exact Nat.mod_lt _ P.modulus_pos - -theorem rModModulus_cast {F : Type} [P : Mont32Field F] : - (P.rModModulus.toNat : ZMod P.modulus) = ((2 ^ 32 : ℕ) : ZMod P.modulus) := by - rw [P.rModModulus_toNat, ZMod.natCast_mod] - -theorem r2ModModulus_cast {F : Type} [P : Mont32Field F] : - (P.r2ModModulus.toNat : ZMod P.modulus) = ((2 ^ 32 : ℕ) : ZMod P.modulus) ^ 2 := by - rw [P.r2ModModulus_toNat, ZMod.natCast_mod, Nat.cast_pow] - end Mont32Field -/-- The fast carrier for the field tagged by `F`: a native word below `modulus`, +/-- The fast carrier for a prime modulus: a native word below `modulus`, interpreted as a Montgomery residue. At runtime this erases to `UInt32`. -/ -def FastField (F : Type) [Mont32Field F] : Type := - { x : UInt32 // x.toNat < Mont32Field.modulus F } +def FastField (modulus : ℕ) [Mont32Field modulus] : Type := + { x : UInt32 // x.toNat < modulus } -instance (F : Type) [Mont32Field F] : DecidableEq (FastField F) := - inferInstanceAs (DecidableEq { x : UInt32 // x.toNat < Mont32Field.modulus F }) +instance (modulus : ℕ) [Mont32Field modulus] : DecidableEq (FastField modulus) := + inferInstanceAs (DecidableEq { x : UInt32 // x.toNat < modulus }) section -variable {F : Type} [P : Mont32Field F] +variable {modulus : ℕ} [P : Mont32Field modulus] -instance : NeZero P.modulus := ⟨P.modulus_pos.ne'⟩ +instance : NeZero modulus := ⟨P.modulus_pos.ne'⟩ /-- The raw Montgomery word backing a fast element. -/ @[inline] -def raw (x : FastField F) : UInt32 := x.val +def raw (x : FastField modulus) : UInt32 := x.val /-! ## Montgomery reduction -/ /-- Reduce a native word known to be below twice the prime. -/ @[inline] -def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := +def reduceUInt32Lt2ModulusRaw (modulus : ℕ) [P : Mont32Field modulus] + (x : UInt32) : UInt32 := if x < P.modulus32 then x else x - P.modulus32 theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) - (h : x.toNat < 2 * P.modulus) : - (reduceUInt32Lt2ModulusRaw (F := F) x).toNat < P.modulus := by + (h : x.toNat < 2 * modulus) : + (reduceUInt32Lt2ModulusRaw modulus x).toNat < modulus := by unfold reduceUInt32Lt2ModulusRaw by_cases hx : x < P.modulus32 · rw [if_pos hx] @@ -155,16 +138,16 @@ theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) /-- Reduce a native word known to be below twice the prime. -/ @[inline] -def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * P.modulus) : - FastField F := - ⟨reduceUInt32Lt2ModulusRaw (F := F) x, reduceUInt32Lt2ModulusRaw_lt x h⟩ +def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * modulus) : + FastField modulus := + ⟨reduceUInt32Lt2ModulusRaw modulus x, reduceUInt32Lt2ModulusRaw_lt x h⟩ theorem reduceUInt32Lt2Modulus_cast (x : UInt32) - (h : x.toNat < 2 * P.modulus) : - ((reduceUInt32Lt2Modulus (F := F) x h).val.toNat : ZMod P.modulus) = - (x.toNat : ZMod P.modulus) := by - change ((reduceUInt32Lt2ModulusRaw (F := F) x).toNat : ZMod P.modulus) = - (x.toNat : ZMod P.modulus) + (h : x.toNat < 2 * modulus) : + ((reduceUInt32Lt2Modulus (modulus := modulus) x h).val.toNat : ZMod modulus) = + (x.toNat : ZMod modulus) := by + change ((reduceUInt32Lt2ModulusRaw modulus x).toNat : ZMod modulus) = + (x.toNat : ZMod modulus) unfold reduceUInt32Lt2ModulusRaw by_cases hx : x < P.modulus32 · rw [if_pos hx] @@ -181,7 +164,7 @@ theorem reduceUInt32Lt2Modulus_cast (x : UInt32) /-- Reduce a native word below `2^32` modulo the prime. -/ @[inline] -def reduceUInt32 (x : UInt32) : FastField F := +def reduceUInt32 (modulus : ℕ) [P : Mont32Field modulus] (x : UInt32) : FastField modulus := if hx : x < P.modulus32 then ⟨x, by rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx @@ -198,8 +181,8 @@ def reduceUInt32 (x : UInt32) : FastField F := rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx exact Nat.le_of_not_gt hx - have hy_eq : y.toNat = x.toNat - P.modulus := by - change (x - P.modulus32).toNat = x.toNat - P.modulus + have hy_eq : y.toNat = x.toNat - modulus := by + change (x - P.modulus32).toNat = x.toNat - modulus rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus32_toNat] have hmod_le_y : P.modulus32 ≤ y := by rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] @@ -213,13 +196,14 @@ def reduceUInt32 (x : UInt32) : FastField F := /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] -def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := - reduceUInt32Lt2ModulusRaw (F := F) +def montgomeryReduceBoundedRaw (modulus : ℕ) [P : Mont32Field modulus] + (x : UInt64) : UInt32 := + reduceUInt32Lt2ModulusRaw modulus (Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x) theorem montgomeryReduceBoundedRaw_lt (x : UInt64) - (h : x.toNat < P.modulus * 2 ^ 32) : - (montgomeryReduceBoundedRaw (F := F) x).toNat < P.modulus := by + (h : x.toNat < modulus * 2 ^ 32) : + (montgomeryReduceBoundedRaw modulus x).toNat < modulus := by have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by rw [P.modulus64_toNat] have hp := P.two_mul_modulus_lt_two_pow_32 @@ -236,56 +220,57 @@ theorem montgomeryReduceBoundedRaw_lt (x : UInt64) /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduceBounded (x : UInt64) - (h : x.toNat < P.modulus * 2 ^ 32) : FastField F := - ⟨montgomeryReduceBoundedRaw (F := F) x, montgomeryReduceBoundedRaw_lt x h⟩ + (h : x.toNat < modulus * 2 ^ 32) : FastField modulus := + ⟨montgomeryReduceBoundedRaw modulus x, montgomeryReduceBoundedRaw_lt x h⟩ theorem montgomeryReduceBounded_cast (x : UInt64) - (h : x.toNat < P.modulus * 2 ^ 32) : - ((montgomeryReduceBounded (F := F) x h).val.toNat : ZMod P.modulus) = - (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ := by + (h : x.toNat < modulus * 2 ^ 32) : + ((montgomeryReduceBounded (modulus := modulus) x h).val.toNat : ZMod modulus) = + (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by rw [P.modulus64_toNat] have hp := P.two_mul_modulus_lt_two_pow_32 omega - change ((montgomeryReduceBoundedRaw (F := F) x).toNat : ZMod P.modulus) = - (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ + change ((montgomeryReduceBoundedRaw modulus x).toNat : ZMod modulus) = + (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ unfold montgomeryReduceBoundedRaw let u := Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x - change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.modulus) = - (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ - have hred := reduceUInt32Lt2Modulus_cast (F := F) u + change ((reduceUInt32Lt2ModulusRaw modulus u).toNat : ZMod modulus) = + (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ + have hred := reduceUInt32Lt2Modulus_cast (modulus := modulus) u (by simpa only [P.modulus64_toNat] using Montgomery.Native32.reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 (by simpa only [P.modulus64_toNat] using P.modulus_pos) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)) - change ((reduceUInt32Lt2ModulusRaw (F := F) u).toNat : ZMod P.modulus) = - (u.toNat : ZMod P.modulus) at hred + change ((reduceUInt32Lt2ModulusRaw modulus u).toNat : ZMod modulus) = + (u.toNat : ZMod modulus) at hred rw [hred] - change (u.toNat : ZMod P.modulus) = - (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ - rw [show u.toNat = reduceNatQuotient (2 ^ 32) P.modulus P.montgomeryNegInv.toNat x.toNat by + change (u.toNat : ZMod modulus) = + (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ + rw [show u.toNat = reduceNatQuotient (2 ^ 32) modulus P.montgomeryNegInv.toNat x.toNat by simpa only [u, P.modulus64_toNat] using Montgomery.Native32.reduceQuotient_toNat P.montgomeryNegInv P.modulus64 (by simpa only [P.modulus64_toNat] using P.modulus_pos) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)] - exact Montgomery.reduceNatQuotient_cast (2 ^ 32) P.modulus P.montgomeryNegInv.toNat + exact Montgomery.reduceNatQuotient_cast (2 ^ 32) modulus P.montgomeryNegInv.toNat (by decide) P.montgomeryNegInv_mul_modulus_mod_two_pow_32 P.two_pow_32_ne_zero_in_field x.toNat /-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ @[inline] -def montgomeryReduce (x : UInt64) : FastField F := +def montgomeryReduce (modulus : ℕ) [P : Mont32Field modulus] + (x : UInt64) : FastField modulus := let u := Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x - reduceUInt32 (F := F) u + reduceUInt32 modulus u /-! ## Conversions -/ /-- Build a fast element from a canonical natural representative. -/ @[inline] -def ofCanonicalNat (n : ℕ) (_h : n < P.modulus) : FastField F := +def ofCanonicalNat (n : ℕ) (_h : n < modulus) : FastField modulus := montgomeryReduceBounded (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by @@ -299,11 +284,12 @@ def ofCanonicalNat (n : ℕ) (_h : n < P.modulus) : FastField F := /-- Reduce a `UInt64` modulo the prime and return a Montgomery fast element. -/ @[inline] -def reduceUInt64 (x : UInt64) : FastField F := +def reduceUInt64 (modulus : ℕ) [P : Mont32Field modulus] + (x : UInt64) : FastField modulus := let y := x % P.modulus64 montgomeryReduceBounded (y * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : (x % P.modulus64).toNat < P.modulus := by + have hy_lt : (x % P.modulus64).toNat < modulus := by rw [UInt64.toNat_mod, P.modulus64_toNat] exact Nat.mod_lt _ P.modulus_pos have hprod : (x % P.modulus64).toNat * P.r2ModModulus.toNat < 2 ^ 64 := by @@ -312,90 +298,93 @@ def reduceUInt64 (x : UInt64) : FastField F := nlinarith [P.r2ModModulus_lt_modulus]) /-- The zero fast element. -/ -def zero : FastField F := ⟨0, by +def zero (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := ⟨0, by have h0 : (0 : UInt32).toNat = 0 := by decide have hp := P.modulus_pos omega⟩ /-- The one fast element. -/ -def one : FastField F := ⟨P.rModModulus, P.rModModulus_lt_modulus⟩ +def one (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := ⟨P.rModModulus, by + rw [P.rModModulus_toNat] + exact Nat.mod_lt _ P.modulus_pos⟩ /-- Convert a natural number into fast Montgomery representation. -/ @[inline] -def ofNat (n : ℕ) : FastField F := - ofCanonicalNat (n % P.modulus) (Nat.mod_lt _ P.modulus_pos) +def ofNat (modulus : ℕ) [P : Mont32Field modulus] (n : ℕ) : FastField modulus := + ofCanonicalNat (n % modulus) (Nat.mod_lt _ P.modulus_pos) /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] -def ofUInt32 (x : UInt32) : FastField F := - reduceUInt64 x.toUInt64 +def ofUInt32 (modulus : ℕ) [P : Mont32Field modulus] + (x : UInt32) : FastField modulus := + reduceUInt64 modulus x.toUInt64 /-- Convert from the canonical `ZMod` field into fast Montgomery form. -/ @[inline] -def ofField (x : ZMod P.modulus) : FastField F := +def ofField (x : ZMod modulus) : FastField modulus := ofCanonicalNat x.val (ZMod.val_lt x) /-- Convert an integer into fast Montgomery representation. -/ @[inline] -def ofInt (n : Int) : FastField F := - ofField (n : ZMod P.modulus) +def ofInt (modulus : ℕ) [P : Mont32Field modulus] (n : Int) : FastField modulus := + ofField (n : ZMod modulus) /-- Convert a fast element to its canonical native-word representative. -/ @[inline] -def toCanonicalUInt32 (x : FastField F) : UInt32 := - raw (montgomeryReduceBounded x.val.toUInt64 (by +def toCanonicalUInt32 (x : FastField modulus) : UInt32 := + raw (montgomeryReduceBounded (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] nlinarith [x.property, P.modulus_pos])) /-- Convert a fast element to its canonical natural representative. -/ @[inline] -def toNat (x : FastField F) : ℕ := +def toNat (x : FastField modulus) : ℕ := (toCanonicalUInt32 x).toNat /-- Convert a fast element to the canonical `ZMod` field. -/ @[inline] -def toField (x : FastField F) : ZMod P.modulus := - (toNat x : ZMod P.modulus) +def toField (x : FastField modulus) : ZMod modulus := + (toNat x : ZMod modulus) -theorem toNat_lt_modulus (x : FastField F) : toNat x < P.modulus := by +theorem toNat_lt_modulus (x : FastField modulus) : toNat x < modulus := by unfold toNat toCanonicalUInt32 raw - change (montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat < P.modulus - exact montgomeryReduceBoundedRaw_lt x.val.toUInt64 (by + change (montgomeryReduceBoundedRaw modulus x.val.toUInt64).toNat < modulus + exact montgomeryReduceBoundedRaw_lt (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] nlinarith [x.property, P.modulus_pos]) -theorem toField_eq_raw_mul_inv (x : FastField F) : +theorem toField_eq_raw_mul_inv (x : FastField modulus) : toField x = - (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ := by + (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by unfold toField toNat toCanonicalUInt32 raw - have hred := montgomeryReduceBounded_cast x.val.toUInt64 (by + have hred := montgomeryReduceBounded_cast (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] nlinarith [x.property, P.modulus_pos]) - change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.modulus) = - (x.val.toUInt64.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (F := F) x.val.toUInt64).toNat : ZMod P.modulus) = - (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ + change ((montgomeryReduceBoundedRaw modulus x.val.toUInt64).toNat : ZMod modulus) = + (x.val.toUInt64.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred + change ((montgomeryReduceBoundedRaw modulus x.val.toUInt64).toNat : ZMod modulus) = + (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ rw [hred] rw [UInt32.toNat_toUInt64] -theorem raw_cast_eq_toField_mul (x : FastField F) : - (x.val.toNat : ZMod P.modulus) = - toField x * ((2 ^ 32 : ℕ) : ZMod P.modulus) := by +theorem raw_cast_eq_toField_mul (x : FastField modulus) : + (x.val.toNat : ZMod modulus) = + toField x * ((2 ^ 32 : ℕ) : ZMod modulus) := by rw [toField_eq_raw_mul_inv] rw [mul_assoc] rw [inv_mul_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] -theorem nat_eq_of_field_eq {a b : ℕ} (ha : a < P.modulus) - (hb : b < P.modulus) (h : (a : ZMod P.modulus) = (b : ZMod P.modulus)) : +theorem nat_eq_of_field_eq {a b : ℕ} (ha : a < modulus) + (hb : b < modulus) (h : (a : ZMod modulus) = (b : ZMod modulus)) : a = b := Montgomery.natCast_inj_of_lt h ha hb -theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < P.modulus) : - ((ofCanonicalNat (F := F) n h).val.toNat : ZMod P.modulus) = - (n : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus) := by +theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < modulus) : + ((ofCanonicalNat (modulus := modulus) n h).val.toNat : ZMod modulus) = + (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by unfold ofCanonicalNat - have hred := montgomeryReduceBounded_cast + have hred := montgomeryReduceBounded_cast (modulus := modulus) (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by @@ -406,13 +395,13 @@ theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < P.modulus) : nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_modulus]) - change ((montgomeryReduceBoundedRaw (F := F) - (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.modulus) = - ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (F := F) - (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod P.modulus) = - (n : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus) + change ((montgomeryReduceBoundedRaw modulus + (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = + ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred + change ((montgomeryReduceBoundedRaw modulus + (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = + (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) rw [hred] simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by @@ -422,79 +411,80 @@ theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < P.modulus) : have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - rw [Nat.cast_mul, P.r2ModModulus_cast] + rw [Nat.cast_mul, P.r2ModModulus_toNat, ZMod.natCast_mod, Nat.cast_pow] rw [pow_two] - rw [mul_assoc (n : ZMod P.modulus) (((2 ^ 32 : ℕ) : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)) (((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹)] - rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod P.modulus) ((2 ^ 32 : ℕ) : ZMod P.modulus) - (((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹)] + rw [mul_assoc (n : ZMod modulus) (((2 ^ 32 : ℕ) : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)) (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] + rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod modulus) ((2 ^ 32 : ℕ) : ZMod modulus) + (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] -theorem toField_ofCanonicalNat_aux (n : ℕ) (h : n < P.modulus) : - toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.modulus) := by +theorem toField_ofCanonicalNat_aux (n : ℕ) (h : n < modulus) : + toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := by rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] rw [mul_assoc] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] theorem reduceUInt64_raw_cast (x : UInt64) : - ((reduceUInt64 (F := F) x).val.toNat : ZMod P.modulus) = - (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus) := by + ((reduceUInt64 modulus x).val.toNat : ZMod modulus) = + (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by unfold reduceUInt64 let y := x % P.modulus64 - have hred := montgomeryReduceBounded_cast (y * P.r2ModModulus.toUInt64) (by + have hred := montgomeryReduceBounded_cast (modulus := modulus) + (y * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < P.modulus := by + have hy_lt : y.toNat < modulus := by rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] exact Nat.mod_lt _ P.modulus_pos have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_modulus]) - change ((montgomeryReduceBoundedRaw (F := F) (y * P.r2ModModulus.toUInt64)).toNat : - ZMod P.modulus) = - ((y * P.r2ModModulus.toUInt64).toNat : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (F := F) (y * P.r2ModModulus.toUInt64)).toNat : - ZMod P.modulus) = - (x.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus) + change ((montgomeryReduceBoundedRaw modulus (y * P.r2ModModulus.toUInt64)).toNat : + ZMod modulus) = + ((y * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred + change ((montgomeryReduceBoundedRaw modulus (y * P.r2ModModulus.toUInt64)).toNat : + ZMod modulus) = + (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) rw [hred] simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < P.modulus := by + have hy_lt : y.toNat < modulus := by rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] exact Nat.mod_lt _ P.modulus_pos have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - have hy_cast : (y.toNat : ZMod P.modulus) = (x.toNat : ZMod P.modulus) := by + have hy_cast : (y.toNat : ZMod modulus) = (x.toNat : ZMod modulus) := by rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] rw [ZMod.natCast_eq_natCast_iff] exact Nat.mod_modEq _ _ - rw [Nat.cast_mul, P.r2ModModulus_cast, hy_cast] + rw [Nat.cast_mul, P.r2ModModulus_toNat, ZMod.natCast_mod, Nat.cast_pow, hy_cast] rw [pow_two] - rw [mul_assoc (x.toNat : ZMod P.modulus) (((2 ^ 32 : ℕ) : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)) (((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹)] - rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod P.modulus) ((2 ^ 32 : ℕ) : ZMod P.modulus) - (((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹)] + rw [mul_assoc (x.toNat : ZMod modulus) (((2 ^ 32 : ℕ) : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)) (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] + rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod modulus) ((2 ^ 32 : ℕ) : ZMod modulus) + (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] rw [mul_one] @[simp] -theorem toNat_ofCanonicalNat (n : ℕ) (h : n < P.modulus) : - toNat (ofCanonicalNat (F := F) n h) = n := +theorem toNat_ofCanonicalNat (n : ℕ) (h : n < modulus) : + toNat (ofCanonicalNat (modulus := modulus) n h) = n := nat_eq_of_field_eq (toNat_lt_modulus _) h (toField_ofCanonicalNat_aux n h) @[simp] -theorem toField_ofCanonicalNat (n : ℕ) (h : n < P.modulus) : - toField (ofCanonicalNat (F := F) n h) = (n : ZMod P.modulus) := +theorem toField_ofCanonicalNat (n : ℕ) (h : n < modulus) : + toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := toField_ofCanonicalNat_aux n h @[simp] theorem toNat_reduceUInt64 (x : UInt64) : - toNat (reduceUInt64 (F := F) x) = x.toNat % P.modulus := by + toNat (reduceUInt64 modulus x) = x.toNat % modulus := by apply nat_eq_of_field_eq (toNat_lt_modulus _) (Nat.mod_lt _ P.modulus_pos) - change toField (reduceUInt64 (F := F) x) = ((x.toNat % P.modulus : ℕ) : ZMod P.modulus) + change toField (reduceUInt64 modulus x) = ((x.toNat % modulus : ℕ) : ZMod modulus) rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] rw [mul_assoc] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] @@ -504,7 +494,7 @@ theorem toNat_reduceUInt64 (x : UInt64) : @[simp] theorem toField_reduceUInt64 (x : UInt64) : - toField (reduceUInt64 (F := F) x) = (x.toNat : ZMod P.modulus) := by + toField (reduceUInt64 modulus x) = (x.toNat : ZMod modulus) := by rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] rw [mul_assoc] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] @@ -514,7 +504,7 @@ theorem toField_reduceUInt64 (x : UInt64) : /-- Fast modular addition in Montgomery form. -/ @[inline] -def add (x y : FastField F) : FastField F := +def add (x y : FastField modulus) : FastField modulus := reduceUInt32Lt2Modulus (x.val + y.val) (by rw [UInt32.toNat_add] exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by @@ -522,9 +512,9 @@ def add (x y : FastField F) : FastField F := /-- Fast modular negation in Montgomery form. -/ @[inline] -def neg (x : FastField F) : FastField F := +def neg (x : FastField modulus) : FastField modulus := if hx : x.val = 0 then - zero + zero modulus else ⟨P.modulus32 - x.val, by have hle : x.val ≤ P.modulus32 := by @@ -542,17 +532,17 @@ def neg (x : FastField F) : FastField F := /-- Fast modular subtraction in Montgomery form. -/ @[inline] -def sub (x y : FastField F) : FastField F := +def sub (x y : FastField modulus) : FastField modulus := if hyx : y.val ≤ x.val then ⟨x.val - y.val, by rw [UInt32.toNat_sub_of_le _ _ hyx] have := x.property; omega⟩ else ⟨x.val + P.modulus32 - y.val, by - have hsum_lt : x.val.toNat + P.modulus < 2 ^ 32 := by + have hsum_lt : x.val.toNat + modulus < 2 ^ 32 := by have htwo := P.two_mul_modulus_lt_two_pow_32 have := x.property; omega - have hsum_eq : (x.val + P.modulus32).toNat = x.val.toNat + P.modulus := by + have hsum_eq : (x.val + P.modulus32).toNat = x.val.toNat + modulus := by rw [UInt32.toNat_add, P.modulus32_toNat, Nat.mod_eq_of_lt hsum_lt] have hyle : y.val ≤ x.val + P.modulus32 := by rw [UInt32.le_iff_toNat_le, hsum_eq] @@ -567,7 +557,7 @@ def sub (x y : FastField F) : FastField F := /-- Fast modular multiplication in Montgomery form. -/ @[inline] -def mul (x y : FastField F) : FastField F := +def mul (x y : FastField modulus) : FastField modulus := montgomeryReduceBounded (x.val.toUInt64 * y.val.toUInt64) (by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by @@ -577,112 +567,112 @@ def mul (x y : FastField F) : FastField F := /-- Fast squaring. -/ @[inline] -def square (x : FastField F) : FastField F := +def square (x : FastField modulus) : FastField modulus := mul x x /-- Exponentiation over the fast representation using repeated squaring. -/ @[specialize] -def pow (x : FastField F) (n : ℕ) : FastField F := - @npowBinRec (FastField F) ⟨one⟩ ⟨mul⟩ n x +def pow (x : FastField modulus) (n : ℕ) : FastField modulus := + @npowBinRec (FastField modulus) ⟨one modulus⟩ ⟨mul⟩ n x /-- Fermat exponent used for inversion in the prime field. -/ -def invExponent : ℕ := P.modulus - 2 +def invExponent (modulus : ℕ) : ℕ := modulus - 2 /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`), by binary exponentiation (`pow`). -/ @[inline] -def inv (x : FastField F) : FastField F := - pow x (invExponent (F := F)) +def inv (x : FastField modulus) : FastField modulus := + pow x (invExponent modulus) /-- Division through inversion and fast multiplication. -/ @[inline] -def div (x y : FastField F) : FastField F := +def div (x y : FastField modulus) : FastField modulus := mul x (inv y) -instance instZero : Zero (FastField F) where - zero := zero +instance instZero : Zero (FastField modulus) where + zero := zero modulus -instance instOne : One (FastField F) where - one := one +instance instOne : One (FastField modulus) where + one := one modulus -instance instAdd : Add (FastField F) where +instance instAdd : Add (FastField modulus) where add := add -instance instNeg : Neg (FastField F) where +instance instNeg : Neg (FastField modulus) where neg := neg -instance instSub : Sub (FastField F) where +instance instSub : Sub (FastField modulus) where sub := sub -instance instMul : Mul (FastField F) where +instance instMul : Mul (FastField modulus) where mul := mul -instance instInv : Inv (FastField F) where +instance instInv : Inv (FastField modulus) where inv := inv -instance instDiv : Div (FastField F) where +instance instDiv : Div (FastField modulus) where div := div -instance instNatCast : NatCast (FastField F) where - natCast := ofNat +instance instNatCast : NatCast (FastField modulus) where + natCast := ofNat modulus -instance instIntCast : IntCast (FastField F) where - intCast := ofInt +instance instIntCast : IntCast (FastField modulus) where + intCast := ofInt modulus -instance instNatSMul : SMul ℕ (FastField F) where - smul n x := ofNat n * x +instance instNatSMul : SMul ℕ (FastField modulus) where + smul n x := ofNat modulus n * x -instance instIntSMul : SMul Int (FastField F) where - smul n x := ofInt n * x +instance instIntSMul : SMul Int (FastField modulus) where + smul n x := ofInt modulus n * x -instance instPowNat : Pow (FastField F) ℕ where +instance instPowNat : Pow (FastField modulus) ℕ where pow := pow -instance instPowInt : Pow (FastField F) Int where +instance instPowInt : Pow (FastField modulus) Int where pow x n := match n with | Int.ofNat k => pow x k | Int.negSucc k => pow (inv x) (k + 1) -instance instNNRatCast : NNRatCast (FastField F) where - nnratCast q := ofField (q : ZMod P.modulus) +instance instNNRatCast : NNRatCast (FastField modulus) where + nnratCast q := ofField (q : ZMod modulus) -instance instRatCast : RatCast (FastField F) where - ratCast q := ofField (q : ZMod P.modulus) +instance instRatCast : RatCast (FastField modulus) where + ratCast q := ofField (q : ZMod modulus) -instance instNNRatSMul : SMul ℚ≥0 (FastField F) where +instance instNNRatSMul : SMul ℚ≥0 (FastField modulus) where smul q x := ofField (q • toField x) -instance instRatSMul : SMul ℚ (FastField F) where +instance instRatSMul : SMul ℚ (FastField modulus) where smul q x := ofField (q • toField x) /-- Fermat-style inversion in `ZMod modulus`. -/ -theorem inv_eq_pow_field (a : ZMod P.modulus) (ha : a ≠ 0) : - a⁻¹ = a ^ (P.modulus - 2) := by - have hcard : Fintype.card (ZMod P.modulus) = P.modulus := ZMod.card P.modulus - have h1 : a ^ (P.modulus - 1) = 1 := by +theorem inv_eq_pow_field (a : ZMod modulus) (ha : a ≠ 0) : + a⁻¹ = a ^ (modulus - 2) := by + have hcard : Fintype.card (ZMod modulus) = modulus := ZMod.card modulus + have h1 : a ^ (modulus - 1) = 1 := by have h := FiniteField.pow_card_sub_one_eq_one a ha rw [hcard] at h; exact h - have hmul : a * a ^ (P.modulus - 2) = 1 := by - rw [← pow_succ']; show a ^ (P.modulus - 2 + 1) = 1 - have : P.modulus - 2 + 1 = P.modulus - 1 := by + have hmul : a * a ^ (modulus - 2) = 1 := by + rw [← pow_succ']; show a ^ (modulus - 2 + 1) = 1 + have : modulus - 2 + 1 = modulus - 1 := by have := P.two_lt_modulus; omega rw [this]; exact h1 exact (eq_inv_of_mul_eq_one_left (by rwa [mul_comm])).symm /-- Converting from the canonical field to fast form and back is the identity. -/ @[simp] -theorem toField_ofField (x : ZMod P.modulus) : toField (ofField (F := F) x) = x := by +theorem toField_ofField (x : ZMod modulus) : toField (ofField (modulus := modulus) x) = x := by unfold ofField rw [toField_ofCanonicalNat] exact ZMod.natCast_zmod_val x /-- Converting from fast form to the canonical field and back is the identity. -/ @[simp] -theorem ofField_toField (x : FastField F) : ofField (toField x) = x := by +theorem ofField_toField (x : FastField modulus) : ofField (toField x) = x := by apply Subtype.ext apply UInt32.toNat_inj.mp - apply nat_eq_of_field_eq (F := F) + apply nat_eq_of_field_eq (modulus := modulus) · exact (ofField (toField x)).property · exact x.property · rw [raw_cast_eq_toField_mul] @@ -690,40 +680,40 @@ theorem ofField_toField (x : FastField F) : ofField (toField x) = x := by rw [raw_cast_eq_toField_mul] /-- The canonical-field interpretation distinguishes fast values. -/ -theorem toField_injective : Function.Injective (toField (F := F)) := +theorem toField_injective : Function.Injective (toField (modulus := modulus)) := Function.LeftInverse.injective ofField_toField /-- `toField` maps fast zero to canonical zero. -/ @[simp] -theorem toField_zero : toField (0 : FastField F) = 0 := by +theorem toField_zero : toField (0 : FastField modulus) = 0 := by rw [toField_eq_raw_mul_inv] - change ((0 : ℕ) : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = 0 + change ((0 : ℕ) : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = 0 rw [Nat.cast_zero, zero_mul] /-- `toField` maps fast one to canonical one. -/ @[simp] -theorem toField_one : toField (1 : FastField F) = 1 := by +theorem toField_one : toField (1 : FastField modulus) = 1 := by rw [toField_eq_raw_mul_inv] - change (P.rModModulus.toNat : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = 1 - rw [P.rModModulus_cast] + change (P.rModModulus.toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = 1 + rw [P.rModModulus_toNat, ZMod.natCast_mod] exact mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field /-- Fast addition agrees with addition in the canonical field. -/ @[simp] -theorem toField_add (x y : FastField F) : toField (x + y) = toField x + toField y := by +theorem toField_add (x y : FastField modulus) : toField (x + y) = toField x + toField y := by rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] unfold instAdd add - have hred := reduceUInt32Lt2Modulus_cast (F := F) (x.val + y.val) (by + have hred := reduceUInt32Lt2Modulus_cast (modulus := modulus) (x.val + y.val) (by rw [UInt32.toNat_add] exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by have hx := x.property; have hy := y.property; omega)) - change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.modulus) = - ((x.val + y.val).toNat : ZMod P.modulus) at hred - change ((reduceUInt32Lt2ModulusRaw (F := F) (x.val + y.val)).toNat : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = - (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ + - (y.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ + change ((reduceUInt32Lt2ModulusRaw modulus (x.val + y.val)).toNat : ZMod modulus) = + ((x.val + y.val).toNat : ZMod modulus) at hred + change ((reduceUInt32Lt2ModulusRaw modulus (x.val + y.val)).toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = + (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ + + (y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ rw [hred] rw [UInt32.toNat_add] have hsum_lt : x.val.toNat + y.val.toNat < 2 ^ 32 := by @@ -734,40 +724,40 @@ theorem toField_add (x y : FastField F) : toField (x + y) = toField x + toField /-- Fast subtraction agrees with subtraction in the canonical field. -/ @[simp] -theorem toField_sub (x y : FastField F) : toField (x - y) = toField x - toField y := by +theorem toField_sub (x y : FastField modulus) : toField (x - y) = toField x - toField y := by rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] by_cases hyx : y.val ≤ x.val - · have hsubval : (x - y : FastField F).val = x.val - y.val := by + · have hsubval : (x - y : FastField modulus).val = x.val - y.val := by change (sub x y).val = x.val - y.val unfold sub rw [dif_pos hyx] rw [hsubval] - change (((x.val - y.val).toNat : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) = - (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ - - (y.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ + change (((x.val - y.val).toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) = + (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - + (y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ rw [UInt32.toNat_sub_of_le _ _ hyx] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le] at hyx exact hyx)] ring - · have hsum_lt : x.val.toNat + P.modulus < 2 ^ 32 := by + · have hsum_lt : x.val.toNat + modulus < 2 ^ 32 := by have htwo := P.two_mul_modulus_lt_two_pow_32 have := x.property; omega - have hsum_eq : (x.val + P.modulus32).toNat = x.val.toNat + P.modulus := by + have hsum_eq : (x.val + P.modulus32).toNat = x.val.toNat + modulus := by rw [UInt32.toNat_add, P.modulus32_toNat, Nat.mod_eq_of_lt hsum_lt] have hyle : y.val ≤ x.val + P.modulus32 := by rw [UInt32.le_iff_toNat_le, hsum_eq] have := y.property; omega - have hsubval : (x - y : FastField F).val = x.val + P.modulus32 - y.val := by + have hsubval : (x - y : FastField modulus).val = x.val + P.modulus32 - y.val := by change (sub x y).val = x.val + P.modulus32 - y.val unfold sub rw [dif_neg hyx] rw [hsubval] - change (((x.val + P.modulus32 - y.val).toNat : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) = - (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ - - (y.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ + change (((x.val + P.modulus32 - y.val).toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) = + (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - + (y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le, hsum_eq] at hyle @@ -777,34 +767,34 @@ theorem toField_sub (x y : FastField F) : toField (x - y) = toField x - toField /-- Fast negation agrees with negation in the canonical field. -/ @[simp] -theorem toField_neg (x : FastField F) : toField (-x) = -toField x := by +theorem toField_neg (x : FastField modulus) : toField (-x) = -toField x := by rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x] by_cases hx : x.val = 0 - · have hnegval : (-x : FastField F).val = (zero : FastField F).val := by - change (neg x).val = (zero : FastField F).val + · have hnegval : (-x : FastField modulus).val = (zero modulus).val := by + change (neg x).val = (zero modulus).val unfold neg rw [dif_pos hx] rw [hnegval] - change ((zero : FastField F).val.toNat : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = - -((x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) + change ((zero modulus).val.toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = + -((x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) have hxNat : x.val.toNat = 0 := by simpa using congrArg UInt32.toNat hx rw [hxNat] - change ((0 : ℕ) : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = - -(((0 : ℕ) : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) + change ((0 : ℕ) : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = + -(((0 : ℕ) : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) simp · have hle : x.val ≤ P.modulus32 := by rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] exact Nat.le_of_lt x.property - have hnegval : (-x : FastField F).val = P.modulus32 - x.val := by + have hnegval : (-x : FastField modulus).val = P.modulus32 - x.val := by change (neg x).val = P.modulus32 - x.val unfold neg rw [dif_neg hx] rw [hnegval] - change (((P.modulus32 - x.val).toNat : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) = - -((x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) + change (((P.modulus32 - x.val).toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) = + -((x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus32_toNat] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] at hle @@ -814,23 +804,25 @@ theorem toField_neg (x : FastField F) : toField (-x) = -toField x := by /-- Fast multiplication agrees with multiplication in the canonical field. -/ @[simp] -theorem toField_mul (x y : FastField F) : toField (x * y) = toField x * toField y := by +theorem toField_mul (x y : FastField modulus) : toField (x * y) = toField x * toField y := by rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] unfold instMul mul - have hred := montgomeryReduceBounded_cast (F := F) (x.val.toUInt64 * y.val.toUInt64) (by - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, P.modulus_lt_two_pow_32, P.modulus_pos]) - change ((montgomeryReduceBoundedRaw (F := F) (x.val.toUInt64 * y.val.toUInt64)).toNat : - ZMod P.modulus) = - ((x.val.toUInt64 * y.val.toUInt64).toNat : ZMod P.modulus) * - ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ at hred - change ((montgomeryReduceBoundedRaw (F := F) (x.val.toUInt64 * y.val.toUInt64)).toNat : - ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ = - (x.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹ * - ((y.val.toNat : ZMod P.modulus) * ((2 ^ 32 : ℕ) : ZMod P.modulus)⁻¹) + have hred := montgomeryReduceBounded_cast (modulus := modulus) + (x.val.toUInt64 * y.val.toUInt64) (by + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] + have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by + nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [x.property, y.property, P.modulus_lt_two_pow_32, P.modulus_pos]) + change ((montgomeryReduceBoundedRaw modulus + (x.val.toUInt64 * y.val.toUInt64)).toNat : ZMod modulus) = + ((x.val.toUInt64 * y.val.toUInt64).toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred + change ((montgomeryReduceBoundedRaw modulus + (x.val.toUInt64 * y.val.toUInt64)).toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = + (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ * + ((y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) rw [hred] simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by @@ -840,7 +832,7 @@ theorem toField_mul (x y : FastField F) : toField (x * y) = toField x * toField ring /-- Ring equivalence between the fast Montgomery representation and the canonical field. -/ -def ringEquiv : FastField F ≃+* ZMod P.modulus where +def ringEquiv (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus ≃+* ZMod modulus where toFun := toField invFun := ofField left_inv := ofField_toField @@ -849,20 +841,20 @@ def ringEquiv : FastField F ≃+* ZMod P.modulus where map_mul' := toField_mul @[simp] -theorem ringEquiv_apply (x : FastField F) : ringEquiv x = toField x := rfl +theorem ringEquiv_apply (x : FastField modulus) : ringEquiv modulus x = toField x := rfl @[simp] -theorem ringEquiv_symm_apply (x : ZMod P.modulus) : - (ringEquiv (F := F)).symm x = ofField x := rfl +theorem ringEquiv_symm_apply (x : ZMod modulus) : + (ringEquiv modulus).symm x = ofField x := rfl -private theorem mul_assoc_field (x y z : FastField F) : (x * y) * z = x * (y * z) := by +private theorem mul_assoc_field (x y z : FastField modulus) : (x * y) * z = x * (y * z) := by apply toField_injective rw [toField_mul, toField_mul, toField_mul, toField_mul] ring -private theorem pow_succ_field (x : FastField F) (n : ℕ) : pow x (n + 1) = pow x n * x := by +private theorem pow_succ_field (x : FastField modulus) (n : ℕ) : pow x (n + 1) = pow x n * x := by unfold pow - letI : Semigroup (FastField F) := { + letI : Semigroup (FastField modulus) := { mul := (· * ·) mul_assoc := mul_assoc_field } @@ -870,13 +862,13 @@ private theorem pow_succ_field (x : FastField F) (n : ℕ) : pow x (n + 1) = pow /-- Fast squaring agrees with multiplication by itself in the canonical field. -/ @[simp] -theorem toField_square (x : FastField F) : toField (square x) = toField x * toField x := by +theorem toField_square (x : FastField modulus) : toField (square x) = toField x * toField x := by change toField (x * x) = toField x * toField x rw [toField_mul] /-- Fast natural-power computation agrees with powers in the canonical field. -/ @[simp] -theorem toField_pow (x : FastField F) (n : ℕ) : toField (pow x n) = toField x ^ n := by +theorem toField_pow (x : FastField modulus) (n : ℕ) : toField (pow x n) = toField x ^ n := by induction n with | zero => unfold pow @@ -886,12 +878,12 @@ theorem toField_pow (x : FastField F) (n : ℕ) : toField (pow x n) = toField x | succ n ih => rw [pow_succ_field, toField_mul, ih, _root_.pow_succ] -private theorem toField_inv_pow (x : FastField F) : - toField (inv x) = toField x ^ (invExponent (F := F)) := by +private theorem toField_inv_pow (x : FastField modulus) : + toField (inv x) = toField x ^ invExponent modulus := by unfold inv - exact toField_pow x (invExponent (F := F)) + exact toField_pow x (invExponent modulus) -private theorem toField_inv_raw (x : FastField F) : toField (inv x) = (toField x)⁻¹ := by +private theorem toField_inv_raw (x : FastField modulus) : toField (inv x) = (toField x)⁻¹ := by rw [toField_inv_pow] by_cases hx : toField x = 0 · rw [hx, inv_zero] @@ -900,25 +892,25 @@ private theorem toField_inv_raw (x : FastField F) : toField (inv x) = (toField x /-- Fast inversion agrees with inversion in the canonical field. -/ @[simp] -theorem toField_inv (x : FastField F) : toField x⁻¹ = (toField x)⁻¹ := by +theorem toField_inv (x : FastField modulus) : toField x⁻¹ = (toField x)⁻¹ := by change toField (inv x) = (toField x)⁻¹ exact toField_inv_raw x -private theorem toField_mul_raw (x y : FastField F) : +private theorem toField_mul_raw (x y : FastField modulus) : toField (mul x y) = toField x * toField y := by change toField (x * y) = toField x * toField y exact toField_mul x y -private theorem toField_div_mul_inv (x y : FastField F) : +private theorem toField_div_mul_inv (x y : FastField modulus) : toField (div x y) = toField x * toField (inv y) := by unfold div exact toField_mul_raw x (inv y) /-- Fast division agrees with division in the canonical field. -/ @[simp] -theorem toField_div (x y : FastField F) : toField (x / y) = toField x / toField y := by +theorem toField_div (x y : FastField modulus) : toField (x / y) = toField x / toField y := by change toField (div x y) = toField x / toField y - have h : ∀ a b c : ZMod P.modulus, c = b⁻¹ → a * c = a / b := by + have h : ∀ a b c : ZMod modulus, c = b⁻¹ → a * c = a / b := by intro a b c hc rw [hc] rfl @@ -927,8 +919,8 @@ theorem toField_div (x y : FastField F) : toField (x / y) = toField x / toField /-- Natural casts into fast form agree with natural casts into the canonical field. -/ @[simp] -theorem toField_natCast (n : ℕ) : toField (n : FastField F) = (n : ZMod P.modulus) := by - change toField (ofNat n) = (n : ZMod P.modulus) +theorem toField_natCast (n : ℕ) : toField (n : FastField modulus) = (n : ZMod modulus) := by + change toField (ofNat modulus n) = (n : ZMod modulus) unfold ofNat rw [toField_ofCanonicalNat] rw [ZMod.natCast_eq_natCast_iff] @@ -936,34 +928,34 @@ theorem toField_natCast (n : ℕ) : toField (n : FastField F) = (n : ZMod P.modu /-- Integer casts into fast form agree with integer casts into the canonical field. -/ @[simp] -theorem toField_intCast (n : Int) : toField (n : FastField F) = (n : ZMod P.modulus) := by - change toField (ofInt n) = (n : ZMod P.modulus) +theorem toField_intCast (n : Int) : toField (n : FastField modulus) = (n : ZMod modulus) := by + change toField (ofInt modulus n) = (n : ZMod modulus) unfold ofInt rw [toField_ofField] /-- Natural scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_nsmul (n : ℕ) (x : FastField F) : toField (n • x) = n • toField x := by - change toField ((n : FastField F) * x) = n • toField x +theorem toField_nsmul (n : ℕ) (x : FastField modulus) : toField (n • x) = n • toField x := by + change toField ((n : FastField modulus) * x) = n • toField x rw [toField_mul, toField_natCast] rw [nsmul_eq_mul] /-- Integer scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_zsmul (n : Int) (x : FastField F) : toField (n • x) = n • toField x := by - change toField ((n : FastField F) * x) = n • toField x +theorem toField_zsmul (n : Int) (x : FastField modulus) : toField (n • x) = n • toField x := by + change toField ((n : FastField modulus) * x) = n • toField x rw [toField_mul, toField_intCast] rw [zsmul_eq_mul] /-- Natural powers through the `Pow` instance are preserved by `toField`. -/ @[simp] -theorem toField_npow (x : FastField F) (n : ℕ) : toField (x ^ n) = toField x ^ n := by +theorem toField_npow (x : FastField modulus) (n : ℕ) : toField (x ^ n) = toField x ^ n := by change toField (pow x n) = toField x ^ n rw [toField_pow] /-- Integer powers through the `Pow` instance are preserved by `toField`. -/ @[simp] -theorem toField_zpow (x : FastField F) (n : Int) : toField (x ^ n) = toField x ^ n := by +theorem toField_zpow (x : FastField modulus) (n : Int) : toField (x ^ n) = toField x ^ n := by cases n with | ofNat n => change toField (pow x n) = toField x ^ (Int.ofNat n) @@ -978,30 +970,30 @@ theorem toField_zpow (x : FastField F) (n : Int) : toField (x ^ n) = toField x ^ /-- Nonnegative rational casts into fast form agree with canonical-field casts. -/ @[simp] -theorem toField_nnratCast (q : ℚ≥0) : toField (q : FastField F) = (q : ZMod P.modulus) := by - change toField (ofField (q : ZMod P.modulus)) = (q : ZMod P.modulus) +theorem toField_nnratCast (q : ℚ≥0) : toField (q : FastField modulus) = (q : ZMod modulus) := by + change toField (ofField (q : ZMod modulus)) = (q : ZMod modulus) rw [toField_ofField] /-- Rational casts into fast form agree with canonical-field casts. -/ @[simp] -theorem toField_ratCast (q : ℚ) : toField (q : FastField F) = (q : ZMod P.modulus) := by - change toField (ofField (q : ZMod P.modulus)) = (q : ZMod P.modulus) +theorem toField_ratCast (q : ℚ) : toField (q : FastField modulus) = (q : ZMod modulus) := by + change toField (ofField (q : ZMod modulus)) = (q : ZMod modulus) rw [toField_ofField] /-- Nonnegative rational scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_nnqsmul (q : ℚ≥0) (x : FastField F) : toField (q • x) = q • toField x := by +theorem toField_nnqsmul (q : ℚ≥0) (x : FastField modulus) : toField (q • x) = q • toField x := by change toField (ofField (q • toField x)) = q • toField x rw [toField_ofField] /-- Rational scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_qsmul (q : ℚ) (x : FastField F) : toField (q • x) = q • toField x := by +theorem toField_qsmul (q : ℚ) (x : FastField modulus) : toField (q • x) = q • toField x := by change toField (ofField (q • toField x)) = q • toField x rw [toField_ofField] /-- Field instance transferred from the canonical field through `toField`. -/ -instance (priority := low) instField : _root_.Field (FastField F) := +instance (priority := low) instField : _root_.Field (FastField modulus) := toField_injective.field toField toField_zero toField_one @@ -1023,21 +1015,21 @@ instance (priority := low) instField : _root_.Field (FastField F) := toField_ratCast /-- Commutative-ring instance inherited from the transferred field structure. -/ -instance (priority := low) instCommRing : CommRing (FastField F) := by +instance (priority := low) instCommRing : CommRing (FastField modulus) := by infer_instance /-- A fast 32-bit-word field is non-binary. -/ -instance (priority := low) instNonBinaryField : NonBinaryField (FastField F) where +instance (priority := low) instNonBinaryField : NonBinaryField (FastField modulus) where char_neq_2 := by - change ((2 : ℕ) : FastField F) ≠ 0 + change ((2 : ℕ) : FastField modulus) ≠ 0 intro h - have htwo : (2 : ZMod P.modulus) = 0 := by + have htwo : (2 : ZMod modulus) = 0 := by calc - (2 : ZMod P.modulus) = ((2 : ℕ) : ZMod P.modulus) := by norm_cast - _ = toField ((2 : ℕ) : FastField F) := (toField_natCast 2).symm - _ = toField (0 : FastField F) := congrArg toField h + (2 : ZMod modulus) = ((2 : ℕ) : ZMod modulus) := by norm_cast + _ = toField ((2 : ℕ) : FastField modulus) := (toField_natCast 2).symm + _ = toField (0 : FastField modulus) := congrArg toField h _ = 0 := toField_zero - have hdvd : P.modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 P.modulus).mp htwo + have hdvd : modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 modulus).mp htwo exact (Nat.not_le_of_gt P.two_lt_modulus) (Nat.le_of_dvd (by decide) hdvd) end From 8d73aa722f7c731e1cb9ed1af724c901a0a6fbab Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 14:56:47 +0200 Subject: [PATCH 15/31] minor --- CompPoly/Fields/BabyBear/Fast/Prelude.lean | 2 +- CompPoly/Fields/KoalaBear/Fast/Prelude.lean | 2 +- CompPoly/Fields/Montgomery/Native32Field.lean | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Prelude.lean index 0ab8c383..1a989da8 100644 --- a/CompPoly/Fields/BabyBear/Fast/Prelude.lean +++ b/CompPoly/Fields/BabyBear/Fast/Prelude.lean @@ -59,7 +59,7 @@ theorem r2ModModulus_toNat : /-- The per-field data realizing BabyBear as a fast 32-bit-word Montgomery field. The five word constants are the only runtime data; every other field is a `decide`-checked fact. -/ instance instMont32Field : Mont32Field BabyBear.fieldSize where - prime := inferInstance + prime := BabyBear.is_prime modulus32 := modulus32 modulus64 := modulus64 rModModulus := rModModulus diff --git a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean index 67af7bee..2ec76e55 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean @@ -59,7 +59,7 @@ theorem r2ModModulus_toNat : /-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. The five word constants are the only runtime data; every other field is a `decide`-checked fact. -/ instance instMont32Field : Mont32Field KoalaBear.fieldSize where - prime := inferInstance + prime := KoalaBear.is_prime modulus32 := modulus32 modulus64 := modulus64 rModModulus := rModModulus diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index 0f4501a2..b39993ea 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -41,7 +41,7 @@ The five word constants (`modulus32`, `modulus64`, `rModModulus`, `r2ModModulus` erased at codegen. -/ class Mont32Field (modulus : ℕ) where /-- `modulus` is prime. -/ - prime : Fact (Nat.Prime modulus) + prime : modulus.Prime /-- `modulus` as a 32-bit word. -/ modulus32 : UInt32 /-- `modulus` as a 64-bit word. -/ @@ -61,9 +61,10 @@ class Mont32Field (modulus : ℕ) where montgomeryNegInv_mul_modulus_mod_two_pow_32 : (montgomeryNegInv.toNat * modulus) % 2 ^ 32 = 2 ^ 32 - 1 -attribute [instance] Mont32Field.prime - namespace Mont32Field +instance factPrime (modulus : ℕ) [P : Mont32Field modulus] : Fact (Nat.Prime modulus) := + ⟨P.prime⟩ + theorem two_lt_modulus {modulus : ℕ} [P : Mont32Field modulus] : 2 < modulus := by have h := P.two_pow_32_lt_three_mul_modulus omega From 3ec01057e21fc6dde6dac6356952f102a831c541 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 17:46:07 +0200 Subject: [PATCH 16/31] refactor(fields): trim native Montgomery API --- CompPoly/Fields/BabyBear/Fast/Montgomery.lean | 37 ++--- CompPoly/Fields/BabyBear/Fast/Prelude.lean | 76 +-------- .../Fields/KoalaBear/Fast/Montgomery.lean | 37 ++--- CompPoly/Fields/KoalaBear/Fast/Prelude.lean | 72 +------- CompPoly/Fields/Montgomery/Native32Field.lean | 156 ++++++------------ tests/CompPolyTests/Fields/BabyBear/Fast.lean | 5 +- .../CompPolyTests/Fields/KoalaBear/Fast.lean | 5 +- 7 files changed, 100 insertions(+), 288 deletions(-) diff --git a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean index 585760b7..804bc8b9 100644 --- a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean +++ b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean @@ -35,47 +35,34 @@ theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) /-- Reduce a native word known to be below twice the BabyBear prime. -/ @[inline] -def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : - Field := +def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : Field := Montgomery.Native32.reduceUInt32Lt2Modulus x h theorem reduceUInt32Lt2Modulus_cast (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : - ((reduceUInt32Lt2Modulus x h).val.toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) := + ((reduceUInt32Lt2Modulus x h).val.toNat : BabyBear.Field) = (x.toNat : BabyBear.Field) := Montgomery.Native32.reduceUInt32Lt2Modulus_cast x h -/-- Reduce a native word below `2^32` modulo the BabyBear prime. -/ -@[inline] -def reduceUInt32 (x : UInt32) : Field := - Montgomery.Native32.reduceUInt32 BabyBear.fieldSize x - /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] -def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := - Montgomery.Native32.montgomeryReduceBoundedRaw BabyBear.fieldSize x +def montgomeryReduceRaw (x : UInt64) : UInt32 := + Montgomery.Native32.montgomeryReduceRaw BabyBear.fieldSize x -theorem montgomeryReduceBoundedRaw_lt (x : UInt64) +theorem montgomeryReduceRaw_lt (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : - (montgomeryReduceBoundedRaw x).toNat < BabyBear.fieldSize := - Montgomery.Native32.montgomeryReduceBoundedRaw_lt x h + (montgomeryReduceRaw x).toNat < BabyBear.fieldSize := + Montgomery.Native32.montgomeryReduceRaw_lt x h /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] -def montgomeryReduceBounded (x : UInt64) - (h : x.toNat < BabyBear.fieldSize * UInt32.size) : Field := - Montgomery.Native32.montgomeryReduceBounded x h +def montgomeryReduce (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : Field := + Montgomery.Native32.montgomeryReduce x h -theorem montgomeryReduceBounded_cast (x : UInt64) +theorem montgomeryReduce_cast (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : - ((montgomeryReduceBounded x h).val.toNat : BabyBear.Field) = + ((montgomeryReduce x h).val.toNat : BabyBear.Field) = (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := - Montgomery.Native32.montgomeryReduceBounded_cast x h - -/-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ -@[inline] -def montgomeryReduce (x : UInt64) : Field := - Montgomery.Native32.montgomeryReduce BabyBear.fieldSize x + Montgomery.Native32.montgomeryReduce_cast x h end Fast end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Prelude.lean index 1a989da8..d10bff40 100644 --- a/CompPoly/Fields/BabyBear/Fast/Prelude.lean +++ b/CompPoly/Fields/BabyBear/Fast/Prelude.lean @@ -3,82 +3,24 @@ Copyright (c) 2026 CompPoly Contributors. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Valerii Huhnin, Georgios Raikos -/ - import CompPoly.Fields.BabyBear.Basic import CompPoly.Fields.Montgomery.Native32Field -/-! -# Fast BabyBear Field — Basics - -The native-word constants and the `Field` carrier type for the fast BabyBear field. The -shared implementation lives in `CompPoly.Fields.Montgomery.Native32Field`; this module -supplies the per-field `Mont32Field` instance (the five word constants plus the -`decide`-checked numeric facts) and pins `Field := Native32.FastField BabyBear.fieldSize`, so -the generic definitions, proofs, and algebraic instances specialize to BabyBear. - -The Montgomery reducers are re-exported in `CompPoly.Fields.BabyBear.Fast.Montgomery`; -conversions in `...Fast.Convert`; the field operations and instances in -`CompPoly.Fields.BabyBear.Fast`. --/ - -namespace BabyBear -namespace Fast +/-! # Fast BabyBear field definitions -/ +namespace BabyBear.Fast open Montgomery.Native32 (Mont32Field FastField) -/-- BabyBear modulus as a native word. -/ -def modulus32 : UInt32 := 0x78000001 - -/-- BabyBear modulus as a 64-bit word for modular reduction. -/ -def modulus64 : UInt64 := 0x78000001 - -/-- `2^32 mod BabyBear.fieldSize`. This is the Montgomery representation of one. -/ -def rModModulus : UInt32 := 0x0FFFFFFE - -/-- `(2^32)^2 mod BabyBear.fieldSize`, used to enter Montgomery representation. -/ -def r2ModModulus : UInt32 := 0x45DDDDE3 - -/-- `-BabyBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ -def montgomeryNegInv : UInt32 := 0x77FFFFFF - -/-- The native `UInt32` modulus agrees with the mathematical BabyBear modulus. -/ -@[simp] theorem modulus32_toNat : modulus32.toNat = BabyBear.fieldSize := by decide - -/-- The native `UInt64` modulus agrees with the mathematical BabyBear modulus. -/ -@[simp] theorem modulus64_toNat : modulus64.toNat = BabyBear.fieldSize := by decide - -theorem two_mul_modulus_lt_two_pow_32 : 2 * BabyBear.fieldSize < 2 ^ 32 := by decide - -theorem two_pow_32_lt_three_mul_modulus : 2 ^ 32 < 3 * BabyBear.fieldSize := by decide - -theorem rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % BabyBear.fieldSize := by decide - -theorem r2ModModulus_toNat : - r2ModModulus.toNat = (2 ^ 32) ^ 2 % BabyBear.fieldSize := by decide - -/-- The per-field data realizing BabyBear as a fast 32-bit-word Montgomery field. The five -word constants are the only runtime data; every other field is a `decide`-checked fact. -/ +/-- The per-field data realizing BabyBear as a fast 32-bit-word Montgomery field. -/ instance instMont32Field : Mont32Field BabyBear.fieldSize where prime := BabyBear.is_prime - modulus32 := modulus32 - modulus64 := modulus64 - rModModulus := rModModulus - r2ModModulus := r2ModModulus - montgomeryNegInv := montgomeryNegInv - modulus32_toNat := modulus32_toNat - modulus64_toNat := modulus64_toNat - two_mul_modulus_lt_two_pow_32 := two_mul_modulus_lt_two_pow_32 - two_pow_32_lt_three_mul_modulus := two_pow_32_lt_three_mul_modulus - rModModulus_toNat := rModModulus_toNat - r2ModModulus_toNat := r2ModModulus_toNat - montgomeryNegInv_mul_modulus_mod_two_pow_32 := by decide + modulus32 := 0x78000001 + modulus64 := 0x78000001 + rModModulus := 0x0FFFFFFE + r2ModModulus := 0x45DDDDE3 + montgomeryNegInv := 0x77FFFFFF /-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ abbrev Field : Type := FastField BabyBear.fieldSize -/-- The raw Montgomery word backing a fast BabyBear element. -/ -@[inline] -def raw (x : Field) : UInt32 := x.val - -end Fast -end BabyBear +end BabyBear.Fast diff --git a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean index 021b8741..8753b318 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean @@ -35,47 +35,34 @@ theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) /-- Reduce a native word known to be below twice the KoalaBear prime. -/ @[inline] -def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : - Field := +def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : Field := Montgomery.Native32.reduceUInt32Lt2Modulus x h theorem reduceUInt32Lt2Modulus_cast (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : - ((reduceUInt32Lt2Modulus x h).val.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) := + ((reduceUInt32Lt2Modulus x h).val.toNat : KoalaBear.Field) = (x.toNat : KoalaBear.Field) := Montgomery.Native32.reduceUInt32Lt2Modulus_cast x h -/-- Reduce a native word below `2^32` modulo the KoalaBear prime. -/ -@[inline] -def reduceUInt32 (x : UInt32) : Field := - Montgomery.Native32.reduceUInt32 KoalaBear.fieldSize x - /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] -def montgomeryReduceBoundedRaw (x : UInt64) : UInt32 := - Montgomery.Native32.montgomeryReduceBoundedRaw KoalaBear.fieldSize x +def montgomeryReduceRaw (x : UInt64) : UInt32 := + Montgomery.Native32.montgomeryReduceRaw KoalaBear.fieldSize x -theorem montgomeryReduceBoundedRaw_lt (x : UInt64) +theorem montgomeryReduceRaw_lt (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - (montgomeryReduceBoundedRaw x).toNat < KoalaBear.fieldSize := - Montgomery.Native32.montgomeryReduceBoundedRaw_lt x h + (montgomeryReduceRaw x).toNat < KoalaBear.fieldSize := + Montgomery.Native32.montgomeryReduceRaw_lt x h /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] -def montgomeryReduceBounded (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : Field := - Montgomery.Native32.montgomeryReduceBounded x h +def montgomeryReduce (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : Field := + Montgomery.Native32.montgomeryReduce x h -theorem montgomeryReduceBounded_cast (x : UInt64) +theorem montgomeryReduce_cast (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - ((montgomeryReduceBounded x h).val.toNat : KoalaBear.Field) = + ((montgomeryReduce x h).val.toNat : KoalaBear.Field) = (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := - Montgomery.Native32.montgomeryReduceBounded_cast x h - -/-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ -@[inline] -def montgomeryReduce (x : UInt64) : Field := - Montgomery.Native32.montgomeryReduce KoalaBear.fieldSize x + Montgomery.Native32.montgomeryReduce_cast x h end Fast end KoalaBear diff --git a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean index 2ec76e55..631f2370 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Prelude.lean @@ -7,78 +7,22 @@ Authors: Valerii Huhnin import CompPoly.Fields.KoalaBear.Basic import CompPoly.Fields.Montgomery.Native32Field -/-! -# Fast KoalaBear Field — Basics - -The native-word constants and the `Field` carrier type for the fast KoalaBear field. The -shared implementation lives in `CompPoly.Fields.Montgomery.Native32Field`; this module -supplies the per-field `Mont32Field` instance (the five word constants plus the -`decide`-checked numeric facts) and pins `Field := Native32.FastField KoalaBear.fieldSize`, so -the generic definitions, proofs, and algebraic instances specialize to KoalaBear. - -The Montgomery reducers are re-exported in `CompPoly.Fields.KoalaBear.Fast.Montgomery`; -conversions in `...Fast.Convert`; the field operations and instances in -`CompPoly.Fields.KoalaBear.Fast`. --/ - -namespace KoalaBear -namespace Fast +/-! # Fast KoalaBear field definitions -/ +namespace KoalaBear.Fast open Montgomery.Native32 (Mont32Field FastField) -/-- KoalaBear modulus as a native word. -/ -def modulus32 : UInt32 := 0x7F000001 - -/-- KoalaBear modulus as a 64-bit word for modular reduction. -/ -def modulus64 : UInt64 := 0x7F000001 - -/-- `2^32 mod KoalaBear.fieldSize`. This is the Montgomery representation of one. -/ -def rModModulus : UInt32 := 0x01FFFFFE - -/-- `(2^32)^2 mod KoalaBear.fieldSize`, used to enter Montgomery representation. -/ -def r2ModModulus : UInt32 := 0x17F7EFE4 - -/-- `-KoalaBear.fieldSize⁻¹ mod 2^32`, used by Montgomery reduction. -/ -def montgomeryNegInv : UInt32 := 0x7EFFFFFF - -/-- The native `UInt32` modulus agrees with the mathematical KoalaBear modulus. -/ -@[simp] theorem modulus32_toNat : modulus32.toNat = KoalaBear.fieldSize := by decide - -/-- The native `UInt64` modulus agrees with the mathematical KoalaBear modulus. -/ -@[simp] theorem modulus64_toNat : modulus64.toNat = KoalaBear.fieldSize := by decide - -theorem two_mul_modulus_lt_two_pow_32 : 2 * KoalaBear.fieldSize < 2 ^ 32 := by decide - -theorem two_pow_32_lt_three_mul_modulus : 2 ^ 32 < 3 * KoalaBear.fieldSize := by decide - -theorem rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % KoalaBear.fieldSize := by decide - -theorem r2ModModulus_toNat : - r2ModModulus.toNat = (2 ^ 32) ^ 2 % KoalaBear.fieldSize := by decide - /-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. The five word constants are the only runtime data; every other field is a `decide`-checked fact. -/ instance instMont32Field : Mont32Field KoalaBear.fieldSize where prime := KoalaBear.is_prime - modulus32 := modulus32 - modulus64 := modulus64 - rModModulus := rModModulus - r2ModModulus := r2ModModulus - montgomeryNegInv := montgomeryNegInv - modulus32_toNat := modulus32_toNat - modulus64_toNat := modulus64_toNat - two_mul_modulus_lt_two_pow_32 := two_mul_modulus_lt_two_pow_32 - two_pow_32_lt_three_mul_modulus := two_pow_32_lt_three_mul_modulus - rModModulus_toNat := rModModulus_toNat - r2ModModulus_toNat := r2ModModulus_toNat - montgomeryNegInv_mul_modulus_mod_two_pow_32 := by decide + modulus32 := 0x7F000001 + modulus64 := 0x7F000001 + rModModulus := 0x01FFFFFE + r2ModModulus := 0x17F7EFE4 + montgomeryNegInv := 0x7EFFFFFF /-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ abbrev Field : Type := FastField KoalaBear.fieldSize -/-- The raw Montgomery word backing a fast KoalaBear element. -/ -@[inline] -def raw (x : Field) : UInt32 := x.val - -end Fast -end KoalaBear +end KoalaBear.Fast diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index b39993ea..d09f0d00 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -52,23 +52,19 @@ class Mont32Field (modulus : ℕ) where r2ModModulus : UInt32 /-- `-modulus⁻¹ mod 2^32`, used by Montgomery reduction. -/ montgomeryNegInv : UInt32 - modulus32_toNat : modulus32.toNat = modulus - modulus64_toNat : modulus64.toNat = modulus - two_mul_modulus_lt_two_pow_32 : 2 * modulus < 2 ^ 32 - two_pow_32_lt_three_mul_modulus : 2 ^ 32 < 3 * modulus - rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % modulus - r2ModModulus_toNat : r2ModModulus.toNat = (2 ^ 32) ^ 2 % modulus + modulus32_toNat : modulus32.toNat = modulus := by decide + modulus64_toNat : modulus64.toNat = modulus := by decide + two_lt_modulus : 2 < modulus := by decide + two_mul_modulus_lt_two_pow_32 : 2 * modulus < 2 ^ 32 := by decide + rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % modulus := by decide + r2ModModulus_toNat : r2ModModulus.toNat = (2 ^ 32) ^ 2 % modulus := by decide montgomeryNegInv_mul_modulus_mod_two_pow_32 : - (montgomeryNegInv.toNat * modulus) % 2 ^ 32 = 2 ^ 32 - 1 + (montgomeryNegInv.toNat * modulus) % 2 ^ 32 = 2 ^ 32 - 1 := by decide namespace Mont32Field instance factPrime (modulus : ℕ) [P : Mont32Field modulus] : Fact (Nat.Prime modulus) := ⟨P.prime⟩ -theorem two_lt_modulus {modulus : ℕ} [P : Mont32Field modulus] : 2 < modulus := by - have h := P.two_pow_32_lt_three_mul_modulus - omega - theorem modulus_pos {modulus : ℕ} [P : Mont32Field modulus] : 0 < modulus := by exact Nat.zero_lt_of_lt P.two_lt_modulus @@ -109,10 +105,6 @@ variable {modulus : ℕ} [P : Mont32Field modulus] instance : NeZero modulus := ⟨P.modulus_pos.ne'⟩ -/-- The raw Montgomery word backing a fast element. -/ -@[inline] -def raw (x : FastField modulus) : UInt32 := x.val - /-! ## Montgomery reduction -/ /-- Reduce a native word known to be below twice the prime. -/ @@ -121,20 +113,17 @@ def reduceUInt32Lt2ModulusRaw (modulus : ℕ) [P : Mont32Field modulus] (x : UInt32) : UInt32 := if x < P.modulus32 then x else x - P.modulus32 -theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) - (h : x.toNat < 2 * modulus) : +theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) (h : x.toNat < 2 * modulus) : (reduceUInt32Lt2ModulusRaw modulus x).toNat < modulus := by - unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < P.modulus32 + simp only [reduceUInt32Lt2ModulusRaw, UInt32.lt_iff_toNat_lt, + Mont32Field.modulus32_toNat] + by_cases hx : x.toNat < modulus · rw [if_pos hx] - rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx exact hx - · rw [if_neg hx] - have hmod_le_x : P.modulus32 ≤ x := by - rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] - rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx - exact Nat.le_of_not_gt hx - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus32_toNat] + · have hmod_le_x : P.modulus32 ≤ x := by + rw [UInt32.le_iff_toNat_le, Mont32Field.modulus32_toNat] + omega + rw [if_neg hx, UInt32.toNat_sub_of_le _ _ hmod_le_x, Mont32Field.modulus32_toNat] omega /-- Reduce a native word known to be below twice the prime. -/ @@ -163,85 +152,53 @@ theorem reduceUInt32Lt2Modulus_cast (x : UInt32) exact hmod_le_x)] simp -/-- Reduce a native word below `2^32` modulo the prime. -/ -@[inline] -def reduceUInt32 (modulus : ℕ) [P : Mont32Field modulus] (x : UInt32) : FastField modulus := - if hx : x < P.modulus32 then - ⟨x, by - rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx - exact hx⟩ - else - let y := x - P.modulus32 - if hy : y < P.modulus32 then - ⟨y, by - rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hy - exact hy⟩ - else - ⟨y - P.modulus32, by - have hmod_le_x : P.modulus32 ≤ x := by - rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] - rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx - exact Nat.le_of_not_gt hx - have hy_eq : y.toNat = x.toNat - modulus := by - change (x - P.modulus32).toNat = x.toNat - modulus - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus32_toNat] - have hmod_le_y : P.modulus32 ≤ y := by - rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] - rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hy - exact Nat.le_of_not_gt hy - rw [UInt32.toNat_sub_of_le _ _ hmod_le_y, P.modulus32_toNat, hy_eq] - have hx_lt := UInt32.toNat_lt_size x - change x.toNat < 2 ^ 32 at hx_lt - have hthree := P.two_pow_32_lt_three_mul_modulus - omega⟩ - /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] -def montgomeryReduceBoundedRaw (modulus : ℕ) [P : Mont32Field modulus] +def montgomeryReduceRaw (modulus : ℕ) [P : Mont32Field modulus] (x : UInt64) : UInt32 := reduceUInt32Lt2ModulusRaw modulus - (Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x) + (reduceQuotient P.montgomeryNegInv P.modulus64 x) -theorem montgomeryReduceBoundedRaw_lt (x : UInt64) +theorem montgomeryReduceRaw_lt (x : UInt64) (h : x.toNat < modulus * 2 ^ 32) : - (montgomeryReduceBoundedRaw modulus x).toNat < modulus := by + (montgomeryReduceRaw modulus x).toNat < modulus := by have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by rw [P.modulus64_toNat] have hp := P.two_mul_modulus_lt_two_pow_32 omega - unfold montgomeryReduceBoundedRaw + unfold montgomeryReduceRaw exact reduceUInt32Lt2ModulusRaw_lt _ (by simpa only [P.modulus64_toNat] using - Montgomery.Native32.reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 + reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 (by simpa only [P.modulus64_toNat] using P.modulus_pos) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)) /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] -def montgomeryReduceBounded (x : UInt64) +def montgomeryReduce (x : UInt64) (h : x.toNat < modulus * 2 ^ 32) : FastField modulus := - ⟨montgomeryReduceBoundedRaw modulus x, montgomeryReduceBoundedRaw_lt x h⟩ + ⟨montgomeryReduceRaw modulus x, montgomeryReduceRaw_lt x h⟩ -theorem montgomeryReduceBounded_cast (x : UInt64) +theorem montgomeryReduce_cast (x : UInt64) (h : x.toNat < modulus * 2 ^ 32) : - ((montgomeryReduceBounded (modulus := modulus) x h).val.toNat : ZMod modulus) = + ((montgomeryReduce (modulus := modulus) x h).val.toNat : ZMod modulus) = (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by rw [P.modulus64_toNat] have hp := P.two_mul_modulus_lt_two_pow_32 omega - change ((montgomeryReduceBoundedRaw modulus x).toNat : ZMod modulus) = + change ((montgomeryReduceRaw modulus x).toNat : ZMod modulus) = (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - unfold montgomeryReduceBoundedRaw - let u := Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x + unfold montgomeryReduceRaw + let u := reduceQuotient P.montgomeryNegInv P.modulus64 x change ((reduceUInt32Lt2ModulusRaw modulus u).toNat : ZMod modulus) = (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ have hred := reduceUInt32Lt2Modulus_cast (modulus := modulus) u (by simpa only [P.modulus64_toNat] using - Montgomery.Native32.reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 + reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 (by simpa only [P.modulus64_toNat] using P.modulus_pos) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)) @@ -252,27 +209,20 @@ theorem montgomeryReduceBounded_cast (x : UInt64) (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ rw [show u.toNat = reduceNatQuotient (2 ^ 32) modulus P.montgomeryNegInv.toNat x.toNat by simpa only [u, P.modulus64_toNat] using - Montgomery.Native32.reduceQuotient_toNat P.montgomeryNegInv P.modulus64 + reduceQuotient_toNat P.montgomeryNegInv P.modulus64 (by simpa only [P.modulus64_toNat] using P.modulus_pos) hmodulus_bound x (by simpa only [P.modulus64_toNat] using h)] - exact Montgomery.reduceNatQuotient_cast (2 ^ 32) modulus P.montgomeryNegInv.toNat + exact reduceNatQuotient_cast (2 ^ 32) modulus P.montgomeryNegInv.toNat (by decide) P.montgomeryNegInv_mul_modulus_mod_two_pow_32 P.two_pow_32_ne_zero_in_field x.toNat -/-- Montgomery reduction of a 64-bit word. Hot bounded callers use `montgomeryReduceBounded`. -/ -@[inline] -def montgomeryReduce (modulus : ℕ) [P : Mont32Field modulus] - (x : UInt64) : FastField modulus := - let u := Montgomery.Native32.reduceQuotient P.montgomeryNegInv P.modulus64 x - reduceUInt32 modulus u - /-! ## Conversions -/ /-- Build a fast element from a canonical natural representative. -/ @[inline] def ofCanonicalNat (n : ℕ) (_h : n < modulus) : FastField modulus := - montgomeryReduceBounded (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by + montgomeryReduce (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by apply Nat.mod_eq_of_lt @@ -288,7 +238,7 @@ def ofCanonicalNat (n : ℕ) (_h : n < modulus) : FastField modulus := def reduceUInt64 (modulus : ℕ) [P : Mont32Field modulus] (x : UInt64) : FastField modulus := let y := x % P.modulus64 - montgomeryReduceBounded (y * P.r2ModModulus.toUInt64) (by + montgomeryReduce (y * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hy_lt : (x % P.modulus64).toNat < modulus := by rw [UInt64.toNat_mod, P.modulus64_toNat] @@ -333,9 +283,9 @@ def ofInt (modulus : ℕ) [P : Mont32Field modulus] (n : Int) : FastField modulu /-- Convert a fast element to its canonical native-word representative. -/ @[inline] def toCanonicalUInt32 (x : FastField modulus) : UInt32 := - raw (montgomeryReduceBounded (modulus := modulus) x.val.toUInt64 (by + (montgomeryReduce (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.modulus_pos])) + nlinarith [x.property, P.modulus_pos])).val /-- Convert a fast element to its canonical natural representative. -/ @[inline] @@ -348,22 +298,22 @@ def toField (x : FastField modulus) : ZMod modulus := (toNat x : ZMod modulus) theorem toNat_lt_modulus (x : FastField modulus) : toNat x < modulus := by - unfold toNat toCanonicalUInt32 raw - change (montgomeryReduceBoundedRaw modulus x.val.toUInt64).toNat < modulus - exact montgomeryReduceBoundedRaw_lt (modulus := modulus) x.val.toUInt64 (by + unfold toNat toCanonicalUInt32 + change (montgomeryReduceRaw modulus x.val.toUInt64).toNat < modulus + exact montgomeryReduceRaw_lt (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] nlinarith [x.property, P.modulus_pos]) theorem toField_eq_raw_mul_inv (x : FastField modulus) : toField x = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - unfold toField toNat toCanonicalUInt32 raw - have hred := montgomeryReduceBounded_cast (modulus := modulus) x.val.toUInt64 (by + unfold toField toNat toCanonicalUInt32 + have hred := montgomeryReduce_cast (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] nlinarith [x.property, P.modulus_pos]) - change ((montgomeryReduceBoundedRaw modulus x.val.toUInt64).toNat : ZMod modulus) = + change ((montgomeryReduceRaw modulus x.val.toUInt64).toNat : ZMod modulus) = (x.val.toUInt64.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((montgomeryReduceBoundedRaw modulus x.val.toUInt64).toNat : ZMod modulus) = + change ((montgomeryReduceRaw modulus x.val.toUInt64).toNat : ZMod modulus) = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ rw [hred] rw [UInt32.toNat_toUInt64] @@ -379,13 +329,13 @@ theorem raw_cast_eq_toField_mul (x : FastField modulus) : theorem nat_eq_of_field_eq {a b : ℕ} (ha : a < modulus) (hb : b < modulus) (h : (a : ZMod modulus) = (b : ZMod modulus)) : a = b := - Montgomery.natCast_inj_of_lt h ha hb + natCast_inj_of_lt h ha hb theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < modulus) : ((ofCanonicalNat (modulus := modulus) n h).val.toNat : ZMod modulus) = (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by unfold ofCanonicalNat - have hred := montgomeryReduceBounded_cast (modulus := modulus) + have hred := montgomeryReduce_cast (modulus := modulus) (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] have hnmod : n % 2 ^ 64 = n := by @@ -396,11 +346,11 @@ theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < modulus) : nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_modulus]) - change ((montgomeryReduceBoundedRaw modulus + change ((montgomeryReduceRaw modulus (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((montgomeryReduceBoundedRaw modulus + change ((montgomeryReduceRaw modulus (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) rw [hred] @@ -433,7 +383,7 @@ theorem reduceUInt64_raw_cast (x : UInt64) : (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by unfold reduceUInt64 let y := x % P.modulus64 - have hred := montgomeryReduceBounded_cast (modulus := modulus) + have hred := montgomeryReduce_cast (modulus := modulus) (y * P.r2ModModulus.toUInt64) (by rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hy_lt : y.toNat < modulus := by @@ -443,11 +393,11 @@ theorem reduceUInt64_raw_cast (x : UInt64) : nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_modulus]) - change ((montgomeryReduceBoundedRaw modulus (y * P.r2ModModulus.toUInt64)).toNat : + change ((montgomeryReduceRaw modulus (y * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = ((y * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((montgomeryReduceBoundedRaw modulus (y * P.r2ModModulus.toUInt64)).toNat : + change ((montgomeryReduceRaw modulus (y * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) rw [hred] @@ -559,7 +509,7 @@ def sub (x y : FastField modulus) : FastField modulus := /-- Fast modular multiplication in Montgomery form. -/ @[inline] def mul (x y : FastField modulus) : FastField modulus := - montgomeryReduceBounded (x.val.toUInt64 * y.val.toUInt64) (by + montgomeryReduce (x.val.toUInt64 * y.val.toUInt64) (by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] @@ -808,18 +758,18 @@ theorem toField_neg (x : FastField modulus) : toField (-x) = -toField x := by theorem toField_mul (x y : FastField modulus) : toField (x * y) = toField x * toField y := by rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] unfold instMul mul - have hred := montgomeryReduceBounded_cast (modulus := modulus) + have hred := montgomeryReduce_cast (modulus := modulus) (x.val.toUInt64 * y.val.toUInt64) (by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [x.property, y.property, P.modulus_lt_two_pow_32, P.modulus_pos]) - change ((montgomeryReduceBoundedRaw modulus + change ((montgomeryReduceRaw modulus (x.val.toUInt64 * y.val.toUInt64)).toNat : ZMod modulus) = ((x.val.toUInt64 * y.val.toUInt64).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((montgomeryReduceBoundedRaw modulus + change ((montgomeryReduceRaw modulus (x.val.toUInt64 * y.val.toUInt64)).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ * diff --git a/tests/CompPolyTests/Fields/BabyBear/Fast.lean b/tests/CompPolyTests/Fields/BabyBear/Fast.lean index 02b25071..090a3400 100644 --- a/tests/CompPolyTests/Fields/BabyBear/Fast.lean +++ b/tests/CompPolyTests/Fields/BabyBear/Fast.lean @@ -14,8 +14,9 @@ Regression checks for the executable Montgomery representation. namespace BabyBear.Fast -#guard raw (0 : Field) = 0 -#guard raw (1 : Field) = rModModulus +#guard (0 : Field).val = 0 +#guard (1 : Field).val = + Montgomery.Native32.Mont32Field.rModModulus (modulus := BabyBear.fieldSize) #guard toCanonicalUInt32 (ofNat 37) = (37 : UInt32) #guard toNat (ofNat BabyBear.fieldSize) = 0 #guard toNat (ofNat (BabyBear.fieldSize + 37)) = 37 diff --git a/tests/CompPolyTests/Fields/KoalaBear/Fast.lean b/tests/CompPolyTests/Fields/KoalaBear/Fast.lean index 5629a825..1df3d965 100644 --- a/tests/CompPolyTests/Fields/KoalaBear/Fast.lean +++ b/tests/CompPolyTests/Fields/KoalaBear/Fast.lean @@ -14,8 +14,9 @@ Regression checks for the executable Montgomery representation. namespace KoalaBear.Fast -#guard raw (0 : Field) = 0 -#guard raw (1 : Field) = rModModulus +#guard (0 : Field).val = 0 +#guard (1 : Field).val = + Montgomery.Native32.Mont32Field.rModModulus (modulus := KoalaBear.fieldSize) #guard toCanonicalUInt32 (ofNat 37) = (37 : UInt32) #guard toNat (ofNat KoalaBear.fieldSize) = 0 #guard toNat (ofNat (KoalaBear.fieldSize + 37)) = 37 From 12d8f4395057f525bd2fa54414bc7d9d773bb268 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 17:47:44 +0200 Subject: [PATCH 17/31] refactor(fields): remove concrete Montgomery shims --- CompPoly.lean | 6 +- CompPoly/Fields/BabyBear/Fast.lean | 2 +- CompPoly/Fields/BabyBear/Fast/Convert.lean | 2 +- .../BabyBear/Fast/{Prelude.lean => Defs.lean} | 0 CompPoly/Fields/BabyBear/Fast/Montgomery.lean | 68 ------------------- CompPoly/Fields/KoalaBear/Fast.lean | 2 +- CompPoly/Fields/KoalaBear/Fast/Convert.lean | 2 +- .../Fast/{Prelude.lean => Defs.lean} | 3 +- .../Fields/KoalaBear/Fast/Montgomery.lean | 68 ------------------- 9 files changed, 7 insertions(+), 146 deletions(-) rename CompPoly/Fields/BabyBear/Fast/{Prelude.lean => Defs.lean} (100%) delete mode 100644 CompPoly/Fields/BabyBear/Fast/Montgomery.lean rename CompPoly/Fields/KoalaBear/Fast/{Prelude.lean => Defs.lean} (87%) delete mode 100644 CompPoly/Fields/KoalaBear/Fast/Montgomery.lean diff --git a/CompPoly.lean b/CompPoly.lean index 61e7a4cd..8096bb04 100644 --- a/CompPoly.lean +++ b/CompPoly.lean @@ -69,8 +69,7 @@ import CompPoly.Fields.BabyBear import CompPoly.Fields.BabyBear.Basic import CompPoly.Fields.BabyBear.Fast import CompPoly.Fields.BabyBear.Fast.Convert -import CompPoly.Fields.BabyBear.Fast.Montgomery -import CompPoly.Fields.BabyBear.Fast.Prelude +import CompPoly.Fields.BabyBear.Fast.Defs import CompPoly.Fields.Basic import CompPoly.Fields.Binary.AdditiveNTT.AdditiveNTT import CompPoly.Fields.Binary.AdditiveNTT.Algorithm @@ -108,8 +107,7 @@ import CompPoly.Fields.KoalaBear import CompPoly.Fields.KoalaBear.Basic import CompPoly.Fields.KoalaBear.Fast import CompPoly.Fields.KoalaBear.Fast.Convert -import CompPoly.Fields.KoalaBear.Fast.Montgomery -import CompPoly.Fields.KoalaBear.Fast.Prelude +import CompPoly.Fields.KoalaBear.Fast.Defs import CompPoly.Fields.Mersenne import CompPoly.Fields.Montgomery.Basic import CompPoly.Fields.Montgomery.Native32 diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index ea05e891..262d46cd 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -16,7 +16,7 @@ A native-word implementation of BabyBear arithmetic as a sidecar to the canonica The operations, their `Field`/`CommRing`/`NonBinaryField` instances, the `toField` bridge, and all correctness theorems are shared across every fast 32-bit-word field; they live once in `CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance -in `CompPoly.Fields.BabyBear.Fast.Prelude`. Because +in `CompPoly.Fields.BabyBear.Fast.Defs`. Because `Field := Native32.FastField BabyBear.fieldSize`, the generic algebraic instances resolve here automatically. This module re-exports the named operations and `simp` lemmas at the BabyBear instance. diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean index 1f9602c4..41adadd3 100644 --- a/CompPoly/Fields/BabyBear/Fast/Convert.lean +++ b/CompPoly/Fields/BabyBear/Fast/Convert.lean @@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Valerii Huhnin, Georgios Raikos -/ -import CompPoly.Fields.BabyBear.Fast.Montgomery +import CompPoly.Fields.BabyBear.Fast.Defs /-! # Fast BabyBear Field — Conversions diff --git a/CompPoly/Fields/BabyBear/Fast/Prelude.lean b/CompPoly/Fields/BabyBear/Fast/Defs.lean similarity index 100% rename from CompPoly/Fields/BabyBear/Fast/Prelude.lean rename to CompPoly/Fields/BabyBear/Fast/Defs.lean diff --git a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean b/CompPoly/Fields/BabyBear/Fast/Montgomery.lean deleted file mode 100644 index 804bc8b9..00000000 --- a/CompPoly/Fields/BabyBear/Fast/Montgomery.lean +++ /dev/null @@ -1,68 +0,0 @@ -/- -Copyright (c) 2026 CompPoly Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin, Georgios Raikos --/ - -import CompPoly.Fields.BabyBear.Fast.Prelude - -/-! -# Fast BabyBear Field — Montgomery Reduction - -The native-word Montgomery reducers specialized to BabyBear. The definitions and their -correctness proofs are shared across every fast 32-bit-word field; they live once in -`CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance -supplied in `CompPoly.Fields.BabyBear.Fast.Prelude`. This module just re-exports them at -the BabyBear instance. Because the shared definitions are `@[inline]`, fixing the instance -folds the constants to literals: the compiled code is identical to a hand-written -monomorphic reducer, with no `Mont32Field` dictionary at runtime. --/ - -namespace BabyBear -namespace Fast - -open Montgomery.Native32 - -/-- Reduce a native word known to be below twice the BabyBear prime. -/ -@[inline] -def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := - Montgomery.Native32.reduceUInt32Lt2ModulusRaw BabyBear.fieldSize x - -theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) - (h : x.toNat < 2 * BabyBear.fieldSize) : - (reduceUInt32Lt2ModulusRaw x).toNat < BabyBear.fieldSize := - Montgomery.Native32.reduceUInt32Lt2ModulusRaw_lt x h - -/-- Reduce a native word known to be below twice the BabyBear prime. -/ -@[inline] -def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * BabyBear.fieldSize) : Field := - Montgomery.Native32.reduceUInt32Lt2Modulus x h - -theorem reduceUInt32Lt2Modulus_cast (x : UInt32) - (h : x.toNat < 2 * BabyBear.fieldSize) : - ((reduceUInt32Lt2Modulus x h).val.toNat : BabyBear.Field) = (x.toNat : BabyBear.Field) := - Montgomery.Native32.reduceUInt32Lt2Modulus_cast x h - -/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ -@[inline] -def montgomeryReduceRaw (x : UInt64) : UInt32 := - Montgomery.Native32.montgomeryReduceRaw BabyBear.fieldSize x - -theorem montgomeryReduceRaw_lt (x : UInt64) - (h : x.toNat < BabyBear.fieldSize * UInt32.size) : - (montgomeryReduceRaw x).toNat < BabyBear.fieldSize := - Montgomery.Native32.montgomeryReduceRaw_lt x h - -/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ -@[inline] -def montgomeryReduce (x : UInt64) (h : x.toNat < BabyBear.fieldSize * UInt32.size) : Field := - Montgomery.Native32.montgomeryReduce x h - -theorem montgomeryReduce_cast (x : UInt64) - (h : x.toNat < BabyBear.fieldSize * UInt32.size) : - ((montgomeryReduce x h).val.toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := - Montgomery.Native32.montgomeryReduce_cast x h - -end Fast -end BabyBear diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index 454d0b33..80a65916 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -16,7 +16,7 @@ A native-word implementation of KoalaBear arithmetic as a sidecar to the canonic The operations, their `Field`/`CommRing`/`NonBinaryField` instances, the `toField` bridge, and all correctness theorems are shared across every fast 32-bit-word field; they live once in `CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance -in `CompPoly.Fields.KoalaBear.Fast.Prelude`. Because +in `CompPoly.Fields.KoalaBear.Fast.Defs`. Because `Field := Native32.FastField KoalaBear.fieldSize`, the generic algebraic instances resolve here automatically. This module re-exports the named operations and `simp` lemmas at the KoalaBear instance. diff --git a/CompPoly/Fields/KoalaBear/Fast/Convert.lean b/CompPoly/Fields/KoalaBear/Fast/Convert.lean index 8afd9595..224974c7 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Convert.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Convert.lean @@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Valerii Huhnin -/ -import CompPoly.Fields.KoalaBear.Fast.Montgomery +import CompPoly.Fields.KoalaBear.Fast.Defs /-! # Fast KoalaBear Field — Conversions diff --git a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean b/CompPoly/Fields/KoalaBear/Fast/Defs.lean similarity index 87% rename from CompPoly/Fields/KoalaBear/Fast/Prelude.lean rename to CompPoly/Fields/KoalaBear/Fast/Defs.lean index 631f2370..48ddfda6 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Prelude.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Defs.lean @@ -12,8 +12,7 @@ import CompPoly.Fields.Montgomery.Native32Field namespace KoalaBear.Fast open Montgomery.Native32 (Mont32Field FastField) -/-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. The five -word constants are the only runtime data; every other field is a `decide`-checked fact. -/ +/-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. -/ instance instMont32Field : Mont32Field KoalaBear.fieldSize where prime := KoalaBear.is_prime modulus32 := 0x7F000001 diff --git a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean b/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean deleted file mode 100644 index 8753b318..00000000 --- a/CompPoly/Fields/KoalaBear/Fast/Montgomery.lean +++ /dev/null @@ -1,68 +0,0 @@ -/- -Copyright (c) 2026 CompPoly Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin --/ - -import CompPoly.Fields.KoalaBear.Fast.Prelude - -/-! -# Fast KoalaBear Field — Montgomery Reduction - -The native-word Montgomery reducers specialized to KoalaBear. The definitions and their -correctness proofs are shared across every fast 32-bit-word field; they live once in -`CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance -supplied in `CompPoly.Fields.KoalaBear.Fast.Prelude`. This module just re-exports them at -the KoalaBear instance. Because the shared definitions are `@[inline]`, fixing the instance -folds the constants to literals: the compiled code is identical to a hand-written -monomorphic reducer, with no `Mont32Field` dictionary at runtime. --/ - -namespace KoalaBear -namespace Fast - -open Montgomery.Native32 - -/-- Reduce a native word known to be below twice the KoalaBear prime. -/ -@[inline] -def reduceUInt32Lt2ModulusRaw (x : UInt32) : UInt32 := - Montgomery.Native32.reduceUInt32Lt2ModulusRaw KoalaBear.fieldSize x - -theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) - (h : x.toNat < 2 * KoalaBear.fieldSize) : - (reduceUInt32Lt2ModulusRaw x).toNat < KoalaBear.fieldSize := - Montgomery.Native32.reduceUInt32Lt2ModulusRaw_lt x h - -/-- Reduce a native word known to be below twice the KoalaBear prime. -/ -@[inline] -def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * KoalaBear.fieldSize) : Field := - Montgomery.Native32.reduceUInt32Lt2Modulus x h - -theorem reduceUInt32Lt2Modulus_cast (x : UInt32) - (h : x.toNat < 2 * KoalaBear.fieldSize) : - ((reduceUInt32Lt2Modulus x h).val.toNat : KoalaBear.Field) = (x.toNat : KoalaBear.Field) := - Montgomery.Native32.reduceUInt32Lt2Modulus_cast x h - -/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ -@[inline] -def montgomeryReduceRaw (x : UInt64) : UInt32 := - Montgomery.Native32.montgomeryReduceRaw KoalaBear.fieldSize x - -theorem montgomeryReduceRaw_lt (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - (montgomeryReduceRaw x).toNat < KoalaBear.fieldSize := - Montgomery.Native32.montgomeryReduceRaw_lt x h - -/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ -@[inline] -def montgomeryReduce (x : UInt64) (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : Field := - Montgomery.Native32.montgomeryReduce x h - -theorem montgomeryReduce_cast (x : UInt64) - (h : x.toNat < KoalaBear.fieldSize * UInt32.size) : - ((montgomeryReduce x h).val.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := - Montgomery.Native32.montgomeryReduce_cast x h - -end Fast -end KoalaBear From f2434fc91abc1c8193d7fa3e38d2425708ad61b9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 21:34:23 +0200 Subject: [PATCH 18/31] remove redundant conversion definitions --- .../Root/FieldRoots/KoalaBear.lean | 3 +- CompPoly/Fields/BabyBear/Fast.lean | 2 +- CompPoly/Fields/BabyBear/Fast/Convert.lean | 49 +++---------------- CompPoly/Fields/KoalaBear/Fast.lean | 1 + CompPoly/Fields/KoalaBear/Fast/Convert.lean | 49 +++---------------- CompPoly/Fields/Montgomery/Native32Field.lean | 31 +++++++----- CompPoly/Univariate/NTT/KoalaBear.lean | 5 +- tests/CompPolyTests/Fields/BabyBear/Fast.lean | 42 ++++++++-------- .../CompPolyTests/Fields/KoalaBear/Fast.lean | 40 +++++++-------- 9 files changed, 83 insertions(+), 139 deletions(-) diff --git a/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean b/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean index 867c2885..2eb5bdf5 100644 --- a/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean +++ b/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean @@ -125,8 +125,7 @@ def fastKoalaBearFiniteFieldContext : intro a apply KoalaBear.Fast.toField_injective rw [KoalaBear.Fast.toField_npow] - simpa [KoalaBear.Field, KoalaBear.fieldSize] using - ZMod.pow_card (KoalaBear.Fast.toField a) + simp [KoalaBear.fieldSize] /-- Primitive generator transported to native-word fast KoalaBear. -/ def fastKoalaBearPrimitiveRoot : KoalaBear.Fast.Field := diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index 262d46cd..8f48a9d8 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -25,7 +25,7 @@ BabyBear instance. namespace BabyBear namespace Fast -open Montgomery.Native32 +open Montgomery.Native32.FastField /-- Fast modular addition in Montgomery form. -/ @[inline] diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean index 41adadd3..7eb756bc 100644 --- a/CompPoly/Fields/BabyBear/Fast/Convert.lean +++ b/CompPoly/Fields/BabyBear/Fast/Convert.lean @@ -17,8 +17,6 @@ Conversions between the fast Montgomery representation and the canonical namespace BabyBear namespace Fast -open Montgomery.Native32 - /-- Build a fast element from a canonical natural representative. -/ @[inline] def ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : Field := @@ -29,56 +27,25 @@ def ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : Field := def reduceUInt64 (x : UInt64) : Field := Montgomery.Native32.reduceUInt64 BabyBear.fieldSize x -/-- The zero fast BabyBear element. -/ -def zero : Field := Montgomery.Native32.zero BabyBear.fieldSize - -/-- The one fast BabyBear element. -/ -def one : Field := Montgomery.Native32.one BabyBear.fieldSize - -/-- Convert a natural number into fast Montgomery representation. -/ -@[inline] -def ofNat (n : Nat) : Field := - Montgomery.Native32.ofNat BabyBear.fieldSize n - /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (x : UInt32) : Field := - Montgomery.Native32.ofUInt32 BabyBear.fieldSize x + Montgomery.Native32.FastField.ofUInt32 BabyBear.fieldSize x /-- Convert from the canonical `ZMod` BabyBear field into fast Montgomery form. -/ @[inline] def ofField (x : BabyBear.Field) : Field := Montgomery.Native32.ofField x -/-- Convert an integer into fast Montgomery representation. -/ -@[inline] -def ofInt (n : Int) : Field := - Montgomery.Native32.ofInt BabyBear.fieldSize n - -/-- Convert a fast element to its canonical native-word representative. -/ -@[inline] -def toCanonicalUInt32 (x : Field) : UInt32 := - Montgomery.Native32.toCanonicalUInt32 x - -/-- Convert a fast BabyBear element to its canonical natural representative. -/ -@[inline] -def toNat (x : Field) : Nat := - Montgomery.Native32.toNat x - -/-- Convert a fast BabyBear element to the canonical `ZMod` BabyBear field. -/ -@[inline] -def toField (x : Field) : BabyBear.Field := - Montgomery.Native32.toField x - -theorem toNat_lt_fieldSize (x : Field) : toNat x < BabyBear.fieldSize := +theorem toNat_lt_fieldSize (x : Field) : x.toNat < BabyBear.fieldSize := Montgomery.Native32.toNat_lt_modulus x theorem toField_eq_raw_mul_inv (x : Field) : - toField x = (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := + x.toField = (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := Montgomery.Native32.toField_eq_raw_mul_inv x theorem raw_cast_eq_toField_mul (x : Field) : - (x.val.toNat : BabyBear.Field) = toField x * (UInt32.size : BabyBear.Field) := + (x.val.toNat : BabyBear.Field) = x.toField * (UInt32.size : BabyBear.Field) := Montgomery.Native32.raw_cast_eq_toField_mul x theorem nat_eq_of_field_eq {a b : Nat} (ha : a < BabyBear.fieldSize) @@ -99,25 +66,25 @@ theorem reduceUInt64_raw_cast (x : UInt64) : /-- Converting a canonical natural representative to fast form preserves its value. -/ @[simp] theorem toNat_ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : - toNat (ofCanonicalNat n h) = n := + (ofCanonicalNat n h).toNat = n := Montgomery.Native32.toNat_ofCanonicalNat n h /-- `ofCanonicalNat` embeds a canonical representative into the canonical field. -/ @[simp] theorem toField_ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : - toField (ofCanonicalNat n h) = (n : BabyBear.Field) := + (ofCanonicalNat n h).toField = (n : BabyBear.Field) := Montgomery.Native32.toField_ofCanonicalNat n h /-- Reducing a `UInt64` gives the canonical natural residue modulo BabyBear. -/ @[simp] theorem toNat_reduceUInt64 (x : UInt64) : - toNat (reduceUInt64 x) = x.toNat % BabyBear.fieldSize := + (reduceUInt64 x).toNat = x.toNat % BabyBear.fieldSize := Montgomery.Native32.toNat_reduceUInt64 x /-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ @[simp] theorem toField_reduceUInt64 (x : UInt64) : - toField (reduceUInt64 x) = (x.toNat : BabyBear.Field) := + (reduceUInt64 x).toField = (x.toNat : BabyBear.Field) := Montgomery.Native32.toField_reduceUInt64 x end Fast diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index 80a65916..7b1f3e5d 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -26,6 +26,7 @@ namespace KoalaBear namespace Fast open Montgomery.Native32 +open Montgomery.Native32.FastField /-- Fast modular addition in Montgomery form. -/ @[inline] diff --git a/CompPoly/Fields/KoalaBear/Fast/Convert.lean b/CompPoly/Fields/KoalaBear/Fast/Convert.lean index 224974c7..bee4e93d 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Convert.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Convert.lean @@ -17,8 +17,6 @@ Conversions between the fast Montgomery representation and the canonical namespace KoalaBear namespace Fast -open Montgomery.Native32 - /-- Build a fast element from a canonical natural representative. -/ @[inline] def ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : Field := @@ -29,56 +27,25 @@ def ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : Field := def reduceUInt64 (x : UInt64) : Field := Montgomery.Native32.reduceUInt64 KoalaBear.fieldSize x -/-- The zero fast KoalaBear element. -/ -def zero : Field := Montgomery.Native32.zero KoalaBear.fieldSize - -/-- The one fast KoalaBear element. -/ -def one : Field := Montgomery.Native32.one KoalaBear.fieldSize - -/-- Convert a natural number into fast Montgomery representation. -/ -@[inline] -def ofNat (n : Nat) : Field := - Montgomery.Native32.ofNat KoalaBear.fieldSize n - /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (x : UInt32) : Field := - Montgomery.Native32.ofUInt32 KoalaBear.fieldSize x + Montgomery.Native32.FastField.ofUInt32 KoalaBear.fieldSize x /-- Convert from the canonical `ZMod` KoalaBear field into fast Montgomery form. -/ @[inline] def ofField (x : KoalaBear.Field) : Field := Montgomery.Native32.ofField x -/-- Convert an integer into fast Montgomery representation. -/ -@[inline] -def ofInt (n : Int) : Field := - Montgomery.Native32.ofInt KoalaBear.fieldSize n - -/-- Convert a fast element to its canonical native-word representative. -/ -@[inline] -def toCanonicalUInt32 (x : Field) : UInt32 := - Montgomery.Native32.toCanonicalUInt32 x - -/-- Convert a fast KoalaBear element to its canonical natural representative. -/ -@[inline] -def toNat (x : Field) : Nat := - Montgomery.Native32.toNat x - -/-- Convert a fast KoalaBear element to the canonical `ZMod` KoalaBear field. -/ -@[inline] -def toField (x : Field) : KoalaBear.Field := - Montgomery.Native32.toField x - -theorem toNat_lt_fieldSize (x : Field) : toNat x < KoalaBear.fieldSize := +theorem toNat_lt_fieldSize (x : Field) : x.toNat < KoalaBear.fieldSize := Montgomery.Native32.toNat_lt_modulus x theorem toField_eq_raw_mul_inv (x : Field) : - toField x = (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := + x.toField = (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := Montgomery.Native32.toField_eq_raw_mul_inv x theorem raw_cast_eq_toField_mul (x : Field) : - (x.val.toNat : KoalaBear.Field) = toField x * (UInt32.size : KoalaBear.Field) := + (x.val.toNat : KoalaBear.Field) = x.toField * (UInt32.size : KoalaBear.Field) := Montgomery.Native32.raw_cast_eq_toField_mul x theorem nat_eq_of_field_eq {a b : Nat} (ha : a < KoalaBear.fieldSize) @@ -99,25 +66,25 @@ theorem reduceUInt64_raw_cast (x : UInt64) : /-- Converting a canonical natural representative to fast form preserves its value. -/ @[simp] theorem toNat_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : - toNat (ofCanonicalNat n h) = n := + (ofCanonicalNat n h).toNat = n := Montgomery.Native32.toNat_ofCanonicalNat n h /-- `ofCanonicalNat` embeds a canonical representative into the canonical field. -/ @[simp] theorem toField_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : - toField (ofCanonicalNat n h) = (n : KoalaBear.Field) := + (ofCanonicalNat n h).toField = (n : KoalaBear.Field) := Montgomery.Native32.toField_ofCanonicalNat n h /-- Reducing a `UInt64` gives the canonical natural residue modulo KoalaBear. -/ @[simp] theorem toNat_reduceUInt64 (x : UInt64) : - toNat (reduceUInt64 x) = x.toNat % KoalaBear.fieldSize := + (reduceUInt64 x).toNat = x.toNat % KoalaBear.fieldSize := Montgomery.Native32.toNat_reduceUInt64 x /-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ @[simp] theorem toField_reduceUInt64 (x : UInt64) : - toField (reduceUInt64 x) = (x.toNat : KoalaBear.Field) := + (reduceUInt64 x).toField = (x.toNat : KoalaBear.Field) := Montgomery.Native32.toField_reduceUInt64 x end Fast diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index d09f0d00..e2d13853 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -249,26 +249,29 @@ def reduceUInt64 (modulus : ℕ) [P : Mont32Field modulus] nlinarith [P.r2ModModulus_lt_modulus]) /-- The zero fast element. -/ -def zero (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := ⟨0, by +private def zero (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := ⟨0, by have h0 : (0 : UInt32).toNat = 0 := by decide have hp := P.modulus_pos omega⟩ /-- The one fast element. -/ -def one (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := ⟨P.rModModulus, by +private def one (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := ⟨P.rModModulus, by rw [P.rModModulus_toNat] exact Nat.mod_lt _ P.modulus_pos⟩ /-- Convert a natural number into fast Montgomery representation. -/ @[inline] -def ofNat (modulus : ℕ) [P : Mont32Field modulus] (n : ℕ) : FastField modulus := +private def ofNat (modulus : ℕ) [P : Mont32Field modulus] (n : ℕ) : FastField modulus := ofCanonicalNat (n % modulus) (Nat.mod_lt _ P.modulus_pos) +namespace FastField + /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (modulus : ℕ) [P : Mont32Field modulus] (x : UInt32) : FastField modulus := reduceUInt64 modulus x.toUInt64 +end FastField /-- Convert from the canonical `ZMod` field into fast Montgomery form. -/ @[inline] @@ -277,28 +280,34 @@ def ofField (x : ZMod modulus) : FastField modulus := /-- Convert an integer into fast Montgomery representation. -/ @[inline] -def ofInt (modulus : ℕ) [P : Mont32Field modulus] (n : Int) : FastField modulus := +private def ofInt (modulus : ℕ) [P : Mont32Field modulus] (n : Int) : FastField modulus := ofField (n : ZMod modulus) +namespace FastField + /-- Convert a fast element to its canonical native-word representative. -/ @[inline] -def toCanonicalUInt32 (x : FastField modulus) : UInt32 := - (montgomeryReduce (modulus := modulus) x.val.toUInt64 (by +def toUInt32 (x : FastField modulus) : UInt32 := + montgomeryReduce (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.modulus_pos])).val + nlinarith [x.property, P.modulus_pos]) + |>.val /-- Convert a fast element to its canonical natural representative. -/ @[inline] def toNat (x : FastField modulus) : ℕ := - (toCanonicalUInt32 x).toNat + x.toUInt32.toNat /-- Convert a fast element to the canonical `ZMod` field. -/ @[inline] def toField (x : FastField modulus) : ZMod modulus := - (toNat x : ZMod modulus) + (x.toNat : ZMod modulus) +end FastField + +open FastField theorem toNat_lt_modulus (x : FastField modulus) : toNat x < modulus := by - unfold toNat toCanonicalUInt32 + unfold FastField.toNat FastField.toUInt32 change (montgomeryReduceRaw modulus x.val.toUInt64).toNat < modulus exact montgomeryReduceRaw_lt (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] @@ -307,7 +316,7 @@ theorem toNat_lt_modulus (x : FastField modulus) : toNat x < modulus := by theorem toField_eq_raw_mul_inv (x : FastField modulus) : toField x = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - unfold toField toNat toCanonicalUInt32 + unfold FastField.toField FastField.toNat FastField.toUInt32 have hred := montgomeryReduce_cast (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] nlinarith [x.property, P.modulus_pos]) diff --git a/CompPoly/Univariate/NTT/KoalaBear.lean b/CompPoly/Univariate/NTT/KoalaBear.lean index aeba091b..ace8bf57 100644 --- a/CompPoly/Univariate/NTT/KoalaBear.lean +++ b/CompPoly/Univariate/NTT/KoalaBear.lean @@ -76,9 +76,10 @@ theorem fast_twoPowNatCast_ne_zero exact twoPowNatCast_ne_zero logN hlogN (by calc (((2 ^ logN : Nat) : KoalaBear.Field)) = - KoalaBear.Fast.toField (((2 ^ logN : Nat) : KoalaBear.Fast.Field)) := by + (((2 ^ logN : Nat) : KoalaBear.Fast.Field)).toField := by rw [KoalaBear.Fast.toField_natCast] - _ = KoalaBear.Fast.toField 0 := congrArg KoalaBear.Fast.toField hzero + _ = (0 : KoalaBear.Fast.Field).toField := + congrArg (fun x : KoalaBear.Fast.Field => x.toField) hzero _ = 0 := KoalaBear.Fast.toField_zero) /-- Fast KoalaBear radix-2 NTT domain for a supported two-adic size. -/ diff --git a/tests/CompPolyTests/Fields/BabyBear/Fast.lean b/tests/CompPolyTests/Fields/BabyBear/Fast.lean index 090a3400..b1b8f63f 100644 --- a/tests/CompPolyTests/Fields/BabyBear/Fast.lean +++ b/tests/CompPolyTests/Fields/BabyBear/Fast.lean @@ -16,27 +16,27 @@ namespace BabyBear.Fast #guard (0 : Field).val = 0 #guard (1 : Field).val = - Montgomery.Native32.Mont32Field.rModModulus (modulus := BabyBear.fieldSize) -#guard toCanonicalUInt32 (ofNat 37) = (37 : UInt32) -#guard toNat (ofNat BabyBear.fieldSize) = 0 -#guard toNat (ofNat (BabyBear.fieldSize + 37)) = 37 -#guard toNat ((ofNat (BabyBear.fieldSize - 1)) + (2 : Field)) = 1 -#guard toNat ((ofNat (BabyBear.fieldSize - 1)) + (ofNat (BabyBear.fieldSize - 1))) = + Montgomery.Native32.Mont32Field.rModModulus BabyBear.fieldSize +#guard (37 : Field).toUInt32 = (37 : UInt32) +#guard (BabyBear.fieldSize : Field).toNat = 0 +#guard (BabyBear.fieldSize + 37 : Field).toNat = 37 +#guard ((BabyBear.fieldSize - 1 : Field) + 2).toNat = 1 +#guard ((BabyBear.fieldSize - 1 : Field) + (BabyBear.fieldSize - 1 : Field)).toNat = BabyBear.fieldSize - 2 -#guard toNat ((9 : Field) - (5 : Field)) = 4 -#guard toNat ((5 : Field) - (9 : Field)) = BabyBear.fieldSize - 4 -#guard toNat (-(0 : Field)) = 0 -#guard toNat (-(1 : Field)) = BabyBear.fieldSize - 1 -#guard toNat ((ofNat (BabyBear.fieldSize - 1)) * (ofNat (BabyBear.fieldSize - 1))) = 1 -#guard toNat (square (12345 : Field)) = 152399025 -#guard toNat ((37 : Field) ^ 0) = 1 -#guard toNat ((37 : Field) ^ 1) = 37 -#guard toField ((123456789 : Field) ^ 17) = ((123456789 : BabyBear.Field) ^ 17) -#guard toField ((123456789 : Field) ^ 255) = ((123456789 : BabyBear.Field) ^ 255) -#guard toNat ((0 : Field)⁻¹) = 0 -#guard toNat ((37 : Field)⁻¹ * (37 : Field)) = 1 -#guard toNat ((37 : Field) / (37 : Field)) = 1 -#guard toField ((37 : Field)⁻¹) = ((37 : BabyBear.Field)⁻¹) -#guard toField ((37 : Field) ^ (-3 : Int)) = ((37 : BabyBear.Field) ^ (-3 : Int)) +#guard ((9 : Field) - 5).toNat = 4 +#guard ((5 : Field) - 9).toNat = BabyBear.fieldSize - 4 +#guard (-(0 : Field)).toNat = 0 +#guard (-(1 : Field)).toNat = BabyBear.fieldSize - 1 +#guard ((BabyBear.fieldSize - 1 : Field) * (BabyBear.fieldSize - 1 : Field)).toNat = 1 +#guard (square (12345 : Field)).toNat = 152399025 +#guard ((37 : Field) ^ 0).toNat = 1 +#guard ((37 : Field) ^ 1).toNat = 37 +#guard ((123456789 : Field) ^ 17).toField = ((123456789 : BabyBear.Field) ^ 17) +#guard ((123456789 : Field) ^ 255).toField = ((123456789 : BabyBear.Field) ^ 255) +#guard ((0 : Field)⁻¹).toNat = 0 +#guard ((37 : Field)⁻¹ * 37).toNat = 1 +#guard ((37 : Field) / 37).toNat = 1 +#guard ((37 : Field)⁻¹).toField = ((37 : BabyBear.Field)⁻¹) +#guard ((37 : Field) ^ (-3 : Int)).toField = ((37 : BabyBear.Field) ^ (-3 : Int)) end BabyBear.Fast diff --git a/tests/CompPolyTests/Fields/KoalaBear/Fast.lean b/tests/CompPolyTests/Fields/KoalaBear/Fast.lean index 1df3d965..ee94cb4f 100644 --- a/tests/CompPolyTests/Fields/KoalaBear/Fast.lean +++ b/tests/CompPolyTests/Fields/KoalaBear/Fast.lean @@ -17,26 +17,26 @@ namespace KoalaBear.Fast #guard (0 : Field).val = 0 #guard (1 : Field).val = Montgomery.Native32.Mont32Field.rModModulus (modulus := KoalaBear.fieldSize) -#guard toCanonicalUInt32 (ofNat 37) = (37 : UInt32) -#guard toNat (ofNat KoalaBear.fieldSize) = 0 -#guard toNat (ofNat (KoalaBear.fieldSize + 37)) = 37 -#guard toNat ((ofNat (KoalaBear.fieldSize - 1)) + (2 : Field)) = 1 -#guard toNat ((ofNat (KoalaBear.fieldSize - 1)) + (ofNat (KoalaBear.fieldSize - 1))) = +#guard (37 : Field).toUInt32 = (37 : UInt32) +#guard (KoalaBear.fieldSize : Field).toNat = 0 +#guard (KoalaBear.fieldSize + 37 : Field).toNat = 37 +#guard ((KoalaBear.fieldSize - 1 : Field) + 2).toNat = 1 +#guard ((KoalaBear.fieldSize - 1 : Field) + (KoalaBear.fieldSize - 1 : Field)).toNat = KoalaBear.fieldSize - 2 -#guard toNat ((9 : Field) - (5 : Field)) = 4 -#guard toNat ((5 : Field) - (9 : Field)) = KoalaBear.fieldSize - 4 -#guard toNat (-(0 : Field)) = 0 -#guard toNat (-(1 : Field)) = KoalaBear.fieldSize - 1 -#guard toNat ((ofNat (KoalaBear.fieldSize - 1)) * (ofNat (KoalaBear.fieldSize - 1))) = 1 -#guard toNat (square (12345 : Field)) = 152399025 -#guard toNat ((37 : Field) ^ 0) = 1 -#guard toNat ((37 : Field) ^ 1) = 37 -#guard toField ((123456789 : Field) ^ 17) = ((123456789 : KoalaBear.Field) ^ 17) -#guard toField ((123456789 : Field) ^ 255) = ((123456789 : KoalaBear.Field) ^ 255) -#guard toNat ((0 : Field)⁻¹) = 0 -#guard toNat ((37 : Field)⁻¹ * (37 : Field)) = 1 -#guard toNat ((37 : Field) / (37 : Field)) = 1 -#guard toField ((37 : Field)⁻¹) = ((37 : KoalaBear.Field)⁻¹) -#guard toField ((37 : Field) ^ (-3 : Int)) = ((37 : KoalaBear.Field) ^ (-3 : Int)) +#guard ((9 : Field) - 5).toNat = 4 +#guard ((5 : Field) - 9).toNat = KoalaBear.fieldSize - 4 +#guard (-(0 : Field)).toNat = 0 +#guard (-(1 : Field)).toNat = KoalaBear.fieldSize - 1 +#guard ((KoalaBear.fieldSize - 1 : Field) * (KoalaBear.fieldSize - 1 : Field)).toNat = 1 +#guard (square (12345 : Field)).toNat = 152399025 +#guard ((37 : Field) ^ 0).toNat = 1 +#guard ((37 : Field) ^ 1).toNat = 37 +#guard ((123456789 : Field) ^ 17).toField = ((123456789 : KoalaBear.Field) ^ 17) +#guard ((123456789 : Field) ^ 255).toField = ((123456789 : KoalaBear.Field) ^ 255) +#guard ((0 : Field)⁻¹).toNat = 0 +#guard ((37 : Field)⁻¹ * 37).toNat = 1 +#guard ((37 : Field) / 37).toNat = 1 +#guard ((37 : Field)⁻¹).toField = ((37 : KoalaBear.Field)⁻¹) +#guard ((37 : Field) ^ (-3 : Int)).toField = ((37 : KoalaBear.Field) ^ (-3 : Int)) end KoalaBear.Fast From 25e3f4fcc46744cc7fed711763a9a0f3a756505a Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 21:41:24 +0200 Subject: [PATCH 19/31] refactor(fields): remove redundant theorem reexports --- .../Root/FieldRoots/KoalaBear.lean | 2 +- CompPoly/Fields/BabyBear/Fast.lean | 4 -- CompPoly/Fields/BabyBear/Fast/Convert.lean | 43 ------------------- CompPoly/Fields/KoalaBear/Fast.lean | 4 -- CompPoly/Fields/KoalaBear/Fast/Convert.lean | 43 ------------------- 5 files changed, 1 insertion(+), 95 deletions(-) diff --git a/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean b/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean index 2eb5bdf5..d07fb8fe 100644 --- a/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean +++ b/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean @@ -123,7 +123,7 @@ def fastKoalaBearFiniteFieldContext : simp [KoalaBear.Field, Nat.card_eq_fintype_card, ZMod.card] frobenius_fixed := by intro a - apply KoalaBear.Fast.toField_injective + apply Montgomery.Native32.toField_injective rw [KoalaBear.Fast.toField_npow] simp [KoalaBear.fieldSize] diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index 8f48a9d8..f7795038 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -76,10 +76,6 @@ theorem toField_ofField (x : BabyBear.Field) : toField (ofField x) = x := theorem ofField_toField (x : Field) : ofField (toField x) = x := Montgomery.Native32.ofField_toField x -/-- The canonical-field interpretation distinguishes fast BabyBear values. -/ -theorem toField_injective : Function.Injective (toField : Field → BabyBear.Field) := - Montgomery.Native32.toField_injective - /-- `toField` maps fast zero to canonical zero. -/ @[simp] theorem toField_zero : toField (0 : Field) = 0 := diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean index 7eb756bc..7d8986e7 100644 --- a/CompPoly/Fields/BabyBear/Fast/Convert.lean +++ b/CompPoly/Fields/BabyBear/Fast/Convert.lean @@ -17,11 +17,6 @@ Conversions between the fast Montgomery representation and the canonical namespace BabyBear namespace Fast -/-- Build a fast element from a canonical natural representative. -/ -@[inline] -def ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : Field := - Montgomery.Native32.ofCanonicalNat n h - /-- Reduce a `UInt64` modulo the BabyBear prime and return a Montgomery fast element. -/ @[inline] def reduceUInt64 (x : UInt64) : Field := @@ -37,44 +32,6 @@ def ofUInt32 (x : UInt32) : Field := def ofField (x : BabyBear.Field) : Field := Montgomery.Native32.ofField x -theorem toNat_lt_fieldSize (x : Field) : x.toNat < BabyBear.fieldSize := - Montgomery.Native32.toNat_lt_modulus x - -theorem toField_eq_raw_mul_inv (x : Field) : - x.toField = (x.val.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field)⁻¹ := - Montgomery.Native32.toField_eq_raw_mul_inv x - -theorem raw_cast_eq_toField_mul (x : Field) : - (x.val.toNat : BabyBear.Field) = x.toField * (UInt32.size : BabyBear.Field) := - Montgomery.Native32.raw_cast_eq_toField_mul x - -theorem nat_eq_of_field_eq {a b : Nat} (ha : a < BabyBear.fieldSize) - (hb : b < BabyBear.fieldSize) (h : (a : BabyBear.Field) = (b : BabyBear.Field)) : - a = b := - Montgomery.Native32.nat_eq_of_field_eq ha hb h - -theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < BabyBear.fieldSize) : - ((ofCanonicalNat n h).val.toNat : BabyBear.Field) = - (n : BabyBear.Field) * (UInt32.size : BabyBear.Field) := - Montgomery.Native32.ofCanonicalNat_raw_cast n h - -theorem reduceUInt64_raw_cast (x : UInt64) : - ((reduceUInt64 x).val.toNat : BabyBear.Field) = - (x.toNat : BabyBear.Field) * (UInt32.size : BabyBear.Field) := - Montgomery.Native32.reduceUInt64_raw_cast x - -/-- Converting a canonical natural representative to fast form preserves its value. -/ -@[simp] -theorem toNat_ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : - (ofCanonicalNat n h).toNat = n := - Montgomery.Native32.toNat_ofCanonicalNat n h - -/-- `ofCanonicalNat` embeds a canonical representative into the canonical field. -/ -@[simp] -theorem toField_ofCanonicalNat (n : Nat) (h : n < BabyBear.fieldSize) : - (ofCanonicalNat n h).toField = (n : BabyBear.Field) := - Montgomery.Native32.toField_ofCanonicalNat n h - /-- Reducing a `UInt64` gives the canonical natural residue modulo BabyBear. -/ @[simp] theorem toNat_reduceUInt64 (x : UInt64) : diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index 7b1f3e5d..b394d9a0 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -77,10 +77,6 @@ theorem toField_ofField (x : KoalaBear.Field) : toField (ofField x) = x := theorem ofField_toField (x : Field) : ofField (toField x) = x := Montgomery.Native32.ofField_toField x -/-- The canonical-field interpretation distinguishes fast KoalaBear values. -/ -theorem toField_injective : Function.Injective (toField : Field → KoalaBear.Field) := - Montgomery.Native32.toField_injective - /-- `toField` maps fast zero to canonical zero. -/ @[simp] theorem toField_zero : toField (0 : Field) = 0 := diff --git a/CompPoly/Fields/KoalaBear/Fast/Convert.lean b/CompPoly/Fields/KoalaBear/Fast/Convert.lean index bee4e93d..4942c23c 100644 --- a/CompPoly/Fields/KoalaBear/Fast/Convert.lean +++ b/CompPoly/Fields/KoalaBear/Fast/Convert.lean @@ -17,11 +17,6 @@ Conversions between the fast Montgomery representation and the canonical namespace KoalaBear namespace Fast -/-- Build a fast element from a canonical natural representative. -/ -@[inline] -def ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : Field := - Montgomery.Native32.ofCanonicalNat n h - /-- Reduce a `UInt64` modulo the KoalaBear prime and return a Montgomery fast element. -/ @[inline] def reduceUInt64 (x : UInt64) : Field := @@ -37,44 +32,6 @@ def ofUInt32 (x : UInt32) : Field := def ofField (x : KoalaBear.Field) : Field := Montgomery.Native32.ofField x -theorem toNat_lt_fieldSize (x : Field) : x.toNat < KoalaBear.fieldSize := - Montgomery.Native32.toNat_lt_modulus x - -theorem toField_eq_raw_mul_inv (x : Field) : - x.toField = (x.val.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field)⁻¹ := - Montgomery.Native32.toField_eq_raw_mul_inv x - -theorem raw_cast_eq_toField_mul (x : Field) : - (x.val.toNat : KoalaBear.Field) = x.toField * (UInt32.size : KoalaBear.Field) := - Montgomery.Native32.raw_cast_eq_toField_mul x - -theorem nat_eq_of_field_eq {a b : Nat} (ha : a < KoalaBear.fieldSize) - (hb : b < KoalaBear.fieldSize) (h : (a : KoalaBear.Field) = (b : KoalaBear.Field)) : - a = b := - Montgomery.Native32.nat_eq_of_field_eq ha hb h - -theorem ofCanonicalNat_raw_cast (n : Nat) (h : n < KoalaBear.fieldSize) : - ((ofCanonicalNat n h).val.toNat : KoalaBear.Field) = - (n : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := - Montgomery.Native32.ofCanonicalNat_raw_cast n h - -theorem reduceUInt64_raw_cast (x : UInt64) : - ((reduceUInt64 x).val.toNat : KoalaBear.Field) = - (x.toNat : KoalaBear.Field) * (UInt32.size : KoalaBear.Field) := - Montgomery.Native32.reduceUInt64_raw_cast x - -/-- Converting a canonical natural representative to fast form preserves its value. -/ -@[simp] -theorem toNat_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : - (ofCanonicalNat n h).toNat = n := - Montgomery.Native32.toNat_ofCanonicalNat n h - -/-- `ofCanonicalNat` embeds a canonical representative into the canonical field. -/ -@[simp] -theorem toField_ofCanonicalNat (n : Nat) (h : n < KoalaBear.fieldSize) : - (ofCanonicalNat n h).toField = (n : KoalaBear.Field) := - Montgomery.Native32.toField_ofCanonicalNat n h - /-- Reducing a `UInt64` gives the canonical natural residue modulo KoalaBear. -/ @[simp] theorem toNat_reduceUInt64 (x : UInt64) : From 0dd17ae903a51a9f99be8f838221a26e3af359e3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 13 Jul 2026 21:45:27 +0200 Subject: [PATCH 20/31] refactor(fields): collapse concrete fast field modules --- CompPoly.lean | 4 - CompPoly/Fields/BabyBear/Fast.lean | 85 ++++++++++++++------ CompPoly/Fields/BabyBear/Fast/Convert.lean | 48 ------------ CompPoly/Fields/BabyBear/Fast/Defs.lean | 26 ------- CompPoly/Fields/KoalaBear/Fast.lean | 86 +++++++++++++++------ CompPoly/Fields/KoalaBear/Fast/Convert.lean | 48 ------------ CompPoly/Fields/KoalaBear/Fast/Defs.lean | 27 ------- 7 files changed, 124 insertions(+), 200 deletions(-) delete mode 100644 CompPoly/Fields/BabyBear/Fast/Convert.lean delete mode 100644 CompPoly/Fields/BabyBear/Fast/Defs.lean delete mode 100644 CompPoly/Fields/KoalaBear/Fast/Convert.lean delete mode 100644 CompPoly/Fields/KoalaBear/Fast/Defs.lean diff --git a/CompPoly.lean b/CompPoly.lean index 8096bb04..7c13359f 100644 --- a/CompPoly.lean +++ b/CompPoly.lean @@ -68,8 +68,6 @@ import CompPoly.Fields.BN254 import CompPoly.Fields.BabyBear import CompPoly.Fields.BabyBear.Basic import CompPoly.Fields.BabyBear.Fast -import CompPoly.Fields.BabyBear.Fast.Convert -import CompPoly.Fields.BabyBear.Fast.Defs import CompPoly.Fields.Basic import CompPoly.Fields.Binary.AdditiveNTT.AdditiveNTT import CompPoly.Fields.Binary.AdditiveNTT.Algorithm @@ -106,8 +104,6 @@ import CompPoly.Fields.Goldilocks import CompPoly.Fields.KoalaBear import CompPoly.Fields.KoalaBear.Basic import CompPoly.Fields.KoalaBear.Fast -import CompPoly.Fields.KoalaBear.Fast.Convert -import CompPoly.Fields.KoalaBear.Fast.Defs import CompPoly.Fields.Mersenne import CompPoly.Fields.Montgomery.Basic import CompPoly.Fields.Montgomery.Native32 diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index f7795038..a3a0b003 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -4,29 +4,67 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Valerii Huhnin, Georgios Raikos -/ -import CompPoly.Fields.BabyBear.Fast.Convert +import CompPoly.Fields.BabyBear.Basic +import CompPoly.Fields.Montgomery.Native32Field /-! -# Fast BabyBear Field Operations - -A native-word implementation of BabyBear arithmetic as a sidecar to the canonical -`BabyBear.Field := ZMod BabyBear.fieldSize` model. Fast values are stored as Montgomery -`UInt32` residues below `BabyBear.fieldSize`, representing `x * 2^32` modulo the prime. - -The operations, their `Field`/`CommRing`/`NonBinaryField` instances, the `toField` bridge, -and all correctness theorems are shared across every fast 32-bit-word field; they live once -in `CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance -in `CompPoly.Fields.BabyBear.Fast.Defs`. Because -`Field := Native32.FastField BabyBear.fieldSize`, the generic algebraic instances resolve -here automatically. This module re-exports the named operations and `simp` lemmas at the -BabyBear instance. +# Fast BabyBear Field + +A native-word Montgomery implementation of BabyBear arithmetic. The shared algorithms and +proofs live in `CompPoly.Fields.Montgomery.Native32Field`; this module supplies the BabyBear +constants and its concrete API. -/ -namespace BabyBear -namespace Fast +namespace BabyBear.Fast +open Montgomery.Native32 (Mont32Field FastField) open Montgomery.Native32.FastField +/-! ## Parameters and carrier -/ + +/-- The per-field data realizing BabyBear as a fast 32-bit-word Montgomery field. -/ +instance instMont32Field : Mont32Field BabyBear.fieldSize where + prime := BabyBear.is_prime + modulus32 := 0x78000001 + modulus64 := 0x78000001 + rModModulus := 0x0FFFFFFE + r2ModModulus := 0x45DDDDE3 + montgomeryNegInv := 0x77FFFFFF + +/-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ +abbrev Field : Type := FastField BabyBear.fieldSize + +/-! ## Conversions -/ + +/-- Reduce a `UInt64` modulo the BabyBear prime and return a Montgomery fast element. -/ +@[inline] +def reduceUInt64 (x : UInt64) : Field := + Montgomery.Native32.reduceUInt64 BabyBear.fieldSize x + +/-- Convert a 32-bit word into fast Montgomery representation. -/ +@[inline] +def ofUInt32 (x : UInt32) : Field := + Montgomery.Native32.FastField.ofUInt32 BabyBear.fieldSize x + +/-- Convert from the canonical `ZMod` BabyBear field into fast Montgomery form. -/ +@[inline] +def ofField (x : BabyBear.Field) : Field := + Montgomery.Native32.ofField x + +/-- Reducing a `UInt64` gives the canonical natural residue modulo BabyBear. -/ +@[simp] +theorem toNat_reduceUInt64 (x : UInt64) : + (reduceUInt64 x).toNat = x.toNat % BabyBear.fieldSize := + Montgomery.Native32.toNat_reduceUInt64 x + +/-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ +@[simp] +theorem toField_reduceUInt64 (x : UInt64) : + (reduceUInt64 x).toField = (x.toNat : BabyBear.Field) := + Montgomery.Native32.toField_reduceUInt64 x + +/-! ## Arithmetic -/ + /-- Fast modular addition in Montgomery form. -/ @[inline] def add (x y : Field) : Field := Montgomery.Native32.add x y @@ -49,10 +87,10 @@ def square (x : Field) : Field := Montgomery.Native32.square x /-- Exponentiation over the fast representation using repeated squaring. -/ @[inline] -def pow (x : Field) (n : Nat) : Field := Montgomery.Native32.pow x n +def pow (x : Field) (n : ℕ) : Field := Montgomery.Native32.pow x n /-- Fermat exponent used for inversion in the BabyBear prime field. -/ -def invExponent : Nat := Montgomery.Native32.invExponent BabyBear.fieldSize +def invExponent : ℕ := Montgomery.Native32.invExponent BabyBear.fieldSize /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ @[inline] @@ -62,6 +100,8 @@ def inv (x : Field) : Field := Montgomery.Native32.inv x @[inline] def div (x y : Field) : Field := Montgomery.Native32.div x y +/-! ## Canonical bridge -/ + /-- Ring equivalence between the fast Montgomery representation and canonical `BabyBear.Field`. -/ def ringEquiv : Field ≃+* BabyBear.Field := Montgomery.Native32.ringEquiv BabyBear.fieldSize @@ -133,7 +173,7 @@ theorem toField_div (x y : Field) : toField (x / y) = toField x / toField y := /-- Natural casts into fast form agree with natural casts into the canonical field. -/ @[simp] -theorem toField_natCast (n : Nat) : toField (n : Field) = (n : BabyBear.Field) := +theorem toField_natCast (n : ℕ) : toField (n : Field) = (n : BabyBear.Field) := Montgomery.Native32.toField_natCast n /-- Integer casts into fast form agree with integer casts into the canonical field. -/ @@ -143,7 +183,7 @@ theorem toField_intCast (n : Int) : toField (n : Field) = (n : BabyBear.Field) : /-- Natural scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_nsmul (n : Nat) (x : Field) : toField (n • x) = n • toField x := +theorem toField_nsmul (n : ℕ) (x : Field) : toField (n • x) = n • toField x := Montgomery.Native32.toField_nsmul n x /-- Integer scalar multiplication is preserved by `toField`. -/ @@ -153,7 +193,7 @@ theorem toField_zsmul (n : Int) (x : Field) : toField (n • x) = n • toField /-- Natural powers through the `Pow` instance are preserved by `toField`. -/ @[simp] -theorem toField_npow (x : Field) (n : Nat) : toField (x ^ n) = toField x ^ n := +theorem toField_npow (x : Field) (n : ℕ) : toField (x ^ n) = toField x ^ n := Montgomery.Native32.toField_npow x n /-- Integer powers through the `Pow` instance are preserved by `toField`. -/ @@ -181,5 +221,4 @@ theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • to theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := Montgomery.Native32.toField_qsmul q x -end Fast -end BabyBear +end BabyBear.Fast diff --git a/CompPoly/Fields/BabyBear/Fast/Convert.lean b/CompPoly/Fields/BabyBear/Fast/Convert.lean deleted file mode 100644 index 7d8986e7..00000000 --- a/CompPoly/Fields/BabyBear/Fast/Convert.lean +++ /dev/null @@ -1,48 +0,0 @@ -/- -Copyright (c) 2026 CompPoly Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin, Georgios Raikos --/ - -import CompPoly.Fields.BabyBear.Fast.Defs - -/-! -# Fast BabyBear Field — Conversions - -Conversions between the fast Montgomery representation and the canonical -`BabyBear.Field` / `Nat` views, re-exported from the shared implementation in -`CompPoly.Fields.Montgomery.Native32Field` at the BabyBear instance. --/ - -namespace BabyBear -namespace Fast - -/-- Reduce a `UInt64` modulo the BabyBear prime and return a Montgomery fast element. -/ -@[inline] -def reduceUInt64 (x : UInt64) : Field := - Montgomery.Native32.reduceUInt64 BabyBear.fieldSize x - -/-- Convert a 32-bit word into fast Montgomery representation. -/ -@[inline] -def ofUInt32 (x : UInt32) : Field := - Montgomery.Native32.FastField.ofUInt32 BabyBear.fieldSize x - -/-- Convert from the canonical `ZMod` BabyBear field into fast Montgomery form. -/ -@[inline] -def ofField (x : BabyBear.Field) : Field := - Montgomery.Native32.ofField x - -/-- Reducing a `UInt64` gives the canonical natural residue modulo BabyBear. -/ -@[simp] -theorem toNat_reduceUInt64 (x : UInt64) : - (reduceUInt64 x).toNat = x.toNat % BabyBear.fieldSize := - Montgomery.Native32.toNat_reduceUInt64 x - -/-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ -@[simp] -theorem toField_reduceUInt64 (x : UInt64) : - (reduceUInt64 x).toField = (x.toNat : BabyBear.Field) := - Montgomery.Native32.toField_reduceUInt64 x - -end Fast -end BabyBear diff --git a/CompPoly/Fields/BabyBear/Fast/Defs.lean b/CompPoly/Fields/BabyBear/Fast/Defs.lean deleted file mode 100644 index d10bff40..00000000 --- a/CompPoly/Fields/BabyBear/Fast/Defs.lean +++ /dev/null @@ -1,26 +0,0 @@ -/- -Copyright (c) 2026 CompPoly Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin, Georgios Raikos --/ -import CompPoly.Fields.BabyBear.Basic -import CompPoly.Fields.Montgomery.Native32Field - -/-! # Fast BabyBear field definitions -/ - -namespace BabyBear.Fast -open Montgomery.Native32 (Mont32Field FastField) - -/-- The per-field data realizing BabyBear as a fast 32-bit-word Montgomery field. -/ -instance instMont32Field : Mont32Field BabyBear.fieldSize where - prime := BabyBear.is_prime - modulus32 := 0x78000001 - modulus64 := 0x78000001 - rModModulus := 0x0FFFFFFE - r2ModModulus := 0x45DDDDE3 - montgomeryNegInv := 0x77FFFFFF - -/-- The fast native-word BabyBear field carrier, stored as a Montgomery residue. -/ -abbrev Field : Type := FastField BabyBear.fieldSize - -end BabyBear.Fast diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index b394d9a0..27c9709e 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -4,30 +4,67 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Valerii Huhnin -/ -import CompPoly.Fields.KoalaBear.Fast.Convert +import CompPoly.Fields.KoalaBear.Basic +import CompPoly.Fields.Montgomery.Native32Field /-! -# Fast KoalaBear Field Operations - -A native-word implementation of KoalaBear arithmetic as a sidecar to the canonical -`KoalaBear.Field := ZMod KoalaBear.fieldSize` model. Fast values are stored as Montgomery -`UInt32` residues below `KoalaBear.fieldSize`, representing `x * 2^32` modulo the prime. - -The operations, their `Field`/`CommRing`/`NonBinaryField` instances, the `toField` bridge, -and all correctness theorems are shared across every fast 32-bit-word field; they live once -in `CompPoly.Fields.Montgomery.Native32Field`, parameterized by the `Mont32Field` instance -in `CompPoly.Fields.KoalaBear.Fast.Defs`. Because -`Field := Native32.FastField KoalaBear.fieldSize`, the generic algebraic instances resolve -here automatically. This module re-exports the named operations and `simp` lemmas at the -KoalaBear instance. +# Fast KoalaBear Field + +A native-word Montgomery implementation of KoalaBear arithmetic. The shared algorithms and +proofs live in `CompPoly.Fields.Montgomery.Native32Field`; this module supplies the KoalaBear +constants and its concrete API. -/ -namespace KoalaBear -namespace Fast +namespace KoalaBear.Fast -open Montgomery.Native32 +open Montgomery.Native32 (Mont32Field FastField) open Montgomery.Native32.FastField +/-! ## Parameters and carrier -/ + +/-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. -/ +instance instMont32Field : Mont32Field KoalaBear.fieldSize where + prime := KoalaBear.is_prime + modulus32 := 0x7F000001 + modulus64 := 0x7F000001 + rModModulus := 0x01FFFFFE + r2ModModulus := 0x17F7EFE4 + montgomeryNegInv := 0x7EFFFFFF + +/-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ +abbrev Field : Type := FastField KoalaBear.fieldSize + +/-! ## Conversions -/ + +/-- Reduce a `UInt64` modulo the KoalaBear prime and return a Montgomery fast element. -/ +@[inline] +def reduceUInt64 (x : UInt64) : Field := + Montgomery.Native32.reduceUInt64 KoalaBear.fieldSize x + +/-- Convert a 32-bit word into fast Montgomery representation. -/ +@[inline] +def ofUInt32 (x : UInt32) : Field := + Montgomery.Native32.FastField.ofUInt32 KoalaBear.fieldSize x + +/-- Convert from the canonical `ZMod` KoalaBear field into fast Montgomery form. -/ +@[inline] +def ofField (x : KoalaBear.Field) : Field := + Montgomery.Native32.ofField x + +/-- Reducing a `UInt64` gives the canonical natural residue modulo KoalaBear. -/ +@[simp] +theorem toNat_reduceUInt64 (x : UInt64) : + (reduceUInt64 x).toNat = x.toNat % KoalaBear.fieldSize := + Montgomery.Native32.toNat_reduceUInt64 x + +/-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ +@[simp] +theorem toField_reduceUInt64 (x : UInt64) : + (reduceUInt64 x).toField = (x.toNat : KoalaBear.Field) := + Montgomery.Native32.toField_reduceUInt64 x + +/-! ## Arithmetic -/ + /-- Fast modular addition in Montgomery form. -/ @[inline] def add (x y : Field) : Field := Montgomery.Native32.add x y @@ -50,10 +87,10 @@ def square (x : Field) : Field := Montgomery.Native32.square x /-- Exponentiation over the fast representation using repeated squaring. -/ @[inline] -def pow (x : Field) (n : Nat) : Field := Montgomery.Native32.pow x n +def pow (x : Field) (n : ℕ) : Field := Montgomery.Native32.pow x n /-- Fermat exponent used for inversion in the KoalaBear prime field. -/ -def invExponent : Nat := Montgomery.Native32.invExponent KoalaBear.fieldSize +def invExponent : ℕ := Montgomery.Native32.invExponent KoalaBear.fieldSize /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ @[inline] @@ -63,6 +100,8 @@ def inv (x : Field) : Field := Montgomery.Native32.inv x @[inline] def div (x y : Field) : Field := Montgomery.Native32.div x y +/-! ## Canonical bridge -/ + /-- Ring equivalence between the fast Montgomery representation and canonical `KoalaBear.Field`. -/ def ringEquiv : Field ≃+* KoalaBear.Field := Montgomery.Native32.ringEquiv KoalaBear.fieldSize @@ -134,7 +173,7 @@ theorem toField_div (x y : Field) : toField (x / y) = toField x / toField y := /-- Natural casts into fast form agree with natural casts into the canonical field. -/ @[simp] -theorem toField_natCast (n : Nat) : toField (n : Field) = (n : KoalaBear.Field) := +theorem toField_natCast (n : ℕ) : toField (n : Field) = (n : KoalaBear.Field) := Montgomery.Native32.toField_natCast n /-- Integer casts into fast form agree with integer casts into the canonical field. -/ @@ -144,7 +183,7 @@ theorem toField_intCast (n : Int) : toField (n : Field) = (n : KoalaBear.Field) /-- Natural scalar multiplication is preserved by `toField`. -/ @[simp] -theorem toField_nsmul (n : Nat) (x : Field) : toField (n • x) = n • toField x := +theorem toField_nsmul (n : ℕ) (x : Field) : toField (n • x) = n • toField x := Montgomery.Native32.toField_nsmul n x /-- Integer scalar multiplication is preserved by `toField`. -/ @@ -154,7 +193,7 @@ theorem toField_zsmul (n : Int) (x : Field) : toField (n • x) = n • toField /-- Natural powers through the `Pow` instance are preserved by `toField`. -/ @[simp] -theorem toField_npow (x : Field) (n : Nat) : toField (x ^ n) = toField x ^ n := +theorem toField_npow (x : Field) (n : ℕ) : toField (x ^ n) = toField x ^ n := Montgomery.Native32.toField_npow x n /-- Integer powers through the `Pow` instance are preserved by `toField`. -/ @@ -182,5 +221,4 @@ theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • to theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := Montgomery.Native32.toField_qsmul q x -end Fast -end KoalaBear +end KoalaBear.Fast diff --git a/CompPoly/Fields/KoalaBear/Fast/Convert.lean b/CompPoly/Fields/KoalaBear/Fast/Convert.lean deleted file mode 100644 index 4942c23c..00000000 --- a/CompPoly/Fields/KoalaBear/Fast/Convert.lean +++ /dev/null @@ -1,48 +0,0 @@ -/- -Copyright (c) 2026 CompPoly Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin --/ - -import CompPoly.Fields.KoalaBear.Fast.Defs - -/-! -# Fast KoalaBear Field — Conversions - -Conversions between the fast Montgomery representation and the canonical -`KoalaBear.Field` / `Nat` views, re-exported from the shared implementation in -`CompPoly.Fields.Montgomery.Native32Field` at the KoalaBear instance. --/ - -namespace KoalaBear -namespace Fast - -/-- Reduce a `UInt64` modulo the KoalaBear prime and return a Montgomery fast element. -/ -@[inline] -def reduceUInt64 (x : UInt64) : Field := - Montgomery.Native32.reduceUInt64 KoalaBear.fieldSize x - -/-- Convert a 32-bit word into fast Montgomery representation. -/ -@[inline] -def ofUInt32 (x : UInt32) : Field := - Montgomery.Native32.FastField.ofUInt32 KoalaBear.fieldSize x - -/-- Convert from the canonical `ZMod` KoalaBear field into fast Montgomery form. -/ -@[inline] -def ofField (x : KoalaBear.Field) : Field := - Montgomery.Native32.ofField x - -/-- Reducing a `UInt64` gives the canonical natural residue modulo KoalaBear. -/ -@[simp] -theorem toNat_reduceUInt64 (x : UInt64) : - (reduceUInt64 x).toNat = x.toNat % KoalaBear.fieldSize := - Montgomery.Native32.toNat_reduceUInt64 x - -/-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ -@[simp] -theorem toField_reduceUInt64 (x : UInt64) : - (reduceUInt64 x).toField = (x.toNat : KoalaBear.Field) := - Montgomery.Native32.toField_reduceUInt64 x - -end Fast -end KoalaBear diff --git a/CompPoly/Fields/KoalaBear/Fast/Defs.lean b/CompPoly/Fields/KoalaBear/Fast/Defs.lean deleted file mode 100644 index 48ddfda6..00000000 --- a/CompPoly/Fields/KoalaBear/Fast/Defs.lean +++ /dev/null @@ -1,27 +0,0 @@ -/- -Copyright (c) 2026 CompPoly Contributors. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Valerii Huhnin --/ - -import CompPoly.Fields.KoalaBear.Basic -import CompPoly.Fields.Montgomery.Native32Field - -/-! # Fast KoalaBear field definitions -/ - -namespace KoalaBear.Fast -open Montgomery.Native32 (Mont32Field FastField) - -/-- The per-field data realizing KoalaBear as a fast 32-bit-word Montgomery field. -/ -instance instMont32Field : Mont32Field KoalaBear.fieldSize where - prime := KoalaBear.is_prime - modulus32 := 0x7F000001 - modulus64 := 0x7F000001 - rModModulus := 0x01FFFFFE - r2ModModulus := 0x17F7EFE4 - montgomeryNegInv := 0x7EFFFFFF - -/-- The fast native-word KoalaBear field carrier, stored as a Montgomery residue. -/ -abbrev Field : Type := FastField KoalaBear.fieldSize - -end KoalaBear.Fast From e7488d9b80a288406972222c33e9617860f834e9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 10:32:09 +0200 Subject: [PATCH 21/31] fix(bench): update fast field conversion calls --- bench/CompPolyBench/Common.lean | 2 +- bench/CompPolyBench/Univariate/Common.lean | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bench/CompPolyBench/Common.lean b/bench/CompPolyBench/Common.lean index 0c885e5b..4e5ab3de 100644 --- a/bench/CompPolyBench/Common.lean +++ b/bench/CompPolyBench/Common.lean @@ -454,7 +454,7 @@ def checksumKoalaBear (x : KoalaBear.Field) : Nat := /-- Convert a fast KoalaBear element to a checksum word. -/ def checksumKoalaBearFast (x : KoalaBear.Fast.Field) : Nat := - KoalaBear.Fast.toNat x + x.toNat /-- Convert a `ZMod` element to a checksum word. -/ def checksumZMod {modulus : Nat} (x : ZMod modulus) : Nat := diff --git a/bench/CompPolyBench/Univariate/Common.lean b/bench/CompPolyBench/Univariate/Common.lean index 8a6ec4cb..72264a3a 100644 --- a/bench/CompPolyBench/Univariate/Common.lean +++ b/bench/CompPolyBench/Univariate/Common.lean @@ -99,7 +99,7 @@ def koalaBearBestDomainForLength? (requiredLen : Nat) : /-- Ring equivalence from fast KoalaBear elements to the canonical `ZMod` model. -/ noncomputable def koalaBearFastRingEquiv : KoalaBear.Fast.Field ≃+* KoalaBear.Field where - toFun := KoalaBear.Fast.toField + toFun := (·.toField) invFun := KoalaBear.Fast.ofField left_inv := KoalaBear.Fast.ofField_toField right_inv := KoalaBear.Fast.toField_ofField @@ -127,9 +127,10 @@ theorem koalaBearFast_twoPowNatCast_ne_zero exact CPolynomial.NTT.KoalaBear.twoPowNatCast_ne_zero logN hlogN (by calc (((2 ^ logN : Nat) : KoalaBear.Field)) = - KoalaBear.Fast.toField (((2 ^ logN : Nat) : KoalaBear.Fast.Field)) := by + (((2 ^ logN : Nat) : KoalaBear.Fast.Field)).toField := by rw [KoalaBear.Fast.toField_natCast] - _ = KoalaBear.Fast.toField 0 := congrArg KoalaBear.Fast.toField hzero + _ = (0 : KoalaBear.Fast.Field).toField := + congrArg (fun x : KoalaBear.Fast.Field ↦ x.toField) hzero _ = 0 := KoalaBear.Fast.toField_zero) /-- Fast KoalaBear radix-2 NTT domain for a supported two-adic size. -/ From 6160d8716f8982326d4da0fe3ec801ca11fa977f Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 10:43:33 +0200 Subject: [PATCH 22/31] refactor(bench): reuse KoalaBear fast NTT domains --- bench/CompPolyBench/Univariate/Common.lean | 53 +--------------------- 1 file changed, 2 insertions(+), 51 deletions(-) diff --git a/bench/CompPolyBench/Univariate/Common.lean b/bench/CompPolyBench/Univariate/Common.lean index 72264a3a..2fd4ad0e 100644 --- a/bench/CompPolyBench/Univariate/Common.lean +++ b/bench/CompPolyBench/Univariate/Common.lean @@ -97,62 +97,13 @@ def koalaBearBestDomainForLength? (requiredLen : Nat) : CPolynomial.NTT.bestDomainForLength? KoalaBear.twoAdicity CPolynomial.NTT.KoalaBear.domainOfLogN (by intro _ _; rfl) requiredLen -/-- Ring equivalence from fast KoalaBear elements to the canonical `ZMod` model. -/ -noncomputable def koalaBearFastRingEquiv : KoalaBear.Fast.Field ≃+* KoalaBear.Field where - toFun := (·.toField) - invFun := KoalaBear.Fast.ofField - left_inv := KoalaBear.Fast.ofField_toField - right_inv := KoalaBear.Fast.toField_ofField - map_mul' := KoalaBear.Fast.toField_mul - map_add' := KoalaBear.Fast.toField_add - -/-- Fast KoalaBear two-adic generators are primitive roots of the same orders. -/ -theorem koalaBearFast_isPrimitiveRoot_twoAdicGenerator - (bits : Fin (KoalaBear.twoAdicity + 1)) : - IsPrimitiveRoot (KoalaBear.Fast.ofField KoalaBear.twoAdicGenerators[bits]) - (2 ^ (bits : Nat)) := by - have hbasic : IsPrimitiveRoot KoalaBear.twoAdicGenerators[bits] (2 ^ (bits : Nat)) := - KoalaBear.isPrimitiveRoot_twoAdicGenerator bits - have hfast : - IsPrimitiveRoot (koalaBearFastRingEquiv.symm KoalaBear.twoAdicGenerators[bits]) - (2 ^ (bits : Nat)) := by - exact hbasic.map_of_injective koalaBearFastRingEquiv.symm.injective - simpa [koalaBearFastRingEquiv] using hfast - -/-- The fast KoalaBear NTT domain size is nonzero for supported two-adic sizes. -/ -theorem koalaBearFast_twoPowNatCast_ne_zero - (logN : Nat) (hlogN : logN ≤ KoalaBear.twoAdicity) : - (((2 ^ logN : Nat) : KoalaBear.Fast.Field) ≠ 0) := by - intro hzero - exact CPolynomial.NTT.KoalaBear.twoPowNatCast_ne_zero logN hlogN (by - calc - (((2 ^ logN : Nat) : KoalaBear.Field)) = - (((2 ^ logN : Nat) : KoalaBear.Fast.Field)).toField := by - rw [KoalaBear.Fast.toField_natCast] - _ = (0 : KoalaBear.Fast.Field).toField := - congrArg (fun x : KoalaBear.Fast.Field ↦ x.toField) hzero - _ = 0 := KoalaBear.Fast.toField_zero) - -/-- Fast KoalaBear radix-2 NTT domain for a supported two-adic size. -/ -def koalaBearFastDomainOfLogN (logN : Nat) (hlogN : logN ≤ KoalaBear.twoAdicity) : - CPolynomial.NTT.Domain KoalaBear.Fast.Field where - logN := logN - omega := KoalaBear.Fast.ofField - KoalaBear.twoAdicGenerators[(⟨logN, Nat.lt_succ_of_le hlogN⟩ : - Fin (KoalaBear.twoAdicity + 1))] - primitive := by - simpa using koalaBearFast_isPrimitiveRoot_twoAdicGenerator - (⟨logN, Nat.lt_succ_of_le hlogN⟩ : Fin (KoalaBear.twoAdicity + 1)) - natCast_ne_zero := koalaBearFast_twoPowNatCast_ne_zero logN hlogN - /-- NTT domain for direct univariate fast KoalaBear multiplication benchmarks. -/ def koalaBearFastMulNttDomain : CPolynomial.NTT.Domain KoalaBear.Fast.Field := - koalaBearFastDomainOfLogN univariateMulLogN (by decide) + CPolynomial.NTT.KoalaBear.fastDomainOfLogN univariateMulLogN (by decide) /-- Fast KoalaBear NTT domain lookup for dynamic multiplication contexts. -/ def koalaBearFastBestDomainForLength? (requiredLen : Nat) : Option (CPolynomial.NTT.FittingDomain KoalaBear.Fast.Field requiredLen) := - CPolynomial.NTT.bestDomainForLength? KoalaBear.twoAdicity - koalaBearFastDomainOfLogN (by intro _ _; rfl) requiredLen + CPolynomial.NTT.KoalaBear.fastBestDomainForLength? requiredLen end CompPolyBench From 73efa515d13e7e0294b43479cf828fd62987d64c Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 12:48:26 +0200 Subject: [PATCH 23/31] refactor(ntt): simplify concrete field domains --- CompPoly/Fields/KoalaBear/Fast.lean | 37 +++++++ CompPoly/Univariate/NTT/BabyBear.lean | 18 ---- CompPoly/Univariate/NTT/Domain.lean | 7 +- CompPoly/Univariate/NTT/KoalaBear.lean | 54 +---------- .../CompPolyBench/Univariate/NTT/FastMul.lean | 97 ++++++++++--------- 5 files changed, 97 insertions(+), 116 deletions(-) diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index 27c9709e..24aa394c 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -221,4 +221,41 @@ theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • to theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := Montgomery.Native32.toField_qsmul q x +/-! ## Two-adic roots -/ + +/-- Precomputed KoalaBear two-adic generators in Montgomery representation. -/ +def twoAdicGenerators : List Field := + [ + ⟨0x01FFFFFE, by decide⟩, + ⟨0x7D000003, by decide⟩, + ⟨0x7B020407, by decide⟩, + ⟨0x60F5EF4D, by decide⟩, + ⟨0x6D249C01, by decide⟩, + ⟨0x788529F3, by decide⟩, + ⟨0x07F7373E, by decide⟩, + ⟨0x6FE91D3C, by decide⟩, + ⟨0x3FD49211, by decide⟩, + ⟨0x1E056392, by decide⟩, + ⟨0x6D969BAB, by decide⟩, + ⟨0x439600CC, by decide⟩, + ⟨0x150276FC, by decide⟩, + ⟨0x68CACC36, by decide⟩, + ⟨0x42336C40, by decide⟩, + ⟨0x019B1972, by decide⟩, + ⟨0x34E52F6D, by decide⟩, + ⟨0x1C2EB437, by decide⟩, + ⟨0x7CB65829, by decide⟩, + ⟨0x29306FAE, by decide⟩, + ⟨0x351C7FA7, by decide⟩, + ⟨0x6E3E9A00, by decide⟩, + ⟨0x47C2BDF7, by decide⟩, + ⟨0x0C895820, by decide⟩, + ⟨0x13C85195, by decide⟩ + ] + +/-- The Montgomery root table represents the canonical KoalaBear roots. -/ +theorem twoAdicGenerators_eq_map : + twoAdicGenerators = KoalaBear.twoAdicGenerators.map ofField := by + decide + end KoalaBear.Fast diff --git a/CompPoly/Univariate/NTT/BabyBear.lean b/CompPoly/Univariate/NTT/BabyBear.lean index f8c4d6d8..4464f689 100644 --- a/CompPoly/Univariate/NTT/BabyBear.lean +++ b/CompPoly/Univariate/NTT/BabyBear.lean @@ -22,23 +22,6 @@ def bitsOfLogN (logN : Nat) (hlogN : logN ≤ BabyBear.twoAdicity) : Fin (BabyBear.twoAdicity + 1) := ⟨logN, Nat.lt_succ_of_le hlogN⟩ -/-- The BabyBear NTT domain size is nonzero in the field for supported two-adic sizes. -/ -theorem twoPowNatCast_ne_zero - (logN : Nat) (hlogN : logN ≤ BabyBear.twoAdicity) : - (((2 ^ logN : Nat) : BabyBear.Field) ≠ 0) := by - intro hzero - have hpow_pos : 0 < 2 ^ logN := by - positivity - have hpow_le : 2 ^ logN ≤ 2 ^ BabyBear.twoAdicity := by - exact Nat.pow_le_pow_right (by decide : 1 ≤ 2) hlogN - have hpow_lt : 2 ^ logN < BabyBear.fieldSize := by - have htop : 2 ^ BabyBear.twoAdicity < BabyBear.fieldSize := by - simp [BabyBear.twoAdicity, BabyBear.fieldSize] - exact lt_of_le_of_lt hpow_le htop - have hdiv : BabyBear.fieldSize ∣ 2 ^ logN := by - exact (ZMod.natCast_eq_zero_iff (2 ^ logN) BabyBear.fieldSize).mp hzero - exact (not_le_of_gt hpow_lt) (Nat.le_of_dvd hpow_pos hdiv) - /-- BabyBear radix-2 NTT domain for a supported two-adic size. -/ def domainOfLogN (logN : Nat) (hlogN : logN ≤ BabyBear.twoAdicity) : Domain BabyBear.Field where @@ -47,7 +30,6 @@ def domainOfLogN (logN : Nat) (hlogN : logN ≤ BabyBear.twoAdicity) : primitive := by simpa [bitsOfLogN] using BabyBear.isPrimitiveRoot_twoAdicGenerator (bitsOfLogN logN hlogN) - natCast_ne_zero := twoPowNatCast_ne_zero logN hlogN end BabyBear end NTT diff --git a/CompPoly/Univariate/NTT/Domain.lean b/CompPoly/Univariate/NTT/Domain.lean index d99f2d6c..8e636c1a 100644 --- a/CompPoly/Univariate/NTT/Domain.lean +++ b/CompPoly/Univariate/NTT/Domain.lean @@ -26,7 +26,6 @@ structure Domain (R : Type*) [Field R] where logN : Nat omega : R primitive : IsPrimitiveRoot omega (2 ^ logN) - natCast_ne_zero : (((2 ^ logN : Nat) : R) ≠ 0) namespace Domain @@ -48,7 +47,6 @@ def inverse (D : Domain R) : Domain R where omega := D.omegaInv primitive := by simpa [omegaInv] using D.primitive.inv - natCast_ne_zero := D.natCast_ne_zero /-- Multiplicative inverse of the domain size in `R`. -/ @[inline] def nInv (D : Domain R) : R := ((D.n : Nat) : R)⁻¹ @@ -59,6 +57,11 @@ def inverse (D : Domain R) : Domain R where @[simp] lemma n_ne_zero (D : Domain R) : D.n ≠ 0 := by exact Nat.ne_of_gt D.n_pos +/-- The size of an NTT domain is nonzero in its coefficient field. -/ +theorem natCast_ne_zero (D : Domain R) : ((D.n : Nat) : R) ≠ 0 := by + letI : NeZero D.n := ⟨D.n_ne_zero⟩ + exact D.primitive.neZero'.out + section RawHelpers variable [BEq R] diff --git a/CompPoly/Univariate/NTT/KoalaBear.lean b/CompPoly/Univariate/NTT/KoalaBear.lean index ace8bf57..edb4794f 100644 --- a/CompPoly/Univariate/NTT/KoalaBear.lean +++ b/CompPoly/Univariate/NTT/KoalaBear.lean @@ -22,23 +22,6 @@ def bitsOfLogN (logN : Nat) (hlogN : logN ≤ KoalaBear.twoAdicity) : Fin (KoalaBear.twoAdicity + 1) := ⟨logN, Nat.lt_succ_of_le hlogN⟩ -/-- The KoalaBear NTT domain size is nonzero in the field for supported two-adic sizes. -/ -theorem twoPowNatCast_ne_zero - (logN : Nat) (hlogN : logN ≤ KoalaBear.twoAdicity) : - (((2 ^ logN : Nat) : KoalaBear.Field) ≠ 0) := by - intro hzero - have hpow_pos : 0 < 2 ^ logN := by - positivity - have hpow_le : 2 ^ logN ≤ 2 ^ KoalaBear.twoAdicity := by - exact Nat.pow_le_pow_right (by decide : 1 ≤ 2) hlogN - have hpow_lt : 2 ^ logN < KoalaBear.fieldSize := by - have htop : 2 ^ KoalaBear.twoAdicity < KoalaBear.fieldSize := by - simp [KoalaBear.twoAdicity, KoalaBear.fieldSize] - exact lt_of_le_of_lt hpow_le htop - have hdiv : KoalaBear.fieldSize ∣ 2 ^ logN := by - exact (ZMod.natCast_eq_zero_iff (2 ^ logN) KoalaBear.fieldSize).mp hzero - exact (not_le_of_gt hpow_lt) (Nat.le_of_dvd hpow_pos hdiv) - /-- KoalaBear radix-2 NTT domain for a supported two-adic size. -/ def domainOfLogN (logN : Nat) (hlogN : logN ≤ KoalaBear.twoAdicity) : Domain KoalaBear.Field where @@ -47,7 +30,6 @@ def domainOfLogN (logN : Nat) (hlogN : logN ≤ KoalaBear.twoAdicity) : primitive := by simpa [bitsOfLogN] using KoalaBear.isPrimitiveRoot_twoAdicGenerator (bitsOfLogN logN hlogN) - natCast_ne_zero := twoPowNatCast_ne_zero logN hlogN /-- KoalaBear NTT domain lookup for dynamic multiplication contexts. -/ def bestDomainForLength? (requiredLen : Nat) : @@ -55,43 +37,15 @@ def bestDomainForLength? (requiredLen : Nat) : CPolynomial.NTT.bestDomainForLength? KoalaBear.twoAdicity domainOfLogN (by intro _ _; rfl) requiredLen -/-- Fast KoalaBear two-adic generators are primitive roots of the same orders. -/ -theorem fast_isPrimitiveRoot_twoAdicGenerator - (bits : Fin (KoalaBear.twoAdicity + 1)) : - IsPrimitiveRoot (KoalaBear.Fast.ofField KoalaBear.twoAdicGenerators[bits]) - (2 ^ (bits : Nat)) := by - have hbasic : IsPrimitiveRoot KoalaBear.twoAdicGenerators[bits] (2 ^ (bits : Nat)) := - KoalaBear.isPrimitiveRoot_twoAdicGenerator bits - have hfast : - IsPrimitiveRoot (KoalaBear.Fast.ringEquiv.symm KoalaBear.twoAdicGenerators[bits]) - (2 ^ (bits : Nat)) := by - exact hbasic.map_of_injective KoalaBear.Fast.ringEquiv.symm.injective - simpa using hfast - -/-- The fast KoalaBear NTT domain size is nonzero for supported two-adic sizes. -/ -theorem fast_twoPowNatCast_ne_zero - (logN : Nat) (hlogN : logN ≤ KoalaBear.twoAdicity) : - (((2 ^ logN : Nat) : KoalaBear.Fast.Field) ≠ 0) := by - intro hzero - exact twoPowNatCast_ne_zero logN hlogN (by - calc - (((2 ^ logN : Nat) : KoalaBear.Field)) = - (((2 ^ logN : Nat) : KoalaBear.Fast.Field)).toField := by - rw [KoalaBear.Fast.toField_natCast] - _ = (0 : KoalaBear.Fast.Field).toField := - congrArg (fun x : KoalaBear.Fast.Field => x.toField) hzero - _ = 0 := KoalaBear.Fast.toField_zero) - /-- Fast KoalaBear radix-2 NTT domain for a supported two-adic size. -/ def fastDomainOfLogN (logN : Nat) (hlogN : logN ≤ KoalaBear.twoAdicity) : Domain KoalaBear.Fast.Field where logN := logN - omega := KoalaBear.Fast.ofField - KoalaBear.twoAdicGenerators[bitsOfLogN logN hlogN] + omega := KoalaBear.Fast.twoAdicGenerators[bitsOfLogN logN hlogN] primitive := by - simpa [bitsOfLogN] using - fast_isPrimitiveRoot_twoAdicGenerator (bitsOfLogN logN hlogN) - natCast_ne_zero := fast_twoPowNatCast_ne_zero logN hlogN + have h := (KoalaBear.isPrimitiveRoot_twoAdicGenerator (bitsOfLogN logN hlogN)).map_of_injective + KoalaBear.Fast.ringEquiv.symm.injective + simpa [KoalaBear.Fast.twoAdicGenerators_eq_map] using h /-- Fast KoalaBear NTT domain lookup for dynamic multiplication contexts. -/ def fastBestDomainForLength? (requiredLen : Nat) : diff --git a/bench/CompPolyBench/Univariate/NTT/FastMul.lean b/bench/CompPolyBench/Univariate/NTT/FastMul.lean index d1465586..1c2c43a0 100644 --- a/bench/CompPolyBench/Univariate/NTT/FastMul.lean +++ b/bench/CompPolyBench/Univariate/NTT/FastMul.lean @@ -22,6 +22,11 @@ def univariateNttFastMulGroupInfos : List BenchGroupInfo := [ ⟨"univariate-mul-koalabear", "Univariate multiplication (KoalaBear)"⟩ ] +/-- Display and checksum operations associated with a benchmark field. -/ +private structure BenchField (F : Type*) where + id : String + checksum : F → Nat + /-- Benchmark KoalaBear direct univariate multiplication and root-of-unity NTT variants. -/ private def runKoalaBearUnivariateMul (preset : BenchPreset) (gen : StdGen) : IO (BenchGroup × StdGen) := do @@ -33,9 +38,14 @@ private def runKoalaBearUnivariateMul (preset : BenchPreset) (gen : StdGen) : let fastMulRhsCoeffs := koalaBearFastArray mulRhsCoeffs let fastMulLhsPoly := cpolyOfArray fastMulLhsCoeffs let fastMulRhsPoly := cpolyOfArray fastMulRhsCoeffs - let koalaBearMulNttFastPlan := CPolynomial.NTTFast.Plan.ofDomain koalaBearMulNttDomain - let koalaBearFastMulNttFastPlan := - CPolynomial.NTTFast.Plan.ofDomain koalaBearFastMulNttDomain + let canonicalPlan := CPolynomial.NTTFast.Plan.ofDomain koalaBearMulNttDomain + let fastPlan := CPolynomial.NTTFast.Plan.ofDomain koalaBearFastMulNttDomain + let canonicalField : BenchField KoalaBear.Field := + ⟨"KoalaBear.Field", checksumKoalaBear⟩ + let fastField : BenchField KoalaBear.Fast.Field := + ⟨"KoalaBear.Fast.Field", checksumKoalaBearFast⟩ + let canonicalChecksum := checksumCPolynomial canonicalField.checksum + let fastChecksum := checksumCPolynomial fastField.checksum let warmup := mulWarmupIterations preset let measured := mulMeasuredIterations preset let nttMeasured := preset.selectNat 200 30 5 @@ -49,66 +59,61 @@ private def runKoalaBearUnivariateMul (preset : BenchPreset) (gen : StdGen) : nttMeasured, nttFastMeasured, nttFastPlanMeasured, fastMeasured, fastNttMeasured, fastNttFastMeasured, fastNttFastPlanMeasured ] - let koalaBearMulNaive ← runTimed - "univariate-mul-naive" "CPolynomial" "mul" "KoalaBear.Field" + let canonicalNaive ← runTimed + "univariate-mul-naive" "CPolynomial" "mul" canonicalField.id univariateMulShape preset warmup measured - (fun _ ↦ mulLhsPoly * mulRhsPoly) - (checksumCPolynomial checksumKoalaBear) (checksumIterations := checksumIterations) - let koalaBearFastMulNaive ← runTimed - "univariate-mul-naive-fast" "CPolynomial" "mul" "KoalaBear.Fast.Field" + (fun _ ↦ mulLhsPoly * mulRhsPoly) canonicalChecksum + (checksumIterations := checksumIterations) + let fastNaive ← runTimed + "univariate-mul-naive-fast" "CPolynomial" "mul" fastField.id univariateMulShape preset warmup fastMeasured - (fun _ ↦ fastMulLhsPoly * fastMulRhsPoly) - (checksumCPolynomial checksumKoalaBearFast) (checksumIterations := checksumIterations) - let koalaBearMulNtt ← runTimed + (fun _ ↦ fastMulLhsPoly * fastMulRhsPoly) fastChecksum + (checksumIterations := checksumIterations) + let canonicalNtt ← runTimed "univariate-mul-ntt" "CPolynomial" (univariateMulNttMethod "FastMul.fastMulImpl") - "KoalaBear.Field" - univariateMulShape preset warmup nttMeasured - (fun _ ↦ CPolynomial.NTT.FastMul.fastMulImpl koalaBearMulNttDomain mulLhsPoly mulRhsPoly) - (checksumCPolynomial checksumKoalaBear) (checksumIterations := checksumIterations) - let koalaBearFastMulNtt ← runTimed + canonicalField.id univariateMulShape preset warmup nttMeasured + (fun _ ↦ + CPolynomial.NTT.FastMul.fastMulImpl koalaBearMulNttDomain mulLhsPoly mulRhsPoly) + canonicalChecksum (checksumIterations := checksumIterations) + let fastNtt ← runTimed "univariate-mul-ntt-koalabear-fast" "CPolynomial" - (univariateMulNttMethod "FastMul.fastMulImpl") "KoalaBear.Fast.Field" + (univariateMulNttMethod "FastMul.fastMulImpl") fastField.id univariateMulShape preset warmup fastNttMeasured - (fun _ ↦ CPolynomial.NTT.FastMul.fastMulImpl koalaBearFastMulNttDomain fastMulLhsPoly - fastMulRhsPoly) - (checksumCPolynomial checksumKoalaBearFast) (checksumIterations := checksumIterations) - let koalaBearMulNttFast ← runTimed + (fun _ ↦ CPolynomial.NTT.FastMul.fastMulImpl koalaBearFastMulNttDomain + fastMulLhsPoly fastMulRhsPoly) + fastChecksum (checksumIterations := checksumIterations) + let canonicalNttFast ← runTimed "univariate-mul-ntt-fast" "CPolynomial" (univariateMulNttMethod "NTTFast.fastMulImpl") - "KoalaBear.Field" - univariateMulShape preset warmup nttFastMeasured - (fun _ ↦ CPolynomial.NTTFast.fastMulImpl koalaBearMulNttDomain mulLhsPoly mulRhsPoly) - (checksumCPolynomial checksumKoalaBear) (checksumIterations := checksumIterations) - let koalaBearFastMulNttFast ← runTimed + canonicalField.id univariateMulShape preset warmup nttFastMeasured + (fun _ ↦ + CPolynomial.NTTFast.fastMulImpl koalaBearMulNttDomain mulLhsPoly mulRhsPoly) + canonicalChecksum (checksumIterations := checksumIterations) + let fastNttFast ← runTimed "univariate-mul-ntt-fast-koalabear-fast" "CPolynomial" - (univariateMulNttMethod "NTTFast.fastMulImpl") "KoalaBear.Fast.Field" + (univariateMulNttMethod "NTTFast.fastMulImpl") fastField.id univariateMulShape preset warmup fastNttFastMeasured - (fun _ ↦ CPolynomial.NTTFast.fastMulImpl koalaBearFastMulNttDomain fastMulLhsPoly - fastMulRhsPoly) - (checksumCPolynomial checksumKoalaBearFast) (checksumIterations := checksumIterations) - let koalaBearMulNttFastPlanRecord ← runTimed + (fun _ ↦ CPolynomial.NTTFast.fastMulImpl koalaBearFastMulNttDomain + fastMulLhsPoly fastMulRhsPoly) + fastChecksum (checksumIterations := checksumIterations) + let canonicalNttFastPlan ← runTimed "univariate-mul-ntt-fast-plan" "CPolynomial" (univariateMulNttMethod "NTTFast.Plan.fastMulImpl, cached twiddles, mixed radix-4 DIF/DIT, dual forward") - "KoalaBear.Field" - univariateMulShape preset warmup nttFastPlanMeasured - (fun _ ↦ CPolynomial.NTTFast.Plan.fastMulImpl koalaBearMulNttFastPlan mulLhsPoly - mulRhsPoly) - (checksumCPolynomial checksumKoalaBear) (checksumIterations := checksumIterations) - let koalaBearFastMulNttFastPlanRecord ← runTimed + canonicalField.id univariateMulShape preset warmup nttFastPlanMeasured + (fun _ ↦ CPolynomial.NTTFast.Plan.fastMulImpl canonicalPlan mulLhsPoly mulRhsPoly) + canonicalChecksum (checksumIterations := checksumIterations) + let fastNttFastPlan ← runTimed "univariate-mul-ntt-fast-plan-fast" "CPolynomial" (univariateMulNttMethod "NTTFast.Plan.fastMulImpl, cached twiddles, mixed radix-4 DIF/DIT, dual forward") - "KoalaBear.Fast.Field" - univariateMulShape preset warmup fastNttFastPlanMeasured - (fun _ ↦ CPolynomial.NTTFast.Plan.fastMulImpl koalaBearFastMulNttFastPlan - fastMulLhsPoly fastMulRhsPoly) - (checksumCPolynomial checksumKoalaBearFast) (checksumIterations := checksumIterations) + fastField.id univariateMulShape preset warmup fastNttFastPlanMeasured + (fun _ ↦ CPolynomial.NTTFast.Plan.fastMulImpl fastPlan fastMulLhsPoly fastMulRhsPoly) + fastChecksum (checksumIterations := checksumIterations) pure ({ groupKey := "univariate-mul-koalabear", title := "Univariate multiplication (KoalaBear)", - records := #[koalaBearMulNaive, koalaBearMulNtt, koalaBearMulNttFast, - koalaBearMulNttFastPlanRecord, koalaBearFastMulNaive, koalaBearFastMulNtt, - koalaBearFastMulNttFast, koalaBearFastMulNttFastPlanRecord] + records := #[canonicalNaive, canonicalNtt, canonicalNttFast, canonicalNttFastPlan, + fastNaive, fastNtt, fastNttFast, fastNttFastPlan] }, gen) /-- Runnable `CompPoly.Univariate.NTT.FastMul` benchmark tasks. -/ From 694893f8810d687876d992228a981e2adab2bc17 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 12:49:14 +0200 Subject: [PATCH 24/31] refactor(fields): simplify KoalaBear root proofs --- CompPoly/Fields/KoalaBear/Basic.lean | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/CompPoly/Fields/KoalaBear/Basic.lean b/CompPoly/Fields/KoalaBear/Basic.lean index e32de202..45c6bf81 100644 --- a/CompPoly/Fields/KoalaBear/Basic.lean +++ b/CompPoly/Fields/KoalaBear/Basic.lean @@ -126,7 +126,6 @@ def twoAdicGenerators : List Field := /-! Statements requested by the Python spec translation. -/ -set_option maxRecDepth 4096 in /-- Fermat-style inversion in `ZMod fieldSize`. -/ lemma inv_eq_pow (a : Field) (ha : a ≠ 0) : a⁻¹ = a ^ (fieldSize - 2) := by have hcard : Fintype.card Field = fieldSize := ZMod.card fieldSize @@ -289,13 +288,11 @@ lemma twoAdicGenerators_order (bits : Fin (twoAdicity + 1)) : /-- Primitive generator used by the smooth field-root splitter. -/ def primitiveRoot : Field := (3 : Field) -set_option maxRecDepth 100000 in /-- `primitiveRoot ^ 127` is the maximal two-adic generator. -/ private lemma primitiveRoot_pow_127_eq_twoAdicGenerator : - primitiveRoot ^ 127 = - twoAdicGenerators[(⟨twoAdicity, by omega⟩ : Fin (twoAdicity + 1))] := by - unfold primitiveRoot twoAdicity - decide + primitiveRoot ^ 127 = twoAdicGenerators[twoAdicity] := by + norm_num [primitiveRoot, twoAdicity, twoAdicGenerators] + rfl /-- `primitiveRoot ^ 2^twoAdicity` is nontrivial. -/ private lemma primitiveRoot_pow_twoAdicity_ne_one : From 4fca1633f56489e1b725415d600e13ea1ce31036 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 14:11:49 +0200 Subject: [PATCH 25/31] refactor(fields): separate native Montgomery reduction --- CompPoly/Fields/Montgomery/Basic.lean | 44 +++-- CompPoly/Fields/Montgomery/Native32.lean | 124 +++++++++--- CompPoly/Fields/Montgomery/Native32Field.lean | 176 +++++------------- CompPoly/Fields/README.md | 4 +- 4 files changed, 182 insertions(+), 166 deletions(-) diff --git a/CompPoly/Fields/Montgomery/Basic.lean b/CompPoly/Fields/Montgomery/Basic.lean index 53205cbd..47336c85 100644 --- a/CompPoly/Fields/Montgomery/Basic.lean +++ b/CompPoly/Fields/Montgomery/Basic.lean @@ -18,28 +18,17 @@ Word-specific implementations refine these results in sibling modules. namespace Montgomery -/-- The Montgomery divisibility identity: if `(negInv * p) % R = R - 1` (i.e. -`negInv = -p⁻¹ mod R`), then `R ∣ x + ((x mod R)·negInv mod R)·p` for every `x`. This is -what makes Montgomery reduction integer-valued. -/ -theorem dvd_add (R p negInv : ℕ) (hR : 0 < R) - (hnegInv : negInv * p % R = R - 1) (x : ℕ) : - R ∣ x + ((x % R * negInv) % R) * p := by - rw [Nat.dvd_iff_mod_eq_zero] - rw [Nat.add_mod, Nat.mod_mul_mod (x % R * negInv) p R, Nat.mul_assoc] - rw [← Nat.mul_mod_mod, hnegInv, Nat.add_mod_mod, add_comm, ← Nat.mul_add_one] - rw [show R - 1 + 1 = R by omega, Nat.mul_mod_left] - -/-- The quotient before the final conditional subtraction in Montgomery reduction. -/ -def reduceNatQuotient (R p negInv x : ℕ) : ℕ := - let m := (x % R * negInv) % R - (x + m * p) / R - /-- Natural-number Montgomery reduction used to specify the native-word reducer. -/ def reduceNat (R p negInv x : ℕ) : ℕ := let m := (x % R * negInv) % R let u := (x + m * p) / R if u < p then u else u - p +/-- The quotient before the final conditional subtraction in Montgomery reduction. -/ +def reduceNatQuotient (R p negInv x : ℕ) : ℕ := + let m := (x % R * negInv) % R + (x + m * p) / R + /-- The pre-subtraction quotient is below twice the modulus. -/ theorem reduceNatQuotient_lt_two_mul (R p negInv x : ℕ) (hR : 0 < R) (hp : 0 < p) (hx : x < p * R) : @@ -56,6 +45,29 @@ theorem reduceNatQuotient_lt_two_mul (R p negInv x : ℕ) _ = 2 * p * R := by ring · exact hR +/-- Montgomery reduction returns a canonical representative. -/ +theorem reduceNat_lt (R p negInv x : ℕ) + (hR : 0 < R) (hp : 0 < p) (hx : x < p * R) : + reduceNat R p negInv x < p := by + change (if reduceNatQuotient R p negInv x < p then + reduceNatQuotient R p negInv x else reduceNatQuotient R p negInv x - p) < p + have hu := reduceNatQuotient_lt_two_mul R p negInv x hR hp hx + by_cases h : reduceNatQuotient R p negInv x < p + · rw [if_pos h] + exact h + · rw [if_neg h] + omega + +/-- The Montgomery divisibility identity: if `(negInv * p) % R = R - 1` (i.e. +`negInv = -p⁻¹ mod R`), then `R ∣ x + ((x mod R)·negInv mod R)·p` for every `x`. -/ +theorem dvd_add (R p negInv : ℕ) (hR : 0 < R) + (hnegInv : negInv * p % R = R - 1) (x : ℕ) : + R ∣ x + ((x % R * negInv) % R) * p := by + rw [Nat.dvd_iff_mod_eq_zero] + rw [Nat.add_mod, Nat.mod_mul_mod (x % R * negInv) p R, Nat.mul_assoc] + rw [← Nat.mul_mod_mod, hnegInv, Nat.add_mod_mod, add_comm, ← Nat.mul_add_one] + rw [show R - 1 + 1 = R by omega, Nat.mul_mod_left] + /-- The pre-subtraction quotient represents multiplication by `R⁻¹` in `ZMod p`. -/ theorem reduceNatQuotient_cast (R p negInv : ℕ) [Fact (Nat.Prime p)] (hR : 0 < R) (hnegInv : negInv * p % R = R - 1) (hRne : (R : ZMod p) ≠ 0) (x : ℕ) : diff --git a/CompPoly/Fields/Montgomery/Native32.lean b/CompPoly/Fields/Montgomery/Native32.lean index e8dbfea6..d35b3363 100644 --- a/CompPoly/Fields/Montgomery/Native32.lean +++ b/CompPoly/Fields/Montgomery/Native32.lean @@ -9,54 +9,132 @@ import CompPoly.Fields.Montgomery.Basic /-! # Native 32-bit Montgomery Reduction -Native `UInt32` Montgomery reduction with radix `2 ^ 32`, connected to the generic -natural-number specification in `Montgomery.Basic`. +Raw word operations for Montgomery reduction with radix `2 ^ 32`. -/ namespace Montgomery namespace Native32 -/-- The native pre-subtraction quotient in 32-bit Montgomery reduction. -/ +/-- Montgomery reduction. -/ @[inline] +def reduceRaw (p32 : UInt32) (p64 : UInt64) (negInv : UInt32) (x : UInt64) : UInt32 := + let m := (x.toUInt32 * negInv).toUInt64 + let u := ((x + m * p64) >>> 32).toUInt32 + if u < p32 then u else u - p32 + +/-- The native pre-subtraction quotient in 32-bit Montgomery reduction. -/ def reduceQuotient (negInv : UInt32) (p x : UInt64) : UInt32 := ((x + (x.toUInt32 * negInv).toUInt64 * p) >>> 32).toUInt32 +/-- Conditional subtraction of the modulus in 32-bit Montgomery reduction. -/ +def conditionalSubtract (p32 : UInt32) (u : UInt32) : UInt32 := + if u < p32 then u else u - p32 + +variable {modulus : ℕ} {p32 negInv u : UInt32} {p64 x : UInt64} + +theorem reduceRaw_eq_conditionalSubtract : + reduceRaw p32 p64 negInv x = + conditionalSubtract p32 (reduceQuotient negInv p64 x) := rfl + /-- The native quotient agrees with the `Nat`-level Montgomery quotient. -/ -theorem reduceQuotient_toNat (negInv : UInt32) (p : UInt64) - (hp_pos : 0 < p.toNat) (hbound : p.toNat < 2 ^ 31) - (x : UInt64) (h : x.toNat < p.toNat * 2 ^ 32) : - (reduceQuotient negInv p x).toNat = - reduceNatQuotient (2 ^ 32) p.toNat negInv.toNat x.toNat := by +theorem reduceQuotient_toNat (hp_pos : 0 < p64.toNat) (hbound : p64.toNat < 2 ^ 31) + (h : x.toNat < p64.toNat * 2 ^ 32) : + (reduceQuotient negInv p64 x).toNat = + reduceNatQuotient (2 ^ 32) p64.toNat negInv.toNat x.toNat := by simp only [UInt64.toNat_shiftRight, UInt64.toNat_toUInt32, UInt64.toNat_add, UInt64.toNat_mul, UInt32.toNat_toUInt64, UInt32.toNat_mul, UInt64.toNat_ofNat, reduceQuotient, reduceNatQuotient, Nat.shiftRight_eq_div_pow] let mNat := x.toNat * negInv.toNat % 2 ^ 32 have hm_lt : mNat < 2 ^ 32 := Nat.mod_lt _ (by decide) - have hsum_lt : x.toNat + mNat * p.toNat < 2 ^ 64 := by - have hprod_lt : mNat * p.toNat < p.toNat * 2 ^ 32 := by + have hsum_lt : x.toNat + mNat * p64.toNat < 2 ^ 64 := by + have hprod_lt : mNat * p64.toNat < p64.toNat * 2 ^ 32 := by have := Nat.mul_lt_mul_of_pos_right hm_lt hp_pos simpa [Nat.mul_comm] using this calc - x.toNat + mNat * p.toNat < - p.toNat * 2 ^ 32 + p.toNat * 2 ^ 32 := Nat.add_lt_add h hprod_lt - _ = 2 * p.toNat * 2 ^ 32 := by ring + x.toNat + mNat * p64.toNat < + p64.toNat * 2 ^ 32 + p64.toNat * 2 ^ 32 := Nat.add_lt_add h hprod_lt + _ = 2 * p64.toNat * 2 ^ 32 := by ring _ < 2 ^ 64 := by omega norm_num [UInt32.size] - change ((x.toNat + mNat * p.toNat) % 2 ^ 64 / 2 ^ 32) % 2 ^ 32 = - (x.toNat + mNat * p.toNat) / 2 ^ 32 + change ((x.toNat + mNat * p64.toNat) % 2 ^ 64 / 2 ^ 32) % 2 ^ 32 = + (x.toNat + mNat * p64.toNat) / 2 ^ 32 rw [Nat.mod_eq_of_lt hsum_lt, Nat.mod_eq_of_lt] rw [Nat.div_lt_iff_lt_mul] · exact hsum_lt · decide -/-- The native Montgomery quotient stays below twice the modulus, so one conditional -subtraction canonicalizes it. -/ -theorem reduceQuotient_toNat_lt_two_mul (negInv : UInt32) (p : UInt64) - (hp_pos : 0 < p.toNat) (hbound : p.toNat < 2 ^ 31) - (x : UInt64) (h : x.toNat < p.toNat * 2 ^ 32) : - (reduceQuotient negInv p x).toNat < 2 * p.toNat := by - rw [reduceQuotient_toNat negInv p hp_pos hbound x h] - apply reduceNatQuotient_lt_two_mul <;> simp_all +theorem conditionalSubtract_toNat : + (conditionalSubtract p32 u).toNat = + if u.toNat < p32.toNat then u.toNat else u.toNat - p32.toNat := by + simp only [conditionalSubtract, UInt32.lt_iff_toNat_lt] + by_cases hx : u.toNat < p32.toNat + · simp only [if_pos hx] + · have hp_le_x : p32 ≤ u := by + rw [UInt32.le_iff_toNat_le] + omega + simp only [if_neg hx, UInt32.toNat_sub_of_le _ _ hp_le_x] + +/-- Native Montgomery reduction agrees with the natural-number specification. -/ +theorem reduceRaw_toNat (hp32 : p32.toNat = modulus) (hp64 : p64.toNat = modulus) + (hp_pos : 0 < modulus) (hp_bound : modulus < 2 ^ 31) + (h : x.toNat < modulus * 2 ^ 32) : + (reduceRaw p32 p64 negInv x).toNat = + reduceNat (2 ^ 32) modulus negInv.toNat x.toNat := by + rw [reduceRaw_eq_conditionalSubtract, conditionalSubtract_toNat] + rw [reduceQuotient_toNat + (by simpa only [hp64] using hp_pos) + (by simpa only [hp64] using hp_bound) + (by simpa only [hp64] using h)] + rw [hp32, hp64] + rfl + +theorem conditionalSubtract_lt (h : u.toNat < 2 * p32.toNat) : + (conditionalSubtract p32 u).toNat < p32.toNat := by + simp only [conditionalSubtract, UInt32.lt_iff_toNat_lt] + by_cases hx : u.toNat < p32.toNat + · rw [if_pos hx] + exact hx + · have hp_le_x : p32 ≤ u := by + rw [UInt32.le_iff_toNat_le] + omega + rw [if_neg hx, UInt32.toNat_sub_of_le _ _ hp_le_x] + omega + +theorem conditionalSubtract_cast : + ((conditionalSubtract p32 u).toNat : ZMod p32.toNat) = + (u.toNat : ZMod p32.toNat) := by + simp only [conditionalSubtract] + by_cases hx : u < p32 + · rw [if_pos hx] + · have hp_le_x : p32 ≤ u := by + rw [UInt32.le_iff_toNat_le] + rw [UInt32.lt_iff_toNat_lt] at hx + exact Nat.le_of_not_gt hx + rw [if_neg hx, UInt32.toNat_sub_of_le _ _ hp_le_x] + rw [Nat.cast_sub (by + rw [UInt32.le_iff_toNat_le] at hp_le_x + exact hp_le_x)] + simp + +theorem reduceRaw_lt (hp : p32.toNat = p64.toNat) (hp_pos : 0 < p64.toNat) + (hp_bound : p64.toNat < 2 ^ 31) + (h : x.toNat < p64.toNat * 2 ^ 32) : + (reduceRaw p32 p64 negInv x).toNat < p32.toNat := by + rw [reduceRaw_toNat hp rfl hp_pos hp_bound h, hp] + exact reduceNat_lt (2 ^ 32) p64.toNat negInv.toNat x.toNat + (by decide) hp_pos h + +theorem reduceRaw_cast [Fact (Nat.Prime modulus)] + (hp32 : p32.toNat = modulus) (hp64 : p64.toNat = modulus) + (hp_pos : 0 < modulus) (hp_bound : modulus < 2 ^ 31) + (hnegInv : negInv.toNat * modulus % 2 ^ 32 = 2 ^ 32 - 1) + (hRne : ((2 ^ 32 : ℕ) : ZMod modulus) ≠ 0) + (h : x.toNat < modulus * 2 ^ 32) : + ((reduceRaw p32 p64 negInv x).toNat : ZMod modulus) = + (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by + rw [reduceRaw_toNat hp32 hp64 hp_pos hp_bound h] + exact reduceNat_cast (2 ^ 32) modulus negInv.toNat + (by decide) hnegInv hRne x.toNat end Native32 end Montgomery diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index e2d13853..f93f5b62 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -8,37 +8,18 @@ import CompPoly.Fields.Basic import CompPoly.Fields.Montgomery.Native32 import Mathlib.Algebra.Field.TransferInstance import Mathlib.FieldTheory.Finite.Basic +import Mathlib.Tactic.Linarith /-! -# Fast 32-bit-word prime fields — shared implementation - -A `BabyBear`-style and `KoalaBear`-style fast field differ only in a handful of word -constants; their definitions, the proofs about them, and the resulting algebraic instances -are otherwise identical. This module captures that common content **once**, parameterized -by a `Mont32Field` instance that supplies the per-field data. - -* `Mont32Field modulus` bundles the native-word forms of the prime modulus, the Montgomery - constants, and the small `decide`-checkable numeric/`ZMod` facts the proofs consume. - Everything except the five word constants is spec-level and erased at codegen. -* `FastField modulus` is the fast carrier `{ x : UInt32 // x.toNat < modulus }`. - At runtime it erases to `UInt32`. -* The executable `def`s are `@[inline]`/`@[specialize]`, so once a concrete instance is - fixed the instance projections fold to literals and the compiled code is identical to a - hand-written monomorphic version — no `Mont32Field` dictionary survives to runtime. - -The radix-generic number theory lives in `CompPoly.Fields.Montgomery.Basic` and the -`R = 2^32` word bridge in `CompPoly.Fields.Montgomery.Native32`. A concrete fast field is -then just a `Mont32Field` instance plus thin re-export shims. +# Fast 32-bit Montgomery Fields + +The bounded carrier, conversions, arithmetic, and field instances built on `Native32` reduction. -/ namespace Montgomery namespace Native32 -/-- Per-field data for a fast 32-bit-word Montgomery prime field. - -The five word constants (`modulus32`, `modulus64`, `rModModulus`, `r2ModModulus`, -`montgomeryNegInv`) are the only runtime data; the remaining fields are `Prop`s and -erased at codegen. -/ +/-- Per-field data for a fast 32-bit Montgomery field. -/ class Mont32Field (modulus : ℕ) where /-- `modulus` is prime. -/ prime : modulus.Prime @@ -62,6 +43,7 @@ class Mont32Field (modulus : ℕ) where (montgomeryNegInv.toNat * modulus) % 2 ^ 32 = 2 ^ 32 - 1 := by decide namespace Mont32Field + instance factPrime (modulus : ℕ) [P : Mont32Field modulus] : Fact (Nat.Prime modulus) := ⟨P.prime⟩ @@ -90,6 +72,7 @@ theorem r2ModModulus_lt_modulus {modulus : ℕ} [P : Mont32Field modulus] : P.r2ModModulus.toNat < modulus := by rw [P.r2ModModulus_toNat] exact Nat.mod_lt _ P.modulus_pos + end Mont32Field /-- The fast carrier for a prime modulus: a native word below `modulus`, @@ -107,115 +90,53 @@ instance : NeZero modulus := ⟨P.modulus_pos.ne'⟩ /-! ## Montgomery reduction -/ -/-- Reduce a native word known to be below twice the prime. -/ -@[inline] -def reduceUInt32Lt2ModulusRaw (modulus : ℕ) [P : Mont32Field modulus] - (x : UInt32) : UInt32 := - if x < P.modulus32 then x else x - P.modulus32 - -theorem reduceUInt32Lt2ModulusRaw_lt (x : UInt32) (h : x.toNat < 2 * modulus) : - (reduceUInt32Lt2ModulusRaw modulus x).toNat < modulus := by - simp only [reduceUInt32Lt2ModulusRaw, UInt32.lt_iff_toNat_lt, - Mont32Field.modulus32_toNat] - by_cases hx : x.toNat < modulus - · rw [if_pos hx] - exact hx - · have hmod_le_x : P.modulus32 ≤ x := by - rw [UInt32.le_iff_toNat_le, Mont32Field.modulus32_toNat] - omega - rw [if_neg hx, UInt32.toNat_sub_of_le _ _ hmod_le_x, Mont32Field.modulus32_toNat] - omega - /-- Reduce a native word known to be below twice the prime. -/ @[inline] def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * modulus) : FastField modulus := - ⟨reduceUInt32Lt2ModulusRaw modulus x, reduceUInt32Lt2ModulusRaw_lt x h⟩ + ⟨conditionalSubtract P.modulus32 x, by + simpa only [P.modulus32_toNat] using + conditionalSubtract_lt (p32 := P.modulus32) (u := x) (by + simpa only [P.modulus32_toNat] using h)⟩ theorem reduceUInt32Lt2Modulus_cast (x : UInt32) (h : x.toNat < 2 * modulus) : ((reduceUInt32Lt2Modulus (modulus := modulus) x h).val.toNat : ZMod modulus) = (x.toNat : ZMod modulus) := by - change ((reduceUInt32Lt2ModulusRaw modulus x).toNat : ZMod modulus) = + change ((conditionalSubtract P.modulus32 x).toNat : ZMod modulus) = (x.toNat : ZMod modulus) - unfold reduceUInt32Lt2ModulusRaw - by_cases hx : x < P.modulus32 - · rw [if_pos hx] - · have hmod_le_x : P.modulus32 ≤ x := by - rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] - rw [UInt32.lt_iff_toNat_lt, P.modulus32_toNat] at hx - exact Nat.le_of_not_gt hx - rw [if_neg hx] - rw [UInt32.toNat_sub_of_le _ _ hmod_le_x, P.modulus32_toNat] - rw [Nat.cast_sub (by - rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] at hmod_le_x - exact hmod_le_x)] - simp - -/-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ -@[inline] -def montgomeryReduceRaw (modulus : ℕ) [P : Mont32Field modulus] - (x : UInt64) : UInt32 := - reduceUInt32Lt2ModulusRaw modulus - (reduceQuotient P.montgomeryNegInv P.modulus64 x) - -theorem montgomeryReduceRaw_lt (x : UInt64) - (h : x.toNat < modulus * 2 ^ 32) : - (montgomeryReduceRaw modulus x).toNat < modulus := by - have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by - rw [P.modulus64_toNat] - have hp := P.two_mul_modulus_lt_two_pow_32 - omega - unfold montgomeryReduceRaw - exact reduceUInt32Lt2ModulusRaw_lt _ - (by - simpa only [P.modulus64_toNat] using - reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 - (by simpa only [P.modulus64_toNat] using P.modulus_pos) - hmodulus_bound - x (by simpa only [P.modulus64_toNat] using h)) + have hraw := conditionalSubtract_cast (p32 := P.modulus32) (u := x) + rw [P.modulus32_toNat] at hraw + exact hraw /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] def montgomeryReduce (x : UInt64) (h : x.toNat < modulus * 2 ^ 32) : FastField modulus := - ⟨montgomeryReduceRaw modulus x, montgomeryReduceRaw_lt x h⟩ + ⟨reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv x, by + simpa only [P.modulus32_toNat] using + reduceRaw_lt (p32 := P.modulus32) (p64 := P.modulus64) + (negInv := P.montgomeryNegInv) (x := x) + (by rw [P.modulus32_toNat, P.modulus64_toNat]) + (by simpa only [P.modulus64_toNat] using P.modulus_pos) + (by + rw [P.modulus64_toNat] + have hp := P.two_mul_modulus_lt_two_pow_32 + omega) + (by simpa only [P.modulus64_toNat] using h)⟩ theorem montgomeryReduce_cast (x : UInt64) (h : x.toNat < modulus * 2 ^ 32) : ((montgomeryReduce (modulus := modulus) x h).val.toNat : ZMod modulus) = (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - have hmodulus_bound : P.modulus64.toNat < 2 ^ 31 := by - rw [P.modulus64_toNat] - have hp := P.two_mul_modulus_lt_two_pow_32 - omega - change ((montgomeryReduceRaw modulus x).toNat : ZMod modulus) = - (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - unfold montgomeryReduceRaw - let u := reduceQuotient P.montgomeryNegInv P.modulus64 x - change ((reduceUInt32Lt2ModulusRaw modulus u).toNat : ZMod modulus) = - (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - have hred := reduceUInt32Lt2Modulus_cast (modulus := modulus) u + exact reduceRaw_cast (modulus := modulus) (p32 := P.modulus32) + (p64 := P.modulus64) (negInv := P.montgomeryNegInv) (x := x) + P.modulus32_toNat P.modulus64_toNat P.modulus_pos (by - simpa only [P.modulus64_toNat] using - reduceQuotient_toNat_lt_two_mul P.montgomeryNegInv P.modulus64 - (by simpa only [P.modulus64_toNat] using P.modulus_pos) - hmodulus_bound - x (by simpa only [P.modulus64_toNat] using h)) - change ((reduceUInt32Lt2ModulusRaw modulus u).toNat : ZMod modulus) = - (u.toNat : ZMod modulus) at hred - rw [hred] - change (u.toNat : ZMod modulus) = - (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - rw [show u.toNat = reduceNatQuotient (2 ^ 32) modulus P.montgomeryNegInv.toNat x.toNat by - simpa only [u, P.modulus64_toNat] using - reduceQuotient_toNat P.montgomeryNegInv P.modulus64 - (by simpa only [P.modulus64_toNat] using P.modulus_pos) - hmodulus_bound - x (by simpa only [P.modulus64_toNat] using h)] - exact reduceNatQuotient_cast (2 ^ 32) modulus P.montgomeryNegInv.toNat - (by decide) P.montgomeryNegInv_mul_modulus_mod_two_pow_32 - P.two_pow_32_ne_zero_in_field x.toNat + have hp := P.two_mul_modulus_lt_two_pow_32 + omega) + P.montgomeryNegInv_mul_modulus_mod_two_pow_32 + P.two_pow_32_ne_zero_in_field h /-! ## Conversions -/ @@ -308,10 +229,11 @@ open FastField theorem toNat_lt_modulus (x : FastField modulus) : toNat x < modulus := by unfold FastField.toNat FastField.toUInt32 - change (montgomeryReduceRaw modulus x.val.toUInt64).toNat < modulus - exact montgomeryReduceRaw_lt (modulus := modulus) x.val.toUInt64 (by + change (reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + x.val.toUInt64).toNat < modulus + exact (montgomeryReduce (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.modulus_pos]) + nlinarith [x.property, P.modulus_pos])).property theorem toField_eq_raw_mul_inv (x : FastField modulus) : toField x = @@ -320,9 +242,11 @@ theorem toField_eq_raw_mul_inv (x : FastField modulus) : have hred := montgomeryReduce_cast (modulus := modulus) x.val.toUInt64 (by rw [UInt32.toNat_toUInt64] nlinarith [x.property, P.modulus_pos]) - change ((montgomeryReduceRaw modulus x.val.toUInt64).toNat : ZMod modulus) = + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + x.val.toUInt64).toNat : ZMod modulus) = (x.val.toUInt64.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((montgomeryReduceRaw modulus x.val.toUInt64).toNat : ZMod modulus) = + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + x.val.toUInt64).toNat : ZMod modulus) = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ rw [hred] rw [UInt32.toNat_toUInt64] @@ -355,11 +279,11 @@ theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < modulus) : nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_modulus]) - change ((montgomeryReduceRaw modulus + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((montgomeryReduceRaw modulus + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) rw [hred] @@ -402,11 +326,13 @@ theorem reduceUInt64_raw_cast (x : UInt64) : nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [P.r2ModModulus_lt_modulus]) - change ((montgomeryReduceRaw modulus (y * P.r2ModModulus.toUInt64)).toNat : + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + (y * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = ((y * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((montgomeryReduceRaw modulus (y * P.r2ModModulus.toUInt64)).toNat : + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + (y * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) rw [hred] @@ -668,9 +594,9 @@ theorem toField_add (x y : FastField modulus) : toField (x + y) = toField x + to rw [UInt32.toNat_add] exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by have hx := x.property; have hy := y.property; omega)) - change ((reduceUInt32Lt2ModulusRaw modulus (x.val + y.val)).toNat : ZMod modulus) = + change ((conditionalSubtract P.modulus32 (x.val + y.val)).toNat : ZMod modulus) = ((x.val + y.val).toNat : ZMod modulus) at hred - change ((reduceUInt32Lt2ModulusRaw modulus (x.val + y.val)).toNat : ZMod modulus) * + change ((conditionalSubtract P.modulus32 (x.val + y.val)).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ + (y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ @@ -774,11 +700,11 @@ theorem toField_mul (x y : FastField modulus) : toField (x * y) = toField x * to nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] nlinarith [x.property, y.property, P.modulus_lt_two_pow_32, P.modulus_pos]) - change ((montgomeryReduceRaw modulus + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv (x.val.toUInt64 * y.val.toUInt64)).toNat : ZMod modulus) = ((x.val.toUInt64 * y.val.toUInt64).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((montgomeryReduceRaw modulus + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv (x.val.toUInt64 * y.val.toUInt64)).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ * diff --git a/CompPoly/Fields/README.md b/CompPoly/Fields/README.md index f7718c2a..6830d132 100644 --- a/CompPoly/Fields/README.md +++ b/CompPoly/Fields/README.md @@ -20,8 +20,8 @@ This directory contains formally verified field infrastructure used in zero-know | **KoalaBear/Fast.lean** | KoalaBear-namespaced API over the shared fast-field implementation (`Montgomery/Native32Field.lean`): thin wrappers forwarding the native `UInt32` Montgomery-residue operations and their `KoalaBear.Field` equivalence (`@[simp]`) lemmas. | | **Mersenne.lean** | \(2^{31} - 1\) — Circle STARKs. | | **Montgomery/Basic.lean** | Radix-generic Montgomery reduction, field-agnostic number theory shared by the fast prime fields. | -| **Montgomery/Native32.lean** | `UInt32 × UInt64`, radix `R = 2^32` word-level Montgomery bridge over the generic core. | -| **Montgomery/Native32Field.lean** | Shared 32-bit-word fast-field implementation over the word bridge: the `Mont32Field` per-field data class, the `FastField` carrier, every field operation, the algebraic instances, and the `toField`/`ofField` correctness bridge — written once and instantiated by both BabyBear and KoalaBear. | +| **Montgomery/Native32.lean** | Raw `UInt32`/`UInt64` Montgomery reduction over explicit word constants, including bounds and correctness. | +| **Montgomery/Native32Field.lean** | Per-field parameters, the shared `FastField` carrier, arithmetic, instances, and canonical-field bridge. | | **Secp256k1.lean** | Base and scalar fields for the Secp256k1 curve (used in Bitcoin/Ethereum). | ## Binary-field modules From 62611d286b2a1956b37acd36d21cac6a2ddfda9c Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 15:38:18 +0200 Subject: [PATCH 26/31] refactor(fields): streamline native Montgomery field --- CompPoly/Fields/BabyBear/Fast.lean | 26 +- CompPoly/Fields/KoalaBear/Fast.lean | 26 +- CompPoly/Fields/Montgomery/Native32Field.lean | 654 +++++++----------- 3 files changed, 265 insertions(+), 441 deletions(-) diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index a3a0b003..2c733f0e 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -36,11 +36,6 @@ abbrev Field : Type := FastField BabyBear.fieldSize /-! ## Conversions -/ -/-- Reduce a `UInt64` modulo the BabyBear prime and return a Montgomery fast element. -/ -@[inline] -def reduceUInt64 (x : UInt64) : Field := - Montgomery.Native32.reduceUInt64 BabyBear.fieldSize x - /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (x : UInt32) : Field := @@ -49,19 +44,7 @@ def ofUInt32 (x : UInt32) : Field := /-- Convert from the canonical `ZMod` BabyBear field into fast Montgomery form. -/ @[inline] def ofField (x : BabyBear.Field) : Field := - Montgomery.Native32.ofField x - -/-- Reducing a `UInt64` gives the canonical natural residue modulo BabyBear. -/ -@[simp] -theorem toNat_reduceUInt64 (x : UInt64) : - (reduceUInt64 x).toNat = x.toNat % BabyBear.fieldSize := - Montgomery.Native32.toNat_reduceUInt64 x - -/-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ -@[simp] -theorem toField_reduceUInt64 (x : UInt64) : - (reduceUInt64 x).toField = (x.toNat : BabyBear.Field) := - Montgomery.Native32.toField_reduceUInt64 x + Montgomery.Native32.FastField.ofField x /-! ## Arithmetic -/ @@ -89,9 +72,6 @@ def square (x : Field) : Field := Montgomery.Native32.square x @[inline] def pow (x : Field) (n : ℕ) : Field := Montgomery.Native32.pow x n -/-- Fermat exponent used for inversion in the BabyBear prime field. -/ -def invExponent : ℕ := Montgomery.Native32.invExponent BabyBear.fieldSize - /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ @[inline] def inv (x : Field) : Field := Montgomery.Native32.inv x @@ -149,12 +129,12 @@ theorem toField_mul (x y : Field) : toField (x * y) = toField x * toField y := /-- Applying `ringEquiv` is the same as interpreting a fast value canonically. -/ @[simp] theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := - Montgomery.Native32.ringEquiv_apply x + Montgomery.Native32.ringEquiv_apply /-- Applying the inverse `ringEquiv` is conversion into fast Montgomery form. -/ @[simp] theorem ringEquiv_symm_apply (x : BabyBear.Field) : ringEquiv.symm x = ofField x := - Montgomery.Native32.ringEquiv_symm_apply x + Montgomery.Native32.ringEquiv_symm_apply /-- Fast squaring agrees with multiplication by itself in the canonical field. -/ @[simp] diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index 24aa394c..ea3330bb 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -36,11 +36,6 @@ abbrev Field : Type := FastField KoalaBear.fieldSize /-! ## Conversions -/ -/-- Reduce a `UInt64` modulo the KoalaBear prime and return a Montgomery fast element. -/ -@[inline] -def reduceUInt64 (x : UInt64) : Field := - Montgomery.Native32.reduceUInt64 KoalaBear.fieldSize x - /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (x : UInt32) : Field := @@ -49,19 +44,7 @@ def ofUInt32 (x : UInt32) : Field := /-- Convert from the canonical `ZMod` KoalaBear field into fast Montgomery form. -/ @[inline] def ofField (x : KoalaBear.Field) : Field := - Montgomery.Native32.ofField x - -/-- Reducing a `UInt64` gives the canonical natural residue modulo KoalaBear. -/ -@[simp] -theorem toNat_reduceUInt64 (x : UInt64) : - (reduceUInt64 x).toNat = x.toNat % KoalaBear.fieldSize := - Montgomery.Native32.toNat_reduceUInt64 x - -/-- Reducing a `UInt64` agrees with casting that word into the canonical field. -/ -@[simp] -theorem toField_reduceUInt64 (x : UInt64) : - (reduceUInt64 x).toField = (x.toNat : KoalaBear.Field) := - Montgomery.Native32.toField_reduceUInt64 x + Montgomery.Native32.FastField.ofField x /-! ## Arithmetic -/ @@ -89,9 +72,6 @@ def square (x : Field) : Field := Montgomery.Native32.square x @[inline] def pow (x : Field) (n : ℕ) : Field := Montgomery.Native32.pow x n -/-- Fermat exponent used for inversion in the KoalaBear prime field. -/ -def invExponent : ℕ := Montgomery.Native32.invExponent KoalaBear.fieldSize - /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ @[inline] def inv (x : Field) : Field := Montgomery.Native32.inv x @@ -149,12 +129,12 @@ theorem toField_mul (x y : Field) : toField (x * y) = toField x * toField y := /-- Applying `ringEquiv` is the same as interpreting a fast value canonically. -/ @[simp] theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := - Montgomery.Native32.ringEquiv_apply x + Montgomery.Native32.ringEquiv_apply /-- Applying the inverse `ringEquiv` is conversion into fast Montgomery form. -/ @[simp] theorem ringEquiv_symm_apply (x : KoalaBear.Field) : ringEquiv.symm x = ofField x := - Montgomery.Native32.ringEquiv_symm_apply x + Montgomery.Native32.ringEquiv_symm_apply /-- Fast squaring agrees with multiplication by itself in the canonical field. -/ @[simp] diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index f93f5b62..1363005c 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -36,31 +36,49 @@ class Mont32Field (modulus : ℕ) where modulus32_toNat : modulus32.toNat = modulus := by decide modulus64_toNat : modulus64.toNat = modulus := by decide two_lt_modulus : 2 < modulus := by decide - two_mul_modulus_lt_two_pow_32 : 2 * modulus < 2 ^ 32 := by decide + modulus_lt_two_pow_31 : modulus < 2 ^ 31 := by decide rModModulus_toNat : rModModulus.toNat = 2 ^ 32 % modulus := by decide r2ModModulus_toNat : r2ModModulus.toNat = (2 ^ 32) ^ 2 % modulus := by decide montgomeryNegInv_mul_modulus_mod_two_pow_32 : (montgomeryNegInv.toNat * modulus) % 2 ^ 32 = 2 ^ 32 - 1 := by decide +attribute [simp] Mont32Field.modulus32_toNat Mont32Field.modulus64_toNat + Mont32Field.rModModulus_toNat Mont32Field.r2ModModulus_toNat + Mont32Field.modulus_lt_two_pow_31 + +attribute [-simp] Nat.reducePow + +/-- The fast carrier for a prime modulus: a native word below `modulus`, +interpreted as a Montgomery residue. At runtime this erases to `UInt32`. -/ +def FastField (modulus : ℕ) [Mont32Field modulus] : Type := + { x : UInt32 // x.toNat < modulus } + +instance (modulus : ℕ) [Mont32Field modulus] : DecidableEq (FastField modulus) := + inferInstanceAs (DecidableEq { x : UInt32 // x.toNat < modulus }) + +section +variable {modulus : ℕ} [P : Mont32Field modulus] + namespace Mont32Field instance factPrime (modulus : ℕ) [P : Mont32Field modulus] : Fact (Nat.Prime modulus) := ⟨P.prime⟩ -theorem modulus_pos {modulus : ℕ} [P : Mont32Field modulus] : 0 < modulus := by - exact Nat.zero_lt_of_lt P.two_lt_modulus +@[simp] +theorem modulus_pos : 0 < modulus := Nat.zero_lt_of_lt P.two_lt_modulus + +@[simp] theorem modulus32_pos : 0 < P.modulus32.toNat := by simp +@[simp] theorem modulus64_pos : 0 < P.modulus64.toNat := by simp +@[simp] theorem modulus32_lt_two_pow_31 : P.modulus32.toNat < 2 ^ 31 := by simp +@[simp] theorem modulus64_lt_two_pow_31 : P.modulus64.toNat < 2 ^ 31 := by simp -theorem modulus_lt_two_pow_32 {modulus : ℕ} [P : Mont32Field modulus] : - modulus < 2 ^ 32 := by - have h := P.two_mul_modulus_lt_two_pow_32 - omega +@[simp] theorem modulus_lt_two_pow_32 : modulus < 2 ^ 32 := by + linarith [P.modulus_lt_two_pow_31] -theorem modulus_sq_lt_two_pow_64 {modulus : ℕ} [P : Mont32Field modulus] : - modulus ^ 2 < 2 ^ 64 := by +theorem modulus_sq_lt_two_pow_64 : modulus ^ 2 < 2 ^ 64 := by nlinarith [P.modulus_lt_two_pow_32] -theorem two_pow_32_ne_zero_in_field {modulus : ℕ} [P : Mont32Field modulus] : - ((2 ^ 32 : ℕ) : ZMod modulus) ≠ 0 := by +theorem two_pow_32_ne_zero_in_field : ((2 ^ 32 : ℕ) : ZMod modulus) ≠ 0 := by have htwo : (2 : ZMod modulus) ≠ 0 := by intro h have hdvd : modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 modulus).mp h @@ -68,131 +86,57 @@ theorem two_pow_32_ne_zero_in_field {modulus : ℕ} [P : Mont32Field modulus] : rw [Nat.cast_pow] exact pow_ne_zero 32 htwo -theorem r2ModModulus_lt_modulus {modulus : ℕ} [P : Mont32Field modulus] : - P.r2ModModulus.toNat < modulus := by +theorem r2ModModulus_lt_modulus : P.r2ModModulus.toNat < modulus := by rw [P.r2ModModulus_toNat] exact Nat.mod_lt _ P.modulus_pos end Mont32Field -/-- The fast carrier for a prime modulus: a native word below `modulus`, -interpreted as a Montgomery residue. At runtime this erases to `UInt32`. -/ -def FastField (modulus : ℕ) [Mont32Field modulus] : Type := - { x : UInt32 // x.toNat < modulus } - -instance (modulus : ℕ) [Mont32Field modulus] : DecidableEq (FastField modulus) := - inferInstanceAs (DecidableEq { x : UInt32 // x.toNat < modulus }) - -section -variable {modulus : ℕ} [P : Mont32Field modulus] - instance : NeZero modulus := ⟨P.modulus_pos.ne'⟩ -/-! ## Montgomery reduction -/ - -/-- Reduce a native word known to be below twice the prime. -/ -@[inline] -def reduceUInt32Lt2Modulus (x : UInt32) (h : x.toNat < 2 * modulus) : - FastField modulus := - ⟨conditionalSubtract P.modulus32 x, by - simpa only [P.modulus32_toNat] using - conditionalSubtract_lt (p32 := P.modulus32) (u := x) (by - simpa only [P.modulus32_toNat] using h)⟩ - -theorem reduceUInt32Lt2Modulus_cast (x : UInt32) - (h : x.toNat < 2 * modulus) : - ((reduceUInt32Lt2Modulus (modulus := modulus) x h).val.toNat : ZMod modulus) = - (x.toNat : ZMod modulus) := by - change ((conditionalSubtract P.modulus32 x).toNat : ZMod modulus) = - (x.toNat : ZMod modulus) - have hraw := conditionalSubtract_cast (p32 := P.modulus32) (u := x) - rw [P.modulus32_toNat] at hraw - exact hraw +/-! ## Implementation -/ /-- Montgomery reduction for inputs known to be below `p * 2^32`. -/ @[inline] -def montgomeryReduce (x : UInt64) - (h : x.toNat < modulus * 2 ^ 32) : FastField modulus := - ⟨reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv x, by - simpa only [P.modulus32_toNat] using - reduceRaw_lt (p32 := P.modulus32) (p64 := P.modulus64) - (negInv := P.montgomeryNegInv) (x := x) - (by rw [P.modulus32_toNat, P.modulus64_toNat]) - (by simpa only [P.modulus64_toNat] using P.modulus_pos) - (by - rw [P.modulus64_toNat] - have hp := P.two_mul_modulus_lt_two_pow_32 - omega) - (by simpa only [P.modulus64_toNat] using h)⟩ - -theorem montgomeryReduce_cast (x : UInt64) - (h : x.toNat < modulus * 2 ^ 32) : - ((montgomeryReduce (modulus := modulus) x h).val.toNat : ZMod modulus) = - (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - exact reduceRaw_cast (modulus := modulus) (p32 := P.modulus32) - (p64 := P.modulus64) (negInv := P.montgomeryNegInv) (x := x) - P.modulus32_toNat P.modulus64_toNat P.modulus_pos - (by - have hp := P.two_mul_modulus_lt_two_pow_32 - omega) - P.montgomeryNegInv_mul_modulus_mod_two_pow_32 - P.two_pow_32_ne_zero_in_field h +def reduce (x : UInt64) (h : x.toNat < modulus * 2 ^ 32) : FastField modulus := + .mk (reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv x) <| by + nth_rw 4 [← P.modulus32_toNat] + apply reduceRaw_lt <;> simp_all -/-! ## Conversions -/ +/-- The zero fast element. -/ +def zero (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := .mk 0 <| by simp -/-- Build a fast element from a canonical natural representative. -/ -@[inline] -def ofCanonicalNat (n : ℕ) (_h : n < modulus) : FastField modulus := - montgomeryReduce (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans _h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) - rw [hnmod] - have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [P.r2ModModulus_lt_modulus]) +/-- The one fast element. -/ +def one (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := .mk P.rModModulus <| by + simp [Nat.mod_lt _ P.modulus_pos] -/-- Reduce a `UInt64` modulo the prime and return a Montgomery fast element. -/ -@[inline] -def reduceUInt64 (modulus : ℕ) [P : Mont32Field modulus] - (x : UInt64) : FastField modulus := - let y := x % P.modulus64 - montgomeryReduce (y * P.r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : (x % P.modulus64).toNat < modulus := by - rw [UInt64.toNat_mod, P.modulus64_toNat] - exact Nat.mod_lt _ P.modulus_pos - have hprod : (x % P.modulus64).toNat * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [P.r2ModModulus_lt_modulus]) +namespace FastField -/-- The zero fast element. -/ -private def zero (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := ⟨0, by - have h0 : (0 : UInt32).toNat = 0 := by decide - have hp := P.modulus_pos - omega⟩ +/-! ### Conversions -/ -/-- The one fast element. -/ -private def one (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := ⟨P.rModModulus, by - rw [P.rModModulus_toNat] - exact Nat.mod_lt _ P.modulus_pos⟩ +/-- Build a fast element from a canonical natural representative. -/ +@[inline] +def ofCanonicalNat (n : ℕ) (h : n < modulus) : FastField modulus := + reduce (UInt64.ofNat n * P.r2ModModulus.toUInt64) <| by + simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64, + P.r2ModModulus_toNat, Nat.mod_mul_mod] + apply Nat.mod_lt_of_lt + grw [Nat.mod_lt _ P.modulus_pos] + apply mul_lt_mul <;> simp [h, le_of_lt] /-- Convert a natural number into fast Montgomery representation. -/ @[inline] -private def ofNat (modulus : ℕ) [P : Mont32Field modulus] (n : ℕ) : FastField modulus := +def ofNat (modulus : ℕ) [P : Mont32Field modulus] (n : ℕ) : FastField modulus := ofCanonicalNat (n % modulus) (Nat.mod_lt _ P.modulus_pos) -namespace FastField - /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] -def ofUInt32 (modulus : ℕ) [P : Mont32Field modulus] - (x : UInt32) : FastField modulus := - reduceUInt64 modulus x.toUInt64 -end FastField +def ofUInt32 (modulus : ℕ) [P : Mont32Field modulus] (x : UInt32) : FastField modulus := + reduce (x.toUInt64 * P.r2ModModulus.toUInt64) <| by + simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64, P.r2ModModulus_toNat] + apply Nat.mod_lt_of_lt + grw [Nat.mod_lt _ P.modulus_pos, mul_comm modulus] + apply Nat.mul_lt_mul_of_pos_right <;> simp /-- Convert from the canonical `ZMod` field into fast Montgomery form. -/ @[inline] @@ -204,197 +148,35 @@ def ofField (x : ZMod modulus) : FastField modulus := private def ofInt (modulus : ℕ) [P : Mont32Field modulus] (n : Int) : FastField modulus := ofField (n : ZMod modulus) -namespace FastField - /-- Convert a fast element to its canonical native-word representative. -/ @[inline] -def toUInt32 (x : FastField modulus) : UInt32 := - montgomeryReduce (modulus := modulus) x.val.toUInt64 (by +def toUInt32 (x : FastField modulus) : UInt32 := Subtype.val <| + reduce (modulus := modulus) x.val.toUInt64 <| by rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.modulus_pos]) - |>.val + nlinarith [x.property, P.modulus_pos] /-- Convert a fast element to its canonical natural representative. -/ @[inline] -def toNat (x : FastField modulus) : ℕ := - x.toUInt32.toNat +def toNat (x : FastField modulus) : ℕ := x.toUInt32.toNat /-- Convert a fast element to the canonical `ZMod` field. -/ @[inline] -def toField (x : FastField modulus) : ZMod modulus := - (x.toNat : ZMod modulus) +def toField (x : FastField modulus) : ZMod modulus := (x.toNat : ZMod modulus) end FastField open FastField -theorem toNat_lt_modulus (x : FastField modulus) : toNat x < modulus := by - unfold FastField.toNat FastField.toUInt32 - change (reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - x.val.toUInt64).toNat < modulus - exact (montgomeryReduce (modulus := modulus) x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.modulus_pos])).property - -theorem toField_eq_raw_mul_inv (x : FastField modulus) : - toField x = - (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - unfold FastField.toField FastField.toNat FastField.toUInt32 - have hred := montgomeryReduce_cast (modulus := modulus) x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.modulus_pos]) - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - x.val.toUInt64).toNat : ZMod modulus) = - (x.val.toUInt64.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - x.val.toUInt64).toNat : ZMod modulus) = - (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - rw [hred] - rw [UInt32.toNat_toUInt64] - -theorem raw_cast_eq_toField_mul (x : FastField modulus) : - (x.val.toNat : ZMod modulus) = - toField x * ((2 ^ 32 : ℕ) : ZMod modulus) := by - rw [toField_eq_raw_mul_inv] - rw [mul_assoc] - rw [inv_mul_cancel₀ P.two_pow_32_ne_zero_in_field] - rw [mul_one] - -theorem nat_eq_of_field_eq {a b : ℕ} (ha : a < modulus) - (hb : b < modulus) (h : (a : ZMod modulus) = (b : ZMod modulus)) : - a = b := - natCast_inj_of_lt h ha hb - -theorem ofCanonicalNat_raw_cast (n : ℕ) (h : n < modulus) : - ((ofCanonicalNat (modulus := modulus) n h).val.toNat : ZMod modulus) = - (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by - unfold ofCanonicalNat - have hred := montgomeryReduce_cast (modulus := modulus) - (UInt64.ofNat n * P.r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) - rw [hnmod] - have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [P.r2ModModulus_lt_modulus]) - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = - ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = - (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) - rw [hred] - simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) - rw [hnmod] - have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - rw [Nat.cast_mul, P.r2ModModulus_toNat, ZMod.natCast_mod, Nat.cast_pow] - rw [pow_two] - rw [mul_assoc (n : ZMod modulus) (((2 ^ 32 : ℕ) : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)) (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] - rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod modulus) ((2 ^ 32 : ℕ) : ZMod modulus) - (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] - rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] - rw [mul_one] - -theorem toField_ofCanonicalNat_aux (n : ℕ) (h : n < modulus) : - toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := by - rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] - rw [mul_one] - -theorem reduceUInt64_raw_cast (x : UInt64) : - ((reduceUInt64 modulus x).val.toNat : ZMod modulus) = - (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by - unfold reduceUInt64 - let y := x % P.modulus64 - have hred := montgomeryReduce_cast (modulus := modulus) - (y * P.r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < modulus := by - rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] - exact Nat.mod_lt _ P.modulus_pos - have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [P.r2ModModulus_lt_modulus]) - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - (y * P.r2ModModulus.toUInt64)).toNat : - ZMod modulus) = - ((y * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - (y * P.r2ModModulus.toUInt64)).toNat : - ZMod modulus) = - (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) - rw [hred] - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hy_lt : y.toNat < modulus := by - rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] - exact Nat.mod_lt _ P.modulus_pos - have hprod : y.toNat * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [hy_lt, P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - have hy_cast : (y.toNat : ZMod modulus) = (x.toNat : ZMod modulus) := by - rw [show y = x % P.modulus64 by rfl, UInt64.toNat_mod, P.modulus64_toNat] - rw [ZMod.natCast_eq_natCast_iff] - exact Nat.mod_modEq _ _ - rw [Nat.cast_mul, P.r2ModModulus_toNat, ZMod.natCast_mod, Nat.cast_pow, hy_cast] - rw [pow_two] - rw [mul_assoc (x.toNat : ZMod modulus) (((2 ^ 32 : ℕ) : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)) (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] - rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod modulus) ((2 ^ 32 : ℕ) : ZMod modulus) - (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] - rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] - rw [mul_one] - -@[simp] -theorem toNat_ofCanonicalNat (n : ℕ) (h : n < modulus) : - toNat (ofCanonicalNat (modulus := modulus) n h) = n := - nat_eq_of_field_eq (toNat_lt_modulus _) h (toField_ofCanonicalNat_aux n h) - -@[simp] -theorem toField_ofCanonicalNat (n : ℕ) (h : n < modulus) : - toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := - toField_ofCanonicalNat_aux n h - -@[simp] -theorem toNat_reduceUInt64 (x : UInt64) : - toNat (reduceUInt64 modulus x) = x.toNat % modulus := by - apply nat_eq_of_field_eq (toNat_lt_modulus _) (Nat.mod_lt _ P.modulus_pos) - change toField (reduceUInt64 modulus x) = ((x.toNat % modulus : ℕ) : ZMod modulus) - rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] - rw [mul_one] - rw [ZMod.natCast_eq_natCast_iff] - exact (Nat.mod_modEq _ _).symm - -@[simp] -theorem toField_reduceUInt64 (x : UInt64) : - toField (reduceUInt64 modulus x) = (x.toNat : ZMod modulus) := by - rw [toField_eq_raw_mul_inv, reduceUInt64_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] - rw [mul_one] - -/-! ## Field operations -/ +/-! ### Field operations -/ /-- Fast modular addition in Montgomery form. -/ @[inline] def add (x y : FastField modulus) : FastField modulus := - reduceUInt32Lt2Modulus (x.val + y.val) (by - rw [UInt32.toNat_add] - exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by - have := x.property; have := y.property; omega)) + ⟨conditionalSubtract P.modulus32 (x.val + y.val), by + simpa only [P.modulus32_toNat] using + conditionalSubtract_lt (p32 := P.modulus32) (u := x.val + y.val) (by + rw [UInt32.toNat_add, P.modulus32_toNat] + exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by + have := x.property; have := y.property; omega))⟩ /-- Fast modular negation in Montgomery form. -/ @[inline] @@ -426,7 +208,7 @@ def sub (x y : FastField modulus) : FastField modulus := else ⟨x.val + P.modulus32 - y.val, by have hsum_lt : x.val.toNat + modulus < 2 ^ 32 := by - have htwo := P.two_mul_modulus_lt_two_pow_32 + have htwo := P.modulus_lt_two_pow_31 have := x.property; omega have hsum_eq : (x.val + P.modulus32).toNat = x.val.toNat + modulus := by rw [UInt32.toNat_add, P.modulus32_toNat, Nat.mod_eq_of_lt hsum_lt] @@ -444,7 +226,7 @@ def sub (x y : FastField modulus) : FastField modulus := /-- Fast modular multiplication in Montgomery form. -/ @[inline] def mul (x y : FastField modulus) : FastField modulus := - montgomeryReduce (x.val.toUInt64 * y.val.toUInt64) (by + reduce (x.val.toUInt64 * y.val.toUInt64) (by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] @@ -453,98 +235,170 @@ def mul (x y : FastField modulus) : FastField modulus := /-- Fast squaring. -/ @[inline] -def square (x : FastField modulus) : FastField modulus := - mul x x +def square (x : FastField modulus) : FastField modulus := mul x x /-- Exponentiation over the fast representation using repeated squaring. -/ @[specialize] def pow (x : FastField modulus) (n : ℕ) : FastField modulus := @npowBinRec (FastField modulus) ⟨one modulus⟩ ⟨mul⟩ n x -/-- Fermat exponent used for inversion in the prime field. -/ -def invExponent (modulus : ℕ) : ℕ := modulus - 2 - /-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`), by binary exponentiation (`pow`). -/ @[inline] def inv (x : FastField modulus) : FastField modulus := - pow x (invExponent modulus) + pow x (modulus - 2) /-- Division through inversion and fast multiplication. -/ @[inline] def div (x y : FastField modulus) : FastField modulus := mul x (inv y) -instance instZero : Zero (FastField modulus) where - zero := zero modulus - -instance instOne : One (FastField modulus) where - one := one modulus - -instance instAdd : Add (FastField modulus) where - add := add - -instance instNeg : Neg (FastField modulus) where - neg := neg +instance : Zero (FastField modulus) := ⟨zero modulus⟩ +instance : One (FastField modulus) := ⟨one modulus⟩ +instance : Add (FastField modulus) where add +instance : Neg (FastField modulus) where neg +instance : Sub (FastField modulus) where sub +instance : Mul (FastField modulus) where mul +instance : Inv (FastField modulus) where inv +instance : Div (FastField modulus) where div -instance instSub : Sub (FastField modulus) where - sub := sub +theorem add_def (x y : FastField modulus) : x + y = add x y := rfl +theorem mul_def (x y : FastField modulus) : x * y = mul x y := rfl -instance instMul : Mul (FastField modulus) where - mul := mul +instance : NatCast (FastField modulus) := ⟨ofNat modulus⟩ +instance : IntCast (FastField modulus) := ⟨ofInt modulus⟩ -instance instInv : Inv (FastField modulus) where - inv := inv - -instance instDiv : Div (FastField modulus) where - div := div - -instance instNatCast : NatCast (FastField modulus) where - natCast := ofNat modulus - -instance instIntCast : IntCast (FastField modulus) where - intCast := ofInt modulus - -instance instNatSMul : SMul ℕ (FastField modulus) where +instance : SMul ℕ (FastField modulus) where smul n x := ofNat modulus n * x -instance instIntSMul : SMul Int (FastField modulus) where +instance : SMul Int (FastField modulus) where smul n x := ofInt modulus n * x -instance instPowNat : Pow (FastField modulus) ℕ where - pow := pow +instance : Pow (FastField modulus) ℕ where pow -instance instPowInt : Pow (FastField modulus) Int where +instance : Pow (FastField modulus) Int where pow x n := match n with | Int.ofNat k => pow x k | Int.negSucc k => pow (inv x) (k + 1) -instance instNNRatCast : NNRatCast (FastField modulus) where +instance : NNRatCast (FastField modulus) where nnratCast q := ofField (q : ZMod modulus) -instance instRatCast : RatCast (FastField modulus) where +instance : RatCast (FastField modulus) where ratCast q := ofField (q : ZMod modulus) -instance instNNRatSMul : SMul ℚ≥0 (FastField modulus) where +instance : SMul ℚ≥0 (FastField modulus) where smul q x := ofField (q • toField x) -instance instRatSMul : SMul ℚ (FastField modulus) where +instance : SMul ℚ (FastField modulus) where smul q x := ofField (q • toField x) -/-- Fermat-style inversion in `ZMod modulus`. -/ -theorem inv_eq_pow_field (a : ZMod modulus) (ha : a ≠ 0) : - a⁻¹ = a ^ (modulus - 2) := by - have hcard : Fintype.card (ZMod modulus) = modulus := ZMod.card modulus - have h1 : a ^ (modulus - 1) = 1 := by - have h := FiniteField.pow_card_sub_one_eq_one a ha - rw [hcard] at h; exact h - have hmul : a * a ^ (modulus - 2) = 1 := by - rw [← pow_succ']; show a ^ (modulus - 2 + 1) = 1 - have : modulus - 2 + 1 = modulus - 1 := by - have := P.two_lt_modulus; omega - rw [this]; exact h1 - exact (eq_inv_of_mul_eq_one_left (by rwa [mul_comm])).symm +/-! ## Correctness -/ + +/-! ### Reduction and conversions -/ + +private theorem reduce_cast {x : UInt64} + (h : x.toNat < modulus * 2 ^ 32) : + ((reduce (modulus := modulus) x h).val.toNat : ZMod modulus) = + (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by + apply reduceRaw_cast + · exact P.modulus32_toNat + · exact P.modulus64_toNat + · exact P.modulus_pos + · exact P.modulus_lt_two_pow_31 + · exact P.montgomeryNegInv_mul_modulus_mod_two_pow_32 + · exact P.two_pow_32_ne_zero_in_field + · exact h + +theorem toNat_lt_modulus {x : FastField modulus} : toNat x < modulus := by + unfold FastField.toNat FastField.toUInt32 + change (reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + x.val.toUInt64).toNat < modulus + exact (reduce (modulus := modulus) x.val.toUInt64 (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, P.modulus_pos])).property + +private theorem toField_eq_raw_mul_inv {x : FastField modulus} : + toField x = + (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by + unfold FastField.toField FastField.toNat FastField.toUInt32 + have hred := reduce_cast (modulus := modulus) (x := x.val.toUInt64) (by + rw [UInt32.toNat_toUInt64] + nlinarith [x.property, P.modulus_pos]) + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + x.val.toUInt64).toNat : ZMod modulus) = + (x.val.toUInt64.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + x.val.toUInt64).toNat : ZMod modulus) = + (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ + rw [hred] + rw [UInt32.toNat_toUInt64] + +private theorem raw_cast_eq_toField_mul {x : FastField modulus} : + (x.val.toNat : ZMod modulus) = + toField x * ((2 ^ 32 : ℕ) : ZMod modulus) := by + rw [toField_eq_raw_mul_inv] + rw [mul_assoc] + rw [inv_mul_cancel₀ P.two_pow_32_ne_zero_in_field] + rw [mul_one] + +private theorem ofCanonicalNat_raw_cast {n : ℕ} (h : n < modulus) : + ((ofCanonicalNat (modulus := modulus) n h).val.toNat : ZMod modulus) = + (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by + unfold ofCanonicalNat + have hred := reduce_cast (modulus := modulus) + (x := UInt64.ofNat n * P.r2ModModulus.toUInt64) (by + rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) + rw [hnmod] + have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] + rw [Nat.mod_eq_of_lt hprod] + nlinarith [P.r2ModModulus_lt_modulus]) + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = + ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred + change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv + (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = + (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) + rw [hred] + simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] + have hnmod : n % 2 ^ 64 = n := by + apply Nat.mod_eq_of_lt + exact Nat.lt_trans h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) + rw [hnmod] + have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by + nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] + rw [Nat.mod_eq_of_lt hprod] + rw [Nat.cast_mul, P.r2ModModulus_toNat, ZMod.natCast_mod, Nat.cast_pow] + rw [pow_two] + rw [mul_assoc (n : ZMod modulus) (((2 ^ 32 : ℕ) : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)) (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] + rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod modulus) ((2 ^ 32 : ℕ) : ZMod modulus) + (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] + rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] + rw [mul_one] + +private theorem toField_ofCanonicalNat_aux {n : ℕ} (h : n < modulus) : + toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := by + rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] + rw [mul_assoc] + rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] + rw [mul_one] + +@[simp] +theorem toNat_ofCanonicalNat {n : ℕ} (h : n < modulus) : + toNat (ofCanonicalNat (modulus := modulus) n h) = n := + natCast_inj_of_lt (toField_ofCanonicalNat_aux h) toNat_lt_modulus h + +@[simp] +theorem toField_ofCanonicalNat {n : ℕ} (h : n < modulus) : + toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := + toField_ofCanonicalNat_aux h /-- Converting from the canonical field to fast form and back is the identity. -/ @[simp] @@ -558,17 +412,19 @@ theorem toField_ofField (x : ZMod modulus) : toField (ofField (modulus := modulu theorem ofField_toField (x : FastField modulus) : ofField (toField x) = x := by apply Subtype.ext apply UInt32.toNat_inj.mp - apply nat_eq_of_field_eq (modulus := modulus) - · exact (ofField (toField x)).property - · exact x.property + apply natCast_inj_of_lt · rw [raw_cast_eq_toField_mul] rw [toField_ofField] rw [raw_cast_eq_toField_mul] + · exact (ofField (toField x)).property + · exact x.property /-- The canonical-field interpretation distinguishes fast values. -/ theorem toField_injective : Function.Injective (toField (modulus := modulus)) := Function.LeftInverse.injective ofField_toField +/-! ### Field operations -/ + /-- `toField` maps fast zero to canonical zero. -/ @[simp] theorem toField_zero : toField (0 : FastField modulus) = 0 := by @@ -588,22 +444,20 @@ theorem toField_one : toField (1 : FastField modulus) = 1 := by /-- Fast addition agrees with addition in the canonical field. -/ @[simp] theorem toField_add (x y : FastField modulus) : toField (x + y) = toField x + toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] - unfold instAdd add - have hred := reduceUInt32Lt2Modulus_cast (modulus := modulus) (x.val + y.val) (by - rw [UInt32.toNat_add] - exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by - have hx := x.property; have hy := y.property; omega)) + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv (x := x), + toField_eq_raw_mul_inv (x := y)] + simp only [add_def] + have hred := conditionalSubtract_cast (p32 := P.modulus32) (u := x.val + y.val) + rw [P.modulus32_toNat] at hred change ((conditionalSubtract P.modulus32 (x.val + y.val)).toNat : ZMod modulus) = ((x.val + y.val).toNat : ZMod modulus) at hred change ((conditionalSubtract P.modulus32 (x.val + y.val)).toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ + (y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - rw [hred] - rw [UInt32.toNat_add] + rw [hred, UInt32.toNat_add] have hsum_lt : x.val.toNat + y.val.toNat < 2 ^ 32 := by - nlinarith [x.property, y.property, P.two_mul_modulus_lt_two_pow_32] + nlinarith [x.property, y.property, P.modulus_lt_two_pow_31] rw [Nat.mod_eq_of_lt hsum_lt] rw [Nat.cast_add] ring @@ -611,7 +465,8 @@ theorem toField_add (x y : FastField modulus) : toField (x + y) = toField x + to /-- Fast subtraction agrees with subtraction in the canonical field. -/ @[simp] theorem toField_sub (x y : FastField modulus) : toField (x - y) = toField x - toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv (x := x), + toField_eq_raw_mul_inv (x := y)] by_cases hyx : y.val ≤ x.val · have hsubval : (x - y : FastField modulus).val = x.val - y.val := by change (sub x y).val = x.val - y.val @@ -628,7 +483,7 @@ theorem toField_sub (x y : FastField modulus) : toField (x - y) = toField x - to exact hyx)] ring · have hsum_lt : x.val.toNat + modulus < 2 ^ 32 := by - have htwo := P.two_mul_modulus_lt_two_pow_32 + have htwo := P.modulus_lt_two_pow_31 have := x.property; omega have hsum_eq : (x.val + P.modulus32).toNat = x.val.toNat + modulus := by rw [UInt32.toNat_add, P.modulus32_toNat, Nat.mod_eq_of_lt hsum_lt] @@ -654,7 +509,7 @@ theorem toField_sub (x y : FastField modulus) : toField (x - y) = toField x - to /-- Fast negation agrees with negation in the canonical field. -/ @[simp] theorem toField_neg (x : FastField modulus) : toField (-x) = -toField x := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x] + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv (x := x)] by_cases hx : x.val = 0 · have hnegval : (-x : FastField modulus).val = (zero modulus).val := by change (neg x).val = (zero modulus).val @@ -691,10 +546,11 @@ theorem toField_neg (x : FastField modulus) : toField (-x) = -toField x := by /-- Fast multiplication agrees with multiplication in the canonical field. -/ @[simp] theorem toField_mul (x y : FastField modulus) : toField (x * y) = toField x * toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv x, toField_eq_raw_mul_inv y] - unfold instMul mul - have hred := montgomeryReduce_cast (modulus := modulus) - (x.val.toUInt64 * y.val.toUInt64) (by + rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv (x := x), + toField_eq_raw_mul_inv (x := y)] + simp only [mul_def] + have hred := reduce_cast (modulus := modulus) + (x := x.val.toUInt64 * y.val.toUInt64) (by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] @@ -717,22 +573,6 @@ theorem toField_mul (x y : FastField modulus) : toField (x * y) = toField x * to rw [Nat.cast_mul] ring -/-- Ring equivalence between the fast Montgomery representation and the canonical field. -/ -def ringEquiv (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus ≃+* ZMod modulus where - toFun := toField - invFun := ofField - left_inv := ofField_toField - right_inv := toField_ofField - map_add' := toField_add - map_mul' := toField_mul - -@[simp] -theorem ringEquiv_apply (x : FastField modulus) : ringEquiv modulus x = toField x := rfl - -@[simp] -theorem ringEquiv_symm_apply (x : ZMod modulus) : - (ringEquiv modulus).symm x = ofField x := rfl - private theorem mul_assoc_field (x y z : FastField modulus) : (x * y) * z = x * (y * z) := by apply toField_injective rw [toField_mul, toField_mul, toField_mul, toField_mul] @@ -764,17 +604,31 @@ theorem toField_pow (x : FastField modulus) (n : ℕ) : toField (pow x n) = toFi | succ n ih => rw [pow_succ_field, toField_mul, ih, _root_.pow_succ] +/-- Fermat-style inversion in `ZMod modulus`. -/ +private theorem inv_eq_pow_field {a : ZMod modulus} (ha : a ≠ 0) : + a⁻¹ = a ^ (modulus - 2) := by + have hcard : Fintype.card (ZMod modulus) = modulus := ZMod.card modulus + have h1 : a ^ (modulus - 1) = 1 := by + have h := FiniteField.pow_card_sub_one_eq_one a ha + rw [hcard] at h; exact h + have hmul : a * a ^ (modulus - 2) = 1 := by + rw [← pow_succ']; show a ^ (modulus - 2 + 1) = 1 + have : modulus - 2 + 1 = modulus - 1 := by + have := P.two_lt_modulus; omega + rw [this]; exact h1 + exact (eq_inv_of_mul_eq_one_left (by rwa [mul_comm])).symm + private theorem toField_inv_pow (x : FastField modulus) : - toField (inv x) = toField x ^ invExponent modulus := by + toField (inv x) = toField x ^ (modulus - 2) := by unfold inv - exact toField_pow x (invExponent modulus) + exact toField_pow x (modulus - 2) private theorem toField_inv_raw (x : FastField modulus) : toField (inv x) = (toField x)⁻¹ := by rw [toField_inv_pow] by_cases hx : toField x = 0 · rw [hx, inv_zero] - exact zero_pow (by unfold invExponent; have := P.two_lt_modulus; omega) - · simpa [invExponent] using (inv_eq_pow_field (toField x) hx).symm + exact zero_pow (by have := P.two_lt_modulus; omega) + · exact (inv_eq_pow_field (a := toField x) hx).symm /-- Fast inversion agrees with inversion in the canonical field. -/ @[simp] @@ -787,21 +641,13 @@ private theorem toField_mul_raw (x y : FastField modulus) : change toField (x * y) = toField x * toField y exact toField_mul x y -private theorem toField_div_mul_inv (x y : FastField modulus) : - toField (div x y) = toField x * toField (inv y) := by - unfold div - exact toField_mul_raw x (inv y) - /-- Fast division agrees with division in the canonical field. -/ @[simp] theorem toField_div (x y : FastField modulus) : toField (x / y) = toField x / toField y := by change toField (div x y) = toField x / toField y - have h : ∀ a b c : ZMod modulus, c = b⁻¹ → a * c = a / b := by - intro a b c hc - rw [hc] - rfl - exact (toField_div_mul_inv x y).trans - (h (toField x) (toField y) (toField (inv y)) (toField_inv_raw y)) + unfold div + rw [toField_mul_raw, toField_inv_raw] + rfl /-- Natural casts into fast form agree with natural casts into the canonical field. -/ @[simp] @@ -878,6 +724,24 @@ theorem toField_qsmul (q : ℚ) (x : FastField modulus) : toField (q • x) = q change toField (ofField (q • toField x)) = q • toField x rw [toField_ofField] +/-! ### Algebraic structure -/ + +/-- Ring equivalence between the fast Montgomery representation and the canonical field. -/ +def ringEquiv (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus ≃+* ZMod modulus where + toFun := toField + invFun := ofField + left_inv := ofField_toField + right_inv := toField_ofField + map_add' := toField_add + map_mul' := toField_mul + +@[simp] +theorem ringEquiv_apply {x : FastField modulus} : ringEquiv modulus x = toField x := rfl + +@[simp] +theorem ringEquiv_symm_apply {x : ZMod modulus} : + (ringEquiv modulus).symm x = ofField x := rfl + /-- Field instance transferred from the canonical field through `toField`. -/ instance (priority := low) instField : _root_.Field (FastField modulus) := toField_injective.field toField From 5305c7a6693040072d1cb8a9e322ad8f4682c5e6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 17:04:58 +0200 Subject: [PATCH 27/31] simplify proofs --- CompPoly/Fields/Montgomery/Native32Field.lean | 385 ++++++------------ 1 file changed, 124 insertions(+), 261 deletions(-) diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index 1363005c..d4c25b73 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -78,16 +78,15 @@ theorem modulus_pos : 0 < modulus := Nat.zero_lt_of_lt P.two_lt_modulus theorem modulus_sq_lt_two_pow_64 : modulus ^ 2 < 2 ^ 64 := by nlinarith [P.modulus_lt_two_pow_32] -theorem two_pow_32_ne_zero_in_field : ((2 ^ 32 : ℕ) : ZMod modulus) ≠ 0 := by - have htwo : (2 : ZMod modulus) ≠ 0 := by - intro h - have hdvd : modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 modulus).mp h - exact (Nat.not_le_of_gt P.two_lt_modulus) (Nat.le_of_dvd (by decide) hdvd) - rw [Nat.cast_pow] - exact pow_ne_zero 32 htwo +theorem two_ne_zero : (2 : ZMod modulus) ≠ 0 := by + intro h + have hdvd : modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 modulus).mp h + exact (Nat.not_le_of_gt P.two_lt_modulus) (Nat.le_of_dvd (by decide) hdvd) + +theorem two_pow_32_ne_zero : ((2 ^ 32 : ℕ) : ZMod modulus) ≠ 0 := by + simp [two_ne_zero] -theorem r2ModModulus_lt_modulus : P.r2ModModulus.toNat < modulus := by - rw [P.r2ModModulus_toNat] +theorem r2ModModulus_lt_modulus : (2 ^ 32) ^ 2 % modulus < modulus := by exact Nat.mod_lt _ P.modulus_pos end Mont32Field @@ -103,13 +102,6 @@ def reduce (x : UInt64) (h : x.toNat < modulus * 2 ^ 32) : FastField modulus := nth_rw 4 [← P.modulus32_toNat] apply reduceRaw_lt <;> simp_all -/-- The zero fast element. -/ -def zero (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := .mk 0 <| by simp - -/-- The one fast element. -/ -def one (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := .mk P.rModModulus <| by - simp [Nat.mod_lt _ P.modulus_pos] - namespace FastField /-! ### Conversions -/ @@ -168,15 +160,22 @@ open FastField /-! ### Field operations -/ +/-- The zero fast element. -/ +def zero (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := .mk 0 <| by simp + +/-- The one fast element. -/ +def one (modulus : ℕ) [P : Mont32Field modulus] : FastField modulus := .mk P.rModModulus <| by + simp [Nat.mod_lt _ P.modulus_pos] + /-- Fast modular addition in Montgomery form. -/ @[inline] def add (x y : FastField modulus) : FastField modulus := - ⟨conditionalSubtract P.modulus32 (x.val + y.val), by - simpa only [P.modulus32_toNat] using - conditionalSubtract_lt (p32 := P.modulus32) (u := x.val + y.val) (by - rw [UInt32.toNat_add, P.modulus32_toNat] - exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) (by - have := x.property; have := y.property; omega))⟩ + .mk (conditionalSubtract P.modulus32 (x.val + y.val)) <| by + simp_rw [← P.modulus32_toNat] + apply conditionalSubtract_lt + simp only [UInt32.toNat_add, P.modulus32_toNat] + apply Nat.lt_of_le_of_lt (Nat.mod_le _ _) + linarith [x.property, y.property] /-- Fast modular negation in Montgomery form. -/ @[inline] @@ -184,44 +183,38 @@ def neg (x : FastField modulus) : FastField modulus := if hx : x.val = 0 then zero modulus else - ⟨P.modulus32 - x.val, by + .mk (P.modulus32 - x.val) <| by have hle : x.val ≤ P.modulus32 := by - rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] - exact Nat.le_of_lt x.property - rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus32_toNat] - have hxpos : 0 < x.val.toNat := by - apply Nat.pos_of_ne_zero - intro hzero - apply hx - apply UInt32.toNat_inj.mp - simpa using hzero - have hp := P.modulus_pos - omega⟩ + simp [Nat.le_of_lt, UInt32.le_iff_toNat_le, x.property] + simp [UInt32.toNat_sub_of_le _ _ hle] + rw [← UInt32.toNat_inj] at hx + change x.val.toNat ≠ 0 at hx + omega /-- Fast modular subtraction in Montgomery form. -/ @[inline] def sub (x y : FastField modulus) : FastField modulus := if hyx : y.val ≤ x.val then - ⟨x.val - y.val, by + .mk (x.val - y.val) <| by rw [UInt32.toNat_sub_of_le _ _ hyx] - have := x.property; omega⟩ + have := x.property + omega else - ⟨x.val + P.modulus32 - y.val, by + .mk (x.val + P.modulus32 - y.val) <| by + simp only [UInt32.le_iff_toNat_le] at hyx have hsum_lt : x.val.toNat + modulus < 2 ^ 32 := by - have htwo := P.modulus_lt_two_pow_31 - have := x.property; omega - have hsum_eq : (x.val + P.modulus32).toNat = x.val.toNat + modulus := by - rw [UInt32.toNat_add, P.modulus32_toNat, Nat.mod_eq_of_lt hsum_lt] + have := x.property + have := P.modulus_lt_two_pow_31 + omega have hyle : y.val ≤ x.val + P.modulus32 := by - rw [UInt32.le_iff_toNat_le, hsum_eq] - have := y.property; omega - rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] - have hyxNat : ¬y.val.toNat ≤ x.val.toNat := by - intro hle - apply hyx - rw [UInt32.le_iff_toNat_le] - exact hle - have := y.property; omega⟩ + simp only [UInt32.le_iff_toNat_le, UInt32.toNat_add, P.modulus32_toNat, + Nat.mod_eq_of_lt hsum_lt] + have := y.property + omega + rw [UInt32.toNat_sub_of_le _ _ hyle, UInt32.toNat_add, P.modulus32_toNat, + Nat.mod_eq_of_lt hsum_lt] + have := y.property + omega /-- Fast modular multiplication in Montgomery form. -/ @[inline] @@ -262,8 +255,15 @@ instance : Mul (FastField modulus) where mul instance : Inv (FastField modulus) where inv instance : Div (FastField modulus) where div +theorem zero_def : (0 : FastField modulus) = zero modulus := rfl +theorem one_def : (1 : FastField modulus) = one modulus := rfl theorem add_def (x y : FastField modulus) : x + y = add x y := rfl +theorem neg_def (x : FastField modulus) : -x = neg x := rfl +theorem sub_def (x y : FastField modulus) : x - y = sub x y := rfl theorem mul_def (x y : FastField modulus) : x * y = mul x y := rfl +theorem inv_def (x : FastField modulus) : x⁻¹ = inv x := rfl +theorem div_def (x y : FastField modulus) : x / y = x * y⁻¹ := rfl +theorem square_def (x : FastField modulus) : square x = x * x := rfl instance : NatCast (FastField modulus) := ⟨ofNat modulus⟩ instance : IntCast (FastField modulus) := ⟨ofInt modulus⟩ @@ -300,112 +300,54 @@ instance : SMul ℚ (FastField modulus) where private theorem reduce_cast {x : UInt64} (h : x.toNat < modulus * 2 ^ 32) : - ((reduce (modulus := modulus) x h).val.toNat : ZMod modulus) = + ((reduce x h).val.toNat : ZMod modulus) = (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - apply reduceRaw_cast - · exact P.modulus32_toNat - · exact P.modulus64_toNat - · exact P.modulus_pos - · exact P.modulus_lt_two_pow_31 - · exact P.montgomeryNegInv_mul_modulus_mod_two_pow_32 - · exact P.two_pow_32_ne_zero_in_field - · exact h + apply reduceRaw_cast <;> + simp [P.montgomeryNegInv_mul_modulus_mod_two_pow_32, P.two_ne_zero, *] + +@[simp] +theorem FastField.val_prop (x : FastField modulus) : x.val.toNat < modulus := x.property theorem toNat_lt_modulus {x : FastField modulus} : toNat x < modulus := by - unfold FastField.toNat FastField.toUInt32 - change (reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - x.val.toUInt64).toNat < modulus - exact (reduce (modulus := modulus) x.val.toUInt64 (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.modulus_pos])).property + simp [FastField.toNat, FastField.toUInt32] -private theorem toField_eq_raw_mul_inv {x : FastField modulus} : +private theorem toField_eq_val_mul_inv {x : FastField modulus} : toField x = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - unfold FastField.toField FastField.toNat FastField.toUInt32 - have hred := reduce_cast (modulus := modulus) (x := x.val.toUInt64) (by - rw [UInt32.toNat_toUInt64] - nlinarith [x.property, P.modulus_pos]) - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - x.val.toUInt64).toNat : ZMod modulus) = - (x.val.toUInt64.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - x.val.toUInt64).toNat : ZMod modulus) = - (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - rw [hred] - rw [UInt32.toNat_toUInt64] - -private theorem raw_cast_eq_toField_mul {x : FastField modulus} : + convert reduce_cast (modulus := modulus) ?_ using 1 + +private theorem val_cast_eq_toField_mul {x : FastField modulus} : (x.val.toNat : ZMod modulus) = toField x * ((2 ^ 32 : ℕ) : ZMod modulus) := by - rw [toField_eq_raw_mul_inv] - rw [mul_assoc] - rw [inv_mul_cancel₀ P.two_pow_32_ne_zero_in_field] - rw [mul_one] + rw [toField_eq_val_mul_inv, mul_assoc] + rw [inv_mul_cancel₀ P.two_pow_32_ne_zero, mul_one] -private theorem ofCanonicalNat_raw_cast {n : ℕ} (h : n < modulus) : +private theorem ofCanonicalNat_toNat_cast {n : ℕ} (h : n < modulus) : ((ofCanonicalNat (modulus := modulus) n h).val.toNat : ZMod modulus) = (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by - unfold ofCanonicalNat - have hred := reduce_cast (modulus := modulus) - (x := UInt64.ofNat n * P.r2ModModulus.toUInt64) (by - rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) - rw [hnmod] - have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by - nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [P.r2ModModulus_lt_modulus]) - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = - ((UInt64.ofNat n * P.r2ModModulus.toUInt64).toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - (UInt64.ofNat n * P.r2ModModulus.toUInt64)).toNat : ZMod modulus) = - (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) - rw [hred] - simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64] - have hnmod : n % 2 ^ 64 = n := by - apply Nat.mod_eq_of_lt - exact Nat.lt_trans h (Nat.lt_trans P.modulus_lt_two_pow_32 (by decide)) - rw [hnmod] - have hprod : n * P.r2ModModulus.toNat < 2 ^ 64 := by + convert reduce_cast (modulus := modulus) ?_ using 1 + simp only [Nat.cast_pow, Nat.cast_ofNat, UInt64.toNat_mul, UInt64.toNat_ofNat', + UInt32.toNat_toUInt64, P.r2ModModulus_toNat, Nat.mod_mul_mod] + have hprod : n * ((2 ^ 32) ^ 2 % modulus) < 2 ^ 64 := by nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] - rw [Nat.cast_mul, P.r2ModModulus_toNat, ZMod.natCast_mod, Nat.cast_pow] - rw [pow_two] - rw [mul_assoc (n : ZMod modulus) (((2 ^ 32 : ℕ) : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)) (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] - rw [mul_assoc ((2 ^ 32 : ℕ) : ZMod modulus) ((2 ^ 32 : ℕ) : ZMod modulus) - (((2 ^ 32 : ℕ) : ZMod modulus)⁻¹)] - rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] - rw [mul_one] - -private theorem toField_ofCanonicalNat_aux {n : ℕ} (h : n < modulus) : + grind + +@[simp] +theorem toField_ofCanonicalNat {n : ℕ} (h : n < modulus) : toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := by - rw [toField_eq_raw_mul_inv, ofCanonicalNat_raw_cast] - rw [mul_assoc] - rw [mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field] - rw [mul_one] + rw [toField_eq_val_mul_inv, ofCanonicalNat_toNat_cast, mul_assoc] + rw [mul_inv_cancel₀ P.two_pow_32_ne_zero, mul_one] @[simp] theorem toNat_ofCanonicalNat {n : ℕ} (h : n < modulus) : toNat (ofCanonicalNat (modulus := modulus) n h) = n := - natCast_inj_of_lt (toField_ofCanonicalNat_aux h) toNat_lt_modulus h - -@[simp] -theorem toField_ofCanonicalNat {n : ℕ} (h : n < modulus) : - toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := - toField_ofCanonicalNat_aux h + natCast_inj_of_lt (toField_ofCanonicalNat h) toNat_lt_modulus h /-- Converting from the canonical field to fast form and back is the identity. -/ @[simp] theorem toField_ofField (x : ZMod modulus) : toField (ofField (modulus := modulus) x) = x := by - unfold ofField - rw [toField_ofCanonicalNat] - exact ZMod.natCast_zmod_val x + simp [ofField, toField_ofCanonicalNat] /-- Converting from fast form to the canonical field and back is the identity. -/ @[simp] @@ -413,11 +355,10 @@ theorem ofField_toField (x : FastField modulus) : ofField (toField x) = x := by apply Subtype.ext apply UInt32.toNat_inj.mp apply natCast_inj_of_lt - · rw [raw_cast_eq_toField_mul] - rw [toField_ofField] - rw [raw_cast_eq_toField_mul] - · exact (ofField (toField x)).property - · exact x.property + · rw [val_cast_eq_toField_mul, toField_ofField] + rw [val_cast_eq_toField_mul] + · simp + · simp /-- The canonical-field interpretation distinguishes fast values. -/ theorem toField_injective : Function.Injective (toField (modulus := modulus)) := @@ -428,55 +369,36 @@ theorem toField_injective : Function.Injective (toField (modulus := modulus)) := /-- `toField` maps fast zero to canonical zero. -/ @[simp] theorem toField_zero : toField (0 : FastField modulus) = 0 := by - rw [toField_eq_raw_mul_inv] - change ((0 : ℕ) : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = 0 - rw [Nat.cast_zero, zero_mul] + simp [toField_eq_val_mul_inv, zero_def, zero] /-- `toField` maps fast one to canonical one. -/ @[simp] theorem toField_one : toField (1 : FastField modulus) = 1 := by - rw [toField_eq_raw_mul_inv] - change (P.rModModulus.toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = 1 - rw [P.rModModulus_toNat, ZMod.natCast_mod] - exact mul_inv_cancel₀ P.two_pow_32_ne_zero_in_field + simp only [toField_eq_val_mul_inv, one_def, one, + Mont32Field.rModModulus_toNat, ZMod.natCast_mod] + exact mul_inv_cancel₀ P.two_pow_32_ne_zero /-- Fast addition agrees with addition in the canonical field. -/ @[simp] theorem toField_add (x y : FastField modulus) : toField (x + y) = toField x + toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv (x := x), - toField_eq_raw_mul_inv (x := y)] - simp only [add_def] + rw [toField_eq_val_mul_inv, toField_eq_val_mul_inv (x := x), + toField_eq_val_mul_inv (x := y), add_def, add] have hred := conditionalSubtract_cast (p32 := P.modulus32) (u := x.val + y.val) rw [P.modulus32_toNat] at hred - change ((conditionalSubtract P.modulus32 (x.val + y.val)).toNat : ZMod modulus) = - ((x.val + y.val).toNat : ZMod modulus) at hred - change ((conditionalSubtract P.modulus32 (x.val + y.val)).toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = - (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ + - (y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ rw [hred, UInt32.toNat_add] have hsum_lt : x.val.toNat + y.val.toNat < 2 ^ 32 := by nlinarith [x.property, y.property, P.modulus_lt_two_pow_31] - rw [Nat.mod_eq_of_lt hsum_lt] - rw [Nat.cast_add] + rw [Nat.mod_eq_of_lt hsum_lt, Nat.cast_add] ring /-- Fast subtraction agrees with subtraction in the canonical field. -/ @[simp] theorem toField_sub (x y : FastField modulus) : toField (x - y) = toField x - toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv (x := x), - toField_eq_raw_mul_inv (x := y)] + rw [toField_eq_val_mul_inv, toField_eq_val_mul_inv (x := x), + toField_eq_val_mul_inv (x := y)] + simp only [sub_def] by_cases hyx : y.val ≤ x.val - · have hsubval : (x - y : FastField modulus).val = x.val - y.val := by - change (sub x y).val = x.val - y.val - unfold sub - rw [dif_pos hyx] - rw [hsubval] - change (((x.val - y.val).toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) = - (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - - (y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ + · simp only [sub, hyx, ↓reduceDIte] rw [UInt32.toNat_sub_of_le _ _ hyx] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le] at hyx @@ -490,15 +412,7 @@ theorem toField_sub (x y : FastField modulus) : toField (x - y) = toField x - to have hyle : y.val ≤ x.val + P.modulus32 := by rw [UInt32.le_iff_toNat_le, hsum_eq] have := y.property; omega - have hsubval : (x - y : FastField modulus).val = x.val + P.modulus32 - y.val := by - change (sub x y).val = x.val + P.modulus32 - y.val - unfold sub - rw [dif_neg hyx] - rw [hsubval] - change (((x.val + P.modulus32 - y.val).toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) = - (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ - - (y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ + simp only [sub, hyx, ↓reduceDIte] rw [UInt32.toNat_sub_of_le _ _ hyle, hsum_eq] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le, hsum_eq] at hyle @@ -509,33 +423,16 @@ theorem toField_sub (x y : FastField modulus) : toField (x - y) = toField x - to /-- Fast negation agrees with negation in the canonical field. -/ @[simp] theorem toField_neg (x : FastField modulus) : toField (-x) = -toField x := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv (x := x)] + rw [toField_eq_val_mul_inv, toField_eq_val_mul_inv (x := x)] + simp only [neg_def] by_cases hx : x.val = 0 - · have hnegval : (-x : FastField modulus).val = (zero modulus).val := by - change (neg x).val = (zero modulus).val - unfold neg - rw [dif_pos hx] - rw [hnegval] - change ((zero modulus).val.toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = - -((x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) - have hxNat : x.val.toNat = 0 := by - simpa using congrArg UInt32.toNat hx - rw [hxNat] - change ((0 : ℕ) : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = - -(((0 : ℕ) : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) - simp + · simp only [neg, hx, ↓reduceDIte, zero] + have hxNat := congrArg UInt32.toNat hx + simp_all · have hle : x.val ≤ P.modulus32 := by rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] exact Nat.le_of_lt x.property - have hnegval : (-x : FastField modulus).val = P.modulus32 - x.val := by - change (neg x).val = P.modulus32 - x.val - unfold neg - rw [dif_neg hx] - rw [hnegval] - change (((P.modulus32 - x.val).toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) = - -((x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) + simp only [neg, hx, ↓reduceDIte] rw [UInt32.toNat_sub_of_le _ _ hle, P.modulus32_toNat] rw [Nat.cast_sub (by rw [UInt32.le_iff_toNat_le, P.modulus32_toNat] at hle @@ -544,33 +441,20 @@ theorem toField_neg (x : FastField modulus) : toField (-x) = -toField x := by ring /-- Fast multiplication agrees with multiplication in the canonical field. -/ -@[simp] -theorem toField_mul (x y : FastField modulus) : toField (x * y) = toField x * toField y := by - rw [toField_eq_raw_mul_inv, toField_eq_raw_mul_inv (x := x), - toField_eq_raw_mul_inv (x := y)] - simp only [mul_def] - have hred := reduce_cast (modulus := modulus) - (x := x.val.toUInt64 * y.val.toUInt64) (by - simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] - have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by - nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - nlinarith [x.property, y.property, P.modulus_lt_two_pow_32, P.modulus_pos]) - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - (x.val.toUInt64 * y.val.toUInt64)).toNat : ZMod modulus) = - ((x.val.toUInt64 * y.val.toUInt64).toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ at hred - change ((reduceRaw P.modulus32 P.modulus64 P.montgomeryNegInv - (x.val.toUInt64 * y.val.toUInt64)).toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ = - (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ * - ((y.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹) - rw [hred] +private theorem mul_toNat_cast (x y : FastField modulus) : + ((mul x y).val.toNat : ZMod modulus) = + (x.val.toNat : ZMod modulus) * (y.val.toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by + convert reduce_cast (modulus := modulus) ?_ using 1 simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] - rw [Nat.mod_eq_of_lt hprod] - rw [Nat.cast_mul] + rw [Nat.mod_eq_of_lt hprod, Nat.cast_mul] + +@[simp] +theorem toField_mul (x y : FastField modulus) : toField (x * y) = toField x * toField y := by + rw [toField_eq_val_mul_inv, toField_eq_val_mul_inv (x := x), + toField_eq_val_mul_inv (x := y), mul_def, mul_toNat_cast] ring private theorem mul_assoc_field (x y z : FastField modulus) : (x * y) * z = x * (y * z) := by @@ -589,8 +473,7 @@ private theorem pow_succ_field (x : FastField modulus) (n : ℕ) : pow x (n + 1) /-- Fast squaring agrees with multiplication by itself in the canonical field. -/ @[simp] theorem toField_square (x : FastField modulus) : toField (square x) = toField x * toField x := by - change toField (x * x) = toField x * toField x - rw [toField_mul] + simp only [square_def, toField_mul] /-- Fast natural-power computation agrees with powers in the canonical field. -/ @[simp] @@ -605,7 +488,7 @@ theorem toField_pow (x : FastField modulus) (n : ℕ) : toField (pow x n) = toFi rw [pow_succ_field, toField_mul, ih, _root_.pow_succ] /-- Fermat-style inversion in `ZMod modulus`. -/ -private theorem inv_eq_pow_field {a : ZMod modulus} (ha : a ≠ 0) : +private theorem inv_eq_pow {a : ZMod modulus} (ha : a ≠ 0) : a⁻¹ = a ^ (modulus - 2) := by have hcard : Fintype.card (ZMod modulus) = modulus := ZMod.card modulus have h1 : a ^ (modulus - 1) = 1 := by @@ -618,35 +501,20 @@ private theorem inv_eq_pow_field {a : ZMod modulus} (ha : a ≠ 0) : rw [this]; exact h1 exact (eq_inv_of_mul_eq_one_left (by rwa [mul_comm])).symm -private theorem toField_inv_pow (x : FastField modulus) : - toField (inv x) = toField x ^ (modulus - 2) := by - unfold inv - exact toField_pow x (modulus - 2) - -private theorem toField_inv_raw (x : FastField modulus) : toField (inv x) = (toField x)⁻¹ := by - rw [toField_inv_pow] - by_cases hx : toField x = 0 - · rw [hx, inv_zero] - exact zero_pow (by have := P.two_lt_modulus; omega) - · exact (inv_eq_pow_field (a := toField x) hx).symm - /-- Fast inversion agrees with inversion in the canonical field. -/ @[simp] theorem toField_inv (x : FastField modulus) : toField x⁻¹ = (toField x)⁻¹ := by - change toField (inv x) = (toField x)⁻¹ - exact toField_inv_raw x - -private theorem toField_mul_raw (x y : FastField modulus) : - toField (mul x y) = toField x * toField y := by - change toField (x * y) = toField x * toField y - exact toField_mul x y + simp only [inv_def, inv, toField_pow] + by_cases hx : toField x = 0 + · rw [hx, inv_zero, zero_pow] + have := P.two_lt_modulus + omega + · rw [inv_eq_pow hx] /-- Fast division agrees with division in the canonical field. -/ @[simp] theorem toField_div (x y : FastField modulus) : toField (x / y) = toField x / toField y := by - change toField (div x y) = toField x / toField y - unfold div - rw [toField_mul_raw, toField_inv_raw] + simp only [div_def, toField_mul, toField_inv] rfl /-- Natural casts into fast form agree with natural casts into the canonical field. -/ @@ -654,8 +522,7 @@ theorem toField_div (x y : FastField modulus) : toField (x / y) = toField x / to theorem toField_natCast (n : ℕ) : toField (n : FastField modulus) = (n : ZMod modulus) := by change toField (ofNat modulus n) = (n : ZMod modulus) unfold ofNat - rw [toField_ofCanonicalNat] - rw [ZMod.natCast_eq_natCast_iff] + rw [toField_ofCanonicalNat, ZMod.natCast_eq_natCast_iff] exact Nat.mod_modEq _ _ /-- Integer casts into fast form agree with integer casts into the canonical field. -/ @@ -771,16 +638,12 @@ instance (priority := low) instCommRing : CommRing (FastField modulus) := by /-- A fast 32-bit-word field is non-binary. -/ instance (priority := low) instNonBinaryField : NonBinaryField (FastField modulus) where char_neq_2 := by - change ((2 : ℕ) : FastField modulus) ≠ 0 intro h - have htwo : (2 : ZMod modulus) = 0 := by - calc - (2 : ZMod modulus) = ((2 : ℕ) : ZMod modulus) := by norm_cast - _ = toField ((2 : ℕ) : FastField modulus) := (toField_natCast 2).symm - _ = toField (0 : FastField modulus) := congrArg toField h - _ = 0 := toField_zero - have hdvd : modulus ∣ 2 := (ZMod.natCast_eq_zero_iff 2 modulus).mp htwo - exact (Nat.not_le_of_gt P.two_lt_modulus) (Nat.le_of_dvd (by decide) hdvd) + apply P.two_ne_zero + calc + _ = toField ((2 : ℕ) : FastField modulus) := (toField_natCast 2).symm + _ = toField (0 : FastField modulus) := congrArg toField h + _ = 0 := toField_zero end From fc6b63ffc80db9c0facc152876f2ea255eea8a06 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 17:16:31 +0200 Subject: [PATCH 28/31] refactor(fields): simplify Montgomery field proofs --- CompPoly/Fields/Montgomery/Native32Field.lean | 49 +++++-------------- 1 file changed, 11 insertions(+), 38 deletions(-) diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index d4c25b73..b454edd4 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -53,16 +53,15 @@ interpreted as a Montgomery residue. At runtime this erases to `UInt32`. -/ def FastField (modulus : ℕ) [Mont32Field modulus] : Type := { x : UInt32 // x.toNat < modulus } -instance (modulus : ℕ) [Mont32Field modulus] : DecidableEq (FastField modulus) := - inferInstanceAs (DecidableEq { x : UInt32 // x.toNat < modulus }) - section variable {modulus : ℕ} [P : Mont32Field modulus] +instance : DecidableEq (FastField modulus) := + inferInstanceAs (DecidableEq { x : UInt32 // x.toNat < modulus }) + namespace Mont32Field -instance factPrime (modulus : ℕ) [P : Mont32Field modulus] : Fact (Nat.Prime modulus) := - ⟨P.prime⟩ +instance : Fact (Nat.Prime modulus) := ⟨P.prime⟩ @[simp] theorem modulus_pos : 0 < modulus := Nat.zero_lt_of_lt P.two_lt_modulus @@ -521,30 +520,26 @@ theorem toField_div (x y : FastField modulus) : toField (x / y) = toField x / to @[simp] theorem toField_natCast (n : ℕ) : toField (n : FastField modulus) = (n : ZMod modulus) := by change toField (ofNat modulus n) = (n : ZMod modulus) - unfold ofNat - rw [toField_ofCanonicalNat, ZMod.natCast_eq_natCast_iff] + rw [ofNat, toField_ofCanonicalNat, ZMod.natCast_eq_natCast_iff] exact Nat.mod_modEq _ _ /-- Integer casts into fast form agree with integer casts into the canonical field. -/ @[simp] theorem toField_intCast (n : Int) : toField (n : FastField modulus) = (n : ZMod modulus) := by - change toField (ofInt modulus n) = (n : ZMod modulus) - unfold ofInt + change toField (ofField n) = (n : ZMod modulus) rw [toField_ofField] /-- Natural scalar multiplication is preserved by `toField`. -/ @[simp] theorem toField_nsmul (n : ℕ) (x : FastField modulus) : toField (n • x) = n • toField x := by change toField ((n : FastField modulus) * x) = n • toField x - rw [toField_mul, toField_natCast] - rw [nsmul_eq_mul] + rw [toField_mul, toField_natCast, nsmul_eq_mul] /-- Integer scalar multiplication is preserved by `toField`. -/ @[simp] theorem toField_zsmul (n : Int) (x : FastField modulus) : toField (n • x) = n • toField x := by change toField ((n : FastField modulus) * x) = n • toField x - rw [toField_mul, toField_intCast] - rw [zsmul_eq_mul] + rw [toField_mul, toField_intCast, zsmul_eq_mul] /-- Natural powers through the `Pow` instance are preserved by `toField`. -/ @[simp] @@ -610,33 +605,11 @@ theorem ringEquiv_symm_apply {x : ZMod modulus} : (ringEquiv modulus).symm x = ofField x := rfl /-- Field instance transferred from the canonical field through `toField`. -/ -instance (priority := low) instField : _root_.Field (FastField modulus) := - toField_injective.field toField - toField_zero - toField_one - toField_add - toField_mul - toField_neg - toField_sub - toField_inv - toField_div - toField_nsmul - toField_zsmul - toField_nnqsmul - toField_qsmul - toField_npow - toField_zpow - toField_natCast - toField_intCast - toField_nnratCast - toField_ratCast - -/-- Commutative-ring instance inherited from the transferred field structure. -/ -instance (priority := low) instCommRing : CommRing (FastField modulus) := by - infer_instance +instance instField : _root_.Field (FastField modulus) := by + apply toField_injective.field toField <;> simp /-- A fast 32-bit-word field is non-binary. -/ -instance (priority := low) instNonBinaryField : NonBinaryField (FastField modulus) where +instance instNonBinaryField : NonBinaryField (FastField modulus) where char_neq_2 := by intro h apply P.two_ne_zero From f929586f09ac7fcaee148c2d4113e4762932fd72 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 17:24:14 +0200 Subject: [PATCH 29/31] refactor(fields): clarify Montgomery cast lemma names --- CompPoly/Fields/Montgomery/Native32Field.lean | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index b454edd4..4ee22ab0 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -297,7 +297,7 @@ instance : SMul ℚ (FastField modulus) where /-! ### Reduction and conversions -/ -private theorem reduce_cast {x : UInt64} +private theorem reduce_val_toNat_cast {x : UInt64} (h : x.toNat < modulus * 2 ^ 32) : ((reduce x h).val.toNat : ZMod modulus) = (x.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by @@ -305,26 +305,26 @@ private theorem reduce_cast {x : UInt64} simp [P.montgomeryNegInv_mul_modulus_mod_two_pow_32, P.two_ne_zero, *] @[simp] -theorem FastField.val_prop (x : FastField modulus) : x.val.toNat < modulus := x.property +theorem FastField.val_toNat_lt (x : FastField modulus) : x.val.toNat < modulus := x.property theorem toNat_lt_modulus {x : FastField modulus} : toNat x < modulus := by simp [FastField.toNat, FastField.toUInt32] -private theorem toField_eq_val_mul_inv {x : FastField modulus} : +private theorem toField_eq_val_toNat_cast_mul_inv {x : FastField modulus} : toField x = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - convert reduce_cast (modulus := modulus) ?_ using 1 + convert reduce_val_toNat_cast (modulus := modulus) ?_ using 1 -private theorem val_cast_eq_toField_mul {x : FastField modulus} : +private theorem val_toNat_cast_eq_toField_mul {x : FastField modulus} : (x.val.toNat : ZMod modulus) = toField x * ((2 ^ 32 : ℕ) : ZMod modulus) := by - rw [toField_eq_val_mul_inv, mul_assoc] + rw [toField_eq_val_toNat_cast_mul_inv, mul_assoc] rw [inv_mul_cancel₀ P.two_pow_32_ne_zero, mul_one] -private theorem ofCanonicalNat_toNat_cast {n : ℕ} (h : n < modulus) : +private theorem ofCanonicalNat_val_toNat_cast {n : ℕ} (h : n < modulus) : ((ofCanonicalNat (modulus := modulus) n h).val.toNat : ZMod modulus) = (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by - convert reduce_cast (modulus := modulus) ?_ using 1 + convert reduce_val_toNat_cast (modulus := modulus) ?_ using 1 simp only [Nat.cast_pow, Nat.cast_ofNat, UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64, P.r2ModModulus_toNat, Nat.mod_mul_mod] have hprod : n * ((2 ^ 32) ^ 2 % modulus) < 2 ^ 64 := by @@ -335,7 +335,7 @@ private theorem ofCanonicalNat_toNat_cast {n : ℕ} (h : n < modulus) : @[simp] theorem toField_ofCanonicalNat {n : ℕ} (h : n < modulus) : toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := by - rw [toField_eq_val_mul_inv, ofCanonicalNat_toNat_cast, mul_assoc] + rw [toField_eq_val_toNat_cast_mul_inv, ofCanonicalNat_val_toNat_cast, mul_assoc] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero, mul_one] @[simp] @@ -354,8 +354,8 @@ theorem ofField_toField (x : FastField modulus) : ofField (toField x) = x := by apply Subtype.ext apply UInt32.toNat_inj.mp apply natCast_inj_of_lt - · rw [val_cast_eq_toField_mul, toField_ofField] - rw [val_cast_eq_toField_mul] + · rw [val_toNat_cast_eq_toField_mul, toField_ofField] + rw [val_toNat_cast_eq_toField_mul] · simp · simp @@ -368,20 +368,20 @@ theorem toField_injective : Function.Injective (toField (modulus := modulus)) := /-- `toField` maps fast zero to canonical zero. -/ @[simp] theorem toField_zero : toField (0 : FastField modulus) = 0 := by - simp [toField_eq_val_mul_inv, zero_def, zero] + simp [toField_eq_val_toNat_cast_mul_inv, zero_def, zero] /-- `toField` maps fast one to canonical one. -/ @[simp] theorem toField_one : toField (1 : FastField modulus) = 1 := by - simp only [toField_eq_val_mul_inv, one_def, one, + simp only [toField_eq_val_toNat_cast_mul_inv, one_def, one, Mont32Field.rModModulus_toNat, ZMod.natCast_mod] exact mul_inv_cancel₀ P.two_pow_32_ne_zero /-- Fast addition agrees with addition in the canonical field. -/ @[simp] theorem toField_add (x y : FastField modulus) : toField (x + y) = toField x + toField y := by - rw [toField_eq_val_mul_inv, toField_eq_val_mul_inv (x := x), - toField_eq_val_mul_inv (x := y), add_def, add] + rw [toField_eq_val_toNat_cast_mul_inv, toField_eq_val_toNat_cast_mul_inv (x := x), + toField_eq_val_toNat_cast_mul_inv (x := y), add_def, add] have hred := conditionalSubtract_cast (p32 := P.modulus32) (u := x.val + y.val) rw [P.modulus32_toNat] at hred rw [hred, UInt32.toNat_add] @@ -393,8 +393,8 @@ theorem toField_add (x y : FastField modulus) : toField (x + y) = toField x + to /-- Fast subtraction agrees with subtraction in the canonical field. -/ @[simp] theorem toField_sub (x y : FastField modulus) : toField (x - y) = toField x - toField y := by - rw [toField_eq_val_mul_inv, toField_eq_val_mul_inv (x := x), - toField_eq_val_mul_inv (x := y)] + rw [toField_eq_val_toNat_cast_mul_inv, toField_eq_val_toNat_cast_mul_inv (x := x), + toField_eq_val_toNat_cast_mul_inv (x := y)] simp only [sub_def] by_cases hyx : y.val ≤ x.val · simp only [sub, hyx, ↓reduceDIte] @@ -422,7 +422,7 @@ theorem toField_sub (x y : FastField modulus) : toField (x - y) = toField x - to /-- Fast negation agrees with negation in the canonical field. -/ @[simp] theorem toField_neg (x : FastField modulus) : toField (-x) = -toField x := by - rw [toField_eq_val_mul_inv, toField_eq_val_mul_inv (x := x)] + rw [toField_eq_val_toNat_cast_mul_inv, toField_eq_val_toNat_cast_mul_inv (x := x)] simp only [neg_def] by_cases hx : x.val = 0 · simp only [neg, hx, ↓reduceDIte, zero] @@ -440,11 +440,11 @@ theorem toField_neg (x : FastField modulus) : toField (-x) = -toField x := by ring /-- Fast multiplication agrees with multiplication in the canonical field. -/ -private theorem mul_toNat_cast (x y : FastField modulus) : - ((mul x y).val.toNat : ZMod modulus) = - (x.val.toNat : ZMod modulus) * (y.val.toNat : ZMod modulus) * - ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - convert reduce_cast (modulus := modulus) ?_ using 1 +private theorem mul_val_toNat_cast (x y : FastField modulus) : + ((mul x y).val.toNat : ZMod modulus) = + (x.val.toNat : ZMod modulus) * (y.val.toNat : ZMod modulus) * + ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by + convert reduce_val_toNat_cast ?_ using 1 simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64] have hprod : x.val.toNat * y.val.toNat < 2 ^ 64 := by nlinarith [x.property, y.property, P.modulus_sq_lt_two_pow_64] @@ -452,21 +452,18 @@ private theorem mul_toNat_cast (x y : FastField modulus) : @[simp] theorem toField_mul (x y : FastField modulus) : toField (x * y) = toField x * toField y := by - rw [toField_eq_val_mul_inv, toField_eq_val_mul_inv (x := x), - toField_eq_val_mul_inv (x := y), mul_def, mul_toNat_cast] + rw [toField_eq_val_toNat_cast_mul_inv, toField_eq_val_toNat_cast_mul_inv (x := x), + toField_eq_val_toNat_cast_mul_inv (x := y), mul_def, mul_val_toNat_cast] ring -private theorem mul_assoc_field (x y z : FastField modulus) : (x * y) * z = x * (y * z) := by +private theorem mul_assoc (x y z : FastField modulus) : (x * y) * z = x * (y * z) := by apply toField_injective rw [toField_mul, toField_mul, toField_mul, toField_mul] ring private theorem pow_succ_field (x : FastField modulus) (n : ℕ) : pow x (n + 1) = pow x n * x := by unfold pow - letI : Semigroup (FastField modulus) := { - mul := (· * ·) - mul_assoc := mul_assoc_field - } + letI : Semigroup (FastField modulus) := { mul, mul_assoc } exact npowBinRec_succ n x /-- Fast squaring agrees with multiplication by itself in the canonical field. -/ From c8b8e6b6493f016e2392146fdc8c5bc82efd3c1a Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 17:29:32 +0200 Subject: [PATCH 30/31] make R2 a uint64 and simplify --- CompPoly/Fields/Montgomery/Native32Field.lean | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CompPoly/Fields/Montgomery/Native32Field.lean b/CompPoly/Fields/Montgomery/Native32Field.lean index 4ee22ab0..c8265181 100644 --- a/CompPoly/Fields/Montgomery/Native32Field.lean +++ b/CompPoly/Fields/Montgomery/Native32Field.lean @@ -30,7 +30,7 @@ class Mont32Field (modulus : ℕ) where /-- `2^32 mod modulus`, the Montgomery representation of one. -/ rModModulus : UInt32 /-- `(2^32)^2 mod modulus`, used to enter Montgomery form. -/ - r2ModModulus : UInt32 + r2ModModulus : UInt64 /-- `-modulus⁻¹ mod 2^32`, used by Montgomery reduction. -/ montgomeryNegInv : UInt32 modulus32_toNat : modulus32.toNat = modulus := by decide @@ -108,9 +108,9 @@ namespace FastField /-- Build a fast element from a canonical natural representative. -/ @[inline] def ofCanonicalNat (n : ℕ) (h : n < modulus) : FastField modulus := - reduce (UInt64.ofNat n * P.r2ModModulus.toUInt64) <| by - simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt32.toNat_toUInt64, - P.r2ModModulus_toNat, Nat.mod_mul_mod] + reduce (UInt64.ofNat n * P.r2ModModulus) <| by + simp only [UInt64.toNat_mul, UInt64.toNat_ofNat', P.r2ModModulus_toNat, + Nat.mod_mul_mod] apply Nat.mod_lt_of_lt grw [Nat.mod_lt _ P.modulus_pos] apply mul_lt_mul <;> simp [h, le_of_lt] @@ -123,7 +123,7 @@ def ofNat (modulus : ℕ) [P : Mont32Field modulus] (n : ℕ) : FastField modulu /-- Convert a 32-bit word into fast Montgomery representation. -/ @[inline] def ofUInt32 (modulus : ℕ) [P : Mont32Field modulus] (x : UInt32) : FastField modulus := - reduce (x.toUInt64 * P.r2ModModulus.toUInt64) <| by + reduce (x.toUInt64 * P.r2ModModulus) <| by simp only [UInt64.toNat_mul, UInt32.toNat_toUInt64, P.r2ModModulus_toNat] apply Nat.mod_lt_of_lt grw [Nat.mod_lt _ P.modulus_pos, mul_comm modulus] @@ -313,7 +313,7 @@ theorem toNat_lt_modulus {x : FastField modulus} : toNat x < modulus := by private theorem toField_eq_val_toNat_cast_mul_inv {x : FastField modulus} : toField x = (x.val.toNat : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus)⁻¹ := by - convert reduce_val_toNat_cast (modulus := modulus) ?_ using 1 + convert reduce_val_toNat_cast ?_ using 1 private theorem val_toNat_cast_eq_toField_mul {x : FastField modulus} : (x.val.toNat : ZMod modulus) = @@ -322,11 +322,11 @@ private theorem val_toNat_cast_eq_toField_mul {x : FastField modulus} : rw [inv_mul_cancel₀ P.two_pow_32_ne_zero, mul_one] private theorem ofCanonicalNat_val_toNat_cast {n : ℕ} (h : n < modulus) : - ((ofCanonicalNat (modulus := modulus) n h).val.toNat : ZMod modulus) = + ((ofCanonicalNat n h).val.toNat : ZMod modulus) = (n : ZMod modulus) * ((2 ^ 32 : ℕ) : ZMod modulus) := by - convert reduce_val_toNat_cast (modulus := modulus) ?_ using 1 + convert reduce_val_toNat_cast ?_ using 1 simp only [Nat.cast_pow, Nat.cast_ofNat, UInt64.toNat_mul, UInt64.toNat_ofNat', - UInt32.toNat_toUInt64, P.r2ModModulus_toNat, Nat.mod_mul_mod] + P.r2ModModulus_toNat, Nat.mod_mul_mod] have hprod : n * ((2 ^ 32) ^ 2 % modulus) < 2 ^ 64 := by nlinarith [P.r2ModModulus_lt_modulus, P.modulus_sq_lt_two_pow_64] rw [Nat.mod_eq_of_lt hprod] @@ -334,18 +334,18 @@ private theorem ofCanonicalNat_val_toNat_cast {n : ℕ} (h : n < modulus) : @[simp] theorem toField_ofCanonicalNat {n : ℕ} (h : n < modulus) : - toField (ofCanonicalNat (modulus := modulus) n h) = (n : ZMod modulus) := by + toField (ofCanonicalNat n h) = (n : ZMod modulus) := by rw [toField_eq_val_toNat_cast_mul_inv, ofCanonicalNat_val_toNat_cast, mul_assoc] rw [mul_inv_cancel₀ P.two_pow_32_ne_zero, mul_one] @[simp] theorem toNat_ofCanonicalNat {n : ℕ} (h : n < modulus) : - toNat (ofCanonicalNat (modulus := modulus) n h) = n := + toNat (ofCanonicalNat n h) = n := natCast_inj_of_lt (toField_ofCanonicalNat h) toNat_lt_modulus h /-- Converting from the canonical field to fast form and back is the identity. -/ @[simp] -theorem toField_ofField (x : ZMod modulus) : toField (ofField (modulus := modulus) x) = x := by +theorem toField_ofField (x : ZMod modulus) : toField (ofField x) = x := by simp [ofField, toField_ofCanonicalNat] /-- Converting from fast form to the canonical field and back is the identity. -/ From 0958afd8a30d3133d72458775200710836478f4c Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2026 18:21:39 +0200 Subject: [PATCH 31/31] refactor(fields): remove redundant fast field wrappers --- .../Root/FieldRoots/KoalaBear.lean | 4 +- CompPoly/Fields/BabyBear/Fast.lean | 149 ------------------ CompPoly/Fields/KoalaBear/Fast.lean | 149 ------------------ CompPoly/Fields/Montgomery/Native32.lean | 4 +- tests/CompPolyTests/Fields/BabyBear/Fast.lean | 2 +- .../CompPolyTests/Fields/KoalaBear/Fast.lean | 2 +- 6 files changed, 6 insertions(+), 304 deletions(-) diff --git a/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean b/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean index d07fb8fe..9af2e636 100644 --- a/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean +++ b/CompPoly/Bivariate/GuruswamiSudan/Root/FieldRoots/KoalaBear.lean @@ -124,7 +124,7 @@ def fastKoalaBearFiniteFieldContext : frobenius_fixed := by intro a apply Montgomery.Native32.toField_injective - rw [KoalaBear.Fast.toField_npow] + rw [Montgomery.Native32.toField_npow] simp [KoalaBear.fieldSize] /-- Primitive generator transported to native-word fast KoalaBear. -/ @@ -138,7 +138,7 @@ lemma fastKoalaBearPrimitiveRoot_order : have h := MulEquiv.orderOf_eq KoalaBear.Fast.ringEquiv.toMulEquiv (KoalaBear.Fast.ofField KoalaBear.primitiveRoot) rw [← h] - simpa [KoalaBear.Fast.ringEquiv_apply, KoalaBear.Fast.toField_ofField] using + simpa [KoalaBear.Fast.ringEquiv, KoalaBear.Fast.ofField] using KoalaBear.primitiveRoot_order /-- Smooth cyclic splitter context for native-word fast KoalaBear. -/ diff --git a/CompPoly/Fields/BabyBear/Fast.lean b/CompPoly/Fields/BabyBear/Fast.lean index 2c733f0e..20cb5efd 100644 --- a/CompPoly/Fields/BabyBear/Fast.lean +++ b/CompPoly/Fields/BabyBear/Fast.lean @@ -46,159 +46,10 @@ def ofUInt32 (x : UInt32) : Field := def ofField (x : BabyBear.Field) : Field := Montgomery.Native32.FastField.ofField x -/-! ## Arithmetic -/ - -/-- Fast modular addition in Montgomery form. -/ -@[inline] -def add (x y : Field) : Field := Montgomery.Native32.add x y - -/-- Fast modular negation in Montgomery form. -/ -@[inline] -def neg (x : Field) : Field := Montgomery.Native32.neg x - -/-- Fast modular subtraction in Montgomery form. -/ -@[inline] -def sub (x y : Field) : Field := Montgomery.Native32.sub x y - -/-- Fast modular multiplication in Montgomery form. -/ -@[inline] -def mul (x y : Field) : Field := Montgomery.Native32.mul x y - -/-- Fast squaring. -/ -@[inline] -def square (x : Field) : Field := Montgomery.Native32.square x - -/-- Exponentiation over the fast representation using repeated squaring. -/ -@[inline] -def pow (x : Field) (n : ℕ) : Field := Montgomery.Native32.pow x n - -/-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ -@[inline] -def inv (x : Field) : Field := Montgomery.Native32.inv x - -/-- Division through inversion and fast multiplication. -/ -@[inline] -def div (x y : Field) : Field := Montgomery.Native32.div x y - /-! ## Canonical bridge -/ /-- Ring equivalence between the fast Montgomery representation and canonical `BabyBear.Field`. -/ def ringEquiv : Field ≃+* BabyBear.Field := Montgomery.Native32.ringEquiv BabyBear.fieldSize -/-- Converting from the canonical field to fast form and back is the identity. -/ -@[simp] -theorem toField_ofField (x : BabyBear.Field) : toField (ofField x) = x := - Montgomery.Native32.toField_ofField x - -/-- Converting from fast form to the canonical field and back is the identity. -/ -@[simp] -theorem ofField_toField (x : Field) : ofField (toField x) = x := - Montgomery.Native32.ofField_toField x - -/-- `toField` maps fast zero to canonical zero. -/ -@[simp] -theorem toField_zero : toField (0 : Field) = 0 := - Montgomery.Native32.toField_zero - -/-- `toField` maps fast one to canonical one. -/ -@[simp] -theorem toField_one : toField (1 : Field) = 1 := - Montgomery.Native32.toField_one - -/-- Fast addition agrees with addition in the canonical BabyBear field. -/ -@[simp] -theorem toField_add (x y : Field) : toField (x + y) = toField x + toField y := - Montgomery.Native32.toField_add x y - -/-- Fast subtraction agrees with subtraction in the canonical BabyBear field. -/ -@[simp] -theorem toField_sub (x y : Field) : toField (x - y) = toField x - toField y := - Montgomery.Native32.toField_sub x y - -/-- Fast negation agrees with negation in the canonical BabyBear field. -/ -@[simp] -theorem toField_neg (x : Field) : toField (-x) = -toField x := - Montgomery.Native32.toField_neg x - -/-- Fast multiplication agrees with multiplication in the canonical BabyBear field. -/ -@[simp] -theorem toField_mul (x y : Field) : toField (x * y) = toField x * toField y := - Montgomery.Native32.toField_mul x y - -/-- Applying `ringEquiv` is the same as interpreting a fast value canonically. -/ -@[simp] -theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := - Montgomery.Native32.ringEquiv_apply - -/-- Applying the inverse `ringEquiv` is conversion into fast Montgomery form. -/ -@[simp] -theorem ringEquiv_symm_apply (x : BabyBear.Field) : ringEquiv.symm x = ofField x := - Montgomery.Native32.ringEquiv_symm_apply - -/-- Fast squaring agrees with multiplication by itself in the canonical field. -/ -@[simp] -theorem toField_square (x : Field) : toField (square x) = toField x * toField x := - Montgomery.Native32.toField_square x - -/-- Fast inversion agrees with inversion in the canonical BabyBear field. -/ -@[simp] -theorem toField_inv (x : Field) : toField x⁻¹ = (toField x)⁻¹ := - Montgomery.Native32.toField_inv x - -/-- Fast division agrees with division in the canonical BabyBear field. -/ -@[simp] -theorem toField_div (x y : Field) : toField (x / y) = toField x / toField y := - Montgomery.Native32.toField_div x y - -/-- Natural casts into fast form agree with natural casts into the canonical field. -/ -@[simp] -theorem toField_natCast (n : ℕ) : toField (n : Field) = (n : BabyBear.Field) := - Montgomery.Native32.toField_natCast n - -/-- Integer casts into fast form agree with integer casts into the canonical field. -/ -@[simp] -theorem toField_intCast (n : Int) : toField (n : Field) = (n : BabyBear.Field) := - Montgomery.Native32.toField_intCast n - -/-- Natural scalar multiplication is preserved by `toField`. -/ -@[simp] -theorem toField_nsmul (n : ℕ) (x : Field) : toField (n • x) = n • toField x := - Montgomery.Native32.toField_nsmul n x - -/-- Integer scalar multiplication is preserved by `toField`. -/ -@[simp] -theorem toField_zsmul (n : Int) (x : Field) : toField (n • x) = n • toField x := - Montgomery.Native32.toField_zsmul n x - -/-- Natural powers through the `Pow` instance are preserved by `toField`. -/ -@[simp] -theorem toField_npow (x : Field) (n : ℕ) : toField (x ^ n) = toField x ^ n := - Montgomery.Native32.toField_npow x n - -/-- Integer powers through the `Pow` instance are preserved by `toField`. -/ -@[simp] -theorem toField_zpow (x : Field) (n : Int) : toField (x ^ n) = toField x ^ n := - Montgomery.Native32.toField_zpow x n - -/-- Nonnegative rational casts into fast form agree with canonical-field casts. -/ -@[simp] -theorem toField_nnratCast (q : ℚ≥0) : toField (q : Field) = (q : BabyBear.Field) := - Montgomery.Native32.toField_nnratCast q - -/-- Rational casts into fast form agree with canonical-field casts. -/ -@[simp] -theorem toField_ratCast (q : ℚ) : toField (q : Field) = (q : BabyBear.Field) := - Montgomery.Native32.toField_ratCast q - -/-- Nonnegative rational scalar multiplication is preserved by `toField`. -/ -@[simp] -theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • toField x := - Montgomery.Native32.toField_nnqsmul q x - -/-- Rational scalar multiplication is preserved by `toField`. -/ -@[simp] -theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := - Montgomery.Native32.toField_qsmul q x - end BabyBear.Fast diff --git a/CompPoly/Fields/KoalaBear/Fast.lean b/CompPoly/Fields/KoalaBear/Fast.lean index ea3330bb..8c1983c9 100644 --- a/CompPoly/Fields/KoalaBear/Fast.lean +++ b/CompPoly/Fields/KoalaBear/Fast.lean @@ -46,161 +46,12 @@ def ofUInt32 (x : UInt32) : Field := def ofField (x : KoalaBear.Field) : Field := Montgomery.Native32.FastField.ofField x -/-! ## Arithmetic -/ - -/-- Fast modular addition in Montgomery form. -/ -@[inline] -def add (x y : Field) : Field := Montgomery.Native32.add x y - -/-- Fast modular negation in Montgomery form. -/ -@[inline] -def neg (x : Field) : Field := Montgomery.Native32.neg x - -/-- Fast modular subtraction in Montgomery form. -/ -@[inline] -def sub (x y : Field) : Field := Montgomery.Native32.sub x y - -/-- Fast modular multiplication in Montgomery form. -/ -@[inline] -def mul (x y : Field) : Field := Montgomery.Native32.mul x y - -/-- Fast squaring. -/ -@[inline] -def square (x : Field) : Field := Montgomery.Native32.square x - -/-- Exponentiation over the fast representation using repeated squaring. -/ -@[inline] -def pow (x : Field) (n : ℕ) : Field := Montgomery.Native32.pow x n - -/-- Inversion in Montgomery form via Fermat's little theorem (`x⁻¹ = x^(p-2)`). -/ -@[inline] -def inv (x : Field) : Field := Montgomery.Native32.inv x - -/-- Division through inversion and fast multiplication. -/ -@[inline] -def div (x y : Field) : Field := Montgomery.Native32.div x y - /-! ## Canonical bridge -/ /-- Ring equivalence between the fast Montgomery representation and canonical `KoalaBear.Field`. -/ def ringEquiv : Field ≃+* KoalaBear.Field := Montgomery.Native32.ringEquiv KoalaBear.fieldSize -/-- Converting from the canonical field to fast form and back is the identity. -/ -@[simp] -theorem toField_ofField (x : KoalaBear.Field) : toField (ofField x) = x := - Montgomery.Native32.toField_ofField x - -/-- Converting from fast form to the canonical field and back is the identity. -/ -@[simp] -theorem ofField_toField (x : Field) : ofField (toField x) = x := - Montgomery.Native32.ofField_toField x - -/-- `toField` maps fast zero to canonical zero. -/ -@[simp] -theorem toField_zero : toField (0 : Field) = 0 := - Montgomery.Native32.toField_zero - -/-- `toField` maps fast one to canonical one. -/ -@[simp] -theorem toField_one : toField (1 : Field) = 1 := - Montgomery.Native32.toField_one - -/-- Fast addition agrees with addition in the canonical KoalaBear field. -/ -@[simp] -theorem toField_add (x y : Field) : toField (x + y) = toField x + toField y := - Montgomery.Native32.toField_add x y - -/-- Fast subtraction agrees with subtraction in the canonical KoalaBear field. -/ -@[simp] -theorem toField_sub (x y : Field) : toField (x - y) = toField x - toField y := - Montgomery.Native32.toField_sub x y - -/-- Fast negation agrees with negation in the canonical KoalaBear field. -/ -@[simp] -theorem toField_neg (x : Field) : toField (-x) = -toField x := - Montgomery.Native32.toField_neg x - -/-- Fast multiplication agrees with multiplication in the canonical KoalaBear field. -/ -@[simp] -theorem toField_mul (x y : Field) : toField (x * y) = toField x * toField y := - Montgomery.Native32.toField_mul x y - -/-- Applying `ringEquiv` is the same as interpreting a fast value canonically. -/ -@[simp] -theorem ringEquiv_apply (x : Field) : ringEquiv x = toField x := - Montgomery.Native32.ringEquiv_apply - -/-- Applying the inverse `ringEquiv` is conversion into fast Montgomery form. -/ -@[simp] -theorem ringEquiv_symm_apply (x : KoalaBear.Field) : ringEquiv.symm x = ofField x := - Montgomery.Native32.ringEquiv_symm_apply - -/-- Fast squaring agrees with multiplication by itself in the canonical field. -/ -@[simp] -theorem toField_square (x : Field) : toField (square x) = toField x * toField x := - Montgomery.Native32.toField_square x - -/-- Fast inversion agrees with inversion in the canonical KoalaBear field. -/ -@[simp] -theorem toField_inv (x : Field) : toField x⁻¹ = (toField x)⁻¹ := - Montgomery.Native32.toField_inv x - -/-- Fast division agrees with division in the canonical KoalaBear field. -/ -@[simp] -theorem toField_div (x y : Field) : toField (x / y) = toField x / toField y := - Montgomery.Native32.toField_div x y - -/-- Natural casts into fast form agree with natural casts into the canonical field. -/ -@[simp] -theorem toField_natCast (n : ℕ) : toField (n : Field) = (n : KoalaBear.Field) := - Montgomery.Native32.toField_natCast n - -/-- Integer casts into fast form agree with integer casts into the canonical field. -/ -@[simp] -theorem toField_intCast (n : Int) : toField (n : Field) = (n : KoalaBear.Field) := - Montgomery.Native32.toField_intCast n - -/-- Natural scalar multiplication is preserved by `toField`. -/ -@[simp] -theorem toField_nsmul (n : ℕ) (x : Field) : toField (n • x) = n • toField x := - Montgomery.Native32.toField_nsmul n x - -/-- Integer scalar multiplication is preserved by `toField`. -/ -@[simp] -theorem toField_zsmul (n : Int) (x : Field) : toField (n • x) = n • toField x := - Montgomery.Native32.toField_zsmul n x - -/-- Natural powers through the `Pow` instance are preserved by `toField`. -/ -@[simp] -theorem toField_npow (x : Field) (n : ℕ) : toField (x ^ n) = toField x ^ n := - Montgomery.Native32.toField_npow x n - -/-- Integer powers through the `Pow` instance are preserved by `toField`. -/ -@[simp] -theorem toField_zpow (x : Field) (n : Int) : toField (x ^ n) = toField x ^ n := - Montgomery.Native32.toField_zpow x n - -/-- Nonnegative rational casts into fast form agree with canonical-field casts. -/ -@[simp] -theorem toField_nnratCast (q : ℚ≥0) : toField (q : Field) = (q : KoalaBear.Field) := - Montgomery.Native32.toField_nnratCast q - -/-- Rational casts into fast form agree with canonical-field casts. -/ -@[simp] -theorem toField_ratCast (q : ℚ) : toField (q : Field) = (q : KoalaBear.Field) := - Montgomery.Native32.toField_ratCast q - -/-- Nonnegative rational scalar multiplication is preserved by `toField`. -/ -@[simp] -theorem toField_nnqsmul (q : ℚ≥0) (x : Field) : toField (q • x) = q • toField x := - Montgomery.Native32.toField_nnqsmul q x - -/-- Rational scalar multiplication is preserved by `toField`. -/ -@[simp] -theorem toField_qsmul (q : ℚ) (x : Field) : toField (q • x) = q • toField x := - Montgomery.Native32.toField_qsmul q x - /-! ## Two-adic roots -/ /-- Precomputed KoalaBear two-adic generators in Montgomery representation. -/ diff --git a/CompPoly/Fields/Montgomery/Native32.lean b/CompPoly/Fields/Montgomery/Native32.lean index d35b3363..bba0de97 100644 --- a/CompPoly/Fields/Montgomery/Native32.lean +++ b/CompPoly/Fields/Montgomery/Native32.lean @@ -33,8 +33,8 @@ def conditionalSubtract (p32 : UInt32) (u : UInt32) : UInt32 := variable {modulus : ℕ} {p32 negInv u : UInt32} {p64 x : UInt64} theorem reduceRaw_eq_conditionalSubtract : - reduceRaw p32 p64 negInv x = - conditionalSubtract p32 (reduceQuotient negInv p64 x) := rfl + reduceRaw p32 p64 negInv x = + conditionalSubtract p32 (reduceQuotient negInv p64 x) := rfl /-- The native quotient agrees with the `Nat`-level Montgomery quotient. -/ theorem reduceQuotient_toNat (hp_pos : 0 < p64.toNat) (hbound : p64.toNat < 2 ^ 31) diff --git a/tests/CompPolyTests/Fields/BabyBear/Fast.lean b/tests/CompPolyTests/Fields/BabyBear/Fast.lean index b1b8f63f..909414df 100644 --- a/tests/CompPolyTests/Fields/BabyBear/Fast.lean +++ b/tests/CompPolyTests/Fields/BabyBear/Fast.lean @@ -28,7 +28,7 @@ namespace BabyBear.Fast #guard (-(0 : Field)).toNat = 0 #guard (-(1 : Field)).toNat = BabyBear.fieldSize - 1 #guard ((BabyBear.fieldSize - 1 : Field) * (BabyBear.fieldSize - 1 : Field)).toNat = 1 -#guard (square (12345 : Field)).toNat = 152399025 +#guard ((12345 : Field) * 12345).toNat = 152399025 #guard ((37 : Field) ^ 0).toNat = 1 #guard ((37 : Field) ^ 1).toNat = 37 #guard ((123456789 : Field) ^ 17).toField = ((123456789 : BabyBear.Field) ^ 17) diff --git a/tests/CompPolyTests/Fields/KoalaBear/Fast.lean b/tests/CompPolyTests/Fields/KoalaBear/Fast.lean index ee94cb4f..52f2d25a 100644 --- a/tests/CompPolyTests/Fields/KoalaBear/Fast.lean +++ b/tests/CompPolyTests/Fields/KoalaBear/Fast.lean @@ -28,7 +28,7 @@ namespace KoalaBear.Fast #guard (-(0 : Field)).toNat = 0 #guard (-(1 : Field)).toNat = KoalaBear.fieldSize - 1 #guard ((KoalaBear.fieldSize - 1 : Field) * (KoalaBear.fieldSize - 1 : Field)).toNat = 1 -#guard (square (12345 : Field)).toNat = 152399025 +#guard ((12345 : Field) * 12345).toNat = 152399025 #guard ((37 : Field) ^ 0).toNat = 1 #guard ((37 : Field) ^ 1).toNat = 37 #guard ((123456789 : Field) ^ 17).toField = ((123456789 : KoalaBear.Field) ^ 17)