/*
	ZArray.hpp
	Author: James Russell <jcrussell@762studios.com>
			Chris Ertel <crertel@762studios.com>
	Created: 9/12/2010

	Purpose:	
	
	Templated dynamic array implementation.

	Because ZArray is a dynamic array implementation (not a dynamic vector implementation), it
	makes no guarantees about the constructor / destructor behavior of individual elements.  This is
	to say that as elements enter and leave scope, the underlying array may not be modified and 
	constructs and destructors may not be called.  For example, if space is reserved for 20 elements,
	then 20 elements are constructed in an array of size 20.  The array will not be deleted until
	Reserve(0) is called on an array of size 0, which even then only guarantees that the allocator
	is called and the array provided for a 'Deallocate' function call.  Whether or not this deletes
	the array is up to the allocator.

	Note that the default allocator stores space for a certain number of elements and does not 
	destruct these until the allocator itself leaves scope.

	Note:

	A visualizer for Visual Studio 2008 for this container is available in vs_visualizers.txt.
	
	License:

	This program is free software. It comes without any warranty, to
	the extent permitted by applicable law. You can redistribute it
	and/or modify it under the terms of the Do What The Fuck You Want
	To Public License, Version 2, as published by Sam Hocevar. See
	http://sam.zoy.org/wtfpl/COPYING for more details.

*/

#pragma once

#ifndef _ZARRAY_HPP
#define _ZARRAY_HPP

#include <ZSTL/ZSTLCommon.hpp>
#include <ZSTL/ZSTLInvalidPos.hpp>

//Default capacity for ZArray when no capacity is defined (recommended that this be 
//less than or equal to local storage count, user can define their own)
#ifndef ZARRAY_DEFAULT_CAPACITY
#define ZARRAY_DEFAULT_CAPACITY (10)
#endif

//Resize factor for ZArray when operations are performed that require an increase in capacity
//and capacity is not specified (user can define their own, should be greater than 1.0)
#ifndef ZARRAY_CAPACITY_RESIZE_FACTOR
#define ZARRAY_CAPACITY_RESIZE_FACTOR (2.0)
#endif

//Local storage number of elements (user can define their own)
#ifndef ZARRAY_DEFAULT_ALLOCATOR_STORAGE
#define ZARRAY_DEFAULT_ALLOCATOR_STORAGE (10)
#endif

//Forward Declaration of ZArray
template <typename T, typename A>
class ZArray;

//Forward Declaration of ZArrayIterator
template <typename T>
class ZArrayIterator;

/* 
Forward Declaration of ZArray Method Implementation Structures 

Existence of these structures allows for template specialization of 
individual methods of the ZArray class.  In order to specialize
a single method of ZArray, simply specialize the corresponding
method implementation structure.
*/

//Forward Declaration of ZArray<T, A>::At struct
template <typename T, typename A>
struct ZArray_AtImpl {
	inline static T& Call(const ZArray<T, A>& _self, size_t _index);
};

//Forward Declaration of ZArray<T, A>::Back struct
template <typename T, typename A>
struct ZArray_BackImpl {
	inline static T& Call(const ZArray<T, A>& _self);
};

//Forward Declaration of ZArray<T, A>::Capacity struct
template <typename T, typename A>
struct ZArray_CapacityImpl {
	inline static size_t Call(const ZArray<T, A>& _self);
};

//Forward Declaration of ZArray<T, A>::Clear struct
template <typename T, typename A>
struct ZArray_ClearImpl {
	inline static void Call(ZArray<T, A>& _self);
	inline static void Call(ZArray<T, A>& _self, size_t _newCapacity);
};

//Forward Declaration of ZArray<T, A>::Copy struct
template <typename T, typename A>
struct ZArray_CopyImpl {
	template <typename B> inline static void Call(ZArray<T, A>& _self, const ZArray<T, B>& _other);
};

//Forward Declaration of ZArray<T, A>::Data struct
template <typename T, typename A>
struct ZArray_DataImpl {
	inline static T* Call(const ZArray<T, A>& _self);
};

//Forward Declaration of ZArray<T, A>::Empty struct
template <typename T, typename A>
struct ZArray_EmptyImpl {
	inline static bool Call(const ZArray<T, A>& _self);
};

//Forward Declaration of ZArray<T, A>::Equals struct
template <typename T, typename A>
struct ZArray_EqualsImpl {
	template <typename B> inline static bool Call(const ZArray<T, A>& _self, const ZArray<T, B>& _other);
};

//Forward Declaration of ZArray<T, A>::Erase struct
template <typename T, typename A>
struct ZArray_EraseImpl {
	inline static T Call(ZArray<T, A>& _self, size_t _index);
	inline static void Call(ZArray<T, A>& _self, size_t _start, size_t _end);
};

//Forward Declaration of ZArray<T, A>::Front struct
template <typename T, typename A>
struct ZArray_FrontImpl {
	inline static T& Call(const ZArray<T, A>& _self);
};

//Forward Declaration of ZArray<T, A>::Find struct
template <typename T, typename A>
struct ZArray_FindImpl {
	inline static size_t Call(const ZArray<T, A>& _self, const T& elem);
};

//Forward Declaration of ZArray<T, A>::Insert struct
template <typename T, typename A>
struct ZArray_InsertImpl {
	inline static void Call(ZArray<T, A>& _self, size_t _index, const T& _value, size_t _count);
	template <typename B> inline static void Call(ZArray<T, A>& _self, size_t _index, const ZArray<T, B>& _other, size_t _start, size_t _count);
};

//Forward Declaration of ZArray<T, A>::PopBack struct
template <typename T, typename A>
struct ZArray_PopBackImpl {
	inline static T Call(ZArray<T, A>& _self);
};

//Forward Declaration of ZArray<T, A>::PopFront struct
template <typename T, typename A>
struct ZArray_PopFrontImpl {
	inline static T Call(ZArray<T, A>& _self);
};

//Forward Declaration of ZArray<T, A>::PushBack struct
template <typename T, typename A>
struct ZArray_PushBackImpl {
	inline static void Call(ZArray<T, A>& _self, const T& _value);
};

//Forward Declaration of ZArray<T, A>::PushFront struct
template <typename T, typename A>
struct ZArray_PushFrontImpl {
	inline static void Call(ZArray<T, A>& _self, const T& _value);
};

