Skip to main content

ark_poly/polynomial/univariate/
dense.rs

1//! A dense univariate polynomial represented in coefficient form.
2use crate::{
3    univariate::{DenseOrSparsePolynomial, SparsePolynomial},
4    DenseUVPolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial,
5};
6use ark_ff::{FftField, Field, Zero};
7use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
8use ark_std::{
9    cfg_iter_mut, fmt,
10    ops::{Add, AddAssign, Deref, DerefMut, Div, Mul, Neg, Sub, SubAssign},
11    rand::Rng,
12    vec,
13    vec::*,
14};
15
16#[cfg(feature = "parallel")]
17use ark_std::cmp::max;
18#[cfg(feature = "parallel")]
19use rayon::prelude::*;
20
21/// Stores a polynomial in coefficient form.
22#[derive(Clone, PartialEq, Eq, Hash, Default, CanonicalSerialize, CanonicalDeserialize)]
23pub struct DensePolynomial<F: Field> {
24    /// The coefficient of `x^i` is stored at location `i` in `self.coeffs`.
25    pub coeffs: Vec<F>,
26}
27
28impl<F: Field> Polynomial<F> for DensePolynomial<F> {
29    type Point = F;
30
31    /// Returns the total degree of the polynomial
32    fn degree(&self) -> usize {
33        if self.is_zero() {
34            0
35        } else {
36            assert!(self.coeffs.last().is_some_and(|coeff| !coeff.is_zero()));
37            self.coeffs.len() - 1
38        }
39    }
40
41    /// Evaluates `self` at the given `point` in `Self::Point`.
42    fn evaluate(&self, point: &F) -> F {
43        if self.is_zero() {
44            F::zero()
45        } else if point.is_zero() {
46            self.coeffs[0]
47        } else {
48            self.internal_evaluate(point)
49        }
50    }
51}
52
53#[cfg(feature = "parallel")]
54// Set some minimum number of field elements to be worked on per thread
55// to avoid per-thread costs dominating parallel execution time.
56const MIN_ELEMENTS_PER_THREAD: usize = 16;
57
58impl<F: Field> DensePolynomial<F> {
59    #[inline]
60    // Horner's method for polynomial evaluation
61    fn horner_evaluate(poly_coeffs: &[F], point: &F) -> F {
62        poly_coeffs
63            .iter()
64            .rfold(F::zero(), move |result, coeff| result * point + coeff)
65    }
66
67    #[cfg(not(feature = "parallel"))]
68    fn internal_evaluate(&self, point: &F) -> F {
69        Self::horner_evaluate(&self.coeffs, point)
70    }
71
72    #[cfg(feature = "parallel")]
73    fn internal_evaluate(&self, point: &F) -> F {
74        // Horners method - parallel method
75        // compute the number of threads we will be using.
76        let num_cpus_available = rayon::current_num_threads();
77        let num_coeffs = self.coeffs.len();
78        let num_elem_per_thread = max(num_coeffs / num_cpus_available, MIN_ELEMENTS_PER_THREAD);
79
80        // run Horners method on each thread as follows:
81        // 1) Split up the coefficients across each thread evenly.
82        // 2) Do polynomial evaluation via horner's method for the thread's coefficients
83        // 3) Scale the result point^{thread coefficient start index}
84        // Then obtain the final polynomial evaluation by summing each threads result.
85        self.coeffs
86            .par_chunks(num_elem_per_thread)
87            .enumerate()
88            .map(|(i, chunk)| {
89                Self::horner_evaluate(chunk, point) * point.pow([(i * num_elem_per_thread) as u64])
90            })
91            .sum()
92    }
93}
94
95impl<F: Field> DenseUVPolynomial<F> for DensePolynomial<F> {
96    /// Constructs a new polynomial from a list of coefficients.
97    fn from_coefficients_slice(coeffs: &[F]) -> Self {
98        Self::from_coefficients_vec(coeffs.to_vec())
99    }
100
101    /// Constructs a new polynomial from a list of coefficients.
102    fn from_coefficients_vec(coeffs: Vec<F>) -> Self {
103        let mut result = Self { coeffs };
104        // While there are zeros at the end of the coefficient vector, pop them off.
105        result.truncate_leading_zeros();
106        // Check that either the coefficients vec is empty or that the last coeff is
107        // non-zero.
108        assert!(result.coeffs.last().map_or(true, |coeff| !coeff.is_zero()));
109        result
110    }
111
112    /// Returns the coefficients of `self`
113    fn coeffs(&self) -> &[F] {
114        &self.coeffs
115    }
116
117    /// Outputs a univariate polynomial of degree `d` where each non-leading
118    /// coefficient is sampled uniformly at random from `F` and the leading
119    /// coefficient is sampled uniformly at random from among the non-zero
120    /// elements of `F`.
121    ///
122    /// # Example
123    /// ```
124    /// use ark_std::test_rng;
125    /// use ark_test_curves::bls12_381::Fr;
126    /// use ark_poly::{univariate::DensePolynomial, Polynomial, DenseUVPolynomial};
127    ///
128    /// let rng = &mut test_rng();
129    /// let poly = DensePolynomial::<Fr>::rand(8, rng);
130    /// assert_eq!(poly.degree(), 8);
131    /// ```
132    fn rand<R: Rng>(d: usize, rng: &mut R) -> Self {
133        let mut random_coeffs = Vec::new();
134
135        if d > 0 {
136            // d - 1 overflows when d = 0
137            for _ in 0..=(d - 1) {
138                random_coeffs.push(F::rand(rng));
139            }
140        }
141
142        let mut leading_coefficient = F::rand(rng);
143
144        while leading_coefficient.is_zero() {
145            leading_coefficient = F::rand(rng);
146        }
147
148        random_coeffs.push(leading_coefficient);
149
150        Self::from_coefficients_vec(random_coeffs)
151    }
152}
153
154impl<F: FftField> DensePolynomial<F> {
155    /// Multiply `self` by the vanishing polynomial for the domain `domain`.
156    /// Returns the result of the multiplication.
157    pub fn mul_by_vanishing_poly<D: EvaluationDomain<F>>(&self, domain: D) -> Self {
158        let mut shifted = vec![F::zero(); domain.size()];
159        shifted.extend_from_slice(&self.coeffs);
160        cfg_iter_mut!(shifted)
161            .zip(&self.coeffs)
162            .for_each(|(s, c)| *s -= c);
163        Self::from_coefficients_vec(shifted)
164    }
165
166    /// Divide `self` by the vanishing polynomial for the domain `domain`.
167    /// Returns the quotient and remainder of the division.
168    pub fn divide_by_vanishing_poly<D: EvaluationDomain<F>>(&self, domain: D) -> (Self, Self) {
169        let domain_size = domain.size();
170
171        if self.coeffs.len() < domain_size {
172            // If degree(self) < len(Domain), then the quotient is zero, and the entire polynomial is the remainder
173            (Self::zero(), self.clone())
174        } else {
175            // Compute the quotient
176            //
177            // If `self.len() <= 2 * domain_size`
178            //    then quotient is simply `self.coeffs[domain_size..]`
179            // Otherwise
180            //    during the division by `x^domain_size - 1`, some of `self.coeffs[domain_size..]` will be updated as well
181            //    which can be computed using the following algorithm.
182            //
183            let mut quotient_vec = self.coeffs[domain_size..].to_vec();
184            for i in 1..(self.len() / domain_size) {
185                cfg_iter_mut!(quotient_vec)
186                    .zip(&self.coeffs[domain_size * (i + 1)..])
187                    .for_each(|(s, c)| *s += c);
188            }
189
190            // Compute the remainder
191            //
192            // `remainder = self - quotient_vec * (x^domain_size - 1)`
193            //
194            // Note that remainder must be smaller than `domain_size`.
195            // So we can look at only the first `domain_size` terms.
196            //
197            // Therefore,
198            // `remainder = self.coeffs[0..domain_size] - quotient_vec * (-1)`
199            // i.e.,
200            // `remainder = self.coeffs[0..domain_size] + quotient_vec`
201            //
202            let mut remainder_vec = self.coeffs[0..domain_size].to_vec();
203            cfg_iter_mut!(remainder_vec)
204                .zip(&quotient_vec)
205                .for_each(|(s, c)| *s += c);
206
207            let quotient = Self::from_coefficients_vec(quotient_vec);
208            let remainder = Self::from_coefficients_vec(remainder_vec);
209            (quotient, remainder)
210        }
211    }
212}
213
214impl<F: Field> DensePolynomial<F> {
215    fn truncate_leading_zeros(&mut self) {
216        while self.coeffs.last().is_some_and(|c| c.is_zero()) {
217            self.coeffs.pop();
218        }
219    }
220
221    /// Perform a naive n^2 multiplication of `self` by `other`.
222    pub fn naive_mul(&self, other: &Self) -> Self {
223        if self.is_zero() || other.is_zero() {
224            Self::zero()
225        } else {
226            let mut result = vec![F::zero(); self.degree() + other.degree() + 1];
227            for (i, self_coeff) in self.coeffs.iter().enumerate() {
228                for (j, other_coeff) in other.coeffs.iter().enumerate() {
229                    result[i + j] += &(*self_coeff * other_coeff);
230                }
231            }
232            Self::from_coefficients_vec(result)
233        }
234    }
235
236    /// Returns the quotient of the division of `self` by `other`
237    /// using a naive O(nk) algorithm, with n, k the respective degrees of
238    /// the dividend and divisor
239    pub fn naive_div(&self, other: &Self) -> Self {
240        let dividend: DenseOrSparsePolynomial<'_, F> = self.into();
241        let divisor: DenseOrSparsePolynomial<'_, F> = other.into();
242
243        dividend.naive_div(&divisor).expect("division failed").0
244    }
245}
246
247impl<F: FftField> DensePolynomial<F> {
248    /// Evaluate `self` over `domain`.
249    pub fn evaluate_over_domain_by_ref<D: EvaluationDomain<F>>(
250        &self,
251        domain: D,
252    ) -> Evaluations<F, D> {
253        let poly: DenseOrSparsePolynomial<'_, F> = self.into();
254        DenseOrSparsePolynomial::<F>::evaluate_over_domain(poly, domain)
255    }
256
257    /// Evaluate `self` over `domain`.
258    pub fn evaluate_over_domain<D: EvaluationDomain<F>>(self, domain: D) -> Evaluations<F, D> {
259        let poly: DenseOrSparsePolynomial<'_, F> = self.into();
260        DenseOrSparsePolynomial::<F>::evaluate_over_domain(poly, domain)
261    }
262}
263
264impl<F: Field> fmt::Debug for DensePolynomial<F> {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
266        for (i, coeff) in self.coeffs.iter().enumerate().filter(|(_, c)| !c.is_zero()) {
267            if i == 0 {
268                write!(f, "\n{coeff:?}")?;
269            } else if i == 1 {
270                write!(f, " + \n{coeff:?} * x")?;
271            } else {
272                write!(f, " + \n{coeff:?} * x^{i}")?;
273            }
274        }
275        Ok(())
276    }
277}
278
279impl<F: Field> Deref for DensePolynomial<F> {
280    type Target = [F];
281
282    fn deref(&self) -> &[F] {
283        &self.coeffs
284    }
285}
286
287impl<F: Field> DerefMut for DensePolynomial<F> {
288    fn deref_mut(&mut self) -> &mut [F] {
289        &mut self.coeffs
290    }
291}
292
293impl<'a, F: Field> Add<&'a DensePolynomial<F>> for &DensePolynomial<F> {
294    type Output = DensePolynomial<F>;
295
296    fn add(self, other: &'a DensePolynomial<F>) -> DensePolynomial<F> {
297        // If the first polynomial is zero, the result is simply the second polynomial.
298        if self.is_zero() {
299            return other.clone();
300        }
301
302        // If the second polynomial is zero, the result is simply the first polynomial.
303        if other.is_zero() {
304            return self.clone();
305        }
306
307        // Determine which polynomial has the higher degree.
308        let (longer, shorter) = if self.degree() >= other.degree() {
309            (self, other)
310        } else {
311            (other, self)
312        };
313
314        // Start with a copy of the longer polynomial as the base for the result.
315        let mut result = longer.clone();
316
317        // Iterate through the coefficients of the `shorter` polynomial.
318        // Add them to the corresponding coefficients in the `longer` polynomial.
319        cfg_iter_mut!(result)
320            .zip(&shorter.coeffs)
321            .for_each(|(a, b)| *a += b);
322
323        // Remove any trailing zeros from the resulting polynomial.
324        result.truncate_leading_zeros();
325
326        result
327    }
328}
329
330impl<'a, F: Field> Add<&'a SparsePolynomial<F>> for &DensePolynomial<F> {
331    type Output = DensePolynomial<F>;
332
333    #[inline]
334    fn add(self, other: &'a SparsePolynomial<F>) -> DensePolynomial<F> {
335        if self.is_zero() {
336            return other.clone().into();
337        }
338
339        if other.is_zero() {
340            return self.clone();
341        }
342
343        let mut result = self.clone();
344
345        // Reserve space for additional coefficients if `other` has a higher degree.
346        let additional_len = other.degree().saturating_sub(result.degree());
347        result.coeffs.reserve(additional_len);
348
349        // Process each term in `other`.
350        for (pow, coeff) in other.iter() {
351            if let Some(target) = result.coeffs.get_mut(*pow) {
352                *target += coeff;
353            } else {
354                // Extend with zeros if the power exceeds the current length.
355                result
356                    .coeffs
357                    .extend(ark_std::iter::repeat(F::zero()).take(pow - result.coeffs.len()));
358                result.coeffs.push(*coeff);
359            }
360        }
361
362        // Remove any leading zeros.
363        // For example: `0 * x^2 + 0 * x + 1` should be represented as `1`.
364        result.truncate_leading_zeros();
365
366        result
367    }
368}
369
370impl<'a, F: Field> AddAssign<&'a Self> for DensePolynomial<F> {
371    fn add_assign(&mut self, other: &'a Self) {
372        if other.is_zero() {
373            self.truncate_leading_zeros();
374            return;
375        }
376
377        if self.is_zero() {
378            self.coeffs.clear();
379            self.coeffs.extend_from_slice(&other.coeffs);
380        } else {
381            let other_coeffs_len = other.coeffs.len();
382            if other_coeffs_len > self.coeffs.len() {
383                // Add the necessary number of zero coefficients.
384                self.coeffs.resize(other_coeffs_len, F::zero());
385            }
386
387            self.coeffs
388                .iter_mut()
389                .zip(&other.coeffs)
390                .for_each(|(a, b)| *a += b);
391        }
392        self.truncate_leading_zeros();
393    }
394}
395
396impl<'a, F: Field> AddAssign<(F, &'a Self)> for DensePolynomial<F> {
397    fn add_assign(&mut self, (f, other): (F, &'a Self)) {
398        // No need to modify self if other is zero
399        if other.is_zero() {
400            return;
401        }
402
403        // If the first polynomial is zero, just copy the second one and scale by f.
404        if self.is_zero() {
405            self.coeffs.clear();
406            self.coeffs.extend_from_slice(&other.coeffs);
407            self.coeffs.iter_mut().for_each(|c| *c *= &f);
408            return;
409        }
410
411        // If the degree of the first polynomial is smaller, resize it.
412        if self.degree() < other.degree() {
413            self.coeffs.resize(other.coeffs.len(), F::zero());
414        }
415
416        // Add corresponding coefficients from the second polynomial, scaled by f.
417        self.coeffs
418            .iter_mut()
419            .zip(&other.coeffs)
420            .for_each(|(a, b)| *a += f * b);
421
422        // If the leading coefficient ends up being zero, pop it off.
423        // This can happen:
424        // - if they were the same degree,
425        // - if a polynomial's coefficients were constructed with leading zeros.
426        self.truncate_leading_zeros();
427    }
428}
429
430impl<'a, F: Field> AddAssign<&'a SparsePolynomial<F>> for DensePolynomial<F> {
431    #[inline]
432    fn add_assign(&mut self, other: &'a SparsePolynomial<F>) {
433        // No need to modify self if other is zero
434        if other.is_zero() {
435            return;
436        }
437
438        // If the first polynomial is zero, just copy the second one.
439        if self.is_zero() {
440            self.coeffs.clear();
441            self.coeffs.resize(other.degree() + 1, F::zero());
442            for (i, coeff) in other.iter() {
443                self.coeffs[*i] = *coeff;
444            }
445        } else {
446            // If neither polynomial is zero, we proceed to add the terms.
447            let lhs_degree = self.degree();
448
449            // Resize the coefficients of the left-hand side if necessary.
450            // This is done to ensure that the left-hand side has enough coefficients.
451            let max_degree = lhs_degree.max(other.degree());
452            self.coeffs.resize(max_degree + 1, F::zero());
453
454            // Add the coefficients of the right-hand side to the left-hand side.
455            // - For pow <= lhs_degree, add the coefficients.
456            // - For pow > lhs_degree, set the coefficients (no addition is needed).
457            for (pow, coeff) in other.iter() {
458                if *pow <= lhs_degree {
459                    self.coeffs[*pow] += coeff;
460                } else {
461                    self.coeffs[*pow] = *coeff;
462                }
463            }
464        }
465
466        // Truncate leading zeros after addition
467        self.truncate_leading_zeros();
468    }
469}
470
471impl<F: Field> Neg for DensePolynomial<F> {
472    type Output = Self;
473
474    #[inline]
475    fn neg(mut self) -> Self {
476        self.coeffs.iter_mut().for_each(|coeff| {
477            *coeff = -*coeff;
478        });
479        self
480    }
481}
482
483impl<'a, F: Field> Sub<&'a DensePolynomial<F>> for &DensePolynomial<F> {
484    type Output = DensePolynomial<F>;
485
486    #[inline]
487    fn sub(self, other: &'a DensePolynomial<F>) -> DensePolynomial<F> {
488        let mut result = if self.is_zero() {
489            let mut result = other.clone();
490            result.coeffs.iter_mut().for_each(|c| *c = -(*c));
491            result
492        } else if other.is_zero() {
493            self.clone()
494        } else if self.degree() >= other.degree() {
495            let mut result = self.clone();
496            result
497                .coeffs
498                .iter_mut()
499                .zip(&other.coeffs)
500                .for_each(|(a, b)| *a -= b);
501            result
502        } else {
503            let mut result = self.clone();
504            result.coeffs.resize(other.coeffs.len(), F::zero());
505            result
506                .coeffs
507                .iter_mut()
508                .zip(&other.coeffs)
509                .for_each(|(a, b)| *a -= b);
510            result
511        };
512        result.truncate_leading_zeros();
513        result
514    }
515}
516
517impl<'a, F: Field> Sub<&'a SparsePolynomial<F>> for &DensePolynomial<F> {
518    type Output = DensePolynomial<F>;
519
520    #[inline]
521    fn sub(self, other: &'a SparsePolynomial<F>) -> DensePolynomial<F> {
522        if self.is_zero() {
523            let result = other.clone();
524            (-result).into()
525        } else if other.is_zero() {
526            self.clone()
527        } else {
528            let mut result = self.clone();
529            // If `other` has higher degree than `self`, create a dense vector
530            // storing the upper coefficients of the subtraction
531            let mut upper_coeffs = match other.degree() > result.degree() {
532                true => vec![F::zero(); other.degree() - result.degree()],
533                false => Vec::new(),
534            };
535            for (pow, coeff) in other.iter() {
536                if *pow <= result.degree() {
537                    result.coeffs[*pow] -= coeff;
538                } else {
539                    upper_coeffs[*pow - result.degree() - 1] = -*coeff;
540                }
541            }
542            result.coeffs.extend(upper_coeffs);
543            result
544        }
545    }
546}
547
548impl<'a, F: Field> SubAssign<&'a Self> for DensePolynomial<F> {
549    #[inline]
550    fn sub_assign(&mut self, other: &'a Self) {
551        if self.is_zero() {
552            self.coeffs.resize(other.coeffs.len(), F::zero());
553        } else if other.is_zero() {
554            return;
555        } else if self.degree() >= other.degree() {
556        } else {
557            // Add the necessary number of zero coefficients.
558            self.coeffs.resize(other.coeffs.len(), F::zero());
559        }
560        self.coeffs
561            .iter_mut()
562            .zip(&other.coeffs)
563            .for_each(|(a, b)| {
564                *a -= b;
565            });
566        // If the leading coefficient ends up being zero, pop it off.
567        // This can happen if they were the same degree, or if other's
568        // coefficients were constructed with leading zeros.
569        self.truncate_leading_zeros();
570    }
571}
572
573impl<'a, F: Field> SubAssign<&'a SparsePolynomial<F>> for DensePolynomial<F> {
574    #[inline]
575    fn sub_assign(&mut self, other: &'a SparsePolynomial<F>) {
576        if self.is_zero() {
577            self.coeffs.truncate(0);
578            self.coeffs.resize(other.degree() + 1, F::zero());
579
580            for (i, coeff) in other.iter() {
581                self.coeffs[*i] = (*coeff).neg();
582            }
583        } else if other.is_zero() {
584        } else {
585            // If `other` has higher degree than `self`, create a dense vector
586            // storing the upper coefficients of the subtraction
587            let mut upper_coeffs = match other.degree() > self.degree() {
588                true => vec![F::zero(); other.degree() - self.degree()],
589                false => Vec::new(),
590            };
591            for (pow, coeff) in other.iter() {
592                if *pow <= self.degree() {
593                    self.coeffs[*pow] -= coeff;
594                } else {
595                    upper_coeffs[*pow - self.degree() - 1] = -*coeff;
596                }
597            }
598            self.coeffs.extend(upper_coeffs);
599        }
600    }
601}
602
603impl<'a, F: FftField> Div<&'a DensePolynomial<F>> for &DensePolynomial<F> {
604    type Output = DensePolynomial<F>;
605
606    #[inline]
607    fn div(self, divisor: &'a DensePolynomial<F>) -> DensePolynomial<F> {
608        let a = DenseOrSparsePolynomial::from(self);
609        let b = DenseOrSparsePolynomial::from(divisor);
610        a.divide(&b).expect("division failed")
611    }
612}
613
614impl<F: Field> Mul<F> for &DensePolynomial<F> {
615    type Output = DensePolynomial<F>;
616
617    #[inline]
618    fn mul(self, elem: F) -> DensePolynomial<F> {
619        if self.is_zero() || elem.is_zero() {
620            DensePolynomial::zero()
621        } else {
622            let mut result = self.clone();
623            cfg_iter_mut!(result).for_each(|e| {
624                *e *= elem;
625            });
626            result
627        }
628    }
629}
630
631impl<F: Field> Mul<F> for DensePolynomial<F> {
632    type Output = Self;
633
634    #[inline]
635    fn mul(self, elem: F) -> Self {
636        &self * elem
637    }
638}
639
640/// Performs O(nlogn) multiplication of polynomials if F is smooth.
641impl<'a, F: FftField> Mul<&'a DensePolynomial<F>> for &DensePolynomial<F> {
642    type Output = DensePolynomial<F>;
643
644    #[inline]
645    fn mul(self, other: &'a DensePolynomial<F>) -> DensePolynomial<F> {
646        if self.is_zero() || other.is_zero() {
647            DensePolynomial::zero()
648        } else {
649            let domain = GeneralEvaluationDomain::new(self.coeffs.len() + other.coeffs.len() - 1)
650                .expect("field is not smooth enough to construct domain");
651            let mut self_evals = self.evaluate_over_domain_by_ref(domain);
652            let other_evals = other.evaluate_over_domain_by_ref(domain);
653            self_evals *= &other_evals;
654            self_evals.interpolate()
655        }
656    }
657}
658
659macro_rules! impl_op {
660    ($trait:ident, $method:ident, $field_bound:ident) => {
661        impl<F: $field_bound> $trait<DensePolynomial<F>> for DensePolynomial<F> {
662            type Output = DensePolynomial<F>;
663
664            #[inline]
665            fn $method(self, other: DensePolynomial<F>) -> DensePolynomial<F> {
666                (&self).$method(&other)
667            }
668        }
669
670        impl<'a, F: $field_bound> $trait<&'a DensePolynomial<F>> for DensePolynomial<F> {
671            type Output = DensePolynomial<F>;
672
673            #[inline]
674            fn $method(self, other: &'a DensePolynomial<F>) -> DensePolynomial<F> {
675                (&self).$method(other)
676            }
677        }
678
679        impl<'a, F: $field_bound> $trait<DensePolynomial<F>> for &'a DensePolynomial<F> {
680            type Output = DensePolynomial<F>;
681
682            #[inline]
683            fn $method(self, other: DensePolynomial<F>) -> DensePolynomial<F> {
684                self.$method(&other)
685            }
686        }
687    };
688}
689
690impl<F: Field> Zero for DensePolynomial<F> {
691    /// Returns the zero polynomial.
692    fn zero() -> Self {
693        Self { coeffs: Vec::new() }
694    }
695
696    /// Checks if the given polynomial is zero.
697    fn is_zero(&self) -> bool {
698        self.coeffs.is_empty() || self.coeffs.iter().all(|coeff| coeff.is_zero())
699    }
700}
701
702impl_op!(Add, add, Field);
703impl_op!(Sub, sub, Field);
704impl_op!(Mul, mul, FftField);
705impl_op!(Div, div, FftField);
706
707#[cfg(test)]
708mod tests {
709    use crate::{polynomial::univariate::*, GeneralEvaluationDomain};
710    use ark_ff::{Fp64, MontBackend, MontConfig};
711    use ark_ff::{One, UniformRand};
712    use ark_std::{rand::Rng, test_rng};
713    use ark_test_curves::bls12_381::Fr;
714
715    fn rand_sparse_poly<R: Rng>(degree: usize, rng: &mut R) -> SparsePolynomial<Fr> {
716        // Initialize coeffs so that its guaranteed to have a x^{degree} term
717        let mut coeffs = vec![(degree, Fr::rand(rng))];
718        for i in 0..degree {
719            if !rng.gen_bool(0.8) {
720                coeffs.push((i, Fr::rand(rng)));
721            }
722        }
723        SparsePolynomial::from_coefficients_vec(coeffs)
724    }
725
726    #[test]
727    fn rand_dense_poly_degree() {
728        #[derive(MontConfig)]
729        #[modulus = "5"]
730        #[generator = "2"]
731        pub(crate) struct F5Config;
732
733        let rng = &mut test_rng();
734        pub(crate) type F5 = Fp64<MontBackend<F5Config, 1>>;
735
736        // if the leading coefficient were uniformly sampled from all of F, this
737        // test would fail with high probability ~99.9%
738        for i in 1..=30 {
739            assert_eq!(DensePolynomial::<F5>::rand(i, rng).degree(), i);
740        }
741    }
742
743    #[test]
744    fn double_polynomials_random() {
745        let rng = &mut test_rng();
746        for degree in 0..70 {
747            let p = DensePolynomial::<Fr>::rand(degree, rng);
748            let p_double = &p + &p;
749            let p_quad = &p_double + &p_double;
750            assert_eq!(&(&(&p + &p) + &p) + &p, p_quad);
751        }
752    }
753
754    #[test]
755    fn add_polynomials() {
756        let rng = &mut test_rng();
757        for a_degree in 0..70 {
758            for b_degree in 0..70 {
759                let p1 = DensePolynomial::<Fr>::rand(a_degree, rng);
760                let p2 = DensePolynomial::<Fr>::rand(b_degree, rng);
761                let res1 = &p1 + &p2;
762                let res2 = &p2 + &p1;
763                assert_eq!(res1, res2);
764            }
765        }
766    }
767
768    #[test]
769    fn add_sparse_polynomials() {
770        let rng = &mut test_rng();
771        for a_degree in 0..70 {
772            for b_degree in 0..70 {
773                let p1 = DensePolynomial::<Fr>::rand(a_degree, rng);
774                let p2 = rand_sparse_poly(b_degree, rng);
775                let res = &p1 + &p2;
776                assert_eq!(res, &p1 + &Into::<DensePolynomial<Fr>>::into(p2));
777            }
778        }
779    }
780
781    #[test]
782    fn add_assign_sparse_polynomials() {
783        let rng = &mut test_rng();
784        for a_degree in 0..70 {
785            for b_degree in 0..70 {
786                let p1 = DensePolynomial::<Fr>::rand(a_degree, rng);
787                let p2 = rand_sparse_poly(b_degree, rng);
788
789                let mut res = p1.clone();
790                res += &p2;
791                assert_eq!(res, &p1 + &Into::<DensePolynomial<Fr>>::into(p2));
792            }
793        }
794    }
795
796    #[test]
797    fn add_polynomials_with_mul() {
798        let rng = &mut test_rng();
799        for a_degree in 0..70 {
800            for b_degree in 0..70 {
801                let mut p1 = DensePolynomial::rand(a_degree, rng);
802                let p2 = DensePolynomial::rand(b_degree, rng);
803                let f = Fr::rand(rng);
804                let f_p2 = DensePolynomial::from_coefficients_vec(
805                    p2.coeffs.iter().map(|c| f * c).collect(),
806                );
807                let res2 = &f_p2 + &p1;
808                p1 += (f, &p2);
809                let res1 = p1;
810                assert_eq!(res1, res2);
811            }
812        }
813    }
814
815    #[test]
816    fn sub_polynomials() {
817        let rng = &mut test_rng();
818        for a_degree in 0..70 {
819            for b_degree in 0..70 {
820                let p1 = DensePolynomial::<Fr>::rand(a_degree, rng);
821                let p2 = DensePolynomial::<Fr>::rand(b_degree, rng);
822                let res1 = &p1 - &p2;
823                let res2 = &p2 - &p1;
824                assert_eq!(&res1 + &p2, p1);
825                assert_eq!(res1, -res2);
826            }
827        }
828    }
829
830    #[test]
831    fn sub_sparse_polynomials() {
832        let rng = &mut test_rng();
833        for a_degree in 0..70 {
834            for b_degree in 0..70 {
835                let p1 = DensePolynomial::<Fr>::rand(a_degree, rng);
836                let p2 = rand_sparse_poly(b_degree, rng);
837                let res = &p1 - &p2;
838                assert_eq!(res, &p1 - &Into::<DensePolynomial<Fr>>::into(p2));
839            }
840        }
841    }
842
843    #[test]
844    fn sub_assign_sparse_polynomials() {
845        let rng = &mut test_rng();
846        for a_degree in 0..70 {
847            for b_degree in 0..70 {
848                let p1 = DensePolynomial::<Fr>::rand(a_degree, rng);
849                let p2 = rand_sparse_poly(b_degree, rng);
850
851                let mut res = p1.clone();
852                res -= &p2;
853                assert_eq!(res, &p1 - &Into::<DensePolynomial<Fr>>::into(p2));
854            }
855        }
856    }
857
858    #[test]
859    fn polynomial_additive_identity() {
860        // Test adding polynomials with its negative equals 0
861        let mut rng = test_rng();
862        for degree in 0..70 {
863            let poly = DensePolynomial::<Fr>::rand(degree, &mut rng);
864            let neg = -poly.clone();
865            let result = poly + neg;
866            assert!(result.is_zero());
867            assert_eq!(result.degree(), 0);
868
869            // Test with SubAssign trait
870            let poly = DensePolynomial::<Fr>::rand(degree, &mut rng);
871            let mut result = poly.clone();
872            result -= &poly;
873            assert!(result.is_zero());
874            assert_eq!(result.degree(), 0);
875        }
876    }
877
878    #[test]
879    fn divide_polynomials_fixed() {
880        let dividend = DensePolynomial::from_coefficients_slice(&[
881            "4".parse().unwrap(),
882            "8".parse().unwrap(),
883            "5".parse().unwrap(),
884            "1".parse().unwrap(),
885        ]);
886        let divisor = DensePolynomial::from_coefficients_slice(&[Fr::one(), Fr::one()]); // Construct a monic linear polynomial.
887        let result = &dividend / &divisor;
888        let expected_result = DensePolynomial::from_coefficients_slice(&[
889            "4".parse().unwrap(),
890            "4".parse().unwrap(),
891            "1".parse().unwrap(),
892        ]);
893        assert_eq!(expected_result, result);
894    }
895
896    #[test]
897    fn divide_polynomials_random() {
898        let rng = &mut test_rng();
899
900        for a_degree in 0..50 {
901            for b_degree in 0..50 {
902                let dividend = DensePolynomial::<Fr>::rand(a_degree, rng);
903                let divisor = DensePolynomial::<Fr>::rand(b_degree, rng);
904                // Test the nlogn division
905                if let Some(quotient) =
906                    DenseOrSparsePolynomial::hensel_div(&(&dividend).into(), &(&divisor).into())
907                {
908                    let remainder = &dividend - &(&divisor * &quotient);
909                    // ark_poly assumes that the 0 polynomial has degree 0 so we need to workaround that case
910                    assert!(remainder.degree() < divisor.degree() || remainder.is_zero());
911                }
912
913                // Test the naive division
914                if let Some((quotient, remainder)) =
915                    DenseOrSparsePolynomial::naive_div(&(&dividend).into(), &(&divisor).into())
916                {
917                    assert!(remainder.degree() < divisor.degree() || remainder.is_zero());
918                    assert_eq!(dividend, &(&divisor * &quotient) + &remainder);
919                }
920            }
921        }
922    }
923
924    #[test]
925    fn evaluate_polynomials() {
926        let rng = &mut test_rng();
927        for a_degree in 0..70 {
928            let p = DensePolynomial::rand(a_degree, rng);
929            let point: Fr = Fr::rand(rng);
930            let mut total = Fr::zero();
931            for (i, coeff) in p.coeffs.iter().enumerate() {
932                total += &(point.pow([i as u64]) * coeff);
933            }
934            assert_eq!(p.evaluate(&point), total);
935        }
936    }
937
938    #[test]
939    fn mul_random_element() {
940        let rng = &mut test_rng();
941        for degree in 0..70 {
942            let a = DensePolynomial::<Fr>::rand(degree, rng);
943            let e = Fr::rand(rng);
944            assert_eq!(
945                &a * e,
946                a.naive_mul(&DensePolynomial::from_coefficients_slice(&[e]))
947            )
948        }
949    }
950
951    #[test]
952    fn mul_polynomials_random() {
953        let rng = &mut test_rng();
954        for a_degree in 0..70 {
955            for b_degree in 0..70 {
956                let a = DensePolynomial::<Fr>::rand(a_degree, rng);
957                let b = DensePolynomial::<Fr>::rand(b_degree, rng);
958                assert_eq!(&a * &b, a.naive_mul(&b))
959            }
960        }
961    }
962
963    #[test]
964    fn mul_by_vanishing_poly() {
965        let rng = &mut test_rng();
966        for size in 1..10 {
967            let domain = GeneralEvaluationDomain::new(1 << size).unwrap();
968            for degree in 0..70 {
969                let p = DensePolynomial::<Fr>::rand(degree, rng);
970                let ans1 = p.mul_by_vanishing_poly(domain);
971                let ans2 = &p * &domain.vanishing_polynomial().into();
972                assert_eq!(ans1, ans2);
973            }
974        }
975    }
976
977    #[test]
978    fn divide_by_vanishing_poly() {
979        let rng = &mut test_rng();
980        for size in 1..10 {
981            let domain = GeneralEvaluationDomain::new(1 << size).unwrap();
982            for degree in 0..12 {
983                let p = DensePolynomial::<Fr>::rand(degree * 100, rng);
984                let (quotient, remainder) = p.divide_by_vanishing_poly(domain);
985                let p_recovered = quotient.mul_by_vanishing_poly(domain) + remainder;
986                assert_eq!(p, p_recovered);
987            }
988        }
989    }
990
991    #[test]
992    fn test_leading_zero() {
993        let n = 10;
994        let rand_poly = DensePolynomial::rand(n, &mut test_rng());
995        let coefficients = rand_poly.coeffs.clone();
996        let leading_coefficient: Fr = coefficients[n];
997
998        let negative_leading_coefficient = -leading_coefficient;
999        let inverse_leading_coefficient = leading_coefficient.inverse().unwrap();
1000
1001        let mut inverse_coefficients = coefficients.clone();
1002        inverse_coefficients[n] = inverse_leading_coefficient;
1003
1004        let mut negative_coefficients = coefficients;
1005        negative_coefficients[n] = negative_leading_coefficient;
1006
1007        let negative_poly = DensePolynomial::<Fr>::from_coefficients_vec(negative_coefficients);
1008        let inverse_poly = DensePolynomial::<Fr>::from_coefficients_vec(inverse_coefficients);
1009
1010        let x = &inverse_poly * &rand_poly;
1011        assert_eq!(x.degree(), 2 * n);
1012        assert!(!x.coeffs.last().unwrap().is_zero());
1013
1014        let y = &negative_poly + &rand_poly;
1015        assert_eq!(y.degree(), n - 1);
1016        assert!(!y.coeffs.last().unwrap().is_zero());
1017    }
1018
1019    #[test]
1020    fn evaluate_over_domain_test() {
1021        let rng = &mut ark_std::test_rng();
1022        let domain = crate::domain::Radix2EvaluationDomain::<Fr>::new(1 << 10).unwrap();
1023        let offset = Fr::GENERATOR;
1024        let coset = domain.get_coset(offset).unwrap();
1025        for _ in 0..100 {
1026            let poly = DensePolynomial::<Fr>::rand(1 << 11, rng);
1027            let evaluations = domain
1028                .elements()
1029                .map(|e| poly.evaluate(&e))
1030                .collect::<Vec<_>>();
1031            assert_eq!(evaluations, poly.evaluate_over_domain_by_ref(domain).evals);
1032            let evaluations = coset
1033                .elements()
1034                .map(|e| poly.evaluate(&e))
1035                .collect::<Vec<_>>();
1036            assert_eq!(evaluations, poly.evaluate_over_domain(coset).evals);
1037        }
1038        let zero = DensePolynomial::zero();
1039        let evaluations = domain
1040            .elements()
1041            .map(|e| zero.evaluate(&e))
1042            .collect::<Vec<_>>();
1043        assert_eq!(evaluations, zero.evaluate_over_domain(domain).evals);
1044    }
1045
1046    use crate::Radix2EvaluationDomain;
1047
1048    #[test]
1049    fn evaluate_over_domain_regression_test() {
1050        // See https://github.com/arkworks-rs/algebra/issues/745
1051        #[derive(MontConfig)]
1052        #[modulus = "18446744069414584321"]
1053        #[generator = "7"]
1054        struct FrConfig64;
1055        type F = Fp64<MontBackend<FrConfig64, 1>>;
1056
1057        let degree = 17;
1058        let eval_domain_size = 16;
1059
1060        let poly = DensePolynomial::from_coefficients_vec(vec![F::ONE; degree]);
1061        let domain = Radix2EvaluationDomain::new(eval_domain_size).unwrap();
1062
1063        // Now we get a coset
1064        let offset = F::from(42u64);
1065        let domain = domain.get_coset(offset).unwrap();
1066
1067        // This is the query points of the domain
1068        let query_points: Vec<_> = domain.elements().collect();
1069
1070        let eval1 = poly.evaluate_over_domain_by_ref(domain).evals;
1071        let eval2 = query_points
1072            .iter()
1073            .map(|x| poly.evaluate(x))
1074            .collect::<Vec<_>>();
1075
1076        assert_eq!(eval1, eval2);
1077    }
1078
1079    #[test]
1080    fn test_add_assign_with_zero_self() {
1081        // Create a polynomial poly1 which is a zero polynomial
1082        let mut poly1 = DensePolynomial::<Fr> { coeffs: Vec::new() };
1083
1084        // Create another polynomial poly2, which is: 2 + 3x (coefficients [2, 3])
1085        let poly2 = DensePolynomial {
1086            coeffs: vec![Fr::from(2), Fr::from(3)],
1087        };
1088
1089        // Add poly2 to the zero polynomial
1090        // Since poly1 is zero, it should just take the coefficients of poly2.
1091        poly1 += (Fr::from(1), &poly2);
1092
1093        // After addition, poly1 should be equal to poly2
1094        assert_eq!(poly1.coeffs, vec![Fr::from(2), Fr::from(3)]);
1095    }
1096
1097    #[test]
1098    fn test_add_assign_with_zero_other() {
1099        // Create a polynomial poly1: 2 + 3x (coefficients [2, 3])
1100        let mut poly1 = DensePolynomial {
1101            coeffs: vec![Fr::from(2), Fr::from(3)],
1102        };
1103
1104        // Create an empty polynomial poly2 (zero polynomial)
1105        let poly2 = DensePolynomial::<Fr> { coeffs: Vec::new() };
1106
1107        // Add zero polynomial poly2 to poly1.
1108        // Since poly2 is zero, poly1 should remain unchanged.
1109        poly1 += (Fr::from(1), &poly2);
1110
1111        // After addition, poly1 should still be [2, 3]
1112        assert_eq!(poly1.coeffs, vec![Fr::from(2), Fr::from(3)]);
1113    }
1114
1115    #[test]
1116    fn test_add_assign_with_different_degrees() {
1117        // Create polynomial poly1: 1 + 2x + 3x^2 (coefficients [1, 2, 3])
1118        let mut poly1 = DensePolynomial {
1119            coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1120        };
1121
1122        // Create another polynomial poly2: 4 + 5x (coefficients [4, 5])
1123        let poly2 = DensePolynomial {
1124            coeffs: vec![Fr::from(4), Fr::from(5)],
1125        };
1126
1127        // Add poly2 to poly1.
1128        // poly1 is degree 2, poly2 is degree 1, so poly2 will be padded with a zero
1129        // to match the degree of poly1.
1130        poly1 += (Fr::from(1), &poly2);
1131
1132        // After addition, the result should be:
1133        // 5 + 7x + 3x^2 (coefficients [5, 7, 3])
1134        assert_eq!(poly1.coeffs, vec![Fr::from(5), Fr::from(7), Fr::from(3)]);
1135    }
1136
1137    #[test]
1138    fn test_add_assign_with_equal_degrees() {
1139        // Create polynomial poly1: 1 + 2x + 3x^2 (coefficients [1, 2, 3])
1140        let mut poly1 = DensePolynomial {
1141            coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1142        };
1143
1144        // Create polynomial poly2: 4 + 5x + 6x^2 (coefficients [4, 5, 6])
1145        let poly2 = DensePolynomial {
1146            coeffs: vec![Fr::from(4), Fr::from(5), Fr::from(6)],
1147        };
1148
1149        // Add poly2 to poly1.
1150        // Since both polynomials have the same degree, we can directly add corresponding terms.
1151        poly1 += (Fr::from(1), &poly2);
1152
1153        // After addition, the result should be:
1154        // 5 + 7x + 9x^2 (coefficients [5, 7, 9])
1155        assert_eq!(poly1.coeffs, vec![Fr::from(5), Fr::from(7), Fr::from(9)]);
1156    }
1157
1158    #[test]
1159    fn test_add_assign_with_smaller_degrees() {
1160        // Create polynomial poly1: 1 + 2x (degree 1)
1161        let mut poly1 = DensePolynomial {
1162            coeffs: vec![Fr::from(1), Fr::from(2)],
1163        };
1164
1165        // Create polynomial poly2: 3 + 4x + 5x^2 (degree 2)
1166        let poly2 = DensePolynomial {
1167            coeffs: vec![Fr::from(3), Fr::from(4), Fr::from(5)],
1168        };
1169
1170        // Add poly2 to poly1.
1171        // poly1 has degree 1, poly2 has degree 2. So poly1 must be padded with zero coefficients
1172        // for the higher degree terms to match poly2's degree.
1173        poly1 += (Fr::from(1), &poly2);
1174
1175        // After addition, the result should be:
1176        // 4 + 6x + 5x^2 (coefficients [4, 6, 5])
1177        assert_eq!(poly1.coeffs, vec![Fr::from(4), Fr::from(6), Fr::from(5)]);
1178    }
1179
1180    #[test]
1181    fn test_add_assign_mixed_with_zero_self() {
1182        // Create a zero DensePolynomial
1183        let mut poly1 = DensePolynomial::<Fr> { coeffs: Vec::new() };
1184
1185        // Create a SparsePolynomial: 2 + 3x (coefficients [2, 3])
1186        let poly2 =
1187            SparsePolynomial::from_coefficients_slice(&[(0, Fr::from(2)), (1, Fr::from(3))]);
1188
1189        // Add poly2 to the zero polynomial
1190        poly1 += &poly2;
1191
1192        // After addition, the result should be 2 + 3x
1193        assert_eq!(poly1.coeffs, vec![Fr::from(2), Fr::from(3)]);
1194    }
1195
1196    #[test]
1197    fn test_add_assign_mixed_with_zero_other() {
1198        // Create a DensePolynomial: 2 + 3x (coefficients [2, 3])
1199        let mut poly1 = DensePolynomial {
1200            coeffs: vec![Fr::from(2), Fr::from(3)],
1201        };
1202
1203        // Create a zero SparsePolynomial
1204        let poly2 = SparsePolynomial::from_coefficients_slice(&[]);
1205
1206        // Add poly2 to poly1
1207        poly1 += &poly2;
1208
1209        // After addition, the result should still be 2 + 3x
1210        assert_eq!(poly1.coeffs, vec![Fr::from(2), Fr::from(3)]);
1211    }
1212
1213    #[test]
1214    fn test_add_assign_mixed_with_different_degrees() {
1215        // Create a DensePolynomial: 1 + 2x + 3x^2 (coefficients [1, 2, 3])
1216        let mut poly1 = DensePolynomial {
1217            coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1218        };
1219
1220        // Create a SparsePolynomial: 4 + 5x (coefficients [4, 5])
1221        let poly2 =
1222            SparsePolynomial::from_coefficients_slice(&[(0, Fr::from(4)), (1, Fr::from(5))]);
1223
1224        // Add poly2 to poly1
1225        poly1 += &poly2;
1226
1227        // After addition, the result should be 5 + 7x + 3x^2 (coefficients [5, 7, 3])
1228        assert_eq!(poly1.coeffs, vec![Fr::from(5), Fr::from(7), Fr::from(3)]);
1229    }
1230
1231    #[test]
1232    fn test_add_assign_mixed_with_smaller_degree() {
1233        // Create a DensePolynomial: 1 + 2x (degree 1)
1234        let mut poly1 = DensePolynomial {
1235            coeffs: vec![Fr::from(1), Fr::from(2)],
1236        };
1237
1238        // Create a SparsePolynomial: 3 + 4x + 5x^2 (degree 2)
1239        let poly2 = SparsePolynomial::from_coefficients_slice(&[
1240            (0, Fr::from(3)),
1241            (1, Fr::from(4)),
1242            (2, Fr::from(5)),
1243        ]);
1244
1245        // Add poly2 to poly1
1246        poly1 += &poly2;
1247
1248        // After addition, the result should be: 4 + 6x + 5x^2 (coefficients [4, 6, 5])
1249        assert_eq!(poly1.coeffs, vec![Fr::from(4), Fr::from(6), Fr::from(5)]);
1250    }
1251
1252    #[test]
1253    fn test_add_assign_mixed_with_equal_degrees() {
1254        // Create a DensePolynomial: 1 + 2x + 3x^2 (coefficients [1, 2, 3])
1255        let mut poly1 = DensePolynomial {
1256            coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1257        };
1258
1259        // Create a SparsePolynomial: 4 + 5x + 6x^2 (coefficients [4, 5, 6])
1260        let poly2 = SparsePolynomial::from_coefficients_slice(&[
1261            (0, Fr::from(4)),
1262            (1, Fr::from(5)),
1263            (2, Fr::from(6)),
1264        ]);
1265
1266        // Add poly2 to poly1
1267        poly1 += &poly2;
1268
1269        // After addition, the result should be 5 + 7x + 9x^2 (coefficients [5, 7, 9])
1270        assert_eq!(poly1.coeffs, vec![Fr::from(5), Fr::from(7), Fr::from(9)]);
1271    }
1272
1273    #[test]
1274    fn test_add_assign_mixed_with_larger_degree() {
1275        // Create a DensePolynomial: 1 + 2x + 3x^2 + 4x^3 (degree 3)
1276        let mut poly1 = DensePolynomial {
1277            coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3), Fr::from(4)],
1278        };
1279
1280        // Create a SparsePolynomial: 3 + 4x (degree 1)
1281        let poly2 =
1282            SparsePolynomial::from_coefficients_slice(&[(0, Fr::from(3)), (1, Fr::from(4))]);
1283
1284        // Add poly2 to poly1
1285        poly1 += &poly2;
1286
1287        // After addition, the result should be: 4 + 6x + 3x^2 + 4x^3 (coefficients [4, 6, 3, 4])
1288        assert_eq!(
1289            poly1.coeffs,
1290            vec![Fr::from(4), Fr::from(6), Fr::from(3), Fr::from(4)]
1291        );
1292    }
1293
1294    #[test]
1295    fn test_truncate_leading_zeros_after_addition() {
1296        // Create a DensePolynomial: 1 + 2x + 3x^2 (coefficients [1, 2, 3])
1297        let mut poly1 = DensePolynomial {
1298            coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1299        };
1300
1301        // Create a SparsePolynomial: -1 - 2x - 3x^2 (coefficients [-1, -2, -3])
1302        let poly2 = SparsePolynomial::from_coefficients_slice(&[
1303            (0, -Fr::from(1)),
1304            (1, -Fr::from(2)),
1305            (2, -Fr::from(3)),
1306        ]);
1307
1308        // Add poly2 to poly1, which should result in a zero polynomial
1309        poly1 += &poly2;
1310
1311        // The resulting polynomial should be zero, with an empty coefficient vector
1312        assert!(poly1.is_zero());
1313        assert_eq!(poly1.coeffs, vec![]);
1314    }
1315
1316    #[test]
1317    fn test_truncate_leading_zeros_after_sparse_addition() {
1318        // Create a DensePolynomial with leading non-zero coefficients.
1319        let poly1 = DensePolynomial {
1320            coeffs: vec![Fr::from(3), Fr::from(2), Fr::from(1)],
1321        };
1322
1323        // Create a SparsePolynomial to subtract the coefficients of poly1,
1324        // leaving trailing zeros after addition.
1325        let poly2 = SparsePolynomial::from_coefficients_slice(&[
1326            (0, -Fr::from(3)),
1327            (1, -Fr::from(2)),
1328            (2, -Fr::from(1)),
1329        ]);
1330
1331        // Perform addition using the Add implementation.
1332        let result = &poly1 + &poly2;
1333
1334        // Assert that the resulting polynomial is zero.
1335        assert!(result.is_zero(), "The resulting polynomial should be zero.");
1336        assert_eq!(result.coeffs, vec![], "Leading zeros were not truncated.");
1337    }
1338
1339    #[test]
1340    fn test_dense_dense_add_assign_with_zero_self() {
1341        // Create a zero polynomial
1342        let mut poly1 = DensePolynomial { coeffs: Vec::new() };
1343
1344        // Create a non-zero polynomial: 2 + 3x
1345        let poly2 = DensePolynomial {
1346            coeffs: vec![Fr::from(2), Fr::from(3)],
1347        };
1348
1349        // Add the non-zero polynomial to the zero polynomial
1350        poly1 += &poly2;
1351
1352        // Check that poly1 now equals poly2
1353        assert_eq!(poly1.coeffs, poly2.coeffs);
1354    }
1355
1356    #[test]
1357    fn test_dense_dense_add_assign_with_zero_other() {
1358        // Create a non-zero polynomial: 2 + 3x
1359        let mut poly1 = DensePolynomial {
1360            coeffs: vec![Fr::from(2), Fr::from(3)],
1361        };
1362
1363        // Create a zero polynomial
1364        let poly2 = DensePolynomial { coeffs: Vec::new() };
1365
1366        // Add the zero polynomial to poly1
1367        poly1 += &poly2;
1368
1369        // Check that poly1 remains unchanged
1370        assert_eq!(poly1.coeffs, vec![Fr::from(2), Fr::from(3)]);
1371    }
1372
1373    #[test]
1374    fn test_dense_dense_add_assign_with_different_degrees() {
1375        // Create a polynomial: 1 + 2x + 3x^2
1376        let mut poly1 = DensePolynomial {
1377            coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1378        };
1379
1380        // Create a smaller polynomial: 4 + 5x
1381        let poly2 = DensePolynomial {
1382            coeffs: vec![Fr::from(4), Fr::from(5)],
1383        };
1384
1385        // Add the smaller polynomial to the larger one
1386        poly1 += &poly2;
1387
1388        assert_eq!(poly1.coeffs, vec![Fr::from(5), Fr::from(7), Fr::from(3)]);
1389    }
1390
1391    #[test]
1392    fn test_dense_dense_truncate_leading_zeros_after_addition() {
1393        // Create a first polynomial
1394        let mut poly1 = DensePolynomial {
1395            coeffs: vec![Fr::from(1), Fr::from(2)],
1396        };
1397
1398        // Create another polynomial that will cancel out the first two terms
1399        let poly2 = DensePolynomial {
1400            coeffs: vec![-poly1.coeffs[0], -poly1.coeffs[1]],
1401        };
1402
1403        // Add the two polynomials
1404        poly1 += &poly2;
1405
1406        // Check that the resulting polynomial is zero (empty coefficients)
1407        assert!(poly1.is_zero());
1408        assert_eq!(poly1.coeffs, vec![]);
1409    }
1410
1411    #[test]
1412    fn test_dense_dense_add_assign_with_equal_degrees() {
1413        // Create two polynomials with the same degree
1414        let mut poly1 = DensePolynomial {
1415            coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1416        };
1417        let poly2 = DensePolynomial {
1418            coeffs: vec![Fr::from(4), Fr::from(5), Fr::from(6)],
1419        };
1420
1421        // Add the polynomials
1422        poly1 += &poly2;
1423
1424        // Check the resulting coefficients
1425        assert_eq!(poly1.coeffs, vec![Fr::from(5), Fr::from(7), Fr::from(9)]);
1426    }
1427
1428    #[test]
1429    fn test_dense_dense_add_assign_with_other_zero_truncates_leading_zeros() {
1430        use ark_test_curves::bls12_381::Fr;
1431
1432        // Create a polynomial with leading zeros: 1 + 2x + 0x^2 + 0x^3
1433        let mut poly1 = DensePolynomial {
1434            coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(0), Fr::from(0)],
1435        };
1436
1437        // Create a zero polynomial
1438        let poly2 = DensePolynomial { coeffs: Vec::new() };
1439
1440        // Add the zero polynomial to poly1
1441        poly1 += &poly2;
1442
1443        // Check that the leading zeros are truncated
1444        assert_eq!(poly1.coeffs, vec![Fr::from(1), Fr::from(2)]);
1445
1446        // Ensure the polynomial is not zero (as it has non-zero coefficients)
1447        assert!(!poly1.is_zero());
1448    }
1449}