use crate::core::{Graph, IgraphError, IgraphResult};
pub fn induced_subgraph_edges(graph: &Graph, vids: &[u32]) -> IgraphResult<Vec<u32>> {
let n = graph.vcount();
let m = graph.ecount();
let mut in_set: Vec<bool> = vec![false; n as usize];
for &vid in vids {
if vid >= n {
return Err(IgraphError::InvalidArgument(format!(
"induced_subgraph_edges: vertex ID {vid} out of range (graph has {n} vertices)"
)));
}
in_set[vid as usize] = true;
}
let mut result: Vec<u32> = Vec::new();
for eid_usize in 0..m {
#[allow(clippy::cast_possible_truncation)]
let eid = eid_usize as u32;
let (from, to) = graph.edge(eid)?;
if in_set[from as usize] && in_set[to as usize] {
result.push(eid);
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_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(); g.add_edge(0, 4).unwrap();
let edges = induced_subgraph_edges(&g, &[0, 1, 2]).unwrap();
assert_eq!(edges, vec![0, 1]);
}
#[test]
fn all_vertices() {
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 edges = induced_subgraph_edges(&g, &[0, 1, 2, 3]).unwrap();
assert_eq!(edges, vec![0, 1, 2]);
}
#[test]
fn single_vertex() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let edges = induced_subgraph_edges(&g, &[1]).unwrap();
assert!(edges.is_empty());
}
#[test]
fn self_loop_included() {
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 edges = induced_subgraph_edges(&g, &[0]).unwrap();
assert_eq!(edges, vec![0]); }
#[test]
fn directed_graph() {
let mut g = Graph::new(4, true).unwrap();
g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 0).unwrap(); g.add_edge(2, 3).unwrap();
let edges = induced_subgraph_edges(&g, &[0, 1, 2]).unwrap();
assert_eq!(edges, vec![0, 1, 2]);
}
#[test]
fn empty_vertex_set() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
let edges = induced_subgraph_edges(&g, &[]).unwrap();
assert!(edges.is_empty());
}
#[test]
fn no_matching_edges() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(2, 3).unwrap();
let edges = induced_subgraph_edges(&g, &[0, 2]).unwrap();
assert!(edges.is_empty());
}
#[test]
fn duplicate_vertex_ids() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap();
let edges = induced_subgraph_edges(&g, &[0, 1, 0, 1]).unwrap();
assert_eq!(edges, vec![0]);
}
#[test]
fn vertex_out_of_range() {
let g = Graph::with_vertices(3);
let err = induced_subgraph_edges(&g, &[5]).unwrap_err();
assert!(matches!(err, IgraphError::InvalidArgument(_)));
}
#[test]
fn graph_with_no_edges() {
let g = Graph::with_vertices(5);
let edges = induced_subgraph_edges(&g, &[0, 1, 2]).unwrap();
assert!(edges.is_empty());
}
}