2025-10-10 07:50:21 -06:00
|
|
|
use core::panic;
|
2026-04-27 19:57:17 -06:00
|
|
|
use fractions::Fraction;
|
2026-04-27 21:00:51 -06:00
|
|
|
use std::fmt::{Debug, Display};
|
2025-10-07 11:53:10 -06:00
|
|
|
use std::ops::Add;
|
|
|
|
|
use std::ops::Mul;
|
2025-10-29 15:13:06 -06:00
|
|
|
use std::ops::Sub;
|
2025-10-07 11:53:10 -06:00
|
|
|
|
2026-04-27 21:00:51 -06:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
|
pub enum MatrixError {
|
|
|
|
|
IndexOutOfRange,
|
|
|
|
|
RowOutOfRange,
|
|
|
|
|
ColumnOutOfRange,
|
|
|
|
|
NotSquared,
|
|
|
|
|
InvalidDataSize,
|
|
|
|
|
InvalidSizeForAdd,
|
|
|
|
|
InvalidSizeForSub,
|
|
|
|
|
InvalidSizeForMul,
|
|
|
|
|
ZeroSize,
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 11:53:10 -06:00
|
|
|
#[derive(PartialEq, Eq, Debug)]
|
2026-04-27 21:00:51 -06:00
|
|
|
pub struct Matrix {
|
2025-10-07 11:53:10 -06:00
|
|
|
rows: usize,
|
|
|
|
|
columns: usize,
|
2026-04-27 21:00:51 -06:00
|
|
|
data: Vec<Fraction>,
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:00:51 -06:00
|
|
|
impl Matrix {
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn new(rows: usize, columns: usize, default: Fraction) -> Result<Self, MatrixError> {
|
|
|
|
|
if columns < 1 || rows < 1 {
|
|
|
|
|
return Err(MatrixError::ZeroSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
2025-10-07 11:53:10 -06:00
|
|
|
rows,
|
|
|
|
|
columns,
|
|
|
|
|
data: vec![default; rows * columns],
|
2026-04-27 21:39:26 -06:00
|
|
|
})
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn get(&self, row: usize, column: usize) -> Result<&Fraction, MatrixError> {
|
2025-10-29 07:27:05 -06:00
|
|
|
if row >= self.rows || column >= self.columns {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Err(MatrixError::IndexOutOfRange);
|
2025-10-29 07:27:05 -06:00
|
|
|
}
|
2025-10-07 11:53:10 -06:00
|
|
|
let mut index = 0;
|
|
|
|
|
index += row * self.columns;
|
|
|
|
|
index += column;
|
2026-04-27 21:39:26 -06:00
|
|
|
return Ok(&self.data[index]);
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn get_row(&self, row: usize) -> Result<Vec<Fraction>, MatrixError> {
|
2025-10-07 11:53:10 -06:00
|
|
|
if row >= self.rows {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Err(MatrixError::RowOutOfRange);
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
2025-10-29 15:13:06 -06:00
|
|
|
|
2025-10-07 11:53:10 -06:00
|
|
|
let mut index = 0;
|
|
|
|
|
let mut data = Vec::new();
|
|
|
|
|
|
|
|
|
|
index += self.columns * row;
|
|
|
|
|
for i in 0..self.columns {
|
|
|
|
|
data.push(self.data[index + i]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
return Ok(data);
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn get_column(&self, column: usize) -> Result<Vec<Fraction>, MatrixError> {
|
2025-10-07 11:53:10 -06:00
|
|
|
if column >= self.columns {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Err(MatrixError::ColumnOutOfRange);
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let index = column;
|
|
|
|
|
let mut data = Vec::new();
|
|
|
|
|
|
|
|
|
|
for i in 0..self.rows {
|
|
|
|
|
data.push(self.data[(i * self.columns) + index])
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
return Ok(data);
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn get_diagonal(&self) -> Result<Vec<Fraction>, MatrixError> {
|
2025-10-29 15:11:33 -06:00
|
|
|
if self.columns != self.rows {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Err(MatrixError::NotSquared);
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut data = Vec::new();
|
|
|
|
|
|
2026-04-27 21:00:51 -06:00
|
|
|
let mut index;
|
2025-10-29 15:11:33 -06:00
|
|
|
for i in 0..self.columns {
|
|
|
|
|
index = i + (i * self.columns);
|
|
|
|
|
data.push(self.data[index]);
|
|
|
|
|
}
|
2026-04-27 21:39:26 -06:00
|
|
|
return Ok(data);
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn set(&mut self, row: usize, column: usize, data: Fraction) -> Option<MatrixError> {
|
2025-10-29 07:27:05 -06:00
|
|
|
if row >= self.rows || column >= self.columns {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Some(MatrixError::IndexOutOfRange);
|
2025-10-29 07:27:05 -06:00
|
|
|
}
|
2025-10-07 11:53:10 -06:00
|
|
|
let mut index = 0;
|
|
|
|
|
index += row * self.columns;
|
|
|
|
|
index += column;
|
|
|
|
|
self.data[index] = data;
|
2026-04-27 21:39:26 -06:00
|
|
|
return None;
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
2025-10-29 15:13:06 -06:00
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn set_row(&mut self, row: usize, data: Vec<Fraction>) -> Option<MatrixError> {
|
2025-10-29 15:11:33 -06:00
|
|
|
if row >= self.rows {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Some(MatrixError::IndexOutOfRange);
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
|
|
|
|
if data.len() != self.columns {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Some(MatrixError::InvalidDataSize);
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i in 0..data.len() {
|
|
|
|
|
self.set(row, i, data[i]);
|
|
|
|
|
}
|
2026-04-27 21:39:26 -06:00
|
|
|
|
|
|
|
|
None
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn set_column(&mut self, column: usize, data: Vec<Fraction>) -> Option<MatrixError> {
|
2025-10-29 15:11:33 -06:00
|
|
|
if column >= self.columns {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Some(MatrixError::ColumnOutOfRange);
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
|
|
|
|
if data.len() != self.rows {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Some(MatrixError::InvalidDataSize);
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i in 0..data.len() {
|
|
|
|
|
self.set(i, column, data[i]);
|
|
|
|
|
}
|
2026-04-27 21:39:26 -06:00
|
|
|
|
|
|
|
|
None
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn exchange_rows(&mut self, row1: usize, row2: usize) -> Option<MatrixError> {
|
2025-10-10 07:50:21 -06:00
|
|
|
if row1 >= self.rows || row2 >= self.rows {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Some(MatrixError::RowOutOfRange);
|
2025-10-10 07:50:21 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Get copy of row2
|
2026-04-27 21:39:26 -06:00
|
|
|
let temp = self.get_row(row2).unwrap();
|
2025-10-10 07:50:21 -06:00
|
|
|
let mut index1 = 0;
|
|
|
|
|
let mut index2 = 0;
|
|
|
|
|
//Move from row1 to row2
|
|
|
|
|
index1 += self.columns * row1;
|
|
|
|
|
index2 += self.columns * row2;
|
|
|
|
|
for i in 0..self.columns {
|
|
|
|
|
self.data[index2 + i] = self.data[index1 + i];
|
2025-10-29 15:13:06 -06:00
|
|
|
self.data[index1 + i] = temp[i];
|
2025-10-10 07:50:21 -06:00
|
|
|
}
|
2026-04-27 21:39:26 -06:00
|
|
|
|
|
|
|
|
None
|
2025-10-10 07:50:21 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn exchange_columns(&mut self, column1: usize, column2: usize) -> Option<MatrixError> {
|
2025-10-29 07:27:05 -06:00
|
|
|
if column1 >= self.columns || column2 >= self.columns {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Some(MatrixError::ColumnOutOfRange);
|
2025-10-29 07:27:05 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Get copy of column2
|
2026-04-27 21:39:26 -06:00
|
|
|
let temp = self.get_column(column2).unwrap();
|
2025-10-29 07:27:05 -06:00
|
|
|
for i in 0..self.rows {
|
|
|
|
|
self.data[column2 + (i * self.columns)] = self.data[column1 + (i * self.columns)];
|
|
|
|
|
self.data[column1 + (i * self.columns)] = temp[i];
|
|
|
|
|
}
|
2026-04-27 21:39:26 -06:00
|
|
|
|
|
|
|
|
None
|
2025-10-29 07:27:05 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
pub fn get_determinant(&self) -> Result<Fraction, MatrixError> {
|
2025-10-10 07:50:21 -06:00
|
|
|
if self.rows != self.columns {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Err(MatrixError::NotSquared);
|
2025-10-10 07:50:21 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut trig_matrix = Matrix {
|
|
|
|
|
columns: self.columns,
|
|
|
|
|
rows: self.rows,
|
|
|
|
|
data: self.data.clone(),
|
2025-10-29 15:13:06 -06:00
|
|
|
};
|
2026-04-27 21:00:51 -06:00
|
|
|
let mut sign = Fraction::new(1, 1).unwrap();
|
2025-10-29 15:13:06 -06:00
|
|
|
|
2025-10-10 07:50:21 -06:00
|
|
|
for i in 0..self.columns {
|
2026-04-27 21:00:51 -06:00
|
|
|
let mut max_row = i;
|
2026-04-27 21:39:26 -06:00
|
|
|
let mut max_value = trig_matrix.get(i, i).unwrap().abs();
|
2026-04-27 21:00:51 -06:00
|
|
|
|
|
|
|
|
// We do parcial pivoting to avoid getting insane
|
|
|
|
|
// numbers that may result in overflow with fractions
|
|
|
|
|
for r in (i + 1)..self.rows {
|
2026-04-27 21:39:26 -06:00
|
|
|
let val = trig_matrix.get(r, i).unwrap().abs();
|
2026-04-27 21:00:51 -06:00
|
|
|
if val > max_value {
|
|
|
|
|
max_value = val;
|
|
|
|
|
max_row = r;
|
2025-10-29 15:13:06 -06:00
|
|
|
}
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
2026-04-27 21:00:51 -06:00
|
|
|
|
|
|
|
|
// If there ain't no other thing but 0 then we're
|
|
|
|
|
// fucked, determinant is zero
|
|
|
|
|
if max_value.is_zero() {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Ok(Fraction::new(0, 1).unwrap());
|
2025-10-29 15:11:33 -06:00
|
|
|
}
|
2025-10-29 07:27:05 -06:00
|
|
|
|
2026-04-27 21:00:51 -06:00
|
|
|
if max_row != i {
|
|
|
|
|
trig_matrix.exchange_rows(i, max_row);
|
|
|
|
|
sign = -sign;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
let pivot = *trig_matrix.get(i, i).unwrap();
|
2026-04-27 21:00:51 -06:00
|
|
|
|
|
|
|
|
// The main gaussian elimination, not even I remember how
|
|
|
|
|
// i did it in such a asimple way
|
|
|
|
|
for x in (i + 1)..trig_matrix.rows {
|
2026-04-27 21:39:26 -06:00
|
|
|
let m = (*trig_matrix.get(x, i).unwrap() / pivot).unwrap();
|
|
|
|
|
|
|
|
|
|
let row_x = trig_matrix.get_row(x)?;
|
|
|
|
|
let row_i = trig_matrix.get_row(i)?;
|
2026-04-27 21:00:51 -06:00
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
let new_row = row_x
|
2025-10-29 15:13:06 -06:00
|
|
|
.iter()
|
2026-04-27 21:39:26 -06:00
|
|
|
.zip(row_i.iter())
|
2025-10-29 15:13:06 -06:00
|
|
|
.map(|(a, b)| *a - m * *b)
|
2026-04-27 21:00:51 -06:00
|
|
|
.collect::<Vec<Fraction>>();
|
|
|
|
|
|
2025-10-29 15:11:33 -06:00
|
|
|
trig_matrix.set_row(x, new_row);
|
|
|
|
|
}
|
2025-10-10 07:50:21 -06:00
|
|
|
}
|
|
|
|
|
|
2025-10-29 15:11:33 -06:00
|
|
|
// YES, now we got ourselves a triangular matrix, now we just
|
2025-10-29 15:13:06 -06:00
|
|
|
// take the product of the diagonal and multiply by sign, that's
|
2025-10-29 15:11:33 -06:00
|
|
|
// the determinant :)
|
2026-04-27 21:00:51 -06:00
|
|
|
let determinant = sign
|
|
|
|
|
* trig_matrix
|
2026-04-27 21:39:26 -06:00
|
|
|
.get_diagonal()?
|
2026-04-27 21:00:51 -06:00
|
|
|
.iter()
|
|
|
|
|
.copied()
|
|
|
|
|
.fold(Fraction::from(1i64), |acc, x| acc * x);
|
2025-10-29 15:11:33 -06:00
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
return Ok(determinant);
|
2025-10-10 07:50:21 -06:00
|
|
|
}
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:00:51 -06:00
|
|
|
impl Add for Matrix {
|
2026-04-27 21:39:26 -06:00
|
|
|
type Output = Result<Self, MatrixError>;
|
2025-10-07 11:53:10 -06:00
|
|
|
|
|
|
|
|
fn add(self, other: Self) -> Self::Output {
|
|
|
|
|
if self.data.len() != other.data.len() {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Err(MatrixError::InvalidSizeForAdd);
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut new_data = Vec::new();
|
|
|
|
|
for i in 0..self.data.len() {
|
|
|
|
|
new_data.push(self.data[i] + other.data[i]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
Ok(Matrix {
|
2025-10-07 11:53:10 -06:00
|
|
|
columns: self.columns,
|
|
|
|
|
rows: self.rows,
|
|
|
|
|
data: new_data,
|
2026-04-27 21:39:26 -06:00
|
|
|
})
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:00:51 -06:00
|
|
|
impl Sub for Matrix {
|
2026-04-27 21:39:26 -06:00
|
|
|
type Output = Result<Self, MatrixError>;
|
2025-10-07 11:53:10 -06:00
|
|
|
|
|
|
|
|
fn sub(self, other: Self) -> Self::Output {
|
|
|
|
|
if self.data.len() != other.data.len() {
|
2026-04-27 21:39:26 -06:00
|
|
|
return Err(MatrixError::InvalidSizeForSub);
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut new_data = Vec::new();
|
|
|
|
|
for i in 0..self.data.len() {
|
|
|
|
|
new_data.push(self.data[i] - other.data[i]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
Ok(Matrix {
|
2025-10-07 11:53:10 -06:00
|
|
|
columns: self.columns,
|
|
|
|
|
rows: self.rows,
|
|
|
|
|
data: new_data,
|
2026-04-27 21:39:26 -06:00
|
|
|
})
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:00:51 -06:00
|
|
|
impl Mul for Matrix {
|
2026-04-27 21:39:26 -06:00
|
|
|
type Output = Result<Self, MatrixError>;
|
2025-10-07 11:53:10 -06:00
|
|
|
|
|
|
|
|
fn mul(self, other: Self) -> Self::Output {
|
|
|
|
|
if self.columns != other.rows {
|
|
|
|
|
panic!("Matrix dimentions are inadecuate.");
|
|
|
|
|
}
|
2025-10-29 15:13:06 -06:00
|
|
|
|
2026-04-27 21:00:51 -06:00
|
|
|
let mut new_data: Vec<Fraction> = Vec::new();
|
2025-10-07 11:53:10 -06:00
|
|
|
for i in 0..self.rows {
|
2026-04-27 21:39:26 -06:00
|
|
|
let current_row = self.get_row(i)?;
|
2025-10-07 11:53:10 -06:00
|
|
|
|
|
|
|
|
for k in 0..other.columns {
|
2026-04-27 21:39:26 -06:00
|
|
|
let current_column = other.get_column(k)?;
|
2026-04-27 21:00:51 -06:00
|
|
|
let mut new_value = Fraction::new(0, 1).unwrap();
|
2025-10-07 11:53:10 -06:00
|
|
|
for (a, b) in current_row.iter().zip(current_column.iter()) {
|
2026-04-27 21:00:51 -06:00
|
|
|
new_value = new_value + (*a * *b);
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
new_data.push(new_value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:39:26 -06:00
|
|
|
Ok(Matrix {
|
2025-10-07 11:53:10 -06:00
|
|
|
rows: self.rows,
|
|
|
|
|
columns: other.columns,
|
|
|
|
|
data: new_data,
|
2026-04-27 21:39:26 -06:00
|
|
|
})
|
2025-10-29 15:13:06 -06:00
|
|
|
}
|
2025-10-07 11:53:10 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 21:00:51 -06:00
|
|
|
impl Display for Matrix {
|
2025-10-07 11:53:10 -06:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
let mut display = String::new();
|
|
|
|
|
let mut index = 0;
|
|
|
|
|
for _i in 0..self.columns {
|
|
|
|
|
display += "{";
|
|
|
|
|
for _k in 0..self.rows {
|
|
|
|
|
display += &format!(" {},", self.data[index]);
|
|
|
|
|
index += 1;
|
|
|
|
|
}
|
|
|
|
|
display += " }\n";
|
|
|
|
|
}
|
|
|
|
|
write!(f, "{}", display)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
}
|