refactor: 💥 basic_concepts, quantity and quantity_cast refactored

BREAKING CHANGE:  ScalableNumber renamed to QuantityValue

Resolves #107
This commit is contained in:
Mateusz Pusz
2020-10-06 18:17:52 +02:00
parent 40f205b381
commit cfc90f4aac
96 changed files with 1620 additions and 1017 deletions

View File

@ -1 +0,0 @@
docs/CHANGELOG.md

0
CHANGELOG.md Normal file
View File

View File

@ -11,7 +11,7 @@ Interface
The difference is that it uses ``double`` as a default representation and has
a few additional member types and functions::
template<Dimension D, UnitOf<D> U, ScalableNumber Rep = double>
template<Dimension D, UnitOf<D> U, QuantityValue Rep = double>
class quantity {
public:
using dimension = D;
@ -27,7 +27,7 @@ a few additional member types and functions::
[[nodiscard]] constexpr Quantity auto operator*(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs);
template<ScalableNumber Value, typename D, typename U, typename Rep>
template<QuantityValue Value, typename D, typename U, typename Rep>
requires std::magma<std::ranges::divided_by, Value, Rep>
[[nodiscard]] constexpr Quantity auto operator/(const Value& v,
const quantity<D, U, Rep>& q);

View File

@ -18,7 +18,7 @@ For example the speed of light constant in :term:`SI` is defined as::
namespace si::si2019 {
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto speed_of_light = speed<metre_per_second, Rep>(299792458);
}
@ -27,7 +27,7 @@ The same constant defined for natural units may be provided as::
namespace natural {
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto speed_of_light = speed<one, Rep>(1);
}

View File

@ -58,8 +58,8 @@ dimension, than we will end up with just a scalable number type:
Time auto dur1 = 10_q_s;
Time auto dur2 = 2_q_s;
Frequency auto fr1 = 5_q_Hz;
ScalableNumber auto v1 = dur1 / dur2; // 5
ScalableNumber auto v2 = dur1 * fr1; // 50
QuantityValue auto v1 = dur1 / dur2; // 5
QuantityValue auto v2 = dur1 * fr1; // 50
Quantity points have a more restricted set of operations.
Quantity points can't be added together,

View File

@ -34,7 +34,7 @@ type to ``double`` by default::
namespace si {
template<Unit U, ScalableNumber Rep = double>
template<Unit U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
}
@ -75,7 +75,7 @@ be used::
All instances of `quantity` class always match the `Quantity` concept.
All other regular types that are not quantities are called
:term:`scalable numbers <scalable number>` by the library and match the
`ScalableNumber` concept.
`QuantityValue` concept.
However, the above is not the most important usage of those concepts. Let's
assume that the user wants to implement an ``avg_speed`` function that will
@ -185,7 +185,7 @@ are provided::
template<typename T>
concept Dimensionless = QuantityOf<T, dim_one>;
template<Unit U, ScalableNumber Rep = double>
template<Unit U, QuantityValue Rep = double>
using dimensionless = quantity<dim_one, U, Rep>;
There are two special units provided for usage with such a quantity:

View File

@ -63,7 +63,7 @@ Concepts
satisfy :expr:`Quantity<typename T::value_type>` recursively
(i.e. ``std::optional<si::length<si::metre>>``).
.. concept:: template<typename T> ScalableNumber
.. concept:: template<typename T> QuantityValue
A concept matching non-Quantity types. Satisfied by types that match
:expr:`(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>` and satisfy one of the

View File

@ -9,11 +9,11 @@ its own custom logic for it (i.e. use a complex number or a measurement class th
not only a value but also a measurement error).
A `ScalableNumber` concept
A `QuantityValue` concept
--------------------------
To support a minimum set of `quantity` operations all custom representation types have to
satisfy at least the `ScalableNumber` concept. Which means that they:
satisfy at least the `QuantityValue` concept. Which means that they:
- cannot be quantities by themselves,
- cannot be wrappers over the `quantity` type (i.e. ``std::optional<si::length<si::metre>>``),
@ -101,7 +101,7 @@ The only difference here is that in this case we have to explicitly cast the `qu
Additional Requirements
-----------------------
As noted in the previous chapter, the `ScalableNumber` concept guarantees us the possibility
As noted in the previous chapter, the `QuantityValue` concept guarantees us the possibility
to construct quantities, convert between the units of the same dimension, and compare them
for equality. To provide additional `quantity` operations the custom representation type
have to satisfy more requirements.
@ -200,7 +200,7 @@ The `quantity` class template has a few static member functions: `quantity::zero
representation type. The default implementation is provided through the `quantity_values` class
template::
template<ScalableNumber Rep>
template<QuantityValue Rep>
struct quantity_values {
static constexpr Rep zero() noexcept { return Rep(0); }
static constexpr Rep one() noexcept { return Rep(1); }
@ -220,7 +220,7 @@ library's framework treat floating-point representation types differently than t
ones. This behavior can also be extended to the custom representation types with
`treat_as_floating_point` customization point which default definition is::
template<ScalableNumber Rep>
template<QuantityValue Rep>
inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
If our representation type should have a floating-point semantics or if it is a class

View File

@ -121,7 +121,7 @@ coherent unit::
struct desk_per_hour : deduced_unit<desk_per_hour, dim_desk_rate, desk, si::hour> {};
// a quantity of our dimension
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_desk_rate> U, QuantityValue Rep = double>
using desk_rate = quantity<dim_desk_rate, U, Rep>;
// a concept matching the above quantity
@ -150,7 +150,7 @@ define a new base dimension, its units, quantity helper, concept, and UDLs::
struct person : named_unit<person, "person", no_prefix> {};
struct dim_people : base_dimension<"people", person> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_people> U, QuantityValue Rep = double>
using people = quantity<dim_people, U, Rep>;
template<typename T>
@ -169,7 +169,7 @@ With the above we can now define a new derived dimension::
struct person_per_desk : deduced_unit<person_per_desk, dim_occupancy_rate, person, desk> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_occupancy_rate> U, QuantityValue Rep = double>
using occupancy_rate = quantity<dim_occupancy_rate, U, Rep>;
template<typename T>
@ -216,7 +216,7 @@ Such units do not share their references with base units of other systems:
struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
} // namespace fps
@ -236,7 +236,7 @@ different systems:
struct dim_length : base_dimension<"L", metre> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
namespace fps {
@ -246,7 +246,7 @@ different systems:
struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
} // namespace fps

View File

@ -33,7 +33,7 @@ struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio(3), foot> {};
struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
} // namespace fps
@ -45,7 +45,7 @@ struct kilometre : prefixed_unit<kilometre, units::physical::si::kilo, metre> {}
struct dim_length : base_dimension<"L", metre> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
namespace fps {
@ -55,7 +55,7 @@ struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio(3), foot> {};
struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
} // namespace fps

View File

@ -87,7 +87,7 @@ public:
}
template<typename V>
requires (ScalableNumber<V> || Dimensionless<V>)
requires (QuantityValue<V> || Dimensionless<V>)
[[nodiscard]] friend constexpr auto operator*(const vector& lhs, const V& value)
requires requires { lhs.magnitude() * value; }
{
@ -95,7 +95,7 @@ public:
}
template<typename V>
requires (ScalableNumber<V> || Dimensionless<V>)
requires (QuantityValue<V> || Dimensionless<V>)
[[nodiscard]] friend constexpr auto operator*(const V& value, const vector& rhs)
requires requires { value * rhs.magnitude(); }
{

View File

@ -200,10 +200,10 @@ void matrix_of_quantity_tests()
matrix_of_quantity_divide_by_scalar();
}
template<units::Unit U = si::metre, units::ScalableNumber Rep = double>
template<units::Unit U = si::metre, units::QuantityValue Rep = double>
using length_v = si::length<U, vector<Rep>>;
template<units::Unit U = si::newton, units::ScalableNumber Rep = double>
template<units::Unit U = si::newton, units::QuantityValue Rep = double>
using force_v = si::force<U, vector<Rep>>;
void quantity_of_vector_add()
@ -273,7 +273,7 @@ void quantity_of_vector_tests()
quantity_of_vector_divide_by_scalar();
}
template<units::Unit U = si::metre, units::ScalableNumber Rep = double>
template<units::Unit U = si::metre, units::QuantityValue Rep = double>
using length_m = si::length<U, matrix<Rep>>;
void quantity_of_matrix_add()

View File

