diff --git a/include/etl/ratio.h b/include/etl/ratio.h index 8c7fa81d9..1035e7741 100644 --- a/include/etl/ratio.h +++ b/include/etl/ratio.h @@ -33,6 +33,8 @@ SOFTWARE. #include "platform.h" +#include "type_traits.h" + #include #include @@ -41,17 +43,17 @@ SOFTWARE. namespace etl { - template + template struct ratio { static ETL_CONSTANT intmax_t num = NUM; static ETL_CONSTANT intmax_t den = DEN; }; - template + template ETL_CONSTANT intmax_t ratio::num; - template + template ETL_CONSTANT intmax_t ratio::den; #if INT_MAX > INT32_MAX @@ -97,6 +99,153 @@ namespace etl /// An approximation of e. typedef ratio<326, 120> ratio_e; + +#if ETL_USING_CPP11 + namespace private_ratio + { + // Primary template for GCD calculation + template + struct ratio_gcd; + + // Specialisation for the case when B is not zero + template + struct ratio_gcd + { + static constexpr T value = ratio_gcd::value; + }; + + // Specialisation for the case when B is zero + template + struct ratio_gcd + { + static constexpr T value = (A < 0) ? -A : A; + }; + + // Primary template for LCM calculation + template + struct ratio_lcm + { + private: + + static constexpr T product = ((A * B) < 0) ? -(A * B) : A * B; + + public: + + static constexpr T value = product / ratio_gcd::value; + }; + + template + struct ratio_reduce + { + private: + + static ETL_CONSTEXPR11 intmax_t gcd = etl::private_ratio::ratio_gcd::value; + + public: + + using value = ratio; + }; + + template + struct ratio_add + { + private: + + static ETL_CONSTEXPR11 intmax_t lcm = etl::private_ratio::ratio_lcm::value; + + public: + + using value = typename ratio_reduce>::value; + }; + + template + struct ratio_subtract + { + public: + using value = typename ratio_add>::value; + }; + + template + struct ratio_multiply + { + private: + static ETL_CONSTEXPR11 intmax_t gcd1 = etl::private_ratio::ratio_gcd::value; + static ETL_CONSTEXPR11 intmax_t gcd2 = etl::private_ratio::ratio_gcd::value; + + public: + using value = ratio<(R1::num / gcd1) * (R2::num / gcd2), (R1::den / gcd2) * (R2::den / gcd1)>; + }; + + template + struct ratio_divide + { + public: + using value = typename ratio_multiply>::value; + }; + } + + template + using ratio_add = typename private_ratio::ratio_add::value; + + template + using ratio_subtract = typename private_ratio::ratio_subtract::value; + + template + using ratio_multiply = typename private_ratio::ratio_multiply::value; + + template + using ratio_divide = typename private_ratio::ratio_divide::value; + + template + struct ratio_equal: etl::integral_constant + { + }; + + template + struct ratio_not_equal: etl::integral_constant + { + }; + + template + struct ratio_less: etl::integral_constant + { + }; + + template + struct ratio_less_equal: etl::integral_constant + { + }; + + template + struct ratio_greater: etl::integral_constant R2::num * R1::den)> + { + }; + + template + struct ratio_greater_equal: etl::integral_constant= R2::num * R1::den)> + { + }; + +#if ETL_USING_CPP14 + template + ETL_CONSTEXPR14 bool ratio_equal_v = ratio_equal::value; + + template + ETL_CONSTEXPR14 bool ratio_not_equal_v = ratio_not_equal::value; + + template + ETL_CONSTEXPR14 bool ratio_less_v = ratio_less::value; + + template + ETL_CONSTEXPR14 bool ratio_less_equal_v = ratio_less_equal::value; + + template + ETL_CONSTEXPR14 bool ratio_greater_v = ratio_greater::value; + + template + ETL_CONSTEXPR14 bool ratio_greater_equal_v = ratio_greater_equal::value; +#endif +#endif } #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ce9e2a664..725b6928f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -222,6 +222,7 @@ add_executable(etl_tests test_queue_spsc_locked.cpp test_queue_spsc_locked_small.cpp test_random.cpp + test_ratio.cpp test_reference_flat_map.cpp test_reference_flat_multimap.cpp test_reference_flat_multiset.cpp diff --git a/test/test_ratio.cpp b/test/test_ratio.cpp new file mode 100644 index 000000000..12a0b406f --- /dev/null +++ b/test/test_ratio.cpp @@ -0,0 +1,144 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2025 BMW AG + +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 "unit_test_framework.h" + +#include "etl/ratio.h" + +#if ETL_USING_CPP11 +namespace +{ + SUITE(test_ratio) + { + //************************************************************************* + TEST(test_ratio_add) + { + using r1 = etl::ratio<1, 2>; + using r2 = etl::ratio<2, 3>; + using r3 = etl::ratio_add; + + CHECK((etl::ratio_equal>::value)); + CHECK((!etl::ratio_equal>::value)); + } + + //************************************************************************* + TEST(test_ratio_subtract) + { + using r1 = etl::ratio<1, 2>; + using r2 = etl::ratio<2, 3>; + using r3 = etl::ratio_subtract; + + CHECK((etl::ratio_equal>::value)); + CHECK((!etl::ratio_equal>::value)); + } + + //************************************************************************* + TEST(test_ratio_multiply) + { + using r1 = etl::ratio<1, 2>; + using r2 = etl::ratio<2, 3>; + using r3 = etl::ratio_multiply; + + CHECK((etl::ratio_equal>::value)); + CHECK((!etl::ratio_equal>::value)); + } + + //************************************************************************* + TEST(test_ratio_divide) + { + using r1 = etl::ratio<1, 2>; + using r2 = etl::ratio<2, 3>; + using r3 = etl::ratio_divide; + + CHECK((!etl::ratio_not_equal>::value)); + CHECK((etl::ratio_not_equal>::value)); + } + + //************************************************************************* + TEST(test_ratio_equal) + { + using r1 = etl::ratio<1, 2>; + using r2 = etl::ratio<2, 3>; + + CHECK((etl::ratio_equal::value)); + CHECK((!etl::ratio_equal::value)); + } + + //************************************************************************* + TEST(test_ratio_not_equal) + { + using r1 = etl::ratio<1, 2>; + using r2 = etl::ratio<2, 3>; + + CHECK((!etl::ratio_not_equal::value)); + CHECK((etl::ratio_not_equal::value)); + } + + //************************************************************************* + TEST(test_ratio_less) + { + using r1 = etl::ratio<1, 2>; + using r2 = etl::ratio<2, 3>; + + CHECK((etl::ratio_less::value)); + CHECK((!etl::ratio_less::value)); + } + + //************************************************************************* + TEST(test_ratio_less_equal) + { + using r1 = etl::ratio<1, 2>; + using r2 = etl::ratio<2, 3>; + + CHECK((etl::ratio_less_equal::value)); + CHECK((etl::ratio_less_equal::value)); + } + + //************************************************************************* + TEST(test_ratio_greater) + { + using r1 = etl::ratio<4, 3>; + using r2 = etl::ratio<2, 3>; + + CHECK((etl::ratio_greater::value)); + CHECK((!etl::ratio_greater::value)); + } + + //************************************************************************* + TEST(test_ratio_greater_equal) + { + using r1 = etl::ratio<4, 3>; + using r2 = etl::ratio<2, 3>; + + CHECK((etl::ratio_greater_equal::value)); + CHECK((etl::ratio_greater_equal::value)); + } + }; + +} +#endif