// Matrix properties checks. use num::{Zero, One}; use approx::ApproxEq; use alga::general::{ClosedAdd, ClosedMul, Real}; use core::{DefaultAllocator, Scalar, Matrix, SquareMatrix}; use core::dimension::{Dim, DimMin}; use core::storage::Storage; use core::allocator::Allocator; impl> Matrix { /// Indicates if this is a square matrix. #[inline] pub fn is_empty(&self) -> bool { let (nrows, ncols) = self.shape(); nrows == 0 || ncols == 0 } /// Indicates if this is a square matrix. #[inline] pub fn is_square(&self) -> bool { let (nrows, ncols) = self.shape(); nrows == ncols } // FIXME: ApproxEq prevents us from using those methods on integer matrices… /// Indicated if this is the identity matrix within a relative error of `eps`. /// /// If the matrix is diagonal, this checks that diagonal elements (i.e. at coordinates `(i, i)` /// for i from `0` to `min(R, C)`) are equal one; and that all other elements are zero. #[inline] pub fn is_identity(&self, eps: N::Epsilon) -> bool where N: Zero + One + ApproxEq, N::Epsilon: Copy { let (nrows, ncols) = self.shape(); let d; if nrows > ncols { d = ncols; for i in d .. nrows { for j in 0 .. ncols { if !relative_eq!(self[(i, j)], N::zero(), epsilon = eps) { return false; } } } } else { // nrows <= ncols d = nrows; for i in 0 .. nrows { for j in d .. ncols { if !relative_eq!(self[(i, j)], N::zero(), epsilon = eps) { return false; } } } } // Off-diagonal elements of the sub-square matrix. for i in 1 .. d { for j in 0 .. i { // FIXME: use unsafe indexing. if !relative_eq!(self[(i, j)], N::zero(), epsilon = eps) || !relative_eq!(self[(j, i)], N::zero(), epsilon = eps) { return false; } } } // Diagonal elements of the sub-square matrix. for i in 0 .. d { if !relative_eq!(self[(i, i)], N::one(), epsilon = eps) { return false; } } true } /// Checks that `Mᵀ × M = Id`. /// /// In this definition `Id` is approximately equal to the identity matrix with a relative error /// equal to `eps`. #[inline] pub fn is_orthogonal(&self, eps: N::Epsilon) -> bool where N: Zero + One + ClosedAdd + ClosedMul + ApproxEq, S: Storage, N::Epsilon: Copy, DefaultAllocator: Allocator { (self.tr_mul(self)).is_identity(eps) } } impl> SquareMatrix where DefaultAllocator: Allocator { /// Checks that this matrix is orthogonal and has a determinant equal to 1. #[inline] pub fn is_special_orthogonal(&self, eps: N) -> bool where D: DimMin, DefaultAllocator: Allocator<(usize, usize), D> { self.is_square() && self.is_orthogonal(eps) && self.determinant() > N::zero() } /// Returns `true` if this matrix is invertible. #[inline] pub fn is_invertible(&self) -> bool { // FIXME: improve this? self.clone_owned().try_inverse().is_some() } }