Skip to content

Commit 7a5b207

Browse files
committed
Merge #11
11: Implement Checked{Add,Sub,Mul,Div} r=cuviper a=psimonyi When the `T` of a `Ratio<T>` supports checked arithmetic, the ratio should too. I'm not really sure whether the checked_* functions should be `#[inline]` or not. They are in this patch because the non-checked operations are and the checked ones don't look that much bigger.
2 parents 7184efc + 13e1678 commit 7a5b207

File tree

1 file changed

+81
-5
lines changed

1 file changed

+81
-5
lines changed

src/lib.rs

Lines changed: 81 additions & 5 deletions
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,62 @@ arith_impl!(impl Sub, sub);
467467
// a/b % c/d = (a*d % b*c)/(b*d)
468468
arith_impl!(impl Rem, rem);
469469

470+
// Like `std::try!` for Option<T>, unwrap the value or early-return None.
471+
// Since Rust 1.22 this can be replaced by the `?` operator.
472+
macro_rules! otry {
473+
($expr:expr) => (match $expr {
474+
Some(val) => val,
475+
None => return None,
476+
})
477+
}
478+
479+
// a/b * c/d = (a*c)/(b*d)
480+
impl<T> CheckedMul for Ratio<T>
481+
where T: Clone + Integer + CheckedMul
482+
{
483+
#[inline]
484+
fn checked_mul(&self, rhs: &Ratio<T>) -> Option<Ratio<T>> {
485+
Some(Ratio::new(otry!(self.numer.checked_mul(&rhs.numer)),
486+
otry!(self.denom.checked_mul(&rhs.denom))))
487+
}
488+
}
489+
490+
// (a/b) / (c/d) = (a*d)/(b*c)
491+
impl<T> CheckedDiv for Ratio<T>
492+
where T: Clone + Integer + CheckedMul
493+
{
494+
#[inline]
495+
fn checked_div(&self, rhs: &Ratio<T>) -> Option<Ratio<T>> {
496+
let bc = otry!(self.denom.checked_mul(&rhs.numer));
497+
if bc.is_zero() {
498+
None
499+
} else {
500+
Some(Ratio::new(otry!(self.numer.checked_mul(&rhs.denom)), bc))
501+
}
502+
}
503+
}
504+
505+
// As arith_impl! but for Checked{Add,Sub} traits
506+
macro_rules! checked_arith_impl {
507+
(impl $imp:ident, $method:ident) => {
508+
impl<T: Clone + Integer + CheckedMul + $imp> $imp for Ratio<T> {
509+
#[inline]
510+
fn $method(&self, rhs: &Ratio<T>) -> Option<Ratio<T>> {
511+
let ad = otry!(self.numer.checked_mul(&rhs.denom));
512+
let bc = otry!(self.denom.checked_mul(&rhs.numer));
513+
let bd = otry!(self.denom.checked_mul(&rhs.denom));
514+
Some(Ratio::new(otry!(ad.$method(&bc)), bd))
515+
}
516+
}
517+
}
518+
}
519+
520+
// a/b + c/d = (a*d + b*c)/(b*d)
521+
checked_arith_impl!(impl CheckedAdd, checked_add);
522+
523+
// a/b - c/d = (a*d - b*c)/(b*d)
524+
checked_arith_impl!(impl CheckedSub, checked_sub);
525+
470526
impl<T> Neg for Ratio<T>
471527
where T: Clone + Integer + Neg<Output = T>
472528
{
@@ -1102,12 +1158,15 @@ mod test {
11021158
mod arith {
11031159
use super::{_0, _1, _2, _1_2, _3_2, _NEG1_2, to_big};
11041160
use super::super::{Ratio, Rational};
1161+
use traits::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv};
11051162

11061163
#[test]
11071164
fn test_add() {
11081165
fn test(a: Rational, b: Rational, c: Rational) {
11091166
assert_eq!(a + b, c);
11101167
assert_eq!(to_big(a) + to_big(b), to_big(c));
1168+
assert_eq!(a.checked_add(&b), Some(c));
1169+
assert_eq!(to_big(a).checked_add(&to_big(b)), Some(to_big(c)));
11111170
}
11121171

11131172
test(_1, _1_2, _3_2);
@@ -1120,7 +1179,9 @@ mod test {
11201179
fn test_sub() {
11211180
fn test(a: Rational, b: Rational, c: Rational) {
11221181
assert_eq!(a - b, c);
1123-
assert_eq!(to_big(a) - to_big(b), to_big(c))
1182+
assert_eq!(to_big(a) - to_big(b), to_big(c));
1183+
assert_eq!(a.checked_sub(&b), Some(c));
1184+
assert_eq!(to_big(a).checked_sub(&to_big(b)), Some(to_big(c)));
11241185
}
11251186

11261187
test(_1, _1_2, _1_2);
@@ -1132,7 +1193,9 @@ mod test {
11321193
fn test_mul() {
11331194
fn test(a: Rational, b: Rational, c: Rational) {
11341195
assert_eq!(a * b, c);
1135-
assert_eq!(to_big(a) * to_big(b), to_big(c))
1196+
assert_eq!(to_big(a) * to_big(b), to_big(c));
1197+
assert_eq!(a.checked_mul(&b), Some(c));
1198+
assert_eq!(to_big(a).checked_mul(&to_big(b)), Some(to_big(c)));
11361199
}
11371200

11381201
test(_1, _1_2, _1_2);
@@ -1144,7 +1207,9 @@ mod test {
11441207
fn test_div() {
11451208
fn test(a: Rational, b: Rational, c: Rational) {
11461209
assert_eq!(a / b, c);
1147-
assert_eq!(to_big(a) / to_big(b), to_big(c))
1210+
assert_eq!(to_big(a) / to_big(b), to_big(c));
1211+
assert_eq!(a.checked_div(&b), Some(c));
1212+
assert_eq!(to_big(a).checked_div(&to_big(b)), Some(to_big(c)));
11481213
}
11491214

11501215
test(_1, _1_2, _2);
@@ -1188,6 +1253,17 @@ mod test {
11881253
fn test_div_0() {
11891254
let _a = _1 / _0;
11901255
}
1256+
1257+
#[test]
1258+
fn test_checked_failures() {
1259+
let big = Ratio::new(128u8, 1);
1260+
let small = Ratio::new(1, 128u8);
1261+
assert_eq!(big.checked_add(&big), None);
1262+
assert_eq!(small.checked_sub(&big), None);
1263+
assert_eq!(big.checked_mul(&big), None);
1264+
assert_eq!(small.checked_div(&big), None);
1265+
assert_eq!(_1.checked_div(&_0), None);
1266+
}
11911267
}
11921268

11931269
#[test]

0 commit comments

Comments
 (0)