/// \file // Range v3 library // // Copyright Eric Niebler 2014-present // Copyright Casey Carter 2016 // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Project home: https://github.com/ericniebler/range-v3 // #ifndef RANGES_V3_ALGORITHM_SAMPLE_HPP #define RANGES_V3_ALGORITHM_SAMPLE_HPP #include #include #include #include #include #include #include #include #include #include #include #include RANGES_DISABLE_WARNINGS namespace ranges { /// \addtogroup group-algorithms /// @{ template using sample_result = detail::in_out_result; /// \cond namespace detail { template auto sample_sized_impl(I first, S last, iter_difference_t pop_size, O out, iter_difference_t sample_size, Gen && gen) -> sample_result { if(pop_size > 0 && sample_size > 0) { std::uniform_int_distribution> dist; using param_t = typename decltype(dist)::param_type; for(; first != last; ++first) { if(sample_size >= pop_size) return copy_n(std::move(first), pop_size, std::move(out)); if(dist(gen, param_t{0, --pop_size}) < sample_size) { *out = *first; ++out; if(--sample_size == 0) break; } } } return {std::move(first), std::move(out)}; } } // namespace detail /// \endcond RANGES_BEGIN_NIEBLOID(sample) /// \brief function template \c sample template auto RANGES_FUN_NIEBLOID(sample)(I first, S last, O out, iter_difference_t const n, Gen && gen = detail::get_random_engine()) // ->CPP_ret(sample_result)( // requires input_iterator && sentinel_for && weakly_incrementable && indirectly_copyable && uniform_random_bit_generator> && (random_access_iterator || forward_iterator || sized_sentinel_for)) { if(RANGES_CONSTEXPR_IF(forward_iterator || sized_sentinel_for)) { auto const k = distance(first, last); return detail::sample_sized_impl(std::move(first), std::move(last), k, std::move(out), n, static_cast(gen)); } else { // out is random-access here; calls to advance(out,n) and // next(out,n) are O(1). if(n > 0) { for(iter_difference_t i = 0; i < n; (void)++i, ++first) { if(first == last) { advance(out, i); goto done; } *next(out, i) = *first; } std::uniform_int_distribution> dist; using param_t = typename decltype(dist)::param_type; for(auto pop_size = n; first != last; (void)++first, ++pop_size) { auto const i = dist(gen, param_t{0, pop_size}); if(i < n) *next(out, i) = *first; } advance(out, n); } done: return {std::move(first), std::move(out)}; } } /// \overload template auto RANGES_FUN_NIEBLOID(sample)(I first, S last, ORng && out, Gen && gen = detail::get_random_engine()) // ->CPP_ret(sample_result>)( // requires input_iterator && sentinel_for && weakly_incrementable> && indirectly_copyable> && uniform_random_bit_generator> && (forward_range || sized_range)&&(random_access_iterator> || forward_iterator || sized_sentinel_for)) { if(RANGES_CONSTEXPR_IF(forward_iterator || sized_sentinel_for)) { auto k = distance(first, last); return detail::sample_sized_impl(std::move(first), std::move(last), k, begin(out), distance(out), static_cast(gen)); } else { return (*this)(std::move(first), std::move(last), begin(out), distance(out), static_cast(gen)); } } /// \overload template auto RANGES_FUN_NIEBLOID(sample)(Rng && rng, O out, iter_difference_t const n, Gen && gen = detail::get_random_engine()) // ->CPP_ret(sample_result, O>)( // requires input_range && weakly_incrementable && indirectly_copyable, O> && uniform_random_bit_generator> && (random_access_iterator || forward_range || sized_range)) { if(RANGES_CONSTEXPR_IF(forward_range || sized_range)) { return detail::sample_sized_impl(begin(rng), end(rng), distance(rng), std::move(out), n, static_cast(gen)); } else { return (*this)( begin(rng), end(rng), std::move(out), n, static_cast(gen)); } } /// \overload template auto RANGES_FUN_NIEBLOID(sample)(IRng && rng, ORng && out, Gen && gen = detail::get_random_engine()) // ->CPP_ret(sample_result, safe_iterator_t>)( // requires input_range && range && indirectly_copyable, iterator_t> && uniform_random_bit_generator> && (random_access_iterator> || forward_range || sized_range)&&(forward_range || sized_range)) { if(RANGES_CONSTEXPR_IF(forward_range || sized_range)) { return detail::sample_sized_impl(begin(rng), end(rng), distance(rng), begin(out), distance(out), static_cast(gen)); } else { return (*this)(begin(rng), end(rng), begin(out), distance(out), static_cast(gen)); } } RANGES_END_NIEBLOID(sample) // Not yet! // namespace cpp20 // { // using ranges::sample_result; // using ranges::sample; // } /// @} } // namespace ranges RANGES_RE_ENABLE_WARNINGS #endif