diff --git a/Package.swift b/Package.swift
index eba63bcd..249551a3 100644
--- a/Package.swift
+++ b/Package.swift
@@ -3,7 +3,7 @@
//
// This source file is part of the Swift Numerics open source project
//
-// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors
+// Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
@@ -71,7 +71,7 @@ let package = Package(
.testTarget(
name: "IntegerUtilitiesTests",
- dependencies: ["IntegerUtilities"],
+ dependencies: ["IntegerUtilities", "_TestSupport"],
exclude: ["CMakeLists.txt"]
),
diff --git a/Sources/_TestSupport/CMakeLists.txt b/Sources/_TestSupport/CMakeLists.txt
index 83b233f5..48b85d12 100644
--- a/Sources/_TestSupport/CMakeLists.txt
+++ b/Sources/_TestSupport/CMakeLists.txt
@@ -1,13 +1,14 @@
#[[
This source file is part of the Swift Numerics open source project
-Copyright (c) 2019-2020 Apple Inc. and the Swift Numerics project authors
+Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See https://swift.org/LICENSE.txt for license information
#]]
add_library(_TestSupport
+ DoubleWidth.swift
Error.swift
Interval.swift
RealTestSupport.swift)
diff --git a/Sources/_TestSupport/DoubleWidth.swift b/Sources/_TestSupport/DoubleWidth.swift
new file mode 100644
index 00000000..b41d9c31
--- /dev/null
+++ b/Sources/_TestSupport/DoubleWidth.swift
@@ -0,0 +1,787 @@
+//===--- DoubleWidth.swift ------------------------------------*- swift -*-===//
+//
+// This source file is part of the Swift Numerics open source project
+//
+// Copyright (c) 2017-2021 Apple Inc. and the Swift Numerics project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+//
+//===----------------------------------------------------------------------===//
+
+/// A fixed-width integer that has twice the bit width of its base type.
+///
+/// You can use the `DoubleWidth` type to continue calculations with the result
+/// of a full width arithmetic operation. Normally, when you perform a full
+/// width operation, the result is a tuple of the high and low parts of the
+/// result.
+///
+/// let a = 2241543570477705381
+/// let b = 186319822866995413
+/// let c = a.multipliedFullWidth(by: b)
+/// // c == (high: 22640526660490081, low: 7959093232766896457)
+///
+/// The tuple `c` can't be used in any further comparisons or calculations. To
+/// use this value, create a `DoubleWidth` instance from the result. You can
+/// use the `DoubleWidth` instance in the same way that you would use any other
+/// integer type.
+///
+/// let d = DoubleWidth(a.multipliedFullWidth(by: b))
+/// // d == 417644001000058515200174966092417353
+///
+/// // Check the calculation:
+/// print(d / DoubleWidth(a) == b)
+/// // Prints "true"
+///
+/// if d > Int.max {
+/// print("Too big to be an 'Int'!")
+/// } else {
+/// print("Small enough to fit in an 'Int'")
+/// }
+/// // Prints "Too big to be an 'Int'!"
+///
+/// The `DoubleWidth` type is not intended as a replacement for a variable-width
+/// integer type. Nesting `DoubleWidth` instances, in particular, may result in
+/// undesirable performance.
+public struct DoubleWidth {
+ public typealias High = Base
+ public typealias Low = Base.Magnitude
+
+#if _endian(big)
+ internal var _storage: (high: High, low: Low)
+#else
+ internal var _storage: (low: Low, high: High)
+#endif
+
+ /// The high part of the value.
+ public var high: High {
+ return _storage.high
+ }
+
+ /// The low part of the value.
+ public var low: Low {
+ return _storage.low
+ }
+
+ /// Creates a new instance from the given tuple of high and low parts.
+ ///
+ /// - Parameter value: The tuple to use as the source of the new instance's
+ /// high and low parts.
+ public init(_ value: (high: High, low: Low)) {
+#if _endian(big)
+ self._storage = (high: value.0, low: value.1)
+#else
+ self._storage = (low: value.1, high: value.0)
+#endif
+ }
+
+ // We expect users to invoke the public initializer above as demonstrated in
+ // the documentation (that is, by passing in the result of a full width
+ // operation).
+ //
+ // Internally, we'll need to create new instances by supplying high and low
+ // parts directly; ((double parentheses)) greatly impair readability,
+ // especially when nested:
+ //
+ // DoubleWidth((DoubleWidth((0, 0)), DoubleWidth((0, 0))))
+ //
+ // For that reason, we'll include an internal initializer that takes two
+ // separate arguments.
+ internal init(_ _high: High, _ low: Low) {
+ self.init((_high, low))
+ }
+
+ public init() {
+ self.init(0, 0)
+ }
+}
+
+extension DoubleWidth : CustomStringConvertible {
+ public var description: String {
+ return String(self, radix: 10)
+ }
+}
+
+extension DoubleWidth : CustomDebugStringConvertible {
+ public var debugDescription: String {
+ return "(\(_storage.high), \(_storage.low))"
+ }
+}
+
+extension DoubleWidth : Equatable {
+ public static func ==(lhs: DoubleWidth, rhs: DoubleWidth) -> Bool {
+ return lhs._storage.low == rhs._storage.low
+ && lhs._storage.high == rhs._storage.high
+ }
+}
+
+extension DoubleWidth : Comparable {
+ public static func <(lhs: DoubleWidth, rhs: DoubleWidth) -> Bool {
+ if lhs._storage.high < rhs._storage.high { return true }
+ else if lhs._storage.high > rhs._storage.high { return false }
+ else { return lhs._storage.low < rhs._storage.low }
+ }
+}
+
+extension DoubleWidth : Hashable {
+ public var hashValue: Int {
+ return _hashValue(for: self)
+ }
+
+ public func hash(into hasher: inout Hasher) {
+ hasher.combine(low)
+ hasher.combine(high)
+ }
+}
+
+extension DoubleWidth : Numeric {
+ public typealias Magnitude = DoubleWidth
+
+ public var magnitude: Magnitude {
+ let result = Magnitude(Low(truncatingIfNeeded: _storage.high), _storage.low)
+ if Base.isSigned && _storage.high < (0 as High) {
+ return ~result &+ 1
+ } else {
+ return result
+ }
+ }
+
+ internal init(_ _magnitude: Magnitude) {
+ self.init(High(_magnitude._storage.high), _magnitude._storage.low)
+ }
+
+ public init(_ source: T) {
+ guard let result = DoubleWidth(exactly: source) else {
+ preconditionFailure("Value is outside the representable range")
+ }
+ self = result
+ }
+
+ public init?(exactly source: T) {
+ // Can't represent a negative 'source' if Base is unsigned.
+ guard DoubleWidth.isSigned || source >= 0 else { return nil }
+
+ // Is 'source' entirely representable in Low?
+ if let low = Low(exactly: source.magnitude) {
+ self.init(source < (0 as T) ? (~0, ~low &+ 1) : (0, low))
+ } else {
+ // At this point we know source.bitWidth > Base.bitWidth, or else we
+ // would've taken the first branch.
+ let lowInT = source & T(~0 as Low)
+ let highInT = source >> Low.bitWidth
+
+ let low = Low(lowInT)
+ guard let high = High(exactly: highInT) else { return nil }
+ self.init(high, low)
+ }
+ }
+}
+
+#if false
+
+// This conformance is only possible once the type is in the stdlib, as it uses
+// Builtin
+extension DoubleWidth: _ExpressibleByBuiltinIntegerLiteral {
+ public init(_builtinIntegerLiteral _x: Builtin.IntegerLiteral) {
+ var _x = _x
+ self = DoubleWidth()
+
+ // If we can capture the entire literal in a single Int64, stop there.
+ // This avoids some potential deep recursion due to literal expressions in
+ // other DoubleWidth methods.
+ let (_value, _overflow) = Builtin.s_to_s_checked_trunc_IntLiteral_Int64(_x)
+ if !Bool(_overflow) {
+ self = DoubleWidth(Int64(_value))
+ return
+ }
+
+ // Convert all but the most significant 64 bits as unsigned integers.
+ let _shift = Builtin.sext_Int64_IntLiteral((64 as Int64)._value)
+ let lowWordCount = (bitWidth - 1) / 64
+ for i in 0..) {
+ // Multiples of word size only.
+ guard Base.bitWidth == Base.Magnitude.bitWidth &&
+ (UInt.bitWidth % Base.bitWidth == 0 ||
+ Base.bitWidth % UInt.bitWidth == 0) else {
+ fatalError("Access to words is not supported on this type")
+ }
+ self._high = value._storage.high.words
+ self._low = value._storage.low.words
+ assert(!_low.isEmpty)
+ }
+ }
+}
+
+extension DoubleWidth.Words: RandomAccessCollection {
+ public typealias Index = Int
+
+ public var startIndex: Index {
+ return 0
+ }
+
+ public var endIndex: Index {
+ return count
+ }
+
+ public var count: Int {
+ if Base.bitWidth < UInt.bitWidth { return 1 }
+ return _low.count + _high.count
+ }
+
+ public subscript(_ i: Index) -> UInt {
+ if Base.bitWidth < UInt.bitWidth {
+ precondition(i == 0, "Invalid index")
+ assert(2 * Base.bitWidth <= UInt.bitWidth)
+ return _low.first! | (_high.first! &<< Base.bitWidth._lowWord)
+ }
+ if i < _low.count {
+ return _low[i + _low.startIndex]
+ }
+
+ return _high[i - _low.count + _high.startIndex]
+ }
+}
+
+extension DoubleWidth : FixedWidthInteger {
+ public var words: Words {
+ return Words(self)
+ }
+
+ public static var isSigned: Bool {
+ return Base.isSigned
+ }
+
+ public static var max: DoubleWidth {
+ return self.init(High.max, Low.max)
+ }
+
+ public static var min: DoubleWidth {
+ return self.init(High.min, Low.min)
+ }
+
+ public static var bitWidth: Int {
+ return High.bitWidth + Low.bitWidth
+ }
+
+ public func addingReportingOverflow(_ rhs: DoubleWidth)
+ -> (partialValue: DoubleWidth, overflow: Bool) {
+ let (low, lowOverflow) =
+ _storage.low.addingReportingOverflow(rhs._storage.low)
+ let (high, highOverflow) =
+ _storage.high.addingReportingOverflow(rhs._storage.high)
+ let result = (high &+ (lowOverflow ? 1 : 0), low)
+ let overflow = highOverflow ||
+ high == Base.max && lowOverflow
+ return (partialValue: DoubleWidth(result), overflow: overflow)
+ }
+ public func subtractingReportingOverflow(_ rhs: DoubleWidth)
+ -> (partialValue: DoubleWidth, overflow: Bool) {
+ let (low, lowOverflow) =
+ _storage.low.subtractingReportingOverflow(rhs._storage.low)
+ let (high, highOverflow) =
+ _storage.high.subtractingReportingOverflow(rhs._storage.high)
+ let result = (high &- (lowOverflow ? 1 : 0), low)
+ let overflow = highOverflow ||
+ high == Base.min && lowOverflow
+ return (partialValue: DoubleWidth(result), overflow: overflow)
+ }
+
+ public func multipliedReportingOverflow(
+ by rhs: DoubleWidth
+ ) -> (partialValue: DoubleWidth, overflow: Bool) {
+ let (carry, product) = multipliedFullWidth(by: rhs)
+ let result = DoubleWidth(truncatingIfNeeded: product)
+
+ let isNegative = DoubleWidth.isSigned &&
+ (self < (0 as DoubleWidth)) != (rhs < (0 as DoubleWidth))
+ let didCarry = isNegative
+ ? carry != ~(0 as DoubleWidth)
+ : carry != (0 as DoubleWidth)
+ let hadPositiveOverflow =
+ DoubleWidth.isSigned && !isNegative && product.leadingZeroBitCount == 0
+
+ return (result, didCarry || hadPositiveOverflow)
+ }
+
+ public func quotientAndRemainder(
+ dividingBy other: DoubleWidth
+ ) -> (quotient: DoubleWidth, remainder: DoubleWidth) {
+ let (quotient, remainder) =
+ Magnitude._divide(self.magnitude, by: other.magnitude)
+ guard DoubleWidth.isSigned else {
+ return (DoubleWidth(quotient), DoubleWidth(remainder))
+ }
+ let isNegative = (self.high < (0 as High)) != (other.high < (0 as High))
+ let quotient_ = isNegative
+ ? quotient == DoubleWidth.min.magnitude
+ ? DoubleWidth.min
+ : 0 - DoubleWidth(quotient)
+ : DoubleWidth(quotient)
+ let remainder_ = self.high < (0 as High)
+ ? 0 - DoubleWidth(remainder)
+ : DoubleWidth(remainder)
+ return (quotient_, remainder_)
+ }
+
+ public func dividedReportingOverflow(
+ by other: DoubleWidth
+ ) -> (partialValue: DoubleWidth, overflow: Bool) {
+ if other == (0 as DoubleWidth) { return (self, true) }
+ if DoubleWidth.isSigned && other == -1 && self == .min {
+ return (self, true)
+ }
+ return (quotientAndRemainder(dividingBy: other).quotient, false)
+ }
+
+ public func remainderReportingOverflow(
+ dividingBy other: DoubleWidth
+ ) -> (partialValue: DoubleWidth, overflow: Bool) {
+ if other == (0 as DoubleWidth) { return (self, true) }
+ if DoubleWidth.isSigned && other == -1 && self == .min { return (0, true) }
+ return (quotientAndRemainder(dividingBy: other).remainder, false)
+ }
+
+ public func multipliedFullWidth(
+ by other: DoubleWidth
+ ) -> (high: DoubleWidth, low: DoubleWidth.Magnitude) {
+ let isNegative = DoubleWidth.isSigned &&
+ (self < (0 as DoubleWidth)) != (other < (0 as DoubleWidth))
+
+ func mul(_ x: Low, _ y: Low) -> (partial: Low, carry: Low) {
+ let (high, low) = x.multipliedFullWidth(by: y)
+ return (low, high)
+ }
+
+ func sum(_ x: Low, _ y: Low, _ z: Low) -> (partial: Low, carry: Low) {
+ let (sum1, overflow1) = x.addingReportingOverflow(y)
+ let (sum2, overflow2) = sum1.addingReportingOverflow(z)
+ let carry: Low = (overflow1 ? 1 : 0) + (overflow2 ? 1 : 0)
+ return (sum2, carry)
+ }
+
+ let lhs = self.magnitude
+ let rhs = other.magnitude
+
+ let a = mul(rhs._storage.low, lhs._storage.low)
+ let b = mul(rhs._storage.low, lhs._storage.high)
+ let c = mul(rhs._storage.high, lhs._storage.low)
+ let d = mul(rhs._storage.high, lhs._storage.high)
+
+ let mid1 = sum(a.carry, b.partial, c.partial)
+ let mid2 = sum(b.carry, c.carry, d.partial)
+
+ let low =
+ DoubleWidth(mid1.partial, a.partial)
+ let high =
+ DoubleWidth(High(mid2.carry + d.carry), mid1.carry + mid2.partial)
+
+ if isNegative {
+ let (lowComplement, overflow) = (~low).addingReportingOverflow(1)
+ return (~high + (overflow ? 1 : 0 as DoubleWidth), lowComplement)
+ } else {
+ return (high, low)
+ }
+ }
+
+ public func dividingFullWidth(
+ _ dividend: (high: DoubleWidth, low: DoubleWidth.Magnitude)
+ ) -> (quotient: DoubleWidth, remainder: DoubleWidth) {
+ let other = DoubleWidth(dividend)
+ let (quotient, remainder) =
+ Magnitude._divide(other.magnitude, by: self.magnitude)
+ guard DoubleWidth.isSigned else {
+ return (DoubleWidth(quotient), DoubleWidth(remainder))
+ }
+ let isNegative =
+ (self.high < (0 as High)) != (other.high.high < (0 as High))
+ let quotient_ = isNegative
+ ? quotient == DoubleWidth.min.magnitude
+ ? DoubleWidth.min
+ : 0 - DoubleWidth(quotient)
+ : DoubleWidth(quotient)
+ let remainder_ = other.high.high < (0 as High)
+ ? 0 - DoubleWidth(remainder)
+ : DoubleWidth(remainder)
+ return (quotient_, remainder_)
+ }
+
+ public static func &=(
+ lhs: inout DoubleWidth, rhs: DoubleWidth
+ ) {
+ lhs._storage.low &= rhs._storage.low
+ lhs._storage.high &= rhs._storage.high
+ }
+ public static func |=(
+ lhs: inout DoubleWidth, rhs: DoubleWidth
+ ) {
+ lhs._storage.low |= rhs._storage.low
+ lhs._storage.high |= rhs._storage.high
+ }
+ public static func ^=(
+ lhs: inout DoubleWidth, rhs: DoubleWidth
+ ) {
+ lhs._storage.low ^= rhs._storage.low
+ lhs._storage.high ^= rhs._storage.high
+ }
+
+ public static func <<=(lhs: inout DoubleWidth, rhs: DoubleWidth) {
+ if DoubleWidth.isSigned && rhs < (0 as DoubleWidth) {
+ lhs >>= 0 - rhs
+ return
+ }
+
+ // Shift is larger than this type's bit width.
+ if rhs._storage.high != (0 as High) ||
+ rhs._storage.low >= DoubleWidth.bitWidth
+ {
+ lhs = 0
+ return
+ }
+
+ lhs &<<= rhs
+ }
+
+ public static func >>=(lhs: inout DoubleWidth, rhs: DoubleWidth) {
+ if DoubleWidth.isSigned && rhs < (0 as DoubleWidth) {
+ lhs <<= 0 - rhs
+ return
+ }
+
+ // Shift is larger than this type's bit width.
+ if rhs._storage.high != (0 as High) ||
+ rhs._storage.low >= DoubleWidth.bitWidth
+ {
+ lhs = lhs < (0 as DoubleWidth) ? ~0 : 0
+ return
+ }
+
+ lhs &>>= rhs
+ }
+
+ /// Returns this value "masked" by its bit width.
+ ///
+ /// "Masking" notionally involves repeatedly incrementing or decrementing this
+ /// value by `self.bitWidth` until the result is contained in the range
+ /// `0.. DoubleWidth {
+ // FIXME(integers): test types with bit widths that aren't powers of 2
+ if DoubleWidth.bitWidth.nonzeroBitCount == 1 {
+ return self & DoubleWidth(DoubleWidth.bitWidth &- 1)
+ }
+ if DoubleWidth.isSigned && self._storage.high < (0 as High) {
+ return self % DoubleWidth(DoubleWidth.bitWidth) + self
+ }
+ return self % DoubleWidth(DoubleWidth.bitWidth)
+ }
+
+ public static func &<<=(lhs: inout DoubleWidth, rhs: DoubleWidth) {
+ let rhs = rhs._masked()
+
+ guard rhs._storage.low < Base.bitWidth else {
+ lhs._storage.high = High(
+ truncatingIfNeeded: lhs._storage.low &<<
+ (rhs._storage.low &- Low(Base.bitWidth)))
+ lhs._storage.low = 0
+ return
+ }
+
+ guard rhs._storage.low != (0 as Low) else { return }
+ lhs._storage.high &<<= High(rhs._storage.low)
+ lhs._storage.high |= High(
+ truncatingIfNeeded: lhs._storage.low &>>
+ (Low(Base.bitWidth) &- rhs._storage.low))
+ lhs._storage.low &<<= rhs._storage.low
+ }
+
+ public static func &>>=(lhs: inout DoubleWidth, rhs: DoubleWidth) {
+ let rhs = rhs._masked()
+
+ guard rhs._storage.low < Base.bitWidth else {
+ lhs._storage.low = Low(
+ truncatingIfNeeded: lhs._storage.high &>>
+ High(rhs._storage.low &- Low(Base.bitWidth)))
+ lhs._storage.high = lhs._storage.high < (0 as High) ? ~0 : 0
+ return
+ }
+
+ guard rhs._storage.low != (0 as Low) else { return }
+ lhs._storage.low &>>= rhs._storage.low
+ lhs._storage.low |= Low(
+ truncatingIfNeeded: lhs._storage.high &<<
+ High(Low(Base.bitWidth) &- rhs._storage.low))
+ lhs._storage.high &>>= High(rhs._storage.low)
+ }
+
+
+ // FIXME(integers): remove this once the operators are back to Numeric
+ public static func + (
+ lhs: DoubleWidth, rhs: DoubleWidth
+ ) -> DoubleWidth {
+ var lhs = lhs
+ lhs += rhs
+ return lhs
+ }
+
+ public static func +=(
+ lhs: inout DoubleWidth, rhs: DoubleWidth
+ ) {
+ let (result, overflow) = lhs.addingReportingOverflow(rhs)
+ precondition(!overflow, "Overflow in +=")
+ lhs = result
+ }
+
+ // FIXME(integers): remove this once the operators are back to Numeric
+ public static func - (
+ lhs: DoubleWidth, rhs: DoubleWidth
+ ) -> DoubleWidth {
+ var lhs = lhs
+ lhs -= rhs
+ return lhs
+ }
+
+ public static func -=(
+ lhs: inout DoubleWidth, rhs: DoubleWidth
+ ) {
+ let (result, overflow) = lhs.subtractingReportingOverflow(rhs)
+ precondition(!overflow, "Overflow in -=")
+ lhs = result
+ }
+
+ // FIXME(integers): remove this once the operators are back to Numeric
+ public static func * (
+ lhs: DoubleWidth, rhs: DoubleWidth
+ ) -> DoubleWidth {
+ var lhs = lhs
+ lhs *= rhs
+ return lhs
+ }
+
+ public static func *=(
+ lhs: inout DoubleWidth, rhs: DoubleWidth
+ ) {
+ let (result, overflow) = lhs.multipliedReportingOverflow(by:rhs)
+ precondition(!overflow, "Overflow in *=")
+ lhs = result
+ }
+
+ // FIXME(integers): remove this once the operators are back to Numeric
+ public static func / (
+ lhs: DoubleWidth, rhs: DoubleWidth
+ ) -> DoubleWidth {
+ var lhs = lhs
+ lhs /= rhs
+ return lhs
+ }
+
+ public static func /=(
+ lhs: inout DoubleWidth, rhs: DoubleWidth
+ ) {
+ let (result, overflow) = lhs.dividedReportingOverflow(by:rhs)
+ precondition(!overflow, "Overflow in /=")
+ lhs = result
+ }
+
+ // FIXME(integers): remove this once the operators are back to Numeric
+ public static func % (
+ lhs: DoubleWidth, rhs: DoubleWidth
+ ) -> DoubleWidth {
+ var lhs = lhs
+ lhs %= rhs
+ return lhs
+ }
+
+ public static func %=(
+ lhs: inout DoubleWidth, rhs: DoubleWidth
+ ) {
+ let (result, overflow) = lhs.remainderReportingOverflow(dividingBy:rhs)
+ precondition(!overflow, "Overflow in %=")
+ lhs = result
+ }
+
+ public init(_truncatingBits bits: UInt) {
+ _storage.low = Low(_truncatingBits: bits)
+ _storage.high = High(_truncatingBits: bits >> UInt(Low.bitWidth))
+ }
+
+ public init(integerLiteral x: Int) {
+ self.init(x)
+ }
+ public var leadingZeroBitCount: Int {
+ return high == (0 as High)
+ ? High.bitWidth + low.leadingZeroBitCount
+ : high.leadingZeroBitCount
+ }
+
+ public var trailingZeroBitCount: Int {
+ return low == (0 as Low)
+ ? Low.bitWidth + high.trailingZeroBitCount
+ : low.trailingZeroBitCount
+ }
+
+ public var nonzeroBitCount: Int {
+ return high.nonzeroBitCount + low.nonzeroBitCount
+ }
+
+ public var byteSwapped: DoubleWidth {
+ return DoubleWidth(
+ High(truncatingIfNeeded: low.byteSwapped),
+ Low(truncatingIfNeeded: high.byteSwapped))
+ }
+}
+
+extension DoubleWidth : UnsignedInteger where Base : UnsignedInteger {
+ /// Returns the quotient and remainder after dividing a triple-width magnitude
+ /// `lhs` by a double-width magnitude `rhs`.
+ ///
+ /// This operation is conceptually that described by Burnikel and Ziegler
+ /// (1998).
+ internal static func _divide(
+ _ lhs: (high: Low, mid: Low, low: Low), by rhs: Magnitude
+ ) -> (quotient: Low, remainder: Magnitude) {
+ // The following invariants are guaranteed to hold by dividingFullWidth or
+ // quotientAndRemainder before this method is invoked:
+ assert(lhs.high != (0 as Low))
+ assert(rhs.leadingZeroBitCount == 0)
+ assert(Magnitude(lhs.high, lhs.mid) < rhs)
+
+ // Estimate the quotient.
+ var quotient = lhs.high == rhs.high
+ ? Low.max
+ : rhs.high.dividingFullWidth((lhs.high, lhs.mid)).quotient
+ // Compute quotient * rhs.
+ // TODO: This could be performed more efficiently.
+ var product =
+ DoubleWidth(
+ 0, Magnitude(quotient.multipliedFullWidth(by: rhs.low)))
+ let (x, y) = quotient.multipliedFullWidth(by: rhs.high)
+ product += DoubleWidth(Magnitude(0, x), Magnitude(y, 0))
+ // Compute the remainder after decrementing quotient as necessary.
+ var remainder =
+ DoubleWidth(
+ Magnitude(0, lhs.high), Magnitude(lhs.mid, lhs.low))
+ while remainder < product {
+ quotient = quotient &- 1
+ remainder += DoubleWidth(0, rhs)
+ }
+ remainder -= product
+
+ return (quotient, remainder.low)
+ }
+
+ /// Returns the quotient and remainder after dividing a quadruple-width
+ /// magnitude `lhs` by a double-width magnitude `rhs`.
+ internal static func _divide(
+ _ lhs: DoubleWidth, by rhs: Magnitude
+ ) -> (quotient: Magnitude, remainder: Magnitude) {
+ guard _fastPath(rhs > (0 as Magnitude)) else {
+ fatalError("Division by zero")
+ }
+ guard _fastPath(rhs >= lhs.high) else {
+ fatalError("Division results in an overflow")
+ }
+
+ if lhs.high == (0 as Magnitude) {
+ return lhs.low.quotientAndRemainder(dividingBy: rhs)
+ }
+
+ if rhs.high == (0 as Low) {
+ let a = lhs.high.high % rhs.low
+ let b = a == (0 as Low)
+ ? lhs.high.low % rhs.low
+ : rhs.low.dividingFullWidth((a, lhs.high.low)).remainder
+ let (x, c) = b == (0 as Low)
+ ? lhs.low.high.quotientAndRemainder(dividingBy: rhs.low)
+ : rhs.low.dividingFullWidth((b, lhs.low.high))
+ let (y, d) = c == (0 as Low)
+ ? lhs.low.low.quotientAndRemainder(dividingBy: rhs.low)
+ : rhs.low.dividingFullWidth((c, lhs.low.low))
+ return (Magnitude(x, y), Magnitude(0, d))
+ }
+
+ // Left shift both rhs and lhs, then divide and right shift the remainder.
+ let shift = rhs.leadingZeroBitCount
+ let rhs = rhs &<< shift
+ let lhs = lhs &<< shift
+ if lhs.high.high == (0 as Low)
+ && Magnitude(lhs.high.low, lhs.low.high) < rhs {
+ let (quotient, remainder) =
+ Magnitude._divide((lhs.high.low, lhs.low.high, lhs.low.low), by: rhs)
+ return (Magnitude(0, quotient), remainder &>> shift)
+ }
+ let (x, a) =
+ Magnitude._divide((lhs.high.high, lhs.high.low, lhs.low.high), by: rhs)
+ let (y, b) =
+ Magnitude._divide((a.high, a.low, lhs.low.low), by: rhs)
+ return (Magnitude(x, y), b &>> shift)
+ }
+
+ /// Returns the quotient and remainder after dividing a double-width
+ /// magnitude `lhs` by a double-width magnitude `rhs`.
+ internal static func _divide(
+ _ lhs: Magnitude, by rhs: Magnitude
+ ) -> (quotient: Magnitude, remainder: Magnitude) {
+ guard _fastPath(rhs > (0 as Magnitude)) else {
+ fatalError("Division by zero")
+ }
+ guard rhs < lhs else {
+ if _fastPath(rhs > lhs) { return (0, lhs) }
+ return (1, 0)
+ }
+
+ if lhs.high == (0 as Low) {
+ let (quotient, remainder) =
+ lhs.low.quotientAndRemainder(dividingBy: rhs.low)
+ return (Magnitude(quotient), Magnitude(remainder))
+ }
+
+ if rhs.high == (0 as Low) {
+ let (x, a) = lhs.high.quotientAndRemainder(dividingBy: rhs.low)
+ let (y, b) = a == (0 as Low)
+ ? lhs.low.quotientAndRemainder(dividingBy: rhs.low)
+ : rhs.low.dividingFullWidth((a, lhs.low))
+ return (Magnitude(x, y), Magnitude(0, b))
+ }
+
+ // Left shift both rhs and lhs, then divide and right shift the remainder.
+ let shift = rhs.leadingZeroBitCount
+ let rhs = rhs &<< shift
+ let high = (lhs &>> (Magnitude.bitWidth &- shift)).low
+ let lhs = lhs &<< shift
+ let (quotient, remainder) =
+ Magnitude._divide((high, lhs.high, lhs.low), by: rhs)
+ return (Magnitude(0, quotient), remainder &>> shift)
+ }
+}
+
+extension DoubleWidth : SignedNumeric, SignedInteger
+ where Base : SignedInteger {}
diff --git a/Tests/IntegerUtilitiesTests/CMakeLists.txt b/Tests/IntegerUtilitiesTests/CMakeLists.txt
index 194e9fe2..a6522d2d 100644
--- a/Tests/IntegerUtilitiesTests/CMakeLists.txt
+++ b/Tests/IntegerUtilitiesTests/CMakeLists.txt
@@ -9,6 +9,7 @@ See https://swift.org/LICENSE.txt for license information
add_library(IntegerUtilitiesTests
DivideTests.swift
+ DoubleWidthTests.swift
GCDTests.swift
RotateTests.swift
ShiftTests.swift)
@@ -17,5 +18,5 @@ target_compile_options(IntegerUtilitiesTests PRIVATE
target_link_libraries(IntegerUtilitiesTests PUBLIC
$<$>:Foundation>
Numerics
+ _TestSupport
XCTest)
-
diff --git a/Tests/IntegerUtilitiesTests/DoubleWidthTests.swift b/Tests/IntegerUtilitiesTests/DoubleWidthTests.swift
new file mode 100644
index 00000000..8e685b25
--- /dev/null
+++ b/Tests/IntegerUtilitiesTests/DoubleWidthTests.swift
@@ -0,0 +1,497 @@
+//===--- DoubleWidthTests.swift -------------------------------*- swift -*-===//
+//
+// This source file is part of the Swift Numerics open source project
+//
+// Copyright (c) 2017-2021 Apple Inc. and the Swift Numerics project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+//
+//===----------------------------------------------------------------------===//
+
+import _TestSupport
+import XCTest
+
+final class DoubleWidthTests: XCTestCase {
+
+ typealias _Int16 = DoubleWidth
+ typealias _Int128 = DoubleWidth
+ typealias _Int256 = DoubleWidth<_Int128>
+ typealias _Int512 = DoubleWidth<_Int256>
+ typealias _Int1024 = DoubleWidth<_Int512>
+
+ typealias _UInt16 = DoubleWidth
+ typealias _UInt128 = DoubleWidth
+ typealias _UInt256 = DoubleWidth<_UInt128>
+ typealias _UInt512 = DoubleWidth<_UInt256>
+ typealias _UInt1024 = DoubleWidth<_UInt512>
+
+ //
+ // Swift 4.x took over 15 minutes to compile this test code.
+ // Swift 5.5 takes less than one second (`-Onone` or `-O` or `-Osize`).
+ func testCompileTime_SR_6947() {
+ typealias _Int32 = DoubleWidth>
+ typealias _Int64 = DoubleWidth>>
+
+ var sum = 0
+ let (q, r) = (_Int128(Int64.max) * 16).quotientAndRemainder(dividingBy: 16)
+ sum += Int(q * r)
+ XCTAssertEqual(sum, 0)
+
+ let x = _Int64(Int64.max / 4)
+ let y = _Int32(Int32.max)
+ let xx = _Int1024(x)
+ let yy = _Int512(y)
+ let (q3, r3) = yy.dividingFullWidth((xx.high, xx.low))
+ sum -= Int(q3 - r3)
+ XCTAssertEqual(sum, -1)
+ }
+
+ func testLiterals() {
+ let w: _UInt16 = 100
+ XCTAssertTrue(w == 100 as Int)
+
+ let x: _UInt16 = 1000
+ XCTAssertTrue(x == 1000 as Int)
+
+ let y: _Int16 = 1000
+ XCTAssertTrue(y == 1000 as Int)
+
+ let z: _Int16 = -1000
+ XCTAssertTrue(z == -1000 as Int)
+ }
+
+ func testLiterals_Underflow() {
+ // TODO: expectCrashLater()
+ // _ = -1 as _UInt16
+ }
+
+#if false // TODO: _ExpressibleByBuiltinIntegerLiteral
+
+ func testLiterals_LargeSigned() {
+ let a: _Int256 =
+ 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ let b: _Int256 =
+ -0x8000000000000000000000000000000000000000000000000000000000000000
+ XCTAssertEqual(a, _Int256.max)
+ XCTAssertEqual(b, _Int256.min)
+ }
+
+ func testLiterals_LargeSigned_Underflow() {
+ // TODO: expectCrashLater()
+ // _ = -0x8000000000000000000000000000000000000000000000000000000000000001
+ // as _Int256
+ }
+
+ func testLiterals_LargeSigned_Overflow() {
+ // TODO: expectCrashLater()
+ // _ = 0x8000000000000000000000000000000000000000000000000000000000000000
+ // as _Int256
+ }
+
+ func testLiterals_LargeUnsigned() {
+ let a: _UInt256 =
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ let b: _UInt256 = 0
+ XCTAssertEqual(a, _UInt256.max)
+ XCTAssertEqual(b, _UInt256.min)
+ }
+
+ func testLiterals_LargeUnsigned_Underflow() {
+ // TODO: expectCrashLater()
+ // _ = -1 as _UInt256
+ }
+
+ func testLiterals_LargeUnsigned_Overflow() {
+ // TODO: expectCrashLater()
+ // _ = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0
+ // as _UInt256
+ }
+
+#endif // TODO: _ExpressibleByBuiltinIntegerLiteral
+
+ func testArithmetic_Unsigned() {
+ let x: _UInt16 = 1000
+ let y: _UInt16 = 1111
+ XCTAssertEqual(x + 1, 1001)
+ XCTAssertEqual(x + x, 2000)
+ XCTAssertEqual(x - (1 as _UInt16), 999)
+ XCTAssertEqual(x - x, 0)
+ XCTAssertEqual(y - x, 111)
+
+ XCTAssertEqual(x * 7, 7000)
+ XCTAssertEqual(y * 7, 7777)
+
+ XCTAssertEqual(x / 3, 333)
+ XCTAssertEqual(x / x, 1)
+ XCTAssertEqual(x / y, 0)
+ XCTAssertEqual(y / x, 1)
+
+ XCTAssertEqual(x % 3, 1)
+ XCTAssertEqual(x % y, x)
+ }
+
+ func testArithmetic_Signed() {
+ let x: _Int16 = 1000
+ let y: _Int16 = -1111
+ XCTAssertEqual(x + 1, 1001)
+ XCTAssertEqual(x + x, 2000)
+ XCTAssertEqual(x - (1 as _Int16), 999)
+ XCTAssertEqual(x - x, 0)
+ XCTAssertEqual(0 - x, -1000)
+ XCTAssertEqual(x + y, -111)
+ XCTAssertEqual(x - y, 2111)
+
+ XCTAssertEqual(x * 7, 7000)
+ XCTAssertEqual(y * 7, -7777)
+ XCTAssertEqual(x * -7, -7000)
+ XCTAssertEqual(y * -7, 7777)
+
+ XCTAssertEqual(x / 3, 333)
+ XCTAssertEqual(x / -3, -333)
+ XCTAssertEqual(x / x, 1)
+ XCTAssertEqual(x / y, 0)
+ XCTAssertEqual(y / x, -1)
+ XCTAssertEqual(y / y, 1)
+
+ XCTAssertEqual(x % 3, 1)
+ XCTAssertEqual(x % -3, 1)
+ XCTAssertEqual(y % 3, -1)
+ XCTAssertEqual(y % -3, -1)
+
+ XCTAssertEqual(-y, 1111)
+ XCTAssertEqual(-x, -1000)
+ }
+
+ func testNested() {
+ do {
+ let x = _UInt1024.max
+ let (y, o) = x.addingReportingOverflow(1)
+ XCTAssertEqual(y, 0)
+ XCTAssertTrue(y == (0 as Int))
+ XCTAssertTrue(o)
+ }
+
+ do {
+ let x = _Int1024.max
+ let (y, o) = x.addingReportingOverflow(1)
+ XCTAssertEqual(y, _Int1024.min)
+ XCTAssertLessThan(y, 0)
+ XCTAssertTrue(y < (0 as Int))
+ XCTAssertTrue(y < (0 as UInt))
+ XCTAssertTrue(o)
+ }
+
+ XCTAssertFalse(_UInt1024.isSigned)
+ XCTAssertEqual(_UInt1024.bitWidth, 1024)
+ XCTAssertTrue(_Int1024.isSigned)
+ XCTAssertEqual(_Int1024.bitWidth, 1024)
+
+ XCTAssertTrue(
+ _UInt1024.max.words.elementsEqual(
+ repeatElement(UInt.max, count: 1024 / UInt.bitWidth)
+ )
+ )
+ }
+
+ func testInitialization() {
+ XCTAssertTrue(_UInt16(UInt16.max) == UInt16.max)
+ XCTAssertNil(_UInt16(exactly: UInt32.max))
+ XCTAssertEqual(_UInt16(truncatingIfNeeded: UInt64.max), _UInt16.max)
+ }
+
+ func testInitialization_Overflow() {
+ // TODO: expectCrashLater()
+ // _ = _UInt16(UInt32.max)
+ }
+
+ func testMagnitude() {
+ XCTAssertTrue(_UInt16.min.magnitude == UInt16.min.magnitude)
+ XCTAssertTrue((42 as _UInt16).magnitude == (42 as UInt16).magnitude)
+ XCTAssertTrue(_UInt16.max.magnitude == UInt16.max.magnitude)
+
+ XCTAssertTrue(_Int16.min.magnitude == Int16.min.magnitude)
+ XCTAssertTrue((-42 as _Int16).magnitude == (-42 as Int16).magnitude)
+ XCTAssertTrue(_Int16().magnitude == Int16(0).magnitude) // See SR-6602.
+ XCTAssertTrue((42 as _Int16).magnitude == (42 as Int16).magnitude)
+ XCTAssertTrue(_Int16.max.magnitude == Int16.max.magnitude)
+ }
+
+ func testTwoWords() {
+ typealias DW = DoubleWidth
+
+ XCTAssertEqual(-1 as DW, DW(truncatingIfNeeded: -1 as Int8))
+
+ XCTAssertNil(Int(exactly: DW(Int.min) - 1))
+ XCTAssertNil(Int(exactly: DW(Int.max) + 1))
+
+ XCTAssertTrue(DW(Int.min) - 1 < Int.min)
+ XCTAssertTrue(DW(Int.max) + 1 > Int.max)
+ }
+
+ func testBitwise_LeftAndRightShifts() {
+ typealias _UInt64 = DoubleWidth>>
+ typealias _Int64 = DoubleWidth>>
+
+ func f(_ x: T, type: U.Type) {
+ let y = U(x)
+ XCTAssertEqual(T.bitWidth, U.bitWidth)
+ for i in -(T.bitWidth + 1)...(T.bitWidth + 1) {
+ XCTAssertTrue(x << i == y << i)
+ XCTAssertTrue(x >> i == y >> i)
+
+ XCTAssertTrue(x &<< i == y &<< i)
+ XCTAssertTrue(x &>> i == y &>> i)
+ }
+ }
+
+ f(1 as UInt64, type: _UInt64.self)
+ f(~(~0 as UInt64 >> 1), type: _UInt64.self)
+ f(UInt64.max, type: _UInt64.self)
+ // 0b01010101_10100101_11110000_10100101_11110000_10100101_11110000_10100101
+ f(17340530535757639845 as UInt64, type: _UInt64.self)
+
+ f(1 as Int64, type: _Int64.self)
+ f(Int64.min, type: _Int64.self)
+ f(Int64.max, type: _Int64.self)
+ // 0b01010101_10100101_11110000_10100101_11110000_10100101_11110000_10100101
+ f(6171603459878809765 as Int64, type: _Int64.self)
+ }
+
+ func testRemainder_ByZero() {
+ func f(_ x: _Int1024, _ y: _Int1024) -> _Int1024 {
+ return x % y
+ }
+ // TODO: expectCrashLater()
+ // _ = f(42, 0)
+ }
+
+ func testRemainder_ByMinusOne() {
+ func f(_ x: _Int256, _ y: _Int256) -> _Int256 {
+ return x.remainderReportingOverflow(dividingBy: y).partialValue
+ }
+ XCTAssertEqual(f(.max, -1), 0)
+ XCTAssertEqual(f(.min, -1), 0)
+ }
+
+ func testDivision_ByZero() {
+ func f(_ x: _Int1024, _ y: _Int1024) -> _Int1024 {
+ return x / y
+ }
+ // TODO: expectCrashLater()
+ // _ = f(42, 0)
+ }
+
+ func testDivision_ByMinusOne() {
+ func f(_ x: _Int1024) -> _Int1024 {
+ return x / -1
+ }
+ // TODO: expectCrashLater()
+ // _ = f(_Int1024.min)
+ }
+
+ func testMultipleOf() {
+ func isMultipleTest(type: T.Type) {
+ XCTAssertTrue(T.min.isMultiple(of: 2))
+ XCTAssertFalse(T.max.isMultiple(of: 10))
+ // Test that these do not crash.
+ XCTAssertTrue((0 as T).isMultiple(of: 0))
+ XCTAssertFalse((1 as T).isMultiple(of: 0))
+ XCTAssertTrue(T.min.isMultiple(of: 0 &- 1))
+ }
+ isMultipleTest(type: _Int128.self)
+ isMultipleTest(type: _UInt128.self)
+ }
+
+ func testMultiplication_ByMinusOne() {
+ func f(_ x: _Int1024) -> _Int1024 {
+ return x * -1
+ }
+ // TODO: expectCrashLater()
+ // _ = f(_Int1024.min)
+ }
+
+ func testConversions() {
+ XCTAssertTrue(_Int16(1 << 15 - 1) == Int(1 << 15 - 1))
+ XCTAssertTrue(_Int16(-1 << 15) == Int(-1 << 15))
+ XCTAssertTrue(_UInt16(1 << 16 - 1) == Int(1 << 16 - 1))
+ XCTAssertTrue(_UInt16(0) == Int(0))
+
+ XCTAssertTrue(_Int16(Double(1 << 15 - 1)) == Int(1 << 15 - 1))
+ XCTAssertTrue(_Int16(Double(-1 << 15)) == Int(-1 << 15))
+ XCTAssertTrue(_UInt16(Double(1 << 16 - 1)) == Int(1 << 16 - 1))
+ XCTAssertTrue(_UInt16(Double(0)) == Int(0))
+
+ XCTAssertTrue(_Int16(Double(1 << 15 - 1) + 0.9) == Int(1 << 15 - 1))
+ XCTAssertTrue(_Int16(Double(-1 << 15) - 0.9) == Int(-1 << 15))
+ XCTAssertTrue(_UInt16(Double(1 << 16 - 1) + 0.9) == Int(1 << 16 - 1))
+ XCTAssertTrue(_UInt16(Double(0) - 0.9) == Int(0))
+
+ XCTAssertEqual(_Int16(0.00001), 0)
+ XCTAssertEqual(_UInt16(0.00001), 0)
+ }
+
+ func testConversions_Exact() {
+ XCTAssertEqual(
+ _Int16(Double(1 << 15 - 1)),
+ _Int16(exactly: Double(1 << 15 - 1))
+ )
+ XCTAssertEqual(
+ _Int16(Double(-1 << 15)),
+ _Int16(exactly: Double(-1 << 15))
+ )
+ XCTAssertEqual(
+ _UInt16(Double(1 << 16 - 1)),
+ _UInt16(exactly: Double(1 << 16 - 1))
+ )
+ XCTAssertEqual(
+ _UInt16(Double(0)),
+ _UInt16(exactly: Double(0))
+ )
+
+ XCTAssertNil(_Int16(exactly: Double(1 << 15 - 1) + 0.9))
+ XCTAssertNil(_Int16(exactly: Double(-1 << 15) - 0.9))
+ XCTAssertNil(_UInt16(exactly: Double(1 << 16 - 1) + 0.9))
+ XCTAssertNil(_UInt16(exactly: Double(0) - 0.9))
+
+ XCTAssertNil(_Int16(exactly: Double(1 << 15)))
+ XCTAssertNil(_Int16(exactly: Double(-1 << 15) - 1))
+ XCTAssertNil(_UInt16(exactly: Double(1 << 16)))
+ XCTAssertNil(_UInt16(exactly: Double(-1)))
+
+ XCTAssertNil(_Int16(exactly: 0.00001))
+ XCTAssertNil(_UInt16(exactly: 0.00001))
+
+ XCTAssertNil(_UInt16(exactly: Double.nan))
+ XCTAssertNil(_UInt16(exactly: Float.nan))
+ XCTAssertNil(_UInt16(exactly: Double.infinity))
+ XCTAssertNil(_UInt16(exactly: Float.infinity))
+ }
+
+ func testConversions_SignedMax() {
+ // TODO: expectCrashLater()
+ // _ = _Int16(1 << 15)
+ }
+
+ func testConversions_SignedMin() {
+ // TODO: expectCrashLater()
+ // _ = _Int16(-1 << 15 - 1)
+ }
+
+ func testConversions_UnsignedMax() {
+ // TODO: expectCrashLater()
+ // _ = _UInt16(1 << 16)
+ }
+
+ func testConversions_UnsignedMin() {
+ // TODO: expectCrashLater()
+ // _ = _UInt16(-1)
+ }
+
+ func testConversions_ToAndFromString(
+ _ expectedNumber: Number,
+ _ expectedString: String,
+ radix: Int,
+ file: StaticString = #filePath,
+ line: UInt = #line
+ ) {
+ let actualString = String(expectedNumber, radix: radix)
+ XCTAssertEqual(expectedString, actualString, file: file, line: line)
+ let actualNumber = Number(expectedString, radix: radix)
+ XCTAssertEqual(expectedNumber, actualNumber, file: file, line: line)
+ }
+
+ func testConversions_ToAndFromString_Binary() {
+ testConversions_ToAndFromString(
+ _Int128.max,
+ """
+ 111111111111111111111111111111111111111111111111111111111111111\
+ 1111111111111111111111111111111111111111111111111111111111111111
+ """,
+ radix: 2
+ )
+ testConversions_ToAndFromString(
+ _Int128.min,
+ """
+ -1000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000
+ """,
+ radix: 2
+ )
+ }
+
+ func testConversions_ToAndFromString_Decimal() {
+ testConversions_ToAndFromString(
+ _Int256.max,
+ """
+ 5789604461865809771178549250434395392663\
+ 4992332820282019728792003956564819967
+ """,
+ radix: 10
+ )
+ testConversions_ToAndFromString(
+ _Int256.min,
+ """
+ -5789604461865809771178549250434395392663\
+ 4992332820282019728792003956564819968
+ """,
+ radix: 10
+ )
+ }
+
+ func testConversions_ToAndFromString_Hexadecimal() {
+ testConversions_ToAndFromString(
+ _Int512.max,
+ """
+ 7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\
+ ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ """,
+ radix: 16
+ )
+ testConversions_ToAndFromString(
+ _Int512.min,
+ """
+ -8000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000
+ """,
+ radix: 16
+ )
+ }
+
+ func testWords() {
+ XCTAssertTrue(_Int16(0).words.elementsEqual([0]))
+ XCTAssertTrue(_Int16(1).words.elementsEqual([1]))
+ XCTAssertTrue(_Int16(-1).words.elementsEqual([UInt.max]))
+ XCTAssertTrue(_Int16(256).words.elementsEqual([256]))
+ XCTAssertTrue(_Int16(-256).words.elementsEqual([UInt.max - 255]))
+ XCTAssertTrue(_Int16.max.words.elementsEqual([32767]))
+ XCTAssertTrue(_Int16.min.words.elementsEqual([UInt.max - 32767]))
+
+ XCTAssertTrue(
+ (0 as _Int1024).words.elementsEqual(
+ repeatElement(0 as UInt, count: 1024 / UInt.bitWidth)
+ )
+ )
+ XCTAssertTrue(
+ (-1 as _Int1024).words.elementsEqual(
+ repeatElement(UInt.max, count: 1024 / UInt.bitWidth)
+ )
+ )
+ XCTAssertTrue(
+ (1 as _Int1024).words.elementsEqual(
+ [1] + Array(repeating: 0, count: 1024 / UInt.bitWidth - 1)
+ )
+ )
+ }
+
+ func testConditionalConformance() {
+ func checkSignedIntegerConformance(_: T) {}
+ func checkUnsignedIntegerConformance(_: T) {}
+
+ checkSignedIntegerConformance(0 as _Int128)
+ checkSignedIntegerConformance(0 as _Int1024)
+
+ checkUnsignedIntegerConformance(0 as _UInt128)
+ checkUnsignedIntegerConformance(0 as _UInt1024)
+ }
+}
diff --git a/Tests/WindowsMain.swift b/Tests/WindowsMain.swift
index 3d378cac..520722af 100644
--- a/Tests/WindowsMain.swift
+++ b/Tests/WindowsMain.swift
@@ -2,7 +2,7 @@
//
// This source file is part of the Swift Numerics open source project
//
-// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors
+// Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
@@ -114,6 +114,39 @@ extension IntegerUtilitiesShiftTests {
])
}
+extension IntegerUtilitiesTests.DoubleWidthTests {
+ static var all = testCase([
+ ("testArithmetic_Signed", IntegerUtilitiesTests.DoubleWidthTests.testArithmetic_Signed),
+ ("testArithmetic_Unsigned", IntegerUtilitiesTests.DoubleWidthTests.testArithmetic_Unsigned),
+ ("testBitwise_LeftAndRightShifts", IntegerUtilitiesTests.DoubleWidthTests.testBitwise_LeftAndRightShifts),
+ ("testCompileTime_SR_6947", IntegerUtilitiesTests.DoubleWidthTests.testCompileTime_SR_6947),
+ ("testConditionalConformance", IntegerUtilitiesTests.DoubleWidthTests.testConditionalConformance),
+ ("testConversions", IntegerUtilitiesTests.DoubleWidthTests.testConversions),
+ ("testConversions_Exact", IntegerUtilitiesTests.DoubleWidthTests.testConversions_Exact),
+ ("testConversions_SignedMax", IntegerUtilitiesTests.DoubleWidthTests.testConversions_SignedMax),
+ ("testConversions_SignedMin", IntegerUtilitiesTests.DoubleWidthTests.testConversions_SignedMin),
+ ("testConversions_ToAndFromString_Binary", IntegerUtilitiesTests.DoubleWidthTests.testConversions_ToAndFromString_Binary),
+ ("testConversions_ToAndFromString_Decimal", IntegerUtilitiesTests.DoubleWidthTests.testConversions_ToAndFromString_Decimal),
+ ("testConversions_ToAndFromString_Hexadecimal", IntegerUtilitiesTests.DoubleWidthTests.testConversions_ToAndFromString_Hexadecimal),
+ ("testConversions_UnsignedMax", IntegerUtilitiesTests.DoubleWidthTests.testConversions_UnsignedMax),
+ ("testConversions_UnsignedMin", IntegerUtilitiesTests.DoubleWidthTests.testConversions_UnsignedMin),
+ ("testDivision_ByMinusOne", IntegerUtilitiesTests.DoubleWidthTests.testDivision_ByMinusOne),
+ ("testDivision_ByZero", IntegerUtilitiesTests.DoubleWidthTests.testDivision_ByZero),
+ ("testInitialization", IntegerUtilitiesTests.DoubleWidthTests.testInitialization),
+ ("testInitialization_Overflow", IntegerUtilitiesTests.DoubleWidthTests.testInitialization_Overflow),
+ ("testLiterals", IntegerUtilitiesTests.DoubleWidthTests.testLiterals),
+ ("testLiterals_Underflow", IntegerUtilitiesTests.DoubleWidthTests.testLiterals_Underflow),
+ ("testMagnitude", IntegerUtilitiesTests.DoubleWidthTests.testMagnitude),
+ ("testMultipleOf", IntegerUtilitiesTests.DoubleWidthTests.testMultipleOf),
+ ("testMultiplication_ByMinusOne", IntegerUtilitiesTests.DoubleWidthTests.testMultiplication_ByMinusOne),
+ ("testNested", IntegerUtilitiesTests.DoubleWidthTests.testNested),
+ ("testRemainder_ByMinusOne", IntegerUtilitiesTests.DoubleWidthTests.testRemainder_ByMinusOne),
+ ("testRemainder_ByZero", IntegerUtilitiesTests.DoubleWidthTests.testRemainder_ByZero),
+ ("testTwoWords", IntegerUtilitiesTests.DoubleWidthTests.testTwoWords),
+ ("testWords", IntegerUtilitiesTests.DoubleWidthTests.testWords),
+ ])
+}
+
var testCases = [
ComplexTests.ApproximateEqualityTests.all,
RealTests.ApproximateEqualityTests.all,
@@ -125,6 +158,7 @@ var testCases = [
IntegerUtilitiesGCDTests.all,
IntegerUtilitiesRotateTests.all,
IntegerUtilitiesShiftTests.all,
+ IntegerUtilitiesTests.DoubleWidthTests.all,
]
#if swift(>=5.3) && canImport(_Differentiation)