use cyb_lens_core::Field;
pub fn tensor_reduce<F: Field>(evals: &[F], challenge: F) -> Vec<F> {
let half = evals.len() / 2;
let mut result = Vec::with_capacity(half);
for i in 0..half {
let even = evals[2 * i];
let odd = evals[2 * i + 1];
result.push(even + challenge * (odd - even));
}
result
}
pub fn evaluate_small<F: Field>(evals: &[F], challenges: &[F]) -> F {
let mut current = evals.to_vec();
for &c in challenges {
current = tensor_reduce(¤t, c);
}
assert_eq!(current.len(), 1, "challenges should reduce to single value");
current[0]
}
#[cfg(test)]
mod tests {
use super::*;
use nebu::Goldilocks;
#[test]
fn tensor_reduce_halves() {
let evals: Vec<Goldilocks> = (0..8).map(|i| Goldilocks::new(i + 1)).collect();
let reduced = tensor_reduce(&evals, Goldilocks::ZERO);
assert_eq!(reduced.len(), 4);
assert_eq!(reduced[0], Goldilocks::new(1));
assert_eq!(reduced[1], Goldilocks::new(3));
assert_eq!(reduced[2], Goldilocks::new(5));
assert_eq!(reduced[3], Goldilocks::new(7));
}
#[test]
fn tensor_reduce_at_one() {
let evals: Vec<Goldilocks> = (0..8).map(|i| Goldilocks::new(i + 1)).collect();
let reduced = tensor_reduce(&evals, Goldilocks::ONE);
assert_eq!(reduced.len(), 4);
assert_eq!(reduced[0], Goldilocks::new(2));
assert_eq!(reduced[1], Goldilocks::new(4));
assert_eq!(reduced[2], Goldilocks::new(6));
assert_eq!(reduced[3], Goldilocks::new(8));
}
#[test]
fn evaluate_small_single() {
let evals = vec![Goldilocks::new(42)];
let result = evaluate_small(&evals, &[]);
assert_eq!(result, Goldilocks::new(42));
}
#[test]
fn evaluate_small_matches_multilinear() {
let evals = vec![
Goldilocks::new(1),
Goldilocks::new(2),
Goldilocks::new(3),
Goldilocks::new(4),
];
let r = evaluate_small(&evals, &[Goldilocks::ZERO, Goldilocks::ZERO]);
assert_eq!(r, Goldilocks::new(1));
let r = evaluate_small(&evals, &[Goldilocks::ONE, Goldilocks::ONE]);
assert_eq!(r, Goldilocks::new(4));
}
}