//Forward Declaration of ZArray<T, A>::Reserve struct
template <typename T, typename A>
struct ZArray_ReserveImpl {
	inline static void Call(ZArray<T, A>& _self, size_t _capacity, bool _reallocate);
};

//Forward Declaration of ZArray<T, A>::Resize struct
template <typename T, typename A>
struct ZArray_ResizeImpl {
	inline static void Call(ZArray<T, A>& _self, size_t _size);
	inline static void Call(ZArray<T, A>& _self, size_t _size, const T& _value);
	inline static void Call(ZArray<T, A>& _self, size_t _size, size_t _capacity, const T& _value);
};

//Forward Declaration of ZArray<T, A>::Size struct
template <typename T, typename A>
struct ZArray_SizeImpl {
	inline static size_t Call(const ZArray<T, A>& _self);
};

//Forward Declaration of ZArray<T, A>::Swap struct
template <typename T, typename A>
struct ZArray_SwapImpl {
	template <typename B> inline static void Call(ZArray<T, A>& _self, ZArray<T, B>& _other);
};

//////////////////////
/* ZArray Allocator */
//////////////////////

/*
ZArray Allocator class.  Used to allocate arrays of a templated type 
when requested by a ZArray instance.

NOTE: This allocator keeps a set amount (N) of locally stored objects of type T.  These
objects are allocated when the array is created and deallocated when the array is
destructed, and there is no way (other than specializing based on type) to cause these
local elements to destruct other than destructing the array.  Reserve(0) will call
'Deallocate', but that will not destruct the elements. So, in summary, do not use the 
default allocator when you have a strict requirement that Reserve(0) should deallocate 
the array and call contained destructors.

The template parameter T is the type contained the array this allocator is for.

The template parameter N is the amount of local storage this allocator will keep
on hand.  Allocations of size less than N (in number of elements) will use the locally
allocated array.

*/
template <typename T, size_t N = ZARRAY_DEFAULT_ALLOCATOR_STORAGE>
class ZArrayAllocator
{
public:

	/*
	public ZArrayAllocator<T>::Allocate

	Allocation method.

	@param _size - size of the array to allocate
	@return - pointer to an array which can store at least _size values
	*/
	T* Allocate(size_t _size)
	{
		if (_size == 0)
			return NULL;
		
		if (_size > N)
		{
			return ZSTL_NEW_ARRAY(T, _size);
		}
		else
		{
			return Local;
		}
	}
	
	/*
	public ZArrayAllocator<T>::Deallocate

	Deallocation method.

	@param _ptr - pointer to previously allocated memory by this allocator
	@param _size - size of the memory
	*/
	void Deallocate(T* _ptr, size_t _size) 
	{
		if (_ptr == NULL)
			return;
		
		if (_ptr != &(Local[0]))
		{
			ZSTL_DEL_ARRAY(_ptr, _size);
		}
	}

protected:

	//The local storage for the allocator
	T Local[N];
};

/*
Specialization of ZArrayAllocator for a 0 size local storage.  Will
always allocate using ZSTL_NEW_ARRAY.
*/
template <typename T>
class ZArrayAllocator <T, 0>
{
public:
	//Allocates the array using ZSTL_NEW_ARRAY
	T* Allocate(size_t _size)
	{
		return ZSTL_NEW_ARRAY(T, _size);
	}
	
	//Deallocates the array using ZSTL_DEL_ARRAY
	void Deallocate(T* _ptr, size_t _size) 
	{
		ZSTL_DEL_ARRAY(_ptr, _size);
	}
};

/*
Heap allocator implementation of an allocator for ZArray.  Will
always allocate on the heap.
*/
template <typename T>
class ZArrayHeapAllocator
{
public:
	//Allocate on the heap
	T* Allocate(size_t _size)
	{
		return new (std::nothrow) T[_size];
	}

	//Deallocate the heap array
	void Deallocate(T* _ptr, size_t _size) 
	{
		URFP(_size);

		delete[] _ptr;
	}
};


/////////////////////
/* ZArray Iterator */
/////////////////////

/*
Iterator class for ZArray.  Can also be used to iterate a raw array of type T.

The template parameter T is the type contained in the list this iterator is for.
*/
template <typename T>
class ZArrayIterator
{
public:

	/*
	Default constructor.
	*/
	ZArrayIterator() 
		: Array(NULL), Index(0), Size(0) 
	{ }

	/*
	Copy constructor.
	
	@param _other - the other iterator
	*/
	ZArrayIterator(const ZArrayIterator& _other) 
		: Array(_other.Array), Index(_other.Index), Size(_other.Size) 
	{ CheckCurrent(); }

	/*
	Parameterized Constructor.

	@param _array - the array this iterator points to
	@param _index - the index into the array at which to start the iterator.
	@param _size - the size of the array we are pointed to
	*/
	ZArrayIterator(T *_array, size_t _index, size_t _size) 
		: Array(_array), Index(_index), Size(_size) 
	{ CheckCurrent(); }
	
	/*
	public ZArrayIterator<T>::CheckCurrent
	
	Check function that determines if the iterator is valid at it's current location. 

	@param _endIsValid - indicates that the iterator can be at the 'end' location
	@return (void)
	*/
	void CheckCurrent(bool _endIsValid = true) const
	{
		#if !ZSTL_DISABLE_RUNTIME_CHECKS
		ZSTL_ASSERT(Array != NULL,								
			"Uninitialized ZArray Iterator used!");
		ZSTL_ASSERT(Index < Size + (_endIsValid ? 1 : 0),	
			"ZArray Iterator has gone past end of array!");
		#else
		URFP(_endIsValid);
		#endif
	}

	/*
	public ZArrayIterator<T>::CheckNext
	
	Check function that determines if incrementing the iterator would be valid,
	assuming it was already valid.
	
	@param _inc - the amount by which the iterator will be incremented
	@return (void)
	*/
	void CheckNext(size_t _inc) const
	{
		#if !ZSTL_DISABLE_RUNTIME_CHECKS
		//Integer overflow possible here, but not likely
		ZSTL_ASSERT(Index + _inc <= Size, "ZArray Iterator has gone past end of array!");
		#else
		URFP(_inc);
		#endif
	}

