mod common;

use common::assert_values_close;
use gonidium::ir::DType;
use gonidium::opt::optimize;
use gonidium::{Value, interp_eval, lower, parse_dsl};

#[test]
fn optimize_preserves_param_and_output_dtypes() {
    let src = r#"
    |x: f32, y: i32|
    cast<f64>((x + y) * 1)
    "#;
    let func = parse_dsl(src).expect("parse should succeed");
    let dag = lower(&func).expect("lower should succeed").dag;
    let opt = optimize(&dag).expect("optimize should succeed");

    assert_eq!(opt.params[0].1, DType::F32);
    assert_eq!(opt.params[1].1, DType::I32);
    assert_eq!(opt.dtypes[opt.outputs[0].0], DType::F64);
}

#[test]
fn optimize_keeps_semantics_with_const_fold_and_fusion_rules() {
    let src = r#"
    |x: f64|
    log(sigmoid(exp(0@f64) + x))
    "#;
    let func = parse_dsl(src).expect("parse should succeed");
    let dag = lower(&func).expect("lower should succeed").dag;
    let opt = optimize(&dag).expect("optimize should succeed");

    let base = interp_eval(&dag, &[Value::F64(0.3)]).expect("baseline eval should succeed");
    let after = interp_eval(&opt, &[Value::F64(0.3)]).expect("optimized eval should succeed");
    assert_values_close(&base, &after);
}

#[test]
fn optimize_preserves_large_i64_literals() {
    let src = r#"
    ||
    9223372036854775807@i64 + 0@i64
    "#;
    let func = parse_dsl(src).expect("parse should succeed");
    let dag = lower(&func).expect("lower should succeed").dag;
    let opt = optimize(&dag).expect("optimize should succeed");

    let base = interp_eval(&dag, &[]).expect("baseline eval should succeed");
    let after = interp_eval(&opt, &[]).expect("optimized eval should succeed");
    assert_eq!(base, after);
}
