Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/IntegerUtilities/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 0 additions & 40 deletions Sources/IntegerUtilities/GCD.swift

This file was deleted.

79 changes: 79 additions & 0 deletions Sources/IntegerUtilities/GreatestCommonDivisor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//===--- 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.
///
/// 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<T: BinaryInteger>(_ a: T, _ b: T) -> T {
let gcd = greatestCommonDivisorFullWidth(a, b)

guard let result = T(exactly: gcd) else {
fatalError("GCD (\(gcd)) is not representable as \(T.self).")
}

return result
}

/// Returns the [greatest common divisor][gcd] of `a` and `b`, along with a Boolean value indicating whether overflow occurred in the operation.
///
/// If both inputs are zero, the result is zero. If one input is zero, the
/// result is the absolute value of the other input.
///
/// - Returns: A tuple containing the result of the function along with a Boolean value indicating whether overflow occurred. If the overflow component is false, the partialValue component contains the entire result. If the
/// overflow component is true, an overflow occurred and the partialValue component contains the truncated result of the operation.
///
/// [gcd]: https://en.wikipedia.org/wiki/Greatest_common_divisor
@inlinable
public func greatestCommonDivisorReportingOverflow<T: BinaryInteger>(_ a: T, _ b: T) -> (partialValue: T, overflow: Bool) {
let gcd = greatestCommonDivisorFullWidth(a, b)

guard let result = T(exactly: gcd) else {
return (partialValue: T(truncatingIfNeeded: gcd), overflow: true)
}

return (partialValue: result, overflow: false)
}

/// 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 greatestCommonDivisorFullWidth<T: BinaryInteger>(_ a: T, _ b: T) -> T.Magnitude {
var x = a.magnitude
var y = b.magnitude

if x < y {
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
}
2 changes: 1 addition & 1 deletion Tests/IntegerUtilitiesTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
44 changes: 0 additions & 44 deletions Tests/IntegerUtilitiesTests/GCDTests.swift

This file was deleted.

103 changes: 103 additions & 0 deletions Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//===--- 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(Int.min, -1) == 1)
await #expect(processExitsWith: .failure) {
_ = gcd(0, Int.min)
}
await #expect(processExitsWith: .failure) {
_ = gcd(Int.min, 0)
}
await #expect(processExitsWith: .failure) {
_ = gcd(Int.min, Int.min)
}
}

@Test func `greatestCommonDivisorReportingOverflow()`() async throws {
#expect(greatestCommonDivisorReportingOverflow(0, 0) == (partialResult: 0, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(0, 1) == (partialResult: 1, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(1, 0) == (partialResult: 1, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(0, -1) == (partialResult: 1, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(-1, 0) == (partialResult: 1, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(1, 1) == (partialResult: 1, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(1, 2) == (partialResult: 1, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(2, 2) == (partialResult: 2, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(4, 2) == (partialResult: 2, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(6, 8) == (partialResult: 2, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(77, 91) == (partialResult: 7, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(24, -36) == (partialResult: 12, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(-24, -36) == (partialResult: 12, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(51, 34) == (partialResult: 17, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(64, 96) == (partialResult: 32, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(-64, 96) == (partialResult: 32, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(4*7*19, 27*25) == (partialResult: 1, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(16*315, 11*315) == (partialResult: 315, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(97*67*53*27*8, 83*67*53*9*32) == (partialResult: 67*53*9*8, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(Int.max, Int.max) == (partialResult: Int.max, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(Int.min, -1) == (partialResult: 1, overflow: false))
#expect(greatestCommonDivisorReportingOverflow(0, Int.min) == (partialResult: Int(truncatingIfNeeded: Int.min.magnitude), overflow: true))
#expect(greatestCommonDivisorReportingOverflow(Int.min, 0) == (partialResult: Int(truncatingIfNeeded: Int.min.magnitude), overflow: true))
#expect(greatestCommonDivisorReportingOverflow(Int.min, Int.min) == (partialResult: Int(truncatingIfNeeded: Int.min.magnitude), overflow: true))
}

@Test func `greatestCommonDivisorFullWidth()`() async throws {
#expect(greatestCommonDivisorFullWidth(0, 0) == 0)
#expect(greatestCommonDivisorFullWidth(0, 1) == 1)
#expect(greatestCommonDivisorFullWidth(1, 0) == 1)
#expect(greatestCommonDivisorFullWidth(0, -1) == 1)
#expect(greatestCommonDivisorFullWidth(-1, 0) == 1)
#expect(greatestCommonDivisorFullWidth(1, 1) == 1)
#expect(greatestCommonDivisorFullWidth(1, 2) == 1)
#expect(greatestCommonDivisorFullWidth(2, 2) == 2)
#expect(greatestCommonDivisorFullWidth(4, 2) == 2)
#expect(greatestCommonDivisorFullWidth(6, 8) == 2)
#expect(greatestCommonDivisorFullWidth(77, 91) == 7)
#expect(greatestCommonDivisorFullWidth(24, -36) == 12)
#expect(greatestCommonDivisorFullWidth(-24, -36) == 12)
#expect(greatestCommonDivisorFullWidth(51, 34) == 17)
#expect(greatestCommonDivisorFullWidth(64, 96) == 32)
#expect(greatestCommonDivisorFullWidth(-64, 96) == 32)
#expect(greatestCommonDivisorFullWidth(4*7*19, 27*25) == 1)
#expect(greatestCommonDivisorFullWidth(16*315, 11*315) == 315)
#expect(greatestCommonDivisorFullWidth(97*67*53*27*8, 83*67*53*9*32) == 67*53*9*8)
#expect(greatestCommonDivisorFullWidth(Int.max, Int.max) == Int.max)
#expect(greatestCommonDivisorFullWidth(Int.min, -1) == 1)
#expect(greatestCommonDivisorFullWidth(0, Int.min) == Int.min.magnitude)
#expect(greatestCommonDivisorFullWidth(Int.min, 0) == Int.min.magnitude)
#expect(greatestCommonDivisorFullWidth(Int.min, Int.min) == Int.min.magnitude)
}
}