Theory Finite_Fields.Rabin_Irreducibility_Test_Code
section ‹Executable Code for Rabin's Irreducibility Test›
theory Rabin_Irreducibility_Test_Code
  imports
    Finite_Fields_Poly_Ring_Code
    Finite_Fields_Mod_Ring_Code
    Rabin_Irreducibility_Test
begin
fun pcoprime⇩C :: "('a, 'b) idx_ring_scheme ⇒ 'a list ⇒ 'a list ⇒ bool"
  where "pcoprime⇩C R f g = (length (snd (ext_euclidean R f g)) = 1)"
declare pcoprime⇩C.simps[simp del]
lemma pcoprime_c:
  assumes "field⇩C R"
  assumes "f ∈ carrier (poly_ring (ring_of R))"
  assumes "g ∈ carrier (poly_ring (ring_of R))"
  shows "pcoprime⇩C R f g ⟷ pcoprime⇘ring_of R⇙ f g"  (is "?L = ?R")
proof (cases "f = [] ∧ g = []")
  case True
  interpret field "ring_of R"
    using assms(1) unfolding field⇩C_def by simp
  interpret d_poly_ring: domain "poly_ring (ring_of R)"
    by (rule univ_poly_is_domain[OF carrier_is_subring])
  have "?L = False" using True by (simp add: pcoprime⇩C.simps ext_euclidean.simps poly_def)
  also have "... ⟷  (length 𝟬⇘poly_ring (ring_of R)⇙ = 1)" by (simp add:univ_poly_zero)
  also have "... ⟷ pcoprime⇘ring_of R⇙ 𝟬⇘poly_ring (ring_of R)⇙  []"
    by (subst pcoprime_zero_iff) (simp_all)
  also have "... ⟷ ?R" using True by (simp add: univ_poly_zero)
  finally show ?thesis by simp
next
  case False
  let ?P = "poly_ring (ring_of R)"
  interpret field "ring_of R"
    using assms(1) unfolding field⇩C_def by simp
  interpret d_poly_ring: domain "poly_ring (ring_of R)"
    by (rule univ_poly_is_domain[OF carrier_is_subring])
  obtain s u v where suv_def: "((u,v),s) = ext_euclidean R f g" by (metis surj_pair)
  have s_eq:"s = f ⊗⇘?P⇙ u ⊕⇘?P⇙ g ⊗⇘?P⇙ v" (is "?T1")
    and s_div_f: "s pdivides⇘ring_of R⇙ f" and s_div_g: "s pdivides⇘ring_of R⇙ g" (is "?T3")
    and suv_carr: "{s, u, v} ⊆ carrier ?P"
    and s_nz: "s ≠ []"
    using False suv_def[symmetric] ext_euclidean[OF assms(1,2,3)] by auto
  have "?L ⟷ length s = 1" using suv_def[symmetric] by (simp add:pcoprime⇩C.simps)
  also have "... ⟷ ?R"
    unfolding pcoprime_def
  proof (intro iffI impI ballI)
    fix r assume len_s: "length s = 1"
    assume r_carr:"r ∈ carrier ?P"
      and "r pdivides⇘ring_of R⇙ f ∧ r pdivides⇘ring_of R⇙ g"
    hence r_div: "pmod f r = 𝟬⇘?P⇙"  "pmod g r = 𝟬⇘?P⇙" unfolding univ_poly_zero
      using assms(2,3) pmod_zero_iff_pdivides[OF carrier_is_subfield] by auto
    have "pmod s r = pmod (f ⊗⇘?P⇙ u) r ⊕⇘?P⇙ pmod (g ⊗⇘?P⇙ v) r"
      using r_carr suv_carr assms unfolding s_eq
      by (intro long_division_add[OF carrier_is_subfield]) auto
    also have "... = pmod (pmod f r ⊗⇘?P⇙ u) r  ⊕⇘?P⇙ pmod (pmod g r ⊗⇘?P⇙ v) r"
      using r_carr suv_carr assms by (intro arg_cong2[where f="(⊕⇘?P⇙)"] pmod_mult_left) auto
    also have "... = pmod 𝟬⇘?P⇙ r  ⊕⇘?P⇙ pmod 𝟬⇘?P⇙ r"
      using suv_carr unfolding r_div by simp
    also have "... = []" using r_carr unfolding univ_poly_zero
      by (simp add: long_division_zero[OF carrier_is_subfield] univ_poly_add)
    finally have "pmod s r =  []" by simp
    hence "r pdivides⇘ring_of R⇙ s"
      using r_carr suv_carr pmod_zero_iff_pdivides[OF carrier_is_subfield] by auto
    hence "degree r ≤ degree s"
      using s_nz r_carr suv_carr by (intro pdivides_imp_degree_le[OF carrier_is_subring]) auto
    thus "degree r = 0" using len_s by simp
  next
    assume "∀r∈carrier ?P. r pdivides⇘ring_of R⇙ f ∧ r pdivides⇘ring_of R⇙ g ⟶ degree r = 0"
    hence "degree s = 0" using s_div_f s_div_g suv_carr by simp
    thus "length s =1" using s_nz
      by (metis diff_is_0_eq diffs0_imp_equal length_0_conv less_one linorder_le_less_linear)
  qed
  finally show ?thesis by simp
