use super::goldilocks::{gl_add, gl_mul, gl_sub};
pub fn multilinear_eval(evals: &[u64], point: &[u64]) -> u64 {
let len = evals.len();
debug_assert!(len.is_power_of_two());
if len == 1 {
return evals[0];
}
let mid = len / 2;
let x = point[0];
let vl = multilinear_eval(&evals[..mid], &point[1..]);
let vr = multilinear_eval(&evals[mid..], &point[1..]);
gl_add(vl, gl_mul(x, gl_sub(vr, vl)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::goldilocks::{gl_inv, gl_mul, P};
fn canonicalize(x: u64) -> u64 {
if x >= P {
x - P
} else {
x
}
}
#[test]
fn eval_length_1() {
assert_eq!(multilinear_eval(&[42u64], &[]), 42);
assert_eq!(multilinear_eval(&[0u64], &[]), 0);
}
#[test]
fn eval_at_zero_returns_first() {
let a = 17u64;
let b = 99u64;
let result = multilinear_eval(&[a, b], &[0u64]);
assert_eq!(result, a);
}
#[test]
fn eval_at_one_returns_second() {
let a = 17u64;
let b = 99u64;
let result = multilinear_eval(&[a, b], &[1u64]);
assert_eq!(result, b);
}
#[test]
fn eval_at_half_is_midpoint() {
let a = 10u64;
let b = 20u64;
let inv2 = gl_inv(2);
let expected = canonicalize(gl_mul(gl_add(a, b), inv2));
let result = canonicalize(multilinear_eval(&[a, b], &[inv2]));
assert_eq!(result, expected);
}
#[test]
fn eval_two_variables_corners() {
let (a, b, c, d) = (1u64, 2, 3, 4);
let evals = [a, b, c, d];
assert_eq!(multilinear_eval(&evals, &[0, 0]), a);
assert_eq!(multilinear_eval(&evals, &[0, 1]), b);
assert_eq!(multilinear_eval(&evals, &[1, 0]), c);
assert_eq!(multilinear_eval(&evals, &[1, 1]), d);
}
#[test]
fn eval_two_variables_arbitrary_point() {
let (a, b, c, d) = (1u64, 2, 3, 4);
let evals = [a, b, c, d];
let x0 = 2u64;
let x1 = 3u64;
let result = canonicalize(multilinear_eval(&evals, &[x0, x1]));
use crate::field::goldilocks::gl_sub;
let one_minus_x0 = gl_sub(1, x0);
let one_minus_x1 = gl_sub(1, x1);
let t_a = gl_mul(gl_mul(a, one_minus_x0), one_minus_x1);
let t_b = gl_mul(gl_mul(b, one_minus_x0), x1);
let t_c = gl_mul(gl_mul(c, x0), one_minus_x1);
let t_d = gl_mul(gl_mul(d, x0), x1);
let expected = canonicalize(gl_add(gl_add(t_a, t_b), gl_add(t_c, t_d)));
assert_eq!(result, expected);
}
#[test]
fn eval_size_8_corners() {
let evals: Vec<u64> = (0..8).map(|i| (i as u64 + 1) * 7).collect();
let corners: &[[u64; 3]] = &[
[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1],
];
for (i, pt) in corners.iter().enumerate() {
let result = canonicalize(multilinear_eval(&evals, pt));
assert_eq!(result, evals[i], "corner {i} mismatch");
}
}
}