802 lines
29 KiB
Swift
Executable File
802 lines
29 KiB
Swift
Executable File
//
|
|
// 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)
|
|
}
|
|
}
|