github.com/consensys/gnark-crypto@v0.14.0/ecc/bw6-633/pairing.go (about)

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