Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 100 additions & 23 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -1925,15 +1925,15 @@ namespace ranges {
auto _UFirst = _Get_unwrapped(_STD move(_First));
auto _ULast = _Get_final_iterator_unwrapped<_It1>(_UFirst, _STD move(_Last));
_Seek_wrapped(_First, _ULast);
_Result = _RANGES _Move_backward_common(_STD move(_UFirst), _STD move(_ULast), _STD move(_Result));
_Result = _Move_backward_common(_STD move(_UFirst), _STD move(_ULast), _STD move(_Result));
return {_STD move(_First), _STD move(_Result)};
}

template <bidirectional_range _Rng, bidirectional_iterator _It>
requires indirectly_movable<iterator_t<_Rng>, _It>
constexpr move_backward_result<borrowed_iterator_t<_Rng>, _It> operator()(_Rng&& _Range, _It _Result) const {
auto _ULast = _Get_final_iterator_unwrapped(_Range);
_Result = _RANGES _Move_backward_common(_Ubegin(_Range), _ULast, _STD move(_Result));
_Result = _Move_backward_common(_Ubegin(_Range), _ULast, _STD move(_Result));
return {_Rewrap_iterator(_Range, _STD move(_ULast)), _STD move(_Result)};
}
// clang-format on
Expand Down Expand Up @@ -6402,7 +6402,7 @@ namespace ranges {
// FUNCTION TEMPLATE make_heap
template <class _RanIt, class _Pr>
_CONSTEXPR20 void _Make_heap_unchecked(_RanIt _First, _RanIt _Last, _Pr _Pred) {
// make nontrivial [_First, _Last) into a heap
// make [_First, _Last) into a heap
using _Diff = _Iter_diff_t<_RanIt>;
_Diff _Bottom = _Last - _First;
for (_Diff _Hole = _Bottom >> 1; _Hole > 0;) { // shift for codegen
Expand Down Expand Up @@ -6431,8 +6431,8 @@ namespace ranges {
template <random_access_iterator _It, class _Pr, class _Pj>
requires sortable<_It, _Pr, _Pj>
constexpr void _Make_heap_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) {
// make nontrivial [_First, _Last) into a heap with respect to _Pred and _Proj
// clang-format on
// make [_First, _Last) into a heap with respect to _Pred and _Proj
using _Diff = iter_difference_t<_It>;
const _Diff _Bottom = _Last - _First;
for (_Diff _Hole = _Bottom >> 1; _Hole > 0;) { // shift for codegen
Expand All @@ -6452,6 +6452,7 @@ namespace ranges {
template <random_access_iterator _It, sentinel_for<_It> _Se, class _Pr = ranges::less, class _Pj = identity>
requires sortable<_It, _Pr, _Pj>
constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_STD move(_First));
auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last));
Expand All @@ -6460,9 +6461,11 @@ namespace ranges {
return _First;
}

// clang-format off
template <random_access_range _Rng, class _Pr = ranges::less, class _Pj = identity>
requires sortable<iterator_t<_Rng>, _Pr, _Pj>
constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
if constexpr (common_range<_Rng>) {
_Make_heap_common(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj));
return _RANGES end(_Range);
Expand All @@ -6472,7 +6475,6 @@ namespace ranges {
return _Rewrap_iterator(_Range, _STD move(_ULast));
}
}
// clang-format on
};

inline constexpr _Make_heap_fn make_heap{_Not_quite_object::_Construct_tag{}};
Expand Down Expand Up @@ -6665,8 +6667,8 @@ namespace ranges {
template <random_access_iterator _It, class _Pr, class _Pj>
requires sortable<_It, _Pr, _Pj>
constexpr void _Sort_heap_common(const _It _First, _It _Last, _Pr _Pred, _Pj _Proj) {
// order heap by repeatedly popping
// clang-format on
// order heap by repeatedly popping
for (; _Last - _First >= 2; --_Last) {
_RANGES _Pop_heap_unchecked(_First, _Last, _Pred, _Proj);
}
Expand All @@ -6680,6 +6682,7 @@ namespace ranges {
template <random_access_iterator _It, sentinel_for<_It> _Se, class _Pr = ranges::less, class _Pj = identity>
requires sortable<_It, _Pr, _Pj>
constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_STD move(_First));
auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last));
Expand All @@ -6688,9 +6691,11 @@ namespace ranges {
return _First;
}

// clang-format off
template <random_access_range _Rng, class _Pr = ranges::less, class _Pj = identity>
requires sortable<iterator_t<_Rng>, _Pr, _Pj>
constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
if constexpr (common_range<_Rng>) {
_Sort_heap_common(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj));
return _RANGES end(_Range);
Expand All @@ -6700,7 +6705,6 @@ namespace ranges {
return _Rewrap_iterator(_Range, _STD move(_ULast));
}
}
// clang-format on
};

