Theory Carmichael_Numbers

(*
  File:     Carmichael_Numbers.thy
  Authors:  Daniel Stüwe

  Definition and basic properties of Carmichael numbers
*)
section ‹Carmichael Numbers›
theory Carmichael_Numbers
imports
  Residues_Nat
begin

text ‹
  A Carmichael number is a composite number n› that Fermat's test incorrectly labels
  as primes no matter which witness a› is chosen (except in the case that a› shares a
  factor with n›). cite"Carmichael_numbers" and "wiki:Carmichael_number"
definition Carmichael_number :: "nat  bool" where
  "Carmichael_number n  n > 1  ¬prime n  (a. coprime a n  [a ^ (n - 1) = 1] (mod n))"

lemma Carmichael_number_0[simp, intro]: "¬Carmichael_number 0"
  unfolding Carmichael_number_def by simp

lemma Carmichael_number_1[simp, intro]: "¬Carmichael_number 1"
  by (auto simp: Carmichael_number_def)

lemma Carmichael_number_Suc_0[simp, intro]: "¬Carmichael_number (Suc 0)"
  by (auto simp: Carmichael_number_def)

lemma Carmichael_number_not_prime: "Carmichael_number n  ¬prime n"
  by (auto simp: Carmichael_number_def)

lemma Carmichael_number_gt_3: "Carmichael_number n  n > 3"
proof -
  assume *: "Carmichael_number n"
  hence "n > 1" by (auto simp: Carmichael_number_def)
  {
    assume "¬(n > 3)"
    with n > 1 have "n = 2  n = 3" by auto
    with * and Carmichael_number_not_prime[of n] have False by auto
  }
  thus "n > 3" by auto
qed

text ‹
  The proofs are inspired by cite"Carmichael_numbers" and "Carmichael_number_square_free".
›
lemma Carmichael_number_imp_squarefree_aux:
  assumes "Carmichael_number n"
  assumes n: "n = p^r * l" and "prime p" "¬p dvd l"
  assumes "r > 1"
  shows False
proof -
  have "¬ prime n" using Carmichael_number n unfolding Carmichael_number_def by blast

  have * : "[a^(n-1) = 1] (mod n)" if "coprime a n" for a
    using Carmichael_number n that
    unfolding Carmichael_number_def
    by blast

  have "1  n"
    unfolding n using prime p ¬ p dvd l
    by (auto intro: gre1I_nat)

  have "2  n"
  proof(cases "n = 1")
    case True
    then show ?thesis
      unfolding n using 1 < r prime_gt_1_nat[OF prime p]
      by simp
  next
    case False
    then show ?thesis using 1  n by linarith
  qed

  have "p < p^r"
    using prime_gt_1_nat[OF prime p] 1 < r
    by (metis power_one_right power_strict_increasing_iff)

  hence "p < n" using 1  n less_le_trans n by fastforce 

  then have [simp]: "{..n} - {0..Suc 0} = {2..n}" by auto

  obtain a where a: "[a = p + 1] (mod p^r)" "[a = 1] (mod l)"
    using binary_chinese_remainder_nat[of "p^r" l "p + 1" 1] 
    and prime p prime_imp_coprime_nat coprime_power_left_iff ¬p dvd l
    by blast

  hence "coprime a n"
    using lucas_coprime_lemma[of 1 a l] cong_imp_coprime[of "p+1" a "p^r"] 
      and coprime_add_one_left cong_sym 
    unfolding n = p ^ r * l coprime_mult_right_iff coprime_power_right_iff power_one_right
    by blast

  hence "[a ^ (n - 1) = 1] (mod n)"
    using * by blast

  hence "[a ^ (n - 1) = 1] (mod p^r)"
    using n cong_modulus_mult_nat by blast

  hence A: "[a ^ n = a] (mod p^r)"
    using cong_scalar_right[of "a^(n-1)" 1 "p^r" a] 1  n
    unfolding power_Suc2[symmetric]
    by simp

  have "r = Suc (Suc (r - 2))"
    using 1 < r by linarith

  then have "p^r = p^2 * p^(r-2)"
    by (simp add: algebra_simps flip: power_add power_Suc)
   
  hence "[a ^ n = a] (mod p^2)" "[a = p + 1] (mod p^2)"
    using 1 < r A cong_modulus_mult_nat [a = p + 1] (mod p^r)
    by algebra+

  hence 1: "[(p + 1) ^ n = (p + 1)] (mod p^2)"
    by (metis (mono_tags) cong_def power_mod)

  have "[(p + 1) ^ n = (kn. of_nat (n choose k) * p ^ k * 1 ^ (n - k))] (mod p^2)"
    using binomial[of p 1 n] by simp

  also have "(kn. of_nat (n choose k) * p ^ k * 1 ^ (n - k)) =
             (k = 0..1. (n choose k) * p ^ k) + (k {2..n}. of_nat (n choose k) * p ^ k * 1 ^ (n - k))"
    using 2  n finite_atMost[of n]
    by (subst sum.subset_diff[where B = "{0..1}"]) auto

  also have "[(k = 0..1. (n choose k) * p ^ k) = 1] (mod p^2)"
    by (simp add: cong_altdef_nat p ^ r = p2 * p ^ (r - 2) n)

  also have "[(k {2..n}. of_nat (n choose k) * p ^ k * 1 ^ (n - k)) = 0] (mod p^2)"
    by (rule cong_eq_0_I) (clarsimp simp: cong_0_iff le_imp_power_dvd)

  finally have 2: "[(p + 1) ^ n = 1] (mod p^2)" by simp

  from cong_trans[OF cong_sym[OF 1] 2] 
  show ?thesis
    using prime_gt_1_nat[OF prime p]
    by (auto dest: residue_one_dvd[unfolded One_nat_def] simp add: cong_def numeral_2_eq_2)
