/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////


#include "EASTLTest.h"
#include <EASTL/bonus/ring_buffer.h>
#include <EASTL/bonus/fixed_ring_buffer.h>
#include <EASTL/vector.h>
#include <EASTL/deque.h>
#include <EASTL/string.h>
#include <EASTL/list.h>
#include <EASTL/fixed_vector.h>
#include <EASTL/fixed_string.h>



using namespace eastl;


// Template instantations.
// These tell the compiler to compile all the functions for the given class.
template class eastl::ring_buffer< int,        eastl::vector<int>        >;
template class eastl::ring_buffer< Align64,    eastl::vector<Align64>    >;
template class eastl::ring_buffer< TestObject, eastl::vector<TestObject> >;

template class eastl::ring_buffer< int,        eastl::deque<int>        >;
template class eastl::ring_buffer< Align64,    eastl::deque<Align64>    >;
template class eastl::ring_buffer< TestObject, eastl::deque<TestObject> >;

template class eastl::ring_buffer< int,        eastl::list<int>        >;
template class eastl::ring_buffer< Align64,    eastl::list<Align64>    >;
template class eastl::ring_buffer< TestObject, eastl::list<TestObject> >;

// TODO(rparolin):  To consider adding support for eastl::array. 
// template class eastl::ring_buffer< int, eastl::array<int, 64>>;

typedef eastl::fixed_string<char, 256, false>          RBFixedString;
typedef eastl::fixed_vector<RBFixedString, 100, false> RBFixedStringVector;
typedef RBFixedStringVector::overflow_allocator_type   RBFixedStringVectorOverflowAllocator;
template class eastl::ring_buffer<RBFixedString, RBFixedStringVector, RBFixedStringVectorOverflowAllocator>;

typedef eastl::fixed_vector<int, 100, false> RBFixedIntVector;
template class eastl::ring_buffer<int, RBFixedIntVector, RBFixedIntVector::overflow_allocator_type>;
// template class eastl::ring_buffer<int, RBFixedIntVector>;  // currently fails to compile

typedef eastl::fixed_vector<int, 100> RBFixedIntVectorWithOverFlow;
template class eastl::ring_buffer<int, RBFixedIntVectorWithOverFlow, RBFixedIntVectorWithOverFlow::overflow_allocator_type>; 
// template class eastl::ring_buffer<int, RBFixedIntVectorWithOverFlow>; // currently fails to compile



