Refactor jet_tests.cc 1. Break tests for one function into its own TEST instance. 2. Use a gmock matcher which gives better logging, previously we were using a function which in turn used another function in test_utils. It made tracking down the failing test very hard. 3. Disable a hypot3 underflow and overflow test when using libc++ as the three argument in libc++ is borked. 4. The use of the matcher has made a number of comments redundant so I have removed them. This is the first step in cleaning up these tests and then expanding them. Change-Id: Ib0c827f44432e2496e50b8cda3e06c26bdf50f08
diff --git a/internal/ceres/jet_test.cc b/internal/ceres/jet_test.cc index 7946908..e613b3f 100644 --- a/internal/ceres/jet_test.cc +++ b/internal/ceres/jet_test.cc
@@ -37,18 +37,17 @@ #include "ceres/stringprintf.h" #include "ceres/test_util.h" #include "glog/logging.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" -#define VL VLOG(1) - namespace ceres { namespace internal { namespace { -const double kE = 2.71828182845904523536; +constexpr double kE = 2.71828182845904523536; -typedef Jet<double, 2> J; +using J = Jet<double, 2>; // Convenient shorthand for making a jet. J MakeJet(double a, double v0, double v1) { @@ -59,13 +58,37 @@ return z; } -// On a 32-bit optimized build, the mismatch is about 1.4e-14. double const kTolerance = 1e-13; -void ExpectJetsClose(const J& x, const J& y) { - ExpectClose(x.a, y.a, kTolerance); - ExpectClose(x.v[0], y.v[0], kTolerance); - ExpectClose(x.v[1], y.v[1], kTolerance); +bool AreAlmostEqual(double x, double y, double max_abs_relative_difference) { + if (std::isnan(x) && std::isnan(y)) { + return true; + } + + if (std::isinf(x) && std::isinf(y)) { + return (std::signbit(x) == std::signbit(y)); + } + + double absolute_difference = std::abs(x - y); + double relative_difference = + absolute_difference / std::max(std::abs(x), std::abs(y)); + + if (x == 0 || y == 0) { + // If x or y is exactly zero, then relative difference doesn't have any + // meaning. Take the absolute difference instead. + relative_difference = absolute_difference; + } + return relative_difference <= max_abs_relative_difference; +} + +MATCHER_P(IsAlmostEqualTo, y, "") { + const bool result = (AreAlmostEqual(arg.a, y.a, kTolerance) && + AreAlmostEqual(arg.v[0], y.v[0], kTolerance) && + AreAlmostEqual(arg.v[1], y.v[1], kTolerance)); + if (!result) { + *result_listener << "\nexpected - actual : " << y - arg; + } + return result; } const double kStep = 1e-8; @@ -77,8 +100,8 @@ const double exact_dx = f(MakeJet(x, 1.0, 0.0)).v[0]; const double estimated_dx = (f(J(x + kStep)).a - f(J(x - kStep)).a) / (2.0 * kStep); - VL << name << "(" << x << "), exact dx: " << exact_dx - << ", estimated dx: " << estimated_dx; + VLOG(1) << name << "(" << x << "), exact dx: " << exact_dx + << ", estimated dx: " << estimated_dx; ExpectClose(exact_dx, estimated_dx, kNumericalTolerance); } @@ -102,504 +125,182 @@ (f(J(x + kStep), J(y)).a - f(J(x - kStep), J(y)).a) / (2.0 * kStep); const double estimated_dy = (f(J(x), J(y + kStep)).a - f(J(x), J(y - kStep)).a) / (2.0 * kStep); - VL << name << "(" << x << ", " << y << "), exact dx: " << exact_dx - << ", estimated dx: " << estimated_dx; + VLOG(1) << name << "(" << x << ", " << y << "), exact dx: " << exact_dx + << ", estimated dx: " << estimated_dx; ExpectClose(exact_dx, estimated_dx, kNumericalTolerance); - VL << name << "(" << x << ", " << y << "), exact dy: " << exact_dy - << ", estimated dy: " << estimated_dy; + VLOG(1) << name << "(" << x << ", " << y << "), exact dy: " << exact_dy + << ", estimated dy: " << estimated_dy; ExpectClose(exact_dy, estimated_dy, kNumericalTolerance); } } // namespace -TEST(Jet, Jet) { - // Pick arbitrary values for x and y. - J x = MakeJet(2.3, -2.7, 1e-3); - J y = MakeJet(1.7, 0.5, 1e+2); - J z = MakeJet(1e-6, 1e-4, 1e-2); +// Pick arbitrary values for x and y. +const J x = MakeJet(2.3, -2.7, 1e-3); +const J y = MakeJet(1.7, 0.5, 1e+2); +const J z = MakeJet(1e-6, 1e-4, 1e-2); - VL << "x = " << x; - VL << "y = " << y; - VL << "z = " << z; - - { // Check that log(exp(x)) == x. - J z = exp(x); - J w = log(z); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, x); - } - - { // Check that expm1(log1p(x)) == x. - J z = expm1(x); - J w = log1p(z); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, x); - } - - { // Check that log1p(expm1(x)) == x. - J z = log1p(x); - J w = expm1(z); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, x); - } - - { // Check that (x * y) / x == y. - J z = x * y; - J w = z / x; - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, y); - } - - { // Check that sqrt(x * x) == x. - J z = x * x; - J w = sqrt(z); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, x); - } - - { // Check that sqrt(y) * sqrt(y) == y. - J z = sqrt(y); - J w = z * z; - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, y); - } +TEST(Jet, Elementary) { + EXPECT_THAT((x * y) / x, IsAlmostEqualTo(y)); + EXPECT_THAT(sqrt(x * x), IsAlmostEqualTo(x)); + EXPECT_THAT(sqrt(y) * sqrt(y), IsAlmostEqualTo(y)); NumericalTest("sqrt", sqrt<double, 2>, 0.00001); NumericalTest("sqrt", sqrt<double, 2>, 1.0); - { // Check that cos(2*x) = cos(x)^2 - sin(x)^2 - J z = cos(J(2.0) * x); - J w = cos(x) * cos(x) - sin(x) * sin(x); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, z); - } - - { // Check that sin(2*x) = 2*cos(x)*sin(x) - J z = sin(J(2.0) * x); - J w = J(2.0) * cos(x) * sin(x); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, z); - } - - { // Check that cos(x)*cos(x) + sin(x)*sin(x) = 1 - J z = cos(x) * cos(x); - J w = sin(x) * sin(x); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z + w, J(1.0)); - } - - { // Check that atan2(r*sin(t), r*cos(t)) = t. - J t = MakeJet(0.7, -0.3, +1.5); - J r = MakeJet(2.3, 0.13, -2.4); - VL << "t = " << t; - VL << "r = " << r; - - J u = atan2(r * sin(t), r * cos(t)); - VL << "u = " << u; - - ExpectJetsClose(u, t); - } - - { // Check that tan(x) = sin(x) / cos(x). - J z = tan(x); - J w = sin(x) / cos(x); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); - } - - { // Check that tan(atan(x)) = x. - J z = tan(atan(x)); - J w = x; - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); - } - - { // Check that cosh(x)*cosh(x) - sinh(x)*sinh(x) = 1 - J z = cosh(x) * cosh(x); - J w = sinh(x) * sinh(x); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z - w, J(1.0)); - } - - { // Check that tanh(x + y) = (tanh(x) + tanh(y)) / (1 + tanh(x) tanh(y)) - J z = tanh(x + y); - J w = (tanh(x) + tanh(y)) / (J(1.0) + tanh(x) * tanh(y)); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); - } - - { // Check that pow(x, 1) == x. - VL << "x = " << x; - - J u = pow(x, 1.); - VL << "u = " << u; - - ExpectJetsClose(x, u); - } - - { // Check that pow(x, 1) == x. - J y = MakeJet(1, 0.0, 0.0); - VL << "x = " << x; - VL << "y = " << y; - - J u = pow(x, y); - VL << "u = " << u; - - ExpectJetsClose(x, u); - } - - { // Check that pow(e, log(x)) == x. - J logx = log(x); - - VL << "x = " << x; - VL << "y = " << y; - - J u = pow(kE, logx); - VL << "u = " << u; - - ExpectJetsClose(x, u); - } - - { // Check that pow(e, log(x)) == x. - J logx = log(x); - J e = MakeJet(kE, 0., 0.); - VL << "x = " << x; - VL << "log(x) = " << logx; - - J u = pow(e, logx); - VL << "u = " << u; - - ExpectJetsClose(x, u); - } - - { // Check that pow(e, log(x)) == x. - J logx = log(x); - J e = MakeJet(kE, 0., 0.); - VL << "x = " << x; - VL << "logx = " << logx; - - J u = pow(e, logx); - VL << "u = " << u; - - ExpectJetsClose(x, u); - } - - { // Check that pow(x,y) = exp(y*log(x)). - J logx = log(x); - J e = MakeJet(kE, 0., 0.); - VL << "x = " << x; - VL << "logx = " << logx; - - J u = pow(e, y * logx); - J v = pow(x, y); - VL << "u = " << u; - VL << "v = " << v; - - ExpectJetsClose(v, u); - } - - { // Check that pow(0, y) == 0 for y > 1, with both arguments Jets. - // This tests special case handling inside pow(). - J a = MakeJet(0, 1, 2); - J b = MakeJet(2, 3, 4); - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - ExpectJetsClose(c, MakeJet(0, 0, 0)); - } - - { // Check that pow(0, y) == 0 for y == 1, with both arguments Jets. - // This tests special case handling inside pow(). - J a = MakeJet(0, 1, 2); - J b = MakeJet(1, 3, 4); - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - ExpectJetsClose(c, MakeJet(0, 1, 2)); - } - - { // Check that pow(0, <1) is not finite, with both arguments Jets. - for (int i = 1; i < 10; i++) { - J a = MakeJet(0, 1, 2); - J b = MakeJet(i * 0.1, 3, 4); // b = 0.1 ... 0.9 - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - EXPECT_EQ(c.a, 0.0); - EXPECT_FALSE(IsFinite(c.v[0])); - EXPECT_FALSE(IsFinite(c.v[1])); - } - for (int i = -10; i < 0; i++) { - J a = MakeJet(0, 1, 2); - J b = MakeJet(i * 0.1, 3, 4); // b = -1,-0.9 ... -0.1 - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - EXPECT_FALSE(IsFinite(c.a)); - EXPECT_FALSE(IsFinite(c.v[0])); - EXPECT_FALSE(IsFinite(c.v[1])); - } - - { - // The special case of 0^0 = 1 defined by the C standard. - J a = MakeJet(0, 1, 2); - J b = MakeJet(0, 3, 4); - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - EXPECT_EQ(c.a, 1.0); - EXPECT_FALSE(IsFinite(c.v[0])); - EXPECT_FALSE(IsFinite(c.v[1])); - } - } - - { // Check that pow(<0, b) is correct for integer b. - // This tests special case handling inside pow(). - J a = MakeJet(-1.5, 3, 4); - - // b integer: - for (int i = -10; i <= 10; i++) { - J b = MakeJet(i, 0, 5); - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - ExpectClose(c.a, pow(-1.5, i), kTolerance); - EXPECT_TRUE(IsFinite(c.v[0])); - EXPECT_FALSE(IsFinite(c.v[1])); - ExpectClose(c.v[0], i * pow(-1.5, i - 1) * 3.0, kTolerance); - } - } - - { // Check that pow(<0, b) is correct for noninteger b. - // This tests special case handling inside pow(). - J a = MakeJet(-1.5, 3, 4); - J b = MakeJet(-2.5, 0, 5); - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - EXPECT_FALSE(IsFinite(c.a)); - EXPECT_FALSE(IsFinite(c.v[0])); - EXPECT_FALSE(IsFinite(c.v[1])); - } - + EXPECT_THAT(x + 1.0, IsAlmostEqualTo(1.0 + x)); { - // Check that pow(0,y) == 0 for y == 2, with the second argument a - // Jet. This tests special case handling inside pow(). - double a = 0; - J b = MakeJet(2, 3, 4); - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - ExpectJetsClose(c, MakeJet(0, 0, 0)); - } - - { - // Check that pow(<0,y) is correct for integer y. This tests special case - // handling inside pow(). - double a = -1.5; - for (int i = -10; i <= 10; i++) { - J b = MakeJet(i, 3, 0); - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - ExpectClose(c.a, pow(-1.5, i), kTolerance); - EXPECT_FALSE(IsFinite(c.v[0])); - EXPECT_TRUE(IsFinite(c.v[1])); - ExpectClose(c.v[1], 0, kTolerance); - } - } - - { - // Check that pow(<0,y) is correct for noninteger y. This tests special - // case handling inside pow(). - double a = -1.5; - J b = MakeJet(-3.14, 3, 0); - VL << "a = " << a; - VL << "b = " << b; - - J c = pow(a, b); - VL << "a^b = " << c; - EXPECT_FALSE(IsFinite(c.a)); - EXPECT_FALSE(IsFinite(c.v[0])); - EXPECT_FALSE(IsFinite(c.v[1])); - } - - { // Check that 1 + x == x + 1. - J a = x + 1.0; - J b = 1.0 + x; J c = x; c += 1.0; - - ExpectJetsClose(a, b); - ExpectJetsClose(a, c); + EXPECT_THAT(c, IsAlmostEqualTo(1.0 + x)); } - { // Check that 1 - x == -(x - 1). - J a = 1.0 - x; - J b = -(x - 1.0); + EXPECT_THAT(-(x - 1.0), IsAlmostEqualTo(1.0 - x)); + { J c = x; c -= 1.0; - - ExpectJetsClose(a, b); - ExpectJetsClose(a, -c); + EXPECT_THAT(c, IsAlmostEqualTo(x - 1.0)); } - { // Check that (x/s)*s == (x*s)/s. - J a = x / 5.0; - J b = x * 5.0; + EXPECT_THAT((x * 5.0) / 5.0, IsAlmostEqualTo((x / 5.0) * 5.0)); + EXPECT_THAT((x * 5.0) / 5.0, IsAlmostEqualTo(x)); + EXPECT_THAT((x / 5.0) * 5.0, IsAlmostEqualTo(x)); + + { J c = x; c /= 5.0; J d = x; d *= 5.0; - - ExpectJetsClose(5.0 * a, b / 5.0); - ExpectJetsClose(a, c); - ExpectJetsClose(b, d); + EXPECT_THAT(c, IsAlmostEqualTo(x / 5.0)); + EXPECT_THAT(d, IsAlmostEqualTo(5.0 * x)); } - { // Check that x / y == 1 / (y / x). - J a = x / y; - J b = 1.0 / (y / x); - VL << "a = " << a; - VL << "b = " << b; + EXPECT_THAT(1.0 / (y / x), IsAlmostEqualTo(x / y)); +} - ExpectJetsClose(a, b); +TEST(Jet, Trigonometric) { + EXPECT_THAT(cos(2.0 * x), IsAlmostEqualTo(cos(x) * cos(x) - sin(x) * sin(x))); + EXPECT_THAT(sin(2.0 * x), IsAlmostEqualTo(2.0 * sin(x) * cos(x))); + EXPECT_THAT(sin(x) * sin(x) + cos(x) * cos(x), IsAlmostEqualTo(J(1.0))); + + { + J t = MakeJet(0.7, -0.3, +1.5); + J r = MakeJet(2.3, 0.13, -2.4); + EXPECT_THAT(atan2(r * sin(t), r * cos(t)), IsAlmostEqualTo(t)); } - { // Check that abs(-x * x) == x * x. - ExpectJetsClose(abs(-x * x), x * x); - // Check that abs(-x) == sqrt(x * x). - ExpectJetsClose(abs(-x), sqrt(x * x)); + EXPECT_THAT(sin(x) / cos(x), IsAlmostEqualTo(tan(x))); + EXPECT_THAT(tan(atan(x)), IsAlmostEqualTo(x)); + + { + J a = MakeJet(0.1, -2.7, 1e-3); + EXPECT_THAT(cos(acos(a)), IsAlmostEqualTo(a)); + EXPECT_THAT(acos(cos(a)), IsAlmostEqualTo(a)); + + J b = MakeJet(0.6, 0.5, 1e+2); + EXPECT_THAT(cos(acos(b)), IsAlmostEqualTo(b)); + EXPECT_THAT(acos(cos(b)), IsAlmostEqualTo(b)); } + + { + J a = MakeJet(0.1, -2.7, 1e-3); + EXPECT_THAT(sin(asin(a)), IsAlmostEqualTo(a)); + EXPECT_THAT(asin(sin(a)), IsAlmostEqualTo(a)); + + J b = MakeJet(0.4, 0.5, 1e+2); + EXPECT_THAT(sin(asin(b)), IsAlmostEqualTo(b)); + EXPECT_THAT(asin(sin(b)), IsAlmostEqualTo(b)); + } +} + +TEST(Jet, Hyperbolic) { + // cosh(x)*cosh(x) - sinh(x)*sinh(x) = 1 + EXPECT_THAT(cosh(x) * cosh(x) - sinh(x) * sinh(x), IsAlmostEqualTo(J(1.0))); + + // tanh(x + y) = (tanh(x) + tanh(y)) / (1 + tanh(x) tanh(y)) + EXPECT_THAT( + tanh(x + y), + IsAlmostEqualTo((tanh(x) + tanh(y)) / (J(1.0) + tanh(x) * tanh(y)))); +} + +TEST(Jet, Abs) { + EXPECT_THAT(abs(-x * x), IsAlmostEqualTo(x * x)); + EXPECT_THAT(abs(-x), IsAlmostEqualTo(sqrt(x * x))); + { J a = MakeJet(-std::numeric_limits<double>::quiet_NaN(), 2.0, 4.0); J b = abs(a); EXPECT_TRUE(std::signbit(b.v[0])); EXPECT_TRUE(std::signbit(b.v[1])); } +} - { // Check that cos(acos(x)) == x. - J a = MakeJet(0.1, -2.7, 1e-3); - ExpectJetsClose(cos(acos(a)), a); - ExpectJetsClose(acos(cos(a)), a); +TEST(Jet, Bessel) { + J zero = J(0.0); - J b = MakeJet(0.6, 0.5, 1e+2); - ExpectJetsClose(cos(acos(b)), b); - ExpectJetsClose(acos(cos(b)), b); - } + EXPECT_THAT(BesselJ0(zero), IsAlmostEqualTo(J(1.0))); + EXPECT_THAT(BesselJ1(zero), IsAlmostEqualTo(zero)); + EXPECT_THAT(BesselJn(2, zero), IsAlmostEqualTo(zero)); + EXPECT_THAT(BesselJn(3, zero), IsAlmostEqualTo(zero)); - { // Check that sin(asin(x)) == x. - J a = MakeJet(0.1, -2.7, 1e-3); - ExpectJetsClose(sin(asin(a)), a); - ExpectJetsClose(asin(sin(a)), a); + J z = MakeJet(0.1, -2.7, 1e-3); - J b = MakeJet(0.4, 0.5, 1e+2); - ExpectJetsClose(sin(asin(b)), b); - ExpectJetsClose(asin(sin(b)), b); - } + EXPECT_THAT(BesselJ0(z), IsAlmostEqualTo(BesselJn(0, z))); + EXPECT_THAT(BesselJ1(z), IsAlmostEqualTo(BesselJn(1, z))); - { - J zero = J(0.0); + // See formula http://dlmf.nist.gov/10.6.E1 + EXPECT_THAT(BesselJ0(z) + BesselJn(2, z), + IsAlmostEqualTo((2.0 / z) * BesselJ1(z))); +} - // Check that J0(0) == 1. - ExpectJetsClose(BesselJ0(zero), J(1.0)); - - // Check that J1(0) == 0. - ExpectJetsClose(BesselJ1(zero), zero); - - // Check that J2(0) == 0. - ExpectJetsClose(BesselJn(2, zero), zero); - - // Check that J3(0) == 0. - ExpectJetsClose(BesselJn(3, zero), zero); - - J z = MakeJet(0.1, -2.7, 1e-3); - - // Check that J0(z) == Jn(0,z). - ExpectJetsClose(BesselJ0(z), BesselJn(0, z)); - - // Check that J1(z) == Jn(1,z). - ExpectJetsClose(BesselJ1(z), BesselJn(1, z)); - - // Check that J0(z)+J2(z) == (2/z)*J1(z). - // See formula http://dlmf.nist.gov/10.6.E1 - ExpectJetsClose(BesselJ0(z) + BesselJn(2, z), (2.0 / z) * BesselJ1(z)); - } - - { // Check that floor of a positive number works. +TEST(Jet, Floor) { + { // floor of a positive number works. J a = MakeJet(0.1, -2.7, 1e-3); J b = floor(a); J expected = MakeJet(floor(a.a), 0.0, 0.0); EXPECT_EQ(expected, b); } - { // Check that floor of a negative number works. + { // floor of a negative number works. J a = MakeJet(-1.1, -2.7, 1e-3); J b = floor(a); J expected = MakeJet(floor(a.a), 0.0, 0.0); EXPECT_EQ(expected, b); } - { // Check that floor of a positive number works. + { // floor of a positive number works. J a = MakeJet(10.123, -2.7, 1e-3); J b = floor(a); J expected = MakeJet(floor(a.a), 0.0, 0.0); EXPECT_EQ(expected, b); } +} - { // Check that ceil of a positive number works. +TEST(Jet, Ceil) { + { // ceil of a positive number works. J a = MakeJet(0.1, -2.7, 1e-3); J b = ceil(a); J expected = MakeJet(ceil(a.a), 0.0, 0.0); EXPECT_EQ(expected, b); } - { // Check that ceil of a negative number works. + { // ceil of a negative number works. J a = MakeJet(-1.1, -2.7, 1e-3); J b = ceil(a); J expected = MakeJet(ceil(a.a), 0.0, 0.0); EXPECT_EQ(expected, b); } - { // Check that ceil of a positive number works. + { // ceil of a positive number works. J a = MakeJet(10.123, -2.7, 1e-3); J b = ceil(a); J expected = MakeJet(ceil(a.a), 0.0, 0.0); EXPECT_EQ(expected, b); } +} - { // Check that erf works. +TEST(Jet, Erf) { + { // erf works. J a = MakeJet(10.123, -2.7, 1e-3); J b = erf(a); J expected = MakeJet(erf(a.a), 0.0, 0.0); @@ -609,8 +310,10 @@ NumericalTest("erf", erf<double, 2>, 1e-5); NumericalTest("erf", erf<double, 2>, 0.5); NumericalTest("erf", erf<double, 2>, 100.0); +} - { // Check that erfc works. +TEST(Jet, Erfc) { + { // erfc works. J a = MakeJet(10.123, -2.7, 1e-3); J b = erfc(a); J expected = MakeJet(erfc(a.a), 0.0, 0.0); @@ -620,82 +323,48 @@ NumericalTest("erfc", erfc<double, 2>, 1e-5); NumericalTest("erfc", erfc<double, 2>, 0.5); NumericalTest("erfc", erfc<double, 2>, 100.0); +} - { // Check that cbrt(x * x * x) == x. - J z = x * x * x; - J w = cbrt(z); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, x); - } +TEST(Jet, Cbrt) { + EXPECT_THAT(cbrt(x * x * x), IsAlmostEqualTo(x)); + EXPECT_THAT(cbrt(y) * cbrt(y) * cbrt(y), IsAlmostEqualTo(y)); + EXPECT_THAT(cbrt(x), IsAlmostEqualTo(pow(x, 1.0 / 3.0))); - { // Check that cbrt(y) * cbrt(y) * cbrt(y) == y. - J z = cbrt(y); - J w = z * z * z; - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(w, y); - } - - { // Check that cbrt(x) == pow(x, 1/3). - J z = cbrt(x); - J w = pow(x, 1.0 / 3.0); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); - } NumericalTest("cbrt", cbrt<double, 2>, -1.0); NumericalTest("cbrt", cbrt<double, 2>, -1e-5); NumericalTest("cbrt", cbrt<double, 2>, 1e-5); NumericalTest("cbrt", cbrt<double, 2>, 1.0); +} - { // Check that log1p(x) == log(1 + x) - J z = log1p(x); - J w = log(J{1} + x); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); - } +TEST(Jet, Log1p) { + EXPECT_THAT(log1p(expm1(x)), IsAlmostEqualTo(x)); + EXPECT_THAT(log1p(x), IsAlmostEqualTo(log(J{1} + x))); - { // Check that log1p(x) does not loose precision for small x + { // log1p(x) does not loose precision for small x J x = MakeJet(1e-16, 1e-8, 1e-4); - J z = log1p(x); - J w = MakeJet(9.9999999999999998e-17, 1e-8, 1e-4); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); + EXPECT_THAT(log1p(x), + IsAlmostEqualTo(MakeJet(9.9999999999999998e-17, 1e-8, 1e-4))); // log(1 + x) collapes to 0 J v = log(J{1} + x); EXPECT_TRUE(v.a == 0); } +} - { // Check that expm1(x) == exp(x) - 1 - J z = expm1(x); - J w = exp(x) - J{1}; - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); - } +TEST(Jet, Expm1) { + EXPECT_THAT(expm1(log1p(x)), IsAlmostEqualTo(x)); + EXPECT_THAT(expm1(x), IsAlmostEqualTo(exp(x) - 1.0)); - { // Check that expm1(x) does not loose precision for small x + { // expm1(x) does not loose precision for small x J x = MakeJet(9.9999999999999998e-17, 1e-8, 1e-4); - J z = expm1(x); - J w = MakeJet(1e-16, 1e-8, 1e-4); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); + EXPECT_THAT(expm1(x), IsAlmostEqualTo(MakeJet(1e-16, 1e-8, 1e-4))); // exp(x) - 1 collapes to 0 J v = exp(x) - J{1}; EXPECT_TRUE(v.a == 0); } +} - { // Check that exp2(x) == exp(x * log(2)) - J z = exp2(x); - J w = exp(x * log(2.0)); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); - } +TEST(Jet, Exp2) { + EXPECT_THAT(exp2(x), IsAlmostEqualTo(exp(x * log(2.0)))); NumericalTest("exp2", exp2<double, 2>, -1.0); NumericalTest("exp2", exp2<double, 2>, -1e-5); NumericalTest("exp2", exp2<double, 2>, -1e-200); @@ -703,97 +372,152 @@ NumericalTest("exp2", exp2<double, 2>, 1e-200); NumericalTest("exp2", exp2<double, 2>, 1e-5); NumericalTest("exp2", exp2<double, 2>, 1.0); +} - { // Check that log10(x) == log(x) / log(10) - J z = log10(x); - J w = log(x) / log(10.0); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); - } +TEST(Jet, Log) { EXPECT_THAT(log(exp(x)), IsAlmostEqualTo(x)); } + +TEST(Jet, Log10) { + EXPECT_THAT(log10(x), IsAlmostEqualTo(log(x) / log(10))); NumericalTest("log10", log10<double, 2>, 1e-5); NumericalTest("log10", log10<double, 2>, 1.0); NumericalTest("log10", log10<double, 2>, 98.76); +} - { // Check that log2(x) == log(x) / log(2) - J z = log2(x); - J w = log(x) / log(2.0); - VL << "z = " << z; - VL << "w = " << w; - ExpectJetsClose(z, w); - } +TEST(Jet, Log2) { + EXPECT_THAT(log2(x), IsAlmostEqualTo(log(x) / log(2))); NumericalTest("log2", log2<double, 2>, 1e-5); NumericalTest("log2", log2<double, 2>, 1.0); NumericalTest("log2", log2<double, 2>, 100.0); +} - { // Check that norm(x) == x^2 - J v = norm(x); - J w = x * x; - VL << "v = " << v; - VL << "w = " << w; - ExpectJetsClose(v, w); +TEST(Jet, Norm) { + EXPECT_THAT(norm(x), IsAlmostEqualTo(x * x)); + EXPECT_THAT(norm(-x), IsAlmostEqualTo(x * x)); +} + +TEST(Jet, Pow) { + EXPECT_THAT(pow(x, 1.0), IsAlmostEqualTo(x)); + EXPECT_THAT(pow(x, MakeJet(1.0, 0.0, 0.0)), IsAlmostEqualTo(x)); + EXPECT_THAT(pow(kE, log(x)), IsAlmostEqualTo(x)); + EXPECT_THAT(pow(MakeJet(kE, 0., 0.), log(x)), IsAlmostEqualTo(x)); + EXPECT_THAT(pow(x, y), + IsAlmostEqualTo(pow(MakeJet(kE, 0.0, 0.0), y * log(x)))); + + // Specially cases + + // pow(0, y) == 0 for y > 1, with both arguments Jets. + EXPECT_THAT(pow(MakeJet(0, 1, 2), MakeJet(2, 3, 4)), + IsAlmostEqualTo(MakeJet(0, 0, 0))); + + // pow(0, y) == 0 for y == 1, with both arguments Jets. + EXPECT_THAT(pow(MakeJet(0, 1, 2), MakeJet(1, 3, 4)), + IsAlmostEqualTo(MakeJet(0, 1, 2))); + + // pow(0, <1) is not finite, with both arguments Jets. + { + for (int i = 1; i < 10; i++) { + J a = MakeJet(0, 1, 2); + J b = MakeJet(i * 0.1, 3, 4); // b = 0.1 ... 0.9 + J c = pow(a, b); + EXPECT_EQ(c.a, 0.0) << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[0])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[1])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + } + + for (int i = -10; i < 0; i++) { + J a = MakeJet(0, 1, 2); + J b = MakeJet(i * 0.1, 3, 4); // b = -1,-0.9 ... -0.1 + J c = pow(a, b); + EXPECT_FALSE(IsFinite(c.a)) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[0])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[1])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + } + + // The special case of 0^0 = 1 defined by the C standard. + { + J a = MakeJet(0, 1, 2); + J b = MakeJet(0, 3, 4); + J c = pow(a, b); + EXPECT_EQ(c.a, 1.0) << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[0])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[1])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + } } - { // Check that norm(-x) == x^2 - J v = norm(-x); - J w = x * x; - VL << "v = " << v; - VL << "w = " << w; - ExpectJetsClose(v, w); + // pow(<0, b) is correct for integer b. + { + J a = MakeJet(-1.5, 3, 4); + + // b integer: + for (int i = -10; i <= 10; i++) { + J b = MakeJet(i, 0, 5); + J c = pow(a, b); + + EXPECT_TRUE(AreAlmostEqual(c.a, pow(-1.5, i), kTolerance)) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_TRUE(IsFinite(c.v[0])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[1])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_TRUE( + AreAlmostEqual(c.v[0], i * pow(-1.5, i - 1) * 3.0, kTolerance)) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + } } - { // Check that hypot(x, y) == sqrt(x^2 + y^2) - J h = hypot(x, y); - J s = sqrt(x * x + y * y); - VL << "h = " << h; - VL << "s = " << s; - ExpectJetsClose(h, s); + // pow(<0, b) is correct for noninteger b. + { + J a = MakeJet(-1.5, 3, 4); + J b = MakeJet(-2.5, 0, 5); + J c = pow(a, b); + EXPECT_FALSE(IsFinite(c.a)) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[0])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[1])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; } - { // Check that hypot(x, x) == sqrt(2) * abs(x) - J h = hypot(x, x); - J s = sqrt(2.0) * abs(x); - VL << "h = " << h; - VL << "s = " << s; - ExpectJetsClose(h, s); + // pow(0,y) == 0 for y == 2, with the second argument a Jet. + EXPECT_THAT(pow(0.0, MakeJet(2, 3, 4)), IsAlmostEqualTo(MakeJet(0, 0, 0))); + + // pow(<0,y) is correct for integer y. + { + double a = -1.5; + for (int i = -10; i <= 10; i++) { + J b = MakeJet(i, 3, 0); + J c = pow(a, b); + ExpectClose(c.a, pow(-1.5, i), kTolerance); + EXPECT_FALSE(IsFinite(c.v[0])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_TRUE(IsFinite(c.v[1])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + ExpectClose(c.v[1], 0, kTolerance); + } } - { // Check that the derivative is zero tangentially to the circle: - J h = hypot(MakeJet(2.0, 1.0, 1.0), MakeJet(2.0, 1.0, -1.0)); - VL << "h = " << h; - ExpectJetsClose(h, MakeJet(sqrt(8.0), std::sqrt(2.0), 0.0)); + // pow(<0,y) is correct for noninteger y. + { + double a = -1.5; + J b = MakeJet(-3.14, 3, 0); + J c = pow(a, b); + EXPECT_FALSE(IsFinite(c.a)) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[0])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; + EXPECT_FALSE(IsFinite(c.v[1])) + << "\na: " << a << "\nb: " << b << "\na^b: " << c; } +} - { // Check that hypot(x, 0) == x - J zero = MakeJet(0.0, 2.0, 3.14); - J h = hypot(x, zero); - VL << "h = " << h; - ExpectJetsClose(x, h); - } - - { // Check that hypot(0, y) == y - J zero = MakeJet(0.0, 2.0, 3.14); - J h = hypot(zero, y); - VL << "h = " << h; - ExpectJetsClose(y, h); - } - - { // Check that hypot(x, 0) == sqrt(x * x) == x, even when x * x underflows: - EXPECT_EQ(DBL_MIN * DBL_MIN, 0.0); // Make sure it underflows - J huge = MakeJet(DBL_MIN, 2.0, 3.14); - J h = hypot(huge, J(0.0)); - VL << "h = " << h; - ExpectJetsClose(h, huge); - } - - { // Check that hypot(x, 0) == sqrt(x * x) == x, even when x * x overflows: - EXPECT_EQ(DBL_MAX * DBL_MAX, std::numeric_limits<double>::infinity()); - J huge = MakeJet(DBL_MAX, 2.0, 3.14); - J h = hypot(huge, J(0.0)); - VL << "h = " << h; - ExpectJetsClose(h, huge); - } - +TEST(Jet, Hypot2) { // Resolve the ambiguity between two and three argument hypot overloads using Hypot2 = J(const J&, const J&); Hypot2* const hypot2 = static_cast<Hypot2*>(&hypot<double, 2>); @@ -810,372 +534,238 @@ NumericalTest2("hypot2", hypot2, 1.0, 2.0); // clang-format on + J zero = MakeJet(0.0, 2.0, 3.14); + EXPECT_THAT(hypot(x, y), IsAlmostEqualTo(sqrt(x * x + y * y))); + EXPECT_THAT(hypot(x, x), IsAlmostEqualTo(sqrt(2.0) * abs(x))); + + // The derivative is zero tangentially to the circle: + EXPECT_THAT(hypot(MakeJet(2.0, 1.0, 1.0), MakeJet(2.0, 1.0, -1.0)), + IsAlmostEqualTo(MakeJet(sqrt(8.0), std::sqrt(2.0), 0.0))); + + EXPECT_THAT(hypot(zero, x), IsAlmostEqualTo(x)); + EXPECT_THAT(hypot(y, zero), IsAlmostEqualTo(y)); + + // hypot(x, 0, 0) == x, even when x * x underflows: + EXPECT_EQ( + std::numeric_limits<double>::min() * std::numeric_limits<double>::min(), + 0.0); // Make sure it underflows + J tiny = MakeJet(std::numeric_limits<double>::min(), 2.0, 3.14); + EXPECT_THAT(hypot(tiny, J{0}), IsAlmostEqualTo(tiny)); + + // hypot(x, 0, 0) == x, even when x * x overflows: + EXPECT_EQ( + std::numeric_limits<double>::max() * std::numeric_limits<double>::max(), + std::numeric_limits<double>::infinity()); + J huge = MakeJet(std::numeric_limits<double>::max(), 2.0, 3.14); + EXPECT_THAT(hypot(huge, J{0}), IsAlmostEqualTo(huge)); +} + #ifdef CERES_HAS_CPP17 - { // Check that hypot(x, y) == sqrt(x^2 + y^2) - J h = hypot(x, y, z); - J s = sqrt(x * x + y * y + z * z); - VL << "h = " << h; - VL << "s = " << s; - ExpectJetsClose(h, s); - } +TEST(Jet, Hypot3) { + J zero = MakeJet(0.0, 2.0, 3.14); - { // Check that hypot(x, x) == sqrt(3) * abs(x) - J h = hypot(x, x, x); - J s = sqrt(3.0) * abs(x); - VL << "h = " << h; - VL << "s = " << s; - ExpectJetsClose(h, s); - } + // hypot(x, y, z) == sqrt(x^2 + y^2 + z^2) + EXPECT_THAT(hypot(x, y, z), IsAlmostEqualTo(sqrt(x * x + y * y + z * z))); - { // Check that the derivative is zero tangentially to the circle: - J h = hypot(MakeJet(2.0, 1.0, 1.0), - MakeJet(2.0, 1.0, -1.0), - MakeJet(2.0, -1.0, 0.0)); - VL << "h = " << h; - ExpectJetsClose(h, MakeJet(sqrt(12.0), 1.0 / std::sqrt(3.0), 0.0)); - } + // hypot(x, x) == sqrt(3) * abs(x) + EXPECT_THAT(hypot(x, x, x), IsAlmostEqualTo(sqrt(3.0) * abs(x))); - { // Check that hypot(x, 0, 0) == x - J zero = MakeJet(0.0, 2.0, 3.14); - J h = hypot(x, zero, zero); - VL << "h = " << h; - ExpectJetsClose(x, h); - } + // The derivative is zero tangentially to the circle: + EXPECT_THAT(hypot(MakeJet(2.0, 1.0, 1.0), + MakeJet(2.0, 1.0, -1.0), + MakeJet(2.0, -1.0, 0.0)), + IsAlmostEqualTo(MakeJet(sqrt(12.0), 1.0 / std::sqrt(3.0), 0.0))); - { // Check that hypot(0, y, 0) == y - J zero = MakeJet(0.0, 2.0, 3.14); - J h = hypot(zero, y, zero); - VL << "h = " << h; - ExpectJetsClose(y, h); - } + EXPECT_THAT(hypot(x, zero, zero), IsAlmostEqualTo(x)); + EXPECT_THAT(hypot(zero, y, zero), IsAlmostEqualTo(y)); + EXPECT_THAT(hypot(zero, zero, z), IsAlmostEqualTo(z)); + EXPECT_THAT(hypot(x, y, z), IsAlmostEqualTo(hypot(hypot(x, y), z))); + EXPECT_THAT(hypot(x, y, z), IsAlmostEqualTo(hypot(x, hypot(y, z)))); - { // Check that hypot(0, 0, z) == z - J zero = MakeJet(0.0, 2.0, 3.14); - J h = hypot(zero, zero, z); - VL << "h = " << h; - ExpectJetsClose(z, h); - } + // The following two tests are disabled because the three argument hypot is + // broken in the libc++ shipped with CLANG as of January 2022. - { // Check that hypot(x, y, z) == hypot(hypot(x, y), z) - J v = hypot(x, y, z); - J w = hypot(hypot(x, y), z); - VL << "v = " << v; - VL << "w = " << w; - ExpectJetsClose(v, w); - } +#if !defined(_LIBCPP_VERSION) + // hypot(x, 0, 0) == x, even when x * x underflows: + EXPECT_EQ( + std::numeric_limits<double>::min() * std::numeric_limits<double>::min(), + 0.0); // Make sure it underflows + J tiny = MakeJet(std::numeric_limits<double>::min(), 2.0, 3.14); + EXPECT_THAT(hypot(tiny, J{0}, J{0}), IsAlmostEqualTo(tiny)); - { // Check that hypot(x, y, z) == hypot(x, hypot(y, z)) - J v = hypot(x, y, z); - J w = hypot(x, hypot(y, z)); - VL << "v = " << v; - VL << "w = " << w; - ExpectJetsClose(v, w); - } + // hypot(x, 0, 0) == x, even when x * x overflows: + EXPECT_EQ( + std::numeric_limits<double>::max() * std::numeric_limits<double>::max(), + std::numeric_limits<double>::infinity()); + J huge = MakeJet(std::numeric_limits<double>::max(), 2.0, 3.14); + EXPECT_THAT(hypot(huge, J{0}, J{0}), IsAlmostEqualTo(huge)); +#endif +} - { // Check that hypot(x, 0, 0) == sqrt(x * x) == x, even when x * x - // underflows: - EXPECT_EQ( - std::numeric_limits<double>::min() * std::numeric_limits<double>::min(), - 0.0); // Make sure it underflows - J huge = MakeJet(std::numeric_limits<double>::min(), 2.0, 3.14); - J h = hypot(huge, J(0.0), J(0.0)); - VL << "h = " << h; - ExpectJetsClose(h, huge); - } - - { // Check that hypot(x, 0, 0) == sqrt(x * x) == x, even when x * x - // overflows: - EXPECT_EQ( - std::numeric_limits<double>::max() * std::numeric_limits<double>::max(), - std::numeric_limits<double>::infinity()); - J huge = MakeJet(std::numeric_limits<double>::max(), 2.0, 3.14); - J h = hypot(huge, J(0.0), J(0.0)); - VL << "h = " << h; - ExpectJetsClose(h, huge); - } #endif // defined(CERES_HAS_CPP17) #ifdef CERES_HAS_CPP20 - { // Check lerp(x, y, 0) == x - J z = lerp(x, y, J{0}); - VL << "z = " << z; - ExpectJetsClose(z, x); - } - { // Check lerp(x, y, 1) == y - J z = lerp(x, y, J{1}); - VL << "z = " << z; - ExpectJetsClose(z, y); - } +TEST(Jet, Lerp) { + EXPECT_THAT(lerp(x, y, J{0}), IsAlmostEqualTo(x)); + EXPECT_THAT(lerp(x, y, J{1}), IsAlmostEqualTo(y)); + EXPECT_THAT(lerp(x, x, J{1}), IsAlmostEqualTo(x)); + EXPECT_THAT(lerp(y, y, J{0}), IsAlmostEqualTo(y)); + EXPECT_THAT(lerp(x, y, J{0.5}), IsAlmostEqualTo((x + y) / J{2.0})); + EXPECT_THAT(lerp(x, y, J{2}), IsAlmostEqualTo(J{2.0} * y - x)); + EXPECT_THAT(lerp(x, y, J{-2}), IsAlmostEqualTo(J{3.0} * x - J{2} * x)); +} - { // Check lerp(x, x, 1) == x - J z = lerp(x, x, J{1}); - VL << "z = " << z; - ExpectJetsClose(z, x); - } +TEST(Jet, Midpoint) { + EXPECT_THAT(midpoint(x, y), IsAlmostEqualTo((x + y) / J{2})); + EXPECT_THAT(midpoint(x, x), IsAlmostEqualTo(x)); - { // Check lerp(y, y, 0) == y - J z = lerp(y, y, J{1}); - VL << "z = " << z; - ExpectJetsClose(z, y); - } - - { // Check lerp(x, y, 0.5) == (x + y) / 2 - J z = lerp(x, y, J{0.5}); - J v = (x + y) / J{2}; - VL << "z = " << z; - VL << "v = " << v; - ExpectJetsClose(z, v); - } - - { // Check lerp(x, y, 2) == 2y - x - J z = lerp(x, y, J{2}); - J v = J{2} * y - x; - VL << "z = " << z; - VL << "v = " << v; - ExpectJetsClose(z, v); - } - - { // Check lerp(x, y, -2) == 3x - 2y - J z = lerp(x, y, -J{2}); - J v = J{3} * x - J{2} * y; - VL << "z = " << z; - VL << "v = " << v; - ExpectJetsClose(z, v); - } - - { // Check that midpoint(x, y) = (x + y) / 2 - J z = midpoint(x, y); - J v = (x + y) / J{2}; - VL << "z = " << z; - VL << "v = " << v; - ExpectJetsClose(z, v); - } - - { // Check that midpoint(x, x) = x - J z = midpoint(x, x); - VL << "z = " << z; - ExpectJetsClose(z, x); - } - - { // Check that midpoint(x, y) = (x + y) / 2 while avoiding overflow + { + // midpoint(x, y) = (x + y) / 2 while avoiding overflow J x = MakeJet(std::numeric_limits<double>::min(), 1, 2); J y = MakeJet(std::numeric_limits<double>::max(), 3, 4); - J z = midpoint(x, y); - J v = x + (y - x) / J{2}; - VL << "z = " << z; - VL << "v = " << v; - ExpectJetsClose(z, v); + EXPECT_THAT(midpoint(x, y), IsAlmostEqualTo(x + (y - x) / J{2})); } - { // Check that midpoint(x, x) = x while avoiding overflow - constexpr double a = std::numeric_limits<double>::max(); - J x = MakeJet(a, a, a); - J z = midpoint(x, x); - VL << "z = " << z; - ExpectJetsClose(z, x); + { + // midpoint(x, x) = x while avoiding overflow + J x = MakeJet(std::numeric_limits<double>::max(), + std::numeric_limits<double>::max(), + std::numeric_limits<double>::max()); + EXPECT_THAT(midpoint(x, x), IsAlmostEqualTo(x)); } - { // Check that midpoint does not overflow for very large values + { // midpoint does not overflow for very large values constexpr double a = 0.75 * std::numeric_limits<double>::max(); J x = MakeJet(a, a, -a); J y = MakeJet(a, a, a); - J z = midpoint(x, y); - VL << "z = " << z; - ExpectJetsClose(z, MakeJet(a, a, 0)); + EXPECT_THAT(midpoint(x, y), IsAlmostEqualTo(MakeJet(a, a, 0))); } +} + #endif // defined(CERES_HAS_CPP20) - { - J z = fmax(x, y); - VL << "z = " << z; - ExpectJetsClose(x, z); - } - { - J z = fmax(y, x); - VL << "z = " << z; - ExpectJetsClose(x, z); - } - { - J z = fmax(x, y.a); - VL << "z = " << z; - ExpectJetsClose(x, z); - } - { - J z = fmax(y, x.a); - VL << "z = " << z; - ExpectJetsClose(J{x.a}, z); - } - { - J z = fmax(x.a, y); - VL << "z = " << z; - ExpectJetsClose(J{x.a}, z); - } - { - J z = fmax(y.a, x); - VL << "z = " << z; - ExpectJetsClose(x, z); - } - { - J z = fmax(std::numeric_limits<double>::quiet_NaN(), x); - VL << "z = " << z; - ExpectJetsClose(x, z); - } - { - J z = fmax(x, std::numeric_limits<double>::quiet_NaN()); - VL << "z = " << z; - ExpectJetsClose(x, z); - } +TEST(Jet, Fmax) { + EXPECT_THAT(fmax(x, y), IsAlmostEqualTo(x)); + EXPECT_THAT(fmax(y, x), IsAlmostEqualTo(x)); + EXPECT_THAT(fmax(x, y.a), IsAlmostEqualTo(x)); + EXPECT_THAT(fmax(y.a, x), IsAlmostEqualTo(x)); + EXPECT_THAT(fmax(y, x.a), IsAlmostEqualTo(J{x.a})); + EXPECT_THAT(fmax(x.a, y), IsAlmostEqualTo(J{x.a})); + EXPECT_THAT(fmax(x, std::numeric_limits<double>::quiet_NaN()), + IsAlmostEqualTo(x)); + EXPECT_THAT(fmax(std::numeric_limits<double>::quiet_NaN(), x), + IsAlmostEqualTo(x)); +} - { - J z = fmin(x, y); - VL << "z = " << z; - ExpectJetsClose(y, z); - } - { - J z = fmin(y, x); - VL << "z = " << z; - ExpectJetsClose(y, z); - } - { - J z = fmin(x, y.a); - VL << "z = " << z; - ExpectJetsClose(J{y.a}, z); - } - { - J z = fmin(y, x.a); - VL << "z = " << z; - ExpectJetsClose(y, z); - } - { - J z = fmin(x.a, y); - VL << "z = " << z; - ExpectJetsClose(y, z); - } - { - J z = fmin(y.a, x); - VL << "z = " << z; - ExpectJetsClose(J{y.a}, z); - } - { - J z = fmin(x, std::numeric_limits<double>::quiet_NaN()); - VL << "z = " << z; - ExpectJetsClose(x, z); - } - { - J z = fmin(std::numeric_limits<double>::quiet_NaN(), x); - VL << "z = " << z; - ExpectJetsClose(x, z); - } +TEST(Jet, Fmin) { + EXPECT_THAT(fmin(x, y), IsAlmostEqualTo(y)); + EXPECT_THAT(fmin(y, x), IsAlmostEqualTo(y)); + EXPECT_THAT(fmin(x, y.a), IsAlmostEqualTo(J{y.a})); + EXPECT_THAT(fmin(y.a, x), IsAlmostEqualTo(J{y.a})); + EXPECT_THAT(fmin(y, x.a), IsAlmostEqualTo(y)); + EXPECT_THAT(fmin(x.a, y), IsAlmostEqualTo(y)); + EXPECT_THAT(fmin(x, std::numeric_limits<double>::quiet_NaN()), + IsAlmostEqualTo(x)); + EXPECT_THAT(fmin(std::numeric_limits<double>::quiet_NaN(), x), + IsAlmostEqualTo(x)); +} +TEST(Jet, CopySign) { { // copysign(x, +1) J z = copysign(x, J{+1}); - VL << "z = " << z; - ExpectJetsClose(x, z); - EXPECT_TRUE(IsFinite(z.v[0])); - EXPECT_TRUE(IsFinite(z.v[1])); + EXPECT_THAT(z, IsAlmostEqualTo(x)); + EXPECT_TRUE(IsFinite(z.v[0])) << z; + EXPECT_TRUE(IsFinite(z.v[1])) << z; } { // copysign(x, -1) J z = copysign(x, J{-1}); - VL << "z = " << z; - ExpectJetsClose(-x, z); - EXPECT_TRUE(IsFinite(z.v[0])); - EXPECT_TRUE(IsFinite(z.v[1])); + EXPECT_THAT(z, IsAlmostEqualTo(-x)); + EXPECT_TRUE(IsFinite(z.v[0])) << z; + EXPECT_TRUE(IsFinite(z.v[1])) << z; } { // copysign(-x, +1) J z = copysign(-x, J{+1}); - VL << "z = " << z; - ExpectJetsClose(x, z); - EXPECT_TRUE(IsFinite(z.v[0])); - EXPECT_TRUE(IsFinite(z.v[1])); + EXPECT_THAT(z, IsAlmostEqualTo(x)); + EXPECT_TRUE(IsFinite(z.v[0])) << z; + EXPECT_TRUE(IsFinite(z.v[1])) << z; } { // copysign(-x, -1) J z = copysign(-x, J{-1}); - VL << "z = " << z; - ExpectJetsClose(-x, z); - EXPECT_TRUE(IsFinite(z.v[0])); - EXPECT_TRUE(IsFinite(z.v[1])); + EXPECT_THAT(z, IsAlmostEqualTo(-x)); + EXPECT_TRUE(IsFinite(z.v[0])) << z; + EXPECT_TRUE(IsFinite(z.v[1])) << z; } { // copysign(-0, +1) J z = copysign(MakeJet(-0, 1, 2), J{+1}); - VL << "z = " << z; - ExpectJetsClose(MakeJet(+0, 1, 2), z); - EXPECT_FALSE(std::signbit(z.a)); - EXPECT_TRUE(IsFinite(z.v[0])); - EXPECT_TRUE(IsFinite(z.v[1])); + EXPECT_THAT(z, IsAlmostEqualTo(MakeJet(+0, 1, 2))); + EXPECT_FALSE(std::signbit(z.a)) << z; + EXPECT_TRUE(IsFinite(z.v[0])) << z; + EXPECT_TRUE(IsFinite(z.v[1])) << z; } { // copysign(-0, -1) J z = copysign(MakeJet(-0, 1, 2), J{-1}); - VL << "z = " << z; - ExpectJetsClose(MakeJet(-0, -1, -2), z); - EXPECT_TRUE(std::signbit(z.a)); - EXPECT_TRUE(IsFinite(z.v[0])); - EXPECT_TRUE(IsFinite(z.v[1])); + EXPECT_THAT(z, IsAlmostEqualTo(MakeJet(-0, -1, -2))); + EXPECT_TRUE(std::signbit(z.a)) << z; + EXPECT_TRUE(IsFinite(z.v[0])) << z; + EXPECT_TRUE(IsFinite(z.v[1])) << z; } { // copysign(+0, -1) J z = copysign(MakeJet(+0, 1, 2), J{-1}); - VL << "z = " << z; - ExpectJetsClose(MakeJet(-0, -1, -2), z); - EXPECT_TRUE(std::signbit(z.a)); - EXPECT_TRUE(IsFinite(z.v[0])); - EXPECT_TRUE(IsFinite(z.v[1])); + EXPECT_THAT(z, IsAlmostEqualTo(MakeJet(-0, -1, -2))); + EXPECT_TRUE(std::signbit(z.a)) << z; + EXPECT_TRUE(IsFinite(z.v[0])) << z; + EXPECT_TRUE(IsFinite(z.v[1])) << z; } { // copysign(+0, +1) J z = copysign(MakeJet(+0, 1, 2), J{+1}); - VL << "z = " << z; - ExpectJetsClose(MakeJet(+0, 1, 2), z); - EXPECT_FALSE(std::signbit(z.a)); - EXPECT_TRUE(IsFinite(z.v[0])); - EXPECT_TRUE(IsFinite(z.v[1])); + EXPECT_THAT(z, IsAlmostEqualTo(MakeJet(+0, 1, 2))); + EXPECT_FALSE(std::signbit(z.a)) << z; + EXPECT_TRUE(IsFinite(z.v[0])) << z; + EXPECT_TRUE(IsFinite(z.v[1])) << z; } { // copysign(+0, +0) J z = copysign(MakeJet(+0, 1, 2), J{+0}); - VL << "z = " << z; - EXPECT_FALSE(std::signbit(z.a)); - EXPECT_TRUE(IsNaN(z.v[0])); - EXPECT_TRUE(IsNaN(z.v[1])); + EXPECT_FALSE(std::signbit(z.a)) << z; + EXPECT_TRUE(IsNaN(z.v[0])) << z; + EXPECT_TRUE(IsNaN(z.v[1])) << z; } { // copysign(+0, -0) J z = copysign(MakeJet(+0, 1, 2), J{-0}); - VL << "z = " << z; - EXPECT_FALSE(std::signbit(z.a)); - EXPECT_TRUE(IsNaN(z.v[0])); - EXPECT_TRUE(IsNaN(z.v[1])); + EXPECT_FALSE(std::signbit(z.a)) << z; + EXPECT_TRUE(IsNaN(z.v[0])) << z; + EXPECT_TRUE(IsNaN(z.v[1])) << z; } { // copysign(-0, +0) J z = copysign(MakeJet(-0, 1, 2), J{+0}); - VL << "z = " << z; - EXPECT_FALSE(std::signbit(z.a)); - EXPECT_TRUE(IsNaN(z.v[0])); - EXPECT_TRUE(IsNaN(z.v[1])); + EXPECT_FALSE(std::signbit(z.a)) << z; + EXPECT_TRUE(IsNaN(z.v[0])) << z; + EXPECT_TRUE(IsNaN(z.v[1])) << z; } { // copysign(-0, -0) J z = copysign(MakeJet(-0, 1, 2), J{-0}); - VL << "z = " << z; - EXPECT_FALSE(std::signbit(z.a)); - EXPECT_TRUE(IsNaN(z.v[0])); - EXPECT_TRUE(IsNaN(z.v[1])); + EXPECT_FALSE(std::signbit(z.a)) << z; + EXPECT_TRUE(IsNaN(z.v[0])) << z; + EXPECT_TRUE(IsNaN(z.v[1])) << z; } { // copysign(1, -nan) J z = copysign(MakeJet(1, 2, 3), -J{std::numeric_limits<double>::quiet_NaN()}); - VL << "z = " << z; - EXPECT_TRUE(std::signbit(z.a)); - EXPECT_TRUE(std::signbit(z.v[0])); - EXPECT_TRUE(std::signbit(z.v[1])); - EXPECT_FALSE(IsNaN(z.v[0])); - EXPECT_FALSE(IsNaN(z.v[1])); + EXPECT_TRUE(std::signbit(z.a)) << z; + EXPECT_TRUE(std::signbit(z.v[0])) << z; + EXPECT_TRUE(std::signbit(z.v[1])) << z; + EXPECT_FALSE(IsNaN(z.v[0])) << z; + EXPECT_FALSE(IsNaN(z.v[1])) << z; } { // copysign(1, +nan) J z = copysign(MakeJet(1, 2, 3), +J{std::numeric_limits<double>::quiet_NaN()}); - VL << "z = " << z; - EXPECT_FALSE(std::signbit(z.a)); - EXPECT_FALSE(std::signbit(z.v[0])); - EXPECT_FALSE(std::signbit(z.v[1])); - EXPECT_FALSE(IsNaN(z.v[0])); - EXPECT_FALSE(IsNaN(z.v[1])); + EXPECT_FALSE(std::signbit(z.a)) << z; + EXPECT_FALSE(std::signbit(z.v[0])) << z; + EXPECT_FALSE(std::signbit(z.v[1])) << z; + EXPECT_FALSE(IsNaN(z.v[0])) << z; + EXPECT_FALSE(IsNaN(z.v[1])) << z; } } @@ -1191,12 +781,12 @@ M << x, y, z, w; v << x, z; - // Check that M * v == (v^T * M^T)^T + // M * v == (v^T * M^T)^T r1 = M * v; r2 = (v.transpose() * M.transpose()).transpose(); - ExpectJetsClose(r1(0), r2(0)); - ExpectJetsClose(r1(1), r2(1)); + EXPECT_THAT(r1(0), IsAlmostEqualTo(r2(0))); + EXPECT_THAT(r1(1), IsAlmostEqualTo(r2(1))); } TEST(JetTraitsTest, ClassificationMixed) { @@ -1307,7 +897,7 @@ const J sum = a.sum(); const J sum2 = a(0) + a(1); - ExpectJetsClose(sum, sum2); + EXPECT_THAT(sum, IsAlmostEqualTo(sum2)); } TEST(JetTraitsTest, MatrixScalarBinaryOps) { @@ -1322,22 +912,22 @@ M << x, y, z, w; v << 0.6, -2.1; - // Check that M * v == M * v.cast<J>(). + // M * v == M * v.cast<J>(). const Eigen::Matrix<J, 2, 1> r1 = M * v; const Eigen::Matrix<J, 2, 1> r2 = M * v.cast<J>(); - ExpectJetsClose(r1(0), r2(0)); - ExpectJetsClose(r1(1), r2(1)); + EXPECT_THAT(r1(0), IsAlmostEqualTo(r2(0))); + EXPECT_THAT(r1(1), IsAlmostEqualTo(r2(1))); - // Check that M * a == M * T(a). + // M * a == M * T(a). const double a = 3.1; const Eigen::Matrix<J, 2, 2> r3 = M * a; const Eigen::Matrix<J, 2, 2> r4 = M * J(a); - ExpectJetsClose(r3(0, 0), r4(0, 0)); - ExpectJetsClose(r3(1, 0), r4(1, 0)); - ExpectJetsClose(r3(0, 1), r4(0, 1)); - ExpectJetsClose(r3(1, 1), r4(1, 1)); + EXPECT_THAT(r3(0, 0), IsAlmostEqualTo(r4(0, 0))); + EXPECT_THAT(r3(0, 1), IsAlmostEqualTo(r4(0, 1))); + EXPECT_THAT(r3(1, 0), IsAlmostEqualTo(r4(1, 0))); + EXPECT_THAT(r3(1, 1), IsAlmostEqualTo(r4(1, 1))); } TEST(JetTraitsTest, ArrayScalarUnaryOps) { @@ -1348,7 +938,7 @@ const J sum = a.sum(); const J sum2 = a(0) + a(1); - ExpectJetsClose(sum, sum2); + EXPECT_THAT(sum, sum2); } TEST(JetTraitsTest, ArrayScalarBinaryOps) { @@ -1361,23 +951,23 @@ a << x, y; b << 0.6, -2.1; - // Check that a * b == a * b.cast<T>() + // a * b == a * b.cast<T>() const Eigen::Array<J, 2, 1> r1 = a * b; const Eigen::Array<J, 2, 1> r2 = a * b.cast<J>(); - ExpectJetsClose(r1(0), r2(0)); - ExpectJetsClose(r1(1), r2(1)); + EXPECT_THAT(r1(0), r2(0)); + EXPECT_THAT(r1(1), r2(1)); - // Check that a * c == a * T(c). + // a * c == a * T(c). const double c = 3.1; const Eigen::Array<J, 2, 1> r3 = a * c; const Eigen::Array<J, 2, 1> r4 = a * J(c); - ExpectJetsClose(r3(0), r3(0)); - ExpectJetsClose(r4(1), r4(1)); + EXPECT_THAT(r3(0), r3(0)); + EXPECT_THAT(r4(1), r4(1)); } -TEST(Jet, nested3x) { +TEST(Jet, Nested3X) { typedef Jet<J, 2> JJ; typedef Jet<JJ, 2> JJJ; @@ -1422,7 +1012,7 @@ using J0 = Jet<T, 0>; using J0d = J0<double>; -TYPED_TEST(JetTest, comparison_jet) { +TYPED_TEST(JetTest, Comparison) { using Scalar = TypeParam; EXPECT_EQ(J0<Scalar>{0}, J0<Scalar>{0}); @@ -1433,7 +1023,7 @@ EXPECT_NE(J0<Scalar>{1}, J0<Scalar>{2}); } -TYPED_TEST(JetTest, comparison_scalar) { +TYPED_TEST(JetTest, ScalarComparison) { using Scalar = TypeParam; EXPECT_EQ(J0d{0.0}, Scalar{0}); @@ -1451,7 +1041,7 @@ EXPECT_NE(Scalar{2}, J0d{1.0}); } -TYPED_TEST(JetTest, comparison_nested2x) { +TYPED_TEST(JetTest, Nested2XComparison) { using Scalar = TypeParam; EXPECT_EQ(J0<J0d>{J0d{0.0}}, Scalar{0}); @@ -1469,7 +1059,7 @@ EXPECT_NE(Scalar{2}, J0<J0d>{J0d{1.0}}); } -TYPED_TEST(JetTest, comparison_nested3x) { +TYPED_TEST(JetTest, Nested3XComparison) { using Scalar = TypeParam; EXPECT_EQ(J0<J0<J0d>>{J0<J0d>{J0d{0.0}}}, Scalar{0});