/*********************** Graphplan **************************************/


/***** planner.c: do the planning part.*********************************/


/* removing subset code, greedy code, constrict code */  

#include "tgraphplan.h"
#include <sys/time.h>

extern hashtable_t *fact_table, *op_table;  /* the arrays of hash tables */
extern int do_subsets, do_minexp, do_completeness,do_remove, do_heuristic_value, print_horizon, debugging_mode, do_aggressive, print_path, do_weirdvals;
extern fact_list the_goals;
extern struct timeval start;


/*stuff for completeness */
extern int same_as_prev_flag, first_full_time, num_hashes[2];

/* for heuristic value */
extern int *value_of_null;

void insert(goal_arr a, int num, int time, double prob);
int setup_forward(int maxtime);
void insert_forward(fact_arr a, vertex_t op, int num, int time, double prob);
hash_entry lookup_forward(fact_arr a, int num, int time);

double runbasicplan(int numgoals, int time);

int hash_inserts = 0, hash_hits = 0, hash_ctr = 0,
  hash_numctr = 0, subset_hits = 0;

int number_of_actions_tried = 0;
int number_of_state_steps = 0;
int linenumber = 0;
int global_maxtime;  /* global variable containing maxtime */
extern int num_goals;  /* how many goals there are */
extern int global_needed_vec; /* vector showing needed goals */

double min_depth_cutoff;
double min_width_cutoff;
double best_plan_prob;

/*****use for storing current facts and ops*****/
fact_arr *facts_true_at = NULL;
goal_arr *goals_at = NULL;
op_arr *ops_at = NULL;

/*** hash table stuff ***/
set_table the_table, the_forward_table;

plan_table the_plan_table;

double basic_forward_plan(int numfacts, int time, int maxtime);

int setup(int maxtime);
double basicplan(int cindex, int nindex, int time, double current_prob);
double runbasicplan(int numgoals, int time);
void mark_exclusive(vertex_t v);
void unmark_exclusive(vertex_t v);
void print_arcplan(int maxtime);
inline void remove_plan(plan_entry prev, plan_entry targ);

vertex_t op_stack[MAXMAXNODES];
incl_list incl_stack[MAXMAXNODES];
int op_count[MAXMAXNODES];
int stack_point=0;
int num_facts = 0;

double do_arcplan(int maxtime, double depth_cutoff, double width_cutoff)
{
  int num_goals, i;
  double result;
  num_goals = setup(maxtime);
  num_facts = setup_forward(maxtime);
  global_maxtime = maxtime;
  printf("goals at time %d:\n  ",maxtime+1);
  for(i=0; i < num_goals; ++i) printf("%s ",goals_at[maxtime][i]->name);
  printf("\n\n");
  min_depth_cutoff=depth_cutoff;
  min_width_cutoff=width_cutoff;
  result = runbasicplan(num_goals, maxtime);
  print_plan(maxtime, result, num_facts);
  return result;
}

/* Print out info.  The best match to a "state-space step" in something like
   Prodigy is somewhere between the number of set-creation steps and the
   number of actions tried. */
void print_arcplan(int maxtime)
{
  int i;
  vertex_t v;
  for(i=0; i < maxtime; ++i) {
    get_next(op_table[i],0);
    while((v = get_next(op_table[i],1)) != NULL) {
      if (v->used && !IS_NOOP(v)) printf("%d %s\n",i+1,v->name);
    }
  }
  printf("%d entries in hash table, ",hash_inserts);
  if (hash_inserts == 0) printf("\n");
  else printf("%d hash hits, avg set size %d.\n",
	      hash_hits, hash_numctr/hash_ctr);
  printf("%d total set-creation steps (entries + hits + plan length - 1).\n",
	 hash_inserts + hash_hits + maxtime - 1);
  printf("%d actions tried\n", number_of_actions_tried);
}

/******Routines for checking proactively if we can stop already **********/

/* when you make a decision, immediately
   mark each op/noop as not doable, by doing MARK(v) (which adds 1 to
   v->cant_do).  Test by doing IS_MARKED(v).
   */
#define MARK(v) ((v)->cant_do += 1)
#define UNMARK(v) ((v)->cant_do -= 1)
#define IS_MARKED(v) ((v)->cant_do > 0)

