use crate::link::Link;
use std::collections::{HashMap, HashSet};
pub fn simplify_changes(changes: Vec<(Link, Link)>) -> Vec<(Link, Link)> {
if changes.is_empty() {
return vec![];
}
let changes = remove_duplicate_before_states(changes);
let mut unchanged_states = Vec::new();
let mut changed_states = Vec::new();
for (before, after) in changes.iter() {
if before == after {
unchanged_states.push((*before, *after));
} else {
changed_states.push((*before, *after));
}
}
let before_links: HashSet<Link> = changed_states.iter().map(|(b, _)| *b).collect();
let after_links: HashSet<Link> = changed_states.iter().map(|(_, a)| *a).collect();
let initial_states: Vec<Link> = before_links
.iter()
.filter(|b| !after_links.contains(b))
.copied()
.collect();
let final_states: HashSet<Link> = after_links
.iter()
.filter(|a| !before_links.contains(a))
.copied()
.collect();
let mut adjacency: HashMap<Link, Vec<Link>> = HashMap::new();
for (before, after) in changed_states.iter() {
adjacency.entry(*before).or_default().push(*after);
}
if initial_states.is_empty() {
return changes;
}
let mut results = Vec::new();
results.extend(unchanged_states);
for initial in initial_states.iter() {
let mut stack = vec![*initial];
let mut visited: HashSet<Link> = HashSet::new();
while let Some(current) = stack.pop() {
if !visited.insert(current) {
continue;
}
let has_next = adjacency.contains_key(¤t);
let next_links = adjacency.get(¤t);
let is_final_or_dead_end = final_states.contains(¤t)
|| !has_next
|| next_links.is_none_or(|v| v.is_empty());
if is_final_or_dead_end {
results.push((*initial, current));
}
if let Some(next_links) = next_links {
for next in next_links {
stack.push(*next);
}
}
}
}
results.sort_by(|a, b| {
a.1.index
.cmp(&b.1.index)
.then_with(|| a.1.source.cmp(&b.1.source))
.then_with(|| a.1.target.cmp(&b.1.target))
});
results
}
fn remove_duplicate_before_states(changes: Vec<(Link, Link)>) -> Vec<(Link, Link)> {
let mut grouped: HashMap<Link, Vec<(Link, Link)>> = HashMap::new();
for change in changes {
grouped.entry(change.0).or_default().push(change);
}
let mut result = Vec::new();
for (_before, changes_for_this_before) in grouped {
if changes_for_this_before.len() == 1 {
result.extend(changes_for_this_before);
} else {
let null_link = Link::new(0, 0, 0);
let has_null_transition = changes_for_this_before
.iter()
.any(|(_, after)| *after == null_link);
let non_null_transitions: Vec<_> = changes_for_this_before
.iter()
.filter(|(_, after)| *after != null_link)
.cloned()
.collect();
if has_null_transition && !non_null_transitions.is_empty() {
result.extend(non_null_transitions);
} else {
result.extend(changes_for_this_before);
}
}
}
result
}