/// \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
#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();
}
template(bool Other)(
requires IsConst AND CPP_NOT(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(bool Const = true)(
requires Const AND
(sized_range> ||
sized_sentinel_for>,
iterator_t>> ||
forward_range>)) //
cursor begin_cursor() const
{
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_base_fn
{
template(typename Rng, typename URNG = detail::default_random_engine)(
requires viewable_range AND input_range AND
uniform_random_bit_generator AND
convertible_to, range_difference_t> AND
(sized_range ||
sized_sentinel_for, iterator_t> ||
forward_range)) //
sample_view, URNG> operator()(
Rng && rng,
range_difference_t sample_size,
URNG & generator = detail::get_random_engine()) const
{
return sample_view, URNG>{
all(static_cast(rng)), sample_size, generator};
}
/// \cond
template
invoke_result_t, URNG &> //
operator()(
Rng && rng,
range_difference_t sample_size,
detail::reference_wrapper_ r) const
{
return (*this)(static_cast(rng), sample_size, r.get());
}
/// \endcond
};
struct sample_fn : sample_base_fn
{
using sample_base_fn::operator();
template(typename Size, typename URNG = detail::default_random_engine)(
requires integral AND uniform_random_bit_generator)
constexpr auto operator()(
Size n,
URNG & urng = detail::get_random_engine()) const //
{
return make_view_closure(bind_back(
sample_base_fn{}, n, detail::reference_wrapper_(urng)));
}
};
/// \relates sample_fn
/// \ingroup group-views
RANGES_INLINE_VARIABLE(sample_fn, sample)
} // namespace views
/// @}
} // namespace ranges
#include
#include
RANGES_SATISFY_BOOST_RANGE(::ranges::sample_view)
#endif