/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2001 Apple Computer, Inc.  All Rights Reserved. The
 * contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License.  Please
 * obtain a copy of the License at http://www.apple.com/publicsource and
 * read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.  Please
 * see the License for the specific language governing rights and
 * limitations under the License.
 *
 *
 * @APPLE_LICENSE_HEADER_END@
 *
 */
/*
	File:		OSHeap.cpp

	Contains:	Implements a heap
					
	
	
*/

#include <string.h>

#include "OSHeap.h"
#include "OSMemory.h"

OSHeap::OSHeap(UInt32 inStartSize)
: fFreeIndex(1)
{
	if (inStartSize < 2)
		fArraySize = 2;
	else
		fArraySize = inStartSize;
		
	fHeap = NEW OSHeapElem*[fArraySize];
}

void OSHeap::Insert(OSHeapElem* inElem)
{
	Assert(inElem != NULL);
	
	if ((fHeap == NULL) || (fFreeIndex == fArraySize))
	{
		fArraySize *= 2;
		OSHeapElem** tempArray = NEW OSHeapElem*[fArraySize];
		if ((fHeap != NULL) && (fFreeIndex > 1))
			memcpy(tempArray, fHeap, sizeof(OSHeapElem*) * fFreeIndex);
			
		delete [] fHeap;
		fHeap = tempArray;
	}
	
	Assert(fHeap != NULL);
	Assert(inElem->fCurrentHeap == NULL);
	Assert(fArraySize > fFreeIndex);
	
#if _OSHEAP_TESTING_
	SanityCheck(1);
#endif

	//insert the element into the last leaf of the tree
	fHeap[fFreeIndex] = inElem;
	
	//bubble the new element up to its proper place in the heap
	
	//start at the last leaf of the tree
	UInt32 swapPos = fFreeIndex;
	while (swapPos > 1)
	{
		//move up the chain until we get to the root, bubbling this new element
		//to its proper place in the tree
		UInt32 nextSwapPos = swapPos >> 1;
		
		//if this child is greater than it's parent, we need to do the old
		//switcheroo
		if (fHeap[swapPos]->fValue < fHeap[nextSwapPos]->fValue)
		{
			OSHeapElem* temp = fHeap[swapPos];
			fHeap[swapPos] = fHeap[nextSwapPos];
			fHeap[nextSwapPos] = temp;
			swapPos = nextSwapPos;
		}
		else
			//if not, we are done!
			break;
	}
	inElem->fCurrentHeap = this;
	fFreeIndex++;
}


OSHeapElem*	OSHeap::Extract(UInt32 inIndex)
{
	if ((fHeap == NULL) || (fFreeIndex <= inIndex))
		return NULL;
		
#if _OSHEAP_TESTING_
	SanityCheck(1);
#endif
	
	//store a reference to the element we want to extract
	OSHeapElem* victim = fHeap[inIndex];
	Assert(victim->fCurrentHeap == this);
	victim->fCurrentHeap = NULL;
	
	//but now we need to preserve this heuristic. We do this by taking
	//the last leaf, putting it at the empty position, then heapifying that chain
	fHeap[inIndex] = fHeap[fFreeIndex - 1];
	fFreeIndex--;
	
	//The following is an implementation of the Heapify algorithm (CLR 7.1 pp 143)
	//The gist is that this new item at the top of the heap needs to be bubbled down
	//until it is bigger than its two children, therefore maintaining the heap property.
	
	UInt32 parent = inIndex;
	while (parent < fFreeIndex)
	{
		//which is bigger? parent or left child?
		UInt32 greatest = parent;
		UInt32 leftChild = parent * 2;
		if ((leftChild < fFreeIndex) && (fHeap[leftChild]->fValue < fHeap[parent]->fValue))
			greatest = leftChild;

		//which is bigger? the biggest so far or the right child?
		UInt32 rightChild = (parent * 2) + 1;
		if ((rightChild < fFreeIndex) && (fHeap[rightChild]->fValue < fHeap[greatest]->fValue))
			greatest = rightChild;
		 
		//if the parent is in fact bigger than its two children, we have bubbled
		//this element down far enough
		if (greatest == parent)
			break;
			
		//parent is not bigger than at least one of its two children, so swap the parent
		//with the largest item.
		OSHeapElem* temp = fHeap[parent];
		fHeap[parent] = fHeap[greatest];
		fHeap[greatest] = temp;
		
		//now heapify the remaining chain
		parent = greatest;
	}
	
	return victim;
}

OSHeapElem*	OSHeap::Remove(OSHeapElem* elem)
{
	if ((fHeap == NULL) || (fFreeIndex == 1))
		return NULL;
		
#if _OSHEAP_TESTING_
	SanityCheck(1);
#endif

	//first attempt to locate this element in the heap
	UInt32 theIndex = 1;
	for ( ; theIndex < fFreeIndex; theIndex++)
		if (elem == fHeap[theIndex])
			break;
			
	//either we've found it, or this is a bogus element
	if (theIndex == fFreeIndex)
		return NULL;
		
	return Extract(theIndex);
}


#if _OSHEAP_TESTING_

void OSHeap::SanityCheck(UInt32 root)
{
	//make sure root is greater than both its children. Do so recursively
	if (root < fFreeIndex)
	{
		if ((root * 2) < fFreeIndex)
		{
			Assert(fHeap[root]->fValue <= fHeap[root * 2]->fValue);
			SanityCheck(root * 2);
		}
		if (((root * 2) + 1) < fFreeIndex)
		{
			Assert(fHeap[root]->fValue <= fHeap[(root * 2) + 1]->fValue);
			SanityCheck((root * 2) + 1);
		}
	}
}


