(* ========================================================================= *)
(* Derived properties of provability.                                        *)
(* ========================================================================= *)
(* ------------------------------------------------------------------------- *)
(* The primitive basis, separated into its named components.                 *)
(* ------------------------------------------------------------------------- *)
let axiom_funcong = prove
 (`(!A s t. A |-- s === t --> Suc s === Suc t) /\
   (!A s t u v. A |-- s === t --> u === v --> s ++ u === t ++ v) /\
   (!A s t u v. A |-- s === t --> u === v --> s ** u === t ** v)`,
 
 
let axiom_predcong = prove
 (`(!A s t u v. A |-- s === t --> u === v --> s === u --> t === v) /\
   (!A s t u v. A |-- s === t --> u === v --> s << u --> t << v) /\
   (!A s t u v. A |-- s === t --> u === v --> s <<= u --> t <<= v)`,
 
 
(* ------------------------------------------------------------------------- *)
(* Some purely propositional schemas and derived rules.                      *)
(* ------------------------------------------------------------------------- *)
let imp_trans2 = prove
 (`!A p q r s. A |-- p --> q --> r /\ A |-- r --> s ==> A |-- p --> q --> s`,
 
 
let imp_contrf = prove
 (`!A p r. A |-- p --> negatef p --> r`,
  REPEAT GEN_TAC THEN REWRITE_TAC[negatef; negativef] THEN
  COND_CASES_TAC THEN POP_ASSUM STRIP_ASSUME_TAC THEN
  ASM_REWRITE_TAC[form_INJ] THEN
  ASM_MESON_TAC[
imp_contr; 
imp_swap]);;
 
 
let imp_true_rule = prove
 (`!A p q r. A |-- (p --> False) --> r /\ A |-- q --> r
             ==> A |-- (p --> q) --> r`,
 
 
let ante_disj = prove
 (`!A p q r. A |-- p --> r /\ A |-- q --> r
             ==> A |-- p || q --> r`,
 
 
(* ------------------------------------------------------------------------- *)
(* Equality rules.                                                           *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* First-order rules.                                                        *)
(* ------------------------------------------------------------------------- *)
let subalpha = prove
 (`!A x y p q. ((x = y) \/ ~(x 
IN FV(q)) /\ ~(y 
IN FV(p))) /\
               A |-- V x === V y --> p --> q
               ==> A |-- (!!x p) --> (!!y q)`,
  REPEAT GEN_TAC THEN ASM_CASES_TAC `x = y:num` THEN ASM_REWRITE_TAC[] THEN
  STRIP_TAC THENL
   [FIRST_X_ASSUM SUBST_ALL_TAC THEN
    ASM_MESON_TAC[genimp; modusponens; 
axiom_eqrefl];
    ALL_TAC] THEN
  MATCH_MP_TAC 
gen_right THEN ASM_REWRITE_TAC[
FV; 
IN_DELETE] THEN
  MATCH_MP_TAC subspec THEN EXISTS_TAC `V y` THEN
  ASM_REWRITE_TAC[
FVT; 
IN_SING]);;
 
 
(* ------------------------------------------------------------------------- *)
(* We'll perform induction on this measure.                                  *)
(* ------------------------------------------------------------------------- *)
let complexity = new_recursive_definition form_RECURSION
  `(complexity False = 1) /\
   (complexity True = 1) /\
   (!s t. complexity (s === t) = 1) /\
   (!s t. complexity (s << t) = 1) /\
   (!s t. complexity (s <<= t) = 1) /\
   (!p. complexity (Not p) = complexity p + 3) /\
   (!p q. complexity (p && q) = complexity p + complexity q + 6) /\
   (!p q. complexity (p || q) = complexity p + complexity q + 16) /\
   (!p q. complexity (p --> q) = complexity p + complexity q + 1) /\
   (!p q. complexity (p <-> q) = 2 * (complexity p + complexity q) + 9) /\
   (!x p. complexity (!!x p) = complexity p + 1) /\
   (!x p. complexity (??x p) = complexity p + 8)`;;
 
let isubst_general = prove
 (`!A p x v s t. A |-- s === t
                       --> formsubst ((x |-> s) v) p
                           --> formsubst ((x |-> t) v) p`,
  GEN_TAC THEN GEN_TAC THEN WF_INDUCT_TAC `complexity p` THEN
  POP_ASSUM MP_TAC THEN SPEC_TAC(`p:form`,`p:form`) THEN
  MATCH_MP_TAC form_INDUCT THEN REWRITE_TAC[formsubst; complexity] THEN
  REPEAT CONJ_TAC THENL
   [MESON_TAC[
imp_refl; 
add_assum];
    MESON_TAC[
imp_refl; 
add_assum];
    MESON_TAC[
imp_trans_chain_2; 
axiom_predcong; 
icongruence_general];
    MESON_TAC[
imp_trans_chain_2; 
axiom_predcong; 
icongruence_general];
    MESON_TAC[
imp_trans_chain_2; 
axiom_predcong; 
icongruence_general];
    X_GEN_TAC `p:form` THEN DISCH_THEN(K ALL_TAC) THEN
    DISCH_THEN(MP_TAC o SPEC `p --> False`) THEN
    REWRITE_TAC[complexity] THEN ANTS_TAC THENL [ARITH_TAC; ALL_TAC] THEN
    REWRITE_TAC[formsubst] THEN
    MESON_TAC[
axiom_not; 
iff_imp1; 
iff_imp2; 
imp_swap; 
imp_trans; 
imp_trans2];
    MAP_EVERY X_GEN_TAC [`p:form`; `q:form`] THEN DISCH_THEN(K ALL_TAC) THEN
    DISCH_THEN(MP_TAC o SPEC `(p --> q --> False) --> False`) THEN
    REWRITE_TAC[complexity] THEN ANTS_TAC THENL [ARITH_TAC; ALL_TAC] THEN
    REWRITE_TAC[formsubst] THEN
    MESON_TAC[
axiom_and; 
iff_imp1; 
iff_imp2; 
imp_swap; 
imp_trans; 
imp_trans2];
    MAP_EVERY X_GEN_TAC [`p:form`; `q:form`] THEN DISCH_THEN(K ALL_TAC) THEN
    DISCH_THEN(MP_TAC o SPEC `Not(Not p && Not q)`) THEN
    REWRITE_TAC[complexity] THEN ANTS_TAC THENL [ARITH_TAC; ALL_TAC] THEN
    REWRITE_TAC[formsubst] THEN
    MESON_TAC[
axiom_or; 
iff_imp1; 
iff_imp2; 
imp_swap; 
imp_trans; 
imp_trans2];
    MAP_EVERY X_GEN_TAC [`p:form`; `q:form`] THEN DISCH_THEN(K ALL_TAC) THEN
    DISCH_THEN(fun th -> MP_TAC(SPEC `p:form` th) THEN
                         MP_TAC(SPEC `q:form` th)) THEN
    REWRITE_TAC[ARITH_RULE `p < p + q + 1 /\ q < p + q + 1`] THEN
    MESON_TAC[
imp_mono_th; 
eq_sym; 
imp_trans; 
imp_trans_chain_2];
    MAP_EVERY X_GEN_TAC [`p:form`; `q:form`] THEN DISCH_THEN(K ALL_TAC) THEN
    DISCH_THEN(MP_TAC o SPEC `(p --> q) && (q --> p)`) THEN
    REWRITE_TAC[complexity] THEN ANTS_TAC THENL [ARITH_TAC; ALL_TAC] THEN
    REWRITE_TAC[formsubst] THEN
    MESON_TAC[
iff_def; 
iff_imp1; 
iff_imp2; 
imp_swap; 
imp_trans; 
imp_trans2];
    ALL_TAC;
    MAP_EVERY X_GEN_TAC [`x:num`; `p:form`] THEN DISCH_THEN(K ALL_TAC) THEN
    DISCH_THEN(MP_TAC o SPEC `Not(!!x (Not p))`) THEN
    REWRITE_TAC[complexity] THEN ANTS_TAC THENL [ARITH_TAC; ALL_TAC] THEN
    REWRITE_TAC[formsubst] THEN
    REPEAT(MATCH_MP_TAC 
MONO_FORALL THEN GEN_TAC) THEN
    REWRITE_TAC[
FV] THEN REPEAT LET_TAC THEN
    ASM_MESON_TAC[
axiom_exists; 
iff_imp1; 
iff_imp2; 
imp_swap; 
imp_trans;
                  
imp_trans2]] THEN
  MAP_EVERY X_GEN_TAC [`u:num`; `p:form`] THEN DISCH_THEN(K ALL_TAC) THEN
  REWRITE_TAC[ARITH_RULE `a < b + 1 <=> a <= b`] THEN DISCH_TAC THEN
  MAP_EVERY X_GEN_TAC [`v:num`; `i:num->term`; `s:term`; `t:term`] THEN
  MAP_EVERY ABBREV_TAC
   [`x = if ?y. y 
IN FV (!! u p) /\ u 
IN FVT ((v |-> s) i y)
         then 
VARIANT (
FV (formsubst ((u |-> V u) ((v |-> s) i)) p))
         else u`;
    `y = if ?y. y 
IN FV (!! u p) /\ u 
IN FVT ((v |-> t) i y)
         then 
VARIANT (
FV (formsubst ((u |-> V u) ((v |-> t) i)) p))
         else u`] THEN
  REWRITE_TAC[
LET_DEF; 
LET_END_DEF] THEN
  SUBGOAL_THEN `~(x 
IN FV(formsubst ((v |-> s) i) (!!u p))) /\
                ~(y 
IN FV(formsubst ((v |-> t) i) (!!u p)))`
  STRIP_ASSUME_TAC THENL
   [MAP_EVERY EXPAND_TAC ["x";
 
  "y"] THEN CONJ_TAC THEN
    (COND_CASES_TAC THENL
       [ALL_TAC; ASM_REWRITE_TAC[FORMSUBST_FV; IN_ELIM_THM]] THEN
      MATCH_MP_TAC NOT_IN_VARIANT THEN REWRITE_TAC[FV_FINITE] THEN
      REWRITE_TAC[SUBSET; FORMSUBST_FV; IN_ELIM_THM; FV; IN_DELETE] THEN
      REWRITE_TAC[valmod] THEN MESON_TAC[FVT; IN_SING]);
    ALL_TAC] THEN
  ASM_CASES_TAC `v:num = u` THENL
   [ASM_REWRITE_TAC[VALMOD_VALMOD_BASIC] THEN
    MATCH_MP_TAC add_assum THEN MATCH_MP_TAC subalpha THEN
    ASM_SIMP_TAC[LE_REFL] THEN
    ASM_CASES_TAC `y:num = x` THEN ASM_REWRITE_TAC[] THEN CONJ_TAC THENL
     [UNDISCH_TAC `~(x IN FV (formsubst ((v |-> s) i) (!! u p)))`;
      UNDISCH_TAC `~(y IN FV (formsubst ((v |-> t) i) (!! u p)))`] THEN
    ASM_REWRITE_TAC[FORMSUBST_FV; FV; IN_ELIM_THM; IN_DELETE] THEN
    MATCH_MP_TAC MONO_NOT THEN MATCH_MP_TAC MONO_EXISTS THEN
    X_GEN_TAC `w:num` THEN ASM_CASES_TAC `w:num = u` THEN
    ASM_REWRITE_TAC[VALMOD_BASIC; FVT; IN_SING] THEN
    ASM_REWRITE_TAC[valmod; FVT; IN_SING];
    ALL_TAC] THEN
  SUBGOAL_THEN
   `?z. ~(z IN FVT s) /\ ~(z IN FVT t) /\
        A |-- !!x (formsubst ((u |-> V x) ((v |-> s) i)) p)
              --> !!z (formsubst ((u |-> V z) ((v |-> s) i)) p) /\
        A |-- !!z (formsubst ((u |-> V z) ((v |-> t) i)) p)
              --> !!y (formsubst ((u |-> V y) ((v |-> t) i)) p)`
  MP_TAC THENL
   [ALL_TAC;
    DISCH_THEN(X_CHOOSE_THEN `z:num` STRIP_ASSUME_TAC) THEN
    MATCH_MP_TAC imp_trans THEN
    EXISTS_TAC `(!!z (formsubst ((v |-> s) ((u |-> V z) i)) p))
                     --> (!!z (formsubst ((v |-> t) ((u |-> V z) i)) p))` THEN
    CONJ_TAC THENL
     [MATCH_MP_TAC imp_trans THEN
      EXISTS_TAC `!!z (formsubst ((v |-> s) ((u |-> V z) i)) p
                       --> formsubst ((v |-> t) ((u |-> V z) i)) p)` THEN
      REWRITE_TAC[axiom_allimp] THEN
      ASM_SIMP_TAC[complexity; LE_REFL; FV; IN_UNION; gen_right];
      ALL_TAC] THEN
    FIRST_ASSUM(fun th -> ONCE_REWRITE_TAC[MATCH_MP VALMOD_SWAP th]) THEN
    ASM_MESON_TAC[imp_mono_th; modusponens]] THEN
  MP_TAC(SPEC
   `FVT(s) UNION FVT(t) UNION
    FV(formsubst ((u |-> V x) ((v |-> s) i)) p) UNION
    FV(formsubst ((u |-> V y) ((v |-> t) i)) p)` VARIANT_FINITE) THEN
  REWRITE_TAC[FINITE_UNION; FV_FINITE; FVT_FINITE] THEN
  W(fun (_,w) -> ABBREV_TAC(mk_comb(`(=) (z:num)`,lhand(rand(lhand w))))) THEN
  REWRITE_TAC[IN_UNION; DE_MORGAN_THM] THEN STRIP_TAC THEN
  EXISTS_TAC `z:num` THEN ASM_REWRITE_TAC[] THEN
  CONJ_TAC THEN MATCH_MP_TAC subalpha THEN ASM_SIMP_TAC[LE_REFL] THENL
   [ASM_CASES_TAC `z:num = x` THEN ASM_REWRITE_TAC[] THEN
    UNDISCH_TAC `~(x IN FV (formsubst ((v |-> s) i) (!! u p)))`;
    ASM_CASES_TAC `z:num = y` THEN ASM_REWRITE_TAC[] THEN
    UNDISCH_TAC `~(y IN FV (formsubst ((v |-> t) i) (!! u p)))`] THEN
  ASM_REWRITE_TAC[FORMSUBST_FV; FV; IN_ELIM_THM; IN_DELETE] THEN
  MATCH_MP_TAC MONO_NOT THEN MATCH_MP_TAC MONO_EXISTS THEN
  X_GEN_TAC `w:num` THEN ASM_CASES_TAC `w:num = u` THEN
  ASM_REWRITE_TAC[VALMOD_BASIC; FVT; IN_SING] THEN
  ASM_REWRITE_TAC[valmod; FVT; IN_SING]);;
let isubst = prove
 (`!A p x s t. A |-- s === t
                     --> formsubst (x |=> s) p --> formsubst (x |=> t) p`,
 
 
(* ------------------------------------------------------------------------- *)
(* To conclude cleanly, useful to have all variables.                        *)
(* ------------------------------------------------------------------------- *)
let TERMSUBST_TWICE_GENERAL = prove
 (`!x z t v s. ~(z 
IN FVT s)
               ==> (termsubst ((x |-> t) v) s =
                    termsubst ((z |-> t) v) (termsubst (x |=> V z) s))`,
  GEN_TAC THEN GEN_TAC THEN GEN_TAC THEN GEN_TAC THEN
  MATCH_MP_TAC term_INDUCT THEN
  REWRITE_TAC[termsubst; 
ASSIGN; valmod; 
FVT; 
IN_SING; 
IN_UNION] THEN
  MESON_TAC[termsubst; 
ASSIGN]);;
 
 
let spec = prove
 (`!A x p t. A |-- !!x p ==> A |-- formsubst (x |=> t) p`,
  MESON_TAC[ispec; modusponens]);;
 
 
(* ------------------------------------------------------------------------- *)
(* Monotonicity and the deduction theorem.                                   *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* A few more derived rules.                                                 *)
(* ------------------------------------------------------------------------- *)
let spec_right = prove
 (`!A p q x. A |-- p --> !!x q ==> A |-- p --> formsubst (x |=> t) q`,
 
 
let cong_add = prove
 (`!A s t u v. A |-- s === t /\ A |-- u === v ==> A |-- s ++ u === t ++ v`,
 
 
let cong_mul = prove
 (`!A s t u v. A |-- s === t /\ A |-- u === v ==> A |-- s ** u === t ** v`,
 
 
let cong_eq = prove
 (`!A s t u v. A |-- s === t /\ A |-- u === v ==> A |-- s === u <-> t === v`,
 
 
let cong_le = prove
 (`!A s t u v. A |-- s === t /\ A |-- u === v ==> A |-- s <<= u <-> t <<= v`,
 
 
let cong_lt = prove
 (`!A s t u v. A |-- s === t /\ A |-- u === v ==> A |-- s << u <-> t << v`,
 
 
let exists_intro = prove
 (`!A x t p. A |-- formsubst (x |=> t) p ==> A |-- ??x p`,
  MESON_TAC[iexists; modusponens]);;
 
 
let ichoose = prove
 (`!A x p q. A |-- !!x (p --> q) /\ ~(x 
IN FV q) ==> A |-- (??x p) --> q`,
  REPEAT STRIP_TAC THEN
  FIRST_ASSUM(MP_TAC o MATCH_MP 
spec_var) THEN
  DISCH_THEN(MP_TAC o SPEC `x:num` o MATCH_MP eximp) THEN
  MATCH_MP_TAC(REWRITE_RULE[
IMP_CONJ_ALT] 
imp_trans) THEN
  ASM_SIMP_TAC[impex]);;
 
 
(* ------------------------------------------------------------------------- *)
(* Some conversions for performing explicit substitution operations in what  *)
(* we hope is the common case where no variable renaming occurs.             *)
(* ------------------------------------------------------------------------- *)
let fv_theorems = ref
 [FV; FV_AXIOM; FV_DIAGONALIZE; FV_DIVIDES; FV_FINITE; FV_FIXPOINT; FV_FORM;
  FV_FORM1; FV_FREEFORM; FV_FREEFORM1; FV_FREETERM; FV_FREETERM1;
  FV_GNUMERAL; FV_GNUMERAL1; FV_GNUMERAL1'; FV_GSENTENCE;
  FV_HSENTENCE; FV_PRIME; FV_PRIMEPOW; FV_PRIMREC; FV_PRIMRECSTEP; FV_PROV;
  FV_PROV1; FV_QDIAG; FV_QSUBST; FV_RTC; FV_RTCP; FV_SUBSET_VARS; FV_TERM;
  FV_TERM1; FVT; FVT_NUMERAL];;
let IN_FV_RULE ths tm =
  try EQT_ELIM
       ((GEN_REWRITE_CONV TOP_DEPTH_CONV
             ([IN_UNION; IN_DELETE; NOT_IN_EMPTY; IN_INSERT] @
              ths @ !fv_theorems) THENC
         NUM_REDUCE_CONV) tm)
  with Failure _ -> ASSUME tm;;
let rec SAFE_FOR_RULE tm =
  try PART_MATCH I SAFE_FOR_V tm
  with Failure _ ->
  try let th1 = PART_MATCH lhand SAFE_FOR_ASSIGN tm in
      let th2 = IN_FV_RULE [] (rand(concl th1)) in
      EQ_MP (SYM th1) th2
  with Failure _ ->
      let th1 = PART_MATCH rand SAFE_FOR_VALMOD tm in
      let l,r = dest_conj(lhand(concl th1)) in
      let th2 = CONJ (SAFE_FOR_RULE l) (IN_FV_RULE [] r) in
      MP th1 th2;;
let VALMOD_CONV =
  GEN_REWRITE_CONV TOP_DEPTH_CONV [ASSIGN; VALMOD] THENC NUM_REDUCE_CONV;;
let rec TERMSUBST_CONV tm =
  (GEN_REWRITE_CONV I [CONJ TERMSUBST_NUMERAL (CONJUNCT1 termsubst)] ORELSEC
   (GEN_REWRITE_CONV I [el 1 (CONJUNCTS termsubst)] THENC
    VALMOD_CONV) ORELSEC
   (GEN_REWRITE_CONV I [el 2 (CONJUNCTS termsubst)] THENC
    RAND_CONV TERMSUBST_CONV) ORELSEC
   (GEN_REWRITE_CONV I [funpow 3 CONJUNCT2 termsubst] THENC
    BINOP_CONV TERMSUBST_CONV)) tm;;
let rec FORMSUBST_CONV tm =
  (GEN_REWRITE_CONV I
    [el 0 (CONJUNCTS formsubst); el 1 (CONJUNCTS formsubst)] ORELSEC
   (GEN_REWRITE_CONV I
    [el 2 (CONJUNCTS formsubst); el 3 (CONJUNCTS formsubst);
     el 4 (CONJUNCTS formsubst)] THENC BINOP_CONV TERMSUBST_CONV) ORELSEC
   (GEN_REWRITE_CONV I [el 5 (CONJUNCTS formsubst)] THENC
    RAND_CONV FORMSUBST_CONV) ORELSEC
   (GEN_REWRITE_CONV I
     [el 6 (CONJUNCTS formsubst); el 7 (CONJUNCTS formsubst);
      el 8 (CONJUNCTS formsubst); el 9 (CONJUNCTS formsubst)] THENC
    BINOP_CONV FORMSUBST_CONV) ORELSEC
   ((fun tm ->
     let th =
       try PART_MATCH (lhand o rand) (CONJUNCT1 FORMSUBST_SAFE_FOR) tm
       with Failure _ ->
           PART_MATCH (lhand o rand) (CONJUNCT2 FORMSUBST_SAFE_FOR) tm in
     MP th (SAFE_FOR_RULE (lhand(concl th)))) THENC
    RAND_CONV FORMSUBST_CONV)) tm;;
(* ------------------------------------------------------------------------- *)
(* Hence a more convenient specialization rule.                              *)
(* ------------------------------------------------------------------------- *)
let spec_var_rule th = MATCH_MP spec_var th;;
let spec_all_rule = repeat spec_var_rule;;
let instantiate_rule ilist th =
  let v_tm = `(|->):num->term->(num->term)->(num->term)` in
  let v = itlist (fun (t,x) v ->
        mk_comb(mk_comb(mk_comb(v_tm,mk_small_numeral x),t),v)) ilist `V` in
  CONV_RULE (RAND_CONV FORMSUBST_CONV)
            (SPEC v (MATCH_MP instantiation th));;
let specl_rule tms th =
  let avs = striplist (dest_binop `!!`) (rand(concl th)) in
  let vs = fst(chop_list(length tms) avs) in
  let ilist = map2 (fun t v -> (t,dest_small_numeral v)) tms vs in
  instantiate_rule ilist (funpow (length vs) spec_var_rule th);;
let spec_rule t th = specl_rule [t] th;;
let gen_rule t th = SPEC (mk_small_numeral t) (MATCH_MP gen th);;
let gens_tac ns (asl,w) =
  let avs,bod = nsplit dest_forall ns w in
  let nvs = map (curry mk_comb `V` o mk_small_numeral) ns in
  let bod' = subst (zip nvs avs) bod in
  let th = GENL avs (instantiate_rule (zip avs ns) (ASSUME bod')) in
  MATCH_MP_TAC (DISCH_ALL th) (asl,w);;
let gen_tac n = gens_tac [n];;