Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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-sys follows the versioning of FLINT and generates its bindings from the FLINT headers. This enables an easier support of new FLINT releases.
  • flint3-sys offers 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-sys in your target directory 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 target directory until you run cargo clean. This frees up about 80 MB per build profile and stale build.
  • Invalidate CI caches. If your CI caches the target directory or ~/.cargo, the cache key should include Cargo.lock, so that it is rebuilt once after the update.
  • Workarounds for gmp-mpfr-sys still apply. As FLINT is still built against gmp-mpfr-sys, the troubleshooting steps in Installation for compilation errors in gmp-mpfr-sys remain relevant.

Migrating Direct FLINT Usage

This section applies to you if you

  • use unsafe getters 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, or Modulus::get_fmpz_mod_ctx, or
  • call FLINT functions via flint-sys in 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_struct is a type alias of fmpz_mat_struct now. It no longer has the fields mat and mod_, i.e. it does not store its modulus. Replace matrix.mat[0] by matrix, and obtain the modulus via MatZq::get_fmpz_mod_ctx.
  • Almost all fmpz_mod_mat_* functions take a *const fmpz_mod_ctx_struct as additional last parameter. For example, fmpz_mod_mat_init(mat, rows, cols, n) took the modulus n as *const fmpz and now takes the context instead, and fmpz_mod_mat_set_entry(mat, i, j, val) became fmpz_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.