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 compose;
|
||||
pub mod differentiate;
|
||||
pub mod simplify;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
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