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


#ifndef EASTLTEST_H
#define EASTLTEST_H


#include <EABase/eabase.h>
#include <EATest/EATest.h>

EA_DISABLE_ALL_VC_WARNINGS()
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <vector>   // For the STD_STL_TYPE defines below.
#if EASTL_EXCEPTIONS_ENABLED
	#include <stdexcept>
	#include <new>
#endif
EA_RESTORE_ALL_VC_WARNINGS();


int TestAlgorithm();
int TestAllocator();
int TestAny();
int TestArray();
int TestBitVector();
int TestBitset();
int TestCharTraits();
int TestChrono();
int TestCppCXTypeTraits();
int TestDeque();
int TestExtra();
int TestFinally();
int TestFixedFunction();
int TestFixedHash();
int TestFixedList();
int TestFixedMap();
int TestFixedSList();
int TestFixedSet();
int TestFixedString();
int TestFixedTupleVector();
int TestFixedVector();
int TestFunctional();
int TestHash();
int TestHeap();
int TestIntrusiveHash();
int TestIntrusiveList();
int TestIntrusiveSDList();
int TestIntrusiveSList();
int TestIterator();
int TestList();
int TestListMap();
int TestLruCache();
int TestMap();
int TestMemory();
int TestMeta();
int TestNumericLimits();
int TestOptional();
int TestRandom();
int TestRatio();
int TestRingBuffer();
int TestSList();
int TestSegmentedVector();
int TestSet();
int TestSmartPtr();
int TestSort();
int TestSpan();
int TestString();
int TestStringHashMap();
int TestStringMap();
int TestStringView();
int TestTuple();
int TestTupleVector();
int TestTypeTraits();
int TestUtility();
int TestVariant();
int TestVector();
int TestVectorMap();
int TestVectorSet();
int TestAtomicBasic();
int TestAtomicAsm();
int TestBitcast();


// Now enable warnings as desired.
#ifdef _MSC_VER
	#pragma warning(disable: 4324)      // 'struct_name' : structure was padded due to __declspec(align())
  //#pragma warning(disable: 4512)      // 'class' : assignment operator could not be generated
  //#pragma warning(disable: 4100)      // 'identifier' : unreferenced formal parameter
  //#pragma warning(disable: 4706)      // assignment within conditional expression

	#pragma warning(default: 4056)      // Floating-point constant arithmetic generates a result that exceeds the maximum allowable value
	#pragma warning(default: 4061)      // The enumerate has no associated handler in a switch statement
	#pragma warning(default: 4062)      // The enumerate has no associated handler in a switch statement, and there is no default label
	#pragma warning(default: 4191)      // Calling this function through the result pointer may cause your program to crash
	#pragma warning(default: 4217)      // Member template functions cannot be used for copy-assignment or copy-construction
  //#pragma warning(default: 4242)      // 'variable' : conversion from 'type' to 'type', possible loss of data
	#pragma warning(default: 4254)      // 'operator' : conversion from 'type1' to 'type2', possible loss of data
	#pragma warning(default: 4255)      // 'function' : no function prototype given: converting '()' to '(void)'
	#pragma warning(default: 4263)      // 'function' : member function does not override any base class virtual member function
	#pragma warning(default: 4264)      // 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden
	#pragma warning(default: 4287)      // 'operator' : unsigned/negative constant mismatch
	#pragma warning(default: 4289)      // Nonstandard extension used : 'var' : loop control variable declared in the for-loop is used outside the for-loop scope
	#pragma warning(default: 4296)      // 'operator' : expression is always false
	#pragma warning(default: 4302)      // 'conversion' : truncation from 'type 1' to 'type 2'
	#pragma warning(default: 4339)      // 'type' : use of undefined type detected in CLR meta-data - use of this type may lead to a runtime exception
	#pragma warning(default: 4347)      // Behavior change: 'function template' is called instead of 'function'
  //#pragma warning(default: 4514)      // unreferenced inline/local function has been removed
	#pragma warning(default: 4529)      // 'member_name' : forming a pointer-to-member requires explicit use of the address-of operator ('&') and a qualified name
	#pragma warning(default: 4545)      // Expression before comma evaluates to a function which is missing an argument list
	#pragma warning(default: 4546)      // Function call before comma missing argument list
	#pragma warning(default: 4547)      // 'operator' : operator before comma has no effect; expected operator with side-effect
  //#pragma warning(default: 4548)      // expression before comma has no effect; expected expression with side-effect
	#pragma warning(default: 4549)      // 'operator' : operator before comma has no effect; did you intend 'operator'?
	#pragma warning(default: 4536)      // 'type name' : type-name exceeds meta-data limit of 'limit' characters
	#pragma warning(default: 4555)      // Expression has no effect; expected expression with side-effect
	#pragma warning(default: 4557)      // '__assume' contains effect 'effect'
  //#pragma warning(default: 4619)      // #pragma warning : there is no warning number 'number'
	#pragma warning(default: 4623)      // 'derived class' : default constructor could not be generated because a base class default constructor is inaccessible
  //#pragma warning(default: 4625)      // 'derived class' : copy constructor could not be generated because a base class copy constructor is inaccessible
  //#pragma warning(default: 4626)      // 'derived class' : assignment operator could not be generated because a base class assignment operator is inaccessible
	#pragma warning(default: 4628)      // Digraphs not supported with -Ze. Character sequence 'digraph' not interpreted as alternate token for 'char'
	#pragma warning(default: 4640)      // 'instance' : construction of local static object is not thread-safe
	#pragma warning(default: 4668)      // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
	#pragma warning(default: 4682)      // 'parameter' : no directional parameter attribute specified, defaulting to [in]
	#pragma warning(default: 4686)      // 'user-defined type' : possible change in behavior, change in UDT return calling convention
  //#pragma warning(default: 4710)      // 'function' : function not inlined
  //#pragma warning(default: 4786)      // 'identifier' : identifier was truncated to 'number' characters in the debug information
	#pragma warning(default: 4793)      // Native code generated for function 'function': 'reason'
  //#pragma warning(default: 4820)      // 'bytes' bytes padding added after member 'member'
	#pragma warning(default: 4905)      // Wide string literal cast to 'LPSTR'
	#pragma warning(default: 4906)      // String literal cast to 'LPWSTR'
	#pragma warning(default: 4917)      // 'declarator' : a GUID cannot only be associated with a class, interface or namespace
	#pragma warning(default: 4928)      // Illegal copy-initialization; more than one user-defined conversion has been implicitly applied
	#pragma warning(default: 4931)      // We are assuming the type library was built for number-bit pointers
	#pragma warning(default: 4946)      // reinterpret_cast used between related classes: 'class1' and 'class2'

#endif


