github.com/consensys/gnark-crypto@v0.14.0/ecc/bn254/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 bn254
    16  
    17  import (
    18  	"errors"
    19  
    20  	"github.com/consensys/gnark-crypto/ecc/bn254/fp"
    21  	"github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower"
    22  )
    23  
    24  // GT target group of the pairing
    25  type GT = fptower.E12
    26  
    27  type lineEvaluation struct {
    28  	r0 fptower.E2
    29  	r1 fptower.E2
    30  	r2 fptower.E2
    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¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r
    61  // we use instead d=s ⋅ (p⁶-1)(p²+1)(p⁴ - p² +1)/r
    62  // where s is the cofactor 2x₀(6x₀²+3x₀+1)
    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 t [5]GT
    73  
    74  	// Easy part
    75  	// (p⁶-1)(p²+1)
    76  	t[0].Conjugate(&result)
    77  	result.Inverse(&result)
    78  	t[0].Mul(&t[0], &result)
    79  	result.FrobeniusSquare(&t[0]).Mul(&result, &t[0])
    80  
    81  	var one GT
    82  	one.SetOne()
    83  	if result.Equal(&one) {
    84  		return result
    85  	}
    86  
    87  	// Hard part (up to permutation)
    88  	// 2x₀(6x₀²+3x₀+1)(p⁴-p²+1)/r
    89  	// Duquesne and Ghammam
    90  	// https://eprint.iacr.org/2015/192.pdf
    91  	// Fuentes et al. (alg. 6)
    92  	t[0].Expt(&result).
    93  		Conjugate(&t[0])
    94  	t[0].CyclotomicSquare(&t[0])
    95  	t[1].CyclotomicSquare(&t[0])
    96  	t[1].Mul(&t[0], &t[1])
    97  	t[2].Expt(&t[1])
    98  	t[2].Conjugate(&t[2])
    99  	t[3].Conjugate(&t[1])
   100  	t[1].Mul(&t[2], &t[3])
   101  	t[3].CyclotomicSquare(&t[2])
   102  	t[4].Expt(&t[3])
   103  	t[4].Mul(&t[1], &t[4])
   104  	t[3].Mul(&t[0], &t[4])
   105  	t[0].Mul(&t[2], &t[4])
   106  	t[0].Mul(&result, &t[0])
   107  	t[2].Frobenius(&t[3])
   108  	t[0].Mul(&t[2], &t[0])
   109  	t[2].FrobeniusSquare(&t[4])
   110  	t[0].Mul(&t[2], &t[0])
   111  	t[2].Conjugate(&result)
   112  	t[2].Mul(&t[2], &t[3])
   113  	t[2].FrobeniusCube(&t[2])
   114  	t[0].Mul(&t[2], &t[0])
   115  
   116  	return t[0]
   117  }
   118  
   119  // MillerLoop computes the multi-Miller loop
   120  // ∏ᵢ MillerLoop(Pᵢ, Qᵢ) =
   121  // ∏ᵢ { fᵢ_{6x₀+2,Qᵢ}(Pᵢ) · ℓᵢ_{[6x₀+2]Qᵢ,π(Qᵢ)}(Pᵢ) · ℓᵢ_{[6x₀+2]Qᵢ+π(Qᵢ),-π²(Qᵢ)}(Pᵢ) }
   122  func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) {
   123  	n := len(P)
   124  	if n == 0 || n != len(Q) {
   125  		return GT{}, errors.New("invalid inputs sizes")
   126  	}
   127  
   128  	// filter infinity points
   129  	p := make([]G1Affine, 0, n)
   130  	q := make([]G2Affine, 0, n)
   131  
   132  	for k := 0; k < n; k++ {
   133  		if P[k].IsInfinity() || Q[k].IsInfinity() {
   134  			continue
   135  		}
   136  		p = append(p, P[k])
   137  		q = append(q, Q[k])
   138  	}
   139  	n = len(p)
   140  
   141  	// projective points for Q
   142  	qProj := make([]g2Proj, n)
   143  	qNeg := make([]G2Affine, n)
   144  	for k := 0; k < n; k++ {
   145  		qProj[k].FromAffine(&q[k])
   146  		qNeg[k].Neg(&q[k])
   147  	}
   148  
   149  	var result GT
   150  	result.SetOne()
   151  	var l2, l1 lineEvaluation
   152  	var prodLines [5]E2
   153  
   154  	// Compute ∏ᵢ { fᵢ_{6x₀+2,Q}(P) }
   155  	if n >= 1 {
   156  		// i = 64, separately to avoid an E12 Square
   157  		// (Square(res) = 1² = 1)
   158  		// LoopCounter[64] = 0
   159  		// k = 0, separately to avoid MulBy034 (res × ℓ)
   160  		// (assign line to res)
   161  
   162  		// qProj[0] ← 2qProj[0] and l1 the tangent ℓ passing 2qProj[0]
   163  		qProj[0].doubleStep(&l1)
   164  		// line evaluation at P[0] (assign)
   165  		result.C0.B0.MulByElement(&l1.r0, &p[0].Y)
   166  		result.C1.B0.MulByElement(&l1.r1, &p[0].X)
   167  		result.C1.B1.Set(&l1.r2)
   168  	}
   169  
   170  	if n >= 2 {
   171  		// k = 1, separately to avoid MulBy034 (res × ℓ)
   172  		// (res is also a line at this point, so we use Mul034By034 ℓ × ℓ)
   173  
   174  		// qProj[1] ← 2qProj[1] and l1 the tangent ℓ passing 2qProj[1]
   175  		qProj[1].doubleStep(&l1)
   176  		// line evaluation at P[1]
   177  		l1.r0.MulByElement(&l1.r0, &p[1].Y)
   178  		l1.r1.MulByElement(&l1.r1, &p[1].X)
   179  		// ℓ × res
   180  		prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &result.C0.B0, &result.C1.B0, &result.C1.B1)
   181  		result.C0.B0 = prodLines[0]
   182  		result.C0.B1 = prodLines[1]
   183  		result.C0.B2 = prodLines[2]
   184  		result.C1.B0 = prodLines[3]
   185  		result.C1.B1 = prodLines[4]
   186  	}
   187  
   188  	// k >= 2
   189  	for k := 2; k < n; k++ {
   190  		// qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k]
   191  		qProj[k].doubleStep(&l1)
   192  		// line evaluation at P[k]
   193  		l1.r0.MulByElement(&l1.r0, &p[k].Y)
   194  		l1.r1.MulByElement(&l1.r1, &p[k].X)
   195  		// ℓ × res
   196  		result.MulBy034(&l1.r0, &l1.r1, &l1.r2)
   197  	}
   198  
   199  	// i = 63, separately to avoid a doubleStep (LoopCounter[63]=-1)
   200  	// (at this point qProj = 2Q, so 2qProj-Q=3Q is equivalent to qProj+Q=3Q
   201  	// this means doubleStep followed by an addMixedStep is equivalent to an
   202  	// addMixedStep here)
   203  
   204  	result.Square(&result)
   205  	for k := 0; k < n; k++ {
   206  		// l2 the line passing qProj[k] and -Q
   207  		// (avoids a point addition: qProj[k]-Q)
   208  		qProj[k].lineCompute(&l2, &qNeg[k])
   209  		// line evaluation at P[k]
   210  		l2.r0.MulByElement(&l2.r0, &p[k].Y)
   211  		l2.r1.MulByElement(&l2.r1, &p[k].X)
   212  		// qProj[k] ← qProj[k]+Q[k] and
   213  		// l1 the line ℓ passing qProj[k] and Q[k]
   214  		qProj[k].addMixedStep(&l1, &q[k])
   215  		// line evaluation at P[k]
   216  		l1.r0.MulByElement(&l1.r0, &p[k].Y)
   217  		l1.r1.MulByElement(&l1.r1, &p[k].X)
   218  		// ℓ × ℓ
   219  		prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2)
   220  		// (ℓ × ℓ) × res
   221  		result.MulBy01234(&prodLines)
   222  	}
   223  
   224  	// i <= 62
   225  	for i := len(LoopCounter) - 4; i >= 0; i-- {
   226  		// mutualize the square among n Miller loops
   227  		// (∏ᵢfᵢ)²
   228  		result.Square(&result)
   229  
   230  		for k := 0; k < n; k++ {
   231  			// qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k]
   232  			qProj[k].doubleStep(&l1)
   233  			// line evaluation at P[k]
   234  			l1.r0.MulByElement(&l1.r0, &p[k].Y)
   235  			l1.r1.MulByElement(&l1.r1, &p[k].X)
   236  
   237  			if LoopCounter[i] == 1 {
   238  				// qProj[k] ← qProj[k]+Q[k] and
   239  				// l2 the line ℓ passing qProj[k] and Q[k]
   240  				qProj[k].addMixedStep(&l2, &q[k])
   241  				// line evaluation at P[k]
   242  				l2.r0.MulByElement(&l2.r0, &p[k].Y)
   243  				l2.r1.MulByElement(&l2.r1, &p[k].X)
   244  				// ℓ × ℓ
   245  				prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2)
   246  				// (ℓ × ℓ) × res
   247  				result.MulBy01234(&prodLines)
   248  
   249  			} else if LoopCounter[i] == -1 {
   250  				// qProj[k] ← qProj[k]-Q[k] and
   251  				// l2 the line ℓ passing qProj[k] and -Q[k]
   252  				qProj[k].addMixedStep(&l2, &qNeg[k])
   253  				// line evaluation at P[k]
   254  				l2.r0.MulByElement(&l2.r0, &p[k].Y)
   255  				l2.r1.MulByElement(&l2.r1, &p[k].X)
   256  				// ℓ × ℓ
   257  				prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2)
   258  				// (ℓ × ℓ) × res
   259  				result.MulBy01234(&prodLines)
   260  			} else {
   261  				// ℓ × res
   262  				result.MulBy034(&l1.r0, &l1.r1, &l1.r2)
   263  			}
   264  		}
   265  	}
   266  
   267  	// Compute  ∏ᵢ { ℓᵢ_{[6x₀+2]Q,π(Q)}(P) · ℓᵢ_{[6x₀+2]Q+π(Q),-π²(Q)}(P) }
   268  	var Q1, Q2 G2Affine
   269  	for k := 0; k < n; k++ {
   270  		//Q1 = π(Q)
   271  		Q1.X.Conjugate(&q[k].X).MulByNonResidue1Power2(&Q1.X)
   272  		Q1.Y.Conjugate(&q[k].Y).MulByNonResidue1Power3(&Q1.Y)
   273  
   274  		// Q2 = -π²(Q)
   275  		Q2.X.MulByNonResidue2Power2(&q[k].X)
   276  		Q2.Y.MulByNonResidue2Power3(&q[k].Y).Neg(&Q2.Y)
   277  
   278  		// qProj[k] ← qProj[k]+π(Q) and
   279  		// l1 the line passing qProj[k] and π(Q)
   280  		qProj[k].addMixedStep(&l2, &Q1)
   281  		// line evaluation at P[k]
   282  		l2.r0.MulByElement(&l2.r0, &p[k].Y)
   283  		l2.r1.MulByElement(&l2.r1, &p[k].X)
   284  
   285  		// l2 the line passing qProj[k] and -π²(Q)
   286  		// (avoids a point addition: qProj[k]-π²(Q))
   287  		qProj[k].lineCompute(&l1, &Q2)
   288  		// line evaluation at P[k]
   289  		l1.r0.MulByElement(&l1.r0, &p[k].Y)
   290  		l1.r1.MulByElement(&l1.r1, &p[k].X)
   291  
   292  		// ℓ × ℓ
   293  		prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2)
   294  		// (ℓ × ℓ) × res
   295  		result.MulBy01234(&prodLines)
   296  	}
   297  
   298  	return result, nil
   299  }
   300  
   301  // doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop
   302  // https://eprint.iacr.org/2013/722.pdf (Section 4.3)
   303  func (p *g2Proj) doubleStep(evaluations *lineEvaluation) {
   304  
   305  	// get some Element from our pool
   306  	var t1, A, B, C, D, E, EE, F, G, H, I, J, K fptower.E2
   307  	A.Mul(&p.x, &p.y)
   308  	A.Halve()
   309  	B.Square(&p.y)
   310  	C.Square(&p.z)
   311  	D.Double(&C).
   312  		Add(&D, &C)
   313  	E.MulBybTwistCurveCoeff(&D)
   314  	F.Double(&E).
   315  		Add(&F, &E)
   316  	G.Add(&B, &F)
   317  	G.Halve()
   318  	H.Add(&p.y, &p.z).
   319  		Square(&H)
   320  	t1.Add(&B, &C)
   321  	H.Sub(&H, &t1)
   322  	I.Sub(&E, &B)
   323  	J.Square(&p.x)
   324  	EE.Square(&E)
   325  	K.Double(&EE).
   326  		Add(&K, &EE)
   327  
   328  	// X, Y, Z
   329  	p.x.Sub(&B, &F).
   330  		Mul(&p.x, &A)
   331  	p.y.Square(&G).
   332  		Sub(&p.y, &K)
   333  	p.z.Mul(&B, &H)
   334  
   335  	// Line evaluation
   336  	evaluations.r0.Neg(&H)
   337  	evaluations.r1.Double(&J).
   338  		Add(&evaluations.r1, &J)
   339  	evaluations.r2.Set(&I)
   340  }
   341  
   342  // addMixedStep point addition in Mixed Homogenous projective and Affine coordinates
   343  // https://eprint.iacr.org/2013/722.pdf (Section 4.3)
   344  func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) {
   345  
   346  	// get some Element from our pool
   347  	var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fptower.E2
   348  	Y2Z1.Mul(&a.Y, &p.z)
   349  	O.Sub(&p.y, &Y2Z1)
   350  	X2Z1.Mul(&a.X, &p.z)
   351  	L.Sub(&p.x, &X2Z1)
   352  	C.Square(&O)
   353  	D.Square(&L)
   354  	E.Mul(&L, &D)
   355  	F.Mul(&p.z, &C)
   356  	G.Mul(&p.x, &D)
   357  	t0.Double(&G)
   358  	H.Add(&E, &F).
   359  		Sub(&H, &t0)
   360  	t1.Mul(&p.y, &E)
   361  
   362  	// X, Y, Z
   363  	p.x.Mul(&L, &H)
   364  	p.y.Sub(&G, &H).
   365  		Mul(&p.y, &O).
   366  		Sub(&p.y, &t1)
   367  	p.z.Mul(&E, &p.z)
   368  
   369  	t2.Mul(&L, &a.Y)
   370  	J.Mul(&a.X, &O).
   371  		Sub(&J, &t2)
   372  
   373  	// Line evaluation
   374  	evaluations.r0.Set(&L)
   375  	evaluations.r1.Neg(&O)
   376  	evaluations.r2.Set(&J)
   377  }
   378  
   379  // lineCompute computes the line through p in Homogenous projective coordinates
   380  // and a in affine coordinates. It does not compute the resulting point p+a.
   381  func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) {
   382  
   383  	// get some Element from our pool
   384  	var Y2Z1, X2Z1, O, L, t2, J fptower.E2
   385  	Y2Z1.Mul(&a.Y, &p.z)
   386  	O.Sub(&p.y, &Y2Z1)
   387  	X2Z1.Mul(&a.X, &p.z)
   388  	L.Sub(&p.x, &X2Z1)
   389  	t2.Mul(&L, &a.Y)
   390  	J.Mul(&a.X, &O).
   391  		Sub(&J, &t2)
   392  
   393  	// Line evaluation
   394  	evaluations.r0.Set(&L)
   395  	evaluations.r1.Neg(&O)
   396  	evaluations.r2.Set(&J)
   397  }
   398  
   399  // ----------------------
   400  // Fixed-argument pairing
   401  // ----------------------
   402  
   403  type LineEvaluationAff struct {
   404  	R0 fptower.E2
   405  	R1 fptower.E2
   406  }
   407  
   408  // PairFixedQ calculates the reduced pairing for a set of points
   409  // ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2.
   410  //
   411  // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup.
   412  func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter)]LineEvaluationAff) (GT, error) {
   413  	f, err := MillerLoopFixedQ(P, lines)
   414  	if err != nil {
   415  		return GT{}, err
   416  	}
   417  	return FinalExponentiation(&f), nil
   418  }
   419  
   420  // PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One
   421  // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2.
   422  //
   423  // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup.
   424  func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter)]LineEvaluationAff) (bool, error) {
   425  	f, err := PairFixedQ(P, lines)
   426  	if err != nil {
   427  		return false, err
   428  	}
   429  	var one GT
   430  	one.SetOne()
   431  	return f.Equal(&one), nil
   432  }
   433  
   434  // PrecomputeLines precomputes the lines for the fixed-argument Miller loop
   435  func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter)]LineEvaluationAff) {
   436  	var accQ, negQ G2Affine
   437  	accQ.Set(&Q)
   438  	negQ.Neg(&Q)
   439  
   440  	n := len(LoopCounter)
   441  	for i := n - 2; i >= 0; i-- {
   442  		switch LoopCounter[i] {
   443  		case 0:
   444  			accQ.doubleStep(&PrecomputedLines[0][i])
   445  		case 1:
   446  			accQ.doubleAndAddStep(&PrecomputedLines[0][i], &PrecomputedLines[1][i], &Q)
   447  		case -1:
   448  			accQ.doubleAndAddStep(&PrecomputedLines[0][i], &PrecomputedLines[1][i], &negQ)
   449  		default:
   450  			return [2][len(LoopCounter)]LineEvaluationAff{}
   451  		}
   452  	}
   453  
   454  	var psiQ, phiQ G2Affine
   455  	phiQ.X.Conjugate(&Q.X).MulByNonResidue1Power2(&phiQ.X)
   456  	phiQ.Y.Conjugate(&Q.Y).MulByNonResidue1Power3(&phiQ.Y)
   457  	psiQ.X.MulByNonResidue2Power2(&Q.X)
   458  	psiQ.Y.MulByNonResidue2Power3(&Q.Y).Neg(&psiQ.Y)
   459  
   460  	accQ.addStep(&PrecomputedLines[1][n-1], &phiQ)
   461  	accQ.addStep(&PrecomputedLines[0][n-1], &psiQ)
   462  
   463  	return PrecomputedLines
   464  }
   465  
   466  // MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop
   467  // but Qᵢ are fixed points in G2 known in advance.
   468  func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter)]LineEvaluationAff) (GT, error) {
   469  	n := len(P)
   470  	if n == 0 || n != len(lines) {
   471  		return GT{}, errors.New("invalid inputs sizes")
   472  	}
   473  
   474  	// no need to filter infinity points:
   475  	// 		1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so
   476  	// 		lines R0 and R1 are 0. At the end it happens that result will stay
   477  	// 		1 through the Miller loop because MulBy34(1,0,0)==1
   478  	// 		Mul34By34(1,0,0,1,0,0)==1 and MulBy01234(1,0,0,0,0)==1.
   479  	//
   480  	// 		2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1
   481  	// 		that are 0 because of gnark-convention (*/0==0) in doubleStep and
   482  	// 		addStep. Similarly to Pᵢ=(0,0) it happens that result stays 1
   483  	// 		throughout the MillerLoop.
   484  
   485  	// precomputations
   486  	yInv := make([]fp.Element, n)
   487  	xNegOverY := make([]fp.Element, n)
   488  	for k := 0; k < n; k++ {
   489  		yInv[k].Set(&P[k].Y)
   490  	}
   491  	yInv = fp.BatchInvert(yInv)
   492  	for k := 0; k < n; k++ {
   493  		xNegOverY[k].Mul(&P[k].X, &yInv[k]).
   494  			Neg(&xNegOverY[k])
   495  	}
   496  
   497  	var result GT
   498  	result.SetOne()
   499  	var prodLines [5]E2
   500  
   501  	// Compute ∏ᵢ { fᵢ_{6x₀+2,Q}(P) }
   502  	if n >= 1 {
   503  		// i = 64, separately to avoid an E12 Square
   504  		// (Square(res) = 1² = 1)
   505  		// LoopCounter[64] = 0
   506  		// k = 0, separately to avoid MulBy34 (res × ℓ)
   507  		// (assign line to res)
   508  
   509  		// line evaluation at P[0] (assign)
   510  		result.C1.B0.MulByElement(&lines[0][0][64].R0, &xNegOverY[0])
   511  		result.C1.B1.MulByElement(&lines[0][0][64].R1, &yInv[0])
   512  		// the coefficient which MulBy34 sets to 1 happens to be already 1 (result = 1)
   513  	}
   514  
   515  	if n >= 2 {
   516  		// k = 1, separately to avoid MulBy34 (res × ℓ)
   517  		// (res is also a line at this point, so we use Mul34By34 ℓ × ℓ)
   518  		// line evaluation at P[1]
   519  		lines[1][0][64].R0.MulByElement(&lines[1][0][64].R0, &xNegOverY[1])
   520  		lines[1][0][64].R1.MulByElement(&lines[1][0][64].R1, &yInv[1])
   521  		// ℓ × res
   522  		prodLines = fptower.Mul34By34(&lines[1][0][64].R0, &lines[1][0][64].R1, &result.C1.B0, &result.C1.B1)
   523  		result.C0.B0 = prodLines[0]
   524  		result.C0.B1 = prodLines[1]
   525  		result.C0.B2 = prodLines[2]
   526  		result.C1.B0 = prodLines[3]
   527  		result.C1.B1 = prodLines[4]
   528  	}
   529  
   530  	// k >= 2
   531  	for k := 2; k < n; k++ {
   532  		// line evaluation at P[k]
   533  		lines[k][0][64].R0.MulByElement(&lines[k][0][64].R0, &xNegOverY[k])
   534  		lines[k][0][64].R1.MulByElement(&lines[k][0][64].R1, &yInv[k])
   535  		// ℓ × res
   536  		result.MulBy34(
   537  			&lines[k][0][64].R0,
   538  			&lines[k][0][64].R1,
   539  		)
   540  	}
   541  
   542  	for i := len(LoopCounter) - 3; i >= 0; i-- {
   543  		// mutualize the square among n Miller loops
   544  		// (∏ᵢfᵢ)²
   545  		result.Square(&result)
   546  
   547  		for k := 0; k < n; k++ {
   548  			// line evaluation at P[k]
   549  			lines[k][0][i].R0.
   550  				MulByElement(
   551  					&lines[k][0][i].R0,
   552  					&xNegOverY[k],
   553  				)
   554  			lines[k][0][i].R1.
   555  				MulByElement(
   556  					&lines[k][0][i].R1,
   557  					&yInv[k],
   558  				)
   559  
   560  			if LoopCounter[i] == 0 {
   561  				// ℓ × res
   562  				result.MulBy34(
   563  					&lines[k][0][i].R0,
   564  					&lines[k][0][i].R1,
   565  				)
   566  			} else {
   567  				// line evaluation at P[k]
   568  				lines[k][1][i].R0.
   569  					MulByElement(
   570  						&lines[k][1][i].R0,
   571  						&xNegOverY[k],
   572  					)
   573  				lines[k][1][i].R1.
   574  					MulByElement(
   575  						&lines[k][1][i].R1,
   576  						&yInv[k],
   577  					)
   578  				// ℓ × ℓ
   579  				prodLines = fptower.Mul34By34(
   580  					&lines[k][0][i].R0, &lines[k][0][i].R1,
   581  					&lines[k][1][i].R0, &lines[k][1][i].R1,
   582  				)
   583  				// (ℓ × ℓ) × res
   584  				result.MulBy01234(&prodLines)
   585  			}
   586  		}
   587  	}
   588  
   589  	// Compute  ∏ᵢ { ℓᵢ_{[6x₀+2]Q,π(Q)}(P) · ℓᵢ_{[6x₀+2]Q+π(Q),-π²(Q)}(P) }
   590  	for k := 0; k < n; k++ {
   591  		// line evaluation at P[k]
   592  		lines[k][1][65].R0.
   593  			MulByElement(
   594  				&lines[k][1][65].R0,
   595  				&xNegOverY[k],
   596  			)
   597  		lines[k][1][65].R1.
   598  			MulByElement(
   599  				&lines[k][1][65].R1,
   600  				&yInv[k],
   601  			)
   602  		// line evaluation at P[k]
   603  		lines[k][0][65].R0.
   604  			MulByElement(
   605  				&lines[k][0][65].R0,
   606  				&xNegOverY[k],
   607  			)
   608  		lines[k][0][65].R1.
   609  			MulByElement(
   610  				&lines[k][0][65].R1,
   611  				&yInv[k],
   612  			)
   613  		// ℓ × ℓ
   614  		prodLines = fptower.Mul34By34(
   615  			&lines[k][1][65].R0, &lines[k][1][65].R1,
   616  			&lines[k][0][65].R0, &lines[k][0][65].R1,
   617  		)
   618  		// (ℓ × ℓ) × res
   619  		result.MulBy01234(&prodLines)
   620  	}
   621  
   622  	return result, nil
   623  }
   624  
   625  func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) {
   626  
   627  	var n, d, λ, xr, yr fptower.E2
   628  	// λ = 3x²/2y
   629  	n.Square(&p.X)
   630  	λ.Double(&n).
   631  		Add(&λ, &n)
   632  	d.Double(&p.Y)
   633  	λ.Div(&λ, &d)
   634  
   635  	// xr = λ²-2x
   636  	xr.Square(&λ).
   637  		Sub(&xr, &p.X).
   638  		Sub(&xr, &p.X)
   639  
   640  	// yr = λ(x-xr)-y
   641  	yr.Sub(&p.X, &xr).
   642  		Mul(&yr, &λ).
   643  		Sub(&yr, &p.Y)
   644  
   645  	evaluations.R0.Set(&λ)
   646  	evaluations.R1.Mul(&λ, &p.X).
   647  		Sub(&evaluations.R1, &p.Y)
   648  
   649  	p.X.Set(&xr)
   650  	p.Y.Set(&yr)
   651  }
   652  
   653  func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) {
   654  	var n, d, λ, λλ, xr, yr fptower.E2
   655  
   656  	// compute λ = (y2-y1)/(x2-x1)
   657  	n.Sub(&a.Y, &p.Y)
   658  	d.Sub(&a.X, &p.X)
   659  	λ.Div(&n, &d)
   660  
   661  	// xr = λ²-x1-x2
   662  	λλ.Square(&λ)
   663  	n.Add(&p.X, &a.X)
   664  	xr.Sub(&λλ, &n)
   665  
   666  	// yr = λ(x1-xr) - y1
   667  	yr.Sub(&p.X, &xr).
   668  		Mul(&yr, &λ).
   669  		Sub(&yr, &p.Y)
   670  
   671  	evaluations.R0.Set(&λ)
   672  	evaluations.R1.Mul(&λ, &p.X).
   673  		Sub(&evaluations.R1, &p.Y)
   674  
   675  	p.X.Set(&xr)
   676  	p.Y.Set(&yr)
   677  }
   678  
   679  func (p *G2Affine) doubleAndAddStep(evaluations1, evaluations2 *LineEvaluationAff, a *G2Affine) {
   680  	var n, d, l1, x3, l2, x4, y4 fptower.E2
   681  
   682  	// compute λ1 = (y2-y1)/(x2-x1)
   683  	n.Sub(&p.Y, &a.Y)
   684  	d.Sub(&p.X, &a.X)
   685  	l1.Div(&n, &d)
   686  
   687  	// compute x3 =λ1²-x1-x2
   688  	x3.Square(&l1)
   689  	x3.Sub(&x3, &p.X)
   690  	x3.Sub(&x3, &a.X)
   691  
   692  	// omit y3 computation
   693  
   694  	// compute line1
   695  	evaluations1.R0.Set(&l1)
   696  	evaluations1.R1.Mul(&l1, &p.X)
   697  	evaluations1.R1.Sub(&evaluations1.R1, &p.Y)
   698  
   699  	// compute λ2 = -λ1-2y1/(x3-x1)
   700  	n.Double(&p.Y)
   701  	d.Sub(&x3, &p.X)
   702  	l2.Div(&n, &d)
   703  	l2.Add(&l2, &l1)
   704  	l2.Neg(&l2)
   705  
   706  	// compute x4 = λ2²-x1-x3
   707  	x4.Square(&l2)
   708  	x4.Sub(&x4, &p.X)
   709  	x4.Sub(&x4, &x3)
   710  
   711  	// compute y4 = λ2(x1 - x4)-y1
   712  	y4.Sub(&p.X, &x4)
   713  	y4.Mul(&l2, &y4)
   714  	y4.Sub(&y4, &p.Y)
   715  
   716  	// compute line2
   717  	evaluations2.R0.Set(&l2)
   718  	evaluations2.R1.Mul(&l2, &p.X)
   719  	evaluations2.R1.Sub(&evaluations2.R1, &p.Y)
   720  
   721  	p.X.Set(&x4)
   722  	p.Y.Set(&y4)
   723  }