[libc++][ranges] implement `std::ranges::inplace_merge`

Differential Revision: https://reviews.llvm.org/D130627
This commit is contained in:
Hui Xie 2022-07-27 13:20:16 +01:00
parent 1dc26b80b8
commit 8a61749f76
17 changed files with 485 additions and 102 deletions

View File

@ -77,7 +77,7 @@ Permutation,sort,Konstantin Varlamov,`D127557 <https://llvm.org/D127557>`_,✅
Permutation,stable_sort,Konstantin Varlamov,`D127834 <https://llvm.org/D127834>`_,✅
Permutation,nth_element,Konstantin Varlamov,`D128149 <https://llvm.org/D128149>`_,✅
Permutation,partial_sort,Konstantin Varlamov,`D128744 <https://llvm.org/D128744>`_,✅
Permutation,inplace_merge,Not assigned,n/a,Not started
Permutation,inplace_merge,Hui Xie,`D130627 <https://llvm.org/D130627>`_,✅
Permutation,make_heap,Konstantin Varlamov,`D128115 <https://llvm.org/D128115>`_,✅
Permutation,push_heap,Konstantin Varlamov,`D128115 <https://llvm.org/D128115>`_,✅
Permutation,pop_heap,Konstantin Varlamov,`D128115 <https://llvm.org/D128115>`_,✅

1 Category Algorithm Assignee CL Complete
77 Permutation stable_sort Konstantin Varlamov `D127834 <https://llvm.org/D127834>`_
78 Permutation nth_element Konstantin Varlamov `D128149 <https://llvm.org/D128149>`_
79 Permutation partial_sort Konstantin Varlamov `D128744 <https://llvm.org/D128744>`_
80 Permutation inplace_merge Not assigned Hui Xie n/a `D130627 <https://llvm.org/D130627>`_ Not started
81 Permutation make_heap Konstantin Varlamov `D128115 <https://llvm.org/D128115>`_
82 Permutation push_heap Konstantin Varlamov `D128115 <https://llvm.org/D128115>`_
83 Permutation pop_heap Konstantin Varlamov `D128115 <https://llvm.org/D128115>`_

View File

