diff --git a/src/algo/dfs.rs b/src/algo/dfs.rs index d3ff6d0..106d3e0 100644 --- a/src/algo/dfs.rs +++ b/src/algo/dfs.rs @@ -1,4 +1,4 @@ -use crate::core::{property::VertexIn, Graph}; +use crate::core::{property::VertexIn, Ensure, Graph, GraphDeref}; use std::borrow::Borrow; /// Performs [depth-first traversal](https://mathworld.wolfram.com/Depth-FirstTraversal.html) @@ -90,9 +90,9 @@ use std::borrow::Borrow; /// /// [`next`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next /// [`get_vertex`]: ../core/property/trait.HasVertex.html#method.get_vertex -pub struct Dfs<'a, G, F> +pub struct Dfs where - G: 'a + Graph, + G: Ensure + GraphDeref, { /// A reference to the graph being traversed. /// @@ -100,49 +100,61 @@ where /// this reference between calls to /// [`next`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next) /// is undefined behaviour. - pub graph: &'a G, + pub graph: G, /// A custom payload, available to the function called upon a vertex exit. /// See [`new`](#method.new). pub payload: F, - visited: Vec, + visited: Vec<::Vertex>, /// The vertex on the stack, and whether on_exit should be called upon /// popping. - stack: Vec<(G::Vertex, bool)>, + stack: Vec<(::Vertex, bool)>, /// Function to call when visiting a vertex - on_visit: fn(&mut Self, G::Vertex), + on_visit: fn(&G::Graph, ::Vertex, &mut F), /// Function to call when exiting a vertex. /// /// Provides a reference to the graph, the vertex that is exiting, /// and a mutable reference to the payload given to the Dfs. - on_exit: fn(&G, G::Vertex, &mut F), + on_exit: fn(&G::Graph, ::Vertex, &mut F), /// Function to call when exploring an edge. /// /// When a vertex is being visited, this function is called for /// every outgoing edge, regardless of whether the sinked vertex /// (second vertex argument) has already been visited. - on_explore: fn(&mut Self, G::Vertex, G::Vertex, &G::EdgeWeight), + on_explore: fn( + &G::Graph, + ::Vertex, + ::Vertex, + &::EdgeWeight, + &mut F, + ), } -impl<'a, G, F> Dfs<'a, G, F> +impl Dfs where - G: 'a + Graph, + G: Ensure + GraphDeref, { pub fn new( - g: &'a G, - on_visit: fn(&mut Self, G::Vertex), - on_exit: fn(&G, G::Vertex, &mut F), - on_explore: fn(&mut Self, G::Vertex, G::Vertex, &G::EdgeWeight), + g: G, + on_visit: fn(&G::Graph, ::Vertex, &mut F), + on_exit: fn(&G::Graph, ::Vertex, &mut F), + on_explore: fn( + &G::Graph, + ::Vertex, + ::Vertex, + &::EdgeWeight, + &mut F, + ), payload: F, ) -> Self where - G: VertexIn<1>, + G::Graph: VertexIn<1>, { - let v = g.vertex_at::<0>(); + let v = g.graph().vertex_at::<0>(); let mut result = Self { graph: g, visited: Vec::new(), @@ -157,16 +169,22 @@ where result } - fn visit(&mut self, to_return: G::Vertex) + fn visit(&mut self, to_return: ::Vertex) { - (self.on_visit)(self, to_return); + (self.on_visit)(self.graph.graph(), to_return, &mut self.payload); // Mark visited self.visited.push(to_return); // Explore children - for (child, weight) in self.graph.edges_sourced_in(to_return.clone()) + for (child, weight) in self.graph.graph().edges_sourced_in(to_return.clone()) { - (self.on_explore)(self, to_return, child, weight.borrow()); + (self.on_explore)( + self.graph.graph(), + to_return, + child, + weight.borrow(), + &mut self.payload, + ); if !self.visited(child.clone()) { // Push to stack without exit mark @@ -175,7 +193,7 @@ where } } - pub fn visited(&self, v: G::Vertex) -> bool + pub fn visited(&self, v: ::Vertex) -> bool { self.visited.contains(&v) } @@ -185,7 +203,7 @@ where /// /// If there was nothing to pop and call `on_exit` on, return false, /// otherwise returns true. - pub fn advance_next_exit(&mut self) -> Option + pub fn advance_next_exit(&mut self) -> Option<::Vertex> { while let Some(last) = self.stack.last() { @@ -196,7 +214,7 @@ where // If its exit marked, call the closure on it. if last.1 { - (self.on_exit)(self.graph, last.0, &mut self.payload); + (self.on_exit)(self.graph.graph(), last.0, &mut self.payload); return Some(last.0); } } @@ -208,7 +226,7 @@ where None } - pub fn continue_from(&mut self, v: G::Vertex) -> bool + pub fn continue_from(&mut self, v: ::Vertex) -> bool { if !self.visited(v.clone()) { @@ -221,16 +239,25 @@ where } } - pub fn do_nothing_on_visit(_: &mut Self, _: G::Vertex) {} + pub fn do_nothing_on_visit(_: &G::Graph, _: ::Vertex, _: &mut F) {} - pub fn do_nothing_on_exit(_: &G, _: G::Vertex, _: &mut F) {} + pub fn do_nothing_on_exit(_: &G::Graph, _: ::Vertex, _: &mut F) {} - pub fn do_nothing_on_explore(_: &mut Self, _: G::Vertex, _: G::Vertex, _: &G::EdgeWeight) {} + pub fn do_nothing_on_explore( + _: &G::Graph, + _: ::Vertex, + _: ::Vertex, + _: &::EdgeWeight, + _: &mut F, + ) + { + } } -impl<'a, G> Dfs<'a, G, ()> +impl Dfs where - G: 'a + VertexIn<1>, + G: Ensure + GraphDeref, + G::Graph: VertexIn<1>, { /// Constructs a new `Dfs` to traverse the specified graph. /// @@ -247,7 +274,7 @@ where /// /// [`next`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next /// [`get_vertex`]: ../core/property/trait.HasVertex.html#method.get_vertex - pub fn new_simple(g: &'a G) -> Self + pub fn new_simple(g: G) -> Self { Self::new( g, @@ -259,11 +286,11 @@ where } } -impl<'a, G, F> Iterator for Dfs<'a, G, F> +impl<'a, G, F> Iterator for Dfs where - G: 'a + Graph, + G: 'a + Ensure + GraphDeref, { - type Item = G::Vertex; + type Item = ::Vertex; fn next(&mut self) -> Option { diff --git a/src/algo/tarjan_scc.rs b/src/algo/tarjan_scc.rs index ccc199a..80ab68f 100644 --- a/src/algo/tarjan_scc.rs +++ b/src/algo/tarjan_scc.rs @@ -11,7 +11,7 @@ //! reachable from this vertex. The lowlink value should be seen as the lowest //! index reachable. //! -//! When a vertex is pushed in the stack, it is assigned an index. It is also +//! When a vertex is pushed on the stack, it is assigned an index. It is also //! given a lowlink value equal to its index (since we know it can at least //! reach itself). When you are finished visiting the children of a vertex, //! check all vertices that are reachable from the current one. If they are on @@ -51,9 +51,9 @@ use crate::{ algo::Dfs, core::{ - property::{ConnectedGraph, VertexIn}, + property::{ConnectedGraph, HasVertex, VertexInGraph}, proxy::SubgraphProxy, - Directed, Graph, Guard, + Directed, Ensure, Graph, GraphDeref, Guard, }, }; use std::cmp::min; @@ -105,7 +105,7 @@ use std::cmp::min; /// // Connect first SCC to second /// graph.add_edge(&v0,&v2).unwrap(); /// -/// let graph = VertexInGraph::ensure(graph, [v0]).unwrap(); +/// let graph = VertexInGraph::<_>::ensure(graph, [v0]).unwrap(); /// /// // Initialize algorithm /// let mut tarj = TarjanScc::new(&graph); @@ -146,20 +146,22 @@ use std::cmp::min; /// [`subgraphs`]: ../core/property/trait.Subgraph.html /// [`Subgraph`]: ../core/property/trait.Subgraph.html /// [`Subgraph::reaches`]: ../core/property/trait.Subgraph.html#method.reaches -pub struct TarjanScc<'a, G> +pub struct TarjanScc where - G: 'a + Graph, + G: Ensure + GraphDeref, + G::Graph: Graph, { - dfs: Dfs<'a, G, Vec<(G::Vertex, usize)>>, + dfs: Dfs, Vec<(::Vertex, usize)>>, - /// We use this to keep track of which vertices we have check for + /// We use this to keep track of which vertices we have to check for /// whether they have been visited. - unchecked: Box>, + unchecked: std::vec::IntoIter<::Vertex>, } -impl<'a, G> TarjanScc<'a, G> +impl TarjanScc where - G: 'a + Graph + VertexIn<1>, + G: Ensure + GraphDeref, + G::Graph: Graph + HasVertex, { /// Constructs a new `TarjanScc` to find the [strongly connected components](https://mathworld.wolfram.com/StronglyConnectedComponent.html) /// of the specified graph. @@ -177,7 +179,7 @@ where /// [`next`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next /// [`get_vertex`]: /// ../core/property/trait.HasVertex.html#tymethod.get_vertex - pub fn new(graph: &'a G) -> Self + pub fn new(graph: G) -> Self { /// Implements part of Tarjan's algorithm, namely what happens when we /// are finished visiting a vertex. @@ -207,36 +209,37 @@ where } } + let v = graph.graph().any_vertex(); + let graph = VertexInGraph::ensure_unchecked(graph, [v]); + let vs = graph.all_vertices().collect::>(); + // Push the start vertex on the stack with low-link = 0 let dfs = Dfs::new( graph, - Dfs::do_nothing_on_visit, + Dfs::, _>::do_nothing_on_visit, on_exit, - Dfs::do_nothing_on_explore, - vec![(graph.vertex_at::<0>(), 0)], + Dfs::, _>::do_nothing_on_explore, + vec![(v, 0)], ); Self { dfs, - unchecked: Box::new(graph.all_vertices()), + unchecked: vs.into_iter(), } } } -impl<'a, G> Iterator for TarjanScc<'a, G> -where - G: 'a + Graph, -{ - type Item = ConnectedGraph>; - - fn next(&mut self) -> Option +macro_rules! next_scc_impl { { + $self_tt:ident, + $($return_code:tt)* + } => { // Repeat until either an SCC is found or all vertices have been visited. loop { // For each vertex we are finished visiting, check if its the root of a SCC. - while let Some(v) = self.dfs.advance_next_exit() + while let Some(v) = $self_tt.dfs.advance_next_exit() { - let stack = &mut self.dfs.payload; + let stack = &mut $self_tt.dfs.payload; // Find the index of the vertex let index = stack.iter().position(|(v2, _)| *v2 == v).unwrap(); @@ -245,7 +248,7 @@ where { // Vertex is root of SCC, pop stack for all before it - let mut scc = SubgraphProxy::new(self.dfs.graph); + let mut scc = SubgraphProxy::new($self_tt.dfs.graph.0 $($return_code)*); while stack.len() > index { scc.expand(stack.pop().unwrap().0).unwrap(); @@ -261,16 +264,16 @@ where } // No SCCs found, let the Dfs run once - if let Some(v) = self.dfs.next() + if let Some(v) = $self_tt.dfs.next() { // First push vertex onto stack, with lowlink value equal to its index - let stack = &mut self.dfs.payload; + let stack = &mut $self_tt.dfs.payload; stack.push((v.clone(), stack.len())); } else { - let dfs = &mut self.dfs; - if !self.unchecked.any(|v| dfs.continue_from(v)) + let dfs = &mut $self_tt.dfs; + if !$self_tt.unchecked.any(|v| dfs.continue_from(v)) { return None; } @@ -278,3 +281,34 @@ where } } } + +impl TarjanScc +where + G: Ensure + GraphDeref, + G::Graph: Graph, +{ + /// Returns the next strongly connected component `TarjanScc` has found, if + /// any. + /// + /// This is similar to `next`, however can be used when `TarjanScc` receives + /// a non-copy ensure. + /// + /// + pub fn next_scc(&mut self) -> Option>> + { + next_scc_impl!(self, .graph()) + } +} + +impl Iterator for TarjanScc +where + G: Ensure + GraphDeref + Copy, + G::Graph: Graph, +{ + type Item = ConnectedGraph>; + + fn next(&mut self) -> Option + { + next_scc_impl!(self,) + } +} diff --git a/src/core/property/acyclic.rs b/src/core/property/acyclic.rs index 112f899..cc8064a 100644 --- a/src/core/property/acyclic.rs +++ b/src/core/property/acyclic.rs @@ -22,45 +22,45 @@ impl Ensure for AcyclicGraph fn can_ensure(c: &Self::Ensured, _: &()) -> bool { - fn on_visit(dfs: &mut Dfs, &mut bool)>, v: G::Vertex) + fn on_visit(_: &G, v: G::Vertex, payload: &mut (Vec, &mut bool)) { - dfs.payload.0.push(v); + payload.0.push(v); } fn on_exit(_: &G, _: G::Vertex, (stack, _): &mut (Vec, &mut bool)) { stack.pop(); } fn on_explore( - dfs: &mut Dfs, &mut bool)>, + graph: &G, source: G::Vertex, sink: G::Vertex, _: &G::EdgeWeight, + payload: &mut (Vec, &mut bool), ) { if G::Directedness::directed() { - *dfs.payload.1 |= dfs.payload.0.contains(&sink); + *payload.1 |= payload.0.contains(&sink); } else { // Check whether the second to last element is the same as the sink // (the last element is the same as source, since on_visit is called // before exploration) - if dfs.payload.0.len() >= 2 - && dfs - .payload + if payload.0.len() >= 2 + && payload .0 - .get(dfs.payload.0.len() - 2) + .get(payload.0.len() - 2) .map_or(false, |&v| v == sink) { // This is an edge to the direct predecessor // Therefore, only counts as a cycle if there // are multiple edge between these two - *dfs.payload.1 |= dfs.graph.edges_between(source, sink).nth(1).is_some(); + *payload.1 |= graph.edges_between(source, sink).nth(1).is_some(); } else { - *dfs.payload.1 |= dfs.visited(sink); + *payload.1 |= payload.0.contains(&sink); } } } diff --git a/src/core/property/has_vertex.rs b/src/core/property/has_vertex.rs index d4747a9..0c989ea 100644 --- a/src/core/property/has_vertex.rs +++ b/src/core/property/has_vertex.rs @@ -115,7 +115,7 @@ pub trait VertexIn: HasVertex } /// Ensures the underlying graph has at least 1 vertex. -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct HasVertexGraph(C); impl HasVertexGraph @@ -177,7 +177,7 @@ impl_ensurer! { /// The designated vertices cannot be removed from the graph. #[derive(Clone)] pub struct VertexInGraph( - C, + pub C, [::Vertex; V], ); diff --git a/tests/algo/dfs.rs b/tests/algo/dfs.rs index d176b16..51ba4d0 100644 --- a/tests/algo/dfs.rs +++ b/tests/algo/dfs.rs @@ -58,9 +58,9 @@ mod __ Dfs::new( &mock, - Dfs::do_nothing_on_visit, + Dfs::, _>::do_nothing_on_visit, on_exit, - Dfs::do_nothing_on_explore, + Dfs::, _>::do_nothing_on_explore, (&stack, &mut success), ) .for_each(|v| {