use ark_ff::FftField;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::rand::Rng;
use ark_std::{fmt, hash, vec::Vec};
#[cfg(feature = "parallel")]
use rayon::prelude::*;
pub mod general;
pub mod mixed_radix;
pub mod radix2;
pub(crate) mod utils;
pub use general::GeneralEvaluationDomain;
pub use mixed_radix::MixedRadixEvaluationDomain;
pub use radix2::Radix2EvaluationDomain;
pub trait EvaluationDomain<F: FftField>:
Copy + Clone + hash::Hash + Eq + PartialEq + fmt::Debug + CanonicalSerialize + CanonicalDeserialize
{
type Elements: Iterator<Item = F> + Sized;
fn sample_element_outside_domain<R: Rng>(&self, rng: &mut R) -> F {
let mut t = F::rand(rng);
while self.evaluate_vanishing_polynomial(t).is_zero() {
t = F::rand(rng);
}
t
}
fn new(num_coeffs: usize) -> Option<Self>;
fn compute_size_of_domain(num_coeffs: usize) -> Option<usize>;
fn size(&self) -> usize;
fn size_as_field_element(&self) -> F {
F::from(self.size() as u64)
}
#[inline]
fn fft<T: DomainCoeff<F>>(&self, coeffs: &[T]) -> Vec<T> {
let mut coeffs = coeffs.to_vec();
self.fft_in_place(&mut coeffs);
coeffs
}
fn fft_in_place<T: DomainCoeff<F>>(&self, coeffs: &mut Vec<T>);
#[inline]
fn ifft<T: DomainCoeff<F>>(&self, evals: &[T]) -> Vec<T> {
let mut evals = evals.to_vec();
self.ifft_in_place(&mut evals);
evals
}
fn ifft_in_place<T: DomainCoeff<F>>(&self, evals: &mut Vec<T>);
fn distribute_powers<T: DomainCoeff<F>>(coeffs: &mut [T], g: F) {
Self::distribute_powers_and_mul_by_const(coeffs, g, F::one());
}
#[cfg(not(feature = "parallel"))]
fn distribute_powers_and_mul_by_const<T: DomainCoeff<F>>(coeffs: &mut [T], g: F, c: F) {
let mut pow = c;
coeffs.iter_mut().for_each(|coeff| {
*coeff *= pow;
pow *= &g
})
}
#[cfg(feature = "parallel")]
fn distribute_powers_and_mul_by_const<T: DomainCoeff<F>>(coeffs: &mut [T], g: F, c: F) {
use ark_std::cmp::max;
let min_parallel_chunk_size = 1024;
let num_cpus_available = rayon::current_num_threads();
let num_elem_per_thread = max(coeffs.len() / num_cpus_available, min_parallel_chunk_size);
ark_std::cfg_chunks_mut!(coeffs, num_elem_per_thread)
.enumerate()
.for_each(|(i, chunk)| {
let offset = c * g.pow([(i * num_elem_per_thread) as u64]);
let mut pow = offset;
chunk.iter_mut().for_each(|coeff| {
*coeff *= pow;
pow *= &g
})
});
}
#[inline]
fn coset_fft<T: DomainCoeff<F>>(&self, coeffs: &[T]) -> Vec<T> {
let mut coeffs = coeffs.to_vec();
self.coset_fft_in_place(&mut coeffs);
coeffs
}
#[inline]
fn coset_fft_in_place<T: DomainCoeff<F>>(&self, coeffs: &mut Vec<T>) {
Self::distribute_powers(coeffs, F::multiplicative_generator());
self.fft_in_place(coeffs);
}
#[inline]
fn coset_ifft<T: DomainCoeff<F>>(&self, evals: &[T]) -> Vec<T> {
let mut evals = evals.to_vec();
self.coset_ifft_in_place(&mut evals);
evals
}
#[inline]
fn coset_ifft_in_place<T: DomainCoeff<F>>(&self, evals: &mut Vec<T>) {
self.ifft_in_place(evals);
Self::distribute_powers(evals, F::multiplicative_generator().inverse().unwrap());
}
fn evaluate_all_lagrange_coefficients(&self, tau: F) -> Vec<F>;
fn vanishing_polynomial(&self) -> crate::univariate::SparsePolynomial<F>;
fn evaluate_vanishing_polynomial(&self, tau: F) -> F;
fn element(&self, i: usize) -> F;
fn elements(&self) -> Self::Elements;
fn divide_by_vanishing_poly_on_coset_in_place(&self, evals: &mut [F]) {
let i = self
.evaluate_vanishing_polynomial(F::multiplicative_generator())
.inverse()
.unwrap();
ark_std::cfg_iter_mut!(evals).for_each(|eval| *eval *= &i);
}
fn reindex_by_subdomain(&self, other: Self, index: usize) -> usize {
assert!(self.size() >= other.size());
let period = self.size() / other.size();
if index < other.size() {
index * period
} else {
let i = index - other.size();
let x = period - 1;
i + (i / x) + 1
}
}
#[must_use]
fn mul_polynomials_in_evaluation_domain(&self, self_evals: &[F], other_evals: &[F]) -> Vec<F> {
assert_eq!(self_evals.len(), other_evals.len());
let mut result = self_evals.to_vec();
ark_std::cfg_iter_mut!(result)
.zip(other_evals)
.for_each(|(a, b)| *a *= b);
result
}
}
pub trait DomainCoeff<F: FftField>:
Copy
+ Send
+ Sync
+ core::ops::Add<Output = Self>
+ core::ops::Sub<Output = Self>
+ core::ops::AddAssign
+ core::ops::SubAssign
+ ark_ff::Zero
+ core::ops::MulAssign<F>
{
}
impl<T, F> DomainCoeff<F> for T
where
F: FftField,
T: Copy
+ Send
+ Sync
+ core::ops::Add<Output = Self>
+ core::ops::Sub<Output = Self>
+ core::ops::AddAssign
+ core::ops::SubAssign
+ ark_ff::Zero
+ core::ops::MulAssign<F>,
{
}