pub use fractions::{Fraction, FractionError}; use std::cmp::min; use std::error::Error; use std::fmt::{self, Debug, Display}; use std::ops::Add; use std::ops::Mul; use std::ops::Sub; #[derive(Debug, Eq, PartialEq)] pub enum MatrixError { IndexOutOfRange, RowOutOfRange, ColumnOutOfRange, NotSquared, InvalidDataSize, InvalidSizeForAdd, InvalidSizeForSub, InvalidSizeForMul, ZeroSize, FailedGauss, FailedGaussJordan, FractionError(FractionError), } impl From for MatrixError { fn from(err: FractionError) -> Self { MatrixError::FractionError(err) } } 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, } } } #[derive(PartialEq, Eq, Debug, Clone)] pub struct Matrix { rows: usize, columns: usize, data: Vec, } impl Matrix { pub fn new(rows: usize, columns: usize, default: Fraction) -> Result { if columns < 1 || rows < 1 { return Err(MatrixError::ZeroSize); } Ok(Self { rows, columns, data: vec![default; rows * columns], }) } pub fn get(&self, row: usize, column: usize) -> Result<&Fraction, MatrixError> { if row >= self.rows || column >= self.columns { return Err(MatrixError::IndexOutOfRange); } let index = row * self.columns + column; return Ok(&self.data[index]); } pub fn get_row(&self, row: usize) -> Result, MatrixError> { if row >= self.rows { return Err(MatrixError::RowOutOfRange); } let start = row * self.columns; let end = start + self.columns; return Ok(self.data[start..end].iter()); } pub fn get_column( &self, column: usize, ) -> Result, MatrixError> { if column >= self.columns { return Err(MatrixError::ColumnOutOfRange); } Ok((0..self.rows).map(move |i| &self.data[i * self.columns + column])) } pub fn get_diagonal(&self) -> Result, 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); } let index = row * self.columns + column; self.data[index] = data; Ok(()) } pub fn set_row(&mut self, row: usize, data: Vec) -> Result<(), MatrixError> { if row >= self.rows { return Err(MatrixError::IndexOutOfRange); } if data.len() != self.columns { return Err(MatrixError::InvalidDataSize); } 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) -> Result<(), MatrixError> { if column >= self.columns { return Err(MatrixError::ColumnOutOfRange); } if data.len() != self.rows { return Err(MatrixError::InvalidDataSize); } 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) -> Result<(), MatrixError> { if index > self.columns { return Err(MatrixError::ColumnOutOfRange); } if data.len() != self.rows { return Err(MatrixError::InvalidDataSize); } for i in 0..self.rows { self.data.insert((i * self.columns) + index + i, data[i]); } self.columns += 1; Ok(()) } pub fn insert_rows(&mut self, index: usize, data: Vec) -> Result<(), MatrixError> { if index > self.rows { return Err(MatrixError::RowOutOfRange); } if data.len() != self.columns { return Err(MatrixError::InvalidDataSize); } 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 { if start.0 >= self.rows || start.1 >= self.columns { return Err(MatrixError::IndexOutOfRange); } 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 = 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 { rows: end.0 - start.0, columns: end.1 - start.1, data: new_data, }; Ok(new_matrix) } fn partial_pivoting(&mut self, col: usize, sign: &mut Fraction) -> Result { if col >= self.columns { return Err(MatrixError::ColumnOutOfRange); } let mut max_row = col; let mut max_value = self.get(col, col)?.abs(); for r in (col + 1)..self.rows { let val = self.get(r, col)?.abs(); 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)?; *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(), }; let mut sign = Fraction::new(1, 1)?; for i in 0..min(self.rows, self.columns) { // We do partial pivoting for better efifiency and security let pivot_exists = trig_matrix.partial_pivoting(i, &mut sign)?; // If there ain't no other thing but 0 then we're // fucked, determinant is zero 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; } let pivot = *trig_matrix.get(i, i)?; if pivot.is_zero() { return Err(MatrixError::FailedGauss); } // 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)?; let new_row = row_x .zip(row_i) .map(|(a, b)| *a - m * *b) .collect::>(); trig_matrix.set_row(x, new_row)?; } } Ok((trig_matrix, sign)) } pub fn gauss_jordan_elimination(&self) -> Result { 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) { let pivot_exists = new_matrix.partial_pivoting(i, &mut dummy)?; if !pivot_exists { return Err(MatrixError::FailedGaussJordan); } let pivot = *new_matrix.get(i, i)?; let new_pivot_row = new_matrix .get_row(i)? .map(|x| *x / pivot) .collect::, _>>()?; new_matrix.set_row(i, new_pivot_row)?; for r in 0..new_matrix.rows { if r == i { continue; } let factor = *new_matrix.get(r, i)?; 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::>(); new_matrix.set_row(r, new_row_normalized)?; } } Ok(new_matrix) } pub fn get_determinant(&self) -> Result { if self.rows != self.columns { return Err(MatrixError::NotSquared); } let (trig_matrix, sign) = match self.gaussian_elimination() { Err(MatrixError::FailedGauss) => return Ok(Fraction::from(0)), Ok((matrix, sign)) => (matrix, sign), Err(err) => return Err(err), }; // YES, now we got ourselves a triangular matrix, now we just // take the product of the diagonal and multiply by sign, that's // the determinant :) let determinant = sign * trig_matrix .get_diagonal()? .copied() .fold(Fraction::from(1i64), |acc, x| acc * x); return Ok(determinant); } pub fn inverse(&self) -> Result { if self.columns != self.rows { return Err(MatrixError::NotSquared); } let mut augmented = Matrix { rows: self.rows, columns: self.columns, data: self.data.clone(), }; // construir [A | I] for i in 0..self.rows { let mut col = vec![Fraction::from(0); self.rows]; col[i] = Fraction::from(1); augmented.insert_column(augmented.columns, col)?; } // ahora debe ser 2N assert_eq!(augmented.columns, self.columns * 2); 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, ) -> Result, 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 .get_column(solved_matrix.columns - 1)? .cloned() .collect(); Ok(result) } pub fn solve_matrix_with_inverse( &self, solution_matrix: Vec, ) -> Result { if self.columns != self.rows { return Err(MatrixError::NotSquared); } if solution_matrix.len() != self.rows { return Err(MatrixError::InvalidDataSize); } let sol = Matrix { rows: self.rows, columns: 1, data: solution_matrix, }; let solution_matrix = (self.inverse()? * sol)?; Ok(solution_matrix) } } impl Add for Matrix { type Output = Result; fn add(self, other: Self) -> Self::Output { if self.rows != other.rows || self.columns != other.columns { return Err(MatrixError::InvalidSizeForAdd); } let mut new_data = Vec::with_capacity(self.data.len()); for i in 0..self.data.len() { new_data.push(self.data[i] + other.data[i]); } Ok(Matrix { columns: self.columns, rows: self.rows, data: new_data, }) } } impl Sub for Matrix { type Output = Result; fn sub(self, other: Self) -> Self::Output { if self.rows != other.rows || self.columns != other.columns { return Err(MatrixError::InvalidSizeForSub); } let mut new_data = Vec::with_capacity(self.data.len()); for i in 0..self.data.len() { new_data.push(self.data[i] - other.data[i]); } Ok(Matrix { columns: self.columns, rows: self.rows, data: new_data, }) } } impl Mul for Matrix { type Output = Result; fn mul(self, other: Self) -> Self::Output { if self.columns != other.rows { return Err(MatrixError::InvalidSizeForMul); } let mut new_data: Vec = Vec::new(); 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)?; let mut new_value = Fraction::from(0); for (a, b) in current_row.zip(current_column) { new_value = new_value + (*a * *b); } new_data.push(new_value); } } Ok(Matrix { rows: self.rows, columns: other.columns, data: new_data, }) } } impl fmt::Display for Matrix { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let strings: Vec = 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, ", ")?; } } writeln!(f, " }}")?; } Ok(()) } } #[cfg(test)] mod tests { use super::*; #[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]); } #[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)); } } } #[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 = 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 = 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 = 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 = 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 = m.get_row(0).unwrap().cloned().collect(); let iter2: Vec = 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()); } } #[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 = 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 = 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 = 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 = 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 = 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 = m.get_column(1).unwrap().cloned().collect(); let col2: Vec = m.get_column(1).unwrap().cloned().collect(); assert_eq!(col1, col2); } #[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 = 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 = 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 = 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 = 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 = m.get_diagonal().unwrap().cloned().collect(); let d2: Vec = m.get_diagonal().unwrap().cloned().collect(); assert_eq!(d1, d2); } #[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); } } } #[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 = 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::>(), 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::>(), 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![Fraction::from(1), Fraction::from(1)] ); // fila 2 intacta assert_eq!( m.get_row(2).unwrap().cloned().collect::>(), 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::>(), 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); } } #[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 = 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::>(), 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::>(), 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![Fraction::from(1); 3] ); // columna 2 intacta assert_eq!( m.get_column(2).unwrap().cloned().collect::>(), 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::>(), 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::>(), new_col ); } #[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![Fraction::from(5), Fraction::from(6)] ); assert_eq!( m.get_row(2).unwrap().cloned().collect::>(), 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![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),] ); } #[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![Fraction::from(3), Fraction::from(6), Fraction::from(9)] ); assert_eq!( m.get_column(2).unwrap().cloned().collect::>(), 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![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::>(), 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)]; let result = m.insert_column(3, data); 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![Fraction::from(1), Fraction::from(3), Fraction::from(5)] ); assert_eq!( m.get_column(2).unwrap().cloned().collect::>(), 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::>(), 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),] ); } #[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::>(), 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::>(), 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)]; let result = m.insert_rows(3, data); 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![Fraction::from(1), Fraction::from(2)] ); assert_eq!( m.get_row(2).unwrap().cloned().collect::>(), 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::>(), 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)); } } #[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)); } #[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))); } #[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))); } #[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]); } } #[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); } #[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); } #[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); } }