FLINT3.7.0-dev

Rational reconstruction

Synopsis#include <flint/fmpq.h>
int fmpq_mod_fmpz(fmpz_t res, const fmpq_t x, const fmpz_t mod)source
int _fmpq_mod_fmpz(fmpz_t res, const fmpz_t num, const fmpz_t den, const fmpz_t mod)source

If the denominator dd of x=n/dx = n/d is invertible modulo mm = mod, set res to the residue a∈[0,m)a \in [0, m) with n≡ad(modm)n \equiv ad \pmod m and return 1; otherwise return 0 and leave an unspecified value in res. Requires m>0m > 0.

int fmpq_reconstruct_fmpz_2(fmpq_t res, const fmpz_t a, const fmpz_t m, const fmpz_t N, const fmpz_t D)source
int _fmpq_reconstruct_fmpz_2(fmpz_t n, fmpz_t d, const fmpz_t a, const fmpz_t m, const fmpz_t N, const fmpz_t D)source
int _fmpq_reconstruct_fmpz_2_naive(fmpz_t n, fmpz_t d, const fmpz_t a, const fmpz_t m, const fmpz_t N, const fmpz_t D)source

Given m>2m > 2, 0≤a<m0 \le a < m and N,D>0N, D > 0 with 2ND<m2ND < m, find the fraction n/dn/d with ∣n∣≤N|n| \le N, 0<d≤D0 < d \le D, gcd⁡(n,d)=1\gcd(n, d) = 1 and n≡ad(modm)n \equiv ad \pmod m, which is unique if it exists. Return 1 and set the result if it exists, otherwise return 0.

int fmpq_reconstruct_fmpz(fmpq_t res, const fmpz_t a, const fmpz_t m)source
int _fmpq_reconstruct_fmpz(fmpz_t n, fmpz_t d, const fmpz_t a, const fmpz_t m)source

Rational reconstruction with the balanced bounds N=D=⌊⌊(m−1)/2⌋⌋N = D = \lfloor\sqrt{\lfloor (m-1)/2 \rfloor}\rfloor, which satisfy 2ND<m2ND < m.

A multimodular or pp-adic algorithm whose answer is a rational number computes it as a residue aa modulo a large integer mm, a product of primes or a prime power. Rational reconstruction recovers the fraction n/dn/d from a=nd−1 mod ma = n d^{-1} \bmod m, provided that ∣n∣|n| and dd are small compared with mm. fmpq_mod_fmpz computes the residue of a fraction, and the reconstruction functions invert it:

  • fmpq_reconstruct_fmpz_2 takes explicit bounds NN and DD with 2ND<m2ND < m and finds the unique n/dn/d with ∣n∣≤N|n| \le N and 0<d≤D0 < d \le D, if it exists;
  • fmpq_reconstruct_fmpz uses the balanced bounds N=D=⌊⌊(m−1)/2⌋⌋N = D = \lfloor\sqrt{\lfloor (m-1)/2 \rfloor}\rfloor.

The residue must be reduced, 0≤a<m0 \le a < m, and m>2m > 2. The result is canonical: gcd⁡(n,d)=1\gcd(n, d) = 1 and d>0d > 0.

AlgorithmRational reconstructionRecovers a fraction n/dn/d with ∣n∣≤N|n| \le N and 0<d≤D0 < d \le D from its residue modulo m>2NDm > 2ND by stopping the extended Euclidean algorithm at the first remainder below NN. Used to return rational results from multimodular and p-adic algorithms.

Success and failure#

The bound 2ND<m2ND < m makes the answer unique: two fractions n1/d1≠n2/d2n_1/d_1 \ne n_2/d_2 within the bounds have different residues, because mm would otherwise divide n1d2−n2d1n_1 d_2 - n_2 d_1, a nonzero number of absolute value at most 2ND2ND. A return value of 0 therefore proves that no fraction within the bounds has residue aa.

