use super::hilbert;
#[derive(Debug, Clone)]
pub struct Dimension {
pub label: &'static str,
pub coord: u32,
}
impl Dimension {
pub fn new(label: &'static str, coord: u32) -> Self {
Self { label, coord }
}
}
#[derive(Debug, Clone, Copy)]
pub struct KeyConfig {
pub bits_per_dim: u32,
}
impl KeyConfig {
pub const STANDARD: KeyConfig = KeyConfig { bits_per_dim: 8 };
pub const COMPACT: KeyConfig = KeyConfig { bits_per_dim: 4 };
}
#[derive(Debug)]
pub struct CompositeKey {
config: KeyConfig,
dims: Vec<Dimension>,
}
impl CompositeKey {
pub fn new(config: KeyConfig) -> Self {
Self { config, dims: Vec::new() }
}
pub fn push(mut self, dim: Dimension) -> Self {
assert!(
self.dims.len() < 16,
"CompositeKey exceeds maximum of 16 dimensions"
);
self.dims.push(dim);
self
}
pub fn encode(&self) -> u128 {
assert!(!self.dims.is_empty(), "Cannot encode a key with zero dimensions");
let coords: Vec<u32> = self.dims.iter().map(|d| d.coord).collect();
hilbert::encode(&coords, self.config.bits_per_dim)
}
pub fn encode_range_min(dims_min: &[u32], bits_per_dim: u32) -> u128 {
hilbert::encode(dims_min, bits_per_dim)
}
pub fn encode_range_max(dims_max: &[u32], bits_per_dim: u32) -> u128 {
hilbert::encode(dims_max, bits_per_dim)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn two_points_order_is_consistent() {
let key_a = CompositeKey::new(KeyConfig::STANDARD)
.push(Dimension::new("x", 10))
.push(Dimension::new("y", 10))
.encode();
let key_b = CompositeKey::new(KeyConfig::STANDARD)
.push(Dimension::new("x", 200))
.push(Dimension::new("y", 200))
.encode();
assert_ne!(key_a, key_b);
}
}