Skip to content

Commit 86f16f2

Browse files
committed
Support operations reporting overflow in Rational. This functionality was lost with the Swift 4 numeric API changes.
1 parent 4bb4f25 commit 86f16f2

File tree

1 file changed

+93
-54
lines changed

1 file changed

+93
-54
lines changed

Sources/NumberKit/Rational.swift

Lines changed: 93 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,31 @@ public protocol RationalNumber: SignedNumeric,
7979

8080
/// Raises this rational value to the power of `exp`.
8181
func toPower(of exp: Integer) -> Self
82+
83+
/// Adds `rhs` to `self` and reports the result together with a boolean indicating an overflow.
84+
func addingReportingOverflow(_ rhs: Self) -> (partialValue: Self, overflow: Bool)
85+
86+
/// Subtracts `rhs` from `self` and reports the result together with a boolean indicating
87+
/// an overflow.
88+
func subtractingReportingOverflow(_ rhs: Self) -> (partialValue: Self, overflow: Bool)
89+
90+
/// Multiplies `rhs` with `self` and reports the result together with a boolean indicating
91+
/// an overflow.
92+
func multipliedReportingOverflow(by rhs: Self) -> (partialValue: Self, overflow: Bool)
93+
94+
/// Divides `self` by `rhs` and reports the result together with a boolean indicating
95+
/// an overflow.
96+
func dividedReportingOverflow(by rhs: Self) -> (partialValue: Self, overflow: Bool)
97+
98+
/// Returns the greatest common denominator for `self` and `y` and a boolean which indicates
99+
/// whether there was an overflow.
100+
func gcdReportingOverflow(with y: Self) -> (partialValue: Self, overflow: Bool)
101+
102+
/// Returns the least common multiplier for `self` and `y` and a boolean which indicates
103+
/// whether there was an overflow.
104+
func lcmReportingOverflow(with y: Self) -> (partialValue: Self, overflow: Bool)
82105
}
83106

84-
// TODO: make this a static member of `Rational` once this is supported
85-
private let rationalSeparator: Character = "/"
86-
87107

