use crate::opt::lang::MathLang;
use egg::{Rewrite, rewrite as rw};

pub fn rules() -> Vec<Rewrite<MathLang, ()>> {
    vec![
        rw!("add-zero-r"; "(+ ?x 0)" => "?x"),
        rw!("add-zero-l"; "(+ 0 ?x)" => "?x"),
        rw!("mul-one-r"; "(* ?x 1)" => "?x"),
        rw!("mul-one-l"; "(* 1 ?x)" => "?x"),
        // NOTE: `mul-zero` (`x * 0 => 0`) is intentionally omitted: it is
        // unsound for IEEE 754 floats when `x` is NaN or ±Inf (NaN * 0 = NaN).
        rw!("sub-zero"; "(- ?x 0)" => "?x"),
        // NOTE: `sub-self` (`x - x => 0`) is intentionally omitted: it is
        // unsound for IEEE 754 floats (NaN - NaN = NaN, not 0).
        // NOTE: `div-self` (`x / x => 1`) is intentionally omitted: it is
        // unsound for IEEE 754 floats (NaN/NaN = NaN, 0/0 = NaN).
        rw!("double-neg"; "(neg (neg ?x))" => "?x"),
        rw!("double-not"; "(! (! ?x))" => "?x"),
        // NOTE: `exp-log` (`exp(log(x)) => x`) is intentionally omitted:
        // log(x) is undefined (produces NaN) for x ≤ 0, so the roundtrip
        // is not a no-op for those inputs.
        rw!("log-exp"; "(log (exp ?x))" => "?x"),
        rw!("if-true"; "(ifelse true ?t ?e)" => "?t"),
        rw!("if-false"; "(ifelse false ?t ?e)" => "?e"),
        rw!("cast-same"; "(cast ?t (cast ?t ?x))" => "(cast ?t ?x)"),
    ]
}

/// Rewrites that may change float rounding (reassociate add/mul so constant subexprs can be folded).
/// Used only when `OptConfig::const_chain_fold_enabled()` is true.
pub fn aggressive_rules() -> Vec<Rewrite<MathLang, ()>> {
    vec![
        // Right-associate: ((x+y)+z) => (x+(y+z)) so (x+1)+2 becomes x+(1+2), then 1+2 folds to 3.
        rw!("add-assoc"; "(+ (+ ?x ?y) ?z)" => "(+ ?x (+ ?y ?z))"),
        rw!("mul-assoc"; "(* (* ?x ?y) ?z)" => "(* ?x (* ?y ?z))"),
    ]
}
