Skip to main content

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 flint3_sys::{_fmpz_mod_mat_reduce, fmpz_mat_set};
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 modulus = modulus.into();
129        let mut out = MatZq::new(matrix.get_num_rows(), matrix.get_num_columns(), &modulus);
130        unsafe {
131            fmpz_mat_set(&mut out.matrix, &matrix.matrix);
132            _fmpz_mod_mat_reduce(&mut out.matrix, modulus.get_fmpz_mod_ctx_struct());
133        }
134        out
135    }
136}
137
138impl<Mod: Into<Modulus>> From<(MatZ, Mod)> for MatZq {
139    /// Creates a [`MatZq`] from a [`MatZ`] and a value that implements [`Into<Modulus>`].
140    ///
141    /// Parameters:
142    /// - `matrix`: the matrix from which the entries are taken
143    /// - `modulus`: the modulus of the matrix
144    ///
145    /// Returns a new [`MatZq`] matrix with entries from the [`MatZ`] instance modulo `modulus`.
146    ///
147    /// # Examples
148    /// ```
149    /// use qfall_math::integer::MatZ;
150    /// use qfall_math::integer_mod_q::MatZq;
151    /// use std::str::FromStr;
152    ///
153    /// let m = MatZ::from_str("[[1, 2],[3, -1]]").unwrap();
154    ///
155    /// let a = MatZq::from((m, 17));
156    /// ```
157    fn from((matrix, modulus): (MatZ, Mod)) -> Self {
158        MatZq::from((&matrix, modulus))
159    }
160}
161
162impl From<&MatZq> for MatZq {
163    /// Alias for [`MatZq::clone`].
164    fn from(value: &MatZq) -> Self {
165        value.clone()
166    }
167}
168
169impl MatZq {
170    /// Create a [`MatZq`] from a [`String`], i.e. its UTF8-Encoding.
171    /// This function can only construct positive or zero integers, but not negative ones.
172    /// If the number of bytes and number of entries does not line up, we pad the message
173    /// with `'0'`s.
174    /// The inverse of this function is [`MatZq::to_utf8`].
175    ///
176    /// **WARNING:** This implementation requires the `modulus` to be larger than
177    /// any single entry in the matrix. This function will denote the same number of bytes
178    /// to every entry and sequentially move through your `message` to encode them.
179    /// If a decimal presentation of these bytes is ever larger than the specified `modulus`,
180    /// the function will return an error.
181    ///
182    /// Parameters:
183    /// - `message`: specifies the message that is transformed via its UTF8-Encoding
184    ///   to a new [`MatZq`] instance.
185    /// - `num_rows`: number of rows the new matrix should have
186    /// - `num_cols`: number of columns the new matrix should have
187    /// - `modulus`: specifies the modulus of the matrix, it is required to be larger
188    ///   than any entry of the matrix
189    ///
190    /// Returns a [`MatZq`] with corresponding entries to the message's UTF8-Encoding or
191    /// a [`ConversionError`](MathError::ConversionError) if the modulus isn't larger than
192    /// every single entry of the matrix after distributing the (potentially padded) UTF8-Bytes
193    /// equally over the matrix.
194    ///
195    /// # Examples
196    /// ```
197    /// use qfall_math::integer_mod_q::MatZq;
198    /// let message = "hello!";
199    ///  
200    /// let matrix = MatZq::from_utf8(&message, 3, 2, 257).unwrap();
201    /// ```
202    ///
203    /// # Errors and Failures
204    /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError)
205    ///   if the modulus isn't larger than the largest entry of the matrix after equally
206    ///   distributing the (potentially padded) UTF8-Conversion over the matrix.
207    ///
208    /// # Panics ...
209    /// - if the provided number of rows and columns are not suited to create a matrix.
210    ///   For further information see [`MatZq::new`].
211    pub fn from_utf8(
212        message: &str,
213        num_rows: impl TryInto<i64> + Display,
214        num_cols: impl TryInto<i64> + Display,
215        modulus: impl Into<Modulus>,
216    ) -> Result<Self, MathError> {
217        let (num_rows_i64, num_cols_i64) = evaluate_indices(num_rows, num_cols).unwrap();
218        let mut mat = MatZq::new(num_rows_i64, num_cols_i64, modulus);
219        let num_columns = mat.get_num_columns() as usize;
220        let nr_entries = mat.get_num_rows() as usize * num_columns;
221        let modulus_as_z = Z::from(mat.get_mod());
222
223        let (byte_vector, nr_bytes_per_entry) = matrix_from_utf8_fill_bytes(message, nr_entries);
224
225        // Fill rows going from left to right, entry by entry
226        for row in 0..mat.get_num_rows() as usize {
227            let offset_row = row * num_columns * nr_bytes_per_entry;
228            for col in 0..num_columns {
229                let entry_value = Z::from_bytes(
230                    &byte_vector[offset_row + nr_bytes_per_entry * col
231                        ..offset_row + nr_bytes_per_entry * (col + 1)],
232                );
233                if modulus_as_z > entry_value {
234                    unsafe { mat.set_entry_unchecked(row as i64, col as i64, entry_value) };
235                } else {
236                    return Err(MathError::ConversionError(
237                        "The provided modulus is smaller than the UTF8-Encoding of your message."
238                            .to_owned(),
239                    ));
240                }
241            }
242        }
243
244        Ok(mat)
245    }
246}
247
248#[cfg(test)]
249mod test_from_mat_z_modulus {
250    use crate::{
251        integer::{MatZ, Z},
252        integer_mod_q::{MatZq, Modulus},
253        traits::{MatrixDimensions, MatrixGetEntry, MatrixSetEntry},
254    };
255
256    /// Test if the dimensions are taken over correctly.
257    #[test]
258    fn dimensions() {
259        let matz = MatZ::new(15, 17);
260        let modulus = Modulus::from(17);
261
262        let matzq_1 = MatZq::from((&matz, &modulus));
263
264        assert_eq!(15, matzq_1.get_num_rows());
265        assert_eq!(17, matzq_1.get_num_columns());
266    }
267
268    /// Test if entries are taken over correctly.
269    #[test]
270    fn entries_taken_over_correctly() {
271        let mut matz = MatZ::new(2, 2);
272        let modulus = Modulus::from(u64::MAX);
273
274        matz.set_entry(0, 0, u64::MAX - 58).unwrap();
275        matz.set_entry(0, 1, -1).unwrap();
276
277        let matzq_1 = MatZq::from((&matz, &modulus));
278
279        let entry_1: Z = matzq_1.get_entry(0, 1).unwrap();
280        let entry_2: Z = matzq_1.get_entry(0, 0).unwrap();
281
282        assert_eq!(u64::MAX - 1, entry_1);
283        assert_eq!(u64::MAX - 58, entry_2);
284    }
285
286    /// Ensures that the function is still available for all values implementing
287    /// `Into<Modulus>`.
288    #[test]
289    fn availability() {
290        let matz = MatZ::new(2, 2);
291
292        let _ = MatZq::from((&matz, 2u8));
293        let _ = MatZq::from((&matz, 2u16));
294        let _ = MatZq::from((&matz, 2u32));
295        let _ = MatZq::from((&matz, 2u64));
296        let _ = MatZq::from((&matz, 2i8));
297        let _ = MatZq::from((&matz, 2i16));
298        let _ = MatZq::from((&matz, 2i32));
299        let _ = MatZq::from((&matz, 2i64));
300        let _ = MatZq::from((&matz, Z::from(2)));
301        let _ = MatZq::from((&matz, Modulus::from(2)));
302
303        let _ = MatZq::from((&matz, &2u8));
304        let _ = MatZq::from((&matz, &2u16));
305        let _ = MatZq::from((&matz, &2u32));
306        let _ = MatZq::from((&matz, &2u64));
307        let _ = MatZq::from((&matz, &2i8));
308        let _ = MatZq::from((&matz, &2i16));
309        let _ = MatZq::from((&matz, &2i32));
310        let _ = MatZq::from((&matz, &2i64));
311        let _ = MatZq::from((&matz, &Z::from(2)));
312        let _ = MatZq::from((&matz, &Modulus::from(2)));
313
314        let _ = MatZq::from((matz, Modulus::from(2)));
315    }
316}
317
318#[cfg(test)]
319mod test_from_str {
320    use crate::{integer::Z, integer_mod_q::MatZq, traits::MatrixGetEntry};
321    use std::str::FromStr;
322
323    /// Ensure that initialization works.
324    #[test]
325    fn init_works() {
326        let matrix_str_1 = &MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 6").unwrap();
327
328        let entry: Z = matrix_str_1.get_entry(0, 0).unwrap();
329
330        assert_eq!(1, entry);
331    }
332
333    /// Ensure that entries are correctly reduced.
334    #[test]
335    fn reduce_works() {
336        let matrix_str_1 = &MatZq::from_str("[[1, 2, 3],[3, 4, 5]] mod 3").unwrap();
337
338        let entry: Z = matrix_str_1.get_entry(1, 1).unwrap();
339
340        assert_eq!(1, entry);
341    }
342
343    /// Ensure that initialization with positive numbers that are larger than [`i64`] works.
344    #[test]
345    fn init_works_large_numbers() {
346        let matrix_string = &MatZq::from_str(&format!(
347            "[[{}, 2, 3],[3, 4, 5]] mod {}",
348            u64::MAX - 1,
349            u64::MAX
350        ))
351        .unwrap();
352
353        let entry: Z = matrix_string.get_entry(0, 0).unwrap();
354
355        assert_eq!(u64::MAX - 1, entry);
356    }
357
358    /// Ensure that initialization with negative numbers that are larger than [`i64`] works.
359    #[test]
360    fn init_works_small_numbers() {
361        let matrix_string = &MatZq::from_str(&format!(
362            "[[-{}, 2, 3],[3, 4, 5]] mod {}",
363            u64::MAX - 1,
364            u64::MAX
365        ))
366        .unwrap();
367
368        let entry: Z = matrix_string.get_entry(0, 0).unwrap();
369
370        assert_eq!(1, entry);
371    }
372
373    /// Ensure that initialization with moduli that are larger than [`i64`] works.
374    #[test]
375    fn init_works_large_modulus() {
376        let matrix_string =
377            &MatZq::from_str(&format!("[[1, 2, 3],[3, 4, 5]] mod {}", u64::MAX)).unwrap();
378
379        let entry: Z = matrix_string.get_entry(0, 0).unwrap();
380
381        assert_eq!(1, entry);
382    }
383
384    /// Ensure that entries can have leading and trailing whitespaces.
385    #[test]
386    fn whitespaces_in_entries_works() {
387        let matrix_str_1 = &MatZq::from_str("[[  1, 2 ,  3  ],[3 , 4, 5 ]]  mod  6 ").unwrap();
388
389        let entry: Z = matrix_str_1.get_entry(0, 0).unwrap();
390
391        assert_eq!(1, entry);
392    }
393
394    /// Ensure that a wrong format causes an error.
395    #[test]
396    fn wrong_format_error() {
397        let matrix_str_1 = "[1, 2, 3],[3, 4, 5]] mod 6";
398        let matrix_str_2 = "[[1, 2, 3][3, 4, 5]] mod 6";
399        let matrix_str_3 = "[[1, 2, 3], 3, 4, 5] mod 6";
400        let matrix_str_4 = "[1, 2, 3, 4, 5] mod 6";
401        let matrix_str_5 = "[ [1, 2, 3],[3, 4, 5]] mod 6";
402        let matrix_str_6 = "[[1, 2, 3],[3, 4, 5]8] mod 6";
403        let matrix_str_7 = "[[1, 2, 3],[3, 4, 5]] md 6";
404        let matrix_str_8 = " mod 6";
405        let matrix_str_9 = "";
406        let matrix_str_10 = "[] mod 6";
407        let matrix_str_11 = "[[]] mod 6";
408
409        assert!(MatZq::from_str(matrix_str_1).is_err());
410        assert!(MatZq::from_str(matrix_str_2).is_err());
411        assert!(MatZq::from_str(matrix_str_3).is_err());
412        assert!(MatZq::from_str(matrix_str_4).is_err());
413        assert!(MatZq::from_str(matrix_str_5).is_err());
414        assert!(MatZq::from_str(matrix_str_6).is_err());
415        assert!(MatZq::from_str(matrix_str_7).is_err());
416        assert!(MatZq::from_str(matrix_str_8).is_err());
417        assert!(MatZq::from_str(matrix_str_9).is_err());
418        assert!(MatZq::from_str(matrix_str_10).is_err());
419        assert!(MatZq::from_str(matrix_str_11).is_err());
420    }
421}
422
423#[cfg(test)]
424/// Test the implementation of [`MatZq::from_utf8`] briefly.
425/// This module omits tests that were already provided for [`Z::from_bytes`]
426/// and [`crate::utils::parse::matrix_from_utf8_fill_bytes`].
427mod test_from_utf8 {
428    use super::{MatZq, Z};
429    use crate::traits::MatrixGetEntry;
430    use std::str::FromStr;
431
432    /// Ensure that the empty string results in a zero value.
433    #[test]
434    fn empty_string() {
435        let message = "";
436
437        let matrix = MatZq::from_utf8(message, 1, 1, 5).unwrap();
438        let value: Z = matrix.get_entry(0, 0).unwrap();
439
440        assert_eq!(Z::ZERO, value);
441    }
442
443    /// Ensures correct conversion of bytes and their order.
444    #[test]
445    fn conversion_and_order() {
446        let message = "{10_chars}";
447        let cmp_matrix =
448            MatZq::from_str("[[12667, 24368],[26723, 29281],[32115, 12336]] mod 65536").unwrap();
449
450        let matrix = MatZq::from_utf8(message, 3, 2, 65536).unwrap();
451
452        assert_eq!(cmp_matrix, matrix);
453    }
454
455    /// Ensures that if the modulus was chosen too small, that the function returns an error.
456    #[test]
457    fn modulus_too_small() {
458        let message = "1";
459
460        let matrix = MatZq::from_utf8(message, 1, 1, 3);
461
462        assert!(matrix.is_err());
463    }
464}