use crate::field::Goldilocks;
pub fn multilinear_eval(evals: &[Goldilocks], point: &[Goldilocks]) -> Goldilocks {
debug_assert_eq!(
evals.len(),
1usize << point.len(),
"evals.len() must equal 2^point.len()"
);
debug_assert!(!evals.is_empty(), "evals must be non-empty");
if point.is_empty() {
return evals[0];
}
let mid = evals.len() / 2;
let vl = multilinear_eval(&evals[..mid], &point[1..]);
let vr = multilinear_eval(&evals[mid..], &point[1..]);
vl + point[0] * (vr - vl)
}
#[cfg(test)]
mod tests {
use super::*;
fn g(v: u64) -> Goldilocks {
Goldilocks::new(v)
}
#[test]
fn one_var_at_zero_returns_first_eval() {
let evals = [g(3), g(7)];
assert_eq!(multilinear_eval(&evals, &[g(0)]), g(3));
}
#[test]
fn one_var_at_one_returns_second_eval() {
let evals = [g(3), g(7)];
assert_eq!(multilinear_eval(&evals, &[g(1)]), g(7));
}
#[test]
fn one_var_at_midpoint_interpolates() {
let evals = [g(0), g(10)];
assert_eq!(multilinear_eval(&evals, &[g(5)]), g(50));
}
#[test]
fn two_var_at_origin() {
let evals = [g(10), g(20), g(30), g(40)];
assert_eq!(multilinear_eval(&evals, &[g(0), g(0)]), g(10));
}
#[test]
fn two_var_at_one_zero() {
let evals = [g(10), g(20), g(30), g(40)];
assert_eq!(multilinear_eval(&evals, &[g(1), g(0)]), g(30));
}
#[test]
fn two_var_at_zero_one() {
let evals = [g(10), g(20), g(30), g(40)];
assert_eq!(multilinear_eval(&evals, &[g(0), g(1)]), g(20));
}
#[test]
fn two_var_at_one_one() {
let evals = [g(10), g(20), g(30), g(40)];
assert_eq!(multilinear_eval(&evals, &[g(1), g(1)]), g(40));
}
#[test]
fn two_var_at_midpoint() {
let evals = [g(10), g(20), g(30), g(40)];
let result = multilinear_eval(&evals, &[g(0), g(2)]);
assert_eq!(result, g(30));
}
}