use snarkvm_curves::traits::{AffineCurve, ProjectiveCurve};
use snarkvm_fields::{FieldParameters, One, PrimeField, Zero};
use snarkvm_utilities::biginteger::BigInteger;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
pub struct VariableBaseMSM;
impl VariableBaseMSM {
fn msm_inner<G: AffineCurve>(bases: &[G], scalars: &[<G::ScalarField as PrimeField>::BigInteger]) -> G::Projective {
let c = if scalars.len() < 32 {
3
} else {
(2.0 / 3.0 * (f64::from(scalars.len() as u32)).log2() + 2.0).ceil() as usize
};
let num_bits = <G::ScalarField as PrimeField>::Parameters::MODULUS_BITS as usize;
let fr_one = G::ScalarField::one().into_repr();
let zero = G::zero().into_projective();
let window_starts: Vec<_> = (0..num_bits).step_by(c).collect();
let window_sums: Vec<_> = cfg_into_iter!(window_starts)
.map(|w_start| {
let mut res = zero;
let mut buckets = vec![zero; (1 << c) - 1];
scalars
.iter()
.zip(bases)
.filter(|(s, _)| !s.is_zero())
.for_each(|(&scalar, base)| {
if scalar == fr_one {
if w_start == 0 {
res.add_assign_mixed(base);
}
} else {
let mut scalar = scalar;
scalar.divn(w_start as u32);
let scalar = scalar.as_ref()[0] % (1 << c);
if scalar != 0 {
buckets[(scalar - 1) as usize].add_assign_mixed(&base);
}
}
});
G::Projective::batch_normalization(&mut buckets);
let mut running_sum = G::Projective::zero();
for b in buckets.into_iter().map(|g| g.into_affine()).rev() {
running_sum.add_assign_mixed(&b);
res += &running_sum;
}
res
})
.collect();
let lowest = window_sums.first().unwrap();
window_sums[1..].iter().rev().fold(zero, |mut total, sum_i| {
total += sum_i;
for _ in 0..c {
total.double_in_place();
}
total
}) + lowest
}
pub fn multi_scalar_mul<G: AffineCurve>(
bases: &[G],
scalars: &[<G::ScalarField as PrimeField>::BigInteger],
) -> G::Projective {
Self::msm_inner(bases, scalars)
}
}