From 0e7314e3a63eff9c5cc280ec0ffd06d9eb83de10 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 08:50:11 +0100 Subject: [PATCH 01/14] fix: math tests for `inverse` fixed after change in `make_reference` --- test/static/math_test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/static/math_test.cpp b/test/static/math_test.cpp index a573fd1f..48682ee8 100644 --- a/test/static/math_test.cpp +++ b/test/static/math_test.cpp @@ -237,17 +237,17 @@ static_assert(compare(round(-1999. * isq::time[ms]), -2. * isq::time #endif // non-truncating -static_assert(compare(inverse(250 * Hz), 4000 * us)); -static_assert(compare(inverse(250 * kHz), 4 * us)); -static_assert(compare(inverse(250 * uHz), 4 * ks)); +static_assert(compare(kind_of(inverse(250 * Hz)), 4000 * us)); +static_assert(compare(kind_of(inverse(250 * kHz)), 4 * us)); +static_assert(compare(kind_of(inverse(250 * uHz)), 4 * ks)); // truncating -static_assert(compare(inverse(1 * kHz), 0 * s)); +static_assert(compare(kind_of(inverse(1 * kHz)), 0 * s)); // floating-point representation does not truncate -static_assert(compare(inverse(1. * kHz), 0.001 * s)); +static_assert(compare(kind_of(inverse(1. * kHz)), 0.001 * s)); // check if constraints work properly for a derived unit of a narrowed kind -static_assert(compare(inverse(1 * s), 1 * Hz)); +static_assert(compare(kind_of(inverse(1 * s)), 1 * Hz)); } // namespace From f368ef5c832ceeb41a6e402f259c7702879255f7 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 09:01:50 +0100 Subject: [PATCH 02/14] style: trailing whitespaces fixed in ISQ Part 6 --- docs/blog/posts/isq-part-6-challenges.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/blog/posts/isq-part-6-challenges.md b/docs/blog/posts/isq-part-6-challenges.md index 9058e82b..45674e30 100644 --- a/docs/blog/posts/isq-part-6-challenges.md +++ b/docs/blog/posts/isq-part-6-challenges.md @@ -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. @@ -490,5 +490,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. From 123b4c0f140a2d9b67d89230209e488889931198 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 09:53:31 +0100 Subject: [PATCH 03/14] docs: "The same or a different kind?" of ISQ Part 6 extended --- docs/blog/posts/isq-part-6-challenges.md | 46 +++++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/docs/blog/posts/isq-part-6-challenges.md b/docs/blog/posts/isq-part-6-challenges.md index 45674e30..f658813e 100644 --- a/docs/blog/posts/isq-part-6-challenges.md +++ b/docs/blog/posts/isq-part-6-challenges.md @@ -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["electromagnetism_power | instantaneous_power
(instantaneous_voltage * instantaneous_electric_current)"] power --- active_power["active_power
(1 / period * instantaneous_power * time)
(re(complex_power))
"] - apparent_power["apparent_power
(voltage * electric_current)
(mod(complex_power))

[VA]"] + nonactive_power["nonactive_power
(mass * length2 / time3)
[VA]"] + nonactive_power --- reactive_power["reactive_power
(im(complex_power))
[var]"] + + complex_power["complex_power
{complex}
(voltage_phasor * electric_current_phasor)
(active_power + j * reactive_power)

[VA]"] + complex_power --- apparent_power["apparent_power
(voltage * electric_current)
(mod(complex_power))
"] +``` + +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 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 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["power
(mass * length2 / time3)
[W]"] + power --- mechanical_power["mechanical_power
(scalar_product(force, velocity))"] + power --- electromagnetism_power["electromagnetism_power | instantaneous_power
(instantaneous_voltage * instantaneous_electric_current)"] + power --- apparent_power["apparent_power
(voltage * electric_current)
(mod(complex_power))

[VA]"] + apparent_power --- active_power["active_power
(1 / period * instantaneous_power * time)
(re(complex_power))
"] apparent_power --- nonactive_power["nonactive_power
(sqrt(apparent_power2 - active_power2))
"] nonactive_power --- reactive_power["reactive_power
(im(complex_power))
[var]"] apparent_power --- complex_power["complex_power
{complex}
(voltage_phasor * electric_current_phasor)
(active_power + j * reactive_power)
"] ``` -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? From 78204c7e5f81b148657eb470ef43e1ab9df6cfb7 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 11:27:52 +0100 Subject: [PATCH 04/14] test: runtime unit tests refactored to have a bigger granularity (less top level tests) --- test/runtime/distribution_test.cpp | 949 +++++++++++------------ test/runtime/fixed_string_test.cpp | 25 +- test/runtime/fmt_test.cpp | 1138 ++++++++++++++-------------- test/runtime/math_test.cpp | 876 ++++++++++----------- test/runtime/quantity_test.cpp | 35 +- 5 files changed, 1528 insertions(+), 1495 deletions(-) diff --git a/test/runtime/distribution_test.cpp b/test/runtime/distribution_test.cpp index 9df1eb3b..e18d5a9e 100644 --- a/test/runtime/distribution_test.cpp +++ b/test/runtime/distribution_test.cpp @@ -43,618 +43,621 @@ import mp_units; using namespace mp_units; -TEST_CASE("uniform_int_distribution") +TEST_CASE("distributions", "[random][distribution]") { - using rep = std::int64_t; - using q = quantity; - - SECTION("default") + SECTION("uniform_int_distribution") { - auto dist = mp_units::uniform_int_distribution(); + using rep = std::int64_t; + using q = quantity; - CHECK(dist.a() == q::zero()); - CHECK(dist.b() == q::max()); + SECTION("default") + { + auto dist = mp_units::uniform_int_distribution(); + + 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); + } } - SECTION("parametrized") + SECTION("uniform_real_distribution") { - constexpr rep a = 2; - constexpr rep b = 5; + using rep = long double; + using q = quantity; - auto stl_dist = std::uniform_int_distribution(a, b); - auto units_dist = mp_units::uniform_int_distribution(a * si::metre, b * si::metre); + SECTION("default") + { + auto dist = mp_units::uniform_real_distribution(); - 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); - } -} + CHECK(dist.a() == q::zero()); + CHECK(dist.b() == q::one()); + } -TEST_CASE("uniform_real_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr rep a = 2.0; + constexpr rep b = 5.0; - SECTION("default") - { - auto dist = mp_units::uniform_real_distribution(); + auto stl_dist = std::uniform_real_distribution(a, b); + auto units_dist = mp_units::uniform_real_distribution(a * si::metre, b * si::metre); - CHECK(dist.a() == q::zero()); - CHECK(dist.b() == q::one()); + 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); + } } - SECTION("parametrized") + SECTION("binomial_distribution") { - constexpr rep a = 2.0; - constexpr rep b = 5.0; + using rep = std::int64_t; + using q = quantity; - auto stl_dist = std::uniform_real_distribution(a, b); - auto units_dist = mp_units::uniform_real_distribution(a * si::metre, b * si::metre); + SECTION("default") + { + auto dist = mp_units::binomial_distribution(); - 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); - } -} + CHECK(dist.p() == 0.5); + CHECK(dist.t() == q::one()); + } -TEST_CASE("binomial_distribution") -{ - using rep = std::int64_t; - using q = quantity; + SECTION("parametrized") + { + constexpr rep t = 5; + constexpr double p = 0.25; - SECTION("default") - { - auto dist = mp_units::binomial_distribution(); + auto stl_dist = std::binomial_distribution(t, p); + auto units_dist = mp_units::binomial_distribution(t * si::metre, p); - CHECK(dist.p() == 0.5); - CHECK(dist.t() == q::one()); + 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); + } } - SECTION("parametrized") + SECTION("negative_binomial_distribution") { - constexpr rep t = 5; - constexpr double p = 0.25; + using rep = std::int64_t; + using q = quantity; - auto stl_dist = std::binomial_distribution(t, p); - auto units_dist = mp_units::binomial_distribution(t * si::metre, p); + SECTION("default") + { + auto dist = mp_units::negative_binomial_distribution(); - 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); - } -} + CHECK(dist.p() == 0.5); + CHECK(dist.k() == q::one()); + } -TEST_CASE("negative_binomial_distribution") -{ - using rep = std::int64_t; - using q = quantity; + SECTION("parametrized") + { + constexpr rep k = 5; + constexpr double p = 0.25; - SECTION("default") - { - auto dist = mp_units::negative_binomial_distribution(); + auto stl_dist = std::negative_binomial_distribution(k, p); + auto units_dist = mp_units::negative_binomial_distribution(k * si::metre, p); - CHECK(dist.p() == 0.5); - CHECK(dist.k() == q::one()); + 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); + } } - SECTION("parametrized") + SECTION("geometric_distribution") { - constexpr rep k = 5; - constexpr double p = 0.25; + using rep = std::int64_t; + using q = quantity; - auto stl_dist = std::negative_binomial_distribution(k, p); - auto units_dist = mp_units::negative_binomial_distribution(k * si::metre, p); + SECTION("default") + { + auto dist = mp_units::geometric_distribution(); - 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); - } -} + CHECK(dist.p() == 0.5); + } -TEST_CASE("geometric_distribution") -{ - using rep = std::int64_t; - using q = quantity; + SECTION("parametrized") + { + constexpr double p = 0.25; - SECTION("default") - { - auto dist = mp_units::geometric_distribution(); + auto stl_dist = std::geometric_distribution(p); + auto units_dist = mp_units::geometric_distribution(p); - CHECK(dist.p() == 0.5); + 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); + } } - SECTION("parametrized") + SECTION("poisson_distribution") { - constexpr double p = 0.25; + using rep = std::int64_t; + using q = quantity; - auto stl_dist = std::geometric_distribution(p); - auto units_dist = mp_units::geometric_distribution(p); + SECTION("default") + { + auto dist = mp_units::poisson_distribution(); - 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); - } -} + CHECK(dist.mean() == 1.0); + } -TEST_CASE("poisson_distribution") -{ - using rep = std::int64_t; - using q = quantity; + SECTION("parametrized") + { + constexpr double mean = 5.0; - SECTION("default") - { - auto dist = mp_units::poisson_distribution(); + auto stl_dist = std::poisson_distribution(mean); + auto units_dist = mp_units::poisson_distribution(mean); - CHECK(dist.mean() == 1.0); + 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); + } } - SECTION("parametrized") + SECTION("exponential_distribution") { - constexpr double mean = 5.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::poisson_distribution(mean); - auto units_dist = mp_units::poisson_distribution(mean); + SECTION("default") + { + auto dist = mp_units::exponential_distribution(); - 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); - } -} + CHECK(dist.lambda() == 1.0); + } -TEST_CASE("exponential_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr double lambda = 2.0; - SECTION("default") - { - auto dist = mp_units::exponential_distribution(); + auto stl_dist = std::exponential_distribution(lambda); + auto units_dist = mp_units::exponential_distribution(lambda); - CHECK(dist.lambda() == 1.0); + 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); + } } - SECTION("parametrized") + SECTION("gamma_distribution") { - constexpr double lambda = 2.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::exponential_distribution(lambda); - auto units_dist = mp_units::exponential_distribution(lambda); + SECTION("default") + { + auto dist = mp_units::gamma_distribution(); - 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); - } -} + CHECK(dist.alpha() == 1.0); + CHECK(dist.beta() == 1.0); + } -TEST_CASE("gamma_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr double alpha = 5.0; + constexpr double beta = 2.0; - SECTION("default") - { - auto dist = mp_units::gamma_distribution(); + auto stl_dist = std::gamma_distribution(alpha, beta); + auto units_dist = mp_units::gamma_distribution(alpha, beta); - CHECK(dist.alpha() == 1.0); - CHECK(dist.beta() == 1.0); + 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); + } } - SECTION("parametrized") + SECTION("weibull_distribution") { - constexpr double alpha = 5.0; - constexpr double beta = 2.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::gamma_distribution(alpha, beta); - auto units_dist = mp_units::gamma_distribution(alpha, beta); + SECTION("default") + { + auto dist = mp_units::weibull_distribution(); - 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); - } -} + CHECK(dist.a() == 1.0); + CHECK(dist.b() == 1.0); + } -TEST_CASE("weibull_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr rep a = 5.0; + constexpr rep b = 2.0; - SECTION("default") - { - auto dist = mp_units::weibull_distribution(); + auto stl_dist = std::weibull_distribution(a, b); + auto units_dist = mp_units::weibull_distribution(a, b); - CHECK(dist.a() == 1.0); - CHECK(dist.b() == 1.0); + 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); + } } - SECTION("parametrized") + SECTION("extreme_value_distribution") { - constexpr rep a = 5.0; - constexpr rep b = 2.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::weibull_distribution(a, b); - auto units_dist = mp_units::weibull_distribution(a, b); + SECTION("default") + { + auto dist = mp_units::extreme_value_distribution(); - 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); - } -} + CHECK(dist.a() == q::zero()); + CHECK(dist.b() == 1.0); + } -TEST_CASE("extreme_value_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr rep a = 5.0; + constexpr rep b = 2.0; - SECTION("default") - { - auto dist = mp_units::extreme_value_distribution(); + auto stl_dist = std::extreme_value_distribution(a, b); + auto units_dist = mp_units::extreme_value_distribution(a * si::metre, b); - CHECK(dist.a() == q::zero()); - CHECK(dist.b() == 1.0); + 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); + } } - SECTION("parametrized") + SECTION("normal_distribution") { - constexpr rep a = 5.0; - constexpr rep b = 2.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::extreme_value_distribution(a, b); - auto units_dist = mp_units::extreme_value_distribution(a * si::metre, b); + SECTION("default") + { + auto dist = mp_units::normal_distribution(); - 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); - } -} + CHECK(dist.mean() == q::zero()); + CHECK(dist.stddev() == q::one()); + } -TEST_CASE("normal_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr rep mean = 5.0; + constexpr rep stddev = 2.0; - SECTION("default") - { - auto dist = mp_units::normal_distribution(); + auto stl_dist = std::normal_distribution(mean, stddev); + auto units_dist = mp_units::normal_distribution(mean * si::metre, stddev * si::metre); - CHECK(dist.mean() == q::zero()); - CHECK(dist.stddev() == q::one()); + 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); + } } - SECTION("parametrized") + SECTION("lognormal_distribution") { - constexpr rep mean = 5.0; - constexpr rep stddev = 2.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::normal_distribution(mean, stddev); - auto units_dist = mp_units::normal_distribution(mean * si::metre, stddev * si::metre); + SECTION("default") + { + auto dist = mp_units::lognormal_distribution(); - 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); - } -} + CHECK(dist.m() == q::zero()); + CHECK(dist.s() == q::one()); + } -TEST_CASE("lognormal_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr rep m = 5.0; + constexpr rep s = 2.0; - SECTION("default") - { - auto dist = mp_units::lognormal_distribution(); + auto stl_dist = std::lognormal_distribution(m, s); + auto units_dist = mp_units::lognormal_distribution(m * si::metre, s * si::metre); - CHECK(dist.m() == q::zero()); - CHECK(dist.s() == q::one()); + 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); + } } - SECTION("parametrized") + SECTION("chi_squared_distribution") { - constexpr rep m = 5.0; - constexpr rep s = 2.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::lognormal_distribution(m, s); - auto units_dist = mp_units::lognormal_distribution(m * si::metre, s * si::metre); + SECTION("default") + { + auto dist = mp_units::chi_squared_distribution(); - 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); - } -} + CHECK(dist.n() == 1.0); + } -TEST_CASE("chi_squared_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr rep n = 5.0; - SECTION("default") - { - auto dist = mp_units::chi_squared_distribution(); + auto stl_dist = std::chi_squared_distribution(n); + auto units_dist = mp_units::chi_squared_distribution(n); - CHECK(dist.n() == 1.0); + 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); + } } - SECTION("parametrized") + SECTION("cauchy_distribution") { - constexpr rep n = 5.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::chi_squared_distribution(n); - auto units_dist = mp_units::chi_squared_distribution(n); + SECTION("default") + { + auto dist = mp_units::cauchy_distribution(); - 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); - } -} + CHECK(dist.a() == q::zero()); + CHECK(dist.b() == q::one()); + } -TEST_CASE("cauchy_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr rep a = 5.0; + constexpr rep b = 2.0; - SECTION("default") - { - auto dist = mp_units::cauchy_distribution(); + auto stl_dist = std::cauchy_distribution(a, b); + auto units_dist = mp_units::cauchy_distribution(a * si::metre, b * si::metre); - CHECK(dist.a() == q::zero()); - CHECK(dist.b() == q::one()); + 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); + } } - SECTION("parametrized") + SECTION("fisher_f_distribution") { - constexpr rep a = 5.0; - constexpr rep b = 2.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::cauchy_distribution(a, b); - auto units_dist = mp_units::cauchy_distribution(a * si::metre, b * si::metre); + SECTION("default") + { + auto dist = mp_units::fisher_f_distribution(); - 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); - } -} + CHECK(dist.m() == 1.0); + CHECK(dist.n() == 1.0); + } -TEST_CASE("fisher_f_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr rep m = 5.0; + constexpr rep n = 2.0; - SECTION("default") - { - auto dist = mp_units::fisher_f_distribution(); + auto stl_dist = std::fisher_f_distribution(m, n); + auto units_dist = mp_units::fisher_f_distribution(m, n); - CHECK(dist.m() == 1.0); - CHECK(dist.n() == 1.0); + 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); + } } - SECTION("parametrized") + SECTION("student_t_distribution") { - constexpr rep m = 5.0; - constexpr rep n = 2.0; + using rep = long double; + using q = quantity; - auto stl_dist = std::fisher_f_distribution(m, n); - auto units_dist = mp_units::fisher_f_distribution(m, n); + SECTION("default") + { + auto dist = mp_units::student_t_distribution(); - 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); - } -} + CHECK(dist.n() == 1.0); + } -TEST_CASE("student_t_distribution") -{ - using rep = long double; - using q = quantity; + SECTION("parametrized") + { + constexpr rep n = 2.0; - SECTION("default") - { - auto dist = mp_units::student_t_distribution(); + auto stl_dist = std::student_t_distribution(n); + auto units_dist = mp_units::student_t_distribution(n); - CHECK(dist.n() == 1.0); + 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); + } } - SECTION("parametrized") + SECTION("discrete_distribution") { - constexpr rep n = 2.0; + using rep = std::int64_t; + using q = quantity; - auto stl_dist = std::student_t_distribution(n); - auto units_dist = mp_units::student_t_distribution(n); + SECTION("default") + { + auto stl_dist = std::discrete_distribution(); + auto units_dist = mp_units::discrete_distribution(); - 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); - } -} + 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()); + } -TEST_CASE("discrete_distribution") -{ - using rep = std::int64_t; - using q = quantity; + SECTION("parametrized_input_it") + { + constexpr std::array weights = {1.0, 2.0, 3.0}; - SECTION("default") - { - auto stl_dist = std::discrete_distribution(); - auto units_dist = mp_units::discrete_distribution(); + auto stl_dist = std::discrete_distribution(weights.cbegin(), weights.cend()); + auto units_dist = mp_units::discrete_distribution(weights.cbegin(), weights.cend()); - 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()); + CHECK(units_dist.probabilities() == stl_dist.probabilities()); + } + + SECTION("parametrized_initializer_list") + { + const std::initializer_list weights = {1.0, 2.0, 3.0}; + + auto stl_dist = std::discrete_distribution(weights); + auto units_dist = mp_units::discrete_distribution(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(count, xmin, xmax, [](double val) { return val; }); + auto units_dist = mp_units::discrete_distribution(count, xmin, xmax, [](double val) { return val; }); + + CHECK(units_dist.probabilities() == stl_dist.probabilities()); + } } - SECTION("parametrized_input_it") + SECTION("piecewise_constant_distribution") { - constexpr std::array weights = {1.0, 2.0, 3.0}; + using rep = long double; + using q = quantity; - auto stl_dist = std::discrete_distribution(weights.cbegin(), weights.cend()); - auto units_dist = mp_units::discrete_distribution(weights.cbegin(), weights.cend()); + std::vector intervals_rep_vec = {1.0, 2.0, 3.0}; + std::vector intervals_qty_vec = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre], + 3.0 * isq::length[si::metre]}; - CHECK(units_dist.probabilities() == stl_dist.probabilities()); + SECTION("default") + { + auto stl_dist = std::piecewise_constant_distribution(); + auto units_dist = mp_units::piecewise_constant_distribution(); + + 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 intervals_rep = {1.0, 2.0, 3.0}; + constexpr std::array intervals_qty = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre], + 3.0 * isq::length[si::metre]}; + constexpr std::array weights = {1.0, 2.0, 3.0}; + + auto stl_dist = + std::piecewise_constant_distribution(intervals_rep.cbegin(), intervals_rep.cend(), weights.cbegin()); + auto units_dist = + mp_units::piecewise_constant_distribution(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") + { + const std::initializer_list intervals_rep = {1.0, 2.0, 3.0}; + const std::initializer_list 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(intervals_rep, [](rep val) { return val; }); + auto units_dist = mp_units::piecewise_constant_distribution( + 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(nw, xmin_rep, xmax_rep, [](rep val) { return val; }); + auto units_dist = mp_units::piecewise_constant_distribution( + 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()); + } } - SECTION("parametrized_initializer_list") + SECTION("piecewise_linear_distribution") { - const std::initializer_list weights = {1.0, 2.0, 3.0}; + using rep = long double; + using q = quantity; - auto stl_dist = std::discrete_distribution(weights); - auto units_dist = mp_units::discrete_distribution(weights); + std::vector intervals_rep_vec = {1.0, 2.0, 3.0}; + std::vector intervals_qty_vec = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre], + 3.0 * isq::length[si::metre]}; - CHECK(units_dist.probabilities() == stl_dist.probabilities()); + SECTION("default") + { + auto stl_dist = std::piecewise_linear_distribution(); + auto units_dist = mp_units::piecewise_linear_distribution(); + + 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 intervals_rep = {1.0, 2.0, 3.0}; + constexpr std::array intervals_qty = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre], + 3.0 * isq::length[si::metre]}; + constexpr std::array weights = {1.0, 2.0, 3.0}; + + auto stl_dist = + std::piecewise_linear_distribution(intervals_rep.cbegin(), intervals_rep.cend(), weights.cbegin()); + auto units_dist = + mp_units::piecewise_linear_distribution(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") + { + const std::initializer_list intervals_rep = {1.0, 2.0, 3.0}; + const std::initializer_list 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(intervals_rep, [](rep val) { return val; }); + auto units_dist = mp_units::piecewise_linear_distribution( + 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(nw, xmin_rep, xmax_rep, [](rep val) { return val; }); + auto units_dist = mp_units::piecewise_linear_distribution( + 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()); + } } - - SECTION("parametrized_range") - { - constexpr std::size_t count = 3; - constexpr double xmin = 1, xmax = 3; - - auto stl_dist = std::discrete_distribution(count, xmin, xmax, [](double val) { return val; }); - auto units_dist = mp_units::discrete_distribution(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; - - std::vector intervals_rep_vec = {1.0, 2.0, 3.0}; - std::vector 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(); - auto units_dist = mp_units::piecewise_constant_distribution(); - - 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 intervals_rep = {1.0, 2.0, 3.0}; - constexpr std::array intervals_qty = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre], - 3.0 * isq::length[si::metre]}; - constexpr std::array weights = {1.0, 2.0, 3.0}; - - auto stl_dist = - std::piecewise_constant_distribution(intervals_rep.cbegin(), intervals_rep.cend(), weights.cbegin()); - auto units_dist = - mp_units::piecewise_constant_distribution(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") - { - const std::initializer_list intervals_rep = {1.0, 2.0, 3.0}; - const std::initializer_list 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(intervals_rep, [](rep val) { return val; }); - auto units_dist = mp_units::piecewise_constant_distribution( - 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(nw, xmin_rep, xmax_rep, [](rep val) { return val; }); - auto units_dist = mp_units::piecewise_constant_distribution( - 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; - - std::vector intervals_rep_vec = {1.0, 2.0, 3.0}; - std::vector 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(); - auto units_dist = mp_units::piecewise_linear_distribution(); - - 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 intervals_rep = {1.0, 2.0, 3.0}; - constexpr std::array intervals_qty = {1.0 * isq::length[si::metre], 2.0 * isq::length[si::metre], - 3.0 * isq::length[si::metre]}; - constexpr std::array weights = {1.0, 2.0, 3.0}; - - auto stl_dist = - std::piecewise_linear_distribution(intervals_rep.cbegin(), intervals_rep.cend(), weights.cbegin()); - auto units_dist = - mp_units::piecewise_linear_distribution(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") - { - const std::initializer_list intervals_rep = {1.0, 2.0, 3.0}; - const std::initializer_list 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(intervals_rep, [](rep val) { return val; }); - auto units_dist = mp_units::piecewise_linear_distribution( - 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(nw, xmin_rep, xmax_rep, [](rep val) { return val; }); - auto units_dist = mp_units::piecewise_linear_distribution( - 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()); - } -} +} \ No newline at end of file diff --git a/test/runtime/fixed_string_test.cpp b/test/runtime/fixed_string_test.cpp index b1b01f21..f3bc737a 100644 --- a/test/runtime/fixed_string_test.cpp +++ b/test/runtime/fixed_string_test.cpp @@ -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")); + } } } diff --git a/test/runtime/fmt_test.cpp b/test/runtime/fmt_test.cpp index 03eef676..4011588e 100644 --- a/test/runtime/fmt_test.cpp +++ b/test/runtime/fmt_test.cpp @@ -55,7 +55,271 @@ constexpr bool mp_units::is_vector = true; using namespace mp_units; using namespace mp_units::si::unit_symbols; -TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") +TEST_CASE("dimension_symbol", "[dimension][symbol]") +{ + using enum text_encoding; + + std::ostringstream os; + + SECTION("default formatting") + { + os << dimension_symbol(isq::power.dimension); + CHECK(os.str() == "L²MT⁻³"); + } + + SECTION("Portable mode") + { + os << dimension_symbol(isq::power.dimension); + CHECK(os.str() == "L^2MT^-3"); + } +} + +TEST_CASE("unit_symbol", "[unit][symbol]") +{ + using enum text_encoding; + using enum unit_symbol_solidus; + using enum unit_symbol_separator; + + std::ostringstream os; + + SECTION("default formatting") + { + os << unit_symbol(m / s2); + CHECK(os.str() == "m/s²"); + } + + SECTION("Portable mode") + { + os << unit_symbol(m / s2); + CHECK(os.str() == "m/s^2"); + } + + SECTION("solidus") + { + os << unit_symbol(m / s2); + CHECK(os.str() == "m s⁻²"); + } + + SECTION("separator") + { + os << unit_symbol(m / s2); + CHECK(os.str() == "m⋅s⁻²"); + } +} + +// TODO add dimension formatting tests + +TEST_CASE("unit formatting", "[unit][fmt]") +{ + SECTION("Unit formatting should use proper text encoding") + { + SECTION("Unicode text output") + { + CHECK(MP_UNITS_STD_FMT::format("{:U}", km / h) == "km/h"); + CHECK(MP_UNITS_STD_FMT::format("{:U}", si::kilo) == "kΩ"); + CHECK(MP_UNITS_STD_FMT::format("{:U}", us) == "µs"); + CHECK(MP_UNITS_STD_FMT::format("{:U}", m / s2) == "m/s²"); + } + + SECTION("Unicode text output is used by default") + { + CHECK(MP_UNITS_STD_FMT::format("{}", km / h) == "km/h"); + CHECK(MP_UNITS_STD_FMT::format("{}", si::kilo) == "kΩ"); + CHECK(MP_UNITS_STD_FMT::format("{}", us) == "µs"); + CHECK(MP_UNITS_STD_FMT::format("{}", m / s2) == "m/s²"); + } + + SECTION("Portable text output") + { + CHECK(MP_UNITS_STD_FMT::format("{:P}", km / h) == "km/h"); + CHECK(MP_UNITS_STD_FMT::format("{:P}", si::kilo) == "kohm"); + CHECK(MP_UNITS_STD_FMT::format("{:P}", us) == "us"); + CHECK(MP_UNITS_STD_FMT::format("{:P}", m / s2) == "m/s^2"); + } + } + + SECTION("unit formatting should print solidus according to specs") + { + SECTION("Solidus for only one element in denominator") + { + CHECK(MP_UNITS_STD_FMT::format("{:1}", km / h) == "km/h"); + CHECK(MP_UNITS_STD_FMT::format("{:1}", m / s2) == "m/s²"); + CHECK(MP_UNITS_STD_FMT::format("{:1}", kg / m / s2) == "kg m⁻¹ s⁻²"); + } + + SECTION("Solidus for only one element in denominator is used by default") + { + CHECK(MP_UNITS_STD_FMT::format("{}", km / h) == "km/h"); + CHECK(MP_UNITS_STD_FMT::format("{}", m / s2) == "m/s²"); + CHECK(MP_UNITS_STD_FMT::format("{}", kg / m / s2) == "kg m⁻¹ s⁻²"); + } + + SECTION("Always use solidus") + { + CHECK(MP_UNITS_STD_FMT::format("{:a}", km / h) == "km/h"); + CHECK(MP_UNITS_STD_FMT::format("{:a}", m / s2) == "m/s²"); + CHECK(MP_UNITS_STD_FMT::format("{:a}", kg / m / s2) == "kg/(m s²)"); + } + + SECTION("Never use solidus") + { + CHECK(MP_UNITS_STD_FMT::format("{:n}", km / h) == "km h⁻¹"); + CHECK(MP_UNITS_STD_FMT::format("{:n}", m / s2) == "m s⁻²"); + CHECK(MP_UNITS_STD_FMT::format("{:n}", kg / m / s2) == "kg m⁻¹ s⁻²"); + } + } + + SECTION("Unit formatting should user proper separator") + { + SECTION("Space") + { + CHECK(MP_UNITS_STD_FMT::format("{:s}", kg * m / s2) == "kg m/s²"); + CHECK(MP_UNITS_STD_FMT::format("{:s}", kg / m / s2) == "kg m⁻¹ s⁻²"); + CHECK(MP_UNITS_STD_FMT::format("{:sa}", kg / m / s2) == "kg/(m s²)"); + } + + SECTION("Space is used by default") + { + CHECK(MP_UNITS_STD_FMT::format("{}", kg * m / s2) == "kg m/s²"); + CHECK(MP_UNITS_STD_FMT::format("{}", kg / m / s2) == "kg m⁻¹ s⁻²"); + CHECK(MP_UNITS_STD_FMT::format("{:a}", kg / m / s2) == "kg/(m s²)"); + } + + SECTION("Dot") + { + CHECK(MP_UNITS_STD_FMT::format("{:d}", kg * m / s2) == "kg⋅m/s²"); + CHECK(MP_UNITS_STD_FMT::format("{:d}", kg / m / s2) == "kg⋅m⁻¹⋅s⁻²"); + CHECK(MP_UNITS_STD_FMT::format("{:ad}", kg / m / s2) == "kg/(m⋅s²)"); + } + } +} + +TEST_CASE("unit formatting error handling", "[unit][fmt][exception]") +{ + SECTION("unknown unit modifiers should throw") + { + SECTION("only the invalid modifier") + { + REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:x}", MP_UNITS_STD_FMT::make_format_args(m)), + 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("{:xUda}", MP_UNITS_STD_FMT::make_format_args(m)), + 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("{:Udax}", MP_UNITS_STD_FMT::make_format_args(m)), + 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("{:Udxa}", MP_UNITS_STD_FMT::make_format_args(m)), + MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("invalid unit modifier specified")); + } + } + + SECTION("repeated unit modifiers should throw") + { + SECTION("text encoding") + { + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:UdaU}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:dUaU}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:dUUa}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); + } + + SECTION("solidus") + { + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:aUda}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:daUa}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:daaU}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); + } + + SECTION("separator") + { + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:dUad}", MP_UNITS_STD_FMT::make_format_args(m)), 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("{:dadU}", MP_UNITS_STD_FMT::make_format_args(m)), 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("{:addU}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); + } + } + + SECTION("more then one modifier of the same kind should throw") + { + SECTION("text encoding") + { + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:UdaP}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:dPaU}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:dPUa}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); + } + + SECTION("solidus") + { + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:aUdn}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:dnUa}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:da1U}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); + } + + SECTION("separator") + { + REQUIRE_THROWS_MATCHES( + MP_UNITS_STD_FMT::vformat("{:dUas}", MP_UNITS_STD_FMT::make_format_args(m)), 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("{:sadU}", MP_UNITS_STD_FMT::make_format_args(m)), 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("{:adsU}", MP_UNITS_STD_FMT::make_format_args(m)), MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); + } + } + + SECTION("half_high_dot separator requested for portable encoding should throw") + { + REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dPa}", MP_UNITS_STD_FMT::make_format_args(m)), + MP_UNITS_STD_FMT::format_error, + Catch::Matchers::Message("half_high_dot unit separator allowed only for UTF-8 encoding")); + } +} + +TEST_CASE("default quantity formatting", "[quantity][ostream][fmt]") { std::ostringstream os; @@ -375,266 +639,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") } } -TEST_CASE("quantity format string with only %N should print quantity value only", "[text][fmt]") -{ - SECTION("integral representation") - { - SECTION("positive value") { CHECK(MP_UNITS_STD_FMT::format("{:%N}", 123 * isq::speed[km / h]) == "123"); } - - SECTION("negative value") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N}", 5 * isq::length[m] - 10 * isq::length[m]) == "-5"); - } - } - - SECTION("floating-point representation") - { - SECTION("positive value") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N}", 221. * isq::length[km] / (2 * isq::time[h])) == "110.5"); - } - - SECTION("negative value") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N}", 3.14 * isq::length[m] - 10 * isq::length[m]) == "-6.859999999999999"); - } - - SECTION("nan") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N}", std::numeric_limits::quiet_NaN() * isq::length[m]) == "nan"); - } - - SECTION("inf") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N}", std::numeric_limits::infinity() * isq::length[m]) == "inf"); - } - - SECTION("-inf") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N}", -std::numeric_limits::infinity() * isq::length[m]) == "-inf"); - } - } -} - -TEST_CASE("quantity format string with only %U should print quantity unit symbol only", "[text][fmt]") -{ - CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::speed[km / h]) == "km/h"); - CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::resistance[si::kilo]) == "kΩ"); - CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::time[us]) == "µs"); - CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::acceleration[m / s2]) == "m/s²"); - CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * percent) == "%"); -} - -TEST_CASE("Unit formatting should use proper text encoding") -{ - SECTION("Unicode text output") - { - CHECK(MP_UNITS_STD_FMT::format("{:U}", km / h) == "km/h"); - CHECK(MP_UNITS_STD_FMT::format("{:U}", si::kilo) == "kΩ"); - CHECK(MP_UNITS_STD_FMT::format("{:U}", us) == "µs"); - CHECK(MP_UNITS_STD_FMT::format("{:U}", m / s2) == "m/s²"); - } - - SECTION("Unicode text output is used by default") - { - CHECK(MP_UNITS_STD_FMT::format("{}", km / h) == "km/h"); - CHECK(MP_UNITS_STD_FMT::format("{}", si::kilo) == "kΩ"); - CHECK(MP_UNITS_STD_FMT::format("{}", us) == "µs"); - CHECK(MP_UNITS_STD_FMT::format("{}", m / s2) == "m/s²"); - } - - SECTION("Portable text output") - { - CHECK(MP_UNITS_STD_FMT::format("{:P}", km / h) == "km/h"); - CHECK(MP_UNITS_STD_FMT::format("{:P}", si::kilo) == "kohm"); - CHECK(MP_UNITS_STD_FMT::format("{:P}", us) == "us"); - CHECK(MP_UNITS_STD_FMT::format("{:P}", m / s2) == "m/s^2"); - } -} - -TEST_CASE("Unit formatting should print solidus according to specs") -{ - SECTION("Solidus for only one element in denominator") - { - CHECK(MP_UNITS_STD_FMT::format("{:1}", km / h) == "km/h"); - CHECK(MP_UNITS_STD_FMT::format("{:1}", m / s2) == "m/s²"); - CHECK(MP_UNITS_STD_FMT::format("{:1}", kg / m / s2) == "kg m⁻¹ s⁻²"); - } - - SECTION("Solidus for only one element in denominator is used by default") - { - CHECK(MP_UNITS_STD_FMT::format("{}", km / h) == "km/h"); - CHECK(MP_UNITS_STD_FMT::format("{}", m / s2) == "m/s²"); - CHECK(MP_UNITS_STD_FMT::format("{}", kg / m / s2) == "kg m⁻¹ s⁻²"); - } - - SECTION("Always use solidus") - { - CHECK(MP_UNITS_STD_FMT::format("{:a}", km / h) == "km/h"); - CHECK(MP_UNITS_STD_FMT::format("{:a}", m / s2) == "m/s²"); - CHECK(MP_UNITS_STD_FMT::format("{:a}", kg / m / s2) == "kg/(m s²)"); - } - - SECTION("Never use solidus") - { - CHECK(MP_UNITS_STD_FMT::format("{:n}", km / h) == "km h⁻¹"); - CHECK(MP_UNITS_STD_FMT::format("{:n}", m / s2) == "m s⁻²"); - CHECK(MP_UNITS_STD_FMT::format("{:n}", kg / m / s2) == "kg m⁻¹ s⁻²"); - } -} - -TEST_CASE("Unit formatting should user proper separator") -{ - SECTION("Space") - { - CHECK(MP_UNITS_STD_FMT::format("{:s}", kg * m / s2) == "kg m/s²"); - CHECK(MP_UNITS_STD_FMT::format("{:s}", kg / m / s2) == "kg m⁻¹ s⁻²"); - CHECK(MP_UNITS_STD_FMT::format("{:sa}", kg / m / s2) == "kg/(m s²)"); - } - - SECTION("Space is used by default") - { - CHECK(MP_UNITS_STD_FMT::format("{}", kg * m / s2) == "kg m/s²"); - CHECK(MP_UNITS_STD_FMT::format("{}", kg / m / s2) == "kg m⁻¹ s⁻²"); - CHECK(MP_UNITS_STD_FMT::format("{:a}", kg / m / s2) == "kg/(m s²)"); - } - - SECTION("Dot") - { - CHECK(MP_UNITS_STD_FMT::format("{:d}", kg * m / s2) == "kg⋅m/s²"); - CHECK(MP_UNITS_STD_FMT::format("{:d}", kg / m / s2) == "kg⋅m⁻¹⋅s⁻²"); - CHECK(MP_UNITS_STD_FMT::format("{:ad}", kg / m / s2) == "kg/(m⋅s²)"); - } -} - -TEST_CASE("unknown unit modifiers should throw", "[text][fmt][exception]") -{ - SECTION("only the invalid modifier") - { - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:x}", MP_UNITS_STD_FMT::make_format_args(m)), - 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("{:xUda}", MP_UNITS_STD_FMT::make_format_args(m)), - 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("{:Udax}", MP_UNITS_STD_FMT::make_format_args(m)), - 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("{:Udxa}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); - } -} - -TEST_CASE("repeated unit modifiers should throw", "[text][fmt][exception]") -{ - SECTION("text encoding") - { - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:UdaU}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dUaU}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dUUa}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); - } - - SECTION("solidus") - { - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:aUda}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:daUa}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:daaU}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); - } - - SECTION("separator") - { - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dUad}", MP_UNITS_STD_FMT::make_format_args(m)), - 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("{:dadU}", MP_UNITS_STD_FMT::make_format_args(m)), - 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("{:addU}", MP_UNITS_STD_FMT::make_format_args(m)), - 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]") -{ - SECTION("text encoding") - { - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:UdaP}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dPaU}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dPUa}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of 'UAP' unit modifiers may be used in the format spec")); - } - - SECTION("solidus") - { - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:aUdn}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dnUa}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:da1U}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("only one of '1an' unit modifiers may be used in the format spec")); - } - - SECTION("separator") - { - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dUas}", MP_UNITS_STD_FMT::make_format_args(m)), - 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("{:sadU}", MP_UNITS_STD_FMT::make_format_args(m)), - 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("{:adsU}", MP_UNITS_STD_FMT::make_format_args(m)), - 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 portable encoding should throw", "[text][fmt][exception]") -{ - REQUIRE_THROWS_MATCHES(MP_UNITS_STD_FMT::vformat("{:dPa}", MP_UNITS_STD_FMT::make_format_args(m)), - MP_UNITS_STD_FMT::format_error, - Catch::Matchers::Message("half_high_dot unit separator allowed only for UTF-8 encoding")); -} - -TEST_CASE("%U and %N can be put anywhere in a format string", "[text][fmt]") -{ - SECTION("no space") { CHECK(MP_UNITS_STD_FMT::format("{:%N%U}", 123 * isq::speed[km / h]) == "123km/h"); } - - SECTION("separator") { CHECK(MP_UNITS_STD_FMT::format("{:%N###%U}", 123 * isq::speed[km / h]) == "123###km/h"); } - - SECTION("opposite order") { CHECK(MP_UNITS_STD_FMT::format("{:%U %N}", 123 * isq::speed[km / h]) == "km/h 123"); } -} - -TEST_CASE("quantity fill and align specification", "[text][fmt][ostream]") +TEST_CASE("quantity fill and align specification", "[quantity][ostream][fmt]") { SECTION("ostream") { @@ -726,356 +731,373 @@ TEST_CASE("quantity fill and align specification", "[text][fmt][ostream]") } } -TEST_CASE("sign specification", "[text][fmt]") +TEST_CASE("quantity subentities selection", "[quantity][fmt]") { - auto inf = std::numeric_limits::infinity() * si::metre; - auto nan = std::numeric_limits::quiet_NaN() * si::metre; - - SECTION("full format {:%N%?%U} on a quantity") + SECTION("quantity format string with only %N should print quantity value only") { - CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:%N%U:N[+]},{0:%N%U:N[-]},{0:%N%U:N[ ]}", 1 * isq::length[m]) == - "1m,+1m,1m, 1m"); - CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:%N%U:N[+]},{0:%N%U:N[-]},{0:%N%U:N[ ]}", -1 * isq::length[m]) == - "-1m,-1m,-1m,-1m"); - CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:%N%U:N[+]},{0:%N%U:N[-]},{0:%N%U:N[ ]}", inf) == - "infm,+infm,infm, infm"); - CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:%N%U:N[+]},{0:%N%U:N[-]},{0:%N%U:N[ ]}", nan) == - "nanm,+nanm,nanm, nanm"); + SECTION("integral representation") + { + SECTION("positive value") { CHECK(MP_UNITS_STD_FMT::format("{:%N}", 123 * isq::speed[km / h]) == "123"); } + + SECTION("negative value") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N}", 5 * isq::length[m] - 10 * isq::length[m]) == "-5"); + } + } + + SECTION("floating-point representation") + { + SECTION("positive value") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N}", 221. * isq::length[km] / (2 * isq::time[h])) == "110.5"); + } + + SECTION("negative value") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N}", 3.14 * isq::length[m] - 10 * isq::length[m]) == "-6.859999999999999"); + } + + SECTION("nan") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N}", std::numeric_limits::quiet_NaN() * isq::length[m]) == "nan"); + } + + SECTION("inf") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N}", std::numeric_limits::infinity() * isq::length[m]) == "inf"); + } + + SECTION("-inf") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N}", -std::numeric_limits::infinity() * isq::length[m]) == "-inf"); + } + } } - SECTION("value only format {:%N} on a quantity") + SECTION("quantity format string with only %U should print quantity unit symbol only") { - CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:%N:N[+]},{0:%N:N[-]},{0:%N:N[ ]}", 1 * isq::length[m]) == "1,+1,1, 1"); - CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:%N:N[+]},{0:%N:N[-]},{0:%N:N[ ]}", -1 * isq::length[m]) == "-1,-1,-1,-1"); - CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:%N:N[+]},{0:%N:N[-]},{0:%N:N[ ]}", inf) == "inf,+inf,inf, inf"); - CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:%N:N[+]},{0:%N:N[-]},{0:%N:N[ ]}", nan) == "nan,+nan,nan, nan"); + CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::speed[km / h]) == "km/h"); + CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::resistance[si::kilo]) == "kΩ"); + CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::time[us]) == "µs"); + CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::acceleration[m / s2]) == "m/s²"); + CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * percent) == "%"); + } + + SECTION("%U and %N can be put anywhere in a format string") + { + SECTION("no space") { CHECK(MP_UNITS_STD_FMT::format("{:%N%U}", 123 * isq::speed[km / h]) == "123km/h"); } + + SECTION("separator") { CHECK(MP_UNITS_STD_FMT::format("{:%N###%U}", 123 * isq::speed[km / h]) == "123###km/h"); } + + SECTION("opposite order") { CHECK(MP_UNITS_STD_FMT::format("{:%U %N}", 123 * isq::speed[km / h]) == "km/h 123"); } } } -TEST_CASE("precision specification", "[text][fmt]") +// TODO provide basic tests if format string when provided in a quantity formatter are passed to respective dimensions +// and units formatters (detail formatting tests for dimensions and units are done separately) + +TEST_CASE("quantity numerical value formatting for `std` arithmetic types", "[quantity][fmt]") { - SECTION("full format on a quantity") + SECTION("sign specification") { - SECTION("default spec") + auto inf = std::numeric_limits::infinity() * si::metre; + auto nan = std::numeric_limits::quiet_NaN() * si::metre; + + SECTION("full format {:%N%?%U} on a quantity") { - CHECK(MP_UNITS_STD_FMT::format("{::N[.0f]}", 1.2345 * isq::length[m]) == "1 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.1f]}", 1.2345 * isq::length[m]) == "1.2 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.2f]}", 1.2345 * isq::length[m]) == "1.23 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3f]}", 1.2345 * isq::length[m]) == "1.234 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.4f]}", 1.2345 * isq::length[m]) == "1.2345 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.5f]}", 1.2345 * isq::length[m]) == "1.23450 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.10f]}", 1.2345 * isq::length[m]) == "1.2345000000 m"); + CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:%N%U:N[+]},{0:%N%U:N[-]},{0:%N%U:N[ ]}", 1 * isq::length[m]) == + "1m,+1m,1m, 1m"); + CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:%N%U:N[+]},{0:%N%U:N[-]},{0:%N%U:N[ ]}", -1 * isq::length[m]) == + "-1m,-1m,-1m,-1m"); + CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:%N%U:N[+]},{0:%N%U:N[-]},{0:%N%U:N[ ]}", inf) == + "infm,+infm,infm, infm"); + CHECK(MP_UNITS_STD_FMT::format("{0:%N%U},{0:%N%U:N[+]},{0:%N%U:N[-]},{0:%N%U:N[ ]}", nan) == + "nanm,+nanm,nanm, nanm"); } - SECTION("explicit spec") + SECTION("value only format {:%N} on a quantity") { - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.0f]}", 1.2345 * isq::length[m]) == "1 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.1f]}", 1.2345 * isq::length[m]) == "1.2 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.2f]}", 1.2345 * isq::length[m]) == "1.23 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3f]}", 1.2345 * isq::length[m]) == "1.234 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.4f]}", 1.2345 * isq::length[m]) == "1.2345 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.5f]}", 1.2345 * isq::length[m]) == "1.23450 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.10f]}", 1.2345 * isq::length[m]) == "1.2345000000 m"); - } - - SECTION("modified spec") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.0f]}", 1.2345 * isq::length[m]) == "1m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.1f]}", 1.2345 * isq::length[m]) == "1.2m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.2f]}", 1.2345 * isq::length[m]) == "1.23m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3f]}", 1.2345 * isq::length[m]) == "1.234m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.4f]}", 1.2345 * isq::length[m]) == "1.2345m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.5f]}", 1.2345 * isq::length[m]) == "1.23450m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.10f]}", 1.2345 * isq::length[m]) == "1.2345000000m"); + CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:%N:N[+]},{0:%N:N[-]},{0:%N:N[ ]}", 1 * isq::length[m]) == "1,+1,1, 1"); + CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:%N:N[+]},{0:%N:N[-]},{0:%N:N[ ]}", -1 * isq::length[m]) == + "-1,-1,-1,-1"); + CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:%N:N[+]},{0:%N:N[-]},{0:%N:N[ ]}", inf) == "inf,+inf,inf, inf"); + CHECK(MP_UNITS_STD_FMT::format("{0:%N},{0:%N:N[+]},{0:%N:N[-]},{0:%N:N[ ]}", nan) == "nan,+nan,nan, nan"); } } - SECTION("value only format {:%N} on a quantity") + SECTION("precision specification") { - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.0f]}", 1.2345 * isq::length[m]) == "1"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.1f]}", 1.2345 * isq::length[m]) == "1.2"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.2f]}", 1.2345 * isq::length[m]) == "1.23"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3f]}", 1.2345 * isq::length[m]) == "1.234"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.4f]}", 1.2345 * isq::length[m]) == "1.2345"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.5f]}", 1.2345 * isq::length[m]) == "1.23450"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.10f]}", 1.2345 * isq::length[m]) == "1.2345000000"); - } -} - -TEST_CASE("type specification", "[text][fmt]") -{ - SECTION("full format {:%N%?%U} on a quantity") - { - SECTION("default spec") + SECTION("full format on a quantity") { - CHECK(MP_UNITS_STD_FMT::format("{::N[b]}", 42 * isq::length[m]) == "101010 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[B]}", 42 * isq::length[m]) == "101010 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[d]}", 42 * isq::length[m]) == "42 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[o]}", 42 * isq::length[m]) == "52 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[x]}", 42 * isq::length[m]) == "2a m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[X]}", 42 * isq::length[m]) == "2A m"); + SECTION("default spec") + { + CHECK(MP_UNITS_STD_FMT::format("{::N[.0f]}", 1.2345 * isq::length[m]) == "1 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.1f]}", 1.2345 * isq::length[m]) == "1.2 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.2f]}", 1.2345 * isq::length[m]) == "1.23 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3f]}", 1.2345 * isq::length[m]) == "1.234 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.4f]}", 1.2345 * isq::length[m]) == "1.2345 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.5f]}", 1.2345 * isq::length[m]) == "1.23450 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.10f]}", 1.2345 * isq::length[m]) == "1.2345000000 m"); + } + + SECTION("explicit spec") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.0f]}", 1.2345 * isq::length[m]) == "1 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.1f]}", 1.2345 * isq::length[m]) == "1.2 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.2f]}", 1.2345 * isq::length[m]) == "1.23 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3f]}", 1.2345 * isq::length[m]) == "1.234 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.4f]}", 1.2345 * isq::length[m]) == "1.2345 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.5f]}", 1.2345 * isq::length[m]) == "1.23450 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.10f]}", 1.2345 * isq::length[m]) == "1.2345000000 m"); + } + + SECTION("modified spec") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.0f]}", 1.2345 * isq::length[m]) == "1m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.1f]}", 1.2345 * isq::length[m]) == "1.2m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.2f]}", 1.2345 * isq::length[m]) == "1.23m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3f]}", 1.2345 * isq::length[m]) == "1.234m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.4f]}", 1.2345 * isq::length[m]) == "1.2345m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.5f]}", 1.2345 * isq::length[m]) == "1.23450m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.10f]}", 1.2345 * isq::length[m]) == "1.2345000000m"); + } + } + + SECTION("value only format {:%N} on a quantity") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.0f]}", 1.2345 * isq::length[m]) == "1"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.1f]}", 1.2345 * isq::length[m]) == "1.2"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.2f]}", 1.2345 * isq::length[m]) == "1.23"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3f]}", 1.2345 * isq::length[m]) == "1.234"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.4f]}", 1.2345 * isq::length[m]) == "1.2345"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.5f]}", 1.2345 * isq::length[m]) == "1.23450"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.10f]}", 1.2345 * isq::length[m]) == "1.2345000000"); + } + } + + SECTION("type specification") + { + SECTION("full format {:%N%?%U} on a quantity") + { + SECTION("default spec") + { + CHECK(MP_UNITS_STD_FMT::format("{::N[b]}", 42 * isq::length[m]) == "101010 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[B]}", 42 * isq::length[m]) == "101010 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[d]}", 42 * isq::length[m]) == "42 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[o]}", 42 * isq::length[m]) == "52 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[x]}", 42 * isq::length[m]) == "2a m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[X]}", 42 * isq::length[m]) == "2A m"); #if MP_UNITS_USE_FMTLIB - CHECK(MP_UNITS_STD_FMT::format("{::N[a]}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3a]}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[A]}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3A]}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[a]}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3a]}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[A]}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3A]}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m"); #else - CHECK(MP_UNITS_STD_FMT::format("{::N[a]}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3a]}", 1.2345678 * isq::length[m]) == "1.3c1p+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[A]}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3A]}", 1.2345678 * isq::length[m]) == "1.3C1P+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[a]}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3a]}", 1.2345678 * isq::length[m]) == "1.3c1p+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[A]}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3A]}", 1.2345678 * isq::length[m]) == "1.3C1P+0 m"); #endif - CHECK(MP_UNITS_STD_FMT::format("{::N[e]}", 1.2345678 * isq::length[m]) == "1.234568e+00 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3e]}", 1.2345678 * isq::length[m]) == "1.235e+00 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[E]}", 1.2345678 * isq::length[m]) == "1.234568E+00 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3E]}", 1.2345678 * isq::length[m]) == "1.235E+00 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[g]}", 1.2345678 * isq::length[m]) == "1.23457 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[g]}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3g]}", 1.2345678 * isq::length[m]) == "1.23 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3g]}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[G]}", 1.2345678 * isq::length[m]) == "1.23457 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[G]}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3G]}", 1.2345678 * isq::length[m]) == "1.23 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[.3G]}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m"); - } + CHECK(MP_UNITS_STD_FMT::format("{::N[e]}", 1.2345678 * isq::length[m]) == "1.234568e+00 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3e]}", 1.2345678 * isq::length[m]) == "1.235e+00 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[E]}", 1.2345678 * isq::length[m]) == "1.234568E+00 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3E]}", 1.2345678 * isq::length[m]) == "1.235E+00 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[g]}", 1.2345678 * isq::length[m]) == "1.23457 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[g]}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3g]}", 1.2345678 * isq::length[m]) == "1.23 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3g]}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[G]}", 1.2345678 * isq::length[m]) == "1.23457 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[G]}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3G]}", 1.2345678 * isq::length[m]) == "1.23 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[.3G]}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m"); + } - SECTION("explicit spec") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[b]}", 42 * isq::length[m]) == "101010 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[B]}", 42 * isq::length[m]) == "101010 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[d]}", 42 * isq::length[m]) == "42 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[o]}", 42 * isq::length[m]) == "52 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[x]}", 42 * isq::length[m]) == "2a m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[X]}", 42 * isq::length[m]) == "2A m"); + SECTION("explicit spec") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[b]}", 42 * isq::length[m]) == "101010 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[B]}", 42 * isq::length[m]) == "101010 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[d]}", 42 * isq::length[m]) == "42 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[o]}", 42 * isq::length[m]) == "52 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[x]}", 42 * isq::length[m]) == "2a m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[X]}", 42 * isq::length[m]) == "2A m"); #if MP_UNITS_USE_FMTLIB - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[a]}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3a]}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[A]}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3A]}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[a]}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3a]}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[A]}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3A]}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m"); #else - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[a]}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3a]}", 1.2345678 * isq::length[m]) == "1.3c1p+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[A]}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3A]}", 1.2345678 * isq::length[m]) == "1.3C1P+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[a]}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3a]}", 1.2345678 * isq::length[m]) == "1.3c1p+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[A]}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3A]}", 1.2345678 * isq::length[m]) == "1.3C1P+0 m"); #endif - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[e]}", 1.2345678 * isq::length[m]) == "1.234568e+00 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3e]}", 1.2345678 * isq::length[m]) == "1.235e+00 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[E]}", 1.2345678 * isq::length[m]) == "1.234568E+00 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3E]}", 1.2345678 * isq::length[m]) == "1.235E+00 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[g]}", 1.2345678 * isq::length[m]) == "1.23457 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[g]}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3g]}", 1.2345678 * isq::length[m]) == "1.23 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3g]}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[G]}", 1.2345678 * isq::length[m]) == "1.23457 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[G]}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3G]}", 1.2345678 * isq::length[m]) == "1.23 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3G]}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m"); - } + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[e]}", 1.2345678 * isq::length[m]) == "1.234568e+00 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3e]}", 1.2345678 * isq::length[m]) == "1.235e+00 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[E]}", 1.2345678 * isq::length[m]) == "1.234568E+00 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3E]}", 1.2345678 * isq::length[m]) == "1.235E+00 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[g]}", 1.2345678 * isq::length[m]) == "1.23457 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[g]}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3g]}", 1.2345678 * isq::length[m]) == "1.23 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3g]}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[G]}", 1.2345678 * isq::length[m]) == "1.23457 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[G]}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3G]}", 1.2345678 * isq::length[m]) == "1.23 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[.3G]}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m"); + } - SECTION("modified spec") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[b]}", 42 * isq::length[m]) == "101010m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[B]}", 42 * isq::length[m]) == "101010m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[d]}", 42 * isq::length[m]) == "42m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[o]}", 42 * isq::length[m]) == "52m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[x]}", 42 * isq::length[m]) == "2am"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[X]}", 42 * isq::length[m]) == "2Am"); + SECTION("modified spec") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[b]}", 42 * isq::length[m]) == "101010m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[B]}", 42 * isq::length[m]) == "101010m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[d]}", 42 * isq::length[m]) == "42m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[o]}", 42 * isq::length[m]) == "52m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[x]}", 42 * isq::length[m]) == "2am"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[X]}", 42 * isq::length[m]) == "2Am"); #if MP_UNITS_USE_FMTLIB - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[a]}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3a]}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[A]}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3A]}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[a]}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3a]}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[A]}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3A]}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0m"); #else - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[a]}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3a]}", 1.2345678 * isq::length[m]) == "1.3c1p+0m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[A]}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3A]}", 1.2345678 * isq::length[m]) == "1.3C1P+0m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[a]}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3a]}", 1.2345678 * isq::length[m]) == "1.3c1p+0m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[A]}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3A]}", 1.2345678 * isq::length[m]) == "1.3C1P+0m"); #endif - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[e]}", 1.2345678 * isq::length[m]) == "1.234568e+00m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3e]}", 1.2345678 * isq::length[m]) == "1.235e+00m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[E]}", 1.2345678 * isq::length[m]) == "1.234568E+00m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3E]}", 1.2345678 * isq::length[m]) == "1.235E+00m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[g]}", 1.2345678 * isq::length[m]) == "1.23457m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[g]}", 1.2345678e8 * isq::length[m]) == "1.23457e+08m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3g]}", 1.2345678 * isq::length[m]) == "1.23m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3g]}", 1.2345678e8 * isq::length[m]) == "1.23e+08m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[G]}", 1.2345678 * isq::length[m]) == "1.23457m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[G]}", 1.2345678e8 * isq::length[m]) == "1.23457E+08m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3G]}", 1.2345678 * isq::length[m]) == "1.23m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3G]}", 1.2345678e8 * isq::length[m]) == "1.23E+08m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[e]}", 1.2345678 * isq::length[m]) == "1.234568e+00m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3e]}", 1.2345678 * isq::length[m]) == "1.235e+00m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[E]}", 1.2345678 * isq::length[m]) == "1.234568E+00m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3E]}", 1.2345678 * isq::length[m]) == "1.235E+00m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[g]}", 1.2345678 * isq::length[m]) == "1.23457m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[g]}", 1.2345678e8 * isq::length[m]) == "1.23457e+08m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3g]}", 1.2345678 * isq::length[m]) == "1.23m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3g]}", 1.2345678e8 * isq::length[m]) == "1.23e+08m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[G]}", 1.2345678 * isq::length[m]) == "1.23457m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[G]}", 1.2345678e8 * isq::length[m]) == "1.23457E+08m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3G]}", 1.2345678 * isq::length[m]) == "1.23m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[.3G]}", 1.2345678e8 * isq::length[m]) == "1.23E+08m"); + } } - } - SECTION("value only format {:%N} on a quantity") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[b]}", 42 * isq::length[m]) == "101010"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[B]}", 42 * isq::length[m]) == "101010"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[d]}", 42 * isq::length[m]) == "42"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[o]}", 42 * isq::length[m]) == "52"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[x]}", 42 * isq::length[m]) == "2a"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[X]}", 42 * isq::length[m]) == "2A"); + SECTION("value only format {:%N} on a quantity") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[b]}", 42 * isq::length[m]) == "101010"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[B]}", 42 * isq::length[m]) == "101010"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[d]}", 42 * isq::length[m]) == "42"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[o]}", 42 * isq::length[m]) == "52"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[x]}", 42 * isq::length[m]) == "2a"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[X]}", 42 * isq::length[m]) == "2A"); #if MP_UNITS_USE_FMTLIB - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[a]}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3a]}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[A]}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3A]}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[a]}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3a]}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[A]}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3A]}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0"); #else - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[a]}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3a]}", 1.2345678 * isq::length[m]) == "1.3c1p+0"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[A]}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3A]}", 1.2345678 * isq::length[m]) == "1.3C1P+0"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[a]}", 1.2345678 * isq::length[m]) == "1.3c0ca2a5b1d5dp+0"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3a]}", 1.2345678 * isq::length[m]) == "1.3c1p+0"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[A]}", 1.2345678 * isq::length[m]) == "1.3C0CA2A5B1D5DP+0"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3A]}", 1.2345678 * isq::length[m]) == "1.3C1P+0"); #endif - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[e]}", 1.2345678 * isq::length[m]) == "1.234568e+00"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3e]}", 1.2345678 * isq::length[m]) == "1.235e+00"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[E]}", 1.2345678 * isq::length[m]) == "1.234568E+00"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3E]}", 1.2345678 * isq::length[m]) == "1.235E+00"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[g]}", 1.2345678 * isq::length[m]) == "1.23457"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[g]}", 1.2345678e8 * isq::length[m]) == "1.23457e+08"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3g]}", 1.2345678 * isq::length[m]) == "1.23"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3g]}", 1.2345678e8 * isq::length[m]) == "1.23e+08"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[G]}", 1.2345678 * isq::length[m]) == "1.23457"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[G]}", 1.2345678e8 * isq::length[m]) == "1.23457E+08"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3G]}", 1.2345678 * isq::length[m]) == "1.23"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3G]}", 1.2345678e8 * isq::length[m]) == "1.23E+08"); - } -} - -TEST_CASE("different base types with the # specifier", "[text][fmt]") -{ - SECTION("full format {:%N%?%U} on a quantity") - { - SECTION("default spec") - { - CHECK(MP_UNITS_STD_FMT::format("{::N[#b]}", 42 * isq::length[m]) == "0b101010 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[#B]}", 42 * isq::length[m]) == "0B101010 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[#o]}", 42 * isq::length[m]) == "052 m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[#x]}", 42 * isq::length[m]) == "0x2a m"); - CHECK(MP_UNITS_STD_FMT::format("{::N[#X]}", 42 * isq::length[m]) == "0X2A m"); - } - - SECTION("explicit spec") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#b]}", 42 * isq::length[m]) == "0b101010 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#B]}", 42 * isq::length[m]) == "0B101010 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#o]}", 42 * isq::length[m]) == "052 m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#x]}", 42 * isq::length[m]) == "0x2a m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#X]}", 42 * isq::length[m]) == "0X2A m"); - } - - SECTION("modified spec") - { - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#b]}", 42 * isq::length[m]) == "0b101010m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#B]}", 42 * isq::length[m]) == "0B101010m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#o]}", 42 * isq::length[m]) == "052m"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#x]}", 42 * isq::length[m]) == "0x2am"); - CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#X]}", 42 * isq::length[m]) == "0X2Am"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[e]}", 1.2345678 * isq::length[m]) == "1.234568e+00"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3e]}", 1.2345678 * isq::length[m]) == "1.235e+00"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[E]}", 1.2345678 * isq::length[m]) == "1.234568E+00"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3E]}", 1.2345678 * isq::length[m]) == "1.235E+00"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[g]}", 1.2345678 * isq::length[m]) == "1.23457"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[g]}", 1.2345678e8 * isq::length[m]) == "1.23457e+08"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3g]}", 1.2345678 * isq::length[m]) == "1.23"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3g]}", 1.2345678e8 * isq::length[m]) == "1.23e+08"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[G]}", 1.2345678 * isq::length[m]) == "1.23457"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[G]}", 1.2345678e8 * isq::length[m]) == "1.23457E+08"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3G]}", 1.2345678 * isq::length[m]) == "1.23"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[.3G]}", 1.2345678e8 * isq::length[m]) == "1.23E+08"); } } - SECTION("value only format {:%N} on a quantity") + SECTION("different base types with the # specifier") { - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#b]}", 42 * isq::length[m]) == "0b101010"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#B]}", 42 * isq::length[m]) == "0B101010"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#o]}", 42 * isq::length[m]) == "052"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#x]}", 42 * isq::length[m]) == "0x2a"); - CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#X]}", 42 * isq::length[m]) == "0X2A"); + SECTION("full format {:%N%?%U} on a quantity") + { + SECTION("default spec") + { + CHECK(MP_UNITS_STD_FMT::format("{::N[#b]}", 42 * isq::length[m]) == "0b101010 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[#B]}", 42 * isq::length[m]) == "0B101010 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[#o]}", 42 * isq::length[m]) == "052 m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[#x]}", 42 * isq::length[m]) == "0x2a m"); + CHECK(MP_UNITS_STD_FMT::format("{::N[#X]}", 42 * isq::length[m]) == "0X2A m"); + } + + SECTION("explicit spec") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#b]}", 42 * isq::length[m]) == "0b101010 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#B]}", 42 * isq::length[m]) == "0B101010 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#o]}", 42 * isq::length[m]) == "052 m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#x]}", 42 * isq::length[m]) == "0x2a m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%?%U:N[#X]}", 42 * isq::length[m]) == "0X2A m"); + } + + SECTION("modified spec") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#b]}", 42 * isq::length[m]) == "0b101010m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#B]}", 42 * isq::length[m]) == "0B101010m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#o]}", 42 * isq::length[m]) == "052m"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#x]}", 42 * isq::length[m]) == "0x2am"); + CHECK(MP_UNITS_STD_FMT::format("{:%N%U:N[#X]}", 42 * isq::length[m]) == "0X2Am"); + } + } + + SECTION("value only format {:%N} on a quantity") + { + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#b]}", 42 * isq::length[m]) == "0b101010"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#B]}", 42 * isq::length[m]) == "0B101010"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#o]}", 42 * isq::length[m]) == "052"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#x]}", 42 * isq::length[m]) == "0x2a"); + CHECK(MP_UNITS_STD_FMT::format("{:%N:N[#X]}", 42 * isq::length[m]) == "0X2A"); + } } -} -TEST_CASE("localization with the 'L' specifier", "[text][fmt][localization]") -{ - struct group2 : std::numpunct { - [[nodiscard]] char do_thousands_sep() const override { return '_'; } - [[nodiscard]] std::string do_grouping() const override { return "\2"; } - }; - - struct group3 : std::numpunct { - [[nodiscard]] char do_thousands_sep() const override { return '\''; } - [[nodiscard]] std::string do_grouping() const override { return "\3"; } - }; - - const std::locale grp2{std::locale::classic(), new group2}; - const std::locale grp3{std::locale::classic(), new group3}; - - SECTION("full format on a quantity") + SECTION("localization with the 'L' specifier") { - SECTION("default spec") - { - CHECK(MP_UNITS_STD_FMT::format(grp2, "{::N[L]}", 299'792'458 * isq::speed[m / s]) == "2_99_79_24_58 m/s"); - CHECK(MP_UNITS_STD_FMT::format(grp3, "{::N[L]}", 299'792'458 * isq::speed[m / s]) == "299'792'458 m/s"); - } + struct group2 : std::numpunct { + [[nodiscard]] char do_thousands_sep() const override { return '_'; } + [[nodiscard]] std::string do_grouping() const override { return "\2"; } + }; - SECTION("explicit spec") - { - CHECK(MP_UNITS_STD_FMT::format(grp2, "{:%N%?%U:N[L]}", 299'792'458 * isq::speed[m / s]) == "2_99_79_24_58 m/s"); - CHECK(MP_UNITS_STD_FMT::format(grp3, "{:%N%?%U:N[L]}", 299'792'458 * isq::speed[m / s]) == "299'792'458 m/s"); - } + struct group3 : std::numpunct { + [[nodiscard]] char do_thousands_sep() const override { return '\''; } + [[nodiscard]] std::string do_grouping() const override { return "\3"; } + }; - SECTION("modified spec") + const std::locale grp2{std::locale::classic(), new group2}; + const std::locale grp3{std::locale::classic(), new group3}; + + SECTION("full format on a quantity") { - CHECK(MP_UNITS_STD_FMT::format(grp2, "{:%N%U:N[L]}", 299'792'458 * isq::speed[m / s]) == "2_99_79_24_58m/s"); - CHECK(MP_UNITS_STD_FMT::format(grp3, "{:%N%U:N[L]}", 299'792'458 * isq::speed[m / s]) == "299'792'458m/s"); + SECTION("default spec") + { + CHECK(MP_UNITS_STD_FMT::format(grp2, "{::N[L]}", 299'792'458 * isq::speed[m / s]) == "2_99_79_24_58 m/s"); + CHECK(MP_UNITS_STD_FMT::format(grp3, "{::N[L]}", 299'792'458 * isq::speed[m / s]) == "299'792'458 m/s"); + } + + SECTION("explicit spec") + { + CHECK(MP_UNITS_STD_FMT::format(grp2, "{:%N%?%U:N[L]}", 299'792'458 * isq::speed[m / s]) == "2_99_79_24_58 m/s"); + CHECK(MP_UNITS_STD_FMT::format(grp3, "{:%N%?%U:N[L]}", 299'792'458 * isq::speed[m / s]) == "299'792'458 m/s"); + } + + SECTION("modified spec") + { + CHECK(MP_UNITS_STD_FMT::format(grp2, "{:%N%U:N[L]}", 299'792'458 * isq::speed[m / s]) == "2_99_79_24_58m/s"); + CHECK(MP_UNITS_STD_FMT::format(grp3, "{:%N%U:N[L]}", 299'792'458 * isq::speed[m / s]) == "299'792'458m/s"); + } } } } -TEST_CASE("unit_symbol", "[text]") -{ - using enum text_encoding; - using enum unit_symbol_solidus; - using enum unit_symbol_separator; - - std::ostringstream os; - - SECTION("default formatting") - { - os << unit_symbol(m / s2); - CHECK(os.str() == "m/s²"); - } - - SECTION("Portable mode") - { - os << unit_symbol(m / s2); - CHECK(os.str() == "m/s^2"); - } - - SECTION("solidus") - { - os << unit_symbol(m / s2); - CHECK(os.str() == "m s⁻²"); - } - - SECTION("separator") - { - os << unit_symbol(m / s2); - CHECK(os.str() == "m⋅s⁻²"); - } -} - -TEST_CASE("dimension_symbol", "[text]") -{ - using enum text_encoding; - - std::ostringstream os; - - SECTION("default formatting") - { - os << dimension_symbol(isq::power.dimension); - CHECK(os.str() == "L²MT⁻³"); - } - - SECTION("Portable mode") - { - os << dimension_symbol(isq::power.dimension); - CHECK(os.str() == "L^2MT^-3"); - } -} - -TEST_CASE("value_cast", "[text][ostream]") +TEST_CASE("check if `value_cast` properly changes the numerical value of a quantity", "[value_cast][ostream]") { std::ostringstream os; diff --git a/test/runtime/math_test.cpp b/test/runtime/math_test.cpp index 79018bff..994c9eeb 100644 --- a/test/runtime/math_test.cpp +++ b/test/runtime/math_test.cpp @@ -43,486 +43,488 @@ using namespace mp_units::si::unit_symbols; // classical -TEST_CASE("'pow()' 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()' 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()' 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]); } + REQUIRE(sqrt(4 * isq::area[m2]) == 2 * isq::length[m]); } - SECTION("'abs()' on a positive quantity returns the abs") + SECTION("'cbrt()' on quantity changes the value and the dimension accordingly") { - 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(isq::length[m]).numerical_value_in(m) == - std::numeric_limits::epsilon()); - } - SECTION("'epsilon' works as expected using integers") - { - REQUIRE(epsilon(isq::length[m]).numerical_value_in(m) == - std::numeric_limits::epsilon()); - } -} - -TEST_CASE("floor functions", "[floor]") -{ - SECTION("floor 1 second with target unit second should be 1 second") - { - REQUIRE(floor(1 * isq::time[s]) == 1 * isq::time[s]); - } - SECTION("floor 1000 milliseconds with target unit second should be 1 second") - { - REQUIRE(floor(1000 * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("floor 1001 milliseconds with target unit second should be 1 second") - { - REQUIRE(floor(1001 * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("floor 1999 milliseconds with target unit second should be 1 second") - { - REQUIRE(floor(1999 * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("floor -1000 milliseconds with target unit second should be -1 second") - { - REQUIRE(floor(-1000 * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("floor -999 milliseconds with target unit second should be -1 second") - { - REQUIRE(floor(-999 * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("floor 1.3 seconds with target unit second should be 1 second") - { - REQUIRE(floor(1.3 * isq::time[s]) == 1 * isq::time[s]); - } - SECTION("floor -1.3 seconds with target unit second should be -2 seconds") - { - REQUIRE(floor(-1.3 * isq::time[s]) == -2 * isq::time[s]); - } - SECTION("floor 1001. milliseconds with target unit second should be 1 second") - { - REQUIRE(floor(1001. * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("floor 1999. milliseconds with target unit second should be 1 second") - { - REQUIRE(floor(1999. * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("floor -1000. milliseconds with target unit second should be -1 second") - { - REQUIRE(floor(-1000. * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("floor -999. milliseconds with target unit second should be -1 second") - { - REQUIRE(floor(-999. * isq::time[ms]) == -1 * isq::time[s]); + REQUIRE(cbrt(8 * isq::volume[m3]) == 2 * isq::length[m]); } - // 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") + SECTION("'fma()' on quantity changes the value and the dimension accordingly") { - REQUIRE(ceil(1 * isq::time[s]) == 1 * isq::time[s]); - } - SECTION("ceil 1000 milliseconds with target unit second should be 1 second") - { - REQUIRE(ceil(1000 * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("ceil 1001 milliseconds with target unit second should be 2 seconds") - { - REQUIRE(ceil(1001 * isq::time[ms]) == 2 * isq::time[s]); - } - SECTION("ceil 1999 milliseconds with target unit second should be 2 seconds") - { - REQUIRE(ceil(1999 * isq::time[ms]) == 2 * isq::time[s]); - } - SECTION("ceil -1000 milliseconds with target unit second should be -1 second") - { - REQUIRE(ceil(-1000 * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("ceil -999 milliseconds with target unit second should be 0 seconds") - { - REQUIRE(ceil(-999 * isq::time[ms]) == 0 * isq::time[s]); - } - SECTION("ceil 1.3 seconds with target unit second should be 2 seconds") - { - REQUIRE(ceil(1.3 * isq::time[s]) == 2 * isq::time[s]); - } - SECTION("ceil -1.3 seconds with target unit second should be -1 second") - { - REQUIRE(ceil(-1.3 * isq::time[s]) == -1 * isq::time[s]); - } - SECTION("ceil 1001. milliseconds with target unit second should be 2 seconds") - { - REQUIRE(ceil(1001. * isq::time[ms]) == 2 * isq::time[s]); - } - SECTION("ceil 1999. milliseconds with target unit second should be 2 seconds") - { - REQUIRE(ceil(1999. * isq::time[ms]) == 2 * isq::time[s]); - } - SECTION("ceil -1000. milliseconds with target unit second should be -1 second") - { - REQUIRE(ceil(-1000. * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("ceil -999. milliseconds with target unit second should be 0 seconds") - { - REQUIRE(ceil(-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(1 * isq::time[s]) == 1 * isq::time[s]); - } - SECTION("round 1000 milliseconds with target unit second should be 1 second") - { - REQUIRE(round(1000 * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("round 1001 milliseconds with target unit second should be 1 second") - { - REQUIRE(round(1001 * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("round 1499 milliseconds with target unit second should be 1 second") - { - REQUIRE(round(1499 * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("round 1500 milliseconds with target unit second should be 2 seconds") - { - REQUIRE(round(1500 * isq::time[ms]) == 2 * isq::time[s]); - } - SECTION("round 1999 milliseconds with target unit second should be 2 seconds") - { - REQUIRE(round(1999 * isq::time[ms]) == 2 * isq::time[s]); - } - SECTION("round -1000 milliseconds with target unit second should be -1 second") - { - REQUIRE(round(-1000 * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("round -1001 milliseconds with target unit second should be -1 second") - { - REQUIRE(round(-1001 * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("round -1499 milliseconds with target unit second should be -1 second") - { - REQUIRE(round(-1499 * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("round -1500 milliseconds with target unit second should be -2 seconds") - { - REQUIRE(round(-1500 * isq::time[ms]) == -2 * isq::time[s]); - } - SECTION("round -1999 milliseconds with target unit second should be -2 seconds") - { - REQUIRE(round(-1999 * isq::time[ms]) == -2 * isq::time[s]); - } - SECTION("round 1000. milliseconds with target unit second should be 1 second") - { - REQUIRE(round(1000. * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("round 1001. milliseconds with target unit second should be 1 second") - { - REQUIRE(round(1001. * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("round 1499. milliseconds with target unit second should be 1 second") - { - REQUIRE(round(1499. * isq::time[ms]) == 1 * isq::time[s]); - } - SECTION("round 1500. milliseconds with target unit second should be 2 seconds") - { - REQUIRE(round(1500. * isq::time[ms]) == 2 * isq::time[s]); - } - SECTION("round 1999. milliseconds with target unit second should be 2 seconds") - { - REQUIRE(round(1999. * isq::time[ms]) == 2 * isq::time[s]); - } - SECTION("round -1000. milliseconds with target unit second should be -1 second") - { - REQUIRE(round(-1000. * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("round -1001. milliseconds with target unit second should be -1 second") - { - REQUIRE(round(-1001. * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("round -1499. milliseconds with target unit second should be -1 second") - { - REQUIRE(round(-1499. * isq::time[ms]) == -1 * isq::time[s]); - } - SECTION("round -1500. milliseconds with target unit second should be -2 seconds") - { - REQUIRE(round(-1500. * isq::time[ms]) == -2 * isq::time[s]); - } - SECTION("round -1999. milliseconds with target unit second should be -2 seconds") - { - REQUIRE(round(-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)); + 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("cos") + SECTION("fmod functions") { - 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("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("tan") + SECTION("remainder functions") { - 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("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("acos") + 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()' on quantity changes the value and the dimension accordingly") { - 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)); + REQUIRE(pow<1, 4>(16 * isq::area[m2]) == sqrt(4 * isq::length[m])); } - SECTION("atan") + // TODO add tests for exp() + + SECTION("absolute functions on quantity returns the absolute value") { - 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("'abs()' on a negative quantity returns the abs") + { + SECTION("integral representation") { REQUIRE(abs(-1 * isq::length[m]) == 1 * isq::length[m]); } -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)); - } -} + 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]); } -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("floating-point representation") { REQUIRE(abs(1. * isq::length[m]) == 1 * isq::length[m]); } + } } - SECTION("cos") + SECTION("numeric_limits functions") { - 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("'epsilon' works as expected using default floating type") + { + REQUIRE(epsilon(isq::length[m]).numerical_value_in(m) == + std::numeric_limits::epsilon()); + } + SECTION("'epsilon' works as expected using integers") + { + REQUIRE(epsilon(isq::length[m]).numerical_value_in(m) == + std::numeric_limits::epsilon()); + } } - SECTION("tan") + SECTION("floor functions") { - 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)); + SECTION("floor 1 second with target unit second should be 1 second") + { + REQUIRE(floor(1 * isq::time[s]) == 1 * isq::time[s]); + } + SECTION("floor 1000 milliseconds with target unit second should be 1 second") + { + REQUIRE(floor(1000 * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("floor 1001 milliseconds with target unit second should be 1 second") + { + REQUIRE(floor(1001 * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("floor 1999 milliseconds with target unit second should be 1 second") + { + REQUIRE(floor(1999 * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("floor -1000 milliseconds with target unit second should be -1 second") + { + REQUIRE(floor(-1000 * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("floor -999 milliseconds with target unit second should be -1 second") + { + REQUIRE(floor(-999 * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("floor 1.3 seconds with target unit second should be 1 second") + { + REQUIRE(floor(1.3 * isq::time[s]) == 1 * isq::time[s]); + } + SECTION("floor -1.3 seconds with target unit second should be -2 seconds") + { + REQUIRE(floor(-1.3 * isq::time[s]) == -2 * isq::time[s]); + } + SECTION("floor 1001. milliseconds with target unit second should be 1 second") + { + REQUIRE(floor(1001. * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("floor 1999. milliseconds with target unit second should be 1 second") + { + REQUIRE(floor(1999. * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("floor -1000. milliseconds with target unit second should be -1 second") + { + REQUIRE(floor(-1000. * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("floor -999. milliseconds with target unit second should be -1 second") + { + REQUIRE(floor(-999. * isq::time[ms]) == -1 * isq::time[s]); + } - 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])); + // TODO Add tests for `N`, `kN` and `kg * m / s2` i `kg * km / s2` } - SECTION("acos") + SECTION("ceil functions") { - 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("ceil 1 second with target unit second should be 1 second") + { + REQUIRE(ceil(1 * isq::time[s]) == 1 * isq::time[s]); + } + SECTION("ceil 1000 milliseconds with target unit second should be 1 second") + { + REQUIRE(ceil(1000 * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("ceil 1001 milliseconds with target unit second should be 2 seconds") + { + REQUIRE(ceil(1001 * isq::time[ms]) == 2 * isq::time[s]); + } + SECTION("ceil 1999 milliseconds with target unit second should be 2 seconds") + { + REQUIRE(ceil(1999 * isq::time[ms]) == 2 * isq::time[s]); + } + SECTION("ceil -1000 milliseconds with target unit second should be -1 second") + { + REQUIRE(ceil(-1000 * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("ceil -999 milliseconds with target unit second should be 0 seconds") + { + REQUIRE(ceil(-999 * isq::time[ms]) == 0 * isq::time[s]); + } + SECTION("ceil 1.3 seconds with target unit second should be 2 seconds") + { + REQUIRE(ceil(1.3 * isq::time[s]) == 2 * isq::time[s]); + } + SECTION("ceil -1.3 seconds with target unit second should be -1 second") + { + REQUIRE(ceil(-1.3 * isq::time[s]) == -1 * isq::time[s]); + } + SECTION("ceil 1001. milliseconds with target unit second should be 2 seconds") + { + REQUIRE(ceil(1001. * isq::time[ms]) == 2 * isq::time[s]); + } + SECTION("ceil 1999. milliseconds with target unit second should be 2 seconds") + { + REQUIRE(ceil(1999. * isq::time[ms]) == 2 * isq::time[s]); + } + SECTION("ceil -1000. milliseconds with target unit second should be -1 second") + { + REQUIRE(ceil(-1000. * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("ceil -999. milliseconds with target unit second should be 0 seconds") + { + REQUIRE(ceil(-999. * isq::time[ms]) == 0 * isq::time[s]); + } } - SECTION("atan") + SECTION("round functions") { - 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("round 1 second with target unit second should be 1 second") + { + REQUIRE(round(1 * isq::time[s]) == 1 * isq::time[s]); + } + SECTION("round 1000 milliseconds with target unit second should be 1 second") + { + REQUIRE(round(1000 * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("round 1001 milliseconds with target unit second should be 1 second") + { + REQUIRE(round(1001 * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("round 1499 milliseconds with target unit second should be 1 second") + { + REQUIRE(round(1499 * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("round 1500 milliseconds with target unit second should be 2 seconds") + { + REQUIRE(round(1500 * isq::time[ms]) == 2 * isq::time[s]); + } + SECTION("round 1999 milliseconds with target unit second should be 2 seconds") + { + REQUIRE(round(1999 * isq::time[ms]) == 2 * isq::time[s]); + } + SECTION("round -1000 milliseconds with target unit second should be -1 second") + { + REQUIRE(round(-1000 * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("round -1001 milliseconds with target unit second should be -1 second") + { + REQUIRE(round(-1001 * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("round -1499 milliseconds with target unit second should be -1 second") + { + REQUIRE(round(-1499 * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("round -1500 milliseconds with target unit second should be -2 seconds") + { + REQUIRE(round(-1500 * isq::time[ms]) == -2 * isq::time[s]); + } + SECTION("round -1999 milliseconds with target unit second should be -2 seconds") + { + REQUIRE(round(-1999 * isq::time[ms]) == -2 * isq::time[s]); + } + SECTION("round 1000. milliseconds with target unit second should be 1 second") + { + REQUIRE(round(1000. * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("round 1001. milliseconds with target unit second should be 1 second") + { + REQUIRE(round(1001. * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("round 1499. milliseconds with target unit second should be 1 second") + { + REQUIRE(round(1499. * isq::time[ms]) == 1 * isq::time[s]); + } + SECTION("round 1500. milliseconds with target unit second should be 2 seconds") + { + REQUIRE(round(1500. * isq::time[ms]) == 2 * isq::time[s]); + } + SECTION("round 1999. milliseconds with target unit second should be 2 seconds") + { + REQUIRE(round(1999. * isq::time[ms]) == 2 * isq::time[s]); + } + SECTION("round -1000. milliseconds with target unit second should be -1 second") + { + REQUIRE(round(-1000. * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("round -1001. milliseconds with target unit second should be -1 second") + { + REQUIRE(round(-1001. * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("round -1499. milliseconds with target unit second should be -1 second") + { + REQUIRE(round(-1499. * isq::time[ms]) == -1 * isq::time[s]); + } + SECTION("round -1500. milliseconds with target unit second should be -2 seconds") + { + REQUIRE(round(-1500. * isq::time[ms]) == -2 * isq::time[s]); + } + SECTION("round -1999. milliseconds with target unit second should be -2 seconds") + { + REQUIRE(round(-1999. * isq::time[ms]) == -2 * isq::time[s]); + } } -} -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("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("atan2 should work on the same quantities") + SECTION("SI trigonometric functions") { - 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("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("atan2 should work with different units of the same dimension") + + SECTION("SI inverse trigonometric functions") { - 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])); + 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])); + } + } +} \ No newline at end of file diff --git a/test/runtime/quantity_test.cpp b/test/runtime/quantity_test.cpp index 14dec787..fcfdbad8 100644 --- a/test/runtime/quantity_test.cpp +++ b/test/runtime/quantity_test.cpp @@ -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); + } +} \ No newline at end of file From c6344c26eed58e18f9019a3b8b9867fef195f0c7 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 11:31:26 +0100 Subject: [PATCH 05/14] style: missing empty line at the end of the file added --- test/runtime/distribution_test.cpp | 2 +- test/runtime/math_test.cpp | 2 +- test/runtime/quantity_test.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/runtime/distribution_test.cpp b/test/runtime/distribution_test.cpp index e18d5a9e..1ea3da1d 100644 --- a/test/runtime/distribution_test.cpp +++ b/test/runtime/distribution_test.cpp @@ -660,4 +660,4 @@ TEST_CASE("distributions", "[random][distribution]") CHECK(units_dist.densities() == stl_dist.densities()); } } -} \ No newline at end of file +} diff --git a/test/runtime/math_test.cpp b/test/runtime/math_test.cpp index 994c9eeb..bee5f7e8 100644 --- a/test/runtime/math_test.cpp +++ b/test/runtime/math_test.cpp @@ -527,4 +527,4 @@ TEST_CASE("math operations", "[math]") REQUIRE_THAT(atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * angle[deg])); } } -} \ No newline at end of file +} diff --git a/test/runtime/quantity_test.cpp b/test/runtime/quantity_test.cpp index fcfdbad8..0984373e 100644 --- a/test/runtime/quantity_test.cpp +++ b/test/runtime/quantity_test.cpp @@ -81,4 +81,4 @@ TEST_CASE("quantity operations", "[quantity]") volatile std::int16_t vint = 123; REQUIRE(quantity(vint * m).numerical_value_in(m) == 123); } -} \ No newline at end of file +} From 5ddbd62ea483dc92e5ef5a37adb12917e3cbc1f5 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 14:19:39 +0100 Subject: [PATCH 06/14] feat(example): `is_vector` specialization no longer needed for `si_constants` --- docs/users_guide/examples/si_constants.md | 14 +++----------- example/si_constants.cpp | 4 ---- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/docs/users_guide/examples/si_constants.md b/docs/users_guide/examples/si_constants.md index f5feb153..847520f4 100644 --- a/docs/users_guide/examples/si_constants.md +++ b/docs/users_guide/examples/si_constants.md @@ -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 diff --git a/example/si_constants.cpp b/example/si_constants.cpp index e09588c4..a43779ab 100644 --- a/example/si_constants.cpp +++ b/example/si_constants.cpp @@ -39,10 +39,6 @@ import mp_units; #include #endif -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - int main() { using namespace mp_units; From e4044f0e4be85bda8e9464d5ef5f35e488067841 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 14:20:19 +0100 Subject: [PATCH 07/14] feat: `ComplexRepresentation` extended with complex operations --- .../mp-units/framework/representation_concepts.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/include/mp-units/framework/representation_concepts.h b/src/core/include/mp-units/framework/representation_concepts.h index 4ddbcb88..8be28d05 100644 --- a/src/core/include/mp-units/framework/representation_concepts.h +++ b/src/core/include/mp-units/framework/representation_concepts.h @@ -118,12 +118,11 @@ concept ComplexRepresentation = Complex && WeaklyRegular && 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? From d8574022f1d1ba2ab1b676e159bac085778fa85a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 14:21:36 +0100 Subject: [PATCH 08/14] test: _surface tension_ replaced with _entropy_ in an fmt test --- test/runtime/fmt_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/runtime/fmt_test.cpp b/test/runtime/fmt_test.cpp index 4011588e..bda19763 100644 --- a/test/runtime/fmt_test.cpp +++ b/test/runtime/fmt_test.cpp @@ -402,12 +402,12 @@ TEST_CASE("default quantity formatting", "[quantity][ostream][fmt]") } } - SECTION("surface tension") + SECTION("entropy") { - const auto q = 20 * isq::force[N] / (2 * isq::length[m]); + const auto q = 20 * isq::kinetic_energy[J] / (2 * isq::thermodynamic_temperature[K]); os << q; - SECTION("iostream") { CHECK(os.str() == "10 N/m"); } + SECTION("iostream") { CHECK(os.str() == "10 J/K"); } SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); } From 6c3c1fe5f72d690c89637f22f603a46abc03fdb3 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 14:34:18 +0100 Subject: [PATCH 09/14] feat: `cartesian_vector` added --- src/core/CMakeLists.txt | 1 + src/core/include/mp-units/cartesian_vector.h | 230 ++++++++++++ src/core/include/mp-units/core.h | 1 + test/runtime/CMakeLists.txt | 5 +- test/runtime/cartesian_vector_test.cpp | 374 +++++++++++++++++++ 5 files changed, 609 insertions(+), 2 deletions(-) create mode 100644 src/core/include/mp-units/cartesian_vector.h create mode 100644 test/runtime/cartesian_vector_test.cpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2132d71a..e6251cf2 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/include/mp-units/cartesian_vector.h b/src/core/include/mp-units/cartesian_vector.h new file mode 100644 index 00000000..7227f193 --- /dev/null +++ b/src/core/include/mp-units/cartesian_vector.h @@ -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 +// +#include +#include + +#if MP_UNITS_HOSTED +#include +#endif + +#ifndef MP_UNITS_IN_MODULE_INTERFACE +#ifdef MP_UNITS_IMPORT_STD +import std; +#else +#include +#include +#include +#if MP_UNITS_HOSTED +#include +#endif +#endif +#endif + +namespace mp_units { + +MP_UNITS_EXPORT template +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 Arg1, std::convertible_to... Args> + constexpr cartesian_vector(Arg1&& arg1, Args&&... args) : + _coordinates_(std::forward(arg1), std::forward(args)...) + { + } + + template U> + constexpr cartesian_vector(const cartesian_vector& other) : _coordinates_{other[0], other[1], other[2]} + { + } + + template U> + constexpr cartesian_vector(cartesian_vector&& other) : + _coordinates_{std::move(other[0]), std::move(other[1]), std::move(other[2])} + { + } + + template U> + constexpr cartesian_vector& operator=(const cartesian_vector& other) + { + _coordinates_[0] = other[0]; + _coordinates_[1] = other[1]; + _coordinates_[2] = other[2]; + return *this; + } + + template U> + constexpr cartesian_vector& operator=(cartesian_vector&& 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 + { + return std::hypot(_coordinates_[0], _coordinates_[1], _coordinates_[2]); + } + + [[nodiscard]] constexpr cartesian_vector unit() const + requires treat_as_floating_point + { + 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 U, typename V> + requires requires(U u, V v) { u + v; } + [[nodiscard]] friend constexpr auto operator+(const cartesian_vector& lhs, const cartesian_vector& 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 U, typename V> + requires requires(U u, V v) { u - v; } + [[nodiscard]] friend constexpr auto operator-(const cartesian_vector& lhs, const cartesian_vector& 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 U> + requires requires(U u, T t) { u* t; } + [[nodiscard]] friend constexpr auto operator*(const cartesian_vector& lhs, const T& rhs) + { + return ::mp_units::cartesian_vector{lhs._coordinates_[0] * rhs, lhs._coordinates_[1] * rhs, + lhs._coordinates_[2] * rhs}; + } + + template U> + requires requires(T t, U u) { t* u; } + [[nodiscard]] friend constexpr auto operator*(const T& lhs, const cartesian_vector& rhs) + { + return rhs * lhs; + } + + template U> + requires requires(U u, T t) { u / t; } + [[nodiscard]] friend constexpr auto operator/(const cartesian_vector& lhs, const T& rhs) + { + return ::mp_units::cartesian_vector{lhs._coordinates_[0] / rhs, lhs._coordinates_[1] / rhs, + lhs._coordinates_[2] / rhs}; + } + + template U, std::equality_comparable_with V> + [[nodiscard]] friend constexpr bool operator==(const cartesian_vector& lhs, const cartesian_vector& 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& v) + requires treat_as_floating_point + { + return v.magnitude(); + } + + [[nodiscard]] friend constexpr cartesian_vector unit_vector(const cartesian_vector& v) + requires treat_as_floating_point + { + return v.unit(); + } + + template 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& lhs, const cartesian_vector& rhs) + { + return lhs._coordinates_[0] * rhs._coordinates_[0] + lhs._coordinates_[1] * rhs._coordinates_[1] + + lhs._coordinates_[2] * rhs._coordinates_[2]; + } + + template 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& lhs, const cartesian_vector& 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& v) + { + return os << '[' << v[0] << ", " << v[1] << ", " << v[2] << ']'; + } +#endif +}; + +template + requires(sizeof...(Args) <= 2) && requires { typename std::common_type_t; } +cartesian_vector(Arg, Args...) -> cartesian_vector>; + +template +constexpr bool is_vector> = true; + +} // namespace mp_units + +#if MP_UNITS_HOSTED +// TODO use parse and use formatter for the underlying type +template +struct MP_UNITS_STD_FMT::formatter, Char> : + formatter, Char> { + template + auto format(const mp_units::cartesian_vector& v, FormatContext& ctx) const + { + return format_to(ctx.out(), "[{}, {}, {}]", v[0], v[1], v[2]); + } +}; +#endif diff --git a/src/core/include/mp-units/core.h b/src/core/include/mp-units/core.h index a99978f8..702f3b6b 100644 --- a/src/core/include/mp-units/core.h +++ b/src/core/include/mp-units/core.h @@ -28,6 +28,7 @@ #include #if MP_UNITS_HOSTED +#include #include #include #include diff --git a/test/runtime/CMakeLists.txt b/test/runtime/CMakeLists.txt index d79ed54b..c1d990ba 100644 --- a/test/runtime/CMakeLists.txt +++ b/test/runtime/CMakeLists.txt @@ -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) diff --git a/test/runtime/cartesian_vector_test.cpp b/test/runtime/cartesian_vector_test.cpp new file mode 100644 index 00000000..04dfe28b --- /dev/null +++ b/test/runtime/cartesian_vector_test.cpp @@ -0,0 +1,374 @@ +// 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 +#include +#ifdef MP_UNITS_MODULES +import mp_units; +#else +#include +#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()); } + } +} From 5a206c3ef1d684df22e5a44800cc43f3346b3ab8 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 14:38:51 +0100 Subject: [PATCH 10/14] test: `cartesian_vector` used in fmt_test --- test/runtime/fmt_test.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/runtime/fmt_test.cpp b/test/runtime/fmt_test.cpp index bda19763..d375f091 100644 --- a/test/runtime/fmt_test.cpp +++ b/test/runtime/fmt_test.cpp @@ -39,6 +39,7 @@ import std; #ifdef MP_UNITS_MODULES import mp_units; #else +#include #include #include // IWYU pragma: keep #include @@ -48,12 +49,9 @@ import mp_units; #include #endif -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - using namespace mp_units; using namespace mp_units::si::unit_symbols; +using v = cartesian_vector; TEST_CASE("dimension_symbol", "[dimension][symbol]") { @@ -437,10 +435,10 @@ TEST_CASE("default quantity formatting", "[quantity][ostream][fmt]") SECTION("angular impulse") { - const auto q = 123 * isq::angular_impulse[N * m * s]; + const auto q = v{1, 2, 3} * isq::angular_impulse[N * m * s]; os << q; - SECTION("iostream") { CHECK(os.str() == "123 m N s"); } + SECTION("iostream") { CHECK(os.str() == "[1, 2, 3] m N s"); } SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); } @@ -467,10 +465,10 @@ TEST_CASE("default quantity formatting", "[quantity][ostream][fmt]") SECTION("angular acceleration") { - const auto q = 123 * isq::angular_acceleration[rad / s2]; + const auto q = v{1, 2, 3} * isq::angular_acceleration[rad / s2]; os << q; - SECTION("iostream") { CHECK(os.str() == "123 rad/s²"); } + SECTION("iostream") { CHECK(os.str() == "[1, 2, 3] rad/s²"); } SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); } @@ -779,7 +777,7 @@ TEST_CASE("quantity subentities selection", "[quantity][fmt]") CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::speed[km / h]) == "km/h"); CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::resistance[si::kilo]) == "kΩ"); CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::time[us]) == "µs"); - CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * isq::acceleration[m / s2]) == "m/s²"); + CHECK(MP_UNITS_STD_FMT::format("{:%U}", v{1, 2, 3} * isq::acceleration[m / s2]) == "m/s²"); CHECK(MP_UNITS_STD_FMT::format("{:%U}", 123 * percent) == "%"); } From 520610ab2f91e9ecbfcbc19f754bfc4767936616 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 14:39:04 +0100 Subject: [PATCH 11/14] test: `cartesian_vector` used in cgs_test --- test/static/cgs_test.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/static/cgs_test.cpp b/test/static/cgs_test.cpp index c027b601..9ea79b1f 100644 --- a/test/static/cgs_test.cpp +++ b/test/static/cgs_test.cpp @@ -25,24 +25,28 @@ #include #include #include - -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; +#if MP_UNITS_HOSTED +#include +#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; +#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)); From 73ad1f08d46f752f934a7106a49cb64fd279111c Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 16:20:51 +0100 Subject: [PATCH 12/14] fix: fmt_test fixed to use `delta` to create a quantity of `thermodynamic_temperature` --- test/runtime/fmt_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtime/fmt_test.cpp b/test/runtime/fmt_test.cpp index d375f091..a163c187 100644 --- a/test/runtime/fmt_test.cpp +++ b/test/runtime/fmt_test.cpp @@ -402,7 +402,7 @@ TEST_CASE("default quantity formatting", "[quantity][ostream][fmt]") SECTION("entropy") { - const auto q = 20 * isq::kinetic_energy[J] / (2 * isq::thermodynamic_temperature[K]); + const auto q = 20 * isq::kinetic_energy[J] / (delta(2)); os << q; SECTION("iostream") { CHECK(os.str() == "10 J/K"); } From 1ee824423e1c28c91a28e28765990123f201ec3d Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 16:22:14 +0100 Subject: [PATCH 13/14] refactor: usage of `v` for the function argument name changed to prevent shadowing errors --- example/include/validated_type.h | 4 +- src/core/include/mp-units/cartesian_vector.h | 16 ++++---- .../include/mp-units/ext/inplace_vector.h | 8 ++-- .../include/mp-units/framework/quantity.h | 40 +++++++++---------- src/core/include/mp-units/ostream.h | 12 +++--- .../include/mp-units/systems/si/chrono.h | 8 ++-- 6 files changed, 44 insertions(+), 44 deletions(-) diff --git a/example/include/validated_type.h b/example/include/validated_type.h index c8b5dc6a..70be6dac 100644 --- a/example/include/validated_type.h +++ b/example/include/validated_type.h @@ -129,8 +129,8 @@ std::basic_ostream& operator<<(std::basic_ostream& template struct MP_UNITS_STD_FMT::formatter, Char> : formatter { template - auto format(const validated_type& v, FormatContext& ctx) const -> decltype(ctx.out()) + auto format(const validated_type& val, FormatContext& ctx) const -> decltype(ctx.out()) { - return formatter::format(v.value(), ctx); + return formatter::format(val.value(), ctx); } }; diff --git a/src/core/include/mp-units/cartesian_vector.h b/src/core/include/mp-units/cartesian_vector.h index 7227f193..0749fae0 100644 --- a/src/core/include/mp-units/cartesian_vector.h +++ b/src/core/include/mp-units/cartesian_vector.h @@ -163,16 +163,16 @@ public: lhs._coordinates_[2] == rhs._coordinates_[2]; } - [[nodiscard]] friend constexpr T norm(const cartesian_vector& v) + [[nodiscard]] friend constexpr T norm(const cartesian_vector& vec) requires treat_as_floating_point { - return v.magnitude(); + return vec.magnitude(); } - [[nodiscard]] friend constexpr cartesian_vector unit_vector(const cartesian_vector& v) + [[nodiscard]] friend constexpr cartesian_vector unit_vector(const cartesian_vector& vec) requires treat_as_floating_point { - return v.unit(); + return vec.unit(); } template U, typename V> @@ -200,9 +200,9 @@ public: } #if MP_UNITS_HOSTED - friend constexpr std::ostream& operator<<(std::ostream& os, const cartesian_vector& v) + friend constexpr std::ostream& operator<<(std::ostream& os, const cartesian_vector& vec) { - return os << '[' << v[0] << ", " << v[1] << ", " << v[2] << ']'; + return os << '[' << vec[0] << ", " << vec[1] << ", " << vec[2] << ']'; } #endif }; @@ -222,9 +222,9 @@ template struct MP_UNITS_STD_FMT::formatter, Char> : formatter, Char> { template - auto format(const mp_units::cartesian_vector& v, FormatContext& ctx) const + auto format(const mp_units::cartesian_vector& vec, FormatContext& ctx) const { - return format_to(ctx.out(), "[{}, {}, {}]", v[0], v[1], v[2]); + return format_to(ctx.out(), "[{}, {}, {}]", vec[0], vec[1], vec[2]); } }; #endif diff --git a/src/core/include/mp-units/ext/inplace_vector.h b/src/core/include/mp-units/ext/inplace_vector.h index 1e93caf4..8074b09b 100644 --- a/src/core/include/mp-units/ext/inplace_vector.h +++ b/src/core/include/mp-units/ext/inplace_vector.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 { - return emplace_back(v); + return emplace_back(val); } - constexpr reference push_back(T&& v) + constexpr reference push_back(T&& val) requires std::constructible_from { - return emplace_back(std::forward(v)); + return emplace_back(std::forward(val)); } template diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index bfd55b92..71bbc699 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -174,21 +174,21 @@ public: template requires detail::SameValueAs, Rep> - constexpr quantity(FwdValue&& v, R2) : numerical_value_is_an_implementation_detail_(std::forward(v)) + constexpr quantity(FwdValue&& val, R2) : numerical_value_is_an_implementation_detail_(std::forward(val)) { } template> requires(!detail::SameValueAs) && detail::QuantityConvertibleTo, quantity> - constexpr quantity(FwdValue&& v, R2) : quantity(quantity{std::forward(v), R2{}}) + constexpr quantity(FwdValue&& val, R2) : quantity(quantity{std::forward(val), R2{}}) { } template FwdValue> requires(unit == ::mp_units::one) - constexpr explicit(false) quantity(FwdValue&& v) : - numerical_value_is_an_implementation_detail_(std::forward(v)) + constexpr explicit(false) quantity(FwdValue&& val) : + numerical_value_is_an_implementation_detail_(std::forward(val)) { } @@ -214,9 +214,9 @@ public: template FwdValue> requires(unit == ::mp_units::one) - constexpr quantity& operator=(FwdValue&& v) + constexpr quantity& operator=(FwdValue&& val) { - numerical_value_is_an_implementation_detail_ = std::forward(v); + numerical_value_is_an_implementation_detail_ = std::forward(val); return *this; } @@ -422,11 +422,11 @@ public: requires(!Quantity) && requires(rep a, Value b) { { a *= b } -> std::same_as; } - 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(lhs); } @@ -444,12 +444,12 @@ public: requires(!Quantity) && requires(rep a, Value b) { { a /= b } -> std::same_as; } - 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::zero()); + MP_UNITS_EXPECTS_DEBUG(val != quantity_values::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(lhs); } @@ -551,17 +551,17 @@ public: template Q, typename Value> requires(!Quantity) && (!Reference) && detail::InvokeResultOf, Rep, const Value&> - [[nodiscard]] friend constexpr QuantityOf auto operator*(const Q& q, const Value& v) + [[nodiscard]] friend constexpr QuantityOf 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 Q> requires(!Quantity) && (!Reference) && detail::InvokeResultOf, const Value&, Rep> - [[nodiscard]] friend constexpr QuantityOf auto operator*(const Value& v, const Q& q) + [[nodiscard]] friend constexpr QuantityOf 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 Q, auto R2, typename Rep2> @@ -575,18 +575,18 @@ public: template Q, typename Value> requires(!Quantity) && (!Reference) && detail::InvokeResultOf, Rep, const Value&> - [[nodiscard]] friend constexpr QuantityOf auto operator/(const Q& q, const Value& v) + [[nodiscard]] friend constexpr QuantityOf auto operator/(const Q& q, const Value& val) { - MP_UNITS_EXPECTS_DEBUG(v != quantity_values::zero()); - return ::mp_units::quantity{q.numerical_value_ref_in(unit) / v, R}; + MP_UNITS_EXPECTS_DEBUG(val != quantity_values::zero()); + return ::mp_units::quantity{q.numerical_value_ref_in(unit) / val, R}; } template Q> requires(!Quantity) && (!Reference) && detail::InvokeResultOf, const Value&, Rep> - [[nodiscard]] friend constexpr QuantityOf auto operator/(const Value& v, const Q& q) + [[nodiscard]] friend constexpr QuantityOf 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 Q, auto R2, typename Rep2> diff --git a/src/core/include/mp-units/ostream.h b/src/core/include/mp-units/ostream.h index 15b6127b..1b3f63ac 100644 --- a/src/core/include/mp-units/ostream.h +++ b/src/core/include/mp-units/ostream.h @@ -68,8 +68,8 @@ void to_stream_impl(std::basic_ostream& os, const quantity -std::basic_ostream& to_stream(std::basic_ostream& os, const T& v) - requires requires { detail::to_stream_impl(os, v); } +std::basic_ostream& to_stream(std::basic_ostream& 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& to_stream(std::basic_ostream& 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 -std::basic_ostream& operator<<(std::basic_ostream& os, const T& v) +std::basic_ostream& operator<<(std::basic_ostream& os, const T& val) requires detail::is_mp_units_stream, T> { - return detail::to_stream(os, v); + return detail::to_stream(os, val); } MP_UNITS_EXPORT_END diff --git a/src/systems/include/mp-units/systems/si/chrono.h b/src/systems/include/mp-units/systems/si/chrono.h index a856fc15..51434421 100644 --- a/src/systems/include/mp-units/systems/si/chrono.h +++ b/src/systems/include/mp-units/systems/si/chrono.h @@ -83,10 +83,10 @@ struct quantity_like_traits> { 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) { - return T(v); + return T(val); } }; @@ -113,10 +113,10 @@ struct quantity_point_like_traits) { - return T(std::chrono::duration(v)); + return T(std::chrono::duration(val)); } }; From 420ffef6c57a7f0b2cd67e5e252ee38f96f56ddb Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 12 Nov 2024 18:12:36 +0100 Subject: [PATCH 14/14] fix(test): missing header files added --- test/runtime/cartesian_vector_test.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/runtime/cartesian_vector_test.cpp b/test/runtime/cartesian_vector_test.cpp index 04dfe28b..252134d0 100644 --- a/test/runtime/cartesian_vector_test.cpp +++ b/test/runtime/cartesian_vector_test.cpp @@ -23,6 +23,13 @@ #include "almost_equals.h" #include #include +#include +#include +#ifdef MP_UNITS_IMPORT_STD +import std; +#else +#include +#endif #ifdef MP_UNITS_MODULES import mp_units; #else