/* lp_stable.c
 * Neil Moore <neil@cs.uky.edu>
 *  5 Apr 2000
 *
 * Part of liblp.  See COPYRIGHT for copyright information.
 */

#define STRAT_ORDER
#define HEURISTIC_THIS_STRATUM
#undef HEURISTIC_LOWER_STRATA
#define HEURISTIC_BY_NANT
#define HEURISTIC_BOTH
#undef TRY_NANT_ONLY

/* TODO: See which #defines work best overall
 *
 * TODO: make it faster.  Even with similar choice point statistics, we
 *   are often much slower than smodels.
 */

#include <string.h>
#include <stddef.h>
#include <lp.h>
#include <lp_util.h>
#include <lp_fks.h>
#include <lp_lm.h>
#include <lp_wfs.h>
#include <lp_stable.h>
#include <lp_queue.h>

#define AUX_EXP_NEG 0
#define AUX_EXP_POS 1
#define AUX_NEG     2
#define AUX_POS     3

static int sm(void);
static void sm_setup(void);
static int Cl(int prepos, int preneg);
static int diminishing_closure(void);
static void expand(int chosen);
static int backtrack(void);
static void backtrack_single(int apos, int reason);
static void compute_nant(void);
static int full_model(void);
static int pick_next_atom(void);
static void maybe_fire(int rule);
static void suppress(int rule, int bypos);
static void backchain_true(int rule);
static void backchain_false(int rule);
static void set_false(int atom, int chosen);
static void set_true(int atom, int chosen);
static void atom_strata(int *rstr, int nstr);
static void trivial_stratification(void);
static int trypos(struct lp_atom *atom, int *SeenPos, int *SeenNeg);
static int tryneg(struct lp_atom *atom, int *SeenPos, int *SeenNeg);

static int ChoicePoints=0;
static int StableModels=0;
static int Conflicts=0;
static int ForcedAtoms=0;
static int TriedAtoms=0;

static struct lp *Prog;    /* The program, of course */

static struct lp_rlist **HeadOf;     /* Of which rules is this atom the head? */
static struct lp_rlist **PosDepends; /* In the bodies of which rules does this atom appear positively? */
static struct lp_rlist **NegDepends; /* In the bodies of which rules does this atom appear negatively? */

static int *PosBodySize; /* How many atoms not known true are in the positive body? */
static int *PBSize2;     /* How many atoms not known true are in the positive body? */
static int *NegBodySize; /* How many atoms not known false are in the negative body? */
static int *Suppressed;  /* How many literals are keeping this rule from firing? */
static int *HeadNumber;  /* How many nonsuppressed rules is this atom the head of? */
static int *NAnt;        /* Which atoms are negative antecedents?  Array of bools. */

/* Callback() (passed to lp_stable()) is called for every stable model.  It returns 1 if no more stable
 * models are needed, 0 if we should compute more.
 */
static int (*Callback)(struct lp *,int *);

static int CurrStratum;            /* Which stratum are we in? */
static int *StratumOf;             /* The stratum containing each atom */
static int *OrdAtoms;              /* Atoms ordered by stratum */
static struct lp_alist **Stratum;  /* Which atoms are in each stratum? */
static int NumStrata;              /* How many strata? */

static int CurrLoc;

static struct lp_aqueue *PosQueue; /* Atoms that will be put into the model */
static struct lp_aqueue *NegQueue; /* Atoms that will be removed from the model */

/* The queue for the diminishing closure is implemented as an array because it
 * is used more than the other two, and our performance depends more closely
 * on it
 */
static int *DQueue;    /* Atoms that may no longer be in the upper closure */
static int DQHead;     /* Head of queue; things pulled off here */
static int DQTail;     /* Tail of queue; things tacked on here */
static int DQSize;     /* Maximum size of queue */


static int dq_dequeue(void);
static int dq_empty(void);

#define dq_enqueue(atom) { DQueue[DQTail++]=(atom); \
			   if(DQTail>=DQSize) \
				   DQTail=0; \
			   if(DQTail==DQHead) { \
				   fprintf(stderr,"Queue overflowed (%d/%d)---exiting.\n",DQTail,DQSize); \
				   exit(1); \
			   }}

/*
static void dq_enqueue(int atom);
void dq_enqueue(int atom) 
{
	DQueue[DQTail++] = atom;
	if(DQTail >= DQSize)
		DQTail = 0;
	if(DQTail == DQHead) {
		fprintf(stderr, "Queue overflowed (%d/%d)---exiting.\n", DQTail, DQSize);
		exit(1);
	}
	
}
*/

int dq_dequeue(void) 
{
	int result = DQueue[DQHead++];
	
	if(DQHead >= DQSize)
		DQHead = 0;

	if(DQTail == DQHead-1) {
		fprintf(stderr, "Queue underflowed (%d/%d)---exiting.\n", DQTail, DQSize);
		exit(1);
	}
	
	return result;
}

int dq_empty(void) 
{
	return (DQHead == DQTail);
}
/* XXX */

/* Monitor() (passed to lp_stable_options()) is called every time an atom is
 * guessed, forced, expanded, or backtracked.
 *
 */
static void (*Monitor)(struct lp *,int, int,int) = 0;


/* XXX */

