Theory Finite_Fields.Rabin_Irreducibility_Test

section ‹Rabin's test for irreducible polynomials›

theory Rabin_Irreducibility_Test
  imports Card_Irreducible_Polynomials_Aux
begin

text ‹This section introduces an effective test for irreducibility of polynomials
(in finite fields) based on Rabin~\cite{rabin1980}.›

definition pcoprime :: "_  'a list  'a list  bool" (pcoprimeı›)
  where "pcoprimeRp q =
    (r  carrier (poly_ring R). r pdividesRp  r pdividesRq  degree r = 0)"

lemma pcoprimeI:
  assumes "r. r  carrier (poly_ring R)  r pdividesRp  r pdividesRq  degree r = 0"
  shows "pcoprimeRp q"
  using assms unfolding pcoprime_def by auto

context field
begin

interpretation r:polynomial_ring R "(carrier R)"
  unfolding polynomial_ring_def polynomial_ring_axioms_def
  using carrier_is_subfield field_axioms by force

lemma pcoprime_one: "pcoprimeRp 𝟭poly_ring R⇙"
proof (rule pcoprimeI)
  fix r
  assume r_carr: "r  carrier (poly_ring R)"
  moreover assume "r pdividesR𝟭poly_ring R⇙"
  moreover have "𝟭poly_ring R []" by (simp add:univ_poly_one)
  ultimately have "degree r  degree 𝟭poly_ring R⇙"
    by (intro pdivides_imp_degree_le[OF carrier_is_subring] r_carr) auto
  also have "... = 0" by (simp add:univ_poly_one)
  finally show "degree r = 0" by auto
qed

lemma pcoprime_left_factor:
  assumes "x  carrier (poly_ring R)"
  assumes "y  carrier (poly_ring R)"
  assumes "z  carrier (poly_ring R)"
  assumes "pcoprimeR(x poly_ring Ry) z"
  shows "pcoprimeRx z"
proof (rule pcoprimeI)
  fix r
  assume r_carr: "r  carrier (poly_ring R)"
  assume "r pdividesRx"
  hence "r pdividesR(x poly_ring Ry)"
    using assms(1,2) r_carr r.p.divides_prod_r unfolding pdivides_def by simp
  moreover assume "r pdividesRz"
  ultimately show "degree r = 0" using assms(4) r_carr unfolding pcoprime_def by simp
qed

lemma pcoprime_sym:
  shows "pcoprime x y = pcoprime y x"
  unfolding pcoprime_def by auto

lemma pcoprime_left_assoc_cong_aux:
  assumes "x1  carrier (poly_ring R)" "x2  carrier (poly_ring R)"
  assumes "x2 poly_ring Rx1"
  assumes "y  carrier (poly_ring R)"
  assumes "pcoprime x1 y"
  shows "pcoprime x2 y"
  using assms r.p.divides_cong_r[OF _ assms(3)] unfolding pcoprime_def pdivides_def by simp

lemma pcoprime_left_assoc_cong:
  assumes "x1  carrier (poly_ring R)" "x2  carrier (poly_ring R)"
  assumes "x1 poly_ring Rx2"
  assumes "y  carrier (poly_ring R)"
  shows "pcoprime x1 y = pcoprime x2 y"
  using assms pcoprime_left_assoc_cong_aux r.p.associated_sym by metis

lemma pcoprime_right_assoc_cong:
  assumes "x1  carrier (poly_ring R)" "x2  carrier (poly_ring R)"
  assumes "x1 poly_ring Rx2"
  assumes "y  carrier (poly_ring R)"
  shows "pcoprime y x1 = pcoprime y x2"
  using assms pcoprime_sym pcoprime_left_assoc_cong by metis

lemma pcoprime_step:
  assumes "f  carrier (poly_ring R)"
  assumes "g  carrier (poly_ring R)"
  shows "pcoprime f g  pcoprime g (f pmod g)"
