1 // -*- mode: rust; -*-
2 //
3 // This file is part of curve25519-dalek.
4 // Copyright (c) 2016-2019 Isis Lovecruft, Henry de Valence
5 // See LICENSE for licensing information.
6 //
7 // Authors:
8 // - Isis Agora Lovecruft <isis@patternsinthevoid.net>
9 // - Henry de Valence <hdevalence@hdevalence.ca>
10 
11 //! Internal curve representations which are not part of the public API.
12 //!
13 //! # Curve representations
14 //!
15 //! Internally, we use several different models for the curve.  Here
16 //! is a sketch of the relationship between the models, following [a
17 //! post][smith-moderncrypto]
18 //! by Ben Smith on the `moderncrypto` mailing list.  This is also briefly
19 //! discussed in section 2.5 of [_Montgomery curves and their
20 //! arithmetic_][costello-smith-2017] by Costello and Smith.
21 //!
22 //! Begin with the affine equation for the curve,
23 //! $$
24 //!     -x\^2 + y\^2 = 1 + dx\^2y\^2.
25 //! $$
26 //! Next, pass to the projective closure \\(\mathbb P\^1 \times \mathbb
27 //! P\^1 \\) by setting \\(x=X/Z\\), \\(y=Y/T.\\)  Clearing denominators
28 //! gives the model
29 //! $$
30 //!     -X\^2T\^2 + Y\^2Z\^2 = Z\^2T\^2 + dX\^2Y\^2.
31 //! $$
32 //! In `curve25519-dalek`, this is represented as the `CompletedPoint`
33 //! struct.
34 //! To map from \\(\mathbb P\^1 \times \mathbb P\^1 \\), a product of
35 //! two lines, to \\(\mathbb P\^3\\), we use the [Segre
36 //! embedding](https://en.wikipedia.org/wiki/Segre_embedding)
37 //! $$
38 //!     \sigma : ((X:Z),(Y:T)) \mapsto (XY:XT:ZY:ZT).
39 //! $$
40 //! Using coordinates \\( (W_0:W_1:W_2:W_3) \\) for \\(\mathbb P\^3\\),
41 //! the image \\(\sigma (\mathbb P\^1 \times \mathbb P\^1) \\) is the
42 //! surface defined by \\( W_0 W_3 = W_1 W_2 \\), and under \\(
43 //! \sigma\\), the equation above becomes
44 //! $$
45 //!     -W\_1\^2 + W\_2\^2 = W\_3\^2 + dW\_0\^2,
46 //! $$
47 //! so that the curve is given by the pair of equations
48 //! $$
49 //! \begin{aligned}
50 //!     -W\_1\^2 + W\_2\^2 &= W\_3\^2 + dW\_0\^2, \\\\  W_0 W_3 &= W_1 W_2.
51 //! \end{aligned}
52 //! $$
53 //! Up to variable naming, this is exactly the "extended" curve model
54 //! introduced in [_Twisted Edwards Curves
55 //! Revisited_][hisil-wong-carter-dawson-2008] by Hisil, Wong, Carter,
56 //! and Dawson.  In `curve25519-dalek`, it is represented as the
57 //! `EdwardsPoint` struct.  We can map from \\(\mathbb P\^3 \\) to
58 //! \\(\mathbb P\^2 \\) by sending \\( (W\_0:W\_1:W\_2:W\_3) \\) to \\(
59 //! (W\_1:W\_2:W\_3) \\).  Notice that
60 //! $$
61 //!     \frac {W\_1} {W\_3} = \frac {XT} {ZT} = \frac X Z = x,
62 //! $$
63 //! and
64 //! $$
65 //!     \frac {W\_2} {W\_3} = \frac {YZ} {ZT} = \frac Y T = y,
66 //! $$
67 //! so this is the same as if we had started with the affine model
68 //! and passed to \\( \mathbb P\^2 \\) by setting \\( x = W\_1 / W\_3
69 //! \\), \\(y = W\_2 / W\_3 \\).
70 //! Up to variable naming, this is the projective representation
71 //! introduced in in [_Twisted Edwards
72 //! Curves_][bernstein-birkner-joye-lange-peters-2008] by Bernstein,
73 //! Birkner, Joye, Lange, and Peters.  In `curve25519-dalek`, it is
74 //! represented by the `ProjectivePoint` struct.
75 //!
76 //! # Passing between curve models
77 //!
78 //! Although the \\( \mathbb P\^3 \\) model provides faster addition
79 //! formulas, the \\( \mathbb P\^2 \\) model provides faster doubling
80 //! formulas.  Hisil, Wong, Carter, and Dawson therefore suggest mixing
81 //! coordinate systems for scalar multiplication, attributing the idea
82 //! to [a 1998 paper][cohen-miyaji-ono-1998] of Cohen, Miyagi, and Ono.
83 //!
84 //! Their suggestion is to vary the formulas used by context, using a
85 //! \\( \mathbb P\^2 \rightarrow \mathbb P\^2 \\) doubling formula when
86 //! a doubling is followed
87 //! by another doubling, a \\( \mathbb P\^2 \rightarrow \mathbb P\^3 \\)
88 //! doubling formula when a doubling is followed by an addition, and
89 //! computing point additions using a \\( \mathbb P\^3 \times \mathbb P\^3
90 //! \rightarrow \mathbb P\^2 \\) formula.
91 //!
92 //! The `ref10` reference implementation of [Ed25519][ed25519], by
93 //! Bernstein, Duif, Lange, Schwabe, and Yang, tweaks
94 //! this strategy, factoring the addition formulas through the
95 //! completion \\( \mathbb P\^1 \times \mathbb P\^1 \\), so that the
96 //! output of an addition or doubling always lies in \\( \mathbb P\^1 \times
97 //! \mathbb P\^1\\), and the choice of which formula to use is replaced
98 //! by a choice of whether to convert the result to \\( \mathbb P\^2 \\)
99 //! or \\(\mathbb P\^3 \\).  However, this tweak is not described in
100 //! their paper, only in their software.
101 //!
102 //! Our naming for the `CompletedPoint` (\\(\mathbb P\^1 \times \mathbb
103 //! P\^1 \\)), `ProjectivePoint` (\\(\mathbb P\^2 \\)), and
104 //! `EdwardsPoint` (\\(\mathbb P\^3 \\)) structs follows the naming in
105 //! Adam Langley's [Golang ed25519][agl-ed25519] implementation, which
106 //! `curve25519-dalek` was originally derived from.
107 //!
108 //! Finally, to accelerate readditions, we use two cached point formats
109 //! in "Niels coordinates", named for Niels Duif,
110 //! one for the affine model and one for the \\( \mathbb P\^3 \\) model:
111 //!
112 //! * `AffineNielsPoint`: \\( (y+x, y-x, 2dxy) \\)
113 //! * `ProjectiveNielsPoint`: \\( (Y+X, Y-X, Z, 2dXY) \\)
114 //!
115 //! [smith-moderncrypto]: https://moderncrypto.org/mail-archive/curves/2016/000807.html
116 //! [costello-smith-2017]: https://eprint.iacr.org/2017/212
117 //! [hisil-wong-carter-dawson-2008]: https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf
118 //! [bernstein-birkner-joye-lange-peters-2008]: https://eprint.iacr.org/2008/013
119 //! [cohen-miyaji-ono-1998]: https://link.springer.com/content/pdf/10.1007%2F3-540-49649-1_6.pdf
120 //! [ed25519]: https://eprint.iacr.org/2011/368
121 //! [agl-ed25519]: https://github.com/agl/ed25519
122 
123 #![allow(non_snake_case)]
124 
125 use core::fmt::Debug;
126 use core::ops::{Add, Neg, Sub};
127 
128 use subtle::Choice;
129 use subtle::ConditionallySelectable;
130 
131 use zeroize::Zeroize;
132 
133 use constants;
134 
135 use edwards::EdwardsPoint;
136 use field::FieldElement;
137 use traits::ValidityCheck;
138 
139 // ------------------------------------------------------------------------
140 // Internal point representations
141 // ------------------------------------------------------------------------
142 
143 /// A `ProjectivePoint` is a point \\((X:Y:Z)\\) on the \\(\mathbb
144 /// P\^2\\) model of the curve.
145 /// A point \\((x,y)\\) in the affine model corresponds to
146 /// \\((x:y:1)\\).
147 ///
148 /// More details on the relationships between the different curve models
149 /// can be found in the module-level documentation.
150 #[derive(Copy, Clone)]
151 pub struct ProjectivePoint {
152     pub X: FieldElement,
153     pub Y: FieldElement,
154     pub Z: FieldElement,
155 }
156 
157 /// A `CompletedPoint` is a point \\(((X:Z), (Y:T))\\) on the \\(\mathbb
158 /// P\^1 \times \mathbb P\^1 \\) model of the curve.
159 /// A point (x,y) in the affine model corresponds to \\( ((x:1),(y:1))
160 /// \\).
161 ///
162 /// More details on the relationships between the different curve models
163 /// can be found in the module-level documentation.
164 #[derive(Copy, Clone)]
165 #[allow(missing_docs)]
166 pub struct CompletedPoint {
167     pub X: FieldElement,
168     pub Y: FieldElement,
169     pub Z: FieldElement,
170     pub T: FieldElement,
171 }
172 
173 /// A pre-computed point in the affine model for the curve, represented as
174 /// \\((y+x, y-x, 2dxy)\\) in "Niels coordinates".
175 ///
176 /// More details on the relationships between the different curve models
177 /// can be found in the module-level documentation.
178 // Safe to derive Eq because affine coordinates.
179 #[derive(Copy, Clone, Eq, PartialEq)]
180 #[allow(missing_docs)]
181 pub struct AffineNielsPoint {
182     pub y_plus_x:  FieldElement,
183     pub y_minus_x: FieldElement,
184     pub xy2d:      FieldElement,
185 }
186 
187 impl Zeroize for AffineNielsPoint {
zeroize(&mut self)188     fn zeroize(&mut self) {
189         self.y_plus_x.zeroize();
190         self.y_minus_x.zeroize();
191         self.xy2d.zeroize();
192     }
193 }
194 
195 /// A pre-computed point on the \\( \mathbb P\^3 \\) model for the
196 /// curve, represented as \\((Y+X, Y-X, Z, 2dXY)\\) in "Niels coordinates".
197 ///
198 /// More details on the relationships between the different curve models
199 /// can be found in the module-level documentation.
200 #[derive(Copy, Clone)]
201 pub struct ProjectiveNielsPoint {
202     pub Y_plus_X:  FieldElement,
203     pub Y_minus_X: FieldElement,
204     pub Z:         FieldElement,
205     pub T2d:       FieldElement,
206 }
207 
208 impl Zeroize for ProjectiveNielsPoint {
zeroize(&mut self)209     fn zeroize(&mut self) {
210         self.Y_plus_X.zeroize();
211         self.Y_minus_X.zeroize();
212         self.Z.zeroize();
213         self.T2d.zeroize();
214     }
215 }
216 
217 // ------------------------------------------------------------------------
218 // Constructors
219 // ------------------------------------------------------------------------
220 
221 use traits::Identity;
222 
223 impl Identity for ProjectivePoint {
identity() -> ProjectivePoint224     fn identity() -> ProjectivePoint {
225         ProjectivePoint {
226             X: FieldElement::zero(),
227             Y: FieldElement::one(),
228             Z: FieldElement::one(),
229         }
230     }
231 }
232 
233 impl Identity for ProjectiveNielsPoint {
identity() -> ProjectiveNielsPoint234     fn identity() -> ProjectiveNielsPoint {
235         ProjectiveNielsPoint{
236             Y_plus_X:  FieldElement::one(),
237             Y_minus_X: FieldElement::one(),
238             Z:         FieldElement::one(),
239             T2d:       FieldElement::zero(),
240         }
241     }
242 }
243 
244 impl Default for ProjectiveNielsPoint {
default() -> ProjectiveNielsPoint245     fn default() -> ProjectiveNielsPoint {
246         ProjectiveNielsPoint::identity()
247     }
248 }
249 
250 impl Identity for AffineNielsPoint {
identity() -> AffineNielsPoint251     fn identity() -> AffineNielsPoint {
252         AffineNielsPoint{
253             y_plus_x:  FieldElement::one(),
254             y_minus_x: FieldElement::one(),
255             xy2d:      FieldElement::zero(),
256         }
257     }
258 }
259 
260 impl Default for AffineNielsPoint {
default() -> AffineNielsPoint261     fn default() -> AffineNielsPoint {
262         AffineNielsPoint::identity()
263     }
264 }
265 
266 // ------------------------------------------------------------------------
267 // Validity checks (for debugging, not CT)
268 // ------------------------------------------------------------------------
269 
270 impl ValidityCheck for ProjectivePoint {
is_valid(&self) -> bool271     fn is_valid(&self) -> bool {
272         // Curve equation is    -x^2 + y^2 = 1 + d*x^2*y^2,
273         // homogenized as (-X^2 + Y^2)*Z^2 = Z^4 + d*X^2*Y^2
274         let XX = self.X.square();
275         let YY = self.Y.square();
276         let ZZ = self.Z.square();
277         let ZZZZ = ZZ.square();
278         let lhs = &(&YY - &XX) * &ZZ;
279         let rhs = &ZZZZ + &(&constants::EDWARDS_D * &(&XX * &YY));
280 
281         lhs == rhs
282     }
283 }
284 
285 // ------------------------------------------------------------------------
286 // Constant-time assignment
287 // ------------------------------------------------------------------------
288 
289 impl ConditionallySelectable for ProjectiveNielsPoint {
conditional_select(a: &Self, b: &Self, choice: Choice) -> Self290     fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
291         ProjectiveNielsPoint {
292             Y_plus_X: FieldElement::conditional_select(&a.Y_plus_X, &b.Y_plus_X, choice),
293             Y_minus_X: FieldElement::conditional_select(&a.Y_minus_X, &b.Y_minus_X, choice),
294             Z: FieldElement::conditional_select(&a.Z, &b.Z, choice),
295             T2d: FieldElement::conditional_select(&a.T2d, &b.T2d, choice),
296         }
297     }
298 
conditional_assign(&mut self, other: &Self, choice: Choice)299     fn conditional_assign(&mut self, other: &Self, choice: Choice) {
300         self.Y_plus_X.conditional_assign(&other.Y_plus_X, choice);
301         self.Y_minus_X.conditional_assign(&other.Y_minus_X, choice);
302         self.Z.conditional_assign(&other.Z, choice);
303         self.T2d.conditional_assign(&other.T2d, choice);
304     }
305 }
306 
307 impl ConditionallySelectable for AffineNielsPoint {
conditional_select(a: &Self, b: &Self, choice: Choice) -> Self308     fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
309         AffineNielsPoint {
310             y_plus_x: FieldElement::conditional_select(&a.y_plus_x, &b.y_plus_x, choice),
311             y_minus_x: FieldElement::conditional_select(&a.y_minus_x, &b.y_minus_x, choice),
312             xy2d: FieldElement::conditional_select(&a.xy2d, &b.xy2d, choice),
313         }
314     }
315 
conditional_assign(&mut self, other: &Self, choice: Choice)316     fn conditional_assign(&mut self, other: &Self, choice: Choice) {
317         self.y_plus_x.conditional_assign(&other.y_plus_x, choice);
318         self.y_minus_x.conditional_assign(&other.y_minus_x, choice);
319         self.xy2d.conditional_assign(&other.xy2d, choice);
320     }
321 }
322 
323 // ------------------------------------------------------------------------
324 // Point conversions
325 // ------------------------------------------------------------------------
326 
327 impl ProjectivePoint {
328     /// Convert this point from the \\( \mathbb P\^2 \\) model to the
329     /// \\( \mathbb P\^3 \\) model.
330     ///
331     /// This costs \\(3 \mathrm M + 1 \mathrm S\\).
to_extended(&self) -> EdwardsPoint332     pub fn to_extended(&self) -> EdwardsPoint {
333         EdwardsPoint {
334             X: &self.X * &self.Z,
335             Y: &self.Y * &self.Z,
336             Z: self.Z.square(),
337             T: &self.X * &self.Y,
338         }
339     }
340 }
341 
342 impl CompletedPoint {
343     /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1
344     /// \\) model to the \\( \mathbb P\^2 \\) model.
345     ///
346     /// This costs \\(3 \mathrm M \\).
to_projective(&self) -> ProjectivePoint347     pub fn to_projective(&self) -> ProjectivePoint {
348         ProjectivePoint {
349             X: &self.X * &self.T,
350             Y: &self.Y * &self.Z,
351             Z: &self.Z * &self.T,
352         }
353     }
354 
355     /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1
356     /// \\) model to the \\( \mathbb P\^3 \\) model.
357     ///
358     /// This costs \\(4 \mathrm M \\).
to_extended(&self) -> EdwardsPoint359     pub fn to_extended(&self) -> EdwardsPoint {
360         EdwardsPoint {
361             X: &self.X * &self.T,
362             Y: &self.Y * &self.Z,
363             Z: &self.Z * &self.T,
364             T: &self.X * &self.Y,
365         }
366     }
367 }
368 
369 // ------------------------------------------------------------------------
370 // Doubling
371 // ------------------------------------------------------------------------
372 
373 impl ProjectivePoint {
374     /// Double this point: return self + self
double(&self) -> CompletedPoint375     pub fn double(&self) -> CompletedPoint { // Double()
376         let XX          = self.X.square();
377         let YY          = self.Y.square();
378         let ZZ2         = self.Z.square2();
379         let X_plus_Y    = &self.X + &self.Y;
380         let X_plus_Y_sq = X_plus_Y.square();
381         let YY_plus_XX  = &YY + &XX;
382         let YY_minus_XX = &YY - &XX;
383 
384         CompletedPoint{
385             X: &X_plus_Y_sq - &YY_plus_XX,
386             Y: YY_plus_XX,
387             Z: YY_minus_XX,
388             T: &ZZ2 - &YY_minus_XX
389         }
390     }
391 }
392 
393 // ------------------------------------------------------------------------
394 // Addition and Subtraction
395 // ------------------------------------------------------------------------
396 
397 // XXX(hdevalence) These were doc(hidden) so they don't appear in the
398 // public API docs.
399 // However, that prevents them being used with --document-private-items,
400 // so comment out the doc(hidden) for now until this is resolved
401 //
402 // upstream rust issue: https://github.com/rust-lang/rust/issues/46380
403 //#[doc(hidden)]
404 impl<'a, 'b> Add<&'b ProjectiveNielsPoint> for &'a EdwardsPoint {
405     type Output = CompletedPoint;
406 
add(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint407     fn add(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint {
408         let Y_plus_X  = &self.Y + &self.X;
409         let Y_minus_X = &self.Y - &self.X;
410         let PP = &Y_plus_X  * &other.Y_plus_X;
411         let MM = &Y_minus_X * &other.Y_minus_X;
412         let TT2d = &self.T * &other.T2d;
413         let ZZ   = &self.Z * &other.Z;
414         let ZZ2  = &ZZ + &ZZ;
415 
416         CompletedPoint{
417             X: &PP - &MM,
418             Y: &PP + &MM,
419             Z: &ZZ2 + &TT2d,
420             T: &ZZ2 - &TT2d
421         }
422     }
423 }
424 
425 //#[doc(hidden)]
426 impl<'a, 'b> Sub<&'b ProjectiveNielsPoint> for &'a EdwardsPoint {
427     type Output = CompletedPoint;
428 
sub(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint429     fn sub(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint {
430         let Y_plus_X  = &self.Y + &self.X;
431         let Y_minus_X = &self.Y - &self.X;
432         let PM = &Y_plus_X * &other.Y_minus_X;
433         let MP = &Y_minus_X  * &other.Y_plus_X;
434         let TT2d = &self.T * &other.T2d;
435         let ZZ   = &self.Z * &other.Z;
436         let ZZ2  = &ZZ + &ZZ;
437 
438         CompletedPoint{
439             X: &PM - &MP,
440             Y: &PM + &MP,
441             Z: &ZZ2 - &TT2d,
442             T: &ZZ2 + &TT2d
443         }
444     }
445 }
446 
447 //#[doc(hidden)]
448 impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a EdwardsPoint {
449     type Output = CompletedPoint;
450 
add(self, other: &'b AffineNielsPoint) -> CompletedPoint451     fn add(self, other: &'b AffineNielsPoint) -> CompletedPoint {
452         let Y_plus_X  = &self.Y + &self.X;
453         let Y_minus_X = &self.Y - &self.X;
454         let PP        = &Y_plus_X  * &other.y_plus_x;
455         let MM        = &Y_minus_X * &other.y_minus_x;
456         let Txy2d     = &self.T * &other.xy2d;
457         let Z2        = &self.Z + &self.Z;
458 
459         CompletedPoint{
460             X: &PP - &MM,
461             Y: &PP + &MM,
462             Z: &Z2 + &Txy2d,
463             T: &Z2 - &Txy2d
464         }
465     }
466 }
467 
468 //#[doc(hidden)]
469 impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a EdwardsPoint {
470     type Output = CompletedPoint;
471 
sub(self, other: &'b AffineNielsPoint) -> CompletedPoint472     fn sub(self, other: &'b AffineNielsPoint) -> CompletedPoint {
473         let Y_plus_X  = &self.Y + &self.X;
474         let Y_minus_X = &self.Y - &self.X;
475         let PM        = &Y_plus_X  * &other.y_minus_x;
476         let MP        = &Y_minus_X * &other.y_plus_x;
477         let Txy2d     = &self.T * &other.xy2d;
478         let Z2        = &self.Z + &self.Z;
479 
480         CompletedPoint{
481             X: &PM - &MP,
482             Y: &PM + &MP,
483             Z: &Z2 - &Txy2d,
484             T: &Z2 + &Txy2d
485         }
486     }
487 }
488 
489 // ------------------------------------------------------------------------
490 // Negation
491 // ------------------------------------------------------------------------
492 
493 impl<'a> Neg for &'a ProjectiveNielsPoint {
494     type Output = ProjectiveNielsPoint;
495 
neg(self) -> ProjectiveNielsPoint496     fn neg(self) -> ProjectiveNielsPoint {
497         ProjectiveNielsPoint{
498             Y_plus_X:   self.Y_minus_X,
499             Y_minus_X:  self.Y_plus_X,
500             Z:          self.Z,
501             T2d:        -(&self.T2d),
502         }
503     }
504 }
505 
506 impl<'a> Neg for &'a AffineNielsPoint {
507     type Output = AffineNielsPoint;
508 
neg(self) -> AffineNielsPoint509     fn neg(self) -> AffineNielsPoint {
510         AffineNielsPoint{
511             y_plus_x:   self.y_minus_x,
512             y_minus_x:  self.y_plus_x,
513             xy2d:       -(&self.xy2d)
514         }
515     }
516 }
517 
518 // ------------------------------------------------------------------------
519 // Debug traits
520 // ------------------------------------------------------------------------
521 
522 impl Debug for ProjectivePoint {
fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result523     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
524         write!(f, "ProjectivePoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n}}",
525                &self.X, &self.Y, &self.Z)
526     }
527 }
528 
529 impl Debug for CompletedPoint {
fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result530     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
531         write!(f, "CompletedPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
532                &self.X, &self.Y, &self.Z, &self.T)
533     }
534 }
535 
536 impl Debug for AffineNielsPoint {
fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result537     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
538         write!(f, "AffineNielsPoint{{\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n}}",
539                &self.y_plus_x, &self.y_minus_x, &self.xy2d)
540     }
541 }
542 
543 impl Debug for ProjectiveNielsPoint {
fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result544     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
545         write!(f, "ProjectiveNielsPoint{{\n\tY_plus_X: {:?},\n\tY_minus_X: {:?},\n\tZ: {:?},\n\tT2d: {:?}\n}}",
546                &self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d)
547     }
548 }
549 
550 
551