mod allocator;
mod discovery;
mod explorer;
mod itinerary;
mod location;
mod path;
mod state;
use std::mem;
pub use allocator::*;
pub(crate) use discovery::*;
pub use itinerary::*;
pub use location::*;
pub(crate) use path::*;
pub(crate) use state::*;
use crate::{
raptor::explorer::{
explore_routes, explore_routes_reverse, explore_transfers, explore_transfers_reverse,
},
repository::Repository,
shared::time::{self, Time},
};
use thiserror::Error;
use tracing::{trace, warn};
pub const MAX_ROUNDS: usize = 15;
#[derive(Error, Debug)]
pub enum Error {
#[error("Area id does not match any entry")]
InvalidAreaID,
#[error("Stop id does not match any entry")]
InvalidStopID,
#[error("A route was found but failed to build it")]
FailedToBuildRoute,
#[error("Could not find a route")]
NoRouteFound,
}
#[derive(Debug, Clone, Copy)]
pub enum TimeConstraint {
Arrival(Time),
Departure(Time),
}
impl TimeConstraint {
pub fn time(&self) -> Time {
match *self {
TimeConstraint::Arrival(time) => time,
TimeConstraint::Departure(time) => time,
}
}
}
pub struct Raptor<'a> {
repository: &'a Repository,
from: Location,
to: Location,
time_constraint: TimeConstraint,
allow_walks: bool,
}
impl<'a> Raptor<'a> {
pub fn new(repository: &'a Repository, from: Location, to: Location) -> Self {
Self {
repository,
from,
to,
time_constraint: TimeConstraint::Departure(Time::now()),
allow_walks: true,
}
}
pub fn departure_at(mut self, departure: Time) -> Self {
self.time_constraint = TimeConstraint::Departure(departure);
self
}
pub fn arrival_at(mut self, arrival: Time) -> Self {
self.time_constraint = TimeConstraint::Arrival(arrival);
self
}
pub fn with_time_constraint(mut self, constrait: TimeConstraint) -> Self {
self.time_constraint = constrait;
self
}
pub fn allow_walks(mut self, value: bool) -> Self {
self.allow_walks = value;
self
}
pub fn solve(self) -> Result<Itinerary, self::Error> {
let mut allocator = Allocator::new(self.repository);
self.solve_with_allocator(&mut allocator)
}
pub fn solve_with_allocator(self, allocator: &mut Allocator) -> Result<Itinerary, self::Error> {
let from_stops = stops_by_location(self.repository, &self.from)?;
let to_stops = stops_by_location(self.repository, &self.to)?;
match self.time_constraint {
TimeConstraint::Arrival(time) => {
to_stops.into_iter().for_each(|stop| {
allocator.marked_stops.set(stop.index as usize, true);
allocator.curr_labels[stop.index as usize] = Some(time);
});
allocator.target.stops = from_stops.into_iter().map(|stop| stop.index).collect();
allocator.target.tau_star = time::MIN;
allocator.active.fill(u32::MIN);
}
TimeConstraint::Departure(time) => {
from_stops.into_iter().for_each(|stop| {
allocator.marked_stops.set(stop.index as usize, true);
allocator.curr_labels[stop.index as usize] = Some(time);
});
allocator.target.stops = to_stops.into_iter().map(|stop| stop.index).collect();
allocator.target.tau_star = time::MAX;
allocator.active.fill(u32::MAX);
}
}
allocator.round = 0;
loop {
if allocator.round >= MAX_ROUNDS {
warn!("Hit round limit!");
break;
}
allocator.swap_labels();
if allocator.marked_stops.not_any() {
break;
}
let mut marked_stops = mem::take(&mut allocator.marked_stops);
trace!(
"Found {} in round {}",
marked_stops.iter_ones().count(),
allocator.round
);
allocator.active_mask.fill(false);
marked_stops.iter_ones().for_each(|stop_idx| {
routes_serving_stop(self.repository, stop_idx as u32, allocator);
for route in allocator.routes_serving_stops.iter() {
let r_idx = route.route_idx as usize;
let p_idx = route.idx_in_route;
match self.time_constraint {
TimeConstraint::Departure(_) => {
let p_idx_to_beat = allocator
.active_mask
.get(r_idx)
.map(|_| allocator.active[r_idx])
.unwrap_or(u32::MAX);
if p_idx < p_idx_to_beat {
allocator.active[r_idx] = p_idx;
allocator.active_mask.set(r_idx, true);
}
}
TimeConstraint::Arrival(_) => {
let p_idx_to_beat = allocator
.active_mask
.get(r_idx)
.map(|_| allocator.active[r_idx])
.unwrap_or(0);
if p_idx > p_idx_to_beat {
allocator.active[r_idx] = p_idx;
allocator.active_mask.set(r_idx, true);
}
}
}
}
});
marked_stops.fill(false);
allocator.marked_stops = mem::take(&mut marked_stops);
match self.time_constraint {
TimeConstraint::Arrival(_) => {
explore_routes_reverse(self.repository, allocator);
allocator.run_updates_reverse();
explore_transfers_reverse(self.allow_walks, self.repository, allocator);
allocator.run_updates_reverse();
}
TimeConstraint::Departure(_) => {
explore_routes(self.repository, allocator);
allocator.run_updates();
explore_transfers(self.allow_walks, self.repository, allocator);
allocator.run_updates();
}
}
allocator
.target
.stops
.iter()
.filter_map(|stop_idx| {
let tau_star = allocator.tau_star[*stop_idx as usize];
tau_star.map(|tau_star| (stop_idx, tau_star))
})
.for_each(|(stop_idx, tau_star)| {
let improvement = match self.time_constraint {
TimeConstraint::Arrival(_) => tau_star > allocator.target.tau_star,
TimeConstraint::Departure(_) => tau_star < allocator.target.tau_star,
};
if improvement {
allocator.target.tau_star = tau_star;
allocator.target.best_stop = Some(*stop_idx);
allocator.target.best_round = Some(allocator.round);
}
});
allocator.next_round();
}
if let Some(target_stop) = allocator.target.best_stop
&& let Some(target_round) = allocator.target.best_round
{
let path = backtrack(
self.repository,
allocator,
target_stop,
target_round,
self.time_constraint,
)?;
Ok(Itinerary::new(self.from, self.to, path, self.repository))
} else {
Err(self::Error::NoRouteFound)
}
}
}