pub fn rank_values(x: &[f64]) -> Vec<f64> {
let n = x.len();
let mut order: Vec<usize> = (0..n).collect();
order.sort_by(|&a, &b| x[a].partial_cmp(&x[b]).unwrap_or(std::cmp::Ordering::Equal));
let mut ranks = vec![0.0_f64; n];
let mut i = 0;
while i < n {
let val = x[order[i]];
let mut j = i + 1;
while j < n && x[order[j]] == val {
j += 1;
}
let avg_rank = (i + 1 + j) as f64 / 2.0;
for k in i..j {
ranks[order[k]] = avg_rank;
}
i = j;
}
ranks
}
pub fn compose_rank(signals: &[&[f64]]) -> Vec<f64> {
if signals.is_empty() {
return vec![];
}
let n_bars = signals[0].len();
let mut scores = vec![0.0_f64; n_bars];
for &column in signals {
let ranks = rank_values(column);
for (bar_idx, rank) in ranks.into_iter().enumerate() {
scores[bar_idx] += rank;
}
}
scores
}
pub fn top_n_indices(x: &[f64], n: usize) -> Vec<i64> {
let len = x.len();
let k = n.min(len);
let mut order: Vec<usize> = (0..len).collect();
order.sort_by(|&a, &b| x[b].partial_cmp(&x[a]).unwrap_or(std::cmp::Ordering::Equal));
order[..k].iter().map(|&i| i as i64).collect()
}
pub fn bottom_n_indices(x: &[f64], n: usize) -> Vec<i64> {
let len = x.len();
let k = n.min(len);
let mut order: Vec<usize> = (0..len).collect();
order.sort_by(|&a, &b| x[a].partial_cmp(&x[b]).unwrap_or(std::cmp::Ordering::Equal));
order[..k].iter().map(|&i| i as i64).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rank_values() {
let x = vec![3.0, 1.0, 2.0];
let ranks = rank_values(&x);
assert!((ranks[0] - 3.0).abs() < 1e-10); assert!((ranks[1] - 1.0).abs() < 1e-10); assert!((ranks[2] - 2.0).abs() < 1e-10); }
#[test]
fn test_rank_values_ties() {
let x = vec![1.0, 2.0, 2.0, 4.0];
let ranks = rank_values(&x);
assert!((ranks[0] - 1.0).abs() < 1e-10);
assert!((ranks[1] - 2.5).abs() < 1e-10); assert!((ranks[2] - 2.5).abs() < 1e-10);
assert!((ranks[3] - 4.0).abs() < 1e-10);
}
#[test]
fn test_compose_rank() {
let col1 = vec![3.0, 1.0, 2.0];
let col2 = vec![1.0, 3.0, 2.0];
let signals: Vec<&[f64]> = vec![&col1, &col2];
let scores = compose_rank(&signals);
assert!((scores[0] - 4.0).abs() < 1e-10);
assert!((scores[1] - 4.0).abs() < 1e-10);
assert!((scores[2] - 4.0).abs() < 1e-10);
}
#[test]
fn test_top_n_indices() {
let x = vec![10.0, 50.0, 30.0, 20.0, 40.0];
let result = top_n_indices(&x, 3);
assert_eq!(result.len(), 3);
assert_eq!(result[0], 1); assert_eq!(result[1], 4); assert_eq!(result[2], 2); }
#[test]
fn test_bottom_n_indices() {
let x = vec![10.0, 50.0, 30.0, 20.0, 40.0];
let result = bottom_n_indices(&x, 2);
assert_eq!(result.len(), 2);
assert_eq!(result[0], 0); assert_eq!(result[1], 3); }
#[test]
fn test_top_n_exceeds_len() {
let x = vec![1.0, 2.0];
let result = top_n_indices(&x, 5);
assert_eq!(result.len(), 2);
}
}