1use 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#[derive(Clone, PartialEq, Eq, Hash, Default, CanonicalSerialize, CanonicalDeserialize)]
23pub struct DensePolynomial<F: Field> {
24 pub coeffs: Vec<F>,
26}
27
28impl<F: Field> Polynomial<F> for DensePolynomial<F> {
29 type Point = F;
30
31 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 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")]
54const MIN_ELEMENTS_PER_THREAD: usize = 16;
57
58impl<F: Field> DensePolynomial<F> {
59 #[inline]
60 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 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 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 fn from_coefficients_slice(coeffs: &[F]) -> Self {
98 Self::from_coefficients_vec(coeffs.to_vec())
99 }
100
101 fn from_coefficients_vec(coeffs: Vec<F>) -> Self {
103 let mut result = Self { coeffs };
104 result.truncate_leading_zeros();
106 assert!(result.coeffs.last().map_or(true, |coeff| !coeff.is_zero()));
109 result
110 }
111
112 fn coeffs(&self) -> &[F] {
114 &self.coeffs
115 }
116
117 fn rand<R: Rng>(d: usize, rng: &mut R) -> Self {
133 let mut random_coeffs = Vec::new();
134
135 if d > 0 {
136 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 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 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 (Self::zero(), self.clone())
174 } else {
175 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 let mut remainder_vec = self.coeffs[0..domain_size].to_vec();
203 cfg_iter_mut!(remainder_vec)
204 .zip("ient_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 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 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 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 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 self.is_zero() {
299 return other.clone();
300 }
301
302 if other.is_zero() {
304 return self.clone();
305 }
306
307 let (longer, shorter) = if self.degree() >= other.degree() {
309 (self, other)
310 } else {
311 (other, self)
312 };
313
314 let mut result = longer.clone();
316
317 cfg_iter_mut!(result)
320 .zip(&shorter.coeffs)
321 .for_each(|(a, b)| *a += b);
322
323 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 let additional_len = other.degree().saturating_sub(result.degree());
347 result.coeffs.reserve(additional_len);
348
349 for (pow, coeff) in other.iter() {
351 if let Some(target) = result.coeffs.get_mut(*pow) {
352 *target += coeff;
353 } else {
354 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 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 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 if other.is_zero() {
400 return;
401 }
402
403 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 self.degree() < other.degree() {
413 self.coeffs.resize(other.coeffs.len(), F::zero());
414 }
415
416 self.coeffs
418 .iter_mut()
419 .zip(&other.coeffs)
420 .for_each(|(a, b)| *a += f * b);
421
422 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 if other.is_zero() {
435 return;
436 }
437
438 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 let lhs_degree = self.degree();
448
449 let max_degree = lhs_degree.max(other.degree());
452 self.coeffs.resize(max_degree + 1, F::zero());
453
454 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 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 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 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 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 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
640impl<'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 fn zero() -> Self {
693 Self { coeffs: Vec::new() }
694 }
695
696 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 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 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 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 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()]); let result = ÷nd / &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 if let Some(quotient) =
906 DenseOrSparsePolynomial::hensel_div(&(÷nd).into(), &(&divisor).into())
907 {
908 let remainder = ÷nd - &(&divisor * "ient);
909 assert!(remainder.degree() < divisor.degree() || remainder.is_zero());
911 }
912
913 if let Some((quotient, remainder)) =
915 DenseOrSparsePolynomial::naive_div(&(÷nd).into(), &(&divisor).into())
916 {
917 assert!(remainder.degree() < divisor.degree() || remainder.is_zero());
918 assert_eq!(dividend, &(&divisor * "ient) + &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 #[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 let offset = F::from(42u64);
1065 let domain = domain.get_coset(offset).unwrap();
1066
1067 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 let mut poly1 = DensePolynomial::<Fr> { coeffs: Vec::new() };
1083
1084 let poly2 = DensePolynomial {
1086 coeffs: vec![Fr::from(2), Fr::from(3)],
1087 };
1088
1089 poly1 += (Fr::from(1), &poly2);
1092
1093 assert_eq!(poly1.coeffs, vec![Fr::from(2), Fr::from(3)]);
1095 }
1096
1097 #[test]
1098 fn test_add_assign_with_zero_other() {
1099 let mut poly1 = DensePolynomial {
1101 coeffs: vec![Fr::from(2), Fr::from(3)],
1102 };
1103
1104 let poly2 = DensePolynomial::<Fr> { coeffs: Vec::new() };
1106
1107 poly1 += (Fr::from(1), &poly2);
1110
1111 assert_eq!(poly1.coeffs, vec![Fr::from(2), Fr::from(3)]);
1113 }
1114
1115 #[test]
1116 fn test_add_assign_with_different_degrees() {
1117 let mut poly1 = DensePolynomial {
1119 coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1120 };
1121
1122 let poly2 = DensePolynomial {
1124 coeffs: vec![Fr::from(4), Fr::from(5)],
1125 };
1126
1127 poly1 += (Fr::from(1), &poly2);
1131
1132 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 let mut poly1 = DensePolynomial {
1141 coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1142 };
1143
1144 let poly2 = DensePolynomial {
1146 coeffs: vec![Fr::from(4), Fr::from(5), Fr::from(6)],
1147 };
1148
1149 poly1 += (Fr::from(1), &poly2);
1152
1153 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 let mut poly1 = DensePolynomial {
1162 coeffs: vec![Fr::from(1), Fr::from(2)],
1163 };
1164
1165 let poly2 = DensePolynomial {
1167 coeffs: vec![Fr::from(3), Fr::from(4), Fr::from(5)],
1168 };
1169
1170 poly1 += (Fr::from(1), &poly2);
1174
1175 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 let mut poly1 = DensePolynomial::<Fr> { coeffs: Vec::new() };
1184
1185 let poly2 =
1187 SparsePolynomial::from_coefficients_slice(&[(0, Fr::from(2)), (1, Fr::from(3))]);
1188
1189 poly1 += &poly2;
1191
1192 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 let mut poly1 = DensePolynomial {
1200 coeffs: vec![Fr::from(2), Fr::from(3)],
1201 };
1202
1203 let poly2 = SparsePolynomial::from_coefficients_slice(&[]);
1205
1206 poly1 += &poly2;
1208
1209 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 let mut poly1 = DensePolynomial {
1217 coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1218 };
1219
1220 let poly2 =
1222 SparsePolynomial::from_coefficients_slice(&[(0, Fr::from(4)), (1, Fr::from(5))]);
1223
1224 poly1 += &poly2;
1226
1227 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 let mut poly1 = DensePolynomial {
1235 coeffs: vec![Fr::from(1), Fr::from(2)],
1236 };
1237
1238 let poly2 = SparsePolynomial::from_coefficients_slice(&[
1240 (0, Fr::from(3)),
1241 (1, Fr::from(4)),
1242 (2, Fr::from(5)),
1243 ]);
1244
1245 poly1 += &poly2;
1247
1248 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 let mut poly1 = DensePolynomial {
1256 coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1257 };
1258
1259 let poly2 = SparsePolynomial::from_coefficients_slice(&[
1261 (0, Fr::from(4)),
1262 (1, Fr::from(5)),
1263 (2, Fr::from(6)),
1264 ]);
1265
1266 poly1 += &poly2;
1268
1269 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 let mut poly1 = DensePolynomial {
1277 coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3), Fr::from(4)],
1278 };
1279
1280 let poly2 =
1282 SparsePolynomial::from_coefficients_slice(&[(0, Fr::from(3)), (1, Fr::from(4))]);
1283
1284 poly1 += &poly2;
1286
1287 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 let mut poly1 = DensePolynomial {
1298 coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1299 };
1300
1301 let poly2 = SparsePolynomial::from_coefficients_slice(&[
1303 (0, -Fr::from(1)),
1304 (1, -Fr::from(2)),
1305 (2, -Fr::from(3)),
1306 ]);
1307
1308 poly1 += &poly2;
1310
1311 assert!(poly1.is_zero());
1313 assert_eq!(poly1.coeffs, vec![]);
1314 }
1315
1316 #[test]
1317 fn test_truncate_leading_zeros_after_sparse_addition() {
1318 let poly1 = DensePolynomial {
1320 coeffs: vec![Fr::from(3), Fr::from(2), Fr::from(1)],
1321 };
1322
1323 let poly2 = SparsePolynomial::from_coefficients_slice(&[
1326 (0, -Fr::from(3)),
1327 (1, -Fr::from(2)),
1328 (2, -Fr::from(1)),
1329 ]);
1330
1331 let result = &poly1 + &poly2;
1333
1334 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 let mut poly1 = DensePolynomial { coeffs: Vec::new() };
1343
1344 let poly2 = DensePolynomial {
1346 coeffs: vec![Fr::from(2), Fr::from(3)],
1347 };
1348
1349 poly1 += &poly2;
1351
1352 assert_eq!(poly1.coeffs, poly2.coeffs);
1354 }
1355
1356 #[test]
1357 fn test_dense_dense_add_assign_with_zero_other() {
1358 let mut poly1 = DensePolynomial {
1360 coeffs: vec![Fr::from(2), Fr::from(3)],
1361 };
1362
1363 let poly2 = DensePolynomial { coeffs: Vec::new() };
1365
1366 poly1 += &poly2;
1368
1369 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 let mut poly1 = DensePolynomial {
1377 coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(3)],
1378 };
1379
1380 let poly2 = DensePolynomial {
1382 coeffs: vec![Fr::from(4), Fr::from(5)],
1383 };
1384
1385 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 let mut poly1 = DensePolynomial {
1395 coeffs: vec![Fr::from(1), Fr::from(2)],
1396 };
1397
1398 let poly2 = DensePolynomial {
1400 coeffs: vec![-poly1.coeffs[0], -poly1.coeffs[1]],
1401 };
1402
1403 poly1 += &poly2;
1405
1406 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 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 poly1 += &poly2;
1423
1424 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 let mut poly1 = DensePolynomial {
1434 coeffs: vec![Fr::from(1), Fr::from(2), Fr::from(0), Fr::from(0)],
1435 };
1436
1437 let poly2 = DensePolynomial { coeffs: Vec::new() };
1439
1440 poly1 += &poly2;
1442
1443 assert_eq!(poly1.coeffs, vec![Fr::from(1), Fr::from(2)]);
1445
1446 assert!(!poly1.is_zero());
1448 }
1449}