rust_igraph/algorithms/operators/
line_graph.rs1use crate::core::graph::EdgeId;
7use crate::core::{Graph, IgraphResult, VertexId};
8
9pub fn line_graph(graph: &Graph) -> IgraphResult<Graph> {
50 let m = graph.ecount();
51 let directed = graph.is_directed();
52 let m_u32 = u32::try_from(m).map_err(|_| {
53 crate::core::IgraphError::Internal("edge count exceeds u32::MAX for line graph")
54 })?;
55 let mut result = Graph::new(m_u32, directed)?;
56
57 if m == 0 {
58 return Ok(result);
59 }
60
61 let n = graph.vcount();
62
63 if directed {
64 for v in 0..n {
67 let in_eids = graph.incident_in(v)?;
68 let out_eids = graph.incident(v)?;
69
70 for &in_eid in &in_eids {
71 for &out_eid in &out_eids {
72 result.add_edge(in_eid as VertexId, out_eid as VertexId)?;
73 }
74 }
75 }
76 } else {
77 for v in 0..n {
81 let incident = incident_unique(graph, v)?;
82 let len = incident.len();
83 for i in 0..len {
84 for j in (i + 1)..len {
85 result.add_edge(incident[i] as VertexId, incident[j] as VertexId)?;
86 }
87 }
88 }
89 }
90
91 Ok(result)
92}
93
94fn incident_unique(graph: &Graph, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
96 let mut ids = graph.incident(v)?;
97 ids.sort_unstable();
98 ids.dedup();
99 Ok(ids)
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn empty_graph_line_graph_is_empty() {
108 let g = Graph::with_vertices(5);
109 let lg = line_graph(&g).unwrap();
110 assert_eq!(lg.vcount(), 0);
111 assert_eq!(lg.ecount(), 0);
112 }
113
114 #[test]
115 fn single_edge_line_graph_is_single_vertex() {
116 let mut g = Graph::with_vertices(2);
117 g.add_edge(0, 1).unwrap();
118 let lg = line_graph(&g).unwrap();
119 assert_eq!(lg.vcount(), 1);
120 assert_eq!(lg.ecount(), 0);
121 }
122
123 #[test]
124 fn triangle_line_graph_is_triangle() {
125 let mut g = Graph::with_vertices(3);
126 g.add_edge(0, 1).unwrap();
127 g.add_edge(1, 2).unwrap();
128 g.add_edge(2, 0).unwrap();
129 let lg = line_graph(&g).unwrap();
130 assert_eq!(lg.vcount(), 3);
131 assert_eq!(lg.ecount(), 3);
132 }
133
134 #[test]
135 fn path_line_graph() {
136 let mut g = Graph::with_vertices(4);
138 g.add_edge(0, 1).unwrap();
139 g.add_edge(1, 2).unwrap();
140 g.add_edge(2, 3).unwrap();
141 let lg = line_graph(&g).unwrap();
142 assert_eq!(lg.vcount(), 3);
143 assert_eq!(lg.ecount(), 2);
144 }
145
146 #[test]
147 fn star_line_graph_is_complete() {
148 let mut g = Graph::with_vertices(5);
151 g.add_edge(0, 1).unwrap();
152 g.add_edge(0, 2).unwrap();
153 g.add_edge(0, 3).unwrap();
154 g.add_edge(0, 4).unwrap();
155 let lg = line_graph(&g).unwrap();
156 assert_eq!(lg.vcount(), 4);
157 assert_eq!(lg.ecount(), 6);
158 }
159
160 #[test]
161 fn directed_path_line_graph() {
162 let mut g = Graph::new(3, true).unwrap();
165 g.add_edge(0, 1).unwrap();
166 g.add_edge(1, 2).unwrap();
167 let lg = line_graph(&g).unwrap();
168 assert_eq!(lg.vcount(), 2);
169 assert_eq!(lg.ecount(), 1);
170 assert!(lg.is_directed());
171 }
172
173 #[test]
174 fn directed_divergent_edges_not_connected() {
175 let mut g = Graph::new(3, true).unwrap();
179 g.add_edge(0, 1).unwrap();
180 g.add_edge(0, 2).unwrap();
181 let lg = line_graph(&g).unwrap();
182 assert_eq!(lg.vcount(), 2);
183 assert_eq!(lg.ecount(), 0);
184 }
185
186 #[test]
187 fn directed_convergent_edges_not_connected() {
188 let mut g = Graph::new(3, true).unwrap();
192 g.add_edge(1, 0).unwrap();
193 g.add_edge(2, 0).unwrap();
194 let lg = line_graph(&g).unwrap();
195 assert_eq!(lg.vcount(), 2);
196 assert_eq!(lg.ecount(), 0);
197 }
198
199 #[test]
200 fn self_loop_undirected() {
201 let mut g = Graph::with_vertices(2);
205 g.add_edge(0, 0).unwrap();
206 g.add_edge(0, 1).unwrap();
207 let lg = line_graph(&g).unwrap();
208 assert_eq!(lg.vcount(), 2);
209 assert_eq!(lg.ecount(), 1);
210 }
211}