(* ========================================================================= *)
(* Basic theory of divisibility, gcd, coprimality and primality (over N).    *)
(* ========================================================================= *)
prioritize_num();;
(* ------------------------------------------------------------------------- *)
(* HOL88 compatibility (since all this is a port of old HOL88 stuff).        *)
(* ------------------------------------------------------------------------- *)
let LESS_THM = ARITH_RULE `!m n. m < (SUC n) <=> (m = n) \/ m < n`;;
let DIFF_LEMMA = prove(
  `!a b. a < b ==> (a = 0) \/ (a + (b - a)) < (a + b)`,
  REPEAT GEN_TAC THEN
  DISJ_CASES_TAC(SPEC `a:num` 
LESS_0_CASES) THEN ASM_REWRITE_TAC[] THEN
  DISCH_THEN(CHOOSE_THEN SUBST1_TAC o MATCH_MP 
LESS_ADD_1) THEN
  DISJ2_TAC THEN REWRITE_TAC[ONCE_REWRITE_RULE[
ADD_SYM] 
ADD_SUB] THEN
  GEN_REWRITE_TAC LAND_CONV [GSYM (CONJUNCT1 
ADD_CLAUSES)] THEN
  REWRITE_TAC[
ADD_ASSOC] THEN
  REPEAT(MATCH_MP_TAC 
LESS_MONO_ADD) THEN POP_ASSUM ACCEPT_TAC);;
 
let EVEN_SQUARE = prove(
  `!n. 
EVEN(n) ==> ?x. n 
EXP 2 = 4 * x`,
  GEN_TAC THEN REWRITE_TAC[
EVEN_EXISTS] THEN
  DISCH_THEN(X_CHOOSE_THEN `m:num` SUBST1_TAC) THEN
  EXISTS_TAC `m * m` THEN REWRITE_TAC[
EXP_2] THEN
  REWRITE_TAC[SYM(REWRITE_CONV[ARITH] `2 * 2`)] THEN
  REWRITE_TAC[
MULT_AC]);;
 
let ADD_IMP_SUB = prove(
  `!x y z. (x + y = z) ==> (x = z - y)`,
  REPEAT GEN_TAC THEN DISCH_THEN(SUBST1_TAC o SYM) THEN
  REWRITE_TAC[
ADD_SUB]);;
 
