use crate::{
templates::twisted_edwards_extended::Affine,
traits::{AffineCurve, ProjectiveCurve, TwistedEdwardsParameters as Parameters},
};
use snarkvm_fields::{impl_add_sub_from_field_ref, Field, One, PrimeField, Zero};
use snarkvm_utilities::{bititerator::BitIteratorBE, 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 t: P::BaseField,
pub z: P::BaseField,
}
impl<P: Parameters> Projective<P> {
#[inline]
pub const fn new(x: P::BaseField, y: P::BaseField, t: P::BaseField, z: P::BaseField) -> Self {
Self { x, y, t, z }
}
}
impl<P: Parameters> Zero for Projective<P> {
fn zero() -> Self {
Self::new(P::BaseField::zero(), P::BaseField::one(), P::BaseField::zero(), P::BaseField::one())
}
fn is_zero(&self) -> bool {
self.x.is_zero() && self.y == self.z && !self.y.is_zero() && self.t.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;
}
(self.x * other.z) == (other.x * self.z) && (self.y * other.z) == (other.y * 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;
}
self.x == (other.x * self.z) && self.y == (other.y * 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.t.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 t = P::BaseField::read_le(&mut reader)?;
let z = P::BaseField::read_le(reader)?;
Ok(Self::new(x, y, t, z))
}
}
impl<P: Parameters> ProjectiveCurve for Projective<P> {
type Affine = Affine<P>;
type BaseField = P::BaseField;
type ScalarField = P::ScalarField;
fn prime_subgroup_generator() -> Self {
Affine::prime_subgroup_generator().into()
}
fn is_normalized(&self) -> bool {
self.z.is_one()
}
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;
}
for g in v.iter_mut().filter(|g| !g.is_normalized()) {
g.x *= &g.z; g.y *= &g.z;
g.t *= &g.z;
g.z = P::BaseField::one(); }
}
#[allow(clippy::many_single_char_names)]
fn add_assign_mixed(&mut self, other: &Self::Affine) {
let a = self.x * other.x;
let b = self.y * other.y;
let c = P::EDWARDS_D * self.t * other.t;
let d = self.z;
let e = (self.x + self.y) * (other.x + other.y) - a - b;
let f = d - c;
let g = d + c;
let h = b - P::mul_by_a(&a);
self.x = e * f;
self.y = g * h;
self.t = e * h;
self.z = f * g;
}
#[inline]
#[must_use]
fn double(&self) -> Self {
let mut result = *self;
result.double_in_place();
result
}
#[inline]
fn double_in_place(&mut self) {
let a = self.x.square();
let b = self.y.square();
let c = self.z.square().double();
let d = P::mul_by_a(&a);
let e = (self.x + self.y).square() - a - b;
let g = d + b;
let f = g - c;
let h = d - b;
self.x = e * f;
self.y = g * h;
self.t = e * h;
self.z = f * g;
}
fn to_affine(&self) -> Affine<P> {
(*self).into()
}
}
impl<P: Parameters> Neg for Projective<P> {
type Output = Self;
fn neg(mut self) -> Self {
self.x = -self.x;
self.t = -self.t;
self
}
}
impl_add_sub_from_field_ref!(Projective, Parameters);
impl<'a, P: Parameters> Add<&'a Self> for Projective<P> {
type Output = Self;
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) {
let a = self.x * other.x;
let b = self.y * other.y;
let c = P::EDWARDS_D * self.t * other.t;
let d = self.z * other.z;
let h = b - P::mul_by_a(&a);
let e = (self.x + self.y) * (other.x + other.y) - a - b;
let f = d - c;
let g = d + c;
self.x = e * f;
self.y = g * h;
self.t = e * h;
self.z = f * g;
}
}
impl<'a, P: Parameters> Sub<&'a Self> for Projective<P> {
type Output = Self;
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 {
let mut res = Self::zero();
let mut found_one = false;
for i in BitIteratorBE::new(other.to_bigint()) {
if found_one {
res.double_in_place();
} else {
found_one = i;
}
if i {
res += self;
}
}
res
}
}
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> {
fn from(p: Affine<P>) -> Projective<P> {
Self::new(p.x, p.y, p.x * p.y, P::BaseField::one())
}
}