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::Normaliser::new(),
|
||||||
eval::Differentiator::new('x'),
|
eval::Differentiator::new('x'),
|
||||||
eval::ConstFolder::new(),
|
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);
|
let mut repl = Repl::new(std::io::stdin(), std::io::stdout(), eval);
|
||||||
repl.run()
|
repl.run()
|
||||||
}
|
}
|
||||||
@@ -3,12 +3,14 @@ pub mod const_fold;
|
|||||||
pub mod compose;
|
pub mod compose;
|
||||||
pub mod differentiate;
|
pub mod differentiate;
|
||||||
pub mod simplify;
|
pub mod simplify;
|
||||||
|
pub mod canonicalise;
|
||||||
|
|
||||||
pub use normalise::*;
|
pub use normalise::*;
|
||||||
pub use const_fold::*;
|
pub use const_fold::*;
|
||||||
pub use compose::*;
|
pub use compose::*;
|
||||||
pub use differentiate::*;
|
pub use differentiate::*;
|
||||||
pub use simplify::*;
|
pub use simplify::*;
|
||||||
|
pub use canonicalise::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Error {
|
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