/* nfa.c - functions for manipulating regexps as NFAs
 *
 ****************************************************************
 * Copyright (C) 1998, 2000, 2004 Thomas Lord
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */



#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/setjmp.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/uni/coding.h"
#include "hackerlab/rx/nfa-cache.h"
#include "hackerlab/rx/nfa.h"


/* __STDC__ prototypes for static functions */
static void rx_build_nfa_internal (struct rx_nfa *rx,
				   struct rx_exp_node *rexp,
				   struct rx_nfa_state **start,
				   struct rx_nfa_state **end,
				   jmp_buf * err_escape);
static int unfa_equal (void * va, void * vb, struct hashtree_rules * rules);
static struct hashtree * unfa_hash_alloc (struct hashtree_rules * rules);
static void unfa_free_hash (struct hashtree * tab, struct hashtree_rules * rules);
static struct hashtree_item * unfa_hash_item_alloc (void * key, struct hashtree_rules * rules);
static void unfa_free_hash_item (struct hashtree_item * item, struct hashtree_rules * rules);
static struct rx_cached_rexp * canonical_unfa (struct hashtree * table, struct rx_exp_node * rexp, int cset_size);
static struct rx_nfa * rx_unfa_to_nfa (struct rx_cached_rexp * cr,
				       struct rx_exp_node * exp,
				       int cset_size);
static void rx_free_nfa_graph (struct rx_nfa *rx);
static void nfa_set_freer (struct hashtree_item * node,
			   struct hashtree_rules * rules);
static int nfa_set_cmp (void * va, void * vb);
static int nfa_set_equal (void * va, void * vb, struct hashtree_rules * rules);
static struct hashtree * nfa_set_hash_alloc (struct hashtree_rules * rules);
static void nfa_set_free_hash (struct hashtree * tab, struct hashtree_rules * rules);
static struct hashtree_item * nfa_set_hash_item_alloc (void * key, struct hashtree_rules * rules);
static void nfa_set_free_hash_item (struct hashtree_item * item, struct hashtree_rules * rules);
static struct rx_nfa_state_set * nfa_set_cons (struct rx_nfa * rx,
					       struct hashtree * memo,
					       struct rx_nfa_state * state,
					       struct rx_nfa_state_set * set);
static struct rx_nfa_state_set * nfa_set_adjoin (struct rx_nfa * rx,
						 struct hashtree * memo,
						 struct rx_nfa_state * state,
						 struct rx_nfa_state_set * set);
static struct rx_nfa_state_set * nfa_set_union (struct rx_nfa * rx,
						struct hashtree * memo,
						struct rx_nfa_state_set * a,
						struct rx_nfa_state_set * b);
static int eclose_node (struct rx_nfa * rx,
			struct rx_nfa_state * outnode,
			struct rx_nfa_state * node);


static struct hashtree_rules nfa_set_hash_rules;
static int n_edges_allocated = 0;

/************************************************************************
 *(h0 "Non-deterministic Finite-state Automata"
 *    :includes ("rx/nfa.h"))
 *
 *
 * A regexp which is a regular expression has a natural representation
 * as a non-deterministic finite-state automata which in turn has a
 * natural representation as a graph of states and edges.
 *
 * To compare a regular expression to a string, Rx builds an NFA from
 * the expression, and then a DFA (deterministic automata) from the NFA.
 *
 * The functions and types in this chapter are concerned with representing
 * and building non-deterministic finite-state automata.
 *
 * Equal regexps (in the sense of `rx_exp_equal') must always yield
 * equivalent non-deterministic finite-state automata.  We can take
 * advantage of this to save space and time by reusing a single NFA
 * whenever two regexps are equal.
 *
 * The savings is large: not only do we avoid allocating a second NFA,
 * but more importantly, we will build only a single DFA for the two
 * regexps.  Cache space for DFA is precious and it is expensive to
 * build a DFA.
 *
 * Procedures are provided which maintain a cached mapping from
 * regexps to NFA.  Using this interface, equal regexps
 * (`rx_exp_equal') will tend to yield NFA which are equal (`==').
 */
/*(menu)
 */

/*(include-documentation "nfa.h")
 */

/************************************************************************
 *(h1 "Translating an Expression Tree to an NFA")
 *
 * Given a *regular expression* (not a *true regexp*) syntax tree (see
 * xref:"Regexp Expression Trees"), this function builds an NFA for
 * the expression.
 */

