libs/random/random_test.cpp
/* boost random_test.cpp various tests * * Copyright Jens Maurer 2000 * 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) * * $Id: random_test.cpp 53871 2009-06-13 17:54:06Z steven_watanabe $ */ #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 #pragma warning( disable : 4786 ) #endif #include <iostream> #include <sstream> #include <string> #include <cmath> #include <iterator> #include <vector> #include <boost/random.hpp> #include <boost/config.hpp> #include <boost/test/test_tools.hpp> #include <boost/test/included/test_exec_monitor.hpp> #ifdef BOOST_NO_STDC_NAMESPACE namespace std { using ::abs; using ::fabs; using ::pow; } #endif /* * General portability note: * MSVC mis-compiles explicit function template instantiations. * For example, f<A>() and f<B>() are both compiled to call f<A>(). * BCC is unable to implicitly convert a "const char *" to a std::string * when using explicit function template instantiations. * * Therefore, avoid explicit function template instantiations. */ /* * A few equidistribution tests */ // yet to come... template<class Generator> void check_uniform_int(Generator & gen, int iter) { std::cout << "testing uniform_int(" << (gen.min)() << "," << (gen.max)() << ")" << std::endl; int range = (gen.max)()-(gen.min)()+1; std::vector<int> bucket(range); for(int j = 0; j < iter; j++) { int result = gen(); if(result < (gen.min)() || result > (gen.max)()) std::cerr << " ... delivers " << result << std::endl; else bucket[result-(gen.min)()]++; } int sum = 0; // use a different variable name "k", because MSVC has broken "for" scoping for(int k = 0; k < range; k++) sum += bucket[k]; double avg = static_cast<double>(sum)/range; double p = 1 / static_cast<double>(range); double threshold = 2*std::sqrt(static_cast<double>(iter)*p*(1-p)); for(int i = 0; i < range; i++) { if(std::fabs(bucket[i] - avg) > threshold) { // 95% confidence interval std::cout << " ... has bucket[" << i << "] = " << bucket[i] << " (distance " << (bucket[i] - avg) << ")" << std::endl; } } } template<class Generator> void test_uniform_int(Generator & gen) { typedef boost::uniform_int<int> int_gen; // large range => small range (modulo case) typedef boost::variate_generator<Generator&, int_gen> level_one; level_one uint12(gen, int_gen(1,2)); BOOST_CHECK((uint12.distribution().min)() == 1); BOOST_CHECK((uint12.distribution().max)() == 2); check_uniform_int(uint12, 100000); level_one uint16(gen, int_gen(1,6)); check_uniform_int(uint16, 100000); // test chaining to get all cases in operator() // identity map typedef boost::variate_generator<level_one&, int_gen> level_two; level_two uint01(uint12, int_gen(0, 1)); check_uniform_int(uint01, 100000); // small range => larger range level_two uint05(uint12, int_gen(-3, 2)); check_uniform_int(uint05, 100000); // small range => larger range level_two uint099(uint12, int_gen(0, 99)); check_uniform_int(uint099, 100000); // larger => small range, rejection case typedef boost::variate_generator<level_two&, int_gen> level_three; level_three uint1_4(uint05, int_gen(1, 4)); check_uniform_int(uint1_4, 100000); typedef boost::uniform_int<boost::uint8_t> int8_gen; typedef boost::variate_generator<Generator&, int8_gen> gen8_t; gen8_t gen8_03(gen, int8_gen(0, 3)); // use the full range of the type, where the destination // range is a power of the source range typedef boost::variate_generator<gen8_t, int8_gen> uniform_uint8; uniform_uint8 uint8_0255(gen8_03, int8_gen(0, 255)); check_uniform_int(uint8_0255, 100000); // use the full range, but a generator whose range is not // a root of the destination range. gen8_t gen8_02(gen, int8_gen(0, 2)); uniform_uint8 uint8_0255_2(gen8_02, int8_gen(0, 255)); check_uniform_int(uint8_0255_2, 100000); // expand the range to a larger type. typedef boost::variate_generator<gen8_t, int_gen> uniform_uint_from8; uniform_uint_from8 uint0300(gen8_03, int_gen(0, 300)); check_uniform_int(uint0300, 100000); } #if defined(BOOST_MSVC) && _MSC_VER < 1300 // These explicit instantiations are necessary, otherwise MSVC does // not find the <boost/operators.hpp> inline friends. // We ease the typing with a suitable preprocessor macro. #define INSTANT(x) \ template class boost::uniform_smallint<x>; \ template class boost::uniform_int<x>; \ template class boost::uniform_real<x>; \ template class boost::bernoulli_distribution<x>; \ template class boost::geometric_distribution<x>; \ template class boost::triangle_distribution<x>; \ template class boost::exponential_distribution<x>; \ template class boost::normal_distribution<x>; \ template class boost::uniform_on_sphere<x>; \ template class boost::lognormal_distribution<x>; INSTANT(boost::minstd_rand0) INSTANT(boost::minstd_rand) INSTANT(boost::ecuyer1988) INSTANT(boost::kreutzer1986) INSTANT(boost::hellekalek1995) INSTANT(boost::mt19937) INSTANT(boost::mt11213b) #undef INSTANT #endif #if !defined(BOOST_NO_INT64_T) && !defined(BOOST_NO_INTEGRAL_INT64_T) // testcase by Mario R�tti class ruetti_gen { public: ruetti_gen() : state((max)() - 1) {} typedef boost::uint64_t result_type; result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 0; } result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return std::numeric_limits<result_type>::max BOOST_PREVENT_MACRO_SUBSTITUTION (); } result_type operator()() { return state--; } private: result_type state; }; void test_overflow_range() { ruetti_gen gen; boost::variate_generator<ruetti_gen, boost::uniform_int<> > rng(gen, boost::uniform_int<>(0, 10)); for (int i=0;i<10;i++) (void) rng(); } #else void test_overflow_range() { } #endif template <typename EngineT> struct rand_for_random_shuffle { explicit rand_for_random_shuffle(EngineT &engine) : m_engine(engine) { } template <typename IntT> IntT operator()(IntT upperBound) { assert(upperBound > 0); if (upperBound == 1) { return 0; } typedef boost::uniform_int<IntT> distribution_type; typedef boost::variate_generator<EngineT &, distribution_type> generator_type; return generator_type(m_engine, distribution_type(0, upperBound - 1))(); } EngineT &m_engine; }; // Test that uniform_int<> can be used with std::random_shuffle // Author: Jos Hickson void test_random_shuffle() { typedef boost::uniform_int<> distribution_type; typedef boost::variate_generator<boost::mt19937 &, distribution_type> generator_type; boost::mt19937 engine1(1234); boost::mt19937 engine2(1234); rand_for_random_shuffle<boost::mt19937> referenceRand(engine1); distribution_type dist(0,10); generator_type testRand(engine2, dist); std::vector<int> referenceVec; for (int i = 0; i < 200; ++i) { referenceVec.push_back(i); } std::vector<int> testVec(referenceVec); std::random_shuffle(referenceVec.begin(), referenceVec.end(), referenceRand); std::random_shuffle(testVec.begin(), testVec.end(), testRand); typedef std::vector<int>::iterator iter_type; iter_type theEnd(referenceVec.end()); for (iter_type referenceIter(referenceVec.begin()), testIter(testVec.begin()); referenceIter != theEnd; ++referenceIter, ++testIter) { BOOST_CHECK_EQUAL(*referenceIter, *testIter); } } int test_main(int, char*[]) { #if !defined(__INTEL_COMPILER) || !defined(_MSC_VER) || __INTEL_COMPILER > 700 boost::mt19937 mt; test_uniform_int(mt); // bug report from Ken Mahler: This used to lead to an endless loop. typedef boost::uniform_int<unsigned int> uint_dist; boost::minstd_rand mr; boost::variate_generator<boost::minstd_rand, uint_dist> r2(mr, uint_dist(0, 0xffffffff)); r2(); r2(); // bug report from Fernando Cacciola: This used to lead to an endless loop. // also from Douglas Gregor boost::variate_generator<boost::minstd_rand, boost::uniform_int<> > x(mr, boost::uniform_int<>(0, 8361)); (void) x(); // bug report from Alan Stokes and others: this throws an assertion boost::variate_generator<boost::minstd_rand, boost::uniform_int<> > y(mr, boost::uniform_int<>(1,1)); std::cout << "uniform_int(1,1) " << y() << ", " << y() << ", " << y() << std::endl; test_overflow_range(); test_random_shuffle(); return 0; #else std::cout << "Intel 7.00 on Win32 loops, so the test is disabled\n"; return 1; #endif }