void mark_exclusive(vertex_t v)
{
  edgelist_t e;
  for(e = v->exclusive; e; e = e->next) MARK(e->endpt);
}

void unmark_exclusive(vertex_t v)
{
  edgelist_t e;
  for(e = v->exclusive; e; e = e->next) UNMARK(e->endpt);
}

/**** see if remaining goals still possible **/
int goals_still_possible(int index, int time)
{
  int i;
  edgelist_t e;
  for(i=0; i < index; ++i) {
    for(e = goals_at[time][i]->in_edges;e && IS_MARKED(e->endpt);e = e->next);
    if (e == NULL) return 0;  /* all were marked */
  }
  return 1;
}

/* helper for setting up */
double runbasicplan(int numgoals, int time)
{
  int i;
  double res;
  for(i=0; i < numgoals; ++i) goals_at[time][i]->used = 1;
  if ((res=basicplan(numgoals-1, 0, time, 1.0))>0.0)
    {
      if (res<min_width_cutoff)
	printf("failed to find plan with probability %f. There exists a plan with probability %f.\n",min_width_cutoff,res);
    }
  for(i=0; i < numgoals; ++i) goals_at[time][i]->used = 0;
  return res;
}

/***********End of checking routines************************************/

void stack_push(vertex_t op, incl_list incl, int time)
{
  if (stack_point<MAXMAXNODES) {
    if (!op->is_noop) {
      op_stack[stack_point]=op;
      incl_stack[stack_point]=incl;
      op_count[time]++;
      stack_point++;
    }
  }
  else
    do_error ("Stack to small.\n");
}

void stack_pop(vertex_t op, int time)
{
  if (!op->is_noop){
    stack_point--;
    op_count[time]--;
  }
}

void add_arc_to_plan(double arc_prob)
{
  int i,j,k,num_newfacts=num_facts,time,jbreak,kbreak,old_numfacts;
  plan_entry pelt;
  plan_entry prev;
  
  time=0;
  for (i=stack_point - op_count[time];i>0; time++, i-=op_count[time]){
    pelt = lookup_plan_entry(facts_true_at[time], num_newfacts, time, &prev);
    if (pelt!=NULL) 
      for (j=0,jbreak=0;j<pelt->num_ops && !jbreak;j++){
	for (k=0,kbreak=0;k<op_count[time] && !kbreak;k++)
	  if (pelt->op_set[j]==op_stack[i])
	    kbreak=1;
	if (kbreak==0) jbreak=1;
      }
    
    if (pelt == NULL || (arc_prob> pelt->util && jbreak==0)) {
      if (pelt != NULL) 
	remove_plan(pelt,prev);
      insert_plan(facts_true_at[time], &(op_stack[i]), op_count[time], num_newfacts, time, arc_prob);
    }
    old_numfacts = num_newfacts;
    num_newfacts = 
      apply_incl_of_op(&(op_stack[i]), op_count[time], time, &(incl_stack[i]), num_newfacts);
    if (time>0)
      unapply_incl_of_op(time-1,old_numfacts);
  }
}

/*********** THIS IS THE MAIN RECURSIVE BACKWARD CHAINING SEARCH **********
   This is a recursive planner.  Two interesting things it does are 
   (1) use the mutual-exclusivity relations, and (2) go layer by layer.

   Idea: Given an array of goals at some time, for each way of generating
   these goals from facts in previous time step that doesn't violate
   exclusivity constraints, try running recursively.

   Returns 1 if doable and 0 if not.

   Arguments:
    goal_index tells us the index of the Current goal we're working on.
    subgoal_index is the next free index at the previous level(the one we're creating)
      We use this to put preconditions of our ops into.
    time is the current time.

   This examines the goals one by one (starting with the one of highest
   index in the goals_at[time] array) and for each, looks at all ways of
   creating it (that aren't exclusive of previous commitments).  For each 
   such way, we first check to see if some future goal has been "cut off",
   and if not, we put the preconditions into goals_at[time-1][subgoal_index].

   Note: NOOP is the first one in list of ways to make a goal true. 
   This routine relies on the exclusivity relations between ops (and noops) 
   being in the graph.  I.e., it does no other (redundant) checking to 
   make sure plan is LEGAL.
   
   The planner is proactive in the sense that it checks to see if remaining 
   goals in the list are still possible. (this is what is meant above by 
   "cut off")
   
   We tried using the heuristic of having the next goal be the
   one with the fewest ways of making it true (rather than the next one
   in the list) but it didn't generally help much.
   */
