ark_poly/polynomial/univariate/
mod.rs1use crate::{DenseUVPolynomial, EvaluationDomain, Evaluations, Polynomial};
4use ark_ff::{FftField, Field, Zero};
5use ark_std::{borrow::Cow, vec::*};
6use DenseOrSparsePolynomial::*;
7
8mod dense;
9mod sparse;
10
11pub use dense::DensePolynomial;
12pub use sparse::SparsePolynomial;
13
14#[cfg(feature = "parallel")]
15use rayon::prelude::*;
16
17#[derive(Clone)]
19pub enum DenseOrSparsePolynomial<'a, F: Field> {
20 SPolynomial(Cow<'a, SparsePolynomial<F>>),
22 DPolynomial(Cow<'a, DensePolynomial<F>>),
24}
25
26impl<'a, F: 'a + Field> From<DensePolynomial<F>> for DenseOrSparsePolynomial<'a, F> {
27 fn from(other: DensePolynomial<F>) -> Self {
28 DPolynomial(Cow::Owned(other))
29 }
30}
31
32impl<'a, F: 'a + Field> From<&'a DensePolynomial<F>> for DenseOrSparsePolynomial<'a, F> {
33 fn from(other: &'a DensePolynomial<F>) -> Self {
34 DPolynomial(Cow::Borrowed(other))
35 }
36}
37
38impl<'a, F: 'a + Field> From<SparsePolynomial<F>> for DenseOrSparsePolynomial<'a, F> {
39 fn from(other: SparsePolynomial<F>) -> Self {
40 SPolynomial(Cow::Owned(other))
41 }
42}
43
44impl<'a, F: Field> From<&'a SparsePolynomial<F>> for DenseOrSparsePolynomial<'a, F> {
45 fn from(other: &'a SparsePolynomial<F>) -> Self {
46 SPolynomial(Cow::Borrowed(other))
47 }
48}
49
50impl<'a, F: Field> From<DenseOrSparsePolynomial<'a, F>> for DensePolynomial<F> {
51 fn from(other: DenseOrSparsePolynomial<'a, F>) -> DensePolynomial<F> {
52 match other {
53 DPolynomial(p) => p.into_owned(),
54 SPolynomial(p) => p.into_owned().into(),
55 }
56 }
57}
58
59impl<'a, F: 'a + Field> TryInto<SparsePolynomial<F>> for DenseOrSparsePolynomial<'a, F> {
60 type Error = ();
61
62 fn try_into(self) -> Result<SparsePolynomial<F>, ()> {
63 match self {
64 SPolynomial(p) => Ok(p.into_owned()),
65 _ => Err(()),
66 }
67 }
68}
69
70impl<'a, F: Field> DenseOrSparsePolynomial<'a, F> {
71 pub fn is_zero(&self) -> bool {
73 match self {
74 SPolynomial(s) => s.is_zero(),
75 DPolynomial(d) => d.is_zero(),
76 }
77 }
78
79 pub fn degree(&self) -> usize {
81 match self {
82 SPolynomial(s) => s.degree(),
83 DPolynomial(d) => d.degree(),
84 }
85 }
86
87 #[inline]
88 fn leading_coefficient(&self) -> Option<&F> {
89 match self {
90 SPolynomial(p) => p.last().map(|(_, c)| c),
91 DPolynomial(p) => p.last(),
92 }
93 }
94
95 #[inline]
96 fn iter_with_index(&self) -> Vec<(usize, F)> {
97 match self {
98 SPolynomial(p) => p.to_vec(),
99 DPolynomial(p) => p.iter().cloned().enumerate().collect(),
100 }
101 }
102
103 pub fn divide_with_q_and_r(
106 &self,
107 divisor: &Self,
108 ) -> Option<(DensePolynomial<F>, DensePolynomial<F>)> {
109 if self.is_zero() {
110 Some((DensePolynomial::zero(), DensePolynomial::zero()))
111 } else if divisor.is_zero() {
112 panic!("Dividing by zero polynomial")
113 } else if self.degree() < divisor.degree() {
114 Some((DensePolynomial::zero(), self.clone().into()))
115 } else {
116 let mut quotient = vec![F::zero(); self.degree() - divisor.degree() + 1];
118 let mut remainder: DensePolynomial<F> = self.clone().into();
119 let divisor_leading_inv = divisor.leading_coefficient().unwrap().inverse().unwrap();
121 while !remainder.is_zero() && remainder.degree() >= divisor.degree() {
122 let cur_q_coeff = *remainder.coeffs.last().unwrap() * divisor_leading_inv;
123 let cur_q_degree = remainder.degree() - divisor.degree();
124 quotient[cur_q_degree] = cur_q_coeff;
125
126 for (i, div_coeff) in divisor.iter_with_index() {
127 remainder[cur_q_degree + i] -= &(cur_q_coeff * div_coeff);
128 }
129 while let Some(true) = remainder.coeffs.last().map(|c| c.is_zero()) {
130 remainder.coeffs.pop();
131 }
132 }
133 Some((DensePolynomial::from_coefficients_vec(quotient), remainder))
134 }
135 }
136}
137impl<'a, F: 'a + FftField> DenseOrSparsePolynomial<'a, F> {
138 pub fn evaluate_over_domain<D: EvaluationDomain<F>>(
141 poly: impl Into<Self>,
142 domain: D,
143 ) -> Evaluations<F, D> {
144 let poly = poly.into();
145 poly.eval_over_domain_helper(domain)
146 }
147
148 fn eval_over_domain_helper<D: EvaluationDomain<F>>(self, domain: D) -> Evaluations<F, D> {
149 let eval_sparse_poly = |s: &SparsePolynomial<F>| {
150 let evals = domain.elements().map(|elem| s.evaluate(&elem)).collect();
151 Evaluations::from_vec_and_domain(evals, domain)
152 };
153
154 match self {
155 SPolynomial(Cow::Borrowed(s)) => eval_sparse_poly(s),
156 SPolynomial(Cow::Owned(s)) => eval_sparse_poly(&s),
157 DPolynomial(Cow::Borrowed(d)) => {
158 if d.is_zero() {
159 Evaluations::zero(domain)
160 } else {
161 let mut chunks = d.coeffs.chunks(domain.size());
162 let mut first = chunks.next().unwrap().to_vec();
163 let offset = domain.coset_offset();
164 for (i, chunk) in chunks.enumerate() {
166 if offset.is_one() {
167 cfg_iter_mut!(first).zip(chunk).for_each(|(x, y)| *x += y);
168 } else {
169 let offset_power = offset.pow(&[((i + 1) * domain.size()) as u64]);
170 cfg_iter_mut!(first)
171 .zip(chunk)
172 .for_each(|(x, y)| *x += offset_power * y);
173 }
174 }
175 domain.fft_in_place(&mut first);
176 Evaluations::from_vec_and_domain(first, domain)
177 }
178 },
179 DPolynomial(Cow::Owned(mut d)) => {
180 if d.is_zero() {
181 Evaluations::zero(domain)
182 } else {
183 let mut chunks = d.coeffs.chunks_mut(domain.size());
184 let first = chunks.next().unwrap();
185 let offset = domain.coset_offset();
186 for (i, chunk) in chunks.enumerate() {
188 if offset.is_one() {
189 cfg_iter_mut!(first).zip(chunk).for_each(|(x, y)| *x += y);
190 } else {
191 let offset_power = offset.pow(&[((i + 1) * domain.size()) as u64]);
192 cfg_iter_mut!(first)
193 .zip(chunk)
194 .for_each(|(x, y)| *x += offset_power * y);
195 }
196 }
197 domain.fft_in_place(&mut d.coeffs);
198 Evaluations::from_vec_and_domain(d.coeffs, domain)
199 }
200 },
201 }
202 }
203}