// rule.cc -- implementation for rules
// Copyright (C) 1999-2000 Tommi Syrjänen <Tommi.Syrjanen@hut.fi>
//  
// This program 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 of the License, or
// (at your option) any later version.
//  
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//  

#include "../config.h"
#include <string.h>
#include <limits.h>

#ifndef GLOBAL_H
#include "global.h"
#endif
#ifndef RULE_H
#include "rule.h"
#endif
#ifndef LITERAL_H
#include "literal.h"
#endif
#ifndef LIST_H
#include "list.h"
#endif
#ifndef GRAPH_H
#include "graph.h"
#endif
#ifndef SYMBOL_H
#include "symbol.h"
#endif
#ifndef PREDICATE_H
#include "predicate.h"
#endif
#ifndef ITERATE_H
#include "iterate.h"
#endif
#ifndef ERROR_H
#include "error.h"
#endif

void clear_pos(int pos)
{
  int i;
  for (i = 0; i < sys_data.total_vars; i++) {
    if (var_pos[i] >= pos) {
      variables[i] = -1;
      var_pos[i] = -1;
    }
  }
}

int Rule::max_tail = 1;

Rule::Rule(Literal *lt, RuleType rt, int dlt)
  : positive(UNORDERED), negative(UNORDERED),
    functions(UNORDERED), positive_conditions(UNORDERED),
    negative_conditions(UNORDERED), head_conditions(UNORDERED)   
{
  Literal *tmp = NULL;
  Function *fun = NULL;
  special_lits = NULL;
  special = 0;
  status  = RT_STRONG;
  head = lt;
  line_end = lt->lineno;
  line_start = lt->lineno;
  type = rt;
  pos = 0;
  weight_head = 0;
  deleted = 0;
  vars = NULL;
  atleast = atmost = head_number = 1;
  max_head = 1;
  head_array = NULL;
  num_weights = 0;
  num_assigns = 0;
  delete_all = dlt;
  num_conditions = 0;
  num_negative_conditions = 0;
  num_positive_conditions = 0;
  num_head_conditions = 0;
  num_positive_externals = 0;
  num_negative_externals = 0;
  
  sorted = 0;
  if (lt->ground)
    ground = 1;
  else
    ground = 0;
  
  if (lt->NeedsSimplifying())
    SimplifyLiteral(lt);

  if (type == OPTIMIZERULE)
    return;
  
  // if there are conditions for head atom, transfer them to the tail:
  //   bar(X):foo(X) :- baz(X)  ===> bar(X) :- foo(X), baz(X).
  if (lt->conditions) {
    while ((tmp = lt->conditions->Iterate())) {
      AddLiteral(tmp);

    }
    // clear the information so it won't interfere with range
    // restriction checking
    delete lt->conditions;
    lt->conditions = NULL;
  }
  if (lt->condition_func) {
    while ((fun = lt->condition_func->Iterate())) {
      AddFunction(fun);
    }
    delete lt->condition_func;
    lt->condition_func = NULL;
  }

}

Rule::Rule(RuleType rt, int initial_head) // for multiple heads
  : positive(UNORDERED), negative(UNORDERED), 
    functions(UNORDERED), positive_conditions(UNORDERED),
    negative_conditions(UNORDERED), head_conditions(UNORDERED)   
{
  assert((rt == CHOICERULE) || (rt == GENERATERULE));
  status = RT_STRONG;
  head = NULL;
  special_lits = NULL;
  special = 1;
  weight_head = 0;
  num_weights = 0;
  num_conditions = 0;
  num_head_conditions = 0;
  num_negative_conditions = 0;
  num_positive_conditions = 0;
  sorted = 0;
  vars = NULL;
  if (initial_head <= 0)
    initial_head = 2;
  head_array = new Literal*[initial_head];
  for (int i = 0; i < initial_head; i++) {
    head_array[i] = 0;
  }
  line_end = 0;
  pos = 0;
  line_start = 0;
  type = rt;
  atleast = atmost = 0;
  deleted = 0;
  delete_all = 1;
  head_number = 0;
  max_head = initial_head;
  ground = 1;
  num_assigns = 0;
}

Rule::~Rule()
{
  Literal *lt = NULL;
  Function *fun = NULL;
  int i;
  if (delete_all) {
    if (head && head != false_lit)
      delete head;
    while (( lt = positive.Iterate())) {
      if (!lt->special) {
	if (lt != false_lit) {
	  delete lt;
	}
      } else {
	delete (SpecialLiteral*) lt;
      }
    }
    while (( lt = negative.Iterate())) {
      if (lt != false_lit)
	delete lt;
    }
    while (( fun = functions.Iterate())) {
	delete fun;
    }
  }
  if (head_array) {
    for (i = 0; i <head_number; i++) {
      if (head_array[i] && head_array[i] != false_lit)
	delete head_array[i];
    }
    delete [] head_array;
  }

  if (special_lits)
    delete special_lits;
  
  if (vars)
    delete [] vars;
}
  

void Rule::SimplifySpecialRule()
{
  Literal *lt = NULL;
  SpecialLiteral *sp = NULL;

  ExpandAllConditions();
  FindWeights();
  
  while ((lt = positive.Iterate())) {
    if (lt->special) {
      sp = (SpecialLiteral*) lt;
      if (sp->type == WEIGHTRULE)
	num_weights++;
      sp->AddVariables();
    }
  }

  while (special_lits && (sp = (SpecialLiteral*) special_lits->Iterate())) {
    if (sp->pred2 >= 0)
      AddLiteral(sp->atmost_lit);
  }
  
  // make all variables global by constructing new domain
  // predicates for each variable
  RemoveLocalVars();
  
  if(type == CHOICERULE) {
    ExpandChoiceRule();
  }

}

void Rule::ExpandChoiceRule()
{
  Rule *rl = NULL;
  Literal *lt = NULL;
  SpecialLiteral *slt = NULL;
  Function *fun = NULL;
  int i = 0;
  int numvars = 0;
  RuleType tp = CONSTRAINTRULE;

  if (weight_head) {
    tp = WEIGHTRULE;
  }
  
  // we must check if any condition expansion made the rule a ground one
  if (atmost > 0) {
    // if there is an upper bound to the number of true heads
    // construct the rule like this:
    //  { a1, ..., an } m :- body.
    // ===> :- m { a1, ..., an }, body.

    if (!false_lit) {
      long pred;
      pred = Predicate::DefinePredicate(FALSE_NAME, 0, line_start);
      false_lit = new Literal(pred, NULL, 0, 0);
      if (!false_lit)
	error(SYS_ERR, "malloc error");
    }
    
    rl = new Rule(false_lit, BASICRULE);
    if (!rl)
      error(SYS_ERR, "malloc error");
    
    slt = new SpecialLiteral(tp, atmost+1, ERROR_NUMBER);
    if (!slt)
      error(SYS_ERR, "malloc error");

    for (i = 0; i < head_number; i++) {
      if (!head_array[i]->deleted) {
	lt = head_array[i];
	numvars += lt->numvars;
	slt->tail.Insert( lt->Duplicate(0));
	if (lt->conditions || lt->condition_func) {
	  slt->num_conditions++;
	  rl->num_conditions++;
	  if (lt->negative)
	    slt->num_negative_conditions++;
	  else
	    slt->num_positive_conditions++;
	}
      }
    }
    slt->AddVariables();
    rl->AddLiteral(slt);
    while ((lt = positive.Iterate())) {
      rl->AddLiteral(lt);
      numvars += lt->numvars;
    }
    while ((lt = negative.Iterate())) {
      rl->AddLiteral(lt);
      numvars += lt->numvars;
    }
    while ((fun = functions.Iterate())) {
      rl->AddFunction(fun);
      //      numvars += fun->numvars;
    }
    if (!ground && !numvars)
      ground = 1;
    predicates[false_lit->pred]->AddSpecialRule(rl);
  }

  if (atleast > 0) {
    // the rules of form:
    // m { a1, ..., an } :- body
    //    ====> :- n-m { not a1, ..., not an }

    if (!false_lit) {
      long pred;
      pred = Predicate::DefinePredicate(FALSE_NAME, 0, line_start);
      false_lit = new Literal(pred, NULL, 0, 0);
      if (!false_lit)
	error(SYS_ERR, "malloc error");
    }
    
    rl = new Rule(false_lit, BASICRULE);
    if (!rl)
      error(SYS_ERR, "malloc error");
    
    slt = new SpecialLiteral(tp, atleast, ERROR_NUMBER);
    //			     head_number - atleast +1 -deleted,
    //		     ERROR_NUMBER);
    if (!slt)
      error(SYS_ERR, "malloc error");
    slt->dynamic_atleast = 1;
    
    for (i = 0; i < head_number; i++) {
      if (!head_array[i]->deleted) {
	lt = head_array[i];
	slt->tail.Insert( lt->Duplicate(1));
	if (lt->conditions || lt->condition_func) {
	  slt->num_conditions++;
	  rl->num_conditions++;
	  if (lt->negative)
	    slt->num_negative_conditions++;
	  else
	    slt->num_positive_conditions++;
	}
      }
    }
    slt->AddVariables();
    rl->AddLiteral(slt);
    while ((lt = positive.Iterate()))
      rl->AddLiteral(lt);
    while ((lt = negative.Iterate()))
      rl->AddLiteral(lt);
    while ((fun = functions.Iterate()))
      rl->AddFunction(fun);
    predicates[false_lit->pred]->AddSpecialRule(rl);
    
    
  }
}
  
    
void Rule::SimplifyLiteral(Literal *lt)
{
  // go through all arguments, generate a new variable and a new
  // predicate for each disjunctive range found and generate a new
  // variable and a function term for every function found. The new
  // term will be added as a condition to literal in question 

  int pos = 0;
  long i = 0, j = 0, start = 0, end = 0, tmp = 0;
  long var = -1, pred = -1;
  Literal *new_lt = NULL;
  Literal *lt2 = NULL;
  Instance new_instance = -1;
  LiteralList to_be_added;
  Term *nt1 = NULL;
  Function *nt2 = NULL;
  Function *fun = NULL;
  InstFunc fp;
  //  Function *fun;

  static char *varname = NULL;
  static char *predname = NULL;

  if (!varname) {
    varname = new char[RANGE_MAX_NAME];
    predname = new char[RANGE_MAX_NAME];
    if (!varname || !predname)
      error(SYS_ERR, "malloc error");
  }

  // recursively simplify conditions, if they exist
  if (lt->conditions) {
    while ((lt2 = lt->conditions->Iterate())) {
      if (lt2->NeedsSimplifying()) {
	SimplifyLiteral(lt2);
	Literal *tmp = 0;
	while (lt2->conditions && (tmp = lt2->conditions->Iterate())) {
	  to_be_added.Insert(tmp);
	}
	while (lt2->condition_func &&
	       (fun = lt2->condition_func->Iterate())) {
	  lt->condition_func->Insert(fun);
	}
	lt2->conditions = 0;
	lt2->condition_func = 0;
      }
    }
    while ((lt2 = to_be_added.Iterate())) {
      lt->conditions->Insert(lt2);
    }
  }
  
  for (i = 0; i < lt->arity; i++) {
    switch(lt->args[i]) {
    case T_RANGE:
      if (sys_data.num_ranges >= RANGE_MAX_NUMBER)
	int_error("maximum number of ranges exceeded", "");
      
      // get the right range number
      pos = sprintf(varname, "I_Range&%d", sys_data.num_ranges);
      sprintf(predname, "_range%d", sys_data.num_ranges);
      sys_data.num_ranges++;
      
      varname[pos] = '\0';
      predname[pos] = '\0';
      
      var = variable_table->Insert(varname);
      if (var < 0)
	int_error("cannot generate range variable '%s'", varname);
      lt->args[i] = T_VARIABLE;
      lt->vars[i] = var;
      lt->ground = 0;
      
      pred = predicate_table->Insert(predname, 1);
      predicates[pred]->SetArity(1);
      predicates[pred]->SetName(strdup(predname));
      predicates[pred]->SetPred(pred);
      predicates[pred]->SetStatus(DM_INTERNAL);
      
      if (pred < 0)
	int_error("cannot generate range domain '%s'", predname);
      
      dependency_graph->AddNode(pred);
      dependency_graph->AddEdge(head->pred, pred);
      
      start = GET_VALUE(((Range *) lt->terms[i])->start);
      end = GET_VALUE(((Range *) lt->terms[i])->end);
      if (start > end) {
	tmp = start;
	start = end;
	end = tmp;
      }
      delete lt->terms[i];
      lt->terms[i] = NULL;
      
      // add the values of range to the predicate
      for (j = start; j <= end; j++) {
	new_instance = MAKE_NUMBER(j);
	predicates[pred]->AddInstance(&new_instance);
      }
      // and finally construct a matching literal and add it
      new_lt = new Literal(var, pred);
      if (!new_lt)
	error(SYS_ERR, "malloc error");
      new_lt->lineno = line_start;
      if (!lt->conditions) {
      	lt->conditions = new LiteralList;
      	lt->condition_func = new FunctionList;
      }
      lt->conditions->Append(new_lt);

      //      AddLiteral(new_lt);
      // if the rule was ground rule, it is not it anymore. 
      if (ground)
	ground = 0;
      break;
    case T_FUNCTION:
      var = DefineSystemVariable();
      if (var < 0)
	int_error("cannot generate function argument '%s'",
		  varname);
      lt->args[i] = T_VARIABLE;
      lt->vars[i] = var;
      lt->ground = 0;
      // construct the new function
      fp = function_table->GetFunction("assign");
      nt1 = new Term(T_VARIABLE, var, line_start);
      nt2 = new Function(fp, "assign", 2, line_start);
      nt2->AddArgument(nt1);
      nt2->AddArgument(lt->terms[i]);
      lt->terms[i] = NULL; // don't delete the function here, it is
      // needed later.
      if (nt2->has_range)
	RestrictFunction(nt2);

      if (!lt->conditions) {
      	lt->conditions = new LiteralList;
      	lt->condition_func = new FunctionList;
      }
      // remove nested assigns 
      if (nt2->args[1]->has_function) {
	Function *f = (Function*) nt2->args[1];
	if (f->assign) {
	  Function *assign_fun = f->DuplicateFunction();
	  // AddFunction(nt2);
	  lt->condition_func->Append(assign_fun);
	}
      }
	
      lt->condition_func->Append(nt2);
      
      //      AddFunction(nt2);
      
      // if the rule was ground it will be anymore... 
      if (ground)
	ground = 0;
      break;
    case T_ATOM:
      var = DefineSystemVariable();
      if (var < 0)
	int_error("cannot generate literal argument '%s'",
		  varname);
      lt->args[i] = T_VARIABLE;
      lt->vars[i] = var;
      lt->ground = 0;
      lt2 = ((LiteralTerm*)lt->terms[i])->lt;
      if (lt2->NeedsSimplifying()) {
	SimplifyLiteral(lt2);
      }
      
      // construct the new assign for the atom
      fp = function_table->GetFunction("assign");
      nt1 = new Term(T_VARIABLE, var, line_start);
      nt2 = new Function(fp, "assign", 2, line_start);
      nt2->AddArgument(nt1);
      nt2->AddArgument(lt->terms[i]);
      lt->terms[i] = NULL;
      
      if (!lt->condition_func) {
      	lt->condition_func = new FunctionList();
      	lt->conditions = new LiteralList();
      }
      if (lt2->condition_func) {
	Function *fun = NULL;
	while ((fun = lt2->condition_func->Iterate())) {
	  lt->condition_func->Append(fun);
	}
	lt2->condition_func = NULL;
      	lt2->conditions = NULL;
      }

      // remove nested assigns 
      //      if (nt2->args[1]->has_function) {
      //	Function *f = (Function*) nt2->args[1];
      //	if (f->assign) {
      //	  Function *assign_fun = f->DuplicateFunction();
      //	  // AddFunction(nt2);
      //	  lt->condition_func->Append(assign_fun);
      //	}
      //      }
	

      
      lt->condition_func->Append(nt2);
      // AddFunction((Function*)nt2, 1);
      if (ground)
	ground = 0;
      break;
    default:
      break;
    }
  }
}
    
    

