use crate::Ratio;
pub use err::MatrixError;
mod det;
mod err;
mod inv;
mod utils;
#[derive(Clone, Debug, PartialEq)]
pub struct Matrix{
data: Vec<Vec<Ratio>>,
cols: usize,
rows: usize,
}
impl Matrix {
pub fn from_vec(data: Vec<Vec<Ratio>>) -> Result<Self, MatrixError> {
if data.is_empty() {
return Ok(Matrix::empty());
}
let rows = data.len();
let cols = data[0].len();
for row in data.iter() {
if row.len() != cols {
return Err(MatrixError::InconsistetRow);
}
}
Ok(Matrix { data, cols, rows })
}
pub fn from_vec_generic<T: Into<Ratio>>(data: Vec<Vec<T>>) -> Result<Self, MatrixError> {
Matrix::from_vec(
data.into_iter().map(
|row| row.into_iter().map(
|n| n.into()
).collect::<Vec<Ratio>>()
).collect()
)
}
pub fn from_raw(data: Vec<Vec<Ratio>>, cols: usize, rows: usize) -> Self {
#[cfg(test)] {
let result = Matrix::from_vec(data.clone()).unwrap();
assert_eq!(cols, result.cols);
assert_eq!(rows, result.rows);
}
Matrix { data, cols, rows }
}
pub fn generate(cols: usize, rows: usize, f: impl Fn(usize, usize) -> Ratio) -> Self {
Matrix {
data: (0..rows).map(
|i| (0..cols).map(
|j| f(i, j)
).collect::<Vec<Ratio>>()
).collect(),
cols, rows,
}
}
pub fn empty() -> Self {
Matrix { data: vec![], cols: 0, rows: 0 }
}
pub fn zeros(cols: usize, rows: usize) -> Self {
Matrix {
data: vec![vec![0.into(); cols]; rows],
cols, rows,
}
}
pub fn identity(n: usize) -> Self {
let mut result = Matrix::zeros(n, n);
for i in 0..n {
*result.get_mut(i, i) = Ratio::one();
}
result
}
pub fn is_square(&self) -> bool {
self.cols == self.rows
}
#[inline]
pub fn get(&self, i: usize, j: usize) -> &Ratio {
&self.data[i][j]
}
#[inline]
pub fn get_mut(&mut self, i: usize, j: usize) -> &mut Ratio {
&mut self.data[i][j]
}
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn add(&self, other: &Matrix) -> Result<Self, MatrixError> {
if (self.cols, self.rows) != (other.cols, other.rows) {
return Err(MatrixError::WrongDimension {
expected: (self.cols, self.rows),
got: (other.cols, other.rows),
});
}
let result = self.data.iter().enumerate().map(
|(row_ind, row)| row.iter().enumerate().map(
|(col_ind, n)| n.add_rat(other.get(row_ind, col_ind))
).collect::<Vec<Ratio>>()
).collect();
let result = Ok(Matrix::from_raw(result, self.cols, self.rows));
#[cfg(test)] {
let mut k = self.clone();
k.add_mut(other).unwrap();
assert_eq!(result, Ok(k));
}
result
}
pub fn add_mut(&mut self, other: &Matrix) -> Result<(), MatrixError> {
if (self.cols, self.rows) != (other.cols, other.rows) {
return Err(MatrixError::WrongDimension {
expected: (self.cols, self.rows),
got: (other.cols, other.rows),
});
}
for i in 0..self.rows {
for j in 0..self.cols {
self.get_mut(i, j).add_rat_mut(other.get(i, j));
}
}
Ok(())
}
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn mul_k<T: Into<Ratio>>(&self, k: T) -> Self {
let k = k.into();
let result = self.data.iter().map(
|row| row.iter().map(
|n| n.mul_rat(&k)
).collect::<Vec<Ratio>>()
).collect();
let result = Matrix::from_raw(result, self.cols, self.rows);
#[cfg(test)] {
let mut s = self.clone();
s.mul_k_mut(k);
assert_eq!(result, s);
}
result
}
pub fn mul_k_mut<T: Into<Ratio>>(&mut self, k: T) {
let k = k.into();
for i in 0..self.rows {
for j in 0..self.cols {
self.get_mut(i, j).mul_rat_mut(&k);
}
}
}
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn mul(&self, other: &Matrix) -> Result<Self, MatrixError> {
if self.cols != other.rows {
return Err(MatrixError::WrongDimension {
expected: (other.cols, self.cols),
got: (other.cols, other.rows),
});
}
let mut result = Matrix::zeros(other.cols, self.rows);
for i in 0..self.rows {
for j in 0..other.cols {
for k in 0..self.cols {
result.get_mut(i, j).add_rat_mut(&self.get(i, k).mul_rat(other.get(k, j)));
}
}
}
Ok(result)
}
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn transpose(&self) -> Self {
let result = (0..self.cols).map(
|i| (0..self.rows).map(
|j| self.get(j, i).clone()
).collect::<Vec<Ratio>>()
).collect::<Vec<Vec<Ratio>>>();
let result = Matrix::from_raw(result, self.rows, self.cols);
result
}
pub fn transpose_mut(&mut self) {
*self = self.transpose();
}
pub fn to_ratio_string(&self) -> String {
format!(
"[{}]", self.data.iter().map(
|row| format!(
"[{}]", row.iter().map(
|n| n.to_ratio_string()
).collect::<Vec<String>>().join(", ")
)
).collect::<Vec<String>>().join(", ")
)
}
#[cfg(feature = "rand")]
pub fn random(cols: usize, rows: usize) -> Self {
Matrix::from_raw(
(0..rows).map(
|_| (0..cols).map(
|_| Ratio::random()
).collect::<Vec<Ratio>>(),
).collect(),
cols,
rows,
)
}
}
#[cfg(test)]
mod tests {
use crate::Matrix;
#[test]
fn basic_test() {
let mat0 = Matrix::from_vec_generic(vec![
vec![7, 3, 3],
vec![2, 9, 1],
vec![5, 5, 3],
vec![6, 5, 1],
]).unwrap();
let mat1 = Matrix::from_vec_generic(vec![
vec![1, 3, 6, 7],
vec![0, 7, 2, 8],
vec![0, 5, 1, 1],
]).unwrap();
let mat2 = Matrix::from_vec_generic(vec![
vec![3, 5, 2, 1],
vec![0, 1, 1, 7],
vec![1, 0, 0, 9],
]).unwrap();
let mat3 = Matrix::from_vec_generic(vec![
vec![4, 8, 8, 8],
vec![0, 8, 3, 15],
vec![1, 5, 1, 10],
]).unwrap();
let mat4 = mat1.add(&mat1).unwrap();
assert_eq!(mat4, mat1.mul_k(2));
let mat5 = mat2.add(&mat2).unwrap();
let mat5 = mat5.add(&mat2).unwrap();
assert_eq!(mat5, mat2.mul_k(3));
let mat6 = mat1.add(&mat2).unwrap();
assert_eq!(mat6, mat3);
assert_eq!(mat1.transpose().transpose(), mat1);
assert_eq!(mat2.transpose().transpose(), mat2);
assert_eq!(mat3.transpose().transpose(), mat3);
let mat7 = mat0.mul(&mat1).unwrap();
assert_eq!(mat7, Matrix::from_vec_generic(vec![
vec![7, 57, 51, 76],
vec![2, 74, 31, 87],
vec![5, 65, 43, 78],
vec![6, 58, 47, 83],
]).unwrap());
}
}