Merge pull request #170 from bluss/filter-map-try-3

Add StableGraph::filter_map
diff --git a/src/graph_impl/stable_graph/mod.rs b/src/graph_impl/stable_graph/mod.rs
index c776f6c..9a1d1f6 100644
--- a/src/graph_impl/stable_graph/mod.rs
+++ b/src/graph_impl/stable_graph/mod.rs
@@ -104,6 +104,14 @@
     g: Graph<Option<N>, Option<E>, Ty, Ix>,
     node_count: usize,
     edge_count: usize,
+
+    // node and edge free lists (both work the same way)
+    //
+    // free_node, if not NodeIndex::end(), points to a node index
+    // that is vacant (after a deletion).  The next item in the list is kept in
+    // that Node's Node.next[0] field. For Node, it's a node index stored
+    // in an EdgeIndex location, and the _into_edge()/_into_node() methods
+    // convert.
     free_node: NodeIndex<Ix>,
     free_edge: EdgeIndex<Ix>,
 }
@@ -248,6 +256,15 @@
         index
     }
 
+    /// free_node: Which free list to update for the vacancy
+    fn add_vacant_node(&mut self, free_node: &mut NodeIndex<Ix>) {
+        let node_idx = self.g.add_node(None);
+        // link the free list
+        let node_slot = &mut self.g.nodes[node_idx.index()];
+        node_slot.next[0] = free_node._into_edge();
+        *free_node = node_idx;
+    }
+
     /// Remove `a` from the graph if it exists, and return its weight.
     /// If it doesn't exist in the graph, return `None`.
     ///
@@ -376,6 +393,20 @@
         edge_idx
     }
 
+    /// free_edge: Which free list to update for the vacancy
+    fn add_vacant_edge(&mut self, free_edge: &mut EdgeIndex<Ix>) {
+        let edge_idx = EdgeIndex::new(self.g.edges.len());
+        debug_assert!(edge_idx != EdgeIndex::end());
+        let mut edge = Edge {
+            weight: None,
+            node: [NodeIndex::end(); 2],
+            next: [EdgeIndex::end(); 2],
+        };
+        edge.next[0] = *free_edge;
+        *free_edge = edge_idx;
+        self.g.edges.push(edge);
+    }
+
     /// Add or update an edge from `a` to `b`.
     /// If the edge already exists, its weight is updated.
     ///
@@ -738,6 +769,61 @@
         }
     }
 
+    /// Create a new `StableGraph` by mapping nodes and edges.
+    /// A node or edge may be mapped to `None` to exclude it from
+    /// the resulting graph.
+    ///
+    /// Nodes are mapped first with the `node_map` closure, then
+    /// `edge_map` is called for the edges that have not had any endpoint
+    /// removed.
+    ///
+    /// The resulting graph has the structure of a subgraph of the original graph.
+    /// Nodes and edges that are not removed maintain their old node or edge
+    /// indices.
+    pub fn filter_map<'a, F, G, N2, E2>(&'a self, mut node_map: F, mut edge_map: G)
+        -> StableGraph<N2, E2, Ty, Ix>
+        where F: FnMut(NodeIndex<Ix>, &'a N) -> Option<N2>,
+              G: FnMut(EdgeIndex<Ix>, &'a E) -> Option<E2>,
+    {
+        let node_bound = self.node_bound();
+        let edge_bound = self.edge_bound();
+        let mut result_g = StableGraph::with_capacity(node_bound, edge_bound);
+        // use separate free lists so that
+        // add_node / add_edge below do not reuse the tombstones
+        let mut free_node = NodeIndex::end();
+        let mut free_edge = EdgeIndex::end();
+
+        // the stable graph keeps the node map itself
+
+        for (i, node) in enumerate(self.raw_nodes()) {
+            if i >= node_bound { break; }
+            if let Some(node_weight) = node.weight.as_ref() {
+                if let Some(new_weight) = node_map(NodeIndex::new(i), node_weight) {
+                    result_g.add_node(new_weight);
+                    continue;
+                }
+            }
+            result_g.add_vacant_node(&mut free_node);
+        }
+        for (i, edge) in enumerate(self.raw_edges()) {
+            if i >= edge_bound { break; }
+            let source = edge.source();
+            let target = edge.target();
+            if let Some(edge_weight) = edge.weight.as_ref() {
+                if result_g.contains_node(source) && result_g.contains_node(target) {
+                    if let Some(new_weight) = edge_map(EdgeIndex::new(i), edge_weight) {
+                        result_g.add_edge(source, target, new_weight);
+                        continue;
+                    }
+                }
+            }
+            result_g.add_vacant_edge(&mut free_edge);
+        }
+        result_g.free_node = free_node;
+        result_g.free_edge = free_edge;
+        result_g.check_free_lists();
+        result_g
+    }
 
     /// Extend the graph from an iterable of edges.
     ///
@@ -772,13 +858,10 @@
         self.g.raw_nodes()
     }
 
-    // used by check_free_lists conditionally
-    #[allow(unused)]
     fn raw_edges(&self) -> &[Edge<Option<E>, Ix>] {
         self.g.raw_edges()
     }
 
-    #[cfg(feature = "serde-1")]
     fn edge_bound(&self) -> usize {
         self.edge_references()
             .next_back()
diff --git a/tests/quickcheck.rs b/tests/quickcheck.rs
index 43b2545..a6a2af3 100644
--- a/tests/quickcheck.rs
+++ b/tests/quickcheck.rs
@@ -806,4 +806,69 @@
             gr2.edge_references().map(|ed| (ed.id(), ed.source().index(), ed.target().index()))
         );
     }
