use std::collections::BTreeMap;
use fixedbitset::FixedBitSet;
use crate::K;
use crate::algorithm::label_bag::LabelBag;
use crate::ids::RouteIdx;
use crate::ids::StopIdx;
use crate::journey::Journey;
use crate::label::Label;
use crate::time::Duration;
use crate::time::SecondOfDay;
#[derive(Debug, Clone, Copy)]
pub(crate) enum Step {
Boarded {
from: StopIdx,
route: RouteIdx,
parent_arrival: SecondOfDay,
},
Walked {
from: StopIdx,
parent_arrival: SecondOfDay,
},
}
pub(crate) type BoardingTree = BTreeMap<(K, StopIdx, SecondOfDay), Step>;
pub(crate) fn best_to_any_target<L: Label>(
best_arrival: &[LabelBag<L>],
targets: &[(StopIdx, Duration)],
) -> SecondOfDay {
targets
.iter()
.map(|&(t, w)| best_arrival[t.idx()].min_arrival() + w)
.min()
.unwrap_or(SecondOfDay::MAX)
}
pub(crate) fn reconstruct_journey(
tree: &BoardingTree,
origins: &FixedBitSet,
pt: StopIdx,
target_arrival: SecondOfDay,
k: K,
) -> Option<(StopIdx, Vec<(RouteIdx, StopIdx)>)> {
if tree.is_empty() {
return None;
}
let mut plan = Vec::with_capacity(k);
let mut parent = pt;
let mut parent_arrival = target_arrival;
let mut inner_k = k;
let mut budget = (k + 1) * 100;
while !origins.contains(parent.idx()) && budget > 0 {
budget -= 1;
let Some(step) = tree.get(&(inner_k, parent, parent_arrival)).copied() else {
break;
};
match step {
Step::Boarded {
from,
route,
parent_arrival: pa,
} => {
plan.push((route, parent));
parent = from;
parent_arrival = pa;
if inner_k == 0 {
break;
}
inner_k -= 1;
}
Step::Walked {
from,
parent_arrival: pa,
} => {
parent = from;
parent_arrival = pa;
}
}
}
if !plan.is_empty() && origins.contains(parent.idx()) {
plan.reverse();
Some((parent, plan))
} else {
None
}
}
pub(crate) fn extract_target_journeys<L: Label>(
labels: &[Vec<LabelBag<L>>],
board_detail: &BoardingTree,
origin_set: &FixedBitSet,
targets: &[(StopIdx, Duration)],
transfers: usize,
) -> Vec<Journey<L>> {
let mut journeys: Vec<Journey<L>> = Vec::new();
for &(target, walk) in targets {
#[allow(clippy::needless_range_loop)]
for k in 1..=transfers {
for raw_label in labels[k][target.idx()].iter() {
let raw_arr = raw_label.arrival();
if raw_arr == SecondOfDay::MAX {
continue;
}
let Some((origin, plan)) =
reconstruct_journey(board_detail, origin_set, target, raw_arr, k)
else {
continue;
};
let label = raw_label.extend_by_footpath(walk);
journeys.push(Journey {
origin,
target,
plan,
label,
});
}
}
}
journeys
}