148 lines
3.9 KiB
Go
148 lines
3.9 KiB
Go
package poly
|
|
|
|
import (
|
|
"slices"
|
|
|
|
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
|
)
|
|
|
|
// A polynomial in lagrange form
|
|
type Polynomial = []fr.Element
|
|
|
|
// A polynomial in monomial form
|
|
type PolynomialCoeff = []fr.Element
|
|
|
|
// PolyAdd adds two polynomials in coefficient form and returns the result.
|
|
// The resulting polynomial has a degree equal to the maximum degree of the input polynomials.
|
|
func PolyAdd(a, b PolynomialCoeff) PolynomialCoeff {
|
|
minPolyLen := min(numCoeffs(a), numCoeffs(b))
|
|
maxPolyLen := max(numCoeffs(a), numCoeffs(b))
|
|
|
|
result := make([]fr.Element, maxPolyLen)
|
|
|
|
for i := 0; i < int(minPolyLen); i++ {
|
|
result[i].Add(&a[i], &b[i])
|
|
}
|
|
|
|
// If a has more coefficients than b, copy the remaining coefficients from a
|
|
// into result
|
|
// If b has more coefficients than a, copy the remaining coefficients of b
|
|
// and copy them into result
|
|
if int(numCoeffs(a)) > int(minPolyLen) {
|
|
for i := minPolyLen; i < numCoeffs(a); i++ {
|
|
result[i].Set(&a[i])
|
|
}
|
|
} else if numCoeffs(b) > minPolyLen {
|
|
for i := minPolyLen; i < numCoeffs(b); i++ {
|
|
result[i].Set(&b[i])
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// PolyMul multiplies two polynomials in coefficient form and returns the result.
|
|
// The degree of the resulting polynomial is the sum of the degrees of the input polynomials.
|
|
func PolyMul(a, b PolynomialCoeff) PolynomialCoeff {
|
|
a = removeTrailingZeros(a)
|
|
b = removeTrailingZeros(b)
|
|
numCoeffsA := numCoeffs(a)
|
|
numCoeffsB := numCoeffs(b)
|
|
|
|
if numCoeffsA == 0 || numCoeffsB == 0 {
|
|
return []fr.Element{}
|
|
}
|
|
|
|
// The degree of result will be degree(a) + degree(b) = numCoeffs(a) + numCoeffs(b) - 1
|
|
productDegree := numCoeffsA + numCoeffsB
|
|
result := make([]fr.Element, productDegree-1)
|
|
|
|
for i := uint64(0); i < numCoeffsA; i++ {
|
|
for j := uint64(0); j < numCoeffsB; j++ {
|
|
mulRes := fr.Element{}
|
|
mulRes.Mul(&a[i], &b[j])
|
|
result[i+j].Add(&result[i+j], &mulRes)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// equalPoly checks if two polynomials in coefficient form are equal.
|
|
// It removes trailing zeros (normalizes) before comparison.
|
|
func equalPoly(a, b PolynomialCoeff) bool {
|
|
a = removeTrailingZeros(a)
|
|
b = removeTrailingZeros(b)
|
|
|
|
// Two polynomials that do not have the same
|
|
if numCoeffs(a) != numCoeffs(b) {
|
|
return false
|
|
}
|
|
|
|
polyLen := numCoeffs(a)
|
|
if polyLen == 0 {
|
|
return true
|
|
}
|
|
// Check element-wise equality
|
|
for i := uint64(0); i < polyLen; i++ {
|
|
if !a[i].Equal(&b[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// PolyEval evaluates a polynomial f(x) at a point z, computing f(z).
|
|
// The polynomial is given in coefficient form, and `z` is denoted as inputPoint.
|
|
func PolyEval(poly PolynomialCoeff, inputPoint fr.Element) fr.Element {
|
|
result := fr.NewElement(0)
|
|
|
|
for i := len(poly) - 1; i >= 0; i-- {
|
|
tmp := fr.Element{}
|
|
tmp.Mul(&result, &inputPoint)
|
|
result.Add(&tmp, &poly[i])
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// DividePolyByXminusA computes f(x) / (x - a) and returns the quotient.
|
|
//
|
|
// Note: (x-a) is not a factor of f(x), the remainder will not be returned.
|
|
//
|
|
// This was copied and modified from the gnark codebase.
|
|
func DividePolyByXminusA(poly PolynomialCoeff, a fr.Element) []fr.Element {
|
|
// clone the slice so we do not modify the slice in place
|
|
quotient := slices.Clone(poly)
|
|
|
|
var t fr.Element
|
|
|
|
for i := len(quotient) - 2; i >= 0; i-- {
|
|
t.Mul("ient[i+1], &a)
|
|
|
|
quotient[i].Add("ient[i], &t)
|
|
}
|
|
|
|
// the result is of degree deg(f)-1
|
|
return quotient[1:]
|
|
}
|
|
|
|
func numCoeffs(p PolynomialCoeff) uint64 {
|
|
return uint64(len(p))
|
|
}
|
|
|
|
// Removes the higher coefficients from the polynomial
|
|
// that are zero.
|
|
//
|
|
// This method assumes that the slice is a polynomial where
|
|
// the higher coefficients are placed at the end of the slice,
|
|
// ie f(x) = 5 + 6x + 10x^2 would be [5, 6, 10] as a slice.
|
|
//
|
|
// This therefore has no impact on the polynomial and is just
|
|
// normalizing the polynomial.
|
|
func removeTrailingZeros(slice []fr.Element) []fr.Element {
|
|
for len(slice) > 0 && slice[len(slice)-1].IsZero() {
|
|
slice = slice[:len(slice)-1]
|
|
}
|
|
return slice
|
|
}
|