use super::{Affine, Projective, SWCurveConfig};
use crate::AffineRepr;
use ark_ff::{fields::Field, AdditiveGroup};
use ark_std::{
borrow::Borrow,
fmt::{Debug, Formatter, Result as FmtResult},
hash::{Hash, Hasher},
ops::{Add, AddAssign, Neg, SubAssign},
One, Zero,
};
use educe::Educe;
use zeroize::Zeroize;
#[derive(Educe)]
#[educe(Copy, Clone)]
#[must_use]
pub struct Bucket<P: SWCurveConfig> {
pub x: P::BaseField,
pub y: P::BaseField,
pub zz: P::BaseField,
pub zzz: P::BaseField,
}
impl<P: SWCurveConfig> Debug for Bucket<P> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self.is_zero() {
true => write!(f, "infinity"),
false => write!(f, "({}, {}, {}, {})", self.x, self.y, self.zz, self.zzz),
}
}
}
impl<P: SWCurveConfig> Eq for Bucket<P> {}
impl<P: SWCurveConfig> PartialEq for Bucket<P> {
fn eq(&self, other: &Self) -> bool {
if self.is_zero() {
return other.is_zero();
}
if other.is_zero() {
return false;
}
if self.x * &other.zz == other.x * &self.zz {
self.y * other.zzz == other.y * &self.zzz
} else {
false
}
}
}
impl<P: SWCurveConfig> Hash for Bucket<P> {
fn hash<H: Hasher>(&self, state: &mut H) {
Affine::from(*self).hash(state)
}
}
impl<P: SWCurveConfig> Default for Bucket<P> {
#[inline]
fn default() -> Self {
Self::zero()
}
}
impl<P: SWCurveConfig> Bucket<P> {
pub const ZERO: Self = Self::new_unchecked(
P::BaseField::ONE,
P::BaseField::ONE,
P::BaseField::ZERO,
P::BaseField::ZERO,
);
pub const fn new_unchecked(
x: P::BaseField,
y: P::BaseField,
zz: P::BaseField,
zzz: P::BaseField,
) -> Self {
Self { x, y, zz, zzz }
}
#[inline]
fn zero() -> Self {
Self::new_unchecked(
P::BaseField::one(),
P::BaseField::one(),
P::BaseField::zero(),
P::BaseField::zero(),
)
}
#[inline]
pub fn is_zero(&self) -> bool {
self.zz == P::BaseField::ZERO && self.zzz == P::BaseField::ZERO
}
pub fn double_in_place(&mut self) {
let mut u = self.y;
u.double_in_place();
let mut v = u;
v.square_in_place();
let mut w = u;
w *= &v;
let mut s = self.x;
s *= &v;
let mut m = self.x.square();
m += m.double();
if P::COEFF_A != P::BaseField::ZERO {
m += P::mul_by_a(self.zz.square());
}
self.x = m;
self.x.square_in_place();
self.x -= &s.double();
self.y = P::BaseField::sum_of_products(&[m, -w], &[(s - &self.x), self.y]);
self.zz *= v;
self.zzz *= &w;
}
}
impl<P: SWCurveConfig> Zeroize for Bucket<P> {
fn zeroize(&mut self) {
self.x.zeroize();
self.y.zeroize();
self.zz.zeroize();
self.zzz.zeroize();
}
}
impl<P: SWCurveConfig> Neg for Bucket<P> {
type Output = Self;
#[inline]
fn neg(mut self) -> Self {
self.y = -self.y;
self
}
}
impl<P: SWCurveConfig, T: Borrow<Affine<P>>> AddAssign<T> for Bucket<P> {
fn add_assign(&mut self, other: T) {
let other = other.borrow();
if let Some((other_x, other_y)) = other.xy() {
if self.is_zero() {
self.x = other_x;
self.y = other_y;
self.zz = P::BaseField::one();
self.zzz = P::BaseField::one();
return;
}
let z1z1 = self.zz;
let mut u2 = other_x;
u2 *= &z1z1;
let s2 = other_y * self.zzz;
if self.x == u2 {
if self.y == s2 {
*self = other.double_to_bucket();
} else {
*self = Self::zero()
}
} else {
let mut p = u2;
p -= &self.x;
let mut r = s2;
r -= &self.y;
let mut pp = p;
pp.square_in_place();
let mut ppp = pp;
ppp *= &p;
let mut q = self.x;
q *= &pp;
self.x = r.square();
self.x -= &ppp;
self.x -= &q.double();
q -= &self.x;
self.y = P::BaseField::sum_of_products(&[r, -self.y], &[q, ppp]);
self.zz *= &pp;
self.zzz *= &ppp;
}
}
}
}
impl<P: SWCurveConfig, T: Borrow<Affine<P>>> SubAssign<T> for Bucket<P> {
fn sub_assign(&mut self, other: T) {
*self += -(*other.borrow());
}
}
impl<'a, P: SWCurveConfig> Add<&'a Self> for Bucket<P> {
type Output = Self;
#[inline]
fn add(mut self, other: &'a Self) -> Self {
self += other;
self
}
}
impl<'a, P: SWCurveConfig> AddAssign<&'a Self> for Bucket<P> {
fn add_assign(&mut self, other: &'a Self) {
if self.is_zero() {
*self = *other;
return;
}
if other.is_zero() {
return;
}
let z1z1 = self.zz;
let z2z2 = other.zz;
let mut u1 = self.x;
u1 *= &z2z2;
let mut u2 = other.x;
u2 *= &z1z1;
let s1 = self.y * other.zzz;
let s2 = other.y * self.zzz;
if u1 == u2 {
if s1 == s2 {
self.double_in_place();
} else {
*self = Self::zero();
}
} else {
let mut p = u2;
p -= &u1;
let mut r = s2;
r -= &s1;
let mut pp = p;
pp.square_in_place();
let mut ppp = pp;
ppp *= &p;
let mut q = u1;
q *= &pp;
self.x = r.square();
self.x -= &ppp;
self.x -= &q.double();
q -= &self.x;
self.y = P::BaseField::sum_of_products(&[r, -s1], &[q, ppp]);
self.zz *= &pp;
self.zz *= &other.zz;
self.zzz *= &ppp;
self.zzz *= &other.zzz;
}
}
}
impl<'a, P: SWCurveConfig> SubAssign<&'a Self> for Bucket<P> {
fn sub_assign(&mut self, other: &'a Self) {
*self += &(-(*other));
}
}
impl<'a, P: SWCurveConfig> AddAssign<&'a Bucket<P>> for Projective<P> {
fn add_assign(&mut self, other: &'a Bucket<P>) {
if self.is_zero() {
*self = (*other).into();
return;
}
if other.is_zero() {
return;
}
*self += Self::from(*other);
}
}
impl<P: SWCurveConfig> From<Affine<P>> for Bucket<P> {
#[inline]
fn from(p: Affine<P>) -> Self {
p.xy().map_or_else(Self::zero, |(x, y)| Self {
x,
y,
zz: P::BaseField::one(),
zzz: P::BaseField::one(),
})
}
}
impl<P: SWCurveConfig> From<Bucket<P>> for Affine<P> {
#[inline]
fn from(p: Bucket<P>) -> Self {
p.zzz.inverse().map_or_else(Self::zero, |zzz_inv| {
let b = p.zz.square();
let x = p.x * &b;
let y = p.y * zzz_inv;
Self::new_unchecked(x, y)
})
}
}
impl<P: SWCurveConfig> From<Bucket<P>> for Projective<P> {
#[inline]
fn from(p: Bucket<P>) -> Self {
if p.is_zero() {
Self::zero()
} else {
Self::new_unchecked(p.x * &p.zz, p.y * &p.zzz, p.zz)
}
}
}