// main.cc -- for lparse
// 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 <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#ifndef CXX_HAS_NO_EXCEPTIONS
#include <exception>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifndef GLOBAL_H
#include "global.h"
#endif
#ifndef SYMBOL_H
#include "symbol.h"
#endif
#ifndef LIBRARY_H
#include "library.h"
#endif
#ifndef PARSETREE_H
#include "parsetree.h"
#endif
#ifndef GRAPH_H
#include "graph.h"
#endif
#ifndef PREDICATE_H
#include "predicate.h"
#endif
#ifndef ERROR_H
#include "error.h"
#endif
// returns the position of first input file in command line
extern int yyparse();
int process_arguments(int argc, char *argv[]);
void find_libraries();

// lnitialize_static initializes data that doesn't depend on the size
// of the program to parse, initialize_dynamic initializes the rest. 
int initialize_dynamic();
int initialize_static();
void delete_vars();

// print out the output stored in a temporary file
void print_output();
void print_compute();
void print_optimize();

int next_input_file();

// read in and print out a program that has been already grounded to
// smodels internal format
void read_grounded_program();
void register_grounded_program(char *);
char version[] = "lparse version " VERSION "\nbuild: " __DATE__ ", "
__TIME__; 

StringList grounded_programs;

int main(int argc, char *argv[])
{
  RestrictType res = RT_STRONG;
  initialize_static();
  process_arguments(argc, argv);
#if HAVE_DLFCN_H
  find_libraries();
#endif
  register_functions();
  if (sys_data.print_registered) {
    function_table->PrintRegistered();
    exit (1);
  }

    
#ifndef CXX_HAS_NO_EXCEPTIONS
  try {
#endif


    
  yyparse();

  if (sys_data.num_errors > 0) {
    fprintf(stderr, "%d error%s found. exiting ...\n",
	    sys_data.num_errors, (sys_data.num_errors > 1) ? "s" : "");
    exit(E_ERROR);
  }
  
  if (!logic_program) {
    exit(E_ERROR);
  }
  logic_program->ProcessTree();

  initialize_dynamic();

  read_grounded_program();
  
  // this is commented out because it doesn't work with special rules
  // and it is not really needed. 
  //  if (sys_data.print_rules) {
  //    Predicate::PrintAllRules();
  //  exit(1);
  // }


  dependency_graph->CheckCycles();

  if (special_rules->Size() > 0) {
    if (sys_data.output_version >= 2) {
      ParseNode::ProcessDelayed();
    } else {
      error(USR_ERR, "Extended rule types may not be used with smodels 1.");
    }
  }

  res = Predicate::CheckAllRestricts();

  switch (res) {
  case RT_NONE:
    print_warnings();
    fprintf(stderr, "\n");
    exit(E_ERROR);
  case RT_WEAK:
    print_warnings();
    fprintf(stderr, "\n");
    exit(E_RANGE);
  default:
    break;
  }

  if (sys_data.warnings & WARN_UNSAT) {
    Predicate::CheckUnsatisfiable();
  }

  print_optimize();
  if (compute->rl)
    compute->rl->CalculateCompute();


  
  if (sys_data.num_errors > 0) {
    fprintf(stderr, "%d error%s found. exiting ...\n",
	    sys_data.num_errors, (sys_data.num_errors > 1) ? "s" :
	    "");
    print_warnings();
    exit(E_ERROR);
  }

  Predicate::EmitAll();

  if (sys_data.use_regular_models &&
      (sys_data.regular_level >= REGULAR_NORMAL)) {
    Predicate::EmitComplements();
  }

  print_warnings();

  if (!sys_data.emit_text)
    print_output();

  if (sys_data.emit_text)
    print_compute();

  
#ifndef CXX_HAS_NO_EXCEPTIONS
  } catch (exception) {
    error(FATAL_ERR, "pieleen\n");
  }
#endif
#ifdef INSURE_MEM
  extern int _Insure_list_allocated_memory(void);
  _Insure_list_allocated_memory();
#endif
  

  delete_vars();
  return E_OK;
}

