(* Resolution prover on h1 clauses.
   Copyright (C) 2003 Jean Goubault-Larrecq and LSV, CNRS UMR 8643 & ENS Cachan.

   This file is part of h1.

   h1 is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   h1 is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with h1; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*)

open "clause_h";
open "heap_h";
open "verbose_h";
open "rel_h";
open "sort_h";
open "scc_h";
open "topsort_h";

val ignore_intermediate_selfq = false; (* still buggy *)

val p_resolve = P_RESOLVE;
val p_split_def = P_SPLIT_DEF;
val p_split_use = P_SPLIT_USE;

(*
val dummy_pi = P_GIVEN "<recipe>";
fun p_resolve _ = dummy_pi;
fun p_split_def _ = dummy_pi;
fun p_split_use _ = dummy_pi;
 *)

(*$P+*)

fun dterm1_vars (DV x) = {x}
  | dterm1_vars (DAPP (_, l)) =
    union {dterm1_vars t | (_, t) in list l};

fun head_vars (HVAR _) = {1}
  | head_vars (HFUN (_, _, _, xs, _)) = xs
  | head_vars _ = {};

fun atl_vars atl = union {dterm1_vars t
			 | (_, t) in list atl};
fun al_vars al = union {atl_vars atl
		       | (_, atl) in list al};

fun clause_vars (CL (h, ql, al, Bs, ...)) =
    head_vars h U
    al_vars al U
    dom Bs;

(* Clause printing functions: *)

fun print_q (f as |[put, ...]|) q =
    let val delimr = ref "ne("
    in
	iterate
	  (put (!delimr); delimr := "_&_"; put P)
	| P in set q
	end;
	put ")"
    end;

exception PrintVarNotInEnv of int;

fun print_dterm1 (f as |[put, ...]|, vars) =
    let fun pdterm1 (DV x) =
	    if x inset vars
		then put (?vars x)
	    else raise PrintVarNotInEnv x
	  | pdterm1 (DAPP (f, nil)) = put f
	  | pdterm1 (DAPP (f, [(_, t)])) = (put f; put " "; pdterm1 t)
	  | pdterm1 (DAPP (f, l)) =
	    let val delimr = ref "("
	    in
		put f;
		iterate
		  (put (!delimr); delimr := ","; pdterm1 t)
		| (_, t) in list l
		end;
		put ")"
	    end
    in
	pdterm1
    end;

fun print_head (f as |[put, ...]|, xname, vars) =
    let val pdterm1 = print_dterm1 (f, vars)
	val pq = print_q f
	fun phead (HVAR P) = (put P; put " "; pdterm1 (DV 1))
	  | phead (HFUN (P, f, k, xs, t)) =
	    (put P; put " "; pdterm1 t)
	  | phead (HQ q) = pq q
	  | phead (HBOT name) = put name
    in
	phead
    end;

fun clause_var_names xname =
    let val f as |[convert, put, seek, ...]| = outstring xname
	val n = size xname
    in
	fn c =>
	   {i => (seek n;
		  print f (pack i);
		  convert ())
	   | i in set clause_vars c}
    end;

fun print_clause (f as |[put, ...]|, xname) =
    let val pq = print_q f
	val cvarnames = clause_var_names xname
    in
	fn (c as CL (h, ql, al, Bs, ...)) =>
	   let val vars = cvarnames c
	       val pt = print_dterm1 (f, vars)
	       val delimr = ref " :- "
	   in
	       print_head (f, xname, vars) h;
	       iterate
		 (put (!delimr); delimr := ", "; pq q)
	       | q in set ql
	       end;
	       iterate
		 iterate
		   (put (!delimr); delimr := ", ";
		    (*put "["; print f (pack maxd); put "] ";*)
		    put P; put " "; pt t)
		 | (P, t) in list atl
		 end
	       | (_, atl) in list al
	       end;
	       iterate
		 iterate
		   (put (!delimr); delimr := ", ";
		    put P; put " ";
		    if i inset vars
			then put (?vars i)
		    else raise PrintVarNotInEnv i)
		 | P in set blk
		 end
	       | i => blk in map Bs
	       end;
	       put "."
	   end
    end;

val perrclause = print_clause (stderr, "X");

fun sig_add (f, k, sigmap) =
    if f inset sigmap
	then if ?sigmap f = k
		 then sigmap
	     else raise InconsistentSig f
    else sigmap ++ {f => k};

fun head_sig (HVAR _, acc) = acc
  | head_sig (HFUN (_, f, k, ...), acc) = sig_add (f, k, acc)
  | head_sig (_, acc) = acc;

fun dt_sig (DV _, acc) = acc
  | dt_sig (DAPP (f, l), acc) =
    dtl_sig (l, sig_add (f, len l, acc))
and dtl_sig (nil, acc) = acc
  | dtl_sig ((_, t)::l, acc) =
    dtl_sig (l, dt_sig (t, acc));

fun atl_sig (nil, acc) = acc
  | atl_sig ((_, t)::l, acc) =
    atl_sig (l, dt_sig (t, acc));

fun al_sig (nil, acc) = acc
  | al_sig ((_, atl)::al, acc) =
    al_sig (al, atl_sig (atl, acc));

fun clause_sig (CL (h, ql, al, Bs, ...), acc) =
    head_sig (h, al_sig (al, acc));

fun clause_list_sig_1 (nil, acc) = acc
  | clause_list_sig_1 (c :: l, acc) =
    clause_list_sig_1 (l, clause_sig (c, acc));

fun clause_list_sig cl = clause_list_sig_1 (cl, {});

fun head_preds (HVAR P) = {P}
  | head_preds (HFUN (P, ...)) = {P}
  | head_preds _ = {};

fun atl_preds nil = {}
  | atl_preds ((P, _)::l) =
    {P} U atl_preds l;

fun al_preds nil = {}
  | al_preds ((_, atl)::al) =
    atl_preds atl U al_preds al;

fun Bs_preds Bs = union {blk
			| _ => blk in map Bs};

fun clause_preds (CL (h, ql, al, Bs, ...)) =
    head_preds h U al_preds al U Bs_preds Bs;

fun clause_list_preds cl = union {clause_preds c
				 | c in list cl};

fun clause_bots (CL (HBOT botname, ...)) = {botname}
  | clause_bots _ = {};

fun clause_list_bots cl = union {clause_bots c
				| c in list cl};

(* The standard ordering on atoms: *)

fun dt_less ((d, t), (d', t')) =
    d<d' orelse
    (d=d' andalso t_less (t, t'))
and t_less (DV _, DAPP _) = true
  | t_less (DV x, DV y) = x<y
  | t_less (DAPP (f, l), DAPP (f', l')) =
    f strless f' orelse
    (f=f' andalso tl_less (l, l'))
  | t_less _ = false
and tl_less (d::l, d'::l') =
    dt_less (d, d') orelse
    (d=d' andalso tl_less (l, l'))
  | tl_less (nil, nil) = false
  | tl_less (nil, _) = true
  | tl_less _ = false;

fun atom_less ((P, t), (P', t')) =
    P strless P'
    orelse (P=P' andalso t_less (t, t'));

