qrng_base.hpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /* boost random/detail/qrng_base.hpp header file
  2. *
  3. * Copyright Justinas Vygintas Daugmaudis 2010-2018
  4. * Distributed under the Boost Software License, Version 1.0. (See
  5. * accompanying file LICENSE_1_0.txt or copy at
  6. * http://www.boost.org/LICENSE_1_0.txt)
  7. */
  8. #ifndef BOOST_RANDOM_DETAIL_QRNG_BASE_HPP
  9. #define BOOST_RANDOM_DETAIL_QRNG_BASE_HPP
  10. #include <stdexcept>
  11. #include <vector>
  12. #include <limits>
  13. #include <istream>
  14. #include <ostream>
  15. #include <sstream>
  16. #include <boost/cstdint.hpp>
  17. #include <boost/random/detail/operators.hpp>
  18. #include <boost/throw_exception.hpp>
  19. #include <boost/mpl/bool.hpp>
  20. #include <boost/random/detail/disable_warnings.hpp>
  21. //!\file
  22. //!Describes the quasi-random number generator base class template.
  23. namespace boost {
  24. namespace random {
  25. namespace qrng_detail {
  26. // If the seed is a signed integer type, then we need to
  27. // check that the value is positive:
  28. template <typename Integer>
  29. inline void check_seed_sign(const Integer& v, const mpl::true_)
  30. {
  31. if (v < 0)
  32. {
  33. boost::throw_exception( std::range_error("seed must be a positive integer") );
  34. }
  35. }
  36. template <typename Integer>
  37. inline void check_seed_sign(const Integer&, const mpl::false_) {}
  38. template <typename Integer>
  39. inline void check_seed_sign(const Integer& v)
  40. {
  41. check_seed_sign(v, mpl::bool_<std::numeric_limits<Integer>::is_signed>());
  42. }
  43. template<typename DerivedT, typename LatticeT, typename SizeT>
  44. class qrng_base
  45. {
  46. public:
  47. typedef SizeT size_type;
  48. typedef typename LatticeT::value_type result_type;
  49. explicit qrng_base(std::size_t dimension)
  50. // Guard against invalid dimensions before creating the lattice
  51. : lattice(prevent_zero_dimension(dimension))
  52. , quasi_state(dimension)
  53. {
  54. derived().seed();
  55. }
  56. // default copy c-tor is fine
  57. // default assignment operator is fine
  58. //!Returns: The dimension of of the quasi-random domain.
  59. //!
  60. //!Throws: nothing.
  61. std::size_t dimension() const { return quasi_state.size(); }
  62. //!Returns: Returns a successive element of an s-dimensional
  63. //!(s = X::dimension()) vector at each invocation. When all elements are
  64. //!exhausted, X::operator() begins anew with the starting element of a
  65. //!subsequent s-dimensional vector.
  66. //!
  67. //!Throws: range_error.
  68. result_type operator()()
  69. {
  70. return curr_elem != dimension() ? load_cached(): next_state();
  71. }
  72. //!Fills a range with quasi-random values.
  73. template<typename Iter> void generate(Iter first, Iter last)
  74. {
  75. for (; first != last; ++first)
  76. *first = this->operator()();
  77. }
  78. //!Effects: Advances *this state as if z consecutive
  79. //!X::operator() invocations were executed.
  80. //!
  81. //!Throws: range_error.
  82. void discard(boost::uintmax_t z)
  83. {
  84. const std::size_t dimension_value = dimension();
  85. // Compiler knows how to optimize subsequent x / y and x % y
  86. // statements. In fact, gcc does this even at -O1, so don't
  87. // be tempted to "optimize" % via subtraction and multiplication.
  88. boost::uintmax_t vec_n = z / dimension_value;
  89. std::size_t carry = curr_elem + (z % dimension_value);
  90. vec_n += carry / dimension_value;
  91. carry = carry % dimension_value;
  92. // Avoid overdiscarding by branchlessly correcting the triple
  93. // (D, S + 1, 0) to (D, S, D) (see equality operator)
  94. const bool corr = (!carry) & static_cast<bool>(vec_n);
  95. // Discards vec_n (with correction) consecutive s-dimensional vectors
  96. discard_vector(vec_n - static_cast<boost::uintmax_t>(corr));
  97. #ifdef BOOST_MSVC
  98. #pragma warning(push)
  99. // disable unary minus operator applied to an unsigned type,
  100. // result still unsigned.
  101. #pragma warning(disable:4146)
  102. #endif
  103. // Sets up the proper position of the element-to-read
  104. // curr_elem = carry + corr*dimension_value
  105. curr_elem = carry ^ (-static_cast<std::size_t>(corr) & dimension_value);
  106. #ifdef BOOST_MSVC
  107. #pragma warning(pop)
  108. #endif
  109. }
  110. //!Writes the textual representation of the generator to a @c std::ostream.
  111. BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, qrng_base, s)
  112. {
  113. os << s.dimension() << " " << s.seq_count << " " << s.curr_elem;
  114. return os;
  115. }
  116. //!Reads the textual representation of the generator from a @c std::istream.
  117. BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, qrng_base, s)
  118. {
  119. std::size_t dim;
  120. size_type seed;
  121. boost::uintmax_t z;
  122. if (is >> dim >> std::ws >> seed >> std::ws >> z) // initialize iff success!
  123. {
  124. // Check seed sign before resizing the lattice and/or recomputing state
  125. check_seed_sign(seed);
  126. if (s.dimension() != prevent_zero_dimension(dim))
  127. {
  128. s.lattice.resize(dim);
  129. s.quasi_state.resize(dim);
  130. }
  131. // Fast-forward to the correct state
  132. s.derived().seed(seed);
  133. if (z != 0) s.discard(z);
  134. }
  135. return is;
  136. }
  137. //!Returns true if the two generators will produce identical sequences of outputs.
  138. BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(qrng_base, x, y)
  139. {
  140. const std::size_t dimension_value = x.dimension();
  141. // Note that two generators with different seq_counts and curr_elems can
  142. // produce the same sequence because the generator triple
  143. // (D, S, D) is equivalent to (D, S + 1, 0), where D is dimension, S -- seq_count,
  144. // and the last one is curr_elem.
  145. return (dimension_value == y.dimension()) &&
  146. // |x.seq_count - y.seq_count| <= 1
  147. !((x.seq_count < y.seq_count ? y.seq_count - x.seq_count : x.seq_count - y.seq_count)
  148. > static_cast<size_type>(1)) &&
  149. // Potential overflows don't matter here, since we've already ascertained
  150. // that sequence counts differ by no more than 1, so if they overflow, they
  151. // can overflow together.
  152. (x.seq_count + (x.curr_elem / dimension_value) == y.seq_count + (y.curr_elem / dimension_value)) &&
  153. (x.curr_elem % dimension_value == y.curr_elem % dimension_value);
  154. }
  155. //!Returns true if the two generators will produce different sequences of outputs.
  156. BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(qrng_base)
  157. protected:
  158. typedef std::vector<result_type> state_type;
  159. typedef typename state_type::iterator state_iterator;
  160. // Getters
  161. size_type curr_seq() const { return seq_count; }
  162. state_iterator state_begin() { return quasi_state.begin(); }
  163. state_iterator state_end() { return quasi_state.end(); }
  164. // Setters
  165. void reset_seq(size_type seq)
  166. {
  167. seq_count = seq;
  168. curr_elem = 0u;
  169. }
  170. private:
  171. DerivedT& derived() throw()
  172. {
  173. return *static_cast<DerivedT * const>(this);
  174. }
  175. // Load the result from the saved state.
  176. result_type load_cached()
  177. {
  178. return quasi_state[curr_elem++];
  179. }
  180. result_type next_state()
  181. {
  182. size_type new_seq = seq_count;
  183. if (BOOST_LIKELY(++new_seq > seq_count))
  184. {
  185. derived().compute_seq(new_seq);
  186. reset_seq(new_seq);
  187. return load_cached();
  188. }
  189. boost::throw_exception( std::range_error("qrng_base: next_state") );
  190. }
  191. // Discards z consecutive s-dimensional vectors,
  192. // and preserves the position of the element-to-read
  193. void discard_vector(boost::uintmax_t z)
  194. {
  195. const boost::uintmax_t max_z = std::numeric_limits<size_type>::max() - seq_count;
  196. // Don't allow seq_count + z overflows here
  197. if (max_z < z)
  198. boost::throw_exception( std::range_error("qrng_base: discard_vector") );
  199. std::size_t tmp = curr_elem;
  200. derived().seed(static_cast<size_type>(seq_count + z));
  201. curr_elem = tmp;
  202. }
  203. static std::size_t prevent_zero_dimension(std::size_t dimension)
  204. {
  205. if (dimension == 0)
  206. boost::throw_exception( std::invalid_argument("qrng_base: zero dimension") );
  207. return dimension;
  208. }
  209. // Member variables are so ordered with the intention
  210. // that the typical memory access pattern would be
  211. // incremental. Moreover, lattice is put before quasi_state
  212. // because we want to construct lattice first. Lattices
  213. // can do some kind of dimension sanity check (as in
  214. // dimension_assert below), and if that fails then we don't
  215. // need to do any more work.
  216. private:
  217. std::size_t curr_elem;
  218. size_type seq_count;
  219. protected:
  220. LatticeT lattice;
  221. private:
  222. state_type quasi_state;
  223. };
  224. inline void dimension_assert(const char* generator, std::size_t dim, std::size_t maxdim)
  225. {
  226. if (!dim || dim > maxdim)
  227. {
  228. std::ostringstream os;
  229. os << "The " << generator << " quasi-random number generator only supports dimensions in range [1; "
  230. << maxdim << "], but dimension " << dim << " was supplied.";
  231. boost::throw_exception( std::invalid_argument(os.str()) );
  232. }
  233. }
  234. } // namespace qrng_detail
  235. } // namespace random
  236. } // namespace boost
  237. #include <boost/random/detail/enable_warnings.hpp>
  238. #endif // BOOST_RANDOM_DETAIL_QRNG_BASE_HPP