| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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_RANGES_NEXT_PERMUTATION_H | ||
| #define _LIBCPP___ALGORITHM_RANGES_NEXT_PERMUTATION_H | ||
|
|
||
| #include <__algorithm/in_found_result.h> | ||
| #include <__algorithm/iterator_operations.h> | ||
| #include <__algorithm/make_projected.h> | ||
| #include <__algorithm/next_permutation.h> | ||
| #include <__config> | ||
| #include <__functional/identity.h> | ||
| #include <__functional/ranges_operations.h> | ||
| #include <__iterator/concepts.h> | ||
| #include <__iterator/sortable.h> | ||
| #include <__ranges/access.h> | ||
| #include <__ranges/concepts.h> | ||
| #include <__ranges/dangling.h> | ||
| #include <__utility/move.h> | ||
|
|
||
| #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | ||
| # pragma GCC system_header | ||
| #endif | ||
|
|
||
| #if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
|
|
||
| namespace ranges { | ||
|
|
||
| template <class _InIter> | ||
| using next_permutation_result = in_found_result<_InIter>; | ||
|
|
||
| namespace __next_permutation { | ||
|
|
||
| struct __fn { | ||
| template <bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent, class _Comp = ranges::less, class _Proj = identity> | ||
| requires sortable<_Iter, _Comp, _Proj> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr next_permutation_result<_Iter> | ||
| operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { | ||
| auto __result = std::__next_permutation<_RangeAlgPolicy>( | ||
| std::move(__first), std::move(__last), std::__make_projected(__comp, __proj)); | ||
| return {std::move(__result.first), std::move(__result.second)}; | ||
| } | ||
|
|
||
| template <bidirectional_range _Range, class _Comp = ranges::less, class _Proj = identity> | ||
| requires sortable<iterator_t<_Range>, _Comp, _Proj> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr next_permutation_result<borrowed_iterator_t<_Range>> | ||
| operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const { | ||
| auto __result = std::__next_permutation<_RangeAlgPolicy>( | ||
| ranges::begin(__range), ranges::end(__range), std::__make_projected(__comp, __proj)); | ||
| return {std::move(__result.first), std::move(__result.second)}; | ||
| } | ||
| }; | ||
|
|
||
| } // namespace __next_permutation | ||
|
|
||
| inline namespace __cpo { | ||
| constexpr inline auto next_permutation = __next_permutation::__fn{}; | ||
| } // namespace __cpo | ||
| } // namespace ranges | ||
|
|
||
| _LIBCPP_END_NAMESPACE_STD | ||
|
|
||
| #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) | ||
|
|
||
| #endif // _LIBCPP___ALGORITHM_RANGES_NEXT_PERMUTATION_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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_RANGES_PREV_PERMUTATION_H | ||
| #define _LIBCPP___ALGORITHM_RANGES_PREV_PERMUTATION_H | ||
|
|
||
| #include <__algorithm/in_found_result.h> | ||
| #include <__algorithm/iterator_operations.h> | ||
| #include <__algorithm/make_projected.h> | ||
| #include <__algorithm/prev_permutation.h> | ||
| #include <__config> | ||
| #include <__functional/identity.h> | ||
| #include <__functional/ranges_operations.h> | ||
| #include <__iterator/concepts.h> | ||
| #include <__iterator/sortable.h> | ||
| #include <__ranges/access.h> | ||
| #include <__ranges/concepts.h> | ||
| #include <__ranges/dangling.h> | ||
| #include <__utility/move.h> | ||
|
|
||
| #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | ||
| # pragma GCC system_header | ||
| #endif | ||
|
|
||
| #if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
|
|
||
| namespace ranges { | ||
|
|
||
| template <class _InIter> | ||
| using prev_permutation_result = in_found_result<_InIter>; | ||
|
|
||
| namespace __prev_permutation { | ||
|
|
||
| struct __fn { | ||
|
|
||
| template <bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent, | ||
| class _Comp = ranges::less, class _Proj = identity> | ||
| requires sortable<_Iter, _Comp, _Proj> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr prev_permutation_result<_Iter> | ||
| operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { | ||
| auto __result = std::__prev_permutation<_RangeAlgPolicy>( | ||
| std::move(__first), std::move(__last), std::__make_projected(__comp, __proj)); | ||
| return {std::move(__result.first), std::move(__result.second)}; | ||
| } | ||
|
|
||
| template <bidirectional_range _Range, | ||
| class _Comp = ranges::less, class _Proj = identity> | ||
| requires sortable<iterator_t<_Range>, _Comp, _Proj> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr prev_permutation_result<borrowed_iterator_t<_Range>> | ||
| operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const { | ||
| auto __result = std::__prev_permutation<_RangeAlgPolicy>( | ||
| ranges::begin(__range), ranges::end(__range), std::__make_projected(__comp, __proj)); | ||
| return {std::move(__result.first), std::move(__result.second)}; | ||
| } | ||
|
|
||
| }; | ||
|
|
||
| } // namespace __prev_permutation | ||
|
|
||
| inline namespace __cpo { | ||
| constexpr inline auto prev_permutation = __prev_permutation::__fn{}; | ||
| } // namespace __cpo | ||
| } // namespace ranges | ||
|
|
||
| _LIBCPP_END_NAMESPACE_STD | ||
|
|
||
| #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) | ||
|
|
||
| #endif // _LIBCPP___ALGORITHM_RANGES_PREV_PERMUTATION_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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_RANGES_SAMPLE_H | ||
| #define _LIBCPP___ALGORITHM_RANGES_SAMPLE_H | ||
|
|
||
| #include <__algorithm/iterator_operations.h> | ||
| #include <__algorithm/sample.h> | ||
| #include <__algorithm/uniform_random_bit_generator_adaptor.h> | ||
| #include <__config> | ||
| #include <__iterator/concepts.h> | ||
| #include <__iterator/incrementable_traits.h> | ||
| #include <__iterator/iterator_traits.h> | ||
| #include <__random/uniform_random_bit_generator.h> | ||
| #include <__ranges/access.h> | ||
| #include <__ranges/concepts.h> | ||
| #include <__utility/forward.h> | ||
| #include <__utility/move.h> | ||
| #include <type_traits> | ||
|
|
||
| #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | ||
| # pragma GCC system_header | ||
| #endif | ||
|
|
||
| #if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
|
|
||
| namespace ranges { | ||
| namespace __sample { | ||
|
|
||
| struct __fn { | ||
|
|
||
| template <input_iterator _Iter, sentinel_for<_Iter> _Sent, weakly_incrementable _OutIter, class _Gen> | ||
| requires (forward_iterator<_Iter> || random_access_iterator<_OutIter>) && | ||
| indirectly_copyable<_Iter, _OutIter> && | ||
| uniform_random_bit_generator<remove_reference_t<_Gen>> | ||
| _LIBCPP_HIDE_FROM_ABI | ||
| _OutIter operator()(_Iter __first, _Sent __last, | ||
| _OutIter __out_first, iter_difference_t<_Iter> __n, _Gen&& __gen) const { | ||
| _ClassicGenAdaptor<_Gen> __adapted_gen(__gen); | ||
| return std::__sample<_RangeAlgPolicy>( | ||
| std::move(__first), std::move(__last), std::move(__out_first), __n, __adapted_gen); | ||
| } | ||
|
|
||
| template <input_range _Range, weakly_incrementable _OutIter, class _Gen> | ||
| requires (forward_range<_Range> || random_access_iterator<_OutIter>) && | ||
| indirectly_copyable<iterator_t<_Range>, _OutIter> && | ||
| uniform_random_bit_generator<remove_reference_t<_Gen>> | ||
| _LIBCPP_HIDE_FROM_ABI | ||
| _OutIter operator()(_Range&& __range, _OutIter __out_first, range_difference_t<_Range> __n, _Gen&& __gen) const { | ||
| return (*this)(ranges::begin(__range), ranges::end(__range), | ||
| std::move(__out_first), __n, std::forward<_Gen>(__gen)); | ||
| } | ||
|
|
||
| }; | ||
|
|
||
| } // namespace __sample | ||
|
|
||
| inline namespace __cpo { | ||
| inline constexpr auto sample = __sample::__fn{}; | ||
| } // namespace __cpo | ||
| } // namespace ranges | ||
|
|
||
| _LIBCPP_END_NAMESPACE_STD | ||
|
|
||
| #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) | ||
|
|
||
| #endif // _LIBCPP___ALGORITHM_RANGES_SAMPLE_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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_RANGES_UNIFORM_RANDOM_BIT_GENERATOR_ADAPTOR_H | ||
| #define _LIBCPP___ALGORITHM_RANGES_UNIFORM_RANDOM_BIT_GENERATOR_ADAPTOR_H | ||
|
|
||
| #include <__config> | ||
| #include <__functional/invoke.h> | ||
| #include <type_traits> | ||
|
|
||
| #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | ||
| # pragma GCC system_header | ||
| #endif | ||
|
|
||
| #if _LIBCPP_STD_VER > 17 | ||
|
|
||
| _LIBCPP_PUSH_MACROS | ||
| #include <__undef_macros> | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
|
|
||
| // Range versions of random algorithms (e.g. `std::shuffle`) are less constrained than their classic counterparts. | ||
| // Range algorithms only require the given generator to satisfy the `std::uniform_random_bit_generator` concept. | ||
| // Classic algorithms require the given generator to meet the uniform random bit generator requirements; these | ||
| // requirements include satisfying `std::uniform_random_bit_generator` and add a requirement for the generator to | ||
| // provide a nested `result_type` typedef (see `[rand.req.urng]`). | ||
| // | ||
| // To be able to reuse classic implementations, make the given generator meet the classic requirements by wrapping | ||
| // it into an adaptor type that forwards all of its interface and adds the required typedef. | ||
| template <class _Gen> | ||
| class _ClassicGenAdaptor { | ||
| private: | ||
| // The generator is not required to be copyable or movable, so it has to be stored as a reference. | ||
| _Gen& __gen; | ||
|
|
||
| public: | ||
| using result_type = invoke_result_t<_Gen&>; | ||
|
|
||
| _LIBCPP_HIDE_FROM_ABI | ||
| static constexpr auto min() { return __uncvref_t<_Gen>::min(); } | ||
| _LIBCPP_HIDE_FROM_ABI | ||
| static constexpr auto max() { return __uncvref_t<_Gen>::max(); } | ||
|
|
||
| _LIBCPP_HIDE_FROM_ABI | ||
| constexpr explicit _ClassicGenAdaptor(_Gen& __g) : __gen(__g) {} | ||
|
|
||
| _LIBCPP_HIDE_FROM_ABI | ||
| constexpr auto operator()() const { return __gen(); } | ||
| }; | ||
|
|
||
| _LIBCPP_END_NAMESPACE_STD | ||
|
|
||
| _LIBCPP_POP_MACROS | ||
|
|
||
| #endif // _LIBCPP_STD_VER > 17 | ||
|
|
||
| #endif // _LIBCPP___ALGORITHM_RANGES_UNIFORM_RANDOM_BIT_GENERATOR_ADAPTOR_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,334 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // UNSUPPORTED: c++03, c++11, c++14, c++17 | ||
| // UNSUPPORTED: libcpp-has-no-incomplete-ranges | ||
|
|
||
| // <algorithm> | ||
|
|
||
| // template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Gen> | ||
| // requires (forward_iterator<I> || random_access_iterator<O>) && | ||
| // indirectly_copyable<I, O> && | ||
| // uniform_random_bit_generator<remove_reference_t<Gen>> | ||
| // O sample(I first, S last, O out, iter_difference_t<I> n, Gen&& g); // Since C++20 | ||
| // | ||
| // template<input_range R, weakly_incrementable O, class Gen> | ||
| // requires (forward_range<R> || random_access_iterator<O>) && | ||
| // indirectly_copyable<iterator_t<R>, O> && | ||
| // uniform_random_bit_generator<remove_reference_t<Gen>> | ||
| // O sample(R&& r, O out, range_difference_t<R> n, Gen&& g); // Since C++20 | ||
|
|
||
| #include <algorithm> | ||
| #include <array> | ||
| #include <concepts> | ||
| #include <functional> | ||
| #include <random> | ||
| #include <ranges> | ||
| #include <utility> | ||
|
|
||
| #include "almost_satisfies_types.h" | ||
| #include "test_iterators.h" | ||
| #include "test_macros.h" | ||
|
|
||
| class RandGen { | ||
| public: | ||
| constexpr static size_t min() { return 0; } | ||
| constexpr static size_t max() { return 255; } | ||
|
|
||
| constexpr size_t operator()() { | ||
| flip = !flip; | ||
| return flip; | ||
| } | ||
|
|
||
| private: | ||
| bool flip = false; | ||
| }; | ||
|
|
||
| static_assert(std::uniform_random_bit_generator<RandGen>); | ||
| // `std::uniform_random_bit_generator` is a subset of requirements of `__libcpp_random_is_valid_urng`. Make sure that | ||
| // a type satisfying the required minimum is still accepted by `ranges::shuffle`. | ||
| LIBCPP_STATIC_ASSERT(!std::__libcpp_random_is_valid_urng<RandGen>::value); | ||
|
|
||
| struct BadGen { | ||
| constexpr static size_t min() { return 255; } | ||
| constexpr static size_t max() { return 0; } | ||
| constexpr size_t operator()() const; | ||
| }; | ||
| static_assert(!std::uniform_random_bit_generator<BadGen>); | ||
|
|
||
| // Test constraints of the (iterator, sentinel) overload. | ||
| // ====================================================== | ||
|
|
||
| template <class Iter = int*, class Sent = int*, class Out = int*, class Gen = RandGen> | ||
| concept HasSampleIter = | ||
| requires(Iter&& iter, Sent&& sent, Out&& out, std::iter_difference_t<Iter> n, Gen&& gen) { | ||
| std::ranges::sample(std::forward<Iter>(iter), std::forward<Sent>(sent), | ||
| std::forward<Out>(out), n, std::forward<Gen>(gen)); | ||
| }; | ||
|
|
||
| static_assert(HasSampleIter<int*, int*, int*, RandGen>); | ||
|
|
||
| // !input_iterator<I> | ||
| static_assert(!HasSampleIter<InputIteratorNotDerivedFrom>); | ||
| static_assert(!HasSampleIter<InputIteratorNotIndirectlyReadable>); | ||
| static_assert(!HasSampleIter<InputIteratorNotInputOrOutputIterator>); | ||
|
|
||
| // !sentinel_for<S, I> | ||
| static_assert(!HasSampleIter<int*, SentinelForNotSemiregular>); | ||
| static_assert(!HasSampleIter<int*, SentinelForNotWeaklyEqualityComparableWith>); | ||
|
|
||
| // !weakly_incrementable<O> | ||
| static_assert(!HasSampleIter<int*, int*, WeaklyIncrementableNotMovable>); | ||
|
|
||
| // (forward_iterator<I> || random_access_iterator<O>) | ||
| static_assert(HasSampleIter< | ||
| forward_iterator<int*>, forward_iterator<int*>, | ||
| cpp20_output_iterator<int*> | ||
| >); | ||
| static_assert(HasSampleIter< | ||
| cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>, | ||
| random_access_iterator<int*> | ||
| >); | ||
| // !(forward_iterator<I> || random_access_iterator<O>) | ||
| static_assert(!HasSampleIter< | ||
| cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>, | ||
| cpp20_output_iterator<int*> | ||
| >); | ||
|
|
||
| // !indirectly_copyable<I, O> | ||
| static_assert(!HasSampleIter<int*, int*, int**>); | ||
|
|
||
| // !uniform_random_bit_generator<remove_reference_t<Gen>> | ||
| static_assert(!HasSampleIter<int*, int*, int*, BadGen>); | ||
|
|
||
| // Test constraints of the (range) overload. | ||
| // ========================================= | ||
|
|
||
| template <class Range, class Out = int*, class Gen = RandGen> | ||
| concept HasSampleRange = | ||
| requires(Range&& range, Out&& out, std::ranges::range_difference_t<Range> n, Gen&& gen) { | ||
| std::ranges::sample(std::forward<Range>(range), std::forward<Out>(out), n, std::forward<Gen>(gen)); | ||
| }; | ||
|
|
||
| template <class T> | ||
| using R = UncheckedRange<T>; | ||
|
|
||
| static_assert(HasSampleRange<R<int*>, int*, RandGen>); | ||
|
|
||
| // !input_range<R> | ||
| static_assert(!HasSampleRange<InputRangeNotDerivedFrom>); | ||
| static_assert(!HasSampleRange<InputRangeNotIndirectlyReadable>); | ||
| static_assert(!HasSampleRange<InputRangeNotInputOrOutputIterator>); | ||
|
|
||
| // !weakly_incrementable<O> | ||
| static_assert(!HasSampleRange<R<int*>, WeaklyIncrementableNotMovable>); | ||
|
|
||
| // (forward_range<R> || random_access_iterator<O>) | ||
| static_assert(HasSampleRange< | ||
| R<forward_iterator<int*>>, | ||
| cpp20_output_iterator<int*> | ||
| >); | ||
| static_assert(HasSampleRange< | ||
| R<cpp20_input_iterator<int*>>, | ||
| random_access_iterator<int*> | ||
| >); | ||
| // !(forward_range<R> || random_access_iterator<O>) | ||
| static_assert(!HasSampleRange< | ||
| R<cpp20_input_iterator<int*>>, | ||
| cpp20_output_iterator<int*> | ||
| >); | ||
|
|
||
| // !indirectly_copyable<I, O> | ||
| static_assert(!HasSampleRange<R<int*>, int**>); | ||
|
|
||
| // !uniform_random_bit_generator<remove_reference_t<Gen>> | ||
| static_assert(!HasSampleRange<R<int*>, int*, BadGen>); | ||
|
|
||
| template <class Iter, class Sent, class Out, size_t N, class Gen> | ||
| void test_one(std::array<int, N> in, size_t n, Gen gen) { | ||
| assert(n <= static_cast<size_t>(N)); | ||
|
|
||
| auto verify_is_subsequence = [&] (auto output) { | ||
| auto sorted_input = in; | ||
| std::ranges::sort(sorted_input); | ||
| auto sorted_output = std::ranges::subrange(output.begin(), output.begin() + n); | ||
| std::ranges::sort(sorted_output); | ||
| assert(std::ranges::includes(sorted_input, sorted_output)); | ||
| }; | ||
|
|
||
| { // (iterator, sentinel) overload. | ||
| auto begin = Iter(in.data()); | ||
| auto end = Sent(Iter(in.data() + in.size())); | ||
| std::array<int, N> output; | ||
| auto out = Out(output.begin()); | ||
|
|
||
| std::same_as<Out> decltype(auto) result = std::ranges::sample( | ||
| std::move(begin), std::move(end), std::move(out), n, gen); | ||
| assert(base(result) == output.data() + n); | ||
| verify_is_subsequence(output); | ||
| // The output of `sample` is implementation-specific. | ||
| } | ||
|
|
||
| { // (range) overload. | ||
| auto begin = Iter(in.data()); | ||
| auto end = Sent(Iter(in.data() + in.size())); | ||
| std::array<int, N> output; | ||
| auto out = Out(output.begin()); | ||
|
|
||
| std::same_as<Out> decltype(auto) result = std::ranges::sample(std::ranges::subrange( | ||
| std::move(begin), std::move(end)), std::move(out), n, gen); | ||
| assert(base(result) == output.data() + n); | ||
| verify_is_subsequence(output); | ||
| // The output of `sample` is implementation-specific. | ||
| } | ||
| } | ||
|
|
||
| template <class Iter, class Sent, class Out> | ||
| void test_iterators_iter_sent_out() { | ||
| RandGen gen; | ||
|
|
||
| // Empty sequence. | ||
| test_one<Iter, Sent, Out, 0>({}, 0, gen); | ||
| // 1-element sequence. | ||
| test_one<Iter, Sent, Out, 1>({1}, 1, gen); | ||
| // 2-element sequence. | ||
| test_one<Iter, Sent, Out, 2>({1, 2}, 1, gen); | ||
| test_one<Iter, Sent, Out, 2>({1, 2}, 2, gen); | ||
| // n == 0. | ||
| test_one<Iter, Sent, Out, 3>({1, 2, 3}, 0, gen); | ||
|
|
||
| // Longer sequence. | ||
| { | ||
| std::array input = {1, 8, 2, 3, 4, 6, 5, 7}; | ||
| for (int i = 0; i <= static_cast<int>(input.size()); ++i){ | ||
| test_one<Iter, Sent, Out, input.size()>(input, i, gen); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| template <class Iter, class Sent> | ||
| void test_iterators_iter_sent() { | ||
| if constexpr (std::forward_iterator<Iter>) { | ||
| test_iterators_iter_sent_out<Iter, Sent, cpp20_output_iterator<int*>>(); | ||
| test_iterators_iter_sent_out<Iter, Sent, forward_iterator<int*>>(); | ||
| } | ||
| test_iterators_iter_sent_out<Iter, Sent, random_access_iterator<int*>>(); | ||
| test_iterators_iter_sent_out<Iter, Sent, contiguous_iterator<int*>>(); | ||
| test_iterators_iter_sent_out<Iter, Sent, int*>(); | ||
| } | ||
|
|
||
| template <class Iter> | ||
| void test_iterators_iter() { | ||
| if constexpr (std::sentinel_for<Iter, Iter>) { | ||
| test_iterators_iter_sent<Iter, Iter>(); | ||
| } | ||
| test_iterators_iter_sent<Iter, sentinel_wrapper<Iter>>(); | ||
| } | ||
|
|
||
| void test_iterators() { | ||
| test_iterators_iter<cpp20_input_iterator<int*>>(); | ||
| test_iterators_iter<random_access_iterator<int*>>(); | ||
| test_iterators_iter<contiguous_iterator<int*>>(); | ||
| test_iterators_iter<int*>(); | ||
| test_iterators_iter<const int*>(); | ||
| } | ||
|
|
||
| // Checks the logic for wrapping the given iterator to make sure it works correctly regardless of the value category of | ||
| // the given generator object. | ||
| template <class Gen, bool CheckConst = true> | ||
| void test_generator() { | ||
| std::array in = {1, 2, 3, 4, 5, 6, 7, 8}; | ||
| constexpr int N = 5; | ||
| std::array<int, N> output; | ||
| auto begin = in.begin(); | ||
| auto end = in.end(); | ||
| auto out = output.begin(); | ||
|
|
||
| { // Lvalue. | ||
| Gen g; | ||
| std::ranges::sample(begin, end, out, N, g); | ||
| std::ranges::sample(in, out, N, g); | ||
| } | ||
|
|
||
| if constexpr (CheckConst) { // Const lvalue. | ||
| const Gen g; | ||
| std::ranges::sample(begin, end, out, N, g); | ||
| std::ranges::sample(in, out, N, g); | ||
| } | ||
|
|
||
| { // Prvalue. | ||
| std::ranges::sample(begin, end, out, N, Gen()); | ||
| std::ranges::sample(in, out, N, Gen()); | ||
| } | ||
|
|
||
| { // Xvalue. | ||
| Gen g1, g2; | ||
| std::ranges::sample(begin, end, out, N, std::move(g1)); | ||
| std::ranges::sample(in, out, N, std::move(g2)); | ||
| } | ||
| } | ||
|
|
||
| // Checks the logic for wrapping the given iterator to make sure it works correctly regardless of whether the given | ||
| // generator class has a const or non-const invocation operator (or both). | ||
| void test_generators() { | ||
| struct GenBase { | ||
| constexpr static size_t min() { return 0; } | ||
| constexpr static size_t max() { return 255; } | ||
| }; | ||
| struct NonconstGen : GenBase { | ||
| size_t operator()() { return 1; } | ||
| }; | ||
| struct ConstGen : GenBase { | ||
| size_t operator()() const { return 1; } | ||
| }; | ||
| struct ConstAndNonconstGen : GenBase { | ||
| size_t operator()() { return 1; } | ||
| size_t operator()() const { return 1; } | ||
| }; | ||
|
|
||
| test_generator<ConstGen>(); | ||
| test_generator<NonconstGen, /*CheckConst=*/false>(); | ||
| test_generator<ConstAndNonconstGen>(); | ||
| } | ||
|
|
||
| void test() { | ||
| test_iterators(); | ||
| test_generators(); | ||
|
|
||
| { // Stable (if `I` models `forward_iterator`). | ||
| struct OrderedValue { | ||
| int value; | ||
| int original_order; | ||
| bool operator==(const OrderedValue&) const = default; | ||
| auto operator<=>(const OrderedValue& rhs) const { return value <=> rhs.value; } | ||
| }; | ||
|
|
||
| const std::array<OrderedValue, 8> in = {{ | ||
| {1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7}, {1, 8} | ||
| }}; | ||
|
|
||
| { // (iterator, sentinel) overload. | ||
| std::array<OrderedValue, in.size()> out; | ||
| std::ranges::sample(in.begin(), in.end(), out.begin(), in.size(), RandGen()); | ||
| assert(out == in); | ||
| } | ||
|
|
||
| { // (range) overload. | ||
| std::array<OrderedValue, in.size()> out; | ||
| std::ranges::sample(in, out.begin(), in.size(), RandGen()); | ||
| assert(out == in); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| int main(int, char**) { | ||
| test(); | ||
| // Note: `ranges::sample` is not `constexpr`. | ||
|
|
||
| return 0; | ||
| } |