/*
  current_prob is the probability that if you are at the current state, you 
  can reach the goal state.
  */
double basicplan(int goal_index, int subgoal_index, int time, 
		 double current_prob)
{
  int i, was_used;
  double result,ret=0.0;
  vertex_t v, op;
  edgelist_t e, edge2;
  hash_entry elt;

  if (time <= 0) {
    if (current_prob>0) 
      add_arc_to_plan(current_prob);
    if (best_plan_prob<current_prob){
      best_plan_prob=current_prob;
      printf("Found a succesful plan with probability %f\n",current_prob);
    }
    return 1.0;  /* we're done */
  }
  if (current_prob<min_depth_cutoff)
    return 0.0;/*the probability of this solution succeeding is too small
	       to consider this path any further*/
  if (goal_index < 0) { /* go on to the previous time step */
    if (DEBUG_FLAG) {
      printf("goals at time %d:\n  ",time);
      for(i=0; i < subgoal_index; ++i) printf("%s ",goals_at[time-1][i]->name);
      printf("\n\n");
    }
    if ((elt = lookup_forward(goals_at[time-1], subgoal_index, time-1)) != NULL) 
      return elt->util;
    
    result = basicplan(subgoal_index-1, 0, time-1, current_prob);
    insert(goals_at[time-1], subgoal_index, time-1, result);
    return result;
  }

  /* Let v be the next goal, skipping over any that may just by luck have
     been made true by the operators we've already committed to */
  while((v = goals_at[time][goal_index])->is_true) {
    --goal_index;
    if (goal_index < 0) return basicplan(-1, subgoal_index, time, current_prob);
  }

  /* Try the ops and noops */
  for(e = v->in_edges; e; e = e->next) {
    op = e->endpt;
    if (IS_MARKED(op)) continue; /* exclusive of something used*/

    /* ok, do it. */
    mark_exclusive(op);
    /* check to see if causes problems for future goals */
    if (!goals_still_possible(goal_index,time)) {
      unmark_exclusive(op);
      continue;
    }
    op->used++;
    stack_push(op,e->incl,time-1);
    current_prob*=get_prob(e->incl);

    /* THIS SORT-OF CORRESPONDS TO STATE-SPACE STEPS */
    if (!IS_NOOP(op)) ++number_of_actions_tried;

    for(edge2 = op->in_edges; edge2; edge2 = edge2->next) {
      was_used = edge2->endpt->used++;
      if (!was_used) goals_at[time-1][subgoal_index++] = edge2->endpt;
      if (subgoal_index > MAXGOALS) do_error("MAXGOALS too small");
    }
    for(edge2 = op->out_edges; edge2; edge2 = edge2->next)
      if (!are_prob_mutex(edge2,e))
	++edge2->endpt->is_true;/*only want to make things true compatible with
				  the possibility chosen.*/
    /*Currently, I assume that there are no seperate probabilistic exclusion
      sets.  In other words, I assume that it is necessary to only deal with
      one probability distribution for any given operator. */

    /* now recurse */
    if (DEBUG_FLAG > 1) {
      if (IS_NOOP(op)) printf("trying %s from prev time step.\n",v->name);
      else printf("Trying %d %s to achieve %s.\n",time, op->name, v->name);
    }
    result = basicplan(goal_index-1, subgoal_index, time,current_prob) * get_prob(e->incl);
    
    /* undo */
    for(edge2 = op->out_edges; edge2; edge2 = edge2->next)
      if (!are_prob_mutex(edge2,e))
	  --edge2->endpt->is_true;
    for(edge2 = op->in_edges; edge2; edge2 = edge2->next) {
      was_used = --edge2->endpt->used;
      if (!was_used) goals_at[time-1][--subgoal_index] = NULL;
    }
    unmark_exclusive(op);
    if (result>min_width_cutoff) 
      return result;   /* DONE */
    
    op->used--;
    stack_pop(op,time-1);

    if (result > min_width_cutoff) return result;                   /* DONE */
    else if (ret<result) 
      ret=result;
  }
  /* otherwise, return 0 */
  return ret;
}

/******************** set up ****************************/
void handle_events(void);
void do_graph_created(void);