/*(c rx_build_nfa)
 * void rx_build_nfa (struct rx_nfa *rx,
 *                    struct rx_exp_node *rexp,
 *                    struct rx_nfa_state **start,
 *                    struct rx_nfa_state **end);
 *
 * Build an NFA for regular expression `rexp'.
 *
 * States and edges of the NFA are added to `rx'.
 *
 * `*start' and `*end' are filled in with the start
 * and end states of the automata.  They may be initialized
 * to 0, or to pre-existing NFA states.
 *
 * The start state returned by this function does not automatically
 * become the start state of the overall NFA and the end state
 * returned by this function does not automatically have a non-0 state
 * label (unless the expression ends with a "cut" operator).  See
 * xref:"The Last Step of Building an NFA".
 */

int
rx_build_nfa (struct rx_nfa *rx,
	      struct rx_exp_node *rexp,
	      struct rx_nfa_state **start,
	      struct rx_nfa_state **end)
{
  jmp_buf err_escape;

  if (setjmp (err_escape))
    {
      return -1;
    }
  rx_build_nfa_internal (rx, rexp, start, end, &err_escape);
  return 0;
}

static void
rx_build_nfa_internal (struct rx_nfa *rx,
		       struct rx_exp_node *rexp,
		       struct rx_nfa_state **start,
		       struct rx_nfa_state **end,
		       jmp_buf * err_escape)
{
  struct rx_nfa_edge *edge;

  if (!*start)
    {
      *start = rx_nfa_state (rx);
      if (!*start)
	longjmp (*err_escape, 1);
    }

  if (!*end)
    {
      *end = rx_nfa_state (rx);
      if (!*end)
	longjmp (*err_escape, 1);
    }

  if (!rexp)
    {
      if (!rx_nfa_edge (rx, ne_epsilon, *start, *end))
	longjmp (*err_escape, 1);
      return;
    }

  switch (rexp->type)
    {
    case r_cset:
      {
	bits cset;
	cset = bits_dup (rexp->cset);
	if (!cset)
	  longjmp (*err_escape, 1);
	edge = rx_nfa_cset_edge (rx, ne_cset, cset, *start, *end);
	if (!edge)
	  longjmp (*err_escape, 1);
	return;
      }

    case r_string:
      {
	t_uchar * str;
	size_t pos;
	size_t len;
	struct rx_nfa_state * from;
	bits cset;
	uni_iscan_fn scan;


	str = rexp->str;
	len = rexp->str_len;
	scan = uni_encoding_iscan_fn (rexp->encoding);
	pos = 0;
	from = *start;

	while (pos < len)
	  {
	    t_unicode c;
	    struct rx_nfa_state * to;

	    c = scan (str, &pos, len);
	    invariant ((c != UNI_SCAN_INPUT_TRUNCATED) && !uni_is_bogus (c));

	    if (pos == len)
	      to = *end;
	    else
	      {
		to = rx_nfa_state (rx);
		if (!to)
		  longjmp (*err_escape, 1);
	      }

	    cset = bits_alloc (rx_nfa_cache_limits (), rx->bitset_rule);
	    if (!cset)
	      longjmp (*err_escape, 1);
	    if (bits_adjoin (cset, c))
	      {
		bits_free (cset);
		longjmp (*err_escape, 1);
	      }
	    edge = rx_nfa_cset_edge (rx, ne_cset, cset, from, to);
	    if (!edge)
	      longjmp (*err_escape, 1);
	    from = to;
	  }
	return;
      }

    case r_interval:
    case r_star:
      {
	struct rx_nfa_state * star_start;
	struct rx_nfa_state * star_end;

	star_start = 0;
	star_end = 0;
	rx_build_nfa_internal (rx, rexp->left, &star_start, &star_end, err_escape);
	if (   !rx_nfa_edge (rx, ne_epsilon, star_start, star_end)
	    || !rx_nfa_edge (rx, ne_epsilon, *start, star_start)
	    || !rx_nfa_edge (rx, ne_epsilon, star_end, *end)
	    || !rx_nfa_edge (rx, ne_epsilon, star_end, star_start))
	  longjmp (*err_escape, 1);
	return;
      }

    case r_cut:
      {
	struct rx_nfa_state * cut_end;

	cut_end = rx_nfa_state (rx);
	if (!cut_end)
	  longjmp (*err_escape, 1);
	if (!rx_nfa_edge (rx, ne_epsilon, *start, cut_end))
	  longjmp (*err_escape, 1);
	cut_end->state_label = rexp->intval;
	return;
      }

    case r_parens:
      rx_build_nfa_internal (rx, rexp->left, start, end, err_escape);
      return;

    case r_right_concat:
    case r_concat:
      {
	struct rx_nfa_state *shared;

	shared = 0;
	rx_build_nfa_internal (rx, rexp->left, start, &shared, err_escape);
	rx_build_nfa_internal (rx, rexp->right, &shared, end, err_escape);
	return;
      }

    case r_alternate:
      {
	struct rx_nfa_state *ls = 0;
	struct rx_nfa_state *le = 0;
	struct rx_nfa_state *rs = 0;
	struct rx_nfa_state *re = 0;

	ls = 0;
	le = 0;
	rs = 0;
	re = 0;

	rx_build_nfa_internal (rx, rexp->left, &ls, &le, err_escape);
	rx_build_nfa_internal (rx, rexp->right, &rs, &re, err_escape);
	if (   !rx_nfa_edge (rx, ne_epsilon, *start, ls)
	    || !rx_nfa_edge (rx, ne_epsilon, *start, rs)
	    || !rx_nfa_edge (rx, ne_epsilon, le, *end)
	    || !rx_nfa_edge (rx, ne_epsilon, re, *end))
	  longjmp (*err_escape, 1);
	return;
      }

    case r_context:
      if (!rx_nfa_edge (rx, ne_epsilon, *start, *end))
	longjmp (*err_escape, 1);
      return;

    default:
      panic ("unreconized node type in rx_build_nfa_internal");
    }
}


