(* ========================================================================= *)
(* Definability in arithmetic of important notions.                          *)
(* ========================================================================= *)
prioritize_num();;
(* ------------------------------------------------------------------------- *)
(* Pairing operation.                                                        *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Decreasingness.                                                           *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Auxiliary concepts needed. NB: these are Delta so can be negated freely.  *)
(* ------------------------------------------------------------------------- *)
let primepow_DELTA = prove
 (`
primepow p x <=>
        
prime(p) /\ ~(x = 0) /\
        !z. z <= x ==> z 
divides x ==> z = 1 \/ p 
divides z`,
  REWRITE_TAC[
primepow; TAUT `a ==> b \/ c <=> a /\ ~b ==> c`] THEN
  ASM_CASES_TAC `
prime(p)` THEN
  ASM_REWRITE_TAC[] THEN EQ_TAC THENL
   [DISCH_THEN(X_CHOOSE_THEN `n:num` SUBST1_TAC) THEN
    ASM_REWRITE_TAC[
EXP_EQ_0] THEN
    ASM_CASES_TAC `p = 0` THEN ASM_REWRITE_TAC[] THENL
     [ASM_MESON_TAC[
PRIME_0]; ALL_TAC] THEN
    REPEAT STRIP_TAC THEN
    FIRST_ASSUM(MP_TAC o SPEC `z:num` o MATCH_MP 
PRIME_COPRIME) THEN
    ASM_REWRITE_TAC[] THEN
    ASM_CASES_TAC `p 
divides z` THEN ASM_REWRITE_TAC[] THEN
    ONCE_REWRITE_TAC[
COPRIME_SYM] THEN
    DISCH_THEN(MP_TAC o SPEC `n:num` o MATCH_MP 
COPRIME_EXP) THEN
    ASM_MESON_TAC[
COPRIME; 
DIVIDES_REFL];
    SPEC_TAC(`x:num`,`x:num`) THEN MATCH_MP_TAC 
num_WF THEN
    REPEAT STRIP_TAC THEN ASM_CASES_TAC `x = 1` THENL
     [EXISTS_TAC `0` THEN ASM_REWRITE_TAC[
EXP]; ALL_TAC] THEN
    FIRST_ASSUM(X_CHOOSE_THEN `q:num` MP_TAC o MATCH_MP 
PRIME_FACTOR) THEN
    STRIP_TAC THEN
    UNDISCH_TAC `!z. z <= x ==> z 
divides x /\ ~(z = 1) ==> p 
divides z` THEN
    DISCH_THEN(fun 
th -> ASSUME_TAC 
th THEN MP_TAC 
th) THEN
    DISCH_THEN(MP_TAC o SPEC `q:num`) THEN ASM_REWRITE_TAC[] THEN
    ASM_CASES_TAC `q = 1` THENL [ASM_MESON_TAC[
PRIME_1]; ALL_TAC] THEN
    ASM_REWRITE_TAC[] THEN
    SUBGOAL_THEN `q <= x` ASSUME_TAC THENL
     [ASM_MESON_TAC[
DIVIDES_LE]; ASM_REWRITE_TAC[]] THEN
    SUBGOAL_THEN `p 
divides x` MP_TAC THENL
     [ASM_MESON_TAC[
DIVIDES_TRANS]; ALL_TAC] THEN
    REWRITE_TAC[
divides] THEN DISCH_THEN(X_CHOOSE_TAC `y:num`) THEN
    SUBGOAL_THEN `y < x` (ANTE_RES_THEN MP_TAC) THENL
     [MATCH_MP_TAC 
PRIME_FACTOR_LT THEN
      EXISTS_TAC `p:num` THEN ASM_REWRITE_TAC[]; ALL_TAC] THEN
    ASM_CASES_TAC `y = 0` THENL
     [UNDISCH_TAC `x = p * y` THEN ASM_REWRITE_TAC[
MULT_CLAUSES]; ALL_TAC] THEN
    ASM_REWRITE_TAC[] THEN
    SUBGOAL_THEN `!z. z <= y ==> z 
divides y /\ ~(z = 1) ==> p 
divides z`
    (fun 
th -> REWRITE_TAC[
th]) THENL
     [REPEAT STRIP_TAC THEN
      FIRST_ASSUM(MATCH_MP_TAC o REWRITE_RULE
        [IMP_IMP]) THEN
      REPEAT CONJ_TAC THENL
       [MATCH_MP_TAC 
LE_TRANS THEN EXISTS_TAC `y:num` THEN
        ASM_REWRITE_TAC[] THEN
        GEN_REWRITE_TAC LAND_CONV [ARITH_RULE `y = 1 * y`] THEN
        REWRITE_TAC[
LE_MULT_RCANCEL] THEN
        ASM_REWRITE_TAC[GSYM 
NOT_LT] THEN
        REWRITE_TAC[num_CONV `1`; 
LT; DE_MORGAN_THM] THEN
        ASM_MESON_TAC[
PRIME_0; 
PRIME_1];
        ASM_REWRITE_TAC[] THEN MATCH_MP_TAC 
DIVIDES_LMUL THEN
        ASM_REWRITE_TAC[];
        ASM_REWRITE_TAC[]];
      DISCH_THEN(X_CHOOSE_THEN `n:num` SUBST1_TAC) THEN
      EXISTS_TAC `SUC n` THEN ASM_REWRITE_TAC[
EXP]]]);;
 
