diff --git a/src/analysis.rs b/src/analysis.rs index c3fc60e..3776727 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -311,36 +311,44 @@ impl Analyzer for NormalAnalyzer { // Evaluate based on negative reports let (negative_reports_mean, negative_reports_sd) = Self::mean_and_std_dev(negative_reports); - let nr_normal = Normal::new(negative_reports_mean, negative_reports_sd); - if negative_reports_sd > 0.0 { - // We use CCDF because more negative reports is worse. - if (1.0 - nr_normal.unwrap().cdf(negative_reports_today as f64)) < alpha { - return true; - } - } else { - // If the standard deviation is 0, we need another option. - // Consider the bridge blocked negative reports increase by - // more than 1 after a long static period. (Note that the - // mean is the exact value because we had no deviation.) - if (negative_reports_today as f64) > negative_reports_mean + 1.0 { - return true; + + // Only use CCDF test if today's numbers are worse than average + if (negative_reports_today as f64) > negative_reports_mean { + let nr_normal = Normal::new(negative_reports_mean, negative_reports_sd); + if negative_reports_sd > 0.0 { + // We use CCDF because more negative reports is worse. + if (1.0 - nr_normal.unwrap().cdf(negative_reports_today as f64)) < alpha { + return true; + } + } else { + // If the standard deviation is 0, we need another option. + // Consider the bridge blocked negative reports increase by + // more than 1 after a long static period. (Note that the + // mean is the exact value because we had no deviation.) + if (negative_reports_today as f64) > negative_reports_mean + 1.0 { + return true; + } } } // Evaluate based on bridge stats let (bridge_ips_mean, bridge_ips_sd) = Self::mean_and_std_dev(bridge_ips); - let bip_normal = Normal::new(bridge_ips_mean, bridge_ips_sd); - if bridge_ips_sd > 0.0 { - if bip_normal.unwrap().cdf(bridge_ips_today as f64) < alpha { - return true; - } - } else { - // If the standard deviation is 0, we need another option. - // Consider the bridge blocked if its usage dropped by more - // than 1 bin. (Note that the mean is the exact value - // because we had no deviation.) - if (bridge_ips_today as f64) < bridge_ips_mean - 8.0 { - return true; + + // Only use CDF test if today's numbers are worse than average + if (bridge_ips_today as f64) < bridge_ips_mean { + let bip_normal = Normal::new(bridge_ips_mean, bridge_ips_sd); + if bridge_ips_sd > 0.0 { + if bip_normal.unwrap().cdf(bridge_ips_today as f64) < alpha { + return true; + } + } else { + // If the standard deviation is 0, we need another option. + // Consider the bridge blocked if its usage dropped by more + // than 1 bin. (Note that the mean is the exact value + // because we had no deviation.) + if (bridge_ips_today as f64) < bridge_ips_mean - 8.0 { + return true; + } } } @@ -370,53 +378,62 @@ impl Analyzer for NormalAnalyzer { // negative reports test first because the positive test may be // expensive. let (negative_reports_mean, negative_reports_sd) = Self::mean_and_std_dev(negative_reports); - let nr_normal = Normal::new(negative_reports_mean, negative_reports_sd); - if negative_reports_sd > 0.0 { - // We use CCDF because more negative reports is worse. - if (1.0 - nr_normal.unwrap().cdf(negative_reports_today as f64)) < alpha { - return true; - } - } else { - // Consider the bridge blocked negative reports increase by - // more than 1 after a long static period. (Note that the - // mean is the exact value because we had no deviation.) - if (negative_reports_today as f64) > negative_reports_mean + 1.0 { - return true; + + // Only use CCDF test if today's numbers are worse than average + if (negative_reports_today as f64) > negative_reports_mean { + let nr_normal = Normal::new(negative_reports_mean, negative_reports_sd); + if negative_reports_sd > 0.0 { + // We use CCDF because more negative reports is worse. + if (1.0 - nr_normal.unwrap().cdf(negative_reports_today as f64)) < alpha { + return true; + } + } else { + // Consider the bridge blocked negative reports increase by + // more than 1 after a long static period. (Note that the + // mean is the exact value because we had no deviation.) + if (negative_reports_today as f64) > negative_reports_mean + 1.0 { + return true; + } } } // Evaluate based on bridge stats and positive reports. let (mean_vec, cov_mat) = Self::stats(&[bridge_ips, positive_reports]); - let mvn = MultivariateNormal::new(mean_vec, cov_mat); - if mvn.is_ok() { - let mvn = mvn.unwrap(); - // Estimate the CDF by integrating the PDF by hand with step - // size 1 - let mut cdf = 0.0; - for bip in 0..bridge_ips_today { - for pr in 0..positive_reports_today { - cdf += mvn.pdf(&DVector::from_vec(vec![bip as f64, pr as f64])); + // Only use CDF test if today's numbers are worse than average + if (bridge_ips_today as f64) < mean_vec[0] || (positive_reports_today as f64) < mean_vec[1] + { + let mvn = MultivariateNormal::new(mean_vec, cov_mat); + if mvn.is_ok() { + let mvn = mvn.unwrap(); + + // Estimate the CDF by integrating the PDF by hand with step + // size 1 + let mut cdf = 0.0; + for bip in 0..bridge_ips_today { + for pr in 0..positive_reports_today { + cdf += mvn.pdf(&DVector::from_vec(vec![bip as f64, pr as f64])); + } + } + if cdf < alpha { + return true; + } + } else { + // If we have 0 standard deviation or a covariance matrix + // that is not positive definite, we need another way to + // evaluate each variable. Ignore positive reports and + // compute as in stage 2 + if self.stage_two( + confidence, + bridge_ips, + bridge_ips_today, + negative_reports, + negative_reports_today, + ) { + return true; } } - if cdf < alpha { - return true; - } - } else { - // If we have 0 standard deviation or a covariance matrix - // that is not positive definite, we need another way to - // evaluate each variable. Ignore positive reports and - // compute as in stage 2 - if self.stage_two( - confidence, - bridge_ips, - bridge_ips_today, - negative_reports, - negative_reports_today, - ) { - return true; - } - }; + } // If none of the tests concluded that the bridge is blocked, // return false