qfall_math/integer_mod_q/mat_polynomial_ring_zq/properties.rs
1// Copyright © 2023 Marcel Luca Schmidt
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 about properties of [`MatPolynomialRingZq`] instances.
10
11use super::MatPolynomialRingZq;
12use crate::{
13 integer::PolyOverZ,
14 integer_mod_q::MatNTTPolynomialRingZq,
15 traits::{MatrixDimensions, MatrixGetEntry},
16};
17
18impl MatPolynomialRingZq {
19 /// Checks if a [`MatPolynomialRingZq`] is the identity matrix.
20 ///
21 /// Returns `true` if every diagonal entry of the matrix is
22 /// the constant polynomial `1` and all other entries are `0`.
23 ///
24 /// # Examples
25 /// ```
26 /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
27 /// use qfall_math::integer::MatPolyOverZ;
28 /// use std::str::FromStr;
29 ///
30 /// let modulus = PolyOverZq::from_str("5 1 0 0 0 1 mod 17").unwrap();
31 /// let id_mat = MatPolyOverZ::identity(2, 2);
32 ///
33 /// let poly_ring_mat = MatPolynomialRingZq::from((id_mat, modulus));
34 /// assert!(poly_ring_mat.is_identity());
35 /// ```
36 ///
37 /// ```
38 /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
39 /// use qfall_math::integer::MatPolyOverZ;
40 /// use std::str::FromStr;
41 ///
42 /// let modulus = PolyOverZq::from_str("5 1 0 0 0 1 mod 17").unwrap();
43 /// let id_mat = MatPolyOverZ::from_str("[[1 1, 0],[0, 1 1],[0, 0]]").unwrap();
44 ///
45 /// let poly_ring_mat = MatPolynomialRingZq::from((id_mat, modulus));
46 /// assert!(poly_ring_mat.is_identity());
47 /// ```
48 pub fn is_identity(&self) -> bool {
49 self.matrix.is_identity()
50 }
51
52 /// Checks if a [`MatPolynomialRingZq`] is a square matrix.
53 ///
54 /// Returns `true` if the number of rows and columns is identical.
55 ///
56 /// # Examples
57 /// ```
58 /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
59 /// use qfall_math::integer::MatPolyOverZ;
60 /// use std::str::FromStr;
61 ///
62 /// let modulus = PolyOverZq::from_str("5 1 0 0 0 1 mod 17").unwrap();
63 /// let poly_mat = MatPolyOverZ::from_str("[[1 13, 0],[2 1 1, 1 1]]").unwrap();
64 ///
65 /// let poly_ring_mat = MatPolynomialRingZq::from((poly_mat, modulus));
66 /// assert!(poly_ring_mat.is_square());
67 /// ```
68 pub fn is_square(&self) -> bool {
69 self.matrix.is_square()
70 }
71
72 /// Checks if every entry of a [`MatPolynomialRingZq`] is `0`.
73 ///
74 /// Returns `true` if every entry is `0`.
75 ///
76 /// # Examples
77 /// ```
78 /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
79 /// use qfall_math::integer::MatPolyOverZ;
80 /// use std::str::FromStr;
81 ///
82 /// let modulus = PolyOverZq::from_str("5 1 0 0 0 1 mod 17").unwrap();
83 /// let poly_mat = MatPolyOverZ::new(2,2);
84 ///
85 /// let poly_ring_mat = MatPolynomialRingZq::from((poly_mat, modulus));
86 /// assert!(poly_ring_mat.is_zero());
87 /// ```
88 pub fn is_zero(&self) -> bool {
89 self.matrix.is_zero()
90 }
91
92 /// Checks if a [`MatPolynomialRingZq`] is symmetric.
93 ///
94 /// Returns `true` if we have `a_ij == a_ji` for all i,j.
95 ///
96 /// # Examples
97 /// ```
98 /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
99 /// use std::str::FromStr;
100 ///
101 /// let modulus = ModulusPolynomialRingZq::from_str("2 2 1 mod 17").unwrap();
102 /// let value = MatPolynomialRingZq::identity(2,2, modulus);
103 /// assert!(value.is_symmetric());
104 /// ```
105 pub fn is_symmetric(&self) -> bool {
106 if !self.is_square() {
107 return false;
108 }
109 for row in 0..self.get_num_rows() {
110 for column in 0..row {
111 if unsafe {
112 MatrixGetEntry::<PolyOverZ>::get_entry_unchecked(self, row, column)
113 != MatrixGetEntry::<PolyOverZ>::get_entry_unchecked(self, column, row)
114 } {
115 return false;
116 }
117 }
118 }
119 true
120 }
121
122 /// Returns the NTT representation of `self`.
123 ///
124 /// # Examples
125 /// ```
126 /// use qfall_math::integer_mod_q::{MatNTTPolynomialRingZq, MatPolynomialRingZq, ModulusPolynomialRingZq, PolyOverZq};
127 /// use crate::qfall_math::traits::SetCoefficient;
128 ///
129 /// let n = 4;
130 /// let modulus = 7681;
131 ///
132 /// let mut mod_poly = PolyOverZq::from(modulus);
133 /// mod_poly.set_coeff(0, 1).unwrap();
134 /// mod_poly.set_coeff(n, 1).unwrap();
135 ///
136 /// let mut polynomial_modulus = ModulusPolynomialRingZq::from(&mod_poly);
137 /// polynomial_modulus.set_ntt_unchecked(1925);
138 ///
139 /// let mat_poly_ring = MatPolynomialRingZq::sample_uniform(2, 3, &polynomial_modulus);
140 ///
141 /// let mat_ntt_poly_ring = mat_poly_ring.ntt();
142 /// ```
143 ///
144 /// # Panics ...
145 /// - if the [`NTTBasisPolynomialRingZq`](crate::integer_mod_q::NTTBasisPolynomialRingZq),
146 /// which is part of the [`ModulusPolynomialRingZq`](crate::integer_mod_q::ModulusPolynomialRingZq) in `self`
147 /// is not set.
148 pub fn ntt(&self) -> MatNTTPolynomialRingZq {
149 MatNTTPolynomialRingZq::from(self)
150 }
151}
152
153#[cfg(test)]
154mod test_is_identity {
155 use crate::{
156 integer::MatPolyOverZ,
157 integer_mod_q::{MatPolynomialRingZq, PolyOverZq},
158 };
159 use std::str::FromStr;
160
161 /// Ensure that is_identity returns `true` for identity matrices.
162 #[test]
163 fn identity_detection() {
164 let modulus = PolyOverZq::from_str("5 1 0 0 0 1 mod 17").unwrap();
165
166 let ident_1 = MatPolynomialRingZq::identity(2, 2, &modulus);
167 let ident_2 = MatPolynomialRingZq::identity(2, 3, modulus);
168
169 assert!(ident_1.is_identity());
170 assert!(ident_2.is_identity());
171 }
172
173 /// Ensure that is_identity returns `false` for non-identity matrices.
174 #[test]
175 fn identity_rejection() {
176 let modulus = PolyOverZq::from_str(&format!("5 1 0 0 0 1 mod {}", u64::MAX)).unwrap();
177 let poly_mat_1 = MatPolyOverZ::from_str("[[0, 0],[0, 1 2]]").unwrap();
178 let poly_mat_2 =
179 MatPolyOverZ::from_str(&format!("[[1 1, 0],[2 1 {}, 1 1]]", i64::MAX)).unwrap();
180
181 let small = MatPolynomialRingZq::from((poly_mat_1, &modulus));
182 let large = MatPolynomialRingZq::from((poly_mat_2, modulus));
183
184 assert!(!small.is_identity());
185 assert!(!large.is_identity());
186 }
187}
188
189#[cfg(test)]
190mod test_is_zero {
191 use crate::{
192 integer::MatPolyOverZ,
193 integer_mod_q::{MatPolynomialRingZq, PolyOverZq},
194 };
195 use std::str::FromStr;
196
197 /// Ensure that is_zero returns `true` for all zero matrices.
198 #[test]
199 fn zero_detection() {
200 let modulus = PolyOverZq::from_str("5 1 0 0 0 1 mod 17").unwrap();
201 let poly_mat_1 = MatPolyOverZ::new(2, 2);
202 let poly_mat_2 = MatPolyOverZ::new(4, 2);
203
204 let zero_1 = MatPolynomialRingZq::from((poly_mat_1, &modulus));
205 let zero_2 = MatPolynomialRingZq::from((poly_mat_2, modulus));
206
207 assert!(zero_1.is_zero());
208 assert!(zero_2.is_zero());
209 }
210
211 /// Ensure that is_zero returns `false` for non-zero matrices.
212 #[test]
213 fn zero_rejection() {
214 let modulus = PolyOverZq::from_str(&format!("5 1 0 0 0 1 mod {}", u64::MAX)).unwrap();
215 let poly_mat_1 = MatPolyOverZ::from_str("[[0, 0],[0, 1 2]]").unwrap();
216 let poly_mat_2 =
217 MatPolyOverZ::from_str(&format!("[[1 1, 0],[2 1 {}, 0]]", i64::MAX)).unwrap();
218
219 let small = MatPolynomialRingZq::from((poly_mat_1, &modulus));
220 let large = MatPolynomialRingZq::from((poly_mat_2, modulus));
221
222 assert!(!small.is_zero());
223 assert!(!large.is_zero());
224 }
225}
226
227#[cfg(test)]
228mod test_is_square {
229 use crate::{
230 integer::MatPolyOverZ,
231 integer_mod_q::{MatPolynomialRingZq, PolyOverZq},
232 };
233 use std::str::FromStr;
234
235 /// Ensure that is_square returns `true` for square matrices.
236 #[test]
237 fn square_detection() {
238 let modulus = PolyOverZq::from_str("5 1 0 0 0 1 mod 17").unwrap();
239 let poly_mat_1 = MatPolyOverZ::from_str("[[1 3, 0],[0, 2 7 1]]").unwrap();
240 let poly_mat_2 =
241 MatPolyOverZ::from_str("[[0, 1 1, 2 2 3],[0, 0, 1 15],[0, 0, 0]]").unwrap();
242
243 let square_1 = MatPolynomialRingZq::from((poly_mat_1, &modulus));
244 let square_2 = MatPolynomialRingZq::from((poly_mat_2, modulus));
245
246 assert!(square_1.is_square());
247 assert!(square_2.is_square());
248 }
249
250 /// Ensure that is_square returns `false` for non-square matrices.
251 #[test]
252 fn square_rejection() {
253 let modulus = PolyOverZq::from_str(&format!("5 1 0 0 0 1 mod {}", u64::MAX)).unwrap();
254 let poly_mat_1 = MatPolyOverZ::new(1, 2);
255 let poly_mat_2 =
256 MatPolyOverZ::from_str(&format!("[[1 1, 0, 1 7],[2 1 {}, 0, 0]]", i64::MAX))
257 .unwrap();
258
259 let small = MatPolynomialRingZq::from((poly_mat_1, &modulus));
260 let large = MatPolynomialRingZq::from((poly_mat_2, modulus));
261
262 assert!(!small.is_square());
263 assert!(!large.is_square());
264 }
265}
266
267#[cfg(test)]
268mod test_is_symmetric {
269 use super::MatPolynomialRingZq;
270 use std::str::FromStr;
271
272 /// Ensure that is_symmetric returns `false` for non-symmetric matrices.
273 #[test]
274 fn symmetric_rejection() {
275 let mat_2x3 =
276 MatPolynomialRingZq::from_str("[[0, 1 6, 2 1 4],[1 2, 0, 2 1 1]] / 2 1 2 mod 17")
277 .unwrap();
278 let mat_2x2 =
279 MatPolynomialRingZq::from_str("[[1 9, 0],[2 1 71, 0]] / 3 1 2 1 mod 17").unwrap();
280
281 assert!(!mat_2x3.is_symmetric());
282 assert!(!mat_2x2.is_symmetric());
283 }
284
285 /// Ensure that is_symmetric returns `true` for symmetric matrices.
286 #[test]
287 fn symmetric_detection() {
288 let mat_2x2 = MatPolynomialRingZq::from_str(&format!(
289 "[[2 1 {}, 2 3 {}],[2 3 {}, 3 1 {} 8]] / 2 1 2 mod {}",
290 u64::MIN,
291 i64::MAX,
292 i64::MAX,
293 i64::MAX,
294 u64::MAX
295 ))
296 .unwrap();
297
298 assert!(mat_2x2.is_symmetric());
299 }
300}