#[must_use]
pub fn window_period_overlaps(
window_start_hours: f64,
window_width_hours: f64,
stage_lengths_hours: &[f64],
) -> Vec<f64> {
debug_assert!(
window_start_hours.is_finite(),
"window_start_hours must be finite"
);
debug_assert!(
window_width_hours.is_finite() && window_width_hours > 0.0,
"window_width_hours must be finite and > 0.0"
);
debug_assert!(
stage_lengths_hours
.iter()
.all(|&length| length.is_finite() && length > 0.0),
"every stage_lengths_hours entry must be finite and > 0.0"
);
let window_end_hours = window_start_hours + window_width_hours;
let mut overlaps = Vec::new();
let mut period_start = 0.0_f64;
for &length in stage_lengths_hours {
if period_start >= window_end_hours {
break;
}
let period_end = period_start + length;
let overlap_start = window_start_hours.max(period_start);
let overlap_end = window_end_hours.min(period_end);
overlaps.push((overlap_end - overlap_start).max(0.0));
period_start = period_end;
}
match overlaps.iter().rposition(|&overlap| overlap > 0.0) {
Some(last_nonzero) => {
overlaps.truncate(last_nonzero + 1);
overlaps
}
None => Vec::new(),
}
}
#[cfg(test)]
mod tests {
use super::*;
const TOL: f64 = 1e-9;
fn assert_close(actual: &[f64], expected: &[f64]) {
assert_eq!(
actual.len(),
expected.len(),
"length mismatch: got {actual:?}, expected {expected:?}"
);
for (a, e) in actual.iter().zip(expected) {
assert!(
(a - e).abs() < TOL,
"value mismatch: got {actual:?}, expected {expected:?}"
);
}
}
#[test]
fn test_uniform_stages_shallow_window() {
let overlaps = window_period_overlaps(0.0, 360.0, &[168.0, 168.0, 168.0]);
assert_close(&overlaps, &[168.0, 168.0, 24.0]);
}
#[test]
fn test_non_uniform_stages_depth_three() {
let overlaps = window_period_overlaps(360.0, 720.0, &[720.0, 168.0, 168.0, 168.0]);
assert_eq!(overlaps.len(), 4);
assert!(overlaps[3] > 0.0, "index 3 must be nonzero: {overlaps:?}");
}
#[test]
fn test_skipped_intermediate_stage_stays_contiguous() {
let overlaps = window_period_overlaps(336.0, 200.0, &[168.0, 168.0, 168.0, 168.0]);
assert_eq!(overlaps.len(), 4);
assert_eq!(
overlaps[1], 0.0,
"index 1 must be exactly 0.0: {overlaps:?}"
);
assert_close(&overlaps, &[0.0, 0.0, 168.0, 32.0]);
}
#[test]
fn test_window_past_all_stages_returns_empty() {
let overlaps = window_period_overlaps(1000.0, 10.0, &[168.0, 168.0]);
assert!(overlaps.is_empty(), "expected empty, got {overlaps:?}");
}
#[test]
fn test_exact_boundary_window_returns_depth_zero() {
let overlaps = window_period_overlaps(0.0, 720.0, &[720.0, 168.0, 168.0]);
assert_close(&overlaps, &[720.0]);
}
}