/*
 *
 *	$Header: /usr/u/wjr/src/ADT/RCS/priq.c,v 1.5 1991/11/07 22:41:46 wjr Exp $
 *
 */
/*
 * Xerox may use this code without restriction.
 */

static char *rcsid = "@(#)$Header: /usr/u/wjr/src/ADT/RCS/priq.c,v 1.5 1991/11/07 22:41:46 wjr Exp $";

/*
 *	priq.c - Priority Queues
 *
 * This module handles generic priority queues.
 *
 * Exports:
 *	type PriQ
 *
 *	constant PriQ NULLPRIQ
 *	constant ListNode NULLLISTNODE
 *
 *	PriQ pqNew(boolean (*compareGT)()) - create a new, empty priority queue
 *
 *	void pqFree(PriQ p) - free priority queue p
 *
 *	PriQNode pqFirst(PriQ p) - return and remove the first entry in p
 *
 *	PriQNode pqAdd(PriQ p, char *userdata) - add a node containing
 *		userdata to p
 *
 *	void pqRem(PriQNode pqn) - remove pqn from the queue containing it
 *
 *	char *pqGet(PriQNode pqn) - return pqn's userdata
 *
 * An object of type PriQ is used as a handle on a priority queue.
 *	Each element of the queue contains a char *
 *	field, which is used for whatever data the user wishes to attach
 *	to that node. It is standard C idiom that (void *) pointers can
 *	be cast to and from any other type of pointer without error or loss,
 *	but the DECStation C compiler chokes on them, so I am using (char *)
 *	instead.
 *
 * A PriQNode refers to an entry in a priority queue.
 *
 * pqNew creates an empty queue. If this is not possible, it returns NULLPRIQ.
 *	The function which it takes is used as the comparison function to
 *	sort the queue. It should be declared as
 *	boolean compareGT(a, b)
 *	char *a;
 *	char *b;
 *	and it returns FALSE if "a" <= "b" and TRUE if "a" > "b".
 *	It does not compare the pointers themselves (priq could do that
 *	itself), but rather compares the user data values associated with
 *	those pointers in whatever way the user desires (which should be
 *	a total order). It will be called as required to ensure that
 *	pqRem returns the smallest valued entry.
 *
 * pqFree frees the storage associated with a priority queue.
 *	Note that it does not free the userdata; the user
 *	must do this before freeing the queue structure.
 *
 * pqFirst returns the first (smallest) entry of the given queue.
 *	It does not guarantee any order among equal elements.
 *	If the queue is empty, it returns NULLPRIQNODE.
 *
 * pqAdd adds an entry to the given priority queue. It returns the new
 *	PriQNode. If it could not allocate needed storage, it returns
 *	NULLPRIQNODE.
 *
 * pqRem removes the given PriQNode from its PriQ. The PriQNode is then
 *	invalid.
 *
 * pqGet returns the userdata value of some PriQNode.
 *
 * Changing the values pointed to by the userdata pointers may result
 *	in pqFirst not returning the smallest current value. To change these,
 *	perform a pqRem and a pqAdd.
 *
 * pqAdd, pqRem and other operations involving PriQNodes may be arbitrarily
 *	interleaved. A PriQNode is valid from the moment pqAdd first returns
 *	it until it is passed into pqRem, or its containing PriQ is passed
 *	to pqFree.
 *
 */

#include "misc.h"
#include "priq.h"

#define	NULLPRIQINTNODE	((PriQIntNode)NULL)

static void swap(PriQIntNode pqin1, PriQIntNode pqin2);

PriQ
pqNew(boolean (*compareGT)())
{
    PriQ newPriQ;

    assert(compareGT != (boolean (*)())NULL);
    if ((newPriQ = (PriQ)malloc(sizeof(* newPriQ))) == NULL) {
	return(NULLPRIQ);
	}
    newPriQ->root = newPriQ->last = NULLPRIQINTNODE;
    newPriQ->compareGT = compareGT;
	   
    return(newPriQ);
    }

