use serde::{Deserialize, Serialize}; /// Universal Slate (per §13.1) — Stage 2 layer 1 only: a dense embedding /// vector. The deeper LLM-comparison layer arrives in Stage 4. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Slate(pub Vec); impl Slate { pub fn dim(&self) -> usize { self.0.len() } pub fn norm(&self) -> f32 { self.0.iter().map(|x| x * x).sum::().sqrt() } /// Cosine similarity in `[-1, 1]`. Returns 0 if either vector is zero or /// dimensions disagree (the latter can happen across embedding model /// changes — see §13 risk #3). pub fn cosine(&self, other: &Slate) -> f32 { if self.0.len() != other.0.len() { return 0.0; } let mut dot = 0.0_f32; let mut a2 = 0.0_f32; let mut b2 = 0.0_f32; for (a, b) in self.0.iter().zip(other.0.iter()) { dot += a * b; a2 += a * a; b2 += b * b; } let denom = (a2.sqrt() * b2.sqrt()).max(1e-9); dot / denom } }