diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 6b93938c314..eb8559309ac 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -7602,6 +7602,79 @@ _NODISCARD bool includes(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _Firs _REQUIRE_PARALLEL_ITERATOR(_FwdIt2); return _STD includes(_First1, _Last1, _First2, _Last2); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::includes + class _Includes_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + class _Pj1 = identity, class _Pj2 = identity, + indirect_strict_weak_order, projected<_It2, _Pj2>> _Pr = ranges::less> + _NODISCARD constexpr bool operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred = {}, + _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + return _Includes_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), _Pass_fn(_Pred), + _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + + template , _Pj1>, projected, _Pj2>> _Pr = + ranges::less> + _NODISCARD constexpr bool operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + return _Includes_unchecked(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + + private: + template + _NODISCARD static constexpr bool _Includes_unchecked( + _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + + if (_First2 == _Last2) { + return true; + } else if (_First1 == _Last1) { + return false; + } + + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + ++_First1; + if (_First1 == _Last1) { + return false; + } + + continue; + } + + if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + return false; + } + + ++_First1; + ++_First2; + if (_First2 == _Last2) { + return true; + } else if (_First1 == _Last1) { + return false; + } + } + } + }; + + inline constexpr _Includes_fn includes{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE set_union @@ -7664,6 +7737,84 @@ _FwdIt3 set_union(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _Fw _REQUIRE_PARALLEL_ITERATOR(_FwdIt3); return _STD set_union(_First1, _Last1, _First2, _Last2, _Dest); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE set_union_result + template + using set_union_result = in_in_out_result<_In1, _In2, _Out>; + + // VARIABLE ranges::set_union + class _Set_union_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2> + constexpr set_union_result<_It1, _It2, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, + _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = _Set_union_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + + template + requires mergeable, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2> + constexpr set_union_result, borrowed_iterator_t<_Rng2>, _Out> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + auto _UResult = _Set_union_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Get_unwrapped(_STD move(_First2)), _Uend(_Range2), _Get_unwrapped_unverified(_STD move(_Result)), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr set_union_result<_It1, _It2, _Out> _Set_union_unchecked(_It1 _First1, + const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>); + + for (; _First1 != _Last1 && _First2 != _Last2; ++_Result) { + if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + *_Result = *_First2; + ++_First2; + } else { + *_Result = *_First1; + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + ++_First2; + } + ++_First1; + } + } + + auto _UResult1 = _RANGES _Copy_unchecked(_STD move(_First1), _STD move(_Last1), _STD move(_Result)); + auto _UResult2 = _RANGES _Copy_unchecked(_STD move(_First2), _STD move(_Last2), _STD move(_UResult1.out)); + return {_STD move(_UResult1.in), _STD move(_UResult2.in), _STD move(_UResult2.out)}; + } + }; + + inline constexpr _Set_union_fn set_union{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE set_intersection @@ -7714,6 +7865,91 @@ _FwdIt3 set_intersection(_ExPo&& _Exec, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 // AND sets [_First1, _Last1) and [_First2, _Last2) return _STD set_intersection(_STD forward<_ExPo>(_Exec), _First1, _Last1, _First2, _Last2, _Dest, less{}); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE set_intersection_result + template + using set_intersection_result = in_in_out_result<_In1, _In2, _Out>; + + // VARIABLE ranges::set_intersection + class _Set_intersection_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2> + constexpr set_intersection_result<_It1, _It2, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, + _Se2 _Last2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = + _Set_intersection_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + + template + requires mergeable, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2> + constexpr set_intersection_result, borrowed_iterator_t<_Rng2>, _Out> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + auto _UResult = _Set_intersection_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Get_unwrapped(_STD move(_First2)), _Uend(_Range2), _Get_unwrapped_unverified(_STD move(_Result)), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr set_intersection_result<_It1, _It2, _Out> _Set_intersection_unchecked(_It1 _First1, + const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>); + + for (;;) { + if (_First1 == _Last1) { + _RANGES advance(_First2, _Last2); + break; + } else if (_First2 == _Last2) { + _RANGES advance(_First1, _Last1); + break; + } + + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + ++_First1; + } else if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + ++_First2; + } else { + *_Result = *_First1; + ++_Result; + ++_First1; + ++_First2; + } + } + + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + }; + + inline constexpr _Set_intersection_fn set_intersection{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE set_difference @@ -7765,6 +8001,87 @@ _FwdIt3 set_difference(_ExPo&& _Exec, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _ // take set [_First2, _Last2) from [_First1, _Last1) return _STD set_difference(_STD forward<_ExPo>(_Exec), _First1, _Last1, _First2, _Last2, _Dest, less{}); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE set_difference_result + template + using set_difference_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::set_difference + class _Set_difference_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2> + constexpr set_difference_result<_It1, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, + _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = + _Set_difference_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_Result)}; + } + + template + requires mergeable, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2> + constexpr set_difference_result, _Out> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + auto _UResult = _Set_difference_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), + _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr set_difference_result<_It1, _Out> _Set_difference_unchecked(_It1 _First1, + const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>); + + for (;;) { + if (_First1 == _Last1) { + return {_STD move(_First1), _STD move(_Result)}; + } + + if (_First2 == _Last2) { + return _RANGES _Copy_unchecked(_STD move(_First1), _STD move(_Last1), _STD move(_Result)); + } + + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + *_Result = *_First1; + ++_Result; + ++_First1; + } else { + if (!_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + ++_First1; + } + + ++_First2; + } + } + } + }; + + inline constexpr _Set_difference_fn set_difference{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE set_symmetric_difference @@ -7830,6 +8147,95 @@ _FwdIt3 set_symmetric_difference(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdI _REQUIRE_PARALLEL_ITERATOR(_FwdIt3); return _STD set_symmetric_difference(_First1, _Last1, _First2, _Last2, _Dest); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE set_symmetric_difference_result + template + using set_symmetric_difference_result = in_in_out_result<_In1, _In2, _Out>; + + // VARIABLE ranges::set_symmetric_difference + class _Set_symmetric_difference_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2> + constexpr set_symmetric_difference_result<_It1, _It2, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, + _Se2 _Last2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = _Set_symmetric_difference_unchecked(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), + _Get_unwrapped(_STD move(_Last2)), _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), + _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + + template + requires mergeable, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2> + constexpr set_symmetric_difference_result, borrowed_iterator_t<_Rng2>, _Out> + operator()(_Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, + _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + auto _UResult = _Set_symmetric_difference_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Get_unwrapped(_STD move(_First2)), _Uend(_Range2), _Get_unwrapped_unverified(_STD move(_Result)), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr set_symmetric_difference_result<_It1, _It2, _Out> + _Set_symmetric_difference_unchecked(_It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, + _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>); + + for (;;) { + if (_First1 == _Last1) { + auto _UResult = _RANGES _Copy_unchecked(_STD move(_First2), _STD move(_Last2), _STD move(_Result)); + return {_STD move(_First1), _STD move(_UResult.in), _STD move(_UResult.out)}; + } + + if (_First2 == _Last2) { + auto _UResult = _RANGES _Copy_unchecked(_STD move(_First1), _STD move(_Last1), _STD move(_Result)); + return {_STD move(_UResult.in), _STD move(_First2), _STD move(_UResult.out)}; + } + + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + *_Result = *_First1; + ++_Result; + ++_First1; + } else if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + *_Result = *_First2; + ++_Result; + ++_First2; + } else { + ++_First1; + ++_First2; + } + } + } + }; + + inline constexpr _Set_symmetric_difference_fn set_symmetric_difference{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE max_element diff --git a/stl/inc/xutility b/stl/inc/xutility index 91b39c72cd9..ffced02cccc 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1106,8 +1106,16 @@ concept permutable = forward_iterator<_It> && indirectly_movable_storable<_It, _It> && indirectly_swappable<_It, _It>; -// CONCEPT sortable +// CONCEPT mergeable namespace ranges { struct less; } +template +concept mergeable = input_iterator<_It1> && input_iterator<_It2> + && weakly_incrementable<_Out> + && indirectly_copyable<_It1, _Out> + && indirectly_copyable<_It2, _Out> + && indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>; + +// CONCEPT sortable template concept sortable = permutable<_It> && indirect_strict_weak_order<_Pr, projected<_It, _Proj>>; // clang-format on diff --git a/tests/std/test.lst b/tests/std/test.lst index 12dbf4d64a4..ab36cc7cee9 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -261,6 +261,7 @@ tests\P0896R4_ranges_alg_for_each_n tests\P0896R4_ranges_alg_generate tests\P0896R4_ranges_alg_generate_n tests\P0896R4_ranges_alg_heap +tests\P0896R4_ranges_alg_includes tests\P0896R4_ranges_alg_is_permutation tests\P0896R4_ranges_alg_is_sorted tests\P0896R4_ranges_alg_minmax @@ -282,6 +283,10 @@ tests\P0896R4_ranges_alg_reverse tests\P0896R4_ranges_alg_sample tests\P0896R4_ranges_alg_search tests\P0896R4_ranges_alg_search_n +tests\P0896R4_ranges_alg_set_difference +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_swap_ranges tests\P0896R4_ranges_alg_transform_binary diff --git a/tests/std/tests/P0896R4_ranges_alg_includes/env.lst b/tests/std/tests/P0896R4_ranges_alg_includes/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_includes/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_includes/test.cpp b/tests/std/tests/P0896R4_ranges_alg_includes/test.cpp new file mode 100644 index 00000000000..082ca91f38e --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_includes/test.cpp @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +struct instantiator { + static constexpr P haystack_elements[] = {{0, 0}, {0, 1}, {1, 0}, {2, 0}, {2, 1}, {4, 0}, {4, 1}}; + static constexpr int needle_elements[] = {-1, 0, 3, 3}; + static constexpr int bad_needle_elements[] = {-1, 0, 2, 2}; + + static constexpr auto add_one = [](const int x) { return x + 1; }; + + template + static constexpr void call() { + using ranges::includes, ranges::less; + + { // Validate range overload + Haystack haystack{haystack_elements}; + Needle needle{needle_elements}; + const same_as auto result = includes(haystack, needle, ranges::less{}, get_first, add_one); + assert(result); + } + { // Validate iterator overload + Haystack haystack{haystack_elements}; + Needle needle{needle_elements}; + const same_as auto result = includes( + haystack.begin(), haystack.end(), needle.begin(), needle.end(), ranges::less{}, get_first, add_one); + assert(result); + } + + { // Validate range overload, empty haystack + Haystack haystack{}; + Needle needle{needle_elements}; + const same_as auto result = includes(haystack, needle, ranges::less{}, get_first, add_one); + assert(!result); + } + { // Validate iterator overload, empty needle + Haystack haystack{haystack_elements}; + Needle needle{}; + const same_as auto result = includes( + haystack.begin(), haystack.end(), needle.begin(), needle.end(), ranges::less{}, get_first, add_one); + assert(result); + } + + { // Validate range overload, needle not found + Haystack haystack{haystack_elements}; + Needle needle{bad_needle_elements}; + const same_as auto result = includes(haystack, needle, ranges::less{}, get_first, add_one); + assert(!result); + } + } +}; + +#ifdef TEST_EVERYTHING +int main() { + STATIC_ASSERT((test_in_in(), true)); + test_in_in(); +} +#else // ^^^ test all permutations of range properties / test only interesting permutations vvv +template +using test_range = test::range}, IsProxyRef>; + +constexpr void run_tests() { + using namespace test; + using test::iterator, test::range; + + // The algorithm is completely oblivious to: + // * categories stronger than input + // * whether the end sentinel is an iterator + // * size information + // * iterator and/or sentinel differencing + // so let's vary proxyness for coverage and add a range of each category out of paranoia. + + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); +} + +int main() { + STATIC_ASSERT((run_tests(), true)); + run_tests(); +} +#endif // TEST_EVERYTHING diff --git a/tests/std/tests/P0896R4_ranges_alg_set_difference/env.lst b/tests/std/tests/P0896R4_ranges_alg_set_difference/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_set_difference/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_set_difference/test.cpp b/tests/std/tests/P0896R4_ranges_alg_set_difference/test.cpp new file mode 100644 index 00000000000..6332bd4d468 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_set_difference/test.cpp @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +// Validate that set_difference_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, + ranges::less{}, identity{}, identity{})), + ranges::set_difference_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, + ranges::less{}, identity{}, identity{})), + ranges::set_difference_result>); + +struct instantiator { + static constexpr P elements1[] = {{0, 10}, {0, 11}, {0, 12}, {1, 10}, {1, 11}, {3, 10}}; + static constexpr P elements2[] = {{13, 0}, {14, 0}, {10, 2}, {11, 3}, {12, 3}}; + static constexpr P expected[] = {{0, 12}, {1, 10}, {1, 11}}; + + template + static constexpr void call() { + using ranges::set_difference, ranges::set_difference_result, ranges::equal, ranges::iterator_t, ranges::less; + + constexpr auto osize = ranges::size(elements1) + ranges::size(elements2); + + { // Validate range overload + P output[osize]{}; + R1 range1{elements1}; + R2 range2{elements2}; + const same_as, O>> auto result = + set_difference(range1, range2, O{output}, ranges::less{}, get_first, get_second); + assert(result.in == range1.end()); + assert(result.out.peek() == output + ranges::size(expected)); + assert(equal(span{output}.first(), expected)); + } + { // Validate iterator overload + P output[osize]{}; + R1 range1{elements1}; + R2 range2{elements2}; + const same_as, O>> auto result = set_difference(range1.begin(), + range1.end(), range2.begin(), range2.end(), O{output}, ranges::less{}, get_first, get_second); + assert(result.in == range1.end()); + assert(result.out.peek() == output + ranges::size(expected)); + assert(equal(span{output}.first(), expected)); + } + + { // Validate range overload, empty range1 + P output[osize]{}; + R1 range1{}; + R2 range2{elements2}; + const same_as, O>> auto result = + set_difference(range1, range2, O{output}, ranges::less{}, get_first, get_second); + assert(result.in == range1.end()); + assert(result.out.peek() == output); + } + { // Validate iterator overload, empty range2 + P output[osize]{}; + R1 range1{elements1}; + R2 range2{}; + const same_as, O>> auto result = set_difference(range1.begin(), + range1.end(), range2.begin(), range2.end(), O{output}, ranges::less{}, get_first, get_second); + assert(result.in == range1.end()); + assert(result.out.peek() == output + ranges::size(elements1)); + assert(equal(span{output}.first(), elements1)); + } + } +}; + +template +struct generate_readable_ranges { + template + static constexpr void call() { + using namespace test; + using test::range; + + // The algorithm is completely oblivious to: + // * categories stronger than input + // * whether the end sentinel is an iterator + // * size information + // * iterator and/or sentinel differencing + // so let's vary proxyness for coverage and call it good. + + Continuation::template call>(); + Continuation::template call>(); + } +}; + +template +struct generate_writable_iterators { + template + static constexpr void call() { + using namespace test; + using test::iterator; + + // The algorithm is completely oblivious to all properties except for proxyness, + // so again we'll vary that property, and we'll also get coverage from input iterators to ensure the algorithm + // doesn't inadvertently depend on the output_iterator-only `*i++ = meow` expression. + + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + } +}; + +constexpr void run_tests() { + generate_readable_ranges>>::call(); +} + +int main() { + STATIC_ASSERT((run_tests(), true)); + run_tests(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_set_intersection/env.lst b/tests/std/tests/P0896R4_ranges_alg_set_intersection/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_set_intersection/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_set_intersection/test.cpp b/tests/std/tests/P0896R4_ranges_alg_set_intersection/test.cpp new file mode 100644 index 00000000000..ee621df8a6c --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_set_intersection/test.cpp @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +// Validate that set_intersection_result aliases in_in_out_result +STATIC_ASSERT( + same_as, ranges::in_in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, + ranges::less{}, identity{}, identity{})), + ranges::set_intersection_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, + ranges::less{}, identity{}, identity{})), + ranges::set_intersection_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, + ranges::less{}, identity{}, identity{})), + ranges::set_intersection_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, ranges::less{}, identity{}, identity{})), + ranges::set_intersection_result>); + +struct instantiator { + static constexpr P elements1[] = {{0, 10}, {0, 11}, {0, 12}, {1, 10}, {1, 11}, {3, 10}}; + static constexpr P elements2[] = {{13, 0}, {14, 0}, {10, 2}, {11, 3}, {12, 3}}; + static constexpr P expected[] = {{0, 10}, {0, 11}, {3, 10}}; + + template + static constexpr void call() { + using ranges::set_intersection, ranges::set_intersection_result, ranges::equal, ranges::iterator_t, + ranges::less; + + constexpr auto osize = ranges::size(elements1) + ranges::size(elements2); + + { // Validate range overload + P output[osize]{}; + R1 range1{elements1}; + R2 range2{elements2}; + const same_as, iterator_t, O>> auto result = + set_intersection(range1, range2, O{output}, ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(expected)); + assert(equal(span{output}.first(), expected)); + } + { // Validate iterator overload + P output[osize]{}; + R1 range1{elements1}; + R2 range2{elements2}; + const same_as, iterator_t, O>> auto result = + set_intersection(range1.begin(), range1.end(), range2.begin(), range2.end(), O{output}, ranges::less{}, + get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(expected)); + assert(equal(span{output}.first(), expected)); + } + + { // Validate range overload, empty range1 + P output[osize]{}; + R1 range1{}; + R2 range2{elements2}; + const same_as, iterator_t, O>> auto result = + set_intersection(range1, range2, O{output}, ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output); + } + { // Validate iterator overload, empty range2 + P output[osize]{}; + R1 range1{elements1}; + R2 range2{}; + const same_as, iterator_t, O>> auto result = + set_intersection(range1.begin(), range1.end(), range2.begin(), range2.end(), O{output}, ranges::less{}, + get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output); + } + } +}; + +template +struct generate_readable_ranges { + template + static constexpr void call() { + using namespace test; + using test::range; + + // The algorithm calls advance(i, s), so it's sensitive to CanDifference and Common in addition to the normal + // proxy sensitivity. + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + } +}; + +template +struct generate_writable_iterators { + template + static constexpr void call() { + using namespace test; + using test::iterator; + + // The algorithm is completely oblivious to all properties except for proxyness, + // so again we'll vary that property, and we'll also get coverage from input iterators to ensure the algorithm + // doesn't inadvertently depend on the output_iterator-only `*i++ = meow` expression. + + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + } +}; + +constexpr void run_tests() { + generate_readable_ranges>>::call(); +} + +int main() { + STATIC_ASSERT((run_tests(), true)); + run_tests(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_set_symmetric_difference/env.lst b/tests/std/tests/P0896R4_ranges_alg_set_symmetric_difference/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_set_symmetric_difference/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_set_symmetric_difference/test.cpp b/tests/std/tests/P0896R4_ranges_alg_set_symmetric_difference/test.cpp new file mode 100644 index 00000000000..d2e82ce92ea --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_set_symmetric_difference/test.cpp @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +// Validate that set_symmetric_difference_result aliases in_in_out_result +STATIC_ASSERT( + same_as, ranges::in_in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, + ranges::less{}, identity{}, identity{})), + ranges::set_symmetric_difference_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, + ranges::less{}, identity{}, identity{})), + ranges::set_symmetric_difference_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, + ranges::less{}, identity{}, identity{})), + ranges::set_symmetric_difference_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, ranges::less{}, identity{}, identity{})), + ranges::set_symmetric_difference_result>); + +struct instantiator { + static constexpr P elements1[] = {{0, 10}, {0, 11}, {0, 12}, {1, 10}, {1, 11}, {3, 10}}; + static constexpr P elements2[] = {{13, 0}, {14, 0}, {10, 2}, {11, 3}, {12, 3}}; + static constexpr P expected[] = {{0, 12}, {1, 10}, {1, 11}, {10, 2}, {12, 3}}; + + template + static constexpr void call() { + using ranges::set_symmetric_difference, ranges::set_symmetric_difference_result, ranges::equal, + ranges::iterator_t, ranges::less; + + constexpr auto osize = ranges::size(elements1) + ranges::size(elements2); + + { // Validate range overload + P output[osize]{}; + R1 range1{elements1}; + R2 range2{elements2}; + const same_as, iterator_t, O>> auto result = + set_symmetric_difference(range1, range2, O{output}, ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(expected)); + assert(equal(span{output}.first(), expected)); + } + { // Validate iterator overload + P output[osize]{}; + R1 range1{elements1}; + R2 range2{elements2}; + const same_as, iterator_t, O>> auto result = + set_symmetric_difference(range1.begin(), range1.end(), range2.begin(), range2.end(), O{output}, + ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(expected)); + assert(equal(span{output}.first(), expected)); + } + + { // Validate range overload, empty range1 + P output[osize]{}; + R1 range1{}; + R2 range2{elements2}; + const same_as, iterator_t, O>> auto result = + set_symmetric_difference(range1, range2, O{output}, ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(elements2)); + assert(equal(span{output}.first(), elements2)); + } + { // Validate iterator overload, empty range2 + P output[osize]{}; + R1 range1{elements1}; + R2 range2{}; + const same_as, iterator_t, O>> auto result = + set_symmetric_difference(range1.begin(), range1.end(), range2.begin(), range2.end(), O{output}, + ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(elements1)); + assert(equal(span{output}.first(), elements1)); + } + } +}; + +template +struct generate_readable_ranges { + template + static constexpr void call() { + using namespace test; + using test::range; + + // The algorithm is completely oblivious to: + // * categories stronger than input + // * whether the end sentinel is an iterator + // * size information + // * iterator and/or sentinel differencing + // so let's vary proxyness for coverage and call it good. + + Continuation::template call>(); + Continuation::template call>(); + } +}; + +template +struct generate_writable_iterators { + template + static constexpr void call() { + using namespace test; + using test::iterator; + + // The algorithm is completely oblivious to all properties except for proxyness, + // so again we'll vary that property, and we'll also get coverage from input iterators to ensure the algorithm + // doesn't inadvertently depend on the output_iterator-only `*i++ = meow` expression. + + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + } +}; + +constexpr void run_tests() { + generate_readable_ranges>>::call(); +} + +int main() { + STATIC_ASSERT((run_tests(), true)); + run_tests(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_set_union/env.lst b/tests/std/tests/P0896R4_ranges_alg_set_union/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_set_union/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_set_union/test.cpp b/tests/std/tests/P0896R4_ranges_alg_set_union/test.cpp new file mode 100644 index 00000000000..3ab9116cfd9 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_set_union/test.cpp @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +// Validate that set_union_result aliases in_in_out_result +STATIC_ASSERT(same_as, ranges::in_in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, ranges::less{}, + identity{}, identity{})), + ranges::set_union_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, ranges::less{}, + identity{}, identity{})), + ranges::set_union_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, ranges::less{}, + identity{}, identity{})), + ranges::set_union_result>); +STATIC_ASSERT(same_as{}, borrowed{}, nullptr_to, ranges::less{}, identity{}, identity{})), + ranges::set_union_result>); + +struct instantiator { + static constexpr P elements1[] = {{0, 10}, {0, 11}, {0, 12}, {1, 10}, {1, 11}, {3, 10}}; + static constexpr P elements2[] = {{13, 0}, {14, 0}, {10, 2}, {11, 3}, {12, 3}}; + static constexpr P expected[] = {{0, 10}, {0, 11}, {0, 12}, {1, 10}, {1, 11}, {10, 2}, {3, 10}, {12, 3}}; + + template + static constexpr void call() { + using ranges::set_union, ranges::set_union_result, ranges::equal, ranges::iterator_t, ranges::less; + + constexpr auto osize = ranges::size(elements1) + ranges::size(elements2); + + { // Validate range overload + P output[osize]{}; + R1 range1{elements1}; + R2 range2{elements2}; + const same_as, iterator_t, O>> auto result = + set_union(range1, range2, O{output}, ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(expected)); + assert(equal(span{output}.first(), expected)); + } + { // Validate iterator overload + P output[osize]{}; + R1 range1{elements1}; + R2 range2{elements2}; + const same_as, iterator_t, O>> auto result = set_union(range1.begin(), + range1.end(), range2.begin(), range2.end(), O{output}, ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(expected)); + assert(equal(span{output}.first(), expected)); + } + + { // Validate range overload, empty range1 + P output[osize]{}; + R1 range1{}; + R2 range2{elements2}; + const same_as, iterator_t, O>> auto result = + set_union(range1, range2, O{output}, ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(elements2)); + assert(equal(span{output}.first(), elements2)); + } + { // Validate iterator overload, empty range2 + P output[osize]{}; + R1 range1{elements1}; + R2 range2{}; + const same_as, iterator_t, O>> auto result = set_union(range1.begin(), + range1.end(), range2.begin(), range2.end(), O{output}, ranges::less{}, get_first, get_second); + assert(result.in1 == range1.end()); + assert(result.in2 == range2.end()); + assert(result.out.peek() == output + ranges::size(elements1)); + assert(equal(span{output}.first(), elements1)); + } + } +}; + +template +struct generate_readable_ranges { + template + static constexpr void call() { + using namespace test; + using test::range; + + // The algorithm is completely oblivious to: + // * categories stronger than input + // * whether the end sentinel is an iterator + // * size information + // * iterator and/or sentinel differencing + // so let's vary proxyness for coverage and call it good. + + Continuation::template call>(); + Continuation::template call>(); + } +}; + +template +struct generate_writable_iterators { + template + static constexpr void call() { + using namespace test; + using test::iterator; + + // The algorithm is completely oblivious to all properties except for proxyness, + // so again we'll vary that property, and we'll also get coverage from input iterators to ensure the algorithm + // doesn't inadvertently depend on the output_iterator-only `*i++ = meow` expression. + + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + } +}; + +constexpr void run_tests() { + generate_readable_ranges>>::call(); +} + +int main() { + STATIC_ASSERT((run_tests(), true)); + run_tests(); +} diff --git a/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp index ff3beb645d5..ae315b2206c 100644 --- a/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp @@ -767,6 +767,135 @@ namespace permutable_test { STATIC_ASSERT(permutable>); } // namespace permutable_test +namespace mergeable_test { + using ranges::less; + using std::identity, std::indirect_strict_weak_order, std::permutable, std::projected, std::mergeable; + + enum class readable_status { not_input_iter, good }; + + template + struct readable_archetype { + using value_type = T; + using difference_type = int; + + T const& operator*() const; + readable_archetype& operator++(); + + // clang-format off + // 0: not input_iterator + void operator++(int) requires (RS != readable_status::not_input_iter); + // clang-format on + }; + + enum class writable_status { not_weakly_incrementable, not_ind_copy_int, not_ind_copy_long, good }; + + template + struct writable_archetype { + using difference_type = int; + + writable_archetype& operator*(); + writable_archetype& operator++(); + + // clang-format off + writable_archetype operator++(int) requires (WS != writable_status::not_weakly_incrementable); + + // 1: not indirectly_copyable + void operator=(int) requires (WS == writable_status::not_ind_copy_int) = delete; + writable_archetype& operator=(int) requires (WS != writable_status::not_ind_copy_int); + + // 2: not indirectly_copyable + void operator=(long) requires (WS == writable_status::not_ind_copy_long) = delete; + writable_archetype& operator=(long) requires (WS != writable_status::not_ind_copy_long); + // clang-format on + }; + + void test() { + using std::indirect_strict_weak_order, std::indirectly_copyable, std::input_iterator, std::mergeable, + std::weakly_incrementable; + + using I1 = readable_archetype; + using I2 = readable_archetype; + using O = writable_archetype; + using Pr = ranges::less; + using Pj1 = std::identity; + using Pj2 = std::identity; + + { + using Bad_I1 = readable_archetype; + STATIC_ASSERT(!input_iterator); + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(weakly_incrementable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirect_strict_weak_order, projected>); + STATIC_ASSERT(!mergeable); + } + + { + using Bad_I2 = readable_archetype; + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(!input_iterator); + STATIC_ASSERT(weakly_incrementable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirect_strict_weak_order, projected>); + STATIC_ASSERT(!mergeable); + } + + { + using Bad_O = writable_archetype; + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(!weakly_incrementable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirect_strict_weak_order, projected>); + STATIC_ASSERT(!mergeable); + } + + { + using Bad_O = writable_archetype; + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(weakly_incrementable); + STATIC_ASSERT(!indirectly_copyable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirect_strict_weak_order, projected>); + STATIC_ASSERT(!mergeable); + } + + { + using Bad_O = writable_archetype; + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(weakly_incrementable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(!indirectly_copyable); + STATIC_ASSERT(indirect_strict_weak_order, projected>); + STATIC_ASSERT(!mergeable); + } + + { + using Bad_Pr = int; + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(weakly_incrementable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(!indirect_strict_weak_order, projected>); + STATIC_ASSERT(!mergeable); + } + + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(input_iterator); + STATIC_ASSERT(weakly_incrementable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirectly_copyable); + STATIC_ASSERT(indirect_strict_weak_order, projected>); + STATIC_ASSERT(mergeable); + } +} // namespace mergeable_test + namespace sortable_test { using ranges::less; using std::identity, std::indirect_strict_weak_order, std::permutable, std::projected, std::sortable;