void
pqFree(PriQ p)
{
    PriQIntNode pqn, nextpqn;

    assert(p != NULLPRIQ);
    for (pqn = p->root; pqn != NULLPRIQINTNODE; pqn = nextpqn) {
	nextpqn = pqn->next;
	free((char *)(pqn->owner));
	free((char *)pqn);
	}
    free((char *)p);
    }

PriQNode
pqFirst(PriQ p)
{
    assert(p != NULLPRIQ);
    if (p->root == NULLPRIQINTNODE) {
	return(NULLPRIQNODE);
	}
    else {
	return(p->root->owner);
	}
    }

void
pqRem(PriQNode pqn)
{
    PriQIntNode pqlast;
    PriQIntNode smallchild;
    PriQIntNode pqin;
    PriQ p;

    assert(pqn != NULLPRIQNODE);
    assert(pqn->val != NULLPRIQINTNODE);
    assert(pqn->val->owner == pqn);
    p = pqn->owner;
    assert(p != NULLPRIQ);
    assert(p->root != NULLPRIQINTNODE);

    /*RESUME HERE*/
    /* Was that the last thing in the queue? */
    if (p->root == p->last) {
	assert(pqn->val == p->root);
	free((char *)p->root);
	free((char *)pqn);
	p->root = p->last = NULLPRIQINTNODE;
	return;
	}

    if (pqn->val == p->last) {
	/*
	 * They're about to remove the last element. Don't need
	 * to reheapify - just patch the data structures
	 */
	pqlast = p->last;
	assert(pqlast->parent != NULLPRIQINTNODE);
	if (pqlast->parent->right == pqlast) {
	    pqlast->parent->right = NULLPRIQINTNODE;
	    }
	else {
	    assert(pqlast->parent->left == pqlast);
	    pqlast->parent->left = NULLPRIQINTNODE;
	    }
	assert(pqlast->prev != NULLPRIQINTNODE);
	pqlast->prev->next = NULLPRIQINTNODE;
	p->last = pqlast->prev;
	free((char *)pqlast);
	free((char *)pqn);
	return;
	}

    /* Now, move the last element up to the to-be deleted element */
    pqlast = p->last;
    pqn->val->userdata = pqlast->userdata;
    assert(pqlast->owner != NULLPRIQNODE);
    assert(pqlast->owner->val == pqlast);
    pqlast->owner->val = pqn->val;
    pqn->val->owner = pqlast->owner;

    /* and delete the last element structure */
    assert(pqlast->parent != NULLPRIQINTNODE);	/*
						 * if it was the root,
						 * it was handled above
						 */
    /* which child was it? */
    if (pqlast->parent->right == pqlast) {
	pqlast->parent->right = NULLPRIQINTNODE;
	}
    else {
	assert(pqlast->parent->left == pqlast);
	pqlast->parent->left = NULLPRIQINTNODE;
	}

    /* if it was the root as well, it was handled above */
    assert(pqlast->prev != NULLPRIQINTNODE);
    pqlast->prev->next = NULLPRIQINTNODE;
    p->last = pqlast->prev;
    free((char *)pqlast);

    /* Now reheapify. */
    /*
     * We have just taken some random value and stuck it into the middle
     * of the heap. It might have to move up or down
     */
    pqin = pqn->val;
    if ((pqin->parent == NULLPRIQINTNODE) ||
	((*(p->compareGT))(pqin->userdata, pqin->parent->userdata))) {
	/* Reheapify down, if at all */
	while (TRUE) {
	    if (pqin->left == NULLPRIQINTNODE) {
		/* Hit the bottom */
		break;
		}

	    /* Find which child has the smaller value */
	    if ((pqin->right == NULLPRIQINTNODE) ||
		((*(p->compareGT))(pqin->right->userdata,
				   pqin->left->userdata))) {
		smallchild = pqin->left;
		}
	    else {
		smallchild = pqin->right;
		}

	    /* Swap if necessary */
	    if ((*(p->compareGT))(pqin->userdata, smallchild->userdata)) {
		/* Swap the two */
		swap(pqin, smallchild);
		pqin = smallchild;
		}
	    else {
		/* Not smaller - heapification has been done */
		break;
		}
	    }
	}
    else {
	/* Need to reheapify upwards */
	while (TRUE) {
	    if ((pqin->parent != NULLPRIQINTNODE) &&
		((*(p->compareGT))(pqin->parent->userdata, pqin->userdata))) {
		/* Swap it and its parent */
		swap(pqin, pqin->parent);
		pqin = pqin->parent;
		}
	    else {
		break;
		}
	    }
	}

    /* and get rid of the PriQNode */
    free((char *)pqn);
    }

