1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
use crate::*;
use std::sync::mpsc::Receiver;
pub struct CycleDetector {
pub ids: HashMap<Arc<String>, usize>,
pub edges: Vec<(usize, usize)>,
}
impl CycleDetector {
/// Constructs cycle detector from receiver.
///
/// Remember to drop the last sender before calling this.
pub fn new(rc: Receiver<(Arc<String>, Arc<String>)>) -> CycleDetector {
let mut ids = HashMap::default();
let mut edges = vec![];
for (a, b) in rc.iter() {
let a_id = if !ids.contains_key(&a) {
let id = ids.len();
ids.insert(a, id);
id
} else {*ids.get(&a).unwrap()};
let b_id = if !ids.contains_key(&b) {
let id = ids.len();
ids.insert(b, id);
id
} else {*ids.get(&b).unwrap()};
edges.push((a_id, b_id));
}
edges.sort();
edges.dedup();
CycleDetector {
ids,
edges,
}
}
/// Detects cycles, if any.
pub fn cycles(&self) -> Option<Vec<(usize, usize)>> {
/*
let mut trace = vec![];
for &(a, b) in &self.edges {
trace.clear();
trace.push(a);
trace.push(b);
loop {
let mut changed = false;
for &(c, d) in &self.edges {
if c == *trace.last().unwrap() {
if trace.iter().any(|&n| n == d) {
trace.push(d);
return Some(trace);
}
trace.push(d);
changed = true;
}
}
if !changed {break}
}
}
*/
let mut visited = vec![false; self.ids.len()];
// Mark all leaves.
for &(a, b) in &self.edges {
if !self.edges.iter().any(|&(_, d)| d == a) {
visited[a] = true;
}
if !self.edges.iter().any(|&(c, _)| c == b) {
visited[b] = true;
}
}
loop {
let mut changed = false;
for &(a, b) in &self.edges {
if !visited[a] {
if self.edges.iter().all(|&(c, d)| a != c || visited[d]) {
visited[a] = true;
changed = true;
}
}
if !visited[b] {
if self.edges.iter().all(|&(c, d)| b != d || visited[c]) {
visited[b] = true;
changed = true;
}
}
}
if !changed {break}
}
let mut cycles = vec![];
for &(a, b) in &self.edges {
if !visited[a] && !visited[b] {cycles.push((a, b))}
}
if cycles.len() == 0 {None} else {Some(cycles)}
}
}