qed

theorem Carmichael_number_imp_squarefree:
  assumes "Carmichael_number n"
  shows "squarefree n"
proof(rule squarefreeI, rule ccontr)
  fix x :: nat
  assume "x2 dvd n"
  from assms have "n > 0" using Carmichael_number_gt_3[of n] by auto
  from x2 dvd n and 0 < n have "0 < x" by auto

  assume "¬ is_unit x"
  then obtain p where "prime p" "p dvd x"
    using prime_divisor_exists[of x] 0 < x
    by blast

  with x2 dvd n have "p^2 dvd n"
    by auto

  obtain l where n: "n = p ^ multiplicity p n * l"
    using multiplicity_dvd[of p n] by blast

  then have "¬ p dvd l"
    using multiplicity_decompose'[where x = n and p = p]
    using prime p 0 < n
    by (metis nat_dvd_1_iff_1 nat_mult_eq_cancel1 neq0_conv prime_prime_factor_sqrt zero_less_power)

  have "2  multiplicity p n"
    using p2 dvd n 0 < n prime_gt_1_nat[OF prime p]
    by (auto intro!: multiplicity_geI simp: power2_eq_square)
    
  then show False 
    using Carmichael_number_imp_squarefree_aux[OF Carmichael_number n n] prime p ¬ p dvd l
    by auto
qed

corollary Carmichael_not_primepow:
  assumes "Carmichael_number n"
  shows   "¬primepow n"
  using Carmichael_number_imp_squarefree[of n] Carmichael_number_not_prime[of n] assms
        primepow_gt_0_nat[of n] by (auto simp: not_squarefree_primepow)

lemma Carmichael_number_imp_squarefree_alt_weak:
  assumes "Carmichael_number n"
  shows "p l. (n = p * l)  prime p  ¬p dvd l"
proof -
  from assms have "n > 1"
    using Carmichael_number_gt_3[of n] by simp
  have "squarefree n"
    using Carmichael_number_imp_squarefree assms 
    by blast

  obtain p l where "p * l = n" "prime p" "1 < p"
    using assms prime_divisor_exists_strong_nat prime_gt_1_nat
    unfolding Carmichael_number_def by blast

  then have "multiplicity p n = 1"
    using 1 < n squarefree n and multiplicity_eq_zero_iff[of n p] squarefree_factorial_semiring''[of n]
    by auto

  then have "¬p dvd l"
    using 1 < n prime p p * l = n multiplicity_decompose'[of n p]
    by force

  show ?thesis 
    using p * l = n prime p ¬p dvd l
    by blast
qed

theorem Carmichael_number_odd:
  assumes "Carmichael_number n"
  shows   "odd n"