fun atom_merge (atl as at::atr, atl' as at' :: atr') =
    if atom_less (at, at')
	then at' :: atom_merge (atl, atr')
    else if at=at'
	then at :: atom_merge (atr, atr')
    else at :: atom_merge (atr, atl')
  | atom_merge (nil, atl') = atl'
  | atom_merge (atl, nil) = atl;

fun atom_sort {} = []
  | atom_sort {at} = [at]
  | atom_sort ats = let val (ats1, ats2) = split ats
		    in
			atom_merge (atom_sort ats1, atom_sort ats2)
		    end;

exception GrInsert;

val depth_sort = sort (op <);

fun gr_insert (d, atl1, al as (d', atl)::al') = (* with d>=1 *)
    if d<d'
	then (d, atl1)::al
    else if d=d'
	then (d, atom_merge (atl1, atl))::al'
    else (d', atl)::gr_insert (d, atl1, al')
  | gr_insert (d, atl1, nil) = [(d, atl1)]
  | gr_insert _ = raise GrInsert;
    
(* gen_resolvent ([t1, ..., tn], [B1, ..., Bn], al, Bs)
 adds B1(t1), ..., Bn(tn) to part of clause referenced as al, Bs. *)

exception GenResolvent;

fun gen_resolvent (t1n, B1n, al, Bs) =
    let val Bsr = ref Bs
	val atomsr = ref ({} : int -m> atom set)
    in
	iterate
	  (case t of
	       DV x => let val curBs = !Bsr
		       in
			   if x inset curBs
			       then Bsr := curBs ++ {x => ?curBs x U B1}
			   else if empty B1
			       then ()
			   else Bsr := curBs ++ {x => B1}
		       end
	     | _ => let val curatoms = !atomsr
			val newatoms = {(P, t) | P in set B1}
		    in
			if empty newatoms
			    then ()
			else if d inset curatoms
			    then atomsr := curatoms ++ {d => ?curatoms d U newatoms}
			else atomsr := curatoms ++ {d => newatoms}
		    end)
	|| (d, t) in list t1n
	and B1 in list B1n
	end;
	       let val atoml = !atomsr
		   val depthl = depth_sort atoml
		   fun grl_insert (nil, al) = al
		     | grl_insert (d::dl, nil) =
		       (d, atom_sort (?atoml d)) :: grl_insert (dl, nil)
		     | grl_insert (dl1 as (d::dl), al as (d', atl)::al') =
		       if d<d'
			   then (d, atom_sort (?atoml d)) :: grl_insert (dl, al)
		       else if d=d'
			   then (d, atom_merge (atom_sort (?atoml d), atl))
			       :: grl_insert (dl, al')
		       else (d', atl) :: grl_insert (dl1, al')
	       in
		   (grl_insert (depthl, al), !Bsr)
	       end
    end;

(*
fun approximate_gen_resolvent (t1n, B1n, al, Bs) =
    gen_resolvent (t1n, B1n, nil, {});
*)

fun approximate_gen_resolvent arg =
    let val (al', Bs') = gen_resolvent arg
    in
	([da | da as (maxd, _) in list al'
		 such that maxd<=1],
	   Bs')
    end;

fun default_do_bot c = raise BotFoundEvt c;

fun al_less (nil, nil) = false
  | al_less (nil, _) = true
  | al_less ((maxd, atl)::al, (maxd', atl')::al') =
    maxd < maxd' orelse (maxd=maxd' andalso
			 let val l = len atl
			     val l' = len atl'
			 in
			     l < l' orelse
			     (l=l' andalso al_less (al, al'))
			 end)
  | al_less _ = false;

fun al_len nil = 0
  | al_len ((maxd, atl)::rest) = maxd * len atl + al_len rest;

(*
memofun Bs_len {} = 0
      | Bs_len {x => B} = card B
      | Bs_len Bs = let val (Bs1, Bs2) = split Bs
		    in
			Bs_len Bs1 + Bs_len Bs2
		    end;
*)

fun Bs_len {} = 0
  | Bs_len {_ => B} U rest = card B + Bs_len rest;

fun clause_category (CL (_, {}, nil, {}, ...)) = 0
  | clause_category (CL (HBOT _, ...)) = 3
  | clause_category (CL (_, _, nil, {}, ...)) = 1
  | clause_category (CL (h, ql, al, Bs, ...)) =
    if empty ql
	then if ignore_intermediate_selfq
		 then if null al
			  then 10000000 + (al_len al + Bs_len Bs)
		      else ~ (al_len al + Bs_len Bs)
	     else if null al
		 then 4 + al_len al + Bs_len Bs
	     else 10000000 + al_len al + Bs_len Bs
    else 2;

(* forward linear subsumption by automaton clauses: *)

datatype blocks = BS_ONE of proof (* {{}} *)
       | BS_ZERO (* {} *)
       | BS_BRANCH of int * (string * int) * blocks * blocks;
	 (* BS_BRANCH (minlen, (P, k), b1, b2) = {P(xk) /\ C | C in b1} U b2
	  minlen is the least length of all conjunctions starting from here. *)

fun trivial_blkl (0, acc) = acc
  | trivial_blkl (n, acc) = trivial_blkl (n-1, {}::acc);

fun blkls_from_blocks (BS_ONE pi, acc, i, n) = {rev (trivial_blkl (n-i, acc)) => pi}
  | blkls_from_blocks (BS_ZERO, ...) = {}
  | blkls_from_blocks (BS_BRANCH (_, (P, k), b1, b2), acc, i, n) =
    let val acc' = if i=k
		       then (hd acc U {P}) :: tl acc
		   else {P} :: trivial_blkl (k-i-1, acc)
    in
	blkls_from_blocks (b1, acc', k, n)
    end ++ blkls_from_blocks (b2, acc, i, n);
    
val pred_less = op strless;
val pred_sort = sort pred_less;

fun body_from_block_list blkl =
    let val ir = ref 0
    in
	append [let val k = (inc ir; !ir)
		in
		    [(P, k) | P in list pred_sort blk]
		end
	       | blk in list blkl]
    end;

fun blocks_from_body pi =
    let fun b_from_body (nil, _) = BS_ONE pi
	  | b_from_body (P_k :: rest, n) =
	    BS_BRANCH (n, P_k, b_from_body (rest, n-1), BS_ZERO)
    in
	b_from_body
    end;

fun bs_branch (_, _, BS_ZERO, b2) = b2
  | bs_branch c = BS_BRANCH c;

fun blocks_remove_subsumed (b as BS_ONE _, ...) = b
  | blocks_remove_subsumed (BS_ZERO, ...) = BS_ZERO
  | blocks_remove_subsumed (_, nil, ...) = BS_ZERO
  | blocks_remove_subsumed (b as BS_BRANCH (minlen, P_k, b1, b2), body as (P_k' :: rest), n) =
    if P_k = P_k'
	then bs_branch (minlen, P_k, blocks_remove_subsumed (b1, rest, n-1), b2)
    else let val (P, k) = P_k and (P', k') = P_k'
	 in
	     if pred_less (P, P') orelse P=P' andalso k<k'
		 then bs_branch (minlen, P_k, blocks_remove_subsumed (b1, body, n),
				 blocks_remove_subsumed (b2, body, n))
	     else b
	 end;

fun blocks_add_body pi = (* adds a body to a blocks structure, while removing all bodies
			  from it that are subsumed by body.
			  n is length of body. *)
    let val b_from_body = blocks_from_body pi
	fun b_add_body (_, nil, _) = BS_ONE pi
	  | b_add_body (BS_ONE pi', ...) = BS_ONE pi'
	  | b_add_body (BS_ZERO, body, n) = b_from_body (body, n)
	  | b_add_body (b as BS_BRANCH (minlen, P_k, b1, b2), body as (P_k' :: rest), n) =
	    if P_k = P_k'
		then BS_BRANCH (min (minlen, n), P_k, b_add_body (b1, rest, n-1), b2)
	    else let val (P, k) = P_k and (P', k') = P_k'
		 in
		     if pred_less (P, P') orelse P=P' andalso k<k'
			 then bs_branch (min (minlen, n), P_k,
					 blocks_remove_subsumed (b1, body, n),
					 b_add_body (b2, body, n))
		     else BS_BRANCH (min (minlen, n), P_k', b_from_body (rest, n-1), b)
		 end
    in
	b_add_body
    end;

fun b_set (c as CL (_, _, _, _, _, bsubsr)) =
    do_verbose_else (2,
		     fn () =>
			if !bsubsr
			    then ()
			else (#put stderr "  Removing backward subsumed non-automaton clause: ";
			      perrclause c;
			      #put stderr "\n";
			      #flush stderr ();
			      bsubsr := true),
			    fn () => bsubsr := true);

(* The prover: *)

memofun match_k 0 = {}
      | match_k 1 = {~1 => DV 1}
      | match_k n = {~n => DV n} ++ match_k (n-1);

exception ResolvePF_Depth0;
exception ResolvePF_EmptyAtomList;
exception ResolvePF_BadSelF;
exception ResolvePF_BadSelX;
exception ResolvePF_BadSelQ;
exception ResolvePF_BlackHole;

exception AlLast;

fun al_last [atl] = atl
  | al_last (_ :: rest) = al_last rest
  | al_last nil = raise AlLast;

fun is_tautology (CL (HVAR P, _, _, Bs, ...)) =
    1 inset Bs andalso P inset ?Bs 1
  | is_tautology (CL (HFUN _, _, nil, ...)) = false
  | is_tautology (CL (HFUN (P, f, k, vars, t), _, al, ...)) =
    let val a = (P, t)
	val (d, atl) = al_last al
    in
	d=1 (* Note: f (x1, ..., xk) is of depth 1. *)
	andalso exists a=a' | a' in list atl end
    end
  | is_tautology (CL (HQ q, ql, ...)) = q inset ql
  | is_tautology _ = false;

datatype predicate = P_BOT of string
       | P_PRED of string
       | P_ROOT;

exception ResolveUseful;
exception InfosMax;

fun opt_inter (NONE, fso) = fso
  | opt_inter (fso, NONE) = fso
  | opt_inter (SOME fs, SOME fs') = SOME (fs & fs');

datatype pathset = PS_ALL
       | PS_ENUM of string * int -m> pathset; (* maps (f, i) to non-empty pathset;
					       maps (f, 0) to PS_ALL in case f is a constant. *)

val ps_empty = PS_ENUM {};

fun ps_union (PS_ALL, _) = PS_ALL
  | ps_union (_, PS_ALL) = PS_ALL
  | ps_union (PS_ENUM fimap, PS_ENUM fimap') =
    PS_ENUM ((fimap delta fimap') ++ {fi => ps_union (ps, ps')
				     | fi => ps in map fimap' <| fimap
					 val ps' = ?fimap' fi});

fun ps_dunion {} = ps_empty
  | ps_dunion {ps} = ps
  | ps_dunion pss = let val (pss1, pss2) = split pss
		    in
			ps_union (ps_dunion pss1, ps_dunion pss2)
		    end;

fun ps_inter (PS_ALL, ps) = ps
  | ps_inter (ps, PS_ALL) = ps
  | ps_inter (PS_ENUM fimap, PS_ENUM fimap') =
    PS_ENUM {fi => ps''
	    | fi => ps in map (fimap' <| fimap)
		val ps' = ?fimap' fi
		val ps'' = ps_inter (ps, ps')
		such that ps''<>ps_empty};

(* ps_prune (ps, ps') removes as much as it can from ps, considering ps';
 this should be such that ps_union (ps', ps_prune (ps, ps')) = ps. *)

fun ps_prune (_, PS_ALL) = ps_empty
  | ps_prune (PS_ALL, ps) = PS_ALL
  | ps_prune (PS_ENUM fimap, PS_ENUM fimap') =
    PS_ENUM ((fimap' <-| fimap) ++
	     {fi => ps''
	     | fi => ps in map (fimap' <| fimap)
		 val ps' = ?fimap' fi
		 val ps'' = ps_prune (ps, ps')
		 such that ps''<>ps_empty});

datatype skeleton = SK_PATHSET of pathset
       | SK_ENUM of string -m> skeleton list set;

val sk_empty = SK_ENUM {};

fun ps_from_sk (SK_PATHSET ps) = ps
  | ps_from_sk (SK_ENUM fmap) =
    let val ir = ref 0
    in
	ps_dunion (union {{(case skl of
				nil => PS_ENUM {(f, 0) => PS_ALL}
			      | _ => (ir := 0;
				      PS_ENUM {(f, (inc ir; !ir)) =>
						   ps_from_sk sk
					      | sk in list skl}
				      )
				)
			  | skl in set skls}
			 | f => skls in map fmap})
    end;

fun sk_union (SK_PATHSET ps1, SK_PATHSET ps2) =
    SK_PATHSET (ps_union (ps1, ps2))
  | sk_union (SK_PATHSET ps1, sk2) =
    SK_PATHSET (ps_union (ps1, ps_from_sk sk2))
  | sk_union (sk1, SK_PATHSET ps2) =
    SK_PATHSET (ps_union (ps_from_sk sk1, ps2))
  | sk_union (SK_ENUM fmap1, SK_ENUM fmap2) =
    SK_ENUM ((fmap1 delta fmap2) ++
		 {f => skls1 U skls2
		 | f => skls1 in map fmap2 <| fmap1
		   val skls2 = ?fmap2 f});

exception SkInterEmptyEvt;

fun ps_sk_inter (PS_ALL, sk) = sk
  | ps_sk_inter (PS_ENUM fimap, SK_ENUM fmap) =
    ps_sk_map_inter (fimap, fmap)
  | ps_sk_inter (ps, SK_PATHSET ps') =
    (case ps_inter (ps, ps') of
	 PS_ENUM {} => sk_empty
       | ps => SK_PATHSET ps)
and ps_sk_map_inter (fimap, fmap) =
    let val ir = ref 0
    in
	SK_ENUM {f => skls'
		| f => skls in map fmap
		  val skls' = {skl'
			      | skl in set skls
				val SOME skl' = (ir := 0;
						 SOME [(case sk' of
							    SK_ENUM {} => raise SkInterEmptyEvt
							  | _ => sk')
						      | sk in list skl
							  val fi = (inc ir; (f, !ir))
							  val sk' =
							      ps_sk_inter (if fi inset fimap
									       then ?fimap fi
									   else raise SkInterEmptyEvt,
									       sk)
							      ] handle SkInterEmptyEvt => NONE)
				    }
		      such that not (empty skls')
		  }
    end;

fun sk_inter (SK_PATHSET ps1, SK_PATHSET ps2) =
    (case ps_inter (ps1, ps2) of
	 PS_ENUM {} => sk_empty
       | ps => SK_PATHSET ps)
  | sk_inter (SK_PATHSET ps, sk) = ps_sk_inter (ps, sk)
  | sk_inter (sk, SK_PATHSET ps) = ps_sk_inter (ps, sk)
  | sk_inter (SK_ENUM fmap1, SK_ENUM fmap2) =
    SK_ENUM {f => skls
	    | f => skls1 in map fmap2 <| fmap1
		val skls2 = ?fmap2 f
		val skls = {skl
			   | skl1 in set skls1 and skl2 in set skls2
			       val SOME skl = (SOME [(case sk_inter (sk1, sk2) of
							  SK_ENUM {} => raise SkInterEmptyEvt
							| sk => sk)
						    || sk1 in list skl1 and sk2 in list skl2]
					       handle SkInterEmptyEvt => NONE)}
		    such that not (empty skls)};

exception SkPruneEmptyEvt;

fun ps_sk_prune (PS_ALL, sk) = sk
  | ps_sk_prune (ps, sk) =
    (case ps_prune (ps, ps_from_sk sk) of
	 PS_ENUM {} => sk_empty
       | ps => SK_PATHSET ps);

fun sk_ps_prune (_, PS_ALL) = sk_empty
  | sk_ps_prune (sk, ps) =
    (case ps_prune (ps_from_sk sk, ps) of
	 PS_ENUM {} => sk_empty
       | ps => SK_PATHSET ps);

(* sk_prune (sk, sk') should be such that sk_union (sk', sk_prune (sk, sk'))
 contains at least sk. *)

fun sk_prune (SK_PATHSET ps1, SK_PATHSET ps2) =
    (case ps_prune (ps1, ps2) of
	 PS_ENUM {} => sk_empty
       | ps => SK_PATHSET ps)
  | sk_prune (SK_PATHSET ps1, sk) = ps_sk_prune (ps1, sk)
  | sk_prune (sk, SK_PATHSET ps2) = sk_ps_prune (sk, ps2)
  | sk_prune (SK_ENUM fmap1, SK_ENUM fmap2) =
    SK_ENUM ((fmap2 <-| fmap1) ++
		 {f => skls'1
		 | f => skls1 in map fmap2 <| fmap1
		     val skls2 = ?fmap2 f
		     val skls'1 = {skl1
				  | skl1 in set skls1
				    such that
					all
					  exists
					    sk_prune (sk1, sk2)<>sk_empty
					  || sk1 in list skl1 and sk2 in list skl2
					  end
					| skl2 in set skls2
					end}
			 such that not (empty skls'1)
		   });

fun ps_funs PS_ALL = NONE
  | ps_funs (PS_ENUM fimap) = SOME {f | (f, _) in set fimap}

fun sk_funs (SK_PATHSET ps) = ps_funs ps
  | sk_funs (SK_ENUM fmap) = SOME (dom fmap)

exception UResolve;

exception NoPathSetEvt;

type clause_env = clause * (int -m> skeleton);

fun compute_skeleta (cl, maxskdepth, maxpathlen) =
    let val facts = ref ({} : string -m> skeleton)
	val qq = ref ({} : block set)
	val bots = ref ({} : string set)

	val selqq = ref ({} : block -m> clause_env set ref)
	val selxq = ref ({} : string -m> clause_env set ref)
	val selfq = ref ({} : string -m> (string -m> clause_env set ref) ref)

	val waitq = ref (nil : clause_env list)

	fun ps_match_term (_, PS_ALL, env) = env
	  | ps_match_term (DV x, ps, env) =
	    if x inset env
		then let val sk' = sk_inter (SK_PATHSET ps, ?env x)
		     in
			 case sk' of
			     SK_ENUM {} => raise NoPathSetEvt
			   | _ => env ++ {x => sk'}
		     end
	    else env ++ {x => SK_PATHSET ps}
	  | ps_match_term (DAPP (f, nil), PS_ENUM fimap, env) =
	    if (f, 0) inset fimap
		then env
	    else raise NoPathSetEvt
	  | ps_match_term (DAPP (f, l), PS_ENUM fimap, env) =
	    ps_match_term_list (f, l, 1, fimap, env)
	and ps_match_term_list (_, nil, _, _, env) = env
	  | ps_match_term_list (f, (_, ti)::l, i, fimap, env) =
	    let val fi = (f, i)
	    in
		if fi inset fimap
		    then let val env' = ps_match_term (ti, ?fimap fi, env)
			 in
			     ps_match_term_list (f, l, i+1, fimap, env')
			 end
		else raise NoPathSetEvt
	    end

	fun match_term (DV x, sk, env) =
	    if x inset env
		then let val sk' = sk_inter (sk, ?env x)
		     in
			 case sk' of
			     SK_ENUM {} => {}
			   | _ => {env ++ {x => sk'}}
		     end
	    else {env ++ {x => sk}}
	  | match_term (_, SK_PATHSET PS_ALL, env) = {env}
	  | match_term (DAPP (f, l), SK_ENUM fmap, env) =
	    if f inset fmap
		then let val skls = ?fmap f
		     in
			 union {match_term_list (l, skl, env)
			       | skl in set skls}
		     end
	    else {}
	  | match_term (t, SK_PATHSET ps, env) =
	    {ps_match_term (t, ps, env)} handle NoPathSetEvt => {}
	and match_term_list ((_, t)::l, sk::skl, env) =
	    union {match_term_list (l, skl, env')
		  | env' in set match_term (t, sk, env)}
	  | match_term_list (_, _, env) = {env}

	fun match_atom ((P, t), env) =
	    if P inset !facts
		then match_term (t, ?(!facts) P, env)
	    else {}

	fun uinsert c = waitq := c :: !waitq

	fun sk_add1 (P, sk) =
	    (if P inset !facts
		 then facts := !facts ++ {P => sk_union (sk, ?(!facts) P)}
	     else facts := !facts ++ {P => sk};
		 if P inset !selxq
		     then let val cs = !(?(!selxq) P)
			  in
			      iterate
				iterate
				  uinsert (CL (h, ql, al,
					       if empty B
						   then rest
					       else rest ++ {i => B},
						   pi, bsubsr),
					   env')
				| env' in set match_term (DV i, sk, env)
				end
			      | (c as CL (h, ql, al, {i => {P} U B} U rest, pi, bsubsr), env) in set cs
			      end
			  end
		 else ();
		     if P inset !selfq
			 then let val fmap1 = !(?(!selfq) P)
				  val fmap = case sk_funs sk of
						 SOME fs => fs <| fmap1
					       | _ => fmap1
			      in
				  iterate
				    iterate
				      iterate
					uinsert (CL (h, ql,
						     if null atl
							 then al
						     else (d, atl)::al,
							 Bs, pi, bsubsr),
						 env')
				      | env' in set match_term (t, sk, env)
				      end
				    | (c as CL (h, ql, (d, (_, t)::atl)::al, Bs, pi, bsubsr), env) in set cs
				    end
				  | f => ref cs in map fmap
				  end
			      end
		     else ())
	fun sk_add (P, sk) =
	    let val sk' = if P inset !facts
			      then sk_prune (sk, ?(!facts) P)
			  else sk
	    in
		case sk' of
		    SK_ENUM {} => ()
		  | _ => sk_add1 (P, sk')
	    end

	fun ps_chop (PS_ALL, _) = PS_ALL
	  | ps_chop (_, 0) = PS_ALL
	  | ps_chop (PS_ENUM fimap, n) =
	    let val n' = n-1
	    in
		PS_ENUM {fi => ps_chop (ps, n')
			| fi => ps in map fimap}
	    end
	fun sk_chop (SK_PATHSET ps, _) = SK_PATHSET (ps_chop (ps, maxpathlen))
	  | sk_chop (sk, 0) = SK_PATHSET (ps_chop (ps_from_sk sk, maxpathlen))
	  | sk_chop (SK_ENUM fmap, n) =
	    let val n' = n-1
	    in
		SK_ENUM {f => {[sk_chop (sk, n')
			       | sk in list skl]
			      | skl in set skls}
			| f => skls in map fmap}
	    end

	fun uresolve (c as CL (h, {q} U ql, al, Bs, pi, bsubsr), env) =
	    if q inset !qq
		then uinsert (CL (h, ql, al, Bs, pi, bsubsr), env)
	    else if q inset !selqq
		then let val clsr = ?(!selqq) q
		     in
			 clsr := !clsr U {(c, env)}
		     end
	    else selqq := !selqq ++ {q => ref {(CL (h, ql, al, Bs, pi, bsubsr), env)}}
	  | uresolve (c as CL (h, _, (_, nil)::al, Bs, pi, bsubsr), env) =
	    uresolve (CL (h, {}, al, Bs, pi, bsubsr), env)
	  | uresolve (c as CL (h, _, (d, a::atl)::al, Bs, pi, bsubsr), env) =
	    let val c' = CL (h, {}, (d, atl)::al, Bs, pi, bsubsr)
	    in
		iterate
		  uinsert (c', env')
		| env' in set match_atom (a, env)
		end;
		case a of
		    (P, DAPP (f, l)) =>
		    if P inset !selfq
			then let val fmapr = ?(!selfq) P
			     in
				 if f inset !fmapr
				     then let val clsr = ?(!fmapr) f
					  in
					      clsr := !clsr U {(c, env)}
					  end
				 else fmapr := !fmapr ++ {f => ref {(c, env)}}
			     end
		    else selfq := !selfq ++ {P => ref {f => ref {(c, env)}}}
		  | _ => raise UResolve
	    end
	  | uresolve (c as CL (h, _, _, {i => B0} U rest, pi, bsubsr), env) =
	    (case B0 of
		 {P} U B =>
		   let val c' = CL (h, {}, nil,
				    if empty B
					then rest
				    else rest ++ {i => B},
				 pi, bsubsr)
		       val a = (P, DV i)
		   in
		       iterate
			 uinsert (c', env')
		       | env' in set match_atom (a, env)
		       end;
		       if P inset !selxq
			   then let val csr = ?(!selxq) P
				in
				    csr := !csr U {(c, env)}
				end
		       else selxq := !selxq ++ {P => ref {(c, env)}}
		   end
	       | _ => uresolve (CL (h, {}, nil, rest, pi, bsubsr), env))
	  | uresolve (c as CL (h, ...), env) =
	    (case h of
		 HVAR P => sk_add (P, if 1 inset env
					  then ?env 1
				      else SK_PATHSET PS_ALL)
	       | HBOT botname => bots := !bots U {botname}
	       | HQ q => (qq := !qq U {q};
			  if q inset !selqq
			      then let val cs = !(?(!selqq) q)
				   in
				       iterate
					 uinsert c_env
				       | c_env in set cs
				       end
				   end
			  else ())
	       | HFUN (P, f, k, vars, ...) =>
		 let val ir = ref 0
		     val sk = SK_ENUM {f => {[(inc ir;
					       if !ir inset env
						   then sk_chop (?env (!ir), maxskdepth)
					       else SK_PATHSET PS_ALL)
					     |while !ir<k]}}
		 in
		     sk_add (P, sk)
		 end)
	  | uresolve (c, env) = raise UResolve
    in
	iterate
	  uinsert (c, {})
	| c in list cl
	end;
	while not (null (!waitq)) do
	    let val c_env :: rest = !waitq
	    in
		waitq := rest;
		uresolve c_env
	    end;
	    (!facts, !bots)
    end

(*
fun opt_dinter {} = NONE
  | opt_dinter {fso} = fso
  | opt_dinter fsos = let val (fsos1, fsos2) = split fsos
		      in
			  opt_inter (opt_dinter fsos1, opt_dinter fsos2)
		      end;
*)

exception FunHeadOptEvt;

fun resolver (do_bot : clause -> unit, (split_approx, resolve_approx), maxskdepth, maxpathlen) =
    let val gen = case resolve_approx of
		      CLAUSE_NO_APPROX => gen_resolvent
		    | CLAUSE_2_CLAUSE => approximate_gen_resolvent
	val predicate_graph = ref ({} : predicate digraph)
	val clause_list = ref (nil : clause list)
	val funheads = ref ({} : string -m> string set option)
	    (* map P to SOME fs: then P(t) only holds when t=f(...) with f in fs;
	     if P is mapped to NONE, then we do not know. *)
	val skeleta = ref ({} : string -m> skeleton)
	val skel_bots = ref ({} : string set)

	    (*
	val dealtwith = ref ({} : clause set)
	     *)

	fun add_graph (pred, CL (_, ql, al, Bs, ...)) =
	    predicate_graph := !predicate_graph Urel {pred => union {{P_PRED P
								     | P in set blk}
								    | blk in set ql}
						       U union {{P_PRED P
								| (P, _) in list atl}
							       | (_, atl) in list al}
						       U union {{P_PRED P
								| P in set blk}
							       | _ => blk in map Bs}}
	fun new_clause (c as CL (HBOT botname, ...)) =
	    (predicate_graph := !predicate_graph Urel {P_ROOT => {P_BOT botname}};
	     add_graph (P_BOT botname, c);
	     clause_list := c :: !clause_list)
	  | new_clause (c as CL (HVAR P, ...)) =
	    (add_graph (P_PRED P, c);
	     funheads := !funheads ++ {P => NONE};
	     clause_list := c :: !clause_list)
	  | new_clause (c as CL (HFUN (P, f, ...), ...)) =
	    (add_graph (P_PRED P, c);
	     if P inset !funheads
		 then case ?(!funheads) P of
			  SOME fs => funheads := !funheads ++ {P => SOME (fs U {f})}
			| _ => ()
	     else funheads := !funheads ++ {P => SOME {f}};
	     clause_list := c :: !clause_list)
	  | new_clause (c as CL (HQ blk, ...)) =
	    (iterate
	       add_graph (P_PRED P, c)
	     | P in set blk
	     end;
	       clause_list := c :: !clause_list)

	val dfs_info = ref ({} : predicate -m> int * int) (* dfsnum, revtopsort number of low *)
	val scc_info = ref ({} : int -m> predicate set) (* map (revtopsort of) low to corresponding scc *)
	val dfs_q_info = ref ({} : block -m> int * int) (* max {(revnum P, low P) | P in blk},
							 where the max is taken in the right-to-left
							 lexicographic ordering. *)
	fun useful (CL (HBOT botname, ...)) = P_BOT botname inset !dfs_info
	  | useful (CL (HVAR P, ...)) = P_PRED P inset !dfs_info
	  | useful (CL (HFUN (P, ...), ...)) = P_PRED P inset !dfs_info
	  | useful (CL (HQ blk, ...)) =
	    all
	      P_PRED P inset !dfs_info
	    | P in set blk
	    end

	fun info_less ((revnum, low), (revnum', low')) =
	    low<low' orelse low=low' andalso revnum<revnum'
	fun info_max (dlow, dlow') =
	    if info_less (dlow, dlow')
		then dlow'
	    else dlow
	fun infos_max {} = raise InfosMax
	  | infos_max {dlow} = dlow
	  | infos_max dlows = let val (dlows1, dlows2) = split dlows
			      in
				  info_max (infos_max dlows1, infos_max dlows2)
			      end
	fun p_info P = ?(!dfs_info) P
	fun head_info (CL (HBOT botname, ...)) = p_info (P_BOT botname)
	  | head_info (CL (HVAR P, ...)) = p_info (P_PRED P)
	  | head_info (CL (HFUN (P, ...), ...)) = p_info (P_PRED P)
	  | head_info (CL (HQ blk, ...)) =
	    let val dfs_q = !dfs_q_info
	    in
		if blk inset dfs_q
		    then ?dfs_q blk
		else let val info = !dfs_info
			 val m = infos_max {p_info (P_PRED P)
					   | P in set blk}
		     in
			 dfs_q_info := dfs_q ++ {blk => m};
			 m
		     end
	    end

	val waitq as |[ insert = wait_insert_basic,
			popmin = wait_pop,
			empty = wait_empty,
			dump_list = get_clauses,
			... ]|
	    = mheap (fn (((revnum, low), n), ((revnum', low'), n')) =>
			n<n' orelse
			(n=n' andalso (low<low' orelse
				       (low=low' andalso revnum<revnum'))))
	(* list of unprocessed clauses; may even be automaton clauses,
	 we do not know yet. *)
	fun wait_insert_1 c =
	    (*
	    if c inset !dealtwith
		then do_verbose (2, fn () =>
				 (#put stderr "Removed clause early: ";
				  perrclause c;
				  #put stderr "\n";
				  #flush stderr ()))
	    else
	     *)
		(do_verbose (1, fn () => (#put stderr "|- ";
					  perrclause c;
					  let val CL (_, _, _, _, pi, ...) = c
					  in
					      #put stderr "   ";
					      case pi of
						  P_GIVEN _ => #put stderr "[given]"
						| P_RESOLVE _ => #put stderr "[by resolution]"
						| P_SPLIT_USE _ => #put stderr "[by splitting]"
						| P_SPLIT_DEF _ =>
						  #put stderr "[defining clause for splitting symbol]"
						| _ => #put stderr "[?]"
				      end;
					  #put stderr "\n";
					  #flush stderr ()));
		 wait_insert_basic ((head_info c, clause_category c), c))
		
	memofun q_funheads {} = NONE
	      | q_funheads {P} = if P inset !funheads then ?(!funheads) P else SOME {}
	      | q_funheads blk = let val (blk1, blk2) = split blk
				 in
				     opt_inter (q_funheads blk1, q_funheads blk2)
				 end

	memofun q_skeleta {} = SK_PATHSET PS_ALL
	      | q_skeleta {P} = if P inset !skeleta then ?(!skeleta) P else sk_empty
	      | q_skeleta blk = let val (blk1, blk2) = split blk
				in
				    sk_inter (q_skeleta blk1, q_skeleta blk2)
				end

	fun match_term_shape (SK_PATHSET PS_ALL, _) = true
	  | match_term_shape (_, DV _) = true
	  | match_term_shape (SK_ENUM fmap, DAPP (f, l)) =
	    f inset fmap andalso
	    let val skls = ?fmap f
	    in
		exists
		  all
		    match_term_shape (sk, t)
		  || sk in list skl and (_, t) in list l
		  end
		| skl in set skls
		end
	    end
	  | match_term_shape (SK_PATHSET (PS_ENUM fimap), DAPP (f, nil)) =
	    (f, 0) inset fimap
	  | match_term_shape (SK_PATHSET (PS_ENUM fimap), DAPP (f, l)) =
	    let val ir = ref 0
	    in
		all
		  let val fi = (inc ir; (f, !ir))
		  in
		      fi inset fimap
		      andalso match_term_shape (SK_PATHSET (?fimap fi), ti)
		  end
		| (_, ti) in list l
		end
	    end

	fun wait_insert_2 (c as CL (h, _, al, Bs, ...)) =
	    (* check that funheads can be satisfied *)
	    (if exists
		 q_funheads blk = SOME {}
		| _ => blk in map Bs
		end orelse
		 exists
		   exists
		     not (P inset !funheads andalso
			  (case ?(!funheads) P of
			       SOME fs => f inset fs
			     | NONE => true))
		   | (P, DAPP (f, _)) in list atl
		   end
		 | (_, atl) in list al
		 end
		 then do_verbose (3, fn () =>
				  (#put stderr "Removed clause by funheads optimization(2): ";
				   perrclause c;
				   #put stderr "\n";
				   #flush stderr ()))
	     else if
		 exists
		   (case q_skeleta blk of
			SK_ENUM {} => true
		      | _ => false)
		 | _ => blk in map Bs
		 end orelse
		 exists
		   exists
		     not (P inset !skeleta andalso
			  match_term_shape (?(!skeleta) P, t))
		   | (P, t) in list atl
		   end
		 | (_, atl) in list al
		 end
		 then do_verbose (3, fn () =>
				  (#put stderr "Removed clause by funheads optimization(4): ";
				   perrclause c;
				   #put stderr "\n";
				   #flush stderr ()))
	     else
		 wait_insert_1 c)

	fun wait_insert c =
	    if is_tautology c
		then ()
	    else let val CL (h, ql, al, Bs, _, bsubsr) = c
		     val splitBs1 = head_vars h <-| Bs
		     val splitBs = if empty splitBs1
				       then {}
				   else al_vars al <-| splitBs1
		 in
		     if empty splitBs
			 then wait_insert_2 c
		     else
			 case split_approx of
			     SPLIT_NO_APPROX =>
			     (* split: first generate splitting clauses *)
			     ((iterate
				 (case q_funheads blk of
				      SOME {} =>
				      (do_verbose (3, fn () =>
						   (#put stderr "Removed clause \
						    \by funheads optimization(1): ";
						    perrclause c;
						    #put stderr "\n";
						    #flush stderr ()));
				       raise FunHeadOptEvt)
				    | _ =>
				      (case q_skeleta blk of
					   SK_ENUM {} => (do_verbose (3, fn () =>
								      (#put stderr "Removed clause \
								       \by funheads optimization(3): ";
								       perrclause c;
								       #put stderr "\n";
								       #flush stderr ()));
							  raise FunHeadOptEvt)
					 | _ => 
					   wait_insert_1 (CL (HQ blk, {}, nil, {1 => blk},
							      p_split_def (c, splitBs, x), ref false))))
			       | x => blk in map splitBs
			       end; (* then process split clause *)
				 wait_insert_2 (CL (h, rng splitBs, al, splitBs <-| Bs,
						    p_split_use (c, splitBs), bsubsr))
				 )
				  handle FunHeadOptEvt => ())
			   | SPLIT_APPROX_AND =>
			     ((iterate
				 (case q_funheads blk of
				      SOME {} => (do_verbose (3, fn () =>
							      (#put stderr "Removed clause \
							       \by funheads optimization(1): ";
							       perrclause c;
							       #put stderr "\n";
							       #flush stderr ()));
						  raise FunHeadOptEvt)
				    | _ =>
				      (wait_insert_1 (CL (HQ blk, {{P} | P in set blk}, nil, {},
							  P_GIVEN "*magic*", ref false));
				       iterate
					 wait_insert_1 (CL (HQ {P}, {}, nil, {1 => {P}},
							    p_split_def (c, splitBs, x), ref false))
				       | P in set blk
				       end))
			       | x => blk in map splitBs
			       end; (* then process split clause *)
				 wait_insert_2 (CL (h, rng splitBs, al, splitBs <-| Bs,
						    p_split_use (c, splitBs), bsubsr))
				 )
				  handle FunHeadOptEvt => ())
			   | SPLIT_APPROX_OR =>
			     ((iterate
				 (case q_funheads blk of
				      SOME {} => (do_verbose (3, fn () =>
							      (#put stderr "Removed clause \
							       \by funheads optimization(1): ";
							       perrclause c;
							       #put stderr "\n";
							       #flush stderr ()));
						  raise FunHeadOptEvt)
				    | _ =>
				      (iterate
					 (wait_insert_1 (CL (HQ blk, {{P}}, nil, {},
							     P_GIVEN "*magic*", ref false));
					  wait_insert_1 (CL (HQ {P}, {}, nil, {1 => {P}},
							     p_split_def (c, splitBs, x), ref false)))
				       | P in set blk
				       end))
			       | x => blk in map splitBs
			       end; (* then process split clause *)
				 wait_insert_2 (CL (h, rng splitBs, al, splitBs <-| Bs,
						    p_split_use (c, splitBs), bsubsr))
				 )
				  handle FunHeadOptEvt => ())
			   | SPLIT_IGNORE =>
			     ((iterate
				 (case q_funheads blk of
				      SOME {} => (do_verbose (3, fn () =>
							      (#put stderr "Removed clause \
							       \by funheads optimization(1): ";
							       perrclause c;
							       #put stderr "\n";
							       #flush stderr ()));
						  raise FunHeadOptEvt)
				    | _ => wait_insert_1 (CL (HQ blk, {}, nil, {}, P_GIVEN "*magic*",
							      ref false)))
			       | x => blk in map splitBs
			       end; (* then process split clause *)
				 wait_insert_2 (CL (h, rng splitBs, al, splitBs <-| Bs,
						    p_split_use (c, splitBs), bsubsr))
				 )
				  handle FunHeadOptEvt => ())
		 end

	val fargsq = ref ({} : string -m> int * int set * dterm1);
	(* map f to (k, {1, ..., k}, f(x1,...,xk)),
	 where k is the arity of f.*)

	val univq = ref ({} : automaton_univ_trans)
	(* set of predicates P for which we have a clause +P(x). *)
	val qq = ref ({} : block -m> proof);
	(* current set of unit clauses +q. *)
	val selfq = ref ({} : string -m> (string -m> clause set ref) ref)
	(* maps P, f to clauses C \/ -P(f(...))
	 where -P(f(...)) is selected. *)
	val selxq = ref ({} : string -m> clause set ref)
	(* maps P to clauses C \/ -P(x), with -P(x) selected. *)
	val selqq = ref ({} : block -m> clause set ref)
	(* maps q to clauses C \/ -q, with -q selected. *)
	val botq = ref ({} : string set);
	(* current set of bot names that have been derived. *)

	val autoinfoq = ref ({} : string -m> (string -m> blocks) ref)
	(* current set of automata clauses, maps P, f, to blocks
	 where blkls is the set of possible bodies, each mapped to a proof of the
	 corresponding pop clause;
	 each body is a list of blocks, one for each variable argument (1, ..., k) to f.
	 Also used by subsumption engine. *)

	fun insert_selfq (c, P, f) =
	    (do_verbose (1, fn () => (#put stderr "selfq clause: ";
				      perrclause c;
				      let val CL (_, _, _, _, pi, ...) = c
				      in
					  #put stderr "   ";
					  case pi of
					      P_GIVEN _ => #put stderr "[given]"
					    | P_RESOLVE _ => #put stderr "[by resolution]"
					    | P_SPLIT_USE _ => #put stderr "[by splitting]"
					    | P_SPLIT_DEF _ =>
					      #put stderr "[defining clause for splitting symbol]"
					    | _ => #put stderr "[?]"
				      end;
				      #put stderr "\n";
				      #flush stderr ()));
	     if P inset !selfq
		 then let val fmapr = ?(!selfq) P
		      in
			  if f inset !fmapr
			      then let val clsr = ?(!fmapr) f
				   in
				       clsr := !clsr U {c}
				   end
			  else fmapr := !fmapr ++ {f => ref {c}}
		     end
	     else selfq := !selfq ++ {P => ref {f => ref {c}}})

	fun clause_compile_subsumed (P, f, blkl, pi) =
	    let val body = body_from_block_list blkl
	    in
		if P inset !autoinfoq
		    then let val fmapr = ?(!autoinfoq) P
			 in
			     if f inset !fmapr
				 then let val blocks = blocks_add_body pi (?(!fmapr) f, body, len body)
				      in
					  fmapr := !fmapr ++ {f => blocks}
				      end
			     else fmapr := !fmapr ++ {f => blocks_from_body pi (body, len body)}
			 end
		else autoinfoq := !autoinfoq ++ {P => ref {f => blocks_from_body pi (body, len body)}}
	    end

	fun block_subsumed Bs =
	    let val n = Bs_len Bs
		fun bsubsumed (BS_ONE _, ...) = true
		  | bsubsumed (BS_ZERO, ...) = false
		  | bsubsumed (BS_BRANCH (minlen, (P, k), b1, b2), n) =
		    minlen<=n andalso
		    (k inset Bs andalso P inset ?Bs k andalso bsubsumed (b1, n-1)
		     orelse bsubsumed (b2, n))
	    in
		fn blocks => bsubsumed (blocks, n)
	    end

	fun ct_subsumed (CL (HVAR P, ...)) =
	    (* check whether +P(x1) is already in univq first: *)
	    P inset !univq
	    (* Otherwise, clause can only be subsumed by non-automata clauses [in selfq or selxq or selqq],
	     with head +P(x1) as well.  We don't test this. *)
	  | ct_subsumed (CL (HFUN (P, f, ...), _, _, blkl, ...)) =
	    P inset !univq orelse
	    P inset !autoinfoq andalso
	    let val fmap = !(?(!autoinfoq) P)
	    in
		f inset fmap andalso block_subsumed blkl (?fmap f)
	    end
	  | ct_subsumed (CL (HQ q, ...)) =
	    q inset !qq
	  | ct_subsumed (CL (HBOT botname, ...)) =
	    botname inset !botq

	fun elim_subsumed_by_univ P =
	    (autoinfoq := {P} <-| !autoinfoq)

	(* resolve_P_f c
	 Resolves clause c against all automata clauses in autoinfoq, qq (if c is a non-automaton clause)
	 or against all non-automata clauses in selfq, selxq (if c is an automaton clause).
	 Adds all resolvents to waitq, as objects of type 'clause', even though
	 some are automata clauses.
	 *)
	fun resolve_P_f (c as CL (h, {q} U ql, al, Bs, pi, bsubsr)) =
	    (if q inset !qq
		 then wait_insert (CL (h, ql, al, Bs,
				       p_resolve (AC_Q (q, ?(!qq) q), c, MGU_EMPTY),
				       bsubsr))
	     (* resolve C \/ -q with +q, and return C in front of acc.
	      Do not add c either to forward subsumption structures or to selqq,
	      because resolvent subsumes premise. *)
	     else ( (* otherwise cannot resolve. *)
		   (* Now update selqq: *)
		   if q inset !selqq
		       then let val clsr = ?(!selqq) q
			    in
				clsr := !clsr U {c}
			    end
		   else selqq := !selqq ++ {q => ref {c}})
		 )
	  | resolve_P_f (c as CL (h, {}, al as (maxd, (P, t as DAPP (f, l))::atl) :: rest, Bs,
				  pi, bsubsr)) =
	    (if P inset !univq
		 then wait_insert (CL (h, {},
				       if null atl
					   then rest
				       else (maxd, atl)::rest, Bs,
					   p_resolve (AC_UNIV (P, ?(!univq) P), c,
						      MGU_AUTO_X1_IS t),
					   bsubsr))
	     (* Do not add c either to forward subsumption structures or to selfq,
	      because resolvent subsumes premise. *)
	     else (if P inset !autoinfoq
		       then let val fmap = !(?(!autoinfoq) P)
			    in
				if f inset fmap
				    then let val (k, vars, t) = ?(!fargsq) f
					     val blkls = blkls_from_blocks (?fmap f, nil, 0, k)
					 in
					     iterate
					       (* Resolve C <= P(f(t1, ..., tn)) [where l = [t1, ..., tn]]
						with P (f (x1, ..., xn)) <= B1(x1), ..., Bn(xn)
						[where blkl = [B1, ..., Bn]]
						=> generate C <= B1(t1), ..., Bn(tn)
						*)
					       let val (al', Bs') = gen (l, blkl,
									 if null atl
									     then rest
									 else (maxd, atl)::rest, Bs)
					       in
						   wait_insert (CL (h, {}, al', Bs',
								    p_resolve (AC_POP (P, f, blkl,
										       k, vars, t, pi'),
									       c, MGU_AUTO_Xs_ARE l),
								    ref false))
					       end
					     | blkl => pi' in map blkls
					     end
					 end
				else () (* cannot resolve: no automaton clause with head P (f (...)). *)
			    end
		   else () (* cannot resolve: no automaton clause with head P(...) *);
		       (* Now update selfq: *)
		       if ignore_intermediate_selfq
			   then ()
		       else insert_selfq (c, P, f)
			   ))
	  | resolve_P_f (CL (_, _, (_, (_, DV _) :: _) :: _, ...)) =
	    raise ResolvePF_Depth0
	  | resolve_P_f (CL (_, _, (_, nil) :: _, ...)) =
	    raise ResolvePF_EmptyAtomList
	  | resolve_P_f (c as CL (h as HFUN (P, f, k, vars, t), {}, nil, Bs, pi, bsubsr)) =
	    (* automaton clause *)
	    (
	     (* first try to resolve with some C \/ -P(f(...)) with -P(f(...)) selected: *)
	     if P inset !selfq
		 then let val fmap = !(?(!selfq) P)
		      in
			  if f inset fmap
			      then let val clauses = !(?fmap f)
				   in
				       iterate
					 (case c' of
					      CL (h', {},
						  (maxd', (_, DAPP (_, l'))::atl')::rest',
						  Bs', pi', ref subsumed) =>
					      if subsumed
						  then ()
					      else
						  let val ir = ref 0
						      val blkl = [(inc ir;
								   if !ir inset Bs
								       then ?Bs (!ir)
								   else {})
								 |while !ir<k]
						      val (al'', Bs'') =
							  gen (l', blkl,
							       if null atl'
								   then rest'
							       else (maxd', atl') :: rest',
								   Bs')
						  in
						      wait_insert (CL (h', {}, al'', Bs'',
								       p_resolve (AC_POP_RAW c, c',
										  MGU_AUTO_Xs_ARE l'),
								       ref false))
						  end
					    | _ => raise ResolvePF_BadSelF
					      )
				       | c' in set clauses
				       end
				   end
			  else ()
		      end
	     else ();
		 (* then try to resolve with some C \/ -P(x) with -P(x) selected: *)
		 if P inset !selxq
		     then let val clauses = !(?(!selxq) P)
			  in
			      iterate
				(case c' of
				     CL (h', {}, nil, {1 => B}, pi', ref subsumed) =>
				     (* with h' of the form P'(x1), q, or bot, and B
				      containing P. *)
				     if subsumed
					 then ()
				     else
					 let val h'' = case h' of
							   HVAR P' => HFUN (P', f, k, vars, t)
							 | _ => h'
					     val B' = B \ {P}
					 in
					     wait_insert (CL (h'', {},
							      if empty B'
								  then nil
							      else
								  [(1, [(Q, t)
								       | Q in set B'])],
								    Bs,
								    p_resolve (AC_POP_RAW c, c',
									       MGU_OTHER_X1_IS t),
								    ref false))
					 end
				   | _ => raise ResolvePF_BadSelX
				     )
			      | c' in set clauses
			      end
			  end
		 else ();
		     (* Finally, update autoinfoq: *)
		     let val ir = ref 0
			 val blkl = [(inc ir;
				      if !ir inset Bs
					  then ?Bs (!ir)
				      else {})
				    |while !ir<k]
		     in
			 do_verbose (2, fn () =>
				     (#put stderr "  Recording automaton clause: ";
				      perrclause c;
				      #put stderr "\n";
				      #flush stderr ())
				     );
			 clause_compile_subsumed (P, f, blkl, pi)
		     end
		 )
	  | resolve_P_f (c as CL (h as HVAR P, {}, nil, Bs, pi, bsubsr)) =
	    (* epsilon-clause *)
	    (case Bs of
		 {_ => {P1} U rest} => (* clause is P(x1) <= P1(x1), ..., with P1(x1) selected.
					Resolve with all automata clauses P1(f(...)) <= ... *)
		   (if P1 inset !univq (* first resolve with the universal clause P1(x1)
					if any. *)
			then wait_insert (CL (h, {}, nil,
					      if empty rest
						  then {}
					      else {1 => rest},
						  p_resolve (AC_UNIV (P1, ?(!univq) P1), c,
							     MGU_OTHER_X1_IS (DV 1)),
						  bsubsr))
		    (* Do not update forward subsumption structures or selxq
		     since resolvent subsumes premise. *)
		    else (if P1 inset !autoinfoq
			      then
				  iterate
				    iterate
				      wait_insert (CL (h', {},
						       if empty rest
							   then nil
						       else
							   [(1, [(Q, t)
								| Q in set rest])],
							     Bs',
							     p_resolve (AC_POP (P1, f, blkl,
										k, vars, t, pi'),
									c, MGU_OTHER_X1_IS t),
							     ref false))
				    | blkl => pi' in map blkls
				    val jr = ref 0
				    val Bs' = {!jr => blk
					      | blk in list blkl
						  such that (inc jr; not (empty blk))}
				    end
				  | f => blocks in map !(?(!autoinfoq) P1)
				  val (k, vars, t) = ?(!fargsq) f
				  val blkls = blkls_from_blocks (blocks, nil, 0, k)
				  val h' = HFUN (P, f, k, vars, t)
				  end
			  else (); (* no resolvent *)
			      (* Now update selxq *)
			      if P1 inset !selxq
				  then let val csr = ?(!selxq) P1
				       in
					   csr := !csr U {c}
				       end
			      else selxq := !selxq ++ {P1 => ref {c}}
				  ))
	       | _ => (* clause is just P(x): resolve with all clauses C \/ -P(...) *)
		 ( (* first try to resolve with some C \/ -P (f (...)) with -P(f(...)) selected *)
		  if P inset !selfq
		      then
			  iterate
			    iterate
			      (case c' of
				   CL (h', {},
				       (maxd', (_, t' as DAPP (_, l'))::atl')::rest',
				       Bs', pi', bsubsr') =>
				   if !bsubsr'
				       then () (* already subsumed *)
				   else (b_set c';
					 wait_insert (CL (h', {},
							  if null atl'
							      then rest'
							  else (maxd', atl')::rest', Bs',
							      p_resolve (AC_UNIV (P, pi), c',
									 MGU_AUTO_X1_IS t'),
							      ref false)))
				 | _ => raise ResolvePF_BadSelF)
			    | c' in set clauses
			    end
			  | f => ref clauses in map !(?(!selfq) P)
			  end
		  else ();
		      (* then resolve with clauses C \/ -P(x) with -P(x) selected *)
		      if P inset !selxq
			  then 
			      iterate
				(case c' of
				     CL (h', {}, nil, {x => B}, pi', bsubsr') =>
				     if !bsubsr'
					 then ()
				     else (b_set c';
					   wait_insert (CL (h', {}, nil,
							    case B \ {P} of
								{} => {}
							      | B' => {x => B'},
								p_resolve (AC_UNIV (P, pi), c',
									   MGU_AUTO_X1_IS (DV x)),
								ref false)))
				   | _ => raise ResolvePF_BadSelX)
			      | c' in set !(?(!selxq) P)
			      end
		      else ();
			  do_verbose (2, fn () =>
				      (#put stderr "  Recording universal clause: ";
				       perrclause c;
				       #put stderr "\n";
				       #flush stderr ()));
			  (* and backward subsume: *)
			  elim_subsumed_by_univ P;
			  (* Now update univq *)
			  univq := !univq ++ {P => pi}
			  ))
	  | resolve_P_f (c as CL (h as HBOT botname, {}, nil, {}, pi, bsubsr)) =
	    (do_bot c;
	     do_verbose (2, fn () =>
			 (#put stderr "  Recording bottom clause: ";
			  perrclause c;
			  #put stderr "\n";
			  #flush stderr ()));
	     (* Now update botq: *)
	     botq := !botq U {botname})
	  | resolve_P_f (c as CL (h as HQ q, {}, nil, Bs, pi, bsubsr)) =
	    (case Bs of
		 {} =>
		   (if q inset !selqq
			then
			    iterate
			      (case c' of
				   CL (h', ql', al', Bs', pi', bsubsr') =>
				   if !bsubsr'
				       then ()
				   else (b_set c';
					 wait_insert (CL (h', ql' \ {q}, al', Bs',
							  p_resolve (AC_Q (q, pi), c', MGU_EMPTY),
							  ref false)))
				 | _ => raise ResolvePF_BadSelQ)
			    | c' in set !(?(!selqq) q)
			    end
		    else ();
			do_verbose (2, fn () =>
				    (#put stderr "  Recording non-emptiness clause: ";
				     perrclause c;
				     #put stderr "\n";
				     #flush stderr ()));
			(* Now update qq: *)
			qq := !qq ++ {q => pi}
			)
	       | {1 => {P1} U rest} => (* clause is q <= P1(x1), ..., with P1(x1) selected.
					Resolve with all automata clauses P1(f(...)) <= ... *)
		 (if P1 inset !univq (* first resolve with the universal clause P1(x1) if any. *)
		      then wait_insert (CL (h, {}, nil,
					    if empty rest
						then {}
					    else {1 => rest},
						p_resolve (AC_UNIV (P1, ?(!univq) P1), c,
							   MGU_OTHER_X1_IS (DV 1)),
						bsubsr))
			  (* Do not update forward subsumption structures or selxq
			   because resolvent is subsumed by premise. *)
		  else (if P1 inset !autoinfoq
			    then
				iterate
				  iterate
				    wait_insert (CL (h, {},
						     if empty rest
							 then nil
						     else
							 [(1, [(Q, t)
							      | Q in set rest])],
							   Bs',
							   p_resolve (AC_POP (P1, f, blkl,
									      k, vars, t, pi'),
								      c, MGU_OTHER_X1_IS t),
							   ref false))
				  | blkl => pi' in map blkls
				  val jr = ref 0
				  val Bs' = {!jr => blk
					    | blk in list blkl
						such that (inc jr; not (empty blk))}
				  end
				| f => blocks in map !(?(!autoinfoq) P1)
				val (k, vars, t) = ?(!fargsq) f
				val blkls = blkls_from_blocks (blocks, nil, 0, k)
				end
			else (); (* no resolvent *)
			    (* Update forward subsumption structures: *)
			    (* Now update selxq *)
			    if P1 inset !selxq
				then let val csr = ?(!selxq) P1
				     in
					 csr := !csr U {c}
				     end
			    else selxq := !selxq ++ {P1 => ref {c}}
				))
	       | _ => raise ResolvePF_BlackHole
		 )
	  | resolve_P_f _ = raise ResolvePF_BlackHole; (* remaining cases for resolve_P_f:
							should never happen. *)

	    (*
	val current_low = ref max_int
	     *)

	fun resolve () =
	    (let val (info, cc) = scc (!predicate_graph, P_ROOT)
		 val (condensed, root) = scc_condense (!predicate_graph, P_ROOT, info, cc)
		 val numbering = revtopsort (condensed, root)
		 val inv_cc = overwrite [{P => low
					 | P in set preds}
					| low => preds in map cc]
		 val dfsinfo = {P => (dfsnum, ?numbering (?inv_cc P))
			       | P => (dfsnum, _) in map info}
		 val sccinfo = cc O inv numbering
	     in
		 dfs_info := dfsinfo;
		 scc_info := sccinfo;
		 dfs_q_info := {};
			   (*
		 current_low := 0;
			    *)
		 univq := {}; qq := {}; selfq := {}; selxq := {}; selqq := {}; botq := {};
		 autoinfoq := {};
		 let val (sk, bots) = compute_skeleta (!clause_list, maxskdepth, maxpathlen)
		 in
		     skeleta := sk;
		     skel_bots := bots
		 end;
		 do_verbose (3, fn () => (#put stderr "Found ";
					  print stderr (pack (card sccinfo));
					  #put stderr " strongly connected components:\n";
					  iterate
					    (#put stderr "  [";
					     print stderr (pack low);
					     #put stderr "] ";
					     let val cc = sort (fn (pred, pred') =>
								   #1 (?dfsinfo pred) > #1 (?dfsinfo pred'))
						     (?sccinfo low)
						 val delimr = ref ""
					     in
						 iterate
						   (#put stderr (!delimr);
						    delimr := ", ";
						    case pred of
							P_ROOT => #put stderr "*"
						      | P_BOT botname => #put stderr botname
						      | P_PRED P => #put stderr P)
						 | pred in list cc
						 end;
						 #put stderr "\n"
					     end)
					  | low in list sort (op <) sccinfo
					  end;
					  #put stderr "Funheads:\n";
					  iterate
					    (#put stderr P;
					     #put stderr ":";
					     (case fso of
						  SOME fs =>
						  (iterate
						     (#put stderr " ";
						      #put stderr f)
						   | f in set fs
						   end;
						     #put stderr "\n")
						| _ => #put stderr " *all*.\n"))
					  | P in list pred_sort (!funheads)
					    val fso = ?(!funheads) P
					  end;
					  #put stderr "Skeleta:\n";
					  iterate
					    (#put stderr P;
					     #put stderr ": ";
					     pretty stderr (pack (?(!skeleta) P)))
					  | P in list pred_sort (!skeleta)
					    val ps = ?(!skeleta) P
					  end;
					  #flush stderr ()))
	     end;
	     let val fsig = clause_list_sig (!clause_list)
		 val ir = ref 0
	     in
		 fargsq := {f => (ir := 0;
				  (k, 1 to k, DAPP (f, [(1, DV (inc ir; !ir))
						       |while !ir<k])))
			   | f => k in map fsig}
	     end;
	     iterate
		 if useful c
		     then if ignore_intermediate_selfq
			      then case c of
				       CL (_, {}, (_, (P, DAPP (f, _)) :: _) :: _, ...) =>
				       insert_selfq (c, P, f)
				     | _ => wait_insert c
			  else wait_insert c
		 else (do_verbose (1, fn () =>
				   (#put stderr "Ignoring useless clause ";
				    perrclause c;
				    #put stderr "\n";
				    #flush stderr ()));
		       case c of (* c is useless, but we need to produce a model of it:
				  just take the head to be always true. *)
			   CL (HVAR P, ...) => univq := !univq ++ {P => P_GIVEN "*magic*"}
			 | CL (HFUN (P, ...), ...) => univq := !univq ++ {P => P_GIVEN "*magic*"}
			 | _ => raise ResolveUseful)
	     | c in list !clause_list
	     end;
	     clause_list := nil;
	     (*dealtwith := {};*)
	     while true do
		 let val (((_, low), _), c as CL (_, _, _, _, _, ref bsubs)) = wait_pop ()
		 in
			       (*
		     if low > !current_low
			 then (do_verbose (1, fn () =>
					   (if !current_low<>0
						then (#put stderr "  Clean sweep: removing all non-automata\
						 \ clauses on ";
						      print stderr (pack (?(!scc_info) (!current_low)));
						      #put stderr "\n";
						      #flush stderr ())
					    else ()));
			       selfq := {}; selxq := {}; selqq := {};
			       (* in fact, do not erase qq. *)
			       current_low := low)
		     else ();
			 *)
		     if bsubs (*orelse c inset !dealtwith*) orelse ct_subsumed c
			 then do_verbose (2, fn () => (#put stderr "  Removing forward subsumed clause: [";
						       print stderr (pack low);
						       #put stderr "] ";
						       perrclause c;
						       #put stderr "\n";
						       #flush stderr ()))
		     else (do_verbose (2, fn () => (#put stderr "  Picking clause: [";
						    print stderr (pack low);
						    #put stderr "] ";
						    perrclause c;
						    #put stderr "\n";
						    #flush stderr ()));
			   ( (*dealtwith := !dealtwith U {c};*)
			    resolve_P_f c))
		 end)
		 handle MHeapEmptyEvt => ();
    in
	|[ new_clause = new_clause,
	   resolve = resolve,
	   get_automaton = (fn () => ({P => {f => (blkls_from_blocks (blocks, nil, 0, k), k, vars)
					    | f => blocks in map fmap
					      val (k, vars, ...) = ?(!fargsq) f}
				      | P => ref fmap in map !autoinfoq},
					!univq)),
	   get_clauses = (fn () => !clause_list @ get_clauses ()),
	   get_true_botnames = (fn () => !botq)
	   ]|
    end;

fun clause_from_automaton_clause (AC_UNIV (P, pi)) =
    CL (HVAR P, {}, nil, {}, pi, ref false)
  | clause_from_automaton_clause (AC_Q (q, pi)) =
    CL (HQ q, {}, nil, {}, pi, ref false)
  | clause_from_automaton_clause (AC_POP (P, f, bl, k, vars, t, pi)) =
    let val ir = ref 0
	val Bs = {(inc ir; !ir) => B
		 | B in list bl}
    in
	CL (HFUN (P, f, k, vars, t), {}, nil, Bs, pi, ref false)
    end
  | clause_from_automaton_clause (AC_POP_RAW c) = c;

fun clean_automaton (Pmap, umap) =
    AUTO ({P => {f => ({blkl | blkl in set blkls}, k, vars)
		| f => (blkls, k, vars) in map fmap}
	  | P => fmap in map Pmap},
	    dom umap);
