#![no_std]
extern crate alloc;
use alloc::vec::Vec;
use p3_field::coset::TwoAdicMultiplicativeCoset;
use p3_field::{
ExtensionField, TwoAdicField, batch_multiplicative_inverse, scale_slice_in_place_single_core,
};
use p3_matrix::Matrix;
use p3_maybe_rayon::prelude::*;
use p3_util::log2_strict_usize;
pub fn interpolate_subgroup<F, EF, Mat>(subgroup_evals: &Mat, point: EF) -> Vec<EF>
where
F: TwoAdicField,
EF: ExtensionField<F>,
Mat: Matrix<F>,
{
interpolate_coset(subgroup_evals, F::ONE, point)
}
pub fn interpolate_coset<F, EF, Mat>(coset_evals: &Mat, shift: F, point: EF) -> Vec<EF>
where
F: TwoAdicField,
EF: ExtensionField<F>,
Mat: Matrix<F>,
{
let height = coset_evals.height();
let log_height = log2_strict_usize(height);
let coset = TwoAdicMultiplicativeCoset::new(shift, log_height)
.unwrap()
.iter()
.collect();
let diffs: Vec<_> = coset.par_iter().map(|&g| point - g).collect();
let diff_invs = batch_multiplicative_inverse(&diffs);
interpolate_coset_with_precomputation(coset_evals, shift, point, &coset, &diff_invs)
}
pub fn interpolate_coset_with_precomputation<F, EF, Mat>(
coset_evals: &Mat,
shift: F,
point: EF,
coset: &[F],
diff_invs: &[EF],
) -> Vec<EF>
where
F: TwoAdicField,
EF: ExtensionField<F>,
Mat: Matrix<F>,
{
debug_assert_eq!(coset.len(), diff_invs.len());
debug_assert_eq!(coset.len(), coset_evals.height());
let height = coset_evals.height();
let log_height = log2_strict_usize(height);
let col_scale: Vec<_> = coset
.par_iter()
.zip(diff_invs)
.map(|(&sg, &diff_inv)| diff_inv * sg)
.collect();
let point_pow_height = point.exp_power_of_2(log_height);
let shift_pow_height = shift.exp_power_of_2(log_height);
let vanishing_polynomial = point_pow_height - shift_pow_height;
let denominator = shift_pow_height.mul_2exp_u64(log_height as u64);
let scaling_factor = vanishing_polynomial * denominator.inverse();
let mut evals = coset_evals.columnwise_dot_product(&col_scale);
scale_slice_in_place_single_core(&mut evals, scaling_factor);
evals
}
#[cfg(test)]
mod tests {
use alloc::vec;
use alloc::vec::Vec;
use p3_baby_bear::BabyBear;
use p3_field::extension::BinomialExtensionField;
use p3_field::{Field, PrimeCharacteristicRing, TwoAdicField, batch_multiplicative_inverse};
use p3_matrix::dense::RowMajorMatrix;
use p3_util::log2_strict_usize;
use crate::{interpolate_coset, interpolate_coset_with_precomputation, interpolate_subgroup};
#[test]
fn test_interpolate_subgroup() {
type F = BabyBear;
let evals = [
6, 886605102, 1443543107, 708307799, 2, 556938009, 569722818, 1874680944,
]
.map(F::from_u32);
let evals_mat = RowMajorMatrix::new(evals.to_vec(), 1);
let point = F::from_u16(100);
let result = interpolate_subgroup(&evals_mat, point);
assert_eq!(result, vec![F::from_u16(10203)]);
}
#[test]
fn test_interpolate_coset() {
type F = BabyBear;
let shift = F::GENERATOR;
let evals = [
1026, 129027310, 457985035, 994890337, 902, 1988942953, 1555278970, 913671254,
]
.map(F::from_u32);
let evals_mat = RowMajorMatrix::new(evals.to_vec(), 1);
let point = F::from_u16(100);
let result = interpolate_coset(&evals_mat, shift, point);
assert_eq!(result, vec![F::from_u16(10203)]);
let n = evals.len();
let k = log2_strict_usize(n);
let coset = F::two_adic_generator(k).shifted_powers(shift).collect_n(n);
let denom: Vec<_> = coset.iter().map(|&w| point - w).collect();
let denom = batch_multiplicative_inverse(&denom);
let result =
interpolate_coset_with_precomputation(&evals_mat, shift, point, &coset, &denom);
assert_eq!(result, vec![F::from_u16(10203)]);
}
#[test]
fn test_interpolate_coset_single_point_identity() {
type F = BabyBear;
let c = F::from_u32(42); let evals = vec![c; 8];
let evals_mat = RowMajorMatrix::new(evals, 1);
let shift = F::GENERATOR;
let point = F::from_u16(1337);
let result = interpolate_coset(&evals_mat, shift, point);
assert_eq!(result, vec![c]); }
#[test]
fn test_interpolate_subgroup_degree_3_correctness() {
type F = BabyBear;
type EF4 = BinomialExtensionField<BabyBear, 4>;
let poly = |x: EF4| x * x * x + x * x * F::TWO + x * F::from_u32(3) + F::from_u32(4);
let subgroup = EF4::two_adic_generator(2).powers().collect_n(4);
let evals: Vec<_> = subgroup.iter().map(|&x| poly(x)).collect();
let evals_mat = RowMajorMatrix::new(evals, 1);
let point = EF4::from_u16(5);
let result = interpolate_subgroup(&evals_mat, point);
let expected = poly(point);
assert_eq!(result[0], expected);
}
#[test]
fn test_interpolate_coset_multiple_polynomials() {
type F = BabyBear;
type EF4 = BinomialExtensionField<BabyBear, 4>;
let shift = EF4::GENERATOR;
let coset = EF4::two_adic_generator(3)
.shifted_powers(shift)
.collect_n(8);
let f1 = |x: EF4| x * x + x * F::TWO + F::from_u32(3);
let f2 = |x: EF4| x * x * F::from_u32(4) + x * F::from_u32(5) + F::from_u32(6);
let evals: Vec<_> = coset.iter().flat_map(|&x| vec![f1(x), f2(x)]).collect();
let evals_mat = RowMajorMatrix::new(evals, 2);
let point = EF4::from_u32(77);
let result = interpolate_coset(&evals_mat, shift, point);
let expected_f1 = f1(point);
let expected_f2 = f2(point);
assert_eq!(result[0], expected_f1);
assert_eq!(result[1], expected_f2);
}
#[test]
fn test_interpolate_subgroup_multiple_columns() {
type F = BabyBear;
type EF4 = BinomialExtensionField<BabyBear, 4>;
let f1 = |x: EF4| x * x + x * F::TWO + F::from_u32(3);
let f2 = |x: EF4| x * x * F::from_u32(4) + x * F::from_u32(5) + F::from_u32(6);
let subgroup_iter = EF4::two_adic_generator(3).powers().take(8);
let evals: Vec<_> = subgroup_iter.flat_map(|x| vec![f1(x), f2(x)]).collect();
let evals_mat = RowMajorMatrix::new(evals, 2);
let point = EF4::from_u32(77);
let result = interpolate_subgroup(&evals_mat, point);
let expected_f1 = f1(point);
let expected_f2 = f2(point);
assert_eq!(result, vec![expected_f1, expected_f2]);
}
}