qfall_math/integer_mod_q/mat_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 [`MatZq`] value from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::MatZq;
14use crate::{
15    error::{MathError, StringConversionError},
16    integer::{MatZ, Z},
17    integer_mod_q::Modulus,
18    traits::{MatrixDimensions, MatrixSetEntry},
19    utils::{
20        dimensions::find_matrix_dimensions,
21        index::evaluate_indices,
22        parse::{matrix_from_utf8_fill_bytes, parse_matrix_string},
23    },
24};
25use flint_sys::{fmpz_mat::fmpz_mat_set, fmpz_mod_mat::_fmpz_mod_mat_reduce};
26use std::{fmt::Display, str::FromStr};
27
28impl FromStr for MatZq {
29    type Err = MathError;
30
31    /// Creates a [`MatZq`] matrix with entries in [`Zq`](crate::integer_mod_q::Zq) from a [`String`].
32    ///
33    /// Parameters:
34    /// - `string`: the matrix of form: `"[[1, 2, 3],[4, 5, 6]] mod 4"` for a 2x3 matrix
35    ///   with entries 1, 2, 3 in the first row, 4, 5, 6 in the second row and 4 as modulus.
36    ///
37    /// Note that the strings for entries and the modulus are trimmed,
38    /// i.e. all whitespaces around all values are ignored.
39    ///
40    /// Returns a [`MatZq`] or an error if the matrix is not formatted in a suitable way,
41    /// the number of rows or columns is too large (must fit into [`i64`]),
42    /// the number of entries in rows is unequal or if the modulus or an entry is not formatted correctly.
43    ///
44    /// # Examples
45    /// ```
46    /// use qfall_math::integer_mod_q::MatZq;
47    /// use std::str::FromStr;
48    ///
49    /// let matrix = MatZq::from_str("[[1, 2, 3],[4, 5, 6]] mod 4").unwrap();
50    /// ```
51    ///
52    /// ```
53    /// use qfall_math::integer_mod_q::MatZq;
54    /// use std::str::FromStr;
55    ///
56    /// let str_1 = "[[1, 2, 3],[4, 5, 6]] mod 4";
57    /// let matrix = MatZq::from_str(str_1).unwrap();
58    /// ```
59    ///
60    /// ```
61    /// use qfall_math::integer_mod_q::MatZq;
62    /// use std::str::FromStr;
63    ///
64    /// let string = String::from("[[1, 2, 3],[4, 5, 6]] mod 4");
65    /// let matrix = MatZq::from_str(&string).unwrap();
66    /// ```
67    ///
68    /// # Errors and Failures
69    /// - Returns a [`MathError`] of type [`StringConversionError`](MathError::StringConversionError)
70    ///     - if the matrix is not formatted in a suitable way,
71    ///     - if the number of rows or columns is too large (must fit into i64),
72    ///     - if the number of entries in rows is unequal,
73    ///     - if the delimiter `mod` could not be found, or
74    ///     - if the modulus or an entry is not formatted correctly.
75    ///       For further information see [`Z::from_str`].
76    ///
77    /// # Panics ...
78    /// - if the provided number of rows and columns or the modulus are not suited to create a matrix.
79    ///   For further information see [`MatZq::new`].
80    /// - if the modulus is smaller than `2`.
81    fn from_str(string: &str) -> Result<Self, MathError> {
82        let (matrix, modulus) = match string.split_once("mod") {
83            Some((matrix, modulus)) => (matrix, modulus),
84            None => {
85                return Err(StringConversionError::InvalidMatrix(format!(
86                    "The word 'mod' could not be found: {string}"
87                )))?;
88            }
89        };
90
91        let modulus = Z::from_str(modulus.trim())?;
92
93        let string_matrix = parse_matrix_string(matrix.trim())?;
94        let (num_rows, num_cols) = find_matrix_dimensions(&string_matrix)?;
95        let mut matrix = MatZq::new(num_rows, num_cols, modulus);
96
97        // fill entries of matrix according to entries in string_matrix
98        for (row_num, row) in string_matrix.iter().enumerate() {
99            for (col_num, entry) in row.iter().enumerate() {
100                let z_entry = Z::from_str(entry)?;
101                matrix.set_entry(row_num, col_num, z_entry)?;
102            }
103        }
104        Ok(matrix)
105    }
106}
107
108impl<Mod: Into<Modulus>> From<(&MatZ, Mod)> for MatZq {
109    /// Creates a [`MatZq`] from a [`MatZ`] and a value that implements [`Into<Modulus>`].
110    ///
111    /// Parameters:
112    /// - `matrix`: the matrix from which the entries are taken
113    /// - `modulus`: the modulus of the matrix
114    ///
115    /// Returns a [`MatZq`].
116    ///
117    /// # Examples
118    /// ```
119    /// use qfall_math::integer::MatZ;
120    /// use qfall_math::integer_mod_q::MatZq;
121    /// use std::str::FromStr;
122    ///
123    /// let m = MatZ::from_str("[[1, 2],[3, -1]]").unwrap();
124    ///
125    /// let a = MatZq::from((&m, 17));
126    /// ```
127    fn from((matrix, modulus): (&MatZ, Mod)) -> Self {
128        let mut out = MatZq::new(matrix.get_num_rows(), matrix.get_num_columns(), modulus);
129        unsafe {
130            fmpz_mat_set(&mut out.matrix.mat[0], &matrix.matrix);
131            _fmpz_mod_mat_reduce(&mut out.matrix);
132        }
133        out
134    }
135}
136
137impl<Mod: Into<Modulus>> From<(MatZ, Mod)> for MatZq {
138    /// Creates a [`MatZq`] from a [`MatZ`] and a value that implements [`Into<Modulus>`].
139    ///
140    /// Parameters:
141    /// - `matrix`: the matrix from which the entries are taken
142    /// - `modulus`: the modulus of the matrix
143    ///
144    /// Returns a new [`MatZq`] matrix with entries from the [`MatZ`] instance modulo `modulus`.
145    ///
146    /// # Examples
147    /// ```
148    /// use qfall_math::integer::MatZ;
149    /// use qfall_math::integer_mod_q::MatZq;
150    /// use std::str::FromStr;
151    ///
152    /// let m = MatZ::from_str("[[1, 2],[3, -1]]").unwrap();
153    ///
154    /// let a = MatZq::from((m, 17));
155    /// ```
156    fn from((matrix, modulus): (MatZ, Mod)) -> Self {
157        MatZq::from((&matrix, modulus))
158    }
159}
160
161impl From<&MatZq> for MatZq {
162    /// Alias for [`MatZq::clone`].
163    fn from(value: &MatZq) -> Self {
164        value.clone()
165    }
166}
167
168impl MatZq {
169    /// Create a [`MatZq`] from a [`String`], i.e. its UTF8-Encoding.
170    /// This function can only construct positive or zero integers, but not negative ones.
171    /// If the number of bytes and number of entries does not line up, we pad the message
172    /// with `'0'`s.
173    /// The inverse of this function is [`MatZq::to_utf8`].
174    ///
175    /// **WARNING:** This implementation requires the `modulus` to be larger than
176    /// any single entry in the matrix. This function will denote the same number of bytes
177    /// to every entry and sequentially move through your `message` to encode them.
178    /// If a decimal presentation of these bytes is ever larger than the specified `modulus`,
179    /// the function will return an error.
180    ///
181    /// Parameters:
182    /// - `message`: specifies the message that is transformed via its UTF8-Encoding
183    ///   to a new [`MatZq`] instance.
184    /// - `num_rows`: number of rows the new matrix should have
185    /// - `num_cols`: number of columns the new matrix should have
186    /// - `modulus`: specifies the modulus of the matrix, it is required to be larger
187    ///   than any entry of the matrix
188    ///
189    /// Returns a [`MatZq`] with corresponding entries to the message's UTF8-Encoding or
190    /// a [`ConversionError`](MathError::ConversionError) if the modulus isn't larger than
191    /// every single entry of the matrix after distributing the (potentially padded) UTF8-Bytes
192    /// equally over the matrix.
193    ///
194    /// # Examples
195    /// ```
196    /// use qfall_math::integer_mod_q::MatZq;
197    /// let message = "hello!";
198    ///  
199    /// let matrix = MatZq::from_utf8(&message, 3, 2, 257).unwrap();
200    /// ```
201    ///
202    /// # Errors and Failures
203    /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError)
204    ///   if the modulus isn't larger than the largest entry of the matrix after equally
205    ///   distributing the (potentially padded) UTF8-Conversion over the matrix.
206    ///
207    /// # Panics ...
208    /// - if the provided number of rows and columns are not suited to create a matrix.
209    ///   For further information see [`MatZq::new`].
210    pub fn from_utf8(
211        message: &str,
212        num_rows: impl TryInto<i64> + Display,
213        num_cols: impl TryInto<i64> + Display,
214        modulus: impl Into<Modulus>,
215    ) -> Result<Self, MathError> {
216        let (num_rows_i64, num_cols_i64) = evaluate_indices(num_rows, num_cols).unwrap();
217        let mut mat = MatZq::new(num_rows_i64, num_cols_i64, modulus);
218        let num_columns = mat.get_num_columns() as usize;
219        let nr_entries = mat.get_num_rows() as usize * num_columns;
220        let modulus_as_z = Z::from(mat.get_mod());
221
222        let (byte_vector, nr_bytes_per_entry) = matrix_from_utf8_fill_bytes(message, nr_entries);
223
224        // Fill rows going from left to right, entry by entry
225        for row in 0..mat.get_num_rows() as usize {
226            let offset_row = row * num_columns * nr_bytes_per_entry;
227            for col in 0..num_columns {
228                let entry_value = Z::from_bytes(
229                    &byte_vector[offset_row + nr_bytes_per_entry * col
230                        ..offset_row + nr_bytes_per_entry * (col + 1)],
231                );
232                if modulus_as_z > entry_value {
233                    unsafe { mat.set_entry_unchecked(row as i64, col as i64, entry_value) };
234                } else {
235                    return Err(MathError::ConversionError(
236                        "The provided modulus is smaller than the UTF8-Encoding of your message."
237                            .to_owned(),
238                    ));
239                }
240            }
241        }
242
243        Ok(mat)
244    }
245}
246
247#[cfg(test)]
248mod test_from_mat_z_modulus {
249    use crate::{
250        integer::{MatZ, Z},
251        integer_mod_q::{MatZq, Modulus},
252        traits::{MatrixDimensions, MatrixGetEntry, MatrixSetEntry},
253    };
254
255    /// Test if the dimensions are taken over correctly.
256    #[test]
257    fn dimensions() {
258        let matz = MatZ::new(15, 17);
259        let modulus = Modulus::from(17);
260
261        let matzq_1 = MatZq::from((&matz, &modulus));
262
263        assert_eq!(15, matzq_1.get_num_rows());
264        assert_eq!(17, matzq_1.get_num_columns());
265    }
266
267    /// Test if entries are taken over correctly.
268    #[test]
269    fn entries_taken_over_correctly() {
270        let mut matz = MatZ::new(2, 2);
271        let modulus = Modulus::from(u64::MAX);
272
273        matz.set_entry(0, 0, u64::MAX - 58).unwrap();
274        matz.set_entry(0, 1, -1).unwrap();
275
276        let matzq_1 = MatZq::from((&matz, &modulus));
277
278        let entry_1: Z = matzq_1.get_entry(0, 1).unwrap();
279        let entry_2: Z = matzq_1.get_entry(0, 0).unwrap();
280
281        assert_eq!(u64::MAX - 1, entry_1);
282        assert_eq!(u64::MAX - 58, entry_2);
283    }
284
285    /// Ensures that the function is still available for all values implementing
286    /// `Into<Modulus>`.
287    #[test]
288    fn availability() {
289        let matz = MatZ::new(2, 2);
290
291        let _ = MatZq::from((&matz, 2u8));
292        let _ = MatZq::from((&matz, 2u16));
293        let _ = MatZq::from((&matz, 2u32));
294        let _ = MatZq::from((&matz, 2u64));
295        let _ = MatZq::from((&matz, 2i8));
296        let _ = MatZq::from((&matz, 2i16));
297        let _ = MatZq::from((&matz, 2i32));
298        let _ = MatZq::from((&matz, 2i64));
299        let _ = MatZq::from((&matz, Z::from(2)));
300        let _ = MatZq::from((&matz, Modulus::from(2)));
301
302        let _ = MatZq::from((&matz, &2u8));
303        let _ = MatZq::from((&matz, &2u16));
304        let _ = MatZq::from((&matz, &2u32));
305        let _ = MatZq::from((&matz, &2u64));
306        let _ = MatZq::from((&matz, &2i8));
307        let _ = MatZq::from((&matz, &2i16));
308        let _ = MatZq::from((&matz, &2i32));
309        let _ = MatZq::from((&matz, &2i64));
310        let _ = MatZq::from((&matz, &Z::from(2)));
311        let _ = MatZq::from((&matz, &Modulus::from(2)));
312
313        let _ = MatZq::from((matz, Modulus::from(2)));
314    }
315}
316
317#[cfg(test)]
318mod test_from_str {
319    use crate::{integer::Z, integer_mod_q::MatZq, traits::MatrixGetEntry};
320    use std::str::FromStr;
321
322    /// Ensure that initialization works.
323    #[test]
324    fn init_works() {
325        let matrix_str_1 = &MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 6").unwrap();
326
327        let entry: Z = matrix_str_1.get_entry(0, 0).unwrap();
328
329        assert_eq!(1, entry);
330    }
331
332    /// Ensure that entries are correctly reduced.
333    #[test]
334    fn reduce_works() {
335        let matrix_str_1 = &MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 3").unwrap();
336
337        let entry: Z = matrix_str_1.get_entry(1, 1).unwrap();
338
339        assert_eq!(1, entry);
340    }
341
342    /// Ensure that initialization with positive numbers that are larger than [`i64`] works.
343    #[test]
344    fn init_works_large_numbers() {
345        let matrix_string = &MatZq::from_str(&format!(
346            "[[{}, 2, 3],[3, 4, 5]] mod {}",
347            u64::MAX - 1,
348            u64::MAX
349        ))
350        .unwrap();
351
352        let entry: Z = matrix_string.get_entry(0, 0).unwrap();
353
354        assert_eq!(u64::MAX - 1, entry);
355    }
356
357    /// Ensure that initialization with negative numbers that are larger than [`i64`] works.
358    #[test]
359    fn init_works_small_numbers() {
360        let matrix_string = &MatZq::from_str(&format!(
361            "[[-{}, 2, 3],[3, 4, 5]] mod {}",
362            u64::MAX - 1,
363            u64::MAX
364        ))
365        .unwrap();
366
367        let entry: Z = matrix_string.get_entry(0, 0).unwrap();
368
369        assert_eq!(1, entry);
370    }
371
372    /// Ensure that initialization with moduli that are larger than [`i64`] works.
373    #[test]
374    fn init_works_large_modulus() {
375        let matrix_string =
376            &MatZq::from_str(&format!("[[1, 2, 3],[3, 4, 5]] mod {}", u64::MAX)).unwrap();
377
378        let entry: Z = matrix_string.get_entry(0, 0).unwrap();
379
380        assert_eq!(1, entry);
381    }
382
383    /// Ensure that entries can have leading and trailing whitespaces.
384    #[test]
385    fn whitespaces_in_entries_works() {
386        let matrix_str_1 = &MatZq::from_str("[[  1, 2 ,  3  ],[3 , 4, 5 ]]  mod  6 ").unwrap();
387
388        let entry: Z = matrix_str_1.get_entry(0, 0).unwrap();
389
390        assert_eq!(1, entry);
391    }
392
393    /// Ensure that a wrong format causes an error.
394    #[test]
395    fn wrong_format_error() {
396        let matrix_str_1 = "[1, 2, 3],[3, 4, 5]] mod 6";
397        let matrix_str_2 = "[[1, 2, 3][3, 4, 5]] mod 6";
398        let matrix_str_3 = "[[1, 2, 3], 3, 4, 5] mod 6";
399        let matrix_str_4 = "[1, 2, 3, 4, 5] mod 6";
400        let matrix_str_5 = "[ [1, 2, 3],[3, 4, 5]] mod 6";
401        let matrix_str_6 = "[[1, 2, 3],[3, 4, 5]8] mod 6";
402        let matrix_str_7 = "[[1, 2, 3],[3, 4, 5]] md 6";
403        let matrix_str_8 = " mod 6";
404        let matrix_str_9 = "";
405        let matrix_str_10 = "[] mod 6";
406        let matrix_str_11 = "[[]] mod 6";
407
408        assert!(MatZq::from_str(matrix_str_1).is_err());
409        assert!(MatZq::from_str(matrix_str_2).is_err());
410        assert!(MatZq::from_str(matrix_str_3).is_err());
411        assert!(MatZq::from_str(matrix_str_4).is_err());
412        assert!(MatZq::from_str(matrix_str_5).is_err());
413        assert!(MatZq::from_str(matrix_str_6).is_err());
414        assert!(MatZq::from_str(matrix_str_7).is_err());
415        assert!(MatZq::from_str(matrix_str_8).is_err());
416        assert!(MatZq::from_str(matrix_str_9).is_err());
417        assert!(MatZq::from_str(matrix_str_10).is_err());
418        assert!(MatZq::from_str(matrix_str_11).is_err());
419    }
420}
421
422#[cfg(test)]
423/// Test the implementation of [`MatZq::from_utf8`] briefly.
424/// This module omits tests that were already provided for [`Z::from_bytes`]
425/// and [`crate::utils::parse::matrix_from_utf8_fill_bytes`].
426mod test_from_utf8 {
427    use super::{MatZq, Z};
428    use crate::traits::MatrixGetEntry;
429    use std::str::FromStr;
430
431    /// Ensure that the empty string results in a zero value.
432    #[test]
433    fn empty_string() {
434        let message = "";
435
436        let matrix = MatZq::from_utf8(message, 1, 1, 5).unwrap();
437        let value: Z = matrix.get_entry(0, 0).unwrap();
438
439        assert_eq!(Z::ZERO, value);
440    }
441
442    /// Ensures correct conversion of bytes and their order.
443    #[test]
444    fn conversion_and_order() {
445        let message = "{10_chars}";
446        let cmp_matrix =
447            MatZq::from_str("[[12667, 24368],[26723, 29281],[32115, 12336]] mod 65536").unwrap();
448
449        let matrix = MatZq::from_utf8(message, 3, 2, 65536).unwrap();
450
451        assert_eq!(cmp_matrix, matrix);
452    }
453
454    /// Ensures that if the modulus was chosen too small, that the function returns an error.
455    #[test]
456    fn modulus_too_small() {
457        let message = "1";
458
459        let matrix = MatZq::from_utf8(message, 1, 1, 3);
460
461        assert!(matrix.is_err());
462    }
463}