use super::{EvaluationDomain, Evaluations};
use crate::util;
use dusk_bls12_381::Scalar;
use rand::Rng;
use rayon::iter::{
IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator,
};
use std::ops::{Add, AddAssign, Deref, DerefMut, Mul, Neg, Sub, SubAssign};
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Polynomial {
pub coeffs: Vec<Scalar>,
}
impl Deref for Polynomial {
type Target = [Scalar];
fn deref(&self) -> &[Scalar] {
&self.coeffs
}
}
impl DerefMut for Polynomial {
fn deref_mut(&mut self) -> &mut [Scalar] {
&mut self.coeffs
}
}
impl Polynomial {
pub fn zero() -> Self {
Self { coeffs: Vec::new() }
}
pub fn is_zero(&self) -> bool {
self.coeffs.is_empty() || self.coeffs.par_iter().all(|coeff| coeff == &Scalar::zero())
}
pub fn from_coefficients_slice(coeffs: &[Scalar]) -> Self {
Self::from_coefficients_vec(coeffs.to_vec())
}
pub fn from_coefficients_vec(coeffs: Vec<Scalar>) -> Self {
let mut result = Self { coeffs };
result.truncate_leading_zeros();
assert!(result
.coeffs
.last()
.map_or(true, |coeff| coeff != &Scalar::zero()));
result
}
pub fn degree(&self) -> usize {
if self.is_zero() {
return 0;
}
assert!(self
.coeffs
.last()
.map_or(false, |coeff| coeff != &Scalar::zero()));
self.coeffs.len() - 1
}
fn truncate_leading_zeros(&mut self) {
while self.coeffs.last().map_or(false, |c| c == &Scalar::zero()) {
self.coeffs.pop();
}
}
pub fn evaluate(&self, point: &Scalar) -> Scalar {
if self.is_zero() {
return Scalar::zero();
}
let powers = util::powers_of(point, self.len());
let p_evals: Vec<_> = self
.par_iter()
.zip(powers.into_par_iter())
.map(|(c, p)| p * c)
.collect();
let mut sum = Scalar::zero();
for eval in p_evals.into_iter() {
sum += &eval;
}
sum
}
pub fn rand<R: Rng>(d: usize, mut rng: &mut R) -> Self {
let mut random_coeffs = Vec::with_capacity(d + 1);
for _ in 0..=d {
random_coeffs.push(util::random_scalar(&mut rng));
}
Self::from_coefficients_vec(random_coeffs)
}
}
use std::iter::Sum;
impl Sum for Polynomial {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = Self>,
{
let sum: Polynomial = iter.fold(Polynomial::zero(), |mut res, val| {
res = &res + &val;
res
});
sum
}
}
impl<'a, 'b> Add<&'a Polynomial> for &'b Polynomial {
type Output = Polynomial;
fn add(self, other: &'a Polynomial) -> Polynomial {
let mut result = if self.is_zero() {
other.clone()
} else if other.is_zero() {
self.clone()
} else if self.degree() >= other.degree() {
let mut result = self.clone();
for (a, b) in result.coeffs.iter_mut().zip(&other.coeffs) {
*a += b
}
result
} else {
let mut result = other.clone();
for (a, b) in result.coeffs.iter_mut().zip(&self.coeffs) {
*a += b
}
result
};
result.truncate_leading_zeros();
result
}
}
#[allow(dead_code)]
fn iter_add(poly_a: &Polynomial, poly_b: &Polynomial) -> Polynomial {
if poly_a.len() == 0 {
return poly_b.clone();
}
if poly_b.len() == 0 {
return poly_a.clone();
}
let max_len = std::cmp::max(poly_a.len(), poly_b.len());
let min_len = std::cmp::min(poly_a.len(), poly_b.len());
let mut data = Vec::with_capacity(max_len);
let (mut poly_a_iter, mut poly_b_iter) = (poly_a.iter(), poly_b.iter());
let partial_addition = poly_a_iter
.by_ref()
.zip(poly_b_iter.by_ref())
.map(|(&a, &b)| a + b)
.take(min_len);
data.extend(partial_addition);
data.extend(poly_a_iter);
data.extend(poly_b_iter);
assert_eq!(data.len(), std::cmp::max(poly_a.len(), poly_b.len()));
let mut result = Polynomial::from_coefficients_vec(data);
result.truncate_leading_zeros();
result
}
impl<'a, 'b> AddAssign<&'a Polynomial> for Polynomial {
fn add_assign(&mut self, other: &'a Polynomial) {
if self.is_zero() {
self.coeffs.truncate(0);
self.coeffs.extend_from_slice(&other.coeffs);
} else if other.is_zero() {
} else if self.degree() >= other.degree() {
for (a, b) in self.coeffs.iter_mut().zip(&other.coeffs) {
*a += b
}
} else {
self.coeffs.resize(other.coeffs.len(), Scalar::zero());
for (a, b) in self.coeffs.iter_mut().zip(&other.coeffs) {
*a += b
}
self.truncate_leading_zeros();
}
}
}
impl<'a, 'b> AddAssign<(Scalar, &'a Polynomial)> for Polynomial {
fn add_assign(&mut self, (f, other): (Scalar, &'a Polynomial)) {
if self.is_zero() {
self.coeffs.truncate(0);
self.coeffs.extend_from_slice(&other.coeffs);
self.coeffs.iter_mut().for_each(|c| *c *= &f);
} else if other.is_zero() {
} else if self.degree() >= other.degree() {
for (a, b) in self.coeffs.iter_mut().zip(&other.coeffs) {
*a += &(f * b);
}
} else {
self.coeffs.resize(other.coeffs.len(), Scalar::zero());
for (a, b) in self.coeffs.iter_mut().zip(&other.coeffs) {
*a += &(f * b);
}
self.truncate_leading_zeros();
}
}
}
impl Neg for Polynomial {
type Output = Polynomial;
#[inline]
fn neg(mut self) -> Polynomial {
for coeff in &mut self.coeffs {
*coeff = -*coeff;
}
self
}
}
impl<'a, 'b> Sub<&'a Polynomial> for &'b Polynomial {
type Output = Polynomial;
#[inline]
fn sub(self, other: &'a Polynomial) -> Polynomial {
let mut result = if self.is_zero() {
let mut result = other.clone();
for coeff in &mut result.coeffs {
*coeff = -(*coeff);
}
result
} else if other.is_zero() {
self.clone()
} else if self.degree() >= other.degree() {
let mut result = self.clone();
for (a, b) in result.coeffs.iter_mut().zip(&other.coeffs) {
*a -= b
}
result
} else {
let mut result = self.clone();
result.coeffs.resize(other.coeffs.len(), Scalar::zero());
for (a, b) in result.coeffs.iter_mut().zip(&other.coeffs) {
*a -= b;
}
result
};
result.truncate_leading_zeros();
result
}
}
impl<'a, 'b> SubAssign<&'a Polynomial> for Polynomial {
#[inline]
fn sub_assign(&mut self, other: &'a Polynomial) {
if self.is_zero() {
self.coeffs.resize(other.coeffs.len(), Scalar::zero());
for (i, coeff) in other.coeffs.iter().enumerate() {
self.coeffs[i] -= coeff;
}
} else if other.is_zero() {
} else if self.degree() >= other.degree() {
for (a, b) in self.coeffs.iter_mut().zip(&other.coeffs) {
*a -= b
}
} else {
self.coeffs.resize(other.coeffs.len(), Scalar::zero());
for (a, b) in self.coeffs.iter_mut().zip(&other.coeffs) {
*a -= b
}
self.truncate_leading_zeros();
}
}
}
impl Polynomial {
#[allow(dead_code)]
#[inline]
fn leading_coefficient(&self) -> Option<&Scalar> {
self.last()
}
#[allow(dead_code)]
#[inline]
fn iter_with_index(&self) -> Vec<(usize, Scalar)> {
self.iter().cloned().enumerate().collect()
}
pub fn ruffini(&self, z: Scalar) -> Polynomial {
let mut quotient: Vec<Scalar> = Vec::with_capacity(self.degree());
let mut k = Scalar::zero();
for coeff in self.coeffs.iter().rev() {
let t = coeff + k;
quotient.push(t);
k = z * t;
}
quotient.pop();
quotient.reverse();
Polynomial::from_coefficients_vec(quotient)
}
}
impl<'a, 'b> Mul<&'a Polynomial> for &'b Polynomial {
type Output = Polynomial;
#[inline]
fn mul(self, other: &'a Polynomial) -> Polynomial {
if self.is_zero() || other.is_zero() {
Polynomial::zero()
} else {
let domain = EvaluationDomain::new(self.coeffs.len() + other.coeffs.len())
.expect("field is not smooth enough to construct domain");
let mut self_evals = Evaluations::from_vec_and_domain(domain.fft(&self.coeffs), domain);
let other_evals = Evaluations::from_vec_and_domain(domain.fft(&other.coeffs), domain);
self_evals *= &other_evals;
self_evals.interpolate()
}
}
}
impl<'a, 'b> Mul<&'a Scalar> for &'b Polynomial {
type Output = Polynomial;
#[inline]
fn mul(self, constant: &'a Scalar) -> Polynomial {
if self.is_zero() || (constant == &Scalar::zero()) {
return Polynomial::zero();
}
let scaled_coeffs: Vec<_> = self
.coeffs
.par_iter()
.map(|coeff| coeff * constant)
.collect();
Polynomial::from_coefficients_vec(scaled_coeffs)
}
}
impl<'a, 'b> Add<&'a Scalar> for &'b Polynomial {
type Output = Polynomial;
#[inline]
fn add(self, constant: &'a Scalar) -> Polynomial {
if self.is_zero() {
return Polynomial::from_coefficients_vec(vec![*constant]);
}
let mut result = self.clone();
if constant == &Scalar::zero() {
return result;
}
result[0] += constant;
result
}
}
impl<'a, 'b> Sub<&'a Scalar> for &'b Polynomial {
type Output = Polynomial;
#[inline]
fn sub(self, constant: &'a Scalar) -> Polynomial {
let negated_constant = -constant;
self + &negated_constant
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_ruffini() {
let quadratic = Polynomial::from_coefficients_vec(vec![
Scalar::from(4),
Scalar::from(4),
Scalar::one(),
]);
let quotient = quadratic.ruffini(-Scalar::from(2));
let expected_quotient =
Polynomial::from_coefficients_vec(vec![Scalar::from(2), Scalar::one()]);
assert_eq!(quotient, expected_quotient);
}
#[test]
fn test_ruffini_zero() {
let zero = Polynomial::zero();
let quotient = zero.ruffini(-Scalar::from(2));
assert_eq!(quotient, Polynomial::zero());
let p =
Polynomial::from_coefficients_vec(vec![Scalar::zero(), Scalar::one(), Scalar::one()]);
let quotient = p.ruffini(Scalar::zero());
let expected_quotient =
Polynomial::from_coefficients_vec(vec![Scalar::one(), Scalar::one()]);
assert_eq!(quotient, expected_quotient);
}
}