Public result of executable Berlekamp factorization.
- input : FpPoly p
The polynomial supplied to Berlekamp factorization.
The returned monic irreducible factors, with repetition.
Instances For
Multiply a list of F_p[x] factors in stored order, starting from 1.
Equations
- Hex.Berlekamp.factorProduct factors = List.foldl (fun (acc factor : Hex.FpPoly p) => acc * factor) 1 factors
Instances For
Product of the factors returned by a Factorization.
Equations
- result.product = Hex.Berlekamp.factorProduct result.factors
Instances For
Empty-list base case for factorProduct.
Unfold a Factorization's product to the factorProduct of its stored factors.
The gcd candidate attached to one field constant c.
Equations
- Hex.Berlekamp.splitFactorAt f witness c = Hex.DensePoly.gcd f (witness - Hex.FpPoly.C c)
Instances For
GCD candidate that resumes the reference Euclidean sequence from a cached
witness remainder only when the current factor is positive-degree and strictly
smaller than the original witness. This is exposed so the compiled profiler
can exercise the production primitive directly; factorization clients should
normally use kernelWitnessSplit?.
Equations
- One or more equations did not get rendered due to their size.
Instances For
With the actual witness remainder cached, splitFactorCached is exactly
the reference candidate, including its unnormalised executable representative.
Search the Berlekamp split candidates gcd(f, h - c) over all constants
c : F_p, returning the first nontrivial factorization found.
A witness w can only split f when w mod f is nonconstant: every
candidate gcd(f, w - c) reduces to gcd(f, (w mod f) - c), which is f
or a unit (never a proper factor) once w mod f is a field constant. The
leading (witness % f).size ≤ 1 guard skips the whole p-wide constant
sweep in that case; the dominant cost once a factor is already irreducible,
since every kernel witness is then constant modulo it. The guard is
value-preserving: kernelWitnessSplitAux_none_of_mod_size_le_one proves the
skipped sweep would have returned none (over a field), so this agrees with
the unguarded search at every input. When the current factor has positive
degree and is strictly smaller than the original witness, the sweep also
resumes each GCD from this cached remainder with the reference execution's
remaining fuel; cachedSplitAux_eq proves the exact representative and result
are unchanged over a prime modulus, which is the intended domain of this
Berlekamp API. This cannot be an @[csimp] swap: the optimized aux has an
extra cached-remainder argument and its equality proof needs PrimeModulus,
so the reference aux instead remains intact behind an explicit equality.
Equations
- Hex.Berlekamp.kernelWitnessSplit? f witness = if Hex.DensePoly.size (witness % f) ≤ 1 then none else Hex.Berlekamp.cachedSplitAux✝ f witness (witness % f) p 0
Instances For
Budget for the residue scan of the root-extraction path. Two tests, both read
off the degree and the field size alone, so the decision is deterministic and
reads nothing about the coefficients of f.
deg f ≤ p is necessary: F_p has p elements, so a polynomial with
deg f distinct roots in F_p cannot have degree above p. A scan of a
higher-degree input can never succeed, so it is never started.
25 * p ≤ (deg f)^2 keeps the scan cheap against the work it would replace.
The scan is one Horner evaluation per residue, p * deg f modular
multiplications; the fixed-space matrix multiplies deg f polynomials modulo
f, each product quadratic in the degree, so about (deg f)^3. The test
therefore admits the scan only when it costs about a twenty-fifth of the matrix
build alone. Measured on the diagnostic grid of issue #9157, an
admitted-but-rejected scan costs between 0.8% and 2.3% of berlekampFactor,
falling as the degree grows.
Together the two tests select 5 √p ≤ deg f ≤ p: a completely split image is a
plausible thing to meet only when the degree is comparable to the field size.
Equations
- Hex.Berlekamp.rootScanBudget f = (decide (2 ≤ Hex.DensePoly.size f) && decide (Hex.DensePoly.size f ≤ p + 1) && decide (25 * p ≤ (Hex.DensePoly.size f - 1) * (Hex.DensePoly.size f - 1)))
Instances For
The length test of the root-extraction path: a list of roots accounts for
all of f exactly when there are deg f of them, and then the monic linear
factors it names are the complete factorization.
Equations
- Hex.Berlekamp.rootFactorsOf f roots = if roots.length + 1 = Hex.DensePoly.size f then some (List.map Hex.Berlekamp.primeFieldLinearFactor roots) else none
Instances For
The root-extraction path: enumerate the roots of f in F_p and, when there
are deg f of them, return the corresponding monic linear factors.
A squarefree f splits into distinct linear factors exactly when it has
deg f roots in F_p, and the scan is its own certificate: the length test is
what makes the result sound, so no separate complete-splitting test is computed
and no Boolean is trusted unchecked. Hex.Berlekamp.eq_foldl_rootsIn_of_length
turns the length test into the reconstruction ∏ (X - rᵢ) = f.
Returning none costs the scan; rootScanBudget bounds that cost.
Equations
Instances For
Compute the Berlekamp factorization of a monic polynomial over F_p.
There is one selection point. When the residue scan is affordable and finds
deg f roots, f is the product of the corresponding monic linear factors and
those are returned directly. Otherwise the fixed-space kernel of Q_f - I is
built and the input is fully split with the resulting kernel representatives.
Both branches return factors whose product is f; the theorems below are
proved for the two branches separately and stated only about
berlekampFactor. The root-extraction branch emits monic linear factors; the
kernel branch emits raw gcd leaves, monic only up to a unit, whose
irreducibility comes from berlekampFactor_factors_irreducible on a
square-free input.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The root-extraction branch of berlekampFactor.
What a successful root extraction records: the input has positive degree,
the scan found deg f roots, and the returned factors are their monic linear
factors.
Root extraction never fires on a constant: the budget requires positive degree.
Reconstruction. The monic linear factors of the enumerated roots
multiply back to f.
The root-extraction branch emits one factor per root, and the budget forces
f to have positive degree, so the result is never empty.
Every factor of the root-extraction branch is genuinely linear.
Every factor of the root-extraction branch has degree one, hence positive degree.
The root-extraction branch returns distinct factors: distinct roots give distinct linear factors, and the residue scan lists each root once.
Definitional unfolding of splitFactorAt to its underlying gcd candidate.
Any successful Berlekamp split records a factor and cofactor whose product is the original polynomial.
Any successful Berlekamp split is nontrivial: the returned factor is neither
0, 1, nor the full input polynomial.
Any successful Berlekamp split returns a factor strictly smaller in size than
the input. This is the strict-descent companion of kernelWitnessSplit_nontrivial
and is what drives the Nodup invariant on the running factor list.
Executable search reflection for one field constant: if the gcd candidate
attached to c is nonzero, nonconstant, and strictly smaller in size than
the input, then the guarded Berlekamp witness search succeeds. The guard
cannot fire here: a genuine split means witness is nonconstant modulo f.
Transport of a failed Berlekamp split search across a unit scaling: if no
kernel-witness split of g is found, none is found for scale c g either
(c ≠ 0). Lets callers move a no-split fact from a raw factor to its monic
associate.
Cons-expansion for factorProduct: pulling the head factor out of the running
product. Useful for downstream proofs that reason about factorProduct without
unfolding the underlying List.foldl.
factorProduct distributes over list append.
The Berlekamp factor list's product equals the input polynomial. Fully
splitting preserves factorProduct without using square-freeness, so the
product equality holds for every monic input.
The executable Berlekamp factorization preserves the input polynomial as the product of the returned factors for any monic input.
Executable Berlekamp factorization always retains at least one factor.
A successful kernel-witness split returns a factor of positive degree, so the recursive splitter always makes progress.
Structural lemma about berlekampFactor output: if its factors list has
length at most one, then every fixed-space kernel witness yields
kernelWitnessSplit? = none. This is the loop-tracing half of the parent
Berlekamp completeness theorem; the algebraic half (no kernel-witness split
forces irreducibility for square-free monic inputs) belongs to a separate
Mathlib-free finite-field module.
Two distinct elements of a Nodup list of FpPoly p have a product that
divides the list's product.
Abstract form of berlekampFactor.factors.Nodup: when no positive-degree
polynomial squares to a divisor of f, the executable Berlekamp factor list
has no duplicates. The Mathlib-free squareness-implies-irreducibility chain
discharges this hypothesis from gcd f f' = 1; see
Hex.Berlekamp.berlekampFactor_factors_nodup in
HexBerlekamp/RabinSoundness.lean.
Abstract pairwise-coprime form of berlekampFactor's output: when no
positive-degree polynomial squares to a divisor of f, distinct factors in
the returned list share no positive-degree common divisor. This strengthens
berlekampFactor_factors_nodup_of_no_squared from "distinct values" to "no
shared positive-degree divisor". The Mathlib-free squareness-implies-
irreducibility chain discharges the no-squared hypothesis from
gcd f f' = 1; see callers that pair this with
isUnitPolynomial_of_squareFree_of_squared_dvd.
Abstract form of berlekampFactor's output factor-degree positivity: if
the input polynomial has positive degree, then every factor in the executable
Berlekamp factor list has positive degree. The squareness-free hypothesis
needed by berlekampFactor_factors_nodup_of_no_squared is not needed here:
positivity is preserved by every split regardless of square-freeness.
For a monic input of size ≤ 1, the executable Berlekamp factor list is
exactly the singleton [f]. A polynomial of size ≤ 1 has no positive-degree
divisors, so it admits no kernel-witness split and fullySplit emits it as a
leaf.
Every factor in the Berlekamp factor list is nonzero. Splits the positive-
degree case (where every factor has positive degree via
berlekampFactor_factors_pos_degree) from the size-≤-1 case (where the factor
list is the singleton [f] and f is monic, hence nonzero).
Per-factor no-split. Every factor returned by the executable Berlekamp
factorization of a monic input resists every fixed-space kernel-witness split.
The f.size + 1 fuel always suffices to reach the witness-irreducible leaves.
Every factor returned by the executable Berlekamp factorization divides the
input. Immediate from factorProduct_berlekampFactor.