One more than the largest exponent of a term array (0 when empty):
the number of coefficients its dense form stores.
Equations
- Hex.SparsePoly.termsBound ts = Array.foldl (fun (m : Nat) (t : Nat × R) => max m (t.fst + 1)) 0 ts
Instances For
Writing a sorted, in-bounds term list into an array puts each coefficient at its exponent and touches nothing else.
Write each term of an arbitrary term array into a coefficient array sized by the largest exponent. A conversion, not a canonicaliser: with duplicate exponents the last write wins, which is what makes the round-trip hypotheses below necessary.
Equations
- Hex.SparsePoly.coeffsOfTerms ts = Array.foldl (fun (acc : Array R) (t : Nat × R) => acc.setIfInBounds t.fst t.snd) (Array.replicate (Hex.SparsePoly.termsBound ts) 0) ts
Instances For
Keep the nonzero coefficients of a coefficient list with their
indices, starting at index i. Kernel-facing walk shared by
termsOfCoeffs and ofDense.
Equations
Instances For
Keep the nonzero coefficients of a coefficient array with their indices.
Equations
Instances For
The walk emits a canonical term list.
The walk stores exactly the nonzero coefficients: its coefficient
at i + e is the list's value at e.
The walk stores nothing below its starting index.
Convert to the dense representation: allocate degree + 1
coefficients and write each stored term into its slot, O(n) in the
degree whatever the term count. The one operation here whose cost is not
governed by the term count; no operation in this library is implemented
through it.
Equations
Instances For
The conversion preserves every coefficient.
Convert from the dense representation, keeping the nonzero coefficients with their indices.
Kernel-facing specification (an ordered List walk); compiled code uses
Hex.SparsePoly.ofDenseImpl, the value-equal array pass selected by
Hex.SparsePoly.ofDense_eq_impl.
Equations
Instances For
Array worker for ofDenseImpl: one indexed pass pushing the
nonzero coefficients.
Equations
Instances For
The array pass agrees with the kernel-facing list walk.
Runtime implementation of ofDense: one array pass (value-equal
to ofDense by ofDense_eq_impl, registered @[csimp]).
Equations
- Hex.SparsePoly.ofDenseImpl p = { terms := Hex.SparsePoly.termsOfCoeffsArray p.coeffs, canonical := ⋯ }
Instances For
The list walk and the array pass build the same polynomial.
Register the array pass as the compiled implementation of
ofDense.
The sparse image of a coefficient array stores exactly its coefficients.
Round trip through the dense coefficients: exact on canonical term arrays, and false without either half of the invariant (a stored zero vanishes, a duplicate is overwritten, unsorted input comes back sorted).
Round trip through the term list: exact on trailing-zero-free
coefficient arrays, and false without DensePolyNormalized (a trailing
zero is not reconstructed). It is DensePoly's own invariant that makes
the sparse round trip exact.
Round trip on the bundled types, dense side out: unconditional, because the type carries its canonical form.
Round trip on the bundled types, sparse side out: unconditional,
because DensePoly carries its trailing-zero-free form.
The dense conversion is injective, which is how sparse identities are transported back from dense ones.
The conversion sends zero to zero.
The conversion sends one to one.
The conversion sends monomials to dense monomials.
The conversion is additive.
The conversion commutes with negation.
The reverse conversion sends zero to zero.
The reverse conversion sends one to one.
The reverse conversion is additive.
The reverse conversion commutes with negation.
A fresh insertion is the inserted coefficient, with no algebra assumed.
Coefficients of a fold of sums, sparse side.
The pairwise-product coefficient, reorganised as a fold of
mulMonomial coefficients over the left operand's terms.
The pairwise product is the fold of monomial multiples: the shape the dense transport consumes.
The dense image of a fold of sums.
A fold of dense monomials at the stored terms rebuilds the dense image.
The dense image of a monomial multiple is the dense monomial product.
The conversion is multiplicative: the transported convolution. This
is where coeff_mul and the multiplicative ring laws come from.
The multiplicative homomorphism, dense side in.
The convolution, by transport: multiplication agrees with the dense coefficient sum.
Multiplication is associative.
Multiplication is commutative.
One is a right identity for multiplication.
One is a left identity for multiplication.
Zero absorbs on the right of multiplication.
Zero absorbs on the left of multiplication.
Multiplication distributes over addition on the left.
Multiplication distributes over addition on the right.
The zeroth power is one.
The conversion respects scalar multiplication.
The conversion sends constants to constants.
Scalar multiplication is multiplication by the constant.
Sparse monomials multiply exponentwise, by transport.
The power recurrence: with pow_zero this characterises the
binary powering completely, and together with coeff_mul it is
what the SPEC's coeff_pow obligation reduces to.
The leading coefficient is the coefficient at the last stored exponent.
The degree boundary transports: a leading coefficient is a
coefficient at the degree, and coeff_toDense alone says nothing about
where the degree is.
The leading coefficient transports through the dense conversion.
A sparse polynomial is monic when its leading coefficient is 1,
matching the DensePoly convention.
Equations
- s.Monic = (s.leadingCoeff = 1)
Instances For
Equations
Monicity transports through the dense conversion.