/************************************************************************
 *(h1 "Shared NFA Functions")
 *
 */

struct rx_cached_rexp
{
  struct rx_unfa unfa;
  struct rx_cached_rexp * next;
  struct rx_cached_rexp * prev;
  struct hashtree_item * hash_item;
};

static int delayed = 0;
static struct hashtree unfa_table = { 0 };
static struct rx_cached_rexp * free_queue = 0;
static int unfa_hits = 0;
static int unfa_misses = 0;
static int unfa_saves = 0;


void
rx__nfa_cache_statistics (size_t * threshold,
			  size_t * failure_pt,
			  size_t * in_use,
			  size_t * high_water_mark,
			  int * hits,
			  int * misses,
			  int * saves)
{
  if (threshold)
    *threshold = rx_nfa_cache_threshold ();
  if (failure_pt)
    *failure_pt = rx_nfa_cache_failure_pt ();
  if (high_water_mark)
    *high_water_mark = rx_nfa_cache_high_water_mark ();
  if (in_use)
    *in_use = rx_nfa_cache_in_use ();
  if (hits)
    *hits = unfa_hits;
  if (misses)
    *misses = unfa_misses;
  if (saves)
    *saves = unfa_saves;
}




static int
unfa_equal (void * va, void * vb, struct hashtree_rules * rules)
{
  return rx_exp_equal ((struct rx_exp_node *)va, (struct rx_exp_node *)vb);
}


static struct hashtree *
unfa_hash_alloc (struct hashtree_rules * rules)
{
  return (struct hashtree *)rx_nfa_cache_soft_malloc (sizeof (struct hashtree));
}


static void
unfa_free_hash (struct hashtree * tab, struct hashtree_rules * rules)
{
  rx_nfa_cache_free ((char *)tab);
}


static struct hashtree_item *
unfa_hash_item_alloc (void * key, struct hashtree_rules * rules)
{
  struct hashtree_item * it;
  it = (struct hashtree_item *)rx_nfa_cache_soft_malloc (sizeof (*it));
  if (!it)
    return 0;
  it->key = key;
  it->binding = 0;
  return it;
}


static void
unfa_free_hash_item (struct hashtree_item * item, struct hashtree_rules * rules)
{
  rx_nfa_cache_free ((char *)item);
}


static struct hashtree_rules unfa_rules =
{
  unfa_equal,
  unfa_hash_alloc,
  unfa_free_hash,
  unfa_hash_item_alloc,
  unfa_free_hash_item
};

static struct rx_cached_rexp *
canonical_unfa (struct hashtree * table, struct rx_exp_node * rexp, int cset_size)
{
  struct hashtree_item * it;

  it = hashtree_store (table, rx_exp_hash (rexp), rexp, &unfa_rules);
  if (!it)
    return 0;

  if (it->binding == 0)
    {
      struct rx_cached_rexp * cr;

      cr = (struct rx_cached_rexp *)rx_nfa_cache_malloc (sizeof (*cr));
      if (!cr)
	{
	  hashtree_delete (it, &unfa_rules);
	  return 0;
	}
      if (it->key == (void *)rexp)
	rx_save_exp (rexp);
      mem_set0 ((char *)cr, sizeof (*cr));
      it->binding = (void *)cr;
      cr->unfa.nfa = 0;
      cr->unfa.exp = rexp;
      cr->hash_item = it;
      if (rexp)
	{
	  rexp->next_same_nfa = rexp;
	  rexp->prev_same_nfa = rexp;
	}
      rx_save_exp (rexp);
    }

  return (struct rx_cached_rexp *)it->binding;
}

