#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Rule {
Pure,
Modified50,
Modified51,
Contributory,
}
impl Rule {
pub fn bars_recovery(self, fault: f64) -> bool {
match self {
Rule::Pure => false,
Rule::Modified50 => fault >= 0.50,
Rule::Modified51 => fault > 0.50,
Rule::Contributory => fault > 0.0,
}
}
}
pub fn net_recovery(gross: f64, fault: f64, rule: Rule) -> f64 {
if gross <= 0.0 {
return 0.0;
}
let fault = fault.clamp(0.0, 1.0);
if rule.bars_recovery(fault) {
return 0.0;
}
gross * (1.0 - fault)
}
pub fn reduction_amount(gross: f64, fault: f64, rule: Rule) -> f64 {
if gross <= 0.0 {
return 0.0;
}
gross - net_recovery(gross, fault, rule)
}
pub fn bar_threshold(rule: Rule) -> Option<f64> {
match rule {
Rule::Pure => None,
Rule::Modified50 => Some(0.50),
Rule::Modified51 => Some(0.5000001),
Rule::Contributory => Some(0.0),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pure_has_no_cutoff() {
assert!((net_recovery(100_000.0, 0.90, Rule::Pure) - 10_000.0).abs() < 1e-6);
assert!(!Rule::Pure.bars_recovery(0.99));
}
#[test]
fn modified_rules_differ_at_exactly_fifty() {
assert_eq!(net_recovery(100_000.0, 0.50, Rule::Modified50), 0.0);
assert!((net_recovery(100_000.0, 0.50, Rule::Modified51) - 50_000.0).abs() < 1e-6);
}
#[test]
fn modified_51_bars_above_half() {
assert_eq!(net_recovery(100_000.0, 0.51, Rule::Modified51), 0.0);
assert!((net_recovery(100_000.0, 0.49, Rule::Modified51) - 51_000.0).abs() < 1e-6);
}
#[test]
fn contributory_bars_any_fault() {
assert_eq!(net_recovery(100_000.0, 0.01, Rule::Contributory), 0.0);
assert_eq!(net_recovery(100_000.0, 0.0, Rule::Contributory), 100_000.0);
}
#[test]
fn reduction_complements_recovery() {
let g = 250_000.0;
for f in [0.0, 0.1, 0.25, 0.49] {
let net = net_recovery(g, f, Rule::Modified51);
assert!((net + reduction_amount(g, f, Rule::Modified51) - g).abs() < 1e-6);
}
}
#[test]
fn clamps_and_guards() {
assert_eq!(net_recovery(0.0, 0.2, Rule::Pure), 0.0);
assert_eq!(net_recovery(-5.0, 0.2, Rule::Pure), 0.0);
assert_eq!(net_recovery(100.0, -1.0, Rule::Pure), 100.0);
assert_eq!(net_recovery(100.0, 5.0, Rule::Pure), 0.0);
}
#[test]
fn thresholds() {
assert_eq!(bar_threshold(Rule::Pure), None);
assert_eq!(bar_threshold(Rule::Modified50), Some(0.50));
assert_eq!(bar_threshold(Rule::Contributory), Some(0.0));
}
}