use crate::error::MatrixError;
use crate::number::Number;
use crate::utils::is_upper_triangle_matrix;
use crate::vector::times_v;
use crate::vector::VectorC;
use crate::vector::VectorR;
#[cfg(feature = "rand_mat")]
use rand::{
distributions::{Distribution, Standard},
Rng,
};
#[derive(Debug, Clone)]
pub struct Matrix<T, const ROW: usize, const COL: usize> {
pub(crate) inner: Vec<T>,
}
impl<T, const ROW: usize, const COL: usize> Matrix<T, ROW, COL> {
pub fn create(data: Vec<T>) -> Result<Self, MatrixError> {
if data.len() < ROW * COL {
Err(MatrixError::IncompatibleSizeError((ROW, COL), data.len()))
} else {
Ok(Matrix::<T, ROW, COL> { inner: data })
}
}
pub fn zeros() -> Result<Self, MatrixError>
where
T: Clone + Default,
{
Self::create(vec![T::default(); ROW * COL])
}
pub fn dimensions(&self) -> (usize, usize) {
(ROW, COL)
}
pub(crate) const fn to_inner_index(row: usize, col: usize) -> usize {
(row - 1) * COL + col - 1
}
pub fn get_element(&self, row: usize, col: usize) -> Result<&T, MatrixError> {
match self.inner.get(Self::to_inner_index(row, col)) {
Some(element) => Ok(element),
None => Err(MatrixError::OutOfBoundary(row, col)),
}
}
pub fn set_element(&mut self, row: usize, col: usize, replace: T) -> Result<(), MatrixError> {
match self.inner.get_mut(Self::to_inner_index(row, col)) {
Some(element) => {
*element = replace;
Ok(())
}
None => Err(MatrixError::OutOfBoundary(row, col)),
}
}
pub fn get_row(&self, row: usize) -> Result<VectorR<&T, COL>, MatrixError> {
if row > ROW {
Err(MatrixError::OutOfBoundary(row, 1))
} else {
let mut nth_row = Vec::with_capacity(COL);
for c in 1..=COL {
nth_row.push(self.get_element(row, c)?);
}
VectorR::<&T, COL>::create(nth_row)
}
}
pub fn get_col(&self, col: usize) -> Result<VectorC<&T, ROW>, MatrixError> {
if col > COL {
Err(MatrixError::OutOfBoundary(1, col))
} else {
let mut nth_col = Vec::with_capacity(COL);
for r in 1..=ROW {
nth_col.push(self.get_element(r, col)?);
}
VectorC::<&T, ROW>::create(nth_col)
}
}
pub const fn get_edge() -> usize {
if ROW > COL {
COL
} else {
ROW
}
}
pub fn diag(data: Vec<T>) -> Result<Self, MatrixError>
where
T: Clone + Default,
{
if data.len() < Self::get_edge() {
Err(MatrixError::IncompatibleSizeError((ROW, COL), data.len()))
} else {
let mut diag_mat = Self::zeros()?;
for index in 1..=Self::get_edge() {
diag_mat.set_element(
index,
index,
data.get(index - 1)
.expect(&format!(
"out of boundary: want {} but {}",
index,
data.len()
))
.to_owned(),
)?;
}
Ok(diag_mat)
}
}
pub fn get_diag(&self) -> Result<VectorC<&T, { Self::get_edge() }>, MatrixError> {
let edge: usize = Self::get_edge();
let mut diag = Vec::with_capacity(edge);
for i in 1..=edge {
diag.push(self.get_element(i, i)?);
}
VectorC::<&T, { Self::get_edge() }>::create(diag)
}
pub fn transpose(self) -> Result<Matrix<T, COL, ROW>, MatrixError>
where
T: Clone,
{
let mut transposed = Vec::with_capacity(self.inner.capacity());
for c in 1..=COL {
for r in 1..=ROW {
transposed.push(self.get_element(r, c)?.to_owned());
}
}
Matrix::<T, COL, ROW>::create(transposed)
}
pub fn map<N>(&self, mut f: impl FnMut(T) -> N) -> Result<Matrix<N, ROW, COL>, MatrixError>
where
T: Clone,
{
let mapped = self
.inner
.iter()
.map(|e| f(e.to_owned()))
.collect::<Vec<_>>();
Matrix::<N, ROW, COL>::create(mapped)
}
}
impl<T, const ROW: usize, const COL: usize> Matrix<T, ROW, COL>
where
T: Number,
{
pub fn eyes() -> Result<Self, MatrixError> {
let mut mat = Self::zeros()?;
for i in 1..=Self::get_edge() {
mat.set_element(i, i, T::one())?;
}
Ok(mat)
}
#[cfg(feature = "rand_mat")]
pub fn rand() -> Result<Self, MatrixError>
where
T: Number,
Standard: Distribution<T>,
{
let mut mat = Self::zeros()?;
let mut rng = rand::rngs::ThreadRng::default();
mat.inner = mat.inner.iter().map(|_| rng.gen()).collect();
Ok(mat)
}
pub fn p_change(i: usize, j: usize) -> Result<Matrix<T, ROW, ROW>, MatrixError> {
let mut mat = Matrix::<T, ROW, ROW>::eyes()?;
mat.set_element(i, i, T::zero())?;
mat.set_element(j, j, T::zero())?;
mat.set_element(i, j, T::one())?;
mat.set_element(j, i, T::one())?;
Ok(mat)
}
pub fn p_muls(i: usize, k: T) -> Result<Matrix<T, ROW, ROW>, MatrixError> {
let mut mat = Matrix::<T, ROW, ROW>::eyes()?;
mat.set_element(i, i, k)?;
Ok(mat)
}
pub fn p_add(i: usize, j: usize, k: T) -> Result<Matrix<T, ROW, ROW>, MatrixError> {
let mut mat = Matrix::<T, ROW, ROW>::eyes()?;
mat.set_element(j, i, k)?;
Ok(mat)
}
pub fn plus(self, rhs: Self) -> Result<Self, MatrixError> {
let sum = self
.inner
.iter()
.zip(rhs.inner.iter())
.map(|(a, b)| a.to_owned() + b.to_owned())
.collect::<Vec<_>>();
Self::create(sum)
}
pub fn adds(self, scalar: T) -> Result<Self, MatrixError> {
self.map(|a| scalar.to_owned() + a)
}
pub fn times<const SIDE: usize>(
self,
rhs: Matrix<T, COL, SIDE>,
) -> Result<Matrix<T, ROW, SIDE>, MatrixError> {
let mut product = Matrix::<T, ROW, SIDE>::create(vec![T::zero(); ROW * SIDE])?;
for c in 1..=COL {
product = product
.plus(times_v(
self.get_col(c)?.map(|e| e.to_owned())?,
rhs.get_row(c)?.map(|e| e.to_owned())?,
)?)?
.to_owned();
}
Ok(product)
}
pub fn muls(self, scalar: T) -> Result<Self, MatrixError> {
self.map(|a| scalar.to_owned() * a)
}
pub fn subtract(self, rhs: Self) -> Result<Self, MatrixError> {
self.plus(rhs.map(|e| -e)?)
}
pub fn trace(&self) -> Result<T, MatrixError>
where
[(); Self::get_edge()]:,
{
if ROW == 1 || COL == 1 {
Ok(self
.inner
.iter()
.fold(T::zero(), |acc: T, e: &T| acc + e.to_owned()))
} else {
Ok(self
.get_diag()?
.inner
.iter()
.cloned()
.fold(T::zero(), |acc: T, e: &T| acc + e.to_owned()))
}
}
pub fn rank(&self) -> Result<usize, MatrixError> {
let reduced = self.row_eliminate()?.0;
Ok((1..=ROW)
.rev()
.map(|i| Ok(reduced.get_row(i)?.inner))
.map(|v| -> Result<bool, MatrixError> { Ok(v?.iter().cloned().all(|e| e.is_zero())) })
.filter(|b| b == &Ok(false))
.count())
}
pub fn submatrix(
&self,
row: usize,
col: usize,
) -> Result<Matrix<T, { ROW - 1 }, { COL - 1 }>, MatrixError> {
let mut submat = Matrix::zeros()?;
let mut row_index = 1;
for r in (1..=ROW).filter(|e| e != &row) {
let mut col_index = 1;
for c in (1..=COL).filter(|e| e != &col) {
submat.set_element(row_index, col_index, self.get_element(r, c)?.to_owned())?;
col_index = col_index + 1;
}
row_index = row_index + 1;
}
Ok(submat)
}
pub fn cofactor(
&self,
row: usize,
col: usize,
) -> Result<Matrix<T, { ROW - 1 }, { COL - 1 }>, MatrixError> {
let cofactor = self.submatrix(row, col)?;
let cofactor = cofactor.muls(if (row + col) & 1 == 1 {
-T::one()
} else {
T::one()
})?;
Ok(cofactor)
}
pub fn adjugate(&self) -> Result<Matrix<T, COL, ROW>, MatrixError>
where
[(); ROW - 1]:,
[(); COL - 1]:,
{
if ROW != COL {
Err(MatrixError::IncompatibleShape((ROW, ROW), (ROW, COL)))
} else {
let mut mat = Self::zeros()?;
for row in 1..=ROW {
for col in 1..=COL {
mat.set_element(row, col, self.cofactor(row, col)?.determinant()?)?;
}
}
mat.transpose()
}
}
pub fn row_eliminate(&self) -> Result<(Self, Self, T), MatrixError> {
let mut reduced = self.to_owned();
let mut p_all = Self::eyes()?;
let mut lambda = T::one();
if !is_upper_triangle_matrix(&reduced)? {
let mut col: usize = 0;
for index in 1..=(ROW - 1) {
if index + col > COL {
break;
}
'check_pivot: while reduced.get_element(index, index + col)?.is_zero() {
for above in (index + 1)..=ROW {
if !reduced.get_element(above, index + col)?.is_zero() {
let p_change = Self::p_change(index, above)?;
p_all = p_change.to_owned().times(p_all)?;
reduced = p_change.times(reduced)?;
lambda = -lambda;
break 'check_pivot;
}
}
if index + col < COL {
col = col + 1;
}
}
let value = reduced.to_owned();
let pivot = value.get_element(index, index + col)?;
for above in (index + 1)..=ROW {
let above_pivot = reduced.get_element(above, index + col)?;
let factor = above_pivot.to_owned().ndiv(pivot.to_owned())?;
let p_add = Self::p_add(index, above, -factor)?;
p_all = p_add.to_owned().times(p_all)?;
reduced = p_add.times(reduced)?;
}
}
}
Ok((reduced, p_all, lambda))
}
pub fn determinant(&self) -> Result<T, MatrixError> {
if ROW != COL {
Err(MatrixError::IncompatibleShape((ROW, ROW), (ROW, COL)))
} else {
let (reduced, _, lambda) = self.row_eliminate()?;
(1..=ROW)
.map(|index| reduced.get_element(index, index))
.fold(Ok(T::one()), |acc, e| {
e.and_then(|ev| acc.map(|v| v * ev.to_owned()))
})
.map(|e| e * lambda) }
}
pub fn row_reduce(&self) -> Result<(Self, Self), MatrixError> {
let eliminates = self.row_eliminate()?;
let mut reduced = eliminates.0;
let mut p_all = eliminates.1;
let mut col = 0;
let mut index = 1;
while index <= Self::get_edge() && index + col <= COL {
if reduced.get_element(index, index + col)?.is_zero() {
col += 1;
} else {
let p_smul = Self::p_muls(
index,
T::one().ndiv(reduced.get_element(index, index + col)?.to_owned())?,
)?;
p_all = p_smul.to_owned().times(p_all)?;
reduced = p_smul.times(reduced)?;
for j in 1..index {
if !reduced.get_element(j, index + col)?.is_zero() {
let p = reduced
.get_element(j, index + col)?
.to_owned()
.ndiv(reduced.get_element(index, index + col)?.to_owned())?;
let p_add = Self::p_add(index, j, -p)?;
p_all = p_add.to_owned().times(p_all)?;
reduced = p_add.times(reduced)?;
}
}
index += 1;
}
}
Ok((reduced, p_all))
}
pub fn inverse(&self) -> Result<Self, MatrixError> {
Ok(self.row_reduce()?.1)
}
}
impl<T, const ROW: usize, const COL: usize> std::fmt::Display for Matrix<T, ROW, COL>
where
T: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{")?;
for r in 1..=ROW {
write!(f, "{{")?;
for c in 1..=COL {
if let Ok(element) = self.get_element(r, c) {
write!(f, "{}", element)?;
}
if c == COL {
write!(f, "}}")?;
} else {
write!(f, ", ")?;
}
}
if r != ROW {
write!(f, ", ")?;
}
}
write!(f, "}}")
}
}
impl<T, const ROW: usize, const COL: usize> std::cmp::PartialEq for Matrix<T, ROW, COL>
where
T: std::cmp::PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}