use std::collections::VecDeque;
use crate::core::{Graph, IgraphResult, VertexId};
pub fn connect_neighborhood(graph: &Graph, order: u32) -> IgraphResult<Graph> {
let n = graph.vcount();
let directed = graph.is_directed();
if order < 2 || n == 0 {
let mut result = Graph::new(n, directed)?;
let ecount = graph.ecount();
let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(ecount);
for eid in 0..ecount {
#[allow(clippy::cast_possible_truncation)]
let eid_u32 = eid as u32;
edges.push(graph.edge(eid_u32)?);
}
result.add_edges(edges)?;
return Ok(result);
}
let adj = build_adjacency_list(graph, directed)?;
let mut new_edges: Vec<(VertexId, VertexId)> = Vec::new();
let mut visited = vec![0u32; n as usize];
for i in 0..n {
let marker = i + 1;
visited[i as usize] = marker;
for &nei in &adj[i as usize] {
visited[nei as usize] = marker;
}
let mut queue: VecDeque<(VertexId, u32)> = VecDeque::new();
for &nei in &adj[i as usize] {
queue.push_back((nei, 1));
}
while let Some((node, dist)) = queue.pop_front() {
if dist >= order {
continue;
}
for &nei in &adj[node as usize] {
if visited[nei as usize] != marker {
visited[nei as usize] = marker;
if directed || i < nei {
new_edges.push((i, nei));
}
if dist + 1 < order {
queue.push_back((nei, dist + 1));
}
}
}
}
}
let ecount = graph.ecount();
let mut all_edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(ecount + new_edges.len());
for eid in 0..ecount {
#[allow(clippy::cast_possible_truncation)]
let eid_u32 = eid as u32;
all_edges.push(graph.edge(eid_u32)?);
}
all_edges.extend(new_edges);
let mut result = Graph::new(n, directed)?;
result.add_edges(all_edges)?;
Ok(result)
}
pub fn graph_power(graph: &Graph, order: u32) -> IgraphResult<Graph> {
let n = graph.vcount();
let directed = graph.is_directed();
if order == 0 {
return Graph::new(n, directed);
}
let adj = build_simple_adjacency_list(graph, directed)?;
if order == 1 {
return build_graph_from_adj(&adj, n, directed);
}
let mut edges: Vec<(VertexId, VertexId)> = Vec::new();
let mut visited = vec![0u32; n as usize];
for i in 0..n {
let marker = i + 1;
visited[i as usize] = marker;
let mut queue: VecDeque<(VertexId, u32)> = VecDeque::new();
for &nei in &adj[i as usize] {
if visited[nei as usize] != marker {
visited[nei as usize] = marker;
if directed || i < nei {
edges.push((i, nei));
}
if order > 1 {
queue.push_back((nei, 1));
}
}
}
while let Some((node, dist)) = queue.pop_front() {
if dist >= order {
continue;
}
for &nei in &adj[node as usize] {
if visited[nei as usize] != marker {
visited[nei as usize] = marker;
if directed || i < nei {
edges.push((i, nei));
}
if dist + 1 < order {
queue.push_back((nei, dist + 1));
}
}
}
}
}
let mut result = Graph::new(n, directed)?;
result.add_edges(edges)?;
Ok(result)
}
fn build_adjacency_list(graph: &Graph, _directed: bool) -> IgraphResult<Vec<Vec<VertexId>>> {
let n = graph.vcount() as usize;
let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
let ecount = graph.ecount();
for eid in 0..ecount {
#[allow(clippy::cast_possible_truncation)]
let eid_u32 = eid as u32;
let (src, tgt) = graph.edge(eid_u32)?;
adj[src as usize].push(tgt);
if !graph.is_directed() && src != tgt {
adj[tgt as usize].push(src);
}
}
Ok(adj)
}
fn build_simple_adjacency_list(graph: &Graph, _directed: bool) -> IgraphResult<Vec<Vec<VertexId>>> {
let n = graph.vcount() as usize;
let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
let ecount = graph.ecount();
for eid in 0..ecount {
#[allow(clippy::cast_possible_truncation)]
let eid_u32 = eid as u32;
let (src, tgt) = graph.edge(eid_u32)?;
if src == tgt {
continue; }
adj[src as usize].push(tgt);
if !graph.is_directed() {
adj[tgt as usize].push(src);
}
}
for list in &mut adj {
list.sort_unstable();
list.dedup();
}
Ok(adj)
}
fn build_graph_from_adj(adj: &[Vec<VertexId>], n: u32, directed: bool) -> IgraphResult<Graph> {
let mut edges: Vec<(VertexId, VertexId)> = Vec::new();
for (i, neighbors) in adj.iter().enumerate() {
#[allow(clippy::cast_possible_truncation)]
let i_u32 = i as u32;
for &nei in neighbors {
if directed || i_u32 < nei {
edges.push((i_u32, nei));
}
}
}
let mut result = Graph::new(n, directed)?;
result.add_edges(edges)?;
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_connect_neighborhood_path() {
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 cg = connect_neighborhood(&g, 2).unwrap();
assert_eq!(cg.vcount(), 4);
assert_eq!(cg.ecount(), 5);
}
#[test]
fn test_connect_neighborhood_order_1() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
let cg = connect_neighborhood(&g, 1).unwrap();
assert_eq!(cg.ecount(), 1); }
#[test]
fn test_connect_neighborhood_order_0() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
let cg = connect_neighborhood(&g, 0).unwrap();
assert_eq!(cg.ecount(), 1); }
#[test]
fn test_connect_neighborhood_complete() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(1, 2).unwrap();
let cg = connect_neighborhood(&g, 2).unwrap();
assert_eq!(cg.ecount(), 3);
}
#[test]
fn test_connect_neighborhood_large_order() {
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 cg = connect_neighborhood(&g, 10).unwrap();
assert_eq!(cg.ecount(), 6); }
#[test]
fn test_connect_neighborhood_empty() {
let g = Graph::with_vertices(0);
let cg = connect_neighborhood(&g, 5).unwrap();
assert_eq!(cg.vcount(), 0);
}
#[test]
fn test_connect_neighborhood_isolated() {
let g = Graph::with_vertices(5);
let cg = connect_neighborhood(&g, 3).unwrap();
assert_eq!(cg.ecount(), 0);
}
#[test]
fn test_graph_power_zero() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
let p = graph_power(&g, 0).unwrap();
assert_eq!(p.vcount(), 3);
assert_eq!(p.ecount(), 0);
}
#[test]
fn test_graph_power_one() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let p = graph_power(&g, 1).unwrap();
assert_eq!(p.vcount(), 3);
assert_eq!(p.ecount(), 2);
}
#[test]
fn test_graph_power_two_path() {
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 p = graph_power(&g, 2).unwrap();
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 5);
}
#[test]
fn test_graph_power_removes_self_loops() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let p = graph_power(&g, 1).unwrap();
assert_eq!(p.ecount(), 2);
}
#[test]
fn test_graph_power_removes_multi_edges() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap();
let p = graph_power(&g, 1).unwrap();
assert_eq!(p.ecount(), 2);
}
#[test]
fn test_graph_power_directed() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let p = graph_power(&g, 2).unwrap();
assert!(p.is_directed());
assert_eq!(p.vcount(), 3);
assert_eq!(p.ecount(), 3);
}
}