# Theory Cong

theory Cong
imports Primes
```(*  Title:      HOL/Number_Theory/Cong.thy
Author:     Christophe Tabacznyj
Author:     Lawrence C. Paulson
Author:     Amine Chaieb
Author:     Thomas M. Rasmussen

Defines congruence (notation: [x = y] (mod z)) for natural numbers and
integers.

This file combines and revises a number of prior developments.

The original theories "GCD" and "Primes" were by Christophe Tabacznyj
and Lawrence C. Paulson, based on @{cite davenport92}. They introduced
gcd, lcm, and prime for the natural numbers.

The original theory "IntPrimes" was by Thomas M. Rasmussen, and
extended gcd, lcm, primes to the integers. Amine Chaieb provided
another extension of the notions to the integers, and added a number
of results to "Primes" and "GCD".

The original theory, "IntPrimes", by Thomas M. Rasmussen, defined and
developed the congruence relations on the integers. The notion was
extended to the natural numbers by Chaieb. Jeremy Avigad combined
these, revised and tidied them, made the development uniform for the
natural numbers and the integers, and added a number of new theorems.
*)

section ‹Congruence›

theory Cong
imports "HOL-Computational_Algebra.Primes"
begin

subsection ‹Turn off ‹One_nat_def››

lemma power_eq_one_eq_nat [simp]: "x^m = 1 ⟷ m = 0 ∨ x = 1"
for x m :: nat
by (induct m) auto

declare mod_pos_pos_trivial [simp]

subsection ‹Main definitions›

class cong =
fixes cong :: "'a ⇒ 'a ⇒ 'a ⇒ bool"  ("(1[_ = _] '(()mod _'))")
begin

abbreviation notcong :: "'a ⇒ 'a ⇒ 'a ⇒ bool"  ("(1[_ ≠ _] '(()mod _'))")
where "notcong x y m ≡ ¬ cong x y m"

end

subsubsection ‹Definitions for the natural numbers›

instantiation nat :: cong
begin

definition cong_nat :: "nat ⇒ nat ⇒ nat ⇒ bool"
where "cong_nat x y m ⟷ x mod m = y mod m"

instance ..

end

subsubsection ‹Definitions for the integers›

instantiation int :: cong
begin

definition cong_int :: "int ⇒ int ⇒ int ⇒ bool"
where "cong_int x y m ⟷ x mod m = y mod m"

instance ..

end

subsection ‹Set up Transfer›

lemma transfer_nat_int_cong:
"x ≥ 0 ⟹ y ≥ 0 ⟹ m ≥ 0 ⟹ [nat x = nat y] (mod (nat m)) ⟷ [x = y] (mod m)"
for x y m :: int
unfolding cong_int_def cong_nat_def
by (metis Divides.transfer_int_nat_functions(2) nat_0_le nat_mod_distrib)

declare transfer_morphism_nat_int [transfer add return: transfer_nat_int_cong]

lemma transfer_int_nat_cong: "[int x = int y] (mod (int m)) = [x = y] (mod m)"

declare transfer_morphism_int_nat [transfer add return: transfer_int_nat_cong]

subsection ‹Congruence›

(* was zcong_0, etc. *)
lemma cong_0_nat [simp, presburger]: "[a = b] (mod 0) ⟷ a = b"
for a b :: nat
by (auto simp: cong_nat_def)

lemma cong_0_int [simp, presburger]: "[a = b] (mod 0) ⟷ a = b"
for a b :: int
by (auto simp: cong_int_def)

lemma cong_1_nat [simp, presburger]: "[a = b] (mod 1)"
for a b :: nat
by (auto simp: cong_nat_def)

lemma cong_Suc_0_nat [simp, presburger]: "[a = b] (mod Suc 0)"
for a b :: nat
by (auto simp: cong_nat_def)

lemma cong_1_int [simp, presburger]: "[a = b] (mod 1)"
for a b :: int
by (auto simp: cong_int_def)

lemma cong_refl_nat [simp]: "[k = k] (mod m)"
for k :: nat
by (auto simp: cong_nat_def)

lemma cong_refl_int [simp]: "[k = k] (mod m)"
for k :: int
by (auto simp: cong_int_def)

lemma cong_sym_nat: "[a = b] (mod m) ⟹ [b = a] (mod m)"
for a b :: nat
by (auto simp: cong_nat_def)

lemma cong_sym_int: "[a = b] (mod m) ⟹ [b = a] (mod m)"
for a b :: int
by (auto simp: cong_int_def)

lemma cong_sym_eq_nat: "[a = b] (mod m) = [b = a] (mod m)"
for a b :: nat
by (auto simp: cong_nat_def)

lemma cong_sym_eq_int: "[a = b] (mod m) = [b = a] (mod m)"
for a b :: int
by (auto simp: cong_int_def)

lemma cong_trans_nat [trans]: "[a = b] (mod m) ⟹ [b = c] (mod m) ⟹ [a = c] (mod m)"
for a b c :: nat
by (auto simp: cong_nat_def)

lemma cong_trans_int [trans]: "[a = b] (mod m) ⟹ [b = c] (mod m) ⟹ [a = c] (mod m)"
for a b c :: int
by (auto simp: cong_int_def)

lemma cong_add_nat: "[a = b] (mod m) ⟹ [c = d] (mod m) ⟹ [a + c = b + d] (mod m)"
for a b c :: nat

lemma cong_add_int: "[a = b] (mod m) ⟹ [c = d] (mod m) ⟹ [a + c = b + d] (mod m)"
for a b c :: int

lemma cong_diff_int: "[a = b] (mod m) ⟹ [c = d] (mod m) ⟹ [a - c = b - d] (mod m)"
for a b c :: int
unfolding cong_int_def by (metis mod_diff_cong)

lemma cong_diff_aux_int:
"[a = b] (mod m) ⟹ [c = d] (mod m) ⟹
a ≥ c ⟹ b ≥ d ⟹ [tsub a c = tsub b d] (mod m)"
for a b c d :: int
by (metis cong_diff_int tsub_eq)

lemma cong_diff_nat:
fixes a b c d :: nat
assumes "[a = b] (mod m)" "[c = d] (mod m)" "a ≥ c" "b ≥ d"
shows "[a - c = b - d] (mod m)"
using assms by (rule cong_diff_aux_int [transferred])

lemma cong_mult_nat: "[a = b] (mod m) ⟹ [c = d] (mod m) ⟹ [a * c = b * d] (mod m)"
for a b c d :: nat
unfolding cong_nat_def  by (metis mod_mult_cong)

lemma cong_mult_int: "[a = b] (mod m) ⟹ [c = d] (mod m) ⟹ [a * c = b * d] (mod m)"
for a b c d :: int
unfolding cong_int_def  by (metis mod_mult_cong)

lemma cong_exp_nat: "[x = y] (mod n) ⟹ [x^k = y^k] (mod n)"
for x y :: nat
by (induct k) (auto simp: cong_mult_nat)

lemma cong_exp_int: "[x = y] (mod n) ⟹ [x^k = y^k] (mod n)"
for x y :: int
by (induct k) (auto simp: cong_mult_int)

lemma cong_sum_nat: "(⋀x. x ∈ A ⟹ [f x = g x] (mod m)) ⟹ [(∑x∈A. f x) = (∑x∈A. g x)] (mod m)"
for f g :: "'a ⇒ nat"
by (induct A rule: infinite_finite_induct) (auto intro: cong_add_nat)

lemma cong_sum_int: "(⋀x. x ∈ A ⟹ [f x = g x] (mod m)) ⟹ [(∑x∈A. f x) = (∑x∈A. g x)] (mod m)"
for f g :: "'a ⇒ int"
by (induct A rule: infinite_finite_induct) (auto intro: cong_add_int)

lemma cong_prod_nat: "(⋀x. x ∈ A ⟹ [f x = g x] (mod m)) ⟹ [(∏x∈A. f x) = (∏x∈A. g x)] (mod m)"
for f g :: "'a ⇒ nat"
by (induct A rule: infinite_finite_induct) (auto intro: cong_mult_nat)

lemma cong_prod_int: "(⋀x. x ∈ A ⟹ [f x = g x] (mod m)) ⟹ [(∏x∈A. f x) = (∏x∈A. g x)] (mod m)"
for f g :: "'a ⇒ int"
by (induct A rule: infinite_finite_induct) (auto intro: cong_mult_int)

lemma cong_scalar_nat: "[a = b] (mod m) ⟹ [a * k = b * k] (mod m)"
for a b k :: nat
by (rule cong_mult_nat) simp_all

lemma cong_scalar_int: "[a = b] (mod m) ⟹ [a * k = b * k] (mod m)"
for a b k :: int
by (rule cong_mult_int) simp_all

lemma cong_scalar2_nat: "[a = b] (mod m) ⟹ [k * a = k * b] (mod m)"
for a b k :: nat
by (rule cong_mult_nat) simp_all

lemma cong_scalar2_int: "[a = b] (mod m) ⟹ [k * a = k * b] (mod m)"
for a b k :: int
by (rule cong_mult_int) simp_all

lemma cong_mult_self_nat: "[a * m = 0] (mod m)"
for a m :: nat
by (auto simp: cong_nat_def)

lemma cong_mult_self_int: "[a * m = 0] (mod m)"
for a m :: int
by (auto simp: cong_int_def)

lemma cong_eq_diff_cong_0_int: "[a = b] (mod m) = [a - b = 0] (mod m)"
for a b :: int

lemma cong_eq_diff_cong_0_aux_int: "a ≥ b ⟹ [a = b] (mod m) = [tsub a b = 0] (mod m)"
for a b :: int
by (subst tsub_eq, assumption, rule cong_eq_diff_cong_0_int)

lemma cong_eq_diff_cong_0_nat:
fixes a b :: nat
assumes "a ≥ b"
shows "[a = b] (mod m) = [a - b = 0] (mod m)"
using assms by (rule cong_eq_diff_cong_0_aux_int [transferred])

lemma cong_diff_cong_0'_nat:
"[x = y] (mod n) ⟷ (if x ≤ y then [y - x = 0] (mod n) else [x - y = 0] (mod n))"
for x y :: nat
by (metis cong_eq_diff_cong_0_nat cong_sym_nat nat_le_linear)

lemma cong_altdef_nat: "a ≥ b ⟹ [a = b] (mod m) ⟷ m dvd (a - b)"
for a b :: nat
apply (subst cong_eq_diff_cong_0_nat, assumption)
apply (unfold cong_nat_def)
done

lemma cong_altdef_int: "[a = b] (mod m) ⟷ m dvd (a - b)"
for a b :: int
by (metis cong_int_def mod_eq_dvd_iff)

lemma cong_abs_int: "[x = y] (mod abs m) ⟷ [x = y] (mod m)"
for x y :: int

lemma cong_square_int:
"prime p ⟹ 0 < a ⟹ [a * a = 1] (mod p) ⟹ [a = 1] (mod p) ∨ [a = - 1] (mod p)"
for a :: int
apply (simp only: cong_altdef_int)
apply (subst prime_dvd_mult_eq_int [symmetric], assumption)
done

lemma cong_mult_rcancel_int: "coprime k m ⟹ [a * k = b * k] (mod m) = [a = b] (mod m)"
for a k m :: int
by (metis cong_altdef_int left_diff_distrib coprime_dvd_mult_iff gcd.commute)

lemma cong_mult_rcancel_nat: "coprime k m ⟹ [a * k = b * k] (mod m) = [a = b] (mod m)"
for a k m :: nat
by (metis cong_mult_rcancel_int [transferred])

lemma cong_mult_lcancel_nat: "coprime k m ⟹ [k * a = k * b ] (mod m) = [a = b] (mod m)"
for a k m :: nat

lemma cong_mult_lcancel_int: "coprime k m ⟹ [k * a = k * b] (mod m) = [a = b] (mod m)"
for a k m :: int

(* was zcong_zgcd_zmult_zmod *)
lemma coprime_cong_mult_int:
"[a = b] (mod m) ⟹ [a = b] (mod n) ⟹ coprime m n ⟹ [a = b] (mod m * n)"
for a b :: int
by (metis divides_mult cong_altdef_int)

lemma coprime_cong_mult_nat:
"[a = b] (mod m) ⟹ [a = b] (mod n) ⟹ coprime m n ⟹ [a = b] (mod m * n)"
for a b :: nat
by (metis coprime_cong_mult_int [transferred])

lemma cong_less_imp_eq_nat: "0 ≤ a ⟹ a < m ⟹ 0 ≤ b ⟹ b < m ⟹ [a = b] (mod m) ⟹ a = b"
for a b :: nat

lemma cong_less_imp_eq_int: "0 ≤ a ⟹ a < m ⟹ 0 ≤ b ⟹ b < m ⟹ [a = b] (mod m) ⟹ a = b"
for a b :: int

lemma cong_less_unique_nat: "0 < m ⟹ (∃!b. 0 ≤ b ∧ b < m ∧ [a = b] (mod m))"
for a m :: nat
by (auto simp: cong_nat_def) (metis mod_less_divisor mod_mod_trivial)

lemma cong_less_unique_int: "0 < m ⟹ (∃!b. 0 ≤ b ∧ b < m ∧ [a = b] (mod m))"
for a m :: int
by (auto simp: cong_int_def)  (metis mod_mod_trivial pos_mod_conj)

lemma cong_iff_lin_int: "[a = b] (mod m) ⟷ (∃k. b = a + m * k)"
for a b :: int
apply (auto simp add: cong_altdef_int dvd_def)
apply (rule_tac [!] x = "-k" in exI, auto)
done

lemma cong_iff_lin_nat: "([a = b] (mod m)) ⟷ (∃k1 k2. b + k1 * m = a + k2 * m)"
(is "?lhs = ?rhs")
for a b :: nat
proof
assume ?lhs
show ?rhs
proof (cases "b ≤ a")
case True
with ‹?lhs› show ?rhs
next
case False
with ‹?lhs› show ?rhs
apply (subst (asm) cong_sym_eq_nat)
apply (auto simp: cong_altdef_nat)
done
qed
next
assume ?rhs
then show ?lhs
by (metis cong_nat_def mod_mult_self2 mult.commute)
qed

lemma cong_gcd_eq_int: "[a = b] (mod m) ⟹ gcd a m = gcd b m"
for a b :: int
by (metis cong_int_def gcd_red_int)

lemma cong_gcd_eq_nat: "[a = b] (mod m) ⟹ gcd a m = gcd b m"
for a b :: nat
by (metis cong_gcd_eq_int [transferred])

lemma cong_imp_coprime_nat: "[a = b] (mod m) ⟹ coprime a m ⟹ coprime b m"
for a b :: nat

lemma cong_imp_coprime_int: "[a = b] (mod m) ⟹ coprime a m ⟹ coprime b m"
for a b :: int

lemma cong_cong_mod_nat: "[a = b] (mod m) ⟷ [a mod m = b mod m] (mod m)"
for a b :: nat

lemma cong_cong_mod_int: "[a = b] (mod m) ⟷ [a mod m = b mod m] (mod m)"
for a b :: int

lemma cong_minus_int [iff]: "[a = b] (mod - m) ⟷ [a = b] (mod m)"
for a b :: int
by (metis cong_iff_lin_int minus_equation_iff mult_minus_left mult_minus_right)

(*
lemma mod_dvd_mod_int:
"0 < (m::int) ⟹ m dvd b ⟹ (a mod b mod m) = (a mod m)"
apply (unfold dvd_def, auto)
apply (rule mod_mod_cancel)
apply auto
done

lemma mod_dvd_mod:
assumes "0 < (m::nat)" and "m dvd b"
shows "(a mod b mod m) = (a mod m)"

apply (rule mod_dvd_mod_int [transferred])
using assms apply auto
done
*)

lemma cong_add_lcancel_nat: "[a + x = a + y] (mod n) ⟷ [x = y] (mod n)"
for a x y :: nat

lemma cong_add_lcancel_int: "[a + x = a + y] (mod n) ⟷ [x = y] (mod n)"
for a x y :: int

lemma cong_add_rcancel_nat: "[x + a = y + a] (mod n) ⟷ [x = y] (mod n)"
for a x y :: nat

lemma cong_add_rcancel_int: "[x + a = y + a] (mod n) ⟷ [x = y] (mod n)"
for a x y :: int

lemma cong_add_lcancel_0_nat: "[a + x = a] (mod n) ⟷ [x = 0] (mod n)"
for a x :: nat

lemma cong_add_lcancel_0_int: "[a + x = a] (mod n) ⟷ [x = 0] (mod n)"
for a x :: int

lemma cong_add_rcancel_0_nat: "[x + a = a] (mod n) ⟷ [x = 0] (mod n)"
for a x :: nat

lemma cong_add_rcancel_0_int: "[x + a = a] (mod n) ⟷ [x = 0] (mod n)"
for a x :: int

lemma cong_dvd_modulus_nat: "[x = y] (mod m) ⟹ n dvd m ⟹ [x = y] (mod n)"
for x y :: nat
apply (auto simp add: cong_iff_lin_nat dvd_def)
apply (rule_tac x= "k1 * k" in exI)
apply (rule_tac x= "k2 * k" in exI)
done

lemma cong_dvd_modulus_int: "[x = y] (mod m) ⟹ n dvd m ⟹ [x = y] (mod n)"
for x y :: int
by (auto simp add: cong_altdef_int dvd_def)

lemma cong_dvd_eq_nat: "[x = y] (mod n) ⟹ n dvd x ⟷ n dvd y"
for x y :: nat
by (auto simp: cong_nat_def dvd_eq_mod_eq_0)

lemma cong_dvd_eq_int: "[x = y] (mod n) ⟹ n dvd x ⟷ n dvd y"
for x y :: int
by (auto simp: cong_int_def dvd_eq_mod_eq_0)

lemma cong_mod_nat: "n ≠ 0 ⟹ [a mod n = a] (mod n)"
for a n :: nat

lemma cong_mod_int: "n ≠ 0 ⟹ [a mod n = a] (mod n)"
for a n :: int

lemma mod_mult_cong_nat: "a ≠ 0 ⟹ b ≠ 0 ⟹ [x mod (a * b) = y] (mod a) ⟷ [x = y] (mod a)"
for a b :: nat

lemma neg_cong_int: "[a = b] (mod m) ⟷ [- a = - b] (mod m)"
for a b :: int
by (metis cong_int_def minus_minus mod_minus_cong)

lemma cong_modulus_neg_int: "[a = b] (mod m) ⟷ [a = b] (mod - m)"
for a b :: int

lemma mod_mult_cong_int: "a ≠ 0 ⟹ b ≠ 0 ⟹ [x mod (a * b) = y] (mod a) ⟷ [x = y] (mod a)"
for a b :: int
proof (cases "b > 0")
case True
then show ?thesis
next
case False
then show ?thesis
apply (subst (1 2) cong_modulus_neg_int)
apply (unfold cong_int_def)
apply (subgoal_tac "a * b = (- a * - b)")
apply (erule ssubst)
apply (subst zmod_zmult2_eq)
apply (metis mod_diff_left_eq mod_diff_right_eq mod_mult_self1_is_0 diff_zero)+
done
qed

lemma cong_to_1_nat:
fixes a :: nat
assumes "[a = 1] (mod n)"
shows "n dvd (a - 1)"
proof (cases "a = 0")
case True
then show ?thesis by force
next
case False
with assms show ?thesis by (metis cong_altdef_nat leI less_one)
qed

lemma cong_0_1_nat': "[0 = Suc 0] (mod n) ⟷ n = Suc 0"
by (auto simp: cong_nat_def)

lemma cong_0_1_nat: "[0 = 1] (mod n) ⟷ n = 1"
for n :: nat
by (auto simp: cong_nat_def)

lemma cong_0_1_int: "[0 = 1] (mod n) ⟷ n = 1 ∨ n = - 1"
for n :: int
by (auto simp: cong_int_def zmult_eq_1_iff)

lemma cong_to_1'_nat: "[a = 1] (mod n) ⟷ a = 0 ∧ n = 1 ∨ (∃m. a = 1 + m * n)"
for a :: nat
by (metis add.right_neutral cong_0_1_nat cong_iff_lin_nat cong_to_1_nat

lemma cong_le_nat: "y ≤ x ⟹ [x = y] (mod n) ⟷ (∃q. x = q * n + y)"
for x y :: nat
by (metis cong_altdef_nat Nat.le_imp_diff_is_add dvd_def mult.commute)

lemma cong_solve_nat:
fixes a :: nat
assumes "a ≠ 0"
shows "∃x. [a * x = gcd a n] (mod n)"
proof (cases "n = 0")
case True
then show ?thesis by force
next
case False
then show ?thesis
using bezout_nat [of a n, OF ‹a ≠ 0›]
by auto (metis cong_add_rcancel_0_nat cong_mult_self_nat mult.commute)
qed

lemma cong_solve_int: "a ≠ 0 ⟹ ∃x. [a * x = gcd a n] (mod n)"
for a :: int
apply (cases "n = 0")
apply (cases "a ≥ 0")
apply auto
apply (rule_tac x = "-1" in exI)
apply auto
apply (insert bezout_int [of a n], auto)
apply (metis cong_iff_lin_int mult.commute)
done

lemma cong_solve_dvd_nat:
fixes a :: nat
assumes a: "a ≠ 0" and b: "gcd a n dvd d"
shows "∃x. [a * x = d] (mod n)"
proof -
from cong_solve_nat [OF a] obtain x where "[a * x = gcd a n](mod n)"
by auto
then have "[(d div gcd a n) * (a * x) = (d div gcd a n) * gcd a n] (mod n)"
by (elim cong_scalar2_nat)
also from b have "(d div gcd a n) * gcd a n = d"
by (rule dvd_div_mult_self)
also have "(d div gcd a n) * (a * x) = a * (d div gcd a n * x)"
by auto
finally show ?thesis
by auto
qed

lemma cong_solve_dvd_int:
assumes a: "(a::int) ≠ 0" and b: "gcd a n dvd d"
shows "∃x. [a * x = d] (mod n)"
proof -
from cong_solve_int [OF a] obtain x where "[a * x = gcd a n](mod n)"
by auto
then have "[(d div gcd a n) * (a * x) = (d div gcd a n) * gcd a n] (mod n)"
by (elim cong_scalar2_int)
also from b have "(d div gcd a n) * gcd a n = d"
by (rule dvd_div_mult_self)
also have "(d div gcd a n) * (a * x) = a * (d div gcd a n * x)"
by auto
finally show ?thesis
by auto
qed

lemma cong_solve_coprime_nat:
fixes a :: nat
assumes "coprime a n"
shows "∃x. [a * x = 1] (mod n)"
proof (cases "a = 0")
case True
with assms show ?thesis by force
next
case False
with assms show ?thesis by (metis cong_solve_nat)
qed

lemma cong_solve_coprime_int: "coprime (a::int) n ⟹ ∃x. [a * x = 1] (mod n)"
apply (cases "a = 0")
apply auto
apply (cases "n ≥ 0")
apply auto
apply (metis cong_solve_int)
done

lemma coprime_iff_invertible_nat:
"m > 0 ⟹ coprime a m = (∃x. [a * x = Suc 0] (mod m))"
by (metis One_nat_def cong_gcd_eq_nat cong_solve_coprime_nat coprime_lmult gcd.commute gcd_Suc_0)

lemma coprime_iff_invertible_int: "m > 0 ⟹ coprime a m ⟷ (∃x. [a * x = 1] (mod m))"
for m :: int
apply (auto intro: cong_solve_coprime_int)
apply (metis cong_int_def coprime_mul_eq gcd_1_int gcd.commute gcd_red_int)
done

lemma coprime_iff_invertible'_nat:
"m > 0 ⟹ coprime a m ⟷ (∃x. 0 ≤ x ∧ x < m ∧ [a * x = Suc 0] (mod m))"
apply (subst coprime_iff_invertible_nat)
apply auto
apply (metis mod_less_divisor mod_mult_right_eq)
done

lemma coprime_iff_invertible'_int:
"m > 0 ⟹ coprime a m ⟷ (∃x. 0 ≤ x ∧ x < m ∧ [a * x = 1] (mod m))"
for m :: int
apply (subst coprime_iff_invertible_int)
apply (metis mod_mult_right_eq pos_mod_conj)
done

lemma cong_cong_lcm_nat: "[x = y] (mod a) ⟹ [x = y] (mod b) ⟹ [x = y] (mod lcm a b)"
for x y :: nat
apply (cases "y ≤ x")
apply (metis cong_altdef_nat lcm_least)
apply (meson cong_altdef_nat cong_sym_nat lcm_least_iff nat_le_linear)
done

lemma cong_cong_lcm_int: "[x = y] (mod a) ⟹ [x = y] (mod b) ⟹ [x = y] (mod lcm a b)"
for x y :: int
by (auto simp add: cong_altdef_int lcm_least)

lemma cong_cong_prod_coprime_nat [rule_format]: "finite A ⟹
(∀i∈A. (∀j∈A. i ≠ j ⟶ coprime (m i) (m j))) ⟶
(∀i∈A. [(x::nat) = y] (mod m i)) ⟶
[x = y] (mod (∏i∈A. m i))"
apply (induct set: finite)
apply auto
apply (metis One_nat_def coprime_cong_mult_nat gcd.commute prod_coprime)
done

lemma cong_cong_prod_coprime_int [rule_format]: "finite A ⟹
(∀i∈A. (∀j∈A. i ≠ j ⟶ coprime (m i) (m j))) ⟶
(∀i∈A. [(x::int) = y] (mod m i)) ⟶
[x = y] (mod (∏i∈A. m i))"
apply (induct set: finite)
apply auto
apply (metis coprime_cong_mult_int gcd.commute prod_coprime)
done

lemma binary_chinese_remainder_aux_nat:
fixes m1 m2 :: nat
assumes a: "coprime m1 m2"
shows "∃b1 b2. [b1 = 1] (mod m1) ∧ [b1 = 0] (mod m2) ∧ [b2 = 0] (mod m1) ∧ [b2 = 1] (mod m2)"
proof -
from cong_solve_coprime_nat [OF a] obtain x1 where 1: "[m1 * x1 = 1] (mod m2)"
by auto
from a have b: "coprime m2 m1"
by (subst gcd.commute)
from cong_solve_coprime_nat [OF b] obtain x2 where 2: "[m2 * x2 = 1] (mod m1)"
by auto
have "[m1 * x1 = 0] (mod m1)"
by (subst mult.commute) (rule cong_mult_self_nat)
moreover have "[m2 * x2 = 0] (mod m2)"
by (subst mult.commute) (rule cong_mult_self_nat)
ultimately show ?thesis
using 1 2 by blast
qed

lemma binary_chinese_remainder_aux_int:
fixes m1 m2 :: int
assumes a: "coprime m1 m2"
shows "∃b1 b2. [b1 = 1] (mod m1) ∧ [b1 = 0] (mod m2) ∧ [b2 = 0] (mod m1) ∧ [b2 = 1] (mod m2)"
proof -
from cong_solve_coprime_int [OF a] obtain x1 where 1: "[m1 * x1 = 1] (mod m2)"
by auto
from a have b: "coprime m2 m1"
by (subst gcd.commute)
from cong_solve_coprime_int [OF b] obtain x2 where 2: "[m2 * x2 = 1] (mod m1)"
by auto
have "[m1 * x1 = 0] (mod m1)"
by (subst mult.commute) (rule cong_mult_self_int)
moreover have "[m2 * x2 = 0] (mod m2)"
by (subst mult.commute) (rule cong_mult_self_int)
ultimately show ?thesis
using 1 2 by blast
qed

lemma binary_chinese_remainder_nat:
fixes m1 m2 :: nat
assumes a: "coprime m1 m2"
shows "∃x. [x = u1] (mod m1) ∧ [x = u2] (mod m2)"
proof -
from binary_chinese_remainder_aux_nat [OF a] obtain b1 b2
where "[b1 = 1] (mod m1)" and "[b1 = 0] (mod m2)"
and "[b2 = 0] (mod m1)" and "[b2 = 1] (mod m2)"
by blast
let ?x = "u1 * b1 + u2 * b2"
have "[?x = u1 * 1 + u2 * 0] (mod m1)"
apply (rule cong_scalar2_nat)
apply (rule ‹[b1 = 1] (mod m1)›)
apply (rule cong_scalar2_nat)
apply (rule ‹[b2 = 0] (mod m1)›)
done
then have "[?x = u1] (mod m1)" by simp
have "[?x = u1 * 0 + u2 * 1] (mod m2)"
apply (rule cong_scalar2_nat)
apply (rule ‹[b1 = 0] (mod m2)›)
apply (rule cong_scalar2_nat)
apply (rule ‹[b2 = 1] (mod m2)›)
done
then have "[?x = u2] (mod m2)"
by simp
with ‹[?x = u1] (mod m1)› show ?thesis
by blast
qed

lemma binary_chinese_remainder_int:
fixes m1 m2 :: int
assumes a: "coprime m1 m2"
shows "∃x. [x = u1] (mod m1) ∧ [x = u2] (mod m2)"
proof -
from binary_chinese_remainder_aux_int [OF a] obtain b1 b2
where "[b1 = 1] (mod m1)" and "[b1 = 0] (mod m2)"
and "[b2 = 0] (mod m1)" and "[b2 = 1] (mod m2)"
by blast
let ?x = "u1 * b1 + u2 * b2"
have "[?x = u1 * 1 + u2 * 0] (mod m1)"
apply (rule cong_scalar2_int)
apply (rule ‹[b1 = 1] (mod m1)›)
apply (rule cong_scalar2_int)
apply (rule ‹[b2 = 0] (mod m1)›)
done
then have "[?x = u1] (mod m1)" by simp
have "[?x = u1 * 0 + u2 * 1] (mod m2)"
apply (rule cong_scalar2_int)
apply (rule ‹[b1 = 0] (mod m2)›)
apply (rule cong_scalar2_int)
apply (rule ‹[b2 = 1] (mod m2)›)
done
then have "[?x = u2] (mod m2)" by simp
with ‹[?x = u1] (mod m1)› show ?thesis
by blast
qed

lemma cong_modulus_mult_nat: "[x = y] (mod m * n) ⟹ [x = y] (mod m)"
for x y :: nat
apply (cases "y ≤ x")
apply (erule dvd_mult_left)
apply (rule cong_sym_nat)
apply (subst (asm) cong_sym_eq_nat)
apply (erule dvd_mult_left)
done

lemma cong_modulus_mult_int: "[x = y] (mod m * n) ⟹ [x = y] (mod m)"
for x y :: int
apply (erule dvd_mult_left)
done

lemma cong_less_modulus_unique_nat: "[x = y] (mod m) ⟹ x < m ⟹ y < m ⟹ x = y"
for x y :: nat

lemma binary_chinese_remainder_unique_nat:
fixes m1 m2 :: nat
assumes a: "coprime m1 m2"
and nz: "m1 ≠ 0" "m2 ≠ 0"
shows "∃!x. x < m1 * m2 ∧ [x = u1] (mod m1) ∧ [x = u2] (mod m2)"
proof -
from binary_chinese_remainder_nat [OF a] obtain y
where "[y = u1] (mod m1)" and "[y = u2] (mod m2)"
by blast
let ?x = "y mod (m1 * m2)"
from nz have less: "?x < m1 * m2"
by auto
have 1: "[?x = u1] (mod m1)"
apply (rule cong_trans_nat)
prefer 2
apply (rule ‹[y = u1] (mod m1)›)
apply (rule cong_modulus_mult_nat)
apply (rule cong_mod_nat)
using nz apply auto
done
have 2: "[?x = u2] (mod m2)"
apply (rule cong_trans_nat)
prefer 2
apply (rule ‹[y = u2] (mod m2)›)
apply (subst mult.commute)
apply (rule cong_modulus_mult_nat)
apply (rule cong_mod_nat)
using nz apply auto
done
have "∀z. z < m1 * m2 ∧ [z = u1] (mod m1) ∧ [z = u2] (mod m2) ⟶ z = ?x"
proof clarify
fix z
assume "z < m1 * m2"
assume "[z = u1] (mod m1)" and  "[z = u2] (mod m2)"
have "[?x = z] (mod m1)"
apply (rule cong_trans_nat)
apply (rule ‹[?x = u1] (mod m1)›)
apply (rule cong_sym_nat)
apply (rule ‹[z = u1] (mod m1)›)
done
moreover have "[?x = z] (mod m2)"
apply (rule cong_trans_nat)
apply (rule ‹[?x = u2] (mod m2)›)
apply (rule cong_sym_nat)
apply (rule ‹[z = u2] (mod m2)›)
done
ultimately have "[?x = z] (mod m1 * m2)"
by (auto intro: coprime_cong_mult_nat a)
with ‹z < m1 * m2› ‹?x < m1 * m2› show "z = ?x"
apply (intro cong_less_modulus_unique_nat)
apply (auto, erule cong_sym_nat)
done
qed
with less 1 2 show ?thesis by auto
qed

lemma chinese_remainder_aux_nat:
fixes A :: "'a set"
and m :: "'a ⇒ nat"
assumes fin: "finite A"
and cop: "∀i ∈ A. (∀j ∈ A. i ≠ j ⟶ coprime (m i) (m j))"
shows "∃b. (∀i ∈ A. [b i = 1] (mod m i) ∧ [b i = 0] (mod (∏j ∈ A - {i}. m j)))"
proof (rule finite_set_choice, rule fin, rule ballI)
fix i
assume "i ∈ A"
with cop have "coprime (∏j ∈ A - {i}. m j) (m i)"
by (intro prod_coprime) auto
then have "∃x. [(∏j ∈ A - {i}. m j) * x = 1] (mod m i)"
by (elim cong_solve_coprime_nat)
then obtain x where "[(∏j ∈ A - {i}. m j) * x = 1] (mod m i)"
by auto
moreover have "[(∏j ∈ A - {i}. m j) * x = 0] (mod (∏j ∈ A - {i}. m j))"
by (subst mult.commute, rule cong_mult_self_nat)
ultimately show "∃a. [a = 1] (mod m i) ∧ [a = 0] (mod prod m (A - {i}))"
by blast
qed

lemma chinese_remainder_nat:
fixes A :: "'a set"
and m :: "'a ⇒ nat"
and u :: "'a ⇒ nat"
assumes fin: "finite A"
and cop: "∀i ∈ A. ∀j ∈ A. i ≠ j ⟶ coprime (m i) (m j)"
shows "∃x. ∀i ∈ A. [x = u i] (mod m i)"
proof -
from chinese_remainder_aux_nat [OF fin cop]
obtain b where b: "∀i ∈ A. [b i = 1] (mod m i) ∧ [b i = 0] (mod (∏j ∈ A - {i}. m j))"
by blast
let ?x = "∑i∈A. (u i) * (b i)"
show ?thesis
proof (rule exI, clarify)
fix i
assume a: "i ∈ A"
show "[?x = u i] (mod m i)"
proof -
from fin a have "?x = (∑j ∈ {i}. u j * b j) + (∑j ∈ A - {i}. u j * b j)"
by (subst sum.union_disjoint [symmetric]) (auto intro: sum.cong)
then have "[?x = u i * b i + (∑j ∈ A - {i}. u j * b j)] (mod m i)"
by auto
also have "[u i * b i + (∑j ∈ A - {i}. u j * b j) =
u i * 1 + (∑j ∈ A - {i}. u j * 0)] (mod m i)"
apply (rule cong_scalar2_nat)
using b a apply blast
apply (rule cong_sum_nat)
apply (rule cong_scalar2_nat)
using b apply auto
apply (rule cong_dvd_modulus_nat)
apply (drule (1) bspec)
apply (erule conjE)
apply assumption
apply rule
using fin a apply auto
done
finally show ?thesis
by simp
qed
qed
qed

lemma coprime_cong_prod_nat [rule_format]: "finite A ⟹
(∀i∈A. (∀j∈A. i ≠ j ⟶ coprime (m i) (m j))) ⟶
(∀i∈A. [(x::nat) = y] (mod m i)) ⟶
[x = y] (mod (∏i∈A. m i))"
apply (induct set: finite)
apply auto
apply (metis One_nat_def coprime_cong_mult_nat gcd.commute prod_coprime)
done

lemma chinese_remainder_unique_nat:
fixes A :: "'a set"
and m :: "'a ⇒ nat"
and u :: "'a ⇒ nat"
assumes fin: "finite A"
and nz: "∀i∈A. m i ≠ 0"
and cop: "∀i∈A. ∀j∈A. i ≠ j ⟶ coprime (m i) (m j)"
shows "∃!x. x < (∏i∈A. m i) ∧ (∀i∈A. [x = u i] (mod m i))"
proof -
from chinese_remainder_nat [OF fin cop]
obtain y where one: "(∀i∈A. [y = u i] (mod m i))"
by blast
let ?x = "y mod (∏i∈A. m i)"
from fin nz have prodnz: "(∏i∈A. m i) ≠ 0"
by auto
then have less: "?x < (∏i∈A. m i)"
by auto
have cong: "∀i∈A. [?x = u i] (mod m i)"
apply auto
apply (rule cong_trans_nat)
prefer 2
using one apply auto
apply (rule cong_dvd_modulus_nat)
apply (rule cong_mod_nat)
using prodnz apply auto
apply rule
apply (rule fin)
apply assumption
done
have unique: "∀z. z < (∏i∈A. m i) ∧ (∀i∈A. [z = u i] (mod m i)) ⟶ z = ?x"
proof clarify
fix z
assume zless: "z < (∏i∈A. m i)"
assume zcong: "(∀i∈A. [z = u i] (mod m i))"
have "∀i∈A. [?x = z] (mod m i)"
apply clarify
apply (rule cong_trans_nat)
using cong apply (erule bspec)
apply (rule cong_sym_nat)
using zcong apply auto
done
with fin cop have "[?x = z] (mod (∏i∈A. m i))"
apply (intro coprime_cong_prod_nat)
apply auto
done
with zless less show "z = ?x"
apply (intro cong_less_modulus_unique_nat)
apply auto
apply (erule cong_sym_nat)
done
qed
from less cong unique show ?thesis
by blast
qed

end
```