openvm_ecc_guest/
weierstrass.rs

1use alloc::vec::Vec;
2use core::ops::Mul;
3
4use openvm_algebra_guest::{Field, IntMod};
5
6use super::group::Group;
7
8/// Short Weierstrass curve affine point.
9pub trait WeierstrassPoint: Clone + Sized {
10    /// The `a` coefficient in the Weierstrass curve equation `y^2 = x^3 + a x + b`.
11    const CURVE_A: Self::Coordinate;
12    /// The `b` coefficient in the Weierstrass curve equation `y^2 = x^3 + a x + b`.
13    const CURVE_B: Self::Coordinate;
14    const IDENTITY: Self;
15
16    type Coordinate: Field;
17
18    /// The concatenated `x, y` coordinates of the affine point, where
19    /// coordinates are in little endian.
20    ///
21    /// **Warning**: The memory layout of `Self` is expected to pack
22    /// `x` and `y` contiguously with no unallocated space in between.
23    fn as_le_bytes(&self) -> &[u8];
24
25    /// Raw constructor without asserting point is on the curve.
26    ///
27    /// # Safety
28    /// - Caller must guarantee `(x, y)` is a valid point on the curve and in any required subgroup.
29    /// - Identity point must be represented by `(0, 0)`.
30    unsafe fn from_xy_unchecked(x: Self::Coordinate, y: Self::Coordinate) -> Self;
31    fn into_coords(self) -> (Self::Coordinate, Self::Coordinate);
32    fn x(&self) -> &Self::Coordinate;
33    fn y(&self) -> &Self::Coordinate;
34    fn x_mut(&mut self) -> &mut Self::Coordinate;
35    fn y_mut(&mut self) -> &mut Self::Coordinate;
36
37    /// Calls any setup required for this curve. The implementation should internally use `OnceBool`
38    /// to ensure that setup is only called once.
39    fn set_up_once();
40
41    /// Add implementation that handles identity and whether points are equal or not.
42    ///
43    /// # Safety
44    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
45    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
46    ///   been called already.
47    fn add_assign_impl<const CHECK_SETUP: bool>(&mut self, p2: &Self);
48
49    /// Double implementation that handles identity.
50    ///
51    /// # Safety
52    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
53    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
54    ///   been called already.
55    fn double_assign_impl<const CHECK_SETUP: bool>(&mut self);
56
57    /// # Safety
58    /// - Assumes self != +- p2 and self != identity and p2 != identity.
59    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
60    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
61    ///   been called already.
62    unsafe fn add_ne_nonidentity<const CHECK_SETUP: bool>(&self, p2: &Self) -> Self;
63    /// # Safety
64    /// - Assumes self != +- p2 and self != identity and p2 != identity.
65    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
66    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
67    ///   been called already.
68    unsafe fn add_ne_assign_nonidentity<const CHECK_SETUP: bool>(&mut self, p2: &Self);
69    /// # Safety
70    /// - Assumes self != +- p2 and self != identity and p2 != identity.
71    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
72    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
73    ///   been called already.
74    unsafe fn sub_ne_nonidentity<const CHECK_SETUP: bool>(&self, p2: &Self) -> Self;
75    /// # Safety
76    /// - Assumes self != +- p2 and self != identity and p2 != identity.
77    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
78    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
79    ///   been called already.
80    unsafe fn sub_ne_assign_nonidentity<const CHECK_SETUP: bool>(&mut self, p2: &Self);
81    /// # Safety
82    /// - Assumes self != identity and 2 * self != identity.
83    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
84    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
85    ///   been called already.
86    unsafe fn double_nonidentity<const CHECK_SETUP: bool>(&self) -> Self;
87    /// # Safety
88    /// - Assumes self != identity and 2 * self != identity.
89    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
90    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
91    ///   been called already.
92    unsafe fn double_assign_nonidentity<const CHECK_SETUP: bool>(&mut self);
93
94    /// Constructs a point on the curve, including the identity point.
95    ///
96    /// # Safety
97    /// - This constructor does not perform any subgroup checks and only guarantees that the point
98    ///   is on the curve.
99    #[inline(always)]
100    unsafe fn from_xy(x: Self::Coordinate, y: Self::Coordinate) -> Option<Self>
101    where
102        for<'a> &'a Self::Coordinate: Mul<&'a Self::Coordinate, Output = Self::Coordinate>,
103    {
104        if x == Self::Coordinate::ZERO && y == Self::Coordinate::ZERO {
105            Some(Self::IDENTITY)
106        } else {
107            Self::from_xy_nonidentity(x, y)
108        }
109    }
110
111    /// Constructs a point on the curve, excluding the identity point.
112    ///
113    /// # Safety
114    /// - This constructor does not perform any subgroup checks and only guarantees that the point
115    ///   is a non-identity point on the curve.
116    #[inline(always)]
117    unsafe fn from_xy_nonidentity(x: Self::Coordinate, y: Self::Coordinate) -> Option<Self>
118    where
119        for<'a> &'a Self::Coordinate: Mul<&'a Self::Coordinate, Output = Self::Coordinate>,
120    {
121        let lhs = &y * &y;
122        let rhs = &x * &x * &x + &Self::CURVE_A * &x + &Self::CURVE_B;
123        if lhs != rhs {
124            return None;
125        }
126        Some(Self::from_xy_unchecked(x, y))
127    }
128}
129
130pub trait FromCompressed<Coordinate> {
131    /// Given `x`-coordinate,
132    ///
133    /// Decompresses a point from its x-coordinate and a recovery identifier which indicates
134    /// the parity of the y-coordinate. Given the x-coordinate, this function attempts to find the
135    /// corresponding y-coordinate that satisfies the elliptic curve equation. If successful, it
136    /// returns the point as an instance of Self. If the point cannot be decompressed, it returns
137    /// None.
138    ///
139    /// # Safety
140    /// This function does not perform subgroup checks and only checks whether the point is on the
141    /// curve.
142    fn decompress(x: Coordinate, rec_id: &u8) -> Option<Self>
143    where
144        Self: core::marker::Sized;
145}
146
147/// A trait for elliptic curves that bridges the openvm types and external types with
148/// CurveArithmetic etc. Implement this for external curves with corresponding openvm point and
149/// scalar types.
150pub trait IntrinsicCurve {
151    type Scalar: Clone;
152    type Point: Clone;
153
154    /// Multi-scalar multiplication.
155    /// The implementation may be specialized to use properties of the curve
156    /// (e.g., if the curve order is prime).
157    fn msm(coeffs: &[Self::Scalar], bases: &[Self::Point]) -> Self::Point;
158}
159
160// MSM using preprocessed table (windowed method)
161// Reference: modified from https://github.com/arkworks-rs/algebra/blob/master/ec/src/scalar_mul/mod.rs
162//
163// We specialize to Weierstrass curves and further make optimizations for when the curve order is
164// prime.
165
166/// Cached precomputations of scalar multiples of several base points.
167/// - `window_bits` is the window size used for the precomputation
168/// - `max_scalar_bits` is the maximum size of the scalars that will be multiplied
169/// - `table` is the precomputed table
170pub struct CachedMulTable<'a, C: IntrinsicCurve> {
171    /// Window bits. Must be > 0.
172    /// For alignment, we currently require this to divide 8 (bits in a byte).
173    pub window_bits: usize,
174    pub bases: &'a [C::Point],
175    /// `table[i][j] = (j + 2) * bases[i]` for `j + 2 < 2 ** window_bits`
176    table: Vec<Vec<C::Point>>,
177    /// Needed to return reference to the identity point.
178    identity: C::Point,
179}
180
181impl<'a, C: IntrinsicCurve> CachedMulTable<'a, C>
182where
183    C::Point: WeierstrassPoint + Group,
184    C::Scalar: IntMod,
185{
186    /// Constructor when each element of `bases` has prime torsion or is identity.
187    ///
188    /// Assumes that `window_bits` is less than (number of bits - 1) of the order of
189    /// subgroup generated by each non-identity `base`.
190    #[inline]
191    pub fn new_with_prime_order(bases: &'a [C::Point], window_bits: usize) -> Self {
192        C::Point::set_up_once();
193        assert!(window_bits > 0);
194        let window_size = 1 << window_bits;
195        let table = bases
196            .iter()
197            .map(|base| {
198                if base.is_identity() {
199                    vec![<C::Point as Group>::IDENTITY; window_size - 2]
200                } else {
201                    let mut multiples = Vec::with_capacity(window_size - 2);
202                    for _ in 0..window_size - 2 {
203                        // Because the order of `base` is prime, we are guaranteed that
204                        // j * base != identity,
205                        // j * base != +- base for j > 1,
206                        // j * base + base != identity
207                        let multiple = multiples
208                            .last()
209                            .map(|last| unsafe {
210                                WeierstrassPoint::add_ne_nonidentity::<false>(last, base)
211                            })
212                            .unwrap_or_else(|| unsafe { base.double_nonidentity::<false>() });
213                        multiples.push(multiple);
214                    }
215                    multiples
216                }
217            })
218            .collect();
219
220        Self {
221            window_bits,
222            bases,
223            table,
224            identity: <C::Point as Group>::IDENTITY,
225        }
226    }
227
228    #[inline(always)]
229    fn get_multiple(&self, base_idx: usize, scalar: usize) -> &C::Point {
230        if scalar == 0 {
231            &self.identity
232        } else if scalar == 1 {
233            unsafe { self.bases.get_unchecked(base_idx) }
234        } else {
235            unsafe { self.table.get_unchecked(base_idx).get_unchecked(scalar - 2) }
236        }
237    }
238
239    /// Computes `sum scalars[i] * bases[i]`.
240    ///
241    /// For implementation simplicity, currently only implemented when
242    /// `window_bits` divides 8 (number of bits in a byte).
243    #[inline]
244    pub fn windowed_mul(&self, scalars: &[C::Scalar]) -> C::Point {
245        C::Point::set_up_once();
246        assert_eq!(8 % self.window_bits, 0);
247        assert_eq!(scalars.len(), self.bases.len());
248        let windows_per_byte = 8 / self.window_bits;
249
250        let num_windows = C::Scalar::NUM_LIMBS * windows_per_byte;
251        let mask = (1u8 << self.window_bits) - 1;
252
253        // The current byte index (little endian) at the current step of the
254        // windowed method, across all scalars.
255        let mut limb_idx = C::Scalar::NUM_LIMBS;
256        // The current bit (little endian) within the current byte of the windowed
257        // method. The window will look at bits `bit_idx..bit_idx + window_bits`.
258        // bit_idx will always be in range [0, 8)
259        let mut bit_idx = 0;
260
261        let mut res = <C::Point as Group>::IDENTITY;
262        for outer in 0..num_windows {
263            if bit_idx == 0 {
264                limb_idx -= 1;
265                bit_idx = 8 - self.window_bits;
266            } else {
267                bit_idx -= self.window_bits;
268            }
269
270            if outer != 0 {
271                for _ in 0..self.window_bits {
272                    // Note: this handles identity
273                    // setup has been called above
274                    res.double_assign_impl::<false>();
275                }
276            }
277            for (base_idx, scalar) in scalars.iter().enumerate() {
278                let scalar = (scalar.as_le_bytes()[limb_idx] >> bit_idx) & mask;
279                let summand = self.get_multiple(base_idx, scalar as usize);
280                // handles identity
281                // setup has been called above
282                res.add_assign_impl::<false>(summand);
283            }
284        }
285        res
286    }
287}
288
289/// Macro to generate a newtype wrapper for [AffinePoint](crate::AffinePoint)
290/// that implements elliptic curve operations by using the underlying field operations according to
291/// the [formulas](https://www.hyperelliptic.org/EFD/g1p/auto-shortw.html) for short Weierstrass curves.
292///
293/// The following imports are required:
294/// ```rust
295/// use core::ops::AddAssign;
296///
297/// use openvm_algebra_guest::{DivUnsafe, Field};
298/// use openvm_ecc_guest::{weierstrass::WeierstrassPoint, AffinePoint, Group};
299/// ```
300#[macro_export]
301macro_rules! impl_sw_affine {
302    // Assumes `a = 0` in curve equation. `$three` should be a constant expression for `3` of type
303    // `$field`.
304    ($struct_name:ident, $field:ty, $three:expr, $b:expr) => {
305        /// A newtype wrapper for [AffinePoint] that implements elliptic curve operations
306        /// by using the underlying field operations according to the [formulas](https://www.hyperelliptic.org/EFD/g1p/auto-shortw.html) for short Weierstrass curves.
307        #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
308        #[repr(transparent)]
309        pub struct $struct_name(AffinePoint<$field>);
310
311        impl $struct_name {
312            pub const fn new(x: $field, y: $field) -> Self {
313                Self(AffinePoint::new(x, y))
314            }
315        }
316
317        impl WeierstrassPoint for $struct_name {
318            const CURVE_A: $field = <$field>::ZERO;
319            const CURVE_B: $field = $b;
320            const IDENTITY: Self = Self(AffinePoint::new(<$field>::ZERO, <$field>::ZERO));
321
322            type Coordinate = $field;
323
324            /// SAFETY: assumes that [$field] has internal representation in little-endian.
325            fn as_le_bytes(&self) -> &[u8] {
326                unsafe {
327                    &*core::ptr::slice_from_raw_parts(
328                        self as *const Self as *const u8,
329                        core::mem::size_of::<Self>(),
330                    )
331                }
332            }
333            unsafe fn from_xy_unchecked(x: Self::Coordinate, y: Self::Coordinate) -> Self {
334                Self(AffinePoint::new(x, y))
335            }
336            fn into_coords(self) -> (Self::Coordinate, Self::Coordinate) {
337                (self.0.x, self.0.y)
338            }
339            fn x(&self) -> &Self::Coordinate {
340                &self.0.x
341            }
342            fn y(&self) -> &Self::Coordinate {
343                &self.0.y
344            }
345            fn x_mut(&mut self) -> &mut Self::Coordinate {
346                &mut self.0.x
347            }
348            fn y_mut(&mut self) -> &mut Self::Coordinate {
349                &mut self.0.y
350            }
351
352            fn set_up_once() {
353                // There are no special opcodes for curve operations in this case, so no additional
354                // setup is required.
355                //
356                // Since the `Self::Coordinate` is abstract, any set up required by the field is not
357                // handled here.
358            }
359
360            fn add_assign_impl<const CHECK_SETUP: bool>(&mut self, p2: &Self) {
361                if self == &<Self as WeierstrassPoint>::IDENTITY {
362                    *self = p2.clone();
363                } else if p2 == &<Self as WeierstrassPoint>::IDENTITY {
364                    // do nothing
365                } else if self.x() == p2.x() {
366                    if self.y() + p2.y() == <Self::Coordinate as openvm_algebra_guest::Field>::ZERO
367                    {
368                        *self = <Self as WeierstrassPoint>::IDENTITY;
369                    } else {
370                        unsafe {
371                            self.double_assign_nonidentity::<CHECK_SETUP>();
372                        }
373                    }
374                } else {
375                    unsafe {
376                        self.add_ne_assign_nonidentity::<CHECK_SETUP>(p2);
377                    }
378                }
379            }
380
381            #[inline(always)]
382            fn double_assign_impl<const CHECK_SETUP: bool>(&mut self) {
383                if self != &<Self as WeierstrassPoint>::IDENTITY {
384                    unsafe {
385                        self.double_assign_nonidentity::<CHECK_SETUP>();
386                    }
387                }
388            }
389
390            unsafe fn double_nonidentity<const CHECK_SETUP: bool>(&self) -> Self {
391                use openvm_algebra_guest::DivUnsafe;
392                // lambda = (3*x1^2+a)/(2*y1)
393                // assume a = 0
394                let lambda = (&THREE * self.x() * self.x()).div_unsafe(self.y() + self.y());
395                // x3 = lambda^2-x1-x1
396                let x3 = &lambda * &lambda - self.x() - self.x();
397                // y3 = lambda * (x1-x3) - y1
398                let y3 = lambda * (self.x() - &x3) - self.y();
399                Self(AffinePoint::new(x3, y3))
400            }
401
402            #[inline(always)]
403            unsafe fn double_assign_nonidentity<const CHECK_SETUP: bool>(&mut self) {
404                *self = self.double_nonidentity::<CHECK_SETUP>();
405            }
406
407            unsafe fn add_ne_nonidentity<const CHECK_SETUP: bool>(&self, p2: &Self) -> Self {
408                use openvm_algebra_guest::DivUnsafe;
409                // lambda = (y2-y1)/(x2-x1)
410                // x3 = lambda^2-x1-x2
411                // y3 = lambda*(x1-x3)-y1
412                let lambda = (p2.y() - self.y()).div_unsafe(p2.x() - self.x());
413                let x3 = &lambda * &lambda - self.x() - p2.x();
414                let y3 = lambda * (self.x() - &x3) - self.y();
415                Self(AffinePoint::new(x3, y3))
416            }
417
418            #[inline(always)]
419            unsafe fn add_ne_assign_nonidentity<const CHECK_SETUP: bool>(&mut self, p2: &Self) {
420                *self = self.add_ne_nonidentity::<CHECK_SETUP>(p2);
421            }
422
423            unsafe fn sub_ne_nonidentity<const CHECK_SETUP: bool>(&self, p2: &Self) -> Self {
424                use openvm_algebra_guest::DivUnsafe;
425                // lambda = (y2+y1)/(x1-x2)
426                // x3 = lambda^2-x1-x2
427                // y3 = lambda*(x1-x3)-y1
428                let lambda = (p2.y() + self.y()).div_unsafe(self.x() - p2.x());
429                let x3 = &lambda * &lambda - self.x() - p2.x();
430                let y3 = lambda * (self.x() - &x3) - self.y();
431                Self(AffinePoint::new(x3, y3))
432            }
433
434            #[inline(always)]
435            unsafe fn sub_ne_assign_nonidentity<const CHECK_SETUP: bool>(&mut self, p2: &Self) {
436                *self = self.sub_ne_nonidentity::<CHECK_SETUP>(p2);
437            }
438        }
439
440        impl core::ops::Neg for $struct_name {
441            type Output = Self;
442
443            #[inline(always)]
444            fn neg(mut self) -> Self::Output {
445                self.0.y.neg_assign();
446                self
447            }
448        }
449
450        impl core::ops::Neg for &$struct_name {
451            type Output = $struct_name;
452
453            #[inline(always)]
454            fn neg(self) -> Self::Output {
455                self.clone().neg()
456            }
457        }
458
459        impl From<$struct_name> for AffinePoint<$field> {
460            fn from(value: $struct_name) -> Self {
461                value.0
462            }
463        }
464
465        impl From<AffinePoint<$field>> for $struct_name {
466            fn from(value: AffinePoint<$field>) -> Self {
467                Self(value)
468            }
469        }
470    };
471}
472
473/// Implements `Group` on `$struct_name` assuming that `$struct_name` implements `WeierstrassPoint`.
474/// Assumes that `Neg` is implemented for `&$struct_name`.
475#[macro_export]
476macro_rules! impl_sw_group_ops {
477    ($struct_name:ident, $field:ty) => {
478        impl Group for $struct_name {
479            type SelfRef<'a> = &'a Self;
480
481            const IDENTITY: Self = <Self as WeierstrassPoint>::IDENTITY;
482
483            #[inline(always)]
484            fn double(&self) -> Self {
485                if self.is_identity() {
486                    self.clone()
487                } else {
488                    unsafe { self.double_nonidentity::<true>() }
489                }
490            }
491
492            #[inline(always)]
493            fn double_assign(&mut self) {
494                self.double_assign_impl::<true>();
495            }
496
497            // This implementation is the same as the default implementation in the `Group` trait,
498            // but it was found that overriding the default implementation reduced the cycle count
499            // by 50% on the ecrecover benchmark.
500            // We hypothesize that this is due to compiler optimizations that are not possible when
501            // the `is_identity` function is defined in a different source file.
502            #[inline(always)]
503            fn is_identity(&self) -> bool {
504                self == &<Self as Group>::IDENTITY
505            }
506        }
507
508        impl core::ops::Add<&$struct_name> for $struct_name {
509            type Output = Self;
510
511            #[inline(always)]
512            fn add(mut self, p2: &$struct_name) -> Self::Output {
513                use core::ops::AddAssign;
514                self.add_assign(p2);
515                self
516            }
517        }
518
519        impl core::ops::Add for $struct_name {
520            type Output = Self;
521
522            #[inline(always)]
523            fn add(self, rhs: Self) -> Self::Output {
524                self.add(&rhs)
525            }
526        }
527
528        impl core::ops::Add<&$struct_name> for &$struct_name {
529            type Output = $struct_name;
530
531            #[inline(always)]
532            fn add(self, p2: &$struct_name) -> Self::Output {
533                if self.is_identity() {
534                    p2.clone()
535                } else if p2.is_identity() {
536                    self.clone()
537                } else if WeierstrassPoint::x(self) == WeierstrassPoint::x(p2) {
538                    if self.y() + p2.y() == <$field as openvm_algebra_guest::Field>::ZERO {
539                        <$struct_name as WeierstrassPoint>::IDENTITY
540                    } else {
541                        unsafe { self.double_nonidentity::<true>() }
542                    }
543                } else {
544                    unsafe { self.add_ne_nonidentity::<true>(p2) }
545                }
546            }
547        }
548
549        impl core::ops::AddAssign<&$struct_name> for $struct_name {
550            #[inline(always)]
551            fn add_assign(&mut self, p2: &$struct_name) {
552                self.add_assign_impl::<true>(p2);
553            }
554        }
555
556        impl core::ops::AddAssign for $struct_name {
557            #[inline(always)]
558            fn add_assign(&mut self, rhs: Self) {
559                self.add_assign(&rhs);
560            }
561        }
562
563        impl core::ops::Sub<&$struct_name> for $struct_name {
564            type Output = Self;
565
566            #[inline(always)]
567            fn sub(self, rhs: &$struct_name) -> Self::Output {
568                core::ops::Sub::sub(&self, rhs)
569            }
570        }
571
572        impl core::ops::Sub for $struct_name {
573            type Output = $struct_name;
574
575            #[inline(always)]
576            fn sub(self, rhs: Self) -> Self::Output {
577                self.sub(&rhs)
578            }
579        }
580
581        impl core::ops::Sub<&$struct_name> for &$struct_name {
582            type Output = $struct_name;
583
584            #[inline(always)]
585            fn sub(self, p2: &$struct_name) -> Self::Output {
586                if p2.is_identity() {
587                    self.clone()
588                } else if self.is_identity() {
589                    core::ops::Neg::neg(p2)
590                } else if WeierstrassPoint::x(self) == WeierstrassPoint::x(p2) {
591                    if self.y() == p2.y() {
592                        <$struct_name as WeierstrassPoint>::IDENTITY
593                    } else {
594                        unsafe { self.double_nonidentity::<true>() }
595                    }
596                } else {
597                    unsafe { self.sub_ne_nonidentity::<true>(p2) }
598                }
599            }
600        }
601
602        impl core::ops::SubAssign<&$struct_name> for $struct_name {
603            #[inline(always)]
604            fn sub_assign(&mut self, p2: &$struct_name) {
605                if p2.is_identity() {
606                    // do nothing
607                } else if self.is_identity() {
608                    *self = core::ops::Neg::neg(p2);
609                } else if WeierstrassPoint::x(self) == WeierstrassPoint::x(p2) {
610                    if self.y() == p2.y() {
611                        *self = <$struct_name as WeierstrassPoint>::IDENTITY
612                    } else {
613                        unsafe {
614                            self.double_assign_nonidentity::<true>();
615                        }
616                    }
617                } else {
618                    unsafe {
619                        self.sub_ne_assign_nonidentity::<true>(p2);
620                    }
621                }
622            }
623        }
624
625        impl core::ops::SubAssign for $struct_name {
626            #[inline(always)]
627            fn sub_assign(&mut self, rhs: Self) {
628                self.sub_assign(&rhs);
629            }
630        }
631    };
632}