use crate::{
templates::short_weierstrass_jacobian::Affine,
traits::{AffineCurve, ProjectiveCurve, ShortWeierstrassParameters as Parameters},
};
use snarkvm_fields::{impl_add_sub_from_field_ref, Field, One, Zero};
use snarkvm_utilities::{rand::Uniform, serialize::*, FromBytes, ToBytes};
use core::{
fmt::{Display, Formatter, Result as FmtResult},
hash::{Hash, Hasher},
ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
};
use rand::{
distributions::{Distribution, Standard},
Rng,
};
use std::io::{Read, Result as IoResult, Write};
#[derive(Copy, Clone, Debug)]
pub struct Projective<P: Parameters> {
pub x: P::BaseField,
pub y: P::BaseField,
pub z: P::BaseField,
}
impl<P: Parameters> Projective<P> {
pub const fn new(x: P::BaseField, y: P::BaseField, z: P::BaseField) -> Self {
Self { x, y, z }
}
}
impl<P: Parameters> Zero for Projective<P> {
#[inline]
fn zero() -> Self {
Self::new(P::BaseField::zero(), P::BaseField::one(), P::BaseField::zero())
}
#[inline]
fn is_zero(&self) -> bool {
self.z.is_zero()
}
}
impl<P: Parameters> Default for Projective<P> {
#[inline]
fn default() -> Self {
Self::zero()
}
}
impl<P: Parameters> Display for Projective<P> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{}", self.to_affine())
}
}
impl<P: Parameters> Hash for Projective<P> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.to_affine().hash(state);
}
}
impl<P: Parameters> Eq for Projective<P> {}
impl<P: Parameters> PartialEq for Projective<P> {
fn eq(&self, other: &Self) -> bool {
if self.is_zero() {
return other.is_zero();
}
if other.is_zero() {
return false;
}
let z1 = self.z.square();
let z2 = other.z.square();
!(self.x * z2 != other.x * z1 || self.y * (z2 * other.z) != other.y * (z1 * self.z))
}
}
impl<P: Parameters> PartialEq<Affine<P>> for Projective<P> {
fn eq(&self, other: &Affine<P>) -> bool {
if self.is_zero() {
return other.is_zero();
}
if other.is_zero() {
return false;
}
let z1 = self.z.square();
(self.x == other.x * z1) & (self.y == other.y * z1 * self.z)
}
}
impl<P: Parameters> Distribution<Projective<P>> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Projective<P> {
loop {
let x = P::BaseField::rand(rng);
let greatest = rng.gen();
if let Some(p) = Affine::from_x_coordinate(x, greatest) {
return p.mul_by_cofactor_to_projective();
}
}
}
}
impl<P: Parameters> ToBytes for Projective<P> {
#[inline]
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
self.x.write_le(&mut writer)?;
self.y.write_le(&mut writer)?;
self.z.write_le(writer)
}
}
impl<P: Parameters> FromBytes for Projective<P> {
#[inline]
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
let x = P::BaseField::read_le(&mut reader)?;
let y = P::BaseField::read_le(&mut reader)?;
let z = P::BaseField::read_le(reader)?;
Ok(Self::new(x, y, z))
}
}
impl<P: Parameters> ProjectiveCurve for Projective<P> {
type Affine = Affine<P>;
type BaseField = P::BaseField;
type ScalarField = P::ScalarField;
#[inline]
fn prime_subgroup_generator() -> Self {
Affine::prime_subgroup_generator().into()
}
#[inline]
fn is_normalized(&self) -> bool {
self.is_zero() || self.z.is_one()
}
#[inline]
fn batch_normalization(v: &mut [Self]) {
let mut prod = Vec::with_capacity(v.len());
let mut tmp = P::BaseField::one();
for g in v
.iter_mut()
.filter(|g| !g.is_normalized())
{
tmp.mul_assign(&g.z);
prod.push(tmp);
}
tmp = tmp.inverse().unwrap();
for (g, s) in v
.iter_mut()
.rev()
.filter(|g| !g.is_normalized())
.zip(
prod.into_iter()
.rev()
.skip(1)
.chain(Some(P::BaseField::one())),
)
{
let newtmp = tmp * g.z;
g.z = tmp * s;
tmp = newtmp;
}
#[cfg(not(feature = "parallel"))]
{
for g in v.iter_mut().filter(|g| !g.is_normalized()) {
let z2 = g.z.square(); g.x *= &z2; g.y *= &(z2 * g.z); g.z = P::BaseField::one(); }
}
#[cfg(feature = "parallel")]
{
use rayon::prelude::*;
v.par_iter_mut().filter(|g| !g.is_normalized()).for_each(|g| {
let z2 = g.z.square(); g.x *= &z2; g.y *= &(z2 * g.z); g.z = P::BaseField::one(); });
}
}
#[allow(clippy::many_single_char_names)]
fn add_assign_mixed(&mut self, other: &Self::Affine) {
if other.is_zero() {
return;
}
if self.is_zero() {
self.x = other.x;
self.y = other.y;
self.z = P::BaseField::one();
return;
}
let z1z1 = self.z.square();
let u2 = other.x * z1z1;
let s2 = (other.y * self.z) * z1z1;
if self.x == u2 && self.y == s2 {
self.double_in_place();
} else {
let mut h = u2;
h -= &self.x;
let hh = h.square();
let mut i = hh;
i.double_in_place();
i.double_in_place();
let mut j = h;
j *= &i;
let mut r = s2;
r -= &self.y;
r.double_in_place();
let mut v = self.x;
v *= &i;
self.x = r.square();
self.x -= &j;
self.x -= &v.double();
self.y = P::BaseField::sum_of_products([r, -self.y.double()].iter(), [(v - self.x), j].iter());
self.z += &h;
self.z.square_in_place();
self.z -= &z1z1;
self.z -= &hh;
}
}
#[inline]
#[must_use]
fn double(&self) -> Self {
let mut tmp = *self;
tmp.double_in_place();
tmp
}
#[inline]
fn double_in_place(&mut self) {
if self.is_zero() {
return;
}
if P::WEIERSTRASS_A.is_zero() {
let mut a = self.x.square();
let b = self.y.square();
let mut c = b.square();
let d = ((self.x + b).square() - a - c).double();
let old_a = a;
a.double_in_place();
let e = old_a + a;
let f = e.square();
self.z *= &self.y;
self.z.double_in_place();
self.x = f - d.double();
c.double_in_place();
c.double_in_place();
c.double_in_place();
self.y = (d - self.x) * e - c;
} else {
let xx = self.x.square();
let yy = self.y.square();
let mut yyyy = yy.square();
let zz = self.z.square();
let s = ((self.x + yy).square() - xx - yyyy).double();
let m = xx.double() + xx + P::mul_by_a(&zz.square());
let t = m.square() - s.double();
self.x = t;
let old_y = self.y;
yyyy.double_in_place();
yyyy.double_in_place();
yyyy.double_in_place();
self.y = m * (s - t) - yyyy;
self.z = (old_y + self.z).square() - yy - zz;
}
}
#[inline]
fn to_affine(&self) -> Affine<P> {
(*self).into()
}
}
impl<P: Parameters> Neg for Projective<P> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
if !self.is_zero() { Self::new(self.x, -self.y, self.z) } else { self }
}
}
impl_add_sub_from_field_ref!(Projective, Parameters);
impl<'a, P: Parameters> Add<&'a Self> for Projective<P> {
type Output = Self;
#[inline]
fn add(self, other: &'a Self) -> Self {
let mut copy = self;
copy += other;
copy
}
}
impl<'a, P: Parameters> AddAssign<&'a Self> for Projective<P> {
#[allow(clippy::many_single_char_names)]
#[allow(clippy::suspicious_op_assign_impl)]
fn add_assign(&mut self, other: &'a Self) {
if self.is_zero() {
*self = *other;
return;
}
if other.is_zero() {
return;
}
let z1z1 = self.z.square();
let z2z2 = other.z.square();
let u1 = self.x * z2z2;
let u2 = other.x * z1z1;
let s1 = self.y * other.z * z2z2;
let s2 = other.y * self.z * z1z1;
if u1 == u2 && s1 == s2 {
self.double_in_place();
} else {
let h = u2 - u1;
let i = (h.double()).square();
let j = h * i;
let r = (s2 - s1).double();
let v = u1 * i;
self.x = r.square() - j - (v.double());
self.y = P::BaseField::sum_of_products([r, -s1.double()].iter(), [(v - self.x), j].iter());
self.z = ((self.z + other.z).square() - z1z1 - z2z2) * h;
}
}
}
impl<'a, P: Parameters> Sub<&'a Self> for Projective<P> {
type Output = Self;
#[inline]
fn sub(self, other: &'a Self) -> Self {
let mut copy = self;
copy -= other;
copy
}
}
impl<'a, P: Parameters> SubAssign<&'a Self> for Projective<P> {
fn sub_assign(&mut self, other: &'a Self) {
*self += &(-(*other));
}
}
impl<P: Parameters> Mul<P::ScalarField> for Projective<P> {
type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
#[inline]
fn mul(self, other: P::ScalarField) -> Self {
P::mul_projective(self, other)
}
}
impl<P: Parameters> MulAssign<P::ScalarField> for Projective<P> {
fn mul_assign(&mut self, other: P::ScalarField) {
*self = *self * other
}
}
impl<P: Parameters> From<Affine<P>> for Projective<P> {
#[inline]
fn from(p: Affine<P>) -> Projective<P> {
if p.is_zero() { Self::zero() } else { Self::new(p.x, p.y, P::BaseField::one()) }
}
}