/* set up goal array "goals_at" and mark the initial facts as "is_true".
   Return number of goals.  Note: possibly might be some nodes in 
   fact_table[maxtime] that aren't goals.  
 */
int setup(int maxtime)
{
  vertex_t v;
  fact_list temp;
  char str[100];
  int i = 0;
  /* set up initial facts */
  get_next(fact_table[0],0);
  while((v = get_next(fact_table[0],1)) != NULL) v->is_true = 1;

  /* set up goal array */
  if (goals_at != NULL) free(goals_at);
  goals_at = (goal_arr *) calloc(maxtime+1,sizeof(goal_arr));
  for(temp=the_goals; temp; temp = temp->next) {
    instantiate_into_string(temp->item, NULL, str,1);
    if ((v = lookup_from_table(fact_table[maxtime],str)) == NULL) 
      do_error("goal not found in output");
    goals_at[maxtime][i++] = v;
    if (i > MAXGOALS) do_error("MAXGOALS too small.");
  }
  return i;
}







/* return the value of a given set of facts at a given time.  The "given
   facts" are those in facts_true_at[time].  The value is utilities[time]
   if the goals are solved, and util_of_failing if not  */

double value_of(int numfacts, int time)
{
  vertex_t v;
  fact_list temp;
  int i;
  static int flag = 0, ngoals;
  static char str[MAXGOALS][100];

  if (flag == 0) {    /* set up str */
    flag = 1;
    for(ngoals=0, temp=the_goals; temp; ngoals++, temp = temp->next) {
      instantiate_into_string(temp->item, NULL, str[ngoals],1);
    }
  }
  for(i=0; i < ngoals; ++i) {
    if ((v = lookup_from_table(fact_table[time],str[i])) == NULL) 
      return util_of_failing;
    if (! v->is_true) return util_of_failing;
  }
  return utilities[time];
}

int rec_find_pairwise_value(vertex_t arr[], int len, int time);

/* sum up the heuristic values of the nodes in this set, and add in
   value_of_null[time]. If we are keeping track of pairwise values,
   then do the slower pairwise method */
int heuristic_value_of(int numfacts, int time)
{
  int value = value_of_null[time];
  int i;
  if (do_heuristic_value > 1) 
    value += rec_find_pairwise_value(facts_true_at[time], numfacts, time);
  else for(i=0; i < numfacts; ++i) 
    value += facts_true_at[time][i]->heuristic_value;
  return value;
}

/* see if all goals are represented */
int not_all_goals_represented(int numfacts, int time)
{
  unsigned int vec = 0;
  int i;
  for(i=0; i < numfacts; ++i) 
    vec |= facts_true_at[time][i]->needed_vec;
  if ((vec & global_needed_vec) != global_needed_vec) return 1;
  return 0;
}


/* uses the "requirement" field to remove vertices in facts_true_at
   that are irrelevant.  Returns the new numfacts.  */
int remove_unsupported_vertices(int numfacts, int time)
{
  int i;
  edgelist_t e;
  for(i=0; i < numfacts; ++i) {
    for(e = facts_true_at[time][i]->requirements; e; e = e->next) {
      if (!e->endpt->is_true) {  /* can get rid of ith elt of array */
	facts_true_at[time][i]->is_true = 0;
	facts_true_at[time][i] = facts_true_at[time][numfacts-1];
	--numfacts;
	i = 0;  /* start over */
	break;
      }
    }
  }
  return numfacts;
}

/* print line number and blank spaces in front of line */
void printlinenumber (int time)
{
  int i;

  printf("%d",linenumber);
  if (linenumber < 10) printf("  ");
  else if (linenumber < 100) printf(" ");

  for(i=0; i < time; ++i) printf(" ");

  linenumber++;
}

