FLINT3.7.0-dev

Discrete logarithms modulo a multiprecision prime

Synopsis#include <flint/fmpz_mod.h>
void fmpz_mod_discrete_log_pohlig_hellman_init(fmpz_mod_discrete_log_pohlig_hellman_t L)source
void fmpz_mod_discrete_log_pohlig_hellman_clear(fmpz_mod_discrete_log_pohlig_hellman_t L)source

Initialise L, which is not ready for computation until fmpz_mod_discrete_log_pohlig_hellman_precompute_prime is called, and free the memory used by L.

double fmpz_mod_discrete_log_pohlig_hellman_precompute_prime(fmpz_mod_discrete_log_pohlig_hellman_t L, const fmpz_t p)source

Configures L for logarithms modulo p to the base of the least primitive root modulo p, and returns an estimate of the number of multiplications modulo p that one call of fmpz_mod_discrete_log_pohlig_hellman_run needs. p must be prime, which is not checked.

This factors p - 1 (with fmpz_factor) and throws an exception if a prime factor does not fit in a word. The cost of the precomputation is dominated by this factorisation, and the tables for each prime factor q have at most 100 entries.

const fmpz * fmpz_mod_discrete_log_pohlig_hellman_primitive_root(fmpz_mod_discrete_log_pohlig_hellman_t L)source

Returns a pointer to the primitive root \alpha used as the base of the logarithms.

void fmpz_mod_discrete_log_pohlig_hellman_run(fmpz_t x, const fmpz_mod_discrete_log_pohlig_hellman_t L, const fmpz_t y)source

Sets x to the logarithm of y to the base \alpha, the integer x \in [0, p - 1) with \alpha^x \equiv y \pmod p. y must be reduced modulo p and nonzero (not checked); if no logarithm is found, which can only happen for invalid input, an exception is thrown.

int fmpz_next_smooth_prime(fmpz_t a, const fmpz_t b)source

Sets a to a prime p > b for which p - 1 is smooth and returns 1, or sets a to 0 and returns 0 if there is none. Such primes are suitable for fmpz_mod_discrete_log_pohlig_hellman_precompute_prime.

The primes come from a fixed table of primes p with p - 1 = 2^{e_1} 5^{e_2} 7^{e_3} 11^{e_4} 13^{e_5} 17^{e_6} 19^{e_7} 23^{e_8}: four to eleven primes in every interval [2^{k-1}, 2^k) for 16 \le k \le 64 (the least is 33857), and twelve each in [2^{127}, 2^{128}) and [2^{191}, 2^{192}). The function returns the least table entry greater than b, which need not be the least prime with this property (for 2^{64} \le b < 2^{127} it is a prime above 2^{127}), and returns 0 for b beyond the last entry, about 5.8 \cdot 10^{57}. The exact set of primes should not be relied upon.

These functions solve \alpha^x \equiv y \pmod p for a prime p of any size and a fixed primitive root \alpha, by the method of Pohlig and Hellman [PohHel1978]: the logarithm is computed modulo each prime power q^e dividing p - 1, one base-q digit at a time with Shanks' baby-step giant-step search in the subgroup of order q, and the results are combined by the Chinese remainder theorem. This is fast when every prime factor q of p - 1 is small; the cost grows like \sqrt{q} for the largest q. The method and its analysis are described in Discrete logarithms modulo a prime (Pohlig–Hellman); the word-size version is nmod_discrete_log_pohlig_hellman_run.

Usage#

  1. Initialise an fmpz_mod_discrete_log_pohlig_hellman_t with fmpz_mod_discrete_log_pohlig_hellman_init.
  2. Call fmpz_mod_discrete_log_pohlig_hellman_precompute_prime with the prime p. It factors p - 1, finds the least primitive root \alpha, which fmpz_mod_discrete_log_pohlig_hellman_primitive_root returns, and precomputes the tables. The return value estimates the number of multiplications modulo p per logarithm, which a caller can use to decide whether p is suitable.
  3. Call fmpz_mod_discrete_log_pohlig_hellman_run for each nonzero residue y; the result is the logarithm in [0, p - 1). The precomputed object is not modified, so several threads may share it.
  4. Free the memory with fmpz_mod_discrete_log_pohlig_hellman_clear.

