FLINT3.7.0-dev

Resultants and discriminants

Synopsis#include <flint/nmod_poly.h>
ulong _nmod_poly_resultant(nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, nmod_t mod)source
ulong nmod_poly_resultant(const nmod_poly_t f, const nmod_poly_t g)source

Return res⁡(f,g)\operatorname{res}(f, g). The underscore version requires len1≥len2≥1\mathit{len1} \ge \mathit{len2} \ge 1.

Small cases are handled by explicit formulas. Otherwise the Euclidean algorithm is used below nmod_poly_gcd_hgcd_cutoff(mod), in Montgomery arithmetic when NMOD_POLY_GCD_EUCLIDEAN_USE_REDC_FAST holds and len1<8len2\mathit{len1} < 8 \mathit{len2}, and the half-GCD algorithm above. If both arguments are the same polynomial of positive degree, the result is 0 at once.

ulong _nmod_poly_resultant_euclidean(nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, nmod_t mod)source
ulong nmod_poly_resultant_euclidean(const nmod_poly_t f, const nmod_poly_t g)source

The resultant by the Euclidean algorithm, using res⁡(f,g)=(−1)deg⁡fdeg⁡glc⁡(g)deg⁡f−deg⁡rres⁡(g,r)\operatorname{res}(f, g) = (-1)^{\deg f \deg g} \operatorname{lc}(g)^{\deg f - \deg r} \operatorname{res}(g, r) for r=f mod gr = f \bmod g.

ulong _nmod_poly_resultant_hgcd(nn_srcptr A, slong lenA, nn_srcptr B, slong lenB, nmod_t mod)source
ulong nmod_poly_resultant_hgcd(const nmod_poly_t A, const nmod_poly_t B)source

The resultant by the half-GCD algorithm, in O(M(n)log⁡n)O(M(n) \log n) operations.

The half-GCD algorithm performs the same divisions as the Euclidean algorithm, but the remainders of a recursive call are only known in their top coefficients until the transformation matrix is applied. The factors lc⁡deg⁡−deg⁡\operatorname{lc}^{\deg - \deg} and signs contributed by each division are therefore recorded when the division is performed and applied once the remainder is complete.

ulong _nmod_poly_discriminant(nn_srcptr poly, slong len, nmod_t mod)source
ulong nmod_poly_discriminant(const nmod_poly_t f)source

Return the discriminant of ff of degree dd,

