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  }