Crate rug [] [src]

Arbitrary-precision numbers

Rug provides integers and floating-point numbers with arbitrary precision and correct rounding. Its main features are

This crate is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See the full text of the GNU LGPL and GNU GPL for details.

Version 1.0.0 coming soon

Version 0.10.0 is meant to be the last release before version 1.0.0. Unless some issue is discovered, version 1.0.0 will be like version 0.10.0 with all the deprecated items removed.

Getting started

Setting up the crate

To use Rug in your crate, add it as a dependency inside Cargo.toml:

[dependencies]
rug = "0.10.0"

This crate depends on the low-level bindings in the gmp-mpfr-sys crate which needs some setup to build; the gmp-mpfr-sys documentation has some details on usage under GNU/Linux, macOS and Windows.

You also need to declare the crate by adding this to your crate root:

extern crate rug;

More details are available in the Crate usage section below.

Quick example

For many operations, you can use the arbitrary-precision types such as Integer like you use primitive types such as i32. The main difference is that the Rug types do not implement Copy. This is because they store their digits in the heap, not on the stack, and copying them could involve an expensive deep copy.

This code uses the Integer type:

extern crate rug;
use rug::{Assign, Integer};

fn main() {
    let mut int = Integer::new();
    assert_eq!(int, 0);
    int.assign(14);
    assert_eq!(int, 14);
    int.assign(Integer::parse("12_345_678_901_234_567_890").unwrap());
    assert!(int > 100_000_000);
    let hex_160 = "ffff0000ffff0000ffff0000ffff0000ffff0000";
    int.assign(Integer::parse_radix(hex_160, 16).unwrap());
    assert_eq!(int.significant_bits(), 160);
    int = (int >> 128) - 1;
    assert_eq!(int, 0xfffe_ffff_u32);
}

Some points from this example follow:

  • Integer::new() creates a new Integer intialized to zero.
  • To assign values to Rug types, we use the Assign trait and its method assign. We do not use the assignment operator = as that would move the left-hand-side operand, which would have the same type.
  • Arbitrary precision numbers can hold numbers that are too large to fit in a primitive type. To assign such a number to the large types, we use strings rather than primitives; in the example this is done using both Integer::parse and Integer::parse_radix.
  • We can compare Rug types with primitive types or with other Rug types using the normal comparison operators, for example int > 100_000_000_000.
  • Most arithmetic operations are supported with Rug types and primitive types on either side of the operator, for example int >> 128.

Using primitive types

With Rust primitive types, arithmetic operators usually operate on two values of the same type, for example 12i32 + 5i32. Unlike primitive types, conversion to and from Rug types can be expensive, so the arithmetic operators are overloaded to work on many combinations of Rug types and primitives. The following are provided:

  1. Where they make sense, all arithmetic operators are overloaded to work with Rug types and the primitives i32, u32, f32 and f64.
  2. Where they make sense, conversions using the From trait and assignments using the Assign trait are supported for all the primitives in 1 above as well as the other primitives i8, i16, i64, isize, u8, u16, u64 and usize.
  3. Comparisons between Rug types and all the primitives listed in 1 and 2 above are supported.
  4. For Rational numbers, operations are also supported for tuples containing two primitives: the first is the numerator and the second is the denominator which cannot be zero. The two primitives do not need to have the same type.
  5. For Complex numbers, operations are also supported for tuples containing two primitives: the first is the real part and the second is the imaginary part. The two primitives do not need to have the same type.

Operators

Operators are overloaded to work on Rug types alone or on a combination of Rug types and Rust primitives. When at least one operand is an owned Rug type, the operation will consume that type and return a Rug type. For example

use rug::Integer;
let a = Integer::from(10);
let b = 5 - a;
assert_eq!(b, 5 - 10);

Here a is consumed by the subtraction, and b is an owned Integer.

If on the other hand there are no owned Rug types and there are references instead, the returned value is not the final value, but an incomplete computation value. For example

use rug::Integer;
let (a, b) = (Integer::from(10), Integer::from(20));
let incomplete = &a - &b;
// This would fail to compile: assert_eq!(incomplete, -10);
let sub = Integer::from(incomplete);
assert_eq!(sub, -10);

Here a and b are not consumed, and incomplete is not the final value. It still needs to be converted or assigned into an Integer. The reason is explained in the section about incomplete computation values.

The left shift << and right shift >> operators support shifting by negative values, for example a << 5 is equivalent to a >> -5. The shifting operators are also supported for the Float and Complex types, where they are equivalent to multiplication or division by a power of two.

