use oxieml::EmlTree;
use scirs2_symbolic::cas::{canonicalize_egraph, SaturationBudget};
use scirs2_symbolic::eml::{to_latex, LoweredOp as SOp};
const MAX_ITERATIONS: u32 = 30;
const MAX_NODES: u32 = 10_000;
fn to_scirs(o: &oxieml::LoweredOp) -> Option<SOp> {
use oxieml::LoweredOp as O;
let conv = |a: &oxieml::LoweredOp| to_scirs(a).map(Box::new);
Some(match o {
O::Const(c) => SOp::Const(*c),
O::Var(i) => SOp::Var(*i),
O::NamedConst(nc) => SOp::Const(nc.value()),
O::Add(a, b) => SOp::Add(conv(a)?, conv(b)?),
O::Sub(a, b) => SOp::Sub(conv(a)?, conv(b)?),
O::Mul(a, b) => SOp::Mul(conv(a)?, conv(b)?),
O::Div(a, b) => SOp::Div(conv(a)?, conv(b)?),
O::Pow(a, b) => SOp::Pow(conv(a)?, conv(b)?),
O::Neg(a) => SOp::Neg(conv(a)?),
O::Exp(a) => SOp::Exp(conv(a)?),
O::Ln(a) => SOp::Ln(conv(a)?),
O::Sin(a) => SOp::Sin(conv(a)?),
O::Cos(a) => SOp::Cos(conv(a)?),
O::Tan(a) => SOp::Tan(conv(a)?),
O::Sinh(a) => SOp::Sinh(conv(a)?),
O::Cosh(a) => SOp::Cosh(conv(a)?),
O::Tanh(a) => SOp::Tanh(conv(a)?),
O::Arcsin(a) => SOp::Arcsin(conv(a)?),
O::Arccos(a) => SOp::Arccos(conv(a)?),
O::Arctan(a) => SOp::Arctan(conv(a)?),
O::Arcsinh(a) => SOp::Arcsinh(conv(a)?),
O::Arccosh(a) => SOp::Arccosh(conv(a)?),
O::Arctanh(a) => SOp::Arctanh(conv(a)?),
_ => return None,
})
}
#[must_use]
pub fn canonical_latex_egraph(tree: &EmlTree) -> String {
let simplified = tree.lower().simplify();
match to_scirs(&simplified) {
Some(op) => {
let budget = SaturationBudget {
max_iterations: MAX_ITERATIONS,
max_nodes: MAX_NODES,
};
to_latex(canonicalize_egraph(&op, Some(budget)).op())
}
None => simplified.to_latex(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use oxieml::Canonical;
#[test]
fn collapses_ln_exp() {
let tree = Canonical::ln(&Canonical::exp(&EmlTree::var(0)));
let eg = canonical_latex_egraph(&tree);
assert!(!eg.is_empty());
assert!(eg.contains('x'), "expected the bare variable, got: {eg}");
assert!(!eg.contains("\\ln"), "ln not eliminated: {eg}");
assert!(
!eg.contains("exp") && !eg.contains("e^"),
"exp not eliminated: {eg}"
);
}
#[test]
fn egraph_is_no_worse_than_oxieml() {
let tree = EmlTree::eml(&EmlTree::var(0), &EmlTree::one());
let eg = canonical_latex_egraph(&tree);
let plain = tree.lower().simplify().to_latex();
assert!(!eg.is_empty());
assert!(eg.len() <= plain.len() + 2, "egraph={eg} plain={plain}");
}
}