(******************************************************************************)
(* FILE : rewrite_rules.ml *)
(* DESCRIPTION : Using axioms and lemmas as rewrite rules. *)
(* *)
(* READS FILES : <none> *)
(* WRITES FILES : <none> *)
(* *)
(* AUTHOR : R.J.Boulton *)
(* DATE : 14th May 1991 *)
(* *)
(* LAST MODIFIED : R.J.Boulton *)
(* DATE : 15th October 1992 *)
(* *)
(* LAST MODIFIED : P. Papapanagiotou (University of Edinburgh) *)
(* DATE : 2008 *)
(******************************************************************************)
(*----------------------------------------------------------------------------*)
(* is_permutative : term -> bool *)
(* *)
(* Determines whether or not an equation is permutative (the left-hand and *)
(* right-hand sides are instances of one another). A permutative equation may *)
(* cause looping when it is used for rewriting. *)
(*----------------------------------------------------------------------------*)
let is_permutative tm =
try (let (l,r) = dest_eq tm
in let bind1 = term_match [] l r
and bind2 = term_match [] r l
in true
) with Failure _ -> false;;
(*----------------------------------------------------------------------------*)
(* lex_smaller_term : term -> term -> bool *)
(* *)
(* Computes whether the first term is `alphabetically' smaller than the *)
(* second term. Used to avoid looping when rewriting with permutative rules. *)
(* *)
(* A constant is considered to be smaller than a variable which in turn is *)
(* considered to be smaller than an application. Two variables or two *)
(* constants are compared alphabetically by name. An application (f1 x1) is *)
(* considered to be smaller than another application (f2 x2) if either f1 is *)
(* smaller than f2, or f1 equals f2 and x1 is smaller than x2. *)
(*----------------------------------------------------------------------------*)
let rec lex_smaller_term tm1 tm2 =
try
(if (is_const tm1) then
(if (is_const tm2)
then let (name1,type1) = dest_const tm1
and (name2,type2) = dest_const tm2
in (if (type1 = type2)
then name1 < name2
else failwith "" )
else true)
else if (is_var tm1) then
(if (is_const tm2) then false
else if (is_var tm2)
then let (name1,type1) = dest_var tm1
and (name2,type2) = dest_var tm2
in (if (type1 = type2)
then name1 < name2
else failwith "" )
else true)
else if (is_comb tm1) then
(if (is_comb tm2)
then let (rator1,rand1) = dest_comb tm1
and (rator2,rand2) = dest_comb tm2
in (lex_smaller_term rator1 rator2) or
((rator1 = rator2) & (lex_smaller_term rand1 rand2))
else false)
else failwith ""
) with Failure _ -> failwith "lex_smaller_term";;
(*----------------------------------------------------------------------------*)
(* inst_eq_thm : ((term # term) list # (type # type) list) -> thm -> thm *)
(* *)
(* Instantiates a theorem (possibly having hypotheses) with a binding. *)
(* Assumes the conclusion is an equality, so that discharging then undisching *)
(* cannot cause parts of the conclusion to be moved into the hypotheses. *)
(*----------------------------------------------------------------------------*)
let inst_eq_thm (tm_bind,ty_bind) th =
let (insts,vars) = List.split tm_bind
in (UNDISCH_ALL o (SPECL insts) o (GENL vars) o
(INST_TYPE ty_bind) o DISCH_ALL) th;;
(*----------------------------------------------------------------------------*)
(* applicable_rewrites : term -> thm list *)
(* *)
(* Returns the results of rewriting the term with those rewrite rules that *)
(* are applicable to it. A rewrite rule is not applicable if it's permutative *)
(* and the rewriting does not produce an alphabetically smaller term. *)
(*----------------------------------------------------------------------------*)
let applicable_rewrites tm =
let applicable_rewrite tm th =
let conc = concl th
in let (_,tm_bind,ty_bind) = term_match [] (lhs conc) tm
in let instth = inst_eq_thm (tm_bind,ty_bind) th
in if (is_permutative conc)
then (let (l,r) = dest_eq (concl instth)
in if (lex_smaller_term r l)
then instth
else failwith "")
else instth
in mapfilter ((applicable_rewrite tm) o snd) !system_rewrites;;
(*----------------------------------------------------------------------------*)
(* ARGS_CONV : conv -> conv *)
(* *)
(* Applies a conversion to every argument of an application of the form *)
(* "f x1 ... xn". *)
(*----------------------------------------------------------------------------*)
let rec ARGS_CONV conv tm =
try (
((RATOR_CONV (ARGS_CONV conv)) THENC (RAND_CONV conv)) tm
) with Failure _ -> ALL_CONV tm;;
(*----------------------------------------------------------------------------*)
(* assump_inst_hyps : term list -> *)
(* term -> *)
(* term list -> *)
(* ((term # term) list # (type # type) list) *)
(* *)
(* Searches a list of hypotheses for one that matches the specified *)
(* assumption such that the variables instantiated are precisely those in the *)
(* list of variables given. If such a hypothesis is found, the binding *)
(* produced by the match is returned. *)
(*----------------------------------------------------------------------------*)
let rec assump_inst_hyps vars assump hyps =
try(let (_,tm_bind,ty_bind) = term_match [] (hd hyps) assump
in let bind = (tm_bind,ty_bind)
in if (set_eq vars (map snd (fst bind)))
then bind
else failwith "")
with Failure _ -> try (assump_inst_hyps vars assump (tl hyps))
with Failure _ -> failwith "assump_inst_hyps";;
(*----------------------------------------------------------------------------*)
(* assumps_inst_hyps : term list -> *)
(* term list -> *)
(* term list -> *)
(* ((term # term) list # (type # type) list) *)
(* *)
(* Searches a list of hypotheses and a list of assumptions for a pairing that *)
(* match (the assumption is an instance of the hypothesis) such that the *)
(* variables instantiated are precisely those in the list of variables given. *)
(* If such a pair is found, the binding produced by the match is returned. *)
(*----------------------------------------------------------------------------*)
let rec assumps_inst_hyps vars assumps hyps =
try (assump_inst_hyps vars (hd assumps) hyps)
with Failure _ -> try (assumps_inst_hyps vars (tl assumps) hyps)
with Failure _ -> failwith "assumps_inst_hyps";;
(*----------------------------------------------------------------------------*)
(* inst_frees_in_hyps : term list -> thm -> thm *)
(* *)
(* Takes a theorem (possibly with hypotheses) and computes a list of *)
(* variables that are free in the hypotheses but not in the conclusion. *)
(* If this list of variables is empty the original theorem is returned. *)
(* The function also takes a list of assumptions as another argument. Once it *)
(* has the list of variables it searches for an assumption and a hypothesis *)
(* such that the hypothesis matches the assumption binding precisely those *)
(* variables in the list. If this is successful the original theorem is *)
(* returned having had the variables in the list instantiated. *)
(*----------------------------------------------------------------------------*)
let inst_frees_in_hyps assumps th =
try (let hyps = hyp th
in let hyp_frees = setify (flat (map frees hyps))
in let vars = subtract hyp_frees (frees (concl th))
in if (vars = [])
then th
else let bind = assumps_inst_hyps vars assumps hyps
in inst_eq_thm bind th
) with Failure _ -> failwith "inst_frees_in_hyps";;
(*----------------------------------------------------------------------------*)
(* NOT_IMP_EQ_EQ_EQ_OR = |- (~x ==> (y = y')) = ((y \/ x) = (y' \/ x)) *)
(*----------------------------------------------------------------------------*)
let NOT_IMP_EQ_EQ_EQ_OR = prove
(`(~x ==> (y = y')) = ((y \/ x) = (y' \/ x))`,
BOOL_CASES_TAC `x:bool` THEN
BOOL_CASES_TAC `y:bool` THEN
BOOL_CASES_TAC `y':bool` THEN
REWRITE_TAC []);;
(*----------------------------------------------------------------------------*)
(* IMP_EQ_EQ_EQ_OR_NOT = |- (x ==> (y = y')) = ((y \/ ~x) = (y' \/ ~x)) *)
(*----------------------------------------------------------------------------*)
let IMP_EQ_EQ_EQ_OR_NOT = prove
(`(x ==> (y = y')) = ((y \/ ~x) = (y' \/ ~x))`,
BOOL_CASES_TAC `x:bool` THEN
BOOL_CASES_TAC `y:bool` THEN
BOOL_CASES_TAC `y':bool` THEN
REWRITE_TAC []);;
(*----------------------------------------------------------------------------*)
(* NOT_IMP_EQ_OR_EQ_EQ_OR_OR = *)
(* |- (~x ==> ((y \/ t) = (y' \/ t))) = ((y \/ (x \/ t)) = (y' \/ (x \/ t))) *)
(*----------------------------------------------------------------------------*)
let NOT_IMP_EQ_OR_EQ_EQ_OR_OR = prove
(`(~x ==> ((y \/ t) = (y' \/ t))) = ((y \/ (x \/ t)) = (y' \/ (x \/ t)))`,
BOOL_CASES_TAC `x:bool` THEN
BOOL_CASES_TAC `y:bool` THEN
BOOL_CASES_TAC `y':bool` THEN
BOOL_CASES_TAC `t:bool` THEN
REWRITE_TAC []);;
(*----------------------------------------------------------------------------*)
(* IMP_EQ_OR_EQ_EQ_OR_NOT_OR = *)
(* |- (x ==> ((y \/ t) = (y' \/ t))) = ((y \/ (~x \/ t)) = (y' \/ (~x \/ t))) *)
(*----------------------------------------------------------------------------*)
let IMP_EQ_OR_EQ_EQ_OR_NOT_OR = prove
(`(x ==> ((y \/ t) = (y' \/ t))) = ((y \/ (~x \/ t)) = (y' \/ (~x \/ t)))`,
BOOL_CASES_TAC `x:bool` THEN
BOOL_CASES_TAC `y:bool` THEN
BOOL_CASES_TAC `y':bool` THEN
BOOL_CASES_TAC `t:bool` THEN
REWRITE_TAC []);;
(*----------------------------------------------------------------------------*)
(* IMP_EQ_EQ_EQ_NOT_OR = |- (x ==> (t = t')) = ((~x \/ t) = (~x \/ t')) *)
(*----------------------------------------------------------------------------*)
let IMP_EQ_EQ_EQ_NOT_OR = prove
(`(x ==> (t = t')) = ((~x \/ t) = (~x \/ t'))`,
BOOL_CASES_TAC `x:bool` THEN
BOOL_CASES_TAC `t:bool` THEN
BOOL_CASES_TAC `t':bool` THEN
REWRITE_TAC []);;
(*----------------------------------------------------------------------------*)
(* IMP_NOT_EQ_EQ_EQ_OR = |- (~x ==> (t = t')) = ((x \/ t) = (x \/ t')) *)
(*----------------------------------------------------------------------------*)
let IMP_NOT_EQ_EQ_EQ_OR = prove
(`(~x ==> (t = t')) = ((x \/ t) = (x \/ t'))`,
BOOL_CASES_TAC `x:bool` THEN
BOOL_CASES_TAC `t:bool` THEN
BOOL_CASES_TAC `t':bool` THEN
REWRITE_TAC []);;
(*----------------------------------------------------------------------------*)
(* T_OR = |- T \/ t = T *)
(* OR_T = |- t \/ T = T *)
(* F_OR = |- F \/ t = t *)
(* OR_F = |- t \/ F = t *)
(*----------------------------------------------------------------------------*)
let [T_OR;OR_T;F_OR;OR_F;_] = CONJUNCTS (SPEC_ALL OR_CLAUSES);;
(*----------------------------------------------------------------------------*)
(* UNDER_DISJ_DISCH : term -> thm -> thm *)
(* *)
(* A, ~x |- y \/ t = y' \/ t A, x |- y \/ t = y' \/ t *)
(* ------------------------------- --------------------------------- *)
(* A |- y \/ x \/ t = y' \/ x \/ t A |- y \/ ~x \/ t = y' \/ ~x \/ t *)
(* *)
(* A, ~x |- y = y' A, x |- y = y' *)
(* --------------------- ----------------------- *)
(* A |- y \/ x = y' \/ x A |- y \/ ~x = y' \/ ~x *)
(* *)
(* The function assumes that y is a literal, so it is valid to test the LHS *)
(* of the theorem to see if it is a disjunction in order to determine which *)
(* rule to use. *)
(*----------------------------------------------------------------------------*)
let UNDER_DISJ_DISCH tm th =
try
(let rewrite =
if (is_disj (lhs (concl th)))
then if (is_neg tm)
then NOT_IMP_EQ_OR_EQ_EQ_OR_OR
else IMP_EQ_OR_EQ_EQ_OR_NOT_OR
else if (is_neg tm)
then NOT_IMP_EQ_EQ_EQ_OR
else IMP_EQ_EQ_EQ_OR_NOT
in CONV_RULE (REWR_CONV rewrite) (DISCH tm th)
) with Failure _ -> failwith "UNDER_DISJ_DISCH";;
(*----------------------------------------------------------------------------*)
(* OVER_DISJ_DISCH : term -> thm -> thm *)
(* *)
(* A, ~x |- t = t' A, x |- t = t' *)
(* --------------------- ----------------------- *)
(* A |- x \/ t = x \/ t' A |- ~x \/ t = ~x \/ t' *)
(*----------------------------------------------------------------------------*)
let OVER_DISJ_DISCH tm th =
try (let rewrite =
if (is_neg tm)
then IMP_NOT_EQ_EQ_EQ_OR
else IMP_EQ_EQ_EQ_NOT_OR
in CONV_RULE (REWR_CONV rewrite) (DISCH tm th)
) with Failure _ -> failwith "OVER_DISJ_DISCH";;
(*----------------------------------------------------------------------------*)
(* MULTI_DISJ_DISCH : (term list # term list) -> thm -> thm *)
(* *)
(* Examples: *)
(* *)
(* MULTI_DISJ_DISCH (["x1"; "x2"],["~x3"; "x4"]) x1, ~x3, x4, x2 |- y = y' *)
(* ---> *)
(* |- ~x1 \/ ~x2 \/ y \/ x3 \/ ~x4 = ~x1 \/ ~x2 \/ y' \/ x3 \/ ~x4 *)
(* *)
(* *)
(* MULTI_DISJ_DISCH (["x1"; "x2"],["~x3"; "x4"]) x1, ~x3, x4, x2 |- y = F *)
(* ---> *)
(* |- ~x1 \/ ~x2 \/ y \/ x3 \/ ~x4 = ~x1 \/ ~x2 \/ x3 \/ ~x4 *)
(* *)
(* *)
(* MULTI_DISJ_DISCH (["x1"; "x2"],["~x3"; "x4"]) x1, ~x3, x4, x2 |- y = T *)
(* ---> *)
(* |- ~x1 \/ ~x2 \/ y \/ x3 \/ ~x4 = T *)
(*----------------------------------------------------------------------------*)
let MULTI_DISJ_DISCH (overs,unders) th =
try
(let th1 = itlist UNDER_DISJ_DISCH unders th
in let tm1 = rhs (concl th1)
in let th2 =
if (try(is_T (fst (dest_disj tm1))) with Failure _ -> false) then
(CONV_RULE (RAND_CONV (REWR_CONV T_OR)) th1)
else if (try(is_F (fst (dest_disj tm1))) with Failure _ -> false) then
(CONV_RULE (RAND_CONV (REWR_CONV F_OR)) th1)
else th1
in let tm2 = rhs (concl th2)
in let rule =
if (is_T tm2) then CONV_RULE (RAND_CONV (REWR_CONV OR_T)) else I
in itlist (fun tm th -> rule (OVER_DISJ_DISCH tm th)) overs th2
) with Failure _ -> failwith "MULTI_DISJ_DISCH";;