From 2e4c0f628ca7ea6b22d3cd8244e9d6580b4a4963 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Sun, 7 Mar 2010 05:22:40 +0000 Subject: [PATCH] Make statistics_tests usable with the current library [SVN r60279] --- extra/Jamfile.v2 | 10 + extra/haertel.hpp | 156 ++++++++++ extra/test_haertel.cpp | 62 ++++ .../boost/random/detail/disable_warnings.hpp | 1 + include/boost/random/uniform_int.hpp | 11 + include/boost/random/uniform_smallint.hpp | 2 +- test/Jamfile.v2 | 5 +- test/statistic_tests.cpp | 292 +++++------------- test/statistic_tests.hpp | 32 +- 9 files changed, 338 insertions(+), 233 deletions(-) create mode 100644 extra/Jamfile.v2 create mode 100644 extra/haertel.hpp create mode 100644 extra/test_haertel.cpp diff --git a/extra/Jamfile.v2 b/extra/Jamfile.v2 new file mode 100644 index 0000000..a0ee2af --- /dev/null +++ b/extra/Jamfile.v2 @@ -0,0 +1,10 @@ +# Jamfile.v2 +# +# Copyright (c) 2009 +# Steven Watanabe +# +# 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) + +run test_haertel.cpp ; diff --git a/extra/haertel.hpp b/extra/haertel.hpp new file mode 100644 index 0000000..c318d80 --- /dev/null +++ b/extra/haertel.hpp @@ -0,0 +1,156 @@ +/* haertel.hpp file + * + * Copyright Jens Maurer 2000, 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) + * + * $Id$ + * + * Revision history + */ + +/* + * NOTE: This is not part of the official boost submission. It exists + * only as a collection of ideas. + */ + +#ifndef BOOST_RANDOM_HAERTEL_HPP +#define BOOST_RANDOM_HAERTEL_HPP + +#include +#include +#include + +namespace boost { +namespace random { + +// Wikramaratna 1989 ACORN +template +class additive_congruential +{ +public: + typedef IntType result_type; +#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION + static const bool has_fixed_range = true; + static const result_type min_value = 0; + static const result_type max_value = m-1; +#else + enum { + has_fixed_range = true, + min_value = 0, + max_value = m-1 + }; +#endif + template + explicit additive_congruential(InputIterator start) { seed(start); } + template + void seed(InputIterator start) + { + for(int i = 0; i <= k; ++i, ++start) + values[i] = *start; + } + + result_type operator()() + { + for(int i = 1; i <= k; ++i) { + IntType tmp = values[i-1] + values[i]; + if(tmp >= m) + tmp -= m; + values[i] = tmp; + } + return values[k]; + } + result_type validation() const { return val; } +private: + IntType values[k+1]; +}; + + +template +class lagged_fibonacci_int +{ +public: + typedef IntType result_type; +#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION + static const bool has_fixed_range = true; + static const result_type min_value = 0; + static const result_type max_value = m-1; +#else + enum { + has_fixed_range = true, + min_value = 0, + max_value = m-1 + }; +#endif + explicit lagged_fibonacci_int(IntType start) { seed(start); } + template + explicit lagged_fibonacci_int(Generator & gen) { seed(gen); } + void seed(IntType start) + { + linear_congruential init; + seed(init); + } + template + void seed(Generator & gen) + { + assert(r > s); + for(int i = 0; i < 607; ++i) + values[i] = gen(); + current = 0; + lag = r-s; + } + + result_type operator()() + { + result_type tmp = values[current] + values[lag]; + if(tmp >= m) + tmp -= m; + values[current] = tmp; + ++current; + if(current >= r) + current = 0; + ++lag; + if(lag >= r) + lag = 0; + return tmp; + } + result_type validation() const { return val; } +private: + result_type values[r]; + int current, lag; +}; + +} // namespace random +} // namespace boost + +// distributions from Haertel's dissertation +// (additional parameterizations of the basic templates) +namespace Haertel { + typedef boost::random::linear_congruential LCG_Af2; + typedef boost::random::linear_congruential LCG_Die1; + typedef boost::random::linear_congruential LCG_Fis; + typedef boost::random::linear_congruential LCG_FM; + typedef boost::random::linear_congruential LCG_Hae; + typedef boost::random::linear_congruential LCG_VAX; + typedef boost::random::inversive_congruential NLG_Inv1; + typedef boost::random::inversive_congruential NLG_Inv2; + typedef boost::random::inversive_congruential NLG_Inv4; + typedef boost::random::inversive_congruential NLG_Inv5; + typedef boost::random::additive_congruential MRG_Acorn7; + typedef boost::random::lagged_fibonacci_int MRG_Fib2; +} // namespace Haertel + +#endif diff --git a/extra/test_haertel.cpp b/extra/test_haertel.cpp new file mode 100644 index 0000000..e5beb26 --- /dev/null +++ b/extra/test_haertel.cpp @@ -0,0 +1,62 @@ +/* haertel.hpp file + * + * Copyright Jens Maurer 2000, 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) + * + * $Id$ + * + * Revision history + */ + +#include +#include + +#include + +#include "haertel.hpp" + +#define BOOST_TEST_MAIN +#include + +template +inline void check_validation(Gen & gen, T value, const std::string & name) +{ + for(int i = 0; i < 100000-1; ++i) + gen(); + + typename Gen::result_type actual = gen(); + BOOST_CHECK_MESSAGE(value == actual, + boost::str(boost::format("%s: check value == gen() failed [%d != %d]") % + name % value % actual)); +} + +// we have validation after 100000 steps with Haertel's generators +template +void validate(T value, const std::string & name) +{ + Gen gen(1234567); + check_validation(gen, value, name); +} + +BOOST_AUTO_TEST_CASE(test_haertel) +{ + using namespace Haertel; + validate(183269031u, "LCG_Af2"); + validate(522319944u, "LCG_Die1"); + validate(-2065162233u, "LCG_Fis"); + validate(581815473u, "LCG_FM"); + validate(28931709, "LCG_Hae"); + validate(1508154087u, "LCG_VAX"); + validate(6666884, "NLG_Inv2"); + validate(1521640076, "NLG_Inv4"); + validate(641840839, "NLG_Inv5"); + static const int acorn7_init[] + = { 1234567, 7654321, 246810, 108642, 13579, 97531, 555555 }; + MRG_Acorn7 acorn7(acorn7_init); + check_validation(acorn7, 874294697, "MRG_Acorn7"); + // This currently fails. I don't want to touch it until + // I trace the source of this generator. --SJW + validate(1234567u, "MRG_Fib2"); +} diff --git a/include/boost/random/detail/disable_warnings.hpp b/include/boost/random/detail/disable_warnings.hpp index 958a83a..f128256 100644 --- a/include/boost/random/detail/disable_warnings.hpp +++ b/include/boost/random/detail/disable_warnings.hpp @@ -19,4 +19,5 @@ #pragma warning(push) #pragma warning(disable:4512) #pragma warning(disable:4127) +#pragma warning(disable:4724) #endif diff --git a/include/boost/random/uniform_int.hpp b/include/boost/random/uniform_int.hpp index 3a2dfbd..2eff53b 100644 --- a/include/boost/random/uniform_int.hpp +++ b/include/boost/random/uniform_int.hpp @@ -115,6 +115,13 @@ public: private: +#ifdef BOOST_MSVC +#pragma warning(push) +// disable division by zero warning, since we can't +// actually divide by zero. +#pragma warning(disable:4723) +#endif + /// \cond hide_private_members template static result_type generate(Engine& eng, result_type min_value, result_type /*max_value*/, range_type range) @@ -271,6 +278,10 @@ private: } } +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif + void init() { _range = random::detail::subtract()(_max, _min); diff --git a/include/boost/random/uniform_smallint.hpp b/include/boost/random/uniform_smallint.hpp index 89c9af8..b28d54f 100644 --- a/include/boost/random/uniform_smallint.hpp +++ b/include/boost/random/uniform_smallint.hpp @@ -126,7 +126,7 @@ public: r_base /= 2; } - return ((eng() - (eng.min)()) / _factor) % _range + _min; + return static_cast(((eng() - (eng.min)()) / _factor) % _range + _min); } #ifndef BOOST_RANDOM_NO_STREAM_OPERATORS diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 350b7a3..750b5a9 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1,4 +1,5 @@ # Copyright 2003 Jens Maurer +# Copyright 2009-2010 Steven Watanabe # Distributed under the Boost Software License, Version 1.0. (See accompany- # ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -43,5 +44,5 @@ for urng in $(all-urngs) # run random_speed.cpp ; # run statistic_tests.cpp ; - - +exe statistic_tests : statistic_tests.cpp ; +explicit statistics_tests ; diff --git a/test/statistic_tests.cpp b/test/statistic_tests.cpp index a53cb5b..cbfbbb2 100644 --- a/test/statistic_tests.cpp +++ b/test/statistic_tests.cpp @@ -10,216 +10,35 @@ * Revision history */ -/* - * NOTE: This is not part of the official boost submission. It exists - * only as a collection of ideas. - */ - #include #include #include #include -#include // lgamma is not in namespace std #include +#include #include #include #include +#include + #include "statistic_tests.hpp" #include "integrate.hpp" -namespace boost { -namespace random { - -// Wikramaratna 1989 ACORN -template -class additive_congruential -{ -public: - typedef IntType result_type; -#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION - static const bool has_fixed_range = true; - static const result_type min_value = 0; - static const result_type max_value = m-1; -#else - enum { - has_fixed_range = true, - min_value = 0, - max_value = m-1 - }; -#endif - template - explicit additive_congruential(InputIterator start) { seed(start); } - template - void seed(InputIterator start) - { - for(int i = 0; i <= k; ++i, ++start) - values[i] = *start; - } - - result_type operator()() - { - for(int i = 1; i <= k; ++i) { - IntType tmp = values[i-1] + values[i]; - if(tmp >= m) - tmp -= m; - values[i] = tmp; - } - return values[k]; - } - result_type validation() const { return val; } -private: - IntType values[k+1]; -}; - - -template -class lagged_fibonacci_int -{ -public: - typedef IntType result_type; -#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION - static const bool has_fixed_range = true; - static const result_type min_value = 0; - static const result_type max_value = m-1; -#else - enum { - has_fixed_range = true, - min_value = 0, - max_value = m-1 - }; -#endif - explicit lagged_fibonacci_int(IntType start) { seed(start); } - template - explicit lagged_fibonacci_int(Generator & gen) { seed(gen); } - void seed(IntType start) - { - linear_congruential init; - seed(init); - } - template - void seed(Generator & gen) - { - assert(r > s); - for(int i = 0; i < 607; ++i) - values[i] = gen(); - current = 0; - lag = r-s; - } - - result_type operator()() - { - result_type tmp = values[current] + values[lag]; - if(tmp >= m) - tmp -= m; - values[current] = tmp; - ++current; - if(current >= r) - current = 0; - ++lag; - if(lag >= r) - lag = 0; - return tmp; - } - result_type validation() const { return val; } -private: - result_type values[r]; - int current, lag; -}; - -} // namespace random -} // namespace boost - -// distributions from Haertel's dissertation -// (additional parameterizations of the basic templates) -namespace Haertel { - typedef boost::random::linear_congruential LCG_Af2; - typedef boost::random::linear_congruential LCG_Die1; - typedef boost::random::linear_congruential LCG_Fis; - typedef boost::random::linear_congruential LCG_FM; - typedef boost::random::linear_congruential LCG_Hae; - typedef boost::random::linear_congruential LCG_VAX; - typedef boost::random::inversive_congruential NLG_Inv1; - typedef boost::random::inversive_congruential NLG_Inv2; - typedef boost::random::inversive_congruential NLG_Inv4; - typedef boost::random::inversive_congruential NLG_Inv5; - typedef boost::random::additive_congruential MRG_Acorn7; - typedef boost::random::lagged_fibonacci_int MRG_Fib2; - - template - inline void check_validation(Gen & gen, T value, const std::string & name) - { - for(int i = 0; i < 100000-1; ++i) - gen(); - if(value != gen()) - std::cout << name << ": validation failed" << std::endl; - } - - // we have validation after 100000 steps with Haertel's generators - template - void validate(T value, const std::string & name) - { - Gen gen(1234567); - check_validation(gen, value, name); - } - - void validate_all() - { - validate(183269031u, "LCG_Af2"); - validate(522319944u, "LCG_Die1"); - validate(-2065162233u, "LCG_Fis"); - validate(581815473u, "LCG_FM"); - validate(28931709, "LCG_Hae"); - validate(1508154087u, "LCG_VAX"); - validate(6666884, "NLG_Inv2"); - validate(1521640076, "NLG_Inv4"); - validate(641840839, "NLG_Inv5"); - static const int acorn7_init[] - = { 1234567, 7654321, 246810, 108642, 13579, 97531, 555555 }; - MRG_Acorn7 acorn7(acorn7_init); - check_validation(acorn7, 874294697, "MRG_Acorn7"); - validate(1234567u, "MRG_Fib2"); - } -} // namespace Haertel - - - - double normal_density(double x) { const double pi = 3.14159265358979323846; return 1/std::sqrt(2*pi) * std::exp(-x*x/2); } -namespace std { -#ifdef _CXXRTCF_H__ - using _CS_swamp::lgamma; -#elif defined __SGI_STL_PORT - using ::lgamma; -#endif -} - - class chi_square_density : public std::unary_function { public: chi_square_density(int freedom) : _exponent( static_cast(freedom)/2-1 ), - _factor(1/(std::pow(2, _exponent+1) * std::exp(lgamma(_exponent+1)))) + _factor(1/(std::pow(2, _exponent+1) * std::exp(boost::math::lgamma(_exponent+1)))) { } double operator()(double x) @@ -288,7 +107,7 @@ public: using namespace boost; std::cout << "equidistribution: " << std::flush; equidistribution_experiment equi(classes); - uniform_smallint uint_linear(rng, 0, classes-1); + variate_generator > uint_linear(rng, uniform_smallint<>(0, classes-1)); check(run_experiment(test_distrib_chi_square, experiment_generator(equi, uint_linear, n1), n2)); check(run_experiment(test_distrib_chi_square, @@ -298,7 +117,7 @@ public: equidistribution_2d_experiment equi_2d(classes); unsigned int root = static_cast(std::sqrt(double(classes))); assert(root * root == classes); - uniform_smallint uint_square(rng, 0, root-1); + variate_generator > uint_square(rng, uniform_smallint<>(0, root-1)); check(run_experiment(test_distrib_chi_square, experiment_generator(equi_2d, uint_square, n1), n2)); check(run_experiment(test_distrib_chi_square, @@ -327,11 +146,12 @@ public: // generator_reference_t gen_ref(rng); RNG& gen_ref(rng); kolmogorov_experiment ks(n1); - uniform_distribution ud((rng.min)(), (rng.max)()); + uniform_distribution ud(static_cast((rng.min)()), static_cast((rng.max)())); check(run_experiment(test_distrib_chi_square, ks_experiment_generator(ks, gen_ref, ud), n2)); check(run_experiment(test_distrib_chi_square, ks_experiment_generator(ks, gen_ref, ud), 2*n2)); + std::cout << std::endl; } private: distribution_experiment test_distrib_chi_square; @@ -417,7 +237,7 @@ public: using namespace boost; std::cout << "poker: " << std::flush; poker_experiment poker(8, classes); - uniform_smallint usmall(rng, 0, 7); + variate_generator > usmall(rng, uniform_smallint<>(0, 7)); check(run_experiment(test_distrib_chi_square, experiment_generator(poker, usmall, n1), n2)); check(run_experiment(test_distrib_chi_square, @@ -445,7 +265,7 @@ public: std::cout << "coupon collector: " << std::flush; coupon_collector_experiment coupon(5, classes); - uniform_smallint usmall(rng, 0, 4); + variate_generator > usmall(rng, uniform_smallint<>(0, 4)); check(run_experiment(test_distrib_chi_square, experiment_generator(coupon, usmall, n1), n2)); check(run_experiment(test_distrib_chi_square, @@ -513,8 +333,9 @@ private: class birthday_test : test_base { public: - birthday_test(test_environment & env) - : test_base(env) + birthday_test(test_environment & env, unsigned int high_classes) + : test_base(env), + test_distrib_chi_square(chi_square_probability(4-1), high_classes) { } template @@ -522,21 +343,22 @@ public: { using namespace boost; std::cout << "birthday spacing: " << std::flush; - uniform_int uni(rng, 0, (1<<25)-1); + boost::variate_generator > uni(rng, boost::uniform_int<>(0, (1<<25)-1)); birthday_spacing_experiment bsp(4, 512, (1<<25)); - std::cout << run_experiment(bsp, uni, n1); -#if 0 check(run_experiment(test_distrib_chi_square, - experiment_generator(perm, gen_ref, n1), n2)); + experiment_generator(bsp, uni, n1), n2)); check(run_experiment(test_distrib_chi_square, - experiment_generator(perm, gen_ref, n1), 2*n2)); -#endif + experiment_generator(bsp, uni, n1), 2*n2)); std::cout << std::endl; } - - +private: + distribution_experiment test_distrib_chi_square; }; +#ifdef BOOST_MSVC +#pragma warning(disable:4355) +#endif + class test_environment { public: @@ -553,7 +375,7 @@ public: cpn_test(*this, 15, classes), perm_test(*this, 5, classes), max_test(*this, classes), - bday_test(*this) + bday_test(*this, classes) { std::cout << "Confidence level: " << confid << "; 1-alpha = " << (1-confid) @@ -645,20 +467,60 @@ void print_ks_table() } } -int main() +class program_args { - // Haertel::validate_all(); +public: + program_args(int argc, char** argv) + { + if(argc > 0) { + names.insert(argv + 1, argv + argc); + } + } + bool check(std::string test_name) const + { + return(names.empty() || names.find(test_name) != names.end()); + } +private: + std::set names; +}; + +int main(int argc, char* argv[]) +{ + program_args args(argc, argv); test_environment env(0.99); - env.run_test("minstd_rand"); - env.run_test("mt19937"); - env.run_test("LCG_Af2"); - env.run_test("LCG_Die1"); - env.run_test("LCG_Fis"); - env.run_test("LCG_FM"); - env.run_test("LCG_Hae"); - env.run_test("LCG_VAX"); - env.run_test("NLG_Inv1"); - env.run_test("NLG_Inv2"); - env.run_test("NLG_Inv4"); - env.run_test("NLG_Inv5"); + +#define TEST(name) \ + if(args.check(#name)) \ + env.run_test(#name) + + TEST(minstd_rand0); + TEST(minstd_rand); + TEST(rand48); + TEST(ecuyer1988); + TEST(kreutzer1986); + TEST(taus88); + TEST(hellekalek1995); + TEST(mt11213b); + TEST(mt19937); + TEST(lagged_fibonacci607); + TEST(lagged_fibonacci1279); + TEST(lagged_fibonacci2281); + TEST(lagged_fibonacci3217); + TEST(lagged_fibonacci4423); + TEST(lagged_fibonacci9689); + TEST(lagged_fibonacci19937); + TEST(lagged_fibonacci23209); + TEST(lagged_fibonacci44497); + TEST(ranlux3); + TEST(ranlux4); + +#if !defined(BOOST_NO_INT64_T) && !defined(BOOST_NO_INTEGRAL_INT64_T) + TEST(ranlux64_3); + TEST(ranlux64_4); +#endif + + TEST(ranlux3_01); + TEST(ranlux4_01); + TEST(ranlux64_3_01); + TEST(ranlux64_4_01); } diff --git a/test/statistic_tests.hpp b/test/statistic_tests.hpp index da51cb4..7386bd1 100644 --- a/test/statistic_tests.hpp +++ b/test/statistic_tests.hpp @@ -84,7 +84,7 @@ public: : experiment_base(classes) { } template - void run(NumberGenerator f, Counter & count, int n) const + void run(NumberGenerator & f, Counter & count, int n) const { assert((f.min)() == 0 && static_cast((f.max)()) == classes()-1); @@ -102,7 +102,7 @@ public: : equidistribution_experiment(classes) { } template - void run(NumberGenerator f, Counter & count, int n) const + void run(NumberGenerator & f, Counter & count, int n) const { unsigned int range = (f.max)()+1; assert((f.min)() == 0 && range*range == classes()); @@ -137,7 +137,7 @@ public: } template - void run(NumberGenerator f, Counter & count, int n) const + void run(NumberGenerator & f, Counter & count, int n) const { for(int i = 0; i < n; ++i) { limits_type::const_iterator it = @@ -158,7 +158,7 @@ public: explicit runs_experiment(unsigned int classes) : experiment_base(classes) { } template - void run(UniformRandomNumberGenerator f, Counter & count, int n) const + void run(UniformRandomNumberGenerator & f, Counter & count, int n) const { typedef typename UniformRandomNumberGenerator::result_type result_type; result_type init = (up ? (f.min)() : (f.max)()); @@ -194,7 +194,7 @@ public: : experiment_base(classes), alpha(alpha), beta(beta) { } template - void run(UniformRandomNumberGenerator f, Counter & count, int n) const + void run(UniformRandomNumberGenerator & f, Counter & count, int n) const { typedef typename UniformRandomNumberGenerator::result_type result_type; double range = (f.max)() - (f.min)() + 1.0; @@ -235,7 +235,7 @@ public: } template - void run(UniformRandomNumberGenerator f, Counter & count, int n) const + void run(UniformRandomNumberGenerator & f, Counter & count, int n) const { typedef typename UniformRandomNumberGenerator::result_type result_type; assert(std::numeric_limits::is_integer); @@ -282,7 +282,7 @@ public: } template - void run(UniformRandomNumberGenerator f, Counter & count, int n) const + void run(UniformRandomNumberGenerator & f, Counter & count, int n) const { typedef typename UniformRandomNumberGenerator::result_type result_type; assert(std::numeric_limits::is_integer); @@ -330,12 +330,14 @@ public: } template - void run(UniformRandomNumberGenerator f, Counter & count, int n) const + void run(UniformRandomNumberGenerator & f, Counter & count, int n) const { typedef typename UniformRandomNumberGenerator::result_type result_type; std::vector v(t); for(int i = 0; i < n; ++i) { - std::generate_n(v.begin(), t, f); + for(int j = 0; j < t; ++j) { + v[j] = f(); + } int x = 0; for(int r = t-1; r > 0; r--) { typename std::vector::iterator it = @@ -360,7 +362,7 @@ public: } template - void run(UniformRandomNumberGenerator f, Counter & count, int n_total) const + void run(UniformRandomNumberGenerator & f, Counter & count, int n_total) const { typedef typename UniformRandomNumberGenerator::result_type result_type; assert(std::numeric_limits::is_integer); @@ -452,14 +454,14 @@ class kolmogorov_experiment public: kolmogorov_experiment(int n) : n(n), ksp(n) { } template - double run(NumberGenerator gen, Distribution distrib) const + double run(NumberGenerator & gen, Distribution distrib) const { const int m = n; typedef std::vector saved_temp; saved_temp a(m,1.0), b(m,0); std::vector c(m,0); for(int i = 0; i < n; ++i) { - double val = gen(); + double val = static_cast(gen()); double y = distrib(val); int k = static_cast(std::floor(m*y)); if(k >= m) @@ -509,7 +511,7 @@ public: private: struct generator { - generator(base_type & f, int t) : f(f), t(t) { } + generator(base_type & f, int t) : f(f, boost::uniform_01<>()), t(t) { } double operator()() { double mx = f(); @@ -518,7 +520,7 @@ private: return mx; } private: - boost::uniform_01 f; + boost::variate_generator > f; int t; }; base_type & f; @@ -579,7 +581,7 @@ private: // chi_square test template -double run_experiment(const Experiment & experiment, Generator gen, int n) +double run_experiment(const Experiment & experiment, Generator & gen, int n) { generic_counter > v(experiment.classes()); experiment.run(gen, v, n);