src/Utils/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 {
var significantBits: UInt128 = 0
var bitsToWalk: UInt64 = 0 // The bits to crawl in loop.
// When upperBits > 0, lowerBits are all significant.
if self.value.upperBits > 0 {
bitsToWalk = self.value.upperBits
significantBits = 64
} else if self.value.lowerBits > 0 {
bitsToWalk = self.value.lowerBits
}
// Walk significant bits by shifting right until all bits are equal to 0.
while bitsToWalk > 0 {
bitsToWalk >>= 1
significantBits += 1
}
return significantBits
}
/// 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 {
public static var bitWidth : Int { return 128 }
// MARK: Instance Properties
public var nonzeroBitCount: Int {
var nonZeroCount = 0
var shiftWidth = 0
while shiftWidth < 128 {
let shiftedSelf = self &>> shiftWidth
let currentBit = shiftedSelf & 1
if currentBit == 1 {
nonZeroCount += 1
}
shiftWidth += 1
}
return nonZeroCount
}
public var leadingZeroBitCount: Int {
var zeroCount = 0
var shiftWidth = 127
while shiftWidth >= 0 {
let currentBit = self &>> shiftWidth
guard currentBit == 0 else { break }
zeroCount += 1
shiftWidth -= 1
}
return zeroCount
}
/// 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) {
#if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64)
self = value.byteSwapped
#else
self = value
#endif
}
/// Creates an integer from its little-endian representation, changing the
/// byte order if necessary.
public init(littleEndian value: UInt128) {
#if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64)
self = value
#else
self = value.byteSwapped
#endif
}
// 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]].init(
repeating: [UInt64].init(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 : BinaryInteger {
// MARK: Instance Properties
public var bitWidth : Int { return 128 }
// MARK: Instance Methods
public var words: [UInt] {
guard self != UInt128.min else {
return []
}
var words: [UInt] = []
for currentWord in 0 ... self.bitWidth / UInt.bitWidth {
let shiftAmount: UInt64 = UInt64(UInt.bitWidth) * UInt64(currentWord)
let mask = UInt64(UInt.max)
var shifted = self
if shiftAmount > 0 {
shifted &>>= UInt128(upperBits: 0, lowerBits: shiftAmount)
}
let masked: UInt128 = shifted & UInt128(upperBits: 0, lowerBits: mask)
words.append(UInt(masked.value.lowerBits))
}
return words
}
public var trailingZeroBitCount: Int {
let mask: UInt128 = 1
var bitsToWalk = self
for currentPosition in 0...128 {
if bitsToWalk & mask == 1 {
return currentPosition
}
bitsToWalk >>= 1
}
return 128
}
// MARK: Initializers
public init?<T : BinaryFloatingPoint>(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<T : BinaryFloatingPoint>(_ 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 func hash(into hasher: inout Hasher) {
hasher.combine(self.value.lowerBits)
hasher.combine(self.value.upperBits)
}
}
// 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(radix > 1 && radix < 37, "radix must be within the range of 2-36.")
// Will store the final string result.
var result = String()
// Simple case.
if self == 0 {
result.append("0")
return result
}
// 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 {
let radix: Int
if string.hasPrefix("0b") { radix = 2 }
else if string.hasPrefix("0o") { radix = 8 }
else if string.hasPrefix("0x") { radix = 16 }
else { radix = 10 }
return radix
}
}
// 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.init(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)
}
}