/// \file // Range v3 library // // Copyright Eric Niebler 2014 // 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 #include RANGES_DISABLE_WARNINGS namespace ranges { inline namespace v3 { /// \addtogroup group-algorithms /// @{ class sample_fn { template using Constraint = meta::strict_and< InputIterator, Sentinel, WeaklyIncrementable, IndirectlyCopyable, UniformRandomNumberGenerator, ConvertibleTo, difference_type_t>>; template())> static tagged_pair sized_impl(I first, S last, difference_type_t pop_size, O out, difference_type_t n, Gen && gen) { std::uniform_int_distribution> dist; using param_t = typename decltype(dist)::param_type; n = ranges::min(pop_size, n); for (; n > 0 && first != last; ++first) { if (dist(gen, param_t{0, --pop_size}) < n) { --n; *out = *first; ++out; } } return {std::move(first), std::move(out)}; } public: template() || SizedSentinel()) && Constraint())> tagged_pair operator()(I first, S last, O out, difference_type_t n, Gen && gen = detail::get_random_engine()) const { auto k = distance(first, last); return sample_fn::sized_impl(std::move(first), std::move(last), k, std::move(out), n, std::forward(gen)); } template() && !(ForwardIterator() || SizedSentinel()) && Constraint())> tagged_pair operator()(I first, S last, O out, difference_type_t n, Gen && gen = detail::get_random_engine()) const { if (n <= 0) goto done; for (difference_type_t i = 0; i < n; (void)++i, ++first) { if (first == last) { out += i; goto done; } 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) out[i] = *first; } out += n; done: return {std::move(first), std::move(out)}; } template() || SizedSentinel()) && (ForwardRange() || SizedRange()) && Constraint, Gen>())> tagged_pair)> operator()(I first, S last, ORng && out, Gen && gen = detail::get_random_engine()) const { auto k = distance(first, last); return sample_fn::sized_impl(std::move(first), std::move(last), k, begin(out), distance(out), std::forward(gen)); } template>() && !(ForwardIterator() || SizedSentinel()) && (ForwardRange() || SizedRange()) && Constraint, Gen>())> tagged_pair)> operator()(I first, S last, ORng && out, Gen && gen = detail::get_random_engine()) const { return (*this)(std::move(first), std::move(last), begin(out), distance(out), std::forward(gen)); } template() && !(ForwardRange() || SizedRange()) && Constraint, sentinel_t, O, Gen>())> tagged_pair), tag::out(O)> operator()(Rng && rng, O out, range_difference_type_t n, Gen && gen = detail::get_random_engine()) const { return (*this)(begin(rng), end(rng), std::move(out), n, std::forward(gen)); } template() || SizedRange()) && Constraint, sentinel_t, O, Gen>())> tagged_pair), tag::out(O)> operator()(Rng && rng, O out, range_difference_type_t n, Gen && gen = detail::get_random_engine()) const { return sample_fn::sized_impl(begin(rng), end(rng), distance(rng), std::move(out), n, std::forward(gen)); } template>() && !(ForwardRange() || SizedRange()) && (ForwardRange() || SizedRange()) && Constraint, sentinel_t, iterator_t, Gen>())> tagged_pair< tag::in(safe_iterator_t), tag::out(safe_iterator_t)> operator()(IRng && rng, ORng && out, Gen && gen = detail::get_random_engine()) const { return (*this)(begin(rng), end(rng), begin(out), distance(out), std::forward(gen)); } template() || SizedRange()) && (ForwardRange() || SizedRange()) && Constraint, sentinel_t, iterator_t, Gen>())> tagged_pair< tag::in(safe_iterator_t), tag::out(safe_iterator_t)> operator()(IRng && rng, ORng && out, Gen && gen = detail::get_random_engine()) const { return sample_fn::sized_impl(begin(rng), end(rng), distance(rng), begin(out), distance(out), std::forward(gen)); } }; /// \sa `sample_fn` /// \ingroup group-algorithms RANGES_INLINE_VARIABLE(with_braced_init_args, sample) /// @} } // namespace v3 } // namespace ranges RANGES_RE_ENABLE_WARNINGS #endif