Created
December 4, 2017 21:00
-
-
Save zackshapiro/848a2c1efeda9a34b89b8b5648a7c24c to your computer and use it in GitHub Desktop.
An implementation of UInt256 based off of https://github.com/Jitsusama/UInt128
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// UInt256.swift | |
// Raiblocks | |
// | |
// Created by Zack Shapiro on 12/4/17. | |
// Copyright © 2017 Zack Shapiro. All rights reserved. | |
// | |
// MARK: Error Type | |
/// An `ErrorType` for `UInt128` data types. It includes cases | |
/// for errors that can occur during string | |
/// conversion. | |
public enum UInt256Errors : Error { | |
/// Input cannot be converted to a UInt128 value. | |
case invalidString | |
} | |
public struct UInt256 { | |
// MARK: Instance Properties | |
/// Internal value is presented as a tuple of 2 64-bit | |
/// unsigned integers. | |
internal var value: (upperBits: UInt128, lowerBits: UInt128) | |
/// Counts up the significant bits in stored data. | |
public var significantBits: UInt256 { | |
var significantBits: UInt256 = 0 | |
var bitsToWalk: UInt128 = 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 255 - leadingZeroBitCount | |
} | |
// MARK: Initializers | |
/// Designated initializer for the UInt128 type. | |
public init(upperBits: UInt128, lowerBits: UInt128) { | |
value.upperBits = upperBits | |
value.lowerBits = lowerBits | |
} | |
public init() { | |
self.init(upperBits: 0, lowerBits: 0) | |
} | |
public init(_ source: UInt256) { | |
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 = UInt256._valueFromString(source) else { | |
throw UInt256Errors.invalidString | |
} | |
self = result | |
} | |
} | |
// MARK: - FixedWidthInteger Conformance | |
extension UInt256 : FixedWidthInteger { | |
// MARK: Instance Properties | |
public var nonzeroBitCount: Int { | |
return value.lowerBits.nonzeroBitCount + value.upperBits.nonzeroBitCount | |
} | |
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: UInt256 { | |
#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: UInt256 { | |
#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: UInt256 { | |
return UInt256(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: UInt128(bits)) | |
} | |
/// Creates an integer from its big-endian representation, changing the | |
/// byte order if necessary. | |
public init(bigEndian value: UInt256) { | |
#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: UInt256) { | |
#if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64) | |
self = value | |
#else | |
self = value.byteSwapped | |
#endif | |
} | |
// MARK: Instance Methods | |
public func addingReportingOverflow(_ rhs: UInt256) -> (partialValue: UInt256, 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: UInt256(upperBits: upperBits, lowerBits: lowerBits), | |
overflow: upperOverflow || resultOverflow) | |
} | |
public func subtractingReportingOverflow(_ rhs: UInt256) -> (partialValue: UInt256, 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: UInt256(upperBits: upperBits, lowerBits: lowerBits), | |
overflow: upperOverflow || resultOverflow) | |
} | |
public func multipliedReportingOverflow(by rhs: UInt256) -> (partialValue: UInt256, overflow: Bool) { | |
let multiplicationResult = self.multipliedFullWidth(by: rhs) | |
let overflowEncountered = multiplicationResult.high > 0 | |
return (partialValue: multiplicationResult.low, | |
overflow: overflowEncountered) | |
} | |
public func multipliedFullWidth(by other: UInt256) -> (high: UInt256, low: UInt256.Magnitude) { | |
// Bit mask that facilitates masking the lower 64 bits of a 128 bit UInt. | |
let lower64 = UInt128(UInt64.max) | |
// Decompose lhs into an array of 4, 32 significant bit UInt64s. | |
let lhsArray = [ | |
self.value.upperBits >> 64, /*0*/ self.value.upperBits & lower64, /*1*/ | |
self.value.lowerBits >> 64, /*2*/ self.value.lowerBits & lower64 /*3*/ | |
] | |
// Decompose rhs into an array of 4, 32 significant bit UInt64s. | |
let rhsArray = [ | |
other.value.upperBits >> 64, /*0*/ other.value.upperBits & lower64, /*1*/ | |
other.value.lowerBits >> 64, /*2*/ other.value.lowerBits & lower64 /*3*/ | |
] | |
// The future contents of this array will be used to store segment | |
// multiplication results. | |
// ZACK: does this need to be different? | |
var resultArray = [[UInt128]].init( | |
repeating: [UInt128].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] & lower64 | |
let bitSegment7 = UInt256._variadicAdditionWithOverflowCount( | |
resultArray[2][3] & lower64, | |
resultArray[3][2] & lower64, | |
resultArray[3][3] >> 32) // overflow from bitSegment8 | |
let bitSegment6 = UInt256._variadicAdditionWithOverflowCount( | |
resultArray[1][3] & lower64, | |
resultArray[2][2] & lower64, | |
resultArray[3][1] & lower64, | |
resultArray[2][3] >> 32, // overflow from bitSegment7 | |
resultArray[3][2] >> 32, // overflow from bitSegment7 | |
bitSegment7.overflowCount) | |
let bitSegment5 = UInt256._variadicAdditionWithOverflowCount( | |
resultArray[0][3] & lower64, | |
resultArray[1][2] & lower64, | |
resultArray[2][1] & lower64, | |
resultArray[3][0] & lower64, | |
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 = UInt256._variadicAdditionWithOverflowCount( | |
resultArray[0][2] & lower64, | |
resultArray[1][1] & lower64, | |
resultArray[2][0] & lower64, | |
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 = UInt256._variadicAdditionWithOverflowCount( | |
resultArray[0][1] & lower64, | |
resultArray[1][0] & lower64, | |
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 = UInt256._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 = UInt256._variadicAdditionWithOverflowCount( | |
bitSegment8, | |
bitSegment7.truncatedValue << 64) | |
let upperLowerBits = UInt256._variadicAdditionWithOverflowCount( | |
bitSegment7.truncatedValue >> 64, | |
bitSegment6.truncatedValue, | |
bitSegment5.truncatedValue << 64, | |
lowerLowerBits.overflowCount) | |
let lowerUpperBits = UInt256._variadicAdditionWithOverflowCount( | |
bitSegment5.truncatedValue >> 64, | |
bitSegment4.truncatedValue, | |
bitSegment3.truncatedValue << 64, | |
upperLowerBits.overflowCount) | |
let upperUpperBits = UInt256._variadicAdditionWithOverflowCount( | |
bitSegment3.truncatedValue >> 64, | |
bitSegment1.truncatedValue, | |
lowerUpperBits.overflowCount) | |
// Bring the 64bit unsigned integer results together into a high and low 128bit unsigned integer result. | |
return (high: UInt256(upperBits: upperUpperBits.truncatedValue, lowerBits: lowerUpperBits.truncatedValue), | |
low: UInt256(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: UInt128...) -> (truncatedValue: UInt128, overflowCount: UInt128) { | |
var sum: UInt128 = 0 | |
var overflowCount: UInt128 = 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: UInt256) -> (partialValue: UInt256, overflow: Bool) { | |
guard rhs != 0 else { | |
return (self, true) | |
} | |
let quotient = self.quotientAndRemainder(dividingBy: rhs).quotient | |
return (quotient, false) | |
} | |
public func dividingFullWidth(_ dividend: (high: UInt256, low: UInt256)) -> (quotient: UInt256, remainder: UInt256) { | |
return self._quotientAndRemainderFullWidth(dividingBy: dividend) | |
} | |
public func remainderReportingOverflow(dividingBy rhs: UInt256) -> (partialValue: UInt256, overflow: Bool) { | |
guard rhs != 0 else { | |
return (self, true) | |
} | |
let remainder = self.quotientAndRemainder(dividingBy: rhs).remainder | |
return (remainder, false) | |
} | |
public func quotientAndRemainder(dividingBy rhs: UInt256) -> (quotient: UInt256, remainder: UInt256) { | |
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: UInt256, low: UInt256)) -> (quotient: UInt256, remainder: UInt256) { | |
let divisor = self | |
let numeratorBitsToWalk: UInt256 | |
if dividend.high > 0 { | |
numeratorBitsToWalk = dividend.high.significantBits + 256 - 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 = UInt256.min | |
var remainder = UInt256.min | |
for numeratorShiftWidth in (0...numeratorBitsToWalk).reversed() { | |
remainder <<= 1 | |
remainder |= UInt256._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: UInt256, for input: (high: UInt256, low: UInt256)) -> UInt256 { | |
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 UInt256 : BinaryInteger { | |
// MARK: Instance Properties | |
public static var bitWidth : Int { return 256 } | |
// MARK: Instance Methods | |
public var words: [UInt] { | |
guard self != UInt256.min else { | |
return [] | |
} | |
var words: [UInt] = [] | |
for currentWord in 0 ... self.bitWidth / UInt.bitWidth { | |
let shiftAmount: UInt128 = UInt128(UInt.bitWidth) * UInt128(currentWord) | |
let mask = UInt128(UInt.max) | |
var shifted = self | |
if shiftAmount > 0 { | |
shifted &>>= UInt256(upperBits: 0, lowerBits: shiftAmount) | |
} | |
let masked: UInt256 = shifted & UInt256(upperBits: 0, lowerBits: mask) | |
words.append(UInt(masked.value.lowerBits)) | |
} | |
return words | |
} | |
public var trailingZeroBitCount: Int { | |
let mask: UInt256 = 1 | |
var bitsToWalk = self | |
for currentPosition in 0...256 { | |
if bitsToWalk & mask == 1 { | |
return currentPosition | |
} | |
bitsToWalk >>= 1 | |
} | |
return 256 | |
} | |
// MARK: Initializers | |
public init?<T : BinaryFloatingPoint>(exactly source: T) { | |
if source.isZero { | |
self = UInt256() | |
} | |
else if source.exponent < 0 || source.rounded() != source { | |
return nil | |
} | |
else { | |
self = UInt256(UInt128(source)) | |
} | |
} | |
public init<T : BinaryFloatingPoint>(_ source: T) { | |
self.init(UInt128(source)) | |
} | |
// MARK: Type Methods | |
public static func /(_ lhs: UInt256, _ rhs: UInt256) -> UInt256 { | |
let result = lhs.dividedReportingOverflow(by: rhs) | |
return result.partialValue | |
} | |
public static func /=(_ lhs: inout UInt256, _ rhs: UInt256) { | |
lhs = lhs / rhs | |
} | |
public static func %(_ lhs: UInt256, _ rhs: UInt256) -> UInt256 { | |
let result = lhs.remainderReportingOverflow(dividingBy: rhs) | |
return result.partialValue | |
} | |
public static func %=(_ lhs: inout UInt256, _ rhs: UInt256) { | |
lhs = lhs % rhs | |
} | |
/// Performs a bitwise AND operation on 2 UInt128 data types. | |
public static func &=(_ lhs: inout UInt256, _ rhs: UInt256) { | |
let upperBits = lhs.value.upperBits & rhs.value.upperBits | |
let lowerBits = lhs.value.lowerBits & rhs.value.lowerBits | |
lhs = UInt256(upperBits: upperBits, lowerBits: lowerBits) | |
} | |
/// Performs a bitwise OR operation on 2 UInt128 data types. | |
public static func |=(_ lhs: inout UInt256, _ rhs: UInt256) { | |
let upperBits = lhs.value.upperBits | rhs.value.upperBits | |
let lowerBits = lhs.value.lowerBits | rhs.value.lowerBits | |
lhs = UInt256(upperBits: upperBits, lowerBits: lowerBits) | |
} | |
/// Performs a bitwise XOR operation on 2 UInt128 data types. | |
public static func ^=(_ lhs: inout UInt256, _ rhs: UInt256) { | |
let upperBits = lhs.value.upperBits ^ rhs.value.upperBits | |
let lowerBits = lhs.value.lowerBits ^ rhs.value.lowerBits | |
lhs = UInt256(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 UInt256, _ rhs: UInt256) { | |
let shiftWidth = rhs.value.lowerBits & 255 | |
switch shiftWidth { | |
case 0: return // Do nothing shift. | |
case 1...127: | |
let upperBits = lhs.value.upperBits >> shiftWidth | |
let lowerBits = (lhs.value.lowerBits >> shiftWidth) + (lhs.value.upperBits << (128 - shiftWidth)) | |
lhs = UInt256(upperBits: upperBits, lowerBits: lowerBits) | |
case 128: | |
// Shift 64 means move upper bits to lower bits. | |
lhs = UInt256(upperBits: 0, lowerBits: lhs.value.upperBits) | |
default: | |
let lowerBits = lhs.value.upperBits >> (shiftWidth - 128) | |
lhs = UInt256(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 UInt256, _ rhs: UInt256) { | |
let shiftWidth = rhs.value.lowerBits & 255 | |
switch shiftWidth { | |
case 0: return // Do nothing shift. | |
case 1...127: | |
let upperBits = (lhs.value.upperBits << shiftWidth) + (lhs.value.lowerBits >> (128 - shiftWidth)) | |
let lowerBits = lhs.value.lowerBits << shiftWidth | |
lhs = UInt256(upperBits: upperBits, lowerBits: lowerBits) | |
case 128: | |
// Shift 64 means move lower bits to upper bits. | |
lhs = UInt256(upperBits: lhs.value.lowerBits, lowerBits: 0) | |
default: | |
let upperBits = lhs.value.lowerBits << (shiftWidth - 128) | |
lhs = UInt256(upperBits: upperBits, lowerBits: 0) | |
} | |
} | |
} | |
// MARK: - UnsignedInteger Conformance | |
extension UInt256 : UnsignedInteger {} | |
// MARK: - Hashable Conformance | |
extension UInt256 : Hashable { | |
public var hashValue: Int { | |
return self.value.lowerBits.hashValue ^ self.value.upperBits.hashValue | |
} | |
} | |
// MARK: - Numeric Conformance | |
extension UInt256 : Numeric { | |
public static func +(_ lhs: UInt256, _ rhs: UInt256) -> UInt256 { | |
precondition(~lhs >= rhs, "Addition overflow!") | |
let result = lhs.addingReportingOverflow(rhs) | |
return result.partialValue | |
} | |
public static func +=(_ lhs: inout UInt256, _ rhs: UInt256) { | |
lhs = lhs + rhs | |
} | |
public static func -(_ lhs: UInt256, _ rhs: UInt256) -> UInt256 { | |
precondition(lhs >= rhs, "Integer underflow") | |
let result = lhs.subtractingReportingOverflow(rhs) | |
return result.partialValue | |
} | |
public static func -=(_ lhs: inout UInt256, _ rhs: UInt256) { | |
lhs = lhs - rhs | |
} | |
public static func *(_ lhs: UInt256, _ rhs: UInt256) -> UInt256 { | |
let result = lhs.multipliedReportingOverflow(by: rhs) | |
precondition(!result.overflow, "Multiplication overflow!") | |
return result.partialValue | |
} | |
public static func *=(_ lhs: inout UInt256, _ rhs: UInt256) { | |
lhs = lhs * rhs | |
} | |
} | |
// MARK: - ExpressibleByIntegerLiteral Conformance | |
extension UInt256 : ExpressibleByIntegerLiteral { | |
public init(integerLiteral value: IntegerLiteralType) { | |
self.init(upperBits: 0, lowerBits: UInt128(value)) | |
} | |
} | |
// MARK: - CustomStringConvertible Conformance | |
extension UInt256 : 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 { | |
//ZACK: does this need to change for UInt256? | |
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: UInt256(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: UInt256(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 UInt256 : CustomDebugStringConvertible { | |
public var debugDescription: String { | |
return self.description | |
} | |
} | |
// MARK: - Comparable Conformance | |
extension UInt256 : Comparable { | |
public static func <(lhs: UInt256, rhs: UInt256) -> 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 UInt256 : ExpressibleByStringLiteral { | |
// MARK: Initializers | |
public init(stringLiteral value: StringLiteralType) { | |
self.init() | |
if let result = UInt256._valueFromString(value) { | |
self = result | |
} | |
} | |
// MARK: Type Methods | |
internal static func _valueFromString(_ value: String) -> UInt256? { | |
let radix = UInt256._determineRadixFromString(value) | |
let inputString = radix == 10 ? value : String(value.dropFirst(2)) | |
return UInt256(inputString, radix: radix) | |
} | |
// ZACK: do these need to change for 256 (copied from 128) | |
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 UInt256 { | |
/// 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 -> UInt256 { | |
return try UInt256.init(source) | |
} | |
} | |
// MARK: - BinaryFloatingPoint Interworking | |
extension BinaryFloatingPoint { | |
public init(_ value: UInt256) { | |
precondition(value.value.upperBits == 0, "Value is too large to fit into a BinaryFloatingPoint until a 256bit BinaryFloatingPoint type is defined.") | |
self.init(value.value.lowerBits) | |
} | |
public init?(exactly value: UInt256) { | |
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 UInt256 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: UInt256, radix: Int = 10, uppercase: Bool = false) { | |
self = value._valueToString(radix: radix, uppercase: uppercase) | |
} | |
} |
This line hits
https://gist.github.com/zackshapiro/848a2c1efeda9a34b89b8b5648a7c24c#file-uint256-swift-L572
When I do a simple
let x: UInt256 = 500
print(x)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Do these lines need to be changed at all for UInt256?