	/*
	public ZArrayIterator<T>::CheckPrevious
	
	Check function that determines if decrementing the iterator would be valid,
	assuming it was already valid.

	@param _dec - the amount by which the iterator will be decremented
	@return (void)
	*/
	void CheckPrevious(size_t _dec) const
	{
		#if !ZSTL_DISABLE_RUNTIME_CHECKS
		//i.e. check that "Index - _dec >= 0", but carefully since
		//these are unsigned numbers and underflow results in SIZE_MAX
		ZSTL_ASSERT(Index >= _dec, "ZArray Iterator has gone past beginning of array!");
		#else
		URFP(_dec);
		#endif
	}

	/*
	public ZArrayIterator<T>::Get
	
	Gets the element this iterator points to.
	
	@return (T&) - the element pointed to
	*/
	T& Get() const												
	{ 
		return *(*this); 
	}

	/*
	public ZArrayIterator<T>::HasCurrent
	
	Determines if this iterator currently points to a valid element.
	
	@return (bool) - true if element at current position
	*/
	bool HasCurrent() const										
	{ 
		return Index < Size; 
	}

	/*
	public ZArrayIterator<T>::HasNext
	
	Determines if this iterator has a valid element after the current element.
	
	@return (bool) - true if element after current, false otherwise
	*/
	bool HasNext() const										
	{ 
		return Size != 0 && Index < Size - 1; 
	}

	/*
	public ZArrayIterator<T>::HasPrev
	
	Determines if this iterator has a valid element before the current element.
	
	@return (bool) - true if element before current, false otherwise
	*/
	bool HasPrev() const
	{ 
		return Index > 0; 
	}

	/*
	public ZArrayIterator<T>::Next
	
	Advances this iterator to the next element.
	
	@return (void)
	@assert - if this would advance past end
	*/
	void Next()
	{ 
		++(*this); 
	}

	/*
	public ZArrayIterator<T>::Prev
	
	Returns this iterator to the previous element.
	
	@return (void)
	@assert - if this would advance past begin
	*/
	void Prev()													
	{ 
		--(*this); 
	}

	//Operator Overrides
	ZArrayIterator& operator ++()								{ CheckNext(1); Index++; return *this; }
	ZArrayIterator operator ++ (int)							{ CheckNext(1); return ZArrayIterator(Array, Index++, Size); }
	ZArrayIterator operator + (int _value)						{ return ZArrayIterator(Array, Index + _value, Size); }
	ZArrayIterator& operator += (int _value)					{ CheckNext(_value); Index += _value; return *this; }

	ZArrayIterator& operator -- ()								{ CheckPrevious(1); Index--; return *this; }
	ZArrayIterator operator -- (int)							{ CheckPrevious(1); return ZArrayIterator(Array, Index--, Size); }
	ZArrayIterator operator - (int _value)						{ return ZArrayIterator(Array, Index - _value, Size); }
	ZArrayIterator& operator -= (int _value)					{ CheckPrevious(_value); Index -= _value; return *this; }
	
	ZArrayIterator& operator = (const ZArrayIterator &_other)	{ Array = _other.Array; Index = _other.Index; Size = _other.Size; CheckCurrent(); return *this; }
	bool operator == (const ZArrayIterator &_other) const		{ return Index == _other.Index; }
	bool operator != (const ZArrayIterator &_other) const		{ return !(Index == _other.Index); }
	
	bool operator < (const ZArrayIterator &_other) const		{ return (Index < _other.Index); }
	bool operator <= (const ZArrayIterator &_other) const		{ return (Index <= _other.Index); }
	bool operator > (const ZArrayIterator &_other) const		{ return (Index > _other.Index); }
	bool operator >= (const ZArrayIterator &_other) const		{ return (Index >= _other.Index); }
	
	T& operator *() const										{ CheckCurrent(false); return Array[Index]; }
	
	operator size_t () const									{ return Index; }
	
protected:

	//The array
	T* Array;

	//The current index into the array
	size_t Index;

	//The size of the array (at construction)
	size_t Size;

};

///////////////////////////
/* ZArray Implementation */
///////////////////////////

/*
Templated dynamic array implementation.

The template parameter T is the type contained in the array.

The template parameter A is the type of allocator to use, which must allocate
arrays of type T.
*/
template <typename T, typename A = ZArrayAllocator<T, ZARRAY_DEFAULT_ALLOCATOR_STORAGE> >
class ZArray
{
friend struct ZArray_AtImpl<T, A>;
friend struct ZArray_BackImpl<T, A>;
friend struct ZArray_CapacityImpl<T, A>;
friend struct ZArray_ClearImpl<T, A>;
friend struct ZArray_CopyImpl<T, A>;
friend struct ZArray_DataImpl<T, A>;
friend struct ZArray_EmptyImpl<T, A>;
friend struct ZArray_EqualsImpl<T, A>;
friend struct ZArray_EraseImpl<T, A>;
friend struct ZArray_FrontImpl<T, A>;
friend struct ZArray_FindImpl<T, A>;
friend struct ZArray_InsertImpl<T, A>;
friend struct ZArray_PopBackImpl<T, A>;
friend struct ZArray_PopFrontImpl<T, A>;
friend struct ZArray_PushBackImpl<T, A>;
friend struct ZArray_ReserveImpl<T, A>;
friend struct ZArray_ResizeImpl<T, A>;
friend struct ZArray_SizeImpl<T, A>;
friend struct ZArray_SwapImpl<T, A>;
public:
	/*
	Typedef for ZArrayIterator (Allows ZArray<T, A>::Iterator notation).
	*/
	typedef ZArrayIterator<T> Iterator;

	/*
	Default Constructor.
	*/
	ZArray()
		: Array(NULL), 
		  ArrayCapacity(ZARRAY_DEFAULT_CAPACITY), 
		  ArraySize(0)
	{
		Allocate(ArrayCapacity);
		CheckIntegrity();
	}

	/*
	Parameterized Constructor.

	@param _capacity - the starting capacity of the array
	*/	
	explicit ZArray(size_t _capacity)
		: Array(NULL), 
		  ArrayCapacity(_capacity), 
		  ArraySize(0)
	{
		Allocate(ArrayCapacity);

		CheckIntegrity();
	}

	/*
	Parameterized Constructor.  Given a reference to an array with a 
	size that can be deduced by the compiler, will create the array
	using that data.

	@param C - the length of the array
	@param _data - the data to initialize with
	@param _count - the length of the data
	*/
	template <size_t C>
	explicit ZArray( const T (&_data)[C])
		: Array(NULL), 
		  ArrayCapacity((size_t)(C * ZARRAY_CAPACITY_RESIZE_FACTOR)), 
		  ArraySize(C)
	{
		Allocate(ArrayCapacity);

		for (size_t i = 0; i < C; i++)
			Array[i] = _data[i];

		CheckIntegrity();
	}

