1use alloc::vec::Vec;
42
43use p3_field::coset::TwoAdicMultiplicativeCoset;
44use p3_field::{
45 ExtensionField, Field, TwoAdicField, batch_multiplicative_inverse,
46 scale_slice_in_place_single_core,
47};
48use p3_maybe_rayon::prelude::*;
49use p3_util::log2_strict_usize;
50
51use crate::Matrix;
52use crate::dense::RowMajorMatrix;
53
54pub fn compute_adjusted_weights<EF: Field>(point: EF, diff_invs: &[EF]) -> Vec<EF> {
68 let point_inv = point.inverse();
70 diff_invs.par_iter().map(|&d| d - point_inv).collect()
72}
73
74pub trait Interpolate<F: TwoAdicField>: Matrix<F> {
79 fn interpolate_subgroup<EF: ExtensionField<F>>(&self, point: EF) -> Vec<EF> {
87 self.interpolate_coset(F::ONE, point)
89 }
90
91 fn interpolate_coset<EF: ExtensionField<F>>(&self, shift: F, point: EF) -> Vec<EF> {
102 let log_height = log2_strict_usize(self.height());
103
104 let coset: Vec<F> = TwoAdicMultiplicativeCoset::new(shift, log_height)
106 .unwrap()
107 .iter()
108 .collect();
109
110 let diffs: Vec<EF> = coset.par_iter().map(|&g| point - g).collect();
113
114 if let Some(i) = diffs.iter().position(|d| d.is_zero()) {
117 return self.row(i).unwrap().into_iter().map(EF::from).collect();
118 }
119
120 let diff_invs = batch_multiplicative_inverse(&diffs);
121
122 let adjusted = compute_adjusted_weights(point, &diff_invs);
124 self.interpolate_coset_with_precomputation(shift, point, &adjusted)
125 }
126
127 fn interpolate_coset_with_precomputation<EF: ExtensionField<F>>(
166 &self,
167 shift: F,
168 point: EF,
169 adjusted_weights: &[EF],
170 ) -> Vec<EF> {
171 debug_assert_eq!(adjusted_weights.len(), self.height());
172
173 let log_height = log2_strict_usize(self.height());
174
175 let z_pow_n = point.exp_power_of_2(log_height);
181 let g_pow_n = shift.exp_power_of_2(log_height);
183 let denom_inv = g_pow_n.mul_2exp_u64(log_height as u64).inverse();
185 let scaling_factor = point * (z_pow_n - g_pow_n) * denom_inv;
187
188 let mut evals = self.columnwise_dot_product(adjusted_weights);
193
194 scale_slice_in_place_single_core(&mut evals, scaling_factor);
196 evals
197 }
198}
199
200impl<F: TwoAdicField, M: Matrix<F>> Interpolate<F> for M {}
201
202pub fn barycentric_weights<F: Field>(x_coords: &[F]) -> Option<Vec<F>> {
216 let n = x_coords.len();
217 if n == 0 {
218 return Some(Vec::new());
219 }
220
221 let mut denoms = alloc::vec![F::ONE; n];
223 for i in 0..n {
224 for j in 0..i {
229 let diff = x_coords[i] - x_coords[j];
230 if diff.is_zero() {
232 return None;
233 }
234 denoms[i] *= diff;
235 denoms[j] *= -diff;
236 }
237 }
238
239 Some(batch_multiplicative_inverse(&denoms))
241}
242
243pub trait InterpolateArbitrary<F: Field>: Matrix<F> {
249 fn interpolate_arbitrary_point<EF: ExtensionField<F>>(
262 &self,
263 x_coords: &[F],
264 point: EF,
265 ) -> Option<Vec<EF>> {
266 debug_assert_eq!(x_coords.len(), self.height());
267
268 let weights = barycentric_weights(x_coords)?;
273
274 for (i, &x) in x_coords.iter().enumerate() {
277 if point == EF::from(x) {
278 return Some(self.row(i).unwrap().into_iter().map(EF::from).collect());
279 }
280 }
281
282 let diffs: Vec<EF> = x_coords.iter().map(|&x| point - x).collect();
284 let diff_invs = batch_multiplicative_inverse(&diffs);
285
286 Some(self.interpolate_arbitrary_with_precomputation(&weights, &diff_invs))
287 }
288
289 fn interpolate_arbitrary_with_precomputation<EF: ExtensionField<F>>(
304 &self,
305 weights: &[F],
306 diff_invs: &[EF],
307 ) -> Vec<EF> {
308 debug_assert_eq!(weights.len(), self.height());
309 debug_assert_eq!(diff_invs.len(), self.height());
310
311 if self.height() == 0 {
314 return EF::zero_vec(self.width());
315 }
316
317 let col_scale: Vec<EF> = weights
327 .iter()
328 .zip(diff_invs)
329 .map(|(&w, &d)| d * w)
330 .collect();
331
332 let denominator = col_scale.iter().copied().fold(EF::ZERO, |a, b| a + b);
334 let denom_inv = denominator.inverse();
335
336 let mut evals = self.columnwise_dot_product(&col_scale);
338
339 scale_slice_in_place_single_core(&mut evals, denom_inv);
341 evals
342 }
343
344 fn recover_coefficients(&self, x_coords: &[F]) -> Option<RowMajorMatrix<F>> {
358 let n = self.height();
359 let w = self.width();
360 debug_assert_eq!(x_coords.len(), n);
361
362 if n == 0 {
363 return Some(RowMajorMatrix::new(Vec::new(), w.max(1)));
364 }
365
366 let mut result = RowMajorMatrix::new(F::zero_vec(n * w), w);
368
369 let mut basis = F::zero_vec(n);
372 basis[0] = F::ONE;
373
374 let mut scratch = F::zero_vec(w);
376
377 for k in 0..n {
378 let x_k = x_coords[k];
379
380 let mut b_xk = F::ONE;
385 for &x_i in &x_coords[..k] {
386 b_xk *= x_k - x_i;
387 }
388 let b_xk_inv = b_xk.try_inverse()?;
390
391 scratch.fill(F::ZERO);
397 for i in (0..k).rev() {
398 let row = result.row_slice(i).unwrap();
399 for j in 0..w {
400 scratch[j] = scratch[j] * x_k + row[j];
401 }
402 }
403
404 for (j, y_kj) in self.row(k).unwrap().into_iter().enumerate() {
407 scratch[j] = (y_kj - scratch[j]) * b_xk_inv;
408 }
409
410 for (i, &b_i) in basis.iter().enumerate().take(k + 1) {
412 let row = result.row_mut(i);
413 for j in 0..w {
414 row[j] += scratch[j] * b_i;
415 }
416 }
417
418 if k + 1 < n {
425 basis[k + 1] = basis[k];
426 }
427 for i in (1..=k).rev() {
428 basis[i] = basis[i - 1] - x_k * basis[i];
429 }
430 basis[0] = -x_k * basis[0];
431 }
432
433 Some(result)
434 }
435}
436
437impl<F: Field, M: Matrix<F>> InterpolateArbitrary<F> for M {}
438
439pub fn interpolate_lagrange<F: Field>(points: &[(F, F)]) -> Option<Vec<F>> {
453 if points.is_empty() {
454 return Some(Vec::new());
455 }
456 let (xs, ys): (Vec<F>, Vec<F>) = points.iter().copied().unzip();
458 let evals = RowMajorMatrix::new_col(ys);
460 Some(evals.recover_coefficients(&xs)?.values)
461}
462
463#[cfg(test)]
464mod tests {
465 use alloc::vec;
466 use alloc::vec::Vec;
467
468 use p3_baby_bear::BabyBear;
469 use p3_field::extension::BinomialExtensionField;
470 use p3_field::{
471 BasedVectorSpace, ExtensionField, Field, HornerIter, PrimeCharacteristicRing, TwoAdicField,
472 batch_multiplicative_inverse,
473 };
474 use p3_util::log2_strict_usize;
475 use proptest::prelude::*;
476
477 use super::*;
478 use crate::dense::RowMajorMatrix;
479
480 type F = BabyBear;
481 type EF4 = BinomialExtensionField<BabyBear, 4>;
482
483 fn eval_poly<EF: ExtensionField<F>>(coeffs: &[F], point: EF) -> EF {
488 coeffs.iter().copied().horner(point)
489 }
490
491 fn eval_poly_on_coset<EF: ExtensionField<F>>(coeffs: &[F], shift: F, log_n: usize) -> Vec<EF> {
492 let n = 1 << log_n;
493 let subgroup_gen = F::two_adic_generator(log_n);
495 (0..n)
496 .map(|i| {
497 let coset_elem = shift * subgroup_gen.exp_u64(i as u64);
499 eval_poly(coeffs, EF::from(coset_elem))
500 })
501 .collect()
502 }
503
504 #[test]
505 fn test_interpolate_subgroup() {
506 let evals = [
511 6, 886605102, 1443543107, 708307799, 2, 556938009, 569722818, 1874680944,
512 ]
513 .map(F::from_u32);
514
515 let evals_mat = RowMajorMatrix::new(evals.to_vec(), 1);
517
518 let point = F::from_u16(100);
520 let result = evals_mat.interpolate_subgroup(point);
521
522 assert_eq!(result, vec![F::from_u16(10203)]);
524 }
525
526 #[test]
527 fn test_interpolate_coset() {
528 let shift = F::GENERATOR;
533
534 let evals = [
536 1026, 129027310, 457985035, 994890337, 902, 1988942953, 1555278970, 913671254,
537 ]
538 .map(F::from_u32);
539
540 let evals_mat = RowMajorMatrix::new(evals.to_vec(), 1);
542
543 let point = F::from_u16(100);
545 let result = evals_mat.interpolate_coset(shift, point);
546 assert_eq!(result, vec![F::from_u16(10203)]);
547
548 let n = evals.len();
551 let k = log2_strict_usize(n);
552
553 let coset = F::two_adic_generator(k).shifted_powers(shift).collect_n(n);
555
556 let denom: Vec<_> = coset.iter().map(|&w| point - w).collect();
558 let denom = batch_multiplicative_inverse(&denom);
559
560 let adjusted = compute_adjusted_weights(point, &denom);
562
563 let result = evals_mat.interpolate_coset_with_precomputation(shift, point, &adjusted);
565 assert_eq!(result, vec![F::from_u16(10203)]);
566 }
567
568 #[test]
569 fn test_interpolate_coset_single_point_identity() {
570 let c = F::from_u32(42);
573
574 let evals = vec![c; 8];
576 let evals_mat = RowMajorMatrix::new(evals, 1);
577
578 let shift = F::GENERATOR;
579 let point = F::from_u16(1337);
580
581 let result = evals_mat.interpolate_coset(shift, point);
582 assert_eq!(result, vec![c]);
583 }
584
585 #[test]
586 fn test_interpolate_coset_point_on_coset() {
587 let log_n = 3;
589 let n = 1usize << log_n;
590 let shift = F::GENERATOR;
591 let h = F::two_adic_generator(log_n);
592
593 let coset: Vec<F> = (0..n).map(|i| shift * h.exp_u64(i as u64)).collect();
594 let evals: Vec<F> = (0..n as u32).map(|i| F::from_u32(100 + i)).collect();
595 let m = RowMajorMatrix::new(evals.clone(), 1);
596
597 for (i, &x) in coset.iter().enumerate() {
599 let result = m.interpolate_coset(shift, x);
600 assert_eq!(result, vec![evals[i]]);
601 }
602 }
603
604 #[test]
605 fn test_interpolate_coset_point_on_coset_extension() {
606 let log_n = 3;
609 let n = 1usize << log_n;
610 let shift = F::GENERATOR;
611 let h = F::two_adic_generator(log_n);
612
613 let coset: Vec<F> = (0..n).map(|i| shift * h.exp_u64(i as u64)).collect();
614
615 let mut evals: Vec<F> = Vec::with_capacity(n * 2);
616 for i in 0..n {
617 evals.push(F::from_u32(200 + i as u32));
618 evals.push(F::from_u32(300 + i as u32));
619 }
620 let m = RowMajorMatrix::new(evals, 2);
621
622 let i = 3;
624 let result = m.interpolate_coset(shift, EF4::from(coset[i]));
625 assert_eq!(
626 result,
627 vec![
628 EF4::from(F::from_u32(200 + i as u32)),
629 EF4::from(F::from_u32(300 + i as u32)),
630 ]
631 );
632 }
633
634 #[test]
635 fn test_interpolate_subgroup_point_on_subgroup() {
636 let log_n = 2;
638 let n = 1usize << log_n;
639 let h = F::two_adic_generator(log_n);
640
641 let evals: Vec<F> = (0..n as u32).map(|i| F::from_u32(10 + i)).collect();
642 let m = RowMajorMatrix::new(evals.clone(), 1);
643
644 let result = m.interpolate_subgroup(h.exp_u64(2));
646 assert_eq!(result, vec![evals[2]]);
647 }
648
649 #[test]
650 fn test_interpolate_subgroup_degree_3_correctness() {
651 let poly = |x: EF4| x * x * x + x * x * F::TWO + x * F::from_u32(3) + F::from_u32(4);
657
658 let subgroup = EF4::two_adic_generator(2).powers().collect_n(4);
660 let evals: Vec<_> = subgroup.iter().map(|&x| poly(x)).collect();
661 let evals_mat = RowMajorMatrix::new(evals, 1);
662
663 let point = EF4::from_u16(5);
665 let result = evals_mat.interpolate_subgroup(point);
666 let expected = poly(point);
667 assert_eq!(result[0], expected);
668 }
669
670 #[test]
671 fn test_interpolate_coset_multiple_polynomials() {
672 let shift = EF4::GENERATOR;
683 let coset = EF4::two_adic_generator(3)
684 .shifted_powers(shift)
685 .collect_n(8);
686
687 let f1 = |x: EF4| x * x + x * F::TWO + F::from_u32(3);
688 let f2 = |x: EF4| x * x * F::from_u32(4) + x * F::from_u32(5) + F::from_u32(6);
689
690 let evals: Vec<_> = coset.iter().flat_map(|&x| vec![f1(x), f2(x)]).collect();
692
693 let evals_mat = RowMajorMatrix::new(evals, 2);
695
696 let point = EF4::from_u32(77);
698 let result = evals_mat.interpolate_coset(shift, point);
699
700 let expected_f1 = f1(point);
702 let expected_f2 = f2(point);
703
704 assert_eq!(result[0], expected_f1);
705 assert_eq!(result[1], expected_f2);
706 }
707
708 #[test]
709 fn test_interpolate_subgroup_multiple_columns() {
710 let f1 = |x: EF4| x * x + x * F::TWO + F::from_u32(3);
718 let f2 = |x: EF4| x * x * F::from_u32(4) + x * F::from_u32(5) + F::from_u32(6);
719
720 let subgroup_iter = EF4::two_adic_generator(3).powers().take(8);
722
723 let evals: Vec<_> = subgroup_iter.flat_map(|x| vec![f1(x), f2(x)]).collect();
725
726 let evals_mat = RowMajorMatrix::new(evals, 2);
728
729 let point = EF4::from_u32(77);
731 let result = evals_mat.interpolate_subgroup(point);
732
733 let expected_f1 = f1(point);
735 let expected_f2 = f2(point);
736
737 assert_eq!(result, vec![expected_f1, expected_f2]);
738 }
739
740 proptest! {
741 #[test]
743 fn prop_roundtrip_subgroup(
744 log_n in 1usize..=4,
745 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=16),
746 point_raw in 1u32..2013265921u32,
747 ) {
748 let n = 1usize << log_n;
756 let coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
757
758 let evals: Vec<F> = eval_poly_on_coset(&coeffs, F::ONE, log_n);
760 let evals_mat = RowMajorMatrix::new(evals, 1);
761
762 let point = EF4::from_u32(point_raw);
763
764 let result = evals_mat.interpolate_subgroup(point);
766 let expected = eval_poly(&coeffs, point);
767 prop_assert_eq!(result[0], expected);
768 }
769
770 #[test]
772 fn prop_roundtrip_coset(
773 log_n in 1usize..=4,
774 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=16),
775 point_raw in 1u32..2013265921u32,
776 ) {
777 let n = 1usize << log_n;
779 let coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
780 let shift = F::GENERATOR;
781
782 let evals: Vec<F> = eval_poly_on_coset(&coeffs, shift, log_n);
783 let evals_mat = RowMajorMatrix::new(evals, 1);
784 let point = EF4::from_u32(point_raw);
785
786 let result = evals_mat.interpolate_coset(shift, point);
787 let expected = eval_poly(&coeffs, point);
788 prop_assert_eq!(result[0], expected);
789 }
790
791 #[test]
793 fn prop_precomputation_equivalence(
794 log_n in 1usize..=4,
795 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=16),
796 point_raw in 1u32..2013265921u32,
797 ) {
798 let n = 1usize << log_n;
804 let coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
805 let shift = F::GENERATOR;
806
807 let evals: Vec<F> = eval_poly_on_coset(&coeffs, shift, log_n);
808 let evals_mat = RowMajorMatrix::new(evals, 1);
809 let point = EF4::from_u32(point_raw);
810
811 let result_standard = evals_mat.interpolate_coset(shift, point);
813
814 let subgroup_gen = F::two_adic_generator(log_n);
816 let coset: Vec<F> =
817 (0..n).map(|i| shift * subgroup_gen.exp_u64(i as u64)).collect();
818 let diffs: Vec<EF4> = coset.iter().map(|&c| point - c).collect();
819 let diff_invs = batch_multiplicative_inverse(&diffs);
820 let adjusted = compute_adjusted_weights(point, &diff_invs);
821 let result_precomp = evals_mat
822 .interpolate_coset_with_precomputation(shift, point, &adjusted);
823
824 prop_assert_eq!(result_standard, result_precomp);
825 }
826
827 #[test]
829 fn prop_constant_polynomial(
830 log_n in 1usize..=4,
831 c_raw in 0u32..2013265921u32,
832 point_raw in 1u32..2013265921u32,
833 ) {
834 let n = 1usize << log_n;
835 let c = F::from_u32(c_raw);
836
837 let evals = vec![c; n];
839 let evals_mat = RowMajorMatrix::new(evals, 1);
840 let point = EF4::from_u32(point_raw);
841
842 let result = evals_mat.interpolate_subgroup(point);
843 prop_assert_eq!(result[0], EF4::from(c));
844 }
845
846 #[test]
848 fn prop_linearity(
849 log_n in 1usize..=3,
850 f_raw in prop::collection::vec(0u32..2013265921, 1..=8),
851 g_raw in prop::collection::vec(0u32..2013265921, 1..=8),
852 a_raw in 0u32..2013265921u32,
853 b_raw in 0u32..2013265921u32,
854 point_raw in 1u32..2013265921u32,
855 ) {
856 let n = 1usize << log_n;
858 let f_coeffs: Vec<F> = f_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
859 let g_coeffs: Vec<F> = g_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
860 let a = F::from_u32(a_raw);
861 let b = F::from_u32(b_raw);
862
863 let f_evals: Vec<F> = eval_poly_on_coset(&f_coeffs, F::ONE, log_n);
865 let g_evals: Vec<F> = eval_poly_on_coset(&g_coeffs, F::ONE, log_n);
866 let combined_evals: Vec<F> = f_evals
867 .iter()
868 .zip(&g_evals)
869 .map(|(&fe, &ge)| a * fe + b * ge)
870 .collect();
871
872 let f_mat = RowMajorMatrix::new(f_evals, 1);
873 let g_mat = RowMajorMatrix::new(g_evals, 1);
874 let combined_mat = RowMajorMatrix::new(combined_evals, 1);
875 let point = EF4::from_u32(point_raw);
876
877 let interp_f = f_mat.interpolate_subgroup(point)[0];
879 let interp_g = g_mat.interpolate_subgroup(point)[0];
880 let interp_combined = combined_mat.interpolate_subgroup(point)[0];
881
882 let expected = EF4::from(a) * interp_f + EF4::from(b) * interp_g;
883 prop_assert_eq!(interp_combined, expected);
884 }
885
886 #[test]
888 fn prop_batch_equals_individual(
889 log_n in 1usize..=3,
890 f_raw in prop::collection::vec(0u32..2013265921, 1..=8),
891 g_raw in prop::collection::vec(0u32..2013265921, 1..=8),
892 point_raw in 1u32..2013265921u32,
893 ) {
894 let n = 1usize << log_n;
901 let f_coeffs: Vec<F> = f_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
902 let g_coeffs: Vec<F> = g_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
903 let shift = F::GENERATOR;
904
905 let f_evals: Vec<F> = eval_poly_on_coset(&f_coeffs, shift, log_n);
906 let g_evals: Vec<F> = eval_poly_on_coset(&g_coeffs, shift, log_n);
907
908 let batch_evals: Vec<F> = f_evals
910 .iter()
911 .zip(&g_evals)
912 .flat_map(|(&fe, &ge)| vec![fe, ge])
913 .collect();
914 let batch_mat = RowMajorMatrix::new(batch_evals, 2);
915
916 let f_mat = RowMajorMatrix::new(f_evals, 1);
918 let g_mat = RowMajorMatrix::new(g_evals, 1);
919 let point = EF4::from_u32(point_raw);
920
921 let batch_result = batch_mat.interpolate_coset(shift, point);
922 let f_result = f_mat.interpolate_coset(shift, point)[0];
923 let g_result = g_mat.interpolate_coset(shift, point)[0];
924
925 prop_assert_eq!(batch_result[0], f_result);
926 prop_assert_eq!(batch_result[1], g_result);
927 }
928 }
929
930 #[test]
931 fn test_barycentric_weights_empty() {
932 assert_eq!(barycentric_weights::<F>(&[]), Some(vec![]));
933 }
934
935 #[test]
936 fn test_barycentric_weights_duplicates() {
937 let xs = [F::from_u32(1), F::from_u32(2), F::from_u32(1)];
938 assert_eq!(barycentric_weights(&xs), None);
939 }
940
941 #[test]
942 fn test_barycentric_weights_known() {
943 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
948 let ws = barycentric_weights(&xs).unwrap();
949 let half = F::TWO.inverse();
950 assert_eq!(ws, vec![half, -F::ONE, half]);
951 }
952
953 #[test]
954 fn test_interpolate_arbitrary_known_quadratic() {
955 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
958 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
959 let result = evals.interpolate_arbitrary_point(&xs, F::from_u32(100));
960 assert_eq!(result, Some(vec![F::from_u32(10203)]));
961 }
962
963 #[test]
964 fn test_interpolate_arbitrary_point_on_domain() {
965 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
967 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
968 let result = evals.interpolate_arbitrary_point(&xs, F::from_u32(1));
969 assert_eq!(result, Some(vec![F::from_u32(6)]));
970 }
971
972 #[test]
973 fn test_interpolate_arbitrary_duplicates() {
974 let xs = [F::from_u32(1), F::from_u32(1)];
975 let evals = RowMajorMatrix::new(vec![F::from_u32(5), F::from_u32(7)], 1);
976 assert_eq!(
977 evals.interpolate_arbitrary_point(&xs, F::from_u32(42)),
978 None
979 );
980 }
981
982 #[test]
983 fn test_interpolate_arbitrary_duplicates_target_on_duplicate() {
984 let xs = [F::ONE, F::TWO, F::ONE];
999 let evals = RowMajorMatrix::new(vec![F::from_u32(10), F::from_u32(20), F::from_u32(30)], 1);
1000 assert_eq!(evals.interpolate_arbitrary_point(&xs, F::ONE), None);
1001 }
1002
1003 #[test]
1004 fn test_interpolate_arbitrary_duplicates_target_on_unique() {
1005 let xs = [F::ONE, F::TWO, F::ONE];
1020 let evals = RowMajorMatrix::new(vec![F::from_u32(10), F::from_u32(20), F::from_u32(30)], 1);
1021 assert_eq!(evals.interpolate_arbitrary_point(&xs, F::TWO), None);
1022 }
1023
1024 #[test]
1025 fn test_interpolate_arbitrary_multi_column() {
1026 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1029 let evals = RowMajorMatrix::new(
1030 vec![
1031 F::from_u32(3),
1032 F::from_u32(6), F::from_u32(6),
1034 F::from_u32(15), F::from_u32(11),
1036 F::from_u32(32), ],
1038 2,
1039 );
1040 let result = evals
1041 .interpolate_arbitrary_point(&xs, F::from_u32(100))
1042 .unwrap();
1043 assert_eq!(result, vec![F::from_u32(10203), F::from_u32(40506)]);
1045 }
1046
1047 #[test]
1048 fn test_interpolate_arbitrary_with_precomputation_equivalence() {
1049 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1050 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
1051
1052 let point = F::from_u32(100);
1053 let standard = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1054
1055 let weights = barycentric_weights(&xs).unwrap();
1056 let diffs: Vec<F> = xs.iter().map(|&x| point - x).collect();
1057 let diff_invs = batch_multiplicative_inverse(&diffs);
1058 let precomp = evals.interpolate_arbitrary_with_precomputation(&weights, &diff_invs);
1059
1060 assert_eq!(standard, precomp);
1061 }
1062
1063 #[test]
1064 fn test_interpolate_arbitrary_extension_point() {
1065 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1067 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
1068
1069 let point = EF4::GENERATOR;
1071 let result = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1072
1073 let expected = point * point + point * F::TWO + EF4::from(F::from_u32(3));
1074 assert_eq!(result, vec![expected]);
1075 }
1076
1077 #[test]
1078 fn test_interpolate_arbitrary_extension_point_on_domain() {
1079 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1081 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
1082
1083 let point = EF4::from(F::from_u32(1));
1084 let result = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1085 assert_eq!(result, vec![EF4::from(F::from_u32(6))]);
1086 }
1087
1088 #[test]
1089 fn test_recover_coefficients_known_quadratic() {
1090 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1092 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
1093 let coeffs = evals.recover_coefficients(&xs).unwrap();
1094 assert_eq!(
1095 coeffs.values,
1096 vec![F::from_u32(3), F::from_u32(2), F::from_u32(1)]
1097 );
1098 }
1099
1100 #[test]
1101 fn test_recover_coefficients_multi_column() {
1102 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1104 let evals = RowMajorMatrix::new(
1105 vec![
1106 F::from_u32(3),
1107 F::from_u32(6), F::from_u32(6),
1109 F::from_u32(15), F::from_u32(11),
1111 F::from_u32(32), ],
1113 2,
1114 );
1115 let coeffs = evals.recover_coefficients(&xs).unwrap();
1116 assert_eq!(
1118 coeffs.values,
1119 vec![
1120 F::from_u32(3),
1121 F::from_u32(6),
1122 F::from_u32(2),
1123 F::from_u32(5),
1124 F::from_u32(1),
1125 F::from_u32(4),
1126 ]
1127 );
1128 }
1129
1130 #[test]
1131 fn test_interpolate_arbitrary_empty_matrix() {
1132 let xs: Vec<F> = vec![];
1135 let evals = RowMajorMatrix::<F>::new(vec![], 3);
1136 let result = evals.interpolate_arbitrary_point(&xs, F::from_u32(42));
1137 assert_eq!(result, Some(vec![F::ZERO, F::ZERO, F::ZERO]));
1138 }
1139
1140 #[test]
1141 fn test_interpolate_arbitrary_with_precomputation_empty_direct() {
1142 let weights: Vec<F> = vec![];
1144 let diff_invs: Vec<F> = vec![];
1145 let evals = RowMajorMatrix::<F>::new(vec![], 5);
1146 let result = evals.interpolate_arbitrary_with_precomputation(&weights, &diff_invs);
1147 assert_eq!(result, vec![F::ZERO; 5]);
1148 }
1149
1150 #[test]
1151 fn test_lagrange_empty() {
1152 assert_eq!(interpolate_lagrange::<F>(&[]), Some(vec![]));
1153 }
1154
1155 #[test]
1156 fn test_lagrange_single_point() {
1157 let points = [(F::from_u32(7), F::from_u32(42))];
1158 assert_eq!(interpolate_lagrange(&points), Some(vec![F::from_u32(42)]));
1159 }
1160
1161 #[test]
1162 fn test_lagrange_known_quadratic() {
1163 let points = [
1164 (F::from_u32(0), F::from_u32(3)),
1165 (F::from_u32(1), F::from_u32(6)),
1166 (F::from_u32(2), F::from_u32(11)),
1167 ];
1168 let coeffs = interpolate_lagrange(&points).unwrap();
1169 assert_eq!(coeffs, vec![F::from_u32(3), F::from_u32(2), F::from_u32(1)]);
1170 }
1171
1172 #[test]
1173 fn test_lagrange_duplicate_x_returns_none() {
1174 let points = [
1175 (F::from_u32(1), F::from_u32(5)),
1176 (F::from_u32(1), F::from_u32(7)),
1177 ];
1178 assert_eq!(interpolate_lagrange(&points), None);
1179 }
1180
1181 proptest! {
1182 #[test]
1183 fn prop_lagrange_roundtrip(
1184 n in 1usize..=8,
1185 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1186 ) {
1187 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1188 coeffs.resize(n, F::ZERO);
1189
1190 let points: Vec<(F, F)> = (0..n)
1191 .map(|i| {
1192 let x = F::from_u32(i as u32);
1193 let y = eval_poly(&coeffs, x);
1194 (x, y)
1195 })
1196 .collect();
1197
1198 let recovered = interpolate_lagrange(&points).unwrap();
1199 prop_assert_eq!(recovered, coeffs);
1200 }
1201
1202 #[test]
1203 fn prop_arbitrary_roundtrip(
1204 n in 1usize..=8,
1205 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1206 point_raw in 1u32..2013265921u32,
1207 ) {
1208 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1212 coeffs.resize(n, F::ZERO);
1213
1214 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1215 let ys: Vec<F> = xs.iter().map(|&x| eval_poly(&coeffs, x)).collect();
1216 let evals = RowMajorMatrix::new(ys, 1);
1217
1218 let point = F::from_u32(point_raw);
1219 let result = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1220 let expected = eval_poly(&coeffs, point);
1221 prop_assert_eq!(result[0], expected);
1222 }
1223
1224 #[test]
1225 fn prop_recover_coefficients_roundtrip(
1226 n in 1usize..=8,
1227 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1228 ) {
1229 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1230 coeffs.resize(n, F::ZERO);
1231
1232 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1233 let ys: Vec<F> = xs.iter().map(|&x| eval_poly(&coeffs, x)).collect();
1234 let evals = RowMajorMatrix::new(ys, 1);
1235
1236 let recovered = evals.recover_coefficients(&xs).unwrap();
1237 prop_assert_eq!(recovered.values, coeffs);
1238 }
1239
1240 #[test]
1241 fn prop_arbitrary_batch_equals_individual(
1242 n in 1usize..=6,
1243 f_raw in prop::collection::vec(0u32..2013265921, 1..=6),
1244 g_raw in prop::collection::vec(0u32..2013265921, 1..=6),
1245 point_raw in 1u32..2013265921u32,
1246 ) {
1247 let mut f_coeffs: Vec<F> = f_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1249 let mut g_coeffs: Vec<F> = g_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1250 f_coeffs.resize(n, F::ZERO);
1251 g_coeffs.resize(n, F::ZERO);
1252
1253 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1254 let f_ys: Vec<F> = xs.iter().map(|&x| eval_poly(&f_coeffs, x)).collect();
1255 let g_ys: Vec<F> = xs.iter().map(|&x| eval_poly(&g_coeffs, x)).collect();
1256
1257 let batch_vals: Vec<F> = f_ys.iter().zip(&g_ys)
1259 .flat_map(|(&f, &g)| vec![f, g])
1260 .collect();
1261 let batch_mat = RowMajorMatrix::new(batch_vals, 2);
1262 let f_mat = RowMajorMatrix::new(f_ys, 1);
1263 let g_mat = RowMajorMatrix::new(g_ys, 1);
1264
1265 let point = F::from_u32(point_raw);
1266 let batch_result = batch_mat.interpolate_arbitrary_point(&xs, point).unwrap();
1267 let f_result = f_mat.interpolate_arbitrary_point(&xs, point).unwrap()[0];
1268 let g_result = g_mat.interpolate_arbitrary_point(&xs, point).unwrap()[0];
1269
1270 prop_assert_eq!(batch_result[0], f_result);
1271 prop_assert_eq!(batch_result[1], g_result);
1272 }
1273
1274 #[test]
1275 fn prop_precomputation_equivalence_arbitrary(
1276 n in 1usize..=8,
1277 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1278 point_raw in 1u32..2013265921u32,
1279 ) {
1280 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1282 coeffs.resize(n, F::ZERO);
1283
1284 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1285 let ys: Vec<F> = xs.iter().map(|&x| eval_poly(&coeffs, x)).collect();
1286 let evals = RowMajorMatrix::new(ys, 1);
1287
1288 let point = F::from_u32(point_raw);
1289 let standard = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1290
1291 let weights = barycentric_weights(&xs).unwrap();
1292 let diffs: Vec<F> = xs.iter().map(|&x| point - x).collect();
1293
1294 if diffs.iter().any(|d| d.is_zero()) {
1296 return Ok(());
1297 }
1298
1299 let diff_invs = batch_multiplicative_inverse(&diffs);
1300 let precomp = evals.interpolate_arbitrary_with_precomputation(&weights, &diff_invs);
1301 prop_assert_eq!(standard, precomp);
1302 }
1303
1304 #[test]
1305 fn prop_arbitrary_roundtrip_extension_point(
1306 n in 1usize..=8,
1307 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1308 point_raw in prop::collection::vec(0u32..2013265921, 4..=4),
1309 ) {
1310 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1314 coeffs.resize(n, F::ZERO);
1315
1316 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1317 let ys: Vec<F> = xs.iter().map(|&x| eval_poly(&coeffs, x)).collect();
1318 let evals = RowMajorMatrix::new(ys, 1);
1319
1320 let point = EF4::from_basis_coefficients_iter(
1321 point_raw.iter().map(|&v| F::from_u32(v)),
1322 ).unwrap();
1323 let result = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1324 let expected: EF4 = eval_poly(&coeffs, point);
1325 prop_assert_eq!(result[0], expected);
1326 }
1327 }
1328}