///////////////////////////////////////////////////////////////////////////////
// EASTL includes
//
// Intentionally keep these includes below the warning settings specified above.
//
#include <EASTL/iterator.h>
#include <EASTL/algorithm.h>




/// EASTL_TestLevel
///
/// Defines how extensive our testing is. A low level is for a desktop or 
/// nightly build in which the test can run quickly but still hit the 
/// majority of functionality. High level is for heavy testing and internal
/// validation which may take numerous hours to run.
///
enum EASTL_TestLevel
{
	kEASTL_TestLevelLow  = 1,   /// ~10 seconds for test completion.
	kEASTL_TestLevelHigh = 10   /// Numerous hours for test completion.
};

extern int gEASTL_TestLevel;



/// EASTLTest_CheckMemory
///
/// Does a global memory heap validation check. Returns 0 if OK and 
/// an error count if there is a problem. 
///
/// Example usage:
///    EASTLTest_CheckMemory();
///
int EASTLTest_CheckMemory_Imp(const char* pFile, int nLine);
#define EASTLTest_CheckMemory() EASTLTest_CheckMemory_Imp(__FILE__, __LINE__)



// EASTLTEST_STD_STL_VER
//
#if defined(_STLPORT_VERSION)
	#define EASTLTEST_STD_STL_VER_STLPORT
#elif defined(_RWSTD_VER_STR) || defined(_RWSTD_NAMESPACE_END)
	#define EASTLTEST_STD_STL_VER_APACHE
#elif defined(_CPPLIB_VER)
	#define EASTLTEST_STD_STL_VER_DINKUMWARE
#elif defined(__GNUC__) && defined(_CXXCONFIG)
	#define EASTLTEST_STD_STL_VER_GCC
#else
	#define EASTLTEST_STD_STL_VER_UNKNOWN
#endif



/// StdSTLType
///
enum StdSTLType
{
	kSTLUnknown,    // Unknown type
	kSTLPort,       // STLPort. Descendent of the old HP / SGI STL.
	kSTLApache,     // Apache stdcxx (previously RogueWave), which is a descendent of the old HP / SGI STL.
	kSTLClang,      // Clang native. a.k.a. libc++
	kSTLGCC,        // GCC native. a.k.a. libstdc++
	kSTLMS,         // Microsoft. Tweaked version of Dinkumware.
	kSTLDinkumware  // Generic Dinkumware
};

StdSTLType GetStdSTLType();




/// GetStdSTLName
///
/// Returns the name of the std C++ STL available to the current build.
/// The returned value will be one of:
///     "STLPort"
///     "GCC"
///     "VC++"
//      "Apache" // Previously RogueWave
///
const char* GetStdSTLName();


/// gEASTLTest_AllocationCount
///
extern int gEASTLTest_AllocationCount; 
extern int gEASTLTest_TotalAllocationCount; 



// For backwards compatibility:
#define EASTLTest_Printf EA::UnitTest::Report
#define VERIFY           EATEST_VERIFY


///////////////////////////////////////////////////////////////////////////////
/// EASTLTest_Rand
///
/// Implements a basic random number generator for EASTL unit tests. It's not
/// intended to be a robust random number generator (though it is decent), 
/// but rather is present so the unit tests can have a portable random number
/// generator they can rely on being present.
///
/// Example usage:
///    EASTLTest_Rand rng;
///    eastl_size_t   x = rng();                      // Generate value in range of [0, 0xffffffff] (i.e. generate any uint32_t)
///    eastl_ssize_t  y = rng.Rand(1000);             // Generate value in range of [0, 1000)
///    eastl_ssize_t  z = rng.RandRange(-50, +30);    // Generate value in range of [-50, +30)
///
/// Example usage in the random_shuffle algorithm:
///    EASTLTest_Rand rng;
///    random_shuffle(first, last, rnd);
///
class EASTLTest_Rand
{
public:
	EASTLTest_Rand(eastl_size_t nSeed) // The user must supply a seed; we don't provide default values.
		: mnSeed(nSeed) { }

	eastl_size_t Rand()
	{
		// This is not designed to be a high quality random number generator.
		if(mnSeed == 0)
			mnSeed = UINT64_C(0xfefefefefefefefe); // Can't have a seed of zero.

		const uint64_t nResult64A = ((mnSeed     * UINT64_C(6364136223846793005)) + UINT64_C(1442695040888963407));
		const uint64_t nResult64B = ((nResult64A * UINT64_C(6364136223846793005)) + UINT64_C(1442695040888963407));

		mnSeed = (nResult64A >> 32) ^ nResult64B;

		return (eastl_size_t)mnSeed; // For eastl_size_t == uint32_t, this is a chop.
	}

	eastl_size_t operator()() // Returns a pseudorandom value in range of [0, 0xffffffffffffffff)] (i.e. generate any eastl_size_t)
		{ return Rand(); }

	eastl_size_t operator()(eastl_size_t n)  // Returns a pseudorandom value in range of [0, n)
		{ return RandLimit(n); }

	eastl_size_t RandLimit(eastl_size_t nLimit) // Returns a pseudorandom value in range of [0, nLimit)
	{
		// Can't do the following correct solution because we don't have a portable int128_t to work with. 
		// We could implement a 128 bit multiply manually. See EAStdC/int128_t.cpp.
		// return (eastl_size_t)((Rand() * (uint128_t)nLimit) >> 64);

		return (Rand() % nLimit); // This results in an imperfect distribution, especially for the case of nLimit being high relative to eastl_size_t.
	}

	eastl_ssize_t RandRange(eastl_ssize_t nBegin, eastl_ssize_t nEnd)   // Returns a pseudorandom value in range of [nBegin, nEnd)
		{ return nBegin + (eastl_ssize_t)RandLimit((eastl_size_t)(nEnd - nBegin)); }

protected:
	uint64_t mnSeed;
};


///////////////////////////////////////////////////////////////////////////////
/// RandGenT
///
/// A wrapper for EASTLTest_Rand which generates values of the given integral
/// data type. This is mostly useful for clearnly avoiding compiler warnings, 
/// as we intentionally enable the highest warning levels in these tests.
///
template <typename Integer>
struct RandGenT
{
	RandGenT(eastl_size_t nSeed)
		: mRand(nSeed) { }

	Integer operator()()
		{ return (Integer)mRand.Rand(); }

	Integer operator()(eastl_size_t n)
		{ return (Integer)mRand.RandLimit(n); }

	EASTLTest_Rand mRand;
};



///////////////////////////////////////////////////////////////////////////////
/// kMagicValue
///
/// Used as a unique integer. We assign this to TestObject in its constructor
/// and verify in the TestObject destructor that the value is unchanged. 
/// This can be used to tell, for example, if an invalid object is being 
/// destroyed.
///
const uint32_t kMagicValue = 0x01f1cbe8;


