Skip to content

Instantly share code, notes, and snippets.

@CTMacUser
Last active February 14, 2018 20:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CTMacUser/df852f3e4533c7a68376890dc744ebcb to your computer and use it in GitHub Desktop.
Save CTMacUser/df852f3e4533c7a68376890dc744ebcb to your computer and use it in GitHub Desktop.
(See "UInt72.swift.") An unsigned integer type larger than the one from the Standard Library.
/*
BinaryInteger+Extensions.swift
Created by Daryle Walker on 1/29/18.
Copyright (c) 2018 Daryle Walker.
Distributed under the MIT License.
Various properties and methods for BitArray's bit-twiddling.
*/
extension BinaryInteger {
/**
Returns a mask with only the given number of least-significant bits set.
- Precondition: `count >= 0`. `count` isn't so large that the result isn't representable.
- Parameter count: The number of low-order bits to set to `1`. All other bits are `0`.
- Returns: One less than two to the `count` power.
*/
static func lowOrderBitsMask(count: Int) -> Self {
precondition(count >= 0)
guard count > 0 else { return 0 }
var mask: Self = 1
mask <<= count - 1
mask |= mask - 1
return mask
}
/**
Assigns the given source to the receiver, but only at the masked bit locations.
- Parameter source: The new value(s) to assign to `self`'s bits.
- Parameter mask: Which bits of `source` get assigned to `self`. Only the bit positions set in `mask` have those corresponding bits in `self` get affected by `source`.
- Returns: The previous values of the bits of `self` targeted by `mask`. The untargeted bits are 0.
- Postcondition:
- `self & mask == source & mask`.
- `self & ~mask == oldSelf & ~mask`.
*/
@discardableResult
mutating func replaceBits(with source: Self, forOnly mask: Self) -> Self {
defer {
self &= ~mask
self |= source & mask
}
return self & mask
}
}
/*
FixedWidthInteger+Extensions.swift
Created by Daryle Walker on 1/29/18.
Copyright (c) 2018 Daryle Walker.
Distributed under the MIT License.
Various properties and methods for BitArray's bit-twiddling.
*/
// MARK: Extensions for Unsigned Fixed-Width Integers
extension FixedWidthInteger where Self: UnsignedInteger {
// MARK: Bit Reversal
/**
Returns this value except its bits are in reverse order.
Taken from <https://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious>, the "Reverse bits the obvious way" section of "Bit Twiddling Hacks" by Sean Eron Anderson.
- Returns: A bit-reversed rendition of `self`.
*/
func bitReversed() -> Self {
var result: Self = 0
var mask: Self = 1
while mask != 0 {
defer { mask <<= 1 }
result <<= 1
if self & mask != 0 {
result |= 1
}
}
return result
}
/**
Reverses the order of the stored bits.
- Postcondition: The most- and least-significant bits swap values, the second-most- and second-least-significant bits swap values, *etc.*
*/
mutating func bitReverse() {
self = bitReversed()
}
// MARK: Hexadecimal Output
/// The minimum count of base-16 digits needed to fully display any value of this type.
static var hexadecimalDigitCount: Int {
let (bq, br) = bitWidth.quotientAndRemainder(dividingBy: 4)
return bq + br.signum()
}
/// This value in hexadecimal, using its full width.
var fullHexadecimalString: String {
return String(self, radix: 16, uppercase: true).paddingPrepended("0", totalCount: Self.hexadecimalDigitCount)
}
// MARK: Multi-Precision Arithmetic
/**
Removes any zero-valued elements from the end of the given collection.
Useful when normalizing a collection that represents an arbitrary-length unsigned integer, where the higher-order digits are towards the end of a collection.
- Parameter c: The collection to trim.
- Postcondition: `c` doesn't lead with a run of zero-valued digits, but still represents the same numeric value.
*/
static func trimLeadingZeros<C: BidirectionalCollection & RangeReplaceableCollection>(_ c: inout C) where C.Element == Self {
while let lastDigit = c.last, lastDigit == 0 {
c.removeLast()
}
}
/**
Adds two arbitrary-length unsigned integers, represented by the given collections.
The addends and sum use radix `Self.max + 1` and arrange their digits starting from the low-order digits upwards.
- Parameter augend: The first operand.
- Parameter addend: The second operand.
- Returns: The sum.
*/
static func addMultiPrecisely<C1: Collection, C2: Collection>(augend: C1, addend: C2) -> [Self] where C1.Element == Self, C2.Element == Self {
var previousCarry = false
var sum = [Self]()
var agIndex = augend.startIndex, adIndex = addend.startIndex
while agIndex != augend.endIndex, adIndex != addend.endIndex {
let (baseSum, firstCarry) = augend[agIndex].addingReportingOverflow(addend[adIndex])
let (digitSum, secondCarry) = baseSum.addingReportingOverflow(previousCarry ? 1 : 0)
sum.append(digitSum)
previousCarry = firstCarry || secondCarry
augend.formIndex(after: &agIndex)
addend.formIndex(after: &adIndex)
}
while agIndex != augend.endIndex {
let (digitSum, newCarry) = augend[agIndex].addingReportingOverflow(previousCarry ? 1 : 0)
sum.append(digitSum)
previousCarry = newCarry
augend.formIndex(after: &agIndex)
}
while adIndex != addend.endIndex {
let (digitSum, newCarry) = addend[adIndex].addingReportingOverflow(previousCarry ? 1 : 0)
sum.append(digitSum)
previousCarry = newCarry
addend.formIndex(after: &adIndex)
}
if previousCarry {
sum.append(1)
}
trimLeadingZeros(&sum)
return sum
}
/**
Subtracts two arbitrary-length unsigned integers, represented by the given collections.
The operands and result use radix `Self.max + 1` and arrange their digits starting from the lowest-order upwards.
- Parameter minuend: The first operand.
- Parameter subtrahend: The second operand.
- Returns: If `minuend >= subtrahend`, the difference. Otherwise, `nil`.
*/
static func subtractMultiPrecisely<C1: Collection, C2: Collection>(minuend: C1, subtrahend: C2) -> [Self]? where C1.Element == Self, C2.Element == Self {
var previousBorrow = false
var difference = [Self]()
var mdIndex = minuend.startIndex, sdIndex = subtrahend.startIndex
while mdIndex != minuend.endIndex, sdIndex != subtrahend.endIndex {
let (baseDifference, firstBorrow) = minuend[mdIndex].subtractingReportingOverflow(subtrahend[sdIndex])
let (digitDifference, secondBorrow) = baseDifference.subtractingReportingOverflow(previousBorrow ? 1 : 0)
difference.append(digitDifference)
previousBorrow = firstBorrow || secondBorrow
minuend.formIndex(after: &mdIndex)
subtrahend.formIndex(after: &sdIndex)
}
while mdIndex != minuend.endIndex {
let (digitDifference, newBorrow) = minuend[mdIndex].subtractingReportingOverflow(previousBorrow ? 1 : 0)
difference.append(digitDifference)
previousBorrow = newBorrow
minuend.formIndex(after: &mdIndex)
}
while sdIndex != subtrahend.endIndex {
let (baseDifference, firstBorrow) = (0 as Self).subtractingReportingOverflow(subtrahend[sdIndex])
let (digitDifference, secondBorrow) = baseDifference.subtractingReportingOverflow(previousBorrow ? 1 : 0)
difference.append(digitDifference)
previousBorrow = firstBorrow || secondBorrow
subtrahend.formIndex(after: &sdIndex)
}
guard !previousBorrow else { return nil }
trimLeadingZeros(&difference)
return difference
}
/**
Multiplies an arbitrary-length unsigned integer, represented by the given sequence, by the given digit.
The operands and result use radix `Self.max + 1`. The first operand and result arrange their digits from the lowest-order upwards.
- Parameter multiplicand: The first operand.
- Parameter multiplier: The second operand.
- Returns: The product.
*/
static func shortMultiplyMultiPrecisely<S: Sequence>(multiplicand: S, multiplier: Self) -> [Self] where S.Element == Self {
var product = [Self]()
var carry: Self = 0
for md in multiplicand {
let (upperDigit, lowerDigit) = md.multipliedFullWidth(by: multiplier)
let (productDigitLow, haveCarry) = (lowerDigit as! Self).addingReportingOverflow(carry)
let productDigitHigh = upperDigit + (haveCarry ? 1 : 0)
product.append(productDigitLow)
carry = productDigitHigh
}
product.append(carry)
trimLeadingZeros(&product)
return product
}
/**
Multiplies two arbitrary-length unsigned integers, represented by the given sequences.
The operands and result use radix `Self.max + 1` and arrange their digits starting from the lowest-order upwards.
- Parameter multiplicand: The first operand.
- Parameter multiplier: The second operand.
- Returns: The product.
*/
static func multiplyMultiPrecisely<C: Collection, S: Sequence>(multiplicand: C, multiplier: S) -> [Self] where C.Element == Self, S.Element == Self {
var multiplierDigitsUsed = 0
var product = [Self]()
for mr in multiplier {
let partialProduct = shortMultiplyMultiPrecisely(multiplicand: multiplicand, multiplier: mr)
product = addMultiPrecisely(augend: product, addend: partialProduct + repeatElement(0 as Self, count: multiplierDigitsUsed))
multiplierDigitsUsed += 1
}
trimLeadingZeros(&product)
return product
}
/**
Divides an arbitrary-length unsigned integer, represented by the given sequence, by the given digit.
The operands and results use radix `Self.max + 1`. The first operand and quotient arrange their digits from the lowest-order upwards.
- Parameter dividend: The first operand.
- Parameter divisor: The second operand.
- Returns: If `divisor != 0`, the `quotient` and `remainder`. Otherwise, `nil`.
*/
static func shortDivideMultiPrecisely<S: Sequence>(dividend: S, divisor: Self) -> (quotient: [Self], remainder: Self)? where S.Element == Self {
guard divisor != 0 else { return nil }
var q = [Self]()
var r = 0 as Self
for d in dividend.reversed() {
let (qq, rr) = divisor.dividingFullWidth((r, d.magnitude))
q.insert(qq, at: q.startIndex)
r = rr
}
trimLeadingZeros(&q)
return (quotient: q, remainder: r)
}
/**
Divides two arbitrary-length unsigned integers, represented by the given sequences.
The operands and results use radix `Self.max + 1` and arrange their digits starting from the lowest-order upwards.
- Parameter dividend: The first operand.
- Parameter divisor: The second operand.
- Returns: If `divisor != 0`, the `quotient` and `remainder`. Otherwise, `nil`.
*/
static func divideMultiPrecisely<S1: Sequence, S2: Sequence>(dividend: S1, divisor: S2) -> (quotient: [Self], remainder: [Self])? where S1.Element == Self, S2.Element == Self {
// Find high-order divisor digit, to normalize.
var adjustedDivisor = Array(divisor)
trimLeadingZeros(&adjustedDivisor)
guard !adjustedDivisor.isEmpty else { return nil } // zero divisor
guard adjustedDivisor.count > 1 else {
// Short division
return shortDivideMultiPrecisely(dividend: dividend, divisor: adjustedDivisor.first!).map {
return (quotient: $0.quotient, remainder: [$0.remainder])
}
}
// Normalize.
/// Calculates `(Self.max + 1).quotientAndRemainder(dividingBy:)`, noting that the receiver isn't representable.
func quotientAndRemainderOnOnePastMax(dividingBy divisor: Self) -> (quotient: Self, remainder: Self) {
var result = Self.max.quotientAndRemainder(dividingBy: divisor)
result.remainder += 1
if result.remainder == divisor {
result.quotient += 1
result.remainder = 0
}
return result
}
let normalizer = quotientAndRemainderOnOnePastMax(dividingBy: adjustedDivisor.last!).quotient
adjustedDivisor = shortMultiplyMultiPrecisely(multiplicand: adjustedDivisor, multiplier: normalizer)
assert(adjustedDivisor.last!.addingReportingOverflow(adjustedDivisor.last!).overflow)
let adjustedDividend = shortMultiplyMultiPrecisely(multiplicand: dividend, multiplier: normalizer)
// Divide.
var q = [Self](), r = [Self]()
for dividendDigit in adjustedDividend.reversed() {
r.insert(dividendDigit, at: r.startIndex)
trimLeadingZeros(&r)
switch r.count {
case ..<adjustedDivisor.count:
// Trial-dividend < divisor -> fits zero times.
q.insert(0, at: q.startIndex)
case adjustedDivisor.count:
if r.lexicographicallyPrecedes(adjustedDivisor) {
// Trial-dividend < divisor -> fits zero times.
q.insert(0, at: q.startIndex)
} else {
// Else trial-dividend >= divisor -> fits once.
q.insert(1, at: q.startIndex)
r = subtractMultiPrecisely(minuend: r, subtrahend: adjustedDivisor)!
// Due to normalization, fitting more than once shouldn't happen.
assert(subtractMultiPrecisely(minuend: r, subtrahend: adjustedDivisor) == nil)
}
case adjustedDivisor.count + 1:
// Estimate the next quotient digit.
var trialQuotient: Self
if r.last! >= adjustedDivisor.last! {
trialQuotient = Self.max
} else {
trialQuotient = adjustedDivisor.last!.dividingFullWidth((r.last!, r[r.count - 2].magnitude)).quotient
}
// Confirm the quotient digit value.
while true {
let possibleNewRemainder = subtractMultiPrecisely(minuend: r, subtrahend: shortMultiplyMultiPrecisely(multiplicand: adjustedDivisor, multiplier: trialQuotient))
if let newRemainder = possibleNewRemainder {
q.insert(trialQuotient, at: q.startIndex)
r = newRemainder
assert(r.count < adjustedDivisor.count || r.count == adjustedDivisor.count && r.lexicographicallyPrecedes(adjustedDivisor))
break
} else {
trialQuotient -= 1
}
}
default:
assertionFailure("An overly large trial-dividend shouldn't happen (\(#function))")
}
}
trimLeadingZeros(&q)
// Denormalize.
let (unadjustedR, shortR) = shortDivideMultiPrecisely(dividend: r, divisor: normalizer)!
assert(shortR == 0)
return (quotient: q, remainder: unadjustedR)
}
}
// MARK: Extensions for any Fixed-Width Integer
extension FixedWidthInteger {
/**
Returns a mask with only the given number of most-significant bits set.
- Precondition: `0 <= count <= bitWidth`.
- Parameter count: The number of high-order bits set to `1`. All other bits are `0`.
- Returns: The bitwise-complement of the lowest `bitWidth - count` bits being set.
*/
static func highOrderBitsMask(count: Int) -> Self {
precondition(count >= 0)
return ~lowOrderBitsMask(count: bitWidth - count)
}
/**
Pushes out and returns the receiver's high-order bits while the low-order bits move up to make room for bits inserted at the least-significant end.
- Precondition: `0 <= count <= bitWidth`.
- Parameter source: The value whose high-order bits will become the new low-order bits of `self`.
- Parameter count: The number of bits from `source` pushed into `self`, and the number of bits formerly from `self` removed.
- Returns: The previous value of `self` with the lower `bitWidth - count` bits zeroed out.
- Postcondition:
- `self >> count == oldSelf & ((2 ** (bitWidth - count)) - 1)`.
- `self & ((2 ** count) - 1) == (source >> (bitWidth - count)) & ((2 ** count) - 1)`.
*/
mutating func pushLowOrderBits(fromHighOrderBitsOf source: Self, count: Int) -> Self {
switch count {
case bitWidth:
defer { self = source }
return self
case 1..<bitWidth:
defer {
self <<= count
replaceBits(with: source >> (bitWidth - count), forOnly: Self.lowOrderBitsMask(count: count))
}
return self & Self.highOrderBitsMask(count: count)
case 0:
return 0
default:
preconditionFailure("Illegal replacing bit-width used")
}
}
/**
Pushes out and returns the receiver's low-order bits while the high-order bits move down to make room for bits inserted at the most-significant end.
- Precondition: `0 <= count <= bitWidth`.
- Parameter source: The value whose low-order bits will become the new high-order bits of `self`.
- Parameter count: The number of bits from `source` pushed into `self`, and the number of bits formerly from `self` removed.
- Returns: The previous value of `self` with the upper `bitWidth - count` bits zeroed out.
- Postcondition:
- `self << count == oldSelf & ~((2 ** count) - 1)`.
- `(self >> (bitWidth - count)) & ((2 ** count) - 1 == source & ((2 ** count) - 1)`.
*/
mutating func pushHighOrderBits(fromLowOrderBitsOf source: Self, count: Int) -> Self {
switch count {
case bitWidth:
defer { self = source }
return self
case 1..<bitWidth:
defer {
self >>= count
replaceBits(with: source << (bitWidth - count), forOnly: Self.highOrderBitsMask(count: count))
}
return self & Self.lowOrderBitsMask(count: count)
case 0:
return 0
default:
preconditionFailure("Illegal replacing bit-width used")
}
}
}
/*
String+Extensions.swift
Created by Daryle Walker on 2/11/18.
Copyright (c) 2018 Daryle Walker.
Distributed under the MIT License.
Method for string-padding.
*/
extension String {
/**
Returns this string left-filled with the given character to the given count.
If `count` already matches or exceeds `totalCount`, then no copies of `fill` are added to the returned string.
- Parameter fill: The character to possibly prepend (multiple times) to this string.
- Parameter totalCount: The length of returned string.
- Returns: `s + self`, where *s* is *n* copies of `fill`, where *n* is `max(totalCount - count, 0)`.
*/
func paddingPrepended(_ fill: Character, totalCount: Int) -> String {
return String(repeating: fill, count: max(0, totalCount - count)) + self
}
}
/*
UInt72.swift
Created by Daryle Walker on 2/11/18.
Copyright (c) 2018 Daryle Walker.
Distributed under the MIT License.
A custom unsigned integer type of a size larger than the ones in the Standard Library.
*/
// MARK: A 72-Bit Unsigned Integer Type
/// A type like the default unsigned integers, but of 72 bits (*i.e.* 9 octet-sized bytes).
struct UInt72 {
/// The type to contain the high-order bits.
typealias High = UInt8
/// The type to contain the low-order bits.
typealias Low = UInt64
/// The high-order bits.
var high: High
/// The low-order bits.
var low: Low
/**
Creates an instance from the given components.
- Parameter highOrderByte: The value of the uppermost byte.
- Parameter lowOrderBytes: The value of the lowermost eight bytes.
- Postcondition:
- `self` represents `(highOrderByte << 64) | lowOrderBytes`.
- `high == highOrderByte`.
- `low == lowOrderBytes`.
*/
init(highOrderByte: High, lowOrderBytes: Low) {
(high, low) = (highOrderByte, lowOrderBytes)
}
}
// MARK: Debugging Output (CustomDebugStringConvertible)
extension UInt72: CustomDebugStringConvertible {
var debugDescription: String {
return "UInt72(\(high.fullHexadecimalString),\(low.fullHexadecimalString))"
}
}
// MARK: Integer Support (FixedWidthInteger / UnsignedInteger / etc.)
extension UInt72: FixedWidthInteger, UnsignedInteger {
// MARK: Secret Initializers
// From FixedWidthInteger
init(_truncatingBits bits: UInt) {
self.init(highOrderByte: 0, lowOrderBytes: Low(bits))
}
/**
Creates an instance from a sequence of words, possibly after skipping some bits.
- Parameter words: The sequence of `UInt` words as the source of the bits. The eariler words correspond to the lower-order bits of `self`. Within a word, the low-order bits go into the lower-order bits of `self`.
- Parameter count: The number of initial bits of `words` to ignore. Set to zero if not given.
- Postcondition: `self.words.elementsEqual(wordsAfterDroppingCountBits)`.
*/
init<S: Sequence>(words: S, bitsToSkip count: Int = 0) where S.Element == UInt {
let (wordSkipCount, bitSkipCount) = Swift.max(count, 0).quotientAndRemainder(dividingBy: UInt.bitWidth)
var wordArray = Array(words)
wordArray.removeFirst(wordSkipCount)
var newHighBits: UInt = 0
for i in wordArray.indices.reversed() {
newHighBits = wordArray[i].pushHighOrderBits(fromLowOrderBitsOf: newHighBits, count: bitSkipCount)
}
precondition(Low.bitWidth % UInt.bitWidth == 0)
precondition(High.bitWidth < UInt.bitWidth)
switch (wordArray.count, UInt.bitWidth) {
case (0, _):
self.init(highOrderByte: 0, lowOrderBytes: 0)
case (1, _):
self.init(highOrderByte: 0, lowOrderBytes: Low(wordArray.first!))
case (2, 32):
self.init(highOrderByte: 0, lowOrderBytes: (Low(wordArray[1]) << 32) | Low(wordArray[0]))
case (let c, 32) where c >= 3:
self.init(highOrderByte: High(truncatingIfNeeded: wordArray[2]), lowOrderBytes: (Low(wordArray[1]) << 32) | Low(wordArray[0]))
case (let c, 64) where c >= 2:
self.init(highOrderByte: High(truncatingIfNeeded: wordArray[1]), lowOrderBytes: Low(wordArray[0]))
default:
preconditionFailure("This shouldn't happen (\(#function))")
}
}
// MARK: FixedWidthInteger, Properties
var byteSwapped: UInt72 {
return UInt72(highOrderByte: High(truncatingIfNeeded: low), lowOrderBytes: (low.byteSwapped << High.bitWidth) | Low(high))
}
var leadingZeroBitCount: Int {
if high != 0 {
return high.leadingZeroBitCount
} else {
return High.bitWidth + low.leadingZeroBitCount
}
}
var nonzeroBitCount: Int {
return high.nonzeroBitCount + low.nonzeroBitCount
}
static var bitWidth: Int {
return High.bitWidth + Low.bitWidth
}
// MARK: BinaryInteger, Properties
var trailingZeroBitCount: Int {
if low != 0 {
return low.trailingZeroBitCount
} else {
return high.trailingZeroBitCount + Low.bitWidth
}
}
var words: [UInt] {
precondition(Low.bitWidth % UInt.bitWidth == 0)
return Array(low.words) + high.words
}
// MARK: ExpressibleByIntegerLiteral Support
init(integerLiteral value: UInt) {
self.init(_truncatingBits: value)
}
// MARK: Hashable Support
var hashValue: Int {
return debugDescription.hashValue
}
// MARK: BinaryInteger, Initializers
init<T>(_ source: T) where T : BinaryFloatingPoint {
switch source.floatingPointClass {
case .negativeNormal where source > -1.0:
fallthrough
case .negativeSubnormal, .negativeZero, .positiveZero, .positiveSubnormal:
self.init(_truncatingBits: 0)
case .positiveNormal:
precondition(T.significandBitCount < UInt72.bitWidth)
var copy: UInt72 = 1
copy <<= T.significandBitCount
copy |= UInt72(exactly: source.significandBitPattern)!
copy >>= T.significandBitCount - (source.significandWidth + 1)
self.init(highOrderByte: copy.high, lowOrderBytes: copy.low)
default:
preconditionFailure("Out-of-range value")
}
}
// MARK: FixedWidthInteger, Core Math: + - *
func addingReportingOverflow(_ rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
let (lowSum, lowOverflow) = low.addingReportingOverflow(rhs.low)
let (highSum, highOverflow) = high.addingReportingOverflow(rhs.high)
let (higherSum, higherOverflow) = highSum.addingReportingOverflow(lowOverflow ? 1 : 0)
let partialValueResult = UInt72(highOrderByte: higherSum, lowOrderBytes: lowSum)
let overflowResult = highOverflow || higherOverflow
return (partialValueResult, overflowResult)
}
func subtractingReportingOverflow(_ rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
let (lowDifference, lowOverflow) = low.subtractingReportingOverflow(rhs.low)
let (highDifference, highOverflow) = high.subtractingReportingOverflow(rhs.high)
let (higherDifference, higherOverflow) = highDifference.subtractingReportingOverflow(lowOverflow ? 1 : 0)
let partialValueResult = UInt72(highOrderByte: higherDifference, lowOrderBytes: lowDifference)
let overflowResult = highOverflow || higherOverflow
return (partialValueResult, overflowResult)
}
func multipliedFullWidth(by other: UInt72) -> (high: UInt72, low: UInt72) {
let selfDigits = words, otherDigits = other.words
var partialProducts = Array(repeatElement([] as [UInt], count: selfDigits.count + otherDigits.count))
for si in 0..<selfDigits.count {
for oi in 0..<otherDigits.count {
let partialProduct = selfDigits[si].multipliedFullWidth(by: otherDigits[oi])
partialProducts[si + oi].append(partialProduct.low)
partialProducts[si + oi + 1].append(partialProduct.high)
}
}
var productDigits = Array(repeatElement(0 as UInt, count: partialProducts.count + 1))
for pi in 0..<partialProducts.count {
let (sum, carries) = partialProducts[pi].reduce((productDigits[pi], 0 as UInt)) {
let sumParts = $0.0.addingReportingOverflow($1)
return (sumParts.partialValue, $0.1 + (sumParts.overflow ? 1 : 0))
}
productDigits[pi] += sum
productDigits[pi + 1] += carries
}
assert(productDigits.last == 0)
productDigits.removeLast()
return (UInt72(words: productDigits, bitsToSkip: UInt72.bitWidth), UInt72(words: productDigits))
}
// MARK: Helpers, Division
/**
Short-divides an arbitrary-length unsigned integer by a digit to a full-width quotient (and short remainder).
The dividend and quotient use radix `UInt72.max + 1`, with lower-order digits at the start going upwards.
- Parameter dividend: The first operand.
- Parameter divisor: The second operand.
- Returns: If `divisor != 0`, the `quotient` and `remainder`. Otherwise, `nil`.
*/
static func divide(dividend: [UInt72], divisor: UInt72) -> (quotient: [UInt72], remainder: UInt72)? {
precondition(Low.bitWidth % UInt.bitWidth == 0)
precondition(UInt.bitWidth % High.bitWidth == 0)
// Move the dividend to regular-sized words. (The divisor is one piece, so its conversion is easy.)
let (wholeWordsWithHighParts, highPartsLeftOver) = dividend.count.quotientAndRemainder(dividingBy: UInt.bitWidth / High.bitWidth)
let wordsNeededForAllHighParts = wholeWordsWithHighParts + highPartsLeftOver.signum()
var dividendWords = Array(repeatElement(0 as UInt, count: wordsNeededForAllHighParts))
for i in dividend.indices.reversed() {
let dividendDigitWords = dividend[i].words
var newLowBits = dividendDigitWords.last! << (UInt.bitWidth - High.bitWidth) // i.e. dividend[i].high
for j in dividendWords.indices {
newLowBits = dividendWords[j].pushLowOrderBits(fromHighOrderBitsOf: newLowBits, count: High.bitWidth)
}
dividendWords.append(contentsOf: dividendDigitWords.dropLast().reversed()) // i.e. dividend[i].low.words
}
// Do the division
return UInt.divideMultiPrecisely(dividend: dividendWords, divisor: divisor.words).map {
// Re-partition the bits back to 72-bit units.
let (qcq, qcr) = ($0.quotient.count * UInt.bitWidth).quotientAndRemainder(dividingBy: UInt72.bitWidth)
let quotient72Count = qcq + qcr.signum()
var quotientDigits = [UInt72]()
for n in 0..<quotient72Count {
quotientDigits.append(UInt72(words: $0.quotient, bitsToSkip: n * UInt72.bitWidth))
}
UInt72.trimLeadingZeros(&quotientDigits)
return (quotientDigits, UInt72(words: $0.remainder))
}
}
// MARK: FixedWidthInteger, Core Math: / %
func dividingFullWidth(_ dividend: (high: UInt72, low: UInt72)) -> (quotient: UInt72, remainder: UInt72) {
let results = UInt72.divide(dividend: [dividend.low, dividend.high], divisor: self)!
switch results.quotient.count {
case ...0:
return (0, results.remainder)
case 1:
return (results.quotient.first!, results.remainder)
default:
preconditionFailure("Quotient too large")
}
}
func dividedReportingOverflow(by rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
guard let result = UInt72.divide(dividend: [self], divisor: rhs)?.quotient else { return (self, true) }
guard let quotientLowDigit = result.first else { return (0, false) }
return (quotientLowDigit, result.count > 1)
}
func remainderReportingOverflow(dividingBy rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
guard let result = UInt72.divide(dividend: [self], divisor: rhs)?.remainder else { return (self, true) }
return (result, false)
}
// MARK: BinaryInteger, Efficency Override
func quotientAndRemainder(dividingBy rhs: UInt72) -> (quotient: UInt72, remainder: UInt72) {
return rhs.dividingFullWidth((high: 0, low: self))
}
// MARK: FixedWidthInteger, More Math
func multipliedReportingOverflow(by rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
let result = multipliedFullWidth(by: rhs)
return (result.low, result.high > 0)
}
// MARK: BinaryInteger, Math Operators
static func %(lhs: UInt72, rhs: UInt72) -> UInt72 {
let results = lhs.remainderReportingOverflow(dividingBy: rhs)
assert(!results.overflow)
return results.partialValue
}
static func %=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs % rhs }
static func *(lhs: UInt72, rhs: UInt72) -> UInt72 {
let results = lhs.multipliedReportingOverflow(by: rhs)
assert(!results.overflow)
return results.partialValue
}
static func *=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs * rhs }
static func +(lhs: UInt72, rhs: UInt72) -> UInt72 {
let results = lhs.addingReportingOverflow(rhs)
assert(!results.overflow)
return results.partialValue
}
static func +=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs + rhs }
static func -(lhs: UInt72, rhs: UInt72) -> UInt72 {
let results = lhs.subtractingReportingOverflow(rhs)
assert(!results.overflow)
return results.partialValue
}
static func -=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs - rhs }
static func /(lhs: UInt72, rhs: UInt72) -> UInt72 {
let results = lhs.dividedReportingOverflow(by: rhs)
assert(!results.overflow)
return results.partialValue
}
static func /=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs / rhs }
}
@CTMacUser
Copy link
Author

I'm on a 64-bit system, so the cases for 32-bit always flag a warning. Is there anyway to restructure that code to be 32/64-bit agnostic for Uint?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment