diff --git a/include/boost/decimal.hpp b/include/boost/decimal.hpp index b746182af..af85af1ef 100644 --- a/include/boost/decimal.hpp +++ b/include/boost/decimal.hpp @@ -37,6 +37,7 @@ #include #include #include +#include #if defined(__clang__) && !defined(__GNUC__) # pragma clang diagnostic pop diff --git a/include/boost/decimal/complex.hpp b/include/boost/decimal/complex.hpp new file mode 100644 index 000000000..6c240b390 --- /dev/null +++ b/include/boost/decimal/complex.hpp @@ -0,0 +1,394 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_COMPLEX_HPP +#define BOOST_DECIMAL_COMPLEX_HPP + +#include +#include +#include +#include +#include +#include + +#ifndef BOOST_DECIMAL_BUILD_MODULE +#include +#include +#include +#endif + +namespace boost { +namespace decimal { + +template +class complex +{ +private: + T real_; + T imag_; + +public: + constexpr complex() noexcept = default; + constexpr explicit complex(T real) noexcept : real_ {real}, imag_ {T{0}} {} + constexpr complex(T real, T imag) noexcept : real_ {real}, imag_ {imag} {} + + constexpr T real() const noexcept { return real_; } + constexpr T imag() const noexcept { return imag_; } + + constexpr complex operator+() const { return *this; } + constexpr complex operator-() const { return {-real_, -imag_}; } + + friend constexpr complex operator+(const complex& lhs, const complex& rhs) noexcept + { + return {lhs.real_ + rhs.real_, lhs.imag_ + rhs.imag_}; + } + + friend constexpr complex operator+(const complex& lhs, const T& rhs) noexcept + { + return {lhs.real_ + rhs, lhs.imag_}; + } + + friend constexpr complex operator+(const T& lhs, const complex& rhs) noexcept + { + return {lhs + rhs.real_, rhs.imag_}; + } + + friend constexpr complex operator-(const complex& lhs, const complex& rhs) noexcept + { + return {lhs.real_ - rhs.real_, lhs.imag_ - rhs.imag_}; + } + + friend constexpr complex operator-(const complex& lhs, const T& rhs) noexcept + { + return {lhs.real_ - rhs, lhs.imag_}; + } + + friend constexpr complex operator-(const T& lhs, const complex& rhs) noexcept + { + return {lhs - rhs.real_, -rhs.imag_}; + } + + friend constexpr complex operator*(const complex& lhs, const complex& rhs) noexcept + { + return {lhs.real_ * rhs.real_ - lhs.imag_ * rhs.imag_, lhs.imag_ * rhs.real_ + lhs.real_ * rhs.imag_}; + } + + friend constexpr complex operator*(const complex& lhs, const T& rhs) noexcept + { + return {lhs.real_ * rhs, lhs.imag_ * rhs}; + } + + friend constexpr complex operator*(const T& lhs, const complex& rhs) noexcept + { + return {lhs * rhs.real_, lhs * rhs.imag_}; + } + + friend constexpr complex operator/(const complex& lhs, const complex& rhs) noexcept + { + const T divisor = rhs.real_ * rhs.real_ + rhs.imag_ * rhs.imag_; + const T real_part = (lhs.real_ * rhs.real_ + lhs.imag_ * rhs.imag_) / divisor; + const T imag_part = (lhs.imag_ * rhs.real_ - lhs.real_ * rhs.imag_) / divisor; + return {real_part, imag_part}; + } + + friend constexpr complex operator/(const complex& lhs, const T& rhs) noexcept + { + const T divisor = rhs * rhs; + const T real_part = (lhs.real_ * rhs) / divisor; + const T imag_part = (lhs.imag_ * rhs) / divisor; + return {real_part, imag_part}; + } + + friend constexpr complex operator/(const T& lhs, const complex& rhs) noexcept + { + const T divisor = rhs.real_ * rhs.real_ + rhs.imag_ * rhs.imag_; + const T real_part = (lhs * rhs.real_) / divisor; + const T imag_part = -(lhs.real_ * rhs.imag_) / divisor; + return {real_part, imag_part}; + } + + constexpr complex& operator+=(const complex& rhs) noexcept + { + *this = *this + rhs; + return *this; + } + + constexpr complex& operator+=(const T& rhs) noexcept + { + *this = *this + rhs; + return *this; + } + + constexpr complex& operator-=(const complex& rhs) noexcept + { + *this = *this - rhs; + return *this; + } + + constexpr complex& operator-=(const T& rhs) noexcept + { + *this = *this - rhs; + return *this; + } + + constexpr complex& operator*=(const complex& rhs) noexcept + { + *this = *this * rhs; + return *this; + } + + constexpr complex& operator*=(const T& rhs) noexcept + { + *this = *this * rhs; + return *this; + } + + constexpr complex& operator/=(const complex& rhs) noexcept + { + *this = *this / rhs; + return *this; + } + + constexpr complex& operator/=(const T& rhs) noexcept + { + *this = *this / rhs; + return *this; + } + + constexpr bool operator==(const complex& rhs) const noexcept + { + return real_ == rhs.real_ && imag_ == rhs.imag_; + } + + constexpr bool operator!=(const complex& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr bool operator==(const T& rhs) const noexcept + { + return real_ == rhs && imag_ == T{0}; + } + + constexpr bool operator!=(const T& rhs) const noexcept + { + return !(*this == rhs); + } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const complex& z) + { + std::basic_ostringstream s; + s.flags(os.flags()); + s.imbue(os.getloc()); + s.precision(os.precision()); + s << '(' << z.real_ << ',' << z.imag_ << ')'; + + return os << s.str(); + } + + // Supported formats: + // 1) real + // 2) (real) + // 3) (real, imag) + template + friend std::basic_istream& operator>>(std::basic_istream& is, complex& z) + { + CharT ch {}; + T real = T{0}; + T imag = T{0}; + + is >> std::ws; + is.get(ch); + + if (ch == '(') + { + // Expecting a format like 2 or 3 + is >> std::ws >> real; + is.get(ch); + if (ch == ',') + { + // A comma indicates it's 3 + is >> std::ws >> imag; + is.get(ch); // Should be ')' + } + else if (ch != ')') + { + // Syntax error: unexpected character + is.setstate(std::ios_base::failbit); + return is; + } + } + else + { + // No parentheses, just a real number from format 1 + is.putback(ch); + is >> real; + } + + if (!is.fail()) + { + z.real_ = real; + z.imag_ = imag; + } + + return is; + } +}; + +// Polar is specialized for each type since libc++ uses just template which leads +// to a compiler error + +constexpr complex polar(const decimal32& rho, const decimal32& theta) noexcept +{ + return {rho * cos(theta), rho * sin(theta)}; +} + +constexpr complex polar(const decimal64& rho, const decimal64& theta) noexcept +{ + return {rho * cos(theta), rho * sin(theta)}; +} + +constexpr complex polar(const decimal128& rho, const decimal128& theta) noexcept +{ + return {rho * cos(theta), rho * sin(theta)}; +} + +template +constexpr T real(const complex& z) noexcept +{ + return z.real(); +} + +template +constexpr T imag(const complex& z) noexcept +{ + return z.imag(); +} + +template +constexpr T abs(const complex& z) noexcept +{ + return hypot(z.real(), z.imag()); +} + +template +constexpr T arg(const complex& z) noexcept +{ + return atan2(z.imag(), z.real()); +} + +template +constexpr T norm(const complex& z) noexcept +{ + return z.real() * z.real() + z.imag() * z.imag(); +} + +template +constexpr complex conj(const complex& z) noexcept +{ + return {z.real(), -z.imag()}; +} + +template +constexpr complex proj(const complex& z) noexcept +{ + if (isinf(z.real()) || isinf(z.imag())) + { + return {std::numeric_limits::infinity(), copysign(T{0}, z.imag())}; + } + + return z; +} + +template +constexpr complex exp(const complex& z) noexcept +{ + return polar(exp(z.real()), z.imag()); +} + +template +constexpr complex log(const complex& z) noexcept +{ + return {log(abs(z)), arg(z)}; +} + +template +constexpr complex log10(const complex& z) noexcept +{ + return log(z) / log(T{10}); +} + +template +constexpr complex pow(const complex& x, const complex& y) noexcept +{ + return exp(y * log(x)); +} + +template +constexpr complex pow(const T& x, const complex& y) noexcept +{ + const complex new_x {x}; + return exp(y * log(new_x)); +} + +template +constexpr complex pow(const complex& x, const T& y) noexcept +{ + const complex new_y {y}; + return exp(new_y * log(x)); +} + +template +constexpr complex sqrt(const complex& z) noexcept +{ + return polar(sqrt(abs(z)), arg(z) / 2); +} + +template +constexpr complex sinh(const complex& z) noexcept +{ + return {sinh(z.real()) * cos(z.imag()), cosh(z.real()) * sin(z.imag())}; +} + +template +constexpr complex cosh(const complex& z) noexcept +{ + return {cosh(z.real()) * cos(z.imag()), sinh(z.real()) * sin(z.imag())}; +} + +template +constexpr complex tanh(const complex& z) noexcept +{ + return {sinh(z) / cosh(z)}; +} + +} //namespace decimal +} //namespace boost + +namespace std { + +template <> +class complex : public boost::decimal::complex +{ +public: + using boost::decimal::complex::complex; +}; + +template <> +class complex : public boost::decimal::complex +{ +public: + using boost::decimal::complex::complex; +}; + +template <> +class complex : public boost::decimal::complex +{ + using boost::decimal::complex::complex; +}; + +} // namespace std + +#endif // BOOST_DECIMAL_COMPLEX_HPP diff --git a/test/Jamfile b/test/Jamfile index cf017d74b..5416bb9d6 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -75,6 +75,7 @@ run test_big_uints.cpp ; run test_boost_math_univariate_stats.cpp ; run test_cbrt.cpp ; run test_cmath.cpp ; +run test_complex.cpp ; run test_constants.cpp ; run test_cosh.cpp ; run test_decimal32.cpp ; diff --git a/test/test_complex.cpp b/test/test_complex.cpp new file mode 100644 index 000000000..ef5759a29 --- /dev/null +++ b/test/test_complex.cpp @@ -0,0 +1,708 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +// Propogates up from boost.math +#define _SILENCE_CXX23_DENORM_DEPRECATION_WARNING + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wold-style-cast" +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wconversion" +# pragma clang diagnostic ignored "-Wsign-conversion" +# pragma clang diagnostic ignored "-Wfloat-equal" +# pragma clang diagnostic ignored "-Wfloat-conversion" + +# if (__clang_major__ >= 10 && !defined(__APPLE__)) || __clang_major__ >= 13 +# pragma clang diagnostic ignored "-Wdeprecated-copy" +# endif + +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wold-style-cast" +# pragma GCC diagnostic ignored "-Wundef" +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wsign-conversion" +# pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Wfloat-conversion" +#endif + +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::decimal; + +template +bool test_equal(T lhs, T rhs, int tol = 15) noexcept +{ + using std::fabs; + const bool res = fabs(lhs - rhs) < static_cast(tol) * std::numeric_limits::epsilon(); + + if (!res) + { + // LCOV_EXCL_START + std::cerr << "LHS: " << lhs + << "\nRHS: " << rhs + << "\nDist: " << fabs(lhs - rhs) / std::numeric_limits::epsilon() << std::endl; + // LCOV_EXCL_STOP + } + + return res; +} + +template +void test_construction() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + std::cerr << typeid(complex_scalar).name() << std::endl; + + complex_scalar v {}; + BOOST_TEST(test_equal(v.real(), T{0})); + BOOST_TEST(test_equal(v.imag(), T{0})); + + complex_scalar v1 {T{1}}; + BOOST_TEST(test_equal(v1.real(), T{1})); + BOOST_TEST(test_equal(v1.imag(), T{0})); + + complex_scalar v2 {T{2}, T{2}}; + BOOST_TEST(test_equal(v2.real(), T{2})); + BOOST_TEST(test_equal(v2.imag(), T{2})); +} + +template +void test_unary_operators() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + const complex_scalar val {T{2}, T{-2}}; + complex_scalar pos_val = +val; + BOOST_TEST(test_equal(val.real(), pos_val.real())); + BOOST_TEST(test_equal(val.imag(), pos_val.imag())); + + complex_scalar neg_val = -val; + BOOST_TEST(test_equal(neg_val.real(), T{-2})); + BOOST_TEST(test_equal(neg_val.imag(), T{2})); +} + +template +void test_addition() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs_1 {T{1}, T{1}}; + complex_scalar rhs_1 {T{2}, T{2}}; + complex_scalar res_1 = lhs_1 + rhs_1; + + BOOST_TEST(test_equal(res_1.real(), T{3})); + BOOST_TEST(test_equal(res_1.imag(), T{3})); +} + +template +void test_subtraction() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs_1 {T{1}, T{1}}; + complex_scalar rhs_1 {T{2}, T{2}}; + complex_scalar res_1 = lhs_1 - rhs_1; + + BOOST_TEST(test_equal(res_1.real(), T{-1})); + BOOST_TEST(test_equal(res_1.imag(), T{-1})); +} + +template +void test_multiplication() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs_1 {T{-3}, T{3}}; + complex_scalar rhs_1 {T{2}, T{2}}; + complex_scalar res_1 = lhs_1 * rhs_1; + + BOOST_TEST(test_equal(res_1.real(), T{-12})); + BOOST_TEST(test_equal(res_1.imag(), T{0})); +} + +template +void test_division() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs_1 {T{6}, T{2}}; + complex_scalar rhs_1 {T{2}, T{2}}; + complex_scalar res_1 = lhs_1 / rhs_1; + + BOOST_TEST(test_equal(res_1.real(), T{2})); + BOOST_TEST(test_equal(res_1.imag(), T{-1})); +} + +template +void test_equality() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{2}, T{-1}}; + complex_scalar rhs {T{2}, T{1}}; + + BOOST_TEST(lhs != rhs); + BOOST_TEST(!(lhs == rhs)); + + T scalar_rhs {T{2}}; + + BOOST_TEST(lhs != scalar_rhs); + BOOST_TEST(!(lhs == scalar_rhs)); + + lhs = complex_scalar{T{2}, T{0}}; + BOOST_TEST(lhs == scalar_rhs); + BOOST_TEST(!(lhs != scalar_rhs)); +} + +template +void test_non_member_real_imag() +{ + using std::complex; + using std::polar; + using std::real; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{2}, T{-1}}; + + BOOST_TEST(test_equal(real(lhs), lhs.real())); + BOOST_TEST(test_equal(imag(lhs), lhs.imag())); +} + +template +void test_abs() +{ + using std::complex; + using std::polar; + using std::abs; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{1}, T{1}}; + + BOOST_TEST(test_equal(static_cast(abs(lhs)), static_cast(sqrt(T{2})))); +} + +template ::value, bool> = true> +void test_arg() +{ + using std::complex; + using std::polar; + using std::arg; + using boost::math::constants::pi; + using boost::math::constants::half_pi; + using complex_scalar = decltype(polar(T(), T())); + + BOOST_TEST(test_equal(arg(complex_scalar{T{1}, T{0}}), T{0})); + BOOST_TEST(test_equal(arg(complex_scalar{T{0}, T{0}}), T{0})); + BOOST_TEST(test_equal(arg(complex_scalar{T{0}, T{1}}), half_pi())); + BOOST_TEST(test_equal(arg(complex_scalar{T{-1}, T{0}}), pi())); +} + +template ::value, bool> = true> +void test_arg() +{ + using std::complex; + using std::polar; + using std::arg; + using complex_scalar = decltype(polar(T(), T())); + + BOOST_TEST(test_equal(arg(complex_scalar{T{1}, T{0}}), T{0})); + BOOST_TEST(test_equal(arg(complex_scalar{T{0}, T{0}}), T{0})); + BOOST_TEST(test_equal(arg(complex_scalar{T{0}, T{1}}), numbers::pi_v / 2)); + BOOST_TEST(test_equal(arg(complex_scalar{T{-1}, T{0}}), numbers::pi_v)); +} + + +template +void test_norm() +{ + using std::complex; + using std::polar; + using std::norm; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{3}, T{4}}; + + BOOST_TEST(test_equal(norm(lhs), T{25})); +} + +template +void test_conj() +{ + using std::complex; + using std::polar; + using std::conj; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{1}, T{1}}; + complex_scalar rhs {T{1}, T{-1}}; + + BOOST_TEST_EQ(conj(lhs), rhs); +} + +template +void test_proj() +{ + using std::complex; + using std::polar; + using std::proj; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{1}, T{1}}; + BOOST_TEST_EQ(lhs, proj(lhs)); + + lhs = complex_scalar{T{std::numeric_limits::infinity()}, T{1}}; + complex_scalar rhs = complex_scalar{T{std::numeric_limits::infinity()}, T{0}}; + BOOST_TEST_EQ(proj(lhs), rhs); +} + +template ::value, bool> = true> +void test_exp() +{ + using std::complex; + using std::polar; + using std::exp; + using boost::math::constants::pi; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{0}, pi()}; + lhs = exp(lhs); + complex_scalar rhs {T{-1}, T{0}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); +} + +template ::value, bool> = true> +void test_exp() +{ + using std::complex; + using std::polar; + using std::exp; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{0}, numbers::pi_v}; + lhs = exp(lhs); + complex_scalar rhs {T{-1}, T{0}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); +} + +template ::value, bool> = true> +void test_log() +{ + using std::complex; + using std::polar; + using std::log; + using boost::math::constants::half_pi; + using boost::math::constants::pi; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{0}, T{1}}; + lhs = log(lhs); + complex_scalar rhs {T{0}, half_pi()}; + + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + + lhs = {T{-1}, T{0}}; + lhs = log(lhs); + rhs = {T{0}, pi()}; + + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + + // Other side of the cut line + lhs = {T {-1}, -T {0}}; + lhs = log(lhs); + rhs = {T {0}, -pi()}; + + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); +} + +/* +template ::value, bool> = true> +void test_log() +{ + using std::complex; + using std::polar; + using std::log; + using boost::math::constants::half_pi; + using boost::math::constants::pi; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{0}, T{1}}; + lhs = log(lhs); + complex_scalar rhs {T{0}, numbers::pi_v / 2}; + + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + + lhs = {T{-1}, T{0}}; + lhs = log(lhs); + rhs = {T{0}, numbers::pi_v}; + + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + + // Other side of the cut line + lhs = {T {-1}, -T {0}}; + lhs = log(lhs); + rhs = {T {0}, -numbers::pi_v}; + + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); +} +*/ + +template +void test_scalar_addition() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs_1 {T{1}, T{1}}; + T rhs_1 {T{2}}; + complex_scalar res_1 = lhs_1 + rhs_1; + + BOOST_TEST(test_equal(res_1.real(), T{3})); + BOOST_TEST(test_equal(res_1.imag(), T{1})); +} + +template +void test_scalar_subtraction() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs_1 {T{1}, T{1}}; + T rhs_1 {T{2}}; + complex_scalar res_1 = lhs_1 - rhs_1; + + BOOST_TEST(test_equal(res_1.real(), T{-1})); + BOOST_TEST(test_equal(res_1.imag(), T{1})); +} + +template +void test_scalar_multiplication() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs_1 {T{3}, T{2}}; + T rhs_1 {T{2}}; + complex_scalar res_1 = lhs_1 * rhs_1; + + BOOST_TEST(test_equal(res_1.real(), T{6})); + BOOST_TEST(test_equal(res_1.imag(), T{4})); +} + +template +void test_scalar_division() +{ + using std::complex; + using std::polar; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs_1 {T{4}, T{2}}; + T rhs_1 {T{2}}; + complex_scalar res_1 = lhs_1 / rhs_1; + + BOOST_TEST(test_equal(res_1.real(), T{2})); + BOOST_TEST(test_equal(res_1.imag(), T{1})); +} + +/* +template +void test_log10() +{ + using std::complex; + using std::polar; + using std::log10; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{-100}, T{0}}; + lhs = log10(lhs); + complex_scalar rhs {T{2}, static_cast(1.36438)}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); +} +*/ + +/* +template +void test_pow() +{ + using std::complex; + using std::polar; + using std::pow; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{1}, T{2}}; + lhs = pow(lhs, T{2}); + complex_scalar rhs {T{-3}, T{4}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real(), 100)); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag(), 100)); + + lhs = {T{-1}, T{0}}; + lhs = pow(lhs, T{1}/2); + rhs = {T{0}, T{1}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + + // Check other side of the cut + // MSVC 14.0 gets this wrong with float and double + #if !defined(_MSC_VER) || (_MSC_VER > 1900) + lhs = {T {-1}, -T {0}}; + lhs = pow(lhs, T {1} / 2); + rhs = {T {0}, T {-1}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + #endif +} +*/ + +template +void test_sqrt() +{ + using std::complex; + using std::polar; + using std::sqrt; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{4}, T{0}}; + lhs = sqrt(lhs); + complex_scalar rhs {T{2}, T{0}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + + // Check other side of the cut + lhs = {T {4}, -T {0}}; + lhs = sqrt(lhs); + rhs = {T {2}, -T {0}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); +} + +template +void test_sinh() +{ + using std::complex; + using std::polar; + using std::sinh; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{1}, T{0}}; + lhs = sinh(lhs); + complex_scalar rhs {sinh(T{1}), T{0}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + + lhs = {T{0}, T{1}}; + lhs = sinh(lhs); + rhs = {T{0}, sin(T{1})}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); +} + +template +void test_cosh() +{ + using std::complex; + using std::polar; + using std::sinh; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{1}, T{0}}; + lhs = cosh(lhs); + complex_scalar rhs {cosh(T{1}), T{0}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + + lhs = {T{0}, T{1}}; + lhs = cosh(lhs); + rhs = {cos(T{1}), T{0}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); +} + +template +void test_tanh() +{ + using std::complex; + using std::polar; + using std::sinh; + using complex_scalar = decltype(polar(T(), T())); + + complex_scalar lhs {T{1}, T{0}}; + lhs = tanh(lhs); + complex_scalar rhs {tanh(T{1}), T{0}}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); + + lhs = {T{0}, T{1}}; + lhs = tanh(lhs); + rhs = {T{0}, tan(T{1})}; + BOOST_TEST(test_equal(lhs.real(), rhs.real())); + BOOST_TEST(test_equal(lhs.imag(), rhs.imag())); +} + +int main() +{ + test_construction(); + test_construction(); + test_construction(); + test_construction(); + + test_unary_operators(); + test_unary_operators(); + test_unary_operators(); + test_unary_operators(); + + test_addition(); + test_addition(); + test_addition(); + test_addition(); + + test_subtraction(); + test_subtraction(); + test_subtraction(); + test_subtraction(); + + test_multiplication(); + test_multiplication(); + test_multiplication(); + test_multiplication(); + + test_division(); + test_division(); + test_division(); + test_division(); + + test_equality(); + test_equality(); + test_equality(); + test_equality(); + + test_non_member_real_imag(); + test_non_member_real_imag(); + test_non_member_real_imag(); + test_non_member_real_imag(); + + test_abs(); + test_abs(); + test_abs(); + test_abs(); + + test_arg(); + test_arg(); + test_arg(); + test_arg(); + + test_norm(); + test_norm(); + test_norm(); + test_norm(); + + test_conj(); + test_conj(); + test_conj(); + test_conj(); + + test_proj(); + test_proj(); + test_proj(); + test_proj(); + + test_exp(); + test_exp(); + test_exp(); + test_exp(); + + /* + test_log(); + test_log(); + test_log(); + test_log(); + */ + + test_scalar_addition(); + test_scalar_addition(); + test_scalar_addition(); + test_scalar_addition(); + + test_scalar_subtraction(); + test_scalar_subtraction(); + test_scalar_subtraction(); + test_scalar_subtraction(); + + test_scalar_multiplication(); + test_scalar_multiplication(); + test_scalar_multiplication(); + test_scalar_multiplication(); + + test_scalar_division(); + test_scalar_division(); + test_scalar_division(); + test_scalar_division(); + + /* + test_log10(); + test_log10(); + test_log10(); + test_log10(); + + test_pow(); + test_pow(); + test_pow(); + test_pow(); + */ + + test_sqrt(); + test_sqrt(); + test_sqrt(); + test_sqrt(); + + test_sinh(); + test_sinh(); + test_sinh(); + test_sinh(); + + test_cosh(); + test_cosh(); + test_cosh(); + test_cosh(); + + test_tanh(); + test_tanh(); + test_tanh(); + test_tanh(); + + return boost::report_errors(); +}