Incomplete computation values

There are two main reasons why operations like &a - &b do not perform a complete computation and return a Rug type:

  1. Sometimes we need to assign the result to an object that already exists. Since Rug types require memory allocations, this can help reduce the number of allocations.
  2. For the Float type, we need to know the precision when we create a value, and the operation itself does not convey information about what precision is desired for the result. The same holds for the Complex type.

There are two things that can be done with incomplete computatin values:

  1. Assign them to an existing object without unnecessary allocations. This is usually achieved using the Assign trait or a similar method, for example int.assign(incomplete) and float.assign_round(incomplete, Round::Up).
  2. Convert them to the final value using the From trait or a similar method, for example Integer::from(incomplete) and Float::with_val(53, incomplete).

Let us consider a couple of examples.

use rug::{Assign, Integer};
let mut buffer = Integer::new();
// ... buffer can be used and reused ...
let (a, b) = (Integer::from(10), Integer::from(20));
let incomplete = &a - &b;
buffer.assign(incomplete);
assert_eq!(buffer, -10);

Here the assignment from incomplete into buffer does not require an allocation unless the result does not fit in the current capacity of buffer. And even then, the reallocation would take place before the computation, so no copies are involved. If &a - &b returned an Integer instead, then an allocation would take place even if it is not necessary.

use rug::Float;
use rug::float::Constant;
// x has a precision of 10 bits, y has a precision of 50 bits
let x = Float::with_val(10, 180);
let y = Float::with_val(50, Constant::Pi);
let incomplete = &x / &y;
// z has a precision of 45 bits
let z = Float::with_val(45, incomplete);
assert!(57.295 < z && z < 57.296);

The precision to use for the result depends on the requirements of the algorithm being implemented. Here c is created with a precision of 45.

In these two examples, we could have left out the incomplete variables altogether and used buffer.assign(&a - &b) and Float::with_val(45, &x / &y) directly.

Many operations can return incomplete computation values:

  • unary operators applied to references, for example -&int;
  • binary operators applied to two references, for example &int1 + &int2;
  • binary operators applied to a primitive and a reference, for example &int * 10;
  • methods that take a reference, for example int.abs_ref();
  • methods that take two references, for example int1.div_rem_ref(&int2);
  • string parsing, for example Integer::parse("12");
  • and more…

These operations return objects that can be stored in temporary variables like incomplete in the last few examples. However, the names of the types are not public, and consequently, the incomplete computation values cannot be for example stored in a struct. If you need to store the value in a struct, convert it to its final type and value.

Crate usage

Rug requires rustc version 1.18.0 or later.

This crate depends on the low-level bindings in the gmp-mpfr-sys crate, which provides Rust FFI bindings for:

It can be helpful to refer to the documentation of the gmp-mpfr-sys crate and of the GMP, MPFR and MPC libraries.

Optional features

The Rug crate has six optional features:

  1. integer, enabled by default. Required for the Integer type and its supporting features.
  2. rational, enabled by default. Required for the Rational type and its supporting features. This feature requires the integer feature.
  3. float, enabled by default. Required for the Float type and its supporting features.
  4. complex, enabled by default. Required for the Complex type and its supporting features. This feature requires the float feature.
  5. rand, enabled by default. Required for the RandState type and its supporting features. This feature requires the integer feature.
  6. serde, disabled by default. This provides serialization support for the Integer, Rational, Float and Complex types, providing that they are enabled. This features requires the serde crate.

The first five optional features are enabled by default; to use features selectively, you can add the dependency like this to Cargo.toml:

[dependencies.rug]
version = "0.10.0"
default-features = false
features = ["integer", "float", "rand"]

Here only the integer, float and rand features are enabled. If none of the features are selected, the gmp-mpfr-sys crate is not required and thus not enabled. In that case, only the Assign trait and some other traits are provided by the crate.

Modules

complex

Multi-precision complex numbers with correct rounding.

float

Multi-precision floating-point numbers with correct rounding.

integer

Aribtrary-precision integers.

ops

Operations on numbers.

rand

Random number generation.

rational

Arbitrary-precision rational numbers.

Structs

Complex

A multi-precision complex number with arbitrarily large precision and correct rounding.

Float

A multi-precision floating-point number with arbitrarily large precision and correct rounding

Integer

An arbitrary-precision integer.

Rational

An arbitrary-precision rational number.

Traits

Assign

Assigns to a number from another value.