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