sys: util: Add gcd and lcm utilities

Add helpers to compute Greates Common Divisor (GCD) and Least Common
Multiple (LCM).

Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com>
Signed-off-by: Trung Hieu Le <trunghieu.le@nxp.com>
This commit is contained in:
Phi Bang Nguyen
2025-11-04 17:31:03 +01:00
committed by Anas Nashif
parent 1f31da3398
commit 646f255373
2 changed files with 129 additions and 0 deletions

View File

@@ -1038,6 +1038,70 @@ static inline size_t sys_count_bits(const void *value, size_t len)
*/
#define SIGN(x) (((x) > 0) - ((x) < 0))
/**
* @brief Compute the Greatest Common Divisor (GCD) of two integers
* using the Euclidean algorithm.
*
* @param a First integer
* @param b Second integer
*
* @return The greatest common divisor of a and b, always returns an unsigned value.
* If one of the parameters is 0, returns the absolute value of the other parameter.
*/
#define gcd(a, b) ((((__typeof__(a))-1) < 0) ? gcd_s(a, b) : gcd_u(a, b))
static ALWAYS_INLINE uint32_t gcd_u(uint32_t a, uint32_t b)
{
uint32_t c;
if (a == 0) {
return b;
}
if (b == 0) {
return a;
}
c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return b;
}
static ALWAYS_INLINE uint32_t gcd_s(int32_t a, int32_t b)
{
return gcd_u(a < 0 ? -(uint32_t)a : (uint32_t)a, b < 0 ? -(uint32_t)b : (uint32_t)b);
}
/**
* @brief Compute the Least Common Multiple (LCM) of two integers.
*
* @param a First integer
* @param b Second integer
*
* @retval The least common multiple of a and b.
* @retval 0 if either input is 0.
*/
#define lcm(a, b) ((((__typeof__(a))-1) < 0) ? lcm_s(a, b) : lcm_u(a, b))
static ALWAYS_INLINE uint64_t lcm_u(uint32_t a, uint32_t b)
{
if (a == 0 || b == 0) {
return 0;
}
return (uint64_t)(a / gcd_u(a, b)) * (uint64_t)b;
}
static ALWAYS_INLINE uint64_t lcm_s(int32_t a, int32_t b)
{
return lcm_u(a < 0 ? -(uint32_t)a : (uint32_t)a, b < 0 ? -(uint32_t)b : (uint32_t)b);
}
#ifdef __cplusplus
}
#endif

View File

@@ -1240,4 +1240,69 @@ ZTEST(util, test_bitmask_find_gap)
test_single_bitmask_find_gap(0x0000000F, 2, 6, false, 4, __LINE__);
}
ZTEST(util, test_gcd)
{
/* Zero cases */
zassert_equal(gcd(0, 0), 0, "should be 0");
zassert_equal(gcd(0, INT_MAX), INT_MAX, "should be 0");
zassert_equal(gcd(INT_MAX, 0), INT_MAX, "should be 0");
/* Normal cases */
zassert_equal(gcd(12, 8), 4, "should be 4");
/* Negative number cases */
zassert_equal(gcd(-12, 8), 4, "should be 4");
zassert_equal(gcd(-12, -8), 4, "should be 4");
/* Prime numbers */
zassert_equal(gcd(17, 13), 1, "should be 1");
zassert_equal(gcd(25, 49), 1, "should be 1");
/* Boundary values */
zassert_equal(gcd(INT_MAX, INT_MAX), INT_MAX, "should be INT_MAX");
zassert_equal(gcd(INT_MIN, INT_MIN), (uint32_t)(-(int64_t)INT_MIN),
"should be INT_MAX + 1");
zassert_equal(gcd(INT_MIN, INT_MAX), 1, "should be 1");
zassert_equal(gcd(UINT32_MAX, UINT32_MAX), UINT32_MAX, "should be UINT32_MAX");
/* Macro expansion */
int a = 12, b = 8;
zassert_equal(gcd(a++, b++), 4, "should be 4");
zassert_equal(a, 13, "should be 13");
zassert_equal(b, 9, "should be 9");
}
ZTEST(util, test_lcm)
{
/* Zero cases - lcm with 0 should be 0 */
zassert_equal(lcm(0, 0), 0, "should be 0");
zassert_equal(lcm(0, INT_MAX), 0, "should be 0");
/* Normal cases */
zassert_equal(lcm(12, 8), 24, "should be 24");
zassert_equal(lcm(8, 12), 24, "should be 24");
/* Negative number cases - lcm should always be positive */
zassert_equal(lcm(-12, 8), 24, "should be 24");
/* Prime numbers (gcd = 1, so lcm = a * b) */
zassert_equal(lcm(17, 13), 221, "should be 221");
/* Boundary values */
zassert_equal(lcm(INT_MAX, INT_MAX - 1), (uint64_t)INT_MAX * (INT_MAX - 1),
"should be INT_MAX * (INT_MAX - 1)");
zassert_equal(lcm(INT_MIN, INT_MIN), (uint64_t)INT_MAX + 1, "should be INT_MAX + 1");
zassert_equal(lcm(INT_MIN, INT_MAX), (uint64_t)INT_MAX * (uint64_t)(-(int64_t)INT_MIN),
"should be INT_MAX * (INT_MAX + 1)");
zassert_equal(lcm(UINT32_MAX, UINT32_MAX), UINT32_MAX, "should be UINT32_MAX");
/* Macro expansion */
int a = 12, b = 8;
zassert_equal(lcm(a++, b++), 24, "should be 4");
zassert_equal(a, 13, "should be 13");
zassert_equal(b, 9, "should be 9");
}
ZTEST_SUITE(util, NULL, NULL, NULL, NULL, NULL);