use nebu::Goldilocks;
pub fn eq_evals(r: &[Goldilocks]) -> Vec<Goldilocks> {
let mut table = vec![Goldilocks::ONE];
for &ri in r {
let n = table.len();
let one_minus_ri = Goldilocks::ONE - ri;
let mut new_table = vec![Goldilocks::ZERO; 2 * n];
for m in 0..n {
new_table[m] = table[m] * one_minus_ri;
new_table[m + n] = table[m] * ri;
}
table = new_table;
}
table
}
pub fn fold_inplace(table: &mut Vec<Goldilocks>, r: Goldilocks) {
let sz = table.len();
assert!(sz >= 2 && sz.is_power_of_two(), "table must be power-of-2 size ≥ 2");
let half = sz / 2;
let one_minus_r = Goldilocks::ONE - r;
for m in 0..half {
let lo = table[m];
let hi = table[m + half];
table[m] = one_minus_r * lo + r * hi;
}
table.truncate(half);
}
pub fn evaluate_multilinear(evals: &[Goldilocks], point: &[Goldilocks]) -> Goldilocks {
debug_assert_eq!(evals.len(), 1 << point.len());
let mut table = evals.to_vec();
for &r in point {
fold_inplace(&mut table, r);
}
table[0]
}
#[inline]
pub fn linear_ext(v0: Goldilocks, v1: Goldilocks, t: Goldilocks) -> Goldilocks {
(Goldilocks::ONE - t) * v0 + t * v1
}
pub fn evals_to_coeffs(evals: &[Goldilocks]) -> Vec<Goldilocks> {
if evals.is_empty() {
return vec![];
}
let d = evals.len() - 1;
let mut coeffs = vec![Goldilocks::ZERO; d + 1];
for (i, &eval_i) in evals.iter().enumerate() {
let mut li = vec![Goldilocks::ONE]; let mut denom = Goldilocks::ONE;
for j in 0..=d {
if j == i {
continue;
}
let mut new_li = vec![Goldilocks::ZERO; li.len() + 1];
let j_field = Goldilocks::new(j as u64);
for k in 0..li.len() {
new_li[k + 1] += li[k];
new_li[k] -= j_field * li[k];
}
li = new_li;
let diff = if i > j {
Goldilocks::new((i - j) as u64)
} else {
Goldilocks::ZERO - Goldilocks::new((j - i) as u64)
};
denom *= diff;
}
let denom_inv = denom.inv();
let scale = eval_i * denom_inv;
for k in 0..=d {
coeffs[k] += scale * li[k];
}
}
coeffs
}
pub fn eval_poly(coeffs: &[Goldilocks], x: Goldilocks) -> Goldilocks {
let mut r = Goldilocks::ZERO;
for &c in coeffs.iter().rev() {
r = r * x + c;
}
r
}
pub fn pad_to_power_of_two(table: &mut Vec<Goldilocks>, target: usize) {
let n = target.next_power_of_two().max(1);
table.resize(n, Goldilocks::ZERO);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn eq_evals_sums_to_one() {
let r = vec![
Goldilocks::new(3),
Goldilocks::new(7),
Goldilocks::new(11),
];
let evals = eq_evals(&r);
let sum = evals.iter().copied().fold(Goldilocks::ZERO, |a, b| a + b);
assert_eq!(sum, Goldilocks::ONE);
}
#[test]
fn eq_evals_binary_point() {
let r = vec![Goldilocks::new(5), Goldilocks::new(9)];
let evals = eq_evals(&r);
let expected = (Goldilocks::ONE - r[0]) * (Goldilocks::ONE - r[1]);
assert_eq!(evals[0], expected);
}
#[test]
fn fold_halves_table() {
let mut t = vec![
Goldilocks::new(1),
Goldilocks::new(2),
Goldilocks::new(3),
Goldilocks::new(4),
];
let r = Goldilocks::new(2);
fold_inplace(&mut t, r);
assert_eq!(t.len(), 2);
let expected0 = (Goldilocks::ONE - r) * Goldilocks::new(1) + r * Goldilocks::new(3);
let expected1 = (Goldilocks::ONE - r) * Goldilocks::new(2) + r * Goldilocks::new(4);
assert_eq!(t[0], expected0);
assert_eq!(t[1], expected1);
}
#[test]
fn evaluate_multilinear_at_binary() {
let evals = vec![
Goldilocks::new(10),
Goldilocks::new(20),
Goldilocks::new(30),
Goldilocks::new(40),
];
let v = evaluate_multilinear(
&evals,
&[Goldilocks::ZERO, Goldilocks::ONE],
);
assert_eq!(v, Goldilocks::new(20));
}
#[test]
fn evals_to_coeffs_roundtrip() {
let evals = vec![Goldilocks::new(1), Goldilocks::new(6), Goldilocks::new(17)];
let coeffs = evals_to_coeffs(&evals);
assert_eq!(coeffs[0], Goldilocks::new(1));
assert_eq!(coeffs[1], Goldilocks::new(2));
assert_eq!(coeffs[2], Goldilocks::new(3));
}
#[test]
fn evals_to_coeffs_eval_matches() {
let evals = vec![
Goldilocks::new(5),
Goldilocks::new(11),
Goldilocks::new(23),
Goldilocks::new(41),
];
let coeffs = evals_to_coeffs(&evals);
for (i, &e) in evals.iter().enumerate() {
let v = eval_poly(&coeffs, Goldilocks::new(i as u64));
assert_eq!(v, e);
}
}
}