let PSEQ = new_recursive_definition num_RECURSION
  `(PSEQ p f m 0 = 0) /\
   (PSEQ p f m (SUC n) = f m + p * PSEQ p f (SUC m) n)`;;
let PSEQ_BOUND = prove
 (`!n. ~(p = 0) /\ (!i. i < n ==> f i < p) ==> 
PSEQ p f 0 n < p 
EXP n`,
  ASM_CASES_TAC `p = 0` THEN ASM_REWRITE_TAC[] THEN
  INDUCT_TAC THENL [REWRITE_TAC[
PSEQ; 
EXP; ARITH]; ALL_TAC] THEN
  DISCH_TAC THEN
  MP_TAC(SPECL [`f:num->num`; `p:num`; `n:num`; `0`; `1`]
               
PSEQ_SPLIT) THEN
  SIMP_TAC[
ADD1; 
ADD_CLAUSES] THEN REPEAT STRIP_TAC THEN
  MATCH_MP_TAC 
LTE_TRANS THEN
  EXISTS_TAC `p 
EXP n + p 
EXP n * 
PSEQ p f n 1` THEN
  ASM_SIMP_TAC[
LT_ADD_RCANCEL; ARITH_RULE `i < n ==> i < SUC n`] THEN
  REWRITE_TAC[ARITH_RULE `p + p * q = p * (q + 1)`] THEN
  ASM_REWRITE_TAC[
EXP_ADD; 
LE_MULT_LCANCEL; 
EXP_EQ_0] THEN
  MATCH_MP_TAC(ARITH_RULE `x < p ==> x + 1 <= p`) THEN
  ASM_SIMP_TAC[
EXP_1; 
PSEQ_1; 
LT]);;
 
