qfall_math/rational/mat_q/
get.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 get information about a [`MatQ`] matrix.
10
11use super::MatQ;
12use crate::rational::Q;
13use crate::traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix};
14use flint_sys::fmpq_mat::{fmpq_mat_init_set, fmpq_mat_window_clear, fmpq_mat_window_init};
15use flint_sys::{
16    fmpq::{fmpq, fmpq_set},
17    fmpq_mat::fmpq_mat_entry,
18};
19use std::mem::MaybeUninit;
20
21impl MatrixDimensions for MatQ {
22    /// Returns the number of rows of the matrix as a [`i64`].
23    ///
24    /// # Examples
25    /// ```
26    /// use qfall_math::rational::MatQ;
27    /// use qfall_math::traits::*;
28    ///
29    /// let matrix = MatQ::new(5, 6);
30    /// let rows = matrix.get_num_rows();
31    /// ```
32    fn get_num_rows(&self) -> i64 {
33        self.matrix.r
34    }
35
36    /// Returns the number of columns of the matrix as a [`i64`].
37    ///
38    /// # Examples
39    /// ```
40    /// use qfall_math::rational::MatQ;
41    /// use qfall_math::traits::*;
42    ///
43    /// let matrix = MatQ::new(5, 6);
44    /// let columns = matrix.get_num_columns();
45    /// ```
46    fn get_num_columns(&self) -> i64 {
47        self.matrix.c
48    }
49}
50
51impl MatrixGetEntry<Q> for MatQ {
52    /// Outputs the [`Q`] value of a specific matrix entry
53    /// without checking whether it's part of the matrix.
54    ///
55    /// Parameters:
56    /// - `row`: specifies the row in which the entry is located
57    /// - `column`: specifies the column in which the entry is located
58    ///
59    /// Returns the [`Q`] value of the matrix at the position of the given
60    /// row and column.
61    ///
62    /// # Safety
63    /// To use this function safely, make sure that the selected entry is part
64    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
65    /// occur.
66    ///
67    /// # Examples
68    /// ```
69    /// use qfall_math::rational::{MatQ, Q};
70    /// use qfall_math::traits::MatrixGetEntry;
71    /// use std::str::FromStr;
72    ///
73    /// let matrix = MatQ::from_str("[[1, 2, 3/4],[4, 5, 6],[7, 8, 9]]").unwrap();
74    ///
75    /// assert_eq!(unsafe { matrix.get_entry_unchecked(0, 2) }, Q::from((3, 4)));
76    /// assert_eq!(unsafe { matrix.get_entry_unchecked(2, 1) }, Q::from(8));
77    /// assert_eq!(unsafe { matrix.get_entry_unchecked(2, 1) }, Q::from(8));
78    /// ```
79    unsafe fn get_entry_unchecked(&self, row: i64, column: i64) -> Q {
80        let mut copy = Q::default();
81        let entry = unsafe { fmpq_mat_entry(&self.matrix, row, column) };
82        unsafe { fmpq_set(&mut copy.value, entry) };
83
84        copy
85    }
86}
87
88impl MatrixGetSubmatrix for MatQ {
89    /// Returns a deep copy of the submatrix defined by the given parameters
90    /// and does not check the provided dimensions.
91    /// There is also a safe version of this function that checks the input.
92    ///
93    /// Parameters:
94    /// `row_1`: the starting row of the submatrix
95    /// `row_2`: the ending row of the submatrix
96    /// `col_1`: the starting column of the submatrix
97    /// `col_2`: the ending column of the submatrix
98    ///
99    /// Returns the submatrix from `(row_1, col_1)` to `(row_2, col_2)`(exclusively).
100    ///
101    /// # Examples
102    /// ```
103    /// use qfall_math::{rational::MatQ, traits::MatrixGetSubmatrix};
104    /// use std::str::FromStr;
105    ///
106    /// let mat = MatQ::identity(3, 3);
107    ///
108    /// let sub_mat_1 = mat.get_submatrix(0, 2, 1, 1).unwrap();
109    /// let sub_mat_2 = mat.get_submatrix(0, -1, 1, -2).unwrap();
110    /// let sub_mat_3 = unsafe{mat.get_submatrix_unchecked(0, 3, 1, 2)};
111    ///
112    /// let e_2 = MatQ::from_str("[[0],[1],[0]]").unwrap();
113    /// assert_eq!(e_2, sub_mat_1);
114    /// assert_eq!(e_2, sub_mat_2);
115    /// assert_eq!(e_2, sub_mat_3);
116    /// ```
117    ///
118    /// # Safety
119    /// To use this function safely, make sure that the selected submatrix is part
120    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
121    /// occur.
122    unsafe fn get_submatrix_unchecked(
123        &self,
124        row_1: i64,
125        row_2: i64,
126        col_1: i64,
127        col_2: i64,
128    ) -> Self {
129        let mut window = MaybeUninit::uninit();
130        // The memory for the elements of window is shared with self.
131        unsafe {
132            fmpq_mat_window_init(
133                window.as_mut_ptr(),
134                &self.matrix,
135                row_1,
136                col_1,
137                row_2,
138                col_2,
139            )
140        };
141        let mut window_copy = MaybeUninit::uninit();
142        unsafe {
143            // Deep clone of the content of the window
144            fmpq_mat_init_set(window_copy.as_mut_ptr(), window.as_ptr());
145            // Clears the matrix window and releases any memory that it uses. Note that
146            // the memory to the underlying matrix that window points to is not freed
147            fmpq_mat_window_clear(window.as_mut_ptr());
148        }
149        MatQ {
150            matrix: unsafe { window_copy.assume_init() },
151        }
152    }
153}
154
155impl MatQ {
156    /// Efficiently collects all [`fmpq`]s in a [`MatQ`] without cloning them.
157    ///
158    /// Hence, the values on the returned [`Vec`] are intended for short-term use
159    /// as the access to [`fmpq`] values could lead to memory leaks or modified values
160    /// once the [`MatQ`] instance was modified or dropped.
161    ///
162    /// # Examples
163    /// ```compile_fail
164    /// use qfall_math::rational::MatQ;
165    /// use std::str::FromStr;
166    ///
167    /// let mat = MatQ::from_str("[[1/1, 2],[3/1, 4],[5/1, 6]]").unwrap();
168    ///
169    /// let fmpq_entries = mat.collect_entries();
170    /// ```
171    ///
172    /// # Safety
173    /// The user has to ensure that all entries are within the matrix dimensions.
174    /// Otherwise, memory leaks can occur and no guarantees are given.
175    pub(crate) fn collect_entries(&self) -> Vec<fmpq> {
176        let mut entries: Vec<fmpq> =
177            Vec::with_capacity((self.get_num_rows() * self.get_num_columns()) as usize);
178
179        for row in 0..self.get_num_rows() {
180            for col in 0..self.get_num_columns() {
181                // efficiently get entry without cloning the entry itself
182                let entry = unsafe { *fmpq_mat_entry(&self.matrix, row, col) };
183                entries.push(entry);
184            }
185        }
186
187        entries
188    }
189
190    /// Returns a copy of all entries of `self` as [`f64`] values in [`Vec`]tors s.t.
191    /// the resulting vector can be used as `entries_f64[i][j]` to
192    /// access the entry in row `i` and column `j`.
193    ///
194    /// **WARNING:** The return is system dependent if any entry of the matrix is
195    /// is too large or too small to fit in an [`f64`], i.e. the value should be within
196    /// [`f64::MIN`] and [`f64::MAX`]. It the entry can't be represented exactly, it will
197    /// be rounded towards zero.
198    ///
199    /// # Examples
200    /// ```
201    /// use qfall_math::rational::MatQ;
202    /// use std::str::FromStr;
203    ///
204    /// let mat = MatQ::from_str("[[1/1, 2],[3/1, 4],[5/1, 6]]").unwrap();
205    ///
206    /// let entries_f64 = mat.collect_entries_f64();
207    ///
208    /// assert_eq!(entries_f64[0][1], 2.0);
209    /// ```
210    pub fn collect_entries_f64(&self) -> Vec<Vec<f64>> {
211        let num_rows = self.get_num_rows() as usize;
212        let num_cols = self.get_num_columns() as usize;
213
214        let mut entries: Vec<Vec<f64>> = vec![Vec::with_capacity(num_cols); num_rows];
215
216        for (i, row) in entries.iter_mut().enumerate() {
217            for j in 0..num_cols {
218                // efficiently get entry without cloning the entry itself
219                let entry = unsafe {
220                    flint_sys::fmpq::fmpq_get_d(fmpq_mat_entry(&self.matrix, i as i64, j as i64))
221                };
222                row.push(entry);
223            }
224        }
225
226        entries
227    }
228}
229
230#[cfg(test)]
231mod test_get_entry {
232    use super::Q;
233    use crate::{
234        rational::MatQ,
235        traits::{MatrixGetEntry, MatrixSetEntry},
236    };
237    use std::str::FromStr;
238
239    /// Ensure that getting entries works with large large numerators and denominators.
240    #[test]
241    fn max_int_positive() {
242        let mut matrix = MatQ::new(5, 10);
243        let value_1 = Q::from(i64::MAX);
244        let value_2 = Q::from((1, i64::MAX));
245        matrix.set_entry(0, 0, value_1).unwrap();
246        matrix.set_entry(1, 1, value_2).unwrap();
247
248        let entry_1 = matrix.get_entry(0, 0).unwrap();
249        let entry_2 = matrix.get_entry(1, 1).unwrap();
250
251        assert_eq!(Q::from(i64::MAX), entry_1);
252        assert_eq!(Q::from((1, i64::MAX)), entry_2);
253    }
254
255    /// Ensure that getting entries works with large numerators and denominators (larger than [`i64`]).
256    #[test]
257    fn large_positive() {
258        let mut matrix = MatQ::new(5, 10);
259        let value_1 = Q::from(u64::MAX);
260        let value_2 = Q::from((1, u64::MAX));
261        matrix.set_entry(0, 0, value_1).unwrap();
262        matrix.set_entry(1, 1, value_2).unwrap();
263
264        let entry_1 = matrix.get_entry(0, 0).unwrap();
265        let entry_2 = matrix.get_entry(1, 1).unwrap();
266
267        assert_eq!(Q::from(u64::MAX), entry_1);
268        assert_eq!(Q::from((1, u64::MAX)), entry_2);
269    }
270
271    /// Ensure that getting entries works with large negative numerators and denominators.
272    #[test]
273    fn max_int_negative() {
274        let mut matrix = MatQ::new(5, 10);
275        let value_1 = Q::from(i64::MIN);
276        let value_2 = Q::from((1, i64::MIN));
277        matrix.set_entry(0, 0, value_1).unwrap();
278        matrix.set_entry(1, 1, value_2).unwrap();
279
280        let entry_1 = matrix.get_entry(0, 0).unwrap();
281        let entry_2 = matrix.get_entry(1, 1).unwrap();
282
283        assert_eq!(Q::from(i64::MIN), entry_1);
284        assert_eq!(Q::from((1, i64::MIN)), entry_2);
285    }
286
287    /// Ensure that getting entries works with large negative numerators and denominators (larger than [`i64`]).
288    #[test]
289    fn large_negative() {
290        let mut matrix = MatQ::new(5, 10);
291        let value_1 = format!("-{}", u64::MAX);
292        let value_2 = format!("1/-{}", u64::MAX);
293        matrix
294            .set_entry(0, 0, Q::from_str(&value_1).unwrap())
295            .unwrap();
296        matrix
297            .set_entry(1, 1, Q::from_str(&value_2).unwrap())
298            .unwrap();
299
300        let entry_1 = matrix.get_entry(0, 0).unwrap();
301        let entry_2 = matrix.get_entry(1, 1).unwrap();
302
303        assert_eq!(Q::from_str(&format!("-{}", u64::MAX)).unwrap(), entry_1);
304        assert_eq!(Q::from_str(&format!("1/-{}", u64::MAX)).unwrap(), entry_2);
305    }
306
307    /// Ensure that getting entries at (0, 0) works.
308    #[test]
309    fn getting_at_zero() {
310        let mut matrix = MatQ::new(5, 10);
311        let value = Q::from(i64::MIN);
312        matrix.set_entry(0, 0, value).unwrap();
313
314        let entry = matrix.get_entry(0, 0).unwrap();
315
316        assert_eq!(entry, Q::from(i64::MIN));
317    }
318
319    /// Ensure that a wrong number of rows yields an Error.
320    #[test]
321    fn error_wrong_row() {
322        let matrix = MatQ::new(5, 10);
323
324        assert!(matrix.get_entry(5, 1).is_err());
325        assert!(matrix.get_entry(-6, 1).is_err());
326    }
327
328    /// Ensure that a wrong number of columns yields an Error.
329    #[test]
330    fn error_wrong_column() {
331        let matrix = MatQ::new(5, 10);
332
333        assert!(matrix.get_entry(1, 100).is_err());
334        assert!(matrix.get_entry(1, -11).is_err());
335    }
336
337    /// Ensure that negative indices return the correct values.
338    #[test]
339    fn negative_indexing() {
340        let matrix = MatQ::from_str("[[1, 2, 3],[4, 5, 6],[7, 8, 9]]").unwrap();
341
342        assert_eq!(matrix.get_entry(-1, -1).unwrap(), Q::from(9));
343        assert_eq!(matrix.get_entry(-1, -2).unwrap(), Q::from(8));
344        assert_eq!(matrix.get_entry(-3, -3).unwrap(), Q::from(1));
345    }
346
347    /// Ensure that the entry is a deep copy and not just a clone of the reference.
348    #[test]
349    fn memory_test() {
350        let mut matrix = MatQ::new(5, 10);
351        let value = Q::from(u64::MAX);
352        matrix.set_entry(1, 1, value).unwrap();
353        let entry = matrix.get_entry(1, 1).unwrap();
354        matrix.set_entry(1, 1, Q::ZERO).unwrap();
355
356        assert_eq!(Q::from(u64::MAX), entry);
357    }
358}
359
360#[cfg(test)]
361mod test_get_num {
362    use crate::{rational::MatQ, traits::MatrixDimensions};
363
364    /// Ensure that the getter for number of rows works correctly.
365    #[test]
366    fn num_rows() {
367        let matrix = MatQ::new(5, 10);
368
369        assert_eq!(matrix.get_num_rows(), 5);
370    }
371
372    /// Ensure that the getter for number of columns works correctly.
373    #[test]
374    fn num_columns() {
375        let matrix = MatQ::new(5, 10);
376
377        assert_eq!(matrix.get_num_columns(), 10);
378    }
379}
380
381#[cfg(test)]
382mod test_get_vec {
383    use crate::{rational::MatQ, traits::MatrixGetSubmatrix};
384    use std::str::FromStr;
385
386    /// Ensure that getting a row works
387    #[test]
388    fn get_row_works() {
389        let matrix = MatQ::from_str(&format!(
390            "[[0, 0, 0, 0, 0],[4/3, {}, {}, 1/{}, 1/{}]]",
391            i64::MAX,
392            i64::MIN,
393            i64::MAX,
394            i64::MIN
395        ))
396        .unwrap();
397        let row_1 = matrix.get_row(0).unwrap();
398        let row_2 = matrix.get_row(1).unwrap();
399
400        let cmp_1 = MatQ::from_str("[[0, 0, 0, 0, 0]]").unwrap();
401        let cmp_2 = MatQ::from_str(&format!(
402            "[[4/3, {}, {}, 1/{}, 1/{}]]",
403            i64::MAX,
404            i64::MIN,
405            i64::MAX,
406            i64::MIN
407        ))
408        .unwrap();
409
410        assert_eq!(cmp_1, row_1);
411        assert_eq!(cmp_2, row_2);
412    }
413
414    /// Ensure that getting a row with a negative index works
415    #[test]
416    fn get_row_negative_indexing_works() {
417        let matrix =
418            MatQ::from_str(&format!("[[0, 0, 0],[42, {}, {}]]", i64::MAX, i64::MIN)).unwrap();
419        let row_1 = matrix.get_row(-2).unwrap();
420        let row_2 = matrix.get_row(-1).unwrap();
421
422        let cmp_1 = MatQ::from_str("[[0, 0, 0]]").unwrap();
423        let cmp_2 = MatQ::from_str(&format!("[[42, {}, {}]]", i64::MAX, i64::MIN)).unwrap();
424
425        assert_eq!(cmp_1, row_1);
426        assert_eq!(cmp_2, row_2);
427    }
428
429    /// Ensure that getting a column works
430    #[test]
431    fn get_column_works() {
432        let matrix = MatQ::from_str(&format!(
433            "[[1, 0, 3],[{}, 0, 5],[{}, 0, 7],[1/{}, 0, 9],[1/{}, 0, 11]]",
434            i64::MAX,
435            i64::MIN,
436            i64::MAX,
437            i64::MIN
438        ))
439        .unwrap();
440        let column_1 = matrix.get_column(0).unwrap();
441        let column_2 = matrix.get_column(1).unwrap();
442        let column_3 = matrix.get_column(2).unwrap();
443
444        let cmp_1 = MatQ::from_str(&format!(
445            "[[1],[{}],[{}],[1/{}],[1/{}]]",
446            i64::MAX,
447            i64::MIN,
448            i64::MAX,
449            i64::MIN
450        ))
451        .unwrap();
452        let cmp_2 = MatQ::from_str("[[0],[0],[0],[0],[0]]").unwrap();
453        let cmp_3 = MatQ::from_str("[[3],[5],[7],[9],[11]]").unwrap();
454
455        assert_eq!(cmp_1, column_1);
456        assert_eq!(cmp_2, column_2);
457        assert_eq!(cmp_3, column_3);
458    }
459
460    /// Ensure that getting a column with a negative index works
461    #[test]
462    fn get_column_negative_indexing_works() {
463        let matrix = MatQ::from_str(&format!(
464            "[[42, 0, 42],[{}, 0, 17],[{}, 0, 42]]",
465            i64::MAX,
466            i64::MIN
467        ))
468        .unwrap();
469        let column_1 = matrix.get_column(-3).unwrap();
470        let column_2 = matrix.get_column(-2).unwrap();
471        let column_3 = matrix.get_column(-1).unwrap();
472
473        let cmp_1 = MatQ::from_str(&format!("[[42],[{}],[{}]]", i64::MAX, i64::MIN)).unwrap();
474        let cmp_2 = MatQ::from_str("[[0],[0],[0]]").unwrap();
475        let cmp_3 = MatQ::from_str("[[42],[17],[42]]").unwrap();
476
477        assert_eq!(cmp_1, column_1);
478        assert_eq!(cmp_2, column_2);
479        assert_eq!(cmp_3, column_3);
480    }
481
482    /// Ensure that wrong row and column dimensions yields an error
483    #[test]
484    fn wrong_dim_error() {
485        let matrix = MatQ::from_str(&format!(
486            "[[1, 2, 3],[{}, 4, 5],[{}, 6, 7]]",
487            i64::MAX,
488            i64::MIN
489        ))
490        .unwrap();
491        let row_1 = matrix.get_row(-4);
492        let row_2 = matrix.get_row(4);
493        let column_1 = matrix.get_column(-4);
494        let column_2 = matrix.get_column(4);
495
496        assert!(row_1.is_err());
497        assert!(row_2.is_err());
498        assert!(column_1.is_err());
499        assert!(column_2.is_err());
500    }
501}
502
503#[cfg(test)]
504mod test_get_submatrix {
505    use crate::{
506        integer::Z,
507        rational::MatQ,
508        traits::{MatrixDimensions, MatrixGetSubmatrix},
509    };
510    use std::str::FromStr;
511
512    /// Ensures that getting the entire matrix as a submatrix works.
513    #[test]
514    fn entire_matrix() {
515        let mat = MatQ::identity(5, 5);
516
517        let sub_mat = mat.get_submatrix(0, 4, 0, 4).unwrap();
518
519        assert_eq!(mat, sub_mat);
520    }
521
522    /// Ensures that a single matrix entry can be retrieved.
523    #[test]
524    fn matrix_single_entry() {
525        let mat = MatQ::identity(5, 5);
526
527        let sub_mat = mat.get_submatrix(0, 0, 0, 0).unwrap();
528
529        let cmp_mat = MatQ::identity(1, 1);
530        assert_eq!(cmp_mat, sub_mat);
531    }
532
533    /// Ensures that the dimensions of the submatrix are correct.
534    #[test]
535    fn correct_dimensions() {
536        let mat = MatQ::identity(100, 100);
537
538        let sub_mat = mat.get_submatrix(1, 37, 0, 29).unwrap();
539
540        assert_eq!(37, sub_mat.get_num_rows());
541        assert_eq!(30, sub_mat.get_num_columns());
542    }
543
544    /// Ensures that a submatrix can be correctly retrieved for a matrix with large
545    /// entries.
546    #[test]
547    fn large_entries() {
548        let mat =
549            MatQ::from_str(&format!("[[{}/3, 2, 3],[1, {}, 3]]", u64::MAX, i64::MIN)).unwrap();
550
551        let sub_mat = mat.get_submatrix(0, 1, 0, 1).unwrap();
552
553        let cmp_mat = MatQ::from_str(&format!("[[{}/3, 2],[1, {}]]", u64::MAX, i64::MIN)).unwrap();
554        assert_eq!(cmp_mat, sub_mat);
555    }
556
557    /// Ensures that an error is returned if coordinates are addressed that are not
558    /// within the matrix.
559    #[test]
560    fn invalid_coordinates() {
561        let mat = MatQ::identity(10, 10);
562
563        assert!(mat.get_submatrix(0, 0, 0, 10).is_err());
564        assert!(mat.get_submatrix(0, 10, 0, 0).is_err());
565        assert!(mat.get_submatrix(0, 0, -11, 0).is_err());
566        assert!(mat.get_submatrix(-11, 0, 0, 0).is_err());
567    }
568
569    /// Ensure that negative indices return the correct submatrix.
570    #[test]
571    fn negative_indexing() {
572        let matrix = MatQ::identity(3, 3);
573
574        assert_eq!(matrix, matrix.get_submatrix(0, -1, 0, -1).unwrap());
575        assert_eq!(matrix, matrix.get_submatrix(-3, -1, -3, -1).unwrap());
576        assert_eq!(
577            matrix.get_row(0).unwrap(),
578            matrix.get_submatrix(0, -3, -3, -1).unwrap()
579        );
580    }
581
582    /// Ensures that the function panics if no columns of the matrix are addressed.
583    #[test]
584    #[should_panic]
585    fn no_columns() {
586        let mat = MatQ::identity(10, 10);
587
588        let _ = mat.get_submatrix(0, 0, 6, 5);
589    }
590
591    /// Ensures that the function panics if no rows of the matrix are addressed.
592    #[test]
593    #[should_panic]
594    fn no_rows() {
595        let mat = MatQ::identity(10, 10);
596
597        let _ = mat.get_submatrix(5, 4, 0, 0);
598    }
599
600    /// Ensure that the submatrix function can be called with several types.
601    #[test]
602    fn availability() {
603        let mat = MatQ::identity(10, 10);
604
605        let _ = mat.get_submatrix(0_i8, 0_i8, 0_i8, 0_i8);
606        let _ = mat.get_submatrix(0_i16, 0_i16, 0_i16, 0_i16);
607        let _ = mat.get_submatrix(0_i32, 0_i32, 0_i32, 0_i32);
608        let _ = mat.get_submatrix(0_i64, 0_i64, 0_i64, 0_i64);
609        let _ = mat.get_submatrix(0_u8, 0_u8, 0_u8, 0_u8);
610        let _ = mat.get_submatrix(0_u16, 0_i16, 0_u16, 0_u16);
611        let _ = mat.get_submatrix(0_u32, 0_i32, 0_u32, 0_u32);
612        let _ = mat.get_submatrix(0_u64, 0_i64, 0_u64, 0_u64);
613        let _ = mat.get_submatrix(&Z::ZERO, &Z::ZERO, &Z::ZERO, &Z::ZERO);
614    }
615}
616
617#[cfg(test)]
618mod test_collect_entries {
619    use super::MatQ;
620    use std::str::FromStr;
621
622    /// Ensures that all entries from the matrices are actually collected in the vector.
623    #[test]
624    fn all_entries_collected() {
625        let mat_1 = MatQ::from_str(&format!(
626            "[[1/{}, 2],[{}, {}],[-3/-4, 4]]",
627            i64::MAX,
628            i64::MAX,
629            i64::MIN
630        ))
631        .unwrap();
632        let mat_2 = MatQ::from_str("[[-1/1, 2/-4]]").unwrap();
633
634        let entries_1 = mat_1.collect_entries();
635        let entries_2 = mat_2.collect_entries();
636
637        assert_eq!(entries_1.len(), 6);
638        assert_eq!(entries_1[0].num.0, 1);
639        assert!(entries_1[0].den.0 >= 2_i64.pow(62));
640        assert_eq!(entries_1[1].num.0, 2);
641        assert_eq!(entries_1[1].den.0, 1);
642        assert!(entries_1[2].num.0 >= 2_i64.pow(62));
643        assert_eq!(entries_1[2].den.0, 1);
644        assert!(entries_1[3].num.0 >= 2_i64.pow(62));
645        assert_eq!(entries_1[3].den.0, 1);
646        assert_eq!(entries_1[4].num.0, 3);
647        assert_eq!(entries_1[4].den.0, 4);
648        assert_eq!(entries_1[5].num.0, 4);
649        assert_eq!(entries_1[5].den.0, 1);
650
651        assert_eq!(entries_2.len(), 2);
652        assert_eq!(entries_2[0].num.0, -1);
653        assert_eq!(entries_2[0].den.0, 1);
654        assert_eq!(entries_2[1].num.0, -1);
655        assert_eq!(entries_2[1].den.0, 2);
656    }
657
658    /// Ensures that all entries from the matrices are actually collected
659    /// in [`MatQ::collect_entries_f64`].
660    #[test]
661    fn all_entries_collected_f64() {
662        let mat_1 = MatQ::from_str(&format!(
663            "[[1/{}, 2],[{}, 4/5],[-3/-4, {}]]",
664            i64::MAX,
665            i64::MAX,
666            i64::MIN
667        ))
668        .unwrap();
669        let mat_2 = MatQ::from_str("[[-1/1, 2/-4]]").unwrap();
670
671        let entries_1 = mat_1.collect_entries_f64();
672        let entries_2 = mat_2.collect_entries_f64();
673
674        assert_eq!(entries_1.len(), 3);
675        assert_eq!(entries_1[0].len(), 2);
676        assert_eq!(entries_1[0][0], 1.0 / i64::MAX as f64);
677        assert_eq!(entries_1[0][1], 2.0);
678        assert!((entries_1[1][0] - i64::MAX as f64).abs() < 1_025.0);
679        assert!((entries_1[1][1] - 0.8).abs() < 0.00001);
680        assert_eq!(entries_1[2][0], 0.75);
681        assert_eq!(entries_1[2][1], i64::MIN as f64);
682
683        assert_eq!(entries_2.len(), 1);
684        assert_eq!(entries_2[0].len(), 2);
685        assert_eq!(entries_2[0][0], -1.0);
686        assert_eq!(entries_2[0][1], -0.5);
687    }
688}