From d73aef069cbb2f986d3f3a0af2adf4c3554d3a8c Mon Sep 17 00:00:00 2001 From: Michael Griebling Date: Tue, 18 Apr 2023 09:42:22 -0400 Subject: [PATCH 1/7] Added improved LosslessStringConvertible init() with order of magnitude faster performance. Removed unnecessary _digits and _digitRadix. Fixed a cut/paste problem in "^=" function. Added test cases for string conversion performance and logical operation tests. --- Sources/BigIntModule/BigInt.swift | 127 +++++++++++++++++++++------- Tests/BigIntTests/BigIntTests.swift | 68 +++++++++++++++ 2 files changed, 166 insertions(+), 29 deletions(-) diff --git a/Sources/BigIntModule/BigInt.swift b/Sources/BigIntModule/BigInt.swift index 2e2721c6..c5b65c44 100644 --- a/Sources/BigIntModule/BigInt.swift +++ b/Sources/BigIntModule/BigInt.swift @@ -30,11 +30,11 @@ public struct BigInt: SignedInteger { words[words.endIndex - 1] > Int.max } - private static let _digits: [BigInt] = (0 ... 36).map { - BigInt(_uncheckedWords: [UInt(bitPattern: $0)]) - } +// private static let _digits: [BigInt] = (0 ... 36).map { +// BigInt(_uncheckedWords: [UInt(bitPattern: $0)]) +// } - private static let _digitRadix = BigInt(_uncheckedWords: [0, 1]) + // private static let _digitRadix = BigInt(_uncheckedWords: [0, 1]) } // MARK: - Basic Behaviors @@ -96,39 +96,108 @@ extension BigInt: LosslessStringConvertible { public init?(_ description: String) { self.init(description, radix: 10) } - + + + //////////////////////////////////////////////////////////////////////////// + /// + /// NEW CODE STARTS + + private static func _pow(_ lhs: UInt, _ rhs: UInt) -> UInt { + // guard rhs>=0 else { return 0 } /* lhs ** (-rhs) = 0 */ + var lexp = rhs + var x = lhs + var y = UInt(1) + while lexp > 0 { + if !lexp.isMultiple(of: 2) { y*=x } + lexp >>= 1 + if lexp > 0 { x=x*x } // could be 2X faster with squared function + } + return y + } + + private func _maxDigits(forRadix radix:Int) -> Int { + switch radix { + case 2: return 62 + case 8: return 20 + case 10: return 18 + case 16: return 14 + default: return 11 // safe but not optimal for other radices + } + } + + /// Speeds the time to initialize a BigInt by about a factor of 16 for + /// the test case of the string for 512! using radix 10. A radix 36 test + /// was 10X faster. BTW, the factorial test code was sped up by almost 2 + /// times (the string to number code accounted for a large part of the + /// total time). public init?(_ description: T, radix: Int = 10) where T: StringProtocol { precondition(2 ... 36 ~= radix, "Radix not in range 2 ... 36") self = 0 - let isNegative = description.hasPrefix("-") let hasPrefix = isNegative || description.hasPrefix("+") - let utf8 = description.utf8.dropFirst(hasPrefix ? 1 : 0) - guard !utf8.isEmpty else { return nil } - - for var byte in utf8 { - switch byte { - case UInt8(ascii: "0") ... UInt8(ascii: "9"): - byte -= UInt8(ascii: "0") - case UInt8(ascii: "A") ... UInt8(ascii: "Z"): - byte -= UInt8(ascii: "A") - byte += 10 - case UInt8(ascii: "a") ... UInt8(ascii: "z"): - byte -= UInt8(ascii: "a") - byte += 10 - default: + var str = description.dropFirst(hasPrefix ? 1 : 0) + guard !str.isEmpty else { return nil } + + /// Main speed-up is due to converting chunks of string via + /// the Int64() initializer instead of a character at a time. + /// We also get free radix digit checks. + let maxDigits = _maxDigits(forRadix: radix) + while !str.isEmpty { + let block = str.prefix(maxDigits) + let size = block.count + str.removeFirst(size) + if let word = UInt(block, radix: radix) { + self *= BigInt(Self._pow(UInt(radix), UInt(size))) + self += BigInt(word) + } else { return nil } - guard byte < radix else { return nil } - self *= BigInt._digits[radix] - self += BigInt._digits[Int(byte)] } if isNegative { self.negate() } } + + /// NEW CODE ENDS + /// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + +//// Original code +// public init?(_ description: T, radix: Int = 10) where T: StringProtocol { +// precondition(2 ... 36 ~= radix, "Radix not in range 2 ... 36") +// +// self = 0 +// +// let isNegative = description.hasPrefix("-") +// let hasPrefix = isNegative || description.hasPrefix("+") +// let utf8 = description.utf8.dropFirst(hasPrefix ? 1 : 0) +// guard !utf8.isEmpty else { return nil } +// +// for var byte in utf8 { +// switch byte { +// case UInt8(ascii: "0") ... UInt8(ascii: "9"): +// byte -= UInt8(ascii: "0") +// case UInt8(ascii: "A") ... UInt8(ascii: "Z"): +// byte -= UInt8(ascii: "A") +// byte += 10 +// case UInt8(ascii: "a") ... UInt8(ascii: "z"): +// byte -= UInt8(ascii: "a") +// byte += 10 +// default: +// return nil +// } +// guard byte < radix else { return nil } +// self *= BigInt._digits[radix] +// self += BigInt._digits[Int(byte)] +// } +// +// if isNegative { +// self.negate() +// } +// } } extension BigInt: Decodable { @@ -158,8 +227,8 @@ extension BigInt: Encodable { extension BigInt: ExpressibleByIntegerLiteral { public init(integerLiteral value: Int) { - if value >= 0, value < BigInt._digits.count { - self = BigInt._digits[value] + if value >= 0, value <= UInt.max { + words = [UInt(value)] // No need for a table lookup here } else { words = [UInt(bitPattern: value)] } @@ -372,8 +441,8 @@ extension BigInt: BinaryInteger { } public init(_ source: T) where T: BinaryInteger { - if source >= 0, source < BigInt._digits.count { - self = BigInt._digits[Int(source)] + if source >= 0, source < UInt.max { + words = [UInt(source)] // no need for _digits } else { words = Words(source.words) if source > 0 && source.words[source.words.endIndex - 1] > Int.max { @@ -464,7 +533,7 @@ extension BigInt: BinaryInteger { BigInt._signExtend(lhsWords: &lhs.words, rhsWords: &rhsWords) for i in 0 ..< rhsWords.count { - lhs.words[i] &= rhsWords[i] + lhs.words[i] ^= rhsWords[i] } BigInt._dropExcessWords(words: &lhs.words) @@ -634,7 +703,7 @@ extension BigInt { /// See _The Art of Computer Programming_ volume 2 by Donald Knuth, Section 4.3.1: The Classical Algorithms @usableFromInline internal static func _div(lhs: BigInt, rhs: BigInt) -> (quotient: BigInt, remainder: BigInt) { - precondition(rhs != _digits[0], "Division by zero error!") + precondition(rhs != 0, "Division by zero error!") if lhs.words.count == 1, rhs.words.count == 1 { let (quot, rem) = Int(bitPattern: lhs.words[0]).quotientAndRemainder(dividingBy: Int(bitPattern: rhs.words[0])) diff --git a/Tests/BigIntTests/BigIntTests.swift b/Tests/BigIntTests/BigIntTests.swift index 444ae38a..81f45395 100644 --- a/Tests/BigIntTests/BigIntTests.swift +++ b/Tests/BigIntTests/BigIntTests.swift @@ -81,6 +81,37 @@ final class BigIntTests: XCTestCase { 7AHD1LS5YKZ66F4UPG0RCBGG000000000000000000000000000000000000000000000000000\ 000000000000000000000000000000000000000000000000000000000000000000000000000 """ + + static let descriptionFactorial512_radix10 = + """ + 3477289793132605363283045917545604711992250655643514570342474831551610412066352543473209850\ + 3395022536443224331102139454529500170207006901326415311326093794135871186404471618686104089\ + 9557497361427588282356254968425012480396855239725120562512065555822121708786443620799246550\ + 9591872320268380814151785881725352800207863134700768597399809657208738499042913738268415847\ + 1279861843038733804232977180172476769109501954575898694273251503355152959500987699927955393\ + 1070378592917099002397061907147143424113252117585950817850896618433994140232823316432187410\ + 3563412623863324969543199731304073425672820273985793825430484568768008623499281404119054312\ + 7619743567460328184253074417752736588572162951225387238661311882154084789749310739838195608\ + 1763695236422795880296204301770808809477147632428639299038833046264585834888158847387737841\ + 8434136648928335862091963669797757488958218269240400578451402875222386750821375703159545267\ + 2743709490491479678264100074077789791913409339353042276095514021138717365004735834735337923\ + 4387609261306673773281412893026941927424000000000000000000000000000000000000000000000000000\ + 000000000000000000000000000000000000000000000000000000000000000000000000000 + """ + static let descriptionFactorial512_radix16 = + """ + 8f9ef398c97defd735dfa6eb05f0aab2afbf84ea79a8e30b10dd6a305f7e1fc1243dc22f19bb1fc48602a8019d5\ + 889719e4de855351eb6fc5db53c44cfc9ad3d56120ebd8e9ac5cfcac1f438a9c62189b0e1987b27344ac0a871b0\ + bafacb4b900597d9408ffe7329be5cf061ccc22723714a2c5576bdb663c32b7e9a9a51f799a6dfd461f7f5805ae\ + 1b9e79950d5552be34cd47ad1b4abd6a731f34825654ad34f676d84533464c50503d7643ffe6a616f055754e580\ + b59be37a89987abde817d5ecc43903a676a7259ca793dc1975dab19b63d0855003af4981fbd726b009309cceb9e\ + 70bd68b548a0f17b78d27da4f1d829ca1adafe45e65a720e2ff815382c9fcbd81342636f6cd97e790ebbaa766f5\ + 122cf6c1585707c09ca491f07603c33c95a4fce736bf54255f16b085aa2ef59cd9883929a14c35be1c7f54547db\ + d2ff9b17bf93175b3950bdf82b97bc3d6ffceb5b3466231ce4c655db08ea6d0ac135113d0253b49f1d15dcf5ffe\ + 372a3edc3ea1a747d78baa21c6163be4580e989c93731057959e3c803a1d292aab93f30419d63f5667be6146889\ + f9532c580769e1a06eb145800000000000000000000000000000000000000000000000000000000000000000000\ + 00000000000000000000000000000000000000000000000000000000000 + """ // MARK: - Basic arithmetic @@ -133,6 +164,16 @@ final class BigIntTests: XCTestCase { """) } } + + func testStringToBigIntImprovements() { + var expectedNumber: BigInt! + + measure { + expectedNumber = BigInt(Self.descriptionFactorial512_radix36, radix: 36) + } + + XCTAssertEqual(expectedNumber, BigInt(Self.descriptionFactorial512_radix10, radix: 10)) + } func testFactorial() { var expectedNumber: BigInt? @@ -279,6 +320,33 @@ final class BigIntTests: XCTestCase { let bar = BigInt(bitPattern: UInt.max) XCTAssertEqual(bar, BigInt(-1)) } + + // MARK: - Testing logical functions + + func testLogical() { + let a = BigInt("7FFF555512340000", radix: 16)! + let b = BigInt("0000ABCD9876FFFF", radix: 16)! + + let aAndb = String(a & b, radix: 16) + let aOrb = String(a | b, radix: 16) + let aXorb = String(a ^ b, radix: 16) + let notb = String(~b, radix: 16) + + let shiftLeft1 = String(a << 16, radix:16) + let shiftLeft2 = String(a << -3, radix:16) + let shiftRight1 = String(a >> 1000, radix:16) + let shiftRight2 = String(a >> -7, radix:16) + + print("a & b = 0x\(aAndb)"); XCTAssertEqual(aAndb, "14510340000") + print("a | b = 0x\(aOrb)"); XCTAssertEqual(aOrb, "7fffffdd9a76ffff") + print("a ^ b = 0x\(aXorb)"); XCTAssertEqual(aXorb, "7ffffe988a42ffff") + print("~b = 0x\(notb)"); XCTAssertEqual(notb, "-abcd98770000") + + print("a << 16 = \(shiftLeft1)"); XCTAssertEqual(shiftLeft1, "7fff5555123400000000") + print("a << -3 = \(shiftLeft2)"); XCTAssertEqual(shiftLeft2, "fffeaaaa2468000") + print("a >> 1000 = \(shiftRight1)"); XCTAssertEqual(shiftRight1, "0") + print("a >> -7 = \(shiftRight2)"); XCTAssertEqual(shiftRight2, "3fffaaaa891a000000") + } // MARK: - Converting to/from textual representations From 400967992d76fa6d75d1954bcab1a2158c1e1297 Mon Sep 17 00:00:00 2001 From: Michael Griebling Date: Wed, 19 Apr 2023 10:44:05 -0400 Subject: [PATCH 2/7] Fixed a negate() corner case issue where a negative integer was turned into a negative. Fixed division/remainder signs after a multi-word division. --- Sources/BigIntModule/BigInt.swift | 48 +++++++++++++++++++++++------ Tests/BigIntTests/BigIntTests.swift | 13 ++++++++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/Sources/BigIntModule/BigInt.swift b/Sources/BigIntModule/BigInt.swift index c5b65c44..559923b2 100644 --- a/Sources/BigIntModule/BigInt.swift +++ b/Sources/BigIntModule/BigInt.swift @@ -29,12 +29,6 @@ public struct BigInt: SignedInteger { internal var _isNegative: Bool { words[words.endIndex - 1] > Int.max } - -// private static let _digits: [BigInt] = (0 ... 36).map { -// BigInt(_uncheckedWords: [UInt(bitPattern: $0)]) -// } - - // private static let _digitRadix = BigInt(_uncheckedWords: [0, 1]) } // MARK: - Basic Behaviors @@ -379,6 +373,7 @@ extension BigInt: SignedNumeric { public mutating func negate() { var isOverflow = true + let isNegative = self._isNegative for i in 0 ..< words.count { if isOverflow { (words[i], isOverflow) = (~words[i]).addingReportingOverflow(1) @@ -388,6 +383,12 @@ extension BigInt: SignedNumeric { } BigInt._dropExcessWords(words: &words) + if self != Self.zero && self._isNegative == isNegative { + // Corner case where numbers like `0x8000000000000000 ... 0000` + // remain unchanged after negation so we make sure any negative + // numbers are truly negated into positive numbers + if isNegative { words.append(0) } // make the number positive + } } @inlinable @@ -442,7 +443,7 @@ extension BigInt: BinaryInteger { public init(_ source: T) where T: BinaryInteger { if source >= 0, source < UInt.max { - words = [UInt(source)] // no need for _digits + words = [UInt(source)] } else { words = Words(source.words) if source > 0 && source.words[source.words.endIndex - 1] > Int.max { @@ -732,7 +733,13 @@ extension BigInt { } BigInt._dropExcessWords(words: ") - return (quotient: BigInt(_uncheckedWords: quot), remainder: BigInt(r)) + // signs are based on the Int definitions + switch (lhsIsNeg, rhsIsNeg) { + case (false, true): return (-BigInt(_uncheckedWords: quot), BigInt(r)) + case (false, false): return ( BigInt(_uncheckedWords: quot), BigInt(r)) + case (true, false): return (-BigInt(_uncheckedWords: quot), -BigInt(r)) + case (true, true): return ( BigInt(_uncheckedWords: quot), -BigInt(r)) + } } while rhsWords[rhsWords.endIndex - 1] == 0 { @@ -839,8 +846,14 @@ extension BigInt { BigInt._dropExcessWords(words: ") BigInt._dropExcessWords(words: &rem) - - return (BigInt(_uncheckedWords: quot), BigInt(_uncheckedWords: rem)) + + // signs are based on the Int definitions + switch (lhsIsNeg, rhsIsNeg) { + case (false, true): return (-BigInt(_uncheckedWords: quot), BigInt(_uncheckedWords: rem)) + case (false, false): return ( BigInt(_uncheckedWords: quot), BigInt(_uncheckedWords: rem)) + case (true, false): return (-BigInt(_uncheckedWords: quot), -BigInt(_uncheckedWords: rem)) + case (true, true): return ( BigInt(_uncheckedWords: quot), -BigInt(_uncheckedWords: rem)) + } } private static func _signExtend(lhsWords: inout Words, rhsWords: inout Words) { @@ -877,3 +890,18 @@ extension BigInt { } } } + +//extension BigInt : CustomStringConvertible { +// +// public var description: String { +// /// Kludge fix for negative numbers like `0x8000000000000000 ... 0000` +// /// whose strings otherwise look like this `-(\'((,*+*),-//+)\'+-(*,*+.+\'+-\',+/.-**(0(` +// /// Probably a division/remainder issue in not detecting the right sign +// if self._isNegative && words.count > 1 && self.magnitude == self { +// let num = BigInt(_uncheckedWords: self.words + [0]) // add leading zero word so sign is positive +// return "-" + String(num, radix: 10) +// } +// return String(self, radix: 10) +// } +// +//} diff --git a/Tests/BigIntTests/BigIntTests.swift b/Tests/BigIntTests/BigIntTests.swift index 81f45395..0ca24528 100644 --- a/Tests/BigIntTests/BigIntTests.swift +++ b/Tests/BigIntTests/BigIntTests.swift @@ -116,6 +116,19 @@ final class BigIntTests: XCTestCase { // MARK: - Basic arithmetic func testDivision() { + // Signed division test + let numa = BigInt("-18446744073709551616")! + let dena = BigInt(123) + let expecteda = BigInt(Int64(-149973529054549200)) + XCTAssertEqual(numa / dena, expecteda) + + let numb = BigInt("18446744073709551616")! + let denb = BigInt(-123) + XCTAssertEqual(numb / denb, expecteda) + let expectedb = BigInt(Int64(149973529054549200)) + XCTAssertEqual(numa / denb, expectedb) + + // Previous test cases let num1 = BigInt("18446744073709551616")! let den1 = BigInt(123) let expected1 = BigInt(UInt64(149973529054549200)) From 5e55107fc77914be6b3483ade8d1160f1f922434 Mon Sep 17 00:00:00 2001 From: Michael Griebling Date: Wed, 19 Apr 2023 16:12:57 -0400 Subject: [PATCH 3/7] Fixes issue with FloatingPoint overflow and other integer test overflows. --- Sources/BigIntModule/BigInt.swift | 55 +----------------- Tests/BigIntTests/BigIntTests.swift | 88 +++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 53 deletions(-) diff --git a/Sources/BigIntModule/BigInt.swift b/Sources/BigIntModule/BigInt.swift index 559923b2..a01d3c0e 100644 --- a/Sources/BigIntModule/BigInt.swift +++ b/Sources/BigIntModule/BigInt.swift @@ -157,41 +157,6 @@ extension BigInt: LosslessStringConvertible { /// NEW CODE ENDS /// ////////////////////////////////////////////////////////////////////////////////////////////////// - - -//// Original code -// public init?(_ description: T, radix: Int = 10) where T: StringProtocol { -// precondition(2 ... 36 ~= radix, "Radix not in range 2 ... 36") -// -// self = 0 -// -// let isNegative = description.hasPrefix("-") -// let hasPrefix = isNegative || description.hasPrefix("+") -// let utf8 = description.utf8.dropFirst(hasPrefix ? 1 : 0) -// guard !utf8.isEmpty else { return nil } -// -// for var byte in utf8 { -// switch byte { -// case UInt8(ascii: "0") ... UInt8(ascii: "9"): -// byte -= UInt8(ascii: "0") -// case UInt8(ascii: "A") ... UInt8(ascii: "Z"): -// byte -= UInt8(ascii: "A") -// byte += 10 -// case UInt8(ascii: "a") ... UInt8(ascii: "z"): -// byte -= UInt8(ascii: "a") -// byte += 10 -// default: -// return nil -// } -// guard byte < radix else { return nil } -// self *= BigInt._digits[radix] -// self += BigInt._digits[Int(byte)] -// } -// -// if isNegative { -// self.negate() -// } -// } } extension BigInt: Decodable { @@ -442,7 +407,7 @@ extension BigInt: BinaryInteger { } public init(_ source: T) where T: BinaryInteger { - if source >= 0, source < UInt.max { + if source >= 0 && source < Int.max { words = [UInt(source)] } else { words = Words(source.words) @@ -460,7 +425,7 @@ extension BigInt: BinaryInteger { } public init(truncatingIfNeeded source: T) where T: BinaryInteger { - words = Words(source.words) + self.init(source) //words = Words(source.words) } public var bitWidth: Int { words.count * UInt.bitWidth } @@ -492,7 +457,6 @@ extension BigInt: BinaryInteger { @inlinable public static func % (lhs: BigInt, rhs: BigInt) -> BigInt { let (_, result) = _div(lhs: lhs, rhs: rhs) - return result } @@ -890,18 +854,3 @@ extension BigInt { } } } - -//extension BigInt : CustomStringConvertible { -// -// public var description: String { -// /// Kludge fix for negative numbers like `0x8000000000000000 ... 0000` -// /// whose strings otherwise look like this `-(\'((,*+*),-//+)\'+-(*,*+.+\'+-\',+/.-**(0(` -// /// Probably a division/remainder issue in not detecting the right sign -// if self._isNegative && words.count > 1 && self.magnitude == self { -// let num = BigInt(_uncheckedWords: self.words + [0]) // add leading zero word so sign is positive -// return "-" + String(num, radix: 10) -// } -// return String(self, radix: 10) -// } -// -//} diff --git a/Tests/BigIntTests/BigIntTests.swift b/Tests/BigIntTests/BigIntTests.swift index 0ca24528..d837385d 100644 --- a/Tests/BigIntTests/BigIntTests.swift +++ b/Tests/BigIntTests/BigIntTests.swift @@ -307,6 +307,94 @@ final class BigIntTests: XCTestCase { XCTAssertEqual(-BigInt("-1234567890123456789012345678901234567890")!, +BigInt("+1234567890123456789012345678901234567890")!) } + + func testsFromViolet() { + // -9223372036854775808 = Int64.min, obviously '-Int64.min' overflows + let int: Int64 = -9223372036854775808 + var big = BigInt(int) + XCTAssertEqual(-big, big * -1) + + // 9223372036854775808 = UInt64(1) << Float80.significandBitCount + var int2 : UInt64 = 9223372036854775808 + big = BigInt(int2) + let fromInt = Float80(exactly: int2) // works + let fromBigInt = Float80(exactly: big) // crash (not anymore) + + // 18446744073709551615 = UInt64.max - was crashing + int2 = 18446744073709551615 + big = BigInt(int2) + let revert = UInt64(big) + } + + func test_initFromInt_exactly() { + let int: UInt64 = 18446744073709551614 + let big = BigInt(exactly: int)! + let revert = UInt64(exactly: big) + XCTAssertEqual(int, revert) + } + + func test_initFromInt_clamping() { + let int: UInt64 = 18446744073709551614 + let big = BigInt(clamping: int) + let revert = UInt64(clamping: big) + XCTAssertEqual(int, revert) + } + + func test_initFromInt_truncatingIfNeeded() { + let int: UInt64 = 18446744073709551615 + let big = BigInt(truncatingIfNeeded: int) + let intString = String(int, radix: 10, uppercase: false) + let bigString = String(big, radix: 10, uppercase: false) + XCTAssertEqual(bigString, intString) + } + + func test_node_div_incorrectSign() { + // positive / negative = negative + var lhs = BigInt("18446744073709551615")! + var rhs = BigInt("-1")! + var expected = BigInt("-18446744073709551615")! + XCTAssertEqual(lhs / rhs, expected) + + // negative / positive = negative + lhs = BigInt("-340282366920938463481821351505477763074")! + rhs = BigInt("18446744073709551629")! + expected = BigInt("-18446744073709551604")! + XCTAssertEqual(lhs / rhs, expected) + } + + func test_node_mod_incorrectSign() { + // SMALL % BIG = SMALL + // We need to satisfy: BIG * 0 + result = SMALL -> result = SMALL + // The same, but on the standard Swift.Int to prove the point: + XCTAssertEqual(-1 % 123, -1) + XCTAssertEqual(-1 % -123, -1) + // In general the 'reminder' follows the 'lhs' sign (round toward 0). + // Except for the case where 'lhs' is negative and 'reminder' is 0. + + var lhs = BigInt("-1")! + var rhs = BigInt("18446744073709551615")! + XCTAssertEqual(lhs % rhs, lhs) + + // Also fails if 'rhs' is negative + lhs = BigInt("-7730941133")! + rhs = BigInt("-18446744073709551615")! + XCTAssertEqual(lhs % rhs, lhs) + } + + // From my observations all of the `xor` tests are failing. + func test_node_xor() { + var lhs = BigInt("0")! + var rhs = BigInt("1")! + var expected = BigInt("1")! + XCTAssertEqual(lhs ^ rhs, expected) + XCTAssertEqual(0 ^ 1, 1) // Proof + + lhs = BigInt("0")! + rhs = BigInt("-1")! + expected = BigInt("-1")! + XCTAssertEqual(lhs ^ rhs, expected) + XCTAssertEqual(0 ^ -1, -1) // Proof + } func testHashable() { let foo = BigInt("1234567890123456789012345678901234567890")! From 67b17c2feccebbf3bf9c3ff91856ad51a427b4d7 Mon Sep 17 00:00:00 2001 From: Michael Griebling Date: Wed, 19 Apr 2023 17:25:50 -0400 Subject: [PATCH 4/7] Fix corner-case overflow with Int.min and -1 division. --- Sources/BigIntModule/BigInt.swift | 10 ++- Tests/BigIntTests/BigIntTests.swift | 96 +++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 7 deletions(-) diff --git a/Sources/BigIntModule/BigInt.swift b/Sources/BigIntModule/BigInt.swift index a01d3c0e..bf4b39be 100644 --- a/Sources/BigIntModule/BigInt.swift +++ b/Sources/BigIntModule/BigInt.swift @@ -670,9 +670,15 @@ extension BigInt { internal static func _div(lhs: BigInt, rhs: BigInt) -> (quotient: BigInt, remainder: BigInt) { precondition(rhs != 0, "Division by zero error!") + // Speed up single-word divisions if lhs.words.count == 1, rhs.words.count == 1 { - let (quot, rem) = Int(bitPattern: lhs.words[0]).quotientAndRemainder(dividingBy: Int(bitPattern: rhs.words[0])) - return (BigInt(_uncheckedWords: [UInt(bitPattern: quot)]), BigInt(_uncheckedWords: [UInt(bitPattern: rem)])) + // check for corner case that causes overflow: Int.min / -1 + let lhsInt = Int(bitPattern: lhs.words[0]) + let rhsInt = Int(bitPattern: rhs.words[0]) + if !(lhsInt == Int.min && rhsInt == -1) { + let (quot, rem) = lhsInt.quotientAndRemainder(dividingBy: rhsInt) + return (BigInt(_uncheckedWords: [UInt(bitPattern: quot)]), BigInt(_uncheckedWords: [UInt(bitPattern: rem)])) + } } let lhsIsNeg = lhs._isNegative diff --git a/Tests/BigIntTests/BigIntTests.swift b/Tests/BigIntTests/BigIntTests.swift index d837385d..95cbafa7 100644 --- a/Tests/BigIntTests/BigIntTests.swift +++ b/Tests/BigIntTests/BigIntTests.swift @@ -308,7 +308,7 @@ final class BigIntTests: XCTestCase { +BigInt("+1234567890123456789012345678901234567890")!) } - func testsFromViolet() { + func testsWhichCrashed() { // -9223372036854775808 = Int64.min, obviously '-Int64.min' overflows let int: Int64 = -9223372036854775808 var big = BigInt(int) @@ -317,13 +317,20 @@ final class BigIntTests: XCTestCase { // 9223372036854775808 = UInt64(1) << Float80.significandBitCount var int2 : UInt64 = 9223372036854775808 big = BigInt(int2) - let fromInt = Float80(exactly: int2) // works - let fromBigInt = Float80(exactly: big) // crash (not anymore) + let _ = Float80(exactly: int2) // works + let _ = Float80(exactly: big) // crash (not anymore) // 18446744073709551615 = UInt64.max - was crashing int2 = 18446744073709551615 big = BigInt(int2) - let revert = UInt64(big) + let _ = UInt64(big) + + // was generating an overflow + let lhsInt = -9223372036854775808 + let rhsInt = -1 + let lhs = BigInt(lhsInt) + let rhs = BigInt(rhsInt) + _ = lhs / rhs // Overflow } func test_initFromInt_exactly() { @@ -348,7 +355,21 @@ final class BigIntTests: XCTestCase { XCTAssertEqual(bigString, intString) } - func test_node_div_incorrectSign() { + func test_unaryMinus() { + // -9223372036854775808 = Int.min + // 'Int.min' negation overflows - ok now + let int = -9223372036854775808 + let expected = BigInt(int.magnitude) + + let big = -BigInt(int) + XCTAssertEqual(big, expected) + + var negated = BigInt(int) + negated.negate() + XCTAssertEqual(negated, expected, "\(negated) == \(expected)") + } + + func test_div_sign() { // positive / negative = negative var lhs = BigInt("18446744073709551615")! var rhs = BigInt("-1")! @@ -361,7 +382,34 @@ final class BigIntTests: XCTestCase { expected = BigInt("-18446744073709551604")! XCTAssertEqual(lhs / rhs, expected) } + + func test_magnitude() { + let big = BigInt(-9223372036854775808) + let expect = UInt64(9223372036854775808) + XCTAssertEqual(big.magnitude, BigInt(expect)) + } + + // ApplyA_ApplyB_Equals_ApplyAB.swift + func test_a_b_ab() { + // (a/-3) / -5 = a/15 + let lhs = BigInt("-18446744073709551615")! + let a = BigInt(-3) + let b = BigInt(-5) + let ab = BigInt(15) + + let r0 = (lhs / a) / b + let r1 = lhs / ab + XCTAssertEqual(r0, r1) + } + // ApplyA_UndoA.swift + func test_a_undoA() { + let n = BigInt(-1) + let x = BigInt(-9223372036854775808) + XCTAssertEqual(n, (n + x) - x) + XCTAssertEqual(n, (n * x) / x) + } + func test_node_mod_incorrectSign() { // SMALL % BIG = SMALL // We need to satisfy: BIG * 0 + result = SMALL -> result = SMALL @@ -395,6 +443,44 @@ final class BigIntTests: XCTestCase { XCTAssertEqual(lhs ^ rhs, expected) XCTAssertEqual(0 ^ -1, -1) // Proof } + + func test_xor_truthTable() { + let lhsWord : UInt8 = 0b1100 + let rhsWord : UInt8 = 0b1010 + + let lhs = BigInt(lhsWord) + let rhs = BigInt(rhsWord) + + let expected = BigInt(lhsWord ^ rhsWord) + XCTAssertEqual(lhs ^ rhs, expected) + XCTAssertEqual(rhs ^ lhs, expected) + } + + func test_binarySub() { + // https://www.wolframalpha.com/input?i=-922337203685477587+-+%28-9223372036854775808%29 + let lhs = BigInt("-922337203685477587")! + let rhs = BigInt("-9223372036854775808")! + let expected = BigInt("8301034833169298221")! + XCTAssertEqual(lhs - rhs, expected) + // The same on Swift.Int: + XCTAssertEqual(-922337203685477587 - (-9223372036854775808), 8301034833169298221) + } + + func test_binarySub_2() { + typealias Word = UInt + + let intMax = Int.max + let intMaxAsWord = Word(intMax.magnitude) + + // intMax - (-(Word.max - intMaxAsWord)) = + // intMax - (-Word.max + intMaxAsWord) = + // intMax + Word.max - intMaxAsWord = + // Word.max + let max = BigInt(intMax) + let value = -BigInt((Word.max - intMaxAsWord)) + let expected = BigInt(Word.max) + XCTAssertEqual(max - value, expected) + } func testHashable() { let foo = BigInt("1234567890123456789012345678901234567890")! From 01928d041c20d47fcc1dbac8361e949331fb671e Mon Sep 17 00:00:00 2001 From: Michael Griebling Date: Thu, 20 Apr 2023 17:01:50 -0400 Subject: [PATCH 5/7] Added new CustomStringConvertible implementation (20X to 70X faster than the default). --- Sources/BigIntModule/BigInt.swift | 178 +++- Tests/BigIntTests/BigIntTests.swift | 1193 ++++++++++++++------------- 2 files changed, 777 insertions(+), 594 deletions(-) diff --git a/Sources/BigIntModule/BigInt.swift b/Sources/BigIntModule/BigInt.swift index bf4b39be..5fa84e0e 100644 --- a/Sources/BigIntModule/BigInt.swift +++ b/Sources/BigIntModule/BigInt.swift @@ -9,6 +9,8 @@ // //===----------------------------------------------------------------------===// +import Foundation + public struct BigInt: SignedInteger { public typealias Words = [UInt] @@ -90,8 +92,8 @@ extension BigInt: LosslessStringConvertible { public init?(_ description: String) { self.init(description, radix: 10) } - - + + //////////////////////////////////////////////////////////////////////////// /// /// NEW CODE STARTS @@ -104,7 +106,7 @@ extension BigInt: LosslessStringConvertible { while lexp > 0 { if !lexp.isMultiple(of: 2) { y*=x } lexp >>= 1 - if lexp > 0 { x=x*x } // could be 2X faster with squared function + if lexp > 0 { x=x*x } } return y } @@ -153,10 +155,178 @@ extension BigInt: LosslessStringConvertible { self.negate() } } + + /// NEW CODE ENDS + /// + //////////////////////////////////////////////////////////////////////////// +} + +extension BigInt : CustomStringConvertible { + + //////////////////////////////////////////////////////////////////////////// + /// + /// NEW CODE STARTS + + /// Code below was shamelessly adapted from the Violet implementation of + /// BigInt. I took a few shortcuts to reduce the code size: There's no + /// DivBuffer and instead used a copy of the self.words for doing the + /// divisions/shifts. A different approach was used to calculate the number + /// of digits needed in a string — again less code was needed. The good: + /// This code is almost 20X faster for decimals and 90X faster for binary + /// radixes. The bad: Lots of code required to implement this improvement. + /// Is it worth it? You decide. It's always easy to comment out and use the + /// default (albeit much slower) String() implementation. Maybe the String + /// in the runtime could be updated to be faster. + public var description: String { + //return String(self, radix: 10) + return toString(radix: 10, uppercase: false) + } + + private static var maxRadix: Int { 36 } + + // ASCII constants + private static let _0: UInt8 = Character("0").asciiValue! + private static let _A: UInt8 = Character("A").asciiValue! + private static let _a: UInt8 = Character("a").asciiValue! + private static let _minus: UInt8 = Character("-").asciiValue! + + internal func div(_ dividend: inout Words, by divisor: UInt) -> UInt { + var carry = UInt(0) + for i in (0.. String { + precondition(2 ... Self.maxRadix ~= radix, "Radix not in range 2 ... \(Self.maxRadix)") + + if self.signum() == 0 { return "0" } + + let logWord = log(Double(UInt.max)) + let logRadix = log(Double(radix)) + let digitCount = Int(logWord/logRadix * Double(words.count) + 0.5) + let (charPerWord, power) = Self.maxRepresentablePower(of: radix) + let stringMaxSize = digitCount + (self._isNegative ? 1 : 0) + let isPowerOfTwoRadix = radix & (radix-1) == 0 + + var words = self.magnitude.words; words.reserveCapacity(self.words.count) + if #available(macOS 11.0, *) { + return StringLiteralType(unsafeUninitializedCapacity: stringMaxSize) { buffer in + + if isPowerOfTwoRadix { + // Handle powers-of-two radixes + let bitsPerChar = radix.trailingZeroBitCount + let qr = UInt.bitWidth.quotientAndRemainder(dividingBy: bitsPerChar) + assert(qr.remainder == 0, "String radix = \(radix) which does not fit in \(UInt.bitWidth) bits") + var index = 0 + if self._isNegative { + buffer[index] = Self._minus + index = 1 + } + + // digits fit exactly into a word + // 0000 0001 1010 0101 + // ^^^^ skip (but only for 1st word) + let charCountPerWord = qr.quotient + let last = words[words.count - 1] + var skip = charCountPerWord - last.leadingZeroBitCount / bitsPerChar - 1 + let mask = UInt(radix - 1) + + for word in words.reversed() { + for groupIndex in stride(from: skip, through: 0, by: -1) { + let shift = groupIndex &* bitsPerChar + let group = (word &>> shift) & mask + buffer[index] = ascii(group, uppercase: uppercase) + index &+= 1 + } + + // From now on we print everything, even middle '0' + skip = charCountPerWord - 1 + } + + assert(index <= stringMaxSize) + return index + } else { + // Deal with the hard-to-use radixes including base 10 + var index = stringMaxSize - 1 // start at end of the buffer + while !words.isEmpty { + var remainder = div(&words, by: power) + let end = index - charPerWord + + // extract `radix` digits and add to the string `buffer` + while remainder != 0 { + let qr = remainder.quotientAndRemainder(dividingBy: UInt(radix)) + remainder = qr.quotient + buffer[index] = ascii(qr.remainder, uppercase: uppercase) + index &-= 1 + } + + // fill remaining word digits (except the first) with "0"s + let isFirstWord = words.isEmpty + while !isFirstWord && index != end { + buffer[index] = Self._0 + index &-= 1 + } + } + + // add a minus sign if the number is negative + if self._isNegative { + buffer[index] = Self._minus + index &-= 1 + } + + // check for invalid capacity estimate + var count = stringMaxSize + if index != -1 { + count = stringMaxSize - index - 1 + let dstPtr = buffer.baseAddress! + let srcPtr = dstPtr.advanced(by: index+1) + dstPtr.update(from: srcPtr, count: count) + dstPtr[count] = 0 + } + return count + } + } + } else { + // Fallback on earlier versions + return String(self, radix: radix, uppercase: uppercase) + } + } + + /// Returns the highest number that satisfy `radix^n <= 2^Self.bitWidth` + internal static func maxRepresentablePower(of radix: Int) -> (n: Int, power: UInt) { + var n = 1 + var power = UInt(radix) + + while true { + let (newPower, overflow) = power.multipliedReportingOverflow(by: UInt(radix)) + + if overflow { + return (n, power) + } + + n += 1 + power = newPower + } + } + + private func ascii(_ n: UInt, uppercase: Bool) -> UInt8 { + // Performance sensitive area! Order of operations matters! + // Compiler will emit code without overflow checks, + // so we do not need to use unchecked '&' (like '&+' and '&-'). + assert(n < Self.maxRadix) // Always less, never equal! + let n = UInt8(truncatingIfNeeded: n) + return n < 10 ? n + Self._0 : n - 10 + (uppercase ? Self._A : Self._a) + } /// NEW CODE ENDS /// - ////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// } extension BigInt: Decodable { diff --git a/Tests/BigIntTests/BigIntTests.swift b/Tests/BigIntTests/BigIntTests.swift index 95cbafa7..5957d220 100644 --- a/Tests/BigIntTests/BigIntTests.swift +++ b/Tests/BigIntTests/BigIntTests.swift @@ -115,601 +115,614 @@ final class BigIntTests: XCTestCase { // MARK: - Basic arithmetic - func testDivision() { - // Signed division test - let numa = BigInt("-18446744073709551616")! - let dena = BigInt(123) - let expecteda = BigInt(Int64(-149973529054549200)) - XCTAssertEqual(numa / dena, expecteda) - - let numb = BigInt("18446744073709551616")! - let denb = BigInt(-123) - XCTAssertEqual(numb / denb, expecteda) - let expectedb = BigInt(Int64(149973529054549200)) - XCTAssertEqual(numa / denb, expectedb) - - // Previous test cases - let num1 = BigInt("18446744073709551616")! - let den1 = BigInt(123) - let expected1 = BigInt(UInt64(149973529054549200)) - XCTAssertEqual(num1 / den1, expected1) - - let num2 = BigInt.pow(BigInt(10), 100) - let den2: BigInt = 3 - let expected2: BigInt = BigInt(String(repeating: "3", count: 100))! - let actual2 = num2 / den2 - XCTAssertEqual(actual2, expected2) - - let num3 = BigInt.pow(BigInt(10), 97) - let den3: BigInt = BigInt("33333333333333333333")! - let expected3: BigInt = BigInt("300000000000000000003000000000000000000030000000000000000000300000000000000000")! - let actual3 = num3 / den3 - XCTAssertEqual(actual3, expected3) - - let foo = BigInt("12345678901234567890123456789012345678901234567890123456789012345678901234567890")! - let bar = BigInt("351235231535161613134135135135")! - let baz = foo / bar - XCTAssertEqual(baz, BigInt("35149318157164029153358504918339691272847595997760")!) - - XCTAssertNotNil(BigInt(exactly: 2.4e39)) - XCTAssertNotNil(BigInt(exactly: 1e38)) - XCTAssertEqual(BigInt(2.4e39) / BigInt(1e38), BigInt(24)) - - for _ in 0 ..< 100 { - let expected = BigInt(Float64.random(in: 0x1p64 ... 0x1p255)) - let divisor = BigInt(Float64.random(in: 0x1p64 ... 0x1p128)) - let (quotient, remainder) = expected.quotientAndRemainder(dividingBy: divisor) - let actual = divisor * quotient + remainder - XCTAssertEqual(quotient, expected / divisor) - XCTAssertEqual(remainder, expected % divisor) - XCTAssertEqual( - actual, expected, - """ - ## FAILURE ## - ~~~~~~~~~~~~~ - actual: \(actual) - != expected: \(expected) - ~~~~~~~~~~~~~ - divisor: \(divisor) - * quotient: \(quotient) - + remainder: \(remainder) - ~~~~~~~~~~~~~ - """) - } - } - - func testStringToBigIntImprovements() { - var expectedNumber: BigInt! - - measure { - expectedNumber = BigInt(Self.descriptionFactorial512_radix36, radix: 36) - } - - XCTAssertEqual(expectedNumber, BigInt(Self.descriptionFactorial512_radix10, radix: 10)) - } - - func testFactorial() { - var expectedNumber: BigInt? - var actualNumber: BigInt! - var actualString: String! - - measure { - expectedNumber = BigInt(Self.descriptionFactorial512_radix36, radix: 36) - actualNumber = BigInt.fac(512) - actualString = String(actualNumber, radix: 36, uppercase: true) - } - - XCTAssertEqual(actualNumber, expectedNumber) - XCTAssertEqual(actualString, Self.descriptionFactorial512_radix36) - - XCTAssertEqual(BigInt.fac(0), 1) - XCTAssertEqual(BigInt.fac(1), 1) - XCTAssertEqual(BigInt.fac(2), 2) - XCTAssertEqual(BigInt.fac(3), 6) - XCTAssertEqual(BigInt.fac(4), 24) - XCTAssertEqual(BigInt.fac(5), 120) - XCTAssertEqual(BigInt.fac(6), 720) - XCTAssertEqual(BigInt.fac(7), 5040) - XCTAssertEqual(BigInt.fac(8), 40320) - XCTAssertEqual(BigInt.fac(9), 362880) - } - - func testMath() { - let foo = BigInt.pow(10, 20) - let bar = BigInt("1234567890123456789012345678901234567890")! - - let baz = foo + bar - - XCTAssertEqual(baz, BigInt("1234567890123456789112345678901234567890")!) - - let fooz = foo >> BigInt(10) - XCTAssertEqual(fooz, foo / 1024) - - let barz = BigInt(1) << 64 - XCTAssertEqual(barz, BigInt(UInt64.max) + 1) - } - - func testNegation() { - let foo = BigInt("1234567890123456789012345678901234567890")! - let bar = BigInt(0) - foo - - XCTAssertEqual(-foo, bar) - - var baz = foo - baz.negate() - XCTAssertEqual(baz, bar) - } - - func testSignum() { - XCTAssertEqual(BigInt(-0x1p1023).signum(), -1) - XCTAssertEqual(BigInt(Int64.min).signum(), -1) - XCTAssertEqual(BigInt(Int32.min).signum(), -1) - XCTAssertEqual(BigInt(Int16.min).signum(), -1) - XCTAssertEqual(BigInt(Int8.min).signum(), -1) - XCTAssertEqual(BigInt(-1).signum(), -1) - XCTAssertEqual(BigInt(0).signum(), 0) - XCTAssertEqual(BigInt(+1).signum(), +1) - XCTAssertEqual(BigInt(Int8.max).signum(), +1) - XCTAssertEqual(BigInt(Int16.max).signum(), +1) - XCTAssertEqual(BigInt(Int32.max).signum(), +1) - XCTAssertEqual(BigInt(Int64.max).signum(), +1) - XCTAssertEqual(BigInt(+0x1p1023).signum(), +1) - } - - func testTrailingZeroCount() { - let foo = BigInt(1) << 300 - XCTAssertEqual(foo.trailingZeroBitCount, 300) - - let bar = (BigInt(1) << 300) + 0b101000 - XCTAssertEqual(bar.trailingZeroBitCount, 3) - } - - // MARK: - Comparing and hashing - - func testComparable() { - let foo = BigInt("1234567890123456789012345678901234567890")! - let bar = foo * foo - - XCTAssertLessThan(foo, bar) - XCTAssertFalse(foo < foo) - XCTAssertFalse(bar < bar) - XCTAssertFalse(foo > foo) - XCTAssertFalse(bar > bar) - XCTAssertGreaterThan(bar, foo) - - let baz = bar * -1 - - XCTAssertLessThan(baz, foo) - XCTAssertNotEqual(bar, baz) - XCTAssertFalse(baz < baz) - } - - func testComparison() { - let foo = BigInt(-10) - let bar = BigInt(-20) - - XCTAssert(foo > bar) - XCTAssert(bar < foo) - XCTAssert(foo == BigInt(-10)) - - let baz = BigInt.pow(foo, -bar) - XCTAssertEqual(baz, BigInt("100000000000000000000")!) - } - - func testExample() { - let bar = BigInt(exactly: -100) - XCTAssertNotNil(bar) - if let bar = bar { - XCTAssertLessThan(bar, 0) - XCTAssertGreaterThan(-bar, 0) - XCTAssertEqual(-bar, BigInt(100)) - } - XCTAssertEqual(-BigInt("-1234567890123456789012345678901234567890")!, - +BigInt("+1234567890123456789012345678901234567890")!) - } - - func testsWhichCrashed() { - // -9223372036854775808 = Int64.min, obviously '-Int64.min' overflows - let int: Int64 = -9223372036854775808 - var big = BigInt(int) - XCTAssertEqual(-big, big * -1) - - // 9223372036854775808 = UInt64(1) << Float80.significandBitCount - var int2 : UInt64 = 9223372036854775808 - big = BigInt(int2) - let _ = Float80(exactly: int2) // works - let _ = Float80(exactly: big) // crash (not anymore) - - // 18446744073709551615 = UInt64.max - was crashing - int2 = 18446744073709551615 - big = BigInt(int2) - let _ = UInt64(big) - - // was generating an overflow - let lhsInt = -9223372036854775808 - let rhsInt = -1 - let lhs = BigInt(lhsInt) - let rhs = BigInt(rhsInt) - _ = lhs / rhs // Overflow - } - - func test_initFromInt_exactly() { - let int: UInt64 = 18446744073709551614 - let big = BigInt(exactly: int)! - let revert = UInt64(exactly: big) - XCTAssertEqual(int, revert) - } - - func test_initFromInt_clamping() { - let int: UInt64 = 18446744073709551614 - let big = BigInt(clamping: int) - let revert = UInt64(clamping: big) - XCTAssertEqual(int, revert) - } - - func test_initFromInt_truncatingIfNeeded() { - let int: UInt64 = 18446744073709551615 - let big = BigInt(truncatingIfNeeded: int) - let intString = String(int, radix: 10, uppercase: false) - let bigString = String(big, radix: 10, uppercase: false) - XCTAssertEqual(bigString, intString) - } - - func test_unaryMinus() { - // -9223372036854775808 = Int.min - // 'Int.min' negation overflows - ok now - let int = -9223372036854775808 - let expected = BigInt(int.magnitude) - - let big = -BigInt(int) - XCTAssertEqual(big, expected) +// func testDivision() { +// // Signed division test +// let numa = BigInt("-18446744073709551616")! +// let dena = BigInt(123) +// let expecteda = BigInt(Int64(-149973529054549200)) +// XCTAssertEqual(numa / dena, expecteda) +// +// let numb = BigInt("18446744073709551616")! +// let denb = BigInt(-123) +// XCTAssertEqual(numb / denb, expecteda) +// let expectedb = BigInt(Int64(149973529054549200)) +// XCTAssertEqual(numa / denb, expectedb) +// +// // Previous test cases +// let num1 = BigInt("18446744073709551616")! +// let den1 = BigInt(123) +// let expected1 = BigInt(UInt64(149973529054549200)) +// XCTAssertEqual(num1 / den1, expected1) +// +// let num2 = BigInt.pow(BigInt(10), 100) +// let den2: BigInt = 3 +// let expected2: BigInt = BigInt(String(repeating: "3", count: 100))! +// let actual2 = num2 / den2 +// XCTAssertEqual(actual2, expected2) +// +// let num3 = BigInt.pow(BigInt(10), 97) +// let den3: BigInt = BigInt("33333333333333333333")! +// let expected3: BigInt = BigInt("300000000000000000003000000000000000000030000000000000000000300000000000000000")! +// let actual3 = num3 / den3 +// XCTAssertEqual(actual3, expected3) +// +// let foo = BigInt("12345678901234567890123456789012345678901234567890123456789012345678901234567890")! +// let bar = BigInt("351235231535161613134135135135")! +// let baz = foo / bar +// XCTAssertEqual(baz, BigInt("35149318157164029153358504918339691272847595997760")!) +// +// XCTAssertNotNil(BigInt(exactly: 2.4e39)) +// XCTAssertNotNil(BigInt(exactly: 1e38)) +// XCTAssertEqual(BigInt(2.4e39) / BigInt(1e38), BigInt(24)) +// +// for _ in 0 ..< 100 { +// let expected = BigInt(Float64.random(in: 0x1p64 ... 0x1p255)) +// let divisor = BigInt(Float64.random(in: 0x1p64 ... 0x1p128)) +// let (quotient, remainder) = expected.quotientAndRemainder(dividingBy: divisor) +// let actual = divisor * quotient + remainder +// XCTAssertEqual(quotient, expected / divisor) +// XCTAssertEqual(remainder, expected % divisor) +// XCTAssertEqual( +// actual, expected, +// """ +// ## FAILURE ## +// ~~~~~~~~~~~~~ +// actual: \(actual) +// != expected: \(expected) +// ~~~~~~~~~~~~~ +// divisor: \(divisor) +// * quotient: \(quotient) +// + remainder: \(remainder) +// ~~~~~~~~~~~~~ +// """) +// } +// } - var negated = BigInt(int) - negated.negate() - XCTAssertEqual(negated, expected, "\(negated) == \(expected)") - } - - func test_div_sign() { - // positive / negative = negative - var lhs = BigInt("18446744073709551615")! - var rhs = BigInt("-1")! - var expected = BigInt("-18446744073709551615")! - XCTAssertEqual(lhs / rhs, expected) - - // negative / positive = negative - lhs = BigInt("-340282366920938463481821351505477763074")! - rhs = BigInt("18446744073709551629")! - expected = BigInt("-18446744073709551604")! - XCTAssertEqual(lhs / rhs, expected) - } - - func test_magnitude() { - let big = BigInt(-9223372036854775808) - let expect = UInt64(9223372036854775808) - XCTAssertEqual(big.magnitude, BigInt(expect)) - } - - // ApplyA_ApplyB_Equals_ApplyAB.swift - func test_a_b_ab() { - // (a/-3) / -5 = a/15 - let lhs = BigInt("-18446744073709551615")! - let a = BigInt(-3) - let b = BigInt(-5) - let ab = BigInt(15) - - let r0 = (lhs / a) / b - let r1 = lhs / ab - XCTAssertEqual(r0, r1) - } - - // ApplyA_UndoA.swift - func test_a_undoA() { - let n = BigInt(-1) - let x = BigInt(-9223372036854775808) - XCTAssertEqual(n, (n + x) - x) - XCTAssertEqual(n, (n * x) / x) - } - - func test_node_mod_incorrectSign() { - // SMALL % BIG = SMALL - // We need to satisfy: BIG * 0 + result = SMALL -> result = SMALL - // The same, but on the standard Swift.Int to prove the point: - XCTAssertEqual(-1 % 123, -1) - XCTAssertEqual(-1 % -123, -1) - // In general the 'reminder' follows the 'lhs' sign (round toward 0). - // Except for the case where 'lhs' is negative and 'reminder' is 0. - - var lhs = BigInt("-1")! - var rhs = BigInt("18446744073709551615")! - XCTAssertEqual(lhs % rhs, lhs) - - // Also fails if 'rhs' is negative - lhs = BigInt("-7730941133")! - rhs = BigInt("-18446744073709551615")! - XCTAssertEqual(lhs % rhs, lhs) - } - - // From my observations all of the `xor` tests are failing. - func test_node_xor() { - var lhs = BigInt("0")! - var rhs = BigInt("1")! - var expected = BigInt("1")! - XCTAssertEqual(lhs ^ rhs, expected) - XCTAssertEqual(0 ^ 1, 1) // Proof - - lhs = BigInt("0")! - rhs = BigInt("-1")! - expected = BigInt("-1")! - XCTAssertEqual(lhs ^ rhs, expected) - XCTAssertEqual(0 ^ -1, -1) // Proof - } - - func test_xor_truthTable() { - let lhsWord : UInt8 = 0b1100 - let rhsWord : UInt8 = 0b1010 - - let lhs = BigInt(lhsWord) - let rhs = BigInt(rhsWord) - - let expected = BigInt(lhsWord ^ rhsWord) - XCTAssertEqual(lhs ^ rhs, expected) - XCTAssertEqual(rhs ^ lhs, expected) - } +// func testStringToBigIntImprovements() { +// var expectedNumber: BigInt! +// +// measure { +// expectedNumber = BigInt(Self.descriptionFactorial512_radix36, radix: 36) +// } +// +// XCTAssertEqual(expectedNumber, BigInt(Self.descriptionFactorial512_radix10, radix: 10)) +// } - func test_binarySub() { - // https://www.wolframalpha.com/input?i=-922337203685477587+-+%28-9223372036854775808%29 - let lhs = BigInt("-922337203685477587")! - let rhs = BigInt("-9223372036854775808")! - let expected = BigInt("8301034833169298221")! - XCTAssertEqual(lhs - rhs, expected) - // The same on Swift.Int: - XCTAssertEqual(-922337203685477587 - (-9223372036854775808), 8301034833169298221) - } - - func test_binarySub_2() { - typealias Word = UInt - - let intMax = Int.max - let intMaxAsWord = Word(intMax.magnitude) - - // intMax - (-(Word.max - intMaxAsWord)) = - // intMax - (-Word.max + intMaxAsWord) = - // intMax + Word.max - intMaxAsWord = - // Word.max - let max = BigInt(intMax) - let value = -BigInt((Word.max - intMaxAsWord)) - let expected = BigInt(Word.max) - XCTAssertEqual(max - value, expected) - } - - func testHashable() { - let foo = BigInt("1234567890123456789012345678901234567890")! - let bar = BigInt("1234567890123456789112345678901234567890")! - let baz: BigInt = 153 - - let dict = [ foo: "Hello", bar: "World", baz: "!" ] - - let hash = foo.hashValue - print(hash) - - XCTAssertEqual(dict[foo]!, "Hello") - XCTAssertEqual(dict[bar]!, "World") - } - - func testClampingConversion() { - XCTAssertEqual(BigInt(clamping: UInt64.max), BigInt(UInt64(18446744073709551615))) - } - - func testUIntConversion() { - let foo = BigInt(UInt.max) - XCTAssertNotEqual(foo, BigInt(-1)) - - let bar = BigInt(bitPattern: UInt.max) - XCTAssertEqual(bar, BigInt(-1)) - } - - // MARK: - Testing logical functions - - func testLogical() { - let a = BigInt("7FFF555512340000", radix: 16)! - let b = BigInt("0000ABCD9876FFFF", radix: 16)! - - let aAndb = String(a & b, radix: 16) - let aOrb = String(a | b, radix: 16) - let aXorb = String(a ^ b, radix: 16) - let notb = String(~b, radix: 16) - - let shiftLeft1 = String(a << 16, radix:16) - let shiftLeft2 = String(a << -3, radix:16) - let shiftRight1 = String(a >> 1000, radix:16) - let shiftRight2 = String(a >> -7, radix:16) + func testBigIntToStringImprovements() { + let number = BigInt.fac(512) + var expectedString = "" - print("a & b = 0x\(aAndb)"); XCTAssertEqual(aAndb, "14510340000") - print("a | b = 0x\(aOrb)"); XCTAssertEqual(aOrb, "7fffffdd9a76ffff") - print("a ^ b = 0x\(aXorb)"); XCTAssertEqual(aXorb, "7ffffe988a42ffff") - print("~b = 0x\(notb)"); XCTAssertEqual(notb, "-abcd98770000") - - print("a << 16 = \(shiftLeft1)"); XCTAssertEqual(shiftLeft1, "7fff5555123400000000") - print("a << -3 = \(shiftLeft2)"); XCTAssertEqual(shiftLeft2, "fffeaaaa2468000") - print("a >> 1000 = \(shiftRight1)"); XCTAssertEqual(shiftRight1, "0") - print("a >> -7 = \(shiftRight2)"); XCTAssertEqual(shiftRight2, "3fffaaaa891a000000") - } - - // MARK: - Converting to/from textual representations - - func testCodable() throws { - let lowerBound = BigInt("-1234567890123456789012345678901234567890")! - let upperBound = BigInt("+1234567890123456789012345678901234567890")! - let expectedRange: Range = lowerBound ..< upperBound - - let encoder = JSONEncoder() - let decoder = JSONDecoder() - let data = try encoder.encode(expectedRange) - let actualRange = try decoder.decode(Range.self, from: data) - - XCTAssertEqual(actualRange, expectedRange) - } - - func testCustomStringConvertible() { - XCTAssertEqual("\(BigInt(UInt64.min) - 2)", "-2") - XCTAssertEqual("\(BigInt(UInt64.min) - 1)", "-1") - XCTAssertEqual("\(BigInt(UInt64.min) + 0)", "0") - XCTAssertEqual("\(BigInt(UInt64.min) + 1)", "1") - XCTAssertEqual("\(BigInt(UInt64.min) + 2)", "2") - - XCTAssertEqual("\(BigInt(UInt64.max) - 2)", "18446744073709551613") - XCTAssertEqual("\(BigInt(UInt64.max) - 1)", "18446744073709551614") - XCTAssertEqual("\(BigInt(UInt64.max) + 0)", "18446744073709551615") - XCTAssertEqual("\(BigInt(UInt64.max) + 1)", "18446744073709551616") - XCTAssertEqual("\(BigInt(UInt64.max) + 2)", "18446744073709551617") - - XCTAssertEqual("\(BigInt(Int64.min) - 2)", "-9223372036854775810") - XCTAssertEqual("\(BigInt(Int64.min) - 1)", "-9223372036854775809") - XCTAssertEqual("\(BigInt(Int64.min) + 0)", "-9223372036854775808") - XCTAssertEqual("\(BigInt(Int64.min) + 1)", "-9223372036854775807") - XCTAssertEqual("\(BigInt(Int64.min) + 2)", "-9223372036854775806") - - XCTAssertEqual("\(BigInt(Int64.max) - 2)", "9223372036854775805") - XCTAssertEqual("\(BigInt(Int64.max) - 1)", "9223372036854775806") - XCTAssertEqual("\(BigInt(Int64.max) + 0)", "9223372036854775807") - XCTAssertEqual("\(BigInt(Int64.max) + 1)", "9223372036854775808") - XCTAssertEqual("\(BigInt(Int64.max) + 2)", "9223372036854775809") - - XCTAssertEqual("\(-(BigInt(1) << 1023))", Self.descriptionInt1024Min) - XCTAssertEqual("\(+(BigInt(1) << 1023) - 1)", Self.descriptionInt1024Max) - } - - func testLosslessStringConvertible() { - XCTAssertNil(BigInt("")) - XCTAssertNil(BigInt("-")) - XCTAssertNil(BigInt("+")) - XCTAssertNil(BigInt("A")) - XCTAssertNil(BigInt(" 0")) - XCTAssertNil(BigInt("0 ")) - - XCTAssertEqual(BigInt(UInt64.min) - 2, BigInt("-2")) - XCTAssertEqual(BigInt(UInt64.min) - 1, BigInt("-1")) - XCTAssertEqual(BigInt(UInt64.min) + 0, BigInt("0")) - XCTAssertEqual(BigInt(UInt64.min) + 1, BigInt("1")) - XCTAssertEqual(BigInt(UInt64.min) + 2, BigInt("2")) - - XCTAssertEqual(BigInt(UInt64.max) - 2, BigInt("18446744073709551613")) - XCTAssertEqual(BigInt(UInt64.max) - 1, BigInt("18446744073709551614")) - XCTAssertEqual(BigInt(UInt64.max) + 0, BigInt("18446744073709551615")) - XCTAssertEqual(BigInt(UInt64.max) + 1, BigInt("18446744073709551616")) - XCTAssertEqual(BigInt(UInt64.max) + 2, BigInt("18446744073709551617")) - - XCTAssertEqual(BigInt(Int64.min) - 2, BigInt("-9223372036854775810")) - XCTAssertEqual(BigInt(Int64.min) - 1, BigInt("-9223372036854775809")) - XCTAssertEqual(BigInt(Int64.min) + 0, BigInt("-9223372036854775808")) - XCTAssertEqual(BigInt(Int64.min) + 1, BigInt("-9223372036854775807")) - XCTAssertEqual(BigInt(Int64.min) + 2, BigInt("-9223372036854775806")) - - XCTAssertEqual(BigInt(Int64.max) - 2, BigInt("9223372036854775805")) - XCTAssertEqual(BigInt(Int64.max) - 1, BigInt("9223372036854775806")) - XCTAssertEqual(BigInt(Int64.max) + 0, BigInt("9223372036854775807")) - XCTAssertEqual(BigInt(Int64.max) + 1, BigInt("9223372036854775808")) - XCTAssertEqual(BigInt(Int64.max) + 2, BigInt("9223372036854775809")) - - XCTAssertEqual(-(BigInt(1) << 1023), BigInt(Self.descriptionInt1024Min)) - XCTAssertEqual(+(BigInt(1) << 1023) - 1, BigInt(Self.descriptionInt1024Max)) - } - - func testRadicesAndNumerals() { - for radix in 2 ... 36 { - for uppercase in [false, true] { - for _ in 0 ..< 100 { - let expectedNumber = BigInt(Int.random(in: .min ... .max)) - let expectedString = String(expectedNumber, - radix: radix, - uppercase: uppercase) - let actualNumber = BigInt(expectedString, radix: radix) - XCTAssertEqual(actualNumber, expectedNumber) - if radix == 10 { - let actualString = expectedNumber.description - XCTAssertEqual(actualString, expectedString) - } - } + measure { + for _ in 1 ... 1000 { + expectedString = number.toString(radix: 10, uppercase: false) } } + + XCTAssertEqual(expectedString, Self.descriptionFactorial512_radix10) } - // MARK: - Converting from floating-point binary types - - func testBinaryFloatingPoint(_ type: T.Type) where T: BinaryFloatingPoint { - var expected = BigInt(T.greatestFiniteMagnitude.significandBitPattern) - expected |= BigInt(1) << T.significandBitCount - expected <<= T.greatestFiniteMagnitude.exponent - expected >>= T.significandBitCount - - XCTAssertEqual(BigInt(exactly: -T.greatestFiniteMagnitude), -expected) - XCTAssertEqual(BigInt(exactly: +T.greatestFiniteMagnitude), +expected) - XCTAssertEqual(BigInt(-T.greatestFiniteMagnitude), -expected) - XCTAssertEqual(BigInt(+T.greatestFiniteMagnitude), +expected) - - XCTAssertNil(BigInt(exactly: -T.infinity)) - XCTAssertNil(BigInt(exactly: +T.infinity)) - - XCTAssertNil(BigInt(exactly: -T.leastNonzeroMagnitude)) - XCTAssertNil(BigInt(exactly: +T.leastNonzeroMagnitude)) - XCTAssertEqual(BigInt(-T.leastNonzeroMagnitude), 0) - XCTAssertEqual(BigInt(+T.leastNonzeroMagnitude), 0) - - XCTAssertNil(BigInt(exactly: -T.leastNormalMagnitude)) - XCTAssertNil(BigInt(exactly: +T.leastNormalMagnitude)) - XCTAssertEqual(BigInt(-T.leastNormalMagnitude), 0) - XCTAssertEqual(BigInt(+T.leastNormalMagnitude), 0) - - XCTAssertNil(BigInt(exactly: T.nan)) - XCTAssertNil(BigInt(exactly: T.signalingNaN)) - - XCTAssertNil(BigInt(exactly: -T.pi)) - XCTAssertNil(BigInt(exactly: +T.pi)) - XCTAssertEqual(BigInt(-T.pi), -3) - XCTAssertEqual(BigInt(+T.pi), +3) - - XCTAssertNil(BigInt(exactly: -T.ulpOfOne)) - XCTAssertNil(BigInt(exactly: +T.ulpOfOne)) - XCTAssertEqual(BigInt(-T.ulpOfOne), 0) - XCTAssertEqual(BigInt(+T.ulpOfOne), 0) - - XCTAssertEqual(BigInt(exactly: -T.zero), 0) - XCTAssertEqual(BigInt(exactly: +T.zero), 0) - XCTAssertEqual(BigInt(-T.zero), 0) - XCTAssertEqual(BigInt(+T.zero), 0) - } - - func testBinaryFloatingPoint() { - testBinaryFloatingPoint(Float32.self) - testBinaryFloatingPoint(Float64.self) - #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) - testBinaryFloatingPoint(Float80.self) - #endif - - for _ in 0 ..< 100 { - let small = Float32.random(in: -10 ... +10) - XCTAssertEqual(BigInt(small), BigInt(Int64(small))) - - let large = Float32.random(in: -0x1p23 ... +0x1p23) - XCTAssertEqual(BigInt(large), BigInt(Int64(large))) - } - - for _ in 0 ..< 100 { - let small = Float64.random(in: -10 ... +10) - XCTAssertEqual(BigInt(small), BigInt(Int64(small))) - - let large = Float64.random(in: -0x1p52 ... +0x1p52) - XCTAssertEqual(BigInt(large), BigInt(Int64(large))) - } - - #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) - for _ in 0 ..< 100 { - let small = Float80.random(in: -10 ... +10) - XCTAssertEqual(BigInt(small), BigInt(Int64(small))) - - let large = Float80.random(in: -0x1p63 ..< +0x1p63) - XCTAssertEqual(BigInt(large), BigInt(Int64(large))) - } - #endif - } +// func testFactorial() { +// var expectedNumber: BigInt? +// var actualNumber: BigInt! +// var actualString: String! +// +// measure { +// expectedNumber = BigInt(Self.descriptionFactorial512_radix36, radix: 36) +// actualNumber = BigInt.fac(512) +// actualString = String(actualNumber, radix: 36, uppercase: true) +// } +// +// XCTAssertEqual(actualNumber, expectedNumber) +// XCTAssertEqual(actualString, Self.descriptionFactorial512_radix36) +// +// XCTAssertEqual(BigInt.fac(0), 1) +// XCTAssertEqual(BigInt.fac(1), 1) +// XCTAssertEqual(BigInt.fac(2), 2) +// XCTAssertEqual(BigInt.fac(3), 6) +// XCTAssertEqual(BigInt.fac(4), 24) +// XCTAssertEqual(BigInt.fac(5), 120) +// XCTAssertEqual(BigInt.fac(6), 720) +// XCTAssertEqual(BigInt.fac(7), 5040) +// XCTAssertEqual(BigInt.fac(8), 40320) +// XCTAssertEqual(BigInt.fac(9), 362880) +// } +// +// func testMath() { +// let foo = BigInt.pow(10, 20) +// let bar = BigInt("1234567890123456789012345678901234567890")! +// +// let baz = foo + bar +// +// XCTAssertEqual(baz, BigInt("1234567890123456789112345678901234567890")!) +// +// let fooz = foo >> BigInt(10) +// XCTAssertEqual(fooz, foo / 1024) +// +// let barz = BigInt(1) << 64 +// XCTAssertEqual(barz, BigInt(UInt64.max) + 1) +// } +// +// func testNegation() { +// let foo = BigInt("1234567890123456789012345678901234567890")! +// let bar = BigInt(0) - foo +// +// XCTAssertEqual(-foo, bar) +// +// var baz = foo +// baz.negate() +// XCTAssertEqual(baz, bar) +// } +// +// func testSignum() { +// XCTAssertEqual(BigInt(-0x1p1023).signum(), -1) +// XCTAssertEqual(BigInt(Int64.min).signum(), -1) +// XCTAssertEqual(BigInt(Int32.min).signum(), -1) +// XCTAssertEqual(BigInt(Int16.min).signum(), -1) +// XCTAssertEqual(BigInt(Int8.min).signum(), -1) +// XCTAssertEqual(BigInt(-1).signum(), -1) +// XCTAssertEqual(BigInt(0).signum(), 0) +// XCTAssertEqual(BigInt(+1).signum(), +1) +// XCTAssertEqual(BigInt(Int8.max).signum(), +1) +// XCTAssertEqual(BigInt(Int16.max).signum(), +1) +// XCTAssertEqual(BigInt(Int32.max).signum(), +1) +// XCTAssertEqual(BigInt(Int64.max).signum(), +1) +// XCTAssertEqual(BigInt(+0x1p1023).signum(), +1) +// } +// +// func testTrailingZeroCount() { +// let foo = BigInt(1) << 300 +// XCTAssertEqual(foo.trailingZeroBitCount, 300) +// +// let bar = (BigInt(1) << 300) + 0b101000 +// XCTAssertEqual(bar.trailingZeroBitCount, 3) +// } +// +// // MARK: - Comparing and hashing +// +// func testComparable() { +// let foo = BigInt("1234567890123456789012345678901234567890")! +// let bar = foo * foo +// +// XCTAssertLessThan(foo, bar) +// XCTAssertFalse(foo < foo) +// XCTAssertFalse(bar < bar) +// XCTAssertFalse(foo > foo) +// XCTAssertFalse(bar > bar) +// XCTAssertGreaterThan(bar, foo) +// +// let baz = bar * -1 +// +// XCTAssertLessThan(baz, foo) +// XCTAssertNotEqual(bar, baz) +// XCTAssertFalse(baz < baz) +// } +// +// func testComparison() { +// let foo = BigInt(-10) +// let bar = BigInt(-20) +// +// XCTAssert(foo > bar) +// XCTAssert(bar < foo) +// XCTAssert(foo == BigInt(-10)) +// +// let baz = BigInt.pow(foo, -bar) +// XCTAssertEqual(baz, BigInt("100000000000000000000")!) +// } +// +// func testExample() { +// let bar = BigInt(exactly: -100) +// XCTAssertNotNil(bar) +// if let bar = bar { +// XCTAssertLessThan(bar, 0) +// XCTAssertGreaterThan(-bar, 0) +// XCTAssertEqual(-bar, BigInt(100)) +// } +// XCTAssertEqual(-BigInt("-1234567890123456789012345678901234567890")!, +// +BigInt("+1234567890123456789012345678901234567890")!) +// } +// +// func testsWhichCrashed() { +// // -9223372036854775808 = Int64.min, obviously '-Int64.min' overflows +// let int: Int64 = -9223372036854775808 +// var big = BigInt(int) +// XCTAssertEqual(-big, big * -1) +// +// // 9223372036854775808 = UInt64(1) << Float80.significandBitCount +// var int2 : UInt64 = 9223372036854775808 +// big = BigInt(int2) +// let _ = Float80(exactly: int2) // works +// let _ = Float80(exactly: big) // crash (not anymore) +// +// // 18446744073709551615 = UInt64.max - was crashing +// int2 = 18446744073709551615 +// big = BigInt(int2) +// let _ = UInt64(big) +// +// // was generating an overflow +// let lhsInt = -9223372036854775808 +// let rhsInt = -1 +// let lhs = BigInt(lhsInt) +// let rhs = BigInt(rhsInt) +// _ = lhs / rhs // Overflow +// } +// +// func test_initFromInt_exactly() { +// let int: UInt64 = 18446744073709551614 +// let big = BigInt(exactly: int)! +// let revert = UInt64(exactly: big) +// XCTAssertEqual(int, revert) +// } +// +// func test_initFromInt_clamping() { +// let int: UInt64 = 18446744073709551614 +// let big = BigInt(clamping: int) +// let revert = UInt64(clamping: big) +// XCTAssertEqual(int, revert) +// } +// +// func test_initFromInt_truncatingIfNeeded() { +// let int: UInt64 = 18446744073709551615 +// let big = BigInt(truncatingIfNeeded: int) +// let intString = String(int, radix: 10, uppercase: false) +// let bigString = String(big, radix: 10, uppercase: false) +// XCTAssertEqual(bigString, intString) +// } +// +// func test_unaryMinus() { +// // -9223372036854775808 = Int.min +// // 'Int.min' negation overflows - ok now +// let int = -9223372036854775808 +// let expected = BigInt(int.magnitude) +// +// let big = -BigInt(int) +// XCTAssertEqual(big, expected) +// +// var negated = BigInt(int) +// negated.negate() +// XCTAssertEqual(negated, expected, "\(negated) == \(expected)") +// } +// +// func test_div_sign() { +// // positive / negative = negative +// var lhs = BigInt("18446744073709551615")! +// var rhs = BigInt("-1")! +// var expected = BigInt("-18446744073709551615")! +// XCTAssertEqual(lhs / rhs, expected) +// +// // negative / positive = negative +// lhs = BigInt("-340282366920938463481821351505477763074")! +// rhs = BigInt("18446744073709551629")! +// expected = BigInt("-18446744073709551604")! +// XCTAssertEqual(lhs / rhs, expected) +// } +// +// func test_magnitude() { +// let big = BigInt(-9223372036854775808) +// let expect = UInt64(9223372036854775808) +// XCTAssertEqual(big.magnitude, BigInt(expect)) +// } +// +// // ApplyA_ApplyB_Equals_ApplyAB.swift +// func test_a_b_ab() { +// // (a/-3) / -5 = a/15 +// let lhs = BigInt("-18446744073709551615")! +// let a = BigInt(-3) +// let b = BigInt(-5) +// let ab = BigInt(15) +// +// let r0 = (lhs / a) / b +// let r1 = lhs / ab +// XCTAssertEqual(r0, r1) +// } +// +// // ApplyA_UndoA.swift +// func test_a_undoA() { +// let n = BigInt(-1) +// let x = BigInt(-9223372036854775808) +// XCTAssertEqual(n, (n + x) - x) +// XCTAssertEqual(n, (n * x) / x) +// } +// +// func test_node_mod_incorrectSign() { +// // SMALL % BIG = SMALL +// // We need to satisfy: BIG * 0 + result = SMALL -> result = SMALL +// // The same, but on the standard Swift.Int to prove the point: +// XCTAssertEqual(-1 % 123, -1) +// XCTAssertEqual(-1 % -123, -1) +// // In general the 'reminder' follows the 'lhs' sign (round toward 0). +// // Except for the case where 'lhs' is negative and 'reminder' is 0. +// +// var lhs = BigInt("-1")! +// var rhs = BigInt("18446744073709551615")! +// XCTAssertEqual(lhs % rhs, lhs) +// +// // Also fails if 'rhs' is negative +// lhs = BigInt("-7730941133")! +// rhs = BigInt("-18446744073709551615")! +// XCTAssertEqual(lhs % rhs, lhs) +// } +// +// // From my observations all of the `xor` tests are failing. +// func test_node_xor() { +// var lhs = BigInt("0")! +// var rhs = BigInt("1")! +// var expected = BigInt("1")! +// XCTAssertEqual(lhs ^ rhs, expected) +// XCTAssertEqual(0 ^ 1, 1) // Proof +// +// lhs = BigInt("0")! +// rhs = BigInt("-1")! +// expected = BigInt("-1")! +// XCTAssertEqual(lhs ^ rhs, expected) +// XCTAssertEqual(0 ^ -1, -1) // Proof +// } +// +// func test_xor_truthTable() { +// let lhsWord : UInt8 = 0b1100 +// let rhsWord : UInt8 = 0b1010 +// +// let lhs = BigInt(lhsWord) +// let rhs = BigInt(rhsWord) +// +// let expected = BigInt(lhsWord ^ rhsWord) +// XCTAssertEqual(lhs ^ rhs, expected) +// XCTAssertEqual(rhs ^ lhs, expected) +// } +// +// func test_binarySub() { +// // https://www.wolframalpha.com/input?i=-922337203685477587+-+%28-9223372036854775808%29 +// let lhs = BigInt("-922337203685477587")! +// let rhs = BigInt("-9223372036854775808")! +// let expected = BigInt("8301034833169298221")! +// XCTAssertEqual(lhs - rhs, expected) +// // The same on Swift.Int: +// XCTAssertEqual(-922337203685477587 - (-9223372036854775808), 8301034833169298221) +// } +// +// func test_binarySub_2() { +// typealias Word = UInt +// +// let intMax = Int.max +// let intMaxAsWord = Word(intMax.magnitude) +// +// // intMax - (-(Word.max - intMaxAsWord)) = +// // intMax - (-Word.max + intMaxAsWord) = +// // intMax + Word.max - intMaxAsWord = +// // Word.max +// let max = BigInt(intMax) +// let value = -BigInt((Word.max - intMaxAsWord)) +// let expected = BigInt(Word.max) +// XCTAssertEqual(max - value, expected) +// } +// +// func testHashable() { +// let foo = BigInt("1234567890123456789012345678901234567890")! +// let bar = BigInt("1234567890123456789112345678901234567890")! +// let baz: BigInt = 153 +// +// let dict = [ foo: "Hello", bar: "World", baz: "!" ] +// +// let hash = foo.hashValue +// print(hash) +// +// XCTAssertEqual(dict[foo]!, "Hello") +// XCTAssertEqual(dict[bar]!, "World") +// } +// +// func testClampingConversion() { +// XCTAssertEqual(BigInt(clamping: UInt64.max), BigInt(UInt64(18446744073709551615))) +// } +// +// func testUIntConversion() { +// let foo = BigInt(UInt.max) +// XCTAssertNotEqual(foo, BigInt(-1)) +// +// let bar = BigInt(bitPattern: UInt.max) +// XCTAssertEqual(bar, BigInt(-1)) +// } +// +// // MARK: - Testing logical functions +// +// func testLogical() { +// let a = BigInt("7FFF555512340000", radix: 16)! +// let b = BigInt("0000ABCD9876FFFF", radix: 16)! +// +// let aAndb = String(a & b, radix: 16) +// let aOrb = String(a | b, radix: 16) +// let aXorb = String(a ^ b, radix: 16) +// let notb = String(~b, radix: 16) +// +// let shiftLeft1 = String(a << 16, radix:16) +// let shiftLeft2 = String(a << -3, radix:16) +// let shiftRight1 = String(a >> 1000, radix:16) +// let shiftRight2 = String(a >> -7, radix:16) +// +// print("a & b = 0x\(aAndb)"); XCTAssertEqual(aAndb, "14510340000") +// print("a | b = 0x\(aOrb)"); XCTAssertEqual(aOrb, "7fffffdd9a76ffff") +// print("a ^ b = 0x\(aXorb)"); XCTAssertEqual(aXorb, "7ffffe988a42ffff") +// print("~b = 0x\(notb)"); XCTAssertEqual(notb, "-abcd98770000") +// +// print("a << 16 = \(shiftLeft1)"); XCTAssertEqual(shiftLeft1, "7fff5555123400000000") +// print("a << -3 = \(shiftLeft2)"); XCTAssertEqual(shiftLeft2, "fffeaaaa2468000") +// print("a >> 1000 = \(shiftRight1)"); XCTAssertEqual(shiftRight1, "0") +// print("a >> -7 = \(shiftRight2)"); XCTAssertEqual(shiftRight2, "3fffaaaa891a000000") +// } +// +// // MARK: - Converting to/from textual representations +// +// func testCodable() throws { +// let lowerBound = BigInt("-1234567890123456789012345678901234567890")! +// let upperBound = BigInt("+1234567890123456789012345678901234567890")! +// let expectedRange: Range = lowerBound ..< upperBound +// +// let encoder = JSONEncoder() +// let decoder = JSONDecoder() +// let data = try encoder.encode(expectedRange) +// let actualRange = try decoder.decode(Range.self, from: data) +// +// XCTAssertEqual(actualRange, expectedRange) +// } +// +// func testCustomStringConvertible() { +// XCTAssertEqual("\(BigInt(UInt64.min) - 2)", "-2") +// XCTAssertEqual("\(BigInt(UInt64.min) - 1)", "-1") +// XCTAssertEqual("\(BigInt(UInt64.min) + 0)", "0") +// XCTAssertEqual("\(BigInt(UInt64.min) + 1)", "1") +// XCTAssertEqual("\(BigInt(UInt64.min) + 2)", "2") +// +// XCTAssertEqual("\(BigInt(UInt64.max) - 2)", "18446744073709551613") +// XCTAssertEqual("\(BigInt(UInt64.max) - 1)", "18446744073709551614") +// XCTAssertEqual("\(BigInt(UInt64.max) + 0)", "18446744073709551615") +// XCTAssertEqual("\(BigInt(UInt64.max) + 1)", "18446744073709551616") +// XCTAssertEqual("\(BigInt(UInt64.max) + 2)", "18446744073709551617") +// +// XCTAssertEqual("\(BigInt(Int64.min) - 2)", "-9223372036854775810") +// XCTAssertEqual("\(BigInt(Int64.min) - 1)", "-9223372036854775809") +// XCTAssertEqual("\(BigInt(Int64.min) + 0)", "-9223372036854775808") +// XCTAssertEqual("\(BigInt(Int64.min) + 1)", "-9223372036854775807") +// XCTAssertEqual("\(BigInt(Int64.min) + 2)", "-9223372036854775806") +// +// XCTAssertEqual("\(BigInt(Int64.max) - 2)", "9223372036854775805") +// XCTAssertEqual("\(BigInt(Int64.max) - 1)", "9223372036854775806") +// XCTAssertEqual("\(BigInt(Int64.max) + 0)", "9223372036854775807") +// XCTAssertEqual("\(BigInt(Int64.max) + 1)", "9223372036854775808") +// XCTAssertEqual("\(BigInt(Int64.max) + 2)", "9223372036854775809") +// +// XCTAssertEqual("\(-(BigInt(1) << 1023))", Self.descriptionInt1024Min) +// XCTAssertEqual("\(+(BigInt(1) << 1023) - 1)", Self.descriptionInt1024Max) +// } +// +// func testLosslessStringConvertible() { +// XCTAssertNil(BigInt("")) +// XCTAssertNil(BigInt("-")) +// XCTAssertNil(BigInt("+")) +// XCTAssertNil(BigInt("A")) +// XCTAssertNil(BigInt(" 0")) +// XCTAssertNil(BigInt("0 ")) +// +// XCTAssertEqual(BigInt(UInt64.min) - 2, BigInt("-2")) +// XCTAssertEqual(BigInt(UInt64.min) - 1, BigInt("-1")) +// XCTAssertEqual(BigInt(UInt64.min) + 0, BigInt("0")) +// XCTAssertEqual(BigInt(UInt64.min) + 1, BigInt("1")) +// XCTAssertEqual(BigInt(UInt64.min) + 2, BigInt("2")) +// +// XCTAssertEqual(BigInt(UInt64.max) - 2, BigInt("18446744073709551613")) +// XCTAssertEqual(BigInt(UInt64.max) - 1, BigInt("18446744073709551614")) +// XCTAssertEqual(BigInt(UInt64.max) + 0, BigInt("18446744073709551615")) +// XCTAssertEqual(BigInt(UInt64.max) + 1, BigInt("18446744073709551616")) +// XCTAssertEqual(BigInt(UInt64.max) + 2, BigInt("18446744073709551617")) +// +// XCTAssertEqual(BigInt(Int64.min) - 2, BigInt("-9223372036854775810")) +// XCTAssertEqual(BigInt(Int64.min) - 1, BigInt("-9223372036854775809")) +// XCTAssertEqual(BigInt(Int64.min) + 0, BigInt("-9223372036854775808")) +// XCTAssertEqual(BigInt(Int64.min) + 1, BigInt("-9223372036854775807")) +// XCTAssertEqual(BigInt(Int64.min) + 2, BigInt("-9223372036854775806")) +// +// XCTAssertEqual(BigInt(Int64.max) - 2, BigInt("9223372036854775805")) +// XCTAssertEqual(BigInt(Int64.max) - 1, BigInt("9223372036854775806")) +// XCTAssertEqual(BigInt(Int64.max) + 0, BigInt("9223372036854775807")) +// XCTAssertEqual(BigInt(Int64.max) + 1, BigInt("9223372036854775808")) +// XCTAssertEqual(BigInt(Int64.max) + 2, BigInt("9223372036854775809")) +// +// XCTAssertEqual(-(BigInt(1) << 1023), BigInt(Self.descriptionInt1024Min)) +// XCTAssertEqual(+(BigInt(1) << 1023) - 1, BigInt(Self.descriptionInt1024Max)) +// } +// +// func testRadicesAndNumerals() { +// for radix in 2 ... 36 { +// for uppercase in [false, true] { +// for _ in 0 ..< 100 { +// let expectedNumber = BigInt(Int.random(in: .min ... .max)) +// let expectedString = String(expectedNumber, +// radix: radix, +// uppercase: uppercase) +// let actualNumber = BigInt(expectedString, radix: radix) +// XCTAssertEqual(actualNumber, expectedNumber) +// if radix == 10 { +// let actualString = expectedNumber.description +// XCTAssertEqual(actualString, expectedString) +// } +// } +// } +// } +// } +// +// // MARK: - Converting from floating-point binary types +// +// func testBinaryFloatingPoint(_ type: T.Type) where T: BinaryFloatingPoint { +// var expected = BigInt(T.greatestFiniteMagnitude.significandBitPattern) +// expected |= BigInt(1) << T.significandBitCount +// expected <<= T.greatestFiniteMagnitude.exponent +// expected >>= T.significandBitCount +// +// XCTAssertEqual(BigInt(exactly: -T.greatestFiniteMagnitude), -expected) +// XCTAssertEqual(BigInt(exactly: +T.greatestFiniteMagnitude), +expected) +// XCTAssertEqual(BigInt(-T.greatestFiniteMagnitude), -expected) +// XCTAssertEqual(BigInt(+T.greatestFiniteMagnitude), +expected) +// +// XCTAssertNil(BigInt(exactly: -T.infinity)) +// XCTAssertNil(BigInt(exactly: +T.infinity)) +// +// XCTAssertNil(BigInt(exactly: -T.leastNonzeroMagnitude)) +// XCTAssertNil(BigInt(exactly: +T.leastNonzeroMagnitude)) +// XCTAssertEqual(BigInt(-T.leastNonzeroMagnitude), 0) +// XCTAssertEqual(BigInt(+T.leastNonzeroMagnitude), 0) +// +// XCTAssertNil(BigInt(exactly: -T.leastNormalMagnitude)) +// XCTAssertNil(BigInt(exactly: +T.leastNormalMagnitude)) +// XCTAssertEqual(BigInt(-T.leastNormalMagnitude), 0) +// XCTAssertEqual(BigInt(+T.leastNormalMagnitude), 0) +// +// XCTAssertNil(BigInt(exactly: T.nan)) +// XCTAssertNil(BigInt(exactly: T.signalingNaN)) +// +// XCTAssertNil(BigInt(exactly: -T.pi)) +// XCTAssertNil(BigInt(exactly: +T.pi)) +// XCTAssertEqual(BigInt(-T.pi), -3) +// XCTAssertEqual(BigInt(+T.pi), +3) +// +// XCTAssertNil(BigInt(exactly: -T.ulpOfOne)) +// XCTAssertNil(BigInt(exactly: +T.ulpOfOne)) +// XCTAssertEqual(BigInt(-T.ulpOfOne), 0) +// XCTAssertEqual(BigInt(+T.ulpOfOne), 0) +// +// XCTAssertEqual(BigInt(exactly: -T.zero), 0) +// XCTAssertEqual(BigInt(exactly: +T.zero), 0) +// XCTAssertEqual(BigInt(-T.zero), 0) +// XCTAssertEqual(BigInt(+T.zero), 0) +// } +// +// func testBinaryFloatingPoint() { +// testBinaryFloatingPoint(Float32.self) +// testBinaryFloatingPoint(Float64.self) +// #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) +// testBinaryFloatingPoint(Float80.self) +// #endif +// +// for _ in 0 ..< 100 { +// let small = Float32.random(in: -10 ... +10) +// XCTAssertEqual(BigInt(small), BigInt(Int64(small))) +// +// let large = Float32.random(in: -0x1p23 ... +0x1p23) +// XCTAssertEqual(BigInt(large), BigInt(Int64(large))) +// } +// +// for _ in 0 ..< 100 { +// let small = Float64.random(in: -10 ... +10) +// XCTAssertEqual(BigInt(small), BigInt(Int64(small))) +// +// let large = Float64.random(in: -0x1p52 ... +0x1p52) +// XCTAssertEqual(BigInt(large), BigInt(Int64(large))) +// } +// +// #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) +// for _ in 0 ..< 100 { +// let small = Float80.random(in: -10 ... +10) +// XCTAssertEqual(BigInt(small), BigInt(Int64(small))) +// +// let large = Float80.random(in: -0x1p63 ..< +0x1p63) +// XCTAssertEqual(BigInt(large), BigInt(Int64(large))) +// } +// #endif +// } } From 696ea95784b573ec52e2337252dbc20830886458 Mon Sep 17 00:00:00 2001 From: Michael Griebling Date: Sat, 22 Apr 2023 14:57:32 -0400 Subject: [PATCH 6/7] New implementation for LosslessStringConvertible init?(); speeds conversions by 35X to 310X times over original code. New implementation for CustomStringConvertible toString(); speeds conversions by 20X to 90X times over original code. --- Sources/BigIntModule/BigInt.swift | 222 +++-- Tests/BigIntTests/BigIntTests.swift | 1194 +++++++++++++-------------- 2 files changed, 752 insertions(+), 664 deletions(-) diff --git a/Sources/BigIntModule/BigInt.swift b/Sources/BigIntModule/BigInt.swift index 5fa84e0e..02113870 100644 --- a/Sources/BigIntModule/BigInt.swift +++ b/Sources/BigIntModule/BigInt.swift @@ -92,70 +92,168 @@ extension BigInt: LosslessStringConvertible { public init?(_ description: String) { self.init(description, radix: 10) } - - + //////////////////////////////////////////////////////////////////////////// /// /// NEW CODE STARTS + /// Code liberated and adapted from the Violet BigInt implementation + /// Speeds the time to initialize a BigInt by about a factor of 35 for + /// the test case of the string for 512! using radix 10. A radix 16 test + /// for the same number was 310X faster. + public init?(_ description: String, radix: Int = 10) { + guard 2 <= radix && radix <= Self.maxRadix else { return nil } + guard !description.isEmpty else { return nil } + + let utf8 = description.utf8 + + // Most of the time we will go 'fast' (in Swift dominant apps). + // Fast (on 'UnsafeBufferPointer') is 2x faster than on 'UTF8View'. + let fast = utf8.withContiguousStorageIfAvailable { buffer in + return Self.parse(buffer, radix: radix) + } - private static func _pow(_ lhs: UInt, _ rhs: UInt) -> UInt { - // guard rhs>=0 else { return 0 } /* lhs ** (-rhs) = 0 */ - var lexp = rhs - var x = lhs - var y = UInt(1) - while lexp > 0 { - if !lexp.isMultiple(of: 2) { y*=x } - lexp >>= 1 - if lexp > 0 { x=x*x } + // Try again -- if necessary -- with a standard buffer + if let r = fast ?? Self.parse(utf8, radix: radix) { + self = r + return } - return y + return nil } - private func _maxDigits(forRadix radix:Int) -> Int { - switch radix { - case 2: return 62 - case 8: return 20 - case 10: return 18 - case 16: return 14 - default: return 11 // safe but not optimal for other radices + private static func mulAdd(result: inout Words, multiplier: UInt, addIn: UInt) { + var carry = addIn + var overflow = false + let highWord = result.count-1 + for i in 0...highWord { + let product = result[i].multipliedFullWidth(by: multiplier) + (result[i], overflow) = carry.addingReportingOverflow(product.low) + (carry, overflow) = product.high.addingReportingOverflow(overflow ? 1 : 0) } + assert(!overflow, "Word size overflow during \(#function)") } - /// Speeds the time to initialize a BigInt by about a factor of 16 for - /// the test case of the string for 512! using radix 10. A radix 36 test - /// was 10X faster. BTW, the factorial test code was sped up by almost 2 - /// times (the string to number code accounted for a large part of the - /// total time). - public init?(_ description: T, radix: Int = 10) where T: StringProtocol { - precondition(2 ... 36 ~= radix, "Radix not in range 2 ... 36") - - self = 0 - let isNegative = description.hasPrefix("-") - let hasPrefix = isNegative || description.hasPrefix("+") - var str = description.dropFirst(hasPrefix ? 1 : 0) - guard !str.isEmpty else { return nil } - - /// Main speed-up is due to converting chunks of string via - /// the Int64() initializer instead of a character at a time. - /// We also get free radix digit checks. - let maxDigits = _maxDigits(forRadix: radix) - while !str.isEmpty { - let block = str.prefix(maxDigits) - let size = block.count - str.removeFirst(size) - if let word = UInt(block, radix: radix) { - self *= BigInt(Self._pow(UInt(radix), UInt(size))) - self += BigInt(word) - } else { - return nil + // MARK: - Parse + + private static func parse(_ chars: C, radix: Int) -> BigInt? where C.Element == UInt8 { + var (isNegative, index) = Self.checkSign(chars) + let endIndex = chars.endIndex + if index == endIndex { return nil } // only the sign was found + + // note and discard leading zeros + let hasZeroPrefix = Self.stripLeadingZeros(chars, index: &index) + if index == endIndex { return hasZeroPrefix ? BigInt() : nil } // only zeros - return 0 + let (charCountPerWord, power) = Self.maxRepresentablePower(of: radix) + let remainingCount = chars.distance(from: index, to: endIndex) + + // allocate one more word than estimated + let capacity = (remainingCount / charCountPerWord) + 1 + let digitParser = DigitParser(radix: radix) + var currentWord = UInt(0) + let firstWordCount = remainingCount % charCountPerWord + var remainingCharsInCurrentWord = firstWordCount == 0 ? charCountPerWord : firstWordCount + let powerOfTwo = radix & (radix - 1) == 0 + + // working buffer + var buffer = Words(repeating: 0, count: capacity) + if powerOfTwo { + // Radix powers of 2 convert about 10X faster with this algorithm + var reverseIndex = chars.endIndex + chars.formIndex(before: &reverseIndex) + var wordIndex = 0 + var wordShift = 0 + let bitsPerChar = radix.trailingZeroBitCount + buffer[wordIndex] = 0 + while reverseIndex >= index { + // check for illegal digits and convert to UInt + let char = chars[reverseIndex] + guard let digit = digitParser.parse(char) else { return nil } + + buffer[wordIndex] |= digit &<< wordShift + wordShift &+= bitsPerChar + if wordShift >= UInt.bitWidth { + wordIndex &+= 1 + wordShift = wordShift % UInt.bitWidth + assert(wordIndex < capacity) + buffer[wordIndex] = digit &>> (bitsPerChar &- wordShift) + } + chars.formIndex(before: &reverseIndex) + } + } else { + while index != endIndex { + // check for illegal digits and convert to UInt + let char = chars[index] + guard let digit = digitParser.parse(char) else { return nil } + + // Overflows are guaranteed to not occur due to `charCountPerWord` + currentWord = currentWord &* UInt(radix) &+ digit + remainingCharsInCurrentWord &-= 1 + if remainingCharsInCurrentWord == 0 { + // Append word even if it is zero -- zeros can occur in the middle of a number + mulAdd(result: &buffer, multiplier: power, addIn: currentWord) + currentWord = 0 + remainingCharsInCurrentWord = charCountPerWord + } + chars.formIndex(after: &index) } } - if isNegative { - self.negate() + BigInt._dropExcessWords(words: &buffer) + var result = BigInt(_uncheckedWords: buffer) + if isNegative { result.negate() } + return result + } + + private static func checkSign(_ chars: C) -> (isNegative:Bool, index: C.Index) where C.Element == UInt8 { + var index = chars.startIndex + let first = chars[index] + if first == _plus { + chars.formIndex(after: &index) + return (isNegative: false, index: index) + } + if first == _minus { + chars.formIndex(after: &index) + return (isNegative: true, index: index) + } + return (isNegative: false, index: index) + } + + private static func stripLeadingZeros(_ chars: C, index: inout C.Index) -> Bool where C.Element == UInt8 { + let hasZeroPrefix = chars[index] == _0 + let endIndex = chars.endIndex + + // skip leading zeros + while index != endIndex && chars[index] == _0 { + chars.formIndex(after: &index) } + return hasZeroPrefix } + + private struct DigitParser { + private let numericalUpperBound: UInt8 + private let uppercaseUpperBound: UInt8 + private let lowercaseUpperBound: UInt8 + + fileprivate init(radix: Int) { + if radix <= 10 { + self.numericalUpperBound = _0 &+ UInt8(truncatingIfNeeded: radix) + self.uppercaseUpperBound = _A + self.lowercaseUpperBound = _a + } else { + self.numericalUpperBound = _0 &+ 10 + self.uppercaseUpperBound = _A &+ UInt8(truncatingIfNeeded: radix &- 10) + self.lowercaseUpperBound = _a &+ UInt8(truncatingIfNeeded: radix &- 10) + } + } + fileprivate func parse(_ char: UInt8) -> UInt? { + return + _0 <= char && char < numericalUpperBound ? UInt(truncatingIfNeeded: char &- _0) : + _A <= char && char < uppercaseUpperBound ? UInt(truncatingIfNeeded: char &- _A &+ 10) : + _a <= char && char < lowercaseUpperBound ? UInt(truncatingIfNeeded: char &- _a &+ 10) : + nil + } + } + /// NEW CODE ENDS /// //////////////////////////////////////////////////////////////////////////// @@ -178,17 +276,18 @@ extension BigInt : CustomStringConvertible { /// default (albeit much slower) String() implementation. Maybe the String /// in the runtime could be updated to be faster. public var description: String { - //return String(self, radix: 10) - return toString(radix: 10, uppercase: false) + return self.toString(radix: 10, uppercase: false) } private static var maxRadix: Int { 36 } // ASCII constants - private static let _0: UInt8 = Character("0").asciiValue! - private static let _A: UInt8 = Character("A").asciiValue! - private static let _a: UInt8 = Character("a").asciiValue! - private static let _minus: UInt8 = Character("-").asciiValue! + + private static let _0 = UInt8(ascii: "0") + private static let _A = UInt8(ascii: "A") + private static let _a = UInt8(ascii: "a") + private static let _plus = UInt8(ascii: "+") + private static let _minus = UInt8(ascii: "-") internal func div(_ dividend: inout Words, by divisor: UInt) -> UInt { var carry = UInt(0) @@ -217,7 +316,6 @@ extension BigInt : CustomStringConvertible { var words = self.magnitude.words; words.reserveCapacity(self.words.count) if #available(macOS 11.0, *) { return StringLiteralType(unsafeUninitializedCapacity: stringMaxSize) { buffer in - if isPowerOfTwoRadix { // Handle powers-of-two radixes let bitsPerChar = radix.trailingZeroBitCount @@ -302,23 +400,15 @@ extension BigInt : CustomStringConvertible { internal static func maxRepresentablePower(of radix: Int) -> (n: Int, power: UInt) { var n = 1 var power = UInt(radix) - while true { let (newPower, overflow) = power.multipliedReportingOverflow(by: UInt(radix)) - - if overflow { - return (n, power) - } - + if overflow { return (n, power) } n += 1 power = newPower } } private func ascii(_ n: UInt, uppercase: Bool) -> UInt8 { - // Performance sensitive area! Order of operations matters! - // Compiler will emit code without overflow checks, - // so we do not need to use unchecked '&' (like '&+' and '&-'). assert(n < Self.maxRadix) // Always less, never equal! let n = UInt8(truncatingIfNeeded: n) return n < 10 ? n + Self._0 : n - 10 + (uppercase ? Self._A : Self._a) @@ -449,7 +539,7 @@ extension BigInt: Numeric { let count = lhsWords.count + rhsWords.count + 1 var newWords = Words(repeating: 0, count: count) - + for i in 0 ..< rhsWords.count { var carry: UInt = 0 var digit: UInt = 0 @@ -482,7 +572,7 @@ extension BigInt: Numeric { newWords[lastJ + 1] = digit } } - + for i in stride(from: count - 1, through: 1, by: -1) { if newWords[i] == 0, newWords[i - 1] <= Int.max { newWords.removeLast() diff --git a/Tests/BigIntTests/BigIntTests.swift b/Tests/BigIntTests/BigIntTests.swift index 5957d220..1014e584 100644 --- a/Tests/BigIntTests/BigIntTests.swift +++ b/Tests/BigIntTests/BigIntTests.swift @@ -115,614 +115,612 @@ final class BigIntTests: XCTestCase { // MARK: - Basic arithmetic -// func testDivision() { -// // Signed division test -// let numa = BigInt("-18446744073709551616")! -// let dena = BigInt(123) -// let expecteda = BigInt(Int64(-149973529054549200)) -// XCTAssertEqual(numa / dena, expecteda) -// -// let numb = BigInt("18446744073709551616")! -// let denb = BigInt(-123) -// XCTAssertEqual(numb / denb, expecteda) -// let expectedb = BigInt(Int64(149973529054549200)) -// XCTAssertEqual(numa / denb, expectedb) -// -// // Previous test cases -// let num1 = BigInt("18446744073709551616")! -// let den1 = BigInt(123) -// let expected1 = BigInt(UInt64(149973529054549200)) -// XCTAssertEqual(num1 / den1, expected1) -// -// let num2 = BigInt.pow(BigInt(10), 100) -// let den2: BigInt = 3 -// let expected2: BigInt = BigInt(String(repeating: "3", count: 100))! -// let actual2 = num2 / den2 -// XCTAssertEqual(actual2, expected2) -// -// let num3 = BigInt.pow(BigInt(10), 97) -// let den3: BigInt = BigInt("33333333333333333333")! -// let expected3: BigInt = BigInt("300000000000000000003000000000000000000030000000000000000000300000000000000000")! -// let actual3 = num3 / den3 -// XCTAssertEqual(actual3, expected3) -// -// let foo = BigInt("12345678901234567890123456789012345678901234567890123456789012345678901234567890")! -// let bar = BigInt("351235231535161613134135135135")! -// let baz = foo / bar -// XCTAssertEqual(baz, BigInt("35149318157164029153358504918339691272847595997760")!) -// -// XCTAssertNotNil(BigInt(exactly: 2.4e39)) -// XCTAssertNotNil(BigInt(exactly: 1e38)) -// XCTAssertEqual(BigInt(2.4e39) / BigInt(1e38), BigInt(24)) -// -// for _ in 0 ..< 100 { -// let expected = BigInt(Float64.random(in: 0x1p64 ... 0x1p255)) -// let divisor = BigInt(Float64.random(in: 0x1p64 ... 0x1p128)) -// let (quotient, remainder) = expected.quotientAndRemainder(dividingBy: divisor) -// let actual = divisor * quotient + remainder -// XCTAssertEqual(quotient, expected / divisor) -// XCTAssertEqual(remainder, expected % divisor) -// XCTAssertEqual( -// actual, expected, -// """ -// ## FAILURE ## -// ~~~~~~~~~~~~~ -// actual: \(actual) -// != expected: \(expected) -// ~~~~~~~~~~~~~ -// divisor: \(divisor) -// * quotient: \(quotient) -// + remainder: \(remainder) -// ~~~~~~~~~~~~~ -// """) -// } -// } + func testDivision() { + // Signed division test + let numa = BigInt("-18446744073709551616")! + let dena = BigInt(123) + let expecteda = BigInt(Int64(-149973529054549200)) + XCTAssertEqual(numa / dena, expecteda) + + let numb = BigInt("18446744073709551616")! + let denb = BigInt(-123) + XCTAssertEqual(numb / denb, expecteda) + let expectedb = BigInt(Int64(149973529054549200)) + XCTAssertEqual(numa / denb, expectedb) + + // Previous test cases + let num1 = BigInt("18446744073709551616")! + let den1 = BigInt(123) + let expected1 = BigInt(UInt64(149973529054549200)) + XCTAssertEqual(num1 / den1, expected1) + + let num2 = BigInt.pow(BigInt(10), 100) + let den2: BigInt = 3 + let expected2: BigInt = BigInt(String(repeating: "3", count: 100))! + let actual2 = num2 / den2 + XCTAssertEqual(actual2, expected2) + + let num3 = BigInt.pow(BigInt(10), 97) + let den3: BigInt = BigInt("33333333333333333333")! + let expected3: BigInt = BigInt("300000000000000000003000000000000000000030000000000000000000300000000000000000")! + let actual3 = num3 / den3 + XCTAssertEqual(actual3, expected3) + + let foo = BigInt("12345678901234567890123456789012345678901234567890123456789012345678901234567890")! + let bar = BigInt("351235231535161613134135135135")! + let baz = foo / bar + XCTAssertEqual(baz, BigInt("35149318157164029153358504918339691272847595997760")!) + + XCTAssertNotNil(BigInt(exactly: 2.4e39)) + XCTAssertNotNil(BigInt(exactly: 1e38)) + XCTAssertEqual(BigInt(2.4e39) / BigInt(1e38), BigInt(24)) + + for _ in 0 ..< 100 { + let expected = BigInt(Float64.random(in: 0x1p64 ... 0x1p255)) + let divisor = BigInt(Float64.random(in: 0x1p64 ... 0x1p128)) + let (quotient, remainder) = expected.quotientAndRemainder(dividingBy: divisor) + let actual = divisor * quotient + remainder + XCTAssertEqual(quotient, expected / divisor) + XCTAssertEqual(remainder, expected % divisor) + XCTAssertEqual( + actual, expected, + """ + ## FAILURE ## + ~~~~~~~~~~~~~ + actual: \(actual) + != expected: \(expected) + ~~~~~~~~~~~~~ + divisor: \(divisor) + * quotient: \(quotient) + + remainder: \(remainder) + ~~~~~~~~~~~~~ + """) + } + } -// func testStringToBigIntImprovements() { -// var expectedNumber: BigInt! -// -// measure { -// expectedNumber = BigInt(Self.descriptionFactorial512_radix36, radix: 36) -// } -// -// XCTAssertEqual(expectedNumber, BigInt(Self.descriptionFactorial512_radix10, radix: 10)) -// } + func testStringToBigIntImprovements() { + var expectedNumber: BigInt! + + measure { + expectedNumber = BigInt(Self.descriptionFactorial512_radix10, radix: 10) + } + + XCTAssertEqual(expectedNumber, BigInt(Self.descriptionFactorial512_radix10, radix: 10)) + } func testBigIntToStringImprovements() { let number = BigInt.fac(512) var expectedString = "" measure { - for _ in 1 ... 1000 { - expectedString = number.toString(radix: 10, uppercase: false) - } + expectedString = number.toString(radix: 10, uppercase: false) } XCTAssertEqual(expectedString, Self.descriptionFactorial512_radix10) } -// func testFactorial() { -// var expectedNumber: BigInt? -// var actualNumber: BigInt! -// var actualString: String! -// -// measure { -// expectedNumber = BigInt(Self.descriptionFactorial512_radix36, radix: 36) -// actualNumber = BigInt.fac(512) -// actualString = String(actualNumber, radix: 36, uppercase: true) -// } -// -// XCTAssertEqual(actualNumber, expectedNumber) -// XCTAssertEqual(actualString, Self.descriptionFactorial512_radix36) -// -// XCTAssertEqual(BigInt.fac(0), 1) -// XCTAssertEqual(BigInt.fac(1), 1) -// XCTAssertEqual(BigInt.fac(2), 2) -// XCTAssertEqual(BigInt.fac(3), 6) -// XCTAssertEqual(BigInt.fac(4), 24) -// XCTAssertEqual(BigInt.fac(5), 120) -// XCTAssertEqual(BigInt.fac(6), 720) -// XCTAssertEqual(BigInt.fac(7), 5040) -// XCTAssertEqual(BigInt.fac(8), 40320) -// XCTAssertEqual(BigInt.fac(9), 362880) -// } -// -// func testMath() { -// let foo = BigInt.pow(10, 20) -// let bar = BigInt("1234567890123456789012345678901234567890")! -// -// let baz = foo + bar -// -// XCTAssertEqual(baz, BigInt("1234567890123456789112345678901234567890")!) -// -// let fooz = foo >> BigInt(10) -// XCTAssertEqual(fooz, foo / 1024) -// -// let barz = BigInt(1) << 64 -// XCTAssertEqual(barz, BigInt(UInt64.max) + 1) -// } -// -// func testNegation() { -// let foo = BigInt("1234567890123456789012345678901234567890")! -// let bar = BigInt(0) - foo -// -// XCTAssertEqual(-foo, bar) -// -// var baz = foo -// baz.negate() -// XCTAssertEqual(baz, bar) -// } -// -// func testSignum() { -// XCTAssertEqual(BigInt(-0x1p1023).signum(), -1) -// XCTAssertEqual(BigInt(Int64.min).signum(), -1) -// XCTAssertEqual(BigInt(Int32.min).signum(), -1) -// XCTAssertEqual(BigInt(Int16.min).signum(), -1) -// XCTAssertEqual(BigInt(Int8.min).signum(), -1) -// XCTAssertEqual(BigInt(-1).signum(), -1) -// XCTAssertEqual(BigInt(0).signum(), 0) -// XCTAssertEqual(BigInt(+1).signum(), +1) -// XCTAssertEqual(BigInt(Int8.max).signum(), +1) -// XCTAssertEqual(BigInt(Int16.max).signum(), +1) -// XCTAssertEqual(BigInt(Int32.max).signum(), +1) -// XCTAssertEqual(BigInt(Int64.max).signum(), +1) -// XCTAssertEqual(BigInt(+0x1p1023).signum(), +1) -// } -// -// func testTrailingZeroCount() { -// let foo = BigInt(1) << 300 -// XCTAssertEqual(foo.trailingZeroBitCount, 300) -// -// let bar = (BigInt(1) << 300) + 0b101000 -// XCTAssertEqual(bar.trailingZeroBitCount, 3) -// } -// -// // MARK: - Comparing and hashing -// -// func testComparable() { -// let foo = BigInt("1234567890123456789012345678901234567890")! -// let bar = foo * foo -// -// XCTAssertLessThan(foo, bar) -// XCTAssertFalse(foo < foo) -// XCTAssertFalse(bar < bar) -// XCTAssertFalse(foo > foo) -// XCTAssertFalse(bar > bar) -// XCTAssertGreaterThan(bar, foo) -// -// let baz = bar * -1 -// -// XCTAssertLessThan(baz, foo) -// XCTAssertNotEqual(bar, baz) -// XCTAssertFalse(baz < baz) -// } -// -// func testComparison() { -// let foo = BigInt(-10) -// let bar = BigInt(-20) -// -// XCTAssert(foo > bar) -// XCTAssert(bar < foo) -// XCTAssert(foo == BigInt(-10)) -// -// let baz = BigInt.pow(foo, -bar) -// XCTAssertEqual(baz, BigInt("100000000000000000000")!) -// } -// -// func testExample() { -// let bar = BigInt(exactly: -100) -// XCTAssertNotNil(bar) -// if let bar = bar { -// XCTAssertLessThan(bar, 0) -// XCTAssertGreaterThan(-bar, 0) -// XCTAssertEqual(-bar, BigInt(100)) -// } -// XCTAssertEqual(-BigInt("-1234567890123456789012345678901234567890")!, -// +BigInt("+1234567890123456789012345678901234567890")!) -// } -// -// func testsWhichCrashed() { -// // -9223372036854775808 = Int64.min, obviously '-Int64.min' overflows -// let int: Int64 = -9223372036854775808 -// var big = BigInt(int) -// XCTAssertEqual(-big, big * -1) -// -// // 9223372036854775808 = UInt64(1) << Float80.significandBitCount -// var int2 : UInt64 = 9223372036854775808 -// big = BigInt(int2) -// let _ = Float80(exactly: int2) // works -// let _ = Float80(exactly: big) // crash (not anymore) -// -// // 18446744073709551615 = UInt64.max - was crashing -// int2 = 18446744073709551615 -// big = BigInt(int2) -// let _ = UInt64(big) -// -// // was generating an overflow -// let lhsInt = -9223372036854775808 -// let rhsInt = -1 -// let lhs = BigInt(lhsInt) -// let rhs = BigInt(rhsInt) -// _ = lhs / rhs // Overflow -// } -// -// func test_initFromInt_exactly() { -// let int: UInt64 = 18446744073709551614 -// let big = BigInt(exactly: int)! -// let revert = UInt64(exactly: big) -// XCTAssertEqual(int, revert) -// } -// -// func test_initFromInt_clamping() { -// let int: UInt64 = 18446744073709551614 -// let big = BigInt(clamping: int) -// let revert = UInt64(clamping: big) -// XCTAssertEqual(int, revert) -// } -// -// func test_initFromInt_truncatingIfNeeded() { -// let int: UInt64 = 18446744073709551615 -// let big = BigInt(truncatingIfNeeded: int) -// let intString = String(int, radix: 10, uppercase: false) -// let bigString = String(big, radix: 10, uppercase: false) -// XCTAssertEqual(bigString, intString) -// } -// -// func test_unaryMinus() { -// // -9223372036854775808 = Int.min -// // 'Int.min' negation overflows - ok now -// let int = -9223372036854775808 -// let expected = BigInt(int.magnitude) -// -// let big = -BigInt(int) -// XCTAssertEqual(big, expected) -// -// var negated = BigInt(int) -// negated.negate() -// XCTAssertEqual(negated, expected, "\(negated) == \(expected)") -// } -// -// func test_div_sign() { -// // positive / negative = negative -// var lhs = BigInt("18446744073709551615")! -// var rhs = BigInt("-1")! -// var expected = BigInt("-18446744073709551615")! -// XCTAssertEqual(lhs / rhs, expected) -// -// // negative / positive = negative -// lhs = BigInt("-340282366920938463481821351505477763074")! -// rhs = BigInt("18446744073709551629")! -// expected = BigInt("-18446744073709551604")! -// XCTAssertEqual(lhs / rhs, expected) -// } -// -// func test_magnitude() { -// let big = BigInt(-9223372036854775808) -// let expect = UInt64(9223372036854775808) -// XCTAssertEqual(big.magnitude, BigInt(expect)) -// } -// -// // ApplyA_ApplyB_Equals_ApplyAB.swift -// func test_a_b_ab() { -// // (a/-3) / -5 = a/15 -// let lhs = BigInt("-18446744073709551615")! -// let a = BigInt(-3) -// let b = BigInt(-5) -// let ab = BigInt(15) -// -// let r0 = (lhs / a) / b -// let r1 = lhs / ab -// XCTAssertEqual(r0, r1) -// } -// -// // ApplyA_UndoA.swift -// func test_a_undoA() { -// let n = BigInt(-1) -// let x = BigInt(-9223372036854775808) -// XCTAssertEqual(n, (n + x) - x) -// XCTAssertEqual(n, (n * x) / x) -// } -// -// func test_node_mod_incorrectSign() { -// // SMALL % BIG = SMALL -// // We need to satisfy: BIG * 0 + result = SMALL -> result = SMALL -// // The same, but on the standard Swift.Int to prove the point: -// XCTAssertEqual(-1 % 123, -1) -// XCTAssertEqual(-1 % -123, -1) -// // In general the 'reminder' follows the 'lhs' sign (round toward 0). -// // Except for the case where 'lhs' is negative and 'reminder' is 0. -// -// var lhs = BigInt("-1")! -// var rhs = BigInt("18446744073709551615")! -// XCTAssertEqual(lhs % rhs, lhs) -// -// // Also fails if 'rhs' is negative -// lhs = BigInt("-7730941133")! -// rhs = BigInt("-18446744073709551615")! -// XCTAssertEqual(lhs % rhs, lhs) -// } -// -// // From my observations all of the `xor` tests are failing. -// func test_node_xor() { -// var lhs = BigInt("0")! -// var rhs = BigInt("1")! -// var expected = BigInt("1")! -// XCTAssertEqual(lhs ^ rhs, expected) -// XCTAssertEqual(0 ^ 1, 1) // Proof -// -// lhs = BigInt("0")! -// rhs = BigInt("-1")! -// expected = BigInt("-1")! -// XCTAssertEqual(lhs ^ rhs, expected) -// XCTAssertEqual(0 ^ -1, -1) // Proof -// } -// -// func test_xor_truthTable() { -// let lhsWord : UInt8 = 0b1100 -// let rhsWord : UInt8 = 0b1010 -// -// let lhs = BigInt(lhsWord) -// let rhs = BigInt(rhsWord) -// -// let expected = BigInt(lhsWord ^ rhsWord) -// XCTAssertEqual(lhs ^ rhs, expected) -// XCTAssertEqual(rhs ^ lhs, expected) -// } -// -// func test_binarySub() { -// // https://www.wolframalpha.com/input?i=-922337203685477587+-+%28-9223372036854775808%29 -// let lhs = BigInt("-922337203685477587")! -// let rhs = BigInt("-9223372036854775808")! -// let expected = BigInt("8301034833169298221")! -// XCTAssertEqual(lhs - rhs, expected) -// // The same on Swift.Int: -// XCTAssertEqual(-922337203685477587 - (-9223372036854775808), 8301034833169298221) -// } -// -// func test_binarySub_2() { -// typealias Word = UInt -// -// let intMax = Int.max -// let intMaxAsWord = Word(intMax.magnitude) -// -// // intMax - (-(Word.max - intMaxAsWord)) = -// // intMax - (-Word.max + intMaxAsWord) = -// // intMax + Word.max - intMaxAsWord = -// // Word.max -// let max = BigInt(intMax) -// let value = -BigInt((Word.max - intMaxAsWord)) -// let expected = BigInt(Word.max) -// XCTAssertEqual(max - value, expected) -// } -// -// func testHashable() { -// let foo = BigInt("1234567890123456789012345678901234567890")! -// let bar = BigInt("1234567890123456789112345678901234567890")! -// let baz: BigInt = 153 -// -// let dict = [ foo: "Hello", bar: "World", baz: "!" ] -// -// let hash = foo.hashValue -// print(hash) -// -// XCTAssertEqual(dict[foo]!, "Hello") -// XCTAssertEqual(dict[bar]!, "World") -// } -// -// func testClampingConversion() { -// XCTAssertEqual(BigInt(clamping: UInt64.max), BigInt(UInt64(18446744073709551615))) -// } -// -// func testUIntConversion() { -// let foo = BigInt(UInt.max) -// XCTAssertNotEqual(foo, BigInt(-1)) -// -// let bar = BigInt(bitPattern: UInt.max) -// XCTAssertEqual(bar, BigInt(-1)) -// } -// -// // MARK: - Testing logical functions -// -// func testLogical() { -// let a = BigInt("7FFF555512340000", radix: 16)! -// let b = BigInt("0000ABCD9876FFFF", radix: 16)! -// -// let aAndb = String(a & b, radix: 16) -// let aOrb = String(a | b, radix: 16) -// let aXorb = String(a ^ b, radix: 16) -// let notb = String(~b, radix: 16) -// -// let shiftLeft1 = String(a << 16, radix:16) -// let shiftLeft2 = String(a << -3, radix:16) -// let shiftRight1 = String(a >> 1000, radix:16) -// let shiftRight2 = String(a >> -7, radix:16) -// -// print("a & b = 0x\(aAndb)"); XCTAssertEqual(aAndb, "14510340000") -// print("a | b = 0x\(aOrb)"); XCTAssertEqual(aOrb, "7fffffdd9a76ffff") -// print("a ^ b = 0x\(aXorb)"); XCTAssertEqual(aXorb, "7ffffe988a42ffff") -// print("~b = 0x\(notb)"); XCTAssertEqual(notb, "-abcd98770000") -// -// print("a << 16 = \(shiftLeft1)"); XCTAssertEqual(shiftLeft1, "7fff5555123400000000") -// print("a << -3 = \(shiftLeft2)"); XCTAssertEqual(shiftLeft2, "fffeaaaa2468000") -// print("a >> 1000 = \(shiftRight1)"); XCTAssertEqual(shiftRight1, "0") -// print("a >> -7 = \(shiftRight2)"); XCTAssertEqual(shiftRight2, "3fffaaaa891a000000") -// } -// -// // MARK: - Converting to/from textual representations -// -// func testCodable() throws { -// let lowerBound = BigInt("-1234567890123456789012345678901234567890")! -// let upperBound = BigInt("+1234567890123456789012345678901234567890")! -// let expectedRange: Range = lowerBound ..< upperBound -// -// let encoder = JSONEncoder() -// let decoder = JSONDecoder() -// let data = try encoder.encode(expectedRange) -// let actualRange = try decoder.decode(Range.self, from: data) -// -// XCTAssertEqual(actualRange, expectedRange) -// } -// -// func testCustomStringConvertible() { -// XCTAssertEqual("\(BigInt(UInt64.min) - 2)", "-2") -// XCTAssertEqual("\(BigInt(UInt64.min) - 1)", "-1") -// XCTAssertEqual("\(BigInt(UInt64.min) + 0)", "0") -// XCTAssertEqual("\(BigInt(UInt64.min) + 1)", "1") -// XCTAssertEqual("\(BigInt(UInt64.min) + 2)", "2") -// -// XCTAssertEqual("\(BigInt(UInt64.max) - 2)", "18446744073709551613") -// XCTAssertEqual("\(BigInt(UInt64.max) - 1)", "18446744073709551614") -// XCTAssertEqual("\(BigInt(UInt64.max) + 0)", "18446744073709551615") -// XCTAssertEqual("\(BigInt(UInt64.max) + 1)", "18446744073709551616") -// XCTAssertEqual("\(BigInt(UInt64.max) + 2)", "18446744073709551617") -// -// XCTAssertEqual("\(BigInt(Int64.min) - 2)", "-9223372036854775810") -// XCTAssertEqual("\(BigInt(Int64.min) - 1)", "-9223372036854775809") -// XCTAssertEqual("\(BigInt(Int64.min) + 0)", "-9223372036854775808") -// XCTAssertEqual("\(BigInt(Int64.min) + 1)", "-9223372036854775807") -// XCTAssertEqual("\(BigInt(Int64.min) + 2)", "-9223372036854775806") -// -// XCTAssertEqual("\(BigInt(Int64.max) - 2)", "9223372036854775805") -// XCTAssertEqual("\(BigInt(Int64.max) - 1)", "9223372036854775806") -// XCTAssertEqual("\(BigInt(Int64.max) + 0)", "9223372036854775807") -// XCTAssertEqual("\(BigInt(Int64.max) + 1)", "9223372036854775808") -// XCTAssertEqual("\(BigInt(Int64.max) + 2)", "9223372036854775809") -// -// XCTAssertEqual("\(-(BigInt(1) << 1023))", Self.descriptionInt1024Min) -// XCTAssertEqual("\(+(BigInt(1) << 1023) - 1)", Self.descriptionInt1024Max) -// } -// -// func testLosslessStringConvertible() { -// XCTAssertNil(BigInt("")) -// XCTAssertNil(BigInt("-")) -// XCTAssertNil(BigInt("+")) -// XCTAssertNil(BigInt("A")) -// XCTAssertNil(BigInt(" 0")) -// XCTAssertNil(BigInt("0 ")) -// -// XCTAssertEqual(BigInt(UInt64.min) - 2, BigInt("-2")) -// XCTAssertEqual(BigInt(UInt64.min) - 1, BigInt("-1")) -// XCTAssertEqual(BigInt(UInt64.min) + 0, BigInt("0")) -// XCTAssertEqual(BigInt(UInt64.min) + 1, BigInt("1")) -// XCTAssertEqual(BigInt(UInt64.min) + 2, BigInt("2")) -// -// XCTAssertEqual(BigInt(UInt64.max) - 2, BigInt("18446744073709551613")) -// XCTAssertEqual(BigInt(UInt64.max) - 1, BigInt("18446744073709551614")) -// XCTAssertEqual(BigInt(UInt64.max) + 0, BigInt("18446744073709551615")) -// XCTAssertEqual(BigInt(UInt64.max) + 1, BigInt("18446744073709551616")) -// XCTAssertEqual(BigInt(UInt64.max) + 2, BigInt("18446744073709551617")) -// -// XCTAssertEqual(BigInt(Int64.min) - 2, BigInt("-9223372036854775810")) -// XCTAssertEqual(BigInt(Int64.min) - 1, BigInt("-9223372036854775809")) -// XCTAssertEqual(BigInt(Int64.min) + 0, BigInt("-9223372036854775808")) -// XCTAssertEqual(BigInt(Int64.min) + 1, BigInt("-9223372036854775807")) -// XCTAssertEqual(BigInt(Int64.min) + 2, BigInt("-9223372036854775806")) -// -// XCTAssertEqual(BigInt(Int64.max) - 2, BigInt("9223372036854775805")) -// XCTAssertEqual(BigInt(Int64.max) - 1, BigInt("9223372036854775806")) -// XCTAssertEqual(BigInt(Int64.max) + 0, BigInt("9223372036854775807")) -// XCTAssertEqual(BigInt(Int64.max) + 1, BigInt("9223372036854775808")) -// XCTAssertEqual(BigInt(Int64.max) + 2, BigInt("9223372036854775809")) -// -// XCTAssertEqual(-(BigInt(1) << 1023), BigInt(Self.descriptionInt1024Min)) -// XCTAssertEqual(+(BigInt(1) << 1023) - 1, BigInt(Self.descriptionInt1024Max)) -// } -// -// func testRadicesAndNumerals() { -// for radix in 2 ... 36 { -// for uppercase in [false, true] { -// for _ in 0 ..< 100 { -// let expectedNumber = BigInt(Int.random(in: .min ... .max)) -// let expectedString = String(expectedNumber, -// radix: radix, -// uppercase: uppercase) -// let actualNumber = BigInt(expectedString, radix: radix) -// XCTAssertEqual(actualNumber, expectedNumber) -// if radix == 10 { -// let actualString = expectedNumber.description -// XCTAssertEqual(actualString, expectedString) -// } -// } -// } -// } -// } -// -// // MARK: - Converting from floating-point binary types -// -// func testBinaryFloatingPoint(_ type: T.Type) where T: BinaryFloatingPoint { -// var expected = BigInt(T.greatestFiniteMagnitude.significandBitPattern) -// expected |= BigInt(1) << T.significandBitCount -// expected <<= T.greatestFiniteMagnitude.exponent -// expected >>= T.significandBitCount -// -// XCTAssertEqual(BigInt(exactly: -T.greatestFiniteMagnitude), -expected) -// XCTAssertEqual(BigInt(exactly: +T.greatestFiniteMagnitude), +expected) -// XCTAssertEqual(BigInt(-T.greatestFiniteMagnitude), -expected) -// XCTAssertEqual(BigInt(+T.greatestFiniteMagnitude), +expected) -// -// XCTAssertNil(BigInt(exactly: -T.infinity)) -// XCTAssertNil(BigInt(exactly: +T.infinity)) -// -// XCTAssertNil(BigInt(exactly: -T.leastNonzeroMagnitude)) -// XCTAssertNil(BigInt(exactly: +T.leastNonzeroMagnitude)) -// XCTAssertEqual(BigInt(-T.leastNonzeroMagnitude), 0) -// XCTAssertEqual(BigInt(+T.leastNonzeroMagnitude), 0) -// -// XCTAssertNil(BigInt(exactly: -T.leastNormalMagnitude)) -// XCTAssertNil(BigInt(exactly: +T.leastNormalMagnitude)) -// XCTAssertEqual(BigInt(-T.leastNormalMagnitude), 0) -// XCTAssertEqual(BigInt(+T.leastNormalMagnitude), 0) -// -// XCTAssertNil(BigInt(exactly: T.nan)) -// XCTAssertNil(BigInt(exactly: T.signalingNaN)) -// -// XCTAssertNil(BigInt(exactly: -T.pi)) -// XCTAssertNil(BigInt(exactly: +T.pi)) -// XCTAssertEqual(BigInt(-T.pi), -3) -// XCTAssertEqual(BigInt(+T.pi), +3) -// -// XCTAssertNil(BigInt(exactly: -T.ulpOfOne)) -// XCTAssertNil(BigInt(exactly: +T.ulpOfOne)) -// XCTAssertEqual(BigInt(-T.ulpOfOne), 0) -// XCTAssertEqual(BigInt(+T.ulpOfOne), 0) -// -// XCTAssertEqual(BigInt(exactly: -T.zero), 0) -// XCTAssertEqual(BigInt(exactly: +T.zero), 0) -// XCTAssertEqual(BigInt(-T.zero), 0) -// XCTAssertEqual(BigInt(+T.zero), 0) -// } -// -// func testBinaryFloatingPoint() { -// testBinaryFloatingPoint(Float32.self) -// testBinaryFloatingPoint(Float64.self) -// #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) -// testBinaryFloatingPoint(Float80.self) -// #endif -// -// for _ in 0 ..< 100 { -// let small = Float32.random(in: -10 ... +10) -// XCTAssertEqual(BigInt(small), BigInt(Int64(small))) -// -// let large = Float32.random(in: -0x1p23 ... +0x1p23) -// XCTAssertEqual(BigInt(large), BigInt(Int64(large))) -// } -// -// for _ in 0 ..< 100 { -// let small = Float64.random(in: -10 ... +10) -// XCTAssertEqual(BigInt(small), BigInt(Int64(small))) -// -// let large = Float64.random(in: -0x1p52 ... +0x1p52) -// XCTAssertEqual(BigInt(large), BigInt(Int64(large))) -// } -// -// #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) -// for _ in 0 ..< 100 { -// let small = Float80.random(in: -10 ... +10) -// XCTAssertEqual(BigInt(small), BigInt(Int64(small))) -// -// let large = Float80.random(in: -0x1p63 ..< +0x1p63) -// XCTAssertEqual(BigInt(large), BigInt(Int64(large))) -// } -// #endif -// } + func testFactorial() { + var expectedNumber: BigInt? + var actualNumber: BigInt! + var actualString: String! + + measure { + expectedNumber = BigInt(Self.descriptionFactorial512_radix36, radix: 36) + actualNumber = BigInt.fac(512) + actualString = String(actualNumber, radix: 36, uppercase: true) + } + + XCTAssertEqual(actualNumber, expectedNumber) + XCTAssertEqual(actualString, Self.descriptionFactorial512_radix36) + + XCTAssertEqual(BigInt.fac(0), 1) + XCTAssertEqual(BigInt.fac(1), 1) + XCTAssertEqual(BigInt.fac(2), 2) + XCTAssertEqual(BigInt.fac(3), 6) + XCTAssertEqual(BigInt.fac(4), 24) + XCTAssertEqual(BigInt.fac(5), 120) + XCTAssertEqual(BigInt.fac(6), 720) + XCTAssertEqual(BigInt.fac(7), 5040) + XCTAssertEqual(BigInt.fac(8), 40320) + XCTAssertEqual(BigInt.fac(9), 362880) + } + + func testMath() { + let foo = BigInt.pow(10, 20) + let bar = BigInt("1234567890123456789012345678901234567890")! + + let baz = foo + bar + + XCTAssertEqual(baz, BigInt("1234567890123456789112345678901234567890")!) + + let fooz = foo >> BigInt(10) + XCTAssertEqual(fooz, foo / 1024) + + let barz = BigInt(1) << 64 + XCTAssertEqual(barz, BigInt(UInt64.max) + 1) + } + + func testNegation() { + let foo = BigInt("1234567890123456789012345678901234567890")! + let bar = BigInt(0) - foo + + XCTAssertEqual(-foo, bar) + + var baz = foo + baz.negate() + XCTAssertEqual(baz, bar) + } + + func testSignum() { + XCTAssertEqual(BigInt(-0x1p1023).signum(), -1) + XCTAssertEqual(BigInt(Int64.min).signum(), -1) + XCTAssertEqual(BigInt(Int32.min).signum(), -1) + XCTAssertEqual(BigInt(Int16.min).signum(), -1) + XCTAssertEqual(BigInt(Int8.min).signum(), -1) + XCTAssertEqual(BigInt(-1).signum(), -1) + XCTAssertEqual(BigInt(0).signum(), 0) + XCTAssertEqual(BigInt(+1).signum(), +1) + XCTAssertEqual(BigInt(Int8.max).signum(), +1) + XCTAssertEqual(BigInt(Int16.max).signum(), +1) + XCTAssertEqual(BigInt(Int32.max).signum(), +1) + XCTAssertEqual(BigInt(Int64.max).signum(), +1) + XCTAssertEqual(BigInt(+0x1p1023).signum(), +1) + } + + func testTrailingZeroCount() { + let foo = BigInt(1) << 300 + XCTAssertEqual(foo.trailingZeroBitCount, 300) + + let bar = (BigInt(1) << 300) + 0b101000 + XCTAssertEqual(bar.trailingZeroBitCount, 3) + } + + // MARK: - Comparing and hashing + + func testComparable() { + let foo = BigInt("1234567890123456789012345678901234567890")! + let bar = foo * foo + + XCTAssertLessThan(foo, bar) + XCTAssertFalse(foo < foo) + XCTAssertFalse(bar < bar) + XCTAssertFalse(foo > foo) + XCTAssertFalse(bar > bar) + XCTAssertGreaterThan(bar, foo) + + let baz = bar * -1 + + XCTAssertLessThan(baz, foo) + XCTAssertNotEqual(bar, baz) + XCTAssertFalse(baz < baz) + } + + func testComparison() { + let foo = BigInt(-10) + let bar = BigInt(-20) + + XCTAssert(foo > bar) + XCTAssert(bar < foo) + XCTAssert(foo == BigInt(-10)) + + let baz = BigInt.pow(foo, -bar) + XCTAssertEqual(baz, BigInt("100000000000000000000")!) + } + + func testExample() { + let bar = BigInt(exactly: -100) + XCTAssertNotNil(bar) + if let bar = bar { + XCTAssertLessThan(bar, 0) + XCTAssertGreaterThan(-bar, 0) + XCTAssertEqual(-bar, BigInt(100)) + } + XCTAssertEqual(-BigInt("-1234567890123456789012345678901234567890")!, + +BigInt("+1234567890123456789012345678901234567890")!) + } + + func testsWhichCrashed() { + // -9223372036854775808 = Int64.min, obviously '-Int64.min' overflows + let int: Int64 = -9223372036854775808 + var big = BigInt(int) + XCTAssertEqual(-big, big * -1) + + // 9223372036854775808 = UInt64(1) << Float80.significandBitCount + var int2 : UInt64 = 9223372036854775808 + big = BigInt(int2) + let _ = Float80(exactly: int2) // works + let _ = Float80(exactly: big) // crash (not anymore) + + // 18446744073709551615 = UInt64.max - was crashing + int2 = 18446744073709551615 + big = BigInt(int2) + let _ = UInt64(big) + + // was generating an overflow + let lhsInt = -9223372036854775808 + let rhsInt = -1 + let lhs = BigInt(lhsInt) + let rhs = BigInt(rhsInt) + _ = lhs / rhs // Overflow + } + + func test_initFromInt_exactly() { + let int: UInt64 = 18446744073709551614 + let big = BigInt(exactly: int)! + let revert = UInt64(exactly: big) + XCTAssertEqual(int, revert) + } + + func test_initFromInt_clamping() { + let int: UInt64 = 18446744073709551614 + let big = BigInt(clamping: int) + let revert = UInt64(clamping: big) + XCTAssertEqual(int, revert) + } + + func test_initFromInt_truncatingIfNeeded() { + let int: UInt64 = 18446744073709551615 + let big = BigInt(truncatingIfNeeded: int) + let intString = String(int, radix: 10, uppercase: false) + let bigString = String(big, radix: 10, uppercase: false) + XCTAssertEqual(bigString, intString) + } + + func test_unaryMinus() { + // -9223372036854775808 = Int.min + // 'Int.min' negation overflows - ok now + let int = -9223372036854775808 + let expected = BigInt(int.magnitude) + + let big = -BigInt(int) + XCTAssertEqual(big, expected) + + var negated = BigInt(int) + negated.negate() + XCTAssertEqual(negated, expected, "\(negated) == \(expected)") + } + + func test_div_sign() { + // positive / negative = negative + var lhs = BigInt("18446744073709551615")! + var rhs = BigInt("-1")! + var expected = BigInt("-18446744073709551615")! + XCTAssertEqual(lhs / rhs, expected) + + // negative / positive = negative + lhs = BigInt("-340282366920938463481821351505477763074")! + rhs = BigInt("18446744073709551629")! + expected = BigInt("-18446744073709551604")! + XCTAssertEqual(lhs / rhs, expected) + } + + func test_magnitude() { + let big = BigInt(-9223372036854775808) + let expect = UInt64(9223372036854775808) + XCTAssertEqual(big.magnitude, BigInt(expect)) + } + + // ApplyA_ApplyB_Equals_ApplyAB.swift + func test_a_b_ab() { + // (a/-3) / -5 = a/15 + let lhs = BigInt("-18446744073709551615")! + let a = BigInt(-3) + let b = BigInt(-5) + let ab = BigInt(15) + + let r0 = (lhs / a) / b + let r1 = lhs / ab + XCTAssertEqual(r0, r1) + } + + // ApplyA_UndoA.swift + func test_a_undoA() { + let n = BigInt(-1) + let x = BigInt(-9223372036854775808) + XCTAssertEqual(n, (n + x) - x) + XCTAssertEqual(n, (n * x) / x) + } + + func test_node_mod_incorrectSign() { + // SMALL % BIG = SMALL + // We need to satisfy: BIG * 0 + result = SMALL -> result = SMALL + // The same, but on the standard Swift.Int to prove the point: + XCTAssertEqual(-1 % 123, -1) + XCTAssertEqual(-1 % -123, -1) + // In general the 'reminder' follows the 'lhs' sign (round toward 0). + // Except for the case where 'lhs' is negative and 'reminder' is 0. + + var lhs = BigInt("-1")! + var rhs = BigInt("18446744073709551615")! + XCTAssertEqual(lhs % rhs, lhs) + + // Also fails if 'rhs' is negative + lhs = BigInt("-7730941133")! + rhs = BigInt("-18446744073709551615")! + XCTAssertEqual(lhs % rhs, lhs) + } + + // From my observations all of the `xor` tests are failing. + func test_node_xor() { + var lhs = BigInt("0")! + var rhs = BigInt("1")! + var expected = BigInt("1")! + XCTAssertEqual(lhs ^ rhs, expected) + XCTAssertEqual(0 ^ 1, 1) // Proof + + lhs = BigInt("0")! + rhs = BigInt("-1")! + expected = BigInt("-1")! + XCTAssertEqual(lhs ^ rhs, expected) + XCTAssertEqual(0 ^ -1, -1) // Proof + } + + func test_xor_truthTable() { + let lhsWord : UInt8 = 0b1100 + let rhsWord : UInt8 = 0b1010 + + let lhs = BigInt(lhsWord) + let rhs = BigInt(rhsWord) + + let expected = BigInt(lhsWord ^ rhsWord) + XCTAssertEqual(lhs ^ rhs, expected) + XCTAssertEqual(rhs ^ lhs, expected) + } + + func test_binarySub() { + // https://www.wolframalpha.com/input?i=-922337203685477587+-+%28-9223372036854775808%29 + let lhs = BigInt("-922337203685477587")! + let rhs = BigInt("-9223372036854775808")! + let expected = BigInt("8301034833169298221")! + XCTAssertEqual(lhs - rhs, expected) + // The same on Swift.Int: + XCTAssertEqual(-922337203685477587 - (-9223372036854775808), 8301034833169298221) + } + + func test_binarySub_2() { + typealias Word = UInt + + let intMax = Int.max + let intMaxAsWord = Word(intMax.magnitude) + + // intMax - (-(Word.max - intMaxAsWord)) = + // intMax - (-Word.max + intMaxAsWord) = + // intMax + Word.max - intMaxAsWord = + // Word.max + let max = BigInt(intMax) + let value = -BigInt((Word.max - intMaxAsWord)) + let expected = BigInt(Word.max) + XCTAssertEqual(max - value, expected) + } + + func testHashable() { + let foo = BigInt("1234567890123456789012345678901234567890")! + let bar = BigInt("1234567890123456789112345678901234567890")! + let baz: BigInt = 153 + + let dict = [ foo: "Hello", bar: "World", baz: "!" ] + + let hash = foo.hashValue + print(hash) + + XCTAssertEqual(dict[foo]!, "Hello") + XCTAssertEqual(dict[bar]!, "World") + } + + func testClampingConversion() { + XCTAssertEqual(BigInt(clamping: UInt64.max), BigInt(UInt64(18446744073709551615))) + } + + func testUIntConversion() { + let foo = BigInt(UInt.max) + XCTAssertNotEqual(foo, BigInt(-1)) + + let bar = BigInt(bitPattern: UInt.max) + XCTAssertEqual(bar, BigInt(-1)) + } + + // MARK: - Testing logical functions + + func testLogical() { + let a = BigInt("7FFF555512340000", radix: 16)! + let b = BigInt("0000ABCD9876FFFF", radix: 16)! + + let aAndb = String(a & b, radix: 16) + let aOrb = String(a | b, radix: 16) + let aXorb = String(a ^ b, radix: 16) + let notb = String(~b, radix: 16) + + let shiftLeft1 = String(a << 16, radix:16) + let shiftLeft2 = String(a << -3, radix:16) + let shiftRight1 = String(a >> 1000, radix:16) + let shiftRight2 = String(a >> -7, radix:16) + + print("a & b = 0x\(aAndb)"); XCTAssertEqual(aAndb, "14510340000") + print("a | b = 0x\(aOrb)"); XCTAssertEqual(aOrb, "7fffffdd9a76ffff") + print("a ^ b = 0x\(aXorb)"); XCTAssertEqual(aXorb, "7ffffe988a42ffff") + print("~b = 0x\(notb)"); XCTAssertEqual(notb, "-abcd98770000") + + print("a << 16 = \(shiftLeft1)"); XCTAssertEqual(shiftLeft1, "7fff5555123400000000") + print("a << -3 = \(shiftLeft2)"); XCTAssertEqual(shiftLeft2, "fffeaaaa2468000") + print("a >> 1000 = \(shiftRight1)"); XCTAssertEqual(shiftRight1, "0") + print("a >> -7 = \(shiftRight2)"); XCTAssertEqual(shiftRight2, "3fffaaaa891a000000") + } + + // MARK: - Converting to/from textual representations + + func testCodable() throws { + let lowerBound = BigInt("-1234567890123456789012345678901234567890")! + let upperBound = BigInt("+1234567890123456789012345678901234567890")! + let expectedRange: Range = lowerBound ..< upperBound + + let encoder = JSONEncoder() + let decoder = JSONDecoder() + let data = try encoder.encode(expectedRange) + let actualRange = try decoder.decode(Range.self, from: data) + + XCTAssertEqual(actualRange, expectedRange) + } + + func testCustomStringConvertible() { + XCTAssertEqual("\(BigInt(UInt64.min) - 2)", "-2") + XCTAssertEqual("\(BigInt(UInt64.min) - 1)", "-1") + XCTAssertEqual("\(BigInt(UInt64.min) + 0)", "0") + XCTAssertEqual("\(BigInt(UInt64.min) + 1)", "1") + XCTAssertEqual("\(BigInt(UInt64.min) + 2)", "2") + + XCTAssertEqual("\(BigInt(UInt64.max) - 2)", "18446744073709551613") + XCTAssertEqual("\(BigInt(UInt64.max) - 1)", "18446744073709551614") + XCTAssertEqual("\(BigInt(UInt64.max) + 0)", "18446744073709551615") + XCTAssertEqual("\(BigInt(UInt64.max) + 1)", "18446744073709551616") + XCTAssertEqual("\(BigInt(UInt64.max) + 2)", "18446744073709551617") + + XCTAssertEqual("\(BigInt(Int64.min) - 2)", "-9223372036854775810") + XCTAssertEqual("\(BigInt(Int64.min) - 1)", "-9223372036854775809") + XCTAssertEqual("\(BigInt(Int64.min) + 0)", "-9223372036854775808") + XCTAssertEqual("\(BigInt(Int64.min) + 1)", "-9223372036854775807") + XCTAssertEqual("\(BigInt(Int64.min) + 2)", "-9223372036854775806") + + XCTAssertEqual("\(BigInt(Int64.max) - 2)", "9223372036854775805") + XCTAssertEqual("\(BigInt(Int64.max) - 1)", "9223372036854775806") + XCTAssertEqual("\(BigInt(Int64.max) + 0)", "9223372036854775807") + XCTAssertEqual("\(BigInt(Int64.max) + 1)", "9223372036854775808") + XCTAssertEqual("\(BigInt(Int64.max) + 2)", "9223372036854775809") + + XCTAssertEqual("\(-(BigInt(1) << 1023))", Self.descriptionInt1024Min) + XCTAssertEqual("\(+(BigInt(1) << 1023) - 1)", Self.descriptionInt1024Max) + } + + func testLosslessStringConvertible() { + XCTAssertNil(BigInt("")) + XCTAssertNil(BigInt("-")) + XCTAssertNil(BigInt("+")) + XCTAssertNil(BigInt("A")) + XCTAssertNil(BigInt(" 0")) + XCTAssertNil(BigInt("0 ")) + + XCTAssertEqual(BigInt(UInt64.min) - 2, BigInt("-2")) + XCTAssertEqual(BigInt(UInt64.min) - 1, BigInt("-1")) + XCTAssertEqual(BigInt(UInt64.min) + 0, BigInt("0")) + XCTAssertEqual(BigInt(UInt64.min) + 1, BigInt("1")) + XCTAssertEqual(BigInt(UInt64.min) + 2, BigInt("2")) + + XCTAssertEqual(BigInt(UInt64.max) - 2, BigInt("18446744073709551613")) + XCTAssertEqual(BigInt(UInt64.max) - 1, BigInt("18446744073709551614")) + XCTAssertEqual(BigInt(UInt64.max) + 0, BigInt("18446744073709551615")) + XCTAssertEqual(BigInt(UInt64.max) + 1, BigInt("18446744073709551616")) + XCTAssertEqual(BigInt(UInt64.max) + 2, BigInt("18446744073709551617")) + + XCTAssertEqual(BigInt(Int64.min) - 2, BigInt("-9223372036854775810")) + XCTAssertEqual(BigInt(Int64.min) - 1, BigInt("-9223372036854775809")) + XCTAssertEqual(BigInt(Int64.min) + 0, BigInt("-9223372036854775808")) + XCTAssertEqual(BigInt(Int64.min) + 1, BigInt("-9223372036854775807")) + XCTAssertEqual(BigInt(Int64.min) + 2, BigInt("-9223372036854775806")) + + XCTAssertEqual(BigInt(Int64.max) - 2, BigInt("9223372036854775805")) + XCTAssertEqual(BigInt(Int64.max) - 1, BigInt("9223372036854775806")) + XCTAssertEqual(BigInt(Int64.max) + 0, BigInt("9223372036854775807")) + XCTAssertEqual(BigInt(Int64.max) + 1, BigInt("9223372036854775808")) + XCTAssertEqual(BigInt(Int64.max) + 2, BigInt("9223372036854775809")) + + XCTAssertEqual(-(BigInt(1) << 1023), BigInt(Self.descriptionInt1024Min)) + XCTAssertEqual(+(BigInt(1) << 1023) - 1, BigInt(Self.descriptionInt1024Max)) + } + + func testRadicesAndNumerals() { + for radix in 2 ... 36 { + for uppercase in [false, true] { + for _ in 0 ..< 100 { + let expectedNumber = BigInt(Int.random(in: .min ... .max)) + let expectedString = String(expectedNumber, + radix: radix, + uppercase: uppercase) + let actualNumber = BigInt(expectedString, radix: radix) + XCTAssertEqual(actualNumber, expectedNumber) + if radix == 10 { + let actualString = expectedNumber.description + XCTAssertEqual(actualString, expectedString) + } + } + } + } + } + + // MARK: - Converting from floating-point binary types + + func testBinaryFloatingPoint(_ type: T.Type) where T: BinaryFloatingPoint { + var expected = BigInt(T.greatestFiniteMagnitude.significandBitPattern) + expected |= BigInt(1) << T.significandBitCount + expected <<= T.greatestFiniteMagnitude.exponent + expected >>= T.significandBitCount + + XCTAssertEqual(BigInt(exactly: -T.greatestFiniteMagnitude), -expected) + XCTAssertEqual(BigInt(exactly: +T.greatestFiniteMagnitude), +expected) + XCTAssertEqual(BigInt(-T.greatestFiniteMagnitude), -expected) + XCTAssertEqual(BigInt(+T.greatestFiniteMagnitude), +expected) + + XCTAssertNil(BigInt(exactly: -T.infinity)) + XCTAssertNil(BigInt(exactly: +T.infinity)) + + XCTAssertNil(BigInt(exactly: -T.leastNonzeroMagnitude)) + XCTAssertNil(BigInt(exactly: +T.leastNonzeroMagnitude)) + XCTAssertEqual(BigInt(-T.leastNonzeroMagnitude), 0) + XCTAssertEqual(BigInt(+T.leastNonzeroMagnitude), 0) + + XCTAssertNil(BigInt(exactly: -T.leastNormalMagnitude)) + XCTAssertNil(BigInt(exactly: +T.leastNormalMagnitude)) + XCTAssertEqual(BigInt(-T.leastNormalMagnitude), 0) + XCTAssertEqual(BigInt(+T.leastNormalMagnitude), 0) + + XCTAssertNil(BigInt(exactly: T.nan)) + XCTAssertNil(BigInt(exactly: T.signalingNaN)) + + XCTAssertNil(BigInt(exactly: -T.pi)) + XCTAssertNil(BigInt(exactly: +T.pi)) + XCTAssertEqual(BigInt(-T.pi), -3) + XCTAssertEqual(BigInt(+T.pi), +3) + + XCTAssertNil(BigInt(exactly: -T.ulpOfOne)) + XCTAssertNil(BigInt(exactly: +T.ulpOfOne)) + XCTAssertEqual(BigInt(-T.ulpOfOne), 0) + XCTAssertEqual(BigInt(+T.ulpOfOne), 0) + + XCTAssertEqual(BigInt(exactly: -T.zero), 0) + XCTAssertEqual(BigInt(exactly: +T.zero), 0) + XCTAssertEqual(BigInt(-T.zero), 0) + XCTAssertEqual(BigInt(+T.zero), 0) + } + + func testBinaryFloatingPoint() { + testBinaryFloatingPoint(Float32.self) + testBinaryFloatingPoint(Float64.self) + #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) + testBinaryFloatingPoint(Float80.self) + #endif + + for _ in 0 ..< 100 { + let small = Float32.random(in: -10 ... +10) + XCTAssertEqual(BigInt(small), BigInt(Int64(small))) + + let large = Float32.random(in: -0x1p23 ... +0x1p23) + XCTAssertEqual(BigInt(large), BigInt(Int64(large))) + } + + for _ in 0 ..< 100 { + let small = Float64.random(in: -10 ... +10) + XCTAssertEqual(BigInt(small), BigInt(Int64(small))) + + let large = Float64.random(in: -0x1p52 ... +0x1p52) + XCTAssertEqual(BigInt(large), BigInt(Int64(large))) + } + + #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) + for _ in 0 ..< 100 { + let small = Float80.random(in: -10 ... +10) + XCTAssertEqual(BigInt(small), BigInt(Int64(small))) + + let large = Float80.random(in: -0x1p63 ..< +0x1p63) + XCTAssertEqual(BigInt(large), BigInt(Int64(large))) + } + #endif + } } From 1fc9e13ca8b5d46cc70fe211130bd6e7a630fb48 Mon Sep 17 00:00:00 2001 From: Michael Griebling Date: Thu, 27 Apr 2023 06:47:02 -0400 Subject: [PATCH 7/7] Removed Foundation import from BigInt. Added an additional check to isValidPowerOfTwoRadix so odd numbers of bits (except radix 2) don't use the fast path in toString. Now use a table lookup to determine the radix number bits. Added a check for iOS for StringLiteralType. Tested code under iOS and macOS under simulator and x86 --- Sources/BigIntModule/BigInt.swift | 29 +++++++++++++++++++---------- Tests/BigIntTests/BigIntTests.swift | 6 +++--- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Sources/BigIntModule/BigInt.swift b/Sources/BigIntModule/BigInt.swift index 02113870..3e06993d 100644 --- a/Sources/BigIntModule/BigInt.swift +++ b/Sources/BigIntModule/BigInt.swift @@ -9,8 +9,6 @@ // //===----------------------------------------------------------------------===// -import Foundation - public struct BigInt: SignedInteger { public typealias Words = [UInt] @@ -151,11 +149,11 @@ extension BigInt: LosslessStringConvertible { var currentWord = UInt(0) let firstWordCount = remainingCount % charCountPerWord var remainingCharsInCurrentWord = firstWordCount == 0 ? charCountPerWord : firstWordCount - let powerOfTwo = radix & (radix - 1) == 0 + let isPowerOfTwo = radix & (radix - 1) == 0 // working buffer var buffer = Words(repeating: 0, count: capacity) - if powerOfTwo { + if isPowerOfTwo { // Radix powers of 2 convert about 10X faster with this algorithm var reverseIndex = chars.endIndex chars.formIndex(before: &reverseIndex) @@ -306,17 +304,16 @@ extension BigInt : CustomStringConvertible { if self.signum() == 0 { return "0" } - let logWord = log(Double(UInt.max)) - let logRadix = log(Double(radix)) - let digitCount = Int(logWord/logRadix * Double(words.count) + 0.5) + let digitCount = Int(Self.radixDigits[radix-2] * Double(words.count) + 0.5) let (charPerWord, power) = Self.maxRepresentablePower(of: radix) let stringMaxSize = digitCount + (self._isNegative ? 1 : 0) - let isPowerOfTwoRadix = radix & (radix-1) == 0 + let radixValid = radix != 8 && radix != 32 + let isValidPowerOfTwoRadix = (radix & (radix-1) == 0) && radixValid var words = self.magnitude.words; words.reserveCapacity(self.words.count) - if #available(macOS 11.0, *) { + if #available(macOS 11.0, iOS 14.0, *) { return StringLiteralType(unsafeUninitializedCapacity: stringMaxSize) { buffer in - if isPowerOfTwoRadix { + if isValidPowerOfTwoRadix { // Handle powers-of-two radixes let bitsPerChar = radix.trailingZeroBitCount let qr = UInt.bitWidth.quotientAndRemainder(dividingBy: bitsPerChar) @@ -414,6 +411,18 @@ extension BigInt : CustomStringConvertible { return n < 10 ? n + Self._0 : n - 10 + (uppercase ? Self._A : Self._a) } + /// Table of the number of digits for each radix from 2 to 36 + /// y = log(Double(UInt.max)) / log(Double(radix)) + static let radixDigits : [Double] = [ + 64, 40.3795042286, 32.0000000000, 27.5632997167, 24.7585796630, + 22.7972599749, 21.3333333333, 20.1897521143, 19.2659197225, 18.5001488843, + 17.8523485217, 17.2952418833, 16.8095702424, 16.3813135878, 16.0000000000, + 15.6576346956, 15.3479978604, 15.0661704555, 14.8082056422, 14.5708959166, + 14.3516047499, 14.1481426853, 13.9586746871, 13.7816498583, 13.6157474274, + 13.4598347429, 13.3129342513, 13.1741972775, 13.0428830138, 12.9183415413, + 12.8000000000, 12.6873512429, 12.5799444629, 12.4773774012, 12.3792898315 + ] + /// NEW CODE ENDS /// //////////////////////////////////////////////////////////////////////////// diff --git a/Tests/BigIntTests/BigIntTests.swift b/Tests/BigIntTests/BigIntTests.swift index 1014e584..103c5b5d 100644 --- a/Tests/BigIntTests/BigIntTests.swift +++ b/Tests/BigIntTests/BigIntTests.swift @@ -182,7 +182,7 @@ final class BigIntTests: XCTestCase { var expectedNumber: BigInt! measure { - expectedNumber = BigInt(Self.descriptionFactorial512_radix10, radix: 10) + expectedNumber = BigInt(Self.descriptionFactorial512_radix16, radix: 16) } XCTAssertEqual(expectedNumber, BigInt(Self.descriptionFactorial512_radix10, radix: 10)) @@ -193,10 +193,10 @@ final class BigIntTests: XCTestCase { var expectedString = "" measure { - expectedString = number.toString(radix: 10, uppercase: false) + expectedString = number.toString(radix: 16, uppercase: false) } - XCTAssertEqual(expectedString, Self.descriptionFactorial512_radix10) + XCTAssertEqual(expectedString, Self.descriptionFactorial512_radix16) } func testFactorial() {