Skip to content

Commit

Permalink
poly: truncate leading zeros for poly add
Browse files Browse the repository at this point in the history
  • Loading branch information
tcoratger committed Nov 26, 2024
1 parent 902ca31 commit 7c59842
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 20 deletions.
2 changes: 1 addition & 1 deletion poly/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! This crate implements functions for manipulating polynomials over finite
//! fields, including FFTs.
#![cfg_attr(not(feature = "std"), no_std)]
// #![cfg_attr(not(feature = "std"), no_std)]
#![warn(
unused,
future_incompatible,
Expand Down
75 changes: 56 additions & 19 deletions poly/src/polynomial/univariate/dense.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,28 +321,42 @@ impl<'a, F: Field> Add<&'a SparsePolynomial<F>> for &DensePolynomial<F> {

#[inline]
fn add(self, other: &'a SparsePolynomial<F>) -> DensePolynomial<F> {
// If `self` is zero, the result is `other` (as adding zero doesn't change anything).
if self.is_zero() {
other.clone().into()
} else if other.is_zero() {
self.clone()
} else {
let mut result = self.clone();
// If `other` has higher degree than `self`, create a dense vector
// storing the upper coefficients of the addition
let mut upper_coeffs = match other.degree() > result.degree() {
true => vec![F::zero(); other.degree() - result.degree()],
false => Vec::new(),
};
for (pow, coeff) in other.iter() {
if *pow <= result.degree() {
result.coeffs[*pow] += coeff;
} else {
upper_coeffs[*pow - result.degree() - 1] = *coeff;
}
return other.clone().into();
}

// If `other` is zero, the result is `self` (as adding zero doesn't change anything).
if other.is_zero() {
return self.clone();
}

let mut result = self.clone();

// Prepare a vector to store the coefficients of the upper terms of the addition.
let mut upper_coeffs = vec![F::zero(); other.degree().saturating_sub(result.degree())];

// Iterate over the sparse polynomial's non-zero terms.
for (pow, coeff) in other.iter() {
// If the power `pow` is within the degree of `result`, add the coefficient to the corresponding term.
if let Some(target) = result.coeffs.get_mut(*pow) {
*target += coeff;
} else {
// Otherwise, store the coefficient in the `upper_coeffs` vector at the appropriate position.
//
// The index is adjusted by subtracting the current length of `result.coeffs`.
upper_coeffs[pow - result.coeffs.len()] = *coeff;
}
result.coeffs.extend(upper_coeffs);
result
}

// Extend the coefficient vector of `result` with the upper terms.
result.coeffs.extend(upper_coeffs);

// Remove any trailing zeros from the coefficient vector to ensure it represents the correct polynomial.
// For example: `0 * x^2 + 0 * x + 1` should be represented as `1`.
result.truncate_leading_zeros();

result
}
}

Expand Down Expand Up @@ -1282,4 +1296,27 @@ mod tests {
assert!(poly1.is_zero());
assert_eq!(poly1.coeffs, vec![]);
}

#[test]
fn test_truncate_leading_zeros_after_sparse_addition() {
// Create a DensePolynomial with leading non-zero coefficients.
let poly1 = DensePolynomial {
coeffs: vec![Fr::from(3), Fr::from(2), Fr::from(1)],
};

// Create a SparsePolynomial to subtract the coefficients of poly1,
// leaving trailing zeros after addition.
let poly2 = SparsePolynomial::from_coefficients_slice(&[
(0, -Fr::from(3)),
(1, -Fr::from(2)),
(2, -Fr::from(1)),
]);

// Perform addition using the Add implementation.
let result = &poly1 + &poly2;

// Assert that the resulting polynomial is zero.
assert!(result.is_zero(), "The resulting polynomial should be zero.");
assert_eq!(result.coeffs, vec![], "Leading zeros were not truncated.");
}
}

0 comments on commit 7c59842

Please sign in to comment.