PriQNode
pqAdd(PriQ p, char *userdata)
{
    PriQIntNode pqin;
    PriQNode newpqn;
    PriQIntNode newpqin;

    assert(p != NULLPRIQ);
    if ((newpqn = (PriQNode)malloc(sizeof(* newpqn))) == NULLPRIQNODE) {
	return(NULLPRIQNODE);
	}

    if ((newpqin = (PriQIntNode)malloc(sizeof(* newpqin))) ==
	NULLPRIQINTNODE) {
	free((char *)newpqn);
	return(NULLPRIQNODE);
	}

    newpqin->left = newpqin->right = newpqin->next = NULLPRIQINTNODE;
    newpqin->owner = newpqn;
    newpqin->userdata = userdata;
    newpqn->val = newpqin;
    newpqn->owner = p;

    if (p->root == NULLPRIQINTNODE) {
	/* The first thing in the queue */
	p->root = p->last = newpqin;
	newpqin->prev = NULLPRIQINTNODE;
	newpqin->parent = NULLPRIQINTNODE;
	return(newpqn);
	}

    newpqin->prev = p->last;
    if (p->last->parent == NULLPRIQINTNODE) {
	/* The last element is also the root */
	assert(p->last == p->root);
	p->last->left = newpqin;
	newpqin->parent = p->last;
	}
    else if (p->last->parent->right == NULLPRIQINTNODE) {
	/* The last entry's parent has an empty slot - use it */
	p->last->parent->right = newpqin;
	newpqin->parent = p->last->parent;
	}
    else {
	assert(p->last->parent->next != NULLPRIQINTNODE);
	assert(p->last->parent->next->left == NULLPRIQINTNODE);
	p->last->parent->next->left = newpqin;
	newpqin->parent = p->last->parent->next;
	}
    p->last->next = newpqin;
    p->last = newpqin;

    /* Now reheapify */
    for (pqin = p->last; ; pqin = pqin->parent) {
	if ((pqin->parent != NULLPRIQINTNODE) &&
	    ((*(p->compareGT))(pqin->parent->userdata, pqin->userdata))) {
	    /* Swap it and its parent */
	    swap(pqin, pqin->parent);
	    }
	else {
	    break;
	    }
	}
    return(newpqn);
    }

char *
pqGet(PriQNode pqn)
{
    assert(pqn != NULLPRIQNODE);
    assert(pqn->val != NULLPRIQINTNODE);
    return(pqn->val->userdata);
    }

static void
swap(PriQIntNode pqin1, PriQIntNode pqin2)
{
    char *temp;
    PriQNode tempqn;

    assert(pqin1 != NULLPRIQINTNODE);
    assert(pqin2 != NULLPRIQINTNODE);

    /* Swap the values */
    temp = pqin1->userdata;
    pqin1->userdata = pqin2->userdata;
    pqin2->userdata = temp;

    /* Now, swap the owner's pointers to them */
    assert(pqin1->owner != NULLPRIQNODE);
    assert(pqin2->owner != NULLPRIQNODE);
    assert(pqin1->owner->val == pqin1);
    assert(pqin2->owner->val == pqin2);

    tempqn = pqin1->owner;
    pqin1->owner = pqin2->owner;
    pqin2->owner = tempqn;
    pqin1->owner->val = pqin1;
    pqin2->owner->val = pqin2;
    }