	/*
	Parameterized Constructor.

	@param _capacity - the starting capacity of the array
	@param _size - the starting size of the array
	*/	
	ZArray(size_t _capacity, size_t _size)
		: Array(NULL), 
		  ArrayCapacity(_capacity), 
		  ArraySize(_size)
	{
		Allocate(ArrayCapacity);
		Resize(ArraySize);

		CheckIntegrity();
	}

	/*
	Parameterized Constructor.  Given a raw array and a size, will
	create this array using that data.

	@param _data - the data to initialize with
	@param _count - the length of the data
	*/
	ZArray(const T* _data, size_t _count)
		: Array(NULL), 
		  ArrayCapacity((size_t)(_count * ZARRAY_CAPACITY_RESIZE_FACTOR)), 
		  ArraySize(_count)
	{
		Allocate(ArrayCapacity);

		for (size_t i = 0; i < _count; i++)
			Array[i] = _data[i];

		CheckIntegrity();
	}

	/*
	Parameterized Constructor.  Given a raw array and a size, will
	create this array using that data.

	@param _data - the data to initialize with
	@param _count - the length of the data
	@param _capacity - the capacity to initialize with (must be greater than size)
	*/
	ZArray(const T* _data, size_t _count, size_t _capacity)
		: Array(NULL), 
		  ArrayCapacity(_capacity), 
		  ArraySize(_count)
	{
		Allocate(ArrayCapacity);

		for (size_t i = 0; i < _count; i++)
			Array[i] = _data[i];

		CheckIntegrity();
	}

	/*
	Copy Constructor.  Makes a deep copy of the array.  Even though the code
	is the same as the more generic copy constructor below, the code is needed to
	ensure a default copy constructor is not generated.

	@param _other - the array to copy from
	*/
	ZArray(const ZArray<T, A>& _other)
		: Array(NULL),
		  ArrayCapacity(_other.Capacity()), 
		  ArraySize(_other.Size())
	{
		T* data;
		size_t i;

		Allocate(ArrayCapacity);
		CheckIntegrity();

		for (i = 0, data = _other.Data(); i < ArraySize; i++)
			Array[i] = data[i];
	}

	/*
	Copy Constructor.  Makes a deep copy of the array from an array with
	a different allocator type.

	@param B - the allocator type of the other array
	@param _other - the array to copy from
	*/
	template <typename B>
	ZArray(const ZArray<T, B>& _other)
		: Array(NULL), 
		  ArrayCapacity(_other.Capacity()), 
		  ArraySize(_other.Size())
	{
		T* data;
		size_t i;

		Allocate(ArrayCapacity);
		CheckIntegrity();

		for (i = 0, data = _other.Data(); i < ArraySize; i++)
			Array[i] = data[i];
	}

	/*
	Slice Copy Constructor.  Constructs this array as a slice of another array.

	@param B - the allocator type of the other array
	@param _other - the array to copy from
	*/
	template <typename B>
	ZArray(const ZArray<T, B>& _other, size_t _start, size_t _end)
		: Array(NULL), 
		  ArrayCapacity(_other.Capacity()), 
		  ArraySize(_end - _start)
	{
		Allocate(ArrayCapacity);

		CheckIntegrity();

		_other.BoundsCheck(_start, _other.Size());
		_other.BoundsCheck(_end, _other.Size() + 1);

		for (size_t i = _start; i < _end; i++)
			Array[i - _start] = _other.Data()[i];
	}

	/*
	Destructor.
	*/
	~ZArray()
	{
		CheckIntegrity();
		Deallocate(Array, ArrayCapacity);
	}

	/*
	[] Operator overload.  Gets a value from the array.  Functionally equivalent to 
	a call to At.

	@param _index - the integer index into the array
	@return (T&) - reference to a value contained in the array
	@assert - if the index is out of bounds
	*/
	T& operator [] (size_t _index) const
		{ return At(_index); }

	/*
	= Operator overload.  Sets this array equal to the other by making
	a copy.  Even though the code is the same as the more generic version below,
	this is needed to prevent a default assignment operator from being constructed.

	@param _other - the array to be set equal to
	@return (ZArray<T, A>&) - this array
	*/
	ZArray<T, A>& operator = (const ZArray<T, A>& _other) 
		{ Copy(_other); return *this; }

	/*
	= Operator overload.  Sets this array equal to the other by making
	a copy.

	@param _other - the array to be set equal to
	@return (ZArray<T, A>&) - this array
	*/
	template <typename B>
	ZArray<T, A>& operator = (const ZArray<T, B>& _other)
		{ Copy(_other); return *this; }

	/*
	== Operator overload.  Performs an element comparison on the two arrays.

	@param B - the type of allocator for the other array
	@param _other - the array to compare to
	@return (bool) - true if this array is equivalent to the other
	*/
	template <typename B>
	bool operator == (const ZArray<T, B>& _other) const 
		{ return Equals(_other); }
	
	/*
	!= Operator overload.  Performs an element comparison on the two arrays.

	@param B - the type of the allocator for the other array
	@param _other - the array to compare to
	@return (bool) - false if this array is equivalent to the other
	*/
	template <typename B>
	bool operator != (const ZArray<T, B>& _other) const 
		{ return !Equals(_other); }

	/*
	public ZArray<T, A>::Allocator

	Returns a reference to the installed allocator.

	@return (A&) - the array allocator
	*/
	A& Allocator()
		{ return ArrayAllocator; }

	/*
	public ZArray<T, A>::At
	
	Gets a value from the array at a given position.
	
	@param _index - index into the array
	@return (T&) - the value at the given index
	@assert - if the index is out of bounds
	*/
	T& At(size_t _index) const 
		{ return ZArray_AtImpl<T, A>::Call(*this, _index); }

	/*
	public ZArray<T, A>::Back
	
	Gets a reference to the value at the back of the array.
	
	@return (T&) - the value at the end of the array
	@assert - if the array is empty
	*/
	T& Back() const 
		{ return ZArray_BackImpl<T, A>::Call(*this); }

	/*
	public ZArray<T, A>::Begin

	Returns an iterator to the beginning of the array.

	@return (ZArray<T, A>::Iterator) - Iterator with index zero
	*/
	Iterator Begin() 
		{ return ZArrayIterator<T>(Array, 0, ArraySize); }

