incremental_topo/lib.rs
1#![forbid(unsafe_code, missing_docs, missing_debug_implementations)]
2
3//! The purpose of this crate is to maintain an topological order in the face
4//! of single updates, like adding new nodes, adding new depedencies, deleting
5//! dependencies, and deleting nodes.
6//!
7//! Adding nodes, deleting nodes, and deleting dependencies require a trivial
8//! amount of work to perform an update, because those operations do not change
9//! the topological ordering. Adding new dependencies can change the topological
10//! ordering.
11//!
12//! ## What is a Topological Order
13//!
14//! To define a topological order requires at least a simple definition of a
15//! graph, and specifically a directed acyclic graph (DAG). A graph can be
16//! described as a pair of sets, `(V, E)` where `V` is the set of all nodes in
17//! the graph, and `E` is the set of edges. An edge is defined as a pair, `(m,
18//! n)` where `m` and `n` are nodes. A directed graph means that edges only
19//! imply a single direction of relationship between two nodes, as opposed to a
20//! undirected graph which implies the relationship goes both ways. An example
21//! of undirected vs. directed in social networks would be Facebook vs.
22//! Twitter. Facebook friendship is a two way relationship, while following
23//! someone on Twitter does not imply that they follow you back.
24//!
25//! A topological ordering, `ord_D` of a directed acyclic graph, `D = (V, E)`
26//! where `x, y ∈ V`, is a mapping of nodes to priority values such that
27//! `ord_D(x) < ord_D(y)` holds for all edges `(x, y) ∈ E`. This yields a total
28//! ordering of the nodes in `D`.
29//!
30//! ## Examples
31//!
32//! ```
33//! use incremental_topo::IncrementalTopo;
34//! use std::{cmp::Ordering::*, collections::HashSet};
35//!
36//! let mut dag = IncrementalTopo::new();
37//!
38//! let dog = dag.add_node();
39//! let cat = dag.add_node();
40//! let mouse = dag.add_node();
41//! let lion = dag.add_node();
42//! let human = dag.add_node();
43//! let gazelle = dag.add_node();
44//! let grass = dag.add_node();
45//!
46//! assert_eq!(dag.len(), 7);
47//!
48//! dag.add_dependency(&lion, &human).unwrap();
49//! dag.add_dependency(&lion, &gazelle).unwrap();
50//!
51//! dag.add_dependency(&human, &dog).unwrap();
52//! dag.add_dependency(&human, &cat).unwrap();
53//!
54//! dag.add_dependency(&dog, &cat).unwrap();
55//! dag.add_dependency(&cat, &mouse).unwrap();
56//!
57//! dag.add_dependency(&gazelle, &grass).unwrap();
58//!
59//! dag.add_dependency(&mouse, &grass).unwrap();
60//!
61//! let pairs = dag
62//! .descendants_unsorted(&human)
63//! .unwrap()
64//! .collect::<HashSet<_>>();
65//! let expected_pairs = [(4, cat), (3, dog), (5, mouse), (7, grass)]
66//! .iter()
67//! .cloned()
68//! .collect::<HashSet<_>>();
69//!
70//! assert_eq!(pairs, expected_pairs);
71//!
72//! assert!(dag.contains_transitive_dependency(&lion, &grass));
73//! assert!(!dag.contains_transitive_dependency(&human, &gazelle));
74//!
75//! assert_eq!(dag.topo_cmp(&cat, &dog), Greater);
76//! assert_eq!(dag.topo_cmp(&lion, &human), Less);
77//! ```
78//!
79//! ## Sources
80//!
81//! The [paper by D. J. Pearce and P. H. J. Kelly] contains descriptions of
82//! three different algorithms for incremental topological ordering, along with
83//! analysis of runtime bounds for each.
84//!
85//! [paper by D. J. Pearce and P. H. J. Kelly]: http://www.doc.ic.ac.uk/~phjk/Publications/DynamicTopoSortAlg-JEA-07.pdf
86
87use fnv::FnvHashSet;
88use generational_arena::{Arena, Index as ArenaIndex};
89use std::{
90 borrow::Borrow,
91 cmp::{Ordering, Reverse},
92 collections::BinaryHeap,
93 fmt,
94 iter::Iterator,
95};
96
97/// Data structure for maintaining a topological ordering over a collection
98/// of elements, in an incremental fashion.
99///
100/// See the [module-level documentation] for more information.
101///
102/// [module-level documentation]: index.html
103#[derive(Default, Debug, Clone)]
104pub struct IncrementalTopo {
105 node_data: Arena<NodeData>,
106 last_topo_order: TopoOrder,
107}
108
109/// An identifier of a node in the [`IncrementalTopo`] object.
110///
111/// This identifier contains metadata so that a node which has been passed to
112/// [`IncrementalTopo::delete_node`] will not be confused with a node created
113/// later.
114#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
115#[repr(transparent)]
116pub struct Node(ArenaIndex);
117
118impl From<ArenaIndex> for Node {
119 fn from(src: ArenaIndex) -> Self {
120 Node(src)
121 }
122}
123
124/// An identifier of a node that is lacking additional safety metadata that
125/// prevents ABA issues.
126#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
127#[repr(transparent)]
128struct UnsafeIndex(usize);
129
130impl From<Node> for UnsafeIndex {
131 fn from(src: Node) -> Self {
132 // extract index part of [`ArenaIndex`], discarding generation information
133 UnsafeIndex(src.0.into_raw_parts().0)
134 }
135}
136
137impl From<&Node> for UnsafeIndex {
138 fn from(src: &Node) -> Self {
139 // extract index part of [`ArenaIndex`], discarding generation information
140 UnsafeIndex(src.0.into_raw_parts().0)
141 }
142}
143
144/// The representation of a node, with all information about it ordering, which
145/// nodes it points to, and which nodes point to it.
146#[derive(Debug, Clone, PartialEq, Eq)]
147struct NodeData {
148 topo_order: TopoOrder,
149 parents: FnvHashSet<UnsafeIndex>,
150 children: FnvHashSet<UnsafeIndex>,
151}
152
153impl NodeData {
154 /// Create a new node entry with the specified topological order.
155 fn new(topo_order: TopoOrder) -> Self {
156 NodeData {
157 topo_order,
158 parents: FnvHashSet::default(),
159 children: FnvHashSet::default(),
160 }
161 }
162}
163
164type TopoOrder = usize;
165
166impl PartialOrd for NodeData {
167 fn partial_cmp(&self, other: &NodeData) -> Option<Ordering> {
168 Some(self.topo_order.cmp(&other.topo_order))
169 }
170}
171
172impl Ord for NodeData {
173 fn cmp(&self, other: &Self) -> Ordering {
174 self.partial_cmp(other).unwrap()
175 }
176}
177
178/// Different types of failures that can occur while updating or querying
179/// the graph.
180#[derive(Debug, PartialEq, Eq)]
181pub enum Error {
182 /// The given node was not found in the topological order.
183 ///
184 /// This usually means that the node was deleted, but a reference was
185 /// kept around after which is now invalid.
186 NodeMissing,
187 /// Cycles of nodes may not be formed in the graph.
188 CycleDetected,
189}
190
191impl fmt::Display for Error {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 match self {
194 Error::NodeMissing => {
195 write!(f, "The given node was not found in the topological order")
196 },
197 Error::CycleDetected => write!(f, "Cycles of nodes may not be formed in the graph"),
198 }
199 }
200}
201
202impl std::error::Error for Error {}
203
204impl IncrementalTopo {
205 /// Create a new IncrementalTopo graph.
206 ///
207 /// # Examples
208 /// ```
209 /// use incremental_topo::IncrementalTopo;
210 /// let dag = IncrementalTopo::new();
211 ///
212 /// assert!(dag.is_empty());
213 /// ```
214 pub fn new() -> Self {
215 IncrementalTopo {
216 last_topo_order: 0,
217 node_data: Arena::new(),
218 }
219 }
220
221 /// Add a new node to the graph and return a unique [`Node`] which
222 /// identifies it.
223 ///
224 /// Initially this node will not have any order relative to the values
225 /// that are already in the graph. Only when relations are added
226 /// with [`add_dependency`] will the order begin to matter.
227 ///
228 /// # Examples
229 /// ```
230 /// use incremental_topo::IncrementalTopo;
231 /// let mut dag = IncrementalTopo::new();
232 ///
233 /// let cat = dag.add_node();
234 /// let dog = dag.add_node();
235 /// let mouse = dag.add_node();
236 /// let human = dag.add_node();
237 ///
238 /// assert_ne!(cat, dog);
239 /// assert_ne!(cat, mouse);
240 /// assert_ne!(cat, human);
241 /// assert_ne!(dog, mouse);
242 /// assert_ne!(dog, human);
243 /// assert_ne!(mouse, human);
244 ///
245 /// assert!(dag.contains_node(&cat));
246 /// assert!(dag.contains_node(&dog));
247 /// assert!(dag.contains_node(&mouse));
248 /// assert!(dag.contains_node(&human));
249 /// ```
250 ///
251 /// [`add_dependency`]: struct.IncrementalTopo.html#method.add_dependency
252 pub fn add_node(&mut self) -> Node {
253 let next_topo_order = self.last_topo_order + 1;
254 self.last_topo_order = next_topo_order;
255
256 let node_data = NodeData::new(next_topo_order);
257
258 Node(self.node_data.insert(node_data))
259 }
260
261 /// Returns true if the graph contains the specified node.
262 ///
263 /// # Examples
264 /// ```
265 /// use incremental_topo::IncrementalTopo;
266 /// let mut dag = IncrementalTopo::new();
267 ///
268 /// let cat = dag.add_node();
269 /// let dog = dag.add_node();
270 ///
271 /// assert!(dag.contains_node(cat));
272 /// assert!(dag.contains_node(dog));
273 /// ```
274 pub fn contains_node(&self, node: impl Borrow<Node>) -> bool {
275 let node = node.borrow();
276 self.node_data.contains(node.0)
277 }
278
279 /// Attempt to remove node from graph, returning true if the node was
280 /// contained and removed.
281 ///
282 /// # Examples
283 /// ```
284 /// use incremental_topo::IncrementalTopo;
285 /// let mut dag = IncrementalTopo::new();
286 ///
287 /// let cat = dag.add_node();
288 /// let dog = dag.add_node();
289 ///
290 /// assert!(dag.delete_node(cat));
291 /// assert!(dag.delete_node(dog));
292 ///
293 /// assert!(!dag.delete_node(cat));
294 /// assert!(!dag.delete_node(dog));
295 /// ```
296 pub fn delete_node(&mut self, node: Node) -> bool {
297 if !self.node_data.contains(node.0) {
298 return false;
299 }
300
301 // Remove associated data
302 let data = self.node_data.remove(node.0).unwrap();
303
304 // Delete forward edges
305 for child in data.children {
306 if let Some((child_data, _)) = self.node_data.get_unknown_gen_mut(child.0) {
307 child_data.parents.remove(&node.into());
308 }
309 }
310
311 // Delete backward edges
312 for parent in data.parents {
313 if let Some((parent_data, _)) = self.node_data.get_unknown_gen_mut(parent.0) {
314 parent_data.children.remove(&node.into());
315 }
316 }
317
318 // TODO Fix inefficient compaction step
319 for (_, other_node) in self.node_data.iter_mut() {
320 if other_node.topo_order > data.topo_order {
321 other_node.topo_order -= 1;
322 }
323 }
324
325 // Decrement last topo order to account for shifted topo values
326 self.last_topo_order -= 1;
327
328 true
329 }
330
331 /// Add a directed link between two nodes already present in the graph.
332 ///
333 /// This link indicates an ordering constraint on the two nodes, now
334 /// `prec` must always come before `succ` in the ordering.
335 ///
336 /// Returns `Ok(true)` if the graph did not previously contain this
337 /// dependency. Returns `Ok(false)` if the graph did have a previous
338 /// dependency between these two nodes.
339 ///
340 /// # Errors
341 /// This function will return an `Err` if the dependency introduces a
342 /// cycle into the graph or if either of the nodes passed is not
343 /// found in the graph.
344 ///
345 /// # Examples
346 /// ```
347 /// use incremental_topo::IncrementalTopo;
348 /// let mut dag = IncrementalTopo::new();
349 ///
350 /// let cat = dag.add_node();
351 /// let mouse = dag.add_node();
352 /// let dog = dag.add_node();
353 /// let human = dag.add_node();
354 ///
355 /// assert!(dag.add_dependency(&human, dog).unwrap());
356 /// assert!(dag.add_dependency(&human, cat).unwrap());
357 /// assert!(dag.add_dependency(&cat, mouse).unwrap());
358 /// ```
359 ///
360 /// Here is an example which returns [`Error::CycleDetected`] when
361 /// introducing a cycle:
362 ///
363 /// ```
364 /// # use incremental_topo::{IncrementalTopo, Error};
365 /// let mut dag = IncrementalTopo::new();
366 ///
367 /// let n0 = dag.add_node();
368 /// assert_eq!(dag.add_dependency(&n0, &n0), Err(Error::CycleDetected));
369 ///
370 /// let n1 = dag.add_node();
371 ///
372 /// assert!(dag.add_dependency(&n0, &n1).unwrap());
373 /// assert_eq!(dag.add_dependency(&n1, &n0), Err(Error::CycleDetected));
374 ///
375 /// let n2 = dag.add_node();
376 ///
377 /// assert!(dag.add_dependency(&n1, &n2).unwrap());
378 /// assert_eq!(dag.add_dependency(&n2, &n0), Err(Error::CycleDetected));
379 /// ```
380 pub fn add_dependency(
381 &mut self,
382 prec: impl Borrow<Node>,
383 succ: impl Borrow<Node>,
384 ) -> Result<bool, Error> {
385 let prec = prec.borrow();
386 let succ = succ.borrow();
387
388 if prec == succ {
389 // No loops to self
390 return Err(Error::CycleDetected);
391 }
392
393 let succ_index = UnsafeIndex::from(succ);
394 let prec_index = UnsafeIndex::from(prec);
395
396 // Insert forward edge
397 let mut no_prev_edge = self.node_data[prec.0].children.insert(succ_index);
398 let upper_bound = self.node_data[prec.0].topo_order;
399
400 // Insert backward edge
401 no_prev_edge = no_prev_edge && self.node_data[succ.0].parents.insert(prec_index);
402 let lower_bound = self.node_data[succ.0].topo_order;
403
404 // If edge already exists short circuit
405 if !no_prev_edge {
406 return Ok(false);
407 }
408
409 log::info!("Adding edge from {:?} to {:?}", prec, succ);
410
411 log::trace!(
412 "Upper: Order({}), Lower: Order({})",
413 upper_bound,
414 lower_bound
415 );
416 // If the affected region of the graph has non-zero size (i.e. the upper and
417 // lower bound are equal) then perform an update to the topological ordering of
418 // the graph
419 if lower_bound < upper_bound {
420 log::trace!("Will change");
421 let mut visited = FnvHashSet::default();
422
423 // Walk changes forward from the succ, checking for any cycles that would be
424 // introduced
425 let change_forward = match self.dfs_forward(succ_index, &mut visited, upper_bound) {
426 Ok(change_set) => change_set,
427 Err(err) => {
428 // need to remove parent + child info that was previously added
429
430 self.node_data[prec.0].children.remove(&succ_index);
431 self.node_data[succ.0].parents.remove(&prec_index);
432
433 return Err(err);
434 },
435 };
436 log::trace!("Change forward: {:?}", change_forward);
437 // Walk backwards from the prec
438 let change_backward = self.dfs_backward(prec_index, &mut visited, lower_bound);
439 log::trace!("Change backward: {:?}", change_backward);
440
441 self.reorder_nodes(change_forward, change_backward);
442 } else {
443 log::trace!("No change");
444 }
445
446 Ok(true)
447 }
448
449 /// Returns true if the graph contains a dependency from `prec` to
450 /// `succ`.
451 ///
452 /// Returns false if either node is not found, or if there is no
453 /// dependency.
454 ///
455 /// # Examples
456 /// ```
457 /// use incremental_topo::IncrementalTopo;
458 /// let mut dag = IncrementalTopo::new();
459 ///
460 /// let cat = dag.add_node();
461 /// let mouse = dag.add_node();
462 /// let human = dag.add_node();
463 /// let horse = dag.add_node();
464 ///
465 /// assert!(dag.add_dependency(&human, &cat).unwrap());
466 /// assert!(dag.add_dependency(&cat, &mouse).unwrap());
467 ///
468 /// assert!(dag.contains_dependency(&cat, &mouse));
469 /// assert!(!dag.contains_dependency(&human, &mouse));
470 /// assert!(!dag.contains_dependency(&cat, &horse));
471 /// ```
472 pub fn contains_dependency(&self, prec: impl Borrow<Node>, succ: impl Borrow<Node>) -> bool {
473 let prec = prec.borrow();
474 let succ = succ.borrow();
475
476 if !self.node_data.contains(prec.0) || !self.node_data.contains(succ.0) {
477 return false;
478 }
479
480 self.node_data[prec.0].children.contains(&succ.into())
481 }
482
483 /// Returns true if the graph contains a transitive dependency from
484 /// `prec` to `succ`.
485 ///
486 /// In this context a transitive dependency means that `succ` exists as
487 /// a descendant of `prec`, with some chain of other nodes in
488 /// between.
489 ///
490 /// Returns false if either node is not found in the graph, or there is
491 /// no transitive dependency.
492 ///
493 /// # Examples
494 /// ```
495 /// use incremental_topo::IncrementalTopo;
496 /// let mut dag = IncrementalTopo::new();
497 ///
498 /// let cat = dag.add_node();
499 /// let mouse = dag.add_node();
500 /// let dog = dag.add_node();
501 /// let human = dag.add_node();
502 ///
503 /// assert!(dag.add_dependency(&human, &cat).unwrap());
504 /// assert!(dag.add_dependency(&human, &dog).unwrap());
505 /// assert!(dag.add_dependency(&cat, &mouse).unwrap());
506 ///
507 /// assert!(dag.contains_transitive_dependency(&human, &mouse));
508 /// assert!(!dag.contains_transitive_dependency(&dog, &mouse));
509 /// ```
510 pub fn contains_transitive_dependency(
511 &self,
512 prec: impl Borrow<Node>,
513 succ: impl Borrow<Node>,
514 ) -> bool {
515 let prec = prec.borrow();
516 let succ = succ.borrow();
517
518 // If either node is missing, return quick
519 if !self.node_data.contains(prec.0) || !self.node_data.contains(succ.0) {
520 return false;
521 }
522
523 // A node cannot depend on itself
524 if prec.0 == succ.0 {
525 return false;
526 }
527
528 // Else we have to search the graph. Using dfs in this case because it avoids
529 // the overhead of the binary heap, and this task doesn't really need ordered
530 // descendants.
531 let mut stack = Vec::new();
532 let mut visited = FnvHashSet::default();
533
534 stack.push(UnsafeIndex::from(prec));
535
536 // For each node key popped off the stack, check that we haven't seen it
537 // before, then check if its children contain the node we're searching for.
538 // If they don't, continue the search by extending the stack with the children.
539 while let Some(key) = stack.pop() {
540 if visited.contains(&key) {
541 continue;
542 } else {
543 visited.insert(key);
544 }
545
546 let children = &self.node_data.get_unknown_gen(key.0).unwrap().0.children;
547
548 if children.contains(&succ.into()) {
549 return true;
550 } else {
551 stack.extend(children);
552
553 continue;
554 }
555 }
556
557 // If we exhaust the stack, then there is no transitive dependency.
558 false
559 }
560
561 /// Attempt to remove a dependency from the graph, returning true if the
562 /// dependency was removed.
563 ///
564 /// Returns false is either node is not found in the graph.
565 ///
566 /// Removing a dependency from the graph is an extremely simple
567 /// operation, which requires no recalculation of the
568 /// topological order. The ordering before and after a removal
569 /// is exactly the same.
570 ///
571 /// # Examples
572 /// ```
573 /// use incremental_topo::IncrementalTopo;
574 /// let mut dag = IncrementalTopo::new();
575 ///
576 /// let cat = dag.add_node();
577 /// let mouse = dag.add_node();
578 /// let dog = dag.add_node();
579 /// let human = dag.add_node();
580 ///
581 /// assert!(dag.add_dependency(&human, &cat).unwrap());
582 /// assert!(dag.add_dependency(&human, &dog).unwrap());
583 /// assert!(dag.add_dependency(&cat, &mouse).unwrap());
584 ///
585 /// assert!(dag.delete_dependency(&cat, mouse));
586 /// assert!(dag.delete_dependency(&human, dog));
587 /// assert!(!dag.delete_dependency(&human, mouse));
588 /// ```
589 pub fn delete_dependency(&mut self, prec: impl Borrow<Node>, succ: impl Borrow<Node>) -> bool {
590 let prec = prec.borrow();
591 let succ = succ.borrow();
592
593 if !self.node_data.contains(prec.0) || !self.node_data.contains(succ.0) {
594 return false;
595 }
596
597 let prec_children = &mut self.node_data[prec.0].children;
598
599 if !prec_children.contains(&succ.into()) {
600 return false;
601 }
602
603 prec_children.remove(&succ.into());
604 self.node_data[succ.0].parents.remove(&prec.into());
605
606 true
607 }
608
609 /// Return the number of nodes within the graph.
610 ///
611 /// # Examples
612 /// ```
613 /// use incremental_topo::IncrementalTopo;
614 /// let mut dag = IncrementalTopo::new();
615 ///
616 /// let cat = dag.add_node();
617 /// let mouse = dag.add_node();
618 /// let dog = dag.add_node();
619 /// let human = dag.add_node();
620 ///
621 /// assert_eq!(dag.len(), 4);
622 /// ```
623 pub fn len(&self) -> usize {
624 self.node_data.len()
625 }
626
627 /// Return true if there are no nodes in the graph.
628 ///
629 /// # Examples
630 /// ```
631 /// use incremental_topo::IncrementalTopo;
632 /// let mut dag = IncrementalTopo::new();
633 ///
634 /// assert!(dag.is_empty());
635 ///
636 /// let cat = dag.add_node();
637 /// let mouse = dag.add_node();
638 /// let dog = dag.add_node();
639 /// let human = dag.add_node();
640 ///
641 /// assert!(!dag.is_empty());
642 /// ```
643 pub fn is_empty(&self) -> bool {
644 self.len() == 0
645 }
646
647 /// Return an iterator over all the nodes of the graph in an unsorted order.
648 ///
649 /// # Examples
650 /// ```
651 /// use incremental_topo::IncrementalTopo;
652 /// use std::collections::HashSet;
653 /// let mut dag = IncrementalTopo::new();
654 ///
655 /// let cat = dag.add_node();
656 /// let mouse = dag.add_node();
657 /// let dog = dag.add_node();
658 /// let human = dag.add_node();
659 ///
660 /// assert!(dag.add_dependency(&human, &cat).unwrap());
661 /// assert!(dag.add_dependency(&human, &dog).unwrap());
662 /// assert!(dag.add_dependency(&cat, &mouse).unwrap());
663 ///
664 /// let pairs = dag.iter_unsorted().collect::<HashSet<_>>();
665 ///
666 /// let mut expected_pairs = HashSet::new();
667 /// expected_pairs.extend(vec![(1, human), (2, cat), (4, mouse), (3, dog)]);
668 ///
669 /// assert_eq!(pairs, expected_pairs);
670 /// ```
671 pub fn iter_unsorted(&self) -> impl Iterator<Item = (TopoOrder, Node)> + '_ {
672 self.node_data
673 .iter()
674 .map(|(index, node)| (node.topo_order, index.into()))
675 }
676
677 /// Return an iterator over the descendants of a node in the graph, in
678 /// an unsorted order.
679 ///
680 /// Accessing the nodes in an unsorted order allows for faster access
681 /// using a iterative DFS search. This is opposed to the order
682 /// descendants iterator which requires the use of a binary heap
683 /// to order the values.
684 ///
685 /// # Errors
686 ///
687 /// This function will return an error if the given node is not present in
688 /// the graph.
689 ///
690 /// # Examples
691 /// ```
692 /// use incremental_topo::IncrementalTopo;
693 /// use std::collections::HashSet;
694 /// let mut dag = IncrementalTopo::new();
695 ///
696 /// let cat = dag.add_node();
697 /// let mouse = dag.add_node();
698 /// let dog = dag.add_node();
699 /// let human = dag.add_node();
700 ///
701 /// assert!(dag.add_dependency(&human, &cat).unwrap());
702 /// assert!(dag.add_dependency(&human, &dog).unwrap());
703 /// assert!(dag.add_dependency(&dog, &cat).unwrap());
704 /// assert!(dag.add_dependency(&cat, &mouse).unwrap());
705 ///
706 /// let pairs = dag
707 /// .descendants_unsorted(human)
708 /// .unwrap()
709 /// .collect::<HashSet<_>>();
710 ///
711 /// let mut expected_pairs = HashSet::new();
712 /// expected_pairs.extend(vec![(2, dog), (3, cat), (4, mouse)]);
713 ///
714 /// assert_eq!(pairs, expected_pairs);
715 /// ```
716 pub fn descendants_unsorted(
717 &self,
718 node: impl Borrow<Node>,
719 ) -> Result<DescendantsUnsorted, Error> {
720 let node = node.borrow();
721 if !self.node_data.contains(node.0) {
722 return Err(Error::NodeMissing);
723 }
724
725 let mut stack = Vec::new();
726 let visited = FnvHashSet::default();
727
728 // Add all children of selected node
729 stack.extend(&self.node_data[node.0].children);
730
731 Ok(DescendantsUnsorted {
732 dag: self,
733 stack,
734 visited,
735 })
736 }
737
738 /// Return an iterator over descendants of a node in the graph, in a
739 /// topologically sorted order.
740 ///
741 /// Accessing the nodes in a sorted order requires the use of a
742 /// BinaryHeap, so some performance penalty is paid there. If
743 /// all is required is access to the descendants of a node, use
744 /// [`IncrementalTopo::descendants_unsorted`].
745 ///
746 /// # Errors
747 ///
748 /// This function will return an error if the given node is not present in
749 /// the graph.
750 ///
751 /// # Examples
752 /// ```
753 /// use incremental_topo::IncrementalTopo;
754 /// let mut dag = IncrementalTopo::new();
755 ///
756 /// let cat = dag.add_node();
757 /// let mouse = dag.add_node();
758 /// let dog = dag.add_node();
759 /// let human = dag.add_node();
760 ///
761 /// assert!(dag.add_dependency(&human, &cat).unwrap());
762 /// assert!(dag.add_dependency(&human, &dog).unwrap());
763 /// assert!(dag.add_dependency(&dog, &cat).unwrap());
764 /// assert!(dag.add_dependency(&cat, &mouse).unwrap());
765 ///
766 /// let ordered_nodes = dag.descendants(human).unwrap().collect::<Vec<_>>();
767 ///
768 /// assert_eq!(ordered_nodes, vec![dog, cat, mouse]);
769 /// ```
770 pub fn descendants(&self, node: impl Borrow<Node>) -> Result<Descendants, Error> {
771 let node = node.borrow();
772 if !self.node_data.contains(node.0) {
773 return Err(Error::NodeMissing);
774 }
775
776 let mut queue = BinaryHeap::new();
777
778 // Add all children of selected node
779 queue.extend(
780 self.node_data[node.0]
781 .children
782 .iter()
783 .cloned()
784 .map(|child_key| {
785 let child_order = self.get_node_data(child_key).topo_order;
786 (Reverse(child_order), child_key)
787 }),
788 );
789
790 let visited = FnvHashSet::default();
791
792 Ok(Descendants {
793 dag: self,
794 queue,
795 visited,
796 })
797 }
798
799 /// Compare two nodes present in the graph, topographically.
800 ///
801 /// # Panics
802 ///
803 /// - If either node given is not present in the graph.
804 ///
805 /// # Examples
806 /// ```
807 /// use incremental_topo::IncrementalTopo;
808 /// use std::cmp::Ordering::*;
809 ///
810 /// let mut dag = IncrementalTopo::new();
811 ///
812 /// let cat = dag.add_node();
813 /// let mouse = dag.add_node();
814 /// let dog = dag.add_node();
815 /// let human = dag.add_node();
816 /// let horse = dag.add_node();
817 ///
818 /// assert!(dag.add_dependency(&human, &cat).unwrap());
819 /// assert!(dag.add_dependency(&human, &dog).unwrap());
820 /// assert!(dag.add_dependency(&dog, &cat).unwrap());
821 /// assert!(dag.add_dependency(&cat, &mouse).unwrap());
822 ///
823 /// assert_eq!(dag.topo_cmp(&human, &mouse), Less);
824 /// assert_eq!(dag.topo_cmp(&cat, &dog), Greater);
825 /// assert_eq!(dag.topo_cmp(&cat, &horse), Less);
826 /// ```
827 pub fn topo_cmp(&self, node_a: impl Borrow<Node>, node_b: impl Borrow<Node>) -> Ordering {
828 let node_a = node_a.borrow();
829 let node_b = node_b.borrow();
830
831 self.node_data[node_a.0]
832 .topo_order
833 .cmp(&self.node_data[node_b.0].topo_order)
834 }
835
836 fn dfs_forward(
837 &self,
838 start_key: UnsafeIndex,
839 visited: &mut FnvHashSet<UnsafeIndex>,
840 upper_bound: TopoOrder,
841 ) -> Result<FnvHashSet<UnsafeIndex>, Error> {
842 let mut stack = Vec::new();
843 let mut result = FnvHashSet::default();
844
845 stack.push(start_key);
846
847 while let Some(next_key) = stack.pop() {
848 visited.insert(next_key);
849 result.insert(next_key);
850
851 for child_key in &self.get_node_data(next_key).children {
852 let child_topo_order = self.get_node_data(*child_key).topo_order;
853
854 if child_topo_order == upper_bound {
855 return Err(Error::CycleDetected);
856 }
857
858 if !visited.contains(child_key) && child_topo_order < upper_bound {
859 stack.push(*child_key);
860 }
861 }
862 }
863
864 Ok(result)
865 }
866
867 fn dfs_backward(
868 &self,
869 start_key: UnsafeIndex,
870 visited: &mut FnvHashSet<UnsafeIndex>,
871 lower_bound: TopoOrder,
872 ) -> FnvHashSet<UnsafeIndex> {
873 let mut stack = Vec::new();
874 let mut result = FnvHashSet::default();
875
876 stack.push(start_key);
877
878 while let Some(next_key) = stack.pop() {
879 visited.insert(next_key);
880 result.insert(next_key);
881
882 for parent_key in &self.get_node_data(next_key).parents {
883 let parent_topo_order = self.get_node_data(*parent_key).topo_order;
884
885 if !visited.contains(parent_key) && lower_bound < parent_topo_order {
886 stack.push(*parent_key);
887 }
888 }
889 }
890
891 result
892 }
893
894 fn reorder_nodes(
895 &mut self,
896 change_forward: FnvHashSet<UnsafeIndex>,
897 change_backward: FnvHashSet<UnsafeIndex>,
898 ) {
899 let mut change_forward: Vec<_> = change_forward
900 .into_iter()
901 .map(|key| (key, self.get_node_data(key).topo_order))
902 .collect();
903 change_forward.sort_unstable_by_key(|pair| pair.1);
904
905 let mut change_backward: Vec<_> = change_backward
906 .into_iter()
907 .map(|key| (key, self.get_node_data(key).topo_order))
908 .collect();
909 change_backward.sort_unstable_by_key(|pair| pair.1);
910
911 let mut all_keys = Vec::new();
912 let mut all_topo_orders = Vec::new();
913
914 for (key, topo_order) in change_backward {
915 all_keys.push(key);
916 all_topo_orders.push(topo_order);
917 }
918
919 for (key, topo_order) in change_forward {
920 all_keys.push(key);
921 all_topo_orders.push(topo_order);
922 }
923
924 all_topo_orders.sort_unstable();
925
926 for (key, topo_order) in all_keys.into_iter().zip(all_topo_orders.into_iter()) {
927 self.node_data
928 .get_unknown_gen_mut(key.0)
929 .unwrap()
930 .0
931 .topo_order = topo_order;
932 }
933 }
934
935 fn get_node_data(&self, idx: UnsafeIndex) -> &NodeData {
936 self.node_data.get_unknown_gen(idx.0).unwrap().0
937 }
938}
939
940/// An iterator over the descendants of a node in the graph, which outputs the
941/// nodes in an unsorted order with their topological ranking.
942///
943/// # Examples
944/// ```
945/// use incremental_topo::IncrementalTopo;
946/// use std::collections::HashSet;
947/// let mut dag = IncrementalTopo::new();
948///
949/// let cat = dag.add_node();
950/// let mouse = dag.add_node();
951/// let dog = dag.add_node();
952/// let human = dag.add_node();
953///
954/// assert!(dag.add_dependency(&human, &cat).unwrap());
955/// assert!(dag.add_dependency(&human, &dog).unwrap());
956/// assert!(dag.add_dependency(&dog, &cat).unwrap());
957/// assert!(dag.add_dependency(&cat, &mouse).unwrap());
958///
959/// let pairs = dag
960/// .descendants_unsorted(human)
961/// .unwrap()
962/// .collect::<HashSet<_>>();
963///
964/// let mut expected_pairs = HashSet::new();
965/// expected_pairs.extend(vec![(2, dog), (3, cat), (4, mouse)]);
966///
967/// assert_eq!(pairs, expected_pairs);
968/// ```
969#[derive(Debug)]
970pub struct DescendantsUnsorted<'a> {
971 dag: &'a IncrementalTopo,
972 stack: Vec<UnsafeIndex>,
973 visited: FnvHashSet<UnsafeIndex>,
974}
975
976impl Iterator for DescendantsUnsorted<'_> {
977 type Item = (TopoOrder, Node);
978
979 fn next(&mut self) -> Option<Self::Item> {
980 while let Some(key) = self.stack.pop() {
981 if self.visited.contains(&key) {
982 continue;
983 } else {
984 self.visited.insert(key);
985 }
986
987 let (node_data, index) = self.dag.node_data.get_unknown_gen(key.0).unwrap();
988
989 let order = node_data.topo_order;
990
991 self.stack.extend(&node_data.children);
992
993 return Some((order, index.into()));
994 }
995
996 None
997 }
998}
999
1000/// An iterator over the descendants of a node in the graph, which outputs the
1001/// nodes in a sorted order by their topological ranking.
1002///
1003/// # Examples
1004/// ```
1005/// use incremental_topo::IncrementalTopo;
1006/// let mut dag = IncrementalTopo::new();
1007///
1008/// let cat = dag.add_node();
1009/// let mouse = dag.add_node();
1010/// let dog = dag.add_node();
1011/// let human = dag.add_node();
1012///
1013/// assert!(dag.add_dependency(&human, &cat).unwrap());
1014/// assert!(dag.add_dependency(&human, &dog).unwrap());
1015/// assert!(dag.add_dependency(&dog, &cat).unwrap());
1016/// assert!(dag.add_dependency(&cat, &mouse).unwrap());
1017///
1018/// let ordered_nodes = dag.descendants(human).unwrap().collect::<Vec<_>>();
1019///
1020/// assert_eq!(ordered_nodes, vec![dog, cat, mouse]);
1021/// ```
1022#[derive(Debug)]
1023pub struct Descendants<'a> {
1024 dag: &'a IncrementalTopo,
1025 queue: BinaryHeap<(Reverse<TopoOrder>, UnsafeIndex)>,
1026 visited: FnvHashSet<UnsafeIndex>,
1027}
1028
1029impl Iterator for Descendants<'_> {
1030 type Item = Node;
1031
1032 fn next(&mut self) -> Option<Self::Item> {
1033 loop {
1034 if let Some((_, key)) = self.queue.pop() {
1035 if self.visited.contains(&key) {
1036 continue;
1037 } else {
1038 self.visited.insert(key);
1039 }
1040
1041 let (node_data, index) = self.dag.node_data.get_unknown_gen(key.0).unwrap();
1042
1043 for child in &node_data.children {
1044 let order = self.dag.get_node_data(*child).topo_order;
1045 self.queue.push((Reverse(order), *child))
1046 }
1047
1048 return Some(index.into());
1049 } else {
1050 return None;
1051 }
1052 }
1053 }
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058 extern crate pretty_env_logger;
1059 use super::*;
1060
1061 fn get_basic_dag() -> Result<([Node; 7], IncrementalTopo), Error> {
1062 let mut dag = IncrementalTopo::new();
1063
1064 let dog = dag.add_node();
1065 let cat = dag.add_node();
1066 let mouse = dag.add_node();
1067 let lion = dag.add_node();
1068 let human = dag.add_node();
1069 let gazelle = dag.add_node();
1070 let grass = dag.add_node();
1071
1072 assert_eq!(dag.len(), 7);
1073
1074 dag.add_dependency(lion, human)?;
1075 dag.add_dependency(lion, gazelle)?;
1076
1077 dag.add_dependency(human, dog)?;
1078 dag.add_dependency(human, cat)?;
1079
1080 dag.add_dependency(dog, cat)?;
1081 dag.add_dependency(cat, mouse)?;
1082
1083 dag.add_dependency(gazelle, grass)?;
1084
1085 dag.add_dependency(mouse, grass)?;
1086
1087 Ok(([dog, cat, mouse, lion, human, gazelle, grass], dag))
1088 }
1089
1090 #[test]
1091 fn add_nodes_basic() {
1092 let mut dag = IncrementalTopo::new();
1093
1094 let dog = dag.add_node();
1095 let cat = dag.add_node();
1096 let mouse = dag.add_node();
1097 let lion = dag.add_node();
1098 let human = dag.add_node();
1099
1100 assert_eq!(dag.len(), 5);
1101 assert!(dag.contains_node(dog));
1102 assert!(dag.contains_node(cat));
1103 assert!(dag.contains_node(mouse));
1104 assert!(dag.contains_node(lion));
1105 assert!(dag.contains_node(human));
1106 }
1107
1108 #[test]
1109 fn delete_nodes() {
1110 let mut dag = IncrementalTopo::new();
1111
1112 let dog = dag.add_node();
1113 let cat = dag.add_node();
1114 let human = dag.add_node();
1115
1116 assert_eq!(dag.len(), 3);
1117
1118 assert!(dag.contains_node(dog));
1119 assert!(dag.contains_node(cat));
1120 assert!(dag.contains_node(human));
1121
1122 assert!(dag.delete_node(human));
1123 assert_eq!(dag.len(), 2);
1124 assert!(!dag.contains_node(human));
1125 }
1126
1127 #[test]
1128 fn reject_cycle() {
1129 let mut dag = IncrementalTopo::new();
1130
1131 let n1 = dag.add_node();
1132 let n2 = dag.add_node();
1133 let n3 = dag.add_node();
1134
1135 assert_eq!(dag.len(), 3);
1136
1137 assert!(dag.add_dependency(n1, n2).is_ok());
1138 assert!(dag.add_dependency(n2, n3).is_ok());
1139
1140 assert!(dag.add_dependency(n3, n1).is_err());
1141 assert!(dag.add_dependency(n1, n1).is_err());
1142 }
1143
1144 #[test]
1145 fn get_children_unordered() {
1146 let ([dog, cat, mouse, _, human, _, grass], dag) = get_basic_dag().unwrap();
1147
1148 let children: FnvHashSet<_> = dag
1149 .descendants_unsorted(human)
1150 .unwrap()
1151 .map(|(_, v)| v)
1152 .collect();
1153
1154 let mut expected_children = FnvHashSet::default();
1155 expected_children.extend(vec![dog, cat, mouse, grass]);
1156
1157 assert_eq!(children, expected_children);
1158
1159 let ordered_children: Vec<_> = dag.descendants(human).unwrap().collect();
1160 assert_eq!(ordered_children, vec![dog, cat, mouse, grass])
1161 }
1162
1163 #[test]
1164 fn topo_order_values_no_gaps() {
1165 let ([.., lion, _, _, _], dag) = get_basic_dag().unwrap();
1166
1167 let topo_orders: FnvHashSet<_> = dag
1168 .descendants_unsorted(lion)
1169 .unwrap()
1170 .map(|p| p.0)
1171 .collect();
1172
1173 assert_eq!(topo_orders, (2..=7).collect::<FnvHashSet<_>>())
1174 }
1175
1176 #[test]
1177 fn readme_example() {
1178 let mut dag = IncrementalTopo::new();
1179
1180 let cat = dag.add_node();
1181 let dog = dag.add_node();
1182 let human = dag.add_node();
1183
1184 assert_eq!(dag.len(), 3);
1185
1186 dag.add_dependency(human, dog).unwrap();
1187 dag.add_dependency(human, cat).unwrap();
1188 dag.add_dependency(dog, cat).unwrap();
1189
1190 let animal_order: Vec<_> = dag.descendants(human).unwrap().collect();
1191
1192 assert_eq!(animal_order, vec![dog, cat]);
1193 }
1194
1195 #[test]
1196 fn unordered_iter() {
1197 let mut dag = IncrementalTopo::new();
1198
1199 let cat = dag.add_node();
1200 let mouse = dag.add_node();
1201 let dog = dag.add_node();
1202 let human = dag.add_node();
1203
1204 assert!(dag.add_dependency(human, cat).unwrap());
1205 assert!(dag.add_dependency(human, dog).unwrap());
1206 assert!(dag.add_dependency(dog, cat).unwrap());
1207 assert!(dag.add_dependency(cat, mouse).unwrap());
1208
1209 let pairs = dag
1210 .descendants_unsorted(human)
1211 .unwrap()
1212 .collect::<FnvHashSet<_>>();
1213
1214 let mut expected_pairs = FnvHashSet::default();
1215 expected_pairs.extend(vec![(2, dog), (3, cat), (4, mouse)]);
1216
1217 assert_eq!(pairs, expected_pairs);
1218 }
1219
1220 #[test]
1221 fn topo_cmp() {
1222 use std::cmp::Ordering::*;
1223 let mut dag = IncrementalTopo::new();
1224
1225 let cat = dag.add_node();
1226 let mouse = dag.add_node();
1227 let dog = dag.add_node();
1228 let human = dag.add_node();
1229 let horse = dag.add_node();
1230
1231 assert!(dag.add_dependency(human, cat).unwrap());
1232 assert!(dag.add_dependency(human, dog).unwrap());
1233 assert!(dag.add_dependency(dog, cat).unwrap());
1234 assert!(dag.add_dependency(cat, mouse).unwrap());
1235
1236 assert_eq!(dag.topo_cmp(human, mouse), Less);
1237 assert_eq!(dag.topo_cmp(cat, dog), Greater);
1238 assert_eq!(dag.topo_cmp(cat, horse), Less);
1239 }
1240}