// $Id: PriorityQueue.cc,v 1.9 2003/06/15 19:18:33 vern Exp $
//
// Copyright (c) 1996, 1997, 1998, 1999, 2001, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "config.h"

#include <stdio.h>
#include <stdlib.h>

#include "PriorityQueue.h"
#include "util.h"

PriorityQueue::PriorityQueue(int initial_size)
	{
	max_heap_size = initial_size;
	heap = new PQ_Element*[max_heap_size];
	peak_heap_size = heap_size = 0;
	}

PriorityQueue::~PriorityQueue()
	{
	for ( int i = 0; i < heap_size; ++i )
		delete heap[i];

	delete heap;
	}

PQ_Element* PriorityQueue::Remove()
	{
	if ( heap_size == 0 )
		return 0;

	PQ_Element* top = heap[0];

	--heap_size;
	heap[0] = heap[heap_size];

	BubbleDown(0);

	return top;
	}

int PriorityQueue::Add(PQ_Element* e)
	{
	heap[heap_size] = e;

	BubbleUp(heap_size);

	if ( ++heap_size > peak_heap_size )
		peak_heap_size = heap_size;

	if ( heap_size >= max_heap_size )
		return Resize(max_heap_size * 2);
	else
		return 1;
	}

int PriorityQueue::Resize(int new_size)
	{
	max_heap_size = new_size;
	heap = (PQ_Element**)
		safe_realloc((void *) heap, max_heap_size * sizeof(PQ_Element*));

	return heap != 0;
	}

void PriorityQueue::BubbleUp(int bin)
	{
	if ( bin == 0 )
		return;

	int p = Parent(bin);
	if ( heap[p]->Time() > heap[bin]->Time() )
		{
		Swap(p, bin);
		BubbleUp(p);
		}
	}

void PriorityQueue::BubbleDown(int bin)
	{
	double v = heap[bin]->Time();

	int l = LeftChild(bin);
	int r = RightChild(bin);

	if ( l >= heap_size )
		return;		// No children.

	if ( r >= heap_size )
		{ // Just a left child.
		if ( heap[l]->Time() < v )
			Swap(l, bin);
		}

	else
		{
		double lv = heap[l]->Time();
		double rv = heap[r]->Time();

		if ( lv < rv )
			{
			if ( lv < v )
				{
				Swap(l, bin);
				BubbleDown(l);
				}
			}

		else if ( rv < v )
			{
			Swap(r, bin);
			BubbleDown(r);
			}
		}
	}
