use super::MatPolyOverZ;
use crate::{
integer::Z,
traits::{MatrixDimensions, MatrixGetEntry},
};
use flint_sys::fmpz_poly_mat::{fmpz_poly_mat_is_one, fmpz_poly_mat_is_zero, fmpz_poly_mat_rank};
impl MatPolyOverZ {
pub fn is_identity(&self) -> bool {
1 == unsafe { fmpz_poly_mat_is_one(&self.matrix) }
}
pub fn is_square(&self) -> bool {
self.get_num_columns() == self.get_num_rows()
}
pub fn is_zero(&self) -> bool {
unsafe { 0 != fmpz_poly_mat_is_zero(&self.matrix) }
}
pub fn is_symmetric(&self) -> bool {
if !self.is_square() {
return false;
}
for row in 0..self.get_num_rows() {
for column in 0..row {
if unsafe {
self.get_entry_unchecked(row, column) != self.get_entry_unchecked(column, row)
} {
return false;
}
}
}
true
}
pub fn rank(&self) -> Z {
Z::from(unsafe { fmpz_poly_mat_rank(&self.matrix) })
}
}
#[cfg(test)]
mod test_is_identity {
use crate::integer::MatPolyOverZ;
use std::str::FromStr;
#[test]
fn identity_true() {
let matrix_1x1 = MatPolyOverZ::from_str("[[1 1]]").unwrap();
let matrix_2x2 = MatPolyOverZ::from_str("[[1 1, 0],[0, 1 1]]").unwrap();
let matrix_3x3 =
MatPolyOverZ::from_str("[[1 1, 0, 0],[0, 1 1, 0],[0, 0, 1 1]]").unwrap();
let matrix_2x3 = MatPolyOverZ::from_str("[[1 1, 0, 0],[0, 1 1, 0]]").unwrap();
let matrix_3x2 = MatPolyOverZ::from_str("[[1 1, 0],[0, 1 1],[0, 0]]").unwrap();
assert!(matrix_1x1.is_identity());
assert!(matrix_2x2.is_identity());
assert!(matrix_3x3.is_identity());
assert!(matrix_2x3.is_identity());
assert!(matrix_3x2.is_identity());
}
#[test]
fn not_identity() {
let matrix_side_entry = MatPolyOverZ::from_str("[[1 1, 1 1],[0, 1 1]]").unwrap();
let matrix_negative_1 = MatPolyOverZ::from_str("[[1 -1, 0],[0, 1 1]]").unwrap();
let matrix_negative_2 = MatPolyOverZ::from_str("[[1 -17, 0],[0, 1 1]]").unwrap();
let matrix_higher_degree = MatPolyOverZ::from_str("[[1 1, 0],[0, 2 1 42]]").unwrap();
let matrix_matrix_positive = MatPolyOverZ::from_str("[[1 17, 0],[0, 1 1]]").unwrap();
let matrix_large_negative =
MatPolyOverZ::from_str(&format!("[[1 -{}, 0],[0, 1 1]]", u64::MAX)).unwrap();
let matrix_large_positive =
MatPolyOverZ::from_str(&format!("[[1 {}, 0],[0, 1 1]]", u64::MAX)).unwrap();
assert!(!matrix_side_entry.is_identity());
assert!(!matrix_negative_1.is_identity());
assert!(!matrix_negative_2.is_identity());
assert!(!matrix_higher_degree.is_identity());
assert!(!matrix_matrix_positive.is_identity());
assert!(!matrix_large_negative.is_identity());
assert!(!matrix_large_positive.is_identity());
}
}
#[cfg(test)]
mod test_is_square {
use crate::integer::MatPolyOverZ;
use std::str::FromStr;
#[test]
fn square_matrix() {
let matrix_negative =
MatPolyOverZ::from_str("[[1 -17, 0, 0],[0, 1 1, 0],[0, 0, 0]]").unwrap();
let matrix_higher_degree = MatPolyOverZ::from_str("[[1 1, 0],[0, 2 1 42]]").unwrap();
let matrix_matrix_positive = MatPolyOverZ::from_str("[[1 17]]").unwrap();
let matrix_large_negative =
MatPolyOverZ::from_str(&format!("[[1 -{}, 0],[0, 1 1]]", u64::MAX)).unwrap();
let matrix_large_positive = MatPolyOverZ::from_str(&format!(
"[[1 {}, 0, 0, 0],[0, 1 1, 0, 0],[0, 1 1, 0, 0],[0, 1 1, 0, 0]]",
u64::MAX
))
.unwrap();
assert!(matrix_negative.is_square());
assert!(matrix_matrix_positive.is_square());
assert!(matrix_higher_degree.is_square());
assert!(matrix_large_negative.is_square());
assert!(matrix_large_positive.is_square());
}
#[test]
fn not_square_matrix() {
let matrix_1x2 = MatPolyOverZ::from_str("[[1 1, 0]]").unwrap();
let matrix_2x1 = MatPolyOverZ::from_str("[[1 1],[0]]").unwrap();
let matrix_2x3 = MatPolyOverZ::from_str("[[1 1, 0, 0],[0, 1 1, 0]]").unwrap();
let matrix_3x2 = MatPolyOverZ::from_str("[[1 1, 0],[0, 1 1],[0, 0]]").unwrap();
assert!(!matrix_1x2.is_square());
assert!(!matrix_2x1.is_square());
assert!(!matrix_2x3.is_square());
assert!(!matrix_3x2.is_square());
}
}
#[cfg(test)]
mod test_is_zero {
use super::MatPolyOverZ;
use std::str::FromStr;
#[test]
fn zero_detection() {
let zero = MatPolyOverZ::from_str("[[0, 0],[0, 0]]").unwrap();
let zero_2 = MatPolyOverZ::from_str("[[0, 0],[0, 0],[0, 3 0 0 0]]").unwrap();
assert!(zero.is_zero());
assert!(zero_2.is_zero());
}
#[test]
fn zero_rejection() {
let small = MatPolyOverZ::from_str("[[0, 0],[4 0 0 0 2, 0]]").unwrap();
let large =
MatPolyOverZ::from_str(&format!("[[0, 0],[1 {}, 0]]", (u128::MAX - 1) / 2 + 1))
.unwrap();
assert!(!small.is_zero());
assert!(!large.is_zero());
}
}
#[cfg(test)]
mod test_is_symmetric {
use super::MatPolyOverZ;
use std::str::FromStr;
#[test]
fn symmetric_rejection() {
let mat_2x3 = MatPolyOverZ::from_str("[[0, 1 6, 2 1 4],[1 2, 0, 2 1 1]]").unwrap();
let mat_2x2 = MatPolyOverZ::from_str("[[1 9, 0],[2 1 71, 0]]").unwrap();
assert!(!mat_2x3.is_symmetric());
assert!(!mat_2x2.is_symmetric());
}
#[test]
fn symmetric_detection() {
let mat_2x2 = MatPolyOverZ::from_str(&format!(
"[[2 1 {}, 2 3 {}],[2 3 {}, 3 1 {} 8]]",
u64::MIN,
u64::MAX,
u64::MAX,
i64::MAX
))
.unwrap();
assert!(mat_2x2.is_symmetric());
}
}
#[cfg(test)]
mod test_rank {
use crate::integer::{MatPolyOverZ, Z};
use std::str::FromStr;
#[test]
fn rank_works() {
let mat_1 = MatPolyOverZ::from_str("[[1 5, 1 2],[1 2, 1 1]]").unwrap();
let mat_2 = MatPolyOverZ::from_str(&format!(
"[[2 {} 3, 0, 0, 0],[0, 0, 1 5, 1 7]]",
i64::MIN
))
.unwrap();
let mat_3 = MatPolyOverZ::from_str("[[0],[0]]").unwrap();
let mat_4 = MatPolyOverZ::from_str("[[0, 0],[0, 1 1]]").unwrap();
let mat_5 = MatPolyOverZ::from_str("[[0, 1 1],[0, 1 5]]").unwrap();
let mat_6 =
MatPolyOverZ::from_str("[[1 6, 0, 1 1],[0, 1 1, 0],[2 1 5, 1 2, 0]]").unwrap();
let rank_1 = mat_1.rank();
let rank_2 = mat_2.rank();
let rank_3 = mat_3.rank();
let rank_4 = mat_4.rank();
let rank_5 = mat_5.rank();
let rank_6 = mat_6.rank();
assert_eq!(Z::from(2), rank_1);
assert_eq!(Z::from(2), rank_2);
assert_eq!(Z::ZERO, rank_3);
assert_eq!(Z::ONE, rank_4);
assert_eq!(Z::ONE, rank_5);
assert_eq!(Z::from(3), rank_6);
}
}