/*
 *
 *  sptree.c:  The following code implements the basic operations on
 *  an event-set or priority-queue implemented using splay trees:
 *
 *  The implementation used here is based on the implementation
 *  which was used in the tests of splay trees reported in:
 *
 *    An Empirical Comparison of Priority-Queue and Event-Set Implementations,
 *	by Douglas W. Jones, Comm. ACM 29, 4 (Apr. 1986) 300-311.
 *
 *  The basic splay tree algorithms were originally
 *  presented in:
 *
 *	Self Adjusting Binary Trees,
 *		by D. D. Sleator and R. E. Tarjan,
 *			Proc. ACM SIGACT Symposium on Theory
 *			of Computing (Boston, Apr 1983) 235-245.
 *
 *	Based on an original posting from the Usenet. Modified by
 *	P. Fox.
 */

# include "sptree.h"
# include	"../foxlib/chkalloc.h"

# if !defined(TRUE)
#	define	TRUE	1
#	define	FALSE	0
# endif

SPSTATS __spstats;
static char	*hd_splays;
static char	*hd_blks;

/**********************************************************************/
/*   spinit() -- initialize an empty splay tree			      */
/**********************************************************************/
SPTREE *
spinit()
{
	register SPTREE * q;
	static SPTREE null_sptree = {0};

	q = (SPTREE *) vm_alloc(sizeof(*q), (void **) &hd_splays);
	if (q == NULL)
		return NULL;
	*q = null_sptree;
	SP_INCR(spinit);
	SP_INCR(trees_alloced);
	return q;
}

/**********************************************************************/
/*   Free the memory associated with the root of a tree.	      */
/**********************************************************************/
void
spfree(q)
SPTREE	*q;
{
	SP_DECR(trees_alloced);
	SP_INCR(spfree);
	vm_free((void *) q, (void **) &hd_splays);
}

/**********************************************************************/
/*   Return  an  SPBLK structure together with some room for user to  */
/*   put data in it.						      */
/**********************************************************************/
SPBLK *
spblk(size)
int	size;
{	SPBLK	*sp;
	static SPBLK null_blk;

	sp = (SPBLK *) vm_alloc(sizeof(*sp), (void **) &hd_blks);
	if (sp == NULL)
		return NULL;
	*sp = null_blk;
	if (size == 0) {
		SP_INCR(spblk);
		SP_INCR(spblk_alloced);
		return sp;
		}

	sp->data = chk_alloc(size);
	if (sp->data == NULL) {
		vm_free((void *) sp, (void **) &hd_blks);
		return NULL;
		}
	SP_INCR(spblk);
	SP_INCR(spblk_alloced);
	return sp;
}

/**********************************************************************/
/*   Free memory for an SPBLK structure.			      */
/**********************************************************************/
void
spfreeblk(sp)
SPBLK	*sp;
{
	SP_INCR(spfreeblk);
	SP_DECR(spblk_alloced);
	vm_free((void *) sp, (void **) &hd_blks);
}
/**********************************************************************/
/*   Return TRUE if tree is empty.				      */
/**********************************************************************/
int
spempty(q)
SPTREE *q;

{
	SP_INCR(spempty);
	return q == NULL || q->root == NULL;
}
SPBLK *
sphead(tree)
SPTREE	*tree;
{

	SP_INCR(sphead);
	return tree->root;
}

/**********************************************************************/
/*   Add a new entry to the tree.				      */
/**********************************************************************/
void
spenq(q, tree)
SPBLK	*q;
SPTREE	*tree;
{	register SPBLK *x = tree->root;
	register char	*key = q->key;

	SPI_INCR(tree->enqs);
	SP_INCR(spenq);
	tree->num_items++;
	q->leftlink = q->rightlink = NULL;
	if (x == NULL) {
		tree->root = q;
		q->uplink = NULL;
		return;
		}
	while (1) {
		register int 	Sct;
		Sct = STRCMP(key, x->key);
		SPI_INCR(tree->enqcmps);
		SP_INCR(spenq_loops);
		if (Sct < 0) {
			if (x->leftlink == NULL) {
				x->leftlink = q;
				break;
				}
			x = x->leftlink;
			}
		else if (Sct > 0) {
			if (x->rightlink == NULL) {
				x->rightlink = q;
				break;
				}
			x = x->rightlink;
			}
		else {
			printf("Duplicate entry in splay tree\n");
			exit(1);
			}
		}
	q->uplink = x;
	
	/***********************************************/
	/*   Add  a  bit of randomness to the way the  */
	/*   splay    tree    is   built   to   avoid  */
	/*   catastrophic       performance      when  */
	/*   inserting alphabetically into a tree.     */
	/***********************************************/
	if ((__spstats.spenq & 0x7) == 0)
		splay(q, tree);
}

