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 pp to the base of the least primitive root modulo pp, and returns an estimate of the number of multiplications modulo pp that one call of fmpz_mod_discrete_log_pohlig_hellman_run needs. pp must be prime, which is not checked.

This factors p−1p - 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 qq 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 xx to the logarithm of yy to the base α\alpha, the integer x∈[0,p−1)x \in [0, p - 1) with αx≡y(modp)\alpha^x \equiv y \pmod p. yy must be reduced modulo pp 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 aa to a prime p>bp > b for which p−1p - 1 is smooth and returns 1, or sets aa 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 pp with p−1=2e15e27e311e413e517e619e723e8p - 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 [2k−1,2k)[2^{k-1}, 2^k) for 16≤k≤6416 \le k \le 64 (the least is 33857), and twelve each in [2127,2128)[2^{127}, 2^{128}) and [2191,2192)[2^{191}, 2^{192}). The function returns the least table entry greater than bb, which need not be the least prime with this property (for 264≤b<21272^{64} \le b < 2^{127} it is a prime above 21272^{127}), and returns 0 for bb beyond the last entry, about 5.8⋅10575.8 \cdot 10^{57}. The exact set of primes should not be relied upon.

These functions solve αx≡y(modp)\alpha^x \equiv y \pmod p for a prime pp 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 qeq^e dividing p−1p - 1, one base-qq digit at a time with Shanks' baby-step giant-step search in the subgroup of order qq, and the results are combined by the Chinese remainder theorem. This is fast when every prime factor qq of p−1p - 1 is small; the cost grows like q\sqrt{q} for the largest qq. 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 pp. It factors p−1p - 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 pp per logarithm, which a caller can use to decide whether pp is suitable.
  3. Call fmpz_mod_discrete_log_pohlig_hellman_run for each nonzero residue yy; the result is the logarithm in [0,p−1)[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 pp is prime or that yy is nonzero and reduced. The precomputation throws an exception if p−1p - 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−1p - 1 dominates the precomputation unless p−1p - 1 is known to be smooth; fmpz_next_smooth_prime provides primes for which it is, with p−1p - 1 a product of powers of 2, 5, 7, 11, 13, 17, 19 and 23.

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

Example#

The program takes the next smooth prime after 103010^{30} (a 128-bit prime; the table of smooth primes has no entries between 2642^{64} and 21272^{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 xx with gx=yg^x = y modulo a prime pp by solving the problem in each subgroup of prime-power order of (Z/pZ)×(\mathbb{Z}/p\mathbb{Z})^\times, one base-qq digit at a time, and combining the results by the Chinese remainder theorem. Fast when p−1p - 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-11-02).