use super::linear_form::LinearFormHandle;
use crate::field::Goldilocks4;
pub(crate) const ETA: usize = 2;
pub(crate) struct OodEvader {
pub(crate) k: usize,
}
impl OodEvader {
pub(crate) fn new(k: usize) -> Self {
Self { k }
}
pub(crate) fn apply(&self, msg: &[Goldilocks4], seeds: &[Goldilocks4]) -> Vec<Goldilocks4> {
assert_eq!(msg.len(), self.k);
seeds
.iter()
.map(|seed| {
let mut acc = Goldilocks4::ZERO;
for c in msg.iter().rev() {
acc = acc * *seed + *c;
}
acc
})
.collect()
}
pub(crate) fn expanded_constraint(&self, seeds: &[Goldilocks4]) -> Vec<Vec<Goldilocks4>> {
seeds
.iter()
.map(|seed| {
let mut row = Vec::with_capacity(self.k);
let mut p = Goldilocks4::ONE;
for _ in 0..self.k {
row.push(p);
p *= *seed;
}
row
})
.collect()
}
}
pub(crate) struct OodLinearFormHandle {
pub(crate) k: usize,
seed: Goldilocks4,
}
impl OodLinearFormHandle {
pub(crate) fn new(k: usize, seed: Goldilocks4) -> Self {
Self { k, seed }
}
}
impl LinearFormHandle for OodLinearFormHandle {
type Alphabet = Goldilocks4;
fn form_size(&self) -> usize {
self.k
}
fn folded_form(&self, rand: &[Self::Alphabet]) -> Vec<Self::Alphabet> {
assert!(
self.k.is_power_of_two(),
"OodLinearFormHandle: k must be a power of two"
);
assert!(
rand.len() <= self.k.ilog2() as usize,
"OodLinearFormHandle: too many fold challenges"
);
let mut values: Vec<Goldilocks4> = {
let mut v = Vec::with_capacity(self.k);
let mut p = Goldilocks4::ONE;
for _ in 0..self.k {
v.push(p);
p *= self.seed;
}
v
};
for &w in rand {
let half = values.len() / 2;
values = (0..half)
.map(|k| values[k] + (values[k + half] - values[k]) * w)
.collect();
}
values
}
}
pub(crate) struct OodEvaderHandle {
pub(crate) k: usize,
}
impl OodEvaderHandle {
pub(crate) fn new(k: usize) -> Self {
Self { k }
}
pub(crate) fn zero_evader_handles(&self, seeds: &[Goldilocks4]) -> Vec<OodLinearFormHandle> {
seeds
.iter()
.map(|s| OodLinearFormHandle::new(self.k, *s))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::Goldilocks;
fn g4(v: u64) -> Goldilocks4 {
Goldilocks4::new([
Goldilocks::new(v),
Goldilocks::new(0),
Goldilocks::new(0),
Goldilocks::new(0),
])
}
#[test]
fn ood_apply_matches_dot_product_with_constraint() {
let m: Vec<Goldilocks4> = (0..16).map(|i| g4(i as u64)).collect();
let seeds = [g4(7), g4(11)];
let evader = OodEvader::new(16);
let applied = evader.apply(&m, &seeds);
let constraints = evader.expanded_constraint(&seeds);
assert_eq!(applied.len(), 2);
assert_eq!(constraints.len(), 2);
for (answer, constraint) in applied.iter().zip(constraints.iter()) {
let dot: Goldilocks4 = m
.iter()
.zip(constraint)
.map(|(a, b)| *a * *b)
.fold(Goldilocks4::ZERO, |acc, x| acc + x);
assert_eq!(*answer, dot);
}
}
#[test]
fn ood_apply_evaluates_polynomial() {
let m = vec![g4(1), g4(2), g4(3)];
let seeds = [g4(5), g4(2)];
let evader = OodEvader::new(3);
assert_eq!(evader.apply(&m, &seeds), vec![g4(86), g4(17)]);
}
#[test]
fn handle_no_fold_returns_powers() {
let alpha = g4(5);
let h = OodEvaderHandle::new(8);
let handles = h.zero_evader_handles(&[alpha]);
let form = handles[0].folded_form(&[]);
let expected: Vec<Goldilocks4> = (0..8).map(|i| alpha.pow(i as u64)).collect();
assert_eq!(form, expected);
}
#[test]
fn handle_one_fold_matches_manual() {
let alpha = g4(3);
let w = g4(11);
let h = OodEvaderHandle::new(8);
let handles = h.zero_evader_handles(&[alpha]);
let powers: Vec<Goldilocks4> = (0..8).map(|i| alpha.pow(i as u64)).collect();
let half = 4;
let manual: Vec<Goldilocks4> = (0..half)
.map(|k| powers[k] + (powers[k + half] - powers[k]) * w)
.collect();
let got = handles[0].folded_form(&[w]);
assert_eq!(got, manual);
}
#[test]
fn handle_two_seeds_produces_two_handles() {
let h = OodEvaderHandle::new(8);
let handles = h.zero_evader_handles(&[g4(3), g4(7)]);
assert_eq!(handles.len(), 2);
}
}