refactor: quantity_spec casts functions renamed

This commit is contained in:
Mateusz Pusz
2023-04-08 19:49:51 +02:00
parent c54aa9b0eb
commit d65393b4e2
8 changed files with 196 additions and 195 deletions

View File

@@ -56,7 +56,7 @@ class quantity;
* @tparam ToQS a quantity specification to use for a target quantity
*/
template<QuantitySpec auto ToQS, typename Q>
requires Quantity<std::remove_cvref_t<Q>> && (castable_to(std::remove_cvref_t<Q>::quantity_spec, ToQS))
requires Quantity<std::remove_cvref_t<Q>> && (castable(std::remove_cvref_t<Q>::quantity_spec, ToQS))
[[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q)
{
constexpr reference<ToQS, std::remove_cvref_t<Q>::unit> r;

View File

@@ -56,7 +56,7 @@ concept Quantity = requires(T* t) { detail::to_base_specialization_of_quantity(t
template<typename Q, auto V>
concept QuantityOf =
Quantity<Q> && ((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
(QuantitySpec<std::remove_const_t<decltype(V)>> && implicitly_convertible_to(Q::quantity_spec, V)));
(QuantitySpec<std::remove_const_t<decltype(V)>> && implicitly_convertible(Q::quantity_spec, V)));
/**
* @brief A concept matching all external quantities like types

View File

@@ -68,7 +68,7 @@ concept PointOrigin = QuantityPoint<T> || detail::is_derived_from_specialization
* Satisfied by all quantity point origins that are defined using a provided quantity specification.
*/
template<typename T, auto Q>
concept PointOriginFor = PointOrigin<T> && QuantitySpec<std::remove_const_t<decltype(Q)>> && Q == T::quantity_spec;
concept PointOriginFor = PointOrigin<T> && QuantitySpec<std::remove_const_t<decltype(Q)>> && implicitly_convertible(Q, T::quantity_spec);
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO,
RepresentationOf<get_quantity_spec(R).character> Rep>
@@ -95,7 +95,7 @@ template<typename QP, auto V>
concept QuantityPointOf =
QuantityPoint<QP> &&
((Dimension<std::remove_const_t<decltype(V)>> && QP::dimension == V) ||
(QuantitySpec<std::remove_const_t<decltype(V)>> && implicitly_convertible_to(QP::quantity_spec, V)) ||
(QuantitySpec<std::remove_const_t<decltype(V)>> && implicitly_convertible(QP::quantity_spec, V)) ||
(PointOrigin<std::remove_const_t<decltype(V)>> &&
std::same_as<std::remove_const_t<decltype(QP::absolute_point_origin)>, std::remove_const_t<decltype(V)>>));

View File

@@ -43,7 +43,7 @@ namespace detail {
* @tparam To a target quantity type to cast to
*/
template<Quantity To, auto R, typename Rep>
requires(castable_to(get_quantity_spec(R), To::quantity_spec)) &&
requires(castable(get_quantity_spec(R), To::quantity_spec)) &&
((get_unit(R) == To::unit && std::constructible_from<typename To::rep, Rep>) ||
(get_unit(R) != To::unit)) // && scalable_with_<typename To::rep>))
// TODO how to constrain the second part here?

View File

@@ -58,7 +58,7 @@ concept Harmonic = // exposition only
template<typename QFrom, typename QTo>
concept QuantityConvertibleTo = // exposition only
Quantity<QFrom> && Quantity<QTo> && implicitly_convertible_to(QFrom::quantity_spec, QTo::quantity_spec) &&
Quantity<QFrom> && Quantity<QTo> && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) &&
convertible_to(QFrom::unit, QTo::unit) && requires(QFrom q) { detail::sudo_cast<QTo>(q); } &&
(treat_as_floating_point<typename QTo::rep> ||
(!treat_as_floating_point<typename QFrom::rep> && Harmonic<QFrom::unit, QTo::unit>));

View File

@@ -323,12 +323,12 @@ struct quantity_spec<Self, QS, Args...> : std::remove_const_t<decltype(QS)> {
#ifdef __cpp_explicit_this_parameter
template<detail::NamedQuantitySpec auto QS, detail::IntermediateDerivedQuantitySpec auto Eq,
one_of<quantity_character> auto... Args>
requires requires { QS._equation_; } && (implicitly_convertible_to(Eq, QS._equation_))
requires requires { QS._equation_; } && (implicitly_convertible(Eq, QS._equation_))
struct quantity_spec<QS, Eq, Args...> : quantity_spec<QS, Args...> {
#else
template<typename Self, detail::NamedQuantitySpec auto QS, detail::IntermediateDerivedQuantitySpec auto Eq,
auto... Args>
requires requires { QS._equation_; } && (implicitly_convertible_to(Eq, QS._equation_))
requires requires { QS._equation_; } && (implicitly_convertible(Eq, QS._equation_))
struct quantity_spec<Self, QS, Eq, Args...> : quantity_spec<Self, QS, Args...> {
#endif
static constexpr auto _equation_ = Eq;
@@ -684,7 +684,7 @@ template<ratio Complexity, IntermediateDerivedQuantitySpec Q>
}
template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval convertible_to_result convertible_to_impl(Q1 q1, Q2 q2)
[[nodiscard]] consteval convertible_to_result convertible_impl(Q1 q1, Q2 q2)
{
using enum convertible_to_result;
@@ -695,9 +695,9 @@ template<QuantitySpec Q1, QuantitySpec Q2>
else if constexpr (QuantityKindSpec<Q1> || QuantityKindSpec<Q2>) {
if constexpr (IntermediateDerivedQuantitySpec<Q1> &&
NamedQuantitySpec<std::remove_const_t<decltype(remove_kind(q2))>>)
return convertible_to_impl(get_kind(q1), remove_kind(q2));
return convertible_impl(get_kind(q1), remove_kind(q2));
else
return convertible_to_impl(get_kind(q1), get_kind(q2)) != no ? yes : no;
return convertible_impl(get_kind(q1), get_kind(q2)) != no ? yes : no;
} else if constexpr (IntermediateDerivedQuantitySpec<Q1> && IntermediateDerivedQuantitySpec<Q2>)
return are_ingredients_convertible_to(q1, q2);
else if constexpr (NamedQuantitySpec<Q1> && NamedQuantitySpec<Q2>) {
@@ -710,15 +710,15 @@ template<QuantitySpec Q1, QuantitySpec Q2>
} else if constexpr (IntermediateDerivedQuantitySpec<Q1>) {
auto q1_exploded = explode<get_complexity(q2)>(q1);
if constexpr (NamedQuantitySpec<std::remove_const_t<decltype(q1_exploded)>>)
return convertible_to_impl(q1_exploded, q2);
return convertible_impl(q1_exploded, q2);
else if constexpr (requires { q2._equation_; })
return convertible_to_impl(q1_exploded, q2._equation_);
return convertible_impl(q1_exploded, q2._equation_);
} else if constexpr (IntermediateDerivedQuantitySpec<Q2>) {
auto q2_exploded = explode<get_complexity(q1)>(q2);
if constexpr (NamedQuantitySpec<std::remove_const_t<decltype(q2_exploded)>>)
return convertible_to_impl(q2_exploded, q1);
return convertible_impl(q1, q2_exploded);
else if constexpr (requires { q1._equation_; })
return std::min(explicit_conversion, convertible_to_impl(q1._equation_, q2_exploded));
return std::min(explicit_conversion, convertible_impl(q1._equation_, q2_exploded));
}
return no;
}
@@ -726,21 +726,21 @@ template<QuantitySpec Q1, QuantitySpec Q2>
} // namespace detail
template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval bool implicitly_convertible_to(Q1 q1, Q2 q2)
[[nodiscard]] consteval bool implicitly_convertible(Q1 q1, Q2 q2)
{
return detail::convertible_to_impl(q1, q2) == detail::convertible_to_result::yes;
return detail::convertible_impl(q1, q2) == detail::convertible_to_result::yes;
}
template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval bool explicitly_convertible_to(Q1 q1, Q2 q2)
[[nodiscard]] consteval bool explicitly_convertible(Q1 q1, Q2 q2)
{
return detail::convertible_to_impl(q1, q2) >= detail::convertible_to_result::explicit_conversion;
return detail::convertible_impl(q1, q2) >= detail::convertible_to_result::explicit_conversion;
}
template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval bool castable_to(Q1 q1, Q2 q2)
[[nodiscard]] consteval bool castable(Q1 q1, Q2 q2)
{
return detail::convertible_to_impl(q1, q2) >= detail::convertible_to_result::cast;
return detail::convertible_impl(q1, q2) >= detail::convertible_to_result::cast;
}
namespace detail {
@@ -783,14 +783,13 @@ template<QuantitySpec Q>
template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2)
requires(implicitly_convertible_to(get_kind(q1), get_kind(q2)) ||
implicitly_convertible_to(get_kind(q2), get_kind(q1)))
requires(implicitly_convertible(get_kind(q1), get_kind(q2)) || implicitly_convertible(get_kind(q2), get_kind(q1)))
{
if constexpr (q1 == q2)
return q1;
else if constexpr (detail::have_common_base(q1, q2))
return detail::get_common_base(q1, q2);
else if constexpr (implicitly_convertible_to(get_kind(q1), get_kind(q2)))
else if constexpr (implicitly_convertible(get_kind(q1), get_kind(q2)))
return get_kind(q2);
else
return get_kind(q1);

View File

@@ -122,19 +122,19 @@ struct reference {
template<auto Q2, auto U2>
[[nodiscard]] friend consteval bool convertible_to(reference, reference<Q2, U2>)
{
return implicitly_convertible_to(Q, Q2) && convertible_to(U, U2);
return implicitly_convertible(Q, Q2) && convertible_to(U, U2);
}
template<AssociatedUnit U2>
[[nodiscard]] friend consteval bool convertible_to(reference, U2 u2)
{
return implicitly_convertible_to(Q, get_quantity_spec(u2)) && convertible_to(U, u2);
return implicitly_convertible(Q, get_quantity_spec(u2)) && convertible_to(U, u2);
}
template<AssociatedUnit U1>
[[nodiscard]] friend consteval bool convertible_to(U1 u1, reference)
{
return implicitly_convertible_to(get_quantity_spec(u1), Q) && convertible_to(u1, U);
return implicitly_convertible(get_quantity_spec(u1), Q) && convertible_to(u1, U);
}
};

View File

@@ -435,233 +435,235 @@ using namespace detail;
using enum convertible_to_result;
// different dimensions
static_assert(convertible_to_impl(mass, length) == no);
static_assert(convertible_to_impl(speed, length) == no);
static_assert(convertible_to_impl(length, speed) == no);
static_assert(convertible_to_impl(energy, speed) == no);
static_assert(convertible_to_impl(length, kind_of<time>) == no);
static_assert(convertible_to_impl(kind_of<time>, length) == no);
static_assert(convertible_to_impl(energy, kind_of<speed>) == no);
static_assert(convertible_to_impl(kind_of<speed>, energy) == no);
static_assert(convertible_impl(mass, length) == no);
static_assert(convertible_impl(speed, length) == no);
static_assert(convertible_impl(length, speed) == no);
static_assert(convertible_impl(energy, speed) == no);
static_assert(convertible_impl(length, kind_of<time>) == no);
static_assert(convertible_impl(kind_of<time>, length) == no);
static_assert(convertible_impl(energy, kind_of<speed>) == no);
static_assert(convertible_impl(kind_of<speed>, energy) == no);
// the same types
static_assert(convertible_to_impl(length, length) == yes);
static_assert(convertible_to_impl(width, width) == yes);
static_assert(convertible_to_impl(energy, energy) == yes);
static_assert(convertible_to_impl(kind_of<length>, kind_of<length>) == yes);
static_assert(convertible_to_impl(kind_of<energy>, kind_of<energy>) == yes);
static_assert(convertible_to_impl(kind_of<get_kind(moment_of_force)>, kind_of<get_kind(moment_of_force)>) == yes);
static_assert(convertible_impl(length, length) == yes);
static_assert(convertible_impl(width, width) == yes);
static_assert(convertible_impl(energy, energy) == yes);
static_assert(convertible_impl(kind_of<length>, kind_of<length>) == yes);
static_assert(convertible_impl(kind_of<energy>, kind_of<energy>) == yes);
static_assert(convertible_impl(kind_of<get_kind(moment_of_force)>, kind_of<get_kind(moment_of_force)>) == yes);
// converting to a different branch
static_assert(convertible_to_impl(height, width) == cast);
static_assert(convertible_to_impl(potential_energy, kinetic_energy) == cast);
static_assert(convertible_to_impl(kinetic_energy, potential_energy) == cast);
static_assert(convertible_to_impl(rate_of_climb, velocity) == cast);
static_assert(convertible_impl(height, width) == cast);
static_assert(convertible_impl(potential_energy, kinetic_energy) == cast);
static_assert(convertible_impl(kinetic_energy, potential_energy) == cast);
static_assert(convertible_impl(rate_of_climb, velocity) == cast);
// converting to a different kind
static_assert(convertible_to_impl(frequency, activity) == no);
static_assert(convertible_to_impl(activity, frequency) == no);
static_assert(convertible_to_impl(energy, moment_of_force) == no);
static_assert(convertible_to_impl(energy, torque) == no);
static_assert(convertible_impl(frequency, activity) == no);
static_assert(convertible_impl(activity, frequency) == no);
static_assert(convertible_impl(energy, moment_of_force) == no);
static_assert(convertible_impl(energy, torque) == no);
// upcasting same hierarchy branch
static_assert(convertible_to_impl(width, length) == yes);
static_assert(convertible_to_impl(path_length, length) == yes);
static_assert(convertible_to_impl(distance, length) == yes);
static_assert(convertible_to_impl(distance, path_length) == yes);
static_assert(convertible_to_impl(potential_energy, energy) == yes);
static_assert(convertible_to_impl(kinetic_energy, energy) == yes);
static_assert(convertible_impl(width, length) == yes);
static_assert(convertible_impl(path_length, length) == yes);
static_assert(convertible_impl(distance, length) == yes);
static_assert(convertible_impl(distance, path_length) == yes);
static_assert(convertible_impl(potential_energy, energy) == yes);
static_assert(convertible_impl(kinetic_energy, energy) == yes);
// upcasting beyond the hierarchy/kind
static_assert(convertible_to_impl(frequency, 1 / time) == explicit_conversion);
static_assert(convertible_to_impl(speed, length / time) == explicit_conversion);
static_assert(convertible_to_impl(speed, length / time) == explicit_conversion);
static_assert(convertible_to_impl(velocity, length / time) == explicit_conversion);
static_assert(convertible_to_impl(rate_of_climb, length / time) == explicit_conversion);
static_assert(convertible_to_impl(rate_of_climb, height / time) == explicit_conversion);
static_assert(convertible_impl(frequency, 1 / time) == explicit_conversion);
static_assert(convertible_impl(speed, length / time) == explicit_conversion);
static_assert(convertible_impl(speed, length / time) == explicit_conversion);
static_assert(convertible_impl(velocity, length / time) == explicit_conversion);
static_assert(convertible_impl(rate_of_climb, length / time) == explicit_conversion);
static_assert(convertible_impl(rate_of_climb, height / time) == explicit_conversion);
// downcasting same hierarchy branch
static_assert(convertible_to_impl(length, width) == explicit_conversion);
static_assert(convertible_to_impl(path_length, distance) == explicit_conversion);
static_assert(convertible_to_impl(length, distance) == explicit_conversion);
static_assert(convertible_to_impl(path_length, distance) == explicit_conversion);
static_assert(convertible_to_impl(energy, potential_energy) == explicit_conversion);
static_assert(convertible_to_impl(energy, kinetic_energy) == explicit_conversion);
static_assert(convertible_impl(length, width) == explicit_conversion);
static_assert(convertible_impl(path_length, distance) == explicit_conversion);
static_assert(convertible_impl(length, distance) == explicit_conversion);
static_assert(convertible_impl(path_length, distance) == explicit_conversion);
static_assert(convertible_impl(energy, potential_energy) == explicit_conversion);
static_assert(convertible_impl(energy, kinetic_energy) == explicit_conversion);
// derived quantities to type
static_assert(convertible_to_impl(1 / frequency, time) == yes);
static_assert(convertible_to_impl(1 / time, frequency) == yes);
static_assert(convertible_to_impl(length * length, area) == yes);
static_assert(convertible_to_impl(length / time, speed) == yes);
static_assert(convertible_to_impl(height / time, speed) == yes);
static_assert(convertible_to_impl(length / time, velocity) == yes);
static_assert(convertible_to_impl(position_vector / time, velocity) == yes);
static_assert(convertible_to_impl(length / time, rate_of_climb) == yes);
static_assert(convertible_to_impl(height / time, rate_of_climb) == yes);
static_assert(convertible_to_impl(area / length, length) == yes);
static_assert(convertible_to_impl(length * length * length, volume) == yes);
static_assert(convertible_to_impl(area * length, volume) == yes);
static_assert(convertible_to_impl(volume / length, area) == yes);
static_assert(convertible_to_impl(volume / area, length) == yes);
static_assert(convertible_to_impl(volume / length / length, length) == yes);
// static_assert(convertible_to_impl(area * area / length, volume) == yes);
// static_assert(convertible_to_impl(area * (area / length), volume) == yes);
static_assert(convertible_to_impl(volume / (length * length), length) == yes);
static_assert(convertible_to_impl(length / time, speed) == yes);
static_assert(convertible_to_impl(length / time, velocity) == yes);
static_assert(convertible_to_impl(position_vector / time, speed) == yes);
static_assert(convertible_to_impl(position_vector / time, velocity) == yes);
static_assert(convertible_to_impl(distance / speed, time) == yes);
// static_assert(convertible_to_impl(length / speed, time) == yes);
static_assert(convertible_to_impl(speed * time, length) == yes);
static_assert(convertible_to_impl(length / time / time, acceleration) == yes);
static_assert(convertible_to_impl(position_vector / time / time, acceleration) == yes);
static_assert(convertible_to_impl(position_vector / (time * time), acceleration) == yes);
static_assert(convertible_to_impl(velocity / time, acceleration) == yes);
static_assert(convertible_to_impl(velocity / acceleration, time) == yes);
static_assert(convertible_to_impl(acceleration * time, velocity) == yes);
static_assert(convertible_to_impl(acceleration * (time * time), position_vector) == yes);
static_assert(convertible_to_impl(acceleration / velocity, frequency) == yes);
static_assert(convertible_to_impl(force * length, energy) == yes);
static_assert(convertible_to_impl(force * position_vector, energy) == yes);
static_assert(convertible_to_impl(force * length, torque) == yes);
static_assert(convertible_to_impl(force * position_vector, torque) == yes);
static_assert(convertible_to_impl(width * height, area) == yes);
static_assert(convertible_impl(1 / frequency, time) == yes);
static_assert(convertible_impl(1 / period_duration, frequency) == yes);
static_assert(convertible_impl(length * length, area) == yes);
static_assert(convertible_impl(length / time, speed) == yes);
static_assert(convertible_impl(position_vector / time, speed) == yes);
static_assert(convertible_impl(position_vector / time, velocity) == yes);
static_assert(convertible_impl(height / time, speed) == yes);
static_assert(convertible_impl(height / time, rate_of_climb) == yes);
static_assert(convertible_impl(area / length, length) == yes);
static_assert(convertible_impl(length * length * length, volume) == yes);
static_assert(convertible_impl(area * length, volume) == yes);
static_assert(convertible_impl(volume / length, area) == yes);
static_assert(convertible_impl(volume / area, length) == yes);
static_assert(convertible_impl(volume / length / length, length) == yes);
// static_assert(convertible_impl(area * area / length, volume) == yes);
// static_assert(convertible_impl(area * (area / length), volume) == yes);
static_assert(convertible_impl(volume / (length * length), length) == yes);
static_assert(convertible_impl(distance / speed, time) == yes);
// static_assert(convertible_impl(length / speed, time) == yes);
static_assert(convertible_impl(speed * time, length) == yes);
static_assert(convertible_impl(position_vector / time / time, acceleration) == yes);
static_assert(convertible_impl(position_vector / (time * time), acceleration) == yes);
static_assert(convertible_impl(velocity / time, acceleration) == yes);
static_assert(convertible_impl(velocity / acceleration, time) == yes);
static_assert(convertible_impl(acceleration * time, velocity) == yes);
static_assert(convertible_impl(acceleration * (time * time), position_vector) == yes);
static_assert(convertible_impl(acceleration / velocity, frequency) == yes);
static_assert(convertible_impl(force * length, energy) == yes);
static_assert(convertible_impl(force * position_vector, energy) == yes);
static_assert(convertible_impl(force * length, torque) == yes);
static_assert(convertible_impl(force * position_vector, torque) == yes);
static_assert(convertible_impl(width * height, area) == yes);
static_assert(convertible_impl(pow<1, 2>(area), length) == yes);
static_assert(convertible_impl(length, pow<1, 2>(area)) == yes);
// derived quantities to more constrained type
// static_assert(convertible_impl(1 / time, frequency) == explicit_conversion);
// static_assert(convertible_impl(length / time / time, acceleration) == explicit_conversion);
// static_assert(convertible_impl(length / time, velocity) == explicit_conversion);
// static_assert(convertible_impl(length / time, rate_of_climb) == explicit_conversion);
// quantities derived from dimensionless
static_assert(convertible_to_impl(power / power, efficiency) == explicit_conversion);
static_assert(convertible_to_impl(stress / stress, strain) == explicit_conversion);
static_assert(convertible_to_impl(stress / stress, efficiency) == explicit_conversion);
static_assert(convertible_impl(power / power, efficiency) == explicit_conversion);
static_assert(convertible_impl(stress / stress, strain) == explicit_conversion);
static_assert(convertible_impl(stress / stress, efficiency) == explicit_conversion);
// derived quantities to incompatible type
// static_assert(convertible_to_impl(height / time, velocity) == cast);
// static_assert(convertible_to_impl(position_vector / time, rate_of_climb) == cast);
// static_assert(convertible_impl(height / time, velocity) == cast);
// static_assert(convertible_impl(position_vector / time, rate_of_climb) == cast);
// type to compatible derived
static_assert(convertible_to_impl(distance, speed* time) == yes);
static_assert(convertible_impl(distance, speed* time) == yes);
// type to more specialized derived quantity
// static_assert(convertible_to_impl(speed, height / time) == explicit_conversion);
// static_assert(convertible_to_impl(speed, position_vector / time) == explicit_conversion);
// static_assert(convertible_impl(speed, height / time) == explicit_conversion);
// static_assert(convertible_impl(speed, position_vector / time) == explicit_conversion);
// type to a derived quantity on a different branch
// static_assert(convertible_to_impl(velocity, height / time) == cast);
// static_assert(convertible_to_impl(rate_of_climb, position_vector / time) == cast);
// static_assert(convertible_impl(velocity, height / time) == cast);
// static_assert(convertible_impl(rate_of_climb, position_vector / time) == cast);
// derived quantities requiring explosion to a type
static_assert(convertible_to_impl(acceleration * time, velocity) == yes);
static_assert(convertible_to_impl(acceleration * period_duration, velocity) == yes);
static_assert(convertible_to_impl(velocity * time / period_duration, velocity) == yes);
static_assert(convertible_to_impl(mass * acceleration_of_free_fall * height / weight, height) == yes);
static_assert(convertible_impl(acceleration * time, velocity) == yes);
static_assert(convertible_impl(acceleration * period_duration, velocity) == yes);
static_assert(convertible_impl(velocity * time / period_duration, velocity) == yes);
static_assert(convertible_impl(mass * acceleration_of_free_fall * height / weight, height) == yes);
// derived quantities to more generic derived compatible type
static_assert(convertible_to_impl(1 / (width * height), 1 / area) == yes);
static_assert(convertible_to_impl(path_length * distance, pow<2>(path_length)) == yes);
static_assert(convertible_impl(1 / (width * height), 1 / area) == yes);
static_assert(convertible_impl(path_length * distance, pow<2>(path_length)) == yes);
// derived to compatible derived
static_assert(convertible_to_impl(1 / (length * length), 1 / area) == yes);
static_assert(convertible_to_impl(velocity * time, acceleration* pow<2>(time)) == yes);
static_assert(convertible_to_impl(height / period_duration, length / time) == yes);
static_assert(convertible_to_impl(height / width, length / length) == yes);
static_assert(convertible_to_impl(height * width, length* length) == yes);
static_assert(convertible_to_impl(1 / (path_length * distance), 1 / pow<2>(path_length)) == yes);
static_assert(convertible_impl(1 / (length * length), 1 / area) == yes);
static_assert(convertible_impl(velocity * time, acceleration* pow<2>(time)) == yes);
static_assert(convertible_impl(height / period_duration, length / time) == yes);
static_assert(convertible_impl(height / width, length / length) == yes);
static_assert(convertible_impl(height * width, length* length) == yes);
static_assert(convertible_impl(1 / (path_length * distance), 1 / pow<2>(path_length)) == yes);
// derived to more specialized derived
// static_assert(convertible_to_impl(length / time, height / period_duration) == explicit_conversion);
// static_assert(convertible_to_impl(length * length, height / width) == explicit_conversion);
// static_assert(convertible_impl(length / time, height / period_duration) == explicit_conversion);
// static_assert(convertible_impl(length * length, height / width) == explicit_conversion);
// derived to incompatible specialized derived
// static_assert(convertible_to_impl(height / time, distance / time) == cast);
// static_assert(convertible_impl(height / time, distance / time) == cast);
// when more than one possible combination is present
// static_assert(convertible_to_impl(width * height, pow<2>(height)) == cast);
// static_assert(convertible_to_impl(1 / (width * height), 1 / pow<2>(height)) == cast);
// static_assert(convertible_to_impl(width * distance, path_length* width) == cast);
// static_assert(convertible_to_impl(height * distance, path_length* height) == cast);
// static_assert(convertible_to_impl(width * length, length* height) == cast);
// static_assert(convertible_to_impl(length * distance, path_length* height) == cast);
// static_assert(convertible_to_impl(width * distance, width* path_length) == cast);
// static_assert(convertible_to_impl(altitude * distance, height* path_length) == cast);
// static_assert(convertible_to_impl(length * distance, altitude* path_length) == cast);
// static_assert(convertible_impl(width * height, pow<2>(height)) == cast);
// static_assert(convertible_impl(1 / (width * height), 1 / pow<2>(height)) == cast);
// static_assert(convertible_impl(width * distance, path_length* width) == cast);
// static_assert(convertible_impl(height * distance, path_length* height) == cast);
// static_assert(convertible_impl(width * length, length* height) == cast);
// static_assert(convertible_impl(length * distance, path_length* height) == cast);
// static_assert(convertible_impl(width * distance, width* path_length) == cast);
// static_assert(convertible_impl(altitude * distance, height* path_length) == cast);
// static_assert(convertible_impl(length * distance, altitude* path_length) == cast);
// kind to its type
static_assert(convertible_to_impl(kind_of<length>, length) == yes);
static_assert(convertible_to_impl(kind_of<length>, width) == yes);
static_assert(convertible_to_impl(kind_of<length>, position_vector) == yes);
static_assert(convertible_to_impl(kind_of<frequency>, frequency) == yes);
static_assert(convertible_to_impl(kind_of<speed>, velocity) == yes);
static_assert(convertible_to_impl(kind_of<energy>, energy) == yes);
static_assert(convertible_to_impl(kind_of<energy>, potential_energy) == yes);
static_assert(convertible_to_impl(kind_of<energy>, kinetic_energy) == yes);
static_assert(convertible_impl(kind_of<length>, length) == yes);
static_assert(convertible_impl(kind_of<length>, width) == yes);
static_assert(convertible_impl(kind_of<length>, position_vector) == yes);
static_assert(convertible_impl(kind_of<frequency>, frequency) == yes);
static_assert(convertible_impl(kind_of<speed>, velocity) == yes);
static_assert(convertible_impl(kind_of<energy>, energy) == yes);
static_assert(convertible_impl(kind_of<energy>, potential_energy) == yes);
static_assert(convertible_impl(kind_of<energy>, kinetic_energy) == yes);
// kind to a type of a different dimension
static_assert(convertible_to_impl(kind_of<length>, mass) == no);
static_assert(convertible_to_impl(kind_of<length>, speed) == no);
static_assert(convertible_to_impl(kind_of<energy>, length) == no);
static_assert(convertible_to_impl(kind_of<energy>, speed) == no);
static_assert(convertible_impl(kind_of<length>, mass) == no);
static_assert(convertible_impl(kind_of<length>, speed) == no);
static_assert(convertible_impl(kind_of<energy>, length) == no);
static_assert(convertible_impl(kind_of<energy>, speed) == no);
// kind to a type of another kind but the same dimension
static_assert(convertible_to_impl(kind_of<energy>, moment_of_force) == no);
static_assert(convertible_to_impl(kind_of<activity>, frequency) == no);
static_assert(convertible_to_impl(kind_of<frequency>, activity) == no);
static_assert(convertible_impl(kind_of<energy>, moment_of_force) == no);
static_assert(convertible_impl(kind_of<activity>, frequency) == no);
static_assert(convertible_impl(kind_of<frequency>, activity) == no);
// derived kind to a compatible type
static_assert(convertible_to_impl(kind_of<length / time>, speed) == yes);
static_assert(convertible_to_impl(kind_of<length / time>, velocity) == yes);
static_assert(convertible_to_impl(kind_of<length / pow<2>(time)>, acceleration) == yes);
static_assert(convertible_to_impl(kind_of<1 / time>, frequency) == yes);
static_assert(convertible_to_impl(kind_of<1 / time>, activity) == yes);
static_assert(convertible_to_impl(kind_of<mass * pow<2>(length) / pow<2>(time)>, energy) == yes);
static_assert(convertible_to_impl(kind_of<mass * pow<2>(length) / pow<2>(time)>, moment_of_force) == yes);
static_assert(convertible_impl(kind_of<length / time>, speed) == yes);
static_assert(convertible_impl(kind_of<length / time>, velocity) == yes);
static_assert(convertible_impl(kind_of<length / pow<2>(time)>, acceleration) == yes);
static_assert(convertible_impl(kind_of<1 / time>, frequency) == yes);
static_assert(convertible_impl(kind_of<1 / time>, activity) == yes);
static_assert(convertible_impl(kind_of<mass * pow<2>(length) / pow<2>(time)>, energy) == yes);
static_assert(convertible_impl(kind_of<mass * pow<2>(length) / pow<2>(time)>, moment_of_force) == yes);
// type to a kind of a different kind
static_assert(convertible_to_impl(mass, kind_of<length>) == no);
static_assert(convertible_to_impl(speed, kind_of<length>) == no);
static_assert(convertible_to_impl(length, kind_of<energy>) == no);
static_assert(convertible_to_impl(speed, kind_of<energy>) == no);
static_assert(convertible_to_impl(moment_of_force, kind_of<energy>) == no);
static_assert(convertible_to_impl(frequency, kind_of<activity>) == no);
static_assert(convertible_to_impl(activity, kind_of<frequency>) == no);
static_assert(convertible_to_impl(moment_of_force, kind_of<energy>) == no);
static_assert(convertible_impl(mass, kind_of<length>) == no);
static_assert(convertible_impl(speed, kind_of<length>) == no);
static_assert(convertible_impl(length, kind_of<energy>) == no);
static_assert(convertible_impl(speed, kind_of<energy>) == no);
static_assert(convertible_impl(moment_of_force, kind_of<energy>) == no);
static_assert(convertible_impl(frequency, kind_of<activity>) == no);
static_assert(convertible_impl(activity, kind_of<frequency>) == no);
static_assert(convertible_impl(moment_of_force, kind_of<energy>) == no);
// converting type to a kind
static_assert(convertible_to_impl(length, kind_of<length>) == yes);
static_assert(convertible_to_impl(width, kind_of<length>) == yes);
static_assert(convertible_to_impl(frequency, kind_of<frequency>) == yes);
static_assert(convertible_to_impl(frequency, kind_of<1 / time>) == yes);
static_assert(convertible_to_impl(frequency, kind_of<activity>) == no);
static_assert(convertible_to_impl(energy, kind_of<energy>) == yes);
static_assert(convertible_to_impl(potential_energy, kind_of<energy>) == yes);
static_assert(convertible_to_impl(kinetic_energy, kind_of<energy>) == yes);
static_assert(convertible_impl(length, kind_of<length>) == yes);
static_assert(convertible_impl(width, kind_of<length>) == yes);
static_assert(convertible_impl(frequency, kind_of<frequency>) == yes);
static_assert(convertible_impl(frequency, kind_of<1 / time>) == yes);
static_assert(convertible_impl(frequency, kind_of<activity>) == no);
static_assert(convertible_impl(energy, kind_of<energy>) == yes);
static_assert(convertible_impl(potential_energy, kind_of<energy>) == yes);
static_assert(convertible_impl(kinetic_energy, kind_of<energy>) == yes);
// converting derived type to a kind
static_assert(convertible_to_impl(1 / time, kind_of<frequency>) == yes);
static_assert(convertible_to_impl(length / time, kind_of<speed>) == yes);
static_assert(convertible_to_impl(length / pow<2>(time), kind_of<acceleration>) == yes);
static_assert(convertible_impl(1 / time, kind_of<frequency>) == yes);
static_assert(convertible_impl(length / time, kind_of<speed>) == yes);
static_assert(convertible_impl(length / pow<2>(time), kind_of<acceleration>) == yes);
// converting derived kind to a kind
static_assert(convertible_to_impl(kind_of<1 / time>, kind_of<frequency>) == yes);
static_assert(convertible_to_impl(kind_of<length / time>, kind_of<speed>) == yes);
static_assert(convertible_to_impl(kind_of<length / pow<2>(time)>, kind_of<acceleration>) == yes);
static_assert(convertible_impl(kind_of<1 / time>, kind_of<frequency>) == yes);
static_assert(convertible_impl(kind_of<length / time>, kind_of<speed>) == yes);
static_assert(convertible_impl(kind_of<length / pow<2>(time)>, kind_of<acceleration>) == yes);
// converting type to a derived kind
static_assert(convertible_to_impl(speed, kind_of<length / time>) == yes);
static_assert(convertible_to_impl(velocity, kind_of<length / time>) == yes);
static_assert(convertible_to_impl(energy, kind_of<mass * pow<2>(length) / pow<2>(time)>) == yes);
static_assert(convertible_impl(speed, kind_of<length / time>) == yes);
static_assert(convertible_impl(velocity, kind_of<length / time>) == yes);
static_assert(convertible_impl(energy, kind_of<mass * pow<2>(length) / pow<2>(time)>) == yes);
// kinds of different dimensions
static_assert(convertible_to_impl(kind_of<mass>, kind_of<length>) == no);
static_assert(convertible_to_impl(kind_of<energy>, kind_of<length>) == no);
static_assert(convertible_to_impl(kind_of<length>, kind_of<energy>) == no);
static_assert(convertible_to_impl(kind_of<frequency>, kind_of<energy>) == no);
static_assert(convertible_impl(kind_of<mass>, kind_of<length>) == no);
static_assert(convertible_impl(kind_of<energy>, kind_of<length>) == no);
static_assert(convertible_impl(kind_of<length>, kind_of<energy>) == no);
static_assert(convertible_impl(kind_of<frequency>, kind_of<energy>) == no);
// derived quantities to dimensionless
static_assert(convertible_to_impl(frequency * period_duration, dimensionless) == yes);
static_assert(convertible_to_impl(frequency * time, dimensionless) == yes);
static_assert(convertible_to_impl(length / length, dimensionless) == yes);
static_assert(convertible_to_impl(length / width, dimensionless) == yes);
static_assert(convertible_impl(frequency * period_duration, dimensionless) == yes);
static_assert(convertible_impl(frequency * time, dimensionless) == yes);
static_assert(convertible_impl(length / length, dimensionless) == yes);
static_assert(convertible_impl(length / width, dimensionless) == yes);
static_assert(convertible_to_impl(efficiency, strain) == cast);
static_assert(convertible_impl(efficiency, strain) == cast);
// quantity character checks
static_assert((position_vector / time).character == quantity_character::vector);