use crate::primitive::int;
use crate::symbolic::{
expr::{SymExpr, Primary},
simplify::{fraction::{extract_integer_fraction, make_fraction}, rules::do_multiply, step::Step},
step_collector::StepCollector,
};
pub fn multiply_zero(expr: &SymExpr, step_collector: &mut dyn StepCollector<Step>) -> Option<SymExpr> {
let opt = do_multiply(expr, |factors| {
if factors.iter().any(|factor| factor.as_integer().map(|n| n.is_zero()).unwrap_or(false)) {
Some(SymExpr::Primary(Primary::Integer(int(0))))
} else {
None
}
})?;
step_collector.push(Step::MultiplyZero);
Some(opt)
}
pub fn multiply_one(expr: &SymExpr, step_collector: &mut dyn StepCollector<Step>) -> Option<SymExpr> {
let opt = do_multiply(expr, |factors| {
let new_factors = factors.iter()
.filter(|factor| {
factor.as_integer()
.map(|n| n != &1)
.unwrap_or(true)
})
.cloned()
.collect::<Vec<_>>();
if new_factors.len() == factors.len() {
None
} else {
Some(SymExpr::Mul(new_factors).downgrade())
}
})?;
step_collector.push(Step::MultiplyOne);
Some(opt)
}
pub fn reduce_numerical_fraction(expr: &SymExpr, step_collector: &mut dyn StepCollector<Step>) -> Option<SymExpr> {
let opt = do_multiply(expr, |factors| {
let mut new_factors = factors.to_vec();
let (numerator, denominator) = extract_integer_fraction(&mut new_factors, false, false)?;
let gcd = numerator.clone().gcd(&denominator.clone());
if gcd == 1 {
return None;
}
Some(SymExpr::Mul(new_factors) * make_fraction(
SymExpr::Primary(Primary::Integer(numerator / &gcd)),
SymExpr::Primary(Primary::Integer(denominator / &gcd)),
))
})?;
step_collector.push(Step::ReduceFraction);
Some(opt)
}
pub fn combine_like_factors(expr: &SymExpr, step_collector: &mut dyn StepCollector<Step>) -> Option<SymExpr> {
let opt = do_multiply(expr, |factors| {
let mut new_factors = factors.to_vec();
let mut current_factor_idx = 0;
fn get_exp(expr: &SymExpr) -> (SymExpr, SymExpr) {
match expr {
SymExpr::Exp(lhs, rhs) => (*lhs.clone(), *rhs.clone()),
expr => (expr.clone(), SymExpr::Primary(Primary::Integer(int(1)))),
}
}
while current_factor_idx < new_factors.len() {
let (mut current_factor, mut current_factor_exp) = get_exp(&new_factors[current_factor_idx]);
let mut next_factor_idx = current_factor_idx + 1;
while next_factor_idx < new_factors.len() {
let (next_factor, next_factor_exp) = get_exp(&new_factors[next_factor_idx]);
if current_factor_exp == next_factor_exp
&& (current_factor.is_integer() && next_factor.is_integer()
|| current_factor.is_float() && next_factor.is_float()) {
current_factor *= next_factor;
new_factors.swap_remove(next_factor_idx);
} else if current_factor == next_factor {
current_factor_exp += next_factor_exp;
new_factors.swap_remove(next_factor_idx);
} else {
next_factor_idx += 1;
}
}
if current_factor_exp.as_integer().map(|n| n == &1).unwrap_or(false) {
new_factors[current_factor_idx] = current_factor;
} else {
new_factors[current_factor_idx] = SymExpr::Exp(
Box::new(current_factor),
Box::new(current_factor_exp),
);
}
current_factor_idx += 1;
}
if new_factors.len() == factors.len() {
None
} else {
Some(SymExpr::Mul(new_factors).downgrade())
}
})?;
step_collector.push(Step::CombineLikeFactors);
Some(opt)
}
pub fn all(expr: &SymExpr, step_collector: &mut dyn StepCollector<Step>) -> Option<SymExpr> {
multiply_zero(expr, step_collector)
.or_else(|| multiply_one(expr, step_collector))
.or_else(|| reduce_numerical_fraction(expr, step_collector))
.or_else(|| combine_like_factors(expr, step_collector))
}