use cairo_lang_test_utils::test;
use itertools::chain;
use test_case::test_case;
use crate::graph_algos::feedback_set::calc_feedback_set;
use crate::graph_algos::graph_node::GraphNode;
use crate::graph_algos::strongly_connected_components::{ComputeScc, 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
}
}
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![]);
assert!(feedback_set(0, graph).is_empty());
}
#[test]
fn test_cycle() {
let graph: Vec<Vec<usize>> = (0..10).map(|id| vec![(id + 1) % 10]).collect();
assert_eq!(feedback_set(0, graph), [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]);
assert!(feedback_set(10, graph.clone()).is_empty());
assert_eq!(feedback_set(1, graph), [1]);
}
#[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);
assert_eq!(feedback_set(0, graph.clone()), [0]);
assert_eq!(feedback_set(5, graph), [5]);
}
#[test]
fn test_mesh() {
let graph = (0..5).map(|i| (0..5).filter(|j| *j != i).collect::<Vec<usize>>()).collect();
assert_eq!(feedback_set(0, graph), [0, 1, 2, 3]);
}
#[test_case(0, vec![0, 3]; "root_0")]
#[test_case(1, vec![1, 3]; "root_1")]
#[test_case(2, vec![2, 3]; "root_2")]
#[test_case(3, vec![3]; "root_3")]
#[test_case(4, vec![3]; "root_4")]
#[test_case(5, vec![3]; "root_5")]
#[test_case(6, vec![3]; "root_6")]
fn test_tangent_cycles(root: usize, expected_fset: Vec<usize>) {
let graph: Vec<Vec<usize>> = chain!(
(0..3).map(|id| vec![id + 1]),
[vec![0, 4]],
(0..3).map(|id| vec![3 + (id + 2) % 4])
)
.collect();
assert_eq!(feedback_set(root, graph), expected_fset);
}
#[test_case(0, vec![0]; "root_0")]
#[test_case(1, vec![1, 2]; "root_1")]
#[test_case(2, vec![2, 3]; "root_2")]
#[test_case(3, vec![3]; "root_3")]
fn test_multiple_cycles(root: usize, expected_fset: Vec<usize>) {
let graph: Vec<Vec<usize>> = vec![
vec![1, 2],
vec![2, 3],
vec![3],
vec![0],
];
assert_eq!(feedback_set(root, graph), expected_fset);
}
#[test_case(0, vec![1, 2]; "root_0")]
#[test_case(1, vec![1, 2]; "root_1")]
#[test_case(2, vec![2, 1]; "root_2")]
fn test_with_self_loops(root: usize, expected_fset: Vec<usize>) {
let graph: Vec<Vec<usize>> = vec![
vec![1, 2],
vec![0, 1],
vec![2, 0],
];
assert_eq!(feedback_set(root, graph), expected_fset);
}
#[test_case(0, vec![0, 1, 3]; "root_0")]
#[test_case(1, vec![1, 3]; "root_1")]
#[test_case(2, vec![1, 3]; "root_2")]
#[test_case(3, vec![3, 0, 1]; "root_3")]
#[test_case(4, vec![4, 0, 1]; "root_4")]
fn test_with_size2_loops(root: usize, expected_fset: Vec<usize>) {
let graph: Vec<Vec<usize>> = vec![
vec![1, 3],
vec![0, 2],
vec![1],
vec![4, 0],
vec![3],
];
assert_eq!(feedback_set(root, graph), expected_fset);
}
fn feedback_set(id: usize, graph: Vec<Vec<usize>>) -> Vec<usize> {
calc_feedback_set(IntegerNode { id, graph }.into()).into_iter().collect()
}