static int *Stack;     /* Stack of integers, each representing an atom */
static int *AuxStack;  /* Stack of integers, each being one of the AUX_* constants */
static int Sp;         /* Stack pointer for Stack and AuxStack:  Stack[Sp] is the first unused position */

static int *Model;     /* The current model; Model[a] is LP_L_{TRUE,FALSE,UNKNOWN} depending of atom a's value */
static int *Closure;   /* Provides an upper bound on the model.  An array of bools */
static int *Source;    /* If atom a is in the closure, Source[a] is the number of the rule which keeps a in the
			* closure */

static int UseSource=0;

#define valid_source(a,r) (!UseSource || (Source[(a)] == (r)) || Source[(a)] == -1)

static int Conflict;   /* Has a conflict been detected? */

static int Sloppy=0;   /* Use sloppy heuristics */

void lp_stable_options(int sloppy, int source, void (*monitor)(struct lp *, int, int, int)) 
{
	Sloppy = sloppy;
	UseSource = source;
	Monitor = monitor;
}

void lp_stable_get_options(int *sloppy, int *source, void (**monitor)(struct lp *, int, int, int)) 
{
	*sloppy = Sloppy;
	*source = UseSource;
	*monitor = Monitor;
}

/* callback() should return true once it is finished.  Returns 1 if callback() ever returned true (if
 * there may be more stable models), 0 if all the stable models were found before callback() returned
 * true.
 */
int lp_stable(struct lp *prog, int (*callback)(struct lp *,int *), int *strata, int nstr) 
{
	int result,i;

	/* Allocate and initialise data structures */
	Prog = prog;

	Model   = lp_calloc(Prog->natoms,sizeof(int));
	Closure = lp_calloc(Prog->natoms,sizeof(int));
	Source = lp_malloc(Prog->natoms*sizeof(int));
	for(i=0;i<Prog->natoms;i++)
		Source[i] = -1;

	/* XXX: make dq_alloc() */
	DQueue   = lp_malloc(Prog->size*2*sizeof(int));
	DQHead   = 0;
	DQTail   = 0;
	DQSize   = Prog->size+1;
	
	Stack    = lp_malloc(Prog->natoms*sizeof(int));
	AuxStack = lp_malloc(Prog->natoms*sizeof(int));
	Sp = 0;

	PosQueue = lp_aq_new();
	NegQueue = lp_aq_new();
	
	compute_nant();    /* Find and mark all negative antecedents */
	
	ChoicePoints = 0;
	StableModels = 0;
	Conflicts = 0;
	ForcedAtoms = 0;
	TriedAtoms = 0;
	
	Conflict = 0;
	Callback = callback;
	
	/* The following functions allocate memory with lp_{c,m}alloc */
	HeadOf = lp_headof(Prog);
	HeadNumber = lp_count_head(Prog);
	PosDepends = lp_posdepends(Prog);
	NegDepends = lp_negdepends(Prog);
	PosBodySize = lp_possizes(Prog);
	PBSize2 = lp_possizes(Prog);
	NegBodySize = lp_negsizes(Prog);
	
	Suppressed = lp_calloc(Prog->nrules,sizeof(int));

	if(strata == NULL || nstr == 0) {
		trivial_stratification();
		NumStrata = 1;
	} else {
		atom_strata(strata, nstr);
		NumStrata = nstr;
	}
	CurrLoc = 0;
	CurrStratum = 0;

	/* End initialisation */

	/* Compute stable models */
	result = sm();

	/* Free data structures */
	lp_aq_free(PosQueue);
	lp_aq_free(NegQueue);
	
	for(i=0; i<Prog->natoms; i++) {
		lp_rl_free(PosDepends[i]);
		lp_rl_free(NegDepends[i]);
		lp_rl_free(HeadOf[i]);
	}
	free(PosDepends);
	free(NegDepends);
	free(HeadOf);
	
	for(i=0; i<NumStrata; i++) {
		lp_al_free(Stratum[i]);
	}
	/* XXX: make dq_free() */
	free(DQueue);
	free(Stratum);
	free(OrdAtoms);	
	free(StratumOf);
	free(HeadNumber);
	free(Suppressed);
	free(PosBodySize);
	free(PBSize2);
	free(NegBodySize);
	free(NAnt);
	free(Model);
	free(Closure);
	free(Source);
	free(Stack);
	free(AuxStack);
	
	return result;
}

/* Accessor for statistics related to the computation.  Usually called by
 * callback(), or after lp_stable() returns.  This function should probably
 * not be called at other times, as the library is not thread-safe
 */
void lp_stable_stats(int *choices, int *models, int *conflicts, int *forced, int *tried)
{
	*choices = ChoicePoints;
	*models = StableModels;
	*conflicts = Conflicts;
	*forced = ForcedAtoms;
	*tried = TriedAtoms;
}


/* Perform the computation.  Assumes all appropriate variables and data
 * structures have been initialised by lp_stable().
 */
