use crate::{Determinant, Errors};
use std::fmt::Display;
use std::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign};
#[derive(Clone, PartialEq)]
pub struct Matrix {
items: Vec<f64>,
pub order: (u32, u32),
}
impl Matrix {
pub fn new(items: Vec<f64>, order: (u32, u32)) -> Result<Matrix, Errors> {
if items.len() as u32 != order.0 * order.1 {
return Err(Errors::InappropriateNumberOfItems);
}
Ok(Matrix { items, order })
}
pub fn generate<F>(f: F, order: (u32, u32)) -> Matrix
where
F: Fn(u32, u32) -> f64,
{
let mut items: Vec<f64> = vec![];
for i in 1..=order.0 {
for j in 1..=order.1 {
items.push(f(i, j))
}
}
Matrix { items, order }
}
pub fn row_matrix(items: Vec<f64>) -> Matrix {
let binding = items.len() as u32;
Matrix {
items,
order: (1, binding),
}
}
pub fn column_matrix(items: Vec<f64>) -> Matrix {
let binding = items.len() as u32;
Matrix {
items,
order: (binding, 1),
}
}
pub fn null_matrix(order: (u32, u32)) -> Matrix {
Matrix::generate(|_, _| 0.0, order)
}
pub fn square_matrix(items: Vec<f64>) -> Result<Matrix, Errors> {
let size = (items.len() as f32).sqrt();
if size.fract() != 0.0 {
return Err(Errors::InappropriateNumberOfItems);
}
let size = size as u32;
Ok(Matrix {
items,
order: (size, size),
})
}
pub fn diagonal_matrix(items: Vec<f64>) -> Matrix {
Matrix::generate(
|i, j| {
if i != j {
return 0.0;
}
items[(i - 1) as usize]
},
(items.len() as u32, items.len() as u32),
)
}
pub fn scalar_matrix(item: f64, size: u32) -> Matrix {
Matrix::generate(
|i, j| {
if i != j {
return 0.0;
}
item
},
(size, size),
)
}
pub fn identity_matrix(size: u32) -> Matrix {
Matrix::scalar_matrix(1.0, size)
}
pub fn trace(&self) -> Result<Vec<f64>, Errors> {
if self.order.0 != self.order.1 {
return Err(Errors::TraceExistsOnlyForSquareMatrices);
}
Ok(self
.items
.clone()
.into_iter()
.enumerate()
.filter(|&(i, _)| {
let row = i as u32 / self.order.0;
let column = i as u32 % self.order.1;
row == column
})
.map(|(_, e)| e)
.collect())
}
pub fn transpose(&self) -> Matrix {
Matrix::generate(
|i, j| self.get(j, i).expect("Impossible"),
(self.order.1, self.order.0),
)
}
pub fn to_determinant(&self) -> Result<Determinant, Errors> {
if self.order.0 != self.order.1 {
return Err(Errors::IncorrectOrdersForOperation);
}
Determinant::new(self.items.clone())
}
pub fn adjoint(&self) -> Result<Matrix, Errors> {
let det = self.to_determinant()?;
Ok(
Matrix::generate(|i, j| det.cofactor(i, j).expect("Impossible"), self.order)
.transpose(),
)
}
pub fn inverse(&self) -> Result<Matrix, Errors> {
Ok(self.adjoint()? / self.to_determinant()?.value())
}
pub fn round(&self) -> Matrix {
Matrix::generate(
|i, j| self.get(i, j).expect("Impossible").round(),
self.order,
)
}
pub fn round_mut(&mut self) {
*self = Matrix::generate(
|i, j| self.get(i, j).expect("Impossible").round(),
self.order,
);
}
pub fn is_horizontal(&self) -> bool {
self.order.1 > self.order.0
}
pub fn is_vertical(&self) -> bool {
self.order.0 > self.order.1
}
pub fn get(&self, i: u32, j: u32) -> Result<f64, Errors> {
match self.items.get(((i - 1) * self.order.1 + (j - 1)) as usize) {
Some(item) => Ok(*item),
None => Err(Errors::IndexOutOfRange),
}
}
pub fn get_row(&self, i: u32) -> Result<Vec<f64>, Errors> {
if i == 0 || i > self.order.0 {
return Err(Errors::IndexOutOfRange);
}
Ok(self
.items
.clone()
.into_iter()
.enumerate()
.filter(|&(idx, _)| {
let row = idx as u32 / self.order.1;
row == (i - 1)
})
.map(|(_, e)| e)
.collect())
}
pub fn get_column(&self, j: u32) -> Result<Vec<f64>, Errors> {
if j == 0 || j > self.order.1 {
return Err(Errors::IndexOutOfRange);
}
Ok(self
.items
.clone()
.into_iter()
.enumerate()
.filter(|&(idx, _)| {
let column = idx as u32 % self.order.1;
column == (j - 1)
})
.map(|(_, e)| e)
.collect())
}
pub fn set(&mut self, i: u32, j: u32, new_value: f64) -> Result<(), Errors> {
if i == 0 || i > self.order.0 || j == 0 || j > self.order.1 {
return Err(Errors::IndexOutOfRange);
}
match self
.items
.get_mut(((i - 1) * self.order.1 + (j - 1)) as usize)
{
Some(item) => {
*item = new_value;
Ok(())
}
None => Err(Errors::IndexOutOfRange),
}
}
}
impl Add for Matrix {
type Output = Matrix;
fn add(self, rhs: Self) -> Self::Output {
if self.order != rhs.order {
eprintln!("Error: {}", Errors::IncorrectOrdersForOperation);
panic!();
}
Matrix::generate(
|i, j| self.get(i, j).expect("Impossible") + rhs.get(i, j).expect("Impossible"),
self.order,
)
}
}
impl AddAssign for Matrix {
fn add_assign(&mut self, rhs: Self) {
if self.order != rhs.order {
eprintln!("Error: {}", Errors::IncorrectOrdersForOperation);
panic!();
}
*self = Matrix::generate(
|i, j| self.get(i, j).expect("Impossible") + rhs.get(i, j).expect("Impossible"),
self.order,
);
}
}
impl Sub for Matrix {
type Output = Matrix;
fn sub(self, rhs: Self) -> Self::Output {
if self.order != rhs.order {
eprintln!("Error: {}", Errors::IncorrectOrdersForOperation);
panic!();
}
Matrix::generate(
|i, j| self.get(i, j).expect("Impossible") - rhs.get(i, j).expect("Impossible"),
self.order,
)
}
}
impl SubAssign for Matrix {
fn sub_assign(&mut self, rhs: Self) {
if self.order != rhs.order {
eprintln!("Error: {}", Errors::IncorrectOrdersForOperation);
panic!();
}
*self = Matrix::generate(
|i, j| self.get(i, j).expect("Impossible") - rhs.get(i, j).expect("Impossible"),
self.order,
);
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl Mul for Matrix {
type Output = Matrix;
fn mul(self, rhs: Self) -> Self::Output {
if self.order.1 != rhs.order.0 {
eprintln!("Error: {}", Errors::IncorrectOrdersForOperation);
panic!();
}
Matrix::generate(
|i, j| {
let mut sum = 0.0;
let a = self.get_row(i).expect("Impossible");
let b = rhs.get_column(j).expect("Impossible");
for r in 0..self.order.1 {
sum += a[r as usize] * b[r as usize]
}
sum
},
(self.order.0, rhs.order.1),
)
}
}
impl Mul<f64> for Matrix {
type Output = Matrix;
fn mul(self, rhs: f64) -> Self::Output {
Matrix::generate(|i, j| self.get(i, j).expect("Impossible") * rhs, self.order)
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl MulAssign for Matrix {
fn mul_assign(&mut self, rhs: Self) {
if self.order.1 != rhs.order.0 {
eprintln!("Error: {}", Errors::IncorrectOrdersForOperation);
panic!();
}
*self = Matrix::generate(
|i, j| {
let mut sum = 0.0;
let a = self.get_row(i).expect("Impossible");
let b = rhs.get_column(j).expect("Impossible");
for r in 0..self.order.1 {
sum += a[r as usize] * b[r as usize]
}
sum
},
(self.order.0, rhs.order.1),
);
}
}
impl MulAssign<f64> for Matrix {
fn mul_assign(&mut self, rhs: f64) {
*self = Matrix::generate(|i, j| self.get(i, j).expect("Impossible") * rhs, self.order);
}
}
impl Div<f64> for Matrix {
type Output = Matrix;
fn div(self, rhs: f64) -> Self::Output {
Matrix::generate(|i, j| self.get(i, j).expect("Impossible") / rhs, self.order)
}
}
impl DivAssign<f64> for Matrix {
fn div_assign(&mut self, rhs: f64) {
*self = Matrix::generate(|i, j| self.get(i, j).expect("Impossible") / rhs, self.order)
}
}
impl Index<(u32, u32)> for Matrix {
type Output = f64;
fn index(&self, (i, j): (u32, u32)) -> &Self::Output {
&self.items[((i - 1) * self.order.1 + (j - 1)) as usize]
}
}
impl IndexMut<(u32, u32)> for Matrix {
fn index_mut(&mut self, (i, j): (u32, u32)) -> &mut Self::Output {
&mut self.items[((i - 1) * self.order.1 + (j - 1)) as usize]
}
}
impl Display for Matrix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut print = String::new();
let mut largest_item_size = 0;
for item in self.items.iter() {
let size = item.to_string().len();
if size > largest_item_size {
largest_item_size = size;
}
}
for (i, item) in self.items.iter().enumerate() {
print += &format!(
"{}{} ",
item,
" ".repeat((largest_item_size - item.to_string().len()) as usize)
);
if (i as u32 + 1) % self.order.1 == 0 {
print += "\n";
}
}
f.write_str(&print)
}
}
mod tests {
#[test]
fn addition() {
use crate::Matrix;
let a = Matrix::new(vec![45.0, 2.0, 65.0, 899.0, 6.0, 61.0], (3, 2)).expect("Impossible");
let b = Matrix::new(vec![4.0, 87.0, 2.0, 99.0, 12.0, 44.0], (3, 2)).expect("Impossible");
let sum = a + b;
assert_eq!(sum.order, (3, 2));
assert_eq!(sum.items, vec![49.0, 89.0, 67.0, 998.0, 18.0, 105.0]);
}
#[test]
fn subtraction() {
use crate::Matrix;
let a = Matrix::new(vec![45.0, 2.0, 65.0, 899.0, 6.0, 61.0], (3, 2)).expect("Impossible");
let b = Matrix::new(vec![4.0, 87.0, 2.0, 99.0, 12.0, 44.0], (3, 2)).expect("Impossible");
let diff = a - b;
assert_eq!(diff.order, (3, 2));
assert_eq!(diff.items, vec![41.0, -85.0, 63.0, 800.0, -6.0, 17.0]);
}
#[test]
fn multiplication() {
use crate::Matrix;
let a = Matrix::new(vec![4.0, 87.0, 2.0, 99.0, 12.0, 44.0], (3, 2)).expect("Impossible");
let scalar_mul = a.clone() * 5.0;
assert_eq!(scalar_mul.order, (3, 2));
assert_eq!(
scalar_mul.items,
vec![20.0, 435.0, 10.0, 495.0, 60.0, 220.0]
);
let b = Matrix::new(vec![45.0, 2.0, 65.0, 899.0, 6.0, 61.0], (2, 3)).expect("Impossible");
let matrix_mul = a * b;
assert_eq!(matrix_mul.order, (3, 3));
assert_eq!(
matrix_mul.items,
vec![78393.0, 530.0, 5567.0, 89091.0, 598.0, 6169.0, 40096.0, 288.0, 3464.0]
);
let matrix =
Matrix::new(vec![1.0, 6.0, 4.0, 2.0, 5.0, 7.0, 4.0, 2.0, 9.0], (3, 3)).unwrap();
let inverse = matrix.inverse().unwrap();
assert!((matrix * inverse).round() == Matrix::identity_matrix(3));
}
}