mod iter;
use crate::primitive::{float_from_str, from_str_radix, int, int_from_str};
use cas_parser::parser::{
ast::{expr::Expr as AstExpr, literal::Literal},
token::op::{BinOpKind, Precedence, UnaryOpKind},
};
use iter::ExprIter;
use rug::{Float, Integer};
use std::{
cmp::Ordering,
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
ops::{Add, AddAssign, Mul, MulAssign, Neg},
};
use super::simplify::fraction::make_fraction;
#[derive(Debug, Clone, PartialEq)]
pub enum Primary {
Integer(Integer),
Float(Float),
Symbol(String),
Call(String, Vec<SymExpr>),
}
impl Hash for Primary {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Self::Integer(int) => int.hash(state),
Self::Float(float) => float.get_significand().unwrap().hash(state),
Self::Symbol(sym) => sym.hash(state),
Self::Call(name, args) => {
name.hash(state);
args.hash(state);
}
}
}
}
impl std::fmt::Display for Primary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Integer(num) => write!(f, "{}", num.to_f64()),
Self::Float(num) => write!(f, "{}", num.to_f64()),
Self::Symbol(sym) => write!(f, "{}", sym),
Self::Call(name, args) => {
write!(f, "{}(", name)?;
let mut iter = args.iter();
if let Some(arg) = iter.next() {
write!(f, "{}", arg)?;
for arg in iter {
write!(f, ", {}", arg)?;
}
}
write!(f, ")")
},
}
}
}
impl Eq for Primary {}
impl Add<Primary> for Primary {
type Output = SymExpr;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Primary::Integer(lhs), Primary::Integer(rhs)) => {
SymExpr::Primary(Primary::Integer(lhs + rhs))
},
(Primary::Float(lhs), Primary::Float(rhs)) => {
SymExpr::Primary(Primary::Float(lhs + rhs))
},
(lhs, rhs) => SymExpr::Add(vec![
SymExpr::Primary(lhs),
SymExpr::Primary(rhs),
]),
}
}
}
impl Mul<Primary> for Primary {
type Output = SymExpr;
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Primary::Integer(lhs), Primary::Integer(rhs)) => {
SymExpr::Primary(Primary::Integer(lhs * rhs))
},
(Primary::Float(lhs), Primary::Float(rhs)) => {
SymExpr::Primary(Primary::Float(lhs * rhs))
},
(lhs, rhs) => SymExpr::Mul(vec![
SymExpr::Primary(lhs),
SymExpr::Primary(rhs),
]),
}
}
}
#[derive(Debug, Clone, Eq)]
pub enum SymExpr {
Primary(Primary),
Add(Vec<SymExpr>),
Mul(Vec<SymExpr>),
Exp(Box<SymExpr>, Box<SymExpr>),
}
impl std::fmt::Display for SymExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Primary(primary) => write!(f, "{}", primary),
Self::Add(terms) => {
let mut iter = terms.iter();
if let Some(term) = iter.next() {
write!(f, "{}", term)?;
for term in iter {
write!(f, " + {}", term)?;
}
}
Ok(())
},
Self::Mul(factors) => {
let mut iter = factors.iter();
if let Some(factor) = iter.next() {
if matches!(factor.cmp_precedence(self), Ordering::Less) {
write!(f, "({})", factor)?;
} else {
write!(f, "{}", factor)?;
}
for factor in iter {
if matches!(factor.cmp_precedence(self), Ordering::Less) {
write!(f, " * ({})", factor)?;
} else {
write!(f, " * {}", factor)?;
}
}
}
Ok(())
},
Self::Exp(base, exp) => {
if matches!(base.cmp_precedence(self), Ordering::Less) {
write!(f, "({})", base)?;
} else {
write!(f, "{}", base)?;
}
write!(f, "^")?;
if matches!(exp.cmp_precedence(self), Ordering::Less) {
write!(f, "({})", exp)?;
} else {
write!(f, "{}", exp)?;
}
Ok(())
},
}
}
}
impl SymExpr {
fn precedence(&self) -> Option<Precedence> {
match self {
Self::Primary(_) => None,
Self::Add(_) => Some(BinOpKind::Add.precedence()),
Self::Mul(_) => Some(BinOpKind::Mul.precedence()),
Self::Exp(_, _) => Some(BinOpKind::Exp.precedence()),
}
}
pub fn cmp_precedence(&self, other: &Self) -> Ordering {
#[derive(PartialEq, Eq)]
enum PrecedenceExt {
Primary,
Op(Precedence),
}
impl PartialOrd for PrecedenceExt {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for PrecedenceExt {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Self::Primary, Self::Primary) => Ordering::Equal,
(Self::Primary, Self::Op(_)) => Ordering::Greater,
(Self::Op(_), Self::Primary) => Ordering::Less,
(Self::Op(lhs), Self::Op(rhs)) => lhs.cmp(rhs),
}
}
}
let lhs = self.precedence().map(PrecedenceExt::Op).unwrap_or(PrecedenceExt::Primary);
let rhs = other.precedence().map(PrecedenceExt::Op).unwrap_or(PrecedenceExt::Primary);
lhs.cmp(&rhs)
}
pub fn as_integer(&self) -> Option<&Integer> {
match self {
Self::Primary(Primary::Integer(int)) => Some(int),
_ => None,
}
}
pub fn into_integer(self) -> Option<Integer> {
match self {
Self::Primary(Primary::Integer(int)) => Some(int),
_ => None,
}
}
pub fn is_integer(&self) -> bool {
matches!(self, Self::Primary(Primary::Integer(_)))
}
pub fn is_integer_recip(&self) -> bool {
if let Self::Exp(base, exp) = self {
if matches!(&**base, Self::Primary(Primary::Integer(_))) {
if let Self::Primary(Primary::Integer(exp)) = &**exp {
return exp == &-1;
}
}
}
false
}
pub fn as_integer_recip(&self) -> Option<&Integer> {
if let Self::Exp(base, exp) = self {
if matches!(&**base, Self::Primary(Primary::Integer(_))) {
if let Self::Primary(Primary::Integer(exp)) = &**exp {
if exp == &-1 {
return base.as_integer();
}
}
}
}
None
}
pub fn into_integer_recip(self) -> Option<Integer> {
if let Self::Exp(base, exp) = self {
if matches!(*base, Self::Primary(Primary::Integer(_))) {
if let Self::Primary(Primary::Integer(exp)) = *exp {
if exp == -1 {
return base.into_integer();
}
}
}
}
None
}
pub fn is_float(&self) -> bool {
matches!(self, Self::Primary(Primary::Float(_)))
}
pub fn as_symbol(&self) -> Option<&str> {
match self {
Self::Primary(Primary::Symbol(sym)) => Some(sym),
_ => None,
}
}
pub(crate) fn downgrade(self) -> Self {
match self {
Self::Add(mut terms) => {
if terms.is_empty() {
Self::Primary(Primary::Integer(int(0)))
} else if terms.len() == 1 {
terms.remove(0)
} else {
Self::Add(terms)
}
},
Self::Mul(mut factors) => {
if factors.is_empty() {
Self::Primary(Primary::Integer(int(1)))
} else if factors.len() == 1 {
factors.remove(0)
} else {
Self::Mul(factors)
}
},
_ => self,
}
}
pub fn sqrt(self) -> Self {
Self::Exp(
Box::new(self),
Box::new(make_fraction(
Self::Primary(Primary::Integer(int(1))),
Self::Primary(Primary::Integer(int(2))),
)),
)
}
pub fn post_order_iter(&self) -> ExprIter {
ExprIter::new(self)
}
}
impl PartialEq for SymExpr {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Primary(lhs), Self::Primary(rhs)) => lhs == rhs,
(Self::Add(_), Self::Add(_)) | (Self::Mul(_), Self::Mul(_)) => {
let lhs_hash = {
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
hasher.finish()
};
let rhs_hash = {
let mut hasher = DefaultHasher::new();
other.hash(&mut hasher);
hasher.finish()
};
lhs_hash == rhs_hash
},
(Self::Exp(lhs_base, lhs_exp), Self::Exp(rhs_base, rhs_exp)) => {
lhs_base == rhs_base && lhs_exp == rhs_exp
},
_ => false,
}
}
}
impl Hash for SymExpr {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
SymExpr::Primary(val) => val.hash(state),
SymExpr::Add(val) | SymExpr::Mul(val) => {
let out: u64 = val
.iter()
.map(|expr| {
let mut hasher = DefaultHasher::new();
expr.hash(&mut hasher);
hasher.finish()
})
.fold(0, |acc, curr| acc.wrapping_add(curr));
out.hash(state);
}
SymExpr::Exp(val_base, val_exp) => {
val_base.hash(state);
val_exp.hash(state);
}
}
}
}
impl From<AstExpr> for SymExpr {
fn from(expr: AstExpr) -> Self {
match expr {
AstExpr::Literal(literal) => match literal {
Literal::Integer(int) => Self::Primary(Primary::Integer(int_from_str(&int.value))),
Literal::Float(float) => Self::Primary(Primary::Float(float_from_str(&float.value))),
Literal::Radix(radix) => Self::Primary(Primary::Integer(from_str_radix(&radix.value, radix.base))),
Literal::Boolean(_) => todo!(),
Literal::Symbol(sym) => Self::Primary(Primary::Symbol(sym.name)),
Literal::Unit(_) => todo!(),
Literal::List(_) => todo!(),
Literal::ListRepeat(_) => todo!(),
},
AstExpr::Paren(paren) => Self::from(paren.into_innermost()),
AstExpr::Block(_) => todo!(),
AstExpr::Sum(_) => todo!(),
AstExpr::Product(_) => todo!(),
AstExpr::If(_) => todo!(),
AstExpr::Loop(_) => todo!(),
AstExpr::While(_) => todo!(),
AstExpr::For(_) => todo!(),
AstExpr::Then(_) => todo!(),
AstExpr::Of(_) => todo!(),
AstExpr::Break(_) => todo!(),
AstExpr::Continue(_) => todo!(),
AstExpr::Return(_) => todo!(),
AstExpr::Call(call) => {
let args = call.args.into_iter().map(Self::from).collect();
Self::Primary(Primary::Call(call.name.name, args))
},
AstExpr::Index(_) => todo!(),
AstExpr::Unary(unary) => {
match unary.op.kind {
UnaryOpKind::Neg => {
Self::from(*unary.operand).neg()
},
_ => todo!(),
}
},
AstExpr::Binary(bin) => {
match bin.op.kind {
BinOpKind::Exp => {
Self::Exp(Box::new(Self::from(*bin.lhs)), Box::new(Self::from(*bin.rhs)))
},
BinOpKind::Mul => {
let mut factors = Self::Mul(Vec::new());
let mut stack = vec![AstExpr::Binary(bin)];
while let Some(bin) = stack.pop() {
match bin {
AstExpr::Binary(bin) => {
if bin.op.kind == BinOpKind::Mul {
stack.push(*bin.lhs);
stack.push(*bin.rhs);
} else {
factors *= Self::from(AstExpr::Binary(bin));
}
},
expr => {
factors *= Self::from(expr);
},
}
}
factors
},
BinOpKind::Div => {
make_fraction(
Self::from(*bin.lhs),
Self::from(*bin.rhs),
)
},
BinOpKind::Mod => todo!(),
BinOpKind::Add => {
let mut terms = Self::Add(Vec::new());
let mut stack = vec![AstExpr::Binary(bin)];
while let Some(bin) = stack.pop() {
match bin {
AstExpr::Binary(bin) => {
if bin.op.kind == BinOpKind::Add {
stack.push(*bin.lhs);
stack.push(*bin.rhs);
} else {
terms += Self::from(AstExpr::Binary(bin));
}
},
_ => {
terms += Self::from(bin);
},
}
}
terms
},
BinOpKind::Sub => {
Self::from(*bin.lhs) +
Self::from(*bin.rhs).neg()
},
BinOpKind::BitRight => todo!(),
BinOpKind::BitLeft => todo!(),
BinOpKind::BitAnd => todo!(),
BinOpKind::BitOr => todo!(),
BinOpKind::Greater => todo!(),
BinOpKind::GreaterEq => todo!(),
BinOpKind::Less => todo!(),
BinOpKind::LessEq => todo!(),
BinOpKind::Eq => todo!(),
BinOpKind::NotEq => todo!(),
BinOpKind::ApproxEq => todo!(),
BinOpKind::ApproxNotEq => todo!(),
BinOpKind::And => todo!(),
BinOpKind::Or => todo!(),
}
},
AstExpr::Assign(_) => todo!(),
AstExpr::Range(_) => todo!(),
}
}
}
impl From<SymExpr> for AstExpr {
fn from(expr: SymExpr) -> Self {
use cas_parser::parser::{
ast::{Binary, Call, LitFloat, LitInt, LitSym},
token::op::BinOp,
};
match expr {
SymExpr::Primary(primary) => match primary {
Primary::Integer(int) => AstExpr::Literal(Literal::Integer(LitInt {
value: int.to_string(),
span: 0..0, })),
Primary::Float(float) => AstExpr::Literal(Literal::Float(LitFloat {
value: float.to_string(),
span: 0..0,
})),
Primary::Symbol(sym) => AstExpr::Literal(Literal::Symbol(LitSym {
name: sym,
span: 0..0,
})),
Primary::Call(name, args) => AstExpr::Call(Call {
name: LitSym { name, span: 0..0 },
derivatives: 0,
args: args.into_iter().map(Self::from).collect(),
span: 0..0,
paren_span: 0..0,
}),
},
SymExpr::Add(terms) => {
let mut iter = terms.into_iter();
let mut expr = Self::from(iter.next().unwrap());
for term in iter {
expr = AstExpr::Binary(Binary {
lhs: Box::new(expr),
op: BinOp {
kind: BinOpKind::Add,
implicit: false,
span: 0..0,
},
rhs: Box::new(Self::from(term)),
span: 0..0,
});
}
expr
},
SymExpr::Mul(factors) => {
let mut iter = factors.into_iter();
let mut expr = Self::from(iter.next().unwrap());
for factor in iter {
expr = AstExpr::Binary(Binary {
lhs: Box::new(expr),
op: BinOp {
kind: BinOpKind::Mul,
implicit: false,
span: 0..0,
},
rhs: Box::new(Self::from(factor)),
span: 0..0,
});
}
expr
},
SymExpr::Exp(lhs, rhs) => AstExpr::Binary(Binary {
lhs: Box::new(Self::from(*lhs)),
op: BinOp {
kind: BinOpKind::Exp,
implicit: false,
span: 0..0,
},
rhs: Box::new(Self::from(*rhs)),
span: 0..0,
}),
}
}
}
impl Add for SymExpr {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Primary(lhs), Self::Primary(rhs)) => lhs + rhs,
(Self::Add(mut terms), Self::Add(rhs_terms)) => {
terms.extend(rhs_terms);
Self::Add(terms)
},
(Self::Add(mut terms), other) | (other, Self::Add(mut terms)) => {
terms.push(other);
Self::Add(terms)
},
(lhs, rhs) => Self::Add(vec![lhs, rhs]),
}
}
}
impl AddAssign for SymExpr {
fn add_assign(&mut self, rhs: Self) {
match (self, rhs) {
(Self::Primary(Primary::Integer(lhs)), Self::Primary(Primary::Integer(rhs))) => {
*lhs += rhs;
},
(Self::Primary(Primary::Float(lhs)), Self::Primary(Primary::Float(rhs))) => {
*lhs += rhs;
},
(Self::Add(terms), Self::Add(rhs_terms)) => {
terms.extend(rhs_terms);
},
(Self::Add(terms), other) => {
terms.push(other);
},
(other, Self::Add(mut terms)) => {
unsafe {
let owned = std::ptr::read(other);
terms.push(owned);
std::ptr::write(other, Self::Add(terms));
}
},
(lhs, rhs) => {
unsafe {
let owned = std::ptr::read(lhs);
std::ptr::write(lhs, Self::Add(vec![owned, rhs]));
}
},
}
}
}
impl Mul for SymExpr {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
match (self, rhs) {
(Self::Primary(lhs), Self::Primary(rhs)) => lhs * rhs,
(Self::Mul(mut factors), Self::Mul(other)) => {
factors.extend(other);
Self::Mul(factors)
},
(Self::Mul(mut factors), other) | (other, Self::Mul(mut factors)) => {
factors.push(other);
Self::Mul(factors)
},
(lhs, rhs) => Self::Mul(vec![lhs, rhs]),
}
}
}
impl MulAssign for SymExpr {
fn mul_assign(&mut self, rhs: Self) {
match (self, rhs) {
(Self::Primary(Primary::Integer(lhs)), Self::Primary(Primary::Integer(rhs))) => {
*lhs *= rhs;
},
(Self::Primary(Primary::Float(lhs)), Self::Primary(Primary::Float(rhs))) => {
*lhs *= rhs;
},
(Self::Mul(factors), Self::Mul(rhs_factors)) => {
factors.extend(rhs_factors);
},
(Self::Mul(factors), other) => {
factors.push(other);
},
(other, Self::Mul(mut factors)) => {
unsafe {
let owned = std::ptr::read(other);
factors.push(owned);
std::ptr::write(other, Self::Mul(factors));
}
},
(lhs, rhs) => {
unsafe {
let owned = std::ptr::read(lhs);
std::ptr::write(lhs, Self::Mul(vec![owned, rhs]));
}
},
}
}
}
impl Neg for SymExpr {
type Output = Self;
fn neg(self) -> Self::Output {
match self {
Self::Primary(Primary::Integer(int)) => Self::Primary(Primary::Integer(-int)),
Self::Primary(Primary::Float(float)) => Self::Primary(Primary::Float(-float)),
expr => Self::Primary(Primary::Integer(int(-1))) * expr,
}
}
}
#[cfg(test)]
mod tests {
use cas_parser::parser::{ast::expr::Expr as AstExpr, Parser};
use pretty_assertions::assert_eq;
use crate::symbolic::simplify;
use super::*;
fn parse_expr(input: &str) -> SymExpr {
let expr = Parser::new(input).try_parse_full::<AstExpr>().unwrap();
SymExpr::from(expr)
}
fn hash(expr: &SymExpr) -> u64 {
let mut hasher = DefaultHasher::new();
expr.hash(&mut hasher);
hasher.finish()
}
#[test]
fn strict_equality() {
let a = parse_expr("2(x + (y - 5))");
let b = parse_expr("(y - 5 + x) * 2");
assert_eq!(a, b);
}
#[test]
fn strict_equality_2() {
let a = parse_expr("2(x + (y - 5))");
let b = parse_expr("2x + 2y - 10");
assert_ne!(a, b);
}
#[test]
fn simple_expr() {
let expr = parse_expr("x^2 + 5x + 6");
assert_eq!(expr, SymExpr::Add(vec![
SymExpr::Primary(Primary::Integer(int(6))),
SymExpr::Mul(vec![
SymExpr::Primary(Primary::Symbol(String::from("x"))),
SymExpr::Primary(Primary::Integer(int(5))),
]),
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Symbol(String::from("x")))),
Box::new(SymExpr::Primary(Primary::Integer(int(2)))),
),
]));
}
#[test]
fn factors_only() {
let expr = parse_expr("-2x^2y^-3/5");
assert_eq!(expr, SymExpr::Mul(vec![
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Symbol(String::from("y")))),
Box::new(SymExpr::Primary(Primary::Integer(int(-3)))),
),
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Symbol(String::from("x")))),
Box::new(SymExpr::Primary(Primary::Integer(int(2)))),
),
SymExpr::Primary(Primary::Integer(int(-2))),
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Integer(int(5)))),
Box::new(SymExpr::Primary(Primary::Integer(int(-1)))),
),
]));
}
#[test]
fn complicated_expr() {
let expr = parse_expr("3x - (x+t)y - z*a^(1/5/6)*b");
assert_eq!(expr, SymExpr::Add(vec![
SymExpr::Mul(vec![
SymExpr::Primary(Primary::Symbol(String::from("x"))),
SymExpr::Primary(Primary::Integer(int(3))),
]),
SymExpr::Mul(vec![
SymExpr::Primary(Primary::Symbol(String::from("y"))),
SymExpr::Add(vec![
SymExpr::Primary(Primary::Symbol(String::from("t"))),
SymExpr::Primary(Primary::Symbol(String::from("x"))),
]),
SymExpr::Primary(Primary::Integer(int(-1))),
]),
SymExpr::Mul(vec![
SymExpr::Primary(Primary::Symbol(String::from("b"))),
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Symbol(String::from("a")))),
Box::new(SymExpr::Mul(vec![
SymExpr::Primary(Primary::Integer(int(1))),
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Integer(int(5)))),
Box::new(SymExpr::Primary(Primary::Integer(int(-1)))),
),
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Integer(int(6)))),
Box::new(SymExpr::Primary(Primary::Integer(int(-1)))),
),
])),
),
SymExpr::Primary(Primary::Symbol(String::from("z"))),
SymExpr::Primary(Primary::Integer(int(-1))),
]),
]));
}
#[test]
fn complicated_expr_2() {
let expr = parse_expr("3x^2y - 16x y + 2x^2y - 13x y + 4x y^2 - 11x y^2");
assert_eq!(expr, SymExpr::Add(vec![
SymExpr::Mul(vec![
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Symbol(String::from("y")))),
Box::new(SymExpr::Primary(Primary::Integer(int(2)))),
),
SymExpr::Primary(Primary::Symbol(String::from("x"))),
SymExpr::Primary(Primary::Integer(int(4))),
]),
SymExpr::Mul(vec![
SymExpr::Primary(Primary::Symbol(String::from("y"))),
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Symbol(String::from("x")))),
Box::new(SymExpr::Primary(Primary::Integer(int(2)))),
),
SymExpr::Primary(Primary::Integer(int(2))),
]),
SymExpr::Mul(vec![
SymExpr::Primary(Primary::Symbol(String::from("y"))),
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Symbol(String::from("x")))),
Box::new(SymExpr::Primary(Primary::Integer(int(2)))),
),
SymExpr::Primary(Primary::Integer(int(3))),
]),
SymExpr::Mul(vec![
SymExpr::Primary(Primary::Symbol(String::from("y"))),
SymExpr::Primary(Primary::Symbol(String::from("x"))),
SymExpr::Primary(Primary::Integer(int(16))),
SymExpr::Primary(Primary::Integer(int(-1))),
]),
SymExpr::Mul(vec![
SymExpr::Primary(Primary::Symbol(String::from("y"))),
SymExpr::Primary(Primary::Symbol(String::from("x"))),
SymExpr::Primary(Primary::Integer(int(13))),
SymExpr::Primary(Primary::Integer(int(-1))),
]),
SymExpr::Mul(vec![
SymExpr::Exp(
Box::new(SymExpr::Primary(Primary::Symbol(String::from("y")))),
Box::new(SymExpr::Primary(Primary::Integer(int(2)))),
),
SymExpr::Primary(Primary::Symbol(String::from("x"))),
SymExpr::Primary(Primary::Integer(int(11))),
SymExpr::Primary(Primary::Integer(int(-1))),
]),
]));
}
#[test]
fn same_symbols_not_eq() {
let expr_a = parse_expr("x * x");
let expr_b = parse_expr("-x");
assert_ne!(expr_a, expr_b);
assert_ne!(expr_b, expr_a);
let expr_c = parse_expr("x * x");
let expr_d = parse_expr("x");
assert_ne!(expr_c, expr_d);
assert_ne!(expr_d, expr_c);
}
#[test]
fn fmt_expr() {
let expr = parse_expr("8a^73b sqrt(2634*a*b)");
assert_eq!(expr.to_string(), "sqrt(b * a * 2634) * b * a^73 * 8");
}
#[test]
fn fmt_expr_2() {
let expr = parse_expr("(((((((((a) b) c) d) e + f) g) h) i) j)");
assert_eq!(expr.to_string(), "j * i * h * g * (f + e * d * c * b * a)");
}
#[test]
fn commutative_hashing() {
let exprs = vec![
("1 + 2", "2 + 1"),
("x + y", "y + x"),
("1 + x", "x + 1"),
("a + b + c", "c + b + a"),
("(x + y) + z", "z + (y + x)"),
("1 * 2", "2 * 1"),
("x * y", "y * x"),
("2 * x * y", "y * x * 2"),
("a * b * c", "c * b * a"),
("(x * y) * z", "z * (y * x)"),
("x * (y + z)", "(y + z) * x"),
("a * (b + c)", "(b + c) * a"),
("1 + (x * y)", "(y * x) + 1"),
("1 2", "2 1"),
("x y", "y x"),
("2 x y", "y x 2"),
("a b c", "c b a"),
("(x y) z", "z * (y x)"),
("1 + (x y)", "(y x) + 1"),
];
for (ea, eb) in exprs {
let a = parse_expr(ea);
let b = parse_expr(eb);
assert_eq!(hash(&a), hash(&b), "a: {:?}, b: {:?}", a, b);
}
}
#[test]
fn non_commutative_hashing() {
let exprs = vec![
("1 - 2", "2 - 1"),
("x / y", "y / x"),
("a - b + c", "c + b - a"),
("1 + 1", "2 + 2"),
("a * a", "b * b")
];
for (nea, neb) in exprs {
let a = parse_expr(nea);
let b = parse_expr(neb);
assert_ne!(hash(&a), hash(&b), "a: {:?}, b: {:?}", a, b);
}
}
}