int sm(void) 
{
	int more;  /* Might there be models we did not compute? */
	
	sm_setup();  /* Find everything that must be true/false */
	
	while(1) {
		if(Conflict) {
			++Conflicts;
			Conflict = 0;
			
			if(Monitor)
				Monitor(Prog,-1,-1,LP_MON_CONFLICT);
			
			if(backtrack()) {
				more = 0;
				break;
			}
			continue;
		} else if(full_model()) {
			++StableModels;

			if(Monitor)
				Monitor(Prog,-1,-1,LP_MON_MODEL);
			
			
			if(Callback(Prog,Model)) {
				more = 1;
				break;
			}
			/* Nothing is unknown, so backtrack */
			if(backtrack()) {
				more = 0;
				break;
			}
			continue;
		}

		/* We cannot expand anything else, so lookahead */
		if(pick_next_atom())
			++ChoicePoints; /* we had to guess */
		else
			++ForcedAtoms;  /* something caused a conflict, so we added its negation */
	}

	return more;
}


/* Perform the initial expansion.  Basically, this function sets Model to the WFS of Prog, and
 * sets Closure to Dcl(P_{WFS(P)},not(NAnt(P))).
 */
void sm_setup(void) 
{
	int i, apos;

	/* begin NFM 3 Dec 1999 */
	/* First, handle negative requirements */
	for(i=0; i<Prog->nnegreq; i++) {
		set_false(lp_apos(Prog,Prog->negreq[i]),0);
	}
	/* Then positive ones */
	for(i=0; i<Prog->nposreq; i++) {
		set_true(lp_apos(Prog,Prog->posreq[i]),0);
	}
	/* end NFM 3 Dec 1999 */
	
	/* Then, enqueue the heads of those rules which must fire */
	for(i=0; i<Prog->nrules; i++) {
		if(Prog->rules[i].npos == 0) {
			apos = lp_apos(Prog,Prog->rules[i].head);
			
			dq_enqueue(apos);
			Source[apos] = i;
			
			if(Prog->rules[i].nneg == 0) {
				lp_aq_enqueue(PosQueue,Prog->rules[i].head);
				if(Monitor)
					Monitor(Prog,lp_apos(Prog,Prog->rules[i].head),i,LP_MON_EBODY);
			}
			
		}
	}

	/* Now, find things that should be in the diminishing closure */
	while(!dq_empty()) {
		apos = dq_dequeue();

		/* If we haven't processed this atom yet */
		if(Closure[apos] == 0) {
			struct lp_rlist *j;

			for(j=PosDepends[apos]; j!=NULL; j=j->next) {
				int rpos = lp_rpos(Prog,j->rule);
				
				if(--PBSize2[rpos] == 0 && !Suppressed[rpos]) {
					int hpos = lp_apos(Prog,j->rule->head);
					dq_enqueue(hpos);
					Source[hpos] = rpos;
				}
			}
			Closure[apos] = 1;
		}
	}

	/* If something is not in the diminishing closure, it cannot be in
	 * the model, either.
	 */
	for(i=0; i<Prog->natoms; i++) {
		if(Closure[i])
			continue;
		if(Model[i] == LP_L_TRUE) {
			Conflict = 1;
			Conflicts++;
			break;
		}
		set_false(i,0);

		
		if(Monitor)
			/* XXX: why is it not in the dim closure?  Want
			 *  source pointer, maybe.
			 */
			Monitor(Prog,i,-1,LP_MON_POSLOOPONLY);
	}

	/* Deal with everything that has been added to the positive queue. */
	if(!Conflict)
		expand(0);
}


/* Backtrack from a single expansion/choice.  This function reverts all changes
 * made when apos was pushed onto the stack.  reason should be the value of
 * AuxStack that goes along with apos.  This function should only be called on
 * the top atom of the stack (or an atom that was just popped off the stack).
 */
void backtrack_single(int apos, int reason) 
{
	struct lp_rlist *j;
	int rpos, hpos;

	/* If we have gone to a lower stratum, indicate so */
	if(StratumOf[apos] < CurrStratum) {
		/*
		CurrStratum = StratumOf[apos];
		CurrLoc = -1;
		*/
	}

	if(Monitor)
		Monitor(Prog,apos,-1,LP_MON_BACKTRACK);
	
	switch(reason) {
	    case AUX_POS:
	    case AUX_EXP_POS:
		/* Inactivate rules apos caused to fire */
		for(j=PosDepends[apos]; j!=NULL; j=j->next) {
			rpos = lp_rpos(Prog,j->rule);
			++PosBodySize[rpos];
		}
		/* Re-enable rules apos suppressed */
		for(j=NegDepends[apos]; j!=NULL; j=j->next) {
			rpos = lp_rpos(Prog,j->rule);
			hpos = lp_apos(Prog,j->rule->head);

			if(--Suppressed[rpos] == 0)
				HeadNumber[hpos]++;
		}
		break;
			
	    case AUX_NEG:
	    case AUX_EXP_NEG:
		/* Inactivate rules we caused to fire */
		for(j=NegDepends[apos]; j!=NULL; j=j->next) {
			rpos = lp_rpos(Prog,j->rule);
			++NegBodySize[rpos];
		}
		/* Re-enable rules apos suppressed */
		for(j=PosDepends[apos]; j!=NULL; j=j->next) {
			rpos = lp_rpos(Prog,j->rule);
			hpos = lp_apos(Prog,j->rule->head);

			--PBSize2[rpos];
			
			if(--Suppressed[rpos] == 0)
				HeadNumber[hpos]++;
		}
		break;
			
	    default:
		fprintf(stderr, "BAD VALUE ON AuxStack: %d (APOS = %d = %s)\n",
			reason, apos, Prog->atoms[apos].name);
		exit(1);
	}

	/* Put this atom back in the diminishing closure; the next expand()
	 * will take it out if need be.
	 */
	Closure[apos] = 1;
	Source[apos] = -1;
	
	Model[apos] = LP_L_UNKNOWN;
}


