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.
Definition poly_nil := @Polynomial R [::] (oner_neq0 R).
Definition polyC c : {poly R} := insubd poly_nil [:: c].
Local Notation "c %:P" := (polyC c).
(* 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.
(* Build a polynomial directly from a list of coefficients. *)
Definition Poly := foldr cons_poly 0%:P.
(* 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)).
(* 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.
Fact coef_opp_poly p i : (opp_poly p)`_i = - p`_i.
Fact add_polyA : associative add_poly.
Fact add_polyN : left_inverse 0%:P opp_poly add_poly.
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.
(* Size, leading coef, morphism properties of coef *)
Fact coef_mul_poly_rev p q i :
(mul_poly p q)`_i = \sum_(j < i.+1) p`_(i - j)%N * q`_j.
Fact mul_polyA : associative mul_poly.
Fact mul_1poly : left_id 1%:P mul_poly.
Fact mul_poly1 : right_id 1%:P mul_poly.
Fact mul_polyDl : left_distributive mul_poly +%R.
Fact mul_polyDr : right_distributive mul_poly +%R.
Fact poly1_neq0 : 1%:P != 0 :> {poly R}.
Fact coefp0_multiplicative : multiplicative (coefp 0 : {poly R} -> R).
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.
Fact scale_polyA a b p : scale_poly a (scale_poly b p) = scale_poly (a * b) p.
Local Notation "''X^' n" := ('X ^+ n).
(* 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.
(* Some facts about regular elements. *)

Lemma monic_comreg p :
p \is monic -> GRing.comm p (lead_coef p)%:P /\ GRing.rreg (lead_coef p).