-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