/* Pop literals off the stack until we reach a chosen (rather than forced
 * or expanded) atom; then pop off that literal and expand on its negation.
 *
 * This function returns 1 if the search space has been exhausted, 0
 * otherwise.
 */
int backtrack(void) 
{
	int apos;
	int reason;
	int replace;
	
	while(Sp) {
		--Sp;
		apos = Stack[Sp];
		reason = AuxStack[Sp];
		replace = 0;
		Closure[apos] = 0;

		backtrack_single(apos, reason);

		/* If the atom was chosen, enqueue its negation and expand */
		switch(reason) {
		    case AUX_EXP_POS:
		    case AUX_EXP_NEG:
			break;
		    case AUX_POS:
			if(Monitor)
				Monitor(Prog,apos,-1,LP_MON_POSFAILED);
			lp_aq_enqueue(NegQueue,&Prog->atoms[apos]);
			expand(0);
			return 0;
		    case AUX_NEG:
			if(Monitor)
				Monitor(Prog,apos,-1,LP_MON_NEGFAILED);
			lp_aq_enqueue(PosQueue,&Prog->atoms[apos]);
			expand(0);
			return 0;
		    default:
			fprintf(stderr, "BAD VALUE IN AUXSTACK[%d]: %d (STACK[%d] = %d = %s)\n",
				Sp, reason, Sp, apos, Prog->atoms[apos].name);
			exit(1);
		}
	}
	return 1;
}


/* Assuming Conflict is false, returns 1 if Model is a stable model, otherwise
 * 0.  Should not be called if Conflict is true.
 */
int full_model(void) 
{
	return Sp >= Prog->natoms;
}


/* Compute the set of negative antecedents of Prog.  Fills NAnt[]. */
void compute_nant(void) 
{
	int i,j, apos;
	struct lp_rule *r;
	NAnt = lp_calloc(Prog->natoms,sizeof(int));
	
	for(i=0; i<Prog->nrules; i++) {
		r = &Prog->rules[i];
		for(j=0;j<r->nneg;j++) {
			apos = lp_apos(Prog,r->neg[j]);
			NAnt[apos] = 1;
		}
	}
}


/* Expand on the set of literals in PosQueue, NegQueue, and DQueue.
 * DQueue is usually empty when expand() is called.  If chosen is true,
 * backtrack() will try the negations of these literals . Otherwise, they
 * are assumed to be forced, and backtrack() will simply backtrack past
 * them.
 */
void expand(int chosen) 
{
	int possize,negsize;
	if(chosen) {
		possize=lp_aq_size(PosQueue);
		negsize=lp_aq_size(NegQueue);
	} else {
		possize = 0;
		negsize = 0;
	}
	

	if(Cl(possize,negsize)) {
		/* We have a conflict: empty the queues */
		while(!lp_aq_empty(PosQueue))
			(void)lp_aq_dequeue(PosQueue);
		while(!lp_aq_empty(NegQueue))
			(void)lp_aq_dequeue(NegQueue);
		while(!dq_empty())
			(void)dq_dequeue();
		
	}		
}


/* Perform the actual expansion.  prepos is the number of chosen (as opposed
 * to expanded or forced) positive literals to push; preneg is the number of
 * chosen negative literals to push.  Returns 1 if there was a conflict,
 * zero otherwise.
 */
int Cl(int prepos, int preneg)
{
	struct lp_atom *a;
	int apos;
	
	for(;;) {
		if(Conflict)
			return 1;
		
		if(!lp_aq_empty(PosQueue)) {
			/* Make something positive */
			a = lp_aq_dequeue(PosQueue);
			apos = lp_apos(Prog,a);
			if(Model[apos] == LP_L_FALSE)
				Conflict = 1;
			else if(!Model[apos])
				set_true(apos, (prepos-- > 0));
		} else if(!lp_aq_empty(NegQueue)) {
			/* Make something negative */
			a = lp_aq_dequeue(NegQueue);
			apos = lp_apos(Prog,a);
			if(Model[apos] == LP_L_TRUE)
				Conflict = 1;
			else if(!Model[apos])
				set_false(apos, (preneg-- > 0));
		} else if(dq_empty()) {
			/* The queues are empty, we are finished */
			return 0;
		} else {
			/* Possibly remove things from the diminishing closure */
			if(diminishing_closure())
				Conflict = 1;
		}
	}
}


/* For each atom in DQueue, remove it from the diminishing closure
 * if necessary.  Returns true if there was a conflict, false otherwise.
 *
 * TODO:  Add source pointers a la smodels 2, to improve performance.
 */
