qfall_math/rational/mat_q/
set.rs

1// Copyright © 2023 Marcel Luca Schmidt, Niklas Siemer
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//! Implementation to manipulate a [`MatQ`] matrix.
10
11use crate::{
12    error::MathError,
13    rational::{MatQ, Q},
14    traits::{MatrixDimensions, MatrixSetEntry, MatrixSetSubmatrix, MatrixSwaps},
15    utils::index::{evaluate_index_for_vector, evaluate_indices_for_matrix},
16};
17use flint_sys::{
18    fmpq::{fmpq_set, fmpq_swap},
19    fmpq_mat::{
20        fmpq_mat_entry, fmpq_mat_invert_cols, fmpq_mat_invert_rows, fmpq_mat_set,
21        fmpq_mat_swap_cols, fmpq_mat_swap_rows, fmpq_mat_window_clear, fmpq_mat_window_init,
22    },
23};
24use std::{
25    fmt::Display,
26    mem::MaybeUninit,
27    ptr::{null, null_mut},
28};
29
30impl<Rational: Into<Q>> MatrixSetEntry<Rational> for MatQ {
31    /// Sets the value of a specific matrix entry according to a given `value`
32    /// that implements [`Into<Q>`] without checking whether the coordinate is part of the matrix.
33    ///
34    /// Parameters:
35    /// - `row`: specifies the row in which the entry is located
36    /// - `column`: specifies the column in which the entry is located
37    /// - `value`: specifies the value to which the entry is set
38    ///
39    /// # Safety
40    /// To use this function safely, make sure that the selected entry is part
41    /// of the matrix. If it is not, memory leaks, unexpected panics, etc. might
42    /// occur.
43    ///
44    /// # Examples
45    /// ```
46    /// use qfall_math::rational::MatQ;
47    /// use qfall_math::rational::Q;
48    /// use qfall_math::traits::*;
49    ///
50    /// let mut matrix = MatQ::new(3, 3);
51    /// let value = Q::from((5, 2));
52    ///
53    /// unsafe {
54    ///     matrix.set_entry_unchecked(0, 1, &value);
55    ///     matrix.set_entry_unchecked(2, 2, 5);
56    ///     matrix.set_entry_unchecked(0, 2, (2, 3));
57    /// }
58    ///
59    /// assert_eq!("[[0, 5/2, 2/3],[0, 0, 0],[0, 0, 5]]", matrix.to_string());
60    /// ```
61    unsafe fn set_entry_unchecked(&mut self, row: i64, column: i64, value: Rational) {
62        let value: Q = value.into();
63
64        unsafe {
65            let entry = fmpq_mat_entry(&self.matrix, row, column);
66            fmpq_set(entry, &value.value)
67        };
68    }
69}
70
71impl MatrixSetSubmatrix for MatQ {
72    /// Sets the matrix entries in `self` to entries defined in `other`.
73    /// The entries in `self` starting from `(row_self_start, col_self_start)` up to
74    /// `(row_self_end, col_self_end)`are set to be
75    /// the entries from the submatrix from `other` defined by `(row_other_start, col_other_start)`
76    /// to `(row_other_end, col_other_end)` (exclusively).
77    ///
78    /// Parameters:
79    /// `row_self_start`: the starting row of the matrix in which to set a submatrix
80    /// `col_self_start`: the starting column of the matrix in which to set a submatrix
81    /// `other`: the matrix from where to take the submatrix to set
82    /// `row_other_start`: the starting row of the specified submatrix
83    /// `col_other_start`: the starting column of the specified submatrix
84    /// `row_other_end`: the ending row of the specified submatrix
85    /// `col_other_end`:the ending column of the specified submatrix
86    ///
87    /// # Examples
88    /// ```
89    /// use qfall_math::{rational::MatQ, traits::MatrixSetSubmatrix};
90    /// use std::str::FromStr;
91    ///
92    /// let mut mat = MatQ::identity(3, 3);
93    ///
94    /// mat.set_submatrix(0, 1, &mat.clone(), 0, 0, 1, 1).unwrap();
95    /// // [[1,1,0],[0,0,1],[0,0,1]]
96    /// let mat_cmp = MatQ::from_str("[[1, 1, 0],[0, 0, 1],[0, 0, 1]]").unwrap();
97    /// assert_eq!(mat, mat_cmp);
98    ///
99    /// unsafe{ mat.set_submatrix_unchecked(2, 0, 3, 2, &mat.clone(), 0, 0, 1, 2) };
100    /// let mat_cmp = MatQ::from_str("[[1, 1, 0],[0, 0, 1],[1, 1, 1]]").unwrap();
101    /// assert_eq!(mat, mat_cmp);
102    /// ```
103    ///
104    /// # Safety
105    /// To use this function safely, make sure that the selected submatrices are part
106    /// of the matrices, the submatrices are of the same dimensions and the base types are the same.
107    /// If not, memory leaks, unexpected panics, etc. might occur.
108    unsafe fn set_submatrix_unchecked(
109        &mut self,
110        row_self_start: i64,
111        col_self_start: i64,
112        row_self_end: i64,
113        col_self_end: i64,
114        other: &Self,
115        row_other_start: i64,
116        col_other_start: i64,
117        row_other_end: i64,
118        col_other_end: i64,
119    ) {
120        let mut window_self = MaybeUninit::uninit();
121        // The memory for the elements of window is shared with self.
122        unsafe {
123            fmpq_mat_window_init(
124                window_self.as_mut_ptr(),
125                &self.matrix,
126                row_self_start,
127                col_self_start,
128                row_self_end,
129                col_self_end,
130            )
131        };
132        let mut window_other = MaybeUninit::uninit();
133        // The memory for the elements of window is shared with other.
134        unsafe {
135            fmpq_mat_window_init(
136                window_other.as_mut_ptr(),
137                &other.matrix,
138                row_other_start,
139                col_other_start,
140                row_other_end,
141                col_other_end,
142            )
143        };
144        unsafe {
145            // TODO: this should not be mutable for the other window
146            fmpq_mat_set(window_self.as_mut_ptr(), window_other.as_mut_ptr());
147
148            // Clears the matrix window and releases any memory that it uses. Note that
149            // the memory to the underlying matrix that window points to is not freed
150            fmpq_mat_window_clear(window_self.as_mut_ptr());
151            fmpq_mat_window_clear(window_other.as_mut_ptr());
152        }
153    }
154}
155
156impl MatrixSwaps for MatQ {
157    /// Swaps two entries of the specified matrix.
158    ///
159    /// Parameters:
160    /// - `row_0`: specifies the row, in which the first entry is located
161    /// - `col_0`: specifies the column, in which the first entry is located
162    /// - `row_1`: specifies the row, in which the second entry is located
163    /// - `col_1`: specifies the column, in which the second entry is located
164    ///
165    /// Negative indices can be used to index from the back, e.g., `-1` for
166    /// the last element.
167    ///
168    /// Returns an empty `Ok` if the action could be performed successfully.
169    /// Otherwise, a [`MathError`] is returned if one of the specified entries
170    /// is not part of the matrix.
171    ///
172    /// # Examples
173    /// ```
174    /// use qfall_math::{rational::MatQ, traits::MatrixSwaps};
175    ///
176    /// let mut matrix = MatQ::new(4, 3);
177    /// matrix.swap_entries(0, 0, 2, 1);
178    /// ```
179    ///
180    /// # Errors and Failures
181    /// - Returns a [`MathError`] of type [`MathError::OutOfBounds`]
182    ///   if row or column are greater than the matrix size.
183    fn swap_entries(
184        &mut self,
185        row_0: impl TryInto<i64> + Display,
186        col_0: impl TryInto<i64> + Display,
187        row_1: impl TryInto<i64> + Display,
188        col_1: impl TryInto<i64> + Display,
189    ) -> Result<(), MathError> {
190        let (row_0, col_0) = evaluate_indices_for_matrix(self, row_0, col_0)?;
191        let (row_1, col_1) = evaluate_indices_for_matrix(self, row_1, col_1)?;
192
193        unsafe {
194            fmpq_swap(
195                fmpq_mat_entry(&self.matrix, row_0, col_0),
196                fmpq_mat_entry(&self.matrix, row_1, col_1),
197            )
198        };
199        Ok(())
200    }
201
202    /// Swaps two columns of the specified matrix.
203    ///
204    /// Parameters:
205    /// - `col_0`: specifies the first column which is swapped with the second one
206    /// - `col_1`: specifies the second column which is swapped with the first one
207    ///
208    /// Negative indices can be used to index from the back, e.g., `-1` for
209    /// the last element.
210    ///
211    /// Returns an empty `Ok` if the action could be performed successfully.
212    /// Otherwise, a [`MathError`] is returned if one of the specified columns is not part of the matrix.
213    ///
214    /// # Examples
215    /// ```
216    /// use qfall_math::{rational::MatQ, traits::MatrixSwaps};
217    ///
218    /// let mut matrix = MatQ::new(4, 3);
219    /// matrix.swap_columns(0, 2);
220    /// ```
221    ///
222    /// # Errors and Failures
223    /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds)
224    ///   if one of the given columns is greater than the matrix.
225    fn swap_columns(
226        &mut self,
227        col_0: impl TryInto<i64> + Display,
228        col_1: impl TryInto<i64> + Display,
229    ) -> Result<(), MathError> {
230        let num_cols = self.get_num_columns();
231        let col_0 = evaluate_index_for_vector(col_0, num_cols)?;
232        let col_1 = evaluate_index_for_vector(col_1, num_cols)?;
233
234        if col_0 >= num_cols || col_1 >= num_cols {
235            return Err(MathError::OutOfBounds(
236                format!("smaller than {num_cols}"),
237                if col_0 > col_1 {
238                    col_0.to_string()
239                } else {
240                    col_1.to_string()
241                },
242            ));
243        }
244        unsafe { fmpq_mat_swap_cols(&mut self.matrix, null(), col_0, col_1) }
245        Ok(())
246    }
247
248    /// Swaps two rows of the specified matrix.
249    ///
250    /// Parameters:
251    /// - `row_0`: specifies the first row which is swapped with the second one
252    /// - `row_1`: specifies the second row which is swapped with the first one
253    ///
254    /// Negative indices can be used to index from the back, e.g., `-1` for
255    /// the last element.
256    ///
257    /// Returns an empty `Ok` if the action could be performed successfully.
258    /// Otherwise, a [`MathError`] is returned if one of the specified rows is not part of the matrix.
259    ///
260    /// # Examples
261    /// ```
262    /// use qfall_math::{rational::MatQ, traits::MatrixSwaps};
263    ///
264    /// let mut matrix = MatQ::new(4, 3);
265    /// matrix.swap_rows(0, 2);
266    /// ```
267    ///
268    /// # Errors and Failures
269    /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds)
270    ///   if one of the given rows is greater than the matrix.
271    fn swap_rows(
272        &mut self,
273        row_0: impl TryInto<i64> + Display,
274        row_1: impl TryInto<i64> + Display,
275    ) -> Result<(), MathError> {
276        let num_rows = self.get_num_rows();
277        let row_0 = evaluate_index_for_vector(row_0, num_rows)?;
278        let row_1 = evaluate_index_for_vector(row_1, num_rows)?;
279
280        if row_0 >= num_rows || row_1 >= num_rows {
281            return Err(MathError::OutOfBounds(
282                format!("smaller than {num_rows}"),
283                if row_0 > row_1 {
284                    row_0.to_string()
285                } else {
286                    row_1.to_string()
287                },
288            ));
289        }
290        unsafe { fmpq_mat_swap_rows(&mut self.matrix, null(), row_0, row_1) }
291        Ok(())
292    }
293}
294
295impl MatQ {
296    /// Swaps the `i`-th column with the `n-i`-th column for all `i <= n/2`
297    /// of the specified matrix with `n` columns.
298    ///
299    /// # Examples
300    /// ```
301    /// use qfall_math::rational::MatQ;
302    ///
303    /// let mut matrix = MatQ::new(4, 3);
304    /// matrix.reverse_columns();
305    /// ```
306    pub fn reverse_columns(&mut self) {
307        // If the second argument to this function is not null, the permutation
308        // of the columns is also applied to this argument.
309        // Hence, passing in null is justified here.
310        unsafe { fmpq_mat_invert_cols(&mut self.matrix, null_mut()) }
311    }
312
313    /// Swaps the `i`-th row with the `n-i`-th row for all `i <= n/2`
314    /// of the specified matrix with `n` rows.
315    ///
316    /// # Examples
317    /// ```
318    /// use qfall_math::rational::MatQ;
319    ///
320    /// let mut matrix = MatQ::new(4, 3);
321    /// matrix.reverse_rows();
322    /// ```
323    pub fn reverse_rows(&mut self) {
324        // If the second argument to this function is not null, the permutation
325        // of the rows is also applied to this argument.
326        // Hence, passing in null is justified here.
327        unsafe { fmpq_mat_invert_rows(&mut self.matrix, null_mut()) }
328    }
329}
330
331#[cfg(test)]
332mod test_setter {
333    use super::Q;
334    use crate::{
335        integer::Z,
336        integer_mod_q::Modulus,
337        rational::MatQ,
338        traits::{MatrixGetEntry, MatrixSetEntry, MatrixSetSubmatrix},
339    };
340    use std::str::FromStr;
341
342    /// Test the different types that can be used to set an entry.
343    #[test]
344    fn availability() {
345        let mut matrix = MatQ::new(1, 1);
346
347        // Set with rationals
348        matrix.set_entry(0, 0, (1, 2)).unwrap();
349        matrix.set_entry(0, 0, (1i16, 2u64)).unwrap();
350        matrix.set_entry(0, 0, Q::from((1, 2))).unwrap();
351        matrix.set_entry(0, 0, &Q::from((1, 2))).unwrap();
352
353        // Set with integers
354        matrix.set_entry(0, 0, 1u8).unwrap();
355        matrix.set_entry(0, 0, 1i8).unwrap();
356        matrix.set_entry(0, 0, 1u16).unwrap();
357        matrix.set_entry(0, 0, 1i16).unwrap();
358        matrix.set_entry(0, 0, 1u32).unwrap();
359        matrix.set_entry(0, 0, 1i32).unwrap();
360        matrix.set_entry(0, 0, 1u64).unwrap();
361        matrix.set_entry(0, 0, 1i64).unwrap();
362        matrix.set_entry(0, 0, Z::from(1)).unwrap();
363        matrix.set_entry(0, 0, &Z::from(1)).unwrap();
364        matrix.set_entry(0, 0, Modulus::from(3)).unwrap();
365        matrix.set_entry(0, 0, &Modulus::from(3)).unwrap();
366    }
367
368    /// Ensure that setting entries works with standard numbers.
369    #[test]
370    fn standard_value() {
371        let mut matrix = MatQ::new(5, 10);
372        let value = Q::from(869);
373        matrix.set_entry(4, 7, &value).unwrap();
374
375        let entry = matrix.get_entry(4, 7).unwrap();
376
377        assert_eq!(Q::from(869), entry);
378    }
379
380    /// Ensure that setting entries works with large large numerators and denominators.
381    #[test]
382    fn max_int_positive() {
383        let mut matrix = MatQ::new(5, 10);
384        let value_1 = Q::from(i64::MAX);
385        let value_2 = Q::from((1, i64::MAX));
386        matrix.set_entry(0, 0, value_1).unwrap();
387        matrix.set_entry(1, 1, value_2).unwrap();
388
389        let entry_1 = matrix.get_entry(0, 0).unwrap();
390        let entry_2 = matrix.get_entry(1, 1).unwrap();
391
392        assert_eq!(Q::from(i64::MAX), entry_1);
393        assert_eq!(Q::from((1, i64::MAX)), entry_2);
394    }
395
396    /// Ensure that setting entries works with large numerators and denominators (larger than [`i64`]).
397    #[test]
398    fn large_positive() {
399        let mut matrix = MatQ::new(5, 10);
400        let value_1 = Q::from(u64::MAX);
401        let value_2 = Q::from((1, u64::MAX));
402        matrix.set_entry(0, 0, value_1).unwrap();
403        matrix.set_entry(1, 1, value_2).unwrap();
404
405        let entry_1 = matrix.get_entry(0, 0).unwrap();
406        let entry_2 = matrix.get_entry(1, 1).unwrap();
407
408        assert_eq!(Q::from(u64::MAX), entry_1);
409        assert_eq!(Q::from((1, u64::MAX)), entry_2);
410    }
411
412    /// Ensure that setting entries works with large negative numerators and denominators.
413    #[test]
414    fn max_int_negative() {
415        let mut matrix = MatQ::new(5, 10);
416        let value_1 = Q::from(i64::MIN);
417        let value_2 = Q::from((1, i64::MIN));
418        matrix.set_entry(0, 0, value_1).unwrap();
419        matrix.set_entry(1, 1, value_2).unwrap();
420
421        let entry_1 = matrix.get_entry(0, 0).unwrap();
422        let entry_2 = matrix.get_entry(1, 1).unwrap();
423
424        assert_eq!(Q::from(i64::MIN), entry_1);
425        assert_eq!(Q::from((1, i64::MIN)), entry_2);
426    }
427
428    /// Ensure that setting entries works with large negative numerators and denominators (larger than [`i64`]).
429    #[test]
430    fn large_negative() {
431        let mut matrix = MatQ::new(5, 10);
432        let value_1 = format!("-{}", u64::MAX);
433        let value_2 = format!("1/-{}", u64::MAX);
434        matrix
435            .set_entry(0, 0, Q::from_str(&value_1).unwrap())
436            .unwrap();
437        matrix
438            .set_entry(1, 1, Q::from_str(&value_2).unwrap())
439            .unwrap();
440
441        let entry_1 = matrix.get_entry(0, 0).unwrap();
442        let entry_2 = matrix.get_entry(1, 1).unwrap();
443
444        assert_eq!(Q::from_str(&format!("-{}", u64::MAX)).unwrap(), entry_1);
445        assert_eq!(Q::from_str(&format!("1/-{}", u64::MAX)).unwrap(), entry_2);
446    }
447
448    /// Ensure that setting entries at (0, 0) works.
449    #[test]
450    fn getting_at_zero() {
451        let mut matrix = MatQ::new(5, 10);
452        let value = Q::from(i64::MIN);
453        matrix.set_entry(0, 0, value).unwrap();
454
455        let entry = matrix.get_entry(0, 0).unwrap();
456
457        assert_eq!(entry, Q::from(i64::MIN));
458    }
459
460    /// Ensure that a wrong number of rows yields an Error.
461    #[test]
462    fn error_wrong_row() {
463        let matrix = MatQ::new(5, 10);
464
465        assert!(matrix.get_entry(5, 1).is_err());
466        assert!(matrix.get_entry(-6, 1).is_err());
467    }
468
469    /// Ensure that a wrong number of columns yields an Error.
470    #[test]
471    fn error_wrong_column() {
472        let matrix = MatQ::new(5, 10);
473
474        assert!(matrix.get_entry(1, 100).is_err());
475        assert!(matrix.get_entry(1, -11).is_err());
476    }
477
478    /// Ensure that negative indices return address the correct entires.
479    #[test]
480    fn negative_indexing() {
481        let mut matrix = MatQ::new(3, 3);
482
483        matrix.set_entry(-1, -1, 9).unwrap();
484        matrix.set_entry(-1, -2, 8).unwrap();
485        matrix.set_entry(-3, -3, 1).unwrap();
486
487        let matrix_cmp = MatQ::from_str("[[1, 0, 0],[0, 0, 0],[0, 8, 9]]").unwrap();
488        assert_eq!(matrix_cmp, matrix);
489    }
490
491    /// Ensures that setting columns works fine for small entries
492    #[test]
493    fn column_small_entries() {
494        let mut mat_1 = MatQ::from_str("[[1, 2, 3/-1],[-4/3, 5, 6]]").unwrap();
495        let mat_2 = MatQ::from_str("[[0],[-1]]").unwrap();
496        let cmp = MatQ::from_str("[[1, 0, -3],[4/-3, -1, 6]]").unwrap();
497
498        mat_1.set_column(1, &mat_2, 0).unwrap();
499
500        assert_eq!(cmp, mat_1);
501    }
502
503    /// Ensures that setting columns works fine for large entries
504    #[test]
505    fn column_large_entries() {
506        let mut mat_1 = MatQ::from_str(&format!(
507            "[[{}/1, 1, 3, 4],[{}/-1, 4, -1/{}, 5],[7, 6, 8, 9]]",
508            i64::MIN,
509            i64::MAX,
510            u64::MAX
511        ))
512        .unwrap();
513        let mat_2 =
514            MatQ::from_str(&format!("[[1, -2/{}],[{}, 0],[7, -1]]", i64::MIN, i64::MAX)).unwrap();
515        let cmp = MatQ::from_str(&format!(
516            "[[-2/{}, 1, 3, 4],[0, 4, -1/{}, 5],[-1, 6, 8, 9]]",
517            i64::MIN,
518            u64::MAX
519        ))
520        .unwrap();
521
522        mat_1.set_column(0, &mat_2, 1).unwrap();
523
524        assert_eq!(cmp, mat_1);
525    }
526
527    /// Ensures that setting the column to itself does not change anything
528    #[test]
529    fn column_swap_same_entry() {
530        let mut mat_1 = MatQ::from_str(&format!(
531            "[[{}/-3, 1, 3, 4],[{}/-1, 4, -{}/2, 5],[7, 6, 8, 9]]",
532            i64::MIN,
533            i64::MAX,
534            u64::MAX
535        ))
536        .unwrap();
537        let cmp = mat_1.clone();
538
539        mat_1.set_column(0, &cmp, 0).unwrap();
540        mat_1.set_column(1, &cmp, 1).unwrap();
541
542        assert_eq!(cmp, mat_1);
543    }
544
545    /// Ensures that `set_column` returns an error if one of the specified columns is out of bounds
546    #[test]
547    fn column_out_of_bounds() {
548        let mut mat_1 = MatQ::new(5, 2);
549        let mat_2 = mat_1.clone();
550
551        assert!(mat_1.set_column(-3, &mat_2, 0).is_err());
552        assert!(mat_1.set_column(2, &mat_2, 0).is_err());
553        assert!(mat_1.set_column(1, &mat_2, -3).is_err());
554        assert!(mat_1.set_column(1, &mat_2, 2).is_err());
555    }
556
557    /// Ensures that mismatching row dimensions result in an error
558    #[test]
559    fn column_mismatching_columns() {
560        let mut mat_1 = MatQ::new(5, 2);
561        let mat_2 = MatQ::new(2, 2);
562
563        assert!(mat_1.set_column(0, &mat_2, 0).is_err());
564        assert!(mat_1.set_column(1, &mat_2, 1).is_err());
565    }
566
567    /// Ensures that setting rows works fine for small entries
568    #[test]
569    fn row_small_entries() {
570        let mut mat_1 = MatQ::from_str("[[1, 2, 3/-1],[-4/3, 5, 6]]").unwrap();
571        let mat_2 = MatQ::from_str("[[0, -1/2, 2]]").unwrap();
572        let cmp = MatQ::from_str("[[1, 2, -3],[0, -1/2, 2]]").unwrap();
573
574        let _ = mat_1.set_row(1, &mat_2, 0);
575
576        assert_eq!(cmp, mat_1);
577    }
578
579    /// Ensures that setting rows works fine for large entries
580    #[test]
581    fn row_large_entries() {
582        let mut mat_1 = MatQ::from_str(&format!(
583            "[[{}, 1, 3, 4],[{}, 4, {}, 5],[7, 6, 8, 9]]",
584            i64::MIN,
585            i64::MAX,
586            u64::MAX
587        ))
588        .unwrap();
589        let mat_2 = MatQ::from_str(&format!(
590            "[[0, 0, 0, 0],[1/{}, 0, {}/3, 0]]",
591            i64::MIN,
592            i64::MAX
593        ))
594        .unwrap();
595        let cmp = MatQ::from_str(&format!(
596            "[[1/{}, 0, {}/3, 0],[{}, 4, {}, 5],[7, 6, 8, 9]]",
597            i64::MIN,
598            i64::MAX,
599            i64::MAX,
600            u64::MAX
601        ))
602        .unwrap();
603
604        let _ = mat_1.set_row(0, &mat_2, 1);
605
606        assert_eq!(cmp, mat_1);
607    }
608
609    /// Ensures that setting the rows to itself does not change anything
610    #[test]
611    fn row_swap_same_entry() {
612        let mut mat_1 = MatQ::from_str(&format!(
613            "[[{}, 1, 3, 4],[-{}/-1, 4, {}/-3, 5],[7, 6, 8, 9]]",
614            i64::MIN,
615            i64::MAX,
616            u64::MAX
617        ))
618        .unwrap();
619        let cmp = mat_1.clone();
620
621        let _ = mat_1.set_row(0, &cmp, 0);
622        let _ = mat_1.set_row(1, &cmp, 1);
623
624        assert_eq!(cmp, mat_1);
625    }
626
627    /// Ensures that `set_row` returns an error if one of the specified rows is out of bounds
628    #[test]
629    fn row_out_of_bounds() {
630        let mut mat_1 = MatQ::new(5, 2);
631        let mat_2 = mat_1.clone();
632
633        assert!(mat_1.set_row(-6, &mat_2, 0).is_err());
634        assert!(mat_1.set_row(5, &mat_2, 0).is_err());
635        assert!(mat_1.set_row(2, &mat_2, -6).is_err());
636        assert!(mat_1.set_row(2, &mat_2, 5).is_err());
637    }
638
639    /// Ensures that mismatching column dimensions result in an error
640    #[test]
641    fn row_mismatching_columns() {
642        let mut mat_1 = MatQ::new(3, 2);
643        let mat_2 = MatQ::new(3, 3);
644
645        assert!(mat_1.set_row(0, &mat_2, 0).is_err());
646        assert!(mat_1.set_row(1, &mat_2, 1).is_err());
647    }
648
649    /// Ensure that negative indices work for set_column/row.
650    #[test]
651    fn negative_indexing_row_column() {
652        let mut matrix = MatQ::identity(3, 3);
653        let matrix2 = MatQ::identity(3, 3);
654
655        matrix.set_column(-1, &matrix2, -2).unwrap();
656        matrix.set_row(-1, &matrix2, -2).unwrap();
657
658        let matrix_cmp = MatQ::from_str("[[1, 0, 0],[0, 1, 1],[0, 1, 0]]").unwrap();
659        assert_eq!(matrix_cmp, matrix);
660    }
661}
662
663#[cfg(test)]
664mod test_swaps {
665    use super::MatQ;
666    use crate::traits::{MatrixGetSubmatrix, MatrixSwaps};
667    use std::str::FromStr;
668
669    /// Ensures that swapping entries works fine for small entries
670    #[test]
671    fn entries_small_entries() {
672        let mut matrix = MatQ::from_str("[[1, 1/2, 3/-5],[-4/3, -5, 6]]").unwrap();
673        let cmp = MatQ::from_str("[[1, -5, 3/-5],[-4/3, 1/2, 6]]").unwrap();
674
675        let _ = matrix.swap_entries(1, 1, 0, 1);
676
677        assert_eq!(cmp, matrix);
678    }
679
680    /// Ensures that swapping entries works fine for large entries
681    #[test]
682    fn entries_large_entries() {
683        let mut matrix = MatQ::from_str(&format!(
684            "[[{}/2, 1, 3, 4],[{}, 4, -1/{}, 5],[7, 6, 8, 9]]",
685            i64::MIN,
686            i64::MAX,
687            u64::MAX
688        ))
689        .unwrap();
690        let cmp = MatQ::from_str(&format!(
691            "[[-1/{}, 1, 3, 4],[{}, 4, {}/2, 5],[7, 6, 8, 9]]",
692            u64::MAX,
693            i64::MAX,
694            i64::MIN
695        ))
696        .unwrap();
697
698        let _ = matrix.swap_entries(0, 0, 1, 2);
699
700        assert_eq!(cmp, matrix);
701    }
702
703    /// Ensures that swapping the same entry does not change anything
704    #[test]
705    fn entries_swap_same_entry() {
706        let mut matrix = MatQ::from_str(&format!(
707            "[[{}, 1, 3, 4],[{}, 4, {}, 5],[7, 6, 8, 9]]",
708            i64::MIN,
709            i64::MAX,
710            u64::MAX
711        ))
712        .unwrap();
713        let cmp = matrix.clone();
714
715        let _ = matrix.swap_entries(0, 0, 0, 0);
716        let _ = matrix.swap_entries(1, 1, 1, 1);
717
718        assert_eq!(cmp, matrix);
719    }
720
721    /// Ensures that `swap_entries` returns an error if one of the specified entries is out of bounds
722    #[test]
723    fn entries_out_of_bounds() {
724        let mut matrix = MatQ::new(5, 2);
725
726        assert!(matrix.swap_entries(-6, 0, 0, 0).is_err());
727        assert!(matrix.swap_entries(0, -3, 0, 0).is_err());
728        assert!(matrix.swap_entries(0, 0, 5, 0).is_err());
729        assert!(matrix.swap_entries(0, 5, 0, 0).is_err());
730    }
731
732    /// Ensures that swapping columns works fine for small entries
733    #[test]
734    fn columns_small_entries() {
735        let mut matrix = MatQ::from_str("[[1, 2/-1, -3/1],[1/4, 5/7, 6]]").unwrap();
736        let cmp_vec_0 = MatQ::from_str("[[1],[1/4]]").unwrap();
737        let cmp_vec_1 = MatQ::from_str("[[-3],[6]]").unwrap();
738        let cmp_vec_2 = MatQ::from_str("[[-2],[5/7]]").unwrap();
739
740        let _ = matrix.swap_columns(1, 2);
741
742        assert_eq!(cmp_vec_0, matrix.get_column(0).unwrap());
743        assert_eq!(cmp_vec_1, matrix.get_column(1).unwrap());
744        assert_eq!(cmp_vec_2, matrix.get_column(2).unwrap());
745    }
746
747    /// Ensures that swapping columns works fine for large entries
748    #[test]
749    fn columns_large_entries() {
750        let mut matrix = MatQ::from_str(&format!(
751            "[[{}, 1, 3, 4/-3],[{}/2, 4, -1/{}, 5],[7, 6, 8/9, 9]]",
752            i64::MIN,
753            i64::MAX,
754            u64::MAX
755        ))
756        .unwrap();
757        let cmp_vec_0 = MatQ::from_str(&format!("[[3],[-1/{}],[8/9]]", u64::MAX)).unwrap();
758        let cmp_vec_1 = MatQ::from_str("[[1],[4],[6]]").unwrap();
759        let cmp_vec_2 = MatQ::from_str(&format!("[[{}],[{}/2],[7]]", i64::MIN, i64::MAX)).unwrap();
760        let cmp_vec_3 = MatQ::from_str("[[4/-3],[5],[9]]").unwrap();
761
762        let _ = matrix.swap_columns(0, 2);
763
764        assert_eq!(cmp_vec_0, matrix.get_column(0).unwrap());
765        assert_eq!(cmp_vec_1, matrix.get_column(1).unwrap());
766        assert_eq!(cmp_vec_2, matrix.get_column(2).unwrap());
767        assert_eq!(cmp_vec_3, matrix.get_column(3).unwrap());
768    }
769
770    /// Ensures that swapping the same column does not change anything
771    #[test]
772    fn columns_swap_same_col() {
773        let mut matrix = MatQ::from_str(&format!(
774            "[[{}, 1, 3, 4],[{}, 4, {}, 5],[7, 6, 8, 9]]",
775            i64::MIN,
776            i64::MAX,
777            u64::MAX
778        ))
779        .unwrap();
780        let cmp = matrix.clone();
781
782        let _ = matrix.swap_columns(0, 0);
783
784        assert_eq!(cmp, matrix);
785    }
786
787    /// Ensures that `swap_columns` returns an error if one of the specified columns is out of bounds
788    #[test]
789    fn column_out_of_bounds() {
790        let mut matrix = MatQ::new(5, 2);
791
792        assert!(matrix.swap_columns(-6, 0).is_err());
793        assert!(matrix.swap_columns(0, -6).is_err());
794        assert!(matrix.swap_columns(5, 0).is_err());
795        assert!(matrix.swap_columns(0, 5).is_err());
796    }
797
798    /// Ensures that swapping rows works fine for small entries
799    #[test]
800    fn rows_small_entries() {
801        let mut matrix = MatQ::from_str("[[1, 2/-3],[3, -4/3]]").unwrap();
802        let cmp_vec_0 = MatQ::from_str("[[3, -4/3]]").unwrap();
803        let cmp_vec_1 = MatQ::from_str("[[1, 2/-3]]").unwrap();
804
805        let _ = matrix.swap_rows(1, 0);
806
807        assert_eq!(cmp_vec_0, matrix.get_row(0).unwrap());
808        assert_eq!(cmp_vec_1, matrix.get_row(1).unwrap());
809    }
810
811    /// Ensures that swapping rows works fine for large entries
812    #[test]
813    fn rows_large_entries() {
814        let mut matrix = MatQ::from_str(&format!(
815            "[[{}, 1, 3, 4],[7, 6/-1, 8, 9/4],[1/{}, 4, -2/{}, 5/3]]",
816            i64::MIN,
817            i64::MAX,
818            u64::MAX
819        ))
820        .unwrap();
821        let cmp_vec_0 =
822            MatQ::from_str(&format!("[[1/{}, 4, -2/{}, 5/3]]", i64::MAX, u64::MAX)).unwrap();
823        let cmp_vec_1 = MatQ::from_str("[[7, 6/-1, 8, 9/4]]").unwrap();
824        let cmp_vec_2 = MatQ::from_str(&format!("[[{}, 1, 3, 4]]", i64::MIN)).unwrap();
825
826        let _ = matrix.swap_rows(0, 2);
827
828        assert_eq!(cmp_vec_0, matrix.get_row(0).unwrap());
829        assert_eq!(cmp_vec_1, matrix.get_row(1).unwrap());
830        assert_eq!(cmp_vec_2, matrix.get_row(2).unwrap());
831    }
832
833    /// Ensures that swapping the same row does not change anything
834    #[test]
835    fn rows_swap_same_row() {
836        let mut matrix = MatQ::from_str(&format!(
837            "[[{}, 1, 3, 4],[{}, 4, {}, 5],[7, 6, 8, 9]]",
838            i64::MIN,
839            i64::MAX,
840            u64::MAX
841        ))
842        .unwrap();
843        let cmp = matrix.clone();
844
845        let _ = matrix.swap_rows(1, 1);
846
847        assert_eq!(cmp, matrix);
848    }
849
850    /// Ensures that `swap_rows` returns an error if one of the specified rows is out of bounds
851    #[test]
852    fn row_out_of_bounds() {
853        let mut matrix = MatQ::new(2, 4);
854
855        assert!(matrix.swap_rows(-3, 0).is_err());
856        assert!(matrix.swap_rows(0, -3).is_err());
857        assert!(matrix.swap_rows(4, 0).is_err());
858        assert!(matrix.swap_rows(0, 4).is_err());
859    }
860
861    /// Ensure that negative indices work for swap_column/row.
862    #[test]
863    fn negative_indexing_row_column() {
864        let mut matrix = MatQ::identity(3, 3);
865        let mut matrix2 = MatQ::identity(3, 3);
866
867        matrix.swap_columns(-1, -2).unwrap();
868        matrix2.swap_rows(-1, -2).unwrap();
869
870        let matrix_cmp = MatQ::from_str("[[1, 0, 0],[0, 0, 1],[0, 1, 0]]").unwrap();
871        assert_eq!(matrix_cmp, matrix);
872        assert_eq!(matrix_cmp, matrix2);
873    }
874}
875
876#[cfg(test)]
877mod test_reverses {
878    use super::MatQ;
879    use crate::traits::MatrixGetSubmatrix;
880    use std::str::FromStr;
881
882    /// Ensures that reversing columns works fine for small entries
883    #[test]
884    fn columns_small_entries() {
885        let mut matrix = MatQ::from_str("[[1, 2/-1, -3/1],[4, 5/6, 6]]").unwrap();
886        let cmp_vec_0 = MatQ::from_str("[[1],[4]]").unwrap();
887        let cmp_vec_1 = MatQ::from_str("[[-2],[5/6]]").unwrap();
888        let cmp_vec_2 = MatQ::from_str("[[-3],[6]]").unwrap();
889
890        matrix.reverse_columns();
891
892        assert_eq!(cmp_vec_2, matrix.get_column(0).unwrap());
893        assert_eq!(cmp_vec_1, matrix.get_column(1).unwrap());
894        assert_eq!(cmp_vec_0, matrix.get_column(2).unwrap());
895    }
896
897    /// Ensures that reversing columns works fine for large entries
898    #[test]
899    fn columns_large_entries() {
900        let mut matrix = MatQ::from_str(&format!(
901            "[[1/{}, 1, 3, 4],[-1/{}, 4, {}, 5],[7, 6, 8/9, 9]]",
902            i64::MIN,
903            i64::MAX,
904            u64::MAX
905        ))
906        .unwrap();
907        let cmp_vec_0 =
908            MatQ::from_str(&format!("[[1/{}],[-1/{}],[7]]", i64::MIN, i64::MAX)).unwrap();
909        let cmp_vec_1 = MatQ::from_str("[[1],[4],[6]]").unwrap();
910        let cmp_vec_2 = MatQ::from_str(&format!("[[3],[{}],[8/9]]", u64::MAX)).unwrap();
911        let cmp_vec_3 = MatQ::from_str("[[4],[5],[9]]").unwrap();
912
913        matrix.reverse_columns();
914
915        assert_eq!(cmp_vec_3, matrix.get_column(0).unwrap());
916        assert_eq!(cmp_vec_2, matrix.get_column(1).unwrap());
917        assert_eq!(cmp_vec_1, matrix.get_column(2).unwrap());
918        assert_eq!(cmp_vec_0, matrix.get_column(3).unwrap());
919    }
920
921    /// Ensures that reversing rows works fine for small entries
922    #[test]
923    fn rows_small_entries() {
924        let mut matrix = MatQ::from_str("[[1, 2/-1],[-3/1, 4]]").unwrap();
925        let cmp_vec_0 = MatQ::from_str("[[1, -2]]").unwrap();
926        let cmp_vec_1 = MatQ::from_str("[[-3, 4]]").unwrap();
927
928        matrix.reverse_rows();
929
930        assert_eq!(cmp_vec_1, matrix.get_row(0).unwrap());
931        assert_eq!(cmp_vec_0, matrix.get_row(1).unwrap());
932    }
933
934    /// Ensures that reversing rows works fine for large entries
935    #[test]
936    fn rows_large_entries() {
937        let mut matrix = MatQ::from_str(&format!(
938            "[[{}, 1, 3, 4],[7, 6, 8, 9],[{}, 4, -2/{}, 5]]",
939            i64::MIN,
940            i64::MAX,
941            u64::MAX
942        ))
943        .unwrap();
944        let cmp_vec_0 = MatQ::from_str(&format!("[[{}, 1, 3, 4]]", i64::MIN)).unwrap();
945        let cmp_vec_1 = MatQ::from_str("[[7, 6, 8, 9]]").unwrap();
946        let cmp_vec_2 =
947            MatQ::from_str(&format!("[[{}, 4, -2/{}, 5]]", i64::MAX, u64::MAX)).unwrap();
948
949        matrix.reverse_rows();
950
951        assert_eq!(cmp_vec_2, matrix.get_row(0).unwrap());
952        assert_eq!(cmp_vec_1, matrix.get_row(1).unwrap());
953        assert_eq!(cmp_vec_0, matrix.get_row(2).unwrap());
954    }
955}
956
957#[cfg(test)]
958mod test_set_submatrix {
959    use crate::{rational::MatQ, traits::MatrixSetSubmatrix};
960    use std::str::FromStr;
961
962    /// Ensure that the entire matrix can be set.
963    #[test]
964    fn entire_matrix() {
965        let mut mat = MatQ::new(10, 10);
966        let identity = MatQ::identity(10, 10);
967
968        mat.set_submatrix(0, 0, &identity, 0, 0, 9, 9).unwrap();
969
970        assert_eq!(identity, mat);
971    }
972
973    /// Ensure that matrix access out of bounds leadss to an error.
974    #[test]
975    fn out_of_bounds() {
976        let mut mat = MatQ::identity(10, 10);
977
978        assert!(mat.set_submatrix(10, 0, &mat.clone(), 0, 0, 9, 9).is_err());
979        assert!(mat.set_submatrix(0, 10, &mat.clone(), 0, 0, 9, 9).is_err());
980        assert!(mat.set_submatrix(0, 0, &mat.clone(), 10, 0, 9, 9).is_err());
981        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 10, 9, 9).is_err());
982        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, 10, 9).is_err());
983        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, 9, 10).is_err());
984        assert!(mat.set_submatrix(-11, 0, &mat.clone(), 0, 0, 9, 9).is_err());
985        assert!(mat.set_submatrix(0, -11, &mat.clone(), 0, 0, 9, 9).is_err());
986        assert!(mat.set_submatrix(0, 0, &mat.clone(), -11, 0, 9, 9).is_err());
987        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, -11, 9, 9).is_err());
988        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, -11, 9).is_err());
989        assert!(mat.set_submatrix(0, 0, &mat.clone(), 0, 0, 9, -11).is_err());
990    }
991
992    /// Ensure that the function returns an error if the defined submatrix is too large
993    /// and there is not enough space in the original matrix.
994    #[test]
995    fn submatrix_too_large() {
996        let mut mat = MatQ::new(10, 10);
997
998        assert!(
999            mat.set_submatrix(0, 0, &MatQ::identity(11, 11), 0, 0, 10, 10)
1000                .is_err()
1001        );
1002        assert!(mat.set_submatrix(1, 2, &mat.clone(), 0, 0, 9, 9).is_err());
1003    }
1004
1005    /// Ensure that setting submatrices with large values works.
1006    #[test]
1007    fn large_values() {
1008        let mut mat = MatQ::from_str(&format!("[[1, {}],[-{}/3, 0]]", u64::MAX, u64::MAX)).unwrap();
1009        let cmp_mat =
1010            MatQ::from_str(&format!("[[-{}/3, 0],[-{}/3, 0]]", u64::MAX, u64::MAX)).unwrap();
1011
1012        mat.set_submatrix(0, 0, &mat.clone(), 1, 0, 1, 1).unwrap();
1013        assert_eq!(cmp_mat, mat);
1014    }
1015
1016    /// Ensure that setting with an undefined submatrix.
1017    #[test]
1018    #[should_panic]
1019    fn submatrix_negative() {
1020        let mut mat = MatQ::identity(10, 10);
1021
1022        let _ = mat.set_submatrix(0, 0, &mat.clone(), 0, 9, 9, 5);
1023    }
1024}