/* $Id: binaryheap.hpp 9662 2007-04-17 20:23:13Z belugas $ */

/** @file binaryheap.hpp */

#ifndef  BINARYHEAP_HPP
#define  BINARYHEAP_HPP

//void* operator new (size_t size, void* p) {return p;}
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
//void operator delete (void* p, void* p2) {}
#endif


/**
 * Binary Heap as C++ template.
 *
 * For information about Binary Heap algotithm,
 *   see: http://www.policyalmanac.org/games/binaryHeaps.htm
 *
 * Implementation specific notes:
 *
 * 1) It allocates space for item pointers (array). Items are allocated elsewhere.
 *
 * 2) ItemPtr [0] is never used. Total array size is max_items + 1, because we
 *    use indices 1..max_items instead of zero based C indexing.
 *
 * 3) Item of the binary heap should support these public members:
 *    - 'lower-then' operator '<' - used for comparing items before moving
 *
 */

template <class Titem_>
class CBinaryHeapT {
public:
	typedef Titem_ *ItemPtr;
private:
	int                     m_size;     ///< Number of items in the heap
	int                     m_max_size; ///< Maximum number of items the heap can hold
	ItemPtr*                m_items;    ///< The heap item pointers

public:
	explicit CBinaryHeapT(int max_items = 102400)
		: m_size(0)
		, m_max_size(max_items)
	{
		m_items = new ItemPtr[max_items + 1];
	}

	~CBinaryHeapT()
	{
		Clear();
		delete [] m_items;
		m_items = NULL;
	}

public:
	/** Return the number of items stored in the priority queue.
	 *  @return number of items in the queue */
	FORCEINLINE int Size() const {return m_size;};

	/** Test if the priority queue is empty.
	 *  @return true if empty */
	FORCEINLINE bool IsEmpty() const {return (m_size == 0);};

	/** Test if the priority queue is full.
	 *  @return true if full. */
	FORCEINLINE bool IsFull() const {return (m_size >= m_max_size);};

	/** Find the smallest item in the priority queue.
	 *  Return the smallest item, or throw assert if empty. */
	FORCEINLINE Titem_& GetHead() {assert(!IsEmpty()); return *m_items[1];}

	/** Insert new item into the priority queue, maintaining heap order.
	 *  @return false if the queue is full. */
	bool Push(Titem_& new_item);

	/** Remove and return the smallest item from the priority queue. */
	FORCEINLINE Titem_& PopHead() {Titem_& ret = GetHead(); RemoveHead(); return ret;};

	/** Remove the smallest item from the priority queue. */
	void RemoveHead();

	/** Remove item specified by index */
	void RemoveByIdx(int idx);

	/** return index of the item that matches (using &item1 == &item2) the given item. */
	int FindLinear(const Titem_& item) const;

	/** Make the priority queue empty.
	 * All remaining items will remain untouched. */
	void Clear() {m_size = 0;};

	/** verifies the heap consistency (added during first YAPF debug phase) */
	void CheckConsistency();
};


template <class Titem_>
FORCEINLINE bool CBinaryHeapT<Titem_>::Push(Titem_& new_item)
{
	if (IsFull()) return false;

	// make place for new item
	int gap = ++m_size;
	// Heapify up
	for (int parent = gap / 2; (parent > 0) && (new_item < *m_items[parent]); gap = parent, parent /= 2)
		m_items[gap] = m_items[parent];
	m_items[gap] = &new_item;
	CheckConsistency();
	return true;
}

template <class Titem_>
FORCEINLINE void CBinaryHeapT<Titem_>::RemoveHead()
{
	assert(!IsEmpty());

	// at index 1 we have a gap now
	int gap = 1;

	// Heapify down:
	//   last item becomes a candidate for the head. Call it new_item.
	Titem_& new_item = *m_items[m_size--];

	// now we must maintain relation between parent and its children:
	//   parent <= any child
	// from head down to the tail
	int child  = 2; // first child is at [parent * 2]

	// while children are valid
	while (child <= m_size) {
		// choose the smaller child
		if (child < m_size && *m_items[child + 1] < *m_items[child])
			child++;
		// is it smaller than our parent?
		if (!(*m_items[child] < new_item)) {
			// the smaller child is still bigger or same as parent => we are done
			break;
		}
		// if smaller child is smaller than parent, it will become new parent
		m_items[gap] = m_items[child];
		gap = child;
		// where do we have our new children?
		child = gap * 2;
	}
	// move last item to the proper place
	if (m_size > 0) m_items[gap] = &new_item;
	CheckConsistency();
}

template <class Titem_>
inline void CBinaryHeapT<Titem_>::RemoveByIdx(int idx)
{
	// at position idx we have a gap now
	int gap = idx;
	Titem_& last = *m_items[m_size];
	if (idx < m_size) {
		assert(idx >= 1);
		m_size--;
		// and the candidate item for fixing this gap is our last item 'last'
		// Move gap / last item up:
		while (gap > 1)
		{
			// compare [gap] with its parent
			int parent = gap / 2;
			if (last < *m_items[parent]) {
				m_items[gap] = m_items[parent];
				gap = parent;
			} else {
				// we don't need to continue upstairs
				break;
			}
		}

		// Heapify (move gap) down:
		while (true) {
			// where we do have our children?
			int child  = gap * 2; // first child is at [parent * 2]
			if (child > m_size) break;
			// choose the smaller child
			if (child < m_size && *m_items[child + 1] < *m_items[child])
				child++;
			// is it smaller than our parent?
			if (!(*m_items[child] < last)) {
				// the smaller child is still bigger or same as parent => we are done
				break;
			}
			// if smaller child is smaller than parent, it will become new parent
			m_items[gap] = m_items[child];
			gap = child;
		}
		// move parent to the proper place
		if (m_size > 0) m_items[gap] = &last;
	}
	else {
		assert(idx == m_size);
		m_size--;
	}
	CheckConsistency();
}

template <class Titem_>
inline int CBinaryHeapT<Titem_>::FindLinear(const Titem_& item) const
{
	if (IsEmpty()) return 0;
	for (ItemPtr *ppI = m_items + 1, *ppLast = ppI + m_size; ppI <= ppLast; ppI++) {
		if (*ppI == &item) {
			return ppI - m_items;
		}
	}
	return 0;
}

template <class Titem_>
FORCEINLINE void CBinaryHeapT<Titem_>::CheckConsistency()
{
	// enable it if you suspect binary heap doesn't work well
#if 0
	for (int child = 2; child <= m_size; child++) {
		int parent = child / 2;
		assert(!(m_items[child] < m_items[parent]));
	}
#endif
}


#endif /* BINARYHEAP_HPP */
