From 9ea179f9743a610f4ddd1d4106a90adca90d0351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Bystr=C3=B6m=20Ericsson?= Date: Tue, 31 Oct 2023 15:29:36 +0100 Subject: [PATCH] [NBKCoreKit] Modular multiplicative inverse. --- ...Integer+ModularMultiplicativeInverse.swift | 23 ++++---- ...Integer+ModularMultiplicativeInverse.swift | 52 +++++++++++++++++++ 2 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 Tests/NBKCoreKitBenchmarks/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift diff --git a/Sources/NBKCoreKit/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift b/Sources/NBKCoreKit/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift index 93022834..e8dedd8f 100644 --- a/Sources/NBKCoreKit/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift +++ b/Sources/NBKCoreKit/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift @@ -21,18 +21,16 @@ extension NBK.ProperBinaryInteger { /// /// - Returns: A value from `0` to `modulus` or `nil`. /// - @inlinable public static func modularMultiplicativeInverse(of integer: Integer, mod modulus: Integer) -> Integer? { + @inlinable public static func modularMultiplicativeInverse(of lhs: Integer, mod rhs: Integer) -> Integer? { //=--------------------------------------= - let lhsIsLessThanZero = integer.isLessThanZero - let rhsIsLessThanZero = modulus.isLessThanZero + let lhsIsLessThanZero = lhs.isLessThanZero + let rhsIsLessThanZero = rhs.isLessThanZero //=--------------------------------------= if rhsIsLessThanZero { return nil } //=--------------------------------------= - guard let unsigned = Magnitude.modularMultiplicativeInverse(of: integer.magnitude, mod: modulus.magnitude) else { return nil } - //=--------------------------------------= - var inverse = Integer(sign: NBK.sign(lhsIsLessThanZero), magnitude: unsigned)! - if inverse.isLessThanZero { inverse += modulus } - return inverse as Integer as Integer as Integer + guard let magnitude = Magnitude.modularMultiplicativeInverse( + sign: NBK.sign(lhsIsLessThanZero), magnitude: lhs.magnitude, mod: rhs.magnitude) else { return nil } + return Integer(sign: NBK.Sign.plus, magnitude: magnitude)! } } @@ -46,23 +44,24 @@ extension NBK.ProperBinaryInteger where Integer: NBKUnsignedInteger { // MARK: Utilities //=------------------------------------------------------------------------= - /// Returns the modular multiplicative inverse of `integer` modulo `modulus`, if it exists. + /// Returns the modular multiplicative inverse of `sign` and `magnitude` modulo `modulus`, if it exists. /// /// - Returns: A value from `0` to `modulus` or `nil`. /// - @inlinable public static func modularMultiplicativeInverse(of integer: Integer, mod modulus: Integer) -> Integer? { + @inlinable public static func modularMultiplicativeInverse(sign: NBK.Sign, magnitude: Integer, mod modulus: Integer) -> Integer? { //=--------------------------------------= switch modulus.compared(to: 1 as Integer.Digit) { case 1: break; case 0: return Integer.zero default: return nil } //=--------------------------------------= - let extended = self.greatestCommonDivisorByEuclideanAlgorithm10(of: integer, and: modulus) + let extended = self.greatestCommonDivisorByEuclideanAlgorithm10(of: magnitude, and: modulus) //=--------------------------------------= guard extended.result.compared(to: 1 as Integer.Digit).isZero else { return nil // the arguments must be coprime } //=--------------------------------------= - return extended.iteration.isOdd ? modulus - extended.lhsCoefficient : extended.lhsCoefficient + Swift.assert(extended.lhsCoefficient.isMoreThanZero) + return (sign == .minus) == extended.iteration.isEven ? modulus - extended.lhsCoefficient : extended.lhsCoefficient } } diff --git a/Tests/NBKCoreKitBenchmarks/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift b/Tests/NBKCoreKitBenchmarks/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift new file mode 100644 index 00000000..1d3f87b0 --- /dev/null +++ b/Tests/NBKCoreKitBenchmarks/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift @@ -0,0 +1,52 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Numberick open source project. +// +// Copyright (c) 2023 Oscar Byström Ericsson +// Licensed under Apache License, Version 2.0 +// +// See http://www.apache.org/licenses/LICENSE-2.0 for license information. +//=----------------------------------------------------------------------------= + +#if !DEBUG + +import NBKCoreKit +import XCTest + +private typealias W = [UInt] +private typealias X = [UInt64] +private typealias Y = [UInt32] + +//*============================================================================* +// MARK: * NBK x Proper Binary Integer x Modular Multiplicative Inverse +//*============================================================================* + +final class NBKProperBinaryIntegerBenchmarksOnModularMultiplicativeInverse: XCTestCase { + + typealias T = NBK.PBI + + //=------------------------------------------------------------------------= + // MARK: Tests x Binary Algorithm + //=------------------------------------------------------------------------= + + func testInt8s() { + for _ in 0 ..< 50 { + for lhs in Int8.min ... Int8.max { + for rhs in Int8.min ... Int8.max { + NBK.blackHole(T.modularMultiplicativeInverse(of: lhs, mod: rhs)) + } + } + } + } + + func testUInt8s() { + for _ in 0 ..< 50 { + for lhs in UInt8.min ... UInt8.max { + for rhs in UInt8.min ... UInt8.max { + NBK.blackHole(T.modularMultiplicativeInverse(of: lhs, mod: rhs)) + } + } + } + } +} + +#endif