Generic powering
- Cost
- \lfloor \log_2 e \rfloor squarings plus \nu(e) - 1 multiplications by x (binary); about \log_2 e squarings plus 2^{k-1} + \log_2(e)/(k+1) multiplications (sliding window of width k).
The default integer powers of the generic interface, gr_pow_ui, gr_pow_si and gr_pow_fmpz, are computed by binary exponentiation in any ring, using only the ring's mul and sqr methods (gr_generic_pow_ui, gr_generic_pow_fmpz). A sliding window variant is also available for exponents and rings in which it is faster. Both are classical; see Knuth [Knu1997, §4.6.3] and Cohen [Coh1996, §1.2]. Throughout, x is an element of a ring, e \ge 1 an integer with binary expansion e = \sum_{i=0}^{\ell} e_i 2^i, e_\ell = 1, and \nu(e) the number of one bits.
Binary exponentiation#
- Set r \gets x.
- For i = \ell - 1, \ell - 2, \ldots, 0: set r \gets r^2, and if e_i = 1 set r \gets r x.
- Return r.
Proposition 1. Algorithm 1 returns x^e using \ell = \lfloor \log_2 e \rfloor squarings and \nu(e) - 1 multiplications by x.
Proof. After the step for index i, r = x^{\lfloor e / 2^i \rfloor}, since \lfloor e / 2^i \rfloor = 2 \lfloor e / 2^{i+1} \rfloor + e_i; at i = 0 this is x^e. Each index costs one squaring and, for each of the \nu(e) - 1 one bits below the leading one, a multiplication.
gr_generic_pow_ui_binexp and gr_generic_pow_fmpz_binexp implement this, with direct formulas for e \le 4 (for example x^4 = (x^2)^2 and x^3 = x^2 \cdot x). To avoid copying, the ulong version alternates between the output and one temporary, choosing the initial buffer from the parity of the number of zero bits so that the final result lands in the output; the output may alias x, in which case x is copied first. All multiplications are by the base x, which matters when x is small compared with the intermediate powers: multiplying a large element by a small one costs much less than a general product.
For negative exponents, gr_generic_pow_si and gr_generic_pow_fmpz invert x with gr_inv and raise the inverse to -e, so the power fails with the inverse (GR_DOMAIN for a non-unit). gr_pow_fmpq with exponent p/2 computes a square root (or reciprocal square root) and then an integer power.
Sliding windows#
The window method processes several bits of e at a time using precomputed odd powers of x.
- Choose a window width k \ge 1. Compute x^2 and the odd powers x, x^3, x^5, \ldots, x^{2^k - 1}, each from the previous one by a multiplication by x^2.
Set r \gets 1 and i \gets \ell. While i \ge 0:
- if e_i = 0, set r \gets r^2 and i \gets i - 1;
- otherwise let j \ge \max(i - k + 1, 0) be the smallest index with e_j = 1, let w = (e_i \ldots e_j)_2 (an odd number below 2^k), set r \gets r^{2^{i - j + 1}} x^w and i \gets j - 1.
- Return r.
Proposition 2. Algorithm 2 returns x^e. It uses \ell + 2 squarings at most (one in the precomputation), 2^{k-1} - 1 multiplications in the precomputation, and one multiplication per window; the number of windows is at most \nu(e) and, for random exponents, about (\ell + 1) / (k + 1) on average.
Proof. Each step consumes the bits e_i, \ldots, e_j and maintains r = x^{\lfloor e / 2^{j} \rfloor} after a window (and r = x^{\lfloor e / 2^{i} \rfloor} after a zero bit), as in Proposition 1, since appending the bits e_i \ldots e_j to the exponent multiplies it by 2^{i - j + 1} and adds w. Every bit position causes one squaring of r (the first squarings of r = 1 are trivial). A window starts at a one bit and covers at least that bit, so there are at most \nu(e) windows; for random bits a window covers k bits and is followed on average by one zero bit, which gives the average count.
The multiplication count 2^{k-1} + (\ell + 1)/(k + 1) is minimised by k \approx \log_2 \ell - 2 \log_2 \log_2 \ell; gr_generic_pow_ui_sliding and gr_generic_pow_fmpz_sliding choose k from a table, from k = 1 for exponents of at most 8 bits to k = 11 above 56651 bits. Unlike binary exponentiation, the window method multiplies by precomputed powers x^w that are as large as r, so it saves work only when every multiplication by the base is a full-size product anyway.
Use in FLINT#
gr_generic_pow_ui,gr_generic_pow_siandgr_generic_pow_fmpz, the defaults ofgr_pow_ui,gr_pow_siandgr_pow_fmpz, use binary exponentiation (Algorithm 1). Rings with a better method override them; for examplenmodcallsnmod_pow_uiandarbcallsarb_pow_ui.fmpz_powmfor moduli of at least 70000 bits (in builds withfft_small), andfmpz_mod_pow_uiandfmpz_mod_pow_fmpzfor such moduli, run the generic algorithms over anfmpz_modcontext: binary exponentiation when the base is a single-word integer, and the sliding window method when the base is large (and, infmpz_mod_pow_fmpz, the exponent has at least 20 bits). Smaller moduli use GMP'smpz_powm.gr_generic_mul_2exp_si,gr_generic_set_fmpz_2exp_fmpzandgr_generic_set_fmpz_10exp_fmpzcompute powers of 2 and 10 in the ring with these functions.
Referenced from#
- gr_generic.h (module)
References#
- [Knu1997] Knuth, D. E. The Art of Computer Programming, volume 2: Seminumerical algorithms, 1997
- [Coh1996] Cohen, Henri : A course in computational algebraic number theory, Springer, 1996
The FLINT team, “Generic powering,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/generic/powering.html (generated 2026-10-20).
@misc{flint-doc-algorithms-generic-powering,
author = {{The FLINT team}},
title = {{Generic powering}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/algorithms/generic/powering.html},
note = {Generated 2026-10-20}
}