terminals-core 0.1.0

Core runtime primitives for Terminals OS: phase dynamics, AXON wire protocol, substrate engine, and sematonic types
Documentation
//! ComputeAtom — Contiguous memory unit with typed projection views.
//!
//! A ComputeAtom owns a byte buffer whose layout is determined by a
//! ProjectionLayout. Projections are read/written via typed accessors.
//! The shape_hash (σ) is recomputed on mutation.

use super::layout::ProjectionLayout;
use super::projection::{Projection, ProjectionId};
use super::splat::SplatProjection;
use super::kuramoto::KuramotoProjection;
use super::expert::ExpertProjection;
use super::graph::GraphProjection;
use super::thermal::ThermalProjection;

/// FNV-1a hash over a byte slice.
fn fnv1a(data: &[u8]) -> u32 {
    let mut hash = 0x811c_9dc5u32;
    for &byte in data {
        hash ^= byte as u32;
        hash = hash.wrapping_mul(0x0100_0193);
    }
    hash
}

/// A single computational atom in the substrate.
#[derive(Debug, Clone)]
pub struct ComputeAtom {
    /// Contiguous memory backing all projections.
    pub buffer: Vec<u8>,
    /// Frozen layout determining projection offsets.
    pub layout: ProjectionLayout,
    /// Shape hash σ — deterministic content address.
    pub shape_hash: u32,
}

impl ComputeAtom {
    /// Create a zero-initialized atom with the given layout.
    /// All projections start at their default (void instantiation).
    pub fn new(layout: ProjectionLayout) -> Self {
        let buffer = vec![0u8; layout.stride];
        let mut atom = Self {
            buffer,
            layout,
            shape_hash: 0,
        };
        atom.recompute_hash();
        atom
    }

    /// Create N atoms sharing the same layout.
    pub fn create_n(layout: &ProjectionLayout, n: usize) -> Vec<Self> {
        (0..n).map(|_| Self::new(layout.clone())).collect()
    }

    /// Read a projection from this atom. Returns None if projection not in layout.
    pub fn read_projection<P: Projection>(&self) -> Option<P> {
        let offset = self.layout.offset_of(P::id())?;
        Some(P::read(&self.buffer[offset..]))
    }

    /// Write a projection into this atom. Returns false if projection not in layout.
    pub fn write_projection<P: Projection>(&mut self, proj: &P) -> bool {
        if let Some(offset) = self.layout.offset_of(P::id()) {
            proj.write(&mut self.buffer[offset..]);
            self.recompute_hash();
            true
        } else {
            false
        }
    }

    /// Get a read-only byte slice for a projection's region.
    pub fn projection_bytes(&self, id: ProjectionId) -> Option<&[u8]> {
        let offset = self.layout.offset_of(id)?;
        let size = 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(),
        };
        Some(&self.buffer[offset..offset + size])
    }

    /// Recompute shape hash from all active projection contributions.
    fn recompute_hash(&mut self) {
        // Hash the full buffer — captures all projection state
        self.shape_hash = fnv1a(&self.buffer);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_atom_full_layout() {
        let atom = ComputeAtom::new(ProjectionLayout::full());
        assert_eq!(atom.buffer.len(), 1612);
        assert!(atom.read_projection::<SplatProjection>().is_some());
        assert!(atom.read_projection::<KuramotoProjection>().is_some());
        assert!(atom.read_projection::<ExpertProjection>().is_some());
        assert!(atom.read_projection::<GraphProjection>().is_some());
        assert!(atom.read_projection::<ThermalProjection>().is_some());
    }

    #[test]
    fn test_atom_minimal_layout() {
        let atom = ComputeAtom::new(ProjectionLayout::minimal());
        assert!(atom.read_projection::<SplatProjection>().is_some());
        assert!(atom.read_projection::<KuramotoProjection>().is_some());
        assert!(atom.read_projection::<ExpertProjection>().is_none());
        assert!(atom.read_projection::<GraphProjection>().is_none());
    }

    #[test]
    fn test_atom_write_read_roundtrip() {
        let mut atom = ComputeAtom::new(ProjectionLayout::full());

        let k = KuramotoProjection {
            theta: 1.5,
            omega: 0.3,
            coupling: 2.0,
        };
        assert!(atom.write_projection(&k));

        let restored = atom.read_projection::<KuramotoProjection>().unwrap();
        assert!((restored.theta - 1.5).abs() < 1e-6);
        assert!((restored.omega - 0.3).abs() < 1e-6);
    }

    #[test]
    fn test_atom_shape_hash_changes_on_write() {
        let mut atom = ComputeAtom::new(ProjectionLayout::full());
        let hash_before = atom.shape_hash;

        let k = KuramotoProjection {
            theta: 3.14,
            omega: 1.0,
            coupling: 1.0,
        };
        atom.write_projection(&k);
        assert_ne!(atom.shape_hash, hash_before);
    }

    #[test]
    fn test_create_n() {
        let layout = ProjectionLayout::minimal();
        let atoms = ComputeAtom::create_n(&layout, 100);
        assert_eq!(atoms.len(), 100);
        assert_eq!(atoms[0].buffer.len(), layout.stride);
    }
}