diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 4e35a4fb..a2d33c08 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -44,7 +44,7 @@ constexpr bool is_prime(std::intmax_t n); // Integer rep is for prime numbers; long double is for any irrational base we permit. template -concept BaseRep = std::is_same_v || std::is_same_v; +concept BaseRep = std::is_same_v || std::is_same_v, long double>; /** * @brief A basis vector in our magnitude representation, raised to some rational power. @@ -85,7 +85,7 @@ base_power(T) -> base_power; template constexpr bool operator==(base_power t, base_power u) { - return std::is_same_v && (t.base == u.base) && (t.power == u.power); + return std::is_same_v && (t.get_base() == u.get_base()) && (t.power == u.power); } template @@ -100,8 +100,8 @@ template constexpr bool is_valid_base_power(const base_power &bp) { if (bp.power == 0) { return false; } - if constexpr (std::is_same_v) { return is_prime(bp.base); } - else if constexpr (std::is_same_v) { return bp.base > 0; } + if constexpr (std::is_same_v) { return is_prime(bp.get_base()); } + else if constexpr (std::is_same_v) { return bp.get_base() > 0; } else { return false; } // Unreachable. } @@ -114,24 +114,29 @@ struct is_base_power> : std::true_type {}; template concept BasePower = detail::is_base_power::value; +template +constexpr bool strictly_increasing(Ts&&... ts); + /** * @brief A representation for positive real numbers which optimizes taking products and rational powers. * * Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, and compare * for equality using this value API. */ -template +template requires requires { - // (is_valid_base_power(BasePowers) && ... && strictly_increasing(BasePowers.base...)); - (detail::is_valid_base_power(BasePowers) && ...); + (detail::is_valid_base_power(BPs) && ... && strictly_increasing(BPs.get_base()...)); } struct magnitude {}; template -constexpr bool operator==(magnitude, magnitude) { return ((LeftBPs == RightBPs) && ...); } +constexpr bool operator==(magnitude, magnitude) { + if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) { return ((LeftBPs == RightBPs) && ...); } + else { return false; } +} -template -constexpr auto inverse(magnitude) { return magnitude{}; } +template +constexpr auto inverse(magnitude) { return magnitude{}; } constexpr auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; } @@ -144,20 +149,25 @@ constexpr auto operator*(magnitude m, magnitude<>) { return m; } template constexpr auto operator*(magnitude, magnitude) { // Shortcut for prepending, which makes it easier to implement some of the other cases. - if constexpr ((sizeof...(T1) == 0) && H1.base < H2.base) { return magnitude{}; } + if constexpr ((sizeof...(T1) == 0) && H1.get_base() < H2.get_base()) { return magnitude{}; } - if constexpr (H1.base == H2.base) { + if constexpr (H1.get_base() == H2.get_base()) { constexpr auto partial_product = magnitude{} * magnitude{}; - constexpr base_power new_head{H1.base, (H1.power + H2.power)}; + + // Make a new base_power with the common base of H1 and H2, whose power is their powers' sum. + constexpr auto new_head = [&](auto head) { + head.power = H1.power + H2.power; + return head; + }(H1); if constexpr (new_head.power == 0) { return partial_product; } else { return magnitude{} * partial_product; } - } else if constexpr(H1.base < H2.base){ + } else if constexpr(H1.get_base() < H2.get_base()){ return magnitude

