use crate::error::MatrixError;
use crate::matrix::Matrix;
use crate::number::Number;
#[cfg(feature = "rayon_mat")]
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
pub fn points<T, R>(
mut f: impl FnMut(usize, usize) -> (T, R),
row: usize,
col: usize,
) -> Vec<(T, R)> {
let mut ps = Vec::with_capacity(row * col);
for r in 1..=row {
for c in 1..=col {
ps.push(f(r, c))
}
}
ps
}
pub fn horizontal_concat<T, const ROW: usize, const COL: usize, const RCOL: usize>(
mat: &Matrix<T, ROW, COL>,
rhs: &Matrix<T, ROW, RCOL>,
) -> Result<Matrix<T, ROW, { COL + RCOL }>, MatrixError>
where
T: Clone + Default + std::marker::Send + std::marker::Sync,
{
let mut hmat = Matrix::zeros()?;
for r in 1..=ROW {
for c1 in 1..=COL {
hmat.set_element(r, c1, mat.get_element(r, c1)?.to_owned())?;
}
for c2 in 1..=RCOL {
hmat.set_element(r, COL + c2, rhs.get_element(r, c2)?.to_owned())?;
}
}
Ok(hmat)
}
pub fn vertical_concat<T, const ROW: usize, const COL: usize, const RROW: usize>(
mat: &Matrix<T, ROW, COL>,
rhs: &Matrix<T, RROW, COL>,
) -> Result<Matrix<T, { ROW + RROW }, COL>, MatrixError>
where
T: Clone + std::marker::Send + std::marker::Sync,
{
Matrix::create([&mat.inner[..], &rhs.inner[..]].concat())
}
pub fn linear_solve<T, const ROW: usize, const COL: usize, const EDGE: usize>(
mat: Matrix<T, ROW, COL>,
b: Matrix<T, ROW, EDGE>,
) -> Result<Matrix<T, ROW, EDGE>, MatrixError>
where
T: Number,
{
if mat.rank()? > COL {
Err(MatrixError::NoSolution(ROW, COL))
} else {
mat.row_reduce()?.1.times(b)
}
}
pub(crate) fn lower_triangularize<T, const ROW: usize>(
mat: Matrix<T, ROW, ROW>,
) -> Result<(Matrix<T, ROW, ROW>, Matrix<T, ROW, ROW>), MatrixError>
where
T: Number,
{
let mut reduced = mat.to_owned();
let mut p_all = Matrix::<T, ROW, ROW>::eyes()?;
if !(is_lower_triangle_matrix(&reduced)? || ROW < 2) {
let mut next: usize = 0;
for index in (2..=ROW).rev() {
if index <= next {
break;
}
'check_pivot: while reduced.get_element(index, index - next)?.is_zero() {
for above in (1..=(index - 1)).rev() {
if !reduced.get_element(above, index - next)?.is_zero() {
let p_change = Matrix::<T, ROW, ROW>::p_change(index, above)?;
p_all = p_change.to_owned().times(p_all)?;
reduced = p_change.times(reduced)?;
break 'check_pivot;
}
}
if index > next {
next = next + 1;
}
}
let value = reduced.to_owned();
let pivot = value.get_element(index, index - next)?;
for over in (1..(index - 1)).rev() {
let over_pivot = reduced.get_element(over, index - next)?;
if !over_pivot.is_zero() {
let factor = over_pivot.to_owned().ndiv(pivot.to_owned())?;
let p_add = Matrix::<T, ROW, ROW>::p_add(index, over, -factor)?;
p_all = p_add.to_owned().times(p_all)?;
reduced = p_add.times(reduced)?;
}
}
}
}
Ok((p_all, reduced))
}
pub fn plu_decomposition<T, const ROW: usize, const COL: usize>(
mat: Matrix<T, ROW, COL>,
) -> Result<
(
Matrix<T, ROW, ROW>,
Matrix<T, ROW, ROW>,
Matrix<T, ROW, COL>,
),
MatrixError,
>
where
T: Number,
{
if mat.determinant()?.is_zero() {
Err(MatrixError::StrangeMatrix)
} else {
let eliminates = mat.row_eliminate()?;
let pl = lower_triangularize(eliminates.1.inverse()?)?;
Ok((pl.0, pl.1, eliminates.0))
}
}
pub fn qr_decomposition() {
todo!()
}
pub fn eigen_system() {
todo!()
}
pub fn is_sqaure_matrix<T, const ROW: usize, const COL: usize>(_: &Matrix<T, ROW, COL>) -> bool {
ROW == COL
}
pub fn is_upper_triangle_matrix<T, const ROW: usize, const COL: usize>(
m: &Matrix<T, ROW, COL>,
) -> Result<bool, MatrixError>
where
T: Number,
{
#[cfg(feature = "rayon_mat")]
let predicate = points(|r, c| (r, c), ROW, COL)
.par_iter()
.filter(|(r, c)| r > c)
.all(|(r, c)| {
m.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
#[cfg(not(feature = "rayon_mat"))]
let predicate = points(|r, c| (r, c), ROW, COL)
.iter()
.filter(|(r, c)| r > c)
.all(|(r, c)| {
m.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
Ok(predicate)
}
pub fn is_lower_triangle_matrix<T, const ROW: usize, const COL: usize>(
m: &Matrix<T, ROW, COL>,
) -> Result<bool, MatrixError>
where
T: Number,
{
#[cfg(feature = "rayon_mat")]
let predicate = points(|r, c| (r, c), ROW, COL)
.par_iter()
.filter(|(r, c)| r < c)
.all(|(r, c)| {
m.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
#[cfg(not(feature = "rayon_mat"))]
let predicate = points(|r, c| (r, c), ROW, COL)
.iter()
.filter(|(r, c)| r < c)
.all(|(r, c)| {
m.get_element(r.to_owned(), c.to_owned())
.is_ok_and(|e| e.is_zero())
});
Ok(predicate)
}