Theory Finite_Fields.Monic_Polynomial_Factorization
section ‹Factorization into Monic Polynomials\label{sec:monic}›
theory Monic_Polynomial_Factorization
imports
  Finite_Fields_Factorization_Ext
  Formal_Polynomial_Derivatives
begin
hide_const Factorial_Ring.multiplicity
hide_const Factorial_Ring.irreducible
lemma (in domain) finprod_mult_of:
  assumes "finite A"
  assumes "⋀x. x ∈ A ⟹ f x ∈ carrier (mult_of R)"
  shows "finprod R f A = finprod (mult_of R) f A"
  using assms by (induction A rule:finite_induct, auto)
lemma (in ring) finite_poly:
  assumes "subring K R"
  assumes "finite K"
  shows
    "finite {f. f ∈ carrier (K[X]) ∧ degree f = n}" (is "finite ?A")
    "finite {f. f ∈ carrier (K[X]) ∧ degree f ≤ n}" (is "finite ?B")
proof -
  have "finite {f. set f ⊆ K ∧ length f ≤ n + 1}" (is "finite ?C")
    using assms(2) finite_lists_length_le by auto
  moreover have "?B ⊆ ?C"
    by (intro subsetI)
      (auto simp:univ_poly_carrier[symmetric] polynomial_def)
  ultimately show a: "finite ?B"
    using finite_subset by auto
  moreover have "?A ⊆ ?B"
    by (intro subsetI, simp)
  ultimately show "finite ?A"
    using finite_subset by auto
qed
definition pmult :: "_ ⇒ 'a list ⇒ 'a list ⇒ nat" (‹pmultı›)
  where "pmult⇘R⇙ d p = multiplicity (mult_of (poly_ring R)) d p"
definition monic_poly :: "_ ⇒ 'a list ⇒ bool"
  where "monic_poly R f =
    (f ≠ [] ∧ lead_coeff f = 𝟭⇘R⇙ ∧ f ∈ carrier (poly_ring R))"
definition monic_irreducible_poly where
  "monic_irreducible_poly R f =
    (monic_poly R f ∧ pirreducible⇘R⇙ (carrier R) f)"
abbreviation "m_i_p ≡ monic_irreducible_poly"
locale polynomial_ring = field +
  fixes K
  assumes polynomial_ring_assms: "subfield K R"
begin
lemma K_subring: "subring K R"
  using polynomial_ring_assms subfieldE(1) by auto
abbreviation P where "P ≡ K[X]"
text ‹This locale is used to specialize the following lemmas for a fixed coefficient ring.
It can be introduced in a context as an intepretation to be able to use the following specialized
lemmas. Because it is not (and should not) introduced as a sublocale it has no lasting effect
for the field locale itself.›
lemmas
    poly_mult_lead_coeff = poly_mult_lead_coeff[OF K_subring]
and degree_add_distinct = degree_add_distinct[OF K_subring]
and coeff_add = coeff_add[OF K_subring]
and var_closed = var_closed[OF K_subring]
and degree_prod = degree_prod[OF _ K_subring]
and degree_pow = degree_pow[OF K_subring]
and pirreducible_degree = pirreducible_degree[OF polynomial_ring_assms]
and degree_one_imp_pirreducible =
    degree_one_imp_pirreducible[OF polynomial_ring_assms]
and var_pow_closed = var_pow_closed[OF K_subring]
and var_pow_carr = var_pow_carr[OF K_subring]
and univ_poly_a_inv_degree = univ_poly_a_inv_degree[OF K_subring]
and var_pow_degree = var_pow_degree[OF K_subring]
and pdivides_zero = pdivides_zero[OF K_subring]
and pdivides_imp_degree_le = pdivides_imp_degree_le[OF K_subring]
and var_carr = var_carr[OF K_subring]
and rupture_eq_0_iff = rupture_eq_0_iff[OF polynomial_ring_assms]
and rupture_is_field_iff_pirreducible =
    rupture_is_field_iff_pirreducible[OF polynomial_ring_assms]
and rupture_surj_hom = rupture_surj_hom[OF K_subring]
and canonical_embedding_ring_hom =
    canonical_embedding_ring_hom[OF K_subring]
