use crate::expr::Expr;
use super::{Pattern, Tag, are_negations, scale_relation, split};
pub(super) struct LowBitAnnihilator;
impl Pattern for LowBitAnnihilator {
fn tags(&self) -> &'static [Tag] {
&[Tag::And]
}
fn apply(&self, e: &Expr, mask: u64) -> Option<Expr> {
let Expr::And(children) = e else {
return None;
};
if children.len() < 3 {
return None;
}
let norm: Vec<(u64, Expr)> = children.iter().map(|c| split(c, mask)).collect();
for (a, core) in &norm {
let a = *a;
let bare_present = norm.iter().any(|(f, c)| *f == 1 && c == core)
|| match core {
Expr::And(elems) => elems.iter().all(|el| children.contains(el)),
_ => false,
};
let neg = a.wrapping_neg() & mask; let tz_a = a.trailing_zeros();
let mut has_neg = false;
let mut has_even_multiple = false;
let mut visit = |b: u64| {
if b == neg {
has_neg = true;
}
if b.trailing_zeros() > tz_a {
has_even_multiple = true;
}
};
for (b, b_core) in &norm {
if b_core == core {
visit(*b);
}
}
if bare_present {
visit(1);
}
if has_neg && has_even_multiple {
return Some(Expr::zero());
}
}
for (i, x) in children.iter().enumerate() {
if !children
.iter()
.enumerate()
.any(|(j, other)| i != j && are_negations(x, other, mask))
{
continue;
}
if children.iter().enumerate().any(|(j, other)| {
i != j
&& scale_relation(other, x, mask)
.is_some_and(|coefficient| coefficient.trailing_zeros() > 0)
}) {
return Some(Expr::zero());
}
}
None
}
}