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}