Last active
February 14, 2018 20:01
-
-
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.
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
/* | |
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 | |
} | |
} |
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
/* | |
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") | |
} | |
} | |
} |
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
/* | |
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 | |
} | |
} |
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
/* | |
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("ientDigits) | |
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 } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
?