diff --git a/include/etl/gcd.h b/include/etl/gcd.h new file mode 100644 index 00000000..b99a9d3a --- /dev/null +++ b/include/etl/gcd.h @@ -0,0 +1,139 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2024 John Wellbelove + +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. +******************************************************************************/ + +#ifndef ETL_GDC_INCLUDED +#define ETL_GDC_INCLUDED + +#include "type_traits.h" +#include "absolute.h" +#include "static_assert.h" + +namespace etl +{ + //*************************************************************************** + // Greatest Common Divisor. + // For unsigned types. + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + typename etl::enable_if::value, T>::type + gcd(T a, T b) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Integral type required"); + + while (b != 0) + { + T t = b; + b = a % b; + a = t; + } + + return a; + } + + //*************************************************************************** + // Greatest Common Divisor. + // For signed types. + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + typename etl::enable_if::value, T>::type + gcd(T a, T b) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Integral type required"); + + typedef typename etl::make_unsigned::type utype; + + utype ua = etl::absolute_unsigned(a); + utype ub = etl::absolute_unsigned(b); + + return static_cast(gcd(ua, ub)); + } + +#if ETL_USING_CPP11 + #if ETL_HAS_INITIALIZER_LIST + //*************************************************************************** + // Greatest Common Divisor. + // Non-recursive, using an initializer_list. + // Top level variadic function. + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + T gcd(T first, TRest... rest) ETL_NOEXCEPT + { + T result = first; + + for (T value : {rest...}) + { + result = gcd(result, value); + + if (result == 1) + { + // Early termination: if the GCD is one, it will remain one + // no matter what other numbers are processed. + return 1; + } + } + + return result; + } + #else + //*************************************************************************** + // Greatest Common Divisor. + // Recursive. + // Top level variadic function. + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + T gcd(T a, T b, TRest... rest) ETL_NOEXCEPT + { + T gcd_ab = gcd(a, b); + + if (gcd_ab == 1) + { + // Early termination: if the GCD is one, it will remain one + // no matter what other numbers are processed. + return 1; + } + else + { + return gcd(gcd_ab, rest...); + } + } + #endif +#endif +} + +#endif + diff --git a/include/etl/lcm.h b/include/etl/lcm.h new file mode 100644 index 00000000..43bb0021 --- /dev/null +++ b/include/etl/lcm.h @@ -0,0 +1,141 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2024 John Wellbelove + +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. +******************************************************************************/ + +#ifndef ETL_LCM_INCLUDED +#define ETL_LCM_INCLUDED + +#include "type_traits.h" +#include "absolute.h" +#include "static_assert.h" +#include "gcd.h" + +namespace etl +{ + //*************************************************************************** + // Least Common Multiple. + // For unsigned types. + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + typename etl::enable_if::value, T>::type + lcm(T a, T b) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Integral type required"); + + // Early termination: if either number is zero, the LCM is zero. + if (a == 0 || b == 0) + { + return 0; + } + else + { + return a * (b / gcd(a, b)); + } + } + + //*************************************************************************** + // Least Common Multiple. + // For signed types. + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + typename etl::enable_if::value, T>::type + lcm(T a, T b) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Integral type required"); + + typedef typename etl::make_unsigned::type utype; + + utype ua = etl::absolute_unsigned(a); + utype ub = etl::absolute_unsigned(b); + + return static_cast(lcm(ua, ub)); + } + +#if ETL_USING_CPP11 + #if ETL_HAS_INITIALIZER_LIST + //*************************************************************************** + // Least Common Multiple. + // Non-recursive, using an initializer_list. + // Top level variadic function. + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + T lcm(T first, TRest... rest) ETL_NOEXCEPT + { + T result = first; + + for (T value : {rest...}) + { + result = lcm(result, value); + + if (result == 0) + { + // Early termination: if the LCM is zero, it will remain zero + // no matter what other numbers are processed. + return 0; + } + } + + return result; + } + #else + //*************************************************************************** + // Least Common Multiple. + // Recursive + // Top level variadic function. + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + T lcm(T a, T b, TRest... rest) ETL_NOEXCEPT + { + T lcm_ab = lcm(a, b); + + if (lcm_ab == 0) + { + // Early termination: if the LCM is zero, it will remain zero + // no matter what other numbers are processed. + return 0; + } + else + { + return lcm(lcm_ab, rest...); + } + } + #endif +#endif +} + +#endif + diff --git a/test/test_math_functions.cpp b/test/test_math_functions.cpp index 95cb9575..c4255a5a 100644 --- a/test/test_math_functions.cpp +++ b/test/test_math_functions.cpp @@ -35,6 +35,8 @@ SOFTWARE. #include "etl/sqrt.h" #include "etl/permutations.h" #include "etl/combinations.h" +#include "etl/gcd.h" +#include "etl/lcm.h" namespace { @@ -457,5 +459,45 @@ namespace CHECK_EQUAL((combinations(13, 9)), (actual = etl::combinations<13, 9>::value)); CHECK_EQUAL((combinations(14, 10)), (actual = etl::combinations<14, 10>::value)); } + + //************************************************************************* + TEST(test_gdc_for_positive_integers) + { + CHECK_EQUAL(1, etl::gcd( 3, 5, 7)); + CHECK_EQUAL(2, etl::gcd( 4, 6, 8)); + CHECK_EQUAL(3, etl::gcd( 9, 6, 3)); + CHECK_EQUAL(4, etl::gcd( 8, 4, 12)); + CHECK_EQUAL(5, etl::gcd(10, 5, 15)); + } + + //************************************************************************* + TEST(test_gdc_for_negative_integers) + { + CHECK_EQUAL(1, etl::gcd( -3, -5, -7)); + CHECK_EQUAL(2, etl::gcd( -4, -6, -8)); + CHECK_EQUAL(3, etl::gcd( -9, -6, -3)); + CHECK_EQUAL(4, etl::gcd( -8, -4, -12)); + CHECK_EQUAL(5, etl::gcd(-10, -5, -15)); + } + + //************************************************************************* + TEST(test_lcm_for_positive_integers) + { + CHECK_EQUAL(12, etl::lcm(3, 4, 6)); + CHECK_EQUAL(30, etl::lcm(3, 5, 10)); + CHECK_EQUAL(20, etl::lcm(2, 5, 4)); + CHECK_EQUAL(0, etl::lcm(0, 5, 7)); + CHECK_EQUAL(5, etl::lcm(5, 5, 5)); + } + + //************************************************************************* + TEST(test_lcm_for_negative_integers) + { + CHECK_EQUAL(12, etl::lcm(-3, -4, -6)); + CHECK_EQUAL(30, etl::lcm(-3, -5, -10)); + CHECK_EQUAL(20, etl::lcm(-2, -5, -4)); + CHECK_EQUAL(0, etl::lcm(-0, -5, -7)); + CHECK_EQUAL(5, etl::lcm(-5, -5, -5)); + } }; } diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index c95a0d70..d8a6e7a1 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3082,6 +3082,8 @@ + + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index c4825d0a..641f479f 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -1395,6 +1395,12 @@ ETL\Maths\CRC + + ETL\Maths + + + ETL\Maths +