/**********************************************************************/
/*   Following   function   removes  an  item  from  the  tree.  The  */
/*   algorithm  is  taken  from:  Knuth,  Vol.  3, Algorithm D, page  */
/*   420.  The  code is convoluted enough that I couldn't figure out  */
/*   how to remove the goto's.					      */
/**********************************************************************/
void
spdeq(q, tree)
SPBLK	*q;
SPTREE	*tree;
{	register SPBLK	*r, *s, *t;

	SP_INCR(spdeq);
	tree->num_items--;
/*D1:*/
	t = q;
	if (t->rightlink == NULL) {
		q = t->leftlink;
		goto D4;
		}
/*D2:*/
	r = t->rightlink;
	if (r->leftlink == NULL) {
		if (r->leftlink = t->leftlink)
			t->leftlink->uplink = r;
		q = r;
		goto D4;
		}
/*D3:*/
	do {
		s = r->leftlink;
		if (s->leftlink == NULL)
			break;
		r = s;
		}
	while (1);
	if (s->leftlink = t->leftlink)
		t->leftlink->uplink = s;
	if (r->leftlink = s->rightlink)
		s->rightlink->uplink = r;
	s->rightlink = t->rightlink;
	t->rightlink->uplink = s;
	q = s;
D4:
	if (tree->root == t)
		tree->root = q;
	if (q)
		q->uplink = t->uplink;
	if (t->uplink && t->uplink->leftlink == t)
		t->uplink->leftlink = q;
	else if (t->uplink && t->uplink->rightlink == t)
		t->uplink->rightlink = q;
}
/*----------------
 *
 *  splay() -- reorganize the tree.
 *
 *  the tree is reorganized so that x is the root of the
 *  splay tree representing tree; x must be in the tree, otherwise
 *  infinite looping will result; the left branch of
 *  the right subtree and the right branch of the left subtree are
 *  shortened in the process
 *
 */
void
splay(x, tree)
SPBLK	*x;
SPTREE	*tree;
{	register SPBLK	*root = tree->root;
	register SPBLK	*up;

	SP_INCR(splay);

	if (tree->flags & SPF_WALK)
		return;
	SPI_INCR(tree->splays);
	if (root == x || root->leftlink == x || root->rightlink == x)
		return;
	while (1) {
		if (x->uplink == NULL) {
			tree->root = x;
			return;
			}
		/* ZIG */
		if (root->leftlink == x || root->rightlink == x) {
			sprotate(x);
			x->uplink = NULL;
			tree->root = x;
			return;
			}
		/* ZIG-ZIG */
		up = x->uplink;
		if (up->leftlink == x &&
		    up->uplink && 
		    up->uplink->leftlink == up) {
			sprotate(up);
			sprotate(x);
			}
		else if (up->rightlink == x &&
			 up->uplink &&
			 up->uplink->rightlink == up) {
			sprotate(up);
			sprotate(x);
			}
		/* ZIG-ZAG */
		else {
			sprotate(x);
			sprotate(x);
			}
		SPI_INCR(tree->splayloops);
		}
}

/**********************************************************************/
/*   Function to move an item one layer in the tree.		      */
/**********************************************************************/
void
sprotate(x)
register SPBLK	*x;
{	register SPBLK	*y;
	SPBLK	*B;
	SPBLK	*up = x->uplink;
	SPBLK	*yup;

	SP_INCR(sprotate);

	y = up;
	if (up->leftlink == x) {
		/*--------------------------------------------------*/
		/* Right rotate at x.				    */
		/*               y		     x		    */
		/*             /   \		   /   \	    */
		/*           x      C	   ==>	 A	 y	    */
		/*         /   \		       /   \	    */
		/*        A     B		     B	     C	    */
		/*--------------------------------------------------*/
		B = x->rightlink;

		if (yup = x->uplink = y->uplink) {
		    if (yup->leftlink == y)
			yup->leftlink = x;
		    else
			yup->rightlink = x;
		    }
		y->uplink = x;
		y->leftlink = B;
		x->rightlink = y;
		}
	else {
		/*--------------------------------------------------*/
		/*               x		     y		    */
		/*             /   \		   /   \	    */
		/*           y      C	   <==	 A	 x	    */
		/*         /   \		       /   \	    */
		/*        A     B		     B	     C	    */
		/*--------------------------------------------------*/
		B = x->leftlink;
		if (yup = x->uplink = y->uplink) {
		    if (yup->leftlink == y)
			yup->leftlink = x;
		    else
			yup->rightlink = x;
		    }
		y->uplink = x;
		x->leftlink = y;
		y->rightlink = B;
	    }
	if (B)
		B->uplink = y;
}
/*----------------
 *
 * splookup() -- given key, find a node in a tree.
 *
 *	Splays the found node to the root.
 */
