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}