PR libstdc++/100795 libstdc++-v3/ChangeLog:
* include/bits/ranges_algo.h (__detail::__move_merge): New, based on the stl_algo.h implementation. (__detail::__merge_sort_loop): Likewise. (__detail::__chunk_insertion_sort): Likewise. (__detail::__merge_sort_with_buffer): Likewise. (__detail::__stable_sort_adaptive): Likewise. (__detail::__stable_sort_adaptive_resize): Likewise. (__detail::__inplace_stable_sort): Likewise. (__stable_sort_fn::operator()): Reimplement in terms of the above. * testsuite/25_algorithms/stable_sort/constrained.cc: --- libstdc++-v3/include/bits/ranges_algo.h | 207 +++++++++++++++++- .../25_algorithms/stable_sort/constrained.cc | 30 +++ 2 files changed, 233 insertions(+), 4 deletions(-) diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h index b0357600adbc..7dfd4e7ed64c 100644 --- a/libstdc++-v3/include/bits/ranges_algo.h +++ b/libstdc++-v3/include/bits/ranges_algo.h @@ -2388,6 +2388,170 @@ namespace ranges inline constexpr __sort_fn sort{}; + namespace __detail + { + /// This is a helper function for the __merge_sort_loop routines. + template<typename _Iter, typename _Out, typename _Comp> + _Out + __move_merge(_Iter __first1, _Iter __last1, + _Iter __first2, _Iter __last2, + _Out __result, _Comp __comp) + { + while (__first1 != __last1 && __first2 != __last2) + { + if (__comp(*__first2, *__first1)) + { + *__result = ranges::iter_move(__first2); + ++__first2; + } + else + { + *__result = ranges::iter_move(__first1); + ++__first1; + } + ++__result; + } + return ranges::move(__first2, __last2, + ranges::move(__first1, __last1, __result).out).out; + } + + template<typename _Iter, typename _Out, typename _Distance, typename _Comp> + void + __merge_sort_loop(_Iter __first, _Iter __last, _Out __result, + _Distance __step_size, _Comp __comp) + { + const _Distance __two_step = 2 * __step_size; + + while (__last - __first >= __two_step) + { + __result = __detail::__move_merge(__first, __first + __step_size, + __first + __step_size, + __first + __two_step, + __result, __comp); + __first += __two_step; + } + __step_size = ranges::min(_Distance(__last - __first), __step_size); + + __detail::__move_merge(__first, __first + __step_size, + __first + __step_size, __last, __result, __comp); + } + + template<typename _Iter, typename _Distance, typename _Compare> + constexpr void + __chunk_insertion_sort(_Iter __first, _Iter __last, + _Distance __chunk_size, _Compare __comp) + { + while (__last - __first >= __chunk_size) + { + __detail::__insertion_sort(__first, __first + __chunk_size, __comp); + __first += __chunk_size; + } + __detail::__insertion_sort(__first, __last, __comp); + } + + template<typename _Iter, typename _Pointer, typename _Comp> + void + __merge_sort_with_buffer(_Iter __first, _Iter __last, + _Pointer __buffer, _Comp __comp) + { + using _Distance = iter_difference_t<_Iter>; + + const _Distance __len = __last - __first; + const _Pointer __buffer_last = __buffer + ptrdiff_t(__len); + + constexpr int __chunk_size = 7; + _Distance __step_size = __chunk_size; + __detail::__chunk_insertion_sort(__first, __last, __step_size, __comp); + + while (__step_size < __len) + { + __detail::__merge_sort_loop(__first, __last, __buffer, + __step_size, __comp); + __step_size *= 2; + __detail::__merge_sort_loop(__buffer, __buffer_last, __first, + ptrdiff_t(__step_size), __comp); + __step_size *= 2; + } + } + + template<typename _Iter, typename _Pointer, typename _Comp> + void + __merge_adaptive(_Iter __first, _Iter __middle, _Iter __last, + iter_difference_t<_Iter> __len1, + iter_difference_t<_Iter> __len2, + _Pointer __buffer, _Comp __comp); // defined near inplace_merge + + template<typename _Iter, typename _Distance, typename _Pointer, typename _Comp> + void + __merge_adaptive_resize(_Iter __first, _Iter __middle, _Iter __last, + _Distance __len1, _Distance __len2, + _Pointer __buffer, _Distance __buffer_size, + _Comp __comp); // defined near inplace_merge + + template<typename _Iter, typename _Distance, typename _Comp> + constexpr void + __merge_without_buffer(_Iter __first, _Iter __middle, _Iter __last, + _Distance __len1, _Distance __len2, + _Comp __comp); // defined near inplace_merge + + template<typename _Iter, typename _Pointer, typename _Comp> + void + __stable_sort_adaptive(_Iter __first, _Iter __middle, _Iter __last, + _Pointer __buffer, _Comp __comp) + { + __detail::__merge_sort_with_buffer(__first, __middle, __buffer, __comp); + __detail::__merge_sort_with_buffer(__middle, __last, __buffer, __comp); + + __detail::__merge_adaptive(__first, __middle, __last, + __middle - __first, __last - __middle, + __buffer, __comp); + } + + template<typename _Iter, typename _Pointer, typename _Distance, typename _Comp> + void + __stable_sort_adaptive_resize(_Iter __first, _Iter __last, + _Pointer __buffer, _Distance __buffer_size, + _Comp __comp) + { + const _Distance __len = (__last - __first + 1) / 2; + const _Iter __middle = __first + __len; + if (__len > __buffer_size) + { + __detail::__stable_sort_adaptive_resize(__first, __middle, __buffer, + __buffer_size, __comp); + __detail::__stable_sort_adaptive_resize(__middle, __last, __buffer, + __buffer_size, __comp); + __detail::__merge_adaptive_resize(__first, __middle, __last, + _Distance(__middle - __first), + _Distance(__last - __middle), + __buffer, __buffer_size, + __comp); + } + else + __detail::__stable_sort_adaptive(__first, __middle, __last, + __buffer, __comp); + } + + template<typename _Iter, typename _Comp> + _GLIBCXX26_CONSTEXPR + void + __inplace_stable_sort(_Iter __first, _Iter __last, _Comp __comp) + { + if (__last - __first < 15) + { + __detail::__insertion_sort(__first, __last, __comp); + return; + } + _Iter __middle = __first + (__last - __first) / 2; + __detail::__inplace_stable_sort(__first, __middle, __comp); + __detail::__inplace_stable_sort(__middle, __last, __comp); + __detail::__merge_without_buffer(__first, __middle, __last, + __middle - __first, + __last - __middle, + __comp); + } + } // namespace __detail + struct __stable_sort_fn { template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent, @@ -2398,10 +2562,45 @@ namespace ranges operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - std::stable_sort(std::move(__first), __lasti, - __detail::__make_comp_proj(__comp, __proj)); - return __lasti; + if constexpr (!same_as<_Iter, _Sent>) + return (*this)(__first, ranges::next(__first, __last), + std::move(__comp), std::move(__proj)); + else + { + using _DistanceType = iter_difference_t<_Iter>; + + if (__first == __last) + return __last; + +#if _GLIBCXX_HOSTED +# if __glibcxx_constexpr_algorithms >= 202306L // >= C++26 + if consteval { + __detail::__inplace_stable_sort(__first, __last, __comp); + return __last; + } +# endif + + typedef _Temporary_buffer<_Iter, iter_value_t<_Iter>> _TmpBuf; + // __stable_sort_adaptive sorts the range in two halves, + // so the buffer only needs to fit half the range at once. + _TmpBuf __buf(__first, ptrdiff_t((__last - __first + 1) / 2)); + + auto __comp_proj = __detail::__make_comp_proj(__comp, __proj); + if (__buf._M_requested_size() == __buf.size()) [[likely]] + __detail::__stable_sort_adaptive(__first, + __first + _DistanceType(__buf.size()), + __last, __buf.begin(), __comp_proj); + else if (__buf.begin()) [[unlikely]] + __detail::__inplace_stable_sort(__first, __last, __comp_proj); + else + __detail::__stable_sort_adaptive_resize(__first, __last, __buf.begin(), + _DistanceType(__buf.size()), + __comp_proj); +#else + __detail::__inplace_stable_sort(__first, __last, __comp_proj); +#endif + return __last; + } } template<random_access_range _Range, diff --git a/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc index 0bd438fa5f29..55043442f4be 100644 --- a/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc @@ -20,6 +20,7 @@ #include <algorithm> #include <random> +#include <ranges> #include <vector> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -62,8 +63,37 @@ test01() } } +void +test02() +{ + // PR libstdc++/100795 - ranges::stable_sort should not use std::stable_sort + // directly +#if __SIZEOF_INT128__ + auto v = std::views::iota(__int128(0), __int128(20)); +#else + auto v = std::views::iota(0ll, 20ll); +#endif + + int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17}; + auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; }); + using type = decltype(w); + using cat = std::iterator_traits<std::ranges::iterator_t<type>>::iterator_category; + static_assert( std::same_as<cat, std::output_iterator_tag> ); + static_assert( std::ranges::random_access_range<type> ); + + ranges::stable_sort(w); + VERIFY( ranges::equal(w, v) ); + + ranges::stable_sort(w, std::ranges::greater{}); + VERIFY( ranges::equal(w, v | std::views::reverse) ); + + ranges::stable_sort(w, std::ranges::greater{}, std::negate{}); + VERIFY( ranges::equal(w, v) ); +} + int main() { test01(); + test02(); } -- 2.50.0.131.gcf6f63ea6b