#![cfg(feature = "std")]
#![allow(clippy::needless_range_loop)]
extern crate std;
use std::vec::Vec;
use crate::config::EngineConfig;
use crate::types::MotifClass;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MotifThresholdRecommendation {
pub motif: MotifClass,
pub recommended_drift_threshold: f64,
pub recommended_slew_threshold: f64,
pub percentile: f64,
}
#[derive(Debug, Clone)]
pub struct CalibrationReport {
pub config: EngineConfig,
pub motif_recommendations: Vec<MotifThresholdRecommendation>,
pub healthy_stats: HealthyStats,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct HealthyStats {
pub mean_residual_norm: f64,
pub p50_residual_norm: f64,
pub p90_residual_norm: f64,
pub p99_residual_norm: f64,
pub mean_drift_magnitude: f64,
pub p90_drift_magnitude: f64,
pub mean_slew_magnitude: f64,
pub p90_slew_magnitude: f64,
pub num_windows: usize,
pub num_signals: usize,
}
pub fn recommend_config_from_healthy(
healthy_data: &[f64],
num_signals: usize,
num_windows: usize,
percentile: f64,
) -> CalibrationReport {
let pct = percentile.clamp(0.5, 0.999);
let mut means = std::vec![0.0_f64; num_signals];
let mut counts = std::vec![0_usize; num_signals];
for w in 0..num_windows {
for s in 0..num_signals {
let idx = w * num_signals + s;
if idx < healthy_data.len() {
let v = healthy_data[idx];
if !v.is_nan() {
means[s] += v;
counts[s] += 1;
}
}
}
}
for s in 0..num_signals {
if counts[s] > 0 {
means[s] /= counts[s] as f64;
}
}
let mut norms: Vec<f64> = Vec::with_capacity(num_windows * num_signals);
for w in 0..num_windows {
for s in 0..num_signals {
let idx = w * num_signals + s;
if idx < healthy_data.len() {
let v = healthy_data[idx];
if !v.is_nan() {
let r = (v - means[s]).abs();
norms.push(r);
}
}
}
}
let mut drifts: Vec<f64> = Vec::new();
let mut slews: Vec<f64> = Vec::new();
for s in 0..num_signals {
let mut prev_norm = 0.0;
let mut prev_drift = 0.0;
for w in 0..num_windows {
let idx = w * num_signals + s;
if idx < healthy_data.len() {
let v = healthy_data[idx];
if !v.is_nan() {
let n = (v - means[s]).abs();
if w > 0 {
let d = (n - prev_norm).abs();
drifts.push(d);
if w > 1 {
let sl = (d - prev_drift).abs();
slews.push(sl);
}
prev_drift = d;
}
prev_norm = n;
}
}
}
}
let stats = HealthyStats {
mean_residual_norm: mean(&norms),
p50_residual_norm: percentile_of(&norms, 0.50),
p90_residual_norm: percentile_of(&norms, 0.90),
p99_residual_norm: percentile_of(&norms, 0.99),
mean_drift_magnitude: mean(&drifts),
p90_drift_magnitude: percentile_of(&drifts, 0.90),
mean_slew_magnitude: mean(&slews),
p90_slew_magnitude: percentile_of(&slews, 0.90),
num_windows,
num_signals,
};
let drift_pct = percentile_of(&drifts, pct);
let slew_pct = percentile_of(&slews, pct.max(0.95));
let motifs = [
MotifClass::MemoryLeakDrift,
MotifClass::CascadingTimeoutSlew,
MotifClass::DeploymentRegressionSlew,
MotifClass::CacheDegradationGrazing,
MotifClass::ConnectionPoolExhaustionDrift,
MotifClass::GcPressureOscillation,
MotifClass::ErrorRateEscalation,
MotifClass::DependencySlowdown,
MotifClass::ResourceSaturation,
MotifClass::QueueBackpressure,
MotifClass::RetryStormCascade,
MotifClass::CircuitBreakerOpenShift,
MotifClass::DatabaseLockContention,
MotifClass::AuthenticationFailureSpike,
MotifClass::ConfigDriftRegression,
MotifClass::PacketLossErrorEscalation,
MotifClass::NetworkDelayDependencyInflation,
MotifClass::DiskIoSaturation,
MotifClass::CpuSaturation,
MotifClass::JvmHeapPressure,
MotifClass::JvmGcPause,
MotifClass::ServiceGraphDriftPropagation,
MotifClass::HighDimAnomalyCluster,
MotifClass::MetricCorrelationCollapse,
MotifClass::LogVolumeAnomaly,
MotifClass::LogTraceTemporalDecorrelation,
MotifClass::LogSeverityEscalation,
MotifClass::SaturationTrending,
MotifClass::EpisodicTransientSpike,
MotifClass::RegressiveDriftWithRecovery,
MotifClass::EnvelopeBoundaryApproach,
MotifClass::EnvelopeBreach,
];
let mut motif_recommendations = Vec::with_capacity(motifs.len());
for &m in motifs.iter() {
motif_recommendations.push(MotifThresholdRecommendation {
motif: m,
recommended_drift_threshold: drift_pct,
recommended_slew_threshold: slew_pct,
percentile: pct,
});
}
let mut config = crate::config::PAPER_LOCK_CONFIG;
let recommended_slew_delta = percentile_of(&slews, 0.95);
if recommended_slew_delta > 0.0 && recommended_slew_delta.is_finite() {
config.slew_delta = recommended_slew_delta;
}
CalibrationReport {
config,
motif_recommendations,
healthy_stats: stats,
}
}
fn mean(xs: &[f64]) -> f64 {
if xs.is_empty() {
return 0.0;
}
let mut sum = 0.0;
for &x in xs {
sum += x;
}
sum / xs.len() as f64
}
fn percentile_of(xs: &[f64], pct: f64) -> f64 {
if xs.is_empty() {
return 0.0;
}
let mut sorted: Vec<f64> = xs.to_vec();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal));
let n = sorted.len();
let pct = pct.clamp(0.0, 1.0);
let rank = (pct * (n - 1) as f64).round() as usize;
let rank = rank.min(n - 1);
sorted[rank]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_data_yields_zero_stats() {
let r = recommend_config_from_healthy(&[], 0, 0, 0.9);
assert_eq!(r.healthy_stats.mean_residual_norm, 0.0);
assert_eq!(r.healthy_stats.p90_residual_norm, 0.0);
assert_eq!(r.motif_recommendations.len(), 32);
}
#[test]
fn constant_healthy_yields_zero_thresholds() {
let data = std::vec![100.0_f64; 200]; let r = recommend_config_from_healthy(&data, 2, 100, 0.9);
assert_eq!(r.healthy_stats.mean_residual_norm, 0.0);
assert_eq!(r.healthy_stats.p90_drift_magnitude, 0.0);
assert_eq!(r.healthy_stats.p90_slew_magnitude, 0.0);
assert_eq!(r.motif_recommendations[0].recommended_drift_threshold, 0.0);
}
#[test]
fn recommendation_rises_with_variance() {
let mut quiet = std::vec![0.0_f64; 200];
for i in 0..200 {
quiet[i] = 100.0 + 0.1 * ((i as f64).sin());
}
let mut noisy = std::vec![0.0_f64; 200];
for i in 0..200 {
noisy[i] = 100.0 + 5.0 * ((i as f64).sin());
}
let r_quiet = recommend_config_from_healthy(&quiet, 2, 100, 0.9);
let r_noisy = recommend_config_from_healthy(&noisy, 2, 100, 0.9);
assert!(r_noisy.healthy_stats.p90_residual_norm
> r_quiet.healthy_stats.p90_residual_norm,
"noisy slice must show higher p90 residual norm");
assert!(r_noisy.motif_recommendations[0].recommended_drift_threshold
> r_quiet.motif_recommendations[0].recommended_drift_threshold,
"noisy slice must produce higher drift_threshold recommendation");
}
#[test]
fn percentile_clamps_to_valid_range() {
let data = std::vec![100.0_f64; 200];
let r0 = recommend_config_from_healthy(&data, 2, 100, 0.0);
let r1 = recommend_config_from_healthy(&data, 2, 100, 1.5);
assert!(r0.motif_recommendations[0].percentile >= 0.5);
assert!(r1.motif_recommendations[0].percentile <= 0.999);
}
#[test]
fn report_carries_all_canonical_motifs() {
let r = recommend_config_from_healthy(&[], 0, 0, 0.9);
assert_eq!(r.motif_recommendations.len(), 32,
"calibration report should cover every canonical motif");
let jvm = r.motif_recommendations.iter()
.find(|m| m.motif == MotifClass::JvmHeapPressure);
assert!(jvm.is_some(), "JvmHeapPressure must be in the recommendation set");
}
}