From 81d493ca7e94f5ce6ed23baca8cdccaa84a9e483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Bystr=C3=B6m=20Ericsson?= Date: Tue, 15 Oct 2024 08:47:50 +0200 Subject: [PATCH] Rework BinaryInteger+Multiplication.swift tests (#108) (#110). --- Package.swift | 2 +- Sources/TestKit2/Expect+Multiplication.swift | 124 ++++ Sources/TestKit2/Global+Info.swift | 22 + Sources/TestKit2/Global+Types.swift | 2 +- Sources/TestKit2/Models/Tag.swift | 6 + .../CoreKitTests/CoreInt+Multiplication.swift | 102 --- .../Division+Validation.swift.swift | 4 +- Tests/CoreKitTests/Division.swift | 8 +- Tests/CoreKitTests/Fallible.swift | 2 +- .../Stdlib/Swift+BinaryInteger.swift | 2 +- .../CoreKitTests/Stdlib/Swift+Optional.swift | 4 +- .../DoubleInt+Multiplication.swift | 77 +-- .../InfiniInt+Multiplication.swift | 490 -------------- .../StdlibInt+Division.swift | 2 +- .../StdlibInt+Multiplication.swift | 58 +- .../BinaryInteger+Division.swift.swift | 72 +- .../BinaryInteger+Multiplication.swift | 620 +++++++----------- .../BinaryInteger+Stdlib.swift | 2 +- .../UltimathnumTests/Utilities/Globals.swift | 18 + 19 files changed, 512 insertions(+), 1105 deletions(-) create mode 100644 Sources/TestKit2/Global+Info.swift delete mode 100644 Tests/CoreKitTests/CoreInt+Multiplication.swift delete mode 100644 Tests/InfiniIntKitTests/InfiniInt+Multiplication.swift diff --git a/Package.swift b/Package.swift index 76a2c3d8..7dc4ba97 100644 --- a/Package.swift +++ b/Package.swift @@ -76,7 +76,7 @@ let package = Package( ), .testTarget( name: "DoubleIntKitTests", - dependencies: ["DoubleIntKit", "TestKit"] + dependencies: ["DoubleIntKit", "TestKit", "TestKit2"] ), .target( name: "FibonacciKit", diff --git a/Sources/TestKit2/Expect+Multiplication.swift b/Sources/TestKit2/Expect+Multiplication.swift index d1c0976e..cb0651f0 100644 --- a/Sources/TestKit2/Expect+Multiplication.swift +++ b/Sources/TestKit2/Expect+Multiplication.swift @@ -9,6 +9,130 @@ import CoreKit +//*============================================================================* +// MARK: * Expect x Multiplication x Binary Integer +//*============================================================================* + +@inlinable public func Ɣexpect( + _ lhs: T, + times rhs: T, + is low: Fallible, + and high: T? = nil, + derivatives: Bool = true, + division: Bool = true, + at location: SourceLocation = #_sourceLocation +) where T: BinaryInteger { + //=------------------------------------------= + let inputsAreEqual = lhs == rhs + let high = high ?? T(repeating: low.value.appendix) + let wide = Doublet(low: T.Magnitude(raw: low.value), high: high) + //=------------------------------------------= + if derivatives { + derivativesOneWayOnly(lhs, rhs) + } + + if derivatives, !inputsAreEqual { + derivativesOneWayOnly(rhs, lhs) + } + + always: do { + #expect(lhs.times(rhs) == low, "BinaryInteger/times(_:)", sourceLocation: location) + } + + if !inputsAreEqual { + #expect(rhs.times(lhs) == low, "BinaryInteger/times(_:)", sourceLocation: location) + } + + if inputsAreEqual { + #expect(lhs.squared() == low, "BinaryInteger/squared()", sourceLocation: location) + } + + always: do { + #expect(lhs.multiplication(rhs) == wide, "BinaryInteger/multiplication(_:)", sourceLocation: location) + } + + if !inputsAreEqual { + #expect(rhs.multiplication(lhs) == wide, "BinaryInteger/multiplication(_:)", sourceLocation: location) + } + + if division, !low.error { + divisionOneWayOnly(lhs, rhs) + } + + if division, !low.error, !inputsAreEqual { + divisionOneWayOnly(rhs, lhs) + } + + complements: do { + let lhsComplement = lhs.complement() + let rhsComplement = rhs.complement() + let lowComplement = low.value.complement() + + always: do { + #expect(lhs.times(rhsComplement).value == (((lowComplement))), "BinaryInteger/times(_:) - complement [0]", sourceLocation: location) + #expect(lhsComplement.times(rhs).value == (((lowComplement))), "BinaryInteger/times(_:) - complement [1]", sourceLocation: location) + #expect(lhsComplement.times(rhsComplement).value == low.value, "BinaryInteger/times(_:) - complement [2]", sourceLocation: location) + } + + if !inputsAreEqual { + #expect(rhs.times(lhsComplement).value == (((lowComplement))), "BinaryInteger/times(_:) - complement [3]", sourceLocation: location) + #expect(rhsComplement.times(lhs).value == (((lowComplement))), "BinaryInteger/times(_:) - complement [4]", sourceLocation: location) + } + + if inputsAreEqual { + #expect(lhsComplement.squared( ).value == (((((low.value))))), "BinaryInteger/squared() - complement [5]", sourceLocation: location) + } + } + + drain: do { + guard let a = lhs.ascending(Bit.zero).natural().optional() else { break drain } + guard let b = rhs.ascending(Bit.zero).natural().optional() else { break drain } + guard let c = a.plus(b).optional() else { break drain } + + let lhsDown = (lhs >> a) + let rhsDown = (rhs >> b) + + always: do { + #expect(((lhs)).times(rhsDown).value << b == low.value, "BinaryInteger/times(_:) - drain [0]", sourceLocation: location) + #expect(lhsDown.times(rhsDown).value << c == low.value, "BinaryInteger/times(_:) - drain [1]", sourceLocation: location) + } + + if !inputsAreEqual { + #expect(lhsDown.times(((rhs))).value << a == low.value, "BinaryInteger/times(_:) - drain [2]", sourceLocation: location) + } + + if inputsAreEqual { + #expect(lhsDown.squared( ).value << c == low.value, "BinaryInteger/times(_:) - drain [3]", sourceLocation: location) + } + } + //=------------------------------------------= + func divisionOneWayOnly(_ lhs: T, _ rhs: T) { + if let divisor = Nonzero(exactly: rhs) { + let division = low.value.division(divisor) + #expect(division.value.quotient == lhs, "invariant: (a * b) / b == a", sourceLocation: location) + #expect(division.value.remainder.isZero, "invariant: (a * b) % b == 0", sourceLocation: location) + } + } + + func derivativesOneWayOnly(_ lhs: T, _ rhs: T) { + scope: do { + #expect(lhs &* rhs == low.value, "BinaryInteger.&*(_:_:)", sourceLocation: location) + } + + scope: if !low.error { + #expect(lhs * rhs == low.value, "BinaryInteger.*(_:_:)", sourceLocation: location) + } + + scope: do { + #expect({ var x = lhs; x &*= rhs; return x }() == low.value, "BinaryInteger.&*=(_:_:)", sourceLocation: location) + } + + scope: if !low.error { + #expect({ var x = lhs; x *= rhs; return x }() == low.value, "BinaryInteger.*=(_:_:)", sourceLocation: location) + } + } +} + //*============================================================================* // MARK: * Expect x Multiplication x Data Integer //*============================================================================* diff --git a/Sources/TestKit2/Global+Info.swift b/Sources/TestKit2/Global+Info.swift new file mode 100644 index 00000000..02a37965 --- /dev/null +++ b/Sources/TestKit2/Global+Info.swift @@ -0,0 +1,22 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Ultimathnum 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. +//=----------------------------------------------------------------------------= + +import CoreKit + +//*============================================================================* +// MARK: * Global x Info +//*============================================================================* + +#if DEBUG +public let isDebug: Bool = true +public let isRelease: Bool = false +#else +public let isDebug: Bool = false +public let isRelease: Bool = true +#endif diff --git a/Sources/TestKit2/Global+Types.swift b/Sources/TestKit2/Global+Types.swift index ff457530..85f09e90 100644 --- a/Sources/TestKit2/Global+Types.swift +++ b/Sources/TestKit2/Global+Types.swift @@ -21,7 +21,7 @@ public let i8u8: [any CoreInteger.Type] = [ U8.self, ] -public let coreIntegers: [any CoreInteger.Type] = { +public let typesAsCoreInteger: [any CoreInteger.Type] = { typesAsCoreIntegersAsSigned + typesAsCoreIntegersAsUnsigned }() diff --git a/Sources/TestKit2/Models/Tag.swift b/Sources/TestKit2/Models/Tag.swift index 38482db4..57ca9b16 100644 --- a/Sources/TestKit2/Models/Tag.swift +++ b/Sources/TestKit2/Models/Tag.swift @@ -29,9 +29,15 @@ extension Tag { /// Sending to another destination. @Tag public static var forwarding: Self + /// Of great significance or value. + @Tag public static var important: Self + /// Something that may be regained. @Tag public static var recoverable: Self + /// Happening, done, or chosen by chance. + @Tag public static var random: Self + /// Not officially authorized or confirmed. @Tag public static var unofficial: Self diff --git a/Tests/CoreKitTests/CoreInt+Multiplication.swift b/Tests/CoreKitTests/CoreInt+Multiplication.swift deleted file mode 100644 index 97f628a3..00000000 --- a/Tests/CoreKitTests/CoreInt+Multiplication.swift +++ /dev/null @@ -1,102 +0,0 @@ -//=----------------------------------------------------------------------------= -// This source file is part of the Ultimathnum 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. -//=----------------------------------------------------------------------------= - -import CoreKit -import TestKit - -//*============================================================================* -// MARK: * Core Int x Multiplication -//*============================================================================* - -extension CoreIntTests { - - //=------------------------------------------------------------------------= - // MARK: Tests - //=------------------------------------------------------------------------= - - func testMultiplication() { - func whereIsSigned(_ type: T.Type) where T: SystemsInteger { - typealias M = T.Magnitude - typealias X = Doublet - typealias F = Fallible> - - Test().multiplication( 0 as T, 0 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication( 0 as T, -1 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication(-1 as T, 0 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication(-1 as T, -1 as T, F(X(low: 1 as M, high: 0 as T))) - - Test().multiplication( 3 as T, 0 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication( 3 as T, -0 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication(-3 as T, 0 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication(-3 as T, -0 as T, F(X(low: 0 as M, high: 0 as T))) - - Test().multiplication( 3 as T, 1 as T, F(X(low: 3 as M, high: 0 as T))) - Test().multiplication( 3 as T, -1 as T, F(X(low: ~2 as M, high: -1 as T))) - Test().multiplication(-3 as T, 1 as T, F(X(low: ~2 as M, high: -1 as T))) - Test().multiplication(-3 as T, -1 as T, F(X(low: 3 as M, high: 0 as T))) - - Test().multiplication( 3 as T, 2 as T, F(X(low: 6 as M, high: 0 as T))) - Test().multiplication( 3 as T, -2 as T, F(X(low: ~5 as M, high: -1 as T))) - Test().multiplication(-3 as T, 2 as T, F(X(low: ~5 as M, high: -1 as T))) - Test().multiplication(-3 as T, -2 as T, F(X(low: 6 as M, high: 0 as T))) - } - - func whereIsUnsigned(_ type: T.Type) where T: SystemsInteger { - typealias M = T.Magnitude - typealias X = Doublet - typealias F = Fallible> - - Test().multiplication( 0 as T, 0 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication( 0 as T, 1 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication( 1 as T, 0 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication( 1 as T, 1 as T, F(X(low: 1 as M, high: 0 as T))) - - Test().multiplication( 3 as T, 0 as T, F(X(low: 0 as M, high: 0 as T))) - Test().multiplication( 3 as T, 1 as T, F(X(low: 3 as M, high: 0 as T))) - Test().multiplication( 3 as T, 2 as T, F(X(low: 6 as M, high: 0 as T))) - Test().multiplication( 3 as T, 3 as T, F(X(low: 9 as M, high: 0 as T))) - } - - for type in Self.typesWhereIsSigned { - whereIsSigned(type) - } - - for type in Self.typesWhereIsUnsigned { - whereIsUnsigned(type) - } - } - - //=------------------------------------------------------------------------= - // MARK: Tests x Miscellaneous - //=------------------------------------------------------------------------= - - /// The complement of one input yields the complement of the output. - /// - /// - Note: This invariant only holds for truncating multiplication (`&*`). - /// - func testMultiplicationByComplementYieldsLowProductComplementForEachBytePair() { - func whereIs(_ type: T.Type) where T: SystemsInteger { - var success = U32.min - - for a in T.min ... T.max { - for b in T.min ... T.max { - let c = a &* b - - success &+= U32(Bit(a &* b.complement() == c.complement())) - success &+= U32(Bit(a.complement() &* b == c.complement())) - } - } - - Test().same(success, 2 * 256 * 256) - } - - whereIs(I8.self) - whereIs(U8.self) - } -} diff --git a/Tests/CoreKitTests/Division+Validation.swift.swift b/Tests/CoreKitTests/Division+Validation.swift.swift index 78c5a4f5..5277ed14 100644 --- a/Tests/CoreKitTests/Division+Validation.swift.swift +++ b/Tests/CoreKitTests/Division+Validation.swift.swift @@ -24,8 +24,8 @@ import TestKit2 @Test("Division/exactly() - T.init(load:)", .serialized, arguments: I8(-2)...I8(2), I8(-2)...I8(2)) func exactly(quotient: I8, remainder: I8) { let error = !remainder.isZero - for quotient in coreIntegers { - for remainder in coreIntegers { + for quotient in typesAsCoreInteger { + for remainder in typesAsCoreInteger { whereIs(quotient, remainder) } } diff --git a/Tests/CoreKitTests/Division.swift b/Tests/CoreKitTests/Division.swift index b185e6bb..e9db3c8c 100644 --- a/Tests/CoreKitTests/Division.swift +++ b/Tests/CoreKitTests/Division.swift @@ -23,8 +23,8 @@ import TestKit2 @Test("Division/init(raw:) - T.init(load:)", .serialized, arguments: I8(-2)...I8(2), I8(-2)...I8(2)) func pattern(quotient: I8, remainder: I8) { - for quotient in coreIntegers { - for remainder in coreIntegers { + for quotient in typesAsCoreInteger { + for remainder in typesAsCoreInteger { whereIs(quotient, remainder) } } @@ -40,8 +40,8 @@ import TestKit2 @Test("Division/components() - T.init(load:)", .serialized, arguments: I8(-2)...I8(2), I8(-2)...I8(2)) func components(quotient: I8, remainder: I8) { - for quotient in coreIntegers { - for remainder in coreIntegers { + for quotient in typesAsCoreInteger { + for remainder in typesAsCoreInteger { whereIs(quotient, remainder) } } diff --git a/Tests/CoreKitTests/Fallible.swift b/Tests/CoreKitTests/Fallible.swift index b350db87..7abe701a 100644 --- a/Tests/CoreKitTests/Fallible.swift +++ b/Tests/CoreKitTests/Fallible.swift @@ -25,7 +25,7 @@ import TestKit2 whereIs(Bool.self) whereIs(Void.self) - for type in coreIntegers { + for type in typesAsCoreInteger { whereIsBinaryInteger(type) } diff --git a/Tests/CoreKitTests/Stdlib/Swift+BinaryInteger.swift b/Tests/CoreKitTests/Stdlib/Swift+BinaryInteger.swift index 630bcca4..f4cace46 100644 --- a/Tests/CoreKitTests/Stdlib/Swift+BinaryInteger.swift +++ b/Tests/CoreKitTests/Stdlib/Swift+BinaryInteger.swift @@ -21,7 +21,7 @@ import TestKit2 // MARK: Tests //=------------------------------------------------------------------------= - @Test("T.init(raw:)", arguments: coreIntegers, fuzzers) + @Test("T.init(raw:)", arguments: typesAsCoreInteger, fuzzers) func bitcasting(type: any CoreInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) diff --git a/Tests/CoreKitTests/Stdlib/Swift+Optional.swift b/Tests/CoreKitTests/Stdlib/Swift+Optional.swift index 24f955b5..e5dfa832 100644 --- a/Tests/CoreKitTests/Stdlib/Swift+Optional.swift +++ b/Tests/CoreKitTests/Stdlib/Swift+Optional.swift @@ -21,7 +21,7 @@ import TestKit2 // MARK: Tests //=------------------------------------------------------------------------= - @Test("T.init(raw:)", arguments: coreIntegers, fuzzers) + @Test("T.init(raw:)", arguments: typesAsCoreInteger, fuzzers) func bitcasting(type: any SystemsInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -39,7 +39,7 @@ import TestKit2 } } - @Test("Optional/unwrap()", arguments: coreIntegers, fuzzers) + @Test("Optional/unwrap()", arguments: typesAsCoreInteger, fuzzers) func unwrapping(type: any SystemsInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) diff --git a/Tests/DoubleIntKitTests/DoubleInt+Multiplication.swift b/Tests/DoubleIntKitTests/DoubleInt+Multiplication.swift index e2a5727c..042bddce 100644 --- a/Tests/DoubleIntKitTests/DoubleInt+Multiplication.swift +++ b/Tests/DoubleIntKitTests/DoubleInt+Multiplication.swift @@ -9,74 +9,41 @@ import CoreKit import DoubleIntKit -import TestKit +import RandomIntKit +import TestKit2 //*============================================================================* // MARK: * Double Int x Multiplication //*============================================================================* -extension DoubleIntTests { +@Suite("DoubleInt/multiplication - not in protocol") +struct DoubleIntTestsOnMultiplicationNotInProtocol { //=------------------------------------------------------------------------= - // MARK: Tests x 2 by 1 + // MARK: Tests x 2 by 1 as 3 //=------------------------------------------------------------------------= - - func testMultiplication213() { - func whereTheBaseIs(_ type: B.Type) where B: SystemsInteger & UnsignedInteger { - typealias T = DoubleInt - typealias P = TripleInt - - Test().same(T(low: 0, high: 0).multiplication( 0 as B), P(low: 0, mid: 0, high: 0)) - Test().same(T(low: 0, high: 0).multiplication(~0 as B), P(low: 0, mid: 0, high: 0)) - Test().same(T(low: ~0, high: ~0).multiplication( 0 as B), P(low: 0, mid: 0, high: 0)) - Test().same(T(low: ~0, high: ~0).multiplication(~0 as B), P(low: 1, mid: ~0, high: ~1)) - - Test().same(T(low: 1, high: 2).multiplication( 3 as B), P(low: 3, mid: 6, high: 0)) - Test().same(T(low: 1, high: 2).multiplication(~3 as B), P(low: ~3, mid: ~7, high: 1)) - Test().same(T(low: ~1, high: ~2).multiplication( 3 as B), P(low: ~5, mid: ~6, high: 2)) - Test().same(T(low: ~1, high: ~2).multiplication(~3 as B), P(low: 8, mid: 6, high: ~5)) - } - - for base in Self.basesWhereIsUnsigned { - whereTheBaseIs(base) - } - } - - //=------------------------------------------------------------------------= - // MARK: Tests x 2 by 2 + // TODO: HalveableInteger/multiplication(_:) would let us hoist this test //=------------------------------------------------------------------------= - func testMultiplication() { - func whereTheBaseTypeIsSigned(_ type: B.Type) where B: SystemsInteger { - typealias T = DoubleInt - typealias M = DoubleInt.Magnitude - typealias X = Doublet - typealias F = Fallible> - - Test().multiplication(T(low: 1, high: 2), T(low: 3, high: 4), F(X(low: M(low: 3, high: 10), high: T(low: 8, high: 0)), error: true)) - Test().multiplication(T(low: 1, high: 2), T(low: ~3, high: ~4), F(X(low: M(low: ~3, high: ~12), high: T(low: ~8, high: ~0)), error: true)) - Test().multiplication(T(low: ~1, high: ~2), T(low: 3, high: 4), F(X(low: M(low: ~5, high: ~14), high: T(low: ~8, high: ~0)), error: true)) - Test().multiplication(T(low: ~1, high: ~2), T(low: ~3, high: ~4), F(X(low: M(low: 8, high: 16), high: T(low: 8, high: 0)), error: true)) - } + @Test("DoubleInt/multiplication - 213 vs 224 [entropic]", arguments: typesAsCoreInteger, fuzzers) + func multiplication213vs224(base: any CoreInteger.Type, randomness: consuming FuzzerInt) { + whereIs(base) - func whereTheBaseTypeIsUnsigned(_ type: B.Type) where B: SystemsInteger { + func whereIs(_ base: B.Type) where B: SystemsInteger { typealias T = DoubleInt - typealias M = DoubleInt.Magnitude - typealias P = Doublet - typealias F = Fallible> - Test().multiplication(T(low: 1, high: 2), T(low: 3, high: 4), F(P(low: M(low: 3, high: 10), high: T(low: 8, high: 0)), error: true)) - Test().multiplication(T(low: 1, high: 2), T(low: ~3, high: ~4), F(P(low: M(low: ~3, high: ~12), high: T(low: ~7, high: 1)), error: true)) - Test().multiplication(T(low: ~1, high: ~2), T(low: 3, high: 4), F(P(low: M(low: ~5, high: ~14), high: T(low: ~5, high: 3)), error: true)) - Test().multiplication(T(low: ~1, high: ~2), T(low: ~3, high: ~4), F(P(low: M(low: 8, high: 16), high: T(low: 2, high: ~5)), error: true)) - } - - for base in Self.basesWhereIsSigned { - whereTheBaseTypeIsSigned(base) - } - - for base in Self.basesWhereIsUnsigned { - whereTheBaseTypeIsUnsigned(base) + for _ in 0 ..< 1024 { + let lhs = T.entropic(using: &randomness) + let rhs = B.entropic(using: &randomness) + + let result3 = lhs.multiplication(rhs) + let result4 = lhs.multiplication(T(load: rhs)) + + #expect(result4.low.low == result3.low) + #expect(result4.low.high == result3.mid) + #expect(result4.high.low == B.Magnitude.init(raw: result3.high)) + #expect(result4.high.high == B(repeating: result3.high.appendix)) + } } } } diff --git a/Tests/InfiniIntKitTests/InfiniInt+Multiplication.swift b/Tests/InfiniIntKitTests/InfiniInt+Multiplication.swift deleted file mode 100644 index 89b0202e..00000000 --- a/Tests/InfiniIntKitTests/InfiniInt+Multiplication.swift +++ /dev/null @@ -1,490 +0,0 @@ -//=----------------------------------------------------------------------------= -// This source file is part of the Ultimathnum 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. -//=----------------------------------------------------------------------------= - -import CoreKit -import InfiniIntKit -import RandomIntKit -import TestKit - -//*============================================================================* -// MARK: * Infini Int x Multiplication -//*============================================================================* - -extension InfiniIntTests { - - //=------------------------------------------------------------------------= - // MARK: Tests - //=------------------------------------------------------------------------= - - func testMultiplicationOfSmallBySmall() { - func whereIs(_ type: T.Type) where T: ArbitraryInteger { - typealias F = Fallible - //=----------------------------------= - Test().multiplication(~2 as T, ~2 as T, F( 9 as T, error: !T.isSigned)) - Test().multiplication(~2 as T, ~1 as T, F( 6 as T, error: !T.isSigned)) - Test().multiplication(~2 as T, ~0 as T, F( 3 as T, error: !T.isSigned)) - Test().multiplication(~2 as T, 0 as T, F( 0 as T)) - Test().multiplication(~2 as T, 1 as T, F(~2 as T)) - Test().multiplication(~2 as T, 2 as T, F(~5 as T, error: !T.isSigned)) - Test().multiplication(~2 as T, 3 as T, F(~8 as T, error: !T.isSigned)) - - Test().multiplication(~1 as T, ~2 as T, F( 6 as T, error: !T.isSigned)) - Test().multiplication(~1 as T, ~1 as T, F( 4 as T, error: !T.isSigned)) - Test().multiplication(~1 as T, ~0 as T, F( 2 as T, error: !T.isSigned)) - Test().multiplication(~1 as T, 0 as T, F( 0 as T)) - Test().multiplication(~1 as T, 1 as T, F(~1 as T)) - Test().multiplication(~1 as T, 2 as T, F(~3 as T, error: !T.isSigned)) - Test().multiplication(~1 as T, 3 as T, F(~5 as T, error: !T.isSigned)) - - Test().multiplication(~0 as T, ~2 as T, F( 3 as T, error: !T.isSigned)) - Test().multiplication(~0 as T, ~1 as T, F( 2 as T, error: !T.isSigned)) - Test().multiplication(~0 as T, ~0 as T, F( 1 as T, error: !T.isSigned)) - Test().multiplication(~0 as T, 0 as T, F( 0 as T)) - Test().multiplication(~0 as T, 1 as T, F(~0 as T)) - Test().multiplication(~0 as T, 2 as T, F(~1 as T, error: !T.isSigned)) - Test().multiplication(~0 as T, 3 as T, F(~2 as T, error: !T.isSigned)) - - Test().multiplication( 0 as T, ~2 as T, F( 0 as T)) - Test().multiplication( 0 as T, ~1 as T, F( 0 as T)) - Test().multiplication( 0 as T, ~0 as T, F( 0 as T)) - Test().multiplication( 0 as T, 0 as T, F( 0 as T)) - Test().multiplication( 0 as T, 1 as T, F( 0 as T)) - Test().multiplication( 0 as T, 2 as T, F( 0 as T)) - Test().multiplication( 0 as T, 3 as T, F( 0 as T)) - - Test().multiplication( 1 as T, ~2 as T, F(~2 as T)) - Test().multiplication( 1 as T, ~1 as T, F(~1 as T)) - Test().multiplication( 1 as T, ~0 as T, F(~0 as T)) - Test().multiplication( 1 as T, 0 as T, F( 0 as T)) - Test().multiplication( 1 as T, 1 as T, F( 1 as T)) - Test().multiplication( 1 as T, 2 as T, F( 2 as T)) - Test().multiplication( 1 as T, 3 as T, F( 3 as T)) - - Test().multiplication( 2 as T, ~2 as T, F(~5 as T, error: !T.isSigned)) - Test().multiplication( 2 as T, ~1 as T, F(~3 as T, error: !T.isSigned)) - Test().multiplication( 2 as T, ~0 as T, F(~1 as T, error: !T.isSigned)) - Test().multiplication( 2 as T, 0 as T, F( 0 as T)) - Test().multiplication( 2 as T, 1 as T, F( 2 as T)) - Test().multiplication( 2 as T, 2 as T, F( 4 as T)) - Test().multiplication( 2 as T, 3 as T, F( 6 as T)) - - Test().multiplication( 3 as T, ~2 as T, F(~8 as T, error: !T.isSigned)) - Test().multiplication( 3 as T, ~1 as T, F(~5 as T, error: !T.isSigned)) - Test().multiplication( 3 as T, ~0 as T, F(~2 as T, error: !T.isSigned)) - Test().multiplication( 3 as T, 0 as T, F( 0 as T)) - Test().multiplication( 3 as T, 1 as T, F( 3 as T)) - Test().multiplication( 3 as T, 2 as T, F( 6 as T)) - Test().multiplication( 3 as T, 3 as T, F( 9 as T)) - } - - for type in Self.types { - whereIs(type) - } - } - - func testMultiplicationOfLargeBySmall() { - func whereIs(_ type: T.Type) where T: ArbitraryInteger { - typealias F = Fallible - //=----------------------------------= - let a1234 = T([1, 2, 3, 4] as [UX], repeating: Bit.zero) - let a5678 = T([5, 6, 7, 8] as [UX], repeating: Bit.zero) - //=----------------------------------= - Test().multiplication( a1234, 5 as T, F(T([ 05, 10, 15, 20, 00] as [UX], repeating: Bit.zero))) - Test().multiplication( a1234, ~5 as T, F(T([~05, ~12, ~18, ~24, ~00] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~a1234, 5 as T, F(T([~09, ~10, ~15, ~20, ~00] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~a1234, ~5 as T, F(T([ 12, 12, 18, 24, 00] as [UX], repeating: Bit.zero), error: !T.isSigned)) - - Test().multiplication( a5678, 5 as T, F(T([ 25, 30, 35, 40, 00] as [UX], repeating: Bit.zero))) - Test().multiplication( a5678, ~5 as T, F(T([~29, ~36, ~42, ~48, ~00] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~a5678, 5 as T, F(T([~29, ~30, ~35, ~40, ~00] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~a5678, ~5 as T, F(T([ 36, 36, 42, 48, 00] as [UX], repeating: Bit.zero), error: !T.isSigned)) - //=----------------------------------= - let b1234 = T([1, 2, 3, 4] as [UX], repeating: Bit.one ) - let b5678 = T([5, 6, 7, 8] as [UX], repeating: Bit.one ) - //=----------------------------------= - Test().multiplication( b1234, 5 as T, F(T([ 05, 10, 15, 20, ~04] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication( b1234, ~5 as T, F(T([~05, ~12, ~18, ~24, 05] as [UX], repeating: Bit.zero), error: !T.isSigned)) - Test().multiplication(~b1234, 5 as T, F(T([~09, ~10, ~15, ~20, 04] as [UX], repeating: Bit.zero))) - Test().multiplication(~b1234, ~5 as T, F(T([ 12, 12, 18, 24, ~05] as [UX], repeating: Bit.one ), error: !T.isSigned)) - - Test().multiplication( b5678, 5 as T, F(T([ 25, 30, 35, 40, ~04] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication( b5678, ~5 as T, F(T([~29, ~36, ~42, ~48, 05] as [UX], repeating: Bit.zero), error: !T.isSigned)) - Test().multiplication(~b5678, 5 as T, F(T([~29, ~30, ~35, ~40, 04] as [UX], repeating: Bit.zero))) - Test().multiplication(~b5678, ~5 as T, F(T([ 36, 36, 42, 48, ~05] as [UX], repeating: Bit.one ), error: !T.isSigned)) - //=----------------------------------= - for number in [a1234, ~a1234, b1234, ~b1234, a5678, ~a5678, b5678, ~b5678] { - Test().multiplication(number, ~0 as T, F(number.complement(), error: !T.isSigned)) - Test().multiplication(number, 0 as T, F(0 as T)) - Test().multiplication(number, 1 as T, F(number)) - } - } - - for type in Self.types { - whereIs(type) - } - } - - func testMultiplicationOfLargeByLarge() { - func whereIs(_ type: T.Type) where T: ArbitraryInteger { - typealias F = Fallible - //=----------------------------------= - let a1234 = T([1, 2, 3, 4] as [UX], repeating: Bit.zero) - let a5678 = T([5, 6, 7, 8] as [UX], repeating: Bit.zero) - //=----------------------------------= - Test().multiplication( a1234, a1234, F(T([ 001, 004, 010, 020, 025, 024, 016, 000] as [UX], repeating: Bit.zero))) - Test().multiplication( a1234, ~a1234, F(T([~001, ~006, ~013, ~024, ~025, ~024, ~016, ~000] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~a1234, ~a1234, F(T([ 004, 008, 016, 028, 025, 024, 016, 000] as [UX], repeating: Bit.zero), error: !T.isSigned)) - - Test().multiplication( a1234, a5678, F(T([ 005, 016, 034, 060, 061, 052, 032, 000] as [UX], repeating: Bit.zero))) - Test().multiplication( a1234, ~a5678, F(T([~005, ~018, ~037, ~064, ~061, ~052, ~032, ~000] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~a1234, a5678, F(T([~009, ~022, ~041, ~068, ~061, ~052, ~032, ~000] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~a1234, ~a5678, F(T([ 012, 024, 044, 072, 061, 052, 032, 000] as [UX], repeating: Bit.zero), error: !T.isSigned)) - - Test().multiplication( a5678, a5678, F(T([ 025, 060, 106, 164, 145, 112, 064, 000] as [UX], repeating: Bit.zero))) - Test().multiplication( a5678, ~a5678, F(T([~029, ~066, ~113, ~172, ~145, ~112, ~064, ~000] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~a5678, ~a5678, F(T([ 036, 072, 120, 180, 145, 112, 064, 000] as [UX], repeating: Bit.zero), error: !T.isSigned)) - //=----------------------------------= - let b1234 = T([1, 2, 3, 4] as [UX], repeating: Bit.one ) - let b5678 = T([5, 6, 7, 8] as [UX], repeating: Bit.one ) - //=----------------------------------= - Test().multiplication( b1234, b1234, F(T([ 001, 004, 010, 020, 023, 020, 010, ~007] as [UX], repeating: Bit.zero), error: !T.isSigned)) - Test().multiplication( b1234, ~b1234, F(T([~001, ~006, ~013, ~024, ~022, ~020, ~010, 007] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~b1234, ~b1234, F(T([ 004, 008, 016, 028, 021, 020, 010, ~007] as [UX], repeating: Bit.zero))) - - Test().multiplication( b1234, b5678, F(T([ 005, 016, 034, 060, 055, 044, 022, ~011] as [UX], repeating: Bit.zero), error: !T.isSigned)) - Test().multiplication( b1234, ~b5678, F(T([~005, ~018, ~037, ~064, ~054, ~044, ~022, 011] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~b1234, b5678, F(T([~009, ~022, ~041, ~068, ~054, ~044, ~022, 011] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~b1234, ~b5678, F(T([ 012, 024, 044, 072, 053, 044, 022, ~011] as [UX], repeating: Bit.zero))) - - Test().multiplication( b5678, b5678, F(T([ 025, 060, 106, 164, 135, 100, 050, ~015] as [UX], repeating: Bit.zero), error: !T.isSigned)) - Test().multiplication( b5678, ~b5678, F(T([~029, ~066, ~113, ~172, ~134, ~100, ~050, 015] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~b5678, ~b5678, F(T([ 036, 072, 120, 180, 133, 100, 050, ~015] as [UX], repeating: Bit.zero))) - //=----------------------------------= - let x0000 = T([~0, ~0, ~0, ~0] as [UX], repeating: Bit.zero) - //=----------------------------------= - Test().multiplication( x0000, x0000, F(T([ 001, 000, 000, 000, ~001, ~000, ~000, ~000] as [UX] + [ 0] as [UX], repeating: Bit.zero))) - Test().multiplication( x0000, ~x0000, F(T([ 000, 000, 000, 000, 001, 000, 000, 000] as [UX] + [~0] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(~x0000, ~x0000, F(T([ 000, 000, 000, 000, 000, 000, 000, 000] as [UX] + [ 1] as [UX], repeating: Bit.zero), error: !T.isSigned)) - } - - for type in Self.types { - whereIs(type) - } - } -} - -//=----------------------------------------------------------------------------= -// MARK: + Code Coverage -//=----------------------------------------------------------------------------= - -extension InfiniIntTests { - - //=------------------------------------------------------------------------= - // MARK: Tests - //=------------------------------------------------------------------------= - - /// https://www.wolframalpha.com/input?i=y%3Dx%5E144%2C+x%3Dproduct+prime%28i%29%2C+i%3D1..54 - func testMultiplicationByBytePrimeComposite() throws { - func whereIs(_ type: T.Type) where T: ArbitraryInteger { - let small: T = primes54.lazy.map(T.init).reduce(1, *) - let large: T = Array.init(repeating: small, count: 143).reduce(small, &*) - Test().same(small, Array(repeating: small, count: 143).reduce(large, /)) - Test().same(large, Array(repeating: small.complement(), count: 144).reduce(1, &*)) - } - - #if DEBUG - throw XCTSkip("req. release mode") - #else - whereIs(InfiniInt.self) - whereIs(InfiniInt.self) - #endif - } - - func testMultiplicationLikeBigShift() { - func whereIs(_ type: T.Type, _ source: S.Type) where T: ArbitraryInteger, S: SystemsInteger & UnsignedInteger { - //=----------------------------------= - var lhs: T, rhs: T, pro: T - //=----------------------------------= - lhs = T((0 as S ..< 16).map({ $0 })) - rhs = T([S](repeating: S.min, count: 16) + [1] as [S]) - pro = lhs << (16 * IX(size: S.self)) - - Test().same(lhs.times(rhs), Fallible(pro)) - Test().same(rhs.times(lhs), Fallible(pro)) - #if !DEBUG - Test().multiplication(lhs, rhs, Fallible(pro)) - #endif - } - - for type in Self.types { - for source in typesAsCoreIntegerAsUnsigned { - whereIs(type, source) - } - } - } - - func testMultiplicationLikeBigSystemsInteger() { - func whereIs(_ type: T.Type, _ source: S.Type) where T: ArbitraryInteger, S: SystemsInteger & UnsignedInteger { - //=----------------------------------= - var lhs: T, rhs: T, pro: T, array = [S]() - //=----------------------------------= - // imagine: (U16.max - 0) * (U16.max - 0) - //=----------------------------------= - lhs = T([S](repeating: S.max, count: 16)) - rhs = T([S](repeating: S.max, count: 16)) - pro = T([ 1] as [S] + [S](repeating: 0, count: 15) + [~1] as [S] + [S](repeating: ~0, count: 15)) - - Test().same(lhs.times(rhs), Fallible(pro)) - Test().same(lhs.squared( ), Fallible(pro)) - #if !DEBUG - Test().multiplication(lhs, rhs, Fallible(pro)) - #endif - //=----------------------------------= - // imagine: (U16.max - 0) * (U16.max - 1) - //=----------------------------------= - lhs = T([S](repeating: S.max, count: 16)) - rhs = T([~1] as [S] + [S](repeating: ~0, count: 15)) - pro = T([ 2] as [S] + [S](repeating: 0, count: 15) + [~2] as [S] + [S](repeating: ~0, count: 15)) - - Test().same(lhs.times(rhs), Fallible(pro)) - Test().same(rhs.times(lhs), Fallible(pro)) - #if !DEBUG - Test().multiplication(lhs, rhs, Fallible(pro)) - #endif - //=----------------------------------= - // imagine: (U16.max - 1) * (U16.max - 1) - //=----------------------------------= - array += [ 4] as [S] - array += Array(repeating: 0, count: 15) - array += [~3] as [S] - array += Array(repeating: ~0, count: 15) - - lhs = T([~1] as [S] + [S](repeating: ~0, count: 15)) - rhs = T([~1] as [S] + [S](repeating: ~0, count: 15)) - pro = T(array) - array.removeAll() - - Test().same(lhs.times(rhs), Fallible(pro)) - Test().same(lhs.squared( ), Fallible(pro)) - #if !DEBUG - Test().multiplication(lhs, rhs, Fallible(pro)) - #endif - //=----------------------------------= - // imagine: (U16.max - 0) * (U8 .max - 0) - //=----------------------------------= - array += [ 1] as [S] - array += Array(repeating: 0, count: 07) - array += Array(repeating: ~0, count: 08) - array += [~1] as [S] - array += Array(repeating: ~0, count: 07) - - lhs = T([S](repeating: S.max, count: 16)) - rhs = T([S](repeating: S.max, count: 08)) - pro = T(array) - array.removeAll() - - Test().same(lhs.times(rhs), Fallible(pro)) - Test().same(rhs.times(lhs), Fallible(pro)) - #if !DEBUG - Test().multiplication(lhs, rhs, Fallible(pro)) - #endif - } - - for type in Self.types { - for source in typesAsCoreIntegerAsUnsigned { - whereIs(type, source) - } - } - } - - /// This checks all combinations for inputs in `I12.min...12.max`. - /// - /// - Note: Each product is compared against the result of addition. - /// - /// - Note: It uses 8-bit elements so some inputs use large storage. - /// - func testMultiplicationForEachSmallEntropyInRunnableRangeWhereElementIsByte() throws { - func whereIs(_ type: T.Type) where T: BinaryInteger { - var success: (value: IX, error: IX) - - success.value = IX.zero - success.error = IX.zero - - var major: (lhs: T, rhs: T, pro: T) - var minor: (lhs: T, rhs: T, pro: T) - - major.lhs = T(load: I16.min >> 4) - major.rhs = T(load: I16.min >> 4) - major.pro = T(1) << 22 - - let one = T(1) - let r12 = (I16.min >> 4) ... (I16.max >> 4) - - for i in r12 { - minor.lhs = major.lhs - minor.rhs = major.rhs - minor.pro = major.pro - - for j in r12 { - let a = (i < 0) && (j != 0 && j != 1) - let b = (j < 0) && (i != 0 && i != 1) - - let product = minor.lhs.times(minor.rhs) - if product.value == minor.pro { - success.value += 1 - } - - if product.error == (!T.isSigned && (a || b)) { - success.error += 1 - } - - minor.rhs &+= one - minor.pro &+= minor.lhs - } - - major.lhs &+= one - major.pro &+= major.rhs - } - - Test().same(success.value, 1 << 24) - Test().same(success.error, 1 << 24) - } - - #if DEBUG - throw XCTSkip("req. release mode") - #else - whereIs(InfiniInt.self) - whereIs(InfiniInt.self) - - whereIs(I64.self) // cf. InfiniInt.self - whereIs(U64.self) // cf. InfiniInt.self - #endif - } -} - -//=----------------------------------------------------------------------------= -// MARK: + Edge Cases -//=----------------------------------------------------------------------------= - -extension InfiniIntTests { - - //=------------------------------------------------------------------------= - // MARK: Tests - //=------------------------------------------------------------------------= - - /// - 2024-05-22: Checks the small-storage multiplication path. - func testMultiplicationBySmallStorageWhereBodyIsZerosAndAppendixIsOne() { - func whereIs(_ type: T.Type) where T: ArbitraryInteger, T.Element.BitPattern == U8.BitPattern { - compact: do { - Test().multiplication(~T(I8.max), 000, Fallible( 00000)) - Test().multiplication(~T(I8.max), 100, Fallible(~12799, error: !T.isSigned)) - Test().multiplication(~T(I8.max), ~100, Fallible( 12928, error: !T.isSigned)) - Test().multiplication(~T(I8.max), 127, Fallible(~16255, error: !T.isSigned)) - Test().multiplication(~T(I8.max), ~127, Fallible( 16384, error: !T.isSigned)) - } - - extended: do { - Test().multiplication(~T(U8.max), 000, Fallible( 00000)) // OK - Test().multiplication(~T(U8.max), 100, Fallible(~25599, error: !T.isSigned)) // :( - Test().multiplication(~T(U8.max), ~100, Fallible( 25856, error: !T.isSigned)) // :( - Test().multiplication(~T(U8.max), 256, Fallible(~65535, error: !T.isSigned)) // :( - Test().multiplication(~T(U8.max), ~256, Fallible( 65792, error: !T.isSigned)) // :( - } - } - - whereIs(InfiniInt.self) - whereIs(InfiniInt.self) - } - - /// - 2024-05-31: Checks the large-storage multiplication path. - func testMultiplicationByLargeStorageWhereBodyIsZerosAndAppendixIsOne() { - func whereIs(_ type: T.Type) where T: ArbitraryInteger, T.Element.BitPattern == U8.BitPattern { - compact: do { - let x16 = T(repeating: Bit.one ) << 15 - Test().multiplication(x16, x16 - 1, Fallible((1 << 30) &- x16, error: !T.isSigned)) - Test().multiplication(x16, x16, Fallible((1 << 30), error: !T.isSigned)) - Test().multiplication(x16, x16 + 1, Fallible((1 << 30) &+ x16, error: !T.isSigned)) - } - - extended: do { - let x16 = T(repeating: Bit.one ) << 16 - Test().multiplication(x16, x16 - 1, Fallible((1 << 32) &- x16, error: !T.isSigned)) // OK - Test().multiplication(x16, x16, Fallible((1 << 32), error: !T.isSigned)) // :( - Test().multiplication(x16, x16 + 1, Fallible((1 << 32) &+ x16, error: !T.isSigned)) // OK - } - } - - whereIs(InfiniInt.self) - whereIs(InfiniInt.self) - } - - /// - Note: The algorithms may special-case ascending zeros like an upshift. - func testMultiplicationByAscendingZeros() { - func whereIs(_ type: T.Type) where T: ArbitraryInteger { - typealias F = Fallible - //=----------------------------------= - let zeros: [[UX]] = (0 ..< 3).map({ - Array(repeating: UX.zero, count: $0) - }) - - for a: [UX] in zeros { - - let a0 = a + [ 1] as [UX] - let a1 = a + [ 0] as [UX] - let a2 = a + [~0] as [UX] - let a3 = a + [~1] as [UX] - - for b: [UX] in zeros { - - let b0 = b + [ 1] as [UX] - let b1 = b + [ 0] as [UX] - let b2 = b + [~0] as [UX] - let b3 = b + [~1] as [UX] - - let c: [UX] = a + b - - Test().multiplication(T(a0, repeating: Bit.zero), T(b0, repeating: Bit.zero), F(T(c + [ 1 ] as [UX], repeating: Bit.zero))) - Test().multiplication(T(a1, repeating: Bit.zero), T(b1, repeating: Bit.zero), F(T(c + [ 0 ] as [UX], repeating: Bit.zero))) - Test().multiplication(T(a2, repeating: Bit.zero), T(b2, repeating: Bit.zero), F(T(c + [ 1, ~1 ] as [UX], repeating: Bit.zero))) - Test().multiplication(T(a3, repeating: Bit.zero), T(b3, repeating: Bit.zero), F(T(c + [ 4, ~3 ] as [UX], repeating: Bit.zero))) - - Test().multiplication(T(a0, repeating: Bit.zero), T(b0, repeating: Bit.one ), F(T(c + [ 1 ] as [UX], repeating: Bit.one ), error: !T.isSigned && a.count > 0)) - Test().multiplication(T(a1, repeating: Bit.zero), T(b1, repeating: Bit.one ), F(T(c + [ 0 ] as [UX], repeating: Bit.zero))) - Test().multiplication(T(a2, repeating: Bit.zero), T(b2, repeating: Bit.one ), F(T(c + [ 1 ] as [UX], repeating: Bit.one ), error: !T.isSigned)) - Test().multiplication(T(a3, repeating: Bit.zero), T(b3, repeating: Bit.one ), F(T(c + [ 4, ~1, ] as [UX], repeating: Bit.one ), error: !T.isSigned)) - - Test().multiplication(T(a0, repeating: Bit.one ), T(b0, repeating: Bit.one ), F(T(c + [ 1, ~1 ] as [UX], repeating: Bit.zero), error: !T.isSigned)) - Test().multiplication(T(a1, repeating: Bit.one ), T(b1, repeating: Bit.one ), F(T(c + [ 0, 0, 1] as [UX], repeating: Bit.zero), error: !T.isSigned)) - Test().multiplication(T(a2, repeating: Bit.one ), T(b2, repeating: Bit.one ), F(T(c + [ 1 ] as [UX], repeating: Bit.zero), error: !T.isSigned)) - Test().multiplication(T(a3, repeating: Bit.one ), T(b3, repeating: Bit.one ), F(T(c + [ 4 ] as [UX], repeating: Bit.zero), error: !T.isSigned)) - } - } - } - - for type in Self.types { - whereIs(type) - } - } -} - -//=----------------------------------------------------------------------------= -// MARK: + Documentation -//=----------------------------------------------------------------------------= - -extension InfiniIntTests { - - //=------------------------------------------------------------------------= - // MARK: Tests - //=------------------------------------------------------------------------= - - func testReadmeMultiplicationDocumentationExamples() { - Test().multiplication(U32.max, U32.max, Fallible(Doublet(low: 1, high: ~1), error: true)) - Test().multiplication(UXL.max, UXL.max, Fallible(Doublet(low: 1, high: ~1), error: true)) - } -} diff --git a/Tests/StdlibIntKitTests/StdlibInt+Division.swift b/Tests/StdlibIntKitTests/StdlibInt+Division.swift index 63ecad34..965c2ab6 100644 --- a/Tests/StdlibIntKitTests/StdlibInt+Division.swift +++ b/Tests/StdlibIntKitTests/StdlibInt+Division.swift @@ -33,7 +33,7 @@ import TestKit2 // MARK: Tests //=------------------------------------------------------------------------= - @Test("StdlibInt vs StdlibInt.Base - [entropic]", .tags(.forwarding), arguments: fuzzers) + @Test("StdlibInt/division: vs StdlibInt.Base", .tags(.forwarding, .random), arguments: fuzzers) func forwarding(_ randomness: consuming FuzzerInt) { for _ in 0 ..< conditional(debug: 64, release: 128) { let dividend = IXL.entropic(through: Shift.max(or: 255), using: &randomness) diff --git a/Tests/StdlibIntKitTests/StdlibInt+Multiplication.swift b/Tests/StdlibIntKitTests/StdlibInt+Multiplication.swift index e0534230..78f2d2f9 100644 --- a/Tests/StdlibIntKitTests/StdlibInt+Multiplication.swift +++ b/Tests/StdlibIntKitTests/StdlibInt+Multiplication.swift @@ -8,6 +8,8 @@ //=----------------------------------------------------------------------------= import CoreKit +import RandomIntKit +import InfiniIntKit import StdlibIntKit import TestKit2 @@ -25,50 +27,26 @@ import TestKit2 /// /// - TODO: Test `StdlibInt` forwarding in generic `BinaryInteger` tests. /// -@Suite struct StdlibIntTestsOnMultiplication { +@Suite("StdlibInt/multiplication") struct StdlibIntTestsOnMultiplication { //=------------------------------------------------------------------------= // MARK: Tests //=------------------------------------------------------------------------= - @Test("StdlibInt - multiplication [forwarding]", arguments: [ - - (-2 as StdlibInt, -2 as StdlibInt, 4 as StdlibInt), - (-1 as StdlibInt, -2 as StdlibInt, 2 as StdlibInt), - ( 0 as StdlibInt, -2 as StdlibInt, 0 as StdlibInt), - ( 1 as StdlibInt, -2 as StdlibInt, -2 as StdlibInt), - ( 2 as StdlibInt, -2 as StdlibInt, -4 as StdlibInt), - - (-2 as StdlibInt, -1 as StdlibInt, 2 as StdlibInt), - (-1 as StdlibInt, -1 as StdlibInt, 1 as StdlibInt), - ( 0 as StdlibInt, -1 as StdlibInt, 0 as StdlibInt), - ( 1 as StdlibInt, -1 as StdlibInt, -1 as StdlibInt), - ( 2 as StdlibInt, -1 as StdlibInt, -2 as StdlibInt), - - (-2 as StdlibInt, 0 as StdlibInt, 0 as StdlibInt), - (-1 as StdlibInt, 0 as StdlibInt, 0 as StdlibInt), - ( 0 as StdlibInt, 0 as StdlibInt, 0 as StdlibInt), - ( 1 as StdlibInt, 0 as StdlibInt, 0 as StdlibInt), - ( 2 as StdlibInt, 0 as StdlibInt, 0 as StdlibInt), - - (-2 as StdlibInt, 1 as StdlibInt, -2 as StdlibInt), - (-1 as StdlibInt, 1 as StdlibInt, -1 as StdlibInt), - ( 0 as StdlibInt, 1 as StdlibInt, 0 as StdlibInt), - ( 1 as StdlibInt, 1 as StdlibInt, 1 as StdlibInt), - ( 2 as StdlibInt, 1 as StdlibInt, 2 as StdlibInt), - - (-2 as StdlibInt, 2 as StdlibInt, -4 as StdlibInt), - (-1 as StdlibInt, 2 as StdlibInt, -2 as StdlibInt), - ( 0 as StdlibInt, 2 as StdlibInt, 0 as StdlibInt), - ( 1 as StdlibInt, 2 as StdlibInt, 2 as StdlibInt), - ( 2 as StdlibInt, 2 as StdlibInt, 4 as StdlibInt), - - ] as [(StdlibInt, StdlibInt, StdlibInt)]) - func multiplication(lhs: StdlibInt, rhs: StdlibInt, expectation: StdlibInt) { - #expect(lhs * rhs == expectation) - #expect(rhs * lhs == expectation) - - #expect({ var x = lhs; x *= rhs; return x }() == expectation) - #expect({ var x = rhs; x *= lhs; return x }() == expectation) + @Test("StdlibInt/multiplication: vs StdlibInt.Base", .tags(.forwarding, .random), arguments: fuzzers) + func forwarding(_ randomness: consuming FuzzerInt) throws { + for _ in 0 ..< conditional(debug: 64, release: 128) { + let lhs = IXL.entropic(through: Shift.max(or: 255), using: &randomness) + let rhs = IXL.entropic(through: Shift.max(or: 255), using: &randomness) + + let a = StdlibInt(lhs) + let b = StdlibInt(rhs) + let c = a * b + + try #require(c == ( a * b )) + try #require(c == { var x = a; x *= b; return x }()) + + Ɣexpect(lhs, times: rhs, is: Fallible(IXL(c))) + } } } diff --git a/Tests/UltimathnumTests/BinaryInteger+Division.swift.swift b/Tests/UltimathnumTests/BinaryInteger+Division.swift.swift index b50ff358..aa0feb48 100644 --- a/Tests/UltimathnumTests/BinaryInteger+Division.swift.swift +++ b/Tests/UltimathnumTests/BinaryInteger+Division.swift.swift @@ -15,13 +15,13 @@ import TestKit2 // MARK: * Binary Integer x Division //*============================================================================* -@Suite("BinaryInteger/division(_:)") struct BinaryIntegerTestsOnDivision { +@Suite struct BinaryIntegerTestsOnDivision { //=------------------------------------------------------------------------= // MARK: Tests //=------------------------------------------------------------------------= - @Test("signs", .serialized, .tags(.documentation), arguments: [ + @Test("BinaryInteger/division - signs", .serialized, .tags(.documentation), arguments: [ (dividend: 7 as I8, divisor: 3 as I8, quotient: 2 as I8, remainder: 1 as I8), (dividend: 7 as I8, divisor: -3 as I8, quotient: -2 as I8, remainder: 1 as I8), @@ -48,8 +48,9 @@ import TestKit2 // MARK: Tests //=------------------------------------------------------------------------= - @Test("1-by-1 division for each 8-bit", .tags(.exhaustive), arguments: i8u8) - func division11ForEachEightBit(type: any SystemsInteger.Type) { + @Test("BinaryInteger/division: for each 8-bit integer pair [1-by-1]", + .tags(.exhaustive), arguments: i8u8) + func division11ForEachEightBitIntegerPair(type: any SystemsInteger.Type) { whereIs(type) func whereIs(_ type: T.Type) where T: SystemsInteger { @@ -69,7 +70,8 @@ import TestKit2 } } - @Test("1-by-1 division of random by random", arguments: typesAsBinaryInteger, fuzzers) + @Test("BinaryInteger/division: 1-by-1 division of random by random", + .tags(.random), arguments: typesAsBinaryInteger, fuzzers) func division11OfRandomByRandom(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -82,7 +84,8 @@ import TestKit2 } } - @Test("1-by-1 division of random by power-of-2-esque [entropic]", arguments: typesAsBinaryInteger, fuzzers) + @Test("BinaryInteger/division: random by power-of-2-esque [1-by-1]", + .tags(.random), arguments: typesAsBinaryInteger, fuzzers) func division11OfRandomByPowerOf2Esque(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -109,7 +112,8 @@ import TestKit2 } } - @Test("1-by-1 division ascending zeros by ascending zeros [entropic]", arguments: typesAsBinaryInteger, fuzzers) + @Test("BinaryInteger/division: ascending zeros by ascending zeros [1-by-1]", + .tags(.random), arguments: typesAsBinaryInteger, fuzzers) func division11OfAscendingZerosByAscendingZeros(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -127,7 +131,8 @@ import TestKit2 } } - @Test("1-by-1 division of contiguous ones by contiguous ones", arguments: typesAsBinaryInteger, fuzzers) + @Test("BinaryInteger/division: contiguous ones by contiguous ones [1-by-1]", + .tags(.random), arguments: typesAsBinaryInteger, fuzzers) func division11OfContiguousOnesByContiguousOnes(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -145,7 +150,8 @@ import TestKit2 } } - @Test("1-by-1 division of silly big by silly big [entropic]", arguments: typesAsArbitraryInteger, fuzzers) + @Test("BinaryInteger/division: silly big by silly big [1-by-1]", + .tags(.random), arguments: typesAsArbitraryInteger, fuzzers) func division11OfSillyBigBySillyBig(type: any ArbitraryInteger.Type, randomness: consuming FuzzerInt) throws { try whereIs(type) @@ -170,7 +176,8 @@ import TestKit2 // MARK: Tests x 2 by 1 //=------------------------------------------------------------------------= - @Test("2-by-1 division of random by random [entropic]", arguments: typesAsSystemsInteger, fuzzers) + @Test("BinaryInteger/division: random by random [2-by-1]", + .tags(.random), arguments: typesAsSystemsInteger, fuzzers) func division21OfRandomByRandom(type: any SystemsInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -185,7 +192,8 @@ import TestKit2 } } - @Test("2-by-1 division of random by power-of-2-esque [entropic]", arguments: typesAsSystemsInteger, fuzzers) + @Test("BinaryInteger/division: random by power-of-2-esque [2-by-1]", + .tags(.random), arguments: typesAsSystemsInteger, fuzzers) func division21OfRandomByPowerOf2Esque(type: any SystemsInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -206,7 +214,8 @@ import TestKit2 } } - @Test("2-by-1 division of ascending zeros by ascending zeros [entropic]", arguments: typesAsSystemsInteger, fuzzers) + @Test("BinaryInteger/division: ascending zeros by ascending zeros [2-by-1]", + .tags(.random), arguments: typesAsSystemsInteger, fuzzers) func division21OfAscendingZerosByAscendingZeros(type: any SystemsInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -225,7 +234,8 @@ import TestKit2 } } - @Test("2-by-1 division of one half by random [entropic]", arguments: typesAsSystemsInteger, fuzzers) + @Test("BinaryInteger/division: one half by random [2-by-1]", + .tags(.random), arguments: typesAsSystemsInteger, fuzzers) func division21OfOneHalfByRandom(type: any SystemsInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -249,14 +259,14 @@ import TestKit2 // MARK: * Binary Integer x Division x Edge Cases //*============================================================================* -@Suite("BinaryInteger/division(_:) - edge cases", .tags(.documentation)) -struct BinaryIntegerTestsOnDivisionEdgeCases { +@Suite(.tags(.documentation)) struct BinaryIntegerTestsOnDivisionEdgeCases { //=------------------------------------------------------------------------= // MARK: Tests x 1 by 1 //=------------------------------------------------------------------------= - @Test("1-by-1 division of random by zero is nil [entropic]", arguments: typesAsBinaryInteger, fuzzers) + @Test("BinaryInteger/division: random by zero is nil [1-by-1]", + .tags(.random), arguments: typesAsBinaryInteger, fuzzers) func division11OfRandomByZeroIsNil(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -268,7 +278,8 @@ struct BinaryIntegerTestsOnDivisionEdgeCases { } } - @Test("1-by-1 division of T.min by -1 as signed systems integers is error", arguments: typesAsSystemsIntegerAsSigned) + @Test("BinaryInteger/division: T.min by -1 as signed systems integers is error [1-by-1]", + .tags(.exhaustive), arguments: typesAsSystemsIntegerAsSigned) func division11OfMinByNegativeOneAsSignedSystemsIntegerIsError(type: any SystemsIntegerAsSigned.Type) { whereIs(type) @@ -278,7 +289,8 @@ struct BinaryIntegerTestsOnDivisionEdgeCases { } } - @Test("1-by-1 division of finite by infinite is trivial [uniform]", arguments: typesAsArbitraryIntegerAsUnsigned, fuzzers) + @Test("BinaryInteger/division: finite by infinite is trivial [1-by-1]", + .tags(.random), arguments: typesAsArbitraryIntegerAsUnsigned, fuzzers) func division11OfFiniteByInfiniteIsTrivial(type: any ArbitraryIntegerAsUnsigned.Type, randomness: consuming FuzzerInt) throws { try whereIs(type) @@ -296,7 +308,8 @@ struct BinaryIntegerTestsOnDivisionEdgeCases { } } - @Test("1-by-1 division of infinite by finite is error like signed [uniform]", arguments: typesAsArbitraryIntegerAsUnsigned, fuzzers) + @Test("BinaryInteger/division: infinite by finite is error like signed [2-by-1]", + .tags(.random), arguments: typesAsArbitraryIntegerAsUnsigned, fuzzers) func division11OfInfiniteByFiniteIsErrorLikeSigned(type: any ArbitraryIntegerAsUnsigned.Type, randomness: consuming FuzzerInt) throws { try whereIs(type) @@ -325,7 +338,8 @@ struct BinaryIntegerTestsOnDivisionEdgeCases { // MARK: Tests x 2 by 1 //=------------------------------------------------------------------------= - @Test("2-by-1 division of random by zero is nil [entropic]", arguments: typesAsSystemsInteger, fuzzers) + @Test("BinaryInteger/division: random by zero is nil [2-by-1]", + .tags(.random), arguments: typesAsSystemsInteger, fuzzers) func division21OfRandomByZeroIsNil(type: any SystemsInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -344,14 +358,14 @@ struct BinaryIntegerTestsOnDivisionEdgeCases { // MARK: * Binary Integer x Division x Recovery Mechanisms //*============================================================================* -@Suite("BinaryInteger/division(_:) - recovery mechanisms", .tags(.recoverable)) -struct BinaryIntegerTestsOnDivisionRecoveryMechanisms { +@Suite(.tags(.recoverable)) struct BinaryIntegerTestsOnDivisionRecoveryMechanisms { //=------------------------------------------------------------------------= // MARK: Tests //=------------------------------------------------------------------------= - @Test("division error propagation as binary integer [entropic]", arguments: typesAsBinaryInteger, fuzzers) + @Test("BinaryInteger/division: division error propagation as binary integer", + .tags(.random), arguments: typesAsBinaryInteger, fuzzers) func divisionErrorPropagationAsBinaryInteger(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -377,7 +391,8 @@ struct BinaryIntegerTestsOnDivisionRecoveryMechanisms { } } - @Test("division error propagation as natural integer [entropic]", arguments: typesAsSystemsIntegerAsUnsigned, fuzzers) + @Test("BinaryInteger/division: division error propagation as natural integer", + .tags(.random), arguments: typesAsSystemsIntegerAsUnsigned, fuzzers) func divisionErrorPropagationAsNaturalInteger(type: any SystemsIntegerAsUnsigned.Type, randomness: consuming FuzzerInt) { whereIs(type) @@ -403,8 +418,7 @@ struct BinaryIntegerTestsOnDivisionRecoveryMechanisms { // MARK: * Binary Integer x Division x Open Source Issues //*============================================================================* -@Suite("BinaryInteger/division - open source issues", .tags(.opensource)) -struct BinaryIntegerTestsOnDivisionOpenSourceIssues { +@Suite(.tags(.opensource)) struct BinaryIntegerTestsOnDivisionOpenSourceIssues { //=------------------------------------------------------------------------= // MARK: Tests @@ -414,7 +428,8 @@ struct BinaryIntegerTestsOnDivisionOpenSourceIssues { /// /// - Note: Checks whether the 3212-path knows when the quotient fits. /// - @Test(arguments: typesAsBinaryInteger) func sourceIsGitHubOscbysproNumberickIssues101(_ type: any BinaryInteger.Type) throws { + @Test(arguments: typesAsBinaryInteger) + func sourceIsGitHubOscbysproNumberickIssues101(_ type: any BinaryInteger.Type) throws { try whereIs(type) func whereIs(_ type: T.Type) throws where T: BinaryInteger { @@ -432,7 +447,8 @@ struct BinaryIntegerTestsOnDivisionOpenSourceIssues { /// /// - Note: Said to crash and/or return incorrect results. /// - @Test(arguments: typesAsBinaryInteger) func sourceIsGitHubAppleSwiftNumericsIssues272(_ type: any BinaryInteger.Type) throws { + @Test(arguments: typesAsBinaryInteger) + func sourceIsGitHubAppleSwiftNumericsIssues272(_ type: any BinaryInteger.Type) throws { try whereIs(type) func whereIs(_ type: T.Type) throws where T: BinaryInteger { diff --git a/Tests/UltimathnumTests/BinaryInteger+Multiplication.swift b/Tests/UltimathnumTests/BinaryInteger+Multiplication.swift index 9d334ee7..84166a61 100644 --- a/Tests/UltimathnumTests/BinaryInteger+Multiplication.swift +++ b/Tests/UltimathnumTests/BinaryInteger+Multiplication.swift @@ -9,205 +9,123 @@ import CoreKit import DoubleIntKit -import InfiniIntKit import RandomIntKit -import TestKit +import TestKit2 //*============================================================================* // MARK: * Binary Integer x Multiplication //*============================================================================* -final class BinaryIntegerTestsOnMultiplication: XCTestCase { +@Suite("BinaryInteger/multiplication") struct BinaryIntegerTestsOnMultiplication { //=------------------------------------------------------------------------= // MARK: Tests //=------------------------------------------------------------------------= - func testMultiplicationOfMsbEsque() { - func whereIsBinaryInteger(_ type: T.Type) where T: BinaryInteger { - //=----------------------------------= - let shl = Esque.shl - let msb = Esque.msb - let bot = Esque.bot - let xor = T(repeating: Bit(T.isSigned)) - let add = T(Bit(T.isSigned)) - //=----------------------------------= - Test().multiplication(msb, msb, Fallible(((msb ) << shl) ^ xor &+ add, error: !T.isArbitrary)) - Test().multiplication(msb, bot, Fallible(((bot ) << shl) ^ xor &+ add, error: !T.isArbitrary)) - Test().multiplication(bot, msb, Fallible(((bot ) << shl) ^ xor &+ add, error: !T.isArbitrary)) - Test().multiplication(bot, bot, Fallible(((bot ^ 1) << shl) | 0000000001, error: !T.isArbitrary)) - //=----------------------------------= - Test().multiplication(bot, ~4, Fallible(0 &- (bot << 2) &- bot, error: T.isEdgy)) - Test().multiplication(bot, ~3, Fallible(0 &- (bot << 2), error: T.isEdgy)) - Test().multiplication(bot, ~2, Fallible(0 &- (bot << 1) &- bot, error: T.isEdgy)) - Test().multiplication(bot, ~1, Fallible(0 &- (bot << 1), error: T.isEdgy)) - Test().multiplication(bot, ~0, Fallible(0 &- (bot), error: !T.isSigned)) - Test().multiplication(bot, 0, Fallible( (000))) - Test().multiplication(bot, 1, Fallible( (bot))) - Test().multiplication(bot, 2, Fallible( (bot << 1), error: !T.isArbitrary && T.isSigned)) - Test().multiplication(bot, 3, Fallible( (bot << 1) &+ bot, error: !T.isArbitrary)) - Test().multiplication(bot, 4, Fallible( (bot << 2), error: !T.isArbitrary)) - - Test().multiplication(msb, ~4, Fallible(0 &- (msb << 2) &- msb, error: T.isEdgy)) - Test().multiplication(msb, ~3, Fallible(0 &- (msb << 2), error: T.isEdgy)) - Test().multiplication(msb, ~2, Fallible(0 &- (msb << 1) &- msb, error: T.isEdgy)) - Test().multiplication(msb, ~1, Fallible(0 &- (msb << 1), error: T.isEdgy)) - Test().multiplication(msb, ~0, Fallible(0 &- (msb), error: T.isEdgy)) - Test().multiplication(msb, 0, Fallible( (000))) - Test().multiplication(msb, 1, Fallible( (msb))) - Test().multiplication(msb, 2, Fallible( (msb << 1), error: !T.isArbitrary)) - Test().multiplication(msb, 3, Fallible( (msb << 1) &+ msb, error: !T.isArbitrary)) - Test().multiplication(msb, 4, Fallible( (msb << 2), error: !T.isArbitrary)) - } + @Test("BinaryInteger/multiplication: 1-by-1-as-2 for low entropies as signed", .serialized, arguments: [ - func whereIsSystemsInteger(_ type: T.Type) where T: SystemsInteger { - typealias M = T.Magnitude - typealias D = Doublet - typealias F = Fallible - //=----------------------------------= - let msb = T.msb - let bot = T.msb.toggled() - //=----------------------------------= - always: do { - Test().multiplication(msb, msb, F(D(low: 0000 as M, high: T(raw: M.msb >> 1)), error: true)) - Test().multiplication(msb, bot, F(D(low: M.msb, high: T(raw: msb >> 1) - T(Bit(!T.isSigned))), error: true)) - Test().multiplication(bot, msb, F(D(low: M.msb, high: T(raw: msb >> 1) - T(Bit(!T.isSigned))), error: true)) - Test().multiplication(bot, bot, F(D(low: 0001 as M, high: T(raw: ~M.msb >> 1)), error: true)) - }; if T.isSigned { - Test().multiplication(bot, ~4, F(D(low: M.msb + 5, high: ~02 as T), error: true)) - Test().multiplication(bot, ~3, F(D(low: 0004 as M, high: ~01 as T), error: true)) - Test().multiplication(bot, ~2, F(D(low: M.msb + 3, high: ~01 as T), error: true)) - Test().multiplication(bot, ~1, F(D(low: 0002 as M, high: ~00 as T), error: true)) - Test().multiplication(bot, ~0, F(D(low: M.msb + 1, high: ~00 as T))) - Test().multiplication(bot, 0, F(D(low: 0000 as M, high: 00 as T))) - Test().multiplication(bot, 1, F(D(low: ~M.msb - 0, high: 00 as T))) - Test().multiplication(bot, 2, F(D(low: ~0001 as M, high: 00 as T), error: true)) - Test().multiplication(bot, 3, F(D(low: ~M.msb - 2, high: 01 as T), error: true)) - Test().multiplication(bot, 4, F(D(low: ~0003 as M, high: 01 as T), error: true)) - - Test().multiplication(msb, ~4, F(D(low: M.msb, high: 02 as T), error: true)) - Test().multiplication(msb, ~3, F(D(low: 0000 as M, high: 02 as T), error: true)) - Test().multiplication(msb, ~2, F(D(low: M.msb, high: 01 as T), error: true)) - Test().multiplication(msb, ~1, F(D(low: 0000 as M, high: 01 as T), error: true)) - Test().multiplication(msb, ~0, F(D(low: M.msb, high: 00 as T), error: true)) - Test().multiplication(msb, 0, F(D(low: 0000 as M, high: 00 as T))) - Test().multiplication(msb, 1, F(D(low: M.msb, high: ~00 as T))) - Test().multiplication(msb, 2, F(D(low: 0000 as M, high: ~00 as T), error: true)) - Test().multiplication(msb, 3, F(D(low: M.msb, high: ~01 as T), error: true)) - Test().multiplication(msb, 4, F(D(low: 0000 as M, high: ~01 as T), error: true)) - } else { - Test().multiplication(bot, ~4, F(D(low: M.msb + 5, high: bot - 3), error: true)) - Test().multiplication(bot, ~3, F(D(low: 0004 as M, high: bot - 2), error: true)) - Test().multiplication(bot, ~2, F(D(low: M.msb + 3, high: bot - 2), error: true)) - Test().multiplication(bot, ~1, F(D(low: 0002 as M, high: bot - 1), error: true)) - Test().multiplication(bot, ~0, F(D(low: M.msb + 1, high: bot - 1), error: true)) - Test().multiplication(bot, 0, F(D(low: 0000 as M, high: 00 as T))) - Test().multiplication(bot, 1, F(D(low: ~M.msb - 0, high: 00 as T))) - Test().multiplication(bot, 2, F(D(low: ~0001 as M, high: 00 as T))) - Test().multiplication(bot, 3, F(D(low: ~M.msb - 2, high: 01 as T), error: true)) - Test().multiplication(bot, 4, F(D(low: ~0003 as M, high: 01 as T), error: true)) - - Test().multiplication(msb, ~4, F(D(low: M.msb, high: bot - 2), error: true)) - Test().multiplication(msb, ~3, F(D(low: 0000 as M, high: bot - 1), error: true)) - Test().multiplication(msb, ~2, F(D(low: M.msb, high: bot - 1), error: true)) - Test().multiplication(msb, ~1, F(D(low: 0000 as M, high: bot - 0), error: true)) - Test().multiplication(msb, ~0, F(D(low: M.msb, high: bot - 0), error: true)) - Test().multiplication(msb, 0, F(D(low: 0000 as M, high: 00 as T))) - Test().multiplication(msb, 1, F(D(low: M.msb, high: 00 as T))) - Test().multiplication(msb, 2, F(D(low: 0000 as M, high: 01 as T), error: true)) - Test().multiplication(msb, 3, F(D(low: M.msb, high: 01 as T), error: true)) - Test().multiplication(msb, 4, F(D(low: 0000 as M, high: 02 as T), error: true)) - } - } + (lhs: ~2 as I8, rhs: ~2 as I8, low: 9 as I8, high: 0 as I8), + (lhs: ~2 as I8, rhs: ~1 as I8, low: 6 as I8, high: 0 as I8), + (lhs: ~2 as I8, rhs: ~0 as I8, low: 3 as I8, high: 0 as I8), + (lhs: ~2 as I8, rhs: 0 as I8, low: 0 as I8, high: 0 as I8), + (lhs: ~2 as I8, rhs: 1 as I8, low: ~2 as I8, high: ~0 as I8), + (lhs: ~2 as I8, rhs: 2 as I8, low: ~5 as I8, high: ~0 as I8), + + (lhs: ~1 as I8, rhs: ~2 as I8, low: 6 as I8, high: 0 as I8), + (lhs: ~1 as I8, rhs: ~1 as I8, low: 4 as I8, high: 0 as I8), + (lhs: ~1 as I8, rhs: ~0 as I8, low: 2 as I8, high: 0 as I8), + (lhs: ~1 as I8, rhs: 0 as I8, low: 0 as I8, high: 0 as I8), + (lhs: ~1 as I8, rhs: 1 as I8, low: ~1 as I8, high: ~0 as I8), + (lhs: ~1 as I8, rhs: 2 as I8, low: ~3 as I8, high: ~0 as I8), - for type in typesAsBinaryInteger { - whereIsBinaryInteger(type) + (lhs: ~0 as I8, rhs: ~2 as I8, low: 3 as I8, high: 0 as I8), + (lhs: ~0 as I8, rhs: ~1 as I8, low: 2 as I8, high: 0 as I8), + (lhs: ~0 as I8, rhs: ~0 as I8, low: 1 as I8, high: 0 as I8), + (lhs: ~0 as I8, rhs: 0 as I8, low: 0 as I8, high: 0 as I8), + (lhs: ~0 as I8, rhs: 1 as I8, low: ~0 as I8, high: ~0 as I8), + (lhs: ~0 as I8, rhs: 2 as I8, low: ~1 as I8, high: ~0 as I8), + + (lhs: 0 as I8, rhs: ~2 as I8, low: 0 as I8, high: 0 as I8), + (lhs: 0 as I8, rhs: ~1 as I8, low: 0 as I8, high: 0 as I8), + (lhs: 0 as I8, rhs: ~0 as I8, low: 0 as I8, high: 0 as I8), + (lhs: 0 as I8, rhs: 0 as I8, low: 0 as I8, high: 0 as I8), + (lhs: 0 as I8, rhs: 1 as I8, low: 0 as I8, high: 0 as I8), + (lhs: 0 as I8, rhs: 2 as I8, low: 0 as I8, high: 0 as I8), + + (lhs: 1 as I8, rhs: ~2 as I8, low: ~2 as I8, high: ~0 as I8), + (lhs: 1 as I8, rhs: ~1 as I8, low: ~1 as I8, high: ~0 as I8), + (lhs: 1 as I8, rhs: ~0 as I8, low: ~0 as I8, high: ~0 as I8), + (lhs: 1 as I8, rhs: 0 as I8, low: 0 as I8, high: 0 as I8), + (lhs: 1 as I8, rhs: 1 as I8, low: 1 as I8, high: 0 as I8), + (lhs: 1 as I8, rhs: 2 as I8, low: 2 as I8, high: 0 as I8), + + (lhs: 2 as I8, rhs: ~2 as I8, low: ~5 as I8, high: ~0 as I8), + (lhs: 2 as I8, rhs: ~1 as I8, low: ~3 as I8, high: ~0 as I8), + (lhs: 2 as I8, rhs: ~0 as I8, low: ~1 as I8, high: ~0 as I8), + (lhs: 2 as I8, rhs: 0 as I8, low: 0 as I8, high: 0 as I8), + (lhs: 2 as I8, rhs: 1 as I8, low: 2 as I8, high: 0 as I8), + (lhs: 2 as I8, rhs: 2 as I8, low: 4 as I8, high: 0 as I8), + + + ] as [(lhs: I8, rhs: I8, low: I8, high: I8)]) + func multiplicationOfLowEntropiesAsFullWidthAsSigned(lhs: I8, rhs: I8, low: I8, high: I8) throws { + for type in typesAsBinaryIntegerAsSigned { + try whereIs(type) } - for type in typesAsSystemsInteger { - whereIsSystemsInteger(type) + func whereIs(_ type: T.Type) throws where T: SignedInteger { + Ɣexpect(T(load: lhs), times: T(load: rhs), is: T(load: low).veto(false), and: T(load: high)) } } - func testMultiplicationOfRepeatingBit() { - func whereIsBinaryInteger(_ type : T.Type) where T: BinaryInteger { - typealias F = Fallible - //=----------------------------------= - let x0 = T(repeating: Bit.zero) - let x1 = T(repeating: Bit.one) - //=----------------------------------= - for multiplier in [4, 3, 2, 1, 0, ~0, ~1, ~2, ~3, ~4] as [T] { - Test().multiplication(x0, multiplier, Fallible(x0)) - } - - if T.isSigned { - Test().multiplication(x1, ~4 as T, F( 5 as T)) - Test().multiplication(x1, ~3 as T, F( 4 as T)) - Test().multiplication(x1, ~2 as T, F( 3 as T)) - Test().multiplication(x1, ~1 as T, F( 2 as T)) - Test().multiplication(x1, ~0 as T, F( 1 as T)) - Test().multiplication(x1, 0 as T, F( 0 as T)) - Test().multiplication(x1, 1 as T, F(~0 as T)) - Test().multiplication(x1, 2 as T, F(~1 as T)) - Test().multiplication(x1, 3 as T, F(~2 as T)) - Test().multiplication(x1, 4 as T, F(~3 as T)) - } else { - Test().multiplication(x1, ~4 as T, F( 5 as T, error: true)) - Test().multiplication(x1, ~3 as T, F( 4 as T, error: true)) - Test().multiplication(x1, ~2 as T, F( 3 as T, error: true)) - Test().multiplication(x1, ~1 as T, F( 2 as T, error: true)) - Test().multiplication(x1, ~0 as T, F( 1 as T, error: true)) - Test().multiplication(x1, 0 as T, F( 0 as T)) - Test().multiplication(x1, 1 as T, F(~0 as T)) - Test().multiplication(x1, 2 as T, F(~1 as T, error: true)) - Test().multiplication(x1, 3 as T, F(~2 as T, error: true)) - Test().multiplication(x1, 4 as T, F(~3 as T, error: true)) - } - } + @Test("BinaryInteger/multiplication: 1-by-1-as-2 for low entropies as unsigned", .serialized, arguments: [ - func whereIsSystemsInteger(_ type: T.Type) where T: SystemsInteger { - typealias M = T.Magnitude - typealias D = Doublet - typealias F = Fallible - //=----------------------------------= - let x0 = T(repeating: Bit.zero) - let x1 = T(repeating: Bit.one ) - //=----------------------------------= - for multiplier in [4, 3, 2, 1, 0, ~0, ~1, ~2, ~3, ~4] as [T] { - Test().multiplication(x0, multiplier, F(D(low: T.Magnitude(raw: x0), high: x0))) - } - - if T.isSigned { - Test().multiplication(x1, ~4 as T, F(D(low: 5 as M, high: 0 as T))) - Test().multiplication(x1, ~3 as T, F(D(low: 4 as M, high: 0 as T))) - Test().multiplication(x1, ~2 as T, F(D(low: 3 as M, high: 0 as T))) - Test().multiplication(x1, ~1 as T, F(D(low: 2 as M, high: 0 as T))) - Test().multiplication(x1, ~0 as T, F(D(low: 1 as M, high: 0 as T))) - Test().multiplication(x1, 0 as T, F(D(low: 0 as M, high: 0 as T))) - Test().multiplication(x1, 1 as T, F(D(low: ~0 as M, high: ~0 as T))) - Test().multiplication(x1, 2 as T, F(D(low: ~1 as M, high: ~0 as T))) - Test().multiplication(x1, 3 as T, F(D(low: ~2 as M, high: ~0 as T))) - Test().multiplication(x1, 4 as T, F(D(low: ~3 as M, high: ~0 as T))) - } else { - Test().multiplication(x1, ~4 as T, F(D(low: 5 as M, high: ~5 as T), error: true)) - Test().multiplication(x1, ~3 as T, F(D(low: 4 as M, high: ~4 as T), error: true)) - Test().multiplication(x1, ~2 as T, F(D(low: 3 as M, high: ~3 as T), error: true)) - Test().multiplication(x1, ~1 as T, F(D(low: 2 as M, high: ~2 as T), error: true)) - Test().multiplication(x1, ~0 as T, F(D(low: 1 as M, high: ~1 as T), error: true)) - Test().multiplication(x1, 0 as T, F(D(low: 0 as M, high: 0 as T))) - Test().multiplication(x1, 1 as T, F(D(low: ~0 as M, high: 0 as T))) - Test().multiplication(x1, 2 as T, F(D(low: ~1 as M, high: 1 as T), error: true)) - Test().multiplication(x1, 3 as T, F(D(low: ~2 as M, high: 2 as T), error: true)) - Test().multiplication(x1, 4 as T, F(D(low: ~3 as M, high: 3 as T), error: true)) - } - } + (lhs: ~5 as I8, rhs: ~5 as I8, low: 36 as I8, high: ~11 as I8, error: true), + (lhs: ~5 as I8, rhs: ~4 as I8, low: 30 as I8, high: ~10 as I8, error: true), + (lhs: ~5 as I8, rhs: ~3 as I8, low: 24 as I8, high: ~9 as I8, error: true), + (lhs: ~5 as I8, rhs: ~2 as I8, low: 18 as I8, high: ~8 as I8, error: true), + (lhs: ~5 as I8, rhs: ~1 as I8, low: 12 as I8, high: ~7 as I8, error: true), + (lhs: ~5 as I8, rhs: ~0 as I8, low: 6 as I8, high: ~6 as I8, error: true), + (lhs: ~5 as I8, rhs: 0 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: ~5 as I8, rhs: 1 as I8, low: ~5 as I8, high: 0 as I8, error: false), + (lhs: ~5 as I8, rhs: 2 as I8, low: ~11 as I8, high: 1 as I8, error: true), + (lhs: ~5 as I8, rhs: 3 as I8, low: ~17 as I8, high: 2 as I8, error: true), + (lhs: ~5 as I8, rhs: 4 as I8, low: ~23 as I8, high: 3 as I8, error: true), + (lhs: ~5 as I8, rhs: 5 as I8, low: ~29 as I8, high: 4 as I8, error: true), + + (lhs: 0 as I8, rhs: ~5 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 0 as I8, rhs: ~3 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 0 as I8, rhs: ~2 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 0 as I8, rhs: ~1 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 0 as I8, rhs: ~0 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 0 as I8, rhs: 0 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 0 as I8, rhs: 1 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 0 as I8, rhs: 2 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 0 as I8, rhs: 4 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 0 as I8, rhs: 5 as I8, low: 0 as I8, high: 0 as I8, error: false), + + (lhs: 5 as I8, rhs: ~5 as I8, low: ~29 as I8, high: 4 as I8, error: true), + (lhs: 5 as I8, rhs: ~4 as I8, low: ~24 as I8, high: 4 as I8, error: true), + (lhs: 5 as I8, rhs: ~3 as I8, low: ~19 as I8, high: 4 as I8, error: true), + (lhs: 5 as I8, rhs: ~2 as I8, low: ~14 as I8, high: 4 as I8, error: true), + (lhs: 5 as I8, rhs: ~1 as I8, low: ~9 as I8, high: 4 as I8, error: true), + (lhs: 5 as I8, rhs: ~0 as I8, low: ~4 as I8, high: 4 as I8, error: true), + (lhs: 5 as I8, rhs: 0 as I8, low: 0 as I8, high: 0 as I8, error: false), + (lhs: 5 as I8, rhs: 1 as I8, low: 5 as I8, high: 0 as I8, error: false), + (lhs: 5 as I8, rhs: 2 as I8, low: 10 as I8, high: 0 as I8, error: false), + (lhs: 5 as I8, rhs: 3 as I8, low: 15 as I8, high: 0 as I8, error: false), + (lhs: 5 as I8, rhs: 4 as I8, low: 20 as I8, high: 0 as I8, error: false), + (lhs: 5 as I8, rhs: 5 as I8, low: 25 as I8, high: 0 as I8, error: false), - for type in typesAsBinaryInteger { - whereIsBinaryInteger(type) + + ] as [(lhs: I8, rhs: I8, low: I8, high: I8, error: Bool)]) + func multiplicationOfLowEntropiesAsFullWidthAsUnsigned(lhs: I8, rhs: I8, low: I8, high: I8, error: Bool) throws { + for type in typesAsBinaryIntegerAsUnsigned { + try whereIs(type) } - for type in typesAsSystemsInteger { - whereIsSystemsInteger(type) + func whereIs(_ type: T.Type) throws where T: UnsignedInteger { + Ɣexpect(T(load: lhs), times: T(load: rhs), is: T(load: low).veto(error), and: T(load: high)) } } @@ -215,244 +133,194 @@ final class BinaryIntegerTestsOnMultiplication: XCTestCase { // MARK: Tests //=------------------------------------------------------------------------= - func testMultiplication112OfLowEntropies() { - func AsSigned(_ type: T.Type) where T: SignedInteger { - Test().multiplication(~2 as T, ~2 as T, Fallible(Doublet(low: 9, high: 0))) - Test().multiplication(~2 as T, ~1 as T, Fallible(Doublet(low: 6, high: 0))) - Test().multiplication(~2 as T, ~0 as T, Fallible(Doublet(low: 3, high: 0))) - Test().multiplication(~2 as T, 0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication(~2 as T, 1 as T, Fallible(Doublet(low: ~2, high: ~0))) - Test().multiplication(~2 as T, 2 as T, Fallible(Doublet(low: ~5, high: ~0))) - - Test().multiplication(~1 as T, ~2 as T, Fallible(Doublet(low: 6, high: 0))) - Test().multiplication(~1 as T, ~1 as T, Fallible(Doublet(low: 4, high: 0))) - Test().multiplication(~1 as T, ~0 as T, Fallible(Doublet(low: 2, high: 0))) - Test().multiplication(~1 as T, 0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication(~1 as T, 1 as T, Fallible(Doublet(low: ~1, high: ~0))) - Test().multiplication(~1 as T, 2 as T, Fallible(Doublet(low: ~3, high: ~0))) - - Test().multiplication(~0 as T, ~2 as T, Fallible(Doublet(low: 3, high: 0))) - Test().multiplication(~0 as T, ~1 as T, Fallible(Doublet(low: 2, high: 0))) - Test().multiplication(~0 as T, ~0 as T, Fallible(Doublet(low: 1, high: 0))) - Test().multiplication(~0 as T, 0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication(~0 as T, 1 as T, Fallible(Doublet(low: ~0, high: ~0))) - Test().multiplication(~0 as T, 2 as T, Fallible(Doublet(low: ~1, high: ~0))) - - Test().multiplication( 0 as T, ~2 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, ~1 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, ~0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, 0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, 1 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, 2 as T, Fallible(Doublet(low: 0, high: 0))) - - Test().multiplication( 1 as T, ~2 as T, Fallible(Doublet(low: ~2, high: ~0))) - Test().multiplication( 1 as T, ~1 as T, Fallible(Doublet(low: ~1, high: ~0))) - Test().multiplication( 1 as T, ~0 as T, Fallible(Doublet(low: ~0, high: ~0))) - Test().multiplication( 1 as T, 0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 1 as T, 1 as T, Fallible(Doublet(low: 1, high: 0))) - Test().multiplication( 1 as T, 2 as T, Fallible(Doublet(low: 2, high: 0))) + @Test("BinaryInteger/multiplication: random by random", .tags(.random), arguments: typesAsBinaryInteger, fuzzers) + func multiplicationOfRandomByRandom(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) throws { + try whereIs(type) + + func whereIs(_ type: T.Type) throws where T: BinaryInteger { + let size = IX(size: T.self) ?? conditional(debug: 256, release: 4096) + let rounds = !T.isArbitrary ? conditional(debug: 128, release: 1024) : 16 - Test().multiplication( 2 as T, ~2 as T, Fallible(Doublet(low: ~5, high: ~0))) - Test().multiplication( 2 as T, ~1 as T, Fallible(Doublet(low: ~3, high: ~0))) - Test().multiplication( 2 as T, ~0 as T, Fallible(Doublet(low: ~1, high: ~0))) - Test().multiplication( 2 as T, 0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 2 as T, 1 as T, Fallible(Doublet(low: 2, high: 0))) - Test().multiplication( 2 as T, 2 as T, Fallible(Doublet(low: 4, high: 0))) + for _ in 0 ..< rounds { + let a = T.entropic(size: size, using: &randomness) + let b = T.entropic(size: size, using: &randomness) + let c = T.entropic(size: size, using: &randomness) + let d = T.entropic(size: size, using: &randomness) + let e = (a &+ b) &* (c &+ d) + let f = (a &* c) &+ (a &* d) &+ (b &* c) &+ (b &* d) + try #require(e == f) + } } + } + + @Test("BinaryInteger/multiplication: random by itself", .tags(.random), arguments: typesAsBinaryInteger, fuzzers) + func multiplicationOfRandomByItself(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) throws { + try whereIs(type) - func AsUnsigned(_ type: T.Type) where T: UnsignedInteger { - Test().multiplication(~5 as T, ~5 as T, Fallible(Doublet(low: 36, high: ~11), error: true)) - Test().multiplication(~5 as T, ~4 as T, Fallible(Doublet(low: 30, high: ~10), error: true)) - Test().multiplication(~5 as T, ~3 as T, Fallible(Doublet(low: 24, high: ~9), error: true)) - Test().multiplication(~5 as T, ~2 as T, Fallible(Doublet(low: 18, high: ~8), error: true)) - Test().multiplication(~5 as T, ~1 as T, Fallible(Doublet(low: 12, high: ~7), error: true)) - Test().multiplication(~5 as T, ~0 as T, Fallible(Doublet(low: 6, high: ~6), error: true)) - Test().multiplication(~5 as T, 0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication(~5 as T, 1 as T, Fallible(Doublet(low: ~5, high: 0))) - Test().multiplication(~5 as T, 2 as T, Fallible(Doublet(low: ~11, high: 1), error: true)) - Test().multiplication(~5 as T, 3 as T, Fallible(Doublet(low: ~17, high: 2), error: true)) - Test().multiplication(~5 as T, 4 as T, Fallible(Doublet(low: ~23, high: 3), error: true)) - Test().multiplication(~5 as T, 5 as T, Fallible(Doublet(low: ~29, high: 4), error: true)) + func whereIs(_ type: T.Type) throws where T: BinaryInteger { + let size = IX(size: T.self) ?? conditional(debug: 256, release: 4096) + let rounds = !T.isArbitrary ? conditional(debug: 128, release: 1024) : 16 - Test().multiplication( 0 as T, ~5 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, ~3 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, ~2 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, ~1 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, ~0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, 0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, 1 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, 2 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, 4 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 0 as T, 5 as T, Fallible(Doublet(low: 0, high: 0))) - - Test().multiplication( 5 as T, ~5 as T, Fallible(Doublet(low: ~29, high: 4), error: true)) - Test().multiplication( 5 as T, ~4 as T, Fallible(Doublet(low: ~24, high: 4), error: true)) - Test().multiplication( 5 as T, ~3 as T, Fallible(Doublet(low: ~19, high: 4), error: true)) - Test().multiplication( 5 as T, ~2 as T, Fallible(Doublet(low: ~14, high: 4), error: true)) - Test().multiplication( 5 as T, ~1 as T, Fallible(Doublet(low: ~9, high: 4), error: true)) - Test().multiplication( 5 as T, ~0 as T, Fallible(Doublet(low: ~4, high: 4), error: true)) - Test().multiplication( 5 as T, 0 as T, Fallible(Doublet(low: 0, high: 0))) - Test().multiplication( 5 as T, 1 as T, Fallible(Doublet(low: 5, high: 0))) - Test().multiplication( 5 as T, 2 as T, Fallible(Doublet(low: 10, high: 0))) - Test().multiplication( 5 as T, 3 as T, Fallible(Doublet(low: 15, high: 0))) - Test().multiplication( 5 as T, 4 as T, Fallible(Doublet(low: 20, high: 0))) - Test().multiplication( 5 as T, 5 as T, Fallible(Doublet(low: 25, high: 0))) + for _ in 0 ..< rounds { + let a = T.entropic(size: size, using: &randomness) + let b = T.entropic(size: size, using: &randomness) + let c = (a.plus(b).squared().value) + let d = (a.squared().plus(a.times(b).times(2)).plus(b.squared()).value) + try #require(c == d) + } } + } + + @Test("BinaryInteger/multiplication: ascending zeros by ascending zeros", .tags(.random), arguments: typesAsBinaryInteger, fuzzers) + func multiplicationOfAscendingZerosByAscendingZeros(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) throws { + try whereIs(type) - for type in typesAsBinaryIntegerAsSigned { - AsSigned(type) + func whereIs(_ type: T.Type) throws where T: BinaryInteger { + let index = Shift.max(or: 255) + let rounds: IX = !T.isArbitrary ? conditional(debug: 64, release: 1024) : 32 + + for _ in 0 ..< rounds { + let a = Shift.random(through: index, using: &randomness) + let b = Shift.random(through: index, using: &randomness) + let c = T .entropic(through: index, as: Domain.finite, using: &randomness).up(a) + let d = T .entropic(through: index, as: Domain.finite, using: &randomness).up(b) + let e = c.multiplication(d).down(a).down(b) + let f = c.down(a).multiplication(d.down(b)) + try #require(e == f) + } } - - for type in typesAsBinaryIntegerAsUnsigned { - AsUnsigned(type) + } + + /// - TODO: Consider full-width square multiplication algorithm. + @Test("BinaryInteger/multiplication: ascending zeros by itself", .tags(.random), arguments: typesAsBinaryInteger, fuzzers) + func multiplicationOfAscendingZerosByItself(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) throws { + try whereIs(type) + + func whereIs(_ type: T.Type) throws where T: BinaryInteger { + let index = Shift.max(or: 255) + let rounds: IX = !T.isArbitrary ? conditional(debug: 64, release: 1024) : 32 + + for _ in 0 ..< rounds { + let a = Shift.random(through: index, using: &randomness) + let b = T .entropic(through: index, as: Domain.finite, using: &randomness).up(a) + try #require(b.squared() == b.times(b)) + } } } //=------------------------------------------------------------------------= - // MARK: Tests + // MARK: Tests x Arbitrary Integer //=------------------------------------------------------------------------= - func testMultiplication112As08Is111As16() throws { - func whereIs(x08: A.Type, x16: B.Type) where A: SystemsInteger, B: SystemsInteger { - precondition(A.size == Count(08)) - precondition(B.size == Count(16)) - precondition(A.isSigned == B.isSigned) - - var success = U32.zero - - for lhs in A.min ... A.max { - for rhs in A.min ... A.max { - let result = lhs.multiplication((rhs)) - let expectation = B(lhs).times(B(rhs)) + /// - Note: The `(0s, 1) ⨉ (0s, 1)` case does not fit in the combined size. + @Test("BinaryInteger/multiplication: zeros then ones", .tags(.important), arguments: typesAsArbitraryIntegerAsByte) + func multiplicationOfZerosThenOnes(type: any ArbitraryInteger.Type) throws { + try whereIs(type) + + func whereIs(_ type: T.Type) throws where T: ArbitraryInteger { + let size = IX(size: T.Element.self) * 8 - guard !expectation.error else { break } - guard result.low == A.Magnitude(load: expectation.value) else { break } - guard result.high == A(load: expectation.value.down(A.size)) else { break } + var a = T(repeating: Bit.one) + for i in 0 ... size { + + var b = T(repeating: Bit.one) + var c = a.complement() + for j in 0 ... size { - success += 1 - } + try #require(a.times(b).value == c) + + if i == j { + try #require(a.squared().value == c) + } + + c = c.up(Shift.one) + b = b.up(Shift.one) + }; a = a.up(Shift.one) } - - Test().same(success, 256 * 256) } - - #if DEBUG - throw XCTSkip("req. release mode") - #else - whereIs(x08: I8.self, x16: I16.self) - whereIs(x08: I8.self, x16: DoubleInt.self) - whereIs(x08: U8.self, x16: U16.self) - whereIs(x08: U8.self, x16: DoubleInt.self) - #endif } //=------------------------------------------------------------------------= - // MARK: Tests x Random + // MARK: Tests x Systems Integer //=------------------------------------------------------------------------= - func testMultiplicationByFuzzing() { - func whereIs(_ type: T.Type, size: IX, rounds: IX, randomness: consuming FuzzerInt) where T: BinaryInteger { - func random() -> T { - let index = IX.random(in: 00000 ..< size, using: &randomness)! - let pattern = T.Signitude.random(through: Shift(Count(index)), using: &randomness) - return T(raw: pattern) // do not forget about infinite values! - } - - for _ in 0 ..< rounds { - let a = random() - let b = random() - let c = random() - let d = random() - let e = (a &+ b) &* (c &+ d) - let f = (a &* c) &+ (a &* d) &+ (b &* c) &+ (b &* d) - Test().same(e,f) - } - } + @Test("BinaryInteger/multiplication: full 8-bit is half 16-bit", + .tags(.exhaustive), .disabled(if: isDebug), arguments: [ - for type in typesAsBinaryInteger { - #if DEBUG - whereIs(type, size: IX(size: type) ?? 0256, rounds: 16, randomness: fuzzer) - #else - whereIs(type, size: IX(size: type) ?? 4096, rounds: 16, randomness: fuzzer) - #endif - } - } - - func testMultiplicationSquareProductByFuzzing() { - func whereIs(_ type: T.Type, size: IX, rounds: IX, randomness: consuming FuzzerInt) where T: BinaryInteger { - func random() -> T { - let index = IX.random(in: 00000 ..< size, using: &randomness)! - let pattern = T.Signitude.random(through: Shift(Count(index)), using: &randomness) - return T(raw: pattern) // do not forget about infinite values! - } + (x08: I8.self, x16: I16.self), + (x08: I8.self, x16: DoubleInt.self), + (x08: U8.self, x16: U16.self), + (x08: U8.self, x16: DoubleInt.self), + + ] as [(x08: any SystemsInteger.Type, x16: any SystemsInteger.Type)]) + func multiplicationAsFull8BitIsHalf16Bit(x08: any SystemsInteger.Type, x16: any SystemsInteger.Type) throws { + try whereIs(x08, x16) + + func whereIs(_ x08: A.Type, _ x16: B.Type) throws where A: SystemsInteger, B: SystemsInteger { + try #require(A.size == Count(08)) + try #require(B.size == Count(16)) + try #require(A.isSigned == B.isSigned) - for _ in 0 ..< rounds { - let a = random() - let b = random() - let c = a.plus(b).squared().value - let d = a.squared().plus(a.times(b).times(2)).plus(b.squared()).value - Test().same(c,d) + var success = IX.zero + + for lhs in A.all { + for rhs in A.all { + let result = lhs.multiplication((rhs)) + let expectation = B(lhs).times(B(rhs)) + + guard !expectation.error else { continue } + guard result.low == A.Magnitude(load: expectation.value) else { continue } + guard result.high == A(load: expectation.value.down(A.size)) else { continue } + + success &+= 1 + } } - } - - for type in typesAsBinaryInteger { - #if DEBUG - whereIs(type, size: IX(size: type) ?? 0256, rounds: 16, randomness: fuzzer) - #else - whereIs(type, size: IX(size: type) ?? 4096, rounds: 16, randomness: fuzzer) - #endif + + #expect(success == IX(A.all.count) &* IX(A.all.count)) } } } -//=----------------------------------------------------------------------------= -// MARK: + Recoverable -//=----------------------------------------------------------------------------= +//*============================================================================* +// MARK: * Binary Integer x Multiplication x Recovery Mechanisms +//*============================================================================* -extension BinaryIntegerTestsOnMultiplication { +@Suite("BinaryInteger/multiplication: recovery mechanisms", .tags(.recoverable)) +struct BinaryIntegerTestsOnMultiplicationRecoveryMechanisms { //=------------------------------------------------------------------------= // MARK: Tests //=------------------------------------------------------------------------= - func testErrorPropagationMechanism() { - func whereIs(_ type: T.Type, size: IX, rounds: IX, randomness: consuming FuzzerInt) where T: BinaryInteger { - var success: IX = 0 - - func random() -> T { - let index = IX.random(in: 00000 ..< size, using: &randomness)! - let pattern = T.Signitude.random(through: Shift(Count(index)), using: &randomness) - return T(raw: pattern) // do not forget about infinite values! - } - - for _ in 0 ..< rounds { - let instance: T = random() + @Test("BinaryInteger/multiplication: error propagation", .tags(.random), arguments: typesAsBinaryInteger, fuzzers) + func multiplicationErrorPropagation(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) { + whereIs(type) + + func whereIs(_ type: T.Type) where T: BinaryInteger { + for _ in 0 ..< 8 { + let instance = T.random(through: Shift.max(or: 255), using: &randomness) let expectation: Fallible = instance.squared() - success &+= IX(Bit(instance .squared() == expectation)) - success &+= IX(Bit(instance.veto(false).squared() == expectation)) - success &+= IX(Bit(instance.veto(true ).squared() == expectation.veto())) + + for error in Bool.all { + #expect(instance.veto(error).squared() == expectation.veto(error)) + } } - for _ in 0 ..< rounds { - let lhs: T = random() - let rhs: T = random() - let expectation: Fallible = lhs.times(rhs) - success &+= IX(Bit(lhs .times(rhs) == expectation)) - success &+= IX(Bit(lhs .times(rhs.veto(false)) == expectation)) - success &+= IX(Bit(lhs .times(rhs.veto(true )) == expectation.veto())) - success &+= IX(Bit(lhs.veto(false).times(rhs) == expectation)) - success &+= IX(Bit(lhs.veto(false).times(rhs.veto(false)) == expectation)) - success &+= IX(Bit(lhs.veto(false).times(rhs.veto(true )) == expectation.veto())) - success &+= IX(Bit(lhs.veto(true ).times(rhs) == expectation.veto())) - success &+= IX(Bit(lhs.veto(true ).times(rhs.veto(false)) == expectation.veto())) - success &+= IX(Bit(lhs.veto(true ).times(rhs.veto(true )) == expectation.veto())) + for _ in 0 ..< 8 { + let lhs = T.entropic(through: Shift.max(or: 255), using: &randomness) + let rhs = T.entropic(through: Shift.max(or: 255), using: &randomness) + let expectation = lhs.times(rhs) as Fallible + + #expect(lhs .times(rhs) == expectation) + #expect(lhs .times(rhs.veto(false)) == expectation) + #expect(lhs .times(rhs.veto(true )) == expectation.veto()) + #expect(lhs.veto(false).times(rhs) == expectation) + #expect(lhs.veto(false).times(rhs.veto(false)) == expectation) + #expect(lhs.veto(false).times(rhs.veto(true )) == expectation.veto()) + #expect(lhs.veto(true ).times(rhs) == expectation.veto()) + #expect(lhs.veto(true ).times(rhs.veto(false)) == expectation.veto()) + #expect(lhs.veto(true ).times(rhs.veto(true )) == expectation.veto()) } - - Test().same(success, rounds &* 12) - } - - for type in typesAsBinaryInteger { - whereIs(type, size: IX(size: type) ?? 256, rounds: 8, randomness: fuzzer) } } } diff --git a/Tests/UltimathnumTests/BinaryInteger+Stdlib.swift b/Tests/UltimathnumTests/BinaryInteger+Stdlib.swift index 3e11c974..c4581e47 100644 --- a/Tests/UltimathnumTests/BinaryInteger+Stdlib.swift +++ b/Tests/UltimathnumTests/BinaryInteger+Stdlib.swift @@ -21,7 +21,7 @@ import TestKit2 // MARK: Tests //=------------------------------------------------------------------------= - @Test("BinaryInteger/stdlib - uniform", arguments: coreIntegers, fuzzers) + @Test("BinaryInteger/stdlib - uniform", arguments: typesAsCoreInteger, fuzzers) func stdlib(_ type: any CoreInteger.Type, _ randomness: consuming FuzzerInt) { whereIs(type) diff --git a/Tests/UltimathnumTests/Utilities/Globals.swift b/Tests/UltimathnumTests/Utilities/Globals.swift index 83e37c4d..d89aa145 100644 --- a/Tests/UltimathnumTests/Utilities/Globals.swift +++ b/Tests/UltimathnumTests/Utilities/Globals.swift @@ -23,6 +23,11 @@ let typesAsArbitraryInteger: [any ArbitraryInteger.Type] = { typesAsArbitraryIntegerAsUnsigned }() +let typesAsArbitraryIntegerAsByte: [any ArbitraryInteger.Type] = [ + InfiniInt.self, + InfiniInt.self, +] + let typesAsArbitraryIntegerAsSigned: [any ArbitraryIntegerAsSigned.Type] = [ InfiniInt.self, InfiniInt.self, @@ -33,6 +38,19 @@ let typesAsArbitraryIntegerAsUnsigned: [any ArbitraryIntegerAsUnsigned.Type] = [ InfiniInt.self, ] +let typesAsAppendixInteger: [any BinaryInteger.Type] = { + typesAsAppendixIntegerAsSigned + + typesAsAppendixIntegerAsUnsigned +}() + +let typesAsAppendixIntegerAsSigned: [any BinaryInteger.Type] = { + typesAsBinaryIntegerAsSigned +}() + +let typesAsAppendixIntegerAsUnsigned: [any BinaryInteger.Type] = { + typesAsArbitraryIntegerAsUnsigned +}() + let typesAsBinaryInteger: [any BinaryInteger.Type] = { typesAsBinaryIntegerAsSigned + typesAsBinaryIntegerAsUnsigned