int TestRingBuffer()
{
	int nErrorCount = 0;

	// GCC prior to 4.1 has a fatal code generation bug in string arrays, which we use below.
	#if !defined(EA_DEBUG) && defined(__GNUC__) && !defined(__EDG__) && (((__GNUC__ * 100) + __GNUC_MINOR__) < 401)
		return nErrorCount;
	#endif

	{ // regression for bug in the capacity() function for the case of capacity == 0.

		vector<int> emptyIntArray;
		ring_buffer<int, vector<int> > intRingBuffer(emptyIntArray);

		EATEST_VERIFY(intRingBuffer.validate());
		EATEST_VERIFY(intRingBuffer.capacity() == 0);

		intRingBuffer.resize(0);
		EATEST_VERIFY(intRingBuffer.validate());
		EATEST_VERIFY(intRingBuffer.size() == 0);

		intRingBuffer.resize(1);
		EATEST_VERIFY(intRingBuffer.validate());
		EATEST_VERIFY(intRingBuffer.size() == 1);
	}

	{
		EA::UnitTest::Rand rng(EA::UnitTest::GetRandSeed());

		typedef ring_buffer< string, vector<string> > RBVectorString;

		int  counter = 0;
		char counterBuffer[32];

		// explicit ring_buffer(size_type size = 0);
		const int kOriginalCapacity = 50;
		RBVectorString rbVectorString(50);

		// bool empty() const;
		// size_type size() const;
		// bool validate() const;
		EATEST_VERIFY(rbVectorString.validate());
		EATEST_VERIFY(rbVectorString.empty());
		EATEST_VERIFY(rbVectorString.size() == 0);
		EATEST_VERIFY(rbVectorString.capacity() == 50);

		// void clear();
		rbVectorString.clear();
		EATEST_VERIFY(rbVectorString.validate());
		EATEST_VERIFY(rbVectorString.empty());
		EATEST_VERIFY(rbVectorString.size() == 0);
		EATEST_VERIFY(rbVectorString.capacity() == 50);

		// container_type& get_container();
		RBVectorString::container_type& c = rbVectorString.get_container();
		EATEST_VERIFY(c.size() == (kOriginalCapacity + 1)); // We need to add one because the ring_buffer mEnd is necessarily an unused element.

		// iterator begin();
		// iterator end();
		// int validate_iterator(const_iterator i) const;
		RBVectorString::iterator it = rbVectorString.begin();
		EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current));

		while(it != rbVectorString.end()) // This loop should do nothing.
		{
			EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current));
			++it;
		}

		// void push_back(const value_type& value);
		sprintf(counterBuffer, "%d", counter++);
		rbVectorString.push_back(string(counterBuffer));
		EATEST_VERIFY(rbVectorString.validate());
		EATEST_VERIFY(!rbVectorString.empty());
		EATEST_VERIFY(rbVectorString.size() == 1);
		EATEST_VERIFY(rbVectorString.capacity() == 50);

		it = rbVectorString.begin();
		EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));
		EATEST_VERIFY(*it == "0");

		// reference front();
		// reference back();              
		string& sFront = rbVectorString.front();
		string& sBack  = rbVectorString.back();
		EATEST_VERIFY(&sFront == &sBack);

		// void push_back();
		string& ref = rbVectorString.push_back();
		EATEST_VERIFY(rbVectorString.validate());
		EATEST_VERIFY(rbVectorString.size() == 2);
		EATEST_VERIFY(rbVectorString.capacity() == 50);
		EATEST_VERIFY(&ref == &rbVectorString.back());

		it = rbVectorString.begin();
		++it;
		EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));
		EATEST_VERIFY(it->empty());

		sprintf(counterBuffer, "%d", counter++);
		*it = counterBuffer;
		EATEST_VERIFY(*it == "1");

		++it;
		EATEST_VERIFY(it == rbVectorString.end());

		it = rbVectorString.begin();
		while(it != rbVectorString.end())
		{
			EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));
			++it;
		}

		// reference operator[](size_type n);
		string& s0 = rbVectorString[0];
		EATEST_VERIFY(s0 == "0");

		string& s1 = rbVectorString[1];
		EATEST_VERIFY(s1 == "1");

		// Now we start hammering the ring buffer with push_back.
		for(eastl_size_t i = 0, iEnd = rbVectorString.capacity() * 5; i != iEnd; i++)
		{
			sprintf(counterBuffer, "%d", counter++);
			rbVectorString.push_back(string(counterBuffer));
			EATEST_VERIFY(rbVectorString.validate());
		}

		int  counterCheck = counter - 1;
		char counterCheckBuffer[32];
		sprintf(counterCheckBuffer, "%d", counterCheck);
		EATEST_VERIFY(rbVectorString.back() == counterCheckBuffer);

		// reverse_iterator rbegin();
		// reverse_iterator rend();
		for(RBVectorString::reverse_iterator ri = rbVectorString.rbegin(); ri != rbVectorString.rend(); ++ri)
		{
			sprintf(counterCheckBuffer, "%d", counterCheck--);
			EATEST_VERIFY(*ri == counterCheckBuffer);
		}

		++counterCheck;

		// iterator begin();
		// iterator end();
		for(RBVectorString::iterator i = rbVectorString.begin(); i != rbVectorString.end(); ++i)
		{
			EATEST_VERIFY(rbVectorString.validate_iterator(i) == (isf_valid | isf_current | isf_can_dereference));
			EATEST_VERIFY(*i == counterCheckBuffer);
			sprintf(counterCheckBuffer, "%d", ++counterCheck);
		}

		// void clear();
		rbVectorString.clear();
		EATEST_VERIFY(rbVectorString.validate());
		EATEST_VERIFY(rbVectorString.empty());
		EATEST_VERIFY(rbVectorString.size() == 0);
		EATEST_VERIFY(rbVectorString.capacity() == 50);

		// Random operations
		// Not easy to test the expected values without some tedium.
		for(int j = 0; j < 10000 + (gEASTL_TestLevel * 10000); j++)
		{
			sprintf(counterBuffer, "%d", counter++);

			const eastl_size_t op = rng.RandLimit(12);
			const eastl_size_t s  = rbVectorString.size();

			if(op == 0)
			{
				// void push_back(const value_type& value);

				rbVectorString.push_back(string(counterBuffer));
				EATEST_VERIFY(rbVectorString.size() == eastl::min(s + 1, rbVectorString.capacity()));
			}
			else if(op == 1)
			{
				// void push_back();

				string& ref2 = rbVectorString.push_back();
				rbVectorString.back() = string(counterBuffer);
				EATEST_VERIFY(rbVectorString.size() == eastl::min(s + 1, rbVectorString.capacity()));
				EATEST_VERIFY(&ref2 == &rbVectorString.back());
			}
			else if(op == 2)
			{
				// void pop_back();

				if(!rbVectorString.empty())
				{
					rbVectorString.pop_back();
					EATEST_VERIFY(rbVectorString.size() == (s - 1));
				}
			}
			else if(op == 3)
			{
				// void push_front(const value_type& value);

				rbVectorString.push_front(string(counterBuffer));
				EATEST_VERIFY(rbVectorString.size() == eastl::min(s + 1, rbVectorString.capacity()));
			}
			else if(op == 4)
			{
				// void push_front();

				string& ref2 = rbVectorString.push_front();
				rbVectorString.front() = string(counterBuffer);
				EATEST_VERIFY(rbVectorString.size() == eastl::min(s + 1, rbVectorString.capacity()));
				EATEST_VERIFY(&ref2 == &rbVectorString.front());
			}
			else if(op == 5)
			{
				// void pop_front();

				if(!rbVectorString.empty())
				{
					rbVectorString.pop_front();
					EATEST_VERIFY(rbVectorString.size() == (s - 1));
				}
			}
			else if(op == 6)
			{
				// iterator insert(iterator position, const value_type& value);

				it = rbVectorString.begin();
				const eastl_size_t dist = rng.RandLimit((uint32_t)s + 1);
				eastl::advance(it, dist);

				if(it == rbVectorString.end())
					EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current));
				else
					EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));

				rbVectorString.insert(it, string(counterBuffer));
				EATEST_VERIFY(rbVectorString.size() == eastl::min(s + 1, rbVectorString.capacity()));
			}
			else if(op == 7)
			{
				// void insert(iterator position, size_type n, const value_type& value);

				it = rbVectorString.begin();
				const eastl_size_t dist = rng.RandLimit((uint32_t)s + 1);
				eastl::advance(it, dist);

				if(it == rbVectorString.end())
					EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current));
				else
					EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));

				const eastl_size_t count = (eastl_size_t)rng.RandLimit(10);

				rbVectorString.insert(it, count, string(counterBuffer));
				EATEST_VERIFY(rbVectorString.size() == eastl::min(s + count, rbVectorString.capacity()));
			}
			else if(op == 8)
			{
				// template <typename InputIterator>
				// void insert(iterator position, InputIterator first, InputIterator last);

				string stringArray[10];

				it = rbVectorString.begin();
				const eastl_size_t dist = rng.RandLimit((uint32_t)s + 1);
				eastl::advance(it, dist);

				if(it == rbVectorString.end())
					EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current));
				else
					EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));

				const eastl_size_t count = (eastl_size_t)rng.RandLimit(10);

				rbVectorString.insert(it, stringArray, stringArray + count);
				EATEST_VERIFY(rbVectorString.size() == eastl::min(s + count, rbVectorString.capacity()));
			}
			else if(op == 9)
			{
				// iterator erase(iterator position);

				if(!rbVectorString.empty())
				{
					it = rbVectorString.begin();
					const eastl_size_t dist = rng.RandLimit((uint32_t)s);
					eastl::advance(it, dist);
					EATEST_VERIFY(rbVectorString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));
					rbVectorString.erase(it);

					EATEST_VERIFY(rbVectorString.size() == (s  - 1));
				}
			}
			else if(op == 10)
			{
				// iterator erase(iterator first, iterator last);

				if(!rbVectorString.empty())
				{
					RBVectorString::iterator it1 = rbVectorString.begin();
					const eastl_size_t pos = rng.RandLimit((uint32_t)s / 4);
					eastl::advance(it1, pos);
					EATEST_VERIFY(rbVectorString.validate_iterator(it1) == (isf_valid | isf_current | isf_can_dereference));

					RBVectorString::iterator it2 = it1;
					const eastl_size_t dist = rng.RandLimit((uint32_t)s / 4);
					eastl::advance(it2, dist);
					EATEST_VERIFY(rbVectorString.validate_iterator(it2) == (isf_valid | isf_current | isf_can_dereference));

					EATEST_VERIFY(s > (pos + dist));
					rbVectorString.erase(it1, it2);
					EATEST_VERIFY(rbVectorString.size() == (s - dist));
				}
			}
			else if(op == 11)
			{
				// void resize(size_type n);
				const eastl_size_t nSubOp = rng.RandLimit(100);

				if(nSubOp == 1)
				{
					rbVectorString.resize(kOriginalCapacity);
					EATEST_VERIFY(rbVectorString.size() == (RBVectorString::size_type)kOriginalCapacity);
				}
				else if(nSubOp == 2)
				{
					const eastl_size_t newSize = rng.RandLimit((uint32_t)s * 2) + 2;

					rbVectorString.resize(newSize);
					EATEST_VERIFY(rbVectorString.size() == newSize);
				}
				else if(nSubOp == 3)
				{
					rbVectorString.clear();
					EATEST_VERIFY(rbVectorString.size() == 0);
				}
			}

			EATEST_VERIFY(rbVectorString.validate());
		}

		// We make sure that after the above we still have some contents.
		if(rbVectorString.size() < 8)
			rbVectorString.resize(8);

		EATEST_VERIFY(rbVectorString.validate());

		// Test const functions
		// const_iterator begin() const;
		// const_iterator end() const;
		// const_reverse_iterator rbegin() const;
		// const_reverse_iterator rend() const;
		// const_reference front() const;
		// const_reference back() const;
		// const_reference operator[](size_type n) const;
		// const container_type& get_container() const;
		const RBVectorString& rbVSConst = rbVectorString;

		for(RBVectorString::const_iterator ic = rbVSConst.begin(); ic != rbVSConst.end(); ++ic)
		{
			EATEST_VERIFY(rbVectorString.validate_iterator(ic) == (isf_valid | isf_current | isf_can_dereference));
		}

		for(RBVectorString::const_reverse_iterator ric = rbVSConst.rbegin(); ric != rbVSConst.rend(); ++ric)
		{
			if(ric == rbVSConst.rbegin())
				EATEST_VERIFY(rbVectorString.validate_iterator(ric.base()) == (isf_valid | isf_current));
			else
				EATEST_VERIFY(rbVectorString.validate_iterator(ric.base()) == (isf_valid | isf_current | isf_can_dereference));
		}

		EATEST_VERIFY(rbVSConst.front() == rbVectorString.front());
		EATEST_VERIFY(rbVSConst.back() == rbVectorString.back());
		EATEST_VERIFY(rbVSConst[0] == rbVectorString[0]);
		EATEST_VERIFY(&rbVSConst.get_container() == &rbVectorString.get_container());


		// Test additional constructors.
		// ring_buffer(const this_type& x);
		// explicit ring_buffer(const Container& x);
		// this_type& operator=(const this_type& x);
		// void swap(this_type& x);
		RBVectorString rbVectorString2(rbVectorString);
		RBVectorString rbVectorString3(rbVectorString.get_container());
		RBVectorString rbVectorString4(rbVectorString.capacity() / 2);
		RBVectorString rbVectorString5(rbVectorString.capacity() * 2);

		EATEST_VERIFY(rbVectorString.validate());
		EATEST_VERIFY(rbVectorString2.validate());
		EATEST_VERIFY(rbVectorString3.validate());
		EATEST_VERIFY(rbVectorString4.validate());
		EATEST_VERIFY(rbVectorString5.validate());

		EATEST_VERIFY(rbVectorString == rbVectorString2);
		EATEST_VERIFY(rbVectorString3.get_container() == rbVectorString2.get_container());

		rbVectorString3 = rbVectorString4;
		EATEST_VERIFY(rbVectorString3.validate());

		eastl::swap(rbVectorString2, rbVectorString4);
		EATEST_VERIFY(rbVectorString2.validate());
		EATEST_VERIFY(rbVectorString3.validate());
		EATEST_VERIFY(rbVectorString4.validate());
		EATEST_VERIFY(rbVectorString == rbVectorString4);
		EATEST_VERIFY(rbVectorString2 == rbVectorString3);

		// void ring_buffer<T, Container>::reserve(size_type n)
		eastl_size_t cap = rbVectorString2.capacity();
		rbVectorString2.reserve(cap += 2);
		EATEST_VERIFY(rbVectorString2.validate());
		EATEST_VERIFY(rbVectorString2.capacity() == cap);
		rbVectorString2.reserve(cap -= 4); // This should act as a no-op if we are following convention.
		EATEST_VERIFY(rbVectorString2.validate());

		// void ring_buffer<T, Container>::set_capacity(size_type n)
		cap = rbVectorString2.capacity();
		rbVectorString2.resize(cap);
		EATEST_VERIFY(rbVectorString2.size() == cap);
		rbVectorString2.set_capacity(cap += 2);
		EATEST_VERIFY(rbVectorString2.validate());
		EATEST_VERIFY(rbVectorString2.capacity() == cap);
		rbVectorString2.set_capacity(cap -= 4);
		EATEST_VERIFY(rbVectorString2.capacity() == cap);
		EATEST_VERIFY(rbVectorString2.validate());

		// template <typename InputIterator>
		// void assign(InputIterator first, InputIterator last);
		string stringArray[10];
		for(int q = 0; q < 10; q++)
			stringArray[q] = (char)('0' + (char)q);

		rbVectorString5.assign(stringArray, stringArray + 10);
		EATEST_VERIFY(rbVectorString5.validate());
		EATEST_VERIFY(rbVectorString5.size() == 10);
		EATEST_VERIFY(rbVectorString5.front() == "0");
		EATEST_VERIFY(rbVectorString5.back() == "9");
	}


	{
		// Additional testing
		typedef ring_buffer< int, vector<int> > RBVectorInt;

		RBVectorInt rbVectorInt(6);

		rbVectorInt.push_back(0);
		rbVectorInt.push_back(1);
		rbVectorInt.push_back(2);
		rbVectorInt.push_back(3);
		rbVectorInt.push_back(4);
		rbVectorInt.push_back(5);
		EATEST_VERIFY(rbVectorInt[0] == 0);
		EATEST_VERIFY(rbVectorInt[5] == 5);

		// iterator insert(iterator position, const value_type& value);
		rbVectorInt.insert(rbVectorInt.begin(), 999);
		EATEST_VERIFY(rbVectorInt[0] == 999);
		EATEST_VERIFY(rbVectorInt[1] == 0);
		EATEST_VERIFY(rbVectorInt[5] == 4);

		rbVectorInt.clear();
		rbVectorInt.push_back(0);
		rbVectorInt.push_back(1);
		rbVectorInt.push_back(2);
		rbVectorInt.push_back(3);
		rbVectorInt.push_back(4);

		// iterator insert(iterator position, const value_type& value);
		rbVectorInt.insert(rbVectorInt.begin(), 999);
		EATEST_VERIFY(rbVectorInt[0] == 999);
		EATEST_VERIFY(rbVectorInt[1] == 0);
		EATEST_VERIFY(rbVectorInt[5] == 4);

		rbVectorInt.clear();
		rbVectorInt.push_back(0);
		rbVectorInt.push_back(1);
		rbVectorInt.push_back(2);
		rbVectorInt.push_back(3);
		rbVectorInt.push_back(4);
		rbVectorInt.push_back(5);
		rbVectorInt.push_back(6);
		EATEST_VERIFY(rbVectorInt[0] == 1);
		EATEST_VERIFY(rbVectorInt[5] == 6);

		// iterator insert(iterator position, const value_type& value);
		rbVectorInt.insert(rbVectorInt.begin(), 999);
		EATEST_VERIFY(rbVectorInt[0] == 999);
		EATEST_VERIFY(rbVectorInt[1] == 1);
		EATEST_VERIFY(rbVectorInt[5] == 5);

		// iterator insert(iterator position, const value_type& value);
		RBVectorInt::iterator it = rbVectorInt.begin();
		eastl::advance(it, 3);
		rbVectorInt.insert(it, 888);
		EATEST_VERIFY(rbVectorInt[0] == 999);
		EATEST_VERIFY(rbVectorInt[1] == 1);
		EATEST_VERIFY(rbVectorInt[2] == 2);
		EATEST_VERIFY(rbVectorInt[3] == 888);
		EATEST_VERIFY(rbVectorInt[4] == 3);
		EATEST_VERIFY(rbVectorInt[5] == 4);
	}


	{
		EA::UnitTest::Rand rng(EA::UnitTest::GetRandSeed());

		typedef ring_buffer< string, list<string> > RBListString;

		int  counter = 0;
		char counterBuffer[32];

		// explicit ring_buffer(size_type size = 0);
		const int kOriginalCapacity = 50;
		RBListString rbListString(50);

		// bool empty() const;
		// size_type size() const;
		// bool validate() const;
		EATEST_VERIFY(rbListString.validate());
		EATEST_VERIFY(rbListString.empty());
		EATEST_VERIFY(rbListString.size() == 0);
		EATEST_VERIFY(rbListString.capacity() == 50);

		// void clear();
		rbListString.clear();
		EATEST_VERIFY(rbListString.validate());
		EATEST_VERIFY(rbListString.empty());
		EATEST_VERIFY(rbListString.size() == 0);
		EATEST_VERIFY(rbListString.capacity() == 50);

		// container_type& get_container();
		RBListString::container_type& c = rbListString.get_container();
		EATEST_VERIFY(c.size() == (kOriginalCapacity + 1)); // We need to add one because the ring_buffer mEnd is necessarily an unused element.

		// iterator begin();
		// iterator end();
		// int validate_iterator(const_iterator i) const;
		RBListString::iterator it = rbListString.begin();
		EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current));

		while(it != rbListString.end()) // This loop should do nothing.
		{
			EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current));
			++it;
		}

		// void push_back(const value_type& value);
		sprintf(counterBuffer, "%d", counter++);
		rbListString.push_back(string(counterBuffer));
		EATEST_VERIFY(rbListString.validate());
		EATEST_VERIFY(!rbListString.empty());
		EATEST_VERIFY(rbListString.size() == 1);
		EATEST_VERIFY(rbListString.capacity() == 50);

		it = rbListString.begin();
		EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));
		EATEST_VERIFY(*it == "0");

		// reference front();
		// reference back();              
		string& sFront = rbListString.front();
		string& sBack  = rbListString.back();
		EATEST_VERIFY(&sFront == &sBack);

		// void push_back();
		string& ref = rbListString.push_back();
		EATEST_VERIFY(rbListString.validate());
		EATEST_VERIFY(rbListString.size() == 2);
		EATEST_VERIFY(rbListString.capacity() == 50);
		EATEST_VERIFY(&ref == &rbListString.back());

		it = rbListString.begin();
		++it;
		EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));
		EATEST_VERIFY(it->empty());

		sprintf(counterBuffer, "%d", counter++);
		*it = counterBuffer;
		EATEST_VERIFY(*it == "1");

		++it;
		EATEST_VERIFY(it == rbListString.end());

		it = rbListString.begin();
		while(it != rbListString.end())
		{
			EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));
			++it;
		}

		// reference operator[](size_type n);
		string& s0 = rbListString[0];
		EATEST_VERIFY(s0 == "0");

		string& s1 = rbListString[1];
		EATEST_VERIFY(s1 == "1");

		// Now we start hammering the ring buffer with push_back.
		for(eastl_size_t i = 0, iEnd = rbListString.capacity() * 5; i != iEnd; i++)
		{
			sprintf(counterBuffer, "%d", counter++);
			rbListString.push_back(string(counterBuffer));
			EATEST_VERIFY(rbListString.validate());
		}

		int  counterCheck = counter - 1;
		char counterCheckBuffer[32];
		sprintf(counterCheckBuffer, "%d", counterCheck);
		EATEST_VERIFY(rbListString.back() == counterCheckBuffer);

		// reverse_iterator rbegin();
		// reverse_iterator rend();
		for(RBListString::reverse_iterator ri = rbListString.rbegin(); ri != rbListString.rend(); ++ri)
		{
			sprintf(counterCheckBuffer, "%d", counterCheck--);
			EATEST_VERIFY(*ri == counterCheckBuffer);
		}

		++counterCheck;

		// iterator begin();
		// iterator end();
		for(RBListString::iterator i = rbListString.begin(); i != rbListString.end(); ++i)
		{
			EATEST_VERIFY(rbListString.validate_iterator(i) == (isf_valid | isf_current | isf_can_dereference));
			EATEST_VERIFY(*i == counterCheckBuffer);
			sprintf(counterCheckBuffer, "%d", ++counterCheck);
		}

		// void clear();
		rbListString.clear();
		EATEST_VERIFY(rbListString.validate());
		EATEST_VERIFY(rbListString.empty());
		EATEST_VERIFY(rbListString.size() == 0);
		EATEST_VERIFY(rbListString.capacity() == 50);

		// Random operations
		// Not easy to test the expected values without some tedium.
		for(int j = 0; j < 10000 + (gEASTL_TestLevel * 10000); j++)
		{
			sprintf(counterBuffer, "%d", counter++);

			const eastl_size_t op = rng.RandLimit(12);
			const eastl_size_t s  = rbListString.size();

			if(op == 0)
			{
				// void push_back(const value_type& value);

				rbListString.push_back(string(counterBuffer));
				EATEST_VERIFY(rbListString.size() == eastl::min(s + 1, rbListString.capacity()));
			}
			else if(op == 1)
			{
				// void push_back();

				string& ref2 = rbListString.push_back();
				rbListString.back() = string(counterBuffer);
				EATEST_VERIFY(rbListString.size() == eastl::min(s + 1, rbListString.capacity()));
				EATEST_VERIFY(&ref2 == &rbListString.back());
			}
			else if(op == 2)
			{
				// void pop_back();

				if(!rbListString.empty())
				{
					rbListString.pop_back();
					EATEST_VERIFY(rbListString.size() == (s - 1));
				}
			}
			else if(op == 3)
			{
				// void push_front(const value_type& value);

				rbListString.push_front(string(counterBuffer));
				EATEST_VERIFY(rbListString.size() == eastl::min(s + 1, rbListString.capacity()));
			}
			else if(op == 4)
			{
				// void push_front();

				string& ref2 = rbListString.push_front();
				rbListString.front() = string(counterBuffer);
				EATEST_VERIFY(rbListString.size() == eastl::min(s + 1, rbListString.capacity()));
				EATEST_VERIFY(&ref2 == &rbListString.front());
			}
			else if(op == 5)
			{
				// void pop_front();

				if(!rbListString.empty())
				{
					rbListString.pop_front();
					EATEST_VERIFY(rbListString.size() == (s - 1));
				}
			}
			else if(op == 6)
			{
				// iterator insert(iterator position, const value_type& value);

				it = rbListString.begin();
				const eastl_size_t dist = rng.RandLimit((uint32_t)s + 1);
				eastl::advance(it, dist);

				if(it == rbListString.end())
					EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current));
				else
					EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));

				rbListString.insert(it, string(counterBuffer));
				EATEST_VERIFY(rbListString.size() == eastl::min(s + 1, rbListString.capacity()));
			}
			else if(op == 7)
			{
				// void insert(iterator position, size_type n, const value_type& value);

				it = rbListString.begin();
				const eastl_size_t dist = rng.RandLimit((uint32_t)s + 1);
				eastl::advance(it, dist);

				if(it == rbListString.end())
					EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current));
				else
					EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));

				const eastl_size_t count = (eastl_size_t)rng.RandLimit(10);

				rbListString.insert(it, count, string(counterBuffer));
				EATEST_VERIFY(rbListString.size() == eastl::min(s + count, rbListString.capacity()));
			}
			else if(op == 8)
			{
				// template <typename InputIterator>
				// void insert(iterator position, InputIterator first, InputIterator last);

				string stringArray[10];

				it = rbListString.begin();
				const eastl_size_t dist = rng.RandLimit((uint32_t)s + 1);
				eastl::advance(it, dist);

				if(it == rbListString.end())
					EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current));
				else
					EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));

				const eastl_size_t count = (eastl_size_t)rng.RandLimit(10);

				rbListString.insert(it, stringArray, stringArray + count);
				EATEST_VERIFY(rbListString.size() == eastl::min(s + count, rbListString.capacity()));
			}
			else if(op == 9)
			{
				// iterator erase(iterator position);

				if(!rbListString.empty())
				{
					it = rbListString.begin();
					const eastl_size_t dist = rng.RandLimit((uint32_t)s);
					eastl::advance(it, dist);
					EATEST_VERIFY(rbListString.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference));
					rbListString.erase(it);

					EATEST_VERIFY(rbListString.size() == (s  - 1));
				}
			}
			else if(op == 10)
			{
				// iterator erase(iterator first, iterator last);

				if(!rbListString.empty())
				{
					RBListString::iterator it1 = rbListString.begin();
					const eastl_size_t pos = rng.RandLimit((uint32_t)s / 4);
					eastl::advance(it1, pos);
					EATEST_VERIFY(rbListString.validate_iterator(it1) == (isf_valid | isf_current | isf_can_dereference));

					RBListString::iterator it2 = it1;
					const eastl_size_t dist = rng.RandLimit((uint32_t)s / 4);
					eastl::advance(it2, dist);
					EATEST_VERIFY(rbListString.validate_iterator(it2) == (isf_valid | isf_current | isf_can_dereference));

					EATEST_VERIFY(s > (pos + dist));
					rbListString.erase(it1, it2);
					EATEST_VERIFY(rbListString.size() == (s - dist));
				}
			}
			else if(op == 11)
			{
				// void resize(size_type n);
				const eastl_size_t nSubOp = rng.RandLimit(100);

				if(nSubOp == 1)
				{
					rbListString.resize(kOriginalCapacity);
					EATEST_VERIFY(rbListString.size() == (RBListString::size_type)kOriginalCapacity);
				}
				else if(nSubOp == 2)
				{
					const eastl_size_t newSize = rng.RandLimit((uint32_t)s * 2) + 2;

					rbListString.resize(newSize);
					EATEST_VERIFY(rbListString.size() == newSize);
				}
				else if(nSubOp == 3)
				{
					rbListString.clear();
					EATEST_VERIFY(rbListString.size() == 0);
				}
			}

			EATEST_VERIFY(rbListString.validate());
		}

		// We make sure that after the above we still have some contents.
		if(rbListString.size() < 8)
			rbListString.resize(8);

		EATEST_VERIFY(rbListString.validate());

		// Test const functions
		// const_iterator begin() const;
		// const_iterator end() const;
		// const_reverse_iterator rbegin() const;
		// const_reverse_iterator rend() const;
		// const_reference front() const;
		// const_reference back() const;
		// const_reference operator[](size_type n) const;
		// const container_type& get_container() const;
		const RBListString& rbVSConst = rbListString;

		for(RBListString::const_iterator ic = rbVSConst.begin(); ic != rbVSConst.end(); ++ic)
		{
			EATEST_VERIFY(rbListString.validate_iterator(ic) == (isf_valid | isf_current | isf_can_dereference));
		}

		for(RBListString::const_reverse_iterator ric = rbVSConst.rbegin(); ric != rbVSConst.rend(); ++ric)
		{
			if(ric == rbVSConst.rbegin())
				EATEST_VERIFY(rbListString.validate_iterator(ric.base()) == (isf_valid | isf_current));
			else
				EATEST_VERIFY(rbListString.validate_iterator(ric.base()) == (isf_valid | isf_current | isf_can_dereference));
		}

		EATEST_VERIFY(rbVSConst.front() == rbListString.front());
		EATEST_VERIFY(rbVSConst.back() == rbListString.back());
		EATEST_VERIFY(rbVSConst[0] == rbListString[0]);
		EATEST_VERIFY(&rbVSConst.get_container() == &rbListString.get_container());


		// Test additional constructors.
		// ring_buffer(const this_type& x);
		// explicit ring_buffer(const Container& x);
		// this_type& operator=(const this_type& x);
		// void swap(this_type& x);
		RBListString rbListString2(rbListString);
		RBListString rbListString3(rbListString.get_container());
		RBListString rbListString4(rbListString.capacity() / 2);
		RBListString rbListString5(rbListString.capacity() * 2);

		EATEST_VERIFY(rbListString.validate());
		EATEST_VERIFY(rbListString2.validate());
		EATEST_VERIFY(rbListString3.validate());
		EATEST_VERIFY(rbListString4.validate());
		EATEST_VERIFY(rbListString5.validate());

		EATEST_VERIFY(rbListString == rbListString2);
		EATEST_VERIFY(rbListString3.get_container() == rbListString2.get_container());

		rbListString3 = rbListString4;
		EATEST_VERIFY(rbListString3.validate());

		eastl::swap(rbListString2, rbListString4);
		EATEST_VERIFY(rbListString2.validate());
		EATEST_VERIFY(rbListString3.validate());
		EATEST_VERIFY(rbListString4.validate());
		EATEST_VERIFY(rbListString == rbListString4);
		EATEST_VERIFY(rbListString2 == rbListString3);

		// void ring_buffer<T, Container>::reserve(size_type n)
		eastl_size_t cap = rbListString2.capacity();
		rbListString2.reserve(cap += 2);
		EATEST_VERIFY(rbListString2.validate());
		EATEST_VERIFY(rbListString2.capacity() == cap);
		rbListString2.reserve(cap -= 4); // This should act as a no-op if we are following convention.
		EATEST_VERIFY(rbListString2.validate());


		// template <typename InputIterator>
		// void assign(InputIterator first, InputIterator last);
		string stringArray[10];
		for(int q = 0; q < 10; q++)
			stringArray[q] = '0' + (char)q;

		rbListString5.assign(stringArray, stringArray + 10);
		EATEST_VERIFY(rbListString5.validate());
		EATEST_VERIFY(rbListString5.size() == 10);
		EATEST_VERIFY(rbListString5.front() == "0");
		EATEST_VERIFY(rbListString5.back() == "9");


		// ring_buffer(this_type&& x);
		// ring_buffer(this_type&& x, const allocator_type& allocator);
		// this_type& operator=(this_type&& x);

		RBListString rbListStringM1(eastl::move(rbListString5));
		EATEST_VERIFY(rbListStringM1.validate() && rbListString5.validate());
		EATEST_VERIFY((rbListStringM1.size() == 10) && (rbListString5.size() == 0));

		RBListString rbListStringM2(eastl::move(rbListStringM1), RBListString::allocator_type());
		EATEST_VERIFY(rbListStringM2.validate() && rbListStringM1.validate());
		EATEST_VERIFY((rbListStringM2.size() == 10) && (rbListStringM1.size() == 0));

		rbListStringM1 = eastl::move(rbListStringM2);
		EATEST_VERIFY(rbListStringM1.validate() && rbListStringM2.validate());
		EATEST_VERIFY((rbListStringM1.size() == 10) && (rbListStringM2.size() == 0));
	}



	{
		// ring_buffer(std::initializer_list<value_type> ilist, const allocator_type& allocator = allocator_type());
		// this_type& operator=(std::initializer_list<value_type> ilist);
		// void     insert(iterator position, std::initializer_list<value_type> ilist);
		#if !defined(EA_COMPILER_NO_INITIALIZER_LISTS)
			ring_buffer<int> intBuffer = { 0, 1, 2 };
			EATEST_VERIFY(VerifySequence(intBuffer.begin(), intBuffer.end(), int(), "ring_buffer std::initializer_list", 0, 1, 2, -1));

			intBuffer = { 16, 17, 18 };
			EATEST_VERIFY(VerifySequence(intBuffer.begin(), intBuffer.end(), int(), "ring_buffer std::initializer_list", 16, 17, 18, -1));

			// We need to increase the capacity in order to insert new items because the ctor above set the capacity to be only enough to hold the initial list.
			intBuffer.reserve(intBuffer.capacity() + 2);
			intBuffer.insert(intBuffer.begin(), { 14, 15 });
			EATEST_VERIFY(VerifySequence(intBuffer.begin(), intBuffer.end(), int(), "ring_buffer std::initializer_list", 14, 15, 16, 17, 18, -1));
		#endif
	}

	{
		// Regression for user-reported problem.
		typedef eastl::fixed_vector<float, 8>                     GamepadData_t;
		typedef eastl::ring_buffer<GamepadData_t>                 GamepadDataDelayBuffer_t;
		typedef eastl::fixed_vector<GamepadDataDelayBuffer_t, 32> GamepadDataDelayBufferTable_t;

		GamepadDataDelayBufferTable_t mDelayTable;
		mDelayTable.resize(32);
		for(eastl_size_t i = 0; i < mDelayTable.size(); i++)
			mDelayTable[i].reserve(16);
		
		GamepadData_t data(8, 1.f);
		mDelayTable[0].push_back(data);
		mDelayTable[0].push_back(data);
		mDelayTable[0].push_back(data);
		mDelayTable[0].push_back(data);

		EATEST_VERIFY(mDelayTable[0].size() == 4);
		GamepadData_t dataFront = mDelayTable[0].front();
		EATEST_VERIFY((dataFront.size() == 8) && (dataFront[0] == 1.f));
		mDelayTable[0].pop_front();
	}

	{
		// Regression for bug with iterator subtraction
		typedef eastl::ring_buffer<int>		IntBuffer_t;
		IntBuffer_t intBuffer = { 0, 1, 2, 3, 4, 5, 6, 7 };
		IntBuffer_t::iterator it = intBuffer.begin();

		EATEST_VERIFY(*it == 0);
		it += 4;
		EATEST_VERIFY(*it == 4);
		it--;
		EATEST_VERIFY(*it == 3);
		it -= 2;
		EATEST_VERIFY(*it == 1);

		intBuffer.push_back(8);
		intBuffer.push_back(9);
		intBuffer.push_back(10);
		intBuffer.push_back(11);

		EATEST_VERIFY(*it == 10);
		it -= 3;
		EATEST_VERIFY(*it == 7); // Test looping around the end of the underlying container
		it -= 5;
		EATEST_VERIFY(*it == 11); // Test wrapping around begin to end of the ring_buffer
		it -= 2;
		EATEST_VERIFY(*it == 9); // It is important to test going back to the beginning of the underlying container.

	}

	// fixed_ring_buffer<T,N> tests
	// ring_buffer<T, fixed_vector<T,N>> tests
	{
		{
			// (MAX_ELEMENTS - 1) accommodates the ring_buffer sentinel
			const int MAX_ELEMENTS = 8;
			eastl::ring_buffer<int, eastl::fixed_vector<int, MAX_ELEMENTS, false>> rb(MAX_ELEMENTS - 1);  

			for (int i = 0; i < MAX_ELEMENTS - 1; i++)
				rb.push_back(i);

			auto it = rb.begin();
			for (int i = 0; i < MAX_ELEMENTS - 1; i++)
				{ EATEST_VERIFY(*it++ == i); }
		}

		#if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES)
		{
			const int MAX_ELEMENTS = 25;
			eastl::fixed_ring_buffer<int, MAX_ELEMENTS> rb(MAX_ELEMENTS);

			for(int i = 0; i < MAX_ELEMENTS; i++)
				rb.push_back(i);

			auto it = rb.begin();
			for(int i = 0; i < MAX_ELEMENTS; i++)
				{ EATEST_VERIFY(*it++ == i); }
		}
		#endif

		#if !defined(EA_COMPILER_NO_INITIALIZER_LISTS) && !defined(EA_COMPILER_NO_TEMPLATE_ALIASES)
		{
			const int MAX_ELEMENTS = 8;
			eastl::fixed_ring_buffer<int, MAX_ELEMENTS> rb = {0, 1, 2, 3, 4, 5, 6, 7};

			auto it = rb.begin();
			for(int i = 0; i < MAX_ELEMENTS; i++)
				{ EATEST_VERIFY(*it++ == i); }
		}

		{
			struct LocalStruct {};
			fixed_ring_buffer<LocalStruct, 8> rb = {{{}, {}, {}}};
			EATEST_VERIFY(rb.size() == 3);
		}
		#endif
	}

	{
		const auto MAX_ELEMENTS = EASTL_MAX_STACK_USAGE;

		// create a container simulating LARGE state that exceeds
		// our maximum stack size macro. This forces our ring_buffer implementation
		// to allocate the container in the heap instead of holding it on the stack.
		// This test ensures that allocation is NOT serviced by the default global heap.  
		// Instead it is serviced by the allocator of the ring_buffers underlying container.
		struct PaddedVector : public eastl::vector<int, MallocAllocator>
		{
			char mPadding[EASTL_MAX_STACK_USAGE];
		};

		MallocAllocator::reset_all();
		CountingAllocator::resetCount();

		{
			CountingAllocator countingAlloc;
			AutoDefaultAllocator _(&countingAlloc);

			eastl::ring_buffer<int, PaddedVector> intBuffer(1);  
			for (int i = 0; i < MAX_ELEMENTS; i++)
				intBuffer.push_back(i);

		#if !EASTL_OPENSOURCE
			const auto cacheAllocationCount = gEASTLTest_TotalAllocationCount;
		#endif
			const auto cacheMallocatorCount = MallocAllocator::mAllocCountAll;
			const auto forceReAllocSize = intBuffer.size() * 2;

			intBuffer.resize(forceReAllocSize);

		#if !EASTL_OPENSOURCE
			VERIFY(cacheAllocationCount == gEASTLTest_TotalAllocationCount);
		#endif
			VERIFY(cacheMallocatorCount <  MallocAllocator::mAllocCountAll);
			VERIFY(CountingAllocator::neverUsed());
		}
	}

	return nErrorCount;
}









