qfall_math/integer/mat_poly_over_z/from.rs
1// Copyright © 2023 Marvin Beckmann, Sven Moog
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 [`MatPolyOverZ`] value from other types.
10//!
11//! The explicit functions contain the documentation.
12
13use super::MatPolyOverZ;
14use crate::{
15 error::MathError,
16 integer::{MatZ, PolyOverZ},
17 macros::for_others::implement_for_owned,
18 traits::*,
19 utils::{dimensions::find_matrix_dimensions, parse::parse_matrix_string},
20};
21use std::str::FromStr;
22
23impl FromStr for MatPolyOverZ {
24 type Err = MathError;
25
26 /// Creates a [`MatPolyOverZ`] matrix from a [`String`].
27 ///
28 /// **Warning**: Each entry is parsed as a [`PolyOverZ`] object.
29 /// If an entry string starts with a correctly formatted [`PolyOverZ`] object,
30 /// the rest of this entry string is ignored. This means that the entry input
31 /// string `"4 0 1 2 3"` is the same as `"4 0 1 2 3 4 5 6 7"`.
32 ///
33 /// Parameters:
34 /// - `string`: the matrix of form: `"[[poly_1, poly_2, poly_3],[poly_4, poly_5, poly_6]]"`
35 /// for a 2x3 matrix where first three polynomials are in the first row
36 /// and the second three are in the second row.
37 ///
38 /// Returns a [`MatPolyOverZ`] or an error if the matrix is not formatted in a suitable way,
39 /// the number of rows or columns is too large (must fit into [`i64`]),
40 /// the number of entries in rows is unequal, or if an entry is not formatted correctly.
41 ///
42 /// # Examples
43 /// ```
44 /// use qfall_math::integer::MatPolyOverZ;
45 /// use std::str::FromStr;
46 ///
47 /// let matrix = MatPolyOverZ::from_str("[[0, 1 42, 2 42 24],[3 17 24 42, 1 17, 1 42]]").unwrap();
48 /// ```
49 ///
50 /// ```
51 /// use qfall_math::integer::MatPolyOverZ;
52 /// use std::str::FromStr;
53 ///
54 /// let str_1 = "[[0, 1 42, 2 42 24],[3 17 24 42, 1 17, 1 42]]";
55 /// let matrix = MatPolyOverZ::from_str(str_1).unwrap();
56 /// ```
57 ///
58 /// ```
59 /// use qfall_math::integer::MatPolyOverZ;
60 /// use std::str::FromStr;
61 ///
62 /// let string = String::from("[[0, 1 42, 2 42 24],[3 17 24 42, 1 17, 1 42]]");
63 /// let matrix = MatPolyOverZ::from_str(&string).unwrap();
64 /// ```
65 ///
66 /// # Errors and Failures
67 /// - Returns a [`MathError`] of type [`MathError::StringConversionError`],
68 /// - if the matrix is not formatted in a suitable way,
69 /// - if the number of rows or columns is too large (must fit into i64),
70 /// - if the number of entries in rows is unequal, or
71 /// - if an entry is not formatted correctly.
72 /// For further information see [`PolyOverZ::from_str`].
73 ///
74 /// # Panics ...
75 /// - if the provided number of rows and columns are not suited to create a matrix.
76 /// For further information see [`MatPolyOverZ::new`].
77 fn from_str(string: &str) -> Result<Self, MathError> {
78 let string_matrix = parse_matrix_string(string)?;
79 let (num_rows, num_cols) = find_matrix_dimensions(&string_matrix)?;
80 let mut matrix = MatPolyOverZ::new(num_rows, num_cols);
81
82 // fill entries of matrix according to entries in string_matrix
83 for (row_num, row) in string_matrix.iter().enumerate() {
84 for (col_num, entry) in row.iter().enumerate() {
85 let z_entry = PolyOverZ::from_str(entry)?;
86 matrix.set_entry(row_num, col_num, z_entry)?;
87 }
88 }
89 Ok(matrix)
90 }
91}
92
93impl From<&MatZ> for MatPolyOverZ {
94 /// Creates a [`MatPolyOverZ`] with constant polynomials defined by a [`MatZ`].
95 ///
96 /// Parameters
97 /// - `matrix`: a matrix with constant integers.
98 ///
99 /// Returns a matrix of polynomial that all have the first coefficient
100 /// set to the value in the matrix.
101 ///
102 /// # Examples
103 /// ```
104 /// use qfall_math::integer::{MatZ, MatPolyOverZ};
105 ///
106 /// let mat_z = MatZ::identity(10, 10);
107 /// let mat_poly = MatPolyOverZ::from(&mat_z);
108 /// ```
109 fn from(matrix: &MatZ) -> Self {
110 let num_rows = matrix.get_num_rows();
111 let num_columns = matrix.get_num_columns();
112 let mut out = MatPolyOverZ::new(num_rows, num_columns);
113
114 for row in 0..num_rows {
115 for column in 0..num_columns {
116 unsafe {
117 out.set_entry_unchecked(
118 row,
119 column,
120 PolyOverZ::from(matrix.get_entry_unchecked(row, column)),
121 )
122 };
123 }
124 }
125
126 out
127 }
128}
129
130implement_for_owned!(MatZ, MatPolyOverZ, From);
131
132impl From<&MatPolyOverZ> for MatPolyOverZ {
133 /// Alias for [`MatPolyOverZ::clone`].
134 fn from(value: &MatPolyOverZ) -> Self {
135 value.clone()
136 }
137}
138
139#[cfg(test)]
140mod test_from_str {
141 use crate::{integer::MatPolyOverZ, traits::MatrixGetEntry};
142 use std::str::FromStr;
143
144 /// Ensure that initialization works.
145 #[test]
146 fn init_works() {
147 let matrix_str = "[[1 42, 2 24 42, 2 24 42],[2 24 42, 2 24 42, 2 24 42]]";
148
149 assert_eq!(
150 "1 42",
151 MatPolyOverZ::from_str(matrix_str)
152 .unwrap()
153 .get_entry(0, 0)
154 .unwrap()
155 .to_string(),
156 );
157 }
158
159 /// Ensure that initialization with polynomials with positive coefficients that are
160 /// larger than [`i64`] works.
161 #[test]
162 fn init_works_large_numbers() {
163 let entry = format!("1 {}", u64::MAX);
164 let matrix_str_1 =
165 format!("[[{entry}, 2 24 42, 2 24 42],[2 24 42, 2 24 42, 2 24 42]]");
166
167 assert_eq!(
168 entry,
169 MatPolyOverZ::from_str(&matrix_str_1)
170 .unwrap()
171 .get_entry(0, 0)
172 .unwrap()
173 .to_string(),
174 );
175 }
176
177 /// Ensure that initialization with polynomials with negative coefficients that
178 /// are larger than [`i64`] works.
179 #[test]
180 fn init_works_small_numbers() {
181 let entry = format!("1 -{}", u64::MAX);
182 let matrix_str_1 =
183 format!("[[{entry}, 2 24 42, 2 24 42],[2 24 42, 2 24 42, 2 24 42]]",);
184
185 assert_eq!(
186 entry,
187 MatPolyOverZ::from_str(&matrix_str_1)
188 .unwrap()
189 .get_entry(0, 0)
190 .unwrap()
191 .to_string(),
192 );
193 }
194
195 /// Ensure that entries can have whitespaces leading and trailing.
196 #[test]
197 fn whitespaces_in_entries_works() {
198 let entry = format!("1 {} ", u64::MAX);
199 let matrix_str_1 = format!(
200 "[[{entry}, 2 24 42, 2 24 42 ],[ 2 24 42, 2 24 42 , 2 24 42]]",
201 );
202
203 assert_eq!(
204 format!("1 {}", u64::MAX),
205 MatPolyOverZ::from_str(&matrix_str_1)
206 .unwrap()
207 .get_entry(0, 0)
208 .unwrap()
209 .to_string(),
210 );
211 }
212
213 /// Ensure that a wrong format causes an error.
214 #[test]
215 fn wrong_format_error() {
216 let matrix_str_1 = "[[1 42, 224 42, 2 24 42][2 24 42, 2 24 42, 2 24 42]]";
217 let matrix_str_2 = "[[1 42, 224 42, 2 24 42], 2 24 42, 2 24 42, 2 24 42]]";
218 let matrix_str_3 = "[1 42, 224 42, 2 24 42, 2 24 42, 2 24 42, 2 24 42]";
219 let matrix_str_4 = "[[1 42, 224 42, 2 24 42, 2 24 42, 2 24 42, 2 24 42]";
220 let matrix_str_5 = "[ [1 42, 224 42, 2 242, 2 24 42, 2 24 42]]";
221 let matrix_str_6 = "[[1 42, 224 42, 2 24 42],[2 24 42, 2 24 42, 2 24 4]2]";
222 let matrix_str_7 = "";
223 let matrix_str_8 = "[]";
224 let matrix_str_9 = "[[]]";
225
226 assert!(MatPolyOverZ::from_str(matrix_str_1).is_err());
227 assert!(MatPolyOverZ::from_str(matrix_str_2).is_err());
228 assert!(MatPolyOverZ::from_str(matrix_str_3).is_err());
229 assert!(MatPolyOverZ::from_str(matrix_str_4).is_err());
230 assert!(MatPolyOverZ::from_str(matrix_str_5).is_err());
231 assert!(MatPolyOverZ::from_str(matrix_str_6).is_err());
232 assert!(MatPolyOverZ::from_str(matrix_str_7).is_err());
233 assert!(MatPolyOverZ::from_str(matrix_str_8).is_err());
234 assert!(MatPolyOverZ::from_str(matrix_str_9).is_err());
235 }
236}
237
238#[cfg(test)]
239mod test_from_matz {
240 use super::*;
241
242 /// Ensure that [`MatPolyOverZ`] can be initialized from [`MatZ`] with small
243 /// values. Validate that the correct [`MatPolyOverZ`] is created.
244 #[test]
245 fn small() {
246 let matz_str = "[[1, 2, 3],[4, 5, 6]]";
247 let matz = MatZ::from_str(matz_str).unwrap();
248
249 let mat_poly = MatPolyOverZ::from(&matz);
250
251 let poly_mat_cmp_str = "[[1 1, 1 2, 1 3],[1 4, 1 5, 1 6]]";
252 let mat_poly_cmp = MatPolyOverZ::from_str(poly_mat_cmp_str).unwrap();
253 assert_eq!(mat_poly, mat_poly_cmp);
254 }
255
256 /// Ensure that [`MatPolyOverZ`] can be initialized from [`MatZ`] with large
257 /// values. Validate that the correct [`MatPolyOverZ`] is created.
258 #[test]
259 fn large() {
260 let matz_str = format!("[[{}],[{}]]", u64::MAX, i64::MIN);
261 let matz = MatZ::from_str(&matz_str).unwrap();
262
263 let mat_poly = MatPolyOverZ::from(&matz);
264
265 let poly_mat_cmp_str = format!("[[1 {}],[1 {}]]", u64::MAX, i64::MIN);
266 let mat_poly_cmp = MatPolyOverZ::from_str(&poly_mat_cmp_str).unwrap();
267 assert_eq!(mat_poly, mat_poly_cmp);
268 }
269
270 /// Ensure that a 100x100 [`MatPolyOverZ`] can be initialized from [`MatZ`]
271 /// with `0` coefficients.
272 /// Validate that the correct [`MatPolyOverZ`] is created.
273 #[test]
274 fn zero() {
275 let matz = MatZ::new(100, 100);
276
277 let mat_poly = MatPolyOverZ::from(&matz);
278
279 let mat_poly_cmp = MatPolyOverZ::new(100, 100);
280 assert_eq!(mat_poly, mat_poly_cmp);
281 }
282
283 /// Ensure that the conversion works for owned values.
284 #[test]
285 fn availability() {
286 let m = MatZ::from_str("[[1, 2],[3, -1]]").unwrap();
287
288 let _ = MatPolyOverZ::from(m);
289 }
290}