Skip to content

Commit

Permalink
[NBKCoreKit] Extended GCD with iteration count.
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Oct 29, 2023
1 parent cbabf10 commit 02cd5b3
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,14 @@ extension NBK.IntegerDescription {
//=----------------------------------=
backwards: while index < split.quotient {
let chunk = UnsafeBufferPointer(rebasing: NBK.removeSuffix(from: &digits, count: radix.exponent()))
guard let element: UInt = self.truncating(digits: chunk, radix: radix.base()) else { return }
guard let element: UInt = self.truncating(digits: chunk, radix: radix.base()) else { return }
words.baseAddress!.advanced(by: index).initialize(to: element)
words.formIndex(after: &index)
}

backwards: if split.remainder.isMoreThanZero {
let chunk = UnsafeBufferPointer(rebasing: NBK.removeSuffix(from: &digits, count: split.remainder))
guard let element: UInt = self.truncating(digits: chunk, radix: radix.base()) else { return }
guard let element: UInt = self.truncating(digits: chunk, radix: radix.base()) else { return }
words.baseAddress!.advanced(by: index).initialize(to: element)
words.formIndex(after: &index)
}
Expand Down Expand Up @@ -263,14 +263,14 @@ extension NBK.IntegerDescription {
//=----------------------------------=
forwards: if split.remainder.isMoreThanZero {
let chunk = UnsafeBufferPointer(rebasing: NBK.removePrefix(from: &digits, count: split.remainder))
guard let element: UInt = self.truncating(digits: chunk, radix: radix.base()) else { return }
guard let element: UInt = self.truncating(digits: chunk, radix: radix.base()) else { return }
words.baseAddress!.advanced(by: index).initialize(to: element)
words.formIndex(after: &index)
}

forwards: while index < count {
let chunk = UnsafeBufferPointer(rebasing: NBK.removePrefix(from: &digits, count: radix.exponent()))
guard let element: UInt = self.truncating(digits: chunk, radix: radix.base()) else { return }
guard let element: UInt = self.truncating(digits: chunk, radix: radix.base()) else { return }
words.baseAddress!.advanced(by: index).initialize(to: NBK.SUISS.multiply(&words[..<index], by: radix.power, add: element))
words.formIndex(after: &index)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ extension NBK.ProperBinaryInteger where Integer: NBKSignedInteger {
/// ### Result
///
/// ```swift
/// precondition(0 <= result && result <= T.min.magnitude)
/// precondition(0 ... T.min.magnitude ~= result)
/// ```
///
/// - Note: The GCD of `0` and `0` is `0`.
///
/// - Note: The GCD of `0` and `X` is `X`.
///
/// - Note: The GCD of `Int.min` and `Int.min` is `Int.min.magnitude`.
///
/// ### Bézout's identity
Expand All @@ -62,26 +64,34 @@ extension NBK.ProperBinaryInteger where Integer: NBKSignedInteger {
/// ### Quotients of dividing by GCD
///
/// ```swift
/// guard result != 00000 else { return }
/// guard result <= T.max else { return }
///
/// guard !result.isZero, result <= T.max else { return }
/// precondition(lhsQuotient == lhs / T(result))
/// precondition(rhsQuotient == rhs / T(result))
/// ```
///
/// ### Iteration
///
/// ```swift
/// let lhsCoefficientSign = lhs.isLessThanZero != iteration.isOdd
/// let rhsCoefficientSign = rhs.isLessThanZero == iteration.isOdd
/// ```
///
@inlinable public static func greatestCommonDivisorByExtendedEuclideanAlgorithm(of lhs: Integer, and rhs: Integer)
-> (result: Integer.Magnitude, lhsCoefficient: Integer, rhsCoefficient: Integer, lhsQuotient: Integer, rhsQuotient: Integer) {
-> (result: Integer.Magnitude, lhsCoefficient: Integer, rhsCoefficient: Integer, lhsQuotient: Integer, rhsQuotient: Integer, iteration: Integer.Magnitude) {
//=--------------------------------------=
let lhsIsLessThanZero: Bool = lhs.isLessThanZero
let rhsIsLessThanZero: Bool = rhs.isLessThanZero
//=--------------------------------------=
let unsigned = Magnitude.greatestCommonDivisorByExtendedEuclideanAlgorithm(of: lhs.magnitude, and: rhs.magnitude)
let odd = unsigned.iteration.isOdd as Bool
//=--------------------------------------=
return (result: unsigned.result,
lhsCoefficient: Integer(sign: lhsIsLessThanZero != unsigned.iterationIsOdd ? .minus : .plus, magnitude: unsigned.lhsCoefficient)!,
rhsCoefficient: Integer(sign: rhsIsLessThanZero == unsigned.iterationIsOdd ? .minus : .plus, magnitude: unsigned.rhsCoefficient)!,
lhsQuotient: Integer(sign: lhsIsLessThanZero /*----------------------*/ ? .minus : .plus, magnitude: unsigned.lhsQuotient )!,
rhsQuotient: Integer(sign: rhsIsLessThanZero /*----------------------*/ ? .minus : .plus, magnitude: unsigned.rhsQuotient )!)
return (
result: unsigned.result as Integer.Magnitude,
lhsCoefficient: Integer(sign: lhsIsLessThanZero != odd ? .minus : .plus, magnitude: unsigned.lhsCoefficient)!,
rhsCoefficient: Integer(sign: rhsIsLessThanZero == odd ? .minus : .plus, magnitude: unsigned.rhsCoefficient)!,
lhsQuotient: Integer(sign: lhsIsLessThanZero /*--*/ ? .minus : .plus, magnitude: unsigned.lhsQuotient )!,
rhsQuotient: Integer(sign: rhsIsLessThanZero /*--*/ ? .minus : .plus, magnitude: unsigned.rhsQuotient )!,
iteration: unsigned.iteration as Integer.Magnitude)
}
}

Expand Down Expand Up @@ -136,15 +146,17 @@ extension NBK.ProperBinaryInteger where Integer: NBKUnsignedInteger {
/// ### Result
///
/// ```swift
/// precondition(0 <= result && result <= T.max)
/// precondition(0 ... T.max ~= result)
/// ```
///
/// - Note: The GCD of `0` and `0` is `0`.
///
/// - Note: The GCD of `0` and `X` is `X`.
///
/// ### Bézout's identity (unsigned)
///
/// ```swift
/// if !iterationIsOdd {
/// if !iteration.isOdd {
/// precondition(result == (lhs &* lhsCoefficient &- rhs &* rhsCoefficient))
/// } else {
/// precondition(result == (rhs &* rhsCoefficient &- lhs &* lhsCoefficient))
Expand All @@ -154,36 +166,34 @@ extension NBK.ProperBinaryInteger where Integer: NBKUnsignedInteger {
/// ### Quotients of dividing by GCD
///
/// ```swift
/// guard result != 0000000000 else { return }
/// guard !result.isZero else { return }
/// precondition(lhsQuotient == lhs / result)
/// precondition(rhsQuotient == rhs / result)
/// ```
///
/// ### Loop count result
/// ### Iteration
///
/// ```swift
/// let lhsCoefficientSign = lhs.isLessThanZero != iterationIsOdd
/// let rhsCoefficientSign = rhs.isLessThanZero == iterationIsOdd
/// let lhsCoefficientSign = lhs.isLessThanZero != iteration.isOdd
/// let rhsCoefficientSign = rhs.isLessThanZero == iteration.isOdd
/// ```
///
@inlinable public static func greatestCommonDivisorByExtendedEuclideanAlgorithm(of lhs: Integer, and rhs: Integer)
-> (result: Integer, lhsCoefficient: Integer, rhsCoefficient: Integer, lhsQuotient: Integer, rhsQuotient: Integer, iterationIsOdd: Bool) {
-> (result: Integer, lhsCoefficient: Integer, rhsCoefficient: Integer, lhsQuotient: Integer, rhsQuotient: Integer, iteration: Integer) {
//=--------------------------------------=
var (a, b) = (lhs, rhs) as (Integer,Integer)
var (c, d) = (001, 000) as (Integer,Integer)
var (e, f) = (000, 001) as (Integer,Integer)
var iterationIsOdd = (((((((((false)))))))))
var iteration = 0000000 as (((((Integer)))))
//=--------------------------------------=
while !b.isZero {

iterationIsOdd.toggle()
let division = a.quotientAndRemainder(dividingBy: b)

(a, b) = (b, division.remainder)
(c, d) = (d, division.quotient * d + c)
(e, f) = (f, division.quotient * f + e)
iteration += 000000001 as Integer.Digit
}
//=--------------------------------------=
return (result: a, lhsCoefficient: c, rhsCoefficient: e, lhsQuotient: f, rhsQuotient: d, iterationIsOdd: iterationIsOdd)
return (result: a, lhsCoefficient: c, rhsCoefficient: e, lhsQuotient: f, rhsQuotient: d, iteration: iteration)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,26 +117,39 @@ final class NBKProperBinaryIntegerTestsOnGreatestCommonDivisorByExtendedEuclidea

func testWrappingBézoutIdentityWorksForAllPairsOfInt8() {
self.continueAfterFailure = false
var maxIteration: UInt8 = 0000000

for lhs in Int8.min ... Int8.max {
for rhs in Int8.min ... Int8.max {
let extended = NBK.PBI.greatestCommonDivisorByExtendedEuclideanAlgorithm(of: lhs, and: rhs)
maxIteration = Swift.max(maxIteration, extended.iteration)
XCTAssertEqual(Int8(bitPattern: extended.result), lhs &* extended.lhsCoefficient &+ rhs &* extended.rhsCoefficient)
XCTAssertEqual(extended.lhsCoefficient < 0, (extended.lhsCoefficient != 0) && (lhs < 0) != extended.iteration.isOdd)
XCTAssertEqual(extended.rhsCoefficient < 0, (extended.rhsCoefficient != 0) && (rhs < 0) == extended.iteration.isOdd)
}
}

XCTAssertEqual(maxIteration, 10)
}

func testWrappingBézoutIdentityWorksForAllPairsOfUInt8() {
self.continueAfterFailure = false
var maxIteration: UInt8 = 0000000

for lhs in UInt8.min ... UInt8.max {
for rhs in UInt8.min ... UInt8.max {
let extended = NBK.PBI.greatestCommonDivisorByExtendedEuclideanAlgorithm(of: lhs, and: rhs)
if !extended.iterationIsOdd {
maxIteration = Swift.max(maxIteration, extended.iteration)

if !extended.iteration.isOdd {
XCTAssertEqual(extended.result, lhs &* extended.lhsCoefficient &- rhs &* extended.rhsCoefficient)
} else {
XCTAssertEqual(extended.result, rhs &* extended.rhsCoefficient &- lhs &* extended.lhsCoefficient)
}
}
}

XCTAssertEqual(maxIteration, 12)
}
}

Expand Down Expand Up @@ -192,11 +205,14 @@ file: StaticString = #file, line: UInt = #line) {
// MARK: + Algorithms
//=----------------------------------------------------------------------------=

private func NBKAssertGreatestCommonDivisorByBinaryAlgorithm<T: NBKBinaryInteger>(
private func NBKAssertGreatestCommonDivisorByBinaryAlgorithm<T: NBKBinaryInteger & NBKFixedWidthInteger>(
_ lhs: T, _ rhs: T, _ result: T.Magnitude, _ overflow: Bool,
file: StaticString = #file, line: UInt = #line) {
//=------------------------------------------=
XCTAssertEqual(NBK.PBI.greatestCommonDivisorByBinaryAlgorithm(of: lhs, and: rhs), result, file: file, line: line)
let binary = NBK.PBI.greatestCommonDivisorByBinaryAlgorithm(of: lhs, and: rhs)
//=------------------------------------------=
XCTAssertEqual(binary, result, file: file, line: line)
XCTAssertEqual(T.isSigned && binary == T.min.magnitude, overflow, file: file, line: line)
}

private func NBKAssertGreatestCommonDivisorByExtendedEuclideanAlgorithmAsSigned<T: NBKSignedInteger & NBKFixedWidthInteger>(
Expand All @@ -206,14 +222,14 @@ file: StaticString = #file, line: UInt = #line) {
let extended = NBK.PBI.greatestCommonDivisorByExtendedEuclideanAlgorithm(of: lhs, and: rhs)
//=------------------------------------------=
XCTAssertEqual(extended.result, result, file: file, line: line)
XCTAssertEqual(extended.result == T.min.magnitude, overflow, file: file, line: line)
XCTAssertEqual(lhs &* extended.lhsCoefficient &+ rhs &* extended.rhsCoefficient, T(bitPattern: result), file: file, line: line)
if overflow {

if result == T.min.magnitude {
let values = [lhs, rhs]
XCTAssert(1 <= values.filter({ $0 == T.min }).count, file: file, line: line)
XCTAssert(2 == values.filter({ $0 == T.min }).count + values.filter(\.isZero).count, file: file, line: line)

XCTAssertEqual(T.min.magnitude, /*-----------------*/ extended.result, file: file, line: line)
XCTAssertEqual(lhs == T.min && rhs != T.min ? -1 : 0, extended.lhsCoefficient, file: file, line: line)
XCTAssertEqual(rhs == T.min /*-----------*/ ? -1 : 0, extended.rhsCoefficient, file: file, line: line)
XCTAssertEqual(lhs == T.min /*-----------*/ ? -1 : 0, extended.lhsQuotient, file: file, line: line)
Expand Down Expand Up @@ -253,7 +269,7 @@ file: StaticString = #file, line: UInt = #line) {
XCTAssertEqual(0, extended.rhsCoefficient, file: file, line: line)
}

if !extended.iterationIsOdd {
if !extended.iteration.isOdd {
XCTAssertEqual(result, lhs &* extended.lhsCoefficient &- rhs &* extended.rhsCoefficient, file: file, line: line)
} else {
XCTAssertEqual(result, rhs &* extended.rhsCoefficient &- lhs &* extended.lhsCoefficient, file: file, line: line)
Expand Down

0 comments on commit 02cd5b3

Please sign in to comment.