/*apply a particular inclusion of an operator and return the number of new facts.*/
int apply_incl_of_op(vertex_t *op, int num_ops, int time, incl_list *incl, int numfacts)
{
  int newnum,i,j,breaker;
  edgelist_t e;
  vertex_t v;

  newnum = 0;
  for(j=0;j<num_ops;j++)
    /* set up facts_true_at[time+1] */
    for(e = op[j]->out_edges; e; e = e->next) {
      if (e->incl != incl[j] || e->endpt->applied) continue;  /* a different outcome */
      if (do_remove && !e->endpt->needed) continue;/* not needed */
      facts_true_at[time+1][newnum++] = e->endpt;  /* store it */
      e->endpt->applied = 1;                       /* mark in the graph */
    }
  for(i=0; i < numfacts; ++i) {         /* copy over facts not deleted */
    v = facts_true_at[time][i]->next_time;
    if (do_remove && !v->needed) continue;       /* not needed */
    breaker=0;
    for (j=0;j<num_ops && !breaker; j++){
      for(e = op[j]->del_edges; e && !breaker; e = e->next) {
	if (e->incl != incl[j]) continue; /* a different outcome */	
	if (e->endpt == v) breaker=1;
      }
    }
    if (breaker) continue;            /* this fact WAS deleted */
    if (v->applied) continue;           /* this fact is already listed */
    
    facts_true_at[time+1][newnum++] = v;         /* store it */
    v->applied = 1;                              /* mark in the graph */
  }
  if (do_remove > 1) {
    newnum = remove_unsupported_vertices(newnum, time+1);
  }
  return newnum;
}

inline void unapply_incl_of_op(int time, int newnum)
{
  int i;
  for(i=0; i < newnum; ++i) facts_true_at[time+1][i]->applied = 0;
}

/* print out the plan.  Current state in facts_true_at[time] */
void print_plan_recursive(int time, int maxtime, int numfacts)
{
  plan_entry pelt,temp;
  double result;
  edgelist_t e;
  int which, newnum, value, testvalue, i;
  incl_list incl_set[MAXOPS];

  if (time > print_horizon) return;  /* don't print if not desired */

  printlinenumber(time);

  /* base case */
  result = value_of(numfacts, time);
  if (time == maxtime || result == utilities[time]) {
    if (result == utilities[time]) printf("success\n");
    else printf("fail\n");
    return;
  }

  /* if heuristic values tell us we cannot hope to succeed, then we fail */
  if (do_heuristic_value) {
    value = heuristic_value_of(numfacts, time);
    testvalue = time;
    if (do_weirdvals) testvalue += maxtime;
    if (value < testvalue) {
      printf("fail\n");
      return;
    }
  }
  /* if not all goals represented, then we fail */
  if (do_remove)
    if (not_all_goals_represented(numfacts, time)) {
      printf("fail\n");
      return;
    }
    
  if (do_remove > 1) numfacts = remove_unsupported_vertices(numfacts,time);

  pelt = lookup_plan_entry(facts_true_at[time], numfacts, time, &temp);
  if (!pelt) {    printf("fail\n"); return;}

  /* if we've seen this state before, can just do the same thing */
  if (pelt->linenumber > 0 && !print_path) {
    printf("See line number %d\n",pelt->linenumber);
    return;
  }

  /* save the line number in case we get to same state again */
  pelt->linenumber = linenumber-1;

  printf("Do:");
  for(i=0;i<pelt->num_ops;i++)
    printf(" %s", pelt->op_set[i]->name);
  printf("\n");
  
  for (i=0;i<pelt->num_ops;i++)
    incl_set[i]=pelt->op_set[i]->inclusions;
  for(which = 0,incl_set[0] = pelt->op_set[0]->inclusions; incl_set[0]; 
      which++, incl_set[0]=incl_set[0]->next) {
    /* if more than one case, say which case it is */
    if (which > 0 || incl_set[0]->next != NULL) {
      printlinenumber(time);
      printf("case %d (%.0f %%) [ ", which, 100*incl_set[0]->prob);
    }
    
    /* set up facts_true_at[time+1] */
    newnum = apply_incl_of_op(pelt->op_set,pelt->num_ops, time, incl_set,numfacts);
    for(e = pelt->op_set[0]->out_edges; e; e = e->next) {
      if (e->incl != incl_set[0]) continue;  /* a different outcome */
	if (do_remove && !e->endpt->needed) continue;/* not needed */
	if (which > 0 || incl_set[0]->next != NULL ) printf("%s ",e->endpt->name);
    }
    if (which > 0 || incl_set[0]->next != NULL ) printf("]\n");
    if (pelt->num_ops>1) printf(" And some more ops... \n");

    print_plan_recursive(time+1, maxtime, newnum);
    
    /* clean up */
    unapply_incl_of_op(time, newnum);
  }
}

/*int srandom( unsigned seed); already defined in linuxland.*/

