terminals-core 0.1.0

Core runtime primitives for Terminals OS: phase dynamics, AXON wire protocol, substrate engine, and sematonic types
Documentation
//! Sematon<T> — The Smallest Meaning-Bearing Unit (Rust port).
//!
//! S = (T, W, A, H, c, σ) where:
//!   T = typed payload (centroid embedding of converged cluster)
//!   W = ConvergenceWitness { R, entropy, converged, step }
//!   A = p-adic address
//!   H = Shannon entropy of payload
//!   c = constructive flag (Deutsch-Marletto invariant)
//!   σ = FNV-1a shape hash

use serde::{Deserialize, Serialize};
use super::witness::ConvergenceWitness;
use super::graph::PadicAddr;

/// The smallest meaning-bearing unit with operational consequence.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sematon<T: Clone> {
    /// Unique identifier.
    pub id: String,
    /// The meaning-bearing payload.
    pub payload: T,
    /// Convergence witness at extraction time.
    pub witness: ConvergenceWitness,
    /// P-adic hierarchical address.
    pub address: PadicAddr,
    /// Shannon entropy of payload (bits).
    pub entropy: f32,
    /// Semantic density (bits per token).
    pub density: f32,
    /// Impedance: Z = H / (ρ × R). Resistance to constructive transformation.
    pub impedance: f32,
    /// Deterministic shape hash σ (FNV-1a).
    pub shape_hash: u32,
    /// Constructor flag: can participate in further transformations?
    /// True iff: witness.converged AND entropy > 0 AND payload is non-empty.
    pub constructive: bool,
    /// Source surface that produced this sematon.
    pub source: String,
}

/// Counter for generating unique sematon IDs.
static SEMATON_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);

impl<T: Clone + Serialize> Sematon<T> {
    /// Create a new sematon from its components.
    pub fn new(
        payload: T,
        witness: ConvergenceWitness,
        address: PadicAddr,
        source: &str,
    ) -> Self {
        let counter = SEMATON_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
        let id = format!("sem_rust_{}", counter);

        // Compute entropy from serialized payload
        let json = serde_json::to_string(&payload).unwrap_or_default();
        let entropy = Self::payload_entropy(&json);
        let density = Self::payload_density(&json, entropy);

        // Impedance: Z = H / (ρ × R)
        let impedance = if density > 0.0 && witness.r > 0.0 {
            entropy / (density * witness.r)
        } else {
            f32::INFINITY
        };

        // Shape hash from canonical representation
        let hash_input = format!(
            "{}|{}|{:.6}|{}|{}",
            source,
            json,
            witness.r,
            witness.converged,
            address.to_string()
        );
        let shape_hash = fnv1a_str(&hash_input);

        // Constructor flag: Deutsch-Marletto invariant
        let constructive = witness.converged
            && entropy.is_finite()
            && entropy > 0.0
            && !json.is_empty()
            && json != "null";

        Self {
            id,
            payload,
            witness,
            address,
            entropy,
            density,
            impedance,
            shape_hash,
            constructive,
            source: source.to_string(),
        }
    }

    /// Check if this sematon can participate in further transformations.
    pub fn is_realizable(&self) -> bool {
        self.constructive
    }

    fn payload_entropy(json: &str) -> f32 {
        if json.is_empty() {
            return 0.0;
        }
        let mut freq = [0u32; 256];
        for &byte in json.as_bytes() {
            freq[byte as usize] += 1;
        }
        let total = json.len() as f32;
        let mut h = 0.0f32;
        for &count in &freq {
            if count > 0 {
                let p = count as f32 / total;
                h -= p * p.log2();
            }
        }
        h
    }

    fn payload_density(json: &str, entropy: f32) -> f32 {
        // Approximate token count by splitting on non-word boundaries
        let token_count = json
            .split(|c: char| c.is_whitespace() || "{}[]\":,".contains(c))
            .filter(|s| !s.is_empty())
            .count();
        if token_count > 0 {
            entropy / token_count as f32
        } else {
            0.0
        }
    }
}

fn fnv1a_str(input: &str) -> u32 {
    let mut hash = 0x811c_9dc5u32;
    for &byte in input.as_bytes() {
        hash ^= byte as u32;
        hash = hash.wrapping_mul(0x0100_0193);
    }
    hash
}

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

    #[test]
    fn test_sematon_constructive_when_converged() {
        let w = ConvergenceWitness {
            r: 0.95,
            entropy: 0.3,
            converged: true,
            step: 10,
        };
        let s = Sematon::new(
            vec![1.0f32, 2.0, 3.0],
            w,
            PadicAddr { base: 0, coeff0: 0, coeff1: 1 },
            "test",
        );
        assert!(s.constructive);
        assert!(s.is_realizable());
        assert!(s.entropy > 0.0);
        assert!(s.impedance.is_finite());
    }

    #[test]
    fn test_sematon_not_constructive_when_unconverged() {
        let w = ConvergenceWitness {
            r: 0.3,
            entropy: 0.8,
            converged: false,
            step: 5,
        };
        let s = Sematon::new(
            "hello".to_string(),
            w,
            PadicAddr::default(),
            "test",
        );
        assert!(!s.constructive);
        assert!(!s.is_realizable());
    }

    #[test]
    fn test_sematon_unique_ids() {
        let w = ConvergenceWitness { r: 0.9, entropy: 0.1, converged: true, step: 1 };
        let a = Sematon::new(42u32, w, PadicAddr::default(), "test");
        let b = Sematon::new(42u32, w, PadicAddr::default(), "test");
        assert_ne!(a.id, b.id);
    }

    #[test]
    fn test_sematon_impedance_infinite_when_zero_r() {
        let w = ConvergenceWitness { r: 0.0, entropy: 0.5, converged: false, step: 0 };
        let s = Sematon::new(vec![1.0f32], w, PadicAddr::default(), "test");
        assert!(s.impedance.is_infinite());
    }

    #[test]
    fn test_sematon_shape_hash_varies() {
        let w = ConvergenceWitness { r: 0.9, entropy: 0.1, converged: true, step: 1 };
        let a = Sematon::new(1u32, w, PadicAddr::default(), "test");
        let b = Sematon::new(2u32, w, PadicAddr::default(), "test");
        assert_ne!(a.shape_hash, b.shape_hash);
    }
}