feat: power<Num, Den>() support added for dimensions and units

This commit is contained in:
Mateusz Pusz
2022-10-23 10:47:12 +02:00
parent 9bfe75ebf6
commit e7bebac2a7
5 changed files with 124 additions and 6 deletions

View File

@ -112,13 +112,17 @@ inline constexpr bool is_specialization_of_power<power<F, Ints...>> = true;
template<typename T, ratio R> template<typename T, ratio R>
consteval auto power_or_T_impl() consteval auto power_or_T_impl()
{ {
if constexpr (R.den == 1) { if constexpr (is_specialization_of_power<T>) {
if constexpr (R.num == 1) return power_or_T_impl<typename T::factor, T::exponent * R>();
return T{};
else
return power<T, R.num>{};
} else { } else {
return power<T, R.num, R.den>{}; if constexpr (R.den == 1) {
if constexpr (R.num == 1)
return T{};
else
return power<T, R.num>{};
} else {
return power<T, R.num, R.den>{};
}
} }
}; };
@ -437,6 +441,32 @@ template<template<typename...> typename To, typename OneType, typename T>
return To<OneType, per<T>>{}; return To<OneType, per<T>>{};
} }
template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, typename... Nums, typename... Dens>
requires detail::non_zero<Den>
[[nodiscard]] consteval auto expr_pow_impl(type_list<Nums...>, type_list<Dens...>)
{
return detail::get_optimized_expression<type_list<power_or_T<Nums, ratio{Num, Den}>...>,
type_list<power_or_T<Dens, ratio{Num, Den}>...>, 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<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, typename T>
requires detail::non_zero<Den>
[[nodiscard]] consteval auto expr_pow(T)
{
return expr_pow_impl<Num, Den, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{});
}
} // namespace detail } // namespace detail
} // namespace units } // namespace units

View File

