use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap};
use super::graph::{DiGraph, NodeIdx};
struct State {
cost: f64,
estimated: f64,
node: NodeIdx,
}
impl PartialEq for State {
fn eq(&self, other: &Self) -> bool {
self.estimated == other.estimated
}
}
impl Eq for State {}
impl Ord for State {
fn cmp(&self, other: &Self) -> Ordering {
other
.estimated
.partial_cmp(&self.estimated)
.unwrap_or(Ordering::Equal)
}
}
impl PartialOrd for State {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
pub fn astar<N, E, G, C, H>(
graph: &DiGraph<N, E>,
start: NodeIdx,
is_goal: G,
edge_cost: C,
heuristic: H,
) -> Option<(f64, Vec<NodeIdx>)>
where
G: Fn(NodeIdx) -> bool,
C: Fn(&E) -> f64,
H: Fn(NodeIdx) -> f64,
{
let mut dist: HashMap<NodeIdx, f64> = HashMap::new();
let mut came_from: HashMap<NodeIdx, NodeIdx> = HashMap::new();
let mut heap = BinaryHeap::new();
dist.insert(start, 0.0);
heap.push(State {
cost: 0.0,
estimated: heuristic(start),
node: start,
});
while let Some(State { cost, node, .. }) = heap.pop() {
if is_goal(node) {
let mut path = vec![node];
let mut current = node;
while let Some(&prev) = came_from.get(¤t) {
path.push(prev);
current = prev;
}
path.reverse();
return Some((cost, path));
}
if let Some(&d) = dist.get(&node) {
if cost > d {
continue;
}
}
for &edge_idx in graph.outgoing_edges(node) {
if let (Some(edge_data), Some((_, to))) =
(graph.edge_weight(edge_idx), graph.edge_endpoints(edge_idx))
{
let next_cost = cost + edge_cost(edge_data);
let is_better = dist.get(&to).map(|&d| next_cost < d).unwrap_or(true);
if is_better {
dist.insert(to, next_cost);
came_from.insert(to, node);
heap.push(State {
cost: next_cost,
estimated: next_cost + heuristic(to),
node: to,
});
}
}
}
}
None
}
#[allow(dead_code)]
pub fn dijkstra<N, E, C>(
graph: &DiGraph<N, E>,
start: NodeIdx,
goal: Option<NodeIdx>,
edge_cost: C,
) -> HashMap<NodeIdx, f64>
where
C: Fn(&E) -> f64,
{
let mut dist: HashMap<NodeIdx, f64> = HashMap::new();
let mut heap = BinaryHeap::new();
dist.insert(start, 0.0);
heap.push(State {
cost: 0.0,
estimated: 0.0,
node: start,
});
while let Some(State { cost, node, .. }) = heap.pop() {
if let Some(g) = goal {
if node == g {
break;
}
}
if let Some(&d) = dist.get(&node) {
if cost > d {
continue;
}
}
for &edge_idx in graph.outgoing_edges(node) {
if let (Some(edge_data), Some((_, to))) =
(graph.edge_weight(edge_idx), graph.edge_endpoints(edge_idx))
{
let next_cost = cost + edge_cost(edge_data);
let is_better = dist.get(&to).map(|&d| next_cost < d).unwrap_or(true);
if is_better {
dist.insert(to, next_cost);
heap.push(State {
cost: next_cost,
estimated: next_cost,
node: to,
});
}
}
}
}
dist
}
pub(crate) fn dijkstra_with_secondary<N, E, C, S>(
graph: &DiGraph<N, E>,
start: NodeIdx,
goal: Option<NodeIdx>,
edge_cost: C,
edge_secondary: S,
) -> HashMap<NodeIdx, (f64, f64)>
where
C: Fn(&E) -> f64,
S: Fn(&E) -> f64,
{
let mut dist: HashMap<NodeIdx, (f64, f64)> = HashMap::new();
let mut heap = BinaryHeap::new();
dist.insert(start, (0.0, 0.0));
heap.push(State {
cost: 0.0,
estimated: 0.0,
node: start,
});
while let Some(State { cost, node, .. }) = heap.pop() {
if let Some(g) = goal {
if node == g {
break;
}
}
let Some(&(known_cost, known_secondary)) = dist.get(&node) else {
continue;
};
if cost > known_cost {
continue;
}
for &edge_idx in graph.outgoing_edges(node) {
if let (Some(edge_data), Some((_, to))) =
(graph.edge_weight(edge_idx), graph.edge_endpoints(edge_idx))
{
let next_cost = cost + edge_cost(edge_data);
let next_secondary = known_secondary + edge_secondary(edge_data);
let is_better = dist
.get(&to)
.map(|&(current_cost, current_secondary)| {
next_cost < current_cost
|| (next_cost == current_cost && next_secondary < current_secondary)
})
.unwrap_or(true);
if is_better {
dist.insert(to, (next_cost, next_secondary));
heap.push(State {
cost: next_cost,
estimated: next_cost,
node: to,
});
}
}
}
}
dist
}
pub fn kosaraju_scc<N, E>(graph: &DiGraph<N, E>) -> Vec<Vec<NodeIdx>> {
let n = graph.node_count();
if n == 0 {
return Vec::new();
}
let mut visited = vec![false; n];
let mut finish_order = Vec::with_capacity(n);
for i in 0..n {
if !visited[i] {
dfs_forward(graph, NodeIdx(i), &mut visited, &mut finish_order);
}
}
let mut visited = vec![false; n];
let mut sccs = Vec::new();
for &node in finish_order.iter().rev() {
if !visited[node.0] {
let mut component = Vec::new();
dfs_backward(graph, node, &mut visited, &mut component);
sccs.push(component);
}
}
sccs
}
fn dfs_forward<N, E>(
graph: &DiGraph<N, E>,
node: NodeIdx,
visited: &mut [bool],
finish_order: &mut Vec<NodeIdx>,
) {
let mut stack = vec![(node, 0usize)];
while let Some((current, edge_idx)) = stack.last_mut() {
let current = *current;
if !visited[current.0] {
visited[current.0] = true;
}
let edges = graph.outgoing_edges(current);
let mut found_next = false;
while *edge_idx < edges.len() {
let e = edges[*edge_idx];
*edge_idx += 1;
if let Some((_, to)) = graph.edge_endpoints(e) {
if !visited[to.0] {
stack.push((to, 0));
found_next = true;
break;
}
}
}
if !found_next {
finish_order.push(current);
stack.pop();
}
}
}
fn dfs_backward<N, E>(
graph: &DiGraph<N, E>,
node: NodeIdx,
visited: &mut [bool],
component: &mut Vec<NodeIdx>,
) {
let mut stack = vec![(node, 0usize)];
while let Some((current, edge_idx)) = stack.last_mut() {
let current = *current;
if !visited[current.0] {
visited[current.0] = true;
component.push(current);
}
let edges = graph.incoming_edges(current);
let mut found_next = false;
while *edge_idx < edges.len() {
let e = edges[*edge_idx];
*edge_idx += 1;
if let Some((from, _)) = graph.edge_endpoints(e) {
if !visited[from.0] {
stack.push((from, 0));
found_next = true;
break;
}
}
}
if !found_next {
stack.pop();
}
}
}