void Rule::AddLiteral(Literal *lt)
{
  if (lt->special) {
    special++;
    if (!special_lits)
      special_lits = new LiteralList;
    if (!special_lits)
      error(SYS_ERR, "malloc error");
    special_lits->Insert(lt);
    if (((SpecialLiteral*)lt)->num_conditions > 0) {
      num_conditions++;
    }
  }

  if (!lt->ground) {
    ground = 0;
  }

  if (lt->NeedsSimplifying())
    SimplifyLiteral(lt);
  
  if (lt->negative)
    negative.Insert(lt);
  else {
    positive.Insert(lt);
    if (positive.Size() > max_tail) {
      max_tail = positive.Size();
    }
  }

  if (line_start < 1) {
    line_start = lt->lineno;
  }
  if (line_end < lt->lineno) {
    line_end = lt->lineno;
  }
  if (type == OPTIMIZERULE)
    return;

  if (lt->conditions) {
    num_conditions++;
    if (lt->negative)
      num_negative_conditions++;
    else
      num_positive_conditions++;
  }
  // check for conditions, and add them as normal literals if this is
  // a basic rule
  //  if (lt->conditions) {
  // while ((tmp = lt->conditions->Iterate())) {
  //   if (type == BASICRULE) 
  //	AddLiteral(tmp);
  // }
  // delete lt->conditions;
  // lt->conditions = NULL;
  // }
  //if (lt->condition_func) {
  //  while ((fun = lt->condition_func->Iterate())) {
  //    if (type == BASICRULE) 
  //	AddFunction(fun);
  //  }
  //  delete lt->condition_func;
  // lt->condition_func = NULL;
  // }
}
  

void Rule::AddFunction(Function *t, int start)
{
  if (!t->ground) {
    ground = 0;
  }

  if (!start)
    functions.Append(t);
  else
    functions.Insert(t);
  if (!t->assign) {
    if (positive.Size() + functions.Size() > max_tail) {
      max_tail = positive.Size() + functions.Size();
    }
  } else {
    num_assigns++;
  }
  if (line_end < t->lineno)
    line_end = t->lineno;
}

void Rule::AddHead(Literal *lt)
{
  assert(lt);

  if (lt->NeedsSimplifying())
    SimplifyLiteral(lt);
  
  if ((line_start == 0) || (line_start > lt->lineno))
    line_start = lt->lineno;
  if (line_end < lt->lineno)
    line_end = lt->lineno;
  
  if (!lt->ground)
    ground = 0;

  if (lt->conditions || lt->condition_func) {
    num_conditions++;
    num_head_conditions++;
  }
  
  head_array[head_number++] = lt;

  if (head_number == max_head) {
    Literal **hds = new Literal*[2*max_head];
    int i;
    if (!hds)
      error(SYS_ERR, "malloc error");
    for (i = 0; i < head_number; i++) {
      hds[i] = head_array[i];
    }
    for ( ; i < 2*max_head; i++) {
      hds[i] = NULL;
    }
    delete [] head_array;
    head_array = hds;
    max_head *= 2;
  }
  
  return;
}

void Rule::ExpandAllConditions()
{
  static LiteralList conds(UNORDERED);
  static LiteralList to_add(UNORDERED);
  static LiteralList add_to_basic(UNORDERED); 
  static FunctionList cond_funcs(UNORDERED);
  Literal *lt = NULL, *lt2 = NULL;
  int i = 0;
  SpecialLiteral *sp = NULL;
  Function *fun = NULL;

  add_to_basic.Clear();
  // first check the heads if necessary
  if (type != BASICRULE) {
    for (i=0; i < head_number; i++) {
      conds.Clear();
      cond_funcs.Clear();
      lt = head_array[i];
      if (lt->condition_func || lt->conditions) {
	if (ExpandCondition(lt, &conds, &cond_funcs, weight_head) ==
	    COND_LOCAL) { 
	  while ((lt2 = conds.Iterate())) {
	    AddHead(lt2);
	  }
	  while ((fun = cond_funcs.Iterate())) {
	    AddFunction(fun);
	  }
	  lt->deleted = 1;
	  deleted++;
	  num_conditions--;
	  num_head_conditions--;
	}
      }
    }
  } else {
    if (head->condition_func || head->conditions) {
      error(USR_ERR, "%s: conditions are not allowed in basic rule "
	    "heads", error_file_and_line(line_start));
    }
  }
  
  // then the tails
  while ((lt = positive.Iterate())) {
    if (lt->special) {
      sp = (SpecialLiteral*) lt;
      if (sp->type == WEIGHTRULE)
	num_weights++;
      
      while ((lt2 = sp->tail.Iterate())) {
	if (lt2->conditions || lt2->condition_func) {
	  conds.Clear();
	  cond_funcs.Clear();
	  
	  if (ExpandCondition(lt2, &conds, &cond_funcs,
			      (sp->type == WEIGHTRULE))  ==
	      COND_LOCAL) {
	    while ((lt = conds.Iterate())) {
	      to_add.Insert(lt);
	    }
	    while ((fun = cond_funcs.Iterate())) {
	      AddFunction(fun);
	    }
	    //	    if ((conds.Size() > 0) || (cond_funcs.Size() > 0)) {
	      sp->tail.Remove(lt2);
	      
	      sp->num_conditions--;
	      if (lt2->negative) {
		sp->num_negative_conditions--;
	      } else {
		sp->num_positive_conditions--;
	      }
	      
	      //	    } else {
	      //	      lt2->empty_condition = 1;
	      //	    }
	  }
	}
	while ((lt = to_add.Iterate()))
	  sp->tail.Insert(lt);
	to_add.Clear();
      }
    } else if (lt->conditions || lt->condition_func) {
      conds.Clear();
      cond_funcs.Clear();
      if (ExpandCondition(lt, &conds, &cond_funcs, 0) == COND_LOCAL) {
	while ((lt2 = conds.Iterate())) {
	  add_to_basic.Insert(lt2);
	}
	while ((fun = cond_funcs.Iterate())) {
	  AddFunction(fun);
	}
	//	if ((conds.Size() > 0) || (cond_funcs.Size() > 0)) {
	positive.Remove(lt);
	num_conditions--;
	num_positive_conditions--;
	//	} else {
	  //	  lt->empty_condition = 1;
	  //	}
      }
    }
  }
  // FIXME: Why this is here???
  while (special_lits && (sp = (SpecialLiteral*) special_lits->Iterate())) {
    if (sp->atmost_lit)
      AddLiteral(sp->atmost_lit);
  }
  
  while ((lt = negative.Iterate())) {
    if (lt->conditions || lt->condition_func) {
      conds.Clear();
      cond_funcs.Clear();
      if (ExpandCondition(lt, &conds, &cond_funcs, 0) == COND_LOCAL) {
	while ((lt2 = conds.Iterate())) {
	  add_to_basic.Insert(lt2);
	}
	while ((fun = cond_funcs.Iterate())) {
	  AddFunction(fun);
	}
	//	if ((conds.Size() > 0) || (cond_funcs.Size() > 0)) {
	  negative.Remove(lt);
	  num_conditions--;
	  num_negative_conditions--;
	  //	} else {
	  lt->empty_condition = 1;
	  //	}
      }
    }
   
  }
  
  if (add_to_basic.Size() > 0) {
    while ((lt = add_to_basic.Iterate())) {
      AddLiteral(lt);
    }
  }
}

