(* ========================================================================= *)
(* A HOL "by" tactic, doing Mizar-like things, trying something that is      *)
(* sufficient for HOL's basic rules, trying a few other things like          *)
(* arithmetic, and finally if all else fails using MESON_TAC[].              *)
(* ========================================================================= *)

(* ------------------------------------------------------------------------- *)
(* More refined net lookup that double-checks conditions like matchability.  *)
(* ------------------------------------------------------------------------- *)

let matching_enter tm y net =
  enter [] (tm,((fun tm' -> can (term_match [] tm) tm'),y)) net;;

let unconditional_enter (tm,y) net =
  enter [] (tm,((fun t -> true),y)) net;;

let conditional_enter (tm,condy) net =
  enter [] (tm,condy) net;;

let careful_lookup tm net =
  map snd (filter (fun (c,y) -> c tm) (lookup tm net));;

(* ------------------------------------------------------------------------- *)
(* Transform theorem list to simplify, eliminate redundant connectives and   *)
(* split the problem into (generally multiple) subproblems. Then, call the   *)
(* prover given as the first argument on each component.                     *)
(* ------------------------------------------------------------------------- *)

let SPLIT_THEN =
  let action_false th f oths = th
  and action_true th f oths = f oths
  and action_conj th f oths =
    f (CONJUNCT1 th :: CONJUNCT2 th :: oths)
  and action_disj th f oths =
    let th1 = f (ASSUME(lhand(concl th)) :: oths)
    and th2 = f (ASSUME(rand(concl th)) :: oths) in
    DISJ_CASES th th1 th2
  and action_taut tm =
    let pfun = PART_MATCH lhs (TAUT tm) in
    let prule th = EQ_MP (pfun (concl th)) th in
    lhand tm,(fun th f oths -> f(prule th :: oths)) in
  let enet = itlist unconditional_enter
    [`F`,action_false;
     `T`,action_true;
     `p /\ q`,action_conj;
     `p \/ q`,action_disj;
     action_taut `(p ==> q) <=> ~p \/ q`;
     action_taut `~F <=> T`;
     action_taut `~T <=> F`;
     action_taut  `~(~p) <=> p`;
     action_taut  `~(p /\ q) <=> ~p \/ ~q`;
     action_taut  `~(p \/ q) <=> ~p /\ ~q`;
     action_taut  `~(p ==> q) <=> p /\ ~q`;
     action_taut `p /\ F <=> F`;
     action_taut `F /\ p <=> F`;
     action_taut `p /\ T <=> p`;
     action_taut `T /\ p <=> p`;
     action_taut `p \/ F <=> p`;
     action_taut `F \/ p <=> p`;
     action_taut `p \/ T <=> T`;
     action_taut `T \/ p <=> T`]
    (let tm,act = action_taut `~(p <=> q) <=> p /\ ~q \/ ~p /\ q` in
     let cond tm = type_of(rand(rand tm)) = bool_ty in
     conditional_enter (tm,(cond,act))
        (let tm,act = action_taut `(p <=> q) <=> p /\ q \/ ~p /\ ~q` in
         let cond tm = type_of(rand tm) = bool_ty in
         conditional_enter (tm,(cond,act)) empty_net)) in
  fun prover ->
    let rec splitthen splat tosplit =
      match tosplit with
        [] -> prover (rev splat)
      | th::oths ->
          let funs = careful_lookup (concl th) enet in
          if funs = [] then splitthen (th::splat) oths
          else (hd funs) th (splitthen splat) oths in
    splitthen [];;

(* ------------------------------------------------------------------------- *)
(* A similar thing that also introduces Skolem constants (but not functions) *)
(* and does some slight first-order simplification like trivial miniscoping. *)
(* ------------------------------------------------------------------------- *)

let SPLIT_FOL_THEN =
  let action_false th f splat oths = th
  and action_true th f splat oths = f oths
  and action_conj th f splat oths =
    f (CONJUNCT1 th :: CONJUNCT2 th :: oths)
  and action_disj th f splat oths =
    let th1 = f (ASSUME(lhand(concl th)) :: oths)
    and th2 = f (ASSUME(rand(concl th)) :: oths) in
    DISJ_CASES th th1 th2
  and action_exists th f splat oths =
    let v,bod = dest_exists(concl th) in
    let vars = itlist (union o thm_frees) (oths @ splat) (thm_frees th) in
    let v' = variant vars v in
    let th' = ASSUME (subst [v',v] bod) in
    CHOOSE (v',th) (f (th'::oths))
  and action_taut tm =
    let pfun = PART_MATCH lhs (TAUT tm) in
    let prule th = EQ_MP (pfun (concl th)) th in
    lhand tm,(fun th f splat oths -> f(prule th :: oths))
  and action_fol tm =
    let pfun = PART_MATCH lhs (prove(tm,MESON_TAC[])) in
    let prule th = EQ_MP (pfun (concl th)) th in
    lhand tm,(fun th f splat oths -> f(prule th :: oths)) in
  let enet = itlist unconditional_enter
    [`F`,action_false;
     `T`,action_true;
     `p /\ q`,action_conj;
     `p \/ q`,action_disj;
     `?x. P x`,action_exists;
     action_taut `~(~p) <=> p`;
     action_taut `~(p /\ q) <=> ~p \/ ~q`;
     action_taut `~(p \/ q) <=> ~p /\ ~q`;
     action_fol `~(!x. P x) <=> (?x. ~(P x))`;
     action_fol `(!x. P x /\ Q x) <=> (!x. P x) /\ (!x. Q x)`]
    empty_net in
  fun prover ->
    let rec splitthen splat tosplit =
      match tosplit with
        [] -> prover (rev splat)
      | th::oths ->
          let funs = careful_lookup (concl th) enet in
          if funs = [] then splitthen (th::splat) oths
          else (hd funs) th (splitthen splat) splat oths in
    splitthen [];;

