use crate::field::FiniteField;
use rand::RngCore;
use smallvec::{smallvec, SmallVec};
use std::{
fmt::Debug,
ops::{AddAssign, Index, IndexMut, MulAssign, SubAssign},
};
use subtle::{Choice, ConstantTimeEq};
pub fn lagrange_coefficient<F: FiniteField>(points: &[F], u: F, e: F) -> F {
lagrange_numerator(points, u, e) * lagrange_denominator(points, u)
}
pub fn lagrange_numerator<F: FiniteField>(points: &[F], u: F, e: F) -> F {
let mut numerator = F::ONE;
for point in points.iter() {
if *point == u {
continue;
}
numerator *= e - *point;
}
numerator
}
pub fn lagrange_denominator<F: FiniteField>(points: &[F], u: F) -> F {
lagrange_numerator(points, u, u).inverse()
}
#[derive(Clone, Eq)]
pub struct Polynomial<FE: FiniteField> {
pub constant: FE,
pub coefficients: SmallVec<[FE; 3]>,
}
impl<FE: FiniteField> Polynomial<FE> {
pub fn random(rng: &mut (impl RngCore + ?Sized), degree: usize) -> Self {
let constant = FE::random(rng);
Self {
constant,
coefficients: (0..degree).map(|_| FE::random(rng)).collect(),
}
}
pub fn zero() -> Self {
Self {
constant: FE::ZERO,
coefficients: Default::default(),
}
}
pub fn one() -> Self {
Self::constant(FE::ONE)
}
pub fn constant(c: FE) -> Self {
Self {
constant: c,
coefficients: Default::default(),
}
}
pub fn x() -> Self {
Polynomial {
constant: FE::ZERO,
coefficients: smallvec![FE::ONE],
}
}
pub fn degree(&self) -> usize {
self.coefficients.len()
- self
.coefficients
.iter()
.rev()
.take_while(|x| **x == FE::ZERO)
.count()
}
pub fn eval(&self, at: FE) -> FE {
let mut reversed = self.coefficients.iter().rev();
if let Some(head) = reversed.next() {
let mut acc = *head;
for coeff in reversed {
acc = acc * at + *coeff;
}
acc * at + self.constant
} else {
self.constant
}
}
pub fn divmod(&self, divisor: &Self) -> (Self, Self) {
let mut q = Self::zero();
let mut r = self.clone();
let d = divisor.degree();
while r != Self::zero() && r.degree() >= divisor.degree() {
let b = r.degree();
let mut t = Polynomial {
constant: FE::ZERO,
coefficients: smallvec![FE::ZERO; b.checked_sub(d).unwrap()],
};
t[b - d] = r[b] / divisor[d];
q += &t;
t *= divisor;
r -= &t;
}
(q, r)
}
pub fn interpolate(points: &[(FE, FE)]) -> Self {
assert!(!points.is_empty());
let mut out = Polynomial {
constant: FE::ZERO,
coefficients: smallvec![FE::ZERO; points.len() - 1],
};
for (j, (xj, yj)) in points.iter().enumerate() {
let mut l = Polynomial::one();
for (m, (xm, _)) in points.iter().enumerate() {
if m == j {
continue;
}
assert_ne!(*xm, *xj);
let delta_x = *xj - *xm;
let delta_x_inverse = delta_x.inverse();
l *= &Polynomial {
constant: -(*xm) * delta_x_inverse,
coefficients: smallvec![delta_x_inverse],
};
}
l *= *yj;
out += &l;
}
out
}
}
impl<'a, FE: FiniteField> AddAssign<&'a Polynomial<FE>> for Polynomial<FE> {
fn add_assign(&mut self, rhs: &'a Polynomial<FE>) {
self.coefficients.resize(
self.coefficients.len().max(rhs.coefficients.len()),
FE::ZERO,
);
self.constant += rhs.constant;
for (a, b) in self.coefficients.iter_mut().zip(rhs.coefficients.iter()) {
*a += *b;
}
}
}
impl<'a, FE: FiniteField> SubAssign<&'a Polynomial<FE>> for Polynomial<FE> {
fn sub_assign(&mut self, rhs: &'a Polynomial<FE>) {
self.coefficients.resize(
self.coefficients.len().max(rhs.coefficients.len()),
FE::ZERO,
);
self.constant -= rhs.constant;
for (a, b) in self.coefficients.iter_mut().zip(rhs.coefficients.iter()) {
*a -= *b;
}
}
}
impl<FE: FiniteField> MulAssign<FE> for Polynomial<FE> {
fn mul_assign(&mut self, rhs: FE) {
self.constant *= rhs;
for coeff in self.coefficients.iter_mut() {
*coeff *= rhs;
}
}
}
impl<FE: FiniteField> Index<usize> for Polynomial<FE> {
type Output = FE;
fn index(&self, index: usize) -> &Self::Output {
if index == 0 {
&self.constant
} else {
&self.coefficients[index - 1]
}
}
}
impl<FE: FiniteField> IndexMut<usize> for Polynomial<FE> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
if index == 0 {
&mut self.constant
} else {
&mut self.coefficients[index - 1]
}
}
}
impl<'a, FE: FiniteField> MulAssign<&'a Polynomial<FE>> for Polynomial<FE> {
fn mul_assign(&mut self, rhs: &'a Polynomial<FE>) {
let tmp = self.clone();
self.constant = FE::ZERO;
for x in self.coefficients.iter_mut() {
*x = FE::ZERO;
}
self.coefficients
.resize(tmp.degree() + rhs.degree(), FE::ZERO);
for i in 0..tmp.degree() + 1 {
for j in 0..rhs.degree() + 1 {
self[i + j] += tmp[i] * rhs[j];
}
}
}
}
impl<FE: FiniteField> PartialEq for Polynomial<FE> {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl<FE: FiniteField> ConstantTimeEq for Polynomial<FE> {
fn ct_eq(&self, other: &Self) -> Choice {
let mut out = self.constant.ct_eq(&other.constant);
for (a, b) in self
.coefficients
.iter()
.cloned()
.chain(std::iter::repeat(FE::ZERO))
.zip(
other
.coefficients
.iter()
.cloned()
.chain(std::iter::repeat(FE::ZERO)),
)
.take(self.coefficients.len().max(other.coefficients.len()))
{
out &= a.ct_eq(&b);
}
out
}
}
impl<FE: FiniteField> From<&[FE]> for Polynomial<FE> {
fn from(v: &[FE]) -> Self {
match v.len() {
0 => Self::zero(),
1 => Self::constant(v[0]),
_ => Self {
constant: v[0],
coefficients: SmallVec::from_slice(&v[1..]),
},
}
}
}
impl<FE: FiniteField> Debug for Polynomial<FE> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "P(X) = {:?}", self.constant)?;
for (i, coeff) in self.coefficients.iter().enumerate() {
if *coeff != FE::ZERO {
write!(f, " + {:?} X^{}", coeff, i + 1)?;
}
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct NewtonPolynomial<F: FiniteField> {
points: Vec<F>,
cache: Vec<F>,
}
impl<F: FiniteField> NewtonPolynomial<F> {
pub fn new(points: Vec<F>) -> Self {
let cache = compute_newton_points(&points);
Self { points, cache }
}
pub fn interpolate_in_place(&self, values: &mut [F]) {
assert!(values.len() <= self.points.len());
for j in 1..values.len() {
for i in (j..values.len()).rev() {
let coef_lower = values[i - 1];
let coef_upper = values[i];
let coef_diff = coef_upper - coef_lower;
let fraction = coef_diff * self.cache[j * values.len() + i];
values[i] = fraction;
}
}
}
pub fn basis_polynomial(&self, point: F, polynomial: &mut Vec<F>) {
let mut product = F::ONE;
polynomial.push(product);
for i in 0..self.points.len() - 1 {
product *= point - self.points[i];
polynomial.push(product);
}
}
pub fn eval_with_basis_polynomial(&self, polynomial: &[F], coefficients: &[F]) -> F {
let mut result = F::ZERO;
for (x, y) in coefficients.iter().zip(polynomial.iter()) {
result += *x * *y;
}
result
}
pub fn eval(&self, coefficients: &[F], point: F) -> F {
assert!(coefficients.len() <= self.points.len());
let mut result = F::ZERO;
let mut product = F::ONE;
for i in 0..coefficients.len() - 1 {
result += coefficients[i] * product;
product *= point - self.points[i];
}
result + *coefficients.last().unwrap() * product
}
}
fn compute_newton_points<F: FiniteField>(points: &[F]) -> Vec<F> {
let length = points.len();
let mut indices: Vec<(usize, usize)> = (0..length).map(|index| (index, index)).collect();
let mut cache = vec![F::ZERO; length * length];
for j in 1..points.len() {
for i in (j..points.len()).rev() {
let index_lower = indices[i - 1].0;
let index_upper = indices[i].1;
let point_lower = points[index_lower];
let point_upper = points[index_upper];
let point_diff = point_upper - point_lower;
let point_diff_inverse = point_diff.inverse();
indices[i] = (index_lower, index_upper);
cache[j * length + i] = point_diff_inverse;
}
}
cache
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{AesRng, Block};
use rand::Rng;
use rand_core::SeedableRng;
#[test]
fn test_degree() {
fn f<FE: FiniteField>() {
assert_eq!(Polynomial::<FE>::zero().degree(), 0);
assert_eq!(Polynomial::<FE>::one().degree(), 0);
assert_eq!(Polynomial::<FE>::x().degree(), 1);
assert_eq!(
(Polynomial {
constant: FE::ZERO,
coefficients: smallvec![FE::ZERO, FE::ZERO],
})
.degree(),
0
);
assert_eq!(
(Polynomial {
constant: FE::ZERO,
coefficients: smallvec![
FE::ZERO,
FE::ZERO,
FE::ONE,
FE::ZERO,
FE::ZERO,
FE::ZERO
],
})
.degree(),
3
);
}
call_with_finite_field!(f);
}
#[test]
fn test_addition() {
fn f<FE: FiniteField>() {
let mut rng = AesRng::from_seed(Block::default());
for _ in 0..100 {
let a = Polynomial::random(&mut rng, 10);
let b = Polynomial::random(&mut rng, 10);
let mut product = a.clone();
product += &b;
for _ in 0..100 {
let x = FE::random(&mut rng);
assert_eq!(product.eval(x), a.eval(x) + b.eval(x));
}
}
}
call_with_finite_field!(f);
}
#[test]
fn test_subtraction() {
fn f<FE: FiniteField>() {
let mut rng = AesRng::from_seed(Block::default());
for _ in 0..100 {
let a = Polynomial::random(&mut rng, 10);
let b = Polynomial::random(&mut rng, 10);
let mut product = a.clone();
product -= &b;
for _ in 0..100 {
let x = FE::random(&mut rng);
assert_eq!(product.eval(x), a.eval(x) - b.eval(x));
}
}
}
call_with_finite_field!(f);
}
#[test]
fn test_multiplication() {
fn f<FE: FiniteField>() {
let mut rng = AesRng::from_seed(Block::default());
for _ in 0..100 {
let a = Polynomial::random(&mut rng, 10);
let b = Polynomial::random(&mut rng, 10);
let mut product = a.clone();
product *= &b;
for _ in 0..100 {
let x = FE::random(&mut rng);
assert_eq!(product.eval(x), a.eval(x) * b.eval(x));
}
}
}
call_with_finite_field!(f);
}
#[test]
fn test_scalar_multiplication() {
fn f<FE: FiniteField>() {
let mut rng = AesRng::from_seed(Block::default());
for _ in 0..100 {
let a = Polynomial::random(&mut rng, 10);
let c = FE::random(&mut rng);
let mut product = a.clone();
product *= c;
for _ in 0..100 {
let x = FE::random(&mut rng);
assert_eq!(product.eval(x), a.eval(x) * c);
}
}
}
call_with_finite_field!(f);
}
#[test]
fn test_interpolation() {
fn f<FE: FiniteField>() {
let mut rng = AesRng::from_seed(Block::default());
{
let poly = Polynomial::interpolate(&[(FE::ZERO, FE::ZERO), (FE::ONE, FE::ONE)]);
assert_eq!(poly.eval(FE::ZERO), FE::ZERO);
assert_eq!(poly.eval(FE::ONE), FE::ONE);
}
{
let poly = Polynomial::interpolate(&[(FE::ZERO, FE::ONE)]);
assert_eq!(poly.eval(FE::ZERO), FE::ONE);
}
for _ in 0..100 {
let n_points = 5;
let mut points = Vec::new();
for _ in 0..n_points {
let x = FE::random(&mut rng);
let y = FE::random(&mut rng);
points.push((x, y));
}
let p = Polynomial::interpolate(&points);
for (x, y) in points {
assert_eq!(p.eval(x), y);
}
}
}
call_with_big_finite_fields!(f);
}
#[test]
fn test_divmod() {
fn f<FE: FiniteField>() {
let mut rng = AesRng::from_seed(Block::default());
for _ in 0..1000 {
let degree1 = rng.gen_range(0usize..20usize);
let degree2 = rng.gen_range(0usize..20usize);
let a = Polynomial::<FE>::random(&mut rng, degree1);
let mut b = Polynomial::<FE>::random(&mut rng, degree2);
if b == Polynomial::<FE>::zero() {
continue;
}
let (q, r) = a.divmod(&b);
assert!(
r == Polynomial::zero() || r.degree() < b.degree(),
"{:?} {:?}",
r,
b
);
b *= &q;
b += &r;
assert_eq!(a, b);
}
}
call_with_finite_field!(f);
}
#[test]
fn test_newton_polynomial() {
fn f<FE: FiniteField>() {
let mut rng = AesRng::from_seed(Block::default());
let poly = Polynomial::random(&mut rng, 10);
let xs: Vec<_> = (0..10).map(|_| FE::random(&mut rng)).collect();
let ys: Vec<FE> = xs.iter().map(|&x| poly.eval(x)).collect();
let npoly = NewtonPolynomial::new(xs.clone());
let mut coeffs = ys.clone();
npoly.interpolate_in_place(&mut coeffs);
let ys_: Vec<FE> = xs.iter().map(|&x| npoly.eval(&coeffs, x)).collect();
assert_eq!(ys, ys_);
}
call_with_big_finite_fields!(f);
}
}