tacet_core/preflight/
mod.rs1extern crate alloc;
25
26use alloc::vec::Vec;
27
28mod autocorr;
29mod resolution;
30mod sanity;
31
32pub use autocorr::{autocorrelation_check, compute_acf, AutocorrWarning};
33pub use resolution::{resolution_check, ResolutionWarning};
34pub use sanity::{sanity_check, SanityWarning};
35
36#[derive(Debug, Clone, Default)]
41#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
42pub struct PreflightResult {
43 pub warnings: PreflightWarnings,
45
46 pub has_critical: bool,
48
49 pub is_valid: bool,
51}
52
53impl PreflightResult {
54 pub fn new() -> Self {
56 Self {
57 warnings: PreflightWarnings::default(),
58 has_critical: false,
59 is_valid: true,
60 }
61 }
62
63 pub fn add_sanity_warning(&mut self, warning: SanityWarning) {
65 if warning.is_result_undermining() {
66 self.has_critical = true;
67 self.is_valid = false;
68 }
69 self.warnings.sanity.push(warning);
70 }
71
72 pub fn add_autocorr_warning(&mut self, warning: AutocorrWarning) {
74 self.warnings.autocorr.push(warning);
75 }
76
77 pub fn add_resolution_warning(&mut self, warning: ResolutionWarning) {
79 if warning.is_result_undermining() {
80 self.has_critical = true;
81 self.is_valid = false;
82 }
83 self.warnings.resolution.push(warning);
84 }
85
86 pub fn has_warnings(&self) -> bool {
88 !self.warnings.sanity.is_empty()
89 || !self.warnings.autocorr.is_empty()
90 || !self.warnings.resolution.is_empty()
91 }
92}
93
94#[derive(Debug, Clone, Default)]
99#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
100pub struct PreflightWarnings {
101 pub sanity: Vec<SanityWarning>,
103
104 pub autocorr: Vec<AutocorrWarning>,
106
107 pub resolution: Vec<ResolutionWarning>,
109}
110
111impl PreflightWarnings {
112 pub fn new() -> Self {
114 Self::default()
115 }
116
117 pub fn count(&self) -> usize {
119 self.sanity.len() + self.autocorr.len() + self.resolution.len()
120 }
121
122 pub fn is_empty(&self) -> bool {
124 self.count() == 0
125 }
126}
127
128pub fn run_core_checks(
148 fixed_samples: &[f64],
149 random_samples: &[f64],
150 timer_resolution_ns: f64,
151 seed: u64,
152) -> PreflightResult {
153 let mut result = PreflightResult::new();
154
155 if let Some(warning) = resolution_check(fixed_samples, timer_resolution_ns) {
157 result.add_resolution_warning(warning);
158 }
159
160 if let Some(warning) = sanity_check(fixed_samples, timer_resolution_ns, seed) {
162 result.add_sanity_warning(warning);
163 }
164
165 if let Some(warning) = autocorrelation_check(fixed_samples, random_samples) {
167 result.add_autocorr_warning(warning);
168 }
169
170 result
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn test_preflight_result_default() {
179 let result = PreflightResult::new();
180 assert!(result.is_valid);
181 assert!(!result.has_critical);
182 assert!(!result.has_warnings());
183 }
184
185 #[test]
186 fn test_warnings_count() {
187 let mut warnings = PreflightWarnings::new();
188 assert_eq!(warnings.count(), 0);
189 assert!(warnings.is_empty());
190
191 warnings.sanity.push(SanityWarning::BrokenHarness {
192 variance_ratio: 7.5,
193 });
194 assert_eq!(warnings.count(), 1);
195 assert!(!warnings.is_empty());
196 }
197
198 #[test]
199 fn test_run_core_checks_empty() {
200 let result = run_core_checks(&[], &[], 1.0, 42);
202 assert!(result.is_valid);
204 }
205
206 #[test]
207 fn test_run_core_checks_normal_data() {
208 let fixed: Vec<f64> = (0..1000).map(|i| 100.0 + (i % 10) as f64).collect();
210 let random: Vec<f64> = (0..1000).map(|i| 105.0 + (i % 10) as f64).collect();
211
212 let result = run_core_checks(&fixed, &random, 1.0, 42);
213 assert!(result.is_valid);
215 }
216}