Skip to main content

topological_sort/
lib.rs

1//! A data structure for topological sorting.
2//!
3//! # Examples
4//!
5//! ## Modeling Makefile-style dependencies
6//!
7//! This example reproduces a small `Makefile`. Each call to [`TopologicalSort::pop_batch`]
8//! returns the next batch of files that can be built in parallel.
9//!
10//! ```Makefile
11//! hello_world: hello_world.o libhello.so
12//!         gcc -o hello_world hello_world.o -lhello
13//!
14//! hello_world.o: hello_world.c hello.h
15//!         gcc -c -o hello_world.o hello_world.c
16//! ```
17//!
18//! ```rust
19//! use topological_sort::TopologicalSort;
20//!
21//! let mut ts = TopologicalSort::<&str>::new();
22//!
23//! ts.add_dependency("hello_world.o", "hello_world");
24//! ts.add_dependency("libhello.so", "hello_world");
25//! ts.add_dependency("hello_world.c", "hello_world.o");
26//! ts.add_dependency("hello.h", "hello_world.o");
27//!
28//! // Source inputs with no remaining dependencies are ready first.
29//! let mut first_group = ts.pop_batch::<Vec<_>>();
30//! first_group.sort();
31//! assert_eq!(first_group, ["hello.h", "hello_world.c", "libhello.so"]);
32//!
33//! // Building those inputs makes the object file ready.
34//! let mut second_group = ts.pop_batch::<Vec<_>>();
35//! second_group.sort();
36//! assert_eq!(second_group, ["hello_world.o"]);
37//!
38//! // Finally, the executable itself becomes ready.
39//! let mut third_group = ts.pop_batch::<Vec<_>>();
40//! third_group.sort();
41//! assert_eq!(third_group, ["hello_world"]);
42//!
43//! assert!(ts.pop_batch::<Vec<_>>().is_empty());
44//! ```
45//!
46//! ## Detecting circular dependencies
47//!
48//! This example consumes a sort by repeatedly popping ready items. If any items remain afterward,
49//! the remaining subgraph contains a cycle.
50//!
51//! ```rust
52//! use topological_sort::TopologicalSort;
53//!
54//! fn has_circular_dependency(mut ts: TopologicalSort<&str>) -> bool {
55//!     // Remove every item that can be processed.
56//!     ts.pop_iter().for_each(drop);
57//!     // Any remaining items must be blocked by a cycle.
58//!     !ts.is_empty()
59//! }
60//!
61//! let mut ts1 = TopologicalSort::<&str>::new();
62//! ts1.add_dependency("scissors", "rock");
63//! ts1.add_dependency("paper", "scissors");
64//! ts1.add_dependency("rock", "paper");
65//!
66//! let mut ts2 = TopologicalSort::<&str>::new();
67//! ts2.add_dependency("grass", "zebra");
68//! ts2.add_dependency("zebra", "lion");
69//!
70//! assert!(has_circular_dependency(ts1));
71//! assert!(!has_circular_dependency(ts2));
72//! ```
73//!
74//! ## Processing items one at a time
75//!
76//! This example repeatedly calls [`TopologicalSort::pop`] to process items as soon as each next
77//! item becomes ready.
78//!
79//! ```rust
80//! use topological_sort::TopologicalSort;
81//!
82//! # fn process(_item: &str) {}
83//! let mut ts = TopologicalSort::<&str>::new();
84//! ts.add_dependency("parse", "analyze");
85//! ts.add_dependency("analyze", "compile");
86//!
87//! # let mut processed = Vec::new();
88//! while let Some(item) = ts.pop() {
89//!     process(item);
90//! #   processed.push(item);
91//! }
92//!
93//! # assert_eq!(processed, ["parse", "analyze", "compile"]);
94//! ```
95//!
96//! ## Using `TopologicalSort` in a task scheduler
97//!
98//! [`TopologicalSort`] can serve as the dependency tracker inside a task
99//! scheduler. [`TopologicalSort::peek_batch`] returns all tasks whose
100//! prerequisites are satisfied, and [`TopologicalSort::remove`] marks a
101//! completed task as done, which may make more tasks ready.
102//!
103//! Because [`TopologicalSort::peek_batch`] does not remove tasks, a scheduler
104//! also needs to track which ready tasks are already running so it does not
105//! start them twice.
106//!
107//! ```rust
108//! use std::collections::HashSet;
109//!
110//! use topological_sort::TopologicalSort;
111//!
112//! type Task = String;
113//!
114//! # fn start_tasks(_tasks: &[Task]) {}
115//! # fn wait_for_task_completion(_running_tasks: &HashSet<Task>) -> Task { todo!() }
116//! fn run_scheduler(tasks: TopologicalSort<Task>) {
117//!     let mut remaining_tasks = tasks;
118//!     let mut running_tasks = HashSet::<Task>::new();
119//!
120//!     while !remaining_tasks.is_empty() {
121//!         // `peek_batch()` returns every task whose prerequisites are
122//!         // satisfied, including tasks that are already running.
123//!         let runnable_or_running_tasks = remaining_tasks.peek_batch();
124//!
125//!         let runnable_tasks = runnable_or_running_tasks
126//!             .filter(|task| !running_tasks.contains(*task))
127//!             .cloned()
128//!             .collect::<Vec<Task>>();
129//!
130//!         if !runnable_tasks.is_empty() {
131//!             start_tasks(&runnable_tasks);
132//!             running_tasks.extend(runnable_tasks);
133//!         }
134//!
135//!         // Wait for one running task to finish, then mark it complete.
136//!         let completed_task = wait_for_task_completion(&running_tasks);
137//!         remaining_tasks.remove(&completed_task);
138//!         running_tasks.remove(&completed_task);
139//!     }
140//! }
141//! ```
142
143#![cfg_attr(docsrs, feature(doc_cfg))]
144
145use std::{
146    borrow::Borrow,
147    collections::{HashMap, HashSet, hash_map},
148    fmt,
149    hash::Hash,
150    iter::{FromIterator, FusedIterator},
151};
152
153#[derive(Clone, Debug)]
154struct Node<T> {
155    num_prec: usize,
156    succ: HashSet<T>,
157}
158
159impl<T> Node<T>
160where
161    T: Eq + Hash,
162{
163    fn new() -> Node<T> {
164        Node {
165            num_prec: 0,
166            succ: HashSet::new(),
167        }
168    }
169
170    fn is_ready(&self) -> bool {
171        self.num_prec == 0
172    }
173}
174
175/// A data structure for topological sorting.
176///
177/// See the [crate-level documentation](crate) for examples.
178#[derive(Clone)]
179pub struct TopologicalSort<T> {
180    nodes: HashMap<T, Node<T>>,
181}
182
183impl<T> Default for TopologicalSort<T> {
184    fn default() -> TopologicalSort<T> {
185        TopologicalSort {
186            nodes: HashMap::new(),
187        }
188    }
189}
190
191impl<T> fmt::Debug for TopologicalSort<T>
192where
193    T: fmt::Debug,
194{
195    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196        f.debug_map()
197            .entries(self.nodes.iter().map(|(k, dep)| (k, &dep.succ)))
198            .finish()
199    }
200}
201
202impl<T> TopologicalSort<T>
203where
204    T: Clone + Eq + Hash,
205{
206    /// Creates a new empty `TopologicalSort`.
207    ///
208    /// See the [crate-level documentation](crate) for examples.
209    #[inline]
210    #[must_use]
211    pub fn new() -> Self {
212        Self::default()
213    }
214
215    /// Returns the number of remaining items in the `TopologicalSort`.
216    ///
217    /// This counts all remaining items, including those that are not yet ready to pop.
218    #[inline]
219    #[must_use]
220    pub fn len(&self) -> usize {
221        self.nodes.len()
222    }
223
224    /// Returns `true` if the `TopologicalSort` contains no remaining items.
225    #[inline]
226    #[must_use]
227    pub fn is_empty(&self) -> bool {
228        self.nodes.is_empty()
229    }
230
231    /// Registers a dependency from `prec` to `succ`.
232    ///
233    /// This means that `succ` depends on `prec`, so `prec` must be popped or removed
234    /// before `succ` becomes ready.
235    ///
236    /// Returns `true` if this dependency link was newly added, or `false` if it was
237    /// already present.
238    ///
239    /// ```rust
240    /// use topological_sort::TopologicalSort;
241    ///
242    /// let mut ts = TopologicalSort::new();
243    /// assert!(ts.add_dependency("compile", "link"));
244    ///
245    /// assert_eq!(ts.pop(), Some("compile"));
246    /// assert_eq!(ts.pop(), Some("link"));
247    /// ```
248    pub fn add_dependency<P, S>(&mut self, prec: P, succ: S) -> bool
249    where
250        P: Into<T>,
251        S: Into<T>,
252    {
253        let prec = prec.into();
254        let succ = succ.into();
255
256        let prec_node = self.nodes.entry(prec).or_insert_with(Node::new);
257        if !prec_node.succ.insert(succ.clone()) {
258            // Already registered
259            return false;
260        }
261        self.nodes.entry(succ).or_insert_with(Node::new).num_prec += 1;
262        true
263    }
264
265    /// Registers a dependency link.
266    ///
267    /// This means that `link.succ` depends on `link.prec`, so `link.prec` must be
268    /// popped or removed before `link.succ` becomes ready.
269    ///
270    /// Returns `true` if this dependency link was newly added, or `false` if it was
271    /// already present.
272    ///
273    /// ```rust
274    /// use topological_sort::{DependencyLink, TopologicalSort};
275    ///
276    /// let mut ts = TopologicalSort::new();
277    /// assert!(ts.add_link(DependencyLink {
278    ///     prec: "compile",
279    ///     succ: "link"
280    /// }));
281    ///
282    /// assert_eq!(ts.pop(), Some("compile"));
283    /// assert_eq!(ts.pop(), Some("link"));
284    /// ```
285    pub fn add_link(&mut self, link: DependencyLink<T>) -> bool {
286        self.add_dependency(link.prec, link.succ)
287    }
288
289    /// Inserts an item, without adding any dependencies from or to it.
290    ///
291    /// Returns `true` if the item was not already present, or `false` otherwise.
292    ///
293    /// ```rust
294    /// use topological_sort::TopologicalSort;
295    ///
296    /// let mut ts = TopologicalSort::new();
297    /// assert!(ts.insert("standalone"));
298    /// assert!(!ts.insert("standalone"));
299    ///
300    /// assert_eq!(ts.pop(), Some("standalone"));
301    /// ```
302    pub fn insert<U>(&mut self, item: U) -> bool
303    where
304        U: Into<T>,
305    {
306        match self.nodes.entry(item.into()) {
307            hash_map::Entry::Vacant(e) => {
308                e.insert(Node::new());
309                true
310            }
311            hash_map::Entry::Occupied(_) => false,
312        }
313    }
314
315    /// Removes one item that does not depend on any other remaining item and returns it, or
316    /// `None` if there is no such item.
317    ///
318    /// If `pop` returns `None` and `len` is not 0, the remaining items contain a cycle.
319    ///
320    /// ```rust
321    /// use topological_sort::TopologicalSort;
322    ///
323    /// let mut ts = TopologicalSort::new();
324    /// ts.add_dependency("a", "b");
325    ///
326    /// assert_eq!(ts.pop(), Some("a"));
327    /// assert_eq!(ts.pop(), Some("b"));
328    /// assert_eq!(ts.pop(), None);
329    /// ```
330    pub fn pop(&mut self) -> Option<T> {
331        let (item, node) = self.nodes.extract_if(|_, node| node.is_ready()).next()?;
332        for succ in node.succ {
333            if let Some(succ_node) = self.nodes.get_mut(&succ) {
334                succ_node.num_prec -= 1;
335            }
336        }
337        Some(item)
338    }
339
340    /// Returns an iterator that repeatedly calls [`pop`](Self::pop).
341    ///
342    /// Each call to [`Iterator::next`] removes one item from the sort.
343    ///
344    /// The iterator ends when the sort becomes empty or when no item can be popped because the
345    /// remaining items contain a cycle.
346    ///
347    /// ```rust
348    /// use topological_sort::TopologicalSort;
349    ///
350    /// let mut ts = TopologicalSort::new();
351    /// ts.add_dependency(1, 2);
352    /// ts.add_dependency(2, 3);
353    ///
354    /// let mut it = ts.pop_iter();
355    /// assert_eq!(Some(1), it.next());
356    /// assert_eq!(Some(2), it.next());
357    /// drop(it);
358    ///
359    /// assert_eq!(Some(3), ts.pop());
360    /// ```
361    pub fn pop_iter(&mut self) -> PopIter<'_, T> {
362        PopIter { ts: self }
363    }
364
365    /// Removes all items that do not depend on any other remaining item at the time of the call
366    /// and returns them, or an empty vector if there are no such items.
367    ///
368    /// The returned items are in arbitrary order.
369    ///
370    /// If `pop_all` returns an empty vector and the sort is not empty, the remaining items contain a cycle.
371    #[deprecated(
372        since = "0.3.0",
373        note = "Use `pop_batch` instead, which returns an arbitrary collection containing all ready items."
374    )]
375    pub fn pop_all(&mut self) -> Vec<T> {
376        self.pop_batch()
377    }
378
379    /// Removes all items that do not depend on any other remaining item at the time of the call
380    /// and returns them, or an empty collection if there are no such items.
381    ///
382    /// Unlike [`pop_iter`](Self::pop_iter), this removes only the current batch of ready items. If
383    /// removing those items makes more items ready, they are returned by the next call to
384    /// `pop_batch`, not the current one.
385    ///
386    /// The returned items are in arbitrary order.
387    ///
388    /// If `pop_batch` returns an empty collection and the sort is not empty, the remaining items contain
389    /// a cycle.
390    ///
391    /// ```rust
392    /// use topological_sort::TopologicalSort;
393    ///
394    /// let mut ts = TopologicalSort::<i32>::new();
395    /// ts.add_dependency(1, 3);
396    /// ts.add_dependency(2, 3);
397    ///
398    /// let mut ready = ts.pop_batch::<Vec<_>>();
399    /// ready.sort_unstable();
400    /// assert_eq!(ready, [1, 2]);
401    ///
402    /// assert_eq!(ts.pop_batch::<Vec<_>>(), [3]);
403    /// ```
404    pub fn pop_batch<R>(&mut self) -> R
405    where
406        R: Default + Extend<T>,
407    {
408        let (items, nodes) = self
409            .nodes
410            .extract_if(|_, node| node.is_ready())
411            .collect::<(R, Vec<_>)>();
412        for node in nodes {
413            for succ in node.succ {
414                if let Some(succ_node) = self.nodes.get_mut(&succ) {
415                    succ_node.num_prec -= 1;
416                }
417            }
418        }
419        items
420    }
421
422    /// Returns a reference to one item that does not depend on any other remaining item, or
423    /// `None` if there is no such item.
424    ///
425    /// ```rust
426    /// use topological_sort::TopologicalSort;
427    ///
428    /// let mut ts = TopologicalSort::new();
429    /// ts.add_dependency("a", "b");
430    ///
431    /// assert_eq!(ts.peek(), Some(&"a"));
432    /// assert_eq!(ts.len(), 2);
433    /// assert_eq!(ts.pop(), Some("a"));
434    /// ```
435    #[must_use]
436    pub fn peek(&self) -> Option<&T> {
437        let (item, _) = self.nodes.iter().find(|&(_, node)| node.is_ready())?;
438        Some(item)
439    }
440
441    /// Returns a vector of references to all items that do not depend on any other remaining item at
442    /// the time of the call.
443    ///
444    /// The returned items are in arbitrary order.
445    #[deprecated(
446        since = "0.3.0",
447        note = "Use `peek_batch` instead, which returns an iterator over all ready items."
448    )]
449    #[must_use]
450    pub fn peek_all(&self) -> Vec<&T> {
451        self.peek_batch().collect()
452    }
453
454    /// Returns an iterator over references to all items that do not depend on any other remaining
455    /// item at the time of the call.
456    ///
457    /// The iterator yields no items if there are no such items. This inspects only the current
458    /// batch of ready items.
459    ///
460    /// The returned items are in arbitrary order.
461    ///
462    /// ```rust
463    /// use topological_sort::TopologicalSort;
464    ///
465    /// let mut ts = TopologicalSort::<i32>::new();
466    /// ts.add_dependency(1, 3);
467    /// ts.add_dependency(2, 3);
468    ///
469    /// let mut ready = ts.peek_batch().copied().collect::<Vec<_>>();
470    /// ready.sort_unstable();
471    /// assert_eq!(ready, [1, 2]);
472    /// assert_eq!(ts.len(), 3);
473    /// ```
474    pub fn peek_batch(&self) -> PeekBatch<'_, T> {
475        PeekBatch {
476            iter: self.nodes.iter(),
477        }
478    }
479
480    /// Returns an iterator visiting all remaining items in arbitrary order.
481    ///
482    /// This includes items that are not yet ready because they are blocked by unresolved
483    /// dependencies or cycles.
484    pub fn items(&self) -> Items<'_, T> {
485        Items {
486            iter: self.nodes.keys(),
487        }
488    }
489
490    /// Returns a consuming iterator visiting all remaining items in arbitrary order.
491    ///
492    /// This includes items that are not yet ready because they are blocked by unresolved
493    /// dependencies or cycles.
494    pub fn into_items(self) -> IntoItems<T> {
495        IntoItems {
496            iter: self.nodes.into_keys(),
497        }
498    }
499
500    /// Removes the specified item if it does not depend on any other remaining item and returns
501    /// it.
502    ///
503    /// Returns `None` if the item is not present or if it still depends on another remaining item.
504    ///
505    /// Removing the item also removes its outgoing dependency links, which may make some successor
506    /// items ready.
507    ///
508    /// ```rust
509    /// use topological_sort::TopologicalSort;
510    ///
511    /// let mut ts = TopologicalSort::new();
512    /// ts.add_dependency("a", "b");
513    ///
514    /// assert_eq!(ts.remove("b"), None);
515    /// assert_eq!(ts.remove("a"), Some("a"));
516    /// assert_eq!(ts.remove("b"), Some("b"));
517    /// ```
518    pub fn remove<Q>(&mut self, item: &Q) -> Option<T>
519    where
520        T: Borrow<Q>,
521        Q: Eq + Hash + ?Sized,
522    {
523        let node = self.nodes.get(item)?;
524        if !node.is_ready() {
525            return None;
526        }
527        let (item, node) = self.nodes.remove_entry(item)?;
528        for succ in node.succ {
529            if let Some(succ_node) = self.nodes.get_mut(succ.borrow()) {
530                succ_node.num_prec -= 1;
531            }
532        }
533        Some(item)
534    }
535}
536
537/// A dependency link between two items in a sort.
538#[derive(Copy, Clone, Debug)]
539pub struct DependencyLink<T> {
540    /// The item that `succ` depends on.
541    pub prec: T,
542    /// The item that depends on `prec`.
543    pub succ: T,
544}
545
546impl<T> FromIterator<DependencyLink<T>> for TopologicalSort<T>
547where
548    T: Clone + Eq + Hash,
549{
550    fn from_iter<I>(iter: I) -> TopologicalSort<T>
551    where
552        I: IntoIterator<Item = DependencyLink<T>>,
553    {
554        let mut ts = TopologicalSort::new();
555        ts.extend(iter);
556        ts
557    }
558}
559
560impl<T> Extend<DependencyLink<T>> for TopologicalSort<T>
561where
562    T: Clone + Eq + Hash,
563{
564    fn extend<I>(&mut self, iter: I)
565    where
566        I: IntoIterator<Item = DependencyLink<T>>,
567    {
568        for link in iter {
569            self.add_link(link);
570        }
571    }
572}
573
574/// An iterator over items popped from a [`TopologicalSort`].
575///
576/// This struct is created by [`TopologicalSort::pop_iter`].
577#[derive(Debug)]
578#[must_use = "iterators are lazy and do nothing unless consumed"]
579pub struct PopIter<'a, T> {
580    ts: &'a mut TopologicalSort<T>,
581}
582
583impl<T> Iterator for PopIter<'_, T>
584where
585    T: Clone + Eq + Hash,
586{
587    type Item = T;
588
589    fn next(&mut self) -> Option<Self::Item> {
590        self.ts.pop()
591    }
592
593    fn size_hint(&self) -> (usize, Option<usize>) {
594        (0, Some(self.ts.len()))
595    }
596}
597
598impl<T> FusedIterator for PopIter<'_, T> where T: Clone + Eq + Hash {}
599
600/// An iterator over all items in a [`TopologicalSort`] that do not depend on any other remaining
601/// item at the time of the call.
602///
603/// This struct is created by [`TopologicalSort::peek_batch`].
604#[derive(Debug)]
605#[must_use = "iterators are lazy and do nothing unless consumed"]
606pub struct PeekBatch<'a, T> {
607    iter: hash_map::Iter<'a, T, Node<T>>,
608}
609
610impl<'a, T> Iterator for PeekBatch<'a, T>
611where
612    T: Clone + Eq + Hash,
613{
614    type Item = &'a T;
615
616    fn next(&mut self) -> Option<Self::Item> {
617        let (item, _) = self.iter.find(|&(_, node)| node.is_ready())?;
618        Some(item)
619    }
620
621    fn size_hint(&self) -> (usize, Option<usize>) {
622        (0, Some(self.iter.len()))
623    }
624}
625
626impl<T> FusedIterator for PeekBatch<'_, T> where T: Clone + Eq + Hash {}
627
628/// An iterator over all remaining items in a [`TopologicalSort`].
629///
630/// This struct is created by [`TopologicalSort::items`].
631#[derive(Debug)]
632#[must_use = "iterators are lazy and do nothing unless consumed"]
633pub struct Items<'a, T> {
634    iter: hash_map::Keys<'a, T, Node<T>>,
635}
636
637impl<'a, T> Iterator for Items<'a, T>
638where
639    T: Clone + Eq + Hash,
640{
641    type Item = &'a T;
642
643    fn next(&mut self) -> Option<Self::Item> {
644        self.iter.next()
645    }
646
647    fn size_hint(&self) -> (usize, Option<usize>) {
648        self.iter.size_hint()
649    }
650}
651
652impl<T> ExactSizeIterator for Items<'_, T> where T: Clone + Eq + Hash {}
653
654impl<T> FusedIterator for Items<'_, T> where T: Clone + Eq + Hash {}
655
656/// An iterator that consumes a [`TopologicalSort`] and yields all remaining items.
657///
658/// This struct is created by [`TopologicalSort::into_items`].
659#[derive(Debug)]
660#[must_use = "iterators are lazy and do nothing unless consumed"]
661pub struct IntoItems<T> {
662    iter: hash_map::IntoKeys<T, Node<T>>,
663}
664
665impl<T> Iterator for IntoItems<T>
666where
667    T: Clone + Eq + Hash,
668{
669    type Item = T;
670
671    fn next(&mut self) -> Option<Self::Item> {
672        self.iter.next()
673    }
674
675    fn size_hint(&self) -> (usize, Option<usize>) {
676        self.iter.size_hint()
677    }
678}
679
680impl<T> ExactSizeIterator for IntoItems<T> where T: Clone + Eq + Hash {}
681impl<T> FusedIterator for IntoItems<T> where T: Clone + Eq + Hash {}
682
683#[cfg(test)]
684mod tests {
685    use quickcheck_macros::quickcheck;
686
687    use super::*;
688
689    #[test]
690    fn add_dependency_returns_true_if_new_dependency_link_created() {
691        let mut ts = TopologicalSort::<&str>::new();
692        assert!(ts.add_dependency("stone", "sharp"));
693        assert_eq!(ts.len(), 2);
694        assert!(!ts.add_dependency("stone", "sharp"));
695        assert_eq!(ts.len(), 2);
696        assert!(ts.add_dependency("sharp", "paper"));
697        assert_eq!(ts.len(), 3);
698        assert!(!ts.add_dependency("sharp", "paper"));
699        assert_eq!(ts.len(), 3);
700        assert!(ts.add_dependency("paper", "stone"));
701        assert_eq!(ts.len(), 3);
702        assert!(!ts.add_dependency("paper", "stone"));
703        assert_eq!(ts.len(), 3);
704    }
705
706    #[test]
707    fn add_link_returns_true_if_new_dependency_link_created() {
708        let mut ts = TopologicalSort::<&str>::new();
709        assert!(ts.add_link(DependencyLink {
710            prec: "stone",
711            succ: "sharp",
712        }));
713        assert!(!ts.add_link(DependencyLink {
714            prec: "stone",
715            succ: "sharp",
716        }));
717        assert_eq!(ts.len(), 2);
718    }
719
720    #[test]
721    fn pop_iter_iterates_all_items_in_topological_order() {
722        let mut ts = TopologicalSort::<i32>::new();
723        ts.add_dependency(1, 2);
724        ts.add_dependency(2, 3);
725        ts.add_dependency(3, 4);
726        ts.add_dependency(4, 5);
727        ts.add_dependency(5, 6);
728        let mut it = ts.pop_iter();
729        assert_eq!(Some(1), it.next());
730        assert_eq!(Some(2), it.next());
731        assert_eq!(Some(3), it.next());
732        assert_eq!(Some(4), it.next());
733        assert_eq!(Some(5), it.next());
734        assert_eq!(Some(6), it.next());
735        assert_eq!(None, it.next());
736        assert_eq!(None, it.next());
737    }
738
739    #[test]
740    fn pop_iter_stops_on_a_cycle() {
741        let mut ts = TopologicalSort::<i32>::new();
742        ts.add_dependency(1, 2);
743        ts.add_dependency(2, 3);
744        ts.add_dependency(3, 4);
745        ts.add_dependency(4, 5);
746        ts.add_dependency(5, 5);
747        ts.add_dependency(5, 6);
748        let mut it = ts.pop_iter();
749        assert_eq!(Some(1), it.next());
750        assert_eq!(Some(2), it.next());
751        assert_eq!(Some(3), it.next());
752        assert_eq!(Some(4), it.next());
753        assert_eq!(None, it.next());
754        assert_eq!(None, it.next());
755    }
756
757    #[test]
758    fn pop_batch_returns_all_currently_ready_items() {
759        fn check(result: &[i32], ts: &mut TopologicalSort<i32>) {
760            let l = ts.len();
761            let mut v = ts.pop_batch::<Vec<_>>();
762            v.sort_unstable();
763            assert_eq!(result, &v[..]);
764            assert_eq!(l - result.len(), ts.len());
765        }
766
767        let mut ts = TopologicalSort::new();
768        ts.add_dependency(7, 11);
769        assert_eq!(2, ts.len());
770        ts.add_dependency(7, 8);
771        assert_eq!(3, ts.len());
772        ts.add_dependency(5, 11);
773        assert_eq!(4, ts.len());
774        ts.add_dependency(3, 8);
775        assert_eq!(5, ts.len());
776        ts.add_dependency(3, 10);
777        assert_eq!(6, ts.len());
778        ts.add_dependency(11, 2);
779        assert_eq!(7, ts.len());
780        ts.add_dependency(11, 9);
781        assert_eq!(8, ts.len());
782        ts.add_dependency(11, 10);
783        assert_eq!(8, ts.len());
784        ts.add_dependency(8, 9);
785        assert_eq!(8, ts.len());
786
787        check(&[3, 5, 7], &mut ts);
788        check(&[8, 11], &mut ts);
789        check(&[2, 9, 10], &mut ts);
790        check(&[], &mut ts);
791    }
792
793    #[test]
794    fn self_dependency_blocks_the_remaining_element() {
795        let mut ts = TopologicalSort::<&str>::new();
796        ts.add_dependency("stone", "sharp");
797        ts.add_dependency("sharp", "sharp");
798        ts.add_dependency("sharp", "water");
799        assert_eq!(ts.len(), 3);
800        assert_eq!(ts.pop(), Some("stone"));
801        assert_eq!(ts.len(), 2);
802        assert_eq!(ts.pop(), None);
803    }
804
805    #[test]
806    fn pop_returns_none_when_remaining_elements_are_cyclic() {
807        let mut ts = TopologicalSort::new();
808        ts.add_dependency("stone", "sharp");
809
810        ts.add_dependency("bucket", "hole");
811        ts.add_dependency("hole", "straw");
812        ts.add_dependency("straw", "axe");
813        ts.add_dependency("axe", "sharp");
814        ts.add_dependency("sharp", "water");
815        ts.add_dependency("water", "bucket");
816        assert_eq!(ts.pop(), Some("stone"));
817        assert!(ts.pop().is_none());
818    }
819
820    #[test]
821    fn add_link_can_create_a_cycle_that_blocks_remaining_elements() {
822        let mut ts = TopologicalSort::<&str>::new();
823
824        ts.add_link(DependencyLink {
825            prec: "omelet",
826            succ: "egg",
827        });
828        ts.add_link(DependencyLink {
829            prec: "egg",
830            succ: "chicken",
831        });
832        ts.add_link(DependencyLink {
833            prec: "chicken",
834            succ: "egg",
835        });
836        assert_eq!(ts.len(), 3);
837        assert_eq!(ts.pop(), Some("omelet"));
838        assert_eq!(ts.pop(), None);
839    }
840
841    #[test]
842    fn remove_removes_item_only_if_exists_and_ready() {
843        let mut ts = TopologicalSort::<&str>::new();
844        ts.add_dependency("a", "b");
845        ts.add_dependency("b", "c");
846        ts.add_dependency("c", "d");
847
848        assert!(ts.remove("x").is_none());
849        assert!(ts.remove("c").is_none());
850        assert_eq!(ts.remove("a").unwrap(), "a");
851        assert!(ts.remove("c").is_none());
852        assert_eq!(ts.remove("b").unwrap(), "b");
853        assert_eq!(ts.remove("c").unwrap(), "c");
854    }
855
856    #[test]
857    fn items_and_into_items_iterate_all_remaining_items() {
858        let mut ts = TopologicalSort::<&str>::new();
859        ts.add_dependency("a", "b");
860        ts.add_dependency("b", "c");
861        ts.add_dependency("c", "d");
862
863        let mut items = ts.items().copied().collect::<Vec<_>>();
864        items.sort_unstable();
865        assert_eq!(items, ["a", "b", "c", "d"]);
866
867        let mut into_items = ts.into_items().collect::<Vec<_>>();
868        into_items.sort_unstable();
869        assert_eq!(into_items, ["a", "b", "c", "d"]);
870    }
871
872    #[quickcheck]
873    fn quickcheck_topological_sort_invariants(n: usize, edges: Vec<(usize, usize)>) {
874        use std::collections::{HashMap, HashSet};
875
876        let n = n.clamp(1, 1000);
877        let mut marked = vec![false; n];
878        let edges = edges
879            .into_iter()
880            .map(|(x, y)| (x % n, y % n))
881            .collect::<Vec<_>>();
882        let mut deps = HashMap::new();
883        let mut toposort = TopologicalSort::<usize>::new();
884
885        for i in 0..n {
886            deps.insert(i, HashSet::new());
887            assert!(toposort.insert(i));
888        }
889
890        for (op, inp) in edges.iter().map(|(x, y)| (y, x)) {
891            let inps = deps.get_mut(op).unwrap();
892            inps.insert(*inp);
893        }
894
895        let deps = deps;
896        for (inp, op) in edges {
897            toposort.add_dependency(inp, op);
898        }
899        while let Some(x) = toposort.pop() {
900            for dep in &deps[&x] {
901                assert!(marked[*dep]);
902            }
903            marked[x] = true;
904        }
905
906        if toposort.is_empty() {
907            assert!(marked.into_iter().all(|x| x));
908        } else {
909            let dep_fixed = {
910                let mut ret = (0..n)
911                    .map(|i| (i, HashSet::new()))
912                    .collect::<HashMap<_, _>>();
913                let mut new_to_add = deps;
914
915                while !new_to_add.is_empty() {
916                    for (k, v) in new_to_add.drain() {
917                        let inps = ret.get_mut(&k).unwrap();
918                        inps.extend(v.into_iter());
919                    }
920                    for (k, vs) in &ret {
921                        for k2 in vs {
922                            for v2 in &ret[k2] {
923                                if !vs.contains(v2) {
924                                    new_to_add
925                                        .entry(*k)
926                                        .or_insert_with(HashSet::new)
927                                        .insert(*v2);
928                                }
929                            }
930                        }
931                    }
932                }
933
934                ret
935            };
936
937            assert!(dep_fixed.into_iter().any(|(op, deps)| deps.contains(&op)));
938        }
939    }
940}