inline constexpr _Sort_heap_fn sort_heap{_Not_quite_object::_Construct_tag{}};
Expand Down Expand Up @@ -7628,13 +7632,15 @@ void sort(_ExPo&& _Exec, const _RanIt _First, const _RanIt _Last) noexcept /* te
// order [_First, _Last)
_STD sort(_STD forward<_ExPo>(_Exec), _First, _Last, less{});
}
#endif // _HAS_CXX17

#ifdef __cpp_lib_concepts
namespace ranges {
// clang-format off
template <bidirectional_iterator _It, class _Pr, class _Pj>
requires sortable<_It, _Pr, _Pj>
constexpr void _Insertion_sort_common(const _It _First, const _It _Last, _Pr _Pred, _Pj _Proj) {
// clang-format on
// insertion sort [_First, _Last)

if (_First == _Last) { // empty range is sorted
Expand All @@ -7643,7 +7649,7 @@ namespace ranges {

for (auto _Mid = _First; ++_Mid != _Last;) { // order next element
iter_value_t<_It> _Val = _RANGES iter_move(_Mid);
auto _Hole = _Mid;
auto _Hole = _Mid;

for (auto _Prev = _Hole;;) {
--_Prev;
Expand All @@ -7660,9 +7666,11 @@ namespace ranges {
}
}

// clang-format off
template <random_access_iterator _It, class _Pr, class _Pj>
requires sortable<_It, _Pr, _Pj>
constexpr void _Med3_unchecked(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) {
constexpr void _Med3_common(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) {
// clang-format on
// sort median of three elements to middle
if (_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) {
_RANGES iter_swap(_Mid, _First);
Expand All @@ -7680,33 +7688,37 @@ namespace ranges {
}
}

// clang-format off
template <random_access_iterator _It, class _Pr, class _Pj>
requires sortable<_It, _Pr, _Pj>
constexpr void _Guess_median_unchecked(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) {
constexpr void _Guess_median_common(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) {
// clang-format on
// sort median element to middle
using _Diff = iter_difference_t<_It>;
const _Diff _Count = _Last - _First;
if (_Count > 40) { // Tukey's ninther
const _Diff _Step = (_Count + 1) >> 3; // +1 can't overflow because range was made inclusive in caller
const _Diff _Two_step = _Step << 1; // note: intentionally discards low-order bit
_Med3_unchecked(_First, _First + _Step, _First + _Two_step, _Pred, _Proj);
_Med3_unchecked(_Mid - _Step, _Mid, _Mid + _Step, _Pred, _Proj);
_Med3_unchecked(_Last - _Two_step, _Last - _Step, _Last, _Pred, _Proj);
_Med3_unchecked(_First + _Step, _Mid, _Last - _Step, _Pred, _Proj);
_Med3_common(_First, _First + _Step, _First + _Two_step, _Pred, _Proj);
_Med3_common(_Mid - _Step, _Mid, _Mid + _Step, _Pred, _Proj);
_Med3_common(_Last - _Two_step, _Last - _Step, _Last, _Pred, _Proj);
_Med3_common(_First + _Step, _Mid, _Last - _Step, _Pred, _Proj);
} else {
_Med3_unchecked(_First, _Mid, _Last, _Pred, _Proj);
_Med3_common(_First, _Mid, _Last, _Pred, _Proj);
}
}

// clang-format off
template <random_access_iterator _It, class _Pr, class _Pj>
requires sortable<_It, _Pr, _Pj>
_NODISCARD constexpr subrange<_It> _Partition_by_median_guess_unchecked(
_NODISCARD constexpr subrange<_It> _Partition_by_median_guess_common(
_It _First, _It _Last, _Pr _Pred, _Pj _Proj) {
// clang-format on
// Choose a pivot, partition [_First, _Last) into elements less than pivot, elements equal to pivot, and
// elements greater than pivot; return the equal partition as a subrange.

_It _Mid = _First + ((_Last - _First) >> 1); // shift for codegen
_RANGES _Guess_median_unchecked(_First, _Mid, _RANGES prev(_Last), _Pred, _Proj);
_Guess_median_common(_First, _Mid, _RANGES prev(_Last), _Pred, _Proj);
_It _Pfirst = _Mid;
_It _Plast = _RANGES next(_Pfirst);

Expand All @@ -7716,8 +7728,7 @@ namespace ranges {
--_Pfirst;
}

while (_Plast < _Last
&& !_STD invoke(_Pred, _STD invoke(_Proj, *_Plast), _STD invoke(_Proj, *_Pfirst))
while (_Plast < _Last && !_STD invoke(_Pred, _STD invoke(_Proj, *_Plast), _STD invoke(_Proj, *_Pfirst))
&& !_STD invoke(_Pred, _STD invoke(_Proj, *_Pfirst), _STD invoke(_Proj, *_Plast))) {
++_Plast;
}
Expand Down Expand Up @@ -7775,10 +7786,76 @@ namespace ranges {
}
}
}
// clang-format on

// VARIABLE ranges::sort
class _Sort_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <random_access_iterator _It, sentinel_for<_It> _Se, class _Pr = ranges::less, class _Pj = identity>
requires sortable<_It, _Pr, _Pj>
constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_STD move(_First));
auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last));
_Seek_wrapped(_First, _ULast);
const auto _Count = _ULast - _UFirst;
_Sort_common(_STD move(_UFirst), _STD move(_ULast), _Count, _Pass_fn(_Pred), _Pass_fn(_Proj));
return _First;
}

// clang-format off
template <random_access_range _Rng, class _Pr = ranges::less, class _Pj = identity>
requires sortable<iterator_t<_Rng>, _Pr, _Pj>
constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
auto _UFirst = _Ubegin(_Range);
auto _ULast = _Get_final_iterator_unwrapped(_Range);
const auto _Count = _ULast - _UFirst;
_Sort_common(_STD move(_UFirst), _ULast, _Count, _Pass_fn(_Pred), _Pass_fn(_Proj));
return _Rewrap_iterator(_Range, _STD move(_ULast));
}

private:
template <class _It, class _Pr, class _Pj>
static constexpr void _Sort_common(_It _First, _It _Last, iter_difference_t<_It> _Ideal, _Pr _Pred, _Pj _Proj) {
// sort [_First, _Last) with respect to _Pred and _Proj
_STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>);

for (;;) {
if (_Last - _First <= _ISORT_MAX) { // small
_Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj);
return;
}

if (_Ideal <= 0) { // heap sort if too many divisions
_Make_heap_common(_First, _Last, _Pred, _Proj);
_Sort_heap_common(_STD move(_First), _STD move(_Last), _Pred, _Proj);
return;
}

// divide and conquer by quicksort
auto [_Mid_first, _Mid_last] = _Partition_by_median_guess_common(_First, _Last, _Pred, _Proj);

_Ideal = (_Ideal >> 1) + (_Ideal >> 2); // allow 1.5 log2(N) divisions

if (_Mid_first - _First < _Last - _Mid_last) { // loop on second half
_Sort_common(_First, _STD move(_Mid_first), _Ideal, _Pred, _Proj);
_First = _STD move(_Mid_last);
} else { // loop on first half
_Sort_common(_STD move(_Mid_last), _Last, _Ideal, _Pred, _Proj);
_Last = _STD move(_Mid_first);
}
}
}
};

inline constexpr _Sort_fn sort{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
#endif // _HAS_CXX17

// FUNCTION TEMPLATE stable_sort
template <class _FwdIt, class _Ty, class _Pr>
Expand Down Expand Up @@ -8184,7 +8261,7 @@ namespace ranges {
}

while (_ISORT_MAX < _Last - _First) { // divide and conquer, ordering partition containing Nth
subrange<_It> _Mid = _RANGES _Partition_by_median_guess_unchecked(_First, _Last, _Pred, _Proj);
subrange<_It> _Mid = _Partition_by_median_guess_common(_First, _Last, _Pred, _Proj);

if (_Mid.end() <= _Nth) {
_First = _Mid.end();
Expand All @@ -8196,7 +8273,7 @@ namespace ranges {
}

// sort any remainder
_RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj));
_Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj));
}
};

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ tests\P0896R4_ranges_alg_set_intersection
tests\P0896R4_ranges_alg_set_symmetric_difference
tests\P0896R4_ranges_alg_set_union
tests\P0896R4_ranges_alg_shuffle
tests\P0896R4_ranges_alg_sort
tests\P0896R4_ranges_alg_swap_ranges
tests\P0896R4_ranges_alg_transform_binary
tests\P0896R4_ranges_alg_transform_unary
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_sort/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_matrix.lst
61 changes: 61 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_sort/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <array>
#include <cassert>
#include <concepts>
#include <ranges>
#include <utility>

#include <range_algorithm_support.hpp>

using namespace std;
using P = pair<int, int>;

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::sort(borrowed<false>{})), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::sort(borrowed<true>{})), int*>);

struct instantiator {
static constexpr array input = {P{-1200257975, 0}, P{-1260655766, 1}, P{-1298559576, 2}, P{-1459960308, 3},
P{-2095681771, 4}, P{-441494788, 5}, P{-47163201, 6}, P{-912489821, 7}, P{1429106719, 8}, P{1668617627, 9}};

template <ranges::random_access_range R>
static constexpr void call() {
#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163
if constexpr (!ranges::contiguous_range<R>)
#endif // TRANSITION, VSO-938163
{
using ranges::sort, ranges::is_sorted, ranges::iterator_t, ranges::less;

{ // Validate range overload
auto buff = input;
const R range{buff};
const same_as<iterator_t<R>> auto result = sort(range, less{}, get_first);
assert(result == range.end());
assert(is_sorted(range, less{}, get_first));
}

{ // Validate iterator overload
auto buff = input;
const R range{buff};
const same_as<iterator_t<R>> auto result = sort(range.begin(), range.end(), less{}, get_first);
assert(result == range.end());
assert(is_sorted(range.begin(), range.end(), less{}, get_first));
}

{ // Validate empty range
const R range{};
const same_as<iterator_t<R>> auto result = sort(range, less{}, get_first);
assert(result == range.end());
assert(is_sorted(range, less{}, get_first));
}
}
}
};

int main() {
STATIC_ASSERT((test_random<instantiator, P>(), true));
test_random<instantiator, P>();
}