int Rule::ExpandCondition(Literal *lt, LiteralList *conds,
			  FunctionList *funs, int needs_weight) 
{
  // go through each condition of the literal, construct a rule out of
  // them, ground the rule, and add the resulting literals to the rule
  // body. 

  //  { foo(Local):bar(Local) } :- ... ====> { foo(a), ... } :- ...
  
  int global_vars = 0;
  int sets_variables = 0; // true, if a condition gives values to
  		 	  // non-internal variables. 
  Literal *tmp = NULL;
  Function *fun = NULL;
  int i = 0;
  int errors = 0;

  while (lt->conditions && (tmp = lt->conditions->Iterate())) { 
    if (!tmp->DomainLiteral()) {
      error(USR_ERR, "%s: A non-domain condition predicate '%s'",
	    error_file_and_line(lt->lineno), predicates[lt->pred]->Name());
      errors++;
      continue;
    }
    for (i = 0; i < tmp->arity; i++) {
      if (tmp->args[i] == T_VARIABLE) {
	if (vars[tmp->vars[i]] != VAR_CONDITION) 
	  global_vars++;
	else if ( !internal_variable(tmp->vars[i]))
	  sets_variables = 1;
      }
    }
  }
  while (lt->condition_func && (fun = lt->condition_func->Iterate())) {
    for (i=0; i < fun->arity; i++) {
      if (fun->args[i]->type == T_VARIABLE) {
	if (vars[fun->args[i]->val] != VAR_CONDITION) {
	  global_vars++;
	} else if (!internal_variable(fun->args[i]->val))
	  sets_variables = 1;
      } else if (fun->args[i]->type == T_FUNCTION &&
		 ((Function*)fun->args[i])->CheckGlobal(vars)) {
	global_vars++;
      }
      else if (fun->args[i]->type == T_ATOM &&
	       ((LiteralTerm*)fun->args[i])->CheckGlobal(vars)) {
	global_vars++; // *** korjaa!!!
      }
    }
  }
  for (i = 0 ; i < lt->arity; i++) {
    if ((lt->args[i] == T_VARIABLE) &&
	vars[lt->vars[i]] != VAR_CONDITION) {
      global_vars++;
    }
  }
  
  
  // don't ground yet if there are global variables 
  if (global_vars > 0) {
    lt->has_global_condition = 1;
    if (!sets_variables) {
      lt->ignore_global_condition = 1;
    }
    return COND_GLOBAL;
  }
    /*  error(USR_ERR, "%ld--%ld: a global variable is used within a condition",
	  line_start, line_end); 
    fprintf(stderr, "\tglobal variables: ");
    for (i = 0; i < global_vars; i++) {
      fprintf(stderr, "%s ", variable_table->symbols[global[i]]);
    }
    fprintf(stderr, "\n");
    errors++;
  }

   
  if (errors)
    return COND_ERROR;
  */

  memset(variables, VAR_NONE, sizeof(Variable) * sys_data.total_vars);
  memset(var_pos, VAR_NONE, sizeof(Variable) * sys_data.total_vars);
  GroundCondition(lt, conds);
  return COND_LOCAL;
#if 0
  condition_set = new InstanceSet(sys_data.domain_size, lt->arity);

  // create the new instances
  rl = new Rule(lt, BASICRULE, 0);

  rl->GroundRule(GRD_CONDITION);
  
  // go through all ground instances, construct matching literals and
  // add them to the literal list
  iter = new InstanceIterator(condition_set);
  if (!iter)
    error(SYS_ERR, "malloc error");
  while ((it = iter->Iterate())) {
    new_lit = lt->Duplicate(0);
    for (i = 0; i < lt->arity; i++) {
      // if the argument is a bound variable, fetch its value,
      // otherwise keep the variable. 
      if (lt->args[i] == T_VARIABLE &&
	  vars[lt->vars[i]] == VAR_CONDITION) {
	new_lit->args[i] = T_CONSTANT;
	new_lit->cons[i] = it[i];
	new_lit->vars[i] = -1;
	new_lit->numvars--;
      }
    }
    if (lt->weight) {
      wt = lt->weight->Instantiate(new_lit, lt);
      new_lit->weight = wt;
    } else if (needs_weight) {
      new_lit->FindWeight();
    }
    conds->Insert(new_lit);
  }
  delete iter;
  delete rl;
  delete condition_set;
  condition_set = NULL;
    
  // since all variables were expanded, add all locally
  return COND_LOCAL;
#endif
}

int Rule::CheckNegativeLiterals()
{
  if (sys_data.print_domains != PR_ALL)
    return 1;
  
  if (type == BASICRULE) {
    if ((predicates[head->pred]->Status() == DM_DOMAIN ) ||
	(predicates[head->pred]->Status() == DM_INTERNAL ))
      return 1;
    else
      return 0;
  }

  // generaterule
  if ((predicates[head_array[0]->pred]->Status() == DM_DOMAIN) ||
      (predicates[head_array[0]->pred]->Status() == DM_INTERNAL))
    return 1;

  return 0;
}


void Rule::RestrictFunction(Function *tm)
{
  int pos = 0;
  long i, j, start, end, tmp;
  long var = -1, pred = -1;
  Literal *lt = NULL;
  Instance new_instance = -1;
  static char *varname = NULL;
  static char *predname = NULL;

  if (!varname) {
    varname = new char[RANGE_MAX_NAME];
    predname = new char[RANGE_MAX_NAME];
    if (!varname || !predname)
      error(SYS_ERR, "malloc error");
  }
    
  for (i = 0; i < tm->arity; i++) {
    if (tm->args[i]->type == T_RANGE) {
      
      if (sys_data.num_ranges >= RANGE_MAX_NUMBER)
	int_error("maximum number of ranges exceeded", "");
      
      // get the right range number
      pos = sprintf(varname, "I_Range&%d", sys_data.num_ranges);
      sprintf(predname, "_range&%d", sys_data.num_ranges);
      sys_data.num_ranges++;
      
      varname[pos] = '\0';
      predname[pos] = '\0';
      
      var = variable_table->Insert(varname);
      if (var < 0)
	int_error("cannot generate range variable '%s'", varname);
      tm->args[i]->type = T_VARIABLE;
      tm->val = var;
      
      pred = predicate_table->Insert(predname, 1);

      if (pred < 0)
	int_error("cannot generate range domain '%s'", predname);
      predicates[pred]->SetArity(1);
      predicates[pred]->SetName(strdup(predname));
      predicates[pred]->SetPred(pred);      
      dependency_graph->AddNode(pred);
      dependency_graph->AddEdge(head->pred, pred);
      
      start = GET_VALUE(((Range *) tm->args[i])->start);
      end = GET_VALUE(((Range *) tm->args[i])->end);
      if (start > end) {
	tmp = start;
	start = end;
	end = tmp;
      }

      // add the values of range to the predicate
      for (j = start; j <= end; j++) {
	new_instance = MAKE_NUMBER(j);
	predicates[pred]->AddInstance(&new_instance);
      }
      // and finally construct a matching literal and add it
      lt = new Literal(var, pred);
      if (!lt)
	error(SYS_ERR, "malloc error");
      lt->lineno = line_start;
      AddLiteral(lt);
    } else if (tm->args[i]->has_range) {
      RestrictFunction((Function*) tm->args[i]);
    }
  }
}

int Rule::TrueGround()
{
  Literal *lt = NULL;
  Predicate *pr = NULL;
  Function *fun = NULL;
  
  while ((lt = positive.Iterate())) {
    pr = predicates[lt->pred];
    if (!lt->DomainLiteral()) {
      positive.ClearIterator();
      return 1;
    }
    if (lt->arity == 0) {
      if (!pr->follows) {
	positive.ClearIterator();
	return 0;
      }
    } else {
      if (pr->atoms->Lookup(lt->cons) < 0) {
	positive.ClearIterator();
	return 0;
      }
    }
  }
  
  while ((lt = negative.Iterate())) {
    if (lt->arity == 0) {
      if (predicates[lt->pred]->follows) {
	negative.ClearIterator();
	return 0;
      }
    } else {
      if (predicates[lt->pred]->atoms->Lookup(lt->cons) >= 0) {
	negative.ClearIterator();
	return 0;
      }
    }
  }

  while ((fun = functions.Iterate())) {
    if (!fun->Test(0)) {
      functions.ClearIterator();
      return 0;
    }
  }
  
  return 1;
}

void Rule::GroundSpecialRule(int domain)
{
  GroundRule(domain);
}