SPBLK *
splookup(key, q)
register char * key;
register SPTREE * q;

{	register SPBLK * n;
	register int Sct;
	
	SP_INCR(splookup);
	/* find node in the tree */
	n = q->root;

	SPI_INCR(q->lookups);
	while (n) {
		SPI_INCR(q->lkpcmps);
		SP_INCR(splookup_loops);
		if ((Sct = STRCMP(key, n->key)) == 0) {
			splay(n, q);
			return n;
			}
		n = (Sct < 0) ? n->leftlink : n->rightlink;
		}
	return NULL;
}
/**********************************************************************/
/*   Like splookup() but tell us if we found an ambiguity.	      */
/**********************************************************************/
SPBLK *
sp_partial_lookup(key, q, ambiguous, first_sp)
register char * key;
register SPTREE * q;
int	*ambiguous;
SPBLK **first_sp;
{	register SPBLK * n;
	register int Sct;
	int	key_len = strlen(key);
		
	SP_INCR(splookup);

	n = q->root;
	*first_sp = NULL;
	*ambiguous = FALSE;
	SPI_INCR(q->lookups);
	while (n) {
		SPI_INCR(q->lkpcmps);
		SP_INCR(splookup_loops);
		if ((Sct = STRCMP(key, n->key)) == 0) {
			/***********************************************/
			/*   Check  for  possible ambiguity even tho'  */
			/*   we found a match.			       */
			/***********************************************/
			if (!*ambiguous) {
				if (n->leftlink && strncmp(key, n->leftlink->key, key_len) == 0)
					*ambiguous = TRUE;
				else if (n->rightlink && strncmp(key, n->rightlink->key, key_len) == 0)
					*ambiguous = TRUE;
				}
			splay(n, q);
			return n;
			}
		/***********************************************/
		/*   See if we have an ambiguity.	       */
		/***********************************************/
		if (!*ambiguous && strncmp(key, n->key, key_len) == 0) {
			*ambiguous = TRUE;
			if (*first_sp == NULL)
				*first_sp = n;
			}
		n = (Sct < 0) ? n->leftlink : n->rightlink;
		}
	return NULL;
}
/**********************************************************************/
/*   The  following  function  visits  each node in a splay tree and  */
/*   calls  the  function  'func'  with  a pointer to the node being  */
/*   visited  and  the  arg2  parameter passed as well. The SPF_WALK  */
/*   flag  is  set  on  the  tree to indicate that if any splay tree  */
/*   functions  are  called  from  the users callback function, that  */
/*   the  tree  shouldn't be re-splayed as a consequence. This makes  */
/*   it easier to walk a tree and delete each node as it is visited.  */
/**********************************************************************/
void
spapply(tree, func, arg2)
SPTREE	*tree;
int	(*func)();
int	arg2;
{
	tree->flags |= SPF_WALK;
	spwalk(tree->root, func, arg2);
	tree->flags &= ~SPF_WALK;
}

/**********************************************************************/
/*   The  following  function is similar to spapply() above, in that  */
/*   each  node  of  the  splay  tree is visited and a user callback  */
/*   function  is  called, except that the SPF_WALK flag is not set,  */
/*   so  that  the  splay tree can be resplayed if the user call the  */
/*   splay  tree  library  from  the callback. IT IS DANGEROUS TO DO  */
/*   THIS.							      */
/**********************************************************************/
void
spwalk(node, func, arg2)
SPBLK	*node;
int	(*func)();
int	arg2;
{
	if (node == NULL)
		return;
	spwalk(node->leftlink, func, arg2);
	(*func)(node, arg2);
	spwalk(node->rightlink, func, arg2);
}

