diff --git a/src/core/include/mp_units/quantity.h b/src/core/include/mp_units/quantity.h index 72fe9e81..b340812f 100644 --- a/src/core/include/mp_units/quantity.h +++ b/src/core/include/mp_units/quantity.h @@ -43,9 +43,6 @@ namespace mp_units { namespace detail { -template -concept QuantityOne = QuantityOf; - template using quantity_like_type = quantity::reference, typename quantity_like_traits::rep>; @@ -53,21 +50,22 @@ template concept RepSafeConstructibleFrom = // exposition only std::constructible_from && (treat_as_floating_point || !treat_as_floating_point); -// QFrom ratio is an exact multiple of QTo -template +// UFrom ratio is an exact multiple of UTo +template concept Harmonic = // exposition only - Quantity && Quantity && - is_integral(get_canonical_unit(QFrom::unit).mag / get_canonical_unit(QTo::unit).mag); + Unit && Unit && + is_integral(get_canonical_unit(UFrom).mag / get_canonical_unit(UTo).mag); template concept QuantityConvertibleTo = // exposition only Quantity && Quantity && implicitly_convertible_to(QFrom::quantity_spec, QTo::quantity_spec) && - requires(QFrom q) { detail::sudo_cast(q); } && + convertible_to(QFrom::unit, QTo::unit) && requires(QFrom q) { detail::sudo_cast(q); } && (treat_as_floating_point || - (!treat_as_floating_point && Harmonic)); + (!treat_as_floating_point && Harmonic)); template -concept InvokeResultOf = std::regular_invocable && RepresentationOf, Ch>; +concept InvokeResultIsRepresentationOf = + std::regular_invocable && RepresentationOf, Ch>; } // namespace detail @@ -163,15 +161,13 @@ public: } -> std::common_with; } { - using ret = quantity; - return ret(+number()); + return +number() * reference; } [[nodiscard]] constexpr Quantity auto operator-() const requires std::regular_invocable, rep> { - using ret = quantity; - return ret(-number()); + return -number() * reference; } constexpr quantity& operator++() @@ -239,49 +235,49 @@ public: } template - constexpr quantity& operator*=(const Rep2& rhs) - requires requires(rep a, const Rep2 b) { + requires(!Quantity) && requires(rep a, const Rep2 b) { { a *= b } -> std::same_as; } + constexpr quantity& operator*=(const Rep2& rhs) { number_ *= rhs; return *this; } - template - constexpr quantity& operator*=(const Q& rhs) - requires requires(rep a, const typename Q::rep b) { + template Q> + requires(Q::unit == ::mp_units::one) && requires(rep a, const typename Q::rep b) { { a *= b } -> std::same_as; } + constexpr quantity& operator*=(const Q& rhs) { number_ *= rhs.number(); return *this; } template - constexpr quantity& operator/=(const Rep2& rhs) - requires requires(rep a, const Rep2 b) { + requires(!Quantity) && requires(rep a, const Rep2 b) { { a /= b } -> std::same_as; } + constexpr quantity& operator/=(const Rep2& rhs) { gsl_ExpectsAudit(rhs != quantity_values::zero()); number_ /= rhs; return *this; } - template - constexpr quantity& operator/=(const Q& rhs) - requires requires(rep a, const typename Q::rep b) { + template Q> + requires(Q::unit == ::mp_units::one) && requires(rep a, const typename Q::rep b) { { a /= b } -> std::same_as; } + constexpr quantity& operator/=(const Q& rhs) { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); number_ /= rhs.number(); @@ -289,26 +285,27 @@ public: } template + requires(!Quantity) && (!treat_as_floating_point) && (!treat_as_floating_point) && + requires(rep a, const Rep2 b) { + { + a %= b + } -> std::same_as; + } constexpr quantity& operator%=(const Rep2& rhs) - requires(!treat_as_floating_point) && (!treat_as_floating_point) && requires(rep a, const Rep2 b) { - { - a %= b - } -> std::same_as; - } { gsl_ExpectsAudit(rhs != quantity_values::zero()); number_ %= rhs; return *this; } - template - constexpr quantity& operator%=(const Q& rhs) - requires(!treat_as_floating_point) && (!treat_as_floating_point) && - requires(rep a, const typename Q::rep b) { + template Q> + requires(Q::unit == ::mp_units::one) && (!treat_as_floating_point) && + (!treat_as_floating_point) && requires(rep a, const typename Q::rep b) { { a %= b } -> std::same_as; } + constexpr quantity& operator%=(const Q& rhs) { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); number_ %= rhs.number(); @@ -329,106 +326,200 @@ public: // Hidden Friends // Below friend functions are to be found via argument-dependent lookup only - template - [[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const Value& rhs) - requires requires { // TODO: Simplify when Clang catches up. - requires !Quantity; - requires dimension == dimension_one; - requires detail::InvokeResultOf, rep, Value>; - } + template + requires requires { common_reference(reference, Q::reference); } && + detail::InvokeResultIsRepresentationOf, rep, typename Q::rep> + [[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const Q& rhs) { - return lhs + rhs * ::mp_units::one; + constexpr auto ref = common_reference(reference, Q::reference); + using ret = quantity; + return (ret(lhs).number() + ret(rhs).number()) * ref; + } + + template + requires requires { common_reference(reference, Q::reference); } && + detail::InvokeResultIsRepresentationOf, rep, typename Q::rep> + [[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const Q& rhs) + { + constexpr auto ref = common_reference(reference, Q::reference); + using ret = quantity; + return (ret(lhs).number() - ret(rhs).number()) * ref; + } + + template + requires detail::InvokeResultIsRepresentationOf<(quantity_spec * Q::quantity_spec).character, std::multiplies<>, + rep, typename Q::rep> + [[nodiscard]] friend constexpr Quantity auto operator*(const quantity& lhs, const Q& rhs) + { + return lhs.number() * rhs.number() * (reference * Q::reference); } template - [[nodiscard]] friend constexpr Quantity auto operator+(const Value& lhs, const quantity& rhs) - requires requires { // TODO: Simplify when Clang catches up. - requires !Quantity; - requires dimension == dimension_one; - requires detail::InvokeResultOf, Value, rep>; - } - { - return lhs * ::mp_units::one + rhs; - } - - template - [[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const Value& rhs) - requires requires { // TODO: Simplify when Clang catches up. - requires !Quantity; - requires dimension == dimension_one; - requires detail::InvokeResultOf, rep, Value>; - } - { - return lhs - rhs * ::mp_units::one; - } - - template - [[nodiscard]] friend constexpr Quantity auto operator-(const Value& lhs, const quantity& rhs) - requires requires { // TODO: Simplify when Clang catches up. - requires !Quantity; - requires dimension == dimension_one; - requires detail::InvokeResultOf, Value, rep>; - } - { - return lhs * ::mp_units::one - rhs; - } - - template - requires detail::InvokeResultOf, rep, const Value&> + requires(!Quantity) && + detail::InvokeResultIsRepresentationOf, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v) { - return q.number() * v * R; - } - - template - requires detail::InvokeResultOf, const Value&, rep> - [[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q) - { - return v * q.number() * R; + return q.number() * v * reference; } template - requires(!Quantity) && detail::InvokeResultOf, rep, const Value&> + requires(!Quantity) && + detail::InvokeResultIsRepresentationOf, const Value&, rep> + [[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q) + { + return v * q.number() * reference; + } + + template + requires detail::InvokeResultIsRepresentationOf<(quantity_spec / Q::quantity_spec).character, std::divides<>, rep, + typename Q::rep> + [[nodiscard]] friend constexpr Quantity auto operator/(const quantity& lhs, const Q& rhs) + { + gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); + return lhs.number() / rhs.number() * (reference / Q::reference); + } + + template + requires(!Quantity) && + detail::InvokeResultIsRepresentationOf, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v) { gsl_ExpectsAudit(v != quantity_values::zero()); - return q.number() / v * R; + return q.number() / v * reference; } template - requires(!Quantity) && detail::InvokeResultOf, const Value&, rep> + requires(!Quantity) && + detail::InvokeResultIsRepresentationOf, const Value&, rep> [[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q) { - return v / q.number() * (dimensionless[::mp_units::one] / reference); + return v / q.number() * (::mp_units::one / reference); } template requires(!Quantity) && (!treat_as_floating_point) && (!treat_as_floating_point) && - detail::InvokeResultOf, rep, const Value&> + detail::InvokeResultIsRepresentationOf, rep, Value> [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v) { gsl_ExpectsAudit(v != quantity_values::zero()); - return q.number() % v * R; + return (q.number() % v) * reference; } [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs) - requires(!treat_as_floating_point) && detail::InvokeResultOf, rep, rep> + requires(!treat_as_floating_point) && + detail::InvokeResultIsRepresentationOf, rep, rep> { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); - return lhs.number() % rhs.number() * R; + return (lhs.number() % rhs.number()) * reference; } - [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs) - requires std::three_way_comparable -#if __cpp_impl_three_way_comparison - = default; -#else + template + requires(!treat_as_floating_point) && (!treat_as_floating_point) && + requires { + common_reference(reference, Q::reference); + } && detail::InvokeResultIsRepresentationOf, rep, typename Q::rep> + [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const Q& rhs) { - return lhs.number() <=> rhs.number(); + gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); + constexpr auto ref = common_reference(reference, Q::reference); + using ret = quantity; + return (ret(lhs).number() % ret(rhs).number()) * ref; } -#endif - [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) = default; + template + requires(!treat_as_floating_point) && (!treat_as_floating_point) && + (Q::dimension == dimension_one) && (Q::unit == ::mp_units::one) && + detail::InvokeResultIsRepresentationOf, rep, typename Q::rep> + [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const Q& rhs) + { + gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); + return (lhs.number() % rhs.number()) * reference; + } + + template + requires requires { common_reference(reference, Q::reference); } && + std::equality_comparable_with + [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const Q& rhs) + { + using ct = std::common_type_t; + return ct(lhs).number() == ct(rhs).number(); + } + + template + requires requires { common_reference(reference, Q::reference); } && + std::three_way_comparable_with + [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const Q& rhs) + { + using ct = std::common_type_t; + return ct(lhs).number() <=> ct(rhs).number(); + } + + + // dimensionless quantities support for interacting with raw values + template + requires(dimension == dimension_one) && + (unit == ::mp_units::one) && detail::RepSafeConstructibleFrom> + constexpr explicit(false) quantity(Value&& v) : number_(std::forward(v)) + { + } + + template + requires(!Quantity) && (dimension == dimension_one) && + detail::InvokeResultIsRepresentationOf, rep, Value> + [[nodiscard]] friend constexpr Quantity auto operator+(const quantity& q, const Value& v) + { + return q + v * ::mp_units::one; + } + + template + requires(!Quantity) && (dimension == dimension_one) && + detail::InvokeResultIsRepresentationOf, Value, rep> + [[nodiscard]] friend constexpr Quantity auto operator+(const Value& v, const quantity& q) + { + return v * ::mp_units::one + q; + } + + template + requires(!Quantity) && (dimension == dimension_one) && + detail::InvokeResultIsRepresentationOf, rep, Value> + [[nodiscard]] friend constexpr Quantity auto operator-(const quantity& q, const Value& v) + { + return q - v * ::mp_units::one; + } + + template + requires(!Quantity) && (dimension == dimension_one) && + detail::InvokeResultIsRepresentationOf, Value, rep> + [[nodiscard]] friend constexpr Quantity auto operator-(const Value& v, const quantity& q) + { + return v * ::mp_units::one - q; + } + + template + requires(!Quantity) && (dimension == dimension_one) && (unit == ::mp_units::one) && + (!treat_as_floating_point) && (!treat_as_floating_point) && + detail::InvokeResultIsRepresentationOf, Value, rep> + [[nodiscard]] friend constexpr Quantity auto operator%(const Value& v, const quantity& q) + { + gsl_ExpectsAudit(q.number() != quantity_values::zero()); + return (v % q.number()) * reference; + } + + template + requires(!Quantity) && (dimension == dimension_one) && std::equality_comparable_with + [[nodiscard]] friend constexpr bool operator==(const quantity& q, const Value& v) + { + return q == v * ::mp_units::one; + } + + template + requires(!Quantity) && (dimension == dimension_one) && std::three_way_comparable_with + [[nodiscard]] friend constexpr auto operator<=>(const quantity& q, const Value& v) + { + return q <=> v * ::mp_units::one; + } private: template @@ -436,100 +527,16 @@ private: friend constexpr quantity> operator*(Rep2&& lhs, R2); template - requires detail::RepSafeConstructibleFrom + requires detail::RepSafeConstructibleFrom> constexpr explicit quantity(Value&& v) : number_(std::forward(v)) { } }; // CTAD -template -explicit(false) quantity(quantity) -> quantity; - -template Rep> -explicit(false) quantity(Rep) -> quantity; - -#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 16 -template Rep> -explicit(false) quantity(Rep&&) -> quantity; -#endif - template explicit quantity(Q) -> quantity::reference, typename quantity_like_traits::rep>; -// non-member binary operators -template - requires(implicitly_convertible_to(get_kind(Q1::quantity_spec), get_kind(Q2::quantity_spec)) || - implicitly_convertible_to(get_kind(Q2::quantity_spec), get_kind(Q1::quantity_spec))) && - detail::InvokeResultOf, - typename Q1::rep, typename Q2::rep> -[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs) -{ - constexpr auto ref = common_reference(Q1::reference, Q2::reference); - using ret = quantity; - return (ret(lhs).number() + ret(rhs).number()) * ref; -} - -template - requires(implicitly_convertible_to(get_kind(Q1::quantity_spec), get_kind(Q2::quantity_spec)) || - implicitly_convertible_to(get_kind(Q2::quantity_spec), get_kind(Q1::quantity_spec))) && - detail::InvokeResultOf, - typename Q1::rep, typename Q2::rep> -[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs) -{ - constexpr auto ref = common_reference(Q1::reference, Q2::reference); - using ret = quantity; - return (ret(lhs).number() - ret(rhs).number()) * ref; -} - -template - requires detail::InvokeResultOf<(Q1::quantity_spec * Q2::quantity_spec).character, std::multiplies<>, - typename Q1::rep, typename Q2::rep> -[[nodiscard]] constexpr Quantity auto operator*(const Q1& lhs, const Q2& rhs) -{ - return lhs.number() * rhs.number() * (Q1::reference * Q2::reference); -} - -template - requires detail::InvokeResultOf<(Q1::quantity_spec / Q2::quantity_spec).character, std::divides<>, typename Q1::rep, - typename Q2::rep> -[[nodiscard]] constexpr Quantity auto operator/(const Q1& lhs, const Q2& rhs) -{ - gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); - return lhs.number() / rhs.number() * (Q1::reference / Q2::reference); -} - -template - requires(!treat_as_floating_point) && (!treat_as_floating_point) && - (interconvertible(Q1::reference, Q2::reference) || QuantityOf) && - detail::InvokeResultOf, typename Q1::rep, typename Q2::rep> -[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs) -{ - gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); - using ret = quantity, typename Q1::rep, typename Q2::rep>>; - return ret(lhs.number() % rhs.number()); -} - -template - requires(implicitly_convertible_to(get_kind(Q1::quantity_spec), get_kind(Q2::quantity_spec)) || - implicitly_convertible_to(get_kind(Q2::quantity_spec), get_kind(Q1::quantity_spec))) && - std::three_way_comparable_with -[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs) -{ - constexpr auto ref = common_reference(Q1::reference, Q2::reference); - return quantity(lhs).number() <=> quantity(rhs).number(); -} - -template - requires(implicitly_convertible_to(get_kind(Q1::quantity_spec), get_kind(Q2::quantity_spec)) || - implicitly_convertible_to(get_kind(Q2::quantity_spec), get_kind(Q1::quantity_spec))) && - std::equality_comparable_with -[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs) -{ - constexpr auto ref = common_reference(Q1::reference, Q2::reference); - return quantity(lhs).number() == quantity(rhs).number(); -} - } // namespace mp_units namespace std { @@ -547,4 +554,19 @@ public: common_type_t>; }; +// dimensionless quantities support for interacting with raw values +template + requires(!mp_units::Quantity) && (Q::dimension == mp_units::dimension_one) && (Q::unit == mp_units::one) && + requires { typename common_type_t; } +struct common_type { +public: + using type = mp_units::quantity>; +}; + +template + requires(!mp_units::Quantity) && (Q::dimension == mp_units::dimension_one) && (Q::unit == mp_units::one) && + requires { typename common_type_t; } +struct common_type : common_type { +}; + } // namespace std diff --git a/src/core/include/mp_units/quantity_point.h b/src/core/include/mp_units/quantity_point.h index 080199bc..ae4a0705 100644 --- a/src/core/include/mp_units/quantity_point.h +++ b/src/core/include/mp_units/quantity_point.h @@ -224,9 +224,6 @@ public: }; // CTAD -template -explicit quantity_point(Rep) -> quantity_point{}, Rep>; - template explicit quantity_point(Q) -> quantity_point{}, typename Q::rep>; diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index aabf8647..434da241 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -22,16 +22,16 @@ cmake_minimum_required(VERSION 3.2) -# add_library(unit_tests_static_truncating quantity_test.cpp) +add_library(unit_tests_static_truncating quantity_test.cpp) # if(NOT ${projectPrefix}LIBCXX) # target_sources(unit_tests_static_truncating PRIVATE quantity_kind_test.cpp quantity_point_kind_test.cpp) # endif() -# target_link_libraries(unit_tests_static_truncating PRIVATE mp-units::mp-units) -# target_compile_options( -# unit_tests_static_truncating PRIVATE $,/wd4242 /wd4244,-Wno-conversion> -# ) +target_link_libraries(unit_tests_static_truncating PRIVATE mp-units::mp-units) +target_compile_options( +unit_tests_static_truncating PRIVATE $,/wd4242 /wd4244,-Wno-conversion> +) add_library( unit_tests_static angle_test.cpp @@ -72,5 +72,4 @@ add_library( # target_sources(unit_tests_static PRIVATE custom_rep_test_min_impl.cpp quantity_point_test.cpp) # endif() target_link_libraries(unit_tests_static PRIVATE mp-units::mp-units) - -# target_link_libraries(unit_tests_static PRIVATE unit_tests_static_truncating mp-units::mp-units) +target_link_libraries(unit_tests_static PRIVATE unit_tests_static_truncating mp-units::mp-units) diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index f510e2b4..89939539 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -21,235 +21,248 @@ // SOFTWARE. #include "test_tools.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include #include namespace { -using namespace units; -namespace si = isq::si; -using namespace si; -using namespace references; - -constexpr auto cgs_cm = cgs::references::cm; - +using namespace mp_units; +using namespace mp_units::si::unit_symbols; ////////////////////////////// // quantity class invariants ////////////////////////////// -static_assert(sizeof(length) == sizeof(double)); -static_assert(sizeof(length) == sizeof(short)); +static_assert(sizeof(quantity) == sizeof(double)); +static_assert(sizeof(quantity) == sizeof(double)); +static_assert(sizeof(quantity) == sizeof(short)); +static_assert(sizeof(quantity) == sizeof(short)); #if UNITS_COMP_GCC != 10 || UNITS_COMP_GCC_MINOR > 2 -template typename Q> +template typename Q> concept invalid_types = requires { - requires !requires { typename Q; }; // unit of a different dimension - requires !requires { typename Q>; }; // quantity used as Rep - requires !requires { typename Q; }; // reordered arguments - requires !requires { typename Q; }; // reordered arguments - }; + requires !requires { typename Q; }; // dimension instead of reference + requires !requires { typename Q; }; // quantity_spec instead of reference + requires !requires { typename Q; }; // bool is not a valid representation type + requires !requires { typename Q>; }; // quantity used as Rep + requires !requires { typename Q; }; // vector representation expected +}; static_assert(invalid_types); #endif -static_assert(std::is_trivially_default_constructible_v>); -static_assert(std::is_trivially_copy_constructible_v>); -static_assert(std::is_trivially_move_constructible_v>); -static_assert(std::is_trivially_copy_assignable_v>); -static_assert(std::is_trivially_move_assignable_v>); -static_assert(std::is_trivially_destructible_v>); +static_assert(std::is_trivially_default_constructible_v>); +static_assert(std::is_trivially_copy_constructible_v>); +static_assert(std::is_trivially_move_constructible_v>); +static_assert(std::is_trivially_copy_assignable_v>); +static_assert(std::is_trivially_move_assignable_v>); +static_assert(std::is_trivially_destructible_v>); -static_assert(std::is_nothrow_default_constructible_v>); -static_assert(std::is_nothrow_copy_constructible_v>); -static_assert(std::is_nothrow_move_constructible_v>); -static_assert(std::is_nothrow_copy_assignable_v>); -static_assert(std::is_nothrow_move_assignable_v>); -static_assert(std::is_nothrow_destructible_v>); +static_assert(std::is_nothrow_default_constructible_v>); +static_assert(std::is_nothrow_copy_constructible_v>); +static_assert(std::is_nothrow_move_constructible_v>); +static_assert(std::is_nothrow_copy_assignable_v>); +static_assert(std::is_nothrow_move_assignable_v>); +static_assert(std::is_nothrow_destructible_v>); -static_assert(std::is_trivially_copyable_v>); -static_assert(std::is_standard_layout_v>); +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_standard_layout_v>); -static_assert(std::default_initializable>); -static_assert(std::move_constructible>); -static_assert(std::copy_constructible>); -static_assert(std::equality_comparable>); -static_assert(std::totally_ordered>); -static_assert(std::regular>); +static_assert(std::default_initializable>); +static_assert(std::move_constructible>); +static_assert(std::copy_constructible>); +static_assert(std::equality_comparable>); +static_assert(std::totally_ordered>); +static_assert(std::regular>); -static_assert(std::three_way_comparable>); +static_assert(std::three_way_comparable>); + + +////////////////// +// member values +////////////////// + +static_assert(quantity::reference == isq::length[m]); +static_assert(quantity::quantity_spec == isq::length); +static_assert(quantity::dimension == isq::dim_length); +static_assert(quantity::unit == si::metre); ////////////////// // member types ////////////////// -static_assert(is_same_v::dimension, dim_length>); -static_assert(is_same_v::dimension, fps::dim_length>); -static_assert(is_same_v::unit, metre>); -static_assert(is_same_v::unit, fps::mile>); -static_assert(is_same_v::rep, int>); -static_assert(is_same_v::rep, double>); +static_assert(is_same_v::rep, double>); +static_assert(is_same_v::rep, int>); //////////////////////////// // static member functions //////////////////////////// -static_assert(length::zero().number() == 0); -static_assert(length::min().number() == std::numeric_limits::lowest()); -static_assert(length::max().number() == std::numeric_limits::max()); -static_assert(length::zero().number() == 0.0); -static_assert(length::min().number() == std::numeric_limits::lowest()); -static_assert(length::max().number() == std::numeric_limits::max()); +static_assert(quantity::zero().number() == 0); +static_assert(quantity::min().number() == std::numeric_limits::lowest()); +static_assert(quantity::max().number() == std::numeric_limits::max()); +static_assert(quantity::zero().number() == 0.0); +static_assert(quantity::min().number() == std::numeric_limits::lowest()); +static_assert(quantity::max().number() == std::numeric_limits::max()); ////////////////////////////// // construction from a value ////////////////////////////// -// only explicit construction from a value -static_assert(std::constructible_from, double>); -static_assert(!std::convertible_to>); +// construction from a value is private +static_assert(!std::constructible_from, double>); +static_assert(!std::convertible_to>); -static_assert(std::constructible_from, float>); -static_assert(!std::convertible_to>); - -static_assert( - std::constructible_from, double>); // truncating implicit conversions double -> float allowed -static_assert(!std::convertible_to>); - -static_assert(std::constructible_from, int>); -static_assert(!std::convertible_to>); - -static_assert(std::constructible_from, short>); -static_assert(!std::convertible_to>); - -static_assert( - std::constructible_from, int>); // truncating implicit conversions int -> short allowed -static_assert(!std::convertible_to>); +static_assert(!std::constructible_from, int>); +static_assert(!std::convertible_to>); // exception, implicit construction from a value allowed for a dimensionless quantity -static_assert(std::constructible_from, double>); -static_assert(std::convertible_to>); +static_assert(std::constructible_from, double>); +static_assert(std::convertible_to>); -static_assert(std::constructible_from, float>); -static_assert(std::convertible_to>); +static_assert(std::constructible_from, float>); +static_assert(std::convertible_to>); -static_assert(std::constructible_from, double>); -static_assert(std::convertible_to>); +static_assert(std::constructible_from, double>); +static_assert(std::convertible_to>); -static_assert(std::constructible_from, int>); -static_assert(std::convertible_to>); +static_assert(std::constructible_from, int>); +static_assert(std::convertible_to>); -static_assert(std::constructible_from, short>); -static_assert(std::convertible_to>); +static_assert(std::constructible_from, short>); +static_assert(std::convertible_to>); -static_assert(std::constructible_from, int>); -static_assert(std::convertible_to>); +static_assert(std::constructible_from, int>); +static_assert(std::convertible_to>); // but only if a dimensionless quantity has a ratio(1) -static_assert(std::constructible_from, double>); -static_assert(!std::convertible_to>); +static_assert(!std::constructible_from, double>); +static_assert(!std::convertible_to>); -static_assert(std::constructible_from, float>); -static_assert(!std::convertible_to>); +static_assert(!std::constructible_from, float>); +static_assert(!std::convertible_to>); -static_assert(std::constructible_from, - double>); // truncating implicit conversions double -> float allowed -static_assert(!std::convertible_to>); +static_assert(!std::constructible_from, double>); +static_assert(!std::convertible_to>); -static_assert(std::constructible_from, int>); -static_assert(!std::convertible_to>); +static_assert(!std::constructible_from, int>); +static_assert(!std::convertible_to>); -static_assert(std::constructible_from, short>); -static_assert(!std::convertible_to>); +static_assert(!std::constructible_from, short>); +static_assert(!std::convertible_to>); -static_assert( - std::constructible_from, int>); // truncating implicit conversions int -> short allowed -static_assert(!std::convertible_to>); - -// floating-point to integral truncating conversion not allowed -static_assert(!std::constructible_from, double>); -static_assert(!std::convertible_to>); - -static_assert(!std::constructible_from, double>); -static_assert(!std::convertible_to>); - -static_assert(length().number() == 0); // value initialization -static_assert(length(1).number() == 1); -static_assert(length(1.0).number() == 1.0); -static_assert(length(1).number() == 1.0); -static_assert(length(3.14).number() == 3.14); +static_assert(!std::constructible_from, + int>); // truncating implicit conversions int -> short allowed +static_assert(!std::convertible_to>); /////////////////////////////////////// // construction from another quantity /////////////////////////////////////// -// conversion only between equivalent dimensions -static_assert(std::constructible_from, length>); -static_assert(std::convertible_to, length>); -static_assert(std::constructible_from, cgs::length>); -static_assert(std::convertible_to, length>); -static_assert(std::constructible_from, cgs::length>); -static_assert(std::convertible_to, fps::length>); +// conversion only between convertible quantities +static_assert(std::constructible_from, quantity>); +static_assert(std::convertible_to, quantity>); +static_assert(std::constructible_from, quantity>); +static_assert(std::convertible_to, quantity>); +static_assert(std::constructible_from, quantity>); +static_assert(std::convertible_to, quantity>); -// conversion between different dimensions not allowed -static_assert(!std::constructible_from, isq::si::time>); -static_assert(!std::convertible_to, length>); -static_assert(!std::constructible_from, speed>); -static_assert(!std::convertible_to, length>); +// conversion between different quantities not allowed +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity>); +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity>); // implicit conversion from another quantity only if non-truncating -static_assert(std::constructible_from, length>); // int -> double OK -static_assert(std::convertible_to, length>); // int -> double OK +static_assert(std::constructible_from, quantity>); // int -> double OK +static_assert(std::convertible_to, quantity>); // int -> double OK -static_assert(!std::constructible_from, length>); // truncating double -> int not allowed -static_assert(!std::convertible_to, length>); // truncating double -> int not allowed +static_assert(!std::constructible_from, + quantity>); // truncating double -> int not allowed +static_assert(!std::convertible_to, + quantity>); // truncating double -> int not allowed -static_assert(std::constructible_from, length>); // kilometre -> metre OK -static_assert(std::convertible_to, length>); // kilometre -> metre OK +static_assert(std::constructible_from, + quantity>); // kilometre -> metre OK +static_assert(std::convertible_to, + quantity>); // kilometre -> metre OK -static_assert(!std::constructible_from, - length>); // truncating metre -> kilometre not allowed -static_assert(!std::convertible_to, - length>); // truncating metre -> kilometre not allowed +static_assert(!std::constructible_from, + quantity>); // truncating metre -> + // kilometre not allowed +static_assert( + !std::convertible_to, + quantity>); // truncating metre -> kilometre not allowed // converting to double always OK -static_assert(std::constructible_from, length>); -static_assert(std::convertible_to, length>); -static_assert(std::constructible_from, length>); -static_assert(std::convertible_to, length>); +static_assert(std::constructible_from, quantity>); +static_assert(std::convertible_to, quantity>); +static_assert(std::constructible_from, quantity>); +static_assert(std::convertible_to, quantity>); + + +/////////////////////// +// obtaining a number +/////////////////////// + +static_assert(quantity(123 * m).number() == 123); +static_assert(quantity(2 * km).number() == 2000); +static_assert(quantity(2 * km).number() == 2); +static_assert(quantity(1500 * m).number() == 1.5); + + +/////////////////////////////////// +// converting to a different unit +/////////////////////////////////// + +static_assert(quantity(2. * km)[km].number() == 2.); +static_assert(quantity(2. * km)[m].number() == 2000.); +static_assert(quantity(2000. * m)[km].number() == 2.); +static_assert(quantity(2 * km)[km].number() == 2); +static_assert(quantity(2 * km)[m].number() == 2000); + +#if UNITS_COMP_GCC != 10 || UNITS_COMP_GCC_MINOR > 2 +template typename Q> +concept invalid_unit_conversion = requires { + requires !requires { Q(2000 * m)[km]; }; // truncating conversion + requires !requires { Q(2 * m)[s]; }; // invalid unit +}; +static_assert(invalid_unit_conversion); +#endif + +static_assert(quantity(2. * km).number_in(km) == 2.); +static_assert(quantity(2. * km).number_in(m) == 2000.); +static_assert(quantity(2000. * m).number_in(km) == 2.); +static_assert(quantity(2 * km).number_in(km) == 2); +static_assert(quantity(2 * km).number_in(m) == 2000); + +#if UNITS_COMP_GCC != 10 || UNITS_COMP_GCC_MINOR > 2 +template typename Q> +concept invalid_getter_with_unit_conversion = requires { + requires !requires { Q(2000 * m).number_in(km); }; // truncating conversion + requires !requires { Q(2 * m).number_in(s); }; // invalid unit +}; +static_assert(invalid_getter_with_unit_conversion); +#endif -static_assert(length(123_q_m).number() == 123); -static_assert(length(2_q_km).number() == 2); -static_assert(length(2_q_km).number() == 2000); -static_assert(length(1500_q_m).number() == 1.5); /////////////////////////////////////// // derived quantities /////////////////////////////////////// template -struct derived_quantity : quantity { - using dimension = typename Q::dimension; - using unit = typename Q::unit; +struct derived_quantity : quantity { + static constexpr auto reference = Q::reference; + static constexpr auto quantity_spec = Q::quantity_spec; + static constexpr auto dimension = Q::dimension; + static constexpr auto unit = Q::unit; using rep = Rep; - using R = quantity; + using R = quantity; derived_quantity() = default; constexpr explicit(!std::is_trivial_v) derived_quantity(const R& t) : R(t) {} @@ -272,47 +285,54 @@ struct derived_quantity : quantity constexpr operator const R&&() const&& noexcept { return *this; } }; -static_assert(detail::is_quantity, "NTTP type description">>); -constexpr isq::Length auto get_length_derived_quantity() noexcept +static_assert(Quantity, "NTTP type description">>); + +constexpr QuantityOf auto get_length_derived_quantity() noexcept { - derived_quantity, "NTTP type description"> dist{}; - dist += 1_q_m; - dist = dist + 1_q_m; + derived_quantity, "NTTP type description"> dist{}; + dist += 1 * m; + dist = dist + 1 * m; dist *= 0.5; return dist; } -static_assert(get_length_derived_quantity() == 1_q_m); +static_assert(get_length_derived_quantity() == 1 * m); + ///////// // CTAD ///////// -#if UNITS_COMP_GCC >= 11 || UNITS_COMP_CLANG > 16 -static_assert(std::is_same_v); -static_assert(std::is_same_v); -static_assert( - std::is_same_v); -#endif +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(quantity{123. * m}.unit == si::metre); +static_assert(quantity{123. * m}.quantity_spec == kind_of); +static_assert(quantity{123. * h}.unit == si::hour); +static_assert(quantity{123. * h}.quantity_spec == kind_of); -static_assert(is_same_v(123)}), length>); -static_assert(is_same_v(123)}), speed>); -// static_assert(is_same_v(123)}), length>); // TODO gcc ICE -static_assert(is_same_v>); -static_assert(is_same_v>); -static_assert(is_same_v>); +using namespace std::chrono_literals; +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(quantity{24h}.unit == si::hour); +static_assert(quantity{24h}.quantity_spec == kind_of); //////////////////////// // assignment operator //////////////////////// -static_assert([]() { - length l1(1), l2(2); +static_assert([] { + auto l1(1 * m), l2(2 * m); + return l2 = l1; +}() + .number() == 1); +static_assert([] { + const auto l1(1 * m); + auto l2(2 * m); return l2 = l1; }() .number() == 1); static_assert([]() { - length l1(1), l2(2); + auto l1(1 * m), l2(2 * m); return l2 = std::move(l1); }() .number() == 1); @@ -322,27 +342,27 @@ static_assert([]() { // unary operators //////////////////// -static_assert((+123_q_m).number() == 123); -static_assert((-123_q_m).number() == -123); -static_assert((+(-123_q_m)).number() == -123); -static_assert((-(-123_q_m)).number() == 123); +static_assert((+123 * m).number() == 123); +static_assert((-123 * m).number() == -123); +static_assert((+(-123 * m)).number() == -123); +static_assert((-(-123 * m)).number() == 123); static_assert([](auto v) { auto vv = v++; return std::pair(v, vv); -}(123_q_m) == std::pair(124_q_m, 123_q_m)); +}(123 * m) == std::pair(124 * m, 123 * m)); static_assert([](auto v) { auto vv = ++v; return std::pair(v, vv); -}(123_q_m) == std::pair(124_q_m, 124_q_m)); +}(123 * m) == std::pair(124 * m, 124 * m)); static_assert([](auto v) { auto vv = v--; return std::pair(v, vv); -}(123_q_m) == std::pair(122_q_m, 123_q_m)); +}(123 * m) == std::pair(122 * m, 123 * m)); static_assert([](auto v) { auto vv = --v; return std::pair(v, vv); -}(123_q_m) == std::pair(122_q_m, 122_q_m)); +}(123 * m) == std::pair(122 * m, 122 * m)); static_assert(is_same_v); @@ -352,32 +372,32 @@ static_assert(is_same_v); //////////////////////// // same type -static_assert((1_q_m += 1_q_m).number() == 2); -static_assert((2_q_m -= 1_q_m).number() == 1); -static_assert((1_q_m *= 2).number() == 2); -static_assert((2_q_m /= 2).number() == 1); -static_assert((7_q_m %= 2).number() == 1); -static_assert((1_q_m *= quantity(2)).number() == 2); -static_assert((2_q_m /= quantity(2)).number() == 1); -static_assert((7_q_m %= quantity(2)).number() == 1); -static_assert((7_q_m %= 2_q_m).number() == 1); +static_assert((1 * m += 1 * m).number() == 2); +static_assert((2 * m -= 1 * m).number() == 1); +static_assert((1 * m *= 2).number() == 2); +static_assert((2 * m /= 2).number() == 1); +static_assert((7 * m %= 2).number() == 1); +static_assert((1 * m *= 2 * one).number() == 2); +static_assert((2 * m /= 2 * one).number() == 1); +static_assert((7 * m %= 2 * one).number() == 1); +static_assert((7 * m %= 2 * m).number() == 1); // different types -static_assert((2.5_q_m += 3_q_m).number() == 5.5); -static_assert((123_q_m += 1_q_km).number() == 1123); -static_assert((5.5_q_m -= 3_q_m).number() == 2.5); -static_assert((1123_q_m -= 1_q_km).number() == 123); -static_assert((2.5_q_m *= 3).number() == 7.5); -static_assert((7.5_q_m /= 3).number() == 2.5); -static_assert((2.5_q_m *= quantity(3)).number() == 7.5); -static_assert((7.5_q_m /= quantity(3)).number() == 2.5); -static_assert((3500_q_m %= 1_q_km).number() == 500); +static_assert((2.5 * m += 3 * m).number() == 5.5); +static_assert((123 * m += 1 * km).number() == 1123); +static_assert((5.5 * m -= 3 * m).number() == 2.5); +static_assert((1123 * m -= 1 * km).number() == 123); +static_assert((2.5 * m *= 3).number() == 7.5); +static_assert((7.5 * m /= 3).number() == 2.5); +static_assert((2.5 * m *= 3 * one).number() == 7.5); +static_assert((7.5 * m /= 3 * one).number() == 2.5); +static_assert((3500 * m %= 1 * km).number() == 500); static_assert((std::uint8_t(255) * m %= 256).number() == [] { std::uint8_t ui(255); return ui %= 256; }()); -static_assert((std::uint8_t(255) * m %= quantity(256)).number() == [] { +static_assert((std::uint8_t(255) * m %= 256 * one).number() == [] { std::uint8_t ui(255); return ui %= 256; }()); @@ -387,7 +407,7 @@ static_assert((std::uint8_t(255) * m %= 257).number() == [] { std::uint8_t ui(255); return ui %= 257; }()); -static_assert((std::uint8_t(255) * m %= quantity(257)).number() == [] { +static_assert((std::uint8_t(255) * m %= 257 * one).number() == [] { std::uint8_t ui(255); return ui %= 257; }()); @@ -397,109 +417,124 @@ static_assert((std::uint8_t(255) * m %= 257 * m).number() != [] { return ui %= 257; }()); -#ifndef UNITS_COMP_MSVC // TODO ICE - // (https://developercommunity2.visualstudio.com/t/ICE-on-a-constexpr-operator-in-mp-unit/1302907) +// TODO ICE +// (https://developercommunity2.visualstudio.com/t/ICE-on-a-constexpr-operator-in-mp-unit/1302907) +#ifndef UNITS_COMP_MSVC // next two lines trigger conversions warnings // (warning disabled in CMake for this file) -static_assert((22_q_m *= 33.33).number() == 733); -static_assert((22_q_m /= 3.33).number() == 6); -static_assert((22_q_m *= quantity(33.33)).number() == 733); -static_assert((22_q_m /= quantity(3.33)).number() == 6); +static_assert((22 * m *= 33.33).number() == 733); +static_assert((22 * m /= 3.33).number() == 6); +static_assert((22 * m *= 33.33 * one).number() == 733); +static_assert((22 * m /= 3.33 * one).number() == 6); #endif -template -concept invalid_compound_assignments = - requires() { - // truncating not allowed - requires !requires(length l) { l += 2.5_q_m; }; - requires !requires(length l) { l -= 2.5_q_m; }; - requires !requires(length l) { l += length(2); }; - requires !requires(length l) { l -= length(2); }; - requires !requires(length l) { l %= length(2); }; - requires !requires(length l) { l %= dimensionless(2); }; - requires !requires(length l) { l %= dimensionless(2); }; +template typename Q> +concept invalid_compound_assignments = requires() { + // truncating not allowed + requires !requires(Q l) { l += 2.5 * m; }; + requires !requires(Q l) { l -= 2.5 * m; }; + requires !requires(Q l) { l += 2 * isq::length[m]; }; + requires !requires(Q l) { l -= 2 * isq::length[m]; }; + requires !requires(Q l) { l %= 2 * isq::length[m]; }; + requires !requires(Q l) { l %= 2 * percent; }; + requires !requires(Q l) { l %= 2. * percent; }; - // TODO: accept non-truncating argument - requires !requires(length l) { l *= 1 * (km / m); }; - requires !requires(length l) { l /= 1 * (km / m); }; - requires !requires(length l) { l %= 1 * (km / m); }; + // TODO: accept non-truncating argument + requires !requires(Q l) { l *= 1 * (km / m); }; + requires !requires(Q l) { l /= 1 * (km / m); }; + requires !requires(Q l) { l %= 1 * (km / m); }; - // only quantities can be added or subtracted - requires !requires(length l) { l += 2; }; - requires !requires(length l) { l -= 2; }; + // only quantities can be added or subtracted + requires !requires(Q l) { l += 2; }; + requires !requires(Q l) { l -= 2; }; - // compound multiply/divide by another quantity not allowed - requires !requires(length l) { l *= 2_q_m; }; - requires !requires(length l) { l /= 2_q_m; }; + // compound multiply/divide by another quantity not allowed + requires !requires(Q l) { l *= 2 * m; }; + requires !requires(Q l) { l /= 2 * m; }; - // modulo operations on a floating point representation not allowed - requires !requires(length l) { l %= 2.; }; - requires !requires(length l) { l %= 2; }; - requires !requires(length l) { l %= 2._q_m; }; - requires !requires(length l) { l %= 2_q_m; }; - requires !requires(length l) { l %= 2._q_m; }; + // modulo operations on a floating point representation not allowed + requires !requires(Q l) { l %= 2.; }; + requires !requires(Q l) { l %= 2; }; + requires !requires(Q l) { l %= 2. * m; }; + requires !requires(Q l) { l %= 2 * m; }; + requires !requires(Q l) { l %= 2. * m; }; - // no unit constants - requires !requires(length l) { l += m; }; - requires !requires(length l) { l -= m; }; - requires !requires(length l) { l *= m; }; - requires !requires(length l) { l /= m; }; - requires !requires(length l) { l %= m; }; - }; -static_assert(invalid_compound_assignments); + // no unit constants + requires !requires(Q l) { l += m; }; + requires !requires(Q l) { l -= m; }; + requires !requires(Q l) { l *= m; }; + requires !requires(Q l) { l /= m; }; + requires !requires(Q l) { l %= m; }; +}; +static_assert(invalid_compound_assignments); //////////////////// // binary operators //////////////////// -template +template typename Q> concept invalid_binary_operations = requires { - // no crossdimensional addition and subtraction - requires !requires { 1_q_s + length(1); }; - requires !requires { 1_q_s - length(1); }; + // no crossdimensional addition and subtraction + requires !requires { 1 * s + Q(1 * m); }; + requires !requires { 1 * s - Q(1 * m); }; - // no floating-point modulo - requires !requires(length a) { a % 2_q_m; }; - requires !requires(length a) { 2_q_m % a; }; - requires !requires(length a) { a % 2; }; - requires !requires(length a, length b) { a % b; }; - requires !requires(length a, length b) { a % b; }; - requires !requires(length a, length b) { b % a; }; + // no floating-point modulo + requires !requires(Q a) { a % 2 * m; }; + requires !requires(Q a) { 2 * m % a; }; + requires !requires(Q a) { a % 2; }; + requires !requires(Q a, Q b) { a % b; }; + requires !requires(Q a, Q b) { a % b; }; + requires !requires(Q a, Q b) { b % a; }; - // unit constants - requires !requires { length(1) + m; }; - requires !requires { length(1) - m; }; - requires !requires { length(1) % m; }; - requires !requires { m + length(1); }; - requires !requires { m - length(1); }; - requires !requires { m % length(1); }; - }; -static_assert(invalid_binary_operations); + // unit constants + requires !requires { Q(1) + m; }; + requires !requires { Q(1) - m; }; + requires !requires { Q(1) % m; }; + requires !requires { m + Q(1); }; + requires !requires { m - Q(1); }; + requires !requires { m % Q(1); }; +}; +static_assert(invalid_binary_operations); // same representation type -static_assert(is_same_v>); -static_assert(is_same_v>); -static_assert(is_same_v>); -static_assert(compare>); -static_assert(is_same_v>); -static_assert(compare>); -static_assert(is_same_v>); -static_assert(compare>); -static_assert(is_same_v>); -static_assert(is_same_v>); -static_assert(is_same_v>); -static_assert(compare(1)), length>); -static_assert(compare(1) * 1_q_m), length>); -static_assert(compare(1)), length>); -static_assert(compare(1)), length>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare(1) / 1_q_s), - frequency(), hertz>, std::int64_t>>); +static_assert(is_of_type<1 * m + 1 * m, quantity>); +static_assert(is_of_type<1 * m + 1 * km, quantity>); +static_assert(is_of_type<1 * km + 1 * m, quantity>); +static_assert(is_of_type<1 * m - 1 * m, quantity>); +static_assert(is_of_type<1 * km - 1 * m, quantity>); +static_assert(is_of_type<1 * m - 1 * km, quantity>); + +static_assert(is_of_type<1 * N * (1 * m), quantity{}, int>>); + +static_assert(is_of_type<1 * m * 1, quantity>); +static_assert(is_of_type<1 * m * (1 * one), quantity>); +static_assert(is_of_type<1 * m * (1 * percent), quantity{}, int>>); + +static_assert(is_of_type<1 * (1 * m), quantity>); +static_assert(is_of_type<1 * one * (1 * m), quantity>); +static_assert(is_of_type<1 * percent * (1 * m), quantity{}, int>>); + +static_assert(is_of_type<1 * m / (1 * s), quantity>{}, int>>); +static_assert(is_of_type<1 * m / (1 * m), quantity>); +static_assert(is_of_type<1 * km / (1 * m), quantity, per>{}, int>>); + +static_assert(is_of_type<1 * m / 1, quantity>); +static_assert(is_of_type<1 * m / (1 * one), quantity>); +static_assert(is_of_type<1 * m / (1 * percent), quantity>{}, int>>); + +static_assert(is_of_type<1 / (1 * s), quantity>{}, int>>); +static_assert(is_of_type<1 * one / (1 * s), quantity>{}, int>>); +static_assert(is_of_type<1 * percent / (1 * s), quantity>{}, int>>); + +static_assert(is_of_type<4 * m % (2 * m), quantity>); +static_assert(is_of_type<1'234 * m % (1 * km), quantity>); +static_assert(is_of_type<1 * km % (300 * m), quantity>); +static_assert(is_of_type<4 * one % (2 * one), quantity>); +static_assert(is_of_type<4 * one % 2, quantity>); + +// check for integral types promotion static_assert(is_same_v); static_assert(is_same_v); static_assert((std::uint8_t(128) * m + std::uint8_t(128) * m).number() == std::uint8_t(128) + std::uint8_t(128)); @@ -509,399 +544,158 @@ static_assert(is_same_v); // different representation types -static_assert(is_same_v>); -static_assert(is_same_v>); -static_assert( - is_same_v>); // TODO should we address fundamental types implicit - // truncating conversions with concepts? -static_assert(is_same_v>); -static_assert(compare>); // TODO should we address fundamental types implicit truncating - // conversions with concepts? -static_assert(compare>); -static_assert(is_same_v>); -static_assert(compare>); -static_assert(compare(1)), length>); -static_assert(compare(1) * 1._q_m), length>); -static_assert(compare>); -static_assert(compare(1)), length>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare(1) / 1._q_s), - frequency(), hertz>, long double>>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare(1)), length>); -static_assert(compare(1)), length>); +static_assert(is_of_type<1. * m + 1 * m, quantity>); +static_assert(is_of_type<1 * m + 1. * km, quantity>); +static_assert(is_of_type<1 * km + 1. * m, quantity>); -static_assert(is_same_v>); -static_assert(is_same_v>); -static_assert(is_same_v>); -static_assert(is_same_v>); -static_assert(compare>); -static_assert(compare>); -static_assert(is_same_v>); -static_assert(compare>); -static_assert(compare(1)), length>); -static_assert(compare(1) * 1_q_m), length>); -static_assert(compare(1)), length>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare(1) / 1_q_s), - frequency(), hertz>, long double>>); +static_assert(is_of_type<1 * m - 1. * m, quantity>); +static_assert(is_of_type<1. * km - 1 * m, quantity>); +static_assert(is_of_type<1. * m - 1 * km, quantity>); + +static_assert(is_of_type<1. * N * (1 * m), quantity{}, double>>); + +static_assert(is_of_type<1 * m * 1., quantity>); +static_assert(is_of_type<1 * m * (1. * one), quantity>); +static_assert(is_of_type<1 * m * (1. * percent), quantity{}, double>>); + +static_assert(is_of_type<1 * (1. * m), quantity>); +static_assert(is_of_type<1. * one * (1 * m), quantity>); +static_assert(is_of_type<1 * percent * (1. * m), quantity{}, double>>); + +static_assert(is_of_type<1 * m / (1. * s), quantity>{}, double>>); +static_assert(is_of_type<1. * m / (1 * m), quantity>); +static_assert( + is_of_type<1. * km / (1 * m), quantity, per>{}, double>>); + +static_assert(is_of_type<1. * m / 1, quantity>); +static_assert(is_of_type<1 * m / (1. * one), quantity>); +static_assert( + is_of_type<1 * m / (1. * percent), quantity>{}, double>>); + +static_assert(is_of_type<1 / (1. * s), quantity>{}, double>>); +static_assert(is_of_type<1. * one / (1 * s), quantity>{}, double>>); +static_assert( + is_of_type<1 * percent / (1. * s), quantity>{}, double>>); // different units -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); +static_assert(is_of_type<1 * m + 1 * km, quantity>); +static_assert(is_of_type<1. * m + 1 * km, quantity>); +static_assert(is_of_type<1 * m + 1. * km, quantity>); +static_assert(is_of_type<1. * m + 1. * km, quantity>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); +static_assert(is_of_type<1 * km + 1 * m, quantity>); +static_assert(is_of_type<1. * km + 1 * m, quantity>); +static_assert(is_of_type<1 * km + 1. * m, quantity>); +static_assert(is_of_type<1. * km + 1. * m, quantity>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); +static_assert(is_of_type<1 * m - 1 * km, quantity>); +static_assert(is_of_type<1. * m - 1 * km, quantity>); +static_assert(is_of_type<1 * m - 1. * km, quantity>); +static_assert(is_of_type<1. * m - 1. * km, quantity>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); -static_assert(compare>); +static_assert(is_of_type<1 * km - 1 * m, quantity>); +static_assert(is_of_type<1. * km - 1 * m, quantity>); +static_assert(is_of_type<1 * km - 1. * m, quantity>); +static_assert(is_of_type<1. * km - 1. * m, quantity>); -static_assert(is_same_v>); -static_assert(is_same_v>); +static_assert(is_of_type<1 * m % (1 * km), quantity>); // different dimensions -static_assert(compare>); -static_assert(compare(), metre>, std::int64_t>>); -static_assert( - compare, exponent>, - scaled_unit(), unknown_coherent_unit, exponent>>, - std::int64_t>>); -static_assert(compare>); -static_assert(compare(), hertz>, std::int64_t>>); -static_assert(compare>); -static_assert( - compare>, - scaled_unit(), unknown_coherent_unit>>, std::int64_t>>); -static_assert(compare(), one>, std::int64_t>>); -static_assert(compare>); -static_assert( - compare(), metre_per_second>, std::int64_t>>); -static_assert( - compare, exponent>, - scaled_unit(), unknown_coherent_unit, exponent>>, - std::int64_t>>); +static_assert(is_of_type<1 * (m / s) * (1 * s), quantity>); +static_assert(is_of_type<1 * (m / s) * (1 * h), + quantity>{}, int>>); +static_assert(is_of_type<1 * m * (1 * min), quantity{}, int>>); +static_assert(is_of_type<1 * s * (1 * Hz), quantity{}, int>>); +static_assert(is_of_type<1 / (1 * min), quantity>{}, int>>); +static_assert(is_of_type<1 / (1 * Hz), quantity>{}, int>>); +static_assert(is_of_type<1 / (1 * km), quantity>>{}, int>>); +static_assert(is_of_type<1 * km / (1 * m), quantity, per>{}, int>>); +static_assert(is_of_type<1 * m / (1 * s), quantity>{}, int>>); +static_assert(is_of_type<1 * m / (1 * min), quantity>{}, int>>); +static_assert(is_of_type<1 * min / (1 * m), quantity>{}, int>>); -static_assert((1_q_m + 1_q_m).number() == 2); -static_assert((1_q_m + 1_q_km).number() == 1001); -static_assert((1_q_km + 1_q_m).number() == 1001); -static_assert((2_q_m - 1_q_m).number() == 1); -static_assert((1_q_km - 1_q_m).number() == 999); -static_assert((2_q_m * 2).number() == 4); -static_assert((2_q_m * quantity{2}).number() == 4); -static_assert((2_q_m * dimensionless(2)).number() == 4); -static_assert((3 * 3_q_m).number() == 9); -static_assert((quantity{3} * 3_q_m).number() == 9); -static_assert((dimensionless(3) * 3_q_m).number() == 9); -static_assert((4_q_m / 2).number() == 2); -static_assert((4_q_m / quantity{2}).number() == 2); -static_assert((4_q_m / dimensionless(2)).number() == 2); -static_assert((4_q_km / 2_q_m).number() == 2); -static_assert((4000_q_m / 2_q_m).number() == 2000); +static_assert((1 * m + 1 * m).number() == 2); +static_assert((1 * m + 1 * km).number() == 1001); +static_assert((1 * km + 1 * m).number() == 1001); +static_assert((2 * m - 1 * m).number() == 1); +static_assert((1 * km - 1 * m).number() == 999); +static_assert((2 * m * 2).number() == 4); +static_assert((2 * m * (2 * one)).number() == 4); +static_assert((2 * m * (2 * percent)).number() == 4); +static_assert((3 * 3 * m).number() == 9); +static_assert(((3 * one) * (3 * m)).number() == 9); +static_assert(((3 * percent) * (3 * m)).number() == 9); +static_assert((4 * m / 2).number() == 2); +static_assert((4 * m / (2 * one)).number() == 2); +static_assert((4 * m / (2 * percent)).number() == 2); +static_assert((4 * km / (2 * m)).number() == 2); +static_assert((4000 * m / (2 * m)).number() == 2000); -static_assert((1.5_q_m + 1_q_m).number() == 2.5); -static_assert((1.5_q_m + 1_q_km).number() == 1001.5); -static_assert((1.5_q_km + 1_q_m).number() == 1501); -static_assert((2.5_q_m - 1_q_m).number() == 1.5); -static_assert((1.5_q_km - 1_q_m).number() == 1499); -static_assert((2.5_q_m * 2).number() == 5); -static_assert((2.5_q_m * quantity{2}).number() == 5); -static_assert((2.5_q_m * dimensionless(2)).number() == 5); -static_assert((2.5L * 2_q_m).number() == 5); -static_assert((quantity{2.5L} * 2_q_m).number() == 5); -static_assert((dimensionless(2.5L) * 2_q_m).number() == 5); -static_assert((5._q_m / 2).number() == 2.5); -static_assert((5._q_m / quantity{2}).number() == 2.5); -static_assert((5._q_m / dimensionless(2)).number() == 2.5); -static_assert((5._q_km / 2_q_m).number() == 2.5); -static_assert((5000._q_m / 2_q_m).number() == 2500); +static_assert((1.5 * m + 1 * m).number() == 2.5); +static_assert((1.5 * m + 1 * km).number() == 1001.5); +static_assert((1.5 * km + 1 * m).number() == 1501); +static_assert((2.5 * m - 1 * m).number() == 1.5); +static_assert((1.5 * km - 1 * m).number() == 1499); +static_assert((2.5 * m * 2).number() == 5); +static_assert((2.5 * m * (2 * one)).number() == 5); +static_assert((2.5 * m * (2 * percent)).number() == 5); +static_assert((2.5L * (2 * m)).number() == 5); +static_assert((2.5L * one * (2 * m)).number() == 5); +static_assert((2.5L * percent * (2 * m)).number() == 5); +static_assert((5. * m / 2).number() == 2.5); +static_assert((5. * m / (2 * one)).number() == 2.5); +static_assert((5. * m / (2 * percent)).number() == 2.5); +static_assert((5. * km / (2 * m)).number() == 2.5); +static_assert((5000. * m / (2 * m)).number() == 2500); -static_assert((1_q_m + 1.5_q_m).number() == 2.5); -static_assert((1_q_m + 1.5_q_km).number() == 1501); -static_assert((1_q_km + 1.5_q_m).number() == 1001.5); -static_assert((2_q_m - 1.5_q_m).number() == 0.5); -static_assert((1_q_km - 1.5_q_m).number() == 998.5); -static_assert((2_q_m * 2.5L).number() == 5); -static_assert((2_q_m * quantity{2.5L}).number() == 5); -static_assert((2_q_m * dimensionless(2.5L)).number() == 5); -static_assert((2 * 2.5_q_m).number() == 5); -static_assert((quantity{2} * 2.5_q_m).number() == 5); -static_assert((dimensionless(2) * 2.5_q_m).number() == 5); -static_assert((5_q_m / 2.5L).number() == 2); -static_assert((5_q_m / quantity{2.5L}).number() == 2); -static_assert((5_q_m / dimensionless(2.5L)).number() == 2); -static_assert((5_q_km / 2.5_q_m).number() == 2); -static_assert((5000_q_m / 2.5_q_m).number() == 2000); +static_assert((1 * m + 1.5 * m).number() == 2.5); +static_assert((1 * m + 1.5 * km).number() == 1501); +static_assert((1 * km + 1.5 * m).number() == 1001.5); +static_assert((2 * m - 1.5 * m).number() == 0.5); +static_assert((1 * km - 1.5 * m).number() == 998.5); +static_assert((2 * m * 2.5L).number() == 5); +static_assert((2 * m * (2.5L * one)).number() == 5); +static_assert((2 * m * (2.5L * percent)).number() == 5); +static_assert((2 * 2.5 * m).number() == 5); +static_assert((2 * one * (2.5 * m)).number() == 5); +static_assert((2 * percent * (2.5 * m)).number() == 5); +static_assert((5 * m / 2.5L).number() == 2); +static_assert((5 * m / (2.5L * one)).number() == 2); +static_assert((5 * m / (2.5L * percent)).number() == 2); +static_assert((5 * km / (2.5 * m)).number() == 2); +static_assert((5000 * m / (2.5 * m)).number() == 2000); -static_assert((7_q_m % 2).number() == 1); -static_assert((7_q_m % quantity{2}).number() == 1); -static_assert((7_q_m % dimensionless(2)).number() == 1); -static_assert((7_q_m % 2_q_m).number() == 1); -static_assert((7_q_km % 2000_q_m).number() == 7); +static_assert((7 * m % (2 * m)).number() == 1); +static_assert((7 * km % (2000 * m)).number() == 1000); +static_assert((1300 * m % (1 * km)).number() == 300); +static_assert((7 * one % (2 * one)).number() == 1); +static_assert((7 * one % 2).number() == 1); -static_assert((10_q_km2 * 10_q_km2) / 50_q_km2 == 2_q_km2); - -static_assert((10_q_km / 5_q_m).number() == 2); -static_assert(dimensionless(10_q_km / 5_q_m).number() == 2000); - -#if UNITS_DOWNCAST_MODE == 0 -static_assert(quantity_cast(10_q_km / 5_q_m).number() == 2000); -#else -static_assert(quantity_cast(10_q_km / 5_q_m).number() == 2000); -#endif - -static_assert((10_q_s * 2_q_kHz).number() == 20); - -// quantity references -static_assert(2_q_m * (1 * m) == (2_q_m2)); -static_assert(2_q_m2 / (1 * m) == (2_q_m)); +static_assert((10 * m2 * (10 * m2)) / (50 * m2) == 2 * m2); +static_assert((10 * km / (5 * m)).number() == 2); +static_assert((10 * km / (5 * m))[one].number() == 2000); +static_assert((10 * s * (2 * kHz)).number() == 20); // dimensionless -static_assert((quantity{3} *= quantity{2}) == 6); -static_assert((quantity{6} /= quantity{2}) == 3); -static_assert(quantity{1} + quantity{1} == 2); -static_assert(1 + quantity{1} == 2); -static_assert(quantity{1} + 1 == 2); -static_assert(quantity{2} - quantity{1} == 1); -static_assert(2 - quantity{1} == 1); -static_assert(quantity{2} - 1 == 1); -static_assert(quantity{2} * quantity{2} == 4); -static_assert(2 * quantity{2} == 4); -static_assert(quantity{2} * 2 == 4); -static_assert(quantity{4} / quantity{2} == 2); -static_assert(4 / quantity{2} == 2); -static_assert(quantity{4} / 2 == 2); -static_assert(quantity{4} % quantity{2} == 0); -static_assert(4 % quantity{2} == 0); -static_assert(quantity{4} % 2 == 0); - -static_assert(is_same_v); -static_assert(is_same_v); -static_assert(is_same_v); -static_assert(is_same_v); -static_assert(quantity(1) + 2.3 == quantity(1 + 2.3)); -static_assert(quantity(1) - 2.3 == quantity(1 - 2.3)); -static_assert(1.2 + quantity(3) == quantity(1.2 + 3)); -static_assert(1.2 - quantity(3) == quantity(1.2 - 3)); - -static_assert(is_same_v); -static_assert(is_same_v); -static_assert((quantity{std::uint8_t(128)} + quantity{std::uint8_t(128)}).number() == - std::uint8_t(128) + std::uint8_t(128)); -static_assert((quantity{std::uint8_t(0)} - quantity{std::uint8_t(1)}).number() == std::uint8_t(0) - std::uint8_t(1)); - -static_assert(is_same_v); - -static_assert(quantity{2} * (1 * m) == 2_q_m); -static_assert(quantity{2} / (1 * m) == 2 / 1_q_m); - - -/////////////////////// -// equality operators -/////////////////////// - -template -concept no_crossdimensional_equality = requires { - requires !requires { 1_q_s == length(1); }; - requires !requires { 1_q_s != length(1); }; - }; -static_assert(no_crossdimensional_equality); - -// same type -static_assert(length(123) == length(123)); -static_assert(length(321) != length(123)); -static_assert(!(length(123) == length(321))); -static_assert(!(length(123) != length(123))); - -// different types -static_assert(length(123) == length(123)); -static_assert(length(321) != length(123)); -static_assert(!(length(123) == length(321))); -static_assert(!(length(123) != length(123))); - -static_assert(length(123) == length(123000)); -static_assert(length(321) != length(123000)); -static_assert(!(length(123) == length(321000))); -static_assert(!(length(123) != length(123000))); - -// dimensionless - -static_assert(quantity{123} == 123); -static_assert(quantity{321} != 123); -static_assert(123 == quantity{123}); -static_assert(123 != quantity{321}); - - -/////////////////////// -// ordering operators -/////////////////////// - -template -concept no_crossdimensional_ordering = requires { - requires !requires { 1_q_s < length(1); }; - requires !requires { 1_q_s > length(1); }; - requires !requires { 1_q_s <= length(1); }; - requires !requires { 1_q_s >= length(1); }; - }; -static_assert(no_crossdimensional_ordering); - -// same type -static_assert(length(123) < length(321)); -static_assert(length(123) <= length(123)); -static_assert(length(123) <= length(321)); -static_assert(length(321) > length(123)); -static_assert(length(123) >= length(123)); -static_assert(length(321) >= length(123)); -static_assert(!(length(321) < length(123))); -static_assert(!(length(123) < length(123))); -static_assert(!(length(321) <= length(123))); -static_assert(!(length(123) > length(321))); -static_assert(!(length(123) > length(123))); -static_assert(!(length(123) >= length(321))); - -// different types -static_assert(length(123) < length(321)); -static_assert(length(123) <= length(123)); -static_assert(length(123) <= length(321)); -static_assert(length(321) > length(123)); -static_assert(length(123) >= length(123)); -static_assert(length(321) >= length(123)); -static_assert(!(length(321) < length(123))); -static_assert(!(length(123) < length(123))); -static_assert(!(length(321) <= length(123))); -static_assert(!(length(123) > length(321))); -static_assert(!(length(123) > length(123))); -static_assert(!(length(123) >= length(321))); - -static_assert(length(123) < length(321000)); -static_assert(length(123) <= length(123000)); -static_assert(length(123) <= length(321000)); -static_assert(length(321) > length(123000)); -static_assert(length(123) >= length(123000)); -static_assert(length(321) >= length(123000)); -static_assert(!(length(321) < length(123000))); -static_assert(!(length(123) < length(123000))); -static_assert(!(length(321) <= length(123000))); -static_assert(!(length(123) > length(321000))); -static_assert(!(length(123) > length(123000))); -static_assert(!(length(123) >= length(321000))); - -// dimensionless - -static_assert(quantity{123} < 321); -static_assert(quantity{123} <= 123); -static_assert(quantity{123} <= 321); -static_assert(quantity{321} > 123); -static_assert(quantity{123} >= 123); -static_assert(quantity{321} >= 123); - -static_assert(123 < quantity{321}); -static_assert(123 <= quantity{123}); -static_assert(123 <= quantity{321}); -static_assert(321 > quantity{123}); -static_assert(123 >= quantity{123}); -static_assert(321 >= quantity{123}); - - -////////////////// -// dimensionless -////////////////// - -static_assert(std::equality_comparable_with, int>); -static_assert(std::equality_comparable_with, double>); -static_assert(std::equality_comparable_with, int>); -static_assert(!std::equality_comparable_with, double>); - -template -concept invalid_dimensionless_operations = requires { - requires !requires(dimensionless d) { 1 + d; }; - requires !requires(dimensionless d) { d + 1; }; - }; -static_assert(invalid_dimensionless_operations); - -static_assert(compare>); - -#if UNITS_DOWNCAST_MODE == 0 -static_assert(quantity_cast(50._q_m / 100._q_m).number() == 50); -#else -static_assert(quantity_cast(50._q_m / 100._q_m).number() == 50); -#endif -static_assert(50._q_m / 100._q_m == dimensionless(50)); - -static_assert(dimensionless(dimensionless(50)).number() == 0.5); - - -//////////////// -// alias units -//////////////// - -static_assert(compare>); -static_assert(2_q_l + 2_q_ml == 2002_q_cm3); -static_assert(2_q_l + 2_q_ml == 2002_q_ml); -static_assert(2_q_l + 2_q_cm3 == 2002_q_ml); -static_assert(2_q_dm3 + 2_q_cm3 == 2002_q_ml); - - -////////////////// -// quantity_cast -////////////////// - -static_assert(quantity_cast>(2_q_km).number() == 2000); -static_assert(quantity_cast>(2000_q_m).number() == 2); -static_assert(quantity_cast>(1.23_q_m).number() == 1); -static_assert(quantity_cast(2_q_km).number() == 2000); -static_assert(quantity_cast(2000_q_m).number() == 2); -static_assert(quantity_cast(1.23_q_m).number() == 1); -static_assert(quantity_cast(2000.0_q_m / 3600.0_q_s).number() == 2); - -static_assert(quantity_cast(1 * cgs_cm) == 1 * cm); - -static_assert(is_same_v(2_q_dm3)), volume>); -static_assert(!is_same_v(2_q_dm3)), volume>); - -//////////////// -// downcasting -//////////////// - -#if UNITS_DOWNCAST_MODE == 0 - -static_assert( - is_same_v, units::exponent>, - scaled_unit(), - unknown_coherent_unit, units::exponent>>, - std::int64_t>>); -static_assert(is_same_v(), metre>, std::int64_t>>); - -#else - -static_assert(compare>); -static_assert(compare>); - -#endif +static_assert((3 * one *= 2 * one) == 6); +static_assert((6 * one /= 2 * one) == 3); +static_assert(1 * one + 1 * one == 2); +static_assert(1 + 1 * one == 2); +static_assert(1 * one + 1 == 2); +static_assert(2 * one - 1 * one == 1); +static_assert(2 - 1 * one == 1); +static_assert(2 * one - 1 == 1); +static_assert(2 * one * (2 * one) == 4); +static_assert(2 * (2 * one) == 4); +static_assert(2 * one * 2 == 4); +static_assert(4 * one / (2 * one) == 2); +static_assert(4 / (2 * one) == 2); +static_assert(4 * one / 2 == 2); +static_assert(4 * one % (2 * one) == 0); +static_assert(4 % (2 * one) == 0); +static_assert(4 % (2 * one) == 0); // modulo arithmetics @@ -913,18 +707,195 @@ constexpr auto quotient_remainder_theorem(auto q1, auto q2) return q; } -static_assert(comp(quotient_remainder_theorem(3'000 * m, 400 * m), 3'000 * m)); -static_assert(comp(quotient_remainder_theorem(3 * km, 400 * m), 3 * km)); -static_assert(comp(quotient_remainder_theorem(3 * km, 2 * m), 3 * km)); -static_assert(comp(quotient_remainder_theorem(3 * km, 400'000 * mm), 3 * km)); -static_assert(comp(quotient_remainder_theorem(3 * km, 2'000 * mm), 3 * km)); -static_assert(comp(quotient_remainder_theorem(3 * km, 400 * mm), 3 * km)); -static_assert(comp(quotient_remainder_theorem(3 * km, 2 * mm), 3 * km)); -static_assert(same(quotient_remainder_theorem(3'000 * m, 400), 3'000 * m)); -static_assert(comp(quotient_remainder_theorem(3'000 * m, quantity(400)), 3'000 * m)); -static_assert(comp(quotient_remainder_theorem(3 * km, quantity(400)), 3 * km)); -static_assert(comp(quotient_remainder_theorem(3 * km, quantity(2)), 3 * km)); -static_assert(comp(quotient_remainder_theorem(3 * km, dimensionless(), one>, int>(400)), - 3 * km)); +static_assert(quotient_remainder_theorem(3'000 * m, 400 * m) == 3'000 * m); +// static_assert(quotient_remainder_theorem(3 * km, 400 * m) == 3 * km); +// static_assert(quotient_remainder_theorem(3 * km, 2 * m) == 3 * km); +// static_assert(quotient_remainder_theorem(3 * km, 400'000 * mm) == 3 * km); +// static_assert(quotient_remainder_theorem(3 * km, 2'000 * mm) == 3 * km); +// static_assert(quotient_remainder_theorem(3 * km, 400 * mm) == 3 * km); +// static_assert(quotient_remainder_theorem(3 * km, 2 * mm) == 3 * km); +// static_assert(quotient_remainder_theorem(3'000 * m, 400) == 3'000 * m); +// static_assert(quotient_remainder_theorem(3'000 * m, 400 * one) == 3'000 * m); +// static_assert(quotient_remainder_theorem(3 * km, 400 * one) == 3 * km); +// static_assert(quotient_remainder_theorem(3 * km, 2 * one) == 3 * km); +// static_assert(quotient_remainder_theorem(3 * km, 400 * (mag * one)) == 3 * km); + +static_assert((7 * m / (3 * m)) * (3 * m) + (7 * m) % (3 * m) == 7 * m); +static_assert((7 * m / 3) * 3 + (7 * m) % 3 == 7 * m); +static_assert((7 * m / (3 * one)) * (3 * one) + (7 * m) % (3 * one) == 7 * m); +static_assert((7 / (3 * one)) * (3 * one) + 7 % (3 * one) == 7); + +// static_assert((7 * km / (2500 * m)) * (2500 * m) + (7 * km) % (2500 * m) == 7 * km); +// static_assert((7500 * m / (3 * km)) * (3 * km) + (75000 * m) % (3 * km) == 7500 * m); + +static_assert(is_same_v); +static_assert(is_same_v); +static_assert(is_same_v); +static_assert(is_same_v); +static_assert(1 * one + 2.3 == (1 + 2.3) * one); +static_assert(1 * one - 2.3 == (1 - 2.3) * one); +static_assert(1.2 + 3 * one == (1.2 + 3) * one); +static_assert(1.2 - 3 * one == (1.2 - 3) * one); + +static_assert(1 - 30 * percent == (100 - 30) * percent); +static_assert(1 + 30 * percent == (100 + 30) * percent); + +static_assert(is_same_v); +static_assert(is_same_v); +static_assert((std::uint8_t(128) * one + std::uint8_t(128) * one).number() == std::uint8_t(128) + std::uint8_t(128)); +static_assert((std::uint8_t(0) * one - std::uint8_t(1) * one).number() == std::uint8_t(0) - std::uint8_t(1)); + +static_assert(is_same_v); + +static_assert(2 * one * (1 * m) == 2 * m); +static_assert(2 * one / (1 * m) == 2 / (1 * m)); + + +/////////////////////// +// equality operators +/////////////////////// + +static_assert(std::equality_comparable_with, quantity>); +static_assert(std::equality_comparable_with, quantity>); +static_assert(std::equality_comparable_with, quantity, int>>); +static_assert(std::equality_comparable_with, quantity, int>>); +static_assert(std::equality_comparable_with, quantity>); +static_assert(std::equality_comparable_with, quantity>); +static_assert(std::equality_comparable_with, quantity, int>>); +static_assert(std::equality_comparable_with, quantity>); +static_assert(std::equality_comparable_with, quantity>); +static_assert(std::equality_comparable_with, quantity, int>>); +static_assert(std::equality_comparable_with, quantity>); +static_assert(std::equality_comparable_with, quantity>); +static_assert( + std::equality_comparable_with, quantity], int>>); + +template +concept no_crossdimensional_equality = requires { + requires !requires { 1 * s == 1 * M; }; + requires !requires { 1 * s != 1 * M; }; +}; +static_assert(no_crossdimensional_equality); + +// same type +static_assert(123 * m == 123 * m); +static_assert(321 * m != 123 * m); +static_assert(!(123 * m == 321 * m)); +static_assert(!(123 * m != 123 * m)); + +// different types +static_assert(123. * m == 123 * m); +static_assert(321. * m != 123 * m); +static_assert(!(123. * m == 321 * m)); +static_assert(!(123. * m != 123 * m)); + +static_assert(123 * km == 123000 * m); +static_assert(321 * km != 123000 * m); +static_assert(!(123 * km == 321000 * m)); +static_assert(!(123 * km != 123000 * m)); + +// dimensionless +static_assert(std::equality_comparable_with, double>); +static_assert(std::equality_comparable_with, int>); +static_assert(std::equality_comparable_with, int>); +static_assert(std::equality_comparable_with, double>); + +static_assert(123 * one == 123); +static_assert(321 * one != 123); +static_assert(123 == 123 * one); +static_assert(123 != 321 * one); + + +/////////////////////// +// ordering operators +/////////////////////// + +template +concept no_crossdimensional_ordering = requires { + requires !requires { 1 * s < 1 * M; }; + requires !requires { 1 * s > 1 * M; }; + requires !requires { 1 * s <= 1 * M; }; + requires !requires { 1 * s >= 1 * M; }; +}; +static_assert(no_crossdimensional_ordering); + +// same type +static_assert(123 * m < 321 * m); +static_assert(123 * m <= 123 * m); +static_assert(123 * m <= 321 * m); +static_assert(321 * m > 123 * m); +static_assert(123 * m >= 123 * m); +static_assert(321 * m >= 123 * m); +static_assert(!(321 * m < 123 * m)); +static_assert(!(123 * m < 123 * m)); +static_assert(!(321 * m <= 123 * m)); +static_assert(!(123 * m > 321 * m)); +static_assert(!(123 * m > 123 * m)); +static_assert(!(123 * m >= 321 * m)); + +// different types +static_assert(123. * m < 321 * m); +static_assert(123. * m <= 123 * m); +static_assert(123. * m <= 321 * m); +static_assert(321. * m > 123 * m); +static_assert(123. * m >= 123 * m); +static_assert(321. * m >= 123 * m); +static_assert(!(321. * m < 123 * m)); +static_assert(!(123. * m < 123 * m)); +static_assert(!(321. * m <= 123 * m)); +static_assert(!(123. * m > 321 * m)); +static_assert(!(123. * m > 123 * m)); +static_assert(!(123. * m >= 321 * m)); + +static_assert(123 * km < 321000 * m); +static_assert(123 * km <= 123000 * m); +static_assert(123 * km <= 321000 * m); +static_assert(321 * km > 123000 * m); +static_assert(123 * km >= 123000 * m); +static_assert(321 * km >= 123000 * m); +static_assert(!(321 * km < 123000 * m)); +static_assert(!(123 * km < 123000 * m)); +static_assert(!(321 * km <= 123000 * m)); +static_assert(!(123 * km > 321000 * m)); +static_assert(!(123 * km > 123000 * m)); +static_assert(!(123 * km >= 321000 * m)); + +// dimensionless +static_assert(123 * one < 321); +static_assert(123 * one <= 123); +static_assert(123 * one <= 321); +static_assert(321 * one > 123); +static_assert(123 * one >= 123); +static_assert(321 * one >= 123); + +static_assert(123 < 321 * one); +static_assert(123 <= 123 * one); +static_assert(123 <= 321 * one); +static_assert(321 > 123 * one); +static_assert(123 >= 123 * one); +static_assert(321 >= 123 * one); + + +////////////////// +// dimensionless +////////////////// + +static_assert(is_of_type<10 * km / (5 * km), quantity>); + +static_assert((50. * m / (100. * m))[percent].number() == 50); +static_assert(50. * m / (100. * m) == 50 * percent); + +static_assert(((50. * percent)[one]).number() == 0.5); + + +////////////////// +// value_cast +////////////////// + +static_assert(value_cast(2 * km).number() == 2000); +static_assert(value_cast(2000 * m).number() == 2); +static_assert(value_cast(1.23 * m).number() == 1); +static_assert(value_cast(2000.0 * m / (3600.0 * s)).number() == 2); } // namespace