// FIXME: GroundRule is not reentrant. 
void Rule::GroundRule(int domain)
{
  debug(DBG_GROUND, 3, "Grounding rule. domain: %d", domain); 
  Literal *lt = NULL;
  Function *fun = NULL;
  static FunctionList **funs = NULL;
  static int old_max = 0;
  Iterator **indices = NULL;
  Predicate *pr = NULL;
  static LiteralList **negs = NULL;
  Literal **lts = NULL;
  Instance *item = NULL;
  static Instance *null_item = NULL;
  Instance **values = NULL;
  int *inds = NULL;
  DomainType tp = DM_UNKNOWN;
  int alloc_max = 0, max = 0, i = 0, existing = 0, ind = -1, end = 0;
  int constant_index = 0, constant_index_pos = 0;
  static Instance *constant_indices = NULL;
  int check_negatives = CheckNegativeLiterals();
  
  LiteralList posg(ORDERED_SMALL), posn(UNORDERED);
  // respectively number of domain positive, nondomain positive and
  // domain negative
  int gpos = 0, npos = 0, gneg = 0, pos = 0, discard = 0; 

  if (!null_item) {
    null_item = new Instance;
    if (!null_item)
      error(SYS_ERR, "malloc error");
    *null_item = -1;
  }

  if (old_max < max_tail) {
    for (i = 0; i < old_max; i++) {
      delete funs[i];
    }
    delete [] funs;
    delete [] constant_indices;
    funs = new FunctionList*[max_tail];
    constant_indices = new Instance[max_tail];
    if (!funs || !constant_indices)
      error(SYS_ERR, "malloc error");
    for (i = 0; i < max_tail; i++) {
      funs[i] = new FunctionList;
      constant_indices[i] = -1;

    }
  }
  
  if (old_max < max_tail) {
    if (negs) {
      delete [] negs;
    }
    negs = new LiteralList*[max_tail];
    if (!negs)
      error(SYS_ERR, "malloc error");
    for (i=0; i < max_tail; i++)
      negs[i] = new LiteralList;
    old_max = max_tail;
  }

  for (i = 0; i < max_tail; i++) {
    funs[i]->Clear();
    negs[i]->Clear();
  }
    
  // first we divide positive literals to domain and non-domain
  // predicates and sort domain predicates by the size of their
  // domains.
  positive.ClearIterator();
  while ((lt = positive.Iterate())) {
    if (lt->special)
      continue;
    if (lt->DomainLiteral()) {
      if (predicates[lt->pred]->NeedsToBePrinted())
	gpos--;
      
      if (lt->has_global_condition) {
	gpos++;
	continue;
      }
      if (lt->arity > 0 && !lt->ground) { 
	posg.Insert(lt, predicates[lt->pred]->Size());
	gpos++;
      } else {
	if (!lt->Test()) { // is there a unsatisfiable literal
	  end = 1;
	}
	gpos++;
      }
    } else {
      posn.Insert(lt);
      npos++;
    }
  }
  
  // allocate and initialize the data structures
  max = posg.Size();

  if (!max)
    alloc_max = 1;
  else
    alloc_max = max;
  
  indices = new Iterator*[alloc_max];

  inds = new int[alloc_max];
  lts = new Literal*[alloc_max];
  values = new Instance*[alloc_max];
  if (!inds || !indices || !lts || !values)
    error(SYS_ERR, "malloc error");

  memset(variables, -1, sys_data.total_vars * sizeof(Instance));
  memset(inds, -1, alloc_max * sizeof(int));
  for (i = 0; i < alloc_max; i++ ) {
    indices[i] = NULL;
    lts[i] = NULL;
  }
  memset(var_pos, -1, sys_data.total_vars * sizeof(Instance));
  for (i = 0; i < max; i++)
    values[i] = null_item;
    
  // set value of variables[i] to the first literal in positive
  // position where it appears in the domain predicates. Confusing,
  // huh. At same time choose an index for it. Use a constant index if
  // possible. 
  pos = -1;
  while ((lt = posg.Iterate())) {
    pos++; existing = 0; ind = -1;
    lts[pos] = lt;
    pr = predicates[lt->pred];
    if (lt->ground && !lt->Test()) { // return if unsatisfiable body 
      end = 1;
    }
    constant_index = 0;
    for (i = 0; i < lt->arity; i++) {
      if (lt->vars[i] >= 0) {
	if (variables[lt->vars[i]] < 0) {
	  variables[lt->vars[i]] = pos;
	} else { // choose as index if needed
	  if ((variables[lt->vars[i]] != pos) && !existing) {
	    if (pr->existing_indexes & (1 << i)) 
	      existing = 1;
	    ind = i;
	    constant_index = 0;
	  }
	}
      } else {
	// check for possibility of using a constant as an index
	if (lt->cons[i] >= 0 && !existing) {
	  if (pr->existing_indexes & (1 << i))
	    existing = 1;
	  ind = i;
	  constant_index = 1;
	}
      }
    }
    // allocate the iterator
    if (ind < 0) {
      indices[pos] = new InstanceIterator(pr->atoms);
    } else {
      if (!existing)
	pr->CreateIndex(ind);
      indices[pos] = new IndexIterator(pr->indices[ind]);
    }
    if (!indices[pos])
      error(SYS_ERR, "malloc error");
    inds[pos] = ind; 
    // and set the values array to point to right variable
    if (ind >= 0) {
      if (!constant_index)
	values[pos] = &variables[lt->vars[ind]];
      else {
	values[pos] = &constant_indices[constant_index_pos];
	constant_indices[constant_index_pos++] = lt->cons[ind];
      }
    }
  }

  // then go through all functions to seek when the function can be
  // computed and add it to right list
  if (functions.Size() > 0) {
    functions.ClearIterator();
    while ((fun = functions.Iterate())) {
      if (fun->assign && variables[fun->args[0]->val] >=0)
	fun->RemoveAssign();

      pos = fun->GetPos();
      if (pos < 0)
	pos = max -1;
      if ((pos < 0) || (pos >= max))
	pos = 0;
      fun->AddVars(pos);
      funs[pos]->Append(fun);
    }
  }

  // same to negative literals
  if (negative.Size() > 0)
    negative.ClearIterator();
    while ((lt = negative.Iterate())) {
      tp = predicates[lt->pred]->Status();
      if (tp == DM_SPECIAL)
	continue;
      if (!lt->ground) {
	pos = lt->GetPos();
	if ((pos != 0) && ((pos < 0) || (pos >= max)))
	  int_error("invalid negative literal position '%d'", pos);
	negs[pos]->Insert(lt);

      } else {
	if (lt->Test()) { // check if a negative ground literal is true
	  end = 1;        // if it is, the rule body is always
	}                 // unsatisfied 
      }
      // at the same time calculate the number of negative domains
      if (tp == DM_DOMAIN && !predicates[lt->pred]->NeedsToBePrinted())
	gneg++;
    }

  // and then do the actual job
  memset(variables, -1, sys_data.total_vars * sizeof(Instance));
  pos = 0;

  if (ground && !end) {
    if (TrueGround()){
      if (domain == GRD_RULE)
	EmitGround(gpos, gneg,1);
      else
	head->CreateInstance(domain);
    }
    end = 1;
  }

  if (!end) {
    while (1) {
      if (pos < 0)
	break; // all possible bindings have been checked
      
      discard = 0;
      if (max != 0) 
	item = indices[pos]->Iterate(*values[pos]);

      // if run out of items in this level go back up one level
      if (!item && max != 0) {
	pos--;
	clear_pos(pos);
      } else {
	// bind the new variables
	if (Literal::BindLiteral(lts[pos], item, pos)) {
	  // values were ok, then check all applicable functions and 
	  // negative literals. These have to be in this order since
	  // fun->Test() may set values of some variables
	  
	  while ((functions.Size() > 0) && (fun = funs[pos]->Iterate()))
	    if (!discard && !fun->Test(pos))
	      discard = 1;


	  if (check_negatives) {
	    while ((negative.Size() > 0)&& (lt = negs[pos]->Iterate()))
	      if (!discard && lt->Test())
		discard = 1;
	  }
	} else
	  discard = 1; // binding failed
	
	if (!discard) {
	  if ((pos == max - 1) || (max == 0)) { // deepest level

	    if (domain == GRD_RULE)
	      EmitGround(gpos, gneg,1);
	    else
	      CreateInstance(domain);
	  
	    if (max != 0)
	      clear_pos(pos);
	    else
	      break;
	  } else {
	    // not yet at deepest level, so go deeper
	    pos++;
	  }
	}else {
	  // clear the variables and stay at this level
	  clear_pos(pos);
	}
      }
    }
  }
  delete [] inds;
  for (i = 0; i < alloc_max; i++) {
    delete indices[i];
  }
  delete [] indices; 
  delete [] lts;
  delete [] values;
}


// takes a condition as its argument, and expands it. This is intended
// to work in the innermost loop and be called by EmitRule, so messing
// up with variables and var_pos is out. Returns the expanded literals
// as a list and clears the changes to global variables that it does. 

// NRGH. I should rm -rf this part of code and start over...
void Rule::GroundCondition(Literal *lt, LiteralList *lst)
{
  assert (lt->conditions || lt->condition_func) ;
  static Literal **arr = NULL;
  static InstanceIterator **iter = NULL;
  static int max_arr = 0;
  static InstanceSet *dummy = NULL;
  static int *exists_value = 0;
  int i;
    
  if (!exists_value) {
    exists_value = new int[sys_data.total_vars];
  }
  
  Weight *wt = 0;
  Literal *l = NULL;
  Instance *item = NULL;
  int discard = 0;
  int end = 0;
  Function  *fun = NULL;
  int treshold =max_tail + 1; // this ensures that we can distinguish
			      //  the variables set by this function
			      // from those set in GroundRule
  int max = 0;
  if (lt->conditions) {
    max = lt->conditions->Length();
  }
  for (i = 0; i < sys_data.total_vars; i++) {
    if (var_pos[i] >= 0)
      exists_value[i] = 1;
    else
      exists_value[i] = 0;
  }
  // allocate the literal and iterator arrays, if needed 
  if (!arr || max > max_arr) {
    max_arr = max;
    if (arr) {
      delete [] arr;
      delete [] iter;
    }
    arr = new Literal*[max_arr+1];
    iter = new InstanceIterator*[max_arr+1];
    if (!arr || !iter)
      error(SYS_ERR, "malloc error");
    for (int i = 0; i < max_arr+1; i++) {
      iter[i] = new InstanceIterator(NULL);
    }
  }

  
  lt->empty_condition = 1;

  // initialize the arrays
  int pos = 0;  
  while (lt->conditions && (l = lt->conditions->Iterate())) {
    if ((l->arity > 0) && !l->ground) {
      arr[pos] = l;
      // for now, don't try to optimize
      iter[pos]->Reset(predicates[l->pred]->atoms);
      pos++;
      for (i = 0; i < l->arity; i++) {
	if (l->args[i] == T_VARIABLE) {
	  exists_value[l->vars[i]] = 1;
	}
      }
    } else if (!l->Test()) { // unsatisfiable literal
      end = 1;
    }
  }
  if (end)
    return;
  
  max = pos;

  while (lt->condition_func && (fun = lt->condition_func->Iterate()))
    {
      if (fun->assign && exists_value[fun->args[0]->val])
	fun->RemoveAssign();
    }
  
  // add a dummy iterator if there are only condition functions 
  if (lt->conditions->Size() == 0) {
    if (!dummy) {
      dummy = new HashSet(1,1);
    }
    iter[0]->Reset(dummy);
  }
  // now do the actual grounding. Construct a new ground literal for
  // each grounded condition, and add it to 'lst'

  pos = 0; 
  while (pos >= 0) {
    if (pos < 0)
      break; // everything that needs to be done is done. 

    discard = 0;
    if (max != 0) {
      item = iter[pos]->Iterate();
    }

    if (!item && max != 0) {
      pos--;
      clear_pos(pos+treshold);
    } else {
      // bind the new variables, if any
      if (Literal::BindLiteral(arr[pos], item, pos + treshold)) {
	// all's well, so go through functions
	if ((pos == max-1) || (max == 0)) {
	  while (lt->condition_func &&
		 (fun = lt->condition_func->Iterate())) {
	    if (!discard && !fun->Test(pos+treshold))
	      discard = 1;
	  }
	}
      } else { // binding failed
	discard = 1;
      }

      if (!discard) {
	if ((pos == max-1) || (max == 0)) {
	    
	  
	  // deepest level, construct a literal
	  lt->empty_condition = 0;
	  l = lt->Instantiate();
	  if (lt->weight) {
	    wt = lt->weight->Instantiate();
	    l->weight = wt;
	  }
	  l->FindWeight();
	  if (lst)
	    lst->Insert(l);
	  clear_pos(pos + treshold);
	  if (max == 0) // only one try if no literals
	    pos--;
	} else {
	  // go deeper
	  pos++;
	}
      } else {
	// clear and stay at same level
	clear_pos(pos + treshold); 
      } 
    }
  }

}

