1use ark_ff::{FftField, Field, Zero};
12use ark_poly::{domain::DomainCoeff, EvaluationDomain, Radix2EvaluationDomain};
13use ark_std::{
14 fmt,
15 marker::PhantomData,
16 ops::{Add, Mul, MulAssign},
17 rand::Rng,
18 vec::Vec,
19 UniformRand,
20};
21use itertools::{
22 EitherOrBoth::{Both, Left, Right},
23 Itertools,
24};
25
26use crate::prelude::PCSError;
27
28pub trait GroupCoeff<F: Field>:
34 Clone + fmt::Debug + Copy + Add<Self, Output = Self> + MulAssign<F> + Zero + UniformRand + Sized
35{
36}
37
38impl<T, F: Field> GroupCoeff<F> for T where
39 T: Clone
40 + fmt::Debug
41 + Copy
42 + Add<Self, Output = Self>
43 + MulAssign<F>
44 + Zero
45 + UniformRand
46 + Sized
47{
48}
49
50#[derive(Clone, PartialEq, Eq, Default)]
52pub struct GeneralDensePolynomial<T: GroupCoeff<F>, F: Field> {
53 pub coeffs: Vec<T>,
55 _phantom: PhantomData<F>,
56}
57
58impl<T, F> GeneralDensePolynomial<T, F>
63where
64 T: GroupCoeff<F>,
65 F: Field,
66{
67 pub fn from_coeff_slice(coeffs: &[T]) -> Self {
69 Self::from_coeff_vec(coeffs.to_vec())
70 }
71
72 pub fn from_coeff_vec(coeffs: Vec<T>) -> Self {
74 let mut result = Self {
75 coeffs,
76 _phantom: PhantomData,
77 };
78 result.truncate_leading_zeros();
79 assert!(result.coeffs.last().map_or(true, |coeff| !coeff.is_zero()));
80 result
81 }
82
83 #[allow(dead_code)]
86 pub fn rand<R: Rng>(d: usize, rng: &mut R) -> Self {
87 let mut random_coeffs = Vec::new();
88 for _ in 0..=d {
89 random_coeffs.push(T::rand(rng));
90 }
91 Self::from_coeff_vec(random_coeffs)
92 }
93
94 #[allow(dead_code)]
96 pub fn degree(&self) -> usize {
97 if self.is_zero() {
98 0
99 } else {
100 assert!(self.coeffs.last().map_or(false, |coeff| !coeff.is_zero()));
101 self.coeffs.len() - 1
102 }
103 }
104
105 pub fn evaluate(&self, point: &F) -> T {
107 if self.is_zero() {
108 return T::zero();
109 } else if point.is_zero() {
110 return self.coeffs[0];
111 }
112 self.horner_evaluate(point)
113 }
114}
115
116impl<T, F> GeneralDensePolynomial<T, F>
117where
118 T: GroupCoeff<F> + DomainCoeff<F> + for<'a> Mul<&'a F, Output = T>,
119 F: FftField,
120{
121 pub fn batch_evaluate(&self, points: &[F]) -> Vec<T> {
123 let cutoff_size = <F as FftField>::TWO_ADICITY.pow(2);
127
128 if points.is_empty() {
129 Vec::new()
130 } else if points.len() < cutoff_size as usize {
131 points.iter().map(|x| self.evaluate(x)).collect()
132 } else {
133 unimplemented!("TODO: (alex) implements Appendix A of FK23");
134 }
135 }
136
137 pub fn batch_evaluate_rou(
141 &mut self,
142 domain: &Radix2EvaluationDomain<F>,
143 ) -> Result<Vec<T>, PCSError> {
144 if self.coeffs.len() > domain.size() {
145 Err(PCSError::InvalidParameters(
146 ark_std::format!(
147 "Polynomial with {} num_of_coeffs can't be evaluated on a smaller domain with size {}",
148 self.coeffs.len(), domain.size(),
149 )
150 ))
151 } else {
152 self.coeffs.resize_with(domain.size(), Zero::zero);
153 Ok(domain.fft(&self.coeffs))
154 }
155 }
156}
157
158impl<T, F> GeneralDensePolynomial<T, F>
159where
160 T: GroupCoeff<F>,
161 F: Field,
162{
163 fn truncate_leading_zeros(&mut self) {
164 while self.coeffs.last().map_or(false, |c| c.is_zero()) {
165 self.coeffs.pop();
166 }
167 }
168
169 fn horner_evaluate(&self, point: &F) -> T {
171 self.coeffs
172 .iter()
173 .rfold(T::zero(), move |mut result, coeff| {
174 result *= *point;
175 result + *coeff
176 })
177 }
178}
179
180impl<T, F> Zero for GeneralDensePolynomial<T, F>
181where
182 T: GroupCoeff<F>,
183 F: Field,
184{
185 fn zero() -> Self {
187 Self {
188 coeffs: Vec::new(),
189 _phantom: PhantomData,
190 }
191 }
192
193 fn is_zero(&self) -> bool {
195 self.coeffs.is_empty() || self.coeffs.iter().all(|coeff| coeff.is_zero())
196 }
197}
198
199impl<T, F> Add<Self> for GeneralDensePolynomial<T, F>
200where
201 T: GroupCoeff<F>,
202 F: Field,
203{
204 type Output = Self;
205
206 fn add(self, rhs: Self) -> Self::Output {
208 let mut res = if self.is_zero() {
209 rhs
210 } else if rhs.is_zero() {
211 self
212 } else {
213 let coeffs = self
214 .coeffs
215 .into_iter()
216 .zip_longest(rhs.coeffs)
217 .map(|pair| match pair {
218 Both(x, y) => x + y,
219 Left(x) | Right(x) => x,
220 })
221 .collect();
222 Self::from_coeff_vec(coeffs)
223 };
224
225 res.truncate_leading_zeros();
226 res
227 }
228}
229
230impl<T, F> fmt::Debug for GeneralDensePolynomial<T, F>
231where
232 T: GroupCoeff<F>,
233 F: Field,
234{
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
236 for (i, coeff) in self.coeffs.iter().enumerate().filter(|(_, c)| !c.is_zero()) {
237 if i == 0 {
238 write!(f, "\n{:?}", coeff)?;
239 } else if i == 1 {
240 write!(f, " + \n{:?} * x", coeff)?;
241 } else {
242 write!(f, " + \n{:?} * x^{}", coeff, i)?;
243 }
244 }
245 Ok(())
246 }
247}
248
249#[cfg(test)]
250pub(crate) mod tests {
251 use super::*;
252 use ark_bls12_381::{Fr, G1Projective};
253 use ark_ec::{short_weierstrass::SWCurveConfig, CurveGroup};
254 use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
255 use ark_std::iter::successors;
256 use jf_utils::test_rng;
257
258 #[test]
259 fn test_poly_eval_single_point() {
260 let mut rng = test_rng();
261 for _ in 0..5 {
262 let degree = rng.gen_range(5..40);
263 let f = GeneralDensePolynomial::<Fr, Fr>::rand(degree, &mut rng);
264 let g = GeneralDensePolynomial::<G1Projective, Fr>::rand(degree, &mut rng);
265 let point = Fr::rand(&mut rng);
266
267 let f_x = f.evaluate(&point);
268 let expected_f_x: Fr =
269 DensePolynomial::from_coefficients_slice(&f.coeffs).evaluate(&point);
270 assert_eq!(f_x, expected_f_x);
271
272 let g_x = g.evaluate(&point);
273 let scalars: Vec<Fr> = successors(Some(Fr::from(1u32)), |&prev| Some(prev * point))
276 .take(g.degree() + 1)
277 .collect();
278 let expected_g_x =
279 SWCurveConfig::msm(&CurveGroup::normalize_batch(&g.coeffs), &scalars).unwrap();
280 assert_eq!(g_x, expected_g_x);
281 }
282 }
283
284 #[test]
285 fn test_poly_add() {
286 let mut rng = test_rng();
287 for _ in 0..5 {
288 let degree = rng.gen_range(5..40);
289 let f_1 = GeneralDensePolynomial::<Fr, Fr>::rand(degree, &mut rng);
290 let mut f_2 = GeneralDensePolynomial::<Fr, Fr>::rand(degree, &mut rng);
291 let expected_f_3 = DensePolynomial::from_coefficients_slice(&f_1.coeffs)
292 + DensePolynomial::from_coefficients_slice(&f_2.coeffs);
293 let f_3 = f_1.clone() + f_2.clone();
294 assert_eq!(f_3.coeffs, expected_f_3.coeffs);
295
296 f_2.coeffs[degree] = -f_1.coeffs[degree];
297 let f_4 = f_1 + f_2;
298 assert_eq!(f_4.degree(), degree - 1,);
299
300 let g_1 = GeneralDensePolynomial::<G1Projective, Fr>::rand(degree, &mut rng);
301 let mut g_2 = GeneralDensePolynomial::<G1Projective, Fr>::rand(degree, &mut rng);
302 let expected_g_3: Vec<G1Projective> = g_1
303 .coeffs
304 .iter()
305 .zip(g_2.coeffs.iter())
306 .map(|(a, b)| a + b)
307 .collect();
308 let g_3 = g_1.clone() + g_2.clone();
309 assert_eq!(g_3.coeffs, expected_g_3);
310
311 g_2.coeffs[degree] = -g_1.coeffs[degree];
312 g_2.coeffs[degree - 1] = -g_1.coeffs[degree - 1];
313 g_2.coeffs[degree - 2] = -g_1.coeffs[degree - 2];
314 let g_4 = g_1 + g_2;
315 assert_eq!(g_4.degree(), degree - 3);
316 }
317 }
318
319 #[test]
320 fn test_multi_open() {
321 let mut rng = test_rng();
322 let degrees = [14, 15, 16, 17, 18];
323
324 for degree in degrees {
325 let num_points = rng.gen_range(degree - 4..degree + 4); let mut f = GeneralDensePolynomial::<Fr, Fr>::rand(degree, &mut rng);
329 let mut g = GeneralDensePolynomial::<G1Projective, Fr>::rand(degree, &mut rng);
330
331 let points: Vec<Fr> = (0..num_points).map(|_| Fr::rand(&mut rng)).collect();
332 ark_std::println!("degree: {}, num_points: {}", degree, num_points);
333
334 assert_eq!(
336 f.batch_evaluate(&points),
337 points.iter().map(|x| f.evaluate(x)).collect::<Vec<_>>()
338 );
339 assert_eq!(
340 g.batch_evaluate(&points),
341 points.iter().map(|x| g.evaluate(x)).collect::<Vec<_>>()
342 );
343
344 let domain =
346 Radix2EvaluationDomain::new(ark_std::cmp::max(degree + 1, num_points)).unwrap();
347 let roots = domain.elements();
348 assert_eq!(
349 f.batch_evaluate_rou(&domain).unwrap()[..num_points],
350 roots
351 .take(num_points)
352 .map(|x| f.evaluate(&x))
353 .collect::<Vec<_>>()
354 );
355 assert_eq!(
356 g.batch_evaluate_rou(&domain).unwrap()[..num_points],
357 domain
358 .elements()
359 .take(num_points)
360 .map(|x| g.evaluate(&x))
361 .collect::<Vec<_>>()
362 );
363 }
364 }
365}