/*
 * heap.c -
 *
 *	Routines to create and manipulate heaps.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/utils/heap.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/heap.h"
#include "utils/utils.h"
#include "utils/malloc.h"

/* C99 compat */
#include "textio/textio.h"

#define heapLeft(used,i)  ((used) < ( (i)<<1)    ? 0 : (i)+(i)  )
#define heapRight(used,i) ((used) < (((i)<<1)+1) ? 0 : (i)+(i)+1)

#define	KEY_LESS_COND(t, l, i1, i2, stmt) \
	switch (t) { \
	    case HE_INT: if (l[i1].he_int < l[i2].he_int) stmt; \
		break; \
	    case HE_DLONG: if ((l[i1].he_dlong < l[i2].he_dlong)) stmt; \
		break; \
	    case HE_DOUBLE: if (l[i1].he_double < l[i2].he_double) stmt; \
		break; \
	    case HE_FLOAT: if (l[i1].he_float < l[i2].he_float) stmt; \
		break; \
	}

#define	KEY_LE_COND(t, l, i1, i2, stmt) \
	switch (t) { \
	    case HE_INT: if (l[i1].he_int <= l[i2].he_int) stmt; \
		break; \
	    case HE_DLONG: if ((l[i1].he_dlong <=l[i2].he_dlong)) stmt; \
		break; \
	    case HE_DOUBLE: if (l[i1].he_double <= l[i2].he_double) stmt; \
		break; \
	    case HE_FLOAT: if (l[i1].he_float <= l[i2].he_float) stmt; \
		break; \
	}

#define	KEY_GREATER_COND(t, l, i1, i2, stmt) \
	switch (t) { \
	    case HE_INT: if (l[i1].he_int > l[i2].he_int) stmt; \
		break; \
	    case HE_DLONG:if(l[i1].he_dlong > l[i2].he_dlong) stmt; \
		break; \
	    case HE_DOUBLE: if (l[i1].he_double > l[i2].he_double) stmt; \
		break; \
	    case HE_FLOAT: if (l[i1].he_float > l[i2].he_float) stmt; \
		break; \
	}

#define	KEY_GE_COND(t, l, i1, i2, stmt) \
	switch (t) { \
	    case HE_INT: if (l[i1].he_int >= l[i2].he_int) stmt; \
		break; \
	    case HE_DLONG: if ((l[i1].he_dlong >= l[i2].he_dlong)) stmt; \
		break; \
	    case HE_DOUBLE: if (l[i1].he_double >= l[i2].he_double) stmt; \
		break; \
	    case HE_FLOAT: if (l[i1].he_float >= l[i2].he_float) stmt; \
		break; \
	}

/* Forward declarations */

extern void heapify();


/*
 * ----------------------------------------------------------------------------
 *
 * HeapInit --
 * HeapInitType --
 *
 * Initialize a heap.  The first form is a heap with integer keys; the
 * second allows specification of the type of key to use.
 *
 * Note that with this addressing scheme it is necessary to allocate
 * 2**n + 1 locations for the heap block, location 0 being unused.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Allocates storage for the heap.  Initializes the fields in the heap
 *	struct.  Memory is allocated.
 *
 * ----------------------------------------------------------------------------
 */

void
HeapInit(
    Heap *heap,		/* Pointer to a heap struct */
    int size,		/* Initial size of heap */
    int	descending,	/* TRUE if largest on top */
    int	stringIds)	/* TRUE if entry id's are strings */
{
    HeapInitType(heap, size, descending, stringIds, HE_INT);
}

void
HeapInitType(
    Heap *heap,		/* Pointer to a heap struct */
    int size,		/* Initial size of heap */
    int	descending,	/* TRUE if largest on top */
    int	stringIds,	/* TRUE if entry id's are strings */
    int keyType)	/* Type of keys to use */
{
    if (size < 0) size = -size;
    heap->he_size = 2;
    while (size > heap->he_size)
	heap->he_size <<= 1;
    heap->he_used = 0;
    heap->he_built = 0;
    heap->he_big = descending;
    heap->he_stringId = stringIds;

    /*
     * Add 2 to the size because location 0 is used as a temporary,
     * and location (2**n) + 1 is the last leaf
     */
    switch (heap->he_keyType = keyType)
    {
	case HE_INT:
	case HE_DLONG:
	case HE_FLOAT:
	case HE_DOUBLE:
	    break;
	default:
	    TxError("Unsupported key type: %d\n", keyType);
	    break;
    }

    heap->he_list = (HeapEntry *) mallocMagic((unsigned) ((heap->he_size+2)
		* sizeof (HeapEntry)));
    ASSERT(heap->he_list != NULL, "Malloc failed in HeapInit");
}

/*
 * ----------------------------------------------------------------------------
 *
 * HeapKill --
 *
 * Deallocate all storage associated with the heap.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Storage is freed.  Fields in the heap record are reset.  If func is
 *	not NULL then call it with each heap element before freeing the heap
 *	entry.
 *
 * ----------------------------------------------------------------------------
 */

