diff --git a/Sources/NBKCoreKit/NBKBinaryInteger.swift b/Sources/NBKCoreKit/NBKBinaryInteger.swift index 388d672c..5d04349b 100644 --- a/Sources/NBKCoreKit/NBKBinaryInteger.swift +++ b/Sources/NBKCoreKit/NBKBinaryInteger.swift @@ -35,6 +35,28 @@ where Magnitude: NBKUnsignedInteger, Words: Sendable { // MARK: Details x Numbers //=------------------------------------------------------------------------= + /// An instance equal to zero. + /// + /// Zero is the identity element for addition. + /// + /// ```swift + /// precondition(x + .one == x) // for each x + /// precondition(.one + x == x) // for each x + /// ``` + /// + @inlinable static var zero: Self { get } + + /// An instance equal to one. + /// + /// One is the identity element for multiplication. + /// + /// ```swift + /// precondition(x * .one == x) // for each x + /// precondition(.one * x == x) // for each x + /// ``` + /// + @inlinable static var one: Self { get } + /// Creates a new instance from the given digit. /// /// ``` @@ -1354,9 +1376,15 @@ where Magnitude: NBKUnsignedInteger, Words: Sendable { extension NBKBinaryInteger { //=------------------------------------------------------------------------= - // MARK: Initializers + // MARK: Details x Numbers + //=------------------------------------------------------------------------= + // NOTE: Zero is already provided by Swift.AdditiveArithmetic. //=------------------------------------------------------------------------= + @inlinable public static var one: Self { + 1 + } + @inlinable public init(digit: Digit) where Digit == Self { self = digit } diff --git a/Sources/NBKCoreKit/NBKFixedWidthInteger.swift b/Sources/NBKCoreKit/NBKFixedWidthInteger.swift index 954f3929..42e9c4f8 100644 --- a/Sources/NBKCoreKit/NBKFixedWidthInteger.swift +++ b/Sources/NBKCoreKit/NBKFixedWidthInteger.swift @@ -728,7 +728,7 @@ extension NBKFixedWidthInteger where Self: NBKSignedInteger { extension NBKFixedWidthInteger { //=------------------------------------------------------------------------= - // MARK: Initializers + // MARK: Details x Numbers //=------------------------------------------------------------------------= @inlinable public init?(magnitude: Magnitude) { diff --git a/Sources/NBKCoreKit/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift b/Sources/NBKCoreKit/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift index a30a8675..34379022 100644 --- a/Sources/NBKCoreKit/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift +++ b/Sources/NBKCoreKit/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift @@ -50,16 +50,13 @@ extension NBK.ProperBinaryInteger where Integer: NBKUnsignedInteger { /// @inlinable public static func modularMultiplicativeInverse(sign: NBK.Sign, magnitude: Integer, modulo modulus: Integer) -> Integer? { //=--------------------------------------= - switch modulus.compared(to: 1 as Integer.Digit) { - case 1: break; - case 0: return Integer.zero + switch modulus.compared(to: Integer.Digit.one) { + case 01: break; + case 00: return Integer.zero default: return nil } //=--------------------------------------= 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 - } + if !extended.result.compared(to: Integer.Digit.one).isZero { return nil } // must be coprime //=--------------------------------------= Swift.assert(extended.lhsCoefficient.isMoreThanZero) return (sign == .minus) == extended.iteration.isEven ? modulus - extended.lhsCoefficient : extended.lhsCoefficient diff --git a/Tests/NBKCoreKitBenchmarks/NBKCoreInteger+Numbers.swift b/Tests/NBKCoreKitBenchmarks/NBKCoreInteger+Numbers.swift new file mode 100644 index 00000000..c716af7c --- /dev/null +++ b/Tests/NBKCoreKitBenchmarks/NBKCoreInteger+Numbers.swift @@ -0,0 +1,89 @@ +//=----------------------------------------------------------------------------= +// 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 Core Integer x Numbers x Int +//*============================================================================* + +final class NBKCoreIntegerBenchmarksOnNumbersAsInt: XCTestCase { + + typealias T = Int + + //=------------------------------------------------------------------------= + // MARK: Tests x Constants + //=------------------------------------------------------------------------= + + func testZero() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.zero) + } + } + + func testOne() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.one) + } + } + + func testMin() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.min) + } + } + + func testMax() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.max) + } + } +} + +//*============================================================================* +// MARK: * NBK x Core Integer x Numbers x UInt +//*============================================================================* + +final class NBKCoreIntegerBenchmarksOnNumbersAsUInt: XCTestCase { + + typealias T = UInt + + //=------------------------------------------------------------------------= + // MARK: Tests x Constants + //=------------------------------------------------------------------------= + + func testZero() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.zero) + } + } + + func testOne() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.one) + } + } + + func testMin() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.min) + } + } + + func testMax() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.max) + } + } +} + +#endif diff --git a/Tests/NBKCoreKitBenchmarks/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift b/Tests/NBKCoreKitBenchmarks/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift index 5e3174c0..c16583e8 100644 --- a/Tests/NBKCoreKitBenchmarks/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift +++ b/Tests/NBKCoreKitBenchmarks/Private/NBKProperBinaryInteger+ModularMultiplicativeInverse.swift @@ -21,7 +21,7 @@ private typealias Y = [UInt32] //*============================================================================* final class NBKProperBinaryIntegerBenchmarksOnModularMultiplicativeInverse: XCTestCase { - + typealias T = NBK.PBI //=------------------------------------------------------------------------= diff --git a/Tests/NBKCoreKitTests/NBKCoreInteger+Numbers.swift b/Tests/NBKCoreKitTests/NBKCoreInteger+Numbers.swift index e63128b1..a5ca1a8d 100644 --- a/Tests/NBKCoreKitTests/NBKCoreInteger+Numbers.swift +++ b/Tests/NBKCoreKitTests/NBKCoreInteger+Numbers.swift @@ -27,7 +27,88 @@ final class NBKCoreIntegerTestsOnNumbers: XCTestCase { let types: [T] = NBKCoreIntegerTests.types //=------------------------------------------------------------------------= - // MARK: Tests + // MARK: Tests x Constants + //=------------------------------------------------------------------------= + + func testZero() { + func whereIs(_ type: T.Type) where T: NBKCoreInteger { + XCTAssertEqual(T.zero, 000) + + for x in Int8.min ... Int8.max { + let x = T(truncatingIfNeeded: x) + + XCTAssertEqual(x + T.zero, x) + XCTAssertEqual(T.zero + x, x) + + XCTAssertEqual(x - x, T.zero) + XCTAssertEqual(x - T.zero, x) + } + } + + for type: T in types { + whereIs(type) + } + } + + func testOne() { + func whereIs(_ type: T.Type) where T: NBKCoreInteger { + XCTAssertEqual(T.one, 0001) + + for x in Int8.min ... Int8.max { + let x = T(truncatingIfNeeded: x) + + XCTAssertEqual(x * T.one, x) + XCTAssertEqual(T.one * x, x) + + XCTAssertEqual(x / T.one, x) + XCTAssertEqual(x % T.one, T.zero) + + if !x.isZero { + XCTAssertEqual(x / x, T.one ) + XCTAssertEqual(x % x, T.zero) + } + } + } + + for type: T in types { + whereIs(type) + } + } + + func testMin() { + func whereIsSigned(_ type: T.Type) where T: NBKCoreInteger { + XCTAssertEqual( T.min, T.one << (T.bitWidth - 1)) + XCTAssertEqual(~T.max, T.one << (T.bitWidth - 1)) + } + + func whereIsUnsigned(_ type: T.Type) where T: NBKCoreInteger { + XCTAssertEqual( T.min, T(repeating: false)) + XCTAssertEqual(~T.max, T(repeating: false)) + } + + for type: T in types { + type.isSigned ? whereIsSigned(type) : whereIsUnsigned(type) + } + } + + func testMax() { + func whereIsSigned(_ type: T.Type) where T: NBKCoreInteger { + XCTAssertEqual( T.max, ~(T.one << (T.bitWidth - 1))) + XCTAssertEqual(~T.min, ~(T.one << (T.bitWidth - 1))) + } + + func whereIsUnsigned(_ type: T.Type) where T: NBKCoreInteger { + XCTAssertEqual( T.max, T(repeating: true)) + XCTAssertEqual(~T.min, T(repeating: true)) + } + + for type: T in types { + type.isSigned ? whereIsSigned(type) : whereIsUnsigned(type) + } + } + + //=------------------------------------------------------------------------= + // MARK: Tests x Integers //=------------------------------------------------------------------------= func testFromDigit() { diff --git a/Tests/NBKDoubleWidthKitBenchmarks/NBKDoubleWidth+Numbers.swift b/Tests/NBKDoubleWidthKitBenchmarks/NBKDoubleWidth+Numbers.swift index 0833e3e8..c429fab6 100644 --- a/Tests/NBKDoubleWidthKitBenchmarks/NBKDoubleWidth+Numbers.swift +++ b/Tests/NBKDoubleWidthKitBenchmarks/NBKDoubleWidth+Numbers.swift @@ -27,19 +27,29 @@ final class NBKDoubleWidthBenchmarksOnNumbersAsInt256: XCTestCase { typealias M = UInt256 //=------------------------------------------------------------------------= - // MARK: Tests + // MARK: Tests x Constants //=------------------------------------------------------------------------= func testZero() { - for _ in 0 ..< 1_000_000 { - NBK.blackHole(T()) + for _ in 0 ..< 5_000_000 { NBK.blackHole(T.zero) } } - func testEdges() { - for _ in 0 ..< 1_000_000 { + func testOne() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.one) + } + } + + func testMin() { + for _ in 0 ..< 5_000_000 { NBK.blackHole(T.min) + } + } + + func testMax() { + for _ in 0 ..< 5_000_000 { NBK.blackHole(T.max) } } @@ -436,23 +446,33 @@ final class NBKDoubleWidthBenchmarksOnNumbersAsUInt256: XCTestCase { typealias M = UInt256 //=------------------------------------------------------------------------= - // MARK: Tests + // MARK: Tests x Constants //=------------------------------------------------------------------------= func testZero() { - for _ in 0 ..< 1_000_000 { - NBK.blackHole(T()) + for _ in 0 ..< 5_000_000 { NBK.blackHole(T.zero) } } - func testEdges() { - for _ in 0 ..< 1_000_000 { + func testOne() { + for _ in 0 ..< 5_000_000 { + NBK.blackHole(T.one) + } + } + + func testMin() { + for _ in 0 ..< 5_000_000 { NBK.blackHole(T.min) + } + } + + func testMax() { + for _ in 0 ..< 5_000_000 { NBK.blackHole(T.max) } } - + //=------------------------------------------------------------------------= // MARK: Tests x Integers //=------------------------------------------------------------------------= diff --git a/Tests/NBKDoubleWidthKitTests/NBKDoubleWidth+Numbers.swift b/Tests/NBKDoubleWidthKitTests/NBKDoubleWidth+Numbers.swift index eaf4ec3b..74fd07c2 100644 --- a/Tests/NBKDoubleWidthKitTests/NBKDoubleWidth+Numbers.swift +++ b/Tests/NBKDoubleWidthKitTests/NBKDoubleWidth+Numbers.swift @@ -31,17 +31,52 @@ final class NBKDoubleWidthTestsOnNumbersAsInt256: XCTestCase { typealias M2 = NBKDoubleWidth //=------------------------------------------------------------------------= - // MARK: Tests + // MARK: Tests x Constants //=------------------------------------------------------------------------= func testZero() { NBKAssertNumbers(from: T( ), default: T(x64: X(0, 0, 0, 0))) NBKAssertNumbers(from: T.zero, default: T(x64: X(0, 0, 0, 0))) + + for x in Int8.min ... Int8.max { + let x = T(truncatingIfNeeded: x) + + XCTAssertEqual(x + T.zero, x) + XCTAssertEqual(T.zero + x, x) + + XCTAssertEqual(x - x, T.zero) + XCTAssertEqual(x - T.zero, x) + } + } + + func testOne() { + NBKAssertNumbers(from: T( 1), default: T(x64: X(1, 0, 0, 0))) + NBKAssertNumbers(from: T.one, default: T(x64: X(1, 0, 0, 0))) + + for x in Int8.min ... Int8.max { + let x = T(truncatingIfNeeded: x) + + XCTAssertEqual(x * T.one, x) + XCTAssertEqual(T.one * x, x) + + XCTAssertEqual(x / T.one, x) + XCTAssertEqual(x % T.one, T.zero) + + if !x.isZero { + XCTAssertEqual(x / x, T.one ) + XCTAssertEqual(x % x, T.zero) + } + } } - func testEdges() { - NBKAssertNumbers(from: T.min, default: T(x64: X(0, 0, 0, 1 << 63))) - NBKAssertNumbers(from: T.max, default: ~T(x64: X(0, 0, 0, 1 << 63))) + func testMin() { + NBKAssertNumbers(from: T.min, default: T(x64: X(0, 0, 0, 1 << 63))) + NBKAssertNumbers(from: ~T.max, default: T(x64: X(0, 0, 0, 1 << 63))) + } + + func testMax() { + NBKAssertNumbers(from: T.max, default: ~T(x64: X(0, 0, 0, 1 << 63))) + NBKAssertNumbers(from: ~T.min, default: ~T(x64: X(0, 0, 0, 1 << 63))) } //=------------------------------------------------------------------------= @@ -296,17 +331,52 @@ final class NBKDoubleWidthTestsOnNumbersAsUInt256: XCTestCase { typealias M2 = NBKDoubleWidth //=------------------------------------------------------------------------= - // MARK: Tests + // MARK: Tests x Constants //=------------------------------------------------------------------------= func testZero() { NBKAssertNumbers(from: T( ), default: T(x64: X(0, 0, 0, 0))) NBKAssertNumbers(from: T.zero, default: T(x64: X(0, 0, 0, 0))) + + for x in Int8.min ... Int8.max { + let x = T(truncatingIfNeeded: x) + + XCTAssertEqual(x + T.zero, x) + XCTAssertEqual(T.zero + x, x) + + XCTAssertEqual(x - x, T.zero) + XCTAssertEqual(x - T.zero, x) + } + } + + func testOne() { + NBKAssertNumbers(from: T( 1), default: T(x64: X(1, 0, 0, 0))) + NBKAssertNumbers(from: T.one, default: T(x64: X(1, 0, 0, 0))) + + for x in Int8.min ... Int8.max { + let x = T(truncatingIfNeeded: x) + + XCTAssertEqual(x * T.one, x) + XCTAssertEqual(T.one * x, x) + + XCTAssertEqual(x / T.one, x) + XCTAssertEqual(x % T.one, T.zero) + + if !x.isZero { + XCTAssertEqual(x / x, T.one ) + XCTAssertEqual(x % x, T.zero) + } + } + } + + func testMin() { + NBKAssertNumbers(from: T.min, default: T(x64: X(0, 0, 0, 0))) + NBKAssertNumbers(from: ~T.max, default: T(x64: X(0, 0, 0, 0))) } - func testEdges() { - NBKAssertNumbers(from: T.min, default: T(x64: X(0, 0, 0, 0))) - NBKAssertNumbers(from: T.max, default: ~T(x64: X(0, 0, 0, 0))) + func testMax() { + NBKAssertNumbers(from: T.max, default: ~T(x64: X(0, 0, 0, 0))) + NBKAssertNumbers(from: ~T.min, default: ~T(x64: X(0, 0, 0, 0))) } //=------------------------------------------------------------------------=