Discrete logarithms modulo a multiprecision prime
#include <flint/fmpz_mod.h>void fmpz_mod_discrete_log_pohlig_hellman_init(fmpz_mod_discrete_log_pohlig_hellman_t L)sourcevoid fmpz_mod_discrete_log_pohlig_hellman_clear(fmpz_mod_discrete_log_pohlig_hellman_t L)sourceInitialise 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)sourceConfigures 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)sourceReturns 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)sourceSets 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.
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#
- Initialise an
fmpz_mod_discrete_log_pohlig_hellman_twithfmpz_mod_discrete_log_pohlig_hellman_init. - Call
fmpz_mod_discrete_log_pohlig_hellman_precompute_primewith the prime p. It factors p - 1, finds the least primitive root \alpha, whichfmpz_mod_discrete_log_pohlig_hellman_primitive_rootreturns, 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. - Call
fmpz_mod_discrete_log_pohlig_hellman_runfor 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. - 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.
#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;
}p = 201315753943344863644036821430284967937 base = 3, cost estimate < 2000 log = 123456789012345678901234567890
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
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).
@misc{flint-doc-fmpz-mod-discrete-log,
author = {{The FLINT team}},
title = {{Discrete logarithms modulo a multiprecision prime (fmpz\_mod.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/fmpz_mod/discrete-log.html},
note = {Generated 2026-10-20}
}