github.com/consensys/gnark-crypto@v0.14.0/ecc/bw6-761/pairing.go (about) 1 // 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package bw6761 15 16 import ( 17 "errors" 18 19 "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" 20 "github.com/consensys/gnark-crypto/ecc/bw6-761/internal/fptower" 21 ) 22 23 // GT target group of the pairing 24 type GT = fptower.E6 25 26 type lineEvaluation struct { 27 r0 fp.Element 28 r1 fp.Element 29 r2 fp.Element 30 } 31 32 // Pair calculates the reduced pairing for a set of points 33 // ∏ᵢ e(Pᵢ, Qᵢ). 34 // 35 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 36 func Pair(P []G1Affine, Q []G2Affine) (GT, error) { 37 f, err := MillerLoop(P, Q) 38 if err != nil { 39 return GT{}, err 40 } 41 return FinalExponentiation(&f), nil 42 } 43 44 // PairingCheck calculates the reduced pairing for a set of points and returns True if the result is One 45 // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 46 // 47 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 48 func PairingCheck(P []G1Affine, Q []G2Affine) (bool, error) { 49 f, err := Pair(P, Q) 50 if err != nil { 51 return false, err 52 } 53 var one GT 54 one.SetOne() 55 return f.Equal(&one), nil 56 } 57 58 // FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ 59 // where d = (p^6-1)/r = (p^6-1)/Φ_6(p) ⋅ Φ_6(p)/r = (p^3-1)(p+1)(p^2 - p +1)/r 60 // we use instead d=s ⋅ (p^3-1)(p+1)(p^2 - p +1)/r 61 // where s is the cofactor (x_0+1) (El Housni and Guillevic) 62 func FinalExponentiation(z *GT, _z ...*GT) GT { 63 64 var result GT 65 result.Set(z) 66 67 for _, e := range _z { 68 result.Mul(&result, e) 69 } 70 71 var buf GT 72 73 // Easy part 74 // (p^3-1)(p+1) 75 buf.Conjugate(&result) 76 result.Inverse(&result) 77 buf.Mul(&buf, &result) 78 result.Frobenius(&buf). 79 Mul(&result, &buf) 80 81 var one GT 82 one.SetOne() 83 if result.Equal(&one) { 84 return result 85 } 86 87 // 2. Hard part (up to permutation) 88 // (x₀+1)(p²-p+1)/r 89 // Algorithm 4.4 from https://yelhousni.github.io/phd.pdf 90 var a, b, c, d, e, f, g, h, i, j, k, t GT 91 a.ExptMinus1Square(&result) 92 t.Frobenius(&result) 93 a.Mul(&a, &t) 94 b.ExptPlus1(&a) 95 t.Conjugate(&result) 96 b.Mul(&b, &t) 97 t.CyclotomicSquare(&a) 98 a.Mul(&a, &t) 99 c.ExptMinus1Div3(&b) 100 d.ExptMinus1(&c) 101 e.ExptMinus1Square(&d) 102 e.Mul(&e, &d) 103 d.Conjugate(&d) 104 f.Mul(&d, &b) 105 g.ExptPlus1(&e) 106 g.Mul(&g, &f) 107 h.Mul(&g, &c) 108 i.Mul(&g, &d) 109 i.ExptPlus1(&i) 110 t.Conjugate(&f) 111 i.Mul(&i, &t) 112 j.Expc1(&h) 113 j.Mul(&j, &e) 114 k.CyclotomicSquare(&j) 115 k.Mul(&k, &j) 116 k.Mul(&k, &b) 117 t.Expc2(&i) 118 k.Mul(&k, &t) 119 result.Mul(&a, &k) 120 121 return result 122 } 123 124 // MillerLoop computes the multi-Miller loop 125 // ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = 126 // ∏ᵢ { fᵢ_{x₀+1+λ(x₀³-x₀²-x₀),Qᵢ}(Pᵢ) } 127 // 128 // Alg.2 in https://eprint.iacr.org/2021/1359.pdf 129 // Eq. (6') in https://hackmd.io/@gnark/BW6-761-changes 130 func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { 131 // check input size match 132 n := len(P) 133 if n == 0 || n != len(Q) { 134 return GT{}, errors.New("invalid inputs sizes") 135 } 136 137 // filter infinity points 138 p := make([]G1Affine, 0, n) 139 q0 := make([]G2Affine, 0, n) 140 141 for k := 0; k < n; k++ { 142 if P[k].IsInfinity() || Q[k].IsInfinity() { 143 continue 144 } 145 p = append(p, P[k]) 146 q0 = append(q0, Q[k]) 147 } 148 149 n = len(p) 150 151 // precomputations 152 qProj1 := make([]g2Proj, n) 153 q1 := make([]G2Affine, n) 154 q1Neg := make([]G2Affine, n) 155 q0Neg := make([]G2Affine, n) 156 for k := 0; k < n; k++ { 157 q1[k].Y.Neg(&q0[k].Y) 158 q0Neg[k].X.Set(&q0[k].X) 159 q0Neg[k].Y.Set(&q1[k].Y) 160 q1[k].X.Mul(&q0[k].X, &thirdRootOneG1) 161 qProj1[k].FromAffine(&q1[k]) 162 q1Neg[k].Neg(&q1[k]) 163 } 164 165 // f_{a0+λ*a1,Q}(P) 166 var result GT 167 result.SetOne() 168 var l, l0 lineEvaluation 169 var prodLines [5]fp.Element 170 171 var j int8 172 173 if n >= 1 { 174 // i = 188, separately to avoid an E12 Square 175 // (Square(res) = 1² = 1) 176 // j = 0 177 // k = 0, separately to avoid MulBy014 (res × ℓ) 178 // (assign line to res) 179 // qProj1[0] ← 2qProj1[0] and l0 the tangent ℓ passing 2qProj1[0] 180 qProj1[0].doubleStep(&l0) 181 // line evaluation at Q[0] (assign) 182 result.B0.A0.Set(&l0.r0) 183 result.B0.A1.Mul(&l0.r1, &p[0].X) 184 result.B1.A1.Mul(&l0.r2, &p[0].Y) 185 } 186 187 // k = 1 188 if n >= 2 { 189 // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] 190 qProj1[1].doubleStep(&l0) 191 // line evaluation at Q[1] 192 l0.r1.Mul(&l0.r1, &p[1].X) 193 l0.r2.Mul(&l0.r2, &p[1].Y) 194 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &result.B0.A0, &result.B0.A1, &result.B1.A1) 195 result.B0.A0 = prodLines[0] 196 result.B0.A1 = prodLines[1] 197 result.B0.A2 = prodLines[2] 198 result.B1.A1 = prodLines[3] 199 result.B1.A2 = prodLines[4] 200 } 201 202 // k >= 2 203 for k := 2; k < n; k++ { 204 // qProj1[k] ← 2qProj1[k] and l0 the tangent ℓ passing 2qProj1[k] 205 qProj1[k].doubleStep(&l0) 206 // line evaluation at Q[k] 207 l0.r1.Mul(&l0.r1, &p[k].X) 208 l0.r2.Mul(&l0.r2, &p[k].Y) 209 // ℓ × res 210 result.MulBy014(&l0.r0, &l0.r1, &l0.r2) 211 } 212 213 for i := 187; i >= 1; i-- { 214 result.Square(&result) 215 216 j = LoopCounter1[i]*3 + LoopCounter[i] 217 218 for k := 0; k < n; k++ { 219 qProj1[k].doubleStep(&l0) 220 l0.r1.Mul(&l0.r1, &p[k].X) 221 l0.r2.Mul(&l0.r2, &p[k].Y) 222 223 switch j { 224 // cases -4, -2, 2, 4 do not occur, given the static LoopCounters 225 case -3: 226 qProj1[k].addMixedStep(&l, &q1Neg[k]) 227 l.r1.Mul(&l.r1, &p[k].X) 228 l.r2.Mul(&l.r2, &p[k].Y) 229 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 230 result.MulBy01245(&prodLines) 231 case -1: 232 qProj1[k].addMixedStep(&l, &q0Neg[k]) 233 l.r1.Mul(&l.r1, &p[k].X) 234 l.r2.Mul(&l.r2, &p[k].Y) 235 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 236 result.MulBy01245(&prodLines) 237 case 0: 238 result.MulBy014(&l0.r0, &l0.r1, &l0.r2) 239 case 1: 240 qProj1[k].addMixedStep(&l, &q0[k]) 241 l.r1.Mul(&l.r1, &p[k].X) 242 l.r2.Mul(&l.r2, &p[k].Y) 243 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 244 result.MulBy01245(&prodLines) 245 case 3: 246 qProj1[k].addMixedStep(&l, &q1[k]) 247 l.r1.Mul(&l.r1, &p[k].X) 248 l.r2.Mul(&l.r2, &p[k].Y) 249 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 250 result.MulBy01245(&prodLines) 251 default: 252 return GT{}, errors.New("invalid LoopCounter") 253 } 254 } 255 } 256 257 // i = 0, separately to avoid a point addition 258 // j = -3 259 result.Square(&result) 260 for k := 0; k < n; k++ { 261 qProj1[k].doubleStep(&l0) 262 l0.r1.Mul(&l0.r1, &p[k].X) 263 l0.r2.Mul(&l0.r2, &p[k].Y) 264 qProj1[k].lineCompute(&l, &q1Neg[k]) 265 l.r1.Mul(&l.r1, &p[k].X) 266 l.r2.Mul(&l.r2, &p[k].Y) 267 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 268 result.MulBy01245(&prodLines) 269 } 270 271 return result, nil 272 273 } 274 275 // doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop 276 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 277 func (p *g2Proj) doubleStep(evaluations *lineEvaluation) { 278 279 // get some Element from our pool 280 var t1, A, B, C, D, E, EE, F, G, H, I, J, K fp.Element 281 A.Mul(&p.x, &p.y) 282 A.Halve() 283 B.Square(&p.y) 284 C.Square(&p.z) 285 D.Double(&C). 286 Add(&D, &C) 287 E.Double(&D).Double(&E) 288 F.Double(&E). 289 Add(&F, &E) 290 G.Add(&B, &F) 291 G.Halve() 292 H.Add(&p.y, &p.z). 293 Square(&H) 294 t1.Add(&B, &C) 295 H.Sub(&H, &t1) 296 I.Sub(&E, &B) 297 J.Square(&p.x) 298 EE.Square(&E) 299 K.Double(&EE). 300 Add(&K, &EE) 301 302 // X, Y, Z 303 p.x.Sub(&B, &F). 304 Mul(&p.x, &A) 305 p.y.Square(&G). 306 Sub(&p.y, &K) 307 p.z.Mul(&B, &H) 308 309 // Line evaluation 310 evaluations.r0.Set(&I) 311 evaluations.r1.Double(&J). 312 Add(&evaluations.r1, &J) 313 evaluations.r2.Neg(&H) 314 } 315 316 // addMixedStep point addition in Mixed Homogenous projective and Affine coordinates 317 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 318 func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) { 319 320 // get some Element from our pool 321 var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fp.Element 322 Y2Z1.Mul(&a.Y, &p.z) 323 O.Sub(&p.y, &Y2Z1) 324 X2Z1.Mul(&a.X, &p.z) 325 L.Sub(&p.x, &X2Z1) 326 C.Square(&O) 327 D.Square(&L) 328 E.Mul(&L, &D) 329 F.Mul(&p.z, &C) 330 G.Mul(&p.x, &D) 331 t0.Double(&G) 332 H.Add(&E, &F). 333 Sub(&H, &t0) 334 t1.Mul(&p.y, &E) 335 336 // X, Y, Z 337 p.x.Mul(&L, &H) 338 p.y.Sub(&G, &H). 339 Mul(&p.y, &O). 340 Sub(&p.y, &t1) 341 p.z.Mul(&E, &p.z) 342 343 t2.Mul(&L, &a.Y) 344 J.Mul(&a.X, &O). 345 Sub(&J, &t2) 346 347 // Line evaluation 348 evaluations.r0.Set(&J) 349 evaluations.r1.Neg(&O) 350 evaluations.r2.Set(&L) 351 } 352 353 // lineCompute computes the line through p in Homogenous projective coordinates 354 // and a in affine coordinates. It does not compute the resulting point p+a. 355 func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { 356 357 // get some Element from our pool 358 var Y2Z1, X2Z1, O, L, t2, J fp.Element 359 Y2Z1.Mul(&a.Y, &p.z) 360 O.Sub(&p.y, &Y2Z1) 361 X2Z1.Mul(&a.X, &p.z) 362 L.Sub(&p.x, &X2Z1) 363 t2.Mul(&L, &a.Y) 364 J.Mul(&a.X, &O). 365 Sub(&J, &t2) 366 367 // Line evaluation 368 evaluations.r0.Set(&J) 369 evaluations.r1.Neg(&O) 370 evaluations.r2.Set(&L) 371 } 372 373 // ---------------------- 374 // Fixed-argument pairing 375 // ---------------------- 376 377 type LineEvaluationAff struct { 378 R0 fp.Element 379 R1 fp.Element 380 } 381 382 // PairFixedQ calculates the reduced pairing for a set of points 383 // ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2. 384 // 385 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 386 func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 387 f, err := MillerLoopFixedQ(P, lines) 388 if err != nil { 389 return GT{}, err 390 } 391 return FinalExponentiation(&f), nil 392 } 393 394 // PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One 395 // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2. 396 // 397 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 398 func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (bool, error) { 399 f, err := PairFixedQ(P, lines) 400 if err != nil { 401 return false, err 402 } 403 var one GT 404 one.SetOne() 405 return f.Equal(&one), nil 406 } 407 408 // PrecomputeLines precomputes the lines for the fixed-argument Miller loop 409 func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter) - 1]LineEvaluationAff) { 410 411 // precomputations 412 var accQ, imQ, imQneg, negQ G2Affine 413 imQ.Y.Neg(&Q.Y) 414 imQ.X.Mul(&Q.X, &thirdRootOneG1) 415 negQ.X.Set(&Q.X) 416 negQ.Y.Set(&imQ.Y) 417 accQ.Set(&imQ) 418 imQneg.X.Set(&imQ.X) 419 imQneg.Y.Set(&Q.Y) 420 421 for i := len(LoopCounter) - 2; i > 0; i-- { 422 423 switch LoopCounter1[i]*3 + LoopCounter[i] { 424 // cases -4, -2, 2, 4 do not occur, given the static LoopCounters 425 case -3: 426 accQ.doubleAndAddStep(&PrecomputedLines[0][i], &PrecomputedLines[1][i], &imQneg) 427 case -1: 428 accQ.doubleAndAddStep(&PrecomputedLines[0][i], &PrecomputedLines[1][i], &negQ) 429 case 0: 430 accQ.doubleStep(&PrecomputedLines[0][i]) 431 case 1: 432 accQ.doubleAndAddStep(&PrecomputedLines[0][i], &PrecomputedLines[1][i], &Q) 433 case 3: 434 accQ.doubleAndAddStep(&PrecomputedLines[0][i], &PrecomputedLines[1][i], &imQ) 435 default: 436 return [2][len(LoopCounter) - 1]LineEvaluationAff{} 437 } 438 } 439 accQ.tangentCompute(&PrecomputedLines[0][0]) 440 441 return PrecomputedLines 442 } 443 444 // MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop 445 // but Qᵢ are fixed points in G2 known in advance. 446 func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 447 // check input size match 448 n := len(P) 449 if n == 0 || n != len(lines) { 450 return GT{}, errors.New("invalid inputs sizes") 451 } 452 453 // no need to filter infinity points: 454 // 1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so 455 // lines R0 and R1 are 0. It happens that result will stay, through 456 // the Miller loop, in 𝔽p⁶ because MulBy01(0,0,1), 457 // Mul01By01(0,0,1,0,0,1) and MulBy01245 set result.C0 to 0. At the 458 // end result will be in a proper subgroup of Fp¹² so it be reduced to 459 // 1 in FinalExponentiation. 460 // 461 // and/or 462 // 463 // 2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1 464 // that are 0 because of gnark-convention (*/0==0) in doubleStep and 465 // addStep. Similarly to Pᵢ=(0,0) it happens that result be 1 466 // after the FinalExponentiation. 467 468 // precomputations 469 yInv := make([]fp.Element, n) 470 xNegOverY := make([]fp.Element, n) 471 for k := 0; k < n; k++ { 472 yInv[k].Set(&P[k].Y) 473 } 474 yInv = fp.BatchInvert(yInv) 475 for k := 0; k < n; k++ { 476 xNegOverY[k].Mul(&P[k].X, &yInv[k]). 477 Neg(&xNegOverY[k]) 478 } 479 480 // f_{a0+λ*a1,Q}(P) 481 var result GT 482 result.SetOne() 483 var prodLines [5]fp.Element 484 485 for i := len(LoopCounter) - 2; i >= 0; i-- { 486 result.Square(&result) 487 488 j := LoopCounter1[i]*3 + LoopCounter[i] 489 for k := 0; k < n; k++ { 490 lines[k][0][i].R1. 491 Mul( 492 &lines[k][0][i].R1, 493 &yInv[k], 494 ) 495 lines[k][0][i].R0. 496 Mul(&lines[k][0][i].R0, 497 &xNegOverY[k], 498 ) 499 if j == 0 { 500 result.MulBy01( 501 &lines[k][0][i].R1, 502 &lines[k][0][i].R0, 503 ) 504 505 } else { 506 lines[k][1][i].R1. 507 Mul( 508 &lines[k][1][i].R1, 509 &yInv[k], 510 ) 511 lines[k][1][i].R0. 512 Mul( 513 &lines[k][1][i].R0, 514 &xNegOverY[k], 515 ) 516 prodLines = fptower.Mul01By01( 517 &lines[k][0][i].R1, &lines[k][0][i].R0, 518 &lines[k][1][i].R1, &lines[k][1][i].R0, 519 ) 520 result.MulBy01245(&prodLines) 521 } 522 } 523 } 524 525 return result, nil 526 527 } 528 529 func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) { 530 531 var n, d, λ, xr, yr fp.Element 532 // λ = 3x²/2y 533 n.Square(&p.X) 534 λ.Double(&n). 535 Add(&λ, &n) 536 d.Double(&p.Y) 537 λ.Div(&λ, &d) 538 539 // xr = λ²-2x 540 xr.Square(&λ). 541 Sub(&xr, &p.X). 542 Sub(&xr, &p.X) 543 544 // yr = λ(x-xr)-y 545 yr.Sub(&p.X, &xr). 546 Mul(&yr, &λ). 547 Sub(&yr, &p.Y) 548 549 evaluations.R0.Set(&λ) 550 evaluations.R1.Mul(&λ, &p.X). 551 Sub(&evaluations.R1, &p.Y) 552 553 p.X.Set(&xr) 554 p.Y.Set(&yr) 555 } 556 557 func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) { 558 var n, d, λ, λλ, xr, yr fp.Element 559 560 // compute λ = (y2-y1)/(x2-x1) 561 n.Sub(&a.Y, &p.Y) 562 d.Sub(&a.X, &p.X) 563 λ.Div(&n, &d) 564 565 // xr = λ²-x1-x2 566 λλ.Square(&λ) 567 n.Add(&p.X, &a.X) 568 xr.Sub(&λλ, &n) 569 570 // yr = λ(x1-xr) - y1 571 yr.Sub(&p.X, &xr). 572 Mul(&yr, &λ). 573 Sub(&yr, &p.Y) 574 575 evaluations.R0.Set(&λ) 576 evaluations.R1.Mul(&λ, &p.X). 577 Sub(&evaluations.R1, &p.Y) 578 579 p.X.Set(&xr) 580 p.Y.Set(&yr) 581 } 582 583 func (p *G2Affine) doubleAndAddStep(evaluations1, evaluations2 *LineEvaluationAff, a *G2Affine) { 584 var n, d, l1, x3, l2, x4, y4 fp.Element 585 586 // compute λ1 = (y2-y1)/(x2-x1) 587 n.Sub(&p.Y, &a.Y) 588 d.Sub(&p.X, &a.X) 589 l1.Div(&n, &d) 590 591 // compute x3 =λ1²-x1-x2 592 x3.Square(&l1) 593 x3.Sub(&x3, &p.X) 594 x3.Sub(&x3, &a.X) 595 596 // omit y3 computation 597 598 // compute line1 599 evaluations1.R0.Set(&l1) 600 evaluations1.R1.Mul(&l1, &p.X) 601 evaluations1.R1.Sub(&evaluations1.R1, &p.Y) 602 603 // compute λ2 = -λ1-2y1/(x3-x1) 604 n.Double(&p.Y) 605 d.Sub(&x3, &p.X) 606 l2.Div(&n, &d) 607 l2.Add(&l2, &l1) 608 l2.Neg(&l2) 609 610 // compute x4 = λ2²-x1-x3 611 x4.Square(&l2) 612 x4.Sub(&x4, &p.X) 613 x4.Sub(&x4, &x3) 614 615 // compute y4 = λ2(x1 - x4)-y1 616 y4.Sub(&p.X, &x4) 617 y4.Mul(&l2, &y4) 618 y4.Sub(&y4, &p.Y) 619 620 // compute line2 621 evaluations2.R0.Set(&l2) 622 evaluations2.R1.Mul(&l2, &p.X) 623 evaluations2.R1.Sub(&evaluations2.R1, &p.Y) 624 625 p.X.Set(&x4) 626 p.Y.Set(&y4) 627 } 628 629 func (p *G2Affine) tangentCompute(evaluations *LineEvaluationAff) { 630 631 var n, d, λ fp.Element 632 // λ = 3x²/2y 633 n.Square(&p.X) 634 λ.Double(&n). 635 Add(&λ, &n) 636 d.Double(&p.Y) 637 λ.Div(&λ, &d) 638 639 evaluations.R0.Set(&λ) 640 evaluations.R1.Mul(&λ, &p.X). 641 Sub(&evaluations.R1, &p.Y) 642 }