and rupture_surj_norm_is_hom = rupture_surj_norm_is_hom[OF K_subring]
and rupture_surj_as_eval = rupture_surj_as_eval[OF K_subring]
and eval_cring_hom = eval_cring_hom[OF K_subring]
and coeff_range = coeff_range[OF K_subring]
and finite_poly = finite_poly[OF K_subring]
and int_embed_consistent_with_poly_of_const =
    int_embed_consistent_with_poly_of_const[OF K_subring]
and pderiv_var_pow = pderiv_var_pow[OF _ K_subring]
and pderiv_add = pderiv_add[OF K_subring]
and pderiv_inv = pderiv_inv[OF K_subring]
and pderiv_mult = pderiv_mult[OF K_subring]
and pderiv_pow = pderiv_pow[OF _ K_subring]
and pderiv_carr = pderiv_carr[OF K_subring]
sublocale p:principal_domain "poly_ring R"
  by (simp add: carrier_is_subfield univ_poly_is_principal)
end
context field
begin
interpretation polynomial_ring "R" "carrier R"
  using carrier_is_subfield field_axioms
  by (simp add:polynomial_ring_def polynomial_ring_axioms_def)
lemma pdivides_mult_r:
  assumes "a ∈ carrier (mult_of P)"
  assumes "b ∈ carrier (mult_of P)"
  assumes "c ∈ carrier (mult_of P)"
  shows "a ⊗⇘P⇙ c pdivides b ⊗⇘P⇙ c ⟷ a pdivides b"
    (is "?lhs ⟷ ?rhs")
proof -
  have a:"b ⊗⇘P⇙ c ∈ carrier P - {𝟬⇘P⇙}"
    using assms p.mult_of.m_closed by force
  have b:"a ⊗⇘P⇙ c ∈ carrier P"
    using assms by simp
  have c:"b ∈ carrier P - {𝟬⇘P⇙}"
    using assms p.mult_of.m_closed by force
  have d:"a ∈ carrier P" using assms by simp
  have "?lhs ⟷ a ⊗⇘P⇙ c divides⇘mult_of P⇙ b ⊗⇘P⇙ c"
    unfolding pdivides_def using p.divides_imp_divides_mult a b
    by (meson divides_mult_imp_divides)
  also have "... ⟷ a divides⇘mult_of P⇙ b"
    using p.mult_of.divides_mult_r[OF assms] by simp
  also have "... ⟷ ?rhs"
    unfolding pdivides_def using p.divides_imp_divides_mult c d
    by (meson divides_mult_imp_divides)
  finally show ?thesis by simp
qed
lemma lead_coeff_carr:
  assumes "x ∈ carrier (mult_of P)"
  shows "lead_coeff x ∈ carrier R - {𝟬}"
proof (cases x)
  case Nil
  then show ?thesis using assms by (simp add:univ_poly_zero)
next
  case (Cons a list)
  hence a: "polynomial (carrier R) (a # list)"
    using assms univ_poly_carrier by auto
  have "lead_coeff x = a"
    using Cons by simp
  also have "a ∈ carrier R - {𝟬}"
    using lead_coeff_not_zero a by simp
  finally show ?thesis by simp
qed
lemma lead_coeff_poly_of_const:
  assumes "r ≠ 𝟬"
  shows "lead_coeff (poly_of_const r) = r"
  using assms
  by (simp add:poly_of_const_def)
lemma lead_coeff_mult:
  assumes "f ∈ carrier (mult_of P)"
  assumes "g ∈ carrier (mult_of P)"
  shows "lead_coeff (f ⊗⇘P⇙ g) = lead_coeff f ⊗ lead_coeff g"
  unfolding univ_poly_mult using assms
  using univ_poly_carrier[where R="R" and K="carrier R"]
  by (subst poly_mult_lead_coeff) (simp_all add:univ_poly_zero)
lemma monic_poly_carr:
  assumes "monic_poly R f"
  shows "f ∈ carrier P"
  using assms unfolding monic_poly_def by simp
lemma monic_poly_add_distinct:
  assumes "monic_poly R f"
  assumes "g ∈ carrier P" "degree g < degree f"
  shows "monic_poly R (f ⊕⇘P⇙ g)"
