github.com/consensys/gnark-crypto@v0.14.0/ecc/bls24-315/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 bls24315
    16  
    17  import (
    18  	"errors"
    19  
    20  	"github.com/consensys/gnark-crypto/ecc/bls24-315/fp"
    21  	"github.com/consensys/gnark-crypto/ecc/bls24-315/internal/fptower"
    22  )
    23  
    24  // GT target group of the pairing
    25  type GT = fptower.E24
    26  
    27  type lineEvaluation struct {
    28  	r0 fptower.E4
    29  	r1 fptower.E4
    30  	r2 fptower.E4
    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 3 (Hayashida et al.)
    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 [9]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.FrobeniusQuad(&t[0]).
    80  		Mul(&result, &t[0])
    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  	// Daiki Hayashida and Kenichiro Hayasaka
    90  	// and Tadanori Teruya
    91  	// https://eprint.iacr.org/2020/875.pdf
    92  	// 3(p⁸ - p⁴ +1)/r = (x₀-1)² * (x₀+p) * (x₀²+p²) * (x₀⁴+p⁴-1) + 3
    93  	t[0].CyclotomicSquare(&result)
    94  	t[1].Expt(&result)
    95  	t[2].InverseUnitary(&result)
    96  	t[1].Mul(&t[1], &t[2])
    97  	t[2].Expt(&t[1])
    98  	t[1].InverseUnitary(&t[1])
    99  	t[1].Mul(&t[1], &t[2])
   100  	t[2].Expt(&t[1])
   101  	t[1].Frobenius(&t[1])
   102  	t[1].Mul(&t[1], &t[2])
   103  	result.Mul(&result, &t[0])
   104  	t[0].Expt(&t[1])
   105  	t[2].Expt(&t[0])
   106  	t[0].FrobeniusSquare(&t[1])
   107  	t[2].Mul(&t[0], &t[2])
   108  	t[1].Expt(&t[2])
   109  	t[1].Expt(&t[1])
   110  	t[1].Expt(&t[1])
   111  	t[1].Expt(&t[1])
   112  	t[0].FrobeniusQuad(&t[2])
   113  	t[0].Mul(&t[0], &t[1])
   114  	t[2].InverseUnitary(&t[2])
   115  	t[0].Mul(&t[0], &t[2])
   116  	result.Mul(&result, &t[0])
   117  
   118  	return result
   119  }
   120  
   121  // MillerLoop computes the multi-Miller loop
   122  // ∏ᵢ MillerLoop(Pᵢ, Qᵢ)
   123  func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) {
   124  	// check input size match
   125  	n := len(P)
   126  	if n == 0 || n != len(Q) {
   127  		return GT{}, errors.New("invalid inputs sizes")
   128  	}
   129  
   130  	// filter infinity points
   131  	p := make([]G1Affine, 0, n)
   132  	q := make([]G2Affine, 0, n)
   133  
   134  	for k := 0; k < n; k++ {
   135  		if P[k].IsInfinity() || Q[k].IsInfinity() {
   136  			continue
   137  		}
   138  		p = append(p, P[k])
   139  		q = append(q, Q[k])
   140  	}
   141  
   142  	n = len(p)
   143  
   144  	// projective points for Q
   145  	qProj := make([]g2Proj, n)
   146  	qNeg := make([]G2Affine, n)
   147  	for k := 0; k < n; k++ {
   148  		qProj[k].FromAffine(&q[k])
   149  		qNeg[k].Neg(&q[k])
   150  	}
   151  
   152  	var result GT
   153  	result.SetOne()
   154  	var l1, l2 lineEvaluation
   155  	var prodLines [5]fptower.E4
   156  
   157  	// Compute ∏ᵢ { fᵢ_{x₀,Q}(P) }
   158  	if n >= 1 {
   159  		// i = 31, separately to avoid an E12 Square
   160  		// (Square(res) = 1² = 1)
   161  		// LoopCounter[31] = 0
   162  		// k = 0, separately to avoid MulBy034 (res × ℓ)
   163  		// (assign line to res)
   164  
   165  		// qProj[0] ← 2qProj[0] and l1 the tangent ℓ passing 2qProj[0]	qProj[0].doubleStep(&l1)
   166  		qProj[0].doubleStep(&l1)
   167  		// line evaluation at P[0] (assign)
   168  		result.D0.C0.MulByElement(&l1.r0, &p[0].Y)
   169  		result.D1.C0.MulByElement(&l1.r1, &p[0].X)
   170  		result.D1.C1.Set(&l1.r2)
   171  	}
   172  
   173  	if n >= 2 {
   174  		// k = 1, separately to avoid MulBy034 (res × ℓ)
   175  		// (res is also a line at this point, so we use Mul034By034 ℓ × ℓ)
   176  
   177  		// qProj[1] ← 2qProj[1] and l1 the tangent ℓ passing 2qProj[1]
   178  		qProj[1].doubleStep(&l1)
   179  		// line evaluation at P[1]
   180  		l1.r0.MulByElement(&l1.r0, &p[1].Y)
   181  		l1.r1.MulByElement(&l1.r1, &p[1].X)
   182  		// ℓ × res
   183  		prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &result.D0.C0, &result.D1.C0, &result.D1.C1)
   184  		result.D0.C0 = prodLines[0]
   185  		result.D0.C1 = prodLines[1]
   186  		result.D0.C2 = prodLines[2]
   187  		result.D1.C0 = prodLines[3]
   188  		result.D1.C1 = prodLines[4]
   189  	}
   190  
   191  	// k >= 2
   192  	for k := 2; k < n; k++ {
   193  		// qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k]
   194  		qProj[k].doubleStep(&l1)
   195  		// line evaluation at P[k]
   196  		l1.r0.MulByElement(&l1.r0, &p[k].Y)
   197  		l1.r1.MulByElement(&l1.r1, &p[k].X)
   198  		// ℓ × res
   199  		result.MulBy034(&l1.r0, &l1.r1, &l1.r2)
   200  	}
   201  
   202  	// i <= 30
   203  	for i := len(LoopCounter) - 3; i >= 1; i-- {
   204  		// (∏ᵢfᵢ)²
   205  		// mutualize the square among n Miller loops
   206  		result.Square(&result)
   207  
   208  		for k := 0; k < n; k++ {
   209  			// qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k]
   210  			qProj[k].doubleStep(&l1)
   211  			// line evaluation at P[k]
   212  			l1.r0.MulByElement(&l1.r0, &p[k].Y)
   213  			l1.r1.MulByElement(&l1.r1, &p[k].X)
   214  
   215  			if LoopCounter[i] == 0 {
   216  				// ℓ × result
   217  				result.MulBy034(&l1.r0, &l1.r1, &l1.r2)
   218  			} else if LoopCounter[i] == 1 {
   219  				// qProj[k] ← qProj[k]+Q[k] and
   220  				// l2 the line ℓ passing qProj[k] and Q[k]
   221  				qProj[k].addMixedStep(&l2, &q[k])
   222  				// line evaluation at P[k]
   223  				l2.r0.MulByElement(&l2.r0, &p[k].Y)
   224  				l2.r1.MulByElement(&l2.r1, &p[k].X)
   225  				// ℓ × ℓ
   226  				prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2)
   227  				// (ℓ × ℓ) × result
   228  				result.MulBy01234(&prodLines)
   229  			} else if LoopCounter[i] == -1 {
   230  				// qProj[k] ← qProj[k]-Q[k] and
   231  				// l2 the line ℓ passing qProj[k] and -Q[k]
   232  				qProj[k].addMixedStep(&l2, &qNeg[k])
   233  				// line evaluation at P[k]
   234  				l2.r0.MulByElement(&l2.r0, &p[k].Y)
   235  				l2.r1.MulByElement(&l2.r1, &p[k].X)
   236  				// ℓ × ℓ
   237  				prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2)
   238  				// (ℓ × ℓ) × result
   239  				result.MulBy01234(&prodLines)
   240  			}
   241  		}
   242  	}
   243  
   244  	// i = 0, separately to avoid a point addition
   245  	// LoopCounter[0] = 1
   246  	result.Square(&result)
   247  	for k := 0; k < n; k++ {
   248  		// qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k]
   249  		qProj[k].doubleStep(&l1)
   250  		// line evaluation at P[k]
   251  		l1.r0.MulByElement(&l1.r0, &p[k].Y)
   252  		l1.r1.MulByElement(&l1.r1, &p[k].X)
   253  
   254  		// l2 the line passing qProj[k] and Q
   255  		qProj[k].lineCompute(&l2, &qNeg[k])
   256  		// line evaluation at P[k]
   257  		l2.r0.MulByElement(&l2.r0, &p[k].Y)
   258  		l2.r1.MulByElement(&l2.r1, &p[k].X)
   259  		// ℓ × ℓ
   260  		prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2)
   261  		// (ℓ × ℓ) × res
   262  		result.MulBy01234(&prodLines)
   263  	}
   264  
   265  	// negative x₀
   266  	result.Conjugate(&result)
   267  
   268  	return result, nil
   269  }
   270  
   271  // doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop
   272  // https://eprint.iacr.org/2013/722.pdf (Section 4.3)
   273  func (p *g2Proj) doubleStep(evaluations *lineEvaluation) {
   274  
   275  	// get some Element from our pool
   276  	var t1, A, B, C, D, E, EE, F, G, H, I, J, K fptower.E4
   277  	A.Mul(&p.x, &p.y)
   278  	A.Halve()
   279  	B.Square(&p.y)
   280  	C.Square(&p.z)
   281  	D.Double(&C).
   282  		Add(&D, &C)
   283  	E.MulBybTwistCurveCoeff(&D)
   284  	F.Double(&E).
   285  		Add(&F, &E)
   286  	G.Add(&B, &F)
   287  	G.Halve()
   288  	H.Add(&p.y, &p.z).
   289  		Square(&H)
   290  	t1.Add(&B, &C)
   291  	H.Sub(&H, &t1)
   292  	I.Sub(&E, &B)
   293  	J.Square(&p.x)
   294  	EE.Square(&E)
   295  	K.Double(&EE).
   296  		Add(&K, &EE)
   297  
   298  	// X, Y, Z
   299  	p.x.Sub(&B, &F).
   300  		Mul(&p.x, &A)
   301  	p.y.Square(&G).
   302  		Sub(&p.y, &K)
   303  	p.z.Mul(&B, &H)
   304  
   305  	// Line evaluation
   306  	evaluations.r0.Neg(&H)
   307  	evaluations.r1.Double(&J).
   308  		Add(&evaluations.r1, &J)
   309  	evaluations.r2.Set(&I)
   310  }
   311  
   312  // addMixedStep point addition in Mixed Homogenous projective and Affine coordinates
   313  // https://eprint.iacr.org/2013/722.pdf (Section 4.3)
   314  func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) {
   315  
   316  	// get some Element from our pool
   317  	var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fptower.E4
   318  	Y2Z1.Mul(&a.Y, &p.z)
   319  	O.Sub(&p.y, &Y2Z1)
   320  	X2Z1.Mul(&a.X, &p.z)
   321  	L.Sub(&p.x, &X2Z1)
   322  	C.Square(&O)
   323  	D.Square(&L)
   324  	E.Mul(&L, &D)
   325  	F.Mul(&p.z, &C)
   326  	G.Mul(&p.x, &D)
   327  	t0.Double(&G)
   328  	H.Add(&E, &F).
   329  		Sub(&H, &t0)
   330  	t1.Mul(&p.y, &E)
   331  
   332  	// X, Y, Z
   333  	p.x.Mul(&L, &H)
   334  	p.y.Sub(&G, &H).
   335  		Mul(&p.y, &O).
   336  		Sub(&p.y, &t1)
   337  	p.z.Mul(&E, &p.z)
   338  
   339  	t2.Mul(&L, &a.Y)
   340  	J.Mul(&a.X, &O).
   341  		Sub(&J, &t2)
   342  
   343  	// Line evaluation
   344  	evaluations.r0.Set(&L)
   345  	evaluations.r1.Neg(&O)
   346  	evaluations.r2.Set(&J)
   347  }
   348  
   349  // lineCompute computes the line through p in Homogenous projective coordinates
   350  // and a in affine coordinates. It does not compute the resulting point p+a.
   351  func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) {
   352  
   353  	// get some Element from our pool
   354  	var Y2Z1, X2Z1, O, L, t2, J fptower.E4
   355  	Y2Z1.Mul(&a.Y, &p.z)
   356  	O.Sub(&p.y, &Y2Z1)
   357  	X2Z1.Mul(&a.X, &p.z)
   358  	L.Sub(&p.x, &X2Z1)
   359  	t2.Mul(&L, &a.Y)
   360  	J.Mul(&a.X, &O).
   361  		Sub(&J, &t2)
   362  
   363  	// Line evaluation
   364  	evaluations.r0.Set(&L)
   365  	evaluations.r1.Neg(&O)
   366  	evaluations.r2.Set(&J)
   367  }
   368  
   369  // ----------------------
   370  // Fixed-argument pairing
   371  // ----------------------
   372  
   373  type LineEvaluationAff struct {
   374  	R0 fptower.E4
   375  	R1 fptower.E4
   376  }
   377  
   378  // PairFixedQ calculates the reduced pairing for a set of points
   379  // ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2.
   380  //
   381  // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup.
   382  func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) {
   383  	f, err := MillerLoopFixedQ(P, lines)
   384  	if err != nil {
   385  		return GT{}, err
   386  	}
   387  	return FinalExponentiation(&f), nil
   388  }
   389  
   390  // PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One
   391  // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2.
   392  //
   393  // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup.
   394  func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (bool, error) {
   395  	f, err := PairFixedQ(P, lines)
   396  	if err != nil {
   397  		return false, err
   398  	}
   399  	var one GT
   400  	one.SetOne()
   401  	return f.Equal(&one), nil
   402  }
   403  
   404  // PrecomputeLines precomputes the lines for the fixed-argument Miller loop
   405  func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter) - 1]LineEvaluationAff) {
   406  	var accQ, negQ G2Affine
   407  	accQ.Set(&Q)
   408  	negQ.Neg(&Q)
   409  
   410  	n := len(LoopCounter)
   411  	for i := n - 2; i >= 0; i-- {
   412  		accQ.doubleStep(&PrecomputedLines[0][i])
   413  		if LoopCounter[i] == 1 {
   414  			accQ.addStep(&PrecomputedLines[1][i], &Q)
   415  		} else if LoopCounter[i] == -1 {
   416  			accQ.addStep(&PrecomputedLines[1][i], &negQ)
   417  		} else {
   418  			continue
   419  		}
   420  	}
   421  	return PrecomputedLines
   422  }
   423  
   424  // MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop
   425  // but Qᵢ are fixed points in G2 known in advance.
   426  func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) {
   427  	n := len(P)
   428  	if n == 0 || n != len(lines) {
   429  		return GT{}, errors.New("invalid inputs sizes")
   430  	}
   431  
   432  	// no need to filter infinity points:
   433  	// 		1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so
   434  	// 		lines R0 and R1 are 0. At the end it happens that result will stay
   435  	// 		1 through the Miller loop because MulBy34(1,0,0)==1
   436  	// 		Mul34By34(1,0,0,1,0,0)==1 and MulBy01234(1,0,0,0,0)==1.
   437  	//
   438  	// 		2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1
   439  	// 		that are 0 because of gnark-convention (*/0==0) in doubleStep and
   440  	// 		addStep. Similarly to Pᵢ=(0,0) it happens that result stays 1
   441  	// 		throughout the MillerLoop.
   442  
   443  	// precomputations
   444  	yInv := make([]fp.Element, n)
   445  	xNegOverY := make([]fp.Element, n)
   446  	for k := 0; k < n; k++ {
   447  		yInv[k].Set(&P[k].Y)
   448  	}
   449  	yInv = fp.BatchInvert(yInv)
   450  	for k := 0; k < n; k++ {
   451  		xNegOverY[k].Mul(&P[k].X, &yInv[k]).
   452  			Neg(&xNegOverY[k])
   453  	}
   454  
   455  	var result GT
   456  	result.SetOne()
   457  	var prodLines [5]E4
   458  
   459  	// Compute ∏ᵢ { fᵢ_{x₀,Q}(P) }
   460  	for i := len(LoopCounter) - 2; i >= 0; i-- {
   461  		// mutualize the square among n Miller loops
   462  		// (∏ᵢfᵢ)²
   463  		result.Square(&result)
   464  
   465  		for k := 0; k < n; k++ {
   466  			// line evaluation at P[k]
   467  			lines[k][0][i].R0.
   468  				MulByElement(
   469  					&lines[k][0][i].R0,
   470  					&xNegOverY[k],
   471  				)
   472  			lines[k][0][i].R1.
   473  				MulByElement(
   474  					&lines[k][0][i].R1,
   475  					&yInv[k],
   476  				)
   477  
   478  			if LoopCounter[i] == 0 {
   479  				// ℓ × res
   480  				result.MulBy34(
   481  					&lines[k][0][i].R0,
   482  					&lines[k][0][i].R1,
   483  				)
   484  			} else {
   485  				// line evaluation at P[k]
   486  				lines[k][1][i].R0.
   487  					MulByElement(
   488  						&lines[k][1][i].R0,
   489  						&xNegOverY[k],
   490  					)
   491  				lines[k][1][i].R1.
   492  					MulByElement(
   493  						&lines[k][1][i].R1,
   494  						&yInv[k],
   495  					)
   496  				// ℓ × ℓ
   497  				prodLines = fptower.Mul34By34(
   498  					&lines[k][0][i].R0, &lines[k][0][i].R1,
   499  					&lines[k][1][i].R0, &lines[k][1][i].R1,
   500  				)
   501  				// (ℓ × ℓ) × res
   502  				result.MulBy01234(&prodLines)
   503  			}
   504  		}
   505  	}
   506  
   507  	// negative x₀
   508  	result.Conjugate(&result)
   509  
   510  	return result, nil
   511  }
   512  
   513  func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) {
   514  
   515  	var n, d, λ, xr, yr fptower.E4
   516  	// λ = 3x²/2y
   517  	n.Square(&p.X)
   518  	λ.Double(&n).
   519  		Add(&λ, &n)
   520  	d.Double(&p.Y)
   521  	λ.Div(&λ, &d)
   522  
   523  	// xr = λ²-2x
   524  	xr.Square(&λ).
   525  		Sub(&xr, &p.X).
   526  		Sub(&xr, &p.X)
   527  
   528  	// yr = λ(x-xr)-y
   529  	yr.Sub(&p.X, &xr).
   530  		Mul(&yr, &λ).
   531  		Sub(&yr, &p.Y)
   532  
   533  	evaluations.R0.Set(&λ)
   534  	evaluations.R1.Mul(&λ, &p.X).
   535  		Sub(&evaluations.R1, &p.Y)
   536  
   537  	p.X.Set(&xr)
   538  	p.Y.Set(&yr)
   539  }
   540  
   541  func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) {
   542  	var n, d, λ, λλ, xr, yr fptower.E4
   543  
   544  	// compute λ = (y2-y1)/(x2-x1)
   545  	n.Sub(&a.Y, &p.Y)
   546  	d.Sub(&a.X, &p.X)
   547  	λ.Div(&n, &d)
   548  
   549  	// xr = λ²-x1-x2
   550  	λλ.Square(&λ)
   551  	n.Add(&p.X, &a.X)
   552  	xr.Sub(&λλ, &n)
   553  
   554  	// yr = λ(x1-xr) - y1
   555  	yr.Sub(&p.X, &xr).
   556  		Mul(&yr, &λ).
   557  		Sub(&yr, &p.Y)
   558  
   559  	evaluations.R0.Set(&λ)
   560  	evaluations.R1.Mul(&λ, &p.X).
   561  		Sub(&evaluations.R1, &p.Y)
   562  
   563  	p.X.Set(&xr)
   564  	p.Y.Set(&yr)
   565  }