Bool16 OSHeap::Test()
{
	OSHeap victim(2);
	OSHeapElem elem1;
	OSHeapElem elem2;
	OSHeapElem elem3;
	OSHeapElem elem4;
	OSHeapElem elem5;
	OSHeapElem elem6;
	OSHeapElem elem7;
	OSHeapElem elem8;
	OSHeapElem elem9;

	OSHeapElem* max = victim.ExtractMin();
	if (max != NULL)
		return false;
		
	elem1.SetValue(100);
	victim.Insert(&elem1);
	
	max = victim.ExtractMin();
	if (max != &elem1)
		return false;
	max = victim.ExtractMin();
	if (max != NULL)
		return false;
	
	elem1.SetValue(100);
	elem2.SetValue(80);
	
	victim.Insert(&elem1);
	victim.Insert(&elem2);
	
	max = victim.ExtractMin();
	if (max != &elem2)
		return false;
	max = victim.ExtractMin();
	if (max != &elem1)
		return false;
	max = victim.ExtractMin();
	if (max != NULL)
		return false;
	
	victim.Insert(&elem2);
	victim.Insert(&elem1);

	max = victim.ExtractMin();
	if (max != &elem2)
		return false;
	max = victim.ExtractMin();
	if (max != &elem1)
		return false;
		
	elem3.SetValue(70);
	elem4.SetValue(60);

	victim.Insert(&elem3);
	victim.Insert(&elem1);
	victim.Insert(&elem2);
	victim.Insert(&elem4);
	
	max = victim.ExtractMin();
	if (max != &elem4)
		return false;
	max = victim.ExtractMin();
	if (max != &elem3)
		return false;
	max = victim.ExtractMin();
	if (max != &elem2)
		return false;
	max = victim.ExtractMin();
	if (max != &elem1)
		return false;

	elem5.SetValue(50);
	elem6.SetValue(40);
	elem7.SetValue(30);
	elem8.SetValue(20);
	elem9.SetValue(10);

	victim.Insert(&elem5);
	victim.Insert(&elem3);
	victim.Insert(&elem1);
	
	max = victim.ExtractMin();
	if (max != &elem5)
		return false;
	
	victim.Insert(&elem4);
	victim.Insert(&elem2);

	max = victim.ExtractMin();
	if (max != &elem4)
		return false;
	max = victim.ExtractMin();
	if (max != &elem3)
		return false;
	
	victim.Insert(&elem2);

	max = victim.ExtractMin();
	if (max != &elem2)
		return false;

	victim.Insert(&elem2);
	victim.Insert(&elem6);

	max = victim.ExtractMin();
	if (max != &elem6)
		return false;

	victim.Insert(&elem6);
	victim.Insert(&elem3);
	victim.Insert(&elem4);
	victim.Insert(&elem5);

	max = victim.ExtractMin();
	if (max != &elem6)
		return false;
	max = victim.ExtractMin();
	if (max != &elem5)
		return false;

	victim.Insert(&elem8);
	max = victim.ExtractMin();
	if (max != &elem8)
		return false;
	max = victim.ExtractMin();
	if (max != &elem4)
		return false;
		
	victim.Insert(&elem5);
	victim.Insert(&elem4);
	victim.Insert(&elem9);
	victim.Insert(&elem7);
	victim.Insert(&elem8);
	victim.Insert(&elem6);

	max = victim.ExtractMin();
	if (max != &elem9)
		return false;
	max = victim.ExtractMin();
	if (max != &elem8)
		return false;
	max = victim.ExtractMin();
	if (max != &elem7)
		return false;
	max = victim.ExtractMin();
	if (max != &elem6)
		return false;
	max = victim.ExtractMin();
	if (max != &elem5)
		return false;
	max = victim.ExtractMin();
	if (max != &elem4)
		return false;
	max = victim.ExtractMin();
	if (max != &elem3)
		return false;
	max = victim.ExtractMin();
	if (max != &elem2)
		return false;
	max = victim.ExtractMin();
	if (max != &elem2)
		return false;
	max = victim.ExtractMin();
	if (max != &elem1)
		return false;
	max = victim.ExtractMin();
	if (max != NULL)
		return false;
		
	victim.Insert(&elem1);
	victim.Insert(&elem2);
	victim.Insert(&elem3);
	victim.Insert(&elem4);
	victim.Insert(&elem5);
	victim.Insert(&elem6);
	victim.Insert(&elem7);
	victim.Insert(&elem8);
	victim.Insert(&elem9);
	
	max = victim.Remove(&elem7);
	if (max != &elem7)
		return false;
	max = victim.Remove(&elem9);
	if (max != &elem9)
		return false;
	max = victim.ExtractMin();
	if (max != &elem8)
		return false;
	max = victim.Remove(&elem2);
	if (max != &elem2)
		return false;
	max = victim.Remove(&elem2);
	if (max != NULL)
		return false;
	max = victim.Remove(&elem8);
	if (max != NULL)
		return false;
	max = victim.Remove(&elem5);
	if (max != &elem5)
		return false;
	max = victim.Remove(&elem6);
	if (max != &elem6)
		return false;
	max = victim.Remove(&elem1);
	if (max != &elem1)
		return false;
	max = victim.ExtractMin();
	if (max != &elem4)
		return false;
	max = victim.Remove(&elem1);
	if (max != NULL)
		return false;
	
	return true;
}
#endif
