// The MIT License (MIT) // // Copyright (c) 2018 Mateusz Pusz // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include "test_tools.h" #include #include #include #include #include #include #include #if MP_UNITS_HOSTED #include #include #endif #ifdef MP_UNITS_IMPORT_STD import std; #else #include #include #include #include #include #if MP_UNITS_HOSTED #include #include #endif #endif namespace { using namespace mp_units; using namespace mp_units::si::unit_symbols; #if MP_UNITS_HOSTED using namespace std::complex_literals; using v = cartesian_vector; #endif ////////////////////////////// // quantity class invariants ////////////////////////////// static_assert(sizeof(quantity) == sizeof(double)); static_assert(sizeof(quantity) == sizeof(double)); static_assert(sizeof(quantity) == sizeof(short)); static_assert(sizeof(quantity) == sizeof(short)); template typename Q> concept invalid_types = requires { 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 #if MP_UNITS_HOSTED requires !requires { typename Q>; }; // vector representation expected requires !requires { typename Q>; }; // scalar representation expected requires !requires { typename Q>; }; // incompatible character requires !requires { typename Q; }; // incompatible character #endif }; static_assert(invalid_types); 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_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::three_way_comparable>); ////////////////// // member values ////////////////// static_assert(quantity::reference == si::metre); static_assert(quantity::quantity_spec == kind_of); static_assert(quantity::dimension == isq::dim_length); static_assert(quantity::unit == si::metre); 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); static_assert(quantity::reference == one); static_assert(quantity::quantity_spec == kind_of); static_assert(quantity::dimension == dimension_one); static_assert(quantity::unit == one); static_assert(quantity::reference == dimensionless[one]); static_assert(quantity::quantity_spec == dimensionless); static_assert(quantity::dimension == dimension_one); static_assert(quantity::unit == one); static_assert(quantity::reference == si::radian); static_assert(quantity::quantity_spec == kind_of); static_assert(quantity::dimension == dimension_one); static_assert(quantity::unit == si::radian); static_assert(quantity::reference == isq::angular_measure[rad]); static_assert(quantity::quantity_spec == isq::angular_measure); static_assert(quantity::dimension == dimension_one); static_assert(quantity::unit == si::radian); ///////////////// // member types ///////////////// static_assert(is_same_v::rep, double>); static_assert(is_same_v::rep, int>); [[maybe_unused]] volatile std::int16_t vint = 123; static_assert(is_same_v); static_assert(is_same_v); //////////////////////////// // static member functions //////////////////////////// static_assert(quantity::zero().numerical_value_in(m) == 0); static_assert(quantity::min().numerical_value_in(m) == std::numeric_limits::lowest()); static_assert(quantity::max().numerical_value_in(m) == std::numeric_limits::max()); static_assert(quantity::zero().numerical_value_in(m) == 0.0); static_assert(quantity::min().numerical_value_in(m) == std::numeric_limits::lowest()); static_assert(quantity::max().numerical_value_in(m) == std::numeric_limits::max()); ///////////////////////////////////////////////// // no construction from value (unless unit one) ///////////////////////////////////////////////// // construction from a value is private 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, double>); static_assert(std::convertible_to>); static_assert(std::constructible_from, int>); 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>); /////////////////////////////////////// // construction from another quantity /////////////////////////////////////// // 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>); #if MP_UNITS_HOSTED 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>>); static_assert(std::constructible_from>, quantity>>); static_assert(std::convertible_to>, quantity>>); #endif // 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, quantity>); // int -> double OK static_assert(std::convertible_to, quantity>); // int -> double OK 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, quantity>); // kilometre -> metre OK static_assert(std::convertible_to, quantity>); // kilometre -> metre OK 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, quantity>); static_assert(std::convertible_to, quantity>); static_assert(std::constructible_from, quantity>); static_assert(std::convertible_to, quantity>); static_assert(!std::convertible_to, quantity>); static_assert(!std::convertible_to, quantity>); static_assert(!std::convertible_to, quantity>); static_assert(!std::convertible_to, quantity>); static_assert(!std::convertible_to, quantity>); // explicit-construction only static_assert(std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity>); /////////////////////// // obtaining a number /////////////////////// static_assert(quantity(123 * m).numerical_value_in(m) == 123); static_assert(quantity(2 * km).numerical_value_in(m) == 2000); static_assert(quantity(2 * km).numerical_value_in(km) == 2); static_assert(quantity(1500 * m).numerical_value_in(km) == 1.5); //////////////////////////////////////////////////////// // explicit conversion to a number (when unit is one) //////////////////////////////////////////////////////// static_assert(!std::convertible_to, double>); static_assert(std::constructible_from>); static_assert(!std::convertible_to, int>); static_assert(std::constructible_from>); static_assert(!std::convertible_to, double>); static_assert(std::constructible_from>); static_assert(!std::convertible_to, double>); static_assert(std::constructible_from>); static_assert(!std::convertible_to, int>); static_assert(std::constructible_from>); static_assert(!std::convertible_to, double>); static_assert(std::constructible_from>); #if MP_UNITS_HOSTED static_assert(!std::convertible_to>, std::complex>); static_assert(std::constructible_from, quantity>>); static_assert(!std::convertible_to, std::complex>); static_assert(std::constructible_from, quantity>); static_assert(!std::convertible_to>, cartesian_vector>); static_assert(std::constructible_from, quantity>>); static_assert(!std::convertible_to, cartesian_vector>); static_assert(std::constructible_from, quantity>); #endif static_assert(!std::convertible_to, double>); static_assert(!std::constructible_from>); static_assert(!std::convertible_to, int>); static_assert(!std::constructible_from>); static_assert(!std::convertible_to, double>); static_assert(!std::constructible_from>); static_assert(!std::convertible_to, double>); static_assert(!std::constructible_from>); static_assert(!std::convertible_to, int>); static_assert(!std::constructible_from>); static_assert(!std::convertible_to, double>); static_assert(!std::constructible_from>); /////////////////////////////////// // converting to a different unit /////////////////////////////////// static_assert(is_of_type<(2. * km).in(m), quantity>); static_assert(is_of_type>); static_assert(is_of_type>); static_assert(is_of_type<(2 * km).in(m), quantity>); static_assert(is_of_type(m), quantity>); static_assert(is_of_type(m), quantity>); static_assert(is_of_type<(2 * m).in(), quantity>); static_assert(is_of_type(), quantity>); static_assert(is_of_type(), quantity>); static_assert(quantity(2. * km).in(km).numerical_value_in(km) == 2.); static_assert(quantity(2. * km).in(m).numerical_value_in(m) == 2000.); static_assert(quantity(2000. * m).in(km).numerical_value_in(km) == 2.); static_assert(quantity(2 * km).in(km).numerical_value_in(km) == 2); static_assert(quantity(2 * km).in(m).numerical_value_in(m) == 2000); static_assert(is_of_type<(2. * km).force_in(m), quantity>); static_assert(is_of_type>); static_assert(is_of_type>); static_assert(quantity(2. * km).force_in(km).numerical_value_in(km) == 2.); static_assert(quantity(2. * km).force_in(m).numerical_value_in(m) == 2000.); static_assert(quantity(2000. * m).force_in(km).numerical_value_in(km) == 2.); static_assert(quantity(2 * km).force_in(km).numerical_value_in(km) == 2); static_assert(quantity(2 * km).force_in(m).numerical_value_in(m) == 2000); static_assert(quantity(2000 * m).force_in(km).numerical_value_in(km) == 2); static_assert((15. * m).in(nm).numerical_value_in(m) == 15.); static_assert((15'000. * nm).in(m).numerical_value_in(nm) == 15'000.); // check if unit conversion works - don't bother about the actual result static_assert((1. * rad + 1. * deg).in(rad) != 0 * rad); static_assert((1. * rad + 1. * deg).in(deg) != 0 * deg); static_assert((1. * rad + 1. * deg).in(one) != 0 * one); #if MP_UNITS_HOSTED static_assert(((2.f + 1if) * isq::voltage_phasor[V]).in(mV).numerical_value_in(mV) == 2000.f + 1000if); static_assert(((2.f + 1if) * isq::voltage_phasor[V]).in(mV).numerical_value_in(V) == 2.f + 1if); static_assert(((2. + 1i) * isq::voltage_phasor[V]).in(mV).numerical_value_in(mV) == 2000. + 1000i); static_assert(((2. + 1i) * isq::voltage_phasor[V]).in(mV).numerical_value_in(V) == 2. + 1i); static_assert(((2.L + 1il) * isq::voltage_phasor[V]).in(mV).numerical_value_in(mV) == 2000.L + 1000il); static_assert(((2.L + 1il) * isq::voltage_phasor[V]).in(mV).numerical_value_in(V) == 2.L + 1il); static_assert((v{1., 2., 3.} * isq::position_vector[m]).in(mm).numerical_value_in(mm) == v{1000., 2000., 3000.}); static_assert((v{1., 2., 3.} * isq::position_vector[m]).in(mm).numerical_value_in(m) == v{1., 2., 3.}); #endif template typename Q> concept invalid_unit_conversion = requires { requires !requires { Q(2000 * m).in(km); }; // truncating conversion requires !requires { Q(2 * m).in(s); }; // unit of a different quantity & dimension requires !requires { Q(60 * Hz).in(Bq); }; // unit of a different kind (same dimension) requires !requires { Q(2 * m).force_in(s); }; // unit of a different quantity & dimension requires !requires { Q(60 * Hz).force_in(Bq); }; // unit of a different kind (same dimension) }; static_assert(invalid_unit_conversion); static_assert(quantity(2. * km).numerical_value_in(km) == 2.); static_assert(quantity(2. * km).numerical_value_in(m) == 2000.); static_assert(quantity(2000. * m).numerical_value_in(km) == 2.); static_assert(quantity(2 * km).numerical_value_in(km) == 2); static_assert(quantity(2 * km).numerical_value_in(m) == 2000); template typename Q> concept invalid_getter_with_unit_conversion = requires { requires !requires { Q(2000 * m).numerical_value_in(km); }; // truncating conversion requires !requires { Q(2 * m).numerical_value_in(s); }; // invalid unit }; static_assert(invalid_getter_with_unit_conversion); /////////////////////////////////////// // derived quantities /////////////////////////////////////// template Rep = double> struct child_quantity : quantity { using quantity_type = quantity; static constexpr auto reference = R; static constexpr auto quantity_spec = quantity_type::quantity_spec; static constexpr auto dimension = quantity_type::dimension; static constexpr auto unit = quantity_type::unit; using rep = Rep; child_quantity() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) constexpr explicit(false) child_quantity(const quantity_type& t) : quantity_type(t) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) constexpr explicit(false) child_quantity(quantity_type&& t) : quantity_type(std::move(t)) {} constexpr child_quantity& operator=(const quantity_type& q) { quantity_type::operator=(q); return *this; } constexpr child_quantity& operator=(quantity_type&& q) { quantity_type::operator=(std::move(q)); return *this; } // NOLINTBEGIN(google-explicit-constructor, hicpp-explicit-conversions) constexpr explicit(false) operator quantity_type&() & noexcept { return *this; } constexpr explicit(false) operator const quantity_type&() const& noexcept { return *this; } constexpr explicit(false) operator quantity_type&&() && noexcept { return *this; } constexpr explicit(false) operator const quantity_type&&() const&& noexcept { return *this; } // NOLINTEND(google-explicit-constructor, hicpp-explicit-conversions) }; static_assert(Quantity>); constexpr QuantityOf auto get_length_child_quantity() noexcept { child_quantity dist{}; dist += 1 * m; dist = dist + 1 * m; dist *= 0.5; return dist; } static_assert(get_length_child_quantity() == 1 * m); ///////// // CTAD ///////// 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(std::is_same_v); static_assert(std::is_same_v); static_assert(quantity{123}.unit == one); static_assert(quantity{123}.quantity_spec == kind_of); #if MP_UNITS_HOSTED static_assert(quantity{123. + 1i}.unit == one); static_assert(quantity{123. + 1i}.quantity_spec == kind_of); static_assert(quantity{v{1., 2., 3}}.unit == one); static_assert(quantity{v{1., 2., 3}}.quantity_spec == kind_of); 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); #endif //////////////////////// // assignment operator //////////////////////// static_assert([] { auto l1 = 1 * m, l2 = 2 * m; return l2 = l1; }() .numerical_value_in(m) == 1); static_assert([] { const auto l1 = 1 * m; auto l2 = 2 * m; return l2 = l1; }() .numerical_value_in(m) == 1); static_assert([]() { auto l1 = 1 * m, l2 = 2 * m; return l2 = std::move(l1); // NOLINT(*-move-const-arg) }() .numerical_value_in(m) == 1); static_assert([]() { quantity q(1); return q = 2; }() .numerical_value_in(one) == 2); //////////////////// // unary operators //////////////////// static_assert((+123 * m).numerical_value_in(m) == 123); static_assert((-123 * m).numerical_value_in(m) == -123); static_assert((+(-123 * m)).numerical_value_in(m) == -123); static_assert((-(-123 * m)).numerical_value_in(m) == 123); static_assert([](auto val) { const auto vv = val++; // NOLINT(bugprone-inc-dec-in-conditions) return std::pair(val, vv); }(123 * m) == std::pair(124 * m, 123 * m)); static_assert([](auto val) { const auto vv = ++val; // NOLINT(bugprone-inc-dec-in-conditions) return std::pair(val, vv); }(123 * m) == std::pair(124 * m, 124 * m)); static_assert([](auto val) { const auto vv = val--; // NOLINT(bugprone-inc-dec-in-conditions) return std::pair(val, vv); }(123 * m) == std::pair(122 * m, 123 * m)); static_assert([](auto val) { const auto vv = --val; // NOLINT(bugprone-inc-dec-in-conditions) return std::pair(val, vv); }(123 * m) == std::pair(122 * m, 122 * m)); static_assert(is_same_v); //////////////////////// // compound assignment //////////////////////// // same type static_assert([q = 1 * m]() mutable { return q += 1 * m; }().numerical_value_in(m) == 2); static_assert([q = 2 * m]() mutable { return q -= 1 * m; }().numerical_value_in(m) == 1); static_assert([q = 1 * m]() mutable { return q *= 2; }().numerical_value_in(m) == 2); static_assert([q = 2 * m]() mutable { return q /= 2; }().numerical_value_in(m) == 1); static_assert([q = 1 * m]() mutable { return q *= 2 * one; }().numerical_value_in(m) == 2); static_assert([q = 2 * m]() mutable { return q /= 2 * one; }().numerical_value_in(m) == 1); static_assert([q = 7 * m]() mutable { return q %= 2 * m; }().numerical_value_in(m) == 1); #if MP_UNITS_HOSTED static_assert([q = (1. + 1i) * V]() mutable { return q += (1. + 1i) * V; }().numerical_value_in(V) == 2. + 2i); static_assert([q = (2. + 2i) * V]() mutable { return q -= (1. + 1i) * V; }().numerical_value_in(V) == 1. + 1i); static_assert([q = (1. + 1i) * V]() mutable { return q += 1. * V; }().numerical_value_in(V) == 2. + 1i); static_assert([q = (2. + 2i) * V]() mutable { return q -= 1. * V; }().numerical_value_in(V) == 1. + 2i); static_assert([q = (1. + 1i) * V]() mutable { return q *= 2.; }().numerical_value_in(V) == 2. + 2i); static_assert([q = (2. + 2i) * V]() mutable { return q /= 2.; }().numerical_value_in(V) == 1. + 1i); static_assert([q = (1. + 1i) * V]() mutable { return q *= 2. * one; }().numerical_value_in(V) == 2. + 2i); static_assert([q = (2. + 2i) * V]() mutable { return q /= 2. * one; }().numerical_value_in(V) == 1. + 1i); static_assert([q = (1. + 1i) * V]() mutable { return q *= (2. + 1i) * one; }().numerical_value_in(V) == (1. + 1i) * (2. + 1i)); static_assert([q = (2. + 2i) * V]() mutable { return q /= (2. + 1i) * one; }().numerical_value_in(V) == (2. + 2i) / (2. + 1i)); static_assert([q = v{1., 2., 3.} * m]() mutable { return q += v{1., 2., 3.} * m; }().numerical_value_in(m) == v{2., 4., 6.}); static_assert([q = v{2., 4., 6.} * m]() mutable { return q -= v{1., 2., 3.} * m; }().numerical_value_in(m) == v{1., 2., 3.}); static_assert([q = v{1., 2., 3.} * m]() mutable { return q *= 2.; }().numerical_value_in(m) == v{2., 4., 6.}); static_assert([q = v{2., 4., 6.} * m]() mutable { return q /= 2.; }().numerical_value_in(m) == v{1., 2., 3.}); static_assert([q = v{1., 2., 3.} * m]() mutable { return q *= 2. * one; }().numerical_value_in(m) == v{2., 4., 6.}); static_assert([q = v{2., 4., 6.} * m]() mutable { return q /= 2. * one; }().numerical_value_in(m) == v{1., 2., 3.}); #endif // different representation types static_assert([q = 2.5 * m]() mutable { return q += 3 * m; }().numerical_value_in(m) == 5.5); static_assert([q = 5.5 * m]() mutable { return q -= 3 * m; }().numerical_value_in(m) == 2.5); static_assert([q = 2.5 * m]() mutable { return q *= 3; }().numerical_value_in(m) == 7.5); static_assert([q = 7.5 * m]() mutable { return q /= 3; }().numerical_value_in(m) == 2.5); static_assert([q = 2.5 * m]() mutable { return q *= 3 * one; }().numerical_value_in(m) == 7.5); static_assert([q = 7.5 * m]() mutable { return q /= 3 * one; }().numerical_value_in(m) == 2.5); // different units static_assert([q = 1 * m]() mutable { return q += 1 * km; }().numerical_value_in(m) == 1001); static_assert([q = 2000 * m]() mutable { return q -= 1 * km; }().numerical_value_in(m) == 1000); static_assert([q = 3500 * m]() mutable { return q %= 1 * km; }().numerical_value_in(m) == 500); #if MP_UNITS_HOSTED static_assert([q = (1000. + 1000i) * V]() mutable { return q += (1. + 1i) * kV; }().numerical_value_in(V) == 2000. + 2000i); static_assert([q = (2000. + 2000i) * V]() mutable { return q -= (1. + 1i) * kV; }().numerical_value_in(V) == 1000. + 1000i); static_assert([q = (v{1000., 2000., 3000.} * m)]() mutable { return q += v{1., 2., 3.} * km; }().numerical_value_in( m) == v{2000., 4000., 6000.}); static_assert([q = (v{2000., 4000., 6000.} * m)]() mutable { return q -= v{1., 2., 3.} * km; }().numerical_value_in( m) == v{1000., 2000., 3000.}); #endif // convertible quantity types static_assert([q = isq::length(1 * m)]() mutable { return q += isq::height(1 * m); }().numerical_value_in(m) == 2); static_assert([q = isq::length(2 * m)]() mutable { return q -= isq::height(1 * m); }().numerical_value_in(m) == 1); static_assert([q = isq::length(7 * m)]() mutable { return q %= isq::height(2 * m); }().numerical_value_in(m) == 1); static_assert([q = std::uint8_t{255} * m]() mutable { return q %= 256 * m; }().numerical_value_in(m) == [] { std::uint8_t ui(255); return ui %= 256; }()); static_assert([q = std::uint8_t{255} * m]() mutable { return q %= 257 * m; }().numerical_value_in(m) == [] { std::uint8_t ui(255); return ui %= 257; }()); // lack of consistency with binary operator static_assert( is_of_type<1 * (isq::length / isq::time)[m / s] + 1 * isq::speed[m / s], quantity>); static_assert(is_of_type<[q = 1 * (isq::length / isq::time)[m / s]]() mutable { return q += 1 * isq::speed[m / s]; }(), quantity<(isq::length / isq::time)[m / s], int>>); 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; }; requires !requires(Q l) { l *= 2.5; }; requires !requires(Q l) { l /= 2.5; }; requires !requires(Q l) { l *= 2.5 * one; }; requires !requires(Q l) { l /= 2.5 * one; }; // compound assignment with a non-convertible quantity not allowed 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]; }; // dimensionless quantities with a unit different than `one` 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(Q l) { l += 2; }; requires !requires(Q l) { l -= 2; }; // 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(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; }; #if MP_UNITS_HOSTED // modulo operations on a complex representation type not allowed requires !requires(Q> l) { l %= (2. + 2i) * V; }; // modulo operations on a vector representation type not allowed requires !requires(Q> l) { l %= v{2.} * m; }; // arithmetics of vector and scalar quantities not allowed requires !requires(Q> l) { l += 2. * m; }; requires !requires(Q> l) { l -= 2. * m; }; // multiplication and division of vector quantities not allowed requires !requires(Q> l) { l *= v{1., 2., 3.}; }; requires !requires(Q> l) { l /= v{1., 2., 3.}; }; requires !requires(Q> l) { l *= v{1., 2., 3.} * one; }; requires !requires(Q> l) { l /= v{1., 2., 3.} * one; }; requires !requires(Q> l) { l *= v{1., 2., 3.} * m; }; requires !requires(Q> l) { l /= v{1., 2., 3.} * m; }; #endif // 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 typename Q> concept invalid_binary_operations = requires { // 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(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; }; #if MP_UNITS_HOSTED // no complex modulo requires !requires(Q> a) { a % ((2. + 2i) * V); }; // no vector modulo requires !requires(Q> a) { a % (v{2.} * m); }; #endif // 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_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 + isq::length(1 * m), quantity>); static_assert(is_of_type<1 * m + isq::length(1 * km), quantity>); static_assert(is_of_type<1 * km + isq::length(1 * m), quantity>); static_assert(is_of_type>); static_assert(is_of_type>); static_assert(is_of_type>); 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 * m - isq::length(1 * m), quantity>); static_assert(is_of_type<1 * m - isq::length(1 * km), quantity>); static_assert(is_of_type<1 * km - isq::length(1 * m), quantity>); static_assert(is_of_type>); static_assert(is_of_type>); static_assert(is_of_type>); 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 / 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>); // 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).numerical_value_in(m) == std::uint8_t{128} + std::uint8_t{128}); static_assert((std::uint8_t{0} * m - std::uint8_t{1} * m).numerical_value_in(m) == std::uint8_t{0} - std::uint8_t{1}); static_assert( is_same_v); // different representation types 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{}, 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. / 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(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(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_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(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_of_type<1 * m % (1 * km), quantity>); // different dimensions 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 / min, quantity>{}, int>>); static_assert(is_of_type<1 / Hz, quantity>{}, int>>); static_assert(is_of_type<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 * m + 1 * m).numerical_value_in(m) == 2); static_assert((1 * m + 1 * km).numerical_value_in(m) == 1001); static_assert((1 * km + 1 * m).numerical_value_in(m) == 1001); static_assert((2 * m - 1 * m).numerical_value_in(m) == 1); static_assert((1 * km - 1 * m).numerical_value_in(m) == 999); static_assert((2 * m * 2).numerical_value_in(m) == 4); static_assert((2 * m * (2 * one)).numerical_value_in(m) == 4); static_assert((2 * m * (2 * percent)).numerical_value_in(percent * m) == 4); static_assert((3 * 3 * m).numerical_value_in(m) == 9); static_assert(((3 * one) * (3 * m)).numerical_value_in(m) == 9); static_assert(((3 * percent) * (3 * m)).numerical_value_in(percent * m) == 9); static_assert((4 * m / 2).numerical_value_in(m) == 2); static_assert((4 * m / (2 * one)).numerical_value_in(m) == 2); static_assert((4 * m / (2 * percent)).numerical_value_in(m / percent) == 2); static_assert((4 * km / (2 * m)).numerical_value_in(km / m) == 2); static_assert((4000 * m / (2 * m)).numerical_value_in(one) == 2000); static_assert((1.5 * m + 1 * m).numerical_value_in(m) == 2.5); static_assert((1.5 * m + 1 * km).numerical_value_in(m) == 1001.5); static_assert((1.5 * km + 1 * m).numerical_value_in(m) == 1501); static_assert((2.5 * m - 1 * m).numerical_value_in(m) == 1.5); static_assert((1.5 * km - 1 * m).numerical_value_in(m) == 1499); static_assert((2.5 * m * 2).numerical_value_in(m) == 5); static_assert((2.5 * m * (2 * one)).numerical_value_in(m) == 5); static_assert((2.5 * m * (2 * percent)).numerical_value_in(m * percent) == 5); static_assert((2.5L * (2 * m)).numerical_value_in(m) == 5); static_assert((2.5L * one * (2 * m)).numerical_value_in(m) == 5); static_assert((2.5L * percent * (2 * m)).numerical_value_in(m * percent) == 5); static_assert((5. * m / 2).numerical_value_in(m) == 2.5); static_assert((5. * m / (2 * one)).numerical_value_in(m) == 2.5); static_assert((5. * m / (2 * percent)).numerical_value_in(m / percent) == 2.5); static_assert((5. * km / (2 * m)).numerical_value_in(km / m) == 2.5); static_assert((5000. * m / (2 * m)).numerical_value_in(one) == 2500); static_assert((1 * m + 1.5 * m).numerical_value_in(m) == 2.5); static_assert((1 * m + 1.5 * km).numerical_value_in(m) == 1501); static_assert((1 * km + 1.5 * m).numerical_value_in(m) == 1001.5); static_assert((2 * m - 1.5 * m).numerical_value_in(m) == 0.5); static_assert((1 * km - 1.5 * m).numerical_value_in(m) == 998.5); static_assert((2 * m * 2.5L).numerical_value_in(m) == 5); static_assert((2 * m * (2.5L * one)).numerical_value_in(m) == 5); static_assert((2 * m * (2.5L * percent)).numerical_value_in(m * percent) == 5); static_assert((2 * 2.5 * m).numerical_value_in(m) == 5); static_assert((2 * one * (2.5 * m)).numerical_value_in(m) == 5); static_assert((2 * percent * (2.5 * m)).numerical_value_in(m * percent) == 5); static_assert((5 * m / 2.5L).numerical_value_in(m) == 2); static_assert((5 * m / (2.5L * one)).numerical_value_in(m) == 2); static_assert((5 * m / (2.5L * percent)).numerical_value_in(m / percent) == 2); static_assert((5 * km / (2.5 * m)).numerical_value_in(km / m) == 2); static_assert((5000 * m / (2.5 * m)).numerical_value_in(one) == 2000); static_assert((7 * m % (2 * m)).numerical_value_in(m) == 1); static_assert((7 * km % (2000 * m)).numerical_value_in(m) == 1000); static_assert((1300 * m % (1 * km)).numerical_value_in(m) == 300); static_assert((7 * one % (2 * one)).numerical_value_in(one) == 1); static_assert((10 * m2 * (10 * m2)) / (50 * m2) == 2 * m2); static_assert((10 * km / (5 * m)).numerical_value_in(km / m) == 2); static_assert((10 * km / (5 * m)).numerical_value_in(one) == 2000); static_assert((10 * s * (2 * kHz)).numerical_value_in(s * kHz) == 20); // commutativity and associativity static_assert(10 * isq::length[si::metre] / (2 * isq::time[s]) + 5 * isq::speed[m / s] == 10 * isq::speed[m / s]); static_assert(5 * isq::speed[m / s] + 10 * isq::length[m] / (2 * isq::time[s]) == 10 * isq::speed[m / s]); static_assert(10 * isq::length[m] / (2 * isq::time[s]) - 5 * isq::speed[m / s] == 0 * isq::speed[m / s]); static_assert(5 * isq::speed[m / s] - 10 * isq::length[m] / (2 * isq::time[s]) == 0 * isq::speed[m / s]); static_assert( is_of_type<10 * isq::length[m] / (2 * isq::time[s]) + 5 * isq::speed[m / s], quantity>); static_assert( is_of_type<5 * isq::speed[m / s] + 10 * isq::length[m] / (2 * isq::time[s]), quantity>); static_assert( is_of_type<10 * isq::length[m] / (2 * isq::time[s]) - 5 * isq::speed[m / s], quantity>); static_assert( is_of_type<5 * isq::speed[m / s] - 10 * isq::length[m] / (2 * isq::time[s]), quantity>); static_assert(10 / (2 * isq::time[s]) + 5 * isq::frequency[Hz] == 10 * isq::frequency[Hz]); static_assert(5 * isq::frequency[Hz] + 10 / (2 * isq::time[s]) == 10 * isq::frequency[Hz]); static_assert(10 / (2 * isq::time[s]) - 5 * isq::frequency[Hz] == 0 * isq::frequency[Hz]); static_assert(5 * isq::frequency[Hz] - 10 / (2 * isq::time[s]) == 0 * isq::frequency[Hz]); static_assert( is_of_type<10 / (2 * isq::period_duration[s]) + 5 * isq::frequency[Hz], quantity>); static_assert( is_of_type<5 * isq::frequency[Hz] + 10 / (2 * isq::period_duration[s]), quantity>); static_assert( is_of_type<10 / (2 * isq::period_duration[s]) - 5 * isq::frequency[Hz], quantity>); static_assert( is_of_type<5 * isq::frequency[Hz] - 10 / (2 * isq::period_duration[s]), quantity>); static_assert( is_of_type>); static_assert( is_of_type>); static_assert( is_of_type>); static_assert( is_of_type>); #if MP_UNITS_HOSTED static_assert(is_same_v(isq::length(1 * m) / isq::time(1 * s))).in(J) + isq::energy(1 * kg * m2 / s2)), quantity>); static_assert(is_same_v(isq::length(1 * m) / isq::time(1 * s))).in(J)), quantity>); static_assert(is_same_v(isq::length(1 * m) / isq::time(1 * s))) .in(J)-isq::energy(1 * kg * m2 / s2)), quantity>); static_assert(is_same_v(isq::length(1 * m) / isq::time(1 * s))).in(J)), quantity>); #endif static_assert(is_of_type(1. * m) + 1 * m, quantity>); static_assert(is_of_type<1 * m + child_quantity(1. * m), quantity>); static_assert(is_of_type(1. * m) - 1 * m, quantity>); static_assert(is_of_type<1 * m - child_quantity(1. * m), quantity>); // Different named dimensions template consteval bool invalid_arithmetic(Ts... ts) { return !requires { (... + ts); } && !requires { (... - ts); }; } static_assert(invalid_arithmetic(5 * isq::activity[Bq], 5 * isq::frequency[Hz])); static_assert(invalid_arithmetic(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz])); // irrational conversion factors require floating point representation static_assert(invalid_arithmetic(1 * rad, 1 * deg)); static_assert(is_of_type<1. * rad + 1 * deg, quantity{}, double>>); static_assert(is_of_type<1 * rad + 1. * deg, quantity{}, double>>); static_assert(is_of_type<1. * rad + 1. * deg, quantity{}, double>>); // Physical constants static_assert(1 * si::si2019::speed_of_light_in_vacuum + 10 * isq::speed[m / s] == 299'792'468 * isq::speed[m / s]); // Implicit conversions allowed between quantities of `convertible` references [[maybe_unused]] constexpr quantity speed = 120 * isq::length[km] / (2 * isq::time[h]); // dimensionless static_assert([q = 3 * one]() mutable { return q *= 2 * one; }() == 6 * one); static_assert([q = 6 * one]() mutable { return q /= 2 * one; }() == 3 * one); static_assert(1 * one + 1 * one == 2 * one); static_assert(2 * one - 1 * one == 1 * one); static_assert(2 * one * (2 * one) == 4 * one); static_assert(2 * (2 * one) == 4 * one); static_assert(2 * one * 2 == 4 * one); static_assert(4 * one / (2 * one) == 2 * one); static_assert(4 / (2 * one) == 2 * one); static_assert(4 * one / 2 == 2 * one); static_assert(4 * one % (2 * one) == 0 * one); #if MP_UNITS_HOSTED static_assert([q = (3. + 3i) * one]() mutable { return q *= (2. + 2i) * one; }() == (3. + 3i) * (2. + 2i) * one); static_assert([q = (6. + 6i) * one]() mutable { return q /= (2. + 2i) * one; }() == (6. + 6i) / (2. + 2i) * one); static_assert((1. + 1i) * one + (1. + 1i) * one == (2. + 2i) * one); static_assert((2. + 2i) * one - (1. + 1i) * one == (1. + 1i) * one); static_assert((2. + 2i) * one * (2. * one) == (4. + 4i) * one); static_assert((2. + 2i) * ((2. + 2i) * one) == (2. + 2i) * (2. + 2i) * one); static_assert((2. + 2i) * one * (2. + 2i) == (2. + 2i) * (2. + 2i) * one); static_assert((4. + 4i) * one / ((2. + 2i) * one) == (4. + 4i) / (2. + 2i) * one); static_assert((4. + 4i) / ((2. + 2i) * one) == (4. + 4i) / (2. + 2i) * one); static_assert((4. + 4i) * one / (2. + 2i) == (4. + 4i) / (2. + 2i) * one); static_assert(v{1., 2., 3.} * one + v{1., 2., 3.} * one == v{2., 4., 6.} * one); static_assert(v{2., 4., 6.} * one - v{1., 2., 3.} * one == v{1., 2., 3.} * one); #endif static_assert(1 * one + 1 == 2); static_assert(1 + 1 * one == 2); static_assert(2 * one - 1 == 1); static_assert(2 - 1 * one == 1); static_assert(1 * one + 1.23 == 2.23); static_assert(1 + 1.23 * one == 2.23); static_assert(2.23 * one - 1 == 1.23); static_assert(2.23 - 1 * one == 1.23); static_assert(4 * one % (2) == 0); static_assert(4 % (2 * one) == 0); #if MP_UNITS_HOSTED static_assert((1. + 1i) * one + (1. + 1i) == 2. + 2i); static_assert((1. + 1i) + (1. + 1i) * one == 2. + 2i); static_assert((2. + 2i) * one - (1. + 1i) == 1. + 1i); static_assert((2. + 2i) - (1. + 1i) * one == 1. + 1i); static_assert(1. * one + (1. + 1i) == (2. + 1i)); static_assert(1. + (1. + 1i) * one == (2. + 1i)); static_assert(2. * one - (1. + 1i) == (1. - 1i)); static_assert(2. - (1. + 1i) * one == (1. - 1i)); static_assert(v{1., 2., 3.} * one + v{1., 2., 3.} == v{2., 4., 6.}); static_assert(v{1., 2., 3.} + v{1., 2., 3.} * one == v{2., 4., 6.}); static_assert(v{2., 4., 6.} * one - v{1., 2., 3.} == v{1., 2., 3.}); static_assert(v{2., 4., 6.} - v{1., 2., 3.} * one == v{1., 2., 3.}); #endif static_assert(2 * rad * (2 * rad) == 4 * pow<2>(rad)); // modulo arithmetics static_assert(5 * h % (120 * min) == 60 * min); static_assert(300 * min % (2 * h) == 60 * min); static_assert(300 * min % (120 * min) == 60 * min); constexpr auto quotient_remainder_theorem(auto q1, auto q2) { auto quotient = q1 / q2; auto reminder = q1 % q2; auto q = quotient * q2 + reminder; return q; } // this works only if two quantities have the same unit static_assert(quotient_remainder_theorem(7 * m, 3 * m) == 7 * m); static_assert(quotient_remainder_theorem(3'000 * m, 400 * m) == 3'000 * 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 - 30 * percent == (100 - 30) * percent); static_assert(1 * one + 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).numerical_value_in(one) == std::uint8_t{128} + std::uint8_t{128}); static_assert((std::uint8_t{0} * one - std::uint8_t{1} * one).numerical_value_in(one) == 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)); #if MP_UNITS_HOSTED /////////////////////// // complex quantities /////////////////////// static_assert((1. + 1i) * V + (1. + 1i) * V == (2. + 2i) * V); static_assert((1. + 1i) * V + 1. * V == (2. + 1i) * V); static_assert((1000. + 1000i) * V + (1. + 1i) * kV == (2000. + 2000i) * V); static_assert((1000. + 1000i) * V + 1. * kV == (2000. + 1000i) * V); static_assert((2. + 2i) * V - (1. + 1i) * V == (1. + 1i) * V); static_assert((2. + 2i) * V - 1. * V == (1. + 2i) * V); static_assert((2000. + 2000i) * V - (1. + 1i) * kV == (1000. + 1000i) * V); static_assert((2000. + 2000i) * V - 1. * kV == (1000. + 2000i) * V); static_assert((1. + 1i) * V + isq::voltage_phasor((1. + 1i) * V) == (2. + 2i) * V); static_assert(isq::voltage_phasor((1. + 1i) * V) + 1. * V == (2. + 1i) * V); static_assert((2. + 2i) * V - isq::voltage_phasor((1. + 1i) * V) == (1. + 1i) * V); static_assert(isq::voltage_phasor((2. + 2i) * V) - 1. * V == (1. + 2i) * V); static_assert((1. + 1i) * V * 2. == (2. + 2i) * V); static_assert((1. + 1i) * V * (2. * one) == (2. + 2i) * V); static_assert((2. + 2i) * V / 2. == (1. + 1i) * V); static_assert((2. + 2i) * V / (2. * one) == (1. + 1i) * V); static_assert((1. + 1i) * V * (2. + 1i) == (1. + 1i) * (2. + 1i) * V); static_assert((2. + 2i) * V / (2. + 1i) == (2. + 2i) / (2. + 1i) * V); static_assert((1. + 1i) * V * (2. + 1i) * one == (1. + 1i) * (2. + 1i) * V); static_assert((2. + 2i) * V / (2. + 1i) * one == (2. + 2i) / (2. + 1i) * V); static_assert((1. + 1i) * V * (2. + 1i) * A == (1. + 1i) * (2. + 1i) * V * A); static_assert((2. + 2i) * V / ((2. + 1i) * A) == (2. + 2i) / (2. + 1i) * V / A); ////////////////////// // vector quantities ////////////////////// static_assert(v{1., 2., 3.} * m + v{1., 2., 3.} * m == v{2., 4., 6.} * m); static_assert(v{1000., 2000., 3000.} * m + v{1., 2., 3.} * km == v{2000., 4000., 6000.} * m); static_assert(v{2., 4., 6.} * m - v{1., 2., 3.} * m == v{1., 2., 3.} * m); static_assert(v{2000., 4000., 6000.} * m - v{1., 2., 3.} * km == v{1000., 2000., 3000.} * m); static_assert(v{1., 2., 3.} * m + isq::displacement(v{1., 2., 3.} * m) == v{2., 4., 6.} * m); static_assert(v{2., 4., 6.} * m - isq::displacement(v{1., 2., 3.} * m) == v{1., 2., 3.} * m); static_assert(v{1., 2., 3.} * m * 2. == v{2., 4., 6.} * m); static_assert(v{1., 2., 3.} * m * (2. * one) == v{2., 4., 6.} * m); static_assert(v{1., 2., 3.} * m * (2. * s) == v{2., 4., 6.} * m * s); static_assert(v{2., 4., 6.} * m / 2. == v{1., 2., 3.} * m); static_assert(v{2., 4., 6.} * m / (2. * one) == v{1., 2., 3.} * m); static_assert(v{2., 4., 6.} * m / (2. * s) == v{1., 2., 3.} * m / s); #endif /////////////////////// // 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 == 123'000 * m); static_assert(321 * km != 123'000 * m); static_assert(!(123 * km == 321'000 * m)); static_assert(!(123 * km != 123'000 * m)); // Named and derived dimensions (same units) static_assert(10 * isq::length[m] / (2 * isq::time[s]) == 5 * isq::speed[m / s]); static_assert(5 * isq::speed[m / s] == 10 * isq::length[m] / (2 * isq::time[s])); // Same named dimension & different but equivalent unit static_assert(10 * isq::frequency[one / s] == 10 * isq::frequency[Hz]); static_assert(10 * isq::frequency[Hz] == 10 * isq::frequency[one / s]); // Named and derived dimensions (different but equivalent units) static_assert(10 / (2 * isq::time[s]) == 5 * isq::frequency[Hz]); static_assert(5 * isq::frequency[Hz] == 10 / (2 * isq::time[s])); static_assert(5 * isq::force[N] * (2 * isq::length[m]) == 10 * isq::mechanical_energy[J]); static_assert(10 * isq::mechanical_energy[J] == 5 * isq::force[N] * (2 * isq::length[m])); // Physical constants static_assert(1 * si::si2019::speed_of_light_in_vacuum == 299'792'458 * isq::speed[m / s]); // Different named dimensions template constexpr bool invalid_comparison = !requires { 2 * R1 == 2 * R2; } && !requires { 2 * R2 == 2 * R1; }; static_assert(invalid_comparison); #if MP_UNITS_HOSTED static_assert((1. + 1i) * one == 1. + 1i); static_assert(1. + 1i != (2. + 2i) * one); static_assert(v{1., 2., 3.} * one == v{1., 2., 3.}); static_assert(v{1., 2., 3.} != v{3., 2., 1.} * one); #endif /////////////////////// // 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 < 321'000 * m); static_assert(123 * km <= 123'000 * m); static_assert(123 * km <= 321'000 * m); static_assert(321 * km > 123'000 * m); static_assert(123 * km >= 123'000 * m); static_assert(321 * km >= 123'000 * m); static_assert(!(321 * km < 123'000 * m)); static_assert(!(123 * km < 123'000 * m)); static_assert(!(321 * km <= 123'000 * m)); static_assert(!(123 * km > 321'000 * m)); static_assert(!(123 * km > 123'000 * m)); static_assert(!(123 * km >= 321'000 * m)); static_assert(1 * one < 2); static_assert(1 < 2 * one); ////////////////// // dimensionless ////////////////// static_assert(is_of_type<10 * km / (5 * km), quantity>); static_assert((50. * m / (100. * m)).numerical_value_in(percent) == 50); static_assert(50. * m / (100. * m) == 50 * percent); static_assert((50. * percent).numerical_value_in(one) == 0.5); ////////////////// // common_type ////////////////// static_assert( is_same_v, quantity<(isq::length / isq::time)[m / s], double>>, quantity>); static_assert(is_same_v(isq::length / isq::time))[J], double>, quantity>, quantity>); static_assert(is_same_v, int>, quantity>); static_assert(is_same_v, double>, quantity>); static_assert(is_same_v, int>, quantity>); static_assert(is_same_v, int>, quantity>); static_assert(is_same_v, double>, quantity>); static_assert(is_same_v, int>, quantity>); ////////////////// // value_cast ////////////////// static_assert(value_cast(2 * km).numerical_value_in(m) == 2000); static_assert(value_cast(2000 * m).numerical_value_in(km) == 2); static_assert(value_cast(2000.0 * m / (3600.0 * s)).numerical_value_in(km / h) == 2); static_assert(value_cast(1.23 * m).numerical_value_in(m) == 1); static_assert(value_cast(1.23 * m).numerical_value_in(km) == 0); static_assert(value_cast(1.23 * m).numerical_value_in(km) == 0); static_assert((2 * km).force_in(m).numerical_value_in(m) == 2000); static_assert((2000 * m).force_in(km).numerical_value_in(km) == 2); static_assert((2000.0 * m / (3600.0 * s)).force_in(km / h).numerical_value_in(km / h) == 2); static_assert((1.23 * m).force_in().numerical_value_in(m) == 1); static_assert((1.23 * m).force_in(km).numerical_value_in(km) == 0); ////////////////// // quantity_cast ////////////////// static_assert(is_of_type(1 * m), quantity>); static_assert(is_of_type(isq::length(1 * m)), quantity>); static_assert(is_of_type>(isq::length(1 * m)), quantity>); static_assert(is_of_type>(isq::distance(1 * m)), quantity>); static_assert(is_of_type(1. * isq::angular_measure[one]), quantity>); static_assert( is_of_type((1. * isq::angular_measure[rad]).in(one)), quantity>); static_assert(is_of_type((1. * rad).in(one)), quantity>); // lvalue references in quantity_cast namespace lvalue_tests { constexpr quantity lvalue_q = 1 * m; static_assert(is_of_type(lvalue_q), quantity>); } // namespace lvalue_tests template typename Q> concept invalid_quantity_cast = requires { requires !requires { quantity_cast(Q(42. * rad)); }; requires !requires { quantity_cast(Q(42. * rad)); }; }; static_assert(invalid_quantity_cast); // QuantityOf static_assert(QuantityOf, isq::length>); static_assert(QuantityOf, isq::length>); static_assert(QuantityOf, isq::length>); static_assert(!QuantityOf, isq::width>); static_assert(QuantityOf, isq::width>); static_assert(QuantityOf, isq::position_vector>); static_assert(QuantityOf[m]>, isq::width>); static_assert(QuantityOf[m]>, isq::position_vector>); static_assert(!QuantityOf, isq::altitude>); static_assert(QuantityOf, isq::speed>); static_assert(QuantityOf, isq::length / isq::time>); static_assert(QuantityOf, isq::length / isq::time>); static_assert(QuantityOf[m / s]>, isq::length / isq::time>); static_assert(!QuantityOf, isq::distance / isq::duration>); static_assert(!QuantityOf, isq::width / isq::duration>); static_assert(QuantityOf, isq::width / isq::duration>); static_assert(QuantityOf[m / s]>, isq::width / isq::duration>); static_assert(!QuantityOf, isq::displacement / isq::duration>); static_assert(QuantityOf, isq::displacement / isq::duration>); static_assert(QuantityOf[m / s]>, isq::displacement / isq::duration>); static_assert(QuantityOf, isq::displacement / isq::duration>); static_assert(QuantityOf); // kind of static_assert(QuantityOf[m]), isq::height>); // kind of static_assert(!QuantityOf); // different kinds static_assert(!QuantityOf); // different kinds static_assert(QuantityOf); static_assert(QuantityOf); // derived unnamed quantity // overflowing unit conversions template concept overflowing_unit_conversion = requires { requires !requires { quantity(Q); }; requires !requires { quantity, std::int16_t>(Q); }; requires !requires { Q.in(si::metre); }; requires !requires { Q.force_in(si::metre); }; requires !requires { Q + std::int8_t(1) * nm; }; // promotion to int requires !requires { Q - std::int8_t(1) * nm; }; // promotion to int requires !requires { Q % std::int8_t(1) * m; }; requires !requires { Q == std::int8_t(1) * m; }; requires !requires { Q < std::int8_t(1) * m; }; requires !requires { typename std::common_type_t>; }; }; static_assert(overflowing_unit_conversion); } // namespace