diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bf1469f4..019ac6842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ ### Bugfixes +- [\#915](https://github.com/arkworks-rs/algebra/pull/915) (`ark-poly`) Added `is_valid_coefficients_vec` to `SparsePolynomial` to remove duplicates and zero coefficients before polynomial creation. + ## v0.5.0 - [\#772](https://github.com/arkworks-rs/algebra/pull/772) (`ark-ff`) Implementation of `mul` method for `BigInteger`. diff --git a/poly/src/polynomial/univariate/sparse.rs b/poly/src/polynomial/univariate/sparse.rs index 8d683d210..25459f861 100644 --- a/poly/src/polynomial/univariate/sparse.rs +++ b/poly/src/polynomial/univariate/sparse.rs @@ -229,7 +229,34 @@ impl Zero for SparsePolynomial { } } +#[derive(Debug)] +pub enum CoeffVecError { + ZeroCoefficient, + DuplicateDegree, +} + impl SparsePolynomial { + // Function to validate that the input coefficient vector is simplified + pub fn is_valid_coefficients_vec(coeffs: &mut [(usize, F)]) -> Result<(), CoeffVecError> { + coeffs.sort_by(|a, b| a.0.cmp(&b.0)); + + let mut last_degree = None; + for &mut (degree, ref mut coeff) in coeffs { + if coeff.is_zero() { + // zero term has no effect + return Err(CoeffVecError::ZeroCoefficient); + } + if let Some(last) = last_degree { + if last == degree { + // like terms should be combined + return Err(CoeffVecError::DuplicateDegree); + } + } + last_degree = Some(degree); + } + Ok(()) + } + /// Constructs a new polynomial from a list of coefficients. pub fn from_coefficients_slice(coeffs: &[(usize, F)]) -> Self { Self::from_coefficients_vec(coeffs.to_vec()) @@ -324,7 +351,7 @@ impl From> for SparsePolynomial { mod tests { use crate::{ polynomial::Polynomial, - univariate::{DensePolynomial, SparsePolynomial}, + univariate::{sparse::CoeffVecError, DensePolynomial, SparsePolynomial}, EvaluationDomain, GeneralEvaluationDomain, }; use ark_ff::{UniformRand, Zero}; @@ -345,6 +372,30 @@ mod tests { SparsePolynomial::from_coefficients_vec(coeffs) } + #[test] + fn test_valid_coefficients() { + let mut coeffs = vec![(1, Fr::from(2)), (2, Fr::from(3)), (3, Fr::from(4))]; + let validation = SparsePolynomial::is_valid_coefficients_vec(&mut coeffs); + assert!(validation.is_ok()); + + let poly = SparsePolynomial::from_coefficients_vec(coeffs); + assert_eq!(poly.coeffs.len(), 3); + } + + #[test] + fn test_invalid_zero_coefficient() { + let mut coeffs = vec![(1, Fr::from(0)), (2, Fr::from(3))]; + let validation = SparsePolynomial::is_valid_coefficients_vec(&mut coeffs); + assert!(matches!(validation, Err(CoeffVecError::ZeroCoefficient))); + } + + #[test] + fn test_invalid_duplicate_degrees() { + let mut coeffs = vec![(1, Fr::from(2)), (1, Fr::from(3))]; + let validation = SparsePolynomial::is_valid_coefficients_vec(&mut coeffs); + assert!(matches!(validation, Err(CoeffVecError::DuplicateDegree))); + } + #[test] fn evaluate_at_point() { let mut rng = test_rng();