qfall_math/rational/mat_q/
trace.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//! This module contains the implementation of the `trace` function.
10
11use flint_sys::fmpq_mat::fmpq_mat_trace;
12
13use super::MatQ;
14use crate::{error::MathError, rational::Q, traits::MatrixDimensions};
15
16impl MatQ {
17    /// Returns the trace of a matrix and an error,
18    /// if the matrix is not square.
19    ///
20    /// # Examples
21    /// ```
22    /// use qfall_math::rational::MatQ;
23    /// use std::str::FromStr;
24    ///
25    /// let matrix = MatQ::from_str("[[1/2, 2],[3/7, 4]]").unwrap();
26    /// let trace = matrix.trace().unwrap();
27    /// ```
28    ///
29    /// # Errors and Failures
30    /// - Returns a [`MathError`] of type
31    ///   [`NoSquareMatrix`](MathError::NoSquareMatrix)
32    ///   if the matrix is not a square matrix
33    pub fn trace(&self) -> Result<Q, MathError> {
34        // check if matrix is square
35        if self.get_num_rows() != self.get_num_columns() {
36            return Err(MathError::NoSquareMatrix(self.to_string()));
37        }
38
39        let mut out = Q::default();
40        unsafe {
41            fmpq_mat_trace(&mut out.value, &self.matrix);
42        }
43        Ok(out)
44    }
45}
46
47#[cfg(test)]
48mod test_trace {
49    use crate::rational::{MatQ, Q};
50    use std::str::FromStr;
51
52    /// Test whether `trace` correctly calculates the trace of a matrix
53    #[test]
54    fn trace_works() {
55        let mat_1 = MatQ::from_str("[[5/2, 2, 0],[2, 1/2, 0],[0, 0, 1/3]]").unwrap();
56        let mat_2 = MatQ::from_str("[[-1, 0],[0, 1]]").unwrap();
57
58        let trace_1 = mat_1.trace().unwrap();
59        let trace_2 = mat_2.trace().unwrap();
60
61        assert_eq!(Q::from((10, 3)), trace_1);
62        assert_eq!(Q::ZERO, trace_2);
63    }
64
65    /// Test whether `trace` works for large values
66    #[test]
67    fn trace_large_values() {
68        let mat_1 = MatQ::from_str(&format!("[[{}, 5],[1, {}]]", i64::MAX, i64::MAX)).unwrap();
69        let mat_2 = MatQ::from_str(&format!("[[{}]]", i64::MIN)).unwrap();
70        let mat_3 = MatQ::from_str(&format!("[[{}, 5],[1, 1/{}]]", i64::MIN, i64::MAX)).unwrap();
71
72        let trace_1 = mat_1.trace().unwrap();
73        let trace_2 = mat_2.trace().unwrap();
74        let trace_3 = mat_3.trace().unwrap();
75
76        assert_eq!(Q::from(2 * i64::MAX as u64), trace_1);
77        assert_eq!(Q::from(i64::MIN), trace_2);
78        assert_eq!(Q::from(i64::MIN) + Q::from((1, i64::MAX)), trace_3);
79    }
80
81    /// Ensure that a matrix that is not square yields an error.
82    #[test]
83    fn trace_error_not_squared() {
84        let mat_1 = MatQ::from_str("[[1/2, 0, 1],[0, 1/9, 1]]").unwrap();
85        let mat_2 = MatQ::from_str("[[2/8, 0],[0, 1],[1/7, 0]]").unwrap();
86
87        assert!(mat_1.trace().is_err());
88        assert!(mat_2.trace().is_err());
89    }
90}