|
20 | 20 | #include "gtest/gtest.h"
|
21 | 21 |
|
22 | 22 | using ::testing::DoubleEq;
|
| 23 | +using ::testing::Eq; |
| 24 | +using ::testing::FloatEq; |
23 | 25 | using ::testing::StaticAssertTypeEq;
|
24 | 26 |
|
25 | 27 | namespace au {
|
@@ -208,6 +210,11 @@ TEST(GetValue, ImpossibleRequestsArePreventedAtCompileTime) {
|
208 | 210 | // get_value<int>(sqrt_2);
|
209 | 211 | }
|
210 | 212 |
|
| 213 | +TEST(GetValue, HandlesRoots) { |
| 214 | + constexpr auto sqrt_2 = get_value<double>(root<2>(mag<2>())); |
| 215 | + EXPECT_DOUBLE_EQ(sqrt_2 * sqrt_2, 2.0); |
| 216 | +} |
| 217 | + |
211 | 218 | TEST(GetValue, WorksForEmptyPack) {
|
212 | 219 | constexpr auto one = Magnitude<>{};
|
213 | 220 | EXPECT_THAT(get_value<int>(one), SameTypeAndValue(1));
|
@@ -270,6 +277,15 @@ MATCHER(CannotFit, "") {
|
270 | 277 | return (arg.outcome == MagRepresentationOutcome::ERR_CANNOT_FIT) && (arg.value == 0);
|
271 | 278 | }
|
272 | 279 |
|
| 280 | +MATCHER(NonIntegerInIntegerType, "") { |
| 281 | + return (arg.outcome == MagRepresentationOutcome::ERR_NON_INTEGER_IN_INTEGER_TYPE) && |
| 282 | + (arg.value == 0); |
| 283 | +} |
| 284 | + |
| 285 | +MATCHER(InvalidRoot, "") { |
| 286 | + return (arg.outcome == MagRepresentationOutcome::ERR_INVALID_ROOT) && (arg.value == 0); |
| 287 | +} |
| 288 | + |
273 | 289 | template <typename T, typename ValueMatcher>
|
274 | 290 | auto FitsAndMatchesValue(ValueMatcher &&matcher) {
|
275 | 291 | return ::testing::AllOf(
|
@@ -298,6 +314,106 @@ TEST(CheckedIntPow, FindsAppropriateLimits) {
|
298 | 314 | EXPECT_THAT(checked_int_pow(10.0, 309), CannotFit());
|
299 | 315 | }
|
300 | 316 |
|
| 317 | +TEST(Root, ReturnsErrorForIntegralType) { |
| 318 | + EXPECT_THAT(root(4, 2), NonIntegerInIntegerType()); |
| 319 | + EXPECT_THAT(root(uint8_t{125}, 3), NonIntegerInIntegerType()); |
| 320 | +} |
| 321 | + |
| 322 | +TEST(Root, ReturnsErrorForZerothRoot) { |
| 323 | + EXPECT_THAT(root(4.0, 0), InvalidRoot()); |
| 324 | + EXPECT_THAT(root(125.0, 0), InvalidRoot()); |
| 325 | +} |
| 326 | + |
| 327 | +TEST(Root, NegativeRootsWorkForOddPowersOnly) { |
| 328 | + EXPECT_THAT(root(-4.0, 2), InvalidRoot()); |
| 329 | + EXPECT_THAT(root(-125.0, 3), FitsAndProducesValue(-5.0)); |
| 330 | + EXPECT_THAT(root(-10000.0, 4), InvalidRoot()); |
| 331 | +} |
| 332 | + |
| 333 | +TEST(Root, AnyRootOfOneIsOne) { |
| 334 | + for (const std::uintmax_t r : {1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u}) { |
| 335 | + EXPECT_THAT(root(1.0, r), FitsAndProducesValue(1.0)); |
| 336 | + } |
| 337 | +} |
| 338 | + |
| 339 | +TEST(Root, AnyRootOfZeroIsZero) { |
| 340 | + for (const std::uintmax_t r : {1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u}) { |
| 341 | + EXPECT_THAT(root(0.0, r), FitsAndProducesValue(0.0)); |
| 342 | + } |
| 343 | +} |
| 344 | + |
| 345 | +TEST(Root, OddRootOfNegativeOneIsItself) { |
| 346 | + EXPECT_THAT(root(-1.0, 1), FitsAndProducesValue(-1.0)); |
| 347 | + EXPECT_THAT(root(-1.0, 2), InvalidRoot()); |
| 348 | + EXPECT_THAT(root(-1.0, 3), FitsAndProducesValue(-1.0)); |
| 349 | + EXPECT_THAT(root(-1.0, 4), InvalidRoot()); |
| 350 | + EXPECT_THAT(root(-1.0, 5), FitsAndProducesValue(-1.0)); |
| 351 | +} |
| 352 | + |
| 353 | +TEST(Root, RecoversExactValueWherePossible) { |
| 354 | + { |
| 355 | + const auto sqrt_4f = root(4.0f, 2); |
| 356 | + EXPECT_THAT(sqrt_4f.outcome, Eq(MagRepresentationOutcome::OK)); |
| 357 | + EXPECT_THAT(sqrt_4f.value, SameTypeAndValue(2.0f)); |
| 358 | + } |
| 359 | + |
| 360 | + { |
| 361 | + const auto cbrt_125L = root(125.0L, 3); |
| 362 | + EXPECT_THAT(cbrt_125L.outcome, Eq(MagRepresentationOutcome::OK)); |
| 363 | + EXPECT_THAT(cbrt_125L.value, SameTypeAndValue(5.0L)); |
| 364 | + } |
| 365 | +} |
| 366 | + |
| 367 | +TEST(Root, HandlesArgumentsBetweenOneAndZero) { |
| 368 | + EXPECT_THAT(root(0.25, 2), FitsAndProducesValue(0.5)); |
| 369 | + EXPECT_THAT(root(0.0001, 4), FitsAndMatchesValue<double>(DoubleEq(0.1))); |
| 370 | +} |
| 371 | + |
| 372 | +TEST(Root, ResultIsVeryCloseToStdPowForPureRoots) { |
| 373 | + for (const double x : {55.5, 123.456, 789.012, 3456.789, 12345.6789, 5.67e25}) { |
| 374 | + for (const auto r : {2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u}) { |
| 375 | + const auto double_result = root(x, r); |
| 376 | + EXPECT_THAT(double_result.outcome, Eq(MagRepresentationOutcome::OK)); |
| 377 | + EXPECT_THAT(double_result.value, DoubleEq(static_cast<double>(std::pow(x, 1.0L / r)))); |
| 378 | + |
| 379 | + const auto float_result = root(static_cast<float>(x), r); |
| 380 | + EXPECT_THAT(float_result.outcome, Eq(MagRepresentationOutcome::OK)); |
| 381 | + EXPECT_THAT(float_result.value, FloatEq(static_cast<float>(std::pow(x, 1.0L / r)))); |
| 382 | + } |
| 383 | + } |
| 384 | +} |
| 385 | + |
| 386 | +TEST(Root, ResultAtLeastAsGoodAsStdPowForRationalPowers) { |
| 387 | + struct RationalPower { |
| 388 | + std::uintmax_t num; |
| 389 | + std::uintmax_t den; |
| 390 | + }; |
| 391 | + |
| 392 | + auto result_via_root = [](double x, RationalPower power) { |
| 393 | + return static_cast<double>( |
| 394 | + root(checked_int_pow(static_cast<long double>(x), power.num).value, power.den).value); |
| 395 | + }; |
| 396 | + |
| 397 | + auto result_via_std_pow = [](double x, RationalPower power) { |
| 398 | + return static_cast<double>( |
| 399 | + std::pow(static_cast<long double>(x), |
| 400 | + static_cast<long double>(power.num) / static_cast<long double>(power.den))); |
| 401 | + }; |
| 402 | + |
| 403 | + auto round_trip_error = [](double x, RationalPower power, auto func) { |
| 404 | + const auto round_trip_result = func(func(x, power), {power.den, power.num}); |
| 405 | + return std::abs(round_trip_result - x); |
| 406 | + }; |
| 407 | + |
| 408 | + for (const auto base : {2.0, 3.1415, 98.6, 1.2e-10, 5.5e15}) { |
| 409 | + for (const auto power : std::vector<RationalPower>{{5, 2}, {2, 3}, {7, 4}}) { |
| 410 | + const auto error_from_root = round_trip_error(base, power, result_via_root); |
| 411 | + const auto error_from_std_pow = round_trip_error(base, power, result_via_std_pow); |
| 412 | + EXPECT_LE(error_from_root, error_from_std_pow); |
| 413 | + } |
| 414 | + } |
| 415 | +} |
| 416 | + |
301 | 417 | TEST(GetValueResult, HandlesNumbersTooBigForUintmax) {
|
302 | 418 | EXPECT_THAT(get_value_result<std::uintmax_t>(pow<64>(mag<2>())), CannotFit());
|
303 | 419 | }
|
|
0 commit comments