Add canonicalisation pass
This commit is contained in:
1 parent
f22e916e0c
commit
c4eca9defe
3 files changed
+146
-1
No files matched your search
+4
-1
@@ -6,7 +6,10 @@ fn main() -> std::io::Result<()> {
|
||||
eval::Normaliser::new(),
|
||||
eval::Differentiator::new('x'),
|
||||
eval::ConstFolder::new(),
|
||||
eval::Simplifier::new());
|
||||
eval::Simplifier::new(),
|
||||
eval::ConstFolder::new(),
|
||||
eval::Canonicaliser::new()
|
||||
);
|
||||
let mut repl = Repl::new(std::io::stdin(), std::io::stdout(), eval);
|
||||
repl.run()
|
||||
}
|
||||
@@ -3,12 +3,14 @@ pub mod const_fold;
|
||||
pub mod compose;
|
||||
pub mod differentiate;
|
||||
pub mod simplify;
|
||||
pub mod canonicalise;
|
||||
|
||||
pub use normalise::*;
|
||||
pub use const_fold::*;
|
||||
pub use compose::*;
|
||||
pub use differentiate::*;
|
||||
pub use simplify::*;
|
||||
pub use canonicalise::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
use crate::expression::Expression;
|
||||
use crate::eval::{Error, Evaluator};
|
||||
|
||||
pub struct Canonicaliser;
|
||||
|
||||
impl Canonicaliser {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluator for Canonicaliser {
|
||||
type Expr = Expression;
|
||||
type Error = Error;
|
||||
|
||||
fn eval(&mut self, input: Expression) -> Result<Expression, Error> {
|
||||
Ok(canonicalise(input))
|
||||
}
|
||||
}
|
||||
|
||||
fn canonicalise(expr: Expression) -> Expression {
|
||||
match expr {
|
||||
Expression::Sum(x, y) => recurse(Expression::Sum, x, y),
|
||||
Expression::Product(x, y) => recurse(product, x, y),
|
||||
Expression::Exponent(x, y) => recurse(Expression::Exponent, x, y),
|
||||
_ => expr
|
||||
}
|
||||
}
|
||||
|
||||
fn product(
|
||||
x: std::boxed::Box<Expression>,
|
||||
y: std::boxed::Box<Expression>)
|
||||
-> Expression
|
||||
{
|
||||
if let Some(_) = y.integer() {
|
||||
Expression::Product(y, x)
|
||||
} else {
|
||||
Expression::Product(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
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 = canonicalise(*expr);
|
||||
expr
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn product_integer_symbol_is_unchanged() {
|
||||
let input = Expression::product(
|
||||
Expression::Integer(1),
|
||||
Expression::Symbol('x'));
|
||||
let result = Canonicaliser::new().eval(input).unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
Expression::product(
|
||||
Expression::Integer(1),
|
||||
Expression::Symbol('x')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn product_symbol_integer_becomes_product_integer_symbol() {
|
||||
let input = Expression::product(
|
||||
Expression::Symbol('x'),
|
||||
Expression::Integer(1));
|
||||
let result = Canonicaliser::new().eval(input).unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
Expression::product(
|
||||
Expression::Integer(1),
|
||||
Expression::Symbol('x')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn product_symbol_integer_nested_in_sum_is_reordered() {
|
||||
let input = Expression::sum(
|
||||
Expression::Integer(1),
|
||||
Expression::product(
|
||||
Expression::Symbol('x'),
|
||||
Expression::Integer(1)));
|
||||
let result = Canonicaliser::new().eval(input).unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
Expression::sum(
|
||||
Expression::Integer(1),
|
||||
Expression::product(
|
||||
Expression::Integer(1),
|
||||
Expression::Symbol('x'))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn product_symbol_integer_nested_in_product_is_reordered() {
|
||||
let input = Expression::product(
|
||||
Expression::Integer(1),
|
||||
Expression::product(
|
||||
Expression::Symbol('x'),
|
||||
Expression::Integer(1)));
|
||||
let result = Canonicaliser::new().eval(input).unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
Expression::product(
|
||||
Expression::Integer(1),
|
||||
Expression::product(
|
||||
Expression::Integer(1),
|
||||
Expression::Symbol('x'))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exponent_symbol_integer_nested_in_exponent_is_reordered() {
|
||||
let input = Expression::exponent(
|
||||
Expression::Integer(1),
|
||||
Expression::product(
|
||||
Expression::Symbol('x'),
|
||||
Expression::Integer(1)));
|
||||
let result = Canonicaliser::new().eval(input).unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
Expression::exponent(
|
||||
Expression::Integer(1),
|
||||
Expression::product(
|
||||
Expression::Integer(1),
|
||||
Expression::Symbol('x'))));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user