use std::collections::HashSet;
use cairo_lang_test_utils::test;
use itertools::chain;
use test_case::test_case;
use super::GraphNode;
use crate::graph_algos::strongly_connected_components::compute_scc;
#[derive(PartialEq, Eq, Hash, Clone)]
struct IntegerNode {
id: usize,
graph: Vec<Vec<usize>>,
}
impl GraphNode for IntegerNode {
type NodeId = usize;
fn get_neighbors(&self) -> Vec<Self> {
self.graph[self.id]
.iter()
.map(|neighbor_id| IntegerNode { id: *neighbor_id, graph: self.graph.clone() })
.collect()
}
fn get_id(&self) -> Self::NodeId {
self.id
}
}
#[test]
fn test_short_list() {
let graph = vec![ vec![1], vec![]];
let scc = HashSet::<usize>::from_iter(compute_scc(&IntegerNode { id: 0, graph }));
assert_eq!(scc, HashSet::from([0]));
}
#[test]
fn test_list() {
let mut graph: Vec<Vec<usize>> = (0..10).map(|id| vec![id + 1]).collect();
graph.push(vec![]);
let scc = HashSet::<usize>::from_iter(compute_scc(&IntegerNode { id: 0, graph }));
assert_eq!(scc, HashSet::from([0]));
}
#[test]
fn test_cycle() {
let graph: Vec<Vec<usize>> = (0..10).map(|id| vec![(id + 1) % 10]).collect();
let scc = HashSet::<usize>::from_iter(compute_scc(&IntegerNode { id: 0, graph }));
assert_eq!(scc, HashSet::from_iter(0..10));
}
#[test]
fn test_mesh() {
let graph = (0..5).map(|i| (0..5).filter(|j| *j != i).collect::<Vec<usize>>()).collect();
let scc = HashSet::<usize>::from_iter(compute_scc(&IntegerNode { id: 0, graph }));
assert_eq!(scc, HashSet::from_iter(0..5));
}
#[test]
fn test_list_with_back_edges() {
let graph = vec![
vec![1],
vec![2],
vec![0, 3],
vec![1],
];
let scc = HashSet::<usize>::from_iter(compute_scc(&IntegerNode { id: 0, graph }));
assert_eq!(scc, HashSet::from_iter(0..4));
}
#[test]
fn test_root_points_to_cycle() {
let mut graph: Vec<Vec<usize>> = (0..10).map(|id| vec![(id + 1) % 10]).collect();
graph.push( vec![0]);
let scc = HashSet::<usize>::from_iter(compute_scc(&IntegerNode { id: 10, graph }));
assert_eq!(scc, HashSet::from([10]));
}
#[test_case(true; "root_in_first_cycle")]
#[test_case(false; "root_in_second_cycle")]
fn test_connected_cycles(root_in_first_cycle: bool) {
let mut graph: Vec<Vec<usize>> =
chain!((0..5).map(|id| vec![(id + 1) % 5]), (0..5).map(|id| vec![5 + (id + 1) % 5]))
.collect();
if root_in_first_cycle {
graph[4].push(5);
} else {
graph[5].push(4);
}
let scc = HashSet::<usize>::from_iter(compute_scc(&IntegerNode { id: 0, graph }));
assert_eq!(scc, HashSet::from_iter(0..5));
}
#[test]
fn test_tangent_cycles() {
let graph: Vec<Vec<usize>> = chain!(
(0..3).map(|id| vec![id + 1]),
vec![vec![0, 4]].into_iter(),
(0..3).map(|id| vec![3 + (id + 2) % 4])
)
.collect();
let scc = HashSet::<usize>::from_iter(compute_scc(&IntegerNode { id: 0, graph }));
assert_eq!(scc, HashSet::from_iter(0..7));
}