-rw-r--r-- 16038 leangoppa-20230726/Goppadecoding/approximant.lean raw
import Mathlib.RingTheory.Polynomial.Basic
import Mathlib.LinearAlgebra.FiniteDimensional
import Goppadecoding.nat
import Goppadecoding.dvd
import Goppadecoding.sum
import Goppadecoding.lineardependence
import Goppadecoding.zcoeff
open Polynomial BigOperators Finset
namespace Goppadecoding
section approximants
variable {k: Type _} [Field k]
def approximant (A B: k[X]) (t: ℕ) (a b: k[X]): Prop :=
IsCoprime a b ∧ a.degree ≤ t ∧ t + (a*B-b*A).degree < A.degree
def small_approximant (A B: k[X]) (t: ℕ) (a b: k[X]): Prop :=
approximant A B t a b ∧ b.degree < t
-- this proof follows the "explicitly" line
-- alternative is to avoid coeffs for k-module {(a,b): deg a ≤ t, deg b < t}:
-- "simply" set up k-linear map to the interesting coeffs of (a*B-b*A)
theorem approximant_exists_lemma {A B: k[X]} (degAB: A.degree > B.degree) (t: ℕ)
: ∃ (a b:k[X]), (a ≠ 0 ∨ b ≠ 0) ∧ a.degree ≤ t ∧ b.degree < t ∧ t + (a*B-b*A).degree < A.degree :=
by
have degAnat: degree A ≠ ⊥ := LT.lt.ne_bot degAB
let n := (degree A).unbot degAnat
have degAn: degree A = n := Eq.symm (WithBot.coe_unbot (degree A) degAnat)
let V := Fin (2*t) → k
let M: (Fin (2*t+1) → V) := fun j ↦ fun i ↦ if (j:ℕ) < t+1 then (zcoeff B (n-t+(i:ℤ)-(j:ℤ))) else (-zcoeff A (n+1+(i:ℤ)-(j:ℤ)))
have explicitdependency: ∃ c : Fin (2*t+1) → k, ∑ j, c j • M j = 0 ∧ ∃ j, c j ≠ 0 := by
have rankVbelowMnum: FiniteDimensional.finrank k V < Fintype.card (Fin (2*t+1)) := by
calc
FiniteDimensional.finrank k V = 2*t := FiniteDimensional.finrank_fin_fun k
_ < 2*t+1 := by simp only [lt_add_iff_pos_right]
_ = Fintype.card (Fin (2*t+1)) := by rw[eq_comm,Fintype.card_fin (2*t+1)]
have dependency: ¬LinearIndependent k M := by
apply dependency_if_finrank_space_lt_card_family
apply rankVbelowMnum
exact Fintype.not_linearIndependent_iff.mp dependency
cases explicitdependency with | intro c cdep =>
have cdepleft: (∑ j: Fin (2*t+1), c j • M j = 0) := by simp only [cdep.left]
have cdepleft2: (∀ i: Fin (2*t), ((∑ j: Fin (2*t+1), c j * M j i) = 0)) := by
intro i
calc
∑ j: Fin (2*t+1), c j * M j i
= ∑ j: Fin (2*t+1), (c j • M j) i := rfl
_ = (∑ j: Fin (2*t+1), (c j • M j)) i := by simp only [Pi.smul_apply, smul_eq_mul, mul_ite, mul_neg, Finset.sum_apply]
_ = 0 := by rw[cdepleft]; simp only [Pi.zero_apply]
let a := ∑ j in range (t+1), (monomial: ℕ → k →ₗ[k] k[X]) j (c j)
let b := ∑ j in range t, (monomial: ℕ → k →ₗ[k] k[X]) j (c (j+t+1))
have nonzero: a ≠ 0 ∨ b ≠ 0 := by
cases cdep.right with | intro x cxnonzero =>
let xn: ℕ := x
by_cases xnt: xn < t+1
. left
have axn: coeff a xn ≠ 0 := by
calc
coeff a xn
= coeff (∑ j in range (t+1), (monomial: ℕ → k →ₗ[k] k[X]) j (c j)) xn := by rfl
_ = c xn := by rw[coeff_sum_range_monomial]; simp only [xnt, ite_true]
_ = c x := by simp only; rw[Fin.cast_val_eq_self]
_ ≠ 0 := by apply cxnonzero
apply nonzero_if_coeff_nonzero axn
right
let i: ℕ := xn-(t+1)
have xn_ge_t1: xn ≥ t+1 := Nat.not_lt.mp xnt
have ixn: xn = i+(t+1) := (Nat.sub_eq_iff_eq_add xn_ge_t1).mp rfl
have xn_lt_2t1: xn < 2*t+1 := Fin.prop x
have i_lt_t: i < t := by linarith
have bxn: coeff b i ≠ 0 := by
calc
coeff b i
= coeff (∑ j in range t, (monomial: ℕ → k →ₗ[k] k[X]) j (c (j+t+1))) i := by rfl
_ = c (i+t+1) := by rw[coeff_sum_range_monomial]; simp only [ge_iff_le, i_lt_t, ite_true]
_ = c (i+(t+1)) := by group
_ = c xn := by rw[ixn]; simp only [ge_iff_le, Nat.cast_add, Nat.cast_one]
_ = c x := by simp only; rw[Fin.cast_val_eq_self]
_ ≠ 0 := by apply cxnonzero
apply nonzero_if_coeff_nonzero bxn
use a
use b
constructor
. apply nonzero
have degale: degree a ≤ t := by
have degalt: degree a < t+1 := by apply degree_sum_range_lt
apply Order.lt_succ_iff.mp
apply degalt
constructor
. apply degale
have degblt: degree b < t := by apply degree_sum_range_lt
constructor
. apply degblt
have aBbAcoeff: ∀ d, coeff (a*B-b*A) d = (∑ j in range (t+1), (c j * zcoeff B (d-j))) + (∑ j in range t, (c (j+t+1) * -zcoeff A (d-j))) := by
intro d
calc
coeff (a*B-b*A) d
= coeff (a*B) d - coeff (b*A) d := by rw[coeff_sub]
_ = (∑ j in range (t+1), (c j * zcoeff B (d-j))) - (∑ j in range t, (c (j+t+1) * zcoeff A (d-j))) := by repeat rw[sum_monomials_mul_coeff]
_ = (∑ j in range (t+1), (c j * zcoeff B (d-j))) + -(∑ j in range t, (c (j+t+1) * zcoeff A (d-j))) := by apply sub_eq_add_neg
_ = (∑ j in range (t+1), (c j * zcoeff B (d-j))) + (∑ j in range t, c (j+t+1) * -zcoeff A (d-j)) := by simp only [mul_neg, sum_neg_distrib]
have abbacoeff0: ∀ d:ℕ, (n-t:ℤ) ≤ (d:ℤ) → coeff (a*B-b*A) d = 0 := by
intro d dlower
by_cases dupper: t+n ≤ d
. have aBbAdeg: degree (a*B-b*A) < t+n := by
have tdegB: t + degree B < t + n := by
by_cases bot: degree B = ⊥
. simp only [bot, WithBot.add_bot]
apply WithBot.bot_lt_coe
let m := (degree B).unbot bot
have degBm: degree B = m := Eq.symm (WithBot.coe_unbot (degree B) bot)
rw[degBm]
apply WithBot.add_lt_add_left
exact WithBot.nat_ne_bot t
rw[← degBm,← degAn]
exact degAB
have aBdeg: degree (a*B) < t+n := by calc
degree (a*B) = degree a + degree B := degree_mul
_ ≤ t + degree B := add_le_add_right degale (degree B)
_ < t + n := tdegB
have bAdeg: degree (b*A) < t+n := by calc
degree (b*A) = degree b + degree A := degree_mul
_ < t + degree A := WithBot.add_lt_add_right degAnat degblt
_ ≤ t+n := Eq.le (congrArg (HAdd.hAdd ↑t) degAn)
calc
degree (a*B-b*A) ≤ max (degree (a*B)) (degree (b*A)) := degree_sub_le (a*B) (b*A)
_ < t+n := max_lt aBdeg bAdeg
have degd: degree (a*B-b*A) < d := by calc
degree (a*B-b*A) < t+n := aBbAdeg
_ ≤ d := by norm_cast
exact coeff_eq_zero_of_degree_lt degd
let i:ℤ := (d:ℤ)-(n-t:ℤ)
have di: d = n-t+i := by group
let inat:ℕ := i.toNat
have inatupper: inat < 2*t := by
have iupper: i < 2*t := by
have dntupper: (d:ℤ)-(n-t:ℤ) ≤ 2*t-1 := by linarith
exact Int.le_sub_one_iff.mp dntupper
have dntlower: 0 ≤ (d:ℤ)-(n-t:ℤ) := by linarith
exact (Int.toNat_lt dntlower).mpr iupper
let ifin: Fin (2*t) := ⟨inat,inatupper⟩
have iifin: i = (ifin:ℤ) := by
have iinat: inat = i := Int.toNat_sub_of_le dlower
exact id (Eq.symm iinat)
calc
coeff (a*B-b*A) d
= (∑ j in range (t+1), (c j * zcoeff B (d-j))) + (∑ j in range t, c (j+t+1) * -zcoeff A (d-j))
:= aBbAcoeff d
_ = (∑ j in range (t+1), (c j * zcoeff B (n-t+i-j))) + (∑ j in range t, c (j+t+1) * -zcoeff A (n-t+i-j))
:= by rw[di]
_ = (∑ j: Fin (2*t+1), if (j:ℕ) < t+1 then (c (j:ℕ) * zcoeff B (n-t+i-(j:ℕ))) else 0) + (∑ j in range t, c (j+t+1) * -zcoeff A (n-t+i-j))
:= by
have t12t1: t+1 ≤ 2*t+1 := by linarith
rw[sum_range_eq_sum_fin_ite t12t1]
_ = (∑ j: Fin (2*t+1), if (j:ℕ) < t+1 then (c (j:ℕ) * zcoeff B (n-t+i-(j:ℕ))) else 0) + (∑ j: Fin (2*t+1), if (j:ℕ) < t then c ((j:ℕ)+t+1) * -zcoeff A (n-t+i-(j:ℕ)) else 0)
:= by
have t2t1: t ≤ 2*t+1 := by linarith
rw[sum_range_eq_sum_fin_ite t2t1]
_ = (∑ j: Fin (2*t+1), if (j:ℕ) < t+1 then (c (j:ℕ) * zcoeff B (n-t+i-(j:ℕ))) else 0) + (∑ j: Fin (2*t+1), if (j:ℕ) < t+1 then 0 else c (((j:ℕ)-(t+1):ℕ)+t+1) * -zcoeff A (n-t+i-((j:ℕ)-(t+1):ℕ)))
:= by
have tt1: 2*t+1 = t+(t+1) := by group
rw[sum_fin_ite_eq_sum_fin_ite_sub tt1 (fun j ↦ c ((j:ℕ)+t+1) * -zcoeff A (n-t+i-(j:ℕ)))]
_ = ∑ j: Fin (2*t+1), if (j:ℕ) < t+1 then (c (j:ℕ) * zcoeff B (n-t+i-(j:ℕ))) else c (((j:ℕ)-(t+1):ℕ)+t+1) * -zcoeff A (n-t+i-((j:ℕ)-(t+1):ℕ))
:= by apply sum_ite_add_sum_ite
_ = ∑ j: Fin (2*t+1), if (j:ℕ) < t+1 then (c (j:ℕ) * zcoeff B (n-t+i-(j:ℕ))) else (c j * -zcoeff A (n-t+i-((j:ℕ)-(t+1):ℕ)))
:= by
apply Finset.sum_congr
simp only
intro x _
simp only [add_sub_cancel'_right, ge_iff_le, mul_neg]
by_cases xt1: (x:ℕ) < t+1
. simp only [xt1, ge_iff_le, ite_true]
. simp only [xt1, ge_iff_le, ite_false, neg_inj, mul_eq_mul_right_iff]
left; apply congr_arg
rw[fin_nat_sub_eq_sub (Nat.not_lt.mp xt1)]
rw[Nat.cast_add]
group
_ = ∑ j: Fin (2*t+1), if (j:ℕ) < t+1 then (c j * zcoeff B (n-t+i-(j:ℕ))) else (c j * -zcoeff A (n-t+i-((j:ℕ)-(t+1):ℕ)))
:= by
apply Finset.sum_congr
simp only
intro x _
simp only [add_sub_cancel'_right, ge_iff_le, mul_neg]
by_cases xt1: (x:ℕ) < t+1
. simp only [xt1, ge_iff_le, ite_true, mul_eq_mul_right_iff]
left; apply congr_arg; exact Fin.cast_val_eq_self x
. simp only [xt1, ge_iff_le, ite_false]
_ = ∑ j: Fin (2*t+1), if (j:ℕ) < t+1 then (c j * zcoeff B (n-t+i-j)) else (c j * -zcoeff A (n-t+i-((j:ℕ)-(t+1):ℕ)))
:= by simp only [add_sub_cancel'_right, ge_iff_le, mul_neg]
_ = ∑ j: Fin (2*t+1), if (j:ℕ) < t+1 then (c j * zcoeff B (n-t+i-j)) else (c j * -zcoeff A (n+1+i-j))
:= by
apply Finset.sum_congr
simp only
intro x _
simp only [add_sub_cancel'_right, ge_iff_le, mul_neg]
by_cases xt1: (x:ℕ) < t+1
. simp only [xt1, ge_iff_le, ite_true]
. simp only [xt1, ge_iff_le, ite_false, neg_inj, mul_eq_mul_left_iff]
left; apply congr_arg
have xsum: ((x:ℕ):ℤ) = ((((x:ℕ)-(t+1):ℕ) + (t+1):ℕ):ℤ) := by
rw[Nat.sub_add_cancel (Nat.not_lt.mp xt1)]
rw[xsum]
group
_ = ∑ j: Fin (2*t+1), c j * (if (j:ℕ) < t+1 then zcoeff B (n-t+i-j) else -zcoeff A (n+1+i-j))
:= by simp only [add_sub_cancel'_right, mul_neg, mul_ite]
_ = ∑ j: Fin (2*t+1), c j * (if (j:ℕ) < t+1 then zcoeff B (n-t+ifin-j) else -zcoeff A (n+1+ifin-j))
:= by rw[iifin]
_ = ∑ j: Fin (2*t+1), c j * M j ifin
:= rfl
_ = 0 := cdepleft2 ifin
rw[degAn]
exact degree_add_lt_if_coeff_zero (a*B-b*A) t n abbacoeff0
theorem small_approximant_exists {A B: k[X]} (degAB: A.degree > B.degree) {t: ℕ}
: ∃ a b, small_approximant A B t a b :=
by classical
unfold small_approximant
unfold approximant
have partway: ∃ a b, (a ≠ 0 ∨ b ≠ 0) ∧ degree a ≤ t ∧ degree b < t ∧ t + degree (a*B-b*A) < degree A := approximant_exists_lemma degAB t
cases partway with | intro a acond =>
cases acond with | intro b abcond =>
let g := EuclideanDomain.gcd a b
have existsag: ∃ ag, a = g*ag := EuclideanDomain.gcd_dvd_left a b
have existsbg: ∃ bg, b = g*bg := EuclideanDomain.gcd_dvd_right a b
cases existsag with | intro ag agformula =>
cases existsbg with | intro bg bgformula =>
use ag
use bg
have g0: g ≠ 0 := by
apply gcd_ne_zero_iff.mpr
simp only [ne_eq, abcond]
have degg: 0 ≤ degree g := zero_le_degree_iff.mpr g0
constructor
. constructor
. let x := EuclideanDomain.gcdA a b
let y := EuclideanDomain.gcdB a b
have axby: g = a*x+b*y := EuclideanDomain.gcd_eq_gcd_ab a b
have gagxbgy: g*(ag*x+bg*y) = g := by rw[mul_add,← mul_assoc,← mul_assoc,← agformula,← bgformula,axby]
have agxbgy: ag*x+bg*y = 1 := eq_one_if_mul_eq_self g0 gagxbgy
unfold IsCoprime
use x
use y
rw[← agxbgy]; ring
constructor
. have degagag: degree a = degree g + degree ag := by rw[agformula,degree_mul]
have degag: degree ag ≤ degree a := by rw[degagag]; apply le_add_of_nonneg_left degg
apply le_trans degag abcond.2.1
calc
t + degree (ag*B-bg*A)
≤ degree g + (t + degree (ag*B-bg*A)) := le_add_self_if_le degg
_ = t + degree (a*B-b*A) := by
have h: a*B-b*A = g*(ag*B-bg*A) := by rw[mul_sub,← mul_assoc,← mul_assoc,agformula,bgformula]
have h2: degree (a*B-b*A) = degree g + degree (ag*B-bg*A) := by rw[h,degree_mul]
rw[h2]
group
_ < degree A := by simp only [abcond]
have degbgbg: degree b = degree g + degree bg := by rw[bgformula,degree_mul]
have degbg: degree bg ≤ degree b := by rw[degbgbg]; apply le_add_of_nonneg_left degg
apply lt_of_le_of_lt degbg abcond.2.2.1
theorem approximant_best {A B a b c d: k[X]} {t: ℕ}
(appr: approximant A B t a b)
(cdeg: c.degree ≤ t)
(cBdAdeg: t + (c*B-d*A).degree < A.degree)
: ∃ (L: k[X]), (c = a*L ∧ d = b*L) :=
by
unfold approximant at appr
let coprime := appr.1
let dega := appr.2.1
let degaBbA := appr.2.2
have D: a*d = b*c := by
have D1: degree (a*d-c*b) + degree A < degree A := by
have degc2: degree (c*(a*B-b*A)) < degree A := by
calc
_ = degree c + degree (a*B-b*A) := degree_mul
_ ≤ t + degree (a*B-b*A) := add_le_add_right cdeg (degree (a*B-b*A))
_ < degree A := degaBbA
have dega2: degree (a*(c*B-d*A)) < degree A := by
calc
_ = degree a + degree (c*B-d*A) := degree_mul
_ ≤ t + degree (c*B-d*A) := add_le_add_right dega (degree (c*B-d*A))
_ < degree A := cBdAdeg
calc
_ = degree ((a*d-c*b)*A) := by rw[← degree_mul]
_ = degree (c*(a*B-b*A)-a*(c*B-d*A)) := by ring_nf
_ ≤ max (degree (c*(a*B-b*A))) (degree (a*(c*B-d*A))) := degree_sub_le (c*(a*B-b*A)) (a*(c*B-d*A))
_ < degree A := max_lt degc2 dega2
have D2: degree (a*d-c*b) < 0 := neg_of_add_lt_left D1
have D3: degree (a*d-c*b) = ⊥ := (Nat.WithBot.lt_zero_iff (degree (a*d-c*b))).mp D2
have D4: a*d-c*b = 0 := degree_eq_bot.mp D3
calc
_ = a*d - 0 := by group
_ = a*d - (a*d-c*b) := by rw[← D4]
_ = b*c := by ring
have cdiv: a ∣ b*c := by
apply Dvd.intro d
rw[D]
have cdiv2: a ∣ c := by apply dvd_if_dvd_mul_left coprime cdiv
have ddiv: b ∣ a*d := by
apply Dvd.intro c
rw[← D]
have ddiv2: b ∣ d := by apply dvd_if_dvd_mul_left (IsCoprime.symm coprime) ddiv
cases cdiv2 with | intro L caL =>
by_cases a0: a = 0
. cases ddiv2 with | intro M dbM =>
use M
constructor
. calc
c = a*L := caL
_ = 0*L := by rw[a0]
_ = 0*M := by ring
_ = a*M := by rw[← a0]
. apply dbM
. use L
constructor
. apply caL
. have h: a*d = a*(b*L) := by calc
a*d = b*c := D
_ = b*(a*L) := by rw[caL]
_ = a*(b*L) := by ring
apply mul_left_cancel₀ a0 h
end approximants