use std::cmp::Ordering;
#[derive(Debug, Clone)]
pub enum Column {
Int(Vec<i64>),
Float(Vec<f64>),
Str(Vec<String>),
Bool(Vec<bool>),
}
impl Column {
pub fn len(&self) -> usize {
match self {
Column::Int(v) => v.len(),
Column::Float(v) => v.len(),
Column::Str(v) => v.len(),
Column::Bool(v) => v.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn type_name(&self) -> &'static str {
match self {
Column::Int(_) => "Int",
Column::Float(_) => "Float",
Column::Str(_) => "Str",
Column::Bool(_) => "Bool",
}
}
pub fn get_display(&self, idx: usize) -> String {
match self {
Column::Int(v) => format!("{}", v[idx]),
Column::Float(v) => format!("{}", v[idx]),
Column::Str(v) => v[idx].clone(),
Column::Bool(v) => format!("{}", v[idx]),
}
}
pub fn get_f64(&self, idx: usize) -> Option<f64> {
match self {
Column::Int(v) => Some(v[idx] as f64),
Column::Float(v) => Some(v[idx]),
Column::Bool(v) => Some(if v[idx] { 1.0 } else { 0.0 }),
Column::Str(_) => None,
}
}
pub fn compare_rows(&self, a: usize, b: usize) -> Ordering {
match self {
Column::Int(v) => v[a].cmp(&v[b]),
Column::Float(v) => v[a].partial_cmp(&v[b]).unwrap_or(Ordering::Equal),
Column::Str(v) => v[a].cmp(&v[b]),
Column::Bool(v) => v[a].cmp(&v[b]),
}
}
pub fn gather(&self, indices: &[usize]) -> Column {
match self {
Column::Int(v) => Column::Int(indices.iter().map(|&i| v[i]).collect()),
Column::Float(v) => Column::Float(indices.iter().map(|&i| v[i]).collect()),
Column::Str(v) => Column::Str(indices.iter().map(|&i| v[i].clone()).collect()),
Column::Bool(v) => Column::Bool(indices.iter().map(|&i| v[i]).collect()),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct FloatKey(pub f64);
impl PartialEq for FloatKey {
fn eq(&self, other: &Self) -> bool {
self.0.to_bits() == other.0.to_bits()
}
}
impl Eq for FloatKey {}
impl PartialOrd for FloatKey {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for FloatKey {
fn cmp(&self, other: &Self) -> Ordering {
self.0.total_cmp(&other.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum GroupKey {
Int(i64),
Float(FloatKey),
Str(String),
Bool(bool),
}
impl GroupKey {
pub fn from_column(col: &Column, row: usize) -> Self {
match col {
Column::Int(v) => GroupKey::Int(v[row]),
Column::Float(v) => GroupKey::Float(FloatKey(v[row])),
Column::Str(v) => GroupKey::Str(v[row].clone()),
Column::Bool(v) => GroupKey::Bool(v[row]),
}
}
pub fn to_display(&self) -> String {
match self {
GroupKey::Int(v) => format!("{}", v),
GroupKey::Float(FloatKey(v)) => format!("{}", v),
GroupKey::Str(s) => s.clone(),
GroupKey::Bool(b) => format!("{}", b),
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ColumnKeyRef<'a> {
Int(i64),
Float(FloatKey),
Str(&'a str),
Bool(bool),
}
impl<'a> ColumnKeyRef<'a> {
#[inline]
pub fn from_column(col: &'a Column, row: usize) -> Self {
match col {
Column::Int(v) => ColumnKeyRef::Int(v[row]),
Column::Float(v) => ColumnKeyRef::Float(FloatKey(v[row])),
Column::Str(v) => ColumnKeyRef::Str(&v[row]),
Column::Bool(v) => ColumnKeyRef::Bool(v[row]),
}
}
pub fn to_owned_key(&self) -> GroupKey {
match self {
ColumnKeyRef::Int(v) => GroupKey::Int(*v),
ColumnKeyRef::Float(v) => GroupKey::Float(*v),
ColumnKeyRef::Str(s) => GroupKey::Str((*s).to_string()),
ColumnKeyRef::Bool(v) => GroupKey::Bool(*v),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_column_len() {
let col = Column::Int(vec![1, 2, 3]);
assert_eq!(col.len(), 3);
}
#[test]
fn test_column_gather() {
let col = Column::Str(vec!["a".into(), "b".into(), "c".into(), "d".into()]);
let gathered = col.gather(&[0, 2, 3]);
if let Column::Str(v) = gathered {
assert_eq!(v, vec!["a", "c", "d"]);
} else {
panic!("wrong type");
}
}
#[test]
fn test_float_key_nan_ordering() {
let a = FloatKey(f64::NAN);
let b = FloatKey(1.0);
let _ = a.cmp(&b);
}
#[test]
fn test_column_key_ref_zero_copy() {
let col = Column::Str(vec!["hello".into(), "world".into()]);
let key = ColumnKeyRef::from_column(&col, 0);
assert_eq!(key, ColumnKeyRef::Str("hello"));
}
}