#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Resolution(pub f32);
impl Resolution {
pub const COARSE: Self = Self(0.0);
pub const MEDIUM: Self = Self(0.5);
pub const FINE: Self = Self(1.0);
pub fn new(level: f32) -> Self {
Self(level.clamp(0.0, 1.0))
}
pub fn level(&self) -> f32 {
self.0
}
pub fn to_tree_level(&self, max_depth: usize) -> usize {
let inverted = 1.0 - self.0; let level = (inverted * max_depth as f32).round() as usize;
level.min(max_depth)
}
}
impl Default for Resolution {
fn default() -> Self {
Self::FINE
}
}
pub trait MultiResolution {
type Item;
type Summary;
fn at_resolution(
&self,
resolution: Resolution,
) -> Vec<ResolutionItem<Self::Item, Self::Summary>>;
fn collapsed(&self) -> Vec<ResolutionItem<Self::Item, Self::Summary>>;
fn available_resolutions(&self) -> Vec<Resolution>;
}
#[derive(Debug, Clone)]
pub struct ResolutionItem<T, S> {
pub id: usize,
pub resolution: Resolution,
pub content: ResolutionContent<T, S>,
pub children: Vec<usize>,
pub parent: Option<usize>,
}
#[derive(Debug, Clone)]
pub enum ResolutionContent<T, S> {
Item(T),
Summary(S),
}
impl<T, S> ResolutionContent<T, S> {
pub fn is_item(&self) -> bool {
matches!(self, Self::Item(_))
}
pub fn is_summary(&self) -> bool {
matches!(self, Self::Summary(_))
}
pub fn as_item(&self) -> Option<&T> {
match self {
Self::Item(t) => Some(t),
Self::Summary(_) => None,
}
}
pub fn as_summary(&self) -> Option<&S> {
match self {
Self::Item(_) => None,
Self::Summary(s) => Some(s),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraversalStrategy {
Collapsed,
TopDown,
BottomUp,
FixedResolution,
CoarseToFine,
}
#[derive(Debug, Clone)]
pub struct HierarchicalConfig {
pub strategy: TraversalStrategy,
pub target_resolution: Resolution,
pub coarse_candidates: usize,
pub k: usize,
pub include_context: bool,
}
impl Default for HierarchicalConfig {
fn default() -> Self {
Self {
strategy: TraversalStrategy::Collapsed,
target_resolution: Resolution::FINE,
coarse_candidates: 50,
k: 10,
include_context: false,
}
}
}
impl HierarchicalConfig {
pub fn collapsed(k: usize) -> Self {
Self {
strategy: TraversalStrategy::Collapsed,
k,
..Default::default()
}
}
pub fn tree_traversal(k: usize) -> Self {
Self {
strategy: TraversalStrategy::TopDown,
k,
include_context: true,
..Default::default()
}
}
pub fn coarse_to_fine(k: usize, coarse_candidates: usize) -> Self {
Self {
strategy: TraversalStrategy::CoarseToFine,
coarse_candidates,
k,
..Default::default()
}
}
pub fn at_resolution(resolution: Resolution, k: usize) -> Self {
Self {
strategy: TraversalStrategy::FixedResolution,
target_resolution: resolution,
k,
..Default::default()
}
}
}
#[derive(Debug, Clone)]
pub struct HierarchyStats {
pub num_levels: usize,
pub level_sizes: Vec<usize>,
pub total_nodes: usize,
pub avg_fanout: f32,
pub max_fanout: usize,
}
impl HierarchyStats {
pub fn compression_ratio(&self) -> f32 {
if self.total_nodes == 0 {
return 1.0;
}
let leaf_count = self.level_sizes.first().copied().unwrap_or(0);
leaf_count as f32 / self.total_nodes as f32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resolution_levels() {
assert_eq!(Resolution::COARSE.level(), 0.0);
assert_eq!(Resolution::FINE.level(), 1.0);
let mid = Resolution::new(0.5);
assert_eq!(mid.to_tree_level(4), 2);
}
#[test]
fn test_resolution_clamping() {
let above = Resolution::new(1.5);
assert_eq!(above.level(), 1.0);
let below = Resolution::new(-0.5);
assert_eq!(below.level(), 0.0);
}
#[test]
fn test_config_builders() {
let collapsed = HierarchicalConfig::collapsed(10);
assert_eq!(collapsed.strategy, TraversalStrategy::Collapsed);
let c2f = HierarchicalConfig::coarse_to_fine(10, 50);
assert_eq!(c2f.strategy, TraversalStrategy::CoarseToFine);
assert_eq!(c2f.coarse_candidates, 50);
}
}