aboutsummaryrefslogtreecommitdiff
path: root/test/source/TestSort.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/source/TestSort.cpp')
-rw-r--r--test/source/TestSort.cpp921
1 files changed, 921 insertions, 0 deletions
diff --git a/test/source/TestSort.cpp b/test/source/TestSort.cpp
new file mode 100644
index 0000000..2d0116f
--- /dev/null
+++ b/test/source/TestSort.cpp
@@ -0,0 +1,921 @@
+/////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+
+#include <EABase/eabase.h>
+
+// Some versions of GCC generate an array bounds warning in opt builds which
+// doesn't say what line below it comes from and doesn't appear to be a valid
+// warning. In researching this on the Internet it appears that this is a
+// known problem with GCC.
+#if defined(EA_DISABLE_GCC_WARNING)
+ EA_DISABLE_GCC_WARNING(-Warray-bounds)
+#endif
+
+#include "EASTLTest.h"
+#include <EASTL/sort.h>
+#include <EASTL/bonus/sort_extra.h>
+#include <EASTL/vector.h>
+#include <EASTL/list.h>
+#include <EASTL/deque.h>
+#include <EASTL/algorithm.h>
+#include <EASTL/allocator.h>
+#include <EASTL/numeric.h>
+#include <EASTL/random.h>
+#include <EABase/eahave.h>
+#include <cmath>
+
+
+namespace eastl
+{
+ namespace Internal
+ {
+ typedef eastl::vector<int> IntArray;
+ typedef eastl::vector<IntArray> IntArrayArray;
+
+
+ // IntArrayCompare
+ // Used to compare IntArray objects.
+ struct IntArrayCompare
+ {
+ bool operator()(const IntArray& a, const IntArray& b)
+ { return a.front() < b.front(); }
+ };
+
+
+ // SafeFloatCompare
+ //
+ // Float comparison has a problem for the case that either of the floats are a NaN.
+ // If you use a NaN in a sort function that uses default floating point comparison then
+ // you will get undefined behavior, as all NaNs compare false. This compare function
+ // class sorts floats such that all negative NaNs sort lower than all integers, and all
+ // positive NaNs sort higher than all integers.
+ //
+ // Example usage:
+ // eastl::sort(floatArray.begin(), floatArray.end(), SafeFloatCompare());
+ //
+ struct SafeFloatCompare
+ {
+ union FloatInt32{ float f; int32_t i; };
+
+ bool operator()(float a, float b) const
+ {
+ #if defined(EA_HAVE_ISNAN)
+ bool aNan = (EA_HAVE_ISNAN(a) != 0);
+ bool bNan = (EA_HAVE_ISNAN(b) != 0);
+ #else
+ bool aNan = (a != a); // This works as long as the compiler doesn't do any tricks to optimize it away.
+ bool bNan = (b != b);
+ #endif
+
+ if(!aNan && !bNan)
+ return (a < b);
+
+ FloatInt32 fia = { a };
+ FloatInt32 fib = { b };
+
+ if(aNan)
+ {
+ if(bNan)
+ return (fia.i < fib.i); // Both are NaNs, so do a binary compare.
+ else
+ return (fia.i < 0); // All negative NaNs are assumed to be less than all non-NaNs.
+ }
+ else
+ return (0 < fib.i); // All negative NaNs are assumed to be less than all non-NaNs.
+ }
+ };
+
+
+
+ // StatefulCompare
+ // Used to verify that sort<int, StatefulCompare&>() respects the
+ // fact that StatefulCompare is passed by reference instead of by value.
+ // All existing commercial STL implementations fail to do what the user
+ // wants and instead pass around the compare object by value, even if
+ // the user specifically asks to use it by reference. EASTL doesn't
+ // have this problem.
+ struct StatefulCompare
+ {
+ static int nCtorCount;
+ static int nDtorCount;
+ static int nCopyCount;
+
+ StatefulCompare()
+ { nCtorCount++; }
+
+ StatefulCompare(StatefulCompare&)
+ { nCopyCount++; }
+
+ ~StatefulCompare()
+ { nDtorCount++; }
+
+ StatefulCompare& operator=(const StatefulCompare&)
+ { nCopyCount++; return *this; }
+
+ bool operator()(int a, int b)
+ { return a < b; }
+
+ static void Reset()
+ { nCtorCount = 0; nDtorCount = 0; nCopyCount = 0; }
+ };
+
+ int StatefulCompare::nCtorCount = 0;
+ int StatefulCompare::nDtorCount = 0;
+ int StatefulCompare::nCopyCount = 0;
+
+
+ // TestObjectPtrCompare
+ // Used to compare sorted objects by pointer instead of value.
+ struct TestObjectPtrCompare
+ {
+ bool operator()(TestObject* a, TestObject* b)
+ { return a->mX < b->mX; }
+ };
+
+
+ // TestObjectIndexCompare
+ // Used to compare sorted objects by array index instead of value.
+ struct TestObjectIndexCompare
+ {
+ vector<TestObject>* mpArray;
+
+ TestObjectIndexCompare(vector<TestObject>* pArray) : mpArray(pArray) { }
+ TestObjectIndexCompare(const TestObjectIndexCompare& x) : mpArray(x.mpArray){ }
+ TestObjectIndexCompare& operator=(const TestObjectIndexCompare& x) { mpArray = x.mpArray; return *this; }
+
+ bool operator()(eastl_size_t a, eastl_size_t b)
+ { return (*mpArray)[a] < (*mpArray)[b]; }
+ };
+
+ // Radix sort elements
+ template <typename Key>
+ struct RadixSortElement
+ {
+ typedef Key radix_type;
+ Key mKey;
+ uint16_t mData;
+
+ bool operator<(const RadixSortElement<Key> &other) const
+ {
+ return mKey < other.mKey;
+ }
+ };
+
+ typedef RadixSortElement<uint8_t> RadixSortElement8;
+ typedef RadixSortElement<uint16_t> RadixSortElement16;
+ typedef RadixSortElement<uint32_t> RadixSortElement32;
+
+ template <typename integer_type>
+ struct identity_extract_radix_key
+ {
+ typedef integer_type radix_type;
+
+ const radix_type operator()(const integer_type& x) const
+ {
+ return x;
+ }
+ };
+ } // namespace Internal
+
+} // namespace eastl
+
+int TestSort()
+{
+ using namespace eastl;
+ using namespace Internal;
+
+ int nErrorCount = 0;
+
+ EASTLTest_Rand rng(EA::UnitTest::GetRandSeed());
+
+ {
+ // is_sorted
+ int array[] = { 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2, 1, 0 };
+
+ EATEST_VERIFY( is_sorted(array + 0, array + 0));
+ EATEST_VERIFY( is_sorted(array + 2, array + 4));
+ EATEST_VERIFY( is_sorted(array + 0, array + 10));
+ EATEST_VERIFY(!is_sorted(array + 0, array + 14));
+ EATEST_VERIFY( is_sorted(array + 11, array + 23, eastl::greater<int>()));
+ }
+
+ {
+ // is_sorted_until
+ int sorted[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ int notsorted[] = { 0, 1, 2, 3, 4, 42, 6, 7, 8, 9 };
+
+ EATEST_VERIFY( is_sorted_until(sorted + EAArrayCount(sorted), sorted + EAArrayCount(sorted)) == sorted + EAArrayCount(sorted) );
+ EATEST_VERIFY( is_sorted_until(sorted , sorted + EAArrayCount(sorted)) == sorted + EAArrayCount(sorted) );
+
+ EATEST_VERIFY( is_sorted_until(sorted + 0, sorted + 0) == sorted );
+ EATEST_VERIFY( is_sorted_until(sorted + 2, sorted + 8) == sorted + 8 );
+
+ EATEST_VERIFY( is_sorted_until(notsorted + 2, notsorted + 8) == notsorted + 6 );
+
+ // is_sorted_until (with compare function)
+ EATEST_VERIFY( is_sorted_until(sorted + EAArrayCount(sorted), sorted + EAArrayCount(sorted), eastl::less<int>()) == sorted + EAArrayCount(sorted) );
+ EATEST_VERIFY( is_sorted_until(notsorted + 2, notsorted + 8, eastl::less<int>()) == notsorted + 6 );
+ }
+
+ // Sort arrays of size 0 - N. Sort M random permutations of each.
+ {
+ vector<int64_t> intArray, intArraySaved;
+
+ for(int i = 0; i < (150 + (gEASTL_TestLevel * 200)); i += (i < 5) ? 1 : 37) // array sizes of 0 to 300 - 2100, depending on test level.
+ {
+ // intArraySaved.clear(); // Do we want to do this?
+
+ for(int n = 0; n < i; n++)
+ {
+ intArraySaved.push_back(n);
+
+ if(rng.RandLimit(10) == 0)
+ {
+ intArraySaved.push_back(n);
+
+ if(rng.RandLimit(5) == 0)
+ intArraySaved.push_back(n);
+ }
+ }
+ const int64_t expectedSum = eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0));
+
+ for(int j = 0; j < 300 + (gEASTL_TestLevel * 50); j++)
+ {
+ eastl::random_shuffle(intArraySaved.begin(), intArraySaved.end(), rng);
+
+ intArray = intArraySaved;
+ bubble_sort(intArray.begin(), intArray.end());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ shaker_sort(intArray.begin(), intArray.end());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ insertion_sort(intArray.begin(), intArray.end());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ selection_sort(intArray.begin(), intArray.end());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ shell_sort(intArray.begin(), intArray.end());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ comb_sort(intArray.begin(), intArray.end());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ heap_sort(intArray.begin(), intArray.end());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ merge_sort(intArray.begin(), intArray.end(), *get_default_allocator((EASTLAllocatorType*)NULL));
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ vector<int64_t> buffer(intArray.size());
+ merge_sort_buffer(intArray.begin(), intArray.end(), buffer.data());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ quick_sort(intArray.begin(), intArray.end());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+
+ intArray = intArraySaved;
+ buffer.resize(intArray.size()/2);
+ tim_sort_buffer(intArray.begin(), intArray.end(), buffer.data());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ EATEST_VERIFY(eastl::accumulate(begin(intArraySaved), end(intArraySaved), int64_t(0)) == expectedSum);
+ }
+ }
+ }
+
+ // Test tim sort with a specific array size and seed that caused a crash
+ {
+ vector<int64_t> intArray;
+ int i = 1000000;
+ {
+ EASTLTest_Rand testRng(232526);
+
+ for (int n = 0; n < i; n++)
+ {
+ intArray.push_back(testRng.Rand());
+ }
+ vector<int64_t> buffer(intArray.size() / 2);
+ tim_sort_buffer(intArray.begin(), intArray.end(), buffer.data());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ }
+ }
+
+ // Test insertion_sort() does not invalidate a BidirectionalIterator by doing --BidirectionalIterator.begin()
+ {
+ // Test Passes if the Test doesn't crash
+ eastl::deque<int> deque;
+ deque.push_back(1);
+
+ insertion_sort(deque.begin(), deque.end());
+
+ insertion_sort(deque.begin(), deque.end(), eastl::less<int>{});
+ }
+
+
+ // TestObject sorting
+ TestObject::Reset();
+ {
+ vector<TestObject> toArray, toArraySaved;
+
+ for(int i = 0; i < (150 + (gEASTL_TestLevel * 200)); i += (i < 5) ? 1 : 37) // array sizes of 0 to 300 - 2100, depending on test level.
+ {
+ for(int n = 0; n < i; n++)
+ {
+ toArraySaved.push_back(TestObject(n));
+
+ if(rng.RandLimit(10) == 0)
+ {
+ toArraySaved.push_back(TestObject(n));
+
+ if(rng.RandLimit(5) == 0)
+ toArraySaved.push_back(TestObject(n));
+ }
+ }
+
+ for(int j = 0; j < 300 + (gEASTL_TestLevel * 50); j++)
+ {
+ eastl::random_shuffle(toArraySaved.begin(), toArraySaved.end(), rng);
+
+ toArray = toArraySaved;
+ bubble_sort(toArray.begin(), toArray.end());
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+
+ toArray = toArraySaved;
+ shaker_sort(toArray.begin(), toArray.end());
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+
+ toArray = toArraySaved;
+ insertion_sort(toArray.begin(), toArray.end());
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+
+ toArray = toArraySaved;
+ selection_sort(toArray.begin(), toArray.end());
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+
+ toArray = toArraySaved;
+ shell_sort(toArray.begin(), toArray.end());
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+
+ toArray = toArraySaved;
+ comb_sort(toArray.begin(), toArray.end());
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+
+ toArray = toArraySaved;
+ heap_sort(toArray.begin(), toArray.end());
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+
+ // Not ready yet:
+ toArray = toArraySaved;
+ merge_sort(toArray.begin(), toArray.end(), *get_default_allocator((EASTLAllocatorType*)NULL));
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+
+ toArray = toArraySaved;
+ quick_sort(toArray.begin(), toArray.end());
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+
+ toArray = toArraySaved;
+ vector<TestObject> buffer(toArray.size()/2);
+ tim_sort_buffer(toArray.begin(), toArray.end(), buffer.data());
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end()));
+ }
+ }
+ }
+
+ // Test that stable sorting algorithms are actually stable
+ {
+ struct StableSortTestObj
+ {
+ StableSortTestObj()
+ {
+ }
+
+ StableSortTestObj(int value)
+ :value(value)
+ ,initialPositionIndex(0)
+ {
+ }
+
+ int value;
+ size_t initialPositionIndex;
+ };
+
+ // During the test this comparison is used to sort elements based on value.
+ struct StableSortCompare
+ {
+ bool operator()(const StableSortTestObj& a, const StableSortTestObj& b)
+ {
+ return a.value < b.value;
+ }
+ };
+
+ // During the test this comparison is used to verify the sort was a stable sort. i.e. if values are the same then
+ // their relative position should be maintained.
+ struct StableSortCompareForStability
+ {
+ bool operator()(const StableSortTestObj& a, const StableSortTestObj& b)
+ {
+ if (a.value != b.value)
+ {
+ return a.value < b.value;
+ }
+ else
+ {
+ return a.initialPositionIndex < b.initialPositionIndex;
+ }
+ }
+ };
+
+ vector<StableSortTestObj> toArray, toArraySaved;
+ StableSortCompare compare;
+ StableSortCompareForStability compareForStability;
+
+ for (int i = 0; i < (150 + (gEASTL_TestLevel * 200)); i += (i < 5) ? 1 : 37) // array sizes of 0 to 300 - 2100, depending on test level.
+ {
+ for (int n = 0; n < i; n++)
+ {
+ toArraySaved.push_back(StableSortTestObj(n));
+
+ if (rng.RandLimit(10) == 0)
+ {
+ toArraySaved.push_back(StableSortTestObj(n));
+
+ if (rng.RandLimit(5) == 0)
+ toArraySaved.push_back(StableSortTestObj(n));
+ }
+ }
+ vector<StableSortTestObj> tempBuffer;
+ tempBuffer.resize(toArraySaved.size());
+
+ for (int j = 0; j < 300 + (gEASTL_TestLevel * 50); j++)
+ {
+ eastl::random_shuffle(toArraySaved.begin(), toArraySaved.end(), rng);
+ // Store the intial position of each element in the array before sorting. This position can then be used to verify that the sorting operation is stable.
+ for (vector<StableSortTestObj>::size_type k = 0; k < toArraySaved.size(); k++)
+ {
+ toArraySaved[k].initialPositionIndex = k;
+ }
+
+ toArray = toArraySaved;
+ bubble_sort(toArray.begin(), toArray.end(), compare);
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end(), compareForStability));
+
+ toArray = toArraySaved;
+ shaker_sort(toArray.begin(), toArray.end(), compare);
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end(), compareForStability));
+
+ toArray = toArraySaved;
+ insertion_sort(toArray.begin(), toArray.end(), compare);
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end(), compareForStability));
+
+ toArray = toArraySaved;
+ tim_sort_buffer(toArray.begin(), toArray.end(), tempBuffer.data(), compare);
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end(), compareForStability));
+
+ toArray = toArraySaved;
+ merge_sort(toArray.begin(), toArray.end(), *get_default_allocator((EASTLAllocatorType*)NULL), compare);
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end(), compareForStability));
+
+ toArray = toArraySaved;
+ merge_sort_buffer(toArray.begin(), toArray.end(), tempBuffer.data(), compare);
+ EATEST_VERIFY(is_sorted(toArray.begin(), toArray.end(), compareForStability));
+ }
+ }
+ }
+
+ {
+ // OutputIterator merge(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result)
+ // This is tested by merge_sort.
+ }
+
+
+ {
+ // void partial_sort(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last)
+ // This is tested by quick_sort.
+ }
+
+
+ {
+ // void nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last)
+ // void nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last, Compare compare)
+ const int intArrayInit[16] = { 4, 2, 8, 6, 9, 1, 1, 4, 0, 5, 5, 7, 8, 9, 3, 3 };
+ int intArraySorted[16]; // Same as intArrayInit but sorted
+ int intArray[16];
+ size_t i, j;
+
+ // We test many combinations of nth_element on the int array.
+ for(i = 1; i < 16; i++)
+ {
+ for(j = 0; j < i; j++)
+ {
+ eastl::copy(intArrayInit, intArrayInit + i, intArraySorted);
+ eastl::sort(intArraySorted, intArraySorted + i);
+
+ eastl::copy(intArrayInit, intArrayInit + i, intArray);
+ nth_element(intArray, intArray + j, intArray + i);
+ EATEST_VERIFY(intArray[j] == intArraySorted[j]);
+ }
+ }
+
+ for(i = 1; i < 16; i++)
+ {
+ for(j = 0; j < i; j++)
+ {
+ eastl::copy(intArrayInit, intArrayInit + i, intArraySorted);
+ eastl::sort(intArraySorted, intArraySorted + i);
+
+ eastl::copy(intArrayInit, intArrayInit + 16, intArray);
+ nth_element(intArray, intArray + j, intArray + i, eastl::less<int>());
+ EATEST_VERIFY(intArray[j] == intArraySorted[j]);
+ }
+ }
+ }
+
+
+ {
+ // void radix_sort(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator buffer);
+ const uint32_t kCount = 100;
+
+ {
+ RadixSortElement32* pRadixSortElementArray32 = new RadixSortElement32[kCount];
+ RadixSortElement32* pRadixSortElementArrayTemp32 = new RadixSortElement32[kCount];
+ for(uint32_t i = 0; i < kCount; i++)
+ {
+ pRadixSortElementArray32[i].mKey = (uint16_t)(kCount - i);
+ pRadixSortElementArray32[i].mData = (uint16_t)i;
+ }
+ radix_sort<RadixSortElement32*, extract_radix_key<RadixSortElement32> >(pRadixSortElementArray32, pRadixSortElementArray32 + kCount, pRadixSortElementArrayTemp32);
+ EATEST_VERIFY(is_sorted(pRadixSortElementArray32, pRadixSortElementArray32 + kCount));
+ delete[] pRadixSortElementArray32;
+ delete[] pRadixSortElementArrayTemp32;
+ }
+
+ {
+ RadixSortElement16* pRadixSortElementArray16 = new RadixSortElement16[kCount];
+ RadixSortElement16* pRadixSortElementArrayTemp16 = new RadixSortElement16[kCount];
+ for(uint32_t i = 0; i < kCount; i++)
+ {
+ pRadixSortElementArray16[i].mKey = (uint16_t)(kCount - i);
+ pRadixSortElementArray16[i].mData = (uint16_t)i;
+ }
+ radix_sort<RadixSortElement16*, extract_radix_key<RadixSortElement16> >(pRadixSortElementArray16, pRadixSortElementArray16 + kCount, pRadixSortElementArrayTemp16);
+ EATEST_VERIFY(is_sorted(pRadixSortElementArray16, pRadixSortElementArray16 + kCount));
+ delete[] pRadixSortElementArray16;
+ delete[] pRadixSortElementArrayTemp16;
+ }
+
+ {
+ RadixSortElement8* pRadixSortElementArray8 = new RadixSortElement8[kCount];
+ RadixSortElement8* pRadixSortElementArrayTemp8 = new RadixSortElement8[kCount];
+ for(uint32_t i = 0; i < kCount; i++)
+ {
+ pRadixSortElementArray8[i].mKey = (uint8_t)(kCount - i);
+ pRadixSortElementArray8[i].mData = (uint8_t)i;
+ }
+ radix_sort<RadixSortElement8*, extract_radix_key<RadixSortElement8> >(pRadixSortElementArray8, pRadixSortElementArray8 + kCount, pRadixSortElementArrayTemp8);
+ EATEST_VERIFY(is_sorted(pRadixSortElementArray8, pRadixSortElementArray8 + kCount));
+ delete[] pRadixSortElementArray8;
+ delete[] pRadixSortElementArrayTemp8;
+ }
+ }
+
+ {
+ // Do some white-box testing of radix sort to verify internal optimizations work properly for some edge cases.
+
+ {
+ uint32_t input[] = { 123, 15, 76, 2, 74, 12, 62, 91 };
+ uint32_t buffer[EAArrayCount(input)];
+ radix_sort<uint32_t*, identity_extract_radix_key<uint32_t>>(begin(input), end(input), buffer);
+ EATEST_VERIFY(is_sorted(begin(input), end(input)));
+ }
+ {
+ // Test values where some digit positions have identical values
+ uint32_t input[] = { 0x75000017, 0x74000003, 0x73000045, 0x76000024, 0x78000033, 0x76000099, 0x78000043, 0x75000010 };
+ uint32_t buffer[EAArrayCount(input)];
+ radix_sort<uint32_t*, identity_extract_radix_key<uint32_t>>(begin(input), end(input), buffer);
+ EATEST_VERIFY(is_sorted(begin(input), end(input)));
+ }
+ {
+ // Test values where some digit positions have identical values
+ uint32_t input[] = { 0x00750017, 0x00740003, 0x00730045, 0x00760024, 0x00780033, 0x00760099, 0x00780043, 0x00750010 };
+ uint32_t buffer[EAArrayCount(input)];
+ radix_sort<uint32_t*, identity_extract_radix_key<uint32_t>>(begin(input), end(input), buffer);
+ EATEST_VERIFY(is_sorted(begin(input), end(input)));
+ }
+ {
+ // Test values where an odd number of scatter operations will be done during sorting (which forces a copy operation to move values back to the input buffer).
+ uint32_t input[] = { 0x00000017, 0x00000003, 0x00000045, 0x00000024, 0x00000033, 0x00000099, 0x00000043, 0x00000010 };
+ uint32_t buffer[EAArrayCount(input)];
+ radix_sort<uint32_t*, identity_extract_radix_key<uint32_t>>(begin(input), end(input), buffer);
+ EATEST_VERIFY(is_sorted(begin(input), end(input)));
+ }
+ }
+
+ {
+ // Test different values for DigitBits
+
+ {
+ uint32_t input[] = {2514513, 6278225, 2726217, 963245656, 35667326, 2625624562, 3562562562, 1556256252};
+ uint32_t buffer[EAArrayCount(input)];
+ radix_sort<uint32_t*, identity_extract_radix_key<uint32_t>, 1>(begin(input), end(input), buffer);
+ EATEST_VERIFY(is_sorted(begin(input), end(input)));
+ }
+ {
+ uint32_t input[] = { 2514513, 6278225, 2726217, 963245656, 35667326, 2625624562, 3562562562, 1556256252 };
+ uint32_t buffer[EAArrayCount(input)];
+ radix_sort<uint32_t*, identity_extract_radix_key<uint32_t>, 3>(begin(input), end(input), buffer);
+ EATEST_VERIFY(is_sorted(begin(input), end(input)));
+ }
+ {
+ uint32_t input[] = { 2514513, 6278225, 2726217, 963245656, 35667326, 2625624562, 3562562562, 1556256252 };
+ uint32_t buffer[EAArrayCount(input)];
+ radix_sort<uint32_t*, identity_extract_radix_key<uint32_t>, 6>(begin(input), end(input), buffer);
+ EATEST_VERIFY(is_sorted(begin(input), end(input)));
+ }
+ {
+ // Test a value for DigitBits that is more than half the size of the type.
+ uint16_t input[] = { 14513, 58225, 26217, 34656, 63326, 24562, 35562, 15652 };
+ uint16_t buffer[EAArrayCount(input)];
+ radix_sort<uint16_t*, identity_extract_radix_key<uint16_t>, 11>(begin(input), end(input), buffer);
+ EATEST_VERIFY(is_sorted(begin(input), end(input)));
+ }
+ {
+ // Test a value for DigitBits that is the size of the type itself.
+ uint8_t input[] = { 113, 225, 217, 56, 26, 162, 62, 152 };
+ uint8_t buffer[EAArrayCount(input)];
+ radix_sort<uint8_t*, identity_extract_radix_key<uint8_t>, 8>(begin(input), end(input), buffer);
+ EATEST_VERIFY(is_sorted(begin(input), end(input)));
+ }
+
+ }
+
+ {
+ // void bucket_sort(ForwardIterator first, ForwardIterator last, ContainerArray& bucketArray, HashFunction hash)
+
+ const size_t kElementRange = 32;
+ vector<int> intArray(1000);
+
+ for(int i = 0; i < 1000; i++)
+ intArray[i] = rng() % kElementRange;
+
+ vector< vector<int> > bucketArray(kElementRange);
+ bucket_sort(intArray.begin(), intArray.end(), bucketArray, eastl::hash_use_self<int>());
+ EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end()));
+ }
+
+
+ {
+ // stable_sort general test
+ typedef eastl::less<int> IntCompare;
+
+ int intArray[2] = { 0, 1 };
+
+ stable_sort(intArray, intArray + 2);
+ stable_sort(intArray, intArray + 2, IntCompare());
+ stable_sort<int*>(intArray, intArray + 2);
+ stable_sort<int*, IntCompare>(intArray, intArray + 2, IntCompare());
+
+ MallocAllocator mallocAllocator;
+
+ //stable_sort(intArray, intArray + 2, mallocAllocator);
+ stable_sort(intArray, intArray + 2, mallocAllocator, IntCompare());
+ //stable_sort<int*, MallocAllocator>(intArray, intArray + 2, mallocAllocator);
+ stable_sort<int*, MallocAllocator, IntCompare>(intArray, intArray + 2, mallocAllocator, IntCompare());
+ }
+
+ {
+ // stable_sort special test
+ IntArrayArray intArrayArray(2);
+ IntArrayCompare compare;
+
+ intArrayArray[0].push_back(0);
+ intArrayArray[1].push_back(1);
+
+ stable_sort(intArrayArray.begin(), intArrayArray.end(), compare);
+ }
+
+
+ {
+ // Test to verify that Compare object references are preserved.
+ typedef deque<int> IntDeque;
+ typedef IntDeque::iterator IntDequeIterator;
+
+ IntDeque intDeque, intDequeSaved;
+ StatefulCompare compare;
+
+ // Set up intDequeSaved with random data.
+ for(int n = 0; n < 500; n++)
+ {
+ intDequeSaved.push_back(n);
+
+ if(rng.RandLimit(10) == 0)
+ {
+ intDequeSaved.push_back(n);
+
+ if(rng.RandLimit(5) == 0)
+ intDequeSaved.push_back(n);
+ }
+ }
+
+ eastl::random_shuffle(intDequeSaved.begin(), intDequeSaved.end(), rng);
+
+ StatefulCompare::Reset();
+ intDeque = intDequeSaved;
+ bubble_sort<IntDequeIterator, StatefulCompare&>(intDeque.begin(), intDeque.end(), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+
+ StatefulCompare::Reset();
+ intDeque = intDequeSaved;
+ shaker_sort<IntDequeIterator, StatefulCompare&>(intDeque.begin(), intDeque.end(), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+
+ StatefulCompare::Reset();
+ intDeque = intDequeSaved;
+ insertion_sort<IntDequeIterator, StatefulCompare&>(intDeque.begin(), intDeque.end(), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+
+ StatefulCompare::Reset();
+ intDeque = intDequeSaved;
+ selection_sort<IntDequeIterator, StatefulCompare&>(intDeque.begin(), intDeque.end(), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+
+ StatefulCompare::Reset();
+ intDeque = intDequeSaved;
+ shell_sort<IntDequeIterator, StatefulCompare&>(intDeque.begin(), intDeque.end(), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+
+ StatefulCompare::Reset();
+ intDeque = intDequeSaved;
+ comb_sort<IntDequeIterator, StatefulCompare&>(intDeque.begin(), intDeque.end(), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+
+ StatefulCompare::Reset();
+ intDeque = intDequeSaved;
+ heap_sort<IntDequeIterator, StatefulCompare&>(intDeque.begin(), intDeque.end(), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+
+ StatefulCompare::Reset();
+ intDeque = intDequeSaved;
+ merge_sort<IntDequeIterator, EASTLAllocatorType, StatefulCompare&>(intDeque.begin(), intDeque.end(), *get_default_allocator((EASTLAllocatorType*)NULL), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+
+ StatefulCompare::Reset();
+ intDeque = intDequeSaved;
+ quick_sort<IntDequeIterator, StatefulCompare&>(intDeque.begin(), intDeque.end(), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+
+ StatefulCompare::Reset();
+ vector<int> buffer(intDeque.size()/2);
+ intDeque = intDequeSaved;
+ tim_sort_buffer<IntDequeIterator, int, StatefulCompare&>(intDeque.begin(), intDeque.end(), buffer.data(), compare);
+ EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0));
+ }
+
+ {
+ // EATEST_VERIFY deque sorting can compile.
+ deque<int> intDeque;
+ vector<int> intVector;
+
+ stable_sort(intDeque.begin(), intDeque.end());
+ stable_sort(intVector.begin(), intVector.end());
+ }
+
+
+ {
+ // Test sorting of a container of pointers to objects as opposed to a container of objects themselves.
+ vector<TestObject> toArray;
+ vector<TestObject*> topArray;
+
+ for(eastl_size_t i = 0; i < 32; i++)
+ toArray.push_back(TestObject((int)rng.RandLimit(20)));
+ for(eastl_size_t i = 0; i < 32; i++) // This needs to be a second loop because the addresses might change in the first loop due to container resizing.
+ topArray.push_back(&toArray[i]);
+
+ quick_sort(topArray.begin(), topArray.end(), TestObjectPtrCompare());
+ EATEST_VERIFY(is_sorted(topArray.begin(), topArray.end(), TestObjectPtrCompare()));
+ }
+
+
+ {
+ // Test sorting of a container of array indexes to objects as opposed to a container of objects themselves.
+
+ vector<TestObject> toArray;
+ vector<eastl_size_t> toiArray;
+
+ for(eastl_size_t i = 0; i < 32; i++)
+ {
+ toArray.push_back(TestObject((int)rng.RandLimit(20)));
+ toiArray.push_back(i);
+ }
+
+ quick_sort(toiArray.begin(), toiArray.end(), TestObjectIndexCompare(&toArray));
+ EATEST_VERIFY(is_sorted(toiArray.begin(), toiArray.end(), TestObjectIndexCompare(&toArray)));
+ }
+
+
+ {
+ // Test of special floating point sort in the presence of NaNs.
+ vector<float> floatArray;
+ union FloatInt32{ float f; int32_t i; } fi;
+
+ for(int i = 0; i < 1000; i++)
+ {
+ fi.i = (int32_t)rng.Rand();
+ floatArray.push_back(fi.f);
+ }
+
+ // Without SafeFloatCompare, the following quick_sort will crash, hang, or generate inconsistent results.
+ quick_sort(floatArray.begin(), floatArray.end(), SafeFloatCompare());
+ EATEST_VERIFY(is_sorted(floatArray.begin(), floatArray.end(), SafeFloatCompare()));
+ }
+
+ {
+ auto test_stable_sort = [&](auto testArray, size_t count)
+ {
+ auto isEven = [](auto val) { return (val % 2) == 0; };
+ auto isOdd = [](auto val) { return (val % 2) != 0; };
+
+ for (size_t i = 0; i < count; i++)
+ testArray.push_back((uint16_t)rng.Rand());
+
+ vector<uint16_t> evenArray;
+ vector<uint16_t> oddArray;
+
+ eastl::copy_if(testArray.begin(), testArray.end(), eastl::back_inserter(evenArray), isEven);
+ eastl::copy_if(testArray.begin(), testArray.end(), eastl::back_inserter(oddArray), isOdd);
+
+ const auto boundary = eastl::stable_partition(testArray.begin(), testArray.end(), isEven);
+
+ const auto evenCount = eastl::distance(testArray.begin(), boundary);
+ const auto oddCount = eastl::distance(boundary, testArray.end());
+
+ const auto evenExpectedCount = (ptrdiff_t)evenArray.size();
+ const auto oddExpectedCount = (ptrdiff_t)oddArray.size();
+
+ EATEST_VERIFY(evenCount == evenExpectedCount);
+ EATEST_VERIFY(oddCount == oddExpectedCount);
+ EATEST_VERIFY(eastl::equal(testArray.begin(), boundary, evenArray.begin()));
+ EATEST_VERIFY(eastl::equal(boundary, testArray.end(), oddArray.begin()));
+ };
+
+ test_stable_sort(vector<uint16_t>(), 1000); // Test stable_partition
+ test_stable_sort(vector<uint16_t>(), 0); // Test stable_partition on empty container
+ test_stable_sort(vector<uint16_t>(), 1); // Test stable_partition on container of one element
+ test_stable_sort(vector<uint16_t>(), 2); // Test stable_partition on container of two element
+ test_stable_sort(list<uint16_t>(), 0); // Test stable_partition on bidirectional iterator (not random access)
+ }
+
+ #if 0 // Disabled because it takes a long time and thus far seems to show no bug in quick_sort.
+ {
+ // Regression of Coverity report for Madden 2014 that quick_sort is reading beyond an array bounds within insertion_sort_simple.
+ // The Madden code was sorting the 11 players on the field for a team by some criteria. We write
+ vector<int> intArray(11);
+ for(eastl_size_t i = 0; i < intArray.size(); i++)
+ intArray[i] = i;
+
+ do {
+ vector<int> intArrayCopy(intArray);
+
+ // We need to verify that intArray[12] is never accessed. We could do that with a stomp allocator,
+ // which we don't currently have set up for the EASTL unit tests, or we could put a breakpoint in
+ // the debugger. Until we get a stomp allocator installed, do the breakpoint solution.
+ quick_sort(intArrayCopy.begin(), intArrayCopy.end());
+ } while(next_permutation(intArray.begin(), intArray.end()));
+ }
+ #endif
+
+ EATEST_VERIFY(TestObject::IsClear());
+ TestObject::Reset();
+
+ return nErrorCount;
+}
+
+
+
+
+
+
+
+
+