1use 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#[derive(Clone, PartialEq, Eq, Hash, Default, CanonicalSerialize, CanonicalDeserialize)]
38#[must_use]
39pub struct DensePolynomial<F: Field> {
40 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 pub fn zero() -> Self {
62 Self { coeffs: Vec::new() }
63 }
64
65 pub fn is_zero(&self) -> bool {
67 self.coeffs.is_empty() || self.coeffs.iter().all(|coeff| coeff.is_zero())
68 }
69
70 pub fn from_coefficients_slice(coeffs: &[F]) -> Self {
72 Self::from_coefficients_vec(coeffs.to_vec())
73 }
74
75 pub fn from_coefficients_vec(mut coeffs: Vec<F>) -> Self {
77 while let Some(true) = coeffs.last().map(|c| c.is_zero()) {
79 coeffs.pop();
80 }
81 assert!(coeffs.last().map_or(true, |coeff| !coeff.is_zero()));
83
84 Self { coeffs }
85 }
86
87 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 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 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 random_coeffs[d] = F::rand(rng);
125 }
126 Self::from_coefficients_vec(random_coeffs)
127 }
128
129 pub fn coeffs(&self) -> &[F] {
131 &self.coeffs
132 }
133
134 #[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 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 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 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 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 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 cfg_iter_mut!(result.coeffs).zip(&self.coeffs).for_each(|(a, b)| *a += b);
211 result
212 };
213 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 } else if self.degree() >= other.degree() {
229 cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a += b);
231 } else {
232 self.coeffs.resize(other.coeffs.len(), F::zero());
234 cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a += b);
236 }
237 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 } else if self.degree() >= other.degree() {
272 cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| {
274 *a += f * b;
275 });
276 } else {
277 self.coeffs.resize(other.coeffs.len(), F::zero());
279 cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| {
281 *a += f * b;
282 });
283 }
284 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 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 cfg_iter_mut!(result.coeffs).zip(&other.coeffs).for_each(|(a, b)| {
326 *a -= b;
327 });
328 result
329 };
330 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 } else if self.degree() >= other.degree() {
349 cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a -= b);
351 } else {
352 self.coeffs.resize(other.coeffs.len(), F::zero());
354 cfg_iter_mut!(self.coeffs).zip(&other.coeffs).for_each(|(a, b)| *a -= b);
356 }
357 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 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 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 #[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 #[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
441impl<'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
459impl<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
470impl<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
481impl<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
489impl<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()]); 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(&(÷nd).into(), &(&divisor).into()).unwrap();
602 assert_eq!(dividend, &(&divisor * "ient) + &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 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 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 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 }
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}