int diminishing_closure(void) 
{
	/* TODO: this is the single greatest source of calls to
	 * lp_apos() and lp_rpos().  Source pointers may help, but
	 * what else?
	 */
	/* struct lp_atom *a; */
	struct lp_rlist *j;
	int apos, rpos, hpos, i;
	
	/* Queue of atoms temporarily assumed to be out of the closure */
	int *tmpf = lp_calloc(Prog->natoms, sizeof(int));
	/* Tail of tmpf: tmpf[tp] is one past the last atom enqueued */
	int tp = 0;

	/* First, assume everything in DQueue is out of the closure */
	while(!dq_empty()) {
		apos = dq_dequeue();

		/* If it was previously in the closure, and it was not in the
		 * model negatively, take it out of the closure and add it to
		 * tmpf.
		 */
		if(Closure[apos] == 1 && Model[apos] != LP_L_FALSE) {
			for(j=PosDepends[apos]; j!=NULL; j=j->next) {
				rpos = lp_rpos(Prog, j->rule);
				hpos = lp_apos(Prog, j->rule->head);
				if(++PBSize2[rpos] == 1 && !Suppressed[rpos] && Closure[hpos] && valid_source(hpos,rpos))
					dq_enqueue(lp_apos(Prog,j->rule->head));
			}
			Closure[apos] = 0;
			tmpf[tp++] = apos; /* enqueue */
		}
	}
	
	/* Enqueue everything which was taken out but should still be in. */
	for(i=0; i<tp; i++) {
		for(j=HeadOf[tmpf[i]]; j!=NULL; j=j->next) {
			rpos = lp_rpos(Prog, j->rule);
			if(PBSize2[rpos] == 0 && !Suppressed[rpos]) {
				hpos = lp_apos(Prog,j->rule->head);
				
				Source[hpos] = rpos;
				dq_enqueue(hpos);
				break;
			}
		}
	}

	/* Propagate truth: add everything in the queue, and everything that
	 * follows from those things, back to the closure.
	 */
	while(!dq_empty()) {
		apos = dq_dequeue();
		
		if(Closure[apos] == 0 && Model[apos] != LP_L_FALSE) {
			for(j=PosDepends[apos]; j!=NULL; j=j->next) {
				/* propagateTrue */
				rpos = lp_rpos(Prog, j->rule);
				hpos = lp_apos(Prog, j->rule->head);
				if(--PBSize2[rpos] == 0 && !Suppressed[rpos] && !Closure[hpos]) {
					hpos = lp_apos(Prog,j->rule->head);

					Source[hpos] = rpos;
					dq_enqueue(hpos);
				}
			}
			Closure[apos] = 1;
		}
	}

	/* If something is not in the closure, it must be false */
	for(i=0; i<tp; i++) {
		apos = tmpf[i];
		if(!Closure[apos]) {
			if(Model[apos] == LP_L_TRUE) {
				/* Revert changes made by diminishing_closure */
				for(; i < tp; i++) {
					apos = tmpf[i];
					if(!Closure[apos]) {
						for(j=PosDepends[apos]; j!=NULL; j=j->next)
							--PBSize2[lp_rpos(Prog,j->rule)];
						/* Source[apos] should be as it was */
						Closure[apos] = 1;
					}
				}
				
				Conflict = 1;
				
				free(tmpf);
				return 1;
			} else {
				/* XXX: why was it taken out? */
				if(Monitor)
					Monitor(Prog,apos,-1,LP_MON_POSLOOPONLY);
				set_false(apos, 0);
				
				/* Revert changes made by diminishing_closure */
				for(j=PosDepends[apos]; j!=NULL; j=j->next)
					--PBSize2[lp_rpos(Prog,j->rule)];
			}
		}
	}

	free(tmpf);
	return 0;
}


/* Possibly fire a rule.  Called whenever a literal in the rules body is added to the
 * model.  If all literals in the body are satisfied, enqueues the head to be added
 * to the model.  If the head is known false and only one body literal is not satisfied,
 * the opposite of that literal is enqueued.
 */
void maybe_fire(int rule) 
{
	int hpos;
	if(Suppressed[rule])
		return;
	hpos = lp_apos(Prog,Prog->rules[rule].head);
	if(PosBodySize[rule] == 0 && NegBodySize[rule] == 0 && Model[hpos] != LP_L_TRUE) {
		if(Monitor)
			Monitor(Prog,hpos,rule,LP_MON_BODYPROVEN);
		lp_aq_enqueue(PosQueue, Prog->rules[rule].head);
	} else if(NegBodySize[rule] + PosBodySize[rule] == 1 && Model[hpos] == LP_L_FALSE)
		backchain_false(rule);
}


/* Suppress a rule.  bypos is true if the rule is being suppressed because an atom
 * in the rule's positive body was added negatively to the model.  Decrements 
 * HeadNumber[] for the head of the rule, possibly adding the head's negation to
 * the model or backchaining from the head.
 */
void suppress(int rule, int bypos) 
{
	int apos,rpos;
	struct lp_rlist *j;

	if(bypos)
		++PBSize2[rule];
	
	if(++Suppressed[rule] == 1) {
		apos = lp_apos(Prog,Prog->rules[rule].head);
		if (--HeadNumber[apos] != 0 && Model[apos] != LP_L_FALSE)
			dq_enqueue(apos);
		
		if(Model[apos] != LP_L_FALSE && HeadNumber[apos] == 0) {
			if(Monitor)
				Monitor(Prog,apos,rule,LP_MON_NOTINHEAD);
			lp_aq_enqueue(NegQueue,&Prog->atoms[apos]);
		} else if(Model[apos] == LP_L_TRUE && HeadNumber[apos] == 1) {
			for(j=HeadOf[apos]; j!=NULL; j=j->next) {
				rpos = lp_rpos(Prog,j->rule);
				if(Suppressed[rpos] == 0) {
					backchain_true(rpos);
					break;
				}
			}
		}
	}
}