@ -1,5 +1,6 @@
set(files
__algorithm/adjacent_find.h
__algorithm/algorithm_family.h
__algorithm/all_of.h
__algorithm/any_of.h
__algorithm/binary_search.h

View File

@ -0,0 +1,52 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___ALGORITHM_ALGORITHM_FAMILY_H
#define _LIBCPP___ALGORITHM_ALGORITHM_FAMILY_H
#include <__algorithm/iterator_operations.h>
#include <__algorithm/move.h>
#include <__algorithm/ranges_move.h>
#include <__config>
#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _AlgPolicy>
struct _AlgFamily;
#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
template <>
struct _AlgFamily<_RangeAlgPolicy> {
static constexpr auto __move = ranges::move;
};
#endif
template <>
struct _AlgFamily<_ClassicAlgPolicy> {
// move
template <class _InputIterator, class _OutputIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 static _OutputIterator
__move(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
return std::move(
std::move(__first),
std::move(__last),
std::move(__result));
}
};
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___ALGORITHM_ALGORITHM_FAMILY_H

View File

@ -9,6 +9,7 @@
#ifndef _LIBCPP___ALGORITHM_INPLACE_MERGE_H
#define _LIBCPP___ALGORITHM_INPLACE_MERGE_H
#include <__algorithm/algorithm_family.h>
#include <__algorithm/comp.h>
#include <__algorithm/comp_ref_type.h>
#include <__algorithm/iterator_operations.h>
@ -54,18 +55,17 @@ public:
bool operator()(const _T1& __x, const _T2& __y) {return __p_(__y, __x);}
};
template <class _AlgPolicy, class _Compare, class _InputIterator1, class _InputIterator2,
class _OutputIterator>
void __half_inplace_merge(_InputIterator1 __first1, _InputIterator1 __last1,
_InputIterator2 __first2, _InputIterator2 __last2,
_OutputIterator __result, _Compare __comp)
template <class _AlgPolicy, class _Compare, class _InputIterator1, class _Sent1,
class _InputIterator2, class _Sent2, class _OutputIterator>
void __half_inplace_merge(_InputIterator1 __first1, _Sent1 __last1,
_InputIterator2 __first2, _Sent2 __last2,
_OutputIterator __result, _Compare&& __comp)
{
for (; __first1 != __last1; ++__result)
{
if (__first2 == __last2)
{
// TODO(alg-policy): pass `_AlgPolicy` once it's supported by `move`.
_VSTD::move(__first1, __last1, __result);
_AlgFamily<_AlgPolicy>::__move(__first1, __last1, __result);
return;
}
@ -84,13 +84,15 @@ void __half_inplace_merge(_InputIterator1 __first1, _InputIterator1 __last1,
}
template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
void
__buffered_inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
_Compare __comp, typename iterator_traits<_BidirectionalIterator>::difference_type __len1,
typename iterator_traits<_BidirectionalIterator>::difference_type __len2,
typename iterator_traits<_BidirectionalIterator>::value_type* __buff)
{
typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
void __buffered_inplace_merge(
_BidirectionalIterator __first,
_BidirectionalIterator __middle,
_BidirectionalIterator __last,
_Compare&& __comp,
typename iterator_traits<_BidirectionalIterator>::difference_type __len1,
typename iterator_traits<_BidirectionalIterator>::difference_type __len2,
typename iterator_traits<_BidirectionalIterator>::value_type* __buff) {
typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
__destruct_n __d(0);
unique_ptr<value_type, __destruct_n&> __h2(__buff, __d);
if (__len1 <= __len2)
@ -98,7 +100,7 @@ __buffered_inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator
value_type* __p = __buff;
for (_BidirectionalIterator __i = __first; __i != __middle; __d.template __incr<value_type>(), (void) ++__i, (void) ++__p)
::new ((void*)__p) value_type(_IterOps<_AlgPolicy>::__iter_move(__i));
std::__half_inplace_merge<_AlgPolicy, _Compare>(__buff, __p, __middle, __last, __first, __comp);
std::__half_inplace_merge<_AlgPolicy>(__buff, __p, __middle, __last, __first, __comp);
}
else
{
@ -108,19 +110,22 @@ __buffered_inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator
typedef __unconstrained_reverse_iterator<_BidirectionalIterator> _RBi;
typedef __unconstrained_reverse_iterator<value_type*> _Rv;
typedef __invert<_Compare> _Inverted;
std::__half_inplace_merge<_AlgPolicy, _Inverted>(_Rv(__p), _Rv(__buff),
std::__half_inplace_merge<_AlgPolicy>(_Rv(__p), _Rv(__buff),
_RBi(__middle), _RBi(__first),
_RBi(__last), _Inverted(__comp));
}
}
template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
void
__inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
_Compare __comp, typename iterator_traits<_BidirectionalIterator>::difference_type __len1,
typename iterator_traits<_BidirectionalIterator>::difference_type __len2,
typename iterator_traits<_BidirectionalIterator>::value_type* __buff, ptrdiff_t __buff_size)
{
void __inplace_merge(
_BidirectionalIterator __first,
_BidirectionalIterator __middle,
_BidirectionalIterator __last,
_Compare&& __comp,
typename iterator_traits<_BidirectionalIterator>::difference_type __len1,
typename iterator_traits<_BidirectionalIterator>::difference_type __len2,
typename iterator_traits<_BidirectionalIterator>::value_type* __buff,
ptrdiff_t __buff_size) {
using _Ops = _IterOps<_AlgPolicy>;
typedef typename iterator_traits<_BidirectionalIterator>::difference_type difference_type;
@ -130,7 +135,7 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
if (__len2 == 0)
return;
if (__len1 <= __buff_size || __len2 <= __buff_size)
return std::__buffered_inplace_merge<_AlgPolicy, _Compare>
return std::__buffered_inplace_merge<_AlgPolicy>
(__first, __middle, __last, __comp, __len1, __len2, __buff);
// shrink [__first, __middle) as much as possible (with no moves), returning if it shrinks to 0
for (; true; ++__first, (void) --__len1)
@ -158,8 +163,7 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
__len21 = __len2 / 2;
__m2 = __middle;
_Ops::advance(__m2, __len21);
// TODO: replace _ClassicAlgPolicy and __identity with _AlgPolicy and projection
__m1 = std::__upper_bound<_ClassicAlgPolicy>(__first, __middle, *__m2, __comp, std::__identity());
__m1 = std::__upper_bound<_AlgPolicy>(__first, __middle, *__m2, __comp, std::__identity());
__len11 = _Ops::distance(__first, __m1);
}
else
@ -187,9 +191,8 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
// merge smaller range with recursive call and larger with tail recursion elimination
if (__len11 + __len21 < __len12 + __len22)
{
std::__inplace_merge<_AlgPolicy, _Compare>(
std::__inplace_merge<_AlgPolicy>(
__first, __m1, __middle, __comp, __len11, __len21, __buff, __buff_size);
// _VSTD::__inplace_merge<_Compare>(__middle, __m2, __last, __comp, __len12, __len22, __buff, __buff_size);
__first = __middle;
__middle = __m2;
__len1 = __len12;
@ -197,9 +200,8 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
}
else
{
std::__inplace_merge<_AlgPolicy, _Compare>(
std::__inplace_merge<_AlgPolicy>(
__middle, __m2, __last, __comp, __len12, __len22, __buff, __buff_size);
// _VSTD::__inplace_merge<_Compare>(__first, __m1, __middle, __comp, __len11, __len21, __buff, __buff_size);
__last = __middle;
__middle = __m1;
__len1 = __len11;
@ -208,33 +210,40 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
}
}
template <class _BidirectionalIterator, class _Compare>
inline _LIBCPP_INLINE_VISIBILITY
template <class _AlgPolicy, class _BidirectionalIterator, class _Compare>
_LIBCPP_HIDE_FROM_ABI
void
inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
_Compare __comp)
__inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
_Compare&& __comp)
{
typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
typedef typename iterator_traits<_BidirectionalIterator>::difference_type difference_type;
difference_type __len1 = _VSTD::distance(__first, __middle);
difference_type __len2 = _VSTD::distance(__middle, __last);
difference_type __len1 = _IterOps<_AlgPolicy>::distance(__first, __middle);
difference_type __len2 = _IterOps<_AlgPolicy>::distance(__middle, __last);
difference_type __buf_size = _VSTD::min(__len1, __len2);
// TODO: Remove the use of std::get_temporary_buffer
_LIBCPP_SUPPRESS_DEPRECATED_PUSH
pair<value_type*, ptrdiff_t> __buf = _VSTD::get_temporary_buffer<value_type>(__buf_size);
_LIBCPP_SUPPRESS_DEPRECATED_POP
unique_ptr<value_type, __return_temporary_buffer> __h(__buf.first);
typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
return _VSTD::__inplace_merge<_ClassicAlgPolicy, _Comp_ref>(__first, __middle, __last, __comp, __len1, __len2,
__buf.first, __buf.second);
return std::__inplace_merge<_AlgPolicy>(
std::move(__first), std::move(__middle), std::move(__last), __comp, __len1, __len2, __buf.first, __buf.second);
}
template <class _BidirectionalIterator, class _Compare>
inline _LIBCPP_HIDE_FROM_ABI void inplace_merge(
_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, _Compare __comp) {
typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
std::__inplace_merge<_ClassicAlgPolicy>(
std::move(__first), std::move(__middle), std::move(__last), static_cast<_Comp_ref>(__comp));
}
template <class _BidirectionalIterator>
inline _LIBCPP_INLINE_VISIBILITY
inline _LIBCPP_HIDE_FROM_ABI
void
inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last)
{
_VSTD::inplace_merge(__first, __middle, __last,
std::inplace_merge(std::move(__first), std::move(__middle), std::move(__last),
__less<typename iterator_traits<_BidirectionalIterator>::value_type>());
}

View File

@ -10,6 +10,7 @@
#define _LIBCPP___ALGORITHM_RANGES_INPLACE_MERGE_H
#include <__algorithm/inplace_merge.h>
#include <__algorithm/iterator_operations.h>
#include <__algorithm/make_projected.h>
#include <__config>
#include <__functional/identity.h>
@ -17,6 +18,7 @@
#include <__functional/ranges_operations.h>
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/next.h>
#include <__iterator/projected.h>
#include <__iterator/sortable.h>
#include <__ranges/access.h>
@ -36,28 +38,38 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges {
namespace __inplace_merge {
struct __fn {
struct __fn {
template <class _Iter, class _Sent, class _Comp, class _Proj>
_LIBCPP_HIDE_FROM_ABI static constexpr auto
__inplace_merge_impl(_Iter __first, _Iter __middle, _Sent __last, _Comp&& __comp, _Proj&& __proj) {
auto __last_iter = ranges::next(__middle, __last);
std::__inplace_merge<_RangeAlgPolicy>(
std::move(__first), std::move(__middle), __last_iter, ranges::__make_projected_comp(__comp, __proj));
return __last_iter;
}
template <bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent, class _Comp = ranges::less, class _Proj = identity>
requires sortable<_Iter, _Comp, _Proj>
_LIBCPP_HIDE_FROM_ABI
_Iter operator()(_Iter __first, _Iter __middle, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
// TODO: implement
(void)__first; (void)__middle; (void)__last; (void)__comp; (void)__proj;
return {};
}
template <
bidirectional_iterator _Iter,
sentinel_for<_Iter> _Sent,
class _Comp = ranges::less,
class _Proj = identity>
requires sortable<_Iter, _Comp, _Proj>
_LIBCPP_HIDE_FROM_ABI _Iter
operator()(_Iter __first, _Iter __middle, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
return __inplace_merge_impl(
std::move(__first), std::move(__middle), std::move(__last), std::move(__comp), std::move(__proj));
}
template <bidirectional_range _Range, class _Comp = ranges::less, class _Proj = identity>
requires sortable<iterator_t<_Range>, _Comp, _Proj>
_LIBCPP_HIDE_FROM_ABI
borrowed_iterator_t<_Range> operator()(_Range&& __range, iterator_t<_Range> __middle,
_Comp __comp = {}, _Proj __proj = {}) const {
// TODO: implement
(void)__range; (void)__middle; (void)__comp; (void)__proj;
return {};
}
};
template <bidirectional_range _Range, class _Comp = ranges::less, class _Proj = identity>
requires sortable<
iterator_t<_Range>,
_Comp,
_Proj> _LIBCPP_HIDE_FROM_ABI borrowed_iterator_t<_Range>
operator()(_Range&& __range, iterator_t<_Range> __middle, _Comp __comp = {}, _Proj __proj = {}) const {
return __inplace_merge_impl(
ranges::begin(__range), std::move(__middle), ranges::end(__range), std::move(__comp), std::move(__proj));
}
};
} // namespace __inplace_merge

View File

@ -203,7 +203,7 @@ __stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp
}
std::__stable_sort<_AlgPolicy, _Compare>(__first, __m, __comp, __l2, __buff, __buff_size);
std::__stable_sort<_AlgPolicy, _Compare>(__m, __last, __comp, __len - __l2, __buff, __buff_size);
std::__inplace_merge<_AlgPolicy, _Compare>(__first, __m, __last, __comp, __l2, __len - __l2, __buff, __buff_size);
std::__inplace_merge<_AlgPolicy>(__first, __m, __last, __comp, __l2, __len - __l2, __buff, __buff_size);
}
template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>

