use thiserror::Error;
use crate::{gf2_192::Gf2_192, Gf2_192Error};
pub struct CoefficientsByteRepr<'a> {
pub coeff0: [u8; 24],
pub more_coeffs: &'a [u8],
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Gf2_192Poly {
coefficients: Vec<Gf2_192>,
degree: usize,
}
#[derive(Error, PartialEq, Eq, Debug, Clone)]
pub enum Gf2_192PolyError {
#[error("`Gf2_192Poly::interpolation`: `points.len() != values.len()`")]
InterpolatePointsAndValuesLengthDiffer,
}
impl Gf2_192Poly {
pub fn interpolate(
points: &[u8],
values: &[Gf2_192],
value_at_zero: Gf2_192,
) -> Result<Gf2_192Poly, Gf2_192PolyError> {
if points.len() != values.len() {
return Err(Gf2_192PolyError::InterpolatePointsAndValuesLengthDiffer);
}
let result_degree = values.len();
let mut result = Gf2_192Poly::make_constant(result_degree, 0);
let mut vanishing_poly = Gf2_192Poly::make_constant(result_degree, 1);
for i in 0..points.len() {
let mut t = result.evaluate(points[i]);
let mut s = vanishing_poly.evaluate(points[i]);
t = t + values[i];
s = Gf2_192::invert(s);
t = t * s;
result.add_monic_times_constant(vanishing_poly.clone(), t);
vanishing_poly.multiply_by_linear_binomial(points[i] as i8);
}
let mut t = result.coefficients[0]; let mut s = vanishing_poly.coefficients[0];
t = t + value_at_zero;
s = Gf2_192::invert(s);
t = t * s;
result.add_monic_times_constant(vanishing_poly, t);
Ok(result)
}
pub fn evaluate(&self, x: u8) -> Gf2_192 {
let mut res = self.coefficients[self.degree];
if self.degree > 0 {
for d in (0..=(self.degree - 1)).rev() {
res = Gf2_192::mul_by_i8(res, x as i8);
res = res + self.coefficients[d];
}
}
res
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut res: Vec<_> = std::iter::repeat(0).take(self.degree * 24).collect();
for i in 1..=self.degree {
#[allow(clippy::unwrap_used)]
self.coefficients[i]
.to_i8_slice(&mut res, (i - 1) * 24)
.unwrap();
}
res.into_iter().map(|x| x as u8).collect()
}
fn add_monic_times_constant(&mut self, p: Gf2_192Poly, r: Gf2_192) {
let mut _t = Gf2_192::new();
for i in 0..p.degree {
_t = p.coefficients[i] * r;
self.coefficients[i] = self.coefficients[i] + _t;
}
self.degree = p.degree;
self.coefficients[self.degree] = r;
}
fn multiply_by_linear_binomial(&mut self, r: i8) {
self.degree += 1;
self.coefficients[self.degree] = Gf2_192::from(1);
for i in (1..self.degree).rev() {
self.coefficients[i] = Gf2_192::mul_by_i8(self.coefficients[i], r);
self.coefficients[i] = self.coefficients[i] + self.coefficients[i - 1];
}
self.coefficients[0] = Gf2_192::mul_by_i8(self.coefficients[0], r);
}
fn make_constant(max_degree: usize, constant_term: i32) -> Gf2_192Poly {
let mut coefficients: Vec<_> = std::iter::repeat_with(Gf2_192::new)
.take(max_degree + 1)
.collect();
coefficients[0] = Gf2_192::from(constant_term);
let degree = 0;
Gf2_192Poly {
degree,
coefficients,
}
}
}
impl<'a> TryFrom<CoefficientsByteRepr<'a>> for Gf2_192Poly {
type Error = Gf2_192Error;
fn try_from(c: CoefficientsByteRepr<'a>) -> Result<Self, Self::Error> {
let degree = c.more_coeffs.len() / 24;
let mut coefficients = Vec::with_capacity(degree + 1);
coefficients.push(Gf2_192::from(c.coeff0));
for i in 1..=degree {
coefficients.push(Gf2_192::try_from(&c.more_coeffs[(i - 1) * 24..])?);
}
Ok(Gf2_192Poly {
degree,
coefficients,
})
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use rand::{thread_rng, Rng};
#[test]
fn test_interpolation() {
let mut p = Gf2_192Poly::interpolate(&[], &[], Gf2_192::new()).unwrap();
assert!(p.evaluate(0).is_zero());
assert!(p.evaluate(5).is_zero());
let val_17 = Gf2_192::from(17);
p = Gf2_192Poly::interpolate(&[], &[], val_17).unwrap();
assert_eq!(p.evaluate(0), val_17);
assert_eq!(p.evaluate(5), val_17);
let mut rng = thread_rng();
for len in 1..100 {
let mut points: Vec<_> = std::iter::repeat(0).take(len).collect();
let mut j = 0;
while j < points.len() {
let b: u8 = rng.gen();
if b != 0 && !points.contains(&b) {
points[j] = b;
j += 1;
}
}
let mut values: Vec<_> = Vec::with_capacity(len);
for _ in 0..len {
let b: [i8; 24] = rng.gen();
values.push(Gf2_192::from(b));
}
let b: [i8; 24] = rng.gen();
let value_at_zero = Gf2_192::from(b);
let res = Gf2_192Poly::interpolate(&points, &values, value_at_zero).unwrap();
for i in 0..points.len() {
assert_eq!(res.evaluate(points[i]), values[i]);
}
assert_eq!(res.evaluate(0), value_at_zero);
}
}
}