static struct rx_nfa *
rx_unfa_to_nfa (struct rx_cached_rexp * cr,
		struct rx_exp_node * exp,
		int cset_size)
{
  struct rx_nfa * new_rx;

  if (cr->unfa.nfa)
    return cr->unfa.nfa;

  new_rx = rx_nfa_xalloc (cset_size);
  if (!new_rx)
    return 0;

  {
    struct rx_nfa_state * start;
    struct rx_nfa_state * end;
    start = end = 0;
    if (rx_build_nfa (new_rx, exp, &start, &end))
      {
	rx_free_nfa (new_rx);
	return 0;
      }
    end->state_label = 1L;
    rx_set_start_state (new_rx, start);
  }
  cr->unfa.nfa = new_rx;
  return new_rx;
}



/*(c rx_unfa)
 * struct rx_unfa * rx_unfa (struct rx_exp_node * exp,
 *			     int cset_size);
 *
 * Construct a unique NFA for regexp `exp'.  For equal
 * (`rx_exp_equal') arguments, an equal (`==') value is usually, but
 * not always returned.  For equal (`==') arguments, an equal (`==')
 * value is always returned.
 */
struct rx_unfa *
rx_unfa (struct rx_exp_node * exp,
	 int cset_size)
{
  struct rx_cached_rexp * cr;

  if (exp && exp->cr)
    {
      ++unfa_hits;
      cr = exp->cr;
    }
  else
    {
      cr = canonical_unfa (&unfa_table, exp, cset_size);
      if (!cr)
	return 0;
      if (exp)
	{
	  exp->cr = cr;
	  exp->next_same_nfa = cr->unfa.exp;
	  exp->prev_same_nfa = cr->unfa.exp->prev_same_nfa;
	  exp->next_same_nfa->prev_same_nfa = exp;
	  exp->prev_same_nfa->next_same_nfa = exp;
	}
      if (cr->unfa.nfa)
	++unfa_hits;
      else
	++unfa_misses;
    }

  if (cr->next)
    {
      if (free_queue == cr)
	{
	  free_queue = cr->next;
	  if (free_queue == cr)
	    free_queue = 0;
	}
      cr->next->prev = cr->prev;
      cr->prev->next = cr->next;
      cr->next = 0;
      cr->prev = 0;
      --delayed;
      ++unfa_saves;
    }
  ++cr->unfa.refs;
  if (!rx_unfa_to_nfa (cr, exp, cset_size))
    {
      rx_free_unfa (&cr->unfa);
      return 0;
    }
  return &cr->unfa;
}


/*(c rx_save_unfa)
 * void rx_save_unfa (struct rx_unfa * unfa);
 *
 * Increment the reference count for a unique NFA.
 */
void
rx_save_unfa (struct rx_unfa * unfa)
{
  ++(unfa->refs);
}



/*(c rx_free_unfa)
 * void rx_free_unfa (struct rx_unfa * unfa);
 *
 * Decrement the reference count for a unique NFA.  If the
 * reference count drops to 0, the NFA may be destroyed.
 *
 * In fact, a queue is kept of unique NFA with 0 references.  The NFA
 * on that queue can be spared death by a call to `rx_unfa'.  If the
 * queue size exceeds a fixed limit, the oldest NFA are finally
 * discarded from the queue.  See also the next function:
 * `rx_set_unfa_release_delay'.
 */
void
rx_free_unfa (struct rx_unfa * unfa)
{
  struct rx_cached_rexp * cr;

  cr = (struct rx_cached_rexp *)unfa;
  if (!cr)
    return;

  if (!--cr->unfa.refs)
    {
      if (!free_queue)
	{
	  free_queue = cr;
	  cr->next = cr->prev = cr;
	}
      else
	{
	  cr->next = free_queue;
	  cr->prev = free_queue->prev;
	  cr->next->prev = cr;
	  cr->prev->next = cr;
	}

      ++delayed;
    }
  else
    return;
}


