use std::ops::AddAssign;
use ff::PrimeField;
pub trait DelayedReduction<Value>: Sized {
type Accumulator: Copy + Clone + Default + AddAssign + Send + Sync;
fn unreduced_multiply_accumulate(acc: &mut Self::Accumulator, field: &Self, value: &Value);
fn reduce(acc: &Self::Accumulator) -> Self;
}
use crate::big_num::{
limbs::{WideLimbs, mul_acc_4_by_4},
montgomery::{MontgomeryLimbs, montgomery_reduce_9},
};
impl<F: MontgomeryLimbs + PrimeField + Copy> DelayedReduction<F> for F {
type Accumulator = WideLimbs<9>;
#[inline(always)]
fn unreduced_multiply_accumulate(acc: &mut Self::Accumulator, field_a: &Self, field_b: &F) {
mul_acc_4_by_4(&mut acc.0, field_a.to_limbs(), field_b.to_limbs());
}
#[inline(always)]
fn reduce(acc: &Self::Accumulator) -> Self {
F::from_limbs(montgomery_reduce_9::<F>(&acc.0))
}
}
#[cfg(test)]
pub(crate) fn test_delayed_reduction_sum_impl<F: DelayedReduction<F> + PrimeField + Copy>() {
use rand::{SeedableRng, rngs::StdRng};
let mut rng = StdRng::seed_from_u64(54321);
let n = 1000;
let a_vec: Vec<F> = (0..n).map(|_| F::random(&mut rng)).collect();
let b_vec: Vec<F> = (0..n).map(|_| F::random(&mut rng)).collect();
let expected: F = a_vec.iter().zip(b_vec.iter()).map(|(a, b)| *a * *b).sum();
let mut acc = <F as DelayedReduction<F>>::Accumulator::default();
for (a, b) in a_vec.iter().zip(b_vec.iter()) {
<F as DelayedReduction<F>>::unreduced_multiply_accumulate(&mut acc, a, b);
}
let result = <F as DelayedReduction<F>>::reduce(&acc);
assert_eq!(
result, expected,
"Delayed reduction sum failed: accumulated result != direct sum"
);
}
#[cfg(test)]
#[macro_export]
macro_rules! test_delayed_reduction {
($mod_name:ident, $field:ty) => {
mod $mod_name {
#[test]
fn delayed_reduction_sum() {
$crate::big_num::delayed_reduction::test_delayed_reduction_sum_impl::<$field>();
}
}
};
}