proof (cases "g ≠ 𝟬⇘P⇙")
  case True
  define n where "n = degree f"
  have "f ∈ carrier P - {𝟬⇘P⇙}"
    using assms(1) univ_poly_zero
    unfolding monic_poly_def by auto
  hence "degree (f ⊕⇘P⇙ g) = max (degree f) (degree g)"
    using assms(2,3) True
    by (subst degree_add_distinct, simp_all)
  also have "... = degree f"
    using assms(3) by simp
  finally have b: "degree (f ⊕⇘P⇙ g) = n"
    unfolding n_def by simp
  moreover have "n > 0"
    using assms(3) unfolding n_def by simp
  ultimately have "degree (f ⊕⇘P⇙ g) ≠ degree ([])"
    by simp
  hence a:"f ⊕⇘P⇙ g ≠ []" by auto
  have "degree [] = 0" by simp
  also have  "... < degree f"
    using assms(3) by simp
  finally have "degree f ≠ degree []" by simp
  hence c: "f ≠ []" by auto
  have d: "length g ≤ n"
    using  assms(3) unfolding n_def by simp
  have "lead_coeff (f ⊕⇘P⇙ g) = coeff (f ⊕⇘P⇙ g) n"
    using a b by (cases "f ⊕⇘P⇙ g", auto)
  also have "... = coeff f n ⊕ coeff g n"
    using monic_poly_carr assms
    by (subst coeff_add, auto)
  also have "... = lead_coeff f ⊕ coeff g n"
    using c unfolding n_def by (cases "f", auto)
  also have "... = 𝟭 ⊕ 𝟬"
    using assms(1) unfolding monic_poly_def
    unfolding subst coeff_length[OF d] by simp
  also have "... = 𝟭"
    by simp
  finally have "lead_coeff (f ⊕⇘P⇙ g) = 𝟭" by simp
  moreover have "f ⊕⇘P⇙ g ∈ carrier P"
    using monic_poly_carr assms by simp
  ultimately show  ?thesis
    using a  unfolding monic_poly_def by auto
next
  case False
  then show ?thesis using assms monic_poly_carr by simp
qed
lemma monic_poly_one: "monic_poly R 𝟭⇘P⇙"
proof -
  have "𝟭⇘P⇙ ∈ carrier P"
    by simp
  thus ?thesis
    by (simp add:univ_poly_one monic_poly_def)
qed
lemma monic_poly_var: "monic_poly R X"
proof -
  have "X ∈ carrier P"
    using var_closed by simp
  thus ?thesis
    by (simp add:var_def monic_poly_def)
qed
lemma monic_poly_carr_2:
  assumes "monic_poly R f"
  shows "f ∈ carrier (mult_of P)"
  using assms unfolding monic_poly_def
  by (simp add:univ_poly_zero)
lemma monic_poly_mult:
  assumes "monic_poly R f"
  assumes "monic_poly R g"
  shows "monic_poly R (f ⊗⇘P⇙ g)"
proof -
  have "lead_coeff (f ⊗⇘P⇙ g) = lead_coeff f ⊗⇘R⇙ lead_coeff g"
    using assms monic_poly_carr_2
    by (subst lead_coeff_mult) auto
  also have "... =  𝟭"
    using assms unfolding monic_poly_def by simp
  finally have "lead_coeff (f ⊗⇘P⇙ g) = 𝟭⇘R⇙" by simp
  moreover have "(f ⊗⇘P⇙ g) ∈ carrier (mult_of P)"
    using monic_poly_carr_2 assms by blast
  ultimately show ?thesis
    by (simp add:monic_poly_def univ_poly_zero)
qed
lemma monic_poly_pow:
  assumes "monic_poly R f"
  shows "monic_poly R (f [^]⇘P⇙ (n::nat))"
  using assms monic_poly_one monic_poly_mult
  by (induction n, auto)
lemma monic_poly_prod:
  assumes "finite A"
  assumes "⋀x. x ∈ A ⟹ monic_poly R (f x)"
  shows "monic_poly R (finprod P f A)"
  using assms
proof (induction A rule:finite_induct)
  case empty
  then show ?case by (simp add:monic_poly_one)
next
  case (insert x F)
  have a: "f ∈ F → carrier P"
    using insert monic_poly_carr by simp
  have b: "f x ∈ carrier P"
    using insert monic_poly_carr by simp
  have "monic_poly R (f x ⊗⇘P⇙ finprod P f F)"
    using insert by (intro monic_poly_mult) auto
  thus ?case
    using insert a b by (subst p.finprod_insert, auto)
