diff --git a/include/zll/numeric/ratio.hpp b/include/zll/numeric/ratio.hpp new file mode 100644 index 0000000..19a59bf --- /dev/null +++ b/include/zll/numeric/ratio.hpp @@ -0,0 +1,38 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef ZLL_NUMERIC_RATIO_HPP +#define ZLL_NUMERIC_RATIO_HPP + +#include "zll/numeric/ratio/ratio_type.hpp" +#include "zll/numeric/ratio/arithmetic.hpp" +#include "zll/numeric/ratio/comparsion.hpp" + + +namespace zll { + +// clang-format off +typedef ratio<1L, 1000000000000000000L> atto; +typedef ratio<1L, 1000000000000000L> femto; +typedef ratio<1L, 1000000000000L> pico; +typedef ratio<1L, 1000000000L> nano; +typedef ratio<1L, 1000000L> micro; +typedef ratio<1L, 1000L> milli; +typedef ratio<1L, 100L> centi; +typedef ratio<1L, 10L> deci; +typedef ratio< 10L, 1L> deca; +typedef ratio< 100L, 1L> hecto; +typedef ratio< 1000L, 1L> kilo; +typedef ratio< 1000000L, 1L> mega; +typedef ratio< 1000000000L, 1L> giga; +typedef ratio< 1000000000000L, 1L> tera; +typedef ratio< 1000000000000000L, 1L> peta; +typedef ratio<1000000000000000000L, 1L> exa; +// clang-format on + +} // namespace zll + +#endif // ZLL_NUMERIC_RATIO_HPP diff --git a/include/zll/numeric/ratio/arithmetic.hpp b/include/zll/numeric/ratio/arithmetic.hpp new file mode 100644 index 0000000..ae96a42 --- /dev/null +++ b/include/zll/numeric/ratio/arithmetic.hpp @@ -0,0 +1,105 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef ZLL_NUMERIC_RATIO_ARITHMETIC_HPP +#define ZLL_NUMERIC_RATIO_ARITHMETIC_HPP + +#include "zll/numeric/ratio/ratio_type.hpp" +#include "zll/numeric/ratio/helper.hpp" +#include "zll/numeric/ratio/overflow_helper.hpp" + +#include "zll/meta/enable_if.hpp" +#include "zll/meta/integral_constant.hpp" + +#include "zll/cstdint.hpp" + +namespace zll { +namespace detail { +template +struct ratio_add_helper { + static const zll::intmax_t gcd = ratio_gcd_helper::value; + + static const zll::intmax_t num = safe_addition::value, + safe_multiply::value>::value; + static const zll::intmax_t den = safe_multiply::value; + + typedef ratio ratio_type; + typedef typename ratio_type::type type; +}; + +} // namespace detail + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_add { + typedef typename detail::ratio_add_helper::type type; +}; +// clang-format on + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_subtract { + typedef typename detail::ratio_add_helper< LHS_T, ratio<-RHS_T::num, RHS_T::den> >::type type; +}; +// clang-format on + +namespace detail { + +template +struct ratio_multiply_helper { + static const zll::intmax_t gcd_x = ratio_gcd_helper::value; + static const zll::intmax_t gcd_y = ratio_gcd_helper::value; + + static const zll::intmax_t num = safe_multiply::value; + static const zll::intmax_t den = safe_multiply::value; + + typedef ratio ratio_type; + typedef typename ratio_type::type type; +}; + +} // namespace detail + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_multiply { + typedef typename detail::ratio_multiply_helper::type type; +}; +// clang-format on +namespace detail { + +template +struct ratio_divide_helper { + static const zll::intmax_t gcd_num = ratio_gcd_helper::value; + static const zll::intmax_t gcd_den = ratio_gcd_helper::value; + + static const zll::intmax_t num = safe_multiply::value; + static const zll::intmax_t den = safe_multiply::value; + + typedef ratio ratio_type; + typedef typename ratio_type::type type; +}; + +} // namespace detail + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_divide { + typedef typename detail::ratio_divide_helper::type type; +}; +// clang-format on + + +} // namespace zll + +#endif // ZLL_NUMERIC_RATIO_ARITHMETIC_HPP diff --git a/include/zll/numeric/ratio/comparsion.hpp b/include/zll/numeric/ratio/comparsion.hpp new file mode 100644 index 0000000..a6bba68 --- /dev/null +++ b/include/zll/numeric/ratio/comparsion.hpp @@ -0,0 +1,94 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef ZLl_NUMERIC_RATIO_COMPARSION_HPP +#define ZLl_NUMERIC_RATIO_COMPARSION_HPP + +#include "zll/numeric/ratio/ratio_type.hpp" +#include "zll/numeric/ratio/helper.hpp" +#include "zll/numeric/ratio/overflow_helper.hpp" + +#include "zll/meta/enable_if.hpp" +#include "zll/meta/integral_constant.hpp" + +namespace zll { + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_equal : meta::bool_constant<((LHS_T::num == RHS_T::num) && (LHS_T::den == RHS_T::den))> {}; +// clang-format on + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_not_equal : meta::bool_constant< !(ratio_equal::value) > {}; +// clang-format on + +namespace detail { +// clang-format off +template +struct ratio_less_helper_impl { + static const zll::intmax_t lhs = detail::safe_multiply::value; + static const zll::intmax_t rhs = detail::safe_multiply::value; + static const bool value = lhs < rhs; +}; +// clang-format on + +// clang-format off +template ::value, + zll::intmax_t RHS_SIGN_V = ratio_sign_helper::value> +struct ratio_less_helper : meta::bool_constant<(LHS_SIGN_V < RHS_SIGN_V)> {}; +// clang-format on + +// clang-format off +template +struct ratio_less_helper + : meta::bool_constant::value> {}; +// clang-format on + +// clang-format off +template +struct ratio_less_helper + : meta::bool_constant, + ratio<-RHS_T::num, RHS_T::den> >::value> {}; +// clang-format on +} // namespace detail + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_less : detail::ratio_less_helper {}; +// clang-format on + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_greater : detail::ratio_less_helper {}; +// clang-format on + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_less_equal : meta::bool_constant::value)> {}; +// clang-format on + +// clang-format off +template ::value, bool>::type = true, + typename meta::enable_if::value, bool>::type = true> +struct ratio_greater_equal : meta::bool_constant::value)> {}; +// clang-format on + +} // namespace zll + +#endif // ZLl_NUMERIC_RATIO_COMPARSION_HPP diff --git a/include/zll/numeric/ratio/helper.hpp b/include/zll/numeric/ratio/helper.hpp new file mode 100644 index 0000000..be3feef --- /dev/null +++ b/include/zll/numeric/ratio/helper.hpp @@ -0,0 +1,49 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef ZLL_NUMERIC_RATIO_HELPER_HPP +#define ZLL_NUMERIC_RATIO_HELPER_HPP + +#include "zll/meta/integral_constant.hpp" + +#include "zll/cstdint.hpp" + +namespace zll { +namespace detail { + +template +struct ratio_gcd_helper_impl { + static const zll::intmax_t value = ratio_gcd_helper_impl::value; +}; + +template +struct ratio_gcd_helper_impl { + static const zll::intmax_t value = V; +}; + +template <> +struct ratio_gcd_helper_impl<0, 0> { + static const zll::intmax_t value = 1; +}; + +template +struct ratio_sign_helper_impl { + static const zll::intmax_t value = (V == 0) ? 0 : ((V < 0) ? -1 : 1); +}; + +template +struct ratio_gcd_helper : meta::integral_constant::value> {}; + +template +struct ratio_abs_helper : meta::integral_constant {}; + +template +struct ratio_sign_helper : meta::integral_constant::value> {}; + +} // namespace detail +} // namespace zll + +#endif // ZLL_NUMERIC_RATIO_HELPER_HPP diff --git a/include/zll/numeric/ratio/overflow_helper.hpp b/include/zll/numeric/ratio/overflow_helper.hpp new file mode 100644 index 0000000..ffce052 --- /dev/null +++ b/include/zll/numeric/ratio/overflow_helper.hpp @@ -0,0 +1,76 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef ZLL_NUMER_RATIO_OVERFLOWHELPER_HPP +#define ZLL_NUMER_RATIO_OVERFLOWHELPER_HPP + +#include "zll/debug/static_assert.hpp" +#include "zll/meta/integral_constant.hpp" +#include "zll/numeric/ratio/helper.hpp" + +#include "zll/cstdint.hpp" + +namespace zll { +namespace detail { + +template +struct safe_multiply_helper { + static const zll::intmax_t lhs_abs = ratio_abs_helper::value; + static const zll::intmax_t rhs_abs = ratio_abs_helper::value; + + static const bool value = lhs_abs <= (INTMAX_MAX / rhs_abs); +}; + +template +struct safe_multiply_helper { + static const bool value = true; +}; + +// clang-format off +template ::value> +struct safe_multiply : meta::integral_constant {}; +// clang-format on + +template +struct safe_multiply { + static const bool no_overflow = safe_multiply_helper::value; + ZLL_STATIC_ASSERT(no_overflow); +}; + +// clang-format off +template ::value, + zll::intmax_t RHS_SIGN_V = ratio_sign_helper::value> +struct safe_addition_helper { + static const bool value = true; +}; +// clang-format on + +template +struct safe_addition_helper { + static const zll::intmax_t lhs_abs = ratio_abs_helper::value; + static const zll::intmax_t rhs_abs = ratio_abs_helper::value; + + static const bool value = lhs_abs <= (INTMAX_MAX - rhs_abs); +}; + +// clang-format off +template ::value> +struct safe_addition : meta::integral_constant {}; +// clang-format on + +template +struct safe_addition { + static const bool no_overflow = safe_addition_helper::value; + ZLL_STATIC_ASSERT(no_overflow); +}; + +} // namespace detail +} // namespace zll + +#endif // ZLL_NUMER_RATIO_OVERFLOWHELPER_HPP diff --git a/include/zll/numeric/ratio/ratio_type.hpp b/include/zll/numeric/ratio/ratio_type.hpp new file mode 100644 index 0000000..5908eca --- /dev/null +++ b/include/zll/numeric/ratio/ratio_type.hpp @@ -0,0 +1,42 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef ZLL_NUMERIC_RATIO_RATIOTYPE_HPP +#define ZLL_NUMERIC_RATIO_RATIOTYPE_HPP + +#include "zll/numeric/ratio/helper.hpp" + +#include "zll/debug/static_assert.hpp" +#include "zll/meta/integral_constant.hpp" + +#include "zll/cstdint.hpp" + +namespace zll { + +template +class ratio { + ZLL_STATIC_ASSERT((DENOM_V != 0)); + + static const zll::intmax_t sign = detail::ratio_sign_helper::value * detail::ratio_sign_helper::value; + static const zll::intmax_t num_abs = detail::ratio_abs_helper::value; + static const zll::intmax_t den_abs = detail::ratio_abs_helper::value; + static const zll::intmax_t gcd = detail::ratio_gcd_helper::value; + +public: + static const zll::intmax_t num = sign * num_abs / gcd; + static const zll::intmax_t den = den_abs / gcd; + + typedef ratio type; +}; + +template +struct is_ratio : meta::false_type {}; + +template +struct is_ratio > : meta::true_type {}; +} // namespace zll + +#endif // ZLL_NUMERIC_RATIO_RATIOTYPE_HPP diff --git a/tests/unit_tests/meson.build b/tests/unit_tests/meson.build index 522145f..8087c80 100644 --- a/tests/unit_tests/meson.build +++ b/tests/unit_tests/meson.build @@ -17,6 +17,9 @@ unit_tests_srcs = [ 'meta/is_scalar_tests.cpp', 'meta/is_union_tests.cpp', 'meta/remove_cv_tests.cpp', + + 'numeric/ratio_tests.cpp', + 'main.cpp', ] diff --git a/tests/unit_tests/numeric/ratio_tests.cpp b/tests/unit_tests/numeric/ratio_tests.cpp new file mode 100644 index 0000000..75040bb --- /dev/null +++ b/tests/unit_tests/numeric/ratio_tests.cpp @@ -0,0 +1,287 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "zll/numeric/ratio.hpp" +#include "zll/meta/is_same.hpp" + +#include + +UTEST(numeric_ratio, normalization) { + { + typedef zll::ratio<-0, 6> test_type; + + ASSERT_EQ(test_type::num, 0); + ASSERT_EQ(test_type::den, 1); + + typedef test_type::type ret_type; + ASSERT_TRUE((zll::meta::is_same >::value)); + } + { + typedef zll::ratio<4, 6> test_type; + + ASSERT_EQ(test_type::num, 2); + ASSERT_EQ(test_type::den, 3); + + typedef test_type::type ret_type; + ASSERT_TRUE((zll::meta::is_same >::value)); + } + { + typedef zll::ratio<2, -7> test_type; + + ASSERT_EQ(test_type::num, -2); + ASSERT_EQ(test_type::den, 7); + + typedef test_type::type ret_type; + ASSERT_TRUE((zll::meta::is_same >::value)); + } + { + typedef zll::ratio<-200, -10> test_type; + + ASSERT_EQ(test_type::num, 20); + ASSERT_EQ(test_type::den, 1); + + typedef test_type::type ret_type; + ASSERT_TRUE((zll::meta::is_same >::value)); + } +} + +UTEST(numeric_ratio, comparsion_equal) { + { + typedef zll::ratio<5, 25> lhs_type; + typedef zll::ratio<1, 5> rhs_type; + + ASSERT_TRUE((zll::ratio_equal::value)); + ASSERT_FALSE((zll::ratio_not_equal::value)); + } + { + typedef zll::ratio<3, -6> lhs_type; + typedef zll::ratio<-1, 2> rhs_type; + + ASSERT_TRUE((zll::ratio_equal::value)); + ASSERT_FALSE((zll::ratio_not_equal::value)); + } + { + typedef zll::ratio<-3, -6> lhs_type; + typedef zll::ratio<-1, 2> rhs_type; + + ASSERT_FALSE((zll::ratio_equal::value)); + ASSERT_TRUE((zll::ratio_not_equal::value)); + } + { + typedef zll::ratio<-2, 1> lhs_type; + typedef zll::ratio<-1, 2> rhs_type; + + ASSERT_FALSE((zll::ratio_equal::value)); + ASSERT_TRUE((zll::ratio_not_equal::value)); + } +} + +UTEST(numeric_ratio, comparsion_less) { + { + typedef zll::ratio<-1, 1> lhs_type; + typedef zll::ratio<1, 1> rhs_type; + + ASSERT_TRUE((zll::ratio_less::value)); + ASSERT_TRUE((zll::ratio_less_equal::value)); + } + { + typedef zll::ratio<5, 25> lhs_type; + typedef zll::ratio<1, 5> rhs_type; + + ASSERT_FALSE((zll::ratio_less::value)); + ASSERT_TRUE((zll::ratio_less_equal::value)); + } + { + typedef zll::ratio<-1, 3> lhs_type; + typedef zll::ratio<-1, 3> rhs_type; + + ASSERT_FALSE((zll::ratio_less::value)); + ASSERT_TRUE((zll::ratio_less_equal::value)); + } + { + typedef zll::ratio<641981, 1339063L> lhs_type; + typedef zll::ratio<1291640, 2694141L> rhs_type; + + ASSERT_FALSE((zll::ratio_less::value)); + ASSERT_FALSE((zll::ratio_less_equal::value)); + } + { + typedef zll::ratio<1291640, 2694141L> lhs_type; + typedef zll::ratio<641981, 1339063L> rhs_type; + + ASSERT_TRUE((zll::ratio_less::value)); + ASSERT_TRUE((zll::ratio_less_equal::value)); + } +} + +UTEST(numeric_ratio, comparsion_greater) { + { + typedef zll::ratio<1, 3> lhs_type; + typedef zll::ratio<-1, 2> rhs_type; + + ASSERT_TRUE((zll::ratio_greater::value)); + ASSERT_TRUE((zll::ratio_greater_equal::value)); + } + { + typedef zll::ratio<34523, 3> lhs_type; + typedef zll::ratio<34523, 2> rhs_type; + + ASSERT_FALSE((zll::ratio_greater::value)); + ASSERT_FALSE((zll::ratio_greater_equal::value)); + } + { + typedef zll::ratio<641981, 1339063L> lhs_type; + typedef zll::ratio<1291640, 2694141L> rhs_type; + + ASSERT_TRUE((zll::ratio_greater::value)); + ASSERT_TRUE((zll::ratio_greater_equal::value)); + } + { + typedef zll::ratio<1291640, 2694141L> lhs_type; + typedef zll::ratio<1291640, 2694141L> rhs_type; + + ASSERT_FALSE((zll::ratio_greater::value)); + ASSERT_TRUE((zll::ratio_greater_equal::value)); + } +} + +UTEST(numeric_ratio, arithmetic_add) { + { + typedef zll::ratio<1, 3> lhs_type; + typedef zll::ratio<1, 2> rhs_type; + + typedef zll::ratio_add::type result_type; + + ASSERT_EQ(result_type::num, 5); + ASSERT_EQ(result_type::den, 6); + } + { + typedef zll::ratio<2, 3> lhs_type; + typedef zll::ratio<4, 3> rhs_type; + + typedef zll::ratio_add::type result_type; + + ASSERT_EQ(result_type::num, 2); + ASSERT_EQ(result_type::den, 1); + } + { + typedef zll::ratio<-2, 3> lhs_type; + typedef zll::ratio<4, 3> rhs_type; + + typedef zll::ratio_add::type result_type; + + ASSERT_EQ(result_type::num, 2); + ASSERT_EQ(result_type::den, 3); + } + { + typedef zll::ratio<1, 5> lhs_type; + typedef zll::ratio<-4, 3> rhs_type; + + typedef zll::ratio_add::type result_type; + + ASSERT_EQ(result_type::num, -17); + ASSERT_EQ(result_type::den, 15); + } +} + +UTEST(numeric_ratio, arithmetic_subtract) { + { + typedef zll::ratio<1, 3> lhs_type; + typedef zll::ratio<1, 2> rhs_type; + + typedef zll::ratio_subtract::type result_type; + + ASSERT_EQ(result_type::num, -1); + ASSERT_EQ(result_type::den, 6); + } + { + typedef zll::ratio<0, 3> lhs_type; + typedef zll::ratio<0, 2> rhs_type; + + typedef zll::ratio_subtract::type result_type; + + ASSERT_EQ(result_type::num, 0); + ASSERT_EQ(result_type::den, 1); + } + { + typedef zll::ratio<0, 1> lhs_type; + typedef zll::ratio<1, 1> rhs_type; + + typedef zll::ratio_subtract::type result_type; + + ASSERT_EQ(result_type::num, -1); + ASSERT_EQ(result_type::den, 1); + } + { + typedef zll::ratio<1, 1> lhs_type; + typedef zll::ratio<0, 1> rhs_type; + + typedef zll::ratio_subtract::type result_type; + + ASSERT_EQ(result_type::num, 1); + ASSERT_EQ(result_type::den, 1); + } +} + +UTEST(numeric_ratio, arithmetic_multiply) { + { + typedef zll::ratio<1, 3> lhs_type; + typedef zll::ratio<1, 2> rhs_type; + + typedef zll::ratio_multiply::type result_type; + + ASSERT_EQ(result_type::num, 1); + ASSERT_EQ(result_type::den, 6); + } + { + typedef zll::ratio<5, 3> lhs_type; + typedef zll::ratio<3, 2> rhs_type; + + typedef zll::ratio_multiply::type result_type; + + ASSERT_EQ(result_type::num, 5); + ASSERT_EQ(result_type::den, 2); + } + { + typedef zll::ratio<-12, 8> lhs_type; + typedef zll::ratio<17, 9> rhs_type; + + typedef zll::ratio_multiply::type result_type; + + ASSERT_EQ(result_type::num, -17); + ASSERT_EQ(result_type::den, 6); + } + { + typedef zll::ratio<-3, 2> lhs_type; + typedef zll::ratio<0, 9> rhs_type; + + typedef zll::ratio_multiply::type result_type; + + ASSERT_EQ(result_type::num, 0); + ASSERT_EQ(result_type::den, 1); + } +} + +UTEST(numeric_ratio, arithmetic_divide) { + { + typedef zll::ratio<-3, 1> lhs_type; + typedef zll::ratio<2, 1> rhs_type; + + typedef zll::ratio_divide::type result_type; + + ASSERT_EQ(result_type::num, -3); + ASSERT_EQ(result_type::den, 2); + } + { + typedef zll::ratio<1, 6> lhs_type; + typedef zll::ratio<-5, 8> rhs_type; + + typedef zll::ratio_divide::type result_type; + + ASSERT_EQ(result_type::num, -4); + ASSERT_EQ(result_type::den, 15); + } +}