const MIN_SAMPLE_EPSILON: f64 = 1e-12;
const CONFIDENCE_SHRINKAGE_Z: f64 = 1.281_551_565_545;
const SAMPLE_GROWTH_SCALE: f64 = 8.0;
pub const MIN_SAMPLES_PER_SIGNATURE: usize = 3;
#[derive(Debug, Clone, Copy)]
pub struct ConfidenceEnvelope {
pub raw_confidence: f64,
pub coverage_fraction: f64,
pub sample_units: f64,
pub signature_count: usize,
}
impl ConfidenceEnvelope {
pub fn bounded_sample_factor(self, min_samples_per_signature: usize) -> f64 {
sample_sufficiency_factor(
self.sample_units,
self.signature_count,
min_samples_per_signature,
)
.clamp(0.0, 1.0)
}
pub fn bounded_confidence(self, min_samples_per_signature: usize) -> f64 {
advisory_confidence(
self.raw_confidence,
self.coverage_fraction,
self.sample_units,
self.signature_count,
min_samples_per_signature,
)
}
}
pub fn sample_sufficiency_factor(
sample_units: f64,
signature_count: usize,
min_samples_per_signature: usize,
) -> f64 {
if signature_count == 0 || min_samples_per_signature == 0 {
return 0.0;
}
let required = (signature_count * min_samples_per_signature) as f64;
if required <= MIN_SAMPLE_EPSILON {
return 0.0;
}
(sample_units / required).clamp(0.0, 1.0)
}
pub fn advisory_confidence(
base_confidence: f64,
coverage_fraction: f64,
sample_units: f64,
signature_count: usize,
min_samples_per_signature: usize,
) -> f64 {
let bounded_confidence = base_confidence.clamp(0.0, 1.0);
let bounded_coverage = coverage_fraction.clamp(0.0, 1.0);
let sufficiency =
sample_sufficiency_factor(sample_units, signature_count, min_samples_per_signature);
let effective_samples = effective_sample_size(sample_units);
let sample_weight = sample_factor(effective_samples);
(bounded_confidence.min(bounded_coverage) * sample_weight * sufficiency).clamp(0.0, 1.0)
}
pub fn posterior_mean(alpha: f64, beta: f64) -> f64 {
let total = (alpha + beta).max(MIN_SAMPLE_EPSILON);
(alpha / total).clamp(0.0, 1.0)
}
pub fn posterior_variance(alpha: f64, beta: f64) -> f64 {
let total = (alpha + beta).max(MIN_SAMPLE_EPSILON);
(alpha * beta) / ((total * total) * (total + 1.0)).max(MIN_SAMPLE_EPSILON)
}
pub fn conservative_reliability(alpha: f64, beta: f64) -> f64 {
let mean = posterior_mean(alpha, beta);
let std_dev = posterior_variance(alpha, beta).sqrt();
let lower = mean - (CONFIDENCE_SHRINKAGE_Z * std_dev);
lower.clamp(0.0, 1.0)
}
pub fn effective_sample_size(total_samples: f64) -> f64 {
(total_samples - 2.0).max(0.0)
}
pub fn sample_factor(effective_sample_size: f64) -> f64 {
(1.0 - (-effective_sample_size / SAMPLE_GROWTH_SCALE).exp()).clamp(0.0, 1.0)
}