/* If we know a rule's head is true, and that atom is the head of this rule
 * only (as well as, possibly, suppressed rules), then all the literals in
 * the rule's body must be satisfied.  This function does that.  It should
 * only be called if it is known that the head of this rule is the head of
 * this rule and suppressed rules only, and that this rule is unsuppressed.
 */
void backchain_true(int rule)  
{
	int i;
	struct lp_rule *r = &Prog->rules[rule];
	
	if(Model[lp_apos(Prog,r->head)] == LP_L_TRUE) {
		/* Add each body literal to the appropriate queue */
		for(i=0; i<r->npos; i++) {
			if(Monitor)
				Monitor(Prog,lp_apos(Prog,r->pos[i]),rule,LP_MON_BACKCHAIN_POS);
			lp_aq_enqueue(PosQueue,r->pos[i]);
		} for(i=0; i<r->nneg; i++) {
			if(Monitor)
				Monitor(Prog,lp_apos(Prog,r->neg[i]),rule,LP_MON_BACKCHAIN_NEG);
			lp_aq_enqueue(NegQueue,r->neg[i]);
		}
	}
}


/* If we know a rule's head is false, and that there is only one unsatisfied
 * literal in the rule's body, then the negation of that literal must be in
 * the model.  This function adds that literal's negation.  It should only be
 * called if the head of this rule is known to be false.
 */
void backchain_false(int rule) 
{
	int apos;
	int i;
	struct lp_rule *r = &Prog->rules[rule];
	
	if(PosBodySize[rule] == 0 && NegBodySize[rule] == 1) {
		/* find the unsatisfied negative literal */
		for(i=0; i<r->nneg; i++) {
			apos = lp_apos(Prog,r->neg[i]);
			if(!Model[apos]) {
				if(Monitor)
					Monitor(Prog,lp_apos(Prog,r->neg[i]),rule,LP_MON_BACKCHAIN_POS_UNSAT);
				lp_aq_enqueue(PosQueue,r->neg[i]);
				break;
			}
		}
	} else if(PosBodySize[rule] == 1 && NegBodySize[rule] == 0) {
		/* find the unsatisfied positive literal */
		for(i=0; i<r->npos; i++) {
			apos = lp_apos(Prog,r->pos[i]);
			if(!Model[apos]) {
				if(Monitor)
					Monitor(Prog,lp_apos(Prog,r->pos[i]),rule,LP_MON_BACKCHAIN_NEG_UNSAT);
				lp_aq_enqueue(NegQueue,r->pos[i]);
				break;
			}
		}
	}
}


/* Assuming we have expanded fully, choose the next literal to try adding to
 * the model.  Only literals in the program's negative antecedent are tried.
 * If some literal causes a conflict, its negation is added and this function
 * returns false; this is known as `forcing'.  Otherwise, we choose an atom
 * which maximises the number of atoms added in the worst case, add that atom
 * (possibly negated) to the model, and return true; this is known as
 * `choosing': decisions made by choosing may be reversed in backtracking.
 *
 * As an optimisation, we note that, if adding the literal A and expanding also
 * adds B, C, and D, then we know that adding B, C, or D cannot result in a
 * larger expansion than that produced by expanding A.
 */
