Tactics sat and satx reimplemented, several improvements
authorwebertj
Sun, 09 Oct 2005 17:06:03 +0200
changeset 17809 195045659c06
parent 17808 81c113e4d6fc
child 17810 3bdf516d93d8
Tactics sat and satx reimplemented, several improvements
NEWS
src/HOL/SAT.thy
src/HOL/Tools/cnf_funcs.ML
src/HOL/Tools/prop_logic.ML
src/HOL/Tools/sat_funcs.ML
src/HOL/ex/SAT_Examples.thy
--- a/NEWS	Sat Oct 08 23:43:15 2005 +0200
+++ b/NEWS	Sun Oct 09 17:06:03 2005 +0200
@@ -28,6 +28,11 @@
 "t = s" to False (by simproc "neq_simproc"). For backward compatibility
 this can be disabled by ML"reset use_neq_simproc".
 
+* Tactics 'sat' and 'satx' reimplemented, several improvements: goals
+no longer need to be stated as "<prems> ==> False", equivalences (i.e.
+"=" on type bool) are handled, variable names of the form "lit_<n>" are
+no longer reserved, significant speedup.
+
 
 New in Isabelle2005 (October 2005)
 ----------------------------------
--- a/src/HOL/SAT.thy	Sat Oct 08 23:43:15 2005 +0200
+++ b/src/HOL/SAT.thy	Sun Oct 09 17:06:03 2005 +0200
@@ -30,10 +30,10 @@
 
 ML {* structure sat = SATFunc(structure cnf = cnf); *}
 
-method_setup sat = {* Method.no_args (Method.SIMPLE_METHOD sat.sat_tac) *}
+method_setup sat = {* Method.no_args (Method.SIMPLE_METHOD (sat.sat_tac 1)) *}
   "SAT solver"
 
-method_setup satx = {* Method.no_args (Method.SIMPLE_METHOD sat.satx_tac) *}
+method_setup satx = {* Method.no_args (Method.SIMPLE_METHOD (sat.satx_tac 1)) *}
   "SAT solver (with definitional CNF)"
 
 end
