/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
* PriorQue.c - implement priority queue, using regular binary trees	     *
* (that guarantees only average time of NlogN...) and with the following     *
* operations:								     *
* 1. PQInit(&PQ) - initialize the queue, must be called before usage.	     *
* 2. PQEmpty(PQ) - returns TRUE iff PQ is empty.			     *
* 3. PQCompFunc(&CompFunc) - sets (a pointer to) the function that is	     *
*    used to compere two items in the queue. the function should get two     *
*    item pointers, and should return >1, 0, <1 as comparison result for     *
*    greater than, equal, or less than respectively.			     *
* 4. PQFirst(&PQ, Delete) - returns the first element of the priority	     *
*    queue, and delete it from queue if delete is TRUE.			     *
* 5. PQInsert(&PQ, NewItem) - insert NewItem into the queue		     *
*    (NewItem is a pointer to it), using the comparison function CompFunc.   *
* 6. PQDelete(&PQ, OldItem) - Delete old item from the queue		     *
*    again using the comparison function CompFunc.			     *
* 7. PQNext(PQ, CmpItem, NULL) - returns the smallest item which is bigger   *
*    than given item CmpItem. PQ is not modified. Return NULL if none.	     *
* 8. PQPrint(PQ, &PrintFunc) - print the queue in order using the given      *
*    printing function PrintFunc.					     *
* 9. PQFree(&PQ, FreeItems) - Free the queue. The items are also freed if    *
*    FreeItems is TRUE.							     *
*									     *
* 			Written by Gershon Elber,   Dec 88                   *
*****************************************************************************/

#include <stdio.h>
#include "program.h"
#include "priorql.h"
#include "priorqg.h"
#include "allocatg.h"

static int (*CompFunc)();     /* Pointer to comparison function (see above). */

/******************************************************************************
* PQEmpty - initialize the queue...					      *
******************************************************************************/
void PQInit(PriorQue **PQ)
{
    *PQ = NULL;
}

/******************************************************************************
* PQEmpty - returns TRUE iff PQ priority queue is empty.		      *
******************************************************************************/
int PQEmpty(PriorQue *PQ)
{
    return PQ == NULL;
}
/******************************************************************************
* PQCompFunc - sets (a pointer to) the function that is used to compere two   *
* items in the queue. The function should get two item pointers, and should   *
* return >1, 0, <1 as comparison result for greater than, equal, or less than *
* respectively.								      *
******************************************************************************/
void PQCompFunc(int (*PQCompFunc)())
{
    CompFunc = PQCompFunc;
}

/******************************************************************************
* PQFirst - returns the first element of the priority queue, and delete it    *
* from queue if Delete is TRUE. return NULL if empty queue		      *
******************************************************************************/
VoidPtr PQFirst(PriorQue **PQ, int Delete)
{
    VoidPtr Data;
    PriorQue *Ptemp = (*PQ);

    if (*PQ == NULL) return NULL;

    while (Ptemp -> Right != NULL)
	Ptemp = Ptemp -> Right;			      /* Find smallest item. */
    Data = Ptemp -> Data;

    if (Delete) PQDelete(PQ, Data);

    return Data;
}

/******************************************************************************
* PQInsert - insert new item into the queue (NewItem is a pointer to it),     *
* using given compare function CompFunc. CompFunc should return >1, 0, <1     *
* as two item comparison result for greater than, equal, or less than         *
* respectively.								      *
*   Insert is always as a leaf of the tree.				      *
*   Return pointer to old data if was replaced or NULL if the item is new.    *
******************************************************************************/
VoidPtr PQInsert(PriorQue **PQ, VoidPtr NewItem)
{
    int Compare;
    VoidPtr Data;

    if ((*PQ) == NULL) {
	PQ_NEW_NODE(*PQ);
	(*PQ) -> Data = NewItem;
	return NULL;
    }
    else {
	Compare = (*CompFunc)(NewItem, (*PQ) -> Data);
	Compare = SIGN(Compare);
	switch (Compare) {
	    case -1:
		return PQInsert(&((*PQ) -> Right), NewItem);
	    case 0:
		Data = (*PQ) -> Data;
		(*PQ) -> Data = NewItem;
		return Data;
	    case 1:
		return PQInsert(&((*PQ) -> Left), NewItem);
	}
    }
    return NULL;				    /* Makes warning silent. */
}

