use std::collections::{BTreeMap, VecDeque};
use crate::{PrefixTree1, PrefixTree2};
#[derive(Debug, Copy, Clone)]
pub enum ToposortError {
CycleDetected,
}
#[derive(Debug)]
pub struct MorphismWithSignature {
pub morph: u32,
pub dom: u32,
pub cod: u32,
}
pub fn morphism_toposort(
dom_new_order_1_0: &PrefixTree2,
dom_old_order_1_0: &PrefixTree2,
cod_new_order_0_1: &PrefixTree2,
cod_old_order_0_1: &PrefixTree2,
obj_old_order_0: &PrefixTree1,
obj_new_order_0: &PrefixTree1,
) -> Result<Vec<MorphismWithSignature>, ToposortError> {
let get_cod = |mor: u32| -> Option<u32> {
let cods: &PrefixTree1 = cod_new_order_0_1
.get(mor)
.or_else(|| cod_old_order_0_1.get(mor))?;
let [cod] = cods.iter().next().unwrap();
Some(cod)
};
let mut in_degree: BTreeMap<u32, u32> = obj_old_order_0
.iter()
.chain(obj_new_order_0.iter())
.map(|[obj]| (obj, 0))
.collect();
for [_, mor] in dom_new_order_1_0.iter().chain(dom_old_order_1_0.iter()) {
if let Some(cod) = get_cod(mor) {
*in_degree.get_mut(&cod).unwrap() += 1;
}
}
let mut queue: VecDeque<u32> = in_degree
.iter()
.filter_map(|(&obj, &in_deg)| if in_deg == 0 { Some(obj) } else { None })
.collect();
let mut ordered_mors: Vec<MorphismWithSignature> = Vec::new();
for &obj in &queue {
in_degree.remove(&obj);
}
while let Some(obj) = queue.pop_front() {
for dom_order_1_0 in [&dom_new_order_1_0, &dom_old_order_1_0] {
let out_morphisms = match dom_order_1_0.get(obj) {
Some(out_morphisms) => out_morphisms,
None => continue,
};
for [out_morphism] in out_morphisms.iter() {
let cod = match get_cod(out_morphism) {
Some(cod) => cod,
None => continue,
};
ordered_mors.push(MorphismWithSignature {
morph: out_morphism,
dom: obj,
cod,
});
let cod_in_degree = in_degree.get_mut(&cod).unwrap();
*cod_in_degree -= 1;
if *cod_in_degree == 0 {
in_degree.remove(&cod);
queue.push_back(cod);
}
}
}
}
if !in_degree.is_empty() {
return Err(ToposortError::CycleDetected);
}
Ok(ordered_mors)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_prefix_tree_1(values: &[u32]) -> PrefixTree1 {
let mut tree = PrefixTree1::new();
for &value in values {
tree.insert([value]);
}
tree
}
fn make_prefix_tree_2(pairs: &[(u32, u32)]) -> PrefixTree2 {
let mut tree = PrefixTree2::new();
for &(a, b) in pairs {
tree.insert([a, b]);
}
tree
}
#[test]
fn test_single_morphism() {
let mut dom_new = PrefixTree2::new();
dom_new.insert([0, 100]);
let mut cod_new = PrefixTree2::new();
cod_new.insert([100, 1]);
let mut obj_new = PrefixTree1::new();
obj_new.insert([0]);
obj_new.insert([1]);
let dom_old = PrefixTree2::new();
let cod_old = PrefixTree2::new();
let obj_old = PrefixTree1::new();
let result = morphism_toposort(&dom_new, &dom_old, &cod_new, &cod_old, &obj_old, &obj_new);
assert!(result.is_ok(), "Expected Ok, got: {:?}", result);
let ordered = result.unwrap();
assert_eq!(ordered.len(), 1);
assert_eq!(ordered[0].morph, 100);
assert_eq!(ordered[0].dom, 0);
assert_eq!(ordered[0].cod, 1);
}
#[test]
fn test_simple_dag() {
let dom_new = make_prefix_tree_2(&[(0, 0), (1, 1)]); let dom_old = make_prefix_tree_2(&[]);
let cod_new = make_prefix_tree_2(&[(0, 1), (1, 2)]); let cod_old = make_prefix_tree_2(&[]);
let obj_new = make_prefix_tree_1(&[0, 1, 2]);
let obj_old = make_prefix_tree_1(&[]);
let result = morphism_toposort(&dom_new, &dom_old, &cod_new, &cod_old, &obj_old, &obj_new);
assert!(result.is_ok());
let ordered = result.unwrap();
assert_eq!(ordered.len(), 2);
assert_eq!(ordered[0].morph, 0); assert_eq!(ordered[1].morph, 1);
}
#[test]
fn test_cycle_detection() {
let dom_new = make_prefix_tree_2(&[(0, 0), (1, 1)]); let dom_old = make_prefix_tree_2(&[]);
let cod_new = make_prefix_tree_2(&[(0, 1), (1, 0)]); let cod_old = make_prefix_tree_2(&[]);
let obj_new = make_prefix_tree_1(&[0, 1]);
let obj_old = make_prefix_tree_1(&[]);
let result = morphism_toposort(&dom_new, &dom_old, &cod_new, &cod_old, &obj_old, &obj_new);
assert!(matches!(result, Err(ToposortError::CycleDetected)));
}
#[test]
fn test_disconnected_graph() {
let dom_new = make_prefix_tree_2(&[(0, 0), (2, 1)]); let dom_old = make_prefix_tree_2(&[]);
let cod_new = make_prefix_tree_2(&[(0, 1), (1, 3)]); let cod_old = make_prefix_tree_2(&[]);
let obj_new = make_prefix_tree_1(&[0, 1, 2, 3]);
let obj_old = make_prefix_tree_1(&[]);
let result = morphism_toposort(&dom_new, &dom_old, &cod_new, &cod_old, &obj_old, &obj_new);
assert!(result.is_ok());
let ordered = result.unwrap();
assert_eq!(ordered.len(), 2);
assert!(ordered.iter().any(|m| m.morph == 0));
assert!(ordered.iter().any(|m| m.morph == 1));
}
#[test]
fn test_empty_graph() {
let dom_new = make_prefix_tree_2(&[]);
let dom_old = make_prefix_tree_2(&[]);
let cod_new = make_prefix_tree_2(&[]);
let cod_old = make_prefix_tree_2(&[]);
let obj_new = make_prefix_tree_1(&[]);
let obj_old = make_prefix_tree_1(&[]);
let result = morphism_toposort(&dom_new, &dom_old, &cod_new, &cod_old, &obj_old, &obj_new);
assert!(result.is_ok());
let ordered = result.unwrap();
assert_eq!(ordered.len(), 0);
}
#[test]
fn test_self_loop() {
let dom_new = make_prefix_tree_2(&[(0, 0)]); let dom_old = make_prefix_tree_2(&[]);
let cod_new = make_prefix_tree_2(&[(0, 0)]); let cod_old = make_prefix_tree_2(&[]);
let obj_new = make_prefix_tree_1(&[0]);
let obj_old = make_prefix_tree_1(&[]);
let result = morphism_toposort(&dom_new, &dom_old, &cod_new, &cod_old, &obj_old, &obj_new);
assert!(matches!(result, Err(ToposortError::CycleDetected)));
}
#[test]
fn test_complex_dag() {
let dom_new = make_prefix_tree_2(&[(0, 0), (0, 1), (1, 2), (1, 3), (2, 4)]); let dom_old = make_prefix_tree_2(&[]);
let cod_new = make_prefix_tree_2(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 4)]); let cod_old = make_prefix_tree_2(&[]);
let obj_new = make_prefix_tree_1(&[0, 1, 2, 3, 4]);
let obj_old = make_prefix_tree_1(&[]);
let result = morphism_toposort(&dom_new, &dom_old, &cod_new, &cod_old, &obj_old, &obj_new);
assert!(result.is_ok());
let ordered = result.unwrap();
assert_eq!(ordered.len(), 5);
let positions: BTreeMap<u32, usize> = ordered
.iter()
.enumerate()
.map(|(i, m)| (m.morph, i))
.collect();
assert!(positions[&0] < positions[&2]);
assert!(positions[&0] < positions[&3]);
assert!(positions[&1] < positions[&4]);
}
#[test]
fn test_mixed_old_new_data() {
let dom_new = make_prefix_tree_2(&[(0, 0)]); let dom_old = make_prefix_tree_2(&[(1, 1)]); let cod_new = make_prefix_tree_2(&[(0, 1)]); let cod_old = make_prefix_tree_2(&[(1, 2)]); let obj_new = make_prefix_tree_1(&[0, 2]);
let obj_old = make_prefix_tree_1(&[1]);
let result = morphism_toposort(&dom_new, &dom_old, &cod_new, &cod_old, &obj_old, &obj_new);
assert!(result.is_ok());
let ordered = result.unwrap();
assert_eq!(ordered.len(), 2);
assert_eq!(ordered[0].morph, 0);
assert_eq!(ordered[1].morph, 1);
}
#[test]
fn test_missing_codomain() {
let dom_new = make_prefix_tree_2(&[(0, 0), (1, 1)]); let dom_old = make_prefix_tree_2(&[]);
let cod_new = make_prefix_tree_2(&[(0, 1)]); let cod_old = make_prefix_tree_2(&[]);
let obj_new = make_prefix_tree_1(&[0, 1]);
let obj_old = make_prefix_tree_1(&[]);
let result = morphism_toposort(&dom_new, &dom_old, &cod_new, &cod_old, &obj_old, &obj_new);
assert!(result.is_ok());
let ordered = result.unwrap();
assert_eq!(ordered.len(), 1);
assert_eq!(ordered[0].morph, 0);
}
}