/////////////////////////////////////////////////////////////////////////////// // Copyright 2024 Matt Borland. Distributed under 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 #include #include #include #include #include #include #include using namespace boost::multiprecision; template bool test_equal(T lhs, T rhs, int tol = 10) 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 ::value, bool> = true> void test_construction() { using std::complex; using complex_scalar = std::complex; std::cerr << typeid(complex_scalar).name() << std::endl; complex_scalar v {}; BOOST_TEST_EQ(v.real(), T{0}); BOOST_TEST_EQ(v.imag(), T{0}); complex_scalar v1 {T{1}}; BOOST_TEST_EQ(v1.real(), T{1}); BOOST_TEST_EQ(v1.imag(), T{0}); complex_scalar v2 {T{2}, T{2}}; BOOST_TEST_EQ(v2.real(), T{2}); BOOST_TEST_EQ(v2.imag(), T{2}); } template ::value, bool> = true> void test_construction() { using std::complex; using complex_scalar = std::complex; std::cerr << typeid(complex_scalar).name() << std::endl; complex_scalar v {}; BOOST_TEST_EQ(v.real(), T{0}); BOOST_TEST_EQ(v.imag(), T{0}); complex_scalar v1 {T{1}}; BOOST_TEST_EQ(v1.real(), T{1}); BOOST_TEST_EQ(v1.imag(), T{0}); complex_scalar v2 {T{2}, T{2}}; BOOST_TEST_EQ(v2.real(), T{2}); BOOST_TEST_EQ(v2.imag(), T{2}); complex_scalar v3 = std::polar(T(1), T(2)); complex_scalar v4 = boost::multiprecision::polar(T(1), T(2)); BOOST_TEST_EQ(v3.real(), v4.real()); BOOST_TEST_EQ(v3.imag(), v4.imag()); } template void test_unary_operators() { using std::complex; using complex_scalar = std::complex; const complex_scalar val {T{2}, T{-2}}; complex_scalar pos_val = +val; BOOST_TEST_EQ(val.real(), pos_val.real()); BOOST_TEST_EQ(val.imag(), pos_val.imag()); complex_scalar neg_val = -val; BOOST_TEST_EQ(neg_val.real(), T{-2}); BOOST_TEST_EQ(neg_val.imag(), T{2}); } template void test_addition() { using std::complex; using complex_scalar = std::complex; 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_EQ(res_1.real(), T{3}); BOOST_TEST_EQ(res_1.imag(), T{3}); } template void test_subtraction() { using std::complex; using complex_scalar = std::complex; 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_EQ(res_1.real(), T{-1}); BOOST_TEST_EQ(res_1.imag(), T{-1}); } template void test_multiplication() { using std::complex; using complex_scalar = std::complex; 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_EQ(res_1.real(), T{-12}); BOOST_TEST_EQ(res_1.imag(), T{0}); } template void test_division() { using std::complex; using complex_scalar = std::complex; 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_EQ(res_1.real(), T{2}); BOOST_TEST_EQ(res_1.imag(), T{-1}); } template void test_equality() { using std::complex; using complex_scalar = std::complex; 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::real; using complex_scalar = std::complex; complex_scalar lhs {T{2}, T{-1}}; BOOST_TEST_EQ(real(lhs), lhs.real()); BOOST_TEST_EQ(imag(lhs), lhs.imag()); } template void test_abs() { using std::complex; using std::abs; using complex_scalar = std::complex; complex_scalar lhs {T{1}, T{1}}; BOOST_TEST(test_equal(static_cast(abs(lhs)), static_cast(sqrt(T{2})))); } template void test_arg() { using std::complex; using std::arg; using boost::math::constants::pi; using boost::math::constants::half_pi; using complex_scalar = std::complex; 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 void test_norm() { using std::complex; using std::norm; using complex_scalar = std::complex; complex_scalar lhs {T{3}, T{4}}; BOOST_TEST(test_equal(norm(lhs), T{25})); } template void test_conj() { using std::complex; using std::conj; using complex_scalar = std::complex; 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::proj; using complex_scalar = std::complex; 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); lhs = complex_scalar{T{std::numeric_limits::infinity()}, T{-1}}; rhs = complex_scalar{T{std::numeric_limits::infinity()}, T{-0}}; BOOST_TEST_EQ(proj(lhs), rhs); } template void test_exp() { using std::complex; using std::exp; using boost::math::constants::pi; using complex_scalar = std::complex; 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 void test_log() { using std::complex; using std::log; using boost::math::constants::half_pi; using boost::math::constants::pi; using complex_scalar = std::complex; 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())); BOOST_IF_CONSTEXPR (!std::is_same::value) { // 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 void test_scalar_addition() { using std::complex; using complex_scalar = std::complex; complex_scalar lhs_1 {T{1}, T{1}}; T rhs_1 {T{2}}; complex_scalar res_1 = lhs_1 + rhs_1; BOOST_TEST_EQ(res_1.real(), T{3}); BOOST_TEST_EQ(res_1.imag(), T{1}); } template void test_scalar_subtraction() { using std::complex; using complex_scalar = std::complex; complex_scalar lhs_1 {T{1}, T{1}}; T rhs_1 {T{2}}; complex_scalar res_1 = lhs_1 - rhs_1; BOOST_TEST_EQ(res_1.real(), T{-1}); BOOST_TEST_EQ(res_1.imag(), T{1}); } template void test_scalar_multiplication() { using std::complex; using complex_scalar = std::complex; complex_scalar lhs_1 {T{3}, T{2}}; T rhs_1 {T{2}}; complex_scalar res_1 = lhs_1 * rhs_1; BOOST_TEST_EQ(res_1.real(), T{6}); BOOST_TEST_EQ(res_1.imag(), T{4}); } template void test_scalar_division() { using std::complex; using complex_scalar = std::complex; complex_scalar lhs_1 {T{4}, T{2}}; T rhs_1 {T{2}}; complex_scalar res_1 = lhs_1 / rhs_1; BOOST_TEST_EQ(res_1.real(), T{2}); BOOST_TEST_EQ(res_1.imag(), T{1}); } template void test_log10() { using std::complex; using std::log10; using complex_scalar = std::complex; 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::pow; using complex_scalar = std::complex; 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) BOOST_IF_CONSTEXPR (!std::is_same::value) { 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::sqrt; using complex_scalar = std::complex; 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 BOOST_IF_CONSTEXPR (!std::is_same::value) { 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::sinh; using std::sin; using complex_scalar = std::complex; 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::cosh; using std::cos; using complex_scalar = std::complex; 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::tanh; using std::tan; using complex_scalar = std::complex; 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(); }