github.com/consensys/gnark-crypto@v0.14.0/internal/generator/iop/template/polynomial.test.go.tmpl (about) 1 import ( 2 "testing" 3 4 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr" 5 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr/fft" 6 7 "github.com/stretchr/testify/require" 8 9 "bytes" 10 "reflect" 11 ) 12 13 func TestEvaluation(t *testing.T) { 14 15 size := 8 16 shift := 2 17 d := fft.NewDomain(uint64(size)) 18 c := randomVector(size) 19 wp := NewPolynomial(c, Form{Basis: Canonical, Layout: Regular}) 20 wps := wp.ShallowClone().Shift(shift) 21 ref := wp.Clone() 22 ref.ToLagrange(d).ToRegular() 23 24 // regular layout 25 a := wp.Evaluate(d.Generator) 26 b := wps.Evaluate(d.Generator) 27 if !a.Equal(&ref.Coefficients()[1]) { 28 t.Fatal("error evaluation") 29 } 30 if !b.Equal(&ref.Coefficients()[1+shift]) { 31 t.Fatal("error evaluation shifted") 32 } 33 34 // bit reversed layout 35 wp.ToBitReverse() 36 wps.ToBitReverse() 37 a = wp.Evaluate(d.Generator) 38 b = wps.Evaluate(d.Generator) 39 if !a.Equal(&ref.Coefficients()[1]) { 40 t.Fatal("error evaluation") 41 } 42 if !b.Equal(&ref.Coefficients()[1+shift]) { 43 t.Fatal("error evaluation shifted") 44 } 45 46 } 47 48 func randomVector(size int) *[]fr.Element { 49 50 r := make([]fr.Element, size) 51 for i := 0; i < size; i++ { 52 r[i].SetRandom() 53 } 54 return &r 55 } 56 57 func TestGetCoeff(t *testing.T) { 58 59 size := 8 60 v := make([]fr.Element, size) 61 for i := 0; i < size; i++ { 62 v[i].SetUint64(uint64(i)) 63 } 64 wp := NewPolynomial(&v, Form{Layout: Regular, Basis: Canonical}) 65 wsp := wp.ShallowClone().Shift(1) 66 67 var aa, bb fr.Element 68 69 // regular layout 70 for i := 0; i < size; i++ { 71 72 a := wp.GetCoeff(i) 73 b := wsp.GetCoeff(i) 74 aa.SetUint64(uint64(i)) 75 bb.SetUint64(uint64((i + 1) % size)) 76 if !a.Equal(&aa) { 77 t.Fatal("error GetCoeff") 78 } 79 if !b.Equal(&bb) { 80 t.Fatal("error GetCoeff") 81 } 82 } 83 84 // bit reverse + bitReverse and shifted 85 wp.ToBitReverse() 86 wsp.ToBitReverse() 87 for i := 0; i < size; i++ { 88 89 a := wp.GetCoeff(i) 90 b := wsp.GetCoeff(i) 91 aa.SetUint64(uint64(i)) 92 bb.SetUint64(uint64((i + 1) % size)) 93 if !a.Equal(&aa) { 94 t.Fatal("error GetCoeff") 95 } 96 if !b.Equal(&bb) { 97 t.Fatal("error GetCoeff") 98 } 99 } 100 101 } 102 103 104 func TestRoundTrip(t *testing.T) { 105 assert := require.New(t) 106 var buf bytes.Buffer 107 108 size := 8 109 d := fft.NewDomain(uint64(8)) 110 blindingOrder := 2 111 112 p := NewPolynomial(randomVector(size), Form{Basis:Lagrange, Layout: Regular}).ToCanonical(d).ToRegular() 113 p.Blind(blindingOrder) 114 115 // serialize 116 written, err := p.WriteTo(&buf) 117 assert.NoError(err) 118 119 // deserialize 120 var reconstructed Polynomial 121 read, err := reconstructed.ReadFrom(&buf) 122 assert.NoError(err) 123 124 assert.Equal(read, written, "number of bytes written != number of bytes read") 125 126 // compare 127 assert.Equal(p.Basis, reconstructed.Basis) 128 assert.Equal(p.Layout, reconstructed.Layout) 129 assert.Equal(p.shift, reconstructed.shift) 130 assert.Equal(p.size, reconstructed.size) 131 assert.Equal(p.blindedSize, reconstructed.blindedSize) 132 c1, c2 := p.Coefficients(), reconstructed.Coefficients() 133 assert.True(reflect.DeepEqual(c1, c2)) 134 } 135 136 func TestBlinding(t *testing.T) { 137 138 size := 8 139 d := fft.NewDomain(uint64(8)) 140 blindingOrder := 2 141 142 // generate a random polynomial in Lagrange form for the moment 143 // to check that an error is raised when the polynomial is not 144 // in canonical form. 145 wp := NewPolynomial(randomVector(size), Form{Basis:Lagrange, Layout: Regular}) 146 147 // checks the blinding is correct: the evaluation of the blinded polynomial 148 // should be the same as the original on d's domain 149 wp.Basis = Canonical 150 wt := wp.Clone() 151 wt.Blind(blindingOrder) 152 if wt.coefficients.Len() != blindingOrder+size+1 { 153 t.Fatal("size of blinded polynomial is incorrect") 154 } 155 if wt.blindedSize != size+blindingOrder+1 { 156 t.Fatal("Size field of blinded polynomial is incorrect") 157 } 158 if wt.size != size { 159 t.Fatal("the size should not have been modified") 160 } 161 x := make([]fr.Element, size) 162 x[0].SetOne() 163 for i := 1; i < size; i++ { 164 x[i].Mul(&x[i-1], &d.Generator) 165 } 166 var a, b fr.Element 167 for i := 0; i < size; i++ { 168 a = wt.Evaluate(x[i]) 169 b = wp.Evaluate(x[i]) 170 if a != b { 171 t.Fatal("polynomial and its blinded version should be equal on V(X^{n}-1)") 172 } 173 } 174 175 } 176 177 // list of functions to turn a polynomial in Lagrange-regular form 178 // to all different forms in ordered using this encoding: 179 // int(p.Basis)*4 + int(p.Layout)*2 + int(p.Status) 180 // p is in Lagrange/Regular here. This function is for testing purpose 181 // only. 182 type TransfoTest func(p polynomial, d *fft.Domain) polynomial 183 184 // CANONICAL REGULAR 185 func fromLagrange0(p *Polynomial, d *fft.Domain) *Polynomial { 186 r := p.Clone() 187 r.Basis = Canonical 188 r.Layout = Regular 189 d.FFTInverse(r.Coefficients(), fft.DIF) 190 fft.BitReverse(r.Coefficients()) 191 return r 192 } 193 194 // CANONICAL BITREVERSE 195 func fromLagrange1(p *Polynomial, d *fft.Domain) *Polynomial { 196 r := p.Clone() 197 r.Basis = Canonical 198 r.Layout = BitReverse 199 d.FFTInverse(r.Coefficients(), fft.DIF) 200 return r 201 } 202 203 // LAGRANGE REGULAR 204 func fromLagrange2(p *Polynomial, d *fft.Domain) *Polynomial { 205 r := p.Clone() 206 r.Basis = Lagrange 207 r.Layout = Regular 208 return r 209 } 210 211 // LAGRANGE BITREVERSE 212 func fromLagrange3(p *Polynomial, d *fft.Domain) *Polynomial { 213 r := p.Clone() 214 r.Basis = Lagrange 215 r.Layout = BitReverse 216 fft.BitReverse(r.Coefficients()) 217 return r 218 } 219 220 // LAGRANGE_COSET REGULAR 221 func fromLagrange4(p *Polynomial, d *fft.Domain) *Polynomial { 222 r := p.Clone() 223 r.Basis = LagrangeCoset 224 r.Layout = Regular 225 d.FFTInverse(r.Coefficients(), fft.DIF) 226 d.FFT(r.Coefficients(), fft.DIT, fft.OnCoset()) 227 return r 228 } 229 230 // LAGRANGE_COSET BITREVERSE 231 func fromLagrange5(p *Polynomial, d *fft.Domain) *Polynomial { 232 r := p.Clone() 233 r.Basis = LagrangeCoset 234 r.Layout = BitReverse 235 d.FFTInverse(r.Coefficients(), fft.DIF) 236 d.FFT(r.Coefficients(), fft.DIT, fft.OnCoset()) 237 fft.BitReverse(r.Coefficients()) 238 return r 239 } 240 241 func fromLagrange(p *Polynomial, d *fft.Domain) *Polynomial { 242 id := p.Form 243 switch id { 244 case canonicalRegular: 245 return fromLagrange0(p, d) 246 case canonicalBitReverse: 247 return fromLagrange1(p, d) 248 case lagrangeRegular: 249 return fromLagrange2(p, d) 250 case lagrangeBitReverse: 251 return fromLagrange3(p, d) 252 case lagrangeCosetRegular: 253 return fromLagrange4(p, d) 254 case lagrangeCosetBitReverse: 255 return fromLagrange5(p, d) 256 default: 257 panic("unknown id") 258 } 259 } 260 261 func cmpCoefficents(p, q *fr.Vector) bool { 262 if p.Len() != q.Len() { 263 return false 264 } 265 for i := 0; i < p.Len(); i++ { 266 if !((*p)[i].Equal(&(*q)[i])){ 267 return false 268 } 269 } 270 return true 271 } 272 273 func TestPutInLagrangeForm(t *testing.T) { 274 275 size := 64 276 domain := fft.NewDomain(uint64(size)) 277 278 // reference vector in Lagrange-regular form 279 c := randomVector(size) 280 p := NewPolynomial(c, Form{Basis: Canonical, Layout: Regular}) 281 282 // CANONICAL REGULAR 283 { 284 _p := fromLagrange(p, domain) 285 q := _p.Clone() 286 q.ToLagrange(domain) 287 if q.Basis != Lagrange { 288 t.Fatal("expected basis is Lagrange") 289 } 290 if q.Layout != BitReverse { 291 t.Fatal("expected layout is BitReverse") 292 } 293 fft.BitReverse(q.Coefficients()) 294 if !cmpCoefficents(q.coefficients, p.coefficients) { 295 t.Fatal("wrong coefficients") 296 } 297 } 298 299 // CANONICAL BITREVERSE 300 { 301 _p := fromLagrange1(p, domain) 302 q := _p.Clone() 303 q.ToLagrange(domain) 304 if q.Basis != Lagrange { 305 t.Fatal("expected basis is Lagrange") 306 } 307 if q.Layout != Regular { 308 t.Fatal("expected layout is Regular") 309 } 310 if !cmpCoefficents(q.coefficients, p.coefficients) { 311 t.Fatal("wrong coefficients") 312 } 313 } 314 315 // LAGRANGE REGULAR 316 { 317 _p := fromLagrange2(p, domain) 318 q := _p.Clone() 319 q.ToLagrange(domain) 320 321 if q.Basis != Lagrange { 322 t.Fatal("expected basis is Lagrange") 323 } 324 if q.Layout != Regular { 325 t.Fatal("expected layout is Regular") 326 } 327 if !cmpCoefficents(q.coefficients, p.coefficients) { 328 t.Fatal("wrong coefficients") 329 } 330 } 331 332 // LAGRANGE BITREVERSE 333 { 334 _p := fromLagrange3(p, domain) 335 q := _p.Clone() 336 q.ToLagrange(domain) 337 if q.Basis != Lagrange { 338 t.Fatal("expected basis is Lagrange") 339 } 340 if q.Layout != BitReverse { 341 t.Fatal("expected layout is BitReverse") 342 } 343 fft.BitReverse(q.Coefficients()) 344 if !cmpCoefficents(q.coefficients, p.coefficients) { 345 t.Fatal("wrong coefficients") 346 } 347 } 348 349 // LAGRANGE_COSET REGULAR 350 { 351 _p := fromLagrange4(p, domain) 352 q := _p.Clone() 353 q.ToLagrange(domain) 354 if q.Basis != Lagrange { 355 t.Fatal("expected basis is Lagrange") 356 } 357 if q.Layout != Regular { 358 t.Fatal("expected layout is Regular") 359 } 360 if !cmpCoefficents(q.coefficients, p.coefficients) { 361 t.Fatal("wrong coefficients") 362 } 363 } 364 365 // LAGRANGE_COSET BITREVERSE 366 { 367 _p := fromLagrange5(p, domain) 368 q := _p.Clone() 369 q.ToLagrange(domain) 370 if q.Basis != Lagrange { 371 t.Fatal("expected basis is Lagrange") 372 } 373 if q.Layout != BitReverse { 374 t.Fatal("expected layout is BitReverse") 375 } 376 fft.BitReverse(q.Coefficients()) 377 if !cmpCoefficents(q.coefficients, p.coefficients) { 378 t.Fatal("wrong coefficients") 379 } 380 } 381 382 } 383 384 // CANONICAL REGULAR 385 func fromCanonical0(p *Polynomial, d *fft.Domain) *Polynomial { 386 _p := p.Clone() 387 _p.Basis = Canonical 388 _p.Layout = Regular 389 return _p 390 } 391 392 // CANONICAL BITREVERSE 393 func fromCanonical1(p *Polynomial, d *fft.Domain) *Polynomial { 394 _p := p.Clone() 395 _p.Basis = Canonical 396 _p.Layout = BitReverse 397 return _p 398 } 399 400 // LAGRANGE REGULAR 401 func fromCanonical2(p *Polynomial, d *fft.Domain) *Polynomial { 402 _p := p.Clone() 403 _p.Basis = Lagrange 404 _p.Layout = Regular 405 d.FFT(_p.Coefficients(), fft.DIF) 406 fft.BitReverse(_p.Coefficients()) 407 return _p 408 } 409 410 // LAGRANGE BITREVERSE 411 func fromCanonical3(p *Polynomial, d *fft.Domain) *Polynomial { 412 _p := p.Clone() 413 _p.Basis = Lagrange 414 _p.Layout = BitReverse 415 d.FFT(_p.Coefficients(), fft.DIF) 416 return _p 417 } 418 419 // LAGRANGE_COSET REGULAR 420 func fromCanonical4(p *Polynomial, d *fft.Domain) *Polynomial { 421 _p := p.Clone() 422 _p.Basis = LagrangeCoset 423 _p.Layout = Regular 424 d.FFT(_p.Coefficients(), fft.DIF, fft.OnCoset()) 425 fft.BitReverse(_p.Coefficients()) 426 return _p 427 } 428 429 // LAGRANGE_COSET BITREVERSE 430 func fromCanonical5(p *Polynomial, d *fft.Domain) *Polynomial { 431 _p := p.Clone() 432 _p.Basis = LagrangeCoset 433 _p.Layout = BitReverse 434 d.FFT(_p.Coefficients(), fft.DIF, fft.OnCoset()) 435 return _p 436 } 437 438 func TestPutInCanonicalForm(t *testing.T) { 439 440 size := 64 441 domain := fft.NewDomain(uint64(size)) 442 443 // reference vector in canonical-regular form 444 c := randomVector(size) 445 p := NewPolynomial(c, Form{Basis: Canonical, Layout: Regular}) 446 447 // CANONICAL REGULAR 448 { 449 _p := fromCanonical0(p, domain) 450 q := _p.Clone() 451 q.ToCanonical(domain) 452 if q.Basis != Canonical { 453 t.Fatal("expected basis is canonical") 454 } 455 if q.Layout != Regular { 456 t.Fatal("expected layout is regular") 457 } 458 if !cmpCoefficents(q.coefficients, p.coefficients) { 459 t.Fatal("wrong coefficients") 460 } 461 } 462 463 // CANONICAL BITREVERSE 464 { 465 _p := fromCanonical1(p, domain) 466 q := _p.Clone() 467 q.ToCanonical(domain) 468 if q.Basis != Canonical { 469 t.Fatal("expected basis is canonical") 470 } 471 if q.Layout != BitReverse { 472 t.Fatal("expected layout is bitReverse") 473 } 474 if !cmpCoefficents(q.coefficients, p.coefficients) { 475 t.Fatal("wrong coefficients") 476 } 477 } 478 479 // LAGRANGE REGULAR 480 { 481 _p := fromCanonical2(p, domain) 482 q := _p.Clone() 483 q.ToCanonical(domain) 484 if q.Basis != Canonical { 485 t.Fatal("expected basis is canonical") 486 } 487 if q.Layout != BitReverse { 488 t.Fatal("expected layout is bitReverse") 489 } 490 fft.BitReverse(q.Coefficients()) 491 if !cmpCoefficents(p.coefficients, q.coefficients) { 492 t.Fatal("wrong coefficients") 493 } 494 } 495 496 // LAGRANGE BITREVERSE 497 { 498 _p := fromCanonical3(p, domain) 499 q := _p.Clone() 500 q.ToCanonical(domain) 501 if q.Basis != Canonical { 502 t.Fatal("expected basis is canonical") 503 } 504 if q.Layout != Regular { 505 t.Fatal("expected layout is regular") 506 } 507 if !cmpCoefficents(q.coefficients, p.coefficients) { 508 t.Fatal("wrong coefficients") 509 } 510 } 511 512 // LAGRANGE_COSET REGULAR 513 { 514 _p := fromCanonical4(p, domain) 515 q := _p.Clone() 516 q.ToCanonical(domain) 517 if q.Basis != Canonical { 518 t.Fatal("expected basis is canonical") 519 } 520 if q.Layout != BitReverse { 521 t.Fatal("expected layout is BitReverse") 522 } 523 fft.BitReverse(q.Coefficients()) 524 if !cmpCoefficents(q.coefficients, p.coefficients) { 525 t.Fatal("wrong coefficients") 526 } 527 } 528 529 // LAGRANGE_COSET BITREVERSE 530 { 531 _p := fromCanonical5(p, domain) 532 q := _p.Clone() 533 q.ToCanonical(domain) 534 if q.Basis != Canonical { 535 t.Fatal("expected basis is canonical") 536 } 537 if q.Layout != Regular { 538 t.Fatal("expected layout is regular") 539 } 540 if !cmpCoefficents(q.coefficients, p.coefficients) { 541 t.Fatal("wrong coefficients") 542 } 543 } 544 545 } 546 547 // CANONICAL REGULAR 548 func fromLagrangeCoset0(p *Polynomial, d *fft.Domain) *Polynomial { 549 _p := p.Clone() 550 _p.Basis = Canonical 551 _p.Layout = Regular 552 d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) 553 fft.BitReverse(_p.Coefficients()) 554 return _p 555 } 556 557 // CANONICAL BITREVERSE 558 func fromLagrangeCoset1(p *Polynomial, d *fft.Domain) *Polynomial { 559 _p := p.Clone() 560 _p.Basis = Canonical 561 _p.Layout = BitReverse 562 d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) 563 return _p 564 } 565 566 // LAGRANGE REGULAR 567 func fromLagrangeCoset2(p *Polynomial, d *fft.Domain) *Polynomial { 568 _p := p.Clone() 569 _p.Basis = Lagrange 570 _p.Layout = Regular 571 d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) 572 d.FFT(_p.Coefficients(), fft.DIT) 573 return _p 574 } 575 576 // LAGRANGE BITREVERSE 577 func fromLagrangeCoset3(p *Polynomial, d *fft.Domain) *Polynomial { 578 _p := p.Clone() 579 _p.Basis = Lagrange 580 _p.Layout = BitReverse 581 d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) 582 d.FFT(_p.Coefficients(), fft.DIT) 583 fft.BitReverse(_p.Coefficients()) 584 return _p 585 } 586 587 // LAGRANGE_COSET REGULAR 588 func fromLagrangeCoset4(p *Polynomial, d *fft.Domain) *Polynomial { 589 _p := p.Clone() 590 _p.Basis = LagrangeCoset 591 _p.Layout = Regular 592 return _p 593 } 594 595 // LAGRANGE_COSET BITREVERSE 596 func fromLagrangeCoset5(p *Polynomial, d *fft.Domain) *Polynomial { 597 _p := p.Clone() 598 _p.Basis = LagrangeCoset 599 _p.Layout = BitReverse 600 fft.BitReverse(p.Coefficients()) 601 return _p 602 } 603 604 func TestPutInLagrangeCosetForm(t *testing.T) { 605 606 size := 64 607 domain := fft.NewDomain(uint64(size)) 608 609 // reference vector in canonical-regular form 610 c := randomVector(size) 611 p := NewPolynomial(c, Form{Basis: LagrangeCoset, Layout: Regular}) 612 613 // CANONICAL REGULAR 614 { 615 _p := fromLagrangeCoset0(p, domain) 616 q := _p.Clone() 617 q.ToLagrangeCoset(domain) 618 if q.Basis != LagrangeCoset { 619 t.Fatal("expected basis is lagrange coset") 620 } 621 if q.Layout != BitReverse { 622 t.Fatal("expected layout is bit reverse") 623 } 624 fft.BitReverse(q.Coefficients()) 625 if !cmpCoefficents(q.coefficients, p.coefficients) { 626 t.Fatal("wrong coefficients") 627 } 628 } 629 630 // CANONICAL BITREVERSE 631 { 632 _p := fromLagrangeCoset1(p, domain) 633 q := _p.Clone() 634 q.ToLagrangeCoset(domain) 635 if q.Basis != LagrangeCoset { 636 t.Fatal("expected basis is lagrange coset") 637 } 638 if q.Layout != Regular { 639 t.Fatal("expected layout is regular") 640 } 641 if !cmpCoefficents(q.coefficients, p.coefficients) { 642 t.Fatal("wrong coefficients") 643 } 644 } 645 646 // LAGRANGE REGULAR 647 { 648 _p := fromLagrangeCoset2(p, domain) 649 q := _p.Clone() 650 q.ToLagrangeCoset(domain) 651 if q.Basis != LagrangeCoset { 652 t.Fatal("expected basis is lagrange coset") 653 } 654 if q.Layout != Regular { 655 t.Fatal("expected layout is regular") 656 } 657 if !cmpCoefficents(q.coefficients, p.coefficients) { 658 t.Fatal("wrong coefficients") 659 } 660 } 661 662 // LAGRANGE BITREVERSE 663 { 664 _p := fromLagrangeCoset3(p, domain) 665 q := _p.Clone() 666 q.ToLagrangeCoset(domain) 667 if q.Basis != LagrangeCoset { 668 t.Fatal("expected basis is lagrange coset") 669 } 670 if q.Layout != BitReverse { 671 t.Fatal("expected layout is bit reverse") 672 } 673 fft.BitReverse(q.Coefficients()) 674 if !cmpCoefficents(q.coefficients, p.coefficients) { 675 t.Fatal("wrong coefficients") 676 } 677 } 678 679 // LAGRANGE_COSET REGULAR 680 { 681 _p := fromLagrangeCoset4(p, domain) 682 q := _p.Clone() 683 q.ToLagrangeCoset(domain) 684 if q.Basis != LagrangeCoset { 685 t.Fatal("expected basis is lagrange coset") 686 } 687 if q.Layout != Regular { 688 t.Fatal("expected layout is regular") 689 } 690 if !cmpCoefficents(q.coefficients, p.coefficients) { 691 t.Fatal("wrong coefficients") 692 } 693 } 694 695 // LAGRANGE_COSET BITREVERSE 696 { 697 _p := fromLagrangeCoset5(p, domain) 698 q := _p.Clone() 699 q.ToLagrangeCoset(domain) 700 if q.Basis != LagrangeCoset { 701 t.Fatal("expected basis is lagrange coset") 702 } 703 if q.Layout != BitReverse { 704 t.Fatal("expected layout is bit reverse") 705 } 706 fft.BitReverse(q.Coefficients()) 707 if !cmpCoefficents(q.coefficients, p.coefficients) { 708 t.Fatal("wrong coefficients") 709 } 710 } 711 712 }