1use crate::{
5 arithmetic::{best_fft, parallelize},
6 fft::recursive::FFTData,
7 plonk::Assigned,
8};
9
10use super::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation};
11
12use group::ff::{BatchInvert, Field, WithSmallOrderMulGroup};
13
14use std::{collections::HashMap, marker::PhantomData};
15
16#[derive(Clone, Debug)]
20pub struct EvaluationDomain<F: Field> {
21 n: u64,
22 k: u32,
23 extended_k: u32,
24 omega: F,
25 omega_inv: F,
26 extended_omega: F,
27 extended_omega_inv: F,
28 g_coset: F,
29 g_coset_inv: F,
30 quotient_poly_degree: u64,
31 ifft_divisor: F,
32 extended_ifft_divisor: F,
33 t_evaluations: Vec<F>,
34 barycentric_weight: F,
35
36 fft_data: HashMap<usize, FFTData<F>>,
38}
39
40impl<F: WithSmallOrderMulGroup<3>> EvaluationDomain<F> {
41 pub fn new(j: u32, k: u32) -> Self {
44 let quotient_poly_degree = (j - 1) as u64;
46
47 let n = 1u64 << k;
49
50 let mut extended_k = k;
54 while (1 << extended_k) < (n * quotient_poly_degree) {
55 extended_k += 1;
56 }
57 #[cfg(feature = "profile")]
58 println!("k: {}, extended_k: {}", k, extended_k);
59
60 assert!(extended_k <= F::S);
62
63 let mut extended_omega = F::ROOT_OF_UNITY;
64
65 for _ in extended_k..F::S {
69 extended_omega = extended_omega.square();
70 }
71 let extended_omega = extended_omega;
72
73 let mut omegas = Vec::with_capacity((extended_k - k + 1) as usize);
79 let mut omega = extended_omega;
80 omegas.push(omega);
81 for _ in k..extended_k {
82 omega = omega.square();
83 omegas.push(omega);
84 }
85 let omega = omega;
86 omegas.reverse();
87 let mut omegas_inv = omegas.clone(); let g_coset = F::ZETA;
94 let g_coset_inv = g_coset.square();
95
96 let mut t_evaluations = Vec::with_capacity(1 << (extended_k - k));
97 {
98 let orig = F::ZETA.pow_vartime([n]);
101 let step = extended_omega.pow_vartime([n]);
102 let mut cur = orig;
103 loop {
104 t_evaluations.push(cur);
105 cur *= &step;
106 if cur == orig {
107 break;
108 }
109 }
110 assert_eq!(t_evaluations.len(), 1 << (extended_k - k));
111
112 for coeff in &mut t_evaluations {
114 *coeff -= &F::ONE;
115 }
116
117 }
120
121 let mut ifft_divisor = F::from(1 << k); let mut extended_ifft_divisor = F::from(1 << extended_k); let mut barycentric_weight = F::from(n); t_evaluations
130 .iter_mut()
131 .chain(Some(&mut ifft_divisor))
132 .chain(Some(&mut extended_ifft_divisor))
133 .chain(Some(&mut barycentric_weight))
134 .chain(&mut omegas_inv)
135 .batch_invert();
136
137 let omega_inv = omegas_inv[0];
138 let extended_omega_inv = *omegas_inv.last().unwrap();
139 let mut fft_data = HashMap::new();
140 for (i, (omega, omega_inv)) in omegas.into_iter().zip(omegas_inv).enumerate() {
141 let intermediate_k = k as usize + i;
142 let len = 1usize << intermediate_k;
143 fft_data.insert(len, FFTData::<F>::new(len, omega, omega_inv));
144 }
145
146 EvaluationDomain {
147 n,
148 k,
149 extended_k,
150 omega,
151 omega_inv,
152 extended_omega,
153 extended_omega_inv,
154 g_coset,
155 g_coset_inv,
156 quotient_poly_degree,
157 ifft_divisor,
158 extended_ifft_divisor,
159 t_evaluations,
160 barycentric_weight,
161 fft_data,
162 }
163 }
164
165 pub fn lagrange_from_vec(&self, values: Vec<F>) -> Polynomial<F, LagrangeCoeff> {
169 assert_eq!(values.len(), self.n as usize);
170
171 Polynomial {
172 values,
173 _marker: PhantomData,
174 }
175 }
176
177 pub fn lagrange_assigned_from_vec(
178 &self,
179 values: Vec<Assigned<F>>,
180 ) -> Polynomial<Assigned<F>, LagrangeCoeff> {
181 assert_eq!(values.len(), self.n as usize);
182
183 Polynomial {
184 values,
185 _marker: PhantomData,
186 }
187 }
188
189 pub fn coeff_from_vec(&self, values: Vec<F>) -> Polynomial<F, Coeff> {
193 assert_eq!(values.len(), self.n as usize);
194
195 Polynomial {
196 values,
197 _marker: PhantomData,
198 }
199 }
200
201 pub fn lagrange_vec_to_extended(
205 &self,
206 values: Vec<Polynomial<F, LagrangeCoeff>>,
207 ) -> Polynomial<F, ExtendedLagrangeCoeff> {
208 assert_eq!(values.len(), self.extended_len() >> self.k);
209 assert_eq!(values[0].len(), self.n as usize);
210
211 let mut transposed = vec![vec![F::ZERO; values.len()]; self.n as usize];
213 values.into_iter().enumerate().for_each(|(i, p)| {
214 parallelize(&mut transposed, |transposed, start| {
215 for (transposed, p) in transposed.iter_mut().zip(p.values[start..].iter()) {
216 transposed[i] = *p;
217 }
218 });
219 });
220
221 Polynomial {
222 values: transposed.into_iter().flatten().collect(),
223 _marker: PhantomData,
224 }
225 }
226
227 pub fn extended_from_lagrange_vec(
231 &self,
232 values: Vec<Polynomial<F, LagrangeCoeff>>,
233 ) -> Polynomial<F, ExtendedLagrangeCoeff> {
234 assert_eq!(values.len(), self.extended_len() >> self.k);
235 assert_eq!(values[0].len(), self.n as usize);
236
237 let mut transposed = vec![vec![F::ZERO; values.len()]; self.n as usize];
239 values.into_iter().enumerate().for_each(|(i, p)| {
240 parallelize(&mut transposed, |transposed, start| {
241 for (transposed, p) in transposed.iter_mut().zip(p.values[start..].iter()) {
242 transposed[i] = *p;
243 }
244 });
245 });
246
247 Polynomial {
248 values: transposed.into_iter().flatten().collect(),
249 _marker: PhantomData,
250 }
251 }
252
253 pub fn empty_coeff(&self) -> Polynomial<F, Coeff> {
255 Polynomial {
256 values: vec![F::ZERO; self.n as usize],
257 _marker: PhantomData,
258 }
259 }
260
261 pub fn empty_lagrange(&self) -> Polynomial<F, LagrangeCoeff> {
263 Polynomial {
264 values: vec![F::ZERO; self.n as usize],
265 _marker: PhantomData,
266 }
267 }
268
269 pub(crate) fn empty_lagrange_assigned(&self) -> Polynomial<Assigned<F>, LagrangeCoeff> {
272 Polynomial {
273 values: vec![F::ZERO.into(); self.n as usize],
274 _marker: PhantomData,
275 }
276 }
277
278 pub fn constant_lagrange(&self, scalar: F) -> Polynomial<F, LagrangeCoeff> {
280 Polynomial {
281 values: vec![scalar; self.n as usize],
282 _marker: PhantomData,
283 }
284 }
285
286 pub fn empty_extended(&self) -> Polynomial<F, ExtendedLagrangeCoeff> {
289 Polynomial {
290 values: vec![F::ZERO; self.extended_len()],
291 _marker: PhantomData,
292 }
293 }
294
295 pub fn constant_extended(&self, scalar: F) -> Polynomial<F, ExtendedLagrangeCoeff> {
298 Polynomial {
299 values: vec![scalar; self.extended_len()],
300 _marker: PhantomData,
301 }
302 }
303
304 pub fn lagrange_to_coeff(&self, mut a: Polynomial<F, LagrangeCoeff>) -> Polynomial<F, Coeff> {
309 assert_eq!(a.values.len(), 1 << self.k);
310
311 self.ifft(&mut a.values, self.omega_inv, self.k, self.ifft_divisor);
313
314 Polynomial {
315 values: a.values,
316 _marker: PhantomData,
317 }
318 }
319
320 pub fn coeff_to_extended(
323 &self,
324 p: &Polynomial<F, Coeff>,
325 ) -> Polynomial<F, ExtendedLagrangeCoeff> {
326 assert_eq!(p.values.len(), 1 << self.k);
327
328 let mut a = Vec::with_capacity(self.extended_len());
329 a.extend(&p.values);
330
331 self.distribute_powers_zeta(&mut a, true);
332 a.resize(self.extended_len(), F::ZERO);
333 self.fft_inner(&mut a, self.extended_omega, self.extended_k, false);
334
335 Polynomial {
336 values: a,
337 _marker: PhantomData,
338 }
339 }
340
341 pub fn coeff_to_extended_parts(
350 &self,
351 a: &Polynomial<F, Coeff>,
352 ) -> Vec<Polynomial<F, LagrangeCoeff>> {
353 assert_eq!(a.values.len(), 1 << self.k);
354
355 let num_parts = self.extended_len() >> self.k;
356 let mut extended_omega_factor = F::ONE;
357 (0..num_parts)
358 .map(|_| {
359 let part = self.coeff_to_extended_part(a.clone(), extended_omega_factor);
360 extended_omega_factor *= self.extended_omega;
361 part
362 })
363 .collect()
364 }
365
366 pub fn batched_coeff_to_extended_parts(
375 &self,
376 a: &[Polynomial<F, Coeff>],
377 ) -> Vec<Vec<Polynomial<F, LagrangeCoeff>>> {
378 assert_eq!(a[0].values.len(), 1 << self.k);
379
380 let mut extended_omega_factor = F::ONE;
381 let num_parts = self.extended_len() >> self.k;
382 (0..num_parts)
383 .map(|_| {
384 let a_lagrange = a
385 .iter()
386 .map(|poly| self.coeff_to_extended_part(poly.clone(), extended_omega_factor))
387 .collect();
388 extended_omega_factor *= self.extended_omega;
389 a_lagrange
390 })
391 .collect()
392 }
393
394 pub fn coeff_to_extended_part(
401 &self,
402 mut a: Polynomial<F, Coeff>,
403 extended_omega_factor: F,
404 ) -> Polynomial<F, LagrangeCoeff> {
405 assert_eq!(a.values.len(), 1 << self.k);
406
407 self.distribute_powers(&mut a.values, self.g_coset * extended_omega_factor);
408 let data = self.get_fft_data(a.len());
409 best_fft(&mut a.values, self.omega, self.k, data, false);
410
411 Polynomial {
412 values: a.values,
413 _marker: PhantomData,
414 }
415 }
416
417 pub fn rotate_extended(
419 &self,
420 poly: &Polynomial<F, ExtendedLagrangeCoeff>,
421 rotation: Rotation,
422 ) -> Polynomial<F, ExtendedLagrangeCoeff> {
423 let new_rotation = ((1 << (self.extended_k - self.k)) * rotation.0.abs()) as usize;
424
425 let mut poly = poly.clone();
426
427 if rotation.0 >= 0 {
428 poly.values.rotate_left(new_rotation);
429 } else {
430 poly.values.rotate_right(new_rotation);
431 }
432
433 poly
434 }
435
436 pub fn extended_to_coeff(&self, mut a: Polynomial<F, ExtendedLagrangeCoeff>) -> Vec<F> {
443 assert_eq!(a.values.len(), self.extended_len());
444
445 self.ifft(
447 &mut a.values,
448 self.extended_omega_inv,
449 self.extended_k,
450 self.extended_ifft_divisor,
451 );
452
453 self.distribute_powers_zeta(&mut a.values, false);
456
457 a.values
461 .truncate((&self.n * self.quotient_poly_degree) as usize);
462
463 a.values
464 }
465
466 pub fn lagrange_vecs_to_extended(
469 &self,
470 mut a: Vec<Vec<Polynomial<F, LagrangeCoeff>>>,
471 ) -> Polynomial<F, ExtendedLagrangeCoeff> {
472 let mut result_poly = if a[a.len() - 1].len() == 1 << (self.extended_k - self.k) {
473 self.lagrange_vec_to_extended(a.pop().unwrap())
474 } else {
475 self.empty_extended()
476 };
477
478 let mut ifft_divisor = self.extended_ifft_divisor;
480 let mut omega_inv = self.extended_omega_inv;
481 {
482 let mut i = a.last().unwrap().len() << self.k;
483 while i < (1 << self.extended_k) {
484 ifft_divisor = ifft_divisor + ifft_divisor;
485 omega_inv = omega_inv * omega_inv;
486 i <<= 1;
487 }
488 }
489
490 let mut result = vec![F::ZERO; 1 << self.extended_k as usize];
491 for (i, a_parts) in a.into_iter().enumerate().rev() {
492 assert_eq!(1 << i, a_parts.len());
494 let mut a_poly: Vec<F> = {
495 let mut transposed = vec![vec![F::ZERO; a_parts.len()]; self.n as usize];
496 a_parts.into_iter().enumerate().for_each(|(j, p)| {
497 parallelize(&mut transposed, |transposed, start| {
498 for (transposed, p) in transposed.iter_mut().zip(p.values[start..].iter()) {
499 transposed[j] = *p;
500 }
501 });
502 });
503 transposed.into_iter().flatten().collect()
504 };
505
506 self.ifft(&mut a_poly, omega_inv, self.k + i as u32, ifft_divisor);
507 ifft_divisor = ifft_divisor + ifft_divisor;
508 omega_inv = omega_inv * omega_inv;
509
510 parallelize(&mut result[0..(self.n << i) as usize], |result, start| {
511 for (other, current) in result.iter_mut().zip(a_poly[start..].iter()) {
512 *other += current;
513 }
514 });
515 }
516 let data = self.get_fft_data(result.len());
517 best_fft(
518 &mut result,
519 self.extended_omega,
520 self.extended_k,
521 data,
522 false,
523 );
524 parallelize(&mut result_poly.values, |values, start| {
525 for (value, other) in values.iter_mut().zip(result[start..].iter()) {
526 *value += other;
527 }
528 });
529 result_poly
530 }
531
532 pub fn divide_by_vanishing_poly(
535 &self,
536 mut a: Polynomial<F, ExtendedLagrangeCoeff>,
537 ) -> Polynomial<F, ExtendedLagrangeCoeff> {
538 assert_eq!(a.values.len(), self.extended_len());
539
540 parallelize(&mut a.values, |h, mut index| {
543 for h in h {
544 *h *= &self.t_evaluations[index % self.t_evaluations.len()];
545 index += 1;
546 }
547 });
548
549 Polynomial {
550 values: a.values,
551 _marker: PhantomData,
552 }
553 }
554
555 fn distribute_powers_zeta(&self, a: &mut [F], into_coset: bool) {
563 let coset_powers = if into_coset {
564 [self.g_coset, self.g_coset_inv]
565 } else {
566 [self.g_coset_inv, self.g_coset]
567 };
568 parallelize(a, |a, mut index| {
569 for a in a {
570 let i = index % (coset_powers.len() + 1);
572 if i != 0 {
573 *a *= &coset_powers[i - 1];
574 }
575 index += 1;
576 }
577 });
578 }
579
580 fn distribute_powers(&self, a: &mut [F], c: F) {
584 parallelize(a, |a, index| {
585 let mut c_power = c.pow_vartime([index as u64]);
586 for a in a {
587 *a *= c_power;
588 c_power *= c;
589 }
590 });
591 }
592
593 fn ifft(&self, a: &mut Vec<F>, omega_inv: F, log_n: u32, divisor: F) {
594 let fft_data = self.get_fft_data(a.len());
595 crate::fft::parallel::fft(a, omega_inv, log_n, fft_data, true);
596 parallelize(a, |a, _| {
598 for a in a {
599 *a *= &divisor;
601 }
602 });
603 }
604
605 fn fft_inner(&self, a: &mut Vec<F>, omega: F, log_n: u32, inverse: bool) {
606 let fft_data = self.get_fft_data(a.len());
607 best_fft(a, omega, log_n, fft_data, inverse)
608 }
609
610 pub fn k(&self) -> u32 {
612 self.k
613 }
614
615 pub fn extended_k(&self) -> u32 {
617 self.extended_k
618 }
619
620 pub fn extended_len(&self) -> usize {
622 1 << self.extended_k
623 }
624
625 pub fn get_omega(&self) -> F {
627 self.omega
628 }
629
630 pub fn get_omega_inv(&self) -> F {
633 self.omega_inv
634 }
635
636 pub fn get_extended_omega(&self) -> F {
638 self.extended_omega
639 }
640
641 pub fn rotate_omega(&self, value: F, rotation: Rotation) -> F {
644 let mut point = value;
645 if rotation.0 >= 0 {
646 point *= &self.get_omega().pow_vartime([rotation.0 as u64]);
647 } else {
648 point *= &self
649 .get_omega_inv()
650 .pow_vartime([(rotation.0 as i64).unsigned_abs()]);
651 }
652 point
653 }
654
655 pub fn l_i_range<I: IntoIterator<Item = i32> + Clone>(
683 &self,
684 x: F,
685 xn: F,
686 rotations: I,
687 ) -> Vec<F> {
688 let mut results;
689 {
690 let rotations = rotations.clone().into_iter();
691 results = Vec::with_capacity(rotations.size_hint().1.unwrap_or(0));
692 for rotation in rotations {
693 let rotation = Rotation(rotation);
694 let result = x - self.rotate_omega(F::ONE, rotation);
695 results.push(result);
696 }
697 results.iter_mut().batch_invert();
698 }
699
700 let common = (xn - F::ONE) * self.barycentric_weight;
701 for (rotation, result) in rotations.into_iter().zip(results.iter_mut()) {
702 let rotation = Rotation(rotation);
703 *result = self.rotate_omega(*result * common, rotation);
704 }
705
706 results
707 }
708
709 pub fn get_quotient_poly_degree(&self) -> usize {
711 self.quotient_poly_degree as usize
712 }
713
714 pub fn pinned(&self) -> PinnedEvaluationDomain<'_, F> {
718 PinnedEvaluationDomain {
719 k: &self.k,
720 extended_k: &self.extended_k,
721 omega: &self.omega,
722 }
723 }
724
725 pub fn get_n(&self) -> u64 {
727 self.n
728 }
729
730 pub fn get_fft_data(&self, l: usize) -> &FFTData<F> {
732 self.fft_data
733 .get(&l)
734 .expect("log_2(l) must be in k..=extended_k")
735 }
736}
737
738#[allow(dead_code)]
740#[derive(Debug)]
741pub struct PinnedEvaluationDomain<'a, F: Field> {
742 k: &'a u32,
743 extended_k: &'a u32,
744 omega: &'a F,
745}
746
747#[test]
748fn test_rotate() {
749 use rand_core::OsRng;
750
751 use crate::arithmetic::eval_polynomial;
752 use halo2curves::pasta::pallas::Scalar;
753
754 let domain = EvaluationDomain::<Scalar>::new(1, 3);
755 let rng = OsRng;
756
757 let mut poly = domain.empty_lagrange();
758 assert_eq!(poly.len(), 8);
759 for value in poly.iter_mut() {
760 *value = Scalar::random(rng);
761 }
762
763 let poly_rotated_cur = poly.rotate(Rotation::cur());
764 let poly_rotated_next = poly.rotate(Rotation::next());
765 let poly_rotated_prev = poly.rotate(Rotation::prev());
766
767 let poly = domain.lagrange_to_coeff(poly);
768 let poly_rotated_cur = domain.lagrange_to_coeff(poly_rotated_cur);
769 let poly_rotated_next = domain.lagrange_to_coeff(poly_rotated_next);
770 let poly_rotated_prev = domain.lagrange_to_coeff(poly_rotated_prev);
771
772 let x = Scalar::random(rng);
773
774 assert_eq!(
775 eval_polynomial(&poly[..], x),
776 eval_polynomial(&poly_rotated_cur[..], x)
777 );
778 assert_eq!(
779 eval_polynomial(&poly[..], x * domain.omega),
780 eval_polynomial(&poly_rotated_next[..], x)
781 );
782 assert_eq!(
783 eval_polynomial(&poly[..], x * domain.omega_inv),
784 eval_polynomial(&poly_rotated_prev[..], x)
785 );
786}
787
788#[test]
789fn test_l_i() {
790 use rand_core::OsRng;
791
792 use crate::arithmetic::{eval_polynomial, lagrange_interpolate};
793 use halo2curves::pasta::pallas::Scalar;
794 let domain = EvaluationDomain::<Scalar>::new(1, 3);
795
796 let mut l = vec![];
797 let mut points = vec![];
798 for i in 0..8 {
799 points.push(domain.omega.pow([i]));
800 }
801 for i in 0..8 {
802 let mut l_i = vec![Scalar::zero(); 8];
803 l_i[i] = Scalar::ONE;
804 let l_i = lagrange_interpolate(&points[..], &l_i[..]);
805 l.push(l_i);
806 }
807
808 let x = Scalar::random(OsRng);
809 let xn = x.pow([8]);
810
811 let evaluations = domain.l_i_range(x, xn, -7..=7);
812 for i in 0..8 {
813 assert_eq!(eval_polynomial(&l[i][..], x), evaluations[7 + i]);
814 assert_eq!(eval_polynomial(&l[(8 - i) % 8][..], x), evaluations[7 - i]);
815 }
816}
817
818#[test]
819fn test_coeff_to_extended_part() {
820 use halo2curves::pasta::pallas::Scalar;
821 use rand_core::OsRng;
822
823 let domain = EvaluationDomain::<Scalar>::new(1, 3);
824 let rng = OsRng;
825 let mut poly = domain.empty_coeff();
826 assert_eq!(poly.len(), 8);
827 for value in poly.iter_mut() {
828 *value = Scalar::random(rng);
829 }
830
831 let want = domain.coeff_to_extended(&poly);
832 let got = {
833 let parts = domain.coeff_to_extended_parts(&poly);
834 domain.lagrange_vec_to_extended(parts)
835 };
836 assert_eq!(want.values, got.values);
837}
838
839#[test]
840fn bench_coeff_to_extended_parts() {
841 use halo2curves::pasta::pallas::Scalar;
842 use rand_core::OsRng;
843 use std::time::Instant;
844
845 let k = 20;
846 let domain = EvaluationDomain::<Scalar>::new(3, k);
847 let rng = OsRng;
848 let mut poly1 = domain.empty_coeff();
849 assert_eq!(poly1.len(), 1 << k);
850
851 for value in poly1.iter_mut() {
852 *value = Scalar::random(rng);
853 }
854
855 let poly2 = poly1.clone();
856
857 let coeff_to_extended_timer = Instant::now();
858 let _ = domain.coeff_to_extended(&poly1);
859 println!(
860 "domain.coeff_to_extended time: {}s",
861 coeff_to_extended_timer.elapsed().as_secs_f64()
862 );
863
864 let coeff_to_extended_parts_timer = Instant::now();
865 let _ = domain.coeff_to_extended_parts(&poly2);
866 println!(
867 "domain.coeff_to_extended_parts time: {}s",
868 coeff_to_extended_parts_timer.elapsed().as_secs_f64()
869 );
870}
871
872#[test]
873fn test_lagrange_vecs_to_extended() {
874 use halo2curves::pasta::pallas::Scalar;
875 use rand_core::OsRng;
876
877 let rng = OsRng;
878 let domain = EvaluationDomain::<Scalar>::new(8, 10);
879 let mut poly_vec = vec![];
880 let mut poly_lagrange_vecs = vec![];
881 let mut want = domain.empty_extended();
882 let mut omega = domain.extended_omega;
883 for i in (0..(domain.extended_k - domain.k + 1)).rev() {
884 let mut poly = vec![Scalar::zero(); (1 << i) * domain.n as usize];
885 for value in poly.iter_mut() {
886 *value = Scalar::random(rng);
887 }
888 poly_vec.push(poly.clone());
890 let mut poly2 = poly.clone();
892 let data = domain.get_fft_data(poly2.len());
893 best_fft(&mut poly2, omega, i + domain.k, data, false);
894 let transposed_poly: Vec<Polynomial<Scalar, LagrangeCoeff>> = (0..(1 << i))
895 .map(|j| {
896 let mut p = domain.empty_lagrange();
897 for k in 0..domain.n {
898 p[k as usize] = poly2[j + (k as usize) * (1 << i)];
899 }
900 p
901 })
902 .collect();
903 poly_lagrange_vecs.push(transposed_poly);
904 poly.resize(domain.extended_len(), Scalar::zero());
906 let data = domain.get_fft_data(poly.len());
907 best_fft(
908 &mut poly,
909 domain.extended_omega,
910 domain.extended_k,
911 data,
912 false,
913 );
914 let poly = {
915 let mut p = domain.empty_extended();
916 p.values = poly;
917 p
918 };
919 want = want + &poly;
920 omega = omega * omega;
921 }
922 poly_lagrange_vecs.reverse();
923 let got = domain.lagrange_vecs_to_extended(poly_lagrange_vecs);
924 assert_eq!(want.values, got.values);
925}
926
927#[test]
928fn bench_lagrange_vecs_to_extended() {
929 use halo2curves::pasta::pallas::Scalar;
930 use rand_core::OsRng;
931 use std::time::Instant;
932
933 let rng = OsRng;
934 let domain = EvaluationDomain::<Scalar>::new(8, 10);
935 let mut poly_vec = vec![];
936 let mut poly_lagrange_vecs = vec![];
937 let mut poly_extended_vecs = vec![];
938 let mut omega = domain.extended_omega;
939
940 for i in (0..(domain.extended_k - domain.k + 1)).rev() {
941 let mut poly = vec![Scalar::zero(); (1 << i) * domain.n as usize];
942 for value in poly.iter_mut() {
943 *value = Scalar::random(rng);
944 }
945 poly_vec.push(poly.clone());
947 let mut poly2 = poly.clone();
949 let data = domain.get_fft_data(poly2.len());
950 best_fft(&mut poly2, omega, i + domain.k, data, false);
951 let transposed_poly: Vec<Polynomial<Scalar, LagrangeCoeff>> = (0..(1 << i))
952 .map(|j| {
953 let mut p = domain.empty_lagrange();
954 for k in 0..domain.n {
955 p[k as usize] = poly2[j + (k as usize) * (1 << i)];
956 }
957 p
958 })
959 .collect();
960 poly_lagrange_vecs.push(transposed_poly);
961 poly.resize(domain.extended_len(), Scalar::zero());
963 let data = domain.get_fft_data(poly.len());
964 best_fft(
965 &mut poly,
966 domain.extended_omega,
967 domain.extended_k,
968 data,
969 false,
970 );
971 let poly = {
972 let mut p = domain.empty_extended();
973 p.values = poly;
974 p
975 };
976 poly_extended_vecs.push(poly);
977 omega = omega * omega;
978 }
979
980 let want_timer = Instant::now();
981 let _ = poly_extended_vecs
982 .iter()
983 .fold(domain.empty_extended(), |acc, p| acc + p);
984 println!("want time: {}s", want_timer.elapsed().as_secs_f64());
985 poly_lagrange_vecs.reverse();
986 let got_timer = Instant::now();
987 let _ = domain.lagrange_vecs_to_extended(poly_lagrange_vecs);
988 println!("got time: {}s", got_timer.elapsed().as_secs_f64());
989}