FLINT3.7.0-dev
Guide · fmpz_poly_q.h

Representation and arithmetic of rational functions

An fmpz_poly_q_t represents f∈Q(x)f \in \mathbb{Q}(x) as a quotient a/ba/b of integer polynomials with gcd⁡(a,b)=1\gcd(a, b) = 1 in Z[x]\mathbb{Z}[x] and bb having positive leading coefficient. Any ff has exactly one such representation: clearing denominators of a representation over Q[x]\mathbb{Q}[x] gives one over Z[x]\mathbb{Z}[x], and two coprime representations a/b=a′/b′a/b = a'/b' satisfy ab′=a′ba b' = a' b, so b∣b′b \mid b' and b′∣bb' \mid b by Gauss's lemma, hence b′=±bb' = \pm b. Since the gcd in Z[x]\mathbb{Z}[x] includes the content, (2x+2)/(4x)(2x + 2)/(4x) is not canonical, while (x+1)/(2x)(x + 1)/(2x) is.

Canonical forms make equality testing a comparison of coefficients, but reducing a quotient costs a polynomial gcd in Z[x]\mathbb{Z}[x], which is much more expensive than a product. The arithmetic functions therefore never reduce a full product. They use Henrici's algorithms [Hen1956], which exploit the coprimality of the inputs to cancel common factors before multiplying.

Henrici's algorithms#

Let a/ba/b and c/dc/d be canonical.

Lemma 1 (Products). With g1=gcd⁡(a,d)g_1 = \gcd(a, d) and g2=gcd⁡(c,b)g_2 = \gcd(c, b),

ab⋅cd=(a/g1)(c/g2)(b/g2)(d/g1),\frac{a}{b} \cdot \frac{c}{d} = \frac{(a/g_1)(c/g_2)}{(b/g_2)(d/g_1)},

and the right-hand side is canonical up to the sign of the denominator.

Proof. An irreducible π\pi dividing both the numerator and the denominator divides one of a/g1a/g_1, c/g2c/g_2 and one of b/g2b/g_2, d/g1d/g_1. It cannot divide both a/g1a/g_1 and b/g2b/g_2, since gcd⁡(a,b)=1\gcd(a, b) = 1, and it cannot divide both a/g1a/g_1 and d/g1d/g_1, by the definition of g1g_1; the other two cases are symmetric.

Lemma 2 (Sums). With g=gcd⁡(b,d)g = \gcd(b, d), b=gb′b = g b', d=gd′d = g d' and t=ad′+cb′t = a d' + c b',

ab+cd=tb′d′g,gcd⁡(t,b′d′g)=gcd⁡(t,g).\frac{a}{b} + \frac{c}{d} = \frac{t}{b' d' g}, \qquad \gcd(t, b' d' g) = \gcd(t, g).

Proof. gcd⁡(t,b′)=gcd⁡(ad′,b′)=1\gcd(t, b') = \gcd(a d', b') = 1, because gcd⁡(a,b′)=1\gcd(a, b') = 1 and gcd⁡(d′,b′)=1\gcd(d', b') = 1; likewise gcd⁡(t,d′)=1\gcd(t, d') = 1. So every common factor of tt and b′d′gb' d' g divides gg.

So a sum costs one gcd of the denominators and one gcd of tt with the (usually small) gg, and a product two gcds of polynomials of the size of the inputs, instead of a gcd of the full numerator and denominator. When the denominators are coprime, which is the common case, no cancellation happens at all. fmpz_poly_q_add, fmpz_poly_q_sub, fmpz_poly_q_mul and fmpz_poly_q_div implement these formulas, with shortcuts for zero operands, for denominators equal to 1 and for constant denominators, where the arithmetic of fmpq_poly is used. The derivative is computed with a similar saving: (a/b)′=(a′b1−ab′/d)/(bb1)(a/b)' = (a' b_1 - a b'/d)/(b b_1) with d=gcd⁡(b,b′)d = \gcd(b, b') and b1=b/db_1 = b/d, whose numerator and denominator can only share a constant factor [Hor1972].

