use symplex::prelude::*;
#[test]
fn liveness_ratio_all_live() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = x.powi(2) + &x + 1;
let ratio = ctx.liveness_ratio(std::slice::from_ref(&expr));
assert!(ratio > 0.0, "should have some live nodes: {ratio}");
assert!(ratio <= 1.0, "ratio must be <= 1.0: {ratio}");
}
#[test]
fn liveness_ratio_with_garbage() {
let ctx = Context::new();
let x = ctx.symbol("x");
for i in 0..100 {
let _ = x.powi(i).sin().cos().exp();
}
let keeper = x.clone();
let ratio = ctx.liveness_ratio(&[keeper]);
assert!(ratio < 0.5, "with garbage, liveness should be low: {ratio}");
}
#[test]
fn should_compact_small_arena() {
let ctx = Context::new();
let x = ctx.symbol("x");
assert!(
!ctx.should_compact(&[x]),
"small arenas should not trigger compaction"
);
}
#[test]
fn liveness_after_compact() {
let ctx = Context::new();
let x = ctx.symbol("x");
let _ = x.powi(10).expand().sin().cos();
let expr = &x + 1;
let ratio_before = ctx.liveness_ratio(std::slice::from_ref(&expr));
let (new_ctx, new_exprs) = ctx.compact(&[expr]);
let ratio_after = new_ctx.liveness_ratio(&[new_exprs[0].clone()]);
assert!(
ratio_after > ratio_before,
"compact should improve liveness: before={ratio_before}, after={ratio_after}"
);
}
#[test]
fn liveness_ratio_empty_roots() {
let ctx = Context::new();
let _x = ctx.symbol("x");
let ratio = ctx.liveness_ratio(&[]);
assert!(
(0.0..=1.0).contains(&ratio),
"ratio should be in [0, 1]: {ratio}"
);
assert!(
(ratio - 0.0).abs() < f64::EPSILON,
"with no roots, ratio should be 0.0: {ratio}"
);
}
#[test]
fn liveness_ratio_multiple_roots_increases_liveness() {
let ctx = Context::new();
let x = ctx.symbol("x");
let y = ctx.symbol("y");
let expr_x = x.sin();
let expr_y = y.cos();
let ratio_one = ctx.liveness_ratio(std::slice::from_ref(&expr_x));
let ratio_both = ctx.liveness_ratio(&[expr_x, expr_y]);
assert!(
ratio_both >= ratio_one,
"more roots should mean equal or higher liveness: one={ratio_one}, both={ratio_both}"
);
}
#[test]
fn should_compact_respects_threshold() {
let ctx = Context::new();
let x = ctx.symbol("x");
for i in 0..200 {
let _ = x.powi(i).sin().cos().exp().ln();
}
assert!(
!ctx.should_compact(&[x]),
"should_compact should return false for arenas under 100K nodes (node_count = {})",
ctx.node_count()
);
}
#[test]
fn last_compact_size_set_after_compact() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = &x + 1;
let (new_ctx, new_exprs) = ctx.compact(&[expr]);
assert!(
!new_ctx.should_compact(&[new_exprs[0].clone()]),
"freshly compacted context should not need another compaction"
);
}