use anyhow::{Result, anyhow};
use malachite::rational::Rational;
use std::sync::atomic::AtomicBool;
use crate::{
ebi_matrix::EbiMatrix,
ebi_number::{One, Zero},
matrix::{
fraction_matrix_enum::FractionMatrixEnum, fraction_matrix_exact::FractionMatrixExact,
fraction_matrix_f64::FractionMatrixF64,
}, GaussJordan,
};
macro_rules! gauss_jordan {
($self:ident) => {
let number_of_rows = $self.number_of_rows();
let number_of_columns = $self.number_of_columns();
if number_of_rows == 0 || number_of_columns == 0 {
return;
}
for row_a in 0..number_of_rows - 1 {
if $self.values[row_a * number_of_columns + row_a].is_zero() {
continue;
} else {
for row_b in row_a..number_of_rows - 1 {
if !$self.values[(row_b + 1) * number_of_columns + row_a].is_zero() {
let mut factor =
$self.values[(row_b + 1) * number_of_columns + row_a].clone();
factor /= &$self.values[row_a * number_of_columns + row_a];
for column in row_a..number_of_columns {
let mut old = $self.values[row_a * number_of_columns + column].clone();
old *= &factor;
$self.values[(row_b + 1) * number_of_columns + column] -= old;
}
}
}
}
}
for i in (0..number_of_rows).rev() {
if $self.values[i * number_of_columns + i].is_zero() {
continue;
} else {
for j in (0..i).rev() {
let mut factor = $self.values[j * number_of_columns + i].clone();
factor /= &$self.values[i * number_of_columns + i];
for k in i..number_of_columns {
let mut old = $self.values[i * number_of_columns + k].clone();
old *= &factor;
$self.values[j * number_of_columns + k] -= old;
}
}
}
}
};
}
macro_rules! gauss_jordan_reduced {
($self:expr, $t:ident) => {{
{
$self.gauss_jordan();
let number_of_rows = $self.number_of_rows();
let number_of_columns = $self.number_of_columns();
let failed = AtomicBool::new(false);
$self
.values
.chunks_mut(number_of_columns)
.enumerate()
.for_each(|(i, row)| {
let factor = row[i].clone();
if factor.is_zero() {
failed.store(true, std::sync::atomic::Ordering::Relaxed);
} else {
for j in number_of_rows..number_of_columns {
row[j] /= &factor;
}
row[i] = $t::one();
}
});
if failed.load(std::sync::atomic::Ordering::Relaxed) {
return Err(anyhow!("matrix has no reduced row-echelon form"));
}
Ok($self)
}
}};
}
impl GaussJordan for FractionMatrixF64 {
fn gauss_jordan(&mut self) {
gauss_jordan!(self);
}
fn gauss_jordan_reduced(mut self) -> Result<Self> {
gauss_jordan_reduced!(self, f64)
}
}
impl GaussJordan for FractionMatrixExact {
fn gauss_jordan(&mut self) {
gauss_jordan!(self);
}
fn gauss_jordan_reduced(mut self) -> Result<Self> {
gauss_jordan_reduced!(self, Rational)
}
}
impl GaussJordan for FractionMatrixEnum {
fn gauss_jordan(&mut self) {
match self {
FractionMatrixEnum::Approx(m) => m.gauss_jordan(),
FractionMatrixEnum::Exact(m) => m.gauss_jordan(),
FractionMatrixEnum::CannotCombineExactAndApprox => {}
}
}
fn gauss_jordan_reduced(self) -> Result<Self> {
match self {
FractionMatrixEnum::Approx(m) => {
Ok(FractionMatrixEnum::Approx(m.gauss_jordan_reduced()?))
}
FractionMatrixEnum::Exact(m) => {
Ok(FractionMatrixEnum::Exact(m.gauss_jordan_reduced()?))
}
FractionMatrixEnum::CannotCombineExactAndApprox => {
return Err(anyhow!("cannot combine exact and approximate arithmetic"));
}
}
}
}