int process_arguments(int argc, char *argv[])
{
  int i = 0;
  char *tmp = NULL;
  char buf[BUFSIZ] = { 0 } ;
  long val = 0;
  int warning = 0;
  
  char *usage_msg = "usage: lparse [options] [input files]\n"
    "\nOptions:\n"
    "\t-1 -- output smodels 1.x format\n"
    "\t-c const=n -- define constant 'const' as number 'n'.\n"
    "\t-d -- which domain predicates should be emitted:\n"
    "\t\tall - all domain predicates are emitted\n"
    "\t\tfacts - domain predicates in rule tails are omitted\n"
    "\t\tpositive - negative domain predicates are omitted\n"
    "\t\tnone - all domain predicates are omitted.\n"
    "\t-D -- debug internal lparse data structures.\n"
    "\t-g file - read in a grounded file.\n"
    "\t-i -- disable internal functions \n"
    "\t-n number -- number of models\n"
    "\t-r [1 | 2 | 3] -- enable regular model translation (default level 2)\n"
    "\t\t1 -- omit integrity constraints\n"
    "\t\t2 -- normal regular model translation\n"
    "\t\t3 -- disallow undefined atoms (mainly for debugging)\n"
    "\t-t -- output plain text\n"
    "\t-v -- print version information\n"
    "\t-w number --- the default weight of literals\n"
    "\t-W warning -- enable warnings\n"
    "\t\tall - enable all warnings\n"
    "\t\tarity - warn if a predicate symbol has two different arities\n"
    "\t\textended - warn about general problems within extended rules\n"
    "\t\tlibrary - warn problems about user-defined functions\n"
    "\t\tsimilar - warn if similarly named constants and variables are used\n"
    "\t\tunsat - warn if there are unsatisfiable predicate symbols\n"
    "\t\tweight - warn if default weights are used\n"
    "\t\tsyntax - defines 'arity', 'extended', and 'weight'\n"
    "\t\ttypo - defines 'similar' and 'unsat'\n"
    "\t\terror - treat warnings as errors\n"
    "\t--dlp  - disjunctive semantics for '|'\n"
    "\t--partial - partial model translation\n"
    "\nThe default commandline is 'lparse -d facts -s 100 -w 1'\n\n";

  for (i=1; i < argc; i++) {
    tmp = argv[i];
    if (*tmp != '-')  // start of input files
      break;
    tmp++;
    switch(*tmp) {
    case '1':
      sys_data.output_version = 1;
      break;
    case 'c':
      i++;
      if(!argv[i]) {
	error(FATAL_ERR, usage_msg);
      }
      strncpy(buf, argv[i], BUFSIZ);
      tmp = buf;
      while(*tmp && (*tmp != '='))
	tmp++;
      if (!*tmp)
	error(FATAL_ERR, usage_msg);
      *tmp = '\0';
      tmp++;
      if (!*tmp)
	error(FATAL_ERR, usage_msg);
      val = atol(tmp);
      if (!val)
	error(FATAL_ERR, usage_msg);
      if (function_table->Lookup(buf) >= 0)
	warn(WARN_LIBRARY, 0, "trying to redefine function '%s' as a "
	      "constant.", buf);
      numeric_constant_table->Insert(buf);
      numeric_constant_table->SetValue(buf, MAKE_NUMBER(val));
      break;
    case 'v':
      printf("%s\n", version);
      exit(0);
    case 'd':
      i++;
      tmp = argv[i];
      if (!tmp)
	error(FATAL_ERR, usage_msg);
      switch(*tmp) {
      case 'a':
      case 'A':
	sys_data.print_domains = PR_ALL;
	break;
      case 'f':
      case 'F':
	sys_data.print_domains = PR_HEADS;
	break;
      case 'n':
      case 'N':
	sys_data.print_domains = PR_NONE;
	break;
      case 'p':
      case 'P':
	sys_data.print_domains = PR_POSITIVE;
	break;
      default:
	error(FATAL_ERR, usage_msg);
      }
      break;
    case 'D':
      sys_data.force_small_domains = 1;
      break;
    case 'g':
      i++;
      tmp = argv[i];
      if (!tmp)
	error(FATAL_ERR, usage_msg);
      register_grounded_program(tmp);
      break;
    case 'i':
    case 'I':
      sys_data.internal_functions = 0;
      break;

    case 'r':
      sys_data.use_regular_models = 1;
      sys_data.allow_only_disjunctive_rules = 1;
      
      // do we want to specify the level?
      if (argv[i+1] && isdigit(*argv[i+1])) {
	i++;
	val = atol(argv[i]);
	if ((val >= REGULAR_ERROR || val <= REGULAR_NONE)) {
	  error(FATAL_ERR, usage_msg);
	} else {
	  sys_data.regular_level = (RegularLevel) val;
	}
      } else {
	sys_data.regular_level = REGULAR_NORMAL;
      }
      break;
	
    case 'n':
      i++;
      tmp = argv[i];

      if (!tmp || !isdigit(*tmp))
	error(FATAL_ERR, usage_msg);
      compute->models = strtol(tmp, NULL, 10);
      compute->command_line = 1;

      if (compute->models < 0) {
	compute->models = 0;
      }
      break;
    case 't':
    case 'T':
      sys_data.emit_text = 1;
      break;
    case 'w':
	i++;
	tmp = argv[i];
	if (!tmp || !*tmp) 
	  error(FATAL_ERR, usage_msg);
	val = atol(tmp);
	sys_data.default_weight = val;
	break;
    case 'W':
      i++;
      tmp = argv[i];
      if (!tmp)
	error(FATAL_ERR, usage_msg);
      
      warning = get_warn_from_string(tmp);

      switch (warning) {
      case WARN_NONE:
	error(FATAL_ERR, usage_msg);
	break;
      case WARN_ERROR:
	sys_data.abort_when_warn = 1;
	break;
      default:
	sys_data.warnings |= warning;
	break;
      break;
      }
      break;
    case '-':
      tmp++;
      if (!strcmp(tmp, "dlp")) {
	sys_data.dlp_semantics = 1;
	sys_data.allow_only_disjunctive_rules = 1;
	break;
      } else if (!strcmp(tmp, "partial")) {
	sys_data.use_regular_models = 1;
	sys_data.allow_only_disjunctive_rules = 1;
	sys_data.regular_level = REGULAR_NORMAL;
	break;
      } else if (!strcmp(tmp, "atom-file")) {
	i++;
	if (!argv[i]) {
	  error(FATAL_ERR, usage_msg);
	}
	sys_data.atom_output_file = argv[i];
	break;
      }
      /* fall through */
    default:
      error(FATAL_ERR, usage_msg);
    }
  }

  if (sys_data.warnings & WARN_SIMILAR) {
    warnings_table = new SymbolTable;
  }
  default_weight = new Weight;
  default_weight->v.val = sys_data.default_weight;

  if ((grounded_programs.Size() > 0) && sys_data.output_version < 2) {
    error(FATAL_ERR, "The command line option '-g' works only "
	  "with smodels 2.x output format");
  }
  if (i == argc)
    return 1;

  // store the number of input files and allocate space for names
  sys_data.num_input_files = argc -i;
  sys_data.input_files = new (char*)[argc-i];
  sys_data.file_start_lines = new long[argc-i];
  int pos = 0;

  // store the file names
  for (; i < argc; i++) {
    sys_data.input_files[pos] = strdup(argv[i]);
    sys_data.file_start_lines[pos++] = LONG_MAX;
  }
  next_input_file();
  return 1;
}