Working with numerators and denominators#

fmpz_poly_q_numref and fmpz_poly_q_denref give access to the two fmpz_poly_structs, so any fmpz_poly function can be used to build a rational function, after which fmpz_poly_q_canonicalise restores the canonical form. Strings in the format of fmpz_poly_q_set_str are an alternative for small inputs.

Examplefmpz_poly_q_basic.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/fmpq.h>
#include <flint/fmpz_poly.h>
#include <flint/fmpz_poly_q.h>

int main(void)
{
    fmpz_poly_q_t f, g, h;
    fmpq_t a, v;

    fmpz_poly_q_init(f);
    fmpz_poly_q_init(g);
    fmpz_poly_q_init(h);
    fmpq_init(a);
    fmpq_init(v);

    /* f = (2x^2 - 2)/(4x^2 + 4x), built directly, then canonicalised */
    fmpz_poly_set_str(fmpz_poly_q_numref(f), "3  -2 0 2");
    fmpz_poly_set_str(fmpz_poly_q_denref(f), "3  0 4 4");
    fmpz_poly_q_canonicalise(f);
    flint_printf("f = "); fmpz_poly_q_print_pretty(f, "x");
    flint_printf("  ("); fmpz_poly_q_print(f); flint_printf(")\n");

    fmpz_poly_q_set_str(g, "2  1 3/1  2");          /* (3x + 1)/2 */
    fmpz_poly_q_mul(h, f, g);
    flint_printf("f g = "); fmpz_poly_q_print_pretty(h, "x"); flint_printf("\n");
    fmpz_poly_q_add(h, f, g);
    flint_printf("f + g = "); fmpz_poly_q_print_pretty(h, "x"); flint_printf("\n");
    fmpz_poly_q_derivative(h, f);
    flint_printf("f' = "); fmpz_poly_q_print_pretty(h, "x"); flint_printf("\n");

    fmpq_set_si(a, 1, 3);
    if (fmpz_poly_q_evaluate_fmpq(v, f, a) == 0)
    {
        flint_printf("f(1/3) = "); fmpq_print(v); flint_printf("\n");
    }
    fmpq_set_si(a, 0, 1);
    flint_printf("f(0) defined: %d\n", fmpz_poly_q_evaluate_fmpq(v, f, a) == 0);

    fmpz_poly_q_clear(f);
    fmpz_poly_q_clear(g);
    fmpz_poly_q_clear(h);
    fmpq_clear(a);
    fmpq_clear(v);
    return 0;
}
Output
f = (x-1)/(2*x)  (2  -1 1/2  0 2)
f g = (3*x^2-2*x-1)/(4*x)
f + g = (3*x^2+2*x-1)/(2*x)
f' = 1/(2*x^2)
f(1/3) = -1
f(0) defined: 0

The common factor 2(x+1)2(x + 1) of the numerator and denominator of ff is removed by fmpz_poly_q_canonicalise. In f+gf + g the denominators 2x2x and 22 have the gcd g=2g = 2, and since the numerator 3x2+2x−13x^2 + 2x - 1 is coprime to 2 in Z[x]\mathbb{Z}[x], nothing more cancels.

References#

  • [Hen1956] Peter Henrici : "A Subroutine for Computations with Rational Numbers" J. ACM (1956), https://doi.org/10.1145/320815.320818
  • [Hor1972] Ellis Horowitz : "Algorithms for Rational Function Arithmetic Operations" Annual ACM Symposium on Theory of Computing: Proceedings of the Fourth Annual ACM Symposium on Theory of Computing (Denver) (1972), https://doi.org/10.1145/800152.804903
Cite this page

The FLINT team, “Representation and arithmetic of rational functions (fmpz_poly_q.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fmpz_poly_q/representation.html (generated 2026-11-02).