Files
mp-units/test/unit_test/static/quantity_point_test.cpp
T

900 lines
53 KiB
C++

// 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 <mp-units/chrono.h>
#include <mp-units/quantity_point.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/si.h>
#include <limits>
#include <type_traits>
#include <utility>
namespace {
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
using namespace std::chrono_literals;
using sys_seconds = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
inline constexpr struct mean_sea_level : absolute_point_origin<isq::height> {
} mean_sea_level;
inline constexpr struct ground_level : relative_point_origin<quantity_point{42 * isq::height[m], mean_sea_level}> {
} ground_level;
inline constexpr struct tower_peak : relative_point_origin<quantity_point{42 * isq::height[m], ground_level}> {
} tower_peak;
inline constexpr struct other_ground_level :
relative_point_origin<quantity_point{123 * isq::height[m], mean_sea_level}> {
} other_ground_level;
QUANTITY_SPEC(special_height, isq::height);
QUANTITY_SPEC(activity, 1 / isq::time);
/////////////////////
// class invariants
/////////////////////
static_assert(sizeof(quantity_point<si::metre>) == sizeof(double));
static_assert(sizeof(quantity_point<isq::length[m]>) == sizeof(double));
static_assert(sizeof(quantity_point<si::metre, absolute_point_origin<isq::length>{}, short>) == sizeof(short));
static_assert(sizeof(quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, short>) == sizeof(short));
template<template<auto, auto, typename> typename QP>
concept invalid_types = requires {
// unit of a different dimension
requires !requires { typename QP<si::second, absolute_point_origin<isq::length>{}, int>; };
requires !requires { typename QP<si::second, absolute_point_origin<kind_of<isq::length>>{}, int>; };
// not compatible quantity_spec
requires !requires { typename QP<isq::width[si::metre], absolute_point_origin<isq::height>{}, int>; };
requires !requires { typename QP<isq::width[m], absolute_point_origin<isq::thickness>{}, int>; };
// quantity used as Rep
requires !requires { typename QP<si::metre, absolute_point_origin<isq::length>{}, quantity<si::metre, int>>; };
// quantity point used as Rep
requires !requires { typename QP<si::metre, absolute_point_origin<isq::length>{}, quantity_point<si::metre>>; };
// reordered arguments
requires !requires { typename QP<absolute_point_origin<isq::length>{}, si::metre, double>; };
// quantity_spec used as origin
requires !requires { typename QP<si::metre, isq::length, int>; };
// quantity_spec used as a reference
requires !requires { typename QP<isq::length, absolute_point_origin<isq::length>{}, int>; };
// dimension used as a reference
requires !requires { typename QP<isq::dim_length, absolute_point_origin<isq::length>{}, int>; };
// bool used as a representation type
requires !requires { typename QP<si::metre, absolute_point_origin<isq::length>{}, bool>; };
// incompatible quantity_spec in the origin and quantity_point
requires !requires { typename QP<isq::length[m], absolute_point_origin<isq::height>{}, int>; };
requires !requires { typename QP<isq::length[m], mean_sea_level, int>; };
requires !requires { typename QP<isq::length[m], ground_level, int>; };
};
static_assert(invalid_types<quantity_point>);
template<template<auto, auto> typename QP>
concept valid_types = requires {
typename QP<si::metre, absolute_point_origin<kind_of<isq::length>>{}>;
typename QP<si::metre, absolute_point_origin<isq::length>{}>;
typename QP<si::metre, absolute_point_origin<isq::height>{}>;
typename QP<isq::length[m], absolute_point_origin<isq::length>{}>;
typename QP<isq::height[m], absolute_point_origin<isq::length>{}>;
typename QP<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}>;
typename QP<isq::height[m], absolute_point_origin<kind_of<isq::length>>{}>;
typename QP<isq::thickness[m], absolute_point_origin<isq::width>{}>;
typename QP<si::metre, mean_sea_level>;
typename QP<isq::height[m], mean_sea_level>;
typename QP<special_height[m], mean_sea_level>;
typename QP<si::metre, ground_level>;
typename QP<isq::height[m], ground_level>;
typename QP<special_height[m], ground_level>;
};
static_assert(valid_types<quantity_point>);
static_assert(std::is_trivially_default_constructible_v<quantity_point<si::metre>>);
static_assert(std::is_trivially_copy_constructible_v<quantity_point<si::metre>>);
static_assert(std::is_trivially_move_constructible_v<quantity_point<si::metre>>);
static_assert(std::is_trivially_copy_assignable_v<quantity_point<si::metre>>);
static_assert(std::is_trivially_move_assignable_v<quantity_point<si::metre>>);
static_assert(std::is_trivially_destructible_v<quantity_point<si::metre>>);
static_assert(std::is_nothrow_default_constructible_v<quantity_point<si::metre>>);
static_assert(std::is_nothrow_copy_constructible_v<quantity_point<si::metre>>);
static_assert(std::is_nothrow_move_constructible_v<quantity_point<si::metre>>);
static_assert(std::is_nothrow_copy_assignable_v<quantity_point<si::metre>>);
static_assert(std::is_nothrow_move_assignable_v<quantity_point<si::metre>>);
static_assert(std::is_nothrow_destructible_v<quantity_point<si::metre>>);
static_assert(std::is_trivially_copyable_v<quantity_point<si::metre>>);
static_assert(std::is_standard_layout_v<quantity_point<si::metre>>);
static_assert(std::default_initializable<quantity_point<si::metre>>);
static_assert(std::move_constructible<quantity_point<si::metre>>);
static_assert(std::copy_constructible<quantity_point<si::metre>>);
static_assert(std::equality_comparable<quantity_point<si::metre>>);
static_assert(std::totally_ordered<quantity_point<si::metre>>);
static_assert(std::regular<quantity_point<si::metre>>);
static_assert(std::three_way_comparable<quantity_point<si::metre>>);
//////////////////
// member values
//////////////////
static_assert(quantity_point<si::metre>::reference == si::metre);
static_assert(quantity_point<si::metre>::quantity_spec == kind_of<isq::length>);
static_assert(quantity_point<si::metre>::dimension == isq::dim_length);
static_assert(quantity_point<si::metre>::unit == si::metre);
static_assert(is_of_type<quantity_point<si::metre>::point_origin, absolute_point_origin<kind_of<isq::length>>>);
static_assert(quantity_point<isq::length[m]>::reference == isq::length[m]);
static_assert(quantity_point<isq::length[m]>::quantity_spec == isq::length);
static_assert(quantity_point<isq::length[m]>::dimension == isq::dim_length);
static_assert(quantity_point<isq::length[m]>::unit == si::metre);
static_assert(is_of_type<quantity_point<isq::length[m]>::point_origin, absolute_point_origin<isq::length>>);
static_assert(is_of_type<quantity_point<isq::length[m]>::absolute_point_origin, absolute_point_origin<isq::length>>);
static_assert(quantity_point<isq::height[m], mean_sea_level>::reference == isq::height[m]);
static_assert(quantity_point<isq::height[m], mean_sea_level>::quantity_spec == isq::height);
static_assert(quantity_point<isq::height[m], mean_sea_level>::dimension == isq::dim_length);
static_assert(quantity_point<isq::height[m], mean_sea_level>::unit == si::metre);
static_assert(is_of_type<quantity_point<isq::height[m], mean_sea_level>::point_origin, struct mean_sea_level>);
static_assert(is_of_type<quantity_point<isq::height[m], mean_sea_level>::absolute_point_origin, struct mean_sea_level>);
static_assert(quantity_point<isq::height[m], ground_level>::reference == isq::height[m]);
static_assert(quantity_point<isq::height[m], ground_level>::quantity_spec == isq::height);
static_assert(quantity_point<isq::height[m], ground_level>::dimension == isq::dim_length);
static_assert(quantity_point<isq::height[m], ground_level>::unit == si::metre);
static_assert(is_of_type<quantity_point<isq::height[m], ground_level>::point_origin, struct ground_level>);
static_assert(is_of_type<quantity_point<isq::height[m], ground_level>::absolute_point_origin, struct mean_sea_level>);
static_assert(quantity_point<isq::height[m], tower_peak>::reference == isq::height[m]);
static_assert(quantity_point<isq::height[m], tower_peak>::quantity_spec == isq::height);
static_assert(quantity_point<isq::height[m], tower_peak>::dimension == isq::dim_length);
static_assert(quantity_point<isq::height[m], tower_peak>::unit == si::metre);
static_assert(is_of_type<quantity_point<isq::height[m], tower_peak>::point_origin, struct tower_peak>);
static_assert(is_of_type<quantity_point<isq::height[m], tower_peak>::absolute_point_origin, struct mean_sea_level>);
//////////////////
// member types
//////////////////
static_assert(is_same_v<quantity_point<si::metre>::rep, double>);
static_assert(is_same_v<quantity_point<si::metre>::quantity_type, quantity<si::metre>>);
static_assert(is_same_v<quantity_point<si::metre, absolute_point_origin<isq::length>{}, int>::rep, int>);
static_assert(is_same_v<quantity_point<si::metre, absolute_point_origin<isq::length>{}, int>::quantity_type,
quantity<si::metre, int>>);
static_assert(is_same_v<quantity_point<isq::length[m]>::rep, double>);
static_assert(is_same_v<quantity_point<isq::length[m]>::quantity_type, quantity<isq::length[m]>>);
static_assert(is_same_v<quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>::rep, int>);
static_assert(is_same_v<quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>::quantity_type,
quantity<isq::length[m], int>>);
////////////////////////////
// static member functions
////////////////////////////
static_assert(quantity_point<isq::length[m]>::zero().relative().number() == 0);
static_assert(quantity_point<isq::length[m]>::min().relative().number() == std::numeric_limits<double>::lowest());
static_assert(quantity_point<isq::length[m]>::max().relative().number() == std::numeric_limits<double>::max());
static_assert(quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>::zero().relative().number() ==
0);
static_assert(quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>::min().relative().number() ==
std::numeric_limits<int>::lowest());
static_assert(quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>::max().relative().number() ==
std::numeric_limits<int>::max());
//////////////////////////////
// construction from a value
//////////////////////////////
// construction from a value is private
static_assert(!std::constructible_from<quantity_point<isq::length[m]>, double>);
static_assert(!std::convertible_to<double, quantity_point<isq::length[m]>>);
static_assert(!std::constructible_from<quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>, int>);
static_assert(!std::convertible_to<int, quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(!std::constructible_from<quantity_point<dimensionless[percent]>, double>);
static_assert(!std::convertible_to<double, quantity_point<dimensionless[percent]>>);
static_assert(
!std::constructible_from<quantity_point<dimensionless[percent], absolute_point_origin<dimensionless>{}, int>, int>);
static_assert(
!std::convertible_to<int, quantity_point<dimensionless[percent], absolute_point_origin<dimensionless>{}, int>>);
static_assert(!std::constructible_from<quantity_point<dimensionless[one]>, double>);
static_assert(!std::convertible_to<double, quantity_point<dimensionless[one]>>);
static_assert(
!std::constructible_from<quantity_point<dimensionless[one], absolute_point_origin<dimensionless>{}, int>, int>);
static_assert(
!std::convertible_to<int, quantity_point<dimensionless[one], absolute_point_origin<dimensionless>{}, int>>);
/////////////////////////////////
// construction from a quantity
/////////////////////////////////
static_assert(std::constructible_from<quantity_point<si::second>, quantity<si::second>>);
static_assert(!std::convertible_to<quantity<si::second>, quantity_point<si::second>>);
static_assert(std::constructible_from<quantity_point<isq::time[s]>, quantity<isq::time[s]>>);
static_assert(!std::convertible_to<quantity<isq::time[s]>, quantity_point<isq::time[s]>>);
static_assert(std::constructible_from<quantity_point<si::second>, quantity<isq::time[s]>>);
static_assert(!std::convertible_to<quantity<isq::time[s]>, quantity_point<si::second>>);
static_assert(std::constructible_from<quantity_point<isq::time[s]>, quantity<si::second>>);
static_assert(!std::convertible_to<quantity<si::second>, quantity_point<isq::time[s]>>);
static_assert(std::constructible_from<quantity_point<isq::length[m]>, quantity<isq::distance[m]>>);
static_assert(!std::convertible_to<quantity<isq::distance[m]>, quantity_point<isq::length[m]>>);
static_assert(std::constructible_from<quantity_point<dimensionless[one]>, quantity<dimensionless[one]>>);
static_assert(!std::convertible_to<quantity<dimensionless[one]>, quantity_point<dimensionless[one]>>);
// different dimensions
static_assert(!std::constructible_from<quantity_point<isq::distance[m]>, quantity<si::second>>);
static_assert(!std::convertible_to<quantity<si::second>, quantity_point<isq::distance[m]>>);
// non-convertible quantity_specs
static_assert(!std::constructible_from<quantity_point<isq::distance[m]>, quantity<isq::length[m]>>);
static_assert(!std::convertible_to<quantity<isq::length[m]>, quantity_point<isq::distance[m]>>);
// quantity-like
static_assert(std::constructible_from<quantity_point<isq::time[s]>, std::chrono::seconds>);
static_assert(!std::convertible_to<std::chrono::seconds, quantity_point<isq::time[s]>>);
///////////////////////////////////////
// construction from a quantity point
///////////////////////////////////////
// same origins
static_assert(std::constructible_from<quantity_point<si::metre>, quantity_point<si::metre>>);
static_assert(std::convertible_to<quantity_point<si::metre>, quantity_point<si::metre>>);
static_assert(std::constructible_from<quantity_point<isq::length[m]>, quantity_point<isq::length[m]>>);
static_assert(std::convertible_to<quantity_point<isq::length[m]>, quantity_point<isq::length[m]>>);
static_assert(std::constructible_from<quantity_point<si::metre>, quantity_point<isq::length[m]>>);
static_assert(std::convertible_to<quantity_point<isq::length[m]>, quantity_point<si::metre>>);
static_assert(std::constructible_from<quantity_point<isq::length[m]>, quantity_point<si::metre>>);
static_assert(std::convertible_to<quantity_point<si::metre>, quantity_point<isq::length[m]>>);
static_assert(std::constructible_from<quantity_point<isq::length[m]>, quantity_point<isq::distance[m]>>);
static_assert(std::convertible_to<quantity_point<isq::distance[m]>, quantity_point<isq::length[m]>>);
static_assert(std::constructible_from<quantity_point<dimensionless[one]>, quantity_point<dimensionless[one]>>);
static_assert(std::convertible_to<quantity_point<dimensionless[one]>, quantity_point<dimensionless[one]>>);
// different dimensions
static_assert(!std::constructible_from<quantity_point<isq::distance[m]>, quantity_point<si::second>>);
static_assert(!std::convertible_to<quantity_point<si::second>, quantity_point<isq::distance[m]>>);
// non-convertible quantity_specs
static_assert(!std::constructible_from<quantity_point<isq::distance[m]>, quantity_point<isq::length[m]>>);
static_assert(!std::convertible_to<quantity_point<isq::length[m]>, quantity_point<isq::distance[m]>>);
// implicit conversion from another quantity only if non-truncating
// int -> double OK
static_assert(std::constructible_from<quantity_point<isq::length[m]>,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(std::convertible_to<quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>,
quantity_point<isq::length[m]>>);
// truncating double -> int not allowed
static_assert(!std::constructible_from<quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>,
quantity_point<isq::length[m]>>);
static_assert(!std::convertible_to<quantity_point<isq::length[m]>,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
// kilometre<int> -> metre<int> OK
static_assert(std::constructible_from<quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>,
quantity_point<isq::length[km], absolute_point_origin<isq::length>{}, int>>);
static_assert(std::convertible_to<quantity_point<isq::length[km], absolute_point_origin<isq::length>{}, int>,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
// truncating metre<int> -> kilometre<int> not allowed
static_assert(!std::constructible_from<quantity_point<isq::length[km], absolute_point_origin<isq::length>{}, int>,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(!std::convertible_to<quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>,
quantity_point<isq::length[km], absolute_point_origin<isq::length>{}, int>>);
// converting to double always OK
static_assert(std::constructible_from<quantity_point<isq::length[m]>,
quantity_point<isq::length[km], absolute_point_origin<isq::length>{}, int>>);
static_assert(std::convertible_to<quantity_point<isq::length[km], absolute_point_origin<isq::length>{}, int>,
quantity_point<isq::length[m]>>);
static_assert(std::constructible_from<quantity_point<isq::length[km]>,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(std::convertible_to<quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>,
quantity_point<isq::length[km]>>);
// same but not a default origin
static_assert(std::constructible_from<quantity_point<isq::height[m], mean_sea_level>,
quantity_point<isq::height[m], mean_sea_level>>);
static_assert(
std::convertible_to<quantity_point<isq::height[m], mean_sea_level>, quantity_point<isq::height[m], mean_sea_level>>);
static_assert(
std::constructible_from<quantity_point<si::metre, mean_sea_level>, quantity_point<isq::height[m], mean_sea_level>>);
static_assert(
std::convertible_to<quantity_point<isq::height[m], mean_sea_level>, quantity_point<si::metre, mean_sea_level>>);
static_assert(
std::constructible_from<quantity_point<isq::height[m], mean_sea_level>, quantity_point<si::metre, mean_sea_level>>);
static_assert(
std::convertible_to<quantity_point<si::metre, mean_sea_level>, quantity_point<isq::height[m], mean_sea_level>>);
static_assert(std::constructible_from<quantity_point<isq::height[m], mean_sea_level>,
quantity_point<special_height[m], mean_sea_level>>);
static_assert(std::convertible_to<quantity_point<special_height[m], mean_sea_level>,
quantity_point<isq::height[m], mean_sea_level>>);
static_assert(!std::constructible_from<quantity_point<special_height[m], mean_sea_level>,
quantity_point<isq::height[m], mean_sea_level>>);
static_assert(!std::convertible_to<quantity_point<isq::height[m], mean_sea_level>,
quantity_point<special_height[m], mean_sea_level>>);
// different origins
static_assert(!std::constructible_from<quantity_point<isq::height[m]>, quantity_point<isq::height[m], mean_sea_level>>);
static_assert(!std::convertible_to<quantity_point<isq::height[m], mean_sea_level>, quantity_point<isq::height[m]>>);
static_assert(!std::constructible_from<quantity_point<isq::height[m], mean_sea_level>, quantity_point<isq::height[m]>>);
static_assert(!std::convertible_to<quantity_point<isq::height[m]>, quantity_point<isq::height[m], mean_sea_level>>);
// quantity-point-like
static_assert(
std::constructible_from<quantity_point<isq::time[s], chrono_point_origin<std::chrono::system_clock>>, sys_seconds>);
static_assert(
!std::convertible_to<sys_seconds, quantity_point<isq::time[s], chrono_point_origin<std::chrono::system_clock>>>);
// incompatible origin
static_assert(!std::constructible_from<quantity_point<isq::time[s]>, sys_seconds>);
static_assert(!std::convertible_to<sys_seconds, quantity_point<isq::time[s]>>);
//////////////////////////////////
// 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<isq::height[m], mean_sea_level>(42 * m).relative() == 42 * m);
static_assert(quantity_point<isq::height[m], ground_level>(42 * m).relative() == 42 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(42 * m).relative() == 42 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(quantity_point<isq::height[m], ground_level>(42 * m))
.relative() == 84 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(quantity_point<isq::height[m], tower_peak>(42 * m))
.relative() == 126 * m);
static_assert(quantity_point<isq::height[m], ground_level>(quantity_point<isq::height[m], mean_sea_level>(84 * m))
.relative() == 42 * m);
static_assert(quantity_point<isq::height[m], ground_level>(quantity_point<isq::height[m], tower_peak>(42 * m))
.relative() == 84 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(quantity_point<isq::height[m], mean_sea_level>(42 * m))
.relative() == -42 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(quantity_point<isq::height[m], ground_level>(84 * m))
.relative() == 42 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(42 * m).point_from(mean_sea_level).relative() == 42 * m);
static_assert(quantity_point<isq::height[m], ground_level>(42 * m).point_from(mean_sea_level).relative() == 84 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(42 * m).point_from(mean_sea_level).relative() == 126 * m);
static_assert(quantity_point<isq::height[m], ground_level>(84 * m).point_from(ground_level).relative() == 84 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(84 * m).point_from(ground_level).relative() == 42 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(42 * m).point_from(ground_level).relative() == 84 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(42 * m).point_from(tower_peak).relative() == 42 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(42 * m).point_from(tower_peak).relative() == -42 * m);
static_assert(quantity_point<isq::height[m], ground_level>(84 * m).point_from(tower_peak).relative() == 42 * m);
///////////////////////////////////
// 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<isq::height[m], mean_sea_level>(42 * m).absolute() == 42 * m);
static_assert(quantity_point<isq::height[m], ground_level>(42 * m).absolute() == 84 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(quantity_point<isq::height[m], ground_level>(42 * m))
.absolute() == 84 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(quantity_point<isq::height[m], tower_peak>(42 * m))
.absolute() == 126 * m);
static_assert(quantity_point<isq::height[m], ground_level>(quantity_point<isq::height[m], mean_sea_level>(84 * m))
.absolute() == 84 * m);
static_assert(quantity_point<isq::height[m], ground_level>(quantity_point<isq::height[m], tower_peak>(42 * m))
.absolute() == 126 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(quantity_point<isq::height[m], mean_sea_level>(42 * m))
.absolute() == 42 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(quantity_point<isq::height[m], ground_level>(84 * m))
.absolute() == 126 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(42 * m).point_from(mean_sea_level).absolute() == 42 * m);
static_assert(quantity_point<isq::height[m], ground_level>(42 * m).point_from(mean_sea_level).absolute() == 84 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(42 * m).point_from(mean_sea_level).absolute() == 126 * m);
static_assert(quantity_point<isq::height[m], ground_level>(42 * m).point_from(ground_level).absolute() == 84 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(84 * m).point_from(ground_level).absolute() == 84 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(42 * m).point_from(ground_level).absolute() == 126 * m);
static_assert(quantity_point<isq::height[m], tower_peak>(42 * m).point_from(tower_peak).absolute() == 126 * m);
static_assert(quantity_point<isq::height[m], mean_sea_level>(42 * m).point_from(tower_peak).absolute() == 42 * m);
static_assert(quantity_point<isq::height[m], ground_level>(84 * m).point_from(tower_peak).absolute() == 126 * m);
///////////////////////////////////
// converting to a different unit
///////////////////////////////////
static_assert(quantity_point<isq::length[km]>(2. * km)[km].relative().number() == 2.);
static_assert(quantity_point<isq::length[km]>(2. * km)[m].relative().number() == 2000.);
static_assert(quantity_point<isq::length[m]>(2000. * m)[km].relative().number() == 2.);
static_assert(
quantity_point<isq::length[km], absolute_point_origin<isq::length>{}, int>(2 * km)[km].relative().number() == 2);
static_assert(
quantity_point<isq::length[km], absolute_point_origin<isq::length>{}, int>(2 * km)[m].relative().number() == 2000);
#if MP_UNITS_COMP_GCC != 10 || MP_UNITS_COMP_GCC_MINOR > 2
template<template<auto, auto, typename> typename QP>
concept invalid_unit_conversion = requires {
requires !requires {
QP<isq::length[m], absolute_point_origin<isq::length>{}, int>(2000 * m)[km];
}; // truncating conversion
requires !requires { QP<isq::length[m], absolute_point_origin<isq::length>{}, int>(2 * m)[s]; }; // invalid unit
};
static_assert(invalid_unit_conversion<quantity_point>);
#endif
/////////
// CTAD
/////////
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{123 * m}.point_origin)>,
absolute_point_origin<kind_of<isq::length>>>);
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{123 * m, mean_sea_level}.point_origin)>,
std::remove_const_t<decltype(mean_sea_level)>>);
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{123 * m, ground_level}.point_origin)>,
std::remove_const_t<decltype(ground_level)>>);
static_assert(std::is_same_v<decltype(quantity_point{123 * m})::rep, int>);
static_assert(std::is_same_v<decltype(quantity_point{123. * m})::rep, double>);
static_assert(quantity_point{123. * m}.unit == si::metre);
static_assert(quantity_point{123. * m}.quantity_spec == kind_of<isq::length>);
static_assert(quantity_point{123. * h}.unit == si::hour);
static_assert(quantity_point{123. * h}.quantity_spec == kind_of<isq::time>);
using namespace std::chrono_literals;
static_assert(std::is_same_v<decltype(quantity_point{sys_seconds{123s}})::rep, std::chrono::seconds::rep>);
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{sys_seconds{123s}}.point_origin)>,
chrono_point_origin_<std::chrono::system_clock>>);
static_assert(quantity_point{sys_seconds{24h}}.unit == si::second);
static_assert(quantity_point{sys_seconds{24h}}.quantity_spec == kind_of<isq::time>);
////////////////////////
// assignment operator
////////////////////////
static_assert(([]() {
quantity_point l1{1 * m}, l2{2 * m};
return l2 = l1;
}())
.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);
////////////////////
// unary operators
////////////////////
static_assert([](auto v) {
auto vv = v++;
return std::pair(v, vv);
}(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);
}(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);
}(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);
}(quantity_point(123 * m)) == std::pair(quantity_point(122 * m), quantity_point(122 * 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<template<auto, auto, typename> typename QP>
concept invalid_compound_assignments = requires() {
// truncating not allowed
requires !requires(QP<isq::length[m], absolute_point_origin<isq::length>{}, int> l) { l += 2.5 * m; };
requires !requires(QP<isq::length[m], absolute_point_origin<isq::length>{}, int> l) { l -= 2.5 * m; };
requires !requires(QP<isq::length[km], absolute_point_origin<isq::length>{}, int> l) { l += 2 * isq::length[m]; };
requires !requires(QP<isq::length[km], absolute_point_origin<isq::length>{}, int> l) { l -= 2 * isq::length[m]; };
// only quantities can be added or subtracted
requires !requires(QP<isq::length[m], absolute_point_origin<isq::length>{}, int> l) { l += 2; };
requires !requires(QP<isq::length[m], absolute_point_origin<isq::length>{}, int> l) { l -= 2; };
// no unit constants
requires !requires(QP<isq::length[m], absolute_point_origin<isq::length>{}, int> l) { l += m; };
requires !requires(QP<isq::length[m], absolute_point_origin<isq::length>{}, int> l) { l -= m; };
};
static_assert(invalid_compound_assignments<quantity_point>);
////////////////////
// binary operators
////////////////////
template<template<auto> typename QP>
concept invalid_binary_operations = requires {
// can't add more generic quantity (violates point_origin quantity_spec)
requires !requires { QP<isq::height[m]>(1 * m) + isq::length(1 * m); };
requires !requires { isq::length(1 * m) + QP<isq::height[m]>(1 * m); };
// can't subtract more generic quantity (violates point_origin quantity_spec)
requires !requires { QP<isq::height[m]>(1 * m) - isq::length(1 * m); };
// quantity point can't be subtracted from a quantity
requires !requires { 1 * m - QP<si::metre>(1 * m); };
// no crossdimensional addition and subtraction
requires !requires { QP<si::metre>(1 * m) + 1 * s; };
requires !requires { QP<si::metre>(1 * m) - 1 * s; };
// unit constants
requires !requires { QP<si::metre>(1) + m; };
requires !requires { QP<si::metre>(1) - m; };
requires !requires { m + QP<si::metre>(1); };
requires !requires { m - QP<si::metre>(1); };
};
static_assert(invalid_binary_operations<quantity_point>);
// same representation type
static_assert(is_of_type<quantity_point(1 * m) + 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * m) + 1 * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * km) + 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * m) + isq::length(1 * m),
quantity_point<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * m) + isq::length(1 * km),
quantity_point<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * km) + isq::length(1 * m),
quantity_point<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * m)) + 1 * m,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * m)) + 1 * km,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * km)) + 1 * m,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * m)) + isq::height(1 * m),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * m)) + isq::height(1 * km),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * km)) + isq::height(1 * m),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<1 * m + quantity_point(1 * m),
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<1 * m + quantity_point(1 * km),
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<1 * km + quantity_point(1 * m),
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<1 * m + quantity_point(isq::length(1 * m)),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<1 * m + quantity_point(isq::length(1 * km)),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<1 * km + quantity_point(isq::length(1 * m)),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<isq::length(1 * m) + quantity_point(1 * m),
quantity_point<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<isq::length(1 * m) + quantity_point(1 * km),
quantity_point<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<isq::length(1 * km) + quantity_point(1 * m),
quantity_point<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<isq::height(1 * m) + quantity_point(isq::length(1 * m)),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<isq::height(1 * m) + quantity_point(isq::length(1 * km)),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<isq::height(1 * km) + quantity_point(isq::length(1 * m)),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(1 * m) - 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * km) - 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * m) - 1 * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * m) - isq::length(1 * m),
quantity_point<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * m) - isq::length(1 * km),
quantity_point<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(1 * km) - isq::length(1 * m),
quantity_point<isq::length[m], absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * m)) - 1 * m,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * m)) - 1 * km,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * km)) - 1 * m,
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * m)) - isq::height(1 * m),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * m)) - isq::height(1 * km),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
static_assert(is_of_type<quantity_point(isq::length(1 * km)) - isq::height(1 * m),
quantity_point<isq::length[m], absolute_point_origin<isq::length>{}, int>>);
// check for integral types promotion
static_assert(
is_same_v<decltype((quantity_point{std::uint8_t(0) * m} + std::uint8_t(0) * m).relative().number()), int&&>);
static_assert(
is_same_v<decltype((std::uint8_t(0) * m + quantity_point{std::uint8_t(0) * m}).relative().number()), int&&>);
static_assert(
is_same_v<decltype((quantity_point{std::uint8_t(0) * m} - std::uint8_t(0) * m).relative().number()), int&&>);
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<quantity_point{1. * m} + 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<1. * m + quantity_point{1 * m},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * m} + 1. * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<1 * m + quantity_point{1. * km},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * km} + 1. * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<1 * km + quantity_point{1. * m},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * m} - 1. * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1. * km} - 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1. * m} - 1 * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
// different units
static_assert(is_of_type<quantity_point{1 * m} + 1 * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<1 * m + quantity_point{1 * km},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point{1. * m} + 1 * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<1. * m + quantity_point{1 * km},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * m} + 1. * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<1 * m + quantity_point{1. * km},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1. * m} + 1. * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<1. * m + quantity_point{1. * km},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * km} + 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<1 * km + quantity_point{1 * m},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point{1. * km} + 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<1. * km + quantity_point{1 * m},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * km} + 1. * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<1 * km + quantity_point{1. * m},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1. * km} + 1. * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<1. * km + quantity_point{1. * m},
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * m} - 1 * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point{1. * m} - 1 * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * m} - 1. * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1. * m} - 1. * km,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * km} - 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, int>>);
static_assert(is_of_type<quantity_point{1. * km} - 1 * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1 * km} - 1. * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, double>>);
static_assert(is_of_type<quantity_point{1. * km} - 1. * m,
quantity_point<si::metre, absolute_point_origin<kind_of<isq::length>>{}, 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<quantity_point{10 * isq::length[m] / (2 * isq::time[s])} + 5 * isq::speed[m / s],
quantity_point<isq::speed[m / s], absolute_point_origin<isq::length / isq::time>{}, int>>);
static_assert(is_of_type<10 * isq::length[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]},
quantity_point<isq::speed[m / s], absolute_point_origin<isq::speed>{}, int>>);
static_assert(is_of_type<quantity_point{5 * isq::speed[m / s]} + 10 * isq::length[m] / (2 * isq::time[s]),
quantity_point<isq::speed[m / s], absolute_point_origin<isq::speed>{}, int>>);
static_assert(is_of_type<5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])},
quantity_point<isq::speed[m / s], absolute_point_origin<isq::length / isq::time>{}, int>>);
static_assert(is_of_type<quantity_point{10 * isq::length[m] / (2 * isq::time[s])} - 5 * isq::speed[m / s],
quantity_point<isq::speed[m / s], absolute_point_origin<isq::length / isq::time>{}, int>>);
static_assert(is_of_type<quantity_point{5 * isq::speed[m / s]} - 10 * isq::length[m] / (2 * isq::time[s]),
quantity_point<isq::speed[m / s], absolute_point_origin<isq::speed>{}, 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<quantity_point{10 / (2 * isq::period_duration[s])} + 5 * isq::frequency[Hz],
quantity_point<isq::frequency[Hz], absolute_point_origin<1 / isq::period_duration>{}, int>>);
static_assert(is_of_type<10 / (2 * isq::period_duration[s]) + quantity_point{5 * isq::frequency[Hz]},
quantity_point<isq::frequency[Hz], absolute_point_origin<isq::frequency>{}, int>>);
static_assert(is_of_type<quantity_point{5 * isq::frequency[Hz]} + 10 / (2 * isq::period_duration[s]),
quantity_point<isq::frequency[Hz], absolute_point_origin<isq::frequency>{}, int>>);
static_assert(is_of_type<5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])},
quantity_point<isq::frequency[Hz], absolute_point_origin<1 / isq::period_duration>{}, int>>);
static_assert(is_of_type<quantity_point{10 / (2 * isq::period_duration[s])} - 5 * isq::frequency[Hz],
quantity_point<isq::frequency[Hz], absolute_point_origin<1 / isq::period_duration>{}, int>>);
static_assert(is_of_type<quantity_point{5 * isq::frequency[Hz]} - 10 / (2 * isq::period_duration[s]),
quantity_point<isq::frequency[Hz], absolute_point_origin<isq::frequency>{}, int>>);
// Different named dimensions
template<typename... Ts>
consteval bool invalid_addition(Ts... ts)
{
return !requires { (... + ts); };
}
template<typename... Ts>
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