From 4f00b87eacbde769e34b615e2e6e5d02a3153143 Mon Sep 17 00:00:00 2001 From: Jason Bobier Date: Sun, 10 Aug 2025 18:27:08 -0400 Subject: [PATCH 1/6] Updated GCD file names. Changed GCD.swift to GreatestCommonDivisor.swift. Chagned GCDTests.swift to GreatestCommonDivisorTests.swift --- Sources/IntegerUtilities/CMakeLists.txt | 2 +- .../IntegerUtilities/{GCD.swift => GreatestCommonDivisor.swift} | 2 +- Tests/IntegerUtilitiesTests/CMakeLists.txt | 2 +- .../{GCDTests.swift => GreatestCommonDivisorTests.swift} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename Sources/IntegerUtilities/{GCD.swift => GreatestCommonDivisor.swift} (94%) rename Tests/IntegerUtilitiesTests/{GCDTests.swift => GreatestCommonDivisorTests.swift} (100%) 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/GreatestCommonDivisor.swift similarity index 94% rename from Sources/IntegerUtilities/GCD.swift rename to Sources/IntegerUtilities/GreatestCommonDivisor.swift index 28e09bdb..6c1e3ea4 100644 --- a/Sources/IntegerUtilities/GCD.swift +++ b/Sources/IntegerUtilities/GreatestCommonDivisor.swift @@ -1,4 +1,4 @@ -//===--- GCD.swift --------------------------------------------*- swift -*-===// +//===--- GreatestCommonDivisor.swift --------------------------------------------*- swift -*-===// // // This source file is part of the Swift Numerics open source project // 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/GreatestCommonDivisorTests.swift similarity index 100% rename from Tests/IntegerUtilitiesTests/GCDTests.swift rename to Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift From d7ea57bb20dd3f223614c264f1e0d49ca24dd985 Mon Sep 17 00:00:00 2001 From: Jason Bobier Date: Sun, 10 Aug 2025 19:07:59 -0400 Subject: [PATCH 2/6] Updated gcd and tests. Updated gcd to return Magnitude. Updated tests to SwiftTesting. --- .../GreatestCommonDivisor.swift | 39 ++++++------ .../GreatestCommonDivisorTests.swift | 59 +++++++++---------- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/Sources/IntegerUtilities/GreatestCommonDivisor.swift b/Sources/IntegerUtilities/GreatestCommonDivisor.swift index 6c1e3ea4..940dc8d1 100644 --- a/Sources/IntegerUtilities/GreatestCommonDivisor.swift +++ b/Sources/IntegerUtilities/GreatestCommonDivisor.swift @@ -1,4 +1,4 @@ -//===--- GreatestCommonDivisor.swift --------------------------------------------*- swift -*-===// +//===--- GreatestCommonDivisor.swift --------------------------*- swift -*-===// // // This source file is part of the Swift Numerics open source project // @@ -14,27 +14,22 @@ /// 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).") +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/GreatestCommonDivisorTests.swift b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift index 6400732c..113952f3 100644 --- a/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift +++ b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift @@ -1,4 +1,5 @@ -//===--- GCDTests.swift ---------------------------------------*- swift -*-===// +//===--- GreatestCommonDivisorTests.swift ---------------------*- swift -*-===// +//===--- GreatestCommonDivisorTests.swift ---------------------------------------*- swift -*-===// // // This source file is part of the Swift Numerics open source project // @@ -11,34 +12,32 @@ //===----------------------------------------------------------------------===// import IntegerUtilities -import XCTest +import Testing -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) } - } +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) + } } From eaf7f8bf2a2365cbd4b11ba87cf4cfda106f2356 Mon Sep 17 00:00:00 2001 From: Jason Bobier Date: Mon, 11 Aug 2025 13:26:44 -0400 Subject: [PATCH 3/6] Removed extra line in header. --- Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift index 113952f3..ed088579 100644 --- a/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift +++ b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift @@ -1,5 +1,4 @@ //===--- GreatestCommonDivisorTests.swift ---------------------*- swift -*-===// -//===--- GreatestCommonDivisorTests.swift ---------------------------------------*- swift -*-===// // // This source file is part of the Swift Numerics open source project // From 7dbd05662c94333bfad2599d34330a6500454755 Mon Sep 17 00:00:00 2001 From: Jason Bobier Date: Wed, 13 Aug 2025 18:11:14 -0400 Subject: [PATCH 4/6] Updated copyright dates. --- Sources/IntegerUtilities/GreatestCommonDivisor.swift | 2 +- Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/IntegerUtilities/GreatestCommonDivisor.swift b/Sources/IntegerUtilities/GreatestCommonDivisor.swift index 940dc8d1..7321bff2 100644 --- a/Sources/IntegerUtilities/GreatestCommonDivisor.swift +++ b/Sources/IntegerUtilities/GreatestCommonDivisor.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2021-2024 Apple Inc. and the Swift Numerics project authors +// 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 diff --git a/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift index ed088579..f22a3d36 100644 --- a/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift +++ b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors +// 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 From da57b9dd0efde0e71b9ecd510a966f36aa40cb84 Mon Sep 17 00:00:00 2001 From: Jason Bobier Date: Sun, 17 Aug 2025 13:24:47 -0400 Subject: [PATCH 5/6] Added greatestCommonDivisor. --- .../GreatestCommonDivisor.swift | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Sources/IntegerUtilities/GreatestCommonDivisor.swift b/Sources/IntegerUtilities/GreatestCommonDivisor.swift index 7321bff2..8cb2c842 100644 --- a/Sources/IntegerUtilities/GreatestCommonDivisor.swift +++ b/Sources/IntegerUtilities/GreatestCommonDivisor.swift @@ -9,6 +9,32 @@ // //===----------------------------------------------------------------------===// +/// 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 { + if a.magnitude == 1 || b.magnitude == 1 { return 1 } + + let gcd = greatestCommonDivisor(a, b) + + // Try to convert result to T. + if let result = T(exactly: gcd) { return result } + // If that fails, produce a diagnostic. + fatalError("GCD (\(gcd)) is not representable as \(T.self).") +} + + /// The [greatest common divisor][gcd] of `a` and `b`. /// /// If both inputs are zero, the result is zero. If one input is zero, the @@ -16,7 +42,7 @@ /// /// [gcd]: https://en.wikipedia.org/wiki/Greatest_common_divisor @inlinable -public func gcd(_ a: T, _ b: T) -> T.Magnitude { +public func greatestCommonDivisor(_ a: T, _ b: T) -> T.Magnitude { var x = a var y = b From cfd389e3c574152ad3602dc0d107f5eb42b38ac3 Mon Sep 17 00:00:00 2001 From: Jason Bobier Date: Wed, 20 Aug 2025 16:29:56 -0400 Subject: [PATCH 6/6] Added gcd, greatestCommonDivisorReportingOverflow, and greatestCommonDivisorFullWidth and updated tests. --- .../GreatestCommonDivisor.swift | 40 ++++++++--- .../GreatestCommonDivisorTests.swift | 69 +++++++++++++++++-- 2 files changed, 94 insertions(+), 15 deletions(-) diff --git a/Sources/IntegerUtilities/GreatestCommonDivisor.swift b/Sources/IntegerUtilities/GreatestCommonDivisor.swift index 8cb2c842..3104c7ef 100644 --- a/Sources/IntegerUtilities/GreatestCommonDivisor.swift +++ b/Sources/IntegerUtilities/GreatestCommonDivisor.swift @@ -24,16 +24,34 @@ /// [gcd]: https://en.wikipedia.org/wiki/Greatest_common_divisor @inlinable public func gcd(_ a: T, _ b: T) -> T { - if a.magnitude == 1 || b.magnitude == 1 { return 1 } + let gcd = greatestCommonDivisorFullWidth(a, b) - let gcd = greatestCommonDivisor(a, b) + guard let result = T(exactly: gcd) else { + fatalError("GCD (\(gcd)) is not representable as \(T.self).") + } - // Try to convert result to T. - if let result = T(exactly: gcd) { return result } - // If that fails, produce a diagnostic. - 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(_ 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`. /// @@ -42,11 +60,11 @@ public func gcd(_ a: T, _ b: T) -> T { /// /// [gcd]: https://en.wikipedia.org/wiki/Greatest_common_divisor @inlinable -public func greatestCommonDivisor(_ a: T, _ b: T) -> T.Magnitude { - var x = a - var y = b +public func greatestCommonDivisorFullWidth(_ a: T, _ b: T) -> T.Magnitude { + var x = a.magnitude + var y = b.magnitude - if x.magnitude < y.magnitude { + if x < y { swap(&x, &y) } @@ -57,5 +75,5 @@ public func greatestCommonDivisor(_ a: T, _ b: T) -> T.Magnitu (x, y) = (y, x % y) } - return x.magnitude + return x } diff --git a/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift index f22a3d36..5745e072 100644 --- a/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift +++ b/Tests/IntegerUtilitiesTests/GreatestCommonDivisorTests.swift @@ -14,7 +14,7 @@ import IntegerUtilities import Testing struct `Greatest Common Divisor Tests` { - @Test func `gcd`() async throws { + @Test func `gcd()`() async throws { #expect(gcd(0, 0) == 0) #expect(gcd(0, 1) == 1) #expect(gcd(1, 0) == 1) @@ -35,8 +35,69 @@ struct `Greatest Common Divisor Tests` { #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) + #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) } }