{} * (magnitude{} * magnitude{}); - } else { // We know H2.base < H1.base + } else { // We know H2.get_base() < H1.get_base() return magnitude

{} * (magnitude{} * magnitude{}); } } @@ -177,12 +187,12 @@ constexpr auto make_ratio() { return detail::prime_factorization_v / detail:: /** * @brief A base to represent pi. */ -template - requires requires { Power != 0; } -constexpr auto pi_power() { return base_power{std::numbers::pi_v, Power}; } +struct pi_base { + static constexpr long double value = std::numbers::pi_v; +}; template -constexpr auto pi_to_the() { return magnitude()>{}; } +constexpr auto pi_to_the() { return magnitude{Power}>{}; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Implementation details below. diff --git a/test/unit_test/runtime/magnitude_test.cpp b/test/unit_test/runtime/magnitude_test.cpp index ff66d952..a4a5e74e 100644 --- a/test/unit_test/runtime/magnitude_test.cpp +++ b/test/unit_test/runtime/magnitude_test.cpp @@ -50,49 +50,46 @@ TEST_CASE("strictly_increasing") } } -// TEST_CASE("make_ratio performs prime factorization correctly") -// { -// SECTION("Performs prime factorization when denominator is 1") -// { -// CHECK(std::is_same_v()), magnitude<>>); -// CHECK(std::is_same_v()), magnitude>); -// CHECK(std::is_same_v()), magnitude>); -// CHECK(std::is_same_v()), magnitude>); -// -// CHECK(std::is_same_v< -// decltype(make_ratio<792>()), -// magnitude>); -// } -// -// SECTION("Reduces fractions to lowest terms") -// { -// CHECK(std::is_same_v()), magnitude<>>); -// CHECK(std::is_same_v< -// decltype(make_ratio<50, 80>()), magnitude>); -// } -// } +TEST_CASE("make_ratio performs prime factorization correctly") +{ + SECTION("Performs prime factorization when denominator is 1") + { + CHECK(make_ratio<1>() == magnitude<>{}); + CHECK(make_ratio<2>() == magnitude{}); + CHECK(make_ratio<3>() == magnitude{}); + CHECK(make_ratio<4>() == magnitude{}); -// TEST_CASE("Equality works for magnitudes") -// { -// SECTION("Equivalent ratios are equal") -// { -// CHECK(make_ratio<1>() == make_ratio<1>()); -// CHECK(make_ratio<3>() == make_ratio<3>()); -// CHECK(make_ratio<3, 4>() == make_ratio<9, 12>()); -// } -// -// SECTION("Different ratios are unequal") -// { -// CHECK(make_ratio<3>() != make_ratio<5>()); -// CHECK(make_ratio<3>() != make_ratio<3, 2>()); -// } -// -// SECTION("Supports constexpr") -// { -// constexpr auto eq = (make_ratio<4, 5>() == make_ratio<4, 3>()); -// CHECK(!eq); -// } -// } + CHECK(make_ratio<792>() == magnitude{}); + } + + SECTION("Reduces fractions to lowest terms") + { + CHECK(make_ratio<8, 8>() == magnitude<>{}); + CHECK(make_ratio<50, 80>() == magnitude{}); + } +} + +TEST_CASE("Equality works for magnitudes") +{ + SECTION("Equivalent ratios are equal") + { + CHECK(make_ratio<1>() == make_ratio<1>()); + CHECK(make_ratio<3>() == make_ratio<3>()); + CHECK(make_ratio<3, 4>() == make_ratio<9, 12>()); + } + + SECTION("Different ratios are unequal") + { + CHECK(make_ratio<3>() != make_ratio<5>()); + CHECK(make_ratio<3>() != make_ratio<3, 2>()); + } + + SECTION("Supports constexpr") + { + constexpr auto eq = (make_ratio<4, 5>() == make_ratio<4, 3>()); + CHECK(!eq); + } +} TEST_CASE("Multiplication works for magnitudes") { @@ -106,12 +103,12 @@ TEST_CASE("Multiplication works for magnitudes") CHECK(make_ratio<4, 5>() * make_ratio<4, 3>() == make_ratio<16, 15>()); } - //SECTION("Products handle pi correctly") - //{ - // CHECK( - // pi_to_the<1>() * make_ratio<2, 3>() * pi_to_the() == - // magnitude()>{}); - //} + SECTION("Products handle pi correctly") + { + CHECK( + pi_to_the<1>() * make_ratio<2, 3>() * pi_to_the() == + magnitude{ratio{1, 2}}>{}); + } SECTION("Supports constexpr") {