-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[NBKCoreKit] Modular multiplicative inverse.
- Loading branch information
Showing
3 changed files
with
223 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
Sources/NBKCoreKit/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
//=----------------------------------------------------------------------------= | ||
// 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. | ||
//=----------------------------------------------------------------------------= | ||
|
||
//*============================================================================* | ||
// MARK: * NBK x Proper Binary Integer x Modular Mul. Inverse | ||
//*============================================================================* | ||
|
||
extension NBK.ProperBinaryInteger { | ||
|
||
//=------------------------------------------------------------------------= | ||
// MARK: Utilities | ||
//=------------------------------------------------------------------------= | ||
|
||
/// Returns the modular multiplicative inverse of `integer` 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? { | ||
//=--------------------------------------= | ||
let lhsIsLessThanZero = integer.isLessThanZero | ||
let rhsIsLessThanZero = modulus.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 | ||
} | ||
} | ||
|
||
//*============================================================================* | ||
// MARK: * NBK x Proper Binary Integer x Modular Mul. Inverse x Unsigned | ||
//*============================================================================* | ||
|
||
extension NBK.ProperBinaryInteger where Integer: NBKUnsignedInteger { | ||
|
||
//=------------------------------------------------------------------------= | ||
// MARK: Utilities | ||
//=------------------------------------------------------------------------= | ||
|
||
/// Returns the modular multiplicative inverse of `integer` 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? { | ||
//=--------------------------------------= | ||
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) | ||
//=--------------------------------------= | ||
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 | ||
} | ||
} |
151 changes: 151 additions & 0 deletions
151
Tests/NBKCoreKitTests/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
//=----------------------------------------------------------------------------= | ||
// 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 | ||
|
||
//*============================================================================* | ||
// MARK: * NBK x Proper Binary Integer x Modular Mul. Inverse | ||
//*============================================================================* | ||
|
||
final class NBKProperBinaryIntegerTestsOnModularMultiplicativeInverse: XCTestCase { | ||
|
||
typealias T = NBK.PBI | ||
|
||
//=------------------------------------------------------------------------= | ||
// MARK: Tests | ||
//=------------------------------------------------------------------------= | ||
|
||
func testSomeSmallPrimes() { | ||
NBKAssertModularMultiplicativeInverse( 2 as Int, 31 as Int, 16 as Int) // primes: 1, 11 | ||
NBKAssertModularMultiplicativeInverse( 3 as Int, 79 as Int, 53 as Int) // primes: 2, 22 | ||
NBKAssertModularMultiplicativeInverse( 5 as Int, 137 as Int, 55 as Int) // primes: 3, 33 | ||
NBKAssertModularMultiplicativeInverse( 7 as Int, 193 as Int, 138 as Int) // primes: 4, 44 | ||
NBKAssertModularMultiplicativeInverse( 11 as Int, 257 as Int, 187 as Int) // primes: 5, 55 | ||
NBKAssertModularMultiplicativeInverse( 13 as Int, 317 as Int, 122 as Int) // primes: 6, 66 | ||
NBKAssertModularMultiplicativeInverse( 17 as Int, 389 as Int, 206 as Int) // primes: 7, 77 | ||
NBKAssertModularMultiplicativeInverse( 19 as Int, 457 as Int, 433 as Int) // primes: 8, 88 | ||
NBKAssertModularMultiplicativeInverse( 23 as Int, 523 as Int, 91 as Int) // primes: 9, 99 | ||
|
||
NBKAssertModularMultiplicativeInverse( 2 as Int, 607 as Int, 304 as Int) // primes: 1, 111 | ||
NBKAssertModularMultiplicativeInverse( 3 as Int, 1399 as Int, 933 as Int) // primes: 2, 222 | ||
NBKAssertModularMultiplicativeInverse( 5 as Int, 2239 as Int, 448 as Int) // primes: 3, 333 | ||
NBKAssertModularMultiplicativeInverse( 7 as Int, 3119 as Int, 2228 as Int) // primes: 4, 444 | ||
NBKAssertModularMultiplicativeInverse( 11 as Int, 4019 as Int, 2923 as Int) // primes: 5, 555 | ||
NBKAssertModularMultiplicativeInverse( 13 as Int, 4973 as Int, 4208 as Int) // primes: 6, 666 | ||
NBKAssertModularMultiplicativeInverse( 17 as Int, 5903 as Int, 1389 as Int) // primes: 7, 777 | ||
NBKAssertModularMultiplicativeInverse( 19 as Int, 6907 as Int, 6180 as Int) // primes: 8, 888 | ||
NBKAssertModularMultiplicativeInverse( 23 as Int, 7907 as Int, 4813 as Int) // primes: 9, 999 | ||
|
||
NBKAssertModularMultiplicativeInverse( 31 as Int, 607 as Int, 235 as Int) // primes: 11, 111 | ||
NBKAssertModularMultiplicativeInverse( 79 as Int, 1399 as Int, 974 as Int) // primes: 22, 222 | ||
NBKAssertModularMultiplicativeInverse(137 as Int, 2239 as Int, 1667 as Int) // primes: 33, 333 | ||
NBKAssertModularMultiplicativeInverse(193 as Int, 3119 as Int, 905 as Int) // primes: 44, 444 | ||
NBKAssertModularMultiplicativeInverse(257 as Int, 4019 as Int, 2377 as Int) // primes: 55, 555 | ||
NBKAssertModularMultiplicativeInverse(317 as Int, 4973 as Int, 4722 as Int) // primes: 66, 666 | ||
NBKAssertModularMultiplicativeInverse(389 as Int, 5903 as Int, 2170 as Int) // primes: 77, 777 | ||
NBKAssertModularMultiplicativeInverse(457 as Int, 6907 as Int, 4383 as Int) // primes: 88, 888 | ||
NBKAssertModularMultiplicativeInverse(523 as Int, 7907 as Int, 2933 as Int) // primes: 99, 999 | ||
} | ||
|
||
//=------------------------------------------------------------------------= | ||
// MARK: Tests x Simple Cases | ||
//=------------------------------------------------------------------------= | ||
|
||
func testInverseModuloZeroIsNil() { | ||
for x in 0 as Int ..< 10 { | ||
NBKAssertModularMultiplicativeInverse(x as Int, 0 as Int, nil as Int?) | ||
} | ||
} | ||
|
||
func testInverseModuloOneIsZero() { | ||
for x in 0 as Int ..< 10 { | ||
NBKAssertModularMultiplicativeInverse(x as Int, 1 as Int, 000 as Int?) | ||
} | ||
} | ||
|
||
func testZeroHasNoModularInverseForEachModulusGreaterThanOne() { | ||
for x in 2 as Int ..< 10 { | ||
NBKAssertModularMultiplicativeInverse(0 as Int, x as Int, nil as Int?) | ||
} | ||
} | ||
|
||
func testOneIsAlwaysItsOwnInverseForEachModulusGreaterThanOne() { | ||
for x in 2 as Int ..< 10 { | ||
NBKAssertModularMultiplicativeInverse(1 as Int, x as Int, 001 as Int?) | ||
} | ||
} | ||
|
||
func testThereIsNoInverseForAnyModulusLessThanOne() { | ||
for x in -10 as Int ..< 1 { | ||
NBKAssertModularMultiplicativeInverse(1 as Int, x as Int, nil as Int?) | ||
} | ||
} | ||
|
||
func testInverseIsNilWhenInputsAreNotCoprime() { | ||
NBKAssertModularMultiplicativeInverse(1 * 1 * 1 as Int, 2 * 3 * 5 as Int, 001 as Int?) | ||
NBKAssertModularMultiplicativeInverse(2 * 1 * 1 as Int, 2 * 3 * 5 as Int, nil as Int?) | ||
NBKAssertModularMultiplicativeInverse(1 * 3 * 1 as Int, 2 * 3 * 5 as Int, nil as Int?) | ||
NBKAssertModularMultiplicativeInverse(1 * 1 * 5 as Int, 2 * 3 * 5 as Int, nil as Int?) | ||
NBKAssertModularMultiplicativeInverse(2 * 3 * 1 as Int, 2 * 3 * 5 as Int, nil as Int?) | ||
NBKAssertModularMultiplicativeInverse(2 * 1 * 5 as Int, 2 * 3 * 5 as Int, nil as Int?) | ||
NBKAssertModularMultiplicativeInverse(1 * 3 * 5 as Int, 2 * 3 * 5 as Int, nil as Int?) | ||
NBKAssertModularMultiplicativeInverse(2 * 3 * 5 as Int, 2 * 3 * 5 as Int, nil as Int?) | ||
NBKAssertModularMultiplicativeInverse(7 * 7 * 7 as Int, 2 * 3 * 5 as Int, 007 as Int?) | ||
} | ||
} | ||
|
||
//*============================================================================* | ||
// MARK: * NBK x Proper Binary Integer x Modular Mul. Inverse x Assertions | ||
//*============================================================================* | ||
|
||
private func NBKAssertModularMultiplicativeInverse<T: NBKFixedWidthInteger>( | ||
_ lhs: T, _ rhs: T, _ expectation: T?, | ||
file: StaticString = #file, line: UInt = #line) { | ||
//=------------------------------------------= | ||
if !lhs.addingReportingOverflow(rhs).overflow { | ||
NBKAssertModularMultiplicativeInverseInvariants(lhs + rhs, rhs, expectation, file: file, line: line) | ||
} | ||
|
||
brr: do { | ||
NBKAssertModularMultiplicativeInverseInvariants(lhs + 000, rhs, expectation, file: file, line: line) | ||
} | ||
|
||
if !lhs.subtractingReportingOverflow(rhs).overflow { | ||
NBKAssertModularMultiplicativeInverseInvariants(lhs - rhs, rhs, expectation, file: file, line: line) | ||
} | ||
} | ||
|
||
private func NBKAssertModularMultiplicativeInverseInvariants<T: NBKFixedWidthInteger>( | ||
_ lhs: T, _ rhs: T, _ expectation: T?, | ||
file: StaticString = #file, line: UInt = #line) { | ||
//=--------------------------------------= | ||
let inverse = NBK.PBI.modularMultiplicativeInverse(of: lhs, mod: rhs) | ||
//=--------------------------------------= | ||
XCTAssertEqual(inverse, expectation, file: file, line: line) | ||
//=--------------------------------------= | ||
if let inverse { | ||
var remainder = rhs.dividingFullWidth(lhs.multipliedFullWidth(by: inverse)).remainder | ||
if remainder.isLessThanZero { | ||
remainder += rhs | ||
} | ||
|
||
XCTAssert(0000 ..< rhs ~= inverse, file: file, line: line) | ||
XCTAssertEqual(remainder, 1 % rhs, file: file, line: line) | ||
XCTAssertEqual(inverse.isZero, rhs == 1, file: file, line: line) | ||
} | ||
//=--------------------------------------= | ||
if T.isSigned, !lhs.isLessThanZero, !rhs.isLessThanZero { | ||
NBKAssertModularMultiplicativeInverseInvariants(lhs.magnitude, rhs.magnitude, expectation?.magnitude, file: file, line: line) | ||
} | ||
} | ||
|
||
#endif |