Files
deriv/src/eval/normalise.rs
T

306 lines
7.5 KiB
Rust

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<Expression, Error> {
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<Expression>) -> std::boxed::Box<Expression> {
*b = normalise(*b);
b
}
fn negation(
expr: std::boxed::Box<Expression>) -> std::boxed::Box<Expression> {
let res = Expression::Product(
std::boxed::Box::new(Expression::Integer(-1)), expr);
std::boxed::Box::new(res)
}
fn reciprocal(
expr: std::boxed::Box<Expression>) -> std::boxed::Box<Expression> {
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)));
}
}