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 = distinct(changed_states.iter().map(|(b, _)| *b));
let after_links = distinct(changed_states.iter().map(|(_, a)| *a));
let before_set: HashSet<Link> = before_links.iter().copied().collect();
let after_set: HashSet<Link> = after_links.iter().copied().collect();
let initial_states: Vec<Link> = before_links
.iter()
.filter(|b| !after_set.contains(b))
.copied()
.collect();
let final_states: HashSet<Link> = after_links
.iter()
.filter(|a| !before_set.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 distinct(initial_states.iter().copied()) {
let initial = &initial;
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 order: Vec<Link> = Vec::new();
let mut grouped: HashMap<Link, Vec<(Link, Link)>> = HashMap::new();
for change in changes {
let group = grouped.entry(change.0).or_insert_with(|| {
order.push(change.0);
Vec::new()
});
group.push(change);
}
let mut result = Vec::new();
for before in &order {
let changes_for_this_before = &grouped[before];
if changes_for_this_before.len() == 1 {
result.extend(changes_for_this_before.iter().copied());
} 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.iter().copied());
}
}
}
result
}
fn distinct(items: impl IntoIterator<Item = Link>) -> Vec<Link> {
let mut seen = HashSet::new();
items
.into_iter()
.filter(|item| seen.insert(*item))
.collect()
}