qed
text ‹The following is a fast version of @{term "pmod"} for polynomials (to a high power) that
need to be reduced, this is used for the higher order term of the Gauss polynomial.›
fun pmod_pow⇩C :: "('a,'b) idx_ring_scheme ⇒ 'a list ⇒ nat ⇒ 'a list ⇒ 'a list"
  where "pmod_pow⇩C F f n g = (
    let r = (if n ≥ 2 then pmod_pow⇩C F f (n div 2) g ^⇩C⇘poly F⇙ 2 else 1⇩C⇘poly F⇙)
    in pmod⇩C F (r *⇩C⇘poly F⇙ (f ^⇩C⇘poly F⇙ (n mod 2))) g)"
declare pmod_pow⇩C.simps[simp del]
lemma pmod_pow_c:
  assumes "field⇩C R"
  assumes "f ∈ carrier (poly_ring (ring_of R))"
  assumes "g ∈ carrier (poly_ring (ring_of R))"
  shows "pmod_pow⇩C R f n g = ring.pmod (ring_of R) (f [^]⇘poly_ring (ring_of R)⇙ n) g"
proof (induction n rule:nat_less_induct)
  case (1 n)
  let ?P = "poly_ring (ring_of R)"
  interpret field "ring_of R"
    using assms(1) unfolding field⇩C_def by simp
  interpret d_poly_ring: domain "poly_ring (ring_of R)"
    by (rule univ_poly_is_domain[OF carrier_is_subring])
  have ring_c: "ring⇩C R" using assms(1) unfolding field⇩C_def domain⇩C_def cring⇩C_def by auto
  have d_poly: "domain⇩C (poly R)" using assms (1) unfolding field⇩C_def by (intro poly_domain) auto
  have ind: "pmod_pow⇩C R f m g = pmod (f [^]⇘?P⇙ m) g" if "m < n" for m
    using 1 that by auto
  define r where "r = (if n ≥ 2 then pmod_pow⇩C R f (n div 2) g ^⇩C⇘poly R⇙ 2 else 1⇩C⇘poly R⇙)"
  have "pmod r g = pmod (f [^]⇘?P⇙ (n - (n mod 2))) g ∧ r ∈ carrier ?P"
  proof (cases "n ≥ 2")
    case True
    hence "r = pmod_pow⇩C R f (n div 2) g [^]⇘?P⇙ (2 :: nat)"
      unfolding r_def domain_cD[OF d_poly] by (simp add:ring_of_poly[OF ring_c])
    also have "... = pmod (f [^]⇘?P⇙ (n div 2)) g [^]⇘?P⇙ (2 :: nat)"
      using True by (intro arg_cong2[where f="([^]⇘?P⇙)"] refl ind) auto
    finally have r_alt: "r = pmod (f [^]⇘?P⇙ (n div 2)) g [^]⇘?P⇙ (2 :: nat)"
      by simp
    have "pmod r g = pmod (pmod (f [^]⇘?P⇙ (n div 2)) g ⊗⇘?P⇙ pmod (f [^]⇘?P⇙ (n div 2)) g) g"
      unfolding r_alt using assms(2,3) long_division_closed[OF carrier_is_subfield]
      by (simp add:numeral_eq_Suc) algebra
    also have "... = pmod (f [^]⇘?P⇙ (n div 2) ⊗⇘?P⇙ f [^]⇘?P⇙ (n div 2)) g"
      using assms(2,3) by (intro pmod_mult_both[symmetric]) auto
    also have "... = pmod (f [^]⇘?P⇙ ((n div 2)+(n div 2))) g"
      using assms(2,3) by (subst d_poly_ring.nat_pow_mult) auto
    also have "... = pmod (f [^]⇘?P⇙ (n - (n mod 2))) g"
      by (intro arg_cong2[where f="pmod"] refl arg_cong2[where f="([^]⇘?P⇙)"]) presburger
    finally have "pmod r g = pmod (f [^]⇘?P⇙ (n - (n mod 2))) g"
      by simp
    moreover have "r ∈ carrier ?P"
      using assms(2,3) long_division_closed[OF carrier_is_subfield] unfolding r_alt by auto
    ultimately show ?thesis by auto
  next
    case False
    hence "r = 𝟭⇘?P⇙"
      unfolding r_def using domain_cD[OF d_poly] ring_of_poly[OF ring_c] by simp
    also have "... = f [^]⇘?P⇙ (0 :: nat)" by simp
    also have "... = f [^]⇘?P⇙ (n - (n mod 2))"
      using False by (intro arg_cong2[where f="([^]⇘?P⇙)"] refl) auto
    finally have "r = f [^]⇘?P⇙ (n - (n mod 2))" by simp
    then show ?thesis using assms(2) by simp
  qed
  hence r_exp: "pmod r g = pmod (f [^]⇘?P⇙ (n - (n mod 2))) g" and r_carr: "r ∈ carrier ?P"
    by auto
  have "pmod_pow⇩C R f n g = pmod⇩C R (r *⇩C⇘poly R⇙ (f ^⇩C⇘poly R⇙ (n mod 2))) g"
    by (subst pmod_pow⇩C.simps) (simp add:r_def[symmetric])
  also have "... = pmod⇩C R (r ⊗⇘?P⇙ (f [^]⇘?P⇙ (n mod 2))) g"
    unfolding domain_cD[OF d_poly] by (simp add:ring_of_poly[OF ring_c])
  also have "... = pmod (r ⊗⇘?P⇙ (f [^]⇘?P⇙ (n mod 2))) g"
    using r_carr assms(2,3) by (intro pmod_c[OF assms(1)]) auto
  also have "... = pmod (pmod r g ⊗⇘?P⇙ (f [^]⇘?P⇙ (n mod 2))) g"
    using r_carr assms(2,3) by (intro pmod_mult_left) auto
  also have "... = pmod (f [^]⇘?P⇙ (n - (n mod 2)) ⊗⇘?P⇙ (f [^]⇘?P⇙ (n mod 2))) g"
    using assms(2,3) unfolding r_exp by (intro pmod_mult_left[symmetric]) auto
  also have "... = pmod (f [^]⇘?P⇙ ((n - (n mod 2)) + (n mod 2))) g"
    using assms(2,3) by (intro arg_cong2[where f="pmod"] refl d_poly_ring.nat_pow_mult) auto
  also have "... = pmod (f [^]⇘?P⇙ n) g" by simp
  finally show "pmod_pow⇩C R f n g = pmod (f [^]⇘?P⇙ n) g" by simp
