/* Copyright (C) 2005,2009-2010 Electronic Arts, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /////////////////////////////////////////////////////////////////////////////// // EASTL/sort.h // Written by Paul Pedriana - 2005. ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // This file implements sorting algorithms. Some of these are equivalent to // std C++ sorting algorithms, while others don't have equivalents in the // C++ standard. We implement the following sorting algorithms: // is_sorted // sort The implementation of this is simply mapped to quick_sort. // quick_sort // partial_sort // insertion_sort // shell_sort // heap_sort // stable_sort The implementation of this is simply mapped to merge_sort. // merge // merge_sort // merge_sort_buffer // nth_element // radix_sort Found in sort_extra.h. // comb_sort Found in sort_extra.h. // bubble_sort Found in sort_extra.h. // selection_sort Found in sort_extra.h. // shaker_sort Found in sort_extra.h. // bucket_sort Found in sort_extra.h. // // Additional sorting and related algorithms we may want to implement: // partial_sort_copy This would be like the std STL version. // paritition This would be like the std STL version. This is not categorized as a sort routine by the language standard. // stable_partition This would be like the std STL version. // counting_sort Maybe we don't want to implement this. // ////////////////////////////////////////////////////////////////////////////// #ifndef EASTL_SORT_H #define EASTL_SORT_H #include #include #include #include #include #include #include #include namespace eastl { /// is_sorted /// /// Returns true if the range [first, last) is sorted. /// An empty range is considered to be sorted. /// To test if a range is reverse-sorted, use 'greater' as the comparison /// instead of 'less'. /// /// Example usage: /// vector intArray; /// bool bIsSorted = is_sorted(intArray.begin(), intArray.end()); /// bool bIsReverseSorted = is_sorted(intArray.begin(), intArray.end(), greater()); /// template bool is_sorted(ForwardIterator first, ForwardIterator last, StrictWeakOrdering compare) { if(first != last) { ForwardIterator current = first; for(++current; current != last; first = current, ++current) { if(compare(*current, *first)) { EASTL_VALIDATE_COMPARE(!compare(*first, *current)); // Validate that the compare function is sane. return false; } } } return true; } template inline bool is_sorted(ForwardIterator first, ForwardIterator last) { typedef eastl::less::value_type> Less; return eastl::is_sorted(first, last, Less()); } /// merge /// /// This function merges two sorted input sorted ranges into a result sorted range. /// This merge is stable in that no element from the first range will be changed /// in order relative to other elements from the first range. /// template OutputIterator merge(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare compare) { while((first1 != last1) && (first2 != last2)) { if(compare(*first2, *first1)) { EASTL_VALIDATE_COMPARE(!compare(*first1, *first2)); // Validate that the compare function is sane. *result = *first2; ++first2; } else { *result = *first1; ++first1; } ++result; } return eastl::copy(first2, last2, eastl::copy(first1, last1, result)); } template inline OutputIterator merge(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) { typedef eastl::less::value_type> Less; return eastl::merge (first1, last1, first2, last2, result, Less()); } /// insertion_sort /// /// Implements the InsertionSort algorithm. /// template void insertion_sort(BidirectionalIterator first, BidirectionalIterator last, StrictWeakOrdering compare) { typedef typename eastl::iterator_traits::value_type value_type; if(first != last) { BidirectionalIterator iCurrent, iNext, iSorted = first; for(++iSorted; iSorted != last; ++iSorted) { const value_type temp(*iSorted); iNext = iCurrent = iSorted; for(--iCurrent; (iNext != first) && compare(temp, *iCurrent); --iNext, --iCurrent) { EASTL_VALIDATE_COMPARE(!compare(*iCurrent, temp)); // Validate that the compare function is sane. *iNext = *iCurrent; } *iNext = temp; } } } // insertion_sort template void insertion_sort(BidirectionalIterator first, BidirectionalIterator last) { typedef typename eastl::iterator_traits::value_type value_type; if(first != last) { BidirectionalIterator iCurrent, iNext, iSorted = first; for(++iSorted; iSorted != last; ++iSorted) { const value_type temp(*iSorted); iNext = iCurrent = iSorted; for(--iCurrent; (iNext != first) && (temp < *iCurrent); --iNext, --iCurrent) { EASTL_VALIDATE_COMPARE(!(*iCurrent < temp)); // Validate that the compare function is sane. *iNext = *iCurrent; } *iNext = temp; } } } // insertion_sort #if 0 /* // STLPort-like variation of insertion_sort. Doesn't seem to run quite as fast for small runs. // template void insertion_sort(RandomAccessIterator first, RandomAccessIterator last, Compare compare) { if(first != last) { for(RandomAccessIterator i = first + 1; i != last; ++i) { const typename eastl::iterator_traits::value_type value(*i); if(compare(value, *first)) { EASTL_VALIDATE_COMPARE(!compare(*first, value)); // Validate that the compare function is sane. eastl::copy_backward(first, i, i + 1); *first = value; } else { RandomAccessIterator end(i), prev(i); for(--prev; compare(value, *prev); --end, --prev) { EASTL_VALIDATE_COMPARE(!compare(*prev, value)); // Validate that the compare function is sane. *end = *prev; } *end = value; } } } } // STLPort-like variation of insertion_sort. Doesn't seem to run quite as fast for small runs. // template void insertion_sort(RandomAccessIterator first, RandomAccessIterator last) { if(first != last) { for(RandomAccessIterator i = first + 1; i != last; ++i) { const typename eastl::iterator_traits::value_type value(*i); if(value < *first) { EASTL_VALIDATE_COMPARE(!(*first < value)); // Validate that the compare function is sane. eastl::copy_backward(first, i, i + 1); *first = value; } else { RandomAccessIterator end(i), prev(i); for(--prev; value < *prev; --end, --prev) { EASTL_VALIDATE_COMPARE(!(*prev < value)); // Validate that the compare function is sane. *end = *prev; } *end = value; } } } } */ #endif /// shell_sort /// /// Implements the ShellSort algorithm. This algorithm is a serious algorithm for larger /// data sets, as reported by Sedgewick in his discussions on QuickSort. Note that shell_sort /// requires a random access iterator, which usually means an array (eg. vector, deque). /// ShellSort has good performance with presorted sequences. /// The term "shell" derives from the name of the inventor, David Shell. /// /// To consider: Allow the user to specify the "h-sequence" array. /// template void shell_sort(RandomAccessIterator first, RandomAccessIterator last, StrictWeakOrdering compare) { typedef typename eastl::iterator_traits::difference_type difference_type; // We use the Knuth 'h' sequence below, as it is easy to calculate at runtime. // However, possibly we are better off using a different sequence based on a table. // One such sequence which averages slightly better than Knuth is: // 1, 5, 19, 41, 109, 209, 505, 929, 2161, 3905, 8929, 16001, 36289, // 64769, 146305, 260609, 587521, 1045505, 2354689, 4188161, 9427969, 16764929 if(first != last) { RandomAccessIterator iCurrent, iBack, iSorted, iInsertFirst; difference_type nSize = last - first; difference_type nSpace = 1; // nSpace is the 'h' value of the ShellSort algorithm. while(nSpace < nSize) nSpace = (nSpace * 3) + 1; // This is the Knuth 'h' sequence: 1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 797161, 2391484, 7174453, 21523360, 64570081, 193710244, for(nSpace = (nSpace - 1) / 3; nSpace >= 1; nSpace = (nSpace - 1) / 3) // Integer division is less than ideal. { for(difference_type i = 0; i < nSpace; i++) { iInsertFirst = first + i; for(iSorted = iInsertFirst + nSpace; iSorted < last; iSorted += nSpace) { iBack = iCurrent = iSorted; for(iBack -= nSpace; (iCurrent != iInsertFirst) && compare(*iCurrent, *iBack); iCurrent = iBack, iBack -= nSpace) { EASTL_VALIDATE_COMPARE(!compare(*iBack, *iCurrent)); // Validate that the compare function is sane. eastl::iter_swap(iCurrent, iBack); } } } } } } // shell_sort template inline void shell_sort(RandomAccessIterator first, RandomAccessIterator last) { typedef eastl::less::value_type> Less; eastl::shell_sort(first, last, Less()); } /// heap_sort /// /// Implements the HeapSort algorithm. /// Note that heap_sort requires a random access iterator, which usually means /// an array (eg. vector, deque). /// template void heap_sort(RandomAccessIterator first, RandomAccessIterator last, StrictWeakOrdering compare) { // We simply call our heap algorithms to do the work for us. eastl::make_heap(first, last, compare); eastl::sort_heap(first, last, compare); } template inline void heap_sort(RandomAccessIterator first, RandomAccessIterator last) { typedef eastl::less::value_type> Less; eastl::heap_sort(first, last, Less()); } /// merge_sort_buffer /// /// Implements the MergeSort algorithm with a user-supplied buffer. /// The input buffer must be able to hold a number of items equal to 'last - first'. /// Note that merge_sort_buffer requires a random access iterator, which usually means /// an array (eg. vector, deque). /// // For reference, the following is the simple version, before inlining one level // of recursion and eliminating the copy: // //template //void merge_sort_buffer(RandomAccessIterator first, RandomAccessIterator last, T* pBuffer, StrictWeakOrdering compare) //{ // typedef typename eastl::iterator_traits::difference_type difference_type; // // const difference_type nCount = last - first; // // if(nCount > 1) // { // const difference_type nMid = nCount / 2; // // eastl::merge_sort_buffer // (first, first + nMid, pBuffer, compare); // eastl::merge_sort_buffer // (first + nMid, last , pBuffer, compare); // eastl::copy(first, last, pBuffer); // eastl::merge // (pBuffer, pBuffer + nMid, pBuffer + nMid, pBuffer + nCount, first, compare); // } //} template void merge_sort_buffer(RandomAccessIterator first, RandomAccessIterator last, T* pBuffer, StrictWeakOrdering compare) { typedef typename eastl::iterator_traits::difference_type difference_type; const difference_type nCount = last - first; if(nCount > 1) { const difference_type nMid = nCount / 2; RandomAccessIterator half = first + nMid; if(nMid > 1) { const difference_type nQ1(nMid / 2); RandomAccessIterator part(first + nQ1); eastl::merge_sort_buffer(first, part, pBuffer, compare); eastl::merge_sort_buffer(part, half, pBuffer + nQ1, compare); eastl::merge (first, part, part, half, pBuffer, compare); } else *pBuffer = *first; if((nCount - nMid) > 1) { const difference_type nQ3((nMid + nCount) / 2); RandomAccessIterator part(first + nQ3); eastl::merge_sort_buffer(half, part, pBuffer + nMid, compare); eastl::merge_sort_buffer(part, last, pBuffer + nQ3, compare); eastl::merge (half, part, part, last, pBuffer + nMid, compare); } else *(pBuffer + nMid) = *half; eastl::merge (pBuffer, pBuffer + nMid, pBuffer + nMid, pBuffer + nCount, first, compare); } } template inline void merge_sort_buffer(RandomAccessIterator first, RandomAccessIterator last, T* pBuffer) { typedef eastl::less::value_type> Less; eastl::merge_sort_buffer(first, last, pBuffer, Less()); } /// merge_sort /// /// Implements the MergeSort algorithm. /// This algorithm allocates memory via the user-supplied allocator. Use merge_sort_buffer /// function if you want a version which doesn't allocate memory. /// Note that merge_sort requires a random access iterator, which usually means /// an array (eg. vector, deque). /// template void merge_sort(RandomAccessIterator first, RandomAccessIterator last, Allocator& allocator, StrictWeakOrdering compare) { typedef typename eastl::iterator_traits::difference_type difference_type; typedef typename eastl::iterator_traits::value_type value_type; const difference_type nCount = last - first; if(nCount > 1) { // We need to allocate an array of nCount value_type objects as a temporary buffer. value_type* const pBuffer = (value_type*)allocate_memory(allocator, nCount * sizeof(value_type), EASTL_ALIGN_OF(value_type), 0); eastl::uninitialized_fill(pBuffer, pBuffer + nCount, value_type()); eastl::merge_sort_buffer (first, last, pBuffer, compare); eastl::destruct(pBuffer, pBuffer + nCount); EASTLFree(allocator, pBuffer, nCount * sizeof(value_type)); } } template inline void merge_sort(RandomAccessIterator first, RandomAccessIterator last, Allocator& allocator) { typedef eastl::less::value_type> Less; eastl::merge_sort(first, last, allocator, Less()); } ///////////////////////////////////////////////////////////////////// // quick_sort // // We do the "introspection sort" variant of quick sort which is now // well-known and understood. You can read about this algorithm in // many articles on quick sort, but briefly what it does is a median- // of-three quick sort whereby the recursion depth is limited to a // some value (after which it gives up on quick sort and switches to // a heap sort) and whereby after a certain amount of sorting the // algorithm stops doing quick-sort and finishes the sorting via // a simple insertion sort. ///////////////////////////////////////////////////////////////////// static const int kQuickSortLimit = 28; // For sorts of random arrays over 100 items, 28 - 32 have been found to be good numbers on VC++/Win32. namespace Internal { template inline Size Log2(Size n) { int i; for(i = 0; n; ++i) n >>= 1; return i - 1; } // To do: Investigate the speed of this bit-trick version of Log2. // It may work better on some platforms but not others. // // union FloatUnion { // float f; // uint32_t i; // }; // // inline uint32_t Log2(uint32_t x) // { // const FloatInt32Union u = { x }; // return (u.i >> 23) - 127; // } } /// get_partition /// /// This function takes const T& instead of T because T may have special alignment /// requirements and some compilers (e.g. VC++) are don't respect alignment requirements /// for function arguments. /// template inline RandomAccessIterator get_partition(RandomAccessIterator first, RandomAccessIterator last, const T& pivotValue) { const T pivotCopy(pivotValue); // Need to make a temporary because the sequence below is mutating. for(; ; ++first) { while(*first < pivotCopy) { EASTL_VALIDATE_COMPARE(!(pivotCopy < *first)); // Validate that the compare function is sane. ++first; } --last; while(pivotCopy < *last) { EASTL_VALIDATE_COMPARE(!(*last < pivotCopy)); // Validate that the compare function is sane. --last; } if(first >= last) // Random access iterators allow operator >= return first; eastl::iter_swap(first, last); } } template inline RandomAccessIterator get_partition(RandomAccessIterator first, RandomAccessIterator last, const T& pivotValue, Compare compare) { const T pivotCopy(pivotValue); // Need to make a temporary because the sequence below is mutating. for(; ; ++first) { while(compare(*first, pivotCopy)) { EASTL_VALIDATE_COMPARE(!compare(pivotCopy, *first)); // Validate that the compare function is sane. ++first; } --last; while(compare(pivotCopy, *last)) { EASTL_VALIDATE_COMPARE(!compare(*last, pivotCopy)); // Validate that the compare function is sane. --last; } if(first >= last) // Random access iterators allow operator >= return first; eastl::iter_swap(first, last); } } namespace Internal { // This function is used by quick_sort and is not intended to be used by itself. // This is because the implementation below makes an assumption about the input // data that quick_sort satisfies but arbitrary data may not. // There is a standalone insertion_sort function. template inline void insertion_sort_simple(RandomAccessIterator first, RandomAccessIterator last) { for(RandomAccessIterator current = first; current != last; ++current) { typedef typename eastl::iterator_traits::value_type value_type; RandomAccessIterator end(current), prev(current); const value_type value(*current); for(--prev; value < *prev; --end, --prev) // We skip checking for (prev >= first) because quick_sort (our caller) makes this unnecessary. { EASTL_VALIDATE_COMPARE(!(*prev < value)); // Validate that the compare function is sane. *end = *prev; } *end = value; } } // This function is used by quick_sort and is not intended to be used by itself. // This is because the implementation below makes an assumption about the input // data that quick_sort satisfies but arbitrary data may not. // There is a standalone insertion_sort function. template inline void insertion_sort_simple(RandomAccessIterator first, RandomAccessIterator last, Compare compare) { for(RandomAccessIterator current = first; current != last; ++current) { typedef typename eastl::iterator_traits::value_type value_type; RandomAccessIterator end(current), prev(current); const value_type value(*current); for(--prev; compare(value, *prev); --end, --prev) // We skip checking for (prev >= first) because quick_sort (our caller) makes this unnecessary. { EASTL_VALIDATE_COMPARE(!compare(*prev, value)); // Validate that the compare function is sane. *end = *prev; } *end = value; } } } // namespace Internal template inline void partial_sort(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last) { typedef typename eastl::iterator_traits::difference_type difference_type; typedef typename eastl::iterator_traits::value_type value_type; eastl::make_heap(first, middle); for(RandomAccessIterator i = middle; i < last; ++i) { if(*i < *first) { EASTL_VALIDATE_COMPARE(!(*first < *i)); // Validate that the compare function is sane. const value_type temp(*i); *i = *first; eastl::adjust_heap (first, difference_type(0), difference_type(middle - first), difference_type(0), temp); } } eastl::sort_heap(first, middle); } template inline void partial_sort(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, Compare compare) { typedef typename eastl::iterator_traits::difference_type difference_type; typedef typename eastl::iterator_traits::value_type value_type; eastl::make_heap(first, middle, compare); for(RandomAccessIterator i = middle; i < last; ++i) { if(compare(*i, *first)) { EASTL_VALIDATE_COMPARE(!compare(*first, *i)); // Validate that the compare function is sane. const value_type temp(*i); *i = *first; eastl::adjust_heap (first, difference_type(0), difference_type(middle - first), difference_type(0), temp, compare); } } eastl::sort_heap(first, middle, compare); } template inline void nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last) { typedef typename iterator_traits::value_type value_type; while((last - first) > 5) { const value_type midValue(eastl::median(*first, *(first + (last - first) / 2), *(last - 1))); const RandomAccessIterator midPos(eastl::get_partition(first, last, midValue)); if(midPos <= nth) first = midPos; else last = midPos; } eastl::insertion_sort(first, last); } template inline void nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last, Compare compare) { typedef typename iterator_traits::value_type value_type; while((last - first) > 5) { const value_type midValue(eastl::median(*first, *(first + (last - first) / 2), *(last - 1), compare)); const RandomAccessIterator midPos(eastl::get_partition(first, last, midValue, compare)); if(midPos <= nth) first = midPos; else last = midPos; } eastl::insertion_sort(first, last, compare); } template inline void quick_sort_impl(RandomAccessIterator first, RandomAccessIterator last, Size kRecursionCount) { typedef typename iterator_traits::value_type value_type; while(((last - first) > kQuickSortLimit) && (kRecursionCount > 0)) { const RandomAccessIterator position(eastl::get_partition(first, last, eastl::median(*first, *(first + (last - first) / 2), *(last - 1)))); eastl::quick_sort_impl(position, last, --kRecursionCount); last = position; } if(kRecursionCount == 0) eastl::partial_sort(first, last, last); } template inline void quick_sort_impl(RandomAccessIterator first, RandomAccessIterator last, Size kRecursionCount, Compare compare) { typedef typename iterator_traits::value_type value_type; while(((last - first) > kQuickSortLimit) && (kRecursionCount > 0)) { const RandomAccessIterator position(eastl::get_partition(first, last, eastl::median(*first, *(first + (last - first) / 2), *(last - 1), compare), compare)); eastl::quick_sort_impl(position, last, --kRecursionCount, compare); last = position; } if(kRecursionCount == 0) eastl::partial_sort(first, last, last, compare); } /// quick_sort /// /// quick_sort sorts the elements in [first, last) into ascending order, /// meaning that if i and j are any two valid iterators in [first, last) /// such that i precedes j, then *j is not less than *i. quick_sort is not /// guaranteed to be stable. That is, suppose that *i and *j are equivalent: /// neither one is less than the other. It is not guaranteed that the /// relative order of these two elements will be preserved by sort. /// /// We implement the "introspective" variation of quick-sort. This is /// considered to be the best general-purpose variant, as it avoids /// worst-case behaviour and optimizes the final sorting stage by /// switching to an insertion sort. /// template void quick_sort(RandomAccessIterator first, RandomAccessIterator last) { typedef typename eastl::iterator_traits::difference_type difference_type; if(first != last) { eastl::quick_sort_impl(first, last, 2 * Internal::Log2(last - first)); if((last - first) > (difference_type)kQuickSortLimit) { eastl::insertion_sort(first, first + kQuickSortLimit); eastl::Internal::insertion_sort_simple(first + kQuickSortLimit, last); } else eastl::insertion_sort(first, last); } } template void quick_sort(RandomAccessIterator first, RandomAccessIterator last, Compare compare) { typedef typename eastl::iterator_traits::difference_type difference_type; if(first != last) { eastl::quick_sort_impl(first, last, 2 * Internal::Log2(last - first), compare); if((last - first) > (difference_type)kQuickSortLimit) { eastl::insertion_sort(first, first + kQuickSortLimit, compare); eastl::Internal::insertion_sort_simple(first + kQuickSortLimit, last, compare); } else eastl::insertion_sort(first, last, compare); } } /// sort /// /// We simply use quick_sort. See quick_sort for details. /// template inline void sort(RandomAccessIterator first, RandomAccessIterator last) { eastl::quick_sort(first, last); } template inline void sort(RandomAccessIterator first, RandomAccessIterator last, Compare compare) { eastl::quick_sort(first, last, compare); } /// stable_sort /// /// We simply use merge_sort. See merge_sort for details. /// Beware that the used merge_sort -- and thus stable_sort -- allocates /// memory during execution. Try using merge_sort_buffer if you want /// to avoid memory allocation. /// template void stable_sort(RandomAccessIterator first, RandomAccessIterator last, StrictWeakOrdering compare) { eastl::merge_sort (first, last, *get_default_allocator(0), compare); } template void stable_sort(RandomAccessIterator first, RandomAccessIterator last) { eastl::merge_sort (first, last, *get_default_allocator(0)); } template void stable_sort(RandomAccessIterator first, RandomAccessIterator last, Allocator& allocator, StrictWeakOrdering compare) { eastl::merge_sort(first, last, allocator, compare); } // This is not defined because it would cause compiler errors due to conflicts with a version above. //template //void stable_sort(RandomAccessIterator first, RandomAccessIterator last, Allocator& allocator) //{ // eastl::merge_sort(first, last, allocator); //} } // namespace eastl #endif // Header include guard