/* Print out the plan */
void print_plan(int maxtime, double utility, int num_facts)
{
  if (!do_minexp) {  /* utilities correspond to prob of success */
    printf("found plan with success probability %f\n\n",utility);
  } else {
    printf("found plan with -utility (expected time) %f\n\n",-utility);
  }
  if (print_horizon < 0) print_horizon = 0;
  if (print_path) {
    printf("printing random paths\n");
    srandom((unsigned) start.tv_usec);
  }
  do {
    linenumber = 0;
    print_plan_recursive(0, maxtime, num_facts);
  } while(--print_path > 0);

  printf("%d entries in hash table, ",hash_inserts);
  if (hash_ctr == 0) printf("\n");
  else printf("%d hash hits, avg set size %d.\n",
	      hash_hits, hash_numctr/hash_ctr);
    printf("%d total set-creation steps (entries + hits + plan length - 1).\n",
    hash_inserts + hash_hits + maxtime - 1);

  /* printf("%d actions tried\n", number_of_actions_tried); */
  printf("%d state steps performed\n", number_of_state_steps);
}

double get_prob(incl_list e)
{
  if (e==NULL)
    return 1;
  else
    return e->prob;
}


/*****************************************************************/
/* These are the functions for memoizing.  Idea: store in a hash table
   the sets of goals that have been seen so far.
   Since we're just doing lookup, we might as well use a big table.

   For hash table, look at rand1 values. Just add the 
   values to hash since in fact you WANT to have lists that are same 
   as sets but different as sequences to collide.  Will also store the actual
   sum, since it's likely will only have equal sums when really are same
   sets.

   NEW: store sets as bit-vectors of length MAXNODES. That way can compare
   with just a few equality or bitwise AND tests. (also need to check they
   are at the same time)

 */

/* sum instead of xor since doesn't seem to matter */
long sum_arr(goal_arr a, int num)
{
  int i;
  unsigned long sum;
  for(i=0,sum=0; i < num; i++)  sum = sum + a[i]->rand1;
  return sum;
}

/* turn a goal array into a bit vector. Assume there is space in v. */
/* NOTE: if levels are equal, then uid_*'s should be equal too */
void make_bit_vector(goal_arr a, int len, bit_vector v)
{
  int i;
  vertex_t w;
  for(i=0; i < bvec_len; ++i) v[i] = 0;  /* zero out first */
  for(i=0; i < len; ++i) {
    w = a[i];
    v[w->uid_block] = v[w->uid_block] | w->uid_mask;
  }
}

/* Find the probability of a given set of facts in the initial conditions */
double find_probability(goal_arr a, int num)
{
  printf("You shouldn't be calling this function\n");
  return 0.0;
}

inline int get_index(long sum, int num)
{
  return (int) (sum & P_HASH);
}

/* doing lookup with verification.  Other option is to hash both
   random values and to use other hash for probabilistic verification

   storing as bit vectors so can check equality easily.

   return the entry in the table, or NULL
 */
hash_entry lookup_forward(fact_arr a, int num, int time)
{
  long sum;
  static bit_vector query;
  int index, i;
  hash_entry elt;

  ++hash_ctr;  /* just for info */
  hash_numctr += num; /* just for info */
  sum = sum_arr(a, num);
  index = get_index(sum,num);
  /*  if (index < 0) index += P_HSIZE; */
  make_bit_vector(a, num, query);   /* "query" is what we actually check*/
  for(elt = the_forward_table[index]; elt; elt = elt->next) {
    if (elt->sum != sum) continue;   /* not same */
    if (elt->time != time) continue; /* not same */
    /* if get here, probably the same, but need to verify */
    for(i=0; i < bvec_len; ++i) {
      if (query[i] != elt->item[i]) break;
    }
    if (i == bvec_len) {
      if (DEBUG_FLAG > 1) printf("hash table hit.\n");
      ++hash_hits; /* just for info */
      return elt;
    }
  }
  return NULL;
}

