Precomputed discrete logarithms
#include <flint/dlog.h>Initialises pre for about num logarithms to the base a of order n modulo mod, with no further information on n. If n is an odd prime the function calls dlog_precomp_p_init; otherwise it uses a complete table for n < 50, baby-step giant-step for n < 500, and the Pohlig–Hellman decomposition (dlog_crt_init) above.
As dlog_precomp_n_init for a base of prime order p: a complete table for p < 50, otherwise baby-step giant-step with a table of dlog_bsgs_size(p, num) powers.
void dlog_precomp_pe_init(dlog_precomp_t pre, ulong a, ulong mod, ulong p, ulong e, ulong pe, ulong num)sourceAs dlog_precomp_n_init for a base of prime power order \mathit{pe} = p^e: a complete table for p^e < 50, dlog_precomp_p_init for e = 1, and otherwise the base-p digit decomposition (dlog_power_init).
void dlog_precomp_modpe_init(dlog_precomp_t pre, ulong a, ulong p, ulong e, ulong pe, ulong num)sourceInitialises pre for logarithms to the base a modulo \mathit{pe} = p^e, where a generates (\mathbb{Z}/p^e\mathbb{Z})^\times (for odd p), or, for p = 2 and e \ge 3, a = 5 generates the subgroup of elements congruent to 1 modulo 4. Uses a complete table for p^e < 50, the reduction to a logarithm modulo p and a p-adic logarithm (dlog_modpe_init) for e \ge 2, and dlog_precomp_n_init modulo p for e = 1.
Initialises pre for a group of small order n: the trivial method for n \le 3, a complete table indexed by the residues if \mathit{mod} < 50, and otherwise a baby-step giant-step table containing all n powers (so that a logarithm is one binary search).
ulong dlog_precomp(const dlog_precomp_t pre, ulong b)sourceReturns the logarithm of b (reduced modulo the modulus) to the base of pre.
void dlog_precomp_clear(dlog_precomp_t pre)sourceFrees the memory used by pre. Does nothing if pre is NULL.
A dlog_precomp_t holds the data for computing many discrete logarithms to a fixed base a modulo a word-size m. It is initialised with the base, the modulus, what is known about the order of a, and the expected number num of logarithms; then each call to dlog_precomp returns one logarithm, and dlog_precomp_clear frees the data. The choice of the initialisation function tells the module what it may assume:
| Function | Assumption |
|---|---|
dlog_precomp_n_init(pre, a, mod, n, num) | a has order n modulo mod |
dlog_precomp_p_init(pre, a, mod, p, num) | a has prime order p |
dlog_precomp_pe_init(pre, a, mod, p, e, pe, num) | a has order \mathit{pe} = p^e |
dlog_precomp_modpe_init(pre, a, p, e, pe, num) | a is a primitive root modulo \mathit{pe} = p^e |
dlog_precomp_small_init(pre, a, mod, n, num) | the order n is small: tabulate everything |
The order must be exact and the modulus a word; neither is checked. dlog_precomp_n_init factors n when it is large, so passing a known structure saves that work and lets the function pick the best method.
Cost and memory#
The precomputation is sized for num queries. In a group of prime order p \ge 50 it stores a baby-step giant-step table of about \sqrt{p \cdot \mathit{num}} entries (16 bytes each), and each query costs about \sqrt{p/\mathit{num}} multiplications and binary searches. For \mathit{num} \ge p/2 the whole group is tabulated and a query is one search. For composite orders the work is split over the prime factors by the Pohlig–Hellman decomposition, so a logarithm in (\mathbb{Z}/p\mathbb{Z})^\times with smooth p - 1 is cheap, and for prime powers p^e the logarithm reduces to one modulo p plus O(e) modular powers. The field pre->cost estimates the number of multiplications of one query.
For a single logarithm use dlog_once, and for the logarithms of all integers below a bound dlog_vec, which sieves with the logarithms of the primes.
Example#
The program computes 1000 logarithms modulo the prime 10^6 + 3, whose group order p - 1 = 2 \cdot 3 \cdot 166667 is decomposed (type DLOG_CRT), one logarithm modulo 7^5 with the p-adic method, and the logarithms to the base 2 of the integers below 12 modulo 13 with dlog_vec.
#include <flint/flint.h>
#include <flint/ulong_extras.h>
#include <flint/nmod.h>
#include <flint/dlog.h>
int main(void)
{
dlog_precomp_t pre;
nmod_t mod, order;
ulong p = 1000003, g, b, x, k;
ulong v[12];
int ok = 1;
g = n_primitive_root_prime(p);
nmod_init(&mod, p);
/* tables for about 1000 logarithms in a group of order p - 1 */
dlog_precomp_n_init(pre, g, p, p - 1, 1000);
for (k = 1; k <= 1000; k++)
{
b = nmod_pow_ui(k, 3, mod);
x = dlog_precomp(pre, b);
ok = ok && (nmod_pow_ui(g, x, mod) == b);
}
flint_printf("g = %wu, CRT: %d, cost %wu, 1000 logarithms ok: %d\n",
g, pre->type == DLOG_CRT, pre->cost, ok);
dlog_precomp_clear(pre);
/* modulus 7^5 with the primitive root 3 */
dlog_precomp_modpe_init(pre, 3, 7, 5, 16807, 10);
x = dlog_precomp(pre, 2);
nmod_init(&mod, 16807);
flint_printf("log_3(2) mod 7^5 = %wu, 3^%wu = %wu\n",
x, x, nmod_pow_ui(3, x, mod));
dlog_precomp_clear(pre);
/* logarithms of 0, ..., 11 modulo 13 to the base 2, of order 12 */
nmod_init(&mod, 13);
nmod_init(&order, 12);
dlog_vec(v, 12, 2, 1, mod, 12, order);
flint_printf("log_2 mod 13:");
for (k = 0; k < 12; k++)
{
if (v[k] == DLOG_NONE)
flint_printf(" -");
else
flint_printf(" %wu", v[k]);
}
flint_printf("\n");
return 0;
}g = 2, CRT: 1, cost 13, 1000 logarithms ok: 1 log_3(2) mod 7^5 = 9308, 3^9308 = 2 log_2 mod 13: - 0 1 4 2 9 5 11 3 8 10 7
The FLINT team, “Precomputed discrete logarithms (dlog.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/dlog/precomp.html (generated 2026-10-20).
@misc{flint-doc-dlog-precomp,
author = {{The FLINT team}},
title = {{Precomputed discrete logarithms (dlog.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/dlog/precomp.html},
note = {Generated 2026-10-20}
}