Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vector and Tensor quantities #493

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/core/include/mp-units/bits/reference_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ concept ReferenceOf =
(QuantitySpec<std::remove_const_t<decltype(V)>> && implicitly_convertible(get_quantity_spec(T{}), V) &&
!detail::DerivedFromQuantityKindSpecOf<get_quantity_spec(T{}), V> &&
(detail::QuantityKindSpec<std::remove_const_t<decltype(get_quantity_spec(T{}))>> ||
!detail::DerivedFromQuantityKindSpecOf<V, get_quantity_spec(T{})>)));
!detail::DerivedFromQuantityKindSpecOf<V, get_quantity_spec(T{})>)) ||
(std::same_as<std::remove_const_t<decltype(V)>, quantity_character> && (get_quantity_spec(T{}).character == V)));

} // namespace mp_units
91 changes: 72 additions & 19 deletions src/core/include/mp-units/bits/representation_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,35 +52,88 @@ enum class quantity_character { scalar, vector, tensor };

namespace detail {

template<typename T, typename U>
concept CommonTypeWith =
std::same_as<std::common_type_t<T, U>, std::common_type_t<U, T>> &&
std::constructible_from<std::common_type_t<T, U>, T> && std::constructible_from<std::common_type_t<T, U>, U>;
template<typename T>
concept Scalar = is_scalar<T>;

template<typename T>
concept Vector = is_vector<T>;

template<typename T>
concept Tensor = is_tensor<T>;

template<typename T, typename U = T>
concept ScalableNumber =
std::regular_invocable<std::multiplies<>, T, U> && std::regular_invocable<std::divides<>, T, U>;
template<typename T, quantity_character Ch>
concept IsOfCharacter =
(Ch == quantity_character::scalar && is_scalar<T>) || (Ch == quantity_character::vector && is_vector<T>) ||
(Ch == quantity_character::tensor && is_tensor<T>);

template<typename T>
[[nodiscard]] consteval quantity_character get_character(T)
{
if constexpr (Tensor<T>)
return quantity_character::tensor;
else if constexpr (Vector<T>)
return quantity_character::vector;
else if constexpr (Scalar<T>)
return quantity_character::scalar;
}

// clang-format off
template<typename T>
concept ScalarRepresentation = std::regular<T> && Scalar<T> &&
requires(T a, T b, std::intmax_t i, long double f) {
{ a * i } -> Scalar;
{ i * a } -> Scalar;
{ a / i } -> Scalar;
{ a * f } -> Scalar; // TODO How this affects freestanding?
{ f * a } -> Scalar;
{ a / f } -> Scalar;
{ a + b } -> Scalar;
{ a - b } -> Scalar;
{ a * b } -> Scalar;
{ a / b } -> Scalar;
};

template<typename T>
concept CastableNumber = CommonTypeWith<T, std::intmax_t> && ScalableNumber<std::common_type_t<T, std::intmax_t>>;
concept VectorRepresentation = std::regular<T> && Vector<T> &&
requires(T a, T b, std::intmax_t i, long double f) {
{ a * i } -> Vector;
{ i * a } -> Vector;
{ a / i } -> Vector;
{ a * f } -> Vector;
{ f * a } -> Vector;
{ a / f } -> Vector;
{ a + b } -> Vector;
{ a - b } -> Vector;
{ dot_product(a, b) } -> Scalar;
{ cross_product(a, b) } -> Vector;
{ tensor_product(a, b) } -> Tensor;
{ norm(a) } -> Scalar;
};

// TODO Fix it according to sudo_cast implementation
template<typename T>
concept Scalable =
CastableNumber<T> ||
(requires { typename T::value_type; } && CastableNumber<typename T::value_type> &&
ScalableNumber<T, std::common_type_t<typename T::value_type, std::intmax_t>>) ||
(requires { typename T::element_type; } && CastableNumber<std::remove_reference_t<typename T::element_type>> &&
ScalableNumber<T, std::common_type_t<std::remove_reference_t<typename T::element_type>, std::intmax_t>>);
concept TensorRepresentation = std::regular<T> && Tensor<T> &&
requires(T a, T b, std::intmax_t i, long double f) {
{ a * i } -> Tensor;
{ i * a } -> Tensor;
{ a / i } -> Tensor;
{ a * f } -> Tensor;
{ f * a } -> Tensor;
{ a / f } -> Tensor;
{ a + b } -> Tensor;
{ a - b } -> Tensor;
{ tensor_product(a, b) } -> Tensor;
{ inner_product(a, b) } -> Tensor;
{ scalar_product(a, b) } -> Scalar;
};
// clang-format on

} // namespace detail

