Create basic simplifier
This commit is contained in:
1 parent
ce336c2bbb
commit
f2135cc3c5
2 files changed
+280
No files matched your search
@@ -2,6 +2,7 @@ pub mod normalise;
|
|||||||
pub mod const_fold;
|
pub mod const_fold;
|
||||||
pub mod compose;
|
pub mod compose;
|
||||||
pub mod differentiate;
|
pub mod differentiate;
|
||||||
|
pub mod simplify;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
|||||||
@@ -0,0 +1,279 @@
|
|||||||
|
use crate::expression::Expression;
|
||||||
|
use crate::eval::{Error, Evaluator};
|
||||||
|
|
||||||
|
pub struct Simplifier;
|
||||||
|
|
||||||
|
impl Simplifier {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Evaluator for Simplifier {
|
||||||
|
type Expr = Expression;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn eval(&mut self, input: Expression) -> Result<Expression, Error> {
|
||||||
|
Ok(simplify(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simplify(expr: Expression) -> Expression {
|
||||||
|
match expr {
|
||||||
|
Expression::Sum(x, y) => recurse(sum, x, y),
|
||||||
|
Expression::Product(x, y) => recurse(product, x, y),
|
||||||
|
Expression::Exponent(x, y) => recurse(exponent, x, y),
|
||||||
|
_ => expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum(
|
||||||
|
x: std::boxed::Box<Expression>,
|
||||||
|
y: std::boxed::Box<Expression>)
|
||||||
|
-> Expression
|
||||||
|
{
|
||||||
|
if y.integer() == Some(0) {
|
||||||
|
*x
|
||||||
|
} else if x.integer() == Some(0) {
|
||||||
|
*y
|
||||||
|
} else {
|
||||||
|
Expression::Sum(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn product(
|
||||||
|
x: std::boxed::Box<Expression>,
|
||||||
|
y: std::boxed::Box<Expression>)
|
||||||
|
-> Expression
|
||||||
|
{
|
||||||
|
if y.integer() == Some(0) || x.integer() == Some(0) {
|
||||||
|
Expression::Integer(0)
|
||||||
|
} else if y.integer() == Some(1) {
|
||||||
|
*x
|
||||||
|
} else if x.integer() == Some(1) {
|
||||||
|
*y
|
||||||
|
} else {
|
||||||
|
Expression::Product(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exponent(
|
||||||
|
base: std::boxed::Box<Expression>,
|
||||||
|
power: std::boxed::Box<Expression>)
|
||||||
|
-> Expression
|
||||||
|
{
|
||||||
|
if power.integer() == Some(0) {
|
||||||
|
Expression::Integer(1)
|
||||||
|
} else if base.integer() == Some(0) {
|
||||||
|
Expression::Integer(0)
|
||||||
|
} else if power.integer() == Some(1) || base.integer() == Some(1) {
|
||||||
|
*base
|
||||||
|
} else {
|
||||||
|
Expression::Exponent(base, power)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recurse(
|
||||||
|
f: impl Fn(
|
||||||
|
std::boxed::Box<Expression>,
|
||||||
|
std::boxed::Box<Expression>) -> Expression,
|
||||||
|
x: std::boxed::Box<Expression>,
|
||||||
|
y: std::boxed::Box<Expression>)
|
||||||
|
-> Expression
|
||||||
|
{
|
||||||
|
f(boxed(x), boxed(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boxed(
|
||||||
|
mut expr: std::boxed::Box<Expression>) -> std::boxed::Box<Expression>
|
||||||
|
{
|
||||||
|
*expr = simplify(*expr);
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn integer_is_unchanged() {
|
||||||
|
let input = Expression::Integer(1);
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Integer(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn symbol_is_unchanged() {
|
||||||
|
let input = Expression::Symbol('x');
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Symbol('x'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sum_of_symbol_with_zero_reduces() {
|
||||||
|
let input = Expression::sum(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(0));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Symbol('x'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sum_of_zero_with_symbol_reduces() {
|
||||||
|
let input = Expression::sum(
|
||||||
|
Expression::Integer(0),
|
||||||
|
Expression::Symbol('x'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Symbol('x'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sum_of_non_zero_integer_with_symbol_is_unchanged() {
|
||||||
|
let input = Expression::sum(
|
||||||
|
Expression::Integer(1),
|
||||||
|
Expression::Symbol('x'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::sum(
|
||||||
|
Expression::Integer(1),
|
||||||
|
Expression::Symbol('x')));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_sum_with_zero_is_reduced() {
|
||||||
|
let input = Expression::sum(
|
||||||
|
Expression::sum(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(0)),
|
||||||
|
Expression::Symbol('y'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
Expression::sum(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Symbol('y')));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn product_of_symbol_with_zero_becomes_zero() {
|
||||||
|
let input = Expression::product(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(0));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Integer(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn product_of_zero_with_symbol_becomes_zero() {
|
||||||
|
let input = Expression::product(
|
||||||
|
Expression::Integer(0),
|
||||||
|
Expression::Symbol('x'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Integer(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn product_of_symbol_with_one_reduces() {
|
||||||
|
let input = Expression::product(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(1));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Symbol('x'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn product_of_one_with_symbol_reduces() {
|
||||||
|
let input = Expression::product(
|
||||||
|
Expression::Integer(1),
|
||||||
|
Expression::Symbol('x'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Symbol('x'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn product_of_two_with_symbol_is_unchanged() {
|
||||||
|
let input = Expression::product(
|
||||||
|
Expression::Integer(2),
|
||||||
|
Expression::Symbol('x'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::product(
|
||||||
|
Expression::Integer(2),
|
||||||
|
Expression::Symbol('x')));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_product_with_one_is_reduced() {
|
||||||
|
let input = Expression::product(
|
||||||
|
Expression::product(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(1)),
|
||||||
|
Expression::Symbol('y'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
Expression::product(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Symbol('y')));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exponent_of_symbol_with_zero_becomes_one() {
|
||||||
|
let input = Expression::exponent(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(0));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Integer(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exponent_of_zero_with_symbol_becomes_zero() {
|
||||||
|
let input = Expression::exponent(
|
||||||
|
Expression::Integer(0),
|
||||||
|
Expression::Symbol('x'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Integer(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exponent_of_symbol_with_one_reduces() {
|
||||||
|
let input = Expression::exponent(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(1));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Symbol('x'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exponent_of_one_with_symbol_becomes_one() {
|
||||||
|
let input = Expression::exponent(
|
||||||
|
Expression::Integer(1),
|
||||||
|
Expression::Symbol('x'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::Integer(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exponent_of_symbol_with_two_is_unchanged() {
|
||||||
|
let input = Expression::exponent(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(2));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(result, Expression::exponent(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_exponent_with_one_is_reduced() {
|
||||||
|
let input = Expression::exponent(
|
||||||
|
Expression::exponent(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Integer(1)),
|
||||||
|
Expression::Symbol('y'));
|
||||||
|
let result = Simplifier::new().eval(input).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
Expression::exponent(
|
||||||
|
Expression::Symbol('x'),
|
||||||
|
Expression::Symbol('y')));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user