Skip to content

Commit 41772ea

Browse files
committed
feat(Algebra/Polynomial/Factors): Introduce predicate and basic API (#30212)
This PR introduces a predicate `Polynomial.Factors` that generalizes `Polynomial.Splits` beyond fields. This PR contains the definition of `Polynomial.Factors` along with most of API for `Polynomial.Splits`. The end goal is to either redefine `Polynomial.Splits` in terms of `Polynomial.Factors` or deprecate `Polynomial.Splits` entirely. Zulip discussion: https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Generalizing.20.60Polynomial.2ESplits.60.20to.20rings Co-authored-by: tb65536 <thomas.l.browning@gmail.com>
1 parent 9fdf911 commit 41772ea

File tree

4 files changed

+305
-1
lines changed

4 files changed

+305
-1
lines changed

Mathlib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@ import Mathlib.Algebra.Polynomial.Eval.Irreducible
10401040
import Mathlib.Algebra.Polynomial.Eval.SMul
10411041
import Mathlib.Algebra.Polynomial.Eval.Subring
10421042
import Mathlib.Algebra.Polynomial.Expand
1043+
import Mathlib.Algebra.Polynomial.Factors
10431044
import Mathlib.Algebra.Polynomial.FieldDivision
10441045
import Mathlib.Algebra.Polynomial.GroupRingAction
10451046
import Mathlib.Algebra.Polynomial.HasseDeriv
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
/-
2+
Copyright (c) 2025 Thomas Browning. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Thomas Browning
5+
-/
6+
import Mathlib.Algebra.Polynomial.FieldDivision
7+
8+
/-!
9+
# Split polynomials
10+
11+
A polynomial `f : R[X]` factors if it is a product of constant and monic linear polynomials.
12+
13+
## Main definitions
14+
15+
* `Polynomial.Factors f`: A predicate on a polynomial `f` saying that `f` is a product of
16+
constant and monic linear polynomials.
17+
18+
## TODO
19+
20+
- Redefine `Splits` in terms of `Factors` and then deprecate `Splits`.
21+
22+
-/
23+
24+
variable {R : Type*}
25+
26+
namespace Polynomial
27+
28+
section Semiring
29+
30+
variable [Semiring R]
31+
32+
/-- A polynomial `Factors` if it is a product of constant and monic linear polynomials.
33+
This will eventually replace `Polynomial.Splits`. -/
34+
def Factors (f : R[X]) : Prop := f ∈ Submonoid.closure ({C a | a : R} ∪ {X + C a | a : R})
35+
36+
@[simp, aesop safe apply]
37+
protected theorem Factors.C (a : R) : Factors (C a) :=
38+
Submonoid.mem_closure_of_mem (Set.mem_union_left _ ⟨a, rfl⟩)
39+
40+
@[simp, aesop safe apply]
41+
protected theorem Factors.zero : Factors (0 : R[X]) := by
42+
simpa using Factors.C (0 : R)
43+
44+
@[simp, aesop safe apply]
45+
protected theorem Factors.one : Factors (1 : R[X]) :=
46+
Factors.C (1 : R)
47+
48+
@[simp, aesop safe apply]
49+
theorem Factors.X_add_C (a : R) : Factors (X + C a) :=
50+
Submonoid.mem_closure_of_mem (Set.mem_union_right _ ⟨a, rfl⟩)
51+
52+
@[simp, aesop safe apply]
53+
protected theorem Factors.X : Factors (X : R[X]) := by
54+
simpa using Factors.X_add_C (0 : R)
55+
56+
@[simp, aesop safe apply]
57+
protected theorem Factors.mul {f g : R[X]} (hf : Factors f) (hg : Factors g) :
58+
Factors (f * g) :=
59+
mul_mem hf hg
60+
61+
protected theorem Factors.C_mul {f : R[X]} (hf : Factors f) (a : R) : Factors (C a * f) :=
62+
(Factors.C a).mul hf
63+
64+
@[simp, aesop safe apply]
65+
theorem Factors.listProd {l : List R[X]} (h : ∀ f ∈ l, Factors f) : Factors l.prod :=
66+
list_prod_mem h
67+
68+
@[simp, aesop safe apply]
69+
protected theorem Factors.pow {f : R[X]} (hf : Factors f) (n : ℕ) : Factors (f ^ n) :=
70+
pow_mem hf n
71+
72+
theorem Factors.X_pow (n : ℕ) : Factors (X ^ n : R[X]) :=
73+
Factors.X.pow n
74+
75+
theorem Factors.C_mul_X_pow (a : R) (n : ℕ) : Factors (C a * X ^ n) :=
76+
(Factors.X_pow n).C_mul a
77+
78+
@[simp, aesop safe apply]
79+
theorem Factors.monomial (n : ℕ) (a : R) : Factors (monomial n a) := by
80+
simp [← C_mul_X_pow_eq_monomial]
81+
82+
protected theorem Factors.map {f : R[X]} (hf : Factors f) {S : Type*} [Semiring S] (i : R →+* S) :
83+
Factors (map i f) := by
84+
induction hf using Submonoid.closure_induction <;> aesop
85+
86+
theorem factors_of_natDegree_eq_zero {f : R[X]} (hf : natDegree f = 0) :
87+
Factors f :=
88+
(natDegree_eq_zero.mp hf).choose_spec ▸ by aesop
89+
90+
theorem factors_of_degree_le_zero {f : R[X]} (hf : degree f ≤ 0) :
91+
Factors f :=
92+
factors_of_natDegree_eq_zero (natDegree_eq_zero_iff_degree_le_zero.mpr hf)
93+
94+
end Semiring
95+
96+
section CommSemiring
97+
98+
variable [CommSemiring R]
99+
100+
@[simp, aesop safe apply]
101+
theorem Factors.multisetProd {m : Multiset R[X]} (hm : ∀ f ∈ m, Factors f) : Factors m.prod :=
102+
multiset_prod_mem _ hm
103+
104+
@[simp, aesop safe apply]
105+
protected theorem Factors.prod {ι : Type*} {f : ι → R[X]} {s : Finset ι}
106+
(h : ∀ i ∈ s, Factors (f i)) : Factors (∏ i ∈ s, f i) :=
107+
prod_mem h
108+
109+
/-- See `factors_iff_exists_multiset` for the version with subtraction. -/
110+
theorem factors_iff_exists_multiset' {f : R[X]} :
111+
Factors f ↔ ∃ m : Multiset R, f = C f.leadingCoeff * (m.map (X + C ·)).prod := by
112+
refine ⟨fun hf ↦ ?_, ?_⟩
113+
· let S : Submonoid R[X] := MonoidHom.mrange C
114+
have hS : S = {C a | a : R} := MonoidHom.coe_mrange C
115+
rw [Factors, Submonoid.closure_union, ← hS, Submonoid.closure_eq, Submonoid.mem_sup] at hf
116+
obtain ⟨-, ⟨a, rfl⟩, g, hg, rfl⟩ := hf
117+
obtain ⟨mg, hmg, rfl⟩ := Submonoid.exists_multiset_of_mem_closure hg
118+
choose! j hj using hmg
119+
have hmg : mg = (mg.map j).map (X + C ·) := by simp [Multiset.map_congr rfl hj]
120+
rw [hmg, leadingCoeff_mul_monic, leadingCoeff_C]
121+
· use mg.map j
122+
· rw [hmg]
123+
apply monic_multiset_prod_of_monic
124+
simp [monic_X_add_C]
125+
· rintro ⟨m, hm⟩
126+
exact hm ▸ (Factors.C _).mul (.multisetProd (by simp [Factors.X_add_C]))
127+
128+
theorem Factors.natDegree_le_one_of_irreducible {f : R[X]} (hf : Factors f)
129+
(h : Irreducible f) : natDegree f ≤ 1 := by
130+
nontriviality R
131+
obtain ⟨m, hm⟩ := factors_iff_exists_multiset'.mp hf
132+
rcases m.empty_or_exists_mem with rfl | ⟨a, ha⟩
133+
· rw [hm]
134+
simp
135+
· obtain ⟨m, rfl⟩ := Multiset.exists_cons_of_mem ha
136+
rw [Multiset.map_cons, Multiset.prod_cons] at hm
137+
rw [hm] at h
138+
simp only [irreducible_mul_iff, IsUnit.mul_iff, not_isUnit_X_add_C, false_and, and_false,
139+
or_false, false_or, ← Multiset.prod_toList, List.prod_isUnit_iff] at h
140+
have : m = 0 := by simpa [not_isUnit_X_add_C, ← Multiset.eq_zero_iff_forall_notMem] using h.1.2
141+
grw [hm, this, natDegree_mul_le]
142+
simp
143+
144+
end CommSemiring
145+
146+
section Ring
147+
148+
variable [Ring R]
149+
150+
@[simp, aesop safe apply]
151+
theorem Factors.X_sub_C (a : R) : Factors (X - C a) := by
152+
simpa using Factors.X_add_C (-a)
153+
154+
@[aesop safe apply]
155+
protected theorem Factors.neg {f : R[X]} (hf : Factors f) : Factors (-f) := by
156+
rw [← neg_one_mul, ← C_1, ← C_neg]
157+
exact hf.C_mul (-1)
158+
159+
@[simp]
160+
theorem factors_neg_iff {f : R[X]} : Factors (-f) ↔ Factors f :=
161+
fun hf ↦ neg_neg f ▸ hf.neg, .neg⟩
162+
163+
end Ring
164+
165+
section CommRing
166+
167+
variable [CommRing R] {f g : R[X]}
168+
169+
theorem factors_iff_exists_multiset :
170+
Factors f ↔ ∃ m : Multiset R, f = C f.leadingCoeff * (m.map (X - C ·)).prod := by
171+
refine factors_iff_exists_multiset'.trans ⟨?_, ?_⟩ <;>
172+
rintro ⟨m, hm⟩ <;> exact ⟨m.map (- ·), by simpa⟩
173+
174+
theorem Factors.exists_eval_eq_zero (hf : Factors f) (hf0 : degree f ≠ 0) :
175+
∃ a, eval a f = 0 := by
176+
obtain ⟨m, hm⟩ := factors_iff_exists_multiset.mp hf
177+
by_cases hf₀ : f.leadingCoeff = 0
178+
· simp [leadingCoeff_eq_zero.mp hf₀]
179+
obtain rfl | ⟨a, ha⟩ := m.empty_or_exists_mem
180+
· rw [hm, Multiset.map_zero, Multiset.prod_zero, mul_one, degree_C hf₀] at hf0
181+
contradiction
182+
obtain ⟨m, rfl⟩ := Multiset.exists_cons_of_mem ha
183+
exact ⟨a, hm ▸ by simp⟩
184+
185+
variable [IsDomain R]
186+
187+
theorem Factors.eq_prod_roots (hf : Factors f) :
188+
f = C f.leadingCoeff * (f.roots.map (X - C ·)).prod := by
189+
by_cases hf0 : f.leadingCoeff = 0
190+
· simp [leadingCoeff_eq_zero.mp hf0]
191+
· obtain ⟨m, hm⟩ := factors_iff_exists_multiset.mp hf
192+
suffices hf : f.roots = m by rwa [hf]
193+
rw [hm, roots_C_mul _ hf0, roots_multiset_prod_X_sub_C]
194+
195+
theorem Factors.natDegree_eq_card_roots (hf : Factors f) :
196+
f.natDegree = f.roots.card := by
197+
by_cases hf0 : f.leadingCoeff = 0
198+
· simp [leadingCoeff_eq_zero.mp hf0]
199+
· conv_lhs => rw [hf.eq_prod_roots, natDegree_C_mul hf0, natDegree_multiset_prod_X_sub_C_eq_card]
200+
201+
theorem Factors.roots_ne_zero (hf : Factors f) (hf0 : natDegree f ≠ 0) :
202+
f.roots ≠ 0 := by
203+
obtain ⟨a, ha⟩ := hf.exists_eval_eq_zero (degree_ne_of_natDegree_ne hf0)
204+
exact mt (· ▸ (mem_roots (by aesop)).mpr ha) (Multiset.notMem_zero a)
205+
206+
theorem factors_X_sub_C_mul_iff {a : R} : Factors ((X - C a) * f) ↔ Factors f := by
207+
refine ⟨fun hf ↦ ?_, ((Factors.X_sub_C _).mul ·)⟩
208+
by_cases hf₀ : f = 0
209+
· aesop
210+
have := hf.eq_prod_roots
211+
rw [leadingCoeff_mul, leadingCoeff_X_sub_C, one_mul,
212+
roots_mul (mul_ne_zero (X_sub_C_ne_zero _) hf₀), roots_X_sub_C,
213+
Multiset.singleton_add, Multiset.map_cons, Multiset.prod_cons, mul_left_comm] at this
214+
rw [mul_left_cancel₀ (X_sub_C_ne_zero _) this]
215+
aesop
216+
217+
theorem factors_mul_iff (hf₀ : f ≠ 0) (hg₀ : g ≠ 0) :
218+
Factors (f * g) ↔ Factors f ∧ Factors g := by
219+
refine ⟨fun h ↦ ?_, and_imp.mpr .mul⟩
220+
generalize hp : f * g = p at *
221+
generalize hn : p.natDegree = n
222+
induction n generalizing p f g with
223+
| zero =>
224+
rw [← hp, natDegree_mul hf₀ hg₀, Nat.add_eq_zero] at hn
225+
exact ⟨factors_of_natDegree_eq_zero hn.1, factors_of_natDegree_eq_zero hn.2
226+
| succ n ih =>
227+
obtain ⟨a, ha⟩ := Factors.exists_eval_eq_zero h (degree_ne_of_natDegree_ne <| hn ▸ by aesop)
228+
have := dvd_iff_isRoot.mpr ha
229+
rw [← hp, (prime_X_sub_C a).dvd_mul] at this
230+
wlog hf : X - C a ∣ f with hf2
231+
· exact .symm <| hf2 n ih hg₀ hf₀ p ((mul_comm g f).trans hp) h hn a ha this.symm <|
232+
this.resolve_left hf
233+
obtain ⟨f, rfl⟩ := hf
234+
rw [mul_assoc] at hp; subst hp
235+
rw [natDegree_mul (by aesop) (by aesop), natDegree_X_sub_C, add_comm, Nat.succ_inj] at hn
236+
have := ih (by aesop) hg₀ (f * g) rfl (factors_X_sub_C_mul_iff.mp h) hn
237+
aesop
238+
239+
theorem Factors.of_dvd (hg : Factors g) (hg₀ : g ≠ 0) (hfg : f ∣ g) : Factors f := by
240+
obtain ⟨g, rfl⟩ := hfg
241+
exact ((factors_mul_iff (by aesop) (by aesop)).mp hg).1
242+
243+
-- Todo: Remove or fix name once `Splits` is gone.
244+
theorem Factors.splits (hf : Factors f) :
245+
f = 0 ∨ ∀ {g : R[X]}, Irreducible g → g ∣ f → degree g ≤ 1 :=
246+
or_iff_not_imp_left.mpr fun hf0 _ hg hgf ↦ degree_le_of_natDegree_le <|
247+
(hf.of_dvd hf0 hgf).natDegree_le_one_of_irreducible hg
248+
249+
end CommRing
250+
251+
section DivisionSemiring
252+
253+
variable [DivisionSemiring R]
254+
255+
theorem Factors.of_natDegree_le_one {f : R[X]} (hf : natDegree f ≤ 1) : Factors f := by
256+
obtain ⟨a, b, rfl⟩ := exists_eq_X_add_C_of_natDegree_le_one hf
257+
by_cases ha : a = 0
258+
· aesop
259+
· rw [← mul_inv_cancel_left₀ ha b, C_mul, ← mul_add]
260+
exact (X_add_C (a⁻¹ * b)).C_mul a
261+
262+
theorem Factors.of_natDegree_eq_one {f : R[X]} (hf : natDegree f = 1) : Factors f :=
263+
of_natDegree_le_one hf.le
264+
265+
theorem Factors.of_degree_le_one {f : R[X]} (hf : degree f ≤ 1) : Factors f :=
266+
of_natDegree_le_one (natDegree_le_of_degree_le hf)
267+
268+
theorem Factors.of_degree_eq_one {f : R[X]} (hf : degree f = 1) : Factors f :=
269+
of_degree_le_one hf.le
270+
271+
end DivisionSemiring
272+
273+
section Field
274+
275+
variable [Field R]
276+
277+
open UniqueFactorizationMonoid in
278+
-- Todo: Remove or fix name once `Splits` is gone.
279+
theorem factors_iff_splits {f : R[X]} :
280+
Factors f ↔ f = 0 ∨ ∀ {g : R[X]}, Irreducible g → g ∣ f → degree g = 1 := by
281+
refine ⟨fun hf ↦ hf.splits.imp_right (forall₃_imp fun g hg hgf ↦
282+
(le_antisymm · (Nat.WithBot.one_le_iff_zero_lt.mpr hg.degree_pos))), ?_⟩
283+
rintro (rfl | hf)
284+
· aesop
285+
classical
286+
by_cases hf0 : f = 0
287+
· simp [hf0]
288+
obtain ⟨u, hu⟩ := factors_prod hf0
289+
rw [← hu]
290+
refine (Factors.multisetProd fun g hg ↦ ?_).mul
291+
(factors_of_natDegree_eq_zero (natDegree_eq_zero_of_isUnit u.isUnit))
292+
exact Factors.of_degree_eq_one (hf (irreducible_of_factor g hg) (dvd_of_mem_factors hg))
293+
294+
end Field
295+
296+
end Polynomial

Mathlib/Algebra/Polynomial/Monic.lean

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@ theorem natDegree_mul_comm (hp : p.Monic) (q : R[X]) : (p * q).natDegree = (q *
180180
rw [hp.natDegree_mul' h, Polynomial.natDegree_mul', add_comm]
181181
simpa [hp.leadingCoeff, leadingCoeff_ne_zero]
182182

183+
theorem _root_.Polynomial.not_isUnit_X_add_C [Nontrivial R] (a : R) : ¬ IsUnit (X + C a) := by
184+
rintro ⟨⟨_, g, hfg, hgf⟩, rfl⟩
185+
have h := (monic_X_add_C a).natDegree_mul' (right_ne_zero_of_mul_eq_one hfg)
186+
rw [hfg, natDegree_one, natDegree_X_add_C] at h
187+
grind
188+
183189
theorem not_dvd_of_natDegree_lt (hp : Monic p) (h0 : q ≠ 0) (hl : natDegree q < natDegree p) :
184190
¬p ∣ q := by
185191
rintro ⟨r, rfl⟩

Mathlib/Algebra/Polynomial/Splits.lean

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ section CommRing
3939
variable [CommRing K] [Field L] [Field F]
4040
variable (i : K →+* L)
4141

42-
/-- A polynomial `Splits` iff it is zero or all of its irreducible factors have `degree` 1. -/
42+
/-- A polynomial `Splits` iff it is zero or all of its irreducible factors have `degree` 1.
43+
This will eventually be replaced by `Polynomial.Factors`. -/
4344
def Splits (f : K[X]) : Prop :=
4445
f.map i = 0 ∨ ∀ {g : L[X]}, Irreducible g → g ∣ f.map i → degree g = 1
4546

0 commit comments

Comments
 (0)