Files
Rusty-Matrices/src/lib.rs

3640 lines
94 KiB
Rust
Raw Normal View History

pub use fractions::{Fraction, FractionError};
use std::cmp::min;
2026-04-29 17:57:30 -06:00
use std::error::Error;
use std::fmt::{self, 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,
2026-04-27 23:41:18 -06:00
FailedGauss,
FailedGaussJordan,
FractionError(FractionError),
}
impl From<FractionError> for MatrixError {
fn from(err: FractionError) -> Self {
MatrixError::FractionError(err)
}
2026-04-27 21:00:51 -06:00
}
2026-04-29 17:57:30 -06:00
impl fmt::Display for MatrixError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MatrixError::IndexOutOfRange => write!(f, "Index out of range"),
MatrixError::RowOutOfRange => write!(f, "Row index out of range"),
MatrixError::ColumnOutOfRange => write!(f, "Column index out of range"),
MatrixError::NotSquared => write!(f, "Matrix must be square"),
MatrixError::InvalidDataSize => write!(f, "Invalid data size for matrix"),
MatrixError::InvalidSizeForAdd => {
write!(f, "Matrices must have same dimensions for addition")
}
MatrixError::InvalidSizeForSub => {
write!(f, "Matrices must have same dimensions for subtraction")
}
MatrixError::InvalidSizeForMul => {
write!(f, "Invalid matrix dimensions for multiplication")
}
MatrixError::ZeroSize => write!(f, "Matrix cannot have zero rows or columns"),
MatrixError::FailedGauss => {
write!(f, "Gaussian elimination failed (possibly singular matrix)")
}
MatrixError::FailedGaussJordan => write!(
f,
"Gauss-Jordan elimination failed (possibly singular matrix)"
),
MatrixError::FractionError(err) => write!(f, "Fraction error: {}", err),
}
}
}
impl Error for MatrixError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
MatrixError::FractionError(err) => Some(err),
_ => None,
}
}
}
2026-04-29 16:57:35 -06:00
#[derive(PartialEq, Eq, Debug, Clone)]
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 {
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],
})
2025-10-07 11:53:10 -06:00
}
pub fn get(&self, row: usize, column: usize) -> Result<&Fraction, MatrixError> {
if row >= self.rows || column >= self.columns {
return Err(MatrixError::IndexOutOfRange);
}
2026-04-29 15:42:33 -06:00
let index = row * self.columns + column;
return Ok(&self.data[index]);
2025-10-07 11:53:10 -06:00
}
pub fn get_row(&self, row: usize) -> Result<impl Iterator<Item = &Fraction>, MatrixError> {
2025-10-07 11:53:10 -06:00
if row >= self.rows {
return Err(MatrixError::RowOutOfRange);
2025-10-07 11:53:10 -06:00
}
2025-10-29 15:13:06 -06:00
let start = row * self.columns;
let end = start + self.columns;
2025-10-07 11:53:10 -06:00
return Ok(self.data[start..end].iter());
2025-10-07 11:53:10 -06:00
}
pub fn get_column(
&self,
column: usize,
) -> Result<impl Iterator<Item = &Fraction>, MatrixError> {
2025-10-07 11:53:10 -06:00
if column >= self.columns {
return Err(MatrixError::ColumnOutOfRange);
2025-10-07 11:53:10 -06:00
}
Ok((0..self.rows).map(move |i| &self.data[i * self.columns + column]))
2025-10-07 11:53:10 -06:00
}
pub fn get_diagonal(&self) -> Result<impl Iterator<Item = &Fraction>, MatrixError> {
if self.columns != self.rows {
return Err(MatrixError::NotSquared);
}
Ok((0..self.rows).map(move |i| &self.data[i * self.columns + i]))
}
pub fn set(&mut self, row: usize, column: usize, data: Fraction) -> Result<(), MatrixError> {
if row >= self.rows || column >= self.columns {
return Err(MatrixError::IndexOutOfRange);
}
2026-04-29 15:58:48 -06:00
let index = row * self.columns + column;
2025-10-07 11:53:10 -06:00
self.data[index] = data;
Ok(())
2025-10-07 11:53:10 -06:00
}
2025-10-29 15:13:06 -06:00
pub fn set_row(&mut self, row: usize, data: Vec<Fraction>) -> Result<(), MatrixError> {
if row >= self.rows {
return Err(MatrixError::IndexOutOfRange);
}
if data.len() != self.columns {
return Err(MatrixError::InvalidDataSize);
}
2026-04-29 16:07:30 -06:00
let start = row * self.columns;
let end = start + self.columns;
self.data[start..end].clone_from_slice(&data);
Ok(())
}
pub fn set_column(&mut self, column: usize, data: Vec<Fraction>) -> Result<(), MatrixError> {
if column >= self.columns {
return Err(MatrixError::ColumnOutOfRange);
}
if data.len() != self.rows {
return Err(MatrixError::InvalidDataSize);
}
2026-04-29 16:07:30 -06:00
for (i, val) in data.into_iter().enumerate() {
self.data[i * self.columns + column] = val;
}
Ok(())
}
pub fn exchange_rows(&mut self, row1: usize, row2: usize) -> Result<(), MatrixError> {
if row1 >= self.rows || row2 >= self.rows {
return Err(MatrixError::RowOutOfRange);
}
let start1 = row1 * self.columns;
let start2 = row2 * self.columns;
for i in 0..self.columns {
self.data.swap(start1 + i, start2 + i);
}
Ok(())
}
pub fn exchange_columns(&mut self, column1: usize, column2: usize) -> Result<(), MatrixError> {
if column1 >= self.columns || column2 >= self.columns {
return Err(MatrixError::ColumnOutOfRange);
}
for i in 0..self.rows {
let idx1 = column1 + i * self.columns;
let idx2 = column2 + i * self.columns;
self.data.swap(idx1, idx2);
}
Ok(())
}
pub fn insert_column(&mut self, index: usize, data: Vec<Fraction>) -> Result<(), MatrixError> {
2026-04-29 17:12:45 -06:00
if index > self.columns {
return Err(MatrixError::ColumnOutOfRange);
2026-04-28 07:23:28 -06:00
}
if data.len() != self.rows {
return Err(MatrixError::InvalidDataSize);
2026-04-28 07:23:28 -06:00
}
for i in 0..self.rows {
self.data.insert((i * self.columns) + index + i, data[i]);
2026-04-28 07:23:28 -06:00
}
self.columns += 1;
2026-04-28 07:23:28 -06:00
Ok(())
2026-04-28 07:23:28 -06:00
}
pub fn insert_rows(&mut self, index: usize, data: Vec<Fraction>) -> Result<(), MatrixError> {
2026-04-29 17:12:45 -06:00
if index > self.rows {
return Err(MatrixError::RowOutOfRange);
}
if data.len() != self.columns {
return Err(MatrixError::InvalidDataSize);
}
2026-04-29 16:29:32 -06:00
let pos = index * self.columns;
self.data.splice(pos..pos, data);
self.rows += 1;
Ok(())
}
pub fn sub_matrix(
&self,
start: (usize, usize),
end: (usize, usize),
) -> Result<Matrix, MatrixError> {
if start.0 >= self.rows || start.1 >= self.columns {
return Err(MatrixError::IndexOutOfRange);
}
2026-04-29 16:29:32 -06:00
if end.0 > self.rows || end.1 > self.columns {
return Err(MatrixError::IndexOutOfRange);
}
if end.0 <= start.0 || end.1 <= start.1 {
return Err(MatrixError::IndexOutOfRange);
}
let mut new_data: Vec<Fraction> = Vec::new();
for i in start.0..end.0 {
for k in start.1..end.1 {
new_data.push(*self.get(i, k)?);
}
}
let new_matrix = Matrix {
2026-04-29 16:29:32 -06:00
rows: end.0 - start.0,
columns: end.1 - start.1,
data: new_data,
};
Ok(new_matrix)
}
2026-04-27 23:41:18 -06:00
fn partial_pivoting(&mut self, col: usize, sign: &mut Fraction) -> Result<bool, MatrixError> {
if col >= self.columns {
return Err(MatrixError::ColumnOutOfRange);
}
let mut max_row = col;
let mut max_value = self.get(col, col)?.abs();
2026-04-27 23:41:18 -06:00
for r in (col + 1)..self.rows {
let val = self.get(r, col)?.abs();
2026-04-27 23:41:18 -06:00
if val > max_value {
max_value = val;
max_row = r;
}
}
if max_value.is_zero() {
return Ok(false);
}
if max_row != col {
self.exchange_rows(col, max_row)?;
2026-04-27 23:41:18 -06:00
*sign = -*sign;
}
Ok(true)
}
pub fn gaussian_elimination(&self) -> Result<(Matrix, Fraction), MatrixError> {
let mut trig_matrix = Matrix {
columns: self.columns,
rows: self.rows,
data: self.data.clone(),
2025-10-29 15:13:06 -06:00
};
let mut sign = Fraction::new(1, 1)?;
2025-10-29 15:13:06 -06:00
for i in 0..min(self.rows, self.columns) {
2026-04-27 23:41:18 -06:00
// We do partial pivoting for better efifiency and security
let pivot_exists = trig_matrix.partial_pivoting(i, &mut sign)?;
2026-04-27 21:00:51 -06:00
// If there ain't no other thing but 0 then we're
// fucked, determinant is zero
2026-04-27 23:41:18 -06:00
if !pivot_exists {
let mut all_zero = true;
for r in i..trig_matrix.rows {
if !trig_matrix.get(r, i)?.is_zero() {
all_zero = false;
break;
}
}
if all_zero {
return Err(MatrixError::FailedGauss); // matriz singular
}
continue;
2026-04-27 21:00:51 -06:00
}
let pivot = *trig_matrix.get(i, i)?;
if pivot.is_zero() {
return Err(MatrixError::FailedGauss);
}
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 {
let m = (*trig_matrix.get(x, i)? / pivot)?;
let row_x = trig_matrix.get_row(x)?;
let row_i = trig_matrix.get_row(i)?;
2026-04-27 21:00:51 -06:00
let new_row = row_x
.zip(row_i)
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>>();
trig_matrix.set_row(x, new_row)?;
}
}
Ok((trig_matrix, sign))
}
2026-04-27 23:41:18 -06:00
pub fn gauss_jordan_elimination(&self) -> Result<Matrix, MatrixError> {
let mut new_matrix = Matrix {
columns: self.columns,
rows: self.rows,
data: self.data.clone(),
};
let mut dummy = Fraction::from(1);
for i in 0..min(self.columns, self.rows) {
2026-04-27 23:41:18 -06:00
let pivot_exists = new_matrix.partial_pivoting(i, &mut dummy)?;
if !pivot_exists {
return Err(MatrixError::FailedGaussJordan);
}
let pivot = *new_matrix.get(i, i)?;
2026-04-27 23:41:18 -06:00
let new_pivot_row = new_matrix
.get_row(i)?
.map(|x| *x / pivot)
.collect::<Result<Vec<_>, _>>()?;
new_matrix.set_row(i, new_pivot_row)?;
2026-04-27 23:41:18 -06:00
for r in 0..new_matrix.rows {
2026-04-27 23:46:01 -06:00
if r == i {
2026-04-27 23:41:18 -06:00
continue;
}
let factor = *new_matrix.get(r, i)?;
2026-04-27 23:41:18 -06:00
if factor.is_zero() {
continue;
}
let new_row_normalized = new_matrix
.get_row(r)?
.zip(new_matrix.get_row(i)?)
.map(|(a, b)| *a - factor * *b)
.collect::<Vec<Fraction>>();
new_matrix.set_row(r, new_row_normalized)?;
2026-04-27 23:41:18 -06:00
}
}
Ok(new_matrix)
}
pub fn get_determinant(&self) -> Result<Fraction, MatrixError> {
if self.rows != self.columns {
return Err(MatrixError::NotSquared);
}
2026-04-27 23:41:18 -06:00
let (trig_matrix, sign) = match self.gaussian_elimination() {
2026-04-29 17:12:45 -06:00
Err(MatrixError::FailedGauss) => return Ok(Fraction::from(0)),
2026-04-27 23:41:18 -06:00
Ok((matrix, sign)) => (matrix, sign),
Err(err) => return Err(err),
};
// 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
// the determinant :)
2026-04-27 21:00:51 -06:00
let determinant = sign
* trig_matrix
.get_diagonal()?
2026-04-27 21:00:51 -06:00
.copied()
.fold(Fraction::from(1i64), |acc, x| acc * x);
return Ok(determinant);
}
pub fn inverse(&self) -> Result<Matrix, MatrixError> {
if self.columns != self.rows {
return Err(MatrixError::NotSquared);
}
2026-04-29 17:12:45 -06:00
let mut augmented = Matrix {
rows: self.rows,
columns: self.columns,
data: self.data.clone(),
};
2026-04-29 17:12:45 -06:00
// construir [A | I]
for i in 0..self.rows {
let mut col = vec![Fraction::from(0); self.rows];
col[i] = Fraction::from(1);
2026-04-29 17:12:45 -06:00
augmented.insert_column(augmented.columns, col)?;
}
2026-04-29 17:12:45 -06:00
// ahora debe ser 2N
assert_eq!(augmented.columns, self.columns * 2);
2026-04-29 17:12:45 -06:00
let reduced = augmented.gauss_jordan_elimination()?;
// extraer lado derecho
let inverse = reduced.sub_matrix((0, self.columns), (self.rows, self.columns * 2))?;
Ok(inverse)
}
pub fn solve_matrix_with_gauss_jordan(
&self,
solution_matrix: Vec<Fraction>,
) -> Result<Vec<Fraction>, MatrixError> {
if self.columns != self.rows {
return Err(MatrixError::NotSquared);
}
if solution_matrix.len() != self.columns {
return Err(MatrixError::InvalidDataSize);
}
let mut solved_matrix = Matrix {
rows: self.rows,
columns: self.columns,
data: self.data.clone(),
};
solved_matrix.insert_column(self.columns, solution_matrix)?;
let solved_matrix = solved_matrix.gauss_jordan_elimination()?;
let result = solved_matrix
2026-04-29 17:18:35 -06:00
.get_column(solved_matrix.columns - 1)?
.cloned()
.collect();
Ok(result)
}
2026-04-29 15:28:49 -06:00
pub fn solve_matrix_with_inverse(
&self,
2026-04-29 17:23:31 -06:00
solution_matrix: Vec<Fraction>,
2026-04-29 15:28:49 -06:00
) -> Result<Matrix, MatrixError> {
if self.columns != self.rows {
return Err(MatrixError::NotSquared);
}
2026-04-29 17:23:31 -06:00
if solution_matrix.len() != self.rows {
2026-04-29 15:28:49 -06:00
return Err(MatrixError::InvalidDataSize);
}
2026-04-29 17:23:31 -06:00
let sol = Matrix {
rows: self.rows,
columns: 1,
data: solution_matrix,
};
let solution_matrix = (self.inverse()? * sol)?;
2026-04-29 15:28:49 -06:00
Ok(solution_matrix)
}
2025-10-07 11:53:10 -06:00
}
2026-04-27 21:00:51 -06:00
impl Add for Matrix {
type Output = Result<Self, MatrixError>;
2025-10-07 11:53:10 -06:00
fn add(self, other: Self) -> Self::Output {
2026-04-29 17:28:51 -06:00
if self.rows != other.rows || self.columns != other.columns {
return Err(MatrixError::InvalidSizeForAdd);
2025-10-07 11:53:10 -06:00
}
2026-04-29 17:31:43 -06:00
let mut new_data = Vec::with_capacity(self.data.len());
2025-10-07 11:53:10 -06:00
for i in 0..self.data.len() {
new_data.push(self.data[i] + other.data[i]);
}
Ok(Matrix {
2025-10-07 11:53:10 -06:00
columns: self.columns,
rows: self.rows,
data: new_data,
})
2025-10-07 11:53:10 -06:00
}
}
2026-04-27 21:00:51 -06:00
impl Sub for Matrix {
type Output = Result<Self, MatrixError>;
2025-10-07 11:53:10 -06:00
fn sub(self, other: Self) -> Self::Output {
2026-04-29 17:28:51 -06:00
if self.rows != other.rows || self.columns != other.columns {
return Err(MatrixError::InvalidSizeForSub);
2025-10-07 11:53:10 -06:00
}
2026-04-29 17:31:43 -06:00
let mut new_data = Vec::with_capacity(self.data.len());
2025-10-07 11:53:10 -06:00
for i in 0..self.data.len() {
new_data.push(self.data[i] - other.data[i]);
}
Ok(Matrix {
2025-10-07 11:53:10 -06:00
columns: self.columns,
rows: self.rows,
data: new_data,
})
2025-10-07 11:53:10 -06:00
}
}
2026-04-27 21:00:51 -06:00
impl Mul for Matrix {
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 {
return Err(MatrixError::InvalidSizeForMul);
2025-10-07 11:53:10 -06:00
}
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 {
for k in 0..other.columns {
let current_column = other.get_column(k)?;
let current_row = self.get_row(i)?;
2026-04-29 17:34:26 -06:00
let mut new_value = Fraction::from(0);
for (a, b) in current_row.zip(current_column) {
2026-04-27 21:00:51 -06:00
new_value = new_value + (*a * *b);
2025-10-07 11:53:10 -06:00
}
2025-10-07 11:53:10 -06:00
new_data.push(new_value);
}
}
Ok(Matrix {
2025-10-07 11:53:10 -06:00
rows: self.rows,
columns: other.columns,
data: new_data,
})
2025-10-29 15:13:06 -06:00
}
2025-10-07 11:53:10 -06:00
}
impl fmt::Display for Matrix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let strings: Vec<String> = self.data.iter().map(|x| x.to_string()).collect();
let max_width = strings.iter().map(|s| s.len()).max().unwrap_or(0);
for r in 0..self.rows {
write!(f, "{{ ")?;
for c in 0..self.columns {
let idx = r * self.columns + c;
let val = &strings[idx];
write!(f, "{:>width$}", val, width = max_width)?;
if c != self.columns - 1 {
write!(f, ", ")?;
}
2025-10-07 11:53:10 -06:00
}
writeln!(f, " }}")?;
2025-10-07 11:53:10 -06:00
}
Ok(())
2025-10-07 11:53:10 -06:00
}
}
#[cfg(test)]
mod tests {
use super::*;
2026-04-29 15:38:47 -06:00
#[test]
fn test_new_valid_matrix() {
let m = Matrix::new(2, 3, Fraction::from(1i64)).unwrap();
assert_eq!(m.rows, 2);
assert_eq!(m.columns, 3);
assert_eq!(m.data.len(), 6);
for val in m.data {
assert_eq!(val, Fraction::from(1i64));
}
}
#[test]
fn test_new_single_element() {
let m = Matrix::new(1, 1, Fraction::from(5i64)).unwrap();
assert_eq!(m.rows, 1);
assert_eq!(m.columns, 1);
assert_eq!(m.data.len(), 1);
assert_eq!(m.data[0], Fraction::from(5i64));
}
#[test]
fn test_new_zero_rows() {
let result = Matrix::new(0, 3, Fraction::from(0i64));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), MatrixError::ZeroSize);
}
#[test]
fn test_new_zero_columns() {
let result = Matrix::new(3, 0, Fraction::from(0i64));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), MatrixError::ZeroSize);
}
#[test]
fn test_new_large_matrix() {
let m = Matrix::new(100, 100, Fraction::from(2i64)).unwrap();
assert_eq!(m.data.len(), 10000);
assert!(m.data.iter().all(|x| *x == Fraction::from(2i64)));
}
#[test]
fn test_new_independent_data() {
let m1 = Matrix::new(2, 2, Fraction::from(1i64)).unwrap();
let mut m2 = Matrix::new(2, 2, Fraction::from(1i64)).unwrap();
// modificar uno no debe afectar al otro
m2.data[0] = Fraction::from(99i64);
assert_ne!(m1.data[0], m2.data[0]);
}
2026-04-29 15:42:33 -06:00
#[test]
fn test_get_valid_positions() {
let m = Matrix {
rows: 2,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
assert_eq!(*m.get(0, 0).unwrap(), Fraction::from(1));
assert_eq!(*m.get(0, 2).unwrap(), Fraction::from(3));
assert_eq!(*m.get(1, 0).unwrap(), Fraction::from(4));
assert_eq!(*m.get(1, 2).unwrap(), Fraction::from(6));
}
#[test]
fn test_get_middle_element() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
Fraction::from(9),
],
};
assert_eq!(*m.get(1, 1).unwrap(), Fraction::from(5));
}
#[test]
fn test_get_out_of_bounds_row() {
let m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.get(2, 0);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), MatrixError::IndexOutOfRange);
}
#[test]
fn test_get_out_of_bounds_column() {
let m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.get(0, 2);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), MatrixError::IndexOutOfRange);
}
#[test]
fn test_get_last_element() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(10),
Fraction::from(20),
Fraction::from(30),
Fraction::from(40),
],
};
assert_eq!(*m.get(1, 1).unwrap(), Fraction::from(40));
}
#[test]
fn test_get_first_element() {
let m = Matrix::new(3, 3, Fraction::from(7)).unwrap();
assert_eq!(*m.get(0, 0).unwrap(), Fraction::from(7));
}
#[test]
fn test_get_consistency_with_manual_index() {
let m = Matrix {
rows: 3,
columns: 4,
data: (0..12).map(|x| Fraction::from(x as i64)).collect(),
};
for r in 0..3 {
for c in 0..4 {
let expected_index = r * 4 + c;
assert_eq!(*m.get(r, c).unwrap(), Fraction::from(expected_index as i64));
}
}
}
2026-04-29 15:50:22 -06:00
#[test]
fn test_get_row_valid() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
Fraction::from(9),
],
};
let row: Vec<Fraction> = m.get_row(1).unwrap().cloned().collect();
assert_eq!(
row,
vec![Fraction::from(4), Fraction::from(5), Fraction::from(6)]
);
}
#[test]
fn test_get_first_row() {
let m = Matrix::new(2, 3, Fraction::from(10)).unwrap();
let row: Vec<Fraction> = m.get_row(0).unwrap().cloned().collect();
assert_eq!(row, vec![Fraction::from(10); 3]);
}
#[test]
fn test_get_last_row() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let row: Vec<Fraction> = m.get_row(1).unwrap().cloned().collect();
assert_eq!(row, vec![Fraction::from(3), Fraction::from(4)]);
}
#[test]
fn test_get_row_out_of_bounds() {
let m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.get_row(2);
assert!(result.is_err());
assert!(matches!(result, Err(MatrixError::RowOutOfRange)));
}
#[test]
fn test_get_row_length() {
let m = Matrix::new(4, 5, Fraction::from(1)).unwrap();
let row: Vec<Fraction> = m.get_row(3).unwrap().cloned().collect();
assert_eq!(row.len(), 5);
}
#[test]
fn test_get_row_iterator_multiple_times() {
let m = Matrix::new(2, 3, Fraction::from(7)).unwrap();
let iter1: Vec<Fraction> = m.get_row(0).unwrap().cloned().collect();
let iter2: Vec<Fraction> = m.get_row(0).unwrap().cloned().collect();
assert_eq!(iter1, iter2);
}
#[test]
fn test_get_row_matches_get() {
let m = Matrix {
rows: 3,
columns: 3,
data: (0..9).map(|x| Fraction::from(x)).collect(),
};
let row = m.get_row(2).unwrap();
for (col, val) in row.enumerate() {
assert_eq!(*val, *m.get(2, col).unwrap());
}
}
2026-04-29 15:51:46 -06:00
#[test]
fn test_get_column_valid() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
Fraction::from(9),
],
};
let col: Vec<Fraction> = m.get_column(1).unwrap().cloned().collect();
assert_eq!(
col,
vec![Fraction::from(2), Fraction::from(5), Fraction::from(8)]
);
}
#[test]
fn test_get_first_column() {
let m = Matrix::new(3, 2, Fraction::from(10)).unwrap();
let col: Vec<Fraction> = m.get_column(0).unwrap().cloned().collect();
assert_eq!(col, vec![Fraction::from(10); 3]);
}
#[test]
fn test_get_last_column() {
let m = Matrix {
rows: 2,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
let col: Vec<Fraction> = m.get_column(2).unwrap().cloned().collect();
assert_eq!(col, vec![Fraction::from(3), Fraction::from(6)]);
}
#[test]
fn test_get_column_out_of_bounds() {
let m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.get_column(2);
assert!(matches!(result, Err(MatrixError::ColumnOutOfRange)));
}
#[test]
fn test_get_column_length() {
let m = Matrix::new(5, 4, Fraction::from(1)).unwrap();
let col: Vec<Fraction> = m.get_column(3).unwrap().cloned().collect();
assert_eq!(col.len(), 5);
}
#[test]
fn test_get_column_matches_get() {
let m = Matrix {
rows: 4,
columns: 3,
data: (0..12).map(|x| Fraction::from(x)).collect(),
};
let col = m.get_column(2).unwrap();
for (row, val) in col.enumerate() {
assert_eq!(*val, *m.get(row, 2).unwrap());
}
}
#[test]
fn test_get_column_different_shapes() {
let m = Matrix {
rows: 3,
columns: 4,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
Fraction::from(9),
Fraction::from(10),
Fraction::from(11),
Fraction::from(12),
],
};
let col: Vec<Fraction> = m.get_column(3).unwrap().cloned().collect();
assert_eq!(
col,
vec![Fraction::from(4), Fraction::from(8), Fraction::from(12)]
);
}
#[test]
fn test_get_column_iterator_repeatability() {
let m = Matrix::new(3, 3, Fraction::from(7)).unwrap();
let col1: Vec<Fraction> = m.get_column(1).unwrap().cloned().collect();
let col2: Vec<Fraction> = m.get_column(1).unwrap().cloned().collect();
assert_eq!(col1, col2);
}
2026-04-29 15:53:10 -06:00
#[test]
fn test_get_diagonal_valid() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
Fraction::from(9),
],
};
let diag: Vec<Fraction> = m.get_diagonal().unwrap().cloned().collect();
assert_eq!(
diag,
vec![Fraction::from(1), Fraction::from(5), Fraction::from(9)]
);
}
#[test]
fn test_get_diagonal_single_element() {
let m = Matrix::new(1, 1, Fraction::from(42)).unwrap();
let diag: Vec<Fraction> = m.get_diagonal().unwrap().cloned().collect();
assert_eq!(diag, vec![Fraction::from(42)]);
}
#[test]
fn test_get_diagonal_identity_like() {
let mut data = vec![Fraction::from(0); 9];
data[0] = Fraction::from(1);
data[4] = Fraction::from(1);
data[8] = Fraction::from(1);
let m = Matrix {
rows: 3,
columns: 3,
data,
};
let diag: Vec<Fraction> = m.get_diagonal().unwrap().cloned().collect();
assert_eq!(
diag,
vec![Fraction::from(1), Fraction::from(1), Fraction::from(1)]
);
}
#[test]
fn test_get_diagonal_not_squared() {
let m = Matrix::new(2, 3, Fraction::from(0)).unwrap();
let result = m.get_diagonal();
assert!(matches!(result, Err(MatrixError::NotSquared)));
}
#[test]
fn test_get_diagonal_matches_get() {
let m = Matrix {
rows: 4,
columns: 4,
data: (0..16).map(|x| Fraction::from(x)).collect(),
};
let diag = m.get_diagonal().unwrap();
for (i, val) in diag.enumerate() {
assert_eq!(*val, *m.get(i, i).unwrap());
}
}
#[test]
fn test_get_diagonal_length() {
let m = Matrix::new(5, 5, Fraction::from(3)).unwrap();
let diag: Vec<Fraction> = m.get_diagonal().unwrap().cloned().collect();
assert_eq!(diag.len(), 5);
}
#[test]
fn test_get_diagonal_iterator_repeatability() {
let m = Matrix::new(3, 3, Fraction::from(7)).unwrap();
let d1: Vec<Fraction> = m.get_diagonal().unwrap().cloned().collect();
let d2: Vec<Fraction> = m.get_diagonal().unwrap().cloned().collect();
assert_eq!(d1, d2);
}
2026-04-29 15:55:38 -06:00
#[test]
fn test_set_valid_position() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
m.set(0, 1, Fraction::from(5)).unwrap();
assert_eq!(*m.get(0, 1).unwrap(), Fraction::from(5));
}
#[test]
fn test_set_first_element() {
let mut m = Matrix::new(3, 3, Fraction::from(0)).unwrap();
m.set(0, 0, Fraction::from(9)).unwrap();
assert_eq!(*m.get(0, 0).unwrap(), Fraction::from(9));
}
#[test]
fn test_set_last_element() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
m.set(1, 1, Fraction::from(7)).unwrap();
assert_eq!(*m.get(1, 1).unwrap(), Fraction::from(7));
}
#[test]
fn test_set_out_of_bounds_row() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.set(2, 0, Fraction::from(1));
assert!(matches!(result, Err(MatrixError::IndexOutOfRange)));
}
#[test]
fn test_set_out_of_bounds_column() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.set(0, 2, Fraction::from(1));
assert!(matches!(result, Err(MatrixError::IndexOutOfRange)));
}
#[test]
fn test_set_does_not_affect_other_elements() {
let mut m = Matrix::new(2, 2, Fraction::from(1)).unwrap();
m.set(0, 1, Fraction::from(99)).unwrap();
// solo cambia ese elemento
assert_eq!(*m.get(0, 0).unwrap(), Fraction::from(1));
assert_eq!(*m.get(0, 1).unwrap(), Fraction::from(99));
assert_eq!(*m.get(1, 0).unwrap(), Fraction::from(1));
assert_eq!(*m.get(1, 1).unwrap(), Fraction::from(1));
}
#[test]
fn test_set_overwrite_value() {
let mut m = Matrix::new(2, 2, Fraction::from(3)).unwrap();
m.set(1, 0, Fraction::from(8)).unwrap();
m.set(1, 0, Fraction::from(10)).unwrap();
assert_eq!(*m.get(1, 0).unwrap(), Fraction::from(10));
}
#[test]
fn test_set_consistency_with_manual_index() {
let mut m = Matrix {
rows: 3,
columns: 3,
data: vec![Fraction::from(0); 9],
};
for r in 0..3 {
for c in 0..3 {
let val = Fraction::from((r * 3 + c) as i64);
m.set(r, c, val.clone()).unwrap();
assert_eq!(*m.get(r, c).unwrap(), val);
}
}
}
2026-04-29 15:58:48 -06:00
#[test]
fn test_set_row_valid() {
let mut m = Matrix::new(3, 3, Fraction::from(0)).unwrap();
let new_row = vec![Fraction::from(1), Fraction::from(2), Fraction::from(3)];
m.set_row(1, new_row.clone()).unwrap();
let row: Vec<Fraction> = m.get_row(1).unwrap().cloned().collect();
assert_eq!(row, new_row);
}
#[test]
fn test_set_first_row() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let new_row = vec![Fraction::from(5), Fraction::from(6)];
m.set_row(0, new_row.clone()).unwrap();
assert_eq!(m.get_row(0).unwrap().cloned().collect::<Vec<_>>(), new_row);
}
#[test]
fn test_set_last_row() {
let mut m = Matrix::new(2, 3, Fraction::from(0)).unwrap();
let new_row = vec![Fraction::from(7), Fraction::from(8), Fraction::from(9)];
m.set_row(1, new_row.clone()).unwrap();
assert_eq!(m.get_row(1).unwrap().cloned().collect::<Vec<_>>(), new_row);
}
#[test]
fn test_set_row_out_of_bounds() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let data = vec![Fraction::from(1), Fraction::from(2)];
let result = m.set_row(2, data);
assert!(matches!(result, Err(MatrixError::IndexOutOfRange)));
}
#[test]
fn test_set_row_invalid_size() {
let mut m = Matrix::new(2, 3, Fraction::from(0)).unwrap();
let data = vec![Fraction::from(1), Fraction::from(2)]; // tamaño incorrecto
let result = m.set_row(0, data);
assert!(matches!(result, Err(MatrixError::InvalidDataSize)));
}
#[test]
fn test_set_row_does_not_affect_other_rows() {
let mut m = Matrix::new(3, 2, Fraction::from(1)).unwrap();
let new_row = vec![Fraction::from(9), Fraction::from(9)];
m.set_row(1, new_row).unwrap();
// fila 0 intacta
assert_eq!(
m.get_row(0).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(1), Fraction::from(1)]
);
// fila 2 intacta
assert_eq!(
m.get_row(2).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(1), Fraction::from(1)]
);
}
#[test]
fn test_set_row_overwrite() {
let mut m = Matrix::new(2, 2, Fraction::from(3)).unwrap();
let row1 = vec![Fraction::from(4), Fraction::from(5)];
let row2 = vec![Fraction::from(8), Fraction::from(9)];
m.set_row(1, row1).unwrap();
m.set_row(1, row2.clone()).unwrap();
assert_eq!(m.get_row(1).unwrap().cloned().collect::<Vec<_>>(), row2);
}
#[test]
fn test_set_row_consistency_with_set() {
let mut m = Matrix::new(3, 3, Fraction::from(0)).unwrap();
let new_row = vec![Fraction::from(10), Fraction::from(20), Fraction::from(30)];
m.set_row(2, new_row.clone()).unwrap();
for (i, val) in new_row.iter().enumerate() {
assert_eq!(*m.get(2, i).unwrap(), *val);
}
}
2026-04-29 16:07:30 -06:00
#[test]
fn test_set_column_valid() {
let mut m = Matrix::new(3, 3, Fraction::from(0)).unwrap();
let new_col = vec![Fraction::from(1), Fraction::from(2), Fraction::from(3)];
m.set_column(1, new_col.clone()).unwrap();
let col: Vec<Fraction> = m.get_column(1).unwrap().cloned().collect();
assert_eq!(col, new_col);
}
#[test]
fn test_set_first_column() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let new_col = vec![Fraction::from(5), Fraction::from(6)];
m.set_column(0, new_col.clone()).unwrap();
assert_eq!(
m.get_column(0).unwrap().cloned().collect::<Vec<_>>(),
new_col
);
}
#[test]
fn test_set_last_column() {
let mut m = Matrix::new(2, 3, Fraction::from(0)).unwrap();
let new_col = vec![Fraction::from(7), Fraction::from(8)];
m.set_column(2, new_col.clone()).unwrap();
assert_eq!(
m.get_column(2).unwrap().cloned().collect::<Vec<_>>(),
new_col
);
}
#[test]
fn test_set_column_out_of_bounds() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let data = vec![Fraction::from(1), Fraction::from(2)];
let result = m.set_column(2, data);
assert!(matches!(result, Err(MatrixError::ColumnOutOfRange)));
}
#[test]
fn test_set_column_invalid_size() {
let mut m = Matrix::new(3, 2, Fraction::from(0)).unwrap();
let data = vec![Fraction::from(1), Fraction::from(2)]; // tamaño incorrecto
let result = m.set_column(1, data);
assert!(matches!(result, Err(MatrixError::InvalidDataSize)));
}
#[test]
fn test_set_column_does_not_affect_other_columns() {
let mut m = Matrix::new(3, 3, Fraction::from(1)).unwrap();
let new_col = vec![Fraction::from(9), Fraction::from(9), Fraction::from(9)];
m.set_column(1, new_col).unwrap();
// columna 0 intacta
assert_eq!(
m.get_column(0).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(1); 3]
);
// columna 2 intacta
assert_eq!(
m.get_column(2).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(1); 3]
);
}
#[test]
fn test_set_column_overwrite() {
let mut m = Matrix::new(2, 2, Fraction::from(3)).unwrap();
let col1 = vec![Fraction::from(4), Fraction::from(5)];
let col2 = vec![Fraction::from(8), Fraction::from(9)];
m.set_column(1, col1).unwrap();
m.set_column(1, col2.clone()).unwrap();
assert_eq!(m.get_column(1).unwrap().cloned().collect::<Vec<_>>(), col2);
}
#[test]
fn test_set_column_consistency_with_set() {
let mut m = Matrix::new(3, 3, Fraction::from(0)).unwrap();
let new_col = vec![Fraction::from(10), Fraction::from(20), Fraction::from(30)];
m.set_column(2, new_col.clone()).unwrap();
for (i, val) in new_col.iter().enumerate() {
assert_eq!(*m.get(i, 2).unwrap(), *val);
}
}
#[test]
fn test_set_column_mixed_values() {
let mut m = Matrix::new(3, 3, Fraction::from(0)).unwrap();
let new_col = vec![Fraction::from(1), Fraction::from(100), Fraction::from(-5)];
m.set_column(0, new_col.clone()).unwrap();
assert_eq!(
m.get_column(0).unwrap().cloned().collect::<Vec<_>>(),
new_col
);
}
2026-04-29 16:09:30 -06:00
#[test]
fn test_exchange_rows_basic() {
let mut m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
m.exchange_rows(0, 1).unwrap();
assert_eq!(
m.data,
vec![
Fraction::from(3),
Fraction::from(4),
Fraction::from(1),
Fraction::from(2),
]
);
}
#[test]
fn test_exchange_rows_same_row() {
let mut m = Matrix::new(3, 3, Fraction::from(5)).unwrap();
let before = m.data.clone();
m.exchange_rows(1, 1).unwrap();
assert_eq!(m.data, before);
}
#[test]
fn test_exchange_rows_first_last() {
let mut m = Matrix {
rows: 3,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2), // row 0
Fraction::from(3),
Fraction::from(4), // row 1
Fraction::from(5),
Fraction::from(6), // row 2
],
};
m.exchange_rows(0, 2).unwrap();
assert_eq!(
m.get_row(0).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(5), Fraction::from(6)]
);
assert_eq!(
m.get_row(2).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(1), Fraction::from(2)]
);
}
#[test]
fn test_exchange_rows_out_of_bounds_row1() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.exchange_rows(2, 0);
assert!(matches!(result, Err(MatrixError::RowOutOfRange)));
}
#[test]
fn test_exchange_rows_out_of_bounds_row2() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.exchange_rows(0, 2);
assert!(matches!(result, Err(MatrixError::RowOutOfRange)));
}
#[test]
fn test_exchange_rows_does_not_affect_other_rows() {
let mut m = Matrix {
rows: 3,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(1),
Fraction::from(2),
Fraction::from(2),
Fraction::from(3),
Fraction::from(3),
],
};
m.exchange_rows(0, 1).unwrap();
// fila 2 intacta
assert_eq!(
m.get_row(2).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(3), Fraction::from(3)]
);
}
#[test]
fn test_exchange_rows_twice_returns_original() {
let mut m = Matrix {
rows: 2,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
let original = m.data.clone();
m.exchange_rows(0, 1).unwrap();
m.exchange_rows(0, 1).unwrap();
assert_eq!(m.data, original);
}
#[test]
fn test_exchange_rows_consistency_with_get() {
let mut m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(10),
Fraction::from(20),
Fraction::from(30),
Fraction::from(40),
],
};
let row0_before: Vec<_> = m.get_row(0).unwrap().cloned().collect();
let row1_before: Vec<_> = m.get_row(1).unwrap().cloned().collect();
m.exchange_rows(0, 1).unwrap();
let row0_after: Vec<_> = m.get_row(0).unwrap().cloned().collect();
let row1_after: Vec<_> = m.get_row(1).unwrap().cloned().collect();
assert_eq!(row0_after, row1_before);
assert_eq!(row1_after, row0_before);
}
#[test]
fn test_exchange_rows_single_column_matrix() {
let mut m = Matrix {
rows: 3,
columns: 1,
data: vec![Fraction::from(1), Fraction::from(2), Fraction::from(3)],
};
m.exchange_rows(0, 2).unwrap();
assert_eq!(
m.data,
vec![Fraction::from(3), Fraction::from(2), Fraction::from(1),]
);
}
2026-04-29 16:13:00 -06:00
#[test]
fn test_exchange_columns_basic() {
let mut m = Matrix {
rows: 2,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
m.exchange_columns(0, 2).unwrap();
assert_eq!(
m.data,
vec![
Fraction::from(3),
Fraction::from(2),
Fraction::from(1),
Fraction::from(6),
Fraction::from(5),
Fraction::from(4),
]
);
}
#[test]
fn test_exchange_columns_same_column() {
let mut m = Matrix::new(3, 3, Fraction::from(7)).unwrap();
let before = m.data.clone();
m.exchange_columns(1, 1).unwrap();
assert_eq!(m.data, before);
}
#[test]
fn test_exchange_columns_first_last() {
let mut m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
Fraction::from(9),
],
};
m.exchange_columns(0, 2).unwrap();
assert_eq!(
m.get_column(0).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(3), Fraction::from(6), Fraction::from(9)]
);
assert_eq!(
m.get_column(2).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(1), Fraction::from(4), Fraction::from(7)]
);
}
#[test]
fn test_exchange_columns_out_of_bounds_col1() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.exchange_columns(2, 0);
assert!(matches!(result, Err(MatrixError::ColumnOutOfRange)));
}
#[test]
fn test_exchange_columns_out_of_bounds_col2() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.exchange_columns(0, 2);
assert!(matches!(result, Err(MatrixError::ColumnOutOfRange)));
}
#[test]
fn test_exchange_columns_does_not_affect_other_columns() {
let mut m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(1),
Fraction::from(1),
Fraction::from(2),
Fraction::from(2),
Fraction::from(2),
Fraction::from(3),
Fraction::from(3),
Fraction::from(3),
],
};
m.exchange_columns(0, 1).unwrap();
// columna 2 intacta
assert_eq!(
m.get_column(2).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(1), Fraction::from(2), Fraction::from(3)]
);
}
#[test]
fn test_exchange_columns_twice_returns_original() {
let mut m = Matrix {
rows: 2,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
let original = m.data.clone();
m.exchange_columns(0, 2).unwrap();
m.exchange_columns(0, 2).unwrap();
assert_eq!(m.data, original);
}
#[test]
fn test_exchange_columns_consistency_with_get() {
let mut m = Matrix {
rows: 3,
columns: 2,
data: vec![
Fraction::from(10),
Fraction::from(20),
Fraction::from(30),
Fraction::from(40),
Fraction::from(50),
Fraction::from(60),
],
};
let col0_before: Vec<_> = m.get_column(0).unwrap().cloned().collect();
let col1_before: Vec<_> = m.get_column(1).unwrap().cloned().collect();
m.exchange_columns(0, 1).unwrap();
let col0_after: Vec<_> = m.get_column(0).unwrap().cloned().collect();
let col1_after: Vec<_> = m.get_column(1).unwrap().cloned().collect();
assert_eq!(col0_after, col1_before);
assert_eq!(col1_after, col0_before);
}
#[test]
fn test_exchange_columns_single_row_matrix() {
let mut m = Matrix {
rows: 1,
columns: 3,
data: vec![Fraction::from(1), Fraction::from(2), Fraction::from(3)],
};
m.exchange_columns(0, 2).unwrap();
assert_eq!(
m.data,
vec![Fraction::from(3), Fraction::from(2), Fraction::from(1),]
);
}
#[test]
fn test_insert_column_basic_middle() {
let mut m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let new_col = vec![Fraction::from(9), Fraction::from(9)];
m.insert_column(1, new_col.clone()).unwrap();
assert_eq!(m.columns, 3);
assert_eq!(
m.data,
vec![
Fraction::from(1),
Fraction::from(9),
Fraction::from(2),
Fraction::from(3),
Fraction::from(9),
Fraction::from(4),
]
);
}
#[test]
fn test_insert_column_at_start() {
let mut m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let new_col = vec![Fraction::from(7), Fraction::from(8)];
m.insert_column(0, new_col.clone()).unwrap();
assert_eq!(
m.data,
vec![
Fraction::from(7),
Fraction::from(1),
Fraction::from(2),
Fraction::from(8),
Fraction::from(3),
Fraction::from(4),
]
);
}
#[test]
fn test_insert_column_at_end_minus_one() {
let mut m = Matrix {
rows: 2,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
let new_col = vec![Fraction::from(9), Fraction::from(9)];
m.insert_column(2, new_col.clone()).unwrap();
assert_eq!(
m.get_column(2).unwrap().cloned().collect::<Vec<_>>(),
new_col
);
}
#[test]
fn test_insert_column_invalid_index() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let data = vec![Fraction::from(1), Fraction::from(2)];
2026-04-29 17:12:45 -06:00
let result = m.insert_column(3, data);
2026-04-29 16:13:00 -06:00
assert!(matches!(result, Err(MatrixError::ColumnOutOfRange)));
}
#[test]
fn test_insert_column_invalid_size() {
let mut m = Matrix::new(3, 2, Fraction::from(0)).unwrap();
let data = vec![Fraction::from(1), Fraction::from(2)];
let result = m.insert_column(1, data);
assert!(matches!(result, Err(MatrixError::InvalidDataSize)));
}
#[test]
fn test_insert_column_preserves_other_data() {
let mut m = Matrix {
rows: 3,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
let new_col = vec![Fraction::from(9), Fraction::from(9), Fraction::from(9)];
m.insert_column(1, new_col).unwrap();
assert_eq!(
m.get_column(0).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(1), Fraction::from(3), Fraction::from(5)]
);
assert_eq!(
m.get_column(2).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(2), Fraction::from(4), Fraction::from(6)]
);
}
#[test]
fn test_insert_column_multiple_times() {
let mut m = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let col1 = vec![Fraction::from(2), Fraction::from(2)];
let col2 = vec![Fraction::from(3), Fraction::from(3)];
m.insert_column(1, col1).unwrap();
m.insert_column(2, col2.clone()).unwrap();
assert_eq!(m.get_column(2).unwrap().cloned().collect::<Vec<_>>(), col2);
}
#[test]
fn test_insert_column_consistency_with_get() {
let mut m = Matrix::new(3, 2, Fraction::from(0)).unwrap();
let new_col = vec![Fraction::from(10), Fraction::from(20), Fraction::from(30)];
m.insert_column(1, new_col.clone()).unwrap();
for (i, val) in new_col.iter().enumerate() {
assert_eq!(*m.get(i, 1).unwrap(), *val);
}
}
#[test]
fn test_insert_column_single_row() {
let mut m = Matrix {
rows: 1,
columns: 2,
data: vec![Fraction::from(1), Fraction::from(2)],
};
let new_col = vec![Fraction::from(9)];
m.insert_column(1, new_col).unwrap();
assert_eq!(
m.data,
vec![Fraction::from(1), Fraction::from(9), Fraction::from(2),]
);
}
2026-04-29 16:29:32 -06:00
#[test]
fn test_insert_row_basic_middle() {
let mut m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let new_row = vec![Fraction::from(9), Fraction::from(9)];
m.insert_rows(1, new_row.clone()).unwrap();
assert_eq!(m.rows, 3);
assert_eq!(m.get_row(1).unwrap().cloned().collect::<Vec<_>>(), new_row);
}
#[test]
fn test_insert_row_at_start() {
let mut m = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let new_row = vec![Fraction::from(5), Fraction::from(6)];
m.insert_rows(0, new_row.clone()).unwrap();
assert_eq!(m.get_row(0).unwrap().cloned().collect::<Vec<_>>(), new_row);
}
#[test]
fn test_insert_row_invalid_index() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let data = vec![Fraction::from(1), Fraction::from(2)];
2026-04-29 17:12:45 -06:00
let result = m.insert_rows(3, data);
2026-04-29 16:29:32 -06:00
assert!(matches!(result, Err(MatrixError::RowOutOfRange)));
}
#[test]
fn test_insert_row_invalid_size() {
let mut m = Matrix::new(2, 3, Fraction::from(0)).unwrap();
let data = vec![Fraction::from(1), Fraction::from(2)];
let result = m.insert_rows(1, data);
assert!(matches!(result, Err(MatrixError::InvalidDataSize)));
}
#[test]
fn test_insert_row_preserves_other_rows() {
let mut m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let new_row = vec![Fraction::from(9), Fraction::from(9)];
m.insert_rows(1, new_row).unwrap();
assert_eq!(
m.get_row(0).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(1), Fraction::from(2)]
);
assert_eq!(
m.get_row(2).unwrap().cloned().collect::<Vec<_>>(),
vec![Fraction::from(3), Fraction::from(4)]
);
}
#[test]
fn test_insert_row_multiple_times() {
let mut m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let r1 = vec![Fraction::from(1), Fraction::from(1)];
let r2 = vec![Fraction::from(2), Fraction::from(2)];
m.insert_rows(1, r1).unwrap();
m.insert_rows(2, r2.clone()).unwrap();
assert_eq!(m.get_row(2).unwrap().cloned().collect::<Vec<_>>(), r2);
}
#[test]
fn test_insert_row_consistency_with_get() {
let mut m = Matrix::new(2, 3, Fraction::from(0)).unwrap();
let new_row = vec![Fraction::from(10), Fraction::from(20), Fraction::from(30)];
m.insert_rows(1, new_row.clone()).unwrap();
for (i, val) in new_row.iter().enumerate() {
assert_eq!(*m.get(1, i).unwrap(), *val);
}
}
#[test]
fn test_insert_row_single_column() {
let mut m = Matrix {
rows: 2,
columns: 1,
data: vec![Fraction::from(1), Fraction::from(2)],
};
let new_row = vec![Fraction::from(9)];
m.insert_rows(1, new_row).unwrap();
assert_eq!(
m.data,
vec![Fraction::from(1), Fraction::from(9), Fraction::from(2),]
);
}
#[test]
fn test_sub_matrix_basic() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
Fraction::from(9),
],
};
let sub = m.sub_matrix((0, 0), (2, 2)).unwrap();
// Esperado (end exclusivo):
assert_eq!(sub.rows, 2);
assert_eq!(sub.columns, 2);
assert_eq!(
sub.data,
vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(4),
Fraction::from(5),
]
);
}
#[test]
fn test_sub_matrix_middle() {
let m = Matrix {
rows: 4,
columns: 4,
data: (1..=16).map(Fraction::from).collect(),
};
let sub = m.sub_matrix((1, 1), (3, 3)).unwrap();
assert_eq!(sub.rows, 2);
assert_eq!(sub.columns, 2);
assert_eq!(
sub.data,
vec![
Fraction::from(6),
Fraction::from(7),
Fraction::from(10),
Fraction::from(11),
]
);
}
#[test]
fn test_sub_matrix_single_element() {
let m = Matrix::new(3, 3, Fraction::from(5)).unwrap();
let sub = m.sub_matrix((1, 1), (2, 2)).unwrap();
assert_eq!(sub.rows, 1);
assert_eq!(sub.columns, 1);
assert_eq!(sub.data, vec![Fraction::from(5)]);
}
#[test]
fn test_sub_matrix_invalid_start() {
let m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.sub_matrix((2, 0), (3, 1));
assert!(matches!(result, Err(MatrixError::IndexOutOfRange)));
}
#[test]
fn test_sub_matrix_invalid_end() {
let m = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = m.sub_matrix((0, 0), (3, 3));
assert!(matches!(result, Err(MatrixError::IndexOutOfRange)));
}
#[test]
fn test_sub_matrix_invalid_range() {
let m = Matrix::new(3, 3, Fraction::from(0)).unwrap();
let result = m.sub_matrix((2, 2), (1, 1));
assert!(matches!(result, Err(MatrixError::IndexOutOfRange)));
}
#[test]
fn test_sub_matrix_full_copy() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let sub = m.sub_matrix((0, 0), (2, 2)).unwrap();
assert_eq!(sub.data, m.data);
}
#[test]
fn test_sub_matrix_consistency_with_get() {
let m = Matrix {
rows: 3,
columns: 3,
data: (0..9).map(Fraction::from).collect(),
};
let sub = m.sub_matrix((0, 0), (3, 3)).unwrap();
for r in 0..sub.rows {
for c in 0..sub.columns {
assert_eq!(*sub.get(r, c).unwrap(), *m.get(r, c).unwrap());
}
}
}
#[test]
fn test_partial_pivoting_no_swap_needed() {
let mut m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(5),
Fraction::from(0),
Fraction::from(0),
Fraction::from(2),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
],
};
let mut sign = Fraction::from(1);
let res = m.partial_pivoting(0, &mut sign).unwrap();
assert!(res);
assert_eq!(sign, Fraction::from(1)); // no swap
}
#[test]
fn test_partial_pivoting_with_swap() {
let mut m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(9),
Fraction::from(0),
Fraction::from(0),
Fraction::from(3),
Fraction::from(0),
Fraction::from(0),
],
};
let mut sign = Fraction::from(1);
let res = m.partial_pivoting(0, &mut sign).unwrap();
assert!(res);
// ahora la fila 0 debe ser la de valor 9
assert_eq!(m.get(0, 0).unwrap(), &Fraction::from(9));
assert_eq!(sign, Fraction::from(-1)); // hubo swap
}
#[test]
fn test_partial_pivoting_all_zero_column() {
let mut m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(0),
Fraction::from(1),
Fraction::from(2),
Fraction::from(0),
Fraction::from(3),
Fraction::from(4),
Fraction::from(0),
Fraction::from(5),
Fraction::from(6),
],
};
let mut sign = Fraction::from(1);
let res = m.partial_pivoting(0, &mut sign).unwrap();
assert!(!res); // no pivot posible
}
#[test]
fn test_partial_pivoting_largest_abs_value() {
let mut m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(-3),
Fraction::from(0),
Fraction::from(0),
Fraction::from(7),
Fraction::from(0),
Fraction::from(0),
Fraction::from(-10),
Fraction::from(0),
Fraction::from(0),
],
};
let mut sign = Fraction::from(1);
m.partial_pivoting(0, &mut sign).unwrap();
assert_eq!(
m.get(0, 0).unwrap(),
&Fraction::from(-10) // mayor en valor absoluto
);
}
#[test]
fn test_gaussian_already_triangular() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(0),
Fraction::from(4),
Fraction::from(5),
Fraction::from(0),
Fraction::from(0),
Fraction::from(6),
],
};
let (res, _) = m.gaussian_elimination().unwrap();
assert_eq!(res.data, m.data);
}
#[test]
fn test_gaussian_simple() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(2),
Fraction::from(1),
Fraction::from(4),
Fraction::from(3),
],
};
let (res, _) = m.gaussian_elimination().unwrap();
// abajo izquierda debe ser 0
assert!(res.get(1, 0).unwrap().is_zero());
}
#[test]
fn test_gaussian_requires_pivoting() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(0),
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
],
};
let (res, _) = m.gaussian_elimination().unwrap();
assert!(res.get(0, 0).unwrap() != &Fraction::from(0));
}
#[test]
fn test_gaussian_singular_matrix() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(2),
Fraction::from(4),
],
};
let res = m.gaussian_elimination();
assert!(matches!(res, Err(MatrixError::FailedGauss)));
}
#[test]
fn test_gaussian_upper_triangular_property() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(2),
Fraction::from(1),
Fraction::from(3),
Fraction::from(4),
Fraction::from(1),
Fraction::from(6),
Fraction::from(6),
Fraction::from(2),
Fraction::from(9),
],
};
let res = m.gaussian_elimination();
assert!(res.is_err());
}
#[test]
fn test_gj_identity_matrix() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
],
};
let res = m.gauss_jordan_elimination().unwrap();
assert_eq!(res.data, m.data);
}
#[test]
fn test_gj_already_reduced() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
],
};
let res = m.gauss_jordan_elimination().unwrap();
assert_eq!(res.data, m.data);
}
#[test]
fn test_gj_simple() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(2),
Fraction::from(1),
Fraction::from(4),
Fraction::from(3),
],
};
let res = m.gauss_jordan_elimination().unwrap();
// debe quedar identidad
assert_eq!(
res.data,
vec![
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
]
);
}
#[test]
fn test_gj_requires_pivoting() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(0),
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
],
};
let res = m.gauss_jordan_elimination().unwrap();
// diagonal debe ser 1
assert_eq!(*res.get(0, 0).unwrap(), Fraction::from(1));
assert_eq!(*res.get(1, 1).unwrap(), Fraction::from(1));
// fuera de diagonal debe ser 0
assert!(res.get(0, 1).unwrap().is_zero());
assert!(res.get(1, 0).unwrap().is_zero());
}
#[test]
fn test_gj_3x3() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(0),
Fraction::from(1),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
Fraction::from(0),
],
};
let res = m.gauss_jordan_elimination().unwrap();
// debe quedar identidad
for i in 0..3 {
for j in 0..3 {
if i == j {
assert_eq!(*res.get(i, j).unwrap(), Fraction::from(1));
} else {
assert!(res.get(i, j).unwrap().is_zero());
}
}
}
}
#[test]
fn test_gj_singular_matrix() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(2),
Fraction::from(4),
],
};
let res = m.gauss_jordan_elimination();
assert!(matches!(res, Err(MatrixError::FailedGaussJordan)));
}
#[test]
fn test_gj_reduced_row_echelon_form() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(2),
Fraction::from(1),
Fraction::from(-1),
Fraction::from(-3),
Fraction::from(-1),
Fraction::from(2),
Fraction::from(-2),
Fraction::from(1),
Fraction::from(2),
],
};
let res = m.gauss_jordan_elimination().unwrap();
for i in 0..3 {
// pivote = 1
assert_eq!(*res.get(i, i).unwrap(), Fraction::from(1));
// columna del pivote = 0 excepto en pivote
for r in 0..3 {
if r != i {
assert!(res.get(r, i).unwrap().is_zero());
}
}
}
}
#[test]
fn test_gj_rectangular_matrix() {
let m = Matrix {
rows: 2,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
let res = m.gauss_jordan_elimination().unwrap();
// al menos debe cumplir forma escalonada reducida parcial
for i in 0..2 {
assert_eq!(*res.get(i, i).unwrap(), Fraction::from(1));
}
}
2026-04-29 16:57:35 -06:00
#[test]
fn test_det_1x1() {
let m = Matrix {
rows: 1,
columns: 1,
data: vec![Fraction::from(5)],
};
let det = m.get_determinant().unwrap();
assert_eq!(det, Fraction::from(5));
}
#[test]
fn test_det_identity() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
],
};
let det = m.get_determinant().unwrap();
assert_eq!(det, Fraction::from(1));
}
#[test]
fn test_det_upper_triangular() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(2),
Fraction::from(1),
Fraction::from(3),
Fraction::from(0),
Fraction::from(4),
Fraction::from(5),
Fraction::from(0),
Fraction::from(0),
Fraction::from(6),
],
};
let det = m.get_determinant().unwrap();
assert_eq!(det, Fraction::from(2 * 4 * 6));
}
#[test]
fn test_det_row_swap_sign() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(0),
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
],
};
let det = m.get_determinant().unwrap();
// determinante = (0*3 - 1*2) = -2
assert_eq!(det, Fraction::from(-2));
}
#[test]
fn test_det_singular_matrix() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(2),
Fraction::from(4),
],
};
let det = m.get_determinant().unwrap();
assert!(det.is_zero());
}
#[test]
fn test_det_3x3_known() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(6),
Fraction::from(1),
Fraction::from(1),
Fraction::from(4),
Fraction::from(-2),
Fraction::from(5),
Fraction::from(2),
Fraction::from(8),
Fraction::from(7),
],
};
let det = m.get_determinant().unwrap();
// determinante = -306
assert_eq!(det, Fraction::from(-306));
}
#[test]
fn test_det_not_squared() {
let m = Matrix::new(2, 3, Fraction::from(1)).unwrap();
let res = m.get_determinant();
assert!(matches!(res, Err(MatrixError::NotSquared)));
}
#[test]
fn test_det_scalar_multiple() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let mut scaled = m.clone();
// multiplicar fila 0 por 2
let row0 = scaled
.get_row(0)
.unwrap()
.map(|x| *x * Fraction::from(2))
.collect();
scaled.set_row(0, row0).unwrap();
let det_original = m.get_determinant().unwrap();
let det_scaled = scaled.get_determinant().unwrap();
assert_eq!(det_scaled, det_original * Fraction::from(2));
}
2026-04-29 17:12:45 -06:00
#[test]
fn test_inverse_identity() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
],
};
let inv = m.inverse().unwrap();
assert_eq!(inv.data, m.data);
}
#[test]
fn test_inverse_2x2() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(4),
Fraction::from(7),
Fraction::from(2),
Fraction::from(6),
],
};
let inv = m.inverse().unwrap();
// inversa conocida:
// (1/det) * [ 6 -7 ; -2 4 ] , det = 10
let expected = vec![
Fraction::new(6, 10).unwrap(),
Fraction::new(-7, 10).unwrap(),
Fraction::new(-2, 10).unwrap(),
Fraction::new(4, 10).unwrap(),
];
assert_eq!(inv.data, expected);
}
#[test]
fn test_inverse_multiplication_identity() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let inv = m.inverse().unwrap();
let identity = (m.clone() * inv.clone()).unwrap();
for i in 0..2 {
for j in 0..2 {
if i == j {
assert_eq!(*identity.get(i, j).unwrap(), Fraction::from(1));
} else {
assert!(identity.get(i, j).unwrap().is_zero());
}
}
}
}
#[test]
fn test_inverse_singular() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(2),
Fraction::from(4),
],
};
let res = m.inverse();
assert!(matches!(res, Err(MatrixError::FailedGaussJordan)));
}
#[test]
fn test_inverse_3x3() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(0),
Fraction::from(1),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
Fraction::from(0),
],
};
let inv = m.inverse().unwrap();
let identity = (m * inv).unwrap();
for i in 0..3 {
for j in 0..3 {
if i == j {
assert_eq!(*identity.get(i, j).unwrap(), Fraction::from(1));
} else {
assert!(identity.get(i, j).unwrap().is_zero());
}
}
}
}
#[test]
fn test_inverse_not_squared() {
let m = Matrix::new(2, 3, Fraction::from(1)).unwrap();
let res = m.inverse();
assert!(matches!(res, Err(MatrixError::NotSquared)));
}
2026-04-29 17:18:35 -06:00
#[test]
fn test_solve_simple() {
// 2x + y = 5
// x + y = 3
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(2),
Fraction::from(1),
Fraction::from(1),
Fraction::from(1),
],
};
let b = vec![Fraction::from(5), Fraction::from(3)];
let res = m.solve_matrix_with_gauss_jordan(b).unwrap();
assert_eq!(res, vec![Fraction::from(2), Fraction::from(1)]);
}
#[test]
fn test_solve_3x3() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(2),
Fraction::from(1),
Fraction::from(-1),
Fraction::from(-3),
Fraction::from(-1),
Fraction::from(2),
Fraction::from(-2),
Fraction::from(1),
Fraction::from(2),
],
};
let b = vec![Fraction::from(8), Fraction::from(-11), Fraction::from(-3)];
let res = m.solve_matrix_with_gauss_jordan(b).unwrap();
assert_eq!(
res,
vec![Fraction::from(2), Fraction::from(3), Fraction::from(-1)]
);
}
#[test]
fn test_solve_identity() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
],
};
let b = vec![Fraction::from(7), Fraction::from(9)];
let res = m.solve_matrix_with_gauss_jordan(b.clone()).unwrap();
assert_eq!(res, b);
}
#[test]
fn test_solve_singular() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(2),
Fraction::from(4),
],
};
let b = vec![Fraction::from(3), Fraction::from(6)];
let res = m.solve_matrix_with_gauss_jordan(b);
assert!(matches!(res, Err(MatrixError::FailedGaussJordan)));
}
#[test]
fn test_solve_invalid_size() {
let m = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let b = vec![Fraction::from(1)];
let res = m.solve_matrix_with_gauss_jordan(b);
assert!(matches!(res, Err(MatrixError::InvalidDataSize)));
}
#[test]
fn test_solve_not_squared() {
let m = Matrix::new(2, 3, Fraction::from(1)).unwrap();
let b = vec![Fraction::from(1), Fraction::from(2)];
let res = m.solve_matrix_with_gauss_jordan(b);
assert!(matches!(res, Err(MatrixError::NotSquared)));
}
2026-04-29 17:23:31 -06:00
#[test]
fn test_solve_inverse_simple() {
// 2x + y = 5
// x + y = 3
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(2),
Fraction::from(1),
Fraction::from(1),
Fraction::from(1),
],
};
let b = vec![Fraction::from(5), Fraction::from(3)];
let res = m.solve_matrix_with_inverse(b).unwrap();
let expected = vec![Fraction::from(2), Fraction::from(1)];
for i in 0..2 {
assert_eq!(*res.get(i, 0).unwrap(), expected[i]);
}
}
#[test]
fn test_solve_inverse_3x3() {
let m = Matrix {
rows: 3,
columns: 3,
data: vec![
Fraction::from(2),
Fraction::from(1),
Fraction::from(-1),
Fraction::from(-3),
Fraction::from(-1),
Fraction::from(2),
Fraction::from(-2),
Fraction::from(1),
Fraction::from(2),
],
};
let b = vec![Fraction::from(8), Fraction::from(-11), Fraction::from(-3)];
let res = m.solve_matrix_with_inverse(b).unwrap();
let expected = vec![Fraction::from(2), Fraction::from(3), Fraction::from(-1)];
for i in 0..3 {
assert_eq!(*res.get(i, 0).unwrap(), expected[i]);
}
}
#[test]
fn test_solve_inverse_identity() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
],
};
let b = vec![Fraction::from(7), Fraction::from(9)];
let res = m.solve_matrix_with_inverse(b.clone()).unwrap();
for i in 0..2 {
assert_eq!(*res.get(i, 0).unwrap(), b[i]);
}
}
#[test]
fn test_solve_inverse_singular() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(2),
Fraction::from(4),
],
};
let b = vec![Fraction::from(3), Fraction::from(6)];
let res = m.solve_matrix_with_inverse(b);
assert!(matches!(res, Err(MatrixError::FailedGaussJordan)));
}
#[test]
fn test_solve_inverse_invalid_size() {
let m = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let b = vec![Fraction::from(1)];
let res = m.solve_matrix_with_inverse(b);
assert!(matches!(res, Err(MatrixError::InvalidDataSize)));
}
#[test]
fn test_solve_inverse_not_squared() {
let m = Matrix::new(2, 3, Fraction::from(1)).unwrap();
let b = vec![Fraction::from(1), Fraction::from(2)];
let res = m.solve_matrix_with_inverse(b);
assert!(matches!(res, Err(MatrixError::NotSquared)));
}
#[test]
fn test_solve_inverse_vs_gauss_jordan() {
let m = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(3),
Fraction::from(2),
Fraction::from(1),
Fraction::from(2),
],
};
let b = vec![Fraction::from(5), Fraction::from(5)];
let res_inv = m.solve_matrix_with_inverse(b.clone()).unwrap();
let res_gj = m.solve_matrix_with_gauss_jordan(b).unwrap();
for i in 0..2 {
assert_eq!(*res_inv.get(i, 0).unwrap(), res_gj[i]);
}
}
2026-04-29 17:28:51 -06:00
#[test]
fn test_add_basic() {
let a = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let b = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
],
};
let result = (a + b).unwrap();
let expected = vec![
Fraction::from(6),
Fraction::from(8),
Fraction::from(10),
Fraction::from(12),
];
assert_eq!(result.data, expected);
}
#[test]
fn test_add_negative_values() {
let a = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(-1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(-4),
],
};
let b = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(-2),
Fraction::from(-3),
Fraction::from(4),
],
};
let result = (a + b).unwrap();
assert!(result.data.iter().all(|x| x.is_zero()));
}
#[test]
fn test_add_commutative() {
let a = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let b = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
],
};
let res1 = (a.clone() + b.clone()).unwrap();
let res2 = (b + a).unwrap();
assert_eq!(res1.data, res2.data);
}
#[test]
fn test_add_associative() {
let a = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let b = Matrix::new(2, 2, Fraction::from(2)).unwrap();
let c = Matrix::new(2, 2, Fraction::from(3)).unwrap();
let res1 = ((a.clone() + b.clone()).unwrap() + c.clone()).unwrap();
let res2 = (a + (b + c).unwrap()).unwrap();
assert_eq!(res1.data, res2.data);
}
#[test]
fn test_add_fractions() {
let a = Matrix {
rows: 1,
columns: 2,
data: vec![Fraction::new(1, 2).unwrap(), Fraction::new(1, 3).unwrap()],
};
let b = Matrix {
rows: 1,
columns: 2,
data: vec![Fraction::new(1, 2).unwrap(), Fraction::new(2, 3).unwrap()],
};
let result: Matrix = (a + b).unwrap();
let expected = vec![
Fraction::from(1), // 1/2 + 1/2
Fraction::from(1), // 1/3 + 2/3
];
assert_eq!(result.data, expected);
}
2026-04-29 17:31:43 -06:00
#[test]
fn test_sub_basic() {
let a = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
],
};
let b = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let result = (a - b).unwrap();
let expected = vec![
Fraction::from(4),
Fraction::from(4),
Fraction::from(4),
Fraction::from(4),
];
assert_eq!(result.data, expected);
}
#[test]
fn test_sub_zero_matrix() {
let a = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let zero = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = (a.clone() - zero).unwrap();
assert_eq!(result.data, a.data);
}
#[test]
fn test_sub_self() {
let a = Matrix::new(3, 3, Fraction::from(5)).unwrap();
let result = (a.clone() - a).unwrap();
assert!(result.data.iter().all(|x| x.is_zero()));
}
#[test]
fn test_sub_negative_values() {
let a = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(-2),
Fraction::from(3),
Fraction::from(-4),
],
};
let b = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(-1),
Fraction::from(2),
Fraction::from(-3),
Fraction::from(4),
],
};
let result = (a - b).unwrap();
let expected = vec![
Fraction::from(2),
Fraction::from(-4),
Fraction::from(6),
Fraction::from(-8),
];
assert_eq!(result.data, expected);
}
#[test]
fn test_sub_invalid_size() {
let a = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let b = Matrix::new(3, 2, Fraction::from(1)).unwrap();
let result = a - b;
assert!(matches!(result, Err(MatrixError::InvalidSizeForSub)));
}
#[test]
fn test_sub_not_commutative() {
let a = Matrix::new(2, 2, Fraction::from(3)).unwrap();
let b = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let res1 = (a.clone() - b.clone()).unwrap();
let res2 = (b - a).unwrap();
assert_ne!(res1.data, res2.data);
}
#[test]
fn test_sub_anti_symmetry() {
let a = Matrix::new(2, 2, Fraction::from(3)).unwrap();
let b = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let res1 = (a.clone() - b.clone()).unwrap();
let res2 = (b - a).unwrap();
for i in 0..res1.data.len() {
assert_eq!(res1.data[i], -res2.data[i]);
}
}
#[test]
fn test_sub_fractions() {
let a = Matrix {
rows: 1,
columns: 2,
data: vec![Fraction::new(3, 4).unwrap(), Fraction::new(5, 6).unwrap()],
};
let b = Matrix {
rows: 1,
columns: 2,
data: vec![Fraction::new(1, 4).unwrap(), Fraction::new(1, 6).unwrap()],
};
let result = (a - b).unwrap();
let expected = vec![Fraction::new(1, 2).unwrap(), Fraction::new(2, 3).unwrap()];
assert_eq!(result.data, expected);
}
2026-04-29 17:34:26 -06:00
#[test]
fn test_mul_basic_2x2() {
let a = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let b = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(5),
Fraction::from(6),
Fraction::from(7),
Fraction::from(8),
],
};
let result = (a * b).unwrap();
let expected = vec![
Fraction::from(19),
Fraction::from(22),
Fraction::from(43),
Fraction::from(50),
];
assert_eq!(result.data, expected);
}
#[test]
fn test_mul_identity() {
let a = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
let identity = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(0),
Fraction::from(0),
Fraction::from(1),
],
};
let result = (a.clone() * identity).unwrap();
assert_eq!(result.data, a.data);
}
#[test]
fn test_mul_not_commutative() {
let a = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
],
};
let b = Matrix {
rows: 2,
columns: 2,
data: vec![
Fraction::from(2),
Fraction::from(0),
Fraction::from(1),
Fraction::from(2),
],
};
let res1 = (a.clone() * b.clone()).unwrap();
let res2 = (b * a).unwrap();
assert_ne!(res1.data, res2.data);
}
#[test]
fn test_mul_rectangular() {
// 2x3 * 3x2
let a = Matrix {
rows: 2,
columns: 3,
data: vec![
Fraction::from(1),
Fraction::from(2),
Fraction::from(3),
Fraction::from(4),
Fraction::from(5),
Fraction::from(6),
],
};
let b = Matrix {
rows: 3,
columns: 2,
data: vec![
Fraction::from(7),
Fraction::from(8),
Fraction::from(9),
Fraction::from(10),
Fraction::from(11),
Fraction::from(12),
],
};
let result = (a * b).unwrap();
let expected = vec![
Fraction::from(58),
Fraction::from(64),
Fraction::from(139),
Fraction::from(154),
];
assert_eq!(result.data, expected);
}
#[test]
fn test_mul_invalid_size() {
let a = Matrix::new(2, 3, Fraction::from(1)).unwrap();
let b = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let result = a * b;
assert!(matches!(result, Err(MatrixError::InvalidSizeForMul)));
}
#[test]
fn test_mul_zero_matrix() {
let a = Matrix::new(2, 2, Fraction::from(5)).unwrap();
let zero = Matrix::new(2, 2, Fraction::from(0)).unwrap();
let result = (a * zero).unwrap();
assert!(result.data.iter().all(|x| x.is_zero()));
}
#[test]
fn test_mul_fractions() {
let a = Matrix {
rows: 1,
columns: 2,
data: vec![Fraction::new(1, 2).unwrap(), Fraction::new(1, 3).unwrap()],
};
let b = Matrix {
rows: 2,
columns: 1,
data: vec![Fraction::new(2, 1).unwrap(), Fraction::new(3, 1).unwrap()],
};
let result = (a * b).unwrap();
// (1/2 * 2) + (1/3 * 3) = 1 + 1 = 2
assert_eq!(result.data, vec![Fraction::from(2)]);
}
#[test]
fn test_mul_associative() {
let a = Matrix::new(2, 2, Fraction::from(1)).unwrap();
let b = Matrix::new(2, 2, Fraction::from(2)).unwrap();
let c = Matrix::new(2, 2, Fraction::from(3)).unwrap();
let res1 = ((a.clone() * b.clone()).unwrap() * c.clone()).unwrap();
let res2 = (a * (b * c).unwrap()).unwrap();
assert_eq!(res1.data, res2.data);
}
2025-10-07 11:53:10 -06:00
}