use serde::{Deserialize, Serialize};
use super::witness::ConvergenceWitness;
use super::graph::PadicAddr;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sematon<T: Clone> {
pub id: String,
pub payload: T,
pub witness: ConvergenceWitness,
pub address: PadicAddr,
pub entropy: f32,
pub density: f32,
pub impedance: f32,
pub shape_hash: u32,
pub constructive: bool,
pub source: String,
}
static SEMATON_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
impl<T: Clone + Serialize> Sematon<T> {
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);
let json = serde_json::to_string(&payload).unwrap_or_default();
let entropy = Self::payload_entropy(&json);
let density = Self::payload_density(&json, entropy);
let impedance = if density > 0.0 && witness.r > 0.0 {
entropy / (density * witness.r)
} else {
f32::INFINITY
};
let hash_input = format!(
"{}|{}|{:.6}|{}|{}",
source,
json,
witness.r,
witness.converged,
address.to_string()
);
let shape_hash = fnv1a_str(&hash_input);
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(),
}
}
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 {
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);
}
}