int pick_next_atom(void) 
{
	int apos;
	int j;

	/* True if expanding some other literal resulted in this one */
	int *SeenPos = lp_calloc(Prog->natoms, sizeof(int));
	int *SeenNeg = lp_calloc(Prog->natoms, sizeof(int));
	
	int foundone=0;
	int hi_min=-1, hi_max=-1;
	int hi_index = -1;
	int hi_pos = 0;
	int pos, neg, min, max;
	int try_as_pos, try_as_neg;
	int wasunseen;

	/* if Prog->natoms = 0, full_model() should have caught it in sm() */
	if(Prog->natoms == 0) {
		fprintf(stderr, "BUG: pick_next_atom() CALLED WITH PROG->NATOMS=0\n");
		exit(1);
	}

	if(CurrLoc == Prog->natoms) {
		CurrLoc = 0;
	}
	
	j = CurrLoc;
	CurrStratum = StratumOf[CurrLoc];
	
	while(1) {
#ifdef STRAT_ORDER
		apos = OrdAtoms[j];
#else
		apos = j;
#endif
		pos = 0;
		neg = 0;
		
		wasunseen=0;

		/* FIXME: we might be able to know for sure that this won't work
		 * even if only one has been seen */

		/* if it is a negative antecedent, unknown, and
		 *   * may cause conflict
		 *     OR
		 *   * is in this stratum and may have a better
		 *     heuristic.
		 */
		try_as_pos = (!SeenPos[apos]
			      || (!Sloppy
#ifdef HEURISTIC_THIS_STRATUM
				  && StratumOf[apos]==CurrStratum
#else
# ifdef HEURISTIC_LOWER_STRATA
				  && StratumOf[apos]<=CurrStratum
# endif
#endif
				  && SeenPos[apos]>=hi_min
				  && (SeenNeg[apos]>=hi_min
				      || !SeenNeg[apos])));
		try_as_neg = (!SeenNeg[apos]
			      || (!Sloppy
#ifdef HEURISTIC_THIS_STRATUM
				  && StratumOf[apos]==CurrStratum
#else
# ifdef HEURISTIC_LOWER_STRATA
				  && StratumOf[apos]<=CurrStratum
# endif
#endif
				  && SeenNeg[apos]>=hi_min
				  && (SeenPos[apos]>=hi_min
				      || !SeenPos[apos])));
	
		if(Model[apos]==LP_L_UNKNOWN
#ifdef TRY_NANT_ONLY
		   && NAnt[apos]
#endif
		   && (try_as_pos || try_as_neg)) {
			/* Do the positive first if it may cause a
			 * conflict.
			 */
			int posfirst = try_as_pos && !SeenPos[apos];
			foundone = 1;

			/* If we have already gotten this atom in a positive
			 * expansion, we know that there is no conflict here,
			 * so we should try expanding negatively first to
			 * increase the chance of getting a conflict. */
			if(posfirst) {
				if((pos = trypos(&(Prog->atoms[apos]), SeenPos, SeenNeg)) < 0) {
					CurrLoc = j+1;
					free(SeenPos);
					free(SeenNeg);
					return 0;
				}
			}
			if(try_as_neg) {
				if((neg = tryneg(&(Prog->atoms[apos]), SeenPos, SeenNeg)) < 0) {
					CurrLoc = j+1;
					free(SeenPos);
					free(SeenNeg);
					return 0;
				}
			}
			if(!posfirst && try_as_pos) {
				if((pos = trypos(&(Prog->atoms[apos]), SeenPos, SeenNeg)) < 0) {
					CurrLoc = j+1;
					free(SeenPos);
					free(SeenNeg);
					return 0;
				}
			}
			if(pos > neg) {
				min = neg;
				max = pos;
			} else {
				min = pos;
				max = neg;
			}
			
			/* If this atom has the best minimum and the best maximum so far */
			if(min > hi_min || (min == hi_min && max > hi_max)) {
				hi_min = min;
				hi_max = max;
				hi_index = apos;
				hi_pos = (max == pos);
			}
		}
		
		if(++j == Prog->natoms)
			j=0;
				
		if(j == CurrLoc)
			break;
	}

	if(Monitor)
		Monitor(Prog,hi_index,-1,LP_MON_GUESS);
	lp_aq_enqueue(hi_pos ? PosQueue : NegQueue, &Prog->atoms[hi_index]);
	TriedAtoms++;
	expand(1);
	
	free(SeenPos);
	free(SeenNeg);
	return 1;
}


/* Set an atom to false, and propagate that information.  If chosen is true,
 * backtracking will try this atom as true.
 */
void set_false(int atom, int chosen) 
{
	struct lp_rlist *j;
	int rpos;

	Model[atom] = LP_L_FALSE;
	Closure[atom] = 0;

	/* push this atom */
	Stack[Sp] = atom;
	AuxStack[Sp++] = chosen ? AUX_NEG : AUX_EXP_NEG;

	/* No rule with this atom in its positive body can fire */
	for(j=PosDepends[atom]; j!=NULL; j=j->next) {
		rpos = lp_rpos(Prog,j->rule);
		suppress(rpos,1);
	}
	/* We have put some rules closer to firing */
	for(j=NegDepends[atom]; j!=NULL; j=j->next) {
		rpos = lp_rpos(Prog,j->rule);
		--NegBodySize[rpos];
		maybe_fire(rpos);
	}
	/* If this atom is still the head of some rules, see if we can find
	 * out anything about the truth values for the bodies of those rules.
	 */
	if(HeadNumber[atom] != 0) {
		for(j=HeadOf[atom]; j!=NULL; j=j->next) {
			rpos = lp_rpos(Prog,j->rule);
			backchain_false(rpos);
		}
	}
}


/* Set an atom to true, and propagate that information.  If chosen is true,
 * backtracking will try this atom as false.
 */
void set_true(int atom, int chosen) 
{
	int rpos;
	struct lp_rlist *j;
	
	Model[atom] = LP_L_TRUE;
				
	/* push this atom */
	Stack[Sp] = atom;
	
	AuxStack[Sp++] = chosen ? AUX_POS : AUX_EXP_POS;

	/* We have put some rules closer to firing */
	for(j=PosDepends[atom]; j!=NULL; j=j->next) {
		rpos = lp_rpos(Prog,j->rule);
		--PosBodySize[rpos];
		maybe_fire(rpos);
	}
	/* No rule with this atom in its negative body can fire */
	for(j=NegDepends[atom]; j!=NULL; j=j->next) {
		rpos = lp_rpos(Prog,j->rule);
		suppress(rpos,0);
	}
	/* If this atom is the head of only one rule, that rule must have fired */
	if(HeadNumber[atom] == 1) {
		for(j=HeadOf[atom]; j!=NULL; j=j->next) {
			rpos = lp_rpos(Prog,j->rule);
			if(Suppressed[rpos] == 0) {
				backchain_true(rpos);
			}
		}
	}
}

