3640 lines
94 KiB
Rust
3640 lines
94 KiB
Rust
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<FractionError> 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<Fraction>,
|
|
}
|
|
|
|
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 {
|
|
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<impl Iterator<Item = &Fraction>, 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<impl Iterator<Item = &Fraction>, 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<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);
|
|
}
|
|
let index = row * self.columns + column;
|
|
|
|
self.data[index] = data;
|
|
Ok(())
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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> {
|
|
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<Fraction>) -> 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<Matrix, MatrixError> {
|
|
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<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 {
|
|
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<bool, MatrixError> {
|
|
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::<Vec<Fraction>>();
|
|
|
|
trig_matrix.set_row(x, new_row)?;
|
|
}
|
|
}
|
|
|
|
Ok((trig_matrix, sign))
|
|
}
|
|
|
|
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) {
|
|
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::<Result<Vec<_>, _>>()?;
|
|
|
|
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::<Vec<Fraction>>();
|
|
|
|
new_matrix.set_row(r, new_row_normalized)?;
|
|
}
|
|
}
|
|
|
|
Ok(new_matrix)
|
|
}
|
|
|
|
pub fn get_determinant(&self) -> Result<Fraction, MatrixError> {
|
|
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<Matrix, MatrixError> {
|
|
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<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
|
|
.get_column(solved_matrix.columns - 1)?
|
|
.cloned()
|
|
.collect();
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
pub fn solve_matrix_with_inverse(
|
|
&self,
|
|
solution_matrix: Vec<Fraction>,
|
|
) -> Result<Matrix, MatrixError> {
|
|
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<Self, MatrixError>;
|
|
|
|
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<Self, MatrixError>;
|
|
|
|
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<Self, MatrixError>;
|
|
|
|
fn mul(self, other: Self) -> Self::Output {
|
|
if self.columns != other.rows {
|
|
return Err(MatrixError::InvalidSizeForMul);
|
|
}
|
|
|
|
let mut new_data: Vec<Fraction> = 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<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, ", ")?;
|
|
}
|
|
}
|
|
|
|
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<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());
|
|
}
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
|
|
#[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<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);
|
|
}
|
|
}
|
|
|
|
#[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
|
|
);
|
|
}
|
|
|
|
#[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),]
|
|
);
|
|
}
|
|
|
|
#[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)];
|
|
|
|
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<_>>(),
|
|
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),]
|
|
);
|
|
}
|
|
|
|
#[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)];
|
|
|
|
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<_>>(),
|
|
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));
|
|
}
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
}
|