1use std::collections::{HashMap, HashSet};
21use std::fmt::{Debug, Display};
22use std::hash::Hash;
23
24mod err;
25pub use err::*;
26use itertools::Itertools;
27
28pub trait TCNode<K> {
34 fn get_key(&self) -> K;
36
37 fn add_edge_to(&mut self, k: K);
39
40 fn out_edges(&self) -> Box<dyn Iterator<Item = &K> + '_>;
42
43 fn has_edge_to(&self, k: &K) -> bool;
46
47 fn reset_edges(&mut self);
49
50 fn direct_edges(&self) -> Box<dyn Iterator<Item = &K> + '_> {
52 self.out_edges()
53 }
54}
55
56pub fn compute_tc<K, V>(nodes: &mut HashMap<K, V>, enforce_dag: bool) -> Result<(), K>
62where
63 K: Clone + Eq + Hash + Debug + Display,
64 V: TCNode<K>,
65{
66 compute_tc_internal::<K, V>(nodes);
67 if enforce_dag {
68 return enforce_dag_from_tc(nodes);
69 }
70 Ok(())
71}
72
73fn compute_tc_internal<K, V>(nodes: &mut HashMap<K, V>)
78where
79 K: Clone + Eq + Hash,
80 V: TCNode<K>,
81{
82 let mut ancestors: HashMap<K, HashSet<K>> = HashMap::new();
87 for node in nodes.values() {
88 let this_node_ancestors: &mut HashSet<K> = ancestors.entry(node.get_key()).or_default();
89 add_ancestors_to_set(node, nodes, this_node_ancestors);
90 }
91 for node in nodes.values_mut() {
92 node.reset_edges();
93 #[allow(clippy::expect_used)]
95 for ancestor_uid in ancestors
96 .get(&node.get_key())
97 .expect("shouldn't have added any new values to the `nodes` map")
98 {
99 node.add_edge_to(ancestor_uid.clone());
100 }
101 }
102}
103
104pub fn enforce_tc_and_dag<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
109where
110 K: Clone + Eq + Hash + Debug + Display,
111 V: TCNode<K>,
112{
113 let res = enforce_tc(entities);
114 if res.is_ok() {
115 return enforce_dag_from_tc(entities);
116 }
117 res
118}
119
120fn enforce_tc<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
125where
126 K: Clone + Eq + Hash + Debug + Display,
127 V: TCNode<K>,
128{
129 for entity in entities.values() {
130 for parent_uid in entity.out_edges() {
131 if let Some(parent) = entities.get(parent_uid) {
133 for grandparent in parent.out_edges() {
134 if !entity.has_edge_to(grandparent) {
135 return Err(TcError::missing_tc_edge(
136 entity.get_key(),
137 parent_uid.clone(),
138 grandparent.clone(),
139 ));
140 }
141 }
142 }
143 }
144 }
145 Ok(())
146}
147
148fn add_ancestors_to_set<K, V>(node: &V, hierarchy: &HashMap<K, V>, ancestors: &mut HashSet<K>)
152where
153 K: Clone + Eq + Hash,
154 V: TCNode<K>,
155{
156 for parent_uid in node.direct_edges() {
157 if ancestors.insert(parent_uid.clone()) {
158 if let Some(ancestor) = hierarchy.get(parent_uid) {
161 add_ancestors_to_set(ancestor, hierarchy, ancestors);
162 }
163 }
164 }
165}
166
167fn enforce_dag_from_tc<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
173where
174 K: Clone + Eq + Hash + Debug + Display,
175 V: TCNode<K>,
176{
177 for entity in entities.values() {
178 let key = entity.get_key();
179 if entity.out_edges().contains(&key) {
180 return Err(TcError::has_cycle(key));
181 }
182 }
183 Ok(())
184}
185
186#[allow(clippy::indexing_slicing)]
188#[allow(clippy::panic)]
190#[cfg(test)]
191mod tests {
192 use crate::ast::{Entity, EntityUID};
193
194 use super::*;
195
196 #[test]
197 fn basic() {
198 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
200 a.add_parent(EntityUID::with_eid("B"));
201 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
202 b.add_parent(EntityUID::with_eid("C"));
203 let c = Entity::with_uid(EntityUID::with_eid("C"));
204 let mut entities = HashMap::from([
205 (a.uid().clone(), a),
206 (b.uid().clone(), b),
207 (c.uid().clone(), c),
208 ]);
209 assert!(enforce_tc(&entities).is_err());
211 compute_tc_internal(&mut entities);
213 let a = &entities[&EntityUID::with_eid("A")];
214 let b = &entities[&EntityUID::with_eid("B")];
215 let c = &entities[&EntityUID::with_eid("C")];
216 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
218 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
220 assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
221 assert!(enforce_tc(&entities).is_ok());
223 assert!(enforce_dag_from_tc(&entities).is_ok());
225 }
226
227 #[test]
228 fn reversed() {
229 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
232 a.add_parent(EntityUID::with_eid("B"));
233 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
234 b.add_parent(EntityUID::with_eid("C"));
235 let c = Entity::with_uid(EntityUID::with_eid("C"));
236 let mut entities = HashMap::from([
237 (c.uid().clone(), c),
238 (b.uid().clone(), b),
239 (a.uid().clone(), a),
240 ]);
241 assert!(enforce_tc(&entities).is_err());
243 compute_tc_internal(&mut entities);
245 let a = &entities[&EntityUID::with_eid("A")];
246 let b = &entities[&EntityUID::with_eid("B")];
247 let c = &entities[&EntityUID::with_eid("C")];
248 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
250 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
252 assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
253 assert!(enforce_tc(&entities).is_ok());
255 assert!(enforce_dag_from_tc(&entities).is_ok());
257 }
258
259 #[test]
260 fn deeper() {
261 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
263 a.add_parent(EntityUID::with_eid("B"));
264 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
265 b.add_parent(EntityUID::with_eid("C"));
266 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
267 c.add_parent(EntityUID::with_eid("D"));
268 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
269 d.add_parent(EntityUID::with_eid("E"));
270 let e = Entity::with_uid(EntityUID::with_eid("E"));
271 let mut entities = HashMap::from([
272 (a.uid().clone(), a),
273 (b.uid().clone(), b),
274 (c.uid().clone(), c),
275 (d.uid().clone(), d),
276 (e.uid().clone(), e),
277 ]);
278 assert!(enforce_tc(&entities).is_err());
280 compute_tc_internal(&mut entities);
282 let a = &entities[&EntityUID::with_eid("A")];
283 let b = &entities[&EntityUID::with_eid("B")];
284 let c = &entities[&EntityUID::with_eid("C")];
285 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
287 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
288 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
289 assert!(b.is_descendant_of(&EntityUID::with_eid("D")));
290 assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
291 assert!(c.is_descendant_of(&EntityUID::with_eid("E")));
292 assert!(enforce_tc(&entities).is_ok());
294 assert!(enforce_dag_from_tc(&entities).is_ok());
296 }
297
298 #[test]
299 fn not_alphabetized() {
300 let mut foo = Entity::with_uid(EntityUID::with_eid("foo"));
306 foo.add_parent(EntityUID::with_eid("bar"));
307 let mut bar = Entity::with_uid(EntityUID::with_eid("bar"));
308 bar.add_parent(EntityUID::with_eid("baz"));
309 let mut baz = Entity::with_uid(EntityUID::with_eid("baz"));
310 baz.add_parent(EntityUID::with_eid("ham"));
311 let mut ham = Entity::with_uid(EntityUID::with_eid("ham"));
312 ham.add_parent(EntityUID::with_eid("eggs"));
313 let eggs = Entity::with_uid(EntityUID::with_eid("eggs"));
314 let mut entities = HashMap::from([
315 (ham.uid().clone(), ham),
316 (bar.uid().clone(), bar),
317 (foo.uid().clone(), foo),
318 (eggs.uid().clone(), eggs),
319 (baz.uid().clone(), baz),
320 ]);
321 assert!(enforce_tc(&entities).is_err());
323 compute_tc_internal(&mut entities);
325 let foo = &entities[&EntityUID::with_eid("foo")];
326 let bar = &entities[&EntityUID::with_eid("bar")];
327 let baz = &entities[&EntityUID::with_eid("baz")];
328 assert!(foo.is_descendant_of(&EntityUID::with_eid("baz")));
330 assert!(foo.is_descendant_of(&EntityUID::with_eid("ham")));
331 assert!(foo.is_descendant_of(&EntityUID::with_eid("eggs")));
332 assert!(bar.is_descendant_of(&EntityUID::with_eid("ham")));
333 assert!(bar.is_descendant_of(&EntityUID::with_eid("eggs")));
334 assert!(baz.is_descendant_of(&EntityUID::with_eid("eggs")));
335 assert!(enforce_tc(&entities).is_ok());
337 assert!(enforce_dag_from_tc(&entities).is_ok());
339 }
340
341 #[test]
342 fn multi_parents() {
343 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
350 a.add_parent(EntityUID::with_eid("B"));
351 a.add_parent(EntityUID::with_eid("D"));
352 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
353 b.add_parent(EntityUID::with_eid("C"));
354 let c = Entity::with_uid(EntityUID::with_eid("C"));
355 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
356 d.add_parent(EntityUID::with_eid("E"));
357 let e = Entity::with_uid(EntityUID::with_eid("E"));
358 let mut entities = HashMap::from([
359 (a.uid().clone(), a),
360 (b.uid().clone(), b),
361 (c.uid().clone(), c),
362 (d.uid().clone(), d),
363 (e.uid().clone(), e),
364 ]);
365 assert!(enforce_tc(&entities).is_err());
367 compute_tc_internal(&mut entities);
369 let a = &entities[&EntityUID::with_eid("A")];
370 let b = &entities[&EntityUID::with_eid("B")];
371 let d = &entities[&EntityUID::with_eid("D")];
372 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
374 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
375 assert!(!b.is_descendant_of(&EntityUID::with_eid("D")));
377 assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
378 assert!(!d.is_descendant_of(&EntityUID::with_eid("B")));
379 assert!(!d.is_descendant_of(&EntityUID::with_eid("C")));
380 assert!(enforce_tc(&entities).is_ok());
382 assert!(enforce_dag_from_tc(&entities).is_ok());
384 }
385
386 #[test]
387 fn dag() {
388 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
395 a.add_parent(EntityUID::with_eid("B"));
396 a.add_parent(EntityUID::with_eid("F"));
397 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
398 b.add_parent(EntityUID::with_eid("C"));
399 b.add_parent(EntityUID::with_eid("D"));
400 let c = Entity::with_uid(EntityUID::with_eid("C"));
401 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
402 d.add_parent(EntityUID::with_eid("E"));
403 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
404 e.add_parent(EntityUID::with_eid("H"));
405 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
406 f.add_parent(EntityUID::with_eid("G"));
407 let mut g = Entity::with_uid(EntityUID::with_eid("G"));
408 g.add_parent(EntityUID::with_eid("E"));
409 let h = Entity::with_uid(EntityUID::with_eid("H"));
410 let mut entities = HashMap::from([
411 (a.uid().clone(), a),
412 (b.uid().clone(), b),
413 (c.uid().clone(), c),
414 (d.uid().clone(), d),
415 (e.uid().clone(), e),
416 (f.uid().clone(), f),
417 (g.uid().clone(), g),
418 (h.uid().clone(), h),
419 ]);
420 assert!(enforce_tc(&entities).is_err());
422 compute_tc_internal(&mut entities);
424 let a = &entities[&EntityUID::with_eid("A")];
425 let b = &entities[&EntityUID::with_eid("B")];
426 let f = &entities[&EntityUID::with_eid("F")];
427 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
429 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
430 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
431 assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
432 assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
433 assert!(a.is_descendant_of(&EntityUID::with_eid("H")));
434 assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
435 assert!(b.is_descendant_of(&EntityUID::with_eid("H")));
436 assert!(f.is_descendant_of(&EntityUID::with_eid("E")));
437 assert!(f.is_descendant_of(&EntityUID::with_eid("H")));
438 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
440 assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
441 assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
442 assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
443 assert!(enforce_tc(&entities).is_ok());
445 assert!(enforce_dag_from_tc(&entities).is_ok());
447 }
448
449 #[test]
450 fn already_edges() {
451 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
459 a.add_parent(EntityUID::with_eid("B"));
460 a.add_parent(EntityUID::with_eid("C"));
461 a.add_parent(EntityUID::with_eid("D"));
462 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
463 b.add_parent(EntityUID::with_eid("C"));
464 b.add_parent(EntityUID::with_eid("E"));
465 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
466 c.add_parent(EntityUID::with_eid("E"));
467 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
468 d.add_parent(EntityUID::with_eid("C"));
469 d.add_parent(EntityUID::with_eid("F"));
470 let e = Entity::with_uid(EntityUID::with_eid("E"));
471 let f = Entity::with_uid(EntityUID::with_eid("F"));
472 let mut entities = HashMap::from([
473 (a.uid().clone(), a),
474 (b.uid().clone(), b),
475 (c.uid().clone(), c),
476 (d.uid().clone(), d),
477 (e.uid().clone(), e),
478 (f.uid().clone(), f),
479 ]);
480 assert!(enforce_tc(&entities).is_err());
482 compute_tc_internal(&mut entities);
484 let a = &entities[&EntityUID::with_eid("A")];
485 let b = &entities[&EntityUID::with_eid("B")];
486 let c = &entities[&EntityUID::with_eid("C")];
487 let d = &entities[&EntityUID::with_eid("D")];
488 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
490 assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
491 assert!(d.is_descendant_of(&EntityUID::with_eid("E")));
492 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
494 assert!(!c.is_descendant_of(&EntityUID::with_eid("F")));
495 assert!(enforce_tc(&entities).is_ok());
497 assert!(enforce_dag_from_tc(&entities).is_ok());
499 }
500
501 #[test]
502 fn disjoint_dag() {
503 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
510 a.add_parent(EntityUID::with_eid("F"));
511 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
512 b.add_parent(EntityUID::with_eid("C"));
513 let c = Entity::with_uid(EntityUID::with_eid("C"));
514 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
515 d.add_parent(EntityUID::with_eid("E"));
516 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
517 e.add_parent(EntityUID::with_eid("H"));
518 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
519 f.add_parent(EntityUID::with_eid("G"));
520 let g = Entity::with_uid(EntityUID::with_eid("G"));
521 let h = Entity::with_uid(EntityUID::with_eid("H"));
522 let mut entities = HashMap::from([
523 (a.uid().clone(), a),
524 (b.uid().clone(), b),
525 (c.uid().clone(), c),
526 (d.uid().clone(), d),
527 (e.uid().clone(), e),
528 (f.uid().clone(), f),
529 (g.uid().clone(), g),
530 (h.uid().clone(), h),
531 ]);
532 assert!(enforce_tc(&entities).is_err());
534 compute_tc_internal(&mut entities);
536 let a = &entities[&EntityUID::with_eid("A")];
537 let b = &entities[&EntityUID::with_eid("B")];
538 let d = &entities[&EntityUID::with_eid("D")];
539 let f = &entities[&EntityUID::with_eid("F")];
540 assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
542 assert!(d.is_descendant_of(&EntityUID::with_eid("H")));
543 assert!(!a.is_descendant_of(&EntityUID::with_eid("C")));
545 assert!(!a.is_descendant_of(&EntityUID::with_eid("D")));
546 assert!(!a.is_descendant_of(&EntityUID::with_eid("E")));
547 assert!(!a.is_descendant_of(&EntityUID::with_eid("H")));
548 assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
549 assert!(!b.is_descendant_of(&EntityUID::with_eid("H")));
550 assert!(!f.is_descendant_of(&EntityUID::with_eid("E")));
551 assert!(!f.is_descendant_of(&EntityUID::with_eid("H")));
552 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
553 assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
554 assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
555 assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
556 assert!(enforce_tc(&entities).is_ok());
558 assert!(enforce_dag_from_tc(&entities).is_ok());
560 }
561
562 #[test]
563 fn trivial_cycle() {
564 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
569 a.add_parent(EntityUID::with_eid("B"));
570 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
571 b.add_parent(EntityUID::with_eid("B"));
572 let mut entities = HashMap::from([(a.uid().clone(), a), (b.uid().clone(), b)]);
573 compute_tc_internal(&mut entities);
575 match enforce_dag_from_tc(&entities) {
577 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
578 Err(TcError::HasCycle(err)) => {
579 assert!(err.vertex_with_loop() == &EntityUID::with_eid("B"));
580 }
581 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
582 }
583 let a = &entities[&EntityUID::with_eid("A")];
584 let b = &entities[&EntityUID::with_eid("B")];
585 assert!(a.is_descendant_of(&EntityUID::with_eid("B")));
587 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
589 assert!(enforce_tc(&entities).is_ok());
592 match enforce_dag_from_tc(&entities) {
594 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
595 Err(TcError::HasCycle(err)) => {
596 assert!(err.vertex_with_loop() == &EntityUID::with_eid("B"));
597 }
598 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
599 }
600 }
601
602 #[test]
603 fn nontrivial_cycle() {
604 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
611 a.add_parent(EntityUID::with_eid("B"));
612 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
613 b.add_parent(EntityUID::with_eid("C"));
614 b.add_parent(EntityUID::with_eid("D"));
615 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
616 c.add_parent(EntityUID::with_eid("A"));
617 let d = Entity::with_uid(EntityUID::with_eid("D"));
618 let mut entities = HashMap::from([
619 (a.uid().clone(), a),
620 (b.uid().clone(), b),
621 (c.uid().clone(), c),
622 (d.uid().clone(), d),
623 ]);
624 compute_tc_internal(&mut entities);
626 match enforce_dag_from_tc(&entities) {
628 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
629 Err(TcError::HasCycle(err)) => {
630 assert!(
631 err.vertex_with_loop() == &EntityUID::with_eid("A")
632 || err.vertex_with_loop() == &EntityUID::with_eid("B")
633 || err.vertex_with_loop() == &EntityUID::with_eid("C")
634 );
635 }
636 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
637 }
638 let a = &entities[&EntityUID::with_eid("A")];
640 let b = &entities[&EntityUID::with_eid("B")];
641 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
643 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
644 assert!(b.is_descendant_of(&EntityUID::with_eid("A")));
646 assert!(enforce_tc(&entities).is_ok());
649 match enforce_dag_from_tc(&entities) {
651 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
652 Err(TcError::HasCycle(err)) => {
653 assert!(
654 err.vertex_with_loop() == &EntityUID::with_eid("A")
655 || err.vertex_with_loop() == &EntityUID::with_eid("B")
656 || err.vertex_with_loop() == &EntityUID::with_eid("C")
657 );
658 }
659 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
660 }
661 }
662
663 #[test]
664 fn disjoint_cycles() {
665 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
672 a.add_parent(EntityUID::with_eid("F"));
673 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
674 b.add_parent(EntityUID::with_eid("C"));
675 let mut c: Entity = Entity::with_uid(EntityUID::with_eid("C"));
676 c.add_parent(EntityUID::with_eid("B"));
677 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
678 d.add_parent(EntityUID::with_eid("E"));
679 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
680 e.add_parent(EntityUID::with_eid("H"));
681 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
682 f.add_parent(EntityUID::with_eid("G"));
683 let g = Entity::with_uid(EntityUID::with_eid("G"));
684 let mut h = Entity::with_uid(EntityUID::with_eid("H"));
685 h.add_parent(EntityUID::with_eid("D"));
686 let mut entities = HashMap::from([
687 (a.uid().clone(), a),
688 (b.uid().clone(), b),
689 (c.uid().clone(), c),
690 (d.uid().clone(), d),
691 (e.uid().clone(), e),
692 (f.uid().clone(), f),
693 (g.uid().clone(), g),
694 (h.uid().clone(), h),
695 ]);
696 assert!(enforce_tc(&entities).is_err());
698 compute_tc_internal(&mut entities);
700 assert!(enforce_tc(&entities).is_ok());
702 match enforce_dag_from_tc(&entities) {
704 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
705 Err(TcError::HasCycle(err)) => {
706 assert!(
708 err.vertex_with_loop() == &EntityUID::with_eid("B")
709 || err.vertex_with_loop() == &EntityUID::with_eid("C")
710 || err.vertex_with_loop() == &EntityUID::with_eid("D")
711 || err.vertex_with_loop() == &EntityUID::with_eid("E")
712 || err.vertex_with_loop() == &EntityUID::with_eid("H")
713 );
714 }
715 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
716 }
717 }
718
719 #[test]
720 fn intersecting_cycles() {
721 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
731 a.add_parent(EntityUID::with_eid("B"));
732 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
733 b.add_parent(EntityUID::with_eid("C"));
734 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
735 c.add_parent(EntityUID::with_eid("E"));
736 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
737 d.add_parent(EntityUID::with_eid("A"));
738 d.add_parent(EntityUID::with_eid("B"));
739 d.add_parent(EntityUID::with_eid("F"));
740 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
741 e.add_parent(EntityUID::with_eid("D"));
742 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
743 f.add_parent(EntityUID::with_eid("E"));
744 let mut entities = HashMap::from([
745 (a.uid().clone(), a),
746 (b.uid().clone(), b),
747 (c.uid().clone(), c),
748 (d.uid().clone(), d),
749 (e.uid().clone(), e),
750 (f.uid().clone(), f),
751 ]);
752 assert!(enforce_tc(&entities).is_err());
754 compute_tc_internal(&mut entities);
756 assert!(enforce_tc(&entities).is_ok());
758 match enforce_dag_from_tc(&entities) {
760 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
761 Err(TcError::HasCycle(_)) => (), Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
763 }
764 }
765
766 #[test]
767 fn updated() {
768 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
770 a.add_parent(EntityUID::with_eid("B"));
771 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
772 b.add_parent(EntityUID::with_eid("C"));
773 let c = Entity::with_uid(EntityUID::with_eid("C"));
774 let mut entities = HashMap::from([
775 (a.uid().clone(), a),
776 (b.uid().clone(), b),
777 (c.uid().clone(), c),
778 ]);
779 assert!(enforce_tc(&entities).is_err());
781 compute_tc_internal(&mut entities);
783 let a = &entities[&EntityUID::with_eid("A")];
784 let b = &entities[&EntityUID::with_eid("B")];
785 let c = &entities[&EntityUID::with_eid("C")];
786 assert!(a.has_edge_to(&EntityUID::with_eid("C")));
788 assert!(!b.has_edge_to(&EntityUID::with_eid("A")));
790 assert!(!c.has_edge_to(&EntityUID::with_eid("A")));
791 assert!(enforce_tc(&entities).is_ok());
793 assert!(enforce_dag_from_tc(&entities).is_ok());
795 assert!(!a.has_edge_to(&EntityUID::with_eid("D")));
797
798 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
800 b.add_parent(EntityUID::with_eid("D"));
801 let d = Entity::with_uid(EntityUID::with_eid("D"));
802 entities.insert(b.uid().clone(), b);
803 entities.insert(d.uid().clone(), d);
804
805 assert!(enforce_tc(&entities).is_err());
807 compute_tc_internal(&mut entities);
809 let a = &entities[&EntityUID::with_eid("A")];
810 let b = &entities[&EntityUID::with_eid("B")];
811 let c = &entities[&EntityUID::with_eid("C")];
812 let d = &entities[&EntityUID::with_eid("D")];
813 assert!(a.has_edge_to(&EntityUID::with_eid("D")));
815 assert!(!a.has_edge_to(&EntityUID::with_eid("C")));
817 assert!(!b.has_edge_to(&EntityUID::with_eid("C")));
818 assert!(!b.has_edge_to(&EntityUID::with_eid("A")));
820 assert!(!c.has_edge_to(&EntityUID::with_eid("A")));
821 assert!(!d.has_edge_to(&EntityUID::with_eid("A")));
822 assert!(enforce_tc(&entities).is_ok());
824 assert!(enforce_dag_from_tc(&entities).is_ok());
826 }
827}