qfall_math/integer_mod_q/mat_polynomial_ring_zq/from.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//! Implementations to create a [`MatPolynomialRingZq`] value from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::MatPolynomialRingZq;
14use crate::{
15 error::{MathError, StringConversionError},
16 integer::MatPolyOverZ,
17 integer_mod_q::{MatNTTPolynomialRingZq, ModulusPolynomialRingZq},
18};
19use std::str::FromStr;
20
21impl From<MatNTTPolynomialRingZq> for MatPolynomialRingZq {
22 /// Creates a polynomial ring matrix of type [`MatPolynomialRingZq`] from
23 /// the corresponding [`MatNTTPolynomialRingZq`].
24 ///
25 /// Parameters:
26 /// - `matrix`: the polynomial matrix defining each entry.
27 ///
28 /// Returns a new [`MatPolynomialRingZq`] with the entries from `matrix`.
29 ///
30 /// # Examples
31 /// ```
32 /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, MatNTTPolynomialRingZq, ModulusPolynomialRingZq};
33 /// use std::str::FromStr;
34 /// let mut modulus = ModulusPolynomialRingZq::from_str("5 1 0 0 0 1 mod 257").unwrap();
35 /// modulus.set_ntt_unchecked(64);
36 /// let ntt_mat = MatNTTPolynomialRingZq::sample_uniform(1, 1, &modulus);
37 ///
38 /// let poly_ring_mat = MatPolynomialRingZq::from(ntt_mat);
39 /// ```
40 ///
41 /// # Panics ...
42 /// - if the [`NTTBasisPolynomialRingZq`](crate::integer_mod_q::NTTBasisPolynomialRingZq) in `modulus`
43 /// is not set.
44 fn from(matrix: MatNTTPolynomialRingZq) -> Self {
45 matrix.inv_ntt()
46 }
47}
48
49impl FromStr for MatPolynomialRingZq {
50 type Err = MathError;
51
52 /// Creates a [`MatPolynomialRingZq`] matrix from a [`String`].
53 ///
54 /// **Warning**: Each entry is parsed as a [`PolyOverZ`](crate::integer::PolyOverZ::from_str) object.
55 /// If an entry string starts with a correctly formatted [`PolyOverZ`](crate::integer::PolyOverZ::from_str) object,
56 /// the rest of this entry string is ignored. This means that the entry input
57 /// string `"4 0 1 2 3"` is the same as `"4 0 1 2 3 4 5 6 7"`.
58 ///
59 /// Parameters:
60 /// - `string`: the matrix of form: `"[[poly_1, poly_2, poly_3],[poly_4, poly_5, poly_6]] / poly_7 mod 11"`
61 /// for a 2x3 matrix where the first three polynomials are in the first row,
62 /// the second three are in the second row, and the seventh polynomial and 11 form the modulus.
63 ///
64 /// Note that the strings for entries, the polynomial modulus and the integer modulus are trimmed,
65 /// i.e. all whitespaces around all values are ignored.
66 ///
67 /// Returns a [`MatPolynomialRingZq`] or an error if the matrix is not formatted in a suitable way,
68 /// the number of rows or columns is too large (must fit into [`i64`]),
69 /// the number of entries in rows is unequal, or if an entry is not formatted correctly.
70 ///
71 /// # Examples
72 /// ```
73 /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
74 /// use std::str::FromStr;
75 ///
76 /// let matrix = MatPolynomialRingZq::from_str("[[2 2 2, 1 2],[0, 1 3]] / 2 3 3 mod 24").unwrap();
77 /// ```
78 ///
79 /// ```
80 /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
81 /// use std::str::FromStr;
82 ///
83 /// let str_1 = "[[2 2 2, 1 2],[0, 1 3]] / 2 3 3 mod 24";
84 /// let matrix = MatPolynomialRingZq::from_str(str_1).unwrap();
85 /// ```
86 ///
87 /// ```
88 /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
89 /// use std::str::FromStr;
90 ///
91 /// let string = String::from("[[2 2 2, 1 2],[0, 1 3]] / 2 3 3 mod 24");
92 /// let matrix = MatPolynomialRingZq::from_str(&string).unwrap();
93 /// ```
94 ///
95 /// # Errors and Failures
96 /// - Returns a [`MathError`] of type [`MathError::StringConversionError`],
97 /// - if the matrix is not formatted in a suitable way,
98 /// - if the number of rows or columns is too large (must fit into i64),
99 /// - if the number of entries in rows is unequal,
100 /// - if the delimiter `/` and `mod` could not be found,
101 /// - if the modulus is not formatted correctly,
102 /// for further information see [`PolyOverZq::from_str`](crate::integer_mod_q::PolyOverZq::from_str), or
103 /// - if an entry is not formatted correctly.
104 /// For further information see [`PolyOverZ::from_str`](crate::integer::PolyOverZ::from_str).
105 /// - Returns a MathError of type InvalidModulus
106 /// - if modulus is smaller than 2, or
107 /// - if the modulus polynomial is 0.
108 ///
109 /// # Panics ...
110 /// - if the provided number of rows and columns are not suited to create a matrix.
111 /// For further information see [`MatPolyOverZ::new`].
112 fn from_str(string: &str) -> Result<Self, MathError> {
113 let (matrix, modulus) = match string.split_once("/") {
114 Some((matrix, modulus)) => (matrix, modulus),
115 None => {
116 return Err(StringConversionError::InvalidMatrix(format!(
117 "The delimiter '/' could not be found: {string}"
118 )))?;
119 }
120 };
121
122 let matrix = MatPolyOverZ::from_str(matrix.trim())?;
123 let modulus = ModulusPolynomialRingZq::from_str(modulus.trim())?;
124
125 Ok(Self::from((matrix, modulus)))
126 }
127}
128
129impl<Matrix: Into<MatPolyOverZ>, Mod: Into<ModulusPolynomialRingZq>> From<(Matrix, Mod)>
130 for MatPolynomialRingZq
131{
132 /// Creates a polynomial ring matrix of type [`MatPolynomialRingZq`] from
133 /// a value that implements [`Into<MatPolyOverZ>`] and a value that
134 /// implements [`Into<ModulusPolynomialRingZq>`].
135 ///
136 /// Parameters:
137 /// - `matrix`: the polynomial matrix defining each entry.
138 /// - `modulus`: the modulus that is applied to each polynomial.
139 ///
140 /// Returns a new [`MatPolynomialRingZq`] with the entries from `matrix`
141 /// under the modulus `modulus`.
142 ///
143 /// # Examples
144 /// ```
145 /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
146 /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
147 /// use qfall_math::integer::MatPolyOverZ;
148 /// use std::str::FromStr;
149 ///
150 /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
151 /// let poly_mat = MatPolyOverZ::from_str("[[4 -1 0 1 1, 1 42],[0, 2 1 2]]").unwrap();
152 ///
153 /// let poly_ring_mat = MatPolynomialRingZq::from((poly_mat, modulus));
154 /// ```
155 fn from((matrix, modulus): (Matrix, Mod)) -> Self {
156 let mut out = Self {
157 matrix: matrix.into(),
158 modulus: modulus.into(),
159 };
160 out.reduce();
161 out
162 }
163}
164
165impl From<&MatPolynomialRingZq> for MatPolynomialRingZq {
166 /// Alias for [`MatPolynomialRingZq::clone`].
167 fn from(value: &MatPolynomialRingZq) -> Self {
168 value.clone()
169 }
170}
171
172#[cfg(test)]
173mod test_from_str {
174 use crate::{integer::PolyOverZ, integer_mod_q::MatPolynomialRingZq, traits::MatrixGetEntry};
175 use std::str::FromStr;
176
177 /// Ensure that initialization works.
178 #[test]
179 fn init_works() {
180 let matrix_str_1 = "[[1 2, 0, 1 3],[1 3, 1 4, 1 5]] / 2 1 1 mod 6";
181
182 let matrix: PolyOverZ = MatPolynomialRingZq::from_str(matrix_str_1)
183 .unwrap()
184 .get_entry(0, 0)
185 .unwrap();
186
187 assert_eq!(PolyOverZ::from(2), matrix);
188 }
189
190 /// Ensure that entries are correctly reduced.
191 #[test]
192 fn reduce_works() {
193 let matrix_str_1 = "[[1 2, 0, 1 3],[1 3, 2 2 2, 1 5]] / 2 1 1 mod 3";
194
195 let matrix: PolyOverZ = MatPolynomialRingZq::from_str(matrix_str_1)
196 .unwrap()
197 .get_entry(1, 0)
198 .unwrap();
199
200 assert_eq!(PolyOverZ::default(), matrix);
201 }
202
203 /// Ensure that initialization with positive numbers that are larger than [`i64`] works.
204 #[test]
205 fn init_works_large_numbers() {
206 let matrix_string = format!(
207 "[[1 {}, 0],[1 3, 1 4]] / 2 1 1 mod {}",
208 u64::MAX - 1,
209 u64::MAX
210 );
211 let matrix: PolyOverZ = MatPolynomialRingZq::from_str(&matrix_string)
212 .unwrap()
213 .get_entry(0, 0)
214 .unwrap();
215
216 assert_eq!(PolyOverZ::from(u64::MAX - 1), matrix);
217 }
218
219 /// Ensure that entries can have leading and trailing whitespaces.
220 #[test]
221 fn whitespaces_in_entries_works() {
222 let matrix_str_1 =
223 "[[1 2 , 0 , 1 3],[ 1 3, 1 4, 1 5 ]] / 2 1 1 mod6 ";
224
225 let matrix: PolyOverZ = MatPolynomialRingZq::from_str(matrix_str_1)
226 .unwrap()
227 .get_entry(0, 2)
228 .unwrap();
229
230 assert_eq!(PolyOverZ::from(3), matrix);
231 }
232
233 /// Ensure that a wrong format causes an error.
234 #[test]
235 fn wrong_format_error() {
236 let matrix_str_1 = "[1 2, 0],[1 3, 1 4]] / 2 1 1 mod 6";
237 let matrix_str_2 = "[[1 2, 0][1 3, 1 4]] / 2 1 1 mod 6";
238 let matrix_str_3 = "[[1 2, 0],1 3, 1 4]] / 2 1 1 mod 6";
239 let matrix_str_4 = "[1 2, 0] / 2 1 1 mod 6";
240 let matrix_str_5 = "[ [1 2, 0],[1 3, 1 4]] / 2 1 1 mod 6";
241 let matrix_str_6 = "[[1 2, 0],[1 3, 1 4]8] / 2 1 1 mod 6";
242 let matrix_str_7 = "[[1 2, 0],[1 3, 1 4]] / 2 1 1 mo 6";
243 let matrix_str_8 = " / 2 1 1 mod 6";
244 let matrix_str_9 = "[[1 2, 0],[1 3, 1 4]]";
245 let matrix_str_10 = "[[1 2, 0],[1 3, 1 4]] mod 6";
246 let matrix_str_11 = "[[1 2, 0],[1 3, 1 4]] / 6";
247 let matrix_str_12 = "";
248 let matrix_str_13 = "[] mod 6";
249 let matrix_str_14 = "[[]] mod 6";
250
251 assert!(MatPolynomialRingZq::from_str(matrix_str_1).is_err());
252 assert!(MatPolynomialRingZq::from_str(matrix_str_2).is_err());
253 assert!(MatPolynomialRingZq::from_str(matrix_str_3).is_err());
254 assert!(MatPolynomialRingZq::from_str(matrix_str_4).is_err());
255 assert!(MatPolynomialRingZq::from_str(matrix_str_5).is_err());
256 assert!(MatPolynomialRingZq::from_str(matrix_str_6).is_err());
257 assert!(MatPolynomialRingZq::from_str(matrix_str_7).is_err());
258 assert!(MatPolynomialRingZq::from_str(matrix_str_8).is_err());
259 assert!(MatPolynomialRingZq::from_str(matrix_str_9).is_err());
260 assert!(MatPolynomialRingZq::from_str(matrix_str_10).is_err());
261 assert!(MatPolynomialRingZq::from_str(matrix_str_11).is_err());
262 assert!(MatPolynomialRingZq::from_str(matrix_str_12).is_err());
263 assert!(MatPolynomialRingZq::from_str(matrix_str_13).is_err());
264 assert!(MatPolynomialRingZq::from_str(matrix_str_14).is_err());
265 }
266}
267
268#[cfg(test)]
269mod test_from {
270 use crate::{
271 integer::{MatPolyOverZ, MatZ},
272 integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolyOverZq},
273 };
274 use std::str::FromStr;
275
276 const LARGE_PRIME: u64 = u64::MAX - 58;
277
278 /// Checks whether `from` is available for all types implementing
279 /// [`Into<MatPolyOverZ>`] and [`Into<ModulusPolynomialRingZq>`]
280 #[test]
281 fn availability() {
282 let modulus_1 = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
283 let modulus_2 = PolyOverZq::from_str("4 1 0 0 1 mod 17").unwrap();
284 let poly_mat_1 = MatPolyOverZ::from_str("[[4 -1 0 1 1, 1 42],[0, 2 1 2]]").unwrap();
285 let poly_mat_2 = MatZ::from_str("[[1, 2, 3],[4, 5, 6]]").unwrap();
286
287 let _ = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
288 let _ = MatPolynomialRingZq::from((&poly_mat_1, modulus_1.clone()));
289 let _ = MatPolynomialRingZq::from((poly_mat_1.clone(), &modulus_1));
290 let _ = MatPolynomialRingZq::from((&poly_mat_2, &modulus_2));
291 let _ = MatPolynomialRingZq::from((&poly_mat_2, modulus_2.clone()));
292 let _ = MatPolynomialRingZq::from((poly_mat_2.clone(), &modulus_2));
293 let _ = MatPolynomialRingZq::from((&poly_mat_1, &modulus_2));
294 let _ = MatPolynomialRingZq::from((&poly_mat_2, modulus_1));
295 }
296
297 /// Ensure that the modulus is applied with a large prime and large coefficients
298 #[test]
299 fn is_reduced_large() {
300 let modulus =
301 ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {LARGE_PRIME}")).unwrap();
302
303 let poly_mat = MatPolyOverZ::from_str(&format!(
304 "[[4 {} {} 1 1, 1 42],[0, 2 1 2]]",
305 LARGE_PRIME + 2,
306 u64::MAX
307 ))
308 .unwrap();
309 let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
310
311 let cmp_poly_mat = MatPolyOverZ::from_str("[[3 1 58 1, 1 42],[0, 2 1 2]]").unwrap();
312 let cmp_poly_ring_mat = MatPolynomialRingZq::from((&cmp_poly_mat, &modulus));
313
314 assert_eq!(poly_ring_mat, cmp_poly_ring_mat);
315 }
316
317 /// Ensure that two ring elements that are instantiated the same way are equal
318 #[test]
319 fn same_instantiation() {
320 let modulus =
321 ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {LARGE_PRIME}")).unwrap();
322 let poly_mat = MatPolyOverZ::from_str(&format!(
323 "[[4 {} {} 1 1, 1 42],[0, 2 1 2]]",
324 LARGE_PRIME + 2,
325 u64::MAX
326 ))
327 .unwrap();
328
329 let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat, &modulus));
330 let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat, &modulus));
331
332 assert_eq!(poly_ring_mat_1, poly_ring_mat_2);
333 }
334
335 /// Ensure that from works for different dimensions
336 #[test]
337 fn different_dimensions() {
338 let modulus =
339 ModulusPolynomialRingZq::from_str(&format!("3 1 9 12 mod {LARGE_PRIME}")).unwrap();
340 let poly_mat_1 = MatPolyOverZ::from_str("[[2 1 8],[2 1 2]]").unwrap();
341 let poly_mat_2 = MatPolyOverZ::from_str("[[2 1 8, 1 42, 0],[0, 2 1 2, 1 17]]").unwrap();
342 let poly_mat_3 = MatPolyOverZ::from_str("[[2 1 8]]").unwrap();
343
344 let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
345 let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
346 let poly_ring_mat_3 = MatPolynomialRingZq::from((&poly_mat_3, &modulus));
347
348 assert_eq!(poly_ring_mat_1.matrix, poly_mat_1);
349 assert_eq!(poly_ring_mat_2.matrix, poly_mat_2);
350 assert_eq!(poly_ring_mat_3.matrix, poly_mat_3);
351 }
352}