--- a/src/HOL/Tools/cnf_funcs.ML	Sat Oct 08 23:43:15 2005 +0200
+++ b/src/HOL/Tools/cnf_funcs.ML	Sun Oct 09 17:06:03 2005 +0200
@@ -1,660 +1,579 @@
 (*  Title:      HOL/Tools/cnf_funcs.ML
     ID:         $Id$
     Author:     Alwen Tiu, QSL Team, LORIA (http://qsl.loria.fr)
+    Author:     Tjark Weber
     Copyright   2005
 
   Description:
-  This file contains functions and tactics to transform a formula into 
-  Conjunctive Normal Forms (CNF). 
+  This file contains functions and tactics to transform a formula into
+  Conjunctive Normal Form (CNF).
   A formula in CNF is of the following form:
 
-      (x11 | x12 | .. x1m) & ... & (xm1 | xm2 | ... | xmn)
+      (x11 | x12 | .. x1n) & ... & (xm1 | xm2 | ... | xmk)
+      False
+      True
 
-  where each xij is a literal (i.e., positive or negative propositional
-  variables).
-  This kind of formula will simply be referred to as CNF.
-  A disjunction of literals is referred to as "clause".
+  where each xij is a literal (a positive or negative atomic Boolean term),
+  i.e. the formula is a conjunction of disjunctions of literals, or
+  "False", or "True".
+
+  A (non-empty) disjunction of literals is referred to as "clause".
 
   For the purpose of SAT proof reconstruction, we also make use of another
   representation of clauses, which we call the "raw clauses".
   Raw clauses are of the form
 
-      (x1 ==> x2 ==> .. ==> xn ==> False)
+      x1 ==> x2 ==> .. ==> xn ==> False ,
 
   where each xi is a literal. Note that the above raw clause corresponds
   to the clause (x1' | ... | xn'), where each xi' is the negation normal
   form of ~xi.
-
-  Notes for current revision:
-  - the "definitional CNF transformation" (anything with prefix cnfx_ )
-    introduces new literals of the form (lit_i) where i is an integer.
-    For these functions to work, it is necessary that no free variables
-    which names are of the form lit_i appears in the formula being
-    transformed.
 *)
 
-
-(***************************************************************************)
-
 signature CNF =
 sig
-  val cnf_tac : Tactical.tactic
-  val cnf_thin_tac : Tactical.tactic
-  val cnfx_thin_tac : Tactical.tactic
-  val cnf_concl_tac : Tactical.tactic
-  val weakening_tac : int -> Tactical.tactic
-  val mk_cnf_thm : Sign.sg -> Term.term -> Thm.thm
-  val mk_cnfx_thm : Sign.sg -> Term.term ->  Thm.thm
-  val is_atm : Term.term -> bool
-  val is_lit : Term.term -> bool
-  val is_clause : Term.term -> bool
-  val is_raw_clause : Term.term -> bool
-  val cnf2raw_thm : Thm.thm -> Thm.thm
-  val cnf2raw_thms : Thm.thm list -> Thm.thm list
-  val cnf2prop : Thm.thm list -> (PropLogic.prop_formula * ((Term.term * int) list))
-end
+	val is_atom           : Term.term -> bool
+	val is_literal        : Term.term -> bool
+	val is_clause         : Term.term -> bool
+	val clause_is_trivial : Term.term -> bool
+
+	val is_raw_clause  : Term.term -> bool
+	val clause2raw_thm : Thm.thm -> Thm.thm
 
+	val weakening_tac : int -> Tactical.tactic  (* removes the first hypothesis of a subgoal *)
 
-(***************************************************************************)
+	val make_cnf_thm  : Theory.theory -> Term.term -> Thm.thm
+	val make_cnfx_thm : Theory.theory -> Term.term ->  Thm.thm
+	val cnf_rewrite_tac  : int -> Tactical.tactic  (* converts all prems of a subgoal to CNF *)
+	val cnfx_rewrite_tac : int -> Tactical.tactic  (* converts all prems of a subgoal to (almost) definitional CNF *)
+end;
 
 structure cnf : CNF =
 struct
 
-val cur_thy = the_context();
-val mk_disj = HOLogic.mk_disj;
-val mk_conj = HOLogic.mk_conj;
-val mk_imp  = HOLogic.mk_imp;
-val Not = HOLogic.Not;
-val false_const = HOLogic.false_const;
-val true_const = HOLogic.true_const;
-
-
-(* Index for new literals *)
-val lit_id = ref 0;
-
+(* string -> Thm.thm *)
 fun thm_by_auto G =
-    prove_goal cur_thy G (fn prems => [cut_facts_tac prems 1, Auto_tac]);
-
-(***************************************************************************)
+	prove_goal (the_context ()) G (fn prems => [cut_facts_tac prems 1, Auto_tac]);
 
-
-val cnf_eq_id = thm_by_auto "(P :: bool) = P";
-
-val cnf_eq_sym = thm_by_auto "(P :: bool) = Q ==> Q = P";
-
-val cnf_not_true_false = thm_by_auto "~True = False";
+(* Thm.thm *)
+val clause2raw_notE      = thm_by_auto "[| P; ~P |] ==> False";
+val clause2raw_not_disj  = thm_by_auto "[| ~P; ~Q |] ==> ~(P | Q)";
+val clause2raw_not_not   = thm_by_auto "P ==> ~~P";
 
-val cnf_not_false_true = thm_by_auto "~False = True";
-
-val cnf_imp2disj = thm_by_auto "(P --> Q) = (~P | Q)";
-
-val cnf_neg_conj = thm_by_auto "(~(P & Q)) = (~P | ~Q)";
-
-val cnf_neg_disj = thm_by_auto "(~(P | Q)) = (~P & ~Q)";
-
-val cnf_neg_imp = thm_by_auto "(~(P --> Q)) = (P & ~Q)";
+val iff_refl             = thm_by_auto "(P::bool) = P";
+val iff_trans            = thm_by_auto "[| (P::bool) = Q; Q = R |] ==> P = R";
+val conj_cong            = thm_by_auto "[| P = P'; Q = Q' |] ==> (P & Q) = (P' & Q')";
+val disj_cong            = thm_by_auto "[| P = P'; Q = Q' |] ==> (P | Q) = (P' | Q')";
 
-val cnf_double_neg = thm_by_auto "(~~P) = P";
- 
-val cnf_disj_conj = thm_by_auto "((P & Q) | R) = ((P | R) & (Q | R))";
-
-val cnf_disj_imp = thm_by_auto "((P --> Q) | R) = (~P | (Q | R))";
-
-val cnf_disj_disj = thm_by_auto "((P | Q) | R) = (P | (Q | R))";
-
-val cnf_disj_false = thm_by_auto "(False | P) = P";
-
-val cnf_disj_true = thm_by_auto "(True | P) = True";
-
-val cnf_disj_not_false = thm_by_auto "(~False | P) = True";
-
-val cnf_disj_not_true = thm_by_auto "(~True | P) = P";
-
-val cnf_eq_trans = thm_by_auto "[| ( (P::bool) = Q) ; Q = R |] ==> (P = R)";
+val make_nnf_imp         = thm_by_auto "[| (~P) = P'; Q = Q' |] ==> (P --> Q) = (P' | Q')";
+val make_nnf_iff         = thm_by_auto "[| P = P'; (~P) = NP; Q = Q'; (~Q) = NQ |] ==> (P = Q) = ((P' | NQ) & (NP | Q'))";
+val make_nnf_not_false   = thm_by_auto "(~False) = True";
+val make_nnf_not_true    = thm_by_auto "(~True) = False";
+val make_nnf_not_conj    = thm_by_auto "[| (~P) = P'; (~Q) = Q' |] ==> (~(P & Q)) = (P' | Q')";
+val make_nnf_not_disj    = thm_by_auto "[| (~P) = P'; (~Q) = Q' |] ==> (~(P | Q)) = (P' & Q')";
+val make_nnf_not_imp     = thm_by_auto "[| P = P'; (~Q) = Q' |] ==> (~(P --> Q)) = (P' & Q')";
+val make_nnf_not_iff     = thm_by_auto "[| P = P'; (~P) = NP; Q = Q'; (~Q) = NQ |] ==> (~(P = Q)) = ((P' | Q') & (NP | NQ))";
+val make_nnf_not_not     = thm_by_auto "P = P' ==> (~~P) = P'";
 
-val cnf_comb2eq = thm_by_auto "[| P = Q ; R = T |] ==> (P & R) = (Q & T)";
-
-val cnf_disj_sym = thm_by_auto "(P | Q) = (Q | P)";
-
-val cnf_cong_disj = thm_by_auto "(P = Q) ==> (P | R) = (Q | R)";
-
-val icnf_elim_disj1 = thm_by_auto "Q = R  ==> (~P | Q) = (P --> R)";
+val simp_TF_conj_True_l  = thm_by_auto "[| P = True; Q = Q' |] ==> (P & Q) = Q'";
+val simp_TF_conj_True_r  = thm_by_auto "[| P = P'; Q = True |] ==> (P & Q) = P'";
+val simp_TF_conj_False_l = thm_by_auto "P = False ==> (P & Q) = False";
+val simp_TF_conj_False_r = thm_by_auto "Q = False ==> (P & Q) = False";
+val simp_TF_disj_True_l  = thm_by_auto "P = True ==> (P | Q) = True";
+val simp_TF_disj_True_r  = thm_by_auto "Q = True ==> (P | Q) = True";
+val simp_TF_disj_False_l = thm_by_auto "[| P = False; Q = Q' |] ==> (P | Q) = Q'";
+val simp_TF_disj_False_r = thm_by_auto "[| P = P'; Q = False |] ==> (P | Q) = P'";
 
-val icnf_elim_disj2 = thm_by_auto "Q = R ==> (P | Q) = (~P --> R)";
-
-val icnf_neg_false1 = thm_by_auto "(~P) = (P --> False)";
+val make_cnf_disj_conj_l = thm_by_auto "[| (P | R) = PR; (Q | R) = QR |] ==> ((P & Q) | R) = (PR & QR)";
+val make_cnf_disj_conj_r = thm_by_auto "[| (P | Q) = PQ; (P | R) = PR |] ==> (P | (Q & R)) = (PQ & PR)";
 
-val icnf_neg_false2 = thm_by_auto "P = (~P --> False)";
+val make_cnfx_disj_ex_l = thm_by_auto "((EX (x::bool). P x) | Q) = (EX x. P x | Q)";
+val make_cnfx_disj_ex_r = thm_by_auto "(P | (EX (x::bool). Q x)) = (EX x. P | Q x)";
+val make_cnfx_newlit    = thm_by_auto "(P | Q) = (EX x. (P | x) & (Q | ~x))";
+val make_cnfx_ex_cong   = thm_by_auto "(ALL (x::bool). P x = Q x) ==> (EX x. P x) = (EX x. Q x)";
 
-val weakening_thm = thm_by_auto "[| P ; Q |] ==> Q";
+val weakening_thm        = thm_by_auto "[| P; Q |] ==> Q";
 
-val cnf_newlit = thm_by_auto 
-    "((P & Q) | R) = (EX (x :: bool). (~x | P) & (~x | Q) & (x | ~P | ~ Q) & (x | R))";
+val cnftac_eq_imp        = thm_by_auto "[| P = Q; P |] ==> Q";
 
-val cnf_all_ex = thm_by_auto 
-    "(ALL (x :: bool). (P x = Q x)) ==> (EX x. P x) = (EX x. Q x)";
-
-(* [| P ; ~P |] ==> False *)
-val cnf_notE = read_instantiate [("R", "False")] (rotate_prems 1 notE);
-
-val cnf_dneg = thm_by_auto "P ==> ~~P";
-
-val cnf_neg_disjI = thm_by_auto "[| ~P ; ~Q |] ==> ~(P | Q)";
-
-val cnf_eq_imp = thm_by_auto "[|((P::bool) = Q) ; P |] ==> Q";
-
-(***************************************************************************)
+(* Term.term -> bool *)
+fun is_atom (Const ("False", _))                                           = false
+  | is_atom (Const ("True", _))                                            = false
+  | is_atom (Const ("op &", _) $ _ $ _)                                    = false
+  | is_atom (Const ("op |", _) $ _ $ _)                                    = false
+  | is_atom (Const ("op -->", _) $ _ $ _)                                  = false
+  | is_atom (Const ("op =", Type ("fun", Type ("bool", []) :: _)) $ _ $ _) = false
+  | is_atom (Const ("Not", _) $ _)                                         = false
+  | is_atom _                                                              = true;
 
-fun is_atm (Const("Trueprop",_) $ x) = is_atm x
-  | is_atm (Const("==>",_) $ x $ y) = false
-  | is_atm (Const("False",_)) = false
-  | is_atm (Const("True", _)) = false
-  | is_atm (Const("op &",_) $ x $ y) = false
-  | is_atm (Const("op |",_) $ x $ y) = false
-  | is_atm (Const("op -->",_) $ x $ y) = false
-  | is_atm (Const("Not",_) $ x) = false
-  | is_atm t = true
+(* Term.term -> bool *)
+fun is_literal (Const ("Not", _) $ x) = is_atom x
+  | is_literal x                      = is_atom x;
 
-
-fun is_lit (Const("Trueprop",_) $ x) = is_lit x
-  | is_lit (Const("Not", _) $ x) = is_atm x
-  | is_lit t = is_atm t
+(* Term.term -> bool *)
+fun is_clause (Const ("op |", _) $ x $ y) = is_clause x andalso is_clause y
+  | is_clause x                           = is_literal x;
 
-fun is_clause (Const("Trueprop",_) $ x) = is_clause x
-  | is_clause (Const("op |", _) $ x $ y) = 
-          (is_clause x) andalso (is_clause y)
-  | is_clause t = is_lit t
+(* ------------------------------------------------------------------------- *)
+(* clause_is_trivial: a clause is trivially true if it contains both an atom *)
+(*      and the atom's negation                                              *)
+(* ------------------------------------------------------------------------- *)
+
+(* Term.term -> bool *)
 
-fun is_cnf (Const("Trueprop", _) $ x) = is_cnf x
-  | is_cnf (Const("op &",_) $ x $ y) = (is_cnf x) andalso (is_cnf y)
-  | is_cnf t = is_clause t
-
-
-(* Checking for raw clauses *)
-fun is_raw_clause (Const("Trueprop",_) $ x) = is_raw_clause x
-  | is_raw_clause (Const("==>",_) $ x $ 
-                   (Const("Trueprop",_) $ Const("False",_))) = is_lit x
-  | is_raw_clause (Const("==>",_) $ x $ y) = 
-        (is_lit x) andalso (is_raw_clause y)
-  | is_raw_clause t = false
-
-
+fun clause_is_trivial c =
+	let
+		(* Term.term -> Term.term list -> Term.term list *)
+		fun collect_literals (Const ("op |", _) $ x $ y) ls = collect_literals x (collect_literals y ls)
+		  | collect_literals x                           ls = x :: ls
+		(* Term.term -> Term.term *)
+		fun dual (Const ("Not", _) $ x) = x
+		  | dual x                      = HOLogic.Not $ x
+		(* Term.term list -> bool *)
+		fun has_duals []      = false
+		  | has_duals (x::xs) = (dual x) mem xs orelse has_duals xs
+	in
+		has_duals (collect_literals c [])
+	end;
 
-(* Translate a CNF clause into a raw clause *)
-fun cnf2raw_thm c =
-let val nc = c RS cnf_notE
-in
-rule_by_tactic (REPEAT_SOME (fn i => 
-               rtac cnf_dneg i 
-               ORELSE rtac cnf_neg_disjI i)) nc
-handle THM _ => nc
-end
+(* ------------------------------------------------------------------------- *)
+(* is_raw_clause: returns true iff the term is of the form                   *)
+(*        x1 ==> ... ==> xn ==> False ,                                      *)
+(*      with n >= 1, where each xi is a literal                              *)
+(* ------------------------------------------------------------------------- *)
+
+(* Term.term -> bool *)
 
-fun cnf2raw_thms nil = nil
-  | cnf2raw_thms (c::l) =
-    let val t = term_of (cprop_of c)
-    in
-       if (is_clause t) then (cnf2raw_thm c) :: cnf2raw_thms l
-       else cnf2raw_thms l
-    end
+fun is_raw_clause (Const ("==>", _) $ x $ y) =
+	is_literal x andalso
+		(y = HOLogic.mk_Trueprop HOLogic.false_const orelse is_raw_clause y)
+  | is_raw_clause _                          =
+	false;
 
+(* ------------------------------------------------------------------------- *)
+(* clause2raw_thm: translates a clause into a raw clause, i.e.               *)
+(*        x1 | ... | xn                                                      *)
+(*      (where each xi is a literal) is translated to                        *)
+(*        x1' ==> ... ==> xn' ==> False ,                                    *)
+(*      where each xi' is the negation normal form of ~xi.                   *)
+(* ------------------------------------------------------------------------- *)
 
-(* Translating HOL formula (in CNF) to PropLogic formula. Returns also an
-   association list, relating literals to their indices *)
+(* Thm.thm -> Thm.thm *)
 
-local
-  (* maps atomic formulas to variable numbers *)
-  val dictionary : ((Term.term * int) list) ref = ref nil;
-  val var_count = ref 0;
-  val pAnd = PropLogic.And;
-  val pOr = PropLogic.Or;
-  val pNot = PropLogic.Not;
-  val pFalse = PropLogic.False;
-  val pTrue = PropLogic.True;
-  val pVar = PropLogic.BoolVar;
-
-  fun mk_clause (Const("Trueprop",_) $ x) = mk_clause x
-    | mk_clause (Const("op |",_) $ x $ y) = pOr(mk_clause x, mk_clause y)
-    | mk_clause (Const("Not", _) $ x) = pNot (mk_clause x)
-    | mk_clause (Const("True",_)) = pTrue
-    | mk_clause (Const("False",_)) = pFalse
-    | mk_clause t =
-      let
-         val idx = AList.lookup op= (!dictionary) t
-      in
-         case idx of
-            (SOME x) => pVar x
-           | NONE =>
-             let
-                val new_var = inc var_count
-             in
-                dictionary := (t, new_var) :: (!dictionary);
-                pVar new_var
-             end
-      end
-
-   fun mk_clauses nil = pTrue
-     | mk_clauses (x::nil) = mk_clause x
-     | mk_clauses (x::l) = pAnd(mk_clause x, mk_clauses l)
+fun clause2raw_thm c =
+let
+	val thm1 = c RS clause2raw_notE  (* ~(x1 | ... | xn) ==> False *)
+	(* eliminates negated disjunctions from the i-th premise, possibly *)
+	(* adding new premises, then continues with the (i+1)-th premise   *)
+	(* Thm.thm -> int -> Thm.thm *)
+	fun not_disj_to_prem thm i =
+		if i > nprems_of thm then
+			thm
+		else
+			not_disj_to_prem (Seq.hd (REPEAT_DETERM (rtac clause2raw_not_disj i) thm)) (i+1)
+	val thm2 = not_disj_to_prem thm1 1  (* ~x1 ==> ... ==> ~xn ==> False *)
+	val thm3 = Seq.hd (TRYALL (rtac clause2raw_not_not) thm2)  (* x1' ==> ... ==> xn' ==> False *)
+in
+	thm3
+end;
 
-in
-   fun cnf2prop thms =
-   (
-     var_count := 0;
-     dictionary := nil;
-     (mk_clauses (map (fn x => term_of (cprop_of x)) thms), !dictionary)
-   )
-end
+(* ------------------------------------------------------------------------- *)
+(* inst_thm: instantiates a theorem with a list of terms                     *)
+(* ------------------------------------------------------------------------- *)
 
-
+(* Theory.theory -> Term.term list -> Thm.thm -> Thm.thm *)
 
-(* Instantiate a theorem with a list of terms *)
-fun inst_thm sign l thm = 
-  instantiate' [] (map (fn x => SOME (cterm_of sign x)) l) thm
-
-(* Tactic to remove the first hypothesis of the first subgoal. *) 
-fun weakening_tac i = (dtac weakening_thm i) THEN (atac (i+1));
+fun inst_thm thy ts thm =
+	instantiate' [] (map (SOME o cterm_of thy) ts) thm;
 
-(* Tactic for removing the n first hypotheses of the first subgoal. *)
-fun weakenings_tac 0 state = all_tac state
-  | weakenings_tac n state = ((weakening_tac  1) THEN (weakenings_tac (n-1))) state
-
+(* ------------------------------------------------------------------------- *)
+(*                         Naive CNF transformation                          *)
+(* ------------------------------------------------------------------------- *)
 
-(* 
-  Transform a formula into a "head" negation normal form, that is, 
-  the top level connective is not a negation, with the exception
-  of negative literals. Returns the pair of the head normal term with
-  the theorem corresponding to the transformation.
-*)
-fun head_nnf sign (Const("Not",_)  $ (Const("op &",_) $ x $ y)) =
-    let val t = mk_disj(Not $ x, Not $ y)
-        val neg_thm = inst_thm sign [x, y] cnf_neg_conj 
-    in
-        (t, neg_thm)
-    end
+(* ------------------------------------------------------------------------- *)
+(* make_nnf_thm: produces a theorem of the form t = t', where t' is the      *)
+(*      negation normal form (i.e. negation only occurs in front of atoms)   *)
+(*      of t; implications ("-->") and equivalences ("=" on bool) are        *)
+(*      eliminated (possibly causing an exponential blowup)                  *)
+(* ------------------------------------------------------------------------- *)
+
+(* Theory.theory -> Term.term -> Thm.thm *)
 
-  | head_nnf sign (Const("Not", _) $ (Const("op |",_) $ x $ y)) =
-    let val t = mk_conj(Not $ x, Not $ y)
-        val neg_thm =  inst_thm sign [x, y] cnf_neg_disj; 
-    in
-        (t, neg_thm)
-    end
-
-  | head_nnf sign (Const("Not", _) $ (Const("op -->",_) $ x $ y)) = 
-    let val t = mk_conj(x, Not $ y)
-        val neg_thm = inst_thm sign [x, y] cnf_neg_imp
-    in
-        (t, neg_thm)
-    end
-
-  | head_nnf sign (Const("Not",_) $ (Const("Not",_) $ x)) =
-    (x, inst_thm sign [x] cnf_double_neg)
-
-  | head_nnf sign (Const("Not",_) $ Const("True",_)) = 
-      (false_const, cnf_not_true_false)
-
-  | head_nnf sign (Const("Not",_) $ Const("False",_)) = 
-      (true_const, cnf_not_false_true)  
-
-  | head_nnf sign t = 
-    (t, inst_thm sign [t] cnf_eq_id)
-
-
-(***************************************************************************)
-(*                  Tactics for simple CNF transformation                  *)
+fun make_nnf_thm thy (Const ("op &", _) $ x $ y) =
+	let
+		val thm1 = make_nnf_thm thy x
+		val thm2 = make_nnf_thm thy y
+	in
+		conj_cong OF [thm1, thm2]
+	end
+  | make_nnf_thm thy (Const ("op |", _) $ x $ y) =
+	let
+		val thm1 = make_nnf_thm thy x
+		val thm2 = make_nnf_thm thy y
+	in
+		disj_cong OF [thm1, thm2]
+	end
+  | make_nnf_thm thy (Const ("op -->", _) $ x $ y) =
+	let
+		val thm1 = make_nnf_thm thy (HOLogic.Not $ x)
+		val thm2 = make_nnf_thm thy y
+	in
+		make_nnf_imp OF [thm1, thm2]
+	end
+  | make_nnf_thm thy (Const ("op =", Type ("fun", Type ("bool", []) :: _)) $ x $ y) =
+	let
+		val thm1 = make_nnf_thm thy x
+		val thm2 = make_nnf_thm thy (HOLogic.Not $ x)
+		val thm3 = make_nnf_thm thy y
+		val thm4 = make_nnf_thm thy (HOLogic.Not $ y)
+	in
+		make_nnf_iff OF [thm1, thm2, thm3, thm4]
+	end
+  | make_nnf_thm thy (Const ("Not", _) $ Const ("False", _)) =
+	make_nnf_not_false
+  | make_nnf_thm thy (Const ("Not", _) $ Const ("True", _)) =
+	make_nnf_not_true
+  | make_nnf_thm thy (Const ("Not", _) $ (Const ("op &", _) $ x $ y)) =
+	let
+		val thm1 = make_nnf_thm thy (HOLogic.Not $ x)
+		val thm2 = make_nnf_thm thy (HOLogic.Not $ y)
+	in
+		make_nnf_not_conj OF [thm1, thm2]
+	end
+  | make_nnf_thm thy (Const ("Not", _) $ (Const ("op |", _) $ x $ y)) =
+	let
+		val thm1 = make_nnf_thm thy (HOLogic.Not $ x)
+		val thm2 = make_nnf_thm thy (HOLogic.Not $ y)
+	in
+		make_nnf_not_disj OF [thm1, thm2]
+	end
+  | make_nnf_thm thy (Const ("Not", _) $ (Const ("op -->", _) $ x $ y)) =
+	let
+		val thm1 = make_nnf_thm thy x
+		val thm2 = make_nnf_thm thy (HOLogic.Not $ y)
+	in
+		make_nnf_not_imp OF [thm1, thm2]
+	end
+  | make_nnf_thm thy (Const ("Not", _) $ (Const ("op =", Type ("fun", Type ("bool", []) :: _)) $ x $ y)) =
+	let
+		val thm1 = make_nnf_thm thy x
+		val thm2 = make_nnf_thm thy (HOLogic.Not $ x)
+		val thm3 = make_nnf_thm thy y
+		val thm4 = make_nnf_thm thy (HOLogic.Not $ y)
+	in
+		make_nnf_not_iff OF [thm1, thm2, thm3, thm4]
+	end
+  | make_nnf_thm thy (Const ("Not", _) $ (Const ("Not", _) $ x)) =
+	let
+		val thm1 = make_nnf_thm thy x
+	in
+		make_nnf_not_not OF [thm1]
+	end
+  | make_nnf_thm thy t =
+	inst_thm thy [t] iff_refl;
 
-(* A naive procedure for CNF transformation:
-   Given any t, produce a theorem t = t', where t' is in
-   conjunction normal form 
-*)
-fun mk_cnf_thm sign (Const("Trueprop",_) $ x) = mk_cnf_thm sign x
-  | mk_cnf_thm sign (t as (Const(_,_))) = inst_thm sign [t] cnf_eq_id
-  | mk_cnf_thm sign (t as (Free(_,_))) =  inst_thm sign [t] cnf_eq_id
- 
-  | mk_cnf_thm sign (Const("op -->", _) $ x $ y) =
-       let val thm1 = inst_thm sign [x, y] cnf_imp2disj;
-           val thm2 = mk_cnf_thm sign (mk_disj(Not $ x, y));
-       in
-           cnf_eq_trans OF [thm1, thm2]
-       end
-
-  | mk_cnf_thm sign (Const("op &", _) $ x $ y) = 
-       let val cnf1 = mk_cnf_thm sign x;
-           val cnf2 = mk_cnf_thm sign y;
-       in
-           cnf_comb2eq OF [cnf1, cnf2]
-       end
-
-  | mk_cnf_thm sign (Const("Not",_) $ Const("True",_)) = 
-        cnf_not_true_false
+(* ------------------------------------------------------------------------- *)
+(* simp_True_False_thm: produces a theorem t = t', where t' is equivalent to *)
+(*      t, but simplified wrt. the following theorems:                       *)
+(*        (True & x) = x                                                     *)
+(*        (x & True) = x                                                     *)
+(*        (False & x) = False                                                *)
+(*        (x & False) = False                                                *)
+(*        (True | x) = True                                                  *)
+(*        (x | True) = True                                                  *)
+(*        (False | x) = x                                                    *)
+(*        (x | False) = x                                                    *)
+(*      No simplification is performed below connectives other than & and |. *)
+(*      Optimization: The right-hand side of a conjunction (disjunction) is  *)
+(*      simplified only if the left-hand side does not simplify to False     *)
+(*      (True, respectively).                                                *)
+(* ------------------------------------------------------------------------- *)
 
-  | mk_cnf_thm sign (Const("Not",_) $ Const("False",_)) = 
-        cnf_not_false_true
-
-  | mk_cnf_thm sign (t as (Const("Not", _) $ x)) =
-      ( 
-       if (is_atm x) then inst_thm sign [t] cnf_eq_id
-       else
-         let val (t1, hn_thm) = head_nnf sign t
-             val cnf_thm = mk_cnf_thm sign t1
-         in
-             cnf_eq_trans OF [hn_thm, cnf_thm]
-         end
-       ) 
-
-  | mk_cnf_thm sign (Const("op |",_) $ (Const("op &", _) $ p $ q) $ r) =
-       let val thm1 = inst_thm sign [p, q, r] cnf_disj_conj;
-           val thm2 = mk_cnf_thm sign (mk_conj(mk_disj(p, r), mk_disj(q,r)));
-       in
-          cnf_eq_trans OF [thm1, thm2]
-       end
-
-  | mk_cnf_thm sign (Const("op |",_) $ (Const("op |", _) $ p $ q) $ r) =
-       let val thm1 = inst_thm sign [p,q,r] cnf_disj_disj;
-           val thm2 = mk_cnf_thm sign (mk_disj(p, mk_disj(q,r)));
-       in
-          cnf_eq_trans OF [thm1, thm2]
-       end
+(* Theory.theory -> Term.term -> Thm.thm *)
 
-  | mk_cnf_thm sign (Const("op |",_) $ (Const("op -->", _) $ p $ q) $ r) =                       
-       let val thm1 = inst_thm sign [p,q,r] cnf_disj_imp;
-           val thm2 = mk_cnf_thm sign (mk_disj(Not $ p, mk_disj(q, r)));
-       in
-           cnf_eq_trans OF [thm1, thm2]
-       end
-
-  | mk_cnf_thm sign (Const("op |",_) $ Const("False",_) $ p) =
-       let val thm1 = inst_thm sign [p] cnf_disj_false;
-           val thm2 = mk_cnf_thm sign p
-       in
-           cnf_eq_trans OF [thm1, thm2]
-       end
-
-  | mk_cnf_thm sign (Const("op |",_) $ Const("True",_) $ p) =
-       inst_thm sign [p] cnf_disj_true
-
-  | mk_cnf_thm sign (Const("op |",_) $ (Const("Not",_) $ Const("True",_)) $ p) =
-       let val thm1 = inst_thm sign [p] cnf_disj_not_true;
-           val thm2 = mk_cnf_thm sign p
-       in
-           cnf_eq_trans OF [thm1, thm2]
-       end
-
-  | mk_cnf_thm sign (Const("op |",_) $ (Const("Not",_) $ Const("False",_)) $ p) =
-       inst_thm sign [p] cnf_disj_not_false
+fun simp_True_False_thm thy (Const ("op &", _) $ x $ y) =
+	let
+		val thm1 = simp_True_False_thm thy x
+		val x'   = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) thm1
+	in
+		if x' = HOLogic.false_const then
+			simp_TF_conj_False_l OF [thm1]  (* (x & y) = False *)
+		else
+			let
+				val thm2 = simp_True_False_thm thy y
+				val y'   = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) thm2
+			in
+				if x' = HOLogic.true_const then
+					simp_TF_conj_True_l OF [thm1, thm2]  (* (x & y) = y' *)
+				else if y' = HOLogic.false_const then
+					simp_TF_conj_False_r OF [thm2]  (* (x & y) = False *)
+				else if y' = HOLogic.true_const then
+					simp_TF_conj_True_r OF [thm1, thm2]  (* (x & y) = x' *)
+				else
+					conj_cong OF [thm1, thm2]  (* (x & y) = (x' & y') *)
+			end
+	end
+  | simp_True_False_thm thy (Const ("op |", _) $ x $ y) =
+	let
+		val thm1 = simp_True_False_thm thy x
+		val x'   = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) thm1
+	in
+		if x' = HOLogic.true_const then
+			simp_TF_disj_True_l OF [thm1]  (* (x | y) = True *)
+		else
+			let
+				val thm2 = simp_True_False_thm thy y
+				val y'   = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) thm2
+			in
+				if x' = HOLogic.false_const then
+					simp_TF_disj_False_l OF [thm1, thm2]  (* (x | y) = y' *)
+				else if y' = HOLogic.true_const then
+					simp_TF_disj_True_r OF [thm2]  (* (x | y) = True *)
+				else if y' = HOLogic.false_const then
+					simp_TF_disj_False_r OF [thm1, thm2]  (* (x | y) = x' *)
+				else
+					disj_cong OF [thm1, thm2]  (* (x | y) = (x' | y') *)
+			end
+	end
+  | simp_True_False_thm thy t =
+	inst_thm thy [t] iff_refl;  (* t = t *)
 
-  | mk_cnf_thm sign (t as (Const("op |",_) $ p $ q)) = 
-       if (is_lit p) then
-          (
-            if (is_clause t) then inst_thm sign [t] cnf_eq_id
-            else 
-             let val thm1 = inst_thm sign [p, q] cnf_disj_sym;
-                 val thm2 = mk_cnf_thm sign (mk_disj(q, p))
-             in
-                cnf_eq_trans OF [thm1, thm2]
-             end
-          )
-       else 
-            let val (u, thm1) = head_nnf sign p;
-                val thm2 = inst_thm sign [p,u,q] cnf_cong_disj;
-                val thm3 = mk_cnf_thm sign (mk_disj(u, q))
-            in
-                cnf_eq_trans OF [(thm1 RS thm2), thm3]
-            end
-
- | mk_cnf_thm sign t = inst_thm sign [t] cnf_eq_id
-    (* error ("I don't know how to handle the formula " ^ 
-                          (Sign.string_of_term sign t))
-    *)
+(* ------------------------------------------------------------------------- *)
+(* make_cnf_thm: given any HOL term 't', produces a theorem t = t', where t' *)
+(*      is in conjunction normal form.  May cause an exponential blowup      *)
+(*      in the length of the term.                                           *)
+(* ------------------------------------------------------------------------- *)
 
-fun term_of_thm c = term_of (cprop_of c)
-
-
-(* Transform a given list of theorems (thms) into CNF *)
-
-fun mk_cnf_thms sg nil = nil
-  | mk_cnf_thms sg (x::l) = 
-    let val t = term_of_thm x
-    in
-      if (is_clause t) then x :: mk_cnf_thms sg l
-      else 
-       let val thm1 = mk_cnf_thm sg t
-           val thm2 = cnf_eq_imp OF [thm1, x]
-       in 
-           thm2 :: mk_cnf_thms sg l
-       end
-    end
-
-
-(* Count the number of hypotheses in a formula *)
-fun num_of_hyps (Const("Trueprop", _) $ x) = num_of_hyps x
-  | num_of_hyps (Const("==>",_) $ x $ y) = 1 + (num_of_hyps y)
-  | num_of_hyps t = 0
+(* Theory.theory -> Term.term -> Thm.thm *)
 
-(* Tactic for converting to CNF (in primitive form): 
-   it takes the first subgoal of the proof state, transform all its
-   hypotheses into CNF (in primivite form) and remove the original 
-   hypotheses.
-*)
-fun cnf_thin_tac state =
-let val sg = Thm.sign_of_thm state
+fun make_cnf_thm thy t =
+let
+	(* Term.term -> Thm.thm *)
+	fun make_cnf_thm_from_nnf (Const ("op &", _) $ x $ y) =
+		let
+			val thm1 = make_cnf_thm_from_nnf x
+			val thm2 = make_cnf_thm_from_nnf y
+		in
+			conj_cong OF [thm1, thm2]
+		end
+	  | make_cnf_thm_from_nnf (Const ("op |", _) $ x $ y) =
+		let
+			(* produces a theorem "(x' | y') = t'", where x', y', and t' are in CNF *)
+			fun make_cnf_disj_thm (Const ("op &", _) $ x1 $ x2) y' =
+				let
+					val thm1 = make_cnf_disj_thm x1 y'
+					val thm2 = make_cnf_disj_thm x2 y'
+				in
+					make_cnf_disj_conj_l OF [thm1, thm2]  (* ((x1 & x2) | y') = ((x1 | y')' & (x2 | y')') *)
+				end
+			  | make_cnf_disj_thm x' (Const ("op &", _) $ y1 $ y2) =
+				let
+					val thm1 = make_cnf_disj_thm x' y1
+					val thm2 = make_cnf_disj_thm x' y2
+				in
+					make_cnf_disj_conj_r OF [thm1, thm2]  (* (x' | (y1 & y2)) = ((x' | y1)' & (x' | y2)') *)
+				end
+			  | make_cnf_disj_thm x' y' =
+				inst_thm thy [HOLogic.mk_disj (x', y')] iff_refl  (* (x' | y') = (x' | y') *)
+			val thm1     = make_cnf_thm_from_nnf x
+			val thm2     = make_cnf_thm_from_nnf y
+			val x'       = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) thm1
+			val y'       = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) thm2
+			val disj_thm = disj_cong OF [thm1, thm2]  (* (x | y) = (x' | y') *)
+		in
+			iff_trans OF [disj_thm, make_cnf_disj_thm x' y']
+		end
+	  | make_cnf_thm_from_nnf t =
+		inst_thm thy [t] iff_refl
+	(* convert 't' to NNF first *)
+	val nnf_thm  = make_nnf_thm thy t
+	val nnf      = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) nnf_thm
+	(* then simplify wrt. True/False (this should preserve NNF) *)
+	val simp_thm = simp_True_False_thm thy nnf
+	val simp     = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) simp_thm
+	(* finally, convert to CNF (this should preserve the simplification) *)
+	val cnf_thm  = make_cnf_thm_from_nnf simp
 in
-case (prems_of state) of 
-  [] => Seq.empty
-| (subgoal::_) => 
-  let 
-    val n = num_of_hyps (strip_all_body subgoal);
-    val tac1 = METAHYPS (fn l => cut_facts_tac (mk_cnf_thms sg l) 1) 1
-  in
-    (tac1 THEN weakenings_tac n THEN (REPEAT (etac conjE 1)) ) state
-  end
-end
-
-(* Tactic for converting to CNF (in primitive form), keeping 
-   the original hypotheses. *)
-
-fun cnf_tac state =
-let val sg = Thm.sign_of_thm state
-in
-case (prems_of state) of 
-  [] => Seq.empty
-| (subgoal::_) => 
-   METAHYPS (fn l => cut_facts_tac (mk_cnf_thms sg l) 1 
-                    THEN (REPEAT (etac conjE 1)) ) 1 state
-end
-
-
-(***************************************************************************)
-(*            CNF transformation by introducing new literals               *)
+	iff_trans OF [iff_trans OF [nnf_thm, simp_thm], cnf_thm]
+end;
 
-(*** IMPORTANT: 
-  This transformation uses variables of the form "lit_i", where i is a natural
-  number. For the transformation to work, these variables must not already
-  occur freely in the formula being transformed.
-***)
-
-fun ext_conj x p q r =
-   mk_conj(
-    mk_disj(Not $ x, p),
-    mk_conj(
-      mk_disj(Not $ x, q),
-      mk_conj(
-        mk_disj(x, mk_disj(Not $ p, Not $ q)),
-        mk_disj(x, r)
-      )
-    )
-   )
-
+(* ------------------------------------------------------------------------- *)
+(*            CNF transformation by introducing new literals                 *)
+(* ------------------------------------------------------------------------- *)
 
-(* Transform to CNF in primitive forms, possibly introduce extra variables *)
-fun mk_cnfx_thm sign (Const("Trueprop",_) $ x) = mk_cnfx_thm sign x 
-  | mk_cnfx_thm sign (t as (Const(_,_)))  = inst_thm sign [t] cnf_eq_id
-  | mk_cnfx_thm sign (t as (Free(_,_)))  = inst_thm sign [t] cnf_eq_id
-  | mk_cnfx_thm sign (Const("op -->", _) $ x $ y)  =
-       let val thm1 = inst_thm sign [x, y] cnf_imp2disj;
-           val thm2 = mk_cnfx_thm sign (mk_disj(Not $ x, y)) 
-       in
-           cnf_eq_trans OF [thm1, thm2]
-       end
+(* ------------------------------------------------------------------------- *)
+(* make_cnfx_thm: given any HOL term 't', produces a theorem t = t', where   *)
+(*      t' is almost in conjunction normal form, except that conjunctions    *)
+(*      and existential quantifiers may be nested.  (Use e.g. 'REPEAT_DETERM *)
+(*      (etac exE i ORELSE etac conjE i)' afterwards to normalize.)  May     *)
+(*      introduce new (existentially bound) literals.  Note: the current     *)
+(*      implementation calls 'make_nnf_thm', causing an exponential blowup   *)
+(*      in the case of nested equivalences.                                  *)
+(* ------------------------------------------------------------------------- *)
 
-  | mk_cnfx_thm sign (Const("op &", _) $ x $ y)  = 
-       let val cnf1 = mk_cnfx_thm sign x
-           val cnf2 = mk_cnfx_thm sign y
-       in
-           cnf_comb2eq OF [cnf1, cnf2]
-       end
-
-  | mk_cnfx_thm sign (Const("Not",_) $ Const("True",_)) = 
-        cnf_not_true_false
+(* Theory.theory -> Term.term -> Thm.thm *)
 
-  | mk_cnfx_thm sign (Const("Not",_) $ Const("False",_))  = 
-        cnf_not_false_true
-
-  | mk_cnfx_thm sign (t as (Const("Not", _) $ x))  =
-      ( 
-       if (is_atm x) then inst_thm sign [t] cnf_eq_id
-       else
-         let val (t1, hn_thm) = head_nnf sign t
-             val cnf_thm = mk_cnfx_thm sign t1 
-         in
-             cnf_eq_trans OF [hn_thm, cnf_thm]
-         end
-       ) 
-
-  | mk_cnfx_thm sign (Const("op |",_) $ (Const("op &", _) $ p $ q) $ r)  =
-      if (is_lit r) then
-        let val thm1 = inst_thm sign [p, q, r] cnf_disj_conj
-            val thm2 = mk_cnfx_thm sign (mk_conj(mk_disj(p, r), mk_disj(q,r)))
-        in
-           cnf_eq_trans OF [thm1, thm2]
-        end
-      else cnfx_newlit sign p q r 
-
-  | mk_cnfx_thm sign (Const("op |",_) $ (Const("op |", _) $ p $ q) $ r)  =
-       let val thm1 = inst_thm sign [p,q,r] cnf_disj_disj
-           val thm2 = mk_cnfx_thm sign (mk_disj(p, mk_disj(q,r))) 
-       in
-          cnf_eq_trans OF [thm1, thm2]
-       end
-
-  | mk_cnfx_thm sign (Const("op |",_) $ (Const("op -->", _) $ p $ q) $ r) =                       
-       let val thm1 = inst_thm sign [p,q,r] cnf_disj_imp
-           val thm2 = mk_cnfx_thm sign (mk_disj(Not $ p, mk_disj(q, r))) 
-       in
-           cnf_eq_trans OF [thm1, thm2]
-       end
-
-  | mk_cnfx_thm sign (Const("op |",_) $ Const("False",_) $ p)  =
-       let val thm1 = inst_thm sign [p] cnf_disj_false;
-           val thm2 = mk_cnfx_thm sign p 
-       in
-           cnf_eq_trans OF [thm1, thm2]
-       end
-
-  | mk_cnfx_thm sign (Const("op |",_) $ Const("True",_) $ p)  =
-       inst_thm sign [p] cnf_disj_true
-
-  | mk_cnfx_thm sign (Const("op |",_) $ (Const("Not",_) $ Const("True",_)) $ p)  =
-       let val thm1 = inst_thm sign [p] cnf_disj_not_true;
-           val thm2 = mk_cnfx_thm sign p 
-       in
-           cnf_eq_trans OF [thm1, thm2]
-       end
-
-  | mk_cnfx_thm sign (Const("op |",_) $ (Const("Not",_) $ Const("False",_)) $ p)  =
-       inst_thm sign [p] cnf_disj_not_false
-
-  | mk_cnfx_thm sign (t as (Const("op |",_) $ p $ q))  = 
-       if (is_lit p) then
-          (
-            if (is_clause t) then inst_thm sign [t] cnf_eq_id
-            else 
-             let val thm1 = inst_thm sign [p, q] cnf_disj_sym
-                 val thm2 = mk_cnfx_thm sign (mk_disj(q, p)) 
-             in
-                cnf_eq_trans OF [thm1, thm2]
-             end
-          )
-       else 
-            let val (u, thm1) = head_nnf sign p
-                val thm2 = inst_thm sign [p,u,q] cnf_cong_disj
-                val thm3 = mk_cnfx_thm sign (mk_disj(u, q)) 
-            in
-                cnf_eq_trans OF [(thm1 RS thm2), thm3]
-            end
+fun make_cnfx_thm thy t =
+let
+	val var_id = ref 0  (* properly initialized below *)
+	(* unit -> Term.term *)
+	fun new_free () =
+		Free ("cnfx_" ^ string_of_int (inc var_id), HOLogic.boolT)
+	(* Term.term -> Thm.thm *)
+	fun make_cnfx_thm_from_nnf (Const ("op &", _) $ x $ y) =
+		let
+			val thm1 = make_cnfx_thm_from_nnf x
+			val thm2 = make_cnfx_thm_from_nnf y
+		in
+			conj_cong OF [thm1, thm2]
+		end
+	  | make_cnfx_thm_from_nnf (Const ("op |", _) $ x $ y) =
+		if is_clause x andalso is_clause y then
+			inst_thm thy [HOLogic.mk_disj (x, y)] iff_refl
+		else if is_literal y orelse is_literal x then let
+			(* produces a theorem "(x' | y') = t'", where x', y', and t' are *)
+			(* almost in CNF, and x' or y' is a literal                      *)
+			fun make_cnfx_disj_thm (Const ("op &", _) $ x1 $ x2) y' =
+				let
+					val thm1 = make_cnfx_disj_thm x1 y'
+					val thm2 = make_cnfx_disj_thm x2 y'
+				in
+					make_cnf_disj_conj_l OF [thm1, thm2]  (* ((x1 & x2) | y') = ((x1 | y')' & (x2 | y')') *)
+				end
+			  | make_cnfx_disj_thm x' (Const ("op &", _) $ y1 $ y2) =
+				let
+					val thm1 = make_cnfx_disj_thm x' y1
+					val thm2 = make_cnfx_disj_thm x' y2
+				in
+					make_cnf_disj_conj_r OF [thm1, thm2]  (* (x' | (y1 & y2)) = ((x' | y1)' & (x' | y2)') *)
+				end
+			  | make_cnfx_disj_thm (Const ("Ex", _) $ x') y' =
+				let
+					val thm1 = inst_thm thy [x', y'] make_cnfx_disj_ex_l   (* ((Ex x') | y') = (Ex (x' | y')) *)
+					val var  = new_free ()
+					val thm2 = make_cnfx_disj_thm (betapply (x', var)) y'  (* (x' | y') = body' *)
+					val thm3 = forall_intr (cterm_of thy var) thm2         (* !!v. (x' | y') = body' *)
+					val thm4 = strip_shyps (thm3 COMP allI)                (* ALL v. (x' | y') = body' *)
+					val thm5 = strip_shyps (thm4 RS make_cnfx_ex_cong)     (* (EX v. (x' | y')) = (EX v. body') *)
+				in
+					iff_trans OF [thm1, thm5]  (* ((Ex x') | y') = (Ex v. body') *)
+				end
+			  | make_cnfx_disj_thm x' (Const ("Ex", _) $ y') =
+				let
+					val thm1 = inst_thm thy [x', y'] make_cnfx_disj_ex_r   (* (x' | (Ex y')) = (Ex (x' | y')) *)
+					val var  = new_free ()
+					val thm2 = make_cnfx_disj_thm x' (betapply (y', var))  (* (x' | y') = body' *)
+					val thm3 = forall_intr (cterm_of thy var) thm2         (* !!v. (x' | y') = body' *)
+					val thm4 = strip_shyps (thm3 COMP allI)                (* ALL v. (x' | y') = body' *)
+					val thm5 = strip_shyps (thm4 RS make_cnfx_ex_cong)     (* (EX v. (x' | y')) = (EX v. body') *)
+				in
+					iff_trans OF [thm1, thm5]  (* (x' | (Ex y')) = (EX v. body') *)
+				end
+			  | make_cnfx_disj_thm x' y' =
+				inst_thm thy [HOLogic.mk_disj (x', y')] iff_refl  (* (x' | y') = (x' | y') *)
+			val thm1     = make_cnfx_thm_from_nnf x
+			val thm2     = make_cnfx_thm_from_nnf y
+			val x'       = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) thm1
+			val y'       = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) thm2
+			val disj_thm = disj_cong OF [thm1, thm2]  (* (x | y) = (x' | y') *)
+		in
+			iff_trans OF [disj_thm, make_cnfx_disj_thm x' y']
+		end else let  (* neither 'x' nor 'y' is a literal: introduce a fresh variable *)
+			val thm1 = inst_thm thy [x, y] make_cnfx_newlit     (* (x | y) = EX v. (x | v) & (y | ~v) *)
+			val var  = new_free ()
+			val body = HOLogic.mk_conj (HOLogic.mk_disj (x, var), HOLogic.mk_disj (y, HOLogic.Not $ var))
+			val thm2 = make_cnfx_thm_from_nnf body              (* (x | v) & (y | ~v) = body' *)
+			val thm3 = forall_intr (cterm_of thy var) thm2      (* !!v. (x | v) & (y | ~v) = body' *)
+			val thm4 = strip_shyps (thm3 COMP allI)             (* ALL v. (x | v) & (y | ~v) = body' *)
+			val thm5 = strip_shyps (thm4 RS make_cnfx_ex_cong)  (* (EX v. (x | v) & (y | ~v)) = (EX v. body') *)
+		in
+			iff_trans OF [thm1, thm5]
+		end
+	  | make_cnfx_thm_from_nnf t =
+		inst_thm thy [t] iff_refl
+	(* convert 't' to NNF first *)
+	val nnf_thm  = make_nnf_thm thy t
+	val nnf      = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) nnf_thm
+	(* then simplify wrt. True/False (this should preserve NNF) *)
+	val simp_thm = simp_True_False_thm thy nnf
+	val simp     = (snd o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) simp_thm
+	(* initialize var_id, in case the term already contains variables of the form "cnfx_<int>" *)
+	val _        = (var_id := fold (fn free => fn max =>
+		let
+			val (name, _) = dest_Free free
+			val idx       = if String.isPrefix "cnfx_" name then
+					(Int.fromString o String.extract) (name, String.size "cnfx_", NONE)
+				else
+					NONE
+		in
+			Int.max (max, getOpt (idx, 0))
+		end) (term_frees simp) 0)
+	(* finally, convert to definitional CNF (this should preserve the simplification) *)
+	val cnfx_thm = make_cnfx_thm_from_nnf simp
+in
+	iff_trans OF [iff_trans OF [nnf_thm, simp_thm], cnfx_thm]
+end;
 
- | mk_cnfx_thm sign t  = error ("I don't know how to handle the formula " ^ 
-                          (Sign.string_of_term sign t))
+(* ------------------------------------------------------------------------- *)
+(*                                  Tactics                                  *)
+(* ------------------------------------------------------------------------- *)
 
-and cnfx_newlit sign p q r  = 
-   let val lit = read ("lit_" ^ (Int.toString (!lit_id)) ^ " :: bool")
-       val _ = (lit_id := !lit_id + 1)
-       val ct_lit = cterm_of sign lit
-       val x_conj = ext_conj lit p q r
-       val thm1 = inst_thm sign [p,q,r] cnf_newlit
-       val thm2 = mk_cnfx_thm sign x_conj 
-       val thm3 = forall_intr ct_lit thm2
-       val thm4 = strip_shyps (thm3 COMP allI)
-       val thm5 = strip_shyps (thm4 RS cnf_all_ex)
-   in
-       cnf_eq_trans OF [thm1, thm5]
-   end
+(* ------------------------------------------------------------------------- *)
+(* weakening_tac: removes the first hypothesis of the 'i'-th subgoal         *)
+(* ------------------------------------------------------------------------- *)
 
+(* int -> Tactical.tactic *)
 
-(* Theorems for converting formula into CNF (in primitive form), with 
-   new extra variables *)
-
+fun weakening_tac i =
+	dtac weakening_thm i THEN atac (i+1);
 
-fun mk_cnfx_thms sg nil = nil
-  | mk_cnfx_thms sg (x::l) = 
-    let val t = term_of_thm x
-    in
-      if (is_clause t) then x :: mk_cnfx_thms sg l
-      else 
-       let val thm1 = mk_cnfx_thm sg t
-           val thm2 = cnf_eq_imp OF [thm1,x]
-       in 
-           thm2 :: mk_cnfx_thms sg l
-       end
-    end
+(* ------------------------------------------------------------------------- *)
+(* cnf_rewrite_tac: converts all premises of the 'i'-th subgoal to CNF       *)
+(*      (possibly causing an exponential blowup in the length of each        *)
+(*      premise)                                                             *)
+(* ------------------------------------------------------------------------- *)
 
+(* int -> Tactical.tactic *)
 
-(* Tactic for converting hypotheses into CNF, possibly
-   introducing new variables *)
-
-fun cnfx_thin_tac state =
-let val sg = Thm.sign_of_thm state
-in
-case (prems_of state) of 
-  [] => Seq.empty
-| (subgoal::_) => 
-   let val n = num_of_hyps (strip_all_body subgoal);
-       val tac1 =  METAHYPS (fn l => cut_facts_tac (mk_cnfx_thms sg l) 1) 1
-   in
-      EVERY [tac1, weakenings_tac n, 
-             REPEAT (etac conjE 1 ORELSE etac exE 1)] state
-   end
-end
-
-(* Tactic for converting the conclusion of a goal into CNF *)
+fun cnf_rewrite_tac i =
+	(* cut the CNF formulas as new premises *)
+	METAHYPS (fn prems =>
+		let
+			val cnf_thms = map (fn pr => make_cnf_thm (theory_of_thm pr) ((HOLogic.dest_Trueprop o prop_of) pr)) prems
+			val cut_thms = map (fn (th, pr) => cnftac_eq_imp OF [th, pr]) (cnf_thms ~~ prems)
+		in
+			cut_facts_tac cut_thms 1
+		end) i
+	(* remove the original premises *)
+	THEN SELECT_GOAL (fn thm =>
+		let
+			val n = Logic.count_prems ((Term.strip_all_body o fst o Logic.dest_implies o prop_of) thm, 0)
+		in
+			PRIMITIVE (funpow (n div 2) (Seq.hd o weakening_tac 1)) thm
+		end) i;
 
-fun get_concl (Const("Trueprop", _) $ x) = get_concl x
-  | get_concl (Const("==>",_) $ x $ y) = get_concl y
-  | get_concl t = t
+(* ------------------------------------------------------------------------- *)
+(* cnfx_rewrite_tac: converts all premises of the 'i'-th subgoal to CNF      *)
+(*      (possibly introducing new literals)                                  *)
+(* ------------------------------------------------------------------------- *)
+
+(* int -> Tactical.tactic *)
 
-fun cnf_concl_tac' state =
-case (prems_of state) of 
-  [] => Seq.empty
-| (subgoal::_) =>
-  let val sg = Thm.sign_of_thm state
-      val c = get_concl subgoal
-      val thm1 = (mk_cnf_thm sg c) RS cnf_eq_sym
-      val thm2 = thm1 RS subst
-  in
-    rtac thm2 1 state
-  end
+fun cnfx_rewrite_tac i =
+	(* cut the CNF formulas as new premises *)
+	METAHYPS (fn prems =>
+		let
+			val cnfx_thms = map (fn pr => make_cnfx_thm (theory_of_thm pr) ((HOLogic.dest_Trueprop o prop_of) pr)) prems
+			val cut_thms  = map (fn (th, pr) => cnftac_eq_imp OF [th, pr]) (cnfx_thms ~~ prems)
+		in
+			cut_facts_tac cut_thms 1
+		end) i
+	(* remove the original premises *)
+	THEN SELECT_GOAL (fn thm =>
+		let
+			val n = Logic.count_prems ((Term.strip_all_body o fst o Logic.dest_implies o prop_of) thm, 0)
+		in
+			PRIMITIVE (funpow (n div 2) (Seq.hd o weakening_tac 1)) thm
+		end) i;
 
-val cnf_concl_tac  = METAHYPS (fn l => cnf_concl_tac') 1 
-
-
-end (*of structure*)
+end;  (* of structure *)
--- a/src/HOL/Tools/prop_logic.ML	Sat Oct 08 23:43:15 2005 +0200
+++ b/src/HOL/Tools/prop_logic.ML	Sun Oct 09 17:06:03 2005 +0200
@@ -34,6 +34,9 @@
 	val defcnf : prop_formula -> prop_formula  (* definitional cnf *)
 
 	val eval : (int -> bool) -> prop_formula -> bool  (* semantics *)
+
+	val prop_formula_of_term : Term.term -> int Termtab.table -> prop_formula * int Termtab.table
+		(* propositional representation of HOL terms *)
 end;
 
 structure PropLogic : PROP_LOGIC =
@@ -421,4 +424,66 @@
 	  | eval a (Or (fm1,fm2))  = (eval a fm1) orelse (eval a fm2)
 	  | eval a (And (fm1,fm2)) = (eval a fm1) andalso (eval a fm2);
 
+(* ------------------------------------------------------------------------- *)
+(* prop_formula_of_term: returns the propositional structure of a HOL term,  *)
+(*      with subterms replaced by Boolean variables.  Also returns a table   *)
+(*      of terms and corresponding variables that extends the table that was *)
+(*      given as an argument.  Usually, you'll just want to use              *)
+(*      'Termtab.empty' as value for 'table'.                                *)
+(* ------------------------------------------------------------------------- *)
+
+(* Note: The implementation is somewhat optimized; the next index to be used *)
+(*       is computed only when it is actually needed.  However, when         *)
+(*       'prop_formula_of_term' is invoked many times, it might be more      *)
+(*       efficient to pass and return this value as an additional parameter, *)
+(*       so that it does not have to be recomputed (by folding over the      *)
+(*       table) for each invocation.                                         *)
+
+	(* Term.term -> int Termtab.table -> prop_formula * int Termtab.table *)
+	fun prop_formula_of_term t table =
+	let
+		val next_idx_is_valid = ref false
+		val next_idx          = ref 0
+		fun get_next_idx () =
+			if !next_idx_is_valid then
+				inc next_idx
+			else (
+				next_idx          := Termtab.fold (curry Int.max o snd) table 0;
+				next_idx_is_valid := true;
+				inc next_idx
+			)
+		fun aux (Const ("True", _))         table =
+			(True, table)
+		  | aux (Const ("False", _))        table =
+			(False, table)
+		  | aux (Const ("Not", _) $ x)      table =
+			apfst Not (aux x table)
+		  | aux (Const ("op |", _) $ x $ y) table =
+			let
+				val (fm1, table1) = aux x table
+				val (fm2, table2) = aux y table1
+			in
+				(Or (fm1, fm2), table2)
+			end
+		  | aux (Const ("op &", _) $ x $ y) table =
+			let
+				val (fm1, table1) = aux x table
+				val (fm2, table2) = aux y table1
+			in
+				(And (fm1, fm2), table2)
+			end
+		  | aux x                           table =
+			(case Termtab.lookup table x of
+			  SOME i =>
+				(BoolVar i, table)
+			| NONE   =>
+				let
+					val i = get_next_idx ()
+				in
+					(BoolVar i, Termtab.update (x, i) table)
+				end)
+	in
+		aux t table
+	end;
+
 end;
--- a/src/HOL/Tools/sat_funcs.ML	Sat Oct 08 23:43:15 2005 +0200
+++ b/src/HOL/Tools/sat_funcs.ML	Sun Oct 09 17:06:03 2005 +0200
@@ -19,7 +19,7 @@
           [| l1; l2; ...; lm |] ==> False,
     where each li is a literal (see also comments in cnf_funcs.ML).
 
-    -- observe that this is the "dualized" version of the standard
+    -- Observe that this is the "dualized" version of the standard
        clausal form
            l1' \/ l2' \/ ... \/ lm', where li is the negation normal
        form of ~li'.
@@ -53,8 +53,8 @@
 signature SAT =
 sig
 	val trace_sat : bool ref  (* print trace messages *)
-	val sat_tac   : Tactical.tactic
-	val satx_tac  : Tactical.tactic
+	val sat_tac   : int -> Tactical.tactic
+	val satx_tac  : int -> Tactical.tactic
 end
 
 functor SATFunc (structure cnf : CNF) : SAT =
@@ -65,193 +65,190 @@
 val counter = ref 0;
 
 (* ------------------------------------------------------------------------- *)
-(* rev_lookup: look up the Isabelle/HOL atom corresponding to a DIMACS       *)
-(*      variable index in the dictionary.  This index should exist in the    *)
-(*      dictionary, otherwise exception Option is raised.                    *)
+(* swap_prem: convert raw clauses of the form                                *)
+(*        [| l1; l2; ...; li; ... |] ==> False                               *)
+(*      to                                                                   *)
+(*        [| l1; l2; ... |] ==> ~li .                                        *)
+(*      Note that ~li may be equal to ~~a for some atom a.                   *)
 (* ------------------------------------------------------------------------- *)
 
-(* 'b -> ('a * 'b) list -> 'a *)
-
-fun rev_lookup idx []                   = raise Option
-  | rev_lookup idx ((key, entry)::dict) = if entry=idx then key else rev_lookup idx dict;
+(* Thm.thm -> int -> Thm.thm *)
 
-(* ------------------------------------------------------------------------- *)
-(* swap_prem: convert rules of the form                                      *)
-(*       l1 ==> l2 ==> .. ==> li ==> .. ==> False                            *)
-(*     to                                                                    *)
-(*       l1 ==> l2 ==> .... ==> ~li                                          *)
-(* ------------------------------------------------------------------------- *)
-
-fun swap_prem rslv c =
-let
-	val thm1 = rule_by_tactic (metacut_tac c 1 THEN (atac 1) THEN (REPEAT_SOME atac)) rslv
-in
-	rule_by_tactic (ALLGOALS (cnf.weakening_tac)) thm1
-end;
+fun swap_prem raw_cl i =
+	Seq.hd ((metacut_tac raw_cl 1  (* [| [| ?P; False |] ==> False; ?P ==> l1; ...; ?P ==> li; ... |] ==> ~ ?P *)
+		THEN atac (i+1)            (* [| [| li; False |] ==> False; li ==> l1; ... |] ==> ~ li *)
+		THEN atac 1                (* [| li ==> l1; ... |] ==> ~ li *)
+		THEN ALLGOALS cnf.weakening_tac) notI);
 
 (* ------------------------------------------------------------------------- *)
-(* is_dual: check if two atoms are dual to each other                        *)
-(* ------------------------------------------------------------------------- *)
-
-(* Term.term -> Term.term -> bool *)
-
-fun is_dual (Const ("Trueprop", _) $ x) y = is_dual x y
-  | is_dual x (Const ("Trueprop", _) $ y) = is_dual x y
-  | is_dual (Const ("Not", _) $ x) y      = (x = y)
-  | is_dual x (Const ("Not", _) $ y)      = (x = y)
-  | is_dual x y                           = false;
-
-(* ------------------------------------------------------------------------- *)
-(* dual_mem: check if an atom has a dual in a list of atoms                  *)
-(* ------------------------------------------------------------------------- *)
-
-(* Term.term -> Term.term list -> bool *)
-
-fun dual_mem x []      = false
-  | dual_mem x (y::ys) = is_dual x y orelse dual_mem x ys;
-
-(* ------------------------------------------------------------------------- *)
-(* replay_chain: proof reconstruction: given two clauses                     *)
-(*        [| x1 ; .. ; a ; .. ; xn |] ==> False                              *)
+(* resolve_raw_clauses: given a non-empty list of raw clauses, we fold       *)
+(*      resolution over the list (starting with its head), i.e. with two raw *)
+(*      clauses                                                              *)
+(*        [| x1; ... ; a; ...; xn |] ==> False                               *)
 (*      and                                                                  *)
-(*        [| y1 ; .. ; ~a ; .. ; ym |] ==> False ,                           *)
-(*      we first convert the first clause into                               *)
-(*        [| x1 ; ... ; xn |] ==> ~a     (using swap_prem)                   *)
-(*      and do a resolution with the second clause to produce                *)
-(*        [| y1 ; ... ; x1 ; ... ; xn ; ... ; yn |] ==> False                *)
+(*        [| y1; ... ; a'; ...; ym |] ==> False                              *)
+(*      (where a and a' are dual to each other), we first convert the first  *)
+(*      clause to                                                            *)
+(*        [| x1; ...; xn |] ==> a'                                           *)
+(*      (using swap_prem and perhaps notnotD), and then do a resolution with *)
+(*      the second clause to produce                                         *)
+(*        [| y1; ...; x1; ...; xn; ...; yn |] ==> False                      *)
+(*      amd finally remove duplicate literals.                               *)
 (* ------------------------------------------------------------------------- *)
 
-(* Theory.theory -> Thm.thm option Array.array -> int -> int list -> unit *)
+(* Thm.thm list -> Thm.thm *)
 
-fun replay_chain sg clauses idx (c::cs) =
-let
-	val fc = (valOf o Array.sub) (clauses, c)
+fun resolve_raw_clauses [] =
+	raise THM ("Proof reconstruction failed (empty list of resolvents)!", 0, [])
+  | resolve_raw_clauses (c::cs) =
+	let
+		fun dual (Const ("Not", _) $ x) = x
+		  | dual x                      = HOLogic.Not $ x
+
+		fun is_neg (Const ("Not", _) $ _) = true
+		  | is_neg _                      = false
 
-	fun strip_neg (Const ("Trueprop", _) $ x) = strip_neg x
-	  | strip_neg (Const ("Not", _) $ x)      = x
-	  | strip_neg x                           = x
+		(* find out which premises are used in the resolution *)
+		(* Term.term list -> Term.term list -> int -> (int * int * bool) *)
+		fun res_prems []      _  _    =
+			raise THM ("Proof reconstruction failed (no literal for resolution)!", 0, [])
+		  | res_prems (x::xs) ys idx1 =
+			let
+				val x'   = HOLogic.dest_Trueprop x
+				val idx2 = find_index_eq (dual x') ys
+			in
+				if idx2 = (~1) then
+					res_prems xs ys (idx1+1)
+				else
+					(idx1, idx2, is_neg x')
+			end
 
-	(* find out which atom (literal) is used in the resolution *)
-	fun res_atom []      _  = raise THM ("Proof reconstruction failed!", 0, [])
-	  | res_atom (x::xs) ys = if dual_mem x ys then strip_neg x else res_atom xs ys
-
-	fun replay old []        =
-		old
-	  | replay old (cl::cls) =
+		(* Thm.thm -> Thm.thm -> Thm.thm *)
+		fun resolution c1 c2 =
 		let
-			val icl  = (valOf o Array.sub) (clauses, cl)
-			val var  = res_atom (prems_of old) (prems_of icl)
-			val atom = cterm_of sg var
-			val rslv = instantiate' [] [SOME atom] notI
 			val _ = if !trace_sat then
-					tracing ("Resolving clause: " ^ string_of_thm old ^
-						"\nwith clause: " ^ string_of_thm icl ^
-						"\nusing literal " ^ string_of_cterm atom ^ ".")
+					tracing ("Resolving clause: " ^ string_of_thm c1 ^ "\nwith clause: " ^ string_of_thm c2)
 				else ()
-			val thm1 = (rule_by_tactic (REPEAT_SOME (rtac (swap_prem rslv old))) icl
-				handle THM _ => rule_by_tactic (REPEAT_SOME (rtac (swap_prem rslv icl))) old)
-			val new  = rule_by_tactic distinct_subgoals_tac thm1
-			val _    = if !trace_sat then tracing ("Resulting clause: " ^ string_of_thm new) else ()
-			val _    = inc counter
-		in
-			replay new cls
-		end
+
+			val prems2                   = map HOLogic.dest_Trueprop (prems_of c2)
+			val (idx1, idx2, is_neg_lit) = res_prems (prems_of c1) prems2 0
+			val swap_c1                  = swap_prem c1 (idx1+1)
+			val swap_c1_nnf              = if is_neg_lit then Seq.hd (rtac swap_c1 1 notnotD) else swap_c1  (* deal with double-negation *)
+			val c_new                    = Seq.hd ((rtac swap_c1_nnf (idx2+1) THEN distinct_subgoals_tac) c2)
 
-	val _ = Array.update (clauses, idx, SOME (replay fc cs))
-
-	val _ = if !trace_sat then
-			tracing ("Replay chain successful; clause stored at #" ^ string_of_int idx)
-		else ()
-in
-	()
-end;
+			val _ = if !trace_sat then tracing ("Resulting clause: " ^ string_of_thm c_new) else ()
+			val _ = inc counter
+		in
+			c_new
+		end
+	in
+		fold resolution cs c
+	end;
 
 (* ------------------------------------------------------------------------- *)
-(* replay_proof: replay the resolution proof returned by the SAT solver; cf. *)
-(*      SatSolver.proof for details of the proof format.  Returns the        *)
-(*      theorem established by the proof (which is just False).              *)
+(* replay_proof: replays the resolution proof returned by the SAT solver;    *)
+(*      cf. SatSolver.proof for details of the proof format.  Updates the    *)
+(*      'clauses' array with derived clauses, and returns the derived clause *)
+(*      at index 'empty_id' (which should just be "False" if proof           *)
+(*      reconstruction was successful, with the used clauses as hyps).       *)
 (* ------------------------------------------------------------------------- *)
 
-(* Theory.theory -> Thm.thm option Array.array -> SatSolver.proof -> Thm.thm *)
+(* Thm.thm option Array.array -> SatSolver.proof -> Thm.thm *)
 
-fun replay_proof sg clauses (clause_table, empty_id) =
+fun replay_proof clauses (clause_table, empty_id) =
 let
-	(* int -> unit *)
+	(* int -> Thm.thm *)
 	fun prove_clause id =
 		case Array.sub (clauses, id) of
-		  SOME _ =>
-			()
-		| NONE   =>
+		  SOME thm =>
+			thm
+		| NONE     =>
 			let
+				val _   = if !trace_sat then tracing ("Proving clause #" ^ string_of_int id ^ " ...") else ()
 				val ids = valOf (Inttab.lookup clause_table id)
-				val _   = map prove_clause ids
+				val thm = resolve_raw_clauses (map prove_clause ids)
+				val _   = Array.update (clauses, id, SOME thm)
+				val _   = if !trace_sat then tracing ("Replay chain successful; clause stored at #" ^ string_of_int id) else ()
 			in
-				replay_chain sg clauses id ids
+				thm
 			end
 
-	val _ = counter := 0
-
-	val _ = prove_clause empty_id
-
-	val _ = if !trace_sat then
-			tracing (string_of_int (!counter) ^ " resolution step(s) total.")
-		else ()
+	val _            = counter := 0
+	val empty_clause = prove_clause empty_id
+	val _            = if !trace_sat then tracing ("Proof reconstruction successful; " ^ string_of_int (!counter) ^ " resolution step(s) total.") else ()
 in
-	(valOf o Array.sub) (clauses, empty_id)
+	empty_clause
 end;
 
-(* ------------------------------------------------------------------------- *)
-(* Functions to build the sat tactic                                         *)
-(* ------------------------------------------------------------------------- *)
-
-fun collect_atoms (Const ("Trueprop", _) $ x) ls = collect_atoms x ls
-  | collect_atoms (Const ("op |", _) $ x $ y) ls = collect_atoms x (collect_atoms y ls)
-  | collect_atoms x                           ls = x ins ls;
-
-fun has_duals []      = false
-  | has_duals (x::xs) = dual_mem x xs orelse has_duals xs;
-
-fun is_trivial_clause (Const ("True", _)) = true
-  | is_trivial_clause c                   = has_duals (collect_atoms c []);
+(* PropLogic.prop_formula -> string *)
+fun string_of_prop_formula PropLogic.True             = "True"
+  | string_of_prop_formula PropLogic.False            = "False"
+  | string_of_prop_formula (PropLogic.BoolVar i)      = "x" ^ string_of_int i
+  | string_of_prop_formula (PropLogic.Not fm)         = "~" ^ string_of_prop_formula fm
+  | string_of_prop_formula (PropLogic.Or (fm1, fm2))  = "(" ^ string_of_prop_formula fm1 ^ " v " ^ string_of_prop_formula fm2 ^ ")"
+  | string_of_prop_formula (PropLogic.And (fm1, fm2)) = "(" ^ string_of_prop_formula fm1 ^ " & " ^ string_of_prop_formula fm2 ^ ")";
 
 (* ------------------------------------------------------------------------- *)
 (* rawsat_thm: run external SAT solver with the given clauses.  Reconstructs *)
-(*      a proof from the resulting proof trace of the SAT solver.            *)
+(*      a proof from the resulting proof trace of the SAT solver.  Each      *)
+(*      premise in 'prems' that is not a clause is ignored, and the theorem  *)
+(*      returned is just "False" (with some clauses as hyps).                *)
 (* ------------------------------------------------------------------------- *)
 
-fun rawsat_thm sg prems =
+(* Thm.thm list -> Thm.thm *)
+
+fun rawsat_thm prems =
 let
-	val thms       = filter (not o is_trivial_clause o term_of o cprop_of) prems  (* remove trivial clauses *)
-	val (fm, dict) = cnf.cnf2prop thms
-	val _          = if !trace_sat then tracing "Invoking SAT solver ..." else ()
+	(* remove premises that equal "True" *)
+	val non_triv_prems    = filter (fn thm =>
+		(not_equal HOLogic.true_const o HOLogic.dest_Trueprop o prop_of) thm
+			handle TERM ("dest_Trueprop", _) => true) prems
+	(* remove non-clausal premises -- of course this shouldn't actually   *)
+	(* remove anything as long as 'rawsat_thm' is only called after the   *)
+	(* premises have been converted to clauses                            *)
+	val clauses           = filter (fn thm =>
+		((cnf.is_clause o HOLogic.dest_Trueprop o prop_of) thm handle TERM ("dest_Trueprop", _) => false)
+		orelse (warning ("Ignoring non-clausal premise " ^ (string_of_cterm o cprop_of) thm); false)) non_triv_prems
+	(* remove trivial clauses -- this is necessary because zChaff removes *)
+	(* trivial clauses during preprocessing, and otherwise our clause     *)
+	(* numbering would be off                                             *)
+	val non_triv_clauses  = filter (not o cnf.clause_is_trivial o HOLogic.dest_Trueprop o prop_of) clauses
+	(* translate clauses from HOL terms to PropLogic.prop_formula *)
+	val (fms, atom_table) = fold_map (PropLogic.prop_formula_of_term o HOLogic.dest_Trueprop o prop_of) non_triv_clauses Termtab.empty
+	val _                 = if !trace_sat then
+			tracing ("Invoking SAT solver on clauses:\n" ^ space_implode "\n" (map string_of_prop_formula fms))
+		else ()
+	val fm                = PropLogic.all fms
 in
 	case SatSolver.invoke_solver "zchaff_with_proofs" fm of
 	  SatSolver.UNSATISFIABLE (SOME (clause_table, empty_id)) =>
 		let
-			val _         = if !trace_sat then
+			val _          = if !trace_sat then
 					tracing ("Proof trace from SAT solver:\n" ^
 						"clauses: [" ^ commas (map (fn (c, cs) =>
 							"(" ^ string_of_int c ^ ", [" ^ commas (map string_of_int cs) ^ "])") (Inttab.dest clause_table)) ^ "]\n" ^
 						"empty clause: " ^ string_of_int empty_id)
 				else ()
-			val raw_thms  = cnf.cnf2raw_thms thms
-			val raw_thms' = map (rule_by_tactic distinct_subgoals_tac) raw_thms
-			(* initialize the clause array with the original clauses *)
-			val max_idx   = valOf (Inttab.max_key clause_table)
-			val clauses   = Array.array (max_idx + 1, NONE)
-			val _         = fold (fn thm => fn idx => (Array.update (clauses, idx, SOME thm); idx+1)) raw_thms' 0
+			(* initialize the clause array with the given clauses, *)
+			(* but converted to raw clause format                  *)
+			val max_idx     = valOf (Inttab.max_key clause_table)
+			val clause_arr  = Array.array (max_idx + 1, NONE)
+			val raw_clauses = map (Seq.hd o distinct_subgoals_tac o cnf.clause2raw_thm) non_triv_clauses
+			val _           = fold (fn thm => fn idx => (Array.update (clause_arr, idx, SOME thm); idx+1)) raw_clauses 0
 		in
-			replay_proof sg clauses (clause_table, empty_id)
+			(* replay the proof to derive the empty clause *)
+			replay_proof clause_arr (clause_table, empty_id)
 		end
 	| SatSolver.UNSATISFIABLE NONE =>
 		raise THM ("SAT solver claims the formula to be unsatisfiable, but did not provide a proof", 0, [])
 	| SatSolver.SATISFIABLE assignment =>
 		let
 			val msg = "SAT solver found a countermodel:\n"
-				^ (enclose "{" "}"
-					o commas
-					o map (Sign.string_of_term sg o fst)
-					o filter (fn (_, idx) => getOpt (assignment idx, false))) dict
+				^ (commas
+					o map (fn (term, idx) =>
+						Sign.string_of_term (the_context ()) term ^ ": "
+							^ (case assignment idx of NONE => "arbitrary" | SOME true => "true" | SOME false => "false")))
+					(Termtab.dest atom_table)
 		in
 			raise THM (msg, 0, [])
 		end
@@ -263,27 +260,73 @@
 (* Tactics                                                                   *)
 (* ------------------------------------------------------------------------- *)
 
-fun cnfsat_basic_tac state =
-let
-	val sg = Thm.sign_of_thm state
-in
-	METAHYPS (fn prems => rtac (rawsat_thm sg prems) 1) 1 state
-end;
+(* ------------------------------------------------------------------------- *)
+(* rawsat_tac: solves the i-th subgoal of the proof state; this subgoal      *)
+(*      should be of the form                                                *)
+(*        [| c1; c2; ...; ck |] ==> False                                    *)
+(*      where each cj is a non-empty clause (i.e. a disjunction of literals) *)
+(*      or "True"                                                            *)
+(* ------------------------------------------------------------------------- *)
+
+(* int -> Tactical.tactic *)
+
+fun rawsat_tac i = METAHYPS (fn prems => rtac (rawsat_thm prems) 1) i;
 
-(* a trivial tactic, used in preprocessing before calling the main tactic *)
-val pre_sat_tac = (REPEAT (etac conjE 1)) THEN (REPEAT ((atac 1) ORELSE (etac FalseE 1)));
+(* ------------------------------------------------------------------------- *)
+(* pre_cnf_tac: converts the i-th subgoal                                    *)
+(*        [| A1 ; ... ; An |] ==> B                                          *)
+(*      to                                                                   *)
+(*        [| A1; ... ; An ; ~B |] ==> False                                  *)
+(*      (handling meta-logical connectives in B properly before negating),   *)
+(*      then replaces meta-logical connectives in the premises (i.e. "==>",  *)
+(*      "!!" and "==") by connectives of the HOL object-logic (i.e. by       *)
+(*      "-->", "!", and "=")                                                 *)
+(* ------------------------------------------------------------------------- *)
+
+(* int -> Tactical.tactic *)
+
+fun pre_cnf_tac i = rtac ccontr i THEN ObjectLogic.atomize_tac i;
+
+(* ------------------------------------------------------------------------- *)
+(* cnfsat_tac: checks if the empty clause "False" occurs among the premises; *)
+(*      if not, eliminates conjunctions (i.e. each clause of the CNF formula *)
+(*      becomes a separate premise), then applies 'rawsat_tac' to solve the  *)
+(*      subgoal                                                              *)
+(* ------------------------------------------------------------------------- *)
 
-(* tactic for calling external SAT solver, taking as input CNF clauses *)
-val cnfsat_tac = pre_sat_tac THEN (IF_UNSOLVED cnfsat_basic_tac);
+(* int -> Tactical.tactic *)
+
+fun cnfsat_tac i = (etac FalseE i) ORELSE (REPEAT_DETERM (etac conjE i) THEN rawsat_tac i);
 
-(* tactic for calling external SAT solver, taking as input arbitrary formula *)
-val sat_tac = cnf.cnf_thin_tac THEN cnfsat_tac;
+(* ------------------------------------------------------------------------- *)
+(* cnfxsat_tac: checks if the empty clause "False" occurs among the          *)
+(*      premises; if not, eliminates conjunctions (i.e. each clause of the   *)
+(*      CNF formula becomes a separate premise) and existential quantifiers, *)
+(*      then applies 'rawsat_tac' to solve the subgoal                       *)
+(* ------------------------------------------------------------------------- *)
+
+(* int -> Tactical.tactic *)
+
+fun cnfxsat_tac i = (etac FalseE i) ORELSE (REPEAT_DETERM (etac conjE i ORELSE etac exE i) THEN rawsat_tac i);
 
-(*
-  Tactic for calling external SAT solver, taking as input arbitrary formula.
-  The input is translated to CNF (in primitive form), possibly introducing
-  new literals.
-*)
-val satx_tac = cnf.cnfx_thin_tac THEN cnfsat_tac;
+(* ------------------------------------------------------------------------- *)
+(* sat_tac: tactic for calling an external SAT solver, taking as input an    *)
+(*      arbitrary formula.  The input is translated to CNF, possibly causing *)
+(*      an exponential blowup.                                               *)
+(* ------------------------------------------------------------------------- *)
+
+(* int -> Tactical.tactic *)
+
+fun sat_tac i = pre_cnf_tac i THEN cnf.cnf_rewrite_tac i THEN cnfsat_tac i;
+
+(* ------------------------------------------------------------------------- *)
+(* satx_tac: tactic for calling an external SAT solver, taking as input an   *)
+(*      arbitrary formula.  The input is translated to CNF, possibly         *)
+(*      introducing new literals.                                            *)
+(* ------------------------------------------------------------------------- *)
+
+(* int -> Tactical.tactic *)
+
+fun satx_tac i = pre_cnf_tac i THEN cnf.cnfx_rewrite_tac i THEN cnfxsat_tac i;
 
 end;  (* of structure *)
--- a/src/HOL/ex/SAT_Examples.thy	Sat Oct 08 23:43:15 2005 +0200
+++ b/src/HOL/ex/SAT_Examples.thy	Sun Oct 09 17:06:03 2005 +0200
@@ -12,6 +12,63 @@
 
 ML {* set sat.trace_sat; *}
 
+lemma "True"
+by sat
+
+lemma "a | ~a"
+by sat
+
+lemma "(a | b) & ~a \<Longrightarrow> b"
+by sat
+
+lemma "(a & b) | (c & d) \<Longrightarrow> (a & b) | (c & d)"
+apply (tactic {* cnf.cnf_rewrite_tac 1 *})
+by sat
+
+lemma "(a & b) | (c & d) \<Longrightarrow> (a & b) | (c & d)"
+apply (tactic {* cnf.cnfx_rewrite_tac 1 *})
+apply (erule exE | erule conjE)+
+by satx
+
+lemma "(a & b | c & d) & (e & f | g & h) | (i & j | k & l) & (m & n | p & q) \<Longrightarrow>
+  (a & b | c & d) & (e & f | g & h) | (i & j | k & l) & (m & n | p & q)"
+apply (tactic {* cnf.cnf_rewrite_tac 1 *})
+by sat
+
+lemma "(a & b | c & d) & (e & f | g & h) | (i & j | k & l) & (m & n | p & q) \<Longrightarrow>
+  (a & b | c & d) & (e & f | g & h) | (i & j | k & l) & (m & n | p & q)"
+apply (tactic {* cnf.cnfx_rewrite_tac 1 *})
+apply (erule exE | erule conjE)+
+by satx
+
+lemma "P=P=P=P=P=P=P=P=P=P"
+by sat
+
+lemma "P=P=P=P=P=P=P=P=P=P"
+by satx
+
+lemma  "!! a b c. [| a | b | c | d ;
+e | f | (a & d) ;
+~(a | (c & ~c)) | b ;
+~(b & (x | ~x)) | c ;
+~(d | False) | c ;
+~(c | (~p & (p | (q & ~q)))) |] ==> False"
+by sat
+
+lemma  "!! a b c. [| a | b | c | d ;
+e | f | (a & d) ;
+~(a | (c & ~c)) | b ;
+~(b & (x | ~x)) | c ;
+~(d | False) | c ;
+~(c | (~p & (p | (q & ~q)))) |] ==> False"
+by satx
+
+ML {* reset sat.trace_sat; *}
+
+(*
+ML {* Toplevel.profiling := 1; *}
+*)
+
 (* Translated from TPTP problem library: PUZ015-2.006.dimacs *)
 
 lemma assumes 1: "~x0"
@@ -199,16 +256,240 @@
 and 183: "~x29 | ~x58"
 and 184: "~x28 | ~x58"
 shows "False"
-using 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
-20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 
-40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 
-60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 
-80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 
-100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 
-120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 
-140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 
-160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 
-180 181 182 183 184 
+using 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
+40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
+60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
+80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
+120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
+140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
+160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
+180 181 182 183 184
 by sat
 
+(* Translated from TPTP problem library: MSC007-1.008.dimacs *)
+
+lemma assumes 1: "x0 | x1 | x2 | x3 | x4 | x5 | x6"
+and 2: "x7 | x8 | x9 | x10 | x11 | x12 | x13"
+and 3: "x14 | x15 | x16 | x17 | x18 | x19 | x20"
+and 4: "x21 | x22 | x23 | x24 | x25 | x26 | x27"
+and 5: "x28 | x29 | x30 | x31 | x32 | x33 | x34"
+and 6: "x35 | x36 | x37 | x38 | x39 | x40 | x41"
+and 7: "x42 | x43 | x44 | x45 | x46 | x47 | x48"
+and 8: "x49 | x50 | x51 | x52 | x53 | x54 | x55"
+and 9: "~x0 | ~x7"
+and 10: "~x0 | ~x14"
+and 11: "~x0 | ~x21"
+and 12: "~x0 | ~x28"
+and 13: "~x0 | ~x35"
+and 14: "~x0 | ~x42"
+and 15: "~x0 | ~x49"
+and 16: "~x7 | ~x14"
+and 17: "~x7 | ~x21"
+and 18: "~x7 | ~x28"
+and 19: "~x7 | ~x35"
+and 20: "~x7 | ~x42"
+and 21: "~x7 | ~x49"
+and 22: "~x14 | ~x21"
+and 23: "~x14 | ~x28"
+and 24: "~x14 | ~x35"
+and 25: "~x14 | ~x42"
+and 26: "~x14 | ~x49"
+and 27: "~x21 | ~x28"
+and 28: "~x21 | ~x35"
+and 29: "~x21 | ~x42"
+and 30: "~x21 | ~x49"
+and 31: "~x28 | ~x35"
+and 32: "~x28 | ~x42"
+and 33: "~x28 | ~x49"
+and 34: "~x35 | ~x42"
+and 35: "~x35 | ~x49"
+and 36: "~x42 | ~x49"
+and 37: "~x1 | ~x8"
+and 38: "~x1 | ~x15"
+and 39: "~x1 | ~x22"
+and 40: "~x1 | ~x29"
+and 41: "~x1 | ~x36"
+and 42: "~x1 | ~x43"
+and 43: "~x1 | ~x50"
+and 44: "~x8 | ~x15"
+and 45: "~x8 | ~x22"
+and 46: "~x8 | ~x29"
+and 47: "~x8 | ~x36"
+and 48: "~x8 | ~x43"
+and 49: "~x8 | ~x50"
+and 50: "~x15 | ~x22"
+and 51: "~x15 | ~x29"
+and 52: "~x15 | ~x36"
+and 53: "~x15 | ~x43"
+and 54: "~x15 | ~x50"
+and 55: "~x22 | ~x29"
+and 56: "~x22 | ~x36"
+and 57: "~x22 | ~x43"
+and 58: "~x22 | ~x50"
+and 59: "~x29 | ~x36"
+and 60: "~x29 | ~x43"
+and 61: "~x29 | ~x50"
+and 62: "~x36 | ~x43"
+and 63: "~x36 | ~x50"
+and 64: "~x43 | ~x50"
+and 65: "~x2 | ~x9"
+and 66: "~x2 | ~x16"
+and 67: "~x2 | ~x23"
+and 68: "~x2 | ~x30"
+and 69: "~x2 | ~x37"
+and 70: "~x2 | ~x44"
+and 71: "~x2 | ~x51"
+and 72: "~x9 | ~x16"
+and 73: "~x9 | ~x23"
+and 74: "~x9 | ~x30"
+and 75: "~x9 | ~x37"
+and 76: "~x9 | ~x44"
+and 77: "~x9 | ~x51"
+and 78: "~x16 | ~x23"
+and 79: "~x16 | ~x30"
+and 80: "~x16 | ~x37"
+and 81: "~x16 | ~x44"
+and 82: "~x16 | ~x51"
+and 83: "~x23 | ~x30"
+and 84: "~x23 | ~x37"
+and 85: "~x23 | ~x44"
+and 86: "~x23 | ~x51"
+and 87: "~x30 | ~x37"
+and 88: "~x30 | ~x44"
+and 89: "~x30 | ~x51"
+and 90: "~x37 | ~x44"
+and 91: "~x37 | ~x51"
+and 92: "~x44 | ~x51"
+and 93: "~x3 | ~x10"
+and 94: "~x3 | ~x17"
+and 95: "~x3 | ~x24"
+and 96: "~x3 | ~x31"
+and 97: "~x3 | ~x38"
+and 98: "~x3 | ~x45"
+and 99: "~x3 | ~x52"
+and 100: "~x10 | ~x17"
+and 101: "~x10 | ~x24"
+and 102: "~x10 | ~x31"
+and 103: "~x10 | ~x38"
+and 104: "~x10 | ~x45"
+and 105: "~x10 | ~x52"
+and 106: "~x17 | ~x24"
+and 107: "~x17 | ~x31"
+and 108: "~x17 | ~x38"
+and 109: "~x17 | ~x45"
+and 110: "~x17 | ~x52"
+and 111: "~x24 | ~x31"
+and 112: "~x24 | ~x38"
+and 113: "~x24 | ~x45"
+and 114: "~x24 | ~x52"
+and 115: "~x31 | ~x38"
+and 116: "~x31 | ~x45"
+and 117: "~x31 | ~x52"
+and 118: "~x38 | ~x45"
+and 119: "~x38 | ~x52"
+and 120: "~x45 | ~x52"
+and 121: "~x4 | ~x11"
+and 122: "~x4 | ~x18"
+and 123: "~x4 | ~x25"
+and 124: "~x4 | ~x32"
+and 125: "~x4 | ~x39"
+and 126: "~x4 | ~x46"
+and 127: "~x4 | ~x53"
+and 128: "~x11 | ~x18"
+and 129: "~x11 | ~x25"
+and 130: "~x11 | ~x32"
+and 131: "~x11 | ~x39"
+and 132: "~x11 | ~x46"
+and 133: "~x11 | ~x53"
+and 134: "~x18 | ~x25"
+and 135: "~x18 | ~x32"
+and 136: "~x18 | ~x39"
+and 137: "~x18 | ~x46"
+and 138: "~x18 | ~x53"
+and 139: "~x25 | ~x32"
+and 140: "~x25 | ~x39"
+and 141: "~x25 | ~x46"
+and 142: "~x25 | ~x53"
+and 143: "~x32 | ~x39"
+and 144: "~x32 | ~x46"
+and 145: "~x32 | ~x53"
+and 146: "~x39 | ~x46"
+and 147: "~x39 | ~x53"
+and 148: "~x46 | ~x53"
+and 149: "~x5 | ~x12"
+and 150: "~x5 | ~x19"
+and 151: "~x5 | ~x26"
+and 152: "~x5 | ~x33"
+and 153: "~x5 | ~x40"
+and 154: "~x5 | ~x47"
+and 155: "~x5 | ~x54"
+and 156: "~x12 | ~x19"
+and 157: "~x12 | ~x26"
+and 158: "~x12 | ~x33"
+and 159: "~x12 | ~x40"
+and 160: "~x12 | ~x47"
+and 161: "~x12 | ~x54"
+and 162: "~x19 | ~x26"
+and 163: "~x19 | ~x33"
+and 164: "~x19 | ~x40"
+and 165: "~x19 | ~x47"
+and 166: "~x19 | ~x54"
+and 167: "~x26 | ~x33"
+and 168: "~x26 | ~x40"
+and 169: "~x26 | ~x47"
+and 170: "~x26 | ~x54"
+and 171: "~x33 | ~x40"
+and 172: "~x33 | ~x47"
+and 173: "~x33 | ~x54"
+and 174: "~x40 | ~x47"
+and 175: "~x40 | ~x54"
+and 176: "~x47 | ~x54"
+and 177: "~x6 | ~x13"
+and 178: "~x6 | ~x20"
+and 179: "~x6 | ~x27"
+and 180: "~x6 | ~x34"
+and 181: "~x6 | ~x41"
+and 182: "~x6 | ~x48"
+and 183: "~x6 | ~x55"
+and 184: "~x13 | ~x20"
+and 185: "~x13 | ~x27"
+and 186: "~x13 | ~x34"
+and 187: "~x13 | ~x41"
+and 188: "~x13 | ~x48"
+and 189: "~x13 | ~x55"
+and 190: "~x20 | ~x27"
+and 191: "~x20 | ~x34"
+and 192: "~x20 | ~x41"
+and 193: "~x20 | ~x48"
+and 194: "~x20 | ~x55"
+and 195: "~x27 | ~x34"
+and 196: "~x27 | ~x41"
+and 197: "~x27 | ~x48"
+and 198: "~x27 | ~x55"
+and 199: "~x34 | ~x41"
+and 200: "~x34 | ~x48"
+and 201: "~x34 | ~x55"
+and 202: "~x41 | ~x48"
+and 203: "~x41 | ~x55"
+and 204: "~x48 | ~x55"
+shows "False"
+using 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
+40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
+60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
+80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
+120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
+140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
+160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
+180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
+200 201 202 203 204
+by sat
+
+(*
+ML {* Toplevel.profiling := 0; *}
+*)
+
 end