	/*
	public ZArray<T, A>::BoundsCheck

	Function that checks the provided index against the provided boundary, asserting if an 
	out of bounds access occurs.  Calls to this will be compiled out if ZSTL_DISABLE_RUNTIME_CHECKS 
	is not zero.

	@param _index - index
	@param _boundary - the (exclusive) boundary to use for bounds checking
	@return (size_t) - unsigned index into the array
	@assert - if the index is not less than boundary
	*/
	size_t BoundsCheck(size_t _index, size_t _boundary) const
	{
		#if !ZSTL_DISABLE_RUNTIME_CHECKS
		ZSTL_ASSERT(_index < _boundary, "ZArray: Out of bounds access!");
		#else
		(void)_boundary;
		#endif

		return _index;
	}

	/*
	public ZArray<T, A>::Capacity

	Returns the capacity of the array, which is the number of values it can contain
	before allocation is required to increase storage size.

	@return (size_t) - array capacity
	*/
	size_t Capacity() const 
		{ return ZArray_CapacityImpl<T, A>::Call(*this); }

	/*
	public ZArray<T, A>::Clear

	Clears out the array of all contained elements, keeping the currently allocated 
	storage.

	@return (void)
	*/
	void Clear() 
		{ ZArray_ClearImpl<T, A>::Call(*this); }
	
	/*
	public ZArray<T, A>::Clear

	Clears out the array of all contained elements and will ensure that the capacity is
	at least as much as the provided value.  Reallocates the internal storage if necessary.

	@param _newCapacity - the (minimum) capacity of the array after clearing
	@return (void)
	*/
	void Clear(size_t _newCapacity) 
		{ ZArray_ClearImpl<T, A>::Call(*this, _newCapacity); }

	/*
	public ZArray<B>::Copy
	
	Makes a deep copy of the provided array.
	
	@param _other - the array to copy from
	@return (void)
	*/
	template <typename B>
	void Copy(const ZArray<T, B>& _other) 
		{ ZArray_CopyImpl<T, A>::template Call<B>(*this, _other); }

	/*
	public ZArray<T, A>::Data
	
	Gets a pointer to the underlying array data.
	
	@return (T*) - pointer to the first element in the array
	*/
	T* Data() const
		{ return ZArray_DataImpl<T, A>::Call(*this); }
	
	/*	
	public ZArray<T, A>::Empty

	Returns true if the array is empty (size 0), false otherwise.
	
	@return (bool) - true if empty, false otherwise
	*/
	bool Empty() const 
		{ return ZArray_EmptyImpl<T, A>::Call(*this); }

	/*
	public ZArray<T, A>::End

	Returns an iterator to the end of the array.

	@return (ZArray<T, A>::Iterator) - Iterator with index set to capacity
	*/
	Iterator End() 
		{ return ZArrayIterator<T>(Array, ArraySize, ArraySize); }

	/*
	public ZArray<T, A>::Equals
	
	Determines, with an element by element comparison, if this array is equivalent
	to another.
	
	@param _other - the array to compare with
	@return (bool) - true if equal, false otherwise
	*/
	template <typename B>
	bool Equals(const ZArray<T, B>& _other) const
		{ return ZArray_EqualsImpl<T, A>::template Call<B>(*this, _other); }

	/*
	public ZArray<T, A>::Erase

	Erase function.  Removes an element from the array at the given index.

	@param _index - the index at which to remove the element from the array.
	@return (T) - the element removed
	@assert - if the index is invalid
	*/
	T Erase(size_t _index) 
		{ return ZArray_EraseImpl<T, A>::Call(*this, _index); }

	/*
	public ZArray<T, A>::Erase

	Erase function.  Removes elements from the array between the given indices.

	@param _start - the starting index
	@param _end - the ending index (exclusive)
	@return (void)
	@assert - if the indices are invalid
	*/
	void Erase(size_t _start, size_t _end)
		{ ZArray_EraseImpl<T, A>::Call(*this, _start, _end); }
	
	/*
	public ZArray<T, A>::Front
	
	Gets a reference to the value at the front of the array.
	
	@return (T&) - the value at the front of the array
	@assert - if the array is empty
	*/
	T& Front() const 
		{ return ZArray_FrontImpl<T, A>::Call(*this); }

	/*
	public ZArray<T, A>::Find

	Find function.  Finds the first element in the array that is equal to elem,
	determined via == operator.

	@param _elem - the elem to find
	@return (size_t) - the index of the first occurence of elem, ZSTL::InvalidPos if not found
	*/
	size_t Find(const T& _elem)
		{ return ZArray_FindImpl<T, A>::Call(*this, _elem); }

	/*
	public ZArray<T, A>::Insert

	Inserts the given value at the specified location.

	@param _index - the index at which to perform the insertion
	@param _value - the value to insert
	@return (void)
	@assert - if the index is invalid
	*/
	void Insert(size_t _index, const T& _value) 
		{ ZArray_InsertImpl<T, A>::Call(*this, _index, _value, 1); }
	
	/*
	public ZArray<T, A>::Insert

	Inserts the given value at the specified location the given number of times.

	@param _index - the index at which to perform the insertion
	@param _value - the value to insert
	@param _count - the number of times to insert _value
	@return (void)
	@assert - if the index is invalid
	*/
	void Insert(size_t _index, const T& _value, size_t _count) 
		{ ZArray_InsertImpl<T, A>::Call(*this, _index, _value, _count); }
	
	/*
	public ZArray<T, A>::Insert

	Inserts the entirety of the given array at the specified location. 

	@param _index - the index at which to perform the insertion
	@param _other - the array to insert values from
	@return (void)
	@assert - if the index is invalid
	*/
	template <typename B>
	void Insert(size_t _index, const ZArray<T, B>& _other)
		{ ZArray_InsertImpl<T, A>::template Call<B>(*this, _index, _other, 0, _other.Size()); }
	
	/*
	public ZArray<T, A>::Insert

	Inserts data from the given array at the specified location. 

	@param _index - the index at which to perform the insertion
	@param _other - the array to insert values from
	@param _start - the index to start getting values from
	@param _count - the number of elements to insert starting at index
	@return (void)
	@assert - if the index is invalid or the start index is invalid
	*/
	template <typename B>
	void Insert(size_t _index, const ZArray<T, B>& _other, size_t _start, size_t _count)
		{ ZArray_InsertImpl<T, A>::template Call<B>(*this, _index, _other, _start, _count); }

