use crate::expr::Expr;
use super::{Pattern, Tag, are_negations, low_bit_mask_multiple, scale_relation, scaled, split};
pub(super) struct LowBitMaskSplit;
impl LowBitMaskSplit {
fn split_masked(term: &Expr, mask: u64) -> Option<(u64, Expr, Expr)> {
let (a, core) = split(term, mask);
let Expr::And(conj) = core else {
return None;
};
if conj.len() != 2 {
return None;
}
for (i, j) in [(0, 1), (1, 0)] {
let multiple = low_bit_mask_multiple(&conj[i], mask).unwrap_or_else(|| conj[i].clone());
let t = conj[j].clone();
let is_even_multiple = scale_relation(&multiple, &t, mask)
.is_some_and(|coefficient| coefficient.trailing_zeros() > 0)
|| {
let (multiple_factor, multiple_core) = split(&multiple, mask);
let (t_factor, t_core) = split(&t, mask);
multiple_core == t_core
&& multiple_factor.trailing_zeros() > t_factor.trailing_zeros()
};
if !is_even_multiple {
continue;
}
return Some((a, t, multiple));
}
None
}
}
impl Pattern for LowBitMaskSplit {
fn tags(&self) -> &'static [Tag] {
&[Tag::Add]
}
fn apply(&self, e: &Expr, mask: u64) -> Option<Expr> {
let Expr::Add(terms) = e else {
return None;
};
if terms.len() < 2 {
return None;
}
for i in 0..terms.len() {
let Some((a1, t1, multiple1)) = Self::split_masked(&terms[i], mask) else {
continue;
};
for j in (i + 1)..terms.len() {
let Some((a2, t2, multiple2)) = Self::split_masked(&terms[j], mask) else {
continue;
};
if a1 != a2 || multiple1 != multiple2 || !are_negations(&t1, &t2, mask) {
continue;
}
let collapsed = scaled(&multiple1, a1, mask);
let kept: Vec<Expr> = terms
.iter()
.enumerate()
.filter(|(k, _)| *k != i && *k != j)
.map(|(_, t)| t.clone())
.chain(std::iter::once(collapsed))
.collect();
return Some(Expr::Add(kept).reduce_masked(mask));
}
}
None
}
}