use std::collections::HashSet;
use itertools::chain;
use test_case::test_case;
use test_log::test;
use crate::graph_algos::feedback_set::calc_feedback_set;
use crate::graph_algos::graph_node::GraphNode;
use crate::graph_algos::strongly_connected_components::{compute_scc, ComputeScc};
#[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
}
}
impl ComputeScc for IntegerNode {
fn compute_scc(&self) -> Vec<Self::NodeId> {
compute_scc(self)
}
}
#[test]
fn test_list() {
let mut graph: Vec<Vec<usize>> = (0..10).map(|id| vec![id + 1]).collect();
graph.push(vec![]);
let fset = HashSet::<usize>::from_iter(calc_feedback_set(&IntegerNode { id: 0, graph }.into()));
assert!(fset.is_empty());
}
#[test]
fn test_cycle() {
let graph: Vec<Vec<usize>> = (0..10).map(|id| vec![(id + 1) % 10]).collect();
let fset = HashSet::<usize>::from_iter(calc_feedback_set(&IntegerNode { id: 0, graph }.into()));
assert_eq!(fset, HashSet::from([0]));
}
#[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 fset = HashSet::<usize>::from_iter(calc_feedback_set(&IntegerNode { id: 0, graph }.into()));
assert_eq!(fset, HashSet::from([0]));
}
#[test]
fn test_connected_cycles() {
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();
graph[4].push(5);
let fset = HashSet::<usize>::from_iter(calc_feedback_set(&IntegerNode { id: 0, graph }.into()));
assert_eq!(fset, HashSet::from([0]));
}
#[test]
fn test_mesh() {
let graph = (0..5).map(|i| (0..5).filter(|j| *j != i).collect::<Vec<usize>>()).collect();
let fset = HashSet::<usize>::from_iter(calc_feedback_set(&IntegerNode { id: 0, graph }.into()));
assert_eq!(fset, HashSet::from_iter(0..4));
}
#[test_case(0, HashSet::from([0, 3]); "root_0")]
#[test_case(3, HashSet::from([3]); "root_3")]
fn test_tangent_cycles(root: usize, expected_fset: HashSet<usize>) {
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 fset =
HashSet::<usize>::from_iter(calc_feedback_set(&IntegerNode { id: root, graph }.into()));
assert_eq!(fset, expected_fset);
}
#[test_case(0, HashSet::from([0]); "root_0")]
#[test_case(1, HashSet::from([1, 2]); "root_1")]
#[test_case(2, HashSet::from([2, 3]); "root_2")]
#[test_case(3, HashSet::from([3]); "root_3")]
fn test_multiple_cycles(root: usize, expected_fset: HashSet<usize>) {
let graph: Vec<Vec<usize>> = chain!(
vec![vec![1, 2]].into_iter(),
vec![vec![2, 3]].into_iter(),
vec![vec![3]].into_iter(),
vec![vec![0]].into_iter(),
)
.collect();
let fset =
HashSet::<usize>::from_iter(calc_feedback_set(&IntegerNode { id: root, graph }.into()));
assert_eq!(fset, expected_fset);
}