From e7bebac2a74eb6f1d7ac5627fc13378f67e19b64 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 23 Oct 2022 10:47:12 +0200 Subject: [PATCH] feat: `power()` support added for dimensions and units --- .../include/units/bits/expression_template.h | 42 ++++++++++++++++--- src/core/include/units/dimension.h | 22 ++++++++++ src/core/include/units/unit.h | 27 ++++++++++++ test/unit_test/static/dimension_test.cpp | 15 +++++++ test/unit_test/static/unit_test.cpp | 24 +++++++++++ 5 files changed, 124 insertions(+), 6 deletions(-) diff --git a/src/core/include/units/bits/expression_template.h b/src/core/include/units/bits/expression_template.h index 07dac42c..b2bdee1e 100644 --- a/src/core/include/units/bits/expression_template.h +++ b/src/core/include/units/bits/expression_template.h @@ -112,13 +112,17 @@ inline constexpr bool is_specialization_of_power> = true; template consteval auto power_or_T_impl() { - if constexpr (R.den == 1) { - if constexpr (R.num == 1) - return T{}; - else - return power{}; + if constexpr (is_specialization_of_power) { + return power_or_T_impl(); } else { - return power{}; + if constexpr (R.den == 1) { + if constexpr (R.num == 1) + return T{}; + else + return power{}; + } else { + return power{}; + } } }; @@ -437,6 +441,32 @@ template typename To, typename OneType, typename T> return To>{}; } + +template typename To, typename OneType, + template typename Pred, typename... Nums, typename... Dens> + requires detail::non_zero +[[nodiscard]] consteval auto expr_pow_impl(type_list, type_list) +{ + return detail::get_optimized_expression...>, + type_list...>, OneType, Pred, To>(); +} + + +/** + * @brief Computes the value of an expression raised to the `Num/Den` power + * + * @tparam Num Exponent numerator + * @tparam Den Exponent denominator + * @tparam T Expression being the base of the operation + */ +template typename To, typename OneType, + template typename Pred, typename T> + requires detail::non_zero +[[nodiscard]] consteval auto expr_pow(T) +{ + return expr_pow_impl(typename T::_num_{}, typename T::_den_{}); +} + } // namespace detail } // namespace units diff --git a/src/core/include/units/dimension.h b/src/core/include/units/dimension.h index 304e5910..742148fb 100644 --- a/src/core/include/units/dimension.h +++ b/src/core/include/units/dimension.h @@ -285,6 +285,28 @@ template return std::derived_from || std::derived_from; } +/** + * @brief Computes the value of a dimension raised to the `Num/Den` power + * + * @tparam Num Exponent numerator + * @tparam Den Exponent denominator + * @param d Dimension being the base of the operation + * + * @return Dimension The result of computation + */ +template + requires detail::non_zero +[[nodiscard]] consteval Dimension auto pow(D d) +{ + if constexpr (BaseDimension) { + if constexpr (Den == 1) + return derived_dimension>{}; + else + return derived_dimension>{}; + } else + return detail::expr_pow(d); +} + // TODO consider adding the support for text output of the dimensional equation } // namespace units diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index 2f899b34..31604c86 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -471,6 +471,33 @@ template return is_same_v; } + +/** + * @brief Computes the value of a unit raised to the `Num/Den` power + * + * @tparam Num Exponent numerator + * @tparam Den Exponent denominator + * @param u Unit being the base of the operation + * + * @return Unit The result of computation + */ +template + requires detail::non_zero +[[nodiscard]] consteval Unit auto pow(U u) +{ + if constexpr (requires { U::symbol; }) { + if constexpr (Den == 1) + return derived_unit>{}; + else + return derived_unit>{}; + } else if constexpr (detail::is_specialization_of_scaled_unit) { + return scaled_unit(U::mag), std::remove_const_t(U::reference_unit))>>{}; + } else { + return detail::expr_pow(u); + } +} + + // Helper variable templates to create common powers template inline constexpr decltype(U * U) square; diff --git a/test/unit_test/static/dimension_test.cpp b/test/unit_test/static/dimension_test.cpp index 00d68473..25cb5c3d 100644 --- a/test/unit_test/static/dimension_test.cpp +++ b/test/unit_test/static/dimension_test.cpp @@ -207,4 +207,19 @@ static_assert(!convertible(length_dim, time_dim)); static_assert(acceleration_dim != speed_dim); static_assert(!convertible(acceleration_dim, speed_dim)); +// power +static_assert(is_of_type(length_dim), derived_dimension>>); +static_assert(is_of_type(length_dim), derived_dimension>>); +static_assert(is_of_type(length_dim* length_dim), length_dim_>); +static_assert(is_of_type(length_dim* length_dim* length_dim), length_dim_>); +static_assert(is_of_type(length_dim* length_dim), derived_dimension>>); +static_assert(is_of_type(length_dim / time_dim), + derived_dimension, per>>>); +static_assert(is_of_type(length_dim / (time_dim * time_dim)), + derived_dimension, per>>); + +static_assert(is_same_v(length_dim)), decltype(length_dim * length_dim)>); +static_assert( + is_same_v(length_dim / time_dim)), decltype(length_dim * length_dim / time_dim / time_dim)>); + } // namespace diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index eac02572..9df7d453 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -357,6 +357,30 @@ static_assert(joule == newton * metre); static_assert(watt == joule / second); static_assert(watt == kilogram * square / cubic); +// power +static_assert(is_same_v(metre)), decltype(metre * metre)>); +static_assert(is_same_v(kilometre)), decltype(kilometre * kilometre)>); +static_assert(is_same_v(si::kilo)), decltype(si::kilo * si::kilo)>); +static_assert(is_same_v(hour)), decltype(hour * hour)>); +static_assert(is_same_v(mag<3600> * second)), decltype((mag<3600> * second) * (mag<3600> * second))>); +static_assert(is_same_v(metre / second)), decltype(metre * metre / second / second)>); +static_assert(is_same_v(kilometre / hour)), decltype(kilometre * kilometre / hour / hour)>); + +static_assert(is_of_type(metre), derived_unit>>); +static_assert(is_of_type(metre), derived_unit>>); +static_assert(is_of_type(metre* metre), metre_>); +static_assert(is_of_type(metre* metre* metre), metre_>); +static_assert(is_of_type(metre* metre), derived_unit>>); +static_assert(is_of_type(metre / second), derived_unit, per>>>); +static_assert(is_of_type(metre / (second * second)), derived_unit, per>>); +static_assert(is_of_type>>); + +static_assert(is_of_type(kilometre), derived_unit>>); +static_assert(is_of_type(si::kilo), derived_unit, 2>>>); +static_assert(is_of_type(hour), derived_unit>>); +static_assert( + is_of_type(mag<3600>* second), scaled_unit * mag<3600>, derived_unit>>>); + // common_type static_assert(std::is_same_v, gram_>); static_assert(std::is_same_v, kilogram_>);