FLINT3.7.0-dev
Algorithm

Generic powering

Cost
⌊log⁡2e⌋\lfloor \log_2 e \rfloor squarings plus ν(e)−1\nu(e) - 1 multiplications by xx (binary); about log⁡2e\log_2 e squarings plus 2k−1+log⁡2(e)/(k+1)2^{k-1} + \log_2(e)/(k+1) multiplications (sliding window of width kk).

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, xx is an element of a ring, e≥1e \ge 1 an integer with binary expansion e=∑i=0ℓei2ie = \sum_{i=0}^{\ell} e_i 2^i, eℓ=1e_\ell = 1, and ν(e)\nu(e) the number of one bits.

Binary exponentiation#

Algorithm 1: Left-to-right binary exponentiation
  1. Set r←xr \gets x.
  2. For i=ℓ−1,ℓ−2,…,0i = \ell - 1, \ell - 2, \ldots, 0: set r←r2r \gets r^2, and if ei=1e_i = 1 set r←rxr \gets r x.
  3. Return rr.

Proposition 1. Algorithm 1 returns xex^e using ℓ=⌊log⁡2e⌋\ell = \lfloor \log_2 e \rfloor squarings and ν(e)−1\nu(e) - 1 multiplications by xx.

Proof. After the step for index ii, r=x⌊e/2i⌋r = x^{\lfloor e / 2^i \rfloor}, since ⌊e/2i⌋=2⌊e/2i+1⌋+ei\lfloor e / 2^i \rfloor = 2 \lfloor e / 2^{i+1} \rfloor + e_i; at i=0i = 0 this is xex^e. Each index costs one squaring and, for each of the ν(e)−1\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≤4e \le 4 (for example x4=(x2)2x^4 = (x^2)^2 and x3=x2⋅xx^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 xx, in which case xx is copied first. All multiplications are by the base xx, which matters when xx 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 xx with gr_inv and raise the inverse to −e-e, so the power fails with the inverse (GR_DOMAIN for a non-unit). gr_pow_fmpq with exponent p/2p/2 computes a square root (or reciprocal square root) and then an integer power.

Sliding windows#

The window method processes several bits of ee at a time using precomputed odd powers of xx.

Algorithm 2: Sliding window exponentiation
  1. Choose a window width k≥1k \ge 1. Compute x2x^2 and the odd powers x,x3,x5,…,x2k−1x, x^3, x^5, \ldots, x^{2^k - 1}, each from the previous one by a multiplication by x2x^2.
  2. Set r←1r \gets 1 and i←ℓi \gets \ell. While i≥0i \ge 0:

    • if ei=0e_i = 0, set r←r2r \gets r^2 and i←i−1i \gets i - 1;
    • otherwise let j≥max⁡(i−k+1,0)j \ge \max(i - k + 1, 0) be the smallest index with ej=1e_j = 1, let w=(ei…ej)2w = (e_i \ldots e_j)_2 (an odd number below 2k2^k), set r←r2i−j+1xwr \gets r^{2^{i - j + 1}} x^w and i←j−1i \gets j - 1.
  3. Return rr.

Proposition 2. Algorithm 2 returns xex^e. It uses ℓ+2\ell + 2 squarings at most (one in the precomputation), 2k−1−12^{k-1} - 1 multiplications in the precomputation, and one multiplication per window; the number of windows is at most ν(e)\nu(e) and, for random exponents, about (ℓ+1)/(k+1)(\ell + 1) / (k + 1) on average.

Proof. Each step consumes the bits ei,…,eje_i, \ldots, e_j and maintains r=x⌊e/2j⌋r = x^{\lfloor e / 2^{j} \rfloor} after a window (and r=x⌊e/2i⌋r = x^{\lfloor e / 2^{i} \rfloor} after a zero bit), as in Proposition 1, since appending the bits ei…eje_i \ldots e_j to the exponent multiplies it by 2i−j+12^{i - j + 1} and adds ww. Every bit position causes one squaring of rr (the first squarings of r=1r = 1 are trivial). A window starts at a one bit and covers at least that bit, so there are at most ν(e)\nu(e) windows; for random bits a window covers kk bits and is followed on average by one zero bit, which gives the average count.

The multiplication count 2k−1+(ℓ+1)/(k+1)2^{k-1} + (\ell + 1)/(k + 1) is minimised by k≈log⁡2ℓ−2log⁡2log⁡2ℓk \approx \log_2 \ell - 2 \log_2 \log_2 \ell; gr_generic_pow_ui_sliding and gr_generic_pow_fmpz_sliding choose kk from a table, from k=1k = 1 for exponents of at most 8 bits to k=11k = 11 above 56651 bits. Unlike binary exponentiation, the window method multiplies by precomputed powers xwx^w that are as large as rr, so it saves work only when every multiplication by the base is a full-size product anyway.

Use in FLINT#

Referenced from#

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
Cite this page

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-11-02).