candding 0.1.0

Pure candle embeddings for Rust with no ONNX: dense, sparse, multi-vector and rerank models today, image models on the roadmap
Documentation
//! Length-sorted batching that restores the caller's order.

/// Indices sorted by descending length so each chunk pads as little as possible.
pub fn sorted_indices(lengths: &[usize]) -> Vec<usize> {
  let mut order: Vec<usize> = (0..lengths.len()).collect();
  order.sort_by(|a, b| lengths[*b].cmp(&lengths[*a]).then(a.cmp(b)));
  order
}

/// Places `items`, produced in `order`, back at their original positions.
pub fn restore<T: Clone>(order: &[usize], items: Vec<T>) -> Vec<T> {
  let mut slots: Vec<Option<T>> = vec![None; order.len()];
  for (slot, item) in order.iter().zip(items) {
    slots[*slot] = Some(item);
  }
  slots
    .into_iter()
    .map(|s| s.expect("every slot is filled"))
    .collect()
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn sorts_longest_first_and_keeps_ties_stable() {
    assert_eq!(sorted_indices(&[3, 9, 3, 7]), vec![1, 3, 0, 2]);
  }

  #[test]
  fn restore_round_trips() {
    let lengths = [3, 9, 3, 7];
    let order = sorted_indices(&lengths);
    let produced: Vec<usize> = order.iter().map(|i| lengths[*i]).collect();
    assert_eq!(restore(&order, produced), lengths.to_vec());
  }
}