1use std::cmp::Reverse;
8use std::collections::BinaryHeap;
9
10#[derive(Debug, Clone)]
12pub struct TemporalPath {
13 pub nodes: Vec<usize>,
15 pub times: Vec<f64>,
17 pub edge_times: Vec<f64>,
19 pub total_duration: f64,
21 pub n_hops: usize,
23}
24
25impl TemporalPath {
26 pub fn new(src: usize, start_time: f64) -> Self {
28 Self {
29 nodes: vec![src],
30 times: vec![start_time],
31 edge_times: Vec::new(),
32 total_duration: 0.0,
33 n_hops: 0,
34 }
35 }
36
37 pub fn extend(&mut self, node: usize, edge_time: f64, arrival_time: f64) {
39 self.nodes.push(node);
40 self.times.push(arrival_time);
41 self.edge_times.push(edge_time);
42 let start = self.times.first().copied().unwrap_or(0.0);
43 self.total_duration = arrival_time - start;
44 self.n_hops += 1;
45 }
46
47 pub fn arrival_time(&self) -> f64 {
49 self.times.last().copied().unwrap_or(0.0)
50 }
51}
52
53const TIME_SCALE: f64 = 1_000_000_000.0;
56
57pub struct TemporalDijkstra;
59
60impl TemporalDijkstra {
61 pub fn earliest_arrival(
72 src: usize,
73 dst: usize,
74 start_time: f64,
75 edges: &[(usize, usize, f64, f64)],
76 n_nodes: usize,
77 ) -> Option<TemporalPath> {
78 let mut arrival = vec![f64::INFINITY; n_nodes];
79 arrival[src] = start_time;
80
81 let mut adj: Vec<Vec<(usize, f64, f64)>> = vec![Vec::new(); n_nodes];
83 for &(u, v, dep, travel) in edges {
84 adj[u].push((v, dep, travel));
85 }
86 for list in &mut adj {
87 list.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
88 }
89
90 let mut pq: BinaryHeap<Reverse<(i64, usize)>> = BinaryHeap::new();
92 pq.push(Reverse(((start_time * TIME_SCALE) as i64, src)));
93
94 let mut prev: Vec<Option<(usize, f64)>> = vec![None; n_nodes];
96
97 while let Some(Reverse((t_scaled, u))) = pq.pop() {
98 let t = t_scaled as f64 / TIME_SCALE;
99 if t > arrival[u] {
100 continue;
101 }
102 if u == dst {
103 break;
104 }
105
106 for &(v, dep, travel) in &adj[u] {
108 if dep >= t {
109 let arrive = dep + travel;
110 if arrive < arrival[v] {
111 arrival[v] = arrive;
112 prev[v] = Some((u, dep));
113 pq.push(Reverse(((arrive * TIME_SCALE) as i64, v)));
114 }
115 }
116 }
117 }
118
119 if arrival[dst].is_infinite() {
120 return None;
121 }
122
123 let mut rev_nodes = vec![dst];
125 let mut cursor = dst;
126 while let Some((p, _)) = prev[cursor] {
127 rev_nodes.push(p);
128 cursor = p;
129 if cursor == src {
130 break;
131 }
132 }
133 rev_nodes.reverse();
134
135 let mut path = TemporalPath::new(src, start_time);
137 let mut curr_arr = start_time;
138
139 for window in rev_nodes.windows(2) {
140 let (u, v) = (window[0], window[1]);
141 let chosen = adj[u]
143 .iter()
144 .find(|&&(nv, dep, _)| nv == v && dep >= curr_arr);
145 if let Some(&(_, dep, travel)) = chosen {
146 path.extend(v, dep, dep + travel);
147 curr_arr = dep + travel;
148 }
149 }
150
151 Some(path)
152 }
153
154 pub fn latest_departure(
158 src: usize,
159 dst: usize,
160 deadline: f64,
161 edges: &[(usize, usize, f64, f64)],
162 n_nodes: usize,
163 ) -> Option<TemporalPath> {
164 let reversed: Vec<(usize, usize, f64, f64)> = edges
168 .iter()
169 .map(|&(u, v, dep, travel)| (v, u, dep + travel, travel))
170 .collect();
171
172 let inverted: Vec<(usize, usize, f64, f64)> = reversed
177 .iter()
178 .map(|&(u, v, arr, travel)| (u, v, deadline - arr, travel))
179 .collect();
180
181 let path = Self::earliest_arrival(dst, src, 0.0, &inverted, n_nodes)?;
182
183 let real_times: Vec<f64> = path.times.iter().map(|&t| deadline - t).collect();
185 let real_edges: Vec<f64> = path.edge_times.iter().map(|&t| deadline - t).collect();
186
187 let mut result = TemporalPath::new(src, real_times.last().copied().unwrap_or(0.0));
188 let mut rev_nodes = path.nodes.clone();
190 rev_nodes.reverse();
191 let mut rev_times = real_times;
192 rev_times.reverse();
193 let mut rev_edges = real_edges;
194 rev_edges.reverse();
195
196 result.nodes = rev_nodes;
197 result.times = rev_times;
198 result.edge_times = rev_edges;
199 result.n_hops = path.n_hops;
200 result.total_duration = path.total_duration;
201
202 Some(result)
203 }
204
205 pub fn fastest_path(
210 src: usize,
211 dst: usize,
212 start_time: f64,
213 edges: &[(usize, usize, f64, f64)],
214 n_nodes: usize,
215 ) -> Option<TemporalPath> {
216 let mut adj: Vec<Vec<(usize, f64, f64)>> = vec![Vec::new(); n_nodes];
219 for &(u, v, dep, travel) in edges {
220 adj[u].push((v, dep, travel));
221 }
222 for list in &mut adj {
223 list.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
224 }
225
226 let mut best_dur = vec![f64::INFINITY; n_nodes];
228 best_dur[src] = 0.0;
229
230 let mut pq: BinaryHeap<Reverse<(i64, usize, i64)>> = BinaryHeap::new();
232 pq.push(Reverse((0, src, (start_time * TIME_SCALE) as i64)));
233
234 let mut prev: Vec<Option<(usize, f64)>> = vec![None; n_nodes];
235
236 while let Some(Reverse((dur_scaled, u, arr_scaled))) = pq.pop() {
237 let dur = dur_scaled as f64 / TIME_SCALE;
238 let arr = arr_scaled as f64 / TIME_SCALE;
239 if dur > best_dur[u] {
240 continue;
241 }
242 if u == dst {
243 break;
244 }
245
246 for &(v, dep, travel) in &adj[u] {
247 if dep >= arr {
248 let new_arr = dep + travel;
249 let new_dur = new_arr - start_time;
250 if new_dur < best_dur[v] {
251 best_dur[v] = new_dur;
252 prev[v] = Some((u, dep));
253 pq.push(Reverse((
254 (new_dur * TIME_SCALE) as i64,
255 v,
256 (new_arr * TIME_SCALE) as i64,
257 )));
258 }
259 }
260 }
261 }
262
263 if best_dur[dst].is_infinite() {
264 return None;
265 }
266
267 let mut rev_nodes = vec![dst];
269 let mut cursor = dst;
270 while let Some((p, _)) = prev[cursor] {
271 rev_nodes.push(p);
272 cursor = p;
273 if cursor == src {
274 break;
275 }
276 }
277 rev_nodes.reverse();
278
279 let mut path = TemporalPath::new(src, start_time);
280 let mut curr_arr = start_time;
281 for window in rev_nodes.windows(2) {
282 let (u, v) = (window[0], window[1]);
283 let chosen = adj[u]
284 .iter()
285 .find(|&&(nv, dep, _)| nv == v && dep >= curr_arr);
286 if let Some(&(_, dep, travel)) = chosen {
287 path.extend(v, dep, dep + travel);
288 curr_arr = dep + travel;
289 }
290 }
291
292 Some(path)
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 fn small_edges() -> Vec<(usize, usize, f64, f64)> {
306 vec![(0, 1, 1.0, 1.0), (1, 2, 3.0, 1.0), (0, 2, 5.0, 1.0)]
307 }
308
309 #[test]
310 fn test_earliest_arrival_two_hop() {
311 let edges = small_edges();
312 let path =
313 TemporalDijkstra::earliest_arrival(0, 2, 0.0, &edges, 3).expect("path should exist");
314 assert_eq!(path.nodes, vec![0, 1, 2], "nodes={:?}", path.nodes);
316 assert!((path.arrival_time() - 4.0).abs() < 1e-9);
317 }
318
319 #[test]
320 fn test_earliest_arrival_direct_faster() {
321 let edges = vec![(0, 1, 1.0, 1.0), (1, 2, 5.0, 1.0), (0, 2, 1.0, 1.0)];
323 let path =
324 TemporalDijkstra::earliest_arrival(0, 2, 0.0, &edges, 3).expect("path should exist");
325 assert_eq!(path.nodes, vec![0, 2], "expected direct path");
326 assert!((path.arrival_time() - 2.0).abs() < 1e-9);
327 }
328
329 #[test]
330 fn test_no_path_returns_none() {
331 let edges = vec![(0, 1, 1.0, 1.0)];
333 let result = TemporalDijkstra::earliest_arrival(0, 2, 0.0, &edges, 3);
334 assert!(result.is_none());
335 }
336
337 #[test]
338 fn test_fastest_path() {
339 let edges = small_edges();
340 let path = TemporalDijkstra::fastest_path(0, 2, 0.0, &edges, 3).expect("path should exist");
341 assert!(
343 path.total_duration <= 4.0 + 1e-9,
344 "duration={}",
345 path.total_duration
346 );
347 }
348}