void
HeapKill(
    Heap *heap,			/* The heap being killed */
    cb_heap_kill_t func)	/* Some function to free each id element */
{
    int i;

    /*
     * Free used locations except 0, since that location is reserved to pass
     * back values.
     */
    if (func)
        for (i = 1; i <= heap->he_used; i++)
	   (*func) (heap, i); /** @invoke cb_heap_kill_t */
    freeMagic((char *) heap->he_list);
    heap->he_list = NULL;
}

/*
 * ----------------------------------------------------------------------------
 *
 * HeapFreeIdFunc --
 *
 * 	Supplied function to HeapKill.  Frees the referenced entry id if it
 *	is a string, otherwise do nothing.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory is freed.
 *
 * ----------------------------------------------------------------------------
 */

void
HeapFreeIdFunc(
    Heap *heap,	/* The heap in which the free is performed */
    int i)			/* The index of the entry whose id gets freed */
{
    if (heap->he_stringId)
	freeMagic(heap->he_list[i].he_id);
}

/*
 * ----------------------------------------------------------------------------
 *
 * HeapRemoveTop --
 *	Delete the top element from the heap.
 *
 * Results:
 *	Pointer to the removed heap element, which is the same as the entry
 *	pointer passed to the function from outside,  or NULL if no entries
 *	remain.
 *
 * Side effects:
 *	If the heap has been touched, restore the heap property before removing
 *	the top element.
 *
 * ----------------------------------------------------------------------------
 */

HeapEntry *
HeapRemoveTop(
    Heap *heap,	/* The heap from which the top is removed */
    HeapEntry *entry)		/* Return the value in this struct */
{
    int i;

    if (heap->he_used == 0)
	return (HeapEntry *) NULL;

    if (!heap->he_built)
	for (i = heap->he_used; i > 0; i--)
	    heapify(heap, i);

    heap->he_built = heap->he_used;
    *entry = heap->he_list[1];
    heap->he_list[1] = heap->he_list[heap->he_used];
    heap->he_used--;
    heapify(heap, 1);
    return entry;
}

/*
 * ----------------------------------------------------------------------------
 *
 * HeapLookAtTop --
 *
 * Return pointer to top element, but don't remove it.
 *
 * Results:
 *	Pointer to the top heap element, or NULL if heap is empty.
 *
 * Side effects:
 *	If the heap has been touched, restore the heap property before
 *      returning the top element.
 *
 * ----------------------------------------------------------------------------
 */

HeapEntry *
HeapLookAtTop(
    Heap *heap)
{
    int i;

    if (heap->he_used == 0)
	return (HeapEntry *) NULL;

    if (!heap->he_built)
	for (i = heap->he_used; i > 0; i--)
	    heapify(heap, i);

    heap->he_built = heap->he_used;
    return &heap->he_list[1];
}

/*
 * ----------------------------------------------------------------------------
 *
 * heapify --
 *
 * Restore the heap property to the heap, where the root has changed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Things get shuffled around in the heap.  Location 0 is trashed.
 *
 * ----------------------------------------------------------------------------
 */

