///////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. ///////////////////////////////////////////////////////////////////////////// #include // 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 #include #include #include #include #include #include #include #include #include #include namespace eastl { namespace Internal { typedef eastl::vector IntArray; typedef eastl::vector 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() 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* mpArray; TestObjectIndexCompare(vector* 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 struct RadixSortElement { typedef Key radix_type; Key mKey; uint16_t mData; bool operator<(const RadixSortElement &other) const { return mKey < other.mKey; } }; typedef RadixSortElement RadixSortElement8; typedef RadixSortElement RadixSortElement16; typedef RadixSortElement RadixSortElement32; template 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())); } { // 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()) == sorted + EAArrayCount(sorted) ); EATEST_VERIFY( is_sorted_until(notsorted + 2, notsorted + 8, eastl::less()) == notsorted + 6 ); } // Sort arrays of size 0 - N. Sort M random permutations of each. { vector 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 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 intArray; int i = 1000000; { EASTLTest_Rand testRng(232526); for (int n = 0; n < i; n++) { intArray.push_back(testRng.Rand()); } vector 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 deque; deque.push_back(1); insertion_sort(deque.begin(), deque.end()); insertion_sort(deque.begin(), deque.end(), eastl::less{}); } // TestObject sorting TestObject::Reset(); { vector 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 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 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 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::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()); 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 >(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 >(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 >(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>(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>(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>(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>(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, 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, 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, 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, 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, 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 intArray(1000); for(int i = 0; i < 1000; i++) intArray[i] = rng() % kElementRange; vector< vector > bucketArray(kElementRange); bucket_sort(intArray.begin(), intArray.end(), bucketArray, eastl::hash_use_self()); EATEST_VERIFY(is_sorted(intArray.begin(), intArray.end())); } { // stable_sort general test typedef eastl::less IntCompare; int intArray[2] = { 0, 1 }; stable_sort(intArray, intArray + 2); stable_sort(intArray, intArray + 2, IntCompare()); stable_sort(intArray, intArray + 2); stable_sort(intArray, intArray + 2, IntCompare()); MallocAllocator mallocAllocator; //stable_sort(intArray, intArray + 2, mallocAllocator); stable_sort(intArray, intArray + 2, mallocAllocator, IntCompare()); //stable_sort(intArray, intArray + 2, mallocAllocator); stable_sort(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 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(intDeque.begin(), intDeque.end(), compare); EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0)); StatefulCompare::Reset(); intDeque = intDequeSaved; shaker_sort(intDeque.begin(), intDeque.end(), compare); EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0)); StatefulCompare::Reset(); intDeque = intDequeSaved; insertion_sort(intDeque.begin(), intDeque.end(), compare); EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0)); StatefulCompare::Reset(); intDeque = intDequeSaved; selection_sort(intDeque.begin(), intDeque.end(), compare); EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0)); StatefulCompare::Reset(); intDeque = intDequeSaved; shell_sort(intDeque.begin(), intDeque.end(), compare); EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0)); StatefulCompare::Reset(); intDeque = intDequeSaved; comb_sort(intDeque.begin(), intDeque.end(), compare); EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0)); StatefulCompare::Reset(); intDeque = intDequeSaved; heap_sort(intDeque.begin(), intDeque.end(), compare); EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0)); StatefulCompare::Reset(); intDeque = intDequeSaved; merge_sort(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(intDeque.begin(), intDeque.end(), compare); EATEST_VERIFY((StatefulCompare::nCtorCount == 0) && (StatefulCompare::nDtorCount == 0) && (StatefulCompare::nCopyCount == 0)); StatefulCompare::Reset(); vector buffer(intDeque.size()/2); intDeque = intDequeSaved; tim_sort_buffer(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 intDeque; vector 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 toArray; vector 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 toArray; vector 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 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 evenArray; vector 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(), 1000); // Test stable_partition test_stable_sort(vector(), 0); // Test stable_partition on empty container test_stable_sort(vector(), 1); // Test stable_partition on container of one element test_stable_sort(vector(), 2); // Test stable_partition on container of two element test_stable_sort(list(), 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 intArray(11); for(eastl_size_t i = 0; i < intArray.size(); i++) intArray[i] = i; do { vector 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; }