use alloc::vec::Vec;
use rand::distributions::{Distribution, Uniform};
use super::field::GF256;
use super::share::Share;
pub fn interpolate(shares: &[Share]) -> Vec<u8> {
(0..shares[0].y.len())
.map(|s| {
shares
.iter()
.map(|s_i| {
shares
.iter()
.filter(|s_j| s_j.x != s_i.x)
.map(|s_j| s_j.x.clone() / (s_j.x.clone() - s_i.x.clone()))
.product::<GF256>()
* s_i.y[s].clone()
})
.sum::<GF256>()
.0
})
.collect()
}
pub fn random_polynomial<R: rand::Rng>(s: GF256, k: u8, rng: &mut R) -> Vec<GF256> {
let k = k as usize;
let mut poly = Vec::with_capacity(k);
let between = Uniform::new_inclusive(1, 255);
for _ in 1..k {
poly.push(GF256(between.sample(rng)));
}
poly.push(s);
poly
}
pub fn get_evaluator(polys: Vec<Vec<GF256>>) -> impl Iterator<Item = Share> {
(1..=u8::max_value()).map(GF256).map(move |x| Share {
x: x.clone(),
y: polys
.iter()
.map(|p| {
p.iter()
.fold(GF256(0), |acc, c| acc * x.clone() + c.clone())
})
.collect(),
})
}
#[cfg(test)]
mod tests {
use super::{get_evaluator, interpolate, random_polynomial, Share, GF256};
use alloc::{vec, vec::Vec};
use rand_chacha::rand_core::SeedableRng;
#[test]
fn random_polynomial_works() {
let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]);
let poly = random_polynomial(GF256(1), 3, &mut rng);
assert_eq!(poly.len(), 3);
assert_eq!(poly[2], GF256(1));
}
#[test]
fn evaluator_works() {
let iter = get_evaluator(vec![vec![GF256(3), GF256(2), GF256(5)]]);
let values: Vec<_> = iter.take(2).map(|s| (s.x.clone(), s.y.clone())).collect();
assert_eq!(
values,
vec![(GF256(1), vec![GF256(4)]), (GF256(2), vec![GF256(13)])]
);
}
#[test]
fn interpolate_works() {
let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]);
let poly = random_polynomial(GF256(185), 10, &mut rng);
let iter = get_evaluator(vec![poly]);
let shares: Vec<Share> = iter.take(10).collect();
let root = interpolate(&shares);
assert_eq!(root, vec![185]);
}
}