let RELPOW_LEMMA_1 = prove
 (`(f 0 = x) /\
   (f n = y) /\
   (!i. i < n ==> R (f i) (f(SUC i)))
   ==> ?p. (?i. i <= n /\ p <= SUC(
FACT(f i))) /\
           
prime p /\
           (?m. m < p 
EXP (SUC n) /\
                x < p /\ y < p /\
                (?qx. m = x + p * qx) /\
                (?ry. ry < p 
EXP n /\ (m = ry + p 
EXP n * y)) /\
                !q. q < p 
EXP n
                    ==> 
primepow p q
                        ==> ?r. r < q /\
                                ?a. a < p /\
                                    ?b. b < p /\
                                        R a b /\
                                        ?s. s <= m /\
                                            (m =
                                             r + q * (a + p * (b + p * s))))`,
  REPEAT STRIP_TAC THEN
  SUBGOAL_THEN `?j. j <= n /\ !i. i <= n ==> f i <= f j` MP_TAC THENL
   [SPEC_TAC(`n:num`,`n:num`) THEN POP_ASSUM_LIST(K ALL_TAC) THEN
    INDUCT_TAC THENL
     [SIMP_TAC[
LE] THEN MESON_TAC[
LE_REFL]; ALL_TAC] THEN
    FIRST_ASSUM(X_CHOOSE_THEN `j:num` STRIP_ASSUME_TAC) THEN
    DISJ_CASES_TAC(ARITH_RULE `f(SUC n) <= f(j) \/ f(j) <= f(SUC n)`) THENL
     [EXISTS_TAC `j:num` THEN
      ASM_SIMP_TAC[ARITH_RULE `j <= n ==> j <= SUC n`] THEN
      REWRITE_TAC[
LE] THEN REPEAT STRIP_TAC THEN
      ASM_SIMP_TAC[] THEN ASM_MESON_TAC[];
      EXISTS_TAC `SUC n` THEN REWRITE_TAC[
LE_REFL] THEN
      REWRITE_TAC[
LE] THEN REPEAT STRIP_TAC THEN
      ASM_SIMP_TAC[
LE_REFL] THEN ASM_MESON_TAC[
LE_TRANS]];
    ALL_TAC] THEN
  DISCH_THEN(X_CHOOSE_THEN `ibig:num` STRIP_ASSUME_TAC) THEN
  MP_TAC(SPEC `(f:num->num) ibig` 
EUCLID_BOUND) THEN
  DISCH_THEN(X_CHOOSE_THEN `p:num` STRIP_ASSUME_TAC) THEN
  EXISTS_TAC `p:num` THEN CONJ_TAC THENL
   [EXISTS_TAC `ibig:num` THEN ASM_REWRITE_TAC[]; ALL_TAC] THEN
  SUBGOAL_THEN `~(p = 0)` ASSUME_TAC THENL
     [ASM_MESON_TAC[
PRIME_0]; ALL_TAC] THEN
  CONJ_TAC THENL [FIRST_ASSUM ACCEPT_TAC; ALL_TAC] THEN
  SUBGOAL_THEN `!i. i <= n ==> f i < p` ASSUME_TAC THENL
   [ASM_MESON_TAC[
LET_TRANS]; ALL_TAC] THEN
  EXISTS_TAC `
PSEQ p f 0 (SUC n)` THEN CONJ_TAC THENL
   [MATCH_MP_TAC 
PSEQ_BOUND THEN ASM_SIMP_TAC[
LT_SUC_LE]; ALL_TAC] THEN
  CONJ_TAC THENL [ASM_MESON_TAC[
LE_0]; ALL_TAC] THEN
  CONJ_TAC THENL [ASM_MESON_TAC[
LE_REFL]; ALL_TAC] THEN
  REPEAT CONJ_TAC THENL
   [ASM_REWRITE_TAC[
PSEQ] THEN MESON_TAC[];
    MP_TAC(SPECL [`f:num->num`; `p:num`; `n:num`; `0`; `1`] 
PSEQ_SPLIT) THEN
    ASM_SIMP_TAC[
ADD1; 
ADD_CLAUSES] THEN
    DISCH_THEN(K ALL_TAC) THEN EXISTS_TAC `
PSEQ p f 0 n` THEN
    ASM_SIMP_TAC[
PSEQ_BOUND; 
PSEQ_1; 
LT_IMP_LE];
    ALL_TAC] THEN
  ONCE_REWRITE_TAC[TAUT `a ==> b ==> c <=> b ==> a ==> c`] THEN
  ASM_SIMP_TAC[
primepow; 
LEFT_IMP_EXISTS_THM] THEN
  GEN_TAC THEN X_GEN_TAC `i:num` THEN DISCH_THEN(K ALL_TAC) THEN
  ASM_REWRITE_TAC[
LT_EXP] THEN STRIP_TAC THEN
  MP_TAC(SPECL [`f:num->num`; `p:num`; `i:num`; `0`; `SUC n - i`]
               
PSEQ_SPLIT) THEN
  ASM_SIMP_TAC[ARITH_RULE `i < n ==> (i + SUC n - i = SUC n)`] THEN
  DISCH_THEN(K ALL_TAC) THEN
  EXISTS_TAC `
PSEQ p f 0 i` THEN REWRITE_TAC[
EQ_ADD_LCANCEL] THEN
  ASM_REWRITE_TAC[
EQ_MULT_LCANCEL; 
EXP_EQ_0; 
ADD_CLAUSES] THEN
  CONJ_TAC THENL
   [ASM_MESON_TAC[
PSEQ_BOUND; 
LT_TRANS; 
LT_IMP_LE]; ALL_TAC] THEN
  MP_TAC(SPECL [`f:num->num`; `p:num`; `1`; `i:num`; `n - i`]
               
PSEQ_SPLIT) THEN
  ASM_SIMP_TAC[ARITH_RULE `i < n ==> (1 + n - i = SUC n - i)`] THEN
  DISCH_THEN(K ALL_TAC) THEN EXISTS_TAC `
PSEQ p f i 1` THEN
  ASM_REWRITE_TAC[
EQ_ADD_LCANCEL; 
EQ_MULT_LCANCEL; 
EXP_1] THEN
  ASM_SIMP_TAC[
PSEQ_1; 
LT_IMP_LE] THEN
  MP_TAC(SPECL [`f:num->num`; `p:num`; `1`; `i + 1`; `n - i - 1`]
               
PSEQ_SPLIT) THEN
  ASM_SIMP_TAC[ARITH_RULE `i < n ==> (1 + n - i - 1 = n - i)`] THEN
  DISCH_THEN(K ALL_TAC) THEN EXISTS_TAC `
PSEQ p f (i + 1) 1` THEN
  ASM_REWRITE_TAC[
EQ_ADD_LCANCEL; 
EQ_MULT_LCANCEL; 
EXP_1] THEN
  ASM_SIMP_TAC[
PSEQ_1; ARITH_RULE `i < n ==> i + 1 <= n`] THEN
  ASM_SIMP_TAC[GSYM 
ADD1] THEN REWRITE_TAC[
ADD1] THEN
  ONCE_REWRITE_TAC[
CONJ_SYM] THEN REWRITE_TAC[
UNWIND_THM1] THEN
  REWRITE_TAC[
LEFT_ADD_DISTRIB; 
MULT_ASSOC; 
ADD_ASSOC] THEN
  MATCH_MP_TAC(ARITH_RULE `1 * a <= c ==> a <= b + c`) THEN
  REWRITE_TAC[
LE_MULT_RCANCEL] THEN DISJ1_TAC THEN
  ASM_REWRITE_TAC[ARITH_RULE `1 <= x <=> ~(x = 0)`; 
MULT_EQ_0; 
EXP_EQ_0]);;
 
