use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct FieldElement(pub Vec<u8>);
impl FieldElement {
pub fn new(bytes: Vec<u8>) -> Self {
Self(bytes)
}
}
pub fn lagrange_basis_at(
xs: &[u64],
i: usize,
x: u64,
op_eval: &impl Fn(i128) -> i128,
op_mul: &impl Fn(i128, i128) -> i128,
op_div: &impl Fn(i128, i128) -> i128,
) -> i128 {
let xi = xs[i] as i128;
let mut result = 1i128;
for (j, &xj) in xs.iter().enumerate() {
if j == i {
continue;
}
let xj_i128 = xj as i128;
let x_i128 = x as i128;
let numerator = op_eval(x_i128 - xj_i128);
let denominator = op_eval(xi - xj_i128);
result = op_mul(result, op_div(numerator, denominator));
}
result
}
pub fn interpolate_at(
points: &[(u64, FieldElement)],
target_x: u64,
op_eval: &impl Fn(i128) -> i128,
op_mul: &impl Fn(i128, i128) -> i128,
op_add: &impl Fn(i128, i128) -> i128,
op_div: &impl Fn(i128, i128) -> i128,
) -> FieldElement {
let xs: Vec<u64> = points.iter().map(|(x, _)| *x).collect();
let mut result = 0i128;
for (i, (_, y)) in points.iter().enumerate() {
let lambda = lagrange_basis_at(&xs, i, target_x, op_eval, op_mul, op_div);
let y_val = i128::from_be_bytes(y.0[..16].try_into().unwrap_or([0u8; 16]));
result = op_add(result, op_mul(lambda, y_val));
}
FieldElement::new(result.to_be_bytes().to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
fn id(x: i128) -> i128 {
x
}
fn mul(a: i128, b: i128) -> i128 {
a * b
}
fn add(a: i128, b: i128) -> i128 {
a + b
}
fn div(a: i128, b: i128) -> i128 {
if b == 0 {
panic!("division by zero");
}
a / b
}
#[test]
fn lagrange_basis_two_points() {
let xs = vec![1, 2];
let result = lagrange_basis_at(&xs, 0, 0, &id, &mul, &div);
assert_eq!(result, 2);
}
#[test]
fn interpolate_recovers_secret() {
let points = vec![
(1u64, FieldElement::new(5i128.to_be_bytes().to_vec())),
(2u64, FieldElement::new(7i128.to_be_bytes().to_vec())),
];
let result = interpolate_at(&points, 0, &id, &mul, &add, &div);
let recovered = i128::from_be_bytes(result.0[..16].try_into().unwrap());
assert_eq!(recovered, 3);
}
}