Make `binomial` and `IterBinomial` less likely to overflow

This commit is contained in:
Vinzent Steinberg 2017-03-26 15:57:25 +02:00
parent d4deba6947
commit a7b459c05e
1 changed files with 17 additions and 10 deletions

View File

@ -693,7 +693,11 @@ impl<T> Iterator for IterBinomial<T>
return None; return None;
} }
self.a = if !self.k.is_zero() { self.a = if !self.k.is_zero() {
(self.a.clone() * (self.n.clone() - self.k.clone() + T::one())) / self.k.clone() multiply_and_divide(
self.a.clone(),
self.n.clone() - self.k.clone() + T::one(),
self.k.clone()
)
} else { } else {
T::one() T::one()
}; };
@ -703,10 +707,12 @@ impl<T> Iterator for IterBinomial<T>
} }
/// Calculate r * a / b, avoiding overflows and fractions. /// Calculate r * a / b, avoiding overflows and fractions.
///
/// Assumes that b divides r * a evenly.
fn multiply_and_divide<T: Integer + Clone>(r: T, a: T, b: T) -> T { fn multiply_and_divide<T: Integer + Clone>(r: T, a: T, b: T) -> T {
// See http://blog.plover.com/math/choose-2.html for the idea. // See http://blog.plover.com/math/choose-2.html for the idea.
let g = gcd(r.clone(), b.clone()); let g = gcd(r.clone(), b.clone());
(r/g.clone() * a) / (b/g) r/g.clone() * (a / (b/g))
} }
/// Calculate the binomial coefficient. /// Calculate the binomial coefficient.
@ -791,14 +797,15 @@ fn test_iter_binomial() {
} } } }
} }
check_binomial!(u8, 6); // Check the largest n for which there is no overflow.
check_binomial!(i8, 6); check_binomial!(u8, 10);
check_binomial!(u16, 14); check_binomial!(i8, 9);
check_binomial!(i16, 14); check_binomial!(u16, 18);
check_binomial!(u32, 14); check_binomial!(i16, 17);
check_binomial!(i32, 14); check_binomial!(u32, 34);
check_binomial!(u64, 35); check_binomial!(i32, 33);
check_binomial!(i64, 35); check_binomial!(u64, 67);
check_binomial!(i64, 66);
} }
#[test] #[test]