plan_entry lookup_plan_entry(fact_arr a, int num, int time, plan_entry *temp)
{
  long sum;
  static bit_vector query;
  int index, i;
  plan_entry pelt,prev;

  sum = sum_arr(a, num);
  index = get_index(sum,num);
  /*  if (index < 0) index += P_HSIZE; */
  make_bit_vector(a, num, query);   /* "query" is what we actually check*/
  for(pelt = the_plan_table[index], prev= pelt; pelt; prev = pelt,pelt = pelt->next) {
    if (pelt->time != time) continue; /* not same */
    /* if get here, probably the same, but need to verify */
    for(i=0; i < bvec_len; ++i) {
      if (query[i] != pelt->item[i]) break;
    }
    if (i == bvec_len) {
      if (DEBUG_FLAG > 1) printf("hash table hit.\n");
      ++hash_hits; /* just for info */

      *temp = prev;
      return pelt;
    }
  }
  return NULL;
}

inline void remove_plan(plan_entry prev, plan_entry targ) 
{
  prev = targ -> next;
}

/* insert into table.  Return pointer */
hash_entry set_insert(set_table table, goal_arr a, int num, int time, 
		      double util)
{
  long sum;
  int index;
  static int mem_flag = 0;
  hash_entry elt;
  
  sum = sum_arr(a, num);
  index = get_index(sum, num);
  /* if (index < 0) index += P_HSIZE; */
  elt = (struct SET_ELT *) malloc(sizeof(struct SET_ELT));
  if (elt == NULL) { /* out of memory */
    if (mem_flag == 0)
      ++mem_flag, printf("no memory at insert %d\n",hash_inserts);
    return NULL;
  }
  elt->num = num;
  elt->sum = sum;
  elt->time = time;
  elt->util = util;
  make_bit_vector(a, num, elt->item);
  elt->next = table[index];
  table[index] = elt;
  return elt;
}

/* insert into the_forward_table */
void insert_plan(fact_arr a, vertex_t *op_set, int num_ops, int num, int time, double util)
{
  int index,i;
  plan_entry pelt;
  
  index = get_index(sum_arr(a, num),num);
  pelt = (struct FACT_ACT *) malloc(sizeof(struct FACT_ACT));
  for (i=0;i<num_ops;i++)
    pelt->op_set[i] = op_set[i];
  pelt->num_ops = num_ops;
  pelt->time = time;
  pelt->util = util;
  make_bit_vector(a, num, pelt->item);
  pelt->next = the_plan_table[index];
  the_plan_table[index] = pelt;
}

/* insert in to hash table the_table.  This is a collection of goal-sets,
   with attached probabilities. */

void insert(goal_arr a, int num, int time, double prob)
{
  set_insert(the_forward_table, a, num, time, prob);
  ++hash_inserts;  /* global: just for info */
}


/******************** set up ****************************/
void handle_events(void);
void do_graph_created(void);


/*static int hcompare(vertex_t *vp, vertex_t *wp)  redefined for linuxland.*/

static int hcompare(const void *vp, const void *wp)
{
  if ((*(vertex_t *)(vp))->heuristic_value > (*(vertex_t *)(wp))->heuristic_value) return -1;
  else if ((*(vertex_t *)(vp))->heuristic_value == (*(vertex_t *)(wp))->heuristic_value) return 0;
  else return 1;
}

/* set up fact_array facts_true_at.  Set up op_arr ops_at.
   Return number of facts */
int setup_forward(int maxtime)
{
  vertex_t v;
  int i, t, n;

  if (facts_true_at != NULL) free(facts_true_at);
  if (ops_at != NULL) free(ops_at);
  facts_true_at = (fact_arr *) calloc(maxtime+1,sizeof(fact_arr));
  ops_at = (op_arr *) calloc(maxtime,sizeof(op_arr));

  get_next(fact_table[0],0);
  for(n=0; (v = get_next(fact_table[0],1)) != NULL; n++) {
    facts_true_at[0][n] = v;
    v->is_true = 1;
  }
  for(t=0; t < maxtime; ++t) {
    get_next(op_table[t], 0);
    for(i=0; (v = get_next(op_table[t],1)) != NULL;) {
      if (v->is_noop) continue;                   /* don't include noops */
      if (do_remove && !v->needed)
	continue;                              /* don't include unneeded */
      ops_at[t][i++] = v;
    }
    if (i >= MAXOPS) do_error("too many ops. need to change MAXOPS in gp.h");
    ops_at[t][i] = NULL;  /* guarantee that last one is NULL */
    if (do_aggressive && do_heuristic_value) { /* order ops by hvalue */
      qsort(ops_at[t], i, sizeof(vertex_t), hcompare);
    }
  }
  return n;
}


