/*
 * spdaveb.c -- daveb's new splay tree functions.
 *
 * The functions in this file provide an interface that is nearly
 * the same as the hash library I swiped from mkmf, allowing
 * replacement of one by the other.  Hey, it worked for me!
 *
 * splookup() -- given a key, find a node in a tree.
 * spinstall() -- install an item in the tree, overwriting existing value.
 * spfhead() -- fast (non-splay) find the first node in a tree.
 * spftail() -- fast (non-splay) find the last node in a tree.
 * spscan() -- forward scan tree from the head.
 * sprscan() -- reverse scan tree from the tail.
 * spfnext() -- non-splaying next.
 * spfprev() -- non-splaying prev.
 * spstats() -- make char string of stats for a tree.
 *
 * Written by David Brower, daveb@rtech.uucp 1/88.
 */


# include "sptree.h"

/* USER SUPPLIED! */

extern char *emalloc();


/*----------------
 *
 * 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;
    register int c;

    /* find node in the tree */
    n = q->root;
    c = ++(q->lkpcmps);
    q->lookups++;
    while( n && (Sct = STRCMP( key, n->key ) ) )
    {
	c++;
	n = ( Sct < 0 ) ? n->leftlink : n->rightlink;
    }
    q->lkpcmps = c;

    /* reorganize tree around this node */
    if( n != NULL )
	splay( n, q );

    return( n );
}



/*----------------
 *
 * spinstall() -- install an entry in a tree, overwriting any existing node.
 *
 *	If the node already exists, replace its contents.
 *	If it does not exist, then allocate a new node and fill it in.
 */

SPBLK *
spinstall( key, data, datb, q )

register char * key;
register char * data;
register char * datb;
register SPTREE *q;

{
    register SPBLK *n;

    if( NULL == ( n = splookup( key, q ) ) )
    {
	n = (SPBLK *) emalloc( sizeof( *n ) );
	n->key = key;
	n->leftlink = NULL;
	n->rightlink = NULL;
	n->uplink = NULL;
	spenq( n, q );
    }

    n->data = data;
    n->datb = datb;

    return( n );
}




/*----------------
 *
 * spfhead() --	return the "lowest" element in the tree.
 *
 *	returns a reference to the head event in the event-set q.
 *	avoids splaying but just searches for and returns a pointer to
 *	the bottom of the left branch.
 */
SPBLK *
spfhead( q )

register SPTREE * q;

{
    register SPBLK * x;

    if( NULL != ( x = q->root ) )
	while( x->leftlink != NULL )
	    x = x->leftlink;

    return( x );

} /* spfhead */




/*----------------
 *
 * spftail() -- find the last node in a tree.
 *
 *	Fast version does not splay result or intermediate steps.
 */
SPBLK *
spftail( q )

SPTREE * q;

{
    register SPBLK * x;


    if( NULL != ( x = q->root ) )
	while( x->rightlink != NULL )
	    x = x->rightlink;

    return( x );

} /* spftail */


/*----------------
 *
 * spscan() -- apply a function to nodes in ascending order.
 *
 *	if n is given, start at that node, otherwise start from
 *	the head.
 */
void
spscan( f, n, q )

register int (*f)();
register SPBLK * n;
register SPTREE * q;

{
    register SPBLK * x;

    for( x = n != NULL ? n : spfhead( q ); x != NULL ; x = spfnext( x ) )
        (*f)( x );
}



/*----------------
 *
 * sprscan() -- apply a function to nodes in descending order.
 *
 *	if n is given, start at that node, otherwise start from
 *	the tail.
 */
void
sprscan( f, n, q )

register int (*f)();
register SPBLK * n;
register SPTREE * q;

{
    register SPBLK *x;

    for( x = n != NULL ? n : spftail( q ); x != NULL ; x = spfprev( x ) )
        (*f)( x );
}



/*----------------
 *
 * spfnext() -- fast return next higer item in the tree, or NULL.
 *
 *	return the successor of n in q, represented as a splay tree.
 *	This is a fast (on average) version that does not splay.
 */
SPBLK *
spfnext( n )

register SPBLK * n;

{
    register SPBLK * next;
    register SPBLK * x;

    /* a long version, avoids splaying for fast average,
     * poor amortized bound
     */

    if( n == NULL )
        return( n );

    x = n->rightlink;
    if( x != NULL )
    {
        while( x->leftlink != NULL )
	    x = x->leftlink;
        next = x;
    }
    else	/* x == NULL */
    {
        x = n->uplink;
        next = NULL;
        while( x != NULL )
	{
            if( x->leftlink == n )
	    {
                next = x;
                x = NULL;
            }
	    else
	    {
                n = x;
                x = n->uplink;
            }
        }
    }

    return( next );

} /* spfnext */



/*----------------
 *
 * spfprev() -- return fast previous node in a tree, or NULL.
 *
 *	return the predecessor of n in q, represented as a splay tree.
 *	This is a fast (on average) version that does not splay.
 */
SPBLK *
spfprev( n )

register SPBLK * n;

{
    register SPBLK * prev;
    register SPBLK * x;

    /* a long version,
     * avoids splaying for fast average, poor amortized bound
     */

    if( n == NULL )
        return( n );

    x = n->leftlink;
    if( x != NULL )
    {
        while( x->rightlink != NULL )
	    x = x->rightlink;
        prev = x;
    }
    else
    {
        x = n->uplink;
        prev = NULL;
        while( x != NULL )
	{
            if( x->rightlink == n )
	    {
                prev = x;
                x = NULL;
            }
	    else
	    {
                n = x;
                x = n->uplink;
            }
        }
    }

    return( prev );

} /* spfprev */



char *
spstats( q )
SPTREE *q;
{
    static char buf[ 128 ];
    float llen;
    float elen;
    float sloops;

    if( q == NULL )
	return("");

    llen = q->lookups ? (float)q->lkpcmps / q->lookups : 0;
    elen = q->enqs ? (float)q->enqcmps/q->enqs : 0;
    sloops = q->splays ? (float)q->splayloops/q->splays : 0;

    sprintf(buf, "f(%d %4.2f) i(%d %4.2f) s(%d %4.2f)",
	q->lookups, llen, q->enqs, elen, q->splays, sloops );

    return buf;
}