int initialize_static()
{
  debug(DBG_MAIN, 1, "initialize_static");
  sys_data.num_errors = 0;
  sys_data.emit_text = 0;
  sys_data.abort_when_warn = 0;
  sys_data.warnings = WARN_NONE;
  sys_data.internal_functions = 1;
  sys_data.print_domains = PR_HEADS;
  sys_data.print_registered = 0;
  sys_data.print_rules = 0;
  sys_data.domain_size = DOMAIN_DEFAULT_SIZE;
  sys_data.max_number = 0;
  sys_data.ground_atoms = 0;
  sys_data.ground_rules = 0;
  sys_data.ground_sigma = 0;
  sys_data.hide_all = 0;
  sys_data.output_version = 2;
  sys_data.unknown_vars = 0;
  sys_data.default_weight = DEFAULT_WEIGHT;
  sys_data.force_small_domains = 0;
  sys_data.total_vars = 0;
  sys_data.term_max_arity = 0;
  sys_data.use_regular_models = 0;
  sys_data.dlp_semantics = 0;
  sys_data.allow_only_disjunctive_rules = 0;
  sys_data.atom_output_file = 0;
  
  function_table = new FunctionTable;
  predicate_table = new SymbolTable;
  constant_table = new SymbolTable(SYMBOL_DEFAULT_SIZE, 1);
  numeric_constant_table = new SymbolTable;
  variable_table = new SymbolTable;
  atom_table = new SymbolTable(ATOM_DEFAULT_SIZE);

  warnings_table = NULL;
  
  dependency_graph = new Graph;
  compute = new ComputeStmt;
  compute->models = 1;
  // initialize the compute rule
  Literal *lt = new Literal;
  if (!lt)
    error(SYS_ERR, "malloc error");
  
  compute->rl = new Rule(lt, OPTIMIZERULE);
  if (!compute->rl)
    error(SYS_ERR, "malloc error");

  warning_list = new StringList(ORDERED_SMALL);
  
  special_rules = new ParseNodeList;
  optimize = new RuleList;
  
  
  if (!function_table || !predicate_table || !constant_table || !
      atom_table || !variable_table || ! compute || !special_rules ||
      !optimize)
    error(SYS_ERR, "malloc error");



#if DEBUG & DBG_PARSE
  extern int yydebug;
  yydebug =1;
#endif
  return 1;
}

