use std::cmp::Reverse;
use std::collections::BinaryHeap;
use fixedbitset::FixedBitSet;
use smallvec::SmallVec;
use crate::RaptorCache;
use crate::Timetable;
use crate::algorithm::boarding::BoardingTree;
use crate::algorithm::boarding::Step;
use crate::algorithm::boarding::best_to_any_target;
use crate::algorithm::boarding::extract_target_journeys;
use crate::algorithm::footpaths::relax_footpaths_round;
use crate::algorithm::footpaths::relax_footpaths_round_closed;
use crate::algorithm::label_bag::LabelBag;
use crate::endpoints::IntoEndpoints;
use crate::ids::RouteIdx;
use crate::ids::StopIdx;
use crate::ids::TripIdx;
use crate::journey::Journey;
use crate::label::Label;
use crate::time::Duration;
use crate::time::SecondOfDay;
#[allow(clippy::too_many_arguments)]
pub(crate) fn run_raptor_rounds<T: Timetable + ?Sized, L: Label>(
tt: &T,
ctx: &L::Ctx,
labels: &mut [Vec<LabelBag<L>>],
best_arrival: &mut [LabelBag<L>],
board_detail: &mut BoardingTree,
marked_stops: &mut FixedBitSet,
q_entry: &mut [Option<u32>],
q_routes: &mut Vec<RouteIdx>,
walked_buf: &mut Vec<StopIdx>,
relax_heap: &mut BinaryHeap<Reverse<(SecondOfDay, u32)>>,
ever_reached: &mut FixedBitSet,
transfers: usize,
require_wheelchair_accessible: bool,
targets: &[(StopIdx, Duration)],
) {
let mut pt_threshold = best_to_any_target(best_arrival, targets);
let footpaths_closed = tt.footpaths_are_transitively_closed();
if footpaths_closed {
relax_footpaths_round_closed(
tt,
ctx,
0,
labels,
best_arrival,
board_detail,
marked_stops,
pt_threshold,
walked_buf,
ever_reached,
);
} else {
relax_footpaths_round(
tt,
ctx,
0,
labels,
best_arrival,
board_detail,
marked_stops,
pt_threshold,
walked_buf,
relax_heap,
ever_reached,
);
}
for s in walked_buf.drain(..) {
marked_stops.insert(s.idx());
}
pt_threshold = best_to_any_target(best_arrival, targets);
for k in 1..=transfers {
let (prev_labels, this_labels) = labels.split_at_mut(k);
let src = &prev_labels[k - 1];
let dst = &mut this_labels[0];
for bit in ever_reached.ones() {
dst[bit] = src[bit].clone();
}
for stop_bit in marked_stops.ones() {
let marked_stop = StopIdx::new(stop_bit as u32);
for &(route, pos) in tt.get_routes_serving_stop(marked_stop) {
match q_entry[route.idx()] {
None => {
q_entry[route.idx()] = Some(pos);
q_routes.push(route);
}
Some(prev_pos) => {
if pos < prev_pos {
q_entry[route.idx()] = Some(pos);
}
}
}
}
}
marked_stops.clear();
let mut route_bag: SmallVec<[(L, TripIdx, StopIdx, u32); 8]> = SmallVec::new();
let mut staged: SmallVec<[L; 8]> = SmallVec::new();
for &route in q_routes.iter() {
let p_pos = q_entry[route.idx()].expect("route in q_routes must have an entry");
route_bag.clear();
for (offset, &pi) in tt.get_stops_after(route, p_pos).iter().enumerate() {
let pos = p_pos + offset as u32;
let stop_accessible_ok =
!require_wheelchair_accessible || tt.stop_wheelchair_accessible(pi);
for &(boarding_label, trip, boarding_stop, board_pos) in route_bag.iter() {
if !tt.drop_off_allowed(trip, pos) {
continue;
}
if !stop_accessible_ok {
continue;
}
let arr = tt.get_arrival_time(trip, pos);
let best_to_pi = best_arrival[pi.idx()].min_arrival();
let time_to_beat = best_to_pi.min(pt_threshold);
if arr >= time_to_beat {
continue;
}
let new_label = boarding_label.extend_by_trip(
ctx,
crate::label::TripContext {
trip,
route,
board_stop: boarding_stop,
board_pos,
alight_stop: pi,
alight_pos: pos,
arrival: arr,
},
);
if labels[k][pi.idx()].insert(new_label) {
best_arrival[pi.idx()].insert(new_label);
board_detail.insert(
(k, pi, new_label.arrival()),
Step::Boarded {
from: boarding_stop,
route,
parent_arrival: boarding_label.arrival(),
},
);
marked_stops.insert(pi.idx());
ever_reached.insert(pi.idx());
}
}
staged.clear();
staged.extend(labels[k - 1][pi.idx()].iter().copied());
for candidate in &staged {
let cand_arr = candidate.arrival();
let trip = match tt.earliest_accessible_trip(
route,
cand_arr,
pos,
require_wheelchair_accessible,
) {
Some(t) => t,
None => continue,
};
let trip_dep = tt.get_departure_time(trip, pos);
let mut redundant = false;
for &(l_existing, t_existing, _, _) in route_bag.iter() {
let existing_dep = tt.get_departure_time(t_existing, pos);
if l_existing.dominates(candidate) && existing_dep <= trip_dep {
redundant = true;
break;
}
}
if redundant {
continue;
}
route_bag.retain(|&mut (l_existing, t_existing, _, _)| {
let existing_dep = tt.get_departure_time(t_existing, pos);
!(candidate.dominates(&l_existing) && trip_dep <= existing_dep)
});
route_bag.push((*candidate, trip, pi, pos));
}
}
}
for r in q_routes.drain(..) {
q_entry[r.idx()] = None;
}
pt_threshold = best_to_any_target(best_arrival, targets);
if footpaths_closed {
relax_footpaths_round_closed(
tt,
ctx,
k,
labels,
best_arrival,
board_detail,
marked_stops,
pt_threshold,
walked_buf,
ever_reached,
);
} else {
relax_footpaths_round(
tt,
ctx,
k,
labels,
best_arrival,
board_detail,
marked_stops,
pt_threshold,
walked_buf,
relax_heap,
ever_reached,
);
}
for s in walked_buf.drain(..) {
marked_stops.insert(s.idx());
}
pt_threshold = best_to_any_target(best_arrival, targets);
if marked_stops.is_clear() {
break;
}
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn run_per_call_query<T: Timetable + ?Sized, L: Label>(
tt: &T,
ctx: &L::Ctx,
cache: &mut RaptorCache<L>,
transfers: usize,
depart: SecondOfDay,
origins: impl IntoEndpoints,
targets: impl IntoEndpoints,
require_wheelchair_accessible: bool,
) -> Vec<Journey<L>> {
let origins = origins.into_endpoints();
let targets = targets.into_endpoints();
let origins = origins.as_slice();
let targets = targets.as_slice();
cache.reset_for_query(transfers, tt.n_stops() as u32, tt.n_routes() as u32);
let RaptorCache {
labels,
best_arrival,
board_detail,
marked_stops,
q_entry,
q_routes,
walked_buf,
origin_set,
relax_heap,
ever_reached,
..
} = cache;
origin_set.clear();
for &(o, _) in origins {
origin_set.insert(o.idx());
}
for &(o, walk) in origins {
let t = depart + walk;
let seed = L::from_departure(ctx, t);
if labels[0][o.idx()].insert(seed) {
best_arrival[o.idx()].insert(seed);
marked_stops.insert(o.idx());
ever_reached.insert(o.idx());
}
}
run_raptor_rounds(
tt,
ctx,
labels,
best_arrival,
board_detail,
marked_stops,
q_entry,
q_routes,
walked_buf,
relax_heap,
ever_reached,
transfers,
require_wheelchair_accessible,
targets,
);
let mut journeys =
extract_target_journeys(ctx, labels, board_detail, origin_set, targets, transfers);
journeys.sort_by_key(|j| (j.plan.len(), j.arrival()));
let mut front: Vec<Journey<L>> = Vec::with_capacity(journeys.len());
'outer: for j in journeys {
for f in &front {
if f.plan.len() <= j.plan.len() && f.label.dominates(&j.label) {
continue 'outer;
}
}
front.retain(|f| !(j.plan.len() <= f.plan.len() && j.label.dominates(&f.label)));
front.push(j);
}
front
}