diff --git a/AK/Iterator.h b/AK/Iterator.h index a0e0b6a7ecc..0ee6d1c1981 100644 --- a/AK/Iterator.h +++ b/AK/Iterator.h @@ -78,6 +78,13 @@ public: constexpr const ValueType* operator->() const { return &m_container[m_index]; } constexpr ValueType* operator->() { return &m_container[m_index]; } + SimpleIterator& operator=(const SimpleIterator& other) + { + m_index = other.m_index; + return *this; + } + SimpleIterator(const SimpleIterator& obj) = default; + private: static constexpr SimpleIterator begin(Container& container) { return { container, 0 }; } static constexpr SimpleIterator end(Container& container) diff --git a/AK/QuickSort.h b/AK/QuickSort.h index 45e7973fa42..b57526deddd 100644 --- a/AK/QuickSort.h +++ b/AK/QuickSort.h @@ -86,35 +86,50 @@ void dual_pivot_quick_sort(Collection& col, int start, int end, LessThan less_th } template -void quick_sort(Iterator start, Iterator end, LessThan less_than) +void single_pivot_quick_sort(Iterator start, Iterator end, LessThan less_than) { - int size = end - start; - if (size <= 1) - return; + for (;;) { + int size = end - start; + if (size <= 1) + return; - int pivot_point = size / 2; - auto pivot = *(start + pivot_point); + int pivot_point = size / 2; + if (pivot_point) + swap(*(start + pivot_point), *start); - if (pivot_point) - swap(*(start + pivot_point), *start); + auto&& pivot = *start; - int i = 1; - for (int j = 1; j < size; ++j) { - if (less_than(*(start + j), pivot)) { - swap(*(start + j), *(start + i)); - ++i; + int i = 1; + for (int j = 1; j < size; ++j) { + if (less_than(*(start + j), pivot)) { + swap(*(start + j), *(start + i)); + ++i; + } + } + + swap(*start, *(start + i - 1)); + // Recur into the shorter part of the remaining data + // to ensure a stack depth of at most log(n). + if (i > size / 2) { + single_pivot_quick_sort(start + i, end, less_than); + end = start + i - 1; + } else { + single_pivot_quick_sort(start, start + i - 1, less_than); + start = start + i; } } - - swap(*start, *(start + i - 1)); - quick_sort(start, start + i - 1, less_than); - quick_sort(start + i, end, less_than); } template void quick_sort(Iterator start, Iterator end) { - quick_sort(start, end, [](auto& a, auto& b) { return a < b; }); + single_pivot_quick_sort(start, end, [](auto& a, auto& b) { return a < b; }); +} + +template +void quick_sort(Iterator start, Iterator end, LessThan less_than) +{ + single_pivot_quick_sort(start, end, move(less_than)); } template diff --git a/AK/Tests/TestQuickSort.cpp b/AK/Tests/TestQuickSort.cpp index 476778a3bdc..8902de967f9 100644 --- a/AK/Tests/TestQuickSort.cpp +++ b/AK/Tests/TestQuickSort.cpp @@ -46,13 +46,77 @@ TEST_CASE(sorts_without_copy) }; Array array; + + // Test the dual pivot quick sort. for (size_t i = 0; i < 64; ++i) array[i].value = (64 - i) % 32 + 32; - quick_sort(array, [](auto& a, auto& b) { return a.value < b.value; }); + dual_pivot_quick_sort(array, 0, array.size() - 1, [](auto& a, auto& b) { return a.value < b.value; }); + + for (size_t i = 0; i < 63; ++i) + EXPECT(array[i].value <= array[i + 1].value); + + // Test the single pivot quick sort. + for (size_t i = 0; i < 64; ++i) + array[i].value = (64 - i) % 32 + 32; + + AK::single_pivot_quick_sort(&array[0], &array[64], [](auto& a, auto& b) { return a.value < b.value; }); for (size_t i = 0; i < 63; ++i) EXPECT(array[i].value <= array[i + 1].value); } +// This test case may fail to construct a worst-case input if the pivot choice +// of the underlying quick_sort no longer matches the one used here. +// So it provides no strong guarantees about the properties of quick_sort. +TEST_CASE(maximum_stack_depth) +{ + const int size = 256; + int* data = new int[size]; + + for (int i = 0; i < size; i++) { + data[i] = i; + } + + // Construct the data in such a way that the assumed pivot choice + // of (size / 2) causes the partitions to be of worst case size. + for (int i = 0; i < size / 2; i++) { + swap(data[i], data[i + (size - i) / 2]); + } + + // Measure the depth of the call stack through the less_than argument + // of quick_sort as it gets copied for each recursive call. + struct DepthMeasurer { + int& max_depth; + int depth { 0 }; + DepthMeasurer(int& max_depth) + : max_depth(max_depth) + { + } + DepthMeasurer(const DepthMeasurer& obj) + : max_depth(obj.max_depth) + { + depth = obj.depth + 1; + if (depth > max_depth) { + max_depth = depth; + } + } + bool operator()(int& a, int& b) + { + return a < b; + } + }; + + int max_depth = 0; + DepthMeasurer measurer(max_depth); + AK::single_pivot_quick_sort(data, data + size, measurer); + + EXPECT(max_depth <= 64); + + for (int i = 0; i < size; i++) + EXPECT(data[i] == i); + + delete[] data; +} + TEST_MAIN(QuickSort)