mirror of
https://github.com/mpusz/mp-units.git
synced 2025-06-25 01:01:33 +02:00
974 lines
48 KiB
C++
974 lines
48 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/bits/hacks.h>
|
|
#include <mp-units/ext/fixed_string.h>
|
|
#include <mp-units/ext/type_traits.h>
|
|
#include <mp-units/systems/isq/mechanics.h>
|
|
#include <mp-units/systems/isq/space_and_time.h>
|
|
#include <mp-units/systems/si.h>
|
|
#include <chrono>
|
|
#include <concepts>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
template<>
|
|
inline constexpr bool mp_units::is_vector<int> = true;
|
|
|
|
namespace {
|
|
|
|
using namespace mp_units;
|
|
using namespace mp_units::si::unit_symbols;
|
|
|
|
//////////////////////////////
|
|
// quantity class invariants
|
|
//////////////////////////////
|
|
|
|
static_assert(sizeof(quantity<si::metre>) == sizeof(double));
|
|
static_assert(sizeof(quantity<isq::length[m]>) == sizeof(double));
|
|
static_assert(sizeof(quantity<si::metre, short>) == sizeof(short));
|
|
static_assert(sizeof(quantity<isq::length[m], short>) == sizeof(short));
|
|
|
|
template<template<auto, typename> typename Q>
|
|
concept invalid_types = requires {
|
|
requires !requires { typename Q<isq::dim_length, double>; }; // dimension instead of reference
|
|
requires !requires { typename Q<isq::length, double>; }; // quantity_spec instead of reference
|
|
requires !requires { typename Q<isq::length[m], bool>; }; // bool is not a valid representation type
|
|
requires !requires { typename Q<isq::length[m], quantity<isq::length[m]>>; }; // quantity used as Rep
|
|
requires !requires { typename Q<isq::position_vector[si::metre], double>; }; // vector representation expected
|
|
};
|
|
static_assert(invalid_types<quantity>);
|
|
|
|
static_assert(std::is_trivially_default_constructible_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_trivially_copy_constructible_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_trivially_move_constructible_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_trivially_copy_assignable_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_trivially_move_assignable_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_trivially_destructible_v<quantity<isq::length[m]>>);
|
|
|
|
static_assert(std::is_nothrow_default_constructible_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_nothrow_copy_constructible_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_nothrow_move_constructible_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_nothrow_copy_assignable_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_nothrow_move_assignable_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_nothrow_destructible_v<quantity<isq::length[m]>>);
|
|
|
|
static_assert(std::is_trivially_copyable_v<quantity<isq::length[m]>>);
|
|
static_assert(std::is_standard_layout_v<quantity<isq::length[m]>>);
|
|
|
|
static_assert(std::default_initializable<quantity<isq::length[m]>>);
|
|
static_assert(std::move_constructible<quantity<isq::length[m]>>);
|
|
static_assert(std::copy_constructible<quantity<isq::length[m]>>);
|
|
static_assert(std::equality_comparable<quantity<isq::length[m]>>);
|
|
static_assert(std::totally_ordered<quantity<isq::length[m]>>);
|
|
static_assert(std::regular<quantity<isq::length[m]>>);
|
|
|
|
static_assert(std::three_way_comparable<quantity<isq::length[m]>>);
|
|
|
|
|
|
//////////////////
|
|
// member values
|
|
//////////////////
|
|
|
|
static_assert(quantity<si::metre>::reference == si::metre);
|
|
static_assert(quantity<si::metre>::quantity_spec == kind_of<isq::length>);
|
|
static_assert(quantity<si::metre>::dimension == isq::dim_length);
|
|
static_assert(quantity<si::metre>::unit == si::metre);
|
|
|
|
static_assert(quantity<isq::length[m]>::reference == isq::length[m]);
|
|
static_assert(quantity<isq::length[m]>::quantity_spec == isq::length);
|
|
static_assert(quantity<isq::length[m]>::dimension == isq::dim_length);
|
|
static_assert(quantity<isq::length[m]>::unit == si::metre);
|
|
|
|
/////////////////
|
|
// member types
|
|
/////////////////
|
|
|
|
static_assert(is_same_v<quantity<isq::length[m]>::rep, double>);
|
|
static_assert(is_same_v<quantity<isq::length[m], int>::rep, int>);
|
|
|
|
|
|
////////////////////////////
|
|
// static member functions
|
|
////////////////////////////
|
|
|
|
static_assert(quantity<isq::length[m], int>::zero().numerical_value_in(m) == 0);
|
|
static_assert(quantity<isq::length[m], int>::one().numerical_value_in(m) == 1);
|
|
static_assert(quantity<isq::length[m], int>::min().numerical_value_in(m) == std::numeric_limits<int>::lowest());
|
|
static_assert(quantity<isq::length[m], int>::max().numerical_value_in(m) == std::numeric_limits<int>::max());
|
|
static_assert(quantity<isq::length[m], double>::zero().numerical_value_in(m) == 0.0);
|
|
static_assert(quantity<isq::length[m], double>::one().numerical_value_in(m) == 1.0);
|
|
static_assert(quantity<isq::length[m], double>::min().numerical_value_in(m) == std::numeric_limits<double>::lowest());
|
|
static_assert(quantity<isq::length[m], double>::max().numerical_value_in(m) == std::numeric_limits<double>::max());
|
|
|
|
|
|
//////////////////////////////
|
|
// construction from a value
|
|
//////////////////////////////
|
|
|
|
// construction from a value is private
|
|
static_assert(!std::constructible_from<quantity<isq::length[m]>, double>);
|
|
static_assert(!std::convertible_to<double, quantity<isq::length[m]>>);
|
|
|
|
static_assert(!std::constructible_from<quantity<isq::length[m], int>, int>);
|
|
static_assert(!std::convertible_to<int, quantity<isq::length[m], int>>);
|
|
|
|
static_assert(!std::constructible_from<quantity<dimensionless[one]>, double>);
|
|
static_assert(!std::convertible_to<double, quantity<dimensionless[one]>>);
|
|
|
|
static_assert(!std::constructible_from<quantity<dimensionless[one]>, int>);
|
|
static_assert(!std::convertible_to<int, quantity<dimensionless[one]>>);
|
|
|
|
|
|
///////////////////////////////////////
|
|
// construction from another quantity
|
|
///////////////////////////////////////
|
|
|
|
// conversion only between convertible quantities
|
|
static_assert(std::constructible_from<quantity<isq::length[m]>, quantity<isq::length[m]>>);
|
|
static_assert(std::convertible_to<quantity<isq::length[m]>, quantity<isq::length[m]>>);
|
|
static_assert(std::constructible_from<quantity<isq::length[m]>, quantity<isq::distance[m]>>);
|
|
static_assert(std::convertible_to<quantity<isq::distance[m]>, quantity<isq::length[m]>>);
|
|
static_assert(std::constructible_from<quantity<isq::length[m]>, quantity<isq::length[km]>>);
|
|
static_assert(std::convertible_to<quantity<isq::length[km]>, quantity<isq::length[m]>>);
|
|
|
|
// conversion between different quantities not allowed
|
|
static_assert(!std::constructible_from<quantity<isq::length[m]>, quantity<isq::time[s]>>);
|
|
static_assert(!std::convertible_to<quantity<isq::time[s]>, quantity<isq::length[m]>>);
|
|
static_assert(!std::constructible_from<quantity<isq::length[m]>, quantity<isq::speed[m / s]>>);
|
|
static_assert(!std::convertible_to<quantity<isq::speed[m / s]>, quantity<isq::length[m]>>);
|
|
|
|
// implicit conversion from another quantity only if non-truncating
|
|
static_assert(std::constructible_from<quantity<isq::length[m]>, quantity<isq::length[m], int>>); // int -> double OK
|
|
static_assert(std::convertible_to<quantity<isq::length[m], int>, quantity<isq::length[m]>>); // int -> double OK
|
|
|
|
static_assert(!std::constructible_from<quantity<isq::length[m], int>,
|
|
quantity<isq::length[m]>>); // truncating double -> int not allowed
|
|
static_assert(!std::convertible_to<quantity<isq::length[m]>,
|
|
quantity<isq::length[m], int>>); // truncating double -> int not allowed
|
|
|
|
static_assert(std::constructible_from<quantity<isq::length[m], int>,
|
|
quantity<isq::length[km], int>>); // kilometre<int> -> metre<int> OK
|
|
static_assert(std::convertible_to<quantity<isq::length[km], int>,
|
|
quantity<isq::length[m], int>>); // kilometre<int> -> metre<int> OK
|
|
|
|
static_assert(!std::constructible_from<quantity<isq::length[km], int>,
|
|
quantity<isq::length[m], int>>); // truncating metre<int> ->
|
|
// kilometre<int> not allowed
|
|
static_assert(
|
|
!std::convertible_to<quantity<isq::length[m], int>,
|
|
quantity<isq::length[km], int>>); // truncating metre<int> -> kilometre<int> not allowed
|
|
|
|
// converting to double always OK
|
|
static_assert(std::constructible_from<quantity<isq::length[m]>, quantity<isq::length[km], int>>);
|
|
static_assert(std::convertible_to<quantity<isq::length[km], int>, quantity<isq::length[m]>>);
|
|
static_assert(std::constructible_from<quantity<isq::length[km]>, quantity<isq::length[m], int>>);
|
|
static_assert(std::convertible_to<quantity<isq::length[m], int>, quantity<isq::length[km]>>);
|
|
|
|
|
|
///////////////////////
|
|
// obtaining a number
|
|
///////////////////////
|
|
|
|
static_assert(quantity<isq::length[m], int>(123 * m).numerical_value_in(m) == 123);
|
|
static_assert(quantity<isq::length[m], int>(2 * km).numerical_value_in(m) == 2000);
|
|
static_assert(quantity<isq::length[km], int>(2 * km).numerical_value_in(km) == 2);
|
|
static_assert(quantity<isq::length[km]>(1500 * m).numerical_value_in(km) == 1.5);
|
|
|
|
|
|
///////////////////////////////////
|
|
// converting to a different unit
|
|
///////////////////////////////////
|
|
|
|
static_assert(is_of_type<(2. * km).in(m), quantity<si::metre>>);
|
|
static_assert(is_of_type<isq::length(2. * km).in(m), quantity<isq::length[m]>>);
|
|
static_assert(is_of_type<isq::height(2. * km).in(m), quantity<isq::height[m]>>);
|
|
|
|
static_assert(quantity<isq::length[km]>(2. * km).in(km).numerical_value_in(km) == 2.);
|
|
static_assert(quantity<isq::length[km]>(2. * km).in(m).numerical_value_in(m) == 2000.);
|
|
static_assert(quantity<isq::length[m]>(2000. * m).in(km).numerical_value_in(km) == 2.);
|
|
static_assert(quantity<isq::length[km], int>(2 * km).in(km).numerical_value_in(km) == 2);
|
|
static_assert(quantity<isq::length[km], int>(2 * km).in(m).numerical_value_in(m) == 2000);
|
|
|
|
static_assert(is_of_type<(2. * km).force_in(m), quantity<si::metre>>);
|
|
static_assert(is_of_type<isq::length(2. * km).force_in(m), quantity<isq::length[m]>>);
|
|
static_assert(is_of_type<isq::height(2. * km).force_in(m), quantity<isq::height[m]>>);
|
|
|
|
static_assert(quantity<isq::length[km]>(2. * km).force_in(km).numerical_value_in(km) == 2.);
|
|
static_assert(quantity<isq::length[km]>(2. * km).force_in(m).numerical_value_in(m) == 2000.);
|
|
static_assert(quantity<isq::length[m]>(2000. * m).force_in(km).numerical_value_in(km) == 2.);
|
|
static_assert(quantity<isq::length[km], int>(2 * km).force_in(km).numerical_value_in(km) == 2);
|
|
static_assert(quantity<isq::length[km], int>(2 * km).force_in(m).numerical_value_in(m) == 2000);
|
|
static_assert(quantity<isq::length[m], int>(2000 * m).force_in(km).numerical_value_in(km) == 2);
|
|
|
|
template<template<auto, typename> typename Q>
|
|
concept invalid_unit_conversion = requires {
|
|
requires !requires { Q<isq::length[m], int>(2000 * m).in(km); }; // truncating conversion
|
|
requires !requires { Q<isq::length[m], int>(2 * m).in(s); }; // unit of a different quantity & dimension
|
|
requires !requires { Q<isq::frequency[Hz], int>(60 * Hz).in(Bq); }; // unit of a different kind (same dimension)
|
|
|
|
requires !requires { Q<isq::length[m], int>(2 * m).force_in(s); }; // unit of a different quantity & dimension
|
|
requires !requires {
|
|
Q<isq::frequency[Hz], int>(60 * Hz).force_in(Bq);
|
|
}; // unit of a different kind (same dimension)
|
|
};
|
|
static_assert(invalid_unit_conversion<quantity>);
|
|
|
|
static_assert(quantity<isq::length[km]>(2. * km).numerical_value_in(km) == 2.);
|
|
static_assert(quantity<isq::length[km]>(2. * km).numerical_value_in(m) == 2000.);
|
|
static_assert(quantity<isq::length[m]>(2000. * m).numerical_value_in(km) == 2.);
|
|
static_assert(quantity<isq::length[km], int>(2 * km).numerical_value_in(km) == 2);
|
|
static_assert(quantity<isq::length[km], int>(2 * km).numerical_value_in(m) == 2000);
|
|
|
|
template<template<auto, typename> typename Q>
|
|
concept invalid_getter_with_unit_conversion = requires {
|
|
requires !requires { Q<isq::length[m], int>(2000 * m).numerical_value_in(km); }; // truncating conversion
|
|
requires !requires { Q<isq::length[m], int>(2 * m).numerical_value_in(s); }; // invalid unit
|
|
};
|
|
static_assert(invalid_getter_with_unit_conversion<quantity>);
|
|
|
|
|
|
///////////////////////////////////////
|
|
// derived quantities
|
|
///////////////////////////////////////
|
|
|
|
template<Representation Rep, Quantity Q, const basic_fixed_string additional_nttp_argument>
|
|
struct derived_quantity : quantity<Q::reference, Rep> {
|
|
static constexpr auto reference = Q::reference;
|
|
static constexpr auto quantity_spec = Q::quantity_spec;
|
|
static constexpr auto dimension = Q::dimension;
|
|
static constexpr auto unit = Q::unit;
|
|
using rep = Rep;
|
|
using R = quantity<reference, Rep>;
|
|
|
|
derived_quantity() = default;
|
|
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
|
|
constexpr explicit(!std::is_trivial_v<Rep>) derived_quantity(const R& t) : R(t) {}
|
|
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
|
|
constexpr explicit(!std::is_trivial_v<Rep>) derived_quantity(R&& t) : R(std::move(t)) {}
|
|
|
|
constexpr derived_quantity& operator=(const R& t)
|
|
{
|
|
R::operator=(t);
|
|
return *this;
|
|
}
|
|
constexpr derived_quantity& operator=(R&& t)
|
|
{
|
|
R::operator=(std::move(t));
|
|
return *this;
|
|
}
|
|
// NOLINTBEGIN(google-explicit-constructor, hicpp-explicit-conversions)
|
|
constexpr explicit(false) operator R&() & noexcept { return *this; }
|
|
constexpr explicit(false) operator const R&() const& noexcept { return *this; }
|
|
constexpr explicit(false) operator R&&() && noexcept { return *this; }
|
|
constexpr explicit(false) operator const R&&() const&& noexcept { return *this; }
|
|
// NOLINTEND(google-explicit-constructor, hicpp-explicit-conversions)
|
|
};
|
|
|
|
static_assert(Quantity<derived_quantity<double, quantity<isq::length[m]>, "NTTP type description">>);
|
|
|
|
constexpr QuantityOf<isq::length> auto get_length_derived_quantity() noexcept
|
|
{
|
|
derived_quantity<double, quantity<isq::length[m]>, "NTTP type description"> dist{};
|
|
dist += 1 * m;
|
|
dist = dist + 1 * m;
|
|
dist *= 0.5;
|
|
return dist;
|
|
}
|
|
static_assert(get_length_derived_quantity() == 1 * m);
|
|
|
|
|
|
/////////
|
|
// CTAD
|
|
/////////
|
|
|
|
static_assert(std::is_same_v<decltype(quantity{123 * m})::rep, int>);
|
|
static_assert(std::is_same_v<decltype(quantity{123. * m})::rep, double>);
|
|
static_assert(quantity{123. * m}.unit == si::metre);
|
|
static_assert(quantity{123. * m}.quantity_spec == kind_of<isq::length>);
|
|
static_assert(quantity{123. * h}.unit == si::hour);
|
|
static_assert(quantity{123. * h}.quantity_spec == kind_of<isq::time>);
|
|
|
|
using namespace std::chrono_literals;
|
|
static_assert(std::is_same_v<decltype(quantity{123s})::rep, std::chrono::seconds::rep>);
|
|
static_assert(std::is_same_v<decltype(quantity{123.s})::rep, long double>);
|
|
static_assert(quantity{24h}.unit == si::hour);
|
|
static_assert(quantity{24h}.quantity_spec == kind_of<isq::time>);
|
|
|
|
|
|
////////////////////////
|
|
// 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);
|
|
|
|
|
|
////////////////////
|
|
// 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 v) {
|
|
const auto vv = v++; // NOLINT(bugprone-inc-dec-in-conditions)
|
|
return std::pair(v, vv);
|
|
}(123 * m) == std::pair(124 * m, 123 * m));
|
|
static_assert([](auto v) {
|
|
const auto vv = ++v; // NOLINT(bugprone-inc-dec-in-conditions)
|
|
return std::pair(v, vv);
|
|
}(123 * m) == std::pair(124 * m, 124 * m));
|
|
static_assert([](auto v) {
|
|
const auto vv = v--; // NOLINT(bugprone-inc-dec-in-conditions)
|
|
return std::pair(v, vv);
|
|
}(123 * m) == std::pair(122 * m, 123 * m));
|
|
static_assert([](auto v) {
|
|
const auto vv = --v; // NOLINT(bugprone-inc-dec-in-conditions)
|
|
return std::pair(v, vv);
|
|
}(123 * m) == std::pair(122 * m, 122 * m));
|
|
|
|
static_assert(is_same_v<decltype((+(short{0} * m)).numerical_value_in(m)), int>);
|
|
|
|
|
|
////////////////////////
|
|
// compound assignment
|
|
////////////////////////
|
|
|
|
// same type
|
|
static_assert((1 * m += 1 * m).numerical_value_in(m) == 2);
|
|
static_assert((2 * m -= 1 * m).numerical_value_in(m) == 1);
|
|
static_assert((1 * m *= 2).numerical_value_in(m) == 2);
|
|
static_assert((2 * m /= 2).numerical_value_in(m) == 1);
|
|
static_assert((1 * m *= 2 * one).numerical_value_in(m) == 2);
|
|
static_assert((2 * m /= 2 * one).numerical_value_in(m) == 1);
|
|
static_assert((7 * m %= 2 * m).numerical_value_in(m) == 1);
|
|
|
|
// different types
|
|
static_assert((2.5 * m += 3 * m).numerical_value_in(m) == 5.5);
|
|
static_assert((123 * m += 1 * km).numerical_value_in(m) == 1123);
|
|
static_assert((5.5 * m -= 3 * m).numerical_value_in(m) == 2.5);
|
|
static_assert((1123 * m -= 1 * km).numerical_value_in(m) == 123);
|
|
static_assert((2.5 * m *= 3).numerical_value_in(m) == 7.5);
|
|
static_assert((7.5 * m /= 3).numerical_value_in(m) == 2.5);
|
|
static_assert((2.5 * m *= 3 * one).numerical_value_in(m) == 7.5);
|
|
static_assert((7.5 * m /= 3 * one).numerical_value_in(m) == 2.5);
|
|
static_assert((3500 * m %= 1 * km).numerical_value_in(m) == 500);
|
|
|
|
// static_assert((std::uint8_t{255} * m %= 256 * m).numerical_value_in(m) == [] {
|
|
// std::uint8_t ui(255);
|
|
// return ui %= 256;
|
|
// }()); // UB
|
|
// TODO: Fix
|
|
static_assert((std::uint8_t{255}* m %= 257 * m).numerical_value_in(m) != [] {
|
|
std::uint8_t ui(255);
|
|
return ui %= 257;
|
|
}());
|
|
|
|
// TODO ICE
|
|
// (https://developercommunity2.visualstudio.com/t/ICE-on-a-constexpr-operator-in-mp-unit/1302907)
|
|
#ifndef MP_UNITS_COMP_MSVC
|
|
// clang-17 with modules build on ignores disabling conversion warnings
|
|
#if !(defined MP_UNITS_COMP_CLANG && MP_UNITS_COMP_CLANG < 18 && defined MP_UNITS_MODULES)
|
|
// next two lines trigger conversions warnings
|
|
// (warning disabled in CMake for this file)
|
|
static_assert((22 * m *= 33.33).numerical_value_in(m) == 733);
|
|
static_assert((22 * m /= 3.33).numerical_value_in(m) == 6);
|
|
static_assert((22 * m *= 33.33 * one).numerical_value_in(m) == 733);
|
|
static_assert((22 * m /= 3.33 * one).numerical_value_in(m) == 6);
|
|
#endif
|
|
#endif
|
|
|
|
template<template<auto, typename> typename Q>
|
|
concept invalid_compound_assignments = requires() {
|
|
// truncating not allowed
|
|
requires !requires(Q<isq::length[m], int> l) { l += 2.5 * m; };
|
|
requires !requires(Q<isq::length[m], int> l) { l -= 2.5 * m; };
|
|
requires !requires(Q<isq::length[km], int> l) { l += 2 * isq::length[m]; };
|
|
requires !requires(Q<isq::length[km], int> l) { l -= 2 * isq::length[m]; };
|
|
requires !requires(Q<isq::length[km], int> l) { l %= 2 * isq::length[m]; };
|
|
requires !requires(Q<isq::length[km], int> l) { l %= 2 * percent; };
|
|
requires !requires(Q<isq::length[km], int> l) { l %= 2. * percent; };
|
|
|
|
// TODO: accept non-truncating argument
|
|
requires !requires(Q<isq::length[km], int> l) { l *= 1 * (km / m); };
|
|
requires !requires(Q<isq::length[km], int> l) { l /= 1 * (km / m); };
|
|
requires !requires(Q<isq::length[km], int> l) { l %= 1 * (km / m); };
|
|
|
|
// only quantities can be added or subtracted
|
|
requires !requires(Q<isq::length[m], int> l) { l += 2; };
|
|
requires !requires(Q<isq::length[m], int> l) { l -= 2; };
|
|
|
|
// compound multiply/divide by another quantity not allowed
|
|
requires !requires(Q<isq::length[m], int> l) { l *= 2 * m; };
|
|
requires !requires(Q<isq::length[m], int> l) { l /= 2 * m; };
|
|
|
|
// modulo operations on a floating point representation not allowed
|
|
requires !requires(Q<isq::length[m], double> l) { l %= 2.; };
|
|
requires !requires(Q<isq::length[m], double> l) { l %= 2; };
|
|
requires !requires(Q<isq::length[m], double> l) { l %= 2. * m; };
|
|
requires !requires(Q<isq::length[m], double> l) { l %= 2 * m; };
|
|
requires !requires(Q<isq::length[m], int> l) { l %= 2. * m; };
|
|
|
|
// no unit constants
|
|
requires !requires(Q<isq::length[m], int> l) { l += m; };
|
|
requires !requires(Q<isq::length[m], int> l) { l -= m; };
|
|
requires !requires(Q<isq::length[m], int> l) { l *= m; };
|
|
requires !requires(Q<isq::length[m], int> l) { l /= m; };
|
|
requires !requires(Q<isq::length[m], int> l) { l %= m; };
|
|
};
|
|
static_assert(invalid_compound_assignments<quantity>);
|
|
|
|
|
|
////////////////////
|
|
// binary operators
|
|
////////////////////
|
|
|
|
template<template<auto, typename> typename Q>
|
|
concept invalid_binary_operations = requires {
|
|
// no crossdimensional addition and subtraction
|
|
requires !requires { 1 * s + Q<isq::length[m], int>(1 * m); };
|
|
requires !requires { 1 * s - Q<isq::length[m], int>(1 * m); };
|
|
|
|
// no floating-point modulo
|
|
requires !requires(Q<isq::length[m], double> a) { a % 2 * m; };
|
|
requires !requires(Q<isq::length[m], double> a) { 2 * m % a; };
|
|
requires !requires(Q<isq::length[m], double> a) { a % 2; };
|
|
requires !requires(Q<isq::length[m], double> a, Q<isq::length[m], double> b) { a % b; };
|
|
requires !requires(Q<isq::length[m], double> a, Q<isq::length[m], int> b) { a % b; };
|
|
requires !requires(Q<isq::length[m], double> a, Q<isq::length[m], int> b) { b % a; };
|
|
|
|
// unit constants
|
|
requires !requires { Q<isq::length[m], int>(1) + m; };
|
|
requires !requires { Q<isq::length[m], int>(1) - m; };
|
|
requires !requires { Q<isq::length[m], int>(1) % m; };
|
|
requires !requires { m + Q<isq::length[m], int>(1); };
|
|
requires !requires { m - Q<isq::length[m], int>(1); };
|
|
requires !requires { m % Q<isq::length[m], int>(1); };
|
|
};
|
|
static_assert(invalid_binary_operations<quantity>);
|
|
|
|
// same representation type
|
|
static_assert(is_of_type<1 * m + 1 * m, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * m + 1 * km, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * km + 1 * m, quantity<si::metre, int>>);
|
|
|
|
static_assert(is_of_type<1 * m + isq::length(1 * m), quantity<isq::length[m], int>>);
|
|
static_assert(is_of_type<1 * m + isq::length(1 * km), quantity<isq::length[m], int>>);
|
|
static_assert(is_of_type<1 * km + isq::length(1 * m), quantity<isq::length[m], int>>);
|
|
|
|
static_assert(is_of_type<isq::length(1 * m) + 1 * m, quantity<isq::length[m], int>>);
|
|
static_assert(is_of_type<isq::length(1 * m) + 1 * km, quantity<isq::length[m], int>>);
|
|
static_assert(is_of_type<isq::length(1 * km) + 1 * m, quantity<isq::length[m], int>>);
|
|
|
|
static_assert(is_of_type<1 * m - 1 * m, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * km - 1 * m, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * m - 1 * km, quantity<si::metre, int>>);
|
|
|
|
static_assert(is_of_type<1 * m - isq::length(1 * m), quantity<isq::length[m], int>>);
|
|
static_assert(is_of_type<1 * m - isq::length(1 * km), quantity<isq::length[m], int>>);
|
|
static_assert(is_of_type<1 * km - isq::length(1 * m), quantity<isq::length[m], int>>);
|
|
|
|
static_assert(is_of_type<isq::length(1 * m) - 1 * m, quantity<isq::length[m], int>>);
|
|
static_assert(is_of_type<isq::length(1 * m) - 1 * km, quantity<isq::length[m], int>>);
|
|
static_assert(is_of_type<isq::length(1 * km) - 1 * m, quantity<isq::length[m], int>>);
|
|
|
|
static_assert(is_of_type<1 * N * (1 * m), quantity<derived_unit<struct si::newton, struct si::metre>{}, int>>);
|
|
|
|
static_assert(is_of_type<1 * m * 1, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * m * (1 * one), quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * m * (1 * percent), quantity<derived_unit<struct percent, struct si::metre>{}, int>>);
|
|
|
|
static_assert(is_of_type<1 * (1 * m), quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * one * (1 * m), quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * percent * (1 * m), quantity<derived_unit<struct percent, struct si::metre>{}, int>>);
|
|
|
|
static_assert(is_of_type<1 * m / (1 * s), quantity<derived_unit<struct si::metre, per<struct si::second>>{}, int>>);
|
|
static_assert(is_of_type<1 * m / (1 * m), quantity<one, int>>);
|
|
static_assert(
|
|
is_of_type<1 * km / (1 * m), quantity<derived_unit<si::kilo_<struct si::metre>, per<struct si::metre>>{}, int>>);
|
|
|
|
static_assert(is_of_type<1 * m / 1, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * m / (1 * one), quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * m / (1 * percent), quantity<derived_unit<struct si::metre, per<struct percent>>{}, int>>);
|
|
|
|
static_assert(is_of_type<1 / (1 * s), quantity<derived_unit<struct one, per<struct si::second>>{}, int>>);
|
|
static_assert(is_of_type<1 / s, quantity<derived_unit<struct one, per<struct si::second>>{}, int>>);
|
|
static_assert(is_of_type<1 * one / (1 * s), quantity<derived_unit<struct one, per<struct si::second>>{}, int>>);
|
|
static_assert(is_of_type<1 * percent / (1 * s), quantity<derived_unit<struct percent, per<struct si::second>>{}, int>>);
|
|
|
|
static_assert(is_of_type<4 * m % (2 * m), quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1'234 * m % (1 * km), quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * km % (300 * m), quantity<si::metre, int>>);
|
|
static_assert(is_of_type<4 * one % (2 * one), quantity<one, int>>);
|
|
|
|
// check for integral types promotion
|
|
static_assert(is_same_v<decltype(std::uint8_t{0} * m + std::uint8_t{0} * m)::rep, int>);
|
|
static_assert(is_same_v<decltype(std::uint8_t{0} * m - std::uint8_t{0} * m)::rep, int>);
|
|
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<decltype((std::uint8_t{0} * m) % (std::uint8_t{0} * m))::rep, decltype(std::uint8_t{0} % std::uint8_t{0})>);
|
|
|
|
// different representation types
|
|
static_assert(is_of_type<1. * m + 1 * m, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * m + 1. * km, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * km + 1. * m, quantity<si::metre, double>>);
|
|
|
|
static_assert(is_of_type<1 * m - 1. * m, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1. * km - 1 * m, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1. * m - 1 * km, quantity<si::metre, double>>);
|
|
|
|
static_assert(is_of_type<1. * N * (1 * m), quantity<derived_unit<struct si::newton, struct si::metre>{}, double>>);
|
|
|
|
static_assert(is_of_type<1 * m * 1., quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * m * (1. * one), quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * m * (1. * percent), quantity<derived_unit<struct percent, struct si::metre>{}, double>>);
|
|
|
|
static_assert(is_of_type<1 * (1. * m), quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1. * one * (1 * m), quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * percent * (1. * m), quantity<derived_unit<struct percent, struct si::metre>{}, double>>);
|
|
|
|
static_assert(is_of_type<1 * m / (1. * s), quantity<derived_unit<struct si::metre, per<struct si::second>>{}, double>>);
|
|
static_assert(is_of_type<1. * m / (1 * m), quantity<one, double>>);
|
|
static_assert(
|
|
is_of_type<1. * km / (1 * m), quantity<derived_unit<si::kilo_<struct si::metre>, per<struct si::metre>>{}, double>>);
|
|
|
|
static_assert(is_of_type<1. * m / 1, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * m / (1. * one), quantity<si::metre, double>>);
|
|
static_assert(
|
|
is_of_type<1 * m / (1. * percent), quantity<derived_unit<struct si::metre, per<struct percent>>{}, double>>);
|
|
|
|
static_assert(is_of_type<1 / (1. * s), quantity<derived_unit<struct one, per<struct si::second>>{}, double>>);
|
|
static_assert(is_of_type<1. / s, quantity<derived_unit<struct one, per<struct si::second>>{}, double>>);
|
|
static_assert(is_of_type<1. * one / (1 * s), quantity<derived_unit<struct one, per<struct si::second>>{}, double>>);
|
|
static_assert(
|
|
is_of_type<1 * percent / (1. * s), quantity<derived_unit<struct percent, per<struct si::second>>{}, double>>);
|
|
|
|
// different units
|
|
static_assert(is_of_type<1 * m + 1 * km, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1. * m + 1 * km, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * m + 1. * km, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1. * m + 1. * km, quantity<si::metre, double>>);
|
|
|
|
static_assert(is_of_type<1 * km + 1 * m, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1. * km + 1 * m, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * km + 1. * m, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1. * km + 1. * m, quantity<si::metre, double>>);
|
|
|
|
static_assert(is_of_type<1 * m - 1 * km, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1. * m - 1 * km, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * m - 1. * km, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1. * m - 1. * km, quantity<si::metre, double>>);
|
|
|
|
static_assert(is_of_type<1 * km - 1 * m, quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1. * km - 1 * m, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1 * km - 1. * m, quantity<si::metre, double>>);
|
|
static_assert(is_of_type<1. * km - 1. * m, quantity<si::metre, double>>);
|
|
|
|
static_assert(is_of_type<1 * m % (1 * km), quantity<si::metre, int>>);
|
|
|
|
// different dimensions
|
|
static_assert(is_of_type<1 * m / s * (1 * s), quantity<si::metre, int>>);
|
|
static_assert(is_of_type<1 * m / s * (1 * h),
|
|
quantity<derived_unit<struct si::hour, struct si::metre, per<struct si::second>>{}, int>>);
|
|
static_assert(is_of_type<1 * m * (1 * min), quantity<derived_unit<struct si::metre, struct si::minute>{}, int>>);
|
|
static_assert(is_of_type<1 * s * (1 * Hz), quantity<derived_unit<struct si::hertz, struct si::second>{}, int>>);
|
|
static_assert(is_of_type<1 / (1 * min), quantity<derived_unit<struct one, per<struct si::minute>>{}, int>>);
|
|
static_assert(is_of_type<1 / (1 * Hz), quantity<derived_unit<struct one, per<struct si::hertz>>{}, int>>);
|
|
static_assert(is_of_type<1 / (1 * km), quantity<derived_unit<struct one, per<si::kilo_<struct si::metre>>>{}, int>>);
|
|
static_assert(is_of_type<1 / min, quantity<derived_unit<struct one, per<struct si::minute>>{}, int>>);
|
|
static_assert(is_of_type<1 / Hz, quantity<derived_unit<struct one, per<struct si::hertz>>{}, int>>);
|
|
static_assert(is_of_type<1 / km, quantity<derived_unit<struct one, per<si::kilo_<struct si::metre>>>{}, int>>);
|
|
static_assert(
|
|
is_of_type<1 * km / (1 * m), quantity<derived_unit<si::kilo_<struct si::metre>, per<struct si::metre>>{}, int>>);
|
|
static_assert(is_of_type<1 * m / (1 * s), quantity<derived_unit<struct si::metre, per<struct si::second>>{}, int>>);
|
|
static_assert(is_of_type<1 * m / (1 * min), quantity<derived_unit<struct si::metre, per<struct si::minute>>{}, int>>);
|
|
static_assert(is_of_type<1 * min / (1 * m), quantity<derived_unit<struct si::minute, per<struct si::metre>>{}, 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<isq::speed[m / s], int>>);
|
|
static_assert(
|
|
is_of_type<5 * isq::speed[m / s] + 10 * isq::length[m] / (2 * isq::time[s]), quantity<isq::speed[m / s], int>>);
|
|
static_assert(
|
|
is_of_type<10 * isq::length[m] / (2 * isq::time[s]) - 5 * isq::speed[m / s], quantity<isq::speed[m / s], int>>);
|
|
static_assert(
|
|
is_of_type<5 * isq::speed[m / s] - 10 * isq::length[m] / (2 * isq::time[s]), quantity<isq::speed[m / s], int>>);
|
|
|
|
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<isq::frequency[Hz], int>>);
|
|
static_assert(
|
|
is_of_type<5 * isq::frequency[Hz] + 10 / (2 * isq::period_duration[s]), quantity<isq::frequency[Hz], int>>);
|
|
static_assert(
|
|
is_of_type<10 / (2 * isq::period_duration[s]) - 5 * isq::frequency[Hz], quantity<isq::frequency[Hz], int>>);
|
|
static_assert(
|
|
is_of_type<5 * isq::frequency[Hz] - 10 / (2 * isq::period_duration[s]), quantity<isq::frequency[Hz], int>>);
|
|
|
|
// Different named dimensions
|
|
template<typename... Ts>
|
|
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]));
|
|
|
|
// 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<isq::speed[km / h]> speed = 120 * isq::length[km] / (2 * isq::time[h]);
|
|
|
|
// dimensionless
|
|
static_assert((3 * one *= 2 * one) == 6 * one);
|
|
static_assert((6 * one /= 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);
|
|
|
|
// 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<decltype(0 * one + 0.0 * one), decltype(0.0 * one)>);
|
|
static_assert(is_same_v<decltype(0 * one - 0.0 * one), decltype(0.0 * one)>);
|
|
static_assert(is_same_v<decltype(0.0 * one + 0 * one), decltype(0.0 * one)>);
|
|
static_assert(is_same_v<decltype(0.0 * one - 0 * one), decltype(0.0 * one)>);
|
|
|
|
static_assert(1 * one - 30 * percent == (100 - 30) * percent);
|
|
static_assert(1 * one + 30 * percent == (100 + 30) * percent);
|
|
|
|
static_assert(is_same_v<decltype(std::uint8_t{0} * one + std::uint8_t{0} * one)::rep, int>);
|
|
static_assert(is_same_v<decltype(std::uint8_t{0} * one - std::uint8_t{0} * one)::rep, int>);
|
|
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<decltype(std::uint8_t{0} * one % (std::uint8_t{0} * one))::rep,
|
|
decltype(std::uint8_t{0} % std::uint8_t{0})>);
|
|
|
|
static_assert(2 * one * (1 * m) == 2 * m);
|
|
static_assert(2 * one / (1 * m) == 2 / (1 * m));
|
|
|
|
|
|
///////////////////////
|
|
// equality operators
|
|
///////////////////////
|
|
|
|
static_assert(std::equality_comparable_with<quantity<si::metre>, quantity<si::metre>>);
|
|
static_assert(std::equality_comparable_with<quantity<si::metre>, quantity<si::metre, int>>);
|
|
static_assert(std::equality_comparable_with<quantity<si::metre>, quantity<si::kilo<si::metre>, int>>);
|
|
static_assert(std::equality_comparable_with<quantity<si::metre, int>, quantity<si::kilo<si::metre>, int>>);
|
|
static_assert(std::equality_comparable_with<quantity<isq::length[si::metre]>, quantity<si::metre>>);
|
|
static_assert(std::equality_comparable_with<quantity<isq::length[si::metre]>, quantity<si::metre, int>>);
|
|
static_assert(std::equality_comparable_with<quantity<isq::length[si::metre]>, quantity<si::kilo<si::metre>, int>>);
|
|
static_assert(std::equality_comparable_with<quantity<isq::width[si::metre]>, quantity<si::metre>>);
|
|
static_assert(std::equality_comparable_with<quantity<isq::width[si::metre]>, quantity<si::metre, int>>);
|
|
static_assert(std::equality_comparable_with<quantity<isq::width[si::metre]>, quantity<si::kilo<si::metre>, int>>);
|
|
static_assert(std::equality_comparable_with<quantity<isq::width[si::metre]>, quantity<isq::height[si::metre]>>);
|
|
static_assert(std::equality_comparable_with<quantity<isq::width[si::metre]>, quantity<isq::height[si::metre], int>>);
|
|
static_assert(
|
|
std::equality_comparable_with<quantity<isq::width[si::metre]>, quantity<isq::height[si::kilo<si::metre>], int>>);
|
|
|
|
template<auto M>
|
|
concept no_crossdimensional_equality = requires {
|
|
requires !requires { 1 * s == 1 * M; };
|
|
requires !requires { 1 * s != 1 * M; };
|
|
};
|
|
static_assert(no_crossdimensional_equality<si::metre>);
|
|
|
|
// 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</*Reference*/ auto R1, /*Reference*/ auto R2> // TODO Use `Reference` when Clang supports it.
|
|
concept invalid_comparison = !requires { 2 * R1 == 2 * R2; } && !requires { 2 * R2 == 2 * R1; };
|
|
static_assert(invalid_comparison<isq::activity[Bq], isq::frequency[Hz]>);
|
|
|
|
|
|
///////////////////////
|
|
// ordering operators
|
|
///////////////////////
|
|
|
|
template<auto M>
|
|
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<si::metre>);
|
|
|
|
// 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));
|
|
|
|
|
|
//////////////////
|
|
// dimensionless
|
|
//////////////////
|
|
|
|
static_assert(is_of_type<10 * km / (5 * km), quantity<one, int>>);
|
|
|
|
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);
|
|
|
|
|
|
//////////////////
|
|
// value_cast
|
|
//////////////////
|
|
|
|
static_assert(value_cast<m>(2 * km).numerical_value_in(m) == 2000);
|
|
static_assert(value_cast<km>(2000 * m).numerical_value_in(km) == 2);
|
|
static_assert(value_cast<int>(1.23 * m).numerical_value_in(m) == 1);
|
|
static_assert(value_cast<km / h>(2000.0 * m / (3600.0 * s)).numerical_value_in(km / h) == 2);
|
|
|
|
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);
|
|
|
|
//////////////////
|
|
// quantity_cast
|
|
//////////////////
|
|
|
|
static_assert(is_of_type<quantity_cast<isq::distance>(1 * m), quantity<isq::distance[m], int>>);
|
|
static_assert(is_of_type<quantity_cast<isq::distance>(isq::length(1 * m)), quantity<isq::distance[m], int>>);
|
|
static_assert(is_of_type<quantity_cast<kind_of<isq::length>>(isq::length(1 * m)), quantity<si::metre, int>>);
|
|
static_assert(is_of_type<quantity_cast<kind_of<isq::length>>(isq::distance(1 * m)), quantity<si::metre, int>>);
|
|
|
|
|
|
// QuantityOf
|
|
static_assert(QuantityOf<quantity<isq::length[m]>, isq::length>);
|
|
static_assert(QuantityOf<quantity<isq::width[m]>, isq::length>);
|
|
static_assert(QuantityOf<quantity<isq::position_vector[m], int>, isq::length>);
|
|
static_assert(!QuantityOf<quantity<isq::length[m]>, isq::width>);
|
|
static_assert(QuantityOf<quantity<m>, isq::width>);
|
|
static_assert(QuantityOf<quantity<m>, isq::position_vector>);
|
|
static_assert(QuantityOf<quantity<kind_of<isq::length>[m]>, isq::width>);
|
|
static_assert(QuantityOf<quantity<kind_of<isq::length>[m]>, isq::position_vector>);
|
|
static_assert(!QuantityOf<quantity<isq::width[m]>, isq::altitude>);
|
|
|
|
static_assert(QuantityOf<quantity<isq::speed[m / s]>, isq::speed>);
|
|
static_assert(QuantityOf<quantity<isq::speed[m / s]>, isq::length / isq::time>);
|
|
static_assert(QuantityOf<quantity<m / s>, isq::length / isq::time>);
|
|
static_assert(QuantityOf<quantity<kind_of<isq::speed>[m / s]>, isq::length / isq::time>);
|
|
static_assert(!QuantityOf<quantity<isq::speed[m / s]>, isq::distance / isq::duration>);
|
|
static_assert(!QuantityOf<quantity<isq::speed[m / s]>, isq::width / isq::duration>);
|
|
static_assert(QuantityOf<quantity<m / s>, isq::width / isq::duration>);
|
|
static_assert(QuantityOf<quantity<kind_of<isq::speed>[m / s]>, isq::width / isq::duration>);
|
|
static_assert(!QuantityOf<quantity<isq::speed[m / s]>, isq::position_vector / isq::duration>);
|
|
static_assert(QuantityOf<quantity<m / s>, isq::position_vector / isq::duration>);
|
|
static_assert(QuantityOf<quantity<kind_of<isq::speed>[m / s]>, isq::position_vector / isq::duration>);
|
|
static_assert(QuantityOf<quantity<isq::velocity[m / s], int>, isq::position_vector / isq::duration>);
|
|
|
|
static_assert(QuantityOf<decltype(10 * m), isq::height>); // kind of
|
|
static_assert(QuantityOf<decltype(10 * kind_of<isq::length>[m]), isq::height>); // kind of
|
|
static_assert(!QuantityOf<decltype(10 * isq::length[m]), isq::height>); // different kinds
|
|
static_assert(!QuantityOf<decltype(10 * isq::width[m]), isq::height>); // different kinds
|
|
static_assert(QuantityOf<decltype(10 * isq::speed[m / s]), isq::speed>);
|
|
static_assert(QuantityOf<decltype(20 * isq::length[m] / (2 * isq::time[s])), isq::speed>); // derived unnamed quantity
|
|
|
|
} // namespace
|