FLINT3.7.0-dev
Algorithm

Hensel lifting

Cost
One quadratic step from pep^e to p2ep^{2e} for a factorisation of a degree-nn polynomial into rr factors costs O(M(n)log⁡r)O(M(n) \log r) operations modulo p2ep^{2e}; lifting to pNp^N costs a constant times the last step.

Hensel lifting turns a factorisation of f∈Z[x]f \in \mathbb{Z}[x] modulo a prime pp into one modulo pNp^N for any NN. It is the bridge between the cheap computations modulo a prime, such as factorisation over Fp\mathbb{F}_p, and the integer answer: once pNp^N exceeds twice a bound for the coefficients of a factor, the factor is determined by its image modulo pNp^N (Bounds and precision). The method goes back to Hensel's pp-adic numbers [Hen1908]; Zassenhaus introduced quadratic lifting [Zas1969], and the factor tree for many factors is described in [GG2003, Ch. 15].

Lifting two factors#

The basic step lifts a factorisation into two coprime factors together with their Bézout cofactors. Below, mm and m1m_1 are moduli with m1∣mm_1 \mid m (for example m=pem = p^e and m1=pe′m_1 = p^{e'} with e′≤ee' \le e), and congruences between polynomials hold coefficientwise.

Theorem 1 (Hensel step). Let f,g,h,a,b∈Z[x]f, g, h, a, b \in \mathbb{Z}[x] with gg and hh monic, deg⁡f=deg⁡g+deg⁡h\deg f = \deg g + \deg h, deg⁡a<deg⁡h\deg a < \deg h, deg⁡b<deg⁡g\deg b < \deg g, and

f≡gh(modm),ag+bh≡1(modm).f \equiv gh \pmod m, \qquad ag + bh \equiv 1 \pmod m.

Let e=(f−gh)/me = (f - gh)/m, and let δ\delta, ε\varepsilon be the remainders of bebe modulo gg and of aeae modulo hh, computed modulo m1m_1. Then G=g+mδG = g + m\delta and H=h+mεH = h + m\varepsilon are monic, congruent to gg and hh modulo mm, and

f≡GH(modmm1).f \equiv GH \pmod{m m_1}.

With e2=(1−aG−bH)/me_2 = (1 - aG - bH)/m, the polynomials A=a+m(ae2 mod H)A = a + m (a e_2 \bmod H) and B=b+m(be2 mod G)B = b + m (b e_2 \bmod G), computed modulo m1m_1, satisfy AG+BH≡1(modmm1)AG + BH \equiv 1 \pmod{m m_1}.

Proof. All computations of δ\delta and ε\varepsilon are modulo m1m_1, where ag+bh≡1ag + bh \equiv 1 since m1∣mm_1 \mid m. Write be=q1g+δbe = q_1 g + \delta and ae=q2h+εae = q_2 h + \varepsilon. Then, modulo m1m_1,

e=e(ag+bh)=g(ae)+h(be)=gε+hδ+gh(q1+q2).e = e(ag + bh) = g(ae) + h(be) = g \varepsilon + h \delta + gh(q_1 + q_2).

Since ff and ghgh have the same degree and f≡gh(modm)f \equiv gh \pmod m with ghgh monic, deg⁡e<deg⁡f\deg e < \deg f; also deg⁡(gε+hδ)<deg⁡g+deg⁡h=deg⁡f\deg(g\varepsilon + h\delta) < \deg g + \deg h = \deg f. Hence gh(q1+q2)≡e−gε−hδgh(q_1 + q_2) \equiv e - g\varepsilon - h\delta has degree less than deg⁡(gh)\deg(gh) modulo m1m_1, and since ghgh is monic, q1+q2≡0(modm1)q_1 + q_2 \equiv 0 \pmod{m_1}. So e≡gε+hδ(modm1)e \equiv g\varepsilon + h\delta \pmod{m_1}, and

GH=gh+m(gε+hδ)+m2δε≡gh+me=f(modmm1),GH = gh + m(g\varepsilon + h\delta) + m^2 \delta\varepsilon \equiv gh + m e = f \pmod{m m_1},

because m1∣mm_1 \mid m gives mm1∣m2m m_1 \mid m^2. For the cofactors, aG+bH≡ag+bh≡1(modm)aG + bH \equiv ag + bh \equiv 1 \pmod{m}, so e2e_2 is integral, and deg⁡(1−aG−bH)<deg⁡f\deg(1 - aG - bH) < \deg f. The same argument with G,HG, H in place of g,hg, h gives e2≡G(ae2 mod H)+H(be2 mod G)(modm1)e_2 \equiv G (a e_2 \bmod H) + H (b e_2 \bmod G) \pmod{m_1}, and therefore AG+BH=aG+bH+m(e2+m1⋅(…))≡1(modmm1)AG + BH = aG + bH + m(e_2 + m_1 \cdot (\ldots)) \equiv 1 \pmod{m m_1}.

The lifted factors are unique: if f≡G′H′f \equiv G'H' modulo mm1m m_1 with monic G′≡gG' \equiv g, H′≡hH' \equiv h modulo mm, and g,hg, h are coprime modulo pp, then G′=GG' = G and H′=HH' = H modulo mm1m m_1 [GG2003, Ch. 15]. Taking m1=mm_1 = m doubles the exponent at each step (quadratic lifting); taking m1=pm_1 = p gains one power of pp (linear lifting). Quadratic lifting reaches pNp^N in ⌈log⁡2N⌉\lceil \log_2 N \rceil steps, and since the cost of a step grows with the size of the modulus, the total cost is dominated by the last step.

Many factors: the factor tree#

For f≡ℓg1⋯gr(modp)f \equiv \ell g_1 \cdots g_r \pmod p with r>2r > 2 monic, pairwise coprime gig_i, the factors are arranged in a binary tree whose leaves are the gig_i and whose inner nodes are the products of their two children; the root is f/ℓf / \ell. Each pair of siblings (v,w)(v, w) carries Bézout cofactors. One lifting step lifts the root pair, then the pairs below, using the lifted product at each node as the "ff" of its children. By Theorem 1, the factorisation lifts node by node. If the tree is balanced, the polynomials at each level have total degree at most nn, and a level costs O(M(n))O(M(n)) operations, so a step costs O(M(n)log⁡r)O(M(n) \log r) operations modulo the new modulus [GG2003, Ch. 15].

Algorithm 1: Multifactor Hensel lifting

Input: ff with p∤ℓ=lc⁡(f)p \nmid \ell = \operatorname{lc}(f), monic pairwise coprime g1,…,grg_1, \ldots, g_r with f≡ℓ∏gi(modp)f \equiv \ell \prod g_i \pmod p, a target N≥1N \ge 1.

Output: monic Gi≡gi(modp)G_i \equiv g_i \pmod p with f≡ℓ∏Gi(modpN)f \equiv \ell \prod G_i \pmod{p^N}.

  1. Build the tree: repeatedly multiply the two polynomials of smallest degree among the leaves and the products made so far. Compute the Bézout cofactors of each pair of siblings modulo pp with the extended Euclidean algorithm.
  2. Replace ff by ℓ−1f mod pN\ell^{-1} f \bmod p^N, which is monic.
  3. Let N=e0>e1>⋯>ek=1N = e_0 > e_1 > \cdots > e_k = 1 with ei+1=⌈ei/2⌉e_{i+1} = \lceil e_i/2 \rceil. For i=k−1,…,0i = k - 1, \ldots, 0, lift the whole tree from pei+1p^{e_{i+1}} to peip^{e_i} with Theorem 1 at every node, using m=pei+1m = p^{e_{i+1}} and m1=pei−ei+1m_1 = p^{e_i - e_{i+1}}; in the last step, do not lift the cofactors.
  4. Return the lifted leaves.

Choosing the two factors of smallest degree, as in Huffman coding, balances the tree by degree rather than by the number of leaves, which minimises the total degree of the products that have to be lifted.

Use in FLINT#

slong _fmpz_poly_hensel_start_lift(fmpz_poly_factor_t lifted_fac, slong * link, fmpz_poly_t * v, fmpz_poly_t * w, const fmpz_poly_t f, const nmod_poly_factor_t local_fac, slong target_exp)
void fmpz_poly_hensel_lift_tree(slong * link, fmpz_poly_t * v, fmpz_poly_t * w, fmpz_poly_t f, slong r, const fmpz_t p, slong e0, slong e1, slong inv)
void fmpz_poly_hensel_lift(fmpz_poly_t Gout, fmpz_poly_t Hout, fmpz_poly_t Aout, fmpz_poly_t Bout, const fmpz_poly_t f, const fmpz_poly_t g, const fmpz_poly_t h, const fmpz_poly_t a, const fmpz_poly_t b, const fmpz_t p, const fmpz_t p1)

Referenced from#

References#

  • [Hen1908] K. Hensel. Theorie der algebraischen Zahlen. Teubner, Leipzig, 1908.
  • [Zas1969] H. Zassenhaus. On Hensel factorization, I. Journal of Number Theory 1 (1969), 291–311. https://doi.org/10.1016/0022-314X(69)90047-X
  • [GG2003] J. von zur Gathen and J. Gerhard, Modern Computer Algebra, second edition, Cambridge University Press (2003)
Cite this page

The FLINT team, “Hensel lifting,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/polynomials/hensel-lifting.html (generated 2026-11-02).