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}