Malachite 0.3.0: better performance and interface improvements

67 points mikhail_hogrefe

Last week I released version 0.3.0 of Malachite, a library for arbitrary-precision arithmetic. With this update, Malachite is now the fastest arbitrary-precision arithmetic library written in pure Rust. Here are the three major changes:

  • I've rewritten the code for FFT multiplication. It was initially based on the GMP implementation and now it's based on the FLINT implementation. This has improved Malachite's performance in the Rust bigint benchmarks. Here's a side-by-side comparison (times are in seconds):
benchmark Malachite 0.2.2 Malachite 0.3.0
e 100k 0.063 0.043
e 1m 1.356 0.929
e 10m 28.604 15.798
fib 10m 1.832 1.197
fib 100m 43.695 21.473
fib\_hex 100m 11.737 3.270

​

  • There is now a meta-crate called malachite which re-exports three sub-crates. That means that instead of importing the three sub-crates you can just import malachite, and do things like use malachite::Natural rather than use malachite_nz::natural::Natural. This is a backwards-compatible change; you can still do imports the old way. Thanks to Andlon for the suggestion.
  • When I first started writing Malachite, the TryFrom trait didn't exist, so I created a CheckedFrom trait based on the standard library's checked_add, checked_mul, etc., functions. Like those functions, it returned an Option. In 0.3.0, I've replaced CheckedFrom with TryFrom everywhere; this is a breaking change. Some notes:
    • Older versions of Malachite implemented CheckedFrom between primitive integers and floats. The standard library does not do this with TryFrom, and due to the orphan rule I cannot implement them in Malachite. Luckily Malachite has NiceFloat, a wrapper around floats that gives them alternative behavior for equality, order, hashing, and formatting; I've put the conversions between primitive integers and floats there, so you can do NiceFloat::<f64>::try_from(1000u32) and u32::try_from(NiceFloat(1000.0f64)).
    • Andlon also pointed out the my From implementations from primitive floats to Rational could panic (when the float is NaN or infinite), which violates the From contract. I've removed these implementations. I had to remove the From implementations from Rational to primitive floats as well, because it's not possible to have both TryFrom and From implementations between the same pair of types (I believe this is because defining From invokes the blanket implementation of TryFrom, which clashes with any other implementation of TryFrom). To recover the behavior of the old From implementations from primitive floats to Rational, you can use e.g. Rational::exact_from(1000.0f64) (which is defined to be Rational::try_from(1000.0f64).unwrap()). To recover the behavior of the old From implementations from Rational to primitive floats, you can use e.g. f64::rounding_from(Rational::from_unsigneds(1, 3), RoundingMode::Nearest).
    • Malachite still has WrappingFrom, SaturatingFrom, and OverflowingFrom, since these do not have equivalents in the standard library.

I'm currently working on Malachite 0.3.1, which will include efficient prime number generation (all 203,280,221 u32 primes in 4.3 seconds), primorials, and binomial coefficients. 0.3.2 will include Fibonacci and Lucas numbers, and the version after that (which will probably be 0.4.0) will have an initial implementation of arbitrary-precision floats!