qed
lemma monic_poly_not_assoc:
  assumes "monic_poly R f"
  assumes "monic_poly R g"
  assumes "f ∼⇘(mult_of P)⇙ g"
  shows "f = g"
proof -
  obtain u where u_def: "f = g ⊗⇘P⇙ u" "u ∈ Units (mult_of P)"
    using p.mult_of.associatedD2 assms monic_poly_carr_2
    by blast
  hence "u ∈ Units P" by simp
  then obtain v where v_def: "u = [v]" "v ≠ 𝟬⇘R⇙" "v ∈ carrier R"
    using univ_poly_carrier_units by auto
  have "𝟭 = lead_coeff f"
    using assms(1) by (simp add:monic_poly_def)
  also have  "... = lead_coeff (g ⊗⇘P⇙ u)"
    by (simp add:u_def)
  also have "... = lead_coeff g ⊗ lead_coeff u"
    using assms(2) monic_poly_carr_2 v_def u_def(2)
    by (subst lead_coeff_mult, auto simp add:univ_poly_zero)
  also have "... = lead_coeff g ⊗ v"
    using v_def by simp
  also have "... = v"
    using assms(2) v_def(3) by (simp add:monic_poly_def)
  finally have "𝟭 = v" by simp
  hence "u = 𝟭⇘P⇙"
    using v_def by (simp add:univ_poly_one)
  thus "f = g"
    using u_def assms monic_poly_carr by simp
qed
lemma monic_poly_span:
  assumes "x ∈ carrier (mult_of P)" "irreducible (mult_of P) x"
  shows "∃y. monic_irreducible_poly R y ∧ x ∼⇘(mult_of P)⇙ y"
proof -
  define z where "z = poly_of_const (inv (lead_coeff x))"
  define y where "y = x ⊗⇘P⇙ z"
  have x_carr: "x ∈ carrier (mult_of P)" using assms by simp
  hence lx_ne_0: "lead_coeff x ≠ 𝟬"
    and lx_unit: "lead_coeff x ∈ Units R"
    using lead_coeff_carr[OF x_carr] by (auto simp add:field_Units)
  have lx_inv_ne_0: "inv (lead_coeff x) ≠ 𝟬"
    using lx_unit
    by (metis Units_closed Units_r_inv r_null zero_not_one)
  have lx_inv_carr: "inv (lead_coeff x) ∈ carrier R"
    using lx_unit by simp
  have "z ∈ carrier P"
    using lx_inv_carr poly_of_const_over_carrier
    unfolding z_def by auto
  moreover have "z ≠ 𝟬⇘P⇙"
    using lx_inv_ne_0
    by (simp add:z_def poly_of_const_def univ_poly_zero)
  ultimately have z_carr: "z ∈ carrier (mult_of P)" by simp
  have z_unit: "z ∈ Units (mult_of P)"
    using lx_inv_ne_0 lx_inv_carr
    by (simp add:univ_poly_carrier_units z_def poly_of_const_def)
  have y_exp: "y = x ⊗⇘(mult_of P)⇙ z"
    by (simp add:y_def)
  hence y_carr: "y ∈ carrier (mult_of P)"
    using x_carr z_carr p.mult_of.m_closed by simp
  have "irreducible (mult_of P) y"
    unfolding y_def using assms z_unit z_carr
    by (intro p.mult_of.irreducible_prod_rI, auto)
  moreover have "lead_coeff y = 𝟭⇘R⇙"
    unfolding y_def using x_carr z_carr lx_inv_ne_0 lx_unit
    by (simp add: lead_coeff_mult z_def lead_coeff_poly_of_const)
  hence "monic_poly R y"
    using y_carr unfolding monic_poly_def
    by (simp add:univ_poly_zero)
  ultimately have "monic_irreducible_poly R y"
    using p.irreducible_mult_imp_irreducible y_carr
    by (simp add:monic_irreducible_poly_def ring_irreducible_def)
  moreover have "y ∼⇘(mult_of P)⇙ x"
    by (intro p.mult_of.associatedI2[OF z_unit] y_def x_carr)
  hence "x ∼⇘(mult_of P)⇙ y"
    using x_carr y_carr by (simp add:p.mult_of.associated_sym)
  ultimately show ?thesis by auto
