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