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}