int Rule::SortFunctions()
{
  static Graph *sort_graph = 0;
  Literal *lt = NULL;
  Function *fun = NULL;
  Variable start = -1;
  long i = 0;

  if (sorted) {
    return 1;
  } else {
    sorted = 1;
  }

  // do we have to do anything?
  if (!num_assigns && !num_conditions)
    return 1;
  
  if (!sort_graph) {
    sort_graph = new Graph;
    for (i =0; i < sys_data.total_vars; i++) {
      sort_graph->AddNode(i);
    }
  } else {
    sort_graph->ClearEdges();
  }
  // first check all functions for assigns
  while ((fun = functions.Iterate())) {
    if (fun->assign) {
      start = fun->args[0]->val;
      fun->AddGraphEdges(sort_graph, start, 1);
    }
  }
  // process the literals
  while ((lt = positive.Iterate())) {
    if (lt->special) {
      SpecialLiteral *slt = (SpecialLiteral*) lt;
      while ((fun = slt->funs.Iterate())) {
	if (fun->assign) {
	  start = fun->args[0]->val;
	  fun->AddGraphEdges(sort_graph, start, 1);
	}
      }
      while ((lt = slt->tail.Iterate())) {
	if (lt->condition_func) {
	  while ((fun = lt->condition_func->Iterate())) {
	    if (fun->assign) {
	      start = fun->args[0]->val;
	      fun->AddGraphEdges(sort_graph, start, 1);
	    }
	  }
	}
      }
    } else {
      if (lt->condition_func) {
	while ((fun = lt->condition_func->Iterate())) {
	  if (fun->assign) {
	    start = fun->args[0]->val;
	    fun->AddGraphEdges(sort_graph, start, 1);
	  }
	}
      }
    }
  }
  while ((lt = negative.Iterate())) {
    if (lt->condition_func) {
      while ((fun = lt->condition_func->Iterate())) {
	if (fun->assign) {
	  start = fun->args[0]->val;
	  fun->AddGraphEdges(sort_graph, start, 1);
	}
      }
    }
  }
  
  // Then sort the graph
  if (!sort_graph->TopologicalSort()) {
    error(USR_ERR, "%ld: The variable assigments in a rule", line_start);
    PrintRule();
    fprintf(stderr, "   form a cycle.\n");
    return 0;
  }

  // arrange all the functions
  functions.Sort(sort_graph);

  //and in the literals...
  while ((lt = negative.Iterate())) {
    if (lt->condition_func) {
      lt->condition_func->Sort(sort_graph);
    }
  }
  while ((lt = positive.Iterate())) {
    if (lt->special) {
      SpecialLiteral *slt = (SpecialLiteral*) lt;
      slt->funs.Sort(sort_graph);
      while ((lt = slt->tail.Iterate())) {
	if (lt->condition_func)
	  lt->condition_func->Sort(sort_graph);
      }
    } else if (lt->condition_func) {
      lt->condition_func->Sort(sort_graph);
    }
  }
  return 1;
}

// This checks _only_ basicrules, not any special constructions. 
RestrictType Rule::CheckRestrict(int print)
{
  long i = 0;
  Literal *lt = NULL;
  Function *fun = NULL;
  RestrictType res = RT_STRONG;
  static long *weak = NULL, *none = NULL;
  long numweak = 0, numnone = 0;

  if (!SortFunctions())
    return RT_NONE;
  
  if (!weak || !none) {
    weak = new long[sys_data.total_vars+1];
    none = new long[sys_data.total_vars+1];
    
    if (! weak || !none)
      error(SYS_ERR, "malloc error");
  }

  if (!vars) {
    vars = new Variable[sys_data.total_vars+1];
    if (!vars)
      error(SYS_ERR, "malloc error");
  }
  memset(variables, R_NONE, sizeof(Variable) * sys_data.total_vars); 
  
  head->Restrict(R_NEGATIVE_LOCAL);

  while (( lt = negative.Iterate()))
    lt->Restrict(R_NEGATIVE_LOCAL);

  while (( fun = functions.Iterate()))
    fun->Restrict(R_NEGATIVE_LOCAL);
  
  while ((lt = positive.Iterate()))
    // Miksi?
    if (lt->DomainLiteral() ) // && !lt->conditions && !lt->condition_func)
      lt->Restrict(R_DOMAIN_LOCAL);
    else
      lt->Restrict(R_POSITIVE_LOCAL);
  
  for (i = 0; i < sys_data.total_vars; i++) {
    switch (variables[i]) {
    case R_CONDITION_LOCAL:
      vars[i] = VAR_CONDITION;
      break;
    case R_FUNCTION_FAIL:
    case R_NEGATIVE_LOCAL:
    case R_NEGATIVE:
      res = RT_NONE;
      none[numnone++] = i;
      break;
    case R_POSITIVE:
    case R_POSITIVE_LOCAL:
      if (res > RT_NONE)
	res = RT_WEAK;
      weak[numweak++] = i;
      break;
    default:
      vars[i] = VAR_GLOBAL;
      break;
    }
    
  }
  if (res < RT_STRONG && print) {
    fprintf(stderr, "%s: %srestricted rule: ",
	    error_file_and_line(line_start), 
	    ( (res == RT_NONE) ? "non" : "weakly "));
    PrintRule();
    if (numnone > 0) {
      fprintf(stderr, "\tunrestricted variables: ");
      for (i = 0; i < numnone; i++)
	fprintf(stderr, "%s ", variable_table->symbols[none[i]]);
      fprintf(stderr, "\n");
    }
    if (numweak > 0) {
      fprintf(stderr, "\tweakly restricted variables: ");
      for (i = 0; i < numweak; i++)
	fprintf(stderr, "%s ", variable_table->symbols[weak[i]]);
      fprintf(stderr, "\n");
    }
  }

  // expand conditions with no globality at this point
  if (num_conditions) {
    ExpandAllConditions();
  }
  FindWeights();
  
  return res;
}

// this checks whether special rules are strongly range restricted
RestrictType Rule::CheckSpecialRestrict()
{
  int i = 0;
  Literal *lt = NULL;
  SpecialLiteral *spl = NULL;
  RestrictType res = RT_STRONG;
  Function *fun = NULL;
  static long *weak = NULL, *none = NULL, *in_conditions = NULL;
  int numnone=0, numweak=0, numcond = 0;

  if (!SortFunctions())
    return RT_NONE;
  
  if (!weak){
    weak = new long[sys_data.total_vars+1];
    none = new long[sys_data.total_vars+1];
    in_conditions = new long[sys_data.total_vars+1];
    
    if ( !weak || !none || !in_conditions)
      error(SYS_ERR, "malloc error");
  }

  memset(variables, R_NONE, sizeof(Variable) * sys_data.total_vars);
  
  // Restrict the bodies. All variables in the local bodies are treated as
  // global variables automatically.
  while ((lt = positive.Iterate())) {
    if (!lt->special) {// restrict oridinary literal
      if (lt->DomainLiteral())
	lt->Restrict(R_DOMAIN);
      else
	lt->Restrict(R_POSITIVE);
    } else { // a special literal
      spl = (SpecialLiteral*)lt;
      spl->RestrictSpecial();
    }
  }

  while ((lt = negative.Iterate())) {
    if (!lt->special) {
      lt->Restrict(R_NEGATIVE);
    } else {
      int_error("a special rule somehow ended as negative literal",
		""); 
    }
  }

  while ((fun = functions.Iterate())) {
    fun->Restrict(R_NEGATIVE);
  }

  if (type == BASICRULE) {
    head->Restrict(R_NEGATIVE_LOCAL);
  } else {
    for (i=0; i < head_number; i++) {
      head_array[i]->Restrict(R_NEGATIVE_LOCAL);


    }
  }
  
  // go through all variables and see that they are restricted
  for (i = 0; i < sys_data.total_vars; i++) {
    switch (variables[i]) {
    case R_WEIGHT_LOCAL:
    case R_WEIGHT:
      //      res = RT_NONE;
      //free[numfree++] = i;
      //break; // fall through, at leas for a while 
    case R_FUNCTION_FAIL:
    case R_NEGATIVE:
    case R_NEGATIVE_LOCAL:
      res = RT_NONE;
      none[numnone++] = i;
      break;
    case R_POSITIVE:
    case R_POSITIVE_LOCAL:
      if (res > RT_NONE)
	res = RT_WEAK;
      weak[numweak++] = i;
      break;
      /*      if (res > RT_NONE)
	res = RT_WEAK;
	in_conditions[numcond++] = i; */
      break;
    default:
      break;
    }
  }
  
  // save the variable data so that it is available when conditions are
  // expanded
  vars = new Variable[sys_data.total_vars+1];
  if (!vars)
    error(SYS_ERR, "malloc error");
  for (i = 0; i < sys_data.total_vars; i++) {
    switch (variables[i]) {
    case R_NONE:
      vars[i] = VAR_NONE;
      break;
    case R_WEIGHT_LOCAL:
    case R_NEGATIVE_LOCAL:
    case R_POSITIVE_LOCAL:
    case R_ASSIGNED_LOCAL:
    case R_DOMAIN_LOCAL:
      vars[i] = VAR_LOCAL;
      break;
    case R_CONDITION_LOCAL:
      vars[i] = VAR_CONDITION;
      break;
    default:
      vars[i] = VAR_GLOBAL;
      break;
    }
  }

  if (res < RT_STRONG) {
    sys_data.num_errors++;
    fprintf(stderr, "%s: %srestricted rule: ",
	    error_file_and_line(line_start) ,
	    ( (res == RT_NONE) ? "non" : "weakly ")); 
    PrintRule();
    if (numnone > 0) {
      fprintf(stderr, "\n\tunrestricted variables: ");
      for (i = 0; i < numnone; i++)
	fprintf(stderr, "%s ", variable_table->symbols[none[i]]);
      fprintf(stderr, "\n");
    }
    if (numweak > 0) {
      fprintf(stderr, "\tweakly restricted variables: ");
      for (i = 0; i < numweak; i++)
	fprintf(stderr, "%s ", variable_table->symbols[weak[i]]);
      fprintf(stderr, "\n");
    }
    if (numcond > 0) {
      fprintf(stderr, "\there were variables that : ");
      for (i = 0; i < numcond; i++)
	fprintf(stderr, "%s ", variable_table->symbols[in_conditions[i]]);
      fprintf(stderr, "\n");
    }
  }
  
  return res;
}
  

