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