// from https://github.com/Jitsusama/UInt128/blob/master/Sources/UInt128.swift // // UInt128.swift // // An implementation of a 128-bit unsigned integer data type not // relying on any outside libraries apart from Swift's standard // library. It also seeks to implement the entirety of the // UnsignedInteger protocol as well as standard functions supported // by Swift's native unsigned integer types. // // Copyright 2017 Joel Gerber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // MARK: Error Type /// An `ErrorType` for `UInt128` data types. It includes cases /// for errors that can occur during string /// conversion. public enum UInt128Errors : Error { /// Input cannot be converted to a UInt128 value. case invalidString } // MARK: - Data Type /// A 128-bit unsigned integer value type. /// Storage is based upon a tuple of 2, 64-bit, unsigned integers. public struct UInt128 { // MARK: Instance Properties /// Internal value is presented as a tuple of 2 64-bit /// unsigned integers. internal var value: (upperBits: UInt64, lowerBits: UInt64) /// Counts up the significant bits in stored data. public var significantBits: UInt128 { return UInt128(UInt128.bitWidth - leadingZeroBitCount) } /// Undocumented private variable required for passing this type /// to a BinaryFloatingPoint type. See FloatingPoint.swift.gyb in /// the Swift stdlib/public/core directory. internal var signBitIndex: Int { return 127 - leadingZeroBitCount } // MARK: Initializers /// Designated initializer for the UInt128 type. public init(upperBits: UInt64, lowerBits: UInt64) { value.upperBits = upperBits value.lowerBits = lowerBits } public init() { self.init(upperBits: 0, lowerBits: 0) } public init(_ source: UInt128) { self.init(upperBits: source.value.upperBits, lowerBits: source.value.lowerBits) } /// Initialize a UInt128 value from a string. /// /// - parameter source: the string that will be converted into a /// UInt128 value. Defaults to being analyzed as a base10 number, /// but can be prefixed with `0b` for base2, `0o` for base8 /// or `0x` for base16. public init(_ source: String) throws { guard let result = UInt128._valueFromString(source) else { throw UInt128Errors.invalidString } self = result } } // MARK: - FixedWidthInteger Conformance extension UInt128 : FixedWidthInteger { // MARK: Instance Properties public var nonzeroBitCount: Int { return value.lowerBits.nonzeroBitCount + value.upperBits.nonzeroBitCount } public var leadingZeroBitCount: Int { if value.upperBits == 0 { return UInt64.bitWidth + value.lowerBits.leadingZeroBitCount } return value.upperBits.leadingZeroBitCount } /// Returns the big-endian representation of the integer, changing the byte order if necessary. public var bigEndian: UInt128 { #if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64) return self.byteSwapped #else return self #endif } /// Returns the little-endian representation of the integer, changing the byte order if necessary. public var littleEndian: UInt128 { #if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64) return self #else return self.byteSwapped #endif } /// Returns the current integer with the byte order swapped. public var byteSwapped: UInt128 { return UInt128(upperBits: self.value.lowerBits.byteSwapped, lowerBits: self.value.upperBits.byteSwapped) } // MARK: Initializers /// Creates a UInt128 from a given value, with the input's value /// truncated to a size no larger than what UInt128 can handle. /// Since the input is constrained to an UInt, no truncation needs /// to occur, as a UInt is currently 64 bits at the maximum. public init(_truncatingBits bits: UInt) { self.init(upperBits: 0, lowerBits: UInt64(bits)) } /// Creates an integer from its big-endian representation, changing the /// byte order if necessary. public init(bigEndian value: UInt128) { self = value.bigEndian } /// Creates an integer from its little-endian representation, changing the /// byte order if necessary. public init(littleEndian value: UInt128) { self = value.littleEndian } // MARK: Instance Methods public func addingReportingOverflow(_ rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { var resultOverflow = false let (lowerBits, lowerOverflow) = self.value.lowerBits.addingReportingOverflow(rhs.value.lowerBits) var (upperBits, upperOverflow) = self.value.upperBits.addingReportingOverflow(rhs.value.upperBits) // If the lower bits overflowed, we need to add 1 to upper bits. if lowerOverflow { (upperBits, resultOverflow) = upperBits.addingReportingOverflow(1) } return (partialValue: UInt128(upperBits: upperBits, lowerBits: lowerBits), overflow: upperOverflow || resultOverflow) } public func subtractingReportingOverflow(_ rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { var resultOverflow = false let (lowerBits, lowerOverflow) = self.value.lowerBits.subtractingReportingOverflow(rhs.value.lowerBits) var (upperBits, upperOverflow) = self.value.upperBits.subtractingReportingOverflow(rhs.value.upperBits) // If the lower bits overflowed, we need to subtract (borrow) 1 from the upper bits. if lowerOverflow { (upperBits, resultOverflow) = upperBits.subtractingReportingOverflow(1) } return (partialValue: UInt128(upperBits: upperBits, lowerBits: lowerBits), overflow: upperOverflow || resultOverflow) } public func multipliedReportingOverflow(by rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { let multiplicationResult = self.multipliedFullWidth(by: rhs) let overflowEncountered = multiplicationResult.high > 0 return (partialValue: multiplicationResult.low, overflow: overflowEncountered) } public func multipliedFullWidth(by other: UInt128) -> (high: UInt128, low: UInt128.Magnitude) { // Bit mask that facilitates masking the lower 32 bits of a 64 bit UInt. let lower32 = UInt64(UInt32.max) // Decompose lhs into an array of 4, 32 significant bit UInt64s. let lhsArray = [ self.value.upperBits >> 32, /*0*/ self.value.upperBits & lower32, /*1*/ self.value.lowerBits >> 32, /*2*/ self.value.lowerBits & lower32 /*3*/ ] // Decompose rhs into an array of 4, 32 significant bit UInt64s. let rhsArray = [ other.value.upperBits >> 32, /*0*/ other.value.upperBits & lower32, /*1*/ other.value.lowerBits >> 32, /*2*/ other.value.lowerBits & lower32 /*3*/ ] // The future contents of this array will be used to store segment // multiplication results. var resultArray = [[UInt64]]( repeating: [UInt64](repeating: 0, count: 4), count: 4 ) // Loop through every combination of lhsArray[x] * rhsArray[y] for rhsSegment in 0 ..< rhsArray.count { for lhsSegment in 0 ..< lhsArray.count { let currentValue = lhsArray[lhsSegment] * rhsArray[rhsSegment] resultArray[lhsSegment][rhsSegment] = currentValue } } // Perform multiplication similar to pen and paper in 64bit, 32bit masked increments. let bitSegment8 = resultArray[3][3] & lower32 let bitSegment7 = UInt128._variadicAdditionWithOverflowCount( resultArray[2][3] & lower32, resultArray[3][2] & lower32, resultArray[3][3] >> 32) // overflow from bitSegment8 let bitSegment6 = UInt128._variadicAdditionWithOverflowCount( resultArray[1][3] & lower32, resultArray[2][2] & lower32, resultArray[3][1] & lower32, resultArray[2][3] >> 32, // overflow from bitSegment7 resultArray[3][2] >> 32, // overflow from bitSegment7 bitSegment7.overflowCount) let bitSegment5 = UInt128._variadicAdditionWithOverflowCount( resultArray[0][3] & lower32, resultArray[1][2] & lower32, resultArray[2][1] & lower32, resultArray[3][0] & lower32, resultArray[1][3] >> 32, // overflow from bitSegment6 resultArray[2][2] >> 32, // overflow from bitSegment6 resultArray[3][1] >> 32, // overflow from bitSegment6 bitSegment6.overflowCount) let bitSegment4 = UInt128._variadicAdditionWithOverflowCount( resultArray[0][2] & lower32, resultArray[1][1] & lower32, resultArray[2][0] & lower32, resultArray[0][3] >> 32, // overflow from bitSegment5 resultArray[1][2] >> 32, // overflow from bitSegment5 resultArray[2][1] >> 32, // overflow from bitSegment5 resultArray[3][0] >> 32, // overflow from bitSegment5 bitSegment5.overflowCount) let bitSegment3 = UInt128._variadicAdditionWithOverflowCount( resultArray[0][1] & lower32, resultArray[1][0] & lower32, resultArray[0][2] >> 32, // overflow from bitSegment4 resultArray[1][1] >> 32, // overflow from bitSegment4 resultArray[2][0] >> 32, // overflow from bitSegment4 bitSegment4.overflowCount) let bitSegment1 = UInt128._variadicAdditionWithOverflowCount( resultArray[0][0], resultArray[0][1] >> 32, // overflow from bitSegment3 resultArray[1][0] >> 32, // overflow from bitSegment3 bitSegment3.overflowCount) // Shift and merge the results into 64 bit groups, adding in overflows as we go. let lowerLowerBits = UInt128._variadicAdditionWithOverflowCount( bitSegment8, bitSegment7.truncatedValue << 32) let upperLowerBits = UInt128._variadicAdditionWithOverflowCount( bitSegment7.truncatedValue >> 32, bitSegment6.truncatedValue, bitSegment5.truncatedValue << 32, lowerLowerBits.overflowCount) let lowerUpperBits = UInt128._variadicAdditionWithOverflowCount( bitSegment5.truncatedValue >> 32, bitSegment4.truncatedValue, bitSegment3.truncatedValue << 32, upperLowerBits.overflowCount) let upperUpperBits = UInt128._variadicAdditionWithOverflowCount( bitSegment3.truncatedValue >> 32, bitSegment1.truncatedValue, lowerUpperBits.overflowCount) // Bring the 64bit unsigned integer results together into a high and low 128bit unsigned integer result. return (high: UInt128(upperBits: upperUpperBits.truncatedValue, lowerBits: lowerUpperBits.truncatedValue), low: UInt128(upperBits: upperLowerBits.truncatedValue, lowerBits: lowerLowerBits.truncatedValue)) } /// Takes a variable amount of 64bit Unsigned Integers and adds them together, /// tracking the total amount of overflows that occurred during addition. /// /// - Parameter addends: /// Variably sized list of UInt64 values. /// - Returns: /// A tuple containing the truncated result and a count of the total /// amount of overflows that occurred during addition. private static func _variadicAdditionWithOverflowCount(_ addends: UInt64...) -> (truncatedValue: UInt64, overflowCount: UInt64) { var sum: UInt64 = 0 var overflowCount: UInt64 = 0 addends.forEach { addend in let interimSum = sum.addingReportingOverflow(addend) if interimSum.overflow { overflowCount += 1 } sum = interimSum.partialValue } return (truncatedValue: sum, overflowCount: overflowCount) } public func dividedReportingOverflow(by rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { guard rhs != 0 else { return (self, true) } let quotient = self.quotientAndRemainder(dividingBy: rhs).quotient return (quotient, false) } public func dividingFullWidth(_ dividend: (high: UInt128, low: UInt128)) -> (quotient: UInt128, remainder: UInt128) { return self._quotientAndRemainderFullWidth(dividingBy: dividend) } public func remainderReportingOverflow(dividingBy rhs: UInt128) -> (partialValue: UInt128, overflow: Bool) { guard rhs != 0 else { return (self, true) } let remainder = self.quotientAndRemainder(dividingBy: rhs).remainder return (remainder, false) } public func quotientAndRemainder(dividingBy rhs: UInt128) -> (quotient: UInt128, remainder: UInt128) { return rhs._quotientAndRemainderFullWidth(dividingBy: (high: 0, low: self)) } /// Provides the quotient and remainder when dividing the provided value by self. internal func _quotientAndRemainderFullWidth(dividingBy dividend: (high: UInt128, low: UInt128)) -> (quotient: UInt128, remainder: UInt128) { let divisor = self let numeratorBitsToWalk: UInt128 if dividend.high > 0 { numeratorBitsToWalk = dividend.high.significantBits + 128 - 1 } else if dividend.low == 0 { return (0, 0) } else { numeratorBitsToWalk = dividend.low.significantBits - 1 } // The below algorithm was adapted from: // https://en.wikipedia.org/wiki/Division_algorithm#Integer_division_.28unsigned.29_with_remainder precondition(self != 0, "Division by 0") var quotient = UInt128.min var remainder = UInt128.min for numeratorShiftWidth in (0...numeratorBitsToWalk).reversed() { remainder <<= 1 remainder |= UInt128._bitFromDoubleWidth(at: numeratorShiftWidth, for: dividend) if remainder >= divisor { remainder -= divisor quotient |= 1 << numeratorShiftWidth } } return (quotient, remainder) } /// Returns the bit stored at the given position for the provided double width UInt128 input. /// /// - parameter at: position to grab bit value from. /// - parameter for: the double width UInt128 data value to grab the /// bit from. /// - returns: single bit stored in a UInt128 value. internal static func _bitFromDoubleWidth(at bitPosition: UInt128, for input: (high: UInt128, low: UInt128)) -> UInt128 { switch bitPosition { case 0: return input.low & 1 case 1...127: return input.low >> bitPosition & 1 case 128: return input.high & 1 default: return input.high >> (bitPosition - 128) & 1 } } } // MARK: - BinaryInteger Conformance extension UInt128 { // MARK: Instance Properties public static var bitWidth : Int { return 128 } } extension UInt128 : BinaryInteger { // MARK: Instance Methods public var words: [UInt] { return Array(value.lowerBits.words) + Array(value.upperBits.words) } public var trailingZeroBitCount: Int { if value.lowerBits == 0 { return UInt64.bitWidth + value.upperBits.trailingZeroBitCount } return value.lowerBits.trailingZeroBitCount } // MARK: Initializers public init?(exactly source: T) { if source.isZero { self = UInt128() } else if source.exponent < 0 || source.rounded() != source { return nil } else { self = UInt128(UInt64(source)) } } public init(_ source: T) { self.init(UInt64(source)) } // MARK: Type Methods public static func /(lhs: UInt128, rhs: UInt128) -> UInt128 { let result = lhs.dividedReportingOverflow(by: rhs) return result.partialValue } public static func /=(lhs: inout UInt128, rhs: UInt128) { lhs = lhs / rhs } public static func %(lhs: UInt128, rhs: UInt128) -> UInt128 { let result = lhs.remainderReportingOverflow(dividingBy: rhs) return result.partialValue } public static func %=(lhs: inout UInt128, rhs: UInt128) { lhs = lhs % rhs } /// Performs a bitwise AND operation on 2 UInt128 data types. public static func &=(lhs: inout UInt128, rhs: UInt128) { let upperBits = lhs.value.upperBits & rhs.value.upperBits let lowerBits = lhs.value.lowerBits & rhs.value.lowerBits lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) } /// Performs a bitwise OR operation on 2 UInt128 data types. public static func |=(lhs: inout UInt128, rhs: UInt128) { let upperBits = lhs.value.upperBits | rhs.value.upperBits let lowerBits = lhs.value.lowerBits | rhs.value.lowerBits lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) } /// Performs a bitwise XOR operation on 2 UInt128 data types. public static func ^=(lhs: inout UInt128, rhs: UInt128) { let upperBits = lhs.value.upperBits ^ rhs.value.upperBits let lowerBits = lhs.value.lowerBits ^ rhs.value.lowerBits lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) } /// Perform a masked right SHIFT operation self. /// /// The masking operation will mask `rhs` against the highest /// shift value that will not cause an overflowing shift before /// performing the shift. IE: `rhs = 128` will become `rhs = 0` /// and `rhs = 129` will become `rhs = 1`. public static func &>>=(lhs: inout UInt128, rhs: UInt128) { let shiftWidth = rhs.value.lowerBits & 127 switch shiftWidth { case 0: return // Do nothing shift. case 1...63: let upperBits = lhs.value.upperBits >> shiftWidth let lowerBits = (lhs.value.lowerBits >> shiftWidth) + (lhs.value.upperBits << (64 - shiftWidth)) lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) case 64: // Shift 64 means move upper bits to lower bits. lhs = UInt128(upperBits: 0, lowerBits: lhs.value.upperBits) default: let lowerBits = lhs.value.upperBits >> (shiftWidth - 64) lhs = UInt128(upperBits: 0, lowerBits: lowerBits) } } /// Perform a masked left SHIFT operation on self. /// /// The masking operation will mask `rhs` against the highest /// shift value that will not cause an overflowing shift before /// performing the shift. IE: `rhs = 128` will become `rhs = 0` /// and `rhs = 129` will become `rhs = 1`. public static func &<<=(lhs: inout UInt128, rhs: UInt128) { let shiftWidth = rhs.value.lowerBits & 127 switch shiftWidth { case 0: return // Do nothing shift. case 1...63: let upperBits = (lhs.value.upperBits << shiftWidth) + (lhs.value.lowerBits >> (64 - shiftWidth)) let lowerBits = lhs.value.lowerBits << shiftWidth lhs = UInt128(upperBits: upperBits, lowerBits: lowerBits) case 64: // Shift 64 means move lower bits to upper bits. lhs = UInt128(upperBits: lhs.value.lowerBits, lowerBits: 0) default: let upperBits = lhs.value.lowerBits << (shiftWidth - 64) lhs = UInt128(upperBits: upperBits, lowerBits: 0) } } } // MARK: - UnsignedInteger Conformance extension UInt128 : UnsignedInteger {} // MARK: - Hashable Conformance extension UInt128 : Hashable { public var hashValue: Int { return self.value.lowerBits.hashValue ^ self.value.upperBits.hashValue } } // MARK: - Numeric Conformance extension UInt128 : Numeric { public static func +(lhs: UInt128, rhs: UInt128) -> UInt128 { precondition(~lhs >= rhs, "Addition overflow!") let result = lhs.addingReportingOverflow(rhs) return result.partialValue } public static func +=(lhs: inout UInt128, rhs: UInt128) { lhs = lhs + rhs } public static func -(lhs: UInt128, rhs: UInt128) -> UInt128 { precondition(lhs >= rhs, "Integer underflow") let result = lhs.subtractingReportingOverflow(rhs) return result.partialValue } public static func -=(lhs: inout UInt128, rhs: UInt128) { lhs = lhs - rhs } public static func *(lhs: UInt128, rhs: UInt128) -> UInt128 { let result = lhs.multipliedReportingOverflow(by: rhs) precondition(!result.overflow, "Multiplication overflow!") return result.partialValue } public static func *=(lhs: inout UInt128, rhs: UInt128) { lhs = lhs * rhs } } // MARK: - Equatable Conformance extension UInt128 : Equatable { /// Checks if the `lhs` is equal to the `rhs`. public static func ==(lhs: UInt128, rhs: UInt128) -> Bool { if lhs.value.lowerBits == rhs.value.lowerBits && lhs.value.upperBits == rhs.value.upperBits { return true } return false } } // MARK: - ExpressibleByIntegerLiteral Conformance extension UInt128 : ExpressibleByIntegerLiteral { public init(integerLiteral value: IntegerLiteralType) { self.init(upperBits: 0, lowerBits: UInt64(value)) } } // MARK: - CustomStringConvertible Conformance extension UInt128 : CustomStringConvertible { // MARK: Instance Properties public var description: String { return self._valueToString() } // MARK: Instance Methods /// Converts the stored value into a string representation. /// - parameter radix: /// The radix for the base numbering system you wish to have /// the type presented in. /// - parameter uppercase: /// Determines whether letter components of the outputted string will be in /// uppercase format or not. /// - returns: /// String representation of the stored UInt128 value. internal func _valueToString(radix: Int = 10, uppercase: Bool = true) -> String { precondition((2...36) ~= radix, "radix must be within the range of 2-36.") // Simple case. if self == 0 { return "0" } // Will store the final string result. var result = String() // Used as the check for indexing through UInt128 for string interpolation. var divmodResult = (quotient: self, remainder: UInt128(0)) // Will hold the pool of possible values. let characterPool = uppercase ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" : "0123456789abcdefghijklmnopqrstuvwxyz" // Go through internal value until every base position is string(ed). repeat { divmodResult = divmodResult.quotient.quotientAndRemainder(dividingBy: UInt128(radix)) let index = characterPool.index(characterPool.startIndex, offsetBy: Int(divmodResult.remainder)) result.insert(characterPool[index], at: result.startIndex) } while divmodResult.quotient > 0 return result } } // MARK: - CustomDebugStringConvertible Conformance extension UInt128 : CustomDebugStringConvertible { public var debugDescription: String { return self.description } } // MARK: - Comparable Conformance extension UInt128 : Comparable { public static func <(lhs: UInt128, rhs: UInt128) -> Bool { if lhs.value.upperBits < rhs.value.upperBits { return true } else if lhs.value.upperBits == rhs.value.upperBits && lhs.value.lowerBits < rhs.value.lowerBits { return true } return false } } // MARK: - ExpressibleByStringLiteral Conformance extension UInt128 : ExpressibleByStringLiteral { // MARK: Initializers public init(stringLiteral value: StringLiteralType) { self.init() if let result = UInt128._valueFromString(value) { self = result } } // MARK: Type Methods internal static func _valueFromString(_ value: String) -> UInt128? { let radix = UInt128._determineRadixFromString(value) let inputString = radix == 10 ? value : String(value.dropFirst(2)) return UInt128(inputString, radix: radix) } internal static func _determineRadixFromString(_ string: String) -> Int { switch string.prefix(2) { case "0b": return 2 case "0o": return 8 case "0x": return 16 default: return 10 } } } // MARK: - Codable Conformance extension UInt128 : Codable { private enum CodingKeys : String, CodingKey { case upperBits = "upperBits", lowerBits = "lowerBits" } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let upperBits = try container.decode(UInt64.self, forKey: .upperBits) let lowerBits = try container.decode(UInt64.self, forKey: .lowerBits) self.init(upperBits: upperBits, lowerBits: lowerBits) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(value.upperBits, forKey: .upperBits) try container.encode(value.lowerBits, forKey: .lowerBits) } } // MARK: - Deprecated API extension UInt128 { /// Initialize a UInt128 value from a string. /// /// - parameter source: the string that will be converted into a /// UInt128 value. Defaults to being analyzed as a base10 number, /// but can be prefixed with `0b` for base2, `0o` for base8 /// or `0x` for base16. @available(swift, deprecated: 3.2, renamed: "init(_:)") public static func fromUnparsedString(_ source: String) throws -> UInt128 { return try UInt128(source) } } // MARK: - BinaryFloatingPoint Interworking extension BinaryFloatingPoint { public init(_ value: UInt128) { precondition(value.value.upperBits == 0, "Value is too large to fit into a BinaryFloatingPoint until a 128bit BinaryFloatingPoint type is defined.") self.init(value.value.lowerBits) } public init?(exactly value: UInt128) { if value.value.upperBits > 0 { return nil } self = Self(value.value.lowerBits) } } // MARK: - String Interworking extension String { /// Creates a string representing the given value in base 10, or some other /// specified base. /// /// - Parameters: /// - value: The UInt128 value to convert to a string. /// - radix: The base to use for the string representation. `radix` must be /// at least 2 and at most 36. The default is 10. /// - uppercase: Pass `true` to use uppercase letters to represent numerals /// or `false` to use lowercase letters. The default is `false`. public init(_ value: UInt128, radix: Int = 10, uppercase: Bool = false) { self = value._valueToString(radix: radix, uppercase: uppercase) } }