extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use crate::result::{PreflightCategory, PreflightSeverity, PreflightWarningInfo};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum ResolutionWarning {
InsufficientResolution {
unique_values: usize,
total_samples: usize,
zero_fraction: f64,
timer_resolution_ns: f64,
},
HighQuantization {
unique_values: usize,
total_samples: usize,
},
}
impl ResolutionWarning {
pub fn is_result_undermining(&self) -> bool {
matches!(self, ResolutionWarning::InsufficientResolution { .. })
}
pub fn severity(&self) -> PreflightSeverity {
match self {
ResolutionWarning::InsufficientResolution { .. } => {
PreflightSeverity::ResultUndermining
}
ResolutionWarning::HighQuantization { .. } => PreflightSeverity::Informational,
}
}
pub fn description(&self) -> String {
match self {
ResolutionWarning::InsufficientResolution {
unique_values,
total_samples,
zero_fraction,
timer_resolution_ns,
} => {
alloc::format!(
"Timer resolution (~{:.0}ns) is too coarse for this operation. \
Only {} unique values in {} samples ({:.0}% are zero).",
timer_resolution_ns,
unique_values,
total_samples,
zero_fraction * 100.0
)
}
ResolutionWarning::HighQuantization {
unique_values,
total_samples,
} => {
alloc::format!(
"High quantization: only {} unique values in {} samples. \
Timer resolution may be affecting measurement quality.",
unique_values,
total_samples
)
}
}
}
pub fn guidance(&self) -> Option<String> {
match self {
ResolutionWarning::InsufficientResolution { .. } => Some(
"Consider: (1) measuring multiple iterations per sample, \
(2) using a more complex operation, or \
(3) enabling cycle-accurate timing for ~0.3ns resolution."
.into(),
),
ResolutionWarning::HighQuantization { .. } => {
Some("Timer resolution may be limiting measurement quality.".into())
}
}
}
pub fn to_warning_info(&self) -> PreflightWarningInfo {
match self.guidance() {
Some(guidance) => PreflightWarningInfo::with_guidance(
PreflightCategory::Resolution,
self.severity(),
self.description(),
guidance,
),
None => PreflightWarningInfo::new(
PreflightCategory::Resolution,
self.severity(),
self.description(),
),
}
}
}
const MIN_UNIQUE_PER_1000: usize = 20;
const CRITICAL_ZERO_FRACTION: f64 = 0.5;
pub fn resolution_check(samples: &[f64], timer_resolution_ns: f64) -> Option<ResolutionWarning> {
if samples.len() < 100 {
return None; }
let mut sorted: Vec<f64> = samples.to_vec();
sorted.sort_by(|a, b| a.total_cmp(b));
let mut unique_count = 1;
let mut last_value = sorted[0];
for &val in &sorted[1..] {
if (val - last_value).abs() > 0.1 {
unique_count += 1;
last_value = val;
}
}
let zero_count = samples.iter().filter(|&&x| x.abs() < 0.1).count();
let zero_fraction = zero_count as f64 / samples.len() as f64;
let expected_unique =
(samples.len() as f64 / 1000.0 * MIN_UNIQUE_PER_1000 as f64).max(10.0) as usize;
if unique_count < expected_unique && zero_fraction > CRITICAL_ZERO_FRACTION {
return Some(ResolutionWarning::InsufficientResolution {
unique_values: unique_count,
total_samples: samples.len(),
zero_fraction,
timer_resolution_ns,
});
}
if unique_count < expected_unique / 2 {
return Some(ResolutionWarning::HighQuantization {
unique_values: unique_count,
total_samples: samples.len(),
});
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insufficient_resolution() {
let mut samples = alloc::vec![0.0; 800];
samples.extend(alloc::vec![41.0; 150]);
samples.extend(alloc::vec![82.0; 50]);
let result = resolution_check(&samples, 41.0);
assert!(result.is_some(), "Should detect insufficient resolution");
assert!(
result.as_ref().unwrap().is_result_undermining(),
"Should be result-undermining"
);
assert_eq!(
result.as_ref().unwrap().severity(),
PreflightSeverity::ResultUndermining
);
}
#[test]
fn test_severity() {
let insufficient = ResolutionWarning::InsufficientResolution {
unique_values: 3,
total_samples: 1000,
zero_fraction: 0.8,
timer_resolution_ns: 41.0,
};
assert_eq!(
insufficient.severity(),
PreflightSeverity::ResultUndermining
);
assert!(insufficient.is_result_undermining());
let high_quant = ResolutionWarning::HighQuantization {
unique_values: 5,
total_samples: 1000,
};
assert_eq!(high_quant.severity(), PreflightSeverity::Informational);
assert!(!high_quant.is_result_undermining());
}
}