diff --git a/benches/ffts.rs b/benches/ffts.rs index 8492cfe9..745d53a8 100644 --- a/benches/ffts.rs +++ b/benches/ffts.rs @@ -1,7 +1,7 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use plonky2::field::field_types::Field; use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::polynomial::polynomial::PolynomialCoeffs; +use plonky2::polynomial::PolynomialCoeffs; use tynm::type_name; pub(crate) fn bench_ffts(c: &mut Criterion) { diff --git a/src/bin/bench_ldes.rs b/src/bin/bench_ldes.rs index d121831b..dbcfa6df 100644 --- a/src/bin/bench_ldes.rs +++ b/src/bin/bench_ldes.rs @@ -2,7 +2,7 @@ use std::time::Instant; use plonky2::field::field_types::Field; use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::polynomial::polynomial::PolynomialValues; +use plonky2::polynomial::PolynomialValues; use rayon::prelude::*; type F = GoldilocksField; diff --git a/src/field/fft.rs b/src/field/fft.rs index 89b6844d..6f5155a4 100644 --- a/src/field/fft.rs +++ b/src/field/fft.rs @@ -6,7 +6,7 @@ use unroll::unroll_for_loops; use crate::field::field_types::Field; use crate::field::packable::Packable; use crate::field::packed_field::{PackedField, Singleton}; -use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::util::{log2_strict, reverse_index_bits}; pub(crate) type FftRootTable = Vec>; @@ -213,7 +213,7 @@ mod tests { use crate::field::fft::{fft, fft_with_options, ifft}; use crate::field::field_types::Field; use crate::field::goldilocks_field::GoldilocksField; - use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; + use crate::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::util::{log2_ceil, log2_strict}; #[test] diff --git a/src/field/interpolation.rs b/src/field/interpolation.rs index c4a49fe1..ad3ddf72 100644 --- a/src/field/interpolation.rs +++ b/src/field/interpolation.rs @@ -1,6 +1,6 @@ use crate::field::fft::ifft; use crate::field::field_types::Field; -use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::util::log2_ceil; /// Computes the unique degree < n interpolant of an arbitrary list of n (point, value) pairs. @@ -80,7 +80,7 @@ mod tests { use crate::field::extension_field::quartic::QuarticExtension; use crate::field::field_types::Field; use crate::field::goldilocks_field::GoldilocksField; - use crate::polynomial::polynomial::PolynomialCoeffs; + use crate::polynomial::PolynomialCoeffs; #[test] fn interpolant_random() { diff --git a/src/fri/commitment.rs b/src/fri/commitment.rs index ee2e66eb..c8506356 100644 --- a/src/fri/commitment.rs +++ b/src/fri/commitment.rs @@ -10,7 +10,7 @@ use crate::iop::challenger::Challenger; use crate::plonk::circuit_data::CommonCircuitData; use crate::plonk::plonk_common::PlonkPolynomials; use crate::plonk::proof::OpeningSet; -use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::timed; use crate::util::reducing::ReducingFactor; use crate::util::timing::TimingTree; diff --git a/src/fri/proof.rs b/src/fri/proof.rs index f6875fcc..72cf594a 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -15,7 +15,7 @@ use crate::iop::target::Target; use crate::plonk::circuit_data::CommonCircuitData; use crate::plonk::plonk_common::PolynomialsIndexBlinding; use crate::plonk::proof::{FriInferredElements, ProofChallenges}; -use crate::polynomial::polynomial::PolynomialCoeffs; +use crate::polynomial::PolynomialCoeffs; /// Evaluations and Merkle proof produced by the prover in a FRI query step. #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] diff --git a/src/fri/prover.rs b/src/fri/prover.rs index ec58fc14..90ef8cfe 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -10,7 +10,7 @@ use crate::hash::merkle_tree::MerkleTree; use crate::iop::challenger::Challenger; use crate::plonk::circuit_data::CommonCircuitData; use crate::plonk::plonk_common::reduce_with_powers; -use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::timed; use crate::util::reverse_index_bits_in_place; use crate::util::timing::TimingTree; diff --git a/src/gates/gate_testing.rs b/src/gates/gate_testing.rs index 9fe8e835..3fceded4 100644 --- a/src/gates/gate_testing.rs +++ b/src/gates/gate_testing.rs @@ -9,7 +9,7 @@ use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::plonk::verifier::verify; -use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::util::{log2_ceil, transpose}; const WITNESS_SIZE: usize = 1 << 5; diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index 3ebf7259..d2142ee1 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -15,7 +15,7 @@ use crate::iop::wire::Wire; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; -use crate::polynomial::polynomial::PolynomialCoeffs; +use crate::polynomial::PolynomialCoeffs; /// Interpolation gate with constraints of degree at most `1<, const D: usize>( diff --git a/src/plonk/permutation_argument.rs b/src/plonk/permutation_argument.rs index 28a07dff..ca3977ce 100644 --- a/src/plonk/permutation_argument.rs +++ b/src/plonk/permutation_argument.rs @@ -5,7 +5,7 @@ use rayon::prelude::*; use crate::field::field_types::Field; use crate::iop::target::Target; use crate::iop::wire::Wire; -use crate::polynomial::polynomial::PolynomialValues; +use crate::polynomial::PolynomialValues; /// Disjoint Set Forest data-structure following https://en.wikipedia.org/wiki/Disjoint-set_data_structure. pub struct Forest { diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 3f8e607d..4f281f06 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -17,7 +17,7 @@ use crate::plonk::plonk_common::ZeroPolyOnCoset; use crate::plonk::proof::{Proof, ProofWithPublicInputs}; use crate::plonk::vanishing_poly::eval_vanishing_poly_base_batch; use crate::plonk::vars::EvaluationVarsBase; -use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::timed; use crate::util::partial_products::{partial_products_and_z_gx, quotient_chunk_products}; use crate::util::timing::TimingTree; diff --git a/src/polynomial/division.rs b/src/polynomial/division.rs index b5dad629..671b7715 100644 --- a/src/polynomial/division.rs +++ b/src/polynomial/division.rs @@ -1,5 +1,5 @@ use crate::field::field_types::Field; -use crate::polynomial::polynomial::PolynomialCoeffs; +use crate::polynomial::PolynomialCoeffs; use crate::util::log2_ceil; impl PolynomialCoeffs { @@ -129,7 +129,7 @@ mod tests { use crate::field::extension_field::quartic::QuarticExtension; use crate::field::field_types::Field; use crate::field::goldilocks_field::GoldilocksField; - use crate::polynomial::polynomial::PolynomialCoeffs; + use crate::polynomial::PolynomialCoeffs; #[test] #[ignore] diff --git a/src/polynomial/mod.rs b/src/polynomial/mod.rs index 2798b1f2..1a7b90fe 100644 --- a/src/polynomial/mod.rs +++ b/src/polynomial/mod.rs @@ -1,4 +1,616 @@ -#![allow(clippy::module_inception)] - pub(crate) mod division; -pub mod polynomial; + +use std::cmp::max; +use std::iter::Sum; +use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; + +use anyhow::{ensure, Result}; +use serde::{Deserialize, Serialize}; + +use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::fft::{fft, fft_with_options, ifft, FftRootTable}; +use crate::field::field_types::Field; +use crate::util::log2_strict; + +/// A polynomial in point-value form. +/// +/// The points are implicitly `g^i`, where `g` generates the subgroup whose size equals the number +/// of points. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PolynomialValues { + pub values: Vec, +} + +impl PolynomialValues { + pub fn new(values: Vec) -> Self { + PolynomialValues { values } + } + + /// The number of values stored. + pub(crate) fn len(&self) -> usize { + self.values.len() + } + + pub fn ifft(&self) -> PolynomialCoeffs { + ifft(self) + } + + /// Returns the polynomial whose evaluation on the coset `shift*H` is `self`. + pub fn coset_ifft(&self, shift: F) -> PolynomialCoeffs { + let mut shifted_coeffs = self.ifft(); + shifted_coeffs + .coeffs + .iter_mut() + .zip(shift.inverse().powers()) + .for_each(|(c, r)| { + *c *= r; + }); + shifted_coeffs + } + + pub fn lde_multiple(polys: Vec, rate_bits: usize) -> Vec { + polys.into_iter().map(|p| p.lde(rate_bits)).collect() + } + + pub fn lde(&self, rate_bits: usize) -> Self { + let coeffs = ifft(self).lde(rate_bits); + fft_with_options(&coeffs, Some(rate_bits), None) + } + + pub fn degree(&self) -> usize { + self.degree_plus_one() + .checked_sub(1) + .expect("deg(0) is undefined") + } + + pub fn degree_plus_one(&self) -> usize { + self.ifft().degree_plus_one() + } +} + +impl From> for PolynomialValues { + fn from(values: Vec) -> Self { + Self::new(values) + } +} + +/// A polynomial in coefficient form. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct PolynomialCoeffs { + pub(crate) coeffs: Vec, +} + +impl PolynomialCoeffs { + pub fn new(coeffs: Vec) -> Self { + PolynomialCoeffs { coeffs } + } + + pub(crate) fn empty() -> Self { + Self::new(Vec::new()) + } + + pub(crate) fn zero(len: usize) -> Self { + Self::new(vec![F::ZERO; len]) + } + + pub(crate) fn is_zero(&self) -> bool { + self.coeffs.iter().all(|x| x.is_zero()) + } + + /// The number of coefficients. This does not filter out any zero coefficients, so it is not + /// necessarily related to the degree. + pub fn len(&self) -> usize { + self.coeffs.len() + } + + pub fn log_len(&self) -> usize { + log2_strict(self.len()) + } + + pub(crate) fn chunks(&self, chunk_size: usize) -> Vec { + self.coeffs + .chunks(chunk_size) + .map(|chunk| PolynomialCoeffs::new(chunk.to_vec())) + .collect() + } + + pub fn eval(&self, x: F) -> F { + self.coeffs + .iter() + .rev() + .fold(F::ZERO, |acc, &c| acc * x + c) + } + + /// Evaluate the polynomial at a point given its powers. The first power is the point itself, not 1. + pub fn eval_with_powers(&self, powers: &[F]) -> F { + debug_assert_eq!(self.coeffs.len(), powers.len() + 1); + let acc = self.coeffs[0]; + self.coeffs[1..] + .iter() + .zip(powers) + .fold(acc, |acc, (&x, &c)| acc + c * x) + } + + pub fn eval_base(&self, x: F::BaseField) -> F + where + F: FieldExtension, + { + self.coeffs + .iter() + .rev() + .fold(F::ZERO, |acc, &c| acc.scalar_mul(x) + c) + } + + /// Evaluate the polynomial at a point given its powers. The first power is the point itself, not 1. + pub fn eval_base_with_powers(&self, powers: &[F::BaseField]) -> F + where + F: FieldExtension, + { + debug_assert_eq!(self.coeffs.len(), powers.len() + 1); + let acc = self.coeffs[0]; + self.coeffs[1..] + .iter() + .zip(powers) + .fold(acc, |acc, (&x, &c)| acc + x.scalar_mul(c)) + } + + pub fn lde_multiple(polys: Vec<&Self>, rate_bits: usize) -> Vec { + polys.into_iter().map(|p| p.lde(rate_bits)).collect() + } + + pub fn lde(&self, rate_bits: usize) -> Self { + self.padded(self.len() << rate_bits) + } + + pub(crate) fn pad(&mut self, new_len: usize) -> Result<()> { + ensure!( + new_len >= self.len(), + "Trying to pad a polynomial of length {} to a length of {}.", + self.len(), + new_len + ); + self.coeffs.resize(new_len, F::ZERO); + Ok(()) + } + + pub(crate) fn padded(&self, new_len: usize) -> Self { + let mut poly = self.clone(); + poly.pad(new_len).unwrap(); + poly + } + + /// Removes leading zero coefficients. + pub fn trim(&mut self) { + self.coeffs.truncate(self.degree_plus_one()); + } + + /// Removes leading zero coefficients. + pub fn trimmed(&self) -> Self { + let coeffs = self.coeffs[..self.degree_plus_one()].to_vec(); + Self { coeffs } + } + + /// Degree of the polynomial + 1, or 0 for a polynomial with no non-zero coefficients. + pub(crate) fn degree_plus_one(&self) -> usize { + (0usize..self.len()) + .rev() + .find(|&i| self.coeffs[i].is_nonzero()) + .map_or(0, |i| i + 1) + } + + /// Leading coefficient. + pub fn lead(&self) -> F { + self.coeffs + .iter() + .rev() + .find(|x| x.is_nonzero()) + .map_or(F::ZERO, |x| *x) + } + + /// Reverse the order of the coefficients, not taking into account the leading zero coefficients. + pub(crate) fn rev(&self) -> Self { + Self::new(self.trimmed().coeffs.into_iter().rev().collect()) + } + + pub fn fft(&self) -> PolynomialValues { + fft(self) + } + + pub fn fft_with_options( + &self, + zero_factor: Option, + root_table: Option<&FftRootTable>, + ) -> PolynomialValues { + fft_with_options(self, zero_factor, root_table) + } + + /// Returns the evaluation of the polynomial on the coset `shift*H`. + pub fn coset_fft(&self, shift: F) -> PolynomialValues { + self.coset_fft_with_options(shift, None, None) + } + + /// Returns the evaluation of the polynomial on the coset `shift*H`. + pub fn coset_fft_with_options( + &self, + shift: F, + zero_factor: Option, + root_table: Option<&FftRootTable>, + ) -> PolynomialValues { + let modified_poly: Self = shift + .powers() + .zip(&self.coeffs) + .map(|(r, &c)| r * c) + .collect::>() + .into(); + modified_poly.fft_with_options(zero_factor, root_table) + } + + pub fn to_extension(&self) -> PolynomialCoeffs + where + F: Extendable, + { + PolynomialCoeffs::new(self.coeffs.iter().map(|&c| c.into()).collect()) + } + + pub fn mul_extension(&self, rhs: F::Extension) -> PolynomialCoeffs + where + F: Extendable, + { + PolynomialCoeffs::new(self.coeffs.iter().map(|&c| rhs.scalar_mul(c)).collect()) + } +} + +impl PartialEq for PolynomialCoeffs { + fn eq(&self, other: &Self) -> bool { + let max_terms = self.coeffs.len().max(other.coeffs.len()); + for i in 0..max_terms { + let self_i = self.coeffs.get(i).cloned().unwrap_or(F::ZERO); + let other_i = other.coeffs.get(i).cloned().unwrap_or(F::ZERO); + if self_i != other_i { + return false; + } + } + true + } +} + +impl Eq for PolynomialCoeffs {} + +impl From> for PolynomialCoeffs { + fn from(coeffs: Vec) -> Self { + Self::new(coeffs) + } +} + +impl Add for &PolynomialCoeffs { + type Output = PolynomialCoeffs; + + fn add(self, rhs: Self) -> Self::Output { + let len = max(self.len(), rhs.len()); + let a = self.padded(len).coeffs; + let b = rhs.padded(len).coeffs; + let coeffs = a.into_iter().zip(b).map(|(x, y)| x + y).collect(); + PolynomialCoeffs::new(coeffs) + } +} + +impl Sum for PolynomialCoeffs { + fn sum>(iter: I) -> Self { + iter.fold(Self::empty(), |acc, p| &acc + &p) + } +} + +impl Sub for &PolynomialCoeffs { + type Output = PolynomialCoeffs; + + fn sub(self, rhs: Self) -> Self::Output { + let len = max(self.len(), rhs.len()); + let mut coeffs = self.padded(len).coeffs; + for (i, &c) in rhs.coeffs.iter().enumerate() { + coeffs[i] -= c; + } + PolynomialCoeffs::new(coeffs) + } +} + +impl AddAssign for PolynomialCoeffs { + fn add_assign(&mut self, rhs: Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, r) in self.coeffs.iter_mut().zip(rhs.coeffs) { + *l += r; + } + } +} + +impl AddAssign<&Self> for PolynomialCoeffs { + fn add_assign(&mut self, rhs: &Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, &r) in self.coeffs.iter_mut().zip(&rhs.coeffs) { + *l += r; + } + } +} + +impl SubAssign for PolynomialCoeffs { + fn sub_assign(&mut self, rhs: Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, r) in self.coeffs.iter_mut().zip(rhs.coeffs) { + *l -= r; + } + } +} + +impl SubAssign<&Self> for PolynomialCoeffs { + fn sub_assign(&mut self, rhs: &Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, &r) in self.coeffs.iter_mut().zip(&rhs.coeffs) { + *l -= r; + } + } +} + +impl Mul for &PolynomialCoeffs { + type Output = PolynomialCoeffs; + + fn mul(self, rhs: F) -> Self::Output { + let coeffs = self.coeffs.iter().map(|&x| rhs * x).collect(); + PolynomialCoeffs::new(coeffs) + } +} + +impl MulAssign for PolynomialCoeffs { + fn mul_assign(&mut self, rhs: F) { + self.coeffs.iter_mut().for_each(|x| *x *= rhs); + } +} + +impl Mul for &PolynomialCoeffs { + type Output = PolynomialCoeffs; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn mul(self, rhs: Self) -> Self::Output { + let new_len = (self.len() + rhs.len()).next_power_of_two(); + let a = self.padded(new_len); + let b = rhs.padded(new_len); + let a_evals = a.fft(); + let b_evals = b.fft(); + + let mul_evals: Vec = a_evals + .values + .into_iter() + .zip(b_evals.values) + .map(|(pa, pb)| pa * pb) + .collect(); + ifft(&mul_evals.into()) + } +} + +#[cfg(test)] +mod tests { + use std::time::Instant; + + use rand::{thread_rng, Rng}; + + use super::*; + use crate::field::goldilocks_field::GoldilocksField; + + #[test] + fn test_trimmed() { + type F = GoldilocksField; + + assert_eq!( + PolynomialCoeffs:: { coeffs: vec![] }.trimmed(), + PolynomialCoeffs:: { coeffs: vec![] } + ); + assert_eq!( + PolynomialCoeffs:: { + coeffs: vec![F::ZERO] + } + .trimmed(), + PolynomialCoeffs:: { coeffs: vec![] } + ); + assert_eq!( + PolynomialCoeffs:: { + coeffs: vec![F::ONE, F::TWO, F::ZERO, F::ZERO] + } + .trimmed(), + PolynomialCoeffs:: { + coeffs: vec![F::ONE, F::TWO] + } + ); + } + + #[test] + fn test_coset_fft() { + type F = GoldilocksField; + + let k = 8; + let n = 1 << k; + let poly = PolynomialCoeffs::new(F::rand_vec(n)); + let shift = F::rand(); + let coset_evals = poly.coset_fft(shift).values; + + let generator = F::primitive_root_of_unity(k); + let naive_coset_evals = F::cyclic_subgroup_coset_known_order(generator, shift, n) + .into_iter() + .map(|x| poly.eval(x)) + .collect::>(); + assert_eq!(coset_evals, naive_coset_evals); + + let ifft_coeffs = PolynomialValues::new(coset_evals).coset_ifft(shift); + assert_eq!(poly, ifft_coeffs); + } + + #[test] + fn test_coset_ifft() { + type F = GoldilocksField; + + let k = 8; + let n = 1 << k; + let evals = PolynomialValues::new(F::rand_vec(n)); + let shift = F::rand(); + let coeffs = evals.coset_ifft(shift); + + let generator = F::primitive_root_of_unity(k); + let naive_coset_evals = F::cyclic_subgroup_coset_known_order(generator, shift, n) + .into_iter() + .map(|x| coeffs.eval(x)) + .collect::>(); + assert_eq!(evals, naive_coset_evals.into()); + + let fft_evals = coeffs.coset_fft(shift); + assert_eq!(evals, fft_evals); + } + + #[test] + fn test_polynomial_multiplication() { + type F = GoldilocksField; + let mut rng = thread_rng(); + let (a_deg, b_deg) = (rng.gen_range(1..10_000), rng.gen_range(1..10_000)); + let a = PolynomialCoeffs::new(F::rand_vec(a_deg)); + let b = PolynomialCoeffs::new(F::rand_vec(b_deg)); + let m1 = &a * &b; + let m2 = &a * &b; + for _ in 0..1000 { + let x = F::rand(); + assert_eq!(m1.eval(x), a.eval(x) * b.eval(x)); + assert_eq!(m2.eval(x), a.eval(x) * b.eval(x)); + } + } + + #[test] + fn test_inv_mod_xn() { + type F = GoldilocksField; + let mut rng = thread_rng(); + let a_deg = rng.gen_range(1..1_000); + let n = rng.gen_range(1..1_000); + let a = PolynomialCoeffs::new(F::rand_vec(a_deg)); + let b = a.inv_mod_xn(n); + let mut m = &a * &b; + m.coeffs.drain(n..); + m.trim(); + assert_eq!( + m, + PolynomialCoeffs::new(vec![F::ONE]), + "a: {:#?}, b:{:#?}, n:{:#?}, m:{:#?}", + a, + b, + n, + m + ); + } + + #[test] + fn test_polynomial_long_division() { + type F = GoldilocksField; + let mut rng = thread_rng(); + let (a_deg, b_deg) = (rng.gen_range(1..10_000), rng.gen_range(1..10_000)); + let a = PolynomialCoeffs::new(F::rand_vec(a_deg)); + let b = PolynomialCoeffs::new(F::rand_vec(b_deg)); + let (q, r) = a.div_rem_long_division(&b); + for _ in 0..1000 { + let x = F::rand(); + assert_eq!(a.eval(x), b.eval(x) * q.eval(x) + r.eval(x)); + } + } + + #[test] + fn test_polynomial_division() { + type F = GoldilocksField; + let mut rng = thread_rng(); + let (a_deg, b_deg) = (rng.gen_range(1..10_000), rng.gen_range(1..10_000)); + let a = PolynomialCoeffs::new(F::rand_vec(a_deg)); + let b = PolynomialCoeffs::new(F::rand_vec(b_deg)); + let (q, r) = a.div_rem(&b); + for _ in 0..1000 { + let x = F::rand(); + assert_eq!(a.eval(x), b.eval(x) * q.eval(x) + r.eval(x)); + } + } + + #[test] + fn test_polynomial_division_by_constant() { + type F = GoldilocksField; + let mut rng = thread_rng(); + let a_deg = rng.gen_range(1..10_000); + let a = PolynomialCoeffs::new(F::rand_vec(a_deg)); + let b = PolynomialCoeffs::from(vec![F::rand()]); + let (q, r) = a.div_rem(&b); + for _ in 0..1000 { + let x = F::rand(); + assert_eq!(a.eval(x), b.eval(x) * q.eval(x) + r.eval(x)); + } + } + + // Test to see which polynomial division method is faster for divisions of the type + // `(X^n - 1)/(X - a) + #[test] + fn test_division_linear() { + type F = GoldilocksField; + let mut rng = thread_rng(); + let l = 14; + let n = 1 << l; + let g = F::primitive_root_of_unity(l); + let xn_minus_one = { + let mut xn_min_one_vec = vec![F::ZERO; n + 1]; + xn_min_one_vec[n] = F::ONE; + xn_min_one_vec[0] = F::NEG_ONE; + PolynomialCoeffs::new(xn_min_one_vec) + }; + + let a = g.exp_u64(rng.gen_range(0..(n as u64))); + let denom = PolynomialCoeffs::new(vec![-a, F::ONE]); + let now = Instant::now(); + xn_minus_one.div_rem(&denom); + println!("Division time: {:?}", now.elapsed()); + let now = Instant::now(); + xn_minus_one.div_rem_long_division(&denom); + println!("Division time: {:?}", now.elapsed()); + } + + #[test] + fn eq() { + type F = GoldilocksField; + assert_eq!( + PolynomialCoeffs::::new(vec![]), + PolynomialCoeffs::new(vec![]) + ); + assert_eq!( + PolynomialCoeffs::::new(vec![F::ZERO]), + PolynomialCoeffs::new(vec![F::ZERO]) + ); + assert_eq!( + PolynomialCoeffs::::new(vec![]), + PolynomialCoeffs::new(vec![F::ZERO]) + ); + assert_eq!( + PolynomialCoeffs::::new(vec![F::ZERO]), + PolynomialCoeffs::new(vec![]) + ); + assert_eq!( + PolynomialCoeffs::::new(vec![F::ZERO]), + PolynomialCoeffs::new(vec![F::ZERO, F::ZERO]) + ); + assert_eq!( + PolynomialCoeffs::::new(vec![F::ONE]), + PolynomialCoeffs::new(vec![F::ONE, F::ZERO]) + ); + assert_ne!( + PolynomialCoeffs::::new(vec![]), + PolynomialCoeffs::new(vec![F::ONE]) + ); + assert_ne!( + PolynomialCoeffs::::new(vec![F::ZERO]), + PolynomialCoeffs::new(vec![F::ZERO, F::ONE]) + ); + assert_ne!( + PolynomialCoeffs::::new(vec![F::ZERO]), + PolynomialCoeffs::new(vec![F::ONE, F::ZERO]) + ); + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs index 586033be..fca6b728 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,7 +1,7 @@ use core::hint::unreachable_unchecked; use crate::field::field_types::Field; -use crate::polynomial::polynomial::PolynomialValues; +use crate::polynomial::PolynomialValues; pub(crate) mod bimap; pub(crate) mod context_tree; diff --git a/src/util/reducing.rs b/src/util/reducing.rs index f700a6ff..4fc15690 100644 --- a/src/util/reducing.rs +++ b/src/util/reducing.rs @@ -8,7 +8,7 @@ use crate::gates::reducing::ReducingGate; use crate::gates::reducing_extension::ReducingExtensionGate; use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; -use crate::polynomial::polynomial::PolynomialCoeffs; +use crate::polynomial::PolynomialCoeffs; /// When verifying the composition polynomial in FRI we have to compute sums of the form /// `(sum_0^k a^i * x_i)/d_0 + (sum_k^r a^i * y_i)/d_1` diff --git a/src/util/serialization.rs b/src/util/serialization.rs index 5ca4f691..b3a51b5f 100644 --- a/src/util/serialization.rs +++ b/src/util/serialization.rs @@ -15,7 +15,7 @@ use crate::plonk::circuit_data::CommonCircuitData; use crate::plonk::proof::{ CompressedProof, CompressedProofWithPublicInputs, OpeningSet, Proof, ProofWithPublicInputs, }; -use crate::polynomial::polynomial::PolynomialCoeffs; +use crate::polynomial::PolynomialCoeffs; #[derive(Debug)] pub struct Buffer(Cursor>);