use crate::expression::Expression; use crate::eval::{Error, Evaluator}; pub struct Normaliser; impl Normaliser { pub fn new() -> Self { Normaliser {} } } impl Evaluator for Normaliser { type Expr = Expression; type Error = Error; fn eval(&mut self, input: Expression) -> Result { Ok(normalise(input)) } } fn normalise(expr: Expression) -> Expression { match expr { Expression::Integer(_) => expr, Expression::Symbol(_) => expr, Expression::Sum(x, y) => Expression::Sum(normalise_boxed(x), normalise_boxed(y)), Expression::Product(x, y) => Expression::Product(normalise_boxed(x), normalise_boxed(y)), Expression::Exponent(x, y) => Expression::Exponent(normalise_boxed(x), normalise_boxed(y)), Expression::Difference(x, y) => Expression::Sum( normalise_boxed(x), negation(normalise_boxed(y))), Expression::Quotient(x, y) => { if x.integer().is_some() && y.integer().is_some() { Expression::Quotient(x, y) } else { Expression::Product( normalise_boxed(x), reciprocal(normalise_boxed(y))) } }, } } fn normalise_boxed( mut b: std::boxed::Box) -> std::boxed::Box { *b = normalise(*b); b } fn negation( expr: std::boxed::Box) -> std::boxed::Box { let res = Expression::Product( std::boxed::Box::new(Expression::Integer(-1)), expr); std::boxed::Box::new(res) } fn reciprocal( expr: std::boxed::Box) -> std::boxed::Box { let res = if expr.is_constant() { Expression::Quotient( std::boxed::Box::new(Expression::Integer(1)), expr) } else { Expression::Exponent( expr, std::boxed::Box::new(Expression::Integer(-1))) }; std::boxed::Box::new(res) } #[cfg(test)] mod tests { use super::*; #[test] fn integer_is_unchanged() { let input = Expression::Integer(0); let result = Normaliser::new().eval(input).unwrap(); assert_eq!(result, Expression::Integer(0)); } #[test] fn symbol_is_unchanged() { let input = Expression::Symbol('x'); let result = Normaliser::new().eval(input).unwrap(); assert_eq!(result, Expression::Symbol('x')); } #[test] fn sum_of_integers_is_unchanged() { let input = Expression::sum( Expression::Symbol('x'), Expression::Symbol('x')); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::sum( Expression::Symbol('x'), Expression::Symbol('x'))); } #[test] fn product_of_integers_is_unchanged() { let input = Expression::product( Expression::Symbol('x'), Expression::Symbol('x')); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::product( Expression::Symbol('x'), Expression::Symbol('x'))); } #[test] fn exponent_of_integers_is_unchanged() { let input = Expression::exponent( Expression::Symbol('x'), Expression::Symbol('x')); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::exponent( Expression::Symbol('x'), Expression::Symbol('x'))); } #[test] fn difference_is_transformed_to_sum() { let input = Expression::difference( Expression::Symbol('x'), Expression::Symbol('x')); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::sum( Expression::Symbol('x'), Expression::product( Expression::Integer(-1), Expression::Symbol('x')))); } #[test] fn quotient_of_symbols_is_transformed_to_product() { let input = Expression::quotient( Expression::Symbol('x'), Expression::Symbol('x')); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::product( Expression::Symbol('x'), Expression::exponent( Expression::Symbol('x'), Expression::Integer(-1)))); } #[test] fn quotient_of_integer_and_symbol_is_transformed_to_product() { let input = Expression::quotient( Expression::Integer(2), Expression::Symbol('x')); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::product( Expression::Integer(2), Expression::exponent( Expression::Symbol('x'), Expression::Integer(-1)))); } #[test] fn quotient_of_symbol_and_integer_is_transformed_to_product() { let input = Expression::quotient( Expression::Symbol('x'), Expression::Integer(2)); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::product( Expression::Symbol('x'), Expression::quotient( Expression::Integer(1), Expression::Integer(2)))); } #[test] fn differences_inside_sum_are_normalised() { let input = Expression::sum( Expression::difference( Expression::Symbol('x'), Expression::Symbol('x')), Expression::difference( Expression::Symbol('x'), Expression::Symbol('x'))); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::sum( Expression::sum( Expression::Symbol('x'), Expression::product( Expression::Integer(-1), Expression::Symbol('x'))), Expression::sum( Expression::Symbol('x'), Expression::product( Expression::Integer(-1), Expression::Symbol('x'))))); } #[test] fn differences_inside_exponent_are_normalised() { let input = Expression::exponent( Expression::difference( Expression::Symbol('x'), Expression::Symbol('x')), Expression::difference( Expression::Symbol('x'), Expression::Symbol('x'))); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::exponent( Expression::sum( Expression::Symbol('x'), Expression::product( Expression::Integer(-1), Expression::Symbol('x'))), Expression::sum( Expression::Symbol('x'), Expression::product( Expression::Integer(-1), Expression::Symbol('x'))))); } #[test] fn differences_inside_difference_are_normalised() { let input = Expression::difference( Expression::difference( Expression::Symbol('x'), Expression::Symbol('x')), Expression::difference( Expression::Symbol('x'), Expression::Symbol('x'))); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::sum( Expression::sum( Expression::Symbol('x'), Expression::product( Expression::Integer(-1), Expression::Symbol('x'))), Expression::product( Expression::Integer(-1), Expression::sum( Expression::Symbol('x'), Expression::product( Expression::Integer(-1), Expression::Symbol('x')))))); } #[test] fn differences_inside_quotient_are_normalised() { let input = Expression::quotient( Expression::difference( Expression::Symbol('x'), Expression::Symbol('x')), Expression::difference( Expression::Symbol('x'), Expression::Symbol('x'))); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::product( Expression::sum( Expression::Symbol('x'), Expression::product( Expression::Integer(-1), Expression::Symbol('x'))), Expression::exponent( Expression::sum( Expression::Symbol('x'), Expression::product( Expression::Integer(-1), Expression::Symbol('x'))), Expression::Integer(-1)))); } #[test] fn quotient_of_integers_is_unchanged() { let input = Expression::quotient( Expression::Integer(1), Expression::Integer(2)); let result = Normaliser::new().eval(input).unwrap(); assert_eq!( result, Expression::quotient( Expression::Integer(1), Expression::Integer(2))); } }