int
rx__really_free_unfa (void)
{
  if (!delayed)
    return -1;
  else
    {
      struct rx_cached_rexp * it;

      it = free_queue;
      if (it->unfa.refs)
	panic ("freeing unfa with non-0 refs");
      free_queue = it->next;
      if (!--delayed)
	free_queue = 0;
      it->prev->next = it->next;
      it->next->prev = it->prev;
      if (it->unfa.exp)
	{
	  struct rx_exp_node * same_nfa;

	  same_nfa = it->unfa.exp;
	  same_nfa->prev_same_nfa->next_same_nfa = 0;
	  do
	    {
	      struct rx_exp_node * next;

	      next = same_nfa->next_same_nfa;
	      same_nfa->next_same_nfa = 0;
	      same_nfa->prev_same_nfa = 0;
	      same_nfa->cr = 0;
	      same_nfa = next;
	    }
	  while (same_nfa);
	}
      rx_free_exp ((struct rx_exp_node *)it->hash_item->key);
      hashtree_delete (it->hash_item, &unfa_rules);
      rx_free_nfa (it->unfa.nfa);
      rx_free_exp (it->unfa.exp);
      rx_nfa_cache_free (it);
      return 0;
    }
}



/************************************************************************
 *(h1 "Low-level NFA Functions")
 *
 * These are the low-level functions for building NFA.
 */
/*(menu)
 */

/************************************************************************
 *(h2 "Allocating and Freeing an NFA")
 *
 */

static unsigned long rx_id = 0;

/*(c rx_nfa_xalloc)
 * struct rx_nfa * rx_nfa_xalloc (int cset_size);
 *
 * Allocate and return a new non-deterministic automata.
 * Initially, the automata has no states or edges.
 *
 * This function panics if allocation fails or
 * if more than `2**(8*sizeof(unsigned long))'
 * automata have been allocated.
 *
 * The next step in creating an automata is to add
 * states and edges.  See xref:"rx_build_nfa" and
 * xref:"Building an NFA State by State".
 */
struct rx_nfa *
rx_nfa_xalloc (int cset_size)
{
  struct rx_nfa * new_rx;

  if ((cset_size != 256) && (cset_size != (1 << 21)))
    panic ("unhandled cset_size in rx_nfa_xalloc");
  new_rx = (struct rx_nfa *)rx_nfa_cache_malloc (sizeof (*new_rx));
  if (!new_rx)
    return 0;
  mem_set0 ((char *)new_rx, sizeof (*new_rx));
  new_rx->rx_id = rx_id++;
  if (rx_id == 0)
    panic ("rx id wraparound!");
  new_rx->local_cset_size = cset_size;
  switch (cset_size)
    {
    case 256:
      new_rx->bitset_rule = rx_8bit_bits_tree_rule;
      break;
    case (1 << 21):
      new_rx->bitset_rule = uni_bits_tree_rule;
      break;
    default:
      panic ("unrecognized cset size in rx_nfa_xalloc");
      break;
    }
  return new_rx;
}


static void
rx_free_nfa_graph (struct rx_nfa *rx)
{
  while (rx->nfa_states)
    {
      while (rx->nfa_states->edges)
	{
	  if (rx->nfa_states->edges->cset)
	    bits_free (rx->nfa_states->edges->cset);
	  {
	    struct rx_nfa_edge * e;
	    e = rx->nfa_states->edges;
	    rx->nfa_states->edges = rx->nfa_states->edges->next;
	    rx_nfa_cache_free (e);
	  }
	  --n_edges_allocated;
	}
      {
	struct rx_nfa_state *n;
	n = rx->nfa_states;
	if (n->superstate_set)
	  n->superstate_set->nfa_state = 0;
	rx->nfa_states = rx->nfa_states->next;
	rx_nfa_cache_free (n);
      }
    }
}


static void
nfa_set_freer (struct hashtree_item * node,
	       struct hashtree_rules * rules)
{
  rx_nfa_cache_free ((char *)node->key);
}


/*(c rx_free_nfa)
 * void rx_free_nfa (struct rx_nfa * rx);
 *
 * Release all storage associated with the NFA `rx'.
 */
void
rx_free_nfa (struct rx_nfa * rx)
{
  hashtree_free_static (&rx->set_list_memo, nfa_set_freer, &nfa_set_hash_rules);
  mem_set0 ((char *)&rx->set_list_memo, sizeof (rx->set_list_memo));
  rx_free_nfa_graph (rx);
  rx_nfa_cache_free (rx);
}


/************************************************************************
 *(h2 "Building an NFA State by State")
 *
 * It is possible to construct an NFA without building an
 * expression tree first.  These functions construct states
 * and edges.
 */


