reputation-core 0.1.0

Core calculation engine for the KnowThat Reputation System with advanced scoring algorithms
Documentation
//! Confidence calculation module
//! 
//! This module handles the calculation of confidence scores based on the
//! volume of interactions.

use crate::error::{CalculationError, Result};

/// Calculates the confidence score based on interaction volume
/// 
/// The confidence score represents how much we trust the empirical data.
/// It grows asymptotically towards 1.0 as interactions increase.
/// 
/// # Formula
/// 
/// `confidence = n / (n + k)`
/// 
/// Where:
/// - `n` = number of interactions
/// - `k` = confidence growth parameter (default 15.0)
/// 
/// # Properties
/// 
/// - When n = 0, confidence = 0
/// - When n = k, confidence = 0.5
/// - As n → ∞, confidence → 1.0
/// 
/// # Errors
/// 
/// Returns an error if the calculated confidence is outside [0, 1] bounds,
/// which should only happen due to numerical errors.
#[inline(always)]
pub(crate) fn calculate_confidence(interactions: u32, confidence_k: f64) -> Result<f64> {
    let n = interactions as f64;
    let confidence = n / (n + confidence_k);
    
    // Ensure confidence is within bounds
    if confidence < 0.0 || confidence > 1.0 {
        return Err(CalculationError::ConfidenceOutOfBounds(confidence).into());
    }
    
    Ok(confidence)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_zero_interactions() {
        let confidence = calculate_confidence(0, 15.0).unwrap();
        assert_eq!(confidence, 0.0);
    }

    #[test]
    fn test_confidence_at_k() {
        // When interactions = k, confidence should be 0.5
        let confidence = calculate_confidence(15, 15.0).unwrap();
        assert!((confidence - 0.5).abs() < 0.001);
        
        // Test with different k values
        let confidence = calculate_confidence(30, 30.0).unwrap();
        assert!((confidence - 0.5).abs() < 0.001);
    }

    #[test]
    fn test_confidence_growth() {
        let k = 15.0;
        
        // Test increasing interactions
        let conf_10 = calculate_confidence(10, k).unwrap();
        let conf_50 = calculate_confidence(50, k).unwrap();
        let conf_100 = calculate_confidence(100, k).unwrap();
        let conf_1000 = calculate_confidence(1000, k).unwrap();
        
        // Confidence should increase monotonically
        assert!(conf_10 < conf_50);
        assert!(conf_50 < conf_100);
        assert!(conf_100 < conf_1000);
        
        // Check specific values
        assert!((conf_10 - 0.4).abs() < 0.01);      // 10/(10+15) = 0.4
        assert!((conf_50 - 0.769).abs() < 0.01);    // 50/(50+15) ≈ 0.769
        assert!((conf_100 - 0.869).abs() < 0.01);   // 100/(100+15) ≈ 0.869
        assert!((conf_1000 - 0.985).abs() < 0.01);  // 1000/(1000+15) ≈ 0.985
    }

    #[test]
    fn test_different_k_values() {
        let interactions = 100;
        
        // Smaller k = faster confidence growth
        let conf_k5 = calculate_confidence(interactions, 5.0).unwrap();
        let conf_k15 = calculate_confidence(interactions, 15.0).unwrap();
        let conf_k30 = calculate_confidence(interactions, 30.0).unwrap();
        
        assert!(conf_k5 > conf_k15);
        assert!(conf_k15 > conf_k30);
        
        // Check specific values
        assert!((conf_k5 - 0.952).abs() < 0.01);   // 100/(100+5) ≈ 0.952
        assert!((conf_k15 - 0.869).abs() < 0.01);  // 100/(100+15) ≈ 0.869
        assert!((conf_k30 - 0.769).abs() < 0.01);  // 100/(100+30) ≈ 0.769
    }

    #[test]
    fn test_confidence_bounds() {
        // Test that confidence is always in [0, 1]
        let k = 15.0;
        
        for interactions in [0, 1, 10, 100, 1000, 10000, u32::MAX] {
            let confidence = calculate_confidence(interactions, k).unwrap();
            assert!(confidence >= 0.0);
            assert!(confidence <= 1.0);
        }
    }

    #[test]
    fn test_very_small_k() {
        // With very small k, confidence should grow very quickly
        let conf = calculate_confidence(1, 0.1).unwrap();
        assert!((conf - 0.909).abs() < 0.01); // 1/(1+0.1) ≈ 0.909
        
        let conf = calculate_confidence(10, 0.1).unwrap();
        assert!((conf - 0.990).abs() < 0.01); // 10/(10+0.1) ≈ 0.990
    }

    #[test]
    fn test_very_large_k() {
        // With very large k, confidence should grow very slowly
        let conf = calculate_confidence(100, 1000.0).unwrap();
        assert!((conf - 0.091).abs() < 0.01); // 100/(100+1000) ≈ 0.091
        
        let conf = calculate_confidence(1000, 1000.0).unwrap();
        assert!((conf - 0.5).abs() < 0.01); // 1000/(1000+1000) = 0.5
    }
}