+
+    fn stable_di_graph_map_id(gr1: StableDiGraph<usize, usize>) -> () {
+        let gr2 = gr1.map(|_, &nw| nw, |_, &ew| ew);
+        assert!(nodes_eq!(gr1, gr2));
+        assert!(edgew_eq!(gr1, gr2));
+        assert!(edges_eq!(gr1, gr2));
+    }
+
+    fn stable_un_graph_map_id(gr1: StableUnGraph<usize, usize>) -> () {
+        let gr2 = gr1.map(|_, &nw| nw, |_, &ew| ew);
+        assert!(nodes_eq!(gr1, gr2));
+        assert!(edgew_eq!(gr1, gr2));
+        assert!(edges_eq!(gr1, gr2));
+    }
+
+    fn stable_di_graph_filter_map_id(gr1: StableDiGraph<usize, usize>) -> () {
+        let gr2 = gr1.filter_map(|_, &nw| Some(nw), |_, &ew| Some(ew));
+        assert!(nodes_eq!(gr1, gr2));
+        assert!(edgew_eq!(gr1, gr2));
+        assert!(edges_eq!(gr1, gr2));
+    }
+
+    fn test_stable_un_graph_filter_map_id(gr1: StableUnGraph<usize, usize>) -> () {
+        let gr2 = gr1.filter_map(|_, &nw| Some(nw), |_, &ew| Some(ew));
+        assert!(nodes_eq!(gr1, gr2));
+        assert!(edgew_eq!(gr1, gr2));
+        assert!(edges_eq!(gr1, gr2));
+    }
+
+    fn stable_di_graph_filter_map_remove(gr1: Small<StableDiGraph<i32, i32>>,
+                                         nodes: Vec<usize>,
+                                         edges: Vec<usize>) -> ()
+    {
+        let gr2 = gr1.filter_map(|ix, &nw| {
+            if !nodes.contains(&ix.index()) { Some(nw) } else { None }
+        },
+        |ix, &ew| {
+            if !edges.contains(&ix.index()) { Some(ew) } else { None }
+        });
+        let check_nodes = &set(gr1.node_indices()) - &set(nodes.iter().map(|&i| node_index(i)));
+        let mut check_edges = &set(gr1.edge_indices()) - &set(edges.iter().map(|&i| edge_index(i)));
+        // remove all edges with endpoint in removed nodes
+        for edge in gr1.edge_references() {
+            if nodes.contains(&edge.source().index()) ||
+                nodes.contains(&edge.target().index()) {
+                check_edges.remove(&edge.id());
+            }
+        }
+        // assert maintained
+        for i in check_nodes {
+            assert_eq!(gr1[i], gr2[i]);
+        }
+        for i in check_edges {
+            assert_eq!(gr1[i], gr2[i]);
+            assert_eq!(gr1.edge_endpoints(i), gr2.edge_endpoints(i));
+        }
+
+        // assert removals
+        for i in nodes {
+            assert!(gr2.node_weight(node_index(i)).is_none());
+        }
+        for i in edges {
+            assert!(gr2.edge_weight(edge_index(i)).is_none());
+        }
+    }
 }