qed
lemma monic_polys_are_canonical_irreducibles:
  "canonical_irreducibles (mult_of P) {d. monic_irreducible_poly R d}"
  (is "canonical_irreducibles (mult_of P) ?S")
proof -
  have sp_1:
    "?S ⊆ {x ∈ carrier (mult_of P). irreducible (mult_of P) x}"
    unfolding monic_irreducible_poly_def ring_irreducible_def
    using monic_poly_carr
    by (intro subsetI, simp add: p.irreducible_imp_irreducible_mult)
  have sp_2: "x = y"
      if "x ∈ ?S" "y ∈ ?S" "x ∼⇘(mult_of P)⇙ y" for x y
    using that monic_poly_not_assoc
    by (simp add:monic_irreducible_poly_def)
  have sp_3: "∃y ∈ ?S. x ∼⇘(mult_of P)⇙ y"
    if "x ∈ carrier (mult_of P)" "irreducible (mult_of P) x" for x
    using that monic_poly_span by simp
  thus ?thesis using sp_1 sp_2 sp_3
    unfolding canonical_irreducibles_def by simp
qed
lemma
  assumes "monic_poly R a"
  shows factor_monic_poly:
    "a = (⨂⇘P⇙d∈{d. monic_irreducible_poly R d ∧ pmult d a > 0}.
      d [^]⇘P⇙ pmult d a)" (is "?lhs = ?rhs")
    and factor_monic_poly_fin:
      "finite {d. monic_irreducible_poly R d ∧ pmult d a > 0}"
proof -
  let ?S = "{d. monic_irreducible_poly R d}"
  let ?T = "{d. monic_irreducible_poly R d ∧ pmult d a > 0}"
  let ?mip = "monic_irreducible_poly R"
  have sp_4: "a ∈ carrier (mult_of P)"
    using assms monic_poly_carr_2
    unfolding monic_irreducible_poly_def by simp
  have b_1: "x ∈ carrier (mult_of P)" if "?mip x" for x
    using that monic_poly_carr_2
    unfolding monic_irreducible_poly_def by simp
  have b_2:"irreducible (mult_of P) x" if "?mip x" for x
    using that
    unfolding monic_irreducible_poly_def ring_irreducible_def
    by (simp add: monic_poly_carr p.irreducible_imp_irreducible_mult)
  have b_3:"x ∈ carrier P" if "?mip x" for x
    using that monic_poly_carr
    unfolding monic_irreducible_poly_def
    by simp
  have a_carr: "a ∈ carrier P - {𝟬⇘P⇙}"
    using sp_4 by simp
  have "?T = {d. ?mip d ∧ multiplicity (mult_of P) d a > 0}"
    by (simp add:pmult_def)
  also have "... = {d ∈ ?S. multiplicity (mult_of P) d a > 0}"
    using p.mult_of.multiplicity_gt_0_iff[OF b_1 b_2 sp_4]
    by (intro order_antisym subsetI, auto)
  finally have t:"?T = {d ∈ ?S. multiplicity (mult_of P) d a > 0}"
    by simp
  show fin_T: "finite ?T"
    unfolding t
    using p.mult_of.split_factors(1)
      [OF monic_polys_are_canonical_irreducibles]
    using sp_4 by auto
  have a:"x [^]⇘P⇙ (n::nat) ∈ carrier (mult_of P)" if "?mip x" for x n
  proof -
    have "monic_poly R (x [^]⇘P⇙ n)"
      using that monic_poly_pow
      unfolding monic_irreducible_poly_def by auto
    thus ?thesis
      using monic_poly_carr_2 by simp
  qed
  have "?lhs ∼⇘(mult_of P)⇙
    finprod (mult_of P)
      (λd. d [^]⇘(mult_of P)⇙ (multiplicity (mult_of P) d a)) ?T"
    unfolding t
    by (intro p.mult_of.split_factors(2)
        [OF monic_polys_are_canonical_irreducibles sp_4])
  also have "... =
    finprod (mult_of P) (λd. d [^]⇘P⇙ (multiplicity (mult_of P) d a)) ?T"
    by (simp add:nat_pow_mult_of)
  also have "... = ?rhs"
    using fin_T a
    by (subst p.finprod_mult_of, simp_all add:pmult_def)
  finally have "?lhs ∼⇘(mult_of P)⇙ ?rhs" by simp
  moreover have "monic_poly R ?rhs"
    using fin_T
    by (intro monic_poly_prod monic_poly_pow)
      (auto simp:monic_irreducible_poly_def)
  ultimately show "?lhs = ?rhs"
    using monic_poly_not_assoc assms monic_irreducible_poly_def
    by blast