///////////////////////////////////////////////////////////////////////////////
/// TestObject
///
/// Implements a generic object that is suitable for use in container tests.
/// Note that we choose a very restricted set of functions that are available
/// for this class. Do not add any additional functions, as that would 
/// compromise the intentions of the unit tests.
///
struct TestObject
{
	int             mX;                  // Value for the TestObject.
	bool            mbThrowOnCopy;       // Throw an exception of this object is copied, moved, or assigned to another.
	int64_t         mId;                 // Unique id for each object, equal to its creation number. This value is not coped from other TestObjects during any operations, including moves.
	uint32_t        mMagicValue;         // Used to verify that an instance is valid and that it is not corrupted. It should always be kMagicValue.
	static int64_t  sTOCount;            // Count of all current existing TestObjects.
	static int64_t  sTOCtorCount;        // Count of times any ctor was called.
	static int64_t  sTODtorCount;        // Count of times dtor was called.
	static int64_t  sTODefaultCtorCount; // Count of times the default ctor was called.
	static int64_t  sTOArgCtorCount;     // Count of times the x0,x1,x2 ctor was called.
	static int64_t  sTOCopyCtorCount;    // Count of times copy ctor was called.
	static int64_t  sTOMoveCtorCount;    // Count of times move ctor was called.
	static int64_t  sTOCopyAssignCount;  // Count of times copy assignment was called.
	static int64_t  sTOMoveAssignCount;  // Count of times move assignment was called.
	static int      sMagicErrorCount;    // Number of magic number mismatch errors.

	explicit TestObject(int x = 0, bool bThrowOnCopy = false)
		: mX(x), mbThrowOnCopy(bThrowOnCopy), mMagicValue(kMagicValue)
	{
		++sTOCount;
		++sTOCtorCount;
		++sTODefaultCtorCount;
		mId = sTOCtorCount;
	}

	// This constructor exists for the purpose of testing variadiac template arguments, such as with the emplace container functions.
	TestObject(int x0, int x1, int x2, bool bThrowOnCopy = false)
		: mX(x0 + x1 + x2), mbThrowOnCopy(bThrowOnCopy), mMagicValue(kMagicValue)
	{
		++sTOCount;
		++sTOCtorCount;
		++sTOArgCtorCount;
		mId = sTOCtorCount;
	}

	TestObject(const TestObject& testObject)
		: mX(testObject.mX), mbThrowOnCopy(testObject.mbThrowOnCopy), mMagicValue(testObject.mMagicValue)
	{
		++sTOCount;
		++sTOCtorCount;
		++sTOCopyCtorCount;
		mId = sTOCtorCount;
		if(mbThrowOnCopy)
		{
			#if EASTL_EXCEPTIONS_ENABLED
				throw "Disallowed TestObject copy";
			#endif
		}
	}

	// Due to the nature of TestObject, there isn't much special for us to 
	// do in our move constructor. A move constructor swaps its contents with 
	// the other object, whhich is often a default-constructed object.
	TestObject(TestObject&& testObject)
		: mX(testObject.mX), mbThrowOnCopy(testObject.mbThrowOnCopy), mMagicValue(testObject.mMagicValue)
	{
		++sTOCount;
		++sTOCtorCount;
		++sTOMoveCtorCount;
		mId = sTOCtorCount;  // testObject keeps its mId, and we assign ours anew.
		testObject.mX = 0;   // We are swapping our contents with the TestObject, so give it our "previous" value.
		if(mbThrowOnCopy)
		{
			#if EASTL_EXCEPTIONS_ENABLED
				throw "Disallowed TestObject copy";
			#endif
		}
	}

	TestObject& operator=(const TestObject& testObject)
	{
		++sTOCopyAssignCount;

		if(&testObject != this)
		{
			mX = testObject.mX;
			// Leave mId alone.
			mMagicValue = testObject.mMagicValue;
			mbThrowOnCopy = testObject.mbThrowOnCopy;
			if(mbThrowOnCopy)
			{
				#if EASTL_EXCEPTIONS_ENABLED
					throw "Disallowed TestObject copy";
				#endif
			}
		}
		return *this;
	}

	TestObject& operator=(TestObject&& testObject)
	{
		++sTOMoveAssignCount;

		if(&testObject != this)
		{
			eastl::swap(mX, testObject.mX);
			// Leave mId alone.
			eastl::swap(mMagicValue, testObject.mMagicValue);
			eastl::swap(mbThrowOnCopy, testObject.mbThrowOnCopy);

			if(mbThrowOnCopy)
			{
				#if EASTL_EXCEPTIONS_ENABLED
					throw "Disallowed TestObject copy";
				#endif
			}
		}
		return *this;
	}

	~TestObject()
	{
		if(mMagicValue != kMagicValue)
			++sMagicErrorCount;
		mMagicValue = 0;
		--sTOCount;
		++sTODtorCount;
	}

	static void Reset()
	{
		sTOCount            = 0;
		sTOCtorCount        = 0;
		sTODtorCount        = 0;
		sTODefaultCtorCount = 0;
		sTOArgCtorCount     = 0;
		sTOCopyCtorCount    = 0;
		sTOMoveCtorCount    = 0;
		sTOCopyAssignCount  = 0;
		sTOMoveAssignCount  = 0;
		sMagicErrorCount    = 0;
	}

	static bool IsClear() // Returns true if there are no existing TestObjects and the sanity checks related to that test OK.
	{
		return (sTOCount == 0) && (sTODtorCount == sTOCtorCount) && (sMagicErrorCount == 0);
	}
};

// Operators
// We specifically define only == and <, in order to verify that 
// our containers and algorithms are not mistakenly expecting other 
// operators for the contained and manipulated classes.
inline bool operator==(const TestObject& t1, const TestObject& t2)
	{ return t1.mX == t2.mX; }

inline bool operator<(const TestObject& t1, const TestObject& t2)
	{ return t1.mX < t2.mX; }


// TestObject hash
// Normally you don't want to put your hash functions in the eastl namespace, as that namespace is owned by EASTL.
// However, these are the EASTL unit tests and we can say that they are also owned by EASTL.
namespace eastl
{
	template <> 
	struct hash<TestObject>
	{
		size_t operator()(const TestObject& a) const 
			{ return static_cast<size_t>(a.mX); }
	};
}


// use_mX
// Used for printing TestObject contents via the PrintSequence function,
// which is defined below. See the PrintSequence function for documentation.
// This function is an analog of the eastl::use_self and use_first functions.
// We declare this all in one line because the user should never need to 
// debug usage of this function.
template <typename T> struct use_mX { int operator()(const T& t) const { return t.mX; } };



