/// \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