use alloc::{vec, vec::Vec};
use core::ops::{Add, AddAssign, Mul, MulAssign, SubAssign};
use core::{cmp::PartialEq, iter::Product};
use num_traits::{Inv, One, Zero};
#[expect(clippy::missing_panics_doc)]
#[cfg_attr(not(test), expect(dead_code))]
pub fn interpolate_uni_poly<F>(polynomial: &[F], x: F) -> F
where
F: Copy
+ Inv<Output = Option<F>>
+ One
+ Zero
+ AddAssign
+ Mul<Output = F>
+ MulAssign
+ SubAssign
+ PartialEq,
{
if polynomial.is_empty() {
return F::zero();
}
let degree = polynomial.len() - 1;
let mut factorials: Vec<F> = Vec::with_capacity(degree + 1);
let mut factorial = F::one();
let mut i = F::zero();
for eval in polynomial {
factorials.push(factorial);
if i == x {
return *eval;
}
i += F::one();
factorial *= i;
}
let mut sum = F::zero();
let mut product = F::one();
let mut x_minus_i = x;
for i in 0..=degree {
let new_term = polynomial[i]
* (factorials[i] * factorials[degree - i] * x_minus_i)
.inv()
.expect(
"Inverse computation failed unexpectedly. This should not happen as `x != i`.",
);
if (degree - i).is_multiple_of(2) {
sum += new_term;
} else {
sum -= new_term;
}
product *= x_minus_i;
x_minus_i -= F::one();
}
sum * product
}
#[expect(
clippy::missing_panics_doc,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap
)]
pub fn interpolate_evaluations_to_reverse_coefficients<S>(evals: &[S]) -> Vec<S>
where
S: Zero
+ Copy
+ From<i32>
+ Mul<Output = S>
+ Add<Output = S>
+ Inv<Output = Option<S>>
+ Product,
{
assert!(i32::try_from(evals.len()).is_ok());
let n = evals.len().max(1) - 1;
evals
.iter()
.enumerate()
.map(|(idx, &eval_i)| {
let i = idx as i32;
let mut scaled_lagrange_basis = vec![S::zero(); n + 1];
scaled_lagrange_basis[0] = (i - n as i32..0)
.chain(1..=i)
.map(S::from)
.product::<S>()
.inv()
.expect("Product will never be zero because the terms being multiplied are non-zero by construction.")
* eval_i;
for neg_j in (-(n as i32)..-i).chain(1 - i..=0).map(S::from) {
for k in (0..n).rev() {
scaled_lagrange_basis[k + 1] =
scaled_lagrange_basis[k + 1] + neg_j * scaled_lagrange_basis[k];
}
}
scaled_lagrange_basis
})
.reduce(|mut acc, b| {
acc.iter_mut().zip(b).for_each(|(a, b)| *a = *a + b);
acc
})
.unwrap_or(vec![])
}