/* Copyright (c) 1991 by the Vrije Universiteit, Amsterdam, the Netherlands.
 * For full copyright and restrictions on use see the file COPYING in the top
 * level of the LLgen tree.
 */

/*
 *  L L G E N
 *
 *  An Extended LL(1) Parser Generator
 *
 *  Author : Ceriel J.H. Jacobs
 */

/*
 * sets.c
 * Set manipulation and allocation routines.
 */

# include "types.h"
# include "extern.h"
# include "sets.h"
# include "assert.h"

# ifndef NORCSID
static string rcsid9 = "$Id: sets.c,v 2.8 1997/02/21 11:27:55 ceriel Exp $";
# endif

/* In this file the following routines are defined: */
extern		setinit();
extern p_set	setalloc();
extern p_set	get_set();
extern int	setunion();
extern int	setintersect();
extern		setminus();
extern int	setempty();
extern int	findindex();
extern int	setcount();

int		nbytes;
static int	setsize;
int		tsetsize;
p_set		*setptr, *maxptr;
static t_info	set_info;
p_mem		alloc();

setinit(nt_needed) {
	/*
	 * Initialises some variables needed for setcomputations
	 */
	register int	 bitset;

	nbytes = NBYTES(ntokens);
	bitset = ALIGN(nbytes);
	tsetsize = NINTS(bitset);
	if (nt_needed) {
		/* nonterminals must be included in the sets */
		bitset += NBYTES(nnonterms);
	}
	setsize = NINTS(bitset);
	set_info.i_esize = sizeof(p_set);
	set_info.i_incr = 20;
}

p_set
get_set() {
	/*
	 * Allocate a set that cannot be freed
	 */
	register p_set p, q;
	static p_set sets, maxsets;

	if ((p = sets) >= maxsets) {
		q = p = (p_set) alloc((unsigned) (50*setsize*sizeof(*sets)));
		maxsets = p + 50 * setsize;
		do {
			*q++ = 0;
		} while (q < maxsets);
	}
	sets = p + setsize;;
	return p;
}

p_set
setalloc() {
	/*
	 * Allocate a set which can later be freed.
	 */
	register p_set	p;
	register int	size = setsize;

	p = (p_set) alloc((unsigned) (size * sizeof(*p))) + size;
	do {
		*--p = 0;
	} while (--size);
	return p;
}

int
setunion(a,b) register p_set a,b; {
	/*
	 * a = a union b.
	 * Return 1 if the set a changed
	 */
	register int	i;
	register int	j;
	register int	nsub = 0;

	i = setsize;
	do {
		*a = (j = *a) | *b++;
		if (*a++ != j) {
			nsub = 1;
		}
	} while (--i);
	return nsub;
}

int
setintersect(a,b) register p_set a,b; {
	/*
	 * a = a intersect b.
	 * return 1 if the result is empty
	 */
	register int	i;
	register int	nempty;

	nempty = 1;
	i =  setsize;
	do {
		if (*a++ &= *b++) nempty = 0;
	} while (--i);
	return nempty;
}

setminus(a,b) register p_set a,b; {
	/*
	 * a = a setminus b
	 */
	register int	i;

	i = setsize;
	do {
		*a++ &= ~(*b++);
	} while (--i);
}

int
setempty(p) register p_set p; {
	/*
	 * Return 1 if the set p is empty
	 */
	register int	i;

	i = tsetsize;
	do {
		if (*p++) return 0;
	} while (--i);
	return 1;
}

int
findindex(set) p_set set; {
	/*
	 * The set "set" will serve as a recovery set.
	 * Search for it in the table. If not present, enter it.
	 * Here is room for improvement. At the moment, the list of
	 * sets is examined with linear search.
	 */
	register p_set	*t;
	p_mem		new_mem();
	register p_set	a;
	register p_set	b;
	register int	i;
	int		saved;

	/*
	 * First search for the set in the table
	 */
	for (t = setptr; t < maxptr; t++) {
		a = *t;
		b = set;
		i = tsetsize;
		do {
			if (*a++ != *b++) break;
		} while (--i);
		if (i) continue;
		/*
		 * Here, the sets are equal.
		 */
		return nbytes * (t - setptr);
	}
	/*
	 * Now check if the set consists of only one element.
	 * It would be a waste to use a set for that
	 */
	if (setcount(set, &saved) == 1) return -(saved + 1);
	/*
	 * If it does, return its number as a negative number.
	 */
	maxptr = (p_set *) new_mem(&set_info);
	setptr = (p_set *) set_info.i_ptr;
	*maxptr = setalloc();
	setunion(*maxptr, set);
	return nbytes * (maxptr++ - setptr);
}

int
setcount(set, saved) register p_set set; int *saved; {
	register int i, j;

	for (j = 0, i = 0; i < ntokens; i++) {
		if (IN(set,i)) {
			j++;
			*saved = i;
		}
	}
	return j;
}
