]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // (C) Copyright John Maddock 2006. |
2 | // Use, modification and distribution are subject to the | |
3 | // Boost Software License, Version 1.0. (See accompanying file | |
4 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
5 | ||
6 | #ifndef BOOST_MATH_TOOLS_TEST_HPP | |
7 | #define BOOST_MATH_TOOLS_TEST_HPP | |
8 | ||
9 | #ifdef _MSC_VER | |
10 | #pragma once | |
11 | #endif | |
12 | ||
13 | #include <boost/math/tools/config.hpp> | |
14 | #include <boost/math/tools/stats.hpp> | |
15 | #include <boost/math/special_functions/fpclassify.hpp> | |
16 | #include <boost/math/special_functions/relative_difference.hpp> | |
17 | #include <boost/math/policies/error_handling.hpp> | |
18 | #include <boost/test/test_tools.hpp> | |
19 | #include <stdexcept> | |
20 | #include <iostream> | |
21 | #include <iomanip> | |
22 | ||
23 | namespace boost{ namespace math{ namespace tools{ | |
24 | ||
25 | template <class T> | |
26 | struct test_result | |
27 | { | |
28 | private: | |
29 | boost::math::tools::stats<T> stat; // Statistics for the test. | |
30 | unsigned worst_case; // Index of the worst case test. | |
31 | public: | |
32 | test_result() { worst_case = 0; } | |
33 | void set_worst(int i){ worst_case = i; } | |
34 | void add(const T& point){ stat.add(point); } | |
35 | // accessors: | |
36 | unsigned worst()const{ return worst_case; } | |
37 | T min BOOST_PREVENT_MACRO_SUBSTITUTION()const{ return (stat.min)(); } | |
38 | T max BOOST_PREVENT_MACRO_SUBSTITUTION()const{ return (stat.max)(); } | |
39 | T total()const{ return stat.total(); } | |
40 | T mean()const{ return stat.mean(); } | |
1e59de90 | 41 | std::uintmax_t count()const{ return stat.count(); } |
7c673cae FG |
42 | T variance()const{ return stat.variance(); } |
43 | T variance1()const{ return stat.variance1(); } | |
44 | T rms()const{ return stat.rms(); } | |
45 | ||
46 | test_result& operator+=(const test_result& t) | |
47 | { | |
48 | if((t.stat.max)() > (stat.max)()) | |
49 | worst_case = t.worst_case; | |
50 | stat += t.stat; | |
51 | return *this; | |
52 | } | |
53 | }; | |
54 | ||
55 | template <class T> | |
56 | struct calculate_result_type | |
57 | { | |
58 | typedef typename T::value_type row_type; | |
59 | typedef typename row_type::value_type value_type; | |
60 | }; | |
61 | ||
62 | template <class T> | |
63 | T relative_error(T a, T b) | |
64 | { | |
65 | return boost::math::relative_difference(a, b); | |
66 | } | |
67 | ||
68 | ||
69 | template <class T> | |
92f5a8d4 | 70 | void set_output_precision(T, std::ostream& os) |
7c673cae | 71 | { |
1e59de90 | 72 | #ifdef _MSC_VER |
7c673cae FG |
73 | #pragma warning(push) |
74 | #pragma warning(disable:4127) | |
75 | #endif | |
76 | if(std::numeric_limits<T>::digits10) | |
77 | { | |
92f5a8d4 | 78 | os << std::setprecision(std::numeric_limits<T>::digits10 + 2); |
7c673cae | 79 | } |
20effc67 TL |
80 | else |
81 | os << std::setprecision(22); // and hope for the best! | |
82 | ||
1e59de90 | 83 | #ifdef _MSC_VER |
7c673cae FG |
84 | #pragma warning(pop) |
85 | #endif | |
86 | } | |
87 | ||
88 | template <class Seq> | |
89 | void print_row(const Seq& row, std::ostream& os = std::cout) | |
90 | { | |
92f5a8d4 TL |
91 | try { |
92 | set_output_precision(row[0], os); | |
93 | for (unsigned i = 0; i < row.size(); ++i) | |
94 | { | |
95 | if (i) | |
96 | os << ", "; | |
97 | os << row[i]; | |
98 | } | |
99 | os << std::endl; | |
7c673cae | 100 | } |
92f5a8d4 | 101 | catch (const std::exception&) {} |
7c673cae FG |
102 | } |
103 | ||
104 | // | |
1e59de90 | 105 | // Function test accepts an matrix of input values (probably a 2D std::array) |
7c673cae FG |
106 | // and calls two functors for each row in the array - one calculates a value |
107 | // to test, and one extracts the expected value from the array (or possibly | |
108 | // calculates it at high precision). The two functors are usually simple lambda | |
109 | // expressions. | |
110 | // | |
111 | template <class A, class F1, class F2> | |
112 | test_result<typename calculate_result_type<A>::value_type> test(const A& a, F1 test_func, F2 expect_func) | |
113 | { | |
114 | typedef typename A::value_type row_type; | |
115 | typedef typename row_type::value_type value_type; | |
116 | ||
117 | test_result<value_type> result; | |
118 | ||
119 | for(unsigned i = 0; i < a.size(); ++i) | |
120 | { | |
121 | const row_type& row = a[i]; | |
122 | value_type point; | |
123 | #ifndef BOOST_NO_EXCEPTIONS | |
124 | try | |
125 | { | |
126 | #endif | |
127 | point = test_func(row); | |
128 | #ifndef BOOST_NO_EXCEPTIONS | |
129 | } | |
130 | catch(const std::underflow_error&) | |
131 | { | |
132 | point = 0; | |
133 | } | |
134 | catch(const std::overflow_error&) | |
135 | { | |
136 | point = std::numeric_limits<value_type>::has_infinity ? | |
137 | std::numeric_limits<value_type>::infinity() | |
138 | : tools::max_value<value_type>(); | |
139 | } | |
140 | catch(const std::exception& e) | |
141 | { | |
142 | std::cerr << e.what() << std::endl; | |
143 | print_row(row, std::cerr); | |
144 | BOOST_ERROR("Unexpected exception."); | |
145 | // so we don't get further errors: | |
146 | point = expect_func(row); | |
147 | } | |
148 | #endif | |
149 | value_type expected = expect_func(row); | |
150 | value_type err = relative_error(point, expected); | |
151 | #ifdef BOOST_INSTRUMENT | |
152 | if(err != 0) | |
153 | { | |
154 | std::cout << row[0] << " " << err; | |
155 | if(std::numeric_limits<value_type>::is_specialized) | |
156 | { | |
157 | std::cout << " (" << err / std::numeric_limits<value_type>::epsilon() << "eps)"; | |
158 | } | |
159 | std::cout << std::endl; | |
160 | } | |
161 | #endif | |
162 | if(!(boost::math::isfinite)(point) && (boost::math::isfinite)(expected)) | |
163 | { | |
164 | std::cerr << "CAUTION: Found non-finite result, when a finite value was expected at entry " << i << "\n"; | |
165 | std::cerr << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl; | |
166 | print_row(row, std::cerr); | |
167 | BOOST_ERROR("Unexpected non-finite result"); | |
168 | } | |
169 | if(err > 0.5) | |
170 | { | |
171 | std::cerr << "CAUTION: Gross error found at entry " << i << ".\n"; | |
172 | std::cerr << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl; | |
173 | print_row(row, std::cerr); | |
174 | BOOST_ERROR("Gross error"); | |
175 | } | |
176 | result.add(err); | |
177 | if((result.max)() == err) | |
178 | result.set_worst(i); | |
179 | } | |
180 | return result; | |
181 | } | |
182 | ||
183 | template <class Real, class A, class F1, class F2> | |
184 | test_result<Real> test_hetero(const A& a, F1 test_func, F2 expect_func) | |
185 | { | |
186 | typedef typename A::value_type row_type; | |
187 | typedef Real value_type; | |
188 | ||
189 | test_result<value_type> result; | |
190 | ||
191 | for(unsigned i = 0; i < a.size(); ++i) | |
192 | { | |
193 | const row_type& row = a[i]; | |
194 | value_type point; | |
195 | #ifndef BOOST_NO_EXCEPTIONS | |
196 | try | |
197 | { | |
198 | #endif | |
199 | point = test_func(row); | |
200 | #ifndef BOOST_NO_EXCEPTIONS | |
201 | } | |
202 | catch(const std::underflow_error&) | |
203 | { | |
204 | point = 0; | |
205 | } | |
206 | catch(const std::overflow_error&) | |
207 | { | |
208 | point = std::numeric_limits<value_type>::has_infinity ? | |
209 | std::numeric_limits<value_type>::infinity() | |
210 | : tools::max_value<value_type>(); | |
211 | } | |
212 | catch(const std::exception& e) | |
213 | { | |
92f5a8d4 | 214 | std::cerr << "Unexpected exception at entry: " << i << "\n"; |
7c673cae FG |
215 | std::cerr << e.what() << std::endl; |
216 | print_row(row, std::cerr); | |
217 | BOOST_ERROR("Unexpected exception."); | |
218 | // so we don't get further errors: | |
219 | point = expect_func(row); | |
220 | } | |
221 | #endif | |
222 | value_type expected = expect_func(row); | |
223 | value_type err = relative_error(point, expected); | |
224 | #ifdef BOOST_INSTRUMENT | |
225 | if(err != 0) | |
226 | { | |
227 | std::cout << row[0] << " " << err; | |
228 | if(std::numeric_limits<value_type>::is_specialized) | |
229 | { | |
230 | std::cout << " (" << err / std::numeric_limits<value_type>::epsilon() << "eps)"; | |
231 | } | |
232 | std::cout << std::endl; | |
233 | } | |
234 | #endif | |
235 | if(!(boost::math::isfinite)(point) && (boost::math::isfinite)(expected)) | |
236 | { | |
237 | std::cerr << "CAUTION: Found non-finite result, when a finite value was expected at entry " << i << "\n"; | |
238 | std::cerr << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl; | |
239 | print_row(row, std::cerr); | |
240 | BOOST_ERROR("Unexpected non-finite result"); | |
241 | } | |
242 | if(err > 0.5) | |
243 | { | |
244 | std::cerr << "CAUTION: Gross error found at entry " << i << ".\n"; | |
245 | std::cerr << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl; | |
246 | print_row(row, std::cerr); | |
247 | BOOST_ERROR("Gross error"); | |
248 | } | |
249 | result.add(err); | |
250 | if((result.max)() == err) | |
251 | result.set_worst(i); | |
252 | } | |
253 | return result; | |
254 | } | |
255 | ||
256 | template <class Val, class Exception> | |
257 | void test_check_throw(Val v, Exception e) | |
258 | { | |
259 | BOOST_CHECK(errno); | |
260 | errno = 0; | |
261 | } | |
262 | ||
263 | template <class Val> | |
264 | void test_check_throw(Val val, std::domain_error const* e) | |
265 | { | |
266 | BOOST_CHECK(errno == EDOM); | |
267 | errno = 0; | |
268 | if(std::numeric_limits<Val>::has_quiet_NaN) | |
269 | { | |
270 | BOOST_CHECK((boost::math::isnan)(val)); | |
271 | } | |
272 | } | |
273 | ||
274 | template <class Val> | |
275 | void test_check_throw(Val v, std::overflow_error const* e) | |
276 | { | |
277 | BOOST_CHECK(errno == ERANGE); | |
278 | errno = 0; | |
279 | BOOST_CHECK((v >= boost::math::tools::max_value<Val>()) || (v <= -boost::math::tools::max_value<Val>())); | |
280 | } | |
281 | ||
282 | template <class Val> | |
283 | void test_check_throw(Val v, boost::math::rounding_error const* e) | |
284 | { | |
285 | BOOST_CHECK(errno == ERANGE); | |
286 | errno = 0; | |
287 | if(std::numeric_limits<Val>::is_specialized && std::numeric_limits<Val>::is_integer) | |
288 | { | |
289 | BOOST_CHECK((v == (std::numeric_limits<Val>::max)()) || (v == (std::numeric_limits<Val>::min)())); | |
290 | } | |
291 | else | |
292 | { | |
293 | BOOST_CHECK((v == boost::math::tools::max_value<Val>()) || (v == -boost::math::tools::max_value<Val>())); | |
294 | } | |
295 | } | |
296 | ||
297 | } // namespace tools | |
298 | } // namespace math | |
299 | } // namespace boost | |
300 | ||
301 | ||
302 | // | |
303 | // exception-free testing support, ideally we'd only define this in our tests, | |
304 | // but to keep things simple we really need it somewhere that's always included: | |
305 | // | |
306 | #ifdef BOOST_NO_EXCEPTIONS | |
307 | # define BOOST_MATH_CHECK_THROW(x, ExceptionType) boost::math::tools::test_check_throw(x, static_cast<ExceptionType const*>(0)); | |
308 | #else | |
309 | # define BOOST_MATH_CHECK_THROW(x, y) BOOST_CHECK_THROW(x, y) | |
310 | #endif | |
311 | ||
312 | #endif | |
313 | ||
314 |