diff options
| author | Enrico Tassi | 2015-03-09 11:07:53 +0100 |
|---|---|---|
| committer | Enrico Tassi | 2015-03-09 11:24:38 +0100 |
| commit | fc84c27eac260dffd8f2fb1cb56d599f1e3486d9 (patch) | |
| tree | c16205f1637c80833a4c4598993c29fa0fd8c373 /mathcomp/algebra/poly.v | |
Initial commit
Diffstat (limited to 'mathcomp/algebra/poly.v')
| -rw-r--r-- | mathcomp/algebra/poly.v | 2591 |
1 files changed, 2591 insertions, 0 deletions
diff --git a/mathcomp/algebra/poly.v b/mathcomp/algebra/poly.v new file mode 100644 index 0000000..813f70d --- /dev/null +++ b/mathcomp/algebra/poly.v @@ -0,0 +1,2591 @@ +(* (c) Copyright Microsoft Corporation and Inria. All rights reserved. *) +Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq div choice fintype. +Require Import bigop ssralg binomial. + +(******************************************************************************) +(* This file provides a library for univariate polynomials over ring *) +(* structures; it also provides an extended theory for polynomials whose *) +(* coefficients range over commutative rings and integral domains. *) +(* *) +(* {poly R} == the type of polynomials with coefficients of type R, *) +(* represented as lists with a non zero last element *) +(* (big endian representation); the coeficient type R *) +(* must have a canonical ringType structure cR. In fact *) +(* {poly R} denotes the concrete type polynomial cR; R *) +(* is just a phantom argument that lets type inference *) +(* reconstruct the (hidden) ringType structure cR. *) +(* p : seq R == the big-endian sequence of coefficients of p, via *) +(* the coercion polyseq : polynomial >-> seq. *) +(* Poly s == the polynomial with coefficient sequence s (ignoring *) +(* trailing zeroes). *) +(* \poly_(i < n) E(i) == the polynomial of degree at most n - 1 whose *) +(* coefficients are given by the general term E(i) *) +(* 0, 1, - p, p + q, == the usual ring operations: {poly R} has a canonical *) +(* p * q, p ^+ n, ... ringType structure, which is commutative / integral *) +(* when R is commutative / integral, respectively. *) +(* polyC c, c%:P == the constant polynomial c *) +(* 'X == the (unique) variable *) +(* 'X^n == a power of 'X; 'X^0 is 1, 'X^1 is convertible to 'X *) +(* p`_i == the coefficient of 'X^i in p; this is in fact just *) +(* the ring_scope notation generic seq-indexing using *) +(* nth 0%R, combined with the polyseq coercion. *) +(* coefp i == the linear function p |-> p`_i (self-exapanding). *) +(* size p == 1 + the degree of p, or 0 if p = 0 (this is the *) +(* generic seq function combined with polyseq). *) +(* lead_coef p == the coefficient of the highest monomial in p, or 0 *) +(* if p = 0 (hence lead_coef p = 0 iff p = 0) *) +(* p \is monic <=> lead_coef p == 1 (0 is not monic). *) +(* p \is a polyOver S <=> the coefficients of p satisfy S; S should have a *) +(* key that should be (at least) an addrPred. *) +(* p.[x] == the evaluation of a polynomial p at a point x using *) +(* the Horner scheme *) +(* *** The multi-rule hornerE (resp., hornerE_comm) unwinds *) +(* horner evaluation of a polynomial expression (resp., *) +(* in a non commutative ring, with side conditions). *) +(* p^`() == formal derivative of p *) +(* p^`(n) == formal n-derivative of p *) +(* p^`N(n) == formal n-derivative of p divided by n! *) +(* p \Po q == polynomial composition; because this is naturally a *) +(* a linear morphism in the first argument, this *) +(* notation is transposed (q comes before p for redex *) +(* selection, etc). *) +(* := \sum(i < size p) p`_i *: q ^+ i *) +(* comm_poly p x == x and p.[x] commute; this is a sufficient condition *) +(* for evaluating (q * p).[x] as q.[x] * p.[x] when R *) +(* is not commutative. *) +(* comm_coef p x == x commutes with all the coefficients of p (clearly, *) +(* this implies comm_poly p x). *) +(* root p x == x is a root of p, i.e., p.[x] = 0 *) +(* n.-unity_root x == x is an nth root of unity, i.e., a root of 'X^n - 1 *) +(* n.-primitive_root x == x is a primitive nth root of unity, i.e., n is the *) +(* least positive integer m > 0 such that x ^+ m = 1. *) +(* *** The submodule poly.UnityRootTheory can be used to *) +(* import selectively the part of the theory of roots *) +(* of unity that doesn't mention polynomials explicitly *) +(* map_poly f p == the image of the polynomial by the function f (which *) +(* (locally, p^f) is usually a ring morphism). *) +(* p^:P == p lifted to {poly {poly R}} (:= map_poly polyC p). *) +(* commr_rmorph f u == u commutes with the image of f (i.e., with all f x). *) +(* horner_morph cfu == given cfu : commr_rmorph f u, the function mapping p *) +(* to the value of map_poly f p at u; this is a ring *) +(* morphism from {poly R} to the codomain of f when f *) +(* is a ring morphism. *) +(* horner_eval u == the function mapping p to p.[u]; this function can *) +(* only be used for u in a commutative ring, so it is *) +(* always a linear ring morphism from {poly R} to R. *) +(* diff_roots x y == x and y are distinct roots; if R is a field, this *) +(* just means x != y, but this concept is generalized *) +(* to the case where R is only a ring with units (i.e., *) +(* a unitRingType); in which case it means that x and y *) +(* commute, and that the difference x - y is a unit *) +(* (i.e., has a multiplicative inverse) in R. *) +(* to just x != y). *) +(* uniq_roots s == s is a sequence or pairwise distinct roots, in the *) +(* sense of diff_roots p above. *) +(* *** We only show that these operations and properties are transferred by *) +(* morphisms whose domain is a field (thus ensuring injectivity). *) +(* We prove the factor_theorem, and the max_poly_roots inequality relating *) +(* the number of distinct roots of a polynomial and its size. *) +(* The some polynomial lemmas use following suffix interpretation : *) +(* C - constant polynomial (as in polyseqC : a%:P = nseq (a != 0) a). *) +(* X - the polynomial variable 'X (as in coefX : 'X`_i = (i == 1%N)). *) +(* Xn - power of 'X (as in monicXn : monic 'X^n). *) +(******************************************************************************) + +Set Implicit Arguments. +Unset Strict Implicit. +Unset Printing Implicit Defensive. + +Import GRing.Theory. +Open Local Scope ring_scope. + +Reserved Notation "{ 'poly' T }" (at level 0, format "{ 'poly' T }"). +Reserved Notation "c %:P" (at level 2, format "c %:P"). +Reserved Notation "p ^:P" (at level 2, format "p ^:P"). +Reserved Notation "'X" (at level 0). +Reserved Notation "''X^' n" (at level 3, n at level 2, format "''X^' n"). +Reserved Notation "\poly_ ( i < n ) E" + (at level 36, E at level 36, i, n at level 50, + format "\poly_ ( i < n ) E"). +Reserved Notation "p \Po q" (at level 50). +Reserved Notation "p ^`N ( n )" (at level 8, format "p ^`N ( n )"). +Reserved Notation "n .-unity_root" (at level 2, format "n .-unity_root"). +Reserved Notation "n .-primitive_root" + (at level 2, format "n .-primitive_root"). + +Local Notation simp := Monoid.simpm. + +Section Polynomial. + +Variable R : ringType. + +(* Defines a polynomial as a sequence with <> 0 last element *) +Record polynomial := Polynomial {polyseq :> seq R; _ : last 1 polyseq != 0}. + +Canonical polynomial_subType := Eval hnf in [subType for polyseq]. +Definition polynomial_eqMixin := Eval hnf in [eqMixin of polynomial by <:]. +Canonical polynomial_eqType := Eval hnf in EqType polynomial polynomial_eqMixin. +Definition polynomial_choiceMixin := [choiceMixin of polynomial by <:]. +Canonical polynomial_choiceType := + Eval hnf in ChoiceType polynomial polynomial_choiceMixin. + +Lemma poly_inj : injective polyseq. Proof. exact: val_inj. Qed. + +Definition poly_of of phant R := polynomial. +Identity Coercion type_poly_of : poly_of >-> polynomial. + +Definition coefp_head h i (p : poly_of (Phant R)) := let: tt := h in p`_i. + +End Polynomial. + +(* We need to break off the section here to let the argument scope *) +(* directives take effect. *) +Bind Scope ring_scope with poly_of. +Bind Scope ring_scope with polynomial. +Arguments Scope polyseq [_ ring_scope]. +Arguments Scope poly_inj [_ ring_scope ring_scope _]. +Arguments Scope coefp_head [_ _ nat_scope ring_scope _]. +Notation "{ 'poly' T }" := (poly_of (Phant T)). +Notation coefp i := (coefp_head tt i). + +Section PolynomialTheory. + +Variable R : ringType. +Implicit Types (a b c x y z : R) (p q r d : {poly R}). + +Canonical poly_subType := Eval hnf in [subType of {poly R}]. +Canonical poly_eqType := Eval hnf in [eqType of {poly R}]. +Canonical poly_choiceType := Eval hnf in [choiceType of {poly R}]. + +Definition lead_coef p := p`_(size p).-1. +Lemma lead_coefE p : lead_coef p = p`_(size p).-1. Proof. by []. Qed. + +Definition poly_nil := @Polynomial R [::] (oner_neq0 R). +Definition polyC c : {poly R} := insubd poly_nil [:: c]. + +Local Notation "c %:P" := (polyC c). + +(* Remember the boolean (c != 0) is coerced to 1 if true and 0 if false *) +Lemma polyseqC c : c%:P = nseq (c != 0) c :> seq R. +Proof. by rewrite val_insubd /=; case: (c == 0). Qed. + +Lemma size_polyC c : size c%:P = (c != 0). +Proof. by rewrite polyseqC size_nseq. Qed. + +Lemma coefC c i : c%:P`_i = if i == 0%N then c else 0. +Proof. by rewrite polyseqC; case: i => [|[]]; case: eqP. Qed. + +Lemma polyCK : cancel polyC (coefp 0). +Proof. by move=> c; rewrite [coefp 0 _]coefC. Qed. + +Lemma polyC_inj : injective polyC. +Proof. by move=> c1 c2 eqc12; have:= coefC c2 0; rewrite -eqc12 coefC. Qed. + +Lemma lead_coefC c : lead_coef c%:P = c. +Proof. by rewrite /lead_coef polyseqC; case: eqP. Qed. + +(* Extensional interpretation (poly <=> nat -> R) *) +Lemma polyP p q : nth 0 p =1 nth 0 q <-> p = q. +Proof. +split=> [eq_pq | -> //]; apply: poly_inj. +without loss lt_pq: p q eq_pq / size p < size q. + move=> IH; case: (ltngtP (size p) (size q)); try by move/IH->. + move/(@eq_from_nth _ 0); exact. +case: q => q nz_q /= in lt_pq eq_pq *; case/eqP: nz_q. +by rewrite (last_nth 0) -(subnKC lt_pq) /= -eq_pq nth_default ?leq_addr. +Qed. + +Lemma size1_polyC p : size p <= 1 -> p = (p`_0)%:P. +Proof. +move=> le_p_1; apply/polyP=> i; rewrite coefC. +by case: i => // i; rewrite nth_default // (leq_trans le_p_1). +Qed. + +(* Builds a polynomial by extension. *) +Definition cons_poly c p : {poly R} := + if p is Polynomial ((_ :: _) as s) ns then + @Polynomial R (c :: s) ns + else c%:P. + +Lemma polyseq_cons c p : + cons_poly c p = (if ~~ nilp p then c :: p else c%:P) :> seq R. +Proof. by case: p => [[]]. Qed. + +Lemma size_cons_poly c p : + size (cons_poly c p) = (if nilp p && (c == 0) then 0%N else (size p).+1). +Proof. by case: p => [[|c' s] _] //=; rewrite size_polyC; case: eqP. Qed. + +Lemma coef_cons c p i : (cons_poly c p)`_i = if i == 0%N then c else p`_i.-1. +Proof. +by case: p i => [[|c' s] _] [] //=; rewrite polyseqC; case: eqP => //= _ []. +Qed. + +(* Build a polynomial directly from a list of coefficients. *) +Definition Poly := foldr cons_poly 0%:P. + +Lemma PolyK c s : last c s != 0 -> Poly s = s :> seq R. +Proof. +case: s => {c}/= [_ |c s]; first by rewrite polyseqC eqxx. +elim: s c => /= [|a s IHs] c nz_c; rewrite polyseq_cons ?{}IHs //. +by rewrite !polyseqC !eqxx nz_c. +Qed. + +Lemma polyseqK p : Poly p = p. +Proof. by apply: poly_inj; exact: PolyK (valP p). Qed. + +Lemma size_Poly s : size (Poly s) <= size s. +Proof. +elim: s => [|c s IHs] /=; first by rewrite polyseqC eqxx. +by rewrite polyseq_cons; case: ifP => // _; rewrite size_polyC; case: (~~ _). +Qed. + +Lemma coef_Poly s i : (Poly s)`_i = s`_i. +Proof. +by elim: s i => [|c s IHs] /= [|i]; rewrite !(coefC, eqxx, coef_cons) /=. +Qed. + +(* Build a polynomial from an infinite sequence of coefficients and a bound. *) +Definition poly_expanded_def n E := Poly (mkseq E n). +Fact poly_key : unit. Proof. by []. Qed. +Definition poly := locked_with poly_key poly_expanded_def. +Canonical poly_unlockable := [unlockable fun poly]. +Local Notation "\poly_ ( i < n ) E" := (poly n (fun i : nat => E)). + +Lemma polyseq_poly n E : + E n.-1 != 0 -> \poly_(i < n) E i = mkseq [eta E] n :> seq R. +Proof. +rewrite unlock; case: n => [|n] nzEn; first by rewrite polyseqC eqxx. +by rewrite (@PolyK 0) // -nth_last nth_mkseq size_mkseq. +Qed. + +Lemma size_poly n E : size (\poly_(i < n) E i) <= n. +Proof. by rewrite unlock (leq_trans (size_Poly _)) ?size_mkseq. Qed. + +Lemma size_poly_eq n E : E n.-1 != 0 -> size (\poly_(i < n) E i) = n. +Proof. by move/polyseq_poly->; apply: size_mkseq. Qed. + +Lemma coef_poly n E k : (\poly_(i < n) E i)`_k = (if k < n then E k else 0). +Proof. +rewrite unlock coef_Poly. +have [lt_kn | le_nk] := ltnP k n; first by rewrite nth_mkseq. +by rewrite nth_default // size_mkseq. +Qed. + +Lemma lead_coef_poly n E : + n > 0 -> E n.-1 != 0 -> lead_coef (\poly_(i < n) E i) = E n.-1. +Proof. +by case: n => // n _ nzE; rewrite /lead_coef size_poly_eq // coef_poly leqnn. +Qed. + +Lemma coefK p : \poly_(i < size p) p`_i = p. +Proof. +by apply/polyP=> i; rewrite coef_poly; case: ltnP => // /(nth_default 0)->. +Qed. + +(* Zmodule structure for polynomial *) +Definition add_poly_def p q := \poly_(i < maxn (size p) (size q)) (p`_i + q`_i). +Fact add_poly_key : unit. Proof. by []. Qed. +Definition add_poly := locked_with add_poly_key add_poly_def. +Canonical add_poly_unlockable := [unlockable fun add_poly]. + +Definition opp_poly_def p := \poly_(i < size p) - p`_i. +Fact opp_poly_key : unit. Proof. by []. Qed. +Definition opp_poly := locked_with opp_poly_key opp_poly_def. +Canonical opp_poly_unlockable := [unlockable fun opp_poly]. + +Fact coef_add_poly p q i : (add_poly p q)`_i = p`_i + q`_i. +Proof. +rewrite unlock coef_poly; case: leqP => //. +by rewrite geq_max => /andP[le_p_i le_q_i]; rewrite !nth_default ?add0r. +Qed. + +Fact coef_opp_poly p i : (opp_poly p)`_i = - p`_i. +Proof. +rewrite unlock coef_poly /=. +by case: leqP => // le_p_i; rewrite nth_default ?oppr0. +Qed. + +Fact add_polyA : associative add_poly. +Proof. by move=> p q r; apply/polyP=> i; rewrite !coef_add_poly addrA. Qed. + +Fact add_polyC : commutative add_poly. +Proof. by move=> p q; apply/polyP=> i; rewrite !coef_add_poly addrC. Qed. + +Fact add_poly0 : left_id 0%:P add_poly. +Proof. +by move=> p; apply/polyP=> i; rewrite coef_add_poly coefC if_same add0r. +Qed. + +Fact add_polyN : left_inverse 0%:P opp_poly add_poly. +Proof. +move=> p; apply/polyP=> i. +by rewrite coef_add_poly coef_opp_poly coefC if_same addNr. +Qed. + +Definition poly_zmodMixin := + ZmodMixin add_polyA add_polyC add_poly0 add_polyN. + +Canonical poly_zmodType := Eval hnf in ZmodType {poly R} poly_zmodMixin. +Canonical polynomial_zmodType := + Eval hnf in ZmodType (polynomial R) poly_zmodMixin. + +(* Properties of the zero polynomial *) +Lemma polyC0 : 0%:P = 0 :> {poly R}. Proof. by []. Qed. + +Lemma polyseq0 : (0 : {poly R}) = [::] :> seq R. +Proof. by rewrite polyseqC eqxx. Qed. + +Lemma size_poly0 : size (0 : {poly R}) = 0%N. +Proof. by rewrite polyseq0. Qed. + +Lemma coef0 i : (0 : {poly R})`_i = 0. +Proof. by rewrite coefC if_same. Qed. + +Lemma lead_coef0 : lead_coef 0 = 0 :> R. Proof. exact: lead_coefC. Qed. + +Lemma size_poly_eq0 p : (size p == 0%N) = (p == 0). +Proof. by rewrite size_eq0 -polyseq0. Qed. + +Lemma size_poly_leq0 p : (size p <= 0) = (p == 0). +Proof. by rewrite leqn0 size_poly_eq0. Qed. + +Lemma size_poly_leq0P p : reflect (p = 0) (size p <= 0%N). +Proof. by apply: (iffP idP); rewrite size_poly_leq0; move/eqP. Qed. + +Lemma size_poly_gt0 p : (0 < size p) = (p != 0). +Proof. by rewrite lt0n size_poly_eq0. Qed. + +Lemma nil_poly p : nilp p = (p == 0). +Proof. exact: size_poly_eq0. Qed. + +Lemma poly0Vpos p : {p = 0} + {size p > 0}. +Proof. by rewrite lt0n size_poly_eq0; exact: eqVneq. Qed. + +Lemma polySpred p : p != 0 -> size p = (size p).-1.+1. +Proof. by rewrite -size_poly_eq0 -lt0n => /prednK. Qed. + +Lemma lead_coef_eq0 p : (lead_coef p == 0) = (p == 0). +Proof. +rewrite -nil_poly /lead_coef nth_last. +by case: p => [[|x s] /= /negbTE // _]; rewrite eqxx. +Qed. + +Lemma polyC_eq0 (c : R) : (c%:P == 0) = (c == 0). +Proof. by rewrite -nil_poly polyseqC; case: (c == 0). Qed. + +Lemma size_poly1P p : reflect (exists2 c, c != 0 & p = c%:P) (size p == 1%N). +Proof. +apply: (iffP eqP) => [pC | [c nz_c ->]]; last by rewrite size_polyC nz_c. +have def_p: p = (p`_0)%:P by rewrite -size1_polyC ?pC. +by exists p`_0; rewrite // -polyC_eq0 -def_p -size_poly_eq0 pC. +Qed. + +Lemma leq_sizeP p i : reflect (forall j, i <= j -> p`_j = 0) (size p <= i). +Proof. +apply: (iffP idP) => [hp j hij| hp]. + by apply: nth_default; apply: leq_trans hij. +case p0: (p == 0); first by rewrite (eqP p0) size_poly0. +move: (lead_coef_eq0 p); rewrite p0 leqNgt; move/negbT; apply: contra => hs. +by apply/eqP; apply: hp; rewrite -ltnS (ltn_predK hs). +Qed. + +(* Size, leading coef, morphism properties of coef *) + +Lemma coefD p q i : (p + q)`_i = p`_i + q`_i. +Proof. exact: coef_add_poly. Qed. + +Lemma coefN p i : (- p)`_i = - p`_i. +Proof. exact: coef_opp_poly. Qed. + +Lemma coefB p q i : (p - q)`_i = p`_i - q`_i. +Proof. by rewrite coefD coefN. Qed. + +Canonical coefp_additive i := + Additive ((fun p => (coefB p)^~ i) : additive (coefp i)). + +Lemma coefMn p n i : (p *+ n)`_i = p`_i *+ n. +Proof. exact: (raddfMn (coefp_additive i)). Qed. + +Lemma coefMNn p n i : (p *- n)`_i = p`_i *- n. +Proof. by rewrite coefN coefMn. Qed. + +Lemma coef_sum I (r : seq I) (P : pred I) (F : I -> {poly R}) k : + (\sum_(i <- r | P i) F i)`_k = \sum_(i <- r | P i) (F i)`_k. +Proof. exact: (raddf_sum (coefp_additive k)). Qed. + +Lemma polyC_add : {morph polyC : a b / a + b}. +Proof. by move=> a b; apply/polyP=> [[|i]]; rewrite coefD !coefC ?addr0. Qed. + +Lemma polyC_opp : {morph polyC : c / - c}. +Proof. by move=> c; apply/polyP=> [[|i]]; rewrite coefN !coefC ?oppr0. Qed. + +Lemma polyC_sub : {morph polyC : a b / a - b}. +Proof. by move=> a b; rewrite polyC_add polyC_opp. Qed. + +Canonical polyC_additive := Additive polyC_sub. + +Lemma polyC_muln n : {morph polyC : c / c *+ n}. +Proof. exact: raddfMn. Qed. + +Lemma size_opp p : size (- p) = size p. +Proof. +by apply/eqP; rewrite eqn_leq -{3}(opprK p) -[-%R]/opp_poly unlock !size_poly. +Qed. + +Lemma lead_coef_opp p : lead_coef (- p) = - lead_coef p. +Proof. by rewrite /lead_coef size_opp coefN. Qed. + +Lemma size_add p q : size (p + q) <= maxn (size p) (size q). +Proof. by rewrite -[+%R]/add_poly unlock; apply: size_poly. Qed. + +Lemma size_addl p q : size p > size q -> size (p + q) = size p. +Proof. +move=> ltqp; rewrite -[+%R]/add_poly unlock size_poly_eq (maxn_idPl (ltnW _))//. +by rewrite addrC nth_default ?simp ?nth_last //; case: p ltqp => [[]]. +Qed. + +Lemma size_sum I (r : seq I) (P : pred I) (F : I -> {poly R}) : + size (\sum_(i <- r | P i) F i) <= \max_(i <- r | P i) size (F i). +Proof. +elim/big_rec2: _ => [|i p q _ IHp]; first by rewrite size_poly0. +by rewrite -(maxn_idPr IHp) maxnA leq_max size_add. +Qed. + +Lemma lead_coefDl p q : size p > size q -> lead_coef (p + q) = lead_coef p. +Proof. +move=> ltqp; rewrite /lead_coef coefD size_addl //. +by rewrite addrC nth_default ?simp // -ltnS (ltn_predK ltqp). +Qed. + +(* Polynomial ring structure. *) + +Definition mul_poly_def p q := + \poly_(i < (size p + size q).-1) (\sum_(j < i.+1) p`_j * q`_(i - j)). +Fact mul_poly_key : unit. Proof. by []. Qed. +Definition mul_poly := locked_with mul_poly_key mul_poly_def. +Canonical mul_poly_unlockable := [unlockable fun mul_poly]. + +Fact coef_mul_poly p q i : + (mul_poly p q)`_i = \sum_(j < i.+1) p`_j * q`_(i - j)%N. +Proof. +rewrite unlock coef_poly -subn1 ltn_subRL add1n; case: leqP => // le_pq_i1. +rewrite big1 // => j _; have [lq_q_ij | gt_q_ij] := leqP (size q) (i - j). + by rewrite [q`__]nth_default ?mulr0. +rewrite nth_default ?mul0r // -(leq_add2r (size q)) (leq_trans le_pq_i1) //. +by rewrite -leq_subLR -subnSK. +Qed. + +Fact coef_mul_poly_rev p q i : + (mul_poly p q)`_i = \sum_(j < i.+1) p`_(i - j)%N * q`_j. +Proof. +rewrite coef_mul_poly (reindex_inj rev_ord_inj) /=. +by apply: eq_bigr => j _; rewrite (sub_ordK j). +Qed. + +Fact mul_polyA : associative mul_poly. +Proof. +move=> p q r; apply/polyP=> i; rewrite coef_mul_poly coef_mul_poly_rev. +pose coef3 j k := p`_j * (q`_(i - j - k)%N * r`_k). +transitivity (\sum_(j < i.+1) \sum_(k < i.+1 | k <= i - j) coef3 j k). + apply: eq_bigr => /= j _; rewrite coef_mul_poly_rev big_distrr /=. + by rewrite (big_ord_narrow_leq (leq_subr _ _)). +rewrite (exchange_big_dep predT) //=; apply: eq_bigr => k _. +transitivity (\sum_(j < i.+1 | j <= i - k) coef3 j k). + apply: eq_bigl => j; rewrite -ltnS -(ltnS j) -!subSn ?leq_ord //. + by rewrite -subn_gt0 -(subn_gt0 j) -!subnDA addnC. +rewrite (big_ord_narrow_leq (leq_subr _ _)) coef_mul_poly big_distrl /=. +by apply: eq_bigr => j _; rewrite /coef3 -!subnDA addnC mulrA. +Qed. + +Fact mul_1poly : left_id 1%:P mul_poly. +Proof. +move=> p; apply/polyP => i; rewrite coef_mul_poly big_ord_recl subn0. +by rewrite big1 => [|j _]; rewrite coefC !simp. +Qed. + +Fact mul_poly1 : right_id 1%:P mul_poly. +Proof. +move=> p; apply/polyP => i; rewrite coef_mul_poly_rev big_ord_recl subn0. +by rewrite big1 => [|j _]; rewrite coefC !simp. +Qed. + +Fact mul_polyDl : left_distributive mul_poly +%R. +Proof. +move=> p q r; apply/polyP=> i; rewrite coefD !coef_mul_poly -big_split. +by apply: eq_bigr => j _; rewrite coefD mulrDl. +Qed. + +Fact mul_polyDr : right_distributive mul_poly +%R. +Proof. +move=> p q r; apply/polyP=> i; rewrite coefD !coef_mul_poly -big_split. +by apply: eq_bigr => j _; rewrite coefD mulrDr. +Qed. + +Fact poly1_neq0 : 1%:P != 0 :> {poly R}. +Proof. by rewrite polyC_eq0 oner_neq0. Qed. + +Definition poly_ringMixin := + RingMixin mul_polyA mul_1poly mul_poly1 mul_polyDl mul_polyDr poly1_neq0. + +Canonical poly_ringType := Eval hnf in RingType {poly R} poly_ringMixin. +Canonical polynomial_ringType := + Eval hnf in RingType (polynomial R) poly_ringMixin. + +Lemma polyC1 : 1%:P = 1 :> {poly R}. Proof. by []. Qed. + +Lemma polyseq1 : (1 : {poly R}) = [:: 1] :> seq R. +Proof. by rewrite polyseqC oner_neq0. Qed. + +Lemma size_poly1 : size (1 : {poly R}) = 1%N. +Proof. by rewrite polyseq1. Qed. + +Lemma coef1 i : (1 : {poly R})`_i = (i == 0%N)%:R. +Proof. by case: i => [|i]; rewrite polyseq1 /= ?nth_nil. Qed. + +Lemma lead_coef1 : lead_coef 1 = 1 :> R. Proof. exact: lead_coefC. Qed. + +Lemma coefM p q i : (p * q)`_i = \sum_(j < i.+1) p`_j * q`_(i - j)%N. +Proof. exact: coef_mul_poly. Qed. + +Lemma coefMr p q i : (p * q)`_i = \sum_(j < i.+1) p`_(i - j)%N * q`_j. +Proof. exact: coef_mul_poly_rev. Qed. + +Lemma size_mul_leq p q : size (p * q) <= (size p + size q).-1. +Proof. by rewrite -[*%R]/mul_poly unlock size_poly. Qed. + +Lemma mul_lead_coef p q : + lead_coef p * lead_coef q = (p * q)`_(size p + size q).-2. +Proof. +pose dp := (size p).-1; pose dq := (size q).-1. +have [-> | nz_p] := eqVneq p 0; first by rewrite lead_coef0 !mul0r coef0. +have [-> | nz_q] := eqVneq q 0; first by rewrite lead_coef0 !mulr0 coef0. +have ->: (size p + size q).-2 = (dp + dq)%N. + by do 2! rewrite polySpred // addSn addnC. +have lt_p_pq: dp < (dp + dq).+1 by rewrite ltnS leq_addr. +rewrite coefM (bigD1 (Ordinal lt_p_pq)) ?big1 ?simp ?addKn //= => i. +rewrite -val_eqE neq_ltn /= => /orP[lt_i_p | gt_i_p]; last first. + by rewrite nth_default ?mul0r //; rewrite -polySpred in gt_i_p. +rewrite [q`__]nth_default ?mulr0 //= -subSS -{1}addnS -polySpred //. +by rewrite addnC -addnBA ?leq_addr. +Qed. + +Lemma size_proper_mul p q : + lead_coef p * lead_coef q != 0 -> size (p * q) = (size p + size q).-1. +Proof. +apply: contraNeq; rewrite mul_lead_coef eqn_leq size_mul_leq -ltnNge => lt_pq. +by rewrite nth_default // -subn1 -(leq_add2l 1) -leq_subLR leq_sub2r. +Qed. + +Lemma lead_coef_proper_mul p q : + let c := lead_coef p * lead_coef q in c != 0 -> lead_coef (p * q) = c. +Proof. by move=> /= nz_c; rewrite mul_lead_coef -size_proper_mul. Qed. + +Lemma size_prod_leq (I : finType) (P : pred I) (F : I -> {poly R}) : + size (\prod_(i | P i) F i) <= (\sum_(i | P i) size (F i)).+1 - #|P|. +Proof. +rewrite -sum1_card. +elim/big_rec3: _ => [|i n m p _ IHp]; first by rewrite size_poly1. +have [-> | nz_p] := eqVneq p 0; first by rewrite mulr0 size_poly0. +rewrite (leq_trans (size_mul_leq _ _)) // subnS -!subn1 leq_sub2r //. +rewrite -addnS -addnBA ?leq_add2l // ltnW // -subn_gt0 (leq_trans _ IHp) //. +by rewrite polySpred. +Qed. + +Lemma coefCM c p i : (c%:P * p)`_i = c * p`_i. +Proof. +rewrite coefM big_ord_recl subn0. +by rewrite big1 => [|j _]; rewrite coefC !simp. +Qed. + +Lemma coefMC c p i : (p * c%:P)`_i = p`_i * c. +Proof. +rewrite coefMr big_ord_recl subn0. +by rewrite big1 => [|j _]; rewrite coefC !simp. +Qed. + +Lemma polyC_mul : {morph polyC : a b / a * b}. +Proof. by move=> a b; apply/polyP=> [[|i]]; rewrite coefCM !coefC ?simp. Qed. + +Fact polyC_multiplicative : multiplicative polyC. +Proof. by split; first exact: polyC_mul. Qed. +Canonical polyC_rmorphism := AddRMorphism polyC_multiplicative. + +Lemma polyC_exp n : {morph polyC : c / c ^+ n}. +Proof. exact: rmorphX. Qed. + +Lemma size_exp_leq p n : size (p ^+ n) <= ((size p).-1 * n).+1. +Proof. +elim: n => [|n IHn]; first by rewrite size_poly1. +have [-> | nzp] := poly0Vpos p; first by rewrite exprS mul0r size_poly0. +rewrite exprS (leq_trans (size_mul_leq _ _)) //. +by rewrite -{1}(prednK nzp) mulnS -addnS leq_add2l. +Qed. + +Lemma size_Msign p n : size ((-1) ^+ n * p) = size p. +Proof. +by rewrite -signr_odd; case: (odd n); rewrite ?mul1r // mulN1r size_opp. +Qed. + +Fact coefp0_multiplicative : multiplicative (coefp 0 : {poly R} -> R). +Proof. +split=> [p q|]; last by rewrite polyCK. +by rewrite [coefp 0 _]coefM big_ord_recl big_ord0 addr0. +Qed. + +Canonical coefp0_rmorphism := AddRMorphism coefp0_multiplicative. + +(* Algebra structure of polynomials. *) +Definition scale_poly_def a (p : {poly R}) := \poly_(i < size p) (a * p`_i). +Fact scale_poly_key : unit. Proof. by []. Qed. +Definition scale_poly := locked_with scale_poly_key scale_poly_def. +Canonical scale_poly_unlockable := [unlockable fun scale_poly]. + +Fact scale_polyE a p : scale_poly a p = a%:P * p. +Proof. +apply/polyP=> n; rewrite unlock coef_poly coefCM. +by case: leqP => // le_p_n; rewrite nth_default ?mulr0. +Qed. + +Fact scale_polyA a b p : scale_poly a (scale_poly b p) = scale_poly (a * b) p. +Proof. by rewrite !scale_polyE mulrA polyC_mul. Qed. + +Fact scale_1poly : left_id 1 scale_poly. +Proof. by move=> p; rewrite scale_polyE mul1r. Qed. + +Fact scale_polyDr a : {morph scale_poly a : p q / p + q}. +Proof. by move=> p q; rewrite !scale_polyE mulrDr. Qed. + +Fact scale_polyDl p : {morph scale_poly^~ p : a b / a + b}. +Proof. by move=> a b /=; rewrite !scale_polyE raddfD mulrDl. Qed. + +Fact scale_polyAl a p q : scale_poly a (p * q) = scale_poly a p * q. +Proof. by rewrite !scale_polyE mulrA. Qed. + +Definition poly_lmodMixin := + LmodMixin scale_polyA scale_1poly scale_polyDr scale_polyDl. + +Canonical poly_lmodType := + Eval hnf in LmodType R {poly R} poly_lmodMixin. +Canonical polynomial_lmodType := + Eval hnf in LmodType R (polynomial R) poly_lmodMixin. +Canonical poly_lalgType := + Eval hnf in LalgType R {poly R} scale_polyAl. +Canonical polynomial_lalgType := + Eval hnf in LalgType R (polynomial R) scale_polyAl. + +Lemma mul_polyC a p : a%:P * p = a *: p. +Proof. by rewrite -scale_polyE. Qed. + +Lemma alg_polyC a : a%:A = a%:P :> {poly R}. +Proof. by rewrite -mul_polyC mulr1. Qed. + +Lemma coefZ a p i : (a *: p)`_i = a * p`_i. +Proof. +rewrite -[*:%R]/scale_poly unlock coef_poly. +by case: leqP => // le_p_n; rewrite nth_default ?mulr0. +Qed. + +Lemma size_scale_leq a p : size (a *: p) <= size p. +Proof. by rewrite -[*:%R]/scale_poly unlock size_poly. Qed. + +Canonical coefp_linear i : {scalar {poly R}} := + AddLinear ((fun a => (coefZ a) ^~ i) : scalable_for *%R (coefp i)). +Canonical coefp0_lrmorphism := [lrmorphism of coefp 0]. + +(* The indeterminate, at last! *) +Definition polyX_def := Poly [:: 0; 1]. +Fact polyX_key : unit. Proof. by []. Qed. +Definition polyX : {poly R} := locked_with polyX_key polyX_def. +Canonical polyX_unlockable := [unlockable of polyX]. +Local Notation "'X" := polyX. + +Lemma polyseqX : 'X = [:: 0; 1] :> seq R. +Proof. by rewrite unlock !polyseq_cons nil_poly eqxx /= polyseq1. Qed. + +Lemma size_polyX : size 'X = 2. Proof. by rewrite polyseqX. Qed. + +Lemma polyX_eq0 : ('X == 0) = false. +Proof. by rewrite -size_poly_eq0 size_polyX. Qed. + +Lemma coefX i : 'X`_i = (i == 1%N)%:R. +Proof. by case: i => [|[|i]]; rewrite polyseqX //= nth_nil. Qed. + +Lemma lead_coefX : lead_coef 'X = 1. +Proof. by rewrite /lead_coef polyseqX. Qed. + +Lemma commr_polyX p : GRing.comm p 'X. +Proof. +apply/polyP=> i; rewrite coefMr coefM. +by apply: eq_bigr => j _; rewrite coefX commr_nat. +Qed. + +Lemma coefMX p i : (p * 'X)`_i = (if (i == 0)%N then 0 else p`_i.-1). +Proof. +rewrite coefMr big_ord_recl coefX ?simp. +case: i => [|i]; rewrite ?big_ord0 //= big_ord_recl polyseqX subn1 /=. +by rewrite big1 ?simp // => j _; rewrite nth_nil !simp. +Qed. + +Lemma coefXM p i : ('X * p)`_i = (if (i == 0)%N then 0 else p`_i.-1). +Proof. by rewrite -commr_polyX coefMX. Qed. + +Lemma cons_poly_def p a : cons_poly a p = p * 'X + a%:P. +Proof. +apply/polyP=> i; rewrite coef_cons coefD coefMX coefC. +by case: ifP; rewrite !simp. +Qed. + +Lemma poly_ind (K : {poly R} -> Type) : + K 0 -> (forall p c, K p -> K (p * 'X + c%:P)) -> (forall p, K p). +Proof. +move=> K0 Kcons p; rewrite -[p]polyseqK. +elim: {p}(p : seq R) => //= p c IHp; rewrite cons_poly_def; exact: Kcons. +Qed. + +Lemma polyseqXsubC a : 'X - a%:P = [:: - a; 1] :> seq R. +Proof. +by rewrite -['X]mul1r -polyC_opp -cons_poly_def polyseq_cons polyseq1. +Qed. + +Lemma size_XsubC a : size ('X - a%:P) = 2%N. +Proof. by rewrite polyseqXsubC. Qed. + +Lemma size_XaddC b : size ('X + b%:P) = 2. +Proof. by rewrite -[b]opprK rmorphN size_XsubC. Qed. + +Lemma lead_coefXsubC a : lead_coef ('X - a%:P) = 1. +Proof. by rewrite lead_coefE polyseqXsubC. Qed. + +Lemma polyXsubC_eq0 a : ('X - a%:P == 0) = false. +Proof. by rewrite -nil_poly polyseqXsubC. Qed. + +Lemma size_MXaddC p c : + size (p * 'X + c%:P) = (if (p == 0) && (c == 0) then 0%N else (size p).+1). +Proof. by rewrite -cons_poly_def size_cons_poly nil_poly. Qed. + +Lemma polyseqMX p : p != 0 -> p * 'X = 0 :: p :> seq R. +Proof. +by move=> nz_p; rewrite -[p * _]addr0 -cons_poly_def polyseq_cons nil_poly nz_p. +Qed. + +Lemma size_mulX p : p != 0 -> size (p * 'X) = (size p).+1. +Proof. by move/polyseqMX->. Qed. + +Lemma lead_coefMX p : lead_coef (p * 'X) = lead_coef p. +Proof. +have [-> | nzp] := eqVneq p 0; first by rewrite mul0r. +by rewrite /lead_coef !nth_last polyseqMX. +Qed. + +Lemma size_XmulC a : a != 0 -> size ('X * a%:P) = 2. +Proof. +by move=> nz_a; rewrite -commr_polyX size_mulX ?polyC_eq0 ?size_polyC nz_a. +Qed. + +Local Notation "''X^' n" := ('X ^+ n). + +Lemma coefXn n i : 'X^n`_i = (i == n)%:R. +Proof. +by elim: n i => [|n IHn] [|i]; rewrite ?coef1 // exprS coefXM ?IHn. +Qed. + +Lemma polyseqXn n : 'X^n = rcons (nseq n 0) 1 :> seq R. +Proof. +elim: n => [|n IHn]; rewrite ?polyseq1 // exprSr. +by rewrite polyseqMX -?size_poly_eq0 IHn ?size_rcons. +Qed. + +Lemma size_polyXn n : size 'X^n = n.+1. +Proof. by rewrite polyseqXn size_rcons size_nseq. Qed. + +Lemma commr_polyXn p n : GRing.comm p 'X^n. +Proof. by apply: commrX; exact: commr_polyX. Qed. + +Lemma lead_coefXn n : lead_coef 'X^n = 1. +Proof. by rewrite /lead_coef nth_last polyseqXn last_rcons. Qed. + +Lemma polyseqMXn n p : p != 0 -> p * 'X^n = ncons n 0 p :> seq R. +Proof. +case: n => [|n] nz_p; first by rewrite mulr1. +elim: n => [|n IHn]; first exact: polyseqMX. +by rewrite exprSr mulrA polyseqMX -?nil_poly IHn. +Qed. + +Lemma coefMXn n p i : (p * 'X^n)`_i = if i < n then 0 else p`_(i - n). +Proof. +have [-> | /polyseqMXn->] := eqVneq p 0; last exact: nth_ncons. +by rewrite mul0r !coef0 if_same. +Qed. + +Lemma coefXnM n p i : ('X^n * p)`_i = if i < n then 0 else p`_(i - n). +Proof. by rewrite -commr_polyXn coefMXn. Qed. + +(* Expansion of a polynomial as an indexed sum *) +Lemma poly_def n E : \poly_(i < n) E i = \sum_(i < n) E i *: 'X^i. +Proof. +rewrite unlock; elim: n => [|n IHn] in E *; first by rewrite big_ord0. +rewrite big_ord_recl /= cons_poly_def addrC expr0 alg_polyC. +congr (_ + _); rewrite (iota_addl 1 0) -map_comp IHn big_distrl /=. +by apply: eq_bigr => i _; rewrite -scalerAl exprSr. +Qed. + +(* Monic predicate *) +Definition monic := [qualify p | lead_coef p == 1]. +Fact monic_key : pred_key monic. Proof. by []. Qed. +Canonical monic_keyed := KeyedQualifier monic_key. + +Lemma monicE p : (p \is monic) = (lead_coef p == 1). Proof. by []. Qed. +Lemma monicP p : reflect (lead_coef p = 1) (p \is monic). +Proof. exact: eqP. Qed. + +Lemma monic1 : 1 \is monic. Proof. exact/eqP/lead_coef1. Qed. +Lemma monicX : 'X \is monic. Proof. exact/eqP/lead_coefX. Qed. +Lemma monicXn n : 'X^n \is monic. Proof. exact/eqP/lead_coefXn. Qed. + +Lemma monic_neq0 p : p \is monic -> p != 0. +Proof. by rewrite -lead_coef_eq0 => /eqP->; exact: oner_neq0. Qed. + +Lemma lead_coef_monicM p q : p \is monic -> lead_coef (p * q) = lead_coef q. +Proof. +have [-> | nz_q] := eqVneq q 0; first by rewrite mulr0. +by move/monicP=> mon_p; rewrite lead_coef_proper_mul mon_p mul1r ?lead_coef_eq0. +Qed. + +Lemma lead_coef_Mmonic p q : q \is monic -> lead_coef (p * q) = lead_coef p. +Proof. +have [-> | nz_p] := eqVneq p 0; first by rewrite mul0r. +by move/monicP=> mon_q; rewrite lead_coef_proper_mul mon_q mulr1 ?lead_coef_eq0. +Qed. + +Lemma size_monicM p q : + p \is monic -> q != 0 -> size (p * q) = (size p + size q).-1. +Proof. +move/monicP=> mon_p nz_q. +by rewrite size_proper_mul // mon_p mul1r lead_coef_eq0. +Qed. + +Lemma size_Mmonic p q : + p != 0 -> q \is monic -> size (p * q) = (size p + size q).-1. +Proof. +move=> nz_p /monicP mon_q. +by rewrite size_proper_mul // mon_q mulr1 lead_coef_eq0. +Qed. + +Lemma monicMl p q : p \is monic -> (p * q \is monic) = (q \is monic). +Proof. by move=> mon_p; rewrite !monicE lead_coef_monicM. Qed. + +Lemma monicMr p q : q \is monic -> (p * q \is monic) = (p \is monic). +Proof. by move=> mon_q; rewrite !monicE lead_coef_Mmonic. Qed. + +Fact monic_mulr_closed : mulr_closed monic. +Proof. by split=> [|p q mon_p]; rewrite (monic1, monicMl). Qed. +Canonical monic_mulrPred := MulrPred monic_mulr_closed. + +Lemma monic_exp p n : p \is monic -> p ^+ n \is monic. +Proof. exact: rpredX. Qed. + +Lemma monic_prod I rI (P : pred I) (F : I -> {poly R}): + (forall i, P i -> F i \is monic) -> \prod_(i <- rI | P i) F i \is monic. +Proof. exact: rpred_prod. Qed. + +Lemma monicXsubC c : 'X - c%:P \is monic. +Proof. exact/eqP/lead_coefXsubC. Qed. + +Lemma monic_prod_XsubC I rI (P : pred I) (F : I -> R) : + \prod_(i <- rI | P i) ('X - (F i)%:P) \is monic. +Proof. by apply: monic_prod => i _; exact: monicXsubC. Qed. + +Lemma size_prod_XsubC I rI (F : I -> R) : + size (\prod_(i <- rI) ('X - (F i)%:P)) = (size rI).+1. +Proof. +elim: rI => [|i r /= <-]; rewrite ?big_nil ?size_poly1 // big_cons. +rewrite size_monicM ?monicXsubC ?monic_neq0 ?monic_prod_XsubC //. +by rewrite size_XsubC. +Qed. + +Lemma size_exp_XsubC n a : size (('X - a%:P) ^+ n) = n.+1. +Proof. by rewrite -[n]card_ord -prodr_const size_prod_XsubC cardE enumT. Qed. + +(* Some facts about regular elements. *) + +Lemma lreg_lead p : GRing.lreg (lead_coef p) -> GRing.lreg p. +Proof. +move/mulrI_eq0=> reg_p; apply: mulrI0_lreg => q /eqP; apply: contraTeq => nz_q. +by rewrite -lead_coef_eq0 lead_coef_proper_mul reg_p lead_coef_eq0. +Qed. + +Lemma rreg_lead p : GRing.rreg (lead_coef p) -> GRing.rreg p. +Proof. +move/mulIr_eq0=> reg_p; apply: mulIr0_rreg => q /eqP; apply: contraTeq => nz_q. +by rewrite -lead_coef_eq0 lead_coef_proper_mul reg_p lead_coef_eq0. +Qed. + +Lemma lreg_lead0 p : GRing.lreg (lead_coef p) -> p != 0. +Proof. by move/lreg_neq0; rewrite lead_coef_eq0. Qed. + +Lemma rreg_lead0 p : GRing.rreg (lead_coef p) -> p != 0. +Proof. by move/rreg_neq0; rewrite lead_coef_eq0. Qed. + +Lemma lreg_size c p : GRing.lreg c -> size (c *: p) = size p. +Proof. +move=> reg_c; have [-> | nz_p] := eqVneq p 0; first by rewrite scaler0. +rewrite -mul_polyC size_proper_mul; first by rewrite size_polyC lreg_neq0. +by rewrite lead_coefC mulrI_eq0 ?lead_coef_eq0. +Qed. + +Lemma lreg_polyZ_eq0 c p : GRing.lreg c -> (c *: p == 0) = (p == 0). +Proof. by rewrite -!size_poly_eq0 => /lreg_size->. Qed. + +Lemma lead_coef_lreg c p : + GRing.lreg c -> lead_coef (c *: p) = c * lead_coef p. +Proof. by move=> reg_c; rewrite !lead_coefE coefZ lreg_size. Qed. + +Lemma rreg_size c p : GRing.rreg c -> size (p * c%:P) = size p. +Proof. +move=> reg_c; have [-> | nz_p] := eqVneq p 0; first by rewrite mul0r. +rewrite size_proper_mul; first by rewrite size_polyC rreg_neq0 ?addn1. +by rewrite lead_coefC mulIr_eq0 ?lead_coef_eq0. +Qed. + +Lemma rreg_polyMC_eq0 c p : GRing.rreg c -> (p * c%:P == 0) = (p == 0). +Proof. by rewrite -!size_poly_eq0 => /rreg_size->. Qed. + +Lemma rreg_div0 q r d : + GRing.rreg (lead_coef d) -> size r < size d -> + (q * d + r == 0) = (q == 0) && (r == 0). +Proof. +move=> reg_d lt_r_d; rewrite addrC addr_eq0. +have [-> | nz_q] := altP (q =P 0); first by rewrite mul0r oppr0. +apply: contraTF lt_r_d => /eqP->; rewrite -leqNgt size_opp. +rewrite size_proper_mul ?mulIr_eq0 ?lead_coef_eq0 //. +by rewrite (polySpred nz_q) leq_addl. +Qed. + +Lemma monic_comreg p : + p \is monic -> GRing.comm p (lead_coef p)%:P /\ GRing.rreg (lead_coef p). +Proof. by move/monicP->; split; [exact: commr1 | exact: rreg1]. Qed. + +(* Horner evaluation of polynomials *) +Implicit Types s rs : seq R. +Fixpoint horner_rec s x := if s is a :: s' then horner_rec s' x * x + a else 0. +Definition horner p := horner_rec p. + +Local Notation "p .[ x ]" := (horner p x) : ring_scope. + +Lemma horner0 x : (0 : {poly R}).[x] = 0. +Proof. by rewrite /horner polyseq0. Qed. + +Lemma hornerC c x : (c%:P).[x] = c. +Proof. by rewrite /horner polyseqC; case: eqP; rewrite /= ?simp. Qed. + +Lemma hornerX x : 'X.[x] = x. +Proof. by rewrite /horner polyseqX /= !simp. Qed. + +Lemma horner_cons p c x : (cons_poly c p).[x] = p.[x] * x + c. +Proof. +rewrite /horner polyseq_cons; case: nilP => //= ->. +by rewrite !simp -/(_.[x]) hornerC. +Qed. + +Lemma horner_coef0 p : p.[0] = p`_0. +Proof. by rewrite /horner; case: (p : seq R) => //= c p'; rewrite !simp. Qed. + +Lemma hornerMXaddC p c x : (p * 'X + c%:P).[x] = p.[x] * x + c. +Proof. by rewrite -cons_poly_def horner_cons. Qed. + +Lemma hornerMX p x : (p * 'X).[x] = p.[x] * x. +Proof. by rewrite -[p * 'X]addr0 hornerMXaddC addr0. Qed. + +Lemma horner_Poly s x : (Poly s).[x] = horner_rec s x. +Proof. by elim: s => [|a s /= <-]; rewrite (horner0, horner_cons). Qed. + +Lemma horner_coef p x : p.[x] = \sum_(i < size p) p`_i * x ^+ i. +Proof. +rewrite /horner. +elim: {p}(p : seq R) => /= [|a s ->]; first by rewrite big_ord0. +rewrite big_ord_recl simp addrC big_distrl /=. +by congr (_ + _); apply: eq_bigr => i _; rewrite -mulrA exprSr. +Qed. + +Lemma horner_coef_wide n p x : + size p <= n -> p.[x] = \sum_(i < n) p`_i * x ^+ i. +Proof. +move=> le_p_n. +rewrite horner_coef (big_ord_widen n (fun i => p`_i * x ^+ i)) // big_mkcond. +by apply: eq_bigr => i _; case: ltnP => // le_p_i; rewrite nth_default ?simp. +Qed. + +Lemma horner_poly n E x : (\poly_(i < n) E i).[x] = \sum_(i < n) E i * x ^+ i. +Proof. +rewrite (@horner_coef_wide n) ?size_poly //. +by apply: eq_bigr => i _; rewrite coef_poly ltn_ord. +Qed. + +Lemma hornerN p x : (- p).[x] = - p.[x]. +Proof. +rewrite -[-%R]/opp_poly unlock horner_poly horner_coef -sumrN /=. +by apply: eq_bigr => i _; rewrite mulNr. +Qed. + +Lemma hornerD p q x : (p + q).[x] = p.[x] + q.[x]. +Proof. +rewrite -[+%R]/add_poly unlock horner_poly; set m := maxn _ _. +rewrite !(@horner_coef_wide m) ?leq_max ?leqnn ?orbT // -big_split /=. +by apply: eq_bigr => i _; rewrite -mulrDl. +Qed. + +Lemma hornerXsubC a x : ('X - a%:P).[x] = x - a. +Proof. by rewrite hornerD hornerN hornerC hornerX. Qed. + +Lemma horner_sum I (r : seq I) (P : pred I) F x : + (\sum_(i <- r | P i) F i).[x] = \sum_(i <- r | P i) (F i).[x]. +Proof. by elim/big_rec2: _ => [|i _ p _ <-]; rewrite (horner0, hornerD). Qed. + +Lemma hornerCM a p x : (a%:P * p).[x] = a * p.[x]. +Proof. +elim/poly_ind: p => [|p c IHp]; first by rewrite !(mulr0, horner0). +by rewrite mulrDr mulrA -polyC_mul !hornerMXaddC IHp mulrDr mulrA. +Qed. + +Lemma hornerZ c p x : (c *: p).[x] = c * p.[x]. +Proof. by rewrite -mul_polyC hornerCM. Qed. + +Lemma hornerMn n p x : (p *+ n).[x] = p.[x] *+ n. +Proof. by elim: n => [| n IHn]; rewrite ?horner0 // !mulrS hornerD IHn. Qed. + +Definition comm_coef p x := forall i, p`_i * x = x * p`_i. + +Definition comm_poly p x := x * p.[x] = p.[x] * x. + +Lemma comm_coef_poly p x : comm_coef p x -> comm_poly p x. +Proof. +move=> cpx; rewrite /comm_poly !horner_coef big_distrl big_distrr /=. +by apply: eq_bigr => i _; rewrite /= mulrA -cpx -!mulrA commrX. +Qed. + +Lemma comm_poly0 x : comm_poly 0 x. +Proof. by rewrite /comm_poly !horner0 !simp. Qed. + +Lemma comm_poly1 x : comm_poly 1 x. +Proof. by rewrite /comm_poly !hornerC !simp. Qed. + +Lemma comm_polyX x : comm_poly 'X x. +Proof. by rewrite /comm_poly !hornerX. Qed. + +Lemma hornerM_comm p q x : comm_poly q x -> (p * q).[x] = p.[x] * q.[x]. +Proof. +move=> comm_qx. +elim/poly_ind: p => [|p c IHp]; first by rewrite !(simp, horner0). +rewrite mulrDl hornerD hornerCM -mulrA -commr_polyX mulrA hornerMX. +by rewrite {}IHp -mulrA -comm_qx mulrA -mulrDl hornerMXaddC. +Qed. + +Lemma horner_exp_comm p x n : comm_poly p x -> (p ^+ n).[x] = p.[x] ^+ n. +Proof. +move=> comm_px; elim: n => [|n IHn]; first by rewrite hornerC. +by rewrite !exprSr -IHn hornerM_comm. +Qed. + +Lemma hornerXn x n : ('X^n).[x] = x ^+ n. +Proof. by rewrite horner_exp_comm /comm_poly hornerX. Qed. + +Definition hornerE_comm := + (hornerD, hornerN, hornerX, hornerC, horner_cons, + simp, hornerCM, hornerZ, + (fun p x => hornerM_comm p (comm_polyX x))). + +Definition root p : pred R := fun x => p.[x] == 0. + +Lemma mem_root p x : x \in root p = (p.[x] == 0). +Proof. by []. Qed. + +Lemma rootE p x : (root p x = (p.[x] == 0)) * ((x \in root p) = (p.[x] == 0)). +Proof. by []. Qed. + +Lemma rootP p x : reflect (p.[x] = 0) (root p x). +Proof. exact: eqP. Qed. + +Lemma rootPt p x : reflect (p.[x] == 0) (root p x). +Proof. exact: idP. Qed. + +Lemma rootPf p x : reflect ((p.[x] == 0) = false) (~~ root p x). +Proof. exact: negPf. Qed. + +Lemma rootC a x : root a%:P x = (a == 0). +Proof. by rewrite rootE hornerC. Qed. + +Lemma root0 x : root 0 x. +Proof. by rewrite rootC. Qed. + +Lemma root1 x : ~~ root 1 x. +Proof. by rewrite rootC oner_eq0. Qed. + +Lemma rootX x : root 'X x = (x == 0). +Proof. by rewrite rootE hornerX. Qed. + +Lemma rootN p x : root (- p) x = root p x. +Proof. by rewrite rootE hornerN oppr_eq0. Qed. + +Lemma root_size_gt1 a p : p != 0 -> root p a -> 1 < size p. +Proof. +rewrite ltnNge => nz_p; apply: contraL => /size1_polyC Dp. +by rewrite Dp rootC -polyC_eq0 -Dp. +Qed. + +Lemma root_XsubC a x : root ('X - a%:P) x = (x == a). +Proof. by rewrite rootE hornerXsubC subr_eq0. Qed. + +Lemma root_XaddC a x : root ('X + a%:P) x = (x == - a). +Proof. by rewrite -root_XsubC rmorphN opprK. Qed. + +Theorem factor_theorem p a : reflect (exists q, p = q * ('X - a%:P)) (root p a). +Proof. +apply: (iffP eqP) => [pa0 | [q ->]]; last first. + by rewrite hornerM_comm /comm_poly hornerXsubC subrr ?simp. +exists (\poly_(i < size p) horner_rec (drop i.+1 p) a). +apply/polyP=> i; rewrite mulrBr coefB coefMX coefMC !coef_poly. +apply: canRL (addrK _) _; rewrite addrC; have [le_p_i | lt_i_p] := leqP. + rewrite nth_default // !simp drop_oversize ?if_same //. + exact: leq_trans (leqSpred _). +case: i => [|i] in lt_i_p *; last by rewrite ltnW // (drop_nth 0 lt_i_p). +by rewrite drop1 /= -{}pa0 /horner; case: (p : seq R) lt_i_p. +Qed. + +Lemma multiplicity_XsubC p a : + {m | exists2 q, (p != 0) ==> ~~ root q a & p = q * ('X - a%:P) ^+ m}. +Proof. +elim: {p}(size p) {-2}p (eqxx (size p)) => [|n IHn] p. + by rewrite size_poly_eq0 => ->; exists 0%N, p; rewrite ?mulr1. +have [/sig_eqW[{p}p ->] sz_p | nz_pa] := altP (factor_theorem p a); last first. + by exists 0%N, p; rewrite ?mulr1 ?nz_pa ?implybT. +have nz_p: p != 0 by apply: contraTneq sz_p => ->; rewrite mul0r size_poly0. +rewrite size_Mmonic ?monicXsubC // size_XsubC addn2 eqSS in sz_p. +have [m /sig2_eqW[q nz_qa Dp]] := IHn p sz_p; rewrite nz_p /= in nz_qa. +by exists m.+1, q; rewrite ?nz_qa ?implybT // exprSr mulrA -Dp. +Qed. + +(* Roots of unity. *) + +Lemma size_Xn_sub_1 n : n > 0 -> size ('X^n - 1 : {poly R}) = n.+1. +Proof. +by move=> n_gt0; rewrite size_addl size_polyXn // size_opp size_poly1. +Qed. + +Lemma monic_Xn_sub_1 n : n > 0 -> 'X^n - 1 \is monic. +Proof. +move=> n_gt0; rewrite monicE lead_coefE size_Xn_sub_1 // coefB. +by rewrite coefXn coef1 eqxx eqn0Ngt n_gt0 subr0. +Qed. + +Definition root_of_unity n : pred R := root ('X^n - 1). +Local Notation "n .-unity_root" := (root_of_unity n) : ring_scope. + +Lemma unity_rootE n z : n.-unity_root z = (z ^+ n == 1). +Proof. +by rewrite /root_of_unity rootE hornerD hornerN hornerXn hornerC subr_eq0. +Qed. + +Lemma unity_rootP n z : reflect (z ^+ n = 1) (n.-unity_root z). +Proof. by rewrite unity_rootE; exact: eqP. Qed. + +Definition primitive_root_of_unity n z := + (n > 0) && [forall i : 'I_n, i.+1.-unity_root z == (i.+1 == n)]. +Local Notation "n .-primitive_root" := (primitive_root_of_unity n) : ring_scope. + +Lemma prim_order_exists n z : + n > 0 -> z ^+ n = 1 -> {m | m.-primitive_root z & (m %| n)}. +Proof. +move=> n_gt0 zn1. +have: exists m, (m > 0) && (z ^+ m == 1) by exists n; rewrite n_gt0 /= zn1. +case/ex_minnP=> m /andP[m_gt0 /eqP zm1] m_min. +exists m. + apply/andP; split=> //; apply/eqfunP=> [[i]] /=. + rewrite leq_eqVlt unity_rootE. + case: eqP => [-> _ | _]; first by rewrite zm1 eqxx. + by apply: contraTF => zi1; rewrite -leqNgt m_min. +have: n %% m < m by rewrite ltn_mod. +apply: contraLR; rewrite -lt0n -leqNgt => nm_gt0; apply: m_min. +by rewrite nm_gt0 /= expr_mod ?zn1. +Qed. + +Section OnePrimitive. + +Variables (n : nat) (z : R). +Hypothesis prim_z : n.-primitive_root z. + +Lemma prim_order_gt0 : n > 0. Proof. by case/andP: prim_z. Qed. +Let n_gt0 := prim_order_gt0. + +Lemma prim_expr_order : z ^+ n = 1. +Proof. +case/andP: prim_z => _; rewrite -(prednK n_gt0) => /forallP/(_ ord_max). +by rewrite unity_rootE eqxx eqb_id => /eqP. +Qed. + +Lemma prim_expr_mod i : z ^+ (i %% n) = z ^+ i. +Proof. exact: expr_mod prim_expr_order. Qed. + +Lemma prim_order_dvd i : (n %| i) = (z ^+ i == 1). +Proof. +move: n_gt0; rewrite -prim_expr_mod /dvdn -(ltn_mod i). +case: {i}(i %% n)%N => [|i] lt_i; first by rewrite !eqxx. +case/andP: prim_z => _ /forallP/(_ (Ordinal (ltnW lt_i))). +by move/eqP; rewrite unity_rootE eqn_leq andbC leqNgt lt_i. +Qed. + +Lemma eq_prim_root_expr i j : (z ^+ i == z ^+ j) = (i == j %[mod n]). +Proof. +wlog le_ji: i j / j <= i. + move=> IH; case: (leqP j i); last move/ltnW; move/IH=> //. + by rewrite eq_sym (eq_sym (j %% n)%N). +rewrite -{1}(subnKC le_ji) exprD -prim_expr_mod eqn_mod_dvd //. +rewrite prim_order_dvd; apply/eqP/eqP=> [|->]; last by rewrite mulr1. +move/(congr1 ( *%R (z ^+ (n - j %% n)))); rewrite mulrA -exprD. +by rewrite subnK ?prim_expr_order ?mul1r // ltnW ?ltn_mod. +Qed. + +Lemma exp_prim_root k : (n %/ gcdn k n).-primitive_root (z ^+ k). +Proof. +set d := gcdn k n; have d_gt0: (0 < d)%N by rewrite gcdn_gt0 orbC n_gt0. +have [d_dv_k d_dv_n]: (d %| k /\ d %| n)%N by rewrite dvdn_gcdl dvdn_gcdr. +set q := (n %/ d)%N; rewrite /q.-primitive_root ltn_divRL // n_gt0. +apply/forallP=> i; rewrite unity_rootE -exprM -prim_order_dvd. +rewrite -(divnK d_dv_n) -/q -(divnK d_dv_k) mulnAC dvdn_pmul2r //. +apply/eqP; apply/idP/idP=> [|/eqP->]; last by rewrite dvdn_mull. +rewrite Gauss_dvdr; first by rewrite eqn_leq ltn_ord; exact: dvdn_leq. +by rewrite /coprime gcdnC -(eqn_pmul2r d_gt0) mul1n muln_gcdl !divnK. +Qed. + +Lemma dvdn_prim_root m : (m %| n)%N -> m.-primitive_root (z ^+ (n %/ m)). +Proof. +set k := (n %/ m)%N => m_dv_n; rewrite -{1}(mulKn m n_gt0) -divnA // -/k. +by rewrite -{1}(@gcdn_idPl k n _) ?exp_prim_root // -(divnK m_dv_n) dvdn_mulr. +Qed. + +End OnePrimitive. + +Lemma prim_root_exp_coprime n z k : + n.-primitive_root z -> n.-primitive_root (z ^+ k) = coprime k n. +Proof. +move=> prim_z;have n_gt0 := prim_order_gt0 prim_z. +apply/idP/idP=> [prim_zk | co_k_n]. + set d := gcdn k n; have dv_d_n: (d %| n)%N := dvdn_gcdr _ _. + rewrite /coprime -/d -(eqn_pmul2r n_gt0) mul1n -{2}(gcdnMl n d). + rewrite -{2}(divnK dv_d_n) (mulnC _ d) -muln_gcdr (gcdn_idPr _) //. + rewrite (prim_order_dvd prim_zk) -exprM -(prim_order_dvd prim_z). + by rewrite muln_divCA_gcd dvdn_mulr. +have zkn_1: z ^+ k ^+ n = 1 by rewrite exprAC (prim_expr_order prim_z) expr1n. +have{zkn_1} [m prim_zk dv_m_n]:= prim_order_exists n_gt0 zkn_1. +suffices /eqP <-: m == n by []. +rewrite eqn_dvd dv_m_n -(@Gauss_dvdr n k m) 1?coprime_sym //=. +by rewrite (prim_order_dvd prim_z) exprM (prim_expr_order prim_zk). +Qed. + +(* Lifting a ring predicate to polynomials. *) + +Definition polyOver (S : pred_class) := + [qualify a p : {poly R} | all (mem S) p]. + +Fact polyOver_key S : pred_key (polyOver S). Proof. by []. Qed. +Canonical polyOver_keyed S := KeyedQualifier (polyOver_key S). + +Lemma polyOverS (S1 S2 : pred_class) : + {subset S1 <= S2} -> {subset polyOver S1 <= polyOver S2}. +Proof. +by move=> sS12 p /(all_nthP 0)S1p; apply/(all_nthP 0)=> i /S1p; apply: sS12. +Qed. + +Lemma polyOver0 S : 0 \is a polyOver S. +Proof. by rewrite qualifE polyseq0. Qed. + +Lemma polyOver_poly (S : pred_class) n E : + (forall i, i < n -> E i \in S) -> \poly_(i < n) E i \is a polyOver S. +Proof. +move=> S_E; apply/(all_nthP 0)=> i lt_i_p /=; rewrite coef_poly. +by case: ifP => [/S_E// | /idP[]]; apply: leq_trans lt_i_p (size_poly n E). +Qed. + +Section PolyOverAdd. + +Variables (S : predPredType R) (addS : addrPred S) (kS : keyed_pred addS). + +Lemma polyOverP {p} : reflect (forall i, p`_i \in kS) (p \in polyOver kS). +Proof. +apply: (iffP (all_nthP 0)) => [Sp i | Sp i _]; last exact: Sp. +by have [/Sp // | /(nth_default 0)->] := ltnP i (size p); apply: rpred0. +Qed. + +Lemma polyOverC c : (c%:P \in polyOver kS) = (c \in kS). +Proof. +by rewrite qualifE polyseqC; case: eqP => [->|] /=; rewrite ?andbT ?rpred0. +Qed. + +Fact polyOver_addr_closed : addr_closed (polyOver kS). +Proof. +split=> [|p q Sp Sq]; first exact: polyOver0. +by apply/polyOverP=> i; rewrite coefD rpredD ?(polyOverP _). +Qed. +Canonical polyOver_addrPred := AddrPred polyOver_addr_closed. + +End PolyOverAdd. + +Fact polyOverNr S (addS : zmodPred S) (kS : keyed_pred addS) : + oppr_closed (polyOver kS). +Proof. +by move=> p /polyOverP Sp; apply/polyOverP=> i; rewrite coefN rpredN. +Qed. +Canonical polyOver_opprPred S addS kS := OpprPred (@polyOverNr S addS kS). +Canonical polyOver_zmodPred S addS kS := ZmodPred (@polyOverNr S addS kS). + +Section PolyOverSemiring. + +Context (S : pred_class) (ringS : @semiringPred R S) (kS : keyed_pred ringS). + +Fact polyOver_mulr_closed : mulr_closed (polyOver kS). +Proof. +split=> [|p q /polyOverP Sp /polyOverP Sq]; first by rewrite polyOverC rpred1. +by apply/polyOverP=> i; rewrite coefM rpred_sum // => j _; apply: rpredM. +Qed. +Canonical polyOver_mulrPred := MulrPred polyOver_mulr_closed. +Canonical polyOver_semiringPred := SemiringPred polyOver_mulr_closed. + +Lemma polyOverZ : {in kS & polyOver kS, forall c p, c *: p \is a polyOver kS}. +Proof. +by move=> c p Sc /polyOverP Sp; apply/polyOverP=> i; rewrite coefZ rpredM ?Sp. +Qed. + +Lemma polyOverX : 'X \in polyOver kS. +Proof. by rewrite qualifE polyseqX /= rpred0 rpred1. Qed. + +Lemma rpred_horner : {in polyOver kS & kS, forall p x, p.[x] \in kS}. +Proof. +move=> p x /polyOverP Sp Sx; rewrite horner_coef rpred_sum // => i _. +by rewrite rpredM ?rpredX. +Qed. + +End PolyOverSemiring. + +Section PolyOverRing. + +Context (S : pred_class) (ringS : @subringPred R S) (kS : keyed_pred ringS). +Canonical polyOver_smulrPred := SmulrPred (polyOver_mulr_closed kS). +Canonical polyOver_subringPred := SubringPred (polyOver_mulr_closed kS). + +Lemma polyOverXsubC c : ('X - c%:P \in polyOver kS) = (c \in kS). +Proof. by rewrite rpredBl ?polyOverX ?polyOverC. Qed. + +End PolyOverRing. + +(* Single derivative. *) + +Definition deriv p := \poly_(i < (size p).-1) (p`_i.+1 *+ i.+1). + +Local Notation "a ^` ()" := (deriv a). + +Lemma coef_deriv p i : p^`()`_i = p`_i.+1 *+ i.+1. +Proof. +rewrite coef_poly -subn1 ltn_subRL. +by case: leqP => // /(nth_default 0) ->; rewrite mul0rn. +Qed. + +Lemma polyOver_deriv S (ringS : semiringPred S) (kS : keyed_pred ringS) : + {in polyOver kS, forall p, p^`() \is a polyOver kS}. +Proof. +by move=> p /polyOverP Kp; apply/polyOverP=> i; rewrite coef_deriv rpredMn ?Kp. +Qed. + +Lemma derivC c : c%:P^`() = 0. +Proof. by apply/polyP=> i; rewrite coef_deriv coef0 coefC mul0rn. Qed. + +Lemma derivX : ('X)^`() = 1. +Proof. by apply/polyP=> [[|i]]; rewrite coef_deriv coef1 coefX ?mul0rn. Qed. + +Lemma derivXn n : 'X^n^`() = 'X^n.-1 *+ n. +Proof. +case: n => [|n]; first exact: derivC. +apply/polyP=> i; rewrite coef_deriv coefMn !coefXn eqSS. +by case: eqP => [-> // | _]; rewrite !mul0rn. +Qed. + +Fact deriv_is_linear : linear deriv. +Proof. +move=> k p q; apply/polyP=> i. +by rewrite !(coef_deriv, coefD, coefZ) mulrnDl mulrnAr. +Qed. +Canonical deriv_additive := Additive deriv_is_linear. +Canonical deriv_linear := Linear deriv_is_linear. + +Lemma deriv0 : 0^`() = 0. +Proof. exact: linear0. Qed. + +Lemma derivD : {morph deriv : p q / p + q}. +Proof. exact: linearD. Qed. + +Lemma derivN : {morph deriv : p / - p}. +Proof. exact: linearN. Qed. + +Lemma derivB : {morph deriv : p q / p - q}. +Proof. exact: linearB. Qed. + +Lemma derivXsubC (a : R) : ('X - a%:P)^`() = 1. +Proof. by rewrite derivB derivX derivC subr0. Qed. + +Lemma derivMn n p : (p *+ n)^`() = p^`() *+ n. +Proof. exact: linearMn. Qed. + +Lemma derivMNn n p : (p *- n)^`() = p^`() *- n. +Proof. exact: linearMNn. Qed. + +Lemma derivZ c p : (c *: p)^`() = c *: p^`(). +Proof. by rewrite linearZ. Qed. + +Lemma deriv_mulC c p : (c%:P * p)^`() = c%:P * p^`(). +Proof. by rewrite !mul_polyC derivZ. Qed. + +Lemma derivMXaddC p c : (p * 'X + c%:P)^`() = p + p^`() * 'X. +Proof. +apply/polyP=> i; rewrite raddfD /= derivC addr0 coefD !(coefMX, coef_deriv). +by case: i; rewrite ?addr0. +Qed. + +Lemma derivM p q : (p * q)^`() = p^`() * q + p * q^`(). +Proof. +elim/poly_ind: p => [|p b IHp]; first by rewrite !(mul0r, add0r, derivC). +rewrite mulrDl -mulrA -commr_polyX mulrA -[_ * 'X]addr0 raddfD /= !derivMXaddC. +by rewrite deriv_mulC IHp !mulrDl -!mulrA !commr_polyX !addrA. +Qed. + +Definition derivE := Eval lazy beta delta [morphism_2 morphism_1] in + (derivZ, deriv_mulC, derivC, derivX, derivMXaddC, derivXsubC, derivM, derivB, + derivD, derivN, derivXn, derivM, derivMn). + +(* Iterated derivative. *) +Definition derivn n p := iter n deriv p. + +Local Notation "a ^` ( n )" := (derivn n a) : ring_scope. + +Lemma derivn0 p : p^`(0) = p. +Proof. by []. Qed. + +Lemma derivn1 p : p^`(1) = p^`(). +Proof. by []. Qed. + +Lemma derivnS p n : p^`(n.+1) = p^`(n)^`(). +Proof. by []. Qed. + +Lemma derivSn p n : p^`(n.+1) = p^`()^`(n). +Proof. exact: iterSr. Qed. + +Lemma coef_derivn n p i : p^`(n)`_i = p`_(n + i) *+ (n + i) ^_ n. +Proof. +elim: n i => [|n IHn] i; first by rewrite ffactn0 mulr1n. +by rewrite derivnS coef_deriv IHn -mulrnA ffactnSr addSnnS addKn. +Qed. + +Lemma polyOver_derivn S (ringS : semiringPred S) (kS : keyed_pred ringS) : + {in polyOver kS, forall p n, p^`(n) \is a polyOver kS}. +Proof. +move=> p /polyOverP Kp /= n; apply/polyOverP=> i. +by rewrite coef_derivn rpredMn. +Qed. + +Fact derivn_is_linear n : linear (derivn n). +Proof. by elim: n => // n IHn a p q; rewrite derivnS IHn linearP. Qed. +Canonical derivn_additive n := Additive (derivn_is_linear n). +Canonical derivn_linear n := Linear (derivn_is_linear n). + +Lemma derivnC c n : c%:P^`(n) = if n == 0%N then c%:P else 0. +Proof. by case: n => // n; rewrite derivSn derivC linear0. Qed. + +Lemma derivnD n : {morph derivn n : p q / p + q}. +Proof. exact: linearD. Qed. + +Lemma derivn_sub n : {morph derivn n : p q / p - q}. +Proof. exact: linearB. Qed. + +Lemma derivnMn n m p : (p *+ m)^`(n) = p^`(n) *+ m. +Proof. exact: linearMn. Qed. + +Lemma derivnMNn n m p : (p *- m)^`(n) = p^`(n) *- m. +Proof. exact: linearMNn. Qed. + +Lemma derivnN n : {morph derivn n : p / - p}. +Proof. exact: linearN. Qed. + +Lemma derivnZ n : scalable (derivn n). +Proof. exact: linearZZ. Qed. + +Lemma derivnXn m n : 'X^m^`(n) = 'X^(m - n) *+ m ^_ n. +Proof. +apply/polyP=>i; rewrite coef_derivn coefMn !coefXn. +case: (ltnP m n) => [lt_m_n | le_m_n]. + by rewrite eqn_leq leqNgt ltn_addr // mul0rn ffact_small. +by rewrite -{1 3}(subnKC le_m_n) eqn_add2l; case: eqP => [->|]; rewrite ?mul0rn. +Qed. + +Lemma derivnMXaddC n p c : + (p * 'X + c%:P)^`(n.+1) = p^`(n) *+ n.+1 + p^`(n.+1) * 'X. +Proof. +elim: n => [|n IHn]; first by rewrite derivn1 derivMXaddC. +rewrite derivnS IHn derivD derivM derivX mulr1 derivMn -!derivnS. +by rewrite addrA addrAC -mulrSr. +Qed. + +Lemma derivn_poly0 p n : size p <= n -> p^`(n) = 0. +Proof. +move=> le_p_n; apply/polyP=> i; rewrite coef_derivn. +rewrite nth_default; first by rewrite mul0rn coef0. +by apply: leq_trans le_p_n _; apply leq_addr. +Qed. + +Lemma lt_size_deriv (p : {poly R}) : p != 0 -> size p^`() < size p. +Proof. by move=> /polySpred->; exact: size_poly. Qed. + +(* A normalising version of derivation to get the division by n! in Taylor *) + +Definition nderivn n p := \poly_(i < size p - n) (p`_(n + i) *+ 'C(n + i, n)). + +Local Notation "a ^`N ( n )" := (nderivn n a) : ring_scope. + +Lemma coef_nderivn n p i : p^`N(n)`_i = p`_(n + i) *+ 'C(n + i, n). +Proof. +rewrite coef_poly ltn_subRL; case: leqP => // le_p_ni. +by rewrite nth_default ?mul0rn. +Qed. + +(* Here is the division by n! *) +Lemma nderivn_def n p : p^`(n) = p^`N(n) *+ n`!. +Proof. +by apply/polyP=> i; rewrite coefMn coef_nderivn coef_derivn -mulrnA bin_ffact. +Qed. + +Lemma polyOver_nderivn S (ringS : semiringPred S) (kS : keyed_pred ringS) : + {in polyOver kS, forall p n, p^`N(n) \in polyOver kS}. +Proof. +move=> p /polyOverP Sp /= n; apply/polyOverP=> i. +by rewrite coef_nderivn rpredMn. +Qed. + +Lemma nderivn0 p : p^`N(0) = p. +Proof. by rewrite -[p^`N(0)](nderivn_def 0). Qed. + +Lemma nderivn1 p : p^`N(1) = p^`(). +Proof. by rewrite -[p^`N(1)](nderivn_def 1). Qed. + +Lemma nderivnC c n : (c%:P)^`N(n) = if n == 0%N then c%:P else 0. +Proof. +apply/polyP=> i; rewrite coef_nderivn. +by case: n => [|n]; rewrite ?bin0 // coef0 coefC mul0rn. +Qed. + +Lemma nderivnXn m n : 'X^m^`N(n) = 'X^(m - n) *+ 'C(m, n). +Proof. +apply/polyP=> i; rewrite coef_nderivn coefMn !coefXn. +have [lt_m_n | le_n_m] := ltnP m n. + by rewrite eqn_leq leqNgt ltn_addr // mul0rn bin_small. +by rewrite -{1 3}(subnKC le_n_m) eqn_add2l; case: eqP => [->|]; rewrite ?mul0rn. +Qed. + +Fact nderivn_is_linear n : linear (nderivn n). +Proof. +move=> k p q; apply/polyP=> i. +by rewrite !(coef_nderivn, coefD, coefZ) mulrnDl mulrnAr. +Qed. +Canonical nderivn_additive n := Additive(nderivn_is_linear n). +Canonical nderivn_linear n := Linear (nderivn_is_linear n). + +Lemma nderivnD n : {morph nderivn n : p q / p + q}. +Proof. exact: linearD. Qed. + +Lemma nderivnB n : {morph nderivn n : p q / p - q}. +Proof. exact: linearB. Qed. + +Lemma nderivnMn n m p : (p *+ m)^`N(n) = p^`N(n) *+ m. +Proof. exact: linearMn. Qed. + +Lemma nderivnMNn n m p : (p *- m)^`N(n) = p^`N(n) *- m. +Proof. exact: linearMNn. Qed. + +Lemma nderivnN n : {morph nderivn n : p / - p}. +Proof. exact: linearN. Qed. + +Lemma nderivnZ n : scalable (nderivn n). +Proof. exact: linearZZ. Qed. + +Lemma nderivnMXaddC n p c : + (p * 'X + c%:P)^`N(n.+1) = p^`N(n) + p^`N(n.+1) * 'X. +Proof. +apply/polyP=> i; rewrite coef_nderivn !coefD !coefMX coefC. +rewrite !addSn /= !coef_nderivn addr0 binS mulrnDr addrC; congr (_ + _). +by rewrite addSnnS; case: i; rewrite // addn0 bin_small. +Qed. + +Lemma nderivn_poly0 p n : size p <= n -> p^`N(n) = 0. +Proof. +move=> le_p_n; apply/polyP=> i; rewrite coef_nderivn. +rewrite nth_default; first by rewrite mul0rn coef0. +by apply: leq_trans le_p_n _; apply leq_addr. +Qed. + +Lemma nderiv_taylor p x h : + GRing.comm x h -> p.[x + h] = \sum_(i < size p) p^`N(i).[x] * h ^+ i. +Proof. +move/commrX=> cxh; elim/poly_ind: p => [|p c IHp]. + by rewrite size_poly0 big_ord0 horner0. +rewrite hornerMXaddC size_MXaddC. +have [-> | nz_p] := altP (p =P 0). + rewrite horner0 !simp; have [-> | _] := c =P 0; first by rewrite big_ord0. + by rewrite size_poly0 big_ord_recl big_ord0 nderivn0 hornerC !simp. +rewrite big_ord_recl nderivn0 !simp hornerMXaddC addrAC; congr (_ + _). +rewrite mulrDr {}IHp !big_distrl polySpred //= big_ord_recl /= mulr1 -addrA. +rewrite nderivn0 /bump /(addn 1) /=; congr (_ + _). +rewrite !big_ord_recr /= nderivnMXaddC -mulrA -exprSr -polySpred // !addrA. +congr (_ + _); last by rewrite (nderivn_poly0 (leqnn _)) !simp. +rewrite addrC -big_split /=; apply: eq_bigr => i _. +by rewrite nderivnMXaddC !hornerE_comm /= mulrDl -!mulrA -exprSr cxh. +Qed. + +Lemma nderiv_taylor_wide n p x h : + GRing.comm x h -> size p <= n -> + p.[x + h] = \sum_(i < n) p^`N(i).[x] * h ^+ i. +Proof. +move/nderiv_taylor=> -> le_p_n. +rewrite (big_ord_widen n (fun i => p^`N(i).[x] * h ^+ i)) // big_mkcond. +apply: eq_bigr => i _; case: leqP => // /nderivn_poly0->. +by rewrite horner0 simp. +Qed. + +End PolynomialTheory. + +Prenex Implicits polyC Poly lead_coef root horner polyOver. +Implicit Arguments monic [[R]]. +Notation "\poly_ ( i < n ) E" := (poly n (fun i => E)) : ring_scope. +Notation "c %:P" := (polyC c) : ring_scope. +Notation "'X" := (polyX _) : ring_scope. +Notation "''X^' n" := ('X ^+ n) : ring_scope. +Notation "p .[ x ]" := (horner p x) : ring_scope. +Notation "n .-unity_root" := (root_of_unity n) : ring_scope. +Notation "n .-primitive_root" := (primitive_root_of_unity n) : ring_scope. +Notation "a ^` ()" := (deriv a) : ring_scope. +Notation "a ^` ( n )" := (derivn n a) : ring_scope. +Notation "a ^`N ( n )" := (nderivn n a) : ring_scope. + +Implicit Arguments monicP [R p]. +Implicit Arguments rootP [R p x]. +Implicit Arguments rootPf [R p x]. +Implicit Arguments rootPt [R p x]. +Implicit Arguments unity_rootP [R n z]. +Implicit Arguments polyOverP [[R] [S0] [addS] [kS] [p]]. + +(* Container morphism. *) +Section MapPoly. + +Section Definitions. + +Variables (aR rR : ringType) (f : aR -> rR). + +Definition map_poly (p : {poly aR}) := \poly_(i < size p) f p`_i. + +(* Alternative definition; the one above is more convenient because it lets *) +(* us use the lemmas on \poly, e.g., size (map_poly p) <= size p is an *) +(* instance of size_poly. *) +Lemma map_polyE p : map_poly p = Poly (map f p). +Proof. +rewrite /map_poly unlock; congr Poly. +apply: (@eq_from_nth _ 0); rewrite size_mkseq ?size_map // => i lt_i_p. +by rewrite (nth_map 0) ?nth_mkseq. +Qed. + +Definition commr_rmorph u := forall x, GRing.comm u (f x). + +Definition horner_morph u of commr_rmorph u := fun p => (map_poly p).[u]. + +End Definitions. + +Variables aR rR : ringType. + +Section Combinatorial. + +Variables (iR : ringType) (f : aR -> rR). +Local Notation "p ^f" := (map_poly f p) : ring_scope. + +Lemma map_poly0 : 0^f = 0. +Proof. by rewrite map_polyE polyseq0. Qed. + +Lemma eq_map_poly (g : aR -> rR) : f =1 g -> map_poly f =1 map_poly g. +Proof. by move=> eq_fg p; rewrite !map_polyE (eq_map eq_fg). Qed. + +Lemma map_poly_id g (p : {poly iR}) : + {in (p : seq iR), g =1 id} -> map_poly g p = p. +Proof. by move=> g_id; rewrite map_polyE map_id_in ?polyseqK. Qed. + +Lemma coef_map_id0 p i : f 0 = 0 -> (p^f)`_i = f p`_i. +Proof. +by move=> f0; rewrite coef_poly; case: ltnP => // le_p_i; rewrite nth_default. +Qed. + +Lemma map_Poly_id0 s : f 0 = 0 -> (Poly s)^f = Poly (map f s). +Proof. +move=> f0; apply/polyP=> j; rewrite coef_map_id0 ?coef_Poly //. +have [/(nth_map 0 0)->// | le_s_j] := ltnP j (size s). +by rewrite !nth_default ?size_map. +Qed. + +Lemma map_poly_comp_id0 (g : iR -> aR) p : + f 0 = 0 -> map_poly (f \o g) p = (map_poly g p)^f. +Proof. by move=> f0; rewrite map_polyE map_comp -map_Poly_id0 -?map_polyE. Qed. + +Lemma size_map_poly_id0 p : f (lead_coef p) != 0 -> size p^f = size p. +Proof. by move=> nz_fp; apply: size_poly_eq. Qed. + +Lemma map_poly_eq0_id0 p : f (lead_coef p) != 0 -> (p^f == 0) = (p == 0). +Proof. by rewrite -!size_poly_eq0 => /size_map_poly_id0->. Qed. + +Lemma lead_coef_map_id0 p : + f 0 = 0 -> f (lead_coef p) != 0 -> lead_coef p^f = f (lead_coef p). +Proof. +by move=> f0 nz_fp; rewrite lead_coefE coef_map_id0 ?size_map_poly_id0. +Qed. + +Hypotheses (inj_f : injective f) (f_0 : f 0 = 0). + +Lemma size_map_inj_poly p : size p^f = size p. +Proof. +have [-> | nz_p] := eqVneq p 0; first by rewrite map_poly0 !size_poly0. +by rewrite size_map_poly_id0 // -f_0 (inj_eq inj_f) lead_coef_eq0. +Qed. + +Lemma map_inj_poly : injective (map_poly f). +Proof. +move=> p q /polyP eq_pq; apply/polyP=> i; apply: inj_f. +by rewrite -!coef_map_id0 ?eq_pq. +Qed. + +Lemma lead_coef_map_inj p : lead_coef p^f = f (lead_coef p). +Proof. by rewrite !lead_coefE size_map_inj_poly coef_map_id0. Qed. + +End Combinatorial. + +Lemma map_polyK (f : aR -> rR) g : + cancel g f -> f 0 = 0 -> cancel (map_poly g) (map_poly f). +Proof. +by move=> gK f_0 p; rewrite /= -map_poly_comp_id0 ?map_poly_id // => x _ //=. +Qed. + +Section Additive. + +Variables (iR : ringType) (f : {additive aR -> rR}). + +Local Notation "p ^f" := (map_poly (GRing.Additive.apply f) p) : ring_scope. + +Lemma coef_map p i : p^f`_i = f p`_i. +Proof. exact: coef_map_id0 (raddf0 f). Qed. + +Lemma map_Poly s : (Poly s)^f = Poly (map f s). +Proof. exact: map_Poly_id0 (raddf0 f). Qed. + +Lemma map_poly_comp (g : iR -> aR) p : + map_poly (f \o g) p = map_poly f (map_poly g p). +Proof. exact: map_poly_comp_id0 (raddf0 f). Qed. + +Fact map_poly_is_additive : additive (map_poly f). +Proof. by move=> p q; apply/polyP=> i; rewrite !(coef_map, coefB) raddfB. Qed. +Canonical map_poly_additive := Additive map_poly_is_additive. + +Lemma map_polyC a : (a%:P)^f = (f a)%:P. +Proof. by apply/polyP=> i; rewrite !(coef_map, coefC) -!mulrb raddfMn. Qed. + +Lemma lead_coef_map_eq p : + f (lead_coef p) != 0 -> lead_coef p^f = f (lead_coef p). +Proof. exact: lead_coef_map_id0 (raddf0 f). Qed. + +End Additive. + +Variable f : {rmorphism aR -> rR}. +Implicit Types p : {poly aR}. + +Local Notation "p ^f" := (map_poly (GRing.RMorphism.apply f) p) : ring_scope. + +Fact map_poly_is_rmorphism : rmorphism (map_poly f). +Proof. +split; first exact: map_poly_is_additive. +split=> [p q|]; apply/polyP=> i; last first. + by rewrite !(coef_map, coef1) /= rmorph_nat. +rewrite coef_map /= !coefM /= !rmorph_sum; apply: eq_bigr => j _. +by rewrite !coef_map rmorphM. +Qed. +Canonical map_poly_rmorphism := RMorphism map_poly_is_rmorphism. + +Lemma map_polyZ c p : (c *: p)^f = f c *: p^f. +Proof. by apply/polyP=> i; rewrite !(coef_map, coefZ) /= rmorphM. Qed. +Canonical map_poly_linear := + AddLinear (map_polyZ : scalable_for (f \; *:%R) (map_poly f)). +Canonical map_poly_lrmorphism := [lrmorphism of map_poly f]. + +Lemma map_polyX : ('X)^f = 'X. +Proof. by apply/polyP=> i; rewrite coef_map !coefX /= rmorph_nat. Qed. + +Lemma map_polyXn n : ('X^n)^f = 'X^n. +Proof. by rewrite rmorphX /= map_polyX. Qed. + +Lemma monic_map p : p \is monic -> p^f \is monic. +Proof. +move/monicP=> mon_p; rewrite monicE. +by rewrite lead_coef_map_eq mon_p /= rmorph1 ?oner_neq0. +Qed. + +Lemma horner_map p x : p^f.[f x] = f p.[x]. +Proof. +elim/poly_ind: p => [|p c IHp]; first by rewrite !(rmorph0, horner0). +rewrite hornerMXaddC !rmorphD !rmorphM /=. +by rewrite map_polyX map_polyC hornerMXaddC IHp. +Qed. + +Lemma map_comm_poly p x : comm_poly p x -> comm_poly p^f (f x). +Proof. by rewrite /comm_poly horner_map -!rmorphM // => ->. Qed. + +Lemma map_comm_coef p x : comm_coef p x -> comm_coef p^f (f x). +Proof. by move=> cpx i; rewrite coef_map -!rmorphM ?cpx. Qed. + +Lemma rmorph_root p x : root p x -> root p^f (f x). +Proof. by move/eqP=> px0; rewrite rootE horner_map px0 rmorph0. Qed. + +Lemma rmorph_unity_root n z : n.-unity_root z -> n.-unity_root (f z). +Proof. +move/rmorph_root; rewrite rootE rmorphB hornerD hornerN. +by rewrite /= map_polyXn rmorph1 hornerC hornerXn subr_eq0 unity_rootE. +Qed. + +Section HornerMorph. + +Variable u : rR. +Hypothesis cfu : commr_rmorph f u. + +Lemma horner_morphC a : horner_morph cfu a%:P = f a. +Proof. by rewrite /horner_morph map_polyC hornerC. Qed. + +Lemma horner_morphX : horner_morph cfu 'X = u. +Proof. by rewrite /horner_morph map_polyX hornerX. Qed. + +Fact horner_is_lrmorphism : lrmorphism_for (f \; *%R) (horner_morph cfu). +Proof. +rewrite /horner_morph; split=> [|c p]; last by rewrite linearZ hornerZ. +split=> [p q|]; first by rewrite /horner_morph rmorphB hornerD hornerN. +split=> [p q|]; last by rewrite /horner_morph rmorph1 hornerC. +rewrite /horner_morph rmorphM /= hornerM_comm //. +by apply: comm_coef_poly => i; rewrite coef_map cfu. +Qed. +Canonical horner_additive := Additive horner_is_lrmorphism. +Canonical horner_rmorphism := RMorphism horner_is_lrmorphism. +Canonical horner_linear := AddLinear horner_is_lrmorphism. +Canonical horner_lrmorphism := [lrmorphism of horner_morph cfu]. + +End HornerMorph. + +Lemma deriv_map p : p^f^`() = (p^`())^f. +Proof. by apply/polyP => i; rewrite !(coef_map, coef_deriv) //= rmorphMn. Qed. + +Lemma derivn_map p n : p^f^`(n) = (p^`(n))^f. +Proof. by apply/polyP => i; rewrite !(coef_map, coef_derivn) //= rmorphMn. Qed. + +Lemma nderivn_map p n : p^f^`N(n) = (p^`N(n))^f. +Proof. by apply/polyP => i; rewrite !(coef_map, coef_nderivn) //= rmorphMn. Qed. + +End MapPoly. + +(* Morphisms from the polynomial ring, and the initiality of polynomials *) +(* with respect to these. *) +Section MorphPoly. + +Variable (aR rR : ringType) (pf : {rmorphism {poly aR} -> rR}). + +Lemma poly_morphX_comm : commr_rmorph (pf \o polyC) (pf 'X). +Proof. by move=> a; rewrite /GRing.comm /= -!rmorphM // commr_polyX. Qed. + +Lemma poly_initial : pf =1 horner_morph poly_morphX_comm. +Proof. +apply: poly_ind => [|p a IHp]; first by rewrite !rmorph0. +by rewrite !rmorphD !rmorphM /= -{}IHp horner_morphC ?horner_morphX. +Qed. + +End MorphPoly. + +Notation "p ^:P" := (map_poly polyC p) : ring_scope. + +Section PolyCompose. + +Variable R : ringType. +Implicit Types p q : {poly R}. + +Definition comp_poly q p := p^:P.[q]. + +Local Notation "p \Po q" := (comp_poly q p) : ring_scope. + +Lemma size_map_polyC p : size p^:P = size p. +Proof. exact: size_map_inj_poly (@polyC_inj R) _ _. Qed. + +Lemma map_polyC_eq0 p : (p^:P == 0) = (p == 0). +Proof. by rewrite -!size_poly_eq0 size_map_polyC. Qed. + +Lemma root_polyC p x : root p^:P x%:P = root p x. +Proof. by rewrite rootE horner_map polyC_eq0. Qed. + +Lemma comp_polyE p q : p \Po q = \sum_(i < size p) p`_i *: q^+i. +Proof. +by rewrite [p \Po q]horner_poly; apply: eq_bigr => i _; rewrite mul_polyC. +Qed. + +Lemma polyOver_comp S (ringS : semiringPred S) (kS : keyed_pred ringS) : + {in polyOver kS &, forall p q, p \Po q \in polyOver kS}. +Proof. +move=> p q /polyOverP Sp Sq; rewrite comp_polyE rpred_sum // => i _. +by rewrite polyOverZ ?rpredX. +Qed. + +Lemma comp_polyCr p c : p \Po c%:P = p.[c]%:P. +Proof. exact: horner_map. Qed. + +Lemma comp_poly0r p : p \Po 0 = (p`_0)%:P. +Proof. by rewrite comp_polyCr horner_coef0. Qed. + +Lemma comp_polyC c p : c%:P \Po p = c%:P. +Proof. by rewrite /(_ \Po p) map_polyC hornerC. Qed. + +Fact comp_poly_is_linear p : linear (comp_poly p). +Proof. +move=> a q r. +by rewrite /comp_poly rmorphD /= map_polyZ !hornerE_comm mul_polyC. +Qed. +Canonical comp_poly_additive p := Additive (comp_poly_is_linear p). +Canonical comp_poly_linear p := Linear (comp_poly_is_linear p). + +Lemma comp_poly0 p : 0 \Po p = 0. +Proof. exact: raddf0. Qed. + +Lemma comp_polyD p q r : (p + q) \Po r = (p \Po r) + (q \Po r). +Proof. exact: raddfD. Qed. + +Lemma comp_polyB p q r : (p - q) \Po r = (p \Po r) - (q \Po r). +Proof. exact: raddfB. Qed. + +Lemma comp_polyZ c p q : (c *: p) \Po q = c *: (p \Po q). +Proof. exact: linearZZ. Qed. + +Lemma comp_polyXr p : p \Po 'X = p. +Proof. by rewrite -{2}/(idfun p) poly_initial. Qed. + +Lemma comp_polyX p : 'X \Po p = p. +Proof. by rewrite /(_ \Po p) map_polyX hornerX. Qed. + +Lemma comp_poly_MXaddC c p q : (p * 'X + c%:P) \Po q = (p \Po q) * q + c%:P. +Proof. +by rewrite /(_ \Po q) rmorphD rmorphM /= map_polyX map_polyC hornerMXaddC. +Qed. + +Lemma comp_polyXaddC_K p z : (p \Po ('X + z%:P)) \Po ('X - z%:P) = p. +Proof. +have addzK: ('X + z%:P) \Po ('X - z%:P) = 'X. + by rewrite raddfD /= comp_polyC comp_polyX subrK. +elim/poly_ind: p => [|p c IHp]; first by rewrite !comp_poly0. +rewrite comp_poly_MXaddC linearD /= comp_polyC {1}/comp_poly rmorphM /=. +by rewrite hornerM_comm /comm_poly -!/(_ \Po _) ?IHp ?addzK ?commr_polyX. +Qed. + +Lemma size_comp_poly_leq p q : + size (p \Po q) <= ((size p).-1 * (size q).-1).+1. +Proof. +rewrite comp_polyE (leq_trans (size_sum _ _ _)) //; apply/bigmax_leqP => i _. +rewrite (leq_trans (size_scale_leq _ _)) // (leq_trans (size_exp_leq _ _)) //. +by rewrite ltnS mulnC leq_mul // -{2}(subnKC (valP i)) leq_addr. +Qed. + +End PolyCompose. + +Notation "p \Po q" := (comp_poly q p) : ring_scope. + +Lemma map_comp_poly (aR rR : ringType) (f : {rmorphism aR -> rR}) p q : + map_poly f (p \Po q) = map_poly f p \Po map_poly f q. +Proof. +elim/poly_ind: p => [|p a IHp]; first by rewrite !raddf0. +rewrite comp_poly_MXaddC !rmorphD !rmorphM /= !map_polyC map_polyX. +by rewrite comp_poly_MXaddC -IHp. +Qed. + +Section PolynomialComRing. + +Variable R : comRingType. +Implicit Types p q : {poly R}. + +Fact poly_mul_comm p q : p * q = q * p. +Proof. +apply/polyP=> i; rewrite coefM coefMr. +by apply: eq_bigr => j _; rewrite mulrC. +Qed. + +Canonical poly_comRingType := Eval hnf in ComRingType {poly R} poly_mul_comm. +Canonical polynomial_comRingType := + Eval hnf in ComRingType (polynomial R) poly_mul_comm. +Canonical poly_algType := Eval hnf in CommAlgType R {poly R}. +Canonical polynomial_algType := + Eval hnf in [algType R of polynomial R for poly_algType]. + +Lemma hornerM p q x : (p * q).[x] = p.[x] * q.[x]. +Proof. by rewrite hornerM_comm //; exact: mulrC. Qed. + +Lemma horner_exp p x n : (p ^+ n).[x] = p.[x] ^+ n. +Proof. by rewrite horner_exp_comm //; exact: mulrC. Qed. + +Lemma horner_prod I r (P : pred I) (F : I -> {poly R}) x : + (\prod_(i <- r | P i) F i).[x] = \prod_(i <- r | P i) (F i).[x]. +Proof. by elim/big_rec2: _ => [|i _ p _ <-]; rewrite (hornerM, hornerC). Qed. + +Definition hornerE := + (hornerD, hornerN, hornerX, hornerC, horner_cons, + simp, hornerCM, hornerZ, hornerM). + +Definition horner_eval (x : R) := horner^~ x. +Lemma horner_evalE x p : horner_eval x p = p.[x]. Proof. by []. Qed. + +Fact horner_eval_is_lrmorphism x : lrmorphism_for *%R (horner_eval x). +Proof. +have cxid: commr_rmorph idfun x by exact: mulrC. +have evalE : horner_eval x =1 horner_morph cxid. + by move=> p; congr _.[x]; rewrite map_poly_id. +split=> [|c p]; last by rewrite !evalE /= -linearZ. +by do 2?split=> [p q|]; rewrite !evalE (rmorphB, rmorphM, rmorph1). +Qed. +Canonical horner_eval_additive x := Additive (horner_eval_is_lrmorphism x). +Canonical horner_eval_rmorphism x := RMorphism (horner_eval_is_lrmorphism x). +Canonical horner_eval_linear x := AddLinear (horner_eval_is_lrmorphism x). +Canonical horner_eval_lrmorphism x := [lrmorphism of horner_eval x]. + +Fact comp_poly_multiplicative q : multiplicative (comp_poly q). +Proof. +split=> [p1 p2|]; last by rewrite comp_polyC. +by rewrite /comp_poly rmorphM hornerM_comm //; exact: mulrC. +Qed. +Canonical comp_poly_rmorphism q := AddRMorphism (comp_poly_multiplicative q). +Canonical comp_poly_lrmorphism q := [lrmorphism of comp_poly q]. + +Lemma comp_polyM p q r : (p * q) \Po r = (p \Po r) * (q \Po r). +Proof. exact: rmorphM. Qed. + +Lemma comp_polyA p q r : p \Po (q \Po r) = (p \Po q) \Po r. +Proof. +elim/poly_ind: p => [|p c IHp]; first by rewrite !comp_polyC. +by rewrite !comp_polyD !comp_polyM !comp_polyX IHp !comp_polyC. +Qed. + +Lemma horner_comp p q x : (p \Po q).[x] = p.[q.[x]]. +Proof. by apply: polyC_inj; rewrite -!comp_polyCr comp_polyA. Qed. + +Lemma root_comp p q x : root (p \Po q) x = root p (q.[x]). +Proof. by rewrite !rootE horner_comp. Qed. + +Lemma deriv_comp p q : (p \Po q) ^`() = (p ^`() \Po q) * q^`(). +Proof. +elim/poly_ind: p => [|p c IHp]; first by rewrite !(deriv0, comp_poly0) mul0r. +rewrite comp_poly_MXaddC derivD derivC derivM IHp derivMXaddC comp_polyD. +by rewrite comp_polyM comp_polyX addr0 addrC mulrAC -mulrDl. +Qed. + +Lemma deriv_exp p n : (p ^+ n)^`() = p^`() * p ^+ n.-1 *+ n. +Proof. +elim: n => [|n IHn]; first by rewrite expr0 mulr0n derivC. +by rewrite exprS derivM {}IHn (mulrC p) mulrnAl -mulrA -exprSr mulrS; case n. +Qed. + +Definition derivCE := (derivE, deriv_exp). + +End PolynomialComRing. + +Section PolynomialIdomain. + +(* Integral domain structure on poly *) +Variable R : idomainType. + +Implicit Types (a b x y : R) (p q r m : {poly R}). + +Lemma size_mul p q : p != 0 -> q != 0 -> size (p * q) = (size p + size q).-1. +Proof. +by move=> nz_p nz_q; rewrite -size_proper_mul ?mulf_neq0 ?lead_coef_eq0. +Qed. + +Fact poly_idomainAxiom p q : p * q = 0 -> (p == 0) || (q == 0). +Proof. +move=> pq0; apply/norP=> [[p_nz q_nz]]; move/eqP: (size_mul p_nz q_nz). +by rewrite eq_sym pq0 size_poly0 (polySpred p_nz) (polySpred q_nz) addnS. +Qed. + +Definition poly_unit : pred {poly R} := + fun p => (size p == 1%N) && (p`_0 \in GRing.unit). + +Definition poly_inv p := if p \in poly_unit then (p`_0)^-1%:P else p. + +Fact poly_mulVp : {in poly_unit, left_inverse 1 poly_inv *%R}. +Proof. +move=> p Up; rewrite /poly_inv Up. +by case/andP: Up => /size_poly1P[c _ ->]; rewrite coefC -polyC_mul => /mulVr->. +Qed. + +Fact poly_intro_unit p q : q * p = 1 -> p \in poly_unit. +Proof. +move=> pq1; apply/andP; split; last first. + apply/unitrP; exists q`_0. + by rewrite 2!mulrC -!/(coefp 0 _) -rmorphM pq1 rmorph1. +have: size (q * p) == 1%N by rewrite pq1 size_poly1. +have [-> | nz_p] := eqVneq p 0; first by rewrite mulr0 size_poly0. +have [-> | nz_q] := eqVneq q 0; first by rewrite mul0r size_poly0. +rewrite size_mul // (polySpred nz_p) (polySpred nz_q) addnS addSn !eqSS. +by rewrite addn_eq0 => /andP[]. +Qed. + +Fact poly_inv_out : {in [predC poly_unit], poly_inv =1 id}. +Proof. by rewrite /poly_inv => p /negbTE/= ->. Qed. + +Definition poly_comUnitMixin := + ComUnitRingMixin poly_mulVp poly_intro_unit poly_inv_out. + +Canonical poly_unitRingType := + Eval hnf in UnitRingType {poly R} poly_comUnitMixin. +Canonical polynomial_unitRingType := + Eval hnf in [unitRingType of polynomial R for poly_unitRingType]. + +Canonical poly_unitAlgType := Eval hnf in [unitAlgType R of {poly R}]. +Canonical polynomial_unitAlgType := Eval hnf in [unitAlgType R of polynomial R]. + +Canonical poly_comUnitRingType := Eval hnf in [comUnitRingType of {poly R}]. +Canonical polynomial_comUnitRingType := + Eval hnf in [comUnitRingType of polynomial R]. + +Canonical poly_idomainType := + Eval hnf in IdomainType {poly R} poly_idomainAxiom. +Canonical polynomial_idomainType := + Eval hnf in [idomainType of polynomial R for poly_idomainType]. + +Lemma poly_unitE p : + (p \in GRing.unit) = (size p == 1%N) && (p`_0 \in GRing.unit). +Proof. by []. Qed. + +Lemma poly_invE p : p ^-1 = if p \in GRing.unit then (p`_0)^-1%:P else p. +Proof. by []. Qed. + +Lemma polyC_inv c : c%:P^-1 = (c^-1)%:P. +Proof. +have [/rmorphV-> // | nUc] := boolP (c \in GRing.unit). +by rewrite !invr_out // poly_unitE coefC (negbTE nUc) andbF. +Qed. + +Lemma rootM p q x : root (p * q) x = root p x || root q x. +Proof. by rewrite !rootE hornerM mulf_eq0. Qed. + +Lemma rootZ x a p : a != 0 -> root (a *: p) x = root p x. +Proof. by move=> nz_a; rewrite -mul_polyC rootM rootC (negPf nz_a). Qed. + +Lemma size_scale a p : a != 0 -> size (a *: p) = size p. +Proof. by move/lregP/lreg_size->. Qed. + +Lemma size_Cmul a p : a != 0 -> size (a%:P * p) = size p. +Proof. by rewrite mul_polyC => /size_scale->. Qed. + +Lemma lead_coefM p q : lead_coef (p * q) = lead_coef p * lead_coef q. +Proof. +have [-> | nz_p] := eqVneq p 0; first by rewrite !(mul0r, lead_coef0). +have [-> | nz_q] := eqVneq q 0; first by rewrite !(mulr0, lead_coef0). +by rewrite lead_coef_proper_mul // mulf_neq0 ?lead_coef_eq0. +Qed. + +Lemma lead_coefZ a p : lead_coef (a *: p) = a * lead_coef p. +Proof. by rewrite -mul_polyC lead_coefM lead_coefC. Qed. + +Lemma scale_poly_eq0 a p : (a *: p == 0) = (a == 0) || (p == 0). +Proof. by rewrite -mul_polyC mulf_eq0 polyC_eq0. Qed. + +Lemma size_prod (I : finType) (P : pred I) (F : I -> {poly R}) : + (forall i, P i -> F i != 0) -> + size (\prod_(i | P i) F i) = ((\sum_(i | P i) size (F i)).+1 - #|P|)%N. +Proof. +move=> nzF; transitivity (\sum_(i | P i) (size (F i)).-1).+1; last first. + apply: canRL (addKn _) _; rewrite addnS -sum1_card -big_split /=. + by congr _.+1; apply: eq_bigr => i /nzF/polySpred. +elim/big_rec2: _ => [|i d p /nzF nzFi IHp]; first by rewrite size_poly1. +by rewrite size_mul // -?size_poly_eq0 IHp // addnS polySpred. +Qed. + +Lemma size_exp p n : (size (p ^+ n)).-1 = ((size p).-1 * n)%N. +Proof. +elim: n => [|n IHn]; first by rewrite size_poly1 muln0. +have [-> | nz_p] := eqVneq p 0; first by rewrite exprS mul0r size_poly0. +rewrite exprS size_mul ?expf_neq0 // mulnS -{}IHn. +by rewrite polySpred // [size (p ^+ n)]polySpred ?expf_neq0 ?addnS. +Qed. + +Lemma lead_coef_exp p n : lead_coef (p ^+ n) = lead_coef p ^+ n. +Proof. +elim: n => [|n IHn]; first by rewrite !expr0 lead_coef1. +by rewrite !exprS lead_coefM IHn. +Qed. + +Lemma root_prod_XsubC rs x : + root (\prod_(a <- rs) ('X - a%:P)) x = (x \in rs). +Proof. +elim: rs => [|a rs IHrs]; first by rewrite rootE big_nil hornerC oner_eq0. +by rewrite big_cons rootM IHrs root_XsubC. +Qed. + +Lemma root_exp_XsubC n a x : root (('X - a%:P) ^+ n.+1) x = (x == a). +Proof. by rewrite rootE horner_exp expf_eq0 [_ == 0]root_XsubC. Qed. + +Lemma size_comp_poly p q : + (size (p \Po q)).-1 = ((size p).-1 * (size q).-1)%N. +Proof. +have [-> | nz_p] := eqVneq p 0; first by rewrite comp_poly0 size_poly0. +have [/size1_polyC-> | nc_q] := leqP (size q) 1. + by rewrite comp_polyCr !size_polyC -!sub1b -!subnS muln0. +have nz_q: q != 0 by rewrite -size_poly_eq0 -(subnKC nc_q). +rewrite mulnC comp_polyE (polySpred nz_p) /= big_ord_recr /= addrC. +rewrite size_addl size_scale ?lead_coef_eq0 ?size_exp //=. +rewrite [X in _ < X]polySpred ?expf_neq0 // ltnS size_exp. +rewrite (leq_trans (size_sum _ _ _)) //; apply/bigmax_leqP => i _. +rewrite (leq_trans (size_scale_leq _ _)) // polySpred ?expf_neq0 //. +by rewrite size_exp -(subnKC nc_q) ltn_pmul2l. +Qed. + +Lemma size_comp_poly2 p q : size q = 2 -> size (p \Po q) = size p. +Proof. +have [/size1_polyC->| p_gt1] := leqP (size p) 1; first by rewrite comp_polyC. +move=> lin_q; have{lin_q} sz_pq: (size (p \Po q)).-1 = (size p).-1. + by rewrite size_comp_poly lin_q muln1. +rewrite -(ltn_predK p_gt1) -sz_pq -polySpred // -size_poly_gt0 ltnW //. +by rewrite -subn_gt0 subn1 sz_pq -subn1 subn_gt0. +Qed. + +Lemma comp_poly2_eq0 p q : size q = 2 -> (p \Po q == 0) = (p == 0). +Proof. by rewrite -!size_poly_eq0 => /size_comp_poly2->. Qed. + +Lemma lead_coef_comp p q : + size q > 1 -> lead_coef (p \Po q) = lead_coef p * lead_coef q ^+ (size p).-1. +Proof. +move=> q_gt1; have nz_q: q != 0 by rewrite -size_poly_gt0 ltnW. +have [-> | nz_p] := eqVneq p 0; first by rewrite comp_poly0 !lead_coef0 mul0r. +rewrite comp_polyE polySpred //= big_ord_recr /= addrC -lead_coefE. +rewrite lead_coefDl; first by rewrite lead_coefZ lead_coef_exp. +rewrite size_scale ?lead_coef_eq0 // (polySpred (expf_neq0 _ nz_q)) ltnS. +apply/leq_sizeP=> i le_qp_i; rewrite coef_sum big1 // => j _. +rewrite coefZ (nth_default 0 (leq_trans _ le_qp_i)) ?mulr0 //=. +by rewrite polySpred ?expf_neq0 // !size_exp -(subnKC q_gt1) ltn_pmul2l. +Qed. + +End PolynomialIdomain. + +Section MapFieldPoly. + +Variables (F : fieldType) (R : ringType) (f : {rmorphism F -> R}). + +Local Notation "p ^f" := (map_poly f p) : ring_scope. + +Lemma size_map_poly p : size p^f = size p. +Proof. +have [-> | nz_p] := eqVneq p 0; first by rewrite rmorph0 !size_poly0. +by rewrite size_poly_eq // fmorph_eq0 // lead_coef_eq0. +Qed. + +Lemma lead_coef_map p : lead_coef p^f = f (lead_coef p). +Proof. +have [-> | nz_p] := eqVneq p 0; first by rewrite !(rmorph0, lead_coef0). +by rewrite lead_coef_map_eq // fmorph_eq0 // lead_coef_eq0. +Qed. + +Lemma map_poly_eq0 p : (p^f == 0) = (p == 0). +Proof. by rewrite -!size_poly_eq0 size_map_poly. Qed. + +Lemma map_poly_inj : injective (map_poly f). +Proof. +move=> p q eqfpq; apply/eqP; rewrite -subr_eq0 -map_poly_eq0. +by rewrite rmorphB /= eqfpq subrr. +Qed. + +Lemma map_monic p : (p^f \is monic) = (p \is monic). +Proof. by rewrite monicE lead_coef_map fmorph_eq1. Qed. + +Lemma map_poly_com p x : comm_poly p^f (f x). +Proof. exact: map_comm_poly (mulrC x _). Qed. + +Lemma fmorph_root p x : root p^f (f x) = root p x. +Proof. by rewrite rootE horner_map // fmorph_eq0. Qed. + +Lemma fmorph_unity_root n z : n.-unity_root (f z) = n.-unity_root z. +Proof. by rewrite !unity_rootE -(inj_eq (fmorph_inj f)) rmorphX ?rmorph1. Qed. + +Lemma fmorph_primitive_root n z : + n.-primitive_root (f z) = n.-primitive_root z. +Proof. +by congr (_ && _); apply: eq_forallb => i; rewrite fmorph_unity_root. +Qed. + +End MapFieldPoly. + +Implicit Arguments map_poly_inj [[F] [R] x1 x2]. + +Section MaxRoots. + +Variable R : unitRingType. +Implicit Types (x y : R) (rs : seq R) (p : {poly R}). + +Definition diff_roots (x y : R) := (x * y == y * x) && (y - x \in GRing.unit). + +Fixpoint uniq_roots rs := + if rs is x :: rs' then all (diff_roots x) rs' && uniq_roots rs' else true. + +Lemma uniq_roots_prod_XsubC p rs : + all (root p) rs -> uniq_roots rs -> + exists q, p = q * \prod_(z <- rs) ('X - z%:P). +Proof. +elim: rs => [|z rs IHrs] /=; first by rewrite big_nil; exists p; rewrite mulr1. +case/andP=> rpz rprs /andP[drs urs]; case: IHrs => {urs rprs}// q def_p. +have [|q' def_q] := factor_theorem q z _; last first. + by exists q'; rewrite big_cons mulrA -def_q. +rewrite {p}def_p in rpz. +elim/last_ind: rs drs rpz => [|rs t IHrs] /=; first by rewrite big_nil mulr1. +rewrite all_rcons => /andP[/andP[/eqP czt Uzt] /IHrs {IHrs}IHrs]. +rewrite -cats1 big_cat big_seq1 /= mulrA rootE hornerM_comm; last first. + by rewrite /comm_poly hornerXsubC mulrBl mulrBr czt. +rewrite hornerXsubC -opprB mulrN oppr_eq0 -(mul0r (t - z)). +by rewrite (inj_eq (mulIr Uzt)) => /IHrs. +Qed. + +Theorem max_ring_poly_roots p rs : + p != 0 -> all (root p) rs -> uniq_roots rs -> size rs < size p. +Proof. +move=> nz_p _ /(@uniq_roots_prod_XsubC p)[// | q def_p]; rewrite def_p in nz_p *. +have nz_q: q != 0 by apply: contraNneq nz_p => ->; rewrite mul0r. +rewrite size_Mmonic ?monic_prod_XsubC // (polySpred nz_q) addSn /=. +by rewrite size_prod_XsubC leq_addl. +Qed. + +Lemma all_roots_prod_XsubC p rs : + size p = (size rs).+1 -> all (root p) rs -> uniq_roots rs -> + p = lead_coef p *: \prod_(z <- rs) ('X - z%:P). +Proof. +move=> size_p /uniq_roots_prod_XsubC def_p Urs. +case/def_p: Urs => q -> {p def_p} in size_p *. +have [q0 | nz_q] := eqVneq q 0; first by rewrite q0 mul0r size_poly0 in size_p. +have{q nz_q size_p} /size_poly1P[c _ ->]: size q == 1%N. + rewrite -(eqn_add2r (size rs)) add1n -size_p. + by rewrite size_Mmonic ?monic_prod_XsubC // size_prod_XsubC addnS. +by rewrite lead_coef_Mmonic ?monic_prod_XsubC // lead_coefC mul_polyC. +Qed. + +End MaxRoots. + +Section FieldRoots. + +Variable F : fieldType. +Implicit Types (p : {poly F}) (rs : seq F). + +Lemma poly2_root p : size p = 2 -> {r | root p r}. +Proof. +case: p => [[|p0 [|p1 []]] //= nz_p1]; exists (- p0 / p1). +by rewrite /root addr_eq0 /= mul0r add0r mulrC divfK ?opprK. +Qed. + +Lemma uniq_rootsE rs : uniq_roots rs = uniq rs. +Proof. +elim: rs => //= r rs ->; congr (_ && _); rewrite -has_pred1 -all_predC. +by apply: eq_all => t; rewrite /diff_roots mulrC eqxx unitfE subr_eq0. +Qed. + +Theorem max_poly_roots p rs : + p != 0 -> all (root p) rs -> uniq rs -> size rs < size p. +Proof. by rewrite -uniq_rootsE; exact: max_ring_poly_roots. Qed. + +Section UnityRoots. + +Variable n : nat. + +Lemma max_unity_roots rs : + n > 0 -> all n.-unity_root rs -> uniq rs -> size rs <= n. +Proof. +move=> n_gt0 rs_n_1 Urs; have szPn := size_Xn_sub_1 F n_gt0. +by rewrite -ltnS -szPn max_poly_roots -?size_poly_eq0 ?szPn. +Qed. + +Lemma mem_unity_roots rs : + n > 0 -> all n.-unity_root rs -> uniq rs -> size rs = n -> + n.-unity_root =i rs. +Proof. +move=> n_gt0 rs_n_1 Urs sz_rs_n x; rewrite -topredE /=. +apply/idP/idP=> xn1; last exact: (allP rs_n_1). +apply: contraFT (ltnn n) => not_rs_x. +by rewrite -{1}sz_rs_n (@max_unity_roots (x :: rs)) //= ?xn1 ?not_rs_x. +Qed. + +(* Showing the existence of a primitive root requires the theory in cyclic. *) + +Variable z : F. +Hypothesis prim_z : n.-primitive_root z. + +Let zn := [seq z ^+ i | i <- index_iota 0 n]. + +Lemma factor_Xn_sub_1 : \prod_(0 <= i < n) ('X - (z ^+ i)%:P) = 'X^n - 1. +Proof. +transitivity (\prod_(w <- zn) ('X - w%:P)); first by rewrite big_map. +have n_gt0: n > 0 := prim_order_gt0 prim_z. +rewrite (@all_roots_prod_XsubC _ ('X^n - 1) zn); first 1 last. +- by rewrite size_Xn_sub_1 // size_map size_iota subn0. +- apply/allP=> _ /mapP[i _ ->] /=; rewrite rootE !hornerE hornerXn. + by rewrite exprAC (prim_expr_order prim_z) expr1n subrr. +- rewrite uniq_rootsE map_inj_in_uniq ?iota_uniq // => i j. + rewrite !mem_index_iota => ltin ltjn /eqP. + by rewrite (eq_prim_root_expr prim_z) !modn_small // => /eqP. +by rewrite (monicP (monic_Xn_sub_1 F n_gt0)) scale1r. +Qed. + +Lemma prim_rootP x : x ^+ n = 1 -> {i : 'I_n | x = z ^+ i}. +Proof. +move=> xn1; pose logx := [pred i : 'I_n | x == z ^+ i]. +case: (pickP logx) => [i /eqP-> | no_i]; first by exists i. +case: notF; suffices{no_i}: x \in zn. + case/mapP=> i; rewrite mem_index_iota => lt_i_n def_x. + by rewrite -(no_i (Ordinal lt_i_n)) /= -def_x. +rewrite -root_prod_XsubC big_map factor_Xn_sub_1. +by rewrite [root _ x]unity_rootE xn1. +Qed. + +End UnityRoots. + +End FieldRoots. + +Section MapPolyRoots. + +Variables (F : fieldType) (R : unitRingType) (f : {rmorphism F -> R}). + +Lemma map_diff_roots x y : diff_roots (f x) (f y) = (x != y). +Proof. +rewrite /diff_roots -rmorphB // fmorph_unit // subr_eq0 //. +by rewrite rmorph_comm // eqxx eq_sym. +Qed. + +Lemma map_uniq_roots s : uniq_roots (map f s) = uniq s. +Proof. +elim: s => //= x s ->; congr (_ && _); elim: s => //= y s ->. +by rewrite map_diff_roots -negb_or. +Qed. + +End MapPolyRoots. + +Section AutPolyRoot. +(* The action of automorphisms on roots of unity. *) + +Variable F : fieldType. +Implicit Types u v : {rmorphism F -> F}. + +Lemma aut_prim_rootP u z n : + n.-primitive_root z -> {k | coprime k n & u z = z ^+ k}. +Proof. +move=> prim_z; have:= prim_z; rewrite -(fmorph_primitive_root u) => prim_uz. +have [[k _] /= def_uz] := prim_rootP prim_z (prim_expr_order prim_uz). +by exists k; rewrite // -(prim_root_exp_coprime _ prim_z) -def_uz. +Qed. + +Lemma aut_unity_rootP u z n : n > 0 -> z ^+ n = 1 -> {k | u z = z ^+ k}. +Proof. +by move=> _ /prim_order_exists[// | m /(aut_prim_rootP u)[k]]; exists k. +Qed. + +Lemma aut_unity_rootC u v z n : n > 0 -> z ^+ n = 1 -> u (v z) = v (u z). +Proof. +move=> n_gt0 /(aut_unity_rootP _ n_gt0) def_z. +have [[i def_uz] [j def_vz]] := (def_z u, def_z v). +by rewrite !(def_uz, def_vz, rmorphX) exprAC. +Qed. + +End AutPolyRoot. + +Module UnityRootTheory. + +Notation "n .-unity_root" := (root_of_unity n) : unity_root_scope. +Notation "n .-primitive_root" := (primitive_root_of_unity n) : unity_root_scope. +Open Scope unity_root_scope. + +Definition unity_rootE := unity_rootE. +Definition unity_rootP := @unity_rootP. +Implicit Arguments unity_rootP [R n z]. + +Definition prim_order_exists := prim_order_exists. +Notation prim_order_gt0 := prim_order_gt0. +Notation prim_expr_order := prim_expr_order. +Definition prim_expr_mod := prim_expr_mod. +Definition prim_order_dvd := prim_order_dvd. +Definition eq_prim_root_expr := eq_prim_root_expr. + +Definition rmorph_unity_root := rmorph_unity_root. +Definition fmorph_unity_root := fmorph_unity_root. +Definition fmorph_primitive_root := fmorph_primitive_root. +Definition max_unity_roots := max_unity_roots. +Definition mem_unity_roots := mem_unity_roots. +Definition prim_rootP := prim_rootP. + +End UnityRootTheory. + +Module PreClosedField. +Section UseAxiom. + +Variable F : fieldType. +Hypothesis closedF : GRing.ClosedField.axiom F. +Implicit Type p : {poly F}. + +Lemma closed_rootP p : reflect (exists x, root p x) (size p != 1%N). +Proof. +have [-> | nz_p] := eqVneq p 0. + by rewrite size_poly0; left; exists 0; rewrite root0. +rewrite neq_ltn {1}polySpred //=. +apply: (iffP idP) => [p_gt1 | [a]]; last exact: root_size_gt1. +pose n := (size p).-1; have n_gt0: n > 0 by rewrite -ltnS -polySpred. +have [a Dan] := closedF (fun i => - p`_i / lead_coef p) n_gt0. +exists a; apply/rootP; rewrite horner_coef polySpred // big_ord_recr /= -/n. +rewrite {}Dan mulr_sumr -big_split big1 //= => i _. +by rewrite -!mulrA mulrCA mulNr mulVKf ?subrr ?lead_coef_eq0. +Qed. + +Lemma closed_nonrootP p : reflect (exists x, ~~ root p x) (p != 0). +Proof. +apply: (iffP idP) => [nz_p | [x]]; last first. + by apply: contraNneq => ->; apply: root0. +have [[x /rootP p1x0]|] := altP (closed_rootP (p - 1)). + by exists x; rewrite -[p](subrK 1) /root hornerD p1x0 add0r hornerC oner_eq0. +rewrite negbK => /size_poly1P[c _ /(canRL (subrK 1)) Dp]. +by exists 0; rewrite Dp -raddfD polyC_eq0 rootC in nz_p *. +Qed. + +End UseAxiom. +End PreClosedField. + +Section ClosedField. + +Variable F : closedFieldType. +Implicit Type p : {poly F}. + +Let closedF := @solve_monicpoly F. + +Lemma closed_rootP p : reflect (exists x, root p x) (size p != 1%N). +Proof. exact: PreClosedField.closed_rootP. Qed. + +Lemma closed_nonrootP p : reflect (exists x, ~~ root p x) (p != 0). +Proof. exact: PreClosedField.closed_nonrootP. Qed. + +Lemma closed_field_poly_normal p : + {r : seq F | p = lead_coef p *: \prod_(z <- r) ('X - z%:P)}. +Proof. +apply: sig_eqW; elim: {p}_.+1 {-2}p (ltnSn (size p)) => // n IHn p le_p_n. +have [/size1_polyC-> | p_gt1] := leqP (size p) 1. + by exists nil; rewrite big_nil lead_coefC alg_polyC. +have [|x /factor_theorem[q Dp]] := closed_rootP p _; first by rewrite gtn_eqF. +have nz_p: p != 0 by rewrite -size_poly_eq0 -(subnKC p_gt1). +have:= nz_p; rewrite Dp mulf_eq0 lead_coefM => /norP[nz_q nz_Xx]. +rewrite ltnS polySpred // Dp size_mul // size_XsubC addn2 in le_p_n. +have [r {1}->] := IHn q le_p_n; exists (x :: r). +by rewrite lead_coefXsubC mulr1 big_cons -scalerAl mulrC. +Qed. + +End ClosedField. |
