Basic Types
Rusts data types such as u64 and i64 do not capture arbitrarily big integers.
For example, the following code causes an overflow:
let overflow: u64 = u64::MAX + 1;
qFALL-math works with arbitrarily big integers s.t. the user does not need to worry about potential overflows or execution failures.
Instantiation
qFALL-math’s basic types are integers (Z), integers in the quotient ring (Zq), and rationals (Q).
Each can be instantiated from strings and Rust’s basic math types.
use qfall_math::{integer::Z, integer_mod_q::Zq, rational::Q};
use std::str::FromStr;
fn basic_instantiations() {
// instantiations using rust types
let z = Z::from(42);
let zq = Zq::from((24, 42)); // instantiates 24 mod 42
let q = Q::from((24, 42)); // instantiates 24 / 42
// instantiations using strings
let z = Z::from_str("42").unwrap();
let zq = Zq::from_str("24 mod 42").unwrap();
let q = Q::from_str("24/42").unwrap();
}
Zq and its Modulus
To identify an element in the quotient ring over the integers, we need a representative (a value in Z) and context information defining the quotient (Modulus).
A modulus object is of type Modulus and can be instantiated from a Z greater or equal to two.
Then, a Zq value consists of a Z value and a Modulus.
A modulus can be shared among different values:
use qfall_math::integer_mod_q::{Modulus, Zq};
fn shared_modulus() {
let modulus = Modulus::from(42);
let zq_1 = Zq::from((17, &modulus));
let zq_2 = Zq::from((24, &modulus));
}
The Modulus struct utilizes a reference counter (Rc).
Therefore, cloning a Modulus only creates another pointer to the underlying object, making it efficient to store a Z and a Modulus together.
Storing only one instance also the following two advantages:
- Mathematical soundness, i.e., no accidental modulus switching or operations between objects with different moduli can occur.
- Easier use, as the developer does not have to carry the modulus manually.
Converting Types
All reasonable type conversions are possible, e.g. we can define a Q from a Z.
use qfall_math::{integer::Z, rational::Q};
fn q_from_z() {
let z = Z::from(42);
let q = Q::from(&z);
}
Basic Arithmetic Operations
All types support basic arithmetic operations. Among them are multiplication, addition and subtraction.
Opposed to what is typical Rust behavior, we natively support operations between several distinct types, e.g., between Z and Q, as output of Q any arithmetic operation between these types results in a rational output Q. As we are working with arbitrarily large values, no memory leaks or rounding errors can occur.
use qfall_math::{integer::Z, rational::Q};
fn basic_arithmetics() {
let z_1 = Z::from(42);
let z_2 = Z::from(24);
let add = &z_1 + &z_2;
let sub = &z_1 - &z_2;
let mul = &z_1 * &z_2;
// arithmetic operations between different types, including native Rust types
let add: Q = &z_1 + Q::from((1, 2));
let mul: Q = 0.5 * &z_1;
}
The operations between types either hide optimized behavior or implicit type casts.
Similarly, it is also to combine our types directly with Rust’s types, e.g., f64.
Printing your Results
You can print the values of operations using print!() or println!(), or
convert them into strings with .to_string().
This naturally extends to more complex structs as well.
use qfall_math::integer_mod_q::Zq;
fn print() {
let zq_1 = Zq::from((24, 42));
let zq_2 = Zq::from((17, 42));
let zq = zq_1 - zq_2;
// print it directly
println!("{zq}");
// explicitly generate string
println!("{}", zq.to_string())
}
Error Handling
In our math crate, the error.rs file contains all custom error types.
We use MathError as our error enum that holds all sorts of errors occurring in
this crate and StringConversionError for all errors that are related to the conversion of strings.
Errors are normally propagated through functions such that they can easily be handled or ignored with unwrap().
In cases of naive errors that are easy to spot like “division by zero” errors, we do not propagate the error but panic instead.
This avoids unnecessary as well as reckless use of unwrap(), which would both defeat the purpose of proper error handling.