github.com/consensys/gnark-crypto@v0.14.0/ecc/bn254/fr/iop/polynomial.go (about) 1 // Copyright 2020 Consensys Software Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Code generated by consensys/gnark-crypto DO NOT EDIT 16 17 package iop 18 19 import ( 20 "encoding/binary" 21 "io" 22 "math/big" 23 "math/bits" 24 "runtime" 25 26 "github.com/consensys/gnark-crypto/ecc/bn254/fr" 27 "github.com/consensys/gnark-crypto/ecc/bn254/fr/fft" 28 ) 29 30 // Basis indicates the basis in which a polynomial is represented. 31 type Basis uint32 32 33 const ( 34 Canonical Basis = 1 << iota 35 Lagrange 36 LagrangeCoset 37 ) 38 39 // Layout indicates if a polynomial has a BitReverse or a Regular layout 40 type Layout uint32 41 42 const ( 43 Regular Layout = 8 << iota 44 BitReverse 45 ) 46 47 // Form describes the form of a polynomial. 48 // TODO should be a regular enum? 49 type Form struct { 50 Basis Basis 51 Layout Layout 52 } 53 54 // enum of the possible Form values for type-safe switches 55 // in this package 56 var ( 57 canonicalRegular = Form{Canonical, Regular} 58 canonicalBitReverse = Form{Canonical, BitReverse} 59 lagrangeRegular = Form{Lagrange, Regular} 60 lagrangeBitReverse = Form{Lagrange, BitReverse} 61 lagrangeCosetRegular = Form{LagrangeCoset, Regular} 62 lagrangeCosetBitReverse = Form{LagrangeCoset, BitReverse} 63 ) 64 65 // Polynomial wraps a polynomial so that it is 66 // interpreted as P'(X)=P(\omega^{s}X). 67 // Size is the real size of the polynomial (seen as a vector). 68 // For instance if len(P)=32 but P.Size=8, it means that P has been 69 // extended (e.g. it is evaluated on a larger set) but P is a polynomial 70 // of degree 7. 71 // blindedSize is the size of the polynomial when it is blinded. By 72 // default blindedSize=Size, until the polynomial is blinded. 73 type Polynomial struct { 74 *polynomial 75 shift int 76 size int 77 blindedSize int 78 } 79 80 // NewPolynomial returned a Polynomial from the provided coefficients in the given form. 81 // A Polynomial can be seen as a "shared pointer" on a list of coefficients. 82 // It is the responsibility of the user to call the Clone method if the coefficients 83 // shouldn't be mutated. 84 func NewPolynomial(coeffs *[]fr.Element, form Form) *Polynomial { 85 return &Polynomial{ 86 polynomial: newPolynomial(coeffs, form), 87 size: len(*coeffs), 88 blindedSize: len(*coeffs), 89 } 90 } 91 92 // Shift the wrapped polynomial; it doesn't modify the underlying data structure, 93 // but flag the Polynomial such that it will be interpreted as p(\omega^shift X) 94 func (p *Polynomial) Shift(shift int) *Polynomial { 95 p.shift = shift 96 return p 97 } 98 99 // BlindedSize returns the the size of the polynomial when it is blinded. By 100 // default blindedSize=Size, until the polynomial is blinded. 101 func (p *Polynomial) BlindedSize() int { 102 return p.blindedSize 103 } 104 105 // Size returns the real size of the polynomial (seen as a vector). 106 // For instance if len(P)=32 but P.Size=8, it means that P has been 107 // extended (e.g. it is evaluated on a larger set) but P is a polynomial 108 // of degree 7. 109 func (p *Polynomial) Size() int { 110 return p.size 111 } 112 113 // SetSize sets the size of the polynomial. 114 // size is the real size of the polynomial (seen as a vector). 115 // For instance if len(P)=32 but P.size=8, it means that P has been 116 // extended (e.g. it is evaluated on a larger set) but P is a polynomial 117 // of degree 7. 118 func (p *Polynomial) SetSize(size int) { 119 p.size = size 120 } 121 122 // SetBlindedSize sets the blinded size of the polynomial. 123 func (p *Polynomial) SetBlindedSize(size int) { 124 p.blindedSize = size 125 } 126 127 // Blind blinds a polynomial q by adding Q(X)*(X^{n}-1), 128 // where deg Q = blindingOrder and Q is random, and n is the 129 // size of q. Sets the result to p and returns it. 130 // 131 // blindingOrder is the degree of Q, where the blinding is Q(X)*(X^{n}-1) 132 // where n is the size of p. The size of p is modified since the underlying 133 // polynomial is of bigger degree now. The new size is p.Size+1+blindingOrder. 134 // 135 // /!\ The code panics if wq is not in canonical, regular layout 136 func (p *Polynomial) Blind(blindingOrder int) *Polynomial { 137 // check that p is in canonical basis 138 if p.Form != canonicalRegular { 139 panic("the input must be in canonical basis, regular layout") 140 } 141 142 // we add Q*(x^{n}-1) so the new size is deg(Q)+n+1 143 // where n is the size of wq. 144 newSize := p.size + blindingOrder + 1 145 146 // Resize p. The size of wq might has already been increased 147 // (e.g. when the polynomial is evaluated on a larger domain), 148 // if that's the case we don't resize the polynomial. 149 p.grow(newSize) 150 151 // blinding: we add Q(X)(X^{n}-1) to P, where deg(Q)=blindingOrder 152 var r fr.Element 153 154 for i := 0; i <= blindingOrder; i++ { 155 r.SetRandom() 156 (*p.coefficients)[i].Sub(&(*p.coefficients)[i], &r) 157 (*p.coefficients)[i+p.size].Add(&(*p.coefficients)[i+p.size], &r) 158 } 159 p.blindedSize = newSize 160 161 return p 162 } 163 164 // Evaluate evaluates p at x. 165 // The code panics if the function is not in canonical form. 166 func (p *Polynomial) Evaluate(x fr.Element) fr.Element { 167 168 if p.shift == 0 { 169 return p.polynomial.evaluate(x) 170 } 171 172 var g fr.Element 173 if p.shift <= 5 { 174 gen, err := fft.Generator(uint64(p.size)) 175 if err != nil { 176 panic(err) 177 } 178 g = smallExp(gen, p.shift) 179 x.Mul(&x, &g) 180 return p.polynomial.evaluate(x) 181 } 182 183 bs := big.NewInt(int64(p.shift)) 184 g = *g.Exp(g, bs) 185 x.Mul(&x, &g) 186 return p.polynomial.evaluate(x) 187 } 188 189 // Clone returns a deep copy of p. The underlying polynomial is cloned; 190 // see also ShallowClone to perform a ShallowClone on the underlying polynomial. 191 // If capacity is provided, the new coefficient slice capacity will be set accordingly. 192 func (p *Polynomial) Clone(capacity ...int) *Polynomial { 193 res := p.ShallowClone() 194 res.polynomial = p.polynomial.clone(capacity...) 195 return res 196 } 197 198 // ShallowClone returns a shallow copy of p. The underlying polynomial coefficient 199 // is NOT cloned and both objects will point to the same coefficient vector. 200 func (p *Polynomial) ShallowClone() *Polynomial { 201 res := *p 202 return &res 203 } 204 205 // GetCoeff returns the i-th entry of p, taking the layout in account. 206 func (p *Polynomial) GetCoeff(i int) fr.Element { 207 208 n := p.coefficients.Len() 209 rho := n / p.size 210 if p.polynomial.Form.Layout == Regular { 211 return (*p.coefficients)[(i+rho*p.shift)%n] 212 } else { 213 nn := uint64(64 - bits.TrailingZeros(uint(n))) 214 iRev := bits.Reverse64(uint64((i+rho*p.shift)%n)) >> nn 215 return (*p.coefficients)[iRev] 216 } 217 218 } 219 220 // polynomial represents a polynomial, the vector of coefficients 221 // along with the basis and the layout. 222 type polynomial struct { 223 coefficients *fr.Vector 224 Form 225 } 226 227 // Coefficients returns a slice on the underlying data structure. 228 func (p *polynomial) Coefficients() []fr.Element { 229 return (*p.coefficients) 230 } 231 232 // newPolynomial creates a new polynomial. The slice coeff NOT copied 233 // but directly assigned to the new polynomial. 234 func newPolynomial(coeffs *[]fr.Element, form Form) *polynomial { 235 return &polynomial{coefficients: (*fr.Vector)(coeffs), Form: form} 236 } 237 238 // clone returns a deep copy of the underlying data structure. 239 func (p *polynomial) clone(capacity ...int) *polynomial { 240 c := p.coefficients.Len() 241 if len(capacity) == 1 && capacity[0] > c { 242 c = capacity[0] 243 } 244 newCoeffs := make(fr.Vector, p.coefficients.Len(), c) 245 r := &polynomial{ 246 coefficients: &newCoeffs, 247 Form: p.Form, 248 } 249 copy((*r.coefficients), (*p.coefficients)) 250 return r 251 } 252 253 // evaluate evaluates p at x. 254 // The code panics if the function is not in canonical form. 255 func (p *polynomial) evaluate(x fr.Element) fr.Element { 256 257 var r fr.Element 258 if p.Basis != Canonical { 259 panic("p must be in canonical basis") 260 } 261 262 if p.Layout == Regular { 263 for i := p.coefficients.Len() - 1; i >= 0; i-- { 264 r.Mul(&r, &x).Add(&r, &(*p.coefficients)[i]) 265 } 266 } else { 267 nn := uint64(64 - bits.TrailingZeros(uint(p.coefficients.Len()))) 268 for i := p.coefficients.Len() - 1; i >= 0; i-- { 269 iRev := bits.Reverse64(uint64(i)) >> nn 270 r.Mul(&r, &x).Add(&r, &(*p.coefficients)[iRev]) 271 } 272 } 273 274 return r 275 276 } 277 278 // ToRegular changes the layout of p to Regular. 279 // Leaves p unchanged if p's layout was already Regular. 280 func (p *Polynomial) ToRegular() *Polynomial { 281 if p.Layout == Regular { 282 return p 283 } 284 fft.BitReverse((*p.coefficients)) 285 p.Layout = Regular 286 return p 287 } 288 289 // ToBitReverse changes the layout of p to BitReverse. 290 // Leaves p unchanged if p's layout was already BitReverse. 291 func (p *Polynomial) ToBitReverse() *Polynomial { 292 if p.Layout == BitReverse { 293 return p 294 } 295 fft.BitReverse((*p.coefficients)) 296 p.Layout = BitReverse 297 return p 298 } 299 300 // ToLagrange converts p to Lagrange form. 301 // Leaves p unchanged if p was already in Lagrange form. 302 func (p *Polynomial) ToLagrange(d *fft.Domain, nbTasks ...int) *Polynomial { 303 id := p.Form 304 p.grow(int(d.Cardinality)) 305 306 n := runtime.NumCPU() 307 if len(nbTasks) > 0 { 308 n = nbTasks[0] 309 } 310 311 switch id { 312 case canonicalRegular: 313 p.Layout = BitReverse 314 d.FFT((*p.coefficients), fft.DIF, fft.WithNbTasks(n)) 315 case canonicalBitReverse: 316 p.Layout = Regular 317 d.FFT((*p.coefficients), fft.DIT, fft.WithNbTasks(n)) 318 case lagrangeRegular, lagrangeBitReverse: 319 return p 320 case lagrangeCosetRegular: 321 p.Layout = Regular 322 d.FFTInverse((*p.coefficients), fft.DIF, fft.OnCoset(), fft.WithNbTasks(n)) 323 d.FFT((*p.coefficients), fft.DIT) 324 case lagrangeCosetBitReverse: 325 p.Layout = BitReverse 326 d.FFTInverse((*p.coefficients), fft.DIT, fft.OnCoset(), fft.WithNbTasks(n)) 327 d.FFT((*p.coefficients), fft.DIF) 328 default: 329 panic("unknown ID") 330 } 331 p.Basis = Lagrange 332 return p 333 } 334 335 // ToCanonical converts p to canonical form. 336 // Leaves p unchanged if p was already in Canonical form. 337 func (p *Polynomial) ToCanonical(d *fft.Domain, nbTasks ...int) *Polynomial { 338 id := p.Form 339 p.grow(int(d.Cardinality)) 340 n := runtime.NumCPU() 341 if len(nbTasks) > 0 { 342 n = nbTasks[0] 343 } 344 switch id { 345 case canonicalRegular, canonicalBitReverse: 346 return p 347 case lagrangeRegular: 348 p.Layout = BitReverse 349 d.FFTInverse((*p.coefficients), fft.DIF, fft.WithNbTasks(n)) 350 case lagrangeBitReverse: 351 p.Layout = Regular 352 d.FFTInverse((*p.coefficients), fft.DIT, fft.WithNbTasks(n)) 353 case lagrangeCosetRegular: 354 p.Layout = BitReverse 355 d.FFTInverse((*p.coefficients), fft.DIF, fft.OnCoset(), fft.WithNbTasks(n)) 356 case lagrangeCosetBitReverse: 357 p.Layout = Regular 358 d.FFTInverse((*p.coefficients), fft.DIT, fft.OnCoset(), fft.WithNbTasks(n)) 359 default: 360 panic("unknown ID") 361 } 362 p.Basis = Canonical 363 return p 364 } 365 366 func (p *polynomial) grow(newSize int) { 367 offset := newSize - p.coefficients.Len() 368 if offset > 0 { 369 (*p.coefficients) = append((*p.coefficients), make(fr.Vector, offset)...) 370 } 371 } 372 373 // ToLagrangeCoset Sets p to q, in LagrangeCoset form and returns it. 374 func (p *Polynomial) ToLagrangeCoset(d *fft.Domain) *Polynomial { 375 id := p.Form 376 p.grow(int(d.Cardinality)) 377 switch id { 378 case canonicalRegular: 379 p.Layout = BitReverse 380 d.FFT((*p.coefficients), fft.DIF, fft.OnCoset()) 381 case canonicalBitReverse: 382 p.Layout = Regular 383 d.FFT((*p.coefficients), fft.DIT, fft.OnCoset()) 384 case lagrangeRegular: 385 p.Layout = Regular 386 d.FFTInverse((*p.coefficients), fft.DIF) 387 d.FFT((*p.coefficients), fft.DIT, fft.OnCoset()) 388 case lagrangeBitReverse: 389 p.Layout = BitReverse 390 d.FFTInverse((*p.coefficients), fft.DIT) 391 d.FFT((*p.coefficients), fft.DIF, fft.OnCoset()) 392 case lagrangeCosetRegular, lagrangeCosetBitReverse: 393 return p 394 default: 395 panic("unknown ID") 396 } 397 398 p.Basis = LagrangeCoset 399 return p 400 } 401 402 // WriteTo implements io.WriterTo 403 func (p *Polynomial) WriteTo(w io.Writer) (int64, error) { 404 // encode coefficients 405 n, err := p.polynomial.coefficients.WriteTo(w) 406 if err != nil { 407 return n, err 408 } 409 410 // encode Form.Basis, Form.Layout, shift, size & blindedSize as uint32 411 var data = []uint32{ 412 uint32(p.Basis), 413 uint32(p.Layout), 414 uint32(p.shift), 415 uint32(p.size), 416 uint32(p.blindedSize), 417 } 418 for _, v := range data { 419 err = binary.Write(w, binary.BigEndian, v) 420 if err != nil { 421 return n, err 422 } 423 n += 4 424 } 425 return n, nil 426 } 427 428 // ReadFrom implements io.ReaderFrom 429 func (p *Polynomial) ReadFrom(r io.Reader) (int64, error) { 430 // decode coefficients 431 if p.polynomial == nil { 432 p.polynomial = new(polynomial) 433 } 434 if p.polynomial.coefficients == nil { 435 v := make(fr.Vector, 0) 436 p.polynomial.coefficients = &v 437 } 438 n, err := p.polynomial.coefficients.ReadFrom(r) 439 if err != nil { 440 return n, err 441 } 442 443 // decode Form.Basis, Form.Layout, shift, size & blindedSize as uint32 444 var data [5]uint32 445 var buf [4]byte 446 for i := range data { 447 read, err := io.ReadFull(r, buf[:4]) 448 n += int64(read) 449 if err != nil { 450 return n, err 451 } 452 data[i] = binary.BigEndian.Uint32(buf[:4]) 453 } 454 455 p.Basis = Basis(data[0]) 456 p.Layout = Layout(data[1]) 457 p.shift = int(data[2]) 458 p.size = int(data[3]) 459 p.blindedSize = int(data[4]) 460 461 return n, nil 462 }