void Rule::PrintSpecialHead()
{
  int first = 1, i = 0;
  
  fprintf(stderr, "%ld %s ", atleast, weight_head ? "[" : "{");
  
  for (i = 0; i < head_number; i++) {
    if (head_array[i]->deleted)
      continue;
    
    if (!first)
      fprintf(stderr, ", ");
    else
      first = 0;
    head_array[i]->Print();
  }
  fprintf(stderr, " %s ", weight_head ? "]" : "}");

  if (atmost > ERROR_NUMBER)
    fprintf(stderr, "%ld ", atmost);
}

void Rule::PrintRule()
{
  int first = 1;
  Literal *lt = NULL;
  Function *ft = NULL;

  if (type == BASICRULE)
    head->Print();
  else 
    PrintSpecialHead();

  fprintf(stderr, " :- ");

  // print first special literals 
  while (special_lits && (lt = special_lits->Iterate())) {
    if (!first)
      fprintf(stderr, ", ");
    first = 0;
    lt->Print();
  } 
  
  while (( lt = positive.Iterate())) {
    if (lt->special)
      continue;
    if (!first)
      fprintf(stderr, ", ");
    first = 0;
    lt->Print();
  }
  while (( lt = negative.Iterate())) {
    if (!first)
      fprintf(stderr, ", ");
    fprintf(stderr, "not ");
    first = 0;
    lt->Print();
  }
  while ((ft = functions.Iterate())) {
    if (!first)
      fprintf(stderr, ", ");
    ft->Print();
  }
  fprintf(stderr, ".\n");
}

Variable *var_pos = NULL;

int Rule::ExpandGlobalConditions()
{
  Literal *lt = NULL;
  SpecialLiteral *slt = NULL;
  int i;

  head_conditions.Clear(1);
  negative_conditions.Clear(1);
  positive_conditions.Clear(1);
  
  // first check the heads
  if (type != BASICRULE) {
    for (i = 0; i < head_number; i++) {
      lt = head_array[i];
      if (lt->conditions || lt->condition_func) {
	GroundCondition(lt, &head_conditions);
      }
    }
  }

  // then the tails
  while ((lt = positive.Iterate())) {
    if (lt->special) {
      slt = (SpecialLiteral *) lt;
      if (slt->num_conditions) {
	slt->negative_conditions.Clear();
	slt->positive_conditions.Clear();
	while ((lt = slt->tail.Iterate())) {
	  if (lt->conditions || lt->condition_func) {
	    if (lt->negative)
	      GroundCondition(lt, &slt->negative_conditions);
	    else 
  	      GroundCondition(lt, &slt->positive_conditions);
	  }
	}
      }
    } else {
      if (lt->conditions || lt->condition_func) {
	  GroundCondition(lt, &positive_conditions);
      }
    }
  }

  // check if there are unsatisfied domain literals in
  // positive_conditions
  if ((sys_data.print_domains != PR_ALL) &&
      (sys_data.print_domains != PR_POSITIVE)) {
    while ((lt = positive_conditions.Iterate())) {
      if (lt->DomainLiteral() && !lt->Test()) {
	positive_conditions.ClearIterator();
	return 0;
      }
    }
  }
  
  while ((lt = negative.Iterate())) {
    if (lt->conditions || lt->condition_func) {
      GroundCondition(lt, &negative_conditions);
    }
  }

  // check for satisfiability
  if (sys_data.print_domains != PR_ALL) {
    while ((lt = negative_conditions.Iterate())) {
      if (lt->Test())
	return 0;
    }
  }
  return 1;
}

void Rule::CreateInstance(int dtype)
{
  if (num_conditions) {
    if (!ExpandGlobalConditions()) {
      return; // some unsatisfiable conditions
    }
  }
  head->CreateInstance(dtype);
}

void Rule::EmitGround(int gpos, int gneg, int comp )
{
  if (num_conditions) {
    if (!ExpandGlobalConditions()) {
      return; // conditions contain unsatisfiable literals
    }
  }
  
  if (!special) {
    if (sys_data.emit_text) {
      EmitText(gpos, gneg,comp);
    } else {
      switch(sys_data.output_version) {
      case 1:
	EmitSmodels1(gpos, gneg,comp);
	break;
      case 2:
	EmitSmodels2(gpos, gneg,comp);
	break;
      default:
	int_error("unknown smodels version %d", sys_data.output_version);
	break;
      }
    }
  } else {
    EmitSpecialRule( gpos,  gneg,  comp);
  }
  return;
}

void Rule::EmitSpecialRule(int gpos, int gneg, int comp)
{
  SpecialLiteral *spl = NULL;

  if (sys_data.emit_text)
    EmitText(gpos, gneg, comp);
  else 
    EmitSmodels2(gpos,gneg, comp);

  while (special_lits && (spl = (SpecialLiteral *)
			  special_lits->Iterate())) { 
    spl->EmitRule();
  }

}   
    
void Rule::EmitText(int, int, int comp)
{
  Literal *lt = NULL;
  int first = 1, i = 0;
  Instance head_atom = -1;
  DomainType status = DM_DOMAIN;

  /// check for empty generaterule
  if (type != BASICRULE) {
    int heads = head_number-deleted;
    heads += head_conditions.Size();
    heads -= num_head_conditions;

    if (heads <= 0) {
      return;
    }
  }
  
  if (type == BASICRULE) {
    head_atom = head->EmitGround();
  } else {
    if (type == GENERATERULE) 
      fprintf(sys_data.output_file, "%ld { ", atleast);
    else
      fprintf(sys_data.output_file, "{ ");
    for (i = 0; i < head_number; i++) {
      if (head_array[i]->deleted ||
	  head_array[i]->has_global_condition)
	continue;

      if (!first)
	fprintf(sys_data.output_file, ", ");
      else {
	first = 0;
      }
      head_array[i]->EmitGround();
    }
    while ((lt = head_conditions.Iterate())) {
      if (!first)
	fprintf(sys_data.output_file, ", ");
      else {
	first = 0;
      }
      lt->EmitGround();
    }
    fprintf(sys_data.output_file, " } ");
  }
  first = 1;
  while (special_lits && (lt = special_lits->Iterate())) {
    if (first) {
      fprintf(sys_data.output_file, " :- ");
      lt->EmitGround();
      first = 0;
    } else {
      fprintf(sys_data.output_file, ", ");
      lt->EmitGround();
    }
  }
  
  while ((lt = positive.Iterate())) {
    if (lt->special || lt->has_global_condition ||
	lt->empty_condition) 
      continue;
    status = predicates[lt->pred]->Status();

    if ((status != DM_INTERNAL) && 
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)
	 || (sys_data.print_domains == PR_POSITIVE)
	 || predicates[lt->pred]->NeedsToBePrinted())) {
      if (first) {
	fprintf(sys_data.output_file, " :- ");
	lt->EmitGround();
	first = 0;
      } else {
	fprintf(sys_data.output_file, ", ");
	lt->EmitGround();
      }
    }
  }

  while ((lt = positive_conditions.Iterate())) {
    status = predicates[lt->pred]->Status();

    if ((status != DM_INTERNAL) &&
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)
	 || (sys_data.print_domains == PR_POSITIVE)
	 || predicates[lt->pred]->NeedsToBePrinted())){
      if (first) {
	fprintf(sys_data.output_file, " :- ");
	lt->EmitGround();
	first = 0;
      } else {
	fprintf(sys_data.output_file, ", ");
	lt->EmitGround();
      }
    }
  }

  while ((lt = negative.Iterate())) {
    if (lt->has_global_condition || lt->empty_condition) 
      continue;
    status = predicates[lt->pred]->Status();
    if ((status != DM_INTERNAL) &&
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)
	 || predicates[lt->pred]->NeedsToBePrinted())){ 
      if (first) {
	fprintf(sys_data.output_file, " :- not ");
	lt->EmitGround();
	first = 0;
      } else {
	fprintf(sys_data.output_file, ", not ");
	lt->EmitGround();
      }
    }
  } 
  while ((lt = negative_conditions.Iterate())) {
    status = predicates[lt->pred]->Status();
    if ((status != DM_INTERNAL) &&
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)
	 || predicates[lt->pred]->NeedsToBePrinted())) {
      if (first) {
	fprintf(sys_data.output_file, " :- not ");
	lt->EmitGround();
	first = 0;
      } else {
	fprintf(sys_data.output_file, ", not ");
	lt->EmitGround();
      }
    }
  }
  fprintf(sys_data.output_file, ".\n");
}