The function does not check that p is prime or that y is nonzero and reduced. The precomputation throws an exception if p - 1 has a prime factor that does not fit in a word, and the search throws one if it finds no logarithm, which can only happen for invalid input. The factorisation of p - 1 dominates the precomputation unless p - 1 is known to be smooth; fmpz_next_smooth_prime provides primes for which it is, with p - 1 a product of powers of 2, 5, 7, 11, 13, 17, 19 and 23.

The tables for a prime q have at most 100 entries: a giant step is about \sqrt{q} for q below about 10^4 and is doubled until the table is small enough for larger q, so the number of baby steps grows from \sqrt{q} to about q/50 for large q.

Example#

The program takes the next smooth prime after 10^{30} (a 128-bit prime; the table of smooth primes has no entries between 2^{64} and 2^{127}), computes a power of the primitive root and recovers the exponent.

Examplefmpz_mod_discrete_log.cchecked by make checkdoc
#include <flint/fmpz.h>
#include <flint/fmpz_mod.h>

int main(void)
{
    fmpz_t p, x, y, e;
    fmpz_mod_ctx_t ctx;
    fmpz_mod_discrete_log_pohlig_hellman_t L;
    double cost;

    fmpz_init(p);
    fmpz_init(x);
    fmpz_init(y);
    fmpz_init(e);

    /* a prime p > 10^30 with p - 1 smooth */
    fmpz_set_str(x, "1000000000000000000000000000000", 10);
    if (!fmpz_next_smooth_prime(p, x))
        return 1;
    printf("p = "); fmpz_print(p); printf("\n");

    fmpz_mod_discrete_log_pohlig_hellman_init(L);
    cost = fmpz_mod_discrete_log_pohlig_hellman_precompute_prime(L, p);
    printf("base = ");
    fmpz_print(fmpz_mod_discrete_log_pohlig_hellman_primitive_root(L));
    printf(", cost estimate %s 2000\n", cost < 2000 ? "<" : ">=");

    /* y = base^e mod p, then recover e */
    fmpz_mod_ctx_init(ctx, p);
    fmpz_set_str(e, "123456789012345678901234567890", 10);
    fmpz_mod_pow_fmpz(y, fmpz_mod_discrete_log_pohlig_hellman_primitive_root(L), e, ctx);
    fmpz_mod_discrete_log_pohlig_hellman_run(x, L, y);
    printf("log = "); fmpz_print(x); printf("\n");

    fmpz_mod_ctx_clear(ctx);
    fmpz_mod_discrete_log_pohlig_hellman_clear(L);
    fmpz_clear(p);
    fmpz_clear(x);
    fmpz_clear(y);
    fmpz_clear(e);
    return 0;
}
Output
p = 201315753943344863644036821430284967937
base = 3, cost estimate < 2000
log = 123456789012345678901234567890
AlgorithmDiscrete logarithms modulo a prime (Pohlig–Hellman)Computes x with g^x = y modulo a prime p by solving the problem in each subgroup of prime-power order of (\mathbb{Z}/p\mathbb{Z})^\times, one base-q digit at a time, and combining the results by the Chinese remainder theorem. Fast when p - 1 has only small prime factors.

References#

  • [PohHel1978] S. C. Pohlig and M. E. Hellman, "An improved algorithm for computing logarithms over GF(p) and its cryptographic significance", IEEE Transactions on Information Theory 24 (1978), 106–110, https://doi.org/10.1109/TIT.1978.1055817
Cite this page

The FLINT team, “Discrete logarithms modulo a multiprecision prime (fmpz_mod.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fmpz_mod/discrete-log.html (generated 2026-10-20).