From d171f5451e127f989eba11bdae76b0f498d01586 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 27 Jun 2020 19:15:46 +0200 Subject: [PATCH] NTTP ratio support added (resolves #49) --- docs/CHANGELOG.md | 1 + docs/framework/units.rst | 67 +++-- docs/reference/core/concepts.rst | 9 +- docs/use_cases/extensions.rst | 10 +- src/include/units/bits/base_units_ratio.h | 35 +-- src/include/units/bits/common_quantity.h | 10 +- src/include/units/bits/deduced_unit.h | 44 +-- src/include/units/bits/dimension_op.h | 6 +- src/include/units/bits/ratio_maths.h | 36 ++- src/include/units/bits/to_string.h | 39 ++- src/include/units/concepts.h | 41 +-- src/include/units/data/information.h | 2 +- src/include/units/data/prefixes.h | 12 +- src/include/units/derived_dimension.h | 2 +- src/include/units/exp.h | 6 +- src/include/units/math.h | 6 +- src/include/units/physical/fps/force.h | 2 +- src/include/units/physical/fps/length.h | 14 +- src/include/units/physical/fps/mass.h | 18 +- src/include/units/physical/fps/power.h | 2 +- src/include/units/physical/fps/pressure.h | 4 +- src/include/units/physical/iau/length.h | 6 +- src/include/units/physical/imperial/length.h | 4 +- .../units/physical/international/length.h | 14 +- .../units/physical/si/catalytic_activity.h | 2 +- src/include/units/physical/si/energy.h | 2 +- src/include/units/physical/si/length.h | 2 +- .../units/physical/si/magnetic_induction.h | 2 +- src/include/units/physical/si/mass.h | 2 +- src/include/units/physical/si/prefixes.h | 40 +-- src/include/units/physical/si/time.h | 6 +- .../units/physical/typographic/length.h | 8 +- src/include/units/physical/us/length.h | 6 +- src/include/units/prefix.h | 6 +- src/include/units/quantity.h | 21 +- src/include/units/quantity_cast.h | 158 +++++----- src/include/units/ratio.h | 282 +++++------------- src/include/units/unit.h | 23 +- test/unit_test/runtime/fmt_test.cpp | 4 +- test/unit_test/runtime/fmt_units_test.cpp | 4 +- test/unit_test/static/cgs_test.cpp | 2 +- test/unit_test/static/fps_test.cpp | 2 +- test/unit_test/static/quantity_point_test.cpp | 2 +- test/unit_test/static/quantity_test.cpp | 16 +- test/unit_test/static/ratio_test.cpp | 170 +++++------ test/unit_test/static/si_test.cpp | 2 +- test/unit_test/static/unit_test.cpp | 16 +- 47 files changed, 504 insertions(+), 664 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d521e942..067cf947 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,6 +8,7 @@ - `math.h` function signatures refactored to use a `Quantity` concept (thanks [@kwikius](https://github.com/kwikius)) - FPS system added (thanks [@mikeford3](https://github.com/mikeford3)) - `quantity_point` support added (thanks [@johelegp](https://github.com/johelegp)) + - `ratio` changed to the NTTP kind - **0.5.0 May 17, 2020** - Major refactoring and rewrite of the library diff --git a/docs/framework/units.rst b/docs/framework/units.rst index 58e19d0b..a10586f4 100644 --- a/docs/framework/units.rst +++ b/docs/framework/units.rst @@ -186,20 +186,23 @@ Those units are the scaled versions of a time dimension's base unit, namely second. Those can be defined easily in the library using `named_scaled_unit` class template:: - struct minute : named_scaled_unit, second> {}; - struct hour : named_scaled_unit, minute> {}; - struct day : named_scaled_unit, hour> {}; + struct minute : named_scaled_unit {}; + struct hour : named_scaled_unit {}; + struct day : named_scaled_unit {}; where `no_prefix` is a special tag type describing that the library should not allow to define a new prefixed unit that would use this unit as a reference ("kilohours" does not have much sense, right?). The `ratio` type used in the definition is really similar to ``std::ratio`` but it takes -the third additional argument that defines the exponent of the ratio. +an additional ``Exp`` template parameter that defines the exponent of the ratio. +Another important difference is the fact that the objects of that class are used +as class NTTPs rather then a type template parameter kind. + Thanks to it we can address nearly infinite scaling factors between units and define units like:: struct electronvolt : named_scaled_unit, joule> {}; + ratio(1'602'176'634, 1'000'000'000, -19), joule> {}; .. TODO Submit a bug for above lexing problem @@ -221,26 +224,26 @@ complete list of all the :term:`SI` prefixes supported by the library:: struct prefix : prefix_family {}; - struct yocto : units::prefix> {}; - struct zepto : units::prefix> {}; - struct atto : units::prefix> {}; - struct femto : units::prefix> {}; - struct pico : units::prefix> {}; - struct nano : units::prefix> {}; - struct micro : units::prefix> {}; - struct milli : units::prefix> {}; - struct centi : units::prefix> {}; - struct deci : units::prefix> {}; - struct deca : units::prefix> {}; - struct hecto : units::prefix> {}; - struct kilo : units::prefix> {}; - struct mega : units::prefix> {}; - struct giga : units::prefix> {}; - struct tera : units::prefix> {}; - struct peta : units::prefix> {}; - struct exa : units::prefix> {}; - struct zetta : units::prefix> {}; - struct yotta : units::prefix> {}; + struct yocto : units::prefix {}; + struct zepto : units::prefix {}; + struct atto : units::prefix {}; + struct femto : units::prefix {}; + struct pico : units::prefix {}; + struct nano : units::prefix {}; + struct micro : units::prefix {}; + struct milli : units::prefix {}; + struct centi : units::prefix {}; + struct deci : units::prefix {}; + struct deca : units::prefix {}; + struct hecto : units::prefix {}; + struct kilo : units::prefix {}; + struct mega : units::prefix {}; + struct giga : units::prefix {}; + struct tera : units::prefix {}; + struct peta : units::prefix {}; + struct exa : units::prefix {}; + struct zetta : units::prefix {}; + struct yotta : units::prefix {}; } @@ -251,12 +254,12 @@ domain:: struct prefix : prefix_family {}; - struct kibi : units::prefix> {}; - struct mebi : units::prefix> {}; - struct gibi : units::prefix> {}; - struct tebi : units::prefix> {}; - struct pebi : units::prefix> {}; - struct exbi : units::prefix> {}; + struct kibi : units::prefix {}; + struct mebi : units::prefix {}; + struct gibi : units::prefix {}; + struct tebi : units::prefix {}; + struct pebi : units::prefix {}; + struct exbi : units::prefix {}; } @@ -381,7 +384,7 @@ unknown/undefined unit type like in the below example:: Length auto l = 100q_km_per_h * 10q_s; The type of ``l`` above will be -:expr:`si::length, si::metre>, long double>`. This is caused +:expr:`si::length, long double>`. This is caused by the fact that the library does not define a unit of a length quantity that has the ratio ``10/36`` of a `si::metre`. If such a unit was predefined we would see its concrete type here instead. diff --git a/docs/reference/core/concepts.rst b/docs/reference/core/concepts.rst index adbe3999..79e8367d 100644 --- a/docs/reference/core/concepts.rst +++ b/docs/reference/core/concepts.rst @@ -16,14 +16,9 @@ Concepts A concept matching a symbol prefix. Satisfied by all instantiations of :class:`prefix`. -.. concept:: template Ratio +.. concept:: template UnitRatio - A concept matching a ratio. Satisfied by all instantiations of :class:`ratio`. - -.. concept:: template UnitRatio - - A concept matching unit's ratio. Satisfied by all types that satisfy :expr:`Ratio` and - for which :expr:`R::num > 0` and :expr:`R::den > 0`. + Satisfied by all ratio values for which :expr:`R.num > 0` and :expr:`R.den > 0`. .. concept:: template BaseDimension diff --git a/docs/use_cases/extensions.rst b/docs/use_cases/extensions.rst index 1f00456a..219a049d 100644 --- a/docs/use_cases/extensions.rst +++ b/docs/use_cases/extensions.rst @@ -22,7 +22,7 @@ Defining a New Unit My working desk is of ``180 cm x 60 cm`` which gives an area of ``0.3 m²``. I would like to make it a unit of area for my project:: - struct desk : named_scaled_unit, si::square_metre> {}; + struct desk : named_scaled_unit {}; With the above I can define a quantity with the area of ``2 desks``:: @@ -59,7 +59,7 @@ Enabling a Unit for Prefixing In case I decide it is reasonable to express my desks with SI prefixes the only thing I have to change in the above code is to replace `no_prefix` with `si_prefix`:: - struct desk : named_scaled_unit, si::square_metre> {}; + struct desk : named_scaled_unit {}; Now I can define a new unit named ``kilodesk``:: @@ -72,12 +72,12 @@ prefix family and prefixes are needed:: struct shipping_prefix : prefix_family {}; - struct package : prefix> {}; - struct lorry : prefix> {}; + struct package : prefix {}; + struct lorry : prefix {}; Now we can use it for our unit:: - struct desk : named_scaled_unit, si::square_metre> {}; + struct desk : named_scaled_unit {}; struct packagedesk : prefixed_unit {}; struct lorrydesk : prefixed_unit {}; diff --git a/src/include/units/bits/base_units_ratio.h b/src/include/units/bits/base_units_ratio.h index 1d6fbdcd..c8aa2855 100644 --- a/src/include/units/bits/base_units_ratio.h +++ b/src/include/units/bits/base_units_ratio.h @@ -30,31 +30,22 @@ namespace units::detail { template requires (E::den == 1 || E::den == 2) // TODO provide support for any den -struct exp_ratio { - using base_ratio = E::dimension::base_unit::ratio; - using positive_ratio = conditional, base_ratio>; - static constexpr std::intmax_t N = E::num * E::den < 0 ? -E::num : E::num; - using pow = ratio_pow; - using type = conditional, pow>; -}; - -template -struct base_units_ratio_impl; - -template -struct base_units_ratio_impl> { - using type = ratio_multiply::type, typename base_units_ratio_impl>::type>; -}; - -template -struct base_units_ratio_impl> { - using type = exp_ratio::type; -}; +constexpr ratio exp_ratio() +{ + const ratio base_ratio = E::dimension::base_unit::ratio; + const ratio positive_ratio = E::num * E::den < 0 ? ratio(base_ratio.den, base_ratio.num, -base_ratio.exp) : base_ratio; + const std::intmax_t N = E::num * E::den < 0 ? -E::num : E::num; + const ratio ratio_pow = pow(positive_ratio); + return E::den == 2 ? sqrt(ratio_pow) : ratio_pow; +} /** * @brief Calculates the common ratio of all the references of base units in the derived dimension */ -template -using base_units_ratio = base_units_ratio_impl::type; +template +constexpr ratio base_units_ratio(exp_list) +{ + return (exp_ratio() * ...); +} } // namespace units::detail diff --git a/src/include/units/bits/common_quantity.h b/src/include/units/bits/common_quantity.h index 7278426c..e9fc7a58 100644 --- a/src/include/units/bits/common_quantity.h +++ b/src/include/units/bits/common_quantity.h @@ -44,20 +44,20 @@ struct common_quantity_impl, quantity, Rep> { template struct common_quantity_impl, quantity, Rep> { - using type = quantity>, Rep>; + using type = quantity, Rep>; }; template requires same_unit_reference, dimension_unit>::value struct common_quantity_impl, quantity, Rep> { - using type = quantity>, Rep>; + using type = quantity, Rep>; }; template struct common_quantity_impl, quantity, Rep> { - using ratio1 = ratio_multiply; - using ratio2 = ratio_multiply; - using type = quantity>, Rep>; + static constexpr ratio r1 = D1::base_units_ratio * U1::ratio; + static constexpr ratio r2 = D2::base_units_ratio * U2::ratio; + using type = quantity, Rep>; }; template diff --git a/src/include/units/bits/deduced_unit.h b/src/include/units/bits/deduced_unit.h index e8832da6..8eee6368 100644 --- a/src/include/units/bits/deduced_unit.h +++ b/src/include/units/bits/deduced_unit.h @@ -34,39 +34,23 @@ template inline constexpr bool same_scaled_units, Us...> = (UnitOf && ...); // deduced_unit -template -struct ratio_op; -template -struct ratio_op { - using ratio = Result; -}; +template +constexpr ratio inverse_if_negative(const ratio& r) +{ + if constexpr(E::num * E::den > 0) + return r; + else + return inverse(r); +} -template -struct ratio_op { - using calc_ratio = - conditional<(UnitExpNum * UnitExpDen > 0), ratio_multiply, ratio_divide>; - static constexpr int value = (UnitExpNum * UnitExpDen > 0) ? (UnitExpNum - UnitExpDen) : (UnitExpNum + UnitExpDen); - using ratio = ratio_op::ratio; -}; - -template -struct derived_ratio; - -template -struct derived_ratio, Us...> { - using ratio = ::units::ratio<1>; -}; - -template -struct derived_ratio, U, URest...> { - using rest_ratio = derived_ratio, URest...>::ratio; - using unit_ratio = ratio_op::ratio; - using ratio = ratio_divide::ratio>; -}; +template +constexpr ratio derived_ratio(exp_list) +{ + return (... * inverse_if_negative(pow(Us::ratio) / dimension_unit::ratio)); +} template -using deduced_unit = - scaled_unit::ratio, typename D::coherent_unit::reference>; +using deduced_unit = scaled_unit(typename D::recipe()), typename D::coherent_unit::reference>; } // namespace units::detail diff --git a/src/include/units/bits/dimension_op.h b/src/include/units/bits/dimension_op.h index f0f54ac1..58526c6e 100644 --- a/src/include/units/bits/dimension_op.h +++ b/src/include/units/bits/dimension_op.h @@ -66,14 +66,14 @@ inline constexpr bool equivalent_dim = detail::equivalent_dim_impl::valu * * Sometimes a temporary partial result of a complex calculation may not result in a predefined * dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit` - * and ratio<1>. + * and ratio(1). * * @tparam E the list of exponents of ingredient dimensions * @tparam ERest the list of exponents of ingredient dimensions */ template -struct unknown_dimension : derived_dimension, scaled_unit, unknown_coherent_unit>, E, ERest...> { - using coherent_unit = scaled_unit, unknown_coherent_unit>; +struct unknown_dimension : derived_dimension, scaled_unit, E, ERest...> { + using coherent_unit = scaled_unit; }; namespace detail { diff --git a/src/include/units/bits/ratio_maths.h b/src/include/units/bits/ratio_maths.h index b11cdeea..c56e95e2 100644 --- a/src/include/units/bits/ratio_maths.h +++ b/src/include/units/bits/ratio_maths.h @@ -23,7 +23,6 @@ #pragma once #include -#include #include #include #include @@ -46,7 +45,7 @@ template // Computes (a * b) mod m relies on unsigned integer arithmetic, should not // overflow -constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m) +[[nodiscard]] constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m) { std::uint64_t res = 0; @@ -78,7 +77,7 @@ constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m } // Calculates (a ^ e) mod m , should not overflow. -constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m) +[[nodiscard]] constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m) { a %= m; std::uint64_t result = 1; @@ -94,7 +93,7 @@ constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m } // gcd(a * 10 ^ e, b), should not overflow -constexpr std::intmax_t gcdpow(std::intmax_t a, std::intmax_t e, std::intmax_t b) noexcept +[[nodiscard]] constexpr std::intmax_t gcdpow(std::intmax_t a, std::intmax_t e, std::intmax_t b) noexcept { assert(a > 0); assert(e >= 0); @@ -120,8 +119,8 @@ constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs) } // Computes the rational gcd of n1/d1 x 10^e1 and n2/d2 x 10^e2 -constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, std::intmax_t n2, std::intmax_t d2, - std::intmax_t e2) noexcept +[[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, std::intmax_t n2, std::intmax_t d2, + std::intmax_t e2) noexcept { // Short cut for equal ratios if (n1 == n2 && d1 == d2 && e1 == e2) { @@ -152,8 +151,14 @@ constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, st return std::array{num / gcd, den / gcd, exp}; } -constexpr auto normalize(std::intmax_t num, std::intmax_t den, std::intmax_t exp) +constexpr void normalize(std::intmax_t& num, std::intmax_t& den, std::intmax_t& exp) { + if(num == 0) { + den = 1; + exp = 0; + return; + } + std::intmax_t gcd = std::gcd(num, den); num = num * (den < 0 ? -1 : 1) / gcd; den = detail::abs(den) / gcd; @@ -166,8 +171,23 @@ constexpr auto normalize(std::intmax_t num, std::intmax_t den, std::intmax_t exp den /= 10; --exp; } +} - return std::array{num, den, exp}; +[[nodiscard]] static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs) +{ + constexpr std::intmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4); + + const std::intmax_t a0 = detail::abs(lhs) % c; + const std::intmax_t a1 = detail::abs(lhs) / c; + const std::intmax_t b0 = detail::abs(rhs) % c; + const std::intmax_t b1 = detail::abs(rhs) / c; + + Expects(a1 == 0 || b1 == 0); // overflow in multiplication + Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication + Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication + Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication + + return lhs * rhs; } } // namespace units::detail diff --git a/src/include/units/bits/to_string.h b/src/include/units/bits/to_string.h index 0c9f4988..7b052cb3 100644 --- a/src/include/units/bits/to_string.h +++ b/src/include/units/bits/to_string.h @@ -32,31 +32,31 @@ namespace units::detail { inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10"); -template +template constexpr auto ratio_text() { - if constexpr(Ratio::num == 1 && Ratio::den == 1 && Ratio::exp != 0) { - return base_multiplier + superscript() + basic_fixed_string(" "); + if constexpr(R.num == 1 && R.den == 1 && R.exp != 0) { + return base_multiplier + superscript() + basic_fixed_string(" "); } - else if constexpr(Ratio::num != 1 || Ratio::den != 1 || Ratio::exp != 0) { - auto txt = basic_fixed_string("[") + regular(); - if constexpr(Ratio::den == 1) { - if constexpr(Ratio::exp == 0) { + else if constexpr(R.num != 1 || R.den != 1 || R.exp != 0) { + auto txt = basic_fixed_string("[") + regular(); + if constexpr(R.den == 1) { + if constexpr(R.exp == 0) { return txt + basic_fixed_string("] "); } else { - return txt + " " + base_multiplier + superscript() + + return txt + " " + base_multiplier + superscript() + basic_fixed_string("] "); } } else { - if constexpr(Ratio::exp == 0) { - return txt + basic_fixed_string("/") + regular() + + if constexpr(R.exp == 0) { + return txt + basic_fixed_string("/") + regular() + basic_fixed_string("] "); } else { - return txt + basic_fixed_string("/") + regular() + - " " + base_multiplier + superscript() + + return txt + basic_fixed_string("/") + regular() + + " " + base_multiplier + superscript() + basic_fixed_string("] "); } } @@ -66,30 +66,30 @@ constexpr auto ratio_text() } } -template +template constexpr auto prefix_or_ratio_text() { - if constexpr(Ratio::num == 1 && Ratio::den == 1 && Ratio::exp == 0) { + if constexpr(R.num == 1 && R.den == 1 && R.exp == 0) { // no ratio/prefix return basic_fixed_string(""); } else { if constexpr (!std::is_same_v) { // try to form a prefix - using prefix = downcast>; + using prefix = downcast>; - if constexpr(!std::is_same_v>) { + if constexpr(!std::is_same_v>) { // print as a prefixed unit return prefix::symbol; } else { // print as a ratio of the coherent unit - return ratio_text(); + return ratio_text(); } } else { // print as a ratio of the coherent unit - return ratio_text(); + return ratio_text(); } } } @@ -150,8 +150,7 @@ constexpr auto unit_text() else { // print as a prefix or ratio of a coherent unit using coherent_unit = dimension_unit; - using ratio = ratio_divide; - auto prefix_txt = prefix_or_ratio_text(); + auto prefix_txt = prefix_or_ratio_text(); if constexpr(has_symbol) { // use predefined coherent unit symbol diff --git a/src/include/units/concepts.h b/src/include/units/concepts.h index a45aadb4..d21ef282 100644 --- a/src/include/units/concepts.h +++ b/src/include/units/concepts.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace units { @@ -63,40 +64,40 @@ template concept Prefix = true; -namespace detail { - -template -inline constexpr bool is_ratio = false; - -} // namespace detail - -/** - * @brief A concept matching a ratio - * - * Satisfied by all instantiations of `ratio`. - */ -template -concept Ratio = detail::is_ratio; - /** * @brief A concept matching unit's ratio * - * Satisfied by all types that satisfy `Ratio` and for which `R::num > 0` and `R::den > 0` + * Satisfied by all ratio values for which `R.num > 0` and `R.den > 0`. */ -template -concept UnitRatio = Ratio && R::num > 0 && R::den > 0; // double negatives not allowed +template +concept UnitRatio = R.num > 0 && R.den > 0; // Unit -template +template + requires UnitRatio struct scaled_unit; +// TODO: Remove when P1985 accepted +namespace detail { + +struct is_derived_from_scaled_unit_impl { + template + static constexpr std::true_type check_base(const scaled_unit&); + static constexpr std::false_type check_base(...); +}; + +template +inline constexpr bool is_derived_from_scaled_unit = decltype(is_derived_from_scaled_unit_impl::check_base(std::declval()))::value; + +} // namespace detail + /** * @brief A concept matching all unit types in the library * * Satisfied by all unit types derived from the instantiation of :class:`scaled_unit`. */ template -concept Unit = is_derived_from_instantiation; +concept Unit = detail::is_derived_from_scaled_unit; template requires U::is_named diff --git a/src/include/units/data/information.h b/src/include/units/data/information.h index 8ecd3531..0828f71e 100644 --- a/src/include/units/data/information.h +++ b/src/include/units/data/information.h @@ -36,7 +36,7 @@ struct gibibit : prefixed_unit {}; struct tebibit : prefixed_unit {}; struct pebibit : prefixed_unit {}; -struct byte : named_scaled_unit, bit> {}; +struct byte : named_scaled_unit {}; struct kibibyte : prefixed_unit {}; struct mebibyte : prefixed_unit {}; struct gibibyte : prefixed_unit {}; diff --git a/src/include/units/data/prefixes.h b/src/include/units/data/prefixes.h index 241534bc..a6c74d3c 100644 --- a/src/include/units/data/prefixes.h +++ b/src/include/units/data/prefixes.h @@ -28,11 +28,11 @@ namespace units::data { struct prefix : prefix_family {}; -struct kibi : units::prefix> {}; -struct mebi : units::prefix> {}; -struct gibi : units::prefix> {}; -struct tebi : units::prefix> {}; -struct pebi : units::prefix> {}; -struct exbi : units::prefix> {}; +struct kibi : units::prefix {}; +struct mebi : units::prefix {}; +struct gibi : units::prefix {}; +struct tebi : units::prefix {}; +struct pebi : units::prefix {}; +struct exbi : units::prefix {}; } // namespace units::data diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index cdfdf31f..acc0585b 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -82,7 +82,7 @@ template struct derived_dimension : downcast_child> { using recipe = exp_list; using coherent_unit = U; - using base_units_ratio = detail::base_units_ratio; + static constexpr ratio base_units_ratio = detail::base_units_ratio(typename derived_dimension::exponents()); }; } // namespace units diff --git a/src/include/units/exp.h b/src/include/units/exp.h index 731187aa..2b7f64f8 100644 --- a/src/include/units/exp.h +++ b/src/include/units/exp.h @@ -70,10 +70,8 @@ namespace detail { template struct exp_multiply_impl { - using r1 = ratio; - using r2 = ratio; - using r = ratio_multiply; - using type = exp; + static constexpr ratio r = ratio(E::num, E::den) * ratio(Num, Den); + using type = exp; }; } // namespace detail diff --git a/src/include/units/math.h b/src/include/units/math.h index ae2e3397..d06965a3 100644 --- a/src/include/units/math.h +++ b/src/include/units/math.h @@ -44,8 +44,7 @@ inline Quantity AUTO pow(const Q& q) noexcept requires requires { std::pow(q.count(), N); } { using dim = dimension_pow; - using ratio = ratio_pow; - using unit = downcast_unit; + using unit = downcast_unit(Q::unit::ratio)>; using rep = Q::rep; return quantity(static_cast(std::pow(q.count(), N))); } @@ -75,8 +74,7 @@ inline Quantity AUTO sqrt(const Q& q) noexcept requires requires { std::sqrt(q.count()); } { using dim = dimension_sqrt; - using ratio = ratio_sqrt; - using unit = downcast_unit; + using unit = downcast_unit; using rep = Q::rep; return quantity(static_cast(std::sqrt(q.count()))); } diff --git a/src/include/units/physical/fps/force.h b/src/include/units/physical/fps/force.h index c58fa3df..47a49c16 100644 --- a/src/include/units/physical/fps/force.h +++ b/src/include/units/physical/fps/force.h @@ -34,7 +34,7 @@ namespace units::physical::fps { struct poundal : named_unit {}; // https://en.wikipedia.org/wiki/Pound_(force) -struct pound_force : named_scaled_unit, poundal> {}; +struct pound_force : named_scaled_unit {}; struct kilopound_force : prefixed_unit {}; diff --git a/src/include/units/physical/fps/length.h b/src/include/units/physical/fps/length.h index c4716c9f..d875ecd1 100644 --- a/src/include/units/physical/fps/length.h +++ b/src/include/units/physical/fps/length.h @@ -29,24 +29,24 @@ namespace units::physical::fps { // https://en.wikipedia.org/wiki/Foot_(unit) -struct foot : named_scaled_unit, si::metre> {}; +struct foot : named_scaled_unit {}; -struct inch : named_scaled_unit, foot> {}; +struct inch : named_scaled_unit {}; // thousandth of an inch -struct thousandth : named_scaled_unit, inch> {}; +struct thousandth : named_scaled_unit {}; struct thou : alias_unit{}; struct mil : alias_unit{}; -struct yard : named_scaled_unit, foot> {}; +struct yard : named_scaled_unit {}; -struct fathom : named_scaled_unit, foot> {}; +struct fathom : named_scaled_unit {}; struct kiloyard : prefixed_unit {}; -struct mile : named_scaled_unit, foot> {}; +struct mile : named_scaled_unit {}; -struct nautical_mile : named_scaled_unit, yard> {}; +struct nautical_mile : named_scaled_unit {}; diff --git a/src/include/units/physical/fps/mass.h b/src/include/units/physical/fps/mass.h index 229f5014..80787d1b 100644 --- a/src/include/units/physical/fps/mass.h +++ b/src/include/units/physical/fps/mass.h @@ -29,28 +29,28 @@ namespace units::physical::fps { // https://en.wikipedia.org/wiki/Pound_(mass) -struct pound : named_scaled_unit, si::kilogram> {}; +struct pound : named_scaled_unit {}; struct dim_mass : physical::dim_mass {}; template using mass = quantity; -struct grain : named_scaled_unit, pound>{}; +struct grain : named_scaled_unit{}; -struct dram : named_scaled_unit, pound>{}; +struct dram : named_scaled_unit{}; -struct ounce : named_scaled_unit, pound>{}; +struct ounce : named_scaled_unit{}; -struct stone : named_scaled_unit, pound>{}; +struct stone : named_scaled_unit{}; -struct quarter : named_scaled_unit, pound>{}; +struct quarter : named_scaled_unit{}; -struct hundredweight : named_scaled_unit, pound>{}; +struct hundredweight : named_scaled_unit{}; -struct short_ton : named_scaled_unit, pound>{}; +struct short_ton : named_scaled_unit{}; -struct long_ton : named_scaled_unit, pound>{}; +struct long_ton : named_scaled_unit{}; inline namespace literals { diff --git a/src/include/units/physical/fps/power.h b/src/include/units/physical/fps/power.h index 40df542d..c2142e9a 100644 --- a/src/include/units/physical/fps/power.h +++ b/src/include/units/physical/fps/power.h @@ -35,7 +35,7 @@ struct dim_power : physical::dim_power {}; -struct horse_power : named_scaled_unit, foot_pound_force_per_second> {}; +struct horse_power : named_scaled_unit {}; template using power = quantity; diff --git a/src/include/units/physical/fps/pressure.h b/src/include/units/physical/fps/pressure.h index 7c43d990..36f4d555 100644 --- a/src/include/units/physical/fps/pressure.h +++ b/src/include/units/physical/fps/pressure.h @@ -37,9 +37,9 @@ struct dim_pressure : physical::dim_pressure using pressure = quantity; -struct pound_force_per_foot_sq : named_scaled_unit, poundal_per_foot_sq> {}; +struct pound_force_per_foot_sq : named_scaled_unit {}; -struct pound_force_per_inch_sq : named_scaled_unit, pound_force_per_foot_sq> {}; +struct pound_force_per_inch_sq : named_scaled_unit {}; struct kilopound_force_per_inch_sq : prefixed_unit {}; diff --git a/src/include/units/physical/iau/length.h b/src/include/units/physical/iau/length.h index e7e58d11..0786dcbc 100644 --- a/src/include/units/physical/iau/length.h +++ b/src/include/units/physical/iau/length.h @@ -28,13 +28,13 @@ namespace units::physical::iau { // https://en.wikipedia.org/wiki/Light-year -struct light_year : named_scaled_unit, si::metre> {}; +struct light_year : named_scaled_unit {}; // https://en.wikipedia.org/wiki/Parsec -struct parsec : named_scaled_unit, si::metre> {}; +struct parsec : named_scaled_unit {}; // https://en.wikipedia.org/wiki/Angstrom -struct angstrom : named_scaled_unit, si::metre> {}; +struct angstrom : named_scaled_unit {}; inline namespace literals { diff --git a/src/include/units/physical/imperial/length.h b/src/include/units/physical/imperial/length.h index 0d81aa8f..cbe856a9 100644 --- a/src/include/units/physical/imperial/length.h +++ b/src/include/units/physical/imperial/length.h @@ -27,10 +27,10 @@ namespace units::physical::imperial { // https://en.wikipedia.org/wiki/Chain_(unit) -struct chain : named_scaled_unit, international::yard> {}; +struct chain : named_scaled_unit {}; // https://en.wikipedia.org/wiki/Rod_(unit) -struct rod : named_scaled_unit, chain> {}; +struct rod : named_scaled_unit {}; inline namespace literals { diff --git a/src/include/units/physical/international/length.h b/src/include/units/physical/international/length.h index 3403e6e4..74a0f223 100644 --- a/src/include/units/physical/international/length.h +++ b/src/include/units/physical/international/length.h @@ -29,30 +29,30 @@ namespace units::physical::international { // international yard // https://en.wikipedia.org/wiki/International_yard_and_pound -struct yard : named_scaled_unit, si::metre> {}; +struct yard : named_scaled_unit {}; // international foot // https://en.wikipedia.org/wiki/Foot_(unit)#International_foot -struct foot : named_scaled_unit, yard> {}; +struct foot : named_scaled_unit {}; // https://en.wikipedia.org/wiki/Fathom#International_fathom -struct fathom : named_scaled_unit, yard> {}; +struct fathom : named_scaled_unit {}; // international inch // https://en.wikipedia.org/wiki/Inch#Equivalences -struct inch : named_scaled_unit, yard> {}; +struct inch : named_scaled_unit {}; // intrnational mile // https://en.wikipedia.org/wiki/Mile#International_mile -struct mile : named_scaled_unit, si::kilometre> {}; +struct mile : named_scaled_unit {}; // international nautical mile // https://en.wikipedia.org/wiki/Nautical_mile -struct nautical_mile : named_scaled_unit, si::metre> {}; +struct nautical_mile : named_scaled_unit {}; // thou // https://en.wikipedia.org/wiki/Thousandth_of_an_inch -struct thou : named_scaled_unit, inch> {}; +struct thou : named_scaled_unit {}; // mil - different name for thou // https://en.wikipedia.org/wiki/Thousandth_of_an_inch diff --git a/src/include/units/physical/si/catalytic_activity.h b/src/include/units/physical/si/catalytic_activity.h index 7ae6c0ed..78ad7ca9 100644 --- a/src/include/units/physical/si/catalytic_activity.h +++ b/src/include/units/physical/si/catalytic_activity.h @@ -52,7 +52,7 @@ struct exakatal : prefixed_unit {}; struct zettakatal : prefixed_unit {}; struct yottakatal : prefixed_unit {}; -struct enzyme_unit : named_scaled_unit, katal> {}; +struct enzyme_unit : named_scaled_unit {}; struct dim_catalytic_activity : physical::dim_catalytic_activity {}; diff --git a/src/include/units/physical/si/energy.h b/src/include/units/physical/si/energy.h index 54848946..6deca407 100644 --- a/src/include/units/physical/si/energy.h +++ b/src/include/units/physical/si/energy.h @@ -47,7 +47,7 @@ struct exajoule : prefixed_unit {}; struct zettajoule : prefixed_unit {}; struct yottajoule : prefixed_unit {}; -struct electronvolt : named_scaled_unit, joule> {}; +struct electronvolt : named_scaled_unit {}; struct gigaelectronvolt : prefixed_unit {}; struct dim_energy : physical::dim_energy {}; diff --git a/src/include/units/physical/si/length.h b/src/include/units/physical/si/length.h index a5c51a67..34a881b8 100644 --- a/src/include/units/physical/si/length.h +++ b/src/include/units/physical/si/length.h @@ -50,7 +50,7 @@ struct exametre : prefixed_unit {}; struct zettametre : prefixed_unit {}; struct yottametre : prefixed_unit {}; -struct astronomical_unit : named_scaled_unit, metre> {}; +struct astronomical_unit : named_scaled_unit {}; struct dim_length : physical::dim_length {}; diff --git a/src/include/units/physical/si/magnetic_induction.h b/src/include/units/physical/si/magnetic_induction.h index fc20a69b..441e67b2 100644 --- a/src/include/units/physical/si/magnetic_induction.h +++ b/src/include/units/physical/si/magnetic_induction.h @@ -50,7 +50,7 @@ struct exatesla : prefixed_unit {}; struct zettatesla : prefixed_unit {}; struct yottatesla : prefixed_unit {}; -struct gauss : named_scaled_unit, tesla> {}; +struct gauss : named_scaled_unit {}; struct dim_magnetic_induction : physical::dim_magnetic_induction {}; diff --git a/src/include/units/physical/si/mass.h b/src/include/units/physical/si/mass.h index 7a5e3337..c015e601 100644 --- a/src/include/units/physical/si/mass.h +++ b/src/include/units/physical/si/mass.h @@ -72,7 +72,7 @@ struct exatonne : prefixed_alias_unit {}; struct zettatonne : prefixed_unit {}; struct yottatonne : prefixed_unit {}; -struct dalton : named_scaled_unit, kilogram> {}; +struct dalton : named_scaled_unit {}; struct dim_mass : physical::dim_mass {}; diff --git a/src/include/units/physical/si/prefixes.h b/src/include/units/physical/si/prefixes.h index fd0c5338..e33b45ce 100644 --- a/src/include/units/physical/si/prefixes.h +++ b/src/include/units/physical/si/prefixes.h @@ -29,26 +29,26 @@ namespace units::physical::si { struct prefix : prefix_family {}; // clang-format off -struct yocto : units::prefix> {}; -struct zepto : units::prefix> {}; -struct atto : units::prefix> {}; -struct femto : units::prefix> {}; -struct pico : units::prefix> {}; -struct nano : units::prefix> {}; -struct micro : units::prefix> {}; -struct milli : units::prefix> {}; -struct centi : units::prefix> {}; -struct deci : units::prefix> {}; -struct deca : units::prefix> {}; -struct hecto : units::prefix> {}; -struct kilo : units::prefix> {}; -struct mega : units::prefix> {}; -struct giga : units::prefix> {}; -struct tera : units::prefix> {}; -struct peta : units::prefix> {}; -struct exa : units::prefix> {}; -struct zetta : units::prefix> {}; -struct yotta : units::prefix> {}; +struct yocto : units::prefix {}; +struct zepto : units::prefix {}; +struct atto : units::prefix {}; +struct femto : units::prefix {}; +struct pico : units::prefix {}; +struct nano : units::prefix {}; +struct micro : units::prefix {}; +struct milli : units::prefix {}; +struct centi : units::prefix {}; +struct deci : units::prefix {}; +struct deca : units::prefix {}; +struct hecto : units::prefix {}; +struct kilo : units::prefix {}; +struct mega : units::prefix {}; +struct giga : units::prefix {}; +struct tera : units::prefix {}; +struct peta : units::prefix {}; +struct exa : units::prefix {}; +struct zetta : units::prefix {}; +struct yotta : units::prefix {}; // clang-format on } // namespace units::physical::si diff --git a/src/include/units/physical/si/time.h b/src/include/units/physical/si/time.h index b3778bd8..5c421f09 100644 --- a/src/include/units/physical/si/time.h +++ b/src/include/units/physical/si/time.h @@ -37,9 +37,9 @@ struct picosecond : prefixed_unit {}; struct nanosecond : prefixed_unit {}; struct microsecond : prefixed_unit {}; struct millisecond : prefixed_unit {}; -struct minute : named_scaled_unit, second> {}; -struct hour : named_scaled_unit, minute> {}; -struct day : named_scaled_unit, hour> {}; +struct minute : named_scaled_unit {}; +struct hour : named_scaled_unit {}; +struct day : named_scaled_unit {}; struct dim_time : physical::dim_time {}; diff --git a/src/include/units/physical/typographic/length.h b/src/include/units/physical/typographic/length.h index d1445d1c..0d4080a8 100644 --- a/src/include/units/physical/typographic/length.h +++ b/src/include/units/physical/typographic/length.h @@ -28,10 +28,10 @@ namespace units::physical::typographic { // TODO Conflicts with (https://en.wikipedia.org/wiki/Pica_(typography)), verify correctness of below conversion factors and provide hyperlinks to definitions -struct pica_comp : named_scaled_unit, si::metre> {}; -struct pica_prn : named_scaled_unit, si::metre> {}; -struct point_comp : named_scaled_unit, si::metre> {}; -struct point_prn : named_scaled_unit, si::metre> {}; +struct pica_comp : named_scaled_unit {}; +struct pica_prn : named_scaled_unit {}; +struct point_comp : named_scaled_unit {}; +struct point_prn : named_scaled_unit {}; inline namespace literals { diff --git a/src/include/units/physical/us/length.h b/src/include/units/physical/us/length.h index 13aa1cda..860e3cbf 100644 --- a/src/include/units/physical/us/length.h +++ b/src/include/units/physical/us/length.h @@ -28,14 +28,14 @@ namespace units::physical::us { // https://en.wikipedia.org/wiki/Foot_(unit)#US_survey_foot // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct foot : named_scaled_unit, si::metre> {}; +struct foot : named_scaled_unit {}; // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct fathom : named_scaled_unit, foot> {}; +struct fathom : named_scaled_unit {}; // https://en.wikipedia.org/wiki/Mile#U.S._survey_mile // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct mile : named_scaled_unit, us::foot> {}; +struct mile : named_scaled_unit {}; inline namespace literals { diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index 27dc1065..fbf45665 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -45,10 +45,10 @@ struct no_prefix : prefix_family {}; namespace detail { -template +template struct prefix_base : downcast_base> { using prefix_family = PF; - using ratio = R; + static constexpr ::units::ratio ratio = R; }; } // namespace detail @@ -68,7 +68,7 @@ struct prefix_base : downcast_base> { * @tparam Symbol a text representation of the prefix * @tparam R factor to be used to scale a unit */ -template +template requires (!std::same_as) struct prefix : downcast_child> { static constexpr auto symbol = Symbol; diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 72c083f3..ec00955c 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -46,7 +46,7 @@ concept safe_convertible = // exposition only template concept safe_divisible = // exposition only treat_as_floating_point || - ratio_divide::is_integral(); + is_integral(UnitFrom::ratio / UnitTo::ratio); } // namespace detail @@ -344,11 +344,11 @@ template> { using common_rep = decltype(lhs.count() * rhs.count()); - using ratio = ratio_multiply; + const ratio r = U1::ratio * U2::ratio; if constexpr (treat_as_floating_point) { - return lhs.count() * rhs.count() * static_cast(ratio::num * fpow10(ratio::exp)) / static_cast(ratio::den); + return lhs.count() * rhs.count() * static_cast(r.num * fpow10(r.exp)) / static_cast(r.den); } else { - return lhs.count() * rhs.count() * static_cast(ratio::num * ipow10(ratio::exp)) / static_cast(ratio::den); + return lhs.count() * rhs.count() * static_cast(r.num * ipow10(r.exp)) / static_cast(r.den); } } @@ -357,10 +357,7 @@ template, Rep1, Rep2> { using dim = dimension_multiply; - using ratio1 = ratio_divide::ratio>; - using ratio2 = ratio_divide::ratio>; - using ratio = ratio_multiply, typename dimension_unit::ratio>; - using unit = downcast_unit; + using unit = downcast_unit::ratio) * (U2::ratio / dimension_unit::ratio) * dimension_unit::ratio>; using common_rep = decltype(lhs.count() * rhs.count()); using ret = quantity; return ret(lhs.count() * rhs.count()); @@ -373,8 +370,7 @@ template Expects(q.count() != 0); using dim = dim_invert; - using ratio = ratio; - using unit = downcast_unit; + using unit = downcast_unit; using common_rep = decltype(v / q.count()); using ret = quantity; return ret(v / q.count()); @@ -411,10 +407,7 @@ template; - using ratio1 = ratio_divide::ratio>; - using ratio2 = ratio_divide::ratio>; - using ratio = ratio_multiply, typename dimension_unit::ratio>; - using unit = downcast_unit; + using unit = downcast_unit::ratio) / (U2::ratio / dimension_unit::ratio) * dimension_unit::ratio>; using ret = quantity; return ret(lhs.count() / rhs.count()); } diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h index 47a7da39..8ee1bea9 100644 --- a/src/include/units/quantity_cast.h +++ b/src/include/units/quantity_cast.h @@ -69,10 +69,10 @@ concept QuantityOf = Quantity && Dimension && equivalent_dim +template struct quantity_cast_impl; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) @@ -81,235 +81,227 @@ struct quantity_cast_impl { } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * static_cast(fpow10(CRatio::exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(fpow10(CRatio.exp)))); } else { - if constexpr (CRatio::exp > 0) { - return To(static_cast(static_cast(q.count()) * static_cast(ipow10(CRatio::exp)))); + if constexpr (CRatio.exp > 0) { + return To(static_cast(static_cast(q.count()) * static_cast(ipow10(CRatio.exp)))); } else { - return To(static_cast(static_cast(q.count()) / static_cast(ipow10(-CRatio::exp)))); + return To(static_cast(static_cast(q.count()) / static_cast(ipow10(-CRatio.exp)))); } } } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(static_cast(q.count()) * - (static_cast(CRatio::num) / - static_cast(CRatio::den)))); + (static_cast(CRatio.num) / + static_cast(CRatio.den)))); } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { return To(static_cast(static_cast(q.count()) * - static_cast(fpow10(CRatio::exp)) * - (static_cast(CRatio::num) / - static_cast(CRatio::den)))); + static_cast(fpow10(CRatio.exp)) * + (static_cast(CRatio.num) / + static_cast(CRatio.den)))); } else { - if constexpr (CRatio::exp > 0) { + if constexpr (CRatio.exp > 0) { return To(static_cast(static_cast(q.count()) * - static_cast(CRatio::num) * - static_cast(ipow10(CRatio::exp)) / - static_cast(CRatio::den))); + static_cast(CRatio.num) * + static_cast(ipow10(CRatio.exp)) / + static_cast(CRatio.den))); } else { return To(static_cast(static_cast(q.count()) * - static_cast(CRatio::num) / - (static_cast(CRatio::den) * - static_cast(ipow10(-CRatio::exp))))); + static_cast(CRatio.num) / + (static_cast(CRatio.den) * + static_cast(ipow10(-CRatio.exp))))); } } } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { - return To(static_cast(static_cast(q.count()) / static_cast(CRatio::den))); + return To(static_cast(static_cast(q.count()) / static_cast(CRatio.den))); } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * static_cast(fpow10(CRatio::exp)) * (CRep{1} / static_cast(CRatio::den)))); + return To(static_cast(static_cast(q.count()) * static_cast(fpow10(CRatio.exp)) * (CRep{1} / static_cast(CRatio.den)))); } else { - if constexpr (CRatio::exp > 0) { - return To(static_cast(static_cast(q.count()) * static_cast(ipow10(CRatio::exp)) / static_cast(CRatio::den))); + if constexpr (CRatio.exp > 0) { + return To(static_cast(static_cast(q.count()) * static_cast(ipow10(CRatio.exp)) / static_cast(CRatio.den))); } else { - return To(static_cast(static_cast(q.count()) / (static_cast(ipow10(-CRatio::exp)) * static_cast(CRatio::den)))); + return To(static_cast(static_cast(q.count()) / (static_cast(ipow10(-CRatio.exp)) * static_cast(CRatio.den)))); } } } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num))); } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) * static_cast(fpow10(CRatio::exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(fpow10(CRatio.exp)))); } else { - if constexpr (CRatio::exp > 0) { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) * static_cast(ipow10(CRatio::exp)))); + if constexpr (CRatio.exp > 0) { + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(ipow10(CRatio.exp)))); } else { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) / static_cast(ipow10(-CRatio::exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) / static_cast(ipow10(-CRatio.exp)))); } } } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * fpow10(CRatio::exp))); + return To(static_cast(q.count() * fpow10(CRatio.exp))); } else { - if constexpr (CRatio::exp > 0) { - return To(static_cast(q.count() * ipow10(CRatio::exp))); + if constexpr (CRatio.exp > 0) { + return To(static_cast(q.count() * ipow10(CRatio.exp))); } else { - return To(static_cast(q.count() / ipow10(-CRatio::exp))); + return To(static_cast(q.count() / ipow10(-CRatio.exp))); } } } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { - return To(static_cast(q.count() * (CRatio::num / CRatio::den))); + return To(static_cast(q.count() * (CRatio.num / CRatio.den))); } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * fpow10(CRatio::exp) * (CRatio::num / CRatio::den))); + return To(static_cast(q.count() * fpow10(CRatio.exp) * (CRatio.num / CRatio.den))); } else { - if constexpr (CRatio::exp > 0) { - return To(static_cast(q.count() * CRatio::num * ipow10(CRatio::exp) / CRatio::den)); + if constexpr (CRatio.exp > 0) { + return To(static_cast(q.count() * CRatio.num * ipow10(CRatio.exp) / CRatio.den)); } else { - return To(static_cast(q.count()) * CRatio::num / (CRatio::den * ipow10(-CRatio::exp))); + return To(static_cast(q.count()) * CRatio.num / (CRatio.den * ipow10(-CRatio.exp))); } } } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { - return To(static_cast(q.count() / CRatio::den)); + return To(static_cast(q.count() / CRatio.den)); } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * fpow10(CRatio::exp) / CRatio::den)); + return To(static_cast(q.count() * fpow10(CRatio.exp) / CRatio.den)); } else { - if constexpr (CRatio::exp > 0) { - return To(static_cast(q.count() * ipow10(CRatio::exp) / CRatio::den)); + if constexpr (CRatio.exp > 0) { + return To(static_cast(q.count() * ipow10(CRatio.exp) / CRatio.den)); } else { - return To(static_cast(q.count() / (ipow10(-CRatio::exp) * CRatio::den))); + return To(static_cast(q.count() / (ipow10(-CRatio.exp) * CRatio.den))); } } } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { - return To(static_cast(q.count() * CRatio::num)); + return To(static_cast(q.count() * CRatio.num)); } }; -template +template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * CRatio::num * fpow10(CRatio::exp))); + return To(static_cast(q.count() * CRatio.num * fpow10(CRatio.exp))); } else { - if constexpr (CRatio::exp > 0) { - return To(static_cast(q.count() * CRatio::num * ipow10(CRatio::exp))); + if constexpr (CRatio.exp > 0) { + return To(static_cast(q.count() * CRatio.num * ipow10(CRatio.exp))); } else { - return To(static_cast(q.count() * CRatio::num / ipow10(-CRatio::exp))); + return To(static_cast(q.count() * CRatio.num / ipow10(-CRatio.exp))); } } } }; template -struct cast_ratio; - -template -struct cast_ratio { - using type = ratio_divide; -}; - -template - requires same_unit_reference::value -struct cast_ratio { - using type = ratio_divide; -}; - -template -struct cast_ratio { - using from_ratio = ratio_multiply; - using to_ratio = ratio_multiply; - using type = ratio_divide; -}; +constexpr ratio cast_ratio() +{ + if constexpr(BaseDimension || same_unit_reference::value) { + return FromU::ratio / ToU::ratio; + } + else { + const ratio from_ratio = FromD::base_units_ratio * FromU::ratio; + const ratio to_ratio = ToD::base_units_ratio * ToU::ratio; + return from_ratio / to_ratio; + } +} } // namespace detail @@ -329,11 +321,11 @@ template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) requires QuantityOf { - using c_ratio = detail::cast_ratio::type; + using c_ratio = std::integral_constant()>; using c_rep = std::common_type_t; - using ret_unit = downcast_unit; + using ret_unit = downcast_unit; using ret = quantity; - using cast = detail::quantity_cast_impl; + using cast = detail::quantity_cast_impl; return cast::cast(q); } diff --git a/src/include/units/ratio.h b/src/include/units/ratio.h index 9495828f..6775355b 100644 --- a/src/include/units/ratio.h +++ b/src/include/units/ratio.h @@ -23,7 +23,6 @@ #pragma once #include -#include #include #include #include @@ -33,192 +32,91 @@ namespace units { +struct ratio; +constexpr ratio inverse(const ratio& r); + /** * @brief Provides compile-time rational arithmetic support. * * This class is really similar to @c std::ratio but gets an additional `Exp` - * template parameter. - * - * @tparam Num Numerator - * @tparam Den Denominator (default @c 1) - * @tparam Exp Exponent (default @c 0) + * template parameter that defines the exponent of the ratio. Another important + * difference is the fact that the objects of that class are used as class NTTPs + * rather then a type template parameter kind. */ -template - requires(Den != 0) struct ratio { - static_assert(-INTMAX_MAX <= Num, "numerator too negative"); - static_assert(-INTMAX_MAX <= Den, "denominator too negative"); + std::intmax_t num; + std::intmax_t den; + std::intmax_t exp; - private: - static constexpr auto norm = detail::normalize(Num, Den, Exp); + explicit constexpr ratio(std::intmax_t n, std::intmax_t d = 1, std::intmax_t e = 0): num(n), den(d), exp(e) + { + detail::normalize(num, den, exp); + } - public: - static constexpr std::intmax_t num = norm[0]; - static constexpr std::intmax_t den = norm[1]; - static constexpr std::intmax_t exp = norm[2]; +#if __GNUC__ >= 10 - using type = ratio; + [[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default; - static constexpr bool is_integral() { - if constexpr (exp < 0) { - return false; - } else { - return detail::gcdpow(num, exp, den) == den; - } +#else + + [[nodiscard]] friend constexpr bool operator==(const ratio& lhs, const ratio& rhs) + { + return lhs.num == rhs.num && lhs.den == rhs.den && lhs.exp == rhs.exp; + } + + [[nodiscard]] friend constexpr bool operator!=(const ratio& lhs, const ratio& rhs) + { + return !(lhs == rhs); + } + +#endif + + [[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs) + { + const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den); + const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den); + return ratio(detail::safe_multiply(lhs.num / gcd1, rhs.num / gcd2), + detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1), + lhs.exp + rhs.exp); + } + + [[nodiscard]] friend constexpr ratio operator/(const ratio& lhs, const ratio& rhs) + { + return lhs * inverse(rhs); } }; -namespace detail { - -template -inline constexpr bool is_ratio> = true; - -// unused, and align exponents process could be subject to overflow in extreme cases - -// template -// constexpr auto ratio_add_detail() { -// std::intmax_t num1 = R1::num; -// std::intmax_t num2 = R2::num; - -// // align exponents -// std::intmax_t new_exp = R1::exp; -// if constexpr (R1::exp > R2::exp) { -// new_exp = R1::exp; -// while (new_exp > R2::exp) { -// num1 *= 10; -// --new_exp; -// } -// } else if constexpr (R1::exp < R2::exp) { -// new_exp = R2::exp; -// while (R1::exp < new_exp) { -// num2 *= 10; -// --new_exp; -// } -// } - -// // common denominator -// std::intmax_t lcm_den = std::lcm(R1::den, R2::den); -// num1 = num1 * (lcm_den / R1::den); -// num2 = num2 * (lcm_den / R2::den); - -// return std::array{num1 + num2, lcm_den, new_exp}; -// } - - -// template -// struct ratio_add_impl { -// static constexpr auto detail = ratio_add_detail(); -// using type = ratio; -// }; -}// namespace detail - - -// ratio_add : not used -// template -// using ratio_add = detail::ratio_add_impl::type; - -// ratio_subtract : not used -// TODO implement ratio_subtract -// template -// using ratio_subtract = detail::ratio_subtract_impl::type; - -// ratio_multiply - -namespace detail { - -static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs) +[[nodiscard]] constexpr ratio inverse(const ratio& r) { - constexpr std::intmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4); - - const std::intmax_t a0 = detail::abs(lhs) % c; - const std::intmax_t a1 = detail::abs(lhs) / c; - const std::intmax_t b0 = detail::abs(rhs) % c; - const std::intmax_t b1 = detail::abs(rhs) / c; - - Expects(a1 == 0 || b1 == 0); // overflow in multiplication - Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication - Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication - Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication - - return lhs * rhs; + return ratio(r.den, r.num, -r.exp); } -template -struct ratio_multiply_impl { -private: - static constexpr std::intmax_t gcd1 = std::gcd(R1::num, R2::den); - static constexpr std::intmax_t gcd2 = std::gcd(R2::num, R1::den); +[[nodiscard]] constexpr bool is_integral(const ratio& r) +{ + if(r.exp < 0) { + return false; + } else { + return detail::gcdpow(r.num, r.exp, r.den) == r.den; + } +} - static constexpr auto norm = detail::normalize(safe_multiply(R1::num / gcd1, R2::num / gcd2), - safe_multiply(R1::den / gcd2, R2::den / gcd1), - R1::exp + R2::exp); - - static constexpr std::intmax_t norm_num = norm[0]; - static constexpr std::intmax_t norm_den = norm[1]; - static constexpr std::intmax_t norm_exp = norm[2]; - -public: - using type = ratio; - static constexpr std::intmax_t num = type::num; - static constexpr std::intmax_t den = type::den; - static constexpr std::intmax_t exp = type::exp; -}; - -} // namespace detail - -template -using ratio_multiply = detail::ratio_multiply_impl::type; - -// ratio_divide - -namespace detail { - -template -struct ratio_divide_impl { - static_assert(R2::num != 0, "division by 0"); - using type = ratio_multiply>; - static constexpr std::intmax_t num = type::num; - static constexpr std::intmax_t den = type::den; - static constexpr std::intmax_t exp = type::exp; -}; - -} // namespace detail - -template -using ratio_divide = detail::ratio_divide_impl::type; - -// ratio_pow - -namespace detail { - -template -struct ratio_pow_impl { - using type = ratio_multiply::type, R>; -}; - -template -struct ratio_pow_impl { - using type = R; -}; - -template -struct ratio_pow_impl { - using type = ratio<1>; -}; - -} // namespace detail - -template -using ratio_pow = detail::ratio_pow_impl::type; - -// ratio_sqrt +template +[[nodiscard]] constexpr ratio pow(const ratio& r) +{ + if constexpr(N == 0) + return ratio(1); + else if constexpr(N == 1) + return r; + else + return pow(r) * r; +} namespace detail { // sqrt_impl avoids overflow and recursion // from http://www.codecodex.com/wiki/Calculate_an_integer_square_root#C.2B.2B // if v >= place this will fail (so we can't quite use the last bit) -static constexpr std::intmax_t sqrt_impl(std::intmax_t v) +[[nodiscard]] constexpr std::intmax_t sqrt_impl(std::intmax_t v) { // place = 0x4000 0000 for 32bit // place = 0x4000 0000 0000 0000 for 64bit @@ -237,52 +135,34 @@ static constexpr std::intmax_t sqrt_impl(std::intmax_t v) return root; } -template -constexpr auto make_exp_even() +[[nodiscard]] constexpr auto make_exp_even(const ratio& r) { - if constexpr (R::exp % 2 == 0) - return std::array{R::num, R::den, R::exp}; // already even (incl zero) + if(r.exp % 2 == 0) + return std::array{r.num, r.den, r.exp}; // already even (incl zero) // safely make exp even, so it can be divided by 2 for square root - if constexpr (R::exp > 0) - return std::array{R::num * 10, R::den, R::exp - 1}; + if(r.exp > 0) + return std::array{r.num * 10, r.den, r.exp - 1}; else - return std::array{R::num, R::den * 10, R::exp + 1}; + return std::array{r.num, r.den * 10, r.exp + 1}; } -template -struct ratio_sqrt_impl { - constexpr static auto even = detail::make_exp_even(); - using type = ratio; -}; - -template -struct ratio_sqrt_impl> { - using type = ratio<0>; -}; - } // namespace detail -template -using ratio_sqrt = detail::ratio_sqrt_impl::type; +[[nodiscard]] constexpr ratio sqrt(const ratio& r) +{ + if(r.num == 0) + return ratio(0); + + const auto even = detail::make_exp_even(r); + return ratio(detail::sqrt_impl(even[0]), detail::sqrt_impl(even[1]), even[2] / 2); +} // common_ratio - -namespace detail { - -// TODO: simplified -template -struct common_ratio_impl { - static constexpr auto res = gcd_frac(R1::num, R1::den, R1::exp, R2::num, R2::den, R2::exp); - static constexpr std::intmax_t num = res[0]; - static constexpr std::intmax_t den = res[1]; - static constexpr std::intmax_t exp = res[2]; - using type = ratio; -}; - -} // namespace detail - -template -using common_ratio = detail::common_ratio_impl::type; +[[nodiscard]] constexpr ratio common_ratio(const ratio& r1, const ratio& r2) +{ + const auto res = detail::gcd_frac(r1.num, r1.den, r1.exp, r2.num, r2.den, r2.exp); + return ratio(res[0], res[1], res[2]); +} } // namespace units diff --git a/src/include/units/unit.h b/src/include/units/unit.h index b3ddfa2c..32f04eac 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -47,16 +47,19 @@ namespace units { * (i.e. all length units are expressed in terms of meter, all mass units are expressed in * terms of gram, ...) * - * @tparam U a unit to use as a reference for this dimension * @tparam R a ratio of a reference unit + * @tparam U a unit to use as a reference for this dimension */ -template +template + requires UnitRatio struct scaled_unit : downcast_base> { - using ratio = R; + static constexpr ::units::ratio ratio = R; using reference = U; }; -template +template +// template // TODO: GCC crash!!! + requires UnitRatio using downcast_unit = downcast::reference>>; template @@ -71,7 +74,7 @@ struct same_unit_reference : std::is_same -struct unit : downcast_child, Child>> { +struct unit : downcast_child> { static constexpr bool is_named = false; using prefix_family = no_prefix; }; @@ -96,7 +99,7 @@ struct unknown_coherent_unit : unit {}; * @tparam PF no_prefix or a type of prefix family */ template -struct named_unit : downcast_child, Child>> { +struct named_unit : downcast_child> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_family = PF; @@ -116,8 +119,9 @@ struct named_unit : downcast_child, Child>> { * @tparam R a scale to apply to U * @tparam U a reference unit to scale */ -template -struct named_scaled_unit : downcast_child, typename U::reference>> { +template + requires UnitRatio +struct named_scaled_unit : downcast_child> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_family = PF; @@ -136,8 +140,7 @@ struct named_scaled_unit : downcast_child requires U::is_named && std::same_as -struct prefixed_unit : - downcast_child, typename U::reference>> { +struct prefixed_unit : downcast_child> { static constexpr bool is_named = true; static constexpr auto symbol = P::symbol + U::symbol; using prefix_family = no_prefix; diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index 976ee8ce..4a185fad 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -115,7 +115,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") { SECTION("in terms of base units") { - const length, metre>> q(123); + const length> q(123); os << q; SECTION("iostream") @@ -136,7 +136,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("in terms of derived units") { - const energy, joule>> q(60); + const energy> q(60); os << q; SECTION("iostream") diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index 3f98afc1..4b15807a 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -319,7 +319,7 @@ TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") SECTION("unknown scaled unit with reference different than the dimension's coherent unit") { - CHECK(fmt::format("{}", mass, gram>>(1)) == "1 [2/3 × 10⁻³] kg"); - CHECK(fmt::format("{:%Q %Aq}", mass, gram>>(1)) == "1 [2/3 x 10^-3] kg"); + CHECK(fmt::format("{}", mass>(1)) == "1 [2/3 × 10⁻³] kg"); + CHECK(fmt::format("{:%Q %Aq}", mass>(1)) == "1 [2/3 x 10^-3] kg"); } } diff --git a/test/unit_test/static/cgs_test.cpp b/test/unit_test/static/cgs_test.cpp index 28ba1453..8cdd0540 100644 --- a/test/unit_test/static/cgs_test.cpp +++ b/test/unit_test/static/cgs_test.cpp @@ -57,7 +57,7 @@ static_assert(10q_cm == 2q_cm_per_s * 5q_s); static_assert(detail::unit_text() == "cm/s"); // area -static_assert(std::is_same_v::ratio>, ratio<1>>); +static_assert(centimetre::ratio / dimension_unit::ratio == ratio(1)); static_assert(1q_cm * 1q_cm == 1q_cm2); static_assert(100q_cm2 / 10q_cm == 10q_cm); diff --git a/test/unit_test/static/fps_test.cpp b/test/unit_test/static/fps_test.cpp index e435f056..d1e575b7 100644 --- a/test/unit_test/static/fps_test.cpp +++ b/test/unit_test/static/fps_test.cpp @@ -57,7 +57,7 @@ static_assert(10q_ft == 2q_ft_per_s * 5q_s); static_assert(detail::unit_text() == "ft/s"); // area -static_assert(std::is_same_v::ratio>, ratio<1>>); +static_assert(foot::ratio / dimension_unit::ratio == ratio(1)); static_assert(1q_ft * 1q_ft == 1q_ft2); static_assert(100q_ft2 / 10q_ft == 10q_ft); diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 8873b7f6..4847627a 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -209,7 +209,7 @@ static_assert(std::equality_comparable_with, metre>>(quantity_point(2q_km)))::unit, metre>); + std::is_same_v>(quantity_point(2q_km)))::unit, metre>); static_assert(quantity_point_cast>(quantity_point(2q_km)).relative().count() == 2000); diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 1c0e4cb4..3ebb77b1 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -39,7 +39,7 @@ using namespace units::physical::si; // constexpr quantity error(0); // should not compile (unit of a different dimension) // constexpr quantity> error(0); // should not compile (quantity used as Rep) // constexpr quantity error(0); // should not compile (reordered arguments) -// constexpr quantity, metre>, int> error(0); // should not compile (negative unit ratio) +// constexpr quantity, int> error(0); // should not compile (negative unit ratio) // member types @@ -138,23 +138,23 @@ static_assert(std::is_same_v()), length() * physical::si::time()), length>); static_assert( - std::is_same_v() * physical::si::time()), length, metre>, int>>); + std::is_same_v() * physical::si::time()), length, int>>); static_assert(std::is_same_v() * physical::si::time()), - quantity, units::exp>, scaled_unit, unknown_coherent_unit>>>); + quantity, units::exp>, scaled_unit>>); static_assert(std::is_same_v()), frequency>); -static_assert(std::is_same_v()), frequency, hertz>, int>>); +static_assert(std::is_same_v()), frequency, int>>); static_assert(std::is_same_v()), physical::si::time>); static_assert(std::is_same_v()), - quantity>, scaled_unit, unknown_coherent_unit>>>); + quantity>, scaled_unit>>); static_assert(std::is_same_v() / 1.0), length>); static_assert(std::is_same_v() / length()), double>); static_assert(std::is_same_v() / length()), double>); static_assert( std::is_same_v() / physical::si::time()), speed>); static_assert( - std::is_same_v() / physical::si::time()), speed, metre_per_second>>>); + std::is_same_v() / physical::si::time()), speed>>); static_assert(std::is_same_v() / length()), - quantity, units::exp>, scaled_unit, unknown_coherent_unit>>>); + quantity, units::exp>, scaled_unit>>); static_assert(std::is_same_v() % short(1)), length>); static_assert(std::is_same_v() % length(1)), length>); @@ -234,7 +234,7 @@ static_assert(std::equality_comparable_with) // quantity_cast -static_assert(std::is_same_v, metre>>(2q_km))::unit, metre>); +static_assert(std::is_same_v>(2q_km))::unit, metre>); static_assert(quantity_cast>(2q_km).count() == 2000); static_assert(quantity_cast>(2000q_m).count() == 2); diff --git a/test/unit_test/static/ratio_test.cpp b/test/unit_test/static/ratio_test.cpp index 7e68a2fe..2c05828b 100644 --- a/test/unit_test/static/ratio_test.cpp +++ b/test/unit_test/static/ratio_test.cpp @@ -1,110 +1,92 @@ - // 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. +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. - #include "units/ratio.h" +#include "units/ratio.h" - namespace { +namespace { - using namespace units; +using namespace units; - template - inline constexpr bool same = R1::num == R2::num && R1::den == R2::den && R1::exp == R2::exp; +static_assert(ratio(2, 4) == ratio(1, 2)); - static_assert(same, ratio<1, 2>>); +// basic exponents tests +// note use of ::type is required because template params are changed while stamping out template +static_assert(ratio(2, 40, 1) == ratio(1, 20, 1)); +static_assert(ratio(20, 4, -1) == ratio(10, 2, -1)); +static_assert(ratio(200, 5) == ratio(20'000, 50, -1)); - // basic exponents tests - // note use of ::type is required because template params are changed while stamping out template - static_assert(std::is_same_v::type, ratio<1, 20, 1>::type>); - static_assert(std::is_same_v::type, ratio<10, 2, -1>::type>); - static_assert(std::is_same_v::type, ratio<20'000, 50, -1>::type>); +static_assert(ratio(1) * ratio(3, 8) == ratio(3, 8)); +static_assert(ratio(3, 8) * ratio(1) == ratio(3, 8)); +static_assert(ratio(4) * ratio(1, 8) == ratio(1, 2)); +static_assert(ratio(4) * ratio(1, 2) == ratio(2)); +static_assert(ratio(1, 8) * ratio(2) == ratio(1, 4)); +static_assert(ratio(1, 2) * ratio(8) == ratio(4)); - static_assert(std::is_same_v, ratio<3, 8>>, ratio<3, 8>>); - static_assert(std::is_same_v, ratio<1>>, ratio<3, 8>>); - static_assert(std::is_same_v, ratio<1, 8>>, ratio<1, 2>>); - static_assert(std::is_same_v, ratio<1, 2>>, ratio<2>>); - static_assert(std::is_same_v, ratio<2>>, ratio<1, 4>>); - static_assert(std::is_same_v, ratio<8>>, ratio<4>>); +// multiply with exponents +static_assert(ratio(1, 8, 2) * ratio(2, 1, 4) == ratio(1, 4, 6)); +static_assert(ratio(1, 2, -4) * ratio(8, 1, 3) == ratio(4, 1, -1)); - // multiply with exponents - static_assert(std::is_same_v, ratio<2, 1, 4>>, ratio<1, 4, 6>>); - static_assert(std::is_same_v, ratio<8, 1, 3>>, ratio<4, 1, -1>>); +static_assert(ratio(4) / ratio(2) == ratio(2)); +static_assert(ratio(2) / ratio(8) == ratio(1, 4)); +static_assert(ratio(1, 8) / ratio(2) == ratio(1, 16)); +static_assert(ratio(6) / ratio(3) == ratio(2)); - static_assert(std::is_same_v, ratio<2>>, ratio<2>>); - static_assert(std::is_same_v, ratio<8>>, ratio<1, 4>>); - static_assert(std::is_same_v, ratio<2>>, ratio<1, 16>>); - static_assert(std::is_same_v, ratio<3>>, ratio<2>>); +// divide with exponents +static_assert(ratio(1, 8, -6) / ratio(2, 1, -8) == ratio(1, 16, 2)); +static_assert(ratio(6, 1, 4) / ratio(3) == ratio(2, 1, 4)); - // divide with exponents - static_assert(std::is_same_v, ratio<2, 1, -8>>, ratio<1, 16, 2>>); - static_assert(std::is_same_v, ratio<3>>, ratio<2, 1, 4>>); +static_assert(pow<0>(ratio(2)) == ratio(1)); +static_assert(pow<1>(ratio(2)) == ratio(2)); +static_assert(pow<2>(ratio(2)) == ratio(4)); +static_assert(pow<3>(ratio(2)) == ratio(8)); +static_assert(pow<0>(ratio(1, 2)) == ratio(1)); +static_assert(pow<1>(ratio(1, 2)) == ratio(1, 2)); +static_assert(pow<2>(ratio(1, 2)) == ratio(1, 4)); +static_assert(pow<3>(ratio(1, 2)) == ratio(1, 8)); - static_assert(std::is_same_v, 0>, ratio<1>>); - static_assert(std::is_same_v, 1>, ratio<2>>); - static_assert(std::is_same_v, 2>, ratio<4>>); - static_assert(std::is_same_v, 3>, ratio<8>>); - static_assert(std::is_same_v, 0>, ratio<1>>); - static_assert(std::is_same_v, 1>, ratio<1, 2>>); - static_assert(std::is_same_v, 2>, ratio<1, 4>>); - static_assert(std::is_same_v, 3>, ratio<1, 8>>); +// pow with exponents +static_assert(pow<2>(ratio(1, 2, 3)) == ratio(1, 4, 6)); +static_assert(pow<3>(ratio(1, 2, -6)) == ratio(1, 8, -18)); - // pow with exponents - static_assert(std::is_same_v, 2>, ratio<1, 4, 6>>); - static_assert(std::is_same_v, 3>, ratio<1, 8, -18>>); +static_assert(sqrt(ratio(9)) == ratio(3)); +static_assert(sqrt(ratio(4)) == ratio(2)); +static_assert(sqrt(ratio(1)) == ratio(1)); +static_assert(sqrt(ratio(0)) == ratio(0)); +static_assert(sqrt(ratio(1, 4)) == ratio(1, 2)); - static_assert(std::is_same_v>, ratio<3>>); - static_assert(std::is_same_v>, ratio<2>>); - static_assert(std::is_same_v>, ratio<1>>); - static_assert(std::is_same_v>, ratio<0>>); - static_assert(std::is_same_v>, ratio<1, 2>>); +// sqrt with exponents +static_assert(sqrt(ratio(9, 1, 2)) == ratio(3, 1, 1)); +static_assert(sqrt(ratio(4)) == ratio(2)); - // sqrt with exponents - static_assert(std::is_same_v>, ratio<3, 1, 1>>); - static_assert(std::is_same_v>, ratio<2>>); +// common_ratio +static_assert(common_ratio(ratio(1), ratio(1000)) == ratio(1)); +static_assert(common_ratio(ratio(1000), ratio(1)) == ratio(1)); +static_assert(common_ratio(ratio(1), ratio(1, 1000)) == ratio(1, 1000)); +static_assert(common_ratio(ratio(1, 1000), ratio(1)) == ratio(1, 1000)); +static_assert(common_ratio(ratio(100, 1), ratio(10, 1)) == ratio(10, 1)); +static_assert(common_ratio(ratio(100, 1), ratio(1, 10)) == ratio(1, 10)); - // unused - // static_assert(std::is_same_v, units::ratio<1, 3, 0>>, units::ratio<5, 6, 0>>); - // static_assert(std::is_same_v, units::ratio<1, 3, 1>>, units::ratio<5, 6, 1>>); - // static_assert(std::is_same_v, units::ratio<2, 7, 2>>, units::ratio<37, 56, 2>>); +// common ratio with exponents +static_assert(common_ratio(ratio(1), ratio(1, 1, 3)) == ratio(1)); +static_assert(common_ratio(ratio(10, 1, -1), ratio(1, 1, -3)) == ratio(1, 1, -3)); - // static_assert(std::is_same_v, units::ratio<2, 7, 1>>, units::ratio<226, 56, 1>>); - // static_assert(std::is_same_v, units::ratio<3, 8, 2>>, units::ratio<226, 56, 1>>); - - // static_assert(std::is_same_v, units::ratio<2, 7, -1>>, units::ratio<181, 56, -2>>); - // static_assert(std::is_same_v, units::ratio<3, 8, -2>>, units::ratio<181, 56, -2>>); - - - // common_ratio - // note use of ::type is required because template params are changed while stamping out template - static_assert(std::is_same_v::type, ratio<1000>>, ratio<1>::type>); - static_assert(std::is_same_v, ratio<1>>::type, ratio<1>::type>); - static_assert(std::is_same_v, ratio<1, 1000>>::type, ratio<1, 1000>::type>); - static_assert(std::is_same_v, ratio<1>>::type, ratio<1, 1000>::type>); - static_assert(std::is_same_v, ratio<10, 1>>::type, ratio<10, 1>::type>); - static_assert(std::is_same_v, ratio<1, 10>>::type, ratio<1, 10>::type>); - - // common ratio with exponents - static_assert(std::is_same_v, ratio<1, 1, 3>>::type, ratio<1>::type>); - static_assert(std::is_same_v, ratio<1, 1, -3>>::type, ratio<1, 1, -3>::type>); - - - - } // namespace +} // namespace diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index 65ce7e3b..05f13ea2 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -237,7 +237,7 @@ static_assert(kilogray::symbol == "kGy"); // speed -static_assert(std::is_same_v, metre_per_second>, std::int64_t>>); +static_assert(std::is_same_v, std::int64_t>>); static_assert(10q_m / 5q_s == 2q_m_per_s); static_assert(10 / 5q_s * 1q_m == 2q_m_per_s); diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 3b641f5e..089283b5 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -31,12 +31,12 @@ using namespace units::physical; struct metre : named_unit {}; struct centimetre : prefixed_unit {}; struct kilometre : prefixed_unit {}; -struct yard : named_scaled_unit, metre> {}; -struct foot : named_scaled_unit, yard> {}; +struct yard : named_scaled_unit {}; +struct foot : named_scaled_unit {}; struct dim_length : base_dimension<"length", metre> {}; struct second : named_unit {}; -struct hour : named_scaled_unit, second> {}; +struct hour : named_scaled_unit {}; struct dim_time : base_dimension<"time", second> {}; struct kelvin : named_unit {}; @@ -46,11 +46,11 @@ struct metre_per_second : unit {}; struct dim_speed : derived_dimension, units::exp> {}; struct kilometre_per_hour : deduced_unit {}; -static_assert(std::is_same_v, metre>>, metre>); -static_assert(std::is_same_v, metre>>, centimetre>); -static_assert(std::is_same_v, metre>>, yard>); -static_assert(std::is_same_v>, metre>>, foot>); -static_assert(std::is_same_v, metre_per_second>>, kilometre_per_hour>); +static_assert(std::is_same_v>, metre>); +static_assert(std::is_same_v>, centimetre>); +static_assert(std::is_same_v>, yard>); +static_assert(std::is_same_v>, foot>); +static_assert(std::is_same_v>, kilometre_per_hour>); static_assert(centimetre::symbol == "cm"); static_assert(kilometre::symbol == "km");