blob: 8c619d535c01e2d007525b5b35ac31578df9d0c5 [file] [log] [blame]
// (C) Copyright John Maddock 2005.
// 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)
#include <boost/test/test_tools.hpp>
#include <boost/test/test_exec_monitor.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/mpl/if.hpp>
#include <boost/static_assert.hpp>
#include <boost/math/complex.hpp>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <typeinfo>
#ifdef BOOST_NO_STDC_NAMESPACE
namespace std{ using ::sqrt; using ::tan; using ::tanh; }
#endif
#ifndef VERBOSE
#undef BOOST_MESSAGE
#define BOOST_MESSAGE(x)
#endif
//
// check_complex:
// Verifies that expected value "a" and found value "b" have a relative error
// less than "max_error" epsilons. Note that relative error is calculated for
// the complex number as a whole; this means that the error in the real or
// imaginary parts alone can be much higher than max_error when the real and
// imaginary parts are of very different magnitudes. This is important, because
// the Hull et al analysis of the acos and asin algorithms requires that very small
// real/imaginary components can be safely ignored if they are negligible compared
// to the other component.
//
template <class T>
bool check_complex(const std::complex<T>& a, const std::complex<T>& b, int max_error)
{
//
// a is the expected value, b is what was actually found,
// compute | (a-b)/b | and compare with max_error which is the
// multiple of E to permit:
//
bool result = true;
static const std::complex<T> zero(0);
static const T eps = std::pow(static_cast<T>(std::numeric_limits<T>::radix), static_cast<T>(1 - std::numeric_limits<T>::digits));
if(a == zero)
{
if(b != zero)
{
if(boost::math::fabs(b) > eps)
{
result = false;
BOOST_ERROR("Expected {0,0} but got: " << b);
}
else
{
BOOST_MESSAGE("Expected {0,0} but got: " << b);
}
}
return result;
}
else if(b == zero)
{
if(boost::math::fabs(a) > eps)
{
BOOST_ERROR("Found {0,0} but expected: " << a);
return false;;
}
else
{
BOOST_MESSAGE("Found {0,0} but expected: " << a);
}
}
T rel = boost::math::fabs((b-a)/b) / eps;
if( rel > max_error)
{
result = false;
BOOST_ERROR("Error in result exceeded permitted limit of " << max_error << " (actual relative error was " << rel << "e). Found " << b << " expected " << a);
}
return result;
}
//
// test_inverse_trig:
// This is nothing more than a sanity check, computes trig(atrig(z))
// and compare the result to z. Note that:
//
// atrig(trig(z)) != z
//
// for certain z because the inverse trig functions are multi-valued, this
// essentially rules this out as a testing method. On the other hand:
//
// trig(atrig(z))
//
// can vary compare to z by an arbitrarily large amount. For one thing we
// have no control over the implementation of the trig functions, for another
// even if both functions were accurate to 1ulp (as accurate as transcendental
// number can get, thanks to the "table makers dilemma"), the errors can still
// be arbitrarily large - often the inverse trig functions will map a very large
// part of the complex domain into a small output domain, so you can never get
// back exactly where you started from. Consequently these tests are no more than
// sanity checks (just verifies that signs are correct and so on).
//
template <class T>
void test_inverse_trig(T)
{
using namespace std;
static const T interval = static_cast<T>(2.0L/128.0L);
T x, y;
std::cout << std::setprecision(std::numeric_limits<T>::digits10+2);
for(x = -1; x <= 1; x += interval)
{
for(y = -1; y <= 1; y += interval)
{
// acos:
std::complex<T> val(x, y), inter, result;
inter = boost::math::acos(val);
result = cos(inter);
if(!check_complex(val, result, 50))
{
std::cout << "Error in testing inverse complex cos for type " << typeid(T).name() << std::endl;
std::cout << " val= " << val << std::endl;
std::cout << " acos(val) = " << inter << std::endl;
std::cout << " cos(acos(val)) = " << result << std::endl;
}
// asin:
inter = boost::math::asin(val);
result = sin(inter);
if(!check_complex(val, result, 5))
{
std::cout << "Error in testing inverse complex sin for type " << typeid(T).name() << std::endl;
std::cout << " val= " << val << std::endl;
std::cout << " asin(val) = " << inter << std::endl;
std::cout << " sin(asin(val)) = " << result << std::endl;
}
}
}
static const T interval2 = static_cast<T>(3.0L/256.0L);
for(x = -3; x <= 3; x += interval2)
{
for(y = -3; y <= 3; y += interval2)
{
// asinh:
std::complex<T> val(x, y), inter, result;
inter = boost::math::asinh(val);
result = sinh(inter);
if(!check_complex(val, result, 5))
{
std::cout << "Error in testing inverse complex sinh for type " << typeid(T).name() << std::endl;
std::cout << " val= " << val << std::endl;
std::cout << " asinh(val) = " << inter << std::endl;
std::cout << " sinh(asinh(val)) = " << result << std::endl;
}
// acosh:
if(!((y == 0) && (x <= 1))) // can't test along the branch cut
{
inter = boost::math::acosh(val);
result = cosh(inter);
if(!check_complex(val, result, 60))
{
std::cout << "Error in testing inverse complex cosh for type " << typeid(T).name() << std::endl;
std::cout << " val= " << val << std::endl;
std::cout << " acosh(val) = " << inter << std::endl;
std::cout << " cosh(acosh(val)) = " << result << std::endl;
}
}
//
// There is a problem in testing atan and atanh:
// The inverse functions map a large input range to a much
// smaller output range, so at the extremes too rather different
// inputs may map to the same output value once rounded to N places.
// Consequently tan(atan(z)) can suffer from arbitrarily large errors
// even if individually they each have a small error bound. On the other
// hand we can't test atan(tan(z)) either because atan is multi-valued, so
// round-tripping in this direction isn't always possible.
// The following heuristic is designed to make the best of a bad job,
// using atan(tan(z)) where possible and tan(atan(z)) when it's not.
//
static const int tanh_error = 20;
if((0 != x) && (0 != y) && ((std::fabs(y) < 1) || (std::fabs(x) < 1)))
{
// atanh:
val = boost::math::atanh(val);
inter = tanh(val);
result = boost::math::atanh(inter);
if(!check_complex(val, result, tanh_error))
{
std::cout << "Error in testing inverse complex tanh for type " << typeid(T).name() << std::endl;
std::cout << " val= " << val << std::endl;
std::cout << " tanh(val) = " << inter << std::endl;
std::cout << " atanh(tanh(val)) = " << result << std::endl;
}
// atan:
if(!((x == 0) && (std::fabs(y) == 1))) // we can't test infinities here
{
val = std::complex<T>(x, y);
val = boost::math::atan(val);
inter = tan(val);
result = boost::math::atan(inter);
if(!check_complex(val, result, tanh_error))
{
std::cout << "Error in testing inverse complex tan for type " << typeid(T).name() << std::endl;
std::cout << " val= " << val << std::endl;
std::cout << " tan(val) = " << inter << std::endl;
std::cout << " atan(tan(val)) = " << result << std::endl;
}
}
}
else
{
// atanh:
inter = boost::math::atanh(val);
result = tanh(inter);
if(!check_complex(val, result, tanh_error))
{
std::cout << "Error in testing inverse complex atanh for type " << typeid(T).name() << std::endl;
std::cout << " val= " << val << std::endl;
std::cout << " atanh(val) = " << inter << std::endl;
std::cout << " tanh(atanh(val)) = " << result << std::endl;
}
// atan:
if(!((x == 0) && (std::fabs(y) == 1))) // we can't test infinities here
{
inter = boost::math::atan(val);
result = tan(inter);
if(!check_complex(val, result, tanh_error))
{
std::cout << "Error in testing inverse complex atan for type " << typeid(T).name() << std::endl;
std::cout << " val= " << val << std::endl;
std::cout << " atan(val) = " << inter << std::endl;
std::cout << " tan(atan(val)) = " << result << std::endl;
}
}
}
}
}
}
//
// check_spots:
// Various spot values, mostly the C99 special cases (infinites and NAN's).
// TODO: add spot checks for the Wolfram spot values.
//
template <class T>
void check_spots(const T&)
{
typedef std::complex<T> ct;
ct result;
static const T two = 2.0;
T eps = std::pow(two, T(1-std::numeric_limits<T>::digits)); // numeric_limits<>::epsilon way too small to be useful on Darwin.
static const T zero = 0;
static const T mzero = -zero;
static const T one = 1;
static const T pi = static_cast<T>(3.141592653589793238462643383279502884197L);
static const T half_pi = static_cast<T>(1.57079632679489661923132169163975144L);
static const T quarter_pi = static_cast<T>(0.78539816339744830961566084581987572L);
static const T three_quarter_pi = static_cast<T>(2.35619449019234492884698253745962716L);
//static const T log_two = static_cast<T>(0.69314718055994530941723212145817657L);
T infinity = std::numeric_limits<T>::infinity();
bool test_infinity = std::numeric_limits<T>::has_infinity;
T nan = 0;
bool test_nan = false;
#if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
// numeric_limits reports that a quiet NaN is present
// but an attempt to access it will terminate the program!!!!
if(std::numeric_limits<T>::has_quiet_NaN)
nan = std::numeric_limits<T>::quiet_NaN();
if(boost::math::detail::test_is_nan(nan))
test_nan = true;
#endif
#if defined(__DECCXX) && !defined(_IEEE_FP)
// Tru64 cxx traps infinities unless the -ieee option is used:
test_infinity = false;
#endif
//
// C99 spot tests for acos:
//
result = boost::math::acos(ct(zero));
check_complex(ct(half_pi), result, 2);
result = boost::math::acos(ct(mzero));
check_complex(ct(half_pi), result, 2);
result = boost::math::acos(ct(zero, mzero));
check_complex(ct(half_pi), result, 2);
result = boost::math::acos(ct(mzero, mzero));
check_complex(ct(half_pi), result, 2);
if(test_nan)
{
result = boost::math::acos(ct(zero,nan));
BOOST_CHECK_CLOSE(result.real(), half_pi, eps*200);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acos(ct(mzero,nan));
BOOST_CHECK_CLOSE(result.real(), half_pi, eps*200);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
}
if(test_infinity)
{
result = boost::math::acos(ct(zero, infinity));
BOOST_CHECK_CLOSE(result.real(), half_pi, eps*200);
BOOST_CHECK(result.imag() == -infinity);
result = boost::math::acos(ct(zero, -infinity));
BOOST_CHECK_CLOSE(result.real(), half_pi, eps*200);
BOOST_CHECK(result.imag() == infinity);
}
if(test_nan)
{
result = boost::math::acos(ct(one, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
}
if(test_infinity)
{
result = boost::math::acos(ct(-infinity, one));
BOOST_CHECK_CLOSE(result.real(), pi, eps*200);
BOOST_CHECK(result.imag() == -infinity);
result = boost::math::acos(ct(infinity, one));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK(result.imag() == -infinity);
result = boost::math::acos(ct(-infinity, -one));
BOOST_CHECK_CLOSE(result.real(), pi, eps*200);
BOOST_CHECK(result.imag() == infinity);
result = boost::math::acos(ct(infinity, -one));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK(result.imag() == infinity);
result = boost::math::acos(ct(-infinity, infinity));
BOOST_CHECK_CLOSE(result.real(), three_quarter_pi, eps*200);
BOOST_CHECK(result.imag() == -infinity);
result = boost::math::acos(ct(infinity, infinity));
BOOST_CHECK_CLOSE(result.real(), quarter_pi, eps*200);
BOOST_CHECK(result.imag() == -infinity);
result = boost::math::acos(ct(-infinity, -infinity));
BOOST_CHECK_CLOSE(result.real(), three_quarter_pi, eps*200);
BOOST_CHECK(result.imag() == infinity);
result = boost::math::acos(ct(infinity, -infinity));
BOOST_CHECK_CLOSE(result.real(), quarter_pi, eps*200);
BOOST_CHECK(result.imag() == infinity);
}
if(test_nan)
{
result = boost::math::acos(ct(infinity, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(std::fabs(result.imag()) == infinity);
result = boost::math::acos(ct(-infinity, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(std::fabs(result.imag()) == infinity);
result = boost::math::acos(ct(nan, zero));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acos(ct(nan, -zero));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acos(ct(nan, one));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acos(ct(nan, -one));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acos(ct(nan, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acos(ct(nan, infinity));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(result.imag() == -infinity);
result = boost::math::acos(ct(nan, -infinity));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(result.imag() == infinity);
}
//
// C99 spot tests for acosh:
//
result = boost::math::acosh(ct(zero, zero));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::acosh(ct(zero, mzero));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::acosh(ct(mzero, zero));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::acosh(ct(mzero, mzero));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
if(test_infinity)
{
result = boost::math::acosh(ct(one, infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::acosh(ct(one, -infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
}
if(test_nan)
{
result = boost::math::acosh(ct(one, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
}
if(test_infinity)
{
result = boost::math::acosh(ct(-infinity, one));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), pi, eps*200);
result = boost::math::acosh(ct(infinity, one));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK(result.imag() == 0);
result = boost::math::acosh(ct(-infinity, -one));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), -pi, eps*200);
result = boost::math::acosh(ct(infinity, -one));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK(result.imag() == 0);
result = boost::math::acosh(ct(-infinity, infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), three_quarter_pi, eps*200);
result = boost::math::acosh(ct(infinity, infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), quarter_pi, eps*200);
result = boost::math::acosh(ct(-infinity, -infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), -three_quarter_pi, eps*200);
result = boost::math::acosh(ct(infinity, -infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), -quarter_pi, eps*200);
}
if(test_nan)
{
result = boost::math::acosh(ct(infinity, nan));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acosh(ct(-infinity, nan));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acosh(ct(nan, one));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acosh(ct(nan, infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acosh(ct(nan, -one));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acosh(ct(nan, -infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::acosh(ct(nan, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
}
//
// C99 spot checks for asinh:
//
result = boost::math::asinh(ct(zero, zero));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK(result.imag() == 0);
result = boost::math::asinh(ct(mzero, zero));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK(result.imag() == 0);
result = boost::math::asinh(ct(zero, mzero));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK(result.imag() == 0);
result = boost::math::asinh(ct(mzero, mzero));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK(result.imag() == 0);
if(test_infinity)
{
result = boost::math::asinh(ct(one, infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::asinh(ct(one, -infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
result = boost::math::asinh(ct(-one, -infinity));
BOOST_CHECK(result.real() == -infinity);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
result = boost::math::asinh(ct(-one, infinity));
BOOST_CHECK(result.real() == -infinity);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
}
if(test_nan)
{
result = boost::math::asinh(ct(one, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::asinh(ct(-one, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::asinh(ct(zero, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
}
if(test_infinity)
{
result = boost::math::asinh(ct(infinity, one));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK(result.imag() == 0);
result = boost::math::asinh(ct(infinity, -one));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK(result.imag() == 0);
result = boost::math::asinh(ct(-infinity, -one));
BOOST_CHECK(result.real() == -infinity);
BOOST_CHECK(result.imag() == 0);
result = boost::math::asinh(ct(-infinity, one));
BOOST_CHECK(result.real() == -infinity);
BOOST_CHECK(result.imag() == 0);
result = boost::math::asinh(ct(infinity, infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), quarter_pi, eps*200);
result = boost::math::asinh(ct(infinity, -infinity));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK_CLOSE(result.imag(), -quarter_pi, eps*200);
result = boost::math::asinh(ct(-infinity, -infinity));
BOOST_CHECK(result.real() == -infinity);
BOOST_CHECK_CLOSE(result.imag(), -quarter_pi, eps*200);
result = boost::math::asinh(ct(-infinity, infinity));
BOOST_CHECK(result.real() == -infinity);
BOOST_CHECK_CLOSE(result.imag(), quarter_pi, eps*200);
}
if(test_nan)
{
result = boost::math::asinh(ct(infinity, nan));
BOOST_CHECK(result.real() == infinity);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::asinh(ct(-infinity, nan));
BOOST_CHECK(result.real() == -infinity);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::asinh(ct(nan, zero));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(result.imag() == 0);
result = boost::math::asinh(ct(nan, mzero));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(result.imag() == 0);
result = boost::math::asinh(ct(nan, one));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::asinh(ct(nan, -one));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::asinh(ct(nan, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::asinh(ct(nan, infinity));
BOOST_CHECK(std::fabs(result.real()) == infinity);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::asinh(ct(nan, -infinity));
BOOST_CHECK(std::fabs(result.real()) == infinity);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
}
//
// C99 special cases for atanh:
//
result = boost::math::atanh(ct(zero, zero));
BOOST_CHECK(result.real() == zero);
BOOST_CHECK(result.imag() == zero);
result = boost::math::atanh(ct(mzero, zero));
BOOST_CHECK(result.real() == zero);
BOOST_CHECK(result.imag() == zero);
result = boost::math::atanh(ct(zero, mzero));
BOOST_CHECK(result.real() == zero);
BOOST_CHECK(result.imag() == zero);
result = boost::math::atanh(ct(mzero, mzero));
BOOST_CHECK(result.real() == zero);
BOOST_CHECK(result.imag() == zero);
if(test_nan)
{
result = boost::math::atanh(ct(zero, nan));
BOOST_CHECK(result.real() == zero);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::atanh(ct(-zero, nan));
BOOST_CHECK(result.real() == zero);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
}
if(test_infinity)
{
result = boost::math::atanh(ct(one, zero));
BOOST_CHECK_EQUAL(result.real(), infinity);
BOOST_CHECK_EQUAL(result.imag(), zero);
result = boost::math::atanh(ct(-one, zero));
BOOST_CHECK_EQUAL(result.real(), -infinity);
BOOST_CHECK_EQUAL(result.imag(), zero);
result = boost::math::atanh(ct(-one, -zero));
BOOST_CHECK_EQUAL(result.real(), -infinity);
BOOST_CHECK_EQUAL(result.imag(), zero);
result = boost::math::atanh(ct(one, -zero));
BOOST_CHECK_EQUAL(result.real(), infinity);
BOOST_CHECK_EQUAL(result.imag(), zero);
result = boost::math::atanh(ct(pi, infinity));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::atanh(ct(pi, -infinity));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
result = boost::math::atanh(ct(-pi, -infinity));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
result = boost::math::atanh(ct(-pi, infinity));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
}
if(test_nan)
{
result = boost::math::atanh(ct(pi, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::atanh(ct(-pi, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
}
if(test_infinity)
{
result = boost::math::atanh(ct(infinity, pi));
BOOST_CHECK(result.real() == zero);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::atanh(ct(infinity, -pi));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
result = boost::math::atanh(ct(-infinity, -pi));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
result = boost::math::atanh(ct(-infinity, pi));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::atanh(ct(infinity, infinity));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::atanh(ct(infinity, -infinity));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
result = boost::math::atanh(ct(-infinity, -infinity));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
result = boost::math::atanh(ct(-infinity, infinity));
BOOST_CHECK_EQUAL(result.real(), zero);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
}
if(test_nan)
{
result = boost::math::atanh(ct(infinity, nan));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::atanh(ct(-infinity, nan));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::atanh(ct(nan, pi));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::atanh(ct(nan, -pi));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
result = boost::math::atanh(ct(nan, infinity));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK_CLOSE(result.imag(), half_pi, eps*200);
result = boost::math::atanh(ct(nan, -infinity));
BOOST_CHECK(result.real() == 0);
BOOST_CHECK_CLOSE(result.imag(), -half_pi, eps*200);
result = boost::math::atanh(ct(nan, nan));
BOOST_CHECK(boost::math::detail::test_is_nan(result.real()));
BOOST_CHECK(boost::math::detail::test_is_nan(result.imag()));
}
}
//
// test_boundaries:
// This is an accuracy test, sets the real and imaginary components
// of the input argument to various "boundary conditions" that exist
// inside the implementation. Then computes the result at double precision
// and again at float precision. The double precision result will be
// computed using the "regular" code, where as the float precision versions
// will calculate the result using the "exceptional value" handlers, so
// we end up comparing the values calculated by two different methods.
//
const float boundaries[] = {
0,
1,
2,
(std::numeric_limits<float>::max)(),
(std::numeric_limits<float>::min)(),
std::numeric_limits<float>::epsilon(),
std::sqrt((std::numeric_limits<float>::max)()) / 8,
static_cast<float>(4) * std::sqrt((std::numeric_limits<float>::min)()),
0.6417F,
1.5F,
std::sqrt((std::numeric_limits<float>::max)()) / 2,
std::sqrt((std::numeric_limits<float>::min)()),
1.0F / 0.3F,
};
void do_test_boundaries(float x, float y)
{
std::complex<float> r1 = boost::math::asin(std::complex<float>(x, y));
std::complex<double> dr = boost::math::asin(std::complex<double>(x, y));
std::complex<float> r2(static_cast<float>(dr.real()), static_cast<float>(dr.imag()));
check_complex(r2, r1, 5);
r1 = boost::math::acos(std::complex<float>(x, y));
dr = boost::math::acos(std::complex<double>(x, y));
r2 = std::complex<float>(std::complex<double>(dr.real(), dr.imag()));
check_complex(r2, r1, 5);
r1 = boost::math::atanh(std::complex<float>(x, y));
dr = boost::math::atanh(std::complex<double>(x, y));
r2 = std::complex<float>(std::complex<double>(dr.real(), dr.imag()));
check_complex(r2, r1, 5);
}
void test_boundaries(float x, float y)
{
do_test_boundaries(x, y);
do_test_boundaries(-x, y);
do_test_boundaries(-x, -y);
do_test_boundaries(x, -y);
}
void test_boundaries(float x)
{
for(unsigned i = 0; i < sizeof(boundaries)/sizeof(float); ++i)
{
test_boundaries(x, boundaries[i]);
test_boundaries(x, boundaries[i] + std::numeric_limits<float>::epsilon()*boundaries[i]);
test_boundaries(x, boundaries[i] - std::numeric_limits<float>::epsilon()*boundaries[i]);
}
}
void test_boundaries()
{
for(unsigned i = 0; i < sizeof(boundaries)/sizeof(float); ++i)
{
test_boundaries(boundaries[i]);
test_boundaries(boundaries[i] + std::numeric_limits<float>::epsilon()*boundaries[i]);
test_boundaries(boundaries[i] - std::numeric_limits<float>::epsilon()*boundaries[i]);//here
}
}
int test_main(int, char*[])
{
std::cout << "Running complex trig sanity checks for type float." << std::endl;
test_inverse_trig(float(0));
std::cout << "Running complex trig sanity checks for type double." << std::endl;
test_inverse_trig(double(0));
//test_inverse_trig((long double)(0));
std::cout << "Running complex trig spot checks for type float." << std::endl;
check_spots(float(0));
std::cout << "Running complex trig spot checks for type double." << std::endl;
check_spots(double(0));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
std::cout << "Running complex trig spot checks for type long double." << std::endl;
check_spots((long double)(0));
#endif
std::cout << "Running complex trig boundary and accuracy tests." << std::endl;
test_boundaries();
return 0;
}