(* ========================================================================= *)
(* Quadratic reciprocity. *)
(* ========================================================================= *)
needs "Library/prime.ml";;
needs "Library/pocklington.ml";;
prioritize_num();;
(* ------------------------------------------------------------------------- *)
(* Misc. lemmas. *)
(* ------------------------------------------------------------------------- *)
let EVEN_DIV = prove
(`!n.
EVEN n <=> n = 2 * (n DIV 2)`,
GEN_TAC THEN REWRITE_TAC[
EVEN_MOD] THEN
MP_TAC(SPEC `n:num` (MATCH_MP
DIVISION (ARITH_RULE `~(2 = 0)`))) THEN
ARITH_TAC);;
let CONG_COND_LEMMA = prove
(`!p x y. 3 <= p /\
((if x then 1 else p - 1) == (if y then 1 else p - 1)) (mod p)
==> (x <=> y)`,
(* ------------------------------------------------------------------------- *)
(* What it means to be a quadratic residue. I keep in the "mod p" as what *)
(* I think is a more intuitive notation. *)
(* *)
(* We might explicitly assume that the two numbers are coprime, ruling out *)
(* the degenerate case of 0 as a quadratic residue. But this seems simpler. *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("is_quadratic_residue",(12,"right"));;
(* ------------------------------------------------------------------------- *)
(* Alternative formulation for special cases. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Some lemmas about dual pairs; these would be more natural over Z. *)
(* ------------------------------------------------------------------------- *)
let IS_QUADRATIC_RESIDUE_PAIR = prove
(`!a p. prime p /\ coprime(a,p)
==> (a
is_quadratic_residue (mod p) <=>
?x y. 0 < x /\ x < p /\ 0 < y /\ y < p /\ x + y = p /\
(x
EXP 2 == a) (mod p) /\ (y
EXP 2 == a) (mod p) /\
!z. 0 < z /\ z < p /\ (z
EXP 2 == a) (mod p)
==> z = x \/ z = y)`,
SIMP_TAC[
IS_QUADRATIC_RESIDUE_COMMON] THEN REPEAT STRIP_TAC THEN
EQ_TAC THENL [ALL_TAC; MESON_TAC[]] THEN
DISCH_THEN(X_CHOOSE_THEN `x:num` STRIP_ASSUME_TAC) THEN
MAP_EVERY EXISTS_TAC [`x:num`; `p - x:num`] THEN
ASM_SIMP_TAC[ARITH_RULE
`0 < x /\ x < p ==> 0 < p - x /\ p - x < p /\ x + (p - x) = p`] THEN
REPEAT STRIP_TAC THEN
FIRST_X_ASSUM(MP_TAC o MATCH_MP
QUADRATIC_RESIDUE_PAIR) THENL
[DISCH_THEN(MP_TAC o SPECL [`x:num`; `p - x:num`]) THEN
ASM_SIMP_TAC[ARITH_RULE `x < p ==> x + (p - x) = p`;
DIVIDES_REFL] THEN
ASM_MESON_TAC[
CONG_TRANS;
CONG_SYM];
DISCH_THEN(MP_TAC o SPECL [`x:num`; `z:num`]) THEN
SUBGOAL_THEN `(x
EXP 2 == z
EXP 2) (mod p)` (fun th -> SIMP_TAC[th]) THENL
[ASM_MESON_TAC[
CONG_TRANS;
CONG_SYM]; ALL_TAC] THEN
DISCH_THEN(DISJ_CASES_THEN (MP_TAC o MATCH_MP
DIVIDES_CASES)) THEN
REWRITE_TAC[
ADD_EQ_0;
DIST_EQ_0] THEN REWRITE_TAC[dist] THEN
ASM_ARITH_TAC]);;
(* ------------------------------------------------------------------------- *)
(* Define the Legendre symbol. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Definition of iterated product. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Factorial in terms of products. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* General "pairing up" theorem for products. *)
(* ------------------------------------------------------------------------- *)
let NPRODUCT_PAIRUP_INDUCT = prove
(`!f r n s k. s
HAS_SIZE (2 * n) /\
(!x:A. x
IN s ==> ?!y. y
IN s /\ ~(y = x) /\
(f(x) * f(y) == k) (mod r))
==> (nproduct s f == k
EXP n) (mod r)`,
GEN_TAC THEN GEN_TAC THEN
MATCH_MP_TAC
num_WF THEN X_GEN_TAC `n:num` THEN DISCH_TAC THEN
X_GEN_TAC `s:A->bool` THEN GEN_TAC THEN ASM_CASES_TAC `n = 0` THENL
[ASM_SIMP_TAC[
MULT_CLAUSES;
HAS_SIZE_0;
NPRODUCT_CLAUSES;
EXP;
CONG_REFL];
ALL_TAC] THEN
ASM_CASES_TAC `s:A->bool = {}` THENL
[ASM_MESON_TAC[
HAS_SIZE_0; ARITH_RULE `2 * n = 0 <=> n = 0`;
HAS_SIZE];
ALL_TAC] THEN
STRIP_TAC THEN FIRST_X_ASSUM(X_CHOOSE_TAC `a:A` o
GEN_REWRITE_RULE I [GSYM
MEMBER_NOT_EMPTY]) THEN
FIRST_X_ASSUM(MP_TAC o SPEC `n - 1`) THEN
ASM_SIMP_TAC[ARITH_RULE `~(n = 0) ==> n - 1 < n`] THEN
FIRST_ASSUM(MP_TAC o SPEC `a:A`) THEN REWRITE_TAC[ASSUME `(a:A)
IN s`] THEN
REWRITE_TAC[
EXISTS_UNIQUE] THEN
DISCH_THEN(X_CHOOSE_THEN `b:A` STRIP_ASSUME_TAC) THEN
DISCH_THEN(MP_TAC o SPECL [`(s
DELETE a)
DELETE (b:A)`; `k:num`]) THEN
SUBGOAL_THEN `s = (a:A)
INSERT (b
INSERT (s
DELETE a
DELETE b))`
(ASSUME_TAC o SYM) THENL [ASM SET_TAC[]; ALL_TAC] THEN ANTS_TAC THENL
[CONJ_TAC THENL
[UNDISCH_TAC `(s:A->bool)
HAS_SIZE 2 * n` THEN
FIRST_X_ASSUM(fun th -> GEN_REWRITE_TAC (LAND_CONV o LAND_CONV)
[SYM th]) THEN
SIMP_TAC[
HAS_SIZE;
FINITE_INSERT;
CARD_CLAUSES;
FINITE_DELETE;
IMP_CONJ;
IN_DELETE;
IN_INSERT] THEN
ASM_REWRITE_TAC[] THEN ARITH_TAC;
ALL_TAC] THEN
X_GEN_TAC `x:A` THEN ASM_REWRITE_TAC[
IN_DELETE] THEN STRIP_TAC THEN
FIRST_ASSUM(MP_TAC o C MATCH_MP (ASSUME `(x:A)
IN s`)) THEN
REWRITE_TAC[
EXISTS_UNIQUE] THEN MATCH_MP_TAC
MONO_EXISTS THEN
X_GEN_TAC `y:A` THEN STRIP_TAC THEN CONJ_TAC THENL
[ALL_TAC; ASM_MESON_TAC[]] THEN
ASM_REWRITE_TAC[] THEN CONJ_TAC THEN DISCH_THEN SUBST_ALL_TAC THENL
[ASM_MESON_TAC[
MULT_SYM]; ALL_TAC] THEN
FIRST_X_ASSUM(MP_TAC o C MATCH_MP (ASSUME `(b:A)
IN s`)) THEN
REWRITE_TAC[
EXISTS_UNIQUE_THM] THEN
DISCH_THEN(MP_TAC o SPECL [`a:A`; `x:A`] o CONJUNCT2) THEN
ASM_MESON_TAC[
MULT_SYM];
ALL_TAC] THEN
DISCH_TAC THEN EXPAND_TAC "s" THEN
FIRST_X_ASSUM(MP_TAC o CONJUNCT1 o REWRITE_RULE[
HAS_SIZE]) THEN
SIMP_TAC[
NPRODUCT_CLAUSES;
FINITE_INSERT;
FINITE_DELETE] THEN
REWRITE_TAC[
IN_INSERT;
IN_DELETE;
MULT_CLAUSES] THEN
FIRST_ASSUM(SUBST1_TAC o MATCH_MP
(ARITH_RULE `~(n = 0) ==> n = SUC(n - 1)`)) THEN
ASM_REWRITE_TAC[
MULT_ASSOC;
EXP] THEN DISCH_TAC THEN
MATCH_MP_TAC
CONG_MULT THEN ASM_REWRITE_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* The two cases. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* We immediately get one part of Wilson's theorem. *)
(* ------------------------------------------------------------------------- *)
let WILSON = prove
(`!p. ~(p = 1) ==> (prime p <=> (
FACT(p - 1) == p - 1) (mod p))`,
X_GEN_TAC `n:num` THEN DISCH_TAC THEN EQ_TAC THEN SIMP_TAC[
WILSON_IMP] THEN
FIRST_X_ASSUM(MP_TAC o MATCH_MP
PRIME_FACTOR) THEN
DISCH_THEN(X_CHOOSE_THEN `p:num` STRIP_ASSUME_TAC) THEN
FIRST_ASSUM(MP_TAC o MATCH_MP
DIVIDES_LE) THEN
ASM_CASES_TAC `n = 0` THEN ASM_REWRITE_TAC[] THENL
[ASM_REWRITE_TAC[
CONG_MOD_0] THEN CONV_TAC NUM_REDUCE_CONV; ALL_TAC] THEN
REWRITE_TAC[
LE_LT] THEN ASM_CASES_TAC `n:num = p` THEN ASM_REWRITE_TAC[] THEN
DISCH_THEN(MP_TAC o MATCH_MP (ARITH_RULE `x < y ==> x <= y - 1`)) THEN
ASM_SIMP_TAC[GSYM
DIVIDES_FACT_PRIME] THEN
DISCH_THEN(fun th -> DISCH_TAC THEN MP_TAC th) THEN
SUBGOAL_THEN `p divides
FACT(n - 1) <=> p divides (n - 1)` SUBST1_TAC THENL
[MATCH_MP_TAC
CONG_DIVIDES THEN
MATCH_MP_TAC
CONG_MOD_MULT THEN EXISTS_TAC `n:num` THEN
ASM_REWRITE_TAC[];
ALL_TAC] THEN
DISCH_TAC THEN SUBGOAL_THEN `p divides 1` MP_TAC THENL
[MATCH_MP_TAC
DIVIDES_ADD_REVR THEN EXISTS_TAC `n - 1` THEN
ASM_SIMP_TAC[ARITH_RULE `~(n = 0) ==> n - 1 + 1 = n`];
REWRITE_TAC[
DIVIDES_ONE] THEN ASM_MESON_TAC[
PRIME_1]]);;
(* ------------------------------------------------------------------------- *)
(* Using Wilson's theorem we can get the Euler criterion. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Gauss's Lemma. *)
(* ------------------------------------------------------------------------- *)
let GAUSS_LEMMA_1 = prove
(`prime p /\ coprime(a,p) /\ 2 * r + 1 = p
==> nproduct(1..r) (\x. let b = (a * x) MOD p in
if b <= r then b else p - b) =
nproduct(1..r) (\x. x)`,
REPEAT STRIP_TAC THEN FIRST_ASSUM(ASSUME_TAC o MATCH_MP
PRIME_IMP_NZ) THEN
GEN_REWRITE_TAC (LAND_CONV o RAND_CONV) [SYM(CONJUNCT1(SPEC_ALL
I_O_ID))] THEN
REWRITE_TAC[
I_DEF] THEN MATCH_MP_TAC
NPRODUCT_INJECTION THEN
REWRITE_TAC[
FINITE_NUMSEG] THEN
ABBREV_TAC `f = \x. let b = (a * x) MOD p in
if b <= r then b else p - b` THEN
MATCH_MP_TAC(TAUT `a /\ (a ==> b) ==> a /\ b`) THEN CONJ_TAC THENL
[GEN_TAC THEN EXPAND_TAC "f" THEN REWRITE_TAC[
IN_NUMSEG] THEN
LET_TAC THEN REWRITE_TAC[
LET_DEF;
LET_END_DEF] THEN REPEAT STRIP_TAC THENL
[ALL_TAC; EXPAND_TAC "p" THEN ARITH_TAC] THEN
REWRITE_TAC[ARITH_RULE `1 <= x <=> ~(x = 0)`] THEN COND_CASES_TAC THENL
[ALL_TAC; ASM_MESON_TAC[
DIVISION;
NOT_LE;
SUB_EQ_0;
PRIME_0]] THEN
EXPAND_TAC "b" THEN ASM_SIMP_TAC[GSYM
DIVIDES_MOD;
PRIME_IMP_NZ] THEN
ASM_SIMP_TAC[
PRIME_DIVPROD_EQ] THEN STRIP_TAC THENL
[ASM_MESON_TAC[coprime;
DIVIDES_REFL;
PRIME_1];
ASM_MESON_TAC[
DIVIDES_LE; ARITH_RULE `~(1 <= 0)`;
ARITH_RULE `~(2 * r + 1 <= i /\ i <= r)`]];
REWRITE_TAC[
LET_DEF;
LET_END_DEF] THEN DISCH_TAC] THEN
MAP_EVERY X_GEN_TAC [`i:num`; `j:num`] THEN REWRITE_TAC[
IN_NUMSEG] THEN
REPEAT STRIP_TAC THEN MATCH_MP_TAC
CONG_IMP_EQ THEN EXISTS_TAC `p:num` THEN
REPEAT(CONJ_TAC THENL
[ASM_MESON_TAC[ARITH_RULE `i <= r ==> i < 2 * r + 1`] ; ALL_TAC]) THEN
MATCH_MP_TAC
CONG_MULT_LCANCEL THEN EXISTS_TAC `a:num` THEN
FIRST_X_ASSUM(MP_TAC o MATCH_MP (ARITH_RULE
`(if a then x else p - x) = (if b then y else p - y) ==> x < p /\ y < p
==> x = y \/ x + y = p`)) THEN ASM_SIMP_TAC[
DIVISION] THEN
DISCH_THEN(DISJ_CASES_THEN MP_TAC) THENL [ASM_MESON_TAC[
CONG]; ALL_TAC] THEN
DISCH_THEN(MP_TAC o C AP_THM `p:num` o AP_TERM `(MOD)`) THEN
ASM_SIMP_TAC[
MOD_ADD_MOD] THEN ASM_SIMP_TAC[GSYM
CONG] THEN
DISCH_THEN(MP_TAC o MATCH_MP
CONG_DIVIDES) THEN
ASM_SIMP_TAC[GSYM
LEFT_ADD_DISTRIB;
PRIME_DIVPROD_EQ;
DIVIDES_REFL] THEN
STRIP_TAC THENL
[ASM_MESON_TAC[coprime;
DIVIDES_REFL;
PRIME_1]; ALL_TAC] THEN
FIRST_ASSUM(MP_TAC o MATCH_MP
DIVIDES_LE) THEN
ASM_SIMP_TAC[ARITH_RULE `1 <= i ==> ~(i + j = 0)`] THEN
MAP_EVERY UNDISCH_TAC [`i <= r`; `j <= r`; `2 * r + 1 = p`] THEN ARITH_TAC);;
let GAUSS_LEMMA_2 = prove
(`prime p /\ coprime(a,p) /\ 2 * r + 1 = p
==> (nproduct(1..r) (\x. let b = (a * x) MOD p in
if b <= r then b else p - b) ==
(p - 1)
EXP (
CARD {x | x
IN 1..r /\ r < (a * x) MOD p}) *
a
EXP r * nproduct(1..r) (\x. x)) (mod p)`,
let GAUSS_LEMMA = prove
(`!a p r. prime p /\ coprime(a,p) /\ 2 * r + 1 = p
==> (a
is_quadratic_residue (mod p) <=>
EVEN(
CARD {x | x
IN 1..r /\ r < (a * x) MOD p}))`,
REPEAT GEN_TAC THEN DISCH_TAC THEN
MATCH_MP_TAC
CONG_COND_LEMMA THEN EXISTS_TAC `p:num` THEN CONJ_TAC THENL
[FIRST_X_ASSUM(CONJUNCTS_THEN2 MP_TAC STRIP_ASSUME_TAC) THEN
EXPAND_TAC "p" THEN ASM_CASES_TAC `r = 0` THENL
[REWRITE_TAC[ASSUME `r = 0`; ARITH;
PRIME_1];
UNDISCH_TAC `~(r = 0)` THEN ARITH_TAC];
FIRST_ASSUM(MP_TAC o MATCH_MP
GAUSS_LEMMA_4) THEN
REPEAT(COND_CASES_TAC THEN ASM_SIMP_TAC[
CONG_REFL]) THEN
REWRITE_TAC[
MULT_CLAUSES] THEN MESON_TAC[
CONG_SYM]]);;
(* ------------------------------------------------------------------------- *)
(* A more symmetrical version. *)
(* ------------------------------------------------------------------------- *)
"q"] THEN DISCH_THEN(MP_TAC o MATCH_MP
(ARITH_RULE `(2 * r + 1) * d + r < (2 * s + 1) * r
==> (2 * r) * d < (2 * r) * s`)) THEN
SIMP_TAC[LT_MULT_LCANCEL; ARITH_RULE `x < y ==> x + 1 <= y`]];
AP_TERM_TAC THEN
REWRITE_TAC[EXTENSION; IN_ELIM_PAIR_THM; FORALL_PAIR_THM] THEN
MAP_EVERY X_GEN_TAC [`x:num`; `y:num`] THEN
AP_TERM_TAC THEN AP_TERM_TAC THEN EQ_TAC THEN DISCH_TAC THENL
[MP_TAC(MATCH_MP PRIME_IMP_NZ (ASSUME `prime p`)) THEN
DISCH_THEN(MP_TAC o SPEC `q * x` o MATCH_MP DIVISION) THEN
FIRST_ASSUM(CONJUNCTS_THEN2 SUBST1_TAC MP_TAC) THEN
UNDISCH_TAC `2 * r + 1 = p` THEN ARITH_TAC;
MATCH_MP_TAC(TAUT `a /\ (a ==> b) ==> a /\ b`) THEN CONJ_TAC THENL
[ALL_TAC;
DISCH_THEN SUBST_ALL_TAC THEN
MATCH_MP_TAC(ARITH_RULE
`!p d. 2 * r + 1 = p /\ p * (d + 1) <= (d * p + m) + r ==> r < m`) THEN
MAP_EVERY EXISTS_TAC [`p:num`; `(q * x) DIV p`] THEN
ASM_MESON_TAC[DIVISION; PRIME_IMP_NZ]] THEN
MATCH_MP_TAC(ARITH_RULE `~(x <= y) /\ ~(y + 2 <= x) ==> x = y + 1`) THEN
REPEAT STRIP_TAC THENL
[SUBGOAL_THEN `y * p <= ((q * x) DIV p) * p` MP_TAC THENL
[ASM_SIMP_TAC[LE_MULT_RCANCEL; PRIME_IMP_NZ]; ALL_TAC];
SUBGOAL_THEN `((q * x) DIV p + 2) * p <= y * p` MP_TAC THENL
[ASM_SIMP_TAC[LE_MULT_RCANCEL; PRIME_IMP_NZ]; ALL_TAC]] THEN
MP_TAC(MATCH_MP PRIME_IMP_NZ (ASSUME `prime p`)) THEN
DISCH_THEN(MP_TAC o SPEC `q * x` o MATCH_MP DIVISION) THEN
ASM_ARITH_TAC]]);;
(* ------------------------------------------------------------------------- *)
(* The main result. *)
(* ------------------------------------------------------------------------- *)
let RECIPROCITY_SIMPLE = prove
(`!p q r s.
prime p /\
prime q /\
coprime (p,q) /\
2 * r + 1 = p /\
2 * s + 1 = q
==> ((q
is_quadratic_residue (mod p) <=>
p
is_quadratic_residue (mod q)) <=>
~(
ODD r /\
ODD s))`,
REPEAT STRIP_TAC THEN
MP_TAC(SPECL [`p:num`; `q:num`; `r:num`; `s:num`]
GAUSS_LEMMA_SYM) THEN
MP_TAC(SPECL [`p:num`; `q:num`; `r:num`; `s:num`] GAUSS_LEMMA_SYM') THEN
ASM_REWRITE_TAC[] THEN
ONCE_REWRITE_TAC[
COPRIME_SYM] THEN ASM_REWRITE_TAC[] THEN
REPEAT(DISCH_THEN SUBST1_TAC) THEN MATCH_MP_TAC
RECIPROCITY_SET_LEMMA THEN
EXISTS_TAC `{x,y | x
IN 1..r /\ y
IN 1..s /\ q * x + r < p * y}` THEN
EXISTS_TAC `{x,y | x
IN 1..r /\ y
IN 1..s /\ p * y + s < q * x}` THEN
REPEAT CONJ_TAC THEN
REWRITE_TAC[
PAIRWISE;
DISJOINT;
EXTENSION;
NOT_IN_EMPTY;
FORALL_PAIR_THM;
ALL;
IN_UNION;
IN_CROSS;
IN_ELIM_PAIR_THM;
IN_INTER]
THENL
[MAP_EVERY X_GEN_TAC [`x:num`; `y:num`] THEN
MAP_EVERY ASM_CASES_TAC [`x
IN 1..r`; `y
IN 1..s`] THEN ASM_SIMP_TAC[] THEN
SUBGOAL_THEN `~(q * x = p * y)` (fun th -> MP_TAC th THEN ARITH_TAC) THEN
DISCH_THEN(MP_TAC o AP_TERM `(divides) p`) THEN
ASM_SIMP_TAC[
PRIME_DIVPROD_EQ;
DIVIDES_REFL] THEN STRIP_TAC THENL
[ASM_MESON_TAC[
DIVIDES_REFL;
PRIME_1; coprime]; ALL_TAC] THEN
FIRST_X_ASSUM(MP_TAC o MATCH_MP
DIVIDES_LE) THEN
UNDISCH_TAC `x
IN 1..r` THEN REWRITE_TAC[
IN_NUMSEG] THEN
EXPAND_TAC "p" THEN ARITH_TAC;
ARITH_TAC;
MATCH_MP_TAC
BIJECTIONS_CARD_EQ THEN
REPEAT(EXISTS_TAC `\(x,y). (r + 1) - x,(s + 1) - y`) THEN
SIMP_TAC[
FINITE_SUBCROSS;
FINITE_NUMSEG] THEN
REWRITE_TAC[
FORALL_PAIR_THM;
IN_ELIM_PAIR_THM;
IN_NUMSEG;
PAIR_EQ] THEN
CONJ_TAC THEN MAP_EVERY X_GEN_TAC [`x:num`; `y:num`] THEN
SIMP_TAC[ARITH_RULE `x <= y ==> (y + 1) - ((y + 1) - x) = x`] THEN
SIMP_TAC[ARITH_RULE
`1 <= x /\ x <= y ==> 1 <= (y + 1) - x /\ (y + 1) - x <= y`] THEN
REWRITE_TAC[
LEFT_SUB_DISTRIB] THEN REPEAT STRIP_TAC THEN
MATCH_MP_TAC(ARITH_RULE
`x <= y /\ v + y + z < x + u ==> (y - x) + z < u - v`) THEN
ASM_SIMP_TAC[
LE_MULT_LCANCEL; ARITH_RULE `x <= r ==> x <= r + 1`] THEN
REWRITE_TAC[ARITH_RULE `a + x < y + a <=> x < y`] THEN
REPEAT(FIRST_X_ASSUM(SUBST_ALL_TAC o SYM)) THEN
ASM_ARITH_TAC]);;
(* ------------------------------------------------------------------------- *)
(* In terms of the Legendre symbol. *)
(* ------------------------------------------------------------------------- *)
let RECIPROCITY_LEGENDRE = prove
(`!p q. prime p /\ prime q /\
ODD p /\
ODD q /\ ~(p = q)
==> legendre(p,q) * legendre(q,p) =
--(&1) pow ((p - 1) DIV 2 * (q - 1) DIV 2)`,