///////////////////////////////////////////////////////////////////////////////
// SizedPOD
//
// Exists for the purpose testing PODs that are larger than built-in types.
//
template <size_t kSize>
struct SizedPOD
{
	char memory[kSize];
};



///////////////////////////////////////////////////////////////////////////////
/// ConstType
///
/// Used to test const type containers (e.g. vector<const ConstType>).
///
class ConstType
{
public:
	ConstType(int value) : mDummy(value) {};
	int mDummy;
};




///////////////////////////////////////////////////////////////////////////////
/// TestObjectHash
///
/// Implements a manually specified hash function for TestObjects.
///
struct TestObjectHash
{
	size_t operator()(const TestObject& t) const 
	{
		return (size_t)t.mX;
	}
};





///////////////////////////////////////////////////////////////////////////////
/// Align16
///

#if defined(EA_PROCESSOR_ARM)
	#define kEASTLTestAlign16 8 //ARM processors can only align to 8 
#else
	#define kEASTLTestAlign16 16
#endif


EA_PREFIX_ALIGN(kEASTLTestAlign16)
struct Align16
{
	explicit Align16(int x = 0) : mX(x) {}
	int mX;
} EA_POSTFIX_ALIGN(kEASTLTestAlign16);

inline bool operator==(const Align16& a, const Align16& b)
	{ return (a.mX == b.mX); }

inline bool operator<(const Align16& a, const Align16& b)
	{ return (a.mX < b.mX); }



///////////////////////////////////////////////////////////////////////////////
/// Align32
///
#if defined(EA_PROCESSOR_ARM)
	#define kEASTLTestAlign32 8 //ARM processors can only align to 8 
#elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) < 400) // GCC 2.x, 3.x
	#define kEASTLTestAlign32 16 // Some versions of GCC fail to support any alignment beyond 16.
#else
	#define kEASTLTestAlign32 32
#endif

EA_PREFIX_ALIGN(kEASTLTestAlign32)
struct Align32
{
	explicit Align32(int x = 0) : mX(x) {}
	int mX;
} EA_POSTFIX_ALIGN(kEASTLTestAlign32);

inline bool operator==(const Align32& a, const Align32& b)
	{ return (a.mX == b.mX); }

inline bool operator<(const Align32& a, const Align32& b)
	{ return (a.mX < b.mX); }



///////////////////////////////////////////////////////////////////////////////
/// Align64
///
/// Used for testing of alignment.
///
#if defined(EA_PROCESSOR_ARM)
	#define kEASTLTestAlign64 8
#elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) < 400) // GCC 2.x, 3.x
	#define kEASTLTestAlign64 16 // Some versions of GCC fail to support any alignment beyond 16.
#else
	#define kEASTLTestAlign64 64
#endif

EA_PREFIX_ALIGN(kEASTLTestAlign64)
struct Align64
{
	explicit Align64(int x = 0) : mX(x) {}
	int mX;
} EA_POSTFIX_ALIGN(kEASTLTestAlign64);

inline bool operator==(const Align64& a, const Align64& b)
	{ return (a.mX == b.mX); }

inline bool operator<(const Align64& a, const Align64& b)
	{ return (a.mX < b.mX); }

namespace eastl
{
	template <>
	struct hash < Align64 >
	{
		size_t operator()(const Align64& a) const
		{
			return static_cast<size_t>(a.mX);
		}
	};
}





/// test_use_self
///
/// Intentionally avoiding a dependency on eastl::use_self.
///
template <typename T>
struct test_use_self
{
	const T& operator()(const T& x) const
		{ return x; }
};



/// GenerateIncrementalIntegers
///
/// Used to seed containers with incremental values based on integers.
///
/// Example usage:
///     vector<int> v(10, 0);
///     generate(v.begin(), v.end(), GenerateIncrementalIntegers<int>());
///     // v will now have 0, 1, 2, ... 8, 9.
/// 
///     generate_n(intArray.begin(), 10, GenerateIncrementalIntegers<int>());
///     // v will now have 0, 1, 2, ... 8, 9.
///
///     vector<TestObject> vTO(10, 0);
///     generate(vTO.begin(), vTO.end(), GenerateIncrementalIntegers<TestObject>());
///     // vTO will now have 0, 1, 2, ... 8, 9.
/// 
template <typename T>
struct GenerateIncrementalIntegers
{
	int mX;

	GenerateIncrementalIntegers(int x = 0)
		: mX(x) { }

	void reset(int x = 0)
		{ mX = x; }

	T operator()()
		{ return T(mX++); } 
};



/// SetIncrementalIntegers
///
/// Used to seed containers with incremental values based on integers.
///
/// Example usage:
///     vector<int> v(10, 0);
///     for_each(v.begin(), v.end(), SetIncrementalIntegers<int>());
///     // v will now have 0, 1, 2, ... 8, 9.
/// 
template <typename T>
struct SetIncrementalIntegers
{
	int mX;

	SetIncrementalIntegers(int x = 0)
		: mX(x) { }

	void reset(int x = 0)
		{ mX = x; }

	void operator()(T& t)
		{ t = T(mX++); } 
};



/// CompareContainers
///
/// Does a comparison between the contents of two containers. 
///
/// Specifically tests for the following properties:
///     empty() is the same for both
///     size() is the same for both
///     iteration through both element by element yields equal values.
///
template <typename T1, typename T2, typename ExtractValue1, typename ExtractValue2>
int CompareContainers(const T1& t1, const T2& t2, const char* ppName, 
					  ExtractValue1 ev1 = test_use_self<T1>(), ExtractValue2 ev2 = test_use_self<T2>())
{
	int nErrorCount = 0;

	// Compare emptiness.
	VERIFY(t1.empty() == t2.empty());

	// Compare sizes.
	const size_t nSize1 = t1.size();
	const size_t nSize2 = t2.size();

	VERIFY(nSize1 == nSize2);
	if(nSize1 != nSize2)
		EASTLTest_Printf("%s: Container size difference: %u, %u\n", ppName, (unsigned)nSize1, (unsigned)nSize2);

	// Compare values.
	if(nSize1 == nSize2)
	{
		// Test iteration
		typename T1::const_iterator it1 = t1.begin();
		typename T2::const_iterator it2 = t2.begin();

		for(unsigned j = 0; it1 != t1.end(); ++it1, ++it2, ++j)
		{
			const typename T1::value_type& v1 = *it1;
			const typename T2::value_type& v2 = *it2;

			VERIFY(ev1(v1) == ev2(v2));
			if(!(ev1(v1) == ev2(v2)))
			{
				EASTLTest_Printf("%s: Container iterator difference at index %d\n", ppName, j);
				break;  
			}
		}

		VERIFY(it1 == t1.end());
		VERIFY(it2 == t2.end());
	}

	return nErrorCount;
}





