// Copyright 2016 oauth-client-rs Developers // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! Performs topological sorting. #![warn(bad_style)] #![warn(missing_copy_implementations)] #![warn(missing_debug_implementations)] #![warn(missing_docs)] #![warn(trivial_casts)] #![warn(trivial_numeric_casts)] #![warn(unused)] #![warn(unused_extern_crates)] #![warn(unused_import_braces)] #![warn(unused_qualifications)] #![warn(unused_results)] #![cfg_attr(feature = "cargo-clippy", warn(if_not_else))] #![cfg_attr(feature = "cargo-clippy", warn(invalid_upcast_comparisons))] #![cfg_attr(feature = "cargo-clippy", warn(items_after_statements))] #![cfg_attr(feature = "cargo-clippy", warn(mut_mut))] #![cfg_attr(feature = "cargo-clippy", warn(never_loop))] #![cfg_attr(feature = "cargo-clippy", warn(nonminimal_bool))] #![cfg_attr(feature = "cargo-clippy", warn(option_map_unwrap_or))] #![cfg_attr(feature = "cargo-clippy", warn(option_map_unwrap_or_else))] #![cfg_attr(feature = "cargo-clippy", warn(option_unwrap_used))] #![cfg_attr(feature = "cargo-clippy", warn(result_unwrap_used))] #![cfg_attr(feature = "cargo-clippy", warn(used_underscore_binding))] use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; use std::collections::hash_map::Entry; use std::fmt; use std::hash::Hash; use std::iter::FromIterator; #[derive(Clone)] struct Dependency { num_prec: usize, succ: HashSet, } impl Dependency { fn new() -> Dependency { Dependency { num_prec: 0, succ: HashSet::new(), } } } /// Performs topological sorting. #[derive(Clone)] pub struct TopologicalSort { top: HashMap>, } impl TopologicalSort { /// Creates new empty `TopologicalSort`. /// /// ```rust /// # extern crate topological_sort; /// # fn main() { /// use topological_sort::TopologicalSort; /// let mut ts = TopologicalSort::<&str>::new(); /// ts.add_dependency("hello_world.o", "hello_world"); /// ts.add_dependency("hello_world.c", "hello_world"); /// ts.add_dependency("stdio.h", "hello_world.o"); /// ts.add_dependency("glibc.so", "hello_world"); /// assert_eq!(vec!["glibc.so", "hello_world.c", "stdio.h"], /// { let mut v = ts.pop_all(); v.sort(); v }); /// assert_eq!(vec!["hello_world.o"], /// { let mut v = ts.pop_all(); v.sort(); v }); /// assert_eq!(vec!["hello_world"], /// { let mut v = ts.pop_all(); v.sort(); v }); /// assert!(ts.pop_all().is_empty()); /// # } /// ``` #[inline] pub fn new() -> TopologicalSort { TopologicalSort { top: HashMap::new(), } } /// Returns the number of elements in the `TopologicalSort`. #[inline] pub fn len(&self) -> usize { self.top.len() } /// Returns true if the `TopologicalSort` contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.top.is_empty() } /// Registers the two elements' dependency. /// /// # Arguments /// /// * `prec` - The element appears before `succ`. `prec` is depended on by `succ`. /// * `succ` - The element appears after `prec`. `succ` depends on `prec`. pub fn add_dependency(&mut self, prec: P, succ: S) where P: Into, S: Into, { self.add_dependency_impl(prec.into(), succ.into()) } fn add_dependency_impl(&mut self, prec: T, succ: T) { match self.top.entry(prec) { Entry::Vacant(e) => { let mut dep = Dependency::new(); let _ = dep.succ.insert(succ.clone()); let _ = e.insert(dep); } Entry::Occupied(e) => { if !e.into_mut().succ.insert(succ.clone()) { // Already registered return; } } } match self.top.entry(succ) { Entry::Vacant(e) => { let mut dep = Dependency::new(); dep.num_prec += 1; let _ = e.insert(dep); } Entry::Occupied(e) => { e.into_mut().num_prec += 1; } } } /// Registers a dependency link. pub fn add_link(&mut self, link: DependencyLink) { self.add_dependency(link.prec, link.succ) } /// Inserts an element, without adding any dependencies from or to it. /// /// If the `TopologicalSort` did not have this element present, `true` is returned. /// /// If the `TopologicalSort` already had this element present, `false` is returned. pub fn insert(&mut self, elt: U) -> bool where U: Into, { match self.top.entry(elt.into()) { Entry::Vacant(e) => { let dep = Dependency::new(); let _ = e.insert(dep); true } Entry::Occupied(_) => false, } } /// Removes the item that is not depended on by any other items and returns it, or `None` if /// there is no such item. /// /// If `pop` returns `None` and `len` is not 0, there is cyclic dependencies. pub fn pop(&mut self) -> Option { self.peek().map(T::clone).map(|key| { let _ = self.remove(&key); key }) } /// Removes all items that are not depended on by any other items and returns it, or empty /// vector if there are no such items. /// /// If `pop_all` returns an empty vector and `len` is not 0, there is cyclic dependencies. pub fn pop_all(&mut self) -> Vec { let keys = self.top .iter() .filter(|&(_, v)| v.num_prec == 0) .map(|(k, _)| k.clone()) .collect::>(); for k in &keys { let _ = self.remove(k); } keys } /// Return a reference to the first item that does not depend on any other items, or `None` if /// there is no such item. pub fn peek(&self) -> Option<&T> { self.top .iter() .filter(|&(_, v)| v.num_prec == 0) .map(|(k, _)| k) .next() } /// Return a vector of references to all items that do not depend on any other items, or an /// empty vector if there are no such items. pub fn peek_all(&self) -> Vec<&T> { self.top .iter() .filter(|&(_, v)| v.num_prec == 0) .map(|(k, _)| k) .collect::>() } fn remove(&mut self, prec: &T) -> Option> { let result = self.top.remove(prec); if let Some(ref p) = result { for s in &p.succ { if let Some(y) = self.top.get_mut(s) { y.num_prec -= 1; } } } result } } impl FromIterator for TopologicalSort { fn from_iter>(iter: I) -> TopologicalSort { let mut top = TopologicalSort::new(); let mut seen = Vec::::default(); for item in iter { let _ = top.insert(item.clone()); for seen_item in seen.iter().cloned() { match seen_item.partial_cmp(&item) { Some(Ordering::Less) => { top.add_dependency(seen_item, item.clone()); } Some(Ordering::Greater) => { top.add_dependency(item.clone(), seen_item); } _ => (), } } seen.push(item); } top } } /// A link between two items in a sort. #[derive(Copy, Clone, Debug)] pub struct DependencyLink { /// The element which is depened upon by `succ`. pub prec: T, /// The element which depends on `prec`. pub succ: T, } impl From<(T, T)> for DependencyLink { fn from(tuple: (T, T)) -> Self { DependencyLink { succ: tuple.0, prec: tuple.1, } } } impl FromIterator> for TopologicalSort { fn from_iter>>(iter: I) -> TopologicalSort { let mut top = TopologicalSort::new(); for link in iter { top.add_link(link); } top } } impl Iterator for TopologicalSort { type Item = T; fn next(&mut self) -> Option { self.pop() } } impl fmt::Debug for Dependency { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "prec={}, succ={:?}", self.num_prec, self.succ) } } impl fmt::Debug for TopologicalSort { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.top) } } #[cfg(test)] mod test { use super::TopologicalSort; use std::iter::FromIterator; #[test] fn from_iter() { let t = vec![4, 3, 3, 5, 7, 6, 8]; let mut ts = TopologicalSort::::from_iter(t); assert_eq!(Some(3), ts.next()); assert_eq!(Some(4), ts.next()); assert_eq!(Some(5), ts.next()); assert_eq!(Some(6), ts.next()); assert_eq!(Some(7), ts.next()); assert_eq!(Some(8), ts.next()); assert_eq!(None, ts.next()); } #[test] fn iter() { let mut ts = TopologicalSort::::new(); ts.add_dependency(1, 2); ts.add_dependency(2, 3); ts.add_dependency(3, 4); assert_eq!(Some(1), ts.next()); assert_eq!(Some(2), ts.next()); assert_eq!(Some(3), ts.next()); assert_eq!(Some(4), ts.next()); assert_eq!(None, ts.next()); } #[test] fn pop_all() { fn check(result: &[i32], ts: &mut TopologicalSort) { let l = ts.len(); let mut v = ts.pop_all(); v.sort(); assert_eq!(result, &v[..]); assert_eq!(l - result.len(), ts.len()); } let mut ts = TopologicalSort::new(); ts.add_dependency(7, 11); assert_eq!(2, ts.len()); ts.add_dependency(7, 8); assert_eq!(3, ts.len()); ts.add_dependency(5, 11); assert_eq!(4, ts.len()); ts.add_dependency(3, 8); assert_eq!(5, ts.len()); ts.add_dependency(3, 10); assert_eq!(6, ts.len()); ts.add_dependency(11, 2); assert_eq!(7, ts.len()); ts.add_dependency(11, 9); assert_eq!(8, ts.len()); ts.add_dependency(11, 10); assert_eq!(8, ts.len()); ts.add_dependency(8, 9); assert_eq!(8, ts.len()); check(&[3, 5, 7], &mut ts); check(&[8, 11], &mut ts); check(&[2, 9, 10], &mut ts); check(&[], &mut ts); } #[test] fn cyclic_deadlock() { let mut ts = TopologicalSort::new(); ts.add_dependency("stone", "sharp"); ts.add_dependency("bucket", "hole"); ts.add_dependency("hole", "straw"); ts.add_dependency("straw", "axe"); ts.add_dependency("axe", "sharp"); ts.add_dependency("sharp", "water"); ts.add_dependency("water", "bucket"); assert_eq!(ts.pop(), Some("stone")); assert!(ts.pop().is_none()); println!("{:?}", ts); } }