View File

@ -803,7 +803,7 @@ namespace ranges {
set_symmetric_difference(I1 first1, S1 last1, I2 first2, S2 last2, O result,
Comp comp = {}, Proj1 proj1 = {},
Proj2 proj2 = {}); // since C++20
template<input_range R1, input_range R2, weakly_incrementable O,
class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity>
requires mergeable<iterator_t<R1>, iterator_t<R2>, O, Comp, Proj1, Proj2>
@ -816,13 +816,13 @@ namespace ranges {
indirect_strict_weak_order<const T*, projected<I, Proj>> Comp = ranges::less>
constexpr subrange<I>
equal_range(I first, S last, const T& value, Comp comp = {}, Proj proj = {}); // since C++20
template<forward_range R, class T, class Proj = identity,
indirect_strict_weak_order<const T*, projected<iterator_t<R>, Proj>> Comp =
ranges::less>
constexpr borrowed_subrange_t<R>
equal_range(R&& r, const T& value, Comp comp = {}, Proj proj = {}); // since C++20
template<class I1, class I2, class O>
using set_union_result = in_in_out_result<I1, I2, O>; // since C++20
@ -847,13 +847,24 @@ namespace ranges {
ranges::less>
constexpr bool includes(I1 first1, S1 last1, I2 first2, S2 last2, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {}); // Since C++20
template<input_range R1, input_range R2, class Proj1 = identity,
class Proj2 = identity,
indirect_strict_weak_order<projected<iterator_t<R1>, Proj1>,
projected<iterator_t<R2>, Proj2>> Comp = ranges::less>
constexpr bool includes(R1&& r1, R2&& r2, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {}); // Since C++20
template<bidirectional_iterator I, sentinel_for<I> S, class Comp = ranges::less,
class Proj = identity>
requires sortable<I, Comp, Proj>
I inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // Since C++20
template<bidirectional_range R, class Comp = ranges::less, class Proj = identity>
requires sortable<iterator_t<R>, Comp, Proj>
borrowed_iterator_t<R>
inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {},
Proj proj = {}); // Since C++20
}
constexpr bool // constexpr in C++20
@ -1607,6 +1618,7 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/ranges_generate.h>
#include <__algorithm/ranges_generate_n.h>
#include <__algorithm/ranges_includes.h>
#include <__algorithm/ranges_inplace_merge.h>
#include <__algorithm/ranges_is_heap.h>
#include <__algorithm/ranges_is_heap_until.h>
#include <__algorithm/ranges_is_partitioned.h>

View File

@ -239,6 +239,7 @@ module std [system] {
module __algorithm {
module adjacent_find { private header "__algorithm/adjacent_find.h" }
module algorithm_family { private header "__algorithm/algorithm_family.h" }
module all_of { private header "__algorithm/all_of.h" }
module any_of { private header "__algorithm/any_of.h" }
module binary_search { private header "__algorithm/binary_search.h" }

View File

@ -138,8 +138,8 @@ constexpr bool all_the_algorithms()
(void)std::ranges::is_sorted(a, Less(&copies)); assert(copies == 0);
(void)std::ranges::is_sorted_until(first, last, Less(&copies)); assert(copies == 0);
(void)std::ranges::is_sorted_until(a, Less(&copies)); assert(copies == 0);
//if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(&copies)); assert(copies == 0); }
//if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(&copies)); assert(copies == 0); }
if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(&copies)); assert(copies == 0); }
if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(&copies)); assert(copies == 0); }
(void)std::ranges::lexicographical_compare(first, last, first2, last2, Less(&copies)); assert(copies == 0);
(void)std::ranges::lexicographical_compare(a, b, Less(&copies)); assert(copies == 0);
(void)std::ranges::lower_bound(first, last, value, Less(&copies)); assert(copies == 0);

