use ff::Field;
use rand_core::RngCore;
use super::super::{Coeff, Polynomial};
use super::{Blind, Params};
use crate::arithmetic::{
best_multiexp, compute_inner_product, eval_polynomial, parallelize, CurveAffine, FieldExt,
};
use crate::transcript::{EncodedChallenge, TranscriptWrite};
use group::Curve;
use std::io;
pub fn create_proof<
C: CurveAffine,
E: EncodedChallenge<C>,
R: RngCore,
T: TranscriptWrite<C, E>,
>(
params: &Params<C>,
mut rng: R,
transcript: &mut T,
p_poly: &Polynomial<C::Scalar, Coeff>,
p_blind: Blind<C::Scalar>,
x_3: C::Scalar,
) -> io::Result<()> {
assert_eq!(p_poly.len(), params.n as usize);
let mut s_poly = (*p_poly).clone();
for coeff in s_poly.iter_mut() {
*coeff = C::Scalar::random(&mut rng);
}
let s_at_x3 = eval_polynomial(&s_poly[..], x_3);
s_poly[0] = s_poly[0] - &s_at_x3;
let s_poly_blind = Blind(C::Scalar::random(&mut rng));
let s_poly_commitment = params.commit(&s_poly, s_poly_blind).to_affine();
transcript.write_point(s_poly_commitment)?;
let xi = *transcript.squeeze_challenge_scalar::<()>();
let z = *transcript.squeeze_challenge_scalar::<()>();
let mut p_prime_poly = s_poly * xi + p_poly;
let v = eval_polynomial(&p_prime_poly, x_3);
p_prime_poly[0] = p_prime_poly[0] - &v;
let p_prime_blind = s_poly_blind * Blind(xi) + p_blind;
let mut f = p_prime_blind.0;
let mut p_prime = p_prime_poly.values;
assert_eq!(p_prime.len(), params.n as usize);
let mut b = Vec::with_capacity(1 << params.k);
{
let mut cur = C::Scalar::one();
for _ in 0..(1 << params.k) {
b.push(cur);
cur *= &x_3;
}
}
let mut g_prime = params.g.clone();
for j in 0..params.k {
let half = 1 << (params.k - j - 1);
let l_j = best_multiexp(&p_prime[half..], &g_prime[0..half]);
let r_j = best_multiexp(&p_prime[0..half], &g_prime[half..]);
let value_l_j = compute_inner_product(&p_prime[half..], &b[0..half]);
let value_r_j = compute_inner_product(&p_prime[0..half], &b[half..]);
let l_j_randomness = C::Scalar::random(&mut rng);
let r_j_randomness = C::Scalar::random(&mut rng);
let l_j = l_j + &best_multiexp(&[value_l_j * &z, l_j_randomness], &[params.u, params.w]);
let r_j = r_j + &best_multiexp(&[value_r_j * &z, r_j_randomness], &[params.u, params.w]);
let l_j = l_j.to_affine();
let r_j = r_j.to_affine();
transcript.write_point(l_j)?;
transcript.write_point(r_j)?;
let u_j = *transcript.squeeze_challenge_scalar::<()>();
let u_j_inv = u_j.invert().unwrap();
for i in 0..half {
p_prime[i] = p_prime[i] + &(p_prime[i + half] * &u_j_inv);
b[i] = b[i] + &(b[i + half] * &u_j);
}
p_prime.truncate(half);
b.truncate(half);
parallel_generator_collapse(&mut g_prime, u_j);
g_prime.truncate(half);
f += &(l_j_randomness * &u_j_inv);
f += &(r_j_randomness * &u_j);
}
assert_eq!(p_prime.len(), 1);
let c = p_prime[0];
transcript.write_scalar(c)?;
transcript.write_scalar(f)?;
Ok(())
}
fn parallel_generator_collapse<C: CurveAffine>(g: &mut [C], challenge: C::Scalar) {
let len = g.len() / 2;
let (g_lo, g_hi) = g.split_at_mut(len);
parallelize(g_lo, |g_lo, start| {
let g_hi = &g_hi[start..];
let mut tmp = Vec::with_capacity(g_lo.len());
for (g_lo, g_hi) in g_lo.iter().zip(g_hi.iter()) {
tmp.push(g_lo.to_curve() + &(*g_hi * challenge));
}
C::Curve::batch_normalize(&tmp, g_lo);
});
}