2.5. Principal operations🔗
Arithmetic is coefficientwise where it can be (addition, subtraction,
negation, scalar and monomial scaling) and a schoolbook convolution for
multiplication. Each operation re-normalizes its result, so the output
is again a canonical representative. The standard Add, Sub, Neg,
and Mul instances on Hex.DensePoly invoke these, so
p + q, p - q, -p, and p * q notation works directly.
🔗def
Add two dense polynomials coefficientwise.
Kernel-facing specification: a single padded walk of the two coefficient
lists. Compiled code uses Hex.DensePoly.addImpl, the value-equal,
one-allocation Array.ofFn loop selected by
Hex.DensePoly.add_eq_impl.
🔗def
Subtract two dense polynomials coefficientwise.
Like Hex.DensePoly.add, this is a kernel-reduction-friendly
specification. Compiled code uses Hex.DensePoly.subImpl, the
value-equal Array.ofFn loop selected by
Hex.DensePoly.sub_eq_impl.
🔗def
Coefficientwise additive inverse, expressed through executable subtraction.
Kernel-facing specification (one Hex.DensePoly.sub against the zero
polynomial); compiled code uses Hex.DensePoly.negImpl, the value-equal
Array.map pass selected by Hex.DensePoly.neg_eq_impl.
🔗def
Multiply every coefficient by c.
Kernel-facing specification: one map over the reference coefficient list.
Compiled code uses Hex.DensePoly.scaleImpl, the value-equal
Array.map pass selected by
Hex.DensePoly.scale_eq_impl.
🔗def
Multiply by x^n.
Kernel-facing specification: one replicate-append of the reference
coefficient list. Compiled code uses
Hex.DensePoly.shiftImpl, the value-equal Array.append
implementation selected by Hex.DensePoly.shift_eq_impl.
🔗def
Schoolbook dense polynomial multiplication by direct coefficient convolution.
This definition is the kernel-reduction-friendly specification: the accumulator
is a plain list walked head-first, so reducing a concrete product costs one
cons-step per (i, j) coefficient pair instead of an O(size) list traversal
per Array access. Compiled code instead runs the in-place Array loop
Hex.DensePoly.mulImpl, selected by
Hex.DensePoly.mul_eq_impl.
Evaluation, composition, and the formal derivative complete the
operations. Evaluation and composition both use Horner's method.
🔗def
Evaluate a polynomial using Horner's method.
Kernel-facing specification: one cons walk of the coefficient list, highest
degree innermost. Compiled code uses Hex.DensePoly.evalImpl, the
value-equal downward Array.foldr loop with no intermediate list or
reverse allocation, selected by Hex.DensePoly.eval_eq_impl.
🔗def
Compose polynomials using Horner's method.
Kernel-facing specification: one cons walk of the coefficient list. Compiled
code uses Hex.DensePoly.composeImpl, the value-equal downward
Array.foldr loop selected by
Hex.DensePoly.compose_eq_impl.
🔗def
Formal derivative. The coefficient of x^i becomes (i + 1) * a_(i+1).
Kernel-facing specification: one cons walk of the coefficient tail. Compiled
code uses Hex.DensePoly.derivativeImpl, the value-equal
Array.ofFn loop selected by
Hex.DensePoly.derivative_eq_impl.
Each operation comes with a characterising coefficient law. These are
the lemmas downstream proofs rewrite with. The zero-absorption side
conditions (hzero) are discharged automatically over any semiring,
via the *_semiring/*_ring specializations.
🔗theorem
Coefficient law for addition. The explicit zero law is needed because the generic
Add/Zero interface does not imply 0 + 0 = 0.
🔗theorem
Characterising coefficient law for multiplication: each coefficient of p * q is computed by
the same nested schoolbook fold as the executable multiplication loop.
🔗theorem
Coefficient law for subtraction. The explicit zero law is needed because the generic
Sub/Zero interface does not imply 0 - 0 = 0.
🔗theorem
Characterising coefficient law for the formal derivative: the coefficient of
x^n in derivative p is (n + 1) * p.coeff (n + 1). The explicit zero law
((n + 1 : Nat) : R) * 0 = 0 is needed because the generic NatCast/Mul/Zero
interface does not guarantee it, mirroring the hypothesis on
Hex.DensePoly.coeff_scale.
2.5.1. Worked example: integer arithmetic🔗
The block below works over DensePoly Int. It builds a quadratic and a
monomial, then computes their sum and product, an evaluation, and a
derivative.
open Hex Hex.DensePoly
namespace HexPolyChapterArith
private def a : DensePoly Int := #p[1, 2, 3]
private def b : DensePoly Int := monomial 1 1
#guard a.toArray.toList = [1, 2, 3]
#guard b.toArray.toList = [0, 1]
#guard a.degree? = some 2
#guard a.support = [0, 1, 2]
#guard a.coeff 2 = 3
#guard #p[1, 2, 3, 0, 0] = a
#guard C (0 : Int) = (0 : DensePoly Int)
#guard (a + b).toArray.toList = [1, 3, 3]
#guard (a * b).toArray.toList = [0, 1, 2, 3]
#guard eval a 2 = 17
#guard (derivative a).toArray.toList = [2, 6]
end HexPolyChapterArith