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> {
85 self.interpolate_coset(F::ONE, point)
87 }
88
89 fn interpolate_coset<EF: ExtensionField<F>>(&self, shift: F, point: EF) -> Vec<EF> {
98 let log_height = log2_strict_usize(self.height());
99
100 let coset: Vec<F> = TwoAdicMultiplicativeCoset::new(shift, log_height)
102 .unwrap()
103 .iter()
104 .collect();
105
106 let diffs: Vec<EF> = coset.par_iter().map(|&g| point - g).collect();
109
110 if let Some(i) = diffs.iter().position(|d| d.is_zero()) {
113 return self.row(i).unwrap().into_iter().map(EF::from).collect();
114 }
115
116 let diff_invs = batch_multiplicative_inverse(&diffs);
117
118 let adjusted = compute_adjusted_weights(point, &diff_invs);
120 self.interpolate_coset_with_precomputation(shift, point, &adjusted)
121 }
122
123 fn interpolate_coset_with_precomputation<EF: ExtensionField<F>>(
162 &self,
163 shift: F,
164 point: EF,
165 adjusted_weights: &[EF],
166 ) -> Vec<EF> {
167 debug_assert_eq!(adjusted_weights.len(), self.height());
168
169 let log_height = log2_strict_usize(self.height());
170
171 let z_pow_n = point.exp_power_of_2(log_height);
177 let g_pow_n = shift.exp_power_of_2(log_height);
179 let denom_inv = g_pow_n.mul_2exp_u64(log_height as u64).inverse();
181 let scaling_factor = point * (z_pow_n - g_pow_n) * denom_inv;
183
184 let mut evals = self.columnwise_dot_product(adjusted_weights);
189
190 scale_slice_in_place_single_core(&mut evals, scaling_factor);
192 evals
193 }
194}
195
196impl<F: TwoAdicField, M: Matrix<F>> Interpolate<F> for M {}
197
198pub fn barycentric_weights<F: Field>(x_coords: &[F]) -> Option<Vec<F>> {
212 let n = x_coords.len();
213 if n == 0 {
214 return Some(Vec::new());
215 }
216
217 let mut denoms = alloc::vec![F::ONE; n];
219 for i in 0..n {
220 for j in 0..i {
225 let diff = x_coords[i] - x_coords[j];
226 if diff.is_zero() {
228 return None;
229 }
230 denoms[i] *= diff;
231 denoms[j] *= -diff;
232 }
233 }
234
235 Some(batch_multiplicative_inverse(&denoms))
237}
238
239pub trait InterpolateArbitrary<F: Field>: Matrix<F> {
245 fn interpolate_arbitrary_point<EF: ExtensionField<F>>(
258 &self,
259 x_coords: &[F],
260 point: EF,
261 ) -> Option<Vec<EF>> {
262 debug_assert_eq!(x_coords.len(), self.height());
263
264 let weights = barycentric_weights(x_coords)?;
269
270 for (i, &x) in x_coords.iter().enumerate() {
273 if point == EF::from(x) {
274 return Some(self.row(i).unwrap().into_iter().map(EF::from).collect());
275 }
276 }
277
278 let diffs: Vec<EF> = x_coords.iter().map(|&x| point - x).collect();
280 let diff_invs = batch_multiplicative_inverse(&diffs);
281
282 Some(self.interpolate_arbitrary_with_precomputation(&weights, &diff_invs))
283 }
284
285 fn interpolate_arbitrary_with_precomputation<EF: ExtensionField<F>>(
300 &self,
301 weights: &[F],
302 diff_invs: &[EF],
303 ) -> Vec<EF> {
304 debug_assert_eq!(weights.len(), self.height());
305 debug_assert_eq!(diff_invs.len(), self.height());
306
307 if self.height() == 0 {
310 return EF::zero_vec(self.width());
311 }
312
313 let col_scale: Vec<EF> = weights
323 .iter()
324 .zip(diff_invs)
325 .map(|(&w, &d)| d * w)
326 .collect();
327
328 let denominator = col_scale.iter().copied().fold(EF::ZERO, |a, b| a + b);
330 let denom_inv = denominator.inverse();
331
332 let mut evals = self.columnwise_dot_product(&col_scale);
334
335 scale_slice_in_place_single_core(&mut evals, denom_inv);
337 evals
338 }
339
340 fn recover_coefficients(&self, x_coords: &[F]) -> Option<RowMajorMatrix<F>> {
354 let n = self.height();
355 let w = self.width();
356 debug_assert_eq!(x_coords.len(), n);
357
358 if n == 0 {
359 return Some(RowMajorMatrix::new(Vec::new(), w.max(1)));
360 }
361
362 let mut result = RowMajorMatrix::new(F::zero_vec(n * w), w);
364
365 let mut basis = F::zero_vec(n);
368 basis[0] = F::ONE;
369
370 let mut scratch = F::zero_vec(w);
372
373 for k in 0..n {
374 let x_k = x_coords[k];
375
376 let mut b_xk = F::ONE;
381 for &x_i in &x_coords[..k] {
382 b_xk *= x_k - x_i;
383 }
384 let b_xk_inv = b_xk.try_inverse()?;
386
387 scratch.fill(F::ZERO);
393 for i in (0..k).rev() {
394 let row = result.row_slice(i).unwrap();
395 for j in 0..w {
396 scratch[j] = scratch[j] * x_k + row[j];
397 }
398 }
399
400 for (j, y_kj) in self.row(k).unwrap().into_iter().enumerate() {
403 scratch[j] = (y_kj - scratch[j]) * b_xk_inv;
404 }
405
406 for (i, &b_i) in basis.iter().enumerate().take(k + 1) {
408 let row = result.row_mut(i);
409 for j in 0..w {
410 row[j] += scratch[j] * b_i;
411 }
412 }
413
414 if k + 1 < n {
421 basis[k + 1] = basis[k];
422 }
423 for i in (1..=k).rev() {
424 basis[i] = basis[i - 1] - x_k * basis[i];
425 }
426 basis[0] = -x_k * basis[0];
427 }
428
429 Some(result)
430 }
431}
432
433impl<F: Field, M: Matrix<F>> InterpolateArbitrary<F> for M {}
434
435pub fn interpolate_lagrange<F: Field>(points: &[(F, F)]) -> Option<Vec<F>> {
449 if points.is_empty() {
450 return Some(Vec::new());
451 }
452 let (xs, ys): (Vec<F>, Vec<F>) = points.iter().copied().unzip();
454 let evals = RowMajorMatrix::new_col(ys);
456 Some(evals.recover_coefficients(&xs)?.values)
457}
458
459#[cfg(test)]
460mod tests {
461 use alloc::vec;
462 use alloc::vec::Vec;
463
464 use p3_baby_bear::BabyBear;
465 use p3_field::extension::BinomialExtensionField;
466 use p3_field::{
467 BasedVectorSpace, ExtensionField, Field, HornerIter, PrimeCharacteristicRing, TwoAdicField,
468 batch_multiplicative_inverse,
469 };
470 use p3_util::log2_strict_usize;
471 use proptest::prelude::*;
472
473 use super::*;
474 use crate::dense::RowMajorMatrix;
475
476 type F = BabyBear;
477 type EF4 = BinomialExtensionField<BabyBear, 4>;
478
479 fn eval_poly<EF: ExtensionField<F>>(coeffs: &[F], point: EF) -> EF {
484 coeffs.iter().copied().horner(point)
485 }
486
487 fn eval_poly_on_coset<EF: ExtensionField<F>>(coeffs: &[F], shift: F, log_n: usize) -> Vec<EF> {
488 let n = 1 << log_n;
489 let subgroup_gen = F::two_adic_generator(log_n);
491 (0..n)
492 .map(|i| {
493 let coset_elem = shift * subgroup_gen.exp_u64(i as u64);
495 eval_poly(coeffs, EF::from(coset_elem))
496 })
497 .collect()
498 }
499
500 #[test]
501 fn test_interpolate_subgroup() {
502 let evals = [
507 6, 886605102, 1443543107, 708307799, 2, 556938009, 569722818, 1874680944,
508 ]
509 .map(F::from_u32);
510
511 let evals_mat = RowMajorMatrix::new(evals.to_vec(), 1);
513
514 let point = F::from_u16(100);
516 let result = evals_mat.interpolate_subgroup(point);
517
518 assert_eq!(result, vec![F::from_u16(10203)]);
520 }
521
522 #[test]
523 fn test_interpolate_coset() {
524 let shift = F::GENERATOR;
529
530 let evals = [
532 1026, 129027310, 457985035, 994890337, 902, 1988942953, 1555278970, 913671254,
533 ]
534 .map(F::from_u32);
535
536 let evals_mat = RowMajorMatrix::new(evals.to_vec(), 1);
538
539 let point = F::from_u16(100);
541 let result = evals_mat.interpolate_coset(shift, point);
542 assert_eq!(result, vec![F::from_u16(10203)]);
543
544 let n = evals.len();
547 let k = log2_strict_usize(n);
548
549 let coset = F::two_adic_generator(k).shifted_powers(shift).collect_n(n);
551
552 let denom: Vec<_> = coset.iter().map(|&w| point - w).collect();
554 let denom = batch_multiplicative_inverse(&denom);
555
556 let adjusted = compute_adjusted_weights(point, &denom);
558
559 let result = evals_mat.interpolate_coset_with_precomputation(shift, point, &adjusted);
561 assert_eq!(result, vec![F::from_u16(10203)]);
562 }
563
564 #[test]
565 fn test_interpolate_coset_single_point_identity() {
566 let c = F::from_u32(42);
569
570 let evals = vec![c; 8];
572 let evals_mat = RowMajorMatrix::new(evals, 1);
573
574 let shift = F::GENERATOR;
575 let point = F::from_u16(1337);
576
577 let result = evals_mat.interpolate_coset(shift, point);
578 assert_eq!(result, vec![c]);
579 }
580
581 #[test]
582 fn test_interpolate_coset_point_on_coset() {
583 let log_n = 3;
585 let n = 1usize << log_n;
586 let shift = F::GENERATOR;
587 let h = F::two_adic_generator(log_n);
588
589 let coset: Vec<F> = (0..n).map(|i| shift * h.exp_u64(i as u64)).collect();
590 let evals: Vec<F> = (0..n as u32).map(|i| F::from_u32(100 + i)).collect();
591 let m = RowMajorMatrix::new(evals.clone(), 1);
592
593 for (i, &x) in coset.iter().enumerate() {
595 let result = m.interpolate_coset(shift, x);
596 assert_eq!(result, vec![evals[i]]);
597 }
598 }
599
600 #[test]
601 fn test_interpolate_coset_point_on_coset_extension() {
602 let log_n = 3;
605 let n = 1usize << log_n;
606 let shift = F::GENERATOR;
607 let h = F::two_adic_generator(log_n);
608
609 let coset: Vec<F> = (0..n).map(|i| shift * h.exp_u64(i as u64)).collect();
610
611 let mut evals: Vec<F> = Vec::with_capacity(n * 2);
612 for i in 0..n {
613 evals.push(F::from_u32(200 + i as u32));
614 evals.push(F::from_u32(300 + i as u32));
615 }
616 let m = RowMajorMatrix::new(evals, 2);
617
618 let i = 3;
620 let result = m.interpolate_coset(shift, EF4::from(coset[i]));
621 assert_eq!(
622 result,
623 vec![
624 EF4::from(F::from_u32(200 + i as u32)),
625 EF4::from(F::from_u32(300 + i as u32)),
626 ]
627 );
628 }
629
630 #[test]
631 fn test_interpolate_subgroup_point_on_subgroup() {
632 let log_n = 2;
634 let n = 1usize << log_n;
635 let h = F::two_adic_generator(log_n);
636
637 let evals: Vec<F> = (0..n as u32).map(|i| F::from_u32(10 + i)).collect();
638 let m = RowMajorMatrix::new(evals.clone(), 1);
639
640 let result = m.interpolate_subgroup(h.exp_u64(2));
642 assert_eq!(result, vec![evals[2]]);
643 }
644
645 #[test]
646 fn test_interpolate_subgroup_degree_3_correctness() {
647 let poly = |x: EF4| x * x * x + x * x * F::TWO + x * F::from_u32(3) + F::from_u32(4);
653
654 let subgroup = EF4::two_adic_generator(2).powers().collect_n(4);
656 let evals: Vec<_> = subgroup.iter().map(|&x| poly(x)).collect();
657 let evals_mat = RowMajorMatrix::new(evals, 1);
658
659 let point = EF4::from_u16(5);
661 let result = evals_mat.interpolate_subgroup(point);
662 let expected = poly(point);
663 assert_eq!(result[0], expected);
664 }
665
666 #[test]
667 fn test_interpolate_coset_multiple_polynomials() {
668 let shift = EF4::GENERATOR;
679 let coset = EF4::two_adic_generator(3)
680 .shifted_powers(shift)
681 .collect_n(8);
682
683 let f1 = |x: EF4| x * x + x * F::TWO + F::from_u32(3);
684 let f2 = |x: EF4| x * x * F::from_u32(4) + x * F::from_u32(5) + F::from_u32(6);
685
686 let evals: Vec<_> = coset.iter().flat_map(|&x| vec![f1(x), f2(x)]).collect();
688
689 let evals_mat = RowMajorMatrix::new(evals, 2);
691
692 let point = EF4::from_u32(77);
694 let result = evals_mat.interpolate_coset(shift, point);
695
696 let expected_f1 = f1(point);
698 let expected_f2 = f2(point);
699
700 assert_eq!(result[0], expected_f1);
701 assert_eq!(result[1], expected_f2);
702 }
703
704 #[test]
705 fn test_interpolate_subgroup_multiple_columns() {
706 let f1 = |x: EF4| x * x + x * F::TWO + F::from_u32(3);
714 let f2 = |x: EF4| x * x * F::from_u32(4) + x * F::from_u32(5) + F::from_u32(6);
715
716 let subgroup_iter = EF4::two_adic_generator(3).powers().take(8);
718
719 let evals: Vec<_> = subgroup_iter.flat_map(|x| vec![f1(x), f2(x)]).collect();
721
722 let evals_mat = RowMajorMatrix::new(evals, 2);
724
725 let point = EF4::from_u32(77);
727 let result = evals_mat.interpolate_subgroup(point);
728
729 let expected_f1 = f1(point);
731 let expected_f2 = f2(point);
732
733 assert_eq!(result, vec![expected_f1, expected_f2]);
734 }
735
736 proptest! {
737 #[test]
739 fn prop_roundtrip_subgroup(
740 log_n in 1usize..=4,
741 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=16),
742 point_raw in 1u32..2013265921u32,
743 ) {
744 let n = 1usize << log_n;
752 let coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
753
754 let evals: Vec<F> = eval_poly_on_coset(&coeffs, F::ONE, log_n);
756 let evals_mat = RowMajorMatrix::new(evals, 1);
757
758 let point = EF4::from_u32(point_raw);
759
760 let result = evals_mat.interpolate_subgroup(point);
762 let expected = eval_poly(&coeffs, point);
763 prop_assert_eq!(result[0], expected);
764 }
765
766 #[test]
768 fn prop_roundtrip_coset(
769 log_n in 1usize..=4,
770 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=16),
771 point_raw in 1u32..2013265921u32,
772 ) {
773 let n = 1usize << log_n;
775 let coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
776 let shift = F::GENERATOR;
777
778 let evals: Vec<F> = eval_poly_on_coset(&coeffs, shift, log_n);
779 let evals_mat = RowMajorMatrix::new(evals, 1);
780 let point = EF4::from_u32(point_raw);
781
782 let result = evals_mat.interpolate_coset(shift, point);
783 let expected = eval_poly(&coeffs, point);
784 prop_assert_eq!(result[0], expected);
785 }
786
787 #[test]
789 fn prop_precomputation_equivalence(
790 log_n in 1usize..=4,
791 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=16),
792 point_raw in 1u32..2013265921u32,
793 ) {
794 let n = 1usize << log_n;
800 let coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
801 let shift = F::GENERATOR;
802
803 let evals: Vec<F> = eval_poly_on_coset(&coeffs, shift, log_n);
804 let evals_mat = RowMajorMatrix::new(evals, 1);
805 let point = EF4::from_u32(point_raw);
806
807 let result_standard = evals_mat.interpolate_coset(shift, point);
809
810 let subgroup_gen = F::two_adic_generator(log_n);
812 let coset: Vec<F> =
813 (0..n).map(|i| shift * subgroup_gen.exp_u64(i as u64)).collect();
814 let diffs: Vec<EF4> = coset.iter().map(|&c| point - c).collect();
815 let diff_invs = batch_multiplicative_inverse(&diffs);
816 let adjusted = compute_adjusted_weights(point, &diff_invs);
817 let result_precomp = evals_mat
818 .interpolate_coset_with_precomputation(shift, point, &adjusted);
819
820 prop_assert_eq!(result_standard, result_precomp);
821 }
822
823 #[test]
825 fn prop_constant_polynomial(
826 log_n in 1usize..=4,
827 c_raw in 0u32..2013265921u32,
828 point_raw in 1u32..2013265921u32,
829 ) {
830 let n = 1usize << log_n;
831 let c = F::from_u32(c_raw);
832
833 let evals = vec![c; n];
835 let evals_mat = RowMajorMatrix::new(evals, 1);
836 let point = EF4::from_u32(point_raw);
837
838 let result = evals_mat.interpolate_subgroup(point);
839 prop_assert_eq!(result[0], EF4::from(c));
840 }
841
842 #[test]
844 fn prop_linearity(
845 log_n in 1usize..=3,
846 f_raw in prop::collection::vec(0u32..2013265921, 1..=8),
847 g_raw in prop::collection::vec(0u32..2013265921, 1..=8),
848 a_raw in 0u32..2013265921u32,
849 b_raw in 0u32..2013265921u32,
850 point_raw in 1u32..2013265921u32,
851 ) {
852 let n = 1usize << log_n;
854 let f_coeffs: Vec<F> = f_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
855 let g_coeffs: Vec<F> = g_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
856 let a = F::from_u32(a_raw);
857 let b = F::from_u32(b_raw);
858
859 let f_evals: Vec<F> = eval_poly_on_coset(&f_coeffs, F::ONE, log_n);
861 let g_evals: Vec<F> = eval_poly_on_coset(&g_coeffs, F::ONE, log_n);
862 let combined_evals: Vec<F> = f_evals
863 .iter()
864 .zip(&g_evals)
865 .map(|(&fe, &ge)| a * fe + b * ge)
866 .collect();
867
868 let f_mat = RowMajorMatrix::new(f_evals, 1);
869 let g_mat = RowMajorMatrix::new(g_evals, 1);
870 let combined_mat = RowMajorMatrix::new(combined_evals, 1);
871 let point = EF4::from_u32(point_raw);
872
873 let interp_f = f_mat.interpolate_subgroup(point)[0];
875 let interp_g = g_mat.interpolate_subgroup(point)[0];
876 let interp_combined = combined_mat.interpolate_subgroup(point)[0];
877
878 let expected = EF4::from(a) * interp_f + EF4::from(b) * interp_g;
879 prop_assert_eq!(interp_combined, expected);
880 }
881
882 #[test]
884 fn prop_batch_equals_individual(
885 log_n in 1usize..=3,
886 f_raw in prop::collection::vec(0u32..2013265921, 1..=8),
887 g_raw in prop::collection::vec(0u32..2013265921, 1..=8),
888 point_raw in 1u32..2013265921u32,
889 ) {
890 let n = 1usize << log_n;
897 let f_coeffs: Vec<F> = f_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
898 let g_coeffs: Vec<F> = g_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
899 let shift = F::GENERATOR;
900
901 let f_evals: Vec<F> = eval_poly_on_coset(&f_coeffs, shift, log_n);
902 let g_evals: Vec<F> = eval_poly_on_coset(&g_coeffs, shift, log_n);
903
904 let batch_evals: Vec<F> = f_evals
906 .iter()
907 .zip(&g_evals)
908 .flat_map(|(&fe, &ge)| vec![fe, ge])
909 .collect();
910 let batch_mat = RowMajorMatrix::new(batch_evals, 2);
911
912 let f_mat = RowMajorMatrix::new(f_evals, 1);
914 let g_mat = RowMajorMatrix::new(g_evals, 1);
915 let point = EF4::from_u32(point_raw);
916
917 let batch_result = batch_mat.interpolate_coset(shift, point);
918 let f_result = f_mat.interpolate_coset(shift, point)[0];
919 let g_result = g_mat.interpolate_coset(shift, point)[0];
920
921 prop_assert_eq!(batch_result[0], f_result);
922 prop_assert_eq!(batch_result[1], g_result);
923 }
924 }
925
926 #[test]
927 fn test_barycentric_weights_empty() {
928 assert_eq!(barycentric_weights::<F>(&[]), Some(vec![]));
929 }
930
931 #[test]
932 fn test_barycentric_weights_duplicates() {
933 let xs = [F::from_u32(1), F::from_u32(2), F::from_u32(1)];
934 assert_eq!(barycentric_weights(&xs), None);
935 }
936
937 #[test]
938 fn test_barycentric_weights_known() {
939 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
944 let ws = barycentric_weights(&xs).unwrap();
945 let half = F::TWO.inverse();
946 assert_eq!(ws, vec![half, -F::ONE, half]);
947 }
948
949 #[test]
950 fn test_interpolate_arbitrary_known_quadratic() {
951 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
954 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
955 let result = evals.interpolate_arbitrary_point(&xs, F::from_u32(100));
956 assert_eq!(result, Some(vec![F::from_u32(10203)]));
957 }
958
959 #[test]
960 fn test_interpolate_arbitrary_point_on_domain() {
961 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
963 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
964 let result = evals.interpolate_arbitrary_point(&xs, F::from_u32(1));
965 assert_eq!(result, Some(vec![F::from_u32(6)]));
966 }
967
968 #[test]
969 fn test_interpolate_arbitrary_duplicates() {
970 let xs = [F::from_u32(1), F::from_u32(1)];
971 let evals = RowMajorMatrix::new(vec![F::from_u32(5), F::from_u32(7)], 1);
972 assert_eq!(
973 evals.interpolate_arbitrary_point(&xs, F::from_u32(42)),
974 None
975 );
976 }
977
978 #[test]
979 fn test_interpolate_arbitrary_duplicates_target_on_duplicate() {
980 let xs = [F::ONE, F::TWO, F::ONE];
995 let evals = RowMajorMatrix::new(vec![F::from_u32(10), F::from_u32(20), F::from_u32(30)], 1);
996 assert_eq!(evals.interpolate_arbitrary_point(&xs, F::ONE), None);
997 }
998
999 #[test]
1000 fn test_interpolate_arbitrary_duplicates_target_on_unique() {
1001 let xs = [F::ONE, F::TWO, F::ONE];
1016 let evals = RowMajorMatrix::new(vec![F::from_u32(10), F::from_u32(20), F::from_u32(30)], 1);
1017 assert_eq!(evals.interpolate_arbitrary_point(&xs, F::TWO), None);
1018 }
1019
1020 #[test]
1021 fn test_interpolate_arbitrary_multi_column() {
1022 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1025 let evals = RowMajorMatrix::new(
1026 vec![
1027 F::from_u32(3),
1028 F::from_u32(6), F::from_u32(6),
1030 F::from_u32(15), F::from_u32(11),
1032 F::from_u32(32), ],
1034 2,
1035 );
1036 let result = evals
1037 .interpolate_arbitrary_point(&xs, F::from_u32(100))
1038 .unwrap();
1039 assert_eq!(result, vec![F::from_u32(10203), F::from_u32(40506)]);
1041 }
1042
1043 #[test]
1044 fn test_interpolate_arbitrary_with_precomputation_equivalence() {
1045 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1046 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
1047
1048 let point = F::from_u32(100);
1049 let standard = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1050
1051 let weights = barycentric_weights(&xs).unwrap();
1052 let diffs: Vec<F> = xs.iter().map(|&x| point - x).collect();
1053 let diff_invs = batch_multiplicative_inverse(&diffs);
1054 let precomp = evals.interpolate_arbitrary_with_precomputation(&weights, &diff_invs);
1055
1056 assert_eq!(standard, precomp);
1057 }
1058
1059 #[test]
1060 fn test_interpolate_arbitrary_extension_point() {
1061 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1063 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
1064
1065 let point = EF4::GENERATOR;
1067 let result = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1068
1069 let expected = point * point + point * F::TWO + EF4::from(F::from_u32(3));
1070 assert_eq!(result, vec![expected]);
1071 }
1072
1073 #[test]
1074 fn test_interpolate_arbitrary_extension_point_on_domain() {
1075 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1077 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
1078
1079 let point = EF4::from(F::from_u32(1));
1080 let result = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1081 assert_eq!(result, vec![EF4::from(F::from_u32(6))]);
1082 }
1083
1084 #[test]
1085 fn test_recover_coefficients_known_quadratic() {
1086 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1088 let evals = RowMajorMatrix::new(vec![F::from_u32(3), F::from_u32(6), F::from_u32(11)], 1);
1089 let coeffs = evals.recover_coefficients(&xs).unwrap();
1090 assert_eq!(
1091 coeffs.values,
1092 vec![F::from_u32(3), F::from_u32(2), F::from_u32(1)]
1093 );
1094 }
1095
1096 #[test]
1097 fn test_recover_coefficients_multi_column() {
1098 let xs = [F::from_u32(0), F::from_u32(1), F::from_u32(2)];
1100 let evals = RowMajorMatrix::new(
1101 vec![
1102 F::from_u32(3),
1103 F::from_u32(6), F::from_u32(6),
1105 F::from_u32(15), F::from_u32(11),
1107 F::from_u32(32), ],
1109 2,
1110 );
1111 let coeffs = evals.recover_coefficients(&xs).unwrap();
1112 assert_eq!(
1114 coeffs.values,
1115 vec![
1116 F::from_u32(3),
1117 F::from_u32(6),
1118 F::from_u32(2),
1119 F::from_u32(5),
1120 F::from_u32(1),
1121 F::from_u32(4),
1122 ]
1123 );
1124 }
1125
1126 #[test]
1127 fn test_interpolate_arbitrary_empty_matrix() {
1128 let xs: Vec<F> = vec![];
1131 let evals = RowMajorMatrix::<F>::new(vec![], 3);
1132 let result = evals.interpolate_arbitrary_point(&xs, F::from_u32(42));
1133 assert_eq!(result, Some(vec![F::ZERO, F::ZERO, F::ZERO]));
1134 }
1135
1136 #[test]
1137 fn test_interpolate_arbitrary_with_precomputation_empty_direct() {
1138 let weights: Vec<F> = vec![];
1140 let diff_invs: Vec<F> = vec![];
1141 let evals = RowMajorMatrix::<F>::new(vec![], 5);
1142 let result = evals.interpolate_arbitrary_with_precomputation(&weights, &diff_invs);
1143 assert_eq!(result, vec![F::ZERO; 5]);
1144 }
1145
1146 #[test]
1147 fn test_lagrange_empty() {
1148 assert_eq!(interpolate_lagrange::<F>(&[]), Some(vec![]));
1149 }
1150
1151 #[test]
1152 fn test_lagrange_single_point() {
1153 let points = [(F::from_u32(7), F::from_u32(42))];
1154 assert_eq!(interpolate_lagrange(&points), Some(vec![F::from_u32(42)]));
1155 }
1156
1157 #[test]
1158 fn test_lagrange_known_quadratic() {
1159 let points = [
1160 (F::from_u32(0), F::from_u32(3)),
1161 (F::from_u32(1), F::from_u32(6)),
1162 (F::from_u32(2), F::from_u32(11)),
1163 ];
1164 let coeffs = interpolate_lagrange(&points).unwrap();
1165 assert_eq!(coeffs, vec![F::from_u32(3), F::from_u32(2), F::from_u32(1)]);
1166 }
1167
1168 #[test]
1169 fn test_lagrange_duplicate_x_returns_none() {
1170 let points = [
1171 (F::from_u32(1), F::from_u32(5)),
1172 (F::from_u32(1), F::from_u32(7)),
1173 ];
1174 assert_eq!(interpolate_lagrange(&points), None);
1175 }
1176
1177 proptest! {
1178 #[test]
1179 fn prop_lagrange_roundtrip(
1180 n in 1usize..=8,
1181 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1182 ) {
1183 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1184 coeffs.resize(n, F::ZERO);
1185
1186 let points: Vec<(F, F)> = (0..n)
1187 .map(|i| {
1188 let x = F::from_u32(i as u32);
1189 let y = eval_poly(&coeffs, x);
1190 (x, y)
1191 })
1192 .collect();
1193
1194 let recovered = interpolate_lagrange(&points).unwrap();
1195 prop_assert_eq!(recovered, coeffs);
1196 }
1197
1198 #[test]
1199 fn prop_arbitrary_roundtrip(
1200 n in 1usize..=8,
1201 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1202 point_raw in 1u32..2013265921u32,
1203 ) {
1204 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1208 coeffs.resize(n, F::ZERO);
1209
1210 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1211 let ys: Vec<F> = xs.iter().map(|&x| eval_poly(&coeffs, x)).collect();
1212 let evals = RowMajorMatrix::new(ys, 1);
1213
1214 let point = F::from_u32(point_raw);
1215 let result = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1216 let expected = eval_poly(&coeffs, point);
1217 prop_assert_eq!(result[0], expected);
1218 }
1219
1220 #[test]
1221 fn prop_recover_coefficients_roundtrip(
1222 n in 1usize..=8,
1223 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1224 ) {
1225 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1226 coeffs.resize(n, F::ZERO);
1227
1228 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1229 let ys: Vec<F> = xs.iter().map(|&x| eval_poly(&coeffs, x)).collect();
1230 let evals = RowMajorMatrix::new(ys, 1);
1231
1232 let recovered = evals.recover_coefficients(&xs).unwrap();
1233 prop_assert_eq!(recovered.values, coeffs);
1234 }
1235
1236 #[test]
1237 fn prop_arbitrary_batch_equals_individual(
1238 n in 1usize..=6,
1239 f_raw in prop::collection::vec(0u32..2013265921, 1..=6),
1240 g_raw in prop::collection::vec(0u32..2013265921, 1..=6),
1241 point_raw in 1u32..2013265921u32,
1242 ) {
1243 let mut f_coeffs: Vec<F> = f_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1245 let mut g_coeffs: Vec<F> = g_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1246 f_coeffs.resize(n, F::ZERO);
1247 g_coeffs.resize(n, F::ZERO);
1248
1249 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1250 let f_ys: Vec<F> = xs.iter().map(|&x| eval_poly(&f_coeffs, x)).collect();
1251 let g_ys: Vec<F> = xs.iter().map(|&x| eval_poly(&g_coeffs, x)).collect();
1252
1253 let batch_vals: Vec<F> = f_ys.iter().zip(&g_ys)
1255 .flat_map(|(&f, &g)| vec![f, g])
1256 .collect();
1257 let batch_mat = RowMajorMatrix::new(batch_vals, 2);
1258 let f_mat = RowMajorMatrix::new(f_ys, 1);
1259 let g_mat = RowMajorMatrix::new(g_ys, 1);
1260
1261 let point = F::from_u32(point_raw);
1262 let batch_result = batch_mat.interpolate_arbitrary_point(&xs, point).unwrap();
1263 let f_result = f_mat.interpolate_arbitrary_point(&xs, point).unwrap()[0];
1264 let g_result = g_mat.interpolate_arbitrary_point(&xs, point).unwrap()[0];
1265
1266 prop_assert_eq!(batch_result[0], f_result);
1267 prop_assert_eq!(batch_result[1], g_result);
1268 }
1269
1270 #[test]
1271 fn prop_precomputation_equivalence_arbitrary(
1272 n in 1usize..=8,
1273 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1274 point_raw in 1u32..2013265921u32,
1275 ) {
1276 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1278 coeffs.resize(n, F::ZERO);
1279
1280 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1281 let ys: Vec<F> = xs.iter().map(|&x| eval_poly(&coeffs, x)).collect();
1282 let evals = RowMajorMatrix::new(ys, 1);
1283
1284 let point = F::from_u32(point_raw);
1285 let standard = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1286
1287 let weights = barycentric_weights(&xs).unwrap();
1288 let diffs: Vec<F> = xs.iter().map(|&x| point - x).collect();
1289
1290 if diffs.iter().any(|d| d.is_zero()) {
1292 return Ok(());
1293 }
1294
1295 let diff_invs = batch_multiplicative_inverse(&diffs);
1296 let precomp = evals.interpolate_arbitrary_with_precomputation(&weights, &diff_invs);
1297 prop_assert_eq!(standard, precomp);
1298 }
1299
1300 #[test]
1301 fn prop_arbitrary_roundtrip_extension_point(
1302 n in 1usize..=8,
1303 coeffs_raw in prop::collection::vec(0u32..2013265921, 1..=8),
1304 point_raw in prop::collection::vec(0u32..2013265921, 4..=4),
1305 ) {
1306 let mut coeffs: Vec<F> = coeffs_raw.iter().take(n).map(|&v| F::from_u32(v)).collect();
1310 coeffs.resize(n, F::ZERO);
1311
1312 let xs: Vec<F> = (0..n).map(|i| F::from_u32(i as u32)).collect();
1313 let ys: Vec<F> = xs.iter().map(|&x| eval_poly(&coeffs, x)).collect();
1314 let evals = RowMajorMatrix::new(ys, 1);
1315
1316 let point = EF4::from_basis_coefficients_iter(
1317 point_raw.iter().map(|&v| F::from_u32(v)),
1318 ).unwrap();
1319 let result = evals.interpolate_arbitrary_point(&xs, point).unwrap();
1320 let expected: EF4 = eval_poly(&coeffs, point);
1321 prop_assert_eq!(result[0], expected);
1322 }
1323 }
1324}