use std::collections::VecDeque;
use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
pub fn distances_all(graph: &Graph) -> IgraphResult<Vec<Option<u32>>> {
let n = graph.vcount();
let n_us = n as usize;
if n == 0 {
return Ok(Vec::new());
}
let mut result = vec![
None;
n_us.checked_mul(n_us).ok_or_else(|| {
IgraphError::InvalidArgument("distances_all: n*n overflows usize".into())
})?
];
if graph.is_directed() {
let adj = build_out_adj(graph, n_us)?;
for src in 0..n {
bfs_distances_with_adj(&adj, src, n_us, &mut result);
}
} else {
for src in 0..n {
bfs_distances_undirected(graph, src, n_us, &mut result)?;
}
}
Ok(result)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DistancesMode {
Out,
In,
All,
}
pub fn distances_all_with_mode(
graph: &Graph,
mode: DistancesMode,
) -> IgraphResult<Vec<Option<u32>>> {
let n = graph.vcount();
let n_us = n as usize;
if n == 0 {
return Ok(Vec::new());
}
let mut result = vec![
None;
n_us.checked_mul(n_us).ok_or_else(|| {
IgraphError::InvalidArgument("distances_all_with_mode: n*n overflows usize".into())
})?
];
if !graph.is_directed() {
for src in 0..n {
bfs_distances_undirected(graph, src, n_us, &mut result)?;
}
return Ok(result);
}
let adj = match mode {
DistancesMode::Out => build_out_adj(graph, n_us)?,
DistancesMode::In => build_in_adj(graph, n_us)?,
DistancesMode::All => build_all_adj(graph, n_us)?,
};
for src in 0..n {
bfs_distances_with_adj(&adj, src, n_us, &mut result);
}
Ok(result)
}
fn bfs_distances_undirected(
graph: &Graph,
source: VertexId,
n_us: usize,
result: &mut [Option<u32>],
) -> IgraphResult<()> {
let src_us = source as usize;
let row_offset = src_us * n_us;
let mut visited = vec![false; n_us];
let mut queue = VecDeque::new();
visited[src_us] = true;
result[row_offset + src_us] = Some(0);
queue.push_back((source, 0u32));
while let Some((cur, dist)) = queue.pop_front() {
let next_dist = dist + 1;
for nb in graph.neighbors_iter(cur)? {
let nb_idx = nb as usize;
if !visited[nb_idx] {
visited[nb_idx] = true;
result[row_offset + nb_idx] = Some(next_dist);
queue.push_back((nb, next_dist));
}
}
}
Ok(())
}
fn bfs_distances_with_adj(
adj: &[Vec<VertexId>],
source: VertexId,
n_us: usize,
result: &mut [Option<u32>],
) {
let src_us = source as usize;
let row_offset = src_us * n_us;
let mut visited = vec![false; n_us];
let mut queue = VecDeque::new();
visited[src_us] = true;
result[row_offset + src_us] = Some(0);
queue.push_back((source, 0u32));
while let Some((cur, dist)) = queue.pop_front() {
let next_dist = dist + 1;
for &nb in &adj[cur as usize] {
let nb_idx = nb as usize;
if !visited[nb_idx] {
visited[nb_idx] = true;
result[row_offset + nb_idx] = Some(next_dist);
queue.push_back((nb, next_dist));
}
}
}
}
fn build_out_adj(graph: &Graph, n_us: usize) -> IgraphResult<Vec<Vec<VertexId>>> {
let m =
u32::try_from(graph.ecount()).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n_us];
for eid in 0..m {
let (from, to) = graph.edge(eid)?;
adj[from as usize].push(to);
}
Ok(adj)
}
fn build_in_adj(graph: &Graph, n_us: usize) -> IgraphResult<Vec<Vec<VertexId>>> {
let m =
u32::try_from(graph.ecount()).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n_us];
for eid in 0..m {
let (from, to) = graph.edge(eid)?;
adj[to as usize].push(from);
}
Ok(adj)
}
fn build_all_adj(graph: &Graph, n_us: usize) -> IgraphResult<Vec<Vec<VertexId>>> {
let m =
u32::try_from(graph.ecount()).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n_us];
for eid in 0..m {
let (from, to) = graph.edge(eid)?;
adj[from as usize].push(to);
if from != to {
adj[to as usize].push(from);
}
}
Ok(adj)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph() {
let g = Graph::with_vertices(0);
let d = distances_all(&g).unwrap();
assert!(d.is_empty());
}
#[test]
fn singleton() {
let g = Graph::with_vertices(1);
let d = distances_all(&g).unwrap();
assert_eq!(d, vec![Some(0)]);
}
#[test]
fn path_graph() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
let d = distances_all(&g).unwrap();
let n = 4usize;
assert_eq!(d[0], Some(0)); assert_eq!(d[1], Some(1)); assert_eq!(d[2], Some(2)); assert_eq!(d[3], Some(3)); assert_eq!(d[3 * n], Some(3)); assert_eq!(d[n + 3], Some(2)); }
#[test]
fn triangle() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
let d = distances_all(&g).unwrap();
let n = 3;
for i in 0..n {
assert_eq!(d[i * n + i], Some(0));
for j in 0..n {
if i != j {
assert_eq!(d[i * n + j], Some(1));
}
}
}
}
#[test]
fn two_components() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(2, 3).unwrap();
let d = distances_all(&g).unwrap();
let n = 4usize;
assert_eq!(d[1], Some(1)); assert_eq!(d[2 * n + 3], Some(1));
assert_eq!(d[2], None); assert_eq!(d[n + 3], None); }
#[test]
fn cycle_5() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(3, 4).unwrap();
g.add_edge(4, 0).unwrap();
let d = distances_all(&g).unwrap();
assert_eq!(d[0], Some(0));
assert_eq!(d[1], Some(1));
assert_eq!(d[2], Some(2));
assert_eq!(d[3], Some(2));
assert_eq!(d[4], Some(1));
}
#[test]
fn directed_out() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let d = distances_all(&g).unwrap();
let n = 3usize;
assert_eq!(d[2], Some(2)); assert_eq!(d[2 * n], None); assert_eq!(d[n], None); }
#[test]
fn directed_in_mode() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let d = distances_all_with_mode(&g, DistancesMode::In).unwrap();
let n = 3usize;
assert_eq!(d[2 * n + 1], Some(1));
assert_eq!(d[2 * n], Some(2)); assert_eq!(d[1], None); }
#[test]
fn directed_all_mode() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let d = distances_all_with_mode(&g, DistancesMode::All).unwrap();
let n = 3;
assert_eq!(d[2], Some(2));
assert_eq!(d[2 * n], Some(2));
}
#[test]
fn symmetric_undirected() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(3, 4).unwrap();
let d = distances_all(&g).unwrap();
let n = 5;
for i in 0..n {
for j in 0..n {
assert_eq!(d[i * n + j], d[j * n + i], "not symmetric at ({i},{j})");
}
}
}
#[test]
fn isolated_vertices() {
let g = Graph::with_vertices(3);
let d = distances_all(&g).unwrap();
let n = 3;
for i in 0..n {
assert_eq!(d[i * n + i], Some(0));
for j in 0..n {
if i != j {
assert_eq!(d[i * n + j], None);
}
}
}
}
#[test]
fn oracle_star() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(0, 3).unwrap();
let d = distances_all(&g).unwrap();
let n = 4;
for item in d.iter().take(4).skip(1) {
assert_eq!(*item, Some(1));
}
assert_eq!(d[n + 2], Some(2));
assert_eq!(d[n + 3], Some(2));
assert_eq!(d[2 * n + 3], Some(2));
}
}