disc⁡(f)=(−1)d(d−1)/2lc⁡(f)−1res⁡(f,f′)=lc⁡(f)2d−2∏i<j(αi−αj)2,\operatorname{disc}(f) = (-1)^{d(d-1)/2} \operatorname{lc}(f)^{-1} \operatorname{res}(f, f') = \operatorname{lc}(f)^{2d - 2} \prod_{i < j} (\alpha_i - \alpha_j)^2,

where the power of lc⁡(f)\operatorname{lc}(f) is adjusted if deg⁡f′<d−1\deg f' < d - 1 (when the characteristic divides dd). The discriminant is 0 for constants and when f′=0f' = 0. The underscore version requires len≥2\mathit{len} \ge 2.

The resultant res⁡(f,g)\operatorname{res}(f, g) is the determinant of the Sylvester matrix of ff and gg; it vanishes exactly when ff and gg have a common factor, and for f=a∏(x−αi)f = a \prod (x - \alpha_i) it equals adeg⁡g∏ig(αi)a^{\deg g} \prod_i g(\alpha_i). The discriminant of ff is, up to sign and a power of the leading coefficient, the resultant of ff and f′f', and it vanishes exactly when ff has a repeated root. Resultants over Z/pZ\mathbb{Z}/p\mathbb{Z} for many primes pp are the usual way to compute resultants of integer polynomials by Chinese remaindering. These functions require a prime modulus.

Algorithms#

The resultant is computed along the Euclidean remainder sequence: for r=f mod gr = f \bmod g,

res⁡(f,g)=(−1)deg⁡fdeg⁡g lc⁡(g)deg⁡f−deg⁡rres⁡(g,r),\operatorname{res}(f, g) = (-1)^{\deg f \deg g}\, \operatorname{lc}(g)^{\deg f - \deg r} \operatorname{res}(g, r),

and res⁡(f,c)=cdeg⁡f\operatorname{res}(f, c) = c^{\deg f} for a constant cc [GG2003, §6]. nmod_poly_resultant_euclidean follows this recursion in O(deg⁡fdeg⁡g)O(\deg f \deg g) operations. nmod_poly_resultant_hgcd performs the same divisions with the half-GCD algorithm in O(M(n)log⁡n)O(M(n) \log n) operations; because a recursive half-GCD call only knows the top coefficients of the remainders, the factors contributed by its last division are recorded and applied after the remainders have been completed. nmod_poly_resultant handles small degrees by explicit formulas, and otherwise chooses between the two at the same cutoff as nmod_poly_gcd, using Montgomery arithmetic for the Euclidean algorithm under the same conditions.

The discriminant is computed as

disc⁡(f)=(−1)d(d−1)/2 lc⁡(f)d−deg⁡f′−2res⁡(f,f′),\operatorname{disc}(f) = (-1)^{d(d-1)/2}\, \operatorname{lc}(f)^{d - \deg f' - 2} \operatorname{res}(f, f'),

which is (−1)d(d−1)/2res⁡(f,f′)/lc⁡(f)(-1)^{d(d-1)/2} \operatorname{res}(f, f') / \operatorname{lc}(f) when deg⁡f′=d−1\deg f' = d - 1, that is, when the characteristic does not divide dd.

Examples#

For the cubic x3+ax+bx^3 + ax + b the discriminant is −4a3−27b2-4a^3 - 27b^2. Modulo p=101p = 101, the program checks this formula, finds the values of bb for which x3+3x+bx^3 + 3x + b has a repeated root, and compares the two resultant algorithms on long random input:

Examplenmod_poly_resultant_disc.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/nmod.h>
#include <flint/nmod_poly.h>

int main(void)
{
    nmod_poly_t f, g, h;
    flint_rand_t state;
    nmod_t mod;
    ulong a = 3, b, d;

    nmod_init(&mod, 101);
    flint_rand_init(state);
    nmod_poly_init_mod(f, mod);
    nmod_poly_init_mod(g, mod);
    nmod_poly_init_mod(h, mod);

    nmod_poly_set_coeff_ui(f, 3, 1);
    nmod_poly_set_coeff_ui(f, 1, a);
    nmod_poly_set_coeff_ui(f, 0, 5);
    d = nmod_sub(nmod_neg(nmod_mul(4, nmod_pow_ui(a, 3, mod), mod), mod),
                 nmod_mul(27, 25, mod), mod);
    flint_printf("disc = %wu, -4a^3 - 27b^2 = %wu\n",
                 nmod_poly_discriminant(f), d);

    flint_printf("repeated root for b =");
    for (b = 0; b < mod.n; b++)
    {
        nmod_poly_set_coeff_ui(f, 0, b);
        if (nmod_poly_discriminant(f) == 0)
            flint_printf(" %wu", b);
    }
    flint_printf("\n");

    /* res(f, g) = 0 exactly when gcd(f, g) != 1 */
    nmod_poly_set_coeff_ui(f, 0, 5);
    nmod_poly_set_str(g, "2 101  1 1");               /* x + 1 */
    flint_printf("res(f, x + 1) = %wu = -f(-1) = %wu\n",
                 nmod_poly_resultant(f, g),
                 nmod_neg(nmod_poly_evaluate_nmod(f, mod.n - 1), mod));

    nmod_poly_rand(g, state, 3000);
    nmod_poly_rand(h, state, 2500);
    flint_printf("euclidean == hgcd: %d\n",
                 nmod_poly_resultant_euclidean(g, h) ==
                 nmod_poly_resultant_hgcd(g, h));

    nmod_poly_clear(f);
    nmod_poly_clear(g);
    nmod_poly_clear(h);
    flint_rand_clear(state);
    return 0;
}
Output
disc = 25, -4a^3 - 27b^2 = 25
repeated root for b = 20 81
res(f, x + 1) = 100 = -f(-1) = 100
euclidean == hgcd: 1

References#

  • [GG2003] J. von zur Gathen and J. Gerhard, Modern Computer Algebra, second edition, Cambridge University Press (2003)
Cite this page

The FLINT team, “Resultants and discriminants (nmod_poly.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_poly/resultant.html (generated 2026-11-02).