github.com/cloudflare/circl@v1.5.0/math/polynomial/polynomial.go (about) 1 // Package polynomial provides representations of polynomials over the scalars 2 // of a group. 3 package polynomial 4 5 import "github.com/cloudflare/circl/group" 6 7 // Polynomial stores a polynomial over the set of scalars of a group. 8 type Polynomial struct { 9 // Internal representation is in polynomial basis: 10 // Thus, 11 // p(x) = \sum_i^k c[i] x^i, 12 // where k = len(c)-1 is the degree of the polynomial. 13 c []group.Scalar 14 } 15 16 // New creates a new polynomial given its coefficients in ascending order. 17 // Thus, 18 // 19 // p(x) = \sum_i^k c[i] x^i, 20 // 21 // where k = len(c)-1 is the degree of the polynomial. 22 // 23 // The zero polynomial has degree equal to -1 and can be instantiated passing 24 // nil to New. 25 func New(coeffs []group.Scalar) (p Polynomial) { 26 if l := len(coeffs); l != 0 { 27 p.c = make([]group.Scalar, l) 28 for i := range coeffs { 29 p.c[i] = coeffs[i].Copy() 30 } 31 } 32 33 return 34 } 35 36 // Degree returns the degree of the polynomial. The zero polynomial has degree 37 // equal to -1. 38 func (p Polynomial) Degree() int { 39 i := len(p.c) - 1 40 for i > 0 && p.c[i].IsZero() { 41 i-- 42 } 43 return i 44 } 45 46 // Evaluate returns the evaluation of p on x. 47 func (p Polynomial) Evaluate(x group.Scalar) group.Scalar { 48 px := x.Group().NewScalar() 49 if l := len(p.c); l != 0 { 50 px.Set(p.c[l-1]) 51 for i := l - 2; i >= 0; i-- { 52 px.Mul(px, x) 53 px.Add(px, p.c[i]) 54 } 55 } 56 return px 57 } 58 59 // Coefficient returns a deep-copy of the n-th polynomial's coefficient. 60 // Note coefficients are sorted in ascending order with respect to the degree. 61 func (p Polynomial) Coefficient(n uint) group.Scalar { 62 if int(n) >= len(p.c) { 63 panic("polynomial: invalid index for coefficient") 64 } 65 return p.c[n].Copy() 66 } 67 68 // LagrangePolynomial stores a Lagrange polynomial over the set of scalars of a group. 69 type LagrangePolynomial struct { 70 // Internal representation is in Lagrange basis: 71 // Thus, 72 // p(x) = \sum_i^k y[i] L_j(x), where k is the degree of the polynomial, 73 // L_j(x) = \prod_i^k (x-x[i])/(x[j]-x[i]), 74 // y[i] = p(x[i]), and 75 // all x[i] are different. 76 x, y []group.Scalar 77 } 78 79 // NewLagrangePolynomial creates a polynomial in Lagrange basis given a list 80 // of nodes (x) and values (y), such that: 81 // 82 // p(x) = \sum_i^k y[i] L_j(x), where k is the degree of the polynomial, 83 // L_j(x) = \prod_i^k (x-x[i])/(x[j]-x[i]), 84 // y[i] = p(x[i]), and 85 // all x[i] are different. 86 // 87 // It panics if one of these conditions does not hold. 88 // 89 // The zero polynomial has degree equal to -1 and can be instantiated passing 90 // (nil,nil) to NewLagrangePolynomial. 91 func NewLagrangePolynomial(x, y []group.Scalar) (l LagrangePolynomial) { 92 if len(x) != len(y) { 93 panic("lagrange: invalid length") 94 } 95 96 if !areAllDifferent(x) { 97 panic("lagrange: x[i] must be different") 98 } 99 100 if n := len(x); n != 0 { 101 l.x, l.y = make([]group.Scalar, n), make([]group.Scalar, n) 102 for i := range x { 103 l.x[i], l.y[i] = x[i].Copy(), y[i].Copy() 104 } 105 } 106 107 return 108 } 109 110 func (l LagrangePolynomial) Degree() int { return len(l.x) - 1 } 111 112 func (l LagrangePolynomial) Evaluate(x group.Scalar) group.Scalar { 113 px := x.Group().NewScalar() 114 tmp := x.Group().NewScalar() 115 for i := range l.x { 116 LjX := baseRatio(uint(i), l.x, x) 117 tmp.Mul(l.y[i], LjX) 118 px.Add(px, tmp) 119 } 120 121 return px 122 } 123 124 // LagrangeBase returns the j-th Lagrange polynomial base evaluated at x. 125 // Thus, L_j(x) = \prod (x - x[i]) / (x[j] - x[i]) for 0 <= i < k, and i != j. 126 func LagrangeBase(jth uint, xi []group.Scalar, x group.Scalar) group.Scalar { 127 if jth >= uint(len(xi)) { 128 panic("lagrange: invalid index") 129 } 130 return baseRatio(jth, xi, x) 131 } 132 133 func baseRatio(jth uint, xi []group.Scalar, x group.Scalar) group.Scalar { 134 num := x.Copy() 135 num.SetUint64(1) 136 den := x.Copy() 137 den.SetUint64(1) 138 139 tmp := x.Copy() 140 for i := range xi { 141 if uint(i) != jth { 142 num.Mul(num, tmp.Sub(x, xi[i])) 143 den.Mul(den, tmp.Sub(xi[jth], xi[i])) 144 } 145 } 146 147 return num.Mul(num, den.Inv(den)) 148 } 149 150 func areAllDifferent(x []group.Scalar) bool { 151 m := make(map[string]struct{}) 152 for i := range x { 153 k, err := x[i].MarshalBinary() 154 if err != nil { 155 panic(err) 156 } 157 if _, exists := m[string(k)]; exists { 158 return false 159 } 160 m[string(k)] = struct{}{} 161 } 162 return true 163 }