/******************************************************************************
* PQDelete - Delete old item from the queue, again using the comparison       *
* function CompFunc.							      *
* Returns pointer to Deleted item if was fould and deleted, NULL otherwise.   *
******************************************************************************/
VoidPtr PQDelete(PriorQue **PQ, VoidPtr OldItem)
{
    int Compare;
    PriorQue *Ptemp;
    VoidPtr Data;
    VoidPtr OldData;

    if ((*PQ) == NULL) {
	return NULL;
    }
    else {
	Compare = (*CompFunc)(OldItem, (*PQ) -> Data);
	Compare = SIGN(Compare);
	switch (Compare) {
	    case -1:
		return PQDelete(&((*PQ) -> Right), OldItem);
	    case 0:
		OldData = (*PQ) -> Data;       /* The returned deleted item. */

		if ((*PQ) -> Right == NULL && (*PQ) -> Left == NULL) {
		    /* Thats easy - we delete a leaf: */
		    Data = (*PQ) -> Data;
		    PQ_FREE_NODE(*PQ); /* Note *PQ is also set to NULL here. */
		}
		else
		if ((*PQ) -> Right != NULL) {
		    /* replace this node by the biggest in the Right branch: */
		    /* move once to the Right and then Left all the time...  */
		    Ptemp = (*PQ) -> Right;
		    if (Ptemp -> Left != NULL) {
			while (Ptemp -> Left -> Left != NULL)
			    Ptemp = Ptemp -> Left;
			Data = Ptemp -> Left -> Data;   /*Save data pointer. */
			PQDelete(&(Ptemp -> Left), Data);  /* Delete recurs. */
			(*PQ) -> Data = Data; /* And put that data instead...*/
		    }
		    else {
			Data = Ptemp -> Data;      /* Save the data pointer. */
			PQDelete(&((*PQ) -> Right), Data); /* Delete recurs. */
			(*PQ) -> Data = Data; /* And put that data instead...*/
		    }
		}
		else {					    /* Left != NULL. */
		    /* replace this node by the biggest in the Left branch:  */
		    /* move once to the Left and then Right all the time...  */
		    Ptemp = (*PQ) -> Left;
		    if (Ptemp -> Right != NULL)	{
			while (Ptemp -> Right -> Right != NULL)
			    Ptemp = Ptemp -> Right;
			Data = Ptemp -> Right -> Data; /* Save data pointer. */
			PQDelete(&(Ptemp -> Right), Data);  /*Delete recurs. */
			(*PQ) -> Data = Data; /* And put that data instead...*/
		    }
		    else {
			Data = Ptemp -> Data;      /* Save the data pointer. */
			PQDelete(&((*PQ) -> Left), Data);  /* Delete recurs. */
			(*PQ) -> Data = Data; /* And put that data instead...*/
		    }
		}
		return OldData;
	    case 1:
		return PQDelete(&((*PQ) -> Left), OldItem);
	}
    }
    return NULL;				    /* Makes warning silent. */
}

/******************************************************************************
* PQNext - returns the smallest item which is bigger than given item CmpItem. *
* PQ is not modified. Return NULL if none was found.			      *
* BiggerThan will allways hold the smallest Item bigger than current one.     *
******************************************************************************/
VoidPtr PQNext(PriorQue *PQ, VoidPtr CmpItem, VoidPtr BiggerThan)
{
    int Compare;
    PriorQue *Ptemp;

    if (PQ == NULL) return BiggerThan;
    else {
	Compare = (*CompFunc)(CmpItem, PQ -> Data);
	Compare = SIGN(Compare);
	switch (Compare) {
	    case -1:
		return PQNext(PQ -> Right, CmpItem, PQ -> Data);
	    case 0:
		/* Found it - if its right tree is not empty, returns its    */
		/* smallest, else returns BiggerThan...			     */
		if (PQ -> Left != NULL) {
		    Ptemp = PQ -> Left;
		    while (Ptemp -> Right != NULL) Ptemp = Ptemp -> Right;
		    return Ptemp -> Data;
		}
		else return BiggerThan;
	    case 1:
		return PQNext(PQ -> Left, CmpItem, BiggerThan);
	}
    }
    return NULL;				    /* Makes warning silent. */
}

/******************************************************************************
* PQPrint - print the queue in order using the given printing functionq       *
* PrintFunc.								      *
******************************************************************************/
void PQPrint(PriorQue *PQ, void (*PrintFunc)())
{
    if (PQ == NULL) return;

    PQPrint(PQ -> Right, PrintFunc);

    (*PrintFunc)(PQ -> Data);

    PQPrint(PQ -> Left, PrintFunc);
}

/******************************************************************************
* PQFree - Free the queue. The items are also freed if FreeItems is TRUE.     *
******************************************************************************/
void PQFree(PriorQue *PQ, int FreeItem)
{
    if (PQ == NULL) return;

    PQFree(PQ -> Right, FreeItem);
    PQFree(PQ -> Left, FreeItem);

    if (FreeItem) MyFree((char *) PQ -> Data, OTHER_TYPE);
    MyFree((char *) PQ, OTHER_TYPE);
}

