qfall_math/integer_mod_q/poly_over_zq/norm.rs
1// Copyright 2023 Phil Milewski
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! This module includes functionality to compute several norms
10//! defined on polynomials.
11
12use crate::{
13 integer::Z,
14 integer_mod_q::{PolyOverZq, Zq, fmpz_mod_helpers::length},
15 rational::Q,
16 traits::{GetCoefficient, Pow},
17};
18use std::cmp::max;
19
20impl PolyOverZq {
21 /// Returns the squared Euclidean norm or squared 2-norm of the given polynomial.
22 /// The squared Euclidean norm for a polynomial is obtained by treating the coefficients
23 /// of the polynomial as a vector and then applying the standard squared Euclidean norm.
24 ///
25 /// Each length of an entry in this vector is defined as the shortest distance
26 /// to the next zero representative modulo q.
27 ///
28 /// # Examples
29 /// ```
30 /// use qfall_math::{integer::Z, integer_mod_q::PolyOverZq};
31 /// use std::str::FromStr;
32 ///
33 /// let poly = PolyOverZq::from_str("3 1 2 3 mod 11").unwrap();
34 ///
35 /// let sqrd_2_norm = poly.norm_eucl_sqrd();
36 ///
37 /// // 1*1 + 2*2 + 3*3 = 14
38 /// assert_eq!(Z::from(14), sqrd_2_norm);
39 /// ```
40 pub fn norm_eucl_sqrd(&self) -> Z {
41 let mut res = Z::ZERO;
42 for i in 0..=self.get_degree() {
43 let coeff: Z = unsafe { self.get_coeff_unchecked(i) };
44 res += length(&coeff.value, &self.modulus.get_fmpz_mod_ctx_struct().n[0])
45 .pow(2)
46 .unwrap();
47 }
48 res
49 }
50
51 /// Returns the Euclidean norm or 2-norm of the given polynomial.
52 /// The Euclidean norm for a polynomial is obtained by treating the coefficients
53 /// of the polynomial as a vector and then applying the standard Euclidean norm.
54 ///
55 /// Each length of an entry in this vector is defined as the shortest distance
56 /// to the next zero representative modulo q.
57 ///
58 /// # Examples
59 /// ```
60 /// use qfall_math::{rational::Q, integer_mod_q::PolyOverZq};
61 /// use std::str::FromStr;
62 ///
63 /// let poly = PolyOverZq::from_str("1 3 mod 11").unwrap();
64 ///
65 /// let norm = poly.norm_eucl();
66 ///
67 /// // sqrt(3^2) = 3
68 /// assert_eq!(Q::from(3), norm);
69 /// ```
70 pub fn norm_eucl(&self) -> Q {
71 self.norm_eucl_sqrd().sqrt()
72 }
73
74 /// Returns the infinity norm or the maximal absolute value of a
75 /// coefficient of the given polynomial.
76 /// The infinity norm for a polynomial is obtained by treating the coefficients
77 /// of the polynomial as a vector and then applying the standard infinity norm.
78 ///
79 /// Each length of an entry in this vector is defined as the shortest distance
80 /// to the next zero representative modulo q.
81 ///
82 /// # Examples
83 /// ```
84 /// use qfall_math::{integer::Z, integer_mod_q::PolyOverZq};
85 /// use std::str::FromStr;
86 ///
87 /// let poly = PolyOverZq::from_str("3 1 2 4 mod 7").unwrap();
88 ///
89 /// let infty_norm = poly.norm_infty();
90 ///
91 /// // max coefficient is 4 = -3
92 /// assert_eq!(Z::from(3), infty_norm);
93 /// ```
94 pub fn norm_infty(&self) -> Z {
95 let mut res = Z::ZERO;
96
97 for i in 0..=self.get_degree() {
98 let coeff: Z = unsafe { self.get_coeff_unchecked(i) };
99 let len = length(&coeff.value, &self.modulus.get_fmpz_mod_ctx_struct().n[0]);
100 res = max(res, len);
101 }
102 res
103 }
104
105 /// Outputs the hamming weight of `self`, i.e. it returns the number of
106 /// non-zero coefficients in the polynomial.
107 ///
108 /// # Examples
109 /// ```
110 /// use qfall_math::integer_mod_q::PolyOverZq;
111 /// use std::str::FromStr;
112 ///
113 /// let poly = PolyOverZq::from_str("5 1 2 3 0 5 mod 5").unwrap();
114 ///
115 /// let hamming_weight = poly.hamming_weight();
116 ///
117 /// assert_eq!(3, hamming_weight);
118 /// ```
119 pub fn hamming_weight(&self) -> i64 {
120 let mut hamming_weight = 0;
121 for i in 0..=self.get_degree() {
122 let coeff: Zq = unsafe { self.get_coeff_unchecked(i) };
123 if !coeff.is_zero() {
124 hamming_weight += 1;
125 }
126 }
127 hamming_weight
128 }
129}
130
131#[cfg(test)]
132mod test_norm_eucl_sqrd {
133 use super::{PolyOverZq, Z};
134 use std::str::FromStr;
135
136 /// Check whether the squared euclidean norm for polynomials
137 /// with small coefficients is calculated correctly
138 #[test]
139 fn poly_small_coefficient() {
140 let poly_1 = PolyOverZq::from_str("0 mod 11").unwrap();
141 let poly_2 = PolyOverZq::from_str("3 1 2 3 mod 11").unwrap();
142 let poly_3 = PolyOverZq::from_str("3 1 20 194 mod 195").unwrap();
143
144 assert_eq!(poly_1.norm_eucl_sqrd(), Z::ZERO);
145 assert_eq!(poly_2.norm_eucl_sqrd(), Z::from(14));
146 assert_eq!(poly_3.norm_eucl_sqrd(), Z::from(402));
147 }
148
149 /// Check whether the squared euclidean norm for polynomials
150 /// with small coefficients is calculated correctly
151 #[test]
152 fn poly_large_coefficient() {
153 let poly_1 = PolyOverZq::from_str(&format!("1 {} mod {}", u64::MAX, u128::MAX)).unwrap();
154 let poly_2 = PolyOverZq::from_str(&format!(
155 "3 {} {} {} mod {}",
156 u64::MAX,
157 i64::MIN,
158 i64::MAX,
159 u64::MAX - 58
160 ))
161 .unwrap();
162
163 assert_eq!(
164 poly_1.norm_eucl_sqrd(),
165 Z::from(u64::MAX) * Z::from(u64::MAX)
166 );
167 assert_eq!(
168 poly_2.norm_eucl_sqrd(),
169 Z::from(58) * Z::from(58)
170 + Z::from((u64::MAX - 1) / 2 - 57) * Z::from((u64::MAX - 1) / 2 - 57)
171 + Z::from((u64::MAX - 1) / 2 - 58) * Z::from((u64::MAX - 1) / 2 - 58)
172 );
173 }
174}
175
176#[cfg(test)]
177mod test_norm_infty {
178 use super::{PolyOverZq, Z};
179 use std::str::FromStr;
180
181 /// Check whether the infinity norm for polynomials
182 /// with small coefficients is calculated correctly
183 #[test]
184 fn poly_small_coefficient() {
185 let poly_1 = PolyOverZq::from_str("0 mod 3").unwrap();
186 let poly_2 = PolyOverZq::from_str("3 1 2 3 mod 5").unwrap();
187 let poly_3 = PolyOverZq::from_str("3 1 2010 90 mod 100").unwrap();
188
189 assert_eq!(poly_1.norm_infty(), Z::ZERO);
190 assert_eq!(poly_2.norm_infty(), Z::from(2));
191 assert_eq!(poly_3.norm_infty(), Z::from(10));
192 }
193
194 /// Check whether the infinity norm for polynomials
195 /// with small coefficients is calculated correctly
196 #[test]
197 fn poly_large_coefficient() {
198 let poly_1 = PolyOverZq::from_str(&format!("1 {} mod {}", u64::MAX, u128::MAX)).unwrap();
199 let poly_2 = PolyOverZq::from_str(&format!(
200 "3 {} {} {} mod {}",
201 u64::MAX,
202 i64::MIN,
203 i64::MAX,
204 u64::MAX - 58
205 ))
206 .unwrap();
207
208 assert_eq!(poly_1.norm_infty(), Z::from(u64::MAX));
209 assert_eq!(poly_2.norm_infty(), Z::from((u64::MAX - 1) / 2 - 57));
210 }
211}
212
213#[cfg(test)]
214mod test_hamming_weight {
215 use super::PolyOverZq;
216 use std::str::FromStr;
217
218 /// Ensures that the hamming weight is computed correctly.
219 #[test]
220 fn hamming_weight() {
221 let poly0 = PolyOverZq::from((0, 3));
222 let poly1 = PolyOverZq::from_str("6 0 0 2 3 4 5 mod 3").unwrap();
223
224 let hw0 = poly0.hamming_weight();
225 let hw1 = poly1.hamming_weight();
226
227 assert_eq!(0, hw0);
228 assert_eq!(3, hw1);
229 }
230}