blob: db7b31616e4240156ff39807c424ac993c988096 [file] [edit]
// (C) Copyright John Maddock 2006.
// (C) Copyright Matt Borland 2024.
// Use, modification and distribution are subject to 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)
#ifndef BOOST_MATH_TOOLS_TEST_HPP
#define BOOST_MATH_TOOLS_TEST_HPP
#ifdef _MSC_VER
#pragma once
#endif
#include <boost/math/tools/config.hpp>
#include <boost/math/tools/stats.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/math/special_functions/relative_difference.hpp>
#include <boost/math/policies/error_handling.hpp>
#include <boost/test/test_tools.hpp>
#include <stdexcept>
#include <iostream>
#include <iomanip>
namespace boost{ namespace math{ namespace tools{
template <class T>
struct test_result
{
private:
boost::math::tools::stats<T> stat; // Statistics for the test.
unsigned worst_case; // Index of the worst case test.
public:
test_result() { worst_case = 0; }
void set_worst(int i){ worst_case = i; }
void add(const T& point){ stat.add(point); }
// accessors:
unsigned worst()const{ return worst_case; }
T min BOOST_MATH_PREVENT_MACRO_SUBSTITUTION()const{ return (stat.min)(); }
T max BOOST_MATH_PREVENT_MACRO_SUBSTITUTION()const{ return (stat.max)(); }
T total()const{ return stat.total(); }
T mean()const{ return stat.mean(); }
std::uintmax_t count()const{ return stat.count(); }
T variance()const{ return stat.variance(); }
T variance1()const{ return stat.variance1(); }
T rms()const{ return stat.rms(); }
test_result& operator+=(const test_result& t)
{
if((t.stat.max)() > (stat.max)())
worst_case = t.worst_case;
stat += t.stat;
return *this;
}
};
template <class T>
struct calculate_result_type
{
typedef typename T::value_type row_type;
typedef typename row_type::value_type value_type;
};
template <class T>
T relative_error(T a, T b)
{
return boost::math::relative_difference(a, b);
}
template <class T>
void set_output_precision(T, std::ostream& os)
{
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4127)
#endif
if(std::numeric_limits<T>::digits10)
{
os << std::setprecision(std::numeric_limits<T>::digits10 + 2);
}
else
os << std::setprecision(22); // and hope for the best!
#ifdef _MSC_VER
#pragma warning(pop)
#endif
}
template <class Seq>
void print_row(const Seq& row, std::ostream& os = std::cout)
{
try {
set_output_precision(row[0], os);
for (unsigned i = 0; i < row.size(); ++i)
{
if (i)
os << ", ";
os << row[i];
}
os << std::endl;
}
catch (const std::exception&) {}
}
//
// Function test accepts an matrix of input values (probably a 2D std::array)
// and calls two functors for each row in the array - one calculates a value
// to test, and one extracts the expected value from the array (or possibly
// calculates it at high precision). The two functors are usually simple lambda
// expressions.
//
template <class A, class F1, class F2>
test_result<typename calculate_result_type<A>::value_type> test(const A& a, F1 test_func, F2 expect_func)
{
typedef typename A::value_type row_type;
typedef typename row_type::value_type value_type;
test_result<value_type> result;
for(unsigned i = 0; i < a.size(); ++i)
{
const row_type& row = a[i];
value_type point;
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
point = test_func(row);
#ifndef BOOST_NO_EXCEPTIONS
}
catch(const std::underflow_error&)
{
point = 0;
}
catch(const std::overflow_error&)
{
point = std::numeric_limits<value_type>::has_infinity ?
std::numeric_limits<value_type>::infinity()
: tools::max_value<value_type>();
}
catch(const std::exception& e)
{
std::cerr << e.what() << std::endl;
print_row(row, std::cerr);
BOOST_ERROR("Unexpected exception.");
// so we don't get further errors:
point = expect_func(row);
}
#endif
value_type expected = expect_func(row);
value_type err = relative_error(point, expected);
#ifdef BOOST_INSTRUMENT
if(err != 0)
{
std::cout << row[0] << " " << err;
if(std::numeric_limits<value_type>::is_specialized)
{
std::cout << " (" << err / std::numeric_limits<value_type>::epsilon() << "eps)";
}
std::cout << std::endl;
}
#endif
if(!(boost::math::isfinite)(point) && (boost::math::isfinite)(expected))
{
std::cerr << "CAUTION: Found non-finite result, when a finite value was expected at entry " << i << "\n";
std::cerr << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
print_row(row, std::cerr);
BOOST_ERROR("Unexpected non-finite result");
}
if(err > 0.5f)
{
std::cerr << "CAUTION: Gross error found at entry " << i << ".\n";
std::cerr << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
print_row(row, std::cerr);
BOOST_ERROR("Gross error");
}
result.add(err);
if((result.max)() == err)
result.set_worst(i);
}
return result;
}
template <class Real, class A, class F1, class F2>
test_result<Real> test_hetero(const A& a, F1 test_func, F2 expect_func)
{
typedef typename A::value_type row_type;
typedef Real value_type;
test_result<value_type> result;
for(unsigned i = 0; i < a.size(); ++i)
{
const row_type& row = a[i];
value_type point;
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
point = test_func(row);
#ifndef BOOST_NO_EXCEPTIONS
}
catch(const std::underflow_error&)
{
point = 0;
}
catch(const std::overflow_error&)
{
point = std::numeric_limits<value_type>::has_infinity ?
std::numeric_limits<value_type>::infinity()
: tools::max_value<value_type>();
}
catch(const std::exception& e)
{
std::cerr << "Unexpected exception at entry: " << i << "\n";
std::cerr << e.what() << std::endl;
print_row(row, std::cerr);
BOOST_ERROR("Unexpected exception.");
// so we don't get further errors:
point = expect_func(row);
}
#endif
value_type expected = expect_func(row);
value_type err = relative_error(point, expected);
#ifdef BOOST_INSTRUMENT
if(err != 0)
{
std::cout << row[0] << " " << err;
if(std::numeric_limits<value_type>::is_specialized)
{
std::cout << " (" << err / std::numeric_limits<value_type>::epsilon() << "eps)";
}
std::cout << std::endl;
}
#endif
if(!(boost::math::isfinite)(point) && (boost::math::isfinite)(expected))
{
std::cerr << "CAUTION: Found non-finite result, when a finite value was expected at entry " << i << "\n";
std::cerr << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
print_row(row, std::cerr);
BOOST_ERROR("Unexpected non-finite result");
}
if(err > 0.5f)
{
std::cerr << "CAUTION: Gross error found at entry " << i << ".\n";
std::cerr << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
print_row(row, std::cerr);
BOOST_ERROR("Gross error");
}
result.add(err);
if((result.max)() == err)
result.set_worst(i);
}
return result;
}
#ifndef BOOST_MATH_NO_EXCEPTIONS
template <class Val, class Exception>
void test_check_throw(Val, Exception)
{
BOOST_CHECK(errno);
errno = 0;
}
template <class Val>
void test_check_throw(Val val, std::domain_error const*)
{
BOOST_CHECK(errno == EDOM);
errno = 0;
if(std::numeric_limits<Val>::has_quiet_NaN)
{
BOOST_CHECK((boost::math::isnan)(val));
}
}
template <class Val>
void test_check_throw(Val v, std::overflow_error const*)
{
BOOST_CHECK(errno == ERANGE);
errno = 0;
BOOST_CHECK((v >= boost::math::tools::max_value<Val>()) || (v <= -boost::math::tools::max_value<Val>()));
}
template <class Val>
void test_check_throw(Val v, boost::math::rounding_error const*)
{
BOOST_CHECK(errno == ERANGE);
errno = 0;
if(std::numeric_limits<Val>::is_specialized && std::numeric_limits<Val>::is_integer)
{
BOOST_CHECK((v == (std::numeric_limits<Val>::max)()) || (v == (std::numeric_limits<Val>::min)()));
}
else
{
BOOST_CHECK((v == boost::math::tools::max_value<Val>()) || (v == -boost::math::tools::max_value<Val>()));
}
}
#endif
} // namespace tools
} // namespace math
} // namespace boost
//
// exception-free testing support, ideally we'd only define this in our tests,
// but to keep things simple we really need it somewhere that's always included:
//
#if defined(BOOST_MATH_NO_EXCEPTIONS) && defined(BOOST_MATH_HAS_GPU_SUPPORT)
# define BOOST_MATH_CHECK_THROW(x, y)
#elif defined(BOOST_MATH_NO_EXCEPTIONS)
# define BOOST_MATH_CHECK_THROW(x, ExceptionType) boost::math::tools::test_check_throw(x, static_cast<ExceptionType const*>(nullptr));
#else
# define BOOST_MATH_CHECK_THROW(x, y) BOOST_CHECK_THROW(x, y)
#endif
#endif