@ -114,15 +114,13 @@ private:
value_type uncertainty_{};
};
static_assert(units::ScalableNumber<measurement<double>>);
} // namespace
template<typename T>
inline constexpr bool units::treat_as_floating_point<measurement<T>> = std::is_floating_point_v<T>;
namespace {
static_assert(units::QuantityValue<measurement<double>>);
void example()
{
using namespace units::physical;

View File

@ -25,6 +25,7 @@
#include <units/bits/external/downcasting.h>
#include <units/bits/external/fixed_string.h>
#include <units/bits/external/hacks.h>
#include <units/customization_points.h>
#include <units/ratio.h>
#include <units/bits/external/type_traits.h>
#include <functional>
@ -229,6 +230,34 @@ template<typename T>
concept QuantityPoint = detail::is_quantity_point<T>;
// QuantityValue
template<typename T, typename U>
concept common_type_with_ =
std::same_as<std::common_type_t<T, U>, std::common_type_t<U, T>> &&
std::constructible_from<std::common_type_t<T, U>, T> &&
std::constructible_from<std::common_type_t<T, U>, U>;
template<typename T, typename U = T>
concept scalable_number_ = // exposition only
std::regular_invocable<std::multiplies<>, T, U> &&
std::regular_invocable<std::divides<>, T, U>;
template<typename T>
concept castable_number_ = // exposition only
common_type_with_<T, std::intmax_t> &&
scalable_number_<std::common_type_t<T, std::intmax_t>>;
template<typename T>
concept scalable_ = // exposition only
castable_number_<T> ||
(requires { typename T::value_type; } && castable_number_<typename T::value_type> && scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
template<typename From, typename To>
concept scalable_with_ = // exposition only
common_type_with_<From, To> &&
scalable_<std::common_type_t<From, To>>;
// WrappedQuantity
namespace detail {
@ -248,31 +277,8 @@ inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::value_type>
* recursively (i.e. `std::optional<si::length<si::metre>>`).
*/
template<typename T>
concept WrappedQuantity = detail::is_wrapped_quantity<T>;
// ScalableNumber
namespace detail {
template<typename T>
concept constructible_from_integral =
// construction from an integral type
std::constructible_from<T, std::int64_t> &&
// unit scaling
std::regular_invocable<std::multiplies<>, T, T> &&
std::regular_invocable<std::divides<>, T, T>;
template<typename T>
concept not_constructible_from_integral =
// not construction from an integral type
(!std::constructible_from<T, std::int64_t>) &&
// scaling by the value from ratio
std::regular_invocable<std::multiplies<>, T, std::int64_t> &&
std::regular_invocable<std::multiplies<>, std::int64_t, T>; // &&
// std::regular_invocable<std::divides<>, T, std::int64_t>; // TODO Uncomment when a bug in LA is fixed
} // namespace detail
concept wrapped_quantity_ = // exposition only
detail::is_wrapped_quantity<T>;
/**
* @brief A concept matching non-Quantity types.
@ -280,10 +286,10 @@ concept not_constructible_from_integral =
* Satisfied by types that satisfy `(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>`.
*/
template<typename T>
concept ScalableNumber =
concept QuantityValue =
(!Quantity<T>) &&
(!WrappedQuantity<T>) &&
(!wrapped_quantity_<T>) &&
std::regular<T> &&
(detail::constructible_from_integral<T> || detail::not_constructible_from_integral<T>);
scalable_<T>;
} // namespace units

View File

@ -28,10 +28,10 @@
namespace units {
template<Dimension D, UnitOf<D> U, ScalableNumber Rep>
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity;
template<Dimension D, UnitOf<D> U, ScalableNumber Rep>
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity_point;
namespace detail {
@ -70,8 +70,7 @@ quantity_point<D, U, Rep> common_quantity_point_impl(quantity<D, U, Rep>);
} // namespace detail
template<Quantity Q1, Quantity Q2, ScalableNumber Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>>
requires equivalent<typename Q1::dimension, typename Q2::dimension>
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2, QuantityValue Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>>
using common_quantity = TYPENAME detail::common_quantity_impl<Q1, Q2, Rep>::type;
template<QuantityPoint QP1, QuantityPoint QP2>

View File

@ -73,7 +73,10 @@ concept QuantityOfT = Quantity<Q> && DimensionOfT<typename Q::dimension, DimTemp
* Satisfied by all quantities with a dimension being the instantiation derived from
* the provided dimension type.
*/
template<typename T, typename Dim>
concept QuantityOf = Quantity<T> && Dimension<Dim> && equivalent<typename T::dimension, Dim>;
template<typename Q, typename Dim>
concept QuantityOf = Quantity<Q> && Dimension<Dim> && equivalent<typename Q::dimension, Dim>;
template<typename Q1, typename Q2>
concept QuantityEquivalentTo = Quantity<Q1> && QuantityOf<Q2, typename Q1::dimension>;
} // namespace units

View File

@ -22,7 +22,6 @@
#pragma once
#include <units/concepts.h>
#include <limits>
#include <type_traits>
@ -37,9 +36,13 @@ namespace units {
*
* @tparam Rep a representation type for which a type trait is defined
*/
template<ScalableNumber Rep>
template<typename Rep>
inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
template<typename T>
requires requires { typename T::value_type; }
inline constexpr bool treat_as_floating_point<T> = treat_as_floating_point<typename T::value_type>;
/**
* @brief A type trait that defines zero, one, min, and max for a representation type
*
@ -49,7 +52,7 @@ inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
*
* @tparam Rep a representation type for which a type trait is defined
*/
template<ScalableNumber Rep>
template<typename Rep>
struct quantity_values {
static constexpr Rep zero() noexcept { return Rep(0); }
static constexpr Rep one() noexcept { return Rep(1); }

View File

@ -48,7 +48,7 @@ struct dim_information : base_dimension<"information", bit> {};
template<typename T>
concept Information = QuantityOf<T, dim_information>;
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_information> U, QuantityValue Rep = double>
using information = quantity<dim_information, U, Rep>;
inline namespace literals {

View File

@ -41,7 +41,7 @@ struct pebibit_per_second : deduced_unit<pebibit_per_second, dim_bitrate, pebibi
template<typename T>
concept Bitrate = QuantityOf<T, dim_bitrate>;
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_bitrate> U, QuantityValue Rep = double>
using bitrate = quantity<dim_bitrate, U, Rep>;
inline namespace literals {

View File

@ -35,7 +35,7 @@ struct dim_angle : base_dimension<"A", U> {};
template<typename T>
concept Angle = QuantityOfT<T, dim_angle>;
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_angle<>> U, QuantityValue Rep = double>
using angle = quantity<dim_angle<>, U, Rep>;
inline namespace literals {

View File

@ -40,7 +40,7 @@ struct dim_one : derived_dimension<dim_one, one> {};
template<typename T>
concept Dimensionless = QuantityOf<T, dim_one>;
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_one> U, QuantityValue Rep = double>
using dimensionless = quantity<dim_one, U, Rep>;
} // namespace units

View File

@ -29,35 +29,35 @@
namespace units::physical::natural {
struct dim_length : physical::dim_length<inverted_gigaelectronvolt> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
struct dim_time : physical::dim_time<inverted_gigaelectronvolt> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_time> U, QuantityValue Rep = double>
using time = quantity<dim_time, U, Rep>;
struct dim_mass : physical::dim_mass<gigaelectronvolt> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_mass> U, QuantityValue Rep = double>
using mass = quantity<dim_mass, U, Rep>;
struct dim_speed : physical::dim_speed<dim_speed, one, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_speed> U, QuantityValue Rep = double>
using speed = quantity<dim_speed, U, Rep>;
struct dim_acceleration : physical::dim_acceleration<dim_acceleration, gigaelectronvolt, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_acceleration> U, QuantityValue Rep = double>
using acceleration = quantity<dim_acceleration, U, Rep>;
struct dim_force : physical::dim_force<dim_force, square_gigaelectronvolt, dim_mass, dim_acceleration> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_force> U, QuantityValue Rep = double>
using force = quantity<dim_force, U, Rep>;
struct dim_momentum : physical::dim_momentum<dim_momentum, gigaelectronvolt, dim_mass, dim_speed> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_momentum> U, QuantityValue Rep = double>
using momentum = quantity<dim_momentum, U, Rep>;
struct dim_energy : physical::dim_energy<dim_energy, gigaelectronvolt, dim_force, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_force> U, QuantityValue Rep = double>
using energy = quantity<dim_force, U, Rep>;
// Typical UDLs will not work here as the same units are reused by many quantities.

View File

@ -26,7 +26,7 @@
namespace units::physical::natural {
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto speed_of_light = speed<one, Rep>(1);
} // namespace units::physical::natural

View File

@ -32,7 +32,7 @@ struct mole : named_unit<mole, "mol", prefix> {};
struct dim_amount_of_substance : physical::dim_amount_of_substance<mole> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_amount_of_substance> U, QuantityValue Rep = double>
using amount_of_substance = quantity<dim_amount_of_substance, U, Rep>;
inline namespace literals {

View File

@ -52,7 +52,7 @@ struct yottaampere : prefixed_unit<yottaampere, yotta, ampere> {};
struct dim_electric_current : physical::dim_electric_current<ampere> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_electric_current> U, QuantityValue Rep = double>
using electric_current = quantity<dim_electric_current, U, Rep>;
inline namespace literals {

View File

@ -54,7 +54,7 @@ struct astronomical_unit : named_scaled_unit<astronomical_unit, "au", no_prefix,
struct dim_length : physical::dim_length<metre> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
inline namespace literals {

View File

@ -52,7 +52,7 @@ struct yottacandela : prefixed_unit<yottacandela, yotta, candela> {};
struct dim_luminous_intensity : physical::dim_luminous_intensity<candela> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_luminous_intensity> U, QuantityValue Rep = double>
using luminous_intensity = quantity<dim_luminous_intensity, U, Rep>;
inline namespace literals {

View File

@ -76,7 +76,7 @@ struct dalton : named_scaled_unit<dalton, "Da", no_prefix, ratio(16'605'390'666'
struct dim_mass : physical::dim_mass<kilogram> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_mass> U, QuantityValue Rep = double>
using mass = quantity<dim_mass, U, Rep>;
inline namespace literals {

View File

@ -31,7 +31,7 @@ struct kelvin : named_unit<kelvin, "K", no_prefix> {};
struct dim_thermodynamic_temperature : physical::dim_thermodynamic_temperature<kelvin> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_thermodynamic_temperature> U, QuantityValue Rep = double>
using thermodynamic_temperature = quantity<dim_thermodynamic_temperature, U, Rep>;
inline namespace literals {

View File

@ -43,7 +43,7 @@ struct day : named_scaled_unit<day, "d", no_prefix, ratio(24), hour> {};
struct dim_time : physical::dim_time<second> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_time> U, QuantityValue Rep = double>
using time = quantity<dim_time, U, Rep>;
inline namespace literals {

View File

@ -31,7 +31,7 @@ using si::centimetre;
struct dim_length : physical::dim_length<centimetre> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
inline namespace literals {

View File

@ -31,7 +31,7 @@ using si::gram;
struct dim_mass : physical::dim_mass<gram> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_mass> U, QuantityValue Rep = double>
using mass = quantity<dim_mass, U, Rep>;
inline namespace literals {

View File

@ -31,7 +31,7 @@ namespace units::physical::si::cgs {
struct gal : named_unit<gal, "Gal", si::prefix> {};
struct dim_acceleration : physical::dim_acceleration<dim_acceleration, gal, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_acceleration> U, QuantityValue Rep = double>
using acceleration = quantity<dim_acceleration, U, Rep>;
inline namespace literals {

View File

@ -32,7 +32,7 @@ using si::square_centimetre;
struct dim_area : physical::dim_area<dim_area, square_centimetre, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_area> U, QuantityValue Rep = double>
using area = quantity<dim_area, U, Rep>;
inline namespace literals {

View File

@ -33,7 +33,7 @@ struct erg : named_unit<erg, "erg", si::prefix> {};
struct dim_energy : physical::dim_energy<dim_energy, erg, dim_force, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_energy> U, QuantityValue Rep = double>
using energy = quantity<dim_energy, U, Rep>;
inline namespace literals {

View File

@ -34,7 +34,7 @@ struct dyne : named_unit<dyne, "dyn", si::prefix> {};
struct dim_force : physical::dim_force<dim_force, dyne, dim_mass, dim_acceleration> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_force> U, QuantityValue Rep = double>
using force = quantity<dim_force, U, Rep>;
inline namespace literals {

View File

@ -33,7 +33,7 @@ struct erg_per_second : unit<erg_per_second> {};
struct dim_power : physical::dim_power<dim_power, erg_per_second, dim_energy, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_power> U, QuantityValue Rep = double>
using power = quantity<dim_power, U, Rep>;
inline namespace literals {

View File

@ -34,7 +34,7 @@ struct barye : named_unit<barye, "Ba", si::prefix> {};
struct dim_pressure : physical::dim_pressure<dim_pressure, barye, dim_force, dim_area> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_pressure> U, QuantityValue Rep = double>
using pressure = quantity<dim_pressure, U, Rep>;
inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si::cgs {
struct centimetre_per_second : unit<centimetre_per_second> {};
struct dim_speed : physical::dim_speed<dim_speed, centimetre_per_second, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_speed> U, QuantityValue Rep = double>
using speed = quantity<dim_speed, U, Rep>;
inline namespace literals {

View File

@ -32,31 +32,31 @@
namespace units::physical::si::si2019 {
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto planck_constant = energy<joule, Rep>(6.62607015e-34) * time<second, Rep>(1);
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto reduced_planck_constant = energy<gigaelectronvolt, Rep>(6.582119569e-10) * time<second, Rep>(1);
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto elementary_charge = electric_charge<coulomb, Rep>(1.602176634e-19);
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto boltzmann_constant = energy<joule, Rep>(1.380649e-23) / temperature<kelvin, Rep>(1);
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto avogadro_constant = Rep(6.02214076e23) / substance<mole, Rep>(1);
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto speed_of_light = speed<metre_per_second, Rep>(299'792'458);
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto hyperfine_structure_transition_frequency = frequency<hertz, Rep>(9'192'631'770);
// template<ScalableNumber Rep = double>
// template<QuantityValue Rep = double>
// inline constexpr auto luminous_efficacy = 683_q_lm / 1_q_W;
template<ScalableNumber Rep = double>
template<QuantityValue Rep = double>
inline constexpr auto standard_gravity = acceleration<metre_per_second_sq, Rep>(9.80665);
} // namespace units::physical::si::si2019

View File

@ -53,7 +53,7 @@ struct yottagray : prefixed_unit<yottagray, yotta, gray> {};
struct dim_absorbed_dose : physical::dim_absorbed_dose<dim_absorbed_dose, gray, dim_energy, dim_mass> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_absorbed_dose> U, QuantityValue Rep = double>
using absorbed_dose = quantity<dim_absorbed_dose, U, Rep>;
inline namespace literals {

View File

@ -31,7 +31,7 @@ namespace units::physical::si {
struct metre_per_second_sq : unit<metre_per_second_sq> {};
struct dim_acceleration : physical::dim_acceleration<dim_acceleration, metre_per_second_sq, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_acceleration> U, QuantityValue Rep = double>
using acceleration = quantity<dim_acceleration, U, Rep>;
inline namespace literals {

View File

@ -34,7 +34,7 @@ struct radian_per_second : named_unit<radian_per_second, basic_symbol_text{"ω",
struct dim_angular_velocity : physical::dim_angular_velocity<dim_angular_velocity, radian_per_second, dim_angle<>, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_angular_velocity> U, QuantityValue Rep = double>
using angular_velocity = quantity<dim_angular_velocity, U, Rep>;
inline namespace literals {

View File

@ -54,7 +54,7 @@ struct square_yottametre : deduced_unit<square_yottametre, dim_area, yottametre>
struct hectare : alias_unit<square_hectometre, "ha", no_prefix> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_area> U, QuantityValue Rep = double>
using area = quantity<dim_area, U, Rep>;
inline namespace literals {

View File

@ -54,7 +54,7 @@ struct yottafarad : prefixed_unit<yottafarad, yotta, farad> {};
struct dim_capacitance : physical::dim_capacitance<dim_capacitance, farad, dim_electric_charge, dim_voltage> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_capacitance> U, QuantityValue Rep = double>
using capacitance = quantity<dim_capacitance, U, Rep>;
inline namespace literals {

View File

@ -56,7 +56,7 @@ struct enzyme_unit : named_scaled_unit<enzyme_unit, "U", prefix, ratio(1, 60, -6
struct dim_catalytic_activity : physical::dim_catalytic_activity<dim_catalytic_activity, katal, dim_time, dim_amount_of_substance> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_catalytic_activity> U, QuantityValue Rep = double>
using catalytic_activity = quantity<dim_catalytic_activity, U, Rep>;
inline namespace literals {

View File

@ -36,10 +36,10 @@ struct coulomb_per_metre_sq : unit<coulomb_per_metre_sq> {};
struct dim_charge_density : physical::dim_charge_density<dim_charge_density, coulomb_per_metre_cub, dim_electric_charge, dim_length> {};
struct dim_surface_charge_density : physical::dim_surface_charge_density<dim_surface_charge_density, coulomb_per_metre_sq, dim_electric_charge, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_charge_density> U, QuantityValue Rep = double>
using charge_density = quantity<dim_charge_density, U, Rep>;
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_surface_charge_density> U, QuantityValue Rep = double>
using surface_charge_density = quantity<dim_surface_charge_density, U, Rep>;
inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si {
struct mol_per_metre_cub : unit<mol_per_metre_cub> {};
struct dim_concentration : physical::dim_concentration<dim_concentration, mol_per_metre_cub, dim_amount_of_substance, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_concentration> U, QuantityValue Rep = double>
using concentration = quantity<dim_concentration, U, Rep>;
inline namespace literals {

View File

@ -49,7 +49,7 @@ struct yottasiemens : prefixed_unit<yottasiemens, yotta, siemens> {};
struct dim_conductance : physical::dim_conductance<dim_conductance, siemens, dim_resistance> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_conductance> U, QuantityValue Rep = double>
using conductance = quantity<dim_conductance, U, Rep>;
inline namespace literals {

View File

@ -34,7 +34,7 @@ struct ampere_per_metre_sq : unit<ampere_per_metre_sq> {};
struct dim_current_density : physical::dim_current_density<dim_current_density, ampere_per_metre_sq, dim_electric_current, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_current_density> U, QuantityValue Rep = double>
using current_density = quantity<dim_current_density, U, Rep>;
inline namespace literals {

View File

@ -34,7 +34,7 @@ struct kilogram_per_metre_cub : unit<kilogram_per_metre_cub> {};
struct dim_density : physical::dim_density<dim_density, kilogram_per_metre_cub, dim_mass, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_density> U, QuantityValue Rep = double>
using density = quantity<dim_density, U, Rep>;
inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si {
struct pascal_second : unit<pascal_second> {};
struct dim_dynamic_viscosity : physical::dim_dynamic_viscosity<dim_dynamic_viscosity, pascal_second, dim_pressure, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_dynamic_viscosity> U, QuantityValue Rep = double>
using dynamic_viscosity = quantity<dim_dynamic_viscosity, U, Rep>;
inline namespace literals {

View File

@ -33,7 +33,7 @@ struct coulomb : named_unit<coulomb, "C", prefix> {};
struct dim_electric_charge : physical::dim_electric_charge<dim_electric_charge, coulomb, dim_time, dim_electric_current> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_electric_charge> U, QuantityValue Rep = double>
using electric_charge = quantity<dim_electric_charge, U, Rep>;
inline namespace literals {

View File

@ -31,7 +31,7 @@ namespace units::physical::si {
struct volt_per_metre : unit<volt_per_metre> {};
struct dim_electric_field_strength : physical::dim_electric_field_strength<dim_electric_field_strength, volt_per_metre, dim_voltage, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_electric_field_strength> U, QuantityValue Rep = double>
using electric_field_strength = quantity<dim_electric_field_strength, U, Rep>;
inline namespace literals {

View File

@ -52,7 +52,7 @@ struct gigaelectronvolt : prefixed_unit<gigaelectronvolt, giga, electronvolt> {}
struct dim_energy : physical::dim_energy<dim_energy, joule, dim_force, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_energy> U, QuantityValue Rep = double>
using energy = quantity<dim_energy, U, Rep>;
inline namespace literals {

View File

@ -54,7 +54,7 @@ struct yottanewton : prefixed_unit<yottanewton, yotta, newton> {};
struct dim_force : physical::dim_force<dim_force, newton, dim_mass, dim_acceleration> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_force> U, QuantityValue Rep = double>
using force = quantity<dim_force, U, Rep>;
inline namespace literals {

View File

@ -48,7 +48,7 @@ struct yottahertz : prefixed_unit<yottahertz, yotta, hertz> {};
struct dim_frequency : physical::dim_frequency<dim_frequency, hertz, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_frequency> U, QuantityValue Rep = double>
using frequency = quantity<dim_frequency, U, Rep>;
inline namespace literals {

View File

@ -39,13 +39,13 @@ struct dim_heat_capacity : physical::dim_heat_capacity<dim_heat_capacity, joule_
struct dim_specific_heat_capacity : physical::dim_specific_heat_capacity<dim_specific_heat_capacity, joule_per_kilogram_kelvin, dim_heat_capacity, dim_mass> {};
struct dim_molar_heat_capacity : physical::dim_molar_heat_capacity<dim_molar_heat_capacity, joule_per_mole_kelvin, dim_heat_capacity, dim_amount_of_substance> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_heat_capacity> U, QuantityValue Rep = double>
using heat_capacity = quantity<dim_heat_capacity, U, Rep>;
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_specific_heat_capacity> U, QuantityValue Rep = double>
using specific_heat_capacity = quantity<dim_specific_heat_capacity, U, Rep>;
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_molar_heat_capacity> U, QuantityValue Rep = double>
using molar_heat_capacity = quantity<dim_molar_heat_capacity, U, Rep>;
inline namespace literals {

View File

@ -50,7 +50,7 @@ struct yottahenry : prefixed_unit<yottahenry, yotta, henry> {};
struct dim_inductance : physical::dim_inductance<dim_inductance, henry, dim_magnetic_flux, dim_electric_current> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_inductance> U, QuantityValue Rep = double>
using inductance = quantity<dim_inductance, U, Rep>;
inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si {
struct candela_per_metre_sq : unit<candela_per_metre_sq> {};
struct dim_luminance : physical::dim_luminance<dim_luminance, candela_per_metre_sq, dim_luminous_intensity, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_luminance> U, QuantityValue Rep = double>
using luminance = quantity<dim_luminance, U, Rep>;
inline namespace literals {

View File

@ -50,7 +50,7 @@ struct yottaweber : prefixed_unit<yottaweber, yotta, weber> {};
struct dim_magnetic_flux : physical::dim_magnetic_flux<dim_magnetic_flux, weber, dim_magnetic_induction, dim_area> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_magnetic_flux> U, QuantityValue Rep = double>
using magnetic_flux = quantity<dim_magnetic_flux, U, Rep>;
inline namespace literals {

View File

@ -54,7 +54,7 @@ struct gauss : named_scaled_unit<gauss, "G", prefix, ratio(1, 10'000), tesla> {}
struct dim_magnetic_induction : physical::dim_magnetic_induction<dim_magnetic_induction, tesla, dim_voltage, dim_time, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_magnetic_induction> U, QuantityValue Rep = double>
using magnetic_induction = quantity<dim_magnetic_induction, U, Rep>;
inline namespace literals {

View File

@ -34,7 +34,7 @@ struct joule_per_mole : unit<joule_per_mole> {};
struct dim_molar_energy : physical::dim_molar_energy<dim_molar_energy, joule_per_mole, dim_energy, dim_amount_of_substance> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_molar_energy> U, QuantityValue Rep = double>
using molar_energy = quantity<dim_molar_energy, U, Rep>;
inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si {
struct kilogram_metre_per_second : unit<kilogram_metre_per_second> {};
struct dim_momentum : physical::dim_momentum<dim_momentum, kilogram_metre_per_second, dim_mass, dim_speed> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_momentum> U, QuantityValue Rep = double>
using momentum = quantity<dim_momentum, U, Rep>;
inline namespace literals {

View File

@ -33,7 +33,7 @@ struct henry_per_metre : unit<henry_per_metre> {};
struct dim_permeability : physical::dim_permeability<dim_permeability, henry_per_metre, dim_inductance, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_permeability> U, QuantityValue Rep = double>
using permeability = quantity<dim_permeability, U, Rep>;
inline namespace literals {

View File

@ -33,7 +33,7 @@ struct farad_per_metre : unit<farad_per_metre> {};
struct dim_permittivity : physical::dim_permittivity<dim_permittivity, farad_per_metre, dim_capacitance, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_permittivity> U, QuantityValue Rep = double>
using permittivity = quantity<dim_permittivity, U, Rep>;
inline namespace literals {

View File

@ -49,7 +49,7 @@ struct yottawatt : prefixed_unit<yottawatt, yotta, watt> {};
struct dim_power : physical::dim_power<dim_power, watt, dim_energy, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_power> U, QuantityValue Rep = double>
using power = quantity<dim_power, U, Rep>;
inline namespace literals {

View File

@ -54,7 +54,7 @@ struct yottapascal : prefixed_unit<yottapascal, yotta, pascal> {};
struct dim_pressure : physical::dim_pressure<dim_pressure, pascal, dim_force, dim_area> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_pressure> U, QuantityValue Rep = double>
using pressure = quantity<dim_pressure, U, Rep>;
inline namespace literals {

View File

@ -50,7 +50,7 @@ struct yottaohm : prefixed_unit<yottaohm, yotta, ohm> {};
struct dim_resistance : physical::dim_resistance<dim_resistance, ohm, dim_voltage, dim_electric_current> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_resistance> U, QuantityValue Rep = double>
using resistance = quantity<dim_resistance, U, Rep>;
inline namespace literals {

View File

@ -34,7 +34,7 @@ struct dim_speed : physical::dim_speed<dim_speed, metre_per_second, dim_length,
struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_speed, kilometre, hour> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_speed> U, QuantityValue Rep = double>
using speed = quantity<dim_speed, U, Rep>;
inline namespace literals {

View File

@ -32,7 +32,7 @@ struct newton_per_metre : unit<newton_per_metre> {};
struct dim_surface_tension : physical::dim_surface_tension<dim_surface_tension, newton_per_metre, dim_force, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_surface_tension> U, QuantityValue Rep = double>
using surface_tension = quantity<dim_surface_tension, U, Rep>;
inline namespace literals {

View File

@ -33,7 +33,7 @@ struct watt_per_metre_kelvin : unit<watt_per_metre_kelvin> {};
struct dim_thermal_conductivity : physical::dim_thermal_conductivity<dim_thermal_conductivity, watt_per_metre_kelvin, dim_power, dim_length, dim_thermodynamic_temperature> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_thermal_conductivity> U, QuantityValue Rep = double>
using thermal_conductivity = quantity<dim_thermal_conductivity, U, Rep>;
inline namespace literals {

View File

@ -34,7 +34,7 @@ struct newton_metre : named_unit<newton_metre, "Nm", prefix> {};
struct dim_torque : physical::dim_torque<dim_torque, newton_metre, dim_energy, dim_angle<>> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_torque> U, QuantityValue Rep = double>
using torque = quantity<dim_torque, U, Rep>;
inline namespace literals {

View File

@ -54,7 +54,7 @@ struct yottavolt : prefixed_unit<yottavolt, yotta, volt> {};
struct dim_voltage : physical::dim_voltage<dim_voltage, volt, dim_power, dim_electric_current> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_voltage> U, QuantityValue Rep = double>
using voltage = quantity<dim_voltage, U, Rep>;
inline namespace literals {

View File

@ -74,7 +74,7 @@ struct exalitre : prefixed_unit<petalitre, exa, litre> {};
struct zettalitre : prefixed_alias_unit<cubic_megametre, zetta, litre> {};
struct yottalitre : prefixed_unit<yottalitre, yotta, litre> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_volume> U, QuantityValue Rep = double>
using volume = quantity<dim_volume, U, Rep>;
inline namespace literals {

View File

@ -51,7 +51,7 @@ struct nautical_mile : named_scaled_unit<nautical_mile, "mi(naut)", no_prefix, r
struct dim_length : physical::dim_length<foot> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>;
inline namespace literals {

View File

@ -32,7 +32,7 @@ struct pound : named_scaled_unit<pound, "lb", no_prefix, ratio(45'359'237, 100'0
struct dim_mass : physical::dim_mass<pound> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_mass> U, QuantityValue Rep = double>
using mass = quantity<dim_mass, U, Rep>;
struct grain : named_scaled_unit<grain, "gr", no_prefix, ratio(1, 7000), pound>{};

View File

@ -31,7 +31,7 @@ namespace units::physical::si::fps {
struct foot_per_second_sq : unit<foot_per_second_sq> {};
struct dim_acceleration : physical::dim_acceleration<dim_acceleration, foot_per_second_sq, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_acceleration> U, QuantityValue Rep = double>
using acceleration = quantity<dim_acceleration, U, Rep>;
inline namespace literals {

View File

@ -32,7 +32,7 @@ struct square_foot : unit<square_foot> {};
struct dim_area : physical::dim_area<dim_area, square_foot, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_area> U, QuantityValue Rep = double>
using area = quantity<dim_area, U, Rep>;
inline namespace literals {

View File

@ -33,7 +33,7 @@ struct pound_per_foot_cub : unit<pound_per_foot_cub> {};
struct dim_density : physical::dim_density<dim_density, pound_per_foot_cub, dim_mass, dim_length> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_density> U, QuantityValue Rep = double>
using density = quantity<dim_density, U, Rep>;
inline namespace literals {

View File

@ -39,7 +39,7 @@ struct foot_pound_force : noble_deduced_unit<foot_pound_force, dim_energy, pound
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_energy> U, QuantityValue Rep = double>
using energy = quantity<dim_energy, U, Rep>;

View File

@ -43,7 +43,7 @@ struct kip : alias_unit<kilopound_force, "klbf", no_prefix> {};
struct dim_force : physical::dim_force<dim_force, poundal, dim_mass, dim_acceleration> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_force> U, QuantityValue Rep = double>
using force = quantity<dim_force, U, Rep>;
inline namespace literals {

View File

@ -37,7 +37,7 @@ struct foot_pound_force_per_second : deduced_unit<foot_pound_force_per_second, d
struct horse_power : named_scaled_unit<horse_power, "hp", no_prefix, ratio(550), foot_pound_force_per_second> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_power> U, QuantityValue Rep = double>
using power = quantity<dim_power, U, Rep>;
inline namespace literals {

View File

@ -34,7 +34,7 @@ struct poundal_per_foot_sq : unit<poundal_per_foot_sq> {};
struct dim_pressure : physical::dim_pressure<dim_pressure, poundal_per_foot_sq, dim_force, dim_area> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_pressure> U, QuantityValue Rep = double>
using pressure = quantity<dim_pressure, U, Rep>;
struct pound_force_per_foot_sq : named_scaled_unit<pound_force_per_foot_sq, "lbf ft2", si::prefix, ratio(32'174'049, 1'000'000), poundal_per_foot_sq> {};

View File

@ -32,7 +32,7 @@ namespace units::physical::si::fps {
struct foot_per_second : unit<foot_per_second> {};
struct dim_speed : physical::dim_speed<dim_speed, foot_per_second, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_speed> U, QuantityValue Rep = double>
using speed = quantity<dim_speed, U, Rep>;
struct mile_per_hour : deduced_unit<mile_per_hour, dim_speed, mile, hour>{};

View File

@ -33,7 +33,7 @@ struct dim_volume : physical::dim_volume<dim_volume, cubic_foot, dim_length> {};
struct cubic_yard : deduced_unit<cubic_yard, dim_volume, yard> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_volume> U, QuantityValue Rep = double>
using volume = quantity<dim_volume, U, Rep>;
inline namespace literals {

View File

@ -34,19 +34,52 @@
namespace units {
namespace detail {
template<typename T>
concept floating_point_ = // exposition only
(Quantity<T> && treat_as_floating_point<typename T::rep>) ||
(!Quantity<T> && treat_as_floating_point<T>);
template<typename From, typename To>
concept safe_convertible = // exposition only
concept safe_convertible_to_ = // exposition only
!(Quantity<From>) &&
!(Quantity<To>) &&
std::convertible_to<From, To> &&
(treat_as_floating_point<To> || (!treat_as_floating_point<From>));
(floating_point_<To> || (!floating_point_<From>));
template<typename Rep, typename QuantityFrom, typename QuantityTo>
concept safe_divisible = // exposition only
treat_as_floating_point<Rep> ||
is_integral(quantity_ratio(QuantityFrom{}) / quantity_ratio(QuantityTo{}));
// QFrom ratio is an exact multiple of QTo
template<typename QFrom, typename QTo>
concept harmonic_ = // exposition only
Quantity<QFrom> &&
Quantity<QTo> &&
requires(QFrom from, QTo to) { requires is_integral(detail::quantity_ratio(from) / detail::quantity_ratio(to)); };
} // namespace detail
template<typename QFrom, typename QTo>
concept safe_castable_to_ = // exposition only
Quantity<QFrom> &&
QuantityOf<QTo, typename QFrom::dimension> &&
scalable_with_<typename QFrom::rep, typename QTo::rep> &&
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
template<typename Func, typename T, typename U>
concept quantity_value_for_ =
std::regular_invocable<Func, T, U> &&
QuantityValue<std::invoke_result_t<Func, T, U>>;
template<typename T, typename Func, typename U, typename V>
concept invoke_result_convertible_to_ =
QuantityValue<T> &&
quantity_value_for_<Func, U, V> &&
safe_convertible_to_<T, std::invoke_result_t<Func, U, V>>;
template<typename Func, typename Q, typename V>
concept have_quantity_for_ =
Quantity<Q> &&
(!Quantity<V>) &&
quantity_value_for_<Func, typename Q::rep, V>;
template<typename Func, Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires quantity_value_for_<Func, typename Q1::rep, typename Q2::rep>
using common_quantity_for = common_quantity<Q1, Q2, std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
/**
* @brief A quantity
@ -58,63 +91,69 @@ concept safe_divisible = // exposition only
* @tparam U a measurement unit of the quantity
* @tparam Rep a type to be used to represent values of a quantity
*/
template<Dimension D, UnitOf<D> U, ScalableNumber Rep = double>
template<Dimension D, UnitOf<D> U, QuantityValue Rep = double>
class quantity {
Rep value_;
public:
// member types
using dimension = D;
using unit = U;
using rep = Rep;
// static member functions
[[nodiscard]] static constexpr quantity zero() noexcept
requires requires { quantity_values<rep>::zero(); }
{
return quantity(quantity_values<rep>::zero());
}
[[nodiscard]] static constexpr quantity one() noexcept
requires requires { quantity_values<rep>::one(); }
{
return quantity(quantity_values<rep>::one());
}
[[nodiscard]] static constexpr quantity min() noexcept
requires requires { quantity_values<rep>::min(); }
{
return quantity(quantity_values<rep>::min());
}
[[nodiscard]] static constexpr quantity max() noexcept
requires requires { quantity_values<rep>::max(); }
{
return quantity(quantity_values<rep>::max());
}
// construction, assignment, destruction
quantity() = default;
quantity(const quantity&) = default;
quantity(quantity&&) = default;
template<ScalableNumber Value>
requires detail::safe_convertible<Value, rep>
constexpr explicit(!(equivalent<quantity, dimensionless<one, Rep>>)) quantity(const Value& v) : value_{static_cast<rep>(v)} {}
template<safe_convertible_to_<rep> Value>
explicit(!(equivalent<quantity, dimensionless<::units::one, rep>>)) constexpr quantity(const Value& v) : value_(static_cast<rep>(v)) {}
template<Quantity Q2>
requires equivalent<D, typename Q2::dimension> &&
detail::safe_convertible<typename Q2::rep, rep> &&
detail::safe_divisible<rep, Q2, quantity>
constexpr quantity(const Q2& q) : value_{quantity_cast<quantity>(q).count()} {}
template<safe_castable_to_<quantity> Q2>
constexpr quantity(const Q2& q) : value_(quantity_cast<quantity>(q).count()) {}
quantity& operator=(const quantity&) = default;
quantity& operator=(quantity&&) = default;
// data access
[[nodiscard]] constexpr rep count() const noexcept { return value_; }
[[nodiscard]] static constexpr quantity zero() noexcept
requires requires { quantity_values<Rep>::zero(); }
// member unary operators
[[nodiscard]] constexpr quantity operator+() const
requires requires(rep v) { { +v } -> std::same_as<rep>; }
{
return quantity(quantity_values<Rep>::zero());
return *this;
}
[[nodiscard]] static constexpr quantity one() noexcept
requires requires { quantity_values<Rep>::one(); }
{
return quantity(quantity_values<Rep>::one());
}
[[nodiscard]] static constexpr quantity min() noexcept
requires requires { quantity_values<Rep>::min(); }
{
return quantity(quantity_values<Rep>::min());
}
[[nodiscard]] static constexpr quantity max() noexcept
requires requires { quantity_values<Rep>::max(); }
{
return quantity(quantity_values<Rep>::max());
}
[[nodiscard]] constexpr quantity operator+() const { return *this; }
[[nodiscard]] constexpr quantity operator-() const
[[nodiscard]] constexpr Quantity auto operator-() const
requires std::regular_invocable<std::negate<>, rep>
{
return quantity(-count());
using ret = quantity<D, U, decltype(-count())>;
return ret(-count());
}
constexpr quantity& operator++()
@ -143,51 +182,45 @@ public:
return quantity(value_--);
}
template<typename Rep2>
requires detail::safe_convertible<Rep2, rep>
constexpr quantity& operator+=(const quantity<D, U, Rep2>& q)
constexpr quantity& operator+=(const quantity& q)
requires requires(rep a, rep b) { { a += b } -> std::same_as<rep&>; }
{
value_ += q.count();
return *this;
}
template<typename Rep2>
requires detail::safe_convertible<Rep2, rep>
constexpr quantity& operator-=(const quantity<D, U, Rep2>& q)
constexpr quantity& operator-=(const quantity& q)
requires requires(rep a, rep b) { { a -= b } -> std::same_as<rep&>; }
{
value_ -= q.count();
return *this;
}
template<typename Rep2>
requires detail::safe_convertible<Rep2, rep>
constexpr quantity& operator*=(const Rep2& rhs)
constexpr quantity& operator*=(const rep& rhs)
requires requires(rep a, rep b) { { a *= b } -> std::same_as<rep&>; }
{
value_ *= rhs;
return *this;
}
template<typename Rep2>
requires detail::safe_convertible<Rep2, rep>
constexpr quantity& operator/=(const Rep2& rhs)
constexpr quantity& operator/=(const rep& rhs)
requires requires(rep a, rep b) { { a /= b } -> std::same_as<rep&>; }
{
value_ /= rhs;
return *this;
}
template<ScalableNumber Value>
requires (!treat_as_floating_point<rep>) &&
(!treat_as_floating_point<Value>)
constexpr quantity& operator%=(const Value& rhs)
requires requires(rep v1, Value v2) { { v1 %= v2 } -> std::same_as<rep&>; }
constexpr quantity& operator%=(const rep& rhs)
requires (!floating_point_<rep>) &&
requires(rep a, rep b) { { a %= b } -> std::same_as<rep&>; }
{
value_ %= rhs;
return *this;
}
constexpr quantity& operator%=(const quantity& q)
requires (!treat_as_floating_point<rep>) &&
requires(rep v1, rep v2) { { v1 %= v2 } -> std::same_as<rep&>; }
requires (!floating_point_<rep>) &&
requires(rep a, rep b) { { a %= b } -> std::same_as<rep&>; }
{
value_ %= q.count();
return *this;
@ -195,143 +228,88 @@ public:
// Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only
[[nodiscard]] friend constexpr quantity operator+(const quantity& lhs, const quantity& rhs)
requires std::regular_invocable<std::plus<>, Rep, Rep>
requires invoke_result_convertible_to_<rep, std::plus<>, rep, rep>
{
return quantity(lhs.count() + rhs.count());
}
template<typename D2, typename U2, typename Rep2>
requires std::regular_invocable<std::plus<>, Rep, Rep2> && equivalent<D, D2>
[[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
{
using common_rep = decltype(lhs.count() + rhs.count());
using ret = common_quantity<quantity, quantity<D2, U2, Rep2>, common_rep>;
return ret(ret(lhs).count() + ret(rhs).count());
}
[[nodiscard]] friend constexpr quantity operator-(const quantity& lhs, const quantity& rhs)
requires std::regular_invocable<std::minus<>, Rep, Rep>
requires invoke_result_convertible_to_<rep, std::minus<>, rep, rep>
{
return quantity(lhs.count() - rhs.count());
}
template<typename D2, typename U2, typename Rep2>
requires std::regular_invocable<std::minus<>, Rep, Rep2> && equivalent<D, D2>
[[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
{
using common_rep = decltype(lhs.count() - rhs.count());
using ret = common_quantity<quantity, quantity<D2, U2, Rep2>, common_rep>;
return ret(ret(lhs).count() - ret(rhs).count());
}
template<ScalableNumber Value>
requires std::regular_invocable<std::multiplies<>, Rep, Value>
template<typename Value>
requires (!Quantity<Value>) &&
invoke_result_convertible_to_<rep, std::multiplies<>, rep, Value>
[[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v)
{
using common_rep = decltype(q.count() * v);
using ret = quantity<D, U, common_rep>;
using ret = quantity<D, U, std::invoke_result_t<std::multiplies<>, rep, Value>>;
return ret(q.count() * v);
}
template<ScalableNumber Value>
requires std::regular_invocable<std::multiplies<>, Value, Rep>
template<typename Value>
requires (!Quantity<Value>) &&
invoke_result_convertible_to_<rep, std::multiplies<>, rep, Value>
[[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q)
{
return q * v;
}
template<typename D2, typename U2, typename Rep2>
requires std::regular_invocable<std::multiplies<>, Rep, Rep2>
[[nodiscard]] friend constexpr Quantity auto operator*(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
{
using dim = dimension_multiply<D, D2>;
using ret_unit = downcast_unit<dim, (U::ratio / dimension_unit<D>::ratio) * (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using common_rep = decltype(lhs.count() * rhs.count());
using ret = quantity<dim, ret_unit, common_rep>;
return ret(lhs.count() * rhs.count());
}
template<ScalableNumber Value>
requires std::regular_invocable<std::divides<>, Value, Rep>
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{
// Expects(q.count() != zero().count());
using dim = dim_invert<D>;
using ret_unit = downcast_unit<dim, ratio(U::ratio.den, U::ratio.num, -U::ratio.exp)>;
using common_rep = decltype(v / q.count());
using ret = quantity<dim, ret_unit, common_rep>;
return ret(v / q.count());
}
template<ScalableNumber Value>
requires std::regular_invocable<std::divides<>, Rep, Value>
template<typename Value>
requires (!Quantity<Value>) &&
invoke_result_convertible_to_<rep, std::divides<>, rep, Value>
[[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v)
{
// Expects(v != zero().count());
using common_rep = decltype(q.count() / v);
using ret = quantity<D, U, common_rep>;
using ret = quantity<D, U, std::invoke_result_t<std::divides<>, rep, Value>>;
return ret(q.count() / v);
}
template<typename D2, typename U2, typename Rep2>
requires std::regular_invocable<std::divides<>, Rep, Rep2>
[[nodiscard]] friend constexpr Quantity auto operator/(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
template<typename Value>
requires (!Quantity<Value>) &&
invoke_result_convertible_to_<rep, std::divides<>, Value, rep>
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{
// Expects(rhs.count() != zero().count());
using common_rep = decltype(lhs.count() / rhs.count());
using dim = dimension_divide<D, D2>;
using ret_unit = downcast_unit<dim, (U::ratio / dimension_unit<D>::ratio) / (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using ret = quantity<dim, ret_unit, common_rep>;
return ret(lhs.count() / rhs.count());
// Expects(q.count() != zero().count());
using dim = dim_invert<D>;
using ret_unit = downcast_unit<dim, inverse(U::ratio)>;
using ret = quantity<dim, ret_unit, std::invoke_result_t<std::divides<>, Value, rep>>;
return ret(v / q.count());
}
template<ScalableNumber Value>
requires (!treat_as_floating_point<Rep>) &&
(!treat_as_floating_point<Value>) &&
std::regular_invocable<std::modulus<>, Rep, Value>
template<typename Value>
requires (!Quantity<Value>) && (!floating_point_<rep>) && (!floating_point_<Value>) &&
invoke_result_convertible_to_<rep, std::modulus<>, rep, Value>
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v)
{
using common_rep = decltype(q.count() % v);
using ret = quantity<D, U, common_rep>;
using ret = quantity<D, U, std::invoke_result_t<std::modulus<>, rep, Value>>;
return ret(q.count() % v);
}
template<typename U2, typename Rep2>
requires (!treat_as_floating_point<Rep>) &&
(!treat_as_floating_point<Rep2>) &&
std::regular_invocable<std::modulus<>, Rep, Rep2>
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity<D, U2, Rep2>& rhs)
[[nodiscard]] friend constexpr quantity operator%(const quantity& lhs, const quantity& rhs)
requires (!floating_point_<rep>) &&
invoke_result_convertible_to_<rep, std::modulus<>, rep, rep>
{
using common_rep = decltype(lhs.count() % rhs.count());
using ret = common_quantity<quantity, quantity<D, U2, Rep2>, common_rep>;
return ret(ret(lhs).count() % ret(rhs).count());
return quantity(lhs.count() % rhs.count());
}
template<typename D2, typename U2, typename Rep2>
requires equivalent<D, D2> &&
std::three_way_comparable_with<Rep, Rep2>
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs)
requires std::three_way_comparable<rep>
#if COMP_GCC == 10 && COMP_GCC_MINOR >= 2
= default;
#else
{
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
return cq(lhs).count() <=> cq(rhs).count();
return lhs.count() <=> rhs.count();
}
#endif
template<typename D2, typename U2, typename Rep2>
requires equivalent<D, D2> &&
std::equality_comparable_with<Rep, Rep2>
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
{
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
return cq(lhs).count() == cq(rhs).count();
}
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) = default;
template<class CharT, class Traits>
friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity& q)
requires requires { os << q.count(); }
{
if(os.width()) {
// std::setw() applies to the whole quantity output so it has to be first put into std::string
@ -345,9 +323,82 @@ public:
}
};
template<ScalableNumber V>
// CTAD
template<QuantityValue V>
/* implicit */ quantity(V) -> quantity<dim_one, one, V>;
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
{
using ret = common_quantity_for<std::plus<>, Q1, Q2>;
return ret(ret(lhs).count() + ret(rhs).count());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
{
using ret = common_quantity_for<std::minus<>, Q1, Q2>;
return ret(ret(lhs).count() - ret(rhs).count());
}
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires quantity_value_for_<std::multiplies<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator*(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
{
using dim = dimension_multiply<D1, D2>;
using unit = downcast_unit<dim, (U1::ratio / dimension_unit<D1>::ratio) * (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using ret = quantity<dim, unit, std::invoke_result_t<std::multiplies<>, Rep1, Rep2>>;
return ret(lhs.count() * rhs.count());
}
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires quantity_value_for_<std::divides<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
{
// Expects(rhs.count() != zero().count());
using dim = dimension_divide<D1, D2>;
using unit = downcast_unit<dim, (U1::ratio / dimension_unit<D1>::ratio) / (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using ret = quantity<dim, unit, std::invoke_result_t<std::divides<>, Rep1, Rep2>>;
return ret(lhs.count() / rhs.count());
}
template<typename D1, typename U1, typename Rep1, typename U2, typename Rep2>
requires (!floating_point_<Rep1>) && (!floating_point_<Rep2>) &&
quantity_value_for_<std::modulus<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator%(const quantity<D1, U1, Rep1>& lhs, const quantity<dim_one, U2, Rep2>& rhs)
{
using unit = downcast_unit<D1, U1::ratio * U2::ratio>;
using ret = quantity<D1, unit, std::invoke_result_t<std::modulus<>, Rep1, Rep2>>;
return ret(lhs.count() % rhs.count());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires (!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) &&
quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs)
{
using ret = common_quantity_for<std::modulus<>, Q1, Q2>;
return ret(ret(lhs).count() % ret(rhs).count());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
{
using cq = common_quantity<Q1, Q2>;
return cq(lhs).count() <=> cq(rhs).count();
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
{
using cq = common_quantity<Q1, Q2>;
return cq(lhs).count() == cq(rhs).count();
}
namespace detail {
template<typename D, typename U, typename Rep>

View File

@ -36,10 +36,10 @@
namespace units {
template<Dimension D, UnitOf<D> U, ScalableNumber Rep>
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity;
template<Dimension D, UnitOf<D> U, ScalableNumber Rep>
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity_point;
namespace detail {
@ -55,232 +55,6 @@ constexpr auto quantity_ratio(const quantity<D, U, Rep>&)
}
}
} // namespace detail
// quantity_cast
namespace detail {
template<typename To, ratio CRatio, typename CRep, bool NumIsOne, bool DenIsOne, bool ExpIsZero>
struct quantity_cast_impl;
template<typename To, ratio CRatio, typename CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, true, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(q.count()));
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, true, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(detail::ipow10(CRatio.exp))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(detail::ipow10(-CRatio.exp))));
}
}
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, true> {
template<typename Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
(static_cast<CRep>(CRatio.num) /
static_cast<CRep>(CRatio.den))));
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, false> {
template<typename Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
(static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp)) *
(static_cast<CRep>(CRatio.num) /
static_cast<CRep>(CRatio.den)))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
(static_cast<CRep>(CRatio.num) *
static_cast<CRep>(detail::ipow10(CRatio.exp)) /
static_cast<CRep>(CRatio.den))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
(static_cast<CRep>(CRatio.num) /
(static_cast<CRep>(CRatio.den) *
static_cast<CRep>(detail::ipow10(-CRatio.exp))))));
}
}
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(CRatio.den)));
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp)) * (CRep{1} / static_cast<CRep>(CRatio.den)))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(detail::ipow10(CRatio.exp)) / static_cast<CRep>(CRatio.den))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / (static_cast<CRep>(detail::ipow10(-CRatio.exp)) * static_cast<CRep>(CRatio.den))));
}
}
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num)));
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(CRatio.num) * static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp)))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(CRatio.num) * static_cast<CRep>(detail::ipow10(CRatio.exp)))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(CRatio.num) / static_cast<CRep>(detail::ipow10(-CRatio.exp)))));
}
}
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, true, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * detail::fpow10<CRep>(CRatio.exp)));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * detail::ipow10(CRatio.exp)));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count() / detail::ipow10(-CRatio.exp)));
}
}
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, true> {
template<typename Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num / CRatio.den)));
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, false> {
template<typename Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * (detail::fpow10<CRep>(CRatio.exp) * (CRatio.num / CRatio.den))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num * detail::ipow10(CRatio.exp) / CRatio.den)));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count()) * (CRatio.num / (CRatio.den * detail::ipow10(-CRatio.exp))));
}
}
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(q.count() / CRatio.den));
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * (detail::fpow10<CRep>(CRatio.exp) / CRatio.den)));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * (detail::ipow10(CRatio.exp) / CRatio.den)));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count() / (detail::ipow10(-CRatio.exp) * CRatio.den)));
}
}
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num));
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num * detail::fpow10<CRep>(CRatio.exp))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num * detail::ipow10(CRatio.exp))));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num / detail::ipow10(-CRatio.exp))));
}
}
}
};
template<typename Q1, typename Q2>
constexpr ratio cast_ratio(const Q1& from, const Q2& to)
{
@ -294,6 +68,26 @@ constexpr ratio cast_ratio(const Q1& from, const Q2& to)
}
}
template<typename From, typename To>
struct cast_traits;
template<typename From, typename To>
requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
struct cast_traits<From, To> {
using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
using rep_type = ratio_type;
};
template<typename From, typename To>
requires (!common_type_with_<std::common_type_t<From, To>, std::intmax_t>) &&
scalable_number_<std::common_type_t<From, To>, std::intmax_t> &&
requires { typename std::common_type_t<From, To>::value_type; } &&
common_type_with_<typename std::common_type_t<From, To>::value_type, std::intmax_t>
struct cast_traits<From, To> {
using ratio_type = std::common_type_t<typename std::common_type_t<From, To>::value_type, std::intmax_t>;
using rep_type = std::common_type_t<From, To>;
};
} // namespace detail
/**
@ -308,16 +102,33 @@ constexpr ratio cast_ratio(const Q1& from, const Q2& to)
*
* @tparam To a target quantity type to cast to
*/
template<Quantity To, typename D, typename U, typename Rep>
template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> Rep>
requires QuantityOf<To, D>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
using c_ratio = std::integral_constant<ratio, detail::cast_ratio(quantity<D, U, Rep>(), To())>;
using c_rep = std::common_type_t<typename To::rep, Rep>;
using ret_unit = downcast_unit<typename To::dimension, To::unit::ratio>;
using ret = quantity<typename To::dimension, ret_unit, typename To::rep>;
using cast = detail::quantity_cast_impl<ret, c_ratio::value, c_rep, c_ratio::value.num == 1, c_ratio::value.den == 1, c_ratio::value.exp == 0>;
return cast::cast(q);
using traits = detail::cast_traits<Rep, typename To::rep>;
using ratio_type = TYPENAME traits::ratio_type;
using rep_type = TYPENAME traits::rep_type;
constexpr auto c_ratio = detail::cast_ratio(quantity<D, U, Rep>(), To());
if constexpr (treat_as_floating_point<rep_type>) {
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
(static_cast<ratio_type>(c_ratio.num) * detail::fpow10<ratio_type>(c_ratio.exp) / static_cast<ratio_type>(c_ratio.den))));
}
else {
if constexpr (c_ratio.exp > 0) {
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
(static_cast<ratio_type>(c_ratio.num) * static_cast<ratio_type>(detail::ipow10(c_ratio.exp))) /
static_cast<ratio_type>(c_ratio.den)));
}
else {
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
static_cast<ratio_type>(c_ratio.num) /
(static_cast<ratio_type>(c_ratio.den) * static_cast<ratio_type>(detail::ipow10(-c_ratio.exp)))));
}
}
}
/**
@ -393,7 +204,7 @@ template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
*
* @tparam ToRep a representation type to use for a target quantity
*/
template<ScalableNumber ToRep, typename D, typename U, typename Rep>
template<QuantityValue ToRep, typename D, typename U, scalable_with_<ToRep> Rep>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{
return quantity_cast<quantity<D, U, ToRep>>(q);

View File

@ -37,7 +37,7 @@ namespace units {
* @tparam U a measurement unit of the quantity point
* @tparam Rep a type to be used to represent values of a quantity point
*/
template<Dimension D, UnitOf<D> U, ScalableNumber Rep = double>
template<Dimension D, UnitOf<D> U, QuantityValue Rep = double>
class quantity_point {
public:
using quantity_type = quantity<D, U, Rep>;

View File

@ -24,7 +24,9 @@ cmake_minimum_required(VERSION 3.12)
add_library(unit_tests_static
cgs_test.cpp
custom_rep_min_req_test.cpp
concepts_test.cpp
custom_rep_test_min_expl.cpp
custom_rep_test_min_impl.cpp
custom_unit_test.cpp
data_test.cpp
dimension_op_test.cpp

View File

@ -0,0 +1,130 @@
// 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 "units/physical/si/cgs/derived/speed.h"
#include "units/physical/si/derived/speed.h"
#include "units/physical/si/fps/derived/speed.h"
#include "units/quantity_point.h"
#include <chrono>
#include <complex>
#include <mutex>
#include <optional>
#include <string>
namespace {
using namespace units;
using namespace units::physical;
// Prefix family
static_assert(PrefixFamily<si::prefix>);
static_assert(!PrefixFamily<si::kilo>);
// Prefix
static_assert(Prefix<si::kilo>);
static_assert(!Prefix<si::prefix>);
static_assert(!Prefix<std::kilo>);
// UnitRatio
static_assert(UnitRatio<ratio(1000)>);
static_assert(!UnitRatio<ratio(0)>);
// static_assert(UnitRatio<ratio(1000, 0)>); // static_assert in ratio
static_assert(UnitRatio<ratio(-1000, -1)>);
static_assert(!UnitRatio<ratio(-1000, 1)>);
static_assert(!UnitRatio<ratio(1, -1000)>);
// BaseDimension
static_assert(BaseDimension<si::dim_length>);
static_assert(!BaseDimension<si::dim_speed>);
static_assert(!BaseDimension<int>);
// DerivedDimension
static_assert(DerivedDimension<si::dim_speed>);
static_assert(!DerivedDimension<si::dim_length>);
static_assert(!DerivedDimension<int>);
// Dimension
static_assert(Dimension<si::dim_length>);
static_assert(Dimension<si::dim_speed>);
static_assert(!Dimension<si::metre>);
static_assert(!Dimension<int>);
static_assert(!Dimension<std::chrono::seconds>);
// Unit
static_assert(Unit<si::metre>);
static_assert(Unit<si::kilometre>);
static_assert(Unit<si::fps::mile>);
static_assert(Unit<si::metre_per_second>);
static_assert(!Unit<si::dim_length>);
static_assert(!Unit<int>);
static_assert(!Unit<std::chrono::seconds>);
// UnitOf
static_assert(UnitOf<si::metre, si::dim_length>);
static_assert(UnitOf<si::kilometre, si::dim_length>);
static_assert(UnitOf<si::fps::mile, si::dim_length>);
static_assert(!UnitOf<si::second, si::dim_length>);
// QuantityValue
static_assert(QuantityValue<int>);
static_assert(QuantityValue<std::complex<double>>);
static_assert(!QuantityValue<si::length<si::metre>>);
static_assert(!QuantityValue<std::optional<si::length<si::metre>>>);
static_assert(!QuantityValue<std::mutex>);
static_assert(!QuantityValue<std::string>);
// Quantity
static_assert(Quantity<si::length<si::metre>>);
static_assert(!Quantity<std::chrono::seconds>);
static_assert(!Quantity<quantity_point<si::dim_length, si::metre>>);
// QuantityPoint
static_assert(QuantityPoint<quantity_point<si::dim_length, si::metre>>);
static_assert(!QuantityPoint<si::length<si::metre>>);
static_assert(!QuantityPoint<std::chrono::seconds>);
// WrappedQuantity
static_assert(wrapped_quantity_<std::optional<si::length<si::metre>>>);
static_assert(!wrapped_quantity_<std::pair<si::length<si::metre>, si::length<si::metre>>>);
// QuantityOf
static_assert(QuantityOf<si::length<si::metre>, si::dim_length>);
// TODO it seems `QuantityOf` is a bad name if `si::cgs::length<si::cgs::centimetre>` matches `si::fps::dim_length`
static_assert(QuantityOf<si::cgs::length<si::cgs::centimetre>, si::dim_length>);
static_assert(QuantityOf<si::cgs::length<si::metre>, si::dim_length>);
static_assert(QuantityOf<si::cgs::length<si::cgs::centimetre>, si::fps::dim_length>);
static_assert(!QuantityOf<si::cgs::length<si::cgs::centimetre>, si::dim_time>);
} // namespace

View File

@ -1,251 +0,0 @@
// 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 "units/math.h"
#include "units/physical/si/si.h"
#include <chrono>
#include <type_traits>
#include <utility>
using namespace units;
namespace {
template<typename T>
struct equality_ops {
[[nodiscard]] friend constexpr bool operator==(T lhs, T rhs) { return lhs.value_ == rhs.value_; }
[[nodiscard]] friend constexpr bool operator!=(T lhs, T rhs) { return !(lhs == rhs); }
};
template<typename T>
struct scaling_ops {
[[nodiscard]] friend constexpr T operator*(T lhs, T rhs) {
return T(lhs.value_ * rhs.value_);
}
[[nodiscard]] friend constexpr T operator/(T lhs, T rhs) {
return T(lhs.value_ / rhs.value_);
}
};
template<typename T>
struct scalar_ops : equality_ops<T>, scaling_ops<T> {};
template<typename T>
struct impl_constructible : scalar_ops<impl_constructible<T>> {
T value_{};
impl_constructible() = default;
constexpr impl_constructible(T v) : value_(std::move(v)) {}
// no conversion to fundamental arithmetic types
};
template<typename T>
using impl = impl_constructible<T>;
template<typename T>
struct expl_constructible : scalar_ops<expl_constructible<T>> {
T value_{};
expl_constructible() = default;
constexpr explicit expl_constructible(T v) : value_(std::move(v)) {}
// no conversion to fundamental arithmetic types
};
template<typename T>
using expl = expl_constructible<T>;
template<typename T>
struct impl_constructible_impl_convertible : scalar_ops<impl_constructible_impl_convertible<T>> /*, int_scaling_ops<impl_constructible_impl_convertible<T>> */ {
T value_{};
impl_constructible_impl_convertible() = default;
constexpr impl_constructible_impl_convertible(T v) : value_(std::move(v)) {}
constexpr operator const T&() const& { return value_; }
};
template<typename T>
using impl_impl = impl_constructible_impl_convertible<T>;
static_assert(std::convertible_to<float, impl_impl<float>>);
static_assert(std::convertible_to<impl_impl<float>, float>);
static_assert(units::ScalableNumber<impl_impl<float>>);
template<typename T>
struct expl_constructible_impl_convertible : scalar_ops<expl_constructible_impl_convertible<T>> {
T value_{};
expl_constructible_impl_convertible() = default;
constexpr explicit expl_constructible_impl_convertible(T v) : value_(std::move(v)) {}
constexpr operator const T&() const& { return value_; }
};
template<typename T>
using expl_impl = expl_constructible_impl_convertible<T>;
static_assert(!std::convertible_to<float, expl_impl<float>>);
static_assert(std::convertible_to<expl_impl<float>, float>);
static_assert(units::ScalableNumber<expl_impl<float>>);
template<typename T>
struct impl_constructible_expl_convertible : scalar_ops<impl_constructible_expl_convertible<T>> {
T value_{};
impl_constructible_expl_convertible() = default;
constexpr impl_constructible_expl_convertible(T v) : value_(std::move(v)) {}
constexpr explicit operator const T&() const& { return value_; }
};
template<typename T>
using impl_expl = impl_constructible_expl_convertible<T>;
static_assert(std::convertible_to<float, impl_expl<float>>);
static_assert(!std::convertible_to<impl_expl<float>, float>);
static_assert(units::ScalableNumber<impl_expl<float>>);
template<typename T>
struct expl_constructible_expl_convertible : scalar_ops<expl_constructible_expl_convertible<T>> {
T value_{};
expl_constructible_expl_convertible() = default;
constexpr explicit expl_constructible_expl_convertible(T v) : value_(std::move(v)) {}
constexpr explicit operator const T&() const& { return value_; }
};
template<typename T>
using expl_expl = expl_constructible_expl_convertible<T>;
static_assert(!std::convertible_to<float, expl_expl<float>>);
static_assert(!std::convertible_to<expl_expl<float>, float>);
static_assert(units::ScalableNumber<expl_expl<float>>);
} // namespace
namespace units {
template<typename T>
inline constexpr bool treat_as_floating_point<impl<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<expl_constructible<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<impl_impl<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<expl_impl<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<impl_expl<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<expl_expl<T>> = std::is_floating_point_v<T>;
template<typename T>
struct quantity_values<impl<T>> {
static constexpr impl<T> zero() { return 0; }
static constexpr impl<T> max() { return std::numeric_limits<T>::max(); }
static constexpr impl<T> min() { return std::numeric_limits<T>::lowest(); }
};
} // namespace units
namespace {
using namespace units::physical::si;
// constructors
// Quantity from ScalableNumber
// int <- int
static_assert(length<metre, int>(expl_impl<int>(1)).count() == 1);
static_assert(!std::is_constructible_v<length<metre, int>, impl_expl<int>>);
static_assert(length<metre, int>(int(impl_expl<int>(1))).count() == 1);
static_assert(!std::is_constructible_v<length<metre, expl_impl<int>>, int>);
static_assert(length<metre, expl_impl<int>>(expl_impl<int>(1)).count() == expl_impl<int>{1});
static_assert(length<metre, impl_expl<int>>(1).count() == impl_expl<int>{1});
// double <- double
static_assert(length<metre, double>(expl_impl<double>(1.0)).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, double>, impl_expl<double>>);
static_assert(length<metre, double>(double(impl_expl<double>(1.0))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, double>);
static_assert(length<metre, expl_impl<double>>(expl_impl<double>(1.0)).count() == expl_impl<double>{1.0});
static_assert(length<metre, impl_expl<double>>(1.0).count() == impl_expl<double>{1.0});
// double <- int
static_assert(length<metre, double>(expl_impl<int>(1)).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, double>, impl_expl<int>>);
static_assert(length<metre, double>(int(impl_expl<int>(1))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, int>);
static_assert(length<metre, expl_impl<double>>(expl_impl<double>(1)).count() == expl_impl<double>{1});
static_assert(length<metre, impl_expl<double>>(1).count() == impl_expl<double>{1.0});
// int <- double
static_assert(!std::is_constructible_v<length<metre, int>, expl_impl<double>>);
static_assert(!std::is_constructible_v<length<metre, impl_expl<int>>, double>);
// Quantity from other Quantity with different Rep
// int <- int
static_assert(length<metre, int>(length<metre, expl_impl<int>>(expl_impl<int>(1))).count() == 1);
static_assert(!std::is_constructible_v<length<metre, int>, length<metre, impl_expl<int>>>);
static_assert(length<metre, int>(quantity_cast<int>(length<metre, impl_expl<int>>(1))).count() == 1);
static_assert(!std::is_constructible_v<length<metre, expl_impl<int>>, length<metre, int>>);
static_assert(length<metre, expl_impl<int>>(quantity_cast<expl_impl<int>>(length<metre, int>(1))).count() == expl_impl<int>{1});
static_assert(length<metre, impl_expl<int>>(length<metre, int>(1)).count() == impl_expl<int>{1});
// double <- double
static_assert(length<metre, double>(length<metre, expl_impl<double>>(expl_impl<double>(1.0))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, double>, length<metre, impl_expl<double>>>);
static_assert(length<metre, double>(quantity_cast<double>(length<metre, impl_expl<double>>(1.0))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, length<metre, double>>);
static_assert(length<metre, expl_impl<double>>(quantity_cast<expl_impl<double>>(length<metre>(1.0))).count() == expl_impl<double>{1.0});
static_assert(length<metre, impl_expl<double>>(length<metre>(1.0)).count() == impl_expl<double>{1.0});
// double <- int
static_assert(length<metre, double>(length<metre, expl_impl<int>>(expl_impl<int>(1))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, double>, length<metre, impl_expl<int>>>);
static_assert(length<metre, double>(quantity_cast<int>(length<metre, impl_expl<int>>(1))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, length<metre, int>>);
static_assert(length<metre, expl_impl<double>>(quantity_cast<expl_impl<double>>(length<metre, int>(1))).count() == expl_impl<double>{1});
static_assert(length<metre, impl_expl<double>>(length<metre, int>(1)).count() == impl_expl<double>{1.0});
// int <- double
static_assert(!std::is_constructible_v<length<metre, int>, length<metre, expl_impl<double>>>);
static_assert(!std::is_constructible_v<length<metre, impl_expl<int>>, length<metre, double>>);
// unit conversions
static_assert(length<metre, impl<int>>(length<kilometre, impl<int>>(1)).count() == impl<int>(1000));
static_assert(length<metre, expl<int>>(length<kilometre, expl<int>>(expl<int>(1))).count() == expl<int>(1000));
static_assert(length<metre, impl_impl<int>>(length<kilometre, impl_impl<int>>(1)).count() == impl_impl<int>(1000));
static_assert(length<metre, impl_expl<int>>(length<kilometre, impl_expl<int>>(1)).count() == impl_expl<int>(1000));
static_assert(length<metre, expl_impl<int>>(length<kilometre, expl_impl<int>>(expl_impl<int>(1))).count() == expl_impl<int>(1000));
static_assert(length<metre, expl_expl<int>>(length<kilometre, expl_expl<int>>(expl_expl<int>(1))).count() == expl_expl<int>(1000));
static_assert(!std::is_constructible_v<length<kilometre, impl<int>>, length<metre, impl<int>>>);
static_assert(length<kilometre, impl<int>>(quantity_cast<kilometre>(length<metre, impl<int>>(2000))).count() == impl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, expl<int>>, length<metre, expl<int>>>);
static_assert(length<kilometre, expl<int>>(quantity_cast<kilometre>(length<metre, expl<int>>(expl<int>(2000)))).count() == expl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, impl_impl<int>>, length<metre, impl_impl<int>>>);
static_assert(length<kilometre, impl_impl<int>>(quantity_cast<kilometre>(length<metre, impl_impl<int>>(2000))).count() == impl_impl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, impl_expl<int>>, length<metre, impl_expl<int>>>);
static_assert(length<kilometre, impl_expl<int>>(quantity_cast<kilometre>(length<metre, impl_expl<int>>(2000))).count() == impl_expl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, expl_impl<int>>, length<metre, expl_impl<int>>>);
static_assert(length<kilometre, expl_impl<int>>(quantity_cast<kilometre>(length<metre, expl_impl<int>>(expl_impl<int>(2000)))).count() == expl_impl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, expl_expl<int>>, length<metre, expl_expl<int>>>);
static_assert(length<kilometre, expl_expl<int>>(quantity_cast<kilometre>(length<metre, expl_expl<int>>(expl_expl<int>(2000)))).count() == expl_expl<int>(2));
} // namespace

View File

@ -0,0 +1,199 @@
// 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 "units/physical/si/base/length.h"
namespace {
/**
* @brief Representation type meeting minimum requirements
*
* This type with a default Mode = 0 provides the minimum set of requirements to
* satisfy @c QuantityValue concept which is used for quantity's representation type.
*
* In case of Mode != 0 only one of mandatory operation is removed which should
* result in @c QuantityValue concept not being satisfied.
*
* @tparam Mode a flag to disable specific type's operations
*/
template<int Mode = 0>
class min_expl {
std::intmax_t value_;
public:
// default construction
min_expl() requires (Mode != 1) = default;
// construction from std::int64_t
explicit constexpr min_expl(std::intmax_t v) noexcept requires (Mode != 2) : value_(v) {}
// copy construction
min_expl(const min_expl&) requires (Mode != 3) = default;
// move construction
min_expl(min_expl&&) requires (Mode != 4) = default;
min_expl(min_expl&&) requires (Mode == 4) = delete;
// copy assignment
min_expl& operator=(const min_expl&) requires (Mode != 5) = default;
// move assignment
min_expl& operator=(min_expl&&) requires (Mode != 6) = default;
min_expl& operator=(min_expl&&) requires (Mode == 6) = delete;
// equality
bool operator==(const min_expl&) const requires (Mode != 7) = default;
// scalability - multiplication
friend constexpr min_expl operator*(const min_expl& lhs, const min_expl& rhs) requires (Mode != 8)
{ return min_expl(lhs.value_ * rhs.value_); }
// scalability - division
friend constexpr min_expl operator/(const min_expl& lhs, const min_expl& rhs) requires (Mode != 9)
{ return min_expl(lhs.value_ / rhs.value_); }
};
}
template<int Mode>
struct std::common_type<std::intmax_t, min_expl<Mode>> : std::type_identity<min_expl<Mode>> {};
template<int Mode>
struct std::common_type<min_expl<Mode>, std::intmax_t> : std::type_identity<min_expl<Mode>> {};
namespace {
using namespace units;
using namespace units::physical::si;
// quantity explicitly constructible (not convertible) from the representation type
static_assert(std::constructible_from<length<metre, min_expl<>>, min_expl<>>);
static_assert(!std::convertible_to<min_expl<>, length<metre, min_expl<>>>);
// not constructible from an underlying type
static_assert(!std::constructible_from<length<metre, min_expl<>>, int>);
static_assert(!std::convertible_to<int, length<metre, min_expl<>>>);
// dimensionless quantity implicitly convertible from the representation type
static_assert(std::constructible_from<dimensionless<one, min_expl<>>, min_expl<>>);
static_assert(std::convertible_to<min_expl<>, dimensionless<one, min_expl<>>>);
// but not from an underlying type
static_assert(!std::constructible_from<dimensionless<one, min_expl<>>, int>);
static_assert(!std::convertible_to<int, dimensionless<one, min_expl<>>>);
// or for ratio != 1
static_assert(std::constructible_from<dimensionless<percent, min_expl<>>, min_expl<>>);
static_assert(!std::convertible_to<min_expl<>, dimensionless<percent, min_expl<>>>);
// quantity convertible from itself
static_assert(std::constructible_from<length<metre, min_expl<>>, length<metre, min_expl<>>>);
static_assert(std::convertible_to<length<metre, min_expl<>>, length<metre, min_expl<>>>);
// not convertible from an underlying type
static_assert(!std::constructible_from<length<metre, min_expl<>>, length<metre, int>>);
static_assert(!std::convertible_to<length<metre, int>, length<metre, min_expl<>>>);
// quantity convertible from another non truncating unit
static_assert(std::constructible_from<length<metre, min_expl<>>, length<kilometre, min_expl<>>>);
static_assert(std::convertible_to<length<kilometre, min_expl<>>, length<metre, min_expl<>>>);
// quantity not convertible from another truncating unit
static_assert(!std::constructible_from<length<kilometre, min_expl<>>, length<metre, min_expl<>>>);
static_assert(!std::convertible_to<length<metre, min_expl<>>, length<kilometre, min_expl<>>>);
// rep type with explicit constructor - implicit construction of rep not allowed
static_assert(!std::constructible_from<length<metre, min_expl<>>, int>);
static_assert(!std::convertible_to<int, length<metre, min_expl<>>>);
static_assert(!std::constructible_from<length<metre, min_expl<>>, length<metre, int>>);
static_assert(!std::convertible_to<length<metre, int>, length<metre, min_expl<>>>);
static_assert(!std::constructible_from<length<metre, int>, min_expl<>>);
static_assert(!std::convertible_to<min_expl<>, length<metre, int>>);
static_assert(!std::constructible_from<length<metre, int>, length<metre, min_expl<>>>);
static_assert(!std::convertible_to<length<metre, min_expl<>>, length<metre, int>>);
// all operations needed to satisfy concept
static_assert(QuantityValue<min_expl<>>);
static_assert(!QuantityValue<min_expl<1>>);
static_assert(!QuantityValue<min_expl<2>>);
static_assert(!QuantityValue<min_expl<3>>);
static_assert(!QuantityValue<min_expl<4>>);
static_assert(!QuantityValue<min_expl<5>>);
static_assert(!QuantityValue<min_expl<6>>);
static_assert(!QuantityValue<min_expl<7>>);
static_assert(!QuantityValue<min_expl<8>>);
static_assert(!QuantityValue<min_expl<9>>);
// quantity's operators should mirror the representation type capabilities
template<typename Rep>
concept invalid_member_operations = requires(length<metre, Rep> lhs) {
requires !requires { +lhs; };
requires !requires { -lhs; };
requires !requires { ++lhs; };
requires !requires { lhs++; };
requires !requires { --lhs; };
requires !requires { lhs--; };
requires !requires(length<metre, Rep> rhs) { lhs += rhs; };
requires !requires(length<metre, Rep> rhs) { lhs -= rhs; };
requires !requires(Rep rhs) { lhs *= rhs; };
requires !requires(Rep rhs) { lhs /= rhs; };
requires !requires(Rep rhs) { lhs %= rhs; };
requires !requires(length<metre, Rep> rhs) { lhs %= rhs; };
requires !requires(length<metre, Rep> rhs) { lhs + rhs; };
requires !requires(length<metre, Rep> rhs) { lhs - rhs; };
requires !requires(Rep rhs) { lhs % rhs; };
requires !requires(length<metre, Rep> rhs) { lhs % rhs; };
requires !requires(length<metre, Rep> rhs) { lhs < rhs; };
requires !requires(length<metre, Rep> rhs) { lhs > rhs; };
requires !requires(length<metre, Rep> rhs) { lhs <= rhs; };
requires !requires(length<metre, Rep> rhs) { lhs >= rhs; };
requires !requires(length<metre, int> rhs) { lhs + rhs; };
requires !requires(length<metre, int> rhs) { lhs - rhs; };
requires !requires(int rhs) { lhs % rhs; };
requires !requires(length<metre, int> rhs) { lhs % rhs; };
requires !requires(length<metre, int> rhs) { lhs == rhs; };
requires !requires(length<metre, int> rhs) { lhs != rhs; };
requires !requires(length<metre, int> rhs) { lhs < rhs; };
requires !requires(length<metre, int> rhs) { lhs > rhs; };
requires !requires(length<metre, int> rhs) { lhs <= rhs; };
requires !requires(length<metre, int> rhs) { lhs >= rhs; };
requires !requires(std::ostream os) { os << lhs; };
};
static_assert(invalid_member_operations<min_expl<>>);
// equality
static_assert(length<kilometre, min_expl<>>(min_expl<>(2)) == length<metre, min_expl<>>(min_expl<>(2000)));
static_assert(length<metre, min_expl<>>(min_expl<>(123)) * min_expl<>(2) == length<metre, min_expl<>>(min_expl<>(246)));
static_assert(length<metre, min_expl<>>(min_expl<>(123)) * quantity{min_expl<>(2)} == length<metre, min_expl<>>(min_expl<>(246)));
static_assert(min_expl<>(2) * length<metre, min_expl<>>(min_expl<>(123)) == length<metre, min_expl<>>(min_expl<>(246)));
static_assert(quantity{min_expl<>(2)} * length<metre, min_expl<>>(min_expl<>(123)) == length<metre, min_expl<>>(min_expl<>(246)));
static_assert(length<metre, min_expl<>>(min_expl<>(246)) / min_expl<>(2) == length<metre, min_expl<>>(min_expl<>(123)));
static_assert(length<metre, min_expl<>>(min_expl<>(246)) / quantity{min_expl<>(2)} == length<metre, min_expl<>>(min_expl<>(123)));
static_assert(length<metre, min_expl<>>(min_expl<>(246)) / length<metre, min_expl<>>(min_expl<>(2)) == quantity{min_expl<>(123)});
static_assert(length<metre, min_expl<>>(min_expl<>(246)) / length<metre, min_expl<>>(min_expl<>(2)) == min_expl<>(123));
} // namespace

View File

@ -0,0 +1,317 @@
// 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 "units/math.h"
#include "units/physical/si/base/length.h"
#include <chrono>
#include <type_traits>
#include <utility>
namespace {
/**
* @brief Implicitly constructible and convertible representation type
*
* A wrapper type that is implicitly convertible from and to the contained type.
*
* @tparam T element type
*/
template<typename T>
class min_impl {
T value_;
public:
using value_type = T;
min_impl() = default;
constexpr min_impl(T v) noexcept : value_(v) {}
template<typename U>
constexpr min_impl(min_impl<U> i) noexcept : value_(static_cast<T>(i.value_)) {}
constexpr operator T() const noexcept { return value_; }
};
}
template<typename T, typename U>
struct std::common_type<min_impl<T>, min_impl<U>> : std::common_type<T, U> {};
template<typename T, typename U>
struct std::common_type<min_impl<T>, U> : std::common_type<T, U> {};
template<typename U, typename T>
struct std::common_type<U, min_impl<T>> : std::common_type<T, U> {};
namespace {
using namespace units;
using namespace units::physical::si;
static_assert(QuantityValue<min_impl<int>>);
static_assert(QuantityValue<min_impl<double>>);
// construction from a value
static_assert(std::constructible_from<length<metre, min_impl<int>>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, min_impl<int>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, length<metre, min_impl<double>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, min_impl<double>>>);
static_assert(!std::constructible_from<length<metre, min_impl<int>>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, length<metre, min_impl<int>>>);
// construction from an underlying type
static_assert(std::constructible_from<length<metre, min_impl<int>>, int>);
static_assert(!std::convertible_to<int, length<metre, min_impl<int>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, double>);
static_assert(!std::convertible_to<double, length<metre, min_impl<double>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, int>);
static_assert(!std::convertible_to<int, length<metre, min_impl<double>>>);
static_assert(!std::constructible_from<length<metre, min_impl<int>>, double>); // narrowing conversion
static_assert(!std::convertible_to<double, length<metre, min_impl<int>>>);
// dimensionless quantity is convertible from a value
static_assert(std::constructible_from<dimensionless<one, min_impl<int>>, min_impl<int>>);
static_assert(std::convertible_to<min_impl<int>, dimensionless<one, min_impl<int>>>);
static_assert(std::constructible_from<dimensionless<one, min_impl<double>>, min_impl<double>>);
static_assert(std::convertible_to<min_impl<double>, dimensionless<one, min_impl<double>>>);
static_assert(std::constructible_from<dimensionless<one, min_impl<double>>, min_impl<int>>);
static_assert(std::convertible_to<min_impl<int>, dimensionless<one, min_impl<double>>>);
static_assert(!std::constructible_from<dimensionless<one, min_impl<int>>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, dimensionless<one, min_impl<int>>>);
// and underlying type
static_assert(std::constructible_from<dimensionless<one, min_impl<int>>, int>);
static_assert(std::convertible_to<int, dimensionless<one, min_impl<int>>>);
static_assert(std::constructible_from<dimensionless<one, min_impl<double>>, double>);
static_assert(std::convertible_to<double, dimensionless<one, min_impl<double>>>);
static_assert(std::constructible_from<dimensionless<one, min_impl<double>>, int>);
static_assert(std::convertible_to<int, dimensionless<one, min_impl<double>>>);
static_assert(!std::constructible_from<dimensionless<one, min_impl<int>>, double>); // narrowing conversion
static_assert(!std::convertible_to<double, dimensionless<one, min_impl<int>>>);
// but only for ratio(1), otherwise not convertible
static_assert(std::constructible_from<dimensionless<percent, min_impl<int>>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, dimensionless<percent, min_impl<int>>>);
static_assert(std::constructible_from<dimensionless<percent, min_impl<double>>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, dimensionless<percent, min_impl<double>>>);
static_assert(std::constructible_from<dimensionless<percent, min_impl<double>>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, dimensionless<percent, min_impl<double>>>);
static_assert(!std::constructible_from<dimensionless<percent, min_impl<int>>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, dimensionless<percent, min_impl<int>>>);
// implicit conversion tests
static_assert(std::constructible_from<length<metre, int>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, int>>);
static_assert(std::constructible_from<length<metre, double>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, length<metre, double>>);
static_assert(std::constructible_from<length<metre, double>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, double>>);
static_assert(!std::constructible_from<length<metre, int>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, length<metre, int>>);
// construction from an underlying type
static_assert(std::constructible_from<length<metre, int>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, int>>);
static_assert(std::constructible_from<length<metre, double>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, length<metre, double>>);
static_assert(std::constructible_from<length<metre, double>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, double>>);
static_assert(!std::constructible_from<length<metre, int>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, length<metre, int>>);
// dimensionless quantity is convertible from a value
static_assert(std::constructible_from<dimensionless<one, int>, min_impl<int>>);
static_assert(std::convertible_to<min_impl<int>, dimensionless<one, int>>);
static_assert(std::constructible_from<dimensionless<one, double>, min_impl<double>>);
static_assert(std::convertible_to<min_impl<double>, dimensionless<one, double>>);
static_assert(std::constructible_from<dimensionless<one, double>, min_impl<int>>);
static_assert(std::convertible_to<min_impl<int>, dimensionless<one, double>>);
static_assert(!std::constructible_from<dimensionless<one, int>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, dimensionless<one, int>>);
// but only for ratio(1), otherwise not convertible
static_assert(std::constructible_from<dimensionless<percent, int>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, dimensionless<percent, int>>);
static_assert(std::constructible_from<dimensionless<percent, double>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, dimensionless<percent, double>>);
static_assert(std::constructible_from<dimensionless<percent, double>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, dimensionless<percent, double>>);
static_assert(!std::constructible_from<dimensionless<percent, int>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, dimensionless<percent, int>>);
// construction from a quantity
// min_impl<T> -> min_impl<T>
static_assert(std::constructible_from<length<metre, min_impl<int>>, length<metre, min_impl<int>>>);
static_assert(std::convertible_to<length<metre, min_impl<int>>, length<metre, min_impl<int>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, length<metre, min_impl<double>>>);
static_assert(std::convertible_to<length<metre, min_impl<double>>, length<metre, min_impl<double>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, length<metre, min_impl<int>>>);
static_assert(std::convertible_to<length<metre, min_impl<int>>, length<metre, min_impl<double>>>);
static_assert(!std::constructible_from<length<metre, min_impl<int>>, length<metre, min_impl<double>>>); // narrowing conversion
static_assert(!std::convertible_to<length<metre, min_impl<double>>, length<metre, min_impl<int>>>);
// T -> min_impl<T>
static_assert(std::constructible_from<length<metre, min_impl<int>>, length<metre, int>>);
static_assert(std::convertible_to<length<metre, int>, length<metre, min_impl<int>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, length<metre, double>>);
static_assert(std::convertible_to<length<metre, double>, length<metre, min_impl<double>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, length<metre, int>>);
static_assert(std::convertible_to<length<metre, int>, length<metre, min_impl<double>>>);
static_assert(!std::constructible_from<length<metre, min_impl<int>>, length<metre, double>>); // narrowing conversion
static_assert(!std::convertible_to<length<metre, double>, length<metre, min_impl<int>>>);
// min_impl<T> -> T
static_assert(std::constructible_from<length<metre, int>, length<metre, min_impl<int>>>);
static_assert(std::convertible_to<length<metre, min_impl<int>>, length<metre, int>>);
static_assert(std::constructible_from<length<metre, double>, length<metre, min_impl<double>>>);
static_assert(std::convertible_to<length<metre, min_impl<double>>, length<metre, double>>);
static_assert(std::constructible_from<length<metre, double>, length<metre, min_impl<int>>>);
static_assert(std::convertible_to<length<metre, min_impl<int>>, length<metre, double>>);
static_assert(!std::constructible_from<length<metre, int>, length<metre, min_impl<double>>>); // narrowing conversion
static_assert(!std::convertible_to<length<metre, min_impl<double>>, length<metre, int>>);
// arithmetic operators
static_assert(length<metre, min_impl<int>>(1) + length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(2));
static_assert(length<metre, min_impl<int>>(1) + length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(2.5));
static_assert(length<metre, int>(1) + length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(2));
static_assert(length<metre, int>(1) + length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(2.5));
static_assert(length<metre, min_impl<int>>(1) + length<metre, int>(1) == length<metre, min_impl<int>>(2));
static_assert(length<metre, min_impl<int>>(1) + length<metre, double>(1.5) == length<metre, min_impl<double>>(2.5));
static_assert(length<metre, min_impl<int>>(1) + length<metre, min_impl<int>>(1) == length<metre, int>(2));
static_assert(length<metre, min_impl<int>>(1) + length<metre, min_impl<double>>(1.5) == length<metre, double>(2.5));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1001.5));
static_assert(length<kilometre, int>(1) + length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<kilometre, int>(1) + length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1001.5));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, int>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, double>(1.5) == length<metre, min_impl<double>>(1001.5));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, min_impl<int>>(1) == length<metre, int>(1001));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, min_impl<double>>(1.5) == length<metre, double>(1001.5));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, min_impl<int>>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1501));
static_assert(length<metre, int>(1) + length<kilometre, min_impl<int>>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<metre, int>(1) + length<kilometre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1501));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, int>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, double>(1.5) == length<metre, min_impl<double>>(1501));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, min_impl<int>>(1) == length<metre, int>(1001));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, min_impl<double>>(1.5) == length<metre, double>(1501));
static_assert(length<metre, min_impl<int>>(2) - length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1));
static_assert(length<metre, min_impl<int>>(2) - length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(0.5));
static_assert(length<metre, int>(2) - length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1));
static_assert(length<metre, int>(2) - length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(0.5));
static_assert(length<metre, min_impl<int>>(2) - length<metre, int>(1) == length<metre, min_impl<int>>(1));
static_assert(length<metre, min_impl<int>>(2) - length<metre, double>(1.5) == length<metre, min_impl<double>>(0.5));
static_assert(length<metre, min_impl<int>>(2) - length<metre, min_impl<int>>(1) == length<metre, int>(1));
static_assert(length<metre, min_impl<int>>(2) - length<metre, min_impl<double>>(1.5) == length<metre, double>(0.5));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1999));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1998.5));
static_assert(length<kilometre, int>(2) - length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1999));
static_assert(length<kilometre, int>(2) - length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1998.5));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, int>(1) == length<metre, min_impl<int>>(1999));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, double>(1.5) == length<metre, min_impl<double>>(1998.5));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, min_impl<int>>(1) == length<metre, int>(1999));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, min_impl<double>>(1.5) == length<metre, double>(1998.5));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, min_impl<int>>(1) == length<metre, min_impl<int>>(1000));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(500));
static_assert(length<metre, int>(2000) - length<kilometre, min_impl<int>>(1) == length<metre, min_impl<int>>(1000));
static_assert(length<metre, int>(2000) - length<kilometre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(500));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, int>(1) == length<metre, min_impl<int>>(1000));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, double>(1.5) == length<metre, min_impl<double>>(500));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, min_impl<int>>(1) == length<metre, int>(1000));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, min_impl<double>>(1.5) == length<metre, double>(500));
static_assert(length<metre, min_impl<int>>(123) * min_impl<double>(1.5) == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, min_impl<int>>(123) * 1.5 == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, int>(123) * min_impl<double>(1.5) == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, min_impl<int>>(123) * quantity{min_impl<double>(1.5)} == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, min_impl<int>>(123) * quantity{1.5} == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, int>(123) * quantity{min_impl<double>(1.5)} == length<metre, min_impl<double>>(184.5));
static_assert(min_impl<double>(1.5) * length<metre, min_impl<int>>(123) == length<metre, min_impl<double>>(184.5));
static_assert(min_impl<double>(1.5) * length<metre, int>(123) == length<metre, min_impl<double>>(184.5));
static_assert(1.5 * length<metre, min_impl<int>>(123) == length<metre, min_impl<double>>(184.5));
static_assert(quantity{min_impl<double>(1.5)} * length<metre, min_impl<int>>(123) == length<metre, min_impl<double>>(184.5));
static_assert(quantity{min_impl<double>(1.5)} * length<metre, int>(123) == length<metre, min_impl<double>>(184.5));
static_assert(quantity{1.5} * length<metre, min_impl<int>>(123) == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, min_impl<int>>(123) / min_impl<double>(2.) == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, min_impl<int>>(123) / 2. == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, int>(123) / min_impl<double>(2.) == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, min_impl<int>>(123) / quantity{min_impl<double>(2.)} == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, min_impl<int>>(123) / quantity{2.} == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, int>(123) / quantity{min_impl<double>(2.)} == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, min_impl<int>>(123) / length<metre, min_impl<double>>(2.) == 61.5);
static_assert(length<metre, min_impl<int>>(123) / length<metre, double>(2.) == 61.5);
static_assert(length<metre, int>(123) / length<metre, min_impl<double>>(2.) == 61.5);
static_assert(length<metre, min_impl<int>>(123) % min_impl<int>(100) == length<metre, int>(23));
static_assert(length<metre, min_impl<int>>(123) % 100 == length<metre, int>(23));
static_assert(length<metre, int>(123) % min_impl<int>(100) == length<metre, int>(23));
static_assert(length<metre, min_impl<int>>(123) % quantity{min_impl<int>(100)} == length<metre, int>(23));
static_assert(length<metre, min_impl<int>>(123) % quantity{100} == length<metre, int>(23));
static_assert(length<metre, int>(123) % quantity{min_impl<int>(100)} == length<metre, int>(23));
} // namespace

