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}