1use super::measure::Measure;
2use crate::Vec;
3use crate::{GraphError, IndexGraphView, Result, String};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct SignedPath<Node, Cost = i64> {
7 nodes: Vec<Node>,
8 total_cost: Cost,
9}
10
11impl<Node, Cost> SignedPath<Node, Cost> {
12 pub(super) fn from_parts(nodes: Vec<Node>, total_cost: Cost) -> Self {
13 Self { nodes, total_cost }
14 }
15
16 #[must_use]
17 pub fn nodes(&self) -> &[Node] {
18 &self.nodes
19 }
20
21 #[must_use]
22 pub const fn total_cost(&self) -> Cost
23 where
24 Cost: Copy,
25 {
26 self.total_cost
27 }
28
29 #[must_use]
30 pub fn into_nodes(self) -> Vec<Node> {
31 self.nodes
32 }
33}
34
35#[derive(Debug, Clone)]
36pub struct BellmanFord<Node, Cost = i64> {
37 source: Node,
38 nodes: Vec<Node>,
39 nodes_by_slot: Vec<Option<Node>>,
40 distances: Vec<Cost>,
41 reachable: Vec<bool>,
42 predecessors: Vec<Option<usize>>,
43 node_slot: fn(Node) -> usize,
44}
45
46impl<Node, Cost> BellmanFord<Node, Cost>
47where
48 Node: Copy + Eq,
49 Cost: Copy,
50{
51 pub(super) fn from_parts(
52 source: Node,
53 nodes: Vec<Node>,
54 nodes_by_slot: Vec<Option<Node>>,
55 distances: Vec<Cost>,
56 reachable: Vec<bool>,
57 predecessors: Vec<Option<usize>>,
58 node_slot: fn(Node) -> usize,
59 ) -> Self {
60 Self {
61 source,
62 nodes,
63 nodes_by_slot,
64 distances,
65 reachable,
66 predecessors,
67 node_slot,
68 }
69 }
70
71 #[must_use]
72 pub const fn source(&self) -> Node {
73 self.source
74 }
75
76 #[must_use]
77 pub fn nodes(&self) -> &[Node] {
78 &self.nodes
79 }
80
81 #[must_use]
82 pub fn distance_to(&self, node: Node) -> Option<Cost> {
83 let slot = (self.node_slot)(node);
84 self.nodes_by_slot
85 .get(slot)
86 .is_some_and(|stored| *stored == Some(node))
87 .then(|| self.reachable[slot])
88 .filter(|reachable| *reachable)
89 .map(|_| self.distances[slot])
90 }
91
92 #[must_use]
93 pub fn predecessor(&self, node: Node) -> Option<Node> {
94 let slot = (self.node_slot)(node);
95 self.nodes_by_slot
96 .get(slot)
97 .is_some_and(|stored| *stored == Some(node))
98 .then(|| self.predecessors[slot])
99 .flatten()
100 .and_then(|predecessor| self.nodes_by_slot[predecessor])
101 }
102
103 #[must_use]
104 pub fn path_to(&self, target: Node) -> Option<SignedPath<Node, Cost>> {
105 let total_cost = self.distance_to(target)?;
106 let mut nodes = vec![target];
107 let mut cursor = target;
108 while cursor != self.source {
109 cursor = self.predecessor(cursor)?;
110 nodes.push(cursor);
111 if nodes.len() > self.nodes.len() {
112 return None;
113 }
114 }
115 nodes.reverse();
116 Some(SignedPath { nodes, total_cost })
117 }
118}
119
120pub fn bellman_ford<G, F>(
126 graph: &G,
127 source: G::Node,
128 edge_cost: F,
129) -> Result<Option<BellmanFord<G::Node>>>
130where
131 G: IndexGraphView,
132 F: Fn(G::Edge) -> i64,
133{
134 bellman_ford_filtered(graph, source, |edge| Some(edge_cost(edge)))
135}
136
137pub fn bellman_ford_filtered<G, F>(
143 graph: &G,
144 source: G::Node,
145 edge_cost: F,
146) -> Result<Option<BellmanFord<G::Node>>>
147where
148 G: IndexGraphView,
149 F: Fn(G::Edge) -> Option<i64>,
150{
151 bellman_ford_measure_filtered(graph, source, edge_cost)
152}
153
154pub fn bellman_ford_measure<G, Cost, F>(
161 graph: &G,
162 source: G::Node,
163 edge_cost: F,
164) -> Result<Option<BellmanFord<G::Node, Cost>>>
165where
166 G: IndexGraphView,
167 Cost: Measure,
168 F: Fn(G::Edge) -> Cost,
169{
170 bellman_ford_measure_filtered(graph, source, |edge| Some(edge_cost(edge)))
171}
172
173pub fn bellman_ford_measure_filtered<G, Cost, F>(
180 graph: &G,
181 source: G::Node,
182 edge_cost: F,
183) -> Result<Option<BellmanFord<G::Node, Cost>>>
184where
185 G: IndexGraphView,
186 Cost: Measure,
187 F: Fn(G::Edge) -> Option<Cost>,
188{
189 if !graph.contains_node(source) {
190 return Ok(None);
191 }
192 let mut nodes_by_slot = vec![None; graph.node_bound()];
193 let nodes = graph.node_indices().collect::<Vec<_>>();
194 for &node in &nodes {
195 nodes_by_slot[G::node_slot(node)] = Some(node);
196 }
197 let mut edges = Vec::with_capacity(graph.edge_count());
198 for (edge, endpoints) in graph.edge_references() {
199 if let Some(weight) = edge_cost(edge) {
200 if !weight.is_valid() {
201 return Err(GraphError::InvalidAlgorithmParameter {
202 algorithm: "Bellman-Ford",
203 parameter: "edge_cost",
204 value: String::from("must be finite and totally ordered"),
205 });
206 }
207 edges.push((
208 G::node_slot(endpoints.source()),
209 G::node_slot(endpoints.target()),
210 weight,
211 ));
212 }
213 }
214 let mut distances = vec![Cost::zero(); graph.node_bound()];
215 let mut reachable = vec![false; graph.node_bound()];
216 let mut predecessors = vec![None; graph.node_bound()];
217 reachable[G::node_slot(source)] = true;
218 for _ in 1..nodes.len() {
219 if !relax_all(&edges, &mut distances, &mut reachable, &mut predecessors)? {
220 break;
221 }
222 }
223 reject_negative_cycle(&edges, &distances, &reachable)?;
224
225 Ok(Some(BellmanFord {
226 source,
227 nodes,
228 nodes_by_slot,
229 distances,
230 reachable,
231 predecessors,
232 node_slot: G::node_slot,
233 }))
234}
235
236fn relax_all<Cost: Measure>(
237 edges: &[(usize, usize, Cost)],
238 distances: &mut [Cost],
239 reachable: &mut [bool],
240 predecessors: &mut [Option<usize>],
241) -> Result<bool> {
242 let mut changed = false;
243 for &(source, target, weight) in edges {
244 if !reachable[source] {
245 continue;
246 }
247 let candidate =
248 distances[source]
249 .checked_add(weight)
250 .ok_or(GraphError::ArithmeticOverflow {
251 operation: "Bellman-Ford edge relaxation",
252 })?;
253 if !reachable[target]
254 || candidate.compare(distances[target]) == Some(core::cmp::Ordering::Less)
255 {
256 distances[target] = candidate;
257 reachable[target] = true;
258 predecessors[target] = Some(source);
259 changed = true;
260 }
261 }
262 Ok(changed)
263}
264
265fn reject_negative_cycle<Cost: Measure>(
266 edges: &[(usize, usize, Cost)],
267 distances: &[Cost],
268 reachable: &[bool],
269) -> Result<()> {
270 for &(source, target, weight) in edges {
271 if !reachable[source] {
272 continue;
273 }
274 let candidate =
275 distances[source]
276 .checked_add(weight)
277 .ok_or(GraphError::ArithmeticOverflow {
278 operation: "Bellman-Ford cycle check",
279 })?;
280 if reachable[target]
281 && candidate.compare(distances[target]) == Some(core::cmp::Ordering::Less)
282 {
283 return Err(GraphError::NegativeCycle {
284 algorithm: "Bellman-Ford",
285 });
286 }
287 }
288 Ok(())
289}