proof -
  have "d pdivides f  d pdivides (f pmod g)" if "d  carrier (poly_ring R)" "d pdivides g" for d
  proof -
    have "d pdivides f  d pdivides (g r.P(f pdiv g) r.P(f pmod g))"
      using pdiv_pmod[OF carrier_is_subfield assms] by simp
    also have "...  d pdivides ((f pmod g))"
      using that assms long_division_closed[OF carrier_is_subfield] r.p.divides_prod_r
      unfolding pdivides_def by (intro r.p.div_sum_iff) simp_all
    finally show ?thesis by simp
  qed
  hence "d pdivides f  d pdivides g  d pdivides g  d pdivides (f pmod g)"
    if "d  carrier (poly_ring R)" for d
    using that by auto
  thus ?thesis
    unfolding pcoprime_def by auto
qed

lemma pcoprime_zero_iff:
  assumes "f  carrier (poly_ring R)"
  shows "pcoprime f []  length f = 1"
proof -
  consider (i) "length f = 0" | (ii) "length f = 1" | (iii) "length f > 1"
    by linarith
  thus ?thesis
  proof (cases)
    case i
    hence "f = []" by simp
    moreover have "X pdivides []" using r.pdivides_zero r.var_closed(1) by blast
    moreover have "degree X = 1" using degree_var by simp
    ultimately have "¬pcoprime f []" using r.var_closed(1) unfolding pcoprime_def by auto
    then show ?thesis using i by auto
  next
    case ii
    hence "f  []" "degree f = 0" by auto
    hence "degree d = 0" if "d pdivides f" "d  carrier (poly_ring R)" for d
      using that(1) pdivides_imp_degree_le[OF carrier_is_subring that(2) assms] by simp
    hence "pcoprime f []" unfolding pcoprime_def by auto
    then show ?thesis using ii by simp
  next
    case iii
    have "f pdivides f" using assms unfolding pdivides_def by simp
    moreover have "f pdivides []" using assms r.pdivides_zero by blast
    moreover have "degree f > 0" using iii by simp
    ultimately have "¬pcoprime f []" using assms unfolding pcoprime_def by auto
    then show ?thesis using iii by auto
  qed
qed

end

context finite_field
begin

interpretation r:polynomial_ring R "(carrier R)"
  unfolding polynomial_ring_def polynomial_ring_axioms_def
  using carrier_is_subfield field_axioms by force

lemma exists_irreducible_proper_factor:
  assumes "monic_poly R f" "degree f > 0" "¬monic_irreducible_poly R f"
  shows "g. monic_irreducible_poly R g  g pdividesRf  degree g < degree f"
