use alloc::collections::{BTreeMap, BTreeSet};
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use elenchus_parser::CloseKind;
pub(crate) fn close(
kind: CloseKind,
pairs: Vec<(String, String)>,
) -> Result<Vec<(String, String)>, String> {
Ok(match kind {
CloseKind::Transitive => {
let closed = transitive_closure(pairs);
if let Some((node, _)) = closed.iter().find(|(a, b)| a == b) {
return Err(node.clone());
}
closed
}
CloseKind::Symmetric => symmetric_closure(pairs),
CloseKind::Reflexive => reflexive_closure(pairs),
CloseKind::Equivalence => equivalence_closure(pairs),
CloseKind::Scc => scc_closure(pairs),
})
}
fn relation_nodes(pairs: &[(String, String)]) -> BTreeSet<String> {
let mut s = BTreeSet::new();
for (a, b) in pairs {
s.insert(a.clone());
s.insert(b.clone());
}
s
}
fn symmetric_closure(pairs: Vec<(String, String)>) -> Vec<(String, String)> {
let mut set: BTreeSet<(String, String)> = pairs.into_iter().collect();
let backs: Vec<(String, String)> = set.iter().map(|(a, b)| (b.clone(), a.clone())).collect();
set.extend(backs);
set.into_iter().collect()
}
fn reflexive_closure(pairs: Vec<(String, String)>) -> Vec<(String, String)> {
let nodes = relation_nodes(&pairs);
let mut set: BTreeSet<(String, String)> = pairs.into_iter().collect();
for n in nodes {
set.insert((n.clone(), n));
}
set.into_iter().collect()
}
fn equivalence_closure(pairs: Vec<(String, String)>) -> Vec<(String, String)> {
reflexive_closure(transitive_closure(symmetric_closure(pairs)))
}
fn scc_closure(pairs: Vec<(String, String)>) -> Vec<(String, String)> {
let nodes = relation_nodes(&pairs);
let reach: BTreeSet<(String, String)> = transitive_closure(pairs).into_iter().collect();
let mut out: BTreeSet<(String, String)> = BTreeSet::new();
for (a, b) in &reach {
if reach.contains(&(b.clone(), a.clone())) {
out.insert((a.clone(), b.clone()));
}
}
for n in nodes {
out.insert((n.clone(), n));
}
out.into_iter().collect()
}
fn transitive_closure(pairs: Vec<(String, String)>) -> Vec<(String, String)> {
let mut succ: BTreeMap<&str, Vec<&str>> = BTreeMap::new();
for (a, b) in &pairs {
succ.entry(a).or_default().push(b);
}
let mut out: BTreeSet<(String, String)> = BTreeSet::new();
for (&start, direct) in &succ {
let mut stack: Vec<&str> = direct.clone();
let mut seen: BTreeSet<&str> = BTreeSet::new();
while let Some(node) = stack.pop() {
if seen.insert(node) {
out.insert((start.to_string(), node.to_string()));
if let Some(next) = succ.get(node) {
stack.extend(next.iter().copied());
}
}
}
}
out.into_iter().collect()
}