let ADD_SUM_DIFF = prove(
  `!v w. v <= w ==> ((w + v) - (w - v) = 2 * v) /\
                    ((w + v) + (w - v) = 2 * w)`,
 
let DIVIDES_ONE = prove(
  `!x. (x 
divides 1) <=> (x = 1)`,
  GEN_TAC THEN REWRITE_TAC[
divides] THEN
  CONV_TAC(LAND_CONV(ONCE_DEPTH_CONV SYM_CONV)) THEN
  REWRITE_TAC[
MULT_EQ_1] THEN EQ_TAC THEN STRIP_TAC THEN
  ASM_REWRITE_TAC[] THEN EXISTS_TAC `1` THEN REFL_TAC);;
 
let DIVIDES_ANTISYM = prove
 (`!x y. x 
divides y /\ y 
divides x <=> (x = y)`,
  REPEAT GEN_TAC THEN EQ_TAC THENL
   [REWRITE_TAC[
divides] THEN
    DISCH_THEN(CONJUNCTS_THEN2 MP_TAC (CHOOSE_THEN SUBST1_TAC)) THEN
    DISCH_THEN(CHOOSE_THEN MP_TAC) THEN
    CONV_TAC(LAND_CONV SYM_CONV) THEN
    REWRITE_TAC[GSYM 
MULT_ASSOC; 
MULT_FIX; 
MULT_EQ_1] THEN
    STRIP_TAC THEN ASM_REWRITE_TAC[];
    DISCH_THEN SUBST1_TAC THEN REWRITE_TAC[
DIVIDES_REFL]]);;
 
let DIVIDES_DIV = prove
 (`!n x. 0 < n /\ (x MOD n = 0) ==> n 
divides x`,
  REPEAT STRIP_TAC THEN
  FIRST_ASSUM(MP_TAC o SPEC `x:num` o MATCH_MP 
DIVISION o
           MATCH_MP (ARITH_RULE `0 < n ==> ~(n = 0)`)) THEN
  ASM_REWRITE_TAC[
ADD_CLAUSES] THEN DISCH_TAC THEN
  REWRITE_TAC[
divides] THEN EXISTS_TAC `x DIV n` THEN
  ONCE_REWRITE_TAC[
MULT_SYM] THEN FIRST_ASSUM MATCH_ACCEPT_TAC);;
 
let DIVIDES_MOD = prove
 (`!m n. ~(m = 0) ==> (m 
divides n <=> (n MOD m = 0))`,
  REWRITE_TAC[
divides] THEN REPEAT STRIP_TAC THEN EQ_TAC THENL
   [ASM_MESON_TAC[MOD_MULT]; DISCH_TAC] THEN
  FIRST_X_ASSUM(MP_TAC o SPEC `n:num` o MATCH_MP 
DIVISION) THEN
  ASM_REWRITE_TAC[
ADD_CLAUSES] THEN MESON_TAC[
MULT_AC]);;
 
let IND_EUCLID = prove(
  `!P. (!a b. P a b <=> P b a) /\
       (!a. P a 0) /\
       (!a b. P a b ==> P a (a + b)) ==>
         !a b. P a b`,
  REPEAT STRIP_TAC THEN
  W(fun (asl,w) -> SUBGOAL_THEN `!n a b. (a + b = n) ==> P a b`
                   MATCH_MP_TAC) THENL
   [ALL_TAC; EXISTS_TAC `a + b` THEN REFL_TAC] THEN
  MATCH_MP_TAC 
num_WF THEN
  REPEAT STRIP_TAC THEN REPEAT_TCL DISJ_CASES_THEN MP_TAC
   (SPECL [`a:num`; `b:num`] 
LESS_LESS_CASES) THENL
   [DISCH_THEN SUBST1_TAC THEN
    GEN_REWRITE_TAC RAND_CONV [GSYM 
ADD_0] THEN
    FIRST_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[];
    ALL_TAC; ALL_TAC] THEN
  DISCH_THEN(fun 
th -> SUBST1_TAC(SYM(MATCH_MP 
SUB_ADD
   (MATCH_MP 
LT_IMP_LE th))) THEN
    DISJ_CASES_THEN MP_TAC (MATCH_MP 
DIFF_LEMMA th)) THENL
   [DISCH_THEN SUBST1_TAC THEN
    FIRST_ASSUM (CONV_TAC o REWR_CONV) THEN
    FIRST_ASSUM MATCH_ACCEPT_TAC;
    REWRITE_TAC[ASSUME `a + b = n`] THEN
    DISCH_TAC THEN ONCE_REWRITE_TAC[
ADD_SYM] THEN
    FIRST_ASSUM MATCH_MP_TAC THEN
    UNDISCH_TAC `a + b - a < n` THEN
    DISCH_THEN(ANTE_RES_THEN MATCH_MP_TAC);
    DISCH_THEN SUBST1_TAC THEN FIRST_ASSUM MATCH_ACCEPT_TAC;
    REWRITE_TAC[ONCE_REWRITE_RULE[
ADD_SYM] (ASSUME `a + b = n`)] THEN
    DISCH_TAC THEN ONCE_REWRITE_TAC[
ADD_SYM] THEN
    FIRST_ASSUM (CONV_TAC o REWR_CONV) THEN
    FIRST_ASSUM MATCH_MP_TAC THEN
    UNDISCH_TAC `b + a - b < n` THEN
    DISCH_THEN(ANTE_RES_THEN MATCH_MP_TAC)] THEN
  REWRITE_TAC[]);;
 
let BEZOUT_LEMMA = prove(
  `!a b. (?d x y. (d 
divides a /\ d 
divides b) /\
                  ((a * x = (b * y) + d) \/
                   (b * x = (a * y) + d)))
     ==> (?d x y. (d 
divides a /\ d 
divides (a + b)) /\
                  ((a * x = ((a + b) * y) + d) \/
                   ((a + b) * x = (a * y) + d)))`,
  REPEAT STRIP_TAC THEN EXISTS_TAC `d:num` THENL
   [MAP_EVERY EXISTS_TAC [`x + y`; `y:num`];
    MAP_EVERY EXISTS_TAC [`x:num`; `x + y`]] THEN
  ASM_REWRITE_TAC[] THEN
  (CONJ_TAC THENL [MATCH_MP_TAC 
DIVIDES_ADD; ALL_TAC]) THEN
  ASM_REWRITE_TAC[
LEFT_ADD_DISTRIB; 
RIGHT_ADD_DISTRIB] THEN
  REWRITE_TAC[
ADD_ASSOC] THEN DISJ1_TAC THEN
  REWRITE_TAC[
ADD_AC]);;
 
let BEZOUT_ADD = prove(
  `!a b. ?d x y. (d 
divides a /\ d 
divides b) /\
                 ((a * x = (b * y) + d) \/
                  (b * x = (a * y) + d))`,
  W(fun (asl,w) -> MP_TAC(SPEC (list_mk_abs([`a:num`; `b:num`],
                                            snd(strip_forall w)))
    
IND_EUCLID)) THEN BETA_TAC THEN DISCH_THEN MATCH_MP_TAC THEN
  REPEAT CONJ_TAC THENL
   [REPEAT GEN_TAC THEN REPEAT
     (AP_TERM_TAC THEN GEN_REWRITE_TAC I [
FUN_EQ_THM] THEN
    GEN_TAC THEN BETA_TAC) THEN
    GEN_REWRITE_TAC (RAND_CONV o RAND_CONV) [
DISJ_SYM] THEN
    GEN_REWRITE_TAC (RAND_CONV o LAND_CONV) [
CONJ_SYM] THEN REFL_TAC;
    GEN_TAC THEN MAP_EVERY EXISTS_TAC [`a:num`; `1`; `0`] THEN
    REWRITE_TAC[
MULT_CLAUSES; 
ADD_CLAUSES; 
DIVIDES_0; 
DIVIDES_REFL];
    MATCH_ACCEPT_TAC 
BEZOUT_LEMMA]);;
 
let BEZOUT = prove(
  `!a b. ?d x y. (d 
divides a /\ d 
divides b) /\
                 (((a * x) - (b * y) = d) \/
                  ((b * x) - (a * y) = d))`,
  REPEAT GEN_TAC THEN REPEAT_TCL STRIP_THM_THEN ASSUME_TAC
   (SPECL [`a:num`; `b:num`] 
BEZOUT_ADD) THEN
  REPEAT(W(EXISTS_TAC o fst o dest_exists o snd)) THEN
  ASM_REWRITE_TAC[] THEN
  ONCE_REWRITE_TAC[
ADD_SYM] THEN REWRITE_TAC[
ADD_SUB]);;
 
let BEZOUT_ADD_STRONG = prove
 (`!a b. ~(a = 0)
         ==> ?d x y. d 
divides a /\ d 
divides b /\ (a * x = b * y + d)`,
  REPEAT STRIP_TAC THEN
  MP_TAC(SPECL [`a:num`; `b:num`] 
BEZOUT_ADD) THEN
  REWRITE_TAC[TAUT `a /\ (b \/ c) <=> a /\ b \/ a /\ c`] THEN
  REWRITE_TAC[
EXISTS_OR_THM; GSYM 
CONJ_ASSOC] THEN
  MATCH_MP_TAC(TAUT `(b ==> a) ==> a \/ b ==> a`) THEN
  DISCH_THEN(X_CHOOSE_THEN `d:num` (X_CHOOSE_THEN `x:num`
    (X_CHOOSE_THEN `y:num` STRIP_ASSUME_TAC))) THEN
  FIRST_X_ASSUM(MP_TAC o SYM) THEN
  ASM_CASES_TAC `b = 0` THENL
   [ASM_SIMP_TAC[
MULT_CLAUSES; 
ADD_EQ_0; 
MULT_EQ_0; 
ADD_CLAUSES] THEN
    STRIP_TAC THEN UNDISCH_TAC `d 
divides a` THEN
    ASM_REWRITE_TAC[
DIVIDES_ZERO]; ALL_TAC] THEN
  MP_TAC(SPECL [`d:num`; `b:num`] 
DIVIDES_LE) THEN ASM_REWRITE_TAC[] THEN
  REWRITE_TAC[
LE_LT] THEN STRIP_TAC THENL
   [ALL_TAC;
    DISCH_TAC THEN EXISTS_TAC `b:num` THEN EXISTS_TAC `b:num` THEN
    EXISTS_TAC `a - 1` THEN
    UNDISCH_TAC `d 
divides a` THEN ASM_SIMP_TAC[
DIVIDES_REFL] THEN
    REWRITE_TAC[ARITH_RULE `b * x + b = (x + 1) * b`] THEN
    ASM_SIMP_TAC[ARITH_RULE `~(a = 0) ==> ((a - 1) + 1 = a)`]] THEN
  ASM_CASES_TAC `x = 0` THENL
   [ASM_SIMP_TAC[
MULT_CLAUSES; 
ADD_EQ_0; 
MULT_EQ_0] THEN STRIP_TAC THEN
    UNDISCH_TAC `d 
divides a` THEN ASM_REWRITE_TAC[
DIVIDES_ZERO]; ALL_TAC] THEN
  DISCH_THEN(MP_TAC o AP_TERM `( * ) (b - 1)`) THEN
  DISCH_THEN(MP_TAC o AP_TERM `(+) (d:num)`) THEN
  GEN_REWRITE_TAC (LAND_CONV o LAND_CONV o RAND_CONV)
   [
LEFT_ADD_DISTRIB] THEN
  REWRITE_TAC[ARITH_RULE `d + bay + b1 * d = (1 + b1) * d + bay`] THEN
  ASM_SIMP_TAC[ARITH_RULE `~(b = 0) ==> (1 + (b - 1) = b)`] THEN
  DISCH_THEN(MP_TAC o MATCH_MP (ARITH_RULE
   `(a + b = c + d) ==> a <= d ==> (b = (d - a) + c:num)`)) THEN
  ANTS_TAC THENL
   [ONCE_REWRITE_TAC[AC 
MULT_AC `(b - 1) * b * x = b * (b - 1) * x`] THEN
    REWRITE_TAC[
LE_MULT_LCANCEL] THEN DISJ2_TAC THEN
    GEN_REWRITE_TAC LAND_CONV [ARITH_RULE `d = d * 1`] THEN
    MATCH_MP_TAC 
LE_MULT2 THEN
    MAP_EVERY UNDISCH_TAC [`d < b:num`; `~(x = 0)`] THEN ARITH_TAC;
    ALL_TAC] THEN
  DISCH_THEN(fun 
th ->
    MAP_EVERY EXISTS_TAC [`d:num`; `y * (b - 1)`; `(b - 1) * x - d`] THEN
    MP_TAC 
th) THEN
  ASM_REWRITE_TAC[] THEN
  GEN_REWRITE_TAC (RAND_CONV o RAND_CONV o LAND_CONV) [
LEFT_SUB_DISTRIB] THEN
  REWRITE_TAC[
MULT_AC]);;
 
let BEZOUT_GCD = prove(
  `!a b. ?x y. ((a * x) - (b * y) = gcd(a,b)) \/
               ((b * x) - (a * y) = gcd(a,b))`,
  REPEAT GEN_TAC THEN
  MP_TAC(SPECL [`a:num`; `b:num`] 
BEZOUT) THEN
  DISCH_THEN(EVERY_TCL (map X_CHOOSE_THEN [`d:num`; `x:num`; `y:num`])
    (CONJUNCTS_THEN ASSUME_TAC)) THEN
  SUBGOAL_THEN `d 
divides gcd(a,b)` MP_TAC THENL
   [MATCH_MP_TAC(last(CONJUNCTS(SPEC_ALL 
GCD))) THEN ASM_REWRITE_TAC[];
    DISCH_THEN(X_CHOOSE_THEN `k:num` SUBST1_TAC o REWRITE_RULE[
divides]) THEN
    MAP_EVERY EXISTS_TAC [`x * k`; `y * k`] THEN
    ASM_REWRITE_TAC[GSYM 
RIGHT_SUB_DISTRIB; 
MULT_ASSOC] THEN
    FIRST_ASSUM(DISJ_CASES_THEN SUBST1_TAC) THEN REWRITE_TAC[]]);;
 
let BEZOUT_GCD_STRONG = prove
 (`!a b. ~(a = 0) ==> ?x y. a * x = b * y + gcd(a,b)`,
  REPEAT STRIP_TAC THEN
  FIRST_ASSUM(MP_TAC o SPEC `b:num` o MATCH_MP 
BEZOUT_ADD_STRONG) THEN
  REWRITE_TAC[
LEFT_IMP_EXISTS_THM] THEN
  MAP_EVERY X_GEN_TAC [`d:num`; `x:num`; `y:num`] THEN
  STRIP_TAC THEN ASM_REWRITE_TAC[] THEN
  SUBGOAL_THEN `d 
divides gcd(a,b)` MP_TAC THENL
   [ASM_MESON_TAC[
GCD]; ALL_TAC] THEN
  DISCH_THEN(X_CHOOSE_THEN `k:num` SUBST1_TAC o REWRITE_RULE[
divides]) THEN
  MAP_EVERY EXISTS_TAC [`x * k`; `y * k`] THEN
  ASM_REWRITE_TAC[GSYM 
RIGHT_ADD_DISTRIB; 
MULT_ASSOC]);;
 
let GCD_RMUL = prove(
  `!a b c. gcd(a * c, b * c) = c * gcd(a,b)`,
  REPEAT GEN_TAC THEN
  GEN_REWRITE_TAC (LAND_CONV o ONCE_DEPTH_CONV) [
MULT_SYM] THEN
  MATCH_ACCEPT_TAC 
GCD_LMUL);;
 
let GCD_BEZOUT = prove(
  `!a b d. (?x y. ((a * x) - (b * y) = d) \/ ((b * x) - (a * y) = d)) <=>
           gcd(a,b) 
divides d`,
  REPEAT GEN_TAC THEN EQ_TAC THENL
   [STRIP_TAC THEN POP_ASSUM(SUBST1_TAC o SYM) THEN
    MATCH_MP_TAC 
DIVIDES_SUB THEN CONJ_TAC THEN
    MATCH_MP_TAC 
DIVIDES_RMUL THEN REWRITE_TAC[
GCD];
    DISCH_THEN(X_CHOOSE_THEN `k:num` SUBST1_TAC o REWRITE_RULE[
divides]) THEN
    STRIP_ASSUME_TAC(SPECL [`a:num`; `b:num`] 
BEZOUT_GCD) THEN
    MAP_EVERY EXISTS_TAC [`x * k`; `y * k`] THEN
    ASM_REWRITE_TAC[GSYM 
RIGHT_SUB_DISTRIB; 
MULT_ASSOC] THEN
    FIRST_ASSUM(DISJ_CASES_THEN SUBST1_TAC) THEN REWRITE_TAC[]]);;
 
let GCD_ZERO = prove(
  `!a b. (gcd(a,b) = 0) <=> (a = 0) /\ (b = 0)`,
  REPEAT GEN_TAC THEN EQ_TAC THEN STRIP_TAC THEN
  ASM_REWRITE_TAC[
GCD_0] THEN
  MP_TAC(SPECL [`a:num`; `b:num`] 
GCD) THEN
  ASM_REWRITE_TAC[
DIVIDES_ZERO] THEN
  STRIP_TAC THEN ASM_REWRITE_TAC[]);;
 
let GCD_1 = prove(
  `!a. gcd(1,a) = 1`,
  GEN_TAC THEN CONV_TAC SYM_CONV THEN
  ONCE_REWRITE_TAC[GSYM 
GCD_UNIQUE] THEN
  REWRITE_TAC[
DIVIDES_1] THEN
  REPEAT STRIP_TAC THEN ASM_REWRITE_TAC[]);;
 
let GCD_ADD = prove
 (`(!a b. gcd(a + b,b) = gcd(a,b)) /\
   (!a b. gcd(b + a,b) = gcd(a,b)) /\
   (!a b. gcd(a,a + b) = gcd(a,b)) /\
   (!a b. gcd(a,b + a) = gcd(a,b))`,
 
let GCD_SUB = prove
 (`(!a b. b <= a ==> gcd(a - b,b) = gcd(a,b)) /\
   (!a b. a <= b ==> gcd(a,b - a) = gcd(a,b))`,
 
let COPRIME = prove(
  `!a b. 
coprime(a,b) <=> !d. d 
divides a /\ d 
divides b <=> (d = 1)`,
  REPEAT GEN_TAC THEN REWRITE_TAC[
coprime] THEN
  REPEAT(EQ_TAC ORELSE STRIP_TAC) THEN ASM_REWRITE_TAC[
DIVIDES_1] THENL
   [FIRST_ASSUM MATCH_MP_TAC;
    FIRST_ASSUM(CONV_TAC o REWR_CONV o GSYM) THEN CONJ_TAC] THEN
  ASM_REWRITE_TAC[]);;
 
let GCD_COPRIME = prove
 (`!a b a' b'. ~(gcd(a,b) = 0) /\ a = a' * gcd(a,b) /\ b = b' * gcd(a,b)
               ==> 
coprime(a',b')`,
  NUMBER_TAC);;
 
let GCD_COPRIME_EXISTS = prove(
  `!a b. ~(gcd(a,b) = 0) ==>
        ?a' b'. (a = a' * gcd(a,b)) /\
                (b = b' * gcd(a,b)) /\
                
coprime(a',b')`,
  REPEAT GEN_TAC THEN DISCH_TAC THEN MP_TAC(SPECL [`a:num`; `b:num`] 
GCD) THEN
  DISCH_THEN(MP_TAC o CONJUNCT1) THEN REWRITE_TAC[
divides] THEN
  DISCH_THEN(CONJUNCTS_THEN2 (X_CHOOSE_TAC `a':num` o GSYM)
   (X_CHOOSE_TAC `b':num` o GSYM)) THEN
  MAP_EVERY EXISTS_TAC [`a':num`; `b':num`] THEN
  ONCE_REWRITE_TAC[
MULT_SYM] THEN ASM_REWRITE_TAC[] THEN
  MATCH_MP_TAC 
GCD_COPRIME THEN
  MAP_EVERY EXISTS_TAC [`a:num`; `b:num`] THEN
  ONCE_REWRITE_TAC[
MULT_SYM] THEN ASM_REWRITE_TAC[]);;
 
let BEZOUT_GCD_POW = prove(
  `!n a b. ?x y. (((a 
EXP n) * x) - ((b 
EXP n) * y) = gcd(a,b) 
EXP n) \/
                 (((b 
EXP n) * x) - ((a 
EXP n) * y) = gcd(a,b) 
EXP n)`,
  REPEAT GEN_TAC THEN ASM_CASES_TAC `gcd(a,b) = 0` THENL
   [STRUCT_CASES_TAC(SPEC `n:num` 
num_CASES) THEN
    ASM_REWRITE_TAC[
EXP; 
MULT_CLAUSES] THENL
     [MAP_EVERY EXISTS_TAC [`1`; `0`] THEN REWRITE_TAC[
SUB_0];
      REPEAT(EXISTS_TAC `0`) THEN REWRITE_TAC[
MULT_CLAUSES; 
SUB_0]];
    MP_TAC(SPECL [`a:num`; `b:num`] 
GCD) THEN
    DISCH_THEN(CONJUNCTS_THEN2 MP_TAC ASSUME_TAC) THEN
    DISCH_THEN(CONJUNCTS_THEN MP_TAC) THEN REWRITE_TAC[
divides] THEN
    DISCH_THEN(X_CHOOSE_THEN `b':num` ASSUME_TAC) THEN
    DISCH_THEN(X_CHOOSE_THEN `a':num` ASSUME_TAC) THEN
    MP_TAC(SPECL [`a:num`; `b:num`; `a':num`; `b':num`] 
GCD_COPRIME) THEN
    RULE_ASSUM_TAC GSYM THEN RULE_ASSUM_TAC(ONCE_REWRITE_RULE[
MULT_SYM]) THEN
    ASM_REWRITE_TAC[] THEN DISCH_THEN(MP_TAC o GSYM) THEN
    ASM_REWRITE_TAC[] THEN
    DISCH_THEN(MP_TAC o SPEC `n:num` o MATCH_MP 
COPRIME_EXP_IMP) THEN
    REWRITE_TAC[
COPRIME_BEZOUT] THEN
    DISCH_THEN(X_CHOOSE_THEN `x:num` (X_CHOOSE_THEN `y:num` MP_TAC)) THEN
    DISCH_THEN(DISJ_CASES_THEN MP_TAC) THEN
    DISCH_THEN (MP_TAC o AP_TERM `(*) (gcd(a,b) EXP n)`) THEN
    REWRITE_TAC[MULT_CLAUSES; LEFT_SUB_DISTRIB] THEN
    DISCH_THEN(SUBST1_TAC o SYM) THEN
    MAP_EVERY EXISTS_TAC [`x:num`; `y:num`] THEN
    REWRITE_TAC[MULT_ASSOC; GSYM MULT_EXP] THEN
    RULE_ASSUM_TAC(ONCE_REWRITE_RULE[MULT_SYM]) THEN
    ASM_REWRITE_TAC[]]);;
let GCD_EXP = prove(
  `!n a b. gcd(a EXP n,b EXP n) = gcd(a,b) EXP n`,
  REPEAT GEN_TAC THEN CONV_TAC SYM_CONV THEN
  ONCE_REWRITE_TAC[GSYM GCD_UNIQUE] THEN REPEAT CONJ_TAC THENL
   [MATCH_MP_TAC DIVIDES_EXP THEN REWRITE_TAC[GCD];
    MATCH_MP_TAC DIVIDES_EXP THEN REWRITE_TAC[GCD];
    X_GEN_TAC `d:num` THEN STRIP_TAC THEN
    MP_TAC(SPECL [`n:num`; `a:num`; `b:num`] BEZOUT_GCD_POW) THEN
    DISCH_THEN(REPEAT_TCL CHOOSE_THEN (DISJ_CASES_THEN
     (SUBST1_TAC o SYM))) THEN
    MATCH_MP_TAC DIVIDES_SUB THEN CONJ_TAC THEN
    MATCH_MP_TAC DIVIDES_RMUL THEN ASM_REWRITE_TAC[]]);;
let DIVISION_DECOMP = prove(
  `!a b c. a divides (b * c) ==>
     ?b' c'. (a = b' * c') /\ b' divides b /\ c' divides c`,
  REPEAT GEN_TAC THEN DISCH_TAC THEN
  EXISTS_TAC `gcd(a,b)` THEN REWRITE_TAC[GCD] THEN
  MP_TAC(SPECL [`a:num`; `b:num`] GCD_COPRIME_EXISTS) THEN
  ASM_CASES_TAC `gcd(a,b) = 0` THENL
   [ASM_REWRITE_TAC[] THEN EXISTS_TAC `1` THEN
    RULE_ASSUM_TAC(REWRITE_RULE[GCD_ZERO]) THEN
    ASM_REWRITE_TAC[MULT_CLAUSES; DIVIDES_1];
    ASM_REWRITE_TAC[] THEN
    DISCH_THEN(X_CHOOSE_THEN `a':num` (X_CHOOSE_THEN `b':num`
      (STRIP_ASSUME_TAC o GSYM o ONCE_REWRITE_RULE[MULT_SYM]))) THEN
    EXISTS_TAC `a':num` THEN ASM_REWRITE_TAC[] THEN
    UNDISCH_TAC `a divides (b * c)` THEN
    FIRST_ASSUM(fun th -> GEN_REWRITE_TAC
      (LAND_CONV o LAND_CONV) [GSYM th]) THEN
    FIRST_ASSUM(fun th -> GEN_REWRITE_TAC (LAND_CONV o RAND_CONV o LAND_CONV)
     [GSYM th]) THEN REWRITE_TAC[MULT_ASSOC] THEN
    DISCH_TAC THEN MATCH_MP_TAC COPRIME_DIVPROD THEN
    EXISTS_TAC `b':num` THEN ASM_REWRITE_TAC[] THEN
    MATCH_MP_TAC DIVIDES_CMUL2 THEN EXISTS_TAC `gcd(a,b)` THEN
    REWRITE_TAC[MULT_ASSOC] THEN CONJ_TAC THEN
    FIRST_ASSUM MATCH_ACCEPT_TAC]);;
let DIVIDES_EXP2_REV = prove
 (`!n a b. (a EXP n) divides (b EXP n) /\ ~(n = 0) ==> a divides b`,
  REPEAT GEN_TAC THEN ASM_CASES_TAC `gcd(a,b) = 0` THENL
   [ASM_MESON_TAC[GCD_ZERO; DIVIDES_REFL]; ALL_TAC] THEN
  FIRST_ASSUM(MP_TAC o MATCH_MP GCD_COPRIME_EXISTS) THEN
  STRIP_TAC THEN DISCH_THEN(CONJUNCTS_THEN2 MP_TAC ASSUME_TAC) THEN
  ONCE_ASM_REWRITE_TAC[] THEN REWRITE_TAC[MULT_EXP] THEN
  ASM_SIMP_TAC[EXP_EQ_0; DIVIDES_RMUL2_EQ] THEN
  DISCH_THEN(MP_TAC o MATCH_MP (NUMBER_RULE
   `a divides b ==> coprime(a,b) ==> a divides 1`)) THEN
  ASM_SIMP_TAC[COPRIME_EXP2; DIVIDES_ONE; DIVIDES_1; EXP_EQ_1]);;
let DIVIDES_EXP2_EQ = prove
 (`!n a b. ~(n = 0) ==> ((a EXP n) divides (b EXP n) <=> a divides b)`,
  MESON_TAC[DIVIDES_EXP2_REV; DIVIDES_EXP]);;
let DIVIDES_MUL = prove
 (`!m n r. m divides r /\ n divides r /\ coprime(m,n) ==> (m * n) divides r`,
  NUMBER_TAC);;
(* ------------------------------------------------------------------------- *)
(* A binary form of the Chinese Remainder Theorem.                           *)
(* ------------------------------------------------------------------------- *)
let CHINESE_REMAINDER = prove
 (`!a b u v. coprime(a,b) /\ ~(a = 0) /\ ~(b = 0)
             ==> ?x q1 q2. (x = u + q1 * a) /\ (x = v + q2 * b)`,
  let lemma = prove
   (`(?d x y. (d = 1) /\ P x y d) <=> (?x y. P x y 1)`,
    MESON_TAC[]) in
  REPEAT STRIP_TAC THEN
  MP_TAC(SPECL [`b:num`; `a:num`] BEZOUT_ADD_STRONG) THEN
  MP_TAC(SPECL [`a:num`; `b:num`] BEZOUT_ADD_STRONG) THEN
  ASM_REWRITE_TAC[CONJ_ASSOC] THEN
  SUBGOAL_THEN `!d. d divides a /\ d divides b <=> (d = 1)`
   (fun th -> REWRITE_TAC[th; ONCE_REWRITE_RULE[CONJ_SYM] th])
  THENL
   [UNDISCH_TAC `coprime(a,b)` THEN
    SIMP_TAC[GSYM DIVIDES_GCD; COPRIME_GCD; DIVIDES_ONE]; ALL_TAC] THEN
  REWRITE_TAC[lemma] THEN
  DISCH_THEN(X_CHOOSE_THEN `x1:num` (X_CHOOSE_TAC `y1:num`)) THEN
  DISCH_THEN(X_CHOOSE_THEN `x2:num` (X_CHOOSE_TAC `y2:num`)) THEN
  EXISTS_TAC `v * a * x1 + u * b * x2:num` THEN
  EXISTS_TAC `v * x1 + u * y2:num` THEN
  EXISTS_TAC `v * y1 + u * x2:num` THEN CONJ_TAC THENL
   [SUBST1_TAC(ASSUME `b * x2 = a * y2 + 1`);
    SUBST1_TAC(ASSUME `a * x1 = b * y1 + 1`)] THEN
  REWRITE_TAC[LEFT_ADD_DISTRIB; RIGHT_ADD_DISTRIB; MULT_CLAUSES] THEN
  REWRITE_TAC[MULT_AC] THEN REWRITE_TAC[ADD_AC]);; 
let PRIME_2 = prove(
  `
prime(2)`,
  REWRITE_TAC[
prime; ARITH] THEN
  REWRITE_TAC[] THEN REPEAT STRIP_TAC THEN
  FIRST_ASSUM(MP_TAC o MATCH_MP 
DIVIDES_LE) THEN
  REWRITE_TAC[ARITH] THEN REWRITE_TAC[
LE_LT] THEN
  REWRITE_TAC[num_CONV `2`; num_CONV `1`; 
LESS_THM; 
NOT_LESS_0] THEN
  DISCH_THEN(REPEAT_TCL DISJ_CASES_THEN SUBST_ALL_TAC) THEN
  REWRITE_TAC[] THEN POP_ASSUM MP_TAC THEN REWRITE_TAC[
DIVIDES_ZERO] THEN
  REWRITE_TAC[ARITH] THEN REWRITE_TAC[]);;
 
let PRIME_FACTOR = prove(
  `!n. ~(n = 1) ==> ?p. 
prime(p) /\ p 
divides n`,
  MATCH_MP_TAC 
num_WF THEN
  X_GEN_TAC `n:num` THEN REPEAT STRIP_TAC THEN
  ASM_CASES_TAC `
prime(n)` THENL
   [EXISTS_TAC `n:num` THEN ASM_REWRITE_TAC[
DIVIDES_REFL];
    UNDISCH_TAC `~prime(n)` THEN
    DISCH_THEN(MP_TAC o REWRITE_RULE[
prime]) THEN
    ASM_REWRITE_TAC[] THEN ONCE_REWRITE_TAC[
NOT_FORALL_THM] THEN
    DISCH_THEN(X_CHOOSE_THEN `m:num` MP_TAC) THEN
    REWRITE_TAC[NOT_IMP; DE_MORGAN_THM] THEN STRIP_TAC THEN
    FIRST_ASSUM(DISJ_CASES_THEN MP_TAC o MATCH_MP 
DIVIDES_LE) THENL
     [ASM_REWRITE_TAC[
LE_LT] THEN
      DISCH_THEN(ANTE_RES_THEN MP_TAC) THEN ASM_REWRITE_TAC[] THEN
      DISCH_THEN(X_CHOOSE_THEN `p:num` STRIP_ASSUME_TAC) THEN
      EXISTS_TAC `p:num` THEN ASM_REWRITE_TAC[] THEN
      MATCH_MP_TAC 
DIVIDES_TRANS THEN EXISTS_TAC `m:num` THEN
      ASM_REWRITE_TAC[];
      DISCH_THEN SUBST1_TAC THEN EXISTS_TAC `2` THEN
      REWRITE_TAC[
PRIME_2; 
DIVIDES_0]]]);;
 
let PRIME_FACTOR_INDUCT = prove
 (`!P. P 0 /\ P 1 /\
       (!p n. 
prime p /\ ~(n = 0) /\ P n ==> P(p * n))
       ==> !n. P n`,
  GEN_TAC THEN STRIP_TAC THEN
  MATCH_MP_TAC 
num_WF THEN X_GEN_TAC `n:num` THEN
  DISCH_TAC THEN MAP_EVERY ASM_CASES_TAC [`n = 0`; `n = 1`] THEN
  ASM_REWRITE_TAC[] THEN FIRST_ASSUM(X_CHOOSE_THEN `p:num`
    STRIP_ASSUME_TAC o MATCH_MP 
PRIME_FACTOR) THEN
  FIRST_X_ASSUM(X_CHOOSE_THEN `d:num` SUBST_ALL_TAC o
    GEN_REWRITE_RULE I [
divides]) THEN
  FIRST_X_ASSUM(MP_TAC o SPECL [`p:num`; `d:num`]) THEN
  RULE_ASSUM_TAC(REWRITE_RULE[
MULT_EQ_0; DE_MORGAN_THM]) THEN
  DISCH_THEN MATCH_MP_TAC THEN ASM_REWRITE_TAC[] THEN
  FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_MESON_TAC[
PRIME_FACTOR_LT; 
MULT_EQ_0]);;
 
let COPRIME_PRIME_EQ = prove(
  `!a b. 
coprime(a,b) <=> !p. ~(
prime(p) /\ p 
divides a /\ p 
divides b)`,
  REPEAT GEN_TAC THEN EQ_TAC THENL
   [DISCH_THEN(fun 
th -> REWRITE_TAC[MATCH_MP 
COPRIME_PRIME th]);
    CONV_TAC CONTRAPOS_CONV THEN REWRITE_TAC[
coprime] THEN
    ONCE_REWRITE_TAC[
NOT_FORALL_THM] THEN REWRITE_TAC[NOT_IMP] THEN
    DISCH_THEN(X_CHOOSE_THEN `d:num` STRIP_ASSUME_TAC) THEN
    FIRST_ASSUM(X_CHOOSE_TAC `p:num` o MATCH_MP 
PRIME_FACTOR) THEN
    EXISTS_TAC `p:num` THEN ASM_REWRITE_TAC[] THEN CONJ_TAC THEN
    MATCH_MP_TAC 
DIVIDES_TRANS THEN EXISTS_TAC `d:num` THEN
    ASM_REWRITE_TAC[]]);;
 
let PRIME_COPRIME = prove(
  `!n p. 
prime(p) ==> (n = 1) \/ p 
divides n \/ 
coprime(p,n)`,
  REPEAT GEN_TAC THEN REWRITE_TAC[
prime; 
COPRIME_GCD] THEN
  STRIP_ASSUME_TAC(SPECL [`p:num`; `n:num`] 
GCD) THEN
  DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC) THEN
  DISCH_THEN(MP_TAC o SPEC `gcd(p,n)`) THEN ASM_REWRITE_TAC[] THEN
  DISCH_THEN(DISJ_CASES_THEN SUBST_ALL_TAC) THEN
  ASM_REWRITE_TAC[]);;
 
let PRIME_DIVEXP = prove(
  `!n p x. 
prime(p) /\ p 
divides (x 
EXP n) ==> p 
divides x`,
  INDUCT_TAC THEN REPEAT GEN_TAC THEN REWRITE_TAC[
EXP; 
DIVIDES_ONE] THENL
   [DISCH_THEN(SUBST1_TAC o CONJUNCT2) THEN REWRITE_TAC[
DIVIDES_1];
    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_REWRITE_TAC[] THEN FIRST_ASSUM MATCH_MP_TAC THEN
    ASM_REWRITE_TAC[]]);;
 
let PRIME_EXP = prove
 (`!p n. 
prime(p 
EXP n) <=> 
prime(p) /\ (n = 1)`,
  GEN_TAC THEN INDUCT_TAC THEN REWRITE_TAC[
EXP; 
PRIME_1; 
ARITH_EQ] THEN
  POP_ASSUM_LIST(K ALL_TAC) THEN SPEC_TAC(`n:num`,`n:num`) THEN
  ASM_CASES_TAC `p = 0` THENL
   [ASM_REWRITE_TAC[
PRIME_0; 
EXP; 
MULT_CLAUSES]; ALL_TAC] THEN
  INDUCT_TAC THEN REWRITE_TAC[ARITH; 
EXP_1; 
EXP; 
MULT_CLAUSES] THEN
  REWRITE_TAC[ARITH_RULE `~(SUC(SUC n) = 1)`] THEN
  REWRITE_TAC[
prime; DE_MORGAN_THM] THEN
  ASM_REWRITE_TAC[
MULT_EQ_1; 
EXP_EQ_1] THEN ASM_CASES_TAC `p = 1` THEN
  ASM_REWRITE_TAC[NOT_IMP; DE_MORGAN_THM] THEN
  DISCH_THEN(MP_TAC o SPEC `p:num`) THEN ASM_REWRITE_TAC[NOT_IMP] THEN
  CONJ_TAC THENL [MESON_TAC[
EXP; 
divides]; ALL_TAC] THEN
  MATCH_MP_TAC(ARITH_RULE `p < pn:num ==> ~(p = pn)`) THEN
  GEN_REWRITE_TAC LAND_CONV [GSYM 
EXP_1] THEN
  REWRITE_TAC[GSYM(CONJUNCT2 
EXP)] THEN
  ASM_REWRITE_TAC[
LT_EXP; 
ARITH_EQ] THEN
  MAP_EVERY UNDISCH_TAC [`~(p = 0)`; `~(p = 1)`] THEN ARITH_TAC);;
 
let PRIME_POWER_MULT = prove
 (`!k x y p. 
prime p /\ (x * y = p 
EXP k)
           ==> ?i j. (x = p 
EXP i) /\ (y = p 
EXP j)`,
  INDUCT_TAC THEN REWRITE_TAC[
EXP; 
MULT_EQ_1] THENL
   [MESON_TAC[
EXP]; ALL_TAC] THEN
  REPEAT STRIP_TAC THEN
  SUBGOAL_THEN `p 
divides x \/ p 
divides y` MP_TAC THENL
   [ASM_MESON_TAC[
PRIME_DIVPROD; 
divides; 
MULT_AC]; ALL_TAC] THEN
  REWRITE_TAC[
divides] THEN
  SUBGOAL_THEN `~(p = 0)` ASSUME_TAC THENL
   [ASM_MESON_TAC[
PRIME_0]; ALL_TAC] THEN
  DISCH_THEN(DISJ_CASES_THEN (X_CHOOSE_THEN `d:num` SUBST_ALL_TAC)) THENL
   [UNDISCH_TAC `(p * d) * y = p * p 
EXP k`;
    UNDISCH_TAC `x * p * d = p * p 
EXP k` THEN
    GEN_REWRITE_TAC (LAND_CONV o LAND_CONV) [
MULT_SYM]] THEN
  REWRITE_TAC[GSYM 
MULT_ASSOC] THEN
  ASM_REWRITE_TAC[
EQ_MULT_LCANCEL] THEN DISCH_TAC THENL
   [FIRST_X_ASSUM(MP_TAC o SPECL [`d:num`; `y:num`; `p:num`]);
    FIRST_X_ASSUM(MP_TAC o SPECL [`d:num`; `x:num`; `p:num`])] THEN
  ASM_REWRITE_TAC[] THEN MESON_TAC[
EXP]);;
 
let INDUCT_COPRIME = prove
 (`!P. (!a b. 1 < a /\ 1 < b /\ 
coprime(a,b) /\ P a /\ P b ==> P(a * b)) /\
       (!p k. 
prime p ==> P(p 
EXP k))
       ==> !n. 1 < n ==> P n`,
  GEN_TAC THEN DISCH_TAC THEN MATCH_MP_TAC 
num_WF THEN
  X_GEN_TAC `n:num` THEN REPEAT STRIP_TAC THEN
  FIRST_ASSUM(MP_TAC o MATCH_MP (ARITH_RULE `1 < n ==> ~(n = 1)`)) THEN
  DISCH_THEN(X_CHOOSE_TAC `p:num` o MATCH_MP 
PRIME_FACTOR) THEN
  MP_TAC(SPECL [`n:num`; `p:num`] 
FACTORIZATION_INDEX) THEN
  ASM_SIMP_TAC[
PRIME_GE_2; ARITH_RULE `1 < n ==> ~(n = 0)`] THEN
  REWRITE_TAC[
divides; 
LEFT_IMP_EXISTS_THM; 
LEFT_AND_EXISTS_THM] THEN
  MAP_EVERY X_GEN_TAC [`k:num`; `m:num`] THEN STRIP_TAC THEN
  FIRST_X_ASSUM SUBST_ALL_TAC THEN
  ASM_CASES_TAC `m = 1` THEN ASM_SIMP_TAC[
MULT_CLAUSES] THEN
  FIRST_X_ASSUM(CONJUNCTS_THEN2 MATCH_MP_TAC MP_TAC) THEN
  ASM_SIMP_TAC[] THEN DISCH_THEN(K ALL_TAC) THEN
  MATCH_MP_TAC(TAUT
   `!p. (a /\ b /\ ~p) /\ c /\ (a /\ ~p ==> b ==> d)
        ==> a /\ b /\ c /\ d`) THEN
  EXISTS_TAC `m = 0` THEN
  SUBGOAL_THEN `~(k = 0)` ASSUME_TAC THENL
   [DISCH_THEN SUBST_ALL_TAC THEN
    FIRST_X_ASSUM(MP_TAC o C MATCH_MP (ARITH_RULE `0 < 1`)) THEN
    FIRST_X_ASSUM(MP_TAC o CONJUNCT2) THEN
    REWRITE_TAC[
EXP; 
EXP_1; 
MULT_CLAUSES; 
divides];
    ALL_TAC] THEN
  CONJ_TAC THENL
   [UNDISCH_TAC `1 < p 
EXP k * m` THEN
    ASM_REWRITE_TAC[ARITH_RULE `1 < x <=> ~(x = 0) /\ ~(x = 1)`] THEN
    ASM_REWRITE_TAC[
EXP_EQ_0; 
EXP_EQ_1; 
MULT_EQ_0; 
MULT_EQ_1] THEN
    FIRST_X_ASSUM(MP_TAC o MATCH_MP 
PRIME_GE_2 o CONJUNCT1) THEN
    ASM_ARITH_TAC;
    ALL_TAC] THEN
  CONJ_TAC THENL
   [FIRST_X_ASSUM(MP_TAC o C MATCH_MP (ARITH_RULE `k < k + 1`)) THEN
    REWRITE_TAC[
EXP_ADD; 
EXP_1; GSYM 
MULT_ASSOC; 
EQ_MULT_LCANCEL] THEN
    ASM_SIMP_TAC[
EXP_EQ_0; 
PRIME_IMP_NZ; GSYM 
divides] THEN DISCH_TAC THEN
    ONCE_REWRITE_TAC[
COPRIME_SYM] THEN MATCH_MP_TAC 
COPRIME_EXP THEN
    ASM_MESON_TAC[
PRIME_COPRIME; 
COPRIME_SYM];
    DISCH_TAC THEN FIRST_X_ASSUM MATCH_MP_TAC THEN
    GEN_REWRITE_TAC LAND_CONV [ARITH_RULE `m = 1 * m`] THEN
    ASM_REWRITE_TAC[
LT_MULT_RCANCEL]]);;
 
let INDUCT_COPRIME_STRONG = prove
 (`!P. (!a b. 1 < a /\ 1 < b /\ 
coprime(a,b) /\ P a /\ P b ==> P(a * b)) /\
       (!p k. 
prime p /\ ~(k = 0) ==> P(p 
EXP k))
       ==> !n. 1 < n ==> P n`,
  GEN_TAC THEN STRIP_TAC THEN
  ONCE_REWRITE_TAC[TAUT `a ==> b <=> a ==> a ==> b`] THEN
  MATCH_MP_TAC 
INDUCT_COPRIME THEN CONJ_TAC THENL
   [ASM_MESON_TAC[];
    MAP_EVERY X_GEN_TAC [`p:num`; `k:num`] THEN ASM_CASES_TAC `k = 0` THEN
    ASM_REWRITE_TAC[
LT_REFL; 
EXP] THEN ASM_MESON_TAC[]]);;
 
let pth_yes_l = prove
   (`(m * x = n * y + 1) ==> (
coprime(m,n) <=> T)`,
    MESON_TAC[
coprime; 
DIVIDES_RMUL; 
DIVIDES_ADD_REVR; 
DIVIDES_ONE])
  and pth_yes_r = prove
   (`(m * x = n * y + 1) ==> (
coprime(n,m) <=> T)`,
    MESON_TAC[
coprime; 
DIVIDES_RMUL; 
DIVIDES_ADD_REVR; 
DIVIDES_ONE])
  and pth_no = prove
   (`(m = x * d) /\ (n = y * d) /\ ~(d = 1) ==> (
coprime(m,n) <=> F)`,
    REWRITE_TAC[
coprime; 
divides] THEN MESON_TAC[
MULT_AC])
  and pth_oo = prove
   (`
coprime(0,0) <=> F`,
    MESON_TAC[
coprime; 
DIVIDES_REFL; NUM_REDUCE_CONV `1 = 0`])
  and m_tm = `m:num` and n_tm = `n:num`
  and x_tm = `x:num` and y_tm = `y:num`
  and d_tm = `d:num` and coprime_tm = `
coprime` in
  let rec bezout (m,n) =
    if m =/ Int 0 then (Int 0,Int 1) else if n =/ Int 0 then (Int 1,Int 0)
    else if m <=/ n then
      let q = quo_num n m and r = mod_num n m in
      let (x,y) = bezout(m,r) in
      (x -/ q */ y,y)
    else let (x,y) = bezout(n,m) in (y,x) in
  fun tm ->
   let pop,ptm = dest_comb tm in
   if pop <> coprime_tm then failwith "COPRIME_CONV" else
   let l,r = dest_pair ptm in
   let m = dest_numeral l and n = dest_numeral r in
   if m =/ Int 0 & n =/ Int 0 then pth_oo else
   let (x,y) = bezout(m,n) in
   let d = x */ m +/ y */ n in
   let 
th =
     if d =/ Int 1 then
       if x >/ Int 0 then
          INST [l,m_tm; r,n_tm; mk_numeral x,x_tm;
                mk_numeral(minus_num y),y_tm] 
pth_yes_l
       else
          INST [r,m_tm; l,n_tm; mk_numeral(minus_num x),y_tm;
                mk_numeral y,x_tm] pth_yes_r
     else
         INST [l,m_tm; r,n_tm; mk_numeral d,d_tm;
              mk_numeral(m // d),x_tm; mk_numeral(n // d),y_tm] pth_no in
    MP 
th (EQT_ELIM(NUM_REDUCE_CONV(lhand(concl 
th))));;
 
let pth0 = prove(`gcd(0,0) = 0`,
REWRITE_TAC[
GCD_0]) in
  
let pth1 = prove
   (`!m n x y d m' n'.
      (m * x = n * y + d) /\ (m = m' * d) /\ (n = n' * d) ==> (gcd(m,n) = d)`,
    REPEAT GEN_TAC THEN
    DISCH_THEN(CONJUNCTS_THEN2 MP_TAC STRIP_ASSUME_TAC) THEN
    CONV_TAC(RAND_CONV SYM_CONV) THEN ASM_REWRITE_TAC[GSYM GCD_UNIQUE] THEN
    ASM_MESON_TAC[DIVIDES_LMUL; DIVIDES_RMUL;
                  DIVIDES_ADD_REVR; DIVIDES_REFL]) in
  let pth2 = prove
   (`!m n x y d m' n'.
       (n * y = m * x + d) /\ (m = m' * d) /\ (n = n' * d) ==> (gcd(m,n) = d)`,
    MESON_TAC[pth1; GCD_SYM]) in
  let gcd_tm = `gcd` in
  let rec bezout (m,n) =
    if m =/ Int 0 then (Int 0,Int 1) else if n =/ Int 0 then (Int 1,Int 0)
    else if m <=/ n then
      let q = quo_num n m and r = mod_num n m in
      let (x,y) = bezout(m,r) in
      (x -/ q */ y,y)
    else let (x,y) = bezout(n,m) in (y,x) in
  fun tm -> let gt,lr = dest_comb tm in
            if gt <> gcd_tm then failwith "GCD_CONV" else
            let mtm,ntm = dest_pair lr in
            let m = dest_numeral mtm and n = dest_numeral ntm in
            if m =/ Int 0 & n =/ Int 0 then pth0 else
            let x0,y0 = bezout(m,n) in
            let x = abs_num x0 and y = abs_num y0 in
            let xtm = mk_numeral x and ytm = mk_numeral y in
            let d = abs_num(x */ m -/ y */ n) in
            let dtm = mk_numeral d in
            let m' = m // d and n' = n // d in
            let mtm' = mk_numeral m' and ntm' = mk_numeral n' in
            let th = SPECL [mtm;ntm;xtm;ytm;dtm;mtm';