Skip to content

Commit 9d84cef

Browse files
Merge pull request #189 from NevinBR/patch-6
GCD function for BinaryInteger
2 parents b505d3c + f2cfa8d commit 9d84cef

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

Sources/IntegerUtilities/GCD.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===--- GCD.swift --------------------------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift Numerics open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
/// The greatest common divisor of `a` and `b`.
13+
///
14+
/// If both inputs are zero, the result is zero. If one input is zero, the
15+
/// result is the absolute value of the other input.
16+
///
17+
/// The result must be representable within its type. In particular, the gcd
18+
/// of a signed, fixed-width integer type's minimum with itself (or zero)
19+
/// cannot be represented, and results in a trap.
20+
///
21+
/// gcd(Int.min, Int.min) // Overflow error
22+
/// gcd(Int.min, 0) // Overflow error
23+
///
24+
/// [wiki]: https://en.wikipedia.org/wiki/Greatest_common_divisor
25+
@inlinable
26+
public func gcd<T: BinaryInteger>(_ a: T, _ b: T) -> T {
27+
var x = a.magnitude
28+
var y = b.magnitude
29+
30+
if y == 0 { return T(x) }
31+
32+
let xtz = x.trailingZeroBitCount
33+
let ytz = y.trailingZeroBitCount
34+
35+
y >>= ytz
36+
37+
// The binary GCD algorithm
38+
//
39+
// After the right-shift in the loop, both x and y are odd. Each pass removes
40+
// at least one low-order bit from the larger of the two, so the number of
41+
// iterations is bounded by the sum of the bit-widths of the inputs.
42+
while x != 0 {
43+
x >>= x.trailingZeroBitCount
44+
if x < y { swap(&x, &y) }
45+
x -= y
46+
}
47+
48+
return T(y << min(xtz, ytz))
49+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===--- GCDTests.swift ---------------------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import IntegerUtilities
14+
import XCTest
15+
16+
final class IntegerUtilitiesGCDTests: XCTestCase {
17+
func testGCDInt() {
18+
XCTAssertEqual(gcd(0, 0), 0)
19+
XCTAssertEqual(gcd(0, 1), 1)
20+
XCTAssertEqual(gcd(1, 0), 1)
21+
XCTAssertEqual(gcd(0, -1), 1)
22+
XCTAssertEqual(gcd(1, 1), 1)
23+
XCTAssertEqual(gcd(1, 2), 1)
24+
XCTAssertEqual(gcd(2, 2), 2)
25+
XCTAssertEqual(gcd(4, 2), 2)
26+
XCTAssertEqual(gcd(6, 8), 2)
27+
XCTAssertEqual(gcd(77, 91), 7)
28+
XCTAssertEqual(gcd(24, -36), 12)
29+
XCTAssertEqual(gcd(-24, -36), 12)
30+
XCTAssertEqual(gcd(51, 34), 17)
31+
XCTAssertEqual(gcd(64, 96), 32)
32+
XCTAssertEqual(gcd(-64, 96), 32)
33+
XCTAssertEqual(gcd(4*7*19, 27*25), 1)
34+
XCTAssertEqual(gcd(16*315, 11*315), 315)
35+
XCTAssertEqual(gcd(97*67*53*27*8, 83*67*53*9*32), 67*53*9*8)
36+
XCTAssertEqual(gcd(Int.min, 2), 2)
37+
38+
// TODO: Enable these when version compatibility allows.
39+
//
40+
// XCTExpectFailure{ gcd(0, Int.min) }
41+
// XCTExpectFailure{ gcd(Int.min, 0) }
42+
// XCTExpectFailure{ gcd(Int.min, Int.min) }
43+
}
44+
}

0 commit comments

Comments
 (0)