use serde::{Deserialize, Serialize};
use super::address::SpaceId;
pub const DEFAULT_BITS_PER_AXIS: u32 = 3;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FlowVector {
pub space: SpaceId,
pub delta: Vec<i32>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct QuantizedDirection {
pub coords: Vec<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct FlowVectorQuantization {
pub bits_per_axis: u32,
}
impl Default for FlowVectorQuantization {
fn default() -> Self {
Self {
bits_per_axis: DEFAULT_BITS_PER_AXIS,
}
}
}
#[derive(Debug, Clone)]
pub struct FlowVectorRecord {
pub edge: super::hyperedge::Hyperedge,
pub vector: FlowVector,
pub quantized: QuantizedDirection,
}
pub fn quantize_axis(delta: i32, bits_per_axis: u32) -> u32 {
let max_level = (1u32 << bits_per_axis).saturating_sub(1);
if delta == 0 {
return 0;
}
let sign = if delta > 0 { 1u32 } else { 0u32 };
let mag = delta.unsigned_abs().min(max_level as u32);
1 + sign * (max_level + 1) + mag
}
pub fn quantize_direction(delta: &[i32], q: &FlowVectorQuantization) -> QuantizedDirection {
QuantizedDirection {
coords: delta
.iter()
.map(|&d| quantize_axis(d, q.bits_per_axis))
.collect(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn distinct_directions_distinct_buckets_at_default_granularity() {
let q = FlowVectorQuantization::default();
let up = quantize_direction(&[0, 10], &q);
let down = quantize_direction(&[0, -10], &q);
let flat = quantize_direction(&[5, 0], &q);
assert_ne!(up.coords, down.coords);
assert_ne!(up.coords, flat.coords);
assert_ne!(down.coords, flat.coords);
}
#[test]
fn zero_delta_maps_to_zero_buckets() {
let q = FlowVectorQuantization::default();
let z = quantize_direction(&[0, 0], &q);
assert_eq!(z.coords, vec![0, 0]);
}
}