Number of quadratic-doubling steps needed to reach precision p^k
from data valid modulo p. Returns 0 when k ≤ 1 (no doubling needed)
and ⌊log₂ (k - 1)⌋ + 1 otherwise, the least n with k ≤ 2 ^ n.
This remains the public fuel bound used by downstream recombination searches.
Instances For
Zero requested precision needs no quadratic doubling steps.
Precision p^1 is the input precision, so it needs no doubling steps.
Canonicalise every component of a quadratic lift at precision p^k.
Equations
- Hex.ZPoly.reduceLift p k r = { g := r.g.reduceModPow p k, h := r.h.reduceModPow p k, s := r.s.reduceModPow p k, t := r.t.reduceModPow p k }
Instances For
Return a lift reduced at exactly exponent k. Recursing through
ceil(k / 2) limits a correction's transient working exponent to k when
even and k + 1 when odd, instead of the next power of two.
This is the specification; liftExactImpl is the compiled shape.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Runtime shape of liftExact, dropping the canonicalisation that
reduceLift_step_eq_of_even proves redundant. For even k the preceding step
runs at modulus p^(k/2) and returns coefficients already canonical modulo
(p^(k/2))^2 = p^k; only an odd k, whose step overshoots to p^(k+1),
needs the descent back to p^k. Proved equal to liftExact in
liftExact_eq_impl.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Proof-backed compiled implementation of the exact-exponent quadratic recursion.
Lift a Bezout-witnessed factorisation modulo p to one valid modulo
p^k by iterating quadraticHenselStep at exact requested exponents.
Each recursive level first reaches p^ceil(k/2), doubles once, and reduces
to p^k. This uses the same logarithmic number of quadratic steps as the
power-of-two schedule while avoiding excess big-integer precision.
Equations
- Hex.ZPoly.henselLiftQuadratic p k f g h s t = Hex.ZPoly.liftExact p f k { g := g, h := h, s := s, t := t }
Instances For
Exact-exponent lift of only the final two factors. Earlier levels retain Bezout witnesses for the next correction; the last level omits the witness update because multifactor recursion consumes only the two factors.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Runtime shape of henselLiftFactors. The final factor-only step obeys the
same coefficient-range invariant as the full step
(quadraticHenselFactors_canonical), so for even k the closing pair of
reductions is the identity. Proved equal to henselLiftFactors in
henselLiftFactors_eq_impl.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Proof-backed compiled implementation of the factor-only exact lift.
The factor-only final step is byte-identical to projecting the full lift.
Both outputs of the factor-only exact lift are canonical modulo p^k:
either branch of henselLiftFactors ends in ZPoly.reduceModPow _ p k.
This is what lets the multifactor tree skip the reduction at every leaf below
the root, since each recursive call's target is a henselLiftFactors output.
Sum of polynomial degrees used to estimate the work on one side of a multifactor Hensel split.
Equations
- Hex.ZPoly.factorDegreeSum factors = List.foldl (fun (total : Nat) (g : Hex.ZPoly) => total + Hex.DensePoly.natDegree g) 0 factors
Instances For
Largest polynomial degree in a prospective multifactor Hensel node.
Equations
- Hex.ZPoly.factorMaxDegree factors = List.foldl (fun (largest : Nat) (g : Hex.ZPoly) => max largest (Hex.DensePoly.natDegree g)) 0 factors
Instances For
Choose a nontrivial prefix split of the factor order supplied to a multifactor Hensel node. When one modular factor has more than half the total degree, choose the prefix split whose two total degrees are closest; this avoids recursively pairing that dominant factor with a much smaller neighbour when the incoming order permits it. Otherwise keep the count-halving tree, whose deliberately unbalanced degree splits can make the root XGCD much cheaper. The final clamp makes the result valid for every list of length at least two, independently of the degree data.
This definition is deliberately opaque across module boundaries: downstream
proofs should use balancedSplitIndex_pos and
balancedSplitIndex_lt_length, rather than unfold the runtime heuristic.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The balanced split leaves at least one factor on its left.
A balanced split of at least two factors leaves at least one factor on its right.
Recursive list-shape worker behind multifactorLiftQuadratic, shaped as a
count-balanced product tree except where a dominant-degree factor calls for a
degree-aware split. At each non-singleton step it lifts the left product g
against the right product h via henselLiftFactors, then recurses into both
sides with the lifted sub-products as the new targets. The two recursive
outputs are concatenated L-then-R, so the returned array stays in the
original factor order. The singleton case returns the input reduced modulo
p^k; the empty case returns the empty array. Every output is the same reduced
value because the Hensel lift is unique modulo p^k.
Equations
- One or more equations did not get rendered due to their size.
- Hex.ZPoly.multifactorLiftQuadraticList p k f [] = #[]
- Hex.ZPoly.multifactorLiftQuadraticList p k f [_g] = #[f.reduceModPow p k]
Instances For
Runtime shape of multifactorLiftQuadraticList. The extra canonical flag
records whether the incoming target is already reduced modulo p^k. Every
recursive call passes a henselLiftFactors output, which
henselLiftFactors_canonical shows is canonical, so only the root -- the one
target the caller supplies as an arbitrary integer polynomial -- can still need
the leaf reduction. Every singleton subtree below a split therefore drops one
full-precision reduction; on a two-factor lift at odd precision that is both
output factors. Proved equal to
multifactorLiftQuadraticList in multifactorLiftQuadraticList_eq_impl.
Equations
Instances For
Root entry point of the compiled tree walk: the caller's target is an arbitrary integer polynomial, so the root leaf still reduces.
Equations
- Hex.ZPoly.multifactorLiftQuadraticListRoot p k f factors = Hex.ZPoly.multifactorLiftQuadraticListImpl p k false f factors
Instances For
Proof-backed compiled implementation of the multifactor tree walk.
Quadratic multifactor Hensel lift.
Lifts an ordered array of factors of f from congruence modulo p to
congruence modulo p^k using the doubling step
Hex.ZPoly.quadraticHenselStep
inside the guarded multifactor product tree.
Equations
- Hex.ZPoly.multifactorLiftQuadratic p k f factors = Hex.ZPoly.multifactorLiftQuadraticList p k f factors.toList
Instances For
The proof state carried by one quadratic Hensel loop modulus m. A
caller writing proofs against this invariant must supply three conjuncts,
in this order:
Product congruence:
acc.g * acc.h ≡ f (mod m);Bezout congruence:
acc.s * acc.g + acc.t * acc.h ≡ 1 (mod m);Leading factor monic:
acc.gis monic.
The forward theorem QuadraticLiftLoopInvariant.of_product_bezout_monic takes
the three facts in the same order. Together they are exactly the preconditions
consumed by one application of Hex.ZPoly.quadraticHenselStep and,
inductively, by the exact-exponent recursion.
Equations
Instances For
Constructor for the initial quadratic split invariant from the three proof obligations supplied by factor-product, Bezout, and monicness facts.
Product-congruence projection of QuadraticLiftLoopInvariant.
Bezout-congruence projection of QuadraticLiftLoopInvariant.
Monicness projection of QuadraticLiftLoopInvariant: the leading factor
acc.g is monic.
One quadratic step preserves the loop invariant while replacing m by m*m.
This is the local invariant-preservation surface consumed by the quadratic lift recursion.
The doubling count reaches the requested precision exponent.
After quadraticDoublingSteps k iterations from precision exponent 1, the
quadratic wrapper has reached exponent 2 ^ quadraticDoublingSteps k, which
is at least k.
The cofactor produced by henselLiftQuadratic is congruent to the input
cofactor modulo p. Every exact-exponent step adjusts h by a multiple of
the current modulus, and canonical reduction preserves the base congruence.
This is the surface used downstream by the multifactor _of_factorsModP
boundary theorem: it lets the recursive call on lifted.h, rest reuse the
same mod-p product hypothesis the caller supplies for the head split.
The leading factor produced by henselLiftQuadratic is congruent to the input
leading factor modulo p. Parallel to henselLiftQuadratic_h_congr_mod_base;
used downstream to show each output of multifactorLiftQuadratic reduces mod
p to its corresponding input factor.
The factor-only lift's left result retains the input factor modulo p.
The factor-only lift's right result retains the input cofactor modulo p.
Public correctness contract for the binary quadratic wrapper: starting
from a QuadraticLiftLoopInvariant p f { g, h, s, t }, the lifted pair
satisfies lifted.g * lifted.h ≡ f (mod p^k).
The factor-only exact lift multiplies to the target modulo p^k.
Public Bezout-pair contract for the binary quadratic wrapper. The lifted
Bezout pair remains valid modulo the exact requested precision p^k.
Recursive preconditions required by the guarded quadratic multifactor tree.
In the non-singleton arm the factor list is split at balancedSplitIndex; the
three conjuncts
are exactly the inputs henselLiftFactors_spec consumes for the binary split
of the left product g := Array.polyProduct L.toArray against the right product
h := Array.polyProduct R.toArray, followed by the recursive preconditions for
each lifted sub-product:
QuadraticLiftLoopInvariantat modulusp; initial state package (product congruence, Bezout, monicness) for the binary exact lift;QuadraticMultifactorLiftInvariantfor the left half withlifted.1as the new target;QuadraticMultifactorLiftInvariantfor the right half withlifted.2as the new target.
The base cases impose the trivial obligations: congr 1 f (p ^ k) for the
empty list (vacuous product) and no preconditions for a singleton.
Equations
- One or more equations did not get rendered due to their size.
- Hex.ZPoly.QuadraticMultifactorLiftInvariant p k f [] = Hex.ZPoly.congr 1 f (p ^ k)
- Hex.ZPoly.QuadraticMultifactorLiftInvariant p k f [_g] = True
Instances For
The split-coprimality boundary data needed to initialise every quadratic split
in the balanced multifactor tree from factors modulo p.
In the non-singleton arm the factor list is split at balancedSplitIndex, and
the requirement is
that the normalised XGCD of the left lifted product
Array.polyProduct ((L.map FpPoly.liftToZ).toArray) against the right lifted
product Array.polyProduct ((R.map FpPoly.liftToZ).toArray) returns gcd = 1
in FpPoly p, and that the same coprimality holds recursively on each half.
The base cases (empty list, singleton) are vacuous.
Consumed by quadraticMultifactorLiftInvariant_of_factorsModP: the
per-split gcd = 1 lifts via normalizedXGCD_liftToZ_bezout_congr_of_gcd_eq_one
into the Bezout half of QuadraticLiftLoopInvariant.
Equations
- One or more equations did not get rendered due to their size.
- Hex.ZPoly.QuadraticMultifactorCoprimeSplits p [] = True
- Hex.ZPoly.QuadraticMultifactorCoprimeSplits p [_g] = True
Instances For
The product of the lifted factors is congruent to f modulo p^k,
under the recursive precondition package consumed by the quadratic
multifactor lifting tree.
The lift-uniqueness companion (linear-vs-quadratic agreement after
canonicalisation) lives in hex-hensel-mathlib.
If a bounded nonnegative cofactor h satisfies a coefficientwise congruence
g * h ≡ f (mod m) against monic g and f, then h is monic. The proof
compares the possible top coefficient of g * h with the top coefficient of
f; the coefficient bounds rule out wraparound modulo m.
Consumed by monic_reduceModPow_of_congr_mul_monic_monic, which specialises
this to the reduceModPow-canonicalised cofactor consumed by
henselLiftQuadratic_h_monic.
Specialisation of monic_of_congr_mul_monic_monic for cofactors already
canonicalised by Hex.ZPoly.reduceModPow; its coefficients automatically lie
in [0, p^k).
Consumed by henselLiftQuadratic_h_monic, where the correctness congruence
(lifted.g * lifted.h) ≡ f (mod p^k) already supplies a reduceModPow-form
cofactor.
The lifted monic factor lifted.g produced by henselLiftQuadratic is
monic. The exact-exponent recursion preserves Monic acc.g via
quadraticHenselStep_monic; each reduceModPow cleanup preserves it via
reduceModPow_monic_of_monic.
Consumed (alongside henselLiftQuadratic_h_monic) by
multifactorLiftQuadratic_each_monic to discharge per-output monicness inside
the balanced split tree.
The lifted cofactor lifted.h produced by henselLiftQuadratic is monic
when f is monic. Derived from the cofactor monic lemma
monic_reduceModPow_of_congr_mul_monic_monic applied to the correctness congruence
(lifted.g * lifted.h) ≡ f (mod p^k) and henselLiftQuadratic_g_monic.
Consumed (alongside henselLiftQuadratic_g_monic) by
multifactorLiftQuadratic_each_monic to discharge per-output monicness inside
the balanced split tree.
The left factor of the factor-only lift is monic.
The right factor of the factor-only lift is monic when the target is.
A product of monic integer polynomials is monic.
The Array.polyProduct of a list of monic integer polynomials is monic.
Needed because a balanced split multiplies a whole half of the factor list into
each side of the binary Hensel step, so the leading factor is a product rather
than a single input factor.
The lifted product splits across a list concatenation: multiplying the lifted products of two halves equals the lifted product of the concatenation. This is the algebraic content that lets a balanced split's two sub-products recombine to the whole modular product.
Build the recursive quadratic multifactor lift invariant from the natural
mod-p boundary facts. The caller supplies, for the list of FpPoly p
factors lifted via FpPoly.liftToZ:
hf_monicandhfactors_monic;fand every factor is monic (each split's leading factor is monic;fitself is needed recursively as the doubling-loop's target stays monic);hproduct_mod_p; the lifted ordered product is congruent tofmodp(feeds the product half ofQuadraticLiftLoopInvariant);hcoprime : QuadraticMultifactorCoprimeSplits p factors; every split's normalised XGCD hasgcd = 1overFpPoly p(feeds the Bezout half vianormalizedXGCD_liftToZ_bezout_congr_of_gcd_eq_one);hnonempty; the factor list is nonempty (rules out the vacuous base case which would forcecongr 1 f (p^k)).
The recursive tail re-establishes the same package using
henselLiftQuadratic_h_congr_mod_base for the lifted complementary factor
and henselLiftQuadratic_h_monic for its monicness.
The multifactorLiftQuadratic output has one entry per input factor.
Used by the Mathlib-side injectivity wrapper to relate output array
indices to original modular-factor indices.
The empty-input boundary of multifactorLiftQuadratic: no factors in,
no factors out.
The singleton-input boundary of multifactorLiftQuadratic: a single
factor input collapses to the target polynomial reduced modulo p^k. The
input factor is discarded because the only remaining lift target is f itself
under the trivial split f = f * 1.
Each output of multifactorLiftQuadratic is congruent modulo p to the
corresponding input factor, given the monic / lift-invariant / mod-p product
hypotheses of quadraticMultifactorLiftInvariant_of_factorsModP.
This is the per-output mod-p preservation surface consumed by the Mathlib
theorem henselLiftData_liftedFactor_injective: pairing it with
Nodup of the original modular factor list shows distinct lifted factors
remain distinct as integer polynomials.
The size equality multifactorLiftQuadratic_size_eq_input is the companion
fact relating output array indices to input array indices.
Every output of Hex.ZPoly.multifactorLiftQuadratic is monic when the input
polynomial f is monic and the quadratic multifactor lift invariant package
holds.
The proof applies Hex.ZPoly.henselLiftFactors_fst_monic and
Hex.ZPoly.henselLiftFactors_snd_monic at each balanced split node,
providing the monicness fact used by the Mathlib-facing wrapper.