/// \file
/// \brief \b [Internal] ADT that can represent an unordered list, ordered list, stack, or queue with a common interface
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.

#ifndef __MULTILIST_H
#define __MULTILIST_H 

#include "RakAssert.h"
#include <string.h> // memmove
#include "Export.h"
#include "RakMemoryOverride.h"
#include "NativeTypes.h"

/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
	/// What algorithm to use to store the data for the Multilist
	enum MultilistType
	{
		/// Removing from the middle of the list will swap the end of the list rather than shift the elements. Push and Pop operate on the tail.
		ML_UNORDERED_LIST,
		/// A normal list, with the list order preserved. Push and Pop operate on the tail.
		ML_STACK,
		/// A queue. Push and Pop operate on the head
		ML_QUEUE,
		/// A list that is always kept in order. Elements must be unique, and compare against each other consistently using <, ==, and >
		ML_ORDERED_LIST,
		/// A list whose type can change at runtime
		ML_VARIABLE_DURING_RUNTIME
	};

	/// Can be used with Multilist::ForEach
	/// Assuming the Multilist holds pointers, will delete those pointers
	template <class templateType>
	void DeletePtr_RakNet(templateType &ptr, const char *file, unsigned int line ) {RakNet::OP_DELETE(ptr, file, line);}

	/// Can be used with Multilist::ForEach
	/// Assuming the Multilist holds pointers, will delete those pointers
	template <class templateType>
	void DeletePtr(templateType &ptr) {delete ptr;}

	/// The following is invalid
	/// bool operator<( const MyClass *myClass, const int &inputKey ) {return myClass->value < inputKey;}
	/// At least one type has to be a reference to a class
	/// MLKeyRef is a helper class to turn a native type into a class, so you can compare that native type against a pointer to a different class
	/// Used for he Multilist, when _DataType != _KeyType 
	template < class templateType >
	class MLKeyRef
	{
	public:
		MLKeyRef(const templateType& input) : val(input) {}
		const templateType &Get(void) const {return val;}
		bool operator<( const templateType &right ) {return val < right;}
		bool operator>( const templateType &right ) {return val > right;}
		bool operator==( const templateType &right ) {return val == right;}
	protected:
		const templateType &val;
	};

	/// For the Multilist, when _DataType != _KeyType, you must define the comparison operators between the key and the data
	/// This is non-trivial due to the need to use MLKeyRef in case the type held is a pointer to a structure or class and the key type is not a class
	/// For convenience, this macro will implement the comparison operators under the following conditions
	/// 1. _DataType is a pointer to a class or structure
	/// 2. The key is a member variable of _DataType
	#define DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS( _CLASS_NAME_, _KEY_TYPE_, _MEMBER_VARIABLE_NAME_ ) \
	bool operator<( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() < cls->_MEMBER_VARIABLE_NAME_;} \
	bool operator>( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() > cls->_MEMBER_VARIABLE_NAME_;} \
	bool operator==( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() == cls->_MEMBER_VARIABLE_NAME_;}

	/// The multilist, representing an abstract data type that generally holds lists
	/// \param[in] _MultilistType What type of list this is, \sa MultilistType
	/// \param[in] _DataType What type of data this list holds.
	/// \param[in] _KeyType If a function takes a key to sort on, what type of key this is. The comparison operator between _DataType and _KeyType must be defined
	/// \param[in] _IndexType What variable type to use for indices
	template <const MultilistType _MultilistType, class _DataType, class _KeyType=_DataType, class _IndexType=uint32_t>
	class RAK_DLL_EXPORT Multilist
	{
	public:
		Multilist();
		~Multilist();
		Multilist( const Multilist& source );
		Multilist& operator= ( const Multilist& source );
		_DataType& operator[] ( const _IndexType position ) const;
		/// Unordered list, stack is LIFO
		/// QUEUE is FIFO
		/// Ordered list is inserted in order
		void Push(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ );
		void Push(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ );

		/// Gets or removes and gets an element from the list, according to the same rules as Push()
		/// Ordered list is LIFO for the purposes of Pop and Peek
		_DataType &Pop(const char *file=__FILE__, unsigned int line=__LINE__);
		_DataType &Peek(void) const;

		/// Same as Push(), except FIFO and LIFO are reversed
		/// Ordered list still inserts in order
		void PushOpposite(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ );
		void PushOpposite(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ );

		/// Same as Pop() and Peek(), except FIFO and LIFO are reversed
		_DataType &PopOpposite(const char *file=__FILE__, unsigned int line=__LINE__);
		_DataType &PeekOpposite(void) const;

		// Stack,Queue: Inserts at index indicated, elements are shifted. Ordered list: Inserts, position is ignored
		void InsertAtIndex(const _DataType &d, _IndexType index, const char *file=__FILE__, unsigned int line=__LINE__);
		void InsertAtIndex(const _DataType &d, const _KeyType &key, _IndexType index, const char *file=__FILE__, unsigned int line=__LINE__);

		/// Unordered list, removes at index indicated, swaps last element with that element
		/// Otherwise, array is shifted left to overwrite removed element
		/// Index[0] returns the same as Pop() for a queue. Same as PopOpposite() for the list and ordered list
		void RemoveAtIndex(_IndexType position, const char *file=__FILE__, unsigned int line=__LINE__);

		/// Find the index of \a key, and remove at that index.
		bool RemoveAtKey(_KeyType key, bool assertIfDoesNotExist);

		/// Finds the index of \a key. Return -1 if the key is not found.
		_IndexType GetIndexOf(_KeyType key) const;

		/// Finds the index of \a key. Return 0 if the key is not found. Useful if _DataType is always non-zero pointers.
		_DataType GetPtr(_KeyType key) const;

		/// Iterate over the list, calling the function pointer on each element.
		void ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line);
		void ForEach(void (*func)(_DataType &item));	

		/// Returns if the list is empty
		bool IsEmpty(void) const;

		/// Returns the number of elements used in the list
		_IndexType GetSize(void) const;

		/// Empties the list. The list is not deallocated if it is small, unless \a deallocateSmallBlocks is true
		void Clear( bool deallocateSmallBlocks=true, const char *file=__FILE__, unsigned int line=__LINE__ );

		/// Reverses the elements in the list, and flips the sort order returned by GetSortOrder() if IsSorted() returns true at the time the function is called
		void ReverseList(void);

		/// Reallocates the list to a larger size.
		/// If \a size is smaller than the value returned by GetSize(), the call does nothing.
		void Reallocate(_IndexType size, const char *file=__FILE__, unsigned int line=__LINE__);

		/// Sorts the list unless it is an ordered list, in which it does nothing as the list is assumed to already be sorted.
		/// However, if \a force is true, it will also resort the ordered list, useful if the comparison operator between _KeyType and _DataType would now return different results
		/// Once the list is sorted, further operations to lookup by key will be log2(n) until the list is modified
		void Sort(bool force);

		/// Defaults to ascending.
		/// Used by Sort(), and by ML_ORDERED_LIST
		void SetSortOrder(bool ascending);

		/// Returns true if ascending
		bool GetSortOrder(void) const;

		/// Returns true if the list is currently believed to be in a sorted state
		/// Doesn't actually check for sortedness, just if Sort() was recently called, or MultilistType is ML_ORDERED_LIST
		bool IsSorted(void) const;

		/// Returns what type of list this is
		MultilistType GetMultilistType(void) const;

		/// Changes what type of list this is
		/// \pre Template must be defined with ML_VARIABLE_DURING_RUNTIME for this to do anything
		/// \param[in] mlType Any value of the enum MultilistType, except ML_VARIABLE_DURING_RUNTIME
		void SetMultilistType(MultilistType newType);

	protected:
		void ReallocateIfNeeded(const char *file, unsigned int line);
		void DeallocateIfNeeded(const char *file, unsigned int line);
		void ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line);
		void ReverseListInternal(void);
		void InsertInOrderedList(const _DataType &d, const _KeyType &key);
		_IndexType GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const;
		void InsertShiftArrayRight(const _DataType &d, _IndexType index);
		void DeleteShiftArrayLeft(_IndexType index);
		void QSortAscending(_IndexType left, _IndexType right);
		void QSortDescending(_IndexType left, _IndexType right);

		/// An array of user values
		_DataType* data;
		
		/// Number of elements in the list 		
		_IndexType dataSize;
		
		/// Size of \a array 		
		_IndexType allocationSize;

		/// Array index for the head of the queue
		_IndexType queueHead;

		/// Array index for the tail of the queue
		_IndexType queueTail;

		/// How many bytes the user chose to preallocate
		/// Won't automatically deallocate below this
		_IndexType preallocationSize;

		enum
		{
			ML_UNSORTED,
			ML_SORTED_ASCENDING,
			ML_SORTED_DESCENDING
		} sortState;

		bool ascendingSort;

		// In case we are using the variable type multilist
		MultilistType variableMultilistType;
	};

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist()
	{
		data=0;
		dataSize=0;
		allocationSize=0;
		ascendingSort=true;
		sortState=ML_UNSORTED;
		queueHead=0;
		queueTail=0;
		preallocationSize=0;

		if (_MultilistType==ML_ORDERED_LIST)
			sortState=ML_SORTED_ASCENDING;
		else
			sortState=ML_UNSORTED;

		if (_MultilistType==ML_VARIABLE_DURING_RUNTIME)
			variableMultilistType=ML_UNORDERED_LIST;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::~Multilist()
	{
		if (data!=0)
			RakNet::OP_DELETE_ARRAY(data, __FILE__, __LINE__);
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist( const Multilist& source )
	{
		Clear(true);
		dataSize=source.GetSize();
		allocationSize=source.allocationSize;
		ascendingSort=source.ascendingSort;
		sortState=source.sortState;
		queueHead=0;
		queueTail=dataSize;
		preallocationSize=source.preallocationSize;
		variableMultilistType=source.variableMultilistType;
		if (source.data==0)
		{
			data=0;
		}
		else
		{
			RakNet::OP_NEW_ARRAY<_DataType>(dataSize,__FILE__,__LINE__);
			_IndexType i;
			for (i=0; i < dataSize; i++)
				data[i]=source[i];
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	Multilist<_MultilistType, _DataType, _KeyType, _IndexType>& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator= ( const Multilist& source )
	{
		Multilist::Multilist(source);
		return *this;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	_DataType& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator[] ( const _IndexType position ) const
	{
		RakAssert(position<GetSize());

		if (GetMultilistType()==ML_QUEUE)
		{
			if ( queueHead + position >= allocationSize )
				return data[ queueHead + position - allocationSize ];
			else
				return data[ queueHead + position ];
		}
		
		return data[position];
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const char *file, unsigned int line )
	{
		Push(d,d,file,line);
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const _KeyType &key, const char *file, unsigned int line )
	{
		ReallocateIfNeeded(file,line);

		if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK)
		{
			data[dataSize]=d;
		}
		else if (GetMultilistType()==ML_QUEUE)
		{
			data[queueTail++] = d;

			if ( queueTail == allocationSize )
				queueTail = 0;
		}
		else
		{
			RakAssert(GetMultilistType()==ML_ORDERED_LIST);
			InsertInOrderedList(d,key);
		}
		dataSize++;

		if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE)
		{
			// Break sort if no longer sorted
			if (sortState!=ML_UNSORTED && dataSize>1)
			{
				if (ascendingSort)
				{
					if ( MLKeyRef<_KeyType>(key) < operator[](dataSize-2) )
						sortState=ML_UNSORTED;
				}
				else
				{
					if ( MLKeyRef<_KeyType>(key) > operator[](dataSize-2) )
						sortState=ML_UNSORTED;
				}
			}
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	_DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Pop(const char *file, unsigned int line)
	{
		RakAssert(IsEmpty()==false);
		DeallocateIfNeeded(file,line);
		if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST)
		{
			dataSize--;
			return data[dataSize];
		}
		else
		{
			RakAssert(GetMultilistType()==ML_QUEUE);

			if ( ++queueHead == allocationSize )
				queueHead = 0;

			if ( queueHead == 0 )
				return data[ allocationSize -1 ];

			dataSize--;
			return data[ queueHead -1 ];
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	_DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Peek(void) const
	{
		RakAssert(IsEmpty()==false);
		if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST)
		{
			return data[dataSize-1];
		}
		else
		{
			RakAssert(GetMultilistType()==ML_QUEUE);
			return data[ queueHead ];
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const char *file, unsigned int line )
	{
		PushOpposite(d,d,file,line);
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const _KeyType &key, const char *file, unsigned int line )
	{
		ReallocateIfNeeded(file,line);

		// Unordered list Push at back
		if (GetMultilistType()==ML_UNORDERED_LIST)
		{
			data[dataSize]=d;
			dataSize++;
		}
		else if (GetMultilistType()==ML_STACK)
		{
			// Stack push at front of the list, instead of back as normal
			InsertAtIndex(d,key,0,file,line);
		}
		else if (GetMultilistType()==ML_QUEUE)
		{
			// Queue push at front of the list, instead of back as normal
			InsertAtIndex(d,key,0,file,line);
		}
		else
		{
			RakAssert(GetMultilistType()==ML_ORDERED_LIST);
			InsertInOrderedList(d,key);
			dataSize++;
		}

		if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE)
		{
			// Break sort if no longer sorted
			if (sortState!=ML_UNSORTED && dataSize>1)
			{
				if (ascendingSort)
				{
					if ( MLKeyRef<_KeyType>(key) > operator[](1) )
						sortState=ML_UNSORTED;
				}
				else
				{
					if ( MLKeyRef<_KeyType>(key) < operator[](1) )
						sortState=ML_UNSORTED;
				}
			}
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	_DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PopOpposite(const char *file, unsigned int line)
	{
		RakAssert(IsEmpty()==false);
		if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST)
		{
			// Copy leftmost to end
			ReallocateIfNeeded(file,line);
			data[dataSize]=data[0];
			DeleteShiftArrayLeft(0);
			--dataSize;
			// Assuming still leaves at least one element past the end of the list allocated
			DeallocateIfNeeded(file,line);
			// Return end
			return data[dataSize+1];
		}
		else
		{
			RakAssert(GetMultilistType()==ML_QUEUE);
			// Deallocate first, since we are returning off the existing list
			DeallocateIfNeeded(file,line);
			dataSize--;

			if (queueTail==0)
				queueTail=allocationSize-1;
			else
				--queueTail;

			return data[queueTail];
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	_DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PeekOpposite(void) const
	{
		RakAssert(IsEmpty()==false);
		if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST)
		{
			return data[0];
		}
		else
		{
			RakAssert(GetMultilistType()==ML_QUEUE);
			_IndexType priorIndex;
			if (queueTail==0)
				priorIndex=allocationSize-1;
			else
				priorIndex=queueTail-1;

			return data[priorIndex];
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertAtIndex(const _DataType &d, _IndexType index, const char *file, unsigned int line)
	{
		InsertAtIndex(d,d,index,file,line);
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertAtIndex(const _DataType &d, const _KeyType &key, _IndexType index, const char *file, unsigned int line)
	{
		ReallocateIfNeeded(file,line);

		if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST)
		{
			if (index>=dataSize)
			{
				// insert at end
				data[dataSize]=d;
			}
			else
			{
				// insert at index
				InsertShiftArrayRight(d,index);
			}

			dataSize++;
		}
		else
		{
			Push(d);

			if (dataSize==1)
				return;

			_IndexType writeIndex, readIndex, trueWriteIndex, trueReadIndex;
			writeIndex=dataSize-1;
			readIndex=writeIndex-1;
			while (readIndex >= index)
			{
				if ( queueHead + writeIndex >= allocationSize )
					trueWriteIndex = queueHead + writeIndex - allocationSize;
				else
					trueWriteIndex = queueHead + writeIndex;

				if ( queueHead + readIndex >= allocationSize )
					trueReadIndex = queueHead + readIndex - allocationSize;
				else
					trueReadIndex = queueHead + readIndex;

				data[trueWriteIndex]=data[trueReadIndex];

				if (readIndex==0)
					break;
				writeIndex--;
				readIndex--;
			}

			if ( queueHead + index >= allocationSize )
				trueWriteIndex = queueHead + index - allocationSize;
			else
				trueWriteIndex = queueHead + index;

			data[trueWriteIndex]=d;
		}

		if (_MultilistType!=ML_ORDERED_LIST)
			sortState=ML_UNSORTED;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtIndex(_IndexType position, const char *file, unsigned int line)
	{
		RakAssert(position < dataSize);
		RakAssert(IsEmpty()==false);

		if (GetMultilistType()==ML_UNORDERED_LIST)
		{
			// Copy tail to current
			data[position]=data[dataSize-1];
		}
		else if (GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST)
		{
			DeleteShiftArrayLeft(position);
		}
		else
		{
			RakAssert(GetMultilistType()==ML_QUEUE);

			_IndexType index, next;

			if ( queueHead + position >= allocationSize )
				index = queueHead + position - allocationSize;
			else
				index = queueHead + position;

			next = index + 1;

			if ( next == allocationSize )
				next = 0;

			while ( next != queueTail )
			{
				// Overwrite the previous element
				data[ index ] = data[ next ];
				index = next;
				//next = (next + 1) % allocationSize;

				if ( ++next == allocationSize )
					next = 0;
			}

			// Move the queueTail back
			if ( queueTail == 0 )
				queueTail = allocationSize - 1;
			else
				--queueTail;
		}
		
		
		dataSize--;
		DeallocateIfNeeded(file,line);
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtKey(_KeyType key, bool assertIfDoesNotExist)
	{
		_IndexType index = GetIndexOf(key);
		if (index==(_IndexType)-1)
		{
			RakAssert(assertIfDoesNotExist==false && "RemoveAtKey element not found");
			return false;
		}
		RemoveAtIndex(index);
		return true;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	_IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexOf(_KeyType key) const
	{
		_IndexType i;
		if (IsSorted())
		{
			bool objectExists;
			i=GetIndexFromKeyInSortedList(key, &objectExists);
			if (objectExists)
				return i;
			return (_IndexType)-1;
		}
		else if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK)
		{
			for (i=0; i < dataSize; i++)
			{
				if (MLKeyRef<_KeyType>(key)==data[i])
					return i;
			}
			return (_IndexType)-1;
		}
		else
		{
			RakAssert( GetMultilistType()==ML_QUEUE );

			for (i=0; i < dataSize; i++)
			{
				if (MLKeyRef<_KeyType>(key)==operator[](i))
					return i;
			}
			return (_IndexType)-1;
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	_DataType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetPtr(_KeyType key) const
	{
		_IndexType i = GetIndexOf(key);
		if (i==(_IndexType)-1)
			return 0;
		return data[i];
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line)
	{
		_IndexType i;
		for (i=0; i < dataSize; i++)
			func(operator[](i), file, line);
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item))
	{
		_IndexType i;
		for (i=0; i < dataSize; i++)
			func(operator[](i));
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsEmpty(void) const
	{
		return dataSize==0;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	_IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSize(void) const
	{
		return dataSize;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Clear( bool deallocateSmallBlocks, const char *file, unsigned int line )
	{
		dataSize=0;
		if (GetMultilistType()==ML_ORDERED_LIST)
			if (ascendingSort)
				sortState=ML_SORTED_ASCENDING;
			else
				sortState=ML_SORTED_DESCENDING;
		else
			sortState=ML_UNSORTED;
		queueHead=0;
		queueTail=0;

		if (deallocateSmallBlocks && allocationSize < 128)
		{
			RakNet::OP_DELETE_ARRAY(data,file,line);
			data=0;
			allocationSize=0;
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseList(void)
	{
		if (IsSorted())
			ascendingSort=!ascendingSort;

		ReverseListInternal();
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Reallocate(_IndexType size, const char *file, unsigned int line)
	{
		_IndexType newAllocationSize;
		if (size < dataSize)
			newAllocationSize=dataSize;
		else
			newAllocationSize=size;
		preallocationSize=size;
		ReallocToSize(newAllocationSize,file,line);
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Sort(bool force)
	{
		if (IsSorted() && force==false)
			return;

		if (dataSize>1)
		{
			if (ascendingSort)
				QSortAscending(0,dataSize-1);		
			else
				QSortDescending(0,dataSize-1);
		}


		if (ascendingSort)
			sortState=ML_SORTED_ASCENDING;
		else
			sortState=ML_SORTED_DESCENDING;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortAscending(_IndexType leftEdge, _IndexType rightEdge)
	{
		_DataType temp;
		_IndexType left=leftEdge;
		_IndexType right=rightEdge;
		_IndexType pivotIndex=left++;

		while (left<right)
		{
			if (data[left] <= data[pivotIndex])
			{
				++left;
			}
			else
			{
				temp=data[left];
				data[left]=data[right];
				data[right]=temp;
				--right;
			}
		}

		temp=data[pivotIndex];

		// Move pivot to center
		if (data[left] > data[pivotIndex])
		{
			--left;

			data[pivotIndex]=data[left];
			data[left]=temp;
		}
		else
		{
			data[pivotIndex]=data[left];
			data[left]=temp;

			--left;
		}

		if (left!=leftEdge)
			QSortAscending(leftEdge, left);

		if (right!=rightEdge)
			QSortAscending(right, rightEdge);		
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortDescending(_IndexType leftEdge, _IndexType rightEdge)
	{
		_DataType temp;
		_IndexType left=leftEdge;
		_IndexType right=rightEdge;
		_IndexType pivotIndex=left++;

		while (left<right)
		{
			if (data[left] >= data[pivotIndex])
			{
				++left;
			}
			else
			{
				temp=data[left];
				data[left]=data[right];
				data[right]=temp;
				--right;
			}
		}

		temp=data[pivotIndex];

		// Move pivot to center
		if (data[left] < data[pivotIndex])
		{
			--left;

			data[pivotIndex]=data[left];
			data[left]=temp;
		}
		else
		{
			data[pivotIndex]=data[left];
			data[left]=temp;

			--left;
		}

		if (left!=leftEdge)
			QSortDescending(leftEdge, left);

		if (right!=rightEdge)
			QSortDescending(right, rightEdge);		
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetSortOrder(bool ascending)
	{
		if (ascendingSort!=ascending && IsSorted())
		{
			ascendingSort=ascending;
			// List is sorted, and the sort order has changed. So reverse the list
			ReverseListInternal();
		}

		if (ascending)
			sortState=ML_SORTED_ASCENDING;
		else
			sortState=ML_SORTED_DESCENDING;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSortOrder(void) const
	{
		return ascendingSort;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsSorted(void) const
	{
		return GetMultilistType()==ML_ORDERED_LIST || sortState!=ML_UNSORTED;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	MultilistType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetMultilistType(void) const
	{
		if (_MultilistType==ML_VARIABLE_DURING_RUNTIME)
			return variableMultilistType;
		return _MultilistType;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetMultilistType(MultilistType newType)
	{
		RakAssert(_MultilistType==ML_VARIABLE_DURING_RUNTIME);
		switch (variableMultilistType)
		{
		case ML_UNORDERED_LIST:
			switch (newType)
			{
			case ML_UNORDERED_LIST:
				// No change
				break;
			case ML_STACK:
				// Same data format
				break;
			case ML_QUEUE:
				queueHead=0;
				queueTail=dataSize;
				break;
			case ML_ORDERED_LIST:
				Sort(false);
				break;
			}
			break;
		case ML_STACK:
			switch (newType)
			{
			case ML_UNORDERED_LIST:
				// Same data format
				break;
			case ML_STACK:
				// No change
				break;
			case ML_QUEUE:
				queueHead=0;
				queueTail=dataSize;
				break;
			case ML_ORDERED_LIST:
				Sort(false);
				break;
			}
			break;
		case ML_QUEUE:
			switch (newType)
			{
			case ML_UNORDERED_LIST:
			case ML_STACK:
			case ML_ORDERED_LIST:
				if (queueTail < queueHead)
				{
					// Realign data if wrapped
					ReallocToSize(dataSize, __FILE__, __LINE__);
				}
				else
				{
					// Else can just copy starting at head
					_IndexType i;
					for (i=0; i < dataSize; i++)
						data[i]=operator[](i);
				}
				if (newType==ML_ORDERED_LIST)
					Sort(false);
				break;
			case ML_QUEUE:
				// No change
				break;
			}
			break;
		case ML_ORDERED_LIST:
			switch (newType)
			{
			case ML_UNORDERED_LIST:
			case ML_STACK:
			case ML_QUEUE:
				// Same data format
				// Tag as sorted
				if (ascendingSort)
					sortState=ML_SORTED_ASCENDING;
				else
					sortState=ML_SORTED_DESCENDING;
				if (newType==ML_QUEUE)
				{
					queueHead=0;
					queueTail=dataSize;
				}
				break;
			case ML_ORDERED_LIST:
				// No change
				break;
			}
			break;
		}

		variableMultilistType=newType;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocateIfNeeded(const char *file, unsigned int line)
	{
		if (dataSize<allocationSize)
			return;

		_IndexType newAllocationSize;
		if (allocationSize<16)
			newAllocationSize=32;
		else if (allocationSize>65536)
			newAllocationSize=allocationSize+65536;
		else
		{
			newAllocationSize=allocationSize<<1; // * 2
			// Protect against underflow
			if (newAllocationSize < allocationSize)
				newAllocationSize=allocationSize+65536;
		}

		ReallocToSize(newAllocationSize,file,line);
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeallocateIfNeeded(const char *file, unsigned int line)
	{
		if (allocationSize<512)
			return;
		if (dataSize >= allocationSize/3 )
			return;
		if (dataSize <= preallocationSize )
			return;
		
		_IndexType newAllocationSize = dataSize<<1; // * 2

		ReallocToSize(newAllocationSize,file,line);
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line)
	{
		_DataType* newData = RakNet::OP_NEW_ARRAY<_DataType>(newAllocationSize,file,line);
		_IndexType i;
		for (i=0; i < dataSize; i++)
			newData[i]=operator[](i);
		if (dataSize>0)
		{
			RakNet::OP_DELETE_ARRAY(data,file,line);
			if (GetMultilistType()==ML_QUEUE)
			{
				queueHead=0;
				queueTail=dataSize;
			}
		}
		data=newData;
		allocationSize=newAllocationSize;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseListInternal(void)
	{
		_DataType temp;
		_IndexType i;
		for (i=0; i < dataSize/2; i++)
		{
			temp=operator[](i);
			operator[](i)=operator[](dataSize-1-i);
			operator[](dataSize-1-i)=temp;
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertInOrderedList(const _DataType &d, const _KeyType &key)
	{
		RakAssert(GetMultilistType()==ML_ORDERED_LIST);

		bool objectExists;
		_IndexType index;
		index = GetIndexFromKeyInSortedList(key, &objectExists);

		// Don't allow duplicate insertion.
		if (objectExists)
		{
			// Ordered list only allows unique insertions
			RakAssert("Duplicate insertion into ordered list" && false);
			return;
		}

		if (index>=dataSize)
		{
			// insert at end
			data[dataSize]=d;
		}
		else
		{
			// insert at index
			InsertShiftArrayRight(d,index);
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	_IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const
	{
		RakAssert(IsSorted());
		_IndexType index, upperBound, lowerBound;

		if (dataSize==0)
		{
			*objectExists=false;
			return 0;
		}

		upperBound=dataSize-1;
		lowerBound=0;
		index = dataSize/2;

#ifdef _MSC_VER
	#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
		while (1)
		{
			if (MLKeyRef<_KeyType>(key) > operator[](index) )
			{
				if (ascendingSort)
					lowerBound=index+1;
				else
					upperBound=index-1;
			}
			else if (MLKeyRef<_KeyType>(key) < operator[](index) )
			{
				if (ascendingSort)
					upperBound=index-1;
				else
					lowerBound=index+1;
			}
			else
			{
				// ==
				*objectExists=true;
				return index;
			}

			index=lowerBound+(upperBound-lowerBound)/2;

			if (lowerBound>upperBound || upperBound==(_IndexType)-1)
			{
				*objectExists=false;
				return lowerBound; // No match
			}
		}
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertShiftArrayRight(const _DataType &d, _IndexType index)
	{
		RakAssert(_MultilistType!=ML_QUEUE);

		// Move the elements in the list to make room
		_IndexType i;
		for ( i = dataSize; i != index; i-- )
			data[ i ] = data[ i - 1 ];

		// Insert the new item at the correct spot
		data[ index ] = d;

		++dataSize;
	}

	template <const MultilistType _MultilistType, class _DataType, class _KeyType, class _IndexType>
	void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeleteShiftArrayLeft( _IndexType index )
	{
		RakAssert(index < dataSize);
		RakAssert(_MultilistType!=ML_QUEUE);

		_IndexType i;
		for ( i = index; i < dataSize-1; i++ )
			data[i]=data[i+1];
	}
};

/*
struct KeyAndValue
{
	int key;
	short value;
};

DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS(KeyAndValue,int,key)

void MultilistUnitTest(void)
{
	DataStructures::Multilist<DataStructures::ML_UNORDERED_LIST, int> ml1;
	ml1.Reallocate(64);
	RakAssert(ml1.IsEmpty());
	ml1.Push(53);
	RakAssert(ml1.Peek()==53);
	RakAssert(ml1.IsEmpty()==false);
	RakAssert(ml1.Pop()==53);
	RakAssert(ml1.IsEmpty()==true);
	for (int i=0; i < 512; i++)
		ml1.Push(i);
	RakAssert(ml1.GetIndexOf(200)==200);
	RakAssert(ml1.PeekOpposite()==0);
	RakAssert(ml1.PopOpposite()==0);
	RakAssert(ml1.PeekOpposite()==1);
	RakAssert(ml1.Peek()==511);
	ml1.ReverseList();
	for (int i=0; i < 511; i++)
		RakAssert(ml1[i]==511-i);
	RakAssert(ml1.PeekOpposite()==511);
	RakAssert(ml1.Peek()==1);
	ml1.RemoveAtIndex(0);
	RakAssert(ml1.PeekOpposite()==1);
	ml1.Clear();
	RakAssert(ml1.IsEmpty()==true);

	ml1.Sort(true);
	ml1.Clear();

	ml1.Push(100);
	ml1.Sort(true);
	ml1.Clear();

	ml1.Push(50);
	ml1.Push(100);
	ml1.Sort(true);
	ml1.Clear();

	ml1.Push(100);
	ml1.Push(50);
	ml1.Sort(true);
	ml1.Clear();

	ml1.Push(100);
	ml1.Push(50);
	ml1.Push(150);
	ml1.Push(25);
	ml1.Push(175);
	ml1.Sort(true);
	RakAssert(ml1[0]==25);
	RakAssert(ml1[1]==50);
	RakAssert(ml1[2]==100);
	RakAssert(ml1[3]==150);
	RakAssert(ml1[4]==175);
	RakAssert(ml1.GetIndexOf(25)==0);
	RakAssert(ml1.GetIndexOf(50)==1);
	RakAssert(ml1.GetIndexOf(100)==2);
	RakAssert(ml1.GetIndexOf(150)==3);
	RakAssert(ml1.GetIndexOf(175)==4);
	ml1.Clear();

	ml1.Push(1);
	ml1.Push(2);
	ml1.Push(3);
	ml1.Push(4);
	ml1.Push(5);
	ml1.Sort(true);
	RakAssert(ml1[0]==1);
	RakAssert(ml1[1]==2);
	RakAssert(ml1[2]==3);
	RakAssert(ml1[3]==4);
	RakAssert(ml1[4]==5);
	RakAssert(ml1.GetIndexOf(1)==0);
	RakAssert(ml1.GetIndexOf(2)==1);
	RakAssert(ml1.GetIndexOf(3)==2);
	RakAssert(ml1.GetIndexOf(4)==3);
	RakAssert(ml1.GetIndexOf(5)==4);
	ml1.Clear();

	ml1.Push(5);
	ml1.Push(4);
	ml1.Push(3);
	ml1.Push(2);
	ml1.Push(1);
	ml1.Sort(true);
	RakAssert(ml1[0]==1);
	RakAssert(ml1[1]==2);
	RakAssert(ml1[2]==3);
	RakAssert(ml1[3]==4);
	RakAssert(ml1[4]==5);
	RakAssert(ml1.GetIndexOf(1)==0);
	RakAssert(ml1.GetIndexOf(2)==1);
	RakAssert(ml1.GetIndexOf(3)==2);
	RakAssert(ml1.GetIndexOf(4)==3);
	RakAssert(ml1.GetIndexOf(5)==4);
	ml1.Sort(true);
	RakAssert(ml1[0]==1);
	RakAssert(ml1[1]==2);
	RakAssert(ml1[2]==3);
	RakAssert(ml1[3]==4);
	RakAssert(ml1[4]==5);
	RakAssert(ml1.GetIndexOf(1)==0);
	RakAssert(ml1.GetIndexOf(2)==1);
	RakAssert(ml1.GetIndexOf(3)==2);
	RakAssert(ml1.GetIndexOf(4)==3);
	RakAssert(ml1.GetIndexOf(5)==4);
	ml1.Clear();

	DataStructures::Multilist<DataStructures::ML_STACK, int> ml2;
	ml2.Reallocate(64);
	RakAssert(ml2.IsEmpty());
	ml2.Push(53);
	RakAssert(ml2.Peek()==53);
	RakAssert(ml2.IsEmpty()==false);
	RakAssert(ml2.Pop()==53);
	RakAssert(ml2.IsEmpty()==true);
	for (int i=0; i < 512; i++)
		ml2.Push(i);
	RakAssert(ml2.GetIndexOf(200)==200);
	RakAssert(ml2.PeekOpposite()==0);
	RakAssert(ml2.PopOpposite()==0);
	RakAssert(ml2.PeekOpposite()==1);
	RakAssert(ml2.Peek()==511);
	ml2.ReverseList();
	for (int i=0; i < 511; i++)
		RakAssert(ml2[i]==511-i);
	RakAssert(ml2.PeekOpposite()==511);
	RakAssert(ml2.Peek()==1);
	ml2.RemoveAtIndex(0);
	RakAssert(ml2.Peek()==1);
	RakAssert(ml2.PeekOpposite()==510);
	ml2.Clear();
	RakAssert(ml2.IsEmpty()==true);

	DataStructures::Multilist<DataStructures::ML_QUEUE, int> ml3;
	RakAssert(ml3.IsEmpty());
	ml3.Push(53);
	RakAssert(ml3.Peek()==53);
	RakAssert(ml3.IsEmpty()==false);
	RakAssert(ml3.Pop()==53);
	RakAssert(ml3.IsEmpty()==true);
	for (int i=0; i < 512; i++)
		ml3.Push(i);
	RakAssert(ml3.GetIndexOf(200)==200);
	RakAssert(ml3.PeekOpposite()==511);
	RakAssert(ml3.PopOpposite()==511);
	RakAssert(ml3.PeekOpposite()==510);
	RakAssert(ml3.Peek()==0);
	ml3.ReverseList();
	for (int i=0; i < 511; i++)
		RakAssert(ml3[i]==511-1-i);
	RakAssert(ml3.PeekOpposite()==0);
	RakAssert(ml3.Peek()==510);
	ml3.RemoveAtIndex(0);
	RakAssert(ml3.Peek()==509);
	RakAssert(ml3.PeekOpposite()==0);
	ml3.Clear();
	RakAssert(ml3.IsEmpty()==true);

	ml3.PushOpposite(100);
	ml3.PushOpposite(50);
	ml3.PushOpposite(150);
	ml3.PushOpposite(25);
	ml3.PushOpposite(175);
	ml3.Sort(true);
	RakAssert(ml3[0]==25);
	RakAssert(ml3[1]==50);
	RakAssert(ml3[2]==100);
	RakAssert(ml3[3]==150);
	RakAssert(ml3[4]==175);
	RakAssert(ml3.GetIndexOf(25)==0);
	RakAssert(ml3.GetIndexOf(50)==1);
	RakAssert(ml3.GetIndexOf(100)==2);
	RakAssert(ml3.GetIndexOf(150)==3);
	RakAssert(ml3.GetIndexOf(175)==4);
	ml3.Clear();

	ml3.PushOpposite(1);
	ml3.PushOpposite(2);
	ml3.PushOpposite(3);
	ml3.PushOpposite(4);
	ml3.PushOpposite(5);
	ml3.Sort(true);
	RakAssert(ml3[0]==1);
	RakAssert(ml3[1]==2);
	RakAssert(ml3[2]==3);
	RakAssert(ml3[3]==4);
	RakAssert(ml3[4]==5);
	RakAssert(ml3.GetIndexOf(1)==0);
	RakAssert(ml3.GetIndexOf(2)==1);
	RakAssert(ml3.GetIndexOf(3)==2);
	RakAssert(ml3.GetIndexOf(4)==3);
	RakAssert(ml3.GetIndexOf(5)==4);
	ml3.Clear();

	ml3.PushOpposite(5);
	ml3.PushOpposite(4);
	ml3.PushOpposite(3);
	ml3.PushOpposite(2);
	ml3.PushOpposite(1);
	ml3.Sort(true);
	RakAssert(ml3[0]==1);
	RakAssert(ml3[1]==2);
	RakAssert(ml3[2]==3);
	RakAssert(ml3[3]==4);
	RakAssert(ml3[4]==5);
	RakAssert(ml3.GetIndexOf(1)==0);
	RakAssert(ml3.GetIndexOf(2)==1);
	RakAssert(ml3.GetIndexOf(3)==2);
	RakAssert(ml3.GetIndexOf(4)==3);
	RakAssert(ml3.GetIndexOf(5)==4);
	ml3.Sort(true);
	RakAssert(ml3[0]==1);
	RakAssert(ml3[1]==2);
	RakAssert(ml3[2]==3);
	RakAssert(ml3[3]==4);
	RakAssert(ml3[4]==5);
	RakAssert(ml3.GetIndexOf(1)==0);
	RakAssert(ml3.GetIndexOf(2)==1);
	RakAssert(ml3.GetIndexOf(3)==2);
	RakAssert(ml3.GetIndexOf(4)==3);
	RakAssert(ml3.GetIndexOf(5)==4);

	ml3.SetSortOrder(false);
	ml3.Sort(false);
	RakAssert(ml3[0]==5);
	RakAssert(ml3[1]==4);
	RakAssert(ml3[2]==3);
	RakAssert(ml3[3]==2);
	RakAssert(ml3[4]==1);
	RakAssert(ml3.GetIndexOf(1)==4);
	RakAssert(ml3.GetIndexOf(2)==3);
	RakAssert(ml3.GetIndexOf(3)==2);
	RakAssert(ml3.GetIndexOf(4)==1);
	RakAssert(ml3.GetIndexOf(5)==0);

	ml3.Clear();

	DataStructures::Multilist<DataStructures::ML_ORDERED_LIST, int> ml4;
	ml4.Reallocate(64);
	RakAssert(ml4.IsEmpty());
	ml4.Push(53);
	RakAssert(ml4.Peek()==53);
	RakAssert(ml4.IsEmpty()==false);
	RakAssert(ml4.Pop()==53);
	RakAssert(ml4.IsEmpty()==true);
	for (int i=0; i < 512; i++)
		ml4.Push(i);
	RakAssert(ml4.GetIndexOf(200)==200);
	RakAssert(ml4.PeekOpposite()==0);
	RakAssert(ml4.PopOpposite()==0);
	RakAssert(ml4.PeekOpposite()==1);
	RakAssert(ml4.Peek()==511);
	ml4.ReverseList();
	for (int i=0; i < 511; i++)
		RakAssert(ml4[i]==511-i);
	RakAssert(ml4.PeekOpposite()==511);
	RakAssert(ml4.Peek()==1);
	ml4.RemoveAtIndex(0);
	RakAssert(ml4.Peek()==1);
	RakAssert(ml4.PeekOpposite()==510);
	ml4.Clear();
	RakAssert(ml4.IsEmpty()==true);

	DataStructures::Multilist<DataStructures::ML_ORDERED_LIST, KeyAndValue*, int> ml5;

	for (int i=0; i < 16; i++)
	{
		KeyAndValue *kav = new KeyAndValue;
		kav->key=i;
		kav->value=i+100;
		ml5.Push(kav,kav->key);
	}

	RakAssert(ml5.GetIndexOf(0)==0);
	RakAssert(ml5.GetIndexOf(5)==5);
	RakAssert(ml5.GetIndexOf(15)==15);
	RakAssert(ml5.GetIndexOf(16)==-1);
	ml5.RemoveAtKey(0,true);
	RakAssert(ml5.GetIndexOf(1)==0);
	KeyAndValue *iPtr = ml5.GetPtr(5);
	RakAssert(iPtr);
	RakAssert(iPtr->value=105);
	iPtr = ml5.GetPtr(1234);
	RakAssert(iPtr==0);
	ml5.ForEach(DataStructures::DeletePtr<KeyAndValue*>);


	DataStructures::Multilist<DataStructures::ML_VARIABLE_DURING_RUNTIME, int> ml6;
	ml6.Push(2);
	ml6.Push(1);
	ml6.Push(6);
	ml6.Push(3);
	RakAssert(ml6.Peek()==3);
	ml6.SetMultilistType(DataStructures::ML_STACK);
	RakAssert(ml6.Peek()==3);
	ml6.SetMultilistType(DataStructures::ML_QUEUE);
	RakAssert(ml6.Peek()==2);
	ml6.SetMultilistType(DataStructures::ML_ORDERED_LIST);
	RakAssert(ml6.Peek()=6);
	ml6.SetMultilistType(DataStructures::ML_STACK);
	RakAssert(ml6.Peek()==6);
	ml6.SetMultilistType(DataStructures::ML_QUEUE);
	RakAssert(ml6.Peek()==1);
}
*/

#endif