@ -285,6 +285,28 @@ template<Dimension D1, Dimension D2>
return std::derived_from<D1, D2> || std::derived_from<D2, D1>; return std::derived_from<D1, D2> || std::derived_from<D2, D1>;
} }
/**
* @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<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
requires detail::non_zero<Den>
[[nodiscard]] consteval Dimension auto pow(D d)
{
if constexpr (BaseDimension<D>) {
if constexpr (Den == 1)
return derived_dimension<power<D, Num>>{};
else
return derived_dimension<power<D, Num, Den>>{};
} else
return detail::expr_pow<Num, Den, derived_dimension, struct one_dim, detail::type_list_of_base_dimension_less>(d);
}
// TODO consider adding the support for text output of the dimensional equation // TODO consider adding the support for text output of the dimensional equation
} // namespace units } // namespace units

View File

@ -471,6 +471,33 @@ template<Unit Lhs, Unit Rhs>
return is_same_v<decltype(canonical_lhs.reference_unit), decltype(canonical_rhs.reference_unit)>; return is_same_v<decltype(canonical_lhs.reference_unit), decltype(canonical_rhs.reference_unit)>;
} }
/**
* @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<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
requires detail::non_zero<Den>
[[nodiscard]] consteval Unit auto pow(U u)
{
if constexpr (requires { U::symbol; }) {
if constexpr (Den == 1)
return derived_unit<power<U, Num>>{};
else
return derived_unit<power<U, Num, Den>>{};
} else if constexpr (detail::is_specialization_of_scaled_unit<U>) {
return scaled_unit<pow<ratio{Num, Den}>(U::mag), std::remove_const_t<decltype(pow<Num, Den>(U::reference_unit))>>{};
} else {
return detail::expr_pow<Num, Den, derived_unit, struct one, detail::type_list_of_unit_less>(u);
}
}
// Helper variable templates to create common powers // Helper variable templates to create common powers
template<Unit auto U> template<Unit auto U>
inline constexpr decltype(U * U) square; inline constexpr decltype(U * U) square;

View File

@ -207,4 +207,19 @@ static_assert(!convertible(length_dim, time_dim));
static_assert(acceleration_dim != speed_dim); static_assert(acceleration_dim != speed_dim);
static_assert(!convertible(acceleration_dim, speed_dim)); static_assert(!convertible(acceleration_dim, speed_dim));
// power
static_assert(is_of_type<pow<2>(length_dim), derived_dimension<power<length_dim_, 2>>>);
static_assert(is_of_type<pow<1, 2>(length_dim), derived_dimension<power<length_dim_, 1, 2>>>);
static_assert(is_of_type<pow<1, 2>(length_dim* length_dim), length_dim_>);
static_assert(is_of_type<pow<1, 3>(length_dim* length_dim* length_dim), length_dim_>);
static_assert(is_of_type<pow<1, 3>(length_dim* length_dim), derived_dimension<power<length_dim_, 2, 3>>>);
static_assert(is_of_type<pow<1, 2>(length_dim / time_dim),
derived_dimension<power<length_dim_, 1, 2>, per<power<time_dim_, 1, 2>>>>);
static_assert(is_of_type<pow<1, 2>(length_dim / (time_dim * time_dim)),
derived_dimension<power<length_dim_, 1, 2>, per<time_dim_>>>);
static_assert(is_same_v<decltype(pow<2>(length_dim)), decltype(length_dim * length_dim)>);
static_assert(
is_same_v<decltype(pow<2>(length_dim / time_dim)), decltype(length_dim * length_dim / time_dim / time_dim)>);
} // namespace } // namespace

View File

@ -357,6 +357,30 @@ static_assert(joule == newton * metre);
static_assert(watt == joule / second); static_assert(watt == joule / second);
static_assert(watt == kilogram * square<metre> / cubic<second>); static_assert(watt == kilogram * square<metre> / cubic<second>);
// power
static_assert(is_same_v<decltype(pow<2>(metre)), decltype(metre * metre)>);
static_assert(is_same_v<decltype(pow<2>(kilometre)), decltype(kilometre * kilometre)>);
static_assert(is_same_v<decltype(pow<2>(si::kilo<metre>)), decltype(si::kilo<metre> * si::kilo<metre>)>);
static_assert(is_same_v<decltype(pow<2>(hour)), decltype(hour * hour)>);
static_assert(is_same_v<decltype(pow<2>(mag<3600> * second)), decltype((mag<3600> * second) * (mag<3600> * second))>);
static_assert(is_same_v<decltype(pow<2>(metre / second)), decltype(metre * metre / second / second)>);
static_assert(is_same_v<decltype(pow<2>(kilometre / hour)), decltype(kilometre * kilometre / hour / hour)>);
static_assert(is_of_type<pow<2>(metre), derived_unit<power<metre_, 2>>>);
static_assert(is_of_type<pow<1, 2>(metre), derived_unit<power<metre_, 1, 2>>>);
static_assert(is_of_type<pow<1, 2>(metre* metre), metre_>);
static_assert(is_of_type<pow<1, 3>(metre* metre* metre), metre_>);
static_assert(is_of_type<pow<1, 3>(metre* metre), derived_unit<power<metre_, 2, 3>>>);
static_assert(is_of_type<pow<1, 2>(metre / second), derived_unit<power<metre_, 1, 2>, per<power<second_, 1, 2>>>>);
static_assert(is_of_type<pow<1, 2>(metre / (second * second)), derived_unit<power<metre_, 1, 2>, per<second_>>>);
static_assert(is_of_type<kilometre * kilometre, derived_unit<power<kilometre_, 2>>>);
static_assert(is_of_type<pow<2>(kilometre), derived_unit<power<kilometre_, 2>>>);
static_assert(is_of_type<pow<2>(si::kilo<metre>), derived_unit<power<si::kilo_<metre>, 2>>>);
static_assert(is_of_type<pow<2>(hour), derived_unit<power<hour_, 2>>>);
static_assert(
is_of_type<pow<2>(mag<3600>* second), scaled_unit<mag<3600> * mag<3600>, derived_unit<power<second_, 2>>>>);
// common_type // common_type
static_assert(std::is_same_v<std::common_type_t<decltype(gram), decltype(gram)>, gram_>); static_assert(std::is_same_v<std::common_type_t<decltype(gram), decltype(gram)>, gram_>);
static_assert(std::is_same_v<std::common_type_t<decltype(kilogram), decltype(kilogram)>, kilogram_>); static_assert(std::is_same_v<std::common_type_t<decltype(kilogram), decltype(kilogram)>, kilogram_>);