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)
6 #ifndef BOOST_MATH_TOOLS_TEST_HPP
7 #define BOOST_MATH_TOOLS_TEST_HPP
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/test/test_tools.hpp>
19 namespace boost{ namespace math{ namespace tools{
25 boost::math::tools::stats<T> stat; // Statistics for the test.
26 unsigned worst_case; // Index of the worst case test.
28 test_result() { worst_case = 0; }
29 void set_worst(int i){ worst_case = i; }
30 void add(const T& point){ stat.add(point); }
32 unsigned worst()const{ return worst_case; }
33 T min BOOST_PREVENT_MACRO_SUBSTITUTION()const{ return (stat.min)(); }
34 T max BOOST_PREVENT_MACRO_SUBSTITUTION()const{ return (stat.max)(); }
35 T total()const{ return stat.total(); }
36 T mean()const{ return stat.mean(); }
37 boost::uintmax_t count()const{ return stat.count(); }
38 T variance()const{ return stat.variance(); }
39 T variance1()const{ return stat.variance1(); }
40 T rms()const{ return stat.rms(); }
42 test_result& operator+=(const test_result& t)
44 if((t.stat.max)() > (stat.max)())
45 worst_case = t.worst_case;
52 struct calculate_result_type
54 typedef typename T::value_type row_type;
55 typedef typename row_type::value_type value_type;
59 T relative_error(T a, T b)
62 #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
64 // If math.h has no long double support we can't rely
65 // on the math functions generating exponents outside
66 // the range of a double:
68 T min_val = (std::max)(
69 tools::min_value<T>(),
70 static_cast<T>((std::numeric_limits<double>::min)()));
71 T max_val = (std::min)(
72 tools::max_value<T>(),
73 static_cast<T>((std::numeric_limits<double>::max)()));
75 T min_val = tools::min_value<T>();
76 T max_val = tools::max_value<T>();
79 if((a != 0) && (b != 0))
81 // TODO: use isfinite:
82 if(fabs(b) >= max_val)
84 if(fabs(a) >= max_val)
85 return 0; // one infinity is as good as another!
87 // If the result is denormalised, treat all denorms as equivalent:
88 if((a < min_val) && (a > 0))
90 else if((a > -min_val) && (a < 0))
92 if((b < min_val) && (b > 0))
94 else if((b > -min_val) && (b < 0))
96 return (std::max)(fabs((a-b)/a), fabs((a-b)/b));
99 // Handle special case where one or both are zero:
102 if(fabs(a) < min_val)
104 if(fabs(b) < min_val)
106 return (std::max)(fabs((a-b)/a), fabs((a-b)/b));
109 #if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
111 inline double relative_error<double>(double a, double b)
115 // On Mac OS X we evaluate "double" functions at "long double" precision,
116 // but "long double" actually has a very slightly narrower range than "double"!
117 // Therefore use the range of "long double" as our limits since results outside
118 // that range may have been truncated to 0 or INF:
120 double min_val = (std::max)((double)tools::min_value<long double>(), tools::min_value<double>());
121 double max_val = (std::min)((double)tools::max_value<long double>(), tools::max_value<double>());
123 if((a != 0) && (b != 0))
125 // TODO: use isfinite:
129 return 0; // one infinity is as good as another!
131 // If the result is denormalised, treat all denorms as equivalent:
132 if((a < min_val) && (a > 0))
134 else if((a > -min_val) && (a < 0))
136 if((b < min_val) && (b > 0))
138 else if((b > -min_val) && (b < 0))
140 return (std::max)(fabs((a-b)/a), fabs((a-b)/b));
143 // Handle special case where one or both are zero:
146 if(fabs(a) < min_val)
148 if(fabs(b) < min_val)
150 return (std::max)(fabs((a-b)/a), fabs((a-b)/b));
155 void set_output_precision(T)
158 #pragma warning(push)
159 #pragma warning(disable:4127)
161 if(std::numeric_limits<T>::digits10)
163 std::cout << std::setprecision(std::numeric_limits<T>::digits10 + 2);
171 void print_row(const Seq& row)
173 set_output_precision(row[0]);
174 for(unsigned i = 0; i < row.size(); ++i)
180 std::cout << std::endl;
184 // Function test accepts an matrix of input values (probably a 2D boost::array)
185 // and calls two functors for each row in the array - one calculates a value
186 // to test, and one extracts the expected value from the array (or possibly
187 // calculates it at high precision). The two functors are usually simple lambda
190 template <class A, class F1, class F2>
191 test_result<typename calculate_result_type<A>::value_type> test(const A& a, F1 test_func, F2 expect_func)
193 typedef typename A::value_type row_type;
194 typedef typename row_type::value_type value_type;
196 test_result<value_type> result;
198 for(unsigned i = 0; i < a.size(); ++i)
200 const row_type& row = a[i];
204 point = test_func(row);
206 catch(const std::underflow_error&)
210 catch(const std::overflow_error&)
212 point = std::numeric_limits<value_type>::has_infinity ?
213 std::numeric_limits<value_type>::infinity()
214 : tools::max_value<value_type>();
216 catch(const std::exception& e)
218 std::cerr << e.what() << std::endl;
220 BOOST_ERROR("Unexpected exception.");
221 // so we don't get further errors:
222 point = expect_func(row);
224 value_type expected = expect_func(row);
225 value_type err = relative_error(point, expected);
226 #ifdef BOOST_INSTRUMENT
229 std::cout << row[0] << " " << err;
230 if(std::numeric_limits<value_type>::is_specialized)
232 std::cout << " (" << err / std::numeric_limits<value_type>::epsilon() << "eps)";
234 std::cout << std::endl;
237 if(!(boost::math::isfinite)(point) && (boost::math::isfinite)(expected))
239 std::cout << "CAUTION: Found non-finite result, when a finite value was expected at entry " << i << "\n";
240 std::cout << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
242 BOOST_ERROR("Unexpected non-finite result");
246 std::cout << "CAUTION: Gross error found at entry " << i << ".\n";
247 std::cout << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
249 BOOST_ERROR("Gross error");
252 if((result.max)() == err)
258 template <class Real, class A, class F1, class F2>
259 test_result<Real> test_hetero(const A& a, F1 test_func, F2 expect_func)
261 typedef typename A::value_type row_type;
262 typedef Real value_type;
264 test_result<value_type> result;
266 for(unsigned i = 0; i < a.size(); ++i)
268 const row_type& row = a[i];
272 point = test_func(row);
274 catch(const std::underflow_error&)
278 catch(const std::overflow_error&)
280 point = std::numeric_limits<value_type>::has_infinity ?
281 std::numeric_limits<value_type>::infinity()
282 : tools::max_value<value_type>();
284 catch(const std::exception& e)
286 std::cerr << e.what() << std::endl;
288 BOOST_ERROR("Unexpected exception.");
289 // so we don't get further errors:
290 point = expect_func(row);
292 value_type expected = expect_func(row);
293 value_type err = relative_error(point, expected);
294 #ifdef BOOST_INSTRUMENT
297 std::cout << row[0] << " " << err;
298 if(std::numeric_limits<value_type>::is_specialized)
300 std::cout << " (" << err / std::numeric_limits<value_type>::epsilon() << "eps)";
302 std::cout << std::endl;
305 if(!(boost::math::isfinite)(point) && (boost::math::isfinite)(expected))
307 std::cout << "CAUTION: Found non-finite result, when a finite value was expected at entry " << i << "\n";
308 std::cout << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
310 BOOST_ERROR("Unexpected non-finite result");
314 std::cout << "CAUTION: Gross error found at entry " << i << ".\n";
315 std::cout << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
317 BOOST_ERROR("Gross error");
320 if((result.max)() == err)