You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
9.7KB

  1. /// \file
  2. // Range v3 library
  3. //
  4. // Copyright Eric Niebler 2014-present
  5. // Copyright Casey Carter 2016
  6. //
  7. // Use, modification and distribution is subject to the
  8. // Boost Software License, Version 1.0. (See accompanying
  9. // file LICENSE_1_0.txt or copy at
  10. // http://www.boost.org/LICENSE_1_0.txt)
  11. //
  12. // Project home: https://github.com/ericniebler/range-v3
  13. //
  14. #ifndef RANGES_V3_ALGORITHM_SAMPLE_HPP
  15. #define RANGES_V3_ALGORITHM_SAMPLE_HPP
  16. #include <utility>
  17. #include <range/v3/range_fwd.hpp>
  18. #include <range/v3/algorithm/copy_n.hpp>
  19. #include <range/v3/iterator/concepts.hpp>
  20. #include <range/v3/iterator/operations.hpp>
  21. #include <range/v3/iterator/traits.hpp>
  22. #include <range/v3/range/access.hpp>
  23. #include <range/v3/range/concepts.hpp>
  24. #include <range/v3/range/dangling.hpp>
  25. #include <range/v3/range/traits.hpp>
  26. #include <range/v3/utility/random.hpp>
  27. #include <range/v3/utility/static_const.hpp>
  28. RANGES_DISABLE_WARNINGS
  29. namespace ranges
  30. {
  31. /// \addtogroup group-algorithms
  32. /// @{
  33. template<typename I, typename O>
  34. using sample_result = detail::in_out_result<I, O>;
  35. /// \cond
  36. namespace detail
  37. {
  38. template<typename I, typename S, typename O, typename Gen>
  39. auto sample_sized_impl(I first, S last, iter_difference_t<I> pop_size, O out,
  40. iter_difference_t<O> sample_size, Gen && gen)
  41. -> sample_result<I, O>
  42. {
  43. if(pop_size > 0 && sample_size > 0)
  44. {
  45. std::uniform_int_distribution<iter_difference_t<I>> dist;
  46. using param_t = typename decltype(dist)::param_type;
  47. for(; first != last; ++first)
  48. {
  49. if(sample_size >= pop_size)
  50. return copy_n(std::move(first), pop_size, std::move(out));
  51. if(dist(gen, param_t{0, --pop_size}) < sample_size)
  52. {
  53. *out = *first;
  54. ++out;
  55. if(--sample_size == 0)
  56. break;
  57. }
  58. }
  59. }
  60. return {std::move(first), std::move(out)};
  61. }
  62. } // namespace detail
  63. /// \endcond
  64. RANGES_BEGIN_NIEBLOID(sample)
  65. /// \brief function template \c sample
  66. template<typename I,
  67. typename S,
  68. typename O,
  69. typename Gen = detail::default_random_engine &>
  70. auto RANGES_FUN_NIEBLOID(sample)(I first,
  71. S last,
  72. O out,
  73. iter_difference_t<O> const n,
  74. Gen && gen = detail::get_random_engine()) //
  75. ->CPP_ret(sample_result<I, O>)( //
  76. requires input_iterator<I> && sentinel_for<S, I> &&
  77. weakly_incrementable<O> && indirectly_copyable<I, O> &&
  78. uniform_random_bit_generator<std::remove_reference_t<Gen>> &&
  79. (random_access_iterator<O> || forward_iterator<I> ||
  80. sized_sentinel_for<S, I>))
  81. {
  82. if(RANGES_CONSTEXPR_IF(forward_iterator<I> || sized_sentinel_for<S, I>))
  83. {
  84. auto const k = distance(first, last);
  85. return detail::sample_sized_impl(std::move(first),
  86. std::move(last),
  87. k,
  88. std::move(out),
  89. n,
  90. static_cast<Gen &&>(gen));
  91. }
  92. else
  93. {
  94. // out is random-access here; calls to advance(out,n) and
  95. // next(out,n) are O(1).
  96. if(n > 0)
  97. {
  98. for(iter_difference_t<O> i = 0; i < n; (void)++i, ++first)
  99. {
  100. if(first == last)
  101. {
  102. advance(out, i);
  103. goto done;
  104. }
  105. *next(out, i) = *first;
  106. }
  107. std::uniform_int_distribution<iter_difference_t<O>> dist;
  108. using param_t = typename decltype(dist)::param_type;
  109. for(auto pop_size = n; first != last; (void)++first, ++pop_size)
  110. {
  111. auto const i = dist(gen, param_t{0, pop_size});
  112. if(i < n)
  113. *next(out, i) = *first;
  114. }
  115. advance(out, n);
  116. }
  117. done:
  118. return {std::move(first), std::move(out)};
  119. }
  120. }
  121. /// \overload
  122. template<typename I,
  123. typename S,
  124. typename ORng,
  125. typename Gen = detail::default_random_engine &>
  126. auto RANGES_FUN_NIEBLOID(sample)(I first,
  127. S last,
  128. ORng && out,
  129. Gen && gen = detail::get_random_engine()) //
  130. ->CPP_ret(sample_result<I, safe_iterator_t<ORng>>)( //
  131. requires input_iterator<I> && sentinel_for<S, I> &&
  132. weakly_incrementable<iterator_t<ORng>> &&
  133. indirectly_copyable<I, iterator_t<ORng>> &&
  134. uniform_random_bit_generator<std::remove_reference_t<Gen>> &&
  135. (forward_range<ORng> ||
  136. sized_range<ORng>)&&(random_access_iterator<iterator_t<ORng>> ||
  137. forward_iterator<I> || sized_sentinel_for<S, I>))
  138. {
  139. if(RANGES_CONSTEXPR_IF(forward_iterator<I> || sized_sentinel_for<S, I>))
  140. {
  141. auto k = distance(first, last);
  142. return detail::sample_sized_impl(std::move(first),
  143. std::move(last),
  144. k,
  145. begin(out),
  146. distance(out),
  147. static_cast<Gen &&>(gen));
  148. }
  149. else
  150. {
  151. return (*this)(std::move(first),
  152. std::move(last),
  153. begin(out),
  154. distance(out),
  155. static_cast<Gen &&>(gen));
  156. }
  157. }
  158. /// \overload
  159. template<typename Rng, typename O, typename Gen = detail::default_random_engine &>
  160. auto RANGES_FUN_NIEBLOID(sample)(Rng && rng,
  161. O out,
  162. iter_difference_t<O> const n,
  163. Gen && gen = detail::get_random_engine()) //
  164. ->CPP_ret(sample_result<safe_iterator_t<Rng>, O>)( //
  165. requires input_range<Rng> && weakly_incrementable<O> &&
  166. indirectly_copyable<iterator_t<Rng>, O> &&
  167. uniform_random_bit_generator<std::remove_reference_t<Gen>> &&
  168. (random_access_iterator<O> || forward_range<Rng> || sized_range<Rng>))
  169. {
  170. if(RANGES_CONSTEXPR_IF(forward_range<Rng> || sized_range<Rng>))
  171. {
  172. return detail::sample_sized_impl(begin(rng),
  173. end(rng),
  174. distance(rng),
  175. std::move(out),
  176. n,
  177. static_cast<Gen &&>(gen));
  178. }
  179. else
  180. {
  181. return (*this)(
  182. begin(rng), end(rng), std::move(out), n, static_cast<Gen &&>(gen));
  183. }
  184. }
  185. /// \overload
  186. template<typename IRng,
  187. typename ORng,
  188. typename Gen = detail::default_random_engine &>
  189. auto RANGES_FUN_NIEBLOID(sample)(IRng && rng,
  190. ORng && out,
  191. Gen && gen = detail::get_random_engine()) //
  192. ->CPP_ret(sample_result<safe_iterator_t<IRng>, safe_iterator_t<ORng>>)( //
  193. requires input_range<IRng> && range<ORng> &&
  194. indirectly_copyable<iterator_t<IRng>, iterator_t<ORng>> &&
  195. uniform_random_bit_generator<std::remove_reference_t<Gen>> &&
  196. (random_access_iterator<iterator_t<ORng>> || forward_range<IRng> ||
  197. sized_range<IRng>)&&(forward_range<ORng> || sized_range<ORng>))
  198. {
  199. if(RANGES_CONSTEXPR_IF(forward_range<IRng> || sized_range<IRng>))
  200. {
  201. return detail::sample_sized_impl(begin(rng),
  202. end(rng),
  203. distance(rng),
  204. begin(out),
  205. distance(out),
  206. static_cast<Gen &&>(gen));
  207. }
  208. else
  209. {
  210. return (*this)(begin(rng),
  211. end(rng),
  212. begin(out),
  213. distance(out),
  214. static_cast<Gen &&>(gen));
  215. }
  216. }
  217. RANGES_END_NIEBLOID(sample)
  218. // Not yet!
  219. // namespace cpp20
  220. // {
  221. // using ranges::sample_result;
  222. // using ranges::sample;
  223. // }
  224. /// @}
  225. } // namespace ranges
  226. RANGES_RE_ENABLE_WARNINGS
  227. #endif