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}