/*(c rx_nfa_state)
 * struct rx_nfa_state * rx_nfa_state (struct rx_nfa *rx);
 *
 * Allocate a new NFA state for the NFA `rx'.
 *
 * This function calls `panic' and does not return
 * if an allocation failure occurs.
 */
struct rx_nfa_state *
rx_nfa_state (struct rx_nfa *rx)
{
  struct rx_nfa_state * n;

  n = (struct rx_nfa_state *)rx_nfa_cache_malloc (sizeof (*n));
  if (!n)
    return 0;
  mem_set0 ((char *)n, sizeof (*n));
  n->id = rx->nfa_state_id++;
  n->next = rx->nfa_states;
  rx->nfa_states = n;
  return n;
}


/*(c rx_nfa_edge)
 * struct rx_nfa_edge * rx_nfa_edge (struct rx_nfa *rx,
 *                                   enum rx_nfa_etype type,
 *                                   struct rx_nfa_state *start,
 *                                   struct rx_nfa_state *dest);
 *
 *
 * Allocate a new NFA edge for the NFA `rx'.
 *
 * `type' indicates which kind of edge is being built.  If the type is
 * `ne_cset', then the `cset' field of the new edge should be set to a
 * privately allocated character set. See xref:"struct rx_nfa_edge", but
 * also see the next function, `rx_nfa_cset_edge'
 *
 * `start' and `dest' describe the source and destination states
 * of the edge.
 *
 * This function calls `panic' and does not return if an allocation
 * failure occurs.
 */
struct rx_nfa_edge *
rx_nfa_edge (struct rx_nfa *rx,
	     enum rx_nfa_etype type,
	     struct rx_nfa_state *start,
	     struct rx_nfa_state *dest)
{
  struct rx_nfa_edge *e;
  e = (struct rx_nfa_edge *)rx_nfa_cache_malloc (sizeof (*e));
  if (!e)
    return 0;
  mem_set0 ((t_uchar *)e, sizeof (*e));
  e->type = type;
  e->cset = 0;
  e->dest = dest;
  e->next = start->edges;
  start->edges = e;
  if (type == ne_cset)
    start->has_cset_edges = 1;
  ++n_edges_allocated;
  return e;
}

/* THESE TWO FUNCTIONS MUST BE ADJACENT FOR THE MANUAL TO READ
 * CORRECTLY. (rx_nfa_edge, rx_nfa_cset_edge)
 */

/*(c rx_nfa_cset_edge)
 * struct rx_nfa_edge * rx_nfa_cset_edge (struct rx_nfa *rx,
 *                                        enum rx_nfa_etype type,
 *				          bits cset,
 *                                        struct rx_nfa_state *start,
 *                                        struct rx_nfa_state *dest);
 *
 *
 * Allocate a new NFA edge for the NFA `rx'.  The new edge will
 * have a character set label.
 *
 * `type' indicates which kind of edge is being built.  Ordinarily
 * this should be `ne_cset'.
 *
 * `cset' is used to initialize the field `cset' field of the edge.
 * `cset' is not copied and will be freed by `rx_nfa_cache_free' when this
 * edge is freed.
 *
 * `start' and `dest' describe the source and destination states
 * of the edge.
 *
 * This function returns 0 if an allocation failure occurs.  In that
 * case, `cset' is not freed.
 */
struct rx_nfa_edge *
rx_nfa_cset_edge (struct rx_nfa *rx,
		  enum rx_nfa_etype type,
		  bits cset,
		  struct rx_nfa_state *start,
		  struct rx_nfa_state *dest)
{
  struct rx_nfa_edge *e;
  e = rx_nfa_edge (rx, type, start, dest);
  if (!e)
    return 0;
  e->cset = cset;
  return e;
}


/************************************************************************
 *(h2 "The Last Step of Building an NFA")
 *
 *
 * After building an NFA graph, the last step is to distinguish the
 * unique start state of the NFA and label all of the final states
 * with non-0 labels.
 */

/*(c rx_set_start_state)
 * void rx_set_start_state (struct rx_nfa * rx, struct rx_nfa_state * n);
 *
 * Set the start state of the NFA `rx'.
 */
void
rx_set_start_state (struct rx_nfa * rx, struct rx_nfa_state * n)
{
  rx->start_nfa_state = n;
  n->is_start = 1;
}


/*(c rx_set_state_label)
 * void rx_set_state_label (struct rx_nfa * rx,
 *			    struct rx_nfa_state * n,
 *			    int label)
 *
 * Set the label of state `n' to `label'.
 */
