From 31dea89fe0c7c912cd47003db5340a4c8612835b Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Mon, 14 Apr 2025 10:31:45 +0100 Subject: [PATCH] AK: Add lowest common multiple and greatest common divisor functions --- AK/IntegralMath.h | 57 ++++++++++++++++++++++++++++++++++++ Tests/AK/TestIntegerMath.cpp | 33 +++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/AK/IntegralMath.h b/AK/IntegralMath.h index ebd279768fa..0f90ae81899 100644 --- a/AK/IntegralMath.h +++ b/AK/IntegralMath.h @@ -85,4 +85,61 @@ constexpr T reinterpret_as_octal(T decimal) return result; } +template +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 +constexpr T gcd(T x, T y) +{ + return gcd(static_cast>(abs(x)), static_cast>(abs(y))); +} + +template +constexpr T lcm(T x, T y) +{ + if (x == 0 || y == 0) + return 0; + return x / gcd(x, y) * y; +} + +template +constexpr T lcm(T x, T y) +{ + return lcm(static_cast>(abs(x)), static_cast>(abs(y))); +} + } diff --git a/Tests/AK/TestIntegerMath.cpp b/Tests/AK/TestIntegerMath.cpp index 26efd83441e..b31ece39ff8 100644 --- a/Tests/AK/TestIntegerMath.cpp +++ b/Tests/AK/TestIntegerMath.cpp @@ -131,3 +131,36 @@ TEST_CASE(clamp_to) EXPECT_EQ(AK::clamp_to(-9223372036854775808.0), NumericLimits::min()); EXPECT_EQ(AK::clamp_to(9223372036854775807.0), NumericLimits::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::max(), NumericLimits::max()), NumericLimits::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); +}