qed
text ‹The following function checks whether a given polynomial is coprime with the
Gauss polynomial $X^n - X$.›
definition pcoprime_with_gauss_poly :: "('a,'b) idx_ring_scheme ⇒ 'a list ⇒ nat ⇒ bool"
  where "pcoprime_with_gauss_poly F p n =
    (pcoprime⇩C F p (pmod_pow⇩C F X⇩C⇘F⇙ n p +⇩C⇘poly F⇙ (-⇩C⇘poly F⇙ pmod⇩C F X⇩C⇘F⇙ p)))"
definition divides_gauss_poly :: "('a,'b) idx_ring_scheme ⇒ 'a list ⇒ nat ⇒ bool"
  where "divides_gauss_poly F p n =
    (pmod_pow⇩C F X⇩C⇘F⇙ n p +⇩C⇘poly F⇙ (-⇩C⇘poly F⇙ pmod⇩C F X⇩C⇘F⇙ p) = [])"
lemma mod_gauss_poly:
  assumes "field⇩C R"
  assumes "f ∈ carrier (poly_ring (ring_of R))"
  shows "pmod_pow⇩C R X⇩C⇘R⇙ n f +⇩C⇘poly R⇙ (-⇩C⇘poly R⇙ pmod⇩C R X⇩C⇘R⇙ f) =
    ring.pmod (ring_of R) (gauss_poly (ring_of R) n) f" (is "?L = ?R")
