import Mathlib.RingTheory.Polynomial.Basic import Goppadecoding.nat import Goppadecoding.zero import Goppadecoding.sum open Polynomial BigOperators Finset namespace Goppadecoding section lemmas_poly theorem degree_add_le_if_degree_ge {k: Type _} [Field k] {f g: k[X]} (gd: g.degree ≤ f.degree): (f+g).degree ≤ f.degree := by calc _ ≤ max f.degree g.degree := degree_add_le f g _ ≤ f.degree := max_le (le_refl f.degree) gd theorem degree_add_eq_if_degree_lt {k: Type _} [Field k] {f g: k[X]} (gd: g.degree < f.degree): (f+g).degree = f.degree := by apply le_antisymm . have gled: g.degree ≤ f.degree := le_of_lt gd exact degree_add_le_if_degree_ge gled apply not_lt.mp intro fd have ff: degree f < degree f := by calc _ = degree (f+g-g) := by group _ ≤ max (f+g).degree g.degree := degree_sub_le (f+g) g _ < f.degree := max_lt fd gd rw[lt_self_iff_false] at ff exact ff theorem nonzero_if_coeff_nonzero {R: Type _} [Semiring R] {f: R[X]} {n: ℕ}: coeff f n ≠ 0 → f ≠ 0 := by contrapose simp only [ne_eq, not_not] intro fzero rw[fzero] apply coeff_zero theorem coeff_bigsum {U: Type _} [DecidableEq U] {R: Type _} [Semiring R] {f: U → R[X]} {d: ℕ}: ∀ S: Finset U, coeff (∑ s in S, f s) d = ∑ s in S, coeff (f s) d := by apply Finset.induction . simp only [Finset.sum_empty, coeff_zero] . intro a S notin prevcase repeat rw[Finset.sum_insert notin] rw[coeff_add,prevcase] theorem zero_coeff_of_zero_poly_as_sum {k: Type _} [Field k] {f: ℕ → k} {d: ℕ}: ∑ j in range d, C (f j) * X^(d-1-j) = 0 → ∀ i, i < d → f i = 0 := by intro poly0 i id apply Eq.symm calc 0 = coeff 0 (d-1-i) := by rw[coeff_zero] _ = coeff (∑ j in range d, C (f j) * X^(d-1-j)) (d-1-i) := by rw[poly0] _ = ∑ j in range d, coeff (C (f j) * X^(d-1-j)) (d-1-i) := by rw[coeff_bigsum] _ = ∑ j in range d, f j * coeff ((X:k[X])^(d-1-j)) (d-1-i) := by apply sum_congr; simp only; intro j _ rw[coeff_C_mul] _ = ∑ j in range d, f j * (if d-1-i = d-1-j then 1 else 0) := by apply sum_congr; simp only; intro j _ rw[coeff_X_pow] _ = ∑ j in range d, (if i = j then f j else 0) := by apply sum_congr; simp only; intro j jd rw[mem_range] at jd rw[mul_ite,mul_one,mul_zero] apply ite_congr rw[eq_iff_iff] constructor . apply eq_if_bounded_bounded_eqflip id jd . intro ij; rw[ij] repeat simp _ = if i ∈ range d then f i else 0 := sum_ite_eq (range d) i f _ = if i < d then f i else 0 := by simp only [mem_range] _ = f i := by simp[id] theorem sum_degree_lt {U: Type _} [DecidableEq U] {R: Type _} [Semiring R] {f: U → R[X]} {d: ℕ}: ∀ S: Finset U, (∀ s, s ∈ S → degree (f s) < d) → degree (∑ s in S, f s) < d := by apply Finset.induction . simp only [Finset.not_mem_empty, IsEmpty.forall_iff, implies_true, Finset.sum_empty, degree_zero, bot_lt_iff_ne_bot, ne_eq, WithBot.nat_ne_bot, not_false_eq_true, forall_true_left] . intro a S notin prevcase rw[Finset.sum_insert] intro hyp calc degree (f a + ∑ x in S, f x) ≤ max (degree (f a)) (degree (∑ x in S, f x)) := by apply degree_add_le _ < d := by apply max_lt apply hyp a (Finset.mem_insert_self a S) apply prevcase intro s sinS apply hyp exact Finset.mem_insert_of_mem sinS simp only [notin, not_false_eq_true] theorem degree_C_mul_le_degree {k: Type _} [Field k] (f: k[X]) (c: k): degree (C c * f) ≤ degree f := by by_cases csign: c = 0 . rw[csign,C_0] ring_nf simp only [degree_zero, bot_le, implies_true] . rw[degree_mul,degree_C csign] simp only [zero_add, le_refl] theorem degree_C_mul_lt_if_degree_lt {k: Type _} [Field k] (f: k[X]) (d: ℕ) (c: k) (fd: degree f < d): degree (C c * f) < d := by calc _ ≤ degree f := degree_C_mul_le_degree f c _ < d := fd theorem degree_mul_C_lt_if_degree_lt {k: Type _} [Field k] (f: k[X]) (d: ℕ) (c: k): degree f < d → degree (f * C c) < d := by rw[mul_comm] apply degree_C_mul_lt_if_degree_lt -- imitating degree_sum_fin_lt in library theorem degree_sum_range_lt {k: Type _} [Field k] {n: ℕ} (f: ℕ → k): degree (∑ i in range n, (monomial i: k →ₗ[k] k[X]) (f i): k[X]) < n := by apply sum_degree_lt intro s srange by_cases fs: f s = 0 . simp only [fs, map_zero, degree_zero] apply bot_lt_iff_ne_bot.mpr exact WithBot.nat_ne_bot n rw[degree_monomial] simp only [Nat.cast_lt] exact List.mem_range.mp srange exact fs theorem coeff_sum_range_monomial {k: Type _} [Field k] {c: ℕ → k} (n d: ℕ): coeff (∑ j in range n, (monomial j: k →ₗ[k] k[X]) (c j)) d = if d < n then c d else 0 := by calc coeff (∑ j in range n, (monomial j: k →ₗ[k] k[X]) (c j)) d = ∑ j in range n, coeff ((monomial j: k →ₗ[k] k[X]) (c j)) d := finset_sum_coeff (range n) (fun j => (monomial j: k →ₗ[k] k[X]) (c j)) d _ = ∑ j in range n, if j = d then c d else 0 := by simp only [coeff_monomial, Finset.sum_ite_eq', Finset.mem_range] _ = if d ∈ range n then c d else 0 := by apply Finset.sum_ite_eq' _ = if d < n then c d else 0 := by simp only [Finset.mem_range] theorem degree_lt_if_coeff_zero {k: Type _} [Field k] (f: k[X]) (n: ℕ): (∀ d:ℕ, n ≤ d → coeff f d = 0) → degree f < n := by intro hyp exact (degree_lt_iff_coeff_zero f n).mpr hyp theorem degree_add_lt_if_coeff_zero {k: Type _} [Field k] (f: k[X]) (m: ℕ) (n: ℕ): (∀ d:ℕ, (n-m:ℤ) ≤ (d:ℤ) → coeff f d = 0) → m + degree f < n := by intro hyp by_cases mn: m ≤ n . let nm:ℕ := n-m have neq: n = m+nm := Eq.symm (Nat.add_sub_of_le mn) have h: ∀ d:ℕ, nm ≤ d → coeff f d = 0 := by intro d nmd apply hyp d rw[neq,Nat.cast_add] group exact Int.ofNat_le.mpr nmd have h2: degree f < nm := degree_lt_if_coeff_zero f nm h have h3: m + degree f < m + nm := by apply WithBot.add_lt_add_left simp only [ne_eq, WithBot.nat_ne_bot, not_false_eq_true] apply h2 rw[neq] norm_cast . have h: ∀ d:ℕ, 0 ≤ d → coeff f d = 0 := by intro d nmd apply hyp d linarith have h2: degree f < 0 := degree_lt_if_coeff_zero f 0 h have h3: degree f = ⊥ := (Nat.WithBot.lt_zero_iff (degree f)).mp h2 have h4: m + degree f = ⊥ := by rw[h3]; rfl rw[h4] apply WithBot.bot_lt_coe theorem add_lt_add_right_nonzero_degree {k: Type _} [Field k] {x y: WithBot ℕ} {a: k[X]}: a ≠ 0 → x < y → x + degree a < y + degree a := by intro anonzero have deganat: degree a ≠ ⊥ := by simp only [ne_eq, degree_eq_bot, anonzero, not_false_eq_true] intro xy exact WithBot.add_lt_add_right deganat xy theorem nonzero_if_divides_nonzero {k: Type _} [Field k] {a b: k[X]}: a ∣ b → b ≠ 0 → a ≠ 0 := by intro adivb contrapose repeat rw[not_ne_iff] intro azero cases adivb with | intro q baq => rw[baq,azero] exact zero_mul q theorem no_shared_root_if_coprime {k: Type _} [Field k] {a b: k[X]} {r: k}: IsCoprime a b → a.eval r = 0 → b.eval r ≠ 0 := by unfold IsCoprime intro coprime ar cases coprime with | intro x xcond => cases xcond with | intro y ycond => have yb1: y.eval r * b.eval r = 1 := by calc _ = (y*b).eval r := by rw[eval_mul] _ = 0 + (y*b).eval r := by group _ = (x.eval r)*0 + (y*b).eval r := by ring _ = (x.eval r)*(a.eval r) + (y*b).eval r := by rw[ar] _ = (x*a).eval r + (y*b).eval r := by rw[← eval_mul] _ = (x*a+y*b).eval r := by rw[eval_add] _ = (1:k[X]).eval r := by rw[ycond] _ = (C 1:k[X]).eval r := by rfl _ = 1 := by rw[eval_C] exact nonzero_if_mul_left_one yb1 theorem eq_C_lead_if_left_inverse {k: Type _} [Field k] {f g: k[X]}: f*g = 1 → g = C g.leadingCoeff := by rw[mul_comm] intro fg1 have gunit: IsUnit g := isUnit_of_mul_eq_one g f fg1 have degg: degree g = 0 := degree_eq_zero_of_isUnit gunit have natdegg: natDegree g = 0 := natDegree_eq_zero_of_isUnit gunit unfold leadingCoeff rw[natdegg] exact eq_C_of_degree_eq_zero degg theorem eq_C_lead_if_right_inverse {k: Type _} [Field k] {f g: k[X]}: f*g = 1 → f = C f.leadingCoeff := by rw[mul_comm] exact eq_C_lead_if_left_inverse theorem eq_lead_mul_if_dvd_dvd {k: Type _} [Field k] {f g: k[X]}: f ∣ g → g ∣ f → g.leadingCoeff = 1 → f = C f.leadingCoeff * g := by intro fg gf gmonic cases fg with | intro q gfq => cases gf with | intro r fgr => have fqr: f*(q*r) = f := by calc _ = (f*q)*r := by rw[mul_assoc] _ = g*r := by rw[gfq] _ = f := by rw[fgr] by_cases f0: f = 0 . simp[f0] have qr: q*r = 1 := eq_one_if_mul_eq_self f0 fqr have rC: r = C r.leadingCoeff := eq_C_lead_if_left_inverse qr have leadqf: r.leadingCoeff = f.leadingCoeff := by calc _ = 1 * r.leadingCoeff := by ring _ = g.leadingCoeff * r.leadingCoeff := by rw[gmonic] _ = (g*r).leadingCoeff := by rw[leadingCoeff_mul g r] _ = f.leadingCoeff := by rw[fgr] calc f = g*r := by rw[fgr] _ = r*g := by rw[mul_comm] _ = (C r.leadingCoeff)*g := by rw[← rC] _ = (C f.leadingCoeff)*g := by rw[leadqf] theorem coprime_X_minus_nonroot {k: Type _} [Field k] {f: k[X]} {r: k}: f.eval r ≠ 0 → IsCoprime f (X - C r) := by intro nonroot have div: X - C r ∣ f - C (f.eval r) := X_sub_C_dvd_sub_C_eval cases div with | intro q qeq => let vinv := (f.eval r)⁻¹ have vvinv: vinv * (f.eval r) = 1 := inv_mul_cancel nonroot unfold IsCoprime use C vinv use - (C vinv * q) calc _ = (C vinv) * (f - C (f.eval r) + C (f.eval r) - q * (X - C r)) := by ring _ = (C vinv) * ((X - C r) * q + C (f.eval r) - q * (X - C r)) := by rw[qeq] _ = (C vinv) * C (f.eval r) := by ring _ = C (vinv * f.eval r) := by rw[C_mul] _ = C 1 := by rw[vvinv] _ = 1 := by rw[map_one] theorem mul_neg_geom_sum2_X_C {k: Type _} [Field k] (d: ℕ) (r: k): X^d - C (r^d) = (X - C r) * ∑ j in range d, C (r^j) * X^(d-1-j) := by simp only[map_pow] rw[Commute.mul_neg_geom_sum₂] simp unfold Commute unfold SemiconjBy rw[mul_comm] theorem as_sum_range_if_degree_lt {k: Type _} [Field k] {f: k[X]} {n: ℕ} (fdeg: f.degree < n): f = ∑ i in range n, (monomial: ℕ → k →ₗ[k] k[X]) i (coeff f i) := by by_cases f0: f = 0 . simp only [f0, coeff_zero, map_zero, sum_const_zero] have fnatdegn: f.natDegree < n := (natDegree_lt_iff_degree_lt f0).mpr fdeg exact as_sum_range' f n fnatdegn theorem coeff_mul_sum_geom {k: Type _} [Field k] {f: k[X]} {n: ℕ} (fdeg: f.degree < n) (r: k): coeff (f * (∑ j in range n, C (r^j) * X^(n-1-j))) (n-1) = f.eval r := by by_cases f0: f = 0 . rw[f0] simp only[zero_mul,coeff_zero,eval_zero] have degfnat: 0 ≤ f.degree := Iff.mpr zero_le_degree_iff f0 have npos1: 0 < (n:WithBot ℕ) := by calc _ ≤ f.degree := degfnat _ < n := fdeg have npos: 0 < n := WithBot.coe_pos.mp npos1 have n1: Nat.succ (n - 1) = n := Nat.succ_pred_eq_of_pos npos have n1n: n-1 < n := by calc _ < Nat.succ (n-1) := Nat.lt.base (n - 1) _ = n := n1 have fsum: f = ∑ i in range n, (monomial: ℕ → k →ₗ[k] k[X]) i (coeff f i) := as_sum_range_if_degree_lt fdeg rw[fsum] rw[eval_finset_sum] rw[coeff_mul] rw[Finset.Nat.sum_antidiagonal_eq_sum_range_succ (fun fst snd ↦ coeff (∑ i in range n, ((monomial: ℕ → k →ₗ[k] k[X]) i) (coeff f i)) fst * coeff (∑ j in range n, C (r^j) * X^(n-1-j)) snd) (n-1)] simp only [finset_sum_coeff, coeff_monomial, sum_ite_eq', mem_range, ite_mul, zero_mul, eval_monomial, coeff_C_mul_X_pow] rw[n1] apply sum_congr; simp only intro x xrange rw[mem_range] at xrange simp only[xrange,ite_true] rw[mul_eq_mul_left_iff]; left rw[sum_flip_simpler] calc _ = ∑ x_1 in range n, if n - 1 - x = x_1 then r ^ (n - 1 - x_1) else 0 := by apply sum_congr; rfl intro x1 x1range rw[mem_range] at x1range have x1n1: x1 ≤ n-1 := Nat.le_pred_of_lt x1range have subsub: n-1-(n-1-x1) = x1 := Nat.sub_sub_self x1n1 rw[subsub] _ = if n - 1 - x ∈ range n then r ^ (n - 1 - (n - 1 - x)) else 0 := sum_ite_eq (range n) (n - 1 - x) fun x => r ^ (n - 1 - x) _ = if n - 1 - x ∈ range n then r ^ x else 0 := by have xn1: x ≤ n-1 := Nat.le_pred_of_lt xrange have subsub: n-1-(n-1-x) = x := Nat.sub_sub_self xn1 rw[subsub] _ = r ^ x := by have xr: n-1-x ∈ range n := by rw[mem_range] calc n-1-x ≤ n-1 := Nat.sub_le (n - 1) x _ < n := n1n simp only[xr,ite_true] theorem coeff_mul_sum_sum_geom {k: Type _} [Field k] {U: Type _} [DecidableEq U] {S: Finset U} {α c: U → k} {f: k[X]} {n: ℕ} (fdeg: f.degree < n): coeff (f * ∑ s in S, (∑ j in range n, C (c s * (α s)^j) * X^(n-1-j))) (n-1) = ∑ s in S, c s * f.eval (α s) := by rw[mul_sum,coeff_bigsum] apply sum_congr; rfl intro s _ simp only[C_mul] calc _ = coeff (f * ∑ j in range n, C (c s) * C (α s ^ j) * X ^ (n - 1 - j)) (n - 1) := by simp only[C_mul] _ = coeff (f * ∑ j in range n, C (c s) * (C (α s ^ j) * X ^ (n - 1 - j))) (n - 1) := by simp only[mul_assoc] _ = coeff (f * (C (c s) * ∑ j in range n, C (α s ^ j) * X ^ (n - 1 - j))) (n - 1) := by simp only[mul_sum] _ = coeff (C (c s) * (f * ∑ j in range n, C (α s ^ j) * X ^ (n - 1 - j))) (n - 1) := by group _ = c s * coeff (f * ∑ j in range n, C (α s ^ j) * X ^ (n - 1 - j)) (n - 1) := by rw[coeff_C_mul] _ = _ := by rw[coeff_mul_sum_geom fdeg] -- can also do this via zcoeff theorem coeff_X_pow_add_one_eq_zero {k: Type _} [Field k] (f: k[X]) (d: ℕ) : coeff (X^(d+1)*f) d = 0 := by rw[coeff_mul] apply sum_eq_zero simp only [Nat.mem_antidiagonal, mul_eq_zero, Prod.forall] intro a b abd left rw[coeff_X_pow] simp only [ite_eq_right_iff, one_ne_zero] intro ad1eq have ad: a ≤ d := by rw[← abd]; exact Nat.le_add_right a b have ad1: a < d+1 := Nat.lt_succ.mpr ad rw[ad1eq,lt_self_iff_false] at ad1 exact ad1 theorem coeff_X_pow_eq_eval_zero {k: Type _} [Field k] (f: k[X]) (d: ℕ) : coeff (X^d*f) d = f.eval 0 := by calc _ = coeff ((1:k[X]) * (X:k[X])^d * f) d := by rw[one_mul] _ = coeff (C (1:k) * (X:k[X])^d * f) d := by rw[map_one] _ = coeff ((monomial: ℕ → k →ₗ[k] k[X]) d (1:k) * f) d := by rw[C_mul_X_pow_eq_monomial] _ = coeff ((monomial: ℕ → k →ₗ[k] k[X]) d (1:k) * f) (0+d) := by rw[zero_add] _ = (1:k) * coeff f 0 := by rw[coeff_monomial_mul] _ = coeff f 0 := by rw[one_mul] _ = f.eval 0 := by rw[coeff_zero_eq_eval_zero] end lemmas_poly