void Rule::EmitSmodels2(int gpos, int gneg, int comp)
{
  Literal *lt = NULL;
  int numlit = 0, negs = 0, i =0 ;
  DomainType status = DM_DOMAIN;
  int heads = 0;
  
  /// check for empty generaterule
  if (type != BASICRULE) {
    heads = head_number-deleted;
    heads += head_conditions.Size();
    heads -= num_head_conditions;

    if (heads <= 0) {
      return;
    }
  }
  
  // output rule type
  fprintf(sys_data.output_file, "%d ", type);

  // count the total number of literals in the body
  switch (sys_data.print_domains) {
  case PR_ALL:
    numlit = positive.Size() + negative.Size();
    numlit += positive_conditions.Size() - num_positive_conditions;
    numlit += negative_conditions.Size() - num_negative_conditions;
    negs = negative.Size() + negative_conditions.Size() -
      num_negative_conditions; 
    break;
  case PR_POSITIVE:
    numlit = positive.Size() + negative.Size() - gneg;
    // domain predicates are already dropped from the condition lists
    numlit += positive_conditions.Size() - num_positive_conditions;
    numlit += negative_conditions.Size() - num_negative_conditions;
    negs = negative.Size() - gneg + negative_conditions.Size() -
      num_negative_conditions;
    break;
  case PR_HEADS:
  case PR_NONE:
    negs = negative.Size() - gneg + negative_conditions.Size() -
      num_negative_conditions; 
    numlit = positive.Size() -gpos + negative.Size() - gneg;
    numlit += positive_conditions.Size() - num_positive_conditions;
    numlit += negative_conditions.Size() - num_negative_conditions;
    break;
  default:
    int_error("really strange value '%d' in print_domains",
	      sys_data.print_domains);
  }
  // print the head
  if (type == BASICRULE)
    head->EmitGround();
  else {
    fprintf(sys_data.output_file, "%d ", heads);
    if (type == GENERATERULE) {
      fprintf(sys_data.output_file, "%ld ", atleast);
    }
    for (i = 0; i < head_number; i++) {
      if (head_array[i]->deleted ||
	  head_array[i]->has_global_condition ||
	  head_array[i]->empty_condition) 
	continue;
      head_array[i]->EmitGround();
    }
    while ((lt = head_conditions.Iterate())) {
      lt->EmitGround();
    }
  }
  
  // print the domain sizes 
  fprintf(sys_data.output_file, "%d ", numlit);
  if (!numlit && type == GENERATERULE) {
    fprintf(sys_data.output_file, "\n");
    return;
  }
  fprintf(sys_data.output_file, "%d ", negs);
  
  // print first negative literals, then positive
  negative.ClearIterator();
  while ((lt = negative.Iterate())) {
    if (lt->has_global_condition || lt->empty_condition)
      // skip expanded literals  
      continue;
    status = predicates[lt->pred]->Status();
    if ((status != DM_INTERNAL) &&
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)
	 || predicates[lt->pred]->NeedsToBePrinted()))
      {
	lt->EmitGround();
	fprintf(sys_data.output_file, " ");
      }
  }
  while ((lt = negative_conditions.Iterate())) {
    status = predicates[lt->pred]->Status();
    if ((status != DM_INTERNAL) &&
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)
	 || predicates[lt->pred]->NeedsToBePrinted()))
      {
	lt->EmitGround();
	fprintf(sys_data.output_file, " ");
      }
  }
  
  // and positive
  positive.ClearIterator();
  while ((lt = positive.Iterate())) {
    if (lt->has_global_condition || lt->empty_condition) 
      continue;
    if (!lt->special) {
      status = predicates[lt->pred]->Status();
    } else {
      status = DM_FALSE;
    }    
    if ((status != DM_INTERNAL) &&
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)
	 || (sys_data.print_domains == PR_POSITIVE)
	 || predicates[lt->pred]->NeedsToBePrinted()))
      {
	lt->EmitGround();
	fprintf(sys_data.output_file, " ");
      }
  }
  while ((lt = positive_conditions.Iterate())) {
    status = predicates[lt->pred]->Status();
    if ((status != DM_INTERNAL) &&
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)
	 || (sys_data.print_domains == PR_POSITIVE)
	 || predicates[lt->pred]->NeedsToBePrinted()))
      {
	lt->EmitGround();
	fprintf(sys_data.output_file, " ");
      }
  }
  
  fprintf(sys_data.output_file, "\n");
  // increase the number of outputted atoms
  sys_data.ground_rules++;
  sys_data.ground_sigma += numlit + 1;
  
}

void Rule::EmitSmodels1(int gpos, int gneg, int comp)
{
  Literal *lt = NULL;
  int negs = 0, poss = 0;
  DomainType status = DM_DOMAIN;
  //   Instance head_atom = -1, cmp = -1;

  // count the total number of literals in the body
  switch (sys_data.print_domains) {
  case PR_ALL:
    poss = positive.Size();
    negs = negative.Size();
    break;
  case PR_POSITIVE:
    poss = positive.Size(); 
    negs = negative.Size() - gneg;
    break;
  case PR_HEADS:
  case PR_NONE:
    poss = positive.Size() - gpos;
    negs = negative.Size() - gneg;
    break;
  default:
    int_error("really strange value '%d' in print_domains",
	      sys_data.print_domains);
  }
  
  // print the head
  head->EmitGround();
  
  // print the negative domain size
  fprintf(sys_data.output_file, "%d ", negs);

  // print first negative literals, then positive
  while ((lt = negative.Iterate())) {
    status = predicates[lt->pred]->Status();
    if ((status != DM_INTERNAL) &&
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)))
      {
	lt->EmitGround();
	fprintf(sys_data.output_file, " ");
      }
  }

  // and positive
  fprintf(sys_data.output_file, "%d ", poss);
    
  while ((lt = positive.Iterate())) {
    status = predicates[lt->pred]->Status();
    if ((status != DM_INTERNAL) &&
	((status != DM_DOMAIN) || (sys_data.print_domains == PR_ALL)
	 || (sys_data.print_domains == PR_POSITIVE)))
      {
	lt->EmitGround();
	fprintf(sys_data.output_file, " ");
      }
  }
  fprintf(sys_data.output_file, "\n");

  // increase the number of outputted atoms
  sys_data.ground_rules++;
  sys_data.ground_sigma += poss + negs + 1;

}



int count_bits(long n)
{
  int res = 0;
  unsigned i = 0;
  
  for(i=0; i < sizeof(long)*CHAR_BIT; i++) {
    if (n & (1 << i))
      res++;
  }
  return res;
}

long get_subset(int pos, int k)
{
  static int subset = 0;
  int bits = (1 << pos);
  
  while(1) {
    if (++subset > bits){
      subset = 0;
      return 0;
    }
    if (count_bits(subset) == k)
      return subset;
  }
}


void Rule::EmitOrHead()
{
  int i = 0, first = 1;
  long set = 0;
  Rule *rl = NULL;
  Literal *lt = NULL;
  Literal *lt1 = NULL, *lt2 = NULL;
  int gpos = 0, gneg = 0;
  
  if (!false_lit) {
    long pred = -1;
    pred = Predicate::DefinePredicate(FALSE_NAME, 0, line_start);
    false_lit = new Literal(pred, NULL, 0, 0);
    if (!false_lit)
      error(SYS_ERR, "malloc error");
    atom_table->Insert(FALSE_NAME);
  }
  // find all 2-subsets of the head atoms
  rl = new Rule(false_lit, BASICRULE);
  
  // first add the tail to the rule
  while ((lt = positive.Iterate())) {
    rl->AddLiteral(lt);
    if (!lt->special && lt->DomainLiteral())
      gpos++;
  }
  while ((lt = negative.Iterate())) {
    rl->AddLiteral(lt);
    if (!lt->special && lt->DomainLiteral())
      gneg++;
  }
  
  while ((set = get_subset(head_number, 2))) {
    for (i = 0; i < head_number; i++) {
      if (set & (1 << i)) {
	if (first) {
	  lt1 = head_array[i];
	  rl->AddLiteral(lt1);
	  first = 0;
	} else {
	  lt2 = head_array[i];
	  rl->AddLiteral(lt2);
	  first = 1;
	}
      }
    }
    
    rl->EmitGround(gpos,gneg,0);
    rl->positive.Remove(lt1);
    rl->positive.Remove(lt2);
  }

  // then demand that at least one atom should be true
  for (i = 0; i < head_number; i++) 
    rl->AddLiteral(head_array[i]->Duplicate(1));
  
  rl->EmitGround(gpos,gneg,0);

  delete rl;
}
  
void Rule::CalculateOptimize()
{
  Literal *lt = NULL;
  static LiteralList conds;
  static FunctionList funs;
  static Variable *free_vars = NULL;
  int free = 0;
  int i = 0;
  Weight *wt = NULL;
  long weight = 0;
  char *buf  = NULL;

  
  vars =  new Variable[sys_data.total_vars+1];
  if (!vars)
    error(SYS_ERR, "malloc error");
  for (i = 0; i < sys_data.total_vars ; i++) {
    vars[i] = VAR_LOCAL;
  }

  if (!free_vars) {
    free_vars = new Variable[sys_data.total_vars+1];
    if (!free_vars)
      error(SYS_ERR, "malloc error");
  }
  
  conds.Clear();
  funs.Clear();

  while ((lt = positive.Iterate())) {
    if (lt->conditions || lt->condition_func) {
      memset(variables, R_NONE, sizeof(Variable) *
	     sys_data.total_vars);
      lt->Restrict(R_NEGATIVE_LOCAL);
      for (i=0; i < sys_data.total_vars; i++) {
	switch (variables[i]) {
	case R_NEGATIVE_LOCAL:
	case R_WEIGHT_LOCAL:
	  free_vars[free++] = i;
	  break;
	case R_CONDITION_LOCAL:
	  vars[i] = VAR_CONDITION;
	  break;
	default:
	  break;
	}
      }
      ExpandCondition(lt, &conds, &funs, 1);
      positive.Remove(lt);
    } else {
      if (!lt->ground) {
	error(USR_ERR, "%s: a non-ground literal in optimize statement:",
	      error_file_and_line(lt->lineno));
	fprintf(stderr, "\t");
	lt->Print();
	fprintf(stderr, "\n");
      }
    }
  }
  while ((lt = negative.Iterate())) {
      if (lt->conditions || lt->condition_func) {
	memset(variables, R_NONE, sizeof(Variable) *
	       sys_data.total_vars);
	lt->Restrict(R_NEGATIVE_LOCAL);
	for (i=0; i < sys_data.total_vars; i++) {
	  switch (variables[i]) {
	  case R_NEGATIVE_LOCAL:
	  case R_WEIGHT_LOCAL:
	    free_vars[free++] = i;
	    break;
	  case R_CONDITION_LOCAL:
	    vars[i] = VAR_CONDITION;
	    break;
	  default:
	    break;
	  }
	}
	ExpandCondition(lt, &conds, &funs, 1);
	negative.Remove(lt);
      } else {
	if (!lt->ground) {
	  error(USR_ERR, "%s: a non-ground literal in optimize statement:",
		error_file_and_line(lt->lineno));
	  fprintf(stderr, "\tnot ");
	  lt->Print();
	  fprintf(stderr, "\n");
	}
      }
  }
  if (free > 0) {
    error(USR_ERR, "%s: There are free variables in the %s-statement",
	  error_file_and_line(line_start), (head->pred) ? "maximize" :
	  "minimize"); 
    fprintf(stderr, "\tFree variables: ");
    for (i = 0; i < free; i++) {
      fprintf(stderr, "%s ", variable_table->symbols[free_vars[i]]);
    }
    fprintf(stderr, "\n");
    return ;
  }
  
  // add all generated literals to the rule tail
  while ((lt = conds.Iterate())) {
    if (lt->negative)
      negative.Insert(lt);
    else
      positive.Insert(lt);
  }
  
  // then print the rule
  if (sys_data.emit_text) {
    int first = 1;
    fprintf(sys_data.output_file, "%s { ", (head->pred) ? "maximize"
	    : "minimize");
    while ((lt = positive.Iterate())) {
      if (!first)
	fprintf(sys_data.output_file, ", ");
      else 
	first = 0;
      lt->EmitGround(1);
    }
    while ((lt = negative.Iterate())) {
      if (!first)
	fprintf(sys_data.output_file, ", not ");
      else {
	fprintf(sys_data.output_file, "not ");
	first = 0;
      }
      lt->EmitGround(1);
    }
    fprintf(sys_data.output_file, " }\n");
  } else { // smodels 2 output
    int inverted_negs = 0, inverted_pos = 0;

    // check for negative weights
    while ((lt = negative.Iterate())) {
      lt->FindWeight();
      weight = lt->weight->GetValue();
      if (head->pred) { // handle maximizing by inverting the sign. 
	weight = -weight;
      }
      
      if (weight < 0) {
	lt->weight->v.val = -weight;
	lt->weight->type = WT_NUMBER;
	lt->inverted = 1;
	inverted_negs++;
      } else {
	lt->weight->v.val = weight;
	lt->weight->type = WT_NUMBER;
      }
    }
    while ((lt = positive.Iterate())) {
      lt->FindWeight();
      weight = lt->weight->GetValue();
      if (head->pred) { // maximize
	weight = -weight;
      }
      if (weight < 0) {
	lt->weight->v.val = -weight;
	lt->weight->type = WT_NUMBER;
	lt->inverted = 1;
	inverted_pos++;
      } else {
	lt->weight->v.val = weight;
	lt->weight->type = WT_NUMBER;
      }
    }
    fprintf(sys_data.output_file, "%d 0 %ld %ld ", type, 
	    positive.Size() + negative.Size(),
	    negative.Size() + inverted_pos - inverted_negs );

    // FIXME: get some sense to this code. 
    
    // the actual atoms
    while ((lt = negative.Iterate())) {
      if (lt->inverted)
	continue;
      buf = get_ground_instance(lt->cons, lt->pred, lt->arity);
      fprintf(sys_data.output_file,"%ld ", atom_table->Insert(buf)+1);
    }
    while ((lt = positive.Iterate())) {
      if (lt->inverted) {
	buf = get_ground_instance(lt->cons, lt->pred, lt->arity);
	fprintf(sys_data.output_file,"%ld ",
		atom_table->Insert(buf)+1);
      }
    }
    while ((lt = positive.Iterate())) {
      if (!lt->inverted) {
	buf = get_ground_instance(lt->cons, lt->pred, lt->arity);
	fprintf(sys_data.output_file,"%ld ",
		atom_table->Insert(buf)+1);
      }
    }
    while ((lt = negative.Iterate())) {
      if (!lt->inverted)
	continue;
      buf = lt->GetPrintString();
      fprintf(sys_data.output_file,"%ld ", atom_table->Insert(buf)+1);
    }

    // then weights
    while ((lt = negative.Iterate())) {
      if (lt->inverted)
	continue;
      lt->FindWeight();
      wt = lt->weight;
      
      if (wt->type != WT_NUMBER)
	int_error("all weights should be numbers by this point",
		  "");
      else
	weight = wt->v.val;
      fprintf(sys_data.output_file,"%ld ", weight);
    }
    while ((lt = positive.Iterate())) {
      if (!lt->inverted)
	continue;
      if (!lt->weight) {
	lt->FindWeight();
      }
      wt = lt->WeightDefined();
      
      if (wt)
	weight = wt->GetValue();
      else
	weight = sys_data.default_weight;
      
      fprintf(sys_data.output_file,"%ld ", weight);
    }
    while ((lt = positive.Iterate())) {
      if (lt->inverted)
	continue;
      if (!lt->weight) {
	lt->FindWeight();
      }
      wt = lt->WeightDefined();
      
      if (wt)
	weight = wt->GetValue();
      else
	weight = sys_data.default_weight;
      
      fprintf(sys_data.output_file,"%ld ", weight);
    }
    while ((lt = negative.Iterate())) {
      if (!lt->inverted)
	continue;
      if (lt->weight)
	wt = lt->weight;
      else
	wt = lt->WeightDefined();
      if (!wt)
	weight = sys_data.default_weight;
      else if (wt->type != WT_NUMBER)
	int_error("all weights should be numbers by this point",
		  "");
      else
	weight = wt->v.val;
      fprintf(sys_data.output_file,"%ld ", weight);
    }    
    fprintf(sys_data.output_file, "\n");
  }
}


