BigIntプロジェクトの中核である BigInt.swift にバグがあったので修正版を載せます。テキストエディタにコピペしてファイル名を必ずBigInt.swift または BigInt.swift.rtf(テキストファイル)で保存してください。
import Foundation
public struct BigInt: Comparable, ExpressibleByStringLiteral, ExpressibleByIntegerLiteral {
public var digits: [UInt8]
public var isNegative: Bool
public init(_ number: String) {
var str = number.trimmingCharacters(in: CharacterSet.whitespaces)
if str.hasPrefix("-") {
isNegative = true
str.removeFirst()
} else {
isNegative = false
}
let dropped = str.drop(while: { $0 == "0" })
str = dropped.isEmpty ? "0" : String(dropped)
if str.allSatisfy({ $0.isNumber }) {
digits = str.reversed().compactMap { UInt8(String($0)) }
} else {
fatalError("Invalid characters in BigInt initializer: \(number)")
}
if digits == [0] {
isNegative = false
}
}
public init(stringLiteral value: String) {
self.init(value)
}
public init(integerLiteral value: Int) {
self.init(String(value))
}
public static func == (lhs: BigInt, rhs: BigInt) -> Bool {
lhs.isNegative == rhs.isNegative && lhs.digits == rhs.digits
}
public static func < (lhs: BigInt, rhs: BigInt) -> Bool {
if lhs.isNegative != rhs.isNegative {
return lhs.isNegative
}
if lhs.digits.count != rhs.digits.count {
return lhs.isNegative ? lhs.digits.count > rhs.digits.count : lhs.digits.count < rhs.digits.count
}
for (l, r) in zip(lhs.digits.reversed(), rhs.digits.reversed()) {
if l != r {
return lhs.isNegative ? l > r : l < r
}
}
return false
}
public static func + (lhs: BigInt, rhs: BigInt) -> BigInt {
if lhs.isNegative == rhs.isNegative {
let result = add(lhs.digits, rhs.digits)
return BigInt(digits: result, isNegative: lhs.isNegative).trimmed()
} else {
if abs(lhs) >= abs(rhs) {
let result = subtract(lhs.digits, rhs.digits)
return BigInt(digits: result, isNegative: lhs.isNegative).trimmed()
} else {
let result = subtract(rhs.digits, lhs.digits)
return BigInt(digits: result, isNegative: rhs.isNegative).trimmed()
}
}
}
public static func * (lhs: BigInt, rhs: BigInt) -> BigInt {
let result = multiply(lhs.digits, rhs.digits)
return BigInt(digits: result, isNegative: lhs.isNegative != rhs.isNegative).trimmed()
}
private static func add(_ a: [UInt8], _ b: [UInt8]) -> [UInt8] {
let maxLength = max(a.count, b.count)
var result: [UInt8] = []
var carry: UInt8 = 0
for i in 0..<maxLength {
let digitA = i < a.count ? a[i] : 0
let digitB = i < b.count ? b[i] : 0
let sum = digitA + digitB + carry
result.append(sum % 10)
carry = sum / 10
}
if carry > 0 { result.append(carry) }
return result
}
private static func subtract(_ a: [UInt8], _ b: [UInt8]) -> [UInt8] {
var result: [UInt8] = []
var borrow: Int = 0
for i in 0..<a.count {
let digitA = Int(a[i])
let digitB = i < b.count ? Int(b[i]) : 0
var sub = digitA - digitB - borrow
if sub < 0 {
sub += 10
borrow = 1
} else {
borrow = 0
}
result.append(UInt8(sub))
}
return result
}
private static func multiply(_ a: [UInt8], _ b: [UInt8]) -> [UInt8] {
var result = [UInt8](repeating: 0, count: a.count + b.count)
for i in 0..<a.count {
var carry: UInt16 = 0
for j in 0..<b.count {
let mul = UInt16(a[i]) * UInt16(b[j]) + UInt16(result[i + j]) + carry
result[i + j] = UInt8(mul % 10)
carry = mul / 10
}
result[i + b.count] += UInt8(carry)
}
return result
}
private func trimmed() -> BigInt {
var copy = self
while copy.digits.count > 1 && copy.digits.last == 0 {
copy.digits.removeLast()
}
if copy.digits == [0] { copy.isNegative = false }
return copy
}
public init(digits: [UInt8], isNegative: Bool) {
self.digits = digits
self.isNegative = isNegative
}
public static prefix func - (value: BigInt) -> BigInt {
if value == BigInt("0") { return value }
return BigInt(digits: value.digits, isNegative: !value.isNegative)
}
}
extension BigInt {
public static func abs(_ value: BigInt) -> BigInt {
return BigInt(digits: value.digits, isNegative: false)
}
}
extension BigInt: CustomStringConvertible {
public var description: String {
let numberString = digits.reversed().map(String.init).joined()
return isNegative && numberString != "0" ? "-" + numberString : numberString
}
}
