use crate::core::graph::EdgeId;
use crate::core::{Graph, IgraphResult, VertexId};
pub fn line_graph(graph: &Graph) -> IgraphResult<Graph> {
let m = graph.ecount();
let directed = graph.is_directed();
let m_u32 = u32::try_from(m).map_err(|_| {
crate::core::IgraphError::Internal("edge count exceeds u32::MAX for line graph")
})?;
let mut result = Graph::new(m_u32, directed)?;
if m == 0 {
return Ok(result);
}
let n = graph.vcount();
if directed {
for v in 0..n {
let in_eids = graph.incident_in(v)?;
let out_eids = graph.incident(v)?;
for &in_eid in &in_eids {
for &out_eid in &out_eids {
result.add_edge(in_eid as VertexId, out_eid as VertexId)?;
}
}
}
} else {
for v in 0..n {
let incident = incident_unique(graph, v)?;
let len = incident.len();
for i in 0..len {
for j in (i + 1)..len {
result.add_edge(incident[i] as VertexId, incident[j] as VertexId)?;
}
}
}
}
Ok(result)
}
fn incident_unique(graph: &Graph, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
let mut ids = graph.incident(v)?;
ids.sort_unstable();
ids.dedup();
Ok(ids)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph_line_graph_is_empty() {
let g = Graph::with_vertices(5);
let lg = line_graph(&g).unwrap();
assert_eq!(lg.vcount(), 0);
assert_eq!(lg.ecount(), 0);
}
#[test]
fn single_edge_line_graph_is_single_vertex() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
let lg = line_graph(&g).unwrap();
assert_eq!(lg.vcount(), 1);
assert_eq!(lg.ecount(), 0);
}
#[test]
fn triangle_line_graph_is_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 lg = line_graph(&g).unwrap();
assert_eq!(lg.vcount(), 3);
assert_eq!(lg.ecount(), 3);
}
#[test]
fn path_line_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 lg = line_graph(&g).unwrap();
assert_eq!(lg.vcount(), 3);
assert_eq!(lg.ecount(), 2);
}
#[test]
fn star_line_graph_is_complete() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(0, 3).unwrap();
g.add_edge(0, 4).unwrap();
let lg = line_graph(&g).unwrap();
assert_eq!(lg.vcount(), 4);
assert_eq!(lg.ecount(), 6);
}
#[test]
fn directed_path_line_graph() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let lg = line_graph(&g).unwrap();
assert_eq!(lg.vcount(), 2);
assert_eq!(lg.ecount(), 1);
assert!(lg.is_directed());
}
#[test]
fn directed_divergent_edges_not_connected() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
let lg = line_graph(&g).unwrap();
assert_eq!(lg.vcount(), 2);
assert_eq!(lg.ecount(), 0);
}
#[test]
fn directed_convergent_edges_not_connected() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(1, 0).unwrap();
g.add_edge(2, 0).unwrap();
let lg = line_graph(&g).unwrap();
assert_eq!(lg.vcount(), 2);
assert_eq!(lg.ecount(), 0);
}
#[test]
fn self_loop_undirected() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 0).unwrap();
g.add_edge(0, 1).unwrap();
let lg = line_graph(&g).unwrap();
assert_eq!(lg.vcount(), 2);
assert_eq!(lg.ecount(), 1);
}
}