let RELPOW_LEMMA_2 = prove
 (`
prime p /\ x < p /\ y < p /\
   (?qx. m = x + p * qx) /\
   (?ry. ry < p 
EXP n /\ (m = ry + p 
EXP n * y)) /\
   (!q. q < p 
EXP n
        ==> 
primepow p q
            ==> ?r a b s. (m = r + q * (a + p * (b + p * s))) /\
                          r < q /\ a < p /\ b < p /\ R a b)
   ==> 
RELPOW n R x y`,
  STRIP_TAC THEN REWRITE_TAC[
RELPOW_SEQUENCE] THEN
  EXISTS_TAC `\i. (m DIV (p 
EXP i)) MOD p` THEN
  SUBGOAL_THEN `~(p = 0)` ASSUME_TAC THENL
   [ASM_MESON_TAC[
PRIME_0]; ALL_TAC] THEN
  REWRITE_TAC[
EXP; DIV_1] THEN REPEAT CONJ_TAC THENL
   [MATCH_MP_TAC 
MOD_UNIQ THEN EXISTS_TAC `qx:num` THEN
    ASM_REWRITE_TAC[
ADD_AC; 
MULT_AC];
    MATCH_MP_TAC 
MOD_UNIQ THEN EXISTS_TAC `0` THEN
    REWRITE_TAC[ASSUME `y < p`; 
MULT_CLAUSES; 
ADD_CLAUSES] THEN
    MATCH_MP_TAC 
DIV_UNIQ THEN EXISTS_TAC `ry:num` THEN
    REWRITE_TAC[ASSUME `m = ry + p 
EXP n * y`] THEN
    ASM_REWRITE_TAC[
ADD_AC; 
MULT_AC];
    ALL_TAC] THEN
  X_GEN_TAC `i:num` THEN DISCH_TAC THEN
  FIRST_X_ASSUM(MP_TAC o SPEC `p 
EXP i`) THEN
  ASM_SIMP_TAC[
LT_EXP; 
PRIME_GE_2] THEN
  ASM_REWRITE_TAC[
primepow] THEN
  W(C SUBGOAL_THEN (fun 
th -> REWRITE_TAC[
th]) o funpow 2 lhand o snd) THENL
   [MESON_TAC[]; ALL_TAC] THEN
  DISCH_THEN(REPEAT_TCL CHOOSE_THEN MP_TAC) THEN
  DISCH_THEN(CONJUNCTS_THEN2 SUBST1_TAC STRIP_ASSUME_TAC) THEN
  UNDISCH_TAC `(R:num->num->bool) a b` THEN
  MATCH_MP_TAC(TAUT `(b <=> a) ==> a ==> b`) THEN BINOP_TAC THENL
   [MATCH_MP_TAC 
MOD_UNIQ THEN EXISTS_TAC `b + p * s` THEN
    ASM_REWRITE_TAC[] THEN
    MATCH_MP_TAC 
DIV_UNIQ THEN EXISTS_TAC `r:num` THEN ASM_REWRITE_TAC[] THEN
    REWRITE_TAC[
ADD_AC; 
MULT_AC];
    MATCH_MP_TAC 
MOD_UNIQ THEN EXISTS_TAC `s:num` THEN ASM_REWRITE_TAC[] THEN
    MATCH_MP_TAC 
DIV_UNIQ THEN EXISTS_TAC `r + a * p 
EXP i` THEN
    CONJ_TAC THENL
     [REWRITE_TAC[
LEFT_ADD_DISTRIB; 
RIGHT_ADD_DISTRIB] THEN
      REWRITE_TAC[
ADD_AC; 
MULT_AC]; ALL_TAC] THEN
    MATCH_MP_TAC 
LTE_TRANS THEN EXISTS_TAC `p 
EXP i + a * p 
EXP i` THEN
    ASM_REWRITE_TAC[
LT_ADD_RCANCEL] THEN
    REWRITE_TAC[ARITH_RULE `p + q * p = (q + 1) * p`] THEN
    ASM_REWRITE_TAC[
LE_MULT_RCANCEL; 
EXP_EQ_0] THEN
    UNDISCH_TAC `a < p` THEN ARITH_TAC]);;
 
let RELPOW_LEMMA = prove
 (`
RELPOW n R x y <=>
        ?m p. 
prime p /\ x < p /\ y < p /\
              (?qx. m = x + p * qx) /\
              (?ry. ry < p 
EXP n /\ (m = ry + p 
EXP n * y)) /\
              !q. q < p 
EXP n
                  ==> 
primepow p q
                      ==> ?r a b s. (m = r + q * (a + p * (b + p * s))) /\
                                    r < q /\ a < p /\ b < p /\ R a b`,
 
let RTC_SIGMA = prove
 (`
RTC R x y <=>
        ?m p Q. 
primepow p Q /\ x < p /\ y < p /\
                (?s. m = x + p * s) /\
                (?r. r < Q /\ (m = r + Q * y)) /\
                !q. q < Q
                    ==> 
primepow p q
                        ==> ?r a b s. (m = r + q * (a + p * (b + p * s))) /\
                                      r < q /\ a < p /\ b < p /\ R a b`,