proof -
  interpret field "ring_of R"
    using assms(1) unfolding field⇩C_def by simp
  interpret d_poly_ring: domain "poly_ring (ring_of R)"
    by (rule univ_poly_is_domain[OF carrier_is_subring])
  have ring_c: "ring⇩C R" using assms(1) unfolding field⇩C_def domain⇩C_def cring⇩C_def by auto
  have d_poly: "domain⇩C (poly R)" using assms (1) unfolding field⇩C_def by (intro poly_domain) auto
  let ?P = "poly_ring (ring_of R)"
  have "?L = pmod_pow⇩C R X⇘ring_of R⇙ n f ⊕⇘?P⇙ -⇩C⇘poly R⇙ pmod⇩C R X⇘ring_of R⇙ f"
    by (simp add: poly_var domain_cD[OF d_poly] ring_of_poly[OF ring_c])
  also have "...= pmod (X⇘ring_of R⇙[^]⇘?P⇙ n) f⊕⇘?P⇙ -⇩C⇘poly R⇙ pmod X⇘ring_of R⇙ f"
    using assms var_carr[OF carrier_is_subring] by (intro refl arg_cong2[where f="(⊕⇘?P⇙)"]
        pmod_pow_c arg_cong[where f="λx. (-⇩C⇘poly R⇙ x)"] pmod_c) auto
  also have "... =pmod (X⇘ring_of R⇙[^]⇘?P⇙ n) f⊖⇘?P⇙ pmod X⇘ring_of R⇙ f"
    unfolding a_minus_def using assms(1,2) var_carr[OF carrier_is_subring]
      ring_of_poly[OF ring_c] long_division_closed[OF carrier_is_subfield]
    by (subst domain_cD[OF d_poly]) auto
  also have "... = pmod (X⇘ring_of R⇙[^]⇘?P⇙ n) f ⊕⇘?P⇙ pmod (⊖⇘?P⇙ X⇘ring_of R⇙) f"
    using assms(2) var_carr[OF carrier_is_subring]
    unfolding a_minus_def by (subst long_division_a_inv[OF carrier_is_subfield]) auto
  also have " ... = pmod (gauss_poly (ring_of R) n) f"
    using assms(2) var_carr[OF carrier_is_subring] var_pow_carr[OF carrier_is_subring]
    unfolding gauss_poly_def a_minus_def by (subst long_division_add[OF carrier_is_subfield]) auto
  finally show ?thesis by simp
qed
lemma pcoprime_with_gauss_poly:
  assumes "field⇩C R"
  assumes "f ∈ carrier (poly_ring (ring_of R))"
  shows "pcoprime_with_gauss_poly R f n ⟷ pcoprime⇘ring_of R⇙ (gauss_poly (ring_of R) n) f"
    (is "?L = ?R")
proof -
  interpret field "ring_of R"
    using assms(1) unfolding field⇩C_def by simp
  have "?L ⟷ pcoprime⇩C R f (pmod (gauss_poly (ring_of R) n) f)"
    unfolding pcoprime_with_gauss_poly_def using assms by (subst mod_gauss_poly) auto
  also have "... = pcoprime⇘ring_of R⇙ f (pmod (gauss_poly (ring_of R) n) f)"
    using assms gauss_poly_carr long_division_closed[OF carrier_is_subfield]
    by (intro pcoprime_c) auto
  also have "... = pcoprime⇘ring_of R⇙ (gauss_poly (ring_of R) n) f"
    by (intro pcoprime_step[symmetric] gauss_poly_carr assms)
  finally show ?thesis by simp
qed
lemma divides_gauss_poly:
  assumes "field⇩C R"
  assumes "f ∈ carrier (poly_ring (ring_of R))"
  shows "divides_gauss_poly R f n ⟷ f pdivides⇘ring_of R⇙ (gauss_poly (ring_of R) n)"
    (is "?L = ?R")
