AK: Add lowest common multiple and greatest common divisor functions

This commit is contained in:
Tim Ledbetter 2025-04-14 10:31:45 +01:00
parent f4bb3a82cf
commit 465b7ebd04
2 changed files with 90 additions and 0 deletions

View file

@ -85,4 +85,61 @@ constexpr T reinterpret_as_octal(T decimal)
return result;
}
template<Unsigned T>
constexpr T gcd(T x, T y)
{
if (x == 0)
return y;
if (y == 0)
return x;
int shift = 0;
while (((x | y) & 1) == 0) {
x >>= 1;
y >>= 1;
shift++;
}
while (x != y) {
if (x & 1) {
if (y & 1) {
if (x > y)
x -= y;
else
y -= x;
} else {
y >>= 1;
}
} else {
x >>= 1;
if (y & 1) {
if (x < y)
swap(x, y);
}
}
}
return x << shift;
}
template<Signed T>
constexpr T gcd(T x, T y)
{
return gcd(static_cast<MakeUnsigned<T>>(abs(x)), static_cast<MakeUnsigned<T>>(abs(y)));
}
template<Unsigned T>
constexpr T lcm(T x, T y)
{
if (x == 0 || y == 0)
return 0;
return x / gcd(x, y) * y;
}
template<Signed T>
constexpr T lcm(T x, T y)
{
return lcm(static_cast<MakeUnsigned<T>>(abs(x)), static_cast<MakeUnsigned<T>>(abs(y)));
}
}

View file

@ -131,3 +131,36 @@ TEST_CASE(clamp_to)
EXPECT_EQ(AK::clamp_to<i64>(-9223372036854775808.0), NumericLimits<i64>::min());
EXPECT_EQ(AK::clamp_to<i64>(9223372036854775807.0), NumericLimits<i64>::max());
}
TEST_CASE(gcd)
{
EXPECT_EQ(AK::gcd(0, 0), 0);
EXPECT_EQ(AK::gcd(1, 1), 1);
EXPECT_EQ(AK::gcd(0, 2), 2);
EXPECT_EQ(AK::gcd(2, 0), 2);
EXPECT_EQ(AK::gcd(8, 12), 4);
EXPECT_EQ(AK::gcd(17, 23), 1);
EXPECT_EQ(AK::gcd(48, 36), 12);
EXPECT_EQ(AK::gcd(-8, 12), 4);
EXPECT_EQ(AK::gcd(8, -12), 4);
EXPECT_EQ(AK::gcd(-8, -12), 4);
EXPECT_EQ(AK::gcd(100, 100), 100);
EXPECT_EQ(AK::gcd(13, 1), 1);
EXPECT_EQ(AK::gcd(-NumericLimits<i32>::max(), NumericLimits<i32>::max()), NumericLimits<i32>::max());
}
TEST_CASE(lcm)
{
EXPECT_EQ(AK::lcm(0, 0), 0);
EXPECT_EQ(AK::lcm(0, 5), 0);
EXPECT_EQ(AK::lcm(5, 0), 0);
EXPECT_EQ(AK::lcm(1, 1), 1);
EXPECT_EQ(AK::lcm(4, 6), 12);
EXPECT_EQ(AK::lcm(7, 13), 91);
EXPECT_EQ(AK::lcm(12, 18), 36);
EXPECT_EQ(AK::lcm(-4, 6), 12);
EXPECT_EQ(AK::lcm(4, -6), 12);
EXPECT_EQ(AK::lcm(-4, -6), 12);
EXPECT_EQ(AK::lcm(10, 10), 10);
EXPECT_EQ(AK::lcm(1, 8), 8);
}