github.com/consensys/gnark-crypto@v0.14.0/internal/generator/kzg/template/kzg.test.go.tmpl (about) 1 import ( 2 "crypto/sha256" 3 "github.com/stretchr/testify/assert" 4 "github.com/stretchr/testify/require" 5 "math/big" 6 "testing" 7 "bytes" 8 9 "github.com/consensys/gnark-crypto/ecc" 10 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}" 11 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr" 12 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr/fft" 13 14 "github.com/consensys/gnark-crypto/utils/testutils" 15 ) 16 17 // Test SRS re-used across tests of the KZG scheme 18 var testSrs *SRS 19 var bAlpha *big.Int 20 21 func init() { 22 const srsSize = 230 23 bAlpha = new(big.Int).SetInt64(42) // randomise ? 24 testSrs, _ = NewSRS(ecc.NextPowerOfTwo(srsSize), bAlpha) 25 } 26 27 28 func TestToLagrangeG1(t *testing.T) { 29 assert := require.New(t) 30 31 const size = 32 32 33 // convert the test SRS to Lagrange form 34 lagrange, err := ToLagrangeG1(testSrs.Pk.G1[:size]) 35 assert.NoError(err) 36 37 // generate the Lagrange SRS manually and compare 38 w, err := fr.Generator(uint64(size)) 39 assert.NoError(err) 40 41 var li, n, d, one, acc, alpha fr.Element 42 alpha.SetBigInt(bAlpha) 43 li.SetUint64(uint64(size)).Inverse(&li) 44 one.SetOne() 45 n.Exp(alpha, big.NewInt(int64(size))).Sub(&n, &one) 46 d.Sub(&alpha, &one) 47 li.Mul(&li, &n).Div(&li, &d) 48 expectedSrsLagrange := make([]{{ .CurvePackage }}.G1Affine, size) 49 _, _, g1Gen, _ := {{ .CurvePackage }}.Generators() 50 var s big.Int 51 acc.SetOne() 52 for i := 0; i < size; i++ { 53 li.BigInt(&s) 54 expectedSrsLagrange[i].ScalarMultiplication(&g1Gen, &s) 55 56 li.Mul(&li, &w).Mul(&li, &d) 57 acc.Mul(&acc, &w) 58 d.Sub(&alpha, &acc) 59 li.Div(&li, &d) 60 } 61 62 for i := 0; i < size; i++ { 63 assert.True(expectedSrsLagrange[i].Equal(&lagrange[i]), "error lagrange conversion") 64 } 65 } 66 67 func TestCommitLagrange(t *testing.T) { 68 69 assert := require.New(t) 70 71 // sample a sparse polynomial (here in Lagrange form) 72 size := 64 73 pol := make([]fr.Element, size) 74 pol[0].SetRandom() 75 for i := 0; i < size; i = i+8 { 76 pol[i].SetRandom() 77 } 78 79 // commitment using Lagrange SRS 80 lagrange, err := ToLagrangeG1(testSrs.Pk.G1[:size]) 81 assert.NoError(err) 82 var pkLagrange ProvingKey 83 pkLagrange.G1 = lagrange 84 85 digestLagrange, err := Commit(pol, pkLagrange) 86 assert.NoError(err) 87 88 // commitment using canonical SRS 89 d := fft.NewDomain(uint64(size)) 90 d.FFTInverse(pol, fft.DIF) 91 fft.BitReverse(pol) 92 digestCanonical, err := Commit(pol, testSrs.Pk) 93 assert.NoError(err) 94 95 // compare the results 96 assert.True(digestCanonical.Equal(&digestLagrange), "error CommitLagrange") 97 } 98 99 func TestDividePolyByXminusA(t *testing.T) { 100 101 const pSize = 230 102 103 // build random polynomial 104 pol := make([]fr.Element, pSize) 105 pol[0].SetRandom() 106 for i := 1; i < pSize; i++ { 107 pol[i] = pol[i-1] 108 } 109 110 // evaluate the polynomial at a random point 111 var point fr.Element 112 point.SetRandom() 113 evaluation := eval(pol, point) 114 115 // probabilistic test (using Schwartz Zippel lemma, evaluation at one point is enough) 116 var randPoint, xminusa fr.Element 117 randPoint.SetRandom() 118 polRandpoint := eval(pol, randPoint) 119 polRandpoint.Sub(&polRandpoint, &evaluation) // f(rand)-f(point) 120 121 // compute f-f(a)/x-a 122 // h re-uses the memory of pol 123 h := dividePolyByXminusA(pol, evaluation, point) 124 125 if len(h) != 229 { 126 t.Fatal("inconsistent size of quotient") 127 } 128 129 hRandPoint := eval(h, randPoint) 130 xminusa.Sub(&randPoint, &point) // rand-point 131 132 // f(rand)-f(point) ==? h(rand)*(rand-point) 133 hRandPoint.Mul(&hRandPoint, &xminusa) 134 135 if !hRandPoint.Equal(&polRandpoint) { 136 t.Fatal("Error f-f(a)/x-a") 137 } 138 } 139 140 func TestSerializationSRS(t *testing.T) { 141 // create a SRS 142 srs, err := NewSRS(64, new(big.Int).SetInt64(42)) 143 assert.NoError(t, err) 144 t.Run("proving key round-trip", testutils.SerializationRoundTrip(&srs.Pk)) 145 t.Run("proving key raw round-trip", testutils.SerializationRoundTripRaw(&srs.Pk)) 146 t.Run("verifying key round-trip", testutils.SerializationRoundTrip(&srs.Vk)) 147 t.Run("whole SRS round-trip", testutils.SerializationRoundTrip(srs)) 148 t.Run("unsafe whole SRS round-trip", testutils.UnsafeBinaryMarshalerRoundTrip(srs)) 149 } 150 151 func TestCommit(t *testing.T) { 152 153 // create a polynomial 154 f := make([]fr.Element, 60) 155 for i := 0; i < 60; i++ { 156 f[i].SetRandom() 157 } 158 159 // commit using the method from KZG 160 _kzgCommit, err := Commit(f, testSrs.Pk) 161 if err != nil { 162 t.Fatal(err) 163 } 164 var kzgCommit {{ .CurvePackage }}.G1Affine 165 kzgCommit.Unmarshal(_kzgCommit.Marshal()) 166 167 // check commitment using manual commit 168 var x fr.Element 169 x.SetString("42") 170 fx := eval(f, x) 171 var fxbi big.Int 172 fx.BigInt(&fxbi) 173 var manualCommit {{ .CurvePackage }}.G1Affine 174 manualCommit.Set(&testSrs.Vk.G1) 175 manualCommit.ScalarMultiplication(&manualCommit, &fxbi) 176 177 // compare both results 178 if !kzgCommit.Equal(&manualCommit) { 179 t.Fatal("error KZG commitment") 180 } 181 182 } 183 184 func TestVerifySinglePoint(t *testing.T) { 185 186 // create a polynomial 187 f := randomPolynomial(60) 188 189 // commit the polynomial 190 digest, err := Commit(f, testSrs.Pk) 191 if err != nil { 192 t.Fatal(err) 193 } 194 195 // compute opening proof at a random point 196 var point fr.Element 197 point.SetString("4321") 198 proof, err := Open(f, point, testSrs.Pk) 199 if err != nil { 200 t.Fatal(err) 201 } 202 203 // verify the claimed valued 204 expected := eval(f, point) 205 if !proof.ClaimedValue.Equal(&expected) { 206 t.Fatal("inconsistent claimed value") 207 } 208 209 // verify correct proof 210 err = Verify(&digest, &proof, point, testSrs.Vk) 211 if err != nil { 212 t.Fatal(err) 213 } 214 215 { 216 // verify wrong proof 217 proof.ClaimedValue.Double(&proof.ClaimedValue) 218 err = Verify(&digest, &proof, point, testSrs.Vk) 219 if err == nil { 220 t.Fatal("verifying wrong proof should have failed") 221 } 222 } 223 { 224 // verify wrong proof with quotient set to zero 225 // see https://cryptosubtlety.medium.com/00-8d4adcf4d255 226 proof.H.X.SetZero() 227 proof.H.Y.SetZero() 228 err = Verify(&digest, &proof, point, testSrs.Vk) 229 if err == nil { 230 t.Fatal("verifying wrong proof should have failed") 231 } 232 } 233 } 234 235 func TestVerifySinglePointQuickSRS(t *testing.T) { 236 237 size := 64 238 srs, err := NewSRS(64, big.NewInt(-1)) 239 if err != nil { 240 t.Fatal(err) 241 } 242 243 // random polynomial 244 p := make([]fr.Element, size) 245 for i := 0; i < size; i++ { 246 p[i].SetRandom() 247 } 248 249 // random value 250 var x fr.Element 251 x.SetRandom() 252 253 // verify valid proof 254 d, err := Commit(p, srs.Pk) 255 if err != nil { 256 t.Fatal(err) 257 } 258 proof, err := Open(p, x, srs.Pk) 259 if err != nil { 260 t.Fatal(err) 261 } 262 err = Verify(&d, &proof, x, srs.Vk) 263 if err != nil { 264 t.Fatal(err) 265 } 266 267 // verify wrong proof 268 proof.ClaimedValue.SetRandom() 269 err = Verify(&d, &proof, x, srs.Vk) 270 if err == nil { 271 t.Fatal(err) 272 } 273 274 } 275 276 func TestBatchVerifySinglePoint(t *testing.T) { 277 278 size := 40 279 280 // create polynomials 281 f := make([][]fr.Element, 10) 282 for i := range f { 283 f[i] = randomPolynomial(size) 284 } 285 286 // commit the polynomials 287 digests := make([]Digest, len(f)) 288 for i := range f { 289 digests[i], _ = Commit(f[i], testSrs.Pk) 290 291 } 292 293 // pick a hash function 294 hf := sha256.New() 295 296 // compute opening proof at a random point 297 var point fr.Element 298 point.SetString("4321") 299 proof, err := BatchOpenSinglePoint(f, digests, point, hf, testSrs.Pk) 300 if err != nil { 301 t.Fatal(err) 302 } 303 304 var salt fr.Element 305 salt.SetRandom() 306 proofExtendedTranscript, err := BatchOpenSinglePoint(f, digests, point, hf, testSrs.Pk, salt.Marshal()) 307 if err != nil { 308 t.Fatal(err) 309 } 310 311 // verify the claimed values 312 for i := range f { 313 expectedClaim := eval(f[i], point) 314 if !expectedClaim.Equal(&proof.ClaimedValues[i]) { 315 t.Fatal("inconsistent claimed values") 316 } 317 } 318 319 // verify correct proof 320 err = BatchVerifySinglePoint(digests, &proof, point, hf, testSrs.Vk) 321 if err != nil { 322 t.Fatal(err) 323 } 324 325 // verify correct proof with extended transcript 326 err = BatchVerifySinglePoint(digests, &proofExtendedTranscript, point, hf, testSrs.Vk, salt.Marshal()) 327 if err != nil { 328 t.Fatal(err) 329 } 330 331 { 332 // verify wrong proof 333 proof.ClaimedValues[0].Double(&proof.ClaimedValues[0]) 334 err = BatchVerifySinglePoint(digests, &proof, point, hf, testSrs.Vk) 335 if err == nil { 336 t.Fatal("verifying wrong proof should have failed") 337 } 338 } 339 { 340 // verify wrong proof with quotient set to zero 341 // see https://cryptosubtlety.medium.com/00-8d4adcf4d255 342 proof.H.X.SetZero() 343 proof.H.Y.SetZero() 344 err = BatchVerifySinglePoint(digests, &proof, point, hf, testSrs.Vk) 345 if err == nil { 346 t.Fatal("verifying wrong proof should have failed") 347 } 348 } 349 } 350 351 func TestBatchVerifyMultiPoints(t *testing.T) { 352 353 // create polynomials 354 f := make([][]fr.Element, 10) 355 for i := 0; i < 10; i++ { 356 f[i] = randomPolynomial(40) 357 } 358 359 // commit the polynomials 360 digests := make([]Digest, 10) 361 for i := 0; i < 10; i++ { 362 digests[i], _ = Commit(f[i], testSrs.Pk) 363 } 364 365 // pick a hash function 366 hf := sha256.New() 367 368 // compute 2 batch opening proofs at 2 random points 369 points := make([]fr.Element, 2) 370 batchProofs := make([]BatchOpeningProof, 2) 371 points[0].SetRandom() 372 batchProofs[0], _ = BatchOpenSinglePoint(f[:5], digests[:5], points[0], hf, testSrs.Pk) 373 points[1].SetRandom() 374 batchProofs[1], _ = BatchOpenSinglePoint(f[5:], digests[5:], points[1], hf, testSrs.Pk) 375 376 // fold the 2 batch opening proofs 377 proofs := make([]OpeningProof, 2) 378 foldedDigests := make([]Digest, 2) 379 proofs[0], foldedDigests[0], _ = FoldProof(digests[:5], &batchProofs[0], points[0], hf) 380 proofs[1], foldedDigests[1], _ = FoldProof(digests[5:], &batchProofs[1], points[1], hf) 381 382 // check that the individual batch proofs are correct 383 err := Verify(&foldedDigests[0], &proofs[0], points[0], testSrs.Vk) 384 if err != nil { 385 t.Fatal(err) 386 } 387 err = Verify(&foldedDigests[1], &proofs[1], points[1], testSrs.Vk) 388 if err != nil { 389 t.Fatal(err) 390 } 391 392 // batch verify correct folded proofs 393 err = BatchVerifyMultiPoints(foldedDigests, proofs, points, testSrs.Vk) 394 if err != nil { 395 t.Fatal(err) 396 } 397 398 { 399 // batch verify tampered folded proofs 400 proofs[0].ClaimedValue.Double(&proofs[0].ClaimedValue) 401 402 err = BatchVerifyMultiPoints(foldedDigests, proofs, points, testSrs.Vk) 403 if err == nil { 404 t.Fatal(err) 405 } 406 } 407 { 408 // batch verify tampered folded proofs with quotients set to infinity 409 // see https://cryptosubtlety.medium.com/00-8d4adcf4d255 410 proofs[0].H.X.SetZero() 411 proofs[0].H.Y.SetZero() 412 proofs[1].H.X.SetZero() 413 proofs[1].H.Y.SetZero() 414 err = BatchVerifyMultiPoints(foldedDigests, proofs, points, testSrs.Vk) 415 if err == nil { 416 t.Fatal(err) 417 } 418 } 419 } 420 421 func TestUnsafeToBytesTruncating(t *testing.T) { 422 assert := require.New(t) 423 srs, err := NewSRS(ecc.NextPowerOfTwo(1 << 10), big.NewInt(-1)) 424 assert.NoError(err) 425 426 // marshal the SRS, but explicitly with less points. 427 var buf bytes.Buffer 428 err = srs.WriteDump(&buf, 1 << 9) 429 assert.NoError(err) 430 431 r := bytes.NewReader(buf.Bytes()) 432 433 // unmarshal the SRS 434 var newSRS SRS 435 err = newSRS.ReadDump(r) 436 assert.NoError(err) 437 438 // check that the SRS proving key has only 1 << 9 points 439 assert.Equal(1<<9, len(newSRS.Pk.G1)) 440 441 // ensure they are equal to the original SRS 442 assert.Equal(srs.Pk.G1[:1<<9], newSRS.Pk.G1) 443 444 // read even less points. 445 var newSRSPartial SRS 446 r = bytes.NewReader(buf.Bytes()) 447 err = newSRSPartial.ReadDump(r, 1 << 8) 448 assert.NoError(err) 449 450 // check that the SRS proving key has only 1 << 8 points 451 assert.Equal(1<<8, len(newSRSPartial.Pk.G1)) 452 453 // ensure they are equal to the original SRS 454 assert.Equal(srs.Pk.G1[:1<<8], newSRSPartial.Pk.G1) 455 } 456 457 const benchSize = 1 << 16 458 459 func BenchmarkSRSGen(b *testing.B) { 460 461 b.Run("real SRS", func(b *testing.B) { 462 b.ResetTimer() 463 for i := 0; i < b.N; i++ { 464 NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) 465 } 466 }) 467 b.Run("quick SRS", func(b *testing.B) { 468 b.ResetTimer() 469 for i := 0; i < b.N; i++ { 470 NewSRS(ecc.NextPowerOfTwo(benchSize), big.NewInt(-1)) 471 } 472 }) 473 } 474 475 func BenchmarkKZGCommit(b *testing.B) { 476 477 b.Run("real SRS", func(b *testing.B){ 478 srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) 479 assert.NoError(b, err) 480 // random polynomial 481 p := randomPolynomial(benchSize / 2) 482 483 b.ResetTimer() 484 for i := 0; i < b.N; i++ { 485 _, _ = Commit(p, srs.Pk) 486 } 487 }) 488 b.Run("quick SRS", func(b *testing.B){ 489 srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), big.NewInt(-1)) 490 assert.NoError(b, err) 491 // random polynomial 492 p := randomPolynomial(benchSize / 2) 493 494 b.ResetTimer() 495 for i := 0; i < b.N; i++ { 496 _, _ = Commit(p, srs.Pk) 497 } 498 }) 499 } 500 501 func BenchmarkDivideByXMinusA(b *testing.B) { 502 const pSize = 1 << 22 503 504 // build random polynomial 505 pol := make([]fr.Element, pSize) 506 pol[0].SetRandom() 507 for i := 1; i < pSize; i++ { 508 pol[i] = pol[i-1] 509 } 510 var a, fa fr.Element 511 a.SetRandom() 512 fa.SetRandom() 513 514 b.ResetTimer() 515 for i := 0; i < b.N; i++ { 516 dividePolyByXminusA(pol, fa, a) 517 pol = pol[:pSize] 518 pol[pSize-1] = pol[0] 519 } 520 } 521 522 func BenchmarkKZGOpen(b *testing.B) { 523 srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) 524 assert.NoError(b, err) 525 526 // random polynomial 527 p := randomPolynomial(benchSize / 2) 528 var r fr.Element 529 r.SetRandom() 530 531 b.ResetTimer() 532 for i := 0; i < b.N; i++ { 533 _, _ = Open(p, r, srs.Pk) 534 } 535 } 536 537 func BenchmarkKZGVerify(b *testing.B) { 538 srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) 539 assert.NoError(b, err) 540 541 // random polynomial 542 p := randomPolynomial(benchSize / 2) 543 var r fr.Element 544 r.SetRandom() 545 546 // commit 547 comm, err := Commit(p, srs.Pk) 548 assert.NoError(b, err) 549 550 // open 551 openingProof, err := Open(p, r, srs.Pk) 552 assert.NoError(b, err) 553 554 b.ResetTimer() 555 for i := 0; i < b.N; i++ { 556 Verify(&comm, &openingProof, r, srs.Vk) 557 } 558 } 559 560 func BenchmarkKZGBatchOpen10(b *testing.B) { 561 srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) 562 assert.NoError(b, err) 563 564 // 10 random polynomials 565 var ps [10][]fr.Element 566 for i := 0; i < 10; i++ { 567 ps[i] = randomPolynomial(benchSize / 2) 568 } 569 570 // commitments 571 var commitments [10]Digest 572 for i := 0; i < 10; i++ { 573 commitments[i], _ = Commit(ps[i], srs.Pk) 574 } 575 576 // pick a hash function 577 hf := sha256.New() 578 579 var r fr.Element 580 r.SetRandom() 581 582 b.ResetTimer() 583 for i := 0; i < b.N; i++ { 584 BatchOpenSinglePoint(ps[:], commitments[:], r, hf, srs.Pk) 585 } 586 } 587 588 func BenchmarkKZGBatchVerify10(b *testing.B) { 589 srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) 590 if err != nil { 591 b.Fatal(err) 592 } 593 594 // 10 random polynomials 595 var ps [10][]fr.Element 596 for i := 0; i < 10; i++ { 597 ps[i] = randomPolynomial(benchSize / 2) 598 } 599 600 // commitments 601 var commitments [10]Digest 602 for i := 0; i < 10; i++ { 603 commitments[i], _ = Commit(ps[i], srs.Pk) 604 } 605 606 // pick a hash function 607 hf := sha256.New() 608 609 var r fr.Element 610 r.SetRandom() 611 612 proof, err := BatchOpenSinglePoint(ps[:], commitments[:], r, hf, srs.Pk) 613 if err != nil { 614 b.Fatal(err) 615 } 616 617 b.ResetTimer() 618 for i := 0; i < b.N; i++ { 619 BatchVerifySinglePoint(commitments[:], &proof, r, hf, testSrs.Vk) 620 } 621 } 622 623 func randomPolynomial(size int) []fr.Element { 624 f := make([]fr.Element, size) 625 for i := 0; i < size; i++ { 626 f[i].SetRandom() 627 } 628 return f 629 } 630 631 632 func BenchmarkToLagrangeG1(b *testing.B) { 633 const size = 1 << 14 634 635 var samplePoints [size]{{ .CurvePackage }}.G1Affine 636 fillBenchBasesG1(samplePoints[:]) 637 b.ResetTimer() 638 639 for i := 0; i < b.N; i++ { 640 if _, err := ToLagrangeG1(samplePoints[:]); err != nil { 641 b.Fatal(err) 642 } 643 } 644 } 645 646 func BenchmarkSerializeSRS(b *testing.B) { 647 // let's create a quick SRS 648 srs, err := NewSRS(ecc.NextPowerOfTwo(1 << 24), big.NewInt(-1)) 649 if err != nil { 650 b.Fatal(err) 651 } 652 653 // now we can benchmark the WriteTo, WriteRawTo and WriteDump methods 654 b.Run("WriteTo", func(b *testing.B) { 655 b.ResetTimer() 656 var buf bytes.Buffer 657 for i := 0; i < b.N; i++ { 658 buf.Reset() 659 _, err := srs.WriteTo(&buf) 660 if err != nil { 661 b.Fatal(err) 662 } 663 } 664 }) 665 666 b.Run("WriteRawTo", func(b *testing.B) { 667 b.ResetTimer() 668 var buf bytes.Buffer 669 for i := 0; i < b.N; i++ { 670 buf.Reset() 671 _, err := srs.WriteRawTo(&buf) 672 if err != nil { 673 b.Fatal(err) 674 } 675 } 676 }) 677 678 b.Run("WriteDump", func(b *testing.B) { 679 b.ResetTimer() 680 var buf bytes.Buffer 681 for i := 0; i < b.N; i++ { 682 buf.Reset() 683 if err := srs.WriteDump(&buf); err != nil { 684 b.Fatal(err) 685 } 686 } 687 }) 688 689 } 690 691 func BenchmarkDeserializeSRS(b *testing.B) { 692 // let's create a quick SRS 693 srs, err := NewSRS(ecc.NextPowerOfTwo(1 << 24), big.NewInt(-1)) 694 if err != nil { 695 b.Fatal(err) 696 } 697 698 b.Run("UnsafeReadFrom", func(b *testing.B) { 699 var buf bytes.Buffer 700 if _, err := srs.WriteRawTo(&buf); err != nil { 701 b.Fatal(err) 702 } 703 b.ResetTimer() 704 for i := 0; i < b.N; i++ { 705 var newSRS SRS 706 _, err := newSRS.UnsafeReadFrom(bytes.NewReader(buf.Bytes())) 707 if err != nil { 708 b.Fatal(err) 709 } 710 } 711 }) 712 713 b.Run("ReadDump", func(b *testing.B) { 714 var buf bytes.Buffer 715 err := srs.WriteDump(&buf) 716 if err != nil { 717 b.Fatal(err) 718 } 719 data := buf.Bytes() 720 b.ResetTimer() 721 for i := 0; i < b.N; i++ { 722 var newSRS SRS 723 if err := newSRS.ReadDump(bytes.NewReader(data)); err != nil { 724 b.Fatal(err) 725 } 726 } 727 }) 728 } 729 730 731 func fillBenchBasesG1(samplePoints []{{ .CurvePackage }}.G1Affine) { 732 var r big.Int 733 r.SetString("340444420969191673093399857471996460938405", 10) 734 samplePoints[0].ScalarMultiplication(&samplePoints[0], &r) 735 736 one := samplePoints[0].X 737 one.SetOne() 738 739 for i := 1; i < len(samplePoints); i++ { 740 samplePoints[i].X.Add(&samplePoints[i-1].X, &one) 741 samplePoints[i].Y.Sub(&samplePoints[i-1].Y, &one) 742 } 743 }