qfall_math/integer_mod_q/mat_zq/
properties.rs

1// Copyright © 2023 Phil Milewski
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 includes functionality about properties of [`MatZq`] instances.
10
11use super::MatZq;
12use crate::{
13    integer::Z,
14    traits::{MatrixDimensions, MatrixGetEntry},
15};
16use flint_sys::{
17    fmpz_mat::fmpz_mat_is_one,
18    fmpz_mod_mat::{fmpz_mod_mat_is_square, fmpz_mod_mat_is_zero},
19};
20
21impl MatZq {
22    /// Checks if a [`MatZq`] is the identity matrix.
23    ///
24    /// Returns `true` if every diagonal entry of the upper square matrix is `1`
25    /// and all other entries are `0`.
26    ///
27    /// # Examples
28    /// ```
29    /// use qfall_math::integer_mod_q::MatZq;
30    /// use std::str::FromStr;
31    ///
32    /// let value = MatZq::from_str("[[1, 0],[0, 1]] mod 17").unwrap();
33    /// assert!(value.is_identity());
34    /// ```
35    ///
36    /// ```
37    /// use qfall_math::integer_mod_q::MatZq;
38    /// use std::str::FromStr;
39    ///
40    /// let value = MatZq::from_str("[[1, 0],[0, 1],[0, 0]] mod 17").unwrap();
41    /// assert!(value.is_identity());
42    /// ```
43    pub fn is_identity(&self) -> bool {
44        unsafe { 1 == fmpz_mat_is_one(&self.matrix.mat[0]) }
45    }
46
47    /// Checks if a [`MatZq`] is a square matrix.
48    ///
49    /// Returns `true` if the number of rows and columns is identical.
50    ///
51    /// # Examples
52    /// ```
53    /// use qfall_math::integer_mod_q::MatZq;
54    /// use std::str::FromStr;
55    ///
56    /// let value = MatZq::from_str("[[4, 0],[0, 1]] mod 17").unwrap();
57    /// assert!(value.is_square());
58    /// ```
59    pub fn is_square(&self) -> bool {
60        1 == unsafe { fmpz_mod_mat_is_square(&self.matrix) }
61    }
62
63    /// Checks if every entry of a [`MatZq`] is `0`.
64    ///
65    /// Returns `true` if every entry is `0`.
66    ///
67    /// # Examples
68    /// ```
69    /// use qfall_math::integer_mod_q::MatZq;
70    /// use std::str::FromStr;
71    ///
72    /// let value = MatZq::from_str("[[0, 0],[0, 0]] mod 17").unwrap();
73    /// assert!(value.is_zero());
74    /// ```
75    pub fn is_zero(&self) -> bool {
76        1 == unsafe { fmpz_mod_mat_is_zero(&self.matrix) }
77    }
78
79    /// Checks if a [`MatZq`] is symmetric.
80    ///
81    /// Returns `true` if we have `a_ij == a_ji` for all i,j.
82    ///
83    /// # Examples
84    /// ```
85    /// use qfall_math::integer_mod_q::MatZq;
86    ///
87    /// let value = MatZq::identity(2,2,3);
88    /// assert!(value.is_symmetric());
89    /// ```
90    pub fn is_symmetric(&self) -> bool {
91        if !self.is_square() {
92            return false;
93        }
94        for row in 0..self.get_num_rows() {
95            for column in 0..row {
96                if unsafe {
97                    MatrixGetEntry::<Z>::get_entry_unchecked(self, row, column)
98                        != MatrixGetEntry::<Z>::get_entry_unchecked(self, column, row)
99                } {
100                    return false;
101                }
102            }
103        }
104        true
105    }
106}
107
108#[cfg(test)]
109mod test_is_identity {
110    use super::MatZq;
111    use std::str::FromStr;
112
113    /// Ensure that is_identity returns `true` for identity matrices.
114    #[test]
115    fn identity_detection() {
116        let ident_1 = MatZq::from_str("[[1, 0],[0, 1]] mod 7").unwrap();
117        let ident_2 = MatZq::from_str("[[1, 0],[0, 1],[0, 0]] mod 7").unwrap();
118
119        assert!(ident_1.is_identity());
120        assert!(ident_2.is_identity());
121    }
122
123    /// Ensure that is_identity returns `false` for non-identity matrices.
124    #[test]
125    fn identity_rejection() {
126        let small = MatZq::from_str("[[0, 0],[2, 0]] mod 17").unwrap();
127        let large = MatZq::from_str(&format!(
128            "[[1, 0],[0, {}]] mod {}",
129            (u128::MAX - 1) / 2 + 2,
130            u128::MAX
131        ))
132        .unwrap(); // the last 64 bit represent `1`
133
134        assert!(!small.is_identity());
135        assert!(!large.is_identity());
136    }
137}
138
139#[cfg(test)]
140mod test_is_zero {
141    use super::MatZq;
142    use std::str::FromStr;
143
144    /// Ensure that is_zero returns `true` for all zero matrices.
145    #[test]
146    fn zero_detection() {
147        let zero_1 = MatZq::from_str("[[0, 0],[0, 0]] mod 7").unwrap();
148        let zero_2 = MatZq::from_str("[[0, 0],[0, 0],[0, 0],[0, 0]] mod 7").unwrap();
149
150        assert!(zero_1.is_zero());
151        assert!(zero_2.is_zero());
152    }
153
154    /// Ensure that is_zero returns `false` for non-zero matrices.
155    #[test]
156    fn zero_rejection() {
157        let small = MatZq::from_str("[[0, 0],[2, 0]] mod 7").unwrap();
158        let large = MatZq::from_str(&format!(
159            "[[0, 0],[{}, 0]] mod {}",
160            (u128::MAX - 1) / 2 + 1,
161            u128::MAX
162        ))
163        .unwrap();
164
165        assert!(!small.is_zero());
166        assert!(!large.is_zero());
167    }
168}
169
170#[cfg(test)]
171mod test_is_square {
172    use super::MatZq;
173    use std::str::FromStr;
174
175    /// Ensure that is_square returns `true` for square matrices.
176    #[test]
177    fn square_detection() {
178        let square_1 = MatZq::from_str("[[0, 4],[0, 0]] mod 10").unwrap();
179        let square_2 = MatZq::from_str("[[0, 6, 4],[0, 0, 1],[4, 6, 1]] mod 7").unwrap();
180
181        assert!(square_1.is_square());
182        assert!(square_2.is_square());
183    }
184
185    /// Ensure that is_square returns `false` for non-square matrices.
186    #[test]
187    fn sqaure_rejection() {
188        let small = MatZq::from_str("[[0, 0, 4],[2, 0, 1]] mod 7").unwrap();
189        let large = MatZq::from_str(&format!(
190            "[[9, 0],[{}, 0],[1, 4]] mod {}",
191            (u128::MAX - 1) / 2 + 1,
192            u128::MAX
193        ))
194        .unwrap();
195
196        assert!(!small.is_square());
197        assert!(!large.is_square());
198    }
199}
200
201#[cfg(test)]
202mod test_is_symmetric {
203    use super::MatZq;
204    use std::str::FromStr;
205
206    /// Ensure that is_symmetric returns `false` for non-symmetric matrices.
207    #[test]
208    fn symmetric_rejection() {
209        let mat_2x3 = MatZq::from_str("[[0, 5, 4],[2, 0, 1]] mod 17").unwrap();
210        let mat_2x2 = MatZq::from_str("[[9, 0],[127, 0]] mod 17").unwrap();
211
212        assert!(!mat_2x3.is_symmetric());
213        assert!(!mat_2x2.is_symmetric());
214    }
215
216    /// Ensure that is_symmetric returns `true` for symmetric matrices.
217    #[test]
218    fn symmetric_detection() {
219        let mat_2x2 = MatZq::from_str(&format!(
220            "[[2, {}],[{}, {}]] mod {}",
221            i64::MAX,
222            i64::MAX,
223            u64::MIN,
224            u64::MAX
225        ))
226        .unwrap();
227
228        assert!(mat_2x2.is_symmetric());
229    }
230}