/* boost random/subtract_with_carry.hpp header file * * Copyright Jens Maurer 2002 * Distributed under the Boost Software License, Version 1.0. (See * accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * See http://www.boost.org for most recent version including documentation. * * $Id$ * * Revision history * 2002-03-02 created */ #ifndef BOOST_RANDOM_SUBTRACT_WITH_CARRY_HPP #define BOOST_RANDOM_SUBTRACT_WITH_CARRY_HPP #include // std::pow #include #include // std::equal #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace random { namespace detail { struct subtract_with_carry_discard { template static void apply(Engine& eng, boost::uintmax_t z) { typedef typename Engine::result_type IntType; const std::size_t short_lag = Engine::short_lag; const std::size_t long_lag = Engine::long_lag; std::size_t k = eng.k; IntType carry = eng.carry; if(k != 0) { // increment k until it becomes 0. if(k < short_lag) { std::size_t limit = (short_lag - k) < z? short_lag : (k + static_cast(z)); for(std::size_t j = k; j < limit; ++j) { carry = eng.do_update(j, j + long_lag - short_lag, carry); } } std::size_t limit = (long_lag - k) < z? long_lag : (k + static_cast(z)); std::size_t start = (k < short_lag ? short_lag : k); for(std::size_t j = start; j < limit; ++j) { carry = eng.do_update(j, j - short_lag, carry); } } k = ((z % long_lag) + k) % long_lag; if(k < z) { // main loop: update full blocks from k = 0 to long_lag for(std::size_t i = 0; i < (z - k) / long_lag; ++i) { for(std::size_t j = 0; j < short_lag; ++j) { carry = eng.do_update(j, j + long_lag - short_lag, carry); } for(std::size_t j = short_lag; j < long_lag; ++j) { carry = eng.do_update(j, j - short_lag, carry); } } // Update the last partial block std::size_t limit = short_lag < k? short_lag : k; for(std::size_t j = 0; j < limit; ++j) { carry = eng.do_update(j, j + long_lag - short_lag, carry); } for(std::size_t j = short_lag; j < k; ++j) { carry = eng.do_update(j, j - short_lag, carry); } } eng.carry = carry; eng.k = k; } }; } /** * Instantiations of @c subtract_with_carry_engine model a * \pseudo_random_number_generator. The algorithm is * described in * * @blockquote * "A New Class of Random Number Generators", George * Marsaglia and Arif Zaman, Annals of Applied Probability, * Volume 1, Number 3 (1991), 462-480. * @endblockquote */ template class subtract_with_carry_engine { public: typedef IntType result_type; BOOST_STATIC_CONSTANT(std::size_t, word_size = w); BOOST_STATIC_CONSTANT(std::size_t, long_lag = r); BOOST_STATIC_CONSTANT(std::size_t, short_lag = s); BOOST_STATIC_CONSTANT(uint32_t, default_seed = 19780503u); // Required by the old Boost.Random concepts BOOST_STATIC_CONSTANT(bool, has_fixed_range = false); // Backwards compatibility BOOST_STATIC_CONSTANT(result_type, modulus = (result_type(1) << w)); BOOST_STATIC_ASSERT(std::numeric_limits::is_integer); /** * Constructs a new @c subtract_with_carry_engine and seeds * it with the default seed. */ subtract_with_carry_engine() { seed(); } /** * Constructs a new @c subtract_with_carry_engine and seeds * it with @c value. */ BOOST_RANDOM_DETAIL_ARITHMETIC_CONSTRUCTOR(subtract_with_carry_engine, IntType, value) { seed(value); } /** * Constructs a new @c subtract_with_carry_engine and seeds * it with values produced by @c seq.generate(). */ BOOST_RANDOM_DETAIL_SEED_SEQ_CONSTRUCTOR(subtract_with_carry_engine, SeedSeq, seq) { seed(seq); } /** * Constructs a new @c subtract_with_carry_engine and seeds * it with values from a range. first is updated to point * one past the last value consumed. If there are not * enough elements in the range to fill the entire state of * the generator, throws @c std::invalid_argument. */ template subtract_with_carry_engine(It& first, It last) { seed(first,last); } // compiler-generated copy ctor and assignment operator are fine /** Seeds the generator with the default seed. */ void seed() { seed(default_seed); } BOOST_RANDOM_DETAIL_ARITHMETIC_SEED(subtract_with_carry_engine, IntType, value) { typedef linear_congruential_engine gen_t; gen_t intgen(static_cast(value == 0 ? default_seed : value)); detail::generator_seed_seq gen(intgen); seed(gen); } /** Seeds the generator with values produced by @c seq.generate(). */ BOOST_RANDOM_DETAIL_SEED_SEQ_SEED(subtract_with_carry, SeedSeq, seq) { detail::seed_array_int(seq, x); carry = (x[long_lag-1] == 0); k = 0; } /** * Seeds the generator with values from a range. Updates @c first to * point one past the last consumed value. If the range does not * contain enough elements to fill the entire state of the generator, * throws @c std::invalid_argument. */ template void seed(It& first, It last) { detail::fill_array_int(first, last, x); carry = (x[long_lag-1] == 0); k = 0; } /** Returns the smallest value that the generator can produce. */ static BOOST_CONSTEXPR result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () { return 0; } /** Returns the largest value that the generator can produce. */ static BOOST_CONSTEXPR result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () { return boost::low_bits_mask_t::sig_bits; } /** Returns the next value of the generator. */ result_type operator()() { std::size_t short_index = (k < short_lag)? (k + long_lag - short_lag) : (k - short_lag); carry = do_update(k, short_index, carry); IntType result = x[k]; ++k; if(k >= long_lag) k = 0; return result; } /** Advances the state of the generator by @c z. */ void discard(boost::uintmax_t z) { detail::subtract_with_carry_discard::apply(*this, z); } /** Fills a range with random values. */ template void generate(It first, It last) { detail::generate_from_int(*this, first, last); } /** Writes a @c subtract_with_carry_engine to a @c std::ostream. */ BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, subtract_with_carry_engine, f) { for(unsigned int j = 0; j < f.long_lag; ++j) os << f.compute(j) << ' '; os << f.carry; return os; } /** Reads a @c subtract_with_carry_engine from a @c std::istream. */ BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, subtract_with_carry_engine, f) { for(unsigned int j = 0; j < f.long_lag; ++j) is >> f.x[j] >> std::ws; is >> f.carry; f.k = 0; return is; } /** * Returns true if the two generators will produce identical * sequences of values. */ BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(subtract_with_carry_engine, x, y) { for(unsigned int j = 0; j < r; ++j) if(x.compute(j) != y.compute(j)) return false; return true; } /** * Returns true if the two generators will produce different * sequences of values. */ BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(subtract_with_carry_engine) private: /// \cond show_private // returns x(i-r+index), where index is in 0..r-1 IntType compute(unsigned int index) const { return x[(k+index) % long_lag]; } friend struct detail::subtract_with_carry_discard; IntType do_update(std::size_t current, std::size_t short_index, IntType carry) { IntType delta; IntType temp = x[current] + carry; if (x[short_index] >= temp) { // x(n) >= 0 delta = x[short_index] - temp; carry = 0; } else { // x(n) < 0 delta = modulus - temp + x[short_index]; carry = 1; } x[current] = delta; return carry; } /// \endcond // state representation; next output (state) is x(i) // x[0] ... x[k] x[k+1] ... x[long_lag-1] represents // x(i-k) ... x(i) x(i+1) ... x(i-k+long_lag-1) // speed: base: 20-25 nsec // ranlux_4: 230 nsec, ranlux_7: 430 nsec, ranlux_14: 810 nsec // This state representation makes operator== and save/restore more // difficult, because we've already computed "too much" and thus // have to undo some steps to get at x(i-r) etc. // state representation: next output (state) is x(i) // x[0] ... x[k] x[k+1] ... x[long_lag-1] represents // x(i-k) ... x(i) x(i-long_lag+1) ... x(i-k-1) // speed: base 28 nsec // ranlux_4: 370 nsec, ranlux_7: 688 nsec, ranlux_14: 1343 nsec IntType x[long_lag]; std::size_t k; IntType carry; }; #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION // A definition is required even for integral static constants template const bool subtract_with_carry_engine::has_fixed_range; template const IntType subtract_with_carry_engine::modulus; template const std::size_t subtract_with_carry_engine::word_size; template const std::size_t subtract_with_carry_engine::long_lag; template const std::size_t subtract_with_carry_engine::short_lag; template const uint32_t subtract_with_carry_engine::default_seed; #endif // use a floating-point representation to produce values in [0..1) /** * Instantiations of \subtract_with_carry_01_engine model a * \pseudo_random_number_generator. The algorithm is * described in * * @blockquote * "A New Class of Random Number Generators", George * Marsaglia and Arif Zaman, Annals of Applied Probability, * Volume 1, Number 3 (1991), 462-480. * @endblockquote */ template class subtract_with_carry_01_engine { public: typedef RealType result_type; BOOST_STATIC_CONSTANT(bool, has_fixed_range = false); BOOST_STATIC_CONSTANT(std::size_t, word_size = w); BOOST_STATIC_CONSTANT(std::size_t, long_lag = r); BOOST_STATIC_CONSTANT(std::size_t, short_lag = s); BOOST_STATIC_CONSTANT(boost::uint32_t, default_seed = 19780503u); BOOST_STATIC_ASSERT(!std::numeric_limits::is_integer); /** Creates a new \subtract_with_carry_01_engine using the default seed. */ subtract_with_carry_01_engine() { init_modulus(); seed(); } /** Creates a new subtract_with_carry_01_engine and seeds it with value. */ BOOST_RANDOM_DETAIL_ARITHMETIC_CONSTRUCTOR(subtract_with_carry_01_engine, boost::uint32_t, value) { init_modulus(); seed(value); } /** * Creates a new \subtract_with_carry_01_engine and seeds with values * produced by seq.generate(). */ BOOST_RANDOM_DETAIL_SEED_SEQ_CONSTRUCTOR(subtract_with_carry_01_engine, SeedSeq, seq) { init_modulus(); seed(seq); } /** * Creates a new \subtract_with_carry_01_engine and seeds it with values * from a range. Advances first to point one past the last consumed * value. If the range does not contain enough elements to fill the * entire state, throws @c std::invalid_argument. */ template subtract_with_carry_01_engine(It& first, It last) { init_modulus(); seed(first,last); } private: /// \cond show_private void init_modulus() { #ifndef BOOST_NO_STDC_NAMESPACE // allow for Koenig lookup using std::pow; #endif _modulus = pow(RealType(2), RealType(word_size)); } /// \endcond public: // compiler-generated copy ctor and assignment operator are fine /** Seeds the generator with the default seed. */ void seed() { seed(default_seed); } /** Seeds the generator with @c value. */ BOOST_RANDOM_DETAIL_ARITHMETIC_SEED(subtract_with_carry_01_engine, boost::uint32_t, value) { typedef linear_congruential_engine gen_t; gen_t intgen(value == 0 ? default_seed : value); detail::generator_seed_seq gen(intgen); seed(gen); } /** Seeds the generator with values produced by @c seq.generate(). */ BOOST_RANDOM_DETAIL_SEED_SEQ_SEED(subtract_with_carry_01_engine, SeedSeq, seq) { detail::seed_array_real(seq, x); carry = (x[long_lag-1] ? result_type(0) : result_type(1 / _modulus)); k = 0; } /** * Seeds the generator with values from a range. Updates first to * point one past the last consumed element. If there are not * enough elements in the range to fill the entire state, throws * @c std::invalid_argument. */ template void seed(It& first, It last) { detail::fill_array_real(first, last, x); carry = (x[long_lag-1] ? result_type(0) : result_type(1 / _modulus)); k = 0; } /** Returns the smallest value that the generator can produce. */ static BOOST_CONSTEXPR result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () { return result_type(0); } /** Returns the largest value that the generator can produce. */ static BOOST_CONSTEXPR result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () { return result_type(1); } /** Returns the next value of the generator. */ result_type operator()() { std::size_t short_index = (k < short_lag) ? (k + long_lag - short_lag) : (k - short_lag); carry = do_update(k, short_index, carry); RealType result = x[k]; ++k; if(k >= long_lag) k = 0; return result; } /** Advances the state of the generator by @c z. */ void discard(boost::uintmax_t z) { detail::subtract_with_carry_discard::apply(*this, z); } /** Fills a range with random values. */ template void generate(Iter first, Iter last) { detail::generate_from_real(*this, first, last); } /** Writes a \subtract_with_carry_01_engine to a @c std::ostream. */ BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, subtract_with_carry_01_engine, f) { std::ios_base::fmtflags oldflags = os.flags(os.dec | os.fixed | os.left); for(unsigned int j = 0; j < f.long_lag; ++j) os << (f.compute(j) * f._modulus) << ' '; os << (f.carry * f._modulus); os.flags(oldflags); return os; } /** Reads a \subtract_with_carry_01_engine from a @c std::istream. */ BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, subtract_with_carry_01_engine, f) { RealType value; for(unsigned int j = 0; j < long_lag; ++j) { is >> value >> std::ws; f.x[j] = value / f._modulus; } is >> value; f.carry = value / f._modulus; f.k = 0; return is; } /** Returns true if the two generators will produce identical sequences. */ BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(subtract_with_carry_01_engine, x, y) { for(unsigned int j = 0; j < r; ++j) if(x.compute(j) != y.compute(j)) return false; return true; } /** Returns true if the two generators will produce different sequences. */ BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(subtract_with_carry_01_engine) private: /// \cond show_private RealType compute(unsigned int index) const { return x[(k+index) % long_lag]; } friend struct detail::subtract_with_carry_discard; RealType do_update(std::size_t current, std::size_t short_index, RealType carry) { RealType delta = x[short_index] - x[current] - carry; if(delta < 0) { delta += RealType(1); carry = RealType(1)/_modulus; } else { carry = 0; } x[current] = delta; return carry; } /// \endcond std::size_t k; RealType carry; RealType x[long_lag]; RealType _modulus; }; #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION // A definition is required even for integral static constants template const bool subtract_with_carry_01_engine::has_fixed_range; template const std::size_t subtract_with_carry_01_engine::word_size; template const std::size_t subtract_with_carry_01_engine::long_lag; template const std::size_t subtract_with_carry_01_engine::short_lag; template const uint32_t subtract_with_carry_01_engine::default_seed; #endif /// \cond show_deprecated template class subtract_with_carry : public subtract_with_carry_engine::value, s, r> { typedef subtract_with_carry_engine::value, s, r> base_type; public: subtract_with_carry() {} BOOST_RANDOM_DETAIL_GENERATOR_CONSTRUCTOR(subtract_with_carry, Gen, gen) { seed(gen); } BOOST_RANDOM_DETAIL_ARITHMETIC_CONSTRUCTOR(subtract_with_carry, IntType, val) { seed(val); } template subtract_with_carry(It& first, It last) : base_type(first, last) {} void seed() { base_type::seed(); } BOOST_RANDOM_DETAIL_GENERATOR_SEED(subtract_with_carry, Gen, gen) { detail::generator_seed_seq seq(gen); base_type::seed(seq); } BOOST_RANDOM_DETAIL_ARITHMETIC_SEED(subtract_with_carry, IntType, val) { base_type::seed(val); } template void seed(It& first, It last) { base_type::seed(first, last); } }; template class subtract_with_carry_01 : public subtract_with_carry_01_engine { typedef subtract_with_carry_01_engine base_type; public: subtract_with_carry_01() {} BOOST_RANDOM_DETAIL_GENERATOR_CONSTRUCTOR(subtract_with_carry_01, Gen, gen) { seed(gen); } BOOST_RANDOM_DETAIL_ARITHMETIC_CONSTRUCTOR(subtract_with_carry_01, uint32_t, val) { seed(val); } template subtract_with_carry_01(It& first, It last) : base_type(first, last) {} void seed() { base_type::seed(); } BOOST_RANDOM_DETAIL_GENERATOR_SEED(subtract_with_carry_01, Gen, gen) { detail::generator_seed_seq seq(gen); base_type::seed(seq); } BOOST_RANDOM_DETAIL_ARITHMETIC_SEED(subtract_with_carry_01, uint32_t, val) { base_type::seed(val); } template void seed(It& first, It last) { base_type::seed(first, last); } }; /// \endcond namespace detail { template struct generator_bits; template struct generator_bits > { static std::size_t value() { return w; } }; template struct generator_bits > { static std::size_t value() { return w; } }; } } // namespace random } // namespace boost #endif // BOOST_RANDOM_SUBTRACT_WITH_CARRY_HPP