use super::projection::ProjectionId;
use super::splat::SplatProjection;
use super::kuramoto::KuramotoProjection;
use super::expert::ExpertProjection;
use super::graph::GraphProjection;
use super::thermal::ThermalProjection;
use super::projection::Projection;
#[derive(Debug, Clone)]
pub struct ProjectionLayout {
pub entries: Vec<(ProjectionId, usize)>,
pub stride: usize,
}
impl ProjectionLayout {
pub fn from_projections(ids: &[ProjectionId]) -> Self {
let canonical = [
ProjectionId::Splat,
ProjectionId::Kuramoto,
ProjectionId::Expert,
ProjectionId::Graph,
ProjectionId::Thermal,
];
let mut entries = Vec::new();
let mut offset = 0usize;
for &proj_id in &canonical {
if ids.contains(&proj_id) {
let size = Self::projection_byte_size(proj_id);
entries.push((proj_id, offset));
offset += size;
}
}
Self {
entries,
stride: offset,
}
}
pub fn full() -> Self {
Self::from_projections(&[
ProjectionId::Splat,
ProjectionId::Kuramoto,
ProjectionId::Expert,
ProjectionId::Graph,
ProjectionId::Thermal,
])
}
pub fn minimal() -> Self {
Self::from_projections(&[ProjectionId::Splat, ProjectionId::Kuramoto])
}
pub fn offset_of(&self, id: ProjectionId) -> Option<usize> {
self.entries.iter().find(|(pid, _)| *pid == id).map(|(_, off)| *off)
}
pub fn has(&self, id: ProjectionId) -> bool {
self.entries.iter().any(|(pid, _)| *pid == id)
}
pub fn count(&self) -> usize {
self.entries.len()
}
fn projection_byte_size(id: ProjectionId) -> usize {
match id {
ProjectionId::Splat => SplatProjection::byte_size(),
ProjectionId::Kuramoto => KuramotoProjection::byte_size(),
ProjectionId::Expert => ExpertProjection::byte_size(),
ProjectionId::Graph => GraphProjection::byte_size(),
ProjectionId::Thermal => ThermalProjection::byte_size(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_full_layout_stride() {
let layout = ProjectionLayout::full();
assert_eq!(layout.stride, 1612);
assert_eq!(layout.count(), 5);
}
#[test]
fn test_minimal_layout() {
let layout = ProjectionLayout::minimal();
assert_eq!(layout.stride, 1548);
assert_eq!(layout.count(), 2);
assert!(layout.has(ProjectionId::Splat));
assert!(layout.has(ProjectionId::Kuramoto));
assert!(!layout.has(ProjectionId::Expert));
}
#[test]
fn test_offset_canonical_order() {
let layout = ProjectionLayout::full();
assert_eq!(layout.offset_of(ProjectionId::Splat), Some(0));
assert_eq!(layout.offset_of(ProjectionId::Kuramoto), Some(1536));
assert_eq!(layout.offset_of(ProjectionId::Expert), Some(1548));
assert_eq!(layout.offset_of(ProjectionId::Graph), Some(1560));
}
#[test]
fn test_order_independent() {
let a = ProjectionLayout::from_projections(&[ProjectionId::Graph, ProjectionId::Splat]);
let b = ProjectionLayout::from_projections(&[ProjectionId::Splat, ProjectionId::Graph]);
assert_eq!(a.stride, b.stride);
assert_eq!(a.offset_of(ProjectionId::Splat), b.offset_of(ProjectionId::Splat));
}
}