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}