	/*
	public ZArray<T, A>::PopBack

	Pop function.  Removes and returns the last element in the array.

	@return (T) - the last element in the array
	@assert - if the array is empty
	*/
	T PopBack() 
		{ return ZArray_PopBackImpl<T, A>::Call(*this); }

	/*
	public ZArray<T, A>::PopFront

	Pop function.  Removes and returns the first element in the array.  This operation 
	requires shifting all elements in the array forward.

	@return (T) - the first element in the array
	@assert - if the array is empty
	*/
	T PopFront() 
		{ return ZArray_PopFrontImpl<T, A>::Call(*this); }

	/*
	public ZArray<T, A>::PushBack

	Push function.  Attaches an element to the end of the array.  Will allocate
	storage if necessary.

	@param _value - the element to place at the end of the array
	@return (void)
	*/
	void PushBack(const T& _value) 
		{ ZArray_PushBackImpl<T, A>::Call(*this, _value); }

	/*
	public ZArray<T, A>::PushFront

	Push function.  Attaches an element to the beginning of the array.  This operation
	requires shifting all elements in the array back.  Will allocate storage if necessary.

	@param _value - the element to place at the beginning of the array
	@return (void)
	*/
	void PushFront(const T& _value) 
		{ ZArray_PushFrontImpl<T, A>::Call(*this, _value); }

	/*
	public ZArray<T, A>::Reserve

	Reserves an amount of space in the vector.  Allocates space if 
	necessary (capacity > current capacity).  The array capacity cannot 
	be reduced in size below the number of contained elements.

	Reserve(0) is guaranteed to always deallocate the array using
	the provided deallocator.

	@param _capacity - the new capacity requested
	@return (void)
	@assert - if capacity is less than size
	*/
	void Reserve(size_t _capacity) 
		{ ZArray_ReserveImpl<T, A>::Call(*this, _capacity, false); }

	/*
	public ZArray<T, A>::Reserve

	Reserves an amount of space in the vector.  Allocates space if 
	necessary (capacity > current capacity).  The array capacity cannot 
	be reduced in size below the number of contained elements.
	
	Reserve(0) is guaranteed to always deallocate the array using
	the provided deallocator, regardless of the value of _reallocate.

	@param _capacity - the new capacity requested
	@param _reallocate - if true, will always reallocate storage
	@return (void)
	@assert - if capacity is less than size
	*/
	void Reserve(size_t _capacity, bool _reallocate) 
		{ ZArray_ReserveImpl<T, A>::Call(*this, _capacity, _reallocate); }

	/*
	public ZArray<T, A>::Resize

	Resize function.  Increases or Decreases the size of the array.
	
	@param _size - the new size to grow the array to
	@return (void)
	*/
	void Resize(size_t _size) 
		{ ZArray_ResizeImpl<T, A>::Call(*this, _size); }

	/*
	public ZArray<T, A>::Resize

	Resize function.  Increases or Decreases the size of the array and
	sets new values equal to the given value.  
	
	@param _size - the new size to grow (or shrink) the array to
	@param _value - the new value to set all added values to
	@return (void)
	*/
	void Resize(size_t _size, const T& _value) 
		{ ZArray_ResizeImpl<T, A>::Call(*this, _size, _value); }

	/*
	public ZArray<T, A>::Resize

	Resize function.  Increases or decreases the size of the array and
	reserves a new capacity (must be greater than size).  Sets new values 
	equal to the given value.

	@param _size - the new size to grow (or shrink) the array to
	@param _capacity - the new capacity to reserve (must be greater than or equal to size)
	@param _value - the new value to set all added values to
	@return (void)
	*/
	void Resize(size_t _size, size_t _capacity, const T& _value)
		{ ZArray_ResizeImpl<T, A>::Call(*this, _size, _capacity, _value); }

	/*
	public ZArray<T, A>::Size

	Size function.  Give the size of the array.

	@return (size_t) - the size of this array
	*/
	size_t Size() const 
		{ return ZArray_SizeImpl<T, A>::Call(*this); }

	/*
	public ZArray<T, A>::Swap

	Swaps the array contents with another array with any
	local storage size.

	@param _other - the array to swap contents with
	@return (void)
	*/
	template <typename B>
	void Swap(ZArray<T, B>& _other) 
		{ ZArray_SwapImpl<T, A>::Call(*this, _other); }
		
protected:

	//The array pointer
	T *Array;

	//Allocator for the Array
	A ArrayAllocator;

	//The 'size' of the raw array pointer
	size_t ArrayCapacity;

	//The current number of contained elements in the array
	size_t ArraySize;

	//Makes a call to the allocator and checks the result
	inline void Allocate(size_t _size)
	{
		Array = ArrayAllocator.Allocate(_size);

		#if !ZSTL_DISABLE_RUNTIME_CHECKS
		ZSTL_ASSERT(!(Array == NULL && _size > 0), "ZArrayAllocator failed to properly allocate array! (returned NULL with size > 0)");
		#endif
	}

	//Makes a call to the allocator
	inline void Deallocate(T* _array, size_t _size)
	{
		ArrayAllocator.Deallocate(_array, _size);
	}

	//Shifts values up in the array from _start by _count
	inline void ShiftUp(size_t _index, size_t _count)
	{
		/*
		Shift elements up, but do so in reverse so that we don't just copy Array[i] to all of the higher addresses.
		If you do:

			Array[i+1] = Array[i];

		...in a loop, then the first iteration overwrites the second value with the first, but it doesn't preserve
		the second so it can be copied into the third. If you run this code on [1, 2, 3, 0], you get [1, 1, 3, 0] after the first
		iteration, and then [1, 1, 1, 0] after the second iteration, and so on.

		The solution is to copy backwards. Consider an array of 100 elements:

		[1, 2, 3, ..., 100]

		...that is to by shifted by 3 elements, starting at index 5. Then, we know that
		the final array should look like:

		[1, 2, 3, 4, X, X, X, ..., 5, 6, 7, ... 100].

		where X is undefined (in this function, it is "_value"). The number of elements that
		will be shifted is 100 - 5 = 95 elements. Further more, each element will be shifted 3
		units. Let S be the size of the array (100), I be the index to start at (5), and N
		be the number to shift by (3). Then,

		Since we are copying backwards, consider the final element: Array[S-1]. This element
		is to be moved N units, so:

			Array[S-1 + N] = Array[S-1];
		
		Add in a counter, K, that decreases the index value each loop iteration, and loop S-I times.

			Array[S-1 + N - K] = ArraySize[S-1 - K]
		*/

		size_t i;
		const size_t nShift = ArraySize - _index;

		//Shift elements up
		for(i = 0; i < nShift; i++)
			Array[ArraySize - 1 + _count - i] = Array[ArraySize - 1 - i];
	}

