uniform_int_distribution.hpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. ///////////////////////////////////////////////////////////////
  2. // Copyright Jens Maurer 2000-2021
  3. // Copyright Steven Watanabe 2011-2021
  4. // Copyright John Maddock 2015-2021
  5. // Copyright Matt Borland 2021
  6. // Distributed under the Boost Software License, Version 1.0.
  7. // (See accompanying file LICENSE_1_0.txt or copy at
  8. // https://www.boost.org/LICENSE_1_0.txt
  9. //
  10. // This is a C++11 compliant port of Boost.Random's implementation
  11. // of uniform_int_distribution. See their comments for detailed
  12. // descriptions
  13. #ifndef BOOST_MP_UNIFORM_INT_DISTRIBUTION_HPP
  14. #define BOOST_MP_UNIFORM_INT_DISTRIBUTION_HPP
  15. #include <limits>
  16. #include <type_traits>
  17. #include <boost/multiprecision/detail/standalone_config.hpp>
  18. #include <boost/multiprecision/detail/assert.hpp>
  19. #include <boost/multiprecision/traits/std_integer_traits.hpp>
  20. namespace boost { namespace multiprecision {
  21. namespace detail {
  22. template <typename T, bool intrinsic>
  23. struct make_unsigned_impl
  24. {
  25. using type = typename boost::multiprecision::detail::make_unsigned<T>::type;
  26. };
  27. template <typename T>
  28. struct make_unsigned_impl<T, false>
  29. {
  30. using type = T;
  31. };
  32. template <typename T>
  33. struct make_unsigned_mp
  34. {
  35. using type = typename make_unsigned_impl<T, boost::multiprecision::detail::is_integral<T>::value>::type;
  36. };
  37. template <typename Engine, typename T>
  38. T generate_uniform_int (Engine& eng, T min_value, T max_value)
  39. {
  40. using range_type = typename boost::multiprecision::detail::make_unsigned_mp<T>::type;
  41. using base_result = typename Engine::result_type;
  42. using base_unsigned = typename boost::multiprecision::detail::make_unsigned_mp<base_result>::type;
  43. const range_type range = max_value - min_value;
  44. const base_result bmin = (eng.min)();
  45. const base_unsigned brange = (eng.max)() - (eng.min)();
  46. if(range == 0)
  47. {
  48. return min_value;
  49. }
  50. else if (brange < range)
  51. {
  52. for(;;)
  53. {
  54. range_type limit;
  55. if(range == (std::numeric_limits<range_type>::max)())
  56. {
  57. limit = range / (range_type(brange) + 1);
  58. if(range % (range_type(brange) + 1) == range_type(brange))
  59. {
  60. ++limit;
  61. }
  62. }
  63. else
  64. {
  65. limit = (range + 1) / (range_type(brange) + 1);
  66. }
  67. range_type result = 0;
  68. range_type mult = 1;
  69. while (mult <= limit)
  70. {
  71. result += static_cast<range_type>(static_cast<range_type>(eng() - bmin) * mult);
  72. if(mult * range_type(brange) == range - mult + 1)
  73. {
  74. return(result);
  75. }
  76. mult *= range_type(brange)+range_type(1);
  77. }
  78. range_type result_increment = generate_uniform_int(eng, range_type(0), range_type(range/mult));
  79. if(std::numeric_limits<range_type>::is_bounded && ((std::numeric_limits<range_type>::max)() / mult < result_increment))
  80. {
  81. continue;
  82. }
  83. result_increment *= mult;
  84. result += result_increment;
  85. if(result < result_increment)
  86. {
  87. continue;
  88. }
  89. if(result > range)
  90. {
  91. continue;
  92. }
  93. return result + min_value;
  94. }
  95. }
  96. else
  97. {
  98. using mixed_range_type =
  99. typename std::conditional<std::numeric_limits<range_type>::is_specialized && std::numeric_limits<base_unsigned>::is_specialized &&
  100. (std::numeric_limits<range_type>::digits >= std::numeric_limits<base_unsigned>::digits),
  101. range_type, base_unsigned>::type;
  102. mixed_range_type bucket_size;
  103. if(brange == (std::numeric_limits<base_unsigned>::max)())
  104. {
  105. bucket_size = static_cast<mixed_range_type>(brange) / (static_cast<mixed_range_type>(range)+1);
  106. if(static_cast<mixed_range_type>(brange) % (static_cast<mixed_range_type>(range)+1) == static_cast<mixed_range_type>(range))
  107. {
  108. ++bucket_size;
  109. }
  110. }
  111. else
  112. {
  113. bucket_size = static_cast<mixed_range_type>(brange + 1) / (static_cast<mixed_range_type>(range)+1);
  114. }
  115. for(;;)
  116. {
  117. mixed_range_type result = eng() - bmin;
  118. result /= bucket_size;
  119. if(result <= static_cast<mixed_range_type>(range))
  120. {
  121. return result + min_value;
  122. }
  123. }
  124. }
  125. }
  126. } // Namespace detail
  127. template <typename Integer = int>
  128. class uniform_int_distribution
  129. {
  130. private:
  131. Integer min_;
  132. Integer max_;
  133. public:
  134. class param_type
  135. {
  136. private:
  137. Integer min_;
  138. Integer max_;
  139. public:
  140. explicit param_type(Integer min_val, Integer max_val) : min_ {min_val}, max_ {max_val}
  141. {
  142. BOOST_MP_ASSERT(min_ <= max_);
  143. }
  144. Integer a() const { return min_; }
  145. Integer b() const { return max_; }
  146. };
  147. explicit uniform_int_distribution(Integer min_arg, Integer max_arg) : min_ {min_arg}, max_ {max_arg}
  148. {
  149. BOOST_MP_ASSERT(min_ <= max_);
  150. }
  151. explicit uniform_int_distribution(const param_type& param_arg) : min_ {param_arg.a()}, max_ {param_arg.b()} {}
  152. Integer min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return min_; }
  153. Integer max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return max_; }
  154. Integer a() const { return min_; }
  155. Integer b() const { return max_; }
  156. param_type param() const { return param_type(min_, max_); }
  157. void param(const param_type& param_arg)
  158. {
  159. min_ = param_arg.a();
  160. max_ = param_arg.b();
  161. }
  162. template <typename Engine>
  163. Integer operator() (Engine& eng) const
  164. {
  165. return detail::generate_uniform_int(eng, min_, max_);
  166. }
  167. template <typename Engine>
  168. Integer operator() (Engine& eng, const param_type& param_arg) const
  169. {
  170. return detail::generate_uniform_int(eng, param_arg.a(), param_arg.b());
  171. }
  172. };
  173. }} // Namespaces
  174. #endif // BOOST_MP_UNIFORM_INT_DISTRIBUTION_HPP