use serde::{Deserialize, Serialize};
pub fn noisy_or(confidences: &[f64]) -> f64 {
if confidences.is_empty() {
return 0.0;
}
1.0 - confidences
.iter()
.map(|c| 1.0 - c.clamp(0.0, 1.0))
.product::<f64>()
}
pub fn temporal_decay(base_confidence: f64, lambda: f64, days_since_verification: f64) -> f64 {
let decay_factor = (-lambda * days_since_verification).exp();
(base_confidence * decay_factor).clamp(0.0, 1.0)
}
pub fn bayesian_update(prior: f64, likelihood_ratio: f64) -> f64 {
let odds = prior / (1.0 - prior + f64::EPSILON);
let posterior_odds = odds * likelihood_ratio;
let posterior = posterior_odds / (1.0 + posterior_odds);
posterior.clamp(0.0, 1.0)
}
pub fn chain_confidence(edge_confidences: &[f64]) -> f64 {
edge_confidences.iter().product()
}
pub fn multi_path_confidence(paths: &[Vec<f64>]) -> f64 {
let path_confidences: Vec<f64> = paths.iter().map(|path| chain_confidence(path)).collect();
noisy_or(&path_confidences)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfidenceReport {
pub raw_score: f64,
pub decayed_score: f64,
pub tier: String,
pub sources: Vec<SourceContribution>,
pub days_since_verified: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceContribution {
pub source: String,
pub individual_confidence: f64,
pub observed_at: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_noisy_or_single_source() {
assert!((noisy_or(&[0.8]) - 0.8).abs() < f64::EPSILON);
}
#[test]
fn test_noisy_or_compounds_agreement() {
let result = noisy_or(&[0.8, 0.8]);
assert!((result - 0.96).abs() < 0.001);
}
#[test]
fn test_noisy_or_empty() {
assert_eq!(noisy_or(&[]), 0.0);
}
#[test]
fn test_temporal_decay() {
assert!((temporal_decay(0.95, 0.01, 0.0) - 0.95).abs() < 0.001);
let decayed = temporal_decay(0.95, 0.01, 30.0);
assert!(decayed < 0.95);
assert!(decayed > 0.5);
let import_decay = temporal_decay(0.95, 0.001, 365.0);
assert!(import_decay > 0.6);
}
#[test]
fn test_bayesian_update() {
let posterior = bayesian_update(0.5, 10.0);
assert!(posterior > 0.9);
let posterior = bayesian_update(0.5, 0.1);
assert!(posterior < 0.15);
}
#[test]
fn test_chain_confidence() {
let chain = chain_confidence(&[0.95, 0.90, 0.80]);
assert!((chain - 0.684).abs() < 0.001);
}
#[test]
fn test_multi_path() {
let result = multi_path_confidence(&[vec![0.9, 0.8], vec![0.7, 0.6]]);
assert!((result - 0.8376).abs() < 0.001);
}
}