use mathlex::{parse, parse_latex, BinaryOp, ExprKind, Expression, ToLatex, UnaryOp};
fn var(name: &str) -> Expression {
ExprKind::Variable(name.to_string()).into()
}
fn int(n: i64) -> Expression {
ExprKind::Integer(n).into()
}
#[test]
fn test_unary_neg_of_sum() {
let ast: Expression = ExprKind::Unary {
op: UnaryOp::Neg,
operand: Box::new(
ExprKind::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(var("b")),
}
.into(),
),
}
.into();
let s = ast.to_string();
assert!(s.contains("("), "Should have parens: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "-(a+b) should round-trip");
}
#[test]
fn test_unary_neg_of_product() {
let ast: Expression = ExprKind::Unary {
op: UnaryOp::Neg,
operand: Box::new(
ExprKind::Binary {
op: BinaryOp::Mul,
left: Box::new(var("a")),
right: Box::new(var("b")),
}
.into(),
),
}
.into();
let s = ast.to_string();
assert!(s.contains("("), "Should have parens: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "-(a*b) should round-trip");
}
#[test]
fn test_power_left_associativity_needs_parens() {
let ast: Expression = ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(
ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(var("a")),
right: Box::new(var("b")),
}
.into(),
),
right: Box::new(var("c")),
}
.into();
let s = ast.to_string();
assert!(s.contains("("), "Should have parens for (a^b)^c: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "(a^b)^c should round-trip");
}
#[test]
fn test_power_right_associativity_no_parens() {
let ast: Expression = ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(var("a")),
right: Box::new(
ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(var("b")),
right: Box::new(var("c")),
}
.into(),
),
}
.into();
let s = ast.to_string();
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "a^(b^c) should round-trip");
}
#[test]
fn test_sum_in_product_needs_parens() {
let ast: Expression = ExprKind::Binary {
op: BinaryOp::Mul,
left: Box::new(
ExprKind::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(var("b")),
}
.into(),
),
right: Box::new(var("c")),
}
.into();
let s = ast.to_string();
assert!(s.contains("("), "Should have parens: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "(a+b)*c should round-trip");
}
#[test]
fn test_product_in_sum_no_parens() {
let ast: Expression = ExprKind::Binary {
op: BinaryOp::Add,
left: Box::new(
ExprKind::Binary {
op: BinaryOp::Mul,
left: Box::new(var("a")),
right: Box::new(var("b")),
}
.into(),
),
right: Box::new(var("c")),
}
.into();
let s = ast.to_string();
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "a*b+c should round-trip");
}
#[test]
fn test_subtraction_associativity() {
let left_assoc: Expression = ExprKind::Binary {
op: BinaryOp::Sub,
left: Box::new(
ExprKind::Binary {
op: BinaryOp::Sub,
left: Box::new(var("a")),
right: Box::new(var("b")),
}
.into(),
),
right: Box::new(var("c")),
}
.into();
let s1 = left_assoc.to_string();
let parsed1 = parse(&s1).unwrap();
assert_eq!(left_assoc, parsed1, "(a-b)-c should round-trip");
let right_assoc: Expression = ExprKind::Binary {
op: BinaryOp::Sub,
left: Box::new(var("a")),
right: Box::new(
ExprKind::Binary {
op: BinaryOp::Sub,
left: Box::new(var("b")),
right: Box::new(var("c")),
}
.into(),
),
}
.into();
let s2 = right_assoc.to_string();
assert!(s2.contains("("), "a-(b-c) should have parens: {}", s2);
let parsed2 = parse(&s2).unwrap();
assert_eq!(right_assoc, parsed2, "a-(b-c) should round-trip");
}
#[test]
fn test_division_associativity() {
let ast: Expression = ExprKind::Binary {
op: BinaryOp::Div,
left: Box::new(var("a")),
right: Box::new(
ExprKind::Binary {
op: BinaryOp::Div,
left: Box::new(var("b")),
right: Box::new(var("c")),
}
.into(),
),
}
.into();
let s = ast.to_string();
assert!(s.contains("("), "a/(b/c) should have parens: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "a/(b/c) should round-trip");
}
#[test]
fn test_nested_unary() {
let ast: Expression = ExprKind::Unary {
op: UnaryOp::Neg,
operand: Box::new(
ExprKind::Unary {
op: UnaryOp::Neg,
operand: Box::new(var("a")),
}
.into(),
),
}
.into();
let s = ast.to_string();
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "--a should round-trip");
}
#[test]
fn test_complex_precedence_chain() {
let ast: Expression = ExprKind::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(
ExprKind::Binary {
op: BinaryOp::Mul,
left: Box::new(var("b")),
right: Box::new(
ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(var("c")),
right: Box::new(var("d")),
}
.into(),
),
}
.into(),
),
}
.into();
let s = ast.to_string();
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "a + b * c^d should round-trip");
}
#[test]
fn test_latex_unary_neg_of_sum() {
let ast: Expression = ExprKind::Unary {
op: UnaryOp::Neg,
operand: Box::new(
ExprKind::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(var("b")),
}
.into(),
),
}
.into();
let s = ast.to_latex();
assert!(
s.contains("(") || s.contains("\\left"),
"Should have parens: {}",
s
);
let parsed = parse_latex(&s).unwrap();
assert_eq!(ast, parsed, "LaTeX -(a+b) should round-trip");
}
#[test]
fn test_latex_fraction_in_sum() {
let ast: Expression = ExprKind::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(
ExprKind::Binary {
op: BinaryOp::Div,
left: Box::new(int(1)),
right: Box::new(int(2)),
}
.into(),
),
}
.into();
let s = ast.to_latex();
let parsed = parse_latex(&s).unwrap();
assert_eq!(ast, parsed, "LaTeX a + frac should round-trip");
}
#[test]
fn test_latex_power_precedence() {
let ast: Expression = ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(
ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(var("a")),
right: Box::new(var("b")),
}
.into(),
),
right: Box::new(var("c")),
}
.into();
let s = ast.to_latex();
let parsed = parse_latex(&s).unwrap();
assert_eq!(ast, parsed, "LaTeX (a^b)^c should round-trip");
}
#[test]
fn test_latex_mul_precedence() {
let ast: Expression = ExprKind::Binary {
op: BinaryOp::Mul,
left: Box::new(
ExprKind::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(var("b")),
}
.into(),
),
right: Box::new(var("c")),
}
.into();
let s = ast.to_latex();
let parsed = parse_latex(&s).unwrap();
assert_eq!(ast, parsed, "LaTeX (a+b)*c should round-trip");
}