A return value of 1 proves only that the fraction found is the unique one within the bounds. If the true answer of a computation is larger than the bounds, the function either fails or returns a different, small fraction with the same residue. About 6/π2≈61%6/\pi^2 \approx 61\% of all residues have a reconstruction with the balanced bounds, so a successful reconstruction of a residue that is not known to come from a small fraction is weak evidence. Multimodular algorithms therefore either use a priori bounds on the answer (Hadamard's bound for determinants and solutions of linear systems, for example) and choose mm larger than 2ND2ND, or verify the reconstructed result, or add primes until the reconstruction stabilises.

When the shape of the answer is known, unbalanced bounds help. For a fraction whose denominator divides a known d0d_0, take D=d0D = d_0 and N=⌊(m−1)/(2d0)⌋N = \lfloor (m-1)/(2 d_0) \rfloor: almost all of mm is then available for the numerator.

Algorithm#

The fraction is found among the remainders and cofactors of the extended Euclidean algorithm applied to mm and aa, stopping at the first remainder rj≤Nr_j \le N; then n/d=±rj/tjn/d = \pm r_j / t_j where tjt_j is the cofactor of aa, and the reconstruction succeeds if ∣tj∣≤D|t_j| \le D and gcd⁡(rj,tj)=1\gcd(r_j, t_j) = 1 (Wang's algorithm [WGD1982]). The implementation works in one or two words or on fixed arrays of up to 12 limbs for small mm, with Lehmer's method for larger mm, and with the half-GCD algorithm when the modulus has at least FMPQ_RECONSTRUCT_HGCD_CUTOFF (500) more limbs than NN, for a total cost of O(M(log⁡m)log⁡log⁡m)O(M(\log m) \log\log m). Since the search stops at a remainder of the size of NN, its cost depends on the size of mm relative to NN, not only on the size of mm. _fmpq_reconstruct_fmpz_2_naive is the plain Euclidean algorithm on fmpz, used for testing.

Example#

The modulus is the prime m=109+7m = 10^9 + 7, for which the balanced bound is N=D=22360N = D = 22360. The fraction −355/113-355/113 is recovered. The fraction 123456/789=41152/263123456/789 = 41152/263 has a numerator above the balanced bound; the balanced reconstruction fails, and unbalanced bounds N=400000N = 400000, D=1000D = 1000 (with 2ND<m2ND < m) recover it. The residue of 1234567/891234567/89 is reconstructed with the balanced bounds, but to a different fraction of small height, which shows that a successful reconstruction has to be checked when the answer is not known to be within the bounds.

Examplefmpq_reconstruct_basic.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/fmpz.h>
#include <flint/fmpq.h>

static void
show(const fmpq_t x, const fmpz_t m, const fmpz_t N, const fmpz_t D)
{
    fmpz_t a;
    fmpq_t y;
    int ok;

    fmpz_init(a);
    fmpq_init(y);
    fmpq_mod_fmpz(a, x, m);
    if (N == NULL)
        ok = fmpq_reconstruct_fmpz(y, a, m);
    else
        ok = fmpq_reconstruct_fmpz_2(y, a, m, N, D);

    fmpq_print(x);
    flint_printf(" -> ");
    fmpz_print(a);
    if (ok)
    {
        flint_printf(" -> ");
        fmpq_print(y);
        flint_printf("\n");
    }
    else
        flint_printf(" -> no fraction within the bounds\n");

    fmpz_clear(a);
    fmpq_clear(y);
}

int main(void)
{
    fmpz_t m, N, D;
    fmpq_t x;

    fmpz_init_set_ui(m, 1000000007);
    fmpz_init_set_ui(N, 400000);
    fmpz_init_set_ui(D, 1000);
    fmpq_init(x);

    fmpq_set_si(x, -355, 113);
    show(x, m, NULL, NULL);

    fmpq_set_si(x, 123456, 789);
    show(x, m, NULL, NULL);
    show(x, m, N, D);

    fmpq_set_si(x, 1234567, 89);
    show(x, m, NULL, NULL);

    fmpz_clear(m);
    fmpz_clear(N);
    fmpz_clear(D);
    fmpq_clear(x);
    return 0;
}
Output
-355/113 -> 699115046 -> -355/113
41152/263 -> 726235903 -> no fraction within the bounds
41152/263 -> 726235903 -> 41152/263
1234567/89 -> 955070058 -> -14062/18629

Guides#

References#

Cite this page

The FLINT team, “Rational reconstruction (fmpq.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fmpq/reconstruct.html (generated 2026-11-02).