(******************************************************************************)
(* 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";;