Skip to content

Commit 42aef66

Browse files
author
Peter Simonyi
committed
Implement Checked{Add,Sub,Mul,Div}
1 parent 7184efc commit 42aef66

File tree

1 file changed

+72
-5
lines changed

1 file changed

+72
-5
lines changed

src/lib.rs

+72-5
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use std::str::FromStr;
3333
use bigint::{BigInt, BigUint, Sign};
3434

3535
use integer::Integer;
36-
use traits::{FromPrimitive, Float, PrimInt, Num, Signed, Zero, One, Bounded, NumCast};
36+
use traits::{FromPrimitive, Float, PrimInt, Num, Signed, Zero, One, Bounded, NumCast, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv};
3737

3838
/// Represents the ratio between 2 numbers.
3939
#[derive(Copy, Clone, Debug)]
@@ -442,7 +442,7 @@ impl<'a, 'b, T> Div<&'b Ratio<T>> for &'a Ratio<T>
442442
}
443443
}
444444

445-
// Abstracts the a/b `op` c/d = (a*d `op` b*d) / (b*d) pattern
445+
// Abstracts the a/b `op` c/d = (a*d `op` b*c) / (b*d) pattern
446446
macro_rules! arith_impl {
447447
(impl $imp:ident, $method:ident) => {
448448
forward_all_binop!(impl $imp, $method);
@@ -467,6 +467,53 @@ arith_impl!(impl Sub, sub);
467467
// a/b % c/d = (a*d % b*c)/(b*d)
468468
arith_impl!(impl Rem, rem);
469469

470+
// a/b * c/d = (a*c)/(b*d)
471+
impl<T> CheckedMul for Ratio<T>
472+
where T: Clone + Integer + CheckedMul
473+
{
474+
#[inline]
475+
fn checked_mul(&self, rhs: &Ratio<T>) -> Option<Ratio<T>> {
476+
Some(Ratio::new(self.numer.checked_mul(&rhs.numer)?,
477+
self.denom.checked_mul(&rhs.denom)?))
478+
}
479+
}
480+
481+
// (a/b) / (c/d) = (a*d)/(b*c)
482+
impl<T> CheckedDiv for Ratio<T>
483+
where T: Clone + Integer + CheckedMul
484+
{
485+
#[inline]
486+
fn checked_div(&self, rhs: &Ratio<T>) -> Option<Ratio<T>> {
487+
let bc = self.denom.checked_mul(&rhs.numer)?;
488+
if bc.is_zero() {
489+
None
490+
} else {
491+
Some(Ratio::new(self.numer.checked_mul(&rhs.denom)?, bc))
492+
}
493+
}
494+
}
495+
496+
// As arith_impl! but for Checked{Add,Sub} traits
497+
macro_rules! checked_arith_impl {
498+
(impl $imp:ident, $method:ident) => {
499+
impl<T: Clone + Integer + CheckedMul + $imp> $imp for Ratio<T> {
500+
#[inline]
501+
fn $method(&self, rhs: &Ratio<T>) -> Option<Ratio<T>> {
502+
let ad = self.numer.checked_mul(&rhs.denom)?;
503+
let bc = self.denom.checked_mul(&rhs.numer)?;
504+
let bd = self.denom.checked_mul(&rhs.denom)?;
505+
Some(Ratio::new(ad.$method(&bc)?, bd))
506+
}
507+
}
508+
}
509+
}
510+
511+
// a/b + c/d = (a*d + b*c)/(b*d)
512+
checked_arith_impl!(impl CheckedAdd, checked_add);
513+
514+
// a/b - c/d = (a*d - b*c)/(b*d)
515+
checked_arith_impl!(impl CheckedSub, checked_sub);
516+
470517
impl<T> Neg for Ratio<T>
471518
where T: Clone + Integer + Neg<Output = T>
472519
{
@@ -1102,12 +1149,15 @@ mod test {
11021149
mod arith {
11031150
use super::{_0, _1, _2, _1_2, _3_2, _NEG1_2, to_big};
11041151
use super::super::{Ratio, Rational};
1152+
use traits::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv};
11051153

11061154
#[test]
11071155
fn test_add() {
11081156
fn test(a: Rational, b: Rational, c: Rational) {
11091157
assert_eq!(a + b, c);
11101158
assert_eq!(to_big(a) + to_big(b), to_big(c));
1159+
assert_eq!(a.checked_add(&b), Some(c));
1160+
assert_eq!(to_big(a).checked_add(&to_big(b)), Some(to_big(c)));
11111161
}
11121162

11131163
test(_1, _1_2, _3_2);
@@ -1120,7 +1170,9 @@ mod test {
11201170
fn test_sub() {
11211171
fn test(a: Rational, b: Rational, c: Rational) {
11221172
assert_eq!(a - b, c);
1123-
assert_eq!(to_big(a) - to_big(b), to_big(c))
1173+
assert_eq!(to_big(a) - to_big(b), to_big(c));
1174+
assert_eq!(a.checked_sub(&b), Some(c));
1175+
assert_eq!(to_big(a).checked_sub(&to_big(b)), Some(to_big(c)));
11241176
}
11251177

11261178
test(_1, _1_2, _1_2);
@@ -1132,7 +1184,9 @@ mod test {
11321184
fn test_mul() {
11331185
fn test(a: Rational, b: Rational, c: Rational) {
11341186
assert_eq!(a * b, c);
1135-
assert_eq!(to_big(a) * to_big(b), to_big(c))
1187+
assert_eq!(to_big(a) * to_big(b), to_big(c));
1188+
assert_eq!(a.checked_mul(&b), Some(c));
1189+
assert_eq!(to_big(a).checked_mul(&to_big(b)), Some(to_big(c)));
11361190
}
11371191

11381192
test(_1, _1_2, _1_2);
@@ -1144,7 +1198,9 @@ mod test {
11441198
fn test_div() {
11451199
fn test(a: Rational, b: Rational, c: Rational) {
11461200
assert_eq!(a / b, c);
1147-
assert_eq!(to_big(a) / to_big(b), to_big(c))
1201+
assert_eq!(to_big(a) / to_big(b), to_big(c));
1202+
assert_eq!(a.checked_div(&b), Some(c));
1203+
assert_eq!(to_big(a).checked_div(&to_big(b)), Some(to_big(c)));
11481204
}
11491205

11501206
test(_1, _1_2, _2);
@@ -1188,6 +1244,17 @@ mod test {
11881244
fn test_div_0() {
11891245
let _a = _1 / _0;
11901246
}
1247+
1248+
#[test]
1249+
fn test_checked_failures() {
1250+
let big = Ratio::new(128u8, 1);
1251+
let small = Ratio::new(1, 128u8);
1252+
assert_eq!(big.checked_add(&big), None);
1253+
assert_eq!(small.checked_sub(&big), None);
1254+
assert_eq!(big.checked_mul(&big), None);
1255+
assert_eq!(small.checked_div(&big), None);
1256+
assert_eq!(_1.checked_div(&_0), None);
1257+
}
11911258
}
11921259

11931260
#[test]

0 commit comments

Comments
 (0)