extern crate alloc;
use crate::f64_abs;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt::Display;
use core::ops::{
Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign,
};
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Mat<const R: usize, const C: usize>([[f64; C]; R]);
impl<const R: usize, const C: usize> Deref for Mat<R, C> {
type Target = [[f64; C]; R];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const R: usize, const C: usize> DerefMut for Mat<R, C> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<const R: usize, const C: usize> Display for Mat<R, C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let max_column_lengths = (*self.transpose()).map(|column| {
column
.map(|x| x.to_string().len())
.iter()
.copied()
.max()
.unwrap()
});
for (row_index, row) in self.iter().enumerate() {
let row_string = row
.iter()
.enumerate()
.map(|(column_index, column)| {
format!(
"{:^len$}",
column.to_string(),
len = max_column_lengths[column_index]
)
})
.collect::<Vec<String>>()
.join(" ");
let (start_char, end_char) = match row_index {
0 if R == 1 => ("[", "]"),
0 => ("┌", "┐\n"),
int if int == R - 1 => ("└", "┘"),
_ => ("│", "│\n"),
};
write!(f, "{start_char} {row_string} {end_char}")?;
}
Ok(())
}
}
impl<const R: usize, const C: usize, T: Into<f64>> From<[[T; C]; R]> for Mat<R, C> {
fn from(value: [[T; C]; R]) -> Self {
Self(value.map(|row| row.map(|n| n.into())))
}
}
impl<const R: usize, const C: usize> Add for Mat<R, C> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::generate(|row, col| self[row][col] + rhs[row][col])
}
}
impl<const R: usize, const C: usize> AddAssign for Mat<R, C> {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<const R: usize, const C: usize> Sub for Mat<R, C> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::generate(|row, col| self[row][col] - rhs[row][col])
}
}
impl<const R: usize, const C: usize> SubAssign for Mat<R, C> {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<const R: usize, const C: usize, T: Into<f64>> Mul<T> for Mat<R, C> {
type Output = Self;
fn mul(self, scalar: T) -> Self::Output {
let scalar: f64 = scalar.into();
Self((*self).map(|row| row.map(|n| n * scalar)))
}
}
impl<const R: usize, const C: usize, T: Into<f64>> MulAssign<T> for Mat<R, C> {
fn mul_assign(&mut self, scalar: T) {
*self = *self * scalar;
}
}
impl<const R: usize, const C: usize> Neg for Mat<R, C> {
type Output = Self;
fn neg(self) -> Self::Output {
self * -1
}
}
impl<const R: usize, const C: usize, const C2: usize> Mul<Mat<C, C2>> for Mat<R, C> {
type Output = Mat<R, C2>;
fn mul(self, rhs: Mat<C, C2>) -> Self::Output {
Self::Output::generate(|r, c| {
let row = self.row(r);
let col = rhs.col(c);
let dot_product = row
.iter()
.enumerate()
.fold(0.0, |acc, (index, n)| acc + n * col[index]);
dot_product
})
}
}
impl<const R: usize, const C: usize, T: Into<f64>> Div<T> for Mat<R, C> {
type Output = Self;
fn div(self, scalar: T) -> Self::Output {
let scalar: f64 = scalar.into();
Self((*self).map(|row| row.map(|n| n / scalar)))
}
}
impl<const R: usize, const C: usize, T: Into<f64>> DivAssign<T> for Mat<R, C> {
fn div_assign(&mut self, scalar: T) {
*self = *self / scalar;
}
}
impl<const R: usize, const C: usize> Mat<R, C> {
#[must_use]
pub const fn zero() -> Self {
Self([[0.0; C]; R])
}
pub fn generate<F: Fn(usize, usize) -> f64>(f: F) -> Self {
let mut mat = Self::zero();
for (row_index, row) in mat.iter_mut().enumerate() {
for (column_index, n) in row.iter_mut().enumerate() {
*n = f(row_index, column_index);
}
}
mat
}
#[must_use]
pub fn map<F: Fn(f64) -> f64>(&self, f: F) -> Self {
Self::generate(|row, col| f(self[row][col]))
}
#[must_use]
pub fn transpose(&self) -> Mat<C, R> {
Mat::<C, R>::generate(|row, column| self[column][row])
}
#[must_use]
pub fn row(&self, row: usize) -> [f64; C] {
self[row]
}
#[must_use]
pub fn col(&self, col: usize) -> [f64; R] {
(**self).map(|row| row[col])
}
}
impl<const N: usize> Mat<N, N> {
#[must_use]
pub fn identity() -> Self {
Self::generate(|row, column| if row == column { 1.0 } else { 0.0 })
}
#[must_use]
pub fn is_diagonal(&self) -> bool {
for row_index in 0..N {
for col_index in 0..N {
if (row_index != col_index) && (self[row_index][col_index] != 0.0) {
return false;
}
}
}
true
}
#[must_use]
pub fn is_scalar_identity_multiple(&self) -> bool {
*self == Self::identity() * self[0][0]
}
#[must_use]
pub fn is_orthogonal(&self) -> bool {
*self * self.transpose() == Self::identity()
}
#[must_use]
pub fn is_symmetric(&self) -> bool {
*self == self.transpose()
}
#[must_use]
pub fn determinant(&self) -> f64 {
let mut reduced = *self;
let mut transformation_coefficient = 1.0;
for k in 0..N {
let pivot = reduced
.col(k)
.iter()
.enumerate()
.fold(k, |acc, (index, x)| {
if f64_abs(*x) > f64_abs(reduced[acc][k]) {
index
} else {
k
}
});
if reduced[pivot][k] == 0.0 {
return 0.0;
};
if k != pivot {
reduced.swap(k, pivot);
transformation_coefficient *= -1.0;
}
for i in k + 1..N {
let c = -reduced[i][k] / reduced[k][k];
for j in k + 1..N {
reduced[i][j] += reduced[k][j] * c;
}
reduced[i][k] = 0.0;
}
}
let diagonal_product = reduced
.iter()
.enumerate()
.fold(1.0, |acc, (index, row)| acc * row[index]);
diagonal_product * transformation_coefficient
}
#[must_use]
pub fn inverse(&self) -> Option<Self> {
let mut augmented = [*self, Self::identity()];
let mut pivot = 0;
'outer: for row in 0..N {
if pivot >= N * 2 {
break;
}
let mut i = row;
while augmented[pivot / N][i][pivot % N] == 0.0 {
i += 1;
if i == N {
i = row;
pivot += 1;
if pivot == N * 2 {
break 'outer;
}
}
}
if row != i {
for col in 0..N {
let temp = augmented[0][row][col];
augmented[0][row][col] = augmented[0][i][col];
augmented[0][i][col] = temp;
}
}
let divisor = augmented[pivot / N][row][pivot % N];
if divisor != 0.0 {
for col in 0..N * 2 {
augmented[col / N][row][col % N] /= divisor;
}
}
for j in 0..N {
if j != row {
let hold = augmented[pivot / N][j][pivot % N];
for col in 0..N * 2 {
augmented[col / N][j][col % N] -= hold * augmented[col / N][row][col % N];
}
}
}
pivot += 1;
}
if augmented[0] == Self::identity() {
Some(augmented[1])
} else {
None
}
}
}
impl<const N: usize> MulAssign for Mat<N, N> {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __mat_macro {
( $( $($e: expr),* );* ) => {
Mat::from([ $([ $($e),* ]),* ])
};
}
#[doc(inline)]
pub use __mat_macro as mat;