use std::rc::Rc;
use crate::{
argument::Argument,
expressions::{product::product_of_iter, sum::sum_of, Expression},
};
use super::DerivationRule;
pub struct DistributiveProperty {}
impl DerivationRule for DistributiveProperty {
fn apply(&self, input: Expression) -> Vec<(Expression, Rc<Argument>)> {
let product = match input {
Expression::Product(ref p) => p,
_ => return vec![],
};
let sum_factors = product
.factors()
.iter()
.filter(|factor| matches!(factor, Expression::Sum(_)));
let mut equivalents: Vec<Expression> = vec![];
for sum in sum_factors {
let terms = match sum {
Expression::Sum(s) => s.terms(),
_ => panic!(),
};
let other_factors: Vec<&Expression> =
product.factors().iter().filter(|f| *f != sum).collect();
let base: u32 = 2;
for s in 1..base.pow(other_factors.len() as u32) {
let (to_distribute, others) = {
let mut subset: Vec<&Expression> = vec![];
let mut complement: Vec<&Expression> = vec![];
(0..other_factors.len()).for_each(|i| {
if s & (1 << i) != 0 {
subset.push(other_factors[i]);
} else {
complement.push(other_factors[i]);
}
});
(subset, complement)
};
let new_terms = terms
.iter()
.map(|term| {
product_of_iter(
&mut [term]
.iter()
.map(|e| (*e).clone())
.chain(to_distribute.iter().map(|e| (*e).clone())),
)
})
.collect::<Vec<Expression>>();
let new_factors = &mut new_terms
.iter()
.chain(others)
.cloned()
.collect::<Vec<Expression>>();
let equivalent = sum_of(new_factors);
equivalents.push(equivalent);
}
}
equivalents
.iter()
.map(|eq| {
(
eq.clone(),
Argument::new(
String::from("Divide fractions"),
vec![input.clone()],
self.name(),
),
)
})
.collect()
}
fn name(&self) -> String {
String::from("Distributive Property")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
convenience_expressions::v, derivation_rules::DerivationRule,
expressions::product::product_of,
};
#[test]
fn test_1() {
let rule = DistributiveProperty {};
let start = product_of(&[sum_of(&[v("a"), v("b")]), v("c")]);
let result = rule.apply(start).first().unwrap().0.clone();
let expected = sum_of(&[product_of(&[v("a"), v("c")]), product_of(&[v("b"), v("c")])]);
assert_eq!(result, expected);
}
}