use std::rc::Rc;
use crate::{
argument::Argument,
expressions::{
product::{product_of, product_of_iter},
sum::sum_of,
Expression,
},
};
use super::DerivationRule;
pub struct Foil {}
impl DerivationRule for Foil {
fn apply(&self, input: Expression) -> Vec<(Expression, Rc<Argument>)> {
let product = match input {
Expression::Product(ref p) => p,
_ => return vec![],
};
let (sums, non_sums) = {
let mut sums = Vec::new();
let mut non_sums = Vec::new();
for factor in product.factors() {
match factor {
Expression::Sum(_) => sums.push(factor.clone()),
_ => non_sums.push(factor.clone()),
}
}
(sums, non_sums)
};
let mut equivalents = Vec::<Expression>::new();
for first in &sums {
for second in &sums {
if first == second {
continue;
}
let first_sum = match first {
Expression::Sum(ref s) => s,
_ => panic!(),
};
let second_sum = match second {
Expression::Sum(ref s) => s,
_ => panic!(),
};
let excluded_sums = sums
.iter()
.filter(|s| *s != first && *s != second)
.map(|s| (*s).clone())
.collect::<Vec<Expression>>();
let mut new_terms = Vec::<Expression>::new();
for term in first_sum.terms() {
for other in second_sum.terms() {
new_terms.push(product_of(&[term.clone(), other.clone()]));
}
}
equivalents.push(product_of_iter(
&mut [sum_of(&new_terms)]
.into_iter()
.chain(non_sums.to_vec())
.chain(excluded_sums),
))
}
}
equivalents
.iter()
.map(|expression| {
(
expression.clone(),
Argument::new(String::from("FOIL"), vec![input.clone()], self.name()),
)
})
.collect::<Vec<(Expression, Rc<Argument>)>>()
}
fn name(&self) -> String {
String::from("Foil")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{convenience_expressions::v, derivation_rules::DerivationRule};
#[test]
fn test_1() {
let rule = Foil {};
let start = product_of(&[sum_of(&[v("a"), v("b")]), sum_of(&[v("c"), v("d")])]);
let result = rule.apply(start);
assert_eq!(
result.first().unwrap().0,
sum_of(&[
product_of(&[v("a"), v("c")]),
product_of(&[v("a"), v("d")]),
product_of(&[v("b"), v("c")]),
product_of(&[v("b"), v("d")]),
])
);
}
}