proof -
  interpret field "ring_of R"
    using assms(1) unfolding field⇩C_def by simp
  have "?L ⟷ (pmod (gauss_poly (ring_of R) n) f = [])"
    unfolding divides_gauss_poly_def using assms by (subst mod_gauss_poly) auto
  also have "... ⟷ ?R"
    using assms gauss_poly_carr by (intro pmod_zero_iff_pdivides[OF carrier_is_subfield]) auto
  finally show ?thesis
    by simp
qed
fun rabin_test_powers :: "('a, 'b) idx_ring_enum_scheme ⇒ nat ⇒ nat list"
  where "rabin_test_powers F n =
    map (λp. idx_size F^(n div p)) (filter (λp. prime p ∧ p dvd n) [2..<(n+1)] )"
text ‹Given a monic polynomial with coefficients over a finite field returns true, if it is
irreducible›
fun rabin_test :: "('a, 'b) idx_ring_enum_scheme ⇒ 'a list ⇒ bool"
  where "rabin_test F f = (
    if degree f = 0 then
      False
    else (if ¬divides_gauss_poly F f (idx_size F^degree f) then
      False
    else (list_all (pcoprime_with_gauss_poly F f) (rabin_test_powers F (degree f)))))"
declare rabin_test.simps[simp del]
context
  fixes R
  assumes field_R: "field⇩C R"
  assumes enum_R: "enum⇩C R"
begin
interpretation finite_field "(ring_of R)"
  using field_R enum_cD[OF enum_R] unfolding field⇩C_def
  by (simp add:finite_field_def finite_field_axioms_def)
lemma rabin_test_powers:
  assumes "n > 0"
  shows "set (rabin_test_powers R n) =
    {order (ring_of R)^ (n div p) | p . Factorial_Ring.prime p ∧ p dvd n}"
    (is "?L = ?R")
proof -
  let ?f = "(λx. order (ring_of R) ^ (n div x))"
  have 0:"p ∈ {2..n}" if "Factorial_Ring.prime p" "p dvd n"  for p
    using assms that by (simp add: dvd_imp_le prime_ge_2_nat)
  have "?L = ?f ` {p ∈ {2..n}.  Factorial_Ring.prime p ∧ p dvd n}"
    using enum_cD[OF enum_R] by auto
  also have "... = ?f ` {p.  Factorial_Ring.prime p ∧ p dvd n}"
    using 0 by (intro image_cong Collect_cong) auto
  also have "... = ?R"
    by auto
  finally show ?thesis by simp
qed
lemma rabin_test:
  assumes "monic_poly (ring_of R) f"
  shows "rabin_test R f ⟷ monic_irreducible_poly (ring_of R) f" (is "?L = ?R")
proof (cases "degree f = 0")
  case True
  thus ?thesis unfolding rabin_test.simps using monic_poly_min_degree by fastforce
next
  case False
  define N where "N = {degree f div p | p . Factorial_Ring.prime p ∧ p dvd degree f}"
  have f_carr: "f ∈ carrier (poly_ring (ring_of R))"
    using assms(1) unfolding monic_poly_def by auto
  have deg_f_gt_0: "degree f > 0"
    using False by auto
  have rt_powers: "set (rabin_test_powers R (degree f)) = (λx. order (ring_of R)^x) ` N"
    unfolding rabin_test_powers[OF deg_f_gt_0] N_def by auto
  have "?L ⟷ divides_gauss_poly R f (idx_size R ^ degree f) ∧
    (∀n ∈ set (rabin_test_powers R (degree f)). (pcoprime_with_gauss_poly R f n))"
    using False by (simp add: list_all_def rabin_test.simps del:rabin_test_powers.simps)
  also have "... ⟷ f pdivides⇘ring_of R⇙ (gauss_poly (ring_of R) (order (ring_of R) ^ degree f))
    ∧ (∀n ∈ N. pcoprime⇘ring_of R⇙ (gauss_poly (ring_of R) (order (ring_of R) ^n)) f)"
    unfolding divides_gauss_poly[OF field_R f_carr] pcoprime_with_gauss_poly[OF field_R f_carr]
      rt_powers enum_cD[OF enum_R] by simp
  also have "... ⟷ ?R"
    using False unfolding N_def by (intro rabin_irreducibility_condition[symmetric] assms(1)) auto
  finally show ?thesis by simp
qed
end
end