From e585513c769e83d50128f64618c96974519cdd78 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Mon, 27 Apr 2026 21:38:12 +0100 Subject: [PATCH 1/5] Update version --- Cargo.toml | 6 +++--- README.md | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9271fe2c..a09958ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qfall-math" -version = "0.1.1" +version = "0.1.2" edition = "2024" rust-version = "1.87" # due to wit_bindgen dependency description = "Mathematical foundations for rapid prototyping of lattice-based cryptography" @@ -15,7 +15,7 @@ autobenches = false [dependencies] criterion = { version = "0.8", features = ["html_reports"] } flint-sys = "0.7" -libc = "0" +libc = "0.2" paste = "1" rand = "0.10" rand_distr = "0.6" @@ -26,7 +26,7 @@ string-builder = "0.2" thiserror = "2" lazy_static = "1" probability = "0.20" -derive_more = { version = "2.1", features = ["display"] } +derive_more = { version = "2", features = ["display"] } [profile.bench] debug = true diff --git a/README.md b/README.md index 952c203d..6c41ff66 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,10 @@ let vec_t = &mat_a * &vec_e; let vec_b = vec_s.transpose() * mat_a + vec_e.transpose(); ``` +## SemVer and Backward Compatibility +While this library is not stable yet, i.e. pre-version 1.0.0, we may introduce API-breaking changes in MINOR version updates. +Therefore, we recommend to fix the used version `version = "0.y"` in your `Cargo.toml`. + ## Bugs Please report bugs through the [GitHub issue tracker](https://github.com/qfall/math/issues). From 2c4d0ad95f72920f3eea665ef01f5757a81917bc Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Mon, 27 Apr 2026 22:07:10 +0100 Subject: [PATCH 2/5] Add hamming weight for integers --- src/integer/mat_poly_over_z/norm.rs | 46 +++++++++++++++++++- src/integer/mat_z/norm.rs | 48 ++++++++++++++++++++- src/integer/poly_over_z/norm.rs | 65 +++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 2 deletions(-) diff --git a/src/integer/mat_poly_over_z/norm.rs b/src/integer/mat_poly_over_z/norm.rs index 0223c8a3..d660a763 100644 --- a/src/integer/mat_poly_over_z/norm.rs +++ b/src/integer/mat_poly_over_z/norm.rs @@ -13,7 +13,7 @@ use super::MatPolyOverZ; use crate::{ integer::Z, rational::Q, - traits::{MatrixDimensions, MatrixGetSubmatrix}, + traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix}, }; impl MatPolyOverZ { @@ -89,6 +89,31 @@ impl MatPolyOverZ { } max_norm } + + /// Outputs the hamming weight of `self`, i.e. it computes the sum of all non-zero + /// coefficients in each polynomial in the matrix. + /// + /// # Examples + /// ``` + /// use qfall_math::integer::MatPolyOverZ; + /// use std::str::FromStr; + /// + /// let mat = MatPolyOverZ::from_str("[[2 2 1, 2 3 0],[1 2, 0]]").unwrap(); + /// + /// let hamming_weight = mat.hamming_weight(); + /// + /// assert_eq!(4, hamming_weight); + /// ``` + pub fn hamming_weight(&self) -> i64 { + let mut hamming_weight = 0; + for row in 0..self.get_num_rows() { + for col in 0..self.get_num_columns() { + let entry = unsafe { self.get_entry_unchecked(row, col) }; + hamming_weight += entry.hamming_weight(); + } + } + hamming_weight + } } #[cfg(test)] @@ -131,3 +156,22 @@ mod test_matrix_norms { assert_eq!(Z::from(5), infty_norm); } } + +#[cfg(test)] +mod test_hamming_weight { + use super::MatPolyOverZ; + use std::str::FromStr; + + /// Ensures that the hamming weight is computed correctly. + #[test] + fn hamming_weight() { + let mat0 = MatPolyOverZ::new(3, 4); + let mat1 = MatPolyOverZ::from_str("[[1 -2, 2 3 2],[2 2 0, 1 -5],[1 -2, 0]]").unwrap(); + + let hw0 = mat0.hamming_weight(); + let hw1 = mat1.hamming_weight(); + + assert_eq!(0, hw0); + assert_eq!(6, hw1); + } +} diff --git a/src/integer/mat_z/norm.rs b/src/integer/mat_z/norm.rs index c5d14cc3..e4d91739 100644 --- a/src/integer/mat_z/norm.rs +++ b/src/integer/mat_z/norm.rs @@ -13,7 +13,7 @@ use super::MatZ; use crate::{ integer::Z, rational::Q, - traits::{MatrixDimensions, MatrixGetSubmatrix}, + traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix}, }; impl MatZ { @@ -89,6 +89,33 @@ impl MatZ { } max_norm } + + /// Outputs the hamming weight of `self`, i.e. it returns the number of + /// non-zero entries in the matrix. + /// + /// # Examples + /// ``` + /// use qfall_math::integer::MatZ; + /// use std::str::FromStr; + /// + /// let mat = MatZ::from_str("[[2, 3],[2, 0]]").unwrap(); + /// + /// let hamming_weight = mat.hamming_weight(); + /// + /// assert_eq!(3, hamming_weight); + /// ``` + pub fn hamming_weight(&self) -> i64 { + let mut hamming_weight = 0; + for row in 0..self.get_num_rows() { + for col in 0..self.get_num_columns() { + let entry = unsafe { self.get_entry_unchecked(row, col) }; + if !entry.is_zero() { + hamming_weight += 1; + } + } + } + hamming_weight + } } #[cfg(test)] @@ -126,3 +153,22 @@ mod test_matrix_norms { assert_eq!(Z::from(5), infty_norm); } } + +#[cfg(test)] +mod test_hamming_weight { + use super::MatZ; + use std::str::FromStr; + + /// Ensures that the hamming weight is computed correctly. + #[test] + fn hamming_weight() { + let mat0 = MatZ::new(10, 8); + let mat1 = MatZ::from_str("[[-2, 3],[2, -5],[-2, 0]]").unwrap(); + + let hw0 = mat0.hamming_weight(); + let hw1 = mat1.hamming_weight(); + + assert_eq!(0, hw0); + assert_eq!(5, hw1); + } +} diff --git a/src/integer/poly_over_z/norm.rs b/src/integer/poly_over_z/norm.rs index 00b5508b..425cfc0f 100644 --- a/src/integer/poly_over_z/norm.rs +++ b/src/integer/poly_over_z/norm.rs @@ -11,6 +11,7 @@ use crate::{ integer::{PolyOverZ, Z}, + rational::Q, traits::{GetCoefficient, Pow}, }; use std::cmp::max; @@ -42,6 +43,26 @@ impl PolyOverZ { res } + /// Returns the Euclidean norm or 2-norm of the given polynomial. + /// The Euclidean norm for a polynomial is obtained by treating the coefficients + /// of the polynomial as a vector and then applying the standard Euclidean norm. + /// + /// # Examples + /// ``` + /// use qfall_math::{integer::PolyOverZ, rational::Q}; + /// use std::str::FromStr; + /// + /// let poly = PolyOverZ::from_str("1 3").unwrap(); + /// + /// let norm = poly.norm_eucl(); + /// + /// // sqrt(3^2) = 3 + /// assert_eq!(Q::from(3), norm); + /// ``` + pub fn norm_eucl(&self) -> Q { + self.norm_eucl_sqrd().sqrt() + } + /// Returns the infinity norm or the maximal absolute value of a /// coefficient of the given polynomial. /// The infinity norm for a polynomial is obtained by treating the coefficients @@ -68,6 +89,31 @@ impl PolyOverZ { } res } + + /// Outputs the hamming weight of `self`, i.e. it returns the number of + /// non-zero coefficients in the polynomial. + /// + /// # Examples + /// ``` + /// use qfall_math::integer::PolyOverZ; + /// use std::str::FromStr; + /// + /// let poly = PolyOverZ::from_str("5 1 2 3 0 4").unwrap(); + /// + /// let hamming_weight = poly.hamming_weight(); + /// + /// assert_eq!(4, hamming_weight); + /// ``` + pub fn hamming_weight(&self) -> i64 { + let mut hamming_weight = 0; + for i in 0..=self.get_degree() { + let coeff = unsafe { self.get_coeff_unchecked(i) }; + if !coeff.is_zero() { + hamming_weight += 1; + } + } + hamming_weight + } } #[cfg(test)] @@ -139,3 +185,22 @@ mod test_norm_infty { assert_eq!(poly_2.norm_infty(), Z::from(u64::MAX)); } } + +#[cfg(test)] +mod test_hamming_weight { + use super::PolyOverZ; + use std::str::FromStr; + + /// Ensures that the hamming weight is computed correctly. + #[test] + fn hamming_weight() { + let poly0 = PolyOverZ::default(); + let poly1 = PolyOverZ::from_str("6 0 0 2 3 4 5").unwrap(); + + let hw0 = poly0.hamming_weight(); + let hw1 = poly1.hamming_weight(); + + assert_eq!(0, hw0); + assert_eq!(4, hw1); + } +} From c491b6c781f42f43d100ecf1a06aef4623bfc8df Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Mon, 27 Apr 2026 22:07:48 +0100 Subject: [PATCH 3/5] Add hamming weight for modulo datatypes --- .../mat_polynomial_ring_zq/norm.rs | 54 +++++++++++++- src/integer_mod_q/mat_zq/norm.rs | 49 ++++++++++++- src/integer_mod_q/poly_over_zq/norm.rs | 70 +++++++++++++++++- src/integer_mod_q/polynomial_ring_zq/norm.rs | 72 ++++++++++++++++++- 4 files changed, 240 insertions(+), 5 deletions(-) diff --git a/src/integer_mod_q/mat_polynomial_ring_zq/norm.rs b/src/integer_mod_q/mat_polynomial_ring_zq/norm.rs index e4431c35..993fd359 100644 --- a/src/integer_mod_q/mat_polynomial_ring_zq/norm.rs +++ b/src/integer_mod_q/mat_polynomial_ring_zq/norm.rs @@ -11,9 +11,9 @@ use super::MatPolynomialRingZq; use crate::{ - integer::Z, + integer::{PolyOverZ, Z}, rational::Q, - traits::{MatrixDimensions, MatrixGetSubmatrix}, + traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix}, }; impl MatPolynomialRingZq { @@ -96,6 +96,33 @@ impl MatPolynomialRingZq { } max_norm } + + /// Outputs the hamming weight of `self`, i.e. it computes the sum of all non-zero + /// coefficients in each polynomial in the matrix. + /// + /// # Examples + /// ``` + /// use qfall_math::{integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq}, integer::{Z, MatPolyOverZ}}; + /// use std::str::FromStr; + /// + /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 7").unwrap(); + /// let mat = MatPolyOverZ::from_str("[[2 2 1, 2 3 0],[1 2, 0]]").unwrap(); + /// let mat = MatPolynomialRingZq::from((&mat, &modulus)); + /// + /// let hamming_weight = mat.hamming_weight(); + /// + /// assert_eq!(4, hamming_weight); + /// ``` + pub fn hamming_weight(&self) -> i64 { + let mut hamming_weight = 0; + for row in 0..self.get_num_rows() { + for col in 0..self.get_num_columns() { + let entry: PolyOverZ = unsafe { self.get_entry_unchecked(row, col) }; + hamming_weight += entry.hamming_weight(); + } + } + hamming_weight + } } #[cfg(test)] @@ -145,3 +172,26 @@ mod test_matrix_norms { assert_eq!(Z::from(3), infty_norm); } } + +#[cfg(test)] +mod test_hamming_weight { + use super::MatPolynomialRingZq; + use crate::{integer::MatPolyOverZ, integer_mod_q::ModulusPolynomialRingZq}; + use std::str::FromStr; + + /// Ensures that the hamming weight is computed correctly. + #[test] + fn hamming_weight() { + let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 8").unwrap(); + let mat0 = MatPolyOverZ::new(3, 4); + let mat0 = MatPolynomialRingZq::from((&mat0, &modulus)); + let mat1 = MatPolyOverZ::from_str("[[1 -2, 2 3 2],[2 2 0, 1 -5],[1 -2, 0]]").unwrap(); + let mat1 = MatPolynomialRingZq::from((&mat1, &modulus)); + + let hw0 = mat0.hamming_weight(); + let hw1 = mat1.hamming_weight(); + + assert_eq!(0, hw0); + assert_eq!(6, hw1); + } +} diff --git a/src/integer_mod_q/mat_zq/norm.rs b/src/integer_mod_q/mat_zq/norm.rs index c1837711..ceef4068 100644 --- a/src/integer_mod_q/mat_zq/norm.rs +++ b/src/integer_mod_q/mat_zq/norm.rs @@ -12,8 +12,9 @@ use super::MatZq; use crate::{ integer::Z, + integer_mod_q::Zq, rational::Q, - traits::{MatrixDimensions, MatrixGetSubmatrix}, + traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix}, }; impl MatZq { @@ -89,6 +90,33 @@ impl MatZq { } max_norm } + + /// Outputs the hamming weight of `self`, i.e. it returns the number of + /// non-zero entries in the matrix. + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::MatZq; + /// use std::str::FromStr; + /// + /// let mat = MatZq::from_str("[[2, 3],[2, 0]] mod 3").unwrap(); + /// + /// let hamming_weight = mat.hamming_weight(); + /// + /// assert_eq!(2, hamming_weight); + /// ``` + pub fn hamming_weight(&self) -> i64 { + let mut hamming_weight = 0; + for row in 0..self.get_num_rows() { + for col in 0..self.get_num_columns() { + let entry: Zq = unsafe { self.get_entry_unchecked(row, col) }; + if !entry.is_zero() { + hamming_weight += 1; + } + } + } + hamming_weight + } } #[cfg(test)] @@ -126,3 +154,22 @@ mod test_matrix_norms { assert_eq!(Z::from(3), infty_norm); } } + +#[cfg(test)] +mod test_hamming_weight { + use super::MatZq; + use std::str::FromStr; + + /// Ensures that the hamming weight is computed correctly. + #[test] + fn hamming_weight() { + let mat0 = MatZq::new(10, 8, 7); + let mat1 = MatZq::from_str("[[-2, 3],[2, -4],[-2, 0]] mod 4").unwrap(); + + let hw0 = mat0.hamming_weight(); + let hw1 = mat1.hamming_weight(); + + assert_eq!(0, hw0); + assert_eq!(4, hw1); + } +} diff --git a/src/integer_mod_q/poly_over_zq/norm.rs b/src/integer_mod_q/poly_over_zq/norm.rs index d3a4548e..95578ddf 100644 --- a/src/integer_mod_q/poly_over_zq/norm.rs +++ b/src/integer_mod_q/poly_over_zq/norm.rs @@ -11,7 +11,8 @@ use crate::{ integer::Z, - integer_mod_q::{PolyOverZq, fmpz_mod_helpers::length}, + integer_mod_q::{PolyOverZq, Zq, fmpz_mod_helpers::length}, + rational::Q, traits::{GetCoefficient, Pow}, }; use std::cmp::max; @@ -47,6 +48,29 @@ impl PolyOverZq { res } + /// Returns the Euclidean norm or 2-norm of the given polynomial. + /// The Euclidean norm for a polynomial is obtained by treating the coefficients + /// of the polynomial as a vector and then applying the standard Euclidean norm. + /// + /// Each length of an entry in this vector is defined as the shortest distance + /// to the next zero representative modulo q. + /// + /// # Examples + /// ``` + /// use qfall_math::{rational::Q, integer_mod_q::PolyOverZq}; + /// use std::str::FromStr; + /// + /// let poly = PolyOverZq::from_str("1 3 mod 11").unwrap(); + /// + /// let norm = poly.norm_eucl(); + /// + /// // sqrt(3^2) = 3 + /// assert_eq!(Q::from(3), norm); + /// ``` + pub fn norm_eucl(&self) -> Q { + self.norm_eucl_sqrd().sqrt() + } + /// Returns the infinity norm or the maximal absolute value of a /// coefficient of the given polynomial. /// The infinity norm for a polynomial is obtained by treating the coefficients @@ -77,6 +101,31 @@ impl PolyOverZq { } res } + + /// Outputs the hamming weight of `self`, i.e. it returns the number of + /// non-zero coefficients in the polynomial. + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::PolyOverZq; + /// use std::str::FromStr; + /// + /// let poly = PolyOverZq::from_str("5 1 2 3 0 5 mod 5").unwrap(); + /// + /// let hamming_weight = poly.hamming_weight(); + /// + /// assert_eq!(3, hamming_weight); + /// ``` + pub fn hamming_weight(&self) -> i64 { + let mut hamming_weight = 0; + for i in 0..=self.get_degree() { + let coeff: Zq = unsafe { self.get_coeff_unchecked(i) }; + if !coeff.is_zero() { + hamming_weight += 1; + } + } + hamming_weight + } } #[cfg(test)] @@ -160,3 +209,22 @@ mod test_norm_infty { assert_eq!(poly_2.norm_infty(), Z::from((u64::MAX - 1) / 2 - 57)); } } + +#[cfg(test)] +mod test_hamming_weight { + use super::PolyOverZq; + use std::str::FromStr; + + /// Ensures that the hamming weight is computed correctly. + #[test] + fn hamming_weight() { + let poly0 = PolyOverZq::from((0, 3)); + let poly1 = PolyOverZq::from_str("6 0 0 2 3 4 5 mod 3").unwrap(); + + let hw0 = poly0.hamming_weight(); + let hw1 = poly1.hamming_weight(); + + assert_eq!(0, hw0); + assert_eq!(3, hw1); + } +} diff --git a/src/integer_mod_q/polynomial_ring_zq/norm.rs b/src/integer_mod_q/polynomial_ring_zq/norm.rs index 7ee1f1c0..e470948e 100644 --- a/src/integer_mod_q/polynomial_ring_zq/norm.rs +++ b/src/integer_mod_q/polynomial_ring_zq/norm.rs @@ -12,7 +12,8 @@ use super::PolynomialRingZq; use crate::{ integer::Z, - integer_mod_q::fmpz_mod_helpers::length, + integer_mod_q::{Zq, fmpz_mod_helpers::length}, + rational::Q, traits::{GetCoefficient, Pow}, }; use std::cmp::max; @@ -49,6 +50,30 @@ impl PolynomialRingZq { res } + /// Returns the Euclidean norm or 2-norm of the given polynomial. + /// The Euclidean norm for a polynomial is obtained by treating the coefficients + /// of the polynomial as a vector and then applying the standard Euclidean norm. + /// + /// Each length of an entry in this vector is defined as the shortest distance + /// to the next zero representative modulo q. + /// + /// # Examples + /// ``` + /// use qfall_math::{rational::Q, integer_mod_q::PolynomialRingZq}; + /// use std::str::FromStr; + /// + /// let poly = PolynomialRingZq::from_str("1 3 / 4 1 0 0 1 mod 7").unwrap(); + /// println!("{poly}"); + /// + /// let norm = poly.norm_eucl(); + /// + /// // sqrt(3*3) = 3 + /// assert_eq!(Q::from(3), norm); + /// ``` + pub fn norm_eucl(&self) -> Q { + self.norm_eucl_sqrd().sqrt() + } + /// Returns the infinity norm or the maximal absolute value of a /// coefficient of the given polynomial. /// The infinity norm for a polynomial is obtained by treating the coefficients @@ -79,6 +104,31 @@ impl PolynomialRingZq { } res } + + /// Outputs the hamming weight of `self`, i.e. it returns the number of + /// non-zero coefficients in the polynomial. + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::PolynomialRingZq; + /// use std::str::FromStr; + /// + /// let poly = PolynomialRingZq::from_str("3 1 7 3 / 4 1 0 0 1 mod 7").unwrap(); + /// + /// let hamming_weight = poly.hamming_weight(); + /// + /// assert_eq!(2, hamming_weight); + /// ``` + pub fn hamming_weight(&self) -> i64 { + let mut hamming_weight = 0; + for i in 0..=self.get_degree() { + let coeff: Zq = unsafe { self.get_coeff_unchecked(i) }; + if !coeff.is_zero() { + hamming_weight += 1; + } + } + hamming_weight + } } #[cfg(test)] @@ -168,3 +218,23 @@ mod test_norm_infty { assert_eq!(poly_2.norm_infty(), Z::from((u64::MAX - 1) / 2 - 57)); } } + +#[cfg(test)] +mod test_hamming_weight { + use super::PolynomialRingZq; + use std::str::FromStr; + + /// Ensures that the hamming weight is computed correctly. + #[test] + fn hamming_weight() { + let poly0 = PolynomialRingZq::from_str("0 / 4 1 0 0 1 mod 3").unwrap(); + let poly1 = + PolynomialRingZq::from_str("6 0 0 2 3 4 5 / 8 1 0 0 0 0 0 0 1 mod 5").unwrap(); + + let hw0 = poly0.hamming_weight(); + let hw1 = poly1.hamming_weight(); + + assert_eq!(0, hw0); + assert_eq!(3, hw1); + } +} From 26e73eb09321b26f51511ce4ce30c35bad92e518 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Mon, 27 Apr 2026 22:07:57 +0100 Subject: [PATCH 4/5] Add hamming weight for rationals --- src/rational/mat_q/norm.rs | 48 +++++++++++++++++++++++++++++++- src/rational/poly_over_q/norm.rs | 44 +++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/rational/mat_q/norm.rs b/src/rational/mat_q/norm.rs index 7806fb9f..f3a70185 100644 --- a/src/rational/mat_q/norm.rs +++ b/src/rational/mat_q/norm.rs @@ -12,7 +12,7 @@ use super::MatQ; use crate::{ rational::Q, - traits::{MatrixDimensions, MatrixGetSubmatrix}, + traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix}, }; impl MatQ { @@ -88,6 +88,33 @@ impl MatQ { } max_norm } + + /// Outputs the hamming weight of `self`, i.e. it returns the number of + /// non-zero entries in the matrix. + /// + /// # Examples + /// ``` + /// use qfall_math::rational::MatQ; + /// use std::str::FromStr; + /// + /// let mat = MatQ::from_str("[[2, 3/2],[2/3, 0]]").unwrap(); + /// + /// let hamming_weight = mat.hamming_weight(); + /// + /// assert_eq!(3, hamming_weight); + /// ``` + pub fn hamming_weight(&self) -> i64 { + let mut hamming_weight = 0; + for row in 0..self.get_num_rows() { + for col in 0..self.get_num_columns() { + let entry = unsafe { self.get_entry_unchecked(row, col) }; + if !entry.is_zero() { + hamming_weight += 1; + } + } + } + hamming_weight + } } #[cfg(test)] @@ -125,3 +152,22 @@ mod test_matrix_norms { assert_eq!(Q::from(5), infty_norm); } } + +#[cfg(test)] +mod test_hamming_weight { + use super::MatQ; + use std::str::FromStr; + + /// Ensures that the hamming weight is computed correctly. + #[test] + fn hamming_weight() { + let mat0 = MatQ::new(10, 8); + let mat1 = MatQ::from_str("[[-2/5, 3/4],[2, -5/2],[-2, 0]]").unwrap(); + + let hw0 = mat0.hamming_weight(); + let hw1 = mat1.hamming_weight(); + + assert_eq!(0, hw0); + assert_eq!(5, hw1); + } +} diff --git a/src/rational/poly_over_q/norm.rs b/src/rational/poly_over_q/norm.rs index 4d62e2ae..bcb0933e 100644 --- a/src/rational/poly_over_q/norm.rs +++ b/src/rational/poly_over_q/norm.rs @@ -68,6 +68,31 @@ impl PolyOverQ { } res } + + /// Outputs the hamming weight of `self`, i.e. it returns the number of + /// non-zero coefficients in the polynomial. + /// + /// # Examples + /// ``` + /// use qfall_math::rational::PolyOverQ; + /// use std::str::FromStr; + /// + /// let poly = PolyOverQ::from_str("5 1 2/3 3/2 0 4").unwrap(); + /// + /// let hamming_weight = poly.hamming_weight(); + /// + /// assert_eq!(4, hamming_weight); + /// ``` + pub fn hamming_weight(&self) -> i64 { + let mut hamming_weight = 0; + for i in 0..=self.get_degree() { + let coeff = unsafe { self.get_coeff_unchecked(i) }; + if !coeff.is_zero() { + hamming_weight += 1; + } + } + hamming_weight + } } #[cfg(test)] @@ -142,3 +167,22 @@ mod test_norm_infty { assert_eq!(poly_2.norm_infty(), Q::from(i64::MAX)); } } + +#[cfg(test)] +mod test_hamming_weight { + use super::PolyOverQ; + use std::str::FromStr; + + /// Ensures that the hamming weight is computed correctly. + #[test] + fn hamming_weight() { + let poly0 = PolyOverQ::default(); + let poly1 = PolyOverQ::from_str("6 0 0 2/2 3/2 4 5/7").unwrap(); + + let hw0 = poly0.hamming_weight(); + let hw1 = poly1.hamming_weight(); + + assert_eq!(0, hw0); + assert_eq!(4, hw1); + } +} From d11551b53495112fbddbf255e6705863097e0d24 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Sun, 3 May 2026 23:40:51 +0100 Subject: [PATCH 5/5] Update doc comments --- src/integer/mat_poly_over_z/norm.rs | 4 ++-- src/integer_mod_q/mat_polynomial_ring_zq/norm.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/integer/mat_poly_over_z/norm.rs b/src/integer/mat_poly_over_z/norm.rs index d660a763..98b2dae9 100644 --- a/src/integer/mat_poly_over_z/norm.rs +++ b/src/integer/mat_poly_over_z/norm.rs @@ -90,8 +90,8 @@ impl MatPolyOverZ { max_norm } - /// Outputs the hamming weight of `self`, i.e. it computes the sum of all non-zero - /// coefficients in each polynomial in the matrix. + /// Outputs the hamming weight of `self`, i.e. it computes the number of non-zero + /// coefficients across all polynomials in the matrix. /// /// # Examples /// ``` diff --git a/src/integer_mod_q/mat_polynomial_ring_zq/norm.rs b/src/integer_mod_q/mat_polynomial_ring_zq/norm.rs index 993fd359..f90b7a96 100644 --- a/src/integer_mod_q/mat_polynomial_ring_zq/norm.rs +++ b/src/integer_mod_q/mat_polynomial_ring_zq/norm.rs @@ -97,8 +97,8 @@ impl MatPolynomialRingZq { max_norm } - /// Outputs the hamming weight of `self`, i.e. it computes the sum of all non-zero - /// coefficients in each polynomial in the matrix. + /// Outputs the hamming weight of `self`, i.e. it computes the number of non-zero + /// coefficients across all polynomials in the matrix. /// /// # Examples /// ```