• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright Justinas Vygintas Daugmaudis, 2010-2018.
2 // Use, modification and distribution is subject to the
3 // Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE-1.0 or http://www.boost.org/LICENSE-1.0)
5 
6 #ifndef TEST_QRNG_FUNCTIONS_HPP_INCLUDED
7 #define TEST_QRNG_FUNCTIONS_HPP_INCLUDED
8 
9 #include <boost/random/uniform_real.hpp>
10 #include <boost/test/floating_point_comparison.hpp>
11 
12 #include <sstream>
13 
14 namespace test {
15 
16 // Invokes operator() precisely n times. This is to check that
17 // Engine::discard(n) actually has the same effect.
18 template<typename Engine>
trivial_discard(Engine & eng,boost::uintmax_t n)19 inline void trivial_discard(Engine& eng, boost::uintmax_t n)
20 {
21   for( ; n != 0; --n ) eng();
22 }
23 
24 
25 template<typename Engine, typename T, std::size_t Dimension>
match_vector(Engine & eng,T (& pt)[Dimension])26 inline void match_vector(Engine& eng, T (&pt)[Dimension])
27 {
28   BOOST_REQUIRE_EQUAL( eng.dimension(), Dimension ); // paranoid check
29 
30   boost::uniform_real<T> dist;
31 
32   for( std::size_t i = 0; i != eng.dimension(); ++i )
33   {
34     T val = dist(eng);
35     // We want to check that quasi-random number generator values differ no
36     // more than 0.0006% of their value.
37     BOOST_CHECK_CLOSE(pt[i], val, 0.0006);
38   }
39 }
40 
41 
42 template<typename Engine, typename T, std::size_t Dimension, std::size_t N>
expected_values(T (& pt)[N][Dimension],std::size_t skip)43 inline void expected_values(T (&pt)[N][Dimension], std::size_t skip)
44 {
45   Engine eng(Dimension);
46 
47   eng.seed(skip);
48 
49   for( std::size_t i = 0; i != N; ++i )
50     match_vector(eng, pt[i]);
51 }
52 
53 template<typename Engine, typename T>
test_zero_seed(std::size_t dimension)54 inline void test_zero_seed(std::size_t dimension)
55 {
56   Engine eng(dimension);
57 
58   Engine other(dimension);
59   other.seed(0);
60 
61   // Check that states are equal after zero seed.
62   boost::uniform_real<T> dist;
63   BOOST_CHECK( eng == other );
64   for( std:: size_t i = 0; i != dimension; ++i )
65   {
66     T q_val = dist(eng);
67     T t_val = dist(other);
68     BOOST_CHECK_CLOSE(q_val, t_val, 0.0001);
69   }
70 }
71 
72 template<typename Engine, typename T, std::size_t Dimension, std::size_t N>
seed_function(T (& pt)[N][Dimension],std::size_t skip)73 inline void seed_function(T (&pt)[N][Dimension], std::size_t skip)
74 {
75   // Test zero seed before doing other tests.
76   test_zero_seed<Engine, T>(Dimension);
77 
78   Engine eng(Dimension);
79   for( std::size_t i = 0; i != N; ++i )
80   {
81     // For all N seeds an engine
82     // and checks if the expected values match.
83     eng.seed(skip + i);
84     match_vector(eng, pt[i]);
85   }
86 }
87 
88 template<typename Engine, typename T, std::size_t Dimension, std::size_t N>
discard_function(T (& pt)[N][Dimension],std::size_t skip)89 inline void discard_function(T (&pt)[N][Dimension], std::size_t skip)
90 {
91   Engine eng(Dimension), trivial(Dimension), streamed(Dimension), initial_state(Dimension);
92   boost::uniform_real<T> dist;
93 
94   const std::size_t element_count = N * Dimension;
95   const T* pt_array = reinterpret_cast<T *>(boost::addressof(pt));
96 
97   std::stringstream ss;
98 
99   initial_state.seed(skip);
100   for (std::size_t step = 0; step != element_count; ++step)
101   {
102     // Init to the same state
103     eng = initial_state;
104     trivial = initial_state;
105 
106     // Discards have to have the same effect
107     eng.discard(step);
108     trivial_discard(trivial, step);
109 
110     // test serialization to stream
111     ss.str(std::string()); // clear stream
112     ss << eng;
113     ss >> streamed;
114 
115     // Therefore, states are equal
116     BOOST_CHECK( eng == trivial );
117     BOOST_CHECK( eng == streamed );
118 
119     // Now, let's check whether they really produce the same sequence
120     T q_val = dist(eng);
121     T t_val = dist(trivial);
122     T s_val = dist(streamed);
123     BOOST_CHECK_CLOSE(q_val, t_val, 0.0001);
124     BOOST_CHECK_CLOSE(q_val, s_val, 0.0001);
125     // ~ BOOST_CHECK(q_val == t_val), but those are floating point values,
126     // so strict equality check may fail unnecessarily
127 
128     // States remain equal!
129     BOOST_CHECK( eng == trivial );
130     BOOST_CHECK( eng == streamed );
131 
132     // We want to check that quasi-random number generator values differ no
133     // more than 0.0006% of their value.
134     BOOST_CHECK_CLOSE(pt_array[step], q_val, 0.0006);
135   }
136 }
137 
accept_all_exceptions(const std::exception & e)138 inline bool accept_all_exceptions(const std::exception& e)
139 {
140   BOOST_TEST_MESSAGE( e.what() );
141   return true;
142 }
143 
144 template<typename Engine>
test_max_seed(std::size_t dim)145 void test_max_seed(std::size_t dim)
146 {
147   typedef typename Engine::size_type size_type;
148   static const size_type maxseed = Engine::max();
149 
150   Engine eng(dim);
151   eng.seed(maxseed-1); // must succeed
152   eng(); // skip one element
153   BOOST_REQUIRE_EXCEPTION( eng.seed(maxseed), std::range_error, accept_all_exceptions );
154 
155   Engine other(dim);
156   other.seed(maxseed-1); // must succeed
157   other(); // skip one element, too
158 
159   // States remain the same even after unsuccessful seeding for eng.
160   BOOST_CHECK( eng == other );
161   BOOST_CHECK( eng() == other() );
162 }
163 
164 template<typename Generator>
test_max_discard(std::size_t dim)165 void test_max_discard(std::size_t dim)
166 {
167   typedef typename Generator::type engine_type;
168 
169   static const boost::uintmax_t maxdiscard = dim * engine_type::max();
170 
171   // Max discard limit
172   {
173     engine_type eng(dim);
174     eng.discard(maxdiscard-1); // must succeed
175     eng(); // the very last element
176 
177     BOOST_REQUIRE_EXCEPTION( eng(), std::range_error, accept_all_exceptions );
178 
179     engine_type other(dim);
180 
181     BOOST_CHECK( eng != other ); // test that comparison does not overflow
182 
183     other(); // the very first element
184     other.discard(maxdiscard-1); // must succeed
185 
186     BOOST_CHECK( eng == other );
187 
188     BOOST_REQUIRE_EXCEPTION( other(), std::range_error, accept_all_exceptions );
189   }
190 
191   // Overdiscarding
192   {
193     engine_type eng(dim);
194     eng.discard(maxdiscard); // must succeed, since it's maxdiscard operator() invocations
195 
196     // must fail because after discarding the whole sequence
197     // we can't put the eng to any valid sequence producing state
198     BOOST_REQUIRE_EXCEPTION( eng(), std::range_error, accept_all_exceptions );
199 
200     // Plain overdiscarding by 1
201     engine_type other(dim);
202     BOOST_REQUIRE_EXCEPTION( other.discard(maxdiscard+1), std::range_error, accept_all_exceptions );
203   }
204 
205   // Test wraparound
206   {
207     engine_type eng(dim);
208 
209     // must fail either because seq_count overflow check is triggered,
210     // or because this discard violates seeding bitcount constraint
211     BOOST_REQUIRE_EXCEPTION( eng.discard(maxdiscard*2), std::range_error, accept_all_exceptions );
212    }
213 }
214 
215 } // namespace test
216 
217 
218 #define QRNG_VALIDATION_TEST_FUNCTIONS(QRNG) \
219 \
220 typedef boost::random::QRNG engine_t; \
221 \
222 template<typename T, std::size_t Dimension, std::size_t N> \
223 inline void test_##QRNG##_values(T (&pt)[N][Dimension], std::size_t skip) \
224 { \
225   test::expected_values<engine_t>(pt, skip); \
226 } \
227 \
228 template<typename T, std::size_t Dimension, std::size_t N> \
229 inline void test_##QRNG##_seed(T (&pt)[N][Dimension], std::size_t skip) \
230 { \
231   test::seed_function<engine_t>(pt, skip); \
232 } \
233 \
234 template<typename T, std::size_t Dimension, std::size_t N> \
235 inline void test_##QRNG##_discard(T (&pt)[N][Dimension], std::size_t skip) \
236 { \
237   test::discard_function<engine_t>(pt, skip); \
238 } \
239 inline void test_##QRNG##_max_seed() \
240 { \
241   test::test_max_seed<engine_t>(2); \
242 } \
243 inline void test_##QRNG##_max_dimension(std::size_t dim) \
244 { \
245   engine_t eng(dim); /*must succeed*/ \
246   BOOST_REQUIRE_EXCEPTION( engine_t(dim+1), std::invalid_argument, test::accept_all_exceptions ); \
247 } \
248 \
249 BOOST_AUTO_TEST_CASE( test_##QRNG##_zero_dimension_fails ) \
250 { \
251   BOOST_REQUIRE_EXCEPTION( engine_t(0), std::invalid_argument, test::accept_all_exceptions ); \
252 } \
253 /**/
254 
255 #define QRNG_VALIDATION_TEST_DISCARD(QRNG) \
256 \
257 template <typename IntType, unsigned w> \
258 struct gen_engine \
259 { \
260   typedef boost::random::QRNG##_engine<IntType, w> type; \
261 }; \
262 \
263 inline void test_##QRNG##_max_discard() \
264 { \
265   static const std::size_t dim = 2;\
266    \
267    /* test full 8 bits */ \
268   test::test_max_discard<gen_engine<boost::uint8_t, 8u> >(dim); \
269   \
270   /* test 7 bits */ \
271   test::test_max_discard<gen_engine<boost::uint8_t, 7u> >(dim); \
272   \
273   /* test 6 bits for a good measure */ \
274   test::test_max_discard<gen_engine<boost::uint8_t, 6u> >(dim); \
275 } \
276 /**/
277 
278 #endif // TEST_QRNG_FUNCTIONS_HPP_INCLUDED
279