use super::Value;
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum LabelKey {
Null,
Bool(bool),
Int(i64),
Float(u64),
Text(String),
Instant(i64, u32),
Period(i64, crate::period::Grain, crate::calendar::Calendar),
Quantity(String, u64),
}
fn float_bits(f: f64) -> u64 {
if f == 0.0 {
0.0f64.to_bits()
} else if f.is_nan() {
f64::NAN.to_bits()
} else {
f.to_bits()
}
}
impl Value {
pub fn label_key(&self) -> Option<LabelKey> {
Some(match self {
Value::Null => LabelKey::Null,
Value::Bool(b) => LabelKey::Bool(*b),
Value::Int(n) => LabelKey::Int(*n),
Value::Float(f) if f.fract() == 0.0 && f.abs() < 9.2e18 => LabelKey::Int(*f as i64),
Value::Float(f) => LabelKey::Float(float_bits(*f)),
Value::Str(s) => LabelKey::Text(s.clone()),
Value::Instant { secs, nanos, .. } => LabelKey::Instant(*secs, *nanos),
Value::Period {
start,
grain,
calendar,
} => LabelKey::Period(*start, *grain, *calendar),
Value::Quantity { value, base, .. } => {
LabelKey::Quantity(base.clone(), float_bits(*value))
}
_ => return None,
})
}
pub fn same_key(&self, other: &Value) -> bool {
match (self.label_key(), other.label_key()) {
(Some(a), Some(b)) => a == b,
(None, None) => self == other,
_ => false,
}
}
}
pub fn same_label(a: &[Value], b: &[Value]) -> bool {
a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.same_key(y))
}
#[derive(Default)]
pub struct LabelIndex {
keyed: HashMap<Vec<LabelKey>, usize>,
loose: Vec<(Vec<Value>, usize)>,
len: usize,
}
impl LabelIndex {
pub fn new() -> LabelIndex {
LabelIndex::default()
}
#[cfg(test)]
pub fn len(&self) -> usize {
self.len
}
fn keys(label: &[Value]) -> Option<Vec<LabelKey>> {
label.iter().map(Value::label_key).collect()
}
#[cfg(test)]
pub fn find(&self, label: &[Value]) -> Option<usize> {
match Self::keys(label) {
Some(k) => self.keyed.get(&k).copied(),
None => self
.loose
.iter()
.find(|(l, _)| same_label(l, label))
.map(|(_, c)| *c),
}
}
pub fn intern(&mut self, label: &[Value]) -> (usize, bool) {
let next = self.len;
let (class, new) = match Self::keys(label) {
Some(k) => {
let class = *self.keyed.entry(k).or_insert(next);
(class, class == next)
}
None => match self.loose.iter().find(|(l, _)| same_label(l, label)) {
Some((_, c)) => (*c, false),
None => {
self.loose.push((label.to_vec(), next));
(next, true)
}
},
};
if new {
self.len += 1;
}
(class, new)
}
}
#[derive(Default)]
pub struct Occurrences(Vec<usize>);
impl Occurrences {
pub fn new() -> Occurrences {
Occurrences::default()
}
pub fn next(&mut self, classes: &mut LabelIndex, label: &[Value]) -> (usize, usize) {
let (class, _) = classes.intern(label);
if self.0.len() <= class {
self.0.resize(class + 1, 0);
}
self.0[class] += 1;
(class, self.0[class])
}
}
#[cfg(test)]
mod tests {
use super::*;
fn instant(secs: i64, offset_min: Option<i16>) -> Value {
Value::Instant {
secs,
nanos: 0,
offset_min,
calendar: Default::default(),
}
}
fn quantity(value: f64, written: (f64, &str)) -> Value {
Value::Quantity {
value,
base: "m".into(),
written: Some((written.0, written.1.into())),
}
}
#[test]
fn keys_are_equal_as_values_and_nothing_coerces_them() {
assert!(Value::Int(1).same_key(&Value::Float(1.0)));
assert!(Value::Float(0.0).same_key(&Value::Float(-0.0)));
assert!(!Value::Int(1).same_key(&Value::Float(1.5)));
assert!(!Value::Int(1).same_key(&Value::Str("1".into())));
assert!(!Value::Bool(true).same_key(&Value::Str("true".into())));
assert!(!Value::Int(9007199254740993).same_key(&Value::Int(9007199254740992)));
assert!(!Value::Int(9007199254740993).same_key(&Value::Float(9007199254740992.0)));
assert!(instant(1_356_998_400, Some(60)).same_key(&instant(1_356_998_400, Some(0))));
assert!(!instant(1_356_998_400, None).same_key(&instant(1_356_998_401, None)));
assert!(quantity(1000.0, (1.0, "km")).same_key(&quantity(1000.0, (1000.0, "m"))));
let l = |v: Vec<Value>| Value::List(std::sync::Arc::new(v));
assert!(l(vec![Value::Int(1)]).same_key(&l(vec![Value::Int(1)])));
assert!(!l(vec![Value::Int(1)]).same_key(&Value::Int(1)));
}
#[test]
fn the_index_numbers_classes_in_the_order_first_met() {
let mut ix = LabelIndex::new();
assert_eq!(ix.intern(&[Value::Int(2)]), (0, true));
assert_eq!(ix.intern(&[Value::Str("2".into())]), (1, true));
assert_eq!(ix.intern(&[Value::Float(2.0)]), (0, false));
let l = Value::List(std::sync::Arc::new(vec![Value::Int(2)]));
assert_eq!(ix.intern(std::slice::from_ref(&l)), (2, true));
assert_eq!(ix.intern(std::slice::from_ref(&l)), (2, false));
assert_eq!(ix.len(), 3);
assert_eq!(ix.find(&[Value::Float(2.0)]), Some(0));
assert_eq!(ix.find(&[Value::Int(3)]), None);
let mut ix = LabelIndex::new();
ix.intern(&[Value::Int(1), Value::Str("a".into())]);
assert_eq!(
ix.find(&[Value::Float(1.0), Value::Str("a".into())]),
Some(0)
);
assert_eq!(ix.find(&[Value::Int(1), Value::Str("b".into())]), None);
}
}