Skip to content

Commit

Permalink
more quotient-remainder-overflow stuff (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Apr 22, 2023
1 parent e6c9130 commit 7bcd36f
Show file tree
Hide file tree
Showing 8 changed files with 493 additions and 95 deletions.
6 changes: 3 additions & 3 deletions Sources/ANKFoundation/ANKBinaryInteger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public protocol ANKBinaryInteger: ANKBitPatternConvertible, BinaryInteger, Senda
@inlinable mutating func divideReportingOverflow(by divisor: Self) -> Bool

/// Returns the quotient of this value divided by the given value,
/// and returns a value indicating whether overflow occurred.
/// along with a value indicating whether overflow occurred.
/// In the case of overflow, the result is either truncated or,
/// if undefined, this value.
///
Expand All @@ -109,7 +109,7 @@ public protocol ANKBinaryInteger: ANKBitPatternConvertible, BinaryInteger, Senda
@inlinable mutating func formRemainderReportingOverflow(dividingBy divisor: Self) -> Bool

/// Returns the remainder of this value divided by the given value,
/// and returns a value indicating whether overflow occurred.
/// along with a value indicating whether overflow occurred.
/// In the case of overflow, the result is either the entire remainder or,
/// if undefined, this value.
///
Expand All @@ -133,7 +133,7 @@ public protocol ANKBinaryInteger: ANKBitPatternConvertible, BinaryInteger, Senda
@inlinable func quotientAndRemainder(dividingBy divisor: Self) -> QR<Self, Self>

/// 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,
/// along with a value indicating whether overflow occurred. In the case of overflow,
/// the result is either truncated or, if undefined, this value.
///
/// ```swift
Expand Down
6 changes: 3 additions & 3 deletions Sources/ANKFoundation/ANKLargeBinaryInteger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public protocol ANKLargeBinaryInteger<Digit>: ANKBinaryInteger where Magnitude:
@inlinable mutating func divideReportingOverflow(by divisor: Digit) -> Bool

/// Returns the quotient of this value divided by the given value,
/// and returns a value indicating whether overflow occurred.
/// along with a value indicating whether overflow occurred.
/// In the case of overflow, the result is either truncated or,
/// if undefined, this value.
///
Expand All @@ -143,7 +143,7 @@ public protocol ANKLargeBinaryInteger<Digit>: ANKBinaryInteger where Magnitude:
@inlinable mutating func formRemainderReportingOverflow(dividingBy divisor: Digit) -> Bool

/// Returns the remainder of this value divided by the given value,
/// and returns a value indicating whether overflow occurred.
/// along with a value indicating whether overflow occurred.
/// In the case of overflow, the result is either the entire remainder
/// or, if undefined, zero.
///
Expand All @@ -167,7 +167,7 @@ public protocol ANKLargeBinaryInteger<Digit>: ANKBinaryInteger where Magnitude:
@inlinable func quotientAndRemainder(dividingBy divisor: Digit) -> QR<Self, Digit>

/// 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,
/// along with a value indicating whether overflow occurred. In the case of overflow,
/// the result is either truncated or, if undefined, this value.
///
/// ```swift
Expand Down
42 changes: 28 additions & 14 deletions Sources/ANKSignedKit/ANKSigned+Division+Digit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,31 @@ extension ANKSigned where Magnitude: ANKLargeBinaryInteger {
//=------------------------------------------------------------------------=

@_disfavoredOverload @_transparent public static func /=(lhs: inout Self, rhs: Digit) {
lhs = lhs / rhs
let overflow: Bool = lhs.divideReportingOverflow(by: rhs)
precondition(!overflow)
}

@_disfavoredOverload @_transparent public static func /(lhs: Self, rhs: Digit) -> Self {
lhs.quotientAndRemainder(dividingBy: rhs).quotient
let pvo: PVO<Self> = lhs.dividedReportingOverflow(by: rhs)
precondition(!pvo.overflow)
return pvo.partialValue as Self
}

@_disfavoredOverload @_transparent public static func %=(lhs: inout Self, rhs: Digit) {
lhs = Self(digit: lhs % rhs)
let overflow: Bool = lhs.formRemainderReportingOverflow(dividingBy: rhs)
precondition(!overflow)
}

@_disfavoredOverload @_transparent public static func %(lhs: Self, rhs: Digit) -> Digit {
lhs.quotientAndRemainder(dividingBy: rhs).remainder
let pvo: PVO<Digit> = lhs.remainderReportingOverflow(dividingBy: rhs)
precondition(!pvo.overflow)
return pvo.partialValue as Digit
}

@_disfavoredOverload @_transparent public func quotientAndRemainder(dividingBy divisor: Digit) -> QR<Self, Digit> {
let qro: PVO<QR<Self, Digit>> = self.quotientAndRemainderReportingOverflow(dividingBy: divisor)
precondition(!qro.overflow)
return qro.partialValue as QR<Self, Digit>
}

//=------------------------------------------------------------------------=
Expand All @@ -46,7 +58,9 @@ extension ANKSigned where Magnitude: ANKLargeBinaryInteger {
}

@_disfavoredOverload @inlinable public func dividedReportingOverflow(by divisor: Digit) -> PVO<Self> {
divisor.isZero ? PVO(self, true) : PVO(self / divisor, false)
let pvo: PVO<Magnitude> = self.magnitude.dividedReportingOverflow(by: divisor.magnitude)
let quotient = Self(pvo.partialValue, as: self.sign ^ divisor.sign)
return PVO(quotient, pvo.overflow)
}

@_disfavoredOverload @inlinable public mutating func formRemainderReportingOverflow(dividingBy divisor: Digit) -> Bool {
Expand All @@ -55,16 +69,16 @@ extension ANKSigned where Magnitude: ANKLargeBinaryInteger {
return pvo.overflow as Bool
}

@_disfavoredOverload @inlinable public func remainderReportingOverflow(dividingBy divisor: Digit) -> PVO<Digit> {
divisor.isZero ? PVO(Digit(), true) : PVO(self % divisor, false)
@_disfavoredOverload @_transparent public func remainderReportingOverflow(dividingBy divisor: Digit) -> PVO<Digit> {
let pvo: PVO<Magnitude.Digit> = self.magnitude.remainderReportingOverflow(dividingBy: divisor.magnitude)
let remainder = Digit(pvo.partialValue, as: self.sign)
return PVO(remainder, pvo.overflow)
}

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=

@_disfavoredOverload @inlinable public func quotientAndRemainder(dividingBy divisor: Digit) -> QR<Self, Digit> {
let qr: QR<Magnitude, Magnitude.Digit> = self.magnitude.quotientAndRemainder(dividingBy: divisor.magnitude)
return QR(Self(qr.quotient, as: self.sign ^ divisor.sign), Digit(qr.remainder, as: self.sign))
@_disfavoredOverload @inlinable public func quotientAndRemainderReportingOverflow(dividingBy divisor: Digit) -> PVO<QR<Self, Digit>> {
let qro: PVO<QR<Magnitude, Magnitude.Digit>> = self.magnitude.quotientAndRemainderReportingOverflow(dividingBy: divisor.magnitude)
let quotient = Self(qro.partialValue.quotient, as: self.sign ^ divisor.sign)
let remainder = Digit(qro.partialValue.remainder, as: self.sign /*----------*/)
return PVO(QR(quotient, remainder), qro.overflow)
}
}
44 changes: 30 additions & 14 deletions Sources/ANKSignedKit/ANKSigned+Division.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,31 @@ extension ANKSigned {
//=------------------------------------------------------------------------=

@_transparent public static func /=(lhs: inout Self, rhs: Self) {
lhs = lhs / rhs
let overflow: Bool = lhs.divideReportingOverflow(by: rhs)
precondition(!overflow)
}

@_transparent public static func /(lhs: Self, rhs: Self) -> Self {
lhs.quotientAndRemainder(dividingBy: rhs).quotient
let pvo: PVO<Self> = lhs.dividedReportingOverflow(by: rhs)
precondition(!pvo.overflow)
return pvo.partialValue as Self
}

@_transparent public static func %=(lhs: inout Self, rhs: Self) {
lhs = lhs % rhs
let overflow: Bool = lhs.formRemainderReportingOverflow(dividingBy: rhs)
precondition(!overflow)
}

@_transparent public static func %(lhs: Self, rhs: Self) -> Self {
lhs.quotientAndRemainder(dividingBy: rhs).remainder
let pvo: PVO<Self> = lhs.remainderReportingOverflow(dividingBy: rhs)
precondition(!pvo.overflow)
return pvo.partialValue as Self
}

@_transparent public func quotientAndRemainder(dividingBy divisor: Self) -> QR<Self, Self> {
let qro: PVO<QR<Self, Self>> = self.quotientAndRemainderReportingOverflow(dividingBy: divisor)
precondition(!qro.overflow)
return qro.partialValue as QR<Self, Self>
}

//=------------------------------------------------------------------------=
Expand All @@ -46,7 +58,9 @@ extension ANKSigned {
}

@inlinable public func dividedReportingOverflow(by divisor: Self) -> PVO<Self> {
divisor.isZero ? PVO(self, true) : PVO(self / divisor, false)
let pvo: PVO<Magnitude> = self.magnitude.dividedReportingOverflow(by: divisor.magnitude)
let quotient = Self(pvo.partialValue, as: self.sign ^ divisor.sign)
return PVO(quotient, pvo.overflow)
}

@inlinable public mutating func formRemainderReportingOverflow(dividingBy divisor: Self) -> Bool {
Expand All @@ -56,16 +70,16 @@ extension ANKSigned {
}

@inlinable public func remainderReportingOverflow(dividingBy divisor: Self) -> PVO<Self> {
divisor.isZero ? PVO(self, true) : PVO(self % divisor, false)
let pvo: PVO<Magnitude> = self.magnitude.remainderReportingOverflow(dividingBy: divisor.magnitude)
let remainder = Self(pvo.partialValue, as: self.sign)
return PVO(remainder, pvo.overflow)
}

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=

@inlinable public func quotientAndRemainder(dividingBy divisor: Self) -> QR<Self, Self> {
let qr: QR<Magnitude, Magnitude> = self.magnitude.quotientAndRemainder(dividingBy: divisor.magnitude)
return QR(Self(qr.quotient, as: self.sign ^ divisor.sign), Self(qr.remainder, as: self.sign))
@inlinable public func quotientAndRemainderReportingOverflow(dividingBy divisor: Self) -> PVO<QR<Self, Self>> {
let qro: PVO<QR<Magnitude, Magnitude>> = self.magnitude.quotientAndRemainderReportingOverflow(dividingBy: divisor.magnitude)
let quotient = Self(qro.partialValue.quotient, as: self.sign ^ divisor.sign)
let remainder = Self(qro.partialValue.remainder, as: self.sign /*----------*/)
return PVO(QR(quotient, remainder), qro.overflow)
}
}

Expand All @@ -81,6 +95,8 @@ extension ANKSigned where Magnitude: FixedWidthInteger {

@inlinable public func dividingFullWidth(_ dividend: HL<Self, Magnitude>) -> QR<Self, Self> {
let qr: QR<Magnitude, Magnitude> = self.magnitude.dividingFullWidth(HL(dividend.high.magnitude, dividend.low))
return QR(Self(qr.quotient, as: dividend.high.sign ^ self.sign), Self(qr.remainder, as: dividend.high.sign))
let quotient = Self(qr.quotient, as: dividend.high.sign ^ self.sign)
let remainder = Self(qr.remainder, as: dividend.high.sign /*-------*/)
return QR(quotient, remainder)
}
}
74 changes: 56 additions & 18 deletions Tests/ANKFoundationTests/ANKCoreInteger+Division.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,65 @@ final class ANKCoreIntegerTestsOnDivision: XCTestCase {
//=------------------------------------------------------------------------=

func testQuotientAndRemainderReportingOverflow() {
func whereIs<T>(_ 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 )
func whereIsSigned<T>(_ type: T.Type) where T: ANKCoreInteger {
var x: PVO<QR<T, T>>
//=----------------------------------=
// Divisor: 0, -1
//=----------------------------------=
x = T(7).quotientAndRemainderReportingOverflow(dividingBy: T( 0))
XCTAssertEqual(x.partialValue.quotient, T( 7))
XCTAssertEqual(x.partialValue.remainder, T( 7))
XCTAssertEqual(x.overflow, true)
//=----------------------------------=
x = T.min.quotientAndRemainderReportingOverflow(dividingBy: T(-1))
XCTAssertEqual(x.partialValue.quotient, T.min)
XCTAssertEqual(x.partialValue.remainder, T( 0))
XCTAssertEqual(x.overflow, true)
//=----------------------------------=
// Standard
//=----------------------------------=
x = T( 7).quotientAndRemainderReportingOverflow(dividingBy: T( 3))
XCTAssertEqual(x.partialValue.quotient, T( 2))
XCTAssertEqual(x.partialValue.remainder, T( 1))
XCTAssertEqual(x.overflow, false)
//=----------------------------------=
x = T( 7).quotientAndRemainderReportingOverflow(dividingBy: T(-3))
XCTAssertEqual(x.partialValue.quotient, T(-2))
XCTAssertEqual(x.partialValue.remainder, T( 1))
XCTAssertEqual(x.overflow, false)
//=----------------------------------=
x = T(-7).quotientAndRemainderReportingOverflow(dividingBy: T( 3))
XCTAssertEqual(x.partialValue.quotient, T(-2))
XCTAssertEqual(x.partialValue.remainder, T(-1))
XCTAssertEqual(x.overflow, false)
//=----------------------------------=
x = T(-7).quotientAndRemainderReportingOverflow(dividingBy: T(-3))
XCTAssertEqual(x.partialValue.quotient, T( 2))
XCTAssertEqual(x.partialValue.remainder, T(-1))
XCTAssertEqual(x.overflow, false)
}

func whereIsUnsigned<T>(_ type: T.Type) where T: ANKCoreInteger {
var x: PVO<QR<T, T>>
//=----------------------------------=
x = T(7).quotientAndRemainderReportingOverflow(dividingBy: T(0))
XCTAssertEqual(x.partialValue.quotient, T(7))
XCTAssertEqual(x.partialValue.remainder, T(7))
XCTAssertEqual(x.overflow, true)
//=----------------------------------=
x = T(7).quotientAndRemainderReportingOverflow(dividingBy: T(1))
XCTAssertEqual(x.partialValue.quotient, T(7))
XCTAssertEqual(x.partialValue.remainder, T(0))
XCTAssertEqual(x.overflow, false)
//=----------------------------------=
x = T(7).quotientAndRemainderReportingOverflow(dividingBy: T(2))
XCTAssertEqual(x.partialValue.quotient, T(3))
XCTAssertEqual(x.partialValue.remainder, T(1))
XCTAssertEqual(x.overflow, false)
}

for type: T in types {
whereIs(type)
type.isSigned ? whereIsSigned(type) : whereIsUnsigned(type)
}
}
}
Expand Down
Loading

0 comments on commit 7bcd36f

Please sign in to comment.