use crate::hypotheses::Hypothesis;
use crate::universe::InformationUniverse;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ResearchRecord<U: InformationUniverse> {
pub version: String,
pub elapsed_seconds: f64,
pub config: HashMap<String, String>,
pub thresholds: HashMap<String, f64>,
pub results: Vec<UniverseResult>,
pub hypotheses: Vec<HypothesisRecord>,
pub boolean_verifications: HashMap<String, usize>,
pub failure_conditions: Vec<String>,
_phantom: std::marker::PhantomData<U>,
}
#[derive(Debug, Clone)]
pub struct UniverseResult {
pub universe_id: usize,
pub structured_ratio: f64,
pub n_rules: usize,
pub rule_names: Vec<String>,
pub persistence: f64,
pub storage: f64,
pub memory: f64,
}
#[derive(Debug, Clone)]
pub struct HypothesisRecord {
pub name: String,
pub condition_desc: String,
pub property_name: String,
pub complexity: f64,
pub accuracy: f64,
pub score: f64,
pub survives: bool,
}
impl<R> From<&Hypothesis<R>> for HypothesisRecord {
fn from(h: &Hypothesis<R>) -> Self {
Self {
name: h.name.clone(),
condition_desc: h.condition_desc.clone(),
property_name: h.property_name.clone(),
complexity: h.complexity,
accuracy: h.accuracy,
score: h.score,
survives: h.survives(),
}
}
}
impl<U: InformationUniverse> ResearchRecord<U> {
pub fn new(version: impl Into<String>) -> Self {
Self {
version: version.into(),
elapsed_seconds: 0.0,
config: HashMap::new(),
thresholds: HashMap::new(),
results: Vec::new(),
hypotheses: Vec::new(),
boolean_verifications: HashMap::new(),
failure_conditions: Vec::new(),
_phantom: std::marker::PhantomData,
}
}
pub fn n_persistent(&self) -> usize {
let threshold = self
.thresholds
.get("persistence")
.copied()
.unwrap_or(f64::MAX);
self.results
.iter()
.filter(|r| r.persistence > threshold)
.count()
}
pub fn n_storage(&self) -> usize {
let threshold = self.thresholds.get("storage").copied().unwrap_or(f64::MAX);
self.results
.iter()
.filter(|r| r.storage > threshold)
.count()
}
pub fn n_memory(&self) -> usize {
let threshold = self.thresholds.get("memory").copied().unwrap_or(f64::MAX);
self.results.iter().filter(|r| r.memory > threshold).count()
}
pub fn summary(&self) -> String {
let mut lines = Vec::new();
lines.push(format!("ARCO v{} — Scientific Cycle Report", self.version));
lines.push("=".repeat(60));
lines.push(format!("Universes: {}", self.results.len()));
lines.push(format!("Duration: {:.1}s", self.elapsed_seconds));
lines.push(String::new());
lines.push("Emergence (above calibrated thresholds):".to_string());
lines.push(format!(
" Storage: {}/{} ({:.1}%)",
self.n_storage(),
self.results.len(),
100.0 * self.n_storage() as f64 / self.results.len() as f64,
));
lines.push(format!(
" Memory: {}/{} ({:.1}%)",
self.n_memory(),
self.results.len(),
100.0 * self.n_memory() as f64 / self.results.len() as f64,
));
lines.push(String::new());
lines.push(format!("Hypotheses tested: {}", self.hypotheses.len()));
let surviving: Vec<&HypothesisRecord> =
self.hypotheses.iter().filter(|h| h.survives).collect();
lines.push(format!("Hypotheses survived: {}", surviving.len()));
if !surviving.is_empty() {
lines.push(String::new());
lines.push("Surviving hypotheses:".to_string());
for h in surviving {
lines.push(format!(
" {}: {} (acc={:.3}, score={:.3})",
h.name, h.condition_desc, h.accuracy, h.score,
));
}
}
if !self.boolean_verifications.is_empty() {
lines.push(String::new());
lines.push("Boolean functions verified:".to_string());
let mut gates: Vec<(&String, &usize)> = self.boolean_verifications.iter().collect();
gates.sort_by(|a, b| a.0.cmp(b.0));
for (gate, count) in gates {
lines.push(format!(" {}: {}", gate, count));
}
}
if !self.failure_conditions.is_empty() {
lines.push(String::new());
lines.push("FAILURE CONDITIONS TRIGGERED:".to_string());
for fc in &self.failure_conditions {
lines.push(format!(" ! {}", fc));
}
}
lines.join("\n")
}
}
impl<U: InformationUniverse> Default for ResearchRecord<U> {
fn default() -> Self {
Self::new(env!("CARGO_PKG_VERSION"))
}
}