(* ------------------------------------------------------------------------- *)
(* Do the basic "semantic correlates" stuff.                                 *)
(* This is more like NNF than Mizar's version.                               *)
(* ------------------------------------------------------------------------- *)

let CORRELATE_RULE =
  PURE_REWRITE_RULE
   [TAUT `(a <=> b) <=> (a ==> b) /\ (b ==> a)`;
    TAUT `(a ==> b) <=> ~a \/ b`;
    DE_MORGAN_THM;
    TAUT `~(~a) <=> a`;
    TAUT `~T <=> F`;
    TAUT `~F <=> T`;
    TAUT `T /\ p <=> p`;
    TAUT `p /\ T <=> p`;
    TAUT `F /\ p <=> F`;
    TAUT `p /\ F <=> F`;
    TAUT `T \/ p <=> T`;
    TAUT `p \/ T <=> T`;
    TAUT `F \/ p <=> p`;
    TAUT `p \/ F <=> p`;
    GSYM CONJ_ASSOC; GSYM DISJ_ASSOC;
    prove(`(?x. P x) <=> ~(!x. ~(P x))`,MESON_TAC[])];;

(* ------------------------------------------------------------------------- *)
(* Look for an immediate contradictory pair of theorems. This is quadratic,  *)
(* but I doubt if that's much of an issue in practice. We could do something *)
(* fancier, but need to be careful over alpha-equivalence if sorting.        *)
(* ------------------------------------------------------------------------- *)

let THMLIST_CONTR_RULE =
  let CONTR_PAIR_THM = UNDISCH_ALL(TAUT `p ==> ~p ==> F`)
  and p_tm = `p:bool` in
  fun ths ->
    let ths_n,ths_p = partition (is_neg o concl) ths in
    let th_n = find (fun thn -> let tm = rand(concl thn) in
                                exists (aconv tm o concl) ths_p) ths_n in
    let tm = rand(concl th_n) in
    let th_p = find (aconv tm o concl) ths_p in
    itlist PROVE_HYP [th_p; th_n] (INST [tm,p_tm] CONTR_PAIR_THM);;

(* ------------------------------------------------------------------------- *)
(* Hence something similar to Mizar's "prechecker".                          *)
(* ------------------------------------------------------------------------- *)

let PRECHECKER_THEN prover =
  SPLIT_THEN (fun ths -> try THMLIST_CONTR_RULE ths
                         with Failure _ ->
                             SPLIT_FOL_THEN prover (map CORRELATE_RULE ths));;

(* ------------------------------------------------------------------------- *)
(* Lazy equations for use in congruence closure.                             *)
(* ------------------------------------------------------------------------- *)

type lazyeq = Lazy of (term * term) * (unit -> thm);;

let cache f =
  let store = ref TRUTH in
  fun () -> let th = !store in
            if is_eq(concl th) then th else
            let th' = f() in
            (store := th'; th');;

let lazy_eq th =
  Lazy((dest_eq(concl th)),(fun () -> th));;

let lazy_eval (Lazy(_,f)) = f();;

let REFL' t = Lazy((t,t),cache(fun () -> REFL t));;

let SYM' = fun (Lazy((t,t'),f)) -> Lazy((t',t),cache(fun () -> SYM(f ())));;

let TRANS' =
  fun (Lazy((s,s'),f)) (Lazy((t,t'),g)) ->
     if not(aconv s' t) then failwith "TRANS'"
     else Lazy((s,t'),cache(fun () -> TRANS (f ()) (g ())));;

let MK_COMB' =
  fun (Lazy((s,s'),f),Lazy((t,t'),g)) ->
     Lazy((mk_comb(s,t),mk_comb(s',t')),cache(fun () -> MK_COMB (f (),g ())));;

let concl' = fun (Lazy(tmp,g)) -> tmp;;

(* ------------------------------------------------------------------------- *)
(* Successors of a term, and predecessor function.                           *)
(* ------------------------------------------------------------------------- *)

let successors tm =
  try let f,x = dest_comb tm in [f;x]
  with Failure _ -> [];;

let predecessor_function tms =
  itlist (fun x -> itlist (fun y f -> (y |-> insert x (tryapplyd f y [])) f)
                          (successors x))
         tms undefined;;

(* ------------------------------------------------------------------------- *)
(* A union-find structure for equivalences, with theorems for edges.         *)
(* ------------------------------------------------------------------------- *)

type termnode = Nonterminal of lazyeq | Terminal of term * term list;;

type termequivalence = Equivalence of (term,termnode)func;;

let rec terminus (Equivalence f as eqv) a =
  match (apply f a) with
    Nonterminal(th) -> let b = snd(concl' th) in
                       let th',n = terminus eqv b in
                       TRANS' th th',n
  | Terminal(t,n) -> (REFL' t,n);;

let tryterminus eqv a =
  try terminus eqv a with Failure _ -> (REFL' a,[a]);;

let canonize eqv a = fst(tryterminus eqv a);;

let equate th (Equivalence f as eqv) =
  let a,b = concl' th in
  let (ath,na) = tryterminus eqv a
  and (bth,nb) = tryterminus eqv b in
  let a' = snd(concl' ath) and b' = snd(concl' bth) in
  Equivalence
   (if a' = b' then f else
    if length na <= length nb then
      let th' = TRANS' (TRANS' (SYM' ath) th) bth in
      (a' |-> Nonterminal th') ((b' |-> Terminal(b',na@nb)) f)
    else
      let th' = TRANS'(SYM'(TRANS' th bth)) ath in
      (b' |-> Nonterminal th') ((a' |-> Terminal(a',na@nb)) f));;

let unequal = Equivalence undefined;;

let equated (Equivalence f) = dom f;;

let prove_equal eqv (s,t) =
  let sth = canonize eqv s and tth = canonize eqv t in
  TRANS' (canonize eqv s) (SYM'(canonize eqv t));;

let equivalence_class eqv a = snd(tryterminus eqv a);;

(* ------------------------------------------------------------------------- *)
(* Prove composite terms equivalent based on 1-step congruence.              *)
(* ------------------------------------------------------------------------- *)

let provecongruent eqv (tm1,tm2) =
  let f1,x1 = dest_comb tm1
  and f2,x2 = dest_comb tm2 in
  MK_COMB'(prove_equal eqv (f1,f2),prove_equal eqv (x1,x2));;

(* ------------------------------------------------------------------------- *)
(* Merge equivalence classes given equation "th", using congruence closure.  *)
(* ------------------------------------------------------------------------- *)

let rec emerge th (eqv,pfn) =
  let s,t = concl' th in
  let sth = canonize eqv s and tth = canonize eqv t in
  let s' = snd(concl' sth) and t' = snd(concl' tth) in
  if s' = t' then (eqv,pfn) else
  let sp = tryapplyd pfn s' [] and tp = tryapplyd pfn t' [] in
  let eqv' = equate th eqv in
  let stth = canonize eqv' s' in
  let sttm = snd(concl' stth) in
  let pfn' = (sttm |-> union sp tp) pfn in
  itlist (fun (u,v) (eqv,pfn as eqp) ->
             try 
let thuv = provecongruent eqv (u,v) in emerge thuv eqp
             with Failure _ -> eqp)
         (allpairs (fun u v -> (u,v)) sp tp) (eqv',pfn');;
(* ------------------------------------------------------------------------- *) (* Find subterms of "tm" that contain as a subterm one of the "tms" terms. *) (* This is intended to be more efficient than the obvious "find_terms ...". *) (* ------------------------------------------------------------------------- *) let rec supersubterms tms tm = let ltms,tms' = if mem tm tms then [tm],filter (fun t -> t <> tm) tms else [],tms in if tms' = [] then ltms else let stms = try let l,r = dest_comb tm in union (supersubterms tms' l) (supersubterms tms' r) with Failure _ -> [] in if stms = [] then ltms else tm::stms;; (* ------------------------------------------------------------------------- *) (* Find an appropriate term universe for overall terms "tms". *) (* ------------------------------------------------------------------------- *) let term_universe tms = setify (itlist ((@) o supersubterms tms) tms []);; (* ------------------------------------------------------------------------- *) (* Congruence closure of "eqs" over term universe "tms". *) (* ------------------------------------------------------------------------- *) let congruence_closure tms eqs = let pfn = predecessor_function tms in let eqv,_ = itlist emerge eqs (unequal,pfn) in eqv;; (* ------------------------------------------------------------------------- *) (* Prove that "eq" follows from "eqs" by congruence closure. *) (* ------------------------------------------------------------------------- *) let CCPROVE eqs eq = let tps = dest_eq eq :: map concl' eqs in let otms = itlist (fun (x,y) l -> x::y::l) tps [] in let tms = term_universe(setify otms) in let eqv = congruence_closure tms eqs in prove_equal eqv (dest_eq eq);; (* ------------------------------------------------------------------------- *) (* Inference rule for `eq1 /\ ... /\ eqn ==> eq` *) (* ------------------------------------------------------------------------- *) let CONGRUENCE_CLOSURE tm = if is_imp tm then let eqs,eq = dest_imp tm in DISCH eqs (lazy_eval(CCPROVE (map lazy_eq (CONJUNCTS(ASSUME eqs))) eq)) else lazy_eval(CCPROVE [] tm);; (* ------------------------------------------------------------------------- *) (* Inference rule for contradictoriness of set of +ve and -ve eqns. *) (* ------------------------------------------------------------------------- *) let CONGRUENCE_CLOSURE_CONTR ths = let nths,pths = partition (is_neg o concl) ths in let peqs = filter (is_eq o concl) pths and neqs = filter (is_eq o rand o concl) nths in let tps = map (dest_eq o concl) peqs @ map (dest_eq o rand o concl) neqs in let otms = itlist (fun (x,y) l -> x::y::l) tps [] in let tms = term_universe(setify otms) in let eqv = congruence_closure tms (map lazy_eq peqs) in let prover th = let eq = dest_eq(rand(concl th)) in
let lth = prove_equal eqv eq in
    EQ_MP (EQF_INTRO th) (lazy_eval lth) in
  tryfind prover neqs;;
(* ------------------------------------------------------------------------- *) (* Attempt to prove equality between terms/formulas based on equivalence. *) (* Note that ABS sideconditions are only checked at inference-time... *) (* ------------------------------------------------------------------------- *) let ABS' v = fun (Lazy((s,t),f)) -> Lazy((mk_abs(v,s),mk_abs(v,t)), cache(fun () -> ABS v (f ())));; let ALPHA_EQ' s' t' = fun (Lazy((s,t),f) as inp) -> if s' = s & t' = t then inp else Lazy((s',t'), cache(fun () -> EQ_MP (ALPHA (mk_eq(s,t)) (mk_eq(s',t'))) (f ())));; let rec PROVE_EQUAL eqv (tm1,tm2 as tmp) = if tm1 = tm2 then REFL' tm1 else try prove_equal eqv tmp with Failure _ -> if is_comb tm1 & is_comb tm2 then let f1,x1 = dest_comb tm1 and f2,x2 = dest_comb tm2 in MK_COMB'(PROVE_EQUAL eqv (f1,f2),PROVE_EQUAL eqv (x1,x2)) else if is_abs tm1 & is_abs tm2 then let x1,bod1 = dest_abs tm1 and x2,bod2 = dest_abs tm2 in let gv = genvar(type_of x1) in ALPHA_EQ' tm1 tm2 (ABS' x1 (PROVE_EQUAL eqv (vsubst[gv,x1] bod1,vsubst[gv,x2] bod2))) else failwith "PROVE_EQUAL";; let PROVE_EQUIVALENT eqv tm1 tm2 = lazy_eval (PROVE_EQUAL eqv (tm1,tm2));; (* ------------------------------------------------------------------------- *) (* Complementary version for formulas. *) (* ------------------------------------------------------------------------- *) let PROVE_COMPLEMENTARY eqv th1 th2 = let tm1 = concl th1 and tm2 = concl th2 in if is_neg tm1 then let th = PROVE_EQUIVALENT eqv (rand tm1) tm2 in EQ_MP (EQF_INTRO th1) (EQ_MP (SYM th) th2) else if is_neg tm2 then let th = PROVE_EQUIVALENT eqv (rand tm2) tm1 in EQ_MP (EQF_INTRO th2) (EQ_MP (SYM th) th1) else failwith "PROVE_COMPLEMENTARY";; (* ------------------------------------------------------------------------- *) (* Check equality under equivalence with "env" mapping for first term. *) (* ------------------------------------------------------------------------- *) let rec test_eq eqv (tm1,tm2) env = if is_comb tm1 & is_comb tm2 then let f1,x1 = dest_comb tm1 and f2,x2 = dest_comb tm2 in test_eq eqv (f1,f2) env & test_eq eqv (x1,x2) env else if is_abs tm1 & is_abs tm2 then let x1,bod1 = dest_abs tm1 and x2,bod2 = dest_abs tm2 in let gv = genvar(type_of x1) in test_eq eqv (vsubst[gv,x1] bod1,vsubst[gv,x2] bod2) env else if is_var tm1 & can (rev_assoc tm1) env then test_eq eqv (rev_assoc tm1 env,tm2) [] else can (prove_equal eqv) (tm1,tm2);; (* ------------------------------------------------------------------------- *) (* Map a term to its equivalence class modulo equivalence *) (* ------------------------------------------------------------------------- *) let rec term_equivs eqv tm = let l = equivalence_class eqv tm in if l <> [tm] then l else if is_comb tm then let f,x = dest_comb tm in allpairs (curry mk_comb) (term_equivs eqv f) (term_equivs eqv x) else if is_abs tm then let v,bod = dest_abs tm in let gv = genvar(type_of v) in map (fun t -> alpha v (mk_abs(gv,t))) (term_equivs eqv (vsubst [gv,v] bod)) else [tm];; (* ------------------------------------------------------------------------- *) (* Replace "outer" universal variables with genvars. This is "outer" in the *) (* second sense, i.e. universals not in scope of an existential or negation. *) (* ------------------------------------------------------------------------- *) let rec GENSPEC th = let tm = concl th in if is_forall tm then let v = bndvar(rand tm) in let gv = genvar(type_of v) in GENSPEC(SPEC gv th) else if is_conj tm then let th1,th2 = CONJ_PAIR th in CONJ (GENSPEC th1) (GENSPEC th2) else if is_disj tm then let th1 = GENSPEC(ASSUME(lhand tm)) and th2 = GENSPEC(ASSUME(rand tm)) in let th3 = DISJ1 th1 (concl th2) and th4 = DISJ2 (concl th1) th2 in DISJ_CASES th th3 th4 else th;; (* ------------------------------------------------------------------------- *) (* Simple first-order matching. *) (* ------------------------------------------------------------------------- *) let rec term_fmatch vars vtm ctm env = if mem vtm vars then if can (rev_assoc vtm) env then term_fmatch vars (rev_assoc vtm env) ctm env else if aconv vtm ctm then env else (ctm,vtm)::env else if is_comb vtm & is_comb ctm then let fv,xv = dest_comb vtm and fc,xc = dest_comb ctm in term_fmatch vars fv fc (term_fmatch vars xv xc env) else if is_abs vtm & is_abs ctm then let xv,bodv = dest_abs vtm and xc,bodc = dest_abs ctm in let gv = genvar(type_of xv) and gc = genvar(type_of xc) in let gbodv = vsubst [gv,xv] bodv and gbodc = vsubst [gc,xc] bodc in term_fmatch (gv::vars) gbodv gbodc ((gc,gv)::env) else if vtm = ctm then env else failwith "term_fmatch";; let rec check_consistency env = match env with [] -> true | (c,v)::es -> forall (fun (c',v') -> v' <> v or c' = c) es;; let separate_insts env = let tyin = itlist (fun (c,v) -> type_match (type_of v) (type_of c)) env [] in let ifn(c,v) = (inst tyin c,inst tyin v) in let tmin = setify (map ifn env) in if check_consistency tmin then (tmin,tyin) else failwith "separate_insts";; let first_order_match vars vtm ctm env = let env' = term_fmatch vars vtm ctm env in if can separate_insts env' then env' else failwith "first_order_match";; (* ------------------------------------------------------------------------- *) (* Try to match all leaves to negation of auxiliary propositions. *) (* ------------------------------------------------------------------------- *) let matchleaves = let rec matchleaves vars vtm ctms env cont = if is_conj vtm then try matchleaves vars (rand vtm) ctms env cont with Failure _ -> matchleaves vars (lhand vtm) ctms env cont else if is_disj vtm then matchleaves vars (lhand vtm) ctms env (fun e -> matchleaves vars (rand vtm) ctms e cont) else tryfind (fun ctm -> cont (first_order_match vars vtm ctm env)) ctms in fun vars vtm ctms env -> matchleaves vars vtm ctms env (fun e -> e);; (* ------------------------------------------------------------------------- *) (* Now actually do the refutation once theorem is instantiated. *) (* ------------------------------------------------------------------------- *) let rec REFUTE_LEAVES eqv cths th = let tm = concl th in if is_conj tm then try REFUTE_LEAVES eqv cths (CONJUNCT1 th) with Failure _ -> REFUTE_LEAVES eqv cths (CONJUNCT2 th) else if is_disj tm then let th1 = REFUTE_LEAVES eqv cths (ASSUME(lhand tm)) and th2 = REFUTE_LEAVES eqv cths (ASSUME(rand tm)) in DISJ_CASES th th1 th2 else tryfind (PROVE_COMPLEMENTARY eqv th) cths;; (* ------------------------------------------------------------------------- *) (* Hence the Mizar "unifier" for given universal formula. *) (* ------------------------------------------------------------------------- *) let negate tm = if is_neg tm then rand tm else mk_neg tm;; let MIZAR_UNIFIER eqv ths th = let gth = GENSPEC th in let vtm = concl gth in let vars = subtract (frees vtm) (frees(concl th)) and ctms = map (negate o concl) ths in let allctms = itlist (union o term_equivs eqv) ctms [] in let env = matchleaves vars vtm allctms [] in let tmin,tyin = separate_insts env in REFUTE_LEAVES eqv ths (PINST tyin tmin gth);; (* ------------------------------------------------------------------------- *) (* Deduce disequalities of subterms and add symmetric versions at the end. *) (* ------------------------------------------------------------------------- *) let rec DISEQUALITIES ths = match ths with [] -> [] | th::oths -> let t1,t2 = dest_eq (rand(concl th)) in let f1,args1 = strip_comb t1 and f2,args2 = strip_comb t2 in if f1 <> f2 or length args1 <> length args2 then th::(GSYM th)::(DISEQUALITIES oths) else let zargs = zip args1 args2 in let diffs = filter (fun (a1,a2) -> a1 <> a2) zargs in if length diffs <> 1 then th::(GSYM th)::(DISEQUALITIES oths) else let eths = map (fun (a1,a2) -> if a1 = a2 then REFL a1 else ASSUME(mk_eq(a1,a2))) zargs in let th1 = rev_itlist (fun x y -> MK_COMB(y,x)) eths (REFL f1) in let th2 = MP (GEN_REWRITE_RULE I [GSYM CONTRAPOS_THM] (DISCH_ALL th1)) th in th::(GSYM th)::(DISEQUALITIES(th2::oths));; (* ------------------------------------------------------------------------- *) (* Get such a starting inequality from complementary literals. *) (* ------------------------------------------------------------------------- *) let ATOMINEQUALITIES th1 th2 = let t1 = concl th1 and t2' = concl th2 in let t2 = dest_neg t2' in let f1,args1 = strip_comb t1 and f2,args2 = strip_comb t2 in if f1 <> f2 or length args1 <> length args2 then [] else let zargs = zip args1 args2 in let diffs = filter (fun (a1,a2) -> a1 <> a2) zargs in if length diffs <> 1 then [] else let eths = map (fun (a1,a2) -> if a1 = a2 then REFL a1 else ASSUME(mk_eq(a1,a2))) zargs in let th3 = rev_itlist (fun x y -> MK_COMB(y,x)) eths (REFL f1) in let th4 = EQ_MP (TRANS th3 (EQF_INTRO th2)) th1 in let th5 = NOT_INTRO(itlist (DISCH o mk_eq) diffs th4) in [itlist PROVE_HYP [th1; th2] th5];; (* ------------------------------------------------------------------------- *) (* Basic prover. *) (* ------------------------------------------------------------------------- *) let BASIC_MIZARBY ths = try let nths,pths = partition (is_neg o concl) ths in let peqs,pneqs = partition (is_eq o concl) pths and neqs,nneqs = partition (is_eq o rand o concl) nths in let tps = map (dest_eq o concl) peqs @ map (dest_eq o rand o concl) neqs in let otms = itlist (fun (x,y) l -> x::y::l) tps [] in let tms = term_universe(setify otms) in let eqv = congruence_closure tms (map lazy_eq peqs) in let eqprover th = let s,t = dest_eq(rand(concl th)) in let th' = PROVE_EQUIVALENT eqv s t in EQ_MP (EQF_INTRO th) th' and contrprover thp thn = let th = PROVE_EQUIVALENT eqv (concl thp) (rand(concl thn)) in EQ_MP (TRANS th (EQF_INTRO thn)) thp in try tryfind eqprover neqs with Failure _ -> try tryfind (fun thp -> tryfind (contrprover thp) nneqs) pneqs with Failure _ -> let new_neqs = unions(allpairs ATOMINEQUALITIES pneqs nneqs) in let allths = pneqs @ nneqs @ peqs @ DISEQUALITIES(neqs @ new_neqs) in tryfind (MIZAR_UNIFIER eqv allths) (filter (is_forall o concl) allths) with Failure _ -> failwith "BASIC_MIZARBY";; (* ------------------------------------------------------------------------- *) (* Put it all together. *) (* ------------------------------------------------------------------------- *) let MIZAR_REFUTER ths = PRECHECKER_THEN BASIC_MIZARBY ths;; (* ------------------------------------------------------------------------- *) (* The Mizar prover for getting a conclusion from hypotheses. *) (* ------------------------------------------------------------------------- *) let MIZAR_BY = let pth = TAUT `(~p ==> F) <=> p` and p_tm = `p:bool` in fun ths tm -> let tm' = mk_neg tm in let th0 = ASSUME tm' in let th1 = MIZAR_REFUTER (th0::ths) in EQ_MP (INST [tm,p_tm] pth) (DISCH tm' th1);; (* ------------------------------------------------------------------------- *) (* As a standalone prover of formulas. *) (* ------------------------------------------------------------------------- *) let MIZAR_RULE tm = MIZAR_BY [] tm;; (* ------------------------------------------------------------------------- *) (* Some additional stuff for HOL. *) (* ------------------------------------------------------------------------- *) let HOL_BY = let BETASET_CONV = TOP_DEPTH_CONV GEN_BETA_CONV THENC REWRITE_CONV[IN_ELIM_THM] and BUILTIN_CONV tm = try EQT_ELIM(NUM_REDUCE_CONV tm) with Failure _ -> try EQT_ELIM(REAL_RAT_REDUCE_CONV tm) with Failure _ -> try ARITH_RULE tm with Failure _ -> try REAL_ARITH tm with Failure _ -> failwith "BUILTIN_CONV" in fun ths tm -> try MIZAR_BY ths tm with Failure _ -> try tryfind (fun th -> PART_MATCH I th tm) ths with Failure _ -> try let avs,bod = strip_forall tm in let gvs = map (genvar o type_of) avs in let gtm = vsubst (zip gvs avs) bod in let th = tryfind (fun th -> PART_MATCH I th gtm) ths in let gth = GENL gvs th in EQ_MP (ALPHA (concl gth) tm) gth with Failure _ -> try (let ths' = map BETA_RULE ths and th' = TOP_DEPTH_CONV BETA_CONV tm in let tm' = rand(concl th') in try EQ_MP (SYM th') (tryfind (fun th -> PART_MATCH I th tm') ths) with Failure _ -> try EQ_MP (SYM th') (BUILTIN_CONV tm') with Failure _ -> let ths'' = map (CONV_RULE BETASET_CONV) ths' and th'' = TRANS th' (BETASET_CONV tm') in EQ_MP (SYM th'') (prove(rand(concl th''),MESON_TAC ths''))) with Failure _ -> failwith "HOL_BY";; (* ------------------------------------------------------------------------- *) (* Standalone prover, breaking down an implication first. *) (* ------------------------------------------------------------------------- *) let HOL_RULE tm = try let l,r = dest_imp tm in DISCH l (HOL_BY (CONJUNCTS(ASSUME l)) r) with Failure _ -> HOL_BY [] tm;; (* ------------------------------------------------------------------------- *) (* Tautology examples (Pelletier problems). *) (* ------------------------------------------------------------------------- *) let prop_1 = time HOL_RULE `p ==> q <=> ~q ==> ~p`;; let prop_2 = time HOL_RULE `~ ~p <=> p`;; let prop_3 = time HOL_RULE `~(p ==> q) ==> q ==> p`;; let prop_4 = time HOL_RULE `~p ==> q <=> ~q ==> p`;; let prop_5 = time HOL_RULE `(p \/ q ==> p \/ r) ==> p \/ (q ==> r)`;; let prop_6 = time HOL_RULE `p \/ ~p`;; let prop_7 = time HOL_RULE `p \/ ~ ~ ~p`;; let prop_8 = time HOL_RULE `((p ==> q) ==> p) ==> p`;; let prop_9 = time HOL_RULE `(p \/ q) /\ (~p \/ q) /\ (p \/ ~q) ==> ~(~q \/ ~q)`;; let prop_10 = time HOL_RULE `(q ==> r) /\ (r ==> p /\ q) /\ (p ==> q /\ r) ==> (p <=> q)`;; let prop_11 = time HOL_RULE `p <=> p`;; let prop_12 = time HOL_RULE `((p <=> q) <=> r) <=> (p <=> (q <=> r))`;; let prop_13 = time HOL_RULE `p \/ q /\ r <=> (p \/ q) /\ (p \/ r)`;; let prop_14 = time HOL_RULE `(p <=> q) <=> (q \/ ~p) /\ (~q \/ p)`;; let prop_15 = time HOL_RULE `p ==> q <=> ~p \/ q`;; let prop_16 = time HOL_RULE `(p ==> q) \/ (q ==> p)`;; let prop_17 = time HOL_RULE `p /\ (q ==> r) ==> s <=> (~p \/ q \/ s) /\ (~p \/ ~r \/ s)`;; (* ------------------------------------------------------------------------- *) (* Congruence closure examples. *) (* ------------------------------------------------------------------------- *) time HOL_RULE `(f(f(f(f(f(x))))) = x) /\ (f(f(f(x))) = x) ==> (f(x) = x)`;; time HOL_RULE `(f(f(f(f(f(f(x)))))) = x) /\ (f(f(f(f(x)))) = x) ==> (f(f(x)) = x)`;; time HOL_RULE `(f a = a) ==> (f(f a) = a)`;; time HOL_RULE `(a = f a) /\ ((g b (f a))=(f (f a))) /\ ((g a b)=(f (g b a))) ==> (g a b = a)`;; time HOL_RULE `((s(s(s(s(s(s(s(s(s(s(s(s(s(s(s a)))))))))))))))=a) /\ ((s (s (s (s (s (s (s (s (s (s a))))))))))=a) /\ ((s (s (s (s (s (s a))))))=a) ==> (a = s a)`;; time HOL_RULE `(u = v) ==> (P u <=> P v)`;; time HOL_RULE `(b + c + d + e + f + g + h + i + j + k + l + m = m + l + k + j + i + h + g + f + e + d + c + b) ==> (a + b + c + d + e + f + g + h + i + j + k + l + m = a + m + l + k + j + i + h + g + f + e + d + c + b)`;; time HOL_RULE `(f(f(f(f(a)))) = a) /\ (f(f(f(f(f(f(a)))))) = a) /\ something(irrelevant) /\ (11 + 12 = 23) /\ (f(f(f(f(b)))) = f(f(f(f(f(f(f(f(f(f(c))))))))))) /\ ~(otherthing) /\ ~(f(a) = a) /\ ~(f(b) = b) /\ P(f(f(f(a)))) ==> P(f(a))`;; time HOL_RULE `((a = b) \/ (c = d)) /\ ((a = c) \/ (b = d)) ==> (a = d) \/ (b = c)`;; (* ------------------------------------------------------------------------- *) (* Various combined examples. *) (* ------------------------------------------------------------------------- *) time HOL_RULE `(f(f(f(f(a:A)))) = a) /\ (f(f(f(f(f(f(a)))))) = a) /\ something(irrelevant) /\ (11 + 12 = 23) /\ (f(f(f(f(b:A)))) = f(f(f(f(f(f(f(f(f(f(c))))))))))) /\ ~(otherthing) /\ ~(f(a) = a) /\ ~(f(b) = b) /\ P(f(a)) /\ ~(f(f(f(a))) = f(a)) ==> ?x. P(f(f(f(x))))`;; time HOL_RULE `(f(f(f(f(a:A)))) = a) /\ (f(f(f(f(f(f(a)))))) = a) /\ something(irrelevant) /\ (11 + 12 = 23) /\ (f(f(f(f(b:A)))) = f(f(f(f(f(f(f(f(f(f(c))))))))))) /\ ~(otherthing) /\ ~(f(a) = a) /\ ~(f(b) = b) /\ P(f(a)) ==> P(f(f(f(a))))`;; time HOL_RULE `(f(f(f(f(a:A)))) = a) /\ (f(f(f(f(f(f(a)))))) = a) /\ something(irrelevant) /\ (11 + 12 = 23) /\ (f(f(f(f(b:A)))) = f(f(f(f(f(f(f(f(f(f(c))))))))))) /\ ~(otherthing) /\ ~(f(a) = a) /\ ~(f(b) = b) /\ P(f(a)) ==> ?x. P(f(f(f(x))))`;; time HOL_RULE `(a = f a) /\ ((g b (f a))=(f (f a))) /\ ((g a b)=(f (g b a))) /\ (!x y. ~P (g x y)) ==> ~P(a)`;; time HOL_RULE `(!x y. x + y = y + x) /\ (1 + 2 = x) /\ (x = 3) ==> (3 = 2 + 1)`;; time HOL_RULE `(!x:num y. x + y = y + x) ==> (1 + 2 = 2 + 1)`;; time HOL_RULE `(!x:num y. ~(x + y = y + x)) ==> ~(1 + 2 = 2 + 1)`;; time HOL_RULE `(1 + 2 = 2 + 1) ==> ?x:num y. x + y = y + x`;; time HOL_RULE `(1 + x = x + 1) ==> ?x:num y. x + y = y + x`;; time (HOL_BY []) `?x. P x ==> !y. P y`;; (* ------------------------------------------------------------------------- *) (* Testing the HOL extensions. *) (* ------------------------------------------------------------------------- *) time HOL_RULE `1 + 1 = 2`;; time HOL_RULE `(\x. x + 1) 2 = 2 + 1`;; time HOL_RULE `!x. x < 2 ==> 2 * x <= 3`;; time HOL_RULE `y IN {x | x < 2} <=> y < 2`;; time HOL_RULE `(!x. (x = a) \/ x > a) ==> (1 + x = a) \/ 1 + x > a`;; time HOL_RULE `(\(x,y). x + y)(1,2) + 5 = (1 + 2) + 5`;; (* ------------------------------------------------------------------------- *) (* These and only these should go to MESON. *) (* ------------------------------------------------------------------------- *) print_string "***** Now the following (only) should use MESON"; print_newline();; time HOL_RULE `?x y. x = y`;; time HOL_RULE `(!Y X Z. p(X,Y) /\ p(Y,Z) ==> p(X,Z)) /\ (!Y X Z. q(X,Y) /\ q(Y,Z) ==> q(X,Z)) /\ (!Y X. q(X,Y) ==> q(Y,X)) /\ (!X Y. p(X,Y) \/ q(X,Y)) ==> p(a,b) \/ q(c,d)`;; time HOL_BY [PAIR_EQ] `(1,2) IN {(x,y) | x < y} <=> 1 < 2`;; HOL_BY [] `?x. !y. P x ==> P y`;;