int initialize_dynamic()
{
  int test_smodels_1 = 0; // change to 1 to debug smodels1
  debug(DBG_MAIN, 1, "initialize_dynamic");
  /*  predicate_table->CreateSymbolArray();
  variable_table->CreateSymbolArray();
  constant_table->CreateSymbolArray();
  */
  if (c_stmt)
    c_stmt->ProcessCompute(0,0);

  sys_data.total_vars = variable_table->Size() +
    sys_data.internal_variables +
    sys_data.unknown_vars;
  
  variables = new Instance[sys_data.total_vars + 1];
  var_pos = new Variable[sys_data.total_vars + 1];

  if (!var_pos || ! variables)
    error(SYS_ERR, "malloc error");

  if (sys_data.output_version > 1 || sys_data.emit_text ||
      test_smodels_1) { 
    sys_data.output_file = stdout;
  } else {
    sys_data.output_file = tmpfile();
    if (!sys_data.output_file)
      error(SYS_ERR, "cannot create temporary file");
  }
  return 1;
}

void print_output()
{
  long i, atom = 0;
  FILE *atom_file = 0;
  char buf[BUFSIZ] = { 0 };
  Literal *lt = NULL;
  // print the size information
  if (sys_data.output_version < 2) {
    printf("%ld\n", atom_table->Size());
    printf("%ld\n", sys_data.ground_rules);
    printf("%ld\n", sys_data.ground_sigma);
    
    // reset tmpfile
    rewind(sys_data.output_file);
    
    // print out the rules
    while(fgets(buf, BUFSIZ, sys_data.output_file) != NULL)
      printf(buf);
    
    fclose(sys_data.output_file);
    sys_data.output_file = stdout;
  }
  // create symbols array for grounded atoms
  //  atom_table->CreateSymbolArray();

  // print out the atoms
  if (sys_data.atom_output_file) {
    atom_file = fopen(sys_data.atom_output_file, "w");
    if (!atom_file)
      error(USR_ERR, "cannot open file '%s' for writing",
	    atom_file);
    
    // if FALSE_LIT exists, add a rule:
    //  __false :- not __false, false.
    if (false_lit) {
      atom = atom_table->Insert("__false") +1;
      printf("1 %ld 2 1 %ld %ld\n",
      	     atom, atom, atom_table->Lookup(FALSE_NAME) +1);
    }
    // do the same for all atoms in the compute statement
    while (compute->rl && (lt = compute->rl->positive.Iterate())) {
      atom = atom_table->Insert("__false") +1;
      printf("1 %ld 2 2 %ld ", atom, atom);
      lt->EmitGround();
      printf("\n");
    }
    while (compute->rl && (lt = compute->rl->negative.Iterate())) {
      atom = atom_table->Insert("__false") +1;
      printf("1 %ld 2 1 %ld ", atom, atom);
      lt->EmitGround();
      printf("\n");
    }

  } else {
      printf("0\n");
  }

  
  for (i = 0; i < atom_table->Size(); i++) {
    // check if it is an internal atom
    if ((*atom_table->symbols[i] != '_') ||
	(sys_data.output_version < 2)) {
      if (!atom_file) {
	if (sys_data.output_version >= 2)
	  printf("%ld ", i+1);
	printf("%s\n", atom_table->symbols[i]);
      } else {
	fprintf(atom_file, "%ld %s\n", i+1, atom_table->symbols[i]);
      }
    }
  }
  if (atom_file) {
    fclose(atom_file);
  }

  if (!atom_file) {
    if (sys_data.output_version >= 2)
      printf("0\n");
    // print compute stmt
    printf("B+\n");
    if (sys_data.output_version < 2)
      if (compute->rl)
	printf("%ld\n", compute->rl->positive.Size());
      else
	printf("0\n");
    
    while (compute->rl && (lt = compute->rl->positive.Iterate())) {
      lt->EmitGround();
      printf("\n");
    }
    while ((atom = compute->positive_atoms.Iterate())) {
      printf("%ld\n", atom);
    }
    
    
    if (sys_data.output_version >= 2)
      printf("0\n");
    
    printf("B-\n");
    
    if (sys_data.output_version < 2) {
      if (compute->rl)
	printf("%ld\n", compute->rl->negative.Size() + (false_lit != NULL));
      else
	printf("0\n");
    }
    while (compute->rl && (lt = compute->rl->negative.Iterate())) {
      lt->EmitGround();
      printf("\n");
    }
    while ((atom = compute->negative_atoms.Iterate())) {
      printf("%ld\n", atom);
    }
    
    if (false_lit) {
      i = atom_table->Lookup(FALSE_NAME);
      printf("%ld\n", i+1);
    }
    
    if (sys_data.output_version >= 2)
      printf("0\n");
    
    // and number of models
    printf("%ld\n", compute->models);
  }
}

