diff --git a/Sources/ANKFoundation/ANKBinaryInteger.swift b/Sources/ANKFoundation/ANKBinaryInteger.swift index 43b7f833..b0a2d4e4 100644 --- a/Sources/ANKFoundation/ANKBinaryInteger.swift +++ b/Sources/ANKFoundation/ANKBinaryInteger.swift @@ -132,6 +132,29 @@ public protocol ANKBinaryInteger: ANKBitPatternConvertible, BinaryInteger, Senda /// @inlinable func quotientAndRemainder(dividingBy divisor: Self) -> QR + /// Returns the quotient and remainder of this value divided by the given value, + /// and returns a value indicating whether overflow occurred. In the case of overflow, + /// the result is either truncated or, if undefined, this value. + /// + /// ```swift + /// let a = Int8(-128).quotientAndRemainderReportingOverflow(dividingBy: Int8( 3)) + /// a.partialValue.quotient // Int8( -42) + /// a.partialValue.remainder // Int8( 2) + /// a.overflow // false + /// + /// let b = Int8(-128).quotientAndRemainderReportingOverflow(dividingBy: Int8( 0)) + /// b.partialValue.quotient // Int8(-128) + /// b.partialValue.remainder // Int8(-128) + /// b.overflow // true + /// + /// let c = Int8(-128).quotientAndRemainderReportingOverflow(dividingBy: Int8(-1)) + /// c.partialValue.quotient // Int8(-128) + /// c.partialValue.remainder // Int8( 0) + /// c.overflow // true + /// ``` + /// + @inlinable func quotientAndRemainderReportingOverflow(dividingBy divisor: Self) -> PVO> + //=------------------------------------------------------------------------= // MARK: Details x Two's Complement //=------------------------------------------------------------------------= @@ -260,6 +283,21 @@ extension ANKBinaryInteger where Self: FixedWidthInteger { precondition(!pvo.overflow) return pvo.partialValue as Self } + + /// Returns the quotient and remainder of this value divided by the given value. + /// + /// ```swift + /// Int8( 7).quotientAndRemainder(dividingBy: Int8( 3)) // (quotient: Int8( 2), remainder: Int8( 1)) + /// Int8( 7).quotientAndRemainder(dividingBy: Int8(-3)) // (quotient: Int8(-2), remainder: Int8( 1)) + /// Int8(-7).quotientAndRemainder(dividingBy: Int8( 3)) // (quotient: Int8(-2), remainder: Int8(-1)) + /// Int8(-7).quotientAndRemainder(dividingBy: Int8(-3)) // (quotient: Int8( 2), remainder: Int8(-1)) + /// ``` + /// + @_transparent public func quotientAndRemainder(dividingBy divisor: Self) -> QR { + let qro: PVO> = self.quotientAndRemainderReportingOverflow(dividingBy: divisor) + precondition(!qro.overflow) + return qro.partialValue as QR + } } //*============================================================================* diff --git a/Sources/ANKFoundation/ANKCoreInteger.swift b/Sources/ANKFoundation/ANKCoreInteger.swift index 227c8714..f7453e9b 100644 --- a/Sources/ANKFoundation/ANKCoreInteger.swift +++ b/Sources/ANKFoundation/ANKCoreInteger.swift @@ -94,6 +94,12 @@ extension ANKCoreInteger { return pvo.overflow as Bool } + @_transparent public mutating func multiplyFullWidth(by amount: Self) -> Self { + let hl: HL = self.multipliedFullWidth(by: amount) + self = Self(bitPattern: hl.low) + return hl.high as Self + } + @_transparent public mutating func divideReportingOverflow(by amount: Self) -> Bool { let pvo: PVO = self.dividedReportingOverflow(by: amount) self = pvo.partialValue @@ -105,11 +111,12 @@ extension ANKCoreInteger { self = pvo.partialValue return pvo.overflow as Bool } - - @_transparent public mutating func multiplyFullWidth(by amount: Self) -> Self { - let hl: HL = self.multipliedFullWidth(by: amount) - self = Self(bitPattern: hl.low) - return hl.high as Self + + @_transparent public func quotientAndRemainderReportingOverflow(dividingBy divisor: Self) -> PVO> { + let quotient: PVO = self.dividedReportingOverflow(by: divisor) + let remainder: PVO = self.remainderReportingOverflow(dividingBy: divisor) + assert(quotient.overflow == remainder.overflow) + return PVO(QR(quotient.partialValue, remainder.partialValue), quotient.overflow) } //=------------------------------------------------------------------------= diff --git a/Sources/ANKFoundation/ANKLargeBinaryInteger.swift b/Sources/ANKFoundation/ANKLargeBinaryInteger.swift index 70010264..e1848e35 100644 --- a/Sources/ANKFoundation/ANKLargeBinaryInteger.swift +++ b/Sources/ANKFoundation/ANKLargeBinaryInteger.swift @@ -166,6 +166,29 @@ public protocol ANKLargeBinaryInteger: ANKBinaryInteger where Magnitude: /// @inlinable func quotientAndRemainder(dividingBy divisor: Digit) -> QR + /// Returns the quotient and remainder of this value divided by the given value, + /// and returns a value indicating whether overflow occurred. In the case of overflow, + /// the result is either truncated or, if undefined, this value. + /// + /// ```swift + /// let a = Int256( 7).quotientAndRemainderReportingOverflow(dividingBy: Int( 3)) + /// a.partialValue.quotient // Int256( 2) + /// a.partialValue.remainder // Int256( 1) + /// a.overflow // false + /// + /// let b = Int256.min.quotientAndRemainderReportingOverflow(dividingBy: Int( 0)) + /// b.partialValue.quotient // Int256.min + /// b.partialValue.remainder // Int256.min + /// b.overflow // true + /// + /// let c = Int256.min.quotientAndRemainderReportingOverflow(dividingBy: Int(-1)) + /// c.partialValue.quotient // Int256.min + /// c.partialValue.remainder // Int256( 0) + /// c.overflow // true + /// ``` + /// + @inlinable func quotientAndRemainderReportingOverflow(dividingBy divisor: Digit) -> PVO> + //=------------------------------------------------------------------------= // MARK: Details x Multiplication //=------------------------------------------------------------------------= @@ -294,6 +317,21 @@ extension ANKLargeBinaryInteger { precondition(!pvo.overflow) return pvo.partialValue as Digit } + + /// Returns the quotient and remainder of this value divided by the given value. + /// + /// ```swift + /// Int256( 7).quotientAndRemainder(dividingBy: Int( 3)) // (quotient: Int256( 2), remainder: Int( 1)) + /// Int256( 7).quotientAndRemainder(dividingBy: Int(-3)) // (quotient: Int256(-2), remainder: Int( 1)) + /// Int256(-7).quotientAndRemainder(dividingBy: Int( 3)) // (quotient: Int256(-2), remainder: Int(-1)) + /// Int256(-7).quotientAndRemainder(dividingBy: Int(-3)) // (quotient: Int256( 2), remainder: Int(-1)) + /// ``` + /// + @_disfavoredOverload @_transparent public func quotientAndRemainder(dividingBy divisor: Digit) -> QR { + let qro: PVO> = self.quotientAndRemainderReportingOverflow(dividingBy: divisor) + precondition(!qro.overflow) + return qro.partialValue as QR + } } //*============================================================================* diff --git a/Sources/ANKFullWidthKit/ANKFullWidth+Division+Digit.swift b/Sources/ANKFullWidthKit/ANKFullWidth+Division+Digit.swift index b1cad523..a51d1ee1 100644 --- a/Sources/ANKFullWidthKit/ANKFullWidth+Division+Digit.swift +++ b/Sources/ANKFullWidthKit/ANKFullWidth+Division+Digit.swift @@ -19,66 +19,59 @@ extension ANKFullWidth { // MARK: Transformations //=------------------------------------------------------------------------= - @_disfavoredOverload @inlinable public mutating func divideReportingOverflow(by divisor: Digit) -> Bool { + @_disfavoredOverload @_transparent public mutating func divideReportingOverflow(by divisor: Digit) -> Bool { let pvo: PVO = self.dividedReportingOverflow(by: divisor) self = pvo.partialValue return pvo.overflow as Bool } - @_disfavoredOverload @inlinable public func dividedReportingOverflow(by divisor: Digit) -> PVO { - //=--------------------------------------= - if divisor.isZero { - return PVO(self, true) - } - //=--------------------------------------= - if Self.isSigned, divisor == (-1 as Digit), self == Self.min { - return PVO(self, true) - } - //=--------------------------------------= - let qr: QR = self.quotientAndRemainder(dividingBy: divisor) - return PVO(qr.quotient, false) + @_disfavoredOverload @_transparent public func dividedReportingOverflow(by divisor: Digit) -> PVO { + let qro: PVO> = self.quotientAndRemainderReportingOverflow(dividingBy: divisor) + return PVO(qro.partialValue.quotient, qro.overflow) } - @_disfavoredOverload @inlinable public mutating func formRemainderReportingOverflow(dividingBy divisor: Digit) -> Bool { + @_disfavoredOverload @_transparent public mutating func formRemainderReportingOverflow(dividingBy divisor: Digit) -> Bool { let pvo: PVO = self.remainderReportingOverflow(dividingBy: divisor) self = Self(digit: pvo.partialValue) return pvo.overflow as Bool } - @_disfavoredOverload @inlinable public func remainderReportingOverflow(dividingBy divisor: Digit) -> PVO { - //=--------------------------------------= - if divisor.isZero { - return PVO(Digit(), true) - } - //=--------------------------------------= - if Self.isSigned, divisor == (-1 as Digit), self == Self.min { - return PVO(Digit(), true) - } - //=--------------------------------------= - let qr: QR = self.quotientAndRemainder(dividingBy: divisor) - return PVO(qr.remainder, false) + @_disfavoredOverload @_transparent public func remainderReportingOverflow(dividingBy divisor: Digit) -> PVO { + let qro: PVO> = self.quotientAndRemainderReportingOverflow(dividingBy: divisor) + return PVO(qro.partialValue.remainder, qro.overflow) } - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - @_disfavoredOverload @inlinable public func quotientAndRemainder(dividingBy divisor: Digit) -> QR { + @_disfavoredOverload @inlinable public func quotientAndRemainderReportingOverflow(dividingBy divisor: Digit) -> PVO> { let dividendIsLessThanZero: Bool = self.isLessThanZero let divisorIsLessThanZero: Bool = divisor.isLessThanZero //=--------------------------------------= - var qr: QR = self.magnitude._quotientAndRemainderAsUnsigned(dividingBy: divisor.magnitude) + let qro_ = self.magnitude._quotientAndRemainderReportingOverflowAsUnsigned(dividingBy: divisor.magnitude) + var qro = PVO(QR(Self(bitPattern: qro_.partialValue.quotient), Digit(bitPattern: qro_.partialValue.remainder)), qro_.overflow) //=--------------------------------------= + if qro.overflow { + assert(divisor.isZero) + assert(qro.partialValue.quotient == self) + assert(qro.partialValue.remainder == Digit()) + return qro + } + if dividendIsLessThanZero != divisorIsLessThanZero { - let overflow = qr.quotient._negateReportingOverflowAsSigned() - precondition(!overflow, "quotient overflowed during division") + qro.partialValue.quotient.formTwosComplement() + } + + if dividendIsLessThanZero, divisorIsLessThanZero, qro.partialValue.quotient.isLessThanZero { + assert(Self.isSigned && self == Self.min && divisor == -1) + assert(qro.partialValue.quotient == self) + assert(qro.partialValue.remainder == Digit()) + qro.overflow = true + return qro } if dividendIsLessThanZero { - qr.remainder.formTwosComplement() + qro.partialValue.remainder.formTwosComplement() } //=--------------------------------------= - return QR(Self(bitPattern: qr.quotient), Digit(bitPattern: qr.remainder)) + return qro as PVO> } } @@ -92,14 +85,17 @@ extension ANKFullWidth where High == High.Magnitude { // MARK: Transformations //=------------------------------------------------------------------------= - @inlinable func _quotientAndRemainderAsUnsigned(dividingBy divisor: Digit) -> QR { + @inlinable func _quotientAndRemainderReportingOverflowAsUnsigned(dividingBy divisor: Digit) -> PVO> { var quotient = self - let remainder = quotient._formQuotientReportingRemainderAsUnsigned(dividingBy: divisor) - return QR(quotient, remainder) + let remainder = quotient._formQuotientReportingRemainderAndOverflowAsUnsigned(dividingBy: divisor) + return PVO(QR(quotient, remainder.partialValue), remainder.overflow) } - @inlinable mutating func _formQuotientReportingRemainderAsUnsigned(dividingBy divisor: Digit) -> Digit { - precondition(!divisor.isZero, "division by zero") + @inlinable mutating func _formQuotientReportingRemainderAndOverflowAsUnsigned(dividingBy divisor: Digit) -> PVO { + //=--------------------------------------= + if divisor.isZero { + return PVO(UInt(), true) + } //=--------------------------------------= var remainder = UInt() //=--------------------------------------= @@ -111,6 +107,6 @@ extension ANKFullWidth where High == High.Magnitude { } } //=--------------------------------------= - return remainder + return PVO(remainder, false) } } diff --git a/Sources/ANKFullWidthKit/ANKFullWidth+Division.swift b/Sources/ANKFullWidthKit/ANKFullWidth+Division.swift index f99cd92a..5bc9c71d 100644 --- a/Sources/ANKFullWidthKit/ANKFullWidth+Division.swift +++ b/Sources/ANKFullWidthKit/ANKFullWidth+Division.swift @@ -19,66 +19,59 @@ extension ANKFullWidth { // MARK: Transformations //=------------------------------------------------------------------------= - @inlinable public mutating func divideReportingOverflow(by divisor: Self) -> Bool { + @_transparent public mutating func divideReportingOverflow(by divisor: Self) -> Bool { let pvo: PVO = self.dividedReportingOverflow(by: divisor) self = pvo.partialValue return pvo.overflow as Bool } - @inlinable public func dividedReportingOverflow(by divisor: Self) -> PVO { - //=--------------------------------------= - if divisor.isZero { - return PVO(self, true) - } - //=--------------------------------------= - if Self.isSigned, divisor == (-1 as Self), self == Self.min { - return PVO(self, true) - } - //=--------------------------------------= - let qr: QR = self.quotientAndRemainder(dividingBy: divisor) - return PVO(qr.quotient, false) + @_transparent public func dividedReportingOverflow(by divisor: Self) -> PVO { + let qro: PVO> = self.quotientAndRemainderReportingOverflow(dividingBy: divisor) + return PVO(qro.partialValue.quotient, qro.overflow) } - @inlinable public mutating func formRemainderReportingOverflow(dividingBy divisor: Self) -> Bool { + @_transparent public mutating func formRemainderReportingOverflow(dividingBy divisor: Self) -> Bool { let pvo: PVO = self.remainderReportingOverflow(dividingBy: divisor) self = pvo.partialValue return pvo.overflow as Bool } - @inlinable public func remainderReportingOverflow(dividingBy divisor: Self) -> PVO { - //=--------------------------------------= - if divisor.isZero { - return PVO(self, true) - } - //=--------------------------------------= - if Self.isSigned, divisor == (-1 as Self), self == Self.min { - return PVO(Self(), true) - } - //=--------------------------------------= - let qr: QR = self.quotientAndRemainder(dividingBy: divisor) - return PVO(qr.remainder, false) + @_transparent public func remainderReportingOverflow(dividingBy divisor: Self) -> PVO { + let qro: PVO> = self.quotientAndRemainderReportingOverflow(dividingBy: divisor) + return PVO(qro.partialValue.remainder, qro.overflow) } - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - @inlinable public func quotientAndRemainder(dividingBy divisor: Self) -> QR { + @inlinable public func quotientAndRemainderReportingOverflow(dividingBy divisor: Self) -> PVO> { let dividendIsLessThanZero: Bool = self.isLessThanZero let divisorIsLessThanZero: Bool = divisor.isLessThanZero //=--------------------------------------= - var qr: QR = self.magnitude._quotientAndRemainderAsUnsigned(dividingBy: divisor.magnitude) + let qro_ = self.magnitude._quotientAndRemainderReportingOverflowAsUnsigned(dividingBy: divisor.magnitude) + var qro = PVO(QR(Self(bitPattern: qro_.partialValue.quotient), Self(bitPattern: qro_.partialValue.remainder)), qro_.overflow) //=--------------------------------------= + if qro.overflow { + assert(divisor.isZero) + assert(qro.partialValue.quotient == self) + assert(qro.partialValue.remainder == self) + return qro + } + if dividendIsLessThanZero != divisorIsLessThanZero { - let overflow = qr.quotient._negateReportingOverflowAsSigned() - precondition(!overflow, "quotient overflowed during division") + qro.partialValue.quotient.formTwosComplement() + } + + if dividendIsLessThanZero, divisorIsLessThanZero, qro.partialValue.quotient.isLessThanZero { + assert(Self.isSigned && self == Self.min && divisor == -1) + assert(qro.partialValue.quotient == self) + assert(qro.partialValue.remainder == Self()) + qro.overflow = true + return qro } if dividendIsLessThanZero { - qr.remainder.formTwosComplement() + qro.partialValue.remainder.formTwosComplement() } //=--------------------------------------= - return QR(Self(bitPattern: qr.quotient), Self(bitPattern: qr.remainder)) + return qro as PVO> } //=------------------------------------------------------------------------= @@ -123,21 +116,23 @@ extension ANKFullWidth where High == High.Magnitude { @_specialize(where Self == ANKUInt256) @_specialize(where Self == ANKUInt384) @_specialize(where Self == ANKUInt512) - @inlinable func _quotientAndRemainderAsUnsigned(dividingBy divisor: Self) -> QR { + @inlinable func _quotientAndRemainderReportingOverflowAsUnsigned(dividingBy divisor: Self) -> PVO> { let divisor_ = divisor.minLastIndexReportingIsZeroOrMinusOne() - precondition(!divisor_.isZeroOrMinusOne, "division by zero") + if divisor_.isZeroOrMinusOne { + return PVO(QR(self, self), true) + } //=--------------------------------------= // Fast: Dividend <= Divisor //=--------------------------------------= if self <= divisor { - return self == divisor ? QR(1, Self()) : QR(Self(), self) + return self == divisor ? PVO(QR(1, Self()), false) : PVO(QR(Self(), self), false) } //=--------------------------------------= // Fast: Divisor Is One Word //=--------------------------------------= if divisor_.minLastIndex.isZero { - let qr: QR = self.quotientAndRemainder(dividingBy: divisor.first) - return QR(qr.quotient, Self(digit: qr.remainder)) + let qro: PVO> = self._quotientAndRemainderReportingOverflowAsUnsigned(dividingBy: divisor.first) + return PVO(QR(qro.partialValue.quotient, Self(digit: qro.partialValue.remainder)), qro.overflow) } //=--------------------------------------= let dividend_ = self.minLastIndexReportingIsZeroOrMinusOne() @@ -195,7 +190,7 @@ extension ANKFullWidth where High == High.Magnitude { //=--------------------------------------= assert(remainder.high.isZero) remainder.low._bitshiftRight(words: Int(), bits: shift) - return QR(quotient, remainder.low) + return PVO(QR(quotient, remainder.low), false) } //=------------------------------------------------------------------------= @@ -204,7 +199,8 @@ extension ANKFullWidth where High == High.Magnitude { @inlinable func _dividingFullWidthAsUnsigned(_ dividend: DoubleWidth) -> QR { let divisor = DoubleWidth(descending: HL(Self(), self)) - let qr: QR = dividend._quotientAndRemainderAsUnsigned(dividingBy: divisor) - return QR(qr.quotient.low, qr.remainder.low) + let qro: PVO> = dividend._quotientAndRemainderReportingOverflowAsUnsigned(dividingBy: divisor) + precondition(!qro.overflow) + return QR(qro.partialValue.quotient.low, qro.partialValue.remainder.low) } } diff --git a/Sources/ANKFullWidthKit/ANKFullWidth+Negation.swift b/Sources/ANKFullWidthKit/ANKFullWidth+Negation.swift index a100fb2e..fcc11746 100644 --- a/Sources/ANKFullWidthKit/ANKFullWidth+Negation.swift +++ b/Sources/ANKFullWidthKit/ANKFullWidth+Negation.swift @@ -19,35 +19,16 @@ extension ANKFullWidth where High: SignedInteger { // MARK: Transformations //=------------------------------------------------------------------------= - @_transparent public mutating func negateReportingOverflow() -> Bool { - self._negateReportingOverflowAsSigned() - } - - @_transparent public func negatedReportingOverflow() -> PVO { - self._negatedReportingOverflowAsSigned() - } -} - -//=----------------------------------------------------------------------------= -// MARK: + Agnostic -//=----------------------------------------------------------------------------= - -extension ANKFullWidth { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - @inlinable mutating func _negateReportingOverflowAsSigned() -> Bool { - let msb0: Bool = self.mostSignificantBit + @inlinable public mutating func negateReportingOverflow() -> Bool { + let msb0: Bool = self.isLessThanZero self.formTwosComplement() - let msb1: Bool = self.mostSignificantBit + let msb1: Bool = self.isLessThanZero return msb0 && msb1 } - @inlinable func _negatedReportingOverflowAsSigned() -> PVO { + @inlinable public func negatedReportingOverflow() -> PVO { var partialValue = self - let overflow: Bool = partialValue._negateReportingOverflowAsSigned() + let overflow: Bool = partialValue.negateReportingOverflow() return PVO(partialValue, overflow) } } diff --git a/Sources/ANKFullWidthKit/ANKFullWidth+Text.swift b/Sources/ANKFullWidthKit/ANKFullWidth+Text.swift index c07c01e5..b7101a40 100644 --- a/Sources/ANKFullWidthKit/ANKFullWidth+Text.swift +++ b/Sources/ANKFullWidthKit/ANKFullWidth+Text.swift @@ -171,7 +171,7 @@ extension ANKFullWidth where High == High.Magnitude { return withUnsafeTemporaryAllocation(of: UInt.self, capacity: capacity) { CHUNKS in var index = CHUNKS.startIndex rebasing: repeat { - CHUNKS[index] = magnitude._formQuotientReportingRemainderAsUnsigned(dividingBy: radix.power) + CHUNKS[index] = magnitude._formQuotientReportingRemainderAndOverflowAsUnsigned(dividingBy: radix.power).partialValue CHUNKS.formIndex(after: &index) } while !magnitude.isZero return String(chunks: CHUNKS[..(_ type: T.Type) where T: ANKCoreInteger { + let a = T( 7).quotientAndRemainderReportingOverflow(dividingBy: T( 3)) + XCTAssertEqual(a.partialValue.quotient, T( 2)) + XCTAssertEqual(a.partialValue.remainder, T( 1)) + XCTAssertEqual(a.overflow, false) + + let b = T( 7).quotientAndRemainderReportingOverflow(dividingBy: T( 0)) + XCTAssertEqual(b.partialValue.quotient, T( 7)) + XCTAssertEqual(b.partialValue.remainder, T( 7)) + XCTAssertEqual(b.overflow, true ) + + guard type.isSigned else { return } + + let c = T.min.quotientAndRemainderReportingOverflow(dividingBy: T(-1)) + XCTAssertEqual(c.partialValue.quotient, T.min) + XCTAssertEqual(c.partialValue.remainder, T( 0)) + XCTAssertEqual(c.overflow, true ) + } + + for type: T in types { + whereIs(type) + } + } +} + +#endif