306 lines
7.5 KiB
Rust
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)));
|
|
}
|
|
}
|