proof (rule ccontr)
  assume "¬ odd n"
  hence "even n" by simp
  from assms have "n  4" using Carmichael_number_gt_3[of n] by simp
  have "[(n - 1) ^ (n - 1) = n - 1] (mod n)"
    using even n and n  4 by (intro odd_pow_cong) auto

  then have "[(n - 1) ^ (n - 1)  1] (mod n)"
    using cong_trans[of 1 "(n - 1) ^ (n - 1)" n "n-1", OF cong_sym] 4  n
    by (auto simp: cong_def)

  moreover have "coprime (n - 1) n"
    using n  4 coprime_diff_one_left_nat[of n] by auto

  ultimately show False
    using assms unfolding Carmichael_number_def by blast
qed

lemma Carmichael_number_imp_squarefree_alt:
  assumes "Carmichael_number n"
  shows "p l. (n = p * l)  prime p  ¬p dvd l  2 < l"
proof -
  obtain p l where [simp]: "(n = p * l)" and "prime p" "¬p dvd l"
    using Carmichael_number_imp_squarefree_alt_weak and assms by blast

  moreover have "odd n" using Carmichael_number_odd and assms by blast

  consider "l = 0  l = 2" | "l = 1" | "2 < l"
    by fastforce

  then have "2 < l" 
  proof cases
    case 1
    then show ?thesis
      using odd n by auto
  next
    case 2
    then show ?thesis
      using n = p * l prime p Carmichael_number n
      unfolding Carmichael_number_def by simp
  qed simp

  ultimately show ?thesis by blast
qed

lemma Carmichael_number_imp_dvd:
  fixes n :: nat
  assumes Carmichael_number: "Carmichael_number n" and "prime p" "p dvd n"
  shows "p - 1 dvd n - 1"
proof -
  have "¬prime n" using Carmichael_number unfolding Carmichael_number_def by blast
  obtain u where "n = p * u" using p dvd n by blast
  have "squarefree n" using Carmichael_number_imp_squarefree assms by blast
  then have "¬p dvd u"
    using prime p not_prime_unit[of p]
    unfolding power2_eq_square squarefree_def n = p * u
    by fastforce

  define R where "R = Residues_nat p"
  interpret residues_nat_prime p R
    by unfold_locales (simp_all only: prime p R_def)
  
  obtain a where a: "a  {0<..<p}" "units.ord a = p - 1"
    using residues_prime_cyclic' prime p by metis
  from a have "a  totatives p" by (auto simp: totatives_prime prime p)

  have "coprime p u"
    using prime p ¬ p dvd u
    by (simp add: prime_imp_coprime_nat) 

  then obtain x where "[x = a] (mod p)" "[x = 1] (mod u)" 
    using binary_chinese_remainder_nat[of p u a 1] by blast

  have "coprime x p"
    using a  totatives p and cong_imp_coprime[OF cong_sym[OF [x = a] (mod p)]]
    by (simp add: coprime_commute totatives_def)
  moreover have "coprime x u" 
    using coprime_1_left and cong_imp_coprime[OF cong_sym[OF [x = 1] (mod u)]] by blast
  ultimately have "coprime x n"
    by (simp add: n = p * u)

  have "[a ^ (n - 1) = x ^ (n - 1)] (mod p)"
    using [x = a] (mod p) by (intro cong_pow) (auto simp: cong_sym_eq)
  also have "[x ^ (n - 1) = 1] (mod n)"
    using Carmichael_number coprime x n unfolding Carmichael_number_def by blast
  then have "[x ^ (n - 1) = 1] (mod p)"
    using n = p * u cong_modulus_mult_nat by blast 
  finally have "ord p a dvd n - 1"
    by (simp add: ord_divides [symmetric])
  also have "ord p a = p - 1"
    using a a  totatives p by (simp add: units.ord_residue_mult_group)
  finally show ?thesis .    
qed

text ‹
  The following lemma is also called Korselt's criterion.
›
lemma Carmichael_numberI:
  fixes n :: nat
  assumes "¬ prime n" "squarefree n" "1 < n" and
          DIV: "p. p  prime_factors n  p - 1 dvd n - 1"
  shows   "Carmichael_number n"
  unfolding Carmichael_number_def
