From 7ce8173b402f4bc4c4ef85585c676c85ae2d5d6f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 1 Aug 2023 12:04:31 +0200 Subject: [PATCH] test: initial version of `quantity_point_test` added --- test/unit_test/static/quantity_point_test.cpp | 995 +++++++++++++----- 1 file changed, 751 insertions(+), 244 deletions(-) diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index cd57a32d5..401e39cb8 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -21,308 +21,815 @@ // SOFTWARE. #include "test_tools.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include namespace { -using namespace units; -using namespace isq::si; -using namespace references; +using namespace mp_units; +using namespace mp_units::si::unit_symbols; using namespace std::chrono_literals; using sys_seconds = std::chrono::time_point; -struct sea_level_origin : point_origin {}; +inline constexpr struct mean_sea_level : absolute_point_origin { +} mean_sea_level; +inline constexpr quantity_point ground_level{42 * isq::height[m]}; +inline constexpr quantity_point everest_base_camp{5364 * m}; + +QUANTITY_SPEC(special_height, isq::height); +QUANTITY_SPEC(activity, 1 / isq::time); + +///////////////////// // class invariants +///////////////////// -template +static_assert(sizeof(quantity_point) == sizeof(double)); +static_assert(sizeof(quantity_point) == sizeof(double)); +static_assert(sizeof(quantity_point{}, short>) == sizeof(short)); +static_assert(sizeof(quantity_point{}, short>) == sizeof(short)); + +template typename QP> concept invalid_types = requires { - // unit of a different dimension: - requires !requires { typename quantity_point, second, int>; }; - // quantity used as Rep: - requires !requires { typename quantity_point, metre, quantity>; }; - // quantity point used as Rep: - requires !requires { - typename quantity_point, metre, quantity_point, metre, int>>; - }; - // reordered arguments: - requires !requires { typename quantity_point, double>; }; - // dimension used as origin: - requires !requires { typename quantity_point; }; + // unit of a different dimension + requires !requires { typename QP{}, int>; }; + requires !requires { typename QP>{}, int>; }; + // not compatible quantity_spec + requires !requires { typename QP{}, int>; }; + requires !requires { typename QP{}, int>; }; + // quantity used as Rep + requires !requires { typename QP{}, quantity>; }; + // quantity point used as Rep + requires !requires { typename QP{}, quantity_point>; }; + // reordered arguments + requires !requires { typename QP{}, si::metre, double>; }; + // quantity_spec used as origin + requires !requires { typename QP; }; + // quantity_spec used as a reference + requires !requires { typename QP{}, int>; }; + // dimension used as a reference + requires !requires { typename QP{}, int>; }; + // bool used as a representation type + requires !requires { typename QP{}, bool>; }; + // incompatible quantity_spec in the origin and quantity_point + requires !requires { typename QP{}, int>; }; + requires !requires { typename QP; }; + requires !requires { typename QP; }; }; +static_assert(invalid_types); + +template typename QP> +concept valid_types = requires { + typename QP>{}>; + typename QP{}>; + typename QP{}>; + typename QP{}>; + typename QP{}>; + typename QP>{}>; + typename QP>{}>; + typename QP{}>; + typename QP; + typename QP; + typename QP; + typename QP; + typename QP; + typename QP; +}; +static_assert(valid_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_point::reference == si::metre); +static_assert(quantity_point::quantity_spec == kind_of); +static_assert(quantity_point::dimension == isq::dim_length); +static_assert(quantity_point::unit == si::metre); +static_assert(is_of_type::point_origin, absolute_point_origin>>); + +static_assert(quantity_point::reference == isq::length[m]); +static_assert(quantity_point::quantity_spec == isq::length); +static_assert(quantity_point::dimension == isq::dim_length); +static_assert(quantity_point::unit == si::metre); +static_assert(is_of_type::point_origin, absolute_point_origin>); +static_assert(is_of_type::absolute_point_origin, absolute_point_origin>); + +static_assert(quantity_point::reference == isq::height[m]); +static_assert(quantity_point::quantity_spec == isq::height); +static_assert(quantity_point::dimension == isq::dim_length); +static_assert(quantity_point::unit == si::metre); +static_assert(is_of_type::point_origin, struct mean_sea_level>); +static_assert(is_of_type::absolute_point_origin, struct mean_sea_level>); + +static_assert(quantity_point::reference == isq::height[m]); +static_assert(quantity_point::quantity_spec == isq::height); +static_assert(quantity_point::dimension == isq::dim_length); +static_assert(quantity_point::unit == si::metre); +static_assert( + is_of_type::point_origin, std::remove_const_t>); +static_assert(is_of_type::absolute_point_origin, struct mean_sea_level>); -static_assert(invalid_types); +////////////////// // member types +////////////////// + +static_assert(is_same_v::rep, double>); +static_assert(is_same_v::quantity_type, quantity>); + +static_assert(is_same_v{}, int>::rep, int>); +static_assert(is_same_v{}, int>::quantity_type, + quantity>); + +static_assert(is_same_v::rep, double>); +static_assert(is_same_v::quantity_type, quantity>); + +static_assert(is_same_v{}, int>::rep, int>); +static_assert(is_same_v{}, int>::quantity_type, + quantity>); + + +//////////////////////////// +// static member functions +//////////////////////////// + +static_assert(quantity_point::zero().relative().number() == 0); +static_assert(quantity_point::min().relative().number() == std::numeric_limits::lowest()); +static_assert(quantity_point::max().relative().number() == std::numeric_limits::max()); +static_assert(quantity_point{}, int>::zero().relative().number() == + 0); +static_assert(quantity_point{}, int>::min().relative().number() == + std::numeric_limits::lowest()); +static_assert(quantity_point{}, int>::max().relative().number() == + std::numeric_limits::max()); + + +////////////////////////////// +// construction from a value +////////////////////////////// + +// construction from a value is private +static_assert(!std::constructible_from, double>); +static_assert(!std::convertible_to>); + +static_assert(!std::constructible_from{}, int>, int>); +static_assert(!std::convertible_to{}, int>>); + +static_assert(!std::constructible_from, double>); +static_assert(!std::convertible_to>); -static_assert(is_same_v, metre, int>::rep, int>); -static_assert(is_same_v, metre, double>::rep, double>); -static_assert(is_same_v, metre, int>::unit, metre>); -static_assert(is_same_v, kilometre, int>::unit, kilometre>); -static_assert(is_same_v, metre, int>::dimension, dim_length>); -static_assert(is_same_v, metre, int>::origin, dynamic_origin>); static_assert( - is_same_v, metre, int>::quantity_type, quantity>); + !std::constructible_from{}, int>, int>); +static_assert( + !std::convertible_to{}, int>>); +static_assert(!std::constructible_from, double>); +static_assert(!std::convertible_to>); +static_assert( + !std::constructible_from{}, int>, int>); +static_assert( + !std::convertible_to{}, int>>); + + +///////////////////////////////// +// construction from a quantity +///////////////////////////////// + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); -// constructors +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); -static_assert(quantity_point(1).relative() == quantity(1)); -static_assert(!std::is_convertible_v, one, int>>); +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +// different dimensions +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +// non-convertible quantity_specs +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +// quantity-like +static_assert(std::constructible_from, std::chrono::seconds>); +static_assert(!std::convertible_to>); + + +/////////////////////////////////////// +// construction from a quantity point +/////////////////////////////////////// + +// same origins +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +// different dimensions +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +// non-convertible quantity_specs +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +// implicit conversion from another quantity only if non-truncating +// int -> double OK +static_assert(std::constructible_from, + quantity_point{}, int>>); +static_assert(std::convertible_to{}, int>, + quantity_point>); + +// truncating double -> int not allowed +static_assert(!std::constructible_from{}, int>, + quantity_point>); +static_assert(!std::convertible_to, + quantity_point{}, int>>); + +// kilometre -> metre OK +static_assert(std::constructible_from{}, int>, + quantity_point{}, int>>); +static_assert(std::convertible_to{}, int>, + quantity_point{}, int>>); + +// truncating metre -> kilometre not allowed +static_assert(!std::constructible_from{}, int>, + quantity_point{}, int>>); +static_assert(!std::convertible_to{}, int>, + quantity_point{}, int>>); + +// converting to double always OK +static_assert(std::constructible_from, + quantity_point{}, int>>); +static_assert(std::convertible_to{}, int>, + quantity_point>); +static_assert(std::constructible_from, + quantity_point{}, int>>); +static_assert(std::convertible_to{}, int>, + quantity_point>); + +// same but not a default origin +static_assert(std::constructible_from, + quantity_point>); +static_assert( + std::convertible_to, quantity_point>); -static_assert(quantity_point(42s).relative() == 42 * s); -static_assert(quantity_point(sys_seconds{42s}).relative() == 42 * s); -static_assert(!std::is_convertible_v, second, std::chrono::seconds::rep>>); static_assert( - !std::is_convertible_v, second, std::chrono::seconds::rep>>); -static_assert(!std::is_convertible_v, second, sys_seconds::rep>>); -static_assert(!std::is_convertible_v< - sys_seconds, quantity_point, second, sys_seconds::rep>>); - -static_assert(quantity_point, metre, int>().relative() == 0_q_m); -constexpr quantity_point, metre, int> km{1000_q_m}; -static_assert(km.relative() == 1000_q_m); -static_assert(quantity_point, metre, int>(km).relative() == km.relative()); - -static_assert(quantity_point, metre, int>(1_q_m).relative() == 1_q_m); + std::constructible_from, quantity_point>); static_assert( - !std::is_constructible_v, metre, int>, double>); // truncating conversion -static_assert(quantity_point, metre, double>(1.0_q_m).relative() == 1.0_q_m); -static_assert(quantity_point, metre, double>(1_q_m).relative() == 1_q_m); -static_assert(quantity_point, metre, long double>(3.14_q_m).relative() == 3.14_q_m); + std::convertible_to, quantity_point>); -static_assert(quantity_point, metre, int>(km).relative() == 1000_q_m); static_assert( - !std::is_constructible_v, metre, int>, - quantity_point, metre, double>>); // truncating conversion -static_assert(quantity_point, metre, double>(quantity_point(1000.0_q_m)).relative() == - 1000.0_q_m); -static_assert(quantity_point, metre, double>(km).relative() == 1000.0_q_m); -static_assert(quantity_point, metre, int>(quantity_point(1_q_km)).relative() == 1000_q_m); -static_assert(!std::is_constructible_v, metre, int>, - quantity_point, second, int>>); // different dimensions + std::constructible_from, quantity_point>); static_assert( - !std::is_constructible_v, kilometre, int>, - quantity_point, metre, int>>); // truncating conversion + std::convertible_to, quantity_point>); -static_assert(!std::is_constructible_v, - quantity_point, metre, int>>, - "non-equivalent origins"); -static_assert(!std::is_constructible_v, second, int>, sys_seconds>, - "non-equivalent origins, no implicit conversion from `clock_origin`"); +static_assert(std::constructible_from, + quantity_point>); +static_assert(std::convertible_to, + quantity_point>); +static_assert(!std::constructible_from, + quantity_point>); +static_assert(!std::convertible_to, + quantity_point>); + +// different origins +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +// quantity-point-like +static_assert( + std::constructible_from>, sys_seconds>); +static_assert( + !std::convertible_to>>); + +// incompatible origin +static_assert(!std::constructible_from, sys_seconds>); +static_assert(!std::convertible_to>); + + +////////////////////////////////// +// obtaining a relative quantity +////////////////////////////////// + +static_assert(quantity_point(42 * m).relative() == 42 * m); +static_assert(quantity_point(isq::height(42 * m)).relative() == 42 * m); + +static_assert(quantity_point(1 * one).relative() == 1 * one); +static_assert(quantity_point(dimensionless(1 * one)).relative() == 1 * one); + +static_assert(quantity_point(42 * m).relative() == 42 * m); +static_assert(quantity_point(42 * m).relative() == 42 * m); + +static_assert(quantity_point(quantity_point(42 * m)) + .relative() == 84 * m); +static_assert(quantity_point(quantity_point(84 * m)) + .relative() == 42 * m); + + +/////////////////////////////////// +// obtaining an absolute quantity +/////////////////////////////////// + +static_assert(quantity_point(42 * m).absolute() == 42 * m); +static_assert(quantity_point(isq::height(42 * m)).absolute() == 42 * m); + +static_assert(quantity_point(1 * one).absolute() == 1 * one); +static_assert(quantity_point(dimensionless(1 * one)).absolute() == 1 * one); + +static_assert(quantity_point(42 * m).absolute() == 42 * m); +static_assert(quantity_point(42 * m).absolute() == 84 * m); + +static_assert(quantity_point(quantity_point(42 * m)) + .absolute() == 84 * m); +static_assert(quantity_point(quantity_point(84 * m)) + .absolute() == 84 * m); + + +/////////////////////////////////// +// converting to a different unit +/////////////////////////////////// + +static_assert(quantity_point(2. * km)[km].relative().number() == 2.); +static_assert(quantity_point(2. * km)[m].relative().number() == 2000.); +static_assert(quantity_point(2000. * m)[km].relative().number() == 2.); +static_assert( + quantity_point{}, int>(2 * km)[km].relative().number() == 2); +static_assert( + quantity_point{}, int>(2 * km)[m].relative().number() == 2000); + +#if MP_UNITS_COMP_GCC != 10 || MP_UNITS_COMP_GCC_MINOR > 2 +template typename QP> +concept invalid_unit_conversion = requires { + requires !requires { + QP{}, int>(2000 * m)[km]; + }; // truncating conversion + requires !requires { QP{}, int>(2 * m)[s]; }; // invalid unit +}; +static_assert(invalid_unit_conversion); +#endif + + +///////// +// CTAD +///////// + +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(quantity_point{123. * m}.unit == si::metre); +static_assert(quantity_point{123. * m}.quantity_spec == kind_of); +static_assert(quantity_point{123. * h}.unit == si::hour); +static_assert(quantity_point{123. * h}.quantity_spec == kind_of); + +using namespace std::chrono_literals; +static_assert(std::is_same_v); +static_assert(std::is_same_v, + chrono_point_origin_>); +static_assert(quantity_point{sys_seconds{24h}}.unit == si::second); +static_assert(quantity_point{sys_seconds{24h}}.quantity_spec == kind_of); + + +//////////////////////// // assignment operator +//////////////////////// static_assert(([]() { - quantity_point, metre, int> l1(1_q_m), l2{}; + quantity_point l1{1 * m}, l2{2 * m}; return l2 = l1; }()) - .relative() == 1_q_m); - -// static member functions + .relative() == 1 * m); +static_assert(([]() { + const quantity_point l1{1 * m}; + quantity_point l2{2 * m}; + return l2 = l1; + }()) + .relative() == 1 * m); +static_assert(([]() { + quantity_point l1{1 * m}, l2{2 * m}; + return l2 = std::move(l1); + }()) + .relative() == 1 * m); -static_assert(quantity_point, metre, int>::min().relative().number() == - std::numeric_limits::lowest()); -static_assert(quantity_point, metre, int>::max().relative().number() == - std::numeric_limits::max()); -static_assert(quantity_point, metre, double>::min().relative().number() == - std::numeric_limits::lowest()); -static_assert(quantity_point, metre, double>::max().relative().number() == - std::numeric_limits::max()); -// unary member operators +//////////////////// +// unary operators +//////////////////// static_assert([](auto v) { auto vv = v++; return std::pair(v, vv); -}(km) == std::pair(quantity_point, metre, int>(1001_q_m), - quantity_point, metre, int>(1000_q_m))); +}(quantity_point(123 * m)) == std::pair(quantity_point(124 * m), quantity_point(123 * m))); static_assert([](auto v) { auto vv = ++v; return std::pair(v, vv); -}(km) == std::pair(quantity_point, metre, int>(1001_q_m), - quantity_point, metre, int>(1001_q_m))); +}(quantity_point(123 * m)) == std::pair(quantity_point(124 * m), quantity_point(124 * m))); static_assert([](auto v) { auto vv = v--; return std::pair(v, vv); -}(km) == std::pair(quantity_point, metre, int>(999_q_m), - quantity_point, metre, int>(1000_q_m))); +}(quantity_point(123 * m)) == std::pair(quantity_point(122 * m), quantity_point(123 * m))); static_assert([](auto v) { auto vv = --v; return std::pair(v, vv); -}(km) == std::pair(quantity_point, metre, int>(999_q_m), - quantity_point, metre, int>(999_q_m))); +}(quantity_point(123 * m)) == std::pair(quantity_point(122 * m), quantity_point(122 * m))); -// compound assignment - -static_assert((quantity_point(1_q_m) += 1_q_m).relative().number() == 2); -static_assert((quantity_point(2_q_m) -= 1_q_m).relative().number() == 1); - -// non-member arithmetic operators - -static_assert(compare, metre, int>() + length()), - quantity_point, metre, double>>); -static_assert(compare() + quantity_point, metre, double>()), - quantity_point, metre, double>>); -static_assert(compare, kilometre, int>() + length()), - quantity_point, metre, double>>); -static_assert(compare() + quantity_point, metre, double>()), - quantity_point, metre, double>>); -static_assert(compare, metre, double>() - length()), - quantity_point, metre, double>>); -static_assert(compare, kilometre, double>() - length()), - quantity_point, metre, double>>); -static_assert(compare, metre, double>() - - quantity_point, metre, int>()), - length>); -static_assert(compare, kilometre, double>() - - quantity_point, metre, int>()), - length>); - -static_assert((1_q_m + km).relative().number() == 1001); -static_assert((quantity_point(1_q_m) + 1_q_km).relative().number() == 1001); -static_assert((km - 1_q_m).relative().number() == 999); -static_assert((quantity_point(1_q_km) - quantity_point(1_q_m)).number() == 999); - -template -concept invalid_subtraction = - requires(quantity_point, metre, Int> lhs, - quantity_point rhs) { requires !requires { rhs - lhs; }; }; -static_assert(invalid_subtraction); - -// comparators - -static_assert(quantity_point(2_q_m) + 1_q_m == quantity_point(3_q_m)); -static_assert(!(2_q_m + quantity_point(2_q_m) == quantity_point(3_q_m))); -static_assert(quantity_point(2_q_m) + 2_q_m != quantity_point(3_q_m)); -static_assert(!(2_q_m + quantity_point(2_q_m) != quantity_point(4_q_m))); -static_assert(quantity_point(2_q_m) > quantity_point(1_q_m)); -static_assert(!(quantity_point(1_q_m) > quantity_point(1_q_m))); -static_assert(quantity_point(1_q_m) < quantity_point(2_q_m)); -static_assert(!(quantity_point(2_q_m) < quantity_point(2_q_m))); -static_assert(quantity_point(2_q_m) >= quantity_point(1_q_m)); -static_assert(quantity_point(2_q_m) >= quantity_point(2_q_m)); -static_assert(!(quantity_point(2_q_m) >= quantity_point(3_q_m))); -static_assert(quantity_point(1_q_m) <= quantity_point(2_q_m)); -static_assert(quantity_point(2_q_m) <= quantity_point(2_q_m)); -static_assert(!(quantity_point(3_q_m) <= quantity_point(2_q_m))); - -static_assert(quantity_point(3_q_m) == quantity_point(3.0_q_m)); -static_assert(quantity_point(3_q_m) != quantity_point(3.14_q_m)); -static_assert(quantity_point(2_q_m) > quantity_point(1.0_q_m)); -static_assert(quantity_point(1.0_q_m) < quantity_point(2_q_m)); -static_assert(quantity_point(2.0_q_m) >= quantity_point(1_q_m)); -static_assert(quantity_point(1_q_m) <= quantity_point(2.0_q_m)); - -static_assert(quantity_point(1000_q_m) == quantity_point(1_q_km)); -static_assert(quantity_point(1001_q_m) != quantity_point(1_q_km)); -static_assert(quantity_point(1001_q_m) > quantity_point(1_q_km)); -static_assert(quantity_point(999_q_m) < quantity_point(1_q_km)); -static_assert(quantity_point(1000_q_m) >= quantity_point(1_q_km)); -static_assert(quantity_point(1000_q_m) <= quantity_point(1_q_km)); - -template -concept invalid_comparisons = requires(quantity_point, metre, Int> lhs, - quantity_point rhs) { - requires !requires { lhs == rhs; }; - requires !requires { lhs < rhs; }; -}; -static_assert(invalid_comparisons); - -// alias units - -static_assert(quantity_point(2_q_l) + 2_q_ml == quantity_point(2002_q_ml)); -static_assert(2_q_l + quantity_point(2_q_ml) == quantity_point(2002_q_cm3)); -static_assert(quantity_point(2_q_l) + 2_q_cm3 == quantity_point(2002_q_ml)); -static_assert(2_q_dm3 + quantity_point(2_q_cm3) == quantity_point(2002_q_ml)); - -// is_quantity_point - -static_assert(QuantityPoint, millimetre, int>>); - -// common_type - -static_assert(compare, metre, int>, - quantity_point, kilometre, int>>, - quantity_point, metre, int>>); -static_assert(compare, kilometre, long long>, - quantity_point, metre, int>>, - quantity_point, metre, long long>>); -static_assert(compare, kilometre, long long>, - quantity_point, millimetre, double>>, - quantity_point, millimetre, double>>); - -// common_type - -using namespace units::isq::si::uscs::literals; - -static_assert(std::equality_comparable); -static_assert(std::equality_comparable_with); -static_assert(quantity_point(0_q_m) == quantity_point(0_q_ft_us)); -static_assert(std::equality_comparable_with); - -// quantity_cast - -static_assert(quantity_point_cast, metre, int>>(quantity_point(2_q_km)) - .relative() - .number() == 2000); -static_assert(quantity_point_cast, kilometre, int>>(quantity_point(2000_q_m)) - .relative() - .number() == 2); -static_assert(quantity_point_cast, metre, int>>(quantity_point(1.23_q_m)) - .relative() - .number() == 1); -static_assert(quantity_point_cast>(quantity_point(2_q_km)).relative().number() == 2000); -static_assert(quantity_point_cast>(quantity_point(2000_q_m)).relative().number() == 2); -static_assert(quantity_point_cast>(quantity_point(1.23_q_m)).relative().number() == 1); -static_assert(quantity_point_cast(quantity_point(2_q_km)).relative().number() == 2000); -static_assert(quantity_point_cast(quantity_point(2000_q_m)).relative().number() == 2); -static_assert(quantity_point_cast(quantity_point(1.23_q_m)).relative().number() == 1); -static_assert( - quantity_point_cast(quantity_point(2000.0_q_m / 3600.0_q_s)).relative().number() == 2); -template -concept invalid_cast = requires(Int i) { - requires !requires { - quantity_point_cast, second, Int>>(quantity_point(i * m)); - }; - requires !requires { - quantity_point_cast, metre, Int>>( - quantity_point(i * m)); - }; +//////////////////////// +// compound assignment +//////////////////////// + +// same type +static_assert((quantity_point(1 * m) += 1 * m).relative().number() == 2); +static_assert((quantity_point(2 * m) -= 1 * m).relative().number() == 1); + +// different types +static_assert((quantity_point(2.5 * m) += 3 * m).relative().number() == 5.5); +static_assert((quantity_point(123 * m) += 1 * km).relative().number() == 1123); +static_assert((quantity_point(5.5 * m) -= 3 * m).relative().number() == 2.5); +static_assert((quantity_point(1123 * m) -= 1 * km).relative().number() == 123); + + +template typename QP> +concept invalid_compound_assignments = requires() { + // truncating not allowed + requires !requires(QP{}, int> l) { l += 2.5 * m; }; + requires !requires(QP{}, int> l) { l -= 2.5 * m; }; + requires !requires(QP{}, int> l) { l += 2 * isq::length[m]; }; + requires !requires(QP{}, int> l) { l -= 2 * isq::length[m]; }; + + // only quantities can be added or subtracted + requires !requires(QP{}, int> l) { l += 2; }; + requires !requires(QP{}, int> l) { l -= 2; }; + + // no unit constants + requires !requires(QP{}, int> l) { l += m; }; + requires !requires(QP{}, int> l) { l -= m; }; }; -static_assert(invalid_cast); - -// time +static_assert(invalid_compound_assignments); -static_assert(quantity_point{1_q_h} == quantity_point{3600_q_s}); -template -concept no_crossdimensional_equality = !requires { quantity_point(1_q_s) == quantity_point(length(1)); }; +//////////////////// +// binary operators +//////////////////// -static_assert(no_crossdimensional_equality); +template typename QP> +concept invalid_binary_operations = requires { + // can't add more generic quantity (violates point_origin quantity_spec) + requires !requires { QP(1 * m) + isq::length(1 * m); }; + requires !requires { isq::length(1 * m) + QP(1 * m); }; -// length + // can't subtract more generic quantity (violates point_origin quantity_spec) + requires !requires { QP(1 * m) - isq::length(1 * m); }; -static_assert(quantity_point(1_q_km) != quantity_point(1_q_m)); -static_assert(quantity_point(1_q_km) == quantity_point(1000_q_m)); -static_assert(quantity_point(1_q_km) + 1_q_m == quantity_point(1001_q_m)); -static_assert(1_q_km + quantity_point(1_q_m) == quantity_point(1001_q_m)); + // quantity point can't be subtracted from a quantity + requires !requires { 1 * m - QP(1 * m); }; -template -concept dimensional_analysis = requires(T t) { pow<2>(t); }; + // no crossdimensional addition and subtraction + requires !requires { QP(1 * m) + 1 * s; }; + requires !requires { QP(1 * m) - 1 * s; }; -static_assert(!dimensional_analysis, metre, int>>); + // unit constants + requires !requires { QP(1) + m; }; + requires !requires { QP(1) - m; }; + requires !requires { m + QP(1); }; + requires !requires { m - QP(1); }; +}; +static_assert(invalid_binary_operations); + + +// same representation type +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); + +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); + +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); + +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); + +static_assert(is_of_type<1 * m + quantity_point(1 * m), + quantity_point>{}, int>>); +static_assert(is_of_type<1 * m + quantity_point(1 * km), + quantity_point>{}, int>>); +static_assert(is_of_type<1 * km + quantity_point(1 * m), + quantity_point>{}, int>>); + +static_assert(is_of_type<1 * m + quantity_point(isq::length(1 * m)), + quantity_point{}, int>>); +static_assert(is_of_type<1 * m + quantity_point(isq::length(1 * km)), + quantity_point{}, int>>); +static_assert(is_of_type<1 * km + quantity_point(isq::length(1 * m)), + quantity_point{}, int>>); + +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); + +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); + +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); + +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, int>>); + +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); + +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); + +// check for integral types promotion +static_assert( + is_same_v); +static_assert( + is_same_v); +static_assert( + is_same_v); +static_assert((quantity_point{std::uint8_t(128) * m} + std::uint8_t(128) * m).relative().number() == + std::uint8_t(128) + std::uint8_t(128)); +static_assert((std::uint8_t(128) * m + quantity_point{std::uint8_t(128) * m}).relative().number() == + std::uint8_t(128) + std::uint8_t(128)); +static_assert((quantity_point{std::uint8_t(0) * m} - std::uint8_t(1) * m).relative().number() == + std::uint8_t(0) - std::uint8_t(1)); + +// different representation types +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type<1. * m + quantity_point{1 * m}, + quantity_point>{}, double>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type<1 * m + quantity_point{1. * km}, + quantity_point>{}, double>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type<1 * km + quantity_point{1. * m}, + quantity_point>{}, double>>); + +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type>{}, double>>); + +// different units +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type<1 * m + quantity_point{1 * km}, + quantity_point>{}, int>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type<1. * m + quantity_point{1 * km}, + quantity_point>{}, double>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type<1 * m + quantity_point{1. * km}, + quantity_point>{}, double>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type<1. * m + quantity_point{1. * km}, + quantity_point>{}, double>>); + +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type<1 * km + quantity_point{1 * m}, + quantity_point>{}, int>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type<1. * km + quantity_point{1 * m}, + quantity_point>{}, double>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type<1 * km + quantity_point{1. * m}, + quantity_point>{}, double>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type<1. * km + quantity_point{1. * m}, + quantity_point>{}, double>>); + +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type>{}, double>>); + +static_assert(is_of_type>{}, int>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type>{}, double>>); +static_assert(is_of_type>{}, double>>); + +static_assert((quantity_point{1 * m} + 1 * m).relative().number() == 2); +static_assert((1 * m + quantity_point{1 * m}).relative().number() == 2); +static_assert((quantity_point{1 * m} + 1 * km).relative().number() == 1001); +static_assert((1 * m + quantity_point{1 * km}).relative().number() == 1001); +static_assert((quantity_point{1 * km} + 1 * m).relative().number() == 1001); +static_assert((1 * km + quantity_point{1 * m}).relative().number() == 1001); +static_assert((quantity_point{2 * m} - 1 * m).relative().number() == 1); +static_assert((quantity_point{1 * km} - 1 * m).relative().number() == 999); + +static_assert((quantity_point{1.5 * m} + 1 * m).relative().number() == 2.5); +static_assert((1.5 * m + quantity_point{1 * m}).relative().number() == 2.5); +static_assert((quantity_point{1.5 * m} + 1 * km).relative().number() == 1001.5); +static_assert((1.5 * m + quantity_point{1 * km}).relative().number() == 1001.5); +static_assert((quantity_point{1.5 * km} + 1 * m).relative().number() == 1501); +static_assert((1.5 * km + quantity_point{1 * m}).relative().number() == 1501); +static_assert((quantity_point{2.5 * m} - 1 * m).relative().number() == 1.5); +static_assert((quantity_point{1.5 * km} - 1 * m).relative().number() == 1499); + +static_assert((quantity_point{1 * m} + 1.5 * m).relative().number() == 2.5); +static_assert((1 * m + quantity_point{1.5 * m}).relative().number() == 2.5); +static_assert((quantity_point{1 * m} + 1.5 * km).relative().number() == 1501); +static_assert((1 * m + quantity_point{1.5 * km}).relative().number() == 1501); +static_assert((quantity_point{1 * km} + 1.5 * m).relative().number() == 1001.5); +static_assert((1 * km + quantity_point{1.5 * m}).relative().number() == 1001.5); +static_assert((quantity_point{2 * m} - 1.5 * m).relative().number() == 0.5); +static_assert((quantity_point{1 * km} - 1.5 * m).relative().number() == 998.5); + +// commutativity and associativity +static_assert((quantity_point{10 * isq::length[si::metre] / (2 * isq::time[s])} + 5 * isq::speed[m / s]).relative() == + 10 * isq::speed[m / s]); +static_assert((10 * isq::length[si::metre] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}).relative() == + 10 * isq::speed[m / s]); +static_assert((quantity_point{5 * isq::speed[m / s]} + 10 * isq::length[m] / (2 * isq::time[s])).relative() == + 10 * isq::speed[m / s]); +static_assert((5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])}).relative() == + 10 * isq::speed[m / s]); +static_assert((quantity_point{10 * isq::length[m] / (2 * isq::time[s])} - 5 * isq::speed[m / s]).relative() == + 0 * isq::speed[m / s]); +static_assert((quantity_point{5 * isq::speed[m / s]} - 10 * isq::length[m] / (2 * isq::time[s])).relative() == + 0 * isq::speed[m / s]); + +// NOTE: quantity_spec of the origin is not "upgraded" to a better type +static_assert(is_of_type{}, int>>); +static_assert(is_of_type<10 * isq::length[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}, + quantity_point{}, int>>); +static_assert(is_of_type{}, int>>); +static_assert(is_of_type<5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])}, + quantity_point{}, int>>); +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); + +// NOTE: 1 / isq::time[s] works for quantities but not for quantity_point (origin can't be weakened) +static_assert((quantity_point{10 / (2 * isq::period_duration[s])} + 5 * isq::frequency[Hz]).relative() == + 10 * isq::frequency[Hz]); +static_assert((10 / (2 * isq::period_duration[s]) + quantity_point{5 * isq::frequency[Hz]}).relative() == + 10 * isq::frequency[Hz]); +static_assert((quantity_point{5 * isq::frequency[Hz]} + 10 / (2 * isq::period_duration[s])).relative() == + 10 * isq::frequency[Hz]); +static_assert((5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])}).relative() == + 10 * isq::frequency[Hz]); +static_assert((quantity_point{10 / (2 * isq::period_duration[s])} - 5 * isq::frequency[Hz]).relative() == + 0 * isq::frequency[Hz]); +static_assert((quantity_point{5 * isq::frequency[Hz]} - 10 / (2 * isq::period_duration[s])).relative() == + 0 * isq::frequency[Hz]); + +// NOTE: quantity_spec of the origin is not "upgraded" to a better type +static_assert(is_of_type{}, int>>); +static_assert(is_of_type<10 / (2 * isq::period_duration[s]) + quantity_point{5 * isq::frequency[Hz]}, + quantity_point{}, int>>); +static_assert(is_of_type{}, int>>); +static_assert(is_of_type<5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])}, + quantity_point{}, int>>); +static_assert(is_of_type{}, int>>); +static_assert(is_of_type{}, int>>); + +// Different named dimensions +template +consteval bool invalid_addition(Ts... ts) +{ + return !requires { (... + ts); }; +} + +template +consteval bool invalid_subtraction(Ts... ts) +{ + return !requires { (... - ts); }; +} + +static_assert(invalid_addition(quantity_point{5 * activity[Bq]}, 5 * isq::frequency[Hz])); +static_assert(invalid_addition(5 * activity[Bq], quantity_point{5 * isq::frequency[Hz]})); +static_assert(invalid_subtraction(quantity_point{5 * activity[Bq]}, 5 * isq::frequency[Hz])); + +static_assert(invalid_addition(quantity_point{5 * activity[Bq]}, 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz])); +static_assert(invalid_addition(5 * activity[Bq], quantity_point{10 / (2 * isq::time[s])}, 5 * isq::frequency[Hz])); +static_assert(invalid_addition(5 * activity[Bq], 10 / (2 * isq::time[s]), quantity_point{5 * isq::frequency[Hz]})); +static_assert(invalid_subtraction(quantity_point{5 * activity[Bq]}, 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz])); } // namespace