void Rule::CalculateCompute()
{
  Literal *lt = NULL;
  static LiteralList conds;
  static FunctionList funs;
  static Variable *free_vars = NULL;
  int free = 0;
  int i = 0;
  
  vars =  new Variable[sys_data.total_vars+1];
  if (!vars)
    error(SYS_ERR, "malloc error");
  for (i = 0; i < sys_data.total_vars ; i++) {
    vars[i] = VAR_LOCAL;
  }

  if (!free_vars) {
    free_vars = new Variable[sys_data.total_vars+1];
    if (!free_vars)
      error(SYS_ERR, "malloc error");
  }
  
  conds.Clear();
  funs.Clear();

  while ((lt = positive.Iterate())) {
    if (lt->conditions || lt->condition_func) {
      memset(variables, R_NONE, sizeof(Variable) *
	     sys_data.total_vars);
      lt->Restrict(R_NEGATIVE_LOCAL);
      for (i=0; i < sys_data.total_vars; i++) {
	switch (variables[i]) {
	case R_NEGATIVE_LOCAL:
	case R_WEIGHT_LOCAL:
	  free_vars[free++] = i;
	  break;
	case R_CONDITION_LOCAL:
	  vars[i] = VAR_CONDITION;
	  break;
	default:
	  break;
	}
      }
      ExpandCondition(lt, &conds, &funs, 0);
      positive.Remove(lt);
    } else {
      if (!lt->ground) {
	error(USR_ERR, "%s: a non-ground literal in compute statement:",
	      error_file_and_line(lt->lineno));
	fprintf(stderr, "\t");
	lt->Print();
	fprintf(stderr, "\n");
      }
    }
  }
  while ((lt = negative.Iterate())) {
      if (lt->conditions || lt->condition_func) {
	memset(variables, R_NONE, sizeof(Variable) *
	       sys_data.total_vars);
	lt->Restrict(R_NEGATIVE_LOCAL);
	for (i=0; i < sys_data.total_vars; i++) {
	  switch (variables[i]) {
	  case R_NEGATIVE_LOCAL:
	  case R_WEIGHT_LOCAL:
	    free_vars[free++] = i;
	    break;
	  case R_CONDITION_LOCAL:
	    vars[i] = VAR_CONDITION;
	    break;
	  default:
	    break;
	  }
	}
	ExpandCondition(lt, &conds, &funs, 0);
	negative.Remove(lt);
      } else {
	if (!lt->ground) {
	  error(USR_ERR, "%s: a non-ground literal in compute statement:",
		error_file_and_line(lt->lineno));
	  fprintf(sys_data.output_file, "\tnot ");
	  lt->Print();
	}
      }
  }
  if (free > 0) {
    error(USR_ERR, "%s: There are free variables in the compute "
	  "statement", error_file_and_line(line_start));
    fprintf(stderr, "\tFree variables: ");
    for (i = 0; i < free; i++) {
      fprintf(stderr, "%s ", variable_table->symbols[free_vars[i]]);
    }
    fprintf(stderr, "\n");
    return ;
  }
  
  // add all generated literals to the rule tail
  while ((lt = conds.Iterate())) {
    if (lt->negative)
      negative.Insert(lt);
    else
      positive.Insert(lt);
  }

}

// makes all variables global variables by adding new literal to
// normal body for each 
void Rule::RemoveLocalVars()
{
  Predicate *pr = NULL;
  long pred = -1;
  Rule *rl = NULL;
  Literal *lt = NULL, *head = NULL, *lt2 = NULL;
  SpecialLiteral *slt = NULL;
  Literal **new_heads = NULL;
  int i = 0, pos = 0, hd = 0;

  if (ground || !special_lits)
    return;
  
  new_heads = new Literal*[sys_data.total_vars];
  memset(variables, R_NONE, sizeof(Variable) * sys_data.total_vars);
  for (i = 0; i < sys_data.total_vars; i++)
    new_heads[i] = NULL;

  while ((slt = (SpecialLiteral*) special_lits->Iterate())) {
    if (slt->arity > 0) {
      while ((lt = slt->tail.Iterate())) {
	if (lt->DomainLiteral()) {
	  for (i = 0; i < lt->arity; i++) {
	    if ((lt->args[i] == T_VARIABLE) &&
		vars[lt->vars[i]] == VAR_LOCAL) {
	      // a local variable found. construct a new literal for
	      // it if necessary, and make a rule for it with this
	      // literal.
	      if (variables[lt->vars[i]] < 0) {
		variables[lt->vars[i]] = pos;
		pred = Predicate::DefineSystemPredicate();
		pr = predicates[pred];
		pr->SetArity(1);
		head = new Literal(lt->vars[i], pred);
		new_heads[pos] = head;
		rl = new Rule(new_heads[pos], BASICRULE);
		pos++;
	      } else {
		hd = variables[lt->vars[i]];
		pr = predicates[new_heads[hd]->pred];
		rl = new Rule(new_heads[hd], BASICRULE);
	      }
	      lt2 = lt->Duplicate(0);
	      lt2->negative = 0;
	      lt2->weight = NULL;
	      rl->AddLiteral(lt2);
	      pr->AddRule(rl);
	    }
	  }
	}
      }
    }
  }

  for (i = 0; i < pos; i++) {
    if (new_heads[i]) {
      predicates[new_heads[i]->pred]->CalculateDomain();
      AddLiteral(new_heads[i]);
    }
  }
  delete [] new_heads;
}

	  
void Rule::FindWeights()
{
  if (weight_head) {
    for (int i = 0; i < head_number; i++) {
      // define weights
      head_array[i]->FindWeight();
      if (weight_head) {
	if (head_array[i]->weight == default_weight) {
	  warn(WARN_WEIGHT, head_array[i]->lineno,
	       "default weight used for atom '%s' in rule head",
	       head_array[i]->GetPrintString());
	}
      }
    }
  }

  if (special_lits) {
    SpecialLiteral *slt = NULL;
    while ((slt = (SpecialLiteral*)special_lits->Iterate())) {
      if (slt->type == WEIGHTRULE) {
	slt->FindWeights();
      }
    }
  }
	
}

Rule *Rule::CreateComplement()
{
  Rule *new_rule = NULL;
  Literal *new_head = NULL;
  Literal *new_lit = NULL;
  

  if (type == BASICRULE) {
    new_head = head->CreateComplement();
    new_rule = new Rule(new_head, BASICRULE);
  } else {
    new_rule = new Rule(type, head_number);
  }
  if (!new_rule)
    error(SYS_ERR, "malloc error");
  
  new_rule->head_number = head_number;
  new_rule->atleast = atleast;
  new_rule->atmost = atmost;
  new_rule->line_start = line_start;
  new_rule->line_end = line_end;
  new_rule->weight_head = weight_head;
  new_rule->ground = ground;
  new_rule->delete_all = delete_all;
  new_rule->deleted = deleted;
  new_rule->num_weights = num_weights;
  new_rule->num_assigns = num_assigns;
  new_rule->num_conditions = num_conditions;
  new_rule->num_positive_conditions = num_positive_conditions;
  new_rule->status = status;
  new_rule->special = special;
  
  if(type == CHOICERULE) {
    for (int i = 0 ; i < head_number; i++) {
      new_rule->head_array[i] = head_array[i]->CreateComplement();
    }
  }
  
  Literal *lt = NULL;
  while ((lt = positive.Iterate())) {
    new_lit = lt->CreateComplement();
    new_rule->AddLiteral(new_lit);
  }

  while ((lt = negative.Iterate())) {
    new_lit = lt->Duplicate(0);
    lt->pred = predicates[lt->pred]->complement;
    new_rule->AddLiteral(new_lit);
  }

  Function *fun = NULL;
  while ((fun = functions.Iterate())) {
    new_rule->AddFunction(fun->DuplicateFunction());
  }
  return new_rule;
}
