Skip to main content

qfall_math/integer_mod_q/mat_zq/
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 flint3_sys::fmpz_mod_mat_trace;
12
13use super::MatZq;
14use crate::{error::MathError, integer_mod_q::Zq, traits::MatrixDimensions};
15
16impl MatZq {
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_mod_q::MatZq;
23    /// use std::str::FromStr;
24    ///
25    /// let matrix = MatZq::from_str("[[1, 2],[3, 4]] mod 5").unwrap();
26    /// let trace = matrix.trace().unwrap();
27    /// ```
28    ///
29    /// # Errors and Failures
30    /// - Returns a [`MathError`] of type [`NoSquareMatrix`](MathError::NoSquareMatrix)
31    ///   if the matrix is not a square matrix
32    pub fn trace(&self) -> Result<Zq, MathError> {
33        // check if matrix is square
34        if self.get_num_rows() != self.get_num_columns() {
35            return Err(MathError::NoSquareMatrix(self.to_string()));
36        }
37
38        let mut out = Zq::from((0, self.get_mod()));
39        unsafe {
40            fmpz_mod_mat_trace(
41                &mut out.value.value,
42                &self.matrix,
43                self.modulus.get_fmpz_mod_ctx_struct(),
44            );
45        }
46        Ok(out)
47    }
48}
49
50#[cfg(test)]
51mod test_trace {
52    use crate::integer_mod_q::{MatZq, Zq};
53    use std::str::FromStr;
54
55    /// Test whether `trace` correctly calculates the trace of a matrix
56    #[test]
57    fn trace_works() {
58        let mat_1 = MatZq::from_str("[[5, 2, 0],[2, 8, 0],[0, 0, 4]] mod 10").unwrap();
59        let mat_2 = MatZq::from_str("[[-1, 0],[0, 1]] mod 2").unwrap();
60
61        let trace_1 = mat_1.trace().unwrap();
62        let trace_2 = mat_2.trace().unwrap();
63
64        assert_eq!(Zq::from((7, 10)), trace_1);
65        assert_eq!(Zq::from((0, 2)), trace_2);
66    }
67
68    /// Test whether `trace` works for large values
69    #[test]
70    fn trace_large_values() {
71        let mat_1 = MatZq::from_str(&format!(
72            "[[{}, 5],[1, {}]] mod {}",
73            i64::MAX,
74            i64::MAX,
75            u64::MAX
76        ))
77        .unwrap();
78        let mat_2 = MatZq::from_str(&format!("[[{}]] mod {}", i64::MIN, u64::MAX)).unwrap();
79        let mat_3 = MatZq::from_str(&format!(
80            "[[{}, 5],[1, {}]] mod {}",
81            i64::MIN,
82            i64::MAX,
83            u64::MAX
84        ))
85        .unwrap();
86
87        let trace_1 = mat_1.trace().unwrap();
88        let trace_2 = mat_2.trace().unwrap();
89        let trace_3 = mat_3.trace().unwrap();
90
91        assert_eq!(Zq::from((2 * i64::MAX as u64, u64::MAX)), trace_1);
92        assert_eq!(Zq::from((i64::MIN, u64::MAX)), trace_2);
93        assert_eq!(Zq::from((-1, u64::MAX)), 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 = MatZq::from_str("[[1, 0, 1],[0, 1, 1]] mod 42").unwrap();
100        let mat_2 = MatZq::from_str("[[1, 0],[0, 1],[1, 0]] mod 17").unwrap();
101
102        assert!(mat_1.trace().is_err());
103        assert!(mat_2.trace().is_err());
104    }
105}