412 lines
14 KiB
Text
412 lines
14 KiB
Text
/-
|
||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Scott Morrison
|
||
-/
|
||
prelude
|
||
import Init.Data.List.Lemmas
|
||
|
||
namespace Lean.Omega
|
||
|
||
/--
|
||
A type synonym for `List Int`, used by `omega` for dense representation of coefficients.
|
||
|
||
We define algebraic operations,
|
||
interpreting `List Int` as a finitely supported function `Nat → Int`.
|
||
-/
|
||
abbrev IntList := List Int
|
||
|
||
namespace IntList
|
||
|
||
/-- Get the `i`-th element (interpreted as `0` if the list is not long enough). -/
|
||
def get (xs : IntList) (i : Nat) : Int := (xs.get? i).getD 0
|
||
|
||
@[simp] theorem get_nil : get ([] : IntList) i = 0 := rfl
|
||
@[simp] theorem get_cons_zero : get (x :: xs) 0 = x := rfl
|
||
@[simp] theorem get_cons_succ : get (x :: xs) (i+1) = get xs i := rfl
|
||
|
||
theorem get_map {xs : IntList} (h : f 0 = 0) : get (xs.map f) i = f (xs.get i) := by
|
||
simp only [get, List.get?_map]
|
||
cases xs.get? i <;> simp_all
|
||
|
||
theorem get_of_length_le {xs : IntList} (h : xs.length ≤ i) : xs.get i = 0 := by
|
||
rw [get, List.get?_eq_none.mpr h]
|
||
rfl
|
||
|
||
-- theorem lt_length_of_get_nonzero {xs : IntList} (h : xs.get i ≠ 0) : i < xs.length := by
|
||
-- revert h
|
||
-- simpa using mt get_of_length_le
|
||
|
||
/-- Like `List.set`, but right-pad with zeroes as necessary first. -/
|
||
def set (xs : IntList) (i : Nat) (y : Int) : IntList :=
|
||
match xs, i with
|
||
| [], 0 => [y]
|
||
| [], (i+1) => 0 :: set [] i y
|
||
| _ :: xs, 0 => y :: xs
|
||
| x :: xs, (i+1) => x :: set xs i y
|
||
|
||
@[simp] theorem set_nil_zero : set [] 0 y = [y] := rfl
|
||
@[simp] theorem set_nil_succ : set [] (i+1) y = 0 :: set [] i y := rfl
|
||
@[simp] theorem set_cons_zero : set (x :: xs) 0 y = y :: xs := rfl
|
||
@[simp] theorem set_cons_succ : set (x :: xs) (i+1) y = x :: set xs i y := rfl
|
||
|
||
/-- Returns the leading coefficient, i.e. the first non-zero entry. -/
|
||
def leading (xs : IntList) : Int := xs.find? (! · == 0) |>.getD 0
|
||
|
||
/-- Implementation of `+` on `IntList`. -/
|
||
def add (xs ys : IntList) : IntList :=
|
||
List.zipWithAll (fun x y => x.getD 0 + y.getD 0) xs ys
|
||
|
||
instance : Add IntList := ⟨add⟩
|
||
|
||
theorem add_def (xs ys : IntList) :
|
||
xs + ys = List.zipWithAll (fun x y => x.getD 0 + y.getD 0) xs ys :=
|
||
rfl
|
||
|
||
@[simp] theorem add_get (xs ys : IntList) (i : Nat) : (xs + ys).get i = xs.get i + ys.get i := by
|
||
simp only [add_def, get, List.zipWithAll_get?, List.get?_eq_none]
|
||
cases xs.get? i <;> cases ys.get? i <;> simp
|
||
|
||
@[simp] theorem add_nil (xs : IntList) : xs + [] = xs := by simp [add_def]
|
||
@[simp] theorem nil_add (xs : IntList) : [] + xs = xs := by simp [add_def]
|
||
@[simp] theorem cons_add_cons (x) (xs : IntList) (y) (ys : IntList) :
|
||
(x :: xs) + (y :: ys) = (x + y) :: (xs + ys) := by simp [add_def]
|
||
|
||
/-- Implementation of `*` on `IntList`. -/
|
||
def mul (xs ys : IntList) : IntList := List.zipWith (· * ·) xs ys
|
||
|
||
instance : Mul IntList := ⟨mul⟩
|
||
|
||
theorem mul_def (xs ys : IntList) : xs * ys = List.zipWith (· * ·) xs ys :=
|
||
rfl
|
||
|
||
@[simp] theorem mul_get (xs ys : IntList) (i : Nat) : (xs * ys).get i = xs.get i * ys.get i := by
|
||
simp only [mul_def, get, List.zipWith_get?]
|
||
cases xs.get? i <;> cases ys.get? i <;> simp
|
||
|
||
@[simp] theorem mul_nil_left : ([] : IntList) * ys = [] := rfl
|
||
@[simp] theorem mul_nil_right : xs * ([] : IntList) = [] := List.zipWith_nil_right
|
||
@[simp] theorem mul_cons₂ : (x::xs : IntList) * (y::ys) = (x * y) :: (xs * ys) := rfl
|
||
|
||
/-- Implementation of negation on `IntList`. -/
|
||
def neg (xs : IntList) : IntList := xs.map fun x => -x
|
||
|
||
instance : Neg IntList := ⟨neg⟩
|
||
|
||
theorem neg_def (xs : IntList) : - xs = xs.map fun x => -x := rfl
|
||
|
||
@[simp] theorem neg_get (xs : IntList) (i : Nat) : (- xs).get i = - xs.get i := by
|
||
simp only [neg_def, get, List.get?_map]
|
||
cases xs.get? i <;> simp
|
||
|
||
@[simp] theorem neg_nil : (- ([] : IntList)) = [] := rfl
|
||
@[simp] theorem neg_cons : (- (x::xs : IntList)) = -x :: -xs := rfl
|
||
|
||
/-- Implementation of subtraction on `IntList`. -/
|
||
def sub (xs ys : IntList) : IntList :=
|
||
List.zipWithAll (fun x y => x.getD 0 - y.getD 0) xs ys
|
||
|
||
instance : Sub IntList := ⟨sub⟩
|
||
|
||
theorem sub_def (xs ys : IntList) :
|
||
xs - ys = List.zipWithAll (fun x y => x.getD 0 - y.getD 0) xs ys :=
|
||
rfl
|
||
|
||
/-- Implementation of scalar multiplication by an integer on `IntList`. -/
|
||
def smul (xs : IntList) (i : Int) : IntList :=
|
||
xs.map fun x => i * x
|
||
|
||
instance : HMul Int IntList IntList where
|
||
hMul i xs := xs.smul i
|
||
|
||
theorem smul_def (xs : IntList) (i : Int) : i * xs = xs.map fun x => i * x := rfl
|
||
|
||
@[simp] theorem smul_get (xs : IntList) (a : Int) (i : Nat) : (a * xs).get i = a * xs.get i := by
|
||
simp only [smul_def, get, List.get?_map]
|
||
cases xs.get? i <;> simp
|
||
|
||
@[simp] theorem smul_nil {i : Int} : i * ([] : IntList) = [] := rfl
|
||
@[simp] theorem smul_cons {i : Int} : i * (x::xs : IntList) = i * x :: i * xs := rfl
|
||
|
||
/-- A linear combination of two `IntList`s. -/
|
||
def combo (a : Int) (xs : IntList) (b : Int) (ys : IntList) : IntList :=
|
||
List.zipWithAll (fun x y => a * x.getD 0 + b * y.getD 0) xs ys
|
||
|
||
theorem combo_eq_smul_add_smul (a : Int) (xs : IntList) (b : Int) (ys : IntList) :
|
||
combo a xs b ys = a * xs + b * ys := by
|
||
dsimp [combo]
|
||
induction xs generalizing ys with
|
||
| nil => simp; rfl
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => simp; rfl
|
||
| cons y ys => simp_all
|
||
|
||
attribute [local simp] add_def mul_def in
|
||
theorem mul_distrib_left (xs ys zs : IntList) : (xs + ys) * zs = xs * zs + ys * zs := by
|
||
induction xs generalizing ys zs with
|
||
| nil =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons _ _ =>
|
||
cases zs with
|
||
| nil => simp
|
||
| cons _ _ => simp_all [Int.add_mul]
|
||
| cons x xs ih₁ =>
|
||
cases ys with
|
||
| nil => simp_all
|
||
| cons _ _ =>
|
||
cases zs with
|
||
| nil => simp
|
||
| cons _ _ => simp_all [Int.add_mul]
|
||
|
||
theorem mul_neg_left (xs ys : IntList) : (-xs) * ys = -(xs * ys) := by
|
||
induction xs generalizing ys with
|
||
| nil => simp
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys => simp_all [Int.neg_mul]
|
||
|
||
attribute [local simp] add_def neg_def sub_def in
|
||
theorem sub_eq_add_neg (xs ys : IntList) : xs - ys = xs + (-ys) := by
|
||
induction xs generalizing ys with
|
||
| nil => simp; rfl
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys => simp_all [Int.sub_eq_add_neg]
|
||
|
||
@[simp] theorem mul_smul_left {i : Int} {xs ys : IntList} : (i * xs) * ys = i * (xs * ys) := by
|
||
induction xs generalizing ys with
|
||
| nil => simp
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys => simp_all [Int.mul_assoc]
|
||
|
||
/-- The sum of the entries of an `IntList`. -/
|
||
def sum (xs : IntList) : Int := xs.foldr (· + ·) 0
|
||
|
||
@[simp] theorem sum_nil : sum ([] : IntList) = 0 := rfl
|
||
@[simp] theorem sum_cons : sum (x::xs : IntList) = x + sum xs := rfl
|
||
|
||
attribute [local simp] sum add_def in
|
||
theorem sum_add (xs ys : IntList) : (xs + ys).sum = xs.sum + ys.sum := by
|
||
induction xs generalizing ys with
|
||
| nil => simp
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys => simp_all [Int.add_assoc, Int.add_left_comm]
|
||
|
||
@[simp]
|
||
theorem sum_neg (xs : IntList) : (-xs).sum = -(xs.sum) := by
|
||
induction xs with
|
||
| nil => simp
|
||
| cons x xs ih => simp_all [Int.neg_add]
|
||
|
||
@[simp]
|
||
theorem sum_smul (i : Int) (xs : IntList) : (i * xs).sum = i * (xs.sum) := by
|
||
induction xs with
|
||
| nil => simp
|
||
| cons x xs ih => simp_all [Int.mul_add]
|
||
|
||
/-- The dot product of two `IntList`s. -/
|
||
def dot (xs ys : IntList) : Int := (xs * ys).sum
|
||
|
||
example : IntList.dot [a, b, c] [x, y, z] = IntList.dot [a, b, c, d] [x, y, z] := rfl
|
||
example : IntList.dot [a, b, c] [x, y, z] = IntList.dot [a, b, c] [x, y, z, w] := rfl
|
||
|
||
@[local simp] theorem dot_nil_left : dot ([] : IntList) ys = 0 := rfl
|
||
@[simp] theorem dot_nil_right : dot xs ([] : IntList) = 0 := by simp [dot]
|
||
@[simp] theorem dot_cons₂ : dot (x::xs) (y::ys) = x * y + dot xs ys := rfl
|
||
|
||
-- theorem dot_comm (xs ys : IntList) : dot xs ys = dot ys xs := by
|
||
-- rw [dot, dot, mul_comm]
|
||
|
||
@[simp] theorem dot_set_left (xs ys : IntList) (i : Nat) (z : Int) :
|
||
dot (xs.set i z) ys = dot xs ys + (z - xs.get i) * ys.get i := by
|
||
induction xs generalizing i ys with
|
||
| nil =>
|
||
induction i generalizing ys with
|
||
| zero => cases ys <;> simp
|
||
| succ i => cases ys <;> simp_all
|
||
| cons x xs ih =>
|
||
induction i generalizing ys with
|
||
| zero =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys =>
|
||
simp only [Nat.zero_eq, set_cons_zero, dot_cons₂, get_cons_zero, Int.sub_mul]
|
||
rw [Int.add_right_comm, Int.add_comm (x * y), Int.sub_add_cancel]
|
||
| succ i =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys => simp_all [Int.add_assoc]
|
||
|
||
theorem dot_distrib_left (xs ys zs : IntList) : (xs + ys).dot zs = xs.dot zs + ys.dot zs := by
|
||
simp [dot, mul_distrib_left, sum_add]
|
||
|
||
@[simp] theorem dot_neg_left (xs ys : IntList) : (-xs).dot ys = -(xs.dot ys) := by
|
||
simp [dot, mul_neg_left]
|
||
|
||
@[simp] theorem dot_smul_left (xs ys : IntList) (i : Int) : (i * xs).dot ys = i * xs.dot ys := by
|
||
simp [dot]
|
||
|
||
theorem dot_of_left_zero (w : ∀ x, x ∈ xs → x = 0) : dot xs ys = 0 := by
|
||
induction xs generalizing ys with
|
||
| nil => simp
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys =>
|
||
rw [dot_cons₂, w x (by simp), ih]
|
||
· simp
|
||
· intro x m
|
||
apply w
|
||
exact List.mem_cons_of_mem _ m
|
||
|
||
/-- Division of an `IntList` by a integer. -/
|
||
def sdiv (xs : IntList) (g : Int) : IntList := xs.map fun x => x / g
|
||
|
||
@[simp] theorem sdiv_nil : sdiv [] g = [] := rfl
|
||
@[simp] theorem sdiv_cons : sdiv (x::xs) g = (x / g) :: sdiv xs g := rfl
|
||
|
||
/-- The gcd of the absolute values of the entries of an `IntList`. -/
|
||
def gcd (xs : IntList) : Nat := xs.foldr (fun x g => Nat.gcd x.natAbs g) 0
|
||
|
||
@[simp] theorem gcd_nil : gcd [] = 0 := rfl
|
||
@[simp] theorem gcd_cons : gcd (x :: xs) = Nat.gcd x.natAbs (gcd xs) := rfl
|
||
|
||
theorem gcd_cons_div_left : (gcd (x::xs) : Int) ∣ x := by
|
||
simp only [gcd, List.foldr_cons, Int.ofNat_dvd_left]
|
||
apply Nat.gcd_dvd_left
|
||
|
||
theorem gcd_cons_div_right : gcd (x::xs) ∣ gcd xs := by
|
||
simp only [gcd, List.foldr_cons]
|
||
apply Nat.gcd_dvd_right
|
||
|
||
theorem gcd_cons_div_right' : (gcd (x::xs) : Int) ∣ (gcd xs : Int) := by
|
||
rw [Int.ofNat_dvd_left, Int.natAbs_ofNat]
|
||
exact gcd_cons_div_right
|
||
|
||
theorem gcd_dvd (xs : IntList) {a : Int} (m : a ∈ xs) : (xs.gcd : Int) ∣ a := by
|
||
rw [Int.ofNat_dvd_left]
|
||
induction m with
|
||
| head =>
|
||
simp only [gcd_cons]
|
||
apply Nat.gcd_dvd_left
|
||
| tail b m ih => -- FIXME: why is the argument of tail implicit?
|
||
simp only [gcd_cons]
|
||
exact Nat.dvd_trans (Nat.gcd_dvd_right _ _) ih
|
||
|
||
theorem dvd_gcd (xs : IntList) (c : Nat) (w : ∀ {a : Int}, a ∈ xs → (c : Int) ∣ a) :
|
||
c ∣ xs.gcd := by
|
||
simp only [Int.ofNat_dvd_left] at w
|
||
induction xs with
|
||
| nil => have := Nat.dvd_zero c; simp at this; exact this
|
||
| cons x xs ih =>
|
||
simp
|
||
apply Nat.dvd_gcd
|
||
· apply w
|
||
simp
|
||
· apply ih
|
||
intro b m
|
||
apply w
|
||
exact List.mem_cons_of_mem x m
|
||
|
||
theorem gcd_eq_iff (xs : IntList) (g : Nat) :
|
||
xs.gcd = g ↔
|
||
(∀ {a : Int}, a ∈ xs → (g : Int) ∣ a) ∧
|
||
(∀ (c : Nat), (∀ {a : Int}, a ∈ xs → (c : Int) ∣ a) → c ∣ g) := by
|
||
constructor
|
||
· rintro rfl
|
||
exact ⟨gcd_dvd _, dvd_gcd _⟩
|
||
· rintro ⟨hi, hg⟩
|
||
apply Nat.dvd_antisymm
|
||
· apply hg
|
||
intro i m
|
||
exact gcd_dvd xs m
|
||
· exact dvd_gcd xs g hi
|
||
|
||
attribute [simp] Int.zero_dvd
|
||
|
||
@[simp] theorem gcd_eq_zero (xs : IntList) : xs.gcd = 0 ↔ ∀ x, x ∈ xs → x = 0 := by
|
||
simp [gcd_eq_iff, Nat.dvd_zero]
|
||
|
||
@[simp] theorem dot_mod_gcd_left (xs ys : IntList) : dot xs ys % xs.gcd = 0 := by
|
||
induction xs generalizing ys with
|
||
| nil => simp
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys =>
|
||
rw [dot_cons₂, Int.add_emod,
|
||
← Int.emod_emod_of_dvd (x * y) (gcd_cons_div_left),
|
||
← Int.emod_emod_of_dvd (dot xs ys) (Int.ofNat_dvd.mpr gcd_cons_div_right)]
|
||
simp_all
|
||
|
||
theorem gcd_dvd_dot_left (xs ys : IntList) : (xs.gcd : Int) ∣ dot xs ys :=
|
||
Int.dvd_of_emod_eq_zero (dot_mod_gcd_left xs ys)
|
||
|
||
@[simp]
|
||
theorem dot_eq_zero_of_left_eq_zero {xs ys : IntList} (h : ∀ x, x ∈ xs → x = 0) : dot xs ys = 0 := by
|
||
induction xs generalizing ys with
|
||
| nil => rfl
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => rfl
|
||
| cons y ys =>
|
||
rw [dot_cons₂, h x (List.mem_cons_self _ _), ih (fun x m => h x (List.mem_cons_of_mem _ m)),
|
||
Int.zero_mul, Int.add_zero]
|
||
|
||
theorem dot_sdiv_left (xs ys : IntList) {d : Int} (h : d ∣ xs.gcd) :
|
||
dot (xs.sdiv d) ys = (dot xs ys) / d := by
|
||
induction xs generalizing ys with
|
||
| nil => simp
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys =>
|
||
have wx : d ∣ x := Int.dvd_trans h (gcd_cons_div_left)
|
||
have wxy : d ∣ x * y := Int.dvd_trans wx (Int.dvd_mul_right x y)
|
||
have w : d ∣ (IntList.gcd xs : Int) := Int.dvd_trans h (gcd_cons_div_right')
|
||
simp_all [Int.add_ediv_of_dvd_left, Int.mul_ediv_assoc']
|
||
|
||
/-- Apply "balanced mod" to each entry in an `IntList`. -/
|
||
abbrev bmod (x : IntList) (m : Nat) : IntList := x.map (Int.bmod · m)
|
||
|
||
theorem bmod_length (x : IntList) (m) : (bmod x m).length ≤ x.length :=
|
||
Nat.le_of_eq (List.length_map _ _)
|
||
|
||
/--
|
||
The difference between the balanced mod of a dot product,
|
||
and the dot product with balanced mod applied to each entry of the left factor.
|
||
-/
|
||
abbrev bmod_dot_sub_dot_bmod (m : Nat) (a b : IntList) : Int :=
|
||
(Int.bmod (dot a b) m) - dot (bmod a m) b
|
||
|
||
theorem dvd_bmod_dot_sub_dot_bmod (m : Nat) (xs ys : IntList) :
|
||
(m : Int) ∣ bmod_dot_sub_dot_bmod m xs ys := by
|
||
dsimp [bmod_dot_sub_dot_bmod]
|
||
rw [Int.dvd_iff_emod_eq_zero]
|
||
induction xs generalizing ys with
|
||
| nil => simp
|
||
| cons x xs ih =>
|
||
cases ys with
|
||
| nil => simp
|
||
| cons y ys =>
|
||
simp only [IntList.dot_cons₂, List.map_cons]
|
||
specialize ih ys
|
||
rw [Int.sub_emod, Int.bmod_emod] at ih
|
||
rw [Int.sub_emod, Int.bmod_emod, Int.add_emod, Int.add_emod (Int.bmod x m * y),
|
||
← Int.sub_emod, ← Int.sub_sub, Int.sub_eq_add_neg, Int.sub_eq_add_neg,
|
||
Int.add_assoc (x * y % m), Int.add_comm (IntList.dot _ _ % m), ← Int.add_assoc,
|
||
Int.add_assoc, ← Int.sub_eq_add_neg, ← Int.sub_eq_add_neg, Int.add_emod, ih, Int.add_zero,
|
||
Int.emod_emod, Int.mul_emod, Int.mul_emod (Int.bmod x m), Int.bmod_emod, Int.sub_self,
|
||
Int.zero_emod]
|
||
|
||
end IntList
|
||
|
||
end Lean.Omega
|