(* ========================================================================= *)
(* HOL primality proving via Pocklington-optimized Pratt certificates. *)
(* ========================================================================= *)
needs "Library/iter.ml";;
needs "Library/prime.ml";;
prioritize_num();;
let num_0 = Int 0;;
let num_1 = Int 1;;
let num_2 = Int 2;;
(* ------------------------------------------------------------------------- *)
(* Mostly for compatibility. Should eliminate this eventually. *)
(* ------------------------------------------------------------------------- *)
let nat_mod_lemma = prove
(`!x y n:num. (x == y) (mod n) /\ y <= x ==> ?q. x = y + n * q`,
REPEAT GEN_TAC THEN REWRITE_TAC[
num_congruent] THEN
DISCH_THEN(CONJUNCTS_THEN2 MP_TAC ASSUME_TAC) THEN
ONCE_REWRITE_TAC
[INTEGER_RULE `(x == y) (mod &n) <=> &n divides (x - y)`] THEN
ASM_SIMP_TAC[
INT_OF_NUM_SUB;
ARITH_RULE `x <= y ==> (y:num = x + d <=> y - x = d)`] THEN
REWRITE_TAC[GSYM
num_divides; divides]);;
let nat_mod = prove
(`!x y n:num. (mod n) x y <=> ?q1 q2. x + n * q1 = y + n * q2`,
REPEAT GEN_TAC THEN GEN_REWRITE_TAC LAND_CONV [GSYM cong] THEN
EQ_TAC THENL [ALL_TAC; NUMBER_TAC] THEN
MP_TAC(SPECL [`x:num`; `y:num`]
LE_CASES) THEN
REWRITE_TAC[TAUT `a \/ b ==> c ==> d <=> (c /\ b) \/ (c /\ a) ==> d`] THEN
DISCH_THEN(DISJ_CASES_THEN MP_TAC) THENL
[ALL_TAC;
ONCE_REWRITE_TAC[NUMBER_RULE
`(x:num == y) (mod n) <=> (y == x) (mod n)`]] THEN
MESON_TAC[
nat_mod_lemma; ARITH_RULE `x + y * 0 = x`]);;
(* ------------------------------------------------------------------------- *)
(* Lemmas about previously defined terms. *)
(* ------------------------------------------------------------------------- *)
let PRIME = prove
(`!p. prime p <=> ~(p = 0) /\ ~(p = 1) /\ !m. 0 < m /\ m < p ==> coprime(p,m)`,
GEN_TAC THEN ASM_CASES_TAC `p = 0` THEN ASM_REWRITE_TAC[
PRIME_0] THEN
ASM_CASES_TAC `p = 1` THEN ASM_REWRITE_TAC[
PRIME_1] THEN
EQ_TAC THENL
[DISCH_THEN(MP_TAC o MATCH_MP
PRIME_COPRIME) THEN
DISCH_TAC THEN X_GEN_TAC `m:num` THEN STRIP_TAC THEN
FIRST_X_ASSUM(MP_TAC o SPEC `m:num`) THEN
STRIP_TAC THEN ASM_REWRITE_TAC[
COPRIME_1] THEN
ASM_MESON_TAC[
NOT_LT;
LT_REFL;
DIVIDES_LE]; ALL_TAC] THEN
FIRST_ASSUM(X_CHOOSE_THEN `q:num` MP_TAC o MATCH_MP
PRIME_FACTOR) THEN
REPEAT STRIP_TAC THEN FIRST_X_ASSUM(MP_TAC o SPEC `q:num`) THEN
SUBGOAL_THEN `~(coprime(p,q))` (fun th -> REWRITE_TAC[th]) THENL
[REWRITE_TAC[coprime;
NOT_FORALL_THM] THEN
EXISTS_TAC `q:num` THEN ASM_REWRITE_TAC[
DIVIDES_REFL] THEN
ASM_MESON_TAC[
PRIME_1]; ALL_TAC] THEN
FIRST_ASSUM(MP_TAC o MATCH_MP
DIVIDES_LE) THEN
ASM_REWRITE_TAC[
LT_LE;
LE_0] THEN
ASM_CASES_TAC `p:num = q` THEN ASM_REWRITE_TAC[] THEN
SIMP_TAC[] THEN DISCH_TAC THEN DISCH_THEN(SUBST_ALL_TAC o SYM) THEN
ASM_MESON_TAC[
DIVIDES_ZERO]);;
(* ------------------------------------------------------------------------- *)
(* Congruences. *)
(* ------------------------------------------------------------------------- *)
let CONG = prove
(`!x y n. ~(n = 0) ==> ((x == y) (mod n) <=> (x MOD n = y MOD n))`,
REWRITE_TAC[cong;
nat_mod] THEN
REPEAT STRIP_TAC THEN EQ_TAC THEN STRIP_TAC THENL
[ASM_CASES_TAC `x <= y` THENL
[CONV_TAC SYM_CONV THEN MATCH_MP_TAC
MOD_EQ THEN EXISTS_TAC `q1 - q2`;
MATCH_MP_TAC
MOD_EQ THEN EXISTS_TAC `q2 - q1`] THEN
REWRITE_TAC[
RIGHT_SUB_DISTRIB] THEN
POP_ASSUM_LIST(MP_TAC o end_itlist CONJ) THEN ARITH_TAC;
MAP_EVERY EXISTS_TAC [`y DIV n`; `x DIV n`] THEN
UNDISCH_TAC `x MOD n = y MOD n` THEN
MATCH_MP_TAC(ARITH_RULE
`(y = dy + my) /\ (x = dx + mx) ==> (mx = my) ==> (x + dy = y + dx)`) THEN
ONCE_REWRITE_TAC[
MULT_SYM] THEN ASM_SIMP_TAC[
DIVISION]]);;
let CONG_0 = prove
(`!x n. ((x == 0) (mod n) <=> n divides x)`,
NUMBER_TAC);;
let CONG_SUB_CASES = prove
(`!x y n. (x == y) (mod n) <=>
if x <= y then (y - x == 0) (mod n)
else (x - y == 0) (mod n)`,
REPEAT GEN_TAC THEN REWRITE_TAC[cong;
nat_mod] THEN
COND_CASES_TAC THENL
[GEN_REWRITE_TAC LAND_CONV [
SWAP_EXISTS_THM]; ALL_TAC] THEN
REPEAT(AP_TERM_TAC THEN ABS_TAC) THEN
POP_ASSUM MP_TAC THEN ARITH_TAC);;
let CONG_CASES = prove
(`!x y n. (x == y) (mod n) <=> (?q. x = q * n + y) \/ (?q. y = q * n + x)`,
REPEAT GEN_TAC THEN EQ_TAC THENL
[ALL_TAC; STRIP_TAC THEN ASM_REWRITE_TAC[] THEN NUMBER_TAC] THEN
REWRITE_TAC[cong;
nat_mod;
LEFT_IMP_EXISTS_THM] THEN
MAP_EVERY X_GEN_TAC [`q1:num`; `q2:num`] THEN
DISCH_THEN(MP_TAC o MATCH_MP(ARITH_RULE
`x + a = y + b ==> x = (b - a) + y \/ y = (a - b) + x`)) THEN
REWRITE_TAC[GSYM
LEFT_SUB_DISTRIB] THEN MESON_TAC[
MULT_SYM]);;
let CONG_SYM = prove
(`!x y n. (x == y) (mod n) <=> (y == x) (mod n)`,
NUMBER_TAC);;
let CONG_TRANS = prove
(`!x y z n. (x == y) (mod n) /\ (y == z) (mod n) ==> (x == z) (mod n)`,
NUMBER_TAC);;
let CONG_ADD = prove
(`!x x' y y'.
(x == x') (mod n) /\ (y == y') (mod n) ==> (x + y == x' + y') (mod n)`,
NUMBER_TAC);;
let CONG_MULT = prove
(`!x x' y y'.
(x == x') (mod n) /\ (y == y') (mod n) ==> (x * y == x' * y') (mod n)`,
NUMBER_TAC);;
let CONG_SUB = prove
(`!x x' y y'.
(x == x') (mod n) /\ (y == y') (mod n) /\ y <= x /\ y' <= x'
==> (x - y == x' - y') (mod n)`,
let CONG_IMP_EQ = prove
(`!x y n. x < n /\ y < n /\ (x == y) (mod n) ==> x = y`,
REPEAT GEN_TAC THEN ASM_CASES_TAC `n = 0` THEN ASM_REWRITE_TAC[
LT] THEN
ASM_MESON_TAC[
CONG;
MOD_LT]);;
let CONG_1_DIVIDES = prove
(`!n x. (x == 1) (mod n) ==> n divides (x - 1)`,
REPEAT GEN_TAC THEN REWRITE_TAC[divides; cong;
nat_mod] THEN
REWRITE_TAC[
LEFT_IMP_EXISTS_THM] THEN REPEAT GEN_TAC THEN
DISCH_THEN(MP_TAC o MATCH_MP (ARITH_RULE
`(x + q1 = 1 + q2) ==> ~(x = 0) ==> (x - 1 = q2 - q1)`)) THEN
ASM_CASES_TAC `x = 0` THEN
ASM_REWRITE_TAC[ARITH; GSYM
LEFT_SUB_DISTRIB] THEN
ASM_MESON_TAC[
MULT_CLAUSES]);;
let CONG_DIVIDES = prove
(`!x y n. (x == y) (mod n) ==> (n divides x <=> n divides y)`,
NUMBER_TAC);;
let CONG_COPRIME = prove
(`!x y n. (x == y) (mod n) ==> (coprime(n,x) <=> coprime(n,y))`,
NUMBER_TAC);;
let CONG_MOD = prove
(`!a n. ~(n = 0) ==> (a MOD n == a) (mod n)`,
REPEAT STRIP_TAC THEN FIRST_ASSUM(MP_TAC o MATCH_MP
DIVISION) THEN
DISCH_THEN(MP_TAC o SPEC `a:num`) THEN
DISCH_THEN(CONJUNCTS_THEN2 MP_TAC ASSUME_TAC) THEN
DISCH_THEN(fun th -> GEN_REWRITE_TAC (RATOR_CONV o RAND_CONV) [th]) THEN
REWRITE_TAC[cong;
nat_mod] THEN
MAP_EVERY EXISTS_TAC [`a DIV n`; `0`] THEN
REWRITE_TAC[
MULT_CLAUSES;
ADD_CLAUSES;
ADD_AC;
MULT_AC]);;
let MOD_MULT_CONG = prove
(`!a b x y. ~(a = 0) /\ ~(b = 0)
==> ((x MOD (a * b) == y) (mod a) <=> (x == y) (mod a))`,
let CONG_MOD_MULT = prove
(`!x y m n. (x == y) (mod n) /\ m divides n ==> (x == y) (mod m)`,
NUMBER_TAC);;
let CONG_LMOD = prove
(`!x y n. ~(n = 0) ==> ((x MOD n == y) (mod n) <=> (x == y) (mod n))`,
let CONG_RMOD = prove
(`!x y n. ~(n = 0) ==> ((x == y MOD n) (mod n) <=> (x == y) (mod n))`,
(* ------------------------------------------------------------------------- *)
(* Some things when we know more about the order. *)
(* ------------------------------------------------------------------------- *)
let CONG_LT = prove
(`!x y n. y < n ==> ((x == y) (mod n) <=> ?d. x = d * n + y)`,
REWRITE_TAC[GSYM
INT_OF_NUM_EQ; GSYM
INT_OF_NUM_LT;
GSYM
INT_OF_NUM_ADD; GSYM
INT_OF_NUM_MUL] THEN
REWRITE_TAC[
num_congruent;
int_congruent] THEN
REWRITE_TAC[INT_ARITH `x = m * n + y <=> x - y:int = n * m`] THEN
REPEAT STRIP_TAC THEN EQ_TAC THENL [ALL_TAC; MESON_TAC[]] THEN
DISCH_THEN(X_CHOOSE_TAC `d:int`) THEN
DISJ_CASES_TAC(SPEC `d:int`
INT_IMAGE) THENL [ASM_MESON_TAC[]; ALL_TAC] THEN
FIRST_X_ASSUM(X_CHOOSE_THEN `m:num` SUBST_ALL_TAC) THEN
FIRST_X_ASSUM(SUBST_ALL_TAC o MATCH_MP (INT_ARITH
`x - y:int = n * --m ==> y = x + n * m`)) THEN
POP_ASSUM MP_TAC THEN DISJ_CASES_TAC(ARITH_RULE `m = 0 \/ 1 <= m`) THEN
ASM_REWRITE_TAC[
INT_MUL_RZERO; INT_ARITH `x - (x + a):int = --a`] THENL
[STRIP_TAC THEN EXISTS_TAC `0` THEN INT_ARITH_TAC;
FIRST_X_ASSUM(MP_TAC o GEN_REWRITE_RULE I [
LE_EXISTS]) THEN
DISCH_THEN(X_CHOOSE_THEN `p:num` SUBST1_TAC) THEN
REWRITE_TAC[
INT_OF_NUM_ADD;
INT_OF_NUM_MUL;
INT_OF_NUM_LT] THEN
ARITH_TAC]);;
let CONG_LE = prove
(`!x y n. y <= x ==> ((x == y) (mod n) <=> ?q. x = q * n + y)`,
ONCE_REWRITE_TAC[
CONG_SYM] THEN ONCE_REWRITE_TAC[
CONG_SUB_CASES] THEN
SIMP_TAC[ARITH_RULE `y <= x ==> (x = a + y <=> x - y = a)`] THEN
REWRITE_TAC[
CONG_0; divides] THEN MESON_TAC[
MULT_SYM]);;
let CONG_TO_1 = prove
(`!a n. (a == 1) (mod n) <=> a = 0 /\ n = 1 \/ ?m. a = 1 + m * n`,
REPEAT STRIP_TAC THEN
ASM_CASES_TAC `n = 1` THEN ASM_REWRITE_TAC[
CONG_MOD_1] THENL
[MESON_TAC[ARITH_RULE `n = 0 \/ n = 1 + (n - 1) * 1`]; ALL_TAC] THEN
DISJ_CASES_TAC(ARITH_RULE `a = 0 \/ ~(a = 0) /\ 1 <= a`) THEN
ASM_SIMP_TAC[
CONG_LE] THENL [ALL_TAC; MESON_TAC[
ADD_SYM;
MULT_SYM]] THEN
ASM_MESON_TAC[
CONG_SYM;
CONG_0;
DIVIDES_ONE; ARITH_RULE `~(0 = 1 + a)`]);;
(* ------------------------------------------------------------------------- *)
(* In particular two common cases. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Conversion to evaluate congruences. *)
(* ------------------------------------------------------------------------- *)
let CONG_CONV =
let pth = prove
(`(x == y) (mod n) <=>
if x <= y then n divides (y - x) else n divides (x - y)`,
ONCE_REWRITE_TAC[
CONG_SUB_CASES] THEN REWRITE_TAC[
CONG_0_DIVIDES]) in
GEN_REWRITE_CONV I [pth] THENC
RATOR_CONV(LAND_CONV NUM_LE_CONV) THENC
GEN_REWRITE_CONV I [
COND_CLAUSES] THENC
RAND_CONV NUM_SUB_CONV THENC
DIVIDES_CONV;;
(* ------------------------------------------------------------------------- *)
(* Some basic theorems about solving congruences. *)
(* ------------------------------------------------------------------------- *)
let CONG_SOLVE_UNIQUE = prove
(`!a b n. coprime(a,n) /\ ~(n = 0) ==> ?!x. x < n /\ (a * x == b) (mod n)`,
REPEAT STRIP_TAC THEN REWRITE_TAC[
EXISTS_UNIQUE] THEN
MP_TAC(SPECL [`a:num`; `b:num`; `n:num`]
CONG_SOLVE) THEN
ASM_REWRITE_TAC[] THEN DISCH_THEN(X_CHOOSE_TAC `x:num`) THEN
EXISTS_TAC `x MOD n` THEN
MATCH_MP_TAC(TAUT `a /\ (a ==> b) ==> a /\ b`) THEN CONJ_TAC THENL
[ASM_SIMP_TAC[
DIVISION] THEN MATCH_MP_TAC
CONG_TRANS THEN
EXISTS_TAC `a * x:num` THEN ASM_REWRITE_TAC[] THEN
MATCH_MP_TAC
CONG_MULT THEN REWRITE_TAC[
CONG_REFL] THEN
ASM_SIMP_TAC[
CONG;
MOD_MOD_REFL];
ALL_TAC] THEN
STRIP_TAC THEN X_GEN_TAC `y:num` THEN STRIP_TAC THEN
MATCH_MP_TAC
EQ_TRANS THEN EXISTS_TAC `y MOD n` THEN CONJ_TAC THENL
[ASM_SIMP_TAC[
MOD_LT]; ALL_TAC] THEN
ASM_SIMP_TAC[GSYM
CONG] THEN MATCH_MP_TAC
CONG_MULT_LCANCEL THEN
EXISTS_TAC `a:num` THEN ASM_MESON_TAC[
CONG_TRANS;
CONG_SYM]);;
let CONG_SOLVE_UNIQUE_NONTRIVIAL = prove
(`!a p x. prime p /\ coprime(p,a) /\ 0 < x /\ x < p
==> ?!y. 0 < y /\ y < p /\ (x * y == a) (mod p)`,
REPEAT GEN_TAC THEN ASM_CASES_TAC `p = 0` THEN ASM_REWRITE_TAC[
PRIME_0] THEN
REPEAT STRIP_TAC THEN SUBGOAL_THEN `1 < p` ASSUME_TAC THENL
[REWRITE_TAC[ARITH_RULE `1 < p <=> ~(p = 0) /\ ~(p = 1)`] THEN
ASM_MESON_TAC[
PRIME_1];
ALL_TAC] THEN
MP_TAC(SPECL [`x:num`; `a:num`; `p:num`]
CONG_SOLVE_UNIQUE) THEN
ANTS_TAC THENL
[CONJ_TAC THENL [ALL_TAC; ASM_MESON_TAC[
PRIME_0]] THEN
ONCE_REWRITE_TAC[
COPRIME_SYM] THEN
MP_TAC(SPECL [`x:num`; `p:num`]
PRIME_COPRIME) THEN
ASM_CASES_TAC `x = 1` THEN ASM_REWRITE_TAC[
COPRIME_1] THEN
ASM_MESON_TAC[
COPRIME_SYM;
NOT_LT;
DIVIDES_LE;
LT_REFL];
ALL_TAC] THEN
MATCH_MP_TAC EQ_IMP THEN
AP_TERM_TAC THEN GEN_REWRITE_TAC I [
FUN_EQ_THM] THEN
X_GEN_TAC `r:num` THEN REWRITE_TAC[] THEN
REWRITE_TAC[ARITH_RULE `0 < r <=> ~(r = 0)`] THEN
ASM_CASES_TAC `r = 0` THEN ASM_REWRITE_TAC[
MULT_CLAUSES] THEN
ASM_SIMP_TAC[ARITH_RULE `~(p = 0) ==> 0 < p`] THEN
ONCE_REWRITE_TAC[
CONG_SYM] THEN REWRITE_TAC[
CONG_0] THEN
ASM_MESON_TAC[
DIVIDES_REFL;
PRIME_1; coprime]);;
(* ------------------------------------------------------------------------- *)
(* Forms of the Chinese remainder theorem. *)
(* ------------------------------------------------------------------------- *)
let CONG_CHINESE = prove
(`coprime(a,b) /\ (x == y) (mod a) /\ (x == y) (mod b)
==> (x == y) (mod (a * b))`,
let CHINESE_REMAINDER_COPRIME_UNIQUE = prove
(`!a b m n.
coprime(a,b) /\ ~(a = 0) /\ ~(b = 0) /\ coprime(m,a) /\ coprime(n,b)
==> ?!x. coprime(x,a * b) /\ x < a * b /\
(x == m) (mod a) /\ (x == n) (mod b)`,
let CONG_CHINESE_EQ = prove
(`!a b x y.
coprime(a,b)
==> ((x == y) (mod (a * b)) <=> (x == y) (mod a) /\ (x == y) (mod b))`,
NUMBER_TAC);;
(* ------------------------------------------------------------------------- *)
(* Euler totient function. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Value on primes and prime powers. *)
(* ------------------------------------------------------------------------- *)
let PHI_PRIME_EQ = prove
(`!n. (phi n = n - 1) /\ ~(n = 0) /\ ~(n = 1) <=> prime n`,
GEN_TAC THEN REWRITE_TAC[
PRIME] THEN
ASM_CASES_TAC `n = 0` THEN ASM_REWRITE_TAC[] THEN
ASM_CASES_TAC `n = 1` THEN ASM_REWRITE_TAC[
PHI_1; ARITH] THEN
MP_TAC(SPEC `n:num`
FINITE_NUMBER_SEGMENT) THEN
ASM_SIMP_TAC[
PHI_ANOTHER;
HAS_SIZE] THEN
DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC (SUBST1_TAC o SYM)) THEN
MATCH_MP_TAC
EQ_TRANS THEN EXISTS_TAC
`{m | 0 < m /\ m < n /\ coprime (m,n)} = {m | 0 < m /\ m < n}` THEN
CONJ_TAC THENL
[ALL_TAC;
REWRITE_TAC[
EXTENSION;
IN_ELIM_THM] THEN
AP_TERM_TAC THEN ABS_TAC THEN
REWRITE_TAC[
COPRIME_SYM] THEN CONV_TAC TAUT] THEN
EQ_TAC THEN SIMP_TAC[] THEN DISCH_TAC THEN
MATCH_MP_TAC
CARD_SUBSET_EQ THEN ASM_REWRITE_TAC[] THEN
SIMP_TAC[
SUBSET;
IN_ELIM_THM]);;
(* ------------------------------------------------------------------------- *)
(* Multiplicativity property. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Even-ness of phi for most arguments. *)
(* ------------------------------------------------------------------------- *)
let EVEN_PHI_EQ = prove
(`!n.
EVEN(phi n) <=> n = 0 \/ 3 <= n`,
GEN_TAC THEN EQ_TAC THENL
[ONCE_REWRITE_TAC[GSYM CONTRAPOS_THM] THEN
REWRITE_TAC[ARITH_RULE `~(n = 0 \/ 3 <= n) <=> n = 1 \/ n = 2`] THEN
STRIP_TAC THEN ASM_REWRITE_TAC[
PHI_1;
PHI_2] THEN CONV_TAC NUM_REDUCE_CONV;
STRIP_TAC THEN ASM_SIMP_TAC[
PHI_0;
EVEN_PHI;
EVEN]]);;
(* ------------------------------------------------------------------------- *)
(* Some iteration theorems. *)
(* ------------------------------------------------------------------------- *)
let COPRIME_NPRODUCT = prove
(`!s n. FINITE s /\ (!x. x IN s ==> coprime(n,a(x)))
==> coprime(n,iterate (*) s a)`,
REWRITE_TAC[IMP_CONJ; RIGHT_FORALL_IMP_THM] THEN
MATCH_MP_TAC FINITE_INDUCT_STRONG THEN
SIMP_TAC[ITERATE_CLAUSES; MONOIDAL_MUL; NEUTRAL_MUL;
IN_INSERT; COPRIME_MUL; COPRIME_1]);;
let ITERATE_OVER_COPRIME = prove
(`!op f n k.
monoidal(op) /\ coprime(k,n) /\
(!x y. (x == y) (mod n) ==> f x = f y)
==> iterate op {d | coprime(d,n) /\ d < n} (\m. f(k * m)) =
iterate op {d | coprime(d,n) /\ d < n} f`,
let lemma = prove
(`~(n = 0) ==> ((a * x MOD n == b) (mod n) <=> (a * x == b) (mod n))`,
MESON_TAC[CONG_REFL; CONG_SYM; CONG_TRANS; CONG_MULT; CONG_MOD]) in
REPEAT GEN_TAC THEN ASM_CASES_TAC `n = 0` THENL
[ASM_SIMP_TAC[LT; SET_RULE `{x | F} = {}`; ITERATE_CLAUSES]; ALL_TAC] THEN
STRIP_TAC THEN SUBGOAL_THEN `?m. (k * m == 1) (mod n)` CHOOSE_TAC THENL
[ASM_MESON_TAC[CONG_SOLVE; MULT_SYM; CONG_SYM]; ALL_TAC] THEN
FIRST_ASSUM(MATCH_MP_TAC o MATCH_MP ITERATE_EQ_GENERAL_INVERSES) THEN
MAP_EVERY EXISTS_TAC [`\x. (k * x) MOD n`; `\x. (m * x) MOD n`] THEN
REWRITE_TAC[IN_ELIM_THM] THEN
ASM_SIMP_TAC[COPRIME_MOD; CONG_MOD_LT; CONG_LMOD; DIVISION; lemma;
COPRIME_LMUL] THEN
REPEAT STRIP_TAC THEN
TRY(FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_SIMP_TAC[CONG_LMOD]) THEN
UNDISCH_TAC `(k * m == 1) (mod n)` THEN CONV_TAC NUMBER_RULE);;
let ITERATE_ITERATE_DIVISORS = prove
(`!op:A->A->A f x.
monoidal op
==> iterate op (1..x) (\n. iterate op {d | d divides n} (f n)) =
iterate op (1..x)
(\n. iterate op (1..(x DIV n)) (\k. f (k * n) n))`,
REPEAT STRIP_TAC THEN
ASM_SIMP_TAC[ITERATE_ITERATE_PRODUCT; FINITE_NUMSEG; FINITE_DIVISORS;
IN_NUMSEG; LE_1] THEN
MATCH_MP_TAC(REWRITE_RULE[RIGHT_IMP_FORALL_THM; IMP_IMP]
ITERATE_EQ_GENERAL_INVERSES) THEN
MAP_EVERY EXISTS_TAC [`\(n,d). d,n DIV d`; `\(n:num,k). n * k,n`] THEN
ASM_SIMP_TAC[FORALL_PAIR_THM; IN_ELIM_PAIR_THM; PAIR_EQ] THEN CONJ_TAC THEN
REWRITE_TAC[IN_ELIM_THM] THEN X_GEN_TAC `n:num` THENL
[X_GEN_TAC `k:num` THEN SIMP_TAC[DIV_MULT; LE_1; GSYM LE_RDIV_EQ] THEN
SIMP_TAC[MULT_EQ_0; ARITH_RULE `1 <= x <=> ~(x = 0)`] THEN
DISCH_THEN(K ALL_TAC) THEN NUMBER_TAC;
X_GEN_TAC `d:num` THEN ASM_CASES_TAC `d = 0` THEN
ASM_REWRITE_TAC[DIVIDES_ZERO] THENL [ARITH_TAC; ALL_TAC] THEN
STRIP_TAC THEN ASM_SIMP_TAC[DIV_MONO] THEN CONJ_TAC THENL
[ALL_TAC; ASM_MESON_TAC[DIVIDES_DIV_MULT; MULT_SYM]] THEN
FIRST_X_ASSUM(MP_TAC o MATCH_MP DIVIDES_LE) THEN
ASM_SIMP_TAC[DIV_EQ_0; ARITH_RULE `1 <= x <=> ~(x = 0)`] THEN
ASM_ARITH_TAC]);;
(* ------------------------------------------------------------------------- *)
(* Fermat's Little theorem / Fermat-Euler theorem. *)
(* ------------------------------------------------------------------------- *)
let FERMAT_LITTLE = prove
(`!a n. coprime(a,n) ==> (a
EXP (phi n) == 1) (mod n)`,
REPEAT GEN_TAC THEN ASM_CASES_TAC `n = 0` THEN
ASM_SIMP_TAC[
COPRIME_0;
PHI_0;
CONG_MOD_0] THEN CONV_TAC NUM_REDUCE_CONV THEN
DISCH_TAC THEN MATCH_MP_TAC
CONG_MULT_LCANCEL THEN
EXISTS_TAC `iterate (*) {m | coprime (m,n) /\ m < n} (\m. m)` THEN
ONCE_REWRITE_TAC[MULT_SYM] THEN REWRITE_TAC[PHI_ALT; MULT_CLAUSES] THEN
SIMP_TAC[IN_ELIM_THM; ONCE_REWRITE_RULE[COPRIME_SYM] COPRIME_NPRODUCT;
PHI_FINITE_LEMMA; GSYM NPRODUCT_CMUL] THEN
ONCE_REWRITE_TAC[CONG_SYM] THEN MATCH_MP_TAC CONG_TRANS THEN
EXISTS_TAC `iterate (*) {m | coprime(m,n) /\ m < n} (\m. (a * m) MOD n)` THEN
ASM_SIMP_TAC[
NPRODUCT_MOD;
PHI_FINITE_LEMMA] THEN
MP_TAC(ISPECL [`( * ):num->num->num`; `\x. x MOD n`; `n:num`; `a:num`]
ITERATE_OVER_COPRIME) THEN
ASM_SIMP_TAC[
MONOIDAL_MUL; GSYM
CONG] THEN
DISCH_TAC THEN ONCE_REWRITE_TAC[
CONG_SYM] THEN
MATCH_MP_TAC
NPRODUCT_MOD THEN ASM_SIMP_TAC[
PHI_FINITE_LEMMA]);;
(* ------------------------------------------------------------------------- *)
(* Lucas's theorem. *)
(* ------------------------------------------------------------------------- *)
let LUCAS_COPRIME_LEMMA = prove
(`!m n a. ~(m = 0) /\ (a
EXP m == 1) (mod n) ==> coprime(a,n)`,
REPEAT GEN_TAC THEN ASM_CASES_TAC `n = 0` THENL
[ASM_REWRITE_TAC[
CONG_MOD_0;
EXP_EQ_1] THEN
ASM_CASES_TAC `m = 0` THEN ASM_REWRITE_TAC[] THEN
ONCE_REWRITE_TAC[
COPRIME_SYM] THEN SIMP_TAC[
COPRIME_1];
ALL_TAC] THEN
ASM_CASES_TAC `n = 1` THEN ASM_REWRITE_TAC[
COPRIME_1] THEN
REPEAT STRIP_TAC THEN
REWRITE_TAC[coprime] THEN X_GEN_TAC `d:num` THEN STRIP_TAC THEN
UNDISCH_TAC `(a
EXP m == 1) (mod n)` THEN
ASM_SIMP_TAC[
CONG] THEN
SUBGOAL_THEN `1 MOD n = 1` SUBST1_TAC THENL
[MATCH_MP_TAC
MOD_UNIQ THEN EXISTS_TAC `0` THEN
REWRITE_TAC[
MULT_CLAUSES;
ADD_CLAUSES] THEN
MAP_EVERY UNDISCH_TAC [`~(n = 0)`; `~(n = 1)`] THEN ARITH_TAC;
ALL_TAC] THEN
DISCH_TAC THEN
SUBGOAL_THEN `d divides (a
EXP m) MOD n` MP_TAC THENL
[ALL_TAC; ASM_SIMP_TAC[
DIVIDES_ONE]] THEN
MATCH_MP_TAC
DIVIDES_ADD_REVR THEN
EXISTS_TAC `a
EXP m DIV n * n` THEN
ASM_SIMP_TAC[GSYM
DIVISION;
DIVIDES_LMUL] THEN
SUBGOAL_THEN `m = SUC(m - 1)` SUBST1_TAC THENL
[UNDISCH_TAC `~(m = 0)` THEN ARITH_TAC;
ASM_SIMP_TAC[
EXP;
DIVIDES_RMUL]]);;
let LUCAS_WEAK = prove
(`!a n. 2 <= n /\
(a
EXP (n - 1) == 1) (mod n) /\
(!m. 0 < m /\ m < n - 1 ==> ~(a
EXP m == 1) (mod n))
==> prime(n)`,
REPEAT STRIP_TAC THEN
ASM_SIMP_TAC[GSYM
PHI_PRIME_EQ;
PHI_LIMIT_STRONG; GSYM
LE_ANTISYM;
ARITH_RULE `2 <= n ==> ~(n = 0) /\ ~(n = 1)`] THEN
FIRST_X_ASSUM(MP_TAC o SPEC `phi n`) THEN
SUBGOAL_THEN `coprime(a,n)` (fun th -> SIMP_TAC[
FERMAT_LITTLE; th]) THENL
[MATCH_MP_TAC
LUCAS_COPRIME_LEMMA THEN EXISTS_TAC `n - 1` THEN
ASM_SIMP_TAC [ARITH_RULE `2 <= n ==> ~(n - 1 = 0)`]; ALL_TAC] THEN
REWRITE_TAC[GSYM
NOT_LT] THEN
MATCH_MP_TAC(TAUT `a ==> ~(a /\ b) ==> ~b`) THEN
ASM_SIMP_TAC[
PHI_LOWERBOUND_1; ARITH_RULE `1 <= n ==> 0 < n`]);;
let LUCAS = prove
(`!a n. 2 <= n /\
(a
EXP (n - 1) == 1) (mod n) /\
(!p. prime(p) /\ p divides (n - 1)
==> ~(a
EXP ((n - 1) DIV p) == 1) (mod n))
==> prime(n)`,
REPEAT STRIP_TAC THEN
FIRST_ASSUM(ASSUME_TAC o MATCH_MP(ARITH_RULE `2 <= n ==> ~(n = 0)`)) THEN
MATCH_MP_TAC
LUCAS_WEAK THEN EXISTS_TAC `a:num` THEN ASM_REWRITE_TAC[] THEN
REWRITE_TAC[TAUT `a ==> ~b <=> ~(a /\ b)`; GSYM
NOT_EXISTS_THM] THEN
ONCE_REWRITE_TAC[
num_WOP] THEN
DISCH_THEN(X_CHOOSE_THEN `m:num` STRIP_ASSUME_TAC) THEN
FIRST_ASSUM(ASSUME_TAC o MATCH_MP(ARITH_RULE `0 < n ==> ~(n = 0)`)) THEN
SUBGOAL_THEN `m divides (n - 1)` MP_TAC THENL
[REWRITE_TAC[divides] THEN ONCE_REWRITE_TAC[
MULT_SYM] THEN
ASM_SIMP_TAC[GSYM
MOD_EQ_0] THEN
MATCH_MP_TAC(ARITH_RULE `~(0 < n) ==> (n = 0)`) THEN DISCH_TAC THEN
FIRST_X_ASSUM(MP_TAC o SPEC `(n - 1) MOD m`) THEN
ASM_SIMP_TAC[
DIVISION] THEN CONJ_TAC THENL
[MATCH_MP_TAC
LT_TRANS THEN EXISTS_TAC `m:num` THEN
ASM_SIMP_TAC[
DIVISION]; ALL_TAC] THEN
MATCH_MP_TAC
CONG_MULT_LCANCEL THEN
EXISTS_TAC `a
EXP ((n - 1) DIV m * m)` THEN CONJ_TAC THENL
[ONCE_REWRITE_TAC[
COPRIME_SYM] THEN MATCH_MP_TAC
COPRIME_EXP THEN
ONCE_REWRITE_TAC[
COPRIME_SYM] THEN MATCH_MP_TAC
LUCAS_COPRIME_LEMMA THEN
EXISTS_TAC `m:num` THEN ASM_SIMP_TAC[]; ALL_TAC] THEN
REWRITE_TAC[GSYM
EXP_ADD] THEN
ASM_SIMP_TAC[GSYM
DIVISION] THEN REWRITE_TAC[
MULT_CLAUSES] THEN
ONCE_REWRITE_TAC[
MULT_SYM] THEN REWRITE_TAC[GSYM
EXP_EXP] THEN
UNDISCH_TAC `(a
EXP (n - 1) == 1) (mod n)` THEN
UNDISCH_TAC `(a
EXP m == 1) (mod n)` THEN
ASM_SIMP_TAC[
CONG] THEN REPEAT DISCH_TAC THEN MATCH_MP_TAC
EQ_TRANS THEN
EXISTS_TAC `((a
EXP m) MOD n)
EXP ((n - 1) DIV m) MOD n` THEN
CONJ_TAC THENL [ALL_TAC; ASM_SIMP_TAC[
MOD_EXP_MOD]] THEN
ASM_REWRITE_TAC[] THEN ASM_SIMP_TAC[
MOD_EXP_MOD] THEN
REWRITE_TAC[
EXP_ONE]; ALL_TAC] THEN
REWRITE_TAC[divides] THEN
DISCH_THEN(X_CHOOSE_THEN `r:num` SUBST_ALL_TAC) THEN
SUBGOAL_THEN `~(r = 1)` MP_TAC THENL
[UNDISCH_TAC `m < m * r` THEN CONV_TAC CONTRAPOS_CONV THEN
SIMP_TAC[
MULT_CLAUSES;
LT_REFL]; ALL_TAC] THEN
DISCH_THEN(MP_TAC o MATCH_MP
PRIME_FACTOR) THEN
DISCH_THEN(X_CHOOSE_THEN `p:num` MP_TAC) THEN STRIP_TAC THEN
UNDISCH_TAC `!p. prime p /\ p divides m * r
==> ~(a
EXP ((m * r) DIV p) == 1) (mod n)` THEN
DISCH_THEN(MP_TAC o SPEC `p:num`) THEN ASM_SIMP_TAC[
DIVIDES_LMUL] THEN
SUBGOAL_THEN `(m * r) DIV p = m * (r DIV p)` SUBST1_TAC THENL
[MATCH_MP_TAC
DIV_UNIQ THEN EXISTS_TAC `0` THEN
UNDISCH_TAC `prime p` THEN
ASM_CASES_TAC `p = 0` THEN ASM_REWRITE_TAC[
PRIME_0] THEN
ASM_SIMP_TAC[ARITH_RULE `~(p = 0) ==> 0 < p`] THEN
DISCH_TAC THEN REWRITE_TAC[
ADD_CLAUSES; GSYM
MULT_ASSOC] THEN
AP_TERM_TAC THEN UNDISCH_TAC `p divides r` THEN
REWRITE_TAC[divides] THEN STRIP_TAC THEN ASM_REWRITE_TAC[] THEN
ASM_SIMP_TAC[DIV_MULT] THEN REWRITE_TAC[
MULT_AC]; ALL_TAC] THEN
UNDISCH_TAC `(a
EXP m == 1) (mod n)` THEN
ASM_SIMP_TAC[
CONG] THEN
DISCH_THEN(MP_TAC o C AP_THM `r DIV p` o AP_TERM `(
EXP)`) THEN
DISCH_THEN(MP_TAC o C AP_THM `n:num` o AP_TERM `(MOD)`) THEN
ASM_SIMP_TAC[
MOD_EXP_MOD] THEN
REWRITE_TAC[
EXP_EXP;
EXP_ONE]);;
(* ------------------------------------------------------------------------- *)
(* Definition of the order of a number mod n (always 0 in non-coprime case). *)
(* ------------------------------------------------------------------------- *)
let ORDER_DIVIDES = prove
(`!n a d. (a
EXP d == 1) (mod n) <=> order(n) a divides d`,
GEN_TAC THEN GEN_TAC THEN REWRITE_TAC[order] THEN CONV_TAC SELECT_CONV THEN
MP_TAC(ISPECL [`\x y:num. (x == y) (mod n)`; `\x:num. a * x`; `1`]
ORDER_EXISTENCE_ITER) THEN
REWRITE_TAC[GSYM
EXP_ITER] THEN DISCH_THEN MATCH_MP_TAC THEN
NUMBER_TAC);;
let ORDER_WORKS = prove
(`!n a. (a
EXP (order(n) a) == 1) (mod n) /\
!m. 0 < m /\ m < order(n) a ==> ~((a
EXP m == 1) (mod n))`,
let ORDER_EQ_0 = prove
(`!n a. order(n) a = 0 <=> ~coprime(n,a)`,
REPEAT GEN_TAC THEN EQ_TAC THEN DISCH_TAC THENL
[ONCE_REWRITE_TAC[
COPRIME_SYM] THEN DISCH_TAC THEN
FIRST_ASSUM(MP_TAC o MATCH_MP
FERMAT_LITTLE) THEN
ASM_REWRITE_TAC[
ORDER_DIVIDES;
DIVIDES_ZERO;
PHI_EQ_0] THEN
ASM_MESON_TAC[
COPRIME_0;
ORDER_1; ARITH_RULE `~(1 = 0)`];
MP_TAC(SPECL [`n:num`; `a:num`]
ORDER) THEN
SPEC_TAC(`order n a`,`m:num`) THEN INDUCT_TAC THEN REWRITE_TAC[] THEN
FIRST_ASSUM(MATCH_MP_TAC o MATCH_MP (TAUT
`~p ==> (q ==> p) ==> q ==> r`)) THEN
REWRITE_TAC[
EXP] THEN CONV_TAC NUMBER_RULE]);;
let COPRIME_ORDER = prove
(`!n a. coprime(n,a)
==> order(n) a > 0 /\
(a
EXP (order(n) a) == 1) (mod n) /\
!m. 0 < m /\ m < order(n) a ==> ~((a
EXP m == 1) (mod n))`,
let ORDER_UNIQUE = prove
(`!n a k. 0 < k /\
(a
EXP k == 1) (mod n) /\
(!m. 0 < m /\ m < k ==> ~(a
EXP m == 1) (mod n))
==> order n a = k`,
REPEAT STRIP_TAC THEN
FIRST_X_ASSUM(MP_TAC o SPEC `order n a`) THEN
MP_TAC(ISPECL [`n:num`; `a:num`]
ORDER_WORKS) THEN
DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC (MP_TAC o SPEC `k:num`)) THEN
ASM_REWRITE_TAC[] THEN ASM_CASES_TAC `order n a = 0` THEN
ASM_REWRITE_TAC[] THENL [ALL_TAC; ASM_ARITH_TAC] THEN
FIRST_X_ASSUM(ASSUME_TAC o GEN_REWRITE_RULE I [
ORDER_EQ_0]) THEN
MP_TAC(ISPECL [`n:num`; `a:num`; `k:num`]
COPRIME_REXP) THEN
ASM_SIMP_TAC[
LE_1;
LT] THEN
UNDISCH_TAC `(a
EXP k == 1) (mod n)` THEN CONV_TAC NUMBER_RULE);;
(* ------------------------------------------------------------------------- *)
(* Another trivial primality characterization. *)
(* ------------------------------------------------------------------------- *)
let PRIME_PRIME_FACTOR = prove
(`!n. prime n <=> ~(n = 1) /\ !p. prime p /\ p divides n ==> (p = n)`,
GEN_TAC THEN GEN_REWRITE_TAC LAND_CONV [prime] THEN
ASM_CASES_TAC `n = 1` THEN ASM_REWRITE_TAC[] THEN EQ_TAC THENL
[MESON_TAC[
PRIME_1]; ALL_TAC] THEN
STRIP_TAC THEN X_GEN_TAC `d:num` THEN
ASM_CASES_TAC `d = 1` THEN ASM_REWRITE_TAC[] THEN DISCH_TAC THEN
FIRST_ASSUM(X_CHOOSE_THEN `p:num` STRIP_ASSUME_TAC o
MATCH_MP
PRIME_FACTOR) THEN
ASM_MESON_TAC[
DIVIDES_TRANS;
DIVIDES_ANTISYM]);;
let PRIME_DIVISOR_SQRT = prove
(`!n. prime(n) <=> ~(n = 1) /\ !d. d divides n /\ d
EXP 2 <= n ==> (d = 1)`,
GEN_TAC THEN GEN_REWRITE_TAC LAND_CONV [prime] THEN
ASM_CASES_TAC `n = 1` THEN ASM_SIMP_TAC[
DIVIDES_ONE] THEN
ASM_CASES_TAC `n = 0` THENL
[ASM_REWRITE_TAC[
DIVIDES_0;
LE;
EXP_EQ_0;
ARITH_EQ] THEN
MATCH_MP_TAC(TAUT `~a /\ ~b ==> (a <=> b)`) THEN CONJ_TAC THENL
[DISCH_THEN(MP_TAC o SPEC `2`) THEN REWRITE_TAC[ARITH];
DISCH_THEN(MP_TAC o SPEC `0`) THEN REWRITE_TAC[ARITH]];
ALL_TAC] THEN
EQ_TAC THEN DISCH_TAC THEN X_GEN_TAC `d:num` THEN STRIP_TAC THENL
[ASM_CASES_TAC `d = n:num` THENL
[ALL_TAC; ASM_MESON_TAC[]] THEN
UNDISCH_TAC `d
EXP 2 <= n` THEN ASM_REWRITE_TAC[] THEN
REWRITE_TAC[
EXP_2; ARITH_RULE `~(n * n <= n) <=> n * 1 < n * n`] THEN
ASM_REWRITE_TAC[
LT_MULT_LCANCEL] THEN
MAP_EVERY UNDISCH_TAC [`~(n = 0)`; `~(n = 1)`] THEN ARITH_TAC;
ALL_TAC] THEN
UNDISCH_TAC `d divides n` THEN REWRITE_TAC[divides] THEN
DISCH_THEN(X_CHOOSE_THEN `e:num` SUBST_ALL_TAC) THEN
SUBGOAL_THEN `d
EXP 2 <= d * e \/ e
EXP 2 <= d * e` MP_TAC THENL
[REWRITE_TAC[
EXP_2;
LE_MULT_LCANCEL;
LE_MULT_RCANCEL] THEN ARITH_TAC;
ALL_TAC] THEN
DISCH_THEN DISJ_CASES_TAC THENL
[FIRST_X_ASSUM(MP_TAC o SPEC `d:num`);
FIRST_X_ASSUM(MP_TAC o SPEC `e:num`)] THEN
ASM_SIMP_TAC[
DIVIDES_RMUL;
DIVIDES_LMUL;
DIVIDES_REFL;
MULT_CLAUSES]);;
let PRIME_PRIME_FACTOR_SQRT = prove
(`!n. prime n <=>
~(n = 0) /\ ~(n = 1) /\ ~(?p. prime p /\ p divides n /\ p
EXP 2 <= n)`,
GEN_TAC THEN ASM_CASES_TAC `n = 1` THEN ASM_REWRITE_TAC[
PRIME_1] THEN
ASM_CASES_TAC `n = 0` THEN ASM_REWRITE_TAC[
PRIME_0] THEN
GEN_REWRITE_TAC LAND_CONV [
PRIME_DIVISOR_SQRT] THEN
EQ_TAC THENL [MESON_TAC[
PRIME_1]; ALL_TAC] THEN
REWRITE_TAC[
NOT_EXISTS_THM] THEN DISCH_TAC THEN
ASM_REWRITE_TAC[] THEN X_GEN_TAC `d:num` THEN STRIP_TAC THEN
ASM_CASES_TAC `d = 1` THEN ASM_REWRITE_TAC[] THEN
FIRST_X_ASSUM(MP_TAC o MATCH_MP
PRIME_FACTOR) THEN
DISCH_THEN(X_CHOOSE_THEN `p:num` STRIP_ASSUME_TAC) THEN
FIRST_X_ASSUM(MP_TAC o SPEC `p:num`) THEN ASM_REWRITE_TAC[] THEN
CONJ_TAC THENL [ASM_MESON_TAC[
DIVIDES_TRANS]; ALL_TAC] THEN
MATCH_MP_TAC
LE_TRANS THEN EXISTS_TAC `d
EXP 2` THEN ASM_REWRITE_TAC[] THEN
REWRITE_TAC[num_CONV `2`;
EXP_MONO_LE_SUC] THEN
ASM_MESON_TAC[
DIVIDES_LE;
DIVIDES_ZERO]);;
(* ------------------------------------------------------------------------- *)
(* Pocklington theorem. *)
(* ------------------------------------------------------------------------- *)
let POCKLINGTON_LEMMA = prove
(`!a n q r.
2 <= n /\ (n - 1 = q * r) /\
(a
EXP (n - 1) == 1) (mod n) /\
(!p. prime(p) /\ p divides q
==> coprime(a
EXP ((n - 1) DIV p) - 1,n))
==> !p. prime p /\ p divides n ==> (p == 1) (mod q)`,
REPEAT STRIP_TAC THEN
SUBGOAL_THEN `order p (a
EXP r) = q` ASSUME_TAC THENL
[ALL_TAC;
SUBGOAL_THEN `coprime(a
EXP r,p)` (MP_TAC o MATCH_MP
FERMAT_LITTLE) THENL
[ALL_TAC;
ASM_REWRITE_TAC[
ORDER_DIVIDES] THEN
SUBGOAL_THEN `phi p = p - 1` SUBST1_TAC THENL
[ASM_MESON_TAC[
PHI_PRIME_EQ]; ALL_TAC] THEN
REWRITE_TAC[divides;
LEFT_IMP_EXISTS_THM] THEN X_GEN_TAC `d:num` THEN
DISCH_THEN(MP_TAC o MATCH_MP (ARITH_RULE
`(p - 1 = q * d) ==> ~(p = 0) ==> (p + q * 0 = 1 + q * d)`)) THEN
REWRITE_TAC[
nat_mod; cong] THEN ASM_MESON_TAC[
PRIME_0]] THEN
ONCE_REWRITE_TAC[
COPRIME_SYM] THEN MATCH_MP_TAC
COPRIME_EXP THEN
UNDISCH_TAC `(a
EXP (n - 1) == 1) (mod n)` THEN
ONCE_REWRITE_TAC[GSYM CONTRAPOS_THM] THEN
REWRITE_TAC[coprime;
NOT_FORALL_THM; NOT_IMP] THEN
DISCH_THEN(X_CHOOSE_THEN `d:num` STRIP_ASSUME_TAC) THEN
SUBGOAL_THEN `d = p:num` SUBST_ALL_TAC THENL
[ASM_MESON_TAC[prime]; ALL_TAC] THEN
SUBGOAL_THEN `p divides (a
EXP (n - 1))` ASSUME_TAC THENL
[FIRST_ASSUM(SUBST1_TAC o MATCH_MP (ARITH_RULE
`2 <= n ==> (n - 1 = SUC(n - 2))`)) THEN
REWRITE_TAC[
EXP] THEN ASM_SIMP_TAC[
DIVIDES_RMUL];
ALL_TAC] THEN
REWRITE_TAC[cong;
nat_mod] THEN
SUBGOAL_THEN `~(p divides 1)` MP_TAC THENL
[ASM_MESON_TAC[
DIVIDES_ONE;
PRIME_1]; ALL_TAC] THEN
ASM_MESON_TAC[
DIVIDES_RMUL;
DIVIDES_ADD;
DIVIDES_ADD_REVL]] THEN
SUBGOAL_THEN `(order p (a
EXP r)) divides q` MP_TAC THENL
[REWRITE_TAC[GSYM
ORDER_DIVIDES;
EXP_EXP] THEN
ONCE_REWRITE_TAC[
MULT_SYM] THEN
UNDISCH_TAC `(a
EXP (n - 1) == 1) (mod n)` THEN
ASM_REWRITE_TAC[] THEN
UNDISCH_TAC `p divides n` THEN REWRITE_TAC[divides] THEN
DISCH_THEN(X_CHOOSE_THEN `b:num` SUBST_ALL_TAC) THEN
REWRITE_TAC[cong;
nat_mod] THEN MESON_TAC[
MULT_AC];
ALL_TAC] THEN
REWRITE_TAC[divides;
LEFT_IMP_EXISTS_THM] THEN X_GEN_TAC `d:num` THEN
ASM_CASES_TAC `d = 1` THEN ASM_SIMP_TAC[
MULT_CLAUSES] THEN
DISCH_THEN(ASSUME_TAC o SYM) THEN
FIRST_ASSUM(MP_TAC o MATCH_MP
PRIME_FACTOR) THEN
DISCH_THEN(X_CHOOSE_THEN `P:num` STRIP_ASSUME_TAC) THEN
SUBGOAL_THEN `P divides q` ASSUME_TAC THENL
[ASM_MESON_TAC[
DIVIDES_LMUL]; ALL_TAC] THEN
FIRST_X_ASSUM(MP_TAC o SPEC `P:num`) THEN ASM_REWRITE_TAC[] THEN
MATCH_MP_TAC(TAUT `~a ==> a ==> b`) THEN
UNDISCH_TAC `P divides q` THEN REWRITE_TAC[divides] THEN
DISCH_THEN(X_CHOOSE_THEN `s:num` SUBST_ALL_TAC) THEN
REWRITE_TAC[GSYM
MULT_ASSOC] THEN
SUBGOAL_THEN `~(P = 0)` ASSUME_TAC THENL
[ASM_MESON_TAC[
PRIME_0]; ALL_TAC] THEN
ASM_SIMP_TAC[DIV_MULT] THEN
UNDISCH_TAC `P divides d` THEN REWRITE_TAC[divides] THEN
DISCH_THEN(X_CHOOSE_THEN `t:num` SUBST_ALL_TAC) THEN
UNDISCH_TAC `order p (a
EXP r) * P * t = P * s` THEN
ONCE_REWRITE_TAC[ARITH_RULE
`(a * p * b = p * c) <=> (p * a * b = p * c)`] THEN
REWRITE_TAC[
EQ_MULT_LCANCEL] THEN ASM_REWRITE_TAC[] THEN
DISCH_THEN(SUBST_ALL_TAC o SYM) THEN REWRITE_TAC[coprime] THEN
DISCH_THEN(MP_TAC o SPEC `p:num`) THEN REWRITE_TAC[NOT_IMP] THEN
CONJ_TAC THENL [ALL_TAC; ASM_MESON_TAC[
PRIME_1]] THEN
ASM_REWRITE_TAC[] THEN
ONCE_REWRITE_TAC[AC
MULT_AC `(d * t) * r = r * d * t`] THEN
REWRITE_TAC[
EXP_MULT] THEN
MATCH_MP_TAC
CONG_1_DIVIDES THEN
MATCH_MP_TAC
CONG_TRANS THEN EXISTS_TAC `1
EXP t` THEN
SIMP_TAC[
CONG_EXP;
ORDER] THEN REWRITE_TAC[
EXP_ONE;
CONG_REFL]);;
let POCKLINGTON = prove
(`!a n q r.
2 <= n /\ (n - 1 = q * r) /\ n <= q
EXP 2 /\
(a
EXP (n - 1) == 1) (mod n) /\
(!p. prime(p) /\ p divides q
==> coprime(a
EXP ((n - 1) DIV p) - 1,n))
==> prime(n)`,
REPEAT STRIP_TAC THEN ONCE_REWRITE_TAC[
PRIME_PRIME_FACTOR_SQRT] THEN
ASM_SIMP_TAC[ARITH_RULE `2 <= n ==> ~(n = 0) /\ ~(n = 1)`] THEN
DISCH_THEN(X_CHOOSE_THEN `p:num` STRIP_ASSUME_TAC) THEN
MP_TAC(SPECL [`a:num`; `n:num`; `q:num`; `r:num`]
POCKLINGTON_LEMMA) THEN
ASM_REWRITE_TAC[] THEN DISCH_THEN(MP_TAC o SPEC `p:num`) THEN
ASM_REWRITE_TAC[] THEN
SUBGOAL_THEN `p
EXP 2 <= q
EXP 2` MP_TAC THENL
[ASM_MESON_TAC[
LE_TRANS]; ALL_TAC] THEN
REWRITE_TAC[num_CONV `2`;
EXP_MONO_LE_SUC] THEN
ONCE_REWRITE_TAC[GSYM CONTRAPOS_THM] THEN REWRITE_TAC[] THEN
DISCH_THEN(MP_TAC o MATCH_MP
CONG_1_DIVIDES) THEN
DISCH_THEN(MP_TAC o MATCH_MP
DIVIDES_LE) THEN
FIRST_ASSUM(MP_TAC o MATCH_MP
PRIME_GE_2) THEN ARITH_TAC);;
(* ------------------------------------------------------------------------- *)
(* Variant for application, to separate the exponentiation. *)
(* ------------------------------------------------------------------------- *)
let POCKLINGTON_ALT = prove
(`!a n q r.
2 <= n /\ (n - 1 = q * r) /\ n <= q
EXP 2 /\
(a
EXP (n - 1) == 1) (mod n) /\
(!p. prime(p) /\ p divides q
==> ?b. (a
EXP ((n - 1) DIV p) == b) (mod n) /\
coprime(b - 1,n))
==> prime(n)`,
REPEAT STRIP_TAC THEN MATCH_MP_TAC
POCKLINGTON THEN
MAP_EVERY EXISTS_TAC [`a:num`; `q:num`; `r:num`] THEN
ASM_REWRITE_TAC[] THEN
X_GEN_TAC `p:num` THEN STRIP_TAC THEN
FIRST_X_ASSUM(MP_TAC o SPEC `p:num`) THEN ASM_REWRITE_TAC[] THEN
DISCH_THEN(X_CHOOSE_THEN `b:num` STRIP_ASSUME_TAC) THEN
SUBGOAL_THEN `(a
EXP ((q * r) DIV p) - 1 == b - 1) (mod n)`
(fun th -> ASM_MESON_TAC[
CONG_COPRIME;
COPRIME_SYM; th]) THEN
MATCH_MP_TAC
CONG_SUB THEN ASM_REWRITE_TAC[
CONG_REFL] THEN
REWRITE_TAC[ARITH_RULE `1 <= n <=> ~(n = 0)`;
EXP_EQ_0] THEN
SUBGOAL_THEN `~(a = 0)` ASSUME_TAC THENL
[DISCH_TAC THEN UNDISCH_TAC `(a
EXP (n - 1) == 1) (mod n)` THEN
SIMP_TAC[ARITH_RULE `2 <= n ==> (n - 1 = SUC(n - 2))`;
ASSUME `a = 0`; ASSUME `2 <= n`] THEN
REWRITE_TAC[
MULT_CLAUSES;
EXP] THEN
ONCE_REWRITE_TAC[
CONG_SYM] THEN
REWRITE_TAC[
CONG_0_DIVIDES;
DIVIDES_ONE] THEN
UNDISCH_TAC `2 <= n` THEN ARITH_TAC;
ALL_TAC] THEN
ASM_REWRITE_TAC[] THEN
UNDISCH_TAC `(a
EXP ((q * r) DIV p) == b) (mod n)` THEN
ONCE_REWRITE_TAC[GSYM CONTRAPOS_THM] THEN REWRITE_TAC[] THEN
DISCH_THEN SUBST1_TAC THEN REWRITE_TAC[
CONG_0_DIVIDES] THEN
SUBGOAL_THEN `~(n divides (a
EXP (n - 1)))` MP_TAC THENL
[ASM_MESON_TAC[
CONG_DIVIDES;
DIVIDES_ONE; ARITH_RULE `~(2 <= 1)`];
ALL_TAC] THEN
ASM_REWRITE_TAC[CONTRAPOS_THM] THEN UNDISCH_TAC `p divides q` THEN
GEN_REWRITE_TAC LAND_CONV [divides] THEN
DISCH_THEN(X_CHOOSE_THEN `d:num` SUBST1_TAC) THEN
REWRITE_TAC[GSYM
MULT_ASSOC] THEN
SUBGOAL_THEN `~(p = 0)` ASSUME_TAC THENL
[ASM_MESON_TAC[
PRIME_0]; ALL_TAC] THEN
ASM_SIMP_TAC[DIV_MULT] THEN
GEN_REWRITE_TAC (RAND_CONV o ONCE_DEPTH_CONV) [
MULT_SYM] THEN
GEN_REWRITE_TAC (RAND_CONV o ONCE_DEPTH_CONV) [
EXP_MULT] THEN
SUBGOAL_THEN `p = SUC(p - 1)` SUBST1_TAC THENL
[UNDISCH_TAC `~(p = 0)` THEN ARITH_TAC; ALL_TAC] THEN
REWRITE_TAC[
EXP;
DIVIDES_RMUL]);;
(* ------------------------------------------------------------------------- *)
(* Prime factorizations. *)
(* ------------------------------------------------------------------------- *)
let primefact = new_definition
`primefact ps n <=> (ITLIST (*) ps 1 = n) /\ !p. MEM p ps ==> prime(p)`;;
let PRIMEFACT = prove
(`!n. ~(n = 0) ==> ?ps. primefact ps n`,
MATCH_MP_TAC num_WF THEN X_GEN_TAC `n:num` THEN
ASM_CASES_TAC `n = 1` THEN ASM_REWRITE_TAC[] THENL
[REPEAT DISCH_TAC THEN EXISTS_TAC `[]:num list` THEN
REWRITE_TAC[primefact; ITLIST; MEM]; ALL_TAC] THEN
DISCH_TAC THEN DISCH_TAC THEN
FIRST_ASSUM(X_CHOOSE_THEN `p:num` STRIP_ASSUME_TAC o
MATCH_MP PRIME_FACTOR) THEN
UNDISCH_TAC `p divides n` THEN REWRITE_TAC[divides] THEN
DISCH_THEN(X_CHOOSE_THEN `m:num` SUBST_ALL_TAC) THEN
FIRST_X_ASSUM(MP_TAC o SPEC `m:num`) THEN
UNDISCH_TAC `~(p * m = 0)` THEN
ASM_CASES_TAC `m = 0` THEN ASM_REWRITE_TAC[MULT_CLAUSES] THEN DISCH_TAC THEN
GEN_REWRITE_TAC (funpow 3 LAND_CONV) [ARITH_RULE `n = 1 * n`] THEN
ASM_REWRITE_TAC[LT_MULT_RCANCEL] THEN
SUBGOAL_THEN `1 < p` (fun th -> REWRITE_TAC[th]) THENL
[MATCH_MP_TAC(ARITH_RULE `~(p = 0) /\ ~(p = 1) ==> 1 < p`) THEN
REPEAT STRIP_TAC THEN UNDISCH_TAC `prime p` THEN
ASM_REWRITE_TAC[PRIME_0; PRIME_1]; ALL_TAC] THEN
REWRITE_TAC[primefact] THEN
DISCH_THEN(X_CHOOSE_THEN `ps:num list` ASSUME_TAC) THEN
EXISTS_TAC `CONS (p:num) ps` THEN
ASM_REWRITE_TAC[MEM; ITLIST] THEN ASM_MESON_TAC[]);;
let PRIMAFACT_CONTAINS = prove
(`!ps n. primefact ps n ==> !p. prime p /\ p divides n ==> MEM p ps`,
REPEAT GEN_TAC THEN REWRITE_TAC[primefact] THEN
DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC) THEN
POP_ASSUM(SUBST1_TAC o SYM) THEN
SPEC_TAC(`ps:num list`,`ps:num list`) THEN LIST_INDUCT_TAC THEN
REWRITE_TAC[ITLIST; MEM] THENL
[ASM_MESON_TAC[DIVIDES_ONE; PRIME_1]; ALL_TAC] THEN
STRIP_TAC THEN GEN_TAC THEN
DISCH_THEN(fun th -> ASSUME_TAC(CONJUNCT1 th) THEN MP_TAC th) THEN
DISCH_THEN(DISJ_CASES_TAC o MATCH_MP PRIME_DIVPROD) THEN
ASM_MESON_TAC[prime; PRIME_1]);;
let PRIMEFACT_VARIANT = prove
(`!ps n. primefact ps n <=> (ITLIST (*) ps 1 = n) /\ ALL prime ps`,
REPEAT GEN_TAC THEN REWRITE_TAC[primefact] THEN AP_TERM_TAC THEN
SPEC_TAC(`ps:num list`,`ps:num list`) THEN LIST_INDUCT_TAC THEN
ASM_REWRITE_TAC[MEM; ALL] THEN ASM_MESON_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* Variant of Lucas theorem. *)
(* ------------------------------------------------------------------------- *)
let LUCAS_PRIMEFACT = prove
(`2 <= n /\
(a EXP (n - 1) == 1) (mod n) /\
(ITLIST (*) ps 1 = n - 1) /\
ALL (\p. prime p /\ ~(a EXP ((n - 1) DIV p) == 1) (mod n)) ps
==> prime n`,
REPEAT STRIP_TAC THEN MATCH_MP_TAC LUCAS THEN
EXISTS_TAC `a:num` THEN ASM_REWRITE_TAC[] THEN
SUBGOAL_THEN `primefact ps (n - 1)` MP_TAC THENL
[ASM_REWRITE_TAC[PRIMEFACT_VARIANT] THEN MATCH_MP_TAC ALL_IMP THEN
EXISTS_TAC `\p. prime p /\ ~(a EXP ((n - 1) DIV p) == 1) (mod n)` THEN
ASM_SIMP_TAC[]; ALL_TAC] THEN
DISCH_THEN(ASSUME_TAC o MATCH_MP PRIMAFACT_CONTAINS) THEN
X_GEN_TAC `p:num` THEN DISCH_THEN(ANTE_RES_THEN MP_TAC) THEN UNDISCH_TAC
`ALL (\p. prime p /\ ~(a EXP ((n - 1) DIV p) == 1) (mod n)) ps` THEN
SPEC_TAC(`ps:num list`,`ps:num list`) THEN LIST_INDUCT_TAC THEN
SIMP_TAC[ALL; MEM] THEN ASM_MESON_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* Variant of Pocklington theorem. *)
(* ------------------------------------------------------------------------- *)
let POCKLINGTON_PRIMEFACT = prove
(`2 <= n /\ (q * r = n - 1) /\ n <= q * q
==> ((a EXP r) MOD n = b)
==> (ITLIST (*) ps 1 = q)
==> ((b EXP q) MOD n = 1)
==> ALL (\p. prime p /\
coprime((b EXP (q DIV p)) MOD n - 1,n)) ps
==> prime n`,
DISCH_THEN(fun th -> DISCH_THEN(SUBST1_TAC o SYM) THEN MP_TAC th) THEN
SIMP_TAC[MOD_EXP_MOD; ARITH_RULE `2 <= n ==> ~(n = 0)`] THEN
SIMP_TAC[ONCE_REWRITE_RULE[MULT_SYM] EXP_EXP] THEN
REPEAT STRIP_TAC THEN MATCH_MP_TAC POCKLINGTON THEN
MAP_EVERY EXISTS_TAC [`a:num`; `q:num`; `r:num`] THEN
ASM_REWRITE_TAC[EXP_2] THEN CONJ_TAC THENL
[MP_TAC(SPECL [`a EXP (n - 1)`; `n:num`] DIVISION) THEN
ASM_SIMP_TAC[ARITH_RULE `2 <= n ==> ~(n = 0)`] THEN
STRIP_TAC THEN ABBREV_TAC `Q = a EXP (n - 1) DIV n` THEN
ONCE_ASM_REWRITE_TAC[] THEN REWRITE_TAC[cong; nat_mod] THEN
MAP_EVERY EXISTS_TAC [`0`; `Q:num`] THEN ARITH_TAC;
ALL_TAC] THEN
SUBGOAL_THEN `primefact ps q` MP_TAC THENL
[ASM_REWRITE_TAC[PRIMEFACT_VARIANT] THEN MATCH_MP_TAC ALL_IMP THEN
EXISTS_TAC `\p. prime p /\ coprime(a EXP (q DIV p * r) MOD n - 1,n)` THEN
ASM_SIMP_TAC[]; ALL_TAC] THEN
DISCH_THEN(ASSUME_TAC o MATCH_MP PRIMAFACT_CONTAINS) THEN
X_GEN_TAC `p:num` THEN
DISCH_THEN(fun th -> STRIP_ASSUME_TAC th THEN MP_TAC th) THEN
DISCH_THEN(ANTE_RES_THEN MP_TAC) THEN
RULE_ASSUM_TAC(REWRITE_RULE[GSYM ALL_MEM]) THEN
DISCH_THEN(ANTE_RES_THEN MP_TAC) THEN
MATCH_MP_TAC(TAUT `(a ==> (b <=> c)) ==> a /\ b ==> c`) THEN
DISCH_TAC THEN ONCE_REWRITE_TAC[COPRIME_SYM] THEN
SUBGOAL_THEN `~(p = 0)` ASSUME_TAC THENL
[ASM_MESON_TAC[PRIME_0]; ALL_TAC] THEN
SUBGOAL_THEN `q DIV p * r = (n - 1) DIV p` SUBST1_TAC THENL
[UNDISCH_TAC `p divides q` THEN REWRITE_TAC[divides] THEN
DISCH_THEN(X_CHOOSE_THEN `d:num` SUBST_ALL_TAC) THEN
UNDISCH_THEN `(p * d) * r = n - 1` (SUBST1_TAC o SYM) THEN
ASM_SIMP_TAC[DIV_MULT; GSYM MULT_ASSOC];
ALL_TAC] THEN
MATCH_MP_TAC CONG_COPRIME THEN MATCH_MP_TAC CONG_SUB THEN
ASM_SIMP_TAC[CONG_MOD; ARITH_RULE `2 <= n ==> ~(n = 0)`; CONG_REFL] THEN
MATCH_MP_TAC(ARITH_RULE `a <= b /\ ~(a = 0) ==> 1 <= a /\ 1 <= b`) THEN
ASM_SIMP_TAC[MOD_LE; ARITH_RULE `2 <= n ==> ~(n = 0)`] THEN
ASM_SIMP_TAC[MOD_EQ_0; ARITH_RULE `2 <= n ==> ~(n = 0)`] THEN
DISCH_THEN(X_CHOOSE_THEN `s:num` MP_TAC) THEN
DISCH_THEN(MP_TAC o C AP_THM `p:num` o AP_TERM `(EXP)`) THEN
REWRITE_TAC[EXP_EXP] THEN
SUBGOAL_THEN `(n - 1) DIV p * p = n - 1` SUBST1_TAC THENL
[SUBST1_TAC(SYM(ASSUME `q * r = n - 1`)) THEN
UNDISCH_TAC `p divides q` THEN REWRITE_TAC[divides] THEN
DISCH_THEN(X_CHOOSE_THEN `d:num` SUBST1_TAC) THEN
REWRITE_TAC[GSYM MULT_ASSOC] THEN
ASM_MESON_TAC[DIV_MULT; MULT_AC; PRIME_0];
ALL_TAC] THEN
DISCH_THEN(MP_TAC o C AP_THM `n:num` o AP_TERM `(MOD)`) THEN
ASM_REWRITE_TAC[] THEN
FIRST_ASSUM(SUBST1_TAC o MATCH_MP (ARITH_RULE
`~(p = 0) ==> (p = SUC(p - 1))`)) THEN
ONCE_REWRITE_TAC[MULT_SYM] THEN REWRITE_TAC[EXP; GSYM MULT_ASSOC] THEN
ASM_SIMP_TAC[MOD_MULT; ARITH_RULE `2 <= n ==> ~(n = 0)`] THEN
REWRITE_TAC[ARITH_EQ]);;
(* ------------------------------------------------------------------------- *)
(* Utility functions. *)
(* ------------------------------------------------------------------------- *)
let even_num n =
mod_num n num_2 =/ num_0;;
let odd_num = not o even_num;;
(* ------------------------------------------------------------------------- *)
(* Least p >= 0 with x <= 2^p. *)
(* ------------------------------------------------------------------------- *)
let log2 =
let rec log2 x y =
if x </ num_1 then y
else log2 (quo_num x num_2) (y +/ num_1) in
fun x -> log2 (x -/ num_1) num_0;;
(* ------------------------------------------------------------------------- *)
(* Raise number to power (x^m) modulo n. *)
(* ------------------------------------------------------------------------- *)
let rec powermod x m n =
if m =/ num_0 then num_1 else
let y = powermod x (quo_num m num_2) n in
let z = mod_num (y */ y) n in
if even_num m then z else
mod_num (x */ z) n;;
(* ------------------------------------------------------------------------- *)
(* Make a call to PARI/GP to factor a number into (probable) primes. *)
(* ------------------------------------------------------------------------- *)
let factor =
let suck_file s = let data = string_of_file s in Sys.remove s; data in
let extract_output s =
let l0 = explode s in
let l0' = rev l0 in
let l1 = snd(chop_list(index "]" l0') l0') in
let l2 = "["::rev(fst(chop_list(index "[" l1) l1)) in
let tm = parse_term (implode l2) in
map ((dest_numeral F_F dest_numeral) o dest_pair) (dest_list tm) in
fun n ->
if n =/ num_1 then [] else
let filename = Filename.temp_file "pocklington" ".out" in
let s = "echo 'print(factorint(" ^
(string_of_num n) ^
")) \n quit' | gp >" ^ filename ^ " 2>/dev/null" in
if Sys.command s = 0 then
let output = suck_file filename in
extract_output output
else
failwith "factor: Call to GP/PARI failed";;
(* ------------------------------------------------------------------------- *)
(* Alternative giving multiset instead of set plus indices. *)
(* Also just use a stupid algorithm for small enough numbers or if PARI/GP *)
(* is not installed. I should really write a better factoring algorithm. *)
(* ------------------------------------------------------------------------- *)
let PARI_THRESHOLD = pow2 25;;
let multifactor =
let rec findfactor m n =
if mod_num n m =/ num_0 then m
else if m */ m >/ n then n
else findfactor (m +/ num_1) n in
let rec stupidfactor n =
let p = findfactor num_2 n in
if p =/ n then [n] else p::(stupidfactor(quo_num n p)) in
let rec multilist l =
if l = [] then [] else
let (x,n) = hd l in
replicate x (Num.int_of_num n) @ multilist (tl l) in
fun n -> try if n </ PARI_THRESHOLD then failwith ""
else multilist (factor n)
with Failure _ -> sort (</) (stupidfactor n);;
(* ------------------------------------------------------------------------- *)
(* Recursive creation of Pratt primality certificates. *)
(* ------------------------------------------------------------------------- *)
type certificate =
Prime_2
| Primroot_and_factors of
((num * num list) * num * (num * certificate) list);;
let find_primitive_root =
let rec find_primitive_root a m ms n =
if gcd_num a n =/ num_1 &
powermod a m n =/ num_1 &
forall (fun k -> powermod a k n <>/ num_1) ms
then a
else find_primitive_root (a +/ num_1) m ms n in
let find_primitive_root_from_2 = find_primitive_root num_2 in
fun m ms n ->
if n </ num_2 then failwith "find_primitive_root: input too small"
else find_primitive_root_from_2 m ms n;;
let uniq_num =
let rec uniq x l =
match l with
[] -> raise Unchanged
| (h::t) -> if x =/ h then
try uniq x t
with Unchanged -> l
else x::(uniq h t) in
fun l -> if l = [] then [] else uniq (hd l) (tl l);;
let setify_num s =
let s' = sort (<=/) s in
try uniq_num s' with Unchanged -> s';;
let certify_prime =
let rec cert_prime n =
if n <=/ num_2 then
if n =/ num_2 then Prime_2
else failwith "certify_prime: not a prime!"
else
let m = n -/ num_1 in
let pfact = multifactor m in
let primes = setify_num pfact in
let ms = map (fun d -> div_num m d) primes in
let a = find_primitive_root m ms n in
Primroot_and_factors((n,pfact),a,map (fun n -> n,cert_prime n) primes) in
fun n -> if length(multifactor n) = 1 then cert_prime n
else failwith "certify_prime: input is not a prime";;
(* ------------------------------------------------------------------------- *)
(* Relatively efficient evaluation of "(a EXP k) MOD n". *)
(* ------------------------------------------------------------------------- *)
let EXP_MOD_CONV =
let pth = prove
(`~(n = 0)
==> ((a
EXP 0) MOD n = 1 MOD n) /\
((a
EXP (
NUMERAL (
BIT0 m))) MOD n =
let b = (a
EXP (
NUMERAL m)) MOD n in
(b * b) MOD n) /\
((a
EXP (
NUMERAL (
BIT1 m))) MOD n =
let b = (a
EXP (
NUMERAL m)) MOD n in
(a * ((b * b) MOD n)) MOD n)`,
DISCH_TAC THEN REWRITE_TAC[
EXP] THEN
REWRITE_TAC[
NUMERAL;
BIT0;
BIT1] THEN
REWRITE_TAC[
EXP;
EXP_ADD] THEN
CONV_TAC(ONCE_DEPTH_CONV let_CONV) THEN
ASM_SIMP_TAC[
MOD_MULT_LMOD;
MOD_MULT_RMOD] THEN
REWRITE_TAC[
MULT_ASSOC] THEN
ASM_SIMP_TAC[
MOD_MULT_LMOD;
MOD_MULT_RMOD] THEN
ONCE_REWRITE_TAC[
MULT_SYM] THEN
REWRITE_TAC[
MULT_ASSOC] THEN
ASM_SIMP_TAC[
MOD_MULT_LMOD;
MOD_MULT_RMOD])
and pth_cong = SPEC_ALL
CONG
and n_tm = `n:num` in
fun tm ->
let ntm = rand tm in
let th1 = INST [ntm,n_tm] pth in
let th2 = EQF_ELIM(NUM_EQ_CONV(rand(lhand(concl th1)))) in
let th_base,th_steps = CONJ_PAIR(MP th1 th2) in
let conv_base = GEN_REWRITE_CONV I [th_base]
and conv_step = GEN_REWRITE_CONV I [th_steps] in
let rec conv tm =
try conv_base tm with Failure _ ->
(conv_step THENC
RAND_CONV conv THENC
let_CONV THENC
NUM_REDUCE_CONV) tm in
conv tm;;
(* ------------------------------------------------------------------------- *)
(* HOL checking of primality certificate, using Pocklington shortcut. *)
(* ------------------------------------------------------------------------- *)
let prime_theorem_cache = ref [];;
let rec lookup_under_num n l =
if l = [] then failwith "lookup_under_num" else
let h = hd l in
if fst h =/ n then snd h
else lookup_under_num n (tl l);;
let rec split_factors q qs ps n =
if q */ q >=/ n then rev qs,ps
else split_factors (q */ hd ps) (hd ps :: qs) (tl ps) n;;
let check_certificate =
let n_tm = `n:num`
and a_tm = `a:num`
and q_tm = `q:num`
and r_tm = `r:num`
and b_tm = `b:num`
and ps_tm = `ps:num list`
and conv_itlist =
GEN_REWRITE_CONV TOP_DEPTH_CONV [ITLIST] THENC NUM_REDUCE_CONV
and conv_all =
GEN_REWRITE_CONV TOP_DEPTH_CONV
[ALL; BETA_THM; TAUT `a /\ T <=> a`] THENC
GEN_REWRITE_CONV DEPTH_CONV
[TAUT `(a /\ a /\ b <=> a /\ b) /\ (a /\ a <=> a)`]
and subarith_conv =
let gconv_net = itlist (uncurry net_of_conv)
[`a - b`,NUM_SUB_CONV;
`a DIV b`,NUM_DIV_CONV;
`(a EXP b) MOD c`,EXP_MOD_CONV;
`coprime(a,b)`,COPRIME_CONV;
`p /\ T`,REWR_CONV(TAUT `p /\ T <=> p`);
`T /\ p`,REWR_CONV(TAUT `T /\ p <=> p`)]
empty_net in
DEPTH_CONV(REWRITES_CONV gconv_net) in
let rec check_certificate cert =
match cert with
Prime_2 ->
PRIME_2
| Primroot_and_factors((n,ps),a,ncerts) ->
try lookup_under_num n (!prime_theorem_cache) with Failure _ ->
let qs,rs = split_factors num_1 [] (rev ps) n in
let q = itlist ( */ ) qs num_1
and r = itlist ( */ ) rs num_1 in
let th1 = INST [mk_numeral n,n_tm;
mk_flist (map mk_numeral qs),ps_tm;
mk_numeral q,q_tm;
mk_numeral r,r_tm;
mk_numeral a,a_tm]
POCKLINGTON_PRIMEFACT in
let th2 = MP th1 (EQT_ELIM(NUM_REDUCE_CONV(lhand(concl th1)))) in
let tha = EXP_MOD_CONV(lhand(lhand(concl th2))) in
let thb = MP (INST [rand(concl tha),b_tm] th2) tha in
let th3 = MP thb (EQT_ELIM(conv_itlist (lhand(concl thb)))) in
let th4 = MP th3 (EXP_MOD_CONV (lhand(lhand(concl th3)))) in
let th5 = conv_all(lhand(concl th4)) in
let th6 = TRANS th5 (subarith_conv(rand(concl th5))) in
let th7 = IMP_TRANS (snd(EQ_IMP_RULE th6)) th4 in
let ants = conjuncts(lhand(concl th7)) in
let certs =
map (fun t -> lookup_under_num (dest_numeral(rand t)) ncerts)
ants in
let ths = map check_certificate certs in
let fth = MP th7 (end_itlist CONJ ths) in
prime_theorem_cache := (n,fth)::(!prime_theorem_cache); fth in
check_certificate;;
(* ------------------------------------------------------------------------- *)
(* Hence a primality-proving rule. *)
(* ------------------------------------------------------------------------- *)
let PROVE_PRIME = check_certificate o certify_prime;;
(* ------------------------------------------------------------------------- *)
(* Rule to generate prime factorization theorems. *)
(* ------------------------------------------------------------------------- *)
let PROVE_PRIMEFACT =
let pth = SPEC_ALL PRIMEFACT_VARIANT
and start_CONV = PURE_REWRITE_CONV[ITLIST; ALL] THENC NUM_REDUCE_CONV
and ps_tm = `ps:num list`
and n_tm = `n:num` in
fun n ->
let pfact = multifactor n in
let th1 = INST [mk_flist(map mk_numeral pfact),ps_tm;
mk_numeral n,n_tm] pth in
let th2 = TRANS th1 (start_CONV(rand(concl th1))) in
let ths = map PROVE_PRIME pfact in
EQ_MP (SYM th2) (end_itlist CONJ ths);;
(* ------------------------------------------------------------------------- *)
(* Conversion for truth or falsity of primality assertion. *)
(* ------------------------------------------------------------------------- *)
let PRIME_TEST =
let NOT_PRIME_THM = prove
(`((m = 1) <=> F) ==> ((m = p) <=> F) ==> (m * n = p) ==> (prime(p) <=> F)`,
MESON_TAC[prime; divides])
and m_tm = `m:num` and n_tm = `n:num` and p_tm = `p:num` in
fun tm ->
let p = dest_numeral tm in
if p =/ num_0 then EQF_INTRO
PRIME_0
else if p =/ num_1 then EQF_INTRO
PRIME_1 else
let pfact = multifactor p in
if length pfact = 1 then
(remark ("proving that " ^ string_of_num p ^ " is prime");
EQT_INTRO(PROVE_PRIME p))
else
(remark ("proving that " ^ string_of_num p ^ " is composite");
let m = hd pfact and n = end_itlist ( */ ) (tl pfact) in
let th0 = INST [mk_numeral m,m_tm; mk_numeral n,n_tm; mk_numeral p,p_tm]
NOT_PRIME_THM in
let th1 = MP th0 (NUM_EQ_CONV (lhand(lhand(concl th0)))) in
let th2 = MP th1 (NUM_EQ_CONV (lhand(lhand(concl th1)))) in
MP th2 (NUM_MULT_CONV(lhand(lhand(concl th2)))));;
let PRIME_CONV =
let prime_tm = `prime` in
fun tm0 ->
let ptm,tm = dest_comb tm0 in
if ptm <> prime_tm then failwith "expected term of form prime(n)"
else PRIME_TEST tm;;
(* ------------------------------------------------------------------------- *)
(* Another lemma. *)
(* ------------------------------------------------------------------------- *)
let PRIME_POWER_EXISTS = prove
(`!q. prime q
==> ((?i. n = q
EXP i) <=>
(!p. prime p /\ p divides n ==> p = q))`,
REPEAT STRIP_TAC THEN EQ_TAC THEN STRIP_TAC THEN
ASM_SIMP_TAC[
IMP_CONJ;
PRIME_DIVEXP_EQ;
DIVIDES_PRIME_PRIME] THEN
ASM_CASES_TAC `n = 0` THENL
[FIRST_X_ASSUM(fun th -> MP_TAC(SPEC `2` th) THEN MP_TAC(SPEC `3` th)) THEN
ASM_REWRITE_TAC[
PRIME_2; PRIME_CONV `prime 3`;
DIVIDES_0] THEN ARITH_TAC;
ALL_TAC] THEN
ASM_CASES_TAC `n = 1` THENL
[EXISTS_TAC `0` THEN ASM_REWRITE_TAC[
EXP]; ALL_TAC] THEN
MP_TAC(ISPEC `n:num`
PRIMEPOW_FACTOR) THEN
ANTS_TAC THENL [ASM_ARITH_TAC; ALL_TAC] THEN
DISCH_THEN(X_CHOOSE_THEN `p:num` MP_TAC) THEN
ASM_CASES_TAC `p:num = q` THENL
[FIRST_X_ASSUM(SUBST_ALL_TAC o SYM);
ASM_MESON_TAC[
DIVIDES_REXP;
LE_1;
DIVIDES_RMUL;
DIVIDES_REFL]] THEN
MATCH_MP_TAC
MONO_EXISTS THEN X_GEN_TAC `i:num` THEN
DISCH_THEN(X_CHOOSE_THEN `m:num` STRIP_ASSUME_TAC) THEN
FIRST_X_ASSUM SUBST_ALL_TAC THEN
MATCH_MP_TAC(NUM_RING `m = 1 ==> x * m = x`) THEN
MATCH_MP_TAC(ARITH_RULE `~(m = 0) /\ ~(2 <= m) ==> m = 1`) THEN
CONJ_TAC THENL [ASM_MESON_TAC[
COPRIME_0;
PRIME_1]; ALL_TAC] THEN
DISCH_THEN(MP_TAC o MATCH_MP
PRIMEPOW_FACTOR) THEN
DISCH_THEN(X_CHOOSE_THEN `r:num` STRIP_ASSUME_TAC) THEN
FIRST_X_ASSUM(MP_TAC o SPEC `r:num`) THEN
REWRITE_TAC[NOT_IMP] THEN CONJ_TAC THENL
[ASM_MESON_TAC[
DIVIDES_LMUL;
DIVIDES_RMUL;
DIVIDES_REXP;
LE_1;
DIVIDES_REFL];
DISCH_THEN SUBST_ALL_TAC THEN FIRST_X_ASSUM SUBST_ALL_TAC THEN
FIRST_X_ASSUM(MP_TAC o GEN_REWRITE_RULE I [
COPRIME_RMUL]) THEN
ASM_SIMP_TAC[
COPRIME_REXP;
LE_1;
COPRIME_REFL] THEN
ASM_MESON_TAC[
PRIME_1]]);;
(* ------------------------------------------------------------------------- *)
(* Example. *)
(* ------------------------------------------------------------------------- *)
map (time PRIME_TEST o mk_small_numeral) (0--50);;
time PRIME_TEST `65535`;;
time PRIME_TEST `65536`;;
time PRIME_TEST `65537`;;
time PROVE_PRIMEFACT (Int 222);;
time PROVE_PRIMEFACT (Int 151);;
(* ------------------------------------------------------------------------- *)
(* The "Landau trick" in Erdos's proof of Chebyshev-Bertrand theorem. *)
(* ------------------------------------------------------------------------- *)
map (time PRIME_TEST o mk_small_numeral)
[3; 5; 7; 13; 23; 43; 83; 163; 317; 631; 1259; 2503; 4001];;