github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-377/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/bls12-377/fr"
    27  	"github.com/consensys/gnark-crypto/ecc/bls12-377/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  }