use crate::error::Error;
#[derive(Debug, Clone)]
pub enum Key<'a> {
Numeric(&'a [f64]),
Categorical(&'a [String]),
}
impl Key<'_> {
fn len(&self) -> usize {
match self {
Key::Numeric(v) => v.len(),
Key::Categorical(v) => v.len(),
}
}
fn ranks(&self) -> Result<Vec<f64>, Error> {
match self {
Key::Numeric(v) => {
if let Some(bad) = v.iter().find(|x| !x.is_finite()) {
return Err(Error::Input(format!(
"sort keys must be finite; found {bad}"
)));
}
Ok(v.to_vec())
}
Key::Categorical(v) => {
let mut levels: Vec<&str> = v.iter().map(String::as_str).collect();
levels.sort_unstable();
levels.dedup();
Ok(v.iter()
.map(|s| (levels.binary_search(&s.as_str()).unwrap() + 1) as f64)
.collect())
}
}
}
}
fn rank_columns(keys: &[Key]) -> Result<(Vec<Vec<f64>>, usize), Error> {
if keys.is_empty() {
return Err(Error::Input("at least one sort key is needed".into()));
}
let n = keys[0].len();
if n == 0 {
return Err(Error::Input("sort keys must not be empty".into()));
}
let mut ranks = Vec::with_capacity(keys.len());
for key in keys {
if key.len() != n {
return Err(Error::Input(format!(
"sort keys of different lengths: {} and {}",
n,
key.len()
)));
}
ranks.push(key.ranks()?);
}
Ok((ranks, n))
}
fn multi_key_order(ranks: &[Vec<f64>], n: usize) -> Vec<usize> {
let mut order: Vec<usize> = (0..n).collect();
order.sort_by(|&a, &b| {
for col in ranks {
match col[a].partial_cmp(&col[b]).unwrap() {
std::cmp::Ordering::Equal => continue,
other => return other,
}
}
std::cmp::Ordering::Equal
});
order
}
pub fn nested(keys: &[Key]) -> Result<Vec<usize>, Error> {
let (ranks, n) = rank_columns(keys)?;
Ok(multi_key_order(&ranks, n))
}
pub fn serpentine(keys: &[Key]) -> Result<Vec<usize>, Error> {
let (ranks, n) = rank_columns(keys)?;
if ranks.len() == 1 {
return Ok(multi_key_order(&ranks, n));
}
let mut order = multi_key_order(&ranks[..1], n);
for j in 1..ranks.len() {
let mut changes = vec![false; n];
changes[0] = true;
for col in &ranks[..j] {
for p in 1..n {
changes[p] = changes[p] || col[order[p]] != col[order[p - 1]];
}
}
let mut level = vec![0_i64; n];
let mut running = 0_i64;
for p in 0..n {
running += i64::from(changes[p]);
level[p] = running;
}
let mut positions: Vec<usize> = (0..n).collect();
positions.sort_by(|&a, &b| {
level[a].cmp(&level[b]).then_with(|| {
let direction = |p: usize| if level[p] % 2 == 1 { 1.0 } else { -1.0 };
let ka = direction(a) * ranks[j][order[a]];
let kb = direction(b) * ranks[j][order[b]];
ka.partial_cmp(&kb).unwrap()
})
});
order = positions.iter().map(|&p| order[p]).collect();
}
Ok(order)
}