use crate::symbolic::{
expr::SymExpr,
simplify::{rules::{do_multiply, do_power}, step::Step},
step_collector::StepCollector,
};
pub fn distributive_property(expr: &SymExpr, step_collector: &mut dyn StepCollector<Step>) -> Option<SymExpr> {
let opt = do_multiply(expr, |factors| {
let mut factors_to_distribute = factors.to_vec();
let add_factor_terms = {
let idx = factors_to_distribute.iter()
.position(|factor| matches!(factor, SymExpr::Add(_)));
if let Some(idx) = idx {
if let SymExpr::Add(terms) = factors_to_distribute.swap_remove(idx) {
terms
} else {
unreachable!()
}
} else {
return None;
}
};
let new_terms = add_factor_terms.into_iter()
.map(|term| {
SymExpr::Mul(factors_to_distribute.clone()) * term
})
.collect::<Vec<_>>();
Some(SymExpr::Add(new_terms))
})?;
step_collector.push(Step::DistributiveProperty);
Some(opt)
}
pub fn distribute_power(expr: &SymExpr, step_collector: &mut dyn StepCollector<Step>) -> Option<SymExpr> {
let opt = do_power(expr, |lhs, rhs| {
if let SymExpr::Mul(factors) = lhs {
let new_factors = factors.iter()
.map(|factor| SymExpr::Exp(
Box::new(factor.clone()),
Box::new(rhs.clone()),
))
.collect::<Vec<_>>();
return Some(SymExpr::Mul(new_factors));
}
None
})?;
step_collector.push(Step::DistributePower);
Some(opt)
}
pub fn all(expr: &SymExpr, step_collector: &mut dyn StepCollector<Step>) -> Option<SymExpr> {
distributive_property(expr, step_collector)
.or_else(|| distribute_power(expr, step_collector))
}