//! Matrix struct and relevant implementations. //! //! Example usage - addition of two matrices: //! ``` //! use matrix::Matrix; //! use std::str::FromStr; //! let m1 = Matrix::from_str("1,2\n3,4").expect("Expect parse correct"); //! let m2 = Matrix::from_str("1,1\n1,1").expect("Expect parse correct"); //! let m_add = &m1 + &m2; //! println!("m1 + m2 =\n{}", m_add); //! ``` //! TODO:: Create matrix multiplication method use crate::error::{MatrixSetValueError, ParseMatrixError}; use std::{ fmt::Display, ops::{Add, Mul, Sub}, str::FromStr, }; #[derive(Debug, PartialEq, Eq)] pub struct Matrix { /// Number of rows in matrix. pub nrows: usize, /// Number of columns in matrix. pub ncols: usize, /// Data stored in the matrix, you should not access this directly data: Vec>, } pub trait MatrixMath { fn inverse(&self) -> Matrix { (1 / (self.determinant())) * &self.adjoint() } /// Finds the matrix of cofactors for any N-by-N matrix fn cofactor(&self) -> Matrix { todo!(); } /// Finds the matrix of minors for any N-by-N matrix. fn minor(&self) -> Matrix { todo!(); } /// Finds the determinant of any N-by-N matrix. fn determinant(&self) -> i32 { todo!(); } /// Finds the transpose of any matrix. fn transpose(&self) -> Matrix { todo!(); } /// Finds the adjoint matrix (transpose of cofactors) for any N-by-N matrix. fn adjoint(&self) -> Matrix { self.cofactor().transpose() } } impl MatrixMath for Matrix { /// Evaluates any N-by-N matrix. /// /// This function panics if the matrix is not square! fn determinant(&self) -> i32 { if !self.is_square() { panic!() }; if self.nrows == 2 && self.ncols == 2 { return self.data[0][0] * self.data[1][1] - self.data[0][1] * self.data[1][0]; } let mut tmp = 0; for (i, n) in self.data[0].iter().enumerate() { let mult = if i % 2 == 0 { -*n } else { *n }; let eval = self.splice(i).determinant(); tmp += mult * eval; } tmp } /// Evaluates the tranpose of the matrix. /// /// Each row becomes a column, each column becomes a row. fn transpose(&self) -> Matrix { let mut new_data = Vec::>::new(); for i in 0..self.nrows { let mut new_row = Vec::::new(); for j in 0..self.ncols { new_row.push(self.data[j][i]); } new_data.push(new_row); } Matrix { nrows: self.ncols, ncols: self.nrows, data: new_data, } } } impl Matrix { /// Matrix initialiser function. /// /// Accepts a new array of data as a list of rows. /// /// TODOs /// - Add row length check pub fn new(data: Vec>) -> Matrix { Matrix { nrows: data.len(), ncols: data[0].len(), data, } } /// Query one element at selected position. /// /// Returns `None` if index is out of bounds. pub fn get(&self, row_index: usize, column_index: usize) -> Option { let r = self.data.get(row_index)?; let n = r.get(column_index)?; Some(*n) } /// Update one element at selected position. /// /// Returns `Err()` if index is out of bounds. pub fn set( &mut self, row_index: usize, column_index: usize, new_data: i32, ) -> Result<(), MatrixSetValueError> { self.data[row_index][column_index] = new_data; Ok(()) } /// Checks if this is a square matrix. pub fn is_square(&self) -> bool { self.nrows == self.ncols } pub fn splice(&self, at_index: usize) -> Matrix { let mut data: Vec> = Vec::new(); for i in 0..self.data.len() { if i == 0 { continue; } let mut r: Vec = Vec::new(); for j in 0..self.data[i].len() { if j == at_index { continue; } r.push(self.data[i][j]); } data.push(r); } Matrix::new(data) } } impl FromStr for Matrix { type Err = ParseMatrixError; fn from_str(s: &str) -> Result { let mut d: Vec> = Vec::new(); let rows_iter = s.split('\n'); for txt in rows_iter { let mut r: Vec = Vec::new(); for ch in txt.split(',') { let parsed = match i32::from_str(ch) { Ok(n) => Ok(n), Err(_e) => Err(ParseMatrixError), }; r.push(parsed?); } d.push(r); } Ok(Matrix::new(d)) } } impl Display for Matrix { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut builder = String::new(); for (i, r) in self.data.iter().enumerate() { let mut row_str = if i == 0 || i == self.nrows - 1 { "+".to_string() } else { "|".to_string() }; for (j, n) in r.iter().enumerate() { row_str += &format!("{}{}", n, if j == self.ncols - 1 { "" } else { "," }); } row_str += if i == 0 || i == self.nrows - 1 { "+\n" } else { "|\n" }; builder += &row_str; } write!(f, "{}", builder) } } impl<'a, 'b> Add<&'b Matrix> for &'a Matrix { type Output = Matrix; fn add(self, rhs: &'b Matrix) -> Self::Output { if (self.nrows != rhs.nrows) || (self.ncols != rhs.ncols) { panic!("Cannot add two matrices with different dimensions"); } let mut x = Matrix { nrows: self.nrows, ncols: self.ncols, data: self.data.clone(), }; for (i, r) in rhs.data.iter().enumerate() { for (j, n) in r.iter().enumerate() { x.data[i][j] += n; } } x } } impl<'a, 'b> Sub<&'b Matrix> for &'a Matrix { type Output = Matrix; fn sub(self, rhs: &'b Matrix) -> Self::Output { todo!() } } impl<'a> Mul<&'a Matrix> for i32 { type Output = Matrix; fn mul(self, rhs: &'a Matrix) -> Self::Output { let mut d: Vec> = Vec::new(); for r in &rhs.data { let mut nr: Vec = Vec::new(); for v in r { nr.push(self * v); } d.push(nr); } Matrix::new(d) } } impl<'a, 'b> Mul<&'b Matrix> for &'a Matrix { type Output = Matrix; fn mul(self, rhs: &'b Matrix) -> Self::Output { fn reduce(lhs: &Matrix, rhs: &Matrix, at_r: usize, at_c: usize) -> i32 { let mut tmp = 0; for i in 0..lhs.ncols { tmp += lhs.get(at_r, i).unwrap() * rhs.get(i, at_c).unwrap(); } tmp } let mut d: Vec> = Vec::new(); if self.ncols != rhs.nrows { println!("LHS: \n{}RHS: \n{}", self, rhs); println!("LHS nrows: {} ;; RHS ncols: {}", self.nrows, rhs.ncols); panic!() } for i in 0..self.nrows { let mut r: Vec = Vec::new(); for j in 0..rhs.ncols { r.push(reduce(self, rhs, i, j)); } d.push(r); } Matrix::new(d) } } impl From> for Matrix { fn from(value: Vec) -> Self { Matrix { nrows: value.len(), ncols: 1, data: value.iter().map(|v| vec![*v]).collect(), } } }