qfall_math/integer/mat_poly_over_z/
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::fmpz_poly_mat::fmpz_poly_mat_trace;
12
13use super::MatPolyOverZ;
14use crate::{error::MathError, integer::PolyOverZ, traits::MatrixDimensions};
15
16impl MatPolyOverZ {
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::integer::MatPolyOverZ;
23    /// use std::str::FromStr;
24    ///
25    /// let matrix = MatPolyOverZ::from_str("[[1  42, 2  1 2],[1  4, 0]]").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<PolyOverZ, 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 = PolyOverZ::default();
40        unsafe {
41            fmpz_poly_mat_trace(&mut out.poly, &self.matrix);
42        }
43        Ok(out)
44    }
45}
46
47#[cfg(test)]
48mod test_trace {
49    use crate::integer::{MatPolyOverZ, PolyOverZ};
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 =
56            MatPolyOverZ::from_str("[[2  4 5, 1  2, 0],[1  2, 1  1, 0],[0, 3  1 2 3, 1  1]]")
57                .unwrap();
58        let mat_2 = MatPolyOverZ::from_str("[[2  -1 -1, 0],[0, 2  1 1]]").unwrap();
59
60        let trace_1 = mat_1.trace().unwrap();
61        let trace_2 = mat_2.trace().unwrap();
62
63        assert_eq!(PolyOverZ::from_str("2  6 5").unwrap(), trace_1);
64        assert_eq!(PolyOverZ::default(), trace_2);
65    }
66
67    /// Test whether `trace` works for large values
68    #[test]
69    fn trace_large_values() {
70        let mat_1 = MatPolyOverZ::from_str(&format!(
71            "[[2  -1 {}, 1  5],[3  1 2 3, 1  {}]]",
72            i64::MAX,
73            i64::MAX
74        ))
75        .unwrap();
76        let mat_2 = MatPolyOverZ::from_str(&format!("[[1  {}]]", i64::MIN)).unwrap();
77        let mat_3 = MatPolyOverZ::from_str(&format!(
78            "[[1  {}, 1  5],[3  1 2 3, 1  {}]]",
79            i64::MIN,
80            i64::MAX
81        ))
82        .unwrap();
83
84        let trace_1 = mat_1.trace().unwrap();
85        let trace_2 = mat_2.trace().unwrap();
86        let trace_3 = mat_3.trace().unwrap();
87
88        assert_eq!(
89            PolyOverZ::from_str(&format!("2  {} {}", i64::MAX - 1, i64::MAX)).unwrap(),
90            trace_1
91        );
92        assert_eq!(PolyOverZ::from(i64::MIN), trace_2);
93        assert_eq!(PolyOverZ::from(-1), trace_3);
94    }
95
96    /// Ensure that a matrix that is not square yields an error.
97    #[test]
98    fn trace_error_not_squared() {
99        let mat_1 = MatPolyOverZ::from_str("[[1  1, 0, 1  1],[0, 1  2, 1  3]]").unwrap();
100        let mat_2 = MatPolyOverZ::from_str("[[1  42, 0],[0, 3  17 9 8],[1  3, 0]]").unwrap();
101
102        assert!(mat_1.trace().is_err());
103        assert!(mat_2.trace().is_err());
104    }
105}