use logicaffeine_language::semantics::knowledge_graph::HwKnowledgeGraph;
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpecCoverage {
pub signal_coverage: f64,
pub property_coverage: f64,
pub edge_coverage: f64,
pub temporal_coverage: f64,
pub uncovered_signals: Vec<String>,
pub uncovered_properties: Vec<String>,
}
pub fn compute_coverage(kg: &HwKnowledgeGraph, covered_signals: &[String]) -> SpecCoverage {
let total_signals = kg.signals.len();
let total_properties = kg.properties.len();
let total_edges = kg.edges.len();
let covered_count = kg.signals.iter()
.filter(|s| covered_signals.iter().any(|c| c == &s.name))
.count();
let uncovered_signals: Vec<String> = kg.signals.iter()
.filter(|s| !covered_signals.iter().any(|c| c == &s.name))
.map(|s| s.name.clone())
.collect();
let signal_coverage = if total_signals > 0 {
covered_count as f64 / total_signals as f64
} else {
0.0
};
let covered_edges = kg.edges.iter()
.filter(|e| covered_signals.iter().any(|c| c == &e.from)
&& covered_signals.iter().any(|c| c == &e.to))
.count();
let edge_coverage = if total_edges > 0 {
covered_edges as f64 / total_edges as f64
} else {
0.0
};
let covered_props = kg.properties.iter()
.filter(|p| {
kg.edges.iter().any(|e| {
e.property.as_deref() == Some(&p.name)
&& covered_signals.iter().any(|c| c == &e.from)
&& covered_signals.iter().any(|c| c == &e.to)
})
})
.count();
let property_coverage = if total_properties > 0 {
covered_props as f64 / total_properties as f64
} else {
0.0
};
let temporal_props = kg.properties.iter()
.filter(|p| p.property_type == "safety" || p.property_type == "liveness")
.count();
let covered_temporal = kg.properties.iter()
.filter(|p| {
(p.property_type == "safety" || p.property_type == "liveness")
&& kg.edges.iter().any(|e| {
e.property.as_deref() == Some(&p.name)
&& covered_signals.iter().any(|c| c == &e.from)
&& covered_signals.iter().any(|c| c == &e.to)
})
})
.count();
let temporal_coverage = if temporal_props > 0 {
covered_temporal as f64 / temporal_props as f64
} else {
0.0
};
let uncovered_properties: Vec<String> = kg.properties.iter()
.filter(|p| {
!kg.edges.iter().any(|e| {
e.property.as_deref() == Some(&p.name)
&& covered_signals.iter().any(|c| c == &e.from)
&& covered_signals.iter().any(|c| c == &e.to)
})
})
.map(|p| p.name.clone())
.collect();
SpecCoverage {
signal_coverage,
property_coverage,
edge_coverage,
temporal_coverage,
uncovered_signals,
uncovered_properties,
}
}