11/-
22Copyright (c) 2024-2025 ArkLib Contributors. All rights reserved.
33Released under Apache 2.0 license as described in the file LICENSE.
4- Authors: Julian Sutherland, Ilia Vlasov
4+ Authors: Julian Sutherland, Ilia Vlasov, Aristotle (Harmonic)
55-/
66import Mathlib.Algebra.Polynomial.BigOperators
77
@@ -33,7 +33,7 @@ open Polynomial
3333
3434namespace Polynomial
3535
36- variable {π½ : Type } [CommSemiring π½] [NoZeroDivisors π½]
36+ variable {π½ : Type } [CommSemiring π½]
3737
3838/--
3939Splits a polynomial into `n` component polynomials based on coefficient indices modulo `n`.
@@ -42,186 +42,116 @@ For a polynomial `f = ββ±Ό aβ±Ό XΚ²` and index `i : Fin n`, returns the polyno
4242coefficients are extracted from positions `j β‘ i (mod n)`, reindexed by `j / n`.
4343Formally: `splitNth f n i = β_{j β‘ i (mod n)} aβ±Ό X^(j/n)`.
4444-/
45- def splitNth (f : π½[X]) (n : β) [inst : NeZero n] : Fin n β π½[X] :=
46- fun i β¦
47- let sup :=
45+ def splitNth (f : π½[X]) (n : β) (i : Fin n) : π½[X] :=
46+ if hn : n = 0 then f else -- contradictory case
47+ Polynomial.ofFinsupp
48+ β¨
4849 Finset.filterMap (fun x β¦ if x % n = i.1 then .some (x / n) else .none)
4950 f.support
5051 (fun a a' b β¦ by
5152 have := Nat.div_add_mod' a n
5253 have := Nat.div_add_mod' a' n
53- aesop)
54- Polynomial.ofFinsupp
55- β¨
56- sup,
57- fun e β¦ f.coeff (e * n + i.1 ),
58- fun a β¦ by
59- simp only [Finset.mem_filterMap, mem_support_iff, ne_eq, Option.ite_none_right_eq_some,
60- Option.some.injEq, sup]
61- constructor
62- Β· rintro β¨a', gβ©
63- have : a' = a * n + i.1 := by
64- have := Nat.div_add_mod' a' n
65- aesop
54+ aesop),
55+ fun e β¦ f.coeff (e * n + i.1 ),
56+ fun a β¦ by
57+ simp only [Finset.mem_filterMap, mem_support_iff, ne_eq, Option.ite_none_right_eq_some,
58+ Option.some.injEq]
59+ constructor
60+ Β· rintro β¨a', gβ©
61+ have : a' = a * n + i.1 := by
62+ have := Nat.div_add_mod' a' n
6663 aesop
67- Β· intros h
68- exists (a * n + i.1 )
69- have {a b : β} : (a * n + b) / n = a + (b / n) := by
70- have := Nat.zero_lt_of_ne_zero inst.out
71- have := Nat.mod_lt b this
72- aesop (add simp [Nat.add_div])
73- aesop (add simp [Nat.mul_add_mod_self_right, Nat.mod_eq_of_lt])
74- β©
64+ aesop
65+ Β· intros h
66+ exists (a * n + i.1 )
67+ have {a b : β} : (a * n + b) / n = a + (b / n) := by
68+ have := Nat.zero_lt_of_ne_zero hn
69+ have := Nat.mod_lt b this
70+ aesop (add simp [Nat.add_div])
71+ aesop (add simp [Nat.mul_add_mod_self_right, Nat.mod_eq_of_lt])
72+ β©
73+
74+ /-- Non-computable helper definition. -/
75+ private noncomputable def splitNthNoncomputable (f : π½[X]) (n : β) (i : Fin n) : π½[X] :=
76+ if n = 0 then f else
77+ β k β f.support,
78+ if k % n = i.1 then Polynomial.C (f.coeff k) * Polynomial.X ^ (k / n) else 0
79+
80+ @[simp]
81+ lemma splitNthNoncomputable_of_nz {f : π½[X]} {n : β} [inst : NeZero n] {i : Fin n} :
82+ splitNthNoncomputable f n i =
83+ β k β f.support,
84+ if k % n = i.1 then Polynomial.C (f.coeff k) * Polynomial.X ^ (k / n) else 0 := by
85+ have := inst.out
86+ aesop (add simp splitNthNoncomputable)
87+
88+ /-- Coefficient formula for `splitNth`: the `e`-th coefficient of the `i`-th component
89+ is the coefficient of `f` at position `e * n + i`. -/
90+ @[simp]
91+ lemma splitNth_coeff {n : β} {f : π½[X]} (i : Fin n) (m : β) :
92+ (splitNth f n i).coeff m = f.coeff (m * n + i.1 ) := by
93+ aesop
94+ (add unsafe [cases Fin])
95+ (add simp [splitNth, Polynomial.coeff_ofFinsupp])
96+
97+ @[simp]
98+ private lemma splitNthNoncomputable_coeff {n : β} {f : π½[X]} (i : Fin n) (m : β) :
99+ (splitNthNoncomputable f n i).coeff m = f.coeff (m * n + i.1 ) := by
100+ by_cases! hn : n β 0
101+ Β· simp only [splitNthNoncomputable, hn, βreduceIte, finsetSum_coeff]
102+ have hi : i.1 < n := i.2
103+ have hdiv : (m * n + i.1 ) / n = m := by
104+ rw [mul_comm, Nat.mul_add_div (Nat.pos_of_ne_zero hn), Nat.div_eq_of_lt hi, Nat.add_zero]
105+ have hmod : (m * n + i.1 ) % n = i.1 := by
106+ rw [mul_comm, Nat.mul_add_mod, Nat.mod_eq_of_lt hi]
107+ have key : β k β f.support,
108+ (if k % n = i.1 then Polynomial.C (f.coeff k) * Polynomial.X ^ (k / n) else 0 ).coeff m
109+ = if k = m * n + i.1 then f.coeff k else 0 := by
110+ intro k hk
111+ have hdm : n * (k / n) + k % n = k := Nat.div_add_mod k n
112+ by_cases h : k % n = i.1
113+ Β· simp only [h, if_true]
114+ rw [Polynomial.coeff_C_mul, Polynomial.coeff_X_pow]
115+ by_cases hm : m = k / n <;> grind
116+ Β· aesop
117+ rw [Finset.sum_congr rfl key]
118+ by_cases hmem : m * n + i.1 β f.support <;>
119+ aesop (add simp [Finset.sum_eq_single,
120+ Finset.sum_eq_zero,
121+ Polynomial.mem_support_iff])
122+ Β· aesop (add safe [cases Fin, (by omega)])
123+
124+ private lemma splitNthNoncomputable_eq_splitNth {n : β} {f : π½[X]} :
125+ splitNth f n = splitNthNoncomputable f n := by aesop
75126
76127/- Proof of key identity `splitNth` has to satisfy. -/
77- omit [NoZeroDivisors π½] in
78- lemma splitNth_def (n : β) (f : π½[X]) [inst : NeZero n] :
128+ lemma eq_sum_splitNth (n : β) [inst : NeZero n] (f : π½[X]) :
79129 f =
80130 β i : Fin n,
81131 (Polynomial.X ^ i.1 ) *
82132 Polynomial.evalβ Polynomial.C (Polynomial.X ^ n) (splitNth f n i) := by
83- ext e
84- rw [Polynomial.finsetSum_coeff]
85- have hβ {b e : β} {f : π½[X]} : (X ^ b * f).coeff e = if e < b then 0 else f.coeff (e - b) := by
86- rw [Polynomial.coeff_X_pow_mul' f b e]
133+ rw [splitNthNoncomputable_eq_splitNth]
134+ have hn : 0 < n := Nat.pos_of_ne_zero inst.out
135+ conv_lhs => rw [Polynomial.as_sum_support_C_mul_X_pow f]
136+ simp only [splitNthNoncomputable_of_nz, evalβ_finsetSum, Finset.mul_sum]
137+ rw [Finset.sum_comm]
138+ apply Finset.sum_congr rfl
139+ intro k hk
140+ have hstep : β i : Fin n, X ^ i.1 * evalβ C (X ^ n)
141+ (if k % n = i.1 then C (f.coeff k) * X ^ (k / n) else 0 )
142+ = if k % n = i.1 then C (f.coeff k) * X ^ k else (0 : π½[X]) := fun i β¦ by
143+ have := Nat.div_add_mod k n
144+ have : X ^ i.1 * (C (f.coeff k) * X ^ (n * (k / n))) =
145+ C (f.coeff k) * X ^ (i.1 + n * (k / n)) := by ring
87146 aesop
88- have hβ {e : β} {f : π½[X]} :
89- (evalβ C (X ^ n) f).coeff e =
90- if e % n = 0
91- then f.coeff (e / n)
92- else 0 := by
93- rw [Polynomial.evalβ_def, Polynomial.coeff_sum, Polynomial.sum_def]
94- conv =>
95- lhs
96- congr
97- Β· skip
98- ext n
99- rw [βpow_mul, Polynomial.coeff_C_mul_X_pow]
100- by_cases h : e % n = 0 <;> simp only [h, βreduceIte]
101- Β· rw [Finset.sum_eq_single (e / n)]
102- Β· have : e = n * (e / n) :=
103- Nat.eq_mul_of_div_eq_right
104- (Nat.dvd_of_mod_eq_zero h) rfl
105- rw [if_pos]
106- exact this
107- Β· intros b hβ hβ
108- have : Β¬ (e = n * b) := by
109- intros h'
110- apply hβ
111- rw [h']
112- exact Nat.eq_div_of_mul_eq_right inst.out rfl
113- simp [this]
114- Β· intros h'
115- split_ifs with h''
116- Β· exact notMem_support_iff.mp h'
117- Β· rfl
118- Β· have {Ξ± : Type } {a b : Ξ±} : β m, (if e = n * m then a else b) = b := by aesop
119- conv =>
120- lhs
121- congr
122- Β· skip
123- ext m
124- rw [this m]
125- rw [Finset.sum_const_zero]
126- conv =>
127- rhs
128- congr
129- Β· skip
130- Β· ext b
131- rw [hβ, hβ]
132- unfold splitNth
133- simp only [coeff_ofFinsupp, Finsupp.coe_mk]
134- rw [Finset.sum_eq_single β¨e % n, by refine Nat.mod_lt e (by have := inst.out; omega)β©]
135- Β· simp only
136- have hβ : Β¬ (e < e % n) := by
137- by_cases h : e < n
138- Β· rw [Nat.mod_eq_of_lt h]
139- simp
140- Β· simp only [not_lt] at h β’
141- exact Nat.mod_le e n
142- have hβ : (e - e % n) % n = 0 := Nat.sub_mod_eq_zero_of_mod_eq (by simp)
143- simp only [hβ, hβ, Eq.symm Nat.div_eq_sub_mod_div, Nat.div_add_mod' e n, βreduceIte]
144- Β· rintro β¨b, hβ© _
145- simp only [ne_eq, Fin.mk.injEq, ite_eq_left_iff, not_lt, ite_eq_right_iff]
146- intros hβ hβ hβ
147- exfalso
148- apply hβ
149- have : e % n = b % n := by
150- have hβ' := hβ
151- rw [βNat.div_add_mod' e n, βNat.div_add_mod' b n] at hβ hβ
152- by_cases h' : e % n β₯ b % n
153- Β· have : e / n * n + e % n - (b / n * n + b % n) =
154- ((e / n - b / n) * n) + (e % n - b % n) := by
155- have : e / n * n + e % n - (b / n * n + b % n) =
156- e / n * n + e % n - b / n * n - b % n := by
157- omega
158- rw [this]
159- have : e / n * n + e % n - b / n * n = ((e / n) - (b / n)) * n + e % n := by
160- have : e / n * n + e % n - b / n * n = (e / n * n - b / n * n) + e % n :=
161- Nat.sub_add_comm (Nat.mul_le_mul (Nat.div_le_div_right hβ') (by rfl))
162- rw [this, βNat.sub_mul]
163- rw [this]
164- exact Nat.add_sub_assoc h' ((e / n - b / n) * n)
165- rw [
166- this, Nat.mul_add_mod_self_right,
167- Nat.mod_eq_of_lt (Nat.sub_lt_of_lt (Nat.mod_lt _ (by linarith)))
168- ] at hβ
169- omega
170- Β· simp only [ge_iff_le, not_le] at h'
171- have : e / n * n + e % n - (b / n * n + b % n) =
172- ((e / n - b / n - 1 ) * n) + (n - (b % n - e % n)) := by
173- have : e / n * n + e % n - (b / n * n + b % n) =
174- e / n * n + e % n - b / n * n - b % n := by
175- omega
176- rw [this]
177- have : e / n * n + e % n - b / n * n = ((e / n) - (b / n)) * n + e % n := by
178- have : e / n * n + e % n - b / n * n = (e / n * n - b / n * n) + e % n :=
179- Nat.sub_add_comm (Nat.mul_le_mul (Nat.div_le_div_right hβ') (by rfl))
180- rw [this, βNat.sub_mul]
181- rw [this]
182- have : e / n - b / n = (e / n - b / n - 1 ) + 1 := by
183- refine Eq.symm (Nat.sub_add_cancel ?_)
184- rw [Nat.one_le_iff_ne_zero]
185- intros h
186- have h := Nat.le_of_sub_eq_zero h
187- nlinarith
188- rw (occs := .pos [1 ]) [this]
189- rw
190- [
191- right_distrib, one_mul, add_assoc,
192- Nat.add_sub_assoc (Nat.le_add_right_of_le (Nat.le_of_lt (Nat.mod_lt_of_lt h)))
193- ]
194- congr 1
195- grind
196- rw [this, Nat.mul_add_mod_self_right] at hβ
197- have {a : β} : (n - a) % n = 0 β§ a < n β a = 0 := by
198- intros h
199- rcases exists_eq_mul_left_of_dvd (Nat.dvd_of_mod_eq_zero h.1 ) with β¨c, h'β©
200- have : a = (1 - c)*n := by
201- have : n = a + c * n := by omega
202- have : n - c * n = a := by omega
203- rw [βthis]
204- have : n = 1 * n := by rw [one_mul]
205- rewrite (occs := .pos [1 ]) [this]
206- exact Eq.symm (Nat.sub_mul 1 c n)
207- have h' := this βΈ h.2
208- rw [this]
209- have : 1 - c = 0 := by
210- have : n = 1 * n := by rw [one_mul]
211- rw (occs := .pos [2 ]) [this] at h'
212- have h' := Nat.lt_of_mul_lt_mul_right h'
213- omega
214- simp [this]
215- exfalso
216- have hβ := this β¨hβ, by apply Nat.sub_lt_of_lt; apply Nat.mod_lt; linarithβ©
217- omega
218- rw [this]
219- exact Eq.symm (Nat.mod_eq_of_lt h)
220- Β· intros h
221- simp at h
147+ (add simp [evalβ_mul, evalβ_C, evalβ_X_pow])
148+ (add unsafe (by rw [β pow_mul]))
149+ (add safe (by omega))
150+ rw [Finset.sum_congr rfl (fun i _ => hstep i),
151+ Finset.sum_eq_single (β¨k % n, Nat.mod_lt k hnβ© : Fin n)] <;> aesop
222152
223153/- Lemma bounding degree of each `n`-split polynomial. -/
224- omit [NoZeroDivisors π½] in
154+ @[simp]
225155lemma splitNth_degree_le {n : β} {f : π½[X]} [inst : NeZero n] {i : Fin n} :
226156 (splitNth f n i).natDegree β€ f.natDegree / n := by
227157 have hn := inst.out
@@ -231,75 +161,41 @@ lemma splitNth_degree_le {n : β} {f : π½[X]} [inst : NeZero n] {i : Fin n} :
231161 rw [Nat.div_lt_iff_lt_mul (by omega),
232162 Nat.lt_iff_le_pred (by omega),
233163 Polynomial.natDegree_le_iff_coeff_eq_zero] at hj
164+ simp only [splitNth_coeff]
234165 exact hj _ (by omega)
235166
236167/-- `foldingPolynomial` in terms of `splitNth`
237168 when `q = X ^ n`. -/
238169@ [simp low]
239- lemma folding_polynomial_eq_sum_splitNth {π½ : Type } [Field π½]
240- {f : Polynomial π½} {n : β}
241- [inst : NeZero n] :
170+ lemma folding_polynomial_eq_sum_splitNth {π½ : Type } [Field π½] {f : Polynomial π½}
171+ {n : β} [inst : NeZero n] :
242172 FoldingPolynomial.foldingPolynomial (X ^ n) f =
243173 β i, C (splitNth f n i) * (X ^ i.val) := by
244174 symm
245175 apply FoldingPolynomial.folding_polynomial_is_unique'
246- Β· conv =>
247- rhs
248- rw [splitNth_def (f := f) (inst := inst)]
249- rw [
250- Polynomial.map_sum,
251- Polynomial.eval_finsetSum]
252- simp only [Polynomial.map_mul, map_C, coe_compRingHom, Polynomial.map_pow, map_X,
253- eval_mul, eval_C, eval_pow, eval_X]
254- simp only [comp]
255- conv =>
256- lhs
257- rhs
258- ext x
259- rw [mul_comm]
260- rfl
261- Β· simp only [Bivariate.degreeX, finsetSum_coeff, coeff_C_mul, coeff_X_pow, mul_ite, mul_one,
262- mul_zero, natDegree_pow, natDegree_X]
263- simp only [Finset.sup_le_iff, mem_support_iff, finsetSum_coeff, coeff_C_mul, coeff_X_pow,
264- mul_ite, mul_one, mul_zero, ne_eq]
265- intro b hb
266- apply natDegree_sum_le_of_forall_le
267- rintro β¨i, hiβ© _
268- by_cases heq: b = i
269- Β· simp only [heq, βreduceIte]
270- exact splitNth_degree_le
271- Β· simp [heq]
176+ Β· conv_rhs => rw [eq_sum_splitNth (f := f) (inst := inst)]
177+ rw [Polynomial.map_sum, Polynomial.eval_finsetSum]
178+ aesop
179+ (add simp [comp])
180+ (add safe (by ac_nf))
181+ Β· aesop
182+ (add simp [Bivariate.degreeX])
183+ (add safe natDegree_sum_le_of_forall_le)
272184 Β· simp only [Bivariate.natDegreeY, natDegree_pow, natDegree_X, mul_one]
273- apply Nat.lt_of_le_pred (by {
274- apply Nat.zero_lt_of_ne_zero
275- aesop
276- })
277- apply Polynomial.natDegree_sum_le_of_forall_le
278- intro i _
279- apply Nat.le_trans Polynomial.natDegree_mul_le
280- rcases i with β¨i, hiβ©
281- simp
282- omega
185+ exact Nat.lt_of_le_pred (by aesop (add unsafe Nat.zero_lt_of_ne_zero)) <| by
186+ exact Polynomial.natDegree_sum_le_of_forall_le _ _ <| fun i _ β¦
187+ Nat.le_trans Polynomial.natDegree_mul_le <| by aesop (add safe (by omega))
283188
284189/-- `polyFold` in terms of `splitNth`. -/
285190@ [simp low]
286191lemma polyFold_eq_sum_of_splitNth {π½ : Type } [Field π½]
287- {f : π½[X]} {n : β} {r : π½}
288- [inst : NeZero n] :
192+ {f : π½[X]} {n : β} {r : π½} [inst : NeZero n] :
289193 FoldingPolynomial.polyFold f n r =
290194 β i, C (r ^ i.val) * splitNth f n i := by
291195 aesop
292196 (add simp [FoldingPolynomial.polyFold, Polynomial.eval_finsetSum])
293197 (add safe (by grind))
294198
295- omit [NoZeroDivisors π½] in
296- /-- Coefficient formula for `splitNth`: the `e`-th coefficient of the `i`-th component
297- is the coefficient of `f` at position `e * n + i`. -/
298- @[simp]
299- lemma splitNth_coeff {n : β} [NeZero n] {g : π½[X]} {i : Fin n} {e : β} :
300- (splitNth g n i).coeff e = g.coeff (e * n + i.1 ) := by simp [splitNth]
301-
302- omit [NoZeroDivisors π½] in
303199/-- `splitNth` is the left inverse of the `n`-way recombination: splitting the polynomial
304200 `β j, X^j * (u j)(X^n)` recovers `u i` for each component `i`. -/
305201@[simp]
@@ -345,7 +241,6 @@ theorem polyFold_sum {π½ : Type} [Field π½] {r : π½}
345241 Polynomial.smul_eq_C_mul])
346242 (add safe (by grind))
347243
348- omit [NoZeroDivisors π½] in
349244/--
350245Lemma bridges the coefficient-level identity `splitNth_def` and
351246evaluation-level reasoning about `splitNth` and `foldNth`.
0 commit comments