mirror of
https://github.com/mpusz/mp-units.git
synced 2025-06-25 01:01:33 +02:00
Merge branch 'master' into feature/faster-CI
This commit is contained in:
@ -386,7 +386,7 @@ the same kind.
|
||||
|
||||
We can try to do this by ourselves, but it is tough. Probably no one, for sure we are
|
||||
not, is an expert in all the fields of ISO/IEC 80000 applicability.
|
||||
|
||||
|
||||
We need the help of subject matter experts who will help us build those trees for their domains
|
||||
and then verify that everything works as expected.
|
||||
|
||||
@ -401,8 +401,8 @@ Some quantities are more complicated than others. For example, _power_ has:
|
||||
- var (e.g., _reactive power_),
|
||||
- complex quantities expressed in VA (volt-ampere) (e.g., _complex power_).
|
||||
|
||||
How should we model this? Maybe those should be two independent trees of quantities, each having
|
||||
a different unit?
|
||||
How should we model this? Maybe those should be two or three independent trees of quantities, each
|
||||
having its own unit?
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
@ -411,14 +411,50 @@ flowchart TD
|
||||
power --- electromagnetism_power["<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>"]
|
||||
power --- active_power["<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>"]
|
||||
|
||||
apparent_power["<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i><br>[VA]"]
|
||||
nonactive_power["<b>nonactive_power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[VA]"]
|
||||
nonactive_power --- reactive_power["<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]"]
|
||||
|
||||
complex_power["<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i><br>[VA]"]
|
||||
complex_power --- apparent_power["<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i>"]
|
||||
```
|
||||
|
||||
This will mean that we will not be able to add or compare _active power_, _reactive power_, and
|
||||
_apparent power_, which probably makes a lot of sense. However, it also means that the following
|
||||
will fail to compile:
|
||||
|
||||
```cpp
|
||||
quantity apparent = isq::apparent_power(100 * VA);
|
||||
quantity active = isq::active_power(60 * W);
|
||||
quantity<isq::nonactive_power[VA]> q = sqrt(pow<2>(apparent) - pow<2>(active)); // Compile-time error
|
||||
```
|
||||
|
||||
Also the following will not work:
|
||||
|
||||
```cpp
|
||||
quantity active = isq::active_power(60 * W);
|
||||
quantity reactive = isq::reactive_power(40 * var);
|
||||
quantity<isq::apparent_power[VA]> q = sqrt(pow<2>(active) + pow<2>(reactive)); // Compile-time error
|
||||
```
|
||||
|
||||
If we want the above to work maybe we need to implement the tree as follows?
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
power["<b>power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[W]"]
|
||||
power --- mechanical_power["<b>mechanical_power</b><br><i>(scalar_product(force, velocity))</i>"]
|
||||
power --- electromagnetism_power["<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>"]
|
||||
power --- apparent_power["<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i><br>[VA]"]
|
||||
apparent_power --- active_power["<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>"]
|
||||
apparent_power --- nonactive_power["<b>nonactive_power</b><br><i>(sqrt(apparent_power<sup>2</sup> - active_power<sup>2</sup>))</i><br>"]
|
||||
nonactive_power --- reactive_power["<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]"]
|
||||
apparent_power --- complex_power["<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i>"]
|
||||
```
|
||||
|
||||
This will mean that we will not be able to add or compare _active power_ with _apparent power_,
|
||||
which probably makes a lot of sense. Again, ISQ does not provide a direct answer here.
|
||||
However, the above allows direct addition and comparison of _active power_ and _nonactive power_,
|
||||
and also will not complain if someone will try to use watt (W) as a unit of _apparent power_ or
|
||||
_reactive power_.
|
||||
|
||||
Again, ISQ does not provide a direct answer here.
|
||||
|
||||
|
||||
## More base quantities?
|
||||
@ -490,5 +526,5 @@ a daily basis.
|
||||
|
||||
I hope you enjoyed following this series and learned more about the International System
|
||||
of Quantities. Please try it out in your domain and share feedback with us. We always love to
|
||||
hear about the projects in which our library is being used and about use cases it helps
|
||||
hear about the projects in which our library is being used and about use cases it helps
|
||||
address.
|
||||
|
@ -16,19 +16,11 @@ work in practice.
|
||||
--8<-- "example/si_constants.cpp:28:40"
|
||||
```
|
||||
|
||||
As always, we start with the inclusion of all the needed header files. After that, for
|
||||
the simplicity of this example, we
|
||||
[hack the character of quantities](../framework_basics/character_of_a_quantity.md#hacking-the-character)
|
||||
to be able to express vector quantities with simple scalar types.
|
||||
|
||||
```cpp title="si_constants.cpp" linenums="14"
|
||||
--8<-- "example/si_constants.cpp:42:44"
|
||||
```
|
||||
|
||||
As always, we start with the inclusion of all the needed header files.
|
||||
The main part of the example prints all of the SI-defining constants:
|
||||
|
||||
```cpp title="si_constants.cpp" linenums="17"
|
||||
--8<-- "example/si_constants.cpp:45:"
|
||||
```cpp title="si_constants.cpp" linenums="14"
|
||||
--8<-- "example/si_constants.cpp:42:"
|
||||
```
|
||||
|
||||
While analyzing the output of this program (provided below), we can easily notice that a direct
|
||||
|
@ -129,8 +129,8 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
||||
template<typename T, typename Validator, typename Char>
|
||||
struct MP_UNITS_STD_FMT::formatter<validated_type<T, Validator>, Char> : formatter<T, Char> {
|
||||
template<typename FormatContext>
|
||||
auto format(const validated_type<T, Validator>& v, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
auto format(const validated_type<T, Validator>& val, FormatContext& ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
return formatter<T, Char>::format(v.value(), ctx);
|
||||
return formatter<T, Char>::format(val.value(), ctx);
|
||||
}
|
||||
};
|
||||
|
@ -39,10 +39,6 @@ import mp_units;
|
||||
#include <mp-units/systems/si.h>
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
requires mp_units::is_scalar<T>
|
||||
constexpr bool mp_units::is_vector<T> = true;
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace mp_units;
|
||||
|
@ -91,6 +91,7 @@ if(NOT ${projectPrefix}API_FREESTANDING)
|
||||
include/mp-units/bits/fmt.h
|
||||
include/mp-units/bits/requires_hosted.h
|
||||
include/mp-units/ext/format.h
|
||||
include/mp-units/cartesian_vector.h
|
||||
include/mp-units/complex.h
|
||||
include/mp-units/format.h
|
||||
include/mp-units/math.h
|
||||
|
230
src/core/include/mp-units/cartesian_vector.h
Normal file
230
src/core/include/mp-units/cartesian_vector.h
Normal file
@ -0,0 +1,230 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mp-units/bits/requires_hosted.h>
|
||||
//
|
||||
#include <mp-units/bits/module_macros.h>
|
||||
#include <mp-units/framework/customization_points.h>
|
||||
|
||||
#if MP_UNITS_HOSTED
|
||||
#include <mp-units/bits/fmt.h>
|
||||
#endif
|
||||
|
||||
#ifndef MP_UNITS_IN_MODULE_INTERFACE
|
||||
#ifdef MP_UNITS_IMPORT_STD
|
||||
import std;
|
||||
#else
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#if MP_UNITS_HOSTED
|
||||
#include <ostream>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace mp_units {
|
||||
|
||||
MP_UNITS_EXPORT template<typename T = double>
|
||||
class cartesian_vector {
|
||||
public:
|
||||
// public members required to satisfy structural type requirements :-(
|
||||
T _coordinates_[3];
|
||||
using value_type = T;
|
||||
|
||||
cartesian_vector() = default;
|
||||
cartesian_vector(const cartesian_vector&) = default;
|
||||
cartesian_vector(cartesian_vector&&) = default;
|
||||
cartesian_vector& operator=(const cartesian_vector&) = default;
|
||||
cartesian_vector& operator=(cartesian_vector&&) = default;
|
||||
|
||||
template<std::convertible_to<T> Arg1, std::convertible_to<T>... Args>
|
||||
constexpr cartesian_vector(Arg1&& arg1, Args&&... args) :
|
||||
_coordinates_(std::forward<Arg1>(arg1), std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<std::convertible_to<T> U>
|
||||
constexpr cartesian_vector(const cartesian_vector<U>& other) : _coordinates_{other[0], other[1], other[2]}
|
||||
{
|
||||
}
|
||||
|
||||
template<std::convertible_to<T> U>
|
||||
constexpr cartesian_vector(cartesian_vector<U>&& other) :
|
||||
_coordinates_{std::move(other[0]), std::move(other[1]), std::move(other[2])}
|
||||
{
|
||||
}
|
||||
|
||||
template<std::convertible_to<T> U>
|
||||
constexpr cartesian_vector& operator=(const cartesian_vector<U>& other)
|
||||
{
|
||||
_coordinates_[0] = other[0];
|
||||
_coordinates_[1] = other[1];
|
||||
_coordinates_[2] = other[2];
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::convertible_to<T> U>
|
||||
constexpr cartesian_vector& operator=(cartesian_vector<U>&& other)
|
||||
{
|
||||
_coordinates_[0] = std::move(other[0]);
|
||||
_coordinates_[1] = std::move(other[1]);
|
||||
_coordinates_[2] = std::move(other[2]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T magnitude() const
|
||||
requires treat_as_floating_point<T>
|
||||
{
|
||||
return std::hypot(_coordinates_[0], _coordinates_[1], _coordinates_[2]);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr cartesian_vector unit() const
|
||||
requires treat_as_floating_point<T>
|
||||
{
|
||||
return *this / magnitude();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t i) { return _coordinates_[i]; }
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const { return _coordinates_[i]; }
|
||||
|
||||
[[nodiscard]] constexpr cartesian_vector operator+() const { return *this; }
|
||||
[[nodiscard]] constexpr cartesian_vector operator-() const
|
||||
{
|
||||
return {-_coordinates_[0], -_coordinates_[1], -_coordinates_[2]};
|
||||
}
|
||||
|
||||
template<std::same_as<T> U, typename V>
|
||||
requires requires(U u, V v) { u + v; }
|
||||
[[nodiscard]] friend constexpr auto operator+(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||
{
|
||||
return ::mp_units::cartesian_vector{lhs._coordinates_[0] + rhs._coordinates_[0],
|
||||
lhs._coordinates_[1] + rhs._coordinates_[1],
|
||||
lhs._coordinates_[2] + rhs._coordinates_[2]};
|
||||
}
|
||||
|
||||
template<std::same_as<T> U, typename V>
|
||||
requires requires(U u, V v) { u - v; }
|
||||
[[nodiscard]] friend constexpr auto operator-(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||
{
|
||||
return ::mp_units::cartesian_vector{lhs._coordinates_[0] - rhs._coordinates_[0],
|
||||
lhs._coordinates_[1] - rhs._coordinates_[1],
|
||||
lhs._coordinates_[2] - rhs._coordinates_[2]};
|
||||
}
|
||||
|
||||
template<std::same_as<T> U>
|
||||
requires requires(U u, T t) { u* t; }
|
||||
[[nodiscard]] friend constexpr auto operator*(const cartesian_vector<U>& lhs, const T& rhs)
|
||||
{
|
||||
return ::mp_units::cartesian_vector{lhs._coordinates_[0] * rhs, lhs._coordinates_[1] * rhs,
|
||||
lhs._coordinates_[2] * rhs};
|
||||
}
|
||||
|
||||
template<std::same_as<T> U>
|
||||
requires requires(T t, U u) { t* u; }
|
||||
[[nodiscard]] friend constexpr auto operator*(const T& lhs, const cartesian_vector<U>& rhs)
|
||||
{
|
||||
return rhs * lhs;
|
||||
}
|
||||
|
||||
template<std::same_as<T> U>
|
||||
requires requires(U u, T t) { u / t; }
|
||||
[[nodiscard]] friend constexpr auto operator/(const cartesian_vector<U>& lhs, const T& rhs)
|
||||
{
|
||||
return ::mp_units::cartesian_vector{lhs._coordinates_[0] / rhs, lhs._coordinates_[1] / rhs,
|
||||
lhs._coordinates_[2] / rhs};
|
||||
}
|
||||
|
||||
template<std::same_as<T> U, std::equality_comparable_with<U> V>
|
||||
[[nodiscard]] friend constexpr bool operator==(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||
{
|
||||
return lhs._coordinates_[0] == rhs._coordinates_[0] && lhs._coordinates_[1] == rhs._coordinates_[1] &&
|
||||
lhs._coordinates_[2] == rhs._coordinates_[2];
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr T norm(const cartesian_vector& vec)
|
||||
requires treat_as_floating_point<T>
|
||||
{
|
||||
return vec.magnitude();
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr cartesian_vector unit_vector(const cartesian_vector& vec)
|
||||
requires treat_as_floating_point<T>
|
||||
{
|
||||
return vec.unit();
|
||||
}
|
||||
|
||||
template<std::same_as<T> U, typename V>
|
||||
requires requires(U u, V v, decltype(u * v) t) {
|
||||
u* v;
|
||||
t + t;
|
||||
}
|
||||
[[nodiscard]] friend constexpr auto scalar_product(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||
{
|
||||
return lhs._coordinates_[0] * rhs._coordinates_[0] + lhs._coordinates_[1] * rhs._coordinates_[1] +
|
||||
lhs._coordinates_[2] * rhs._coordinates_[2];
|
||||
}
|
||||
|
||||
template<std::same_as<T> U, typename V>
|
||||
requires requires(U u, V v, decltype(u * v) t) {
|
||||
u* v;
|
||||
t - t;
|
||||
}
|
||||
[[nodiscard]] friend constexpr auto vector_product(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
|
||||
{
|
||||
return ::mp_units::cartesian_vector{
|
||||
lhs._coordinates_[1] * rhs._coordinates_[2] - lhs._coordinates_[2] * rhs._coordinates_[1],
|
||||
lhs._coordinates_[2] * rhs._coordinates_[0] - lhs._coordinates_[0] * rhs._coordinates_[2],
|
||||
lhs._coordinates_[0] * rhs._coordinates_[1] - lhs._coordinates_[1] * rhs._coordinates_[0]};
|
||||
}
|
||||
|
||||
#if MP_UNITS_HOSTED
|
||||
friend constexpr std::ostream& operator<<(std::ostream& os, const cartesian_vector& vec)
|
||||
{
|
||||
return os << '[' << vec[0] << ", " << vec[1] << ", " << vec[2] << ']';
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename Arg, typename... Args>
|
||||
requires(sizeof...(Args) <= 2) && requires { typename std::common_type_t<Arg, Args...>; }
|
||||
cartesian_vector(Arg, Args...) -> cartesian_vector<std::common_type_t<Arg, Args...>>;
|
||||
|
||||
template<class T>
|
||||
constexpr bool is_vector<cartesian_vector<T>> = true;
|
||||
|
||||
} // namespace mp_units
|
||||
|
||||
#if MP_UNITS_HOSTED
|
||||
// TODO use parse and use formatter for the underlying type
|
||||
template<typename T, typename Char>
|
||||
struct MP_UNITS_STD_FMT::formatter<mp_units::cartesian_vector<T>, Char> :
|
||||
formatter<std::basic_string_view<Char>, Char> {
|
||||
template<typename FormatContext>
|
||||
auto format(const mp_units::cartesian_vector<T>& vec, FormatContext& ctx) const
|
||||
{
|
||||
return format_to(ctx.out(), "[{}, {}, {}]", vec[0], vec[1], vec[2]);
|
||||
}
|
||||
};
|
||||
#endif
|
@ -28,6 +28,7 @@
|
||||
#include <mp-units/framework.h>
|
||||
|
||||
#if MP_UNITS_HOSTED
|
||||
#include <mp-units/cartesian_vector.h>
|
||||
#include <mp-units/complex.h>
|
||||
#include <mp-units/format.h>
|
||||
#include <mp-units/math.h>
|
||||
|
@ -90,16 +90,16 @@ public:
|
||||
constexpr T* data() noexcept { return data_; }
|
||||
constexpr const T* data() const noexcept { return data_; }
|
||||
|
||||
constexpr reference push_back(const T& v)
|
||||
constexpr reference push_back(const T& val)
|
||||
requires std::constructible_from<T, const T&>
|
||||
{
|
||||
return emplace_back(v);
|
||||
return emplace_back(val);
|
||||
}
|
||||
|
||||
constexpr reference push_back(T&& v)
|
||||
constexpr reference push_back(T&& val)
|
||||
requires std::constructible_from<T, T&&>
|
||||
{
|
||||
return emplace_back(std::forward<T&&>(v));
|
||||
return emplace_back(std::forward<T&&>(val));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
|
@ -174,21 +174,21 @@ public:
|
||||
|
||||
template<typename FwdValue, Reference R2>
|
||||
requires detail::SameValueAs<R2{}, R, std::remove_cvref_t<FwdValue>, Rep>
|
||||
constexpr quantity(FwdValue&& v, R2) : numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(v))
|
||||
constexpr quantity(FwdValue&& val, R2) : numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(val))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename FwdValue, Reference R2, typename Value = std::remove_cvref_t<FwdValue>>
|
||||
requires(!detail::SameValueAs<R2{}, R, Value, Rep>) &&
|
||||
detail::QuantityConvertibleTo<quantity<R2{}, Value>, quantity>
|
||||
constexpr quantity(FwdValue&& v, R2) : quantity(quantity<R2{}, Value>{std::forward<FwdValue>(v), R2{}})
|
||||
constexpr quantity(FwdValue&& val, R2) : quantity(quantity<R2{}, Value>{std::forward<FwdValue>(val), R2{}})
|
||||
{
|
||||
}
|
||||
|
||||
template<detail::ValuePreservingTo<Rep> FwdValue>
|
||||
requires(unit == ::mp_units::one)
|
||||
constexpr explicit(false) quantity(FwdValue&& v) :
|
||||
numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(v))
|
||||
constexpr explicit(false) quantity(FwdValue&& val) :
|
||||
numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(val))
|
||||
{
|
||||
}
|
||||
|
||||
@ -214,9 +214,9 @@ public:
|
||||
|
||||
template<detail::ValuePreservingTo<Rep> FwdValue>
|
||||
requires(unit == ::mp_units::one)
|
||||
constexpr quantity& operator=(FwdValue&& v)
|
||||
constexpr quantity& operator=(FwdValue&& val)
|
||||
{
|
||||
numerical_value_is_an_implementation_detail_ = std::forward<FwdValue>(v);
|
||||
numerical_value_is_an_implementation_detail_ = std::forward<FwdValue>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -422,11 +422,11 @@ public:
|
||||
requires(!Quantity<Value>) && requires(rep a, Value b) {
|
||||
{ a *= b } -> std::same_as<rep&>;
|
||||
}
|
||||
friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& v)
|
||||
friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& val)
|
||||
{
|
||||
// TODO use *= when compiler bug is resolved:
|
||||
// https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445
|
||||
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ * v;
|
||||
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ * val;
|
||||
return std::forward<Q>(lhs);
|
||||
}
|
||||
|
||||
@ -444,12 +444,12 @@ public:
|
||||
requires(!Quantity<Value>) && requires(rep a, Value b) {
|
||||
{ a /= b } -> std::same_as<rep&>;
|
||||
}
|
||||
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& v)
|
||||
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& val)
|
||||
{
|
||||
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
|
||||
MP_UNITS_EXPECTS_DEBUG(val != quantity_values<Value>::zero());
|
||||
// TODO use /= when compiler bug is resolved:
|
||||
// https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445
|
||||
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ / v;
|
||||
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ / val;
|
||||
return std::forward<Q>(lhs);
|
||||
}
|
||||
|
||||
@ -551,17 +551,17 @@ public:
|
||||
template<std::derived_from<quantity> Q, typename Value>
|
||||
requires(!Quantity<Value>) &&
|
||||
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::multiplies<>, Rep, const Value&>
|
||||
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Q& q, const Value& v)
|
||||
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Q& q, const Value& val)
|
||||
{
|
||||
return ::mp_units::quantity{q.numerical_value_ref_in(unit) * v, R};
|
||||
return ::mp_units::quantity{q.numerical_value_ref_in(unit) * val, R};
|
||||
}
|
||||
|
||||
template<typename Value, std::derived_from<quantity> Q>
|
||||
requires(!Quantity<Value>) &&
|
||||
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::multiplies<>, const Value&, Rep>
|
||||
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Value& v, const Q& q)
|
||||
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Value& val, const Q& q)
|
||||
{
|
||||
return ::mp_units::quantity{v * q.numerical_value_ref_in(unit), R};
|
||||
return ::mp_units::quantity{val * q.numerical_value_ref_in(unit), R};
|
||||
}
|
||||
|
||||
template<std::derived_from<quantity> Q, auto R2, typename Rep2>
|
||||
@ -575,18 +575,18 @@ public:
|
||||
template<std::derived_from<quantity> Q, typename Value>
|
||||
requires(!Quantity<Value>) &&
|
||||
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::divides<>, Rep, const Value&>
|
||||
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator/(const Q& q, const Value& v)
|
||||
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator/(const Q& q, const Value& val)
|
||||
{
|
||||
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
|
||||
return ::mp_units::quantity{q.numerical_value_ref_in(unit) / v, R};
|
||||
MP_UNITS_EXPECTS_DEBUG(val != quantity_values<Value>::zero());
|
||||
return ::mp_units::quantity{q.numerical_value_ref_in(unit) / val, R};
|
||||
}
|
||||
|
||||
template<typename Value, std::derived_from<quantity> Q>
|
||||
requires(!Quantity<Value>) &&
|
||||
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::divides<>, const Value&, Rep>
|
||||
[[nodiscard]] friend constexpr QuantityOf<inverse(quantity_spec)> auto operator/(const Value& v, const Q& q)
|
||||
[[nodiscard]] friend constexpr QuantityOf<inverse(quantity_spec)> auto operator/(const Value& val, const Q& q)
|
||||
{
|
||||
return ::mp_units::quantity{v / q.numerical_value_ref_in(unit), ::mp_units::one / R};
|
||||
return ::mp_units::quantity{val / q.numerical_value_ref_in(unit), ::mp_units::one / R};
|
||||
}
|
||||
|
||||
template<std::derived_from<quantity> Q, auto R2, typename Rep2>
|
||||
|
@ -118,12 +118,11 @@ concept ComplexRepresentation = Complex<T> && WeaklyRegular<T> && requires(T a,
|
||||
{ a - b } -> Complex;
|
||||
{ a* b } -> Complex;
|
||||
{ a / b } -> Complex;
|
||||
// TBD
|
||||
// { re(a) } -> Scalar;
|
||||
// { im(a) } -> Scalar;
|
||||
// { mod(a) } -> Scalar;
|
||||
// { arg(a) } -> Scalar;
|
||||
// { conj(a) } -> Complex;
|
||||
{ real(a) } -> Scalar;
|
||||
{ imag(a) } -> Scalar;
|
||||
{ abs(a) } -> Scalar;
|
||||
{ arg(a) } -> Scalar;
|
||||
{ conj(a) } -> Complex;
|
||||
};
|
||||
|
||||
// TODO how to check for a complex(Scalar, Scalar) -> Complex?
|
||||
|
@ -68,8 +68,8 @@ void to_stream_impl(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep
|
||||
}
|
||||
|
||||
template<typename CharT, class Traits, typename T>
|
||||
std::basic_ostream<CharT, Traits>& to_stream(std::basic_ostream<CharT, Traits>& os, const T& v)
|
||||
requires requires { detail::to_stream_impl(os, v); }
|
||||
std::basic_ostream<CharT, Traits>& to_stream(std::basic_ostream<CharT, Traits>& os, const T& val)
|
||||
requires requires { detail::to_stream_impl(os, val); }
|
||||
{
|
||||
if (os.width()) {
|
||||
// std::setw() applies to the whole output so it has to be first put into std::string
|
||||
@ -77,11 +77,11 @@ std::basic_ostream<CharT, Traits>& to_stream(std::basic_ostream<CharT, Traits>&
|
||||
oss.flags(os.flags());
|
||||
oss.imbue(os.getloc());
|
||||
oss.precision(os.precision());
|
||||
detail::to_stream_impl(oss, v);
|
||||
detail::to_stream_impl(oss, val);
|
||||
return os << std::move(oss).str();
|
||||
}
|
||||
|
||||
detail::to_stream_impl(os, v);
|
||||
detail::to_stream_impl(os, val);
|
||||
return os;
|
||||
}
|
||||
|
||||
@ -93,10 +93,10 @@ constexpr bool is_mp_units_stream = requires(OStream os, T v) { detail::to_strea
|
||||
MP_UNITS_EXPORT_BEGIN
|
||||
|
||||
template<typename CharT, typename Traits, typename T>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const T& v)
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const T& val)
|
||||
requires detail::is_mp_units_stream<std::basic_ostream<CharT, Traits>, T>
|
||||
{
|
||||
return detail::to_stream(os, v);
|
||||
return detail::to_stream(os, val);
|
||||
}
|
||||
|
||||
MP_UNITS_EXPORT_END
|
||||
|
@ -83,10 +83,10 @@ struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
|
||||
return q.count();
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr T from_numerical_value(const rep& v) noexcept(
|
||||
[[nodiscard]] static constexpr T from_numerical_value(const rep& val) noexcept(
|
||||
std::is_nothrow_copy_constructible_v<rep>)
|
||||
{
|
||||
return T(v);
|
||||
return T(val);
|
||||
}
|
||||
};
|
||||
|
||||
@ -113,10 +113,10 @@ struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::durati
|
||||
return tp.time_since_epoch().count();
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr T from_numerical_value(const rep& v) noexcept(
|
||||
[[nodiscard]] static constexpr T from_numerical_value(const rep& val) noexcept(
|
||||
std::is_nothrow_copy_constructible_v<rep>)
|
||||
{
|
||||
return T(std::chrono::duration<Rep, Period>(v));
|
||||
return T(std::chrono::duration<Rep, Period>(val));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -24,13 +24,14 @@ find_package(Catch2 3 REQUIRED)
|
||||
|
||||
add_executable(
|
||||
unit_tests_runtime
|
||||
atomic_test.cpp
|
||||
cartesian_vector_test.cpp
|
||||
distribution_test.cpp
|
||||
fixed_string_test.cpp
|
||||
fmt_test.cpp
|
||||
math_test.cpp
|
||||
atomic_test.cpp
|
||||
truncation_test.cpp
|
||||
quantity_test.cpp
|
||||
truncation_test.cpp
|
||||
)
|
||||
if(${projectPrefix}BUILD_CXX_MODULES)
|
||||
target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES)
|
||||
|
381
test/runtime/cartesian_vector_test.cpp
Normal file
381
test/runtime/cartesian_vector_test.cpp
Normal file
@ -0,0 +1,381 @@
|
||||
// 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 "almost_equals.h"
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <mp-units/compat_macros.h>
|
||||
#include <mp-units/ext/format.h>
|
||||
#ifdef MP_UNITS_IMPORT_STD
|
||||
import std;
|
||||
#else
|
||||
#include <sstream>
|
||||
#endif
|
||||
#ifdef MP_UNITS_MODULES
|
||||
import mp_units;
|
||||
#else
|
||||
#include <mp-units/cartesian_vector.h>
|
||||
#endif
|
||||
|
||||
using namespace mp_units;
|
||||
using namespace Catch::Matchers;
|
||||
|
||||
TEST_CASE("cartesian_vector operations", "[vector]")
|
||||
{
|
||||
SECTION("cartesian_vector initialization and access")
|
||||
{
|
||||
SECTION("one argument")
|
||||
{
|
||||
cartesian_vector v{1.0};
|
||||
REQUIRE(v[0] == 1.0);
|
||||
REQUIRE(v[1] == 0);
|
||||
REQUIRE(v[2] == 0);
|
||||
}
|
||||
|
||||
SECTION("two arguments")
|
||||
{
|
||||
cartesian_vector v{1.0, 2.0};
|
||||
REQUIRE(v[0] == 1.0);
|
||||
REQUIRE(v[1] == 2.0);
|
||||
REQUIRE(v[2] == 0);
|
||||
}
|
||||
|
||||
SECTION("all arguments")
|
||||
{
|
||||
cartesian_vector v{1.0, 2.0, 3.0};
|
||||
REQUIRE(v[0] == 1.0);
|
||||
REQUIRE(v[1] == 2.0);
|
||||
REQUIRE(v[2] == 3.0);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("convertibility")
|
||||
{
|
||||
cartesian_vector v1{1, 2, 3};
|
||||
|
||||
SECTION("construction")
|
||||
{
|
||||
cartesian_vector v2 = v1;
|
||||
REQUIRE(v2[0] == 1.0);
|
||||
REQUIRE(v2[1] == 2.0);
|
||||
REQUIRE(v2[2] == 3.0);
|
||||
}
|
||||
|
||||
SECTION("assignment")
|
||||
{
|
||||
cartesian_vector v2{3.0, 2.0, 1.0};
|
||||
v2 = v1;
|
||||
REQUIRE(v2[0] == 1.0);
|
||||
REQUIRE(v2[1] == 2.0);
|
||||
REQUIRE(v2[2] == 3.0);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("cartesian_vector addition")
|
||||
{
|
||||
SECTION("double + double")
|
||||
{
|
||||
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||
cartesian_vector result = v1 + v2;
|
||||
REQUIRE(result[0] == 5.0);
|
||||
REQUIRE(result[1] == 7.0);
|
||||
REQUIRE(result[2] == 9.0);
|
||||
}
|
||||
|
||||
SECTION("double + int")
|
||||
{
|
||||
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||
cartesian_vector v2{4, 5, 6};
|
||||
cartesian_vector result = v1 + v2;
|
||||
REQUIRE(result[0] == 5.0);
|
||||
REQUIRE(result[1] == 7.0);
|
||||
REQUIRE(result[2] == 9.0);
|
||||
}
|
||||
|
||||
SECTION("int + double")
|
||||
{
|
||||
cartesian_vector v1{1, 2, 3};
|
||||
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||
cartesian_vector result = v1 + v2;
|
||||
REQUIRE(result[0] == 5.0);
|
||||
REQUIRE(result[1] == 7.0);
|
||||
REQUIRE(result[2] == 9.0);
|
||||
}
|
||||
|
||||
SECTION("int + int")
|
||||
{
|
||||
cartesian_vector v1{1, 2, 3};
|
||||
cartesian_vector v2{4, 5, 6};
|
||||
cartesian_vector result = v1 + v2;
|
||||
REQUIRE(result[0] == 5);
|
||||
REQUIRE(result[1] == 7);
|
||||
REQUIRE(result[2] == 9);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("cartesian_vector subtraction")
|
||||
{
|
||||
SECTION("double - double")
|
||||
{
|
||||
cartesian_vector v1{4.0, 5.0, 6.0};
|
||||
cartesian_vector v2{1.0, 2.0, 3.0};
|
||||
cartesian_vector result = v1 - v2;
|
||||
REQUIRE(result[0] == 3.0);
|
||||
REQUIRE(result[1] == 3.0);
|
||||
REQUIRE(result[2] == 3.0);
|
||||
}
|
||||
|
||||
SECTION("double - int")
|
||||
{
|
||||
cartesian_vector v1{4.0, 5.0, 6.0};
|
||||
cartesian_vector v2{1, 2, 3};
|
||||
cartesian_vector result = v1 - v2;
|
||||
REQUIRE(result[0] == 3.0);
|
||||
REQUIRE(result[1] == 3.0);
|
||||
REQUIRE(result[2] == 3.0);
|
||||
}
|
||||
|
||||
SECTION("int - double")
|
||||
{
|
||||
cartesian_vector v1{4, 5, 6};
|
||||
cartesian_vector v2{1.0, 2.0, 3.0};
|
||||
cartesian_vector result = v1 - v2;
|
||||
REQUIRE(result[0] == 3.0);
|
||||
REQUIRE(result[1] == 3.0);
|
||||
REQUIRE(result[2] == 3.0);
|
||||
}
|
||||
|
||||
SECTION("int - int")
|
||||
{
|
||||
cartesian_vector v1{4, 5, 6};
|
||||
cartesian_vector v2{1, 2, 3};
|
||||
cartesian_vector result = v1 - v2;
|
||||
REQUIRE(result[0] == 3);
|
||||
REQUIRE(result[1] == 3);
|
||||
REQUIRE(result[2] == 3);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("cartesian_vector scalar multiplication")
|
||||
{
|
||||
SECTION("double * double")
|
||||
{
|
||||
cartesian_vector v{1.0, 2.0, 3.0};
|
||||
cartesian_vector result = v * 2.0;
|
||||
REQUIRE(result[0] == 2.0);
|
||||
REQUIRE(result[1] == 4.0);
|
||||
REQUIRE(result[2] == 6.0);
|
||||
}
|
||||
|
||||
SECTION("double * int")
|
||||
{
|
||||
cartesian_vector v{1.0, 2.0, 3.0};
|
||||
cartesian_vector result = v * 2;
|
||||
REQUIRE(result[0] == 2.0);
|
||||
REQUIRE(result[1] == 4.0);
|
||||
REQUIRE(result[2] == 6.0);
|
||||
}
|
||||
|
||||
SECTION("int * double")
|
||||
{
|
||||
cartesian_vector v{1, 2, 3};
|
||||
cartesian_vector result = v * 2.0;
|
||||
REQUIRE(result[0] == 2.0);
|
||||
REQUIRE(result[1] == 4.0);
|
||||
REQUIRE(result[2] == 6.0);
|
||||
}
|
||||
|
||||
SECTION("int * int")
|
||||
{
|
||||
cartesian_vector v{1, 2, 3};
|
||||
cartesian_vector result = v * 2;
|
||||
REQUIRE(result[0] == 2);
|
||||
REQUIRE(result[1] == 4);
|
||||
REQUIRE(result[2] == 6);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("cartesian_vector scalar division")
|
||||
{
|
||||
SECTION("double / double")
|
||||
{
|
||||
cartesian_vector v{2.0, 4.0, 6.0};
|
||||
cartesian_vector result = v / 2.0;
|
||||
REQUIRE(result[0] == 1.0);
|
||||
REQUIRE(result[1] == 2.0);
|
||||
REQUIRE(result[2] == 3.0);
|
||||
}
|
||||
|
||||
SECTION("double / int")
|
||||
{
|
||||
cartesian_vector v{2.0, 4.0, 6.0};
|
||||
cartesian_vector result = v / 2;
|
||||
REQUIRE(result[0] == 1.0);
|
||||
REQUIRE(result[1] == 2.0);
|
||||
REQUIRE(result[2] == 3.0);
|
||||
}
|
||||
|
||||
SECTION("int / double")
|
||||
{
|
||||
cartesian_vector v{2, 4, 6};
|
||||
cartesian_vector result = v / 2.0;
|
||||
REQUIRE(result[0] == 1.0);
|
||||
REQUIRE(result[1] == 2.0);
|
||||
REQUIRE(result[2] == 3.0);
|
||||
}
|
||||
|
||||
SECTION("int / int")
|
||||
{
|
||||
cartesian_vector v{2, 4, 6};
|
||||
cartesian_vector result = v / 2;
|
||||
REQUIRE(result[0] == 1);
|
||||
REQUIRE(result[1] == 2);
|
||||
REQUIRE(result[2] == 3);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("cartesian_vector magnitude")
|
||||
{
|
||||
cartesian_vector v1{3.0, 4.0, 0.0};
|
||||
cartesian_vector v2{2.0, 3.0, 6.0};
|
||||
REQUIRE(v1.magnitude() == 5.0);
|
||||
REQUIRE(v2.magnitude() == 7.0);
|
||||
}
|
||||
|
||||
SECTION("cartesian_vector unit vector")
|
||||
{
|
||||
cartesian_vector v{3.0, 4.0, 0.0};
|
||||
cartesian_vector unit_v = v.unit();
|
||||
REQUIRE_THAT(unit_v.magnitude(), WithinULP(1.0, 1));
|
||||
}
|
||||
|
||||
SECTION("cartesian_vector equality")
|
||||
{
|
||||
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||
cartesian_vector v2{1, 2, 3};
|
||||
cartesian_vector v3{1.1, 2.0, 3.0};
|
||||
cartesian_vector v4{1.0, 2.1, 3.0};
|
||||
cartesian_vector v5{1.0, 2.0, 3.1};
|
||||
REQUIRE(v1 == v2);
|
||||
REQUIRE(v1 != v3);
|
||||
REQUIRE(v1 != v4);
|
||||
REQUIRE(v1 != v5);
|
||||
}
|
||||
|
||||
SECTION("cartesian_vector scalar product")
|
||||
{
|
||||
SECTION("double * double")
|
||||
{
|
||||
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||
REQUIRE(scalar_product(v1, v2) == 32.0);
|
||||
}
|
||||
|
||||
SECTION("double * int")
|
||||
{
|
||||
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||
cartesian_vector v2{4, 5, 6};
|
||||
REQUIRE(scalar_product(v1, v2) == 32.0);
|
||||
}
|
||||
|
||||
SECTION("int * double")
|
||||
{
|
||||
cartesian_vector v1{1, 2, 3};
|
||||
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||
REQUIRE(scalar_product(v1, v2) == 32.0);
|
||||
}
|
||||
|
||||
SECTION("int * int")
|
||||
{
|
||||
cartesian_vector v1{1, 2, 3};
|
||||
cartesian_vector v2{4, 5, 6};
|
||||
REQUIRE(scalar_product(v1, v2) == 32);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("cartesian_vector vector product")
|
||||
{
|
||||
SECTION("double * double")
|
||||
{
|
||||
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||
cartesian_vector result = vector_product(v1, v2);
|
||||
REQUIRE(result[0] == -3.0);
|
||||
REQUIRE(result[1] == 6.0);
|
||||
REQUIRE(result[2] == -3.0);
|
||||
}
|
||||
|
||||
SECTION("double * int")
|
||||
{
|
||||
cartesian_vector v1{1.0, 2.0, 3.0};
|
||||
cartesian_vector v2{4, 5, 6};
|
||||
cartesian_vector result = vector_product(v1, v2);
|
||||
REQUIRE(result[0] == -3.0);
|
||||
REQUIRE(result[1] == 6.0);
|
||||
REQUIRE(result[2] == -3.0);
|
||||
}
|
||||
|
||||
SECTION("int * double")
|
||||
{
|
||||
cartesian_vector v1{1, 2, 3};
|
||||
cartesian_vector v2{4.0, 5.0, 6.0};
|
||||
cartesian_vector result = vector_product(v1, v2);
|
||||
REQUIRE(result[0] == -3.0);
|
||||
REQUIRE(result[1] == 6.0);
|
||||
REQUIRE(result[2] == -3.0);
|
||||
}
|
||||
|
||||
SECTION("int * int")
|
||||
{
|
||||
cartesian_vector v1{1, 2, 3};
|
||||
cartesian_vector v2{4, 5, 6};
|
||||
cartesian_vector result = vector_product(v1, v2);
|
||||
REQUIRE(result[0] == -3);
|
||||
REQUIRE(result[1] == 6);
|
||||
REQUIRE(result[2] == -3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("cartesian_vector text output", "[vector][fmt][ostream]")
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
SECTION("integral representation")
|
||||
{
|
||||
cartesian_vector v{1, 2, 3};
|
||||
os << v;
|
||||
|
||||
SECTION("iostream") { CHECK(os.str() == "[1, 2, 3]"); }
|
||||
SECTION("fmt with default format {}") { CHECK(MP_UNITS_STD_FMT::format("{}", v) == os.str()); }
|
||||
}
|
||||
|
||||
SECTION("floating-point representation")
|
||||
{
|
||||
cartesian_vector v{1.2, 2.3, 3.4};
|
||||
os << v;
|
||||
|
||||
SECTION("iostream") { CHECK(os.str() == "[1.2, 2.3, 3.4]"); }
|
||||
SECTION("fmt with default format {}") { CHECK(MP_UNITS_STD_FMT::format("{}", v) == os.str()); }
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -38,19 +38,22 @@ import mp_units;
|
||||
|
||||
using namespace mp_units;
|
||||
|
||||
TEST_CASE("fixed_string::at", "[fixed_string]")
|
||||
TEST_CASE("fixed_string operations", "[fixed_string]")
|
||||
{
|
||||
basic_fixed_string txt = "abc";
|
||||
SECTION("in range")
|
||||
SECTION("fixed_string::at")
|
||||
{
|
||||
CHECK(txt.at(0) == 'a');
|
||||
CHECK(txt.at(1) == 'b');
|
||||
CHECK(txt.at(2) == 'c');
|
||||
}
|
||||
SECTION("out of range")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(txt.at(3), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
|
||||
REQUIRE_THROWS_MATCHES(txt.at(1024), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
|
||||
basic_fixed_string txt = "abc";
|
||||
SECTION("in range")
|
||||
{
|
||||
CHECK(txt.at(0) == 'a');
|
||||
CHECK(txt.at(1) == 'b');
|
||||
CHECK(txt.at(2) == 'c');
|
||||
}
|
||||
SECTION("out of range")
|
||||
{
|
||||
REQUIRE_THROWS_MATCHES(txt.at(3), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
|
||||
REQUIRE_THROWS_MATCHES(txt.at(1024), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -43,486 +43,488 @@ using namespace mp_units::si::unit_symbols;
|
||||
|
||||
// classical
|
||||
|
||||
TEST_CASE("'pow<N>()' on quantity changes the value and the dimension accordingly", "[math][pow]")
|
||||
TEST_CASE("math operations", "[math]")
|
||||
{
|
||||
SECTION("'pow<0>(q)' returns '1'") { CHECK(pow<0>(2 * isq::length[m]) == 1 * one); }
|
||||
|
||||
SECTION("'pow<1>(q)' returns 'q'") { CHECK(pow<1>(2 * isq::length[m]) == 2 * isq::length[m]); }
|
||||
|
||||
SECTION("'pow<2>(q)' squares both the value and a dimension")
|
||||
SECTION("'pow<N>()' on quantity changes the value and the dimension accordingly")
|
||||
{
|
||||
CHECK(pow<2>(2 * isq::length[m]) == 4 * isq::area[m2]);
|
||||
SECTION("'pow<0>(q)' returns '1'") { CHECK(pow<0>(2 * isq::length[m]) == 1 * one); }
|
||||
|
||||
SECTION("'pow<1>(q)' returns 'q'") { CHECK(pow<1>(2 * isq::length[m]) == 2 * isq::length[m]); }
|
||||
|
||||
SECTION("'pow<2>(q)' squares both the value and a dimension")
|
||||
{
|
||||
CHECK(pow<2>(2 * isq::length[m]) == 4 * isq::area[m2]);
|
||||
}
|
||||
|
||||
SECTION("'pow<3>(q)' cubes both the value and a dimension")
|
||||
{
|
||||
CHECK(pow<3>(2 * isq::length[m]) == 8 * isq::volume[m3]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("'pow<3>(q)' cubes both the value and a dimension")
|
||||
SECTION("'sqrt()' on quantity changes the value and the dimension accordingly")
|
||||
{
|
||||
CHECK(pow<3>(2 * isq::length[m]) == 8 * isq::volume[m3]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("'sqrt()' on quantity changes the value and the dimension accordingly", "[math][sqrt]")
|
||||
{
|
||||
REQUIRE(sqrt(4 * isq::area[m2]) == 2 * isq::length[m]);
|
||||
}
|
||||
|
||||
TEST_CASE("'cbrt()' on quantity changes the value and the dimension accordingly", "[math][cbrt]")
|
||||
{
|
||||
REQUIRE(cbrt(8 * isq::volume[m3]) == 2 * isq::length[m]);
|
||||
}
|
||||
|
||||
TEST_CASE("'fma()' on quantity changes the value and the dimension accordingly", "[math][fma]")
|
||||
{
|
||||
REQUIRE(fma(1.0 * isq::length[m], 2.0 * one, 2.0 * isq::length[m]) == 4.0 * isq::length[m]);
|
||||
REQUIRE(fma(isq::speed(10.0 * m / s), isq::time(2.0 * s), isq::height(42.0 * m)) == isq::length(62.0 * m));
|
||||
}
|
||||
|
||||
TEST_CASE("fmod functions", "[math][fmod]")
|
||||
{
|
||||
SECTION("fmod should work on the same quantities")
|
||||
{
|
||||
REQUIRE(fmod(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
|
||||
REQUIRE(fmod(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
|
||||
REQUIRE(fmod(3 * isq::length[km], 2 * isq::length[km]) == 1 * isq::length[km]);
|
||||
REQUIRE(fmod(4 * isq::length[km], 2.5f * isq::length[km]) == 1.5 * isq::length[km]);
|
||||
}
|
||||
SECTION("fmod should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE(fmod(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
|
||||
REQUIRE(fmod(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
|
||||
REQUIRE(fmod(3. * isq::length[km], 2000. * isq::length[m]) == 1000 * isq::length[m]);
|
||||
REQUIRE(fmod(4 * isq::length[km], 2500 * isq::length[m]) == 1500 * isq::length[m]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("remainder functions", "[math][remainder]")
|
||||
{
|
||||
SECTION("remainder should work on the same quantities")
|
||||
{
|
||||
REQUIRE(remainder(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
|
||||
REQUIRE(remainder(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
|
||||
REQUIRE(remainder(3 * isq::length[km], 2 * isq::length[km]) == -1 * isq::length[km]);
|
||||
REQUIRE(remainder(4 * isq::length[km], 2.75f * isq::length[km]) == 1.25 * isq::length[km]);
|
||||
}
|
||||
SECTION("remainder should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE(remainder(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
|
||||
REQUIRE(remainder(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
|
||||
REQUIRE(remainder(3. * isq::length[km], 2000. * isq::length[m]) == -1000 * isq::length[m]);
|
||||
REQUIRE(remainder(4 * isq::length[km], 2750 * isq::length[m]) == 1250 * isq::length[m]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("'isfinite()' accepts dimensioned arguments", "[math][isfinite]") { REQUIRE(isfinite(4.0 * isq::length[m])); }
|
||||
|
||||
TEST_CASE("'isinf()' accepts dimensioned arguments", "[math][isinf]") { REQUIRE(!isinf(4.0 * isq::length[m])); }
|
||||
|
||||
TEST_CASE("'isnan()' accepts dimensioned arguments", "[math][isnan]") { REQUIRE(!isnan(4.0 * isq::length[m])); }
|
||||
|
||||
|
||||
TEST_CASE("'pow<Num, Den>()' on quantity changes the value and the dimension accordingly", "[math][pow]")
|
||||
{
|
||||
REQUIRE(pow<1, 4>(16 * isq::area[m2]) == sqrt(4 * isq::length[m]));
|
||||
}
|
||||
|
||||
// TODO add tests for exp()
|
||||
|
||||
TEST_CASE("absolute functions on quantity returns the absolute value", "[math][abs][fabs]")
|
||||
{
|
||||
SECTION("'abs()' on a negative quantity returns the abs")
|
||||
{
|
||||
SECTION("integral representation") { REQUIRE(abs(-1 * isq::length[m]) == 1 * isq::length[m]); }
|
||||
|
||||
SECTION("floating-point representation") { REQUIRE(abs(-1. * isq::length[m]) == 1 * isq::length[m]); }
|
||||
}
|
||||
|
||||
SECTION("'abs()' on a positive quantity returns the abs")
|
||||
{
|
||||
SECTION("integral representation") { REQUIRE(abs(1 * isq::length[m]) == 1 * isq::length[m]); }
|
||||
|
||||
SECTION("floating-point representation") { REQUIRE(abs(1. * isq::length[m]) == 1 * isq::length[m]); }
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("numeric_limits functions", "[limits]")
|
||||
{
|
||||
SECTION("'epsilon' works as expected using default floating type")
|
||||
{
|
||||
REQUIRE(epsilon<double>(isq::length[m]).numerical_value_in(m) ==
|
||||
std::numeric_limits<decltype(1. * isq::length[m])::rep>::epsilon());
|
||||
}
|
||||
SECTION("'epsilon' works as expected using integers")
|
||||
{
|
||||
REQUIRE(epsilon<int>(isq::length[m]).numerical_value_in(m) ==
|
||||
std::numeric_limits<decltype(1 * isq::length[m])::rep>::epsilon());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("floor functions", "[floor]")
|
||||
{
|
||||
SECTION("floor 1 second with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1000 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1001 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1999 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1999 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -1000 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -999 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-999 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1.3 seconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1.3 * isq::time[s]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -1.3 seconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-1.3 * isq::time[s]) == -2 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1001. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1999. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1999. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -1000. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -999. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-999. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
|
||||
// TODO Add tests for `N`, `kN` and `kg * m / s2` i `kg * km / s2`
|
||||
}
|
||||
|
||||
TEST_CASE("ceil functions", "[ceil]")
|
||||
{
|
||||
SECTION("ceil 1 second with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1000 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1001 milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1001 * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1999 milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -1000 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -999 milliseconds with target unit second should be 0 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-999 * isq::time[ms]) == 0 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1.3 seconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1.3 * isq::time[s]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -1.3 seconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-1.3 * isq::time[s]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1001. milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1001. * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1999. milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -1000. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -999. milliseconds with target unit second should be 0 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-999. * isq::time[ms]) == 0 * isq::time[s]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("round functions", "[round]")
|
||||
{
|
||||
SECTION("round 1 second with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1000 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1001 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1499 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1499 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1500 milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(1500 * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1999 milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1000 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1001 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1001 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1499 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1499 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1500 milliseconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1500 * isq::time[ms]) == -2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1999 milliseconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1999 * isq::time[ms]) == -2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1000. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1000. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1001. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1499. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1499. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1500. milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(1500. * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1999. milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1000. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1001. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1001. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1499. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1499. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1500. milliseconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1500. * isq::time[ms]) == -2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1999. milliseconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1999. * isq::time[ms]) == -2 * isq::time[s]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("hypot functions", "[hypot]")
|
||||
{
|
||||
SECTION("hypot should work on the same quantities")
|
||||
{
|
||||
REQUIRE(hypot(3. * isq::length[km], 4. * isq::length[km]) == 5. * isq::length[km]);
|
||||
REQUIRE(hypot(2. * isq::length[km], 3. * isq::length[km], 6. * isq::length[km]) == 7. * isq::length[km]);
|
||||
}
|
||||
SECTION("hypot should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE(hypot(3. * isq::length[km], 4000. * isq::length[m]) == 5. * isq::length[km]);
|
||||
REQUIRE(hypot(2. * isq::length[km], 3000. * isq::length[m], 6. * isq::length[km]) == 7. * isq::length[km]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("SI trigonometric functions", "[trig][si]")
|
||||
{
|
||||
SECTION("sin")
|
||||
{
|
||||
REQUIRE_THAT(si::sin(0 * deg), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(si::sin(90 * deg), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(si::sin(180 * deg), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(si::sin(270 * deg), AlmostEquals(-1. * one));
|
||||
}
|
||||
|
||||
SECTION("cos")
|
||||
{
|
||||
REQUIRE_THAT(si::cos(0 * deg), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(si::cos(90 * deg), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(si::cos(180 * deg), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(si::cos(270 * deg), AlmostEquals(0. * one));
|
||||
}
|
||||
|
||||
SECTION("tan")
|
||||
{
|
||||
REQUIRE_THAT(si::tan(0 * deg), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(si::tan(45. * deg), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(si::tan(135. * deg), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(si::tan(180. * deg), AlmostEquals(0. * one));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("SI inverse trigonometric functions", "[inv trig][si]")
|
||||
{
|
||||
SECTION("asin")
|
||||
{
|
||||
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
|
||||
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
|
||||
}
|
||||
|
||||
SECTION("acos")
|
||||
{
|
||||
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
|
||||
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
|
||||
}
|
||||
|
||||
SECTION("atan")
|
||||
{
|
||||
REQUIRE_THAT(si::atan(-1 * one), AlmostEquals(-45. * deg));
|
||||
REQUIRE_THAT(si::atan(0 * one), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::atan(1 * one), AlmostEquals(45. * deg));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("SI atan2 functions", "[atan2][si]")
|
||||
{
|
||||
SECTION("atan2 should work on the same quantities")
|
||||
{
|
||||
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * deg));
|
||||
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * deg));
|
||||
}
|
||||
SECTION("atan2 should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * deg));
|
||||
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * deg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Angle trigonometric functions", "[trig][angle]")
|
||||
{
|
||||
using namespace mp_units::angular;
|
||||
using namespace mp_units::angular::unit_symbols;
|
||||
using mp_units::angular::unit_symbols::deg;
|
||||
|
||||
SECTION("sin")
|
||||
{
|
||||
REQUIRE_THAT(sin(0 * angle[deg]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(sin(90 * angle[deg]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(sin(180 * angle[deg]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(sin(270 * angle[deg]), AlmostEquals(-1. * one));
|
||||
|
||||
REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
||||
REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one));
|
||||
}
|
||||
|
||||
SECTION("cos")
|
||||
{
|
||||
REQUIRE_THAT(cos(0 * angle[deg]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(cos(90 * angle[deg]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(cos(180 * angle[deg]), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(cos(270 * angle[deg]), AlmostEquals(0. * one));
|
||||
|
||||
REQUIRE_THAT(cos(0 * angle[grad]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(cos(100 * angle[grad]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(cos(200 * angle[grad]), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(cos(300 * angle[grad]), AlmostEquals(0. * one));
|
||||
}
|
||||
|
||||
SECTION("tan")
|
||||
{
|
||||
REQUIRE_THAT(tan(0 * angle[deg]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(tan(45 * angle[deg]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(tan(135 * angle[deg]), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(tan(180 * angle[deg]), AlmostEquals(0. * one));
|
||||
|
||||
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Angle inverse trigonometric functions", "[inv trig][angle]")
|
||||
{
|
||||
using namespace mp_units::angular;
|
||||
using namespace mp_units::angular::unit_symbols;
|
||||
using mp_units::angular::unit_symbols::deg;
|
||||
|
||||
SECTION("asin")
|
||||
{
|
||||
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
|
||||
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
|
||||
}
|
||||
|
||||
SECTION("acos")
|
||||
{
|
||||
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
|
||||
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
|
||||
}
|
||||
|
||||
SECTION("atan")
|
||||
{
|
||||
REQUIRE_THAT(atan(-1 * one), AlmostEquals(-45. * angle[deg]));
|
||||
REQUIRE_THAT(atan(0 * one), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(atan(1 * one), AlmostEquals(45. * angle[deg]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Angle atan2 functions", "[atan2][angle]")
|
||||
{
|
||||
using namespace mp_units::angular;
|
||||
using namespace mp_units::angular::unit_symbols;
|
||||
using mp_units::angular::unit_symbols::deg;
|
||||
|
||||
SECTION("atan2 should work on the same quantities")
|
||||
{
|
||||
REQUIRE_THAT(atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * angle[deg]));
|
||||
REQUIRE_THAT(atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * angle[deg]));
|
||||
}
|
||||
SECTION("atan2 should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE_THAT(atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * angle[deg]));
|
||||
REQUIRE_THAT(atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * angle[deg]));
|
||||
REQUIRE(sqrt(4 * isq::area[m2]) == 2 * isq::length[m]);
|
||||
}
|
||||
|
||||
SECTION("'cbrt()' on quantity changes the value and the dimension accordingly")
|
||||
{
|
||||
REQUIRE(cbrt(8 * isq::volume[m3]) == 2 * isq::length[m]);
|
||||
}
|
||||
|
||||
SECTION("'fma()' on quantity changes the value and the dimension accordingly")
|
||||
{
|
||||
REQUIRE(fma(1.0 * isq::length[m], 2.0 * one, 2.0 * isq::length[m]) == 4.0 * isq::length[m]);
|
||||
REQUIRE(fma(isq::speed(10.0 * m / s), isq::time(2.0 * s), isq::height(42.0 * m)) == isq::length(62.0 * m));
|
||||
}
|
||||
|
||||
SECTION("fmod functions")
|
||||
{
|
||||
SECTION("fmod should work on the same quantities")
|
||||
{
|
||||
REQUIRE(fmod(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
|
||||
REQUIRE(fmod(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
|
||||
REQUIRE(fmod(3 * isq::length[km], 2 * isq::length[km]) == 1 * isq::length[km]);
|
||||
REQUIRE(fmod(4 * isq::length[km], 2.5f * isq::length[km]) == 1.5 * isq::length[km]);
|
||||
}
|
||||
SECTION("fmod should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE(fmod(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
|
||||
REQUIRE(fmod(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
|
||||
REQUIRE(fmod(3. * isq::length[km], 2000. * isq::length[m]) == 1000 * isq::length[m]);
|
||||
REQUIRE(fmod(4 * isq::length[km], 2500 * isq::length[m]) == 1500 * isq::length[m]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("remainder functions")
|
||||
{
|
||||
SECTION("remainder should work on the same quantities")
|
||||
{
|
||||
REQUIRE(remainder(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
|
||||
REQUIRE(remainder(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
|
||||
REQUIRE(remainder(3 * isq::length[km], 2 * isq::length[km]) == -1 * isq::length[km]);
|
||||
REQUIRE(remainder(4 * isq::length[km], 2.75f * isq::length[km]) == 1.25 * isq::length[km]);
|
||||
}
|
||||
SECTION("remainder should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE(remainder(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
|
||||
REQUIRE(remainder(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
|
||||
REQUIRE(remainder(3. * isq::length[km], 2000. * isq::length[m]) == -1000 * isq::length[m]);
|
||||
REQUIRE(remainder(4 * isq::length[km], 2750 * isq::length[m]) == 1250 * isq::length[m]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("'isfinite()' accepts dimensioned arguments") { REQUIRE(isfinite(4.0 * isq::length[m])); }
|
||||
|
||||
SECTION("'isinf()' accepts dimensioned arguments") { REQUIRE(!isinf(4.0 * isq::length[m])); }
|
||||
|
||||
SECTION("'isnan()' accepts dimensioned arguments") { REQUIRE(!isnan(4.0 * isq::length[m])); }
|
||||
|
||||
|
||||
SECTION("'pow<Num, Den>()' on quantity changes the value and the dimension accordingly")
|
||||
{
|
||||
REQUIRE(pow<1, 4>(16 * isq::area[m2]) == sqrt(4 * isq::length[m]));
|
||||
}
|
||||
|
||||
// TODO add tests for exp()
|
||||
|
||||
SECTION("absolute functions on quantity returns the absolute value")
|
||||
{
|
||||
SECTION("'abs()' on a negative quantity returns the abs")
|
||||
{
|
||||
SECTION("integral representation") { REQUIRE(abs(-1 * isq::length[m]) == 1 * isq::length[m]); }
|
||||
|
||||
SECTION("floating-point representation") { REQUIRE(abs(-1. * isq::length[m]) == 1 * isq::length[m]); }
|
||||
}
|
||||
|
||||
SECTION("'abs()' on a positive quantity returns the abs")
|
||||
{
|
||||
SECTION("integral representation") { REQUIRE(abs(1 * isq::length[m]) == 1 * isq::length[m]); }
|
||||
|
||||
SECTION("floating-point representation") { REQUIRE(abs(1. * isq::length[m]) == 1 * isq::length[m]); }
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("numeric_limits functions")
|
||||
{
|
||||
SECTION("'epsilon' works as expected using default floating type")
|
||||
{
|
||||
REQUIRE(epsilon<double>(isq::length[m]).numerical_value_in(m) ==
|
||||
std::numeric_limits<decltype(1. * isq::length[m])::rep>::epsilon());
|
||||
}
|
||||
SECTION("'epsilon' works as expected using integers")
|
||||
{
|
||||
REQUIRE(epsilon<int>(isq::length[m]).numerical_value_in(m) ==
|
||||
std::numeric_limits<decltype(1 * isq::length[m])::rep>::epsilon());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("floor functions")
|
||||
{
|
||||
SECTION("floor 1 second with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1000 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1001 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1999 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1999 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -1000 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -999 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-999 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1.3 seconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1.3 * isq::time[s]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -1.3 seconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-1.3 * isq::time[s]) == -2 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1001. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor 1999. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(1999. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -1000. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("floor -999. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(floor<si::second>(-999. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
|
||||
// TODO Add tests for `N`, `kN` and `kg * m / s2` i `kg * km / s2`
|
||||
}
|
||||
|
||||
SECTION("ceil functions")
|
||||
{
|
||||
SECTION("ceil 1 second with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1000 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1001 milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1001 * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1999 milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -1000 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -999 milliseconds with target unit second should be 0 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-999 * isq::time[ms]) == 0 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1.3 seconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1.3 * isq::time[s]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -1.3 seconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-1.3 * isq::time[s]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1001. milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1001. * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil 1999. milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -1000. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("ceil -999. milliseconds with target unit second should be 0 seconds")
|
||||
{
|
||||
REQUIRE(ceil<si::second>(-999. * isq::time[ms]) == 0 * isq::time[s]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("round functions")
|
||||
{
|
||||
SECTION("round 1 second with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1000 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1001 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1499 milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1499 * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1500 milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(1500 * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1999 milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1000 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1001 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1001 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1499 milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1499 * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1500 milliseconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1500 * isq::time[ms]) == -2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1999 milliseconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1999 * isq::time[ms]) == -2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1000. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1000. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1001. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1499. milliseconds with target unit second should be 1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(1499. * isq::time[ms]) == 1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1500. milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(1500. * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round 1999. milliseconds with target unit second should be 2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1000. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1001. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1001. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1499. milliseconds with target unit second should be -1 second")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1499. * isq::time[ms]) == -1 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1500. milliseconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1500. * isq::time[ms]) == -2 * isq::time[s]);
|
||||
}
|
||||
SECTION("round -1999. milliseconds with target unit second should be -2 seconds")
|
||||
{
|
||||
REQUIRE(round<si::second>(-1999. * isq::time[ms]) == -2 * isq::time[s]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("hypot functions")
|
||||
{
|
||||
SECTION("hypot should work on the same quantities")
|
||||
{
|
||||
REQUIRE(hypot(3. * isq::length[km], 4. * isq::length[km]) == 5. * isq::length[km]);
|
||||
REQUIRE(hypot(2. * isq::length[km], 3. * isq::length[km], 6. * isq::length[km]) == 7. * isq::length[km]);
|
||||
}
|
||||
SECTION("hypot should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE(hypot(3. * isq::length[km], 4000. * isq::length[m]) == 5. * isq::length[km]);
|
||||
REQUIRE(hypot(2. * isq::length[km], 3000. * isq::length[m], 6. * isq::length[km]) == 7. * isq::length[km]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("SI trigonometric functions")
|
||||
{
|
||||
SECTION("sin")
|
||||
{
|
||||
REQUIRE_THAT(si::sin(0 * deg), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(si::sin(90 * deg), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(si::sin(180 * deg), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(si::sin(270 * deg), AlmostEquals(-1. * one));
|
||||
}
|
||||
|
||||
SECTION("cos")
|
||||
{
|
||||
REQUIRE_THAT(si::cos(0 * deg), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(si::cos(90 * deg), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(si::cos(180 * deg), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(si::cos(270 * deg), AlmostEquals(0. * one));
|
||||
}
|
||||
|
||||
SECTION("tan")
|
||||
{
|
||||
REQUIRE_THAT(si::tan(0 * deg), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(si::tan(45. * deg), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(si::tan(135. * deg), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(si::tan(180. * deg), AlmostEquals(0. * one));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("SI inverse trigonometric functions")
|
||||
{
|
||||
SECTION("asin")
|
||||
{
|
||||
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
|
||||
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
|
||||
}
|
||||
|
||||
SECTION("acos")
|
||||
{
|
||||
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
|
||||
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
|
||||
}
|
||||
|
||||
SECTION("atan")
|
||||
{
|
||||
REQUIRE_THAT(si::atan(-1 * one), AlmostEquals(-45. * deg));
|
||||
REQUIRE_THAT(si::atan(0 * one), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::atan(1 * one), AlmostEquals(45. * deg));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("SI atan2 functions")
|
||||
{
|
||||
SECTION("atan2 should work on the same quantities")
|
||||
{
|
||||
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * deg));
|
||||
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * deg));
|
||||
}
|
||||
SECTION("atan2 should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * deg));
|
||||
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * deg));
|
||||
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * deg));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Angle trigonometric functions")
|
||||
{
|
||||
using namespace mp_units::angular;
|
||||
using namespace mp_units::angular::unit_symbols;
|
||||
using mp_units::angular::unit_symbols::deg;
|
||||
|
||||
SECTION("sin")
|
||||
{
|
||||
REQUIRE_THAT(sin(0 * angle[deg]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(sin(90 * angle[deg]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(sin(180 * angle[deg]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(sin(270 * angle[deg]), AlmostEquals(-1. * one));
|
||||
|
||||
REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
||||
REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one));
|
||||
}
|
||||
|
||||
SECTION("cos")
|
||||
{
|
||||
REQUIRE_THAT(cos(0 * angle[deg]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(cos(90 * angle[deg]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(cos(180 * angle[deg]), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(cos(270 * angle[deg]), AlmostEquals(0. * one));
|
||||
|
||||
REQUIRE_THAT(cos(0 * angle[grad]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(cos(100 * angle[grad]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(cos(200 * angle[grad]), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(cos(300 * angle[grad]), AlmostEquals(0. * one));
|
||||
}
|
||||
|
||||
SECTION("tan")
|
||||
{
|
||||
REQUIRE_THAT(tan(0 * angle[deg]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(tan(45 * angle[deg]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(tan(135 * angle[deg]), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(tan(180 * angle[deg]), AlmostEquals(0. * one));
|
||||
|
||||
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one));
|
||||
REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one));
|
||||
REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Angle inverse trigonometric functions")
|
||||
{
|
||||
using namespace mp_units::angular;
|
||||
using namespace mp_units::angular::unit_symbols;
|
||||
using mp_units::angular::unit_symbols::deg;
|
||||
|
||||
SECTION("asin")
|
||||
{
|
||||
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
|
||||
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
|
||||
}
|
||||
|
||||
SECTION("acos")
|
||||
{
|
||||
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
|
||||
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
|
||||
}
|
||||
|
||||
SECTION("atan")
|
||||
{
|
||||
REQUIRE_THAT(atan(-1 * one), AlmostEquals(-45. * angle[deg]));
|
||||
REQUIRE_THAT(atan(0 * one), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(atan(1 * one), AlmostEquals(45. * angle[deg]));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Angle atan2 functions")
|
||||
{
|
||||
using namespace mp_units::angular;
|
||||
using namespace mp_units::angular::unit_symbols;
|
||||
using mp_units::angular::unit_symbols::deg;
|
||||
|
||||
SECTION("atan2 should work on the same quantities")
|
||||
{
|
||||
REQUIRE_THAT(atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * angle[deg]));
|
||||
REQUIRE_THAT(atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * angle[deg]));
|
||||
}
|
||||
SECTION("atan2 should work with different units of the same dimension")
|
||||
{
|
||||
REQUIRE_THAT(atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * angle[deg]));
|
||||
REQUIRE_THAT(atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * angle[deg]));
|
||||
REQUIRE_THAT(atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * angle[deg]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,22 +60,25 @@ constexpr bool within_4_ulps(T a, T b)
|
||||
|
||||
} // namespace
|
||||
|
||||
// conversion requiring radical magnitudes
|
||||
TEST_CASE("unit conversions support radical magnitudes", "[conversion][radical]")
|
||||
TEST_CASE("quantity operations", "[quantity]")
|
||||
{
|
||||
REQUIRE(within_4_ulps(sqrt((1.0 * m) * (1.0 * km)).numerical_value_in(m), sqrt(1000.0)));
|
||||
}
|
||||
// conversion requiring radical magnitudes
|
||||
SECTION("unit conversions support radical magnitudes")
|
||||
{
|
||||
REQUIRE(within_4_ulps(sqrt((1.0 * m) * (1.0 * km)).numerical_value_in(m), sqrt(1000.0)));
|
||||
}
|
||||
|
||||
// Reproducing issue #474 exactly:
|
||||
TEST_CASE("Issue 474 is fixed", "[conversion][radical]")
|
||||
{
|
||||
constexpr auto val_issue_474 = 8.0 * si::si2019::boltzmann_constant * 1000.0 * K / (std::numbers::pi * 10 * Da);
|
||||
REQUIRE(within_4_ulps(sqrt(val_issue_474).numerical_value_in(m / s),
|
||||
sqrt(val_issue_474.numerical_value_in(m * m / s / s))));
|
||||
}
|
||||
// Reproducing issue #474 exactly:
|
||||
SECTION("Issue 474 is fixed")
|
||||
{
|
||||
constexpr auto val_issue_474 = 8.0 * si::si2019::boltzmann_constant * 1000.0 * K / (std::numbers::pi * 10 * Da);
|
||||
REQUIRE(within_4_ulps(sqrt(val_issue_474).numerical_value_in(m / s),
|
||||
sqrt(val_issue_474.numerical_value_in(m * m / s / s))));
|
||||
}
|
||||
|
||||
TEST_CASE("Volatile representation type", "[volatile]")
|
||||
{
|
||||
volatile std::int16_t vint = 123;
|
||||
REQUIRE(quantity(vint * m).numerical_value_in(m) == 123);
|
||||
SECTION("Volatile representation type")
|
||||
{
|
||||
volatile std::int16_t vint = 123;
|
||||
REQUIRE(quantity(vint * m).numerical_value_in(m) == 123);
|
||||
}
|
||||
}
|
||||
|
@ -25,24 +25,28 @@
|
||||
#include <mp-units/systems/isq/mechanics.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
#include <mp-units/systems/si/units.h>
|
||||
|
||||
template<class T>
|
||||
requires mp_units::is_scalar<T>
|
||||
constexpr bool mp_units::is_vector<T> = true;
|
||||
#if MP_UNITS_HOSTED
|
||||
#include <mp-units/cartesian_vector.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace mp_units;
|
||||
using namespace mp_units::cgs;
|
||||
using namespace mp_units::cgs::unit_symbols;
|
||||
#if MP_UNITS_HOSTED
|
||||
using v = cartesian_vector<double>;
|
||||
#endif
|
||||
|
||||
// https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units#Definitions_and_conversion_factors_of_CGS_units_in_mechanics
|
||||
static_assert(isq::length(100 * cm) == isq::length(1 * si::metre));
|
||||
static_assert(isq::mass(1000 * g) == isq::mass(1 * si::kilogram));
|
||||
static_assert(isq::time(1 * s) == isq::time(1 * si::second));
|
||||
static_assert(isq::speed(100 * cm / s) == isq::speed(1 * si::metre / si::second));
|
||||
static_assert(isq::acceleration(100 * Gal) == isq::acceleration(1 * si::metre / square(si::second)));
|
||||
static_assert(isq::force(100'000 * dyn) == isq::force(1 * si::newton));
|
||||
#if MP_UNITS_HOSTED
|
||||
static_assert(isq::acceleration(v{100} * Gal) == isq::acceleration(v{1} * si::metre / square(si::second)));
|
||||
static_assert(isq::force(v{100'000} * dyn) == isq::force(v{1} * si::newton));
|
||||
#endif
|
||||
static_assert(isq::energy(10'000'000 * erg) == isq::energy(1 * si::joule));
|
||||
static_assert(isq::power(10'000'000 * erg / s) == isq::power(1 * si::watt));
|
||||
static_assert(isq::pressure(10 * Ba) == isq::pressure(1 * si::pascal));
|
||||
|
@ -237,17 +237,17 @@ static_assert(compare(round<si::second>(-1999. * isq::time[ms]), -2. * isq::time
|
||||
#endif
|
||||
|
||||
// non-truncating
|
||||
static_assert(compare(inverse<us>(250 * Hz), 4000 * us));
|
||||
static_assert(compare(inverse<us>(250 * kHz), 4 * us));
|
||||
static_assert(compare(inverse<ks>(250 * uHz), 4 * ks));
|
||||
static_assert(compare(kind_of<isq::time>(inverse<us>(250 * Hz)), 4000 * us));
|
||||
static_assert(compare(kind_of<isq::time>(inverse<us>(250 * kHz)), 4 * us));
|
||||
static_assert(compare(kind_of<isq::time>(inverse<ks>(250 * uHz)), 4 * ks));
|
||||
|
||||
// truncating
|
||||
static_assert(compare(inverse<s>(1 * kHz), 0 * s));
|
||||
static_assert(compare(kind_of<isq::time>(inverse<s>(1 * kHz)), 0 * s));
|
||||
|
||||
// floating-point representation does not truncate
|
||||
static_assert(compare(inverse<s>(1. * kHz), 0.001 * s));
|
||||
static_assert(compare(kind_of<isq::time>(inverse<s>(1. * kHz)), 0.001 * s));
|
||||
|
||||
// check if constraints work properly for a derived unit of a narrowed kind
|
||||
static_assert(compare(inverse<Hz>(1 * s), 1 * Hz));
|
||||
static_assert(compare(kind_of<isq::frequency>(inverse<Hz>(1 * s)), 1 * Hz));
|
||||
|
||||
} // namespace
|
||||
|
Reference in New Issue
Block a user