template<typename T>
concept Representation = (is_scalar<T> || is_vector<T> || is_tensor<T>)&&std::regular<T> && detail::Scalable<T>;
concept Representation =
detail::ScalarRepresentation<T> || detail::VectorRepresentation<T> || detail::TensorRepresentation<T>;

template<typename T, quantity_character Ch>
concept RepresentationOf = Representation<T> && ((Ch == quantity_character::scalar && is_scalar<T>) ||
(Ch == quantity_character::vector && is_vector<T>) ||
(Ch == quantity_character::tensor && is_tensor<T>));
concept RepresentationOf = detail::IsOfCharacter<T, Ch> && Representation<T>;

} // namespace mp_units
113 changes: 83 additions & 30 deletions src/core/include/mp-units/quantity.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,6 @@ concept QuantityConvertibleTo =
// deduced thus the function is evaluated here and may emit truncating conversion or other warnings)
requires(QFrom q) { detail::sudo_cast<QTo>(q); };

template<quantity_character Ch, typename Func, typename T, typename U>
concept InvokeResultOf = std::regular_invocable<Func, T, U> && RepresentationOf<std::invoke_result_t<Func, T, U>, Ch>;

template<typename Func, typename Q1, typename Q2>
concept InvocableQuantities = Quantity<Q1> && Quantity<Q2> &&
InvokeResultOf<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, Func,
typename Q1::rep, typename Q2::rep> &&
requires { common_reference(Q1::reference, Q2::reference); };

template<typename Func, Quantity Q1, Quantity Q2>
requires detail::InvocableQuantities<Func, Q1, Q2>
using common_quantity_for = quantity<common_reference(Q1::reference, Q2::reference),
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;

} // namespace detail