void print_compute()
{
  int first = 1;
  long atom = 0;
  Literal *lt = NULL;
  if ((compute->models == 1) &&
      (compute->rl->positive.Size() == 0) &&
      (compute->rl->negative.Size() == 0) &&
      (compute->positive_atoms.Size() == 0) &&
      (compute->negative_atoms.Size() == 0)) {
    if (false_lit) 
      printf("compute 1 { not _false }.\n");
    return;
  }
  printf("compute %ld { ", compute->models);

  while ((lt = compute->rl->positive.Iterate())) {
    if (!first)
      printf(", ");
    else
      first = 0;
    lt->EmitGround();
  }

  while ((atom = compute->positive_atoms.Iterate())) {
    if (!first)
      printf(", ");
    else
      first = 0;
    printf("%s", atom_table->symbols[atom-1]);
  }
  
  while ((lt = compute->rl->negative.Iterate())) {
    if (!first)
      printf(", not ");
    else {
      first = 0;
      printf("not ");
    }
    lt->EmitGround();
  }
  while ((atom = compute->negative_atoms.Iterate())) {
    if (!first) {
      printf(", not ");
    } else {
      first = 0;
      printf("not ");
    }
    printf("%s", atom_table->symbols[atom-1]);
  }
  
  if (false_lit) {
    printf("%s not %s", first ? "": ",",FALSE_NAME);
  }
  printf(" }.\n");
}

