|
| 1 | + |
| 2 | +def _add(E, P, Q): |
| 3 | + r""" |
| 4 | + Addition formulas for elliptic curves over general rings |
| 5 | + with trivial Picard group. |
| 6 | +
|
| 7 | + This function returns a generator which yields tuples of projective |
| 8 | + coordinates. Some linear combination of those coordinate tuples is |
| 9 | + guaranteed to form a valid projective point. |
| 10 | +
|
| 11 | + .. NOTE:: |
| 12 | +
|
| 13 | + This function is for internal use. To add two elliptic-curve |
| 14 | + points, users should simply use the `+` operator. |
| 15 | +
|
| 16 | + REFERENCES: |
| 17 | +
|
| 18 | + These formulas were derived by Bosma and Lenstra [BL1995]_, |
| 19 | + with corrections by Best [Best2021]_ (Appendix A). |
| 20 | +
|
| 21 | + EXAMPLES:: |
| 22 | +
|
| 23 | + sage: from sage.schemes.elliptic_curves.addition_formulas_ring import _add |
| 24 | + sage: M = Zmod(13*17*19) |
| 25 | + sage: R.<U,V> = M[] |
| 26 | + sage: S.<u,v> = R.quotient(U*V - 17) |
| 27 | + sage: E = EllipticCurve(S, [1,2,3,4,5]) |
| 28 | + sage: P = E(817, 13, 19) |
| 29 | + sage: Q = E(425, 123, 17) |
| 30 | + sage: PQ1, PQ2 = _add(E, P, Q) |
| 31 | + sage: PQ1 |
| 32 | + (1188, 1674, 540) |
| 33 | + sage: PQ2 |
| 34 | + (582, 2347, 1028) |
| 35 | + sage: E(PQ1) == E(PQ2) |
| 36 | + True |
| 37 | +
|
| 38 | + TESTS: |
| 39 | +
|
| 40 | + We ensure that these formulas return the same result as the ones over a field:: |
| 41 | +
|
| 42 | + sage: from sage.schemes.elliptic_curves.addition_formulas_ring import _add |
| 43 | + sage: F = GF(2^127-1) |
| 44 | + sage: E = EllipticCurve(j=F.random_element()) |
| 45 | + sage: E = choice(E.twists()) |
| 46 | + sage: P = E.random_point() |
| 47 | + sage: Q = E.random_point() |
| 48 | + sage: PQ1, PQ2 = _add(E, P, Q) |
| 49 | + sage: assert E(*PQ1) == P + Q |
| 50 | + sage: assert E(*PQ2) == P + Q |
| 51 | + """ |
| 52 | + a1, a2, a3, a4, a6 = E.a_invariants() |
| 53 | + b2, b4, b6, b8 = E.b_invariants() |
| 54 | + |
| 55 | + if P not in E: |
| 56 | + raise ValueError('P must be in E') |
| 57 | + if Q not in E: |
| 58 | + raise ValueError('Q must be in E') |
| 59 | + X1, Y1, Z1 = P |
| 60 | + X2, Y2, Z2 = Q |
| 61 | + |
| 62 | + # TODO: I've made a half-hearted attempt at simplifying the formulas |
| 63 | + # by caching common subexpressions. This could almost certainly be |
| 64 | + # sped up significantly with some more serious optimization effort. |
| 65 | + |
| 66 | + XYdif = X1*Y2 - X2*Y1 |
| 67 | + XYsum = X1*Y2 + X2*Y1 |
| 68 | + XZdif = X1*Z2 - X2*Z1 |
| 69 | + XZsum = X1*Z2 + X2*Z1 |
| 70 | + YZdif = Y1*Z2 - Y2*Z1 |
| 71 | + YZsum = Y1*Z2 + Y2*Z1 |
| 72 | + |
| 73 | + a1sq, a2sq, a3sq, a4sq = (a**2 for a in (a1, a2, a3, a4)) |
| 74 | + |
| 75 | + X31 = XYdif*YZsum+XZdif*Y1*Y2+a1*X1*X2*YZdif+a1*XYdif*XZsum-a2*X1*X2*XZdif+a3*XYdif*Z1*Z2+a3*XZdif*YZsum-a4*XZsum*XZdif-3*a6*XZdif*Z1*Z2 |
| 76 | + |
| 77 | + Y31 = -3*X1*X2*XYdif-Y1*Y2*YZdif-2*a1*XZdif*Y1*Y2+(a1sq+3*a2)*X1*X2*YZdif-(a1sq+a2)*XYsum*XZdif+(a1*a2-3*a3)*X1*X2*XZdif-(2*a1*a3+a4)*XYdif*Z1*Z2+a4*XZsum*YZdif+(a1*a4-a2*a3)*XZsum*XZdif+(a3sq+3*a6)*YZdif*Z1*Z2+(3*a1*a6-a3*a4)*XZdif*Z1*Z2 |
| 78 | + |
| 79 | + Z31 = 3*X1*X2*XZdif-YZsum*YZdif+a1*XYdif*Z1*Z2-a1*XZdif*YZsum+a2*XZsum*XZdif-a3*YZdif*Z1*Z2+a4*XZdif*Z1*Z2 |
| 80 | + |
| 81 | + yield (X31, Y31, Z31) |
| 82 | + |
| 83 | + X32 = Y1*Y2*XYsum+a1*(2*X1*Y2+X2*Y1)*X2*Y1+a1sq*X1*X2**2*Y1-a2*X1*X2*XYsum-a1*a2*X1**2*X2**2+a3*X2*Y1*(YZsum+Y2*Z1)+a1*a3*X1*X2*YZdif-a1*a3*XYsum*XZdif-a4*X1*X2*YZsum-a4*XYsum*XZsum-a1sq*a3*X1**2*X2*Z2-a1*a4*X1*X2*(X1*Z2+XZsum)-a2*a3*X1*X2**2*Z1-a3sq*X1*Z2*(Y2*Z1+YZsum)-3*a6*XYsum*Z1*Z2-3*a6*XZsum*YZsum-a1*a3sq*X1*Z2*(XZsum+X2*Z1)-3*a1*a6*X1*Z2*(XZsum+X2*Z1)-a3*a4*(X1*Z2+XZsum)*X2*Z1-b8*YZsum*Z1*Z2-a1*b8*X1*Z1*Z2**2-a3**3*XZsum*Z1*Z2-3*a3*a6*(XZsum+X2*Z1)*Z1*Z2-a3*b8*Z1**2*Z2**2 |
| 84 | + |
| 85 | + Y32 = Y1**2*Y2**2+a1*X2*Y1**2*Y2+(a1*a2-3*a3)*X1*X2**2*Y1+a3*Y1**2*Y2*Z2-(a2sq-3*a4)*X1**2*X2**2+(a1*a4-a2*a3)*(2*X1*Z2+X2*Z1)*X2*Y1+(a1sq*a4-2*a1*a2*a3+3*a3sq)*X1**2*X2*Z2-(a2*a4-9*a6)*X1*X2*XZsum+(3*a1*a6-a3*a4)*(XZsum+X2*Z1)*Y1*Z2+(3*a1sq*a6-2*a1*a3*a4+a2*a3sq+3*a2*a6-a4sq)*X1*Z2*(XZsum+X2*Z1)+(3*a2*a6-a4sq)*X2*Z1*(2*X1*Z2+Z1*X2)+(a1**3*a6-a1sq*a3*a4+a1*a2*a3sq-a1*a4sq+4*a1*a2*a6-a3**3-3*a3*a6)*Y1*Z1*Z2**2+(a1**4*a6-a1**3*a3*a4+5*a1sq*a2*a6+a1sq*a2*a3sq-a1*a2*a3*a4-a1*a3**3-3*a1*a3*a6-a1sq*a4sq+a2sq*a3sq-a2*a4sq+4*a2sq*a6-a3**2*a4-3*a4*a6)*X1*Z1*Z2**2+(a1sq*a2*a6-a1*a2*a3*a4+3*a1*a3*a6+a2sq*a3sq-a2*a4sq+4*a2sq*a6-2*a3sq*a4-3*a4*a6)*X2*Z1**2*Z2+(a1**3*a3*a6-a1sq*a3sq*a4+a1sq*a4*a6+a1*a2*a3**3+4*a1*a2*a3*a6-2*a1*a3*a4sq+a2*a3sq*a4+4*a2*a4*a6-a3**4-6*a3**2*a6-a4**3-9*a6**2)*Z1**2*Z2**2 |
| 86 | + |
| 87 | + Z32 = 3*X1*X2*XYsum+Y1*Y2*YZsum+3*a1*X1**2*X2**2+a1*(2*X1*Y2+Y1*X2)*Y1*Z2+a1sq*X1*Z2*(2*X2*Y1+X1*Y2)+a2*X1*X2*YZsum+a2*XYsum*XZsum+a1**3*X1**2*X2*Z2+a1*a2*X1*X2*(2*X1*Z2+X2*Z1)+3*a3*X1*X2**2*Z1+a3*Y1*Z2*(YZsum+Y2*Z1)+2*a1*a3*X1*Z2*YZsum+2*a1*a3*X2*Y1*Z1*Z2+a4*XYsum*Z1*Z2+a4*XZsum*YZsum+(a1sq*a3+a1*a4)*X1*Z2*(XZsum+X2*Z1)+a2*a3*X2*Z1*(2*X1*Z2+X2*Z1)+a3sq*Y1*Z1*Z2**2+(a3sq+3*a6)*YZsum*Z1*Z2+a1*a3sq*(2*X1*Z2+X2*Z1)*Z1*Z2+3*a1*a6*X1*Z1*Z2**2+a3*a4*(XZsum+X2*Z1)*Z1*Z2+(a3**3+3*a3*a6)*Z1**2*Z2**2 |
| 88 | + |
| 89 | + yield (X32, Y32, Z32) |
0 commit comments