diff --git a/Sources/IntegerUtilities/CMakeLists.txt b/Sources/IntegerUtilities/CMakeLists.txt index db167c77..6dcac644 100644 --- a/Sources/IntegerUtilities/CMakeLists.txt +++ b/Sources/IntegerUtilities/CMakeLists.txt @@ -9,7 +9,7 @@ See https://swift.org/LICENSE.txt for license information add_library(IntegerUtilities DivideWithRounding.swift - GCD.swift + GreatestCommonDivisor.swift Rotate.swift RoundingRule.swift SaturatingArithmetic.swift diff --git a/Sources/IntegerUtilities/GCD.swift b/Sources/IntegerUtilities/GCD.swift deleted file mode 100644 index 28e09bdb..00000000 --- a/Sources/IntegerUtilities/GCD.swift +++ /dev/null @@ -1,40 +0,0 @@ -//===--- GCD.swift --------------------------------------------*- swift -*-===// -// -// This source file is part of the Swift Numerics open source project -// -// Copyright (c) 2021-2024 Apple Inc. and the Swift Numerics project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -/// The [greatest common divisor][gcd] of `a` and `b`. -/// -/// If both inputs are zero, the result is zero. If one input is zero, the -/// result is the absolute value of the other input. -/// -/// The result must be representable within its type. In particular, the gcd -/// of a signed, fixed-width integer type's minimum with itself (or zero) -/// cannot be represented, and results in a trap. -/// -/// gcd(Int.min, Int.min) // Overflow error -/// gcd(Int.min, 0) // Overflow error -/// -/// [gcd]: https://en.wikipedia.org/wiki/Greatest_common_divisor -@inlinable -public func gcd(_ a: T, _ b: T) -> T { - var x = a - var y = b - if x.magnitude < y.magnitude { swap(&x, &y) } - // Avoid overflow when x = signed min, y = -1. - if y.magnitude == 1 { return 1 } - // Euclidean algorithm for GCD. It's worth using Lehmer instead for larger - // integer types, but for now this is good and dead-simple and faster than - // the other obvious choice, the binary algorithm. - while y != 0 { (x, y) = (y, x%y) } - // Try to convert result to T. - if let result = T(exactly: x.magnitude) { return result } - // If that fails, produce a diagnostic. - fatalError("GCD (\(x)) is not representable as \(T.self).") -} diff --git a/Sources/IntegerUtilities/GreatestCommonDivisor.swift b/Sources/IntegerUtilities/GreatestCommonDivisor.swift new file mode 100644 index 00000000..7321bff2 --- /dev/null +++ b/Sources/IntegerUtilities/GreatestCommonDivisor.swift @@ -0,0 +1,35 @@ +//===--- GreatestCommonDivisor.swift --------------------------*- swift -*-===// +// +// This source file is part of the Swift Numerics open source project +// +// Copyright (c) 2021-2025 Apple Inc. and the Swift Numerics project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +/// The [greatest common divisor][gcd] of `a` and `b`. +/// +/// If both inputs are zero, the result is zero. If one input is zero, the +/// result is the absolute value of the other input. +/// +/// [gcd]: https://en.wikipedia.org/wiki/Greatest_common_divisor +@inlinable +public func gcd(_ a: T, _ b: T) -> T.Magnitude { + var x = a + var y = b + + if x.magnitude < y.magnitude { + swap(&x, &y) + } + + // Euclidean algorithm for GCD. It's worth using Lehmer instead for larger + // integer types, but for now this is good and dead-simple and faster than + // the other obvious choice, the binary algorithm. + while y != 0 { + (x, y) = (y, x % y) + } + + return x.magnitude +} diff --git a/Tests/IntegerUtilitiesTests/CMakeLists.txt b/Tests/IntegerUtilitiesTests/CMakeLists.txt index 15376625..9fd0cd7a 100644 --- a/Tests/IntegerUtilitiesTests/CMakeLists.txt +++ b/Tests/IntegerUtilitiesTests/CMakeLists.txt @@ -10,7 +10,7 @@ See https://swift.org/LICENSE.txt for license information add_library(IntegerUtilitiesTests DivideTests.swift DoubleWidthTests.swift - GCDTests.swift + GreatestCommonDivisorTests.swift RotateTests.swift SaturatingArithmeticTests.swift ShiftTests.swift) diff --git a/Tests/IntegerUtilitiesTests/GCDTests.swift b/Tests/IntegerUtilitiesTests/GCDTests.swift deleted file mode 100644 index 6400732c..00000000 --- a/Tests/IntegerUtilitiesTests/GCDTests.swift +++ /dev/null @@ -1,44 +0,0 @@ -//===--- GCDTests.swift ---------------------------------------*- swift -*-===// -// -// This source file is part of the Swift Numerics open source project -// -// Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import IntegerUtilities -import XCTest - -final class IntegerUtilitiesGCDTests: XCTestCase { - func testGCDInt() { - XCTAssertEqual(gcd(0, 0), 0) - XCTAssertEqual(gcd(0, 1), 1) - XCTAssertEqual(gcd(1, 0), 1) - XCTAssertEqual(gcd(0, -1), 1) - XCTAssertEqual(gcd(1, 1), 1) - XCTAssertEqual(gcd(1, 2), 1) - XCTAssertEqual(gcd(2, 2), 2) - XCTAssertEqual(gcd(4, 2), 2) - XCTAssertEqual(gcd(6, 8), 2) - XCTAssertEqual(gcd(77, 91), 7) - XCTAssertEqual(gcd(24, -36), 12) - XCTAssertEqual(gcd(-24, -36), 12) - XCTAssertEqual(gcd(51, 34), 17) - XCTAssertEqual(gcd(64, 96), 32) - XCTAssertEqual(gcd(-64, 96), 32) - XCTAssertEqual(gcd(4*7*19, 27*25), 1) - XCTAssertEqual(gcd(16*315, 11*315), 315) - XCTAssertEqual(gcd(97*67*53*27*8, 83*67*53*9*32), 67*53*9*8) - XCTAssertEqual(gcd(Int.min, 2), 2) - - // TODO: Enable these when version compatibility allows. - // - // XCTExpectFailure{ gcd(0, Int.min) } - // XCTExpectFailure{ gcd(Int.min, 0) } - // XCTExpectFailure{ gcd(Int.min, Int.min) } - } -} diff --git a/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift new file mode 100644 index 00000000..f22a3d36 --- /dev/null +++ b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift @@ -0,0 +1,42 @@ +//===--- GreatestCommonDivisorTests.swift ---------------------*- swift -*-===// +// +// This source file is part of the Swift Numerics open source project +// +// Copyright (c) 2021-2025 Apple Inc. and the Swift Numerics project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import IntegerUtilities +import Testing + +struct `Greatest Common Divisor Tests` { + @Test func `gcd`() async throws { + #expect(gcd(0, 0) == 0) + #expect(gcd(0, 1) == 1) + #expect(gcd(1, 0) == 1) + #expect(gcd(0, -1) == 1) + #expect(gcd(-1, 0) == 1) + #expect(gcd(1, 1) == 1) + #expect(gcd(1, 2) == 1) + #expect(gcd(2, 2) == 2) + #expect(gcd(4, 2) == 2) + #expect(gcd(6, 8) == 2) + #expect(gcd(77, 91) == 7) + #expect(gcd(24, -36) == 12) + #expect(gcd(-24, -36) == 12) + #expect(gcd(51, 34) == 17) + #expect(gcd(64, 96) == 32) + #expect(gcd(-64, 96) == 32) + #expect(gcd(4*7*19, 27*25) == 1) + #expect(gcd(16*315, 11*315) == 315) + #expect(gcd(97*67*53*27*8, 83*67*53*9*32) == 67*53*9*8) + #expect(gcd(Int.max, Int.max) == Int.max) + #expect(gcd(0, Int.min) == Int.min.magnitude) + #expect(gcd(Int.min, 0) == Int.min.magnitude) + #expect(gcd(Int.min, Int.min) == Int.min.magnitude) + } +}