void print_optimize()
{
  Rule *rl = NULL;

  while ((rl = optimize->Iterate())) {
    rl->CalculateOptimize();
  }
}

void delete_vars()
{
  delete numeric_constant_table;
  delete function_table;
  delete predicate_table;
  delete constant_table;
  delete variable_table;
  delete atom_table;
  delete dependency_graph;
  delete compute;
  delete [] variables;
  delete [] var_pos;
  
  delete condition_set;
  delete condition_weights;
  delete optimize;
  //  if (false_lit)
  //    delete false_lit;
}

StringList *parse_line(char *ln)
{
  char *line = strdup(ln);
  char *st = NULL;
  StringList *new_list = NULL;
  
  st = strtok(line, " =,\t\n:;");

  if (!st)
    return NULL;

  new_list = new StringList();

  if (*st == '/')
    new_list->Append(st);
  
  if (!new_list)
    error(SYS_ERR, "malloc error");

  while ((st = strtok(NULL, " =,\t\n:"))) {
    new_list->Append(st);
  }
  return new_list;
}

// find the dynamically loaded libraries
void find_libraries()
{
  StringList *paths = NULL;
  StringList *libs = NULL;
  StringList *tmp = NULL;
  LibraryNode *new_node = NULL;
  FILE *in = NULL;
  char *st = NULL;
  char *st2 = NULL;
  int pos = 0, found = 0;
  char buf[BUFSIZ] = { 0 };

  user_libraries = new LibraryList();
  if (!user_libraries)
    error(SYS_ERR, "malloc error");

  // search order for directories:
  // 	LPARSE_LIBRARY_PATH env. variable
  //	paths in .lparserc
  //	paths in LD_LIBRARY_PATH

  // LPARSE_LIBRARY_PATH:
  st = getenv(LPARSE_LIBRARY_PATH);
  if (st) {
    paths = parse_line(st);
  }

  st = getenv(LPARSE_LIBRARIES);
  if (st) {
    libs = parse_line(st);
  }

  
  // .lparserc
  st = getenv("HOME");
  pos = sprintf(buf, "%s/%s", st, RC_FILE);
  buf[pos] = '\0';
  in = fopen(buf,  "r");
  if (in) {
    while (fgets(buf, BUFSIZ, in) != NULL) {
      tmp = parse_line(buf);
      if (tmp && (buf[0] != '#')) { // weed out comments
	if (strstr(buf, LPARSE_LIBRARIES)) {
	  if (libs)
	    libs->Merge(tmp);
	  else
	    libs = tmp;
	} else if (strstr(buf, LPARSE_LIBRARY_PATH)) {
	  if (paths)
	    paths->Merge(tmp);
	  else
	    paths = tmp;
	}
      }
    }
    fclose(in);
  }

  if (!libs) // no point of continuing anymore
    return ;
  
  // LD_LIBRARY_PATH
  st = getenv("LD_LIBRARY_PATH");
  if (st) {
    tmp = parse_line(st);
    if (paths) {
      paths->Merge(tmp);
    } else
      paths = tmp;
  }

  // find the needed library files:
  while ((st = libs->Iterate())) {
    found = 0;
    paths->ClearIterator();
    while ((st2 = paths->Iterate())) {
      pos = sprintf(buf, "%s/%s", st2, st);
      buf[pos] = '\0';
      // check if the file exists
      in = fopen(buf, "r");
      if (in) {
	new_node = new LibraryNode(strdup(buf));
	if (!new_node)
	  error(SYS_ERR, "malloc error");
	fclose(in);
	found = 1;
	user_libraries->Append(new_node);
	break;
      }
    }
    if (!found) {
      warn(WARN_LIBRARY, 0, "cannot open function library %s", st);
    }
  }

}
    
