extern crate alloc;
use alloc::vec::Vec;
mod autocorr;
mod resolution;
mod sanity;
pub use autocorr::{autocorrelation_check, compute_acf, AutocorrWarning};
pub use resolution::{resolution_check, ResolutionWarning};
pub use sanity::{sanity_check, SanityWarning};
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct PreflightResult {
pub warnings: PreflightWarnings,
pub has_critical: bool,
pub is_valid: bool,
}
impl PreflightResult {
pub fn new() -> Self {
Self {
warnings: PreflightWarnings::default(),
has_critical: false,
is_valid: true,
}
}
pub fn add_sanity_warning(&mut self, warning: SanityWarning) {
if warning.is_result_undermining() {
self.has_critical = true;
self.is_valid = false;
}
self.warnings.sanity.push(warning);
}
pub fn add_autocorr_warning(&mut self, warning: AutocorrWarning) {
self.warnings.autocorr.push(warning);
}
pub fn add_resolution_warning(&mut self, warning: ResolutionWarning) {
if warning.is_result_undermining() {
self.has_critical = true;
self.is_valid = false;
}
self.warnings.resolution.push(warning);
}
pub fn has_warnings(&self) -> bool {
!self.warnings.sanity.is_empty()
|| !self.warnings.autocorr.is_empty()
|| !self.warnings.resolution.is_empty()
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct PreflightWarnings {
pub sanity: Vec<SanityWarning>,
pub autocorr: Vec<AutocorrWarning>,
pub resolution: Vec<ResolutionWarning>,
}
impl PreflightWarnings {
pub fn new() -> Self {
Self::default()
}
pub fn count(&self) -> usize {
self.sanity.len() + self.autocorr.len() + self.resolution.len()
}
pub fn is_empty(&self) -> bool {
self.count() == 0
}
}
pub fn run_core_checks(
fixed_samples: &[f64],
random_samples: &[f64],
timer_resolution_ns: f64,
seed: u64,
) -> PreflightResult {
let mut result = PreflightResult::new();
if let Some(warning) = resolution_check(fixed_samples, timer_resolution_ns) {
result.add_resolution_warning(warning);
}
if let Some(warning) = sanity_check(fixed_samples, timer_resolution_ns, seed) {
result.add_sanity_warning(warning);
}
if let Some(warning) = autocorrelation_check(fixed_samples, random_samples) {
result.add_autocorr_warning(warning);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_preflight_result_default() {
let result = PreflightResult::new();
assert!(result.is_valid);
assert!(!result.has_critical);
assert!(!result.has_warnings());
}
#[test]
fn test_warnings_count() {
let mut warnings = PreflightWarnings::new();
assert_eq!(warnings.count(), 0);
assert!(warnings.is_empty());
warnings.sanity.push(SanityWarning::BrokenHarness {
variance_ratio: 7.5,
});
assert_eq!(warnings.count(), 1);
assert!(!warnings.is_empty());
}
#[test]
fn test_run_core_checks_empty() {
let result = run_core_checks(&[], &[], 1.0, 42);
assert!(result.is_valid);
}
#[test]
fn test_run_core_checks_normal_data() {
let fixed: Vec<f64> = (0..1000).map(|i| 100.0 + (i % 10) as f64).collect();
let random: Vec<f64> = (0..1000).map(|i| 105.0 + (i % 10) as f64).collect();
let result = run_core_checks(&fixed, &random, 1.0, 42);
assert!(result.is_valid);
}
}