use core::ops::{AddAssign, Neg, SubAssign};
pub use ark_bls12_377::g1::Parameters;
use ark_bls12_377::{Fq, G1Affine};
use ark_ec::{
models::twisted_edwards::{Affine, Projective, TECurveConfig},
AffineRepr as _,
};
use ark_ff::Field as _;
use ark_std::{One as _, Zero as _};
use derivative::Derivative;
use crate::bls12_377::G1PTEAffine;
#[derive(Derivative)]
#[derivative(Copy(bound = "P: TECurveConfig"), Clone(bound = "P: TECurveConfig"))]
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct PreprocessedAffine<P: TECurveConfig> {
pub x: P::BaseField,
pub y: P::BaseField,
pub kt: P::BaseField,
}
impl<P: TECurveConfig> PreprocessedAffine<P> {
pub fn new(x: P::BaseField, y: P::BaseField) -> Self {
Self {
x,
y,
kt: (P::COEFF_D + P::COEFF_D) * x * y,
}
}
pub const fn zero() -> Self {
Self {
x: P::BaseField::ZERO,
y: P::BaseField::ONE,
kt: P::BaseField::ZERO,
}
}
pub fn is_zero(&self) -> bool {
self.x.is_zero() && self.y.is_one()
}
}
impl<P: TECurveConfig> From<Affine<P>> for PreprocessedAffine<P> {
fn from(affine: Affine<P>) -> PreprocessedAffine<P> {
Self::new(affine.x, affine.y)
}
}
impl<P: TECurveConfig> From<&Affine<P>> for PreprocessedAffine<P> {
fn from(affine: &Affine<P>) -> PreprocessedAffine<P> {
Self::new(affine.x, affine.y)
}
}
impl<P: TECurveConfig> From<PreprocessedAffine<P>> for Affine<P> {
fn from(pre: PreprocessedAffine<P>) -> Affine<P> {
Self::new(pre.x, pre.y)
}
}
impl<P: TECurveConfig> From<&PreprocessedAffine<P>> for Affine<P> {
fn from(pre: &PreprocessedAffine<P>) -> Affine<P> {
Self::new(pre.x, pre.y)
}
}
impl<'a, P: TECurveConfig> AddAssign<&'a PreprocessedAffine<P>> for Projective<P> {
fn add_assign(&mut self, other: &PreprocessedAffine<P>) {
if self.is_zero() {
self.x = other.x;
self.y = other.y;
self.t = other.x * other.y;
self.z = P::BaseField::one();
return;
}
let r1 = self.y - self.x;
let r2 = other.y - other.x;
let r3 = self.y + self.x;
let r4 = other.y + other.x;
let r5 = r1 * r2;
let r6 = r3 * r4;
let r7 = self.t * other.kt;
let r8 = self.z.double();
let r1b = r6 - r5;
let r2b = r8 - r7;
let r3b = r8 + r7;
let r4b = r6 + r5;
self.x = r1b * r2b;
self.y = r3b * r4b;
self.t = r1b * r4b;
self.z = r2b * r3b;
}
}
impl<'a, P: TECurveConfig> SubAssign<&'a PreprocessedAffine<P>> for Projective<P> {
fn sub_assign(&mut self, other: &'a PreprocessedAffine<P>) {
self.add_assign(&(-*other))
}
}
impl<P: TECurveConfig> Neg for PreprocessedAffine<P> {
type Output = Self;
fn neg(self) -> Self {
Self {
x: -self.x,
y: self.y,
kt: -self.kt,
}
}
}
pub fn batch_preprocess(a: &[G1Affine], b: &mut [G1PTEAffine]) {
use crate::bls12_377::{FQ_S, FQ_SQRT_MIN_A};
debug_assert!(a.len() == b.len());
let mut x = vec![Fq::ZERO; a.len()];
let mut y = vec![Fq::ZERO; a.len()];
let mut z = vec![Fq::ZERO; a.len()];
for (i, p) in a.iter().enumerate() {
if p.is_zero() {
x[i] = Fq::ZERO;
y[i] = Fq::ONE;
z[i] = Fq::ONE;
continue;
}
let xpo = p.x + Fq::ONE;
let sxpo = xpo * FQ_S;
let axpo = xpo * FQ_SQRT_MIN_A;
let syxpo = sxpo * p.y;
x[i] = (sxpo + Fq::ONE) * axpo;
y[i] = syxpo - p.y;
z[i] = syxpo + p.y;
}
ark_ff::batch_inversion(&mut z);
for i in 0..a.len() {
b[i] = G1PTEAffine::new(x[i] * z[i], y[i] * z[i]);
}
}
pub fn preprocess_points(points: &[G1Affine]) -> Vec<G1PTEAffine> {
let mut ppoints = vec![G1PTEAffine::zero(); points.len()];
const CHUNK: usize = 1 << 16;
for (chunk_in, chunk_out) in points
.chunks(CHUNK)
.zip(ppoints.as_mut_slice().chunks_mut(CHUNK))
{
crate::preprocess::batch_preprocess(chunk_in, chunk_out);
}
ppoints
}