From 0566158e91579494ae7e5cbf2fcd7288f6381d87 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Wed, 29 Dec 2021 11:38:56 -0500 Subject: [PATCH] Add concept for magnitude --- src/core/include/units/magnitude.h | 61 ++++++++++++++++++----- test/unit_test/runtime/magnitude_test.cpp | 59 ++++++++++++++++++++++ 2 files changed, 107 insertions(+), 13 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 060b1642..41d03e6a 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -24,6 +24,7 @@ #include #include +#include namespace units::mag { @@ -33,6 +34,21 @@ namespace units::mag template struct magnitude; +template +struct base_power; + +template +struct is_magnitude; +template +inline constexpr bool is_magnitude_v = is_magnitude::value; +template +concept Magnitude = is_magnitude_v; + +template +struct int_base : std::integral_constant {}; +template +using int_base_power = base_power, ratio{num, den}>; + template struct inverse; template @@ -54,6 +70,17 @@ template using prime_factorization_t = typename prime_factorization::type; } // namespace detail +template +using ratio_t = quotient_t, detail::prime_factorization_t>; + +struct pi { + static inline constexpr long double value = std::numbers::pi_v; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Implementation details below. +//////////////////////////////////////////////////////////////////////////////////////////////////// + template struct magnitude { bool operator==(magnitude) const { return true; } @@ -62,23 +89,31 @@ struct magnitude { bool operator==(magnitude) const { return false; } }; -template -using ratio_t = quotient_t, detail::prime_factorization_t>; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Implementation details below. //////////////////////////////////////////////////////////////////////////////////////////////////// +// Magnitude concept implementation. -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Base powers. +template +struct is_magnitude: std::false_type {}; -template -struct base_power; +// Check whether a tuple of (possibly heterogeneously typed) values are strictly increasing. +template +constexpr bool strictly_increasing(const std::tuple &ts) { + // Carefully handle different sizes, avoiding unsigned integer underflow. + constexpr auto num_comparisons = [](auto num_elements) { + return (num_elements > 1) ? (num_elements - 1) : 0; + }(sizeof...(Ts)); -template -struct int_base : std::integral_constant {}; -template -using int_base_power = base_power, ratio{num, den}>; + // Compare zero or more pairs of neighbours as needed. + return [&ts](std::integer_sequence) { + return ((std::get(ts) < std::get(ts)) && ...); + }(std::make_index_sequence()); +} + +template +struct is_magnitude...>> + : std::bool_constant<( + strictly_increasing(std::make_tuple(bases::value...)) + && ((powers.num != 0) && ...))> {}; //////////////////////////////////////////////////////////////////////////////////////////////////// // Inverse implementation. diff --git a/test/unit_test/runtime/magnitude_test.cpp b/test/unit_test/runtime/magnitude_test.cpp index a21a4741..8553fd73 100644 --- a/test/unit_test/runtime/magnitude_test.cpp +++ b/test/unit_test/runtime/magnitude_test.cpp @@ -115,6 +115,65 @@ TEST_CASE("Magnitude supports products") } } +TEST_CASE("is_magnitude detects well formed magnitudes") +{ + SECTION ("Arbitrary other types are not magnitudes") + { + CHECK(!is_magnitude_v); + CHECK(!is_magnitude_v); + CHECK(!is_magnitude_v>); + } + + SECTION ("Null magnitude is magnitude") + { + CHECK(is_magnitude_v>); + } + + SECTION ("Single-base magnitude is magnitude") + { + CHECK(is_magnitude_v>>); + } + + SECTION ("Out-of-order bases disqualify magnitudes") + { + CHECK(!is_magnitude_v, int_base_power<2>>>); + } + + SECTION ("Repeated bases disqualify magnitudes") + { + CHECK(!is_magnitude_v, int_base_power<2, 2>>>); + } + + SECTION ("Mixed base types are magnitudes if sorted") + { + CHECK(is_magnitude_v, base_power>>); + CHECK(is_magnitude_v, base_power>>); + CHECK(!is_magnitude_v, base_power>>); + } +} + +TEST_CASE("strictly_increasing") +{ + SECTION ("Empty tuple is sorted") + { + CHECK(strictly_increasing(std::make_tuple())); + } + + SECTION ("Single-element tuple is sorted") + { + CHECK(strictly_increasing(std::make_tuple(3))); + CHECK(strictly_increasing(std::make_tuple(15.42))); + CHECK(strictly_increasing(std::make_tuple('c'))); + } + + SECTION ("Multi-element tuples compare correctly") + { + CHECK(strictly_increasing(std::make_tuple(3, 3.14))); + CHECK(!strictly_increasing(std::make_tuple(3, 3.0))); + CHECK(!strictly_increasing(std::make_tuple(4, 3.0))); + } +} + // TEST_CASE("Ratio shortcut performs prime factorization") // { // // CHECK(std::is_same_v, magnitude<>>);