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:
committed by
Anas Nashif
parent
1f31da3398
commit
646f255373
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user