/// VerifySequence
///
/// Allows the user to specify that a container has a given set of values.
/// 
/// Example usage:
///    vector<int> v;
///    v.push_back(1); v.push_back(3); v.push_back(5);
///    VerifySequence(v.begin(), v.end(), int(), "v.push_back", 1, 3, 5, -1);
///
/// Note: The StackValue template argument is a hint to the compiler about what type 
///       the passed vararg sequence is.
///
template <typename InputIterator, typename StackValue>
bool VerifySequence(InputIterator first, InputIterator last, StackValue /*unused*/, const char* pName, ...)
{
	typedef typename eastl::iterator_traits<InputIterator>::value_type value_type;

	int        argIndex = 0;
	int        seqIndex = 0;
	bool       bReturnValue = true;
	StackValue next;

	va_list args;
	va_start(args, pName);

	for( ; first != last; ++first, ++argIndex, ++seqIndex)
	{
		next = va_arg(args, StackValue);

		if((next == StackValue(-1)) || !(value_type(next) == *first))
		{
			if(pName)
				EASTLTest_Printf("[%s] Mismatch at index %d\n", pName, argIndex);
			else
				EASTLTest_Printf("Mismatch at index %d\n", argIndex);
			bReturnValue = false;
		}
	}

	for(; first != last; ++first)
		++seqIndex;

	if(bReturnValue)
	{
		next = va_arg(args, StackValue);

		if(!(next == StackValue(-1)))
		{
			do {
				++argIndex;
				next = va_arg(args, StackValue);
			} while(!(next == StackValue(-1)));

			if(pName)
				EASTLTest_Printf("[%s] Too many elements: expected %d, found %d\n", pName, argIndex, seqIndex);
			else
				EASTLTest_Printf("Too many elements: expected %d, found %d\n", argIndex, seqIndex);
			bReturnValue = false;
		}
	}

	va_end(args);

	return bReturnValue;
}




/// PrintSequence
///
/// Allows the user to print a sequence of values.
/// 
/// Example usage:
///    vector<int> v;
///    PrintSequence(v.begin(), v.end(), use_self<int>(), 100, "vector", 1, 3, 5, -1);
///
/// Example usage:
///    template <typename T> struct use_mX { int operator()(const T& t) const { return t.mX; } };
///    vector<TestObject> v;
///    PrintSequence(v.begin(), v.end(), use_mX<TestObject>(), 100, "vector", 1, 3, 5, -1);
///    
template <typename InputIterator, typename ExtractInt>
void PrintSequence(InputIterator first, InputIterator last, ExtractInt extractInt, int nMaxCount, const char* pName, ...)
{
	if(pName)
		EASTLTest_Printf("[%s]", pName);

	for(int i = 0; (i < nMaxCount) && (first != last); ++i, ++first)
	{
		EASTLTest_Printf("%d ", (int)extractInt(*first));
	}

	EASTLTest_Printf("\n");
}




/// demoted_iterator
///
/// Converts an iterator into a demoted category. For example, you can convert
/// an iterator of type bidirectional_iterator_tag to forward_iterator_tag.
/// The following is a list of iterator types. A demonted iterator can be demoted
/// only to a lower iterator category (earlier in the following list):
///     input_iterator_tag
///     forward_iterator_tag
///     bidirectional_iterator_tag
///     random_access_iterator_tag
///     contiguous_iterator_tag
///
/// Converts something which can be iterated into a formal input iterator.
/// This class is useful for testing functions and algorithms that expect
/// InputIterators, which are the lowest and 'weakest' form of iterators.
/// 
/// Key traits of InputIterators:
///    Algorithms on input iterators should never attempt to pass
///    through the same iterator twice. They should be single pass 
///    algorithms. value_type T is not required to be an lvalue type.
///
/// Example usage:
///     typedef demoted_iterator<int*, eastl::bidirectional_iterator_tag>         PointerAsBidirectionalIterator;
///     typedef demoted_iterator<MyVector::iterator, eastl::forward_iterator_tag> VectorIteratorAsForwardIterator;
///
/// Example usage:
///     IntVector v;
///     comb_sort(to_forward_iterator(v.begin()), to_forward_iterator(v.end()));
///
template <typename Iterator, typename IteratorCategory>
class demoted_iterator
{
protected:
	Iterator mIterator;

public:
	typedef demoted_iterator<Iterator, IteratorCategory>                 this_type;
	typedef Iterator                                                     iterator_type;
	typedef IteratorCategory                                             iterator_category;
	typedef typename eastl::iterator_traits<Iterator>::value_type        value_type;
	typedef typename eastl::iterator_traits<Iterator>::difference_type   difference_type;
	typedef typename eastl::iterator_traits<Iterator>::reference         reference;
	typedef typename eastl::iterator_traits<Iterator>::pointer           pointer;

	demoted_iterator()
		: mIterator() { }

	explicit demoted_iterator(const Iterator& i)
		: mIterator(i) { }

	demoted_iterator(const this_type& x)
		: mIterator(x.mIterator) { }

	this_type& operator=(const Iterator& i)
		{ mIterator = i; return *this; }

	this_type& operator=(const this_type& x)
		{ mIterator = x.mIterator; return *this; }

	reference operator*() const
		{ return *mIterator; }

	pointer operator->() const
		{ return mIterator; }

	this_type& operator++()
		{ ++mIterator; return *this; }

	this_type operator++(int)
		{ return this_type(mIterator++); }

	this_type& operator--()
		{ --mIterator; return *this; }

	this_type operator--(int)
		{ return this_type(mIterator--); }

	reference operator[](const difference_type& n) const
		{ return mIterator[n]; }

	this_type& operator+=(const difference_type& n)
		{ mIterator += n; return *this; }

	this_type operator+(const difference_type& n) const
		{ return this_type(mIterator + n); }

	this_type& operator-=(const difference_type& n)
		{ mIterator -= n; return *this; }

	this_type operator-(const difference_type& n) const
		{ return this_type(mIterator - n); }

	const iterator_type& base() const
		{ return mIterator; }

}; // class demoted_iterator

