aboutsummaryrefslogtreecommitdiff
path: root/mathcomp/field/separable.v
diff options
context:
space:
mode:
Diffstat (limited to 'mathcomp/field/separable.v')
-rw-r--r--mathcomp/field/separable.v995
1 files changed, 995 insertions, 0 deletions
diff --git a/mathcomp/field/separable.v b/mathcomp/field/separable.v
new file mode 100644
index 0000000..9638500
--- /dev/null
+++ b/mathcomp/field/separable.v
@@ -0,0 +1,995 @@
+(* (c) Copyright Microsoft Corporation and Inria. All rights reserved. *)
+Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq div choice fintype.
+Require Import tuple finfun bigop finset prime binomial ssralg poly polydiv.
+Require Import fingroup perm morphism quotient gproduct finalg zmodp cyclic.
+Require Import matrix mxalgebra mxpoly polyXY vector falgebra fieldext.
+
+(******************************************************************************)
+(* This file provides a theory of separable and inseparable field extensions. *)
+(* *)
+(* separable_poly p <=> p has no multiple roots in any field extension. *)
+(* separable_element K x <=> the minimal polynomial of x over K is separable. *)
+(* separable K E <=> every member of E is separable over K. *)
+(* separable_generator K E == some x \in E that generates the largest *)
+(* subfield K[x] that is separable over K. *)
+(* purely_inseparable_element K x <=> there is a [char L].-nat n such that *)
+(* x ^+ n \in K. *)
+(* purely_inseparable K E <=> every member of E is purely inseparable over K. *)
+(* *)
+(* Derivations are introduced to prove the adjoin_separableP Lemma: *)
+(* Derivation K D <=> the linear operator D satifies the Leibniz *)
+(* product rule inside K. *)
+(* extendDerivation x D K == given a derivation D on K and a separable *)
+(* element x over K, this function returns the *)
+(* unique extension of D to K(x). *)
+(******************************************************************************)
+
+Set Implicit Arguments.
+Unset Strict Implicit.
+Unset Printing Implicit Defensive.
+
+Open Local Scope ring_scope.
+Import GRing.Theory.
+
+Section SeparablePoly.
+
+Variable R : idomainType.
+Implicit Types p q d u v : {poly R}.
+
+Definition separable_poly p := coprimep p p^`().
+
+Local Notation separable := separable_poly.
+Local Notation lcn_neq0 := (Pdiv.Idomain.lc_expn_scalp_neq0 _).
+
+Lemma separable_poly_neq0 p : separable p -> p != 0.
+Proof.
+by apply: contraTneq => ->; rewrite /separable deriv0 coprime0p eqp01.
+Qed.
+
+Lemma poly_square_freeP p :
+ (forall u v, u * v %| p -> coprimep u v)
+ <-> (forall u, size u != 1%N -> ~~ (u ^+ 2 %| p)).
+Proof.
+split=> [sq'p u | sq'p u v dvd_uv_p].
+ by apply: contra => /sq'p; rewrite coprimepp.
+rewrite coprimep_def (contraLR (sq'p _)) // (dvdp_trans _ dvd_uv_p) //.
+by rewrite dvdp_mul ?dvdp_gcdl ?dvdp_gcdr.
+Qed.
+
+Lemma separable_polyP {p} :
+ reflect [/\ forall u v, u * v %| p -> coprimep u v
+ & forall u, u %| p -> 1 < size u -> u^`() != 0]
+ (separable p).
+Proof.
+apply: (iffP idP) => [sep_p | [sq'p nz_der1p]].
+ split=> [u v | u u_dv_p]; last first.
+ apply: contraTneq => u'0; rewrite -leqNgt -(eqnP sep_p).
+ rewrite dvdp_leq -?size_poly_eq0 ?(eqnP sep_p) // dvdp_gcd u_dv_p.
+ have /dvdp_scaler <-: lead_coef u ^+ scalp p u != 0 by rewrite lcn_neq0.
+ by rewrite -derivZ -Pdiv.Idomain.divpK //= derivM u'0 mulr0 addr0 dvdp_mull.
+ rewrite Pdiv.Idomain.dvdp_eq mulrCA mulrA; set c := _ ^+ _ => /eqP Dcp.
+ have nz_c: c != 0 by rewrite lcn_neq0.
+ move: sep_p; rewrite coprimep_sym -[separable _](coprimep_scalel _ _ nz_c).
+ rewrite -(coprimep_scaler _ _ nz_c) -derivZ Dcp derivM coprimep_mull.
+ by rewrite coprimep_addl_mul !coprimep_mulr -andbA => /and4P[].
+rewrite /separable coprimep_def eqn_leq size_poly_gt0; set g := gcdp _ _.
+have nz_g: g != 0.
+ rewrite -dvd0p dvdp_gcd -(mulr0 0); apply/nandP; left.
+ by have /poly_square_freeP-> := sq'p; rewrite ?size_poly0.
+have [g_p]: g %| p /\ g %| p^`() by rewrite dvdp_gcdr ?dvdp_gcdl.
+pose c := lead_coef g ^+ scalp p g; have nz_c: c != 0 by rewrite lcn_neq0.
+have Dcp: c *: p = p %/ g * g by rewrite Pdiv.Idomain.divpK.
+rewrite nz_g andbT leqNgt -(dvdp_scaler _ _ nz_c) -derivZ Dcp derivM.
+rewrite dvdp_addr; last by rewrite dvdp_mull.
+rewrite Gauss_dvdpr; last by rewrite sq'p // mulrC -Dcp dvdp_scalel.
+by apply: contraL => /nz_der1p nz_g'; rewrite gtNdvdp ?nz_g' ?lt_size_deriv.
+Qed.
+
+Lemma separable_coprime p u v : separable p -> u * v %| p -> coprimep u v.
+Proof. by move=> /separable_polyP[sq'p _] /sq'p. Qed.
+
+Lemma separable_nosquare p u k :
+ separable p -> 1 < k -> size u != 1%N -> (u ^+ k %| p) = false.
+Proof.
+move=> /separable_polyP[/poly_square_freeP sq'p _] /subnKC <- /sq'p.
+by apply: contraNF; apply: dvdp_trans; rewrite exprD dvdp_mulr.
+Qed.
+
+Lemma separable_deriv_eq0 p u :
+ separable p -> u %| p -> 1 < size u -> (u^`() == 0) = false.
+Proof. by move=> /separable_polyP[_ nz_der1p] u_p /nz_der1p/negPf->. Qed.
+
+Lemma dvdp_separable p q : q %| p -> separable p -> separable q.
+Proof.
+move=> /(dvdp_trans _)q_dv_p /separable_polyP[sq'p nz_der1p].
+by apply/separable_polyP; split=> [u v /q_dv_p/sq'p | u /q_dv_p/nz_der1p].
+Qed.
+
+Lemma separable_mul p q :
+ separable (p * q) = [&& separable p, separable q & coprimep p q].
+Proof.
+apply/idP/and3P => [sep_pq | [sep_p seq_q co_pq]].
+ rewrite !(dvdp_separable _ sep_pq) ?dvdp_mulIr ?dvdp_mulIl //.
+ by rewrite (separable_coprime sep_pq).
+rewrite /separable derivM coprimep_mull {1}addrC mulrC !coprimep_addl_mul.
+by rewrite !coprimep_mulr (coprimep_sym q p) co_pq !andbT; apply/andP.
+Qed.
+
+Lemma eqp_separable p q : p %= q -> separable p = separable q.
+Proof. by case/andP=> p_q q_p; apply/idP/idP=> /dvdp_separable->. Qed.
+
+Lemma separable_root p x :
+ separable (p * ('X - x%:P)) = separable p && ~~ root p x.
+Proof.
+rewrite separable_mul; apply: andb_id2l => seq_p.
+by rewrite /separable derivXsubC coprimep1 coprimep_XsubC.
+Qed.
+
+Lemma separable_prod_XsubC (r : seq R) :
+ separable (\prod_(x <- r) ('X - x%:P)) = uniq r.
+Proof.
+elim: r => [|x r IH]; first by rewrite big_nil /separable_poly coprime1p.
+by rewrite big_cons mulrC separable_root IH root_prod_XsubC andbC.
+Qed.
+
+Lemma make_separable p : p != 0 -> separable (p %/ gcdp p p^`()).
+Proof.
+set g := gcdp p p^`() => nz_p; apply/separable_polyP.
+have max_dvd_u (u : {poly R}): 1 < size u -> exists k, ~~ (u ^+ k %| p).
+ move=> u_gt1; exists (size p); rewrite gtNdvdp // polySpred //.
+ by rewrite -(ltn_subRL 1) subn1 size_exp leq_pmull // -(subnKC u_gt1).
+split=> [|u u_pg u_gt1]; last first.
+ apply/eqP=> u'0 /=; have [k /negP[]] := max_dvd_u u u_gt1.
+ elim: k => [|k IHk]; first by rewrite dvd1p.
+ suffices: u ^+ k.+1 %| (p %/ g) * g.
+ by rewrite Pdiv.Idomain.divpK ?dvdp_gcdl // dvdp_scaler ?lcn_neq0.
+ rewrite exprS dvdp_mul // dvdp_gcd IHk //=.
+ suffices: u ^+ k %| (p %/ u ^+ k * u ^+ k)^`().
+ by rewrite Pdiv.Idomain.divpK // derivZ dvdp_scaler ?lcn_neq0.
+ by rewrite !derivCE u'0 mul0r mul0rn mulr0 addr0 dvdp_mull.
+have pg_dv_p: p %/ g %| p by rewrite divp_dvd ?dvdp_gcdl.
+apply/poly_square_freeP=> u; rewrite neq_ltn ltnS leqn0 size_poly_eq0.
+case/predU1P=> [-> | /max_dvd_u[k]].
+ by apply: contra nz_p; rewrite expr0n -dvd0p => /dvdp_trans->.
+apply: contra => u2_dv_pg; case: k; [by rewrite dvd1p | elim=> [|n IHn]].
+ exact: dvdp_trans (dvdp_mulr _ _) (dvdp_trans u2_dv_pg pg_dv_p).
+suff: u ^+ n.+2 %| (p %/ g) * g.
+ by rewrite Pdiv.Idomain.divpK ?dvdp_gcdl // dvdp_scaler ?lcn_neq0.
+rewrite -add2n exprD dvdp_mul // dvdp_gcd.
+rewrite (dvdp_trans _ IHn) ?exprS ?dvdp_mull //=.
+suff: u ^+ n %| ((p %/ u ^+ n.+1) * u ^+ n.+1)^`().
+ by rewrite Pdiv.Idomain.divpK // derivZ dvdp_scaler ?lcn_neq0.
+by rewrite !derivCE dvdp_add // -1?mulr_natl ?exprS !dvdp_mull.
+Qed.
+
+End SeparablePoly.
+
+Implicit Arguments separable_polyP [R p].
+
+Lemma separable_map (F : fieldType) (R : idomainType)
+ (f : {rmorphism F -> R}) (p : {poly F}) :
+ separable_poly (map_poly f p) = separable_poly p.
+Proof.
+by rewrite /separable_poly deriv_map /coprimep -gcdp_map size_map_poly.
+Qed.
+
+Section InfinitePrimitiveElementTheorem.
+
+Local Notation "p ^ f" := (map_poly f p) : ring_scope.
+
+Variables (F L : fieldType) (iota : {rmorphism F -> L}).
+Variables (x y : L) (p : {poly F}).
+Hypotheses (nz_p : p != 0) (px_0 : root (p ^ iota) x).
+
+Let inFz z w := exists q, (q ^ iota).[z] = w.
+
+Lemma large_field_PET q :
+ root (q ^ iota) y -> separable_poly q ->
+ exists2 r, r != 0
+ & forall t (z := iota t * y - x), ~~ root r (iota t) -> inFz z x /\ inFz z y.
+Proof.
+move=> qy_0 sep_q; have nz_q := separable_poly_neq0 sep_q.
+have /factor_theorem[q0 Dq] := qy_0.
+set p1 := p ^ iota \Po ('X + x%:P); set q1 := q0 \Po ('X + y%:P).
+have nz_p1: p1 != 0.
+ apply: contraNneq nz_p => /(canRL (fun r => comp_polyXaddC_K r _))/eqP.
+ by rewrite comp_poly0 map_poly_eq0.
+have{sep_q} nz_q10: q1.[0] != 0.
+ move: sep_q; rewrite -(separable_map iota) Dq separable_root => /andP[_].
+ by rewrite horner_comp !hornerE.
+have nz_q1: q1 != 0 by apply: contraNneq nz_q10 => ->; rewrite horner0.
+pose p2 := p1 ^ polyC \Po ('X * 'Y); pose q2 := q1 ^ polyC.
+have /Bezout_coprimepP[[u v]]: coprimep p2 q2.
+ rewrite coprimep_def eqn_leq leqNgt andbC size_poly_gt0 gcdp_eq0 poly_XmY_eq0.
+ by rewrite map_polyC_eq0 (negPf nz_p1) -resultant_eq0 div_annihilant_neq0.
+rewrite -size_poly_eq1 => /size_poly1P[r nzr Dr]; exists r => {nzr}// t z nz_rt.
+have [r1 nz_r1 r1z_0]: algebraicOver iota z.
+ apply/algebraic_sub; last by exists p.
+ by apply: algebraic_mul; [apply: algebraic_id | exists q].
+pose Fz := subFExtend iota z r1; pose kappa : Fz -> L := subfx_inj.
+pose kappa' := inj_subfx iota z r1.
+have /eq_map_poly Diota: kappa \o kappa' =1 iota.
+ by move=> w; rewrite /kappa /= subfx_inj_eval // map_polyC hornerC.
+suffices [y3]: exists y3, y = kappa y3.
+ have [q3 ->] := subfxE y3; rewrite /kappa subfx_inj_eval // => Dy.
+ split; [exists (t *: q3 - 'X) | by exists q3].
+ by rewrite rmorphB linearZ /= map_polyX !hornerE -Dy opprB addrC addrNK.
+pose p0 := p ^ iota \Po (iota t *: 'X - z%:P).
+have co_p0_q0: coprimep p0 q0.
+ pose at_t := horner_eval (iota t); have at_t0: at_t 0 = 0 by apply: rmorph0.
+ have /map_polyK polyCK: cancel polyC at_t by move=> w; apply: hornerC.
+ have ->: p0 = p2 ^ at_t \Po ('X - y%:P).
+ rewrite map_comp_poly polyCK // rmorphM /= map_polyC map_polyX /=.
+ rewrite horner_evalE hornerX.
+ rewrite -!comp_polyA comp_polyM comp_polyD !comp_polyC !comp_polyX.
+ by rewrite mulrC mulrBr mul_polyC addrAC -addrA -opprB -rmorphM -rmorphB.
+ have ->: q0 = q2 ^ at_t \Po ('X - y%:P) by rewrite polyCK ?comp_polyXaddC_K.
+ apply/coprimep_comp_poly/Bezout_coprimepP; exists (u ^ at_t, v ^ at_t).
+ by rewrite -!rmorphM -rmorphD Dr /= map_polyC polyC_eqp1.
+have{co_p0_q0}: gcdp p0 (q ^ iota) %= 'X - y%:P.
+ rewrite /eqp Dq (eqp_dvdl _ (Gauss_gcdpr _ _)) // dvdp_gcdr dvdp_gcd.
+ rewrite dvdp_mull // -root_factor_theorem rootE horner_comp !hornerE.
+ by rewrite opprB addrC subrK.
+have{p0} [p3 ->]: exists p3, p0 = p3 ^ kappa.
+ exists (p ^ kappa' \Po (kappa' t *: 'X - (subfx_eval iota z r1 'X)%:P)).
+ rewrite map_comp_poly rmorphB linearZ /= map_polyC map_polyX /=.
+ rewrite !subfx_inj_eval // map_polyC hornerC map_polyX hornerX.
+ by rewrite -map_poly_comp Diota.
+rewrite -Diota map_poly_comp -gcdp_map /= -/kappa.
+move: (gcdp _ _) => r3 /eqpf_eq[c nz_c Dr3].
+exists (- (r3`_0 / r3`_1)); rewrite [kappa _]rmorphN fmorph_div -!coef_map Dr3.
+by rewrite !coefZ polyseqXsubC mulr1 mulrC mulKf ?opprK.
+Qed.
+
+Lemma char0_PET (q : {poly F}) :
+ q != 0 -> root (q ^ iota) y -> [char F] =i pred0 ->
+ exists n, let z := y *+ n - x in inFz z x /\ inFz z y.
+Proof.
+move=> nz_q qy_0 /charf0P charF0.
+without loss{nz_q} sep_q: q qy_0 / separable_poly q.
+ move=> IHq; apply: IHq (make_separable nz_q).
+ have /dvdpP[q1 Dq] := dvdp_gcdl q q^`().
+ rewrite {1}Dq mulpK ?gcdp_eq0; last by apply/nandP; left.
+ have [n [r nz_ry Dr]] := multiplicity_XsubC (q ^ iota) y.
+ rewrite map_poly_eq0 nz_q /= in nz_ry.
+ case: n => [|n] in Dr; first by rewrite Dr mulr1 (negPf nz_ry) in qy_0.
+ have: ('X - y%:P) ^+ n.+1 %| q ^ iota by rewrite Dr dvdp_mulIr.
+ rewrite Dq rmorphM /= gcdp_map -(eqp_dvdr _ (gcdp_mul2l _ _ _)) -deriv_map Dr.
+ rewrite dvdp_gcd derivM deriv_exp derivXsubC mul1r !mulrA dvdp_mulIr /=.
+ rewrite mulrDr mulrA dvdp_addr ?dvdp_mulIr // exprS -scaler_nat -!scalerAr.
+ rewrite dvdp_scaler -?(rmorph_nat iota) ?fmorph_eq0 ?charF0 //.
+ rewrite mulrA dvdp_mul2r ?expf_neq0 ?polyXsubC_eq0 //.
+ by rewrite Gauss_dvdpl ?dvdp_XsubCl // coprimep_sym coprimep_XsubC.
+have [r nz_r PETxy] := large_field_PET qy_0 sep_q.
+pose ts := mkseq (fun n => iota n%:R) (size r).
+have /(max_ring_poly_roots nz_r)/=/implyP: uniq_roots ts.
+ rewrite uniq_rootsE mkseq_uniq // => m n eq_mn; apply/eqP; rewrite eqn_leq.
+ wlog suffices: m n eq_mn / m <= n by move=> IHmn; rewrite !IHmn.
+ move/fmorph_inj/eqP: eq_mn; rewrite -subr_eq0 leqNgt; apply: contraL => lt_mn.
+ by rewrite -natrB ?(ltnW lt_mn) // charF0 -lt0n subn_gt0.
+rewrite size_mkseq ltnn implybF all_map => /allPn[n _ /= /PETxy].
+by rewrite rmorph_nat mulr_natl; exists n.
+Qed.
+
+End InfinitePrimitiveElementTheorem.
+
+Section Separable.
+
+Variables (F : fieldType) (L : fieldExtType F).
+Implicit Types (U V W : {vspace L}) (E K M : {subfield L}) (D : 'End(L)).
+
+Section Derivation.
+
+Variables (K : {vspace L}) (D : 'End(L)).
+
+(* A deriviation only needs to be additive and satify Lebniz's law, but all *)
+(* the deriviations used here are going to be linear, so we only define *)
+(* the Derivation predicate for linear endomorphisms. *)
+Definition Derivation (s := vbasis K) : bool :=
+ all (fun u => all (fun v => D (u * v) == D u * v + u * D v) s) s.
+
+Hypothesis derD : Derivation.
+
+Lemma Derivation_mul : {in K &, forall u v, D (u * v) = D u * v + u * D v}.
+Proof.
+move=> u v /coord_vbasis-> /coord_vbasis->.
+rewrite !(mulr_sumr, linear_sum) -big_split; apply: eq_bigr => /= j _.
+rewrite !mulr_suml linear_sum -big_split; apply: eq_bigr => /= i _.
+rewrite !(=^~ scalerAl, linearZZ) -!scalerAr linearZZ -!scalerDr !scalerA /=.
+by congr (_ *: _); apply/eqP; rewrite (allP (allP derD _ _)) ?memt_nth.
+Qed.
+
+Lemma Derivation_mul_poly (Dp := map_poly D) :
+ {in polyOver K &, forall p q, Dp (p * q) = Dp p * q + p * Dp q}.
+Proof.
+move=> p q Kp Kq; apply/polyP=> i; rewrite {}/Dp coefD coef_map /= !coefM.
+rewrite linear_sum -big_split; apply: eq_bigr => /= j _.
+by rewrite !{1}coef_map Derivation_mul ?(polyOverP _).
+Qed.
+
+End Derivation.
+
+Lemma DerivationS E K D : (K <= E)%VS -> Derivation E D -> Derivation K D.
+Proof.
+move/subvP=> sKE derD; apply/allP=> x Kx; apply/allP=> y Ky; apply/eqP.
+by rewrite (Derivation_mul derD) ?sKE // vbasis_mem.
+Qed.
+
+Section DerivationAlgebra.
+
+Variables (E : {subfield L}) (D : 'End(L)).
+Hypothesis derD : Derivation E D.
+
+Lemma Derivation1 : D 1 = 0.
+Proof.
+apply: (addIr (D (1 * 1))); rewrite add0r {1}mul1r.
+by rewrite (Derivation_mul derD) ?mem1v // mulr1 mul1r.
+Qed.
+
+Lemma Derivation_scalar x : x \in 1%VS -> D x = 0.
+Proof. by case/vlineP=> y ->; rewrite linearZ /= Derivation1 scaler0. Qed.
+
+Lemma Derivation_exp x m : x \in E -> D (x ^+ m) = x ^+ m.-1 *+ m * D x.
+Proof.
+move=> Ex; case: m; first by rewrite expr0 mulr0n mul0r Derivation1.
+elim=> [|m IHm]; first by rewrite mul1r.
+rewrite exprS (Derivation_mul derD) //; last by apply: rpredX.
+by rewrite mulrC IHm mulrA mulrnAr -exprS -mulrDl.
+Qed.
+
+Lemma Derivation_horner p x :
+ p \is a polyOver E -> x \in E ->
+ D p.[x] = (map_poly D p).[x] + p^`().[x] * D x.
+Proof.
+move=> Ep Ex; elim/poly_ind: p Ep => [|p c IHp] /polyOverP EpXc.
+ by rewrite !(raddf0, horner0) mul0r add0r.
+have Ep: p \is a polyOver E.
+ by apply/polyOverP=> i; have:= EpXc i.+1; rewrite coefD coefMX coefC addr0.
+have->: map_poly D (p * 'X + c%:P) = map_poly D p * 'X + (D c)%:P.
+ apply/polyP=> i; rewrite !(coefD, coefMX, coef_map) /= linearD /= !coefC.
+ by rewrite !(fun_if D) linear0.
+rewrite derivMXaddC !hornerE mulrDl mulrAC addrAC linearD /=; congr (_ + _).
+by rewrite addrCA -mulrDl -IHp // addrC (Derivation_mul derD) ?rpred_horner.
+Qed.
+
+End DerivationAlgebra.
+
+Definition separable_element U x := separable_poly (minPoly U x).
+
+Section SeparableElement.
+
+Variables (K : {subfield L}) (x : L).
+(* begin hide *)
+Let sKxK : (K <= <<K; x>>)%VS := subv_adjoin K x.
+Let Kx_x : x \in <<K; x>>%VS := memv_adjoin K x.
+(* end hide *)
+
+Lemma separable_elementP :
+ reflect (exists f, [/\ f \is a polyOver K, root f x & separable_poly f])
+ (separable_element K x).
+Proof.
+apply: (iffP idP) => [sep_x | [f [Kf /(minPoly_dvdp Kf)/dvdpP[g ->]]]].
+ by exists (minPoly K x); rewrite minPolyOver root_minPoly.
+by rewrite separable_mul => /and3P[].
+Qed.
+
+Lemma base_separable : x \in K -> separable_element K x.
+Proof.
+move=> Kx; apply/separable_elementP; exists ('X - x%:P).
+by rewrite polyOverXsubC root_XsubC /separable_poly !derivCE coprimep1.
+Qed.
+
+Lemma separable_nz_der : separable_element K x = ((minPoly K x)^`() != 0).
+Proof.
+rewrite /separable_element /separable_poly.
+apply/idP/idP=> [|nzPx'].
+ by apply: contraTneq => ->; rewrite coprimep0 -size_poly_eq1 size_minPoly.
+have gcdK : gcdp (minPoly K x) (minPoly K x)^`() \in polyOver K.
+ by rewrite gcdp_polyOver ?polyOver_deriv // minPolyOver.
+rewrite -gcdp_eqp1 -size_poly_eq1 -dvdp1.
+have /orP[/andP[_]|/andP[]//] := minPoly_irr gcdK (dvdp_gcdl _ _).
+rewrite dvdp_gcd dvdpp /= => /(dvdp_leq nzPx')/leq_trans/(_ (size_poly _ _)).
+by rewrite size_minPoly ltnn.
+Qed.
+
+Lemma separablePn :
+ reflect (exists2 p, p \in [char L] &
+ exists2 g, g \is a polyOver K & minPoly K x = g \Po 'X^p)
+ (~~ separable_element K x).
+Proof.
+rewrite separable_nz_der negbK; set f := minPoly K x.
+apply: (iffP eqP) => [f'0 | [p Hp [g _ ->]]]; last first.
+ by rewrite deriv_comp derivXn -scaler_nat (charf0 Hp) scale0r mulr0.
+pose n := adjoin_degree K x; have sz_f: size f = n.+1 := size_minPoly K x.
+have fn1: f`_n = 1 by rewrite -(monicP (monic_minPoly K x)) lead_coefE sz_f.
+have dimKx: (adjoin_degree K x)%:R == 0 :> L.
+ by rewrite -(coef0 _ n.-1) -f'0 coef_deriv fn1.
+have /natf0_char[// | p charLp] := dimKx.
+have /dvdnP[r Dn]: (p %| n)%N by rewrite (dvdn_charf charLp).
+exists p => //; exists (\poly_(i < r.+1) f`_(i * p)).
+ by apply: polyOver_poly => i _; rewrite (polyOverP _) ?minPolyOver.
+rewrite comp_polyE size_poly_eq -?Dn ?fn1 ?oner_eq0 //.
+have pr_p := charf_prime charLp; have p_gt0 := prime_gt0 pr_p.
+apply/polyP=> i; rewrite coef_sum.
+have [[{i} i ->] | p'i] := altP (@dvdnP p i); last first.
+ rewrite big1 => [|j _]; last first.
+ rewrite coefZ -exprM coefXn [_ == _](contraNF _ p'i) ?mulr0 // => /eqP->.
+ by rewrite dvdn_mulr.
+ rewrite (dvdn_charf charLp) in p'i; apply: mulfI p'i _ _ _.
+ by rewrite mulr0 mulr_natl; case: i => // i; rewrite -coef_deriv f'0 coef0.
+have [ltri | leir] := leqP r.+1 i.
+ rewrite nth_default ?sz_f ?Dn ?ltn_pmul2r ?big1 // => j _.
+ rewrite coefZ -exprM coefXn mulnC gtn_eqF ?mulr0 //.
+ by rewrite ltn_pmul2l ?(leq_trans _ ltri).
+rewrite (bigD1 (Sub i _)) //= big1 ?addr0 => [|j i'j]; last first.
+ by rewrite coefZ -exprM coefXn mulnC eqn_pmul2l // mulr_natr mulrb ifN_eqC.
+by rewrite coef_poly leir coefZ -exprM coefXn mulnC eqxx mulr1.
+Qed.
+
+Lemma separable_root_der : separable_element K x (+) root (minPoly K x)^`() x.
+Proof.
+have KpKx': _^`() \is a polyOver K := polyOver_deriv (minPolyOver K x).
+rewrite separable_nz_der addNb (root_small_adjoin_poly KpKx') ?addbb //.
+by rewrite (leq_trans (size_poly _ _)) ?size_minPoly.
+Qed.
+
+Lemma Derivation_separable D :
+ Derivation <<K; x>> D -> separable_element K x ->
+ D x = - (map_poly D (minPoly K x)).[x] / (minPoly K x)^`().[x].
+Proof.
+move=> derD sepKx; have:= separable_root_der; rewrite {}sepKx -sub0r => nzKx'x.
+apply: canRL (mulfK nzKx'x) (canRL (addrK _) _); rewrite mulrC addrC.
+rewrite -(Derivation_horner derD) ?minPolyxx ?linear0 //.
+exact: polyOverSv sKxK _ (minPolyOver _ _).
+Qed.
+
+Section ExtendDerivation.
+
+Variable D : 'End(L).
+
+Let Dx E := - (map_poly D (minPoly E x)).[x] / ((minPoly E x)^`()).[x].
+
+Fact extendDerivation_subproof E (adjEx := Fadjoin_poly E x) :
+ let body y (p := adjEx y) := (map_poly D p).[x] + p^`().[x] * Dx E in
+ linear body.
+Proof.
+move: Dx => C /= a u v.
+rewrite /adjEx linearP /= -mul_polyC derivD derivM derivC mul0r add0r -/adjEx.
+rewrite !hornerE /= -scalerAl mul1r raddfD /=.
+have ->: map_poly D (a%:A%:P * adjEx u) = a%:A%:P * map_poly D (adjEx u).
+ apply/polyP=> i; rewrite !mul_polyC !coef_map !coefZ !mulr_algl /= linearZ.
+ by rewrite coef_map.
+rewrite !hornerE !mulr_algl mulrDl scalerDr -scalerAl -!addrA; congr (_ + _).
+by rewrite addrCA.
+Qed.
+
+Definition extendDerivation E : 'End(L) :=
+ linfun (Linear (extendDerivation_subproof E)).
+
+Hypothesis derD : Derivation K D.
+
+Lemma extendDerivation_id y : y \in K -> extendDerivation K y = D y.
+Proof.
+move=> yK; rewrite lfunE /= Fadjoin_polyC // derivC map_polyC hornerC.
+by rewrite horner0 mul0r addr0.
+Qed.
+
+Lemma extendDerivation_horner p :
+ p \is a polyOver K -> separable_element K x ->
+ extendDerivation K p.[x] = (map_poly D p).[x] + p^`().[x] * Dx K.
+Proof.
+move=> Kp sepKx; have:= separable_root_der; rewrite {}sepKx /= => nz_pKx'x.
+rewrite {-1}(divp_eq p (minPoly K x)) lfunE /= Fadjoin_poly_mod // raddfD /=.
+rewrite {1}(Derivation_mul_poly derD) ?divp_polyOver ?minPolyOver //.
+rewrite derivD derivM !{1}hornerD !{1}hornerM minPolyxx !{1}mulr0 !{1}add0r.
+rewrite mulrDl addrA [_ + (_ * _ * _)]addrC {2}/Dx -mulrA -/Dx.
+by rewrite [_ / _]mulrC (mulVKf nz_pKx'x) mulrN addKr.
+Qed.
+
+Lemma extendDerivationP :
+ separable_element K x -> Derivation <<K; x>> (extendDerivation K).
+Proof.
+move=> sep; apply/allP=> u /vbasis_mem Hu; apply/allP=> v /vbasis_mem Hv.
+apply/eqP.
+rewrite -(Fadjoin_poly_eq Hu) -(Fadjoin_poly_eq Hv) -hornerM.
+rewrite !{1}extendDerivation_horner ?{1}rpredM ?Fadjoin_polyOver //.
+rewrite (Derivation_mul_poly derD) ?Fadjoin_polyOver //.
+rewrite derivM !{1}hornerD !{1}hornerM !{1}mulrDl !{1}mulrDr -!addrA.
+congr (_ + _); rewrite [Dx K]lock -!{1}mulrA !{1}addrA; congr (_ + _).
+by rewrite addrC; congr (_ * _ + _); rewrite mulrC.
+Qed.
+
+End ExtendDerivation.
+
+(* Reference:
+http://www.math.uconn.edu/~kconrad/blurbs/galoistheory/separable2.pdf *)
+Lemma Derivation_separableP :
+ reflect
+ (forall D, Derivation <<K; x>> D -> K <= lker D -> <<K; x>> <= lker D)%VS
+ (separable_element K x).
+Proof.
+apply: (iffP idP) => [sepKx D derD /subvP DK_0 | derKx_0].
+ have{DK_0} DK_0 q: q \is a polyOver K -> map_poly D q = 0.
+ move=> /polyOverP Kq; apply/polyP=> i; apply/eqP.
+ by rewrite coef0 coef_map -memv_ker DK_0.
+ apply/subvP=> _ /Fadjoin_polyP[p Kp ->]; rewrite memv_ker.
+ rewrite (Derivation_horner derD) ?(polyOverSv sKxK) //.
+ rewrite (Derivation_separable derD sepKx) !DK_0 ?minPolyOver //.
+ by rewrite horner0 oppr0 mul0r mulr0 addr0.
+apply: wlog_neg; rewrite {1}separable_nz_der negbK => /eqP pKx'_0.
+have Dlin: linear (fun y => (Fadjoin_poly K x y)^`().[x]).
+ move=> a u v; rewrite linearP /= -mul_polyC derivD derivM derivC mul0r add0r.
+ by rewrite hornerD hornerM hornerC -scalerAl mul1r.
+pose D := linfun (Linear Dlin); apply: base_separable.
+have DK_0: (K <= lker D)%VS.
+ apply/subvP=> v Kv; rewrite memv_ker lfunE /= Fadjoin_polyC //.
+ by rewrite derivC horner0.
+have Dder: Derivation <<K; x>> D.
+ apply/allP=> u /vbasis_mem Kx_u; apply/allP=> v /vbasis_mem Kx_v.
+ rewrite !lfunE /= -{-2}(Fadjoin_poly_eq Kx_u) -{-3}(Fadjoin_poly_eq Kx_v).
+ rewrite -!hornerM -hornerD -derivM.
+ rewrite Fadjoin_poly_mod ?rpredM ?Fadjoin_polyOver //.
+ rewrite {2}(divp_eq (_ * _) (minPoly K x)) derivD derivM pKx'_0 mulr0 addr0.
+ by rewrite hornerD hornerM minPolyxx mulr0 add0r.
+have{Dder DK_0}: x \in lker D by apply: subvP Kx_x; apply: derKx_0.
+apply: contraLR => K'x; rewrite memv_ker lfunE /= Fadjoin_polyX //.
+by rewrite derivX hornerC oner_eq0.
+Qed.
+
+End SeparableElement.
+
+Implicit Arguments separable_elementP [K x].
+
+Lemma separable_elementS K E x :
+ (K <= E)%VS -> separable_element K x -> separable_element E x.
+Proof.
+move=> sKE /separable_elementP[f [fK rootf sepf]]; apply/separable_elementP.
+by exists f; rewrite (polyOverSv sKE).
+Qed.
+
+Lemma adjoin_separableP {K x} :
+ reflect (forall y, y \in <<K; x>>%VS -> separable_element K y)
+ (separable_element K x).
+Proof.
+apply: (iffP idP) => [sepKx | -> //]; last exact: memv_adjoin.
+move=> _ /Fadjoin_polyP[q Kq ->]; apply/Derivation_separableP=> D derD DK_0.
+apply/subvP=> _ /Fadjoin_polyP[p Kp ->].
+rewrite memv_ker -(extendDerivation_id x D (mempx_Fadjoin _ Kp)).
+have sepFyx: (separable_element <<K; q.[x]>> x).
+ by apply: (separable_elementS (subv_adjoin _ _)).
+have KyxEqKx: (<< <<K; q.[x]>>; x>> = <<K; x>>)%VS.
+ apply/eqP; rewrite eqEsubv andbC adjoinSl ?subv_adjoin //=.
+ apply/FadjoinP/andP; rewrite memv_adjoin andbT.
+ by apply/FadjoinP/andP; rewrite subv_adjoin mempx_Fadjoin.
+have:= extendDerivationP derD sepFyx; rewrite KyxEqKx => derDx.
+rewrite -horner_comp (Derivation_horner derDx) ?memv_adjoin //; last first.
+ by apply: (polyOverSv (subv_adjoin _ _)); apply: polyOver_comp.
+set Dx_p := map_poly _; have Dx_p_0 t: t \is a polyOver K -> (Dx_p t).[x] = 0.
+ move/polyOverP=> Kt; congr (_.[x] = 0): (horner0 x); apply/esym/polyP => i.
+ have /eqP Dti_0: D t`_i == 0 by rewrite -memv_ker (subvP DK_0) ?Kt.
+ by rewrite coef0 coef_map /= {1}extendDerivation_id ?subvP_adjoin.
+rewrite (Derivation_separable derDx sepKx) -/Dx_p Dx_p_0 ?polyOver_comp //.
+by rewrite add0r mulrCA Dx_p_0 ?minPolyOver ?oppr0 ?mul0r.
+Qed.
+
+Lemma separable_exponent K x :
+ exists n, [char L].-nat n && separable_element K (x ^+ n).
+Proof.
+pose d := adjoin_degree K x; move: {2}d.+1 (ltnSn d) => n.
+elim: n => // n IHn in x @d *; rewrite ltnS => le_d_n.
+have [[p charLp]|] := altP (separablePn K x); last by rewrite negbK; exists 1%N.
+case=> g Kg defKx; have p_pr := charf_prime charLp.
+suffices /IHn[m /andP[charLm sepKxpm]]: adjoin_degree K (x ^+ p) < n.
+ by exists (p * m)%N; rewrite pnat_mul pnatE // charLp charLm exprM.
+apply: leq_trans le_d_n; rewrite -ltnS -!size_minPoly.
+have nzKx: minPoly K x != 0 by rewrite monic_neq0 ?monic_minPoly.
+have nzg: g != 0 by apply: contra_eqN defKx => /eqP->; rewrite comp_poly0.
+apply: leq_ltn_trans (dvdp_leq nzg _) _.
+ by rewrite minPoly_dvdp // rootE -hornerXn -horner_comp -defKx minPolyxx.
+rewrite (polySpred nzKx) ltnS defKx size_comp_poly size_polyXn /=.
+suffices g_gt1: 1 < size g by rewrite -(subnKC g_gt1) ltn_Pmulr ?prime_gt1.
+apply: contra_eqT (size_minPoly K x); rewrite defKx -leqNgt => /size1_polyC->.
+by rewrite comp_polyC size_polyC; case: (_ != 0).
+Qed.
+
+Lemma charf0_separable K : [char L] =i pred0 -> forall x, separable_element K x.
+Proof.
+move=> charL0 x; have [n /andP[charLn]] := separable_exponent K x.
+by rewrite (pnat_1 charLn (sub_in_pnat _ charLn)) // => p _; rewrite charL0.
+Qed.
+
+Lemma charf_p_separable K x e p :
+ p \in [char L] -> separable_element K x = (x \in <<K; x ^+ (p ^ e.+1)>>%VS).
+Proof.
+move=> charLp; apply/idP/idP=> [sepKx | /Fadjoin_poly_eq]; last first.
+ set m := p ^ _;set f := Fadjoin_poly K _ x => Dx; apply/separable_elementP.
+ have mL0: m%:R = 0 :> L by apply/eqP; rewrite -(dvdn_charf charLp) dvdn_exp.
+ exists ('X - (f \Po 'X^m)); split.
+ - by rewrite rpredB ?polyOver_comp ?rpredX ?polyOverX ?Fadjoin_polyOver.
+ - by rewrite rootE !hornerE horner_comp hornerXn Dx subrr.
+ rewrite /separable_poly !(derivE, deriv_comp) -mulr_natr -rmorphMn /= mL0.
+ by rewrite !mulr0 subr0 coprimep1.
+without loss{e} ->: e x sepKx / e = 0%N.
+ move=> IH; elim: {e}e.+1 => [|e]; [exact: memv_adjoin | apply: subvP].
+ apply/FadjoinP/andP; rewrite subv_adjoin expnSr exprM (IH 0%N) //.
+ by have /adjoin_separableP-> := sepKx; rewrite ?rpredX ?memv_adjoin.
+set K' := <<K; x ^+ p>>%VS; have sKK': (K <= K')%VS := subv_adjoin _ _.
+pose q := minPoly K' x; pose g := 'X^p - (x ^+ p)%:P.
+have [K'g]: g \is a polyOver K' /\ q \is a polyOver K'.
+ by rewrite minPolyOver rpredB ?rpredX ?polyOverX // polyOverC memv_adjoin.
+have /dvdpP[c Dq]: 'X - x%:P %| q by rewrite dvdp_XsubCl root_minPoly.
+have co_c_g: coprimep c g.
+ have charPp: p \in [char {poly L}] := rmorph_char (polyC_rmorphism _) charLp.
+ rewrite /g polyC_exp -!(Frobenius_autE charPp) -rmorphB coprimep_expr //.
+ have: separable_poly q := separable_elementS sKK' sepKx.
+ by rewrite Dq separable_mul => /and3P[].
+have{g K'g co_c_g} /size_poly1P[a nz_a Dc]: size c == 1%N.
+ suffices c_dv_g: c %| g by rewrite -(eqp_size (dvdp_gcd_idl c_dv_g)).
+ have: q %| g by rewrite minPoly_dvdp // rootE !hornerE hornerXn subrr.
+ by apply: dvdp_trans; rewrite Dq dvdp_mulIl.
+rewrite {q}Dq {c}Dc mulrBr -rmorphM -rmorphN -cons_poly_def qualifE.
+by rewrite polyseq_cons !polyseqC nz_a /= rpredN andbCA => /and3P[/fpredMl->].
+Qed.
+
+Lemma charf_n_separable K x n :
+ [char L].-nat n -> 1 < n -> separable_element K x = (x \in <<K; x ^+ n>>%VS).
+Proof.
+rewrite -pi_pdiv; set p := pdiv n => charLn pi_n_p.
+have charLp: p \in [char L] := pnatPpi charLn pi_n_p.
+have <-: (n`_p)%N = n by rewrite -(eq_partn n (charf_eq charLp)) part_pnat_id.
+by rewrite p_part lognE -mem_primes pi_n_p -charf_p_separable.
+Qed.
+
+Definition purely_inseparable_element U x :=
+ x ^+ ex_minn (separable_exponent <<U>> x) \in U.
+
+Lemma purely_inseparable_elementP {K x} :
+ reflect (exists2 n, [char L].-nat n & x ^+ n \in K)
+ (purely_inseparable_element K x).
+Proof.
+rewrite /purely_inseparable_element.
+case: ex_minnP => n /andP[charLn /=]; rewrite subfield_closed => sepKxn min_xn.
+apply: (iffP idP) => [Kxn | [m charLm Kxm]]; first by exists n.
+have{min_xn}: n <= m by rewrite min_xn ?charLm ?base_separable.
+rewrite leq_eqVlt => /predU1P[-> // | ltnm]; pose p := pdiv m.
+have m_gt1: 1 < m by have [/leq_ltn_trans->] := andP charLn.
+have charLp: p \in [char L] by rewrite (pnatPpi charLm) ?pi_pdiv.
+have [/p_natP[em Dm] /p_natP[en Dn]]: p.-nat m /\ p.-nat n.
+ by rewrite -!(eq_pnat _ (charf_eq charLp)).
+rewrite Dn Dm ltn_exp2l ?prime_gt1 ?pdiv_prime // in ltnm.
+rewrite -(Fadjoin_idP Kxm) Dm -(subnKC ltnm) addSnnS expnD exprM -Dn.
+by rewrite -charf_p_separable.
+Qed.
+
+Lemma separable_inseparable_element K x :
+ separable_element K x && purely_inseparable_element K x = (x \in K).
+Proof.
+rewrite /purely_inseparable_element; case: ex_minnP => [[|m]] //=.
+rewrite subfield_closed; case: m => /= [-> //| m _ /(_ 1%N)/implyP/= insepKx].
+by rewrite (negPf insepKx) (contraNF (@base_separable K x) insepKx).
+Qed.
+
+Lemma base_inseparable K x : x \in K -> purely_inseparable_element K x.
+Proof. by rewrite -separable_inseparable_element => /andP[]. Qed.
+
+Lemma sub_inseparable K E x :
+ (K <= E)%VS -> purely_inseparable_element K x ->
+ purely_inseparable_element E x.
+Proof.
+move/subvP=> sKE /purely_inseparable_elementP[n charLn /sKE Exn].
+by apply/purely_inseparable_elementP; exists n.
+Qed.
+
+Section PrimitiveElementTheorem.
+
+Variables (K : {subfield L}) (x y : L).
+
+Section FiniteCase.
+
+Variable N : nat.
+
+Let K_is_large := exists s, [/\ uniq s, {subset s <= K} & N < size s].
+
+Let cyclic_or_large (z : L) : z != 0 -> K_is_large \/ exists a, z ^+ a.+1 = 1.
+Proof.
+move=> nz_z; pose d := adjoin_degree K z.
+pose h0 (i : 'I_(N ^ d).+1) (j : 'I_d) := (Fadjoin_poly K z (z ^+ i))`_j.
+pose s := undup [seq h0 i j | i <- enum 'I_(N ^ d).+1, j <- enum 'I_d].
+have s_h0 i j: h0 i j \in s.
+ by rewrite mem_undup; apply/allpairsP; exists (i, j); rewrite !mem_enum.
+pose h i := [ffun j => Ordinal (etrans (index_mem _ _) (s_h0 i j))].
+pose h' (f : {ffun 'I_d -> 'I_(size s)}) := \sum_(j < d) s`_(f j) * z ^+ j.
+have hK i: h' (h i) = z ^+ i.
+ have Kz_zi: z ^+ i \in <<K; z>>%VS by rewrite rpredX ?memv_adjoin.
+ rewrite -(Fadjoin_poly_eq Kz_zi) (horner_coef_wide z (size_poly _ _)) -/d.
+ by apply: eq_bigr => j _; rewrite ffunE /= nth_index.
+have [inj_h | ] := altP (@injectiveP _ _ h).
+ left; exists s; split=> [|zi_j|]; rewrite ?undup_uniq ?mem_undup //=.
+ by case/allpairsP=> ij [_ _ ->]; apply/polyOverP/Fadjoin_polyOver.
+ rewrite -[size s]card_ord -(@ltn_exp2r _ _ d) // -{2}[d]card_ord -card_ffun.
+ by rewrite -[_.+1]card_ord -(card_image inj_h) max_card.
+case/injectivePn=> i1 [i2 i1'2 /(congr1 h')]; rewrite !hK => eq_zi12; right.
+without loss{i1'2} lti12: i1 i2 eq_zi12 / i1 < i2.
+ by move=> IH; move: i1'2; rewrite neq_ltn => /orP[]; apply: IH.
+by exists (i2 - i1.+1)%N; rewrite subnSK ?expfB // eq_zi12 divff ?expf_neq0.
+Qed.
+
+Lemma finite_PET : K_is_large \/ exists z, (<< <<K; y>>; x>> = <<K; z>>)%VS.
+Proof.
+have [-> | /cyclic_or_large[|[a Dxa]]] := eqVneq x 0; first 2 [by left].
+ by rewrite addv0 subfield_closed; right; exists y.
+have [-> | /cyclic_or_large[|[b Dyb]]] := eqVneq y 0; first 2 [by left].
+ by rewrite addv0 subfield_closed; right; exists x.
+pose h0 (ij : 'I_a.+1 * 'I_b.+1) := x ^+ ij.1 * y ^+ ij.2.
+pose H := <<[set ij | h0 ij == 1%R]>>%G; pose h (u : coset_of H) := h0 (repr u).
+have h0M: {morph h0: ij1 ij2 / (ij1 * ij2)%g >-> ij1 * ij2}.
+ by rewrite /h0 => [] [i1 j1] [i2 j2] /=; rewrite mulrACA -!exprD !expr_mod.
+have memH ij: (ij \in H) = (h0 ij == 1).
+ rewrite /= gen_set_id ?inE //; apply/group_setP; rewrite inE [h0 _]mulr1.
+ by split=> // ? ?; rewrite !inE h0M => /eqP-> /eqP->; rewrite mulr1.
+have nH ij: ij \in 'N(H)%g.
+ by apply/(subsetP (cent_sub _))/centP=> ij1 _; congr (_, _); rewrite Zp_mulgC.
+have hE ij: h (coset H ij) = h0 ij.
+ rewrite /h val_coset //; case: repr_rcosetP => ij1.
+ by rewrite memH h0M => /eqP->; rewrite mul1r.
+have h1: h 1%g = 1 by rewrite /h repr_coset1 [h0 _]mulr1.
+have hM: {morph h: u v / (u * v)%g >-> u * v}.
+ by do 2![move=> u; have{u} [? _ ->] := cosetP u]; rewrite -morphM // !hE h0M.
+have /cyclicP[w defW]: cyclic [set: coset_of H].
+ apply: field_mul_group_cyclic (in2W hM) _ => u _; have [ij _ ->] := cosetP u.
+ by split=> [/eqP | -> //]; rewrite hE -memH => /coset_id.
+have Kw_h ij t: h0 ij = t -> t \in <<K; h w>>%VS.
+ have /cycleP[k Dk]: coset H ij \in <[w]>%g by rewrite -defW inE.
+ rewrite -hE {}Dk => <-; elim: k => [|k IHk]; first by rewrite h1 rpred1.
+ by rewrite expgS hM rpredM // memv_adjoin.
+right; exists (h w); apply/eqP; rewrite eqEsubv !(sameP FadjoinP andP).
+rewrite subv_adjoin (subv_trans (subv_adjoin K y)) ?subv_adjoin //=.
+rewrite (Kw_h (0, inZp 1)) 1?(Kw_h (inZp 1, 0)) /h0 ?mulr1 ?mul1r ?expr_mod //=.
+by rewrite rpredM ?rpredX ?memv_adjoin // subvP_adjoin ?memv_adjoin.
+Qed.
+
+End FiniteCase.
+
+Hypothesis sepKy : separable_element K y.
+
+Lemma Primitive_Element_Theorem : exists z, (<< <<K; y>>; x>> = <<K; z>>)%VS.
+Proof.
+have /polyOver_subvs[p Dp]: minPoly K x \is a polyOver K := minPolyOver K x.
+have nz_pKx: minPoly K x != 0 by rewrite monic_neq0 ?monic_minPoly.
+have{nz_pKx} nz_p: p != 0 by rewrite Dp map_poly_eq0 in nz_pKx.
+have{Dp} px0: root (map_poly vsval p) x by rewrite -Dp root_minPoly.
+have [q0 [Kq0 [q0y0 sepKq0]]] := separable_elementP sepKy.
+have /polyOver_subvs[q Dq]: minPoly K y \is a polyOver K := minPolyOver K y.
+have qy0: root (map_poly vsval q) y by rewrite -Dq root_minPoly.
+have sep_pKy: separable_poly (minPoly K y).
+ by rewrite (dvdp_separable _ sepKq0) ?minPoly_dvdp.
+have{sep_pKy} sep_q: separable_poly q by rewrite Dq separable_map in sep_pKy.
+have [r [nz_r PETr]] := large_field_PET nz_p px0 qy0 sep_q.
+have [[s [Us Ks /ltnW leNs]] | //] := finite_PET (size r).
+have{s Us Ks leNs} /allPn[t /Ks Kt nz_rt]: ~~ all (root r) s.
+ by apply: contraTN leNs; rewrite -ltnNge => /max_poly_roots->.
+have{PETr} [/= [p1 Dx] [q1 Dy]] := PETr (Subvs Kt) nz_rt.
+set z := t * y - x in Dx Dy; exists z; apply/eqP.
+rewrite eqEsubv !(sameP FadjoinP andP) subv_adjoin.
+have Kz_p1z (r1 : {poly subvs_of K}): (map_poly vsval r1).[z] \in <<K; z>>%VS.
+ rewrite rpred_horner ?memv_adjoin ?(polyOverSv (subv_adjoin K z)) //.
+ by apply/polyOver_subvs; exists r1.
+rewrite -{1}Dx -{1}Dy !{Dx Dy}Kz_p1z /=.
+rewrite (subv_trans (subv_adjoin K y)) ?subv_adjoin // rpredB ?memv_adjoin //.
+by rewrite subvP_adjoin // rpredM ?memv_adjoin ?subvP_adjoin.
+Qed.
+
+Lemma adjoin_separable : separable_element <<K; y>> x -> separable_element K x.
+Proof.
+have /Derivation_separableP derKy := sepKy => /Derivation_separableP derKy_x.
+have [z defKz] := Primitive_Element_Theorem.
+suffices /adjoin_separableP: separable_element K z.
+ by apply; rewrite -defKz memv_adjoin.
+apply/Derivation_separableP=> D; rewrite -defKz => derKxyD DK_0.
+suffices derKyD: Derivation <<K; y>>%VS D by rewrite derKy_x // derKy.
+by apply: DerivationS derKxyD; apply: subv_adjoin.
+Qed.
+
+End PrimitiveElementTheorem.
+
+Lemma strong_Primitive_Element_Theorem K x y :
+ separable_element <<K; x>> y ->
+ exists2 z : L, (<< <<K; y>>; x>> = <<K; z>>)%VS
+ & separable_element K x -> separable_element K y.
+Proof.
+move=> sepKx_y; have [n /andP[charLn sepKyn]] := separable_exponent K y.
+have adjK_C z t: (<<<<K; z>>; t>> = <<<<K; t>>; z>>)%VS.
+ by rewrite !agenv_add_id -!addvA (addvC <[_]>%VS).
+have [z defKz] := Primitive_Element_Theorem x sepKyn.
+exists z => [|/adjoin_separable->]; rewrite ?sepKx_y // -defKz.
+have [|n_gt1|-> //] := ltngtP n 1%N; first by case: (n) charLn.
+apply/eqP; rewrite !(adjK_C _ x) eqEsubv; apply/andP.
+split; apply/FadjoinP/andP; rewrite subv_adjoin ?rpredX ?memv_adjoin //=.
+by rewrite -charf_n_separable ?sepKx_y.
+Qed.
+
+Definition separable U W : bool :=
+ all (separable_element U) (vbasis W).
+
+Definition purely_inseparable U W : bool :=
+ all (purely_inseparable_element U) (vbasis W).
+
+Lemma separable_add K x y :
+ separable_element K x -> separable_element K y -> separable_element K (x + y).
+Proof.
+move/(separable_elementS (subv_adjoin K y))=> sepKy_x sepKy.
+have [z defKz] := Primitive_Element_Theorem x sepKy.
+have /(adjoin_separableP _): x + y \in <<K; z>>%VS.
+ by rewrite -defKz rpredD ?memv_adjoin // subvP_adjoin ?memv_adjoin.
+apply; apply: adjoin_separable sepKy (adjoin_separable sepKy_x _).
+by rewrite defKz base_separable ?memv_adjoin.
+Qed.
+
+Lemma separable_sum I r (P : pred I) (v_ : I -> L) K :
+ (forall i, P i -> separable_element K (v_ i)) ->
+ separable_element K (\sum_(i <- r | P i) v_ i).
+Proof.
+move=> sepKi.
+by elim/big_ind: _; [apply/base_separable/mem0v | apply: separable_add |].
+Qed.
+
+Lemma inseparable_add K x y :
+ purely_inseparable_element K x -> purely_inseparable_element K y ->
+ purely_inseparable_element K (x + y).
+Proof.
+have insepP := purely_inseparable_elementP.
+move=> /insepP[n charLn Kxn] /insepP[m charLm Kym]; apply/insepP.
+have charLnm: [char L].-nat (n * m)%N by rewrite pnat_mul charLn.
+by exists (n * m)%N; rewrite ?exprDn_char // {2}mulnC !exprM memvD // rpredX.
+Qed.
+
+Lemma inseparable_sum I r (P : pred I) (v_ : I -> L) K :
+ (forall i, P i -> purely_inseparable_element K (v_ i)) ->
+ purely_inseparable_element K (\sum_(i <- r | P i) v_ i).
+Proof.
+move=> insepKi.
+by elim/big_ind: _; [apply/base_inseparable/mem0v | apply: inseparable_add |].
+Qed.
+
+Lemma separableP {K E} :
+ reflect (forall y, y \in E -> separable_element K y) (separable K E).
+Proof.
+apply/(iffP idP)=> [/allP|] sepK_E; last by apply/allP=> x /vbasis_mem/sepK_E.
+move=> y /coord_vbasis->; apply/separable_sum=> i _.
+have: separable_element K (vbasis E)`_i by apply/sepK_E/memt_nth.
+by move/adjoin_separableP; apply; rewrite rpredZ ?memv_adjoin.
+Qed.
+
+Lemma purely_inseparableP {K E} :
+ reflect (forall y, y \in E -> purely_inseparable_element K y)
+ (purely_inseparable K E).
+Proof.
+apply/(iffP idP)=> [/allP|] sep'K_E; last by apply/allP=> x /vbasis_mem/sep'K_E.
+move=> y /coord_vbasis->; apply/inseparable_sum=> i _.
+have: purely_inseparable_element K (vbasis E)`_i by apply/sep'K_E/memt_nth.
+case/purely_inseparable_elementP=> n charLn K_Ein.
+by apply/purely_inseparable_elementP; exists n; rewrite // exprZn rpredZ.
+Qed.
+
+Lemma adjoin_separable_eq K x : separable_element K x = separable K <<K; x>>%VS.
+Proof. exact: sameP adjoin_separableP separableP. Qed.
+
+Lemma separable_inseparable_decomposition E K :
+ {x | x \in E /\ separable_element K x & purely_inseparable <<K; x>> E}.
+Proof.
+without loss sKE: K / (K <= E)%VS.
+ case/(_ _ (capvSr K E)) => x [Ex sepKEx] /purely_inseparableP sep'KExE.
+ exists x; first by split; last exact/(separable_elementS _ sepKEx)/capvSl.
+ apply/purely_inseparableP=> y /sep'KExE; apply: sub_inseparable.
+ exact/adjoinSl/capvSl.
+pose E_ i := (vbasis E)`_i; pose fP i := separable_exponent K (E_ i).
+pose f i := E_ i ^+ ex_minn (fP i); pose s := mkseq f (\dim E).
+pose K' := <<K & s>>%VS.
+have sepKs: all (separable_element K) s.
+ by rewrite all_map /f; apply/allP=> i _ /=; case: ex_minnP => m /andP[].
+have [x sepKx defKx]: {x | x \in E /\ separable_element K x & K' = <<K; x>>%VS}.
+ have: all (mem E) s.
+ rewrite all_map; apply/allP=> i; rewrite mem_iota => ltis /=.
+ by rewrite rpredX // vbasis_mem // memt_nth.
+ rewrite {}/K'; elim/last_ind: s sepKs => [|s t IHs].
+ by exists 0; [rewrite base_separable mem0v | rewrite adjoin_nil addv0].
+ rewrite adjoin_rcons !all_rcons => /andP[sepKt sepKs] /andP[/= Et Es].
+ have{IHs sepKs Es} [y [Ey sepKy] ->{s}] := IHs sepKs Es.
+ have /sig_eqW[x defKx] := Primitive_Element_Theorem t sepKy.
+ exists x; [split | exact: defKx].
+ suffices: (<<K; x>> <= E)%VS by case/FadjoinP.
+ by rewrite -defKx !(sameP FadjoinP andP) sKE Ey Et.
+ apply/adjoin_separableP=> z; rewrite -defKx => Kyt_z.
+ apply: adjoin_separable sepKy _; apply: adjoin_separableP Kyt_z.
+ exact: separable_elementS (subv_adjoin K y) sepKt.
+exists x; rewrite // -defKx; apply/(all_nthP 0)=> i; rewrite size_tuple => ltiE.
+apply/purely_inseparable_elementP.
+exists (ex_minn (fP i)); first by case: ex_minnP => n /andP[].
+by apply/seqv_sub_adjoin/map_f; rewrite mem_iota.
+Qed.
+
+Definition separable_generator K E : L :=
+ s2val (locked (separable_inseparable_decomposition E K)).
+
+Lemma separable_generator_mem E K : separable_generator K E \in E.
+Proof. by rewrite /separable_generator; case: (locked _) => ? []. Qed.
+
+Lemma separable_generatorP E K : separable_element K (separable_generator K E).
+Proof. by rewrite /separable_generator; case: (locked _) => ? []. Qed.
+
+Lemma separable_generator_maximal E K :
+ purely_inseparable <<K; separable_generator K E>> E.
+Proof. by rewrite /separable_generator; case: (locked _). Qed.
+
+Lemma sub_adjoin_separable_generator E K :
+ separable K E -> (E <= <<K; separable_generator K E>>)%VS.
+Proof.
+move/separableP=> sepK_E; apply/subvP=> v Ev.
+rewrite -separable_inseparable_element.
+have /purely_inseparableP-> // := separable_generator_maximal E K.
+by rewrite (separable_elementS _ (sepK_E _ Ev)) // subv_adjoin.
+Qed.
+
+Lemma eq_adjoin_separable_generator E K :
+ separable K E -> (K <= E)%VS ->
+ E = <<K; separable_generator K E>>%VS :> {vspace _}.
+Proof.
+move=> sepK_E sKE; apply/eqP; rewrite eqEsubv sub_adjoin_separable_generator //.
+by apply/FadjoinP/andP; rewrite sKE separable_generator_mem.
+Qed.
+
+Lemma separable_refl K : separable K K.
+Proof. by apply/separableP; apply: base_separable. Qed.
+
+Lemma separable_trans M K E : separable K M -> separable M E -> separable K E.
+Proof.
+move/sub_adjoin_separable_generator.
+set x := separable_generator K M => sMKx /separableP sepM_E.
+apply/separableP => w /sepM_E/(separable_elementS sMKx).
+case/strong_Primitive_Element_Theorem => _ _ -> //.
+exact: separable_generatorP.
+Qed.
+
+Lemma separableS K1 K2 E2 E1 :
+ (K1 <= K2)%VS -> (E2 <= E1)%VS -> separable K1 E1 -> separable K2 E2.
+Proof.
+move=> sK12 /subvP sE21 /separableP sepK1_E1.
+by apply/separableP=> y /sE21/sepK1_E1/(separable_elementS sK12).
+Qed.
+
+Lemma separableSl K M E : (K <= M)%VS -> separable K E -> separable M E.
+Proof. by move/separableS; apply. Qed.
+
+Lemma separableSr K M E : (M <= E)%VS -> separable K E -> separable K M.
+Proof. exact: separableS. Qed.
+
+Lemma separable_Fadjoin_seq K rs :
+ all (separable_element K) rs -> separable K <<K & rs>>.
+Proof.
+elim/last_ind: rs => [|s x IHs] in K *.
+ by rewrite adjoin_nil subfield_closed separable_refl.
+rewrite all_rcons adjoin_rcons => /andP[sepKx /IHs/separable_trans-> //].
+by rewrite -adjoin_separable_eq (separable_elementS _ sepKx) ?subv_adjoin_seq.
+Qed.
+
+Lemma purely_inseparable_refl K : purely_inseparable K K.
+Proof. by apply/purely_inseparableP; apply: base_inseparable. Qed.
+
+Lemma purely_inseparable_trans M K E :
+ purely_inseparable K M -> purely_inseparable M E -> purely_inseparable K E.
+Proof.
+have insepP := purely_inseparableP => /insepP insepK_M /insepP insepM_E.
+have insepPe := purely_inseparable_elementP.
+apply/insepP=> x /insepM_E/insepPe[n charLn /insepK_M/insepPe[m charLm Kxnm]].
+by apply/insepPe; exists (n * m)%N; rewrite ?exprM // pnat_mul charLn charLm.
+Qed.
+
+End Separable.
+
+Implicit Arguments separable_elementP [F L K x].
+Implicit Arguments separablePn [F L K x].
+Implicit Arguments Derivation_separableP [F L K x].
+Implicit Arguments adjoin_separableP [F L K x].
+Implicit Arguments purely_inseparable_elementP [F L K x].
+Implicit Arguments separableP [F L K E].
+Implicit Arguments purely_inseparableP [F L K E].