/// \file // Range v3 library // // 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_VIEW_SAMPLE_HPP #define RANGES_V3_VIEW_SAMPLE_HPP #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { /// \cond namespace detail { template, iterator_t>> class size_tracker { range_difference_t size_; public: CPP_assert(forward_range || sized_range); size_tracker() = default; size_tracker(Rng & rng) : size_(ranges::distance(rng)) {} void decrement() { --size_; } range_difference_t get(Rng &, iterator_t &) const { return size_; } }; // Impl for sized_sentinel_for (no need to store anything) template class size_tracker { public: size_tracker() = default; size_tracker(Rng &) {} void decrement() {} range_difference_t get(Rng & rng, iterator_t const & it) const { return ranges::end(rng) - it; } }; } // namespace detail /// \endcond /// \addtogroup group-views /// @{ // Take a random sampling from another view template class sample_view : public view_facade, finite> { friend range_access; using D = range_difference_t; Rng rng_; // Mutable is OK here because sample_view is an Input view. mutable range_difference_t size_; URNG * engine_; template class cursor { friend cursor; using Base = meta::const_if_c; meta::const_if_c * parent_; iterator_t current_; RANGES_NO_UNIQUE_ADDRESS detail::size_tracker size_; D pop_size() { return size_.get(parent_->rng_, current_); } void advance() { if(parent_->size_ > 0) { using Dist = std::uniform_int_distribution; Dist dist{}; URNG & engine = *parent_->engine_; for(;; ++current_, size_.decrement()) { RANGES_ASSERT(current_ != ranges::end(parent_->rng_)); auto n = pop_size(); RANGES_EXPECT(n > 0); typename Dist::param_type const interval{0, n - 1}; if(dist(engine, interval) < parent_->size_) break; } } } public: using value_type = range_value_t; using difference_type = D; cursor() = default; explicit cursor(meta::const_if_c * rng) : parent_(rng) , current_(ranges::begin(rng->rng_)) , size_{rng->rng_} { auto n = pop_size(); if(rng->size_ > n) rng->size_ = n; advance(); } CPP_template(bool Other)( // requires IsConst && (!Other)) cursor(cursor that) : parent_(that.parent_) , current_(std::move(that.current_)) , size_(that.size_) {} range_reference_t read() const { return *current_; } bool equal(default_sentinel_t) const { RANGES_EXPECT(parent_); return parent_->size_ <= 0; } void next() { RANGES_EXPECT(parent_); RANGES_EXPECT(parent_->size_ > 0); --parent_->size_; RANGES_ASSERT(current_ != ranges::end(parent_->rng_)); ++current_; size_.decrement(); advance(); } }; cursor begin_cursor() { return cursor{this}; } template auto begin_cursor() const -> CPP_ret(cursor)( // requires Const && (sized_range> || sized_sentinel_for>, iterator_t>> || forward_range>)) { return cursor{this}; } public: sample_view() = default; explicit sample_view(Rng rng, D sample_size, URNG & generator) : rng_(std::move(rng)) , size_(sample_size) , engine_(std::addressof(generator)) { RANGES_EXPECT(sample_size >= 0); } Rng base() const { return rng_; } }; #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17 template sample_view(Rng &&, range_difference_t, URNG &) ->sample_view, URNG>; #endif namespace views { /// Returns a random sample of a range of length `size(range)`. struct sample_fn { private: friend view_access; #ifdef RANGES_WORKAROUND_MSVC_OLD_LAMBDA template struct lamduh { Size n; URNG & urng; template auto operator()(Rng && rng) const -> invoke_result_t, URNG &> { return sample_fn{}(static_cast(rng), static_cast>(n), urng); } }; template static auto CPP_fun(bind)(sample_fn, Size n, URNG & urng = detail::get_random_engine())( // requires integral && uniform_random_bit_generator) { return make_pipeable(lamduh{std::move(n), urng}); } #else // ^^^ workaround / no workaround vvv template static auto CPP_fun(bind)(sample_fn, Size n, URNG & urng = detail::get_random_engine())( // requires integral && uniform_random_bit_generator) { return make_pipeable( [n, &urng]( auto && rng) -> invoke_result_t, URNG &> { return sample_fn{}( static_cast(rng), static_cast>(n), urng); }); } #endif // RANGES_WORKAROUND_MSVC_OLD_LAMBDA public: template auto operator()(Rng && rng, range_difference_t sample_size, URNG & generator = detail::get_random_engine()) const -> CPP_ret(sample_view, URNG>)( // requires viewable_range && input_range && uniform_random_bit_generator && convertible_to< invoke_result_t, range_difference_t> && (sized_range || sized_sentinel_for, iterator_t> || forward_range)) { return sample_view, URNG>{ all(static_cast(rng)), sample_size, generator}; } }; /// \relates sample_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(view, sample) } // namespace views /// @} } // namespace ranges #include RANGES_SATISFY_BOOST_RANGE(::ranges::sample_view) #endif