void
heapify(
    Heap *heap,
    int root)
{
    HeapEntry *list = heap->he_list;
    int x, r, used = heap->he_used;
    int keyType = heap->he_keyType;

    if (heap->he_big)
    {
	/* Biggest one on the top of the heap */
	while (1)
	{
	    if ((x = heapLeft(used, root)) == 0) break;
	    if ((r = heapRight(used, root)))
		KEY_LESS_COND(keyType, list, x, r, x = r);

	    KEY_LE_COND(keyType, list, x, root, return);
	    *list = list[root];
	    list[root] = list[x];
	    list[x] = *list;
	    root = x;
	}
    }
    else
    {
	/* Smallest one on the top of the heap */
	while (1)
	{
	    if ((x = heapLeft(used, root)) == 0) break;
	    if ((r = heapRight(used, root)))
				KEY_GREATER_COND(keyType, list, x, r, x = r);
	    KEY_GE_COND(keyType, list, x, root, return);
	    *list = list[root];
	    list[root] = list[x];
	    list[x] = *list;
	    root = x;
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * HeapAdd --
 *
 * Add an item to the bottom of the heap.  Restore the heap structure
 * by propagating the item upwards.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Things get shuffled around in the heap.
 *	Free the old block and allocate a larger block if necessary.
 *
 * Warning:
 *	Something awful may happen if the id value was declared a string in
 *	HeapInit and you provide something else.
 *
 * Note:
 *	Because throwing pointers to indeterminate types (int, float, etc.)
 *	into the second argument produces results with unititialized fields,
 *	use of HeapAdd() is now deprecated, in favor of the determinate-type
 *	versions HeapAddInt, etc. (see below).  These routines also protect
 *	against writing any data type into a heap initialized as a different
 *	data type.
 *
 * ----------------------------------------------------------------------------
 */

void
HeapAdd(
    Heap *heap,
    union heUnion *pKey,
    char *id)
{
    HeapEntry *list = heap->he_list;
    int i, cmp, keyType;

    keyType = heap->he_keyType;
    if (heap->he_used == heap->he_size)
    {
	HeapEntry *new;

	/* Need to recopy to a larger area */
	new = (HeapEntry *) mallocMagic((unsigned) ((2 * heap->he_size + 2)
			* sizeof (HeapEntry)));
	bcopy((char *) list, (char *) new,
		(heap->he_size + 2) * sizeof (HeapEntry));
	freeMagic((char *) heap->he_list);
	heap->he_list = list = new;
	heap->he_size *= 2;
    }

    i = ++heap->he_used;
    list[i].he_union = *pKey;
    if (heap->he_stringId) list[i].he_id = StrDup((char **) NULL, id);
    else list[i].he_id = id;

    if (heap->he_built)
    {
	if (heap->he_big)
	{
	    /* Biggest on the top */
	    while (1)
	    {
		/* If odd then new entry is the right half of a pair */
		cmp = i;
		if ((i & 1) && (i != 1))
		    KEY_LE_COND(keyType, list, i, i-1, cmp = i-1);

		/* Find parent.  If 0 then at the root so quit */
		if ((i >>= 1) == 0) return;

		KEY_LE_COND(keyType, list, cmp, i, return);
		list[0] = list[cmp]; list[cmp] = list[i]; list[i] = list[0];
		heapify(heap, cmp);
	    }
	}
	else
	{
	    /* Smallest on top */
	    while (1)
	    {
		/* If odd then new entry is the right half of a pair */
		cmp = i;
		if ((i & 1) && (i != 1))
		    KEY_GE_COND(keyType, list, i, i-1, cmp = i-1);

		/* Find parent.  If 0 then at the root so quit */
		if ((i >>= 1) == 0) return;

		KEY_GE_COND(keyType, list, cmp, i, return);
		list[0] = list[cmp]; list[cmp] = list[i]; list[i] = list[0];
		heapify(heap, cmp);
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 * HeapAddInt, HeapAddDLong, HeapAddFloat, HeapAddDouble --
 *
 *	Type-determinate versions of HeapAdd().  Note that unlike HeapAdd(),
 *	the second argument is the value, not a pointer to the value.
 *
 * ----------------------------------------------------------------------------
 */
void
HeapAddInt(
    Heap *heap,
    int data,
    char *id)
{
    union heUnion pKey;

    ASSERT(heap->he_keyType == HE_INT,
		"Attempt to add type int to non-int heap.");
    pKey.hu_int = data;
    HeapAdd(heap, &pKey, id);
}

void
HeapAddDLong(
    Heap *heap,
    dlong data,
    char *id)
{
    union heUnion pKey;

    ASSERT(heap->he_keyType == HE_DLONG,
		"Attempt to add type double long to non-double long heap.");
    pKey.hu_dlong = data;
    HeapAdd(heap, &pKey, id);
}

void
HeapAddFloat(
    Heap *heap,
    float data,
    char *id)
{
    union heUnion pKey;

    ASSERT(heap->he_keyType == HE_FLOAT,
		"Attempt to add type float to non-float heap.");
    pKey.hu_float = data;
    HeapAdd(heap, &pKey, id);
}

void
HeapAddDouble(
    Heap *heap,
    double data,
    char *id)
{
    union heUnion pKey;

    ASSERT(heap->he_keyType == HE_DOUBLE,
		"Attempt to add type double to non-double heap.");
    pKey.hu_double = data;
    HeapAdd(heap, &pKey, id);
}


/*
 * ----------------------------------------------------------------------------
 *
 * HeapDump --
 * 	Dump the contents of the heap for debugging purposes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
HeapDump(
    Heap *heap)
{
    int i;

    if (heap->he_big) printf("Heap with biggest on the top\n");
    else printf("Heap with smallest on the top\n");

    for (i = 1; i <= heap->he_used; i++)
    {
	printf("[%d]: Key ", i);
	switch (heap->he_keyType)
	{
	    case HE_INT:	printf("%d", heap->he_list[i].he_int); break;
	    case HE_FLOAT:	printf("%f", heap->he_list[i].he_float); break;
	    case HE_DOUBLE:	printf("%f", heap->he_list[i].he_double); break;
	    case HE_DLONG:	printf("%"DLONG_PREFIX"d", heap->he_list[i].he_dlong); break;
	}
	if (heap->he_stringId == TRUE)
	    printf("//id %s; ", heap->he_list[i].he_id);
	else
	    printf("//id %p; ", heap->he_list[i].he_id);
    }
    printf("\n");
}
