snarkvm_algorithms/fft/polynomial/
dense.rs

1// Copyright 2024 Aleo Network Foundation
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! A polynomial represented in coefficient form.
17
18use super::PolyMultiplier;
19use crate::fft::{EvaluationDomain, Evaluations, Polynomial};
20use snarkvm_fields::{Field, PrimeField};
21use snarkvm_utilities::{cfg_iter_mut, serialize::*};
22
23use anyhow::Result;
24use num_traits::CheckedDiv;
25use rand::Rng;
26use std::{
27    fmt,
28    ops::{Add, AddAssign, Deref, DerefMut, Div, Mul, MulAssign, Neg, Sub, SubAssign},
29};
30
31use itertools::Itertools;
32
33#[cfg(not(feature = "serial"))]
34use rayon::prelude::*;
35
36/// Stores a polynomial in coefficient form.
37#[derive(Clone, PartialEq, Eq, Hash, Default, CanonicalSerialize, CanonicalDeserialize)]
38#[must_use]
39pub struct DensePolynomial<F: Field> {
40    /// The coefficient of `x^i` is stored at location `i` in `self.coeffs`.
41    pub coeffs: Vec<F>,
42}
43
44impl<F: Field> fmt::Debug for DensePolynomial<F> {
45    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
46        for (i, coeff) in self.coeffs.iter().enumerate().filter(|(_, c)| !c.is_zero()) {
47            if i == 0 {
48                write!(f, "\n{coeff:?}",)?;
49            } else if i == 1 {
50                write!(f, " + \n{coeff:?} * x")?;
51            } else {
52                write!(f, " + \n{coeff:?} * x^{i}")?;
53            }
54        }
55        Ok(())
56    }
57}
58
59impl<F: Field> DensePolynomial<F> {
60    /// Returns the zero polynomial.
61    pub fn zero() -> Self {
62        Self { coeffs: Vec::new() }
63    }
64
65    /// Checks if the given polynomial is zero.
66    pub fn is_zero(&self) -> bool {
67        self.coeffs.is_empty() || self.coeffs.iter().all(|coeff| coeff.is_zero())
68    }
69
70    /// Constructs a new polynomial from a list of coefficients.
71    pub fn from_coefficients_slice(coeffs: &[F]) -> Self {
72        Self::from_coefficients_vec(coeffs.to_vec())
73    }
74
75    /// Constructs a new polynomial from a list of coefficients.
76    pub fn from_coefficients_vec(mut coeffs: Vec<F>) -> Self {
77        // While there are zeros at the end of the coefficient vector, pop them off.
78        while let Some(true) = coeffs.last().map(|c| c.is_zero()) {
79            coeffs.pop();
80        }
81        // Check that either the coefficients vec are empty or that the last coeff is non-zero.
82        assert!(coeffs.last().map_or(true, |coeff| !coeff.is_zero()));
83
84        Self { coeffs }
85    }
86
87    /// Returns the degree of the polynomial.
88    pub fn degree(&self) -> usize {
89        if self.is_zero() {
90            0
91        } else {
92            assert!(self.coeffs.last().map_or(false, |coeff| !coeff.is_zero()));
93            self.coeffs.len() - 1
94        }
95    }
96
97    /// Evaluates `self` at the given `point` in the field.
98    pub fn evaluate(&self, point: F) -> F {
99        if self.is_zero() {
100            return F::zero();
101        } else if point.is_zero() {
102            return self.coeffs[0];
103        }
104        let mut powers_of_point = Vec::with_capacity(1 + self.degree());
105        powers_of_point.push(F::one());
106        let mut cur = point;
107        for _ in 0..self.degree() {
108            powers_of_point.push(cur);
109            cur *= point;
110        }
111        let zero = F::zero();
112        let mapping = crate::cfg_into_iter!(powers_of_point).zip_eq(&self.coeffs).map(|(power, coeff)| power * coeff);
113        crate::cfg_reduce!(mapping, || zero, |a, b| a + b)
114    }
115
116    /// Outputs a univariate polynomial of degree `d` where each non-leading
117    /// coefficient is sampled uniformly at random from R and the leading
118    /// coefficient is sampled uniformly at random from among the non-zero
119    /// elements of R.
120    pub fn rand<R: Rng>(d: usize, rng: &mut R) -> Self {
121        let mut random_coeffs = (0..(d + 1)).map(|_| F::rand(rng)).collect_vec();
122        while random_coeffs[d].is_zero() {
123            // In the extremely unlikely event, sample again.
124            random_coeffs[d] = F::rand(rng);
125        }
126        Self::from_coefficients_vec(random_coeffs)
127    }
128
129    /// Returns the coefficients of `self`.
130    pub fn coeffs(&self) -> &[F] {
131        &self.coeffs
132    }
133
134    /// Perform a naive n^2 multiplication of `self` by `other`.
135    #[cfg(test)]
136    fn naive_mul(&self, other: &Self) -> Self {
137        if self.is_zero() || other.is_zero() {
138            DensePolynomial::zero()
139        } else {
140            let mut result = vec![F::zero(); self.degree() + other.degree() + 1];
141            for (i, self_coeff) in self.coeffs.iter().enumerate() {
142                for (j, other_coeff) in other.coeffs.iter().enumerate() {
143                    result[i + j] += *self_coeff * other_coeff;
144                }
145            }
146            DensePolynomial::from_coefficients_vec(result)
147        }
148    }
149}
150
151impl<F: PrimeField> DensePolynomial<F> {
152    /// Multiply `self` by the vanishing polynomial for the domain `domain`.
153    pub fn mul_by_vanishing_poly(&self, domain: EvaluationDomain<F>) -> DensePolynomial<F> {
154        let mut shifted = vec![F::zero(); domain.size()];
155        shifted.extend_from_slice(&self.coeffs);
156        crate::cfg_iter_mut!(shifted[..self.coeffs.len()]).zip_eq(&self.coeffs).for_each(|(s, c)| *s -= c);
157        DensePolynomial::from_coefficients_vec(shifted)
158    }
159
160    /// Divide `self` by the vanishing polynomial for the domain `domain`.
161    /// Returns the quotient and remainder of the division.
162    pub fn divide_by_vanishing_poly(
163        &self,
164        domain: EvaluationDomain<F>,
165    ) -> Result<(DensePolynomial<F>, DensePolynomial<F>)> {
166        let self_poly = Polynomial::from(self);
167        let vanishing_poly = Polynomial::from(domain.vanishing_polynomial());
168        self_poly.divide_with_q_and_r(&vanishing_poly)
169    }
170
171    /// Evaluate `self` over `domain`.
172    pub fn evaluate_over_domain_by_ref(&self, domain: EvaluationDomain<F>) -> Evaluations<F> {
173        let poly: Polynomial<'_, F> = self.into();
174        Polynomial::<F>::evaluate_over_domain(poly, domain)
175    }
176
177    /// Evaluate `self` over `domain`.
178    pub fn evaluate_over_domain(self, domain: EvaluationDomain<F>) -> Evaluations<F> {
179        let poly: Polynomial<'_, F> = self.into();
180        Polynomial::<F>::evaluate_over_domain(poly, domain)
181    }
182}
183
184impl<F: Field> From<super::SparsePolynomial<F>> for DensePolynomial<F> {
185    fn from(other: super::SparsePolynomial<F>) -> Self {
186        let mut result = vec![F::zero(); other.degree() + 1];
187        for (i, coeff) in other.coeffs() {
188            result[*i] = *coeff;
189        }
190        DensePolynomial::from_coefficients_vec(result)
191    }
192}
193
194impl<'a, F: Field> Add<&'a DensePolynomial<F>> for &'_ DensePolynomial<F> {
195    type Output = DensePolynomial<F>;
196
197    fn add(self, other: &'a DensePolynomial<F>) -> DensePolynomial<F> {
198        let mut result = if self.is_zero() {
199            other.clone()
200        } else if other.is_zero() {
201            self.clone()
202        } else if self.degree() >= other.degree() {
203            let mut result = self.clone();
204            // Zip safety: `result` and `other` could have different lengths.
205            cfg_iter_mut!(result.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a += b);
206            result
207        } else {
208            let mut result = other.clone();
209            // Zip safety: `result` and `other` could have different lengths.
210            cfg_iter_mut!(result.coeffs).zip(&self.coeffs).for_each(|(a, b)| *a += b);
211            result
212        };
213        // If the leading coefficient ends up being zero, pop it off.
214        while let Some(true) = result.coeffs.last().map(|c| c.is_zero()) {
215            result.coeffs.pop();
216        }
217        result
218    }
219}
220
221impl<'a, F: Field> AddAssign<&'a DensePolynomial<F>> for DensePolynomial<F> {
222    fn add_assign(&mut self, other: &'a DensePolynomial<F>) {
223        if self.is_zero() {
224            self.coeffs.clear();
225            self.coeffs.extend_from_slice(&other.coeffs);
226        } else if other.is_zero() {
227            // return
228        } else if self.degree() >= other.degree() {
229            // Zip safety: `self` and `other` could have different lengths.
230            cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a += b);
231        } else {
232            // Add the necessary number of zero coefficients.
233            self.coeffs.resize(other.coeffs.len(), F::zero());
234            // Zip safety: `self` and `other` have the same length.
235            cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a += b);
236        }
237        // If the leading coefficient ends up being zero, pop it off.
238        while let Some(true) = self.coeffs.last().map(|c| c.is_zero()) {
239            self.coeffs.pop();
240        }
241    }
242}
243
244impl<'a, F: Field> AddAssign<&'a Polynomial<'a, F>> for DensePolynomial<F> {
245    fn add_assign(&mut self, other: &'a Polynomial<F>) {
246        match other {
247            Polynomial::Sparse(p) => *self += &Self::from(p.clone().into_owned()),
248            Polynomial::Dense(p) => *self += p.as_ref(),
249        }
250    }
251}
252
253impl<'a, F: Field> AddAssign<(F, &'a Polynomial<'a, F>)> for DensePolynomial<F> {
254    fn add_assign(&mut self, (f, other): (F, &'a Polynomial<F>)) {
255        match other {
256            Polynomial::Sparse(p) => *self += (f, &Self::from(p.clone().into_owned())),
257            Polynomial::Dense(p) => *self += (f, p.as_ref()),
258        }
259    }
260}
261
262impl<'a, F: Field> AddAssign<(F, &'a DensePolynomial<F>)> for DensePolynomial<F> {
263    #[allow(clippy::suspicious_op_assign_impl)]
264    fn add_assign(&mut self, (f, other): (F, &'a DensePolynomial<F>)) {
265        if self.is_zero() {
266            self.coeffs.clear();
267            self.coeffs.extend_from_slice(&other.coeffs);
268            self.coeffs.iter_mut().for_each(|c| *c *= &f);
269        } else if other.is_zero() {
270            // return
271        } else if self.degree() >= other.degree() {
272            // Zip safety: `self` and `other` could have different lengths.
273            cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| {
274                *a += f * b;
275            });
276        } else {
277            // Add the necessary number of zero coefficients.
278            self.coeffs.resize(other.coeffs.len(), F::zero());
279            // Zip safety: `self` and `other` have the same length after the resize.
280            cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| {
281                *a += f * b;
282            });
283        }
284        // If the leading coefficient ends up being zero, pop it off.
285        while let Some(true) = self.coeffs.last().map(|c| c.is_zero()) {
286            self.coeffs.pop();
287        }
288    }
289}
290
291impl<F: Field> Neg for DensePolynomial<F> {
292    type Output = DensePolynomial<F>;
293
294    #[inline]
295    fn neg(mut self) -> DensePolynomial<F> {
296        for coeff in &mut self.coeffs {
297            *coeff = -*coeff;
298        }
299        self
300    }
301}
302
303impl<'a, F: Field> Sub<&'a DensePolynomial<F>> for &'_ DensePolynomial<F> {
304    type Output = DensePolynomial<F>;
305
306    #[inline]
307    fn sub(self, other: &'a DensePolynomial<F>) -> DensePolynomial<F> {
308        let mut result = if self.is_zero() {
309            let mut result = other.clone();
310            for coeff in &mut result.coeffs {
311                *coeff = -(*coeff);
312            }
313            result
314        } else if other.is_zero() {
315            self.clone()
316        } else if self.degree() >= other.degree() {
317            let mut result = self.clone();
318            // Zip safety: `result` and `other` could have different degrees.
319            cfg_iter_mut!(result.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a -= b);
320            result
321        } else {
322            let mut result = self.clone();
323            result.coeffs.resize(other.coeffs.len(), F::zero());
324            // Zip safety: `result` and `other` have the same length after the resize.
325            cfg_iter_mut!(result.coeffs).zip(&other.coeffs).for_each(|(a, b)| {
326                *a -= b;
327            });
328            result
329        };
330        // If the leading coefficient ends up being zero, pop it off.
331        while let Some(true) = result.coeffs.last().map(|c| c.is_zero()) {
332            result.coeffs.pop();
333        }
334        result
335    }
336}
337
338impl<'a, F: Field> SubAssign<&'a DensePolynomial<F>> for DensePolynomial<F> {
339    #[inline]
340    fn sub_assign(&mut self, other: &'a DensePolynomial<F>) {
341        if self.is_zero() {
342            self.coeffs.resize(other.coeffs.len(), F::zero());
343            for (i, coeff) in other.coeffs.iter().enumerate() {
344                self.coeffs[i] -= coeff;
345            }
346        } else if other.is_zero() {
347            // return
348        } else if self.degree() >= other.degree() {
349            // Zip safety: self and other could have different lengths.
350            cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a -= b);
351        } else {
352            // Add the necessary number of zero coefficients.
353            self.coeffs.resize(other.coeffs.len(), F::zero());
354            // Zip safety: self and other have the same length after the resize.
355            cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a -= b);
356        }
357        // If the leading coefficient ends up being zero, pop it off.
358        while let Some(true) = self.coeffs.last().map(|c| c.is_zero()) {
359            self.coeffs.pop();
360        }
361    }
362}
363
364impl<'a, F: Field> AddAssign<&'a super::SparsePolynomial<F>> for DensePolynomial<F> {
365    #[inline]
366    fn add_assign(&mut self, other: &'a super::SparsePolynomial<F>) {
367        if self.degree() < other.degree() {
368            self.coeffs.resize(other.degree() + 1, F::zero());
369        }
370        for (i, b) in other.coeffs() {
371            self.coeffs[*i] += b;
372        }
373        // If the leading coefficient ends up being zero, pop it off.
374        while let Some(true) = self.coeffs.last().map(|c| c.is_zero()) {
375            self.coeffs.pop();
376        }
377    }
378}
379
380impl<'a, F: Field> Sub<&'a super::SparsePolynomial<F>> for DensePolynomial<F> {
381    type Output = Self;
382
383    #[inline]
384    fn sub(mut self, other: &'a super::SparsePolynomial<F>) -> Self::Output {
385        if self.degree() < other.degree() {
386            self.coeffs.resize(other.degree() + 1, F::zero());
387        }
388        for (i, b) in other.coeffs() {
389            self.coeffs[*i] -= b;
390        }
391        // If the leading coefficient ends up being zero, pop it off.
392        while let Some(true) = self.coeffs.last().map(|c| c.is_zero()) {
393            self.coeffs.pop();
394        }
395        self
396    }
397}
398
399impl<'a, F: Field> Div<&'a DensePolynomial<F>> for &'_ DensePolynomial<F> {
400    type Output = DensePolynomial<F>;
401
402    /// This division can panic and ignores remainders
403    #[inline]
404    fn div(self, divisor: &'a DensePolynomial<F>) -> DensePolynomial<F> {
405        let a: Polynomial<_> = self.into();
406        let b: Polynomial<_> = divisor.into();
407        a.divide_with_q_and_r(&b).expect("division failed").0
408    }
409}
410
411impl<F: Field> Div<DensePolynomial<F>> for DensePolynomial<F> {
412    type Output = DensePolynomial<F>;
413
414    /// This division can panic and ignores remainders
415    #[inline]
416    fn div(self, divisor: DensePolynomial<F>) -> DensePolynomial<F> {
417        let a: Polynomial<_> = self.into();
418        let b: Polynomial<_> = divisor.into();
419        a.divide_with_q_and_r(&b).expect("division failed").0
420    }
421}
422
423impl<F: Field> CheckedDiv for DensePolynomial<F> {
424    #[inline]
425    fn checked_div(&self, divisor: &DensePolynomial<F>) -> Option<DensePolynomial<F>> {
426        let a: Polynomial<_> = self.into();
427        let b: Polynomial<_> = divisor.into();
428        match a.divide_with_q_and_r(&b) {
429            Ok((divisor, remainder)) => {
430                if remainder.is_zero() {
431                    Some(divisor)
432                } else {
433                    None
434                }
435            }
436            Err(_) => None,
437        }
438    }
439}
440
441/// Performs O(nlogn) multiplication of polynomials if F is smooth.
442impl<'a, F: PrimeField> Mul<&'a DensePolynomial<F>> for &'_ DensePolynomial<F> {
443    type Output = DensePolynomial<F>;
444
445    #[inline]
446    #[allow(clippy::suspicious_arithmetic_impl)]
447    fn mul(self, other: &'a DensePolynomial<F>) -> DensePolynomial<F> {
448        if self.is_zero() || other.is_zero() {
449            DensePolynomial::zero()
450        } else {
451            let mut m = PolyMultiplier::new();
452            m.add_polynomial_ref(self, "");
453            m.add_polynomial_ref(other, "");
454            m.multiply().unwrap()
455        }
456    }
457}
458
459/// Multiplies `self` by `other: F`.
460impl<F: Field> Mul<F> for DensePolynomial<F> {
461    type Output = Self;
462
463    #[inline]
464    fn mul(mut self, other: F) -> Self {
465        self.iter_mut().for_each(|c| *c *= other);
466        self
467    }
468}
469
470/// Multiplies `self` by `other: F`.
471impl<F: Field> Mul<F> for &'_ DensePolynomial<F> {
472    type Output = DensePolynomial<F>;
473
474    #[inline]
475    fn mul(self, other: F) -> Self::Output {
476        let result = self.clone();
477        result * other
478    }
479}
480
481/// Multiplies `self` by `other: F`.
482impl<F: Field> MulAssign<F> for DensePolynomial<F> {
483    #[allow(clippy::suspicious_arithmetic_impl)]
484    fn mul_assign(&mut self, other: F) {
485        cfg_iter_mut!(self).for_each(|c| *c *= other);
486    }
487}
488
489/// Multiplies `self` by `other: F`.
490impl<F: Field> std::iter::Sum for DensePolynomial<F> {
491    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
492        iter.fold(DensePolynomial::zero(), |a, b| &a + &b)
493    }
494}
495
496impl<F: Field> Deref for DensePolynomial<F> {
497    type Target = [F];
498
499    fn deref(&self) -> &[F] {
500        &self.coeffs
501    }
502}
503
504impl<F: Field> DerefMut for DensePolynomial<F> {
505    fn deref_mut(&mut self) -> &mut [F] {
506        &mut self.coeffs
507    }
508}
509
510#[cfg(test)]
511mod tests {
512    use crate::fft::polynomial::*;
513    use num_traits::CheckedDiv;
514    use snarkvm_curves::bls12_377::Fr;
515    use snarkvm_fields::{Field, One, Zero};
516    use snarkvm_utilities::rand::{TestRng, Uniform};
517
518    use rand::RngCore;
519
520    #[test]
521    fn double_polynomials_random() {
522        let rng = &mut TestRng::default();
523        for degree in 0..70 {
524            let p = DensePolynomial::<Fr>::rand(degree, rng);
525            let p_double = &p + &p;
526            let p_quad = &p_double + &p_double;
527            assert_eq!(&(&(&p + &p) + &p) + &p, p_quad);
528        }
529    }
530
531    #[test]
532    fn add_polynomials() {
533        let rng = &mut TestRng::default();
534        for a_degree in 0..70 {
535            for b_degree in 0..70 {
536                let p1 = DensePolynomial::<Fr>::rand(a_degree, rng);
537                let p2 = DensePolynomial::<Fr>::rand(b_degree, rng);
538                let res1 = &p1 + &p2;
539                let res2 = &p2 + &p1;
540                assert_eq!(res1, res2);
541            }
542        }
543    }
544
545    #[test]
546    fn add_polynomials_with_mul() {
547        let rng = &mut TestRng::default();
548        for a_degree in 0..70 {
549            for b_degree in 0..70 {
550                let mut p1 = DensePolynomial::rand(a_degree, rng);
551                let p2 = DensePolynomial::rand(b_degree, rng);
552                let f = Fr::rand(rng);
553                let f_p2 = DensePolynomial::from_coefficients_vec(p2.coeffs.iter().map(|c| f * c).collect());
554                let res2 = &f_p2 + &p1;
555                p1 += (f, &p2);
556                let res1 = p1;
557                assert_eq!(res1, res2);
558            }
559        }
560    }
561
562    #[test]
563    fn sub_polynomials() {
564        let rng = &mut TestRng::default();
565        let p1 = DensePolynomial::<Fr>::rand(5, rng);
566        let p2 = DensePolynomial::<Fr>::rand(3, rng);
567        let res1 = &p1 - &p2;
568        let res2 = &p2 - &p1;
569        assert_eq!(&res1 + &p2, p1, "Subtraction should be inverse of addition!");
570        assert_eq!(res1, -res2, "p2 - p1 = -(p1 - p2)");
571    }
572
573    #[test]
574    fn divide_polynomials_fixed() {
575        let dividend = DensePolynomial::from_coefficients_slice(&[
576            "4".parse().unwrap(),
577            "8".parse().unwrap(),
578            "5".parse().unwrap(),
579            "1".parse().unwrap(),
580        ]);
581        let divisor = DensePolynomial::from_coefficients_slice(&[Fr::one(), Fr::one()]); // Construct a monic linear polynomial.
582        let result = dividend.checked_div(&divisor).unwrap();
583        let expected_result = DensePolynomial::from_coefficients_slice(&[
584            "4".parse().unwrap(),
585            "4".parse().unwrap(),
586            "1".parse().unwrap(),
587        ]);
588        assert_eq!(expected_result, result);
589    }
590
591    #[test]
592    #[allow(clippy::needless_borrow)]
593    fn divide_polynomials_random() {
594        let rng = &mut TestRng::default();
595
596        for a_degree in 0..70 {
597            for b_degree in 0..70 {
598                let dividend = DensePolynomial::<Fr>::rand(a_degree, rng);
599                let divisor = DensePolynomial::<Fr>::rand(b_degree, rng);
600                let (quotient, remainder) =
601                    Polynomial::divide_with_q_and_r(&(&dividend).into(), &(&divisor).into()).unwrap();
602                assert_eq!(dividend, &(&divisor * &quotient) + &remainder)
603            }
604        }
605    }
606
607    #[test]
608    fn evaluate_polynomials() {
609        let rng = &mut TestRng::default();
610        for a_degree in 0..70 {
611            let p = DensePolynomial::rand(a_degree, rng);
612            let point: Fr = Fr::from(10u64);
613            let mut total = Fr::zero();
614            for (i, coeff) in p.coeffs.iter().enumerate() {
615                total += point.pow([i as u64]) * coeff;
616            }
617            assert_eq!(p.evaluate(point), total);
618        }
619    }
620
621    #[test]
622    fn divide_poly_by_zero() {
623        let a = Polynomial::<Fr>::zero();
624        let b = Polynomial::<Fr>::zero();
625        assert!(a.divide_with_q_and_r(&b).is_err());
626    }
627
628    #[test]
629    fn mul_polynomials_random() {
630        let rng = &mut TestRng::default();
631        for a_degree in 0..70 {
632            for b_degree in 0..70 {
633                dbg!(a_degree);
634                dbg!(b_degree);
635                let a = DensePolynomial::<Fr>::rand(a_degree, rng);
636                let b = DensePolynomial::<Fr>::rand(b_degree, rng);
637                assert_eq!(&a * &b, a.naive_mul(&b))
638            }
639        }
640    }
641
642    #[test]
643    fn mul_polynomials_n_random() {
644        let rng = &mut TestRng::default();
645
646        let max_degree = 1 << 8;
647
648        for _ in 0..10 {
649            let mut multiplier = PolyMultiplier::new();
650            let a = DensePolynomial::<Fr>::rand(max_degree / 2, rng);
651            let mut mul_degree = a.degree() + 1;
652            multiplier.add_polynomial(a.clone(), "a");
653            let mut naive = a.clone();
654
655            // Include polynomials and evaluations
656            let num_polys = (rng.next_u32() as usize) % 8;
657            let num_evals = (rng.next_u32() as usize) % 4;
658            println!("\nnum_polys {num_polys}, num_evals {num_evals}");
659
660            for _ in 1..num_polys {
661                let degree = (rng.next_u32() as usize) % max_degree;
662                mul_degree += degree + 1;
663                println!("poly degree {degree}");
664                let a = DensePolynomial::<Fr>::rand(degree, rng);
665                naive = naive.naive_mul(&a);
666                multiplier.add_polynomial(a.clone(), "a");
667            }
668
669            // Add evaluations but don't overflow the domain
670            let mut eval_degree = mul_degree;
671            let domain = EvaluationDomain::new(mul_degree).unwrap();
672            println!("mul_degree {}, domain {}", mul_degree, domain.size());
673            for _ in 0..num_evals {
674                let a = DensePolynomial::<Fr>::rand(mul_degree / 8, rng);
675                eval_degree += a.len() + 1;
676                if eval_degree < domain.size() {
677                    println!("eval degree {eval_degree}");
678                    let mut a_evals = a.clone().coeffs;
679                    domain.fft_in_place(&mut a_evals);
680                    let a_evals = Evaluations::from_vec_and_domain(a_evals, domain);
681
682                    naive = naive.naive_mul(&a);
683                    multiplier.add_evaluation(a_evals, "a");
684                }
685            }
686
687            assert_eq!(multiplier.multiply().unwrap(), naive);
688        }
689    }
690
691    #[test]
692    fn mul_polynomials_corner_cases() {
693        let rng = &mut TestRng::default();
694
695        let a_degree = 70;
696
697        // Single polynomial
698        println!("Test single polynomial");
699        let a = DensePolynomial::<Fr>::rand(a_degree, rng);
700        let mut multiplier = PolyMultiplier::new();
701        multiplier.add_polynomial(a.clone(), "a");
702        assert_eq!(multiplier.multiply().unwrap(), a);
703
704        // Note PolyMultiplier doesn't support evaluations with no polynomials
705    }
706
707    #[test]
708    fn mul_by_vanishing_poly() {
709        let rng = &mut TestRng::default();
710        for size in 1..10 {
711            let domain = EvaluationDomain::new(1 << size).unwrap();
712            for degree in 0..70 {
713                let p = DensePolynomial::<Fr>::rand(degree, rng);
714                let ans1 = p.mul_by_vanishing_poly(domain);
715                let ans2 = &p * &domain.vanishing_polynomial().into();
716                assert_eq!(ans1, ans2);
717            }
718        }
719    }
720}