Skip to content

Commit fa7db57

Browse files
Merge pull request #285 from stephentyrone/euclidean-gcd
Adopt the Euclidean algorithm for GCD.
2 parents 57ad9c5 + 907430b commit fa7db57

File tree

1 file changed

+16
-29
lines changed

1 file changed

+16
-29
lines changed

Sources/IntegerUtilities/GCD.swift

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
//
33
// This source file is part of the Swift Numerics open source project
44
//
5-
// Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors
5+
// Copyright (c) 2021-2024 Apple Inc. and the Swift Numerics project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
//
1010
//===----------------------------------------------------------------------===//
1111

12-
/// The greatest common divisor of `a` and `b`.
12+
/// The [greatest common divisor][gcd] of `a` and `b`.
1313
///
1414
/// If both inputs are zero, the result is zero. If one input is zero, the
1515
/// result is the absolute value of the other input.
@@ -21,33 +21,20 @@
2121
/// gcd(Int.min, Int.min) // Overflow error
2222
/// gcd(Int.min, 0) // Overflow error
2323
///
24-
/// [wiki]: https://en.wikipedia.org/wiki/Greatest_common_divisor
24+
/// [gcd]: https://en.wikipedia.org/wiki/Greatest_common_divisor
2525
@inlinable
2626
public func gcd<T: BinaryInteger>(_ a: T, _ b: T) -> T {
27-
var x = a.magnitude
28-
var y = b.magnitude
29-
30-
if x == 0 { return T(y) }
31-
if y == 0 { return T(x) }
32-
33-
let xtz = x.trailingZeroBitCount
34-
let ytz = y.trailingZeroBitCount
35-
36-
y >>= ytz
37-
38-
// The binary GCD algorithm
39-
//
40-
// After the right-shift in the loop, both x and y are odd. Each pass removes
41-
// at least one low-order bit from the larger of the two, so the number of
42-
// iterations is bounded by the sum of the bit-widths of the inputs.
43-
//
44-
// A tighter bound is the maximum bit-width of the inputs, which is achieved
45-
// by odd numbers that sum to a power of 2, though the proof is more involved.
46-
repeat {
47-
x >>= x.trailingZeroBitCount
48-
if x < y { swap(&x, &y) }
49-
x -= y
50-
} while x != 0
51-
52-
return T(y << min(xtz, ytz))
27+
var x = a
28+
var y = b
29+
if x.magnitude < y.magnitude { swap(&x, &y) }
30+
// Avoid overflow when x = signed min, y = -1.
31+
if y.magnitude == 1 { return 1 }
32+
// Euclidean algorithm for GCD. It's worth using Lehmer instead for larger
33+
// integer types, but for now this is good and dead-simple and faster than
34+
// the other obvious choice, the binary algorithm.
35+
while y != 0 { (x, y) = (y, x%y) }
36+
// Try to convert result to T.
37+
if let result = T(exactly: x.magnitude) { return result }
38+
// If that fails, produce a diagnostic.
39+
fatalError("GCD (\(x)) is not representable as \(T.self).")
5340
}

0 commit comments

Comments
 (0)