(* ========================================================================== *)
(* FLYSPECK - BOOK FORMALIZATION                                              *)
(*                                                                            *)
(* Lemma: General Tactics                                                     *)
(* Author: Thomas C. Hales                                                    *)
(* Date: 2010-07-16                                                           *)
(* ========================================================================== *)

module Tactics = struct

(* ASSUMPTION LISTS *)

let ASM_STRIP_TAC = 
   FIRST[FIRST_X_ASSUM CHOOSE_TAC;
   FIRST_X_ASSUM (CONJUNCTS_THEN ASSUME_TAC);
	];;

let FIRST_RULE_ASSUM rule ttc = FIRST_ASSUM (ttc o rule );;

let FIRST_RULE_X_ASSUM rule ttc = FIRST_X_ASSUM (ttc o rule );;

(* INDUCTION *)

let INDUCT_SPEC_TAC vn = 
  let v = mk_var (vn,`:nat`) in
  SPEC_TAC (v,v) THEN INDUCT_TAC;;


(* ------------------------------------------------------------------ *)
(* SELECT ELIMINATION.
   SELECT_TAC should work whenever there is a single predicate selected.
   Something more sophisticated might be needed when there
   is (@)A and (@)B
   in the same formula.
   Useful for proving statements such as  `1 + (@x. (x=3)) = 4` *)
(* ------------------------------------------------------------------ *)

(* spec form of SELECT_AX *)
let select_thm select_fn select_exist =
  BETA_RULE (ISPECL [select_fn;select_exist]
             SELECT_AX);;

(* example *)
select_thm
    `\m. (X:num->bool) m /\ (!n. X n ==> m <= n)` `n:num`;;

let SELECT_EXIST = 
prove_by_refinement( `!(P:A->bool) Q. (?y. P y) /\ (!t. (P t ==> Q t)) ==> Q ((@) P)`,
(* {{{ proof *) [ REPEAT STRIP_TAC; ASSUME_TAC (ISPECL[`P:(A->bool)`;`y:A`] SELECT_AX); ASM_MESON_TAC[]; ]);;
(* }}} *) let SELECT_THM = MESON[SELECT_EXIST] `!(P:A->bool) Q. (((?y. P y) ==> (!t. (P t ==> Q t))) /\ ((~(?y. P y)) ==> (!t. Q t))) ==> Q ((@) P)`;; let SELECT_TAC =
let unbeta = 
prove( `!(P:A->bool) (Q:A->bool). (Q ((@) P)) <=> (\t. Q t) ((@) P)`,
MESON_TAC[]) in let unbeta_tac = CONV_TAC (HIGHER_REWRITE_CONV[unbeta] true) in unbeta_tac THEN (MATCH_MP_TAC SELECT_THM) THEN BETA_TAC THEN REPEAT STRIP_TAC;;
(* PATTERNS *) let (PAT_MATCH:term list -> (string*thm) -> bool) = fun pat sth -> not(search_thml (term_match[]) pat [sth] = []);; let (PAT_TAC2:tactic -> tactic -> term list -> (string * thm) -> tactic) = fun tac1 tac2 pat sth -> if PAT_MATCH pat sth then tac1 else tac2;; let (PAT_TAC:term list -> (string * thm) -> tactic) = PAT_TAC2 ALL_TAC NO_TAC;; let (FIRST_PAT_ASSUM: term list -> thm_tactic -> tactic) = fun pat ttac gl -> tryfind (fun sth -> (PAT_TAC pat sth THEN ttac (snd sth)) gl) (goal_asms gl);; let (FIRST_PAT_X_ASSUM:term list -> thm_tactic -> tactic) = fun pat ttac -> FIRST_PAT_ASSUM pat (fun th -> UNDISCH_THEN (concl th) ttac);; let (EVERY_PAT_ASSUM:term list -> thm_tactic -> tactic) = fun pat ttac gl -> let rl = map snd (filter (PAT_MATCH pat) (goal_asms gl)) in let tacl = map ttac rl in EVERY tacl gl;; (* Well defined functions *) (* Harrison's lemma *)
let WELLDEFINED_FUNCTION_1 = 
prove (`(?f:B->C. !x:A. f(s x) = t x) <=> (!x x'. s x = s x' ==> t x = t x')`,
MATCH_MP_TAC EQ_TRANS THEN EXISTS_TAC `?f:B->C. !y. !x:A. s x = y ==> f y = t x` THEN CONJ_TAC THENL [MESON_TAC[]; REWRITE_TAC[GSYM SKOLEM_THM] THEN MESON_TAC[]]);;
let WELLDEFINED_FUNCTION_2b = 
prove_by_refinement( `(?f:C->D. (!x:A y:B. P x y ==> f(s x y) = t x y)) <=> (!x x' y y'. (P x y /\ P x' y' /\ (s x y = s x' y')) ==> t x y = t x' y')`,
[ MATCH_MP_TAC EQ_TRANS ; EXISTS_TAC `?f:C->D. !z. !x:A y:B. ((P x y) /\ (s x y = z)) ==> f z = t x y`; CONJ_TAC; MESON_TAC[]; REWRITE_TAC[GSYM SKOLEM_THM]; MESON_TAC[]; ]);;
end;;