Create basic simplifier

This commit is contained in:
cdo committed 2026-09-26 13:19:28 +01:00
1 parent ce336c2bbb
commit f2135cc3c5
2 files changed
+280

No files matched your search

+1
View File
@@ -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 {
+279
View File
@@ -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')));
}
}