use std::collections::{BTreeSet, HashMap, HashSet, VecDeque};
fn caller_index<'a>(
calls: &'a HashMap<String, BTreeSet<String>>,
in_all: &HashSet<&str>,
) -> HashMap<&'a str, Vec<&'a str>> {
let mut rev: HashMap<&str, Vec<&str>> = HashMap::new();
for (f, cs) in calls {
if !in_all.contains(f.as_str()) {
continue; }
for c in cs {
rev.entry(c.as_str()).or_default().push(f.as_str());
}
}
rev
}
pub fn propagate_str(
direct: &HashMap<String, BTreeSet<String>>,
calls: &HashMap<String, BTreeSet<String>>,
all: &[String],
) -> HashMap<String, BTreeSet<String>> {
let mut acc = direct.clone();
let in_all: HashSet<&str> = all.iter().map(String::as_str).collect();
let rev = caller_index(calls, &in_all);
let mut queue: VecDeque<String> = all.iter().cloned().collect();
let mut queued: HashSet<String> = all.iter().cloned().collect();
while let Some(f) = queue.pop_front() {
queued.remove(&f);
let add: BTreeSet<String> = calls
.get(&f)
.map(|cs| cs.iter().filter_map(|c| acc.get(c)).flatten().cloned().collect())
.unwrap_or_default();
if add.is_empty() {
continue;
}
let e = acc.entry(f.clone()).or_default();
let before = e.len();
e.extend(add);
if e.len() != before {
if let Some(callers) = rev.get(f.as_str()) {
for &caller in callers {
if queued.insert(caller.to_string()) {
queue.push_back(caller.to_string());
}
}
}
}
}
acc
}