qfall_math/integer/mat_z/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 [`MatZ`] matrix from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::MatZ;
14use crate::{
15 error::MathError,
16 integer::Z,
17 traits::{MatrixDimensions, MatrixSetEntry},
18 utils::{
19 dimensions::find_matrix_dimensions,
20 index::evaluate_indices,
21 parse::{matrix_from_utf8_fill_bytes, parse_matrix_string},
22 },
23};
24use std::{fmt::Display, str::FromStr};
25
26impl FromStr for MatZ {
27 type Err = MathError;
28
29 /// Creates a [`MatZ`] matrix with entries in [`Z`] from a [`String`].
30 ///
31 /// Parameters:
32 /// - `string`: the matrix of form: `"[[1, 2, 3],[4, 5, 6]]"`
33 /// for a 2x3 matrix with entries 1, 2, 3 in the first row and 4, 5, 6
34 /// in the second row.
35 ///
36 /// Returns a [`MatZ`] or an error if the matrix is not formatted in a suitable way,
37 /// the number of rows or columns is too large (must fit into [`i64`]),
38 /// the number of entries in rows is unequal or if an entry is not formatted correctly.
39 ///
40 /// # Examples
41 /// ```
42 /// use qfall_math::integer::MatZ;
43 /// use std::str::FromStr;
44 ///
45 /// let string = String::from("[[1, 2, 3],[3, 4, 5]]");
46 /// let matrix = MatZ::from_str(&string).unwrap();
47 /// ```
48 ///
49 /// # Errors and Failures
50 /// - Returns a [`MathError`] of type [`StringConversionError`](MathError::StringConversionError)
51 /// - if the matrix is not formatted in a suitable way,
52 /// - if the number of rows or columns is too large (must fit into i64),
53 /// - if the number of entries in rows is unequal, or
54 /// - if an entry is not formatted correctly.
55 /// For further information see [`Z::from_str`].
56 ///
57 /// # Panics ...
58 /// - if the provided number of rows and columns are not suited to create a matrix.
59 /// For further information see [`MatZ::new`].
60 fn from_str(string: &str) -> Result<Self, MathError> {
61 let string_matrix = parse_matrix_string(string)?;
62 let (num_rows, num_cols) = find_matrix_dimensions(&string_matrix)?;
63 let mut matrix = MatZ::new(num_rows, num_cols);
64
65 // fill entries of matrix according to entries in string_matrix
66 for (row_num, row) in string_matrix.iter().enumerate() {
67 for (col_num, entry) in row.iter().enumerate() {
68 let z_entry = Z::from_str(entry)?;
69 matrix.set_entry(row_num, col_num, z_entry)?;
70 }
71 }
72 Ok(matrix)
73 }
74}
75
76impl From<&MatZ> for MatZ {
77 /// Alias for [`MatZ::clone`].
78 fn from(value: &MatZ) -> Self {
79 value.clone()
80 }
81}
82
83impl MatZ {
84 /// Create a [`MatZ`] from a [`String`], i.e. its UTF8-Encoding.
85 /// This function can only construct positive or zero integers, but not negative ones.
86 /// If the number of bytes and number of entries does not line up, we pad the message
87 /// with `'0'`s.
88 /// The inverse of this function is [`MatZ::to_utf8`].
89 ///
90 /// Parameters:
91 /// - `message`: specifies the message that is transformed via its UTF8-Encoding
92 /// to a new [`MatZ`] instance.
93 /// - `num_rows`: number of rows the new matrix should have
94 /// - `num_cols`: number of columns the new matrix should have
95 ///
96 /// Returns a [`MatZ`] with corresponding entries to the message's UTF8-Encoding.
97 ///
98 /// # Examples
99 /// ```
100 /// use qfall_math::integer::MatZ;
101 /// let message = "hello!";
102 ///
103 /// let matrix = MatZ::from_utf8(&message, 2, 1);
104 /// ```
105 ///
106 /// # Panics ...
107 /// - if the provided number of rows and columns are not suited to create a matrix.
108 /// For further information see [`MatZ::new`].
109 pub fn from_utf8(
110 message: &str,
111 num_rows: impl TryInto<i64> + Display,
112 num_cols: impl TryInto<i64> + Display,
113 ) -> Self {
114 let (num_rows_i64, num_cols_i64) = evaluate_indices(num_rows, num_cols).unwrap();
115 let mut mat = MatZ::new(num_rows_i64, num_cols_i64);
116 let num_columns = mat.get_num_columns() as usize;
117 let nr_entries = mat.get_num_rows() as usize * num_columns;
118
119 // This error can't be triggered as no modulus is provided.
120 let (byte_vector, nr_bytes_per_entry) = matrix_from_utf8_fill_bytes(message, nr_entries);
121
122 // Fill rows going from left to right, entry by entry
123 for row in 0..mat.get_num_rows() as usize {
124 let offset_row = row * num_columns * nr_bytes_per_entry;
125 for col in 0..num_columns {
126 let entry_value = Z::from_bytes(
127 &byte_vector[offset_row + nr_bytes_per_entry * col
128 ..offset_row + nr_bytes_per_entry * (col + 1)],
129 );
130 unsafe { mat.set_entry_unchecked(row as i64, col as i64, entry_value) };
131 }
132 }
133
134 mat
135 }
136}
137
138#[cfg(test)]
139mod test_from_str {
140 use crate::{
141 integer::{MatZ, Z},
142 traits::MatrixGetEntry,
143 };
144 use std::str::FromStr;
145
146 /// Ensure that initialization works.
147 #[test]
148 fn init_works() {
149 let matrix_str = "[[1, 2, 3],[3, 4, 5]]";
150
151 assert_eq!(
152 Z::ONE,
153 MatZ::from_str(matrix_str).unwrap().get_entry(0, 0).unwrap()
154 );
155 }
156
157 /// Ensure that initialization with positive numbers that are larger than [`i64`] works.
158 #[test]
159 fn init_works_large_numbers() {
160 let matrix_string = format!("[[{}, 2, 3],[3, 4, 5]]", u64::MAX);
161
162 assert_eq!(
163 Z::from(u64::MAX),
164 MatZ::from_str(&matrix_string)
165 .unwrap()
166 .get_entry(0, 0)
167 .unwrap()
168 );
169 }
170
171 /// Ensure that initialization with negative numbers that are larger than [`i64`] works.
172 #[test]
173 fn init_works_small_numbers() {
174 let matrix_string = format!("[[-{}, 2, 3],[3, 4, 5]]", u64::MAX);
175
176 let entry = format!("-{}", u64::MAX);
177
178 assert_eq!(
179 Z::from_str(&entry).unwrap(),
180 MatZ::from_str(&matrix_string)
181 .unwrap()
182 .get_entry(0, 0)
183 .unwrap()
184 );
185 }
186
187 /// Ensure that entries can have leading and trailing whitespaces.
188 #[test]
189 fn whitespaces_in_entries_works() {
190 let matrix_str = "[[ 1, 2 , 3 ],[3 , 4, 5 ]]";
191
192 assert_eq!(
193 Z::ONE,
194 MatZ::from_str(matrix_str).unwrap().get_entry(0, 0).unwrap()
195 );
196 }
197
198 /// Ensure that a wrong format causes an error.
199 #[test]
200 fn wrong_format_error() {
201 let matrix_str_1 = "[1, 2, 3],[3, 4, 5]]";
202 let matrix_str_2 = "[[1, 2, 3][3, 4, 5]]";
203 let matrix_str_3 = "[[1, 2, 3], 3, 4, 5]";
204 let matrix_str_4 = "[1, 2, 3, 4, 5]";
205 let matrix_str_5 = "[ [1, 2, 3],[3, 4, 5]]";
206 let matrix_str_6 = "[[1, 2, 3],[3, 4, 5]8]";
207 let matrix_str_7 = "";
208 let matrix_str_8 = "[]";
209 let matrix_str_9 = "[[]]";
210
211 assert!(MatZ::from_str(matrix_str_1).is_err());
212 assert!(MatZ::from_str(matrix_str_2).is_err());
213 assert!(MatZ::from_str(matrix_str_3).is_err());
214 assert!(MatZ::from_str(matrix_str_4).is_err());
215 assert!(MatZ::from_str(matrix_str_5).is_err());
216 assert!(MatZ::from_str(matrix_str_6).is_err());
217 assert!(MatZ::from_str(matrix_str_7).is_err());
218 assert!(MatZ::from_str(matrix_str_8).is_err());
219 assert!(MatZ::from_str(matrix_str_9).is_err());
220 }
221}
222
223#[cfg(test)]
224/// Test the implementation of [`MatZ::from_utf8`] briefly.
225/// This module omits tests that were already provided for [`Z::from_bytes`]
226/// and [`crate::utils::parse::matrix_from_utf8_fill_bytes`].
227mod test_from_utf8 {
228 use super::{MatZ, Z};
229 use crate::traits::MatrixGetEntry;
230 use std::str::FromStr;
231
232 /// Ensures that a wide range of (special) characters are transformed correctly.
233 #[test]
234 fn characters() {
235 let message = "flag{text#1234567890! a_zA-Z$€?/:;,.<>+*}";
236
237 let matrix = MatZ::from_utf8(message, 1, 1);
238 let value = matrix.get_entry(0, 0).unwrap();
239
240 // easy trick s.t. we don't have to initialize a huge [`Z`] value
241 // while this test should still fail if the value changes
242 let value_zq = value % 65537;
243
244 assert_eq!(Z::from(58285), value_zq);
245 }
246
247 /// Ensure that the empty string results in a zero value.
248 #[test]
249 fn empty_string() {
250 let message = "";
251
252 let matrix = MatZ::from_utf8(message, 1, 1);
253 let value = matrix.get_entry(0, 0).unwrap();
254
255 assert_eq!(Z::ZERO, value);
256 }
257
258 /// Ensures correct conversion of bytes and their order.
259 #[test]
260 fn conversion_and_order() {
261 let message = "{10_chars}";
262 let cmp_matrix = MatZ::from_str("[[12667, 24368],[26723, 29281],[32115, 12336]]").unwrap();
263
264 let matrix = MatZ::from_utf8(message, 3, 2);
265
266 assert_eq!(cmp_matrix, matrix);
267 }
268}