use std::cmp::Reverse;
use std::collections::BinaryHeap;
#[derive(Debug, Clone)]
pub struct TemporalPath {
pub nodes: Vec<usize>,
pub times: Vec<f64>,
pub edge_times: Vec<f64>,
pub total_duration: f64,
pub n_hops: usize,
}
impl TemporalPath {
pub fn new(src: usize, start_time: f64) -> Self {
Self {
nodes: vec![src],
times: vec![start_time],
edge_times: Vec::new(),
total_duration: 0.0,
n_hops: 0,
}
}
pub fn extend(&mut self, node: usize, edge_time: f64, arrival_time: f64) {
self.nodes.push(node);
self.times.push(arrival_time);
self.edge_times.push(edge_time);
let start = self.times.first().copied().unwrap_or(0.0);
self.total_duration = arrival_time - start;
self.n_hops += 1;
}
pub fn arrival_time(&self) -> f64 {
self.times.last().copied().unwrap_or(0.0)
}
}
const TIME_SCALE: f64 = 1_000_000_000.0;
pub struct TemporalDijkstra;
impl TemporalDijkstra {
pub fn earliest_arrival(
src: usize,
dst: usize,
start_time: f64,
edges: &[(usize, usize, f64, f64)],
n_nodes: usize,
) -> Option<TemporalPath> {
let mut arrival = vec![f64::INFINITY; n_nodes];
arrival[src] = start_time;
let mut adj: Vec<Vec<(usize, f64, f64)>> = vec![Vec::new(); n_nodes];
for &(u, v, dep, travel) in edges {
adj[u].push((v, dep, travel));
}
for list in &mut adj {
list.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
}
let mut pq: BinaryHeap<Reverse<(i64, usize)>> = BinaryHeap::new();
pq.push(Reverse(((start_time * TIME_SCALE) as i64, src)));
let mut prev: Vec<Option<(usize, f64)>> = vec![None; n_nodes];
while let Some(Reverse((t_scaled, u))) = pq.pop() {
let t = t_scaled as f64 / TIME_SCALE;
if t > arrival[u] {
continue;
}
if u == dst {
break;
}
for &(v, dep, travel) in &adj[u] {
if dep >= t {
let arrive = dep + travel;
if arrive < arrival[v] {
arrival[v] = arrive;
prev[v] = Some((u, dep));
pq.push(Reverse(((arrive * TIME_SCALE) as i64, v)));
}
}
}
}
if arrival[dst].is_infinite() {
return None;
}
let mut rev_nodes = vec![dst];
let mut cursor = dst;
while let Some((p, _)) = prev[cursor] {
rev_nodes.push(p);
cursor = p;
if cursor == src {
break;
}
}
rev_nodes.reverse();
let mut path = TemporalPath::new(src, start_time);
let mut curr_arr = start_time;
for window in rev_nodes.windows(2) {
let (u, v) = (window[0], window[1]);
let chosen = adj[u]
.iter()
.find(|&&(nv, dep, _)| nv == v && dep >= curr_arr);
if let Some(&(_, dep, travel)) = chosen {
path.extend(v, dep, dep + travel);
curr_arr = dep + travel;
}
}
Some(path)
}
pub fn latest_departure(
src: usize,
dst: usize,
deadline: f64,
edges: &[(usize, usize, f64, f64)],
n_nodes: usize,
) -> Option<TemporalPath> {
let reversed: Vec<(usize, usize, f64, f64)> = edges
.iter()
.map(|&(u, v, dep, travel)| (v, u, dep + travel, travel))
.collect();
let inverted: Vec<(usize, usize, f64, f64)> = reversed
.iter()
.map(|&(u, v, arr, travel)| (u, v, deadline - arr, travel))
.collect();
let path = Self::earliest_arrival(dst, src, 0.0, &inverted, n_nodes)?;
let real_times: Vec<f64> = path.times.iter().map(|&t| deadline - t).collect();
let real_edges: Vec<f64> = path.edge_times.iter().map(|&t| deadline - t).collect();
let mut result = TemporalPath::new(src, real_times.last().copied().unwrap_or(0.0));
let mut rev_nodes = path.nodes.clone();
rev_nodes.reverse();
let mut rev_times = real_times;
rev_times.reverse();
let mut rev_edges = real_edges;
rev_edges.reverse();
result.nodes = rev_nodes;
result.times = rev_times;
result.edge_times = rev_edges;
result.n_hops = path.n_hops;
result.total_duration = path.total_duration;
Some(result)
}
pub fn fastest_path(
src: usize,
dst: usize,
start_time: f64,
edges: &[(usize, usize, f64, f64)],
n_nodes: usize,
) -> Option<TemporalPath> {
let mut adj: Vec<Vec<(usize, f64, f64)>> = vec![Vec::new(); n_nodes];
for &(u, v, dep, travel) in edges {
adj[u].push((v, dep, travel));
}
for list in &mut adj {
list.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
}
let mut best_dur = vec![f64::INFINITY; n_nodes];
best_dur[src] = 0.0;
let mut pq: BinaryHeap<Reverse<(i64, usize, i64)>> = BinaryHeap::new();
pq.push(Reverse((0, src, (start_time * TIME_SCALE) as i64)));
let mut prev: Vec<Option<(usize, f64)>> = vec![None; n_nodes];
while let Some(Reverse((dur_scaled, u, arr_scaled))) = pq.pop() {
let dur = dur_scaled as f64 / TIME_SCALE;
let arr = arr_scaled as f64 / TIME_SCALE;
if dur > best_dur[u] {
continue;
}
if u == dst {
break;
}
for &(v, dep, travel) in &adj[u] {
if dep >= arr {
let new_arr = dep + travel;
let new_dur = new_arr - start_time;
if new_dur < best_dur[v] {
best_dur[v] = new_dur;
prev[v] = Some((u, dep));
pq.push(Reverse((
(new_dur * TIME_SCALE) as i64,
v,
(new_arr * TIME_SCALE) as i64,
)));
}
}
}
}
if best_dur[dst].is_infinite() {
return None;
}
let mut rev_nodes = vec![dst];
let mut cursor = dst;
while let Some((p, _)) = prev[cursor] {
rev_nodes.push(p);
cursor = p;
if cursor == src {
break;
}
}
rev_nodes.reverse();
let mut path = TemporalPath::new(src, start_time);
let mut curr_arr = start_time;
for window in rev_nodes.windows(2) {
let (u, v) = (window[0], window[1]);
let chosen = adj[u]
.iter()
.find(|&&(nv, dep, _)| nv == v && dep >= curr_arr);
if let Some(&(_, dep, travel)) = chosen {
path.extend(v, dep, dep + travel);
curr_arr = dep + travel;
}
}
Some(path)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn small_edges() -> Vec<(usize, usize, f64, f64)> {
vec![(0, 1, 1.0, 1.0), (1, 2, 3.0, 1.0), (0, 2, 5.0, 1.0)]
}
#[test]
fn test_earliest_arrival_two_hop() {
let edges = small_edges();
let path = TemporalDijkstra::earliest_arrival(0, 2, 0.0, &edges, 3)
.expect("path should exist");
assert_eq!(path.nodes, vec![0, 1, 2], "nodes={:?}", path.nodes);
assert!((path.arrival_time() - 4.0).abs() < 1e-9);
}
#[test]
fn test_earliest_arrival_direct_faster() {
let edges = vec![(0, 1, 1.0, 1.0), (1, 2, 5.0, 1.0), (0, 2, 1.0, 1.0)];
let path = TemporalDijkstra::earliest_arrival(0, 2, 0.0, &edges, 3)
.expect("path should exist");
assert_eq!(path.nodes, vec![0, 2], "expected direct path");
assert!((path.arrival_time() - 2.0).abs() < 1e-9);
}
#[test]
fn test_no_path_returns_none() {
let edges = vec![(0, 1, 1.0, 1.0)];
let result = TemporalDijkstra::earliest_arrival(0, 2, 0.0, &edges, 3);
assert!(result.is_none());
}
#[test]
fn test_fastest_path() {
let edges = small_edges();
let path = TemporalDijkstra::fastest_path(0, 2, 0.0, &edges, 3)
.expect("path should exist");
assert!(path.total_duration <= 4.0 + 1e-9, "duration={}", path.total_duration);
}
}