Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:51:09

0001 /* boost random/detail/qrng_base.hpp header file
0002  *
0003  * Copyright Justinas Vygintas Daugmaudis 2010-2018
0004  * Distributed under the Boost Software License, Version 1.0. (See
0005  * accompanying file LICENSE_1_0.txt or copy at
0006  * http://www.boost.org/LICENSE_1_0.txt)
0007  */
0008 
0009 #ifndef BOOST_RANDOM_DETAIL_QRNG_BASE_HPP
0010 #define BOOST_RANDOM_DETAIL_QRNG_BASE_HPP
0011 
0012 #include <stdexcept>
0013 #include <vector>
0014 #include <limits>
0015 
0016 #include <istream>
0017 #include <ostream>
0018 #include <sstream>
0019 
0020 #include <boost/cstdint.hpp>
0021 #include <boost/random/detail/operators.hpp>
0022 
0023 #include <boost/throw_exception.hpp>
0024 
0025 #include <boost/type_traits/integral_constant.hpp>
0026 
0027 #include <boost/random/detail/disable_warnings.hpp>
0028 
0029 //!\file
0030 //!Describes the quasi-random number generator base class template.
0031 
0032 namespace boost {
0033 namespace random {
0034 
0035 namespace qrng_detail {
0036 
0037 // If the seed is a signed integer type, then we need to
0038 // check that the value is positive:
0039 template <typename Integer>
0040 inline void check_seed_sign(const Integer& v, const boost::true_type)
0041 {
0042   if (v < 0)
0043   {
0044     boost::throw_exception( std::range_error("seed must be a positive integer") );
0045   }
0046 }
0047 template <typename Integer>
0048 inline void check_seed_sign(const Integer&, const boost::false_type) {}
0049 
0050 template <typename Integer>
0051 inline void check_seed_sign(const Integer& v)
0052 {
0053   check_seed_sign(v, integral_constant<bool, std::numeric_limits<Integer>::is_signed>());
0054 }
0055 
0056 
0057 template<typename DerivedT, typename LatticeT, typename SizeT>
0058 class qrng_base
0059 {
0060 public:
0061   typedef SizeT size_type;
0062   typedef typename LatticeT::value_type result_type;
0063 
0064   explicit qrng_base(std::size_t dimension)
0065     // Guard against invalid dimensions before creating the lattice
0066     : lattice(prevent_zero_dimension(dimension))
0067     , quasi_state(dimension)
0068   {
0069     derived().seed();
0070   }
0071 
0072   // default copy c-tor is fine
0073 
0074   // default assignment operator is fine
0075 
0076   //!Returns: The dimension of of the quasi-random domain.
0077   //!
0078   //!Throws: nothing.
0079   std::size_t dimension() const { return quasi_state.size(); }
0080 
0081   //!Returns: Returns a successive element of an s-dimensional
0082   //!(s = X::dimension()) vector at each invocation. When all elements are
0083   //!exhausted, X::operator() begins anew with the starting element of a
0084   //!subsequent s-dimensional vector.
0085   //!
0086   //!Throws: range_error.
0087   result_type operator()()
0088   {
0089     return curr_elem != dimension() ? load_cached(): next_state();
0090   }
0091 
0092   //!Fills a range with quasi-random values.
0093   template<typename Iter> void generate(Iter first, Iter last)
0094   {
0095     for (; first != last; ++first)
0096       *first = this->operator()();
0097   }
0098 
0099   //!Effects: Advances *this state as if z consecutive
0100   //!X::operator() invocations were executed.
0101   //!
0102   //!Throws: range_error.
0103   void discard(boost::uintmax_t z)
0104   {
0105     const std::size_t dimension_value = dimension();
0106 
0107     // Compiler knows how to optimize subsequent x / y and x % y
0108     // statements. In fact, gcc does this even at -O1, so don't
0109     // be tempted to "optimize" % via subtraction and multiplication.
0110 
0111     boost::uintmax_t vec_n = z / dimension_value;
0112     std::size_t carry = curr_elem + (z % dimension_value);
0113 
0114     vec_n += carry / dimension_value;
0115     carry  = carry % dimension_value;
0116 
0117     // Avoid overdiscarding by branchlessly correcting the triple
0118     // (D, S + 1, 0) to (D, S, D) (see equality operator)
0119     const bool corr = (!carry) & static_cast<bool>(vec_n);
0120 
0121     // Discards vec_n (with correction) consecutive s-dimensional vectors
0122     discard_vector(vec_n - static_cast<boost::uintmax_t>(corr));
0123 
0124 #ifdef BOOST_MSVC
0125 #pragma warning(push)
0126 // disable unary minus operator applied to an unsigned type,
0127 // result still unsigned.
0128 #pragma warning(disable:4146)
0129 #endif
0130 
0131     // Sets up the proper position of the element-to-read
0132     // curr_elem = carry + corr*dimension_value
0133     curr_elem = carry ^ (-static_cast<std::size_t>(corr) & dimension_value);
0134 
0135 #ifdef BOOST_MSVC
0136 #pragma warning(pop)
0137 #endif
0138   }
0139 
0140   //!Writes the textual representation of the generator to a @c std::ostream.
0141   BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, qrng_base, s)
0142   {
0143     os << s.dimension() << " " << s.seq_count << " " << s.curr_elem;
0144     return os;
0145   }
0146 
0147   //!Reads the textual representation of the generator from a @c std::istream.
0148   BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, qrng_base, s)
0149   {
0150     std::size_t dim;
0151     size_type seed;
0152     boost::uintmax_t z;
0153     if (is >> dim >> std::ws >> seed >> std::ws >> z) // initialize iff success!
0154     {
0155       // Check seed sign before resizing the lattice and/or recomputing state
0156       check_seed_sign(seed);
0157 
0158       if (s.dimension() != prevent_zero_dimension(dim))
0159       {
0160         s.lattice.resize(dim);
0161         s.quasi_state.resize(dim);
0162       }
0163       // Fast-forward to the correct state
0164       s.derived().seed(seed);
0165       if (z != 0) s.discard(z);
0166     }
0167     return is;
0168   }
0169 
0170   //!Returns true if the two generators will produce identical sequences of outputs.
0171   BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(qrng_base, x, y)
0172   {
0173     const std::size_t dimension_value = x.dimension();
0174 
0175     // Note that two generators with different seq_counts and curr_elems can
0176     // produce the same sequence because the generator triple
0177     // (D, S, D) is equivalent to (D, S + 1, 0), where D is dimension, S -- seq_count,
0178     // and the last one is curr_elem.
0179 
0180     return (dimension_value == y.dimension()) &&
0181       // |x.seq_count - y.seq_count| <= 1
0182       !((x.seq_count < y.seq_count ? y.seq_count - x.seq_count : x.seq_count - y.seq_count)
0183           > static_cast<size_type>(1)) &&
0184       // Potential overflows don't matter here, since we've already ascertained
0185       // that sequence counts differ by no more than 1, so if they overflow, they
0186       // can overflow together.
0187       (x.seq_count + (x.curr_elem / dimension_value) == y.seq_count + (y.curr_elem / dimension_value)) &&
0188       (x.curr_elem % dimension_value == y.curr_elem % dimension_value);
0189   }
0190 
0191   //!Returns true if the two generators will produce different sequences of outputs.
0192   BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(qrng_base)
0193 
0194 protected:
0195   typedef std::vector<result_type> state_type;
0196   typedef typename state_type::iterator state_iterator;
0197 
0198   // Getters
0199   size_type curr_seq() const { return seq_count; }
0200 
0201   state_iterator state_begin() { return quasi_state.begin(); }
0202   state_iterator state_end() { return quasi_state.end(); }
0203 
0204   // Setters
0205   void reset_seq(size_type seq)
0206   {
0207     seq_count = seq;
0208     curr_elem = 0u;
0209   }
0210 
0211 private:
0212   DerivedT& derived() throw()
0213   {
0214     return *static_cast<DerivedT * const>(this);
0215   }
0216 
0217   // Load the result from the saved state.
0218   result_type load_cached()
0219   {
0220     return quasi_state[curr_elem++];
0221   }
0222 
0223   result_type next_state()
0224   {
0225     size_type new_seq = seq_count;
0226     if (BOOST_LIKELY(++new_seq > seq_count))
0227     {
0228       derived().compute_seq(new_seq);
0229       reset_seq(new_seq);
0230       return load_cached();
0231     }
0232     boost::throw_exception( std::range_error("qrng_base: next_state") );
0233   }
0234 
0235   // Discards z consecutive s-dimensional vectors,
0236   // and preserves the position of the element-to-read
0237   void discard_vector(boost::uintmax_t z)
0238   {
0239     const boost::uintmax_t max_z = (std::numeric_limits<size_type>::max)() - seq_count;
0240 
0241     // Don't allow seq_count + z overflows here
0242     if (max_z < z)
0243       boost::throw_exception( std::range_error("qrng_base: discard_vector") );
0244 
0245     std::size_t tmp = curr_elem;
0246     derived().seed(static_cast<size_type>(seq_count + z));
0247     curr_elem = tmp;
0248   }
0249 
0250   static std::size_t prevent_zero_dimension(std::size_t dimension)
0251   {
0252     if (dimension == 0)
0253       boost::throw_exception( std::invalid_argument("qrng_base: zero dimension") );
0254     return dimension;
0255   }
0256 
0257   // Member variables are so ordered with the intention
0258   // that the typical memory access pattern would be
0259   // incremental. Moreover, lattice is put before quasi_state
0260   // because we want to construct lattice first. Lattices
0261   // can do some kind of dimension sanity check (as in
0262   // dimension_assert below), and if that fails then we don't
0263   // need to do any more work.
0264 private:
0265   std::size_t curr_elem;
0266   size_type seq_count;
0267 protected:
0268   LatticeT lattice;
0269 private:
0270   state_type quasi_state;
0271 };
0272 
0273 inline void dimension_assert(const char* generator, std::size_t dim, std::size_t maxdim)
0274 {
0275   if (!dim || dim > maxdim)
0276   {
0277     std::ostringstream os;
0278     os << "The " << generator << " quasi-random number generator only supports dimensions in range [1; "
0279       << maxdim << "], but dimension " << dim << " was supplied.";
0280     boost::throw_exception( std::invalid_argument(os.str()) );
0281   }
0282 }
0283 
0284 } // namespace qrng_detail
0285 
0286 } // namespace random
0287 } // namespace boost
0288 
0289 #include <boost/random/detail/enable_warnings.hpp>
0290 
0291 #endif // BOOST_RANDOM_DETAIL_QRNG_BASE_HPP