template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
inline bool
operator==(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
	{ return a.base() == b.base(); }

template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
inline bool
operator!=(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
	{ return !(a == b); }

template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
inline bool
operator<(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
	{ return a.base() < b.base(); }

template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
inline bool
operator<=(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
	{ return !(b < a); }

template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
inline bool
operator>(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
	{ return b < a; }

template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
inline bool
operator>=(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
	{ return !(a < b); }

template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
inline demoted_iterator<Iterator1, IteratorCategory1>
operator-(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
	{ return demoted_iterator<Iterator1, IteratorCategory1>(a.base() - b.base()); }

template<typename Iterator1, typename IteratorCategory1>
inline demoted_iterator<Iterator1, IteratorCategory1>
operator+(typename demoted_iterator<Iterator1, IteratorCategory1>::difference_type n, const demoted_iterator<Iterator1, IteratorCategory1>& a)
	{ return a + n; }


// to_xxx_iterator
//
// Returns a demoted iterator
//
template <typename Iterator>
inline demoted_iterator<Iterator, EASTL_ITC_NS::input_iterator_tag>
to_input_iterator(const Iterator& i)
	{ return demoted_iterator<Iterator, EASTL_ITC_NS::input_iterator_tag>(i); }

template <typename Iterator>
inline demoted_iterator<Iterator, EASTL_ITC_NS::forward_iterator_tag>
to_forward_iterator(const Iterator& i)
	{ return demoted_iterator<Iterator, EASTL_ITC_NS::forward_iterator_tag>(i); }

template <typename Iterator>
inline demoted_iterator<Iterator, EASTL_ITC_NS::bidirectional_iterator_tag>
to_bidirectional_iterator(const Iterator& i)
	{ return demoted_iterator<Iterator, EASTL_ITC_NS::bidirectional_iterator_tag>(i); }

template <typename Iterator>
inline demoted_iterator<Iterator, EASTL_ITC_NS::random_access_iterator_tag>
to_random_access_iterator(const Iterator& i)
	{ return demoted_iterator<Iterator, EASTL_ITC_NS::random_access_iterator_tag>(i); }






///////////////////////////////////////////////////////////////////////////////
// MallocAllocator
//
// Implements an EASTL allocator that uses malloc/free as opposed to 
// new/delete or PPMalloc Malloc/Free. This is useful for testing 
// allocator behaviour of code.
//
// Example usage:
//      vector<int, MallocAllocator> intVector;
//
class MallocAllocator
{
public:
	MallocAllocator(const char* = EASTL_NAME_VAL("MallocAllocator"))
		: mAllocCount(0), mFreeCount(0), mAllocVolume(0) {}

	MallocAllocator(const MallocAllocator& x)
		: mAllocCount(x.mAllocCount), mFreeCount(x.mFreeCount), mAllocVolume(x.mAllocVolume) {}

	MallocAllocator(const MallocAllocator& x, const char*) : MallocAllocator(x) {}

	MallocAllocator& operator=(const MallocAllocator& x)
	{
		mAllocCount = x.mAllocCount;
		mFreeCount = x.mFreeCount;
		mAllocVolume = x.mAllocVolume;
		return *this;
	}

	void* allocate(size_t n, int = 0);
	void* allocate(size_t n, size_t, size_t, int = 0); // We don't support alignment, so you can't use this class where alignment is required.
	void deallocate(void* p, size_t n);

	const char* get_name() const { return "MallocAllocator"; }
	void set_name(const char*) {}

	static void reset_all()
	{
		mAllocCountAll = 0;
		mFreeCountAll = 0;
		mAllocVolumeAll = 0;
		mpLastAllocation = NULL;
	}

public:
	int mAllocCount;
	int mFreeCount;
	size_t mAllocVolume;

	static int mAllocCountAll;
	static int mFreeCountAll;
	static size_t mAllocVolumeAll;
	static void* mpLastAllocation;
};

inline bool operator==(const MallocAllocator&, const MallocAllocator&) { return true; }
inline bool operator!=(const MallocAllocator&, const MallocAllocator&) { return false; }


///////////////////////////////////////////////////////////////////////////////
// CustomAllocator
//
// Implements an allocator that works just like eastl::allocator but is defined
// within this test as opposed to within EASTL.
//
// Example usage:
//      vector<int, CustomAllocator> intVector;
//
class CustomAllocator
{
public:
	CustomAllocator(const char* = NULL) {}
	CustomAllocator(const CustomAllocator&) {}
	CustomAllocator(const CustomAllocator&, const char*) {}
	CustomAllocator& operator=(const CustomAllocator&) { return *this; }

	void* allocate(size_t n, int flags = 0);
	void* allocate(size_t n, size_t, size_t, int flags = 0);
	void deallocate(void* p, size_t n);

	const char* get_name() const { return "CustomAllocator"; }
	void set_name(const char*) {}
};

inline bool operator==(const CustomAllocator&, const CustomAllocator&) { return true; }
inline bool operator!=(const CustomAllocator&, const CustomAllocator&) { return false; }


///////////////////////////////////////////////////////////////////////////////
/// UnequalAllocator
///
/// Acts the same as eastl::allocator, but always compares as unequal to an 
/// instance of itself.
///
class UnequalAllocator
{
public:
	EASTL_ALLOCATOR_EXPLICIT UnequalAllocator(const char* pName = EASTL_NAME_VAL(EASTL_ALLOCATOR_DEFAULT_NAME))
	    : mAllocator(pName) {}

	UnequalAllocator(const UnequalAllocator& x) : mAllocator(x.mAllocator) {}
	UnequalAllocator(const UnequalAllocator& x, const char* pName) : mAllocator(x.mAllocator) { set_name(pName); }
	UnequalAllocator& operator=(const UnequalAllocator& x)
	{
		mAllocator = x.mAllocator;
		return *this;
	}

	void* allocate(size_t n, int flags = 0) { return mAllocator.allocate(n, flags); }
	void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0) { return mAllocator.allocate(n, alignment, offset, flags); }
	void deallocate(void* p, size_t n) { return mAllocator.deallocate(p, n); }

	const char* get_name() const { return mAllocator.get_name(); }
	void set_name(const char* pName) { mAllocator.set_name(pName); }

protected:
	eastl::allocator mAllocator;
};

inline bool operator==(const UnequalAllocator&, const UnequalAllocator&) { return false; }
inline bool operator!=(const UnequalAllocator&, const UnequalAllocator&) { return true; }


///////////////////////////////////////////////////////////////////////////////
/// CountingAllocator
///
/// Counts allocation events allowing unit tests to validate assumptions.
///
class CountingAllocator : public eastl::allocator
{
public:
	using base_type = eastl::allocator;

	EASTL_ALLOCATOR_EXPLICIT CountingAllocator(const char* pName = EASTL_NAME_VAL(EASTL_ALLOCATOR_DEFAULT_NAME))
	    : base_type(pName)
	{
		totalCtorCount++;
		defaultCtorCount++;
	}

	CountingAllocator(const CountingAllocator& x) : base_type(x)
	{
		totalCtorCount++;
		copyCtorCount++;
	}

	CountingAllocator(const CountingAllocator& x, const char* pName) : base_type(x)
	{
		totalCtorCount++;
		copyCtorCount++;
		set_name(pName);
	}

	CountingAllocator& operator=(const CountingAllocator& x)
	{
		base_type::operator=(x);
		assignOpCount++;
		return *this;
	}

	virtual void* allocate(size_t n, int flags = 0)
	{
		activeAllocCount++;
		totalAllocCount++;
		totalAllocatedMemory += n;
		activeAllocatedMemory += n;
		return base_type::allocate(n, flags);
	}

	virtual void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0)
	{
		activeAllocCount++;
		totalAllocCount++;
		totalAllocatedMemory += n;
		activeAllocatedMemory += n;
		return base_type::allocate(n, alignment, offset, flags);
	}

	void deallocate(void* p, size_t n)
	{
		activeAllocCount--;
		totalDeallocCount--;
		activeAllocatedMemory -= n;
		return base_type::deallocate(p, n);
	}

	const char* get_name() const          { return base_type::get_name(); }
	void set_name(const char* pName)      { base_type::set_name(pName); }

	static auto getTotalAllocationCount()  { return totalAllocCount; }
	static auto getTotalAllocationSize()   { return totalAllocatedMemory; }
	static auto getActiveAllocationSize()  { return activeAllocatedMemory; }
	static auto getActiveAllocationCount() { return activeAllocCount; }
	static auto neverUsed()				   { return totalAllocCount == 0; }

	static void resetCount()
	{
		activeAllocCount      = 0;
		totalAllocCount       = 0;
		totalDeallocCount     = 0;
		totalCtorCount        = 0;
		defaultCtorCount      = 0;
		copyCtorCount         = 0;
		assignOpCount         = 0;
		totalAllocatedMemory  = 0;
		activeAllocatedMemory = 0;
	}

	virtual ~CountingAllocator() = default;

	static uint64_t activeAllocCount;
	static uint64_t totalAllocCount;
	static uint64_t totalDeallocCount;
	static uint64_t totalCtorCount;
	static uint64_t defaultCtorCount;
	static uint64_t copyCtorCount;
	static uint64_t assignOpCount;
	static uint64_t totalAllocatedMemory;  // the total amount of memory allocated
	static uint64_t activeAllocatedMemory; // currently allocated memory by allocator
};

inline bool operator==(const CountingAllocator& rhs, const CountingAllocator& lhs) { return operator==(CountingAllocator::base_type(rhs), CountingAllocator::base_type(lhs)); }
inline bool operator!=(const CountingAllocator& rhs, const CountingAllocator& lhs) { return !(rhs == lhs); }




///////////////////////////////////////////////////////////////////////////////
// InstanceAllocator
//
// Implements an allocator which has a instance id that makes it different
// from other InstanceAllocators of a different id. Allocations between 
// InstanceAllocators of different ids are incompatible. An allocation done 
// by an InstanceAllocator of id=0 cannot be freed by an InstanceAllocator
// of id=1.
//
// Example usage:
//         InstanceAllocator ia0((uint8_t)0);
//         InstanceAllocator ia1((uint8_t)1);
// 
//         eastl::list<int, InstanceAllocator> list0(1, ia0);
//         eastl::list<int, InstanceAllocator> list1(1, ia1);
// 
//         list0 = list1; // list0 cannot free it's current contents with list1's allocator, and InstanceAllocator's purpose is to detect if it mistakenly does so.
//
class InstanceAllocator
{
public:
	enum
	{
		kMultiplier = 16
	}; // Use 16 because it's the highest currently known platform alignment requirement.

	InstanceAllocator(const char* = NULL, uint8_t instanceId = 0) : mInstanceId(instanceId) {}
	InstanceAllocator(uint8_t instanceId) : mInstanceId(instanceId) {}
	InstanceAllocator(const InstanceAllocator& x) : mInstanceId(x.mInstanceId) {}
	InstanceAllocator(const InstanceAllocator& x, const char*) : mInstanceId(x.mInstanceId) {}

	InstanceAllocator& operator=(const InstanceAllocator& x)
	{
		mInstanceId = x.mInstanceId;
		return *this;
	}

	void* allocate(size_t n, int = 0)
	{ // +1 so that we always have space to write mInstanceId.
		uint8_t* p8 =
		    static_cast<uint8_t*>(malloc(n + (kMultiplier * (mInstanceId + 1)))); // We make allocations between
		                                                                          // different instances incompatible by
		                                                                          // tweaking their return values.
		eastl::fill(p8, p8 + kMultiplier, 0xff);
		EA_ANALYSIS_ASSUME(p8 != NULL);
		*p8 = mInstanceId;
		return p8 + (kMultiplier * (mInstanceId + 1));
	}

	void* allocate(size_t n, size_t, size_t, int = 0)
	{ // +1 so that we always have space to write mInstanceId.
		uint8_t* p8 =
		    static_cast<uint8_t*>(malloc(n + (kMultiplier * (mInstanceId + 1)))); // We make allocations between
		                                                                          // different instances incompatible by
		                                                                          // tweaking their return values.
		eastl::fill(p8, p8 + kMultiplier, 0xff);
		EA_ANALYSIS_ASSUME(p8 != NULL);
		*p8 = mInstanceId;
		return p8 + (kMultiplier * (mInstanceId + 1));
	}

	void deallocate(void* p, size_t /*n*/)
	{
		uint8_t* p8 = static_cast<uint8_t*>(p) - (kMultiplier * (mInstanceId + 1));
		EASTL_ASSERT(*p8 == mInstanceId); // mInstanceId must match the id used in allocate(), otherwise the behavior is
		                                  // undefined (probably a heap assert).
		if (*p8 == mInstanceId) // It's possible that *p8 coincidentally matches mInstanceId if p8 is offset into memory
		                        // we don't control.
			free(p8);
		else
			++mMismatchCount;
	}

	const char* get_name()
	{
		sprintf(mName, "InstanceAllocator %u", mInstanceId);
		return mName;
	}

	void set_name(const char*) {}

	static void reset_all() { mMismatchCount = 0; }

public:
	uint8_t mInstanceId;
	char mName[32];

	static int mMismatchCount;
};

inline bool operator==(const InstanceAllocator& a, const InstanceAllocator& b) { return (a.mInstanceId == b.mInstanceId); }
inline bool operator!=(const InstanceAllocator& a, const InstanceAllocator& b) { return (a.mInstanceId != b.mInstanceId); }


///////////////////////////////////////////////////////////////////////////////
// ThrowingAllocator
//
// Implements an EASTL allocator that uses malloc/free as opposed to 
// new/delete or PPMalloc Malloc/Free. This is useful for testing 
// allocator behaviour of code.
//
// Example usage:
//      vector<int, ThrowingAllocator< false<> > intVector;
//
template <bool initialShouldThrow = true>
class ThrowingAllocator
{
public:
	ThrowingAllocator(const char* = EASTL_NAME_VAL("ThrowingAllocator")) : mbShouldThrow(initialShouldThrow) {}
	ThrowingAllocator(const ThrowingAllocator& x) : mbShouldThrow(x.mbShouldThrow) {}
	ThrowingAllocator(const ThrowingAllocator& x, const char*) : mbShouldThrow(x.mbShouldThrow) {}

	ThrowingAllocator& operator=(const ThrowingAllocator& x)
	{
		mbShouldThrow = x.mbShouldThrow;
		return *this;
	}

	void* allocate(size_t n, int = 0)
	{
#if EASTL_EXCEPTIONS_ENABLED
		if (mbShouldThrow)
			throw std::bad_alloc();
#endif
		return malloc(n);
	}

	void* allocate(size_t n, size_t, size_t, int = 0)
	{
#if EASTL_EXCEPTIONS_ENABLED
		if (mbShouldThrow)
			throw std::bad_alloc();
#endif
		return malloc(n); // We don't support alignment, so you can't use this class where alignment is required.
	}

	void deallocate(void* p, size_t) { free(p); }

	const char* get_name() const { return "ThrowingAllocator"; }
	void set_name(const char*) {}

	void set_should_throw(bool shouldThrow) { mbShouldThrow = shouldThrow; }
	bool get_should_throw() const { return mbShouldThrow; }

protected:
	bool mbShouldThrow;
};

template <bool initialShouldThrow>
inline bool operator==(const ThrowingAllocator<initialShouldThrow>&, const ThrowingAllocator<initialShouldThrow>&)
{
	return true;
}

template <bool initialShouldThrow>
inline bool operator!=(const ThrowingAllocator<initialShouldThrow>&, const ThrowingAllocator<initialShouldThrow>&)
{
	return false;
}


///////////////////////////////////////////////////////////////////////////////
// Helper utility that does a case insensitive string comparsion with two sets of overloads
//
struct TestStrCmpI_2
{
	bool operator()(const char* pCStr, const eastl::string& str) const { return str.comparei(pCStr) == 0; }
	bool operator()(const eastl::string& str, const char* pCStr) const { return str.comparei(pCStr) == 0; }
};


///////////////////////////////////////////////////////////////////////////////
// StompDetectAllocator
// 
// An allocator that has sentinal values surrounding its allocator in an 
// effort to detected if its internal memory has been stomped.
//
static uint64_t STOMP_MAGIC_V1 = 0x0101DEC1A551F1ED;
static uint64_t STOMP_MAGIC_V2 = 0x12345C1A551F1ED5;

struct StompDetectAllocator
{
	StompDetectAllocator() { Validate(); }
	~StompDetectAllocator() { Validate(); }

	StompDetectAllocator(const char*) { Validate(); }

	void* allocate(size_t n, int = 0) { return mMallocAllocator.allocate(n); }
	void* allocate(size_t n, size_t, size_t, int = 0) { return mMallocAllocator.allocate(n); }
	void deallocate(void* p, size_t n) { mMallocAllocator.deallocate(p, n); }

	const char* get_name() const { return "FatAllocator"; }
	void set_name(const char*) {}

	void Validate() const
	{
		EASTL_ASSERT(mSentinal1 == STOMP_MAGIC_V1);
		EASTL_ASSERT(mSentinal2 == STOMP_MAGIC_V2);
	}

	uint64_t mSentinal1 = STOMP_MAGIC_V1;
	MallocAllocator mMallocAllocator;
	uint64_t mSentinal2 = STOMP_MAGIC_V2;
};

inline bool operator==(const StompDetectAllocator& a, const StompDetectAllocator& b)
{
	a.Validate();
	b.Validate();

	return (a.mMallocAllocator == b.mMallocAllocator);
}

inline bool operator!=(const StompDetectAllocator& a, const StompDetectAllocator& b)
{
	a.Validate();
	b.Validate();

	return (a.mMallocAllocator != b.mMallocAllocator);
}


// Commonly used free-standing functions to test callables
inline int ReturnVal(int param) { return param; }
inline int ReturnZero() { return 0; }
inline int ReturnOne() { return 1; }


// ValueInit
template<class T>
struct ValueInitOf
{
	ValueInitOf() : mV() {}
	~ValueInitOf() = default;

	ValueInitOf(const ValueInitOf&) = default;
	ValueInitOf(ValueInitOf&&) = default;

	ValueInitOf& operator=(const ValueInitOf&) = default;
	ValueInitOf& operator=(ValueInitOf&&) = default;

	T get() { return mV; }

	T mV;
};

// MoveOnlyType - useful for verifying containers that may hold, e.g., unique_ptrs to make sure move ops are implemented
struct MoveOnlyType
{
	MoveOnlyType() = delete;
	MoveOnlyType(int val) : mVal(val) {}
	MoveOnlyType(const MoveOnlyType&) = delete;
	MoveOnlyType(MoveOnlyType&& x) : mVal(x.mVal) { x.mVal = 0; }
	MoveOnlyType& operator=(const MoveOnlyType&) = delete;
	MoveOnlyType& operator=(MoveOnlyType&& x)
	{
		mVal = x.mVal;
		x.mVal = 0;
		return *this;
	}
	bool operator==(const MoveOnlyType& o) const { return mVal == o.mVal; }

	int mVal;
};

// MoveOnlyTypeDefaultCtor - useful for verifying containers that may hold, e.g., unique_ptrs to make sure move ops are implemented
struct MoveOnlyTypeDefaultCtor
{
	MoveOnlyTypeDefaultCtor() = default;
	MoveOnlyTypeDefaultCtor(int val) : mVal(val) {}
	MoveOnlyTypeDefaultCtor(const MoveOnlyTypeDefaultCtor&) = delete;
	MoveOnlyTypeDefaultCtor(MoveOnlyTypeDefaultCtor&& x) : mVal(x.mVal) { x.mVal = 0; }
	MoveOnlyTypeDefaultCtor& operator=(const MoveOnlyTypeDefaultCtor&) = delete;
	MoveOnlyTypeDefaultCtor& operator=(MoveOnlyTypeDefaultCtor&& x)
	{
		mVal = x.mVal;
		x.mVal = 0;
		return *this;
	}
	bool operator==(const MoveOnlyTypeDefaultCtor& o) const { return mVal == o.mVal; }

	int mVal;
};



//////////////////////////////////////////////////////////////////////////////
// Utility RAII class that sets a new default allocator for the scope
//
struct AutoDefaultAllocator
{
	eastl::allocator* mPrevAllocator = nullptr;

	AutoDefaultAllocator(eastl::allocator* nextAllocator) { mPrevAllocator = SetDefaultAllocator(nextAllocator); }
	~AutoDefaultAllocator()                               { SetDefaultAllocator(mPrevAllocator); }
};


#endif // Header include guard







