use errors::MatrixError;
use num::{
traits::{One, Zero},
Integer,
};
use std::{
fmt::{self, Debug, Display, Formatter},
ops::{Add, Div, Mul, Neg, Sub},
result::Result,
};
pub mod errors;
mod tests;
pub trait ToMatrix:
Mul<Output = Self>
+ Add<Output = Self>
+ Sub<Output = Self>
+ Zero<Output = Self>
+ Neg<Output = Self>
+ Copy
{
}
impl<T> ToMatrix for T where
T: Mul<Output = T>
+ Add<Output = T>
+ Sub<Output = T>
+ Zero<Output = T>
+ Neg<Output = T>
+ Copy
{
}
#[derive(PartialEq, Debug, Clone)]
pub struct Matrix<T: ToMatrix> {
entries: Vec<Vec<T>>,
}
impl<T: ToMatrix> Matrix<T> {
pub fn from(entries: Vec<Vec<T>>) -> Result<Matrix<T>, MatrixError> {
let mut equal_rows = true;
let row_len = entries[0].len();
for row in &entries {
if row_len != row.len() {
equal_rows = false;
break;
}
}
if equal_rows {
Ok(Matrix { entries })
} else {
Err(MatrixError::UnequalRows)
}
}
pub fn height(&self) -> usize {
self.entries.len()
}
pub fn width(&self) -> usize {
self.entries[0].len()
}
pub fn transpose(&self) -> Self {
let mut out = Vec::new();
for i in 0..self.width() {
let mut column = Vec::new();
for row in &self.entries {
column.push(row[i]);
}
out.push(column)
}
Matrix { entries: out }
}
pub fn rows(&self) -> &Vec<Vec<T>> {
&self.entries
}
pub fn columns(&self) -> Vec<Vec<T>> {
self.transpose().entries
}
pub fn is_square(&self) -> bool {
self.height() == self.width()
}
pub fn submatrix(&self, row: usize, col: usize) -> Self {
let mut out = Vec::new();
for (m, row_iter) in self.entries.iter().enumerate() {
if m == row {
continue;
}
let mut new_row = Vec::new();
for (n, entry) in row_iter.iter().enumerate() {
if n != col {
new_row.push(*entry);
}
}
out.push(new_row);
}
Matrix { entries: out }
}
pub fn det(&self) -> Result<T, MatrixError> {
if self.is_square() {
let out = if self.width() == 1 {
self.entries[0][0]
} else {
let n = 0..self.width();
let mut out = T::zero();
for i in n {
if i.is_even() {
out = out + (self.entries[0][i] * self.submatrix(0, i).det().unwrap());
} else {
out = out - (self.entries[0][i] * self.submatrix(0, i).det().unwrap());
}
}
out
};
Ok(out)
} else {
Err(MatrixError::NotSquare)
}
}
pub fn det_in_field(&self) -> Result<T, MatrixError>
where
T: One,
T: PartialEq,
T: Div<Output = T>,
{
if self.is_square() {
let mut rows = self.entries.clone();
let mut multiplier = T::one();
let h = self.height();
let w = self.width();
for i in 0..(h - 1) {
if rows[i][i] == T::zero() {
let mut zero_column = true;
for j in (i + 1)..h {
if rows[j][i] != T::zero() {
rows.swap(i, j);
multiplier = -multiplier;
zero_column = false;
break;
}
}
if zero_column {
return Ok(T::zero());
}
}
for j in (i + 1)..h {
let ratio = rows[j][i] / rows[i][i];
for k in i..w {
rows[j][k] = rows[j][k] - rows[i][k] * ratio;
}
}
}
for (i, row) in rows.iter().enumerate() {
multiplier = multiplier * row[i];
}
Ok(multiplier)
} else {
Err(MatrixError::NotSquare)
}
}
pub fn row_echelon(&self) -> Self
where
T: PartialEq,
T: Div<Output = T>,
{
let mut rows = self.entries.clone();
let mut offset = 0;
let h = self.height();
let w = self.width();
for i in 0..(h - 1) {
if i + offset >= self.width() {
break;
}
if rows[i][i + offset] == T::zero() {
let mut zero_column = true;
for j in (i + 1)..h {
if rows[j][i + offset] != T::zero() {
rows.swap(i, j);
zero_column = false;
break;
}
}
if zero_column {
offset += 1;
}
}
for j in (i + 1)..h {
let ratio = rows[j][i + offset] / rows[i][i + offset];
for k in (i + offset)..w {
rows[j][k] = rows[j][k] - rows[i][k] * ratio;
}
}
}
Matrix { entries: rows }
}
pub fn column_echelon(&self) -> Self
where
T: PartialEq,
T: Div<Output = T>,
{
self.transpose().row_echelon().transpose()
}
pub fn reduced_row_echelon(&self) -> Self
where
T: PartialEq,
T: Div<Output = T>,
{
let mut echelon = self.row_echelon();
let mut offset = 0;
for row in &mut echelon.entries {
while row[offset] == T::zero() {
offset += 1;
}
let divisor = row[offset];
for entry in row.iter_mut().skip(offset) {
*entry = *entry / divisor;
}
offset += 1;
}
echelon
}
pub fn zero(height: usize, width: usize) -> Self {
let mut out = Vec::new();
for _ in 0..height {
let mut new_row = Vec::new();
for _ in 0..width {
new_row.push(T::zero());
}
out.push(new_row);
}
Matrix { entries: out }
}
pub fn identity(size: usize) -> Self
where
T: One,
{
let mut out = Matrix::zero(size, size);
for (i, row) in out.entries.iter_mut().enumerate() {
row[i] = T::one();
}
out
}
pub fn trace(self) -> Result<T, MatrixError> {
if self.is_square() {
let mut out = self.entries[0][0];
for i in 1..self.height() {
out = out + self.entries[i][i];
}
Ok(out)
} else {
Err(MatrixError::NotSquare)
}
}
pub fn diagonal_matrix(diag: Vec<T>) -> Self {
let size = diag.len();
let mut out = Matrix::zero(size, size);
for (i, row) in out.entries.iter_mut().enumerate() {
row[i] = diag[i];
}
out
}
pub fn mul_scalar(&mut self, scalar: T) {
for row in &mut self.entries {
for entry in row {
*entry = *entry * scalar;
}
}
}
pub fn inverse(&self) -> Result<Self, MatrixError>
where
T: Div<Output = T>,
T: One,
T: PartialEq,
{
if self.is_square() {
let mut rows = self.entries.clone();
let h = self.height();
let w = self.width();
let mut out = Self::identity(h).entries;
for i in 0..(h - 1) {
if rows[i][i] == T::zero() {
let mut zero_column = true;
for j in (i + 1)..h {
if rows[j][i] != T::zero() {
rows.swap(i, j);
out.swap(i, j);
zero_column = false;
break;
}
}
if zero_column {
return Err(MatrixError::Singular);
}
}
for j in (i + 1)..h {
let ratio = rows[j][i] / rows[i][i];
for k in i..w {
rows[j][k] = rows[j][k] - rows[i][k] * ratio;
}
for k in 0..w {
out[j][k] = out[j][k] - out[i][k] * ratio;
}
}
}
for i in 0..h {
if rows[i][i] == T::zero() {
return Err(MatrixError::Singular);
}
let divisor = rows[i][i];
for entry in rows[i].iter_mut().skip(i) {
*entry = *entry / divisor;
}
for entry in out[i].iter_mut() {
*entry = *entry / divisor;
}
}
for i in (1..h).rev() {
for j in (0..i).rev() {
let ratio = rows[j][i];
for k in 0..w {
out[j][k] = out[j][k] - out[i][k] * ratio;
}
}
}
Ok(Matrix { entries: out })
} else {
Err(MatrixError::NotSquare)
}
}
}
impl<T: Debug + ToMatrix> Display for Matrix<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{:?}", self.entries)
}
}
impl<T: Mul<Output = T> + ToMatrix> Mul for Matrix<T> {
type Output = Self;
fn mul(self, other: Self) -> Self::Output {
let width = self.width();
if width != other.height() {
panic!("row length of first matrix != column length of second matrix");
} else {
let mut out = Vec::new();
for row in self.rows() {
let mut new_row = Vec::new();
for col in other.columns() {
let mut prod = row[0] * col[0];
for i in 1..width {
prod = prod + (row[i] * col[i]);
}
new_row.push(prod)
}
out.push(new_row);
}
Matrix { entries: out }
}
}
}
impl<T: Mul<Output = T> + ToMatrix> Add for Matrix<T> {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
if self.height() == other.height() && self.width() == other.width() {
let mut out = self.entries.clone();
for (i, row) in self.rows().iter().enumerate() {
for (j, entry) in other.rows()[i].iter().enumerate() {
out[i][j] = row[j] + *entry;
}
}
Matrix { entries: out }
} else {
panic!("provided matrices have different dimensions");
}
}
}
impl<T: ToMatrix> Neg for Matrix<T> {
type Output = Self;
fn neg(self) -> Self::Output {
let mut out = self;
for row in &mut out.entries {
for entry in row {
*entry = -*entry;
}
}
out
}
}
impl<T: ToMatrix> Sub for Matrix<T> {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
if self.height() == other.height() && self.width() == other.width() {
self + -other
} else {
panic!("provided matrices have different dimensions");
}
}
}
pub trait MatrixFrom<T: ToMatrix> {
fn matrix_from(input: Matrix<T>) -> Self;
}
impl<T: ToMatrix, S: ToMatrix + From<T>> MatrixFrom<T> for Matrix<S> {
fn matrix_from(input: Matrix<T>) -> Self {
let mut out = Vec::new();
for row in input.entries {
let mut new_row: Vec<S> = Vec::new();
for entry in row {
new_row.push(entry.into());
}
out.push(new_row)
}
Matrix { entries: out }
}
}
pub trait MatrixInto<T> {
fn matrix_into(self) -> T;
}
impl<T: MatrixFrom<S>, S: ToMatrix> MatrixInto<T> for Matrix<S> {
fn matrix_into(self) -> T {
T::matrix_from(self)
}
}