use crate::util::partition;
#[derive(Debug, Clone)]
pub struct SortEntry {
pub vs: Vec<String>,
pub i: usize,
pub barycenter: Option<f64>,
pub weight: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct SortResult {
pub vs: Vec<String>,
pub barycenter: Option<f64>,
pub weight: Option<f64>,
}
pub fn sort(entries: Vec<SortEntry>, bias_right: bool) -> SortResult {
let parts = partition(entries, |e| e.barycenter.is_some());
let mut sortable = parts.lhs;
let mut unsortable: Vec<SortEntry> = parts.rhs;
unsortable.sort_by_key(|b| std::cmp::Reverse(b.i));
let mut vs: Vec<Vec<String>> = Vec::new();
let mut sum = 0.0f64;
let mut weight = 0.0f64;
let mut vs_index = 0usize;
sortable.sort_by(compare_with_bias(bias_right));
vs_index = consume_unsortable(&mut vs, &mut unsortable, vs_index);
for entry in &sortable {
vs_index += entry.vs.len();
vs.push(entry.vs.clone());
sum += entry.barycenter.unwrap_or(0.0) * entry.weight.unwrap_or(0.0);
weight += entry.weight.unwrap_or(0.0);
vs_index = consume_unsortable(&mut vs, &mut unsortable, vs_index);
}
let flat_vs: Vec<String> = vs.into_iter().flatten().collect();
let mut result = SortResult {
vs: flat_vs,
barycenter: None,
weight: None,
};
if weight != 0.0 {
result.barycenter = Some(sum / weight);
result.weight = Some(weight);
}
result
}
fn consume_unsortable(
vs: &mut Vec<Vec<String>>,
unsortable: &mut Vec<SortEntry>,
mut index: usize,
) -> usize {
while let Some(last) = unsortable.last() {
if last.i <= index {
let last = unsortable.pop().unwrap();
vs.push(last.vs);
index += 1;
} else {
break;
}
}
index
}
fn compare_with_bias(bias: bool) -> impl Fn(&SortEntry, &SortEntry) -> std::cmp::Ordering {
move |a: &SortEntry, b: &SortEntry| {
let ba = a.barycenter.unwrap_or(0.0);
let bb = b.barycenter.unwrap_or(0.0);
if ba < bb {
std::cmp::Ordering::Less
} else if ba > bb {
std::cmp::Ordering::Greater
} else if !bias {
if a.i > b.i {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Less
}
} else {
if a.i < b.i {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Less
}
}
}
}