diff --git a/Tests/InfiniIntKitTests/InfiniInt+Factorial.swift b/Tests/InfiniIntKitTests/InfiniInt+Factorial.swift deleted file mode 100644 index a47144fc..00000000 --- a/Tests/InfiniIntKitTests/InfiniInt+Factorial.swift +++ /dev/null @@ -1,98 +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 TestKit - -//*============================================================================* -// MARK: * Infini Int x Factorial -//*============================================================================* - -final class InfiniIntTestsOnFactorial: XCTestCase { - - //=------------------------------------------------------------------------= - // MARK: Tests - //=------------------------------------------------------------------------= - - /// - Seealso: https://www.wolframalpha.com/input?i=1337%21 - func testElementAtIndex1337() { - let index = I32(1337) - let expectation = UXL(""" - 0000000000000000000000000000000000000000000000088746638366576796\ - 1662410646418133009547720120993472817948825961826558347208242245\ - 1756532829820227220586694812398717056396577963484185843624972811\ - 5666957529675078095475832753552497225147925032072561058716604731\ - 6052852866030929583178433027418481048030909480696419787417341906\ - 4572030730827369138507112563270949558364041355124827802950693106\ - 8269157399549150062385457960158781784950362878981114976656707404\ - 5228982210153765358361566631588607877546386029998012175018958929\ - 3582268308469269104282190071516000938218917563445346261044245950\ - 1188171227111263255419825853726532340116198945685498762638201684\ - 1485520206291409251317581091038442775621830936404993634843379290\ - 1361100509611552684596678558211306866991110527104484564523037155\ - 6478471360899164682373688208253571354777727508512242286955149145\ - 8340859781864591504272223365288587594617674139500396689715172922\ - 6359392578147064352610223393483998956246879837841320985989769626\ - 6179205512520481820737999482598193449891304475697988001622701191\ - 9060243351342003850059552276235127922399190788983464032761842853\ - 7779125657988384921093162403329422846750070080790435980548011320\ - 8012589893605733000032695906552073425564369371069248760095607207\ - 8505707260781540866742465477586689361471938191739558464749996286\ - 4413771116506778459499623367780957390738229935737419898775801368\ - 3016897266615653111897811764005836445828334070894094916416321421\ - 2205585345343467103936615504309510942658940054307206713898430351\ - 2765763757406092393826588587336838931053010025291844821518220368\ - 6997921561397076063950777833351117947017284810049693597206497166\ - 1937839645155149990343920418359376780238516182208337827385812062\ - 7637322394117182921334148019921300324968502074248850527953855369\ - 2799334077418352464827518766413942962156425866660113699769089339\ - 4377729608513211320512270149552612669389229439481776767545946390\ - 2878893912569300830451490352289724636463392114204127798336363985\ - 5771698195723868490914292685644330145722348682172596462099682883\ - 3653018978436973049396466355275048510896815912978257797414563383\ - 5207973366697117157148942636872087371642483893143517378758644998\ - 8464426436536631157600211369872325447977414398436560514078798743\ - 9413601308015052341997692402113801272966233644030244020754954191\ - 9066259118723591507750735712313844602304884313145508736481447704\ - 5488678431392483647586712465074591137549320739633574910014760488\ - 6064560970902257373275335399229457426300052890490827954589255639\ - 1315465642093856120179282119393738619979280502407518321372860305\ - 8326104665419968327865042354199849986547736943151560629608102053\ - 3403211545135539930030125186619333002398441459913859932886526049\ - 7338780659327709932009885060564610355341365548766608829381808144\ - 5944746250847506805559294814927409883522217663983575214011913266\ - 1690451806244719475225312994508695604018925535701812269958861020\ - 0682687421950641658953426083582402430628845526662341581578829689\ - 9891354397168436987776237040575004903091492072939590734941921683\ - 0563653773210805203036149159257276532887915564922718621996437779\ - 0769672632868865437856170600479629204015063722948541356859602014\ - 6350750607517433577548376487027468181942593305623356616611128614\ - 7971012005610019299941678022428072587797937576139070865042268513\ - 6783106507900865379664560961392968153883432982179193291897376909\ - 0276043156634245302730352969198703152215639130636288000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000 - """)! - - func whereIs(_ type: T.Type) where T: ArbitraryInteger { - Test().comparison(T(index).factorial()!, expectation, Signum.zero) - } - - whereIs(IXL.self) - whereIs(UXL.self) - #if !DEBUG - whereIs(InfiniInt.self) - whereIs(InfiniInt.self) - #endif - } -} diff --git a/Tests/UltimathnumTests/BinaryInteger+Factorial.swift b/Tests/UltimathnumTests/BinaryInteger+Factorial.swift index bf73d39b..898a24d3 100644 --- a/Tests/UltimathnumTests/BinaryInteger+Factorial.swift +++ b/Tests/UltimathnumTests/BinaryInteger+Factorial.swift @@ -11,109 +11,73 @@ import CoreKit import DoubleIntKit import InfiniIntKit import RandomIntKit -import TestKit +import TestKit2 //*============================================================================* // MARK: * Binary Integer x Factorial //*============================================================================* -final class BinaryIntegerTestsOnFactorial: XCTestCase { +@Suite("BinaryInteger/factorial()", .serialized) +struct BinaryIntegerTestsOnFactorial { //=------------------------------------------------------------------------= // MARK: Tests //=------------------------------------------------------------------------= - func testVersusNaiveApproach() { - func whereIs(_ type: T.Type, through factorial: UX) where T: BinaryInteger { - var expectation = Fallible(T.lsb) - - for instance: T in 0...T(clamping: factorial) { - expectation = expectation.times(Swift.max(1, instance)) - - let optional: Optional = instance.factorial() - let unsigned: Fallible = instance.magnitude().factorial() - let fallible: Fallible = T.exactly(unsigned) - - Test().same(optional, expectation.optional()) - Test().same(fallible, expectation) - } - } + @Test("element at small natural index", arguments: [ - #if DEBUG - let limit: UX = 80 - #else - let limit: UX = 160 - #endif - for type in systemsIntegers { - whereIs(type, through: limit) - } + (index: 0 as U8, element: 1 as IXL), + (index: 1 as U8, element: 1 as IXL), + (index: 2 as U8, element: 2 as IXL), + (index: 3 as U8, element: 6 as IXL), + (index: 4 as U8, element: 24 as IXL), + (index: 5 as U8, element: 120 as IXL), + (index: 6 as U8, element: 720 as IXL), + (index: 7 as U8, element: 5040 as IXL), + (index: 8 as U8, element: 40320 as IXL), + (index: 9 as U8, element: 362880 as IXL), + (index: 10 as U8, element: 3628800 as IXL), + (index: 11 as U8, element: 39916800 as IXL), + (index: 12 as U8, element: 479001600 as IXL), + (index: 13 as U8, element: 6227020800 as IXL), + (index: 14 as U8, element: 87178291200 as IXL), + (index: 15 as U8, element: 1307674368000 as IXL), + (index: 16 as U8, element: 20922789888000 as IXL), + (index: 17 as U8, element: 355687428096000 as IXL), + (index: 18 as U8, element: 6402373705728000 as IXL), + (index: 19 as U8, element: 121645100408832000 as IXL), + (index: 20 as U8, element: 2432902008176640000 as IXL), + (index: 21 as U8, element: 51090942171709440000 as IXL), + (index: 22 as U8, element: 1124000727777607680000 as IXL), + (index: 23 as U8, element: 25852016738884976640000 as IXL), + (index: 24 as U8, element: 620448401733239439360000 as IXL), + (index: 25 as U8, element: 15511210043330985984000000 as IXL), + (index: 26 as U8, element: 403291461126605635584000000 as IXL), + (index: 27 as U8, element: 10888869450418352160768000000 as IXL), + (index: 28 as U8, element: 304888344611713860501504000000 as IXL), + (index: 29 as U8, element: 8841761993739701954543616000000 as IXL), + (index: 30 as U8, element: 265252859812191058636308480000000 as IXL), + (index: 31 as U8, element: 8222838654177922817725562880000000 as IXL), + (index: 32 as U8, element: 263130836933693530167218012160000000 as IXL), - whereIs(InfiniInt.self, through: limit) - whereIs(InfiniInt.self, through: limit) - } - - func testElementThatFitsInUnsignedButNotInSigned() { - Test().same(I16(7).factorial(), 5040) - Test().same(U16(7).factorial(), 5040) - Test().same(I16(8).factorial(), nil) - Test().same(U16(8).factorial(), 40320) - } - - func testElementAtInfiniteIndexIsZeroBecauseOfEvenFactors() { - Test().same((~0 as UXL).factorial(), UXL.zero.veto()) - Test().same((~1 as UXL).factorial(), UXL.zero.veto()) - Test().same((~2 as UXL).factorial(), UXL.zero.veto()) - Test().same((~3 as UXL).factorial(), UXL.zero.veto()) - } - - func testElementAtNegativeIndexIsNil() { - func whereIs(_ type: T.Type) where T: BinaryInteger { - precondition(T.isSigned) - - Test().none((-1 as T).factorial()) - Test().none((-2 as T).factorial()) - Test().none((-3 as T).factorial()) - Test().none((-4 as T).factorial()) - - Test().none((Esque.min + 0).factorial()) - Test().none((Esque.min + 1).factorial()) - Test().none((Esque.min + 2).factorial()) - Test().none((Esque.min + 3).factorial()) - } - - for type in binaryIntegersWhereIsSigned { + ] as [(index: U8, element: IXL)]) + func elementAtSmallNaturalIndex(index: U8, element: IXL) throws { + for type in binaryIntegers { whereIs(type) } - } - - func testElementAtIndexGreaterThanOrEqualToSizePlus2IsZero() { - func whereIs(_ type: T.Type) where T: SystemsInteger { - Test().none((T.max - 0).factorial()) - Test().none((T.max - 1).factorial()) - Test().none((T.max - 2).factorial()) - Test().none((T.max - 3).factorial()) - Test().none(T(UX(size: T.self) + 2).factorial()) - - Test().same((T.max - 0).magnitude().factorial(), T.Magnitude.zero.veto()) - Test().same((T.max - 1).magnitude().factorial(), T.Magnitude.zero.veto()) - Test().same((T.max - 2).magnitude().factorial(), T.Magnitude.zero.veto()) - Test().same((T.max - 3).magnitude().factorial(), T.Magnitude.zero.veto()) - - Test().yay(T(UX(size: T.self) + 3).magnitude().factorial().value.isZero) - Test().yay(T(UX(size: T.self) + 2).magnitude().factorial().value.isZero) - Test().nay(T(UX(size: T.self) + 1).magnitude().factorial().value.isZero) - Test().nay(T(UX(size: T.self) + 0).magnitude().factorial().value.isZero) - } - for type in systemsIntegers { - whereIs(type) + // TODO: consider Optional> approach + + func whereIs(_ type: T.Type) where T: BinaryInteger { + #expect(T.exactly(index).optional()?.factorial() == T.exactly(element).optional()) } } /// - Seealso: https://www.wolframalpha.com/input?i=1000%21 - func testElementAtIndex1000() throws { - let index: U32 = 1000 - let expectation = UXL(""" + /// - Seealso: https://www.wolframalpha.com/input?i=1024%21 + @Test("element at large natural index", arguments: [ + + (index: IXL(1000), element: IXL(""" 0000000000000000000000000000000000000000000000000000000040238726\ 0077093773543702433923003985719374864210714632543799910429938512\ 3986290205920442084869694048004799886101971960586316668729948085\ @@ -155,30 +119,9 @@ final class BinaryIntegerTestsOnFactorial: XCTestCase { 0000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000 - """)! + """)!), - func whereIs(_ type: T.Type) throws where T: BinaryInteger { - typealias M = T.Magnitude - - if T.isSigned { - Test().same(try T.exactly(index).prune(Bad.any).factorial(), T.exactly(expectation).optional()) - } else { - Test().same(try M.exactly(index).prune(Bad.any).factorial(), M.exactly(expectation)) - } - } - - try whereIs(IX.self) - try whereIs(UX.self) - try whereIs(DoubleInt.self) - try whereIs(DoubleInt.self) - try whereIs(InfiniInt.self) - try whereIs(InfiniInt.self) - } - - /// - Seealso: https://www.wolframalpha.com/input?i=1024%21 - func testElementAtIndex1024() throws { - let index: U32 = 1024 - let expectation = UXL(""" + (index: IXL(1024), element: IXL(""" 0000000000000000000000000000000000000000000000005418528796058857\ 2830769219446838547380015539635380134444828702706832106120733766\ 0373314098413621458671907918845708980753931994165770187368260454\ @@ -221,81 +164,95 @@ final class BinaryIntegerTestsOnFactorial: XCTestCase { 0000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000 - """)! + """)!), + + ] as [(index: IXL, element: IXL)]) + func elementAtLargeNaturalIndex(index: IXL, element: IXL) throws { + for type in systemsIntegers { + try whereIs(type) + } + + try whereIs(InfiniInt.self) + try whereIs(InfiniInt.self) + + // TODO: consider Optional> approach func whereIs(_ type: T.Type) throws where T: BinaryInteger { - typealias M = T.Magnitude + guard let index = T.exactly(index).optional() else { return } if T.isSigned { - Test().same(try T.exactly(index).prune(Bad.any).factorial(), T.exactly(expectation).optional()) + #expect(index.factorial() == T.exactly(element).optional()) } else { - Test().same(try M.exactly(index).prune(Bad.any).factorial(), M.exactly(expectation)) + #expect(index.magnitude().factorial() == T.Magnitude.exactly(element)) } } - - try whereIs(IX.self) - try whereIs(UX.self) - try whereIs(DoubleInt.self) - try whereIs(DoubleInt.self) - try whereIs(InfiniInt.self) - try whereIs(InfiniInt.self) } } -//=----------------------------------------------------------------------------= -// MARK: + Recoverable -//=----------------------------------------------------------------------------= +//*============================================================================* +// MARK: * Binary Integer x Factorial x Edge Cases +//*============================================================================* -extension BinaryIntegerTestsOnFactorial { +@Suite("BinaryInteger/factorial() - edge cases", .tags(.documentation)) +struct BinaryIntegerTestsOnFactorialEdgeCases { //=------------------------------------------------------------------------= // MARK: Tests //=------------------------------------------------------------------------= - func testErrorPropagationMechanism() { - func whereIs(_ type: T.Type, size: IX, rounds: IX, randomness: consuming FuzzerInt) where T: UnsignedInteger { - 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! - } + @Test("element at negative index is nil [uniform]", arguments: binaryIntegersWhereIsSigned, fuzzers) + func elementAtNegativeIndexIsNil(type: any SignedInteger.Type, randomness: consuming FuzzerInt) { + whereIs(type) - for _ in 0 ..< rounds { - let instance = random() - let expectation = instance.factorial() - success &+= IX(Bit(instance.veto(false).factorial() == expectation)) - success &+= IX(Bit(instance.veto(true ).factorial() == expectation.veto())) - } + func whereIs(_ type: T.Type) where T: SignedInteger { + let low = T(repeating: Bit.one).up(Shift.max(or: 255)) + let high = T(repeating: Bit.one) + let expectation = Optional.none - Test().same(success, rounds &* 2) + #expect(low .factorial() == expectation) + #expect(high.factorial() == expectation) + + for _ in 0 ..< 32 { + let random = T.random(in: low...high, using: &randomness) + #expect(random.isNegative) + #expect(random.factorial() == expectation) + } } + } + + /// Here we check that the infinite even factors overshift the result. + @Test("element at infinite index is zero with error [uniform]", arguments: arbitraryIntegersWhereIsUnsigned, fuzzers) + func elementAtInfiniteIndexIsZeroWithError(type: any ArbitraryIntegerWhereIsUnsigned.Type, randomness: consuming FuzzerInt) { + whereIs(type) - for type in binaryIntegersWhereIsUnsigned { - whereIs(type, size: IX(size: type) ?? 8, rounds: 32, randomness: fuzzer) + func whereIs(_ type: T.Type) where T: ArbitraryIntegerWhereIsUnsigned { + let low = T(repeating: Bit.one).up(Shift.max(or: 255)) + let high = T(repeating: Bit.one) + let expectation = T.zero.veto() + + #expect(low .factorial() == expectation) + #expect(high.factorial() == expectation) + + for _ in 0 ..< 32 { + let random = T.random(in: low...high, using: &randomness) + #expect(random.isInfinite) + #expect(random.factorial() == expectation) + } } } -} - -//=----------------------------------------------------------------------------= -// MARK: + Documentation -//=----------------------------------------------------------------------------= - -extension BinaryIntegerTestsOnFactorial { - - //=------------------------------------------------------------------------= - // MARK: Tests - //=------------------------------------------------------------------------= - func testMethodsCodeSnippet() { - Test().same(U8(0).factorial(), U8.exactly( 1)) - Test().same(U8(1).factorial(), U8.exactly( 1)) - Test().same(U8(2).factorial(), U8.exactly( 2)) - Test().same(U8(3).factorial(), U8.exactly( 6)) - Test().same(U8(4).factorial(), U8.exactly( 24)) - Test().same(U8(5).factorial(), U8.exactly( 120)) - Test().same(U8(6).factorial(), U8.exactly( 720)) - Test().same(U8(7).factorial(), U8.exactly(5040)) + @Test("element at random index error propagation [entropic]", arguments: binaryIntegersWhereIsUnsigned, fuzzers) + func elementAtRandomIndexErrorPropagation(type: any UnsignedInteger.Type, randomness: consuming FuzzerInt) { + whereIs(type) + + func whereIs(_ type: T.Type) where T: UnsignedInteger { + for _ in 0 ..< 32 { + let index = T.entropic(through: Shift.max(or: 7), using: &randomness) + let element: Fallible = index.factorial() + for error in Bool.all { + #expect(index.veto(error).factorial() == element.veto(error)) + } + } + } } }