proof -
  define S where "S = {d. monic_irreducible_poly R d  0 < pmult d f}"

  have f_carr: "f  carrier (poly_ring R)" "f  𝟬poly_ring R⇙"
    using assms(1) unfolding monic_poly_def univ_poly_zero by auto

  have "S  {}"
  proof (rule ccontr)
    assume S_empty: "¬(S  {})"
    have "f = (poly_ring RdS. d [^]poly_ring Rpmult d f)"
      unfolding S_def by (intro factor_monic_poly assms(1))
    also have "... = 𝟭poly_ring R⇙" using S_empty by simp
    finally have "f = 𝟭poly_ring R⇙" by simp
    hence "degree f = 0" using degree_one by simp
    thus "False" using assms(2) by simp
  qed
  then obtain g where g_irred: "monic_irreducible_poly R g" and "0 < pmult g f"
    unfolding S_def by auto

  hence "1  pmult g f" by simp

  hence g_div: "g pdivides f" using multiplicity_ge_1_iff_pdivides f_carr g_irred by blast

  then obtain h where f_def: "f = g poly_ring Rh" and h_carr:"h  carrier (poly_ring R)"
    unfolding pdivides_def by auto

  have g_nz: "g  𝟬poly_ring R⇙" and h_nz: "h  𝟬poly_ring R⇙"
    and g_carr: "g  carrier (poly_ring R)"
    using f_carr(2) h_carr g_irred unfolding f_def monic_irreducible_poly_def monic_poly_def
    by auto

  have "degree f = degree g + degree h"
    using g_nz h_nz g_carr h_carr unfolding f_def by (intro degree_mult[OF r.K_subring]) auto
  moreover have "degree h > 0"
  proof (rule ccontr)
    assume "¬(degree h > 0)"
    hence "degree h = 0" by simp
    hence "h  Units (poly_ring R)"
      using h_carr h_nz by (simp add: carrier_is_subfield univ_poly_units' univ_poly_zero)
    hence "f poly_ring Rg"
      unfolding f_def using g_carr r.p.associatedI2' by force
    hence "f mult_of (poly_ring R)g"
      using f_carr g_nz g_carr by (simp add: r.p.assoc_iff_assoc_mult)
    hence "f = g"
      using monic_poly_not_assoc assms(1) g_irred unfolding monic_irreducible_poly_def by simp
    hence "monic_irreducible_poly R f"
      using g_irred by simp
    thus "False"
      using assms(3) by auto
  qed
  ultimately have "degree g < degree f" by simp
  thus ?thesis using g_irred g_div by auto
qed

theorem rabin_irreducibility_condition:
  assumes "monic_poly R f" "degree f > 0"
  defines "N  {degree f div p | p . Factorial_Ring.prime p  p dvd degree f}"
  shows "monic_irreducible_poly R f 
    (f pdivides gauss_poly R (order R^degree f)  (n  N. pcoprime (gauss_poly R (order R^n)) f))"
    (is "?L  ?R1  ?R2")
proof -
  have f_carr: "f  carrier (poly_ring R)"
    using assms(1) unfolding monic_poly_def by blast

  have "?R1" if "?L"
    using div_gauss_poly_iff[where n="degree f"] that assms(2) by simp
  moreover have "False" if cthat:"¬pcoprime (gauss_poly R (order R^n)) f" "?L" "n  N" for n
  proof -
    obtain d where d_def:
      "d pdivides f"
      "d pdivides (gauss_poly R (order R^n))" "degree d > 0" "d  carrier (poly_ring R)"
      using cthat(1) unfolding pcoprime_def by auto

    obtain p where p_def:
      "n = degree f div p" "Factorial_Ring.prime p" "p dvd degree f"
      using cthat(3) unfolding N_def by auto

    have n_gt_0: "n > 0"
      using p_def assms(2) by (metis dvd_div_eq_0_iff gr0I)

    have "d  Units (poly_ring R)"
      using d_def(3,4)  univ_poly_units'[OF carrier_is_subfield] by simp
    hence "f pdivides d"
      using cthat(2) d_def(1,4) unfolding monic_irreducible_poly_def ring_irreducible_def
        Divisibility.irreducible_def properfactor_def pdivides_def f_carr by auto
    hence "f pdivides (gauss_poly R (order R^n))"
      using d_def(2,4) f_carr r.p.divides_trans unfolding pdivides_def by metis
    hence "degree f dvd n"
      using n_gt_0 div_gauss_poly_iff[OF _ cthat(2)] by auto
    thus "False"
      using p_def by (metis assms(2) div_less_dividend n_gt_0 nat_dvd_not_less prime_gt_1_nat)
  qed
  moreover have "False" if not_l:"¬?L" and r1:"?R1" and r2: "?R2"
  proof -
    obtain g where g_def: "g pdivides f" "degree g < degree f" "monic_irreducible_poly R g"
      using r1 not_l exists_irreducible_proper_factor assms(1,2) by auto

    have g_carr: "g  carrier (poly_ring R)" and g_nz: "g  𝟬poly_ring R⇙"
      using g_def(3) unfolding monic_irreducible_poly_def monic_poly_def by (auto simp:univ_poly_zero)

    have "g pdivides gauss_poly R (order R^degree f)"
      using g_carr r1 g_def(1) unfolding pdivides_def using r.p.divides_trans by blast

    hence "degree g dvd degree f"
      using div_gauss_poly_iff[OF assms(2) g_def(3)] by auto

    then obtain t where deg_f_def:"degree f = t * degree g"
      by fastforce
    hence "t > 1" using g_def(2) by simp
    then obtain p where p_prime: "Factorial_Ring.prime p" "p dvd t"
      by (metis order_less_irrefl prime_factor_nat)
    hence p_div_deg_f: "p dvd degree f"
      unfolding deg_f_def by simp
    define n where "n = degree f div p"
    have n_in_N: "n  N"
      unfolding N_def n_def using p_prime(1) p_div_deg_f by auto

    have deg_g_dvd_n: "degree g dvd n"
      using p_prime(2) unfolding n_def deg_f_def by auto

    have n_gt_0: "n > 0"
      using p_div_deg_f assms(2) p_prime(1) unfolding n_def
      by (metis dvd_div_eq_0_iff gr0I)

    have deg_g_gt_0: "degree g > 0"
      using monic_poly_min_degree[OF g_def(3)] by simp

    have 0:"g pdivides gauss_poly R (order R^n)"
      using deg_g_dvd_n div_gauss_poly_iff[OF n_gt_0 g_def(3)] by simp

    have "pcoprime (gauss_poly R (order R^n)) f"
      using n_in_N r2 by simp
    thus "False"
      using 0 g_def(1) g_carr deg_g_gt_0 unfolding pcoprime_def by simp
  qed
  ultimately show ?thesis
    by auto
qed

text ‹A more general variant of the previous theorem for non-monic polynomials. The result is
from Lemma~1 \cite{rabin1980}.›

theorem rabin_irreducibility_condition_2:
  assumes "f  carrier (poly_ring R)" "degree f > 0"
  defines "N  {degree f div p | p . Factorial_Ring.prime p  p dvd degree f}"
  shows "pirreducible (carrier R) f 
    (f pdivides gauss_poly R (order R^degree f)  (n  N. pcoprime (gauss_poly R (order R^n)) f))"
    (is "?L  ?R1  ?R2")
proof -
  define α where "α = [inv (hd f)]"
  let ?g = "(λx. gauss_poly R (order R^x))"
  let ?h = "α poly_ring Rf"

  have f_nz: "f  𝟬poly_ring R⇙" unfolding univ_poly_zero using assms(2) by auto

  hence "hd f  carrier R - {𝟬}" using assms(1) lead_coeff_carr by simp
  hence "inv (hd f)  carrier R - {𝟬}" using field_Units by auto
  hence α_unit: "α  Units (poly_ring R)"
    unfolding α_def using univ_poly_carrier_units by simp

  have α_nz: "α  𝟬poly_ring R⇙" unfolding univ_poly_zero α_def by simp
  have "hd ?h = hd α  hd f"
    using α_nz f_nz assms(1) α_unit by (intro lead_coeff_mult) auto
  also have "... = inv (hd f)  hd f" unfolding α_def by simp
  also have "... = 𝟭" using lead_coeff_carr f_nz assms(1) by (simp add: field_Units)
  finally have "hd ?h = 𝟭" by simp
  moreover have "?h  []"
    using α_nz f_nz univ_poly_zero by (metis α_unit assms(1) r.p.Units_closed r.p.integral)
  ultimately have h_monic: "monic_poly R ?h"
    using r.p.Units_closed[OF α_unit] assms(1) unfolding monic_poly_def by auto

  have "degree ?h = degree α + degree f"
    using assms(1) f_nz α_unit α_nz by (intro degree_mult[OF carrier_is_subring]) auto
  also have "... = degree f" unfolding α_def by simp
  finally have deg_f: "degree f = degree ?h" by simp

  have hf_cong:"?h r.Pf"
    using assms(1) α_unit by (simp add: r.p.Units_closed r.p.associatedI2 r.p.m_comm)
  hence 0: "f pdivides ?g (degree f)  ?h pdivides ?g (degree f)"
    unfolding pdivides_def using r.p.divides_cong_l r.p.associated_sym
    using r.p.Units_closed[OF α_unit] assms(1) gauss_poly_carr by blast

  have 1: "pcoprime (?g n) f  pcoprime (?g n) ?h" for n
    using hf_cong r.p.associated_sym r.p.Units_closed[OF α_unit] assms(1)
    by (intro pcoprime_right_assoc_cong gauss_poly_carr) auto

  have "?L  pirreducible (carrier R) (α poly_ring Rf)"
    using α_unit α_nz assms(1) f_nz r.p.integral unfolding ring_irreducible_def
    by (intro arg_cong2[where f="(∧)"] r.p.irreducible_prod_unit assms) auto
  also have "...  monic_irreducible_poly R (α poly_ring Rf)"
    using h_monic unfolding monic_irreducible_poly_def by auto
  also have "...  ?h pdivides ?g (degree f)  (n  N. pcoprime (?g n) ?h)"
    using assms(2) unfolding N_def deg_f by (intro rabin_irreducibility_condition h_monic) auto
  also have "...  f pdivides ?g (degree f)  (n  N. pcoprime (?g n) f)"
    using 0 1 by simp
  finally show ?thesis by simp
qed

end

end