use super::field::element::FieldElement;
use crate::field::traits::{IsField, IsSubFieldOf};
use alloc::{borrow::ToOwned, vec, vec::Vec};
use core::{fmt::Display, ops};
pub mod dense_multilinear_poly;
mod error;
pub mod sparse_multilinear_poly;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Polynomial<FE> {
pub coefficients: Vec<FE>,
}
impl<F: IsField> Polynomial<FieldElement<F>> {
pub fn new(coefficients: &[FieldElement<F>]) -> Self {
let mut unpadded_coefficients = coefficients
.iter()
.rev()
.skip_while(|x| **x == FieldElement::zero())
.cloned()
.collect::<Vec<FieldElement<F>>>();
unpadded_coefficients.reverse();
Polynomial {
coefficients: unpadded_coefficients,
}
}
pub fn new_monomial(coefficient: FieldElement<F>, degree: usize) -> Self {
let mut coefficients = vec![FieldElement::zero(); degree];
coefficients.push(coefficient);
Self::new(&coefficients)
}
pub fn zero() -> Self {
Self::new(&[])
}
pub fn interpolate(
xs: &[FieldElement<F>],
ys: &[FieldElement<F>],
) -> Result<Self, InterpolateError> {
if xs.len() != ys.len() {
return Err(InterpolateError::UnequalLengths(xs.len(), ys.len()));
}
if xs.is_empty() {
return Ok(Polynomial::new(&[]));
}
let mut denominators = Vec::with_capacity(xs.len() * (xs.len() - 1) / 2);
let mut indexes = Vec::with_capacity(xs.len());
let mut idx = 0;
for (i, xi) in xs.iter().enumerate().skip(1) {
indexes.push(idx);
for xj in xs.iter().take(i) {
if xi == xj {
return Err(InterpolateError::NonUniqueXs);
}
denominators.push(xi - xj);
idx += 1;
}
}
FieldElement::inplace_batch_inverse(&mut denominators).unwrap();
let mut result = Polynomial::zero();
for (i, y) in ys.iter().enumerate() {
let mut y_term = Polynomial::new(&[y.clone()]);
for (j, x) in xs.iter().enumerate() {
if i == j {
continue;
}
let denominator = if i > j {
denominators[indexes[i - 1] + j].clone()
} else {
-&denominators[indexes[j - 1] + i]
};
let denominator_poly = Polynomial::new(&[denominator]);
let numerator = Polynomial::new(&[-x, FieldElement::one()]);
y_term = y_term.mul_with_ref(&(numerator * denominator_poly));
}
result = result + y_term;
}
Ok(result)
}
pub fn evaluate<E>(&self, x: &FieldElement<E>) -> FieldElement<E>
where
E: IsField,
F: IsSubFieldOf<E>,
{
self.coefficients
.iter()
.rev()
.fold(FieldElement::zero(), |acc, coeff| {
coeff + acc * x.to_owned()
})
}
pub fn evaluate_slice(&self, input: &[FieldElement<F>]) -> Vec<FieldElement<F>> {
input.iter().map(|x| self.evaluate(x)).collect()
}
pub fn degree(&self) -> usize {
if self.coefficients.is_empty() {
0
} else {
self.coefficients.len() - 1
}
}
pub fn leading_coefficient(&self) -> FieldElement<F> {
if let Some(coefficient) = self.coefficients.last() {
coefficient.clone()
} else {
FieldElement::zero()
}
}
pub fn coefficients(&self) -> &[FieldElement<F>] {
&self.coefficients
}
pub fn coeff_len(&self) -> usize {
self.coefficients().len()
}
pub fn ruffini_division_inplace(&mut self, b: &FieldElement<F>) {
let mut c = FieldElement::zero();
for coeff in self.coefficients.iter_mut().rev() {
*coeff = &*coeff + b * &c;
core::mem::swap(coeff, &mut c);
}
self.coefficients.pop();
}
pub fn ruffini_division<L>(&self, b: &FieldElement<L>) -> Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
if let Some(c) = self.coefficients.last() {
let mut c = c.clone().to_extension();
let mut coefficients = Vec::with_capacity(self.degree());
for coeff in self.coefficients.iter().rev().skip(1) {
coefficients.push(c.clone());
c = coeff + c * b;
}
coefficients = coefficients.into_iter().rev().collect();
Polynomial::new(&coefficients)
} else {
Polynomial::zero()
}
}
pub fn long_division_with_remainder(self, dividend: &Self) -> (Self, Self) {
if dividend.degree() > self.degree() {
(Polynomial::zero(), self)
} else {
let mut n = self;
let mut q: Vec<FieldElement<F>> = vec![FieldElement::zero(); n.degree() + 1];
let denominator = dividend.leading_coefficient().inv().unwrap();
while n != Polynomial::zero() && n.degree() >= dividend.degree() {
let new_coefficient = n.leading_coefficient() * &denominator;
q[n.degree() - dividend.degree()] = new_coefficient.clone();
let d = dividend.mul_with_ref(&Polynomial::new_monomial(
new_coefficient,
n.degree() - dividend.degree(),
));
n = n - d;
}
(Polynomial::new(&q), n)
}
}
pub fn div_with_ref(self, dividend: &Self) -> Self {
let (quotient, _remainder) = self.long_division_with_remainder(dividend);
quotient
}
pub fn mul_with_ref(&self, factor: &Self) -> Self {
let degree = self.degree() + factor.degree();
let mut coefficients = vec![FieldElement::zero(); degree + 1];
if self.coefficients.is_empty() || factor.coefficients.is_empty() {
Polynomial::new(&[FieldElement::zero()])
} else {
for i in 0..=factor.degree() {
for j in 0..=self.degree() {
coefficients[i + j] += &factor.coefficients[i] * &self.coefficients[j];
}
}
Polynomial::new(&coefficients)
}
}
pub fn scale<S: IsSubFieldOf<F>>(&self, factor: &FieldElement<S>) -> Self {
let scaled_coefficients = self
.coefficients
.iter()
.zip(core::iter::successors(Some(FieldElement::one()), |x| {
Some(x * factor)
}))
.map(|(coeff, power)| power * coeff)
.collect();
Self {
coefficients: scaled_coefficients,
}
}
pub fn scale_coeffs(&self, factor: &FieldElement<F>) -> Self {
let scaled_coefficients = self
.coefficients
.iter()
.map(|coeff| factor * coeff)
.collect();
Self {
coefficients: scaled_coefficients,
}
}
pub fn break_in_parts(&self, number_of_parts: usize) -> Vec<Self> {
let coef = self.coefficients();
let mut parts: Vec<Self> = Vec::with_capacity(number_of_parts);
for i in 0..number_of_parts {
let coeffs: Vec<_> = coef
.iter()
.skip(i)
.step_by(number_of_parts)
.cloned()
.collect();
parts.push(Polynomial::new(&coeffs));
}
parts
}
pub fn to_extension<L: IsField>(self) -> Polynomial<FieldElement<L>>
where
F: IsSubFieldOf<L>,
{
Polynomial {
coefficients: self
.coefficients
.into_iter()
.map(|x| x.to_extension::<L>())
.collect(),
}
}
}
pub fn pad_with_zero_coefficients_to_length<F: IsField>(
pa: &mut Polynomial<FieldElement<F>>,
n: usize,
) {
pa.coefficients.resize(n, FieldElement::zero());
}
pub fn pad_with_zero_coefficients<L: IsField, F: IsSubFieldOf<L>>(
pa: &Polynomial<FieldElement<F>>,
pb: &Polynomial<FieldElement<L>>,
) -> (Polynomial<FieldElement<F>>, Polynomial<FieldElement<L>>) {
let mut pa = pa.clone();
let mut pb = pb.clone();
if pa.coefficients.len() > pb.coefficients.len() {
pad_with_zero_coefficients_to_length(&mut pb, pa.coefficients.len());
} else {
pad_with_zero_coefficients_to_length(&mut pa, pb.coefficients.len());
}
(pa, pb)
}
pub fn compose<F>(
poly_1: &Polynomial<FieldElement<F>>,
poly_2: &Polynomial<FieldElement<F>>,
) -> Polynomial<FieldElement<F>>
where
F: IsField,
{
let max_degree: u64 = (poly_1.degree() * poly_2.degree()) as u64;
let mut interpolation_points = vec![];
for i in 0_u64..max_degree + 1 {
interpolation_points.push(FieldElement::<F>::from(i));
}
let values: Vec<_> = interpolation_points
.iter()
.map(|value| {
let intermediate_value = poly_2.evaluate(value);
poly_1.evaluate(&intermediate_value)
})
.collect();
Polynomial::interpolate(interpolation_points.as_slice(), values.as_slice())
.expect("xs and ys have equal length and xs are unique")
}
impl<F, L> ops::Add<&Polynomial<FieldElement<L>>> for &Polynomial<FieldElement<F>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, a_polynomial: &Polynomial<FieldElement<L>>) -> Self::Output {
let (pa, pb) = pad_with_zero_coefficients(self, a_polynomial);
let iter_coeff_pa = pa.coefficients.iter();
let iter_coeff_pb = pb.coefficients.iter();
let new_coefficients = iter_coeff_pa.zip(iter_coeff_pb).map(|(x, y)| x + y);
let new_coefficients_vec = new_coefficients.collect::<Vec<FieldElement<L>>>();
Polynomial::new(&new_coefficients_vec)
}
}
impl<F, L> ops::Add<Polynomial<FieldElement<L>>> for Polynomial<FieldElement<F>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, a_polynomial: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&self + &a_polynomial
}
}
impl<F, L> ops::Add<&Polynomial<FieldElement<L>>> for Polynomial<FieldElement<F>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, a_polynomial: &Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&self + a_polynomial
}
}
impl<F, L> ops::Add<Polynomial<FieldElement<L>>> for &Polynomial<FieldElement<F>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, a_polynomial: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
self + &a_polynomial
}
}
impl<F: IsField> ops::Neg for &Polynomial<FieldElement<F>> {
type Output = Polynomial<FieldElement<F>>;
fn neg(self) -> Polynomial<FieldElement<F>> {
let neg = self
.coefficients
.iter()
.map(|x| -x)
.collect::<Vec<FieldElement<F>>>();
Polynomial::new(&neg)
}
}
impl<F: IsField> ops::Neg for Polynomial<FieldElement<F>> {
type Output = Polynomial<FieldElement<F>>;
fn neg(self) -> Polynomial<FieldElement<F>> {
-&self
}
}
impl<F, L> ops::Sub<&Polynomial<FieldElement<L>>> for &Polynomial<FieldElement<F>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, substrahend: &Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
self + (-substrahend)
}
}
impl<F, L> ops::Sub<Polynomial<FieldElement<L>>> for Polynomial<FieldElement<F>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, substrahend: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&self - &substrahend
}
}
impl<F, L> ops::Sub<&Polynomial<FieldElement<L>>> for Polynomial<FieldElement<F>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, substrahend: &Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&self - substrahend
}
}
impl<F, L> ops::Sub<Polynomial<FieldElement<L>>> for &Polynomial<FieldElement<F>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, substrahend: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
self - &substrahend
}
}
impl<F> ops::Div<Polynomial<FieldElement<F>>> for Polynomial<FieldElement<F>>
where
F: IsField,
{
type Output = Polynomial<FieldElement<F>>;
fn div(self, dividend: Polynomial<FieldElement<F>>) -> Polynomial<FieldElement<F>> {
self.div_with_ref(÷nd)
}
}
impl<F: IsField> ops::Mul<&Polynomial<FieldElement<F>>> for &Polynomial<FieldElement<F>> {
type Output = Polynomial<FieldElement<F>>;
fn mul(self, factor: &Polynomial<FieldElement<F>>) -> Polynomial<FieldElement<F>> {
self.mul_with_ref(factor)
}
}
impl<F: IsField> ops::Mul<Polynomial<FieldElement<F>>> for Polynomial<FieldElement<F>> {
type Output = Polynomial<FieldElement<F>>;
fn mul(self, factor: Polynomial<FieldElement<F>>) -> Polynomial<FieldElement<F>> {
&self * &factor
}
}
impl<F: IsField> ops::Mul<Polynomial<FieldElement<F>>> for &Polynomial<FieldElement<F>> {
type Output = Polynomial<FieldElement<F>>;
fn mul(self, factor: Polynomial<FieldElement<F>>) -> Polynomial<FieldElement<F>> {
self * &factor
}
}
impl<F: IsField> ops::Mul<&Polynomial<FieldElement<F>>> for Polynomial<FieldElement<F>> {
type Output = Polynomial<FieldElement<F>>;
fn mul(self, factor: &Polynomial<FieldElement<F>>) -> Polynomial<FieldElement<F>> {
&self * factor
}
}
impl<F, L> ops::Mul<FieldElement<F>> for Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn mul(self, multiplicand: FieldElement<F>) -> Polynomial<FieldElement<L>> {
let new_coefficients = self
.coefficients
.iter()
.map(|value| &multiplicand * value)
.collect();
Polynomial {
coefficients: new_coefficients,
}
}
}
impl<F, L> ops::Mul<&FieldElement<F>> for &Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn mul(self, multiplicand: &FieldElement<F>) -> Polynomial<FieldElement<L>> {
self.clone() * multiplicand.clone()
}
}
impl<F, L> ops::Mul<FieldElement<F>> for &Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn mul(self, multiplicand: FieldElement<F>) -> Polynomial<FieldElement<L>> {
self * &multiplicand
}
}
impl<F, L> ops::Mul<&FieldElement<F>> for Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn mul(self, multiplicand: &FieldElement<F>) -> Polynomial<FieldElement<L>> {
&self * multiplicand
}
}
impl<F, L> ops::Mul<&Polynomial<FieldElement<L>>> for &FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn mul(self, multiplicand: &Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
multiplicand * self
}
}
impl<F, L> ops::Mul<Polynomial<FieldElement<L>>> for &FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn mul(self, multiplicand: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&multiplicand * self
}
}
impl<F, L> ops::Mul<&Polynomial<FieldElement<L>>> for FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn mul(self, multiplicand: &Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
multiplicand * self
}
}
impl<F, L> ops::Mul<Polynomial<FieldElement<L>>> for FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn mul(self, multiplicand: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&multiplicand * &self
}
}
impl<F, L> ops::Add<&FieldElement<F>> for &Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, other: &FieldElement<F>) -> Polynomial<FieldElement<L>> {
Polynomial::new_monomial(other.clone(), 0) + self
}
}
impl<F, L> ops::Add<FieldElement<F>> for Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, other: FieldElement<F>) -> Polynomial<FieldElement<L>> {
&self + &other
}
}
impl<F, L> ops::Add<FieldElement<F>> for &Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, other: FieldElement<F>) -> Polynomial<FieldElement<L>> {
self + &other
}
}
impl<F, L> ops::Add<&FieldElement<F>> for Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, other: &FieldElement<F>) -> Polynomial<FieldElement<L>> {
&self + other
}
}
impl<F, L> ops::Add<&Polynomial<FieldElement<L>>> for &FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, other: &Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
Polynomial::new_monomial(self.clone(), 0) + other
}
}
impl<F, L> ops::Add<Polynomial<FieldElement<L>>> for FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, other: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&self + &other
}
}
impl<F, L> ops::Add<Polynomial<FieldElement<L>>> for &FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, other: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
self + &other
}
}
impl<F, L> ops::Add<&Polynomial<FieldElement<L>>> for FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn add(self, other: &Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&self + other
}
}
impl<F, L> ops::Sub<&FieldElement<F>> for &Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, other: &FieldElement<F>) -> Polynomial<FieldElement<L>> {
-Polynomial::new_monomial(other.clone(), 0) + self
}
}
impl<F, L> ops::Sub<FieldElement<F>> for Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, other: FieldElement<F>) -> Polynomial<FieldElement<L>> {
&self - &other
}
}
impl<F, L> ops::Sub<FieldElement<F>> for &Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, other: FieldElement<F>) -> Polynomial<FieldElement<L>> {
self - &other
}
}
impl<F, L> ops::Sub<&FieldElement<F>> for Polynomial<FieldElement<L>>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, other: &FieldElement<F>) -> Polynomial<FieldElement<L>> {
&self - other
}
}
impl<F, L> ops::Sub<&Polynomial<FieldElement<L>>> for &FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, other: &Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
Polynomial::new_monomial(self.clone(), 0) - other
}
}
impl<F, L> ops::Sub<Polynomial<FieldElement<L>>> for FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, other: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&self - &other
}
}
impl<F, L> ops::Sub<Polynomial<FieldElement<L>>> for &FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, other: Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
self - &other
}
}
impl<F, L> ops::Sub<&Polynomial<FieldElement<L>>> for FieldElement<F>
where
L: IsField,
F: IsSubFieldOf<L>,
{
type Output = Polynomial<FieldElement<L>>;
fn sub(self, other: &Polynomial<FieldElement<L>>) -> Polynomial<FieldElement<L>> {
&self - other
}
}
#[derive(Debug)]
pub enum InterpolateError {
UnequalLengths(usize, usize),
NonUniqueXs,
}
impl Display for InterpolateError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
InterpolateError::UnequalLengths(x, y) => {
write!(f, "xs and ys must be the same length. Got: {x} != {y}")
}
InterpolateError::NonUniqueXs => write!(f, "xs values should be unique."),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for InterpolateError {}
#[cfg(test)]
mod tests {
use crate::field::fields::u64_prime_field::U64PrimeField;
use super::*;
const ORDER: u64 = 23;
type F = U64PrimeField<ORDER>;
type FE = FieldElement<F>;
fn polynomial_a() -> Polynomial<FE> {
Polynomial::new(&[FE::new(1), FE::new(2), FE::new(3)])
}
fn polynomial_minus_a() -> Polynomial<FE> {
Polynomial::new(&[FE::new(ORDER - 1), FE::new(ORDER - 2), FE::new(ORDER - 3)])
}
fn polynomial_b() -> Polynomial<FE> {
Polynomial::new(&[FE::new(3), FE::new(4), FE::new(5)])
}
fn polynomial_a_plus_b() -> Polynomial<FE> {
Polynomial::new(&[FE::new(4), FE::new(6), FE::new(8)])
}
fn polynomial_b_minus_a() -> Polynomial<FE> {
Polynomial::new(&[FE::new(2), FE::new(2), FE::new(2)])
}
#[test]
fn adding_a_and_b_equals_a_plus_b() {
assert_eq!(polynomial_a() + polynomial_b(), polynomial_a_plus_b());
}
#[test]
fn adding_a_and_a_plus_b_does_not_equal_b() {
assert_ne!(polynomial_a() + polynomial_a_plus_b(), polynomial_b());
}
#[test]
fn add_5_to_0_is_5() {
let p1 = Polynomial::new(&[FE::new(5)]);
let p2 = Polynomial::new(&[FE::new(0)]);
assert_eq!(p1 + p2, Polynomial::new(&[FE::new(5)]));
}
#[test]
fn add_0_to_5_is_5() {
let p1 = Polynomial::new(&[FE::new(5)]);
let p2 = Polynomial::new(&[FE::new(0)]);
assert_eq!(p2 + p1, Polynomial::new(&[FE::new(5)]));
}
#[test]
fn negating_0_returns_0() {
let p1 = Polynomial::new(&[FE::new(0)]);
assert_eq!(-p1, Polynomial::new(&[FE::new(0)]));
}
#[test]
fn negating_a_is_equal_to_minus_a() {
assert_eq!(-polynomial_a(), polynomial_minus_a());
}
#[test]
fn negating_a_is_not_equal_to_a() {
assert_ne!(-polynomial_a(), polynomial_a());
}
#[test]
fn substracting_5_5_gives_0() {
let p1 = Polynomial::new(&[FE::new(5)]);
let p2 = Polynomial::new(&[FE::new(5)]);
let p3 = Polynomial::new(&[FE::new(0)]);
assert_eq!(p1 - p2, p3);
}
#[test]
fn substracting_b_and_a_equals_b_minus_a() {
assert_eq!(polynomial_b() - polynomial_a(), polynomial_b_minus_a());
}
#[test]
fn constructor_removes_zeros_at_the_end_of_polynomial() {
let p1 = Polynomial::new(&[FE::new(3), FE::new(4), FE::new(0)]);
assert_eq!(p1.coefficients, &[FE::new(3), FE::new(4)]);
}
#[test]
fn pad_with_zero_coefficients_returns_polynomials_with_zeros_until_matching_size() {
let p1 = Polynomial::new(&[FE::new(3), FE::new(4)]);
let p2 = Polynomial::new(&[FE::new(3)]);
assert_eq!(p2.coefficients, &[FE::new(3)]);
let (pp1, pp2) = pad_with_zero_coefficients(&p1, &p2);
assert_eq!(pp1, p1);
assert_eq!(pp2.coefficients, &[FE::new(3), FE::new(0)]);
}
#[test]
fn multiply_5_and_0_is_0() {
let p1 = Polynomial::new(&[FE::new(5)]);
let p2 = Polynomial::new(&[FE::new(0)]);
assert_eq!(p1 * p2, Polynomial::new(&[FE::new(0)]));
}
#[test]
fn multiply_0_and_x_is_0() {
let p1 = Polynomial::new(&[FE::new(0)]);
let p2 = Polynomial::new(&[FE::new(0), FE::new(1)]);
assert_eq!(p1 * p2, Polynomial::new(&[FE::new(0)]));
}
#[test]
fn multiply_2_by_3_is_6() {
let p1 = Polynomial::new(&[FE::new(2)]);
let p2 = Polynomial::new(&[FE::new(3)]);
assert_eq!(p1 * p2, Polynomial::new(&[FE::new(6)]));
}
#[test]
fn multiply_2xx_3x_3_times_x_4() {
let p1 = Polynomial::new(&[FE::new(3), FE::new(3), FE::new(2)]);
let p2 = Polynomial::new(&[FE::new(4), FE::new(1)]);
assert_eq!(
p1 * p2,
Polynomial::new(&[FE::new(12), FE::new(15), FE::new(11), FE::new(2)])
);
}
#[test]
fn multiply_x_4_times_2xx_3x_3() {
let p1 = Polynomial::new(&[FE::new(3), FE::new(3), FE::new(2)]);
let p2 = Polynomial::new(&[FE::new(4), FE::new(1)]);
assert_eq!(
p2 * p1,
Polynomial::new(&[FE::new(12), FE::new(15), FE::new(11), FE::new(2)])
);
}
#[test]
fn division_works() {
let p1 = Polynomial::new(&[FE::new(1), FE::new(3)]);
let p2 = Polynomial::new(&[FE::new(1), FE::new(3)]);
let p3 = p1.mul_with_ref(&p2);
assert_eq!(p3 / p2, p1);
}
#[test]
fn division_by_zero_degree_polynomial_works() {
let four = FE::new(4);
let two = FE::new(2);
let p1 = Polynomial::new(&[four, four]);
let p2 = Polynomial::new(&[two]);
assert_eq!(Polynomial::new(&[two, two]), p1 / p2);
}
#[test]
fn evaluate_constant_polynomial_returns_constant() {
let three = FE::new(3);
let p = Polynomial::new(&[three]);
assert_eq!(p.evaluate(&FE::new(10)), three);
}
#[test]
fn evaluate_slice() {
let three = FE::new(3);
let p = Polynomial::new(&[three]);
let ret = p.evaluate_slice(&[FE::new(10), FE::new(15)]);
assert_eq!(ret, [three, three]);
}
#[test]
fn create_degree_0_new_monomial() {
assert_eq!(
Polynomial::new_monomial(FE::new(3), 0),
Polynomial::new(&[FE::new(3)])
);
}
#[test]
fn zero_poly_evals_0_in_3() {
assert_eq!(
Polynomial::new_monomial(FE::new(0), 0).evaluate(&FE::new(3)),
FE::new(0)
);
}
#[test]
fn evaluate_degree_1_new_monomial() {
let two = FE::new(2);
let four = FE::new(4);
let p = Polynomial::new_monomial(two, 1);
assert_eq!(p.evaluate(&two), four);
}
#[test]
fn evaluate_degree_2_monomyal() {
let two = FE::new(2);
let eight = FE::new(8);
let p = Polynomial::new_monomial(two, 2);
assert_eq!(p.evaluate(&two), eight);
}
#[test]
fn evaluate_3_term_polynomial() {
let p = Polynomial::new(&[FE::new(3), -FE::new(2), FE::new(4)]);
assert_eq!(p.evaluate(&FE::new(2)), FE::new(15));
}
#[test]
fn simple_interpolating_polynomial_by_hand_works() {
let denominator = Polynomial::new(&[FE::new(1) / (FE::new(2) - FE::new(4))]);
let numerator = Polynomial::new(&[-FE::new(4), FE::new(1)]);
let interpolating = numerator * denominator;
assert_eq!(
(FE::new(2) - FE::new(4)) * (FE::new(1) / (FE::new(2) - FE::new(4))),
FE::new(1)
);
assert_eq!(interpolating.evaluate(&FE::new(2)), FE::new(1));
assert_eq!(interpolating.evaluate(&FE::new(4)), FE::new(0));
}
#[test]
fn interpolate_x_2_y_3() {
let p = Polynomial::interpolate(&[FE::new(2)], &[FE::new(3)]).unwrap();
assert_eq!(FE::new(3), p.evaluate(&FE::new(2)));
}
#[test]
fn interpolate_x_0_2_y_3_4() {
let p =
Polynomial::interpolate(&[FE::new(0), FE::new(2)], &[FE::new(3), FE::new(4)]).unwrap();
assert_eq!(FE::new(3), p.evaluate(&FE::new(0)));
assert_eq!(FE::new(4), p.evaluate(&FE::new(2)));
}
#[test]
fn interpolate_x_2_5_7_y_10_19_43() {
let p = Polynomial::interpolate(
&[FE::new(2), FE::new(5), FE::new(7)],
&[FE::new(10), FE::new(19), FE::new(43)],
)
.unwrap();
assert_eq!(FE::new(10), p.evaluate(&FE::new(2)));
assert_eq!(FE::new(19), p.evaluate(&FE::new(5)));
assert_eq!(FE::new(43), p.evaluate(&FE::new(7)));
}
#[test]
fn interpolate_x_0_0_y_1_1() {
let p =
Polynomial::interpolate(&[FE::new(0), FE::new(1)], &[FE::new(0), FE::new(1)]).unwrap();
assert_eq!(FE::new(0), p.evaluate(&FE::new(0)));
assert_eq!(FE::new(1), p.evaluate(&FE::new(1)));
}
#[test]
fn interpolate_x_0_y_0() {
let p = Polynomial::interpolate(&[FE::new(0)], &[FE::new(0)]).unwrap();
assert_eq!(FE::new(0), p.evaluate(&FE::new(0)));
}
#[test]
fn composition_works() {
let p = Polynomial::new(&[FE::new(0), FE::new(2)]);
let q = Polynomial::new(&[FE::new(0), FE::new(0), FE::new(1)]);
assert_eq!(
compose(&p, &q),
Polynomial::new(&[FE::new(0), FE::new(0), FE::new(2)])
);
}
#[test]
fn break_in_parts() {
let p = Polynomial::new(&[FE::new(1), FE::new(2), FE::new(1), FE::new(3)]);
let p0_expected = Polynomial::new(&[FE::new(1), FE::new(1)]);
let p1_expected = Polynomial::new(&[FE::new(2), FE::new(3)]);
let parts = p.break_in_parts(2);
assert_eq!(parts.len(), 2);
let p0 = &parts[0];
let p1 = &parts[1];
assert_eq!(p0, &p0_expected);
assert_eq!(p1, &p1_expected);
}
use alloc::format;
use proptest::prelude::*;
proptest! {
#[test]
fn ruffini_inplace_equals_division(p in any::<Vec<u64>>(), b in any::<u64>()) {
let p: Vec<_> = p.into_iter().map(FE::from).collect();
let mut p = Polynomial::new(&p);
let b = FE::from(b);
let p_ref = p.clone();
let m = Polynomial::new_monomial(FE::one(), 1) - b;
p.ruffini_division_inplace(&b);
prop_assert_eq!(p, p_ref / m);
}
}
proptest! {
#[test]
fn ruffini_inplace_equals_ruffini(p in any::<Vec<u64>>(), b in any::<u64>()) {
let p: Vec<_> = p.into_iter().map(FE::from).collect();
let mut p = Polynomial::new(&p);
let b = FE::from(b);
let q = p.ruffini_division(&b);
p.ruffini_division_inplace(&b);
prop_assert_eq!(q, p);
}
}
}