mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-16 17:46:40 +00:00
[libc++] Make std::stable_sort constexpr friendly (#110320)
Implementing `constexpr std::stable_sort`. This is part of P2562R1, tracked via issue #105360. Closes #119394 Co-authored-by: A. Jiang <de34@live.cn> Co-authored-by: Louis Dionne <ldionne.2@gmail.com>
This commit is contained in:
parent
b1751faada
commit
438e2ccd4a
@ -2,7 +2,7 @@
|
||||
"`P2497R0 <https://wg21.link/P2497R0>`__","Testing for success or failure of ``<charconv>`` functions","2023-06 (Varna)","|Complete|","18",""
|
||||
"`P2592R3 <https://wg21.link/P2592R3>`__","Hashing support for ``std::chrono`` value classes","2023-06 (Varna)","","",""
|
||||
"`P2587R3 <https://wg21.link/P2587R3>`__","``to_string`` or not ``to_string``","2023-06 (Varna)","","",""
|
||||
"`P2562R1 <https://wg21.link/P2562R1>`__","``constexpr`` Stable Sorting","2023-06 (Varna)","","",""
|
||||
"`P2562R1 <https://wg21.link/P2562R1>`__","``constexpr`` Stable Sorting","2023-06 (Varna)","|Partial|","20.0",""
|
||||
"`P2545R4 <https://wg21.link/P2545R4>`__","Read-Copy Update (RCU)","2023-06 (Varna)","","",""
|
||||
"`P2530R3 <https://wg21.link/P2530R3>`__","Hazard Pointers for C++26","2023-06 (Varna)","","",""
|
||||
"`P2538R1 <https://wg21.link/P2538R1>`__","ADL-proof ``std::projected``","2023-06 (Varna)","|Complete|","18",""
|
||||
|
|
@ -44,17 +44,17 @@ private:
|
||||
_Predicate __p_;
|
||||
|
||||
public:
|
||||
_LIBCPP_HIDE_FROM_ABI __invert() {}
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __invert() {}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI explicit __invert(_Predicate __p) : __p_(__p) {}
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __invert(_Predicate __p) : __p_(__p) {}
|
||||
|
||||
template <class _T1>
|
||||
_LIBCPP_HIDE_FROM_ABI bool operator()(const _T1& __x) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool operator()(const _T1& __x) {
|
||||
return !__p_(__x);
|
||||
}
|
||||
|
||||
template <class _T1, class _T2>
|
||||
_LIBCPP_HIDE_FROM_ABI bool operator()(const _T1& __x, const _T2& __y) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool operator()(const _T1& __x, const _T2& __y) {
|
||||
return __p_(__y, __x);
|
||||
}
|
||||
};
|
||||
@ -66,7 +66,7 @@ template <class _AlgPolicy,
|
||||
class _InputIterator2,
|
||||
class _Sent2,
|
||||
class _OutputIterator>
|
||||
_LIBCPP_HIDE_FROM_ABI void __half_inplace_merge(
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __half_inplace_merge(
|
||||
_InputIterator1 __first1,
|
||||
_Sent1 __last1,
|
||||
_InputIterator2 __first2,
|
||||
@ -91,7 +91,7 @@ _LIBCPP_HIDE_FROM_ABI void __half_inplace_merge(
|
||||
}
|
||||
|
||||
template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
|
||||
_LIBCPP_HIDE_FROM_ABI void __buffered_inplace_merge(
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __buffered_inplace_merge(
|
||||
_BidirectionalIterator __first,
|
||||
_BidirectionalIterator __middle,
|
||||
_BidirectionalIterator __last,
|
||||
@ -122,7 +122,7 @@ _LIBCPP_HIDE_FROM_ABI void __buffered_inplace_merge(
|
||||
}
|
||||
|
||||
template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
|
||||
void __inplace_merge(
|
||||
_LIBCPP_CONSTEXPR_SINCE_CXX26 void __inplace_merge(
|
||||
_BidirectionalIterator __first,
|
||||
_BidirectionalIterator __middle,
|
||||
_BidirectionalIterator __last,
|
||||
|
@ -240,7 +240,7 @@ __selection_sort(_BidirectionalIterator __first, _BidirectionalIterator __last,
|
||||
// Sort the iterator range [__first, __last) using the comparator __comp using
|
||||
// the insertion sort algorithm.
|
||||
template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
|
||||
_LIBCPP_HIDE_FROM_ABI void
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
|
||||
__insertion_sort(_BidirectionalIterator __first, _BidirectionalIterator __last, _Compare __comp) {
|
||||
using _Ops = _IterOps<_AlgPolicy>;
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <__cstddef/ptrdiff_t.h>
|
||||
#include <__debug_utils/strict_weak_ordering_check.h>
|
||||
#include <__iterator/iterator_traits.h>
|
||||
#include <__memory/construct_at.h>
|
||||
#include <__memory/destruct_n.h>
|
||||
#include <__memory/unique_ptr.h>
|
||||
#include <__memory/unique_temporary_buffer.h>
|
||||
@ -41,7 +42,7 @@ _LIBCPP_PUSH_MACROS
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
|
||||
_LIBCPP_HIDE_FROM_ABI void __insertion_sort_move(
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insertion_sort_move(
|
||||
_BidirectionalIterator __first1,
|
||||
_BidirectionalIterator __last1,
|
||||
typename iterator_traits<_BidirectionalIterator>::value_type* __first2,
|
||||
@ -53,19 +54,19 @@ _LIBCPP_HIDE_FROM_ABI void __insertion_sort_move(
|
||||
__destruct_n __d(0);
|
||||
unique_ptr<value_type, __destruct_n&> __h(__first2, __d);
|
||||
value_type* __last2 = __first2;
|
||||
::new ((void*)__last2) value_type(_Ops::__iter_move(__first1));
|
||||
std::__construct_at(__last2, _Ops::__iter_move(__first1));
|
||||
__d.template __incr<value_type>();
|
||||
for (++__last2; ++__first1 != __last1; ++__last2) {
|
||||
value_type* __j2 = __last2;
|
||||
value_type* __i2 = __j2;
|
||||
if (__comp(*__first1, *--__i2)) {
|
||||
::new ((void*)__j2) value_type(std::move(*__i2));
|
||||
std::__construct_at(__j2, std::move(*__i2));
|
||||
__d.template __incr<value_type>();
|
||||
for (--__j2; __i2 != __first2 && __comp(*__first1, *--__i2); --__j2)
|
||||
*__j2 = std::move(*__i2);
|
||||
*__j2 = _Ops::__iter_move(__first1);
|
||||
} else {
|
||||
::new ((void*)__j2) value_type(_Ops::__iter_move(__first1));
|
||||
std::__construct_at(__j2, _Ops::__iter_move(__first1));
|
||||
__d.template __incr<value_type>();
|
||||
}
|
||||
}
|
||||
@ -74,7 +75,7 @@ _LIBCPP_HIDE_FROM_ABI void __insertion_sort_move(
|
||||
}
|
||||
|
||||
template <class _AlgPolicy, class _Compare, class _InputIterator1, class _InputIterator2>
|
||||
_LIBCPP_HIDE_FROM_ABI void __merge_move_construct(
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __merge_move_construct(
|
||||
_InputIterator1 __first1,
|
||||
_InputIterator1 __last1,
|
||||
_InputIterator2 __first2,
|
||||
@ -89,22 +90,22 @@ _LIBCPP_HIDE_FROM_ABI void __merge_move_construct(
|
||||
for (; true; ++__result) {
|
||||
if (__first1 == __last1) {
|
||||
for (; __first2 != __last2; ++__first2, (void)++__result, __d.template __incr<value_type>())
|
||||
::new ((void*)__result) value_type(_Ops::__iter_move(__first2));
|
||||
std::__construct_at(__result, _Ops::__iter_move(__first2));
|
||||
__h.release();
|
||||
return;
|
||||
}
|
||||
if (__first2 == __last2) {
|
||||
for (; __first1 != __last1; ++__first1, (void)++__result, __d.template __incr<value_type>())
|
||||
::new ((void*)__result) value_type(_Ops::__iter_move(__first1));
|
||||
std::__construct_at(__result, _Ops::__iter_move(__first1));
|
||||
__h.release();
|
||||
return;
|
||||
}
|
||||
if (__comp(*__first2, *__first1)) {
|
||||
::new ((void*)__result) value_type(_Ops::__iter_move(__first2));
|
||||
std::__construct_at(__result, _Ops::__iter_move(__first2));
|
||||
__d.template __incr<value_type>();
|
||||
++__first2;
|
||||
} else {
|
||||
::new ((void*)__result) value_type(_Ops::__iter_move(__first1));
|
||||
std::__construct_at(__result, _Ops::__iter_move(__first1));
|
||||
__d.template __incr<value_type>();
|
||||
++__first1;
|
||||
}
|
||||
@ -112,7 +113,7 @@ _LIBCPP_HIDE_FROM_ABI void __merge_move_construct(
|
||||
}
|
||||
|
||||
template <class _AlgPolicy, class _Compare, class _InputIterator1, class _InputIterator2, class _OutputIterator>
|
||||
_LIBCPP_HIDE_FROM_ABI void __merge_move_assign(
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __merge_move_assign(
|
||||
_InputIterator1 __first1,
|
||||
_InputIterator1 __last1,
|
||||
_InputIterator2 __first2,
|
||||
@ -140,19 +141,21 @@ _LIBCPP_HIDE_FROM_ABI void __merge_move_assign(
|
||||
}
|
||||
|
||||
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
|
||||
void __stable_sort(_RandomAccessIterator __first,
|
||||
_RandomAccessIterator __last,
|
||||
_Compare __comp,
|
||||
typename iterator_traits<_RandomAccessIterator>::difference_type __len,
|
||||
typename iterator_traits<_RandomAccessIterator>::value_type* __buff,
|
||||
ptrdiff_t __buff_size);
|
||||
_LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort(
|
||||
_RandomAccessIterator __first,
|
||||
_RandomAccessIterator __last,
|
||||
_Compare __comp,
|
||||
typename iterator_traits<_RandomAccessIterator>::difference_type __len,
|
||||
typename iterator_traits<_RandomAccessIterator>::value_type* __buff,
|
||||
ptrdiff_t __buff_size);
|
||||
|
||||
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
|
||||
void __stable_sort_move(_RandomAccessIterator __first1,
|
||||
_RandomAccessIterator __last1,
|
||||
_Compare __comp,
|
||||
typename iterator_traits<_RandomAccessIterator>::difference_type __len,
|
||||
typename iterator_traits<_RandomAccessIterator>::value_type* __first2) {
|
||||
_LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort_move(
|
||||
_RandomAccessIterator __first1,
|
||||
_RandomAccessIterator __last1,
|
||||
_Compare __comp,
|
||||
typename iterator_traits<_RandomAccessIterator>::difference_type __len,
|
||||
typename iterator_traits<_RandomAccessIterator>::value_type* __first2) {
|
||||
using _Ops = _IterOps<_AlgPolicy>;
|
||||
|
||||
typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
|
||||
@ -160,21 +163,21 @@ void __stable_sort_move(_RandomAccessIterator __first1,
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
::new ((void*)__first2) value_type(_Ops::__iter_move(__first1));
|
||||
std::__construct_at(__first2, _Ops::__iter_move(__first1));
|
||||
return;
|
||||
case 2:
|
||||
__destruct_n __d(0);
|
||||
unique_ptr<value_type, __destruct_n&> __h2(__first2, __d);
|
||||
if (__comp(*--__last1, *__first1)) {
|
||||
::new ((void*)__first2) value_type(_Ops::__iter_move(__last1));
|
||||
std::__construct_at(__first2, _Ops::__iter_move(__last1));
|
||||
__d.template __incr<value_type>();
|
||||
++__first2;
|
||||
::new ((void*)__first2) value_type(_Ops::__iter_move(__first1));
|
||||
std::__construct_at(__first2, _Ops::__iter_move(__first1));
|
||||
} else {
|
||||
::new ((void*)__first2) value_type(_Ops::__iter_move(__first1));
|
||||
std::__construct_at(__first2, _Ops::__iter_move(__first1));
|
||||
__d.template __incr<value_type>();
|
||||
++__first2;
|
||||
::new ((void*)__first2) value_type(_Ops::__iter_move(__last1));
|
||||
std::__construct_at(__first2, _Ops::__iter_move(__last1));
|
||||
}
|
||||
__h2.release();
|
||||
return;
|
||||
@ -218,12 +221,13 @@ _LIBCPP_HIDE_FROM_ABI constexpr unsigned __radix_sort_max_bound() {
|
||||
#endif // _LIBCPP_STD_VER >= 17
|
||||
|
||||
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
|
||||
void __stable_sort(_RandomAccessIterator __first,
|
||||
_RandomAccessIterator __last,
|
||||
_Compare __comp,
|
||||
typename iterator_traits<_RandomAccessIterator>::difference_type __len,
|
||||
typename iterator_traits<_RandomAccessIterator>::value_type* __buff,
|
||||
ptrdiff_t __buff_size) {
|
||||
_LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort(
|
||||
_RandomAccessIterator __first,
|
||||
_RandomAccessIterator __last,
|
||||
_Compare __comp,
|
||||
typename iterator_traits<_RandomAccessIterator>::difference_type __len,
|
||||
typename iterator_traits<_RandomAccessIterator>::value_type* __buff,
|
||||
ptrdiff_t __buff_size) {
|
||||
typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
|
||||
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
|
||||
switch (__len) {
|
||||
@ -279,7 +283,7 @@ void __stable_sort(_RandomAccessIterator __first,
|
||||
}
|
||||
|
||||
template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>
|
||||
inline _LIBCPP_HIDE_FROM_ABI void
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
|
||||
__stable_sort_impl(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare& __comp) {
|
||||
using value_type = typename iterator_traits<_RandomAccessIterator>::value_type;
|
||||
using difference_type = typename iterator_traits<_RandomAccessIterator>::difference_type;
|
||||
@ -298,18 +302,18 @@ __stable_sort_impl(_RandomAccessIterator __first, _RandomAccessIterator __last,
|
||||
}
|
||||
|
||||
template <class _RandomAccessIterator, class _Compare>
|
||||
inline _LIBCPP_HIDE_FROM_ABI void
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
|
||||
stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp) {
|
||||
std::__stable_sort_impl<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __comp);
|
||||
}
|
||||
|
||||
template <class _RandomAccessIterator>
|
||||
inline _LIBCPP_HIDE_FROM_ABI void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
|
||||
stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {
|
||||
std::stable_sort(__first, __last, __less<>());
|
||||
}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
_LIBCPP_POP_MACROS
|
||||
|
||||
#endif // _LIBCPP___ALGORITHM_STABLE_SORT_H
|
||||
|
@ -25,35 +25,35 @@ private:
|
||||
size_t __size_;
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI void __process(_Tp* __p, false_type) _NOEXCEPT {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __process(_Tp* __p, false_type) _NOEXCEPT {
|
||||
for (size_t __i = 0; __i < __size_; ++__i, ++__p)
|
||||
__p->~_Tp();
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI void __process(_Tp*, true_type) _NOEXCEPT {}
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __process(_Tp*, true_type) _NOEXCEPT {}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI void __incr(false_type) _NOEXCEPT { ++__size_; }
|
||||
_LIBCPP_HIDE_FROM_ABI void __incr(true_type) _NOEXCEPT {}
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __incr(false_type) _NOEXCEPT { ++__size_; }
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __incr(true_type) _NOEXCEPT {}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI void __set(size_t __s, false_type) _NOEXCEPT { __size_ = __s; }
|
||||
_LIBCPP_HIDE_FROM_ABI void __set(size_t, true_type) _NOEXCEPT {}
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set(size_t __s, false_type) _NOEXCEPT { __size_ = __s; }
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set(size_t, true_type) _NOEXCEPT {}
|
||||
|
||||
public:
|
||||
_LIBCPP_HIDE_FROM_ABI explicit __destruct_n(size_t __s) _NOEXCEPT : __size_(__s) {}
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __destruct_n(size_t __s) _NOEXCEPT : __size_(__s) {}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI void __incr() _NOEXCEPT {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __incr() _NOEXCEPT {
|
||||
__incr(integral_constant<bool, is_trivially_destructible<_Tp>::value>());
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI void __set(size_t __s, _Tp*) _NOEXCEPT {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set(size_t __s, _Tp*) _NOEXCEPT {
|
||||
__set(__s, integral_constant<bool, is_trivially_destructible<_Tp>::value>());
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI void operator()(_Tp* __p) _NOEXCEPT {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(_Tp* __p) _NOEXCEPT {
|
||||
__process(__p, integral_constant<bool, is_trivially_destructible<_Tp>::value>());
|
||||
}
|
||||
};
|
||||
|
@ -1530,11 +1530,11 @@ template <class RandomAccessIterator, class Compare>
|
||||
sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
|
||||
|
||||
template <class RandomAccessIterator>
|
||||
void
|
||||
constexpr void // constexpr in C++26
|
||||
stable_sort(RandomAccessIterator first, RandomAccessIterator last);
|
||||
|
||||
template <class RandomAccessIterator, class Compare>
|
||||
void
|
||||
constexpr void // constexpr in C++26
|
||||
stable_sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
|
||||
|
||||
template <class RandomAccessIterator>
|
||||
|
@ -819,7 +819,10 @@ module std [system] {
|
||||
module sort_heap { header "__algorithm/sort_heap.h" }
|
||||
module sort { header "__algorithm/sort.h" }
|
||||
module stable_partition { header "__algorithm/stable_partition.h" }
|
||||
module stable_sort { header "__algorithm/stable_sort.h" }
|
||||
module stable_sort {
|
||||
header "__algorithm/stable_sort.h"
|
||||
export std.memory.unique_temporary_buffer // TODO: Workaround for https://github.com/llvm/llvm-project/issues/120108
|
||||
}
|
||||
module swap_ranges { header "__algorithm/swap_ranges.h" }
|
||||
module three_way_comp_ref_type { header "__algorithm/three_way_comp_ref_type.h" }
|
||||
module transform { header "__algorithm/transform.h" }
|
||||
|
@ -8,11 +8,12 @@
|
||||
|
||||
// <algorithm>
|
||||
|
||||
// template<RandomAccessIterator Iter>
|
||||
// requires ShuffleIterator<Iter>
|
||||
// && LessThanComparable<Iter::value_type>
|
||||
// void
|
||||
// stable_sort(Iter first, Iter last);
|
||||
// template <class RandomAccessIterator>
|
||||
// constexpr void // constexpr since C++26
|
||||
// stable_sort(RandomAccessIterator first, RandomAccessIterator last);
|
||||
|
||||
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=200000000
|
||||
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=200000000
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
@ -23,156 +24,181 @@
|
||||
#include "count_new.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
std::mt19937 randomness;
|
||||
template <class Iterator>
|
||||
TEST_CONSTEXPR_CXX26 void test_all_permutations(Iterator first, Iterator last) {
|
||||
using T = typename std::iterator_traits<Iterator>::value_type;
|
||||
|
||||
template <class RI>
|
||||
void
|
||||
test_sort_helper(RI f, RI l)
|
||||
{
|
||||
typedef typename std::iterator_traits<RI>::value_type value_type;
|
||||
typedef typename std::iterator_traits<RI>::difference_type difference_type;
|
||||
|
||||
if (f != l)
|
||||
{
|
||||
difference_type len = l - f;
|
||||
value_type* save(new value_type[len]);
|
||||
do
|
||||
{
|
||||
std::copy(f, l, save);
|
||||
std::stable_sort(save, save+len);
|
||||
assert(std::is_sorted(save, save+len));
|
||||
} while (std::next_permutation(f, l));
|
||||
delete [] save;
|
||||
}
|
||||
do {
|
||||
std::vector<T> save(first, last);
|
||||
std::stable_sort(save.begin(), save.end());
|
||||
assert(std::is_sorted(save.begin(), save.end()));
|
||||
} while (std::next_permutation(first, last));
|
||||
}
|
||||
|
||||
template <class RI>
|
||||
void
|
||||
test_sort_driver_driver(RI f, RI l, int start, RI real_last)
|
||||
{
|
||||
using value_type = typename std::iterator_traits<RI>::value_type;
|
||||
template <class Iterator>
|
||||
TEST_CONSTEXPR_CXX26 void test_sort_exhaustive_impl(Iterator first, Iterator last, int start, Iterator real_last) {
|
||||
using T = typename std::iterator_traits<Iterator>::value_type;
|
||||
|
||||
for (RI i = l; i > f + start;) {
|
||||
*--i = static_cast<value_type>(start);
|
||||
if (f == i) {
|
||||
test_sort_helper(f, real_last);
|
||||
for (Iterator i = last; i > first + start;) {
|
||||
*--i = static_cast<T>(start);
|
||||
if (first == i) {
|
||||
test_all_permutations(first, real_last);
|
||||
}
|
||||
if (start > 0)
|
||||
test_sort_driver_driver(f, i, start-1, real_last);
|
||||
test_sort_exhaustive_impl(first, i, start - 1, real_last);
|
||||
}
|
||||
}
|
||||
|
||||
template <class RI>
|
||||
void
|
||||
test_sort_driver(RI f, RI l, int start)
|
||||
{
|
||||
test_sort_driver_driver(f, l, start, l);
|
||||
}
|
||||
|
||||
template <int sa, class V>
|
||||
void test_sort_() {
|
||||
V ia[sa];
|
||||
for (int i = 0; i < sa; ++i) {
|
||||
test_sort_driver(ia, ia + sa, i);
|
||||
}
|
||||
}
|
||||
|
||||
template <int sa>
|
||||
void test_sort_() {
|
||||
test_sort_<sa, int>();
|
||||
test_sort_<sa, float>();
|
||||
}
|
||||
|
||||
template <class V>
|
||||
void test_larger_sorts(int N, int M) {
|
||||
assert(N != 0);
|
||||
assert(M != 0);
|
||||
// create array length N filled with M different numbers
|
||||
V* array = new V[N];
|
||||
int x = 0;
|
||||
template <class T>
|
||||
TEST_CONSTEXPR_CXX26 void test_sort_exhaustive(int N) {
|
||||
std::vector<T> vec;
|
||||
vec.resize(N);
|
||||
for (int i = 0; i < N; ++i) {
|
||||
array[i] = static_cast<V>(x);
|
||||
test_sort_exhaustive_impl(vec.begin(), vec.end(), i, vec.end());
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
TEST_CONSTEXPR_CXX26 std::vector<T> generate_sawtooth(int N, int M) {
|
||||
// Populate a sequence of length N with M different numbers
|
||||
std::vector<T> v;
|
||||
T x = 0;
|
||||
for (int i = 0; i < N; ++i) {
|
||||
v.push_back(x);
|
||||
if (++x == M)
|
||||
x = 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
TEST_CONSTEXPR_CXX26 void test_larger_sorts(int N, int M) {
|
||||
assert(N != 0);
|
||||
assert(M != 0);
|
||||
|
||||
// test saw tooth pattern
|
||||
std::stable_sort(array, array + N);
|
||||
assert(std::is_sorted(array, array + N));
|
||||
{
|
||||
auto v = generate_sawtooth<T>(N, M);
|
||||
std::stable_sort(v.begin(), v.end());
|
||||
assert(std::is_sorted(v.begin(), v.end()));
|
||||
}
|
||||
|
||||
// test random pattern
|
||||
std::shuffle(array, array + N, randomness);
|
||||
std::stable_sort(array, array + N);
|
||||
assert(std::is_sorted(array, array + N));
|
||||
// test sorted pattern
|
||||
std::stable_sort(array, array + N);
|
||||
assert(std::is_sorted(array, array + N));
|
||||
// test reverse sorted pattern
|
||||
std::reverse(array, array + N);
|
||||
std::stable_sort(array, array + N);
|
||||
assert(std::is_sorted(array, array + N));
|
||||
// test swap ranges 2 pattern
|
||||
std::swap_ranges(array, array + N / 2, array + N / 2);
|
||||
std::stable_sort(array, array + N);
|
||||
assert(std::is_sorted(array, array + N));
|
||||
// test reverse swap ranges 2 pattern
|
||||
std::reverse(array, array + N);
|
||||
std::swap_ranges(array, array + N / 2, array + N / 2);
|
||||
std::stable_sort(array, array + N);
|
||||
assert(std::is_sorted(array, array + N));
|
||||
delete[] array;
|
||||
}
|
||||
|
||||
void test_larger_sorts(int N, int M) {
|
||||
test_larger_sorts<int>(N, M);
|
||||
test_larger_sorts<float>(N, M);
|
||||
}
|
||||
|
||||
void
|
||||
test_larger_sorts(int N)
|
||||
{
|
||||
test_larger_sorts(N, 1);
|
||||
test_larger_sorts(N, 2);
|
||||
test_larger_sorts(N, 3);
|
||||
test_larger_sorts(N, N/2-1);
|
||||
test_larger_sorts(N, N/2);
|
||||
test_larger_sorts(N, N/2+1);
|
||||
test_larger_sorts(N, N-2);
|
||||
test_larger_sorts(N, N-1);
|
||||
test_larger_sorts(N, N);
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
// test null range
|
||||
int d = 0;
|
||||
std::stable_sort(&d, &d);
|
||||
// exhaustively test all possibilities up to length 8
|
||||
test_sort_<1>();
|
||||
test_sort_<2>();
|
||||
test_sort_<3>();
|
||||
test_sort_<4>();
|
||||
test_sort_<5>();
|
||||
test_sort_<6>();
|
||||
test_sort_<7>();
|
||||
test_sort_<8>();
|
||||
|
||||
test_larger_sorts(256);
|
||||
test_larger_sorts(257);
|
||||
test_larger_sorts(499);
|
||||
test_larger_sorts(500);
|
||||
test_larger_sorts(997);
|
||||
test_larger_sorts(1000);
|
||||
test_larger_sorts(1009);
|
||||
test_larger_sorts(1024);
|
||||
test_larger_sorts(1031);
|
||||
test_larger_sorts(2053);
|
||||
|
||||
#if !defined(TEST_HAS_NO_EXCEPTIONS)
|
||||
{ // check that the algorithm works without memory
|
||||
std::vector<int> vec(150, 3);
|
||||
getGlobalMemCounter()->throw_after = 0;
|
||||
std::stable_sort(vec.begin(), vec.end());
|
||||
{
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
auto v = generate_sawtooth<T>(N, M);
|
||||
std::mt19937 randomness;
|
||||
std::shuffle(v.begin(), v.end(), randomness);
|
||||
std::stable_sort(v.begin(), v.end());
|
||||
assert(std::is_sorted(v.begin(), v.end()));
|
||||
}
|
||||
#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
|
||||
}
|
||||
|
||||
// test sorted pattern
|
||||
{
|
||||
auto v = generate_sawtooth<T>(N, M);
|
||||
std::sort(v.begin(), v.end());
|
||||
|
||||
std::stable_sort(v.begin(), v.end());
|
||||
assert(std::is_sorted(v.begin(), v.end()));
|
||||
}
|
||||
|
||||
// test reverse sorted pattern
|
||||
{
|
||||
auto v = generate_sawtooth<T>(N, M);
|
||||
std::sort(v.begin(), v.end());
|
||||
std::reverse(v.begin(), v.end());
|
||||
|
||||
std::stable_sort(v.begin(), v.end());
|
||||
assert(std::is_sorted(v.begin(), v.end()));
|
||||
}
|
||||
|
||||
// test swap ranges 2 pattern
|
||||
{
|
||||
auto v = generate_sawtooth<T>(N, M);
|
||||
std::sort(v.begin(), v.end());
|
||||
std::swap_ranges(v.begin(), v.begin() + (N / 2), v.begin() + (N / 2));
|
||||
|
||||
std::stable_sort(v.begin(), v.end());
|
||||
assert(std::is_sorted(v.begin(), v.end()));
|
||||
}
|
||||
|
||||
// test reverse swap ranges 2 pattern
|
||||
{
|
||||
auto v = generate_sawtooth<T>(N, M);
|
||||
std::sort(v.begin(), v.end());
|
||||
std::reverse(v.begin(), v.end());
|
||||
std::swap_ranges(v.begin(), v.begin() + (N / 2), v.begin() + (N / 2));
|
||||
|
||||
std::stable_sort(v.begin(), v.end());
|
||||
assert(std::is_sorted(v.begin(), v.end()));
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
TEST_CONSTEXPR_CXX26 void test_larger_sorts(int N) {
|
||||
test_larger_sorts<T>(N, 1);
|
||||
test_larger_sorts<T>(N, 2);
|
||||
test_larger_sorts<T>(N, 3);
|
||||
test_larger_sorts<T>(N, N / 2 - 1);
|
||||
test_larger_sorts<T>(N, N / 2);
|
||||
test_larger_sorts<T>(N, N / 2 + 1);
|
||||
test_larger_sorts<T>(N, N - 2);
|
||||
test_larger_sorts<T>(N, N - 1);
|
||||
test_larger_sorts<T>(N, N);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
TEST_CONSTEXPR_CXX26 bool test() {
|
||||
// test null range
|
||||
{
|
||||
T value = 0;
|
||||
std::stable_sort(&value, &value);
|
||||
}
|
||||
|
||||
// exhaustively test all possibilities up to length 8
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
test_sort_exhaustive<T>(1);
|
||||
test_sort_exhaustive<T>(2);
|
||||
test_sort_exhaustive<T>(3);
|
||||
test_sort_exhaustive<T>(4);
|
||||
test_sort_exhaustive<T>(5);
|
||||
test_sort_exhaustive<T>(6);
|
||||
test_sort_exhaustive<T>(7);
|
||||
test_sort_exhaustive<T>(8);
|
||||
}
|
||||
|
||||
test_larger_sorts<T>(256);
|
||||
test_larger_sorts<T>(257);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) { // avoid blowing past constexpr evaluation limit
|
||||
test_larger_sorts<T>(499);
|
||||
test_larger_sorts<T>(500);
|
||||
test_larger_sorts<T>(997);
|
||||
test_larger_sorts<T>(1000);
|
||||
test_larger_sorts<T>(1009);
|
||||
test_larger_sorts<T>(1024);
|
||||
test_larger_sorts<T>(1031);
|
||||
test_larger_sorts<T>(2053);
|
||||
}
|
||||
|
||||
// check that the algorithm works without memory
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
std::vector<T> vec(150, T(3));
|
||||
getGlobalMemCounter()->throw_after = 0;
|
||||
std::stable_sort(vec.begin(), vec.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test<int>();
|
||||
test<float>();
|
||||
#if TEST_STD_VER >= 26
|
||||
static_assert(test<int>());
|
||||
static_assert(test<float>());
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -9,10 +9,11 @@
|
||||
// <algorithm>
|
||||
|
||||
// template<RandomAccessIterator Iter, StrictWeakOrder<auto, Iter::value_type> Compare>
|
||||
// requires ShuffleIterator<Iter>
|
||||
// && CopyConstructible<Compare>
|
||||
// void
|
||||
// stable_sort(Iter first, Iter last, Compare comp);
|
||||
// requires ShuffleIterator<Iter> && CopyConstructible<Compare>
|
||||
// constexpr void stable_sort(Iter first, Iter last, Compare comp); // constexpr since C++26
|
||||
//
|
||||
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=200000000
|
||||
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=200000000
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
@ -27,57 +28,79 @@
|
||||
|
||||
struct indirect_less {
|
||||
template <class P>
|
||||
bool operator()(const P& x, const P& y) const {
|
||||
TEST_CONSTEXPR_CXX26 bool operator()(const P& x, const P& y) const {
|
||||
return *x < *y;
|
||||
}
|
||||
};
|
||||
|
||||
std::mt19937 randomness;
|
||||
|
||||
struct first_only {
|
||||
bool operator()(const std::pair<int, int>& x, const std::pair<int, int>& y) const { return x.first < y.first; }
|
||||
TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& x, const std::pair<int, int>& y) const {
|
||||
return x.first < y.first;
|
||||
}
|
||||
};
|
||||
|
||||
void test()
|
||||
{
|
||||
typedef std::pair<int, int> P;
|
||||
const int N = 1000;
|
||||
const int M = 10;
|
||||
std::vector<P> v(N);
|
||||
int x = 0;
|
||||
int ver = 0;
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
v[i] = P(x, ver);
|
||||
if (++x == M)
|
||||
{
|
||||
x = 0;
|
||||
++ver;
|
||||
}
|
||||
using Pair = std::pair<int, int>;
|
||||
|
||||
TEST_CONSTEXPR_CXX26 std::vector<Pair> generate_sawtooth(int N, int M) {
|
||||
std::vector<Pair> v(N);
|
||||
int x = 0;
|
||||
int ver = 0;
|
||||
for (int i = 0; i < N; ++i) {
|
||||
v[i] = Pair(x, ver);
|
||||
if (++x == M) {
|
||||
x = 0;
|
||||
++ver;
|
||||
}
|
||||
for (int i = 0; i < N - M; i += M)
|
||||
{
|
||||
std::shuffle(v.begin() + i, v.begin() + i + M, randomness);
|
||||
}
|
||||
std::stable_sort(v.begin(), v.end(), first_only());
|
||||
assert(std::is_sorted(v.begin(), v.end()));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
test();
|
||||
TEST_CONSTEXPR_CXX26 bool test() {
|
||||
int const N = 1000;
|
||||
int const M = 10;
|
||||
|
||||
// test sawtooth pattern
|
||||
{
|
||||
auto v = generate_sawtooth(N, M);
|
||||
std::stable_sort(v.begin(), v.end(), first_only());
|
||||
assert(std::is_sorted(v.begin(), v.end()));
|
||||
}
|
||||
|
||||
// Test sorting a sequence where subsequences of elements are not sorted with <,
|
||||
// but everything is already sorted with respect to the first element. This ensures
|
||||
// that we don't change the order of "equivalent" elements.
|
||||
{
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
auto v = generate_sawtooth(N, M);
|
||||
std::mt19937 randomness;
|
||||
for (int i = 0; i < N - M; i += M) {
|
||||
std::shuffle(v.begin() + i, v.begin() + i + M, randomness);
|
||||
}
|
||||
std::stable_sort(v.begin(), v.end(), first_only());
|
||||
assert(std::is_sorted(v.begin(), v.end()));
|
||||
}
|
||||
}
|
||||
|
||||
#if TEST_STD_VER >= 11
|
||||
{
|
||||
{
|
||||
std::vector<std::unique_ptr<int> > v(1000);
|
||||
for (int i = 0; static_cast<std::size_t>(i) < v.size(); ++i)
|
||||
v[i].reset(new int(i));
|
||||
v[i].reset(new int(i));
|
||||
std::stable_sort(v.begin(), v.end(), indirect_less());
|
||||
assert(std::is_sorted(v.begin(), v.end(), indirect_less()));
|
||||
assert(*v[0] == 0);
|
||||
assert(*v[1] == 1);
|
||||
assert(*v[2] == 2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
#if TEST_STD_VER >= 26
|
||||
static_assert(test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user