// read in a program in smodels input format, print it
// supposes that if the first atom in the program is hidden, it is
// really _false. 
void read_grounded_program()
{
  char *name = 0;
  char *dummy = "__dummy";
  long pos = 1, atom = 0;
  long pred = 0;
  char *p;
  char line[BUFSIZ];
  FILE *fp;
  
  while ((name = grounded_programs.Iterate())) {
    fp = fopen(name, "r");
    if (!fp) {
      error(USR_ERR, "cannot open input file '%s'.",  name);
      return;
    }

    pos = 1;
    while (fgets(line, BUFSIZ, fp)) {
      if (!strcmp(line, "0\n")) {
	// the atoms begin, read them to the memory and stop printing
	// them out
	while (fgets(line, BUFSIZ, fp)) {
	  if (!strcmp(line, "0\n")) {
	    break;
	  }

	  atom = atol(line);
	  
	  while (pos < atom ) {
	    if (pos == 1) {
	      if (atom_table->symbols[0] &&
		  strcmp(atom_table->symbols[0], FALSE_NAME)) {
		error(FATAL_ERR,
		      "Two pre-grounded programs have "
		      " different sets of atoms:\n"
		      "\tThe input file '%s' has a "
		      "hidden atom in a position where \n"
		      "\tthe atom '%s' occurred previously",
		      name, atom_table->symbols[0]);
	      } else {
		atom_table->Insert(FALSE_NAME);
	      }
	    } else {
	      if (atom_table->symbols[pos-1] &&
		  strcmp(atom_table->symbols[pos-1], dummy)) {
		error(FATAL_ERR,
		      "Two pre-grounded programs have "
		      "different sets of atoms:\n"
		      "\tThe input file '%s' has a "
		      "hidden atom in a position where \n"
		      "\tthe atom '%s' occurred previously",
		      name, atom_table->symbols[pos-1]);
	      } else {
		atom_table->Insert(dummy);
	      }
	    }
	    pos++;
	  }
	  for (p = line; *p != ' '; p++) ;
	  p++;
	  char *q = strchr(p, '\n');
	  if (q)
	    *q = '\0';

	  pred = Predicate::decode_atom(p);
	  if (predicates[pred]->hidden) {
	    p -= 2; // this is always valid because of line format 
	    p[0] = p[1] =  '_';
	  }
	  if (atom_table->Size() >= atom) {
	    if (strcmp(p, atom_table->symbols[atom-1])) {
	      error(USR_ERR, "Two pre-grounded programs have"
		    " different sets of atoms:");
	      if (strcmp(atom_table->symbols[atom-1], dummy) &&
		  strcmp(atom_table->symbols[atom-1], FALSE_NAME)) {
		fprintf(stderr, "\tThe atom '%s' " 
			"in the input file '%s'\n"
			"\tis used in "
			"a position where '%s' occurred previously.\n" 
			, p, name, atom_table->symbols[atom-1]);
	      } else {
		fprintf(stderr, "\tThe atom '%s' "
			"in the input file '%s'\n"
			"\tis used in "
			"a position where a hidden atom occurred"
			" previously.\n",  p, name);
	      }
	      exit(1);
	    }
	  }
	  atom_table->Insert(p);
	  pos++;
	}
	// read the compute statements
	fgets(line, BUFSIZ, fp);
	while (fgets(line, BUFSIZ, fp)) {
	  if (!strcmp(line, "0\n"))
	    break;
	  atom = atol(line);
	  compute->positive_atoms.Insert(atom);
	}
	fgets(line, BUFSIZ, fp);
	while (fgets(line, BUFSIZ, fp)) {
	  if (!strcmp(line, "0\n"))
	    break;
	  atom = atol(line);
	  compute->negative_atoms.Insert(atom);
	}
	
	break;
      }
      fprintf(sys_data.output_file, "%s", line);
    }
    fclose(fp);
  }
}

void register_grounded_program(char *filename)
{
  grounded_programs.Insert(strdup(filename));
}
