use rsfgsea::prelude::ScoreType;
fn cpu_es(hits: &[usize], weights: &[f32], n_total: usize, score_type: ScoreType) -> f64 {
let weights_f64: Vec<f64> = weights.iter().map(|&w| w as f64).collect();
rsfgsea::algo::calculate_es_fgsea(&weights_f64, hits, n_total, score_type).0
}
#[test]
fn calculate_es_matches_reference_for_top_enriched_pathway() {
let n_total = 100usize;
let weights: Vec<f32> = (0..n_total).map(|i| (n_total - i) as f32).collect();
let hits: Vec<usize> = (0..10).collect();
let es = cpu_es(&hits, &weights, n_total, ScoreType::Std);
assert!((es - 1.0).abs() < 1e-9, "top-enriched ES should be 1.0");
}
#[test]
fn calculate_es_respects_score_type_direction() {
let n_total = 100usize;
let weights: Vec<f32> = (0..n_total).map(|i| (n_total - i) as f32).collect();
let top_hits: Vec<usize> = (0..10).collect();
let bottom_hits: Vec<usize> = (n_total - 10..n_total).collect();
let pos_top = cpu_es(&top_hits, &weights, n_total, ScoreType::Pos);
let neg_top = cpu_es(&top_hits, &weights, n_total, ScoreType::Neg);
let pos_bottom = cpu_es(&bottom_hits, &weights, n_total, ScoreType::Pos);
let neg_bottom = cpu_es(&bottom_hits, &weights, n_total, ScoreType::Neg);
assert!(pos_top > 0.0);
assert!(neg_top <= 0.0);
assert!(pos_bottom >= 0.0);
assert!(neg_bottom < 0.0);
}
#[test]
fn calculate_es_stays_within_expected_range() {
let n_total = 1000usize;
let weights: Vec<f32> = (0..n_total)
.map(|i| ((n_total - i) as f32).sqrt())
.collect();
let hits: Vec<usize> = (0..50).step_by(2).collect();
let es_std = cpu_es(&hits, &weights, n_total, ScoreType::Std);
let es_pos = cpu_es(&hits, &weights, n_total, ScoreType::Pos);
let es_neg = cpu_es(&hits, &weights, n_total, ScoreType::Neg);
assert!((-1.0..=1.0).contains(&es_std));
assert!((-1.0..=1.0).contains(&es_pos));
assert!((-1.0..=1.0).contains(&es_neg));
}