void
rx_set_state_label (struct rx_nfa * rx,
		    struct rx_nfa_state * n,
		    int label)
{
  n->state_label = label;
}





/************************************************************************
 * Managing Sets of NFA States
 *
 */

/* static int nfa_set_cmp (void * va, void * vb);
 *
 * Compare two NFA state sets.  The ordering of state sets
 * is well defined but unspecified.  Return -1 if `va' is
 * less than `vb', 1 if it is greater, and 0 if they are equal.
 */
static int
nfa_set_cmp (void * va, void * vb)
{
  struct rx_nfa_state_set * a;
  struct rx_nfa_state_set * b;

  a = (struct rx_nfa_state_set *)va;
  b = (struct rx_nfa_state_set *)vb;

  return ((va == vb)
	  ? 0
	  : (!va
	     ? -1
	     : (!vb
		? 1
		: (a->car->id < b->car->id
		   ? -1
		   : (a->car->id > b->car->id
		      ? 1
		      : nfa_set_cmp ((void *)a->cdr, (void *)b->cdr))))));
}


/* static int nfa_set_equal (void * va, void * vb, struct hashtree_rules * rules);
 *
 * Return 1 if `va' and `vb' are equal state sets.
 * We know that if they are equal, their `cdr's must be equal
 * in the sense of `==', but their cars need not be.
 */
static int
nfa_set_equal (void * va, void * vb, struct hashtree_rules * rules)
{
  struct rx_nfa_state_set * a;
  struct rx_nfa_state_set * b;
  a = (struct rx_nfa_state_set *)va;
  b = (struct rx_nfa_state_set *)vb;
  return (   (a == b)
	  || (   a
	      && b
	      && (a->car->id == b->car->id)
	      && (a->cdr == b->cdr)));
}

static struct hashtree *
nfa_set_hash_alloc (struct hashtree_rules * rules)
{
  return (struct hashtree *)rx_nfa_cache_soft_malloc (sizeof (struct hashtree));
}


static void
nfa_set_free_hash (struct hashtree * tab, struct hashtree_rules * rules)
{
  rx_nfa_cache_free ((char *)tab);
}


static struct hashtree_item *
nfa_set_hash_item_alloc (void * key, struct hashtree_rules * rules)
{
  struct hashtree_item * it;
  it = (struct hashtree_item *)rx_nfa_cache_soft_malloc (sizeof (*it));
  if (!it)
    return 0;
  it->key = key;
  it->binding = 0;
  return it;
}


static void
nfa_set_free_hash_item (struct hashtree_item * item, struct hashtree_rules * rules)
{
  rx_nfa_cache_free ((char *)item);
}


static struct hashtree_rules nfa_set_hash_rules =
{
  nfa_set_equal,
  nfa_set_hash_alloc,
  nfa_set_free_hash,
  nfa_set_hash_item_alloc,
  nfa_set_free_hash_item
};


/* static struct rx_nfa_state_set * nfa_set_cons (struct rx_nfa * rx,
 *						  struct hashtree * memo,
 *						  struct rx_nfa_state * state,
 *						  struct rx_nfa_state_set * set);
 *
 * Construct a new state set by adding a new state to the head of the list.
 * For equal `set' and `state', return equal (`==') results.
 */
static struct rx_nfa_state_set *
nfa_set_cons (struct rx_nfa * rx,
	      struct hashtree * memo,
	      struct rx_nfa_state * state,
	      struct rx_nfa_state_set * set)
{
  struct rx_nfa_state_set template;
  struct hashtree_item * node;
  unsigned long hash_value;

  template.car = state;
  template.cdr = set;
  hash_value = (  (unsigned long)set
		^ (((unsigned long)state) << 19)
		^ (((unsigned long)state) >> (8 * sizeof (unsigned long) - 19)));

  node = hashtree_store (memo,
			hash_value,
			&template,
			&nfa_set_hash_rules);

  if (!node)
    return 0;

  if (node->key == &template)
    {
      struct rx_nfa_state_set * l;

      l = (struct rx_nfa_state_set *) rx_nfa_cache_malloc (sizeof (*l));
      if (!l)
	{
	  node->key = 0;
	  hashtree_delete (node, &nfa_set_hash_rules);
	  return 0;
	}
      *l = template;
      node->key = (void *) l;
    }

  return (struct rx_nfa_state_set *)node->key;
}


/* static struct rx_nfa_state_set * nfa_set_adjoin (struct rx_nfa * rx,
 *                                                  struct hashtree * memo,
 *                                                  struct rx_nfa_state * state,
 *                                                  struct rx_nfa_state_set * set);
 *
 * Construct a state set by adding a state to the appropriate position
 * in the (sorted) list.
 *
 * For equal `set' and `state', return equal (`==') results.
 */
