diff --git a/include/zephyr/sys/util.h b/include/zephyr/sys/util.h index d65aab7ece216..a9876536a82db 100644 --- a/include/zephyr/sys/util.h +++ b/include/zephyr/sys/util.h @@ -1027,6 +1027,95 @@ static inline size_t sys_count_bits(const void *value, size_t len) return cnt; } +/** + * @brief Returns the sign of a number. + * + * @param x The input value to determine the sign + * + * @retval 1 if x is positive + * @retval -1 if x is negative + * @retval 0 if x is zero + */ +#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) \ + _Generic((a), \ + int8_t : gcd_s, \ + int16_t : gcd_s, \ + int32_t : gcd_s, \ + uint8_t : gcd_u, \ + uint16_t : gcd_u, \ + uint32_t : 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 ? -a : a, b < 0 ? -b : 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) \ + _Generic((a), \ + int8_t : lcm_s, \ + int16_t : lcm_s, \ + int32_t : lcm_s, \ + uint8_t : lcm_u, \ + uint16_t : lcm_u, \ + uint32_t : 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 ? -a : a, b < 0 ? -b : b); +} + #ifdef __cplusplus } #endif diff --git a/tests/unit/util/main.c b/tests/unit/util/main.c index 49295c72a56f9..fc21d7b29ca2d 100644 --- a/tests/unit/util/main.c +++ b/tests/unit/util/main.c @@ -1209,4 +1209,55 @@ 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"); +} + +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"); +} + ZTEST_SUITE(util, NULL, NULL, NULL, NULL, NULL); diff --git a/west.yml b/west.yml index dc9bd944f250d..1af755ff6e99c 100644 --- a/west.yml +++ b/west.yml @@ -250,7 +250,7 @@ manifest: groups: - hal - name: hal_stm32 - revision: 286dd285b5bb4fddafdfff27b5405264e5a61bfe + revision: 2c18f2b49d66d23cabfbd20dd7dbbaef8ee9520b path: modules/hal/stm32 groups: - hal