How to Transition from 0.1 to 0.2
This chapter guides you step by step from version 0.1 to 0.2.
Most of it covers the switch from FLINT 2.8 (flint-sys) to FLINT 3.6 (flint3-sys).
If you only use the safe API of qFALL, the FLINT switch affects your build, but not your code, and you can skip Migrating Direct FLINT Usage.
Updating Dependencies
Rust Toolchain
qfall-tools 0.2 and qfall-schemes 0.2 require Rust 1.88 or newer, mostly due to their dependency on sha2 0.11.
rustup update stable
rustc --version
Cargo.toml
Update all qFALL crates at once.
[dependencies]
qfall-math = "0.2"
qfall-tools = "0.2"
qfall-schemes = "0.2"
To avoid build issues, run cargo clean and cargo update.
If Cargo reports a conflict regarding links = "flint", some dependency still requires a 0.1 release of qFALL, see Versioning and Updates.
Building FLINT 3
What Changed
qfall-math is a memory-safe wrapper around the C-library FLINT.
Until 0.1, it accessed FLINT through the crate flint-sys 0.7, which bundles the sources of FLINT 2.8.5.
Since 0.2, it uses flint3-sys 3.6, which bundles the sources of FLINT 3.6 and generates its bindings with bindgen.
qfall-math enables the feature gmp-mpfr-sys of flint3-sys, i.e. FLINT is built against the GMP and MPFR libraries provided by the crate gmp-mpfr-sys, as before.
We switched for the following reasons.
- FLINT 2.8 is outdated. FLINT 3 merged the libraries Arb, Antic, and Calcium into FLINT and received several years of performance improvements and bug fixes.
flint3-sysfollows the versioning of FLINT and generates its bindings from the FLINT headers. This enables an easier support of new FLINT releases.flint3-sysoffers better cross-platform support of the build process.
What This Means for Your Build
The build requirements are unchanged: you need m4, make, and a C-compiler like gcc, see Installation.
- The first build recompiles FLINT. Artifacts of
flint-sysin yourtargetdirectory or CI caches cannot be reused. Depending on your hardware, the first build takes several minutes up to half an hour. Subsequent builds reuse the compiled FLINT version as before. - Clean up old artifacts. The compiled FLINT version 2.8 remains in your
targetdirectory until you runcargo clean. This frees up about 80 MB per build profile and stale build. - Invalidate CI caches. If your CI caches the
targetdirectory or~/.cargo, the cache key should includeCargo.lock, so that it is rebuilt once after the update. - Workarounds for
gmp-mpfr-sysstill apply. As FLINT is still built againstgmp-mpfr-sys, the troubleshooting steps in Installation for compilation errors ingmp-mpfr-sysremain relevant.
Migrating Direct FLINT Usage
This section applies to you if you
- use
unsafegetters and setters of qFALL to access the underlying FLINT structures, e.g.Z::get_fmpz,MatZ::get_fmpz_mat_struct,MatZq::get_fmpz_mod_mat_struct, orModulus::get_fmpz_mod_ctx, or - call FLINT functions via
flint-sysin your own code, e.g. to use FLINT functionality that qFALL does not cover yet.
The names of all unsafe getters and setters of qFALL are unchanged, but they return and accept the types of flint3-sys now.
Hence, your code has to move from flint-sys to flint3-sys as well.
Step 1: Replace the Dependency
[dependencies]
flint3-sys = "3.6"
The version requirement must be compatible with the one of qfall-math, i.e. 3.6.
Otherwise, Cargo compiles two copies of FLINT, and the types of your copy do not match the types returned by qFALL.
Step 2: Flatten Your Imports
flint-sys organized its bindings in one module per FLINT header, e.g. flint_sys::fmpz or flint_sys::fmpz_mat.
flint3-sys exports all bindings at the crate root.
Rename the crate from flint_sys to flint3_sys and drop the module from each path.
0.1 (flint-sys) | 0.2 (flint3-sys) |
|---|---|
use flint_sys::fmpz::{fmpz, fmpz_set}; | use flint3_sys::{fmpz, fmpz_set}; |
use flint_sys::fmpz_mat::fmpz_mat_entry; | use flint3_sys::fmpz_mat_entry; |
Step 3: fmpz Is a Plain Integer Now
In flint-sys, fmpz was a tuple struct pub struct fmpz(pub slong).
In flint3-sys, it is a type alias pub type fmpz = slong, i.e. an i64 on 64-bit platforms, which matches its definition in C.
Wherever you constructed or destructured an fmpz, remove the wrapper.
0.1 (flint-sys) | 0.2 (flint3-sys) |
|---|---|
let value = fmpz(2); | let value: fmpz = 2; |
fmpz_set(entry, &fmpz(2)); | fmpz_set(entry, &2); |
let raw = value.0; | let raw = value; |
The same applies to all structs that contain an fmpz, e.g. the fields num and den of fmpq.
Keep in mind that the value of an fmpz is only the integer itself if it is smaller than 2^{62}.
Large integers are stored as a tagged pointer, in FLINT 2.8 and in FLINT 3.6.
Therefore, never interpret the raw value directly. Use FLINT functions like fmpz_get_si, or convert it into a Z.
As fmpz is a plain i64 now, the compiler no longer prevents you from mixing it up with ordinary integers.
The following example writes an entry of a MatZ via FLINT and reads a small Z.
Step 4: Pass the Modulus Context Explicitly
FLINT 3 redesigned the fmpz_mod_* types around the modulus context fmpz_mod_ctx.
This affects the internals of MatZq the most.
- For example,
fmpz_mod_mat_structis a type alias offmpz_mat_structnow. It no longer has the fieldsmatandmod_, i.e. it does not store its modulus. Replacematrix.mat[0]bymatrix, and obtain the modulus viaMatZq::get_fmpz_mod_ctx. - Almost all
fmpz_mod_mat_*functions take a*const fmpz_mod_ctx_structas additional last parameter. For example,fmpz_mod_mat_init(mat, rows, cols, n)took the modulusnas*const fmpzand now takes the context instead, andfmpz_mod_mat_set_entry(mat, i, j, val)becamefmpz_mod_mat_set_entry(mat, i, j, val, ctx).
Some function signatures include minor differences between flint-sys and flint3-sys. However, most of them are self-explanatory to resolve.
Changed Signatures
Besides the FLINT types, the signatures of the following functions changed.
Initialization of Samplers
The init functions of the samplers in qfall_math::utils::sample take an additional last parameter seed: Option<[u8; 32]>.
Pass None to keep the previous behavior.
use qfall_math::utils::sample::{
binomial::BinomialSampler,
discrete_gauss::{DiscreteGaussianIntegerSampler, LookupTableSetting},
uniform::UniformIntegerSampler,
};
use qfall_math::integer::Z;
let mut uniform = UniformIntegerSampler::init(&Z::from(256), None).unwrap();
let mut binomial = BinomialSampler::init(4, 0.5, None).unwrap();
let mut gaussian =
DiscreteGaussianIntegerSampler::init(0, 3, 6, LookupTableSetting::FillOnTheFly, None).unwrap();
The sampling functions of the data types, e.g. MatZq::sample_uniform, keep their signatures. Their seeded counterparts are new functions with the suffix _seeded.
MatZq::gaussian_elimination_prime
gaussian_elimination_prime borrows self instead of consuming it.
Behavior Changes
The following changes do not cause compiler errors. Search your code for the affected functions.
Zq::get_representative_least_absolute_residue
The function returned the absolute value of the least absolute residue in 0.1, e.g. 2 for 15 mod 17.
It now returns the residue itself, i.e. -2, see Bug Fixes.
If your code computes the distance of a value to 0, e.g. to decode an LWE ciphertext, add .abs() to preserve the previous result.
Reduced Samples of MatZq
MatZq::sample_discrete_gauss, MatZq::sample_binomial, and MatZq::sample_binomial_with_offset return entries in [0, q) now.
If your code worked around this bug, e.g. by reducing the matrix explicitly, multiplying with another matrix, or by mapping negative entries, the workaround is no longer needed.
Debug Output
Zq, PolynomialRingZq, MatPolynomialRingZq, ModulusPolynomialRingZq, and the NTT types derive Debug now.
Their Debug output is formatted differently and includes the internal FLINT structures of the modulus.
Don’t compare Debug strings in tests. Use to_string(), which is unchanged, or compare the values directly.
Getting Help
If your project does not compile after following this guide, or you observe different results that are not explained here, please open an issue in the repository of the respective crate: qfall-math, qfall-tools, or qfall-schemes.