diff --git a/src/include/units/bits/ratio_tools.h b/src/include/units/bits/ratio_tools.h deleted file mode 100644 index 4c906e7c..00000000 --- a/src/include/units/bits/ratio_tools.h +++ /dev/null @@ -1,87 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include - -namespace units { - - // static_sign - - template - struct static_sign : std::integral_constant { - }; - - // static_abs - - template - struct static_abs : std::integral_constant::value> { - }; - - // static_gcd - - template - struct static_gcd : static_gcd { - }; - - template - struct static_gcd : std::integral_constant::value> { - }; - - template - struct static_gcd<0, Qn> : std::integral_constant::value> { - }; - - // is_ratio - - namespace detail { - - template - inline constexpr bool is_ratio = false; - - template - inline constexpr bool is_ratio> = true; - - } // namespace detail - - template - concept bool Ratio = detail::is_ratio; - - // common_ratio - namespace detail { - - // todo: simplified - template - struct common_ratio_impl { - using gcd_num = static_gcd; - using gcd_den = static_gcd; - using type = std::ratio; - }; - - } - - template - using common_ratio = detail::common_ratio_impl::type; - -} // namespace units diff --git a/src/include/units/length.h b/src/include/units/length.h index c9c90a2a..22fb7a4f 100644 --- a/src/include/units/length.h +++ b/src/include/units/length.h @@ -70,16 +70,16 @@ namespace units { } // namespace literals // US customary units - struct yard : unit> {}; + struct yard : unit> {}; template<> struct upcasting_traits> : upcast_to {}; - struct foot : unit, yard::ratio>> {}; + struct foot : unit, yard::ratio>> {}; template<> struct upcasting_traits> : upcast_to {}; - struct inch : unit, foot::ratio>> {}; + struct inch : unit, foot::ratio>> {}; template<> struct upcasting_traits> : upcast_to {}; - struct mile : unit, yard::ratio>> {}; + struct mile : unit, yard::ratio>> {}; template<> struct upcasting_traits> : upcast_to {}; inline namespace literals { diff --git a/src/include/units/mass.h b/src/include/units/mass.h index 1e8fe8e7..3f458d3c 100644 --- a/src/include/units/mass.h +++ b/src/include/units/mass.h @@ -36,7 +36,7 @@ namespace units { template using mass = quantity; - struct gram : unit {}; + struct gram : unit> {}; template<> struct upcasting_traits> : upcast_to {}; struct kilogram : kilo {}; diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index e91d65dd..06b73fa5 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -125,7 +125,7 @@ namespace units { requires std::Same constexpr To quantity_cast(const quantity& q) { - using c_ratio = std::ratio_divide; + using c_ratio = ratio_divide; using c_rep = std::common_type_t; using cast = detail::quantity_cast_impl; return cast::cast(q); @@ -284,7 +284,7 @@ namespace units { { using dim = dimension_multiply_t; using common_rep = decltype(lhs.count() * rhs.count()); - using ret = quantity>>, common_rep>; + using ret = quantity>>, common_rep>; return ret(lhs.count() * rhs.count()); } @@ -298,7 +298,7 @@ namespace units { using dim = dim_invert_t; using common_rep = decltype(v / q.count()); - using ret = quantity>>, common_rep>; + using ret = quantity>>, common_rep>; using den = quantity; return ret(v / den(q).count()); } @@ -331,13 +331,13 @@ namespace units { [[nodiscard]] constexpr Quantity operator/(const quantity& lhs, const quantity& rhs) requires treat_as_floating_point || - (std::ratio_divide::den == 1) + (ratio_divide::den == 1) { Expects(rhs != std::remove_cvref_t(0)); using common_rep = decltype(lhs.count() / rhs.count()); using dim = dimension_divide_t; - using ret = quantity>>, common_rep>; + using ret = quantity>>, common_rep>; return ret(lhs.count() / rhs.count()); } diff --git a/src/include/units/ratio.h b/src/include/units/ratio.h new file mode 100644 index 00000000..eca4333d --- /dev/null +++ b/src/include/units/ratio.h @@ -0,0 +1,138 @@ +// 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 +#include + +namespace units { + + namespace detail { + + template + [[nodiscard]] constexpr T abs(T v) noexcept { return v < 0 ? -v : v; } + + } + + template + struct ratio { + static_assert(Den != 0, "zero denominator"); + static_assert(-INTMAX_MAX <= Num, "numerator too negative"); + static_assert(-INTMAX_MAX <= Den, "denominator too negative"); + + static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den); + static constexpr std::intmax_t den = detail::abs(Den) / std::gcd(Num, Den); + + using type = ratio; + }; + + // is_ratio + + namespace detail { + + template + inline constexpr bool is_ratio = false; + + template + inline constexpr bool is_ratio> = true; + + } // namespace detail + + template + concept bool Ratio = detail::is_ratio; + + // ratio_multiply + + namespace detail { + + static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs) + { + constexpr std::uintmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4); + + const std::uintmax_t a0 = detail::abs(lhs) % c; + const std::uintmax_t a1 = detail::abs(lhs) / c; + const std::uintmax_t b0 = detail::abs(rhs) % c; + const std::uintmax_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; + } + + 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); + + public: + using type = ratio; + static constexpr std::intmax_t num = type::num; + static constexpr std::intmax_t den = type::den; + }; + + } + + template + using ratio_multiply = typename 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; + }; + + } + + template + using ratio_divide = typename detail::ratio_divide_impl::type; + + // common_ratio + + namespace detail { + + // todo: simplified + template + struct common_ratio_impl { + static constexpr std::intmax_t gcd_num = std::gcd(R1::num, R2::num); + static constexpr std::intmax_t gcd_den = std::gcd(R1::den, R2::den); + using type = ratio; + }; + + } + + template + using common_ratio = typename detail::common_ratio_impl::type; + +} // namespace units diff --git a/src/include/units/time.h b/src/include/units/time.h index d1bec3d3..c62eca42 100644 --- a/src/include/units/time.h +++ b/src/include/units/time.h @@ -48,10 +48,10 @@ namespace units { struct millisecond : milli {}; template<> struct upcasting_traits> : upcast_to {}; - struct minute : unit> {}; + struct minute : unit> {}; template<> struct upcasting_traits> : upcast_to {}; - struct hour : unit> {}; + struct hour : unit> {}; template<> struct upcasting_traits> : upcast_to {}; inline namespace literals { diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 1dfbed93..bed885d2 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -23,11 +23,12 @@ #pragma once #include -#include +#include +#include namespace units { - template> + template> requires (R::num > 0) struct unit : upcast_base> { using dimension = D; @@ -67,7 +68,7 @@ namespace units { template struct get_ratio { - using ratio = std::ratio<1>; + using ratio = ::units::ratio<1>; }; template @@ -87,8 +88,8 @@ namespace units { template struct ratio_op { - using calc_ratio = std::conditional_t<(UnitExpValue > 0), std::ratio_multiply, - std::ratio_divide>; + using calc_ratio = std::conditional_t<(UnitExpValue > 0), ratio_multiply, + ratio_divide>; static constexpr int value = UnitExpValue > 0 ? UnitExpValue - 1 : UnitExpValue + 1; using ratio = ratio_op::ratio; }; @@ -98,7 +99,7 @@ namespace units { template struct derived_ratio, Us...> { - using ratio = std::ratio<1>; + using ratio = ::units::ratio<1>; }; template @@ -114,20 +115,20 @@ namespace units { using derived_unit = unit::ratio>; // prefixes - template using atto = unit>; - template using femto = unit>; - template using pico = unit>; - template using nano = unit>; - template using micro = unit>; - template using milli = unit>; - template using centi = unit>; - template using deca = unit>; - template using hecto = unit>; - template using kilo = unit>; - template using mega = unit>; - template using giga = unit>; - template using tera = unit>; - template using peta = unit>; - template using exa = unit>; + template using atto = unit>>; + template using femto = unit>>; + template using pico = unit>>; + template using nano = unit>>; + template using micro = unit>>; + template using milli = unit>>; + template using centi = unit>>; + template using deca = unit>>; + template using hecto = unit>>; + template using kilo = unit>>; + template using mega = unit>>; + template using giga = unit>>; + template using tera = unit>>; + template using peta = unit>>; + template using exa = unit>>; } // namespace units diff --git a/test/metabench/ratio/ratio_type_constexpr.h b/test/metabench/ratio/ratio_type_constexpr.h index 14826324..90b6cbf7 100644 --- a/test/metabench/ratio/ratio_type_constexpr.h +++ b/test/metabench/ratio/ratio_type_constexpr.h @@ -48,21 +48,6 @@ namespace units { using type = ratio; }; - // is_ratio - - namespace detail { - - template - inline constexpr bool is_ratio = false; - - template - inline constexpr bool is_ratio> = true; - - } // namespace detail - - template - concept bool Ratio = detail::is_ratio; - // ratio_multiply namespace detail { @@ -120,15 +105,19 @@ namespace units { // common_ratio - // todo: simplified - template - struct common_ratio { - static constexpr std::intmax_t gcd_num = std::gcd(R1::num, R2::num); - static constexpr std::intmax_t gcd_den = std::gcd(R1::den, R2::den); - using type = ratio; - }; + namespace detail { - template - using common_ratio_t = typename common_ratio::type; + // todo: simplified + template + struct common_ratio_impl { + static constexpr std::intmax_t gcd_num = std::gcd(R1::num, R2::num); + static constexpr std::intmax_t gcd_den = std::gcd(R1::den, R2::den); + using type = ratio; + }; + + } + + template + using common_ratio_t = typename detail::common_ratio_impl::type; } // namespace units diff --git a/test/metabench/ratio/std_ratio.h b/test/metabench/ratio/std_ratio.h index c992e35d..45422fe9 100644 --- a/test/metabench/ratio/std_ratio.h +++ b/test/metabench/ratio/std_ratio.h @@ -53,32 +53,17 @@ namespace units { struct static_gcd<0, Qn> : std::integral_constant::value> { }; - // is_ratio - - namespace detail { - - template - inline constexpr bool is_ratio = false; - - template - inline constexpr bool is_ratio> = true; - - } // namespace detail - - template - concept bool Ratio = detail::is_ratio; - // common_ratio // todo: simplified - template + template struct common_ratio { using gcd_num = static_gcd; using gcd_den = static_gcd; using type = std::ratio; }; - template + template using common_ratio_t = typename common_ratio::type; } // namespace units diff --git a/test/unit_test/test_tools.cpp b/test/unit_test/test_tools.cpp index 1f3fec6c..ee887fba 100644 --- a/test/unit_test/test_tools.cpp +++ b/test/unit_test/test_tools.cpp @@ -20,29 +20,32 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/bits/ratio_tools.h" +#include "units/ratio.h" namespace { using namespace units; - - // static_sign - static_assert(static_sign<2>::value == 1); - static_assert(static_sign<-3>::value == -1); - static_assert(static_sign<0>::value == 1); + template + inline constexpr bool same = R1::num == R2::num && R1::den == R2::den; - // static_abs + static_assert(same, ratio<1, 2>>); - static_assert(static_abs<2>::value == 2); - static_assert(static_abs<-3>::value == 3); - static_assert(static_abs<0>::value == 0); + 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>>); + + 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>>); // common_ratio - static_assert(std::is_same_v, std::kilo>, std::ratio<1>>); - static_assert(std::is_same_v>, std::ratio<1>>); - static_assert(std::is_same_v, std::milli>, std::milli>); - static_assert(std::is_same_v>, std::milli>); + static_assert(std::is_same_v, ratio<1000>>, ratio<1>>); + static_assert(std::is_same_v, ratio<1>>, ratio<1>>); + static_assert(std::is_same_v, ratio<1, 1000>>, ratio<1, 1000>>); + static_assert(std::is_same_v, ratio<1>>, ratio<1, 1000>>); } // namespace \ No newline at end of file diff --git a/test/unit_test/test_units.cpp b/test/unit_test/test_units.cpp index 12b0b91d..cbb679ff 100644 --- a/test/unit_test/test_units.cpp +++ b/test/unit_test/test_units.cpp @@ -74,7 +74,7 @@ namespace { // velocity - static_assert(std::is_same_v>, std::int64_t>>); + static_assert(std::is_same_v>, std::int64_t>>); static_assert(10_m / 5_s == 2_mps); static_assert(10 / 5_s * 1_m == 2_mps);