use thiserror::Error;
#[derive(Debug, Error)]
pub enum SriError {
#[error("VEV must be non-negative, got {0}")]
InvalidVev(f64),
}
#[derive(Debug, Clone)]
pub struct SriResult {
pub mrm: u8,
pub vev: f64,
}
#[derive(Debug, Clone)]
pub struct DivergenceReport {
pub prescribed_sri: u8,
pub kernel_sri: u8,
pub sri_difference: u8,
pub prescribed_vev: f64,
pub kernel_vev: f64,
pub flag: DivergenceFlag,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DivergenceFlag {
Green,
Yellow,
Red,
}
#[must_use]
pub fn var_equivalent_volatility(var: f64, z_alpha: f64) -> f64 {
if z_alpha.abs() < f64::EPSILON {
return 0.0;
}
var / z_alpha
}
pub fn classify_mrm(vev: f64) -> Result<SriResult, SriError> {
if vev.is_nan() || vev < 0.0 {
return Err(SriError::InvalidVev(vev));
}
let vev_pct = vev * 100.0;
let mrm = if vev_pct < 0.5 {
1
} else if vev_pct < 5.0 {
2
} else if vev_pct < 12.0 {
3
} else if vev_pct < 20.0 {
4
} else if vev_pct < 30.0 {
5
} else if vev_pct < 80.0 {
6
} else {
7
};
Ok(SriResult { mrm, vev })
}
pub fn divergence_report(
prescribed_vev: f64,
kernel_vev: f64,
) -> Result<DivergenceReport, SriError> {
let prescribed = classify_mrm(prescribed_vev)?;
let kernel = classify_mrm(kernel_vev)?;
let sri_difference = prescribed.mrm.abs_diff(kernel.mrm);
let flag = match sri_difference {
0 => DivergenceFlag::Green,
1 => DivergenceFlag::Yellow,
_ => DivergenceFlag::Red,
};
Ok(DivergenceReport {
prescribed_sri: prescribed.mrm,
kernel_sri: kernel.mrm,
sri_difference,
prescribed_vev,
kernel_vev,
flag,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mrm_1() {
let result = classify_mrm(0.003).unwrap();
assert_eq!(result.mrm, 1);
}
#[test]
fn test_mrm_2() {
assert_eq!(classify_mrm(0.005).unwrap().mrm, 2);
assert_eq!(classify_mrm(0.03).unwrap().mrm, 2);
assert_eq!(classify_mrm(0.049).unwrap().mrm, 2);
}
#[test]
fn test_mrm_3() {
assert_eq!(classify_mrm(0.05).unwrap().mrm, 3);
assert_eq!(classify_mrm(0.10).unwrap().mrm, 3);
}
#[test]
fn test_mrm_4() {
assert_eq!(classify_mrm(0.12).unwrap().mrm, 4);
assert_eq!(classify_mrm(0.15).unwrap().mrm, 4);
}
#[test]
fn test_mrm_5() {
assert_eq!(classify_mrm(0.20).unwrap().mrm, 5);
assert_eq!(classify_mrm(0.25).unwrap().mrm, 5);
}
#[test]
fn test_mrm_6() {
assert_eq!(classify_mrm(0.30).unwrap().mrm, 6);
assert_eq!(classify_mrm(0.50).unwrap().mrm, 6);
}
#[test]
fn test_mrm_7() {
assert_eq!(classify_mrm(0.80).unwrap().mrm, 7);
assert_eq!(classify_mrm(1.50).unwrap().mrm, 7);
}
#[test]
fn test_mrm_zero() {
assert_eq!(classify_mrm(0.0).unwrap().mrm, 1);
}
#[test]
fn test_mrm_negative() {
assert!(classify_mrm(-0.01).is_err());
}
#[test]
fn test_mrm_nan() {
assert!(classify_mrm(f64::NAN).is_err());
}
#[test]
fn test_vev_computation() {
let vev = var_equivalent_volatility(0.3099, 1.96);
assert!((vev - 0.1581).abs() < 0.001);
}
#[test]
fn test_divergence_green() {
let report = divergence_report(0.15, 0.16).unwrap();
assert_eq!(report.prescribed_sri, 4);
assert_eq!(report.kernel_sri, 4);
assert_eq!(report.flag, DivergenceFlag::Green);
}
#[test]
fn test_divergence_yellow() {
let report = divergence_report(0.15, 0.22).unwrap();
assert_eq!(report.prescribed_sri, 4);
assert_eq!(report.kernel_sri, 5);
assert_eq!(report.sri_difference, 1);
assert_eq!(report.flag, DivergenceFlag::Yellow);
}
#[test]
fn test_divergence_red() {
let report = divergence_report(0.03, 0.15).unwrap();
assert_eq!(report.prescribed_sri, 2);
assert_eq!(report.kernel_sri, 4);
assert_eq!(report.sri_difference, 2);
assert_eq!(report.flag, DivergenceFlag::Red);
}
}