View File

@ -36,14 +36,14 @@ using namespace units::physical::si;
struct sq_volt_per_hertz : unit<sq_volt_per_hertz> {};
struct dim_power_spectral_density : derived_dimension<dim_power_spectral_density, sq_volt_per_hertz, units::exponent<dim_voltage, 2>, units::exponent<dim_frequency, -1>> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_power_spectral_density> U, QuantityValue Rep = double>
using power_spectral_density = quantity<dim_power_spectral_density, U, Rep>;
// amplitude spectral density
struct volt_per_sqrt_hertz : unit<volt_per_sqrt_hertz> {};
struct dim_amplitude_spectral_density : derived_dimension<dim_amplitude_spectral_density, volt_per_sqrt_hertz, units::exponent<dim_voltage, 1>, units::exponent<dim_frequency, -1, 2>> {};
template<Unit U, ScalableNumber Rep = double>
template<UnitOf<dim_amplitude_spectral_density> U, QuantityValue Rep = double>
using amplitude_spectral_density = quantity<dim_amplitude_spectral_density, U, Rep>;
}

View File

@ -22,9 +22,16 @@
#include "test_tools.h"
#include "units/math.h"
#include "units/physical/si/si.h"
#include "units/physical/si/us/us.h"
#include "units/physical/si/cgs/derived/speed.h"
#include "units/physical/si/derived/area.h"
#include "units/physical/si/derived/frequency.h"
#include "units/physical/si/derived/speed.h"
#include "units/physical/si/derived/volume.h"
#include "units/physical/si/fps/derived/speed.h"
#include <chrono>
#include <complex>
#include <mutex>
#include <string>
#include <utility>
namespace {
@ -32,54 +39,67 @@ namespace {
using namespace units;
using namespace units::physical::si;
// class invariants
template<typename DimLength>
concept invalid_types = requires
{
requires !requires { typename quantity<DimLength, second, int>; }; // unit of a different dimension
requires !requires { typename quantity<DimLength, metre, quantity<DimLength, metre, int>>; }; // quantity used as Rep
requires !requires { typename quantity<metre, DimLength, double>; }; // reordered arguments
//////////////////////////////
// quantity class invariants
//////////////////////////////
static_assert(sizeof(length<metre>) == sizeof(double));
static_assert(sizeof(length<metre, short>) == sizeof(short));
#if COMP_GCC != 10 || COMP_GCC_MINOR != 2 // TODO remove when gcc 10.3 releases
template<template<typename, typename, typename> typename Q>
concept invalid_types = requires {
requires !requires { typename Q<dim_length, second, int>; }; // unit of a different dimension
requires !requires { typename Q<dim_length, metre, length<metre>>; }; // quantity used as Rep
requires !requires { typename Q<metre, dim_length, double>; }; // reordered arguments
requires !requires { typename Q<metre, double, dim_length>; }; // reordered arguments
};
static_assert(invalid_types<quantity>);
#endif
static_assert(invalid_types<dim_length>);
static_assert(std::is_trivially_default_constructible_v<length<metre>>);
static_assert(std::is_trivially_copy_constructible_v<length<metre>>);
static_assert(std::is_trivially_move_constructible_v<length<metre>>);
static_assert(std::is_trivially_copy_assignable_v<length<metre>>);
static_assert(std::is_trivially_move_assignable_v<length<metre>>);
static_assert(std::is_trivially_destructible_v<length<metre>>);
static_assert(std::is_nothrow_default_constructible_v<length<metre>>);
static_assert(std::is_nothrow_copy_constructible_v<length<metre>>);
static_assert(std::is_nothrow_move_constructible_v<length<metre>>);
static_assert(std::is_nothrow_copy_assignable_v<length<metre>>);
static_assert(std::is_nothrow_move_assignable_v<length<metre>>);
static_assert(std::is_nothrow_destructible_v<length<metre>>);
static_assert(std::is_trivially_copyable_v<length<metre>>);
static_assert(std::is_standard_layout_v<length<metre>>);
static_assert(std::default_initializable<length<metre>>);
static_assert(std::move_constructible<length<metre>>);
static_assert(std::copy_constructible<length<metre>>);
static_assert(std::equality_comparable<length<metre>>);
static_assert(std::totally_ordered<length<metre>>);
static_assert(std::regular<length<metre>>);
static_assert(std::three_way_comparable<length<metre>>);
//////////////////
// member types
//////////////////
static_assert(is_same_v<length<metre, int>::rep, int>);
static_assert(is_same_v<length<metre, double>::rep, double>);
static_assert(is_same_v<length<metre, int>::dimension, dim_length>);
static_assert(is_same_v<fps::length<fps::mile>::dimension, fps::dim_length>);
static_assert(is_same_v<length<metre, int>::unit, metre>);
static_assert(is_same_v<length<kilometre, int>::unit, kilometre>);
static_assert(is_same_v<fps::length<fps::mile>::unit, fps::mile>);
static_assert(is_same_v<length<metre, int>::rep, int>);
static_assert(is_same_v<fps::length<fps::mile>::rep, double>);
// constructors
static_assert(length<metre, int>().count() == 0);
constexpr length<metre, int> km{1000};
static_assert(km.count() == 1000);
static_assert(length<metre, int>(km).count() == km.count());
static_assert(length<metre, int>(1).count() == 1);
static_assert(!std::is_constructible_v<length<metre, int>, double>); // truncating conversion
static_assert(length<metre, double>(1.0).count() == 1.0);
static_assert(length<metre, double>(1).count() == 1.0);
static_assert(length<metre, double>(3.14).count() == 3.14);
static_assert(length<metre, int>(km).count() == 1000);
static_assert(!std::is_constructible_v<length<metre, int>,
length<metre, double>>); // truncating conversion
static_assert(length<metre, double>(1000.0_q_m).count() == 1000.0);
static_assert(length<metre, double>(km).count() == 1000.0);
static_assert(length<metre, int>(1_q_km).count() == 1000);
static_assert(!std::is_constructible_v<length<metre, int>,
physical::si::time<second, int>>); // different dimensions
static_assert(!std::is_constructible_v<length<kilometre, int>,
length<metre, int>>); // truncating conversion
// assignment operator
static_assert([]() { length<metre, int> l1(1), l2(2); return l2 = l1; }().count() == 1);
////////////////////////////
// static member functions
////////////////////////////
static_assert(length<metre, int>::zero().count() == 0);
static_assert(length<metre, int>::min().count() == std::numeric_limits<int>::lowest());
@ -88,32 +108,165 @@ static_assert(length<metre, double>::zero().count() == 0.0);
static_assert(length<metre, double>::min().count() == std::numeric_limits<double>::lowest());
static_assert(length<metre, double>::max().count() == std::numeric_limits<double>::max());
// unary member operators
static_assert((+km).count() == 1000);
static_assert((-km).count() == -1000);
static_assert((+(-km)).count() == -1000);
static_assert((-(-km)).count() == 1000);
//////////////////////////////
// construction from a value
//////////////////////////////
static_assert([](auto v) {
auto vv = v++;
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(1001), length<metre, int>(1000)));
static_assert([](auto v) {
auto vv = ++v;
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(1001), length<metre, int>(1001)));
static_assert([](auto v) {
auto vv = v--;
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(999), length<metre, int>(1000)));
static_assert([](auto v) {
auto vv = --v;
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(999), length<metre, int>(999)));
// only explicit construction from a value
static_assert(std::constructible_from<length<metre>, double>);
static_assert(!std::convertible_to<double, length<metre>>);
static_assert(std::constructible_from<length<metre>, float>);
static_assert(!std::convertible_to<float, length<metre>>);
static_assert(std::constructible_from<length<metre, float>, double>); // truncating implicit conversions double -> float allowed
static_assert(!std::convertible_to<double, length<metre, float>>);
static_assert(std::constructible_from<length<metre>, int>);
static_assert(!std::convertible_to<int, length<metre>>);
static_assert(std::constructible_from<length<metre>, short>);
static_assert(!std::convertible_to<short, length<metre>>);
static_assert(std::constructible_from<length<metre, short>, int>); // truncating implicit conversions int -> short allowed
static_assert(!std::convertible_to<int, length<metre, short>>);
// exception, implicit construction from a value allowed for a dimensionless quantity
static_assert(std::constructible_from<dimensionless<one>, double>);
static_assert(std::convertible_to<double, dimensionless<one>>);
static_assert(std::constructible_from<dimensionless<one>, float>);
static_assert(std::convertible_to<float, dimensionless<one>>);
static_assert(std::constructible_from<dimensionless<one, float>, double>);
static_assert(std::convertible_to<double, dimensionless<one, float>>);
static_assert(std::constructible_from<dimensionless<one>, int>);
static_assert(std::convertible_to<int, dimensionless<one>>);
static_assert(std::constructible_from<dimensionless<one>, short>);
static_assert(std::convertible_to<short, dimensionless<one>>);
static_assert(std::constructible_from<dimensionless<one, short>, int>);
static_assert(std::convertible_to<int, dimensionless<one, short>>);
// but only if a dimensionless quantity has a ratio(1)
static_assert(std::constructible_from<dimensionless<percent>, double>);
static_assert(!std::convertible_to<double, dimensionless<percent>>);
static_assert(std::constructible_from<dimensionless<percent>, float>);
static_assert(!std::convertible_to<float, dimensionless<percent>>);
static_assert(std::constructible_from<dimensionless<percent, float>, double>); // truncating implicit conversions double -> float allowed
static_assert(!std::convertible_to<double, dimensionless<percent, float>>);
static_assert(std::constructible_from<dimensionless<percent>, int>);
static_assert(!std::convertible_to<int, dimensionless<percent>>);
static_assert(std::constructible_from<dimensionless<percent>, short>);
static_assert(!std::convertible_to<short, dimensionless<percent>>);
static_assert(std::constructible_from<dimensionless<percent, short>, int>); // truncating implicit conversions int -> short allowed
static_assert(!std::convertible_to<int, dimensionless<percent, short>>);
// floating-point to integral truncating conversion not allowed
static_assert(!std::constructible_from<length<metre, int>, double>);
static_assert(!std::convertible_to<double, length<metre, int>>);
static_assert(!std::constructible_from<dimensionless<one, int>, double>);
static_assert(!std::convertible_to<double, dimensionless<one, int>>);
static_assert(length<metre, int>().count() == 0); // value initialization
static_assert(length<metre, int>(1).count() == 1);
static_assert(length<metre, double>(1.0).count() == 1.0);
static_assert(length<metre, double>(1).count() == 1.0);
static_assert(length<metre, double>(3.14).count() == 3.14);
///////////////////////////////////////
// construction from another quantity
///////////////////////////////////////
// conversion only between equivalent dimensions
static_assert(std::constructible_from<length<metre>, length<metre>>);
static_assert(std::convertible_to<length<metre>, length<metre>>);
static_assert(std::constructible_from<length<centimetre>, cgs::length<cgs::centimetre>>);
static_assert(std::convertible_to<cgs::length<cgs::centimetre>, length<centimetre>>);
static_assert(std::constructible_from<fps::length<fps::foot>, cgs::length<cgs::centimetre>>);
static_assert(std::convertible_to<cgs::length<cgs::centimetre>, fps::length<fps::foot>>);
// conversion between different dimensions not allowed
static_assert(!std::constructible_from<length<metre>, physical::si::time<second>>);
static_assert(!std::convertible_to<physical::si::time<second>, length<metre>>);
static_assert(!std::constructible_from<length<metre>, speed<metre_per_second>>);
static_assert(!std::convertible_to<speed<metre_per_second>, length<metre>>);
// implicit conversion from another quantity only if non-truncating
static_assert(std::constructible_from<length<metre>, length<metre, int>>); // int -> double OK
static_assert(std::convertible_to<length<metre, int>, length<metre>>); // int -> double OK
static_assert(!std::constructible_from<length<metre, int>, length<metre>>); // truncating double -> int not allowed
static_assert(!std::convertible_to<length<metre>, length<metre, int>>); // truncating double -> int not allowed
static_assert(std::constructible_from<length<metre, int>, length<kilometre, int>>); // kilometre<int> -> metre<int> OK
static_assert(std::convertible_to<length<kilometre, int>, length<metre, int>>); // kilometre<int> -> metre<int> OK
static_assert(!std::constructible_from<length<kilometre, int>, length<metre, int>>); // truncating metre<int> -> kilometre<int> not allowed
static_assert(!std::convertible_to<length<metre, int>, length<kilometre, int>>); // truncating metre<int> -> kilometre<int> not allowed
// converting to double always OK
static_assert(std::constructible_from<length<metre>, length<kilometre, int>>);
static_assert(std::convertible_to<length<kilometre, int>, length<metre>>);
static_assert(std::constructible_from<length<kilometre>, length<metre, int>>);
static_assert(std::convertible_to<length<metre, int>, length<kilometre>>);
static_assert(length<metre, int>(123_q_m).count() == 123);
static_assert(length<kilometre, int>(2_q_km).count() == 2);
static_assert(length<metre, int>(2_q_km).count() == 2000);
static_assert(length<kilometre>(1500_q_m).count() == 1.5);
/////////
// CTAD
/////////
static_assert(std::is_same_v<decltype(quantity{length<metre, int>(123)}), length<metre, int>>);
static_assert(std::is_same_v<decltype(quantity{speed<metre_per_second>(123)}), speed<metre_per_second>>);
// static_assert(std::is_same_v<decltype(length{length<metre, int>(123)}), length<metre, int>>); // TODO gcc ICE
static_assert(std::is_same_v<decltype(quantity{123_q_m}), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(quantity{1}), dimensionless<one, int>>);
static_assert(std::is_same_v<decltype(quantity{1.23}), dimensionless<one, double>>);
////////////////////////
// assignment operator
////////////////////////
static_assert([]() { length<metre, int> l1(1), l2(2); return l2 = l1; }().count() == 1);
static_assert([]() { length<metre, int> l1(1), l2(2); return l2 = std::move(l1); }().count() == 1);
////////////////////
// unary operators
////////////////////
static_assert((+123_q_m).count() == 123);
static_assert((-123_q_m).count() == -123);
static_assert((+(-123_q_m)).count() == -123);
static_assert((-(-123_q_m)).count() == 123);
static_assert([](auto v) { auto vv = v++; return std::pair(v, vv); }(123_q_m) == std::pair(124_q_m, 123_q_m));
static_assert([](auto v) { auto vv = ++v; return std::pair(v, vv); }(123_q_m) == std::pair(124_q_m, 124_q_m));
static_assert([](auto v) { auto vv = v--; return std::pair(v, vv); }(123_q_m) == std::pair(122_q_m, 123_q_m));
static_assert([](auto v) { auto vv = --v; return std::pair(v, vv); }(123_q_m) == std::pair(122_q_m, 122_q_m));
////////////////////////
// compound assignment
////////////////////////
// same type
static_assert((1_q_m += 1_q_m).count() == 2);
static_assert((2_q_m -= 1_q_m).count() == 1);
static_assert((1_q_m *= 2).count() == 2);
@ -121,190 +274,372 @@ static_assert((2_q_m /= 2).count() == 1);
static_assert((7_q_m %= 2).count() == 1);
static_assert((7_q_m %= 2_q_m).count() == 1);
// different types
static_assert((2.5_q_m += 3_q_m).count() == 5.5);
static_assert((2.5_q_m += 3.5_q_m).count() == 6);
static_assert((123_q_m += 1_q_km).count() == 1123);
static_assert((5.5_q_m -= 3_q_m).count() == 2.5);
static_assert((1123_q_m -= 1_q_km).count() == 123);
static_assert((2.5_q_m *= 3).count() == 7.5);
static_assert((2.5_q_m *= 3.5).count() == 8.75);
static_assert((7.5_q_m /= 3).count() == 2.5);
static_assert((3500_q_m %= 1_q_km).count() == 500);
// operations not allowed for the respective quantities
template<typename Metre>
concept invalid_compound_assignments = requires()
{
concept invalid_compound_assignments = requires() {
// truncating not allowed
requires !requires(length<Metre, int> l) { l += 2.5_q_m; };
requires !requires(length<Metre, int> l) { l -= 2.5_q_m; };
requires !requires(length<kilometre, int> l) { l += length<Metre, int>(2); };
requires !requires(length<kilometre, int> l) { l -= length<Metre, int>(2); };
requires !requires(length<kilometre, int> l) { l %= length<Metre, int>(2); };
// only quantities can be added or subtracted
requires !requires(length<Metre, int> l) { l += 2; };
requires !requires(length<Metre, int> l) { l -= 2; };
// compound multiply/divide by another quantity not allowed
requires !requires(length<Metre, int> l) { l *= 2_q_m; };
requires !requires(length<Metre, int> l) { l /= 2_q_m; };
// modulo operations on a floating point representation not allowed
requires !requires(length<Metre, double> l) { l %= 2.; };
requires !requires(length<Metre, double> l) { l %= 2; };
requires !requires(length<Metre, int> l) { l %= 2.; };
requires !requires(length<Metre, double> l) { l %= 2._q_m; };
requires !requires(length<Metre, double> l) { l %= 2_q_m; };
requires !requires(length<Metre, int> l) { l %= 2._q_m; };
requires !requires(length<Metre, int> l) { l += 3.5_q_m; };
requires !requires(length<Metre, int> l) { l *= 3.5_q_m; };
};
static_assert(invalid_compound_assignments<metre>);
// non-member arithmetic operators
static_assert(compare<decltype(length<metre, int>() + length<metre, double>()), length<metre, double>>);
static_assert(compare<decltype(length<metre, int>() + length<metre, double>()), length<metre, double>>);
static_assert(compare<decltype(length<kilometre, int>() + length<metre, double>()), length<metre, double>>);
static_assert(compare<decltype(length<metre, double>() - length<metre, int>()), length<metre, double>>);
static_assert(compare<decltype(length<kilometre, double>() - length<metre, int>()), length<metre, double>>);
static_assert(compare<decltype(length<metre, int>() * 1.0), length<metre, double>>);
static_assert(compare<decltype(1.0 * length<metre, int>()), length<metre, double>>);
static_assert(
compare<decltype(speed<metre_per_second, int>() * physical::si::time<second, int>()), length<metre, int>>);
static_assert(
compare<decltype(speed<metre_per_second, int>() * physical::si::time<hour, int>()), length<scaled_unit<ratio(36, 1, 2), metre>, int>>);
static_assert(compare<decltype(length<metre>() * physical::si::time<minute>()),
quantity<unknown_dimension<units::exponent<dim_length, 1>, units::exponent<dim_time, 1>>, scaled_unit<ratio(6, 1, 1), unknown_coherent_unit>>>);
static_assert(compare<decltype(1 / physical::si::time<second, int>()), frequency<hertz, int>>);
static_assert(compare<decltype(1 / physical::si::time<minute, int>()), frequency<scaled_unit<ratio(1, 6, -1), hertz>, int>>);
static_assert(compare<decltype(1 / frequency<hertz, int>()), physical::si::time<second, int>>);
static_assert(compare<decltype(1 / length<kilometre>()),
quantity<unknown_dimension<units::exponent<dim_length, -1>>, scaled_unit<ratio(1, 1, -3), unknown_coherent_unit>>>);
static_assert(compare<decltype(length<metre, int>() / 1.0), length<metre, double>>);
static_assert(compare<decltype(length<metre, int>() / length<metre, double>()), dimensionless<one, double>>);
static_assert(compare<decltype(length<kilometre, int>() / length<metre, double>()), dimensionless<scaled_unit<ratio(1, 1, 3), one>, double>>);
static_assert(
compare<decltype(length<metre, int>() / physical::si::time<second, int>()), speed<metre_per_second, int>>);
static_assert(
compare<decltype(length<metre>() / physical::si::time<minute>()), speed<scaled_unit<ratio(1, 6, -1), metre_per_second>>>);
static_assert(compare<decltype(physical::si::time<minute>() / length<metre>()),
quantity<unknown_dimension<units::exponent<dim_length, -1>, units::exponent<dim_time, 1>>, scaled_unit<ratio(6 ,1 , 1), unknown_coherent_unit>>>);
static_assert(compare<decltype(length<metre, int>() % short(1)), length<metre, int>>);
static_assert(compare<decltype(length<metre, int>() % length<metre, short>(1)), length<metre, int>>);
////////////////////
// binary operators
////////////////////
static_assert((1_q_m + km).count() == 1001);
template<typename Metre>
concept invalid_binary_operations = requires {
// no crossdimensional addition and subtraction
requires !requires { 1_q_s + length<Metre, int>(1); };
requires !requires { 1_q_s - length<Metre, int>(1); };
// no floating-point modulo
requires !requires(length<Metre, double> a) { a % 2_q_m; };
requires !requires(length<Metre, double> a) { 2_q_m % a; };
requires !requires(length<Metre, double> a) { a % 2; };
requires !requires(length<Metre, double> a, length<Metre, double> b) { a % b; };
requires !requires(length<Metre, double> a, length<Metre, int> b) { a % b; };
requires !requires(length<Metre, double> a, length<Metre, int> b) { b % a; };
};
static_assert(invalid_binary_operations<metre>);
// same representation type
static_assert(std::is_same_v<decltype(1_q_m + 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m - 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m * 1), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m * quantity{1}), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1 * 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(quantity{1} * 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m / 1), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m / quantity{1}), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m % 1), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m % quantity{1}), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m % 1_q_m), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m * dimensionless<percent, std::int64_t>(1)), length<centimetre, std::int64_t>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) * 1_q_m), length<centimetre, std::int64_t>>);
static_assert(compare<decltype(1_q_m / dimensionless<percent, std::int64_t>(1)), length<hectometre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % dimensionless<percent, std::int64_t>(1)), length<centimetre, std::int64_t>>);
static_assert(compare<decltype(1_q_m * 1_q_m), area<square_metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m / 1_q_m), dimensionless<one, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_s), frequency<hertz, std::int64_t>>);
static_assert(compare<decltype(quantity{1} / 1_q_s), frequency<hertz, std::int64_t>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) / 1_q_s), frequency<scaled_unit<ratio(1, 100), hertz>, std::int64_t>>);
// different representation types
static_assert(std::is_same_v<decltype(1_q_m + 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m - 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m * 1.L), length<metre, long double>>); // TODO should we address fundamental types implicit truncating conversions with concepts?
static_assert(std::is_same_v<decltype(1 * 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m * quantity{1.L}), length<metre, long double>>); // TODO should we address fundamental types implicit truncating conversions with concepts?
static_assert(std::is_same_v<decltype(quantity{1} * 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m / 1.L), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m / quantity{1.L}), length<metre, long double>>);
static_assert(compare<decltype(1_q_m * dimensionless<percent, long double>(1)), length<centimetre, long double>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) * 1._q_m), length<centimetre, long double>>);
static_assert(compare<decltype(1_q_m * 1._q_m), area<square_metre, long double>>);
static_assert(compare<decltype(1_q_m / dimensionless<percent, long double>(1)), length<hectometre, long double>>);
static_assert(compare<decltype(1_q_m / 1._q_m), dimensionless<one, long double>>);
static_assert(compare<decltype(1 / 1._q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(quantity{1} / 1._q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) / 1._q_s), frequency<scaled_unit<ratio(1, 100), hertz>, long double>>);
static_assert(compare<decltype(1_q_m % short(1)), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % quantity{short(1)}), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % dimensionless<percent, short>(1)), length<centimetre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % length<metre, short>(1)), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_m + 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m - 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m * 1), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1.L * 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m * quantity{1}), length<metre, long double>>);
static_assert(std::is_same_v<decltype(quantity{1.L} * 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m / 1), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m / quantity{1}), length<metre, long double>>);
static_assert(compare<decltype(1._q_m * dimensionless<percent, std::int64_t>(1)), length<centimetre, long double>>);
static_assert(compare<decltype(dimensionless<percent, long double>(1) * 1_q_m), length<centimetre, long double>>);
static_assert(compare<decltype(1._q_m / dimensionless<percent, std::int64_t>(1)), length<hectometre, long double>>);
static_assert(compare<decltype(1._q_m * 1_q_m), area<square_metre, long double>>);
static_assert(compare<decltype(1._q_m / 1_q_m), dimensionless<one, long double>>);
static_assert(compare<decltype(1.L / 1_q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(quantity{1.L} / 1_q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(dimensionless<percent, long double>(1) / 1_q_s), frequency<scaled_unit<ratio(1, 100), hertz>, long double>>);
// different units
static_assert(std::is_same_v<decltype(1_q_m + 1_q_km), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_m + 1_q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m + 1._q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m + 1._q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_km + 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_km + 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_km + 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_km + 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m - 1_q_km), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_m - 1_q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m - 1._q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m - 1._q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_km - 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_km - 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_km - 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_km - 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m % 1_q_km), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_km % 1_q_m), length<metre, std::int64_t>>);
// different dimensions
static_assert(compare<decltype(1_q_m_per_s * 1_q_s), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m_per_s * 1_q_h), length<scaled_unit<ratio(36, 1, 2), metre>, std::int64_t>>);
static_assert(compare<decltype(1_q_m * 1_q_min), quantity<unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, 1>>, scaled_unit<ratio(60), unknown_coherent_unit>, std::int64_t>>);
static_assert(compare<decltype(1_q_s * 1_q_Hz), dimensionless<one, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_min), frequency<scaled_unit<ratio(1, 60), hertz>, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_Hz), physical::si::time<second, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_km), quantity<unknown_dimension<exponent<dim_length, -1>>, scaled_unit<ratio(1, 1, -3), unknown_coherent_unit>, std::int64_t>>);
static_assert(compare<decltype(1_q_km / 1_q_m), dimensionless<scaled_unit<ratio(1000), one>, std::int64_t>>);
static_assert(compare<decltype(1_q_m / 1_q_s), speed<metre_per_second, std::int64_t>>);
static_assert(compare<decltype(1_q_m / 1_q_min), speed<scaled_unit<ratio(1, 60), metre_per_second>, std::int64_t>>);
static_assert(compare<decltype(1_q_min / 1_q_m), quantity<unknown_dimension<exponent<dim_length, -1>, exponent<dim_time, 1>>, scaled_unit<ratio(60), unknown_coherent_unit>, std::int64_t>>);
static_assert((1_q_m + 1_q_m).count() == 2);
static_assert((1_q_m + 1_q_km).count() == 1001);
static_assert((km - 1_q_m).count() == 999);
static_assert((1_q_km + 1_q_m).count() == 1001);
static_assert((2_q_m - 1_q_m).count() == 1);
static_assert((1_q_km - 1_q_m).count() == 999);
static_assert((2_q_m * 2).count() == 4);
static_assert((2_q_m * quantity{2}).count() == 4);
static_assert((2_q_m * dimensionless<percent, int>(2)).count() == 4);
static_assert((3 * 3_q_m).count() == 9);
static_assert((quantity{3} * 3_q_m).count() == 9);
static_assert((dimensionless<percent, int>(3) * 3_q_m).count() == 9);
static_assert((4_q_m / 2).count() == 2);
static_assert((4_q_m / quantity{2}).count() == 2);
static_assert((4_q_m / dimensionless<percent, int>(2)).count() == 2);
static_assert((4_q_km / 2_q_m).count() == 2);
static_assert((4000_q_m / 2_q_m).count() == 2000);
static_assert((1.5_q_m + 1_q_m).count() == 2.5);
static_assert((1.5_q_m + 1_q_km).count() == 1001.5);
static_assert((1.5_q_km + 1_q_m).count() == 1501);
static_assert((2.5_q_m - 1_q_m).count() == 1.5);
static_assert((1.5_q_km - 1_q_m).count() == 1499);
static_assert((2.5_q_m * 2).count() == 5);
static_assert((2.5_q_m * quantity{2}).count() == 5);
static_assert((2.5_q_m * dimensionless<percent, int>(2)).count() == 5);
static_assert((2.5L * 2_q_m).count() == 5);
static_assert((quantity{2.5L} * 2_q_m).count() == 5);
static_assert((dimensionless<percent, long double>(2.5L) * 2_q_m).count() == 5);
static_assert((5._q_m / 2).count() == 2.5);
static_assert((5._q_m / quantity{2}).count() == 2.5);
static_assert((5._q_m / dimensionless<percent, int>(2)).count() == 2.5);
static_assert((5._q_km / 2_q_m).count() == 2.5);
static_assert((5000._q_m / 2_q_m).count() == 2500);
static_assert((1_q_m + 1.5_q_m).count() == 2.5);
static_assert((1_q_m + 1.5_q_km).count() == 1501);
static_assert((1_q_km + 1.5_q_m).count() == 1001.5);
static_assert((2_q_m - 1.5_q_m).count() == 0.5);
static_assert((1_q_km - 1.5_q_m).count() == 998.5);
static_assert((2_q_m * 2.5L).count() == 5);
static_assert((2_q_m * quantity{2.5L}).count() == 5);
static_assert((2_q_m * dimensionless<percent, long double>(2.5L)).count() == 5);
static_assert((2 * 2.5_q_m).count() == 5);
static_assert((quantity{2} * 2.5_q_m).count() == 5);
static_assert((dimensionless<percent, int>(2) * 2.5_q_m).count() == 5);
static_assert((5_q_m / 2.5L).count() == 2);
static_assert((5_q_m / quantity{2.5L}).count() == 2);
static_assert((5_q_m / dimensionless<percent, long double>(2.5L)).count() == 2);
static_assert((5_q_km / 2.5_q_m).count() == 2);
static_assert((5000_q_m / 2.5_q_m).count() == 2000);
static_assert((7_q_m % 2).count() == 1);
static_assert((7_q_m % quantity{2}).count() == 1);
static_assert((7_q_m % dimensionless<percent, int>(2)).count() == 1);
static_assert((7_q_m % 2_q_m).count() == 1);
static_assert((7_q_km % 2000_q_m).count() == 1000);
static_assert((10_q_km2 * 10_q_km2) / 50_q_km2 == 2_q_km2);
constexpr auto q1 = 10_q_km / 5_q_m;
static_assert(compare<decltype(q1), const dimensionless<scaled_unit<ratio(1, 1, 3), one>, std::int64_t>>);
static_assert(q1.count() == 2);
constexpr dimensionless<one> q2 = q1;
static_assert(q2.count() == 2000);
static_assert((10_q_km / 5_q_m).count() == 2);
static_assert(dimensionless<one>(10_q_km / 5_q_m).count() == 2000);
#if UNITS_DOWNCAST_MODE == 0
static_assert(quantity_cast<dim_one, one>(q1).count() == 2000);
static_assert(quantity_cast<dim_one, one>(10_q_km / 5_q_m).count() == 2000);
#else
static_assert(quantity_cast<one>(q1).count() == 2000);
static_assert(quantity_cast<one>(10_q_km / 5_q_m).count() == 2000);
#endif
constexpr auto q3 = 10_q_s * 2_q_kHz;
static_assert(compare<decltype(q3), const dimensionless<scaled_unit<ratio(1, 1, 3), one>, std::int64_t>>);
static_assert(q3.count() == 20);
static_assert((10_q_s * 2_q_kHz).count() == 20);
// comparators
static_assert(2_q_m + 1_q_m == 3_q_m);
static_assert(!(2_q_m + 2_q_m == 3_q_m));
static_assert(2_q_m + 2_q_m != 3_q_m);
static_assert(!(2_q_m + 2_q_m != 4_q_m));
static_assert(2_q_m > 1_q_m);
static_assert(!(1_q_m > 1_q_m));
static_assert(1_q_m < 2_q_m);
static_assert(!(2_q_m < 2_q_m));
static_assert(2_q_m >= 1_q_m);
static_assert(2_q_m >= 2_q_m);
static_assert(!(2_q_m >= 3_q_m));
static_assert(1_q_m <= 2_q_m);
static_assert(2_q_m <= 2_q_m);
static_assert(!(3_q_m <= 2_q_m));
// dimensionless
static_assert(3_q_m == 3.0_q_m);
static_assert(3_q_m != 3.14_q_m);
static_assert(2_q_m > 1.0_q_m);
static_assert(1.0_q_m < 2_q_m);
static_assert(2.0_q_m >= 1_q_m);
static_assert(1_q_m <= 2.0_q_m);
static_assert(quantity{1} + quantity{1} == 2);
static_assert(1 + quantity{1} == 2);
static_assert(quantity{1} + 1 == 2);
static_assert(quantity{2} - quantity{1} == 1);
static_assert(2 - quantity{1} == 1);
static_assert(quantity{2} - 1 == 1);
static_assert(quantity{2} * quantity{2} == 4);
static_assert(2 * quantity{2} == 4);
static_assert(quantity{2} * 2 == 4);
static_assert(quantity{4} / quantity{2} == 2);
static_assert(4 / quantity{2} == 2);
static_assert(quantity{4} / 2 == 2);
static_assert(quantity{4} % quantity{2} == 0);
static_assert(4 % quantity{2} == 0);
static_assert(quantity{4} % 2 == 0);
static_assert(1000_q_m == 1_q_km);
static_assert(1001_q_m != 1_q_km);
static_assert(1001_q_m > 1_q_km);
static_assert(999_q_m < 1_q_km);
static_assert(1000_q_m >= 1_q_km);
static_assert(1000_q_m <= 1_q_km);
// alias units
///////////////////////
// equality operators
///////////////////////
static_assert(2_q_l + 2_q_ml == 2002_q_ml);
static_assert(2_q_l + 2_q_ml == 2002_q_cm3);
static_assert(2_q_l + 2_q_cm3 == 2002_q_ml);
static_assert(2_q_dm3 + 2_q_cm3 == 2002_q_ml);
template<typename Metre>
concept no_crossdimensional_equality = requires {
requires !requires { 1_q_s == length<Metre, int>(1); };
requires !requires { 1_q_s != length<Metre, int>(1); };
};
static_assert(no_crossdimensional_equality<metre>);
// is_quantity
// same type
static_assert(length<metre, int>(123) == length<metre, int>(123));
static_assert(length<metre, int>(321) != length<metre, int>(123));
static_assert(!(length<metre, int>(123) == length<metre, int>(321)));
static_assert(!(length<metre, int>(123) != length<metre, int>(123)));
static_assert(Quantity<length<millimetre, int>>);
// different types
static_assert(length<metre, double>(123) == length<metre, int>(123));
static_assert(length<metre, double>(321) != length<metre, int>(123));
static_assert(!(length<metre, double>(123) == length<metre, int>(321)));
static_assert(!(length<metre, double>(123) != length<metre, int>(123)));
// common_quantity
static_assert(length<kilometre, int>(123) == length<metre, int>(123000));
static_assert(length<kilometre, int>(321) != length<metre, int>(123000));
static_assert(!(length<kilometre, int>(123) == length<metre, int>(321000)));
static_assert(!(length<kilometre, int>(123) != length<metre, int>(123000)));
static_assert(compare<common_quantity<length<metre, int>, length<kilometre, int>>, length<metre, int>>);
static_assert(compare<common_quantity<length<kilometre, long long>, length<metre, int>>, length<metre, long long>>);
static_assert(
compare<common_quantity<length<kilometre, long long>, length<millimetre, double>>, length<millimetre, double>>);
// dimensionless
// common_type
static_assert(quantity{123} == 123);
static_assert(quantity{321} != 123);
static_assert(123 == quantity{123});
static_assert(123 != quantity{321});
using namespace units::physical::si::us::literals;
static_assert(std::equality_comparable<decltype(1_q_m)>);
static_assert(std::equality_comparable_with<decltype(1_q_m), decltype(1_q_cm)>);
static_assert(0_q_m == 0_q_ft_us);
static_assert(std::equality_comparable_with<decltype(1_q_m), decltype(1_q_ft_us)>);
///////////////////////
// ordering operators
///////////////////////
template<typename Metre>
concept no_crossdimensional_ordering = requires {
requires !requires { 1_q_s < length<Metre, int>(1); };
requires !requires { 1_q_s > length<Metre, int>(1); };
requires !requires { 1_q_s <= length<Metre, int>(1); };
requires !requires { 1_q_s >= length<Metre, int>(1); };
};
static_assert(no_crossdimensional_ordering<metre>);
// same type
static_assert(length<metre, int>(123) < length<metre, int>(321));
static_assert(length<metre, int>(123) <= length<metre, int>(123));
static_assert(length<metre, int>(123) <= length<metre, int>(321));
static_assert(length<metre, int>(321) > length<metre, int>(123));
static_assert(length<metre, int>(123) >= length<metre, int>(123));
static_assert(length<metre, int>(321) >= length<metre, int>(123));
static_assert(!(length<metre, int>(321) < length<metre, int>(123)));
static_assert(!(length<metre, int>(123) < length<metre, int>(123)));
static_assert(!(length<metre, int>(321) <= length<metre, int>(123)));
static_assert(!(length<metre, int>(123) > length<metre, int>(321)));
static_assert(!(length<metre, int>(123) > length<metre, int>(123)));
static_assert(!(length<metre, int>(123) >= length<metre, int>(321)));
// different types
static_assert(length<metre, double>(123) < length<metre, int>(321));
static_assert(length<metre, double>(123) <= length<metre, int>(123));
static_assert(length<metre, double>(123) <= length<metre, int>(321));
static_assert(length<metre, double>(321) > length<metre, int>(123));
static_assert(length<metre, double>(123) >= length<metre, int>(123));
static_assert(length<metre, double>(321) >= length<metre, int>(123));
static_assert(!(length<metre, double>(321) < length<metre, int>(123)));
static_assert(!(length<metre, double>(123) < length<metre, int>(123)));
static_assert(!(length<metre, double>(321) <= length<metre, int>(123)));
static_assert(!(length<metre, double>(123) > length<metre, int>(321)));
static_assert(!(length<metre, double>(123) > length<metre, int>(123)));
static_assert(!(length<metre, double>(123) >= length<metre, int>(321)));
static_assert(length<kilometre, int>(123) < length<metre, int>(321000));
static_assert(length<kilometre, int>(123) <= length<metre, int>(123000));
static_assert(length<kilometre, int>(123) <= length<metre, int>(321000));
static_assert(length<kilometre, int>(321) > length<metre, int>(123000));
static_assert(length<kilometre, int>(123) >= length<metre, int>(123000));
static_assert(length<kilometre, int>(321) >= length<metre, int>(123000));
static_assert(!(length<kilometre, int>(321) < length<metre, int>(123000)));
static_assert(!(length<kilometre, int>(123) < length<metre, int>(123000)));
static_assert(!(length<kilometre, int>(321) <= length<metre, int>(123000)));
static_assert(!(length<kilometre, int>(123) > length<metre, int>(321000)));
static_assert(!(length<kilometre, int>(123) > length<metre, int>(123000)));
static_assert(!(length<kilometre, int>(123) >= length<metre, int>(321000)));
// dimensionless
static_assert(quantity{123} < 321);
static_assert(quantity{123} <= 123);
static_assert(quantity{123} <= 321);
static_assert(quantity{321} > 123);
static_assert(quantity{123} >= 123);
static_assert(quantity{321} >= 123);
static_assert(123 < quantity{321});
static_assert(123 <= quantity{123});
static_assert(123 <= quantity{321});
static_assert(321 > quantity{123});
static_assert(123 >= quantity{123});
static_assert(321 >= quantity{123});
//////////////////
// dimensionless
//////////////////
static_assert(std::equality_comparable_with<dimensionless<one>, int>);
static_assert(std::equality_comparable_with<dimensionless<one>, double>);
static_assert(std::equality_comparable_with<dimensionless<one, int>, int>);
static_assert(!std::equality_comparable_with<dimensionless<one, int>, double>);
// quantity_cast
static_assert(compare<decltype(quantity_cast<scaled_unit<ratio(1), metre>>(2_q_km))::unit, metre>);
static_assert(quantity_cast<length<metre, int>>(2_q_km).count() == 2000);
static_assert(quantity_cast<length<kilometre, int>>(2000_q_m).count() == 2);
static_assert(quantity_cast<length<metre, int>>(1.23_q_m).count() == 1);
static_assert(quantity_cast<metre>(2_q_km).count() == 2000);
static_assert(quantity_cast<kilometre>(2000_q_m).count() == 2);
static_assert(quantity_cast<int>(1.23_q_m).count() == 1);
static_assert(quantity_cast<dim_speed, kilometre_per_hour>(2000.0_q_m / 3600.0_q_s).count() == 2);
// dimensionless
static_assert(std::is_convertible_v<double, dimensionless<one>>);
static_assert(std::is_convertible_v<float, dimensionless<one>>);
static_assert(!std::is_convertible_v<double, dimensionless<one, int>>);
static_assert(std::is_convertible_v<int, dimensionless<one>>);
static_assert(!std::is_convertible_v<double, dimensionless<scaled_unit<ratio(1, 1, 1), one>>>);
static_assert(std::is_constructible_v<dimensionless<scaled_unit<ratio(1, 1, 1), one>>, double>);
static_assert(dimensionless<one>(1.23) + dimensionless<one>(1.23) == dimensionless<one>(2.46));
static_assert(dimensionless<one>(1.23) + dimensionless<one>(1.23) == 2.46);
static_assert(dimensionless<one>(1.23) + 1.23 == 2.46);
static_assert(1.23 + dimensionless<one>(1.23) == 2.46);
static_assert(dimensionless<one>(1) + 1 == 2);
static_assert(dimensionless<one, int>(1) + 1 == 2);
template<typename Int>
concept invalid_dimensionless_operations = requires
{
concept invalid_dimensionless_operations = requires {
requires !requires(dimensionless<one, Int> d) { d + 1.23; };
requires !requires(dimensionless<one, Int> d) { 1.23 + d; };
requires !requires(dimensionless<scaled_unit<ratio(1, 1, 1), one>, Int> d) { 1 + d; };
requires !requires(dimensionless<scaled_unit<ratio(1, 1, 1), one>, Int> d) { d + 1; };
requires !requires(dimensionless<percent, Int> d) { 1 + d; };
requires !requires(dimensionless<percent, Int> d) { d + 1; };
};
static_assert(invalid_dimensionless_operations<int>);
@ -319,36 +654,36 @@ static_assert(50._q_m / 100._q_m == dimensionless<percent>(50));
static_assert(dimensionless<one>(dimensionless<percent>(50)).count() == 0.5);
static_assert(std::is_same_v<decltype(quantity{1}), dimensionless<one, int>>);
static_assert(std::is_same_v<decltype(quantity{1.23}), dimensionless<one, double>>);
// time
////////////////
// alias units
////////////////
static_assert(1_q_h == 3600_q_s);
static_assert(compare<decltype(2_q_l + 2_q_ml), volume<cubic_centimetre, std::int64_t>>);
static_assert(2_q_l + 2_q_ml == 2002_q_cm3);
static_assert(2_q_l + 2_q_ml == 2002_q_ml);
static_assert(2_q_l + 2_q_cm3 == 2002_q_ml);
static_assert(2_q_dm3 + 2_q_cm3 == 2002_q_ml);
template<typename Metre>
concept no_crossdimensional_equality = !requires { 1_q_s == length<Metre, int>(1); };
static_assert(no_crossdimensional_equality<metre>);
//////////////////
// quantity_cast
//////////////////
// length
static_assert(compare<decltype(quantity_cast<scaled_unit<ratio(1), metre>>(2_q_km))::unit, metre>);
static_assert(1_q_km == 1000_q_m);
static_assert(1_q_km + 1_q_m == 1001_q_m);
static_assert(10_q_km / 5_q_km == 2);
static_assert(10_q_km / 2 == 5_q_km);
static_assert(quantity_cast<length<metre, int>>(2_q_km).count() == 2000);
static_assert(quantity_cast<length<kilometre, int>>(2000_q_m).count() == 2);
static_assert(quantity_cast<length<metre, int>>(1.23_q_m).count() == 1);
static_assert(quantity_cast<metre>(2_q_km).count() == 2000);
static_assert(quantity_cast<kilometre>(2000_q_m).count() == 2);
static_assert(quantity_cast<int>(1.23_q_m).count() == 1);
static_assert(quantity_cast<dim_speed, kilometre_per_hour>(2000.0_q_m / 3600.0_q_s).count() == 2);
// speed
static_assert(10_q_m / 5_q_s == 2_q_m_per_s);
static_assert(10 / 5_q_s * 1_q_m == 2_q_m_per_s);
static_assert(1_q_km / 1_q_s == 1000_q_m_per_s);
static_assert(2_q_km_per_h * 2_q_h == 4_q_km);
static_assert(2_q_km / 2_q_km_per_h == 1_q_h);
static_assert(compare<decltype(pow<2>(2_q_m)), decltype(4_q_m2)>);
////////////////
// downcasting
////////////////
#if UNITS_DOWNCAST_MODE == 0

View File

@ -20,8 +20,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "test_tools.h"
#include <units/math.h>
#include <units/physical/si/si.h>
#include <utility>
namespace {