proof (intro assms conjI allI impI)
  fix a :: nat assume "coprime a n"

  have n: "n = (prime_factors n)"
    using prime_factorization_nat and squarefree_factorial_semiring'[of n] 1 < n squarefree n
    by fastforce

  have "x ∈# prime_factorization n  y ∈# prime_factorization n  x  y  coprime x y" for x y
    using in_prime_factors_imp_prime primes_coprime
    by blast

  moreover {
    fix p 
    assume p: "p ∈# prime_factorization n"

    have "¬p dvd a"
      using coprime a n p coprime_common_divisor_nat[of a n p]
      by (auto simp: in_prime_factors_iff)
    with p have "[a ^ (p - 1) = 1] (mod p)"
      by (intro fermat_theorem) auto
    hence "ord p a dvd p - 1"
      by (subst (asm) ord_divides)
    also from p have "p - 1 dvd n - 1"
      by (rule DIV)
    finally have "[a ^ (n - 1) = 1] (mod p)"
      by (subst ord_divides)
  }

  ultimately show "[a ^ (n - 1) = 1] (mod n)"
    using n coprime_cong_prod_nat by metis
qed

theorem Carmichael_number_iff:
  "Carmichael_number n 
     n  1  ¬prime n  squarefree n  (pprime_factors n. p - 1 dvd n - 1)"
proof -
  consider "n = 0" | "n = 1" | "n > 1" by force
  thus ?thesis using Carmichael_numberI[of n] Carmichael_number_imp_dvd[of n]
    by cases (auto simp: Carmichael_number_not_prime Carmichael_number_imp_squarefree)
qed

text ‹
  Every Carmichael number has at least three distinct prime factors.
›
theorem Carmichael_number_card_prime_factors:
  assumes "Carmichael_number n"
  shows   "card (prime_factors n)  3"
proof (rule ccontr)
  from assms have "n > 3"
    using Carmichael_number_gt_3[of n] by simp
  assume "¬(card (prime_factors n)  3)"
  moreover have "card (prime_factors n)  0"
    using assms Carmichael_number_gt_3[of n] by (auto simp: prime_factorization_empty_iff)
  moreover have "card (prime_factors n)  1"
    using assms by (auto simp: one_prime_factor_iff_primepow Carmichael_not_primepow)
  ultimately have "card (prime_factors n) = 2"
    by linarith
  then obtain p q where pq: "prime_factors n = {p, q}" "p  q"
    by (auto simp: card_Suc_eq numeral_2_eq_2)
  hence "prime p" "prime q" by (auto simp: in_prime_factors_iff)

  have "n = (prime_factors n)"
    using assms by (subst squarefree_imp_prod_prime_factors_eq)
                   (auto simp: Carmichael_number_imp_squarefree)
  with pq have n_eq: "n = p * q" by simp

  have "p - 1 dvd n - 1" and "q - 1 dvd n - 1" using assms pq
    unfolding Carmichael_number_iff by blast+
  with prime p prime q n = p * q p  q show False
  proof (induction p q rule: linorder_wlog)
    case (le p q)
    hence "p < q" by auto
    have "[q = 1] (mod q - 1)"
      using prime_gt_1_nat[of q] prime q by (simp add: cong_def le_mod_geq)
    hence "[p * q - 1 = p * 1 - 1] (mod q - 1)"
      using le prime_gt_1_nat[of p] by (intro cong_diff_nat cong_mult) auto
    hence "[p - 1 = n - 1] (mod q - 1)"
      by (simp add: n = p * q cong_sym_eq)
    also have "[n - 1 = 0] (mod q - 1)"
      using le by (simp add: cong_def)
    finally have "(p - 1) mod (q - 1) = 0"
      by (simp add: cong_def)
    also have "(p - 1) mod (q - 1) = p - 1"
      using prime_gt_1_nat[of p] prime p p < q by (intro mod_less) auto
    finally show False
      using prime_gt_1_nat[of p] prime p by simp
  qed (simp_all add: mult.commute)
qed

lemma Carmichael_number_iff':
  fixes n :: nat
  defines "P  prime_factorization n"
  shows "Carmichael_number n 
           n > 1  size P  1  (p∈#P. count P p = 1  p - 1 dvd n - 1)"
  unfolding Carmichael_number_iff
  by (cases "n = 0") (auto simp: P_def squarefree_factorial_semiring' count_prime_factorization)

text ‹
  The smallest Carmichael number is 561, and it was found and proven so by
  Carmichael in 1910~cite"carmichael1910note".
›
lemma Carmichael_number_561: "Carmichael_number 561" (is "Carmichael_number ?n")
proof -
  have [simp]: "prime_factorization (561 :: nat) = {#3, 11, 17#}"
    by (rule prime_factorization_eqI) auto
  show ?thesis by (subst Carmichael_number_iff') auto
qed

end