88108
/// Struct `Rational<T>` implements the `RationalNumber` interface on top of the
89109
/// integer type `T`. `Rational<T>` always represents rational numbers in normalized
@@ -354,79 +374,81 @@ extension Rational: ExpressibleByStringLiteral {
354374
let (dn, overflow4) = dp.multipliedReportingOverflow(by: div)
355375
return (n1, n2, dn, overflow1 || overflow2 || overflow3 || overflow4)
356376
}
357-
358-
/// Add `lhs` and `rhs` and return a tuple consisting of the result and a boolean which
377+
378+
/// Compute the greatest common divisor for `x` and `y`.
379+
public static func gcdWithOverflow(_ x: T, _ y: T) -> (T, Bool) {
380+
var (x, y, (rest, overflow)) = (x, y, x.remainderReportingOverflow(dividingBy: y))
381+
while rest > 0 {
382+
x = y
383+
y = rest
384+
let (rem, overflow1) = x.remainderReportingOverflow(dividingBy: y)
385+
rest = rem
386+
overflow = overflow || overflow1
387+
}
388+
return (y, overflow)
389+
}
390+
391+
/// Compute the least common multiplier of `x` and `y`.
392+
public static func lcmWithOverflow(_ x: T, _ y: T) -> (T, Bool) {
393+
let (abs, overflow1) = x.multipliedReportingOverflow(by: y)
394+
let (gcd, overflow2) = Rational.gcdWithOverflow(x, y)
395+
return ((abs < 0 ? -abs : abs) / gcd, overflow1 || overflow2)
396+
}
397+
398+
/// Add `self` and `rhs` and return a tuple consisting of the result and a boolean which
359399
/// indicates whether there was an overflow.
360-
public static func addWithOverflow(_ lhs: Rational<T>, _ rhs: Rational<T>)
361-
-> (Rational<T>, Bool) {
362-
let (n1, n2, denom, overflow1) = Rational.commonDenomWithOverflow(lhs, rhs)
400+
public func addingReportingOverflow(_ rhs: Rational<T>)
401+
-> (partialValue: Rational<T>, overflow: Bool) {
402+
let (n1, n2, denom, overflow1) = Rational.commonDenomWithOverflow(self, rhs)
363403
let (numer, overflow2) = n1.addingReportingOverflow(n2)
364404
let (res, overflow3) = Rational.rationalWithOverflow(numer, denom)
365405
return (res, overflow1 || overflow2 || overflow3)
366406
}
367-
368-
/// Subtract `rhs` from `lhs` and return a tuple consisting of the result and a boolean which
407+
408+
/// Subtract `rhs` from `self` and return a tuple consisting of the result and a boolean which
369409
/// indicates whether there was an overflow.
370-
public static func subtractWithOverflow(_ lhs: Rational<T>, _ rhs: Rational<T>)
371-
-> (Rational<T>, Bool) {
372-
let (n1, n2, denom, overflow1) = Rational.commonDenomWithOverflow(lhs, rhs)
410+
public func subtractingReportingOverflow(_ rhs: Rational<T>)
411+
-> (partialValue: Rational<T>, overflow: Bool) {
412+
let (n1, n2, denom, overflow1) = Rational.commonDenomWithOverflow(self, rhs)
373413
let (numer, overflow2) = n1.subtractingReportingOverflow(n2)
374414
let (res, overflow3) = Rational.rationalWithOverflow(numer, denom)
375415
return (res, overflow1 || overflow2 || overflow3)
376416
}
377-
378-
/// Multiply `lhs` and `rhs` and return a tuple consisting of the result and a boolean which
417+
418+
/// Multiply `self` and `rhs` and return a tuple consisting of the result and a boolean which
379419
/// indicates whether there was an overflow.
380-
public static func multiplyWithOverflow(_ lhs: Rational<T>, _ rhs: Rational<T>)
381-
-> (Rational<T>, Bool) {
382-
let (numer, overflow1) = lhs.numerator.multipliedReportingOverflow(by: rhs.numerator)
383-
let (denom, overflow2) = lhs.denominator.multipliedReportingOverflow(by: rhs.denominator)
420+
public func multipliedReportingOverflow(by rhs: Rational<T>)
421+
-> (partialValue: Rational<T>, overflow: Bool) {
422+
let (numer, overflow1) = self.numerator.multipliedReportingOverflow(by: rhs.numerator)
423+
let (denom, overflow2) = self.denominator.multipliedReportingOverflow(by: rhs.denominator)
384424
let (res, overflow3) = Rational.rationalWithOverflow(numer, denom)
385425
return (res, overflow1 || overflow2 || overflow3)
386426
}
387427

388428
/// Divide `lhs` by `rhs` and return a tuple consisting of the result and a boolean which
389429
/// indicates whether there was an overflow.
390-
public static func divideWithOverflow(_ lhs: Rational<T>, _ rhs: Rational<T>)
391-
-> (Rational<T>, Bool) {
392-
let (numer, overflow1) = lhs.numerator.multipliedReportingOverflow(by: rhs.denominator)
393-
let (denom, overflow2) = lhs.denominator.multipliedReportingOverflow(by: rhs.numerator)
430+
public func dividedReportingOverflow(by rhs: Rational<T>)
431+
-> (partialValue: Rational<T>, overflow: Bool) {
432+
let (numer, overflow1) = self.numerator.multipliedReportingOverflow(by: rhs.denominator)
433+
let (denom, overflow2) = self.denominator.multipliedReportingOverflow(by: rhs.numerator)
394434
let (res, overflow3) = Rational.rationalWithOverflow(numer, denom)
395435
return (res, overflow1 || overflow2 || overflow3)
396436
}
397-
398-
/// Compute the greatest common divisor for `x` and `y`.
399-
public static func gcdWithOverflow(_ x: T, _ y: T) -> (T, Bool) {
400-
var (x, y, (rest, overflow)) = (x, y, x.remainderReportingOverflow(dividingBy: y))
401-
while rest > 0 {
402-
x = y
403-
y = rest
404-
let (rem, overflow1) = x.remainderReportingOverflow(dividingBy: y)
405-
rest = rem
406-
overflow = overflow || overflow1
407-
}
408-
return (y, overflow)
409-
}
410-
411-
/// Compute the least common multiplier of `x` and `y`.
412-
public static func lcmWithOverflow(_ x: T, _ y: T) -> (T, Bool) {
413-
let (abs, overflow1) = x.multipliedReportingOverflow(by: y)
414-
let (gcd, overflow2) = Rational.gcdWithOverflow(x, y)
415-
return ((abs < 0 ? -abs : abs) / gcd, overflow1 || overflow2)
416-
}
417-
418-
/// Returns the greatest common denominator for the two given rational numbers and a boolean
419-
/// which indicates whether there was an overflow.
420-
public static func gcdWithOverflow(_ x: Rational<T>, _ y: Rational<T>) -> (Rational<T>, Bool) {
421-
let (numer, overflow1) = Rational.gcdWithOverflow(x.numerator, y.numerator)
422-
let (denom, overflow2) = Rational.lcmWithOverflow(x.denominator, y.denominator)
437+
438+
/// Returns the greatest common denominator for `self` and `y` and a boolean which indicates
439+
/// whether there was an overflow.
440+
public func gcdReportingOverflow(with y: Rational<T>)
441+
-> (partialValue: Rational<T>, overflow: Bool) {
442+
let (numer, overflow1) = Rational.gcdWithOverflow(self.numerator, y.numerator)
443+
let (denom, overflow2) = Rational.lcmWithOverflow(self.denominator, y.denominator)
423444
return (Rational(numer, denom), overflow1 || overflow2)
424445
}
425-
426-
/// Returns the least common multiplier for the two given rational numbers and a boolean
427-
/// which indicates whether there was an overflow.
428-
public static func lcmWithOverflow(_ x: Rational<T>, _ y: Rational<T>) -> (Rational<T>, Bool) {
429-
let (xn, yn, denom, overflow1) = Rational.commonDenomWithOverflow(x, y)
446+
447+
/// Returns the least common multiplier for `self` and `y` and a boolean which indicates
448+
/// whether there was an overflow.
449+
public func lcmReportingOverflow(with y: Rational<T>)
450+
-> (partialValue: Rational<T>, overflow: Bool) {
451+
let (xn, yn, denom, overflow1) = Rational.commonDenomWithOverflow(self, y)
430452
let (numer, overflow2) = Rational.lcmWithOverflow(xn, yn)
431453
return (Rational(numer, denom), overflow1 || overflow2)
432454
}
@@ -492,6 +514,21 @@ public func **= <R: RationalNumber>(lhs: inout R, exp: R.Integer) {
492514
lhs = lhs.toPower(of: exp)
493515
}
494516

517+
/// Returns the sum of `lhs` and `rhs`.
518+
public func &+ <R: RationalNumber>(lhs: R, rhs: R) -> R {
519+
return lhs.addingReportingOverflow(rhs).partialValue
520+
}
521+
522+
/// Returns the difference between `lhs` and `rhs`.
523+
public func &- <R: RationalNumber>(lhs: R, rhs: R) -> R {
524+
return lhs.subtractingReportingOverflow(rhs).partialValue
525+
}
526+
527+
/// Multiplies `lhs` with `rhs` and returns the result.
528+
public func &* <R: RationalNumber>(lhs: R, rhs: R) -> R {
529+
return lhs.multipliedReportingOverflow(by: rhs).partialValue
530+
}
531+
495532
/// Returns true if `lhs` is less than `rhs`, false otherwise.
496533
public func < <R: RationalNumber>(lhs: R, rhs: R) -> Bool {
497534
return lhs.compare(to: rhs) < 0
@@ -522,3 +559,5 @@ public func != <R: RationalNumber>(lhs: R, rhs: R) -> Bool {
522559
return lhs.compare(to: rhs) != 0
523560
}
524561

562+
// TODO: make this a static member of `Rational` once this is supported
563+
private let rationalSeparator: Character = "/"

0 commit comments

Comments
 (0)