Skip to content

Commit be30446

Browse files
committed
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 <[email protected]> Signed-off-by: Trung Hieu Le <[email protected]>
1 parent d57d636 commit be30446

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

include/zephyr/sys/util.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,84 @@ static inline size_t sys_count_bits(const void *value, size_t len)
10381038
*/
10391039
#define SIGN(x) (((x) > 0) - ((x) < 0))
10401040

1041+
/**
1042+
* @brief Compute the Greatest Common Divisor (GCD) of two integers
1043+
* using the Euclidean algorithm.
1044+
*
1045+
* @param a First integer
1046+
* @param b Second integer
1047+
*
1048+
* @return The greatest common divisor of a and b, always returns an unsigned value.
1049+
* If one of the parameters is 0, returns the absolute value of the other parameter.
1050+
*/
1051+
#define gcd(a, b) \
1052+
_Generic((a), \
1053+
int8_t : gcd_s, \
1054+
int16_t : gcd_s, \
1055+
int32_t : gcd_s, \
1056+
uint8_t : gcd_u, \
1057+
uint16_t : gcd_u, \
1058+
uint32_t : gcd_u)(a, b)
1059+
1060+
static ALWAYS_INLINE uint32_t gcd_u(uint32_t a, uint32_t b)
1061+
{
1062+
uint32_t c;
1063+
1064+
if (a == 0) {
1065+
return b;
1066+
}
1067+
1068+
if (b == 0) {
1069+
return a;
1070+
}
1071+
1072+
c = a % b;
1073+
while (c != 0) {
1074+
a = b;
1075+
b = c;
1076+
c = a % b;
1077+
}
1078+
1079+
return b;
1080+
}
1081+
1082+
static ALWAYS_INLINE uint32_t gcd_s(int32_t a, int32_t b)
1083+
{
1084+
return gcd_u(a < 0 ? -a : a, b < 0 ? -b : b);
1085+
}
1086+
1087+
/**
1088+
* @brief Compute the Least Common Multiple (LCM) of two integers.
1089+
*
1090+
* @param a First integer
1091+
* @param b Second integer
1092+
*
1093+
* @retval The least common multiple of a and b.
1094+
* @retval 0 if either input is 0.
1095+
*/
1096+
#define lcm(a, b) \
1097+
_Generic((a), \
1098+
int8_t : lcm_s, \
1099+
int16_t : lcm_s, \
1100+
int32_t : lcm_s, \
1101+
uint8_t : lcm_u, \
1102+
uint16_t : lcm_u, \
1103+
uint32_t : lcm_u)(a, b)
1104+
1105+
static ALWAYS_INLINE uint64_t lcm_u(uint32_t a, uint32_t b)
1106+
{
1107+
if (a == 0 || b == 0) {
1108+
return 0;
1109+
}
1110+
1111+
return (uint64_t)(a / gcd_u(a, b)) * (uint64_t)b;
1112+
}
1113+
1114+
static ALWAYS_INLINE uint64_t lcm_s(int32_t a, int32_t b)
1115+
{
1116+
return lcm_u(a < 0 ? -a : a, b < 0 ? -b : b);
1117+
}
1118+
10411119
#ifdef __cplusplus
10421120
}
10431121
#endif

tests/unit/util/main.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,4 +1209,57 @@ ZTEST(util, test_bitmask_find_gap)
12091209
test_single_bitmask_find_gap(0x0000000F, 2, 6, false, 4, __LINE__);
12101210
}
12111211

1212+
ZTEST(util, test_gcd)
1213+
{
1214+
/* Zero cases */
1215+
zassert_equal(gcd(0, 0), 0, "should be 0");
1216+
zassert_equal(gcd(0, INT_MAX), INT_MAX, "should be 0");
1217+
zassert_equal(gcd(INT_MAX, 0), INT_MAX, "should be 0");
1218+
1219+
/* normal cases */
1220+
zassert_equal(gcd(12, 8), 4, "should be 4");
1221+
1222+
/* Negative number cases */
1223+
zassert_equal(gcd(-12, 8), 4, "should be 4");
1224+
zassert_equal(gcd(-12, -8), 4, "should be 4");
1225+
1226+
/* prime numbers */
1227+
zassert_equal(gcd(17, 13), 1, "should be 1");
1228+
zassert_equal(gcd(25, 49), 1, "should be 1");
1229+
1230+
/* Boundary values */
1231+
zassert_equal(gcd(INT_MAX, INT_MAX), INT_MAX, "should be INT_MAX");
1232+
zassert_equal(gcd(INT_MIN, INT_MIN), (uint32_t)(-(int64_t)INT_MIN), "should be INT_MAX + 1");
1233+
zassert_equal(gcd(INT_MIN, INT_MAX), 1, "should be 1");
1234+
zassert_equal(gcd(UINT32_MAX, UINT32_MAX), UINT32_MAX, "should be UINT32_MAX");
1235+
}
1236+
1237+
ZTEST(util, test_lcm)
1238+
{
1239+
/* Zero cases - lcm with 0 should be 0 */
1240+
zassert_equal(lcm(0, 0), 0, "should be 0");
1241+
zassert_equal(lcm(0, INT_MAX), 0, "should be 0");
1242+
1243+
/* Normal cases */
1244+
zassert_equal(lcm(12, 8), 24, "should be 24");
1245+
zassert_equal(lcm(8, 12), 24, "should be 24");
1246+
1247+
/* Negative number cases - lcm should always be positive */
1248+
zassert_equal(lcm(-12, 8), 24, "should be 24");
1249+
1250+
/* Prime numbers (gcd = 1, so lcm = a * b) */
1251+
zassert_equal(lcm(17, 13), 221, "should be 221");
1252+
1253+
/* Boundary values */
1254+
zassert_equal(lcm(INT_MAX, INT_MAX - 1), (uint64_t)INT_MAX * (INT_MAX - 1),
1255+
"should be INT_MAX * (INT_MAX - 1)");
1256+
zassert_equal(lcm(INT_MIN, INT_MIN), (uint64_t)INT_MAX + 1,
1257+
"should be INT_MIN");
1258+
zassert_equal(lcm(INT_MIN, INT_MAX),
1259+
(uint64_t)INT_MAX * (uint64_t)(-(int64_t)INT_MIN),
1260+
"should be INT_MAX * (INT_MAX + 1)");
1261+
zassert_equal(lcm(UINT32_MAX, UINT32_MAX), UINT32_MAX,
1262+
"should be UINT32_MAX");
1263+
}
1264+
12121265
ZTEST_SUITE(util, NULL, NULL, NULL, NULL, NULL);

0 commit comments

Comments
 (0)