static struct rx_nfa_state_set *
nfa_set_adjoin (struct rx_nfa * rx,
		struct hashtree * memo,
		struct rx_nfa_state * state,
		struct rx_nfa_state_set * set)
{
  if (!set || (state->id < set->car->id))
    return nfa_set_cons (rx, memo, state, set);
  if (state->id == set->car->id)
    return set;
  else
    {
      struct rx_nfa_state_set * newcdr;
      newcdr = nfa_set_adjoin (rx, memo, state, set->cdr);
      if (!newcdr)
	return 0;
      if (newcdr != set->cdr)
	set = nfa_set_cons (rx, memo, set->car, newcdr);
      return set;
    }
}


/* static struct rx_nfa_state_set * nfa_set_union (struct rx_nfa * rx,
 *						   struct hashtree * memo,
 *						   struct rx_nfa_state_set * a,
 *						   struct rx_nfa_state_set * b);
 *
 * Construct a state set containing the members of `a' and `b'.
 *
 * For equal sets of members, return equal (`==') results.
 */
static struct rx_nfa_state_set *
nfa_set_union (struct rx_nfa * rx,
	       struct hashtree * memo,
	       struct rx_nfa_state_set * a,
	       struct rx_nfa_state_set * b)
{
  if (!a)
    return b;
  if (!b)
    return a;

  if (a->car->id < b->car->id)
    {
      struct rx_nfa_state_set * u;
      u = nfa_set_union (rx, memo, a->cdr, b);
      if (!u)
	return 0;
      return nfa_set_cons (rx, memo, a->car, u);
    }
  else if (b->car->id < a->car->id)
    {
      struct rx_nfa_state_set * u;
      u = nfa_set_union (rx, memo, b->cdr, a);
      if (!u)
	return 0;
      return nfa_set_cons (rx, memo, b->car, u);
    }
  else
    return nfa_set_union (rx, memo, a, b->cdr);
}


/************************************************************************
 *(h2 "Computing Epsilon Closures")
 *
 * Given an NFA and a state `S' from that NFA, there is a set of NFA
 * states which is the set of all states that can be reached from `S'
 * by following 0 or more epsilon edges.  This set plays an important
 * role in the construction of a deterministic automata from a
 * non-deterministic automata.  The function in this section computes
 * that set.
 *
 */


/* static void eclose_node (struct rx_nfa * rx,
 *			    struct rx_nfa_state * outnode,
 *			    struct rx_nfa_state * node);
 *
 * Compute the epsilon closure of one node of the `nfa' graph.
 * Do this by traversing epsilon edges while avoiding cycles.
 */
static int
eclose_node (struct rx_nfa * rx,
	     struct rx_nfa_state * outnode,
	     struct rx_nfa_state * node)
{
  struct rx_nfa_edge *e;

  if (node->closure_computed)
    {
      outnode->closure = nfa_set_union (rx, &rx->set_list_memo, outnode->closure, node->closure);
      if (!outnode->closure)
	return -1;
    }

  if (node->mark)
    return 0;

  node->mark = 1;

  outnode->closure = nfa_set_adjoin (rx,
				     &rx->set_list_memo,
				     node,
				     outnode->closure);
  if (!outnode->closure)
    {
      node->mark = 0;
      return -1;
    }

  e = node->edges;
  while (e)
    {
      if (e->type == ne_epsilon)
	{
	  if (eclose_node (rx, outnode, e->dest))
	    {
	      node->mark = 0;
	      return -1;
	    }
	}
      e = e->next;
    }

  node->mark = 0;
  return 0;
}


/*(c rx_state_closure)
 * struct rx_nfa_state_set * rx_state_closure (struct rx_nfa * rx,
 *					       struct rx_nfa_state * n);
 *
 * Return the state set which is the epsilon-edge closure of NFA state
 * `n' in NFA `rx'.  This function records its result so that
 * subsequent calls with the same arguments run faster.  The first
 * call for particular arguments can be slow, depending on the size
 * and structure of the NFA graph.
 *
 * If an allocation failure occurs, this function panics and exits.
 */
struct rx_nfa_state_set *
rx_state_closure (struct rx_nfa * rx,
		  struct rx_nfa_state * n)
{
  if (!n->closure_computed)
    {
      if (eclose_node (rx, n, n))
	return 0;
      n->closure_computed = 1;
      return n->closure;
    }
  return n->closure;
}

