use crate::error::{Result, StatError};
fn sort_indexed(data: &[f64]) -> Vec<(usize, f64)> {
let mut indexed: Vec<(usize, f64)> = data.iter().cloned().enumerate().collect();
indexed.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
indexed
}
fn find_tie_group_end(indexed: &[(usize, f64)], start: usize) -> usize {
let value = indexed[start].1;
indexed[start..]
.iter()
.take_while(|(_, v)| *v == value)
.count()
+ start
}
fn assign_tie_group_ranks(
indexed: &[(usize, f64)],
ranks: &mut [f64],
start: usize,
end: usize,
tie_sizes: Option<&mut Vec<usize>>,
) {
let avg_rank = (start + 1 + end) as f64 / 2.0;
for item in indexed.iter().take(end).skip(start) {
ranks[item.0] = avg_rank;
}
if let Some(ties) = tie_sizes {
let tie_size = end - start;
if tie_size > 1 {
ties.push(tie_size);
}
}
}
pub fn rank(data: &[f64]) -> Result<Vec<f64>> {
if data.is_empty() {
return Err(StatError::EmptyData);
}
let indexed = sort_indexed(data);
let mut ranks = vec![0.0; data.len()];
let mut i = 0;
while i < indexed.len() {
let j = find_tie_group_end(&indexed, i);
assign_tie_group_ranks(&indexed, &mut ranks, i, j, None);
i = j;
}
Ok(ranks)
}
pub(crate) fn rank_with_ties(data: &[f64]) -> Result<(Vec<f64>, Vec<usize>)> {
if data.is_empty() {
return Err(StatError::EmptyData);
}
let indexed = sort_indexed(data);
let mut ranks = vec![0.0; data.len()];
let mut tie_sizes = Vec::new();
let mut i = 0;
while i < indexed.len() {
let j = find_tie_group_end(&indexed, i);
assign_tie_group_ranks(&indexed, &mut ranks, i, j, Some(&mut tie_sizes));
i = j;
}
Ok((ranks, tie_sizes))
}