feat: PointOrigin arithmetics support added

This commit is contained in:
Mateusz Pusz
2023-08-18 18:35:41 +02:00
parent 110986ee1f
commit 752408449f
3 changed files with 321 additions and 50 deletions

View File

@@ -116,8 +116,34 @@ template<typename T>
requires is_derived_from_specialization_of_quantity_point<T>
inline constexpr bool is_quantity_point<T> = true;
// the below was introduced to workaround gcc-12 bug that produced an error
// "error: const struct mp_units::isq::time has no member named absolute_point_origin"
template<typename PO1, typename PO2>
constexpr bool same_absolute_point_origins_lazy(PO1, PO2)
{
if constexpr (is_derived_from_specialization_of_relative_point_origin<PO1> &&
is_derived_from_specialization_of_relative_point_origin<PO2>)
return std::same_as<std::remove_const_t<decltype(PO1::absolute_point_origin)>,
std::remove_const_t<decltype(PO2::absolute_point_origin)>>;
else if constexpr (is_derived_from_specialization_of_relative_point_origin<PO1>)
return std::same_as<std::remove_const_t<decltype(PO1::absolute_point_origin)>, PO2>;
else if constexpr (is_derived_from_specialization_of_relative_point_origin<PO2>)
return std::same_as<PO1, std::remove_const_t<decltype(PO2::absolute_point_origin)>>;
else
return false;
}
} // namespace detail
template<typename PO1, auto PO2>
concept PointOriginOf =
PointOrigin<PO1> && PointOrigin<std::remove_const_t<decltype(PO2)>> &&
(std::same_as<PO1, std::remove_const_t<decltype(PO2)>> || detail::same_absolute_point_origins_lazy(PO1{}, PO2) ||
(detail::is_specialization_of_absolute_point_origin<PO1> &&
detail::is_specialization_of_absolute_point_origin<std::remove_const_t<decltype(PO2)>> &&
implicitly_convertible(PO1::quantity_spec, PO2.quantity_spec) &&
!detail::NestedQuantityKindSpecOf<PO1::quantity_spec, PO2.quantity_spec>));
/**
* @brief A concept matching all quantity points with provided dimension or quantity spec
*
@@ -127,15 +153,8 @@ inline constexpr bool is_quantity_point<T> = true;
*/
template<typename QP, auto V>
concept QuantityPointOf =
QuantityPoint<QP> &&
(ReferenceOf<std::remove_const_t<decltype(QP::reference)>, V> ||
(PointOrigin<std::remove_const_t<decltype(V)>> &&
(std::same_as<std::remove_const_t<decltype(QP::point_origin)>, std::remove_const_t<decltype(V)>> ||
std::same_as<std::remove_const_t<decltype(QP::absolute_point_origin)>, std::remove_const_t<decltype(V)>> ||
(detail::is_specialization_of_absolute_point_origin<std::remove_const_t<decltype(QP::absolute_point_origin)>> &&
detail::is_specialization_of_absolute_point_origin<std::remove_const_t<decltype(V)>> &&
implicitly_convertible(QP::absolute_point_origin.quantity_spec, V.quantity_spec) &&
!detail::DerivedFromQuantityKindSpecOf<QP::absolute_point_origin.quantity_spec, V.quantity_spec>))));
QuantityPoint<QP> && (ReferenceOf<std::remove_const_t<decltype(QP::reference)>, V> ||
PointOriginOf<std::remove_const_t<decltype(QP::absolute_point_origin)>, V>);
/**
* @brief A concept matching all external quantity point like types

View File

@@ -37,7 +37,9 @@ struct absolute_point_origin {
template<QuantityPoint auto QP>
struct relative_point_origin {
static constexpr QuantityPoint auto quantity_point = QP;
static constexpr QuantitySpec auto quantity_spec = QP.quantity_spec;
static constexpr QuantitySpec auto quantity_spec =
common_quantity_spec(QP.quantity_spec, QP.point_origin.quantity_spec);
static constexpr PointOrigin auto absolute_point_origin = QP.absolute_point_origin;
};
namespace detail {
@@ -114,16 +116,17 @@ public:
template<QuantityPointOf<absolute_point_origin> QP>
requires std::constructible_from<quantity_type, typename QP::quantity_type>
// TODO add perfect forwarding
constexpr explicit(!std::convertible_to<typename QP::quantity_type, quantity_type>) quantity_point(const QP& qp) :
q_([&] {
if constexpr (is_same_v<std::remove_const_t<decltype(point_origin)>,
std::remove_const_t<decltype(QP::point_origin)>>) {
return qp.relative();
} else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin<
} else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<
std::remove_const_t<decltype(point_origin)>>) {
return qp.absolute() - zero().absolute();
} else {
return qp.absolute();
} else {
return qp.absolute() - zero().absolute();
}
}())
{
@@ -146,11 +149,11 @@ public:
{
if constexpr (is_same_v<NewPO, std::remove_const_t<decltype(point_origin)>>) {
return *this;
} else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin<NewPO>) {
auto q = absolute() - origin.quantity_point.absolute();
} else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<NewPO>) {
auto q = absolute();
return quantity_point<reference, NewPO{}, typename decltype(q)::rep>(std::move(q));
} else {
auto q = absolute();
auto q = absolute() - origin.quantity_point.absolute();
return quantity_point<reference, NewPO{}, typename decltype(q)::rep>(std::move(q));
}
}
@@ -242,35 +245,57 @@ explicit quantity_point(QP)
template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
// TODO simplify when gcc catches up
requires ReferenceOf<std::remove_const_t<decltype(R2)>, get_quantity_spec(R1)>
[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity_point<R1, PO1, Rep1>& lhs,
const quantity<R2, Rep2>& rhs)
requires requires { lhs.relative() + rhs; }
[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity_point<R1, PO1, Rep1>& qp,
const quantity<R2, Rep2>& q)
requires requires { qp.relative() + q; }
{
const auto q = lhs.relative() + rhs;
using q_type = decltype(q);
return quantity_point<q_type::reference, PO1, typename q_type::rep>(q);
auto temp = qp.relative() + q;
using q_type = decltype(temp);
return quantity_point<q_type::reference, PO1, typename q_type::rep>(std::move(temp));
}
template<auto R1, typename Rep1, auto R2, auto PO2, typename Rep2>
// TODO simplify when gcc catches up
requires ReferenceOf<std::remove_const_t<decltype(R1)>, get_quantity_spec(R2)>
[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity<R1, Rep1>& lhs,
const quantity_point<R2, PO2, Rep2>& rhs)
requires requires { rhs + lhs; }
[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity<R1, Rep1>& q,
const quantity_point<R2, PO2, Rep2>& qp)
requires requires { q + qp.relative(); }
{
return rhs + lhs;
return qp + q;
}
template<PointOrigin PO, Quantity Q>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(PO, Q&& q)
{
return quantity_point<Q::reference, PO{}, typename Q::rep>(std::forward<Q>(q));
}
template<Quantity Q, PointOrigin PO>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(Q&& q, PO po)
{
return po + std::forward<Q>(q);
}
template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
// TODO simplify when gcc catches up
requires ReferenceOf<std::remove_const_t<decltype(R2)>, get_quantity_spec(R1)>
[[nodiscard]] constexpr QuantityPoint auto operator-(const quantity_point<R1, PO1, Rep1>& lhs,
const quantity<R2, Rep2>& rhs)
requires requires { lhs.relative() - rhs; }
[[nodiscard]] constexpr QuantityPoint auto operator-(const quantity_point<R1, PO1, Rep1>& qp,
const quantity<R2, Rep2>& q)
requires requires { qp.relative() - q; }
{
const auto q = lhs.relative() - rhs;
using q_type = decltype(q);
return quantity_point<q_type::reference, PO1, typename q_type::rep>(q);
const auto temp = qp.relative() - q;
using q_type = decltype(temp);
return quantity_point<q_type::reference, PO1, typename q_type::rep>(std::move(temp));
}
template<PointOrigin PO, Quantity Q>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] constexpr QuantityPoint auto operator-(PO po, const Q& q)
requires requires { -q; }
{
return po + (-q);
}
template<QuantityPoint QP1, QuantityPointOf<QP1::absolute_point_origin> QP2>
@@ -278,12 +303,55 @@ template<QuantityPoint QP1, QuantityPointOf<QP1::absolute_point_origin> QP2>
requires requires { lhs.absolute() - rhs.absolute(); }
{
if constexpr (is_same_v<std::remove_const_t<decltype(QP1::point_origin)>,
std::remove_const_t<decltype(QP2::point_origin)>>)
return lhs.relative() - rhs.relative();
else
std::remove_const_t<decltype(QP2::point_origin)>>) {
constexpr auto common_qs = common_quantity_spec(QP1::quantity_spec, QP1::point_origin.quantity_spec,
QP2::quantity_spec, QP2::point_origin.quantity_spec);
return quantity_cast<common_qs>(lhs.relative() - rhs.relative());
} else
return lhs.absolute() - rhs.absolute();
}
template<PointOrigin PO, QuantityPointOf<PO{}> QP>
requires ReferenceOf<std::remove_const_t<decltype(QP::reference)>, PO::quantity_spec>
[[nodiscard]] constexpr Quantity auto operator-(const QP& qp, PO)
{
constexpr auto common_qs = common_quantity_spec(PO::quantity_spec, QP::quantity_spec, QP::point_origin.quantity_spec);
if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<PO>) {
if constexpr (is_same_v<std::remove_const_t<PO>, std::remove_const_t<decltype(QP::point_origin)>>)
return quantity_cast<common_qs>(qp.relative());
else
return quantity_cast<common_qs>(qp.absolute());
} else {
if constexpr (is_same_v<std::remove_const_t<PO>, std::remove_const_t<decltype(QP::point_origin)>>)
return quantity_cast<common_qs>(qp.relative());
else
return quantity_cast<common_qs>(qp.absolute() - PO::quantity_point.absolute());
}
}
template<PointOrigin PO, QuantityPointOf<PO{}> QP>
requires ReferenceOf<std::remove_const_t<decltype(QP::reference)>, PO::quantity_spec>
[[nodiscard]] constexpr Quantity auto operator-(PO po, const QP& qp)
{
return -(qp - po);
}
template<PointOrigin PO1, PointOriginOf<PO1{}> PO2>
requires QuantitySpecOf<std::remove_const_t<decltype(PO1::quantity_spec)>, PO2::quantity_spec> &&
(detail::is_derived_from_specialization_of_relative_point_origin<PO1> ||
detail::is_derived_from_specialization_of_relative_point_origin<PO2>)
[[nodiscard]] constexpr Quantity auto operator-(PO1 po1, PO2 po2)
{
constexpr auto common_qs = common_quantity_spec(PO1::quantity_spec, PO2::quantity_spec);
if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<PO1>) {
return quantity_cast<common_qs>(-po2.quantity_point.absolute());
} else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<PO2>) {
return quantity_cast<common_qs>(po1.quantity_point.absolute());
} else {
return quantity_cast<common_qs>(po1.quantity_point - po2.quantity_point);
}
}
template<QuantityPoint QP1, QuantityPointOf<QP1::absolute_point_origin> QP2>
requires std::three_way_comparable_with<typename QP1::quantity_type, typename QP2::quantity_type>
[[nodiscard]] constexpr auto operator<=>(const QP1& lhs, const QP2& rhs)

View File

@@ -420,6 +420,8 @@ static_assert(quantity_point<isq::height[m], tower_peak>(42 * m).point_from(towe
static_assert(quantity_point<isq::height[m], mean_sea_level>(42 * m).point_from(tower_peak).relative() == -42 * m);
static_assert(quantity_point<isq::height[m], ground_level>(84 * m).point_from(tower_peak).relative() == 42 * m);
static_assert(is_of_type<quantity_point<isq::height[m], ground_level, short>(42 * m).point_from(mean_sea_level),
quantity_point<isq::height[m], mean_sea_level, int>>);
///////////////////////////////////
// obtaining an absolute quantity
@@ -593,7 +595,7 @@ static_assert(invalid_compound_assignments<quantity_point>);
// binary operators
////////////////////
template<template<auto> typename QP>
template<template<auto> typename QP, auto Origin>
concept invalid_binary_operations = requires {
// can't add two quantity points
requires !requires { QP<isq::length[m]>(1 * m) + QP<isq::length[m]>(1 * m); };
@@ -601,24 +603,34 @@ concept invalid_binary_operations = requires {
// can't add more generic quantity (violates point_origin quantity_spec)
requires !requires { QP<isq::height[m]>(1 * m) + isq::length(1 * m); };
requires !requires { isq::length(1 * m) + QP<isq::height[m]>(1 * m); };
requires !requires { Origin + isq::length(1 * m); };
requires !requires { isq::length(1 * m) + Origin; };
// can't subtract more generic quantity (violates point_origin quantity_spec)
requires !requires { QP<isq::height[m]>(1 * m) - isq::length(1 * m); };
requires !requires { Origin - isq::length(1 * m); };
// quantity point can't be subtracted from a quantity
requires !requires { 1 * m - QP<si::metre>(1 * m); };
requires !requires { 1 * m - Origin; };
// no crossdimensional addition and subtraction
requires !requires { QP<si::metre>(1 * m) + 1 * s; };
requires !requires { QP<si::metre>(1 * m) - 1 * s; };
requires !requires { Origin + 1 * s; };
requires !requires { Origin - 1 * s; };
// unit constants
requires !requires { QP<si::metre>(1) + m; };
requires !requires { QP<si::metre>(1) - m; };
requires !requires { Origin + m; };
requires !requires { Origin - m; };
requires !requires { m + QP<si::metre>(1); };
requires !requires { m - QP<si::metre>(1); };
requires !requires { m + Origin; };
requires !requires { m - Origin; };
};
static_assert(invalid_binary_operations<quantity_point>);
static_assert(invalid_binary_operations<quantity_point, mean_sea_level>);
template<template<auto, auto> typename QP>
concept invalid_binary_operations_with_origins = requires {
@@ -638,6 +650,20 @@ concept invalid_binary_operations_with_origins = requires {
};
static_assert(invalid_binary_operations_with_origins<quantity_point>);
template<PointOrigin auto QP1, PointOrigin auto QP2>
concept invalid_subtraction_of_point_origins = requires {
requires !requires { QP1 - QP2; };
requires !requires { QP2 - QP1; };
};
// cant'subtract two unrelated quantity points
static_assert(invalid_subtraction_of_point_origins<mean_sea_level, absolute_point_origin<isq::length>{}>);
// cant'subtract two absolute quantity points as we do not know the unit for the resulting quantity
static_assert(
invalid_subtraction_of_point_origins<absolute_point_origin<isq::length>{}, absolute_point_origin<isq::length>{}>);
static_assert(invalid_subtraction_of_point_origins<mean_sea_level, mean_sea_level>);
// same representation type
static_assert(is_of_type<quantity_point(1 * m) + 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
@@ -695,6 +721,58 @@ static_assert(is_of_type<isq::height(1 * m) + quantity_point(isq::length(1 * km)
static_assert(is_of_type<isq::height(1 * km) + quantity_point(isq::length(1 * m)),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<absolute_point_origin<isq::length>{} + 1 * m,
quantity_point<si::metre, absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<absolute_point_origin<isq::length>{} + 1 * km,
quantity_point<si::kilo<si::metre>, absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<absolute_point_origin<isq::length>{} + isq::length(1 * m),
quantity_point<isq::length[si::metre], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<absolute_point_origin<isq::length>{} + isq::distance(1 * m),
quantity_point<isq::distance[si::metre], absolute_point_origin<isq::length>{}, int>>);
static_assert(
is_of_type<absolute_point_origin<isq::length>{} + isq::distance(1 * km),
quantity_point<isq::distance[si::kilo<si::metre>], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<1 * m + absolute_point_origin<isq::length>{},
quantity_point<si::metre, absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<1 * km + absolute_point_origin<isq::length>{},
quantity_point<si::kilo<si::metre>, absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<isq::length(1 * m) + absolute_point_origin<isq::length>{},
quantity_point<isq::length[si::metre], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<isq::distance(1 * m) + absolute_point_origin<isq::length>{},
quantity_point<isq::distance[si::metre], absolute_point_origin<isq::length>{}, int>>);
static_assert(
is_of_type<isq::distance(1 * km) + absolute_point_origin<isq::length>{},
quantity_point<isq::distance[si::kilo<si::metre>], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<absolute_point_origin<kind_of<isq::length>>{} + 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<absolute_point_origin<kind_of<isq::length>>{} + 1 * km,
quantity_point<si::kilo<si::metre>, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<absolute_point_origin<kind_of<isq::length>>{} + isq::distance(1 * m),
quantity_point<isq::distance[si::metre], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(
is_of_type<absolute_point_origin<kind_of<isq::length>>{} + isq::distance(1 * km),
quantity_point<isq::distance[si::kilo<si::metre>], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<1 * m + absolute_point_origin<kind_of<isq::length>>{},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<1 * km + absolute_point_origin<kind_of<isq::length>>{},
quantity_point<si::kilo<si::metre>, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<isq::distance(1 * m) + absolute_point_origin<kind_of<isq::length>>{},
quantity_point<isq::distance[si::metre], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(
is_of_type<isq::distance(1 * km) + absolute_point_origin<kind_of<isq::length>>{},
quantity_point<isq::distance[si::kilo<si::metre>], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<mean_sea_level + 1 * m, quantity_point<si::metre, mean_sea_level, int>>);
static_assert(is_of_type<mean_sea_level + 1 * km, quantity_point<si::kilo<si::metre>, mean_sea_level, int>>);
static_assert(is_of_type<1 * m + mean_sea_level, quantity_point<si::metre, mean_sea_level, int>>);
static_assert(is_of_type<1 * km + mean_sea_level, quantity_point<si::kilo<si::metre>, mean_sea_level, int>>);
static_assert(is_of_type<ground_level + 1 * m, quantity_point<si::metre, ground_level, int>>);
static_assert(is_of_type<ground_level + 1 * km, quantity_point<si::kilo<si::metre>, ground_level, int>>);
static_assert(is_of_type<1 * m + ground_level, quantity_point<si::metre, ground_level, int>>);
static_assert(is_of_type<1 * km + ground_level, quantity_point<si::kilo<si::metre>, ground_level, int>>);
static_assert(is_of_type<quantity_point(1 * m) - 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * km) - 1 * m,
@@ -723,6 +801,32 @@ static_assert(is_of_type<quantity_point(isq::length(1 * m)) - isq::height(1 * km
static_assert(is_of_type<quantity_point(isq::length(1 * km)) - isq::height(1 * m),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<absolute_point_origin<isq::length>{} - 1 * m,
quantity_point<si::metre, absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<absolute_point_origin<isq::length>{} - 1 * km,
quantity_point<si::kilo<si::metre>, absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<absolute_point_origin<isq::length>{} - isq::distance(1 * m),
quantity_point<isq::distance[si::metre], absolute_point_origin<isq::length>{}, int>>);
static_assert(
is_of_type<absolute_point_origin<isq::length>{} - isq::distance(1 * km),
quantity_point<isq::distance[si::kilo<si::metre>], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<absolute_point_origin<kind_of<isq::length>>{} - 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<absolute_point_origin<kind_of<isq::length>>{} - 1 * km,
quantity_point<si::kilo<si::metre>, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<absolute_point_origin<kind_of<isq::length>>{} - isq::distance(1 * m),
quantity_point<isq::distance[si::metre], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(
is_of_type<absolute_point_origin<kind_of<isq::length>>{} - isq::distance(1 * km),
quantity_point<isq::distance[si::kilo<si::metre>], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<mean_sea_level - 1 * m, quantity_point<si::metre, mean_sea_level, int>>);
static_assert(is_of_type<mean_sea_level - 1 * km, quantity_point<si::kilo<si::metre>, mean_sea_level, int>>);
static_assert(is_of_type<ground_level - 1 * m, quantity_point<si::metre, ground_level, int>>);
static_assert(is_of_type<ground_level - 1 * km, quantity_point<si::kilo<si::metre>, ground_level, int>>);
static_assert(is_of_type<quantity_point(1 * m) - quantity_point(1 * m), quantity<si::metre, int>>);
static_assert(is_of_type<quantity_point(1 * km) - quantity_point(1 * m), quantity<si::metre, int>>);
static_assert(is_of_type<quantity_point(1 * m) - quantity_point(1 * km), quantity<si::metre, int>>);
@@ -742,27 +846,107 @@ static_assert(
static_assert(
is_of_type<quantity_point(isq::length(1 * km)) - quantity_point(isq::height(1 * m)), quantity<isq::length[m], int>>);
static_assert(is_of_type<quantity_point(1 * m, mean_sea_level) - quantity_point(1 * m, ground_level),
static_assert(is_of_type<quantity_point<si::metre, mean_sea_level, int>(1 * m) -
quantity_point<si::metre, mean_sea_level, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point(1 * m, ground_level) - quantity_point(1 * m, mean_sea_level),
static_assert(
is_of_type<quantity_point<si::metre, ground_level, int>(1 * m) - quantity_point<si::metre, ground_level, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(
is_of_type<quantity_point<si::metre, tower_peak, int>(1 * m) - quantity_point<si::metre, tower_peak, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point<si::metre, mean_sea_level, int>(1 * m) -
quantity_point<si::metre, ground_level, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point(1 * m, tower_peak) - quantity_point(1 * m, ground_level),
static_assert(is_of_type<quantity_point<si::metre, ground_level, int>(1 * m) -
quantity_point<si::metre, mean_sea_level, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point(1 * m, ground_level) - quantity_point(1 * m, tower_peak),
static_assert(
is_of_type<quantity_point<si::metre, tower_peak, int>(1 * m) - quantity_point<si::metre, ground_level, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(
is_of_type<quantity_point<si::metre, ground_level, int>(1 * m) - quantity_point<si::metre, tower_peak, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(
is_of_type<quantity_point<si::metre, tower_peak, int>(1 * m) - quantity_point<si::metre, mean_sea_level, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(
is_of_type<quantity_point<si::metre, mean_sea_level, int>(1 * m) - quantity_point<si::metre, tower_peak, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point<si::metre, other_ground_level, int>(1 * m) -
quantity_point<si::metre, ground_level, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point(1 * m, tower_peak) - quantity_point(1 * m, mean_sea_level),
static_assert(is_of_type<quantity_point<si::metre, ground_level, int>(1 * m) -
quantity_point<si::metre, other_ground_level, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point(1 * m, mean_sea_level) - quantity_point(1 * m, tower_peak),
static_assert(is_of_type<quantity_point<si::metre, other_ground_level, int>(1 * m) -
quantity_point<si::metre, tower_peak, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point(1 * m, other_ground_level) - quantity_point(1 * m, ground_level),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point(1 * m, ground_level) - quantity_point(1 * m, other_ground_level),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point(1 * m, other_ground_level) - quantity_point(1 * m, tower_peak),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<quantity_point(1 * m, tower_peak) - quantity_point(1 * m, other_ground_level),
static_assert(is_of_type<quantity_point<si::metre, tower_peak, int>(1 * m) -
quantity_point<si::metre, other_ground_level, int>(1 * m),
quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<absolute_point_origin<isq::length>{} - quantity_point(1 * m), quantity<isq::length[m], int>>);
static_assert(
is_of_type<absolute_point_origin<isq::length>{} - quantity_point(1 * km), quantity<isq::length[km], int>>);
static_assert(
is_of_type<absolute_point_origin<isq::length>{} - quantity_point(isq::height(1 * m)), quantity<isq::length[m], int>>);
static_assert(is_of_type<absolute_point_origin<isq::length>{} - quantity_point(isq::height(1 * km)),
quantity<isq::length[km], int>>);
static_assert(
is_of_type<absolute_point_origin<kind_of<isq::length>>{} - quantity_point(1 * m), quantity<si::metre, int>>);
static_assert(is_of_type<absolute_point_origin<kind_of<isq::length>>{} - quantity_point(1 * km),
quantity<si::kilo<si::metre>, int>>);
static_assert(is_of_type<absolute_point_origin<kind_of<isq::length>>{} - quantity_point(isq::height(1 * m)),
quantity<isq::height[m], int>>);
static_assert(is_of_type<absolute_point_origin<kind_of<isq::length>>{} - quantity_point(isq::height(1 * km)),
quantity<isq::height[km], int>>);
static_assert(is_of_type<mean_sea_level - quantity_point(1 * m, mean_sea_level), quantity<isq::height[m], int>>);
static_assert(is_of_type<mean_sea_level - quantity_point(1 * km, mean_sea_level), quantity<isq::height[km], int>>);
static_assert(is_of_type<quantity_point(1 * m, mean_sea_level) - mean_sea_level, quantity<isq::height[m], int>>);
static_assert(is_of_type<quantity_point(1 * km, mean_sea_level) - mean_sea_level, quantity<isq::height[km], int>>);
static_assert(is_of_type<ground_level - quantity_point(1 * m, ground_level), quantity<isq::height[m], int>>);
static_assert(is_of_type<ground_level - quantity_point(1 * km, ground_level), quantity<isq::height[km], int>>);
static_assert(is_of_type<quantity_point(1 * m, ground_level) - ground_level, quantity<isq::height[m], int>>);
static_assert(is_of_type<quantity_point(1 * km, ground_level) - ground_level, quantity<isq::height[km], int>>);
static_assert(is_of_type<mean_sea_level - quantity_point(1 * m, ground_level), quantity<isq::height[m], int>>);
static_assert(is_of_type<mean_sea_level - quantity_point(1 * km, ground_level), quantity<isq::height[m], int>>);
static_assert(is_of_type<quantity_point(1 * m, ground_level) - mean_sea_level, quantity<isq::height[m], int>>);
static_assert(is_of_type<quantity_point(1 * km, ground_level) - mean_sea_level, quantity<isq::height[m], int>>);
static_assert(is_of_type<ground_level - quantity_point(1 * m, mean_sea_level), quantity<isq::height[m], int>>);
static_assert(is_of_type<ground_level - quantity_point(1 * km, mean_sea_level), quantity<isq::height[m], int>>);
static_assert(is_of_type<quantity_point(1 * m, mean_sea_level) - ground_level, quantity<isq::height[m], int>>);
static_assert(is_of_type<quantity_point(1 * km, mean_sea_level) - ground_level, quantity<isq::height[m], int>>);
static_assert(is_of_type<ground_level - ground_level, quantity<isq::height[m], int>>);
static_assert(is_of_type<mean_sea_level - ground_level, quantity<isq::height[m], int>>);
static_assert(is_of_type<ground_level - mean_sea_level, quantity<isq::height[m], int>>);
static_assert(is_of_type<ground_level - tower_peak, quantity<isq::height[m], int>>);
static_assert(is_of_type<tower_peak - ground_level, quantity<isq::height[m], int>>);
static_assert(
is_of_type<quantity_point(isq::length(1 * m)) - quantity_point(isq::height(1 * m)), quantity<isq::length[m], int>>);
static_assert(
is_of_type<quantity_point(isq::length(1 * m)) - quantity_point(isq::height(1 * km)), quantity<isq::length[m], int>>);
static_assert(
is_of_type<quantity_point(isq::length(1 * km)) - quantity_point(isq::height(1 * m)), quantity<isq::length[m], int>>);
static_assert(is_of_type<(1 * m + mean_sea_level) - (1 * m + ground_level), quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<(1 * m + ground_level) - (1 * m + mean_sea_level), quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<(1 * m + tower_peak) - (1 * m + ground_level), quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<(1 * m + ground_level) - (1 * m + tower_peak), quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<(1 * m + tower_peak) - (1 * m + mean_sea_level), quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<(1 * m + mean_sea_level) - (1 * m + tower_peak), quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<(1 * m + other_ground_level) - (1 * m + ground_level), quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<(1 * m + ground_level) - (1 * m + other_ground_level), quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<(1 * m + other_ground_level) - (1 * m + tower_peak), quantity<isq::height[si::metre], int>>);
static_assert(is_of_type<(1 * m + tower_peak) - (1 * m + other_ground_level), quantity<isq::height[si::metre], int>>);
// check for integral types promotion
static_assert(