/* $Id: tess_heap.c,v 1.9 1999/10/13 19:12:48 gareth Exp $ */

/*
 * Mesa 3-D graphics library
 * Version:  3.1
 *
 * Copyright (C) 1999  Brian Paul   All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*****************************************************************************
 *
 * GLU 1.3 Polygon Tessellation - Implementation of priority queue
 *
 * Gareth Hughes <garethh@bell-labs.com>, April 1999
 *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glu.h>

#include "gluP.h"

#include "tess.h"
#include "tess_macros.h"
#include "tess_heap.h"

/*****************************************************************************
 * Internal function prototypes:
 *****************************************************************************/

#define PARENT(i)		(((i+1)>>1)-1)
#define LEFT(i)			(((i+1)<<1)-1)
#define RIGHT(i)		((i+1)<<1)

static void heapify( heap_t *heap, GLuint n );


/*****************************************************************************
 *
 *				HEAP FUNCTIONS
 *
 *****************************************************************************/


/*****************************************************************************
 * heap_init
 *
 * Allocate and initialize a new heap.
 *****************************************************************************/
heap_t *heap_init()
{
    heap_t	*heap;
    GLuint	i;

    heap = (heap_t *) malloc( sizeof(heap_t) );
    if ( heap == NULL ) {
	return NULL;
    }

    heap->elements = (heap_elt_t **)
	malloc( HEAP_ALLOC * sizeof(heap_elt_t *) );
    if ( heap->elements == NULL ) {
	free( heap );
	return NULL;
    }

    heap->length = HEAP_ALLOC;
    heap->size = 0;
    heap->flags = 0;

    for ( i = 0 ; i < heap->length ; i++ ) {
	heap->elements[ i ] = NULL;
    }

    return heap;
}


/*****************************************************************************
 * heap_build
 *
 * Build a heap from an unordered array.
 *****************************************************************************/
void heap_build( heap_t *heap )
{
    GLint	i;

    heap->size = heap->length;

    for ( i = PARENT( heap->length ) ; i >= 0 ; i-- ) {
	heapify( heap, i );
    }
}


/*****************************************************************************
 * heap_extract_max
 *
 * Remove and return the maximum element in the heap.
 *****************************************************************************/
heap_elt_t *heap_extract_max( heap_t *heap )
{
    heap_elt_t	*max;

    if ( heap->size < 1 ) {
	return NULL;
    }

    max = heap->elements[ 0 ];

    heap->elements[ 0 ] = heap->elements[ heap->size - 1 ];
    heap->elements[ 0 ]->index = 0;

    heap->size--;

    heapify( heap, 0 );

    return max;
}


/*****************************************************************************
 * heap_insert
 *
 * Insert a new element into the heap.
 *****************************************************************************/
GLboolean heap_insert( heap_t *heap, heap_elt_t *element )
{
    GLuint	i;

    heap->size++;

    if ( heap->size > heap->length )
    {
	/* Allocate some more space for the heap. */
	if ( ( heap->elements =
	           realloc( heap->elements, ( heap->length + HEAP_ALLOC )
			    * sizeof(heap_elt_t *) ) ) == NULL )
	{
	    return GL_FALSE;
	}

	heap->length += HEAP_ALLOC;
    }

    i = heap->size - 1;

    while ( ( i > 0 ) &&
	    ( heap->elements[ PARENT( i ) ]->value < element->value ) )
    {
	heap->elements[ i ] = heap->elements[ PARENT( i ) ];
	heap->elements[ i ]->index = i;

	i = PARENT( i );
    }

    heap->elements[ i ] = element;
    heap->elements[ i ]->index = i;

    return GL_TRUE;
}


/*****************************************************************************
 * heap_delete
 *
 * Delete element n from the heap.
 *****************************************************************************/
heap_elt_t *heap_delete( heap_t *heap, GLuint n )
{
    heap_elt_t	*element;

    if ( ( heap->size < 1 ) || ( n >= heap->size ) ) {
	return NULL;
    }

    element = heap->elements[ n ];

    heap->elements[ n ] = heap->elements[ heap->size - 1 ];
    heap->elements[ n ]->index = n;

    heap->size--;

    heapify( heap, n );

    return element;
}


/*****************************************************************************
 * heap_cleanup
 *
 * Deallocate all memory associated with the heap.
 *****************************************************************************/
void heap_cleanup( heap_t **heap )
{
    GLuint	i;

    if ( *heap )
    {
	if ( (*heap)->elements )
	{
	    for ( i = 0; i < (*heap)->size; i++ )
	    {
		if ( (*heap)->elements[ i ] ) {
		    free( (*heap)->elements[ i ] );
		}
	    }
	    free( (*heap)->elements );
	}
	free( *heap );
	*heap = NULL;
    }
}



/*****************************************************************************
 *
 *				INTERNAL FUNCTIONS
 *
 *****************************************************************************/


/*****************************************************************************
 * heapify
 *
 * Heapify the heap :)
 *****************************************************************************/
static void heapify( heap_t *heap, GLuint n )
{
    GLuint	left = LEFT( n );
    GLuint	right = RIGHT( n );
    GLuint	largest;

    if ( ( left < heap->size ) &&
	 ( heap->elements[ left ]->value > heap->elements[ n ]->value ) )
    {
	largest = left;
    }
    else
    {
	largest = n;
    }

    if ( ( right < heap->size ) &&
	 ( heap->elements[ right ]->value >
	   heap->elements[ largest ]->value ) )
    {
	largest = right;
    }

    if ( largest != n )
    {
	heap_elt_t	*element;

	element = heap->elements[ n ];

	heap->elements[ n ] = heap->elements[ largest ];
	heap->elements[ n ]->index = n;

	heap->elements[ largest ] = element;
	heap->elements[ largest ]->index = largest;

	heapify( heap, largest );
    }
}
