use thiserror::Error;
use open_hypergraphs::category::Arrow;
use open_hypergraphs::lax::{NodeId, OpenHypergraph};
#[derive(Debug, Error)]
pub enum UnifyError {
#[error("Not all node labels known")]
NotAllLabelsKnown,
#[error("Could not unify {0:?}")]
Mismatch(NodeId),
#[error("Quotient failed")]
Quotient,
}
pub fn unify<O: Clone + PartialEq, A: Clone>(
f: OpenHypergraph<Option<O>, A>,
) -> Result<OpenHypergraph<O, A>, UnifyError> {
let coequalizer = f.hypergraph.coequalizer();
let mut class_labels: Vec<Option<O>> = vec![None; coequalizer.target()];
for (i, node) in f.hypergraph.nodes.iter().enumerate() {
let class = coequalizer.table[i];
let label = &mut class_labels[class];
match (node, label.as_mut()) {
(Some(x), None) => *label = Some(x.clone()),
(None, _) => (),
(Some(x), Some(u)) => {
if x != u {
return Err(UnifyError::Mismatch(NodeId(i)));
}
}
};
}
let class_labels: Vec<O> = class_labels
.into_iter()
.collect::<Option<_>>()
.ok_or(UnifyError::NotAllLabelsKnown)?;
let nodes = (0..f.hypergraph.nodes.len())
.map(|i| class_labels[coequalizer.table[i]].clone())
.collect();
let mut f = f
.with_nodes(|_| nodes)
.ok_or(UnifyError::NotAllLabelsKnown)?;
f.quotient().map_err(|_| UnifyError::Quotient)?;
Ok(f)
}