cairo_lang_utils/graph_algos/
feedback_set.rs1use std::collections::VecDeque;
12
13use super::graph_node::GraphNode;
14use super::scc_graph_node::SccGraphNode;
15use super::strongly_connected_components::ComputeScc;
16use crate::ordered_hash_set::OrderedHashSet;
17use crate::unordered_hash_set::UnorderedHashSet;
18
19#[cfg(test)]
20#[path = "feedback_set_test.rs"]
21mod feedback_set_test;
22
23struct FeedbackSetAlgoContext<Node: ComputeScc> {
25 pending: VecDeque<SccGraphNode<Node>>,
31 feedback_set: OrderedHashSet<Node::NodeId>,
34 in_flight: UnorderedHashSet<Node::NodeId>,
37 visited: UnorderedHashSet<Node::NodeId>,
39}
40
41pub fn calc_feedback_set<Node: ComputeScc>(
43 node: SccGraphNode<Node>,
44) -> OrderedHashSet<Node::NodeId> {
45 let mut ctx = FeedbackSetAlgoContext {
46 feedback_set: OrderedHashSet::default(),
47 in_flight: UnorderedHashSet::default(),
48 pending: VecDeque::new(),
49 visited: UnorderedHashSet::default(),
50 };
51 ctx.pending.push_back(node);
52 while let Some(node) = ctx.pending.pop_front() {
53 calc_feedback_set_recursive(node, &mut ctx);
54 }
55 ctx.feedback_set
56}
57
58fn calc_feedback_set_recursive<Node: ComputeScc>(
59 node: SccGraphNode<Node>,
60 ctx: &mut FeedbackSetAlgoContext<Node>,
61) {
62 let cur_node_id = node.get_id();
63 if !ctx.visited.insert(cur_node_id.clone()) {
64 return;
65 };
66 let neighbors = node.get_neighbors();
67 let has_self_loop = neighbors.iter().any(|neighbor| neighbor.get_id() == cur_node_id);
68 let mut remaining_neighbors = neighbors.into_iter();
69 if has_self_loop {
70 ctx.feedback_set.insert(cur_node_id.clone());
73 } else {
74 ctx.in_flight.insert(cur_node_id.clone());
75 for neighbor in remaining_neighbors.by_ref() {
76 let neighbor_id = neighbor.get_id();
77 if ctx.feedback_set.contains(&neighbor_id) {
78 continue;
79 } else if ctx.in_flight.contains(&neighbor_id) {
80 ctx.feedback_set.insert(neighbor_id);
81 } else {
82 calc_feedback_set_recursive(neighbor, ctx);
83 }
84
85 if ctx.feedback_set.contains(&cur_node_id) {
88 break;
89 }
90 }
91 ctx.in_flight.remove(&cur_node_id);
92 };
93 ctx.pending.extend(remaining_neighbors.filter(|node| !ctx.visited.contains(&node.get_id())));
94}