View File

@ -121,8 +121,8 @@ constexpr bool all_the_algorithms()
(void)std::ranges::is_sorted(a, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::is_sorted_until(first, last, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::is_sorted_until(a, Less(), Proj(&copies)); assert(copies == 0);
//if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(), Proj(&copies)); assert(copies == 0); }
//if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(), Proj(&copies)); assert(copies == 0); }
if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(), Proj(&copies)); assert(copies == 0); }
if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(), Proj(&copies)); assert(copies == 0); }
(void)std::ranges::lexicographical_compare(first, last, first2, last2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
(void)std::ranges::lexicographical_compare(a, b, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
(void)std::ranges::lower_bound(first, last, value, Less(), Proj(&copies)); assert(copies == 0);

View File

@ -37,6 +37,7 @@ END-SCRIPT
// DO NOT MANUALLY EDIT ANYTHING BETWEEN THE MARKERS BELOW
// GENERATED-MARKER
#include <__algorithm/adjacent_find.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/adjacent_find.h'}}
#include <__algorithm/algorithm_family.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/algorithm_family.h'}}
#include <__algorithm/all_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/all_of.h'}}
#include <__algorithm/any_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/any_of.h'}}
#include <__algorithm/binary_search.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/binary_search.h'}}

View File

@ -27,23 +27,315 @@
#include <concepts>
#include <functional>
#include <ranges>
#include <type_traits>
#include "almost_satisfies_types.h"
#include "counting_predicates.h"
#include "counting_projection.h"
#include "test_iterators.h"
// TODO: SFINAE tests.
template < class Iter,
class Middle = Iter,
class Sent = sentinel_wrapper<std::remove_cvref_t<Iter>>,
class Comp = std::ranges::less,
class Proj = std::identity>
concept HasInplaceMergeIter =
requires(Iter&& iter, Middle&& mid, Sent&& sent, Comp&& comp, Proj&& proj) {
std::ranges::inplace_merge(
std::forward<Iter>(iter),
std::forward<Middle>(mid),
std::forward<Sent>(sent),
std::forward<Comp>(comp),
std::forward<Proj>(proj));
};
constexpr bool test() {
// TODO: main tests.
// TODO: A custom comparator works.
// TODO: A custom projection works.
static_assert(HasInplaceMergeIter<int*, int*, int*>);
// !bidirectional_­iterator<I>
static_assert(!HasInplaceMergeIter<BidirectionalIteratorNotDerivedFrom>);
static_assert(!HasInplaceMergeIter<cpp20_input_iterator<int*>>);
// !sentinel_for<S, I>
static_assert(!HasInplaceMergeIter<int*, int*, SentinelForNotSemiregular>);
static_assert(!HasInplaceMergeIter<int*, int*, SentinelForNotWeaklyEqualityComparableWith>);
// !sortable<I, Comp, Proj>
static_assert(!HasInplaceMergeIter<int*, int*, int*, ComparatorNotCopyable<int*>>);
static_assert(!HasInplaceMergeIter<const int*, const int*, const int*>);
template < class Range,
class Middle = std::ranges::iterator_t<Range>,
class Comp = std::ranges::less,
class Proj = std::identity>
concept HasInplaceMergeRange =
requires(Range&& r, Middle&& mid, Comp&& comp, Proj&& proj) {
std::ranges::inplace_merge(
std::forward<Range>(r), std::forward<Middle>(mid), std::forward<Comp>(comp), std::forward<Proj>(proj));
};
template <class T>
using R = UncheckedRange<T>;
static_assert(HasInplaceMergeRange<R<int*>, int*>);
// !bidirectional_range<R>
static_assert(!HasInplaceMergeRange<R<cpp20_input_iterator<int*>>>);
static_assert(!HasInplaceMergeRange<R<BidirectionalIteratorNotDecrementable>>);
// !sortable<iterator_t<R>, Comp, Proj>
static_assert(!HasInplaceMergeRange<R<int*>, int*, ComparatorNotCopyable<int*>>);
static_assert(!HasInplaceMergeIter<R<const int*>, const int*>);
template <class In, template <class> class SentWrapper, std::size_t N1, std::size_t N2>
void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int, N2> expected) {
std::is_sorted(input.begin(), input.begin() + midIdx);
std::is_sorted(input.begin() + midIdx, input.end());
std::is_sorted(expected.begin(), expected.end());
using Sent = SentWrapper<In>;
// iterator overload
{
auto in = input;
std::same_as<In> decltype(auto) result =
std::ranges::inplace_merge(In{in.data()}, In{in.data() + midIdx}, Sent{In{in.data() + in.size()}});
assert(std::ranges::equal(in, expected));
assert(base(result) == in.data() + in.size());
}
// range overload
{
auto in = input;
std::ranges::subrange r{In{in.data()}, Sent{In{in.data() + in.size()}}};
std::same_as<In> decltype(auto) result = std::ranges::inplace_merge(r, In{in.data() + midIdx});
assert(std::ranges::equal(in, expected));
assert(base(result) == in.data() + in.size());
}
}
template <class In, template <class> class SentWrapper>
void testImpl() {
// sorted range
{
std::array in{0, 1, 5, 6, 9, 10};
std::array expected = in;
testInplaceMergeImpl<In, SentWrapper>(in, 3, expected);
}
// [first, mid) is longer
{
std::array in{0, 5, 9, 15, 18, 22, 2, 4, 6, 10};
std::array expected = {0, 2, 4, 5, 6, 9, 10, 15, 18, 22};
testInplaceMergeImpl<In, SentWrapper>(in, 6, expected);
}
// [first, mid) is shorter
{
std::array in{0, 5, 9, 2, 4, 6, 10};
std::array expected = {0, 2, 4, 5, 6, 9, 10};
testInplaceMergeImpl<In, SentWrapper>(in, 3, expected);
}
// [first, mid) == [mid, last)
{
std::array in{0, 5, 9, 0, 5, 9};
std::array expected = {0, 0, 5, 5, 9, 9};
testInplaceMergeImpl<In, SentWrapper>(in, 3, expected);
}
// duplicates within each range
{
std::array in{1, 5, 5, 2, 9, 9, 9};
std::array expected = {1, 2, 5, 5, 9, 9, 9};
testInplaceMergeImpl<In, SentWrapper>(in, 3, expected);
}
// all the same
{
std::array in{5, 5, 5, 5, 5, 5, 5, 5};
std::array expected = in;
testInplaceMergeImpl<In, SentWrapper>(in, 5, expected);
}
// [first, mid) is empty (mid == begin)
{
std::array in{0, 1, 5, 6, 9, 10};
std::array expected = in;
testInplaceMergeImpl<In, SentWrapper>(in, 0, expected);
}
// [mid, last] is empty (mid == end)
{
std::array in{0, 1, 5, 6, 9, 10};
std::array expected = in;
testInplaceMergeImpl<In, SentWrapper>(in, 6, expected);
}
// both empty
{
std::array<int, 0> in{};
std::array expected = in;
testInplaceMergeImpl<In, SentWrapper>(in, 0, expected);
}
// mid == first + 1
{
std::array in{9, 2, 5, 7, 10};
std::array expected{2, 5, 7, 9, 10};
testInplaceMergeImpl<In, SentWrapper>(in, 1, expected);
}
// mid == last - 1
{
std::array in{2, 5, 7, 10, 9};
std::array expected{2, 5, 7, 9, 10};
testInplaceMergeImpl<In, SentWrapper>(in, 4, expected);
}
}
template < template <class> class SentWrapper>
void withAllPermutationsOfIter() {
testImpl<bidirectional_iterator<int*>, SentWrapper>();
testImpl<random_access_iterator<int*>, SentWrapper>();
testImpl<contiguous_iterator<int*>, SentWrapper>();
testImpl<int*, SentWrapper>();
}
bool test() {
withAllPermutationsOfIter<std::type_identity_t>();
withAllPermutationsOfIter<sentinel_wrapper>();
struct Data {
int data;
};
const auto equal = [](const Data& x, const Data& y) { return x.data == y.data; };
// Test custom comparator
{
std::array<Data, 4> input{{{4}, {8}, {2}, {5}}};
std::array<Data, 4> expected{{{2}, {4}, {5}, {8}}};
const auto comp = [](const Data& x, const Data& y) { return x.data < y.data; };
// iterator overload
{
auto in = input;
auto result = std::ranges::inplace_merge(in.begin(), in.begin() + 2, in.end(), comp);
assert(std::ranges::equal(in, expected, equal));
assert(result == in.end());
}
// range overload
{
auto in = input;
auto result = std::ranges::inplace_merge(in, in.begin() + 2, comp);
assert(std::ranges::equal(in, expected, equal));
assert(result == in.end());
}
}
// Test custom projection
{
std::array<Data, 4> input{{{4}, {8}, {2}, {5}}};
std::array<Data, 4> expected{{{2}, {4}, {5}, {8}}};
const auto proj = &Data::data;
// iterator overload
{
auto in = input;
auto result = std::ranges::inplace_merge(in.begin(), in.begin() + 2, in.end(), {}, proj);
assert(std::ranges::equal(in, expected, equal));
assert(result == in.end());
}
// range overload
{
auto in = input;
auto result = std::ranges::inplace_merge(in, in.begin() + 2, {}, proj);
assert(std::ranges::equal(in, expected, equal));
assert(result == in.end());
}
}
// Remarks: Stable.
{
struct IntAndID {
int data;
int id;
constexpr auto operator<=>(const IntAndID& rhs) const { return data <=> rhs.data; }
constexpr auto operator==(const IntAndID& rhs) const { return data == rhs.data; }
};
std::array<IntAndID, 6> input{{{0, 0}, {1, 0}, {2, 0}, {0, 1}, {1, 1}, {2, 1}}};
// iterator overload
{
auto in = input;
auto result = std::ranges::inplace_merge(in.begin(), in.begin() + 3, in.end());
assert(std::ranges::equal(in, std::array{0, 0, 1, 1, 2, 2}, {}, &IntAndID::data));
assert(std::ranges::equal(in, std::array{0, 1, 0, 1, 0, 1}, {}, &IntAndID::id));
assert(result == in.end());
}
// range overload
{
auto in = input;
auto result = std::ranges::inplace_merge(in, in.begin() + 3);
assert(std::ranges::equal(in, std::array{0, 0, 1, 1, 2, 2}, {}, &IntAndID::data));
assert(std::ranges::equal(in, std::array{0, 1, 0, 1, 0, 1}, {}, &IntAndID::id));
assert(result == in.end());
}
}
// Complexity: Let N = last - first :
// - For the overloads with no ExecutionPolicy, and if enough
// additional memory is available, exactly N 1 comparisons.
// - Otherwise, O(NlogN) comparisons.
// In either case, twice as many projections as comparisons.
{
std::array input{1, 2, 3, 3, 3, 7, 7, 2, 2, 5, 5, 6, 6};
std::array expected{1, 2, 2, 2, 3, 3, 3, 5, 5, 6, 6, 7, 7};
auto mid = 7;
// iterator overload
{
auto in = input;
int numberOfComp = 0;
int numberOfProj = 0;
auto result = std::ranges::inplace_merge(
in.begin(),
in.begin() + mid,
in.end(),
counting_predicate{std::ranges::less{}, numberOfComp},
counting_projection{numberOfProj});
assert(std::ranges::equal(in, expected));
assert(result == in.end());
// the spec specifies exactly N-1 comparison but we actually
// do not invoke as many times as specified
assert(numberOfComp <= static_cast<int>(in.size() - 1));
assert(numberOfProj <= 2 * numberOfComp);
}
// range overload
{
auto in = input;
int numberOfComp = 0;
int numberOfProj = 0;
auto result = std::ranges::inplace_merge(
in,
in.begin() + mid,
counting_predicate{std::ranges::less{}, numberOfComp},
counting_projection{numberOfProj});
assert(std::ranges::equal(in, expected));
assert(result == in.end());
assert(numberOfComp <= static_cast<int>(in.size() - 1));
assert(numberOfProj <= 2 * numberOfComp);
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
// inplace_merge is not constexpr in the latest finished Standard (C++20)
return 0;
}

View File

@ -193,8 +193,8 @@ constexpr bool test_all() {
dangling_1st(std::ranges::stable_sort, in);
dangling_1st(std::ranges::partial_sort, in, mid);
dangling_1st(std::ranges::nth_element, in, mid);
//if (!std::is_constant_evaluated())
// dangling_1st(std::ranges::inplace_merge, in, mid);
if (!std::is_constant_evaluated())
dangling_1st(std::ranges::inplace_merge, in, mid);
dangling_1st(std::ranges::make_heap, in);
dangling_1st(std::ranges::push_heap, in);
dangling_1st(std::ranges::pop_heap, in);

View File

@ -35,36 +35,36 @@ static_assert(std::convertible_to<decltype(binary_pred(1, 2)), bool>);
// Invokes both the (iterator, sentinel, ...) and the (range, ...) overloads of the given niebloid.
// (in, ...)
template <class Func, std::ranges::range Input, class ...Args>
constexpr void test(Func&& func, Input& in, Args&& ...args) {
template <class Func, std::ranges::range Input, class... Args>
constexpr void test(Func&& func, Input& in, Args&&... args) {
func(in.begin(), in.end(), std::forward<Args>(args)...);
func(in, std::forward<Args>(args)...);
}
// (in1, in2, ...)
template <class Func, std::ranges::range Input, class ...Args>
constexpr void test(Func&& func, Input& in1, Input& in2, Args&& ...args) {
template <class Func, std::ranges::range Input, class... Args>
constexpr void test(Func&& func, Input& in1, Input& in2, Args&&... args) {
func(in1.begin(), in1.end(), in2.begin(), in2.end(), std::forward<Args>(args)...);
func(in1, in2, std::forward<Args>(args)...);
}
// (in, mid, ...)
template <class Func, std::ranges::range Input, class ...Args>
constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&& ...args) {
template <class Func, std::ranges::range Input, class... Args>
constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&&... args) {
func(in.begin(), mid, in.end(), std::forward<Args>(args)...);
func(in, mid, std::forward<Args>(args)...);
}
constexpr bool test_all() {
std::array in = {1, 2, 3};
std::array in = {1, 2, 3};
std::array in2 = {4, 5, 6};
auto mid = in.begin() + 1;
auto mid = in.begin() + 1;
std::array output = {7, 8, 9, 10, 11, 12};
auto out = output.begin();
auto out2 = output.begin() + 1;
auto out = output.begin();
auto out2 = output.begin() + 1;
int x = 2;
int x = 2;
int count = 1;
test(std::ranges::any_of, in, unary_pred);
@ -133,7 +133,8 @@ constexpr bool test_all() {
test(std::ranges::stable_sort, in, binary_pred);
test_mid(std::ranges::partial_sort, in, mid, binary_pred);
test_mid(std::ranges::nth_element, in, mid, binary_pred);
//test_mid(std::ranges::inplace_merge, in, mid, binary_pred);
if (!std::is_constant_evaluated())
test_mid(std::ranges::inplace_merge, in, mid, binary_pred);
test(std::ranges::make_heap, in, binary_pred);
test(std::ranges::push_heap, in, binary_pred);
test(std::ranges::pop_heap, in, binary_pred);

View File

@ -36,34 +36,34 @@ struct Bar {
// Invokes both the (iterator, sentinel, ...) and the (range, ...) overloads of the given niebloid.
// (in, ...)
template <class Func, std::ranges::range Input, class ...Args>
constexpr void test(Func&& func, Input& in, Args&& ...args) {
template <class Func, std::ranges::range Input, class... Args>
constexpr void test(Func&& func, Input& in, Args&&... args) {
func(in.begin(), in.end(), std::forward<Args>(args)...);
func(in, std::forward<Args>(args)...);
}
// (in1, in2, ...)
template <class Func, std::ranges::range Input, class ...Args>
constexpr void test(Func&& func, Input& in1, Input& in2, Args&& ...args) {
template <class Func, std::ranges::range Input, class... Args>
constexpr void test(Func&& func, Input& in1, Input& in2, Args&&... args) {
func(in1.begin(), in1.end(), in2.begin(), in2.end(), std::forward<Args>(args)...);
func(in1, in2, std::forward<Args>(args)...);
}
// (in, mid, ...)
template <class Func, std::ranges::range Input, class ...Args>
constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&& ...args) {
template <class Func, std::ranges::range Input, class... Args>
constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&&... args) {
func(in.begin(), mid, in.end(), std::forward<Args>(args)...);
func(in, mid, std::forward<Args>(args)...);
}
constexpr bool test_all() {
std::array in = {Bar{Foo{1}}, Bar{Foo{2}}, Bar{Foo{3}}};
std::array in = {Bar{Foo{1}}, Bar{Foo{2}}, Bar{Foo{3}}};
std::array in2 = {Bar{Foo{4}}, Bar{Foo{5}}, Bar{Foo{6}}};
auto mid = in.begin() + 1;
auto mid = in.begin() + 1;
std::array output = {Bar{Foo{7}}, Bar{Foo{8}}, Bar{Foo{9}}, Bar{Foo{10}}, Bar{Foo{11}}, Bar{Foo{12}}};
auto out = output.begin();
auto out2 = output.begin() + 1;
auto out = output.begin();
auto out2 = output.begin() + 1;
Bar a{Foo{1}};
Bar b{Foo{2}};
@ -162,7 +162,8 @@ constexpr bool test_all() {
test(std::ranges::stable_sort, in, &Foo::binary_pred, &Bar::val);
test_mid(std::ranges::partial_sort, in, mid, &Foo::binary_pred, &Bar::val);
test_mid(std::ranges::nth_element, in, mid, &Foo::binary_pred, &Bar::val);
//test_mid(std::ranges::inplace_merge, in, mid, binary_pred);
if (!std::is_constant_evaluated())
test_mid(std::ranges::inplace_merge, in, mid, &Foo::binary_pred, &Bar::val);
test(std::ranges::make_heap, in, &Foo::binary_pred, &Bar::val);
test(std::ranges::push_heap, in, &Foo::binary_pred, &Bar::val);
test(std::ranges::pop_heap, in, &Foo::binary_pred, &Bar::val);

View File

@ -164,6 +164,7 @@ constexpr void run_tests() {
// test(std::ranges::stable_sort, in);
test_mid(std::ranges::partial_sort, in, mid);
test_mid(std::ranges::nth_element, in, mid);
// TODO(ranges): `inplace_merge` requires `ranges::rotate` to be implemented.
//if (!std::is_constant_evaluated())
// test_mid(std::ranges::inplace_merge, in, mid);
test(std::ranges::make_heap, in);

View File

@ -87,7 +87,7 @@ static_assert(test(std::ranges::for_each_n, a, 10, odd));
static_assert(test(std::ranges::generate, a, gen));
static_assert(test(std::ranges::generate_n, a, 10, gen));
static_assert(test(std::ranges::includes, a, a));
//static_assert(test(std::ranges::inplace_merge, a, a+5));
static_assert(test(std::ranges::inplace_merge, a, a+5));
static_assert(test(std::ranges::is_heap, a));
static_assert(test(std::ranges::is_heap_until, a));
static_assert(test(std::ranges::is_partitioned, a, odd));