use std::cmp::Eq;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Display};
use std::hash::Hash;
mod err;
pub use err::*;
use itertools::Itertools;
pub trait TCNode<K> {
fn get_key(&self) -> K;
fn add_edge_to(&mut self, k: K);
fn out_edges(&self) -> Box<dyn Iterator<Item = &K> + '_>;
fn has_edge_to(&self, k: &K) -> bool;
}
pub fn compute_tc<K, V>(nodes: &mut HashMap<K, V>, enforce_dag: bool) -> Result<(), K>
where
K: Clone + Eq + Hash + Debug + Display,
V: TCNode<K>,
{
let res = compute_tc_internal::<K, V>(nodes);
if res.is_ok() && enforce_dag {
return enforce_dag_from_tc(nodes);
}
res
}
fn compute_tc_internal<K, V>(nodes: &mut HashMap<K, V>) -> Result<(), K>
where
K: Clone + Eq + Hash + Debug + Display,
V: TCNode<K>,
{
let mut ancestors: HashMap<K, HashSet<K>> = HashMap::new();
for node in nodes.values() {
let this_node_ancestors: &mut HashSet<K> = ancestors.entry(node.get_key()).or_default();
add_ancestors_to_set(node, nodes, this_node_ancestors)?;
}
for node in nodes.values_mut() {
#[allow(clippy::expect_used)]
for ancestor_uid in ancestors
.get(&node.get_key())
.expect("shouldn't have added any new values to the `nodes` map")
{
node.add_edge_to(ancestor_uid.clone());
}
}
Ok(())
}
pub fn enforce_tc_and_dag<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
where
K: Clone + Eq + Hash + Debug + Display,
V: TCNode<K>,
{
let res = enforce_tc(entities);
if res.is_ok() {
return enforce_dag_from_tc(entities);
}
res
}
fn enforce_tc<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
where
K: Clone + Eq + Hash + Debug + Display,
V: TCNode<K>,
{
for entity in entities.values() {
for parent_uid in entity.out_edges() {
if let Some(parent) = entities.get(parent_uid) {
for grandparent in parent.out_edges() {
if !entity.has_edge_to(grandparent) {
return Err(TcError::MissingTcEdge {
child: entity.get_key(),
parent: parent_uid.clone(),
grandparent: grandparent.clone(),
});
}
}
}
}
}
Ok(())
}
fn add_ancestors_to_set<K, V>(
node: &V,
hierarchy: &HashMap<K, V>,
ancestors: &mut HashSet<K>,
) -> Result<(), K>
where
K: Clone + Eq + Hash + Debug + Display,
V: TCNode<K>,
{
for ancestor_uid in node.out_edges() {
if ancestors.insert(ancestor_uid.clone()) {
if let Some(ancestor) = hierarchy.get(ancestor_uid) {
add_ancestors_to_set(ancestor, hierarchy, ancestors)?;
}
}
}
Ok(())
}
fn enforce_dag_from_tc<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
where
K: Clone + Eq + Hash + Debug + Display,
V: TCNode<K>,
{
for entity in entities.values() {
let key = entity.get_key();
if entity.out_edges().contains(&key) {
return Err(TcError::HasCycle {
vertex_with_loop: key,
});
}
}
Ok(())
}
#[allow(clippy::indexing_slicing)]
#[allow(clippy::panic)]
#[cfg(test)]
mod tests {
use crate::ast::{Entity, EntityUID};
use super::*;
#[test]
fn basic() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("B"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
let c = Entity::with_uid(EntityUID::with_eid("C"));
let mut entities = HashMap::from([(a.uid(), a), (b.uid(), b), (c.uid(), c)]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
let a = &entities[&EntityUID::with_eid("A")];
let b = &entities[&EntityUID::with_eid("B")];
let c = &entities[&EntityUID::with_eid("C")];
assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
assert!(enforce_tc(&entities).is_ok());
assert!(enforce_dag_from_tc(&entities).is_ok());
}
#[test]
fn reversed() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("B"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
let c = Entity::with_uid(EntityUID::with_eid("C"));
let mut entities = HashMap::from([(c.uid(), c), (b.uid(), b), (a.uid(), a)]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
let a = &entities[&EntityUID::with_eid("A")];
let b = &entities[&EntityUID::with_eid("B")];
let c = &entities[&EntityUID::with_eid("C")];
assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
assert!(enforce_tc(&entities).is_ok());
assert!(enforce_dag_from_tc(&entities).is_ok());
}
#[test]
fn deeper() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("B"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
let mut c = Entity::with_uid(EntityUID::with_eid("C"));
c.add_ancestor(EntityUID::with_eid("D"));
let mut d = Entity::with_uid(EntityUID::with_eid("D"));
d.add_ancestor(EntityUID::with_eid("E"));
let e = Entity::with_uid(EntityUID::with_eid("E"));
let mut entities = HashMap::from([
(a.uid(), a),
(b.uid(), b),
(c.uid(), c),
(d.uid(), d),
(e.uid(), e),
]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
let a = &entities[&EntityUID::with_eid("A")];
let b = &entities[&EntityUID::with_eid("B")];
let c = &entities[&EntityUID::with_eid("C")];
assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
assert!(b.is_descendant_of(&EntityUID::with_eid("D")));
assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
assert!(c.is_descendant_of(&EntityUID::with_eid("E")));
assert!(enforce_tc(&entities).is_ok());
assert!(enforce_dag_from_tc(&entities).is_ok());
}
#[test]
fn not_alphabetized() {
let mut foo = Entity::with_uid(EntityUID::with_eid("foo"));
foo.add_ancestor(EntityUID::with_eid("bar"));
let mut bar = Entity::with_uid(EntityUID::with_eid("bar"));
bar.add_ancestor(EntityUID::with_eid("baz"));
let mut baz = Entity::with_uid(EntityUID::with_eid("baz"));
baz.add_ancestor(EntityUID::with_eid("ham"));
let mut ham = Entity::with_uid(EntityUID::with_eid("ham"));
ham.add_ancestor(EntityUID::with_eid("eggs"));
let eggs = Entity::with_uid(EntityUID::with_eid("eggs"));
let mut entities = HashMap::from([
(ham.uid(), ham),
(bar.uid(), bar),
(foo.uid(), foo),
(eggs.uid(), eggs),
(baz.uid(), baz),
]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
let foo = &entities[&EntityUID::with_eid("foo")];
let bar = &entities[&EntityUID::with_eid("bar")];
let baz = &entities[&EntityUID::with_eid("baz")];
assert!(foo.is_descendant_of(&EntityUID::with_eid("baz")));
assert!(foo.is_descendant_of(&EntityUID::with_eid("ham")));
assert!(foo.is_descendant_of(&EntityUID::with_eid("eggs")));
assert!(bar.is_descendant_of(&EntityUID::with_eid("ham")));
assert!(bar.is_descendant_of(&EntityUID::with_eid("eggs")));
assert!(baz.is_descendant_of(&EntityUID::with_eid("eggs")));
assert!(enforce_tc(&entities).is_ok());
assert!(enforce_dag_from_tc(&entities).is_ok());
}
#[test]
fn multi_parents() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("B"));
a.add_ancestor(EntityUID::with_eid("D"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
let c = Entity::with_uid(EntityUID::with_eid("C"));
let mut d = Entity::with_uid(EntityUID::with_eid("D"));
d.add_ancestor(EntityUID::with_eid("E"));
let e = Entity::with_uid(EntityUID::with_eid("E"));
let mut entities = HashMap::from([
(a.uid(), a),
(b.uid(), b),
(c.uid(), c),
(d.uid(), d),
(e.uid(), e),
]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
let a = &entities[&EntityUID::with_eid("A")];
let b = &entities[&EntityUID::with_eid("B")];
let d = &entities[&EntityUID::with_eid("D")];
assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("D")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
assert!(!d.is_descendant_of(&EntityUID::with_eid("B")));
assert!(!d.is_descendant_of(&EntityUID::with_eid("C")));
assert!(enforce_tc(&entities).is_ok());
assert!(enforce_dag_from_tc(&entities).is_ok());
}
#[test]
fn dag() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("B"));
a.add_ancestor(EntityUID::with_eid("F"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
b.add_ancestor(EntityUID::with_eid("D"));
let c = Entity::with_uid(EntityUID::with_eid("C"));
let mut d = Entity::with_uid(EntityUID::with_eid("D"));
d.add_ancestor(EntityUID::with_eid("E"));
let mut e = Entity::with_uid(EntityUID::with_eid("E"));
e.add_ancestor(EntityUID::with_eid("H"));
let mut f = Entity::with_uid(EntityUID::with_eid("F"));
f.add_ancestor(EntityUID::with_eid("G"));
let mut g = Entity::with_uid(EntityUID::with_eid("G"));
g.add_ancestor(EntityUID::with_eid("E"));
let h = Entity::with_uid(EntityUID::with_eid("H"));
let mut entities = HashMap::from([
(a.uid(), a),
(b.uid(), b),
(c.uid(), c),
(d.uid(), d),
(e.uid(), e),
(f.uid(), f),
(g.uid(), g),
(h.uid(), h),
]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
let a = &entities[&EntityUID::with_eid("A")];
let b = &entities[&EntityUID::with_eid("B")];
let f = &entities[&EntityUID::with_eid("F")];
assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
assert!(a.is_descendant_of(&EntityUID::with_eid("H")));
assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
assert!(b.is_descendant_of(&EntityUID::with_eid("H")));
assert!(f.is_descendant_of(&EntityUID::with_eid("E")));
assert!(f.is_descendant_of(&EntityUID::with_eid("H")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
assert!(enforce_tc(&entities).is_ok());
assert!(enforce_dag_from_tc(&entities).is_ok());
}
#[test]
fn already_edges() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("B"));
a.add_ancestor(EntityUID::with_eid("C"));
a.add_ancestor(EntityUID::with_eid("D"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
b.add_ancestor(EntityUID::with_eid("E"));
let mut c = Entity::with_uid(EntityUID::with_eid("C"));
c.add_ancestor(EntityUID::with_eid("E"));
let mut d = Entity::with_uid(EntityUID::with_eid("D"));
d.add_ancestor(EntityUID::with_eid("C"));
d.add_ancestor(EntityUID::with_eid("F"));
let e = Entity::with_uid(EntityUID::with_eid("E"));
let f = Entity::with_uid(EntityUID::with_eid("F"));
let mut entities = HashMap::from([
(a.uid(), a),
(b.uid(), b),
(c.uid(), c),
(d.uid(), d),
(e.uid(), e),
(f.uid(), f),
]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
let a = &entities[&EntityUID::with_eid("A")];
let b = &entities[&EntityUID::with_eid("B")];
let c = &entities[&EntityUID::with_eid("C")];
let d = &entities[&EntityUID::with_eid("D")];
assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
assert!(d.is_descendant_of(&EntityUID::with_eid("E")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
assert!(!c.is_descendant_of(&EntityUID::with_eid("F")));
assert!(enforce_tc(&entities).is_ok());
assert!(enforce_dag_from_tc(&entities).is_ok());
}
#[test]
fn disjoint_dag() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("F"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
let c = Entity::with_uid(EntityUID::with_eid("C"));
let mut d = Entity::with_uid(EntityUID::with_eid("D"));
d.add_ancestor(EntityUID::with_eid("E"));
let mut e = Entity::with_uid(EntityUID::with_eid("E"));
e.add_ancestor(EntityUID::with_eid("H"));
let mut f = Entity::with_uid(EntityUID::with_eid("F"));
f.add_ancestor(EntityUID::with_eid("G"));
let g = Entity::with_uid(EntityUID::with_eid("G"));
let h = Entity::with_uid(EntityUID::with_eid("H"));
let mut entities = HashMap::from([
(a.uid(), a),
(b.uid(), b),
(c.uid(), c),
(d.uid(), d),
(e.uid(), e),
(f.uid(), f),
(g.uid(), g),
(h.uid(), h),
]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
let a = &entities[&EntityUID::with_eid("A")];
let b = &entities[&EntityUID::with_eid("B")];
let d = &entities[&EntityUID::with_eid("D")];
let f = &entities[&EntityUID::with_eid("F")];
assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
assert!(d.is_descendant_of(&EntityUID::with_eid("H")));
assert!(!a.is_descendant_of(&EntityUID::with_eid("C")));
assert!(!a.is_descendant_of(&EntityUID::with_eid("D")));
assert!(!a.is_descendant_of(&EntityUID::with_eid("E")));
assert!(!a.is_descendant_of(&EntityUID::with_eid("H")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("H")));
assert!(!f.is_descendant_of(&EntityUID::with_eid("E")));
assert!(!f.is_descendant_of(&EntityUID::with_eid("H")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
assert!(enforce_tc(&entities).is_ok());
assert!(enforce_dag_from_tc(&entities).is_ok());
}
#[test]
fn trivial_cycle() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("B"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("B"));
let mut entities = HashMap::from([(a.uid(), a), (b.uid(), b)]);
assert!(compute_tc_internal(&mut entities).is_ok());
match enforce_dag_from_tc(&entities) {
Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
Err(TcError::HasCycle { vertex_with_loop }) => {
assert!(vertex_with_loop == EntityUID::with_eid("B"));
}
Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
}
let a = &entities[&EntityUID::with_eid("A")];
let b = &entities[&EntityUID::with_eid("B")];
assert!(a.is_descendant_of(&EntityUID::with_eid("B")));
assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
assert!(enforce_tc(&entities).is_ok());
match enforce_dag_from_tc(&entities) {
Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
Err(TcError::HasCycle { vertex_with_loop }) => {
assert!(vertex_with_loop == EntityUID::with_eid("B"));
}
Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
}
}
#[test]
fn nontrivial_cycle() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("B"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
b.add_ancestor(EntityUID::with_eid("D"));
let mut c = Entity::with_uid(EntityUID::with_eid("C"));
c.add_ancestor(EntityUID::with_eid("A"));
let d = Entity::with_uid(EntityUID::with_eid("D"));
let mut entities = HashMap::from([(a.uid(), a), (b.uid(), b), (c.uid(), c), (d.uid(), d)]);
assert!(compute_tc_internal(&mut entities).is_ok());
match enforce_dag_from_tc(&entities) {
Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
Err(TcError::HasCycle { vertex_with_loop }) => {
assert!(
vertex_with_loop == EntityUID::with_eid("A")
|| vertex_with_loop == EntityUID::with_eid("B")
|| vertex_with_loop == EntityUID::with_eid("C")
);
}
Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
}
let a = &entities[&EntityUID::with_eid("A")];
let b = &entities[&EntityUID::with_eid("B")];
assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
assert!(b.is_descendant_of(&EntityUID::with_eid("A")));
assert!(enforce_tc(&entities).is_ok());
match enforce_dag_from_tc(&entities) {
Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
Err(TcError::HasCycle { vertex_with_loop }) => {
assert!(
vertex_with_loop == EntityUID::with_eid("A")
|| vertex_with_loop == EntityUID::with_eid("B")
|| vertex_with_loop == EntityUID::with_eid("C")
);
}
Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
}
}
#[test]
fn disjoint_cycles() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("F"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
let mut c = Entity::with_uid(EntityUID::with_eid("C"));
c.add_ancestor(EntityUID::with_eid("B"));
let mut d = Entity::with_uid(EntityUID::with_eid("D"));
d.add_ancestor(EntityUID::with_eid("E"));
let mut e = Entity::with_uid(EntityUID::with_eid("E"));
e.add_ancestor(EntityUID::with_eid("H"));
let mut f = Entity::with_uid(EntityUID::with_eid("F"));
f.add_ancestor(EntityUID::with_eid("G"));
let g = Entity::with_uid(EntityUID::with_eid("G"));
let mut h = Entity::with_uid(EntityUID::with_eid("H"));
h.add_ancestor(EntityUID::with_eid("D"));
let mut entities = HashMap::from([
(a.uid(), a),
(b.uid(), b),
(c.uid(), c),
(d.uid(), d),
(e.uid(), e),
(f.uid(), f),
(g.uid(), g),
(h.uid(), h),
]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
assert!(enforce_tc(&entities).is_ok());
match enforce_dag_from_tc(&entities) {
Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
Err(TcError::HasCycle { vertex_with_loop }) => {
assert!(
vertex_with_loop == EntityUID::with_eid("B")
|| vertex_with_loop == EntityUID::with_eid("C")
|| vertex_with_loop == EntityUID::with_eid("D")
|| vertex_with_loop == EntityUID::with_eid("E")
|| vertex_with_loop == EntityUID::with_eid("H")
);
}
Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
}
}
#[test]
fn intersecting_cycles() {
let mut a = Entity::with_uid(EntityUID::with_eid("A"));
a.add_ancestor(EntityUID::with_eid("B"));
let mut b = Entity::with_uid(EntityUID::with_eid("B"));
b.add_ancestor(EntityUID::with_eid("C"));
let mut c = Entity::with_uid(EntityUID::with_eid("C"));
c.add_ancestor(EntityUID::with_eid("E"));
let mut d = Entity::with_uid(EntityUID::with_eid("D"));
d.add_ancestor(EntityUID::with_eid("A"));
d.add_ancestor(EntityUID::with_eid("B"));
d.add_ancestor(EntityUID::with_eid("F"));
let mut e = Entity::with_uid(EntityUID::with_eid("E"));
e.add_ancestor(EntityUID::with_eid("D"));
let mut f = Entity::with_uid(EntityUID::with_eid("F"));
f.add_ancestor(EntityUID::with_eid("E"));
let mut entities = HashMap::from([
(a.uid(), a),
(b.uid(), b),
(c.uid(), c),
(d.uid(), d),
(e.uid(), e),
(f.uid(), f),
]);
assert!(enforce_tc(&entities).is_err());
assert!(compute_tc_internal(&mut entities).is_ok());
assert!(enforce_tc(&entities).is_ok());
match enforce_dag_from_tc(&entities) {
Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
Err(TcError::HasCycle {
vertex_with_loop: _,
}) => (), Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
}
}
}