/**
Expand Down Expand Up @@ -341,46 +327,85 @@ explicit quantity(Q) -> quantity<quantity_like_traits<Q>::reference, typename qu

// binary operators on quantities
template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvocableQuantities<std::plus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
requires requires(Rep1 a, Rep2 b) {
common_reference(R1, R2);
{
a + b
} -> detail::IsOfCharacter<get_quantity_spec(R1).character>
}
[[nodiscard]] constexpr Quantity auto operator+(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
using ret = detail::common_quantity_for<std::plus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>;
using ret = quantity<common_reference(R1, R2), decltype(lhs.number() + rhs.number())>;
return make_quantity<ret::reference>(ret(lhs).number() + ret(rhs).number());
}

template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvocableQuantities<std::minus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
requires requires(Rep1 a, Rep2 b) {
common_reference(R1, R2);
{
a - b
} -> detail::IsOfCharacter<get_quantity_spec(R1).character>
}
[[nodiscard]] constexpr Quantity auto operator-(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
using ret = detail::common_quantity_for<std::minus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>;
using ret = quantity<common_reference(R1, R2), decltype(lhs.number() - rhs.number())>;
return make_quantity<ret::reference>(ret(lhs).number() - ret(rhs).number());
}

template<ReferenceOf<quantity_character::scalar> auto R1, typename Rep1,
ReferenceOf<quantity_character::scalar> auto R2, typename Rep2>
requires(!treat_as_floating_point<Rep1>) && (!treat_as_floating_point<Rep2>) && requires(Rep1 a, Rep2 b) {
common_reference(R1, R2);
{
a % b
} -> detail::IsOfCharacter<quantity_character::scalar>
}
[[nodiscard]] constexpr Quantity auto operator%(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<Rep1>::zero());
using ret = quantity<common_reference(R1, R2), decltype(lhs.number() % rhs.number())>;
return make_quantity<ret::reference>(ret(lhs).number() % ret(rhs).number());
}

template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvokeResultOf<(get_quantity_spec(R1) * get_quantity_spec(R2)).character, std::multiplies<>, Rep1,
Rep2>
requires(ReferenceOf<std::remove_const_t<decltype(R1)>, quantity_character::scalar> ||
ReferenceOf<std::remove_const_t<decltype(R2)>, quantity_character::scalar>) &&
requires(Rep1 a, Rep2 b) {
{
a* b
} -> detail::IsOfCharacter<(get_quantity_spec(R1) * get_quantity_spec(R2)).character>
}
[[nodiscard]] constexpr Quantity auto operator*(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
return make_quantity<R1 * R2>(lhs.number() * rhs.number());
}

template<auto R, typename Rep, typename Value>
requires(!Quantity<Value>) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, Rep, const Value&>
[[nodiscard]] constexpr Quantity auto operator*(const quantity<R, Rep>& q, const Value& v)
(ReferenceOf<std::remove_const_t<decltype(R)>, quantity_character::scalar> || detail::Scalar<Value>) &&
requires(Rep a, Value b) {
{
a* b
} -> detail::IsOfCharacter<(get_quantity_spec(R) * detail::get_character(b)).character>
}

detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, Rep,
const Value&> [[nodiscard]] constexpr Quantity auto
operator*(const quantity<R, Rep>& q, const Value& v)
{
return make_quantity<R>(q.number() * v);
}

template<typename Value, auto R, typename Rep>
requires(!Quantity<Value>) &&
(ReferenceOf<std::remove_const_t<decltype(R)>, quantity_character::scalar> || detail::Scalar<Value>) &&
detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, const Value&, Rep>
[[nodiscard]] constexpr Quantity auto operator*(const Value& v, const quantity<R, Rep>& q)
{
return make_quantity<R>(v * q.number());
}

template<auto R1, typename Rep1, auto R2, typename Rep2>
template<auto R1, typename Rep1, ReferenceOf<quantity_character::scalar> auto R2, typename Rep2>
requires detail::InvokeResultOf<(get_quantity_spec(R1) / get_quantity_spec(R2)).character, std::divides<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
Expand All @@ -405,14 +430,41 @@ template<typename Value, auto R, typename Rep>
return make_quantity<::mp_units::one / R>(v / q.number());
}

template<auto R1, typename Rep1, auto R2, typename Rep2>
requires(!treat_as_floating_point<Rep1>) && (!treat_as_floating_point<Rep2>) &&
detail::InvocableQuantities<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
[[nodiscard]] constexpr Quantity auto operator%(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
template<ReferenceOf<quantity_character::vector> auto R1, typename Rep1,
ReferenceOf<quantity_character::vector> auto R2, typename Rep2>
requires requires(Rep1 v1, Rep2 v2) {
{
dot_product(v1, v2)
} -> detail::Vector;
}
[[nodiscard]] QuantityOf<dot_product(get_quantity_spec(R1), get_quantity_spec(R2))> auto dot_product(
const quantity<R1, Rep1>& q1, const quantity<R2, Rep2>& q2)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<Rep1>::zero());
using ret = detail::common_quantity_for<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>;
return make_quantity<ret::reference>(ret(lhs).number() % ret(rhs).number());
return make_quantity<dot_product(R1, R2)>(dot_product(q1.number(), q2.number()));
}

template<ReferenceOf<quantity_character::vector> auto R1, typename Rep1,
ReferenceOf<quantity_character::vector> auto R2, typename Rep2>
requires requires(Rep1 v1, Rep2 v2) {
{
cross_product(v1, v2)
} -> detail::Scalar;
}
[[nodiscard]] QuantityOf<cross_product(get_quantity_spec(R1), get_quantity_spec(R2))> auto cross_product(
const quantity<R1, Rep1>& q1, const quantity<R2, Rep2>& q2)
{
return make_quantity<cross_product(R1, R2)>(cross_product(q1.number(), q2.number()));
}

template<ReferenceOf<quantity_character::vector> auto R, typename Rep>
requires requires(Rep v) {
{
norm(v)
} -> detail::Scalar;
}
[[nodiscard]] QuantityOf<norm(get_quantity_spec(R))> auto norm(const quantity<R, Rep>& q)
{
return make_quantity<norm(R)>(norm(q.number()));
}

template<auto R1, typename Rep1, auto R2, typename Rep2>
Expand Down Expand Up @@ -492,6 +544,7 @@ template<auto R, typename Rep, typename Value>
return q <=> make_quantity<::mp_units::one>(v);
}


// make_quantity
template<Reference auto R, typename Rep>
requires quantity<R, std::remove_cvref_t<Rep>>::_rep_safe_constructible_
Expand Down
35 changes: 31 additions & 4 deletions src/core/include/mp-units/quantity_spec.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,19 +275,20 @@ struct quantity_spec<Self, Eq, Args...> : detail::quantity_spec_interface<Self>
* errors. Having them of the same names improves user experience and somehow blurs those separate domains.
*
* @tparam Q quantity specification of a parent quantity
* @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar
* or `is_kind` in case the quantity starts a new hierarchy tree of a kind
* @tparam Args optionally `is_kind` in case the quantity starts a new hierarchy tree of a kind
*/
#ifdef __cpp_explicit_this_parameter
template<detail::NamedQuantitySpec auto QS, one_of<quantity_character, struct is_kind> auto... Args>
template<detail::NamedQuantitySpec auto QS, one_of<struct is_kind> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<QS, Args...> : std::remove_const_t<decltype(QS)> {
#else
// template<typename Self, detail::NamedQuantitySpec auto QS, one_of<struct is_kind> auto... Args>
template<typename Self, detail::NamedQuantitySpec auto QS, one_of<quantity_character, struct is_kind> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<Self, QS, Args...> : std::remove_const_t<decltype(QS)> {
#endif
static constexpr auto _parent_ = QS;
// static constexpr quantity_character character = QS.character;
static constexpr quantity_character character = detail::quantity_character_init<Args...>(QS.character);

#ifndef __cpp_explicit_this_parameter
Expand Down Expand Up @@ -488,6 +489,7 @@ template<QuantitySpec auto... From, QuantitySpec Q>
// Operators

[[nodiscard]] consteval QuantitySpec auto operator*(QuantitySpec auto lhs, QuantitySpec auto rhs)
requires(lhs.character == quantity_character::scalar || rhs.character == quantity_character::scalar)
{
return clone_kind_of<lhs, rhs>(
detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
Expand All @@ -496,13 +498,15 @@ template<QuantitySpec auto... From, QuantitySpec Q>

template<QuantitySpec Lhs, QuantitySpec Rhs>
[[nodiscard]] consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs)
requires(rhs.character == quantity_character::scalar)
{
return clone_kind_of<lhs, rhs>(
detail::expr_divide<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
remove_kind(lhs), remove_kind(rhs)));
}

[[nodiscard]] consteval QuantitySpec auto operator/(int value, QuantitySpec auto q)
requires(q.character == quantity_character::scalar)
{
gsl_Expects(value == 1);
return clone_kind_of<q>(detail::expr_invert<derived_quantity_spec, struct dimensionless>(q));
Expand All @@ -516,6 +520,28 @@ template<QuantitySpec Lhs, QuantitySpec Rhs>
return is_same_v<Lhs, Rhs>;
}

[[nodiscard]] consteval QuantitySpec auto dot_product(QuantitySpec auto lhs, QuantitySpec auto rhs)
requires(lhs.character == quantity_character::vector && rhs.character == quantity_character::vector)
{
return clone_kind_of<lhs, rhs>(
detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
remove_kind(lhs), remove_kind(rhs)));
}

[[nodiscard]] consteval QuantitySpec auto cross_product(QuantitySpec auto lhs, QuantitySpec auto rhs)
requires(lhs.character == quantity_character::vector && rhs.character == quantity_character::vector)
{
return clone_kind_of<lhs, rhs>(
detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
remove_kind(lhs), remove_kind(rhs)));
}

[[nodiscard]] consteval QuantitySpec auto norm(QuantitySpec auto q)
requires(q.character == quantity_character::vector)
{
return q;
}

template<detail::QuantityKindSpec Lhs, detail::QuantityKindSpec Rhs>
[[nodiscard]] consteval bool operator==(Lhs, Rhs)
{
Expand Down Expand Up @@ -1475,7 +1501,8 @@ template<QuantitySpec Q>

template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2)
requires(implicitly_convertible(get_kind(q1), get_kind(q2)) || implicitly_convertible(get_kind(q2), get_kind(q1)))
requires(Q1::character == Q2::character) &&
(implicitly_convertible(get_kind(q1), get_kind(q2)) || implicitly_convertible(get_kind(q2), get_kind(q1)))
{
using QQ1 = std::remove_const_t<decltype(remove_kind(q1))>;
using QQ2 = std::remove_const_t<decltype(remove_kind(q2))>;
Expand Down
Loading
Loading