void trivial_stratification(void) {
	int i;

	Stratum = lp_calloc(1, sizeof(struct lp_alist *));
	OrdAtoms = lp_malloc(Prog->natoms * sizeof(int));
	StratumOf = lp_calloc(Prog->natoms, sizeof(int));

	for(i=0; i<Prog->natoms; i++) {
		lp_al_add(&Stratum[0], &Prog->atoms[i]);
		OrdAtoms[i] = i;
	}
	
}


void atom_strata(int *rstr, int nstr) {
	int i, currrule=0, end;
	struct lp_atom *head;
	int hpos;
	int lastpos=0;

	Stratum = lp_calloc(nstr, sizeof(struct lp_alist *));
	OrdAtoms = lp_malloc(Prog->natoms * sizeof(int));
	StratumOf = lp_malloc(sizeof(int) * Prog->natoms);

	for(i=0; i<Prog->natoms; i++)
		StratumOf[i] = -1;
	
	for(i=0; i<nstr; i++) {
		end = currrule + rstr[i];
		
		for(;currrule < end; ++currrule) {
			head = Prog->rules[currrule].head;
			hpos = lp_apos(Prog,head);
			if(StratumOf[hpos] == -1) {
				lp_al_add(&Stratum[i], head);
				StratumOf[hpos] = i;
				OrdAtoms[lastpos++] = hpos;
			}
		}
	}
}

int trypos(struct lp_atom *atom, int *SeenPos, int *SeenNeg) 
{
	int oldsp;
	int j,a;
	int pos;

	oldsp = Sp;
	
	if(Monitor)
		Monitor(Prog,lp_apos(Prog,atom),-1,LP_MON_TEST_POS);
	
	lp_aq_enqueue(PosQueue, atom);
	expand(1);
	++TriedAtoms;

	pos = Sp - oldsp;
#ifdef HEURISTIC_BY_NANT
# ifndef HEURISTIC_BOTH
	pos = 0;
# endif
	for(j=oldsp; j<Sp; j++)
		if(NAnt[Stack[j]])
			++pos;
#endif
	
	if(!Conflict) {
		/* If we have found a stable model, return immediately */
		if(full_model())
			return -1;
		
		/* Do not later try things that were expanded */
		for(j=oldsp; j<Sp; j++) {
			a = Stack[j];
			if(Model[a] == LP_L_TRUE)
				SeenPos[a] = pos;
			else if(Model[a] == LP_L_FALSE)
				SeenNeg[a] = pos;
		}

		/* And backtrack */
		for(--Sp; Sp>=oldsp; --Sp)
			backtrack_single(Stack[Sp], AuxStack[Sp]);
		Sp = oldsp;
	} else {
		/* Replace the thing that conflicted with its negation */
		Conflict = 0;
		
		for(--Sp; Sp>=oldsp; --Sp)
			backtrack_single(Stack[Sp], AuxStack[Sp]);
		Sp = oldsp;
		if(Monitor)
			Monitor(Prog,lp_apos(Prog,atom),-1,LP_MON_FORCED_NEG);
		lp_aq_enqueue(NegQueue, atom);
		expand(0);
		return -1;
	}

	if(Monitor)
		Monitor(Prog,lp_apos(Prog,atom),-1,LP_MON_TEST_END);
	
	return pos;
}

int tryneg(struct lp_atom *atom, int *SeenPos, int *SeenNeg) 
{
	int oldsp;
	int j,a;
	int neg;

	oldsp = Sp;
	
	if(Monitor)
		Monitor(Prog,lp_apos(Prog,atom),-1,LP_MON_TEST_NEG);
	
	lp_aq_enqueue(NegQueue, atom);
	expand(1);
	++TriedAtoms;

	neg = Sp - oldsp;
#ifdef HEURISTIC_BY_NANT
# ifndef HEURISTIC_BOTH
	neg = 0;
# endif
	for(j=oldsp; j<Sp; j++)
		if(NAnt[Stack[j]])
			++neg;
#endif
	
	if(!Conflict) {
		/* If we have found a stable model, return immediately */
		if(full_model())
			return -1;
		
		/* Do not later try things that were expanded */
		for(j=oldsp; j<Sp; j++) {
			a = Stack[j];
			if(Model[a] == LP_L_TRUE)
				SeenPos[a] = neg;
			else if(Model[a] == LP_L_FALSE)
				SeenNeg[a] = neg;
		}

		/* And backtrack */
		for(--Sp; Sp>=oldsp; --Sp)
			backtrack_single(Stack[Sp], AuxStack[Sp]);
		Sp = oldsp;
	} else {
		/* Replace the thing that conflicted with its negation */
		Conflict = 0;
		
		for(--Sp; Sp>=oldsp; --Sp)
			backtrack_single(Stack[Sp], AuxStack[Sp]);
		Sp = oldsp;
		if(Monitor)
			Monitor(Prog,lp_apos(Prog,atom),-1,LP_MON_FORCED_POS);
		lp_aq_enqueue(PosQueue, atom);
		expand(0);
		return -1;
	}

	if(Monitor)
		Monitor(Prog,lp_apos(Prog,atom),-1,LP_MON_TEST_END);
	
	return neg;
}