qed
lemma degree_monic_poly':
  assumes "monic_poly R f"
  shows
    "sum' (λd. pmult d f * degree d) {d. monic_irreducible_poly R d} =
    degree f"
proof -
  let ?mip = "monic_irreducible_poly R"
  have b: "d ∈ carrier P - {𝟬⇘P⇙}" if "?mip d" for d
    using that monic_poly_carr_2
    unfolding monic_irreducible_poly_def by simp
  have a: "d [^]⇘P⇙ n ∈ carrier P - {𝟬⇘P⇙}" if "?mip d" for d and n :: "nat"
    using b that monic_poly_pow
    unfolding monic_irreducible_poly_def
    by (simp add: p.pow_non_zero)
  have "degree f =
    degree (⨂⇘P⇙d∈{d. ?mip d ∧ pmult d f > 0}. d [^]⇘P⇙ pmult d f)"
    using factor_monic_poly[OF assms(1)] by simp
  also have "... =
    (∑i∈{d. ?mip d ∧ 0 < pmult d f}. degree (i [^]⇘P⇙ pmult i f))"
    using a assms(1)
    by (subst degree_prod[OF factor_monic_poly_fin])
     (simp_all add:Pi_def)
  also have "... =
    (∑i∈{d. ?mip d ∧ 0 < pmult d f}. degree i * pmult i f)"
    using b degree_pow by (intro sum.cong, auto)
  also have "... =
    (∑d∈{d. ?mip d ∧ 0 < pmult d f}. pmult d f * degree d)"
    by (simp add:mult.commute)
  also have "... =
    sum' (λd. pmult d f * degree d) {d. ?mip d ∧ 0 < pmult d f}"
    using sum.eq_sum factor_monic_poly_fin[OF assms(1)] by simp
  also have "... = sum' (λd. pmult d f * degree d) {d. ?mip d}"
    by (intro sum.mono_neutral_cong_left' subsetI, auto)
  finally show ?thesis by simp
qed
lemma monic_poly_min_degree:
  assumes "monic_irreducible_poly R f"
  shows  "degree f ≥ 1"
  using assms unfolding monic_irreducible_poly_def monic_poly_def
  by (intro pirreducible_degree) auto
lemma degree_one_monic_poly:
  "monic_irreducible_poly R f ∧ degree f = 1 ⟷
  (∃x ∈ carrier R. f = [𝟭, ⊖x])"
proof
  assume "monic_irreducible_poly R f ∧ degree f = 1"
  hence a:"monic_poly R f" "length f = 2"
    unfolding monic_irreducible_poly_def by auto
  then obtain u v where f_def: "f = [u,v]"
    by (cases f, simp, cases "tl f", auto)
  have "u = 𝟭" using a unfolding monic_poly_def f_def by simp
  moreover have "v ∈ carrier R"
    using a unfolding monic_poly_def univ_poly_carrier[symmetric]
    unfolding polynomial_def f_def by simp
  ultimately have "f =  [𝟭, ⊖(⊖v)]" "(⊖v) ∈ carrier R"
    using a_inv_closed f_def by auto
  thus "(∃x ∈ carrier R. f = [𝟭⇘R⇙, ⊖⇘R⇙x])" by auto
next
  assume "(∃x ∈ carrier R. f = [𝟭, ⊖x])"
  then obtain x where f_def: "f = [𝟭,⊖x]" "x ∈ carrier R" by auto
  have a:"degree f = 1" using f_def(2) unfolding f_def by simp
  have b:"f ∈ carrier P"
    using f_def(2) unfolding univ_poly_carrier[symmetric]
    unfolding f_def polynomial_def by simp
  have c: "pirreducible (carrier R) f"
    by (intro degree_one_imp_pirreducible a b)
  have d: "lead_coeff f = 𝟭" unfolding f_def by simp
  show "monic_irreducible_poly R f ∧ degree f = 1"
    using a b c d
    unfolding monic_irreducible_poly_def monic_poly_def
    by auto
qed
lemma multiplicity_ge_iff:
  assumes "monic_irreducible_poly R d"
  assumes "f ∈ carrier P - {𝟬⇘P⇙}"
  shows "pmult d f ≥ k ⟷ d [^]⇘P⇙ k pdivides f"
proof -
  have a:"f ∈ carrier (mult_of P)"
    using assms(2) by simp
  have b: "d ∈ carrier (mult_of P)"
    using assms(1) monic_poly_carr_2
    unfolding monic_irreducible_poly_def by simp
  have c: "irreducible (mult_of P) d"
    using assms(1) monic_poly_carr_2
    using p.irreducible_imp_irreducible_mult
    unfolding monic_irreducible_poly_def
    unfolding ring_irreducible_def monic_poly_def
    by simp
  have d: "d [^]⇘P⇙ k ∈ carrier P" using b by simp
  have "pmult d f ≥ k ⟷ d [^]⇘(mult_of P)⇙ k divides⇘(mult_of P)⇙ f"
    unfolding pmult_def
    by (intro p.mult_of.multiplicity_ge_iff a b c)
  also have "... ⟷ d [^]⇘P⇙ k pdivides⇘R⇙ f"
    using p.divides_imp_divides_mult[OF d assms(2)]
    using divides_mult_imp_divides
    unfolding pdivides_def nat_pow_mult_of
    by auto
  finally show ?thesis by simp
qed
lemma multiplicity_ge_1_iff_pdivides:
  assumes "monic_irreducible_poly R d" "f ∈ carrier P - {𝟬⇘P⇙}"
  shows "pmult d f ≥ 1 ⟷ d pdivides f"
proof -
  have "d ∈ carrier P"
    using assms(1) monic_poly_carr
    unfolding monic_irreducible_poly_def
    by simp
  thus ?thesis
    using multiplicity_ge_iff[OF assms, where k="1"]
    by simp
qed
lemma divides_monic_poly:
  assumes "monic_poly R f" "monic_poly R g"
  assumes "⋀d. monic_irreducible_poly R d
    ⟹ pmult d f ≤ pmult d g"
  shows "f pdivides g"
proof -
  have a:"f ∈ carrier (mult_of P)" "g ∈ carrier (mult_of P)"
    using monic_poly_carr_2 assms(1,2) by auto
  have "f divides⇘(mult_of P)⇙ g"
    using assms(3) unfolding pmult_def
    by (intro p.mult_of.divides_iff_mult_mono
        [OF a monic_polys_are_canonical_irreducibles]) simp
  thus ?thesis
    unfolding pdivides_def using divides_mult_imp_divides by simp
qed
end
lemma monic_poly_hom:
  assumes "monic_poly R f"
  assumes "h ∈ ring_iso R S" "domain R" "domain S"
  shows "monic_poly S (map h f)"
proof -
  have c: "h ∈ ring_hom R S"
    using assms(2) ring_iso_def by auto
  have e: "f ∈ carrier (poly_ring R)"
    using assms(1) unfolding monic_poly_def by simp
  have a:"f ≠ []"
    using assms(1) unfolding monic_poly_def by simp
  hence "map h f ≠ []" by simp
  moreover have "lead_coeff f = 𝟭⇘R⇙"
    using assms(1) unfolding monic_poly_def by simp
  hence "lead_coeff (map h f) = 𝟭⇘S⇙"
    using ring_hom_one[OF c] by (simp add: hd_map[OF a])
  ultimately show ?thesis
    using carrier_hom[OF e assms(2-4)]
    unfolding monic_poly_def by simp
qed
lemma monic_irreducible_poly_hom:
  assumes "monic_irreducible_poly R f"
  assumes "h ∈ ring_iso R S" "domain R" "domain S"
  shows "monic_irreducible_poly S (map h f)"
proof -
  have a:
    "pirreducible⇘R⇙ (carrier R) f"
    "f ∈ carrier (poly_ring R)"
    "monic_poly R f"
    using assms(1)
    unfolding monic_poly_def monic_irreducible_poly_def
    by auto
  have "pirreducible⇘S⇙ (carrier S) (map h f)"
    using a pirreducible_hom assms by auto
  moreover have "monic_poly S (map h f)"
    using a monic_poly_hom[OF _ assms(2,3,4)] by simp
  ultimately show ?thesis
    unfolding monic_irreducible_poly_def by simp
qed
end