use super::state::{PresolveState, PresolveStatus};
use super::substitution::{eliminate_variable_via_eq_row, fill_in_exceeds_budget};
use crate::problem::ConstraintType;
use crate::tolerances::ZERO_TOL;
pub(super) fn step7_free_var_substitution(
st: &mut PresolveState,
new_subst: &mut usize,
) -> Result<(), PresolveStatus> {
let n = st.bounds.len();
for j in 0..n {
if st.removed_cols[j] {
continue;
}
let (orig_lb, orig_ub) = st.orig_bounds[j];
if orig_lb != f64::NEG_INFINITY || orig_ub != f64::INFINITY {
continue;
}
let col_entries = st.active_col_entries(j);
if col_entries.is_empty() {
continue;
}
let mut best: Option<(usize, f64)> = None;
for &(i, a_ij) in &col_entries {
if st.constraint_types[i] != ConstraintType::Eq {
continue;
}
if a_ij.abs() < ZERO_TOL {
continue;
}
match best {
None => best = Some((i, a_ij)),
Some((_, ba)) => {
if a_ij.abs() > ba.abs() {
best = Some((i, a_ij));
}
}
}
}
if let Some((piv_row, _)) = best {
if fill_in_exceeds_budget(st, piv_row, j) {
continue;
}
eliminate_variable_via_eq_row(st, piv_row, j)?;
*new_subst += 1;
}
}
Ok(())
}
pub(super) fn step8_free_singleton_col(
st: &mut PresolveState,
new_subst: &mut usize,
) -> Result<(), PresolveStatus> {
let n = st.bounds.len();
for j in 0..n {
if st.removed_cols[j] {
continue;
}
let (orig_lb, orig_ub) = st.orig_bounds[j];
let col_entries = st.active_col_entries(j);
if col_entries.len() != 1 {
continue;
}
let (i, a_ij) = col_entries[0];
if a_ij.abs() < ZERO_TOL {
continue;
}
let ct = st.constraint_types[i];
let cj = st.c[j];
if orig_lb != f64::NEG_INFINITY || orig_ub != f64::INFINITY {
continue;
}
if ct != ConstraintType::Eq {
continue;
}
if fill_in_exceeds_budget(st, i, j) {
continue;
}
eliminate_variable_via_eq_row(st, i, j)?;
*new_subst += 1;
let _ = cj;
}
Ok(())
}