refactor: modules support redesigned

This commit is contained in:
Mateusz Pusz committed 2024-01-06 08:51:01 +01:00
1 parent b582e200a0
commit 0eaf6e2e56
129 files changed
+271 -838

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
# 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.
cmake_minimum_required(VERSION 3.5)
find_package(Catch2 3 CONFIG REQUIRED)
add_executable(unit_tests_runtime distribution_test.cpp fmt_test.cpp math_test.cpp)
if(${projectPrefix}BUILD_CXX_MODULES)
target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES)
endif()
target_link_libraries(unit_tests_runtime PRIVATE mp-units::mp-units Catch2::Catch2WithMain)
if(${projectPrefix}BUILD_LA)
find_package(wg21_linear_algebra CONFIG REQUIRED)
target_sources(unit_tests_runtime PRIVATE linear_algebra_test.cpp)
target_link_libraries(unit_tests_runtime PRIVATE wg21_linear_algebra::wg21_linear_algebra)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(
unit_tests_runtime PRIVATE /wd4244 # 'conversion' conversion from 'type1' to 'type2', possible loss of data
)
endif()
include(Catch)
catch_discover_tests(unit_tests_runtime)
+63
View File
@@ -0,0 +1,63 @@
// 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 <catch2/matchers/catch_matchers_templated.hpp>
#include <mp-units/compat_fmt_macros.h>
#include <algorithm>
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/format.h>
#include <mp-units/quantity.h>
#endif
namespace mp_units {
template<Quantity T>
struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase {
AlmostEqualsMatcher(const T& target) : target_{target} {}
template<std::convertible_to<T> U>
requires std::same_as<typename T::rep, typename U::rep> && treat_as_floating_point<typename T::rep>
bool match(const U& other) const
{
using std::abs;
using common = std::common_type_t<T, U>;
const auto x = common(target_).numerical_value_in(common::unit);
const auto y = common(other).numerical_value_in(common::unit);
const auto maxXYOne = std::max({typename T::rep{1}, abs(x), abs(y)});
return abs(x - y) <= std::numeric_limits<typename T::rep>::epsilon() * maxXYOne;
}
std::string describe() const override { return "almost equals: " + MP_UNITS_STD_FMT::format("{}", target_); }
private:
const T& target_;
};
template<Quantity T>
AlmostEqualsMatcher<T> AlmostEquals(const T& target)
{
return {target};
}
} // namespace mp_units
+654
View File
@@ -0,0 +1,654 @@
// 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 <catch2/catch_test_macros.hpp>
#include <array>
#include <initializer_list>
#include <random>
#include <vector>
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/ostream.h>
#include <mp-units/random.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <mp-units/systems/si/units.h>
#endif
using namespace mp_units;
TEST_CASE("uniform_int_distribution")
{
using rep = std::int64_t;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::uniform_int_distribution<q>();
CHECK(dist.a() == q::zero());
CHECK(dist.b() == q::max());
}
SECTION("parametrized")
{
constexpr rep a = 2;
constexpr rep b = 5;
auto stl_dist = std::uniform_int_distribution(a, b);
auto units_dist = mp_units::uniform_int_distribution(a * si::metre, b * si::metre);
CHECK(units_dist.a() == stl_dist.a() * si::metre);
CHECK(units_dist.b() == stl_dist.b() * si::metre);
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("uniform_real_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::uniform_real_distribution<q>();
CHECK(dist.a() == q::zero());
CHECK(dist.b() == q::one());
}
SECTION("parametrized")
{
constexpr rep a = 2.0;
constexpr rep b = 5.0;
auto stl_dist = std::uniform_real_distribution(a, b);
auto units_dist = mp_units::uniform_real_distribution(a * si::metre, b * si::metre);
CHECK(units_dist.a() == stl_dist.a() * si::metre);
CHECK(units_dist.b() == stl_dist.b() * si::metre);
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("binomial_distribution")
{
using rep = std::int64_t;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::binomial_distribution<q>();
CHECK(dist.p() == 0.5);
CHECK(dist.t() == q::one());
}
SECTION("parametrized")
{
constexpr rep t = 5;
constexpr double p = 0.25;
auto stl_dist = std::binomial_distribution(t, p);
auto units_dist = mp_units::binomial_distribution(t * si::metre, p);
CHECK(units_dist.p() == stl_dist.p());
CHECK(units_dist.t() == stl_dist.t() * si::metre);
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("negative_binomial_distribution")
{
using rep = std::int64_t;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::negative_binomial_distribution<q>();
CHECK(dist.p() == 0.5);
CHECK(dist.k() == q::one());
}
SECTION("parametrized")
{
constexpr rep k = 5;
constexpr double p = 0.25;
auto stl_dist = std::negative_binomial_distribution(k, p);
auto units_dist = mp_units::negative_binomial_distribution(k * si::metre, p);
CHECK(units_dist.p() == stl_dist.p());
CHECK(units_dist.k() == stl_dist.k() * si::metre);
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("geometric_distribution")
{
using rep = std::int64_t;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::geometric_distribution<q>();
CHECK(dist.p() == 0.5);
}
SECTION("parametrized")
{
constexpr double p = 0.25;
auto stl_dist = std::geometric_distribution<rep>(p);
auto units_dist = mp_units::geometric_distribution<q>(p);
CHECK(units_dist.p() == stl_dist.p());
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("poisson_distribution")
{
using rep = std::int64_t;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::poisson_distribution<q>();
CHECK(dist.mean() == 1.0);
}
SECTION("parametrized")
{
constexpr double mean = 5.0;
auto stl_dist = std::poisson_distribution<rep>(mean);
auto units_dist = mp_units::poisson_distribution<q>(mean);
CHECK(units_dist.mean() == stl_dist.mean());
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("exponential_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::exponential_distribution<q>();
CHECK(dist.lambda() == 1.0);
}
SECTION("parametrized")
{
constexpr double lambda = 2.0;
auto stl_dist = std::exponential_distribution<rep>(lambda);
auto units_dist = mp_units::exponential_distribution<q>(lambda);
CHECK(units_dist.lambda() == stl_dist.lambda());
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("gamma_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::gamma_distribution<q>();
CHECK(dist.alpha() == 1.0);
CHECK(dist.beta() == 1.0);
}
SECTION("parametrized")
{
constexpr double alpha = 5.0;
constexpr double beta = 2.0;
auto stl_dist = std::gamma_distribution<rep>(alpha, beta);
auto units_dist = mp_units::gamma_distribution<q>(alpha, beta);
CHECK(units_dist.alpha() == stl_dist.alpha());
CHECK(units_dist.beta() == stl_dist.beta());
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("weibull_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::weibull_distribution<q>();
CHECK(dist.a() == 1.0);
CHECK(dist.b() == 1.0);
}
SECTION("parametrized")
{
constexpr rep a = 5.0;
constexpr rep b = 2.0;
auto stl_dist = std::weibull_distribution(a, b);
auto units_dist = mp_units::weibull_distribution<q>(a, b);
CHECK(units_dist.a() == stl_dist.a());
CHECK(units_dist.b() == stl_dist.b());
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("extreme_value_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::extreme_value_distribution<q>();
CHECK(dist.a() == q::zero());
CHECK(dist.b() == 1.0);
}
SECTION("parametrized")
{
constexpr rep a = 5.0;
constexpr rep b = 2.0;
auto stl_dist = std::extreme_value_distribution(a, b);
auto units_dist = mp_units::extreme_value_distribution<q>(a * si::metre, b);
CHECK(units_dist.a() == stl_dist.a() * si::metre);
CHECK(units_dist.b() == stl_dist.b());
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("normal_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::normal_distribution<q>();
CHECK(dist.mean() == q::zero());
CHECK(dist.stddev() == q::one());
}
SECTION("parametrized")
{
constexpr rep mean = 5.0;
constexpr rep stddev = 2.0;
auto stl_dist = std::normal_distribution(mean, stddev);
auto units_dist = mp_units::normal_distribution(mean * si::metre, stddev * si::metre);
CHECK(units_dist.mean() == stl_dist.mean() * si::metre);
CHECK(units_dist.stddev() == stl_dist.stddev() * si::metre);
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("lognormal_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::lognormal_distribution<q>();
CHECK(dist.m() == q::zero());
CHECK(dist.s() == q::one());
}
SECTION("parametrized")
{
constexpr rep m = 5.0;
constexpr rep s = 2.0;
auto stl_dist = std::lognormal_distribution(m, s);
auto units_dist = mp_units::lognormal_distribution(m * si::metre, s * si::metre);
CHECK(units_dist.m() == stl_dist.m() * si::metre);
CHECK(units_dist.s() == stl_dist.s() * si::metre);
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("chi_squared_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::chi_squared_distribution<q>();
CHECK(dist.n() == 1.0);
}
SECTION("parametrized")
{
constexpr rep n = 5.0;
auto stl_dist = std::chi_squared_distribution(n);
auto units_dist = mp_units::chi_squared_distribution<q>(n);
CHECK(units_dist.n() == stl_dist.n());
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("cauchy_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::cauchy_distribution<q>();
CHECK(dist.a() == q::zero());
CHECK(dist.b() == q::one());
}
SECTION("parametrized")
{
constexpr rep a = 5.0;
constexpr rep b = 2.0;
auto stl_dist = std::cauchy_distribution(a, b);
auto units_dist = mp_units::cauchy_distribution(a * si::metre, b * si::metre);
CHECK(units_dist.a() == stl_dist.a() * si::metre);
CHECK(units_dist.b() == stl_dist.b() * si::metre);
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("fisher_f_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::fisher_f_distribution<q>();
CHECK(dist.m() == 1.0);
CHECK(dist.n() == 1.0);
}
SECTION("parametrized")
{
constexpr rep m = 5.0;
constexpr rep n = 2.0;
auto stl_dist = std::fisher_f_distribution<rep>(m, n);
auto units_dist = mp_units::fisher_f_distribution<q>(m, n);
CHECK(units_dist.m() == stl_dist.m());
CHECK(units_dist.n() == stl_dist.n());
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("student_t_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto dist = mp_units::student_t_distribution<q>();
CHECK(dist.n() == 1.0);
}
SECTION("parametrized")
{
constexpr rep n = 2.0;
auto stl_dist = std::student_t_distribution<rep>(n);
auto units_dist = mp_units::student_t_distribution<q>(n);
CHECK(units_dist.n() == stl_dist.n());
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
}
}
TEST_CASE("discrete_distribution")
{
using rep = std::int64_t;
using q = quantity<isq::length[si::metre], rep>;
SECTION("default")
{
auto stl_dist = std::discrete_distribution<rep>();
auto units_dist = mp_units::discrete_distribution<q>();
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
CHECK(units_dist.probabilities() == stl_dist.probabilities());
}
SECTION("parametrized_input_it")
{
constexpr std::array<double, 3> weights = {1.0, 2.0, 3.0};
auto stl_dist = std::discrete_distribution<rep>(weights.cbegin(), weights.cend());
auto units_dist = mp_units::discrete_distribution<q>(weights.cbegin(), weights.cend());
CHECK(units_dist.probabilities() == stl_dist.probabilities());
}
SECTION("parametrized_initializer_list")
{
std::initializer_list<double> weights = {1.0, 2.0, 3.0};
auto stl_dist = std::discrete_distribution<rep>(weights);
auto units_dist = mp_units::discrete_distribution<q>(weights);
CHECK(units_dist.probabilities() == stl_dist.probabilities());
}
SECTION("parametrized_range")
{
constexpr std::size_t count = 3;
constexpr double xmin = 1, xmax = 3;
auto stl_dist = std::discrete_distribution<rep>(count, xmin, xmax, [](double val) { return val; });
auto units_dist = mp_units::discrete_distribution<q>(count, xmin, xmax, [](double val) { return val; });
CHECK(units_dist.probabilities() == stl_dist.probabilities());
}
}
TEST_CASE("piecewise_constant_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
std::vector<rep> intervals_rep_vec = {1.0, 2.0, 3.0};
std::vector<q> intervals_qty_vec = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre],
3.0 * isq::length[si::metre]};
SECTION("default")
{
auto stl_dist = std::piecewise_constant_distribution<rep>();
auto units_dist = mp_units::piecewise_constant_distribution<q>();
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
CHECK(stl_dist.intervals().size() == 2);
CHECK(units_dist.intervals().size() == 2);
CHECK(stl_dist.densities().size() == 1);
CHECK(units_dist.densities().size() == 1);
}
SECTION("parametrized_input_it")
{
constexpr std::array<rep, 3> intervals_rep = {1.0, 2.0, 3.0};
constexpr std::array<q, 3> intervals_qty = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre],
3.0 * isq::length[si::metre]};
constexpr std::array<rep, 3> weights = {1.0, 2.0, 3.0};
auto stl_dist =
std::piecewise_constant_distribution<rep>(intervals_rep.cbegin(), intervals_rep.cend(), weights.cbegin());
auto units_dist =
mp_units::piecewise_constant_distribution<q>(intervals_qty.cbegin(), intervals_qty.cend(), weights.cbegin());
CHECK(stl_dist.intervals() == intervals_rep_vec);
CHECK(units_dist.intervals() == intervals_qty_vec);
CHECK(units_dist.densities() == stl_dist.densities());
}
SECTION("parametrized_initializer_list")
{
std::initializer_list<rep> intervals_rep = {1.0, 2.0, 3.0};
std::initializer_list<q> intervals_qty = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre],
3.0 * isq::length[si::metre]};
auto stl_dist = std::piecewise_constant_distribution<rep>(intervals_rep, [](rep val) { return val; });
auto units_dist = mp_units::piecewise_constant_distribution<q>(
intervals_qty, [](q qty) { return qty.numerical_value_ref_in(qty.unit); });
CHECK(units_dist.intervals() == intervals_qty_vec);
CHECK(units_dist.densities() == stl_dist.densities());
}
SECTION("parametrized_range")
{
constexpr std::size_t nw = 2;
constexpr rep xmin_rep = 1.0, xmax_rep = 3.0;
constexpr q xmin_qty = 1.0 * isq::length[si::metre], xmax_qty = 3.0 * isq::length[si::metre];
auto stl_dist = std::piecewise_constant_distribution<rep>(nw, xmin_rep, xmax_rep, [](rep val) { return val; });
auto units_dist = mp_units::piecewise_constant_distribution<q>(
nw, xmin_qty, xmax_qty, [](q qty) { return qty.numerical_value_ref_in(qty.unit); });
CHECK(units_dist.intervals() == intervals_qty_vec);
CHECK(units_dist.densities() == stl_dist.densities());
}
}
TEST_CASE("piecewise_linear_distribution")
{
using rep = long double;
using q = quantity<isq::length[si::metre], rep>;
std::vector<rep> intervals_rep_vec = {1.0, 2.0, 3.0};
std::vector<q> intervals_qty_vec = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre],
3.0 * isq::length[si::metre]};
SECTION("default")
{
auto stl_dist = std::piecewise_linear_distribution<rep>();
auto units_dist = mp_units::piecewise_linear_distribution<q>();
CHECK(units_dist.min() == stl_dist.min() * si::metre);
CHECK(units_dist.max() == stl_dist.max() * si::metre);
CHECK(stl_dist.intervals().size() == 2);
CHECK(units_dist.intervals().size() == 2);
CHECK(stl_dist.densities().size() == 2);
CHECK(units_dist.densities().size() == 2);
}
SECTION("parametrized_input_it")
{
constexpr std::array<rep, 3> intervals_rep = {1.0, 2.0, 3.0};
constexpr std::array<q, 3> intervals_qty = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre],
3.0 * isq::length[si::metre]};
constexpr std::array<rep, 3> weights = {1.0, 2.0, 3.0};
auto stl_dist =
std::piecewise_linear_distribution<rep>(intervals_rep.cbegin(), intervals_rep.cend(), weights.cbegin());
auto units_dist =
mp_units::piecewise_linear_distribution<q>(intervals_qty.cbegin(), intervals_qty.cend(), weights.cbegin());
CHECK(stl_dist.intervals() == intervals_rep_vec);
CHECK(units_dist.intervals() == intervals_qty_vec);
CHECK(units_dist.densities() == stl_dist.densities());
}
SECTION("parametrized_initializer_list")
{
std::initializer_list<rep> intervals_rep = {1.0, 2.0, 3.0};
std::initializer_list<q> intervals_qty = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre],
3.0 * isq::length[si::metre]};
auto stl_dist = std::piecewise_linear_distribution<rep>(intervals_rep, [](rep val) { return val; });
auto units_dist = mp_units::piecewise_linear_distribution<q>(
intervals_qty, [](q qty) { return qty.numerical_value_ref_in(qty.unit); });
CHECK(units_dist.intervals() == intervals_qty_vec);
CHECK(units_dist.densities() == stl_dist.densities());
}
SECTION("parametrized_range")
{
constexpr std::size_t nw = 2;
constexpr rep xmin_rep = 1.0, xmax_rep = 3.0;
constexpr q xmin_qty = 1.0 * isq::length[si::metre], xmax_qty = 3.0 * isq::length[si::metre];
auto stl_dist = std::piecewise_linear_distribution<rep>(nw, xmin_rep, xmax_rep, [](rep val) { return val; });
auto units_dist = mp_units::piecewise_linear_distribution<q>(
nw, xmin_qty, xmax_qty, [](q qty) { return qty.numerical_value_ref_in(qty.unit); });
CHECK(units_dist.intervals() == intervals_qty_vec);
CHECK(units_dist.densities() == stl_dist.densities());
}
}
+904
View File
@@ -0,0 +1,904 @@
// 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 <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_exception.hpp>
#include <mp-units/compat_fmt_macros.h>
#include <iomanip>
#include <limits>
#include <locale>
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/customization_points.h>
#include <mp-units/format.h>
#include <mp-units/ostream.h>
#include <mp-units/systems/cgs/cgs.h>
#include <mp-units/systems/isq/mechanics.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/si.h>
#endif
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
{
std::ostringstream os;
SECTION("quantity with a predefined unit")
{
SECTION("integral representation")
{
const auto q = 60 * isq::power[W];
os << q;
SECTION("iostream") { CHECK(os.str() == "60 W"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("floating-point representation")
{
const auto q = 1023.5 * isq::pressure[Pa];
os << q;
SECTION("iostream") { CHECK(os.str() == "1023.5 Pa"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
}
SECTION("quantity with a predefined prefixed unit")
{
const auto q = 125 * isq::time[us];
os << q;
SECTION("iostream") { CHECK(os.str() == "125 µs"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("quantity with a derived unit")
{
SECTION("coherent derived unit")
{
SECTION("acceleration")
{
const auto q = 20 * isq::length[m] / (2 * isq::time[s]) / (1 * isq::time[s]);
os << q;
SECTION("iostream") { CHECK(os.str() == "10 m/s²"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
}
}
SECTION("volume")
{
const auto q = 2 * isq::length[m] * (1 * isq::length[m]) * (1 * isq::length[m]);
os << q;
SECTION("iostream") { CHECK(os.str() == "2 m³"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
}
}
SECTION("surface tension")
{
const auto q = 20 * isq::force[N] / (2 * isq::length[m]);
os << q;
SECTION("iostream") { CHECK(os.str() == "10 N/m"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
}
}
}
SECTION("scaled derived unit")
{
SECTION("speed")
{
const auto q = 20 * isq::length[km] / (2 * isq::time[h]);
os << q;
SECTION("iostream") { CHECK(os.str() == "10 km/h"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
}
}
SECTION("angular impulse")
{
const auto q = 123 * isq::angular_impulse[N * m * s];
os << q;
SECTION("iostream") { CHECK(os.str() == "123 N m s"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
}
}
SECTION("compressibility")
{
const auto q = 123 * isq::compressibility[one / Pa];
os << q;
SECTION("iostream") { CHECK(os.str() == "123 1/Pa"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
}
}
SECTION("angular acceleration")
{
const auto q = 123 * isq::angular_acceleration[rad / s2];
os << q;
SECTION("iostream") { CHECK(os.str() == "123 rad/s²"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str());
}
}
}
}
SECTION("dimensionless quantity")
{
SECTION("one with ratio == 1")
{
const auto q = 4 * isq::length[m] / (2 * isq::length[m]);
os << q;
SECTION("iostream") { CHECK(os.str() == "2"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "2 "); }
}
SECTION("one with ratio.exp != 0")
{
const auto q = 4 * isq::length[km] / (2 * isq::length[m]);
os << q;
SECTION("iostream") { CHECK(os.str() == "2 km/m"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "2 km/m"); }
}
SECTION("percents")
{
const auto q = value_cast<percent>(15. * isq::length[m] / (100. * isq::length[m]));
os << q;
SECTION("iostream") { CHECK(os.str() == "15 %"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("radians")
{
const auto q = 42 * rad;
os << q;
SECTION("iostream") { CHECK(os.str() == "42 rad"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
}
SECTION("no space before unit symbol")
{
SECTION("degree")
{
const auto q = 42 * deg;
os << q;
SECTION("iostream") { CHECK(os.str() == "42°"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "42 °"); }
}
SECTION("arcminute")
{
const auto q = 42 * arcmin;
os << q;
SECTION("iostream") { CHECK(os.str() == "42′"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "42 ′"); }
}
SECTION("arcsecond")
{
const auto q = 42 * arcsec;
os << q;
SECTION("iostream") { CHECK(os.str() == "42″"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "42 ″"); }
}
}
SECTION("8-bit integers")
{
SECTION("signed positive")
{
const auto q = std::int8_t{42} * si::second;
os << q;
SECTION("iostream") { CHECK(os.str() == "42 s"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("signed negative")
{
const auto q = std::int8_t{-42} * si::second;
os << q;
SECTION("iostream") { CHECK(os.str() == "-42 s"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("unsigned")
{
const auto q = std::uint8_t{42} * si::second;
os << q;
SECTION("iostream") { CHECK(os.str() == "42 s"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
}
}
TEST_CASE("format string with only %Q should print quantity value only", "[text][fmt]")
{
SECTION("integral representation")
{
SECTION("positive value") { CHECK(MP_UNITS_STD_FMT::format("{:%Q}", 123 * isq::speed[km / h]) == "123"); }
SECTION("negative value")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", 5 * isq::length[m] - 10 * isq::length[m]) == "-5");
}
}
SECTION("floating-point representation")
{
SECTION("positive value")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", 221. * isq::length[km] / (2 * isq::time[h])) == "110.5");
}
SECTION("negative value")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", 3.14 * isq::length[m] - 10 * isq::length[m]) == "-6.86");
}
SECTION("nan")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", std::numeric_limits<double>::quiet_NaN() * isq::length[m]) == "nan");
}
SECTION("inf")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", std::numeric_limits<double>::infinity() * isq::length[m]) == "inf");
}
SECTION("-inf")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Q}", -std::numeric_limits<double>::infinity() * isq::length[m]) == "-inf");
}
}
}
TEST_CASE("format string with only %q should print quantity unit symbol only", "[text][fmt]")
{
SECTION("Text encoding")
{
SECTION("Unicode text output")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Uq}", 123 * isq::speed[km / h]) == "km/h");
// TODO enable this when resistance is defined
// CHECK(MP_UNITS_STD_FMT::format("{:%Uq}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
CHECK(MP_UNITS_STD_FMT::format("{:%Uq}", 123 * isq::time[us]) == "µs");
CHECK(MP_UNITS_STD_FMT::format("{:%Uq}", 123 * isq::acceleration[m / s2]) == "m/s²");
}
SECTION("Unicode text output is used by default")
{
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
// CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::time[us]) == "µs");
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²");
}
SECTION("ASCII text output")
{
CHECK(MP_UNITS_STD_FMT::format("{:%Aq}", 123 * isq::speed[km / h]) == "km/h");
// CHECK(MP_UNITS_STD_FMT::format("{:%Aq}", 123 * isq::resistance[kilo<ohm>]) == "kohm");
CHECK(MP_UNITS_STD_FMT::format("{:%Aq}", 123 * isq::time[us]) == "us");
CHECK(MP_UNITS_STD_FMT::format("{:%Aq}", 123 * isq::acceleration[m / s2]) == "m/s^2");
}
}
SECTION("Solidus")
{
SECTION("Solidus for only one element in denominator")
{
CHECK(MP_UNITS_STD_FMT::format("{:%oq}", 123 * isq::speed[km / h]) == "km/h");
CHECK(MP_UNITS_STD_FMT::format("{:%oq}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(MP_UNITS_STD_FMT::format("{:%oq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
}
SECTION("Solidus for only one element in denominator is used by default")
{
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
}
SECTION("Always use solidus")
{
CHECK(MP_UNITS_STD_FMT::format("{:%aq}", 123 * isq::speed[km / h]) == "km/h");
CHECK(MP_UNITS_STD_FMT::format("{:%aq}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(MP_UNITS_STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
}
SECTION("Never use solidus")
{
CHECK(MP_UNITS_STD_FMT::format("{:%nq}", 123 * isq::speed[km / h]) == "km h⁻¹");
CHECK(MP_UNITS_STD_FMT::format("{:%nq}", 123 * isq::acceleration[m / s2]) == "m s⁻²");
CHECK(MP_UNITS_STD_FMT::format("{:%nq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
}
}
SECTION("Separator")
{
SECTION("Space")
{
CHECK(MP_UNITS_STD_FMT::format("{:%sq}", 123 * isq::force[kg * m / s2]) == "kg m/s²");
CHECK(MP_UNITS_STD_FMT::format("{:%sq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
CHECK(MP_UNITS_STD_FMT::format("{:%asq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
}
SECTION("Space is used by default")
{
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::force[kg * m / s2]) == "kg m/s²");
CHECK(MP_UNITS_STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
CHECK(MP_UNITS_STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
}
SECTION("Dot")
{
CHECK(MP_UNITS_STD_FMT::format("{:%dq}", 123 * isq::force[kg * m / s2]) == "kg⋅m/s²");
CHECK(MP_UNITS_STD_FMT::format("{:%dq}", 123 * isq::pressure[kg / m / s2]) == "kg⋅m⁻¹⋅s⁻²");
CHECK(MP_UNITS_STD_FMT::format("{:%adq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m⋅s²)");
}
}
}
TEST_CASE("unknown unit modifiers should throw", "[text][fmt][exception]")
{
auto q = 1 * isq::length[m];
SECTION("only the invalid modifier")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%xq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
}
SECTION("invalid modifier in the front")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%xUdaq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
}
SECTION("invalid modifier in the end")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%Udaxq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
}
SECTION("invalid modifier in the middle")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%Udxaq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
}
}
TEST_CASE("repeated unit modifiers should throw", "[text][fmt][exception]")
{
auto q = 1 * isq::length[m];
SECTION("text encoding")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%UdaUq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dUaUq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dUUaq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
}
SECTION("solidus")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%aUdaq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%daUaq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%daaUq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
}
SECTION("separator")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dUadq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dadUq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%addUq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
}
}
TEST_CASE("more then one modifier of the same kind should throw", "[text][fmt][exception]")
{
auto q = 1 * isq::length[m];
SECTION("text encoding")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%UdaAq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dAaUq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dAUaq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
}
SECTION("solidus")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%aUdnq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dnUaq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%daoUq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
}
SECTION("separator")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dUasq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%sadUq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%adsUq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
}
}
TEST_CASE("half_high_dot separator requested for ASCII encoding should throw", "[text][fmt][exception]")
{
auto q = 1 * isq::length[m];
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%dAaq}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("half_high_dot unit separator allowed only for Unicode encoding"));
}
TEST_CASE("%q and %Q can be put anywhere in a format string", "[text][fmt]")
{
SECTION("no space") { CHECK(MP_UNITS_STD_FMT::format("{:%Q%q}", 123 * isq::speed[km / h]) == "123km/h"); }
SECTION("separator") { CHECK(MP_UNITS_STD_FMT::format("{:%Q###%q}", 123 * isq::speed[km / h]) == "123###km/h"); }
SECTION("opposite order") { CHECK(MP_UNITS_STD_FMT::format("{:%q %Q}", 123 * isq::speed[km / h]) == "km/h 123"); }
}
TEST_CASE("fill and align specification", "[text][fmt][ostream]")
{
SECTION("ostream")
{
std::ostringstream os;
SECTION("width = 10")
{
os << "|" << std::setw(10) << 123 * isq::length[m] << "|";
CHECK(os.str() == "| 123 m|");
}
SECTION("width = 10, align = right")
{
os << "|" << std::setw(10) << std::right << 123 * isq::length[m] << "|";
CHECK(os.str() == "| 123 m|");
}
SECTION("width = 10, align = left")
{
os << "|" << std::setw(10) << std::left << 123 * isq::length[m] << "|";
CHECK(os.str() == "|123 m |");
}
SECTION("width = 10, fill = *")
{
os << "|" << std::setw(10) << std::setfill('*') << 123 * isq::length[m] << "|";
CHECK(os.str() == "|*****123 m|");
}
SECTION("width = 10, fill = *, align = right")
{
os << "|" << std::setw(10) << std::setfill('*') << std::right << 123 * isq::length[m] << "|";
CHECK(os.str() == "|*****123 m|");
}
SECTION("width = 10, fill = *, align = left")
{
os << "|" << std::setw(10) << std::setfill('*') << std::left << 123 * isq::length[m] << "|";
CHECK(os.str() == "|123 m*****|");
}
}
SECTION("default format {} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("|{:0}|", 123 * isq::length[m]) == "|123 m|");
CHECK(MP_UNITS_STD_FMT::format("|{:10}|", 123 * isq::length[m]) == "| 123 m|");
CHECK(MP_UNITS_STD_FMT::format("|{:<10}|", 123 * isq::length[m]) == "|123 m |");
CHECK(MP_UNITS_STD_FMT::format("|{:>10}|", 123 * isq::length[m]) == "| 123 m|");
CHECK(MP_UNITS_STD_FMT::format("|{:^10}|", 123 * isq::length[m]) == "| 123 m |");
CHECK(MP_UNITS_STD_FMT::format("|{:*<10}|", 123 * isq::length[m]) == "|123 m*****|");
CHECK(MP_UNITS_STD_FMT::format("|{:*>10}|", 123 * isq::length[m]) == "|*****123 m|");
CHECK(MP_UNITS_STD_FMT::format("|{:*^10}|", 123 * isq::length[m]) == "|**123 m***|");
}
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("|{:0%Q%q}|", 123 * isq::length[m]) == "|123m|");
CHECK(MP_UNITS_STD_FMT::format("|{:10%Q%q}|", 123 * isq::length[m]) == "| 123m|");
CHECK(MP_UNITS_STD_FMT::format("|{:<10%Q%q}|", 123 * isq::length[m]) == "|123m |");
CHECK(MP_UNITS_STD_FMT::format("|{:>10%Q%q}|", 123 * isq::length[m]) == "| 123m|");
CHECK(MP_UNITS_STD_FMT::format("|{:^10%Q%q}|", 123 * isq::length[m]) == "| 123m |");
CHECK(MP_UNITS_STD_FMT::format("|{:*<10%Q%q}|", 123 * isq::length[m]) == "|123m******|");
CHECK(MP_UNITS_STD_FMT::format("|{:*>10%Q%q}|", 123 * isq::length[m]) == "|******123m|");
CHECK(MP_UNITS_STD_FMT::format("|{:*^10%Q%q}|", 123 * isq::length[m]) == "|***123m***|");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("|{:0%Q}|", 123 * isq::length[m]) == "|123|");
CHECK(MP_UNITS_STD_FMT::format("|{:10%Q}|", 123 * isq::length[m]) == "| 123|");
CHECK(MP_UNITS_STD_FMT::format("|{:<10%Q}|", 123 * isq::length[m]) == "|123 |");
CHECK(MP_UNITS_STD_FMT::format("|{:>10%Q}|", 123 * isq::length[m]) == "| 123|");
CHECK(MP_UNITS_STD_FMT::format("|{:^10%Q}|", 123 * isq::length[m]) == "| 123 |");
CHECK(MP_UNITS_STD_FMT::format("|{:*<10%Q}|", 123 * isq::length[m]) == "|123*******|");
CHECK(MP_UNITS_STD_FMT::format("|{:*>10%Q}|", 123 * isq::length[m]) == "|*******123|");
CHECK(MP_UNITS_STD_FMT::format("|{:*^10%Q}|", 123 * isq::length[m]) == "|***123****|");
}
SECTION("symbol only format {:%q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("|{:0%q}|", 123 * isq::length[m]) == "|m|");
CHECK(MP_UNITS_STD_FMT::format("|{:10%q}|", 123 * isq::length[m]) == "| m|");
CHECK(MP_UNITS_STD_FMT::format("|{:<10%q}|", 123 * isq::length[m]) == "|m |");
CHECK(MP_UNITS_STD_FMT::format("|{:>10%q}|", 123 * isq::length[m]) == "| m|");
CHECK(MP_UNITS_STD_FMT::format("|{:^10%q}|", 123 * isq::length[m]) == "| m |");
CHECK(MP_UNITS_STD_FMT::format("|{:*<10%q}|", 123 * isq::length[m]) == "|m*********|");
CHECK(MP_UNITS_STD_FMT::format("|{:*>10%q}|", 123 * isq::length[m]) == "|*********m|");
CHECK(MP_UNITS_STD_FMT::format("|{:*^10%q}|", 123 * isq::length[m]) == "|****m*****|");
}
}
TEST_CASE("sign specification", "[text][fmt]")
{
auto inf = std::numeric_limits<double>::infinity() * si::metre;
auto nan = std::numeric_limits<double>::quiet_NaN() * si::metre;
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", 1 * isq::length[m]) == "1m,+1m,1m, 1m");
CHECK(MP_UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", -1 * isq::length[m]) == "-1m,-1m,-1m,-1m");
CHECK(MP_UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", inf) == "infm,+infm,infm, infm");
CHECK(MP_UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", nan) == "nanm,+nanm,nanm, nanm");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", 1 * isq::length[m]) == "1,+1,1, 1");
CHECK(MP_UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", -1 * isq::length[m]) == "-1,-1,-1,-1");
CHECK(MP_UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", inf) == "inf,+inf,inf, inf");
CHECK(MP_UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", nan) == "nan,+nan,nan, nan");
}
}
TEST_CASE("precision specification", "[text][fmt]")
{
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%.0Q %q}", 1.2345 * isq::length[m]) == "1 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.1Q %q}", 1.2345 * isq::length[m]) == "1.2 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.2Q %q}", 1.2345 * isq::length[m]) == "1.23 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.3Q %q}", 1.2345 * isq::length[m]) == "1.234 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.4Q %q}", 1.2345 * isq::length[m]) == "1.2345 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.5Q %q}", 1.2345 * isq::length[m]) == "1.23450 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.10Q %q}", 1.2345 * isq::length[m]) == "1.2345000000 m");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%.0Q}", 1.2345 * isq::length[m]) == "1");
CHECK(MP_UNITS_STD_FMT::format("{:%.1Q}", 1.2345 * isq::length[m]) == "1.2");
CHECK(MP_UNITS_STD_FMT::format("{:%.2Q}", 1.2345 * isq::length[m]) == "1.23");
CHECK(MP_UNITS_STD_FMT::format("{:%.3Q}", 1.2345 * isq::length[m]) == "1.234");
CHECK(MP_UNITS_STD_FMT::format("{:%.4Q}", 1.2345 * isq::length[m]) == "1.2345");
CHECK(MP_UNITS_STD_FMT::format("{:%.5Q}", 1.2345 * isq::length[m]) == "1.23450");
CHECK(MP_UNITS_STD_FMT::format("{:%.10Q}", 1.2345 * isq::length[m]) == "1.2345000000");
}
}
TEST_CASE("precision specification for integral representation should throw", "[text][fmt][exception]")
{
auto q = 1 * isq::length[m];
SECTION("full format {:%Q %q} on a quantity")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%.1Q %q}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("precision not allowed for integral quantity representation"));
}
SECTION("value only format {:%Q} on a quantity")
{
REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:%.1Q}", MP_UNITS_STD_FMT::make_format_args(q)),
MP_UNITS_STD_FMT::format_error,
Catch::Matchers::Message("precision not allowed for integral quantity representation"));
}
}
TEST_CASE("type specification", "[text][fmt]")
{
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%bQ %q}", 42 * isq::length[m]) == "101010 m");
CHECK(MP_UNITS_STD_FMT::format("{:%BQ %q}", 42 * isq::length[m]) == "101010 m");
CHECK(MP_UNITS_STD_FMT::format("{:%dQ %q}", 42 * isq::length[m]) == "42 m");
CHECK(MP_UNITS_STD_FMT::format("{:%oQ %q}", 42 * isq::length[m]) == "52 m");
CHECK(MP_UNITS_STD_FMT::format("{:%xQ %q}", 42 * isq::length[m]) == "2a m");
CHECK(MP_UNITS_STD_FMT::format("{:%XQ %q}", 42 * isq::length[m]) == "2A m");
CHECK(MP_UNITS_STD_FMT::format("{:%aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.3aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m");
CHECK(MP_UNITS_STD_FMT::format("{:%AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.3AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m");
CHECK(MP_UNITS_STD_FMT::format("{:%eQ %q}", 1.2345678 * isq::length[m]) == "1.234568e+00 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.3eQ %q}", 1.2345678 * isq::length[m]) == "1.235e+00 m");
CHECK(MP_UNITS_STD_FMT::format("{:%EQ %q}", 1.2345678 * isq::length[m]) == "1.234568E+00 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.3EQ %q}", 1.2345678 * isq::length[m]) == "1.235E+00 m");
CHECK(MP_UNITS_STD_FMT::format("{:%gQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m");
CHECK(MP_UNITS_STD_FMT::format("{:%gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.3gQ %q}", 1.2345678 * isq::length[m]) == "1.23 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.3gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m");
CHECK(MP_UNITS_STD_FMT::format("{:%GQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m");
CHECK(MP_UNITS_STD_FMT::format("{:%GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.3GQ %q}", 1.2345678 * isq::length[m]) == "1.23 m");
CHECK(MP_UNITS_STD_FMT::format("{:%.3GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%bQ}", 42 * isq::length[m]) == "101010");
CHECK(MP_UNITS_STD_FMT::format("{:%BQ}", 42 * isq::length[m]) == "101010");
CHECK(MP_UNITS_STD_FMT::format("{:%dQ}", 42 * isq::length[m]) == "42");
CHECK(MP_UNITS_STD_FMT::format("{:%oQ}", 42 * isq::length[m]) == "52");
CHECK(MP_UNITS_STD_FMT::format("{:%xQ}", 42 * isq::length[m]) == "2a");
CHECK(MP_UNITS_STD_FMT::format("{:%XQ}", 42 * isq::length[m]) == "2A");
CHECK(MP_UNITS_STD_FMT::format("{:%aQ}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0");
CHECK(MP_UNITS_STD_FMT::format("{:%.3aQ}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0");
CHECK(MP_UNITS_STD_FMT::format("{:%AQ}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0");
CHECK(MP_UNITS_STD_FMT::format("{:%.3AQ}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0");
CHECK(MP_UNITS_STD_FMT::format("{:%eQ}", 1.2345678 * isq::length[m]) == "1.234568e+00");
CHECK(MP_UNITS_STD_FMT::format("{:%.3eQ}", 1.2345678 * isq::length[m]) == "1.235e+00");
CHECK(MP_UNITS_STD_FMT::format("{:%EQ}", 1.2345678 * isq::length[m]) == "1.234568E+00");
CHECK(MP_UNITS_STD_FMT::format("{:%.3EQ}", 1.2345678 * isq::length[m]) == "1.235E+00");
CHECK(MP_UNITS_STD_FMT::format("{:%gQ}", 1.2345678 * isq::length[m]) == "1.23457");
CHECK(MP_UNITS_STD_FMT::format("{:%gQ}", 1.2345678e8 * isq::length[m]) == "1.23457e+08");
CHECK(MP_UNITS_STD_FMT::format("{:%.3gQ}", 1.2345678 * isq::length[m]) == "1.23");
CHECK(MP_UNITS_STD_FMT::format("{:%.3gQ}", 1.2345678e8 * isq::length[m]) == "1.23e+08");
CHECK(MP_UNITS_STD_FMT::format("{:%GQ}", 1.2345678 * isq::length[m]) == "1.23457");
CHECK(MP_UNITS_STD_FMT::format("{:%GQ}", 1.2345678e8 * isq::length[m]) == "1.23457E+08");
CHECK(MP_UNITS_STD_FMT::format("{:%.3GQ}", 1.2345678 * isq::length[m]) == "1.23");
CHECK(MP_UNITS_STD_FMT::format("{:%.3GQ}", 1.2345678e8 * isq::length[m]) == "1.23E+08");
}
}
TEST_CASE("different base types with the # specifier", "[text][fmt]")
{
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%#bQ %q}", 42 * isq::length[m]) == "0b101010 m");
CHECK(MP_UNITS_STD_FMT::format("{:%#BQ %q}", 42 * isq::length[m]) == "0B101010 m");
CHECK(MP_UNITS_STD_FMT::format("{:%#oQ %q}", 42 * isq::length[m]) == "052 m");
CHECK(MP_UNITS_STD_FMT::format("{:%#xQ %q}", 42 * isq::length[m]) == "0x2a m");
CHECK(MP_UNITS_STD_FMT::format("{:%#XQ %q}", 42 * isq::length[m]) == "0X2A m");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format("{:%#bQ}", 42 * isq::length[m]) == "0b101010");
CHECK(MP_UNITS_STD_FMT::format("{:%#BQ}", 42 * isq::length[m]) == "0B101010");
CHECK(MP_UNITS_STD_FMT::format("{:%#oQ}", 42 * isq::length[m]) == "052");
CHECK(MP_UNITS_STD_FMT::format("{:%#xQ}", 42 * isq::length[m]) == "0x2a");
CHECK(MP_UNITS_STD_FMT::format("{:%#XQ}", 42 * isq::length[m]) == "0X2A");
}
}
TEST_CASE("localization with the 'L' specifier", "[text][fmt][localization]")
{
struct group2 : std::numpunct<char> {
char do_thousands_sep() const override { return '_'; }
std::string do_grouping() const override { return "\2"; }
};
struct group3 : std::numpunct<char> {
char do_thousands_sep() const override { return '\''; }
std::string do_grouping() const override { return "\3"; }
};
std::locale grp2{std::locale::classic(), new group2};
std::locale grp3{std::locale::classic(), new group3};
SECTION("full format {:%LQ %q} on a quantity")
{
CHECK(MP_UNITS_STD_FMT::format(grp2, "{:%LQ %q}", 299'792'458 * isq::speed[m / s]) == "2_99_79_24_58 m/s");
CHECK(MP_UNITS_STD_FMT::format(grp3, "{:%LQ %q}", 299'792'458 * isq::speed[m / s]) == "299'792'458 m/s");
}
}
TEST_CASE("value_cast", "[text][ostream]")
{
std::ostringstream os;
SECTION("int to double representation")
{
const auto q = 121 * isq::length[km] / (2 * isq::time[h]);
SECTION("original")
{
os << q;
CHECK(os.str() == "60 km/h");
}
SECTION("int")
{
os << value_cast<int>(q);
CHECK(os.str() == "60 km/h");
}
SECTION("double")
{
os << value_cast<double>(q);
CHECK(os.str() == "60 km/h");
}
}
SECTION("double to int representation")
{
const auto q = 121. * isq::length[km] / (2 * isq::time[h]);
SECTION("original")
{
os << q;
CHECK(os.str() == "60.5 km/h");
}
SECTION("int")
{
os << value_cast<int>(q);
CHECK(os.str() == "60 km/h");
}
SECTION("double")
{
os << value_cast<double>(q);
CHECK(os.str() == "60.5 km/h");
}
}
}
+499
View File
@@ -0,0 +1,499 @@
// 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 <catch2/catch_test_macros.hpp>
#include <mp-units/compat_fmt_macros.h>
#include <cmath>
#include <matrix>
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/customization_points.h>
#include <mp-units/format.h>
#include <mp-units/math.h>
#include <mp-units/ostream.h>
#include <mp-units/systems/isq/mechanics.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/si.h>
#endif
template<typename Rep = double>
using vector = STD_LA::fixed_size_column_vector<Rep, 3>;
template<class Rep>
inline constexpr bool mp_units::treat_as_floating_point<vector<Rep>> = mp_units::treat_as_floating_point<Rep>;
template<typename Rep>
inline constexpr bool mp_units::is_vector<vector<Rep>> = true;
template<typename Rep>
std::ostream& operator<<(std::ostream& os, const vector<Rep>& v)
{
os << "|";
for (auto i = 0U; i < v.size(); ++i) {
os << MP_UNITS_STD_FMT::format(" {:>9}", v(i));
}
os << " |";
return os;
}
namespace {
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
template<QuantitySpec auto QS, QuantityOf<QS> Q>
requires(Q::quantity_spec.character == quantity_character::vector) && (QS.character == quantity_character::scalar)
[[nodiscard]] constexpr QuantityOf<QS> auto get_magnitude(const Q& q)
{
const auto& v = q.numerical_value_ref_in(q.unit);
return hypot(v(0) * QS[Q::unit], v(1) * QS[Q::unit], v(2) * QS[Q::unit]);
}
template<QuantitySpec auto QS, QuantityOf<QS> T>
requires(T::quantity_spec.character == quantity_character::vector) && (QS.character == quantity_character::scalar)
[[nodiscard]] constexpr QuantityOf<QS> auto get_magnitude(const vector<T>& v)
{
return hypot(QS(v(0)), QS(v(1)), QS(v(2)));
}
template<typename T, typename U>
[[nodiscard]] constexpr vector<decltype(T{} * U{})> cross_product(const vector<T>& a, const vector<U>& b)
{
return {a(1) * b(2) - a(2) * b(1), a(2) * b(0) - a(0) * b(2), a(0) * b(1) - a(1) * b(0)};
}
template<Quantity Q1, Quantity Q2>
requires is_vector<typename Q1::rep> && is_vector<typename Q2::rep> &&
requires(typename Q1::rep v1, typename Q2::rep v2) { cross_product(v1, v2); }
[[nodiscard]] constexpr QuantityOf<Q1::quantity_spec * Q2::quantity_spec> auto cross_product(const Q1& q1, const Q2& q2)
{
return cross_product(q1.numerical_value_ref_in(q1.unit), q2.numerical_value_ref_in(q2.unit)) *
(Q1::reference * Q2::reference);
}
} // namespace
TEST_CASE("vector quantity", "[la]")
{
SECTION("cast of unit")
{
SECTION("non-truncating")
{
const auto v = vector<int>{3, 2, 1} * isq::position_vector[km];
CHECK(v.numerical_value_in(m) == vector<int>{3000, 2000, 1000});
}
SECTION("truncating")
{
const auto v = vector<int>{1001, 1002, 1003} * isq::position_vector[m];
CHECK(v.force_numerical_value_in(km) == vector<int>{1, 1, 1});
}
}
SECTION("to scalar magnitude")
{
const auto v = vector<int>{2, 3, 6} * isq::velocity[km / h];
const auto speed = get_magnitude<isq::speed>(v);
CHECK(speed.numerical_value_ref_in(km / h) == 7);
}
SECTION("multiply by scalar value")
{
const auto v = vector<int>{1, 2, 3} * isq::position_vector[m];
SECTION("integral")
{
SECTION("scalar on LHS") { CHECK((2 * v).numerical_value_in(m) == vector<int>{2, 4, 6}); }
SECTION("scalar on RHS") { CHECK((v * 2).numerical_value_in(m) == vector<int>{2, 4, 6}); }
}
SECTION("floating-point")
{
SECTION("scalar on LHS") { CHECK((0.5 * v).numerical_value_in(m) == vector<double>{0.5, 1., 1.5}); }
SECTION("scalar on RHS") { CHECK((v * 0.5).numerical_value_in(m) == vector<double>{0.5, 1., 1.5}); }
}
}
SECTION("divide by scalar value")
{
const auto v = vector<int>{2, 4, 6} * isq::position_vector[m];
SECTION("integral") { CHECK((v / 2).numerical_value_in(m) == vector<int>{1, 2, 3}); }
SECTION("floating-point") { CHECK((v / 0.5).numerical_value_in(m) == vector<double>{4., 8., 12.}); }
}
SECTION("add")
{
const auto v = vector<int>{1, 2, 3} * isq::position_vector[m];
SECTION("same unit")
{
const auto u = vector<int>{3, 2, 1} * isq::position_vector[m];
CHECK((v + u).numerical_value_in(m) == vector<int>{4, 4, 4});
}
SECTION("different units")
{
const auto u = vector<int>{3, 2, 1} * isq::position_vector[km];
CHECK((v + u).numerical_value_in(m) == vector<int>{3001, 2002, 1003});
}
}
SECTION("subtract")
{
const auto v = vector<int>{1, 2, 3} * isq::position_vector[m];
SECTION("same unit")
{
const auto u = vector<int>{3, 2, 1} * isq::position_vector[m];
CHECK((v - u).numerical_value_in(m) == vector<int>{-2, 0, 2});
}
SECTION("different units")
{
const auto u = vector<int>{3, 2, 1} * isq::position_vector[km];
CHECK((v - u).numerical_value_in(m) == vector<int>{-2999, -1998, -997});
}
}
SECTION("multiply by scalar quantity")
{
const auto v = vector<int>{1, 2, 3} * isq::velocity[m / s];
SECTION("integral")
{
const auto mass = 2 * isq::mass[kg];
SECTION("derived_quantity_spec")
{
SECTION("scalar on LHS") { CHECK((mass * v).numerical_value_in(kg * m / s) == vector<int>{2, 4, 6}); }
SECTION("scalar on RHS") { CHECK((v * mass).numerical_value_in(kg * m / s) == vector<int>{2, 4, 6}); }
}
SECTION("quantity_cast to momentum")
{
SECTION("scalar on LHS")
{
CHECK(quantity_cast<isq::momentum>(mass * v).numerical_value_in(N * s) == vector<int>{2, 4, 6});
}
SECTION("scalar on RHS")
{
CHECK(quantity_cast<isq::momentum>(v * mass).numerical_value_in(N * s) == vector<int>{2, 4, 6});
}
}
SECTION("quantity of momentum")
{
SECTION("scalar on LHS")
{
const quantity<isq::momentum[N * s], vector<int>> momentum = mass * v;
CHECK(momentum.numerical_value_ref_in(N * s) == vector<int>{2, 4, 6});
}
SECTION("scalar on RHS")
{
const quantity<isq::momentum[N * s], vector<int>> momentum = v * mass;
CHECK(momentum.numerical_value_ref_in(N * s) == vector<int>{2, 4, 6});
}
}
}
SECTION("floating-point")
{
const auto mass = 0.5 * isq::mass[kg];
SECTION("derived_quantity_spec")
{
SECTION("scalar on LHS") { CHECK((mass * v).numerical_value_in(kg * m / s) == vector<double>{0.5, 1., 1.5}); }
SECTION("scalar on RHS") { CHECK((v * mass).numerical_value_in(kg * m / s) == vector<double>{0.5, 1., 1.5}); }
}
SECTION("quantity_cast to momentum")
{
SECTION("scalar on LHS")
{
CHECK(quantity_cast<isq::momentum>(mass * v).numerical_value_in(N * s) == vector<double>{0.5, 1., 1.5});
}
SECTION("scalar on RHS")
{
CHECK(quantity_cast<isq::momentum>(v * mass).numerical_value_in(N * s) == vector<double>{0.5, 1., 1.5});
}
}
SECTION("quantity of momentum")
{
SECTION("scalar on LHS")
{
const quantity<isq::momentum[N * s], vector<double>> momentum = mass * v;
CHECK(momentum.numerical_value_ref_in(N * s) == vector<double>{0.5, 1., 1.5});
}
SECTION("scalar on RHS")
{
const quantity<isq::momentum[N * s], vector<double>> momentum = v * mass;
CHECK(momentum.numerical_value_ref_in(N * s) == vector<double>{0.5, 1., 1.5});
}
}
}
}
SECTION("divide by scalar quantity")
{
const auto pos = vector<int>{30, 20, 10} * isq::position_vector[km];
SECTION("integral")
{
const auto dur = 2 * isq::duration[h];
SECTION("derived_quantity_spec") { CHECK((pos / dur).numerical_value_in(km / h) == vector<int>{15, 10, 5}); }
SECTION("quantity_cast to velocity")
{
CHECK(quantity_cast<isq::velocity>(pos / dur).numerical_value_in(km / h) == vector<int>{15, 10, 5});
}
SECTION("quantity of velocity")
{
const quantity<isq::velocity[km / h], vector<int>> v = pos / dur;
CHECK(v.numerical_value_ref_in(km / h) == vector<int>{15, 10, 5});
}
}
SECTION("floating-point")
{
const auto dur = 0.5 * isq::duration[h];
SECTION("derived_quantity_spec") { CHECK((pos / dur).numerical_value_in(km / h) == vector<double>{60, 40, 20}); }
SECTION("quantity_cast to velocity")
{
CHECK(quantity_cast<isq::velocity>(pos / dur).numerical_value_in(km / h) == vector<double>{60, 40, 20});
}
SECTION("quantity of velocity")
{
const quantity<isq::velocity[km / h], vector<double>> v = pos / dur;
CHECK(v.numerical_value_ref_in(km / h) == vector<double>{60, 40, 20});
}
}
}
SECTION("cross product with a vector quantity")
{
const auto r = vector<int>{3, 0, 0} * isq::position_vector[m];
const auto f = vector<int>{0, 10, 0} * isq::force[N];
CHECK(cross_product(r, f) == vector<int>{0, 0, 30} * isq::moment_of_force[N * m]);
}
}
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
TEST_CASE("vector of quantities", "[la]")
{
SECTION("cast of unit")
{
SECTION("non-truncating")
{
const vector<quantity<isq::position_vector[km], int>> v = {3 * km, 2 * km, 1 * km};
CHECK(vector<quantity<isq::position_vector[m], int>>(v) ==
vector<quantity<isq::position_vector[m], int>>{3000 * m, 2000 * m, 1000 * m});
}
// truncating not possible (no way to apply quantity_cast to sub-components of a vector)
}
SECTION("to scalar magnitude")
{
const vector<quantity<isq::velocity[km / h], int>> v = {2 * km / h, 3 * km / h, 6 * km / h};
const auto speed = get_magnitude<isq::speed>(v);
CHECK(speed.numerical_value_ref_in(km / h) == 7);
}
SECTION("multiply by scalar value")
{
const vector<quantity<isq::position_vector[m], int>> v = {1 * m, 2 * m, 3 * m};
SECTION("integral")
{
const vector<quantity<isq::position_vector[m], int>> result = {2 * m, 4 * m, 6 * m};
SECTION("scalar on LHS") { CHECK(2 * v == result); }
SECTION("scalar on RHS") { CHECK(v * 2 == result); }
}
SECTION("floating-point")
{
const vector<quantity<isq::position_vector[m], double>> result = {0.5 * m, 1. * m, 1.5 * m};
SECTION("scalar on LHS") { CHECK(0.5 * v == result); }
SECTION("scalar on RHS") { CHECK(v * 0.5 == result); }
}
}
SECTION("divide by scalar value")
{
const vector<quantity<isq::position_vector[m], int>> v = {2 * m, 4 * m, 6 * m};
SECTION("integral") { CHECK(v / 2 == vector<quantity<isq::position_vector[m], int>>{1 * m, 2 * m, 3 * m}); }
SECTION("floating-point")
{
CHECK(v / 0.5 == vector<quantity<isq::position_vector[m], double>>{4. * m, 8. * m, 12. * m});
}
}
SECTION("add")
{
const vector<quantity<isq::position_vector[m], int>> v = {1 * m, 2 * m, 3 * m};
SECTION("same unit")
{
const vector<quantity<isq::position_vector[m], int>> u = {3 * m, 2 * m, 1 * m};
CHECK(v + u == vector<quantity<isq::position_vector[m], int>>{4 * m, 4 * m, 4 * m});
}
SECTION("different units")
{
const vector<quantity<isq::position_vector[km], int>> u = {3 * km, 2 * km, 1 * km};
CHECK(v + u == vector<quantity<isq::position_vector[m], int>>{3001 * m, 2002 * m, 1003 * m});
}
}
SECTION("subtract")
{
const vector<quantity<isq::position_vector[m], int>> v = {1 * m, 2 * m, 3 * m};
SECTION("same unit")
{
const vector<quantity<isq::position_vector[m], int>> u = {3 * m, 2 * m, 1 * m};
CHECK(v - u == vector<quantity<isq::position_vector[m], int>>{-2 * m, 0 * m, 2 * m});
}
SECTION("different units")
{
const vector<quantity<isq::position_vector[km], int>> u = {3 * km, 2 * km, 1 * km};
CHECK(v - u == vector<quantity<isq::position_vector[m], int>>{-2999 * m, -1998 * m, -997 * m});
}
}
SECTION("multiply by scalar quantity")
{
const vector<quantity<isq::velocity[m / s], int>> v = {1 * m / s, 2 * m / s, 3 * m / s};
SECTION("integral")
{
const auto mass = 2 * isq::mass[kg];
const auto result = vector<quantity<isq::momentum[N * s], int>>{2 * N * s, 4 * N * s, 6 * N * s};
SECTION("derived_quantity_spec")
{
SECTION("scalar on LHS") { CHECK(mass * v == result); }
SECTION("scalar on RHS") { CHECK(v * mass == result); }
}
// no way to apply quantity_cast to sub-components
SECTION("quantity of momentum")
{
SECTION("scalar on LHS")
{
const vector<quantity<isq::momentum[N * s], int>> momentum = mass * v;
CHECK(momentum == result);
}
SECTION("scalar on RHS")
{
const vector<quantity<isq::momentum[N * s], int>> momentum = v * mass;
CHECK(momentum == result);
}
}
}
SECTION("floating-point")
{
const auto mass = 0.5 * isq::mass[kg];
const auto result = vector<quantity<isq::momentum[N * s], double>>{0.5 * N * s, 1. * N * s, 1.5 * N * s};
SECTION("derived_quantity_spec")
{
SECTION("scalar on LHS") { CHECK(mass * v == result); }
SECTION("scalar on RHS") { CHECK(v * mass == result); }
}
// no way to apply quantity_cast to sub-components
SECTION("quantity of momentum")
{
SECTION("scalar on LHS")
{
const vector<quantity<isq::momentum[N * s], double>> momentum = mass * v;
CHECK(momentum == result);
}
SECTION("scalar on RHS")
{
const vector<quantity<isq::momentum[N * s], double>> momentum = v * mass;
CHECK(momentum == result);
}
}
}
}
SECTION("divide by scalar quantity")
{
const vector<quantity<isq::position_vector[km], int>> pos = {30 * km, 20 * km, 10 * km};
SECTION("integral")
{
const auto dur = 2 * isq::duration[h];
SECTION("derived_quantity_spec")
{
CHECK(pos / dur == vector<quantity<isq::velocity[km / h], int>>{15 * km / h, 10 * km / h, 5 * km / h});
}
// no way to apply quantity_cast to sub-components
SECTION("quantity of velocity")
{
const vector<quantity<isq::velocity[km / h], int>> v = pos / dur;
CHECK(v == vector<quantity<isq::velocity[km / h], int>>{15 * km / h, 10 * km / h, 5 * km / h});
}
}
SECTION("floating-point")
{
const auto dur = 0.5 * isq::duration[h];
SECTION("derived_quantity_spec")
{
CHECK(pos / dur == vector<quantity<isq::velocity[km / h], double>>{60. * km / h, 40. * km / h, 20. * km / h});
}
// no way to apply quantity_cast to sub-components
SECTION("quantity of velocity")
{
const vector<quantity<isq::velocity[km / h], double>> v = pos / dur;
CHECK(v == vector<quantity<isq::velocity[km / h], double>>{60. * km / h, 40. * km / h, 20. * km / h});
}
}
}
SECTION("cross product with a vector of quantities")
{
const vector<quantity<isq::position_vector[m], int>> r = {3 * m, 0 * m, 0 * m};
const vector<quantity<isq::force[N], int>> f = {0 * N, 10 * N, 0 * N};
CHECK(cross_product(r, f) == vector<quantity<isq::moment_of_force[N * m], int>>{0 * N * m, 0 * N * m, 30 * N * m});
}
}
+451
View File
@@ -0,0 +1,451 @@
// 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_all.hpp>
#include <limits>
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/math.h>
#include <mp-units/ostream.h>
#include <mp-units/systems/angular/angular.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/si.h>
#endif
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
// classical
TEST_CASE("'pow<N>()' on quantity changes the value and the dimension accordingly", "[math][pow]")
{
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]);
}
}
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("'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("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));
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));
}
}
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]));
}
}