	//Integrity Check (used for internal debugging)
	inline void CheckIntegrity() const
	{
		#if ZSTL_CHECK_INTEGRITY
		ZSTL_ASSERT(!(Array == NULL && ArrayCapacity > 0), "ZArray Error: Array is invalid!");
		ZSTL_ASSERT(ArrayCapacity >= ArraySize, "ZArray Error: Array capacity less than size!");
		#endif
	}
};

////////////////////////////////////////////
/* Non-Specialized Method Implementations */
////////////////////////////////////////////

template <typename T, typename A>
T& ZArray_AtImpl<T, A>::Call( const ZArray<T, A>& _self, size_t _index )
{
	size_t index = _self.BoundsCheck(_index, _self.ArraySize);

	return _self.Array[index];
}

/*************************************************************************/

template <typename T, typename A>
T& ZArray_BackImpl<T, A>::Call( const ZArray<T, A>& _self )
{
	#if !ZSTL_DISABLE_RUNTIME_CHECKS
	ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot get back element from empty array!");
	#endif

	return _self.Array[_self.ArraySize - 1];
}

/*************************************************************************/

template <typename T, typename A>
size_t ZArray_CapacityImpl<T, A>::Call(const ZArray<T, A>& _self)
{ 
	//Give back our current capacity
	return _self.ArrayCapacity;	
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_ClearImpl<T, A>::Call(ZArray<T, A>& _self)
{
	//Reset size and check integrity
	_self.ArraySize = 0;
	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_ClearImpl<T, A>::Call(ZArray<T, A>& _self, size_t _newCapacity)
{ 
	//Reset size, reserve new capacity, then check integrity
	_self.ArraySize = 0;
	_self.Reserve(_newCapacity, false);

	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A> template <typename B>
void ZArray_CopyImpl<T, A>::Call(ZArray<T, A>& _self, const ZArray<T, B>& _other)
{
	size_t i;

	//If both arrays point to the same data, we're done here
	if (_self.Data() == _other.Data())
		return;

	//Make sure we have room, write data, and check integrity
	_self.Resize(_other.Size());

	for (i = 0; i < _other.Size(); i++)
		_self.Array[i] = _other.Data()[i];

	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A>
T* ZArray_DataImpl<T, A>::Call(const ZArray<T, A>& _self)
{
	return _self.Array;
}

/*************************************************************************/

template <typename T, typename A>
bool ZArray_EmptyImpl<T, A>::Call(const ZArray<T, A>& _self)
{
	return _self.ArraySize == 0;
}

/*************************************************************************/

template <typename T, typename A> template <typename B>
bool ZArray_EqualsImpl<T, A>::Call(const ZArray<T, A>& _self, const ZArray<T, B>& _other)
{
	size_t i;

	//First see if we have the same size
	if (_self.ArraySize == _other.Size())
	{
		//Element wise comparison
		for (i = 0; i < _self.ArraySize; i++)
		{
			if (!(_self.Array[i] == _other.Array[i]))
				return false;
		}
	}		
	else
	{
		//Nope, so false
		return false;
	}

	return true;
}

/*************************************************************************/

template <typename T, typename A>
T ZArray_EraseImpl<T, A>::Call(ZArray<T, A>& _self, size_t _index)
{ 
	size_t index = _self.BoundsCheck(_index, _self.ArraySize);

	//Grab the currently held element, shift the array down, reduce size, and check integrity
	T element = _self.Array[index];

	for (size_t i = index; i + 1 < _self.ArraySize; i++)
		_self.Array[i] = _self.Array[i + 1];

	_self.ArraySize--;
	_self.CheckIntegrity();

	return element;
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_EraseImpl<T, A>::Call(ZArray<T, A>& _self, size_t _start, size_t _end)
{
	if (_start == _end)
		return;

	size_t start = _self.BoundsCheck(_start, _self.ArraySize);
	size_t end = _self.BoundsCheck(_end, _self.ArraySize + 1);

	#if !ZSTL_DISABLE_RUNTIME_CHECKS
	ZSTL_ASSERT(start <= end, "ZArray: cannot erase with start < end!");
	#endif

	//Copy the elements down, compute new size, and check integrity
	for (size_t idx = start; idx + (end - start) < _self.ArraySize; idx++)
		_self.Array[idx] = _self.Array[idx + (end - start)];

	_self.ArraySize = _self.ArraySize - (end - start);
	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A>
T& ZArray_FrontImpl<T, A>::Call( const ZArray<T, A>& _self )
{
	#if !ZSTL_DISABLE_RUNTIME_CHECKS
	ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot get front element from empty array!");
	#endif

	return _self.Array[0];
}

/*************************************************************************/

template <typename T, typename A>
size_t ZArray_FindImpl<T, A>::Call( const ZArray<T, A>& _self, const T& _elem )
{
	for (size_t i = 0; i < _self.ArraySize; i++) {
		if (_self.Array[i] == _elem) {
			return i;
		}
	}
	
	return ZSTL::InvalidPos;
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_InsertImpl<T, A>::Call(ZArray<T, A>& _self, size_t _index, const T& _value, size_t _count)
{ 
	//Get our insertion index (we allow past end to indicate insert at 'end')
	size_t index = _self.BoundsCheck(_index, _self.ArraySize + 1);

	const size_t newSize = _self.ArraySize + _count;

	//Reserve enough space for the new elements
	if (newSize > _self.ArrayCapacity)
		_self.Reserve(newSize);

	//Shift up existing elements
	_self.ShiftUp(index, _count);

	//Insert new values
	for (size_t i = 0; i < _count; i++)
		_self.Array[index + i] = _value;

	//Increase our array size
	_self.ArraySize += _count;

	//We are non const, so check
	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A> template <typename B>
void ZArray_InsertImpl<T, A>::Call(ZArray<T, A>& _self, size_t _index, const ZArray<T, B>& _other, size_t _start, size_t _count)
{
	//Get our indices (once again, allowed to go past end to indicate 'append')
	size_t index = _self.BoundsCheck(_index, _self.ArraySize + 1);
	size_t start = _other.BoundsCheck(_start, _other.Size() + 1);

	_other.BoundsCheck(_start + _count, _other.Size() + 1);	//This will do a bounds check on other

	const size_t newSize = _self.ArraySize + _count;

	//Make sure we have enough space
	if (newSize > _self.ArrayCapacity)
		_self.Reserve( newSize );

	//Shift elements up
	_self.ShiftUp(index, _count);

	//Copy in the data from the other
	for (size_t i = 0; i < _count; i++)
		_self.Array[index + i] = _other.Data()[start + i];

	//Set our new size
	_self.ArraySize = newSize;

	//We are non-const, so check
	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A>
T ZArray_PopBackImpl<T, A>::Call(ZArray<T, A>& _self)
{ 
	#if !ZSTL_DISABLE_RUNTIME_CHECKS
	ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot pop from array with no elements!");
	#endif

	//Grab the last element in the array and decrease our array size
	return _self.Array[--(_self.ArraySize)];
}

/*************************************************************************/

template <typename T, typename A>
T ZArray_PopFrontImpl<T, A>::Call(ZArray<T, A>& _self)
{ 
	#if !ZSTL_DISABLE_RUNTIME_CHECKS
	ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot pop from array with no elements!");
	#endif

	//Just use our erase function
	return _self.Erase(0);
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_PushBackImpl<T, A>::Call(ZArray<T, A>& _self, const T& _value)
{ 
	//See if we need more space
	if (_self.ArraySize >= _self.ArrayCapacity)
		_self.Reserve((size_t)((float)_self.ArrayCapacity * ZARRAY_CAPACITY_RESIZE_FACTOR) + 1 ); //Calls CheckIntegrity

	//Add in the element
	_self.Array[(_self.ArraySize)++] = _value;
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_PushFrontImpl<T, A>::Call(ZArray<T, A>& _self, const T& _value)
{ 
	//Just use the insert function
	return _self.Insert(0, _value, 1);
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_ReserveImpl<T, A>::Call(ZArray<T, A>& _self, size_t _capacity, bool _reallocate)
{ 
	#if !ZSTL_DISABLE_RUNTIME_CHECKS
	ZSTL_ASSERT(_capacity >= _self.ArraySize, "ZArray: Cannot reserve capacity less than array size!");
	#endif

	//Special case for reserve 0
	if (_capacity == 0)
	{
		_self.Deallocate(_self.Array, _self.ArrayCapacity);
		_self.Array = NULL;
		_self.ArrayCapacity = 0;
		return;
	}

	//See if we actually need to increase our capacity
	if (!_reallocate && _capacity <= _self.ArrayCapacity)
		return;
		
	//Set our temp array
	T* temp = _self.Array;

	//Allocate an array
	_self.Allocate(_capacity);

	//Make sure we aren't doing something pointless here
	if (temp == _self.Array)
	{
		_self.ArrayCapacity = _capacity;
		return;
	}

	//Copy the data
	for (size_t i = 0; i < _self.ArraySize; i++)
		_self.Array[i] = temp[i];

	//Deallocate the temporary (old) array
	_self.Deallocate(temp, _self.ArrayCapacity);

	//Set our new capacity
	_self.ArrayCapacity = _capacity;

	//non-const, so check integrity
	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_ResizeImpl<T, A>::Call(ZArray<T, A>& _self, size_t _size)
{ 
	//Check to see if we need more space, change our size, and check integrity
	if (_size > _self.ArrayCapacity)
		_self.Reserve(_size);

	_self.ArraySize = _size;
	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_ResizeImpl<T, A>::Call(ZArray<T, A>& _self, size_t _size, const T& _value)
{
	//See if we need more space, copy in the new value, change our size, and check integrity
	if (_size > _self.ArrayCapacity)
		_self.Reserve(_size);

	for (size_t i = _self.ArraySize; i < _size; i++)
		_self.Array[i] = _value;

	_self.ArraySize = _size;
	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A>
void ZArray_ResizeImpl<T, A>::Call(ZArray<T, A>& _self, size_t _size, size_t _capacity, const T& _value) 
{
	_self.Reserve(_capacity);

	//See if we need more space, copy in the new value, change our size, and check integrity
	if (_size > _self.ArrayCapacity)
		_self.Reserve(_size);

	for (size_t i = _self.ArraySize; i < _size; i++)
		_self.Array[i] = _value;

	_self.ArraySize = _size;
	_self.CheckIntegrity();
}

/*************************************************************************/

template <typename T, typename A>
size_t ZArray_SizeImpl<T, A>::Call(const ZArray<T, A>& _self)
{
	return _self.ArraySize;
}

/*************************************************************************/

template <typename T, typename A> template <typename B>
void ZArray_SwapImpl<T, A>::Call(ZArray<T, A>& _self, ZArray<T, B>& _other)
{	
	T tempElement;
	size_t i;
	size_t smallerSize;

	// Arrays are same size.
	if (_self.ArraySize == _other.ArraySize)
	{
		for (i = 0; i < _self.ArraySize; i++)
		{
			tempElement = _other[i];
			_other[i] = _self[i];
			_self[i] = tempElement;
		}
		return;
	}
	// Self is larger than other.
	else if (_self.ArraySize > _other.ArraySize)
	{
		smallerSize = _other.ArraySize;

		// Swap all of the smaller array's elements
		for (i = 0; i < smallerSize; i++)
		{
			tempElement = _other[i];
			_other[i] = _self[i];
			_self[i] = tempElement;
		}

		// Resize smaller array to be larger.
		_other.Reserve(_self.ArraySize);
		for (i = smallerSize; i < _self.ArraySize; i++)
			_other.PushBack(_self[i]);

		// Truncate our array.
		_self.Resize(smallerSize);
	}
	else
	{
		smallerSize = _self.ArraySize;

		// Swap all of the smaller array's elements
		for (i = 0; i < smallerSize; i++)
		{
			tempElement = _other[i];
			_other[i] = _self[i];
			_self[i] = tempElement;
		}

		// Resize smaller array to be larger.
		_self.Reserve(_other.ArraySize);
		for (i = smallerSize; i < _other.ArraySize; i++)
			_self.PushBack(_other[i]);

		// Truncate our array.
		_other.Resize(smallerSize);
	}

	//Check integrity on both arrays
	_self.CheckIntegrity();
	_other.CheckIntegrity();
}

#endif
