pub type Departure = f64;
#[derive(Debug, Clone)]
pub enum Schedule {
FixedHeadway {
headway: f64,
first: f64,
},
Timetable(Vec<Departure>),
}
impl Schedule {
pub fn fixed_headway(headway: f64) -> Self {
Self::FixedHeadway {
headway,
first: 0.0,
}
}
pub fn fixed_headway_starting(headway: f64, first: f64) -> Self {
Self::FixedHeadway { headway, first }
}
pub fn timetable(departures: Vec<Departure>) -> Self {
Self::Timetable(departures)
}
pub fn next_departure(&self, now: f64) -> Option<Departure> {
match self {
Schedule::FixedHeadway { headway, first } => {
if *headway <= 0.0 {
return None;
}
let elapsed = (now - first).max(0.0);
let k = (elapsed / headway).ceil();
Some(first + k * headway)
}
Schedule::Timetable(ts) => ts.iter().copied().find(|t| *t >= now),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fixed_headway_alignment() {
let s = Schedule::fixed_headway(300.0);
assert_eq!(s.next_departure(0.0), Some(0.0));
assert_eq!(s.next_departure(100.0), Some(300.0));
assert_eq!(s.next_departure(600.0), Some(600.0));
}
#[test]
fn timetable_picks_next_after_now() {
let s = Schedule::timetable(vec![100.0, 200.0, 350.0]);
assert_eq!(s.next_departure(50.0), Some(100.0));
assert_eq!(s.next_departure(150.0), Some(200.0));
assert_eq!(s.next_departure(400.0), None);
}
}