use crate::tree::{EmlNode, EmlTree};
use std::sync::{Arc, OnceLock};
use super::LoweredOp;
const WILDCARD_VAR: usize = usize::MAX / 2;
fn sin_template() -> &'static EmlNode {
static TEMPLATE: OnceLock<EmlNode> = OnceLock::new();
TEMPLATE.get_or_init(|| {
let placeholder = EmlTree::var(WILDCARD_VAR);
let tree = crate::canonical::Canonical::sin(&placeholder);
(*tree.root).clone()
})
}
fn cos_template() -> &'static EmlNode {
static TEMPLATE: OnceLock<EmlNode> = OnceLock::new();
TEMPLATE.get_or_init(|| {
let placeholder = EmlTree::var(WILDCARD_VAR);
let tree = crate::canonical::Canonical::cos(&placeholder);
(*tree.root).clone()
})
}
fn unify_with_wildcard<'a>(
candidate: &'a EmlNode,
template: &EmlNode,
captured: &mut Option<&'a EmlNode>,
) -> bool {
if let EmlNode::Var(idx) = template {
if *idx == WILDCARD_VAR {
match captured {
None => {
*captured = Some(candidate);
return true;
}
Some(prev) => {
return nodes_structurally_equal(prev, candidate);
}
}
}
}
match (candidate, template) {
(EmlNode::One, EmlNode::One) => true,
(EmlNode::Var(a), EmlNode::Var(b)) => a == b,
(EmlNode::Const(a), EmlNode::Const(b)) => a.to_bits() == b.to_bits(),
(
EmlNode::Eml {
left: la,
right: ra,
},
EmlNode::Eml {
left: lb,
right: rb,
},
) => {
unify_with_wildcard(la.as_ref(), lb.as_ref(), captured)
&& unify_with_wildcard(ra.as_ref(), rb.as_ref(), captured)
}
_ => false,
}
}
fn nodes_structurally_equal(a: &EmlNode, b: &EmlNode) -> bool {
match (a, b) {
(EmlNode::One, EmlNode::One) => true,
(EmlNode::Var(i), EmlNode::Var(j)) => i == j,
(EmlNode::Const(a), EmlNode::Const(b)) => (a - b).abs() < 1e-15,
(
EmlNode::Eml {
left: la,
right: ra,
},
EmlNode::Eml {
left: lb,
right: rb,
},
) => {
nodes_structurally_equal(la.as_ref(), lb.as_ref())
&& nodes_structurally_equal(ra.as_ref(), rb.as_ref())
}
_ => false,
}
}
fn match_sin_structure(node: &EmlNode) -> Option<EmlNode> {
let mut captured: Option<&EmlNode> = None;
if unify_with_wildcard(node, sin_template(), &mut captured) {
captured.cloned()
} else {
None
}
}
fn match_cos_structure(node: &EmlNode) -> Option<EmlNode> {
let mut captured: Option<&EmlNode> = None;
if unify_with_wildcard(node, cos_template(), &mut captured) {
captured.cloned()
} else {
None
}
}
fn match_ln_structure(node: &EmlNode) -> Option<EmlNode> {
if let EmlNode::Eml { left, right } = node {
if !matches!(left.as_ref(), EmlNode::One) {
return None;
}
if let EmlNode::Eml {
left: mid_l,
right: mid_r,
} = right.as_ref()
{
if !matches!(mid_r.as_ref(), EmlNode::One) {
return None;
}
if let EmlNode::Eml {
left: inner_l,
right: inner_r,
} = mid_l.as_ref()
{
if matches!(inner_l.as_ref(), EmlNode::One) {
return Some(inner_r.as_ref().clone());
}
}
}
}
None
}
fn match_ln_of_right(right: &EmlNode) -> Option<EmlNode> {
if let EmlNode::Eml {
left: mid_l,
right: mid_r,
} = right
{
if !matches!(mid_r.as_ref(), EmlNode::One) {
return None;
}
if let EmlNode::Eml {
left: inner_l,
right: inner_r,
} = mid_l.as_ref()
{
if matches!(inner_l.as_ref(), EmlNode::One) {
return Some(inner_r.as_ref().clone());
}
}
}
None
}
pub(super) fn lower_node(node: &EmlNode) -> LoweredOp {
match node {
EmlNode::Const(v) => LoweredOp::Const(*v),
EmlNode::One => LoweredOp::Const(1.0),
EmlNode::Var(i) => LoweredOp::Var(*i),
EmlNode::Eml { left, right } => {
if let Some(inner) = match_sin_structure(node) {
return LoweredOp::Sin(Arc::new(lower_node(&inner)));
}
if let Some(inner) = match_cos_structure(node) {
return LoweredOp::Cos(Arc::new(lower_node(&inner)));
}
if matches!(right.as_ref(), EmlNode::One) {
if let Some(inner) = match_ln_structure(left) {
return lower_node(&inner);
}
return LoweredOp::Exp(Arc::new(lower_node(left)));
}
if matches!(left.as_ref(), EmlNode::One) && matches!(right.as_ref(), EmlNode::One) {
return LoweredOp::Const(std::f64::consts::E);
}
if matches!(left.as_ref(), EmlNode::One) {
if let Some(inner) = match_ln_of_right(right) {
return LoweredOp::Ln(Arc::new(lower_node(&inner)));
}
}
if matches!(left.as_ref(), EmlNode::One) {
if let EmlNode::Eml {
left: inner_l,
right: inner_r,
} = right.as_ref()
{
if matches!(inner_r.as_ref(), EmlNode::One) {
let x_lowered = lower_node(inner_l);
return LoweredOp::Sub(
Arc::new(LoweredOp::Const(std::f64::consts::E)),
Arc::new(x_lowered),
);
}
}
}
if let Some(x_inner) = match_ln_structure(left) {
if let EmlNode::Eml {
left: y_node,
right: y_one,
} = right.as_ref()
{
if matches!(y_one.as_ref(), EmlNode::One) {
return LoweredOp::Sub(
Arc::new(lower_node(&x_inner)),
Arc::new(lower_node(y_node)),
);
}
}
}
let left_lowered = lower_node(left);
let right_lowered = lower_node(right);
LoweredOp::Sub(
Arc::new(LoweredOp::Exp(Arc::new(left_lowered))),
Arc::new(LoweredOp::Ln(Arc::new(right_lowered))),
)
}
}
}