Merge pull request #213 from jrraymond/optimize-is_isomorphic
Optimize is isomorphic
diff --git a/.travis.yml b/.travis.yml
index ee1d3fd..3d19f3d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,16 +2,10 @@
sudo: false
matrix:
include:
- - rust: 1.12.0
- before_script:
- # rand 0.4.2 requires rust 1.15, and rand-0.3.22 requires rand-0.4 :/
- # manually hacking the lockfile due to the limitations of cargo#2773
- - cargo generate-lockfile
- - sed -i -e 's/"rand 0.[34].[0-9]\+/"rand 0.3.20/' Cargo.lock
- - sed -i -e '/^name = "rand"/,/^$/s/version = "0.3.[0-9]\+"/version = "0.3.20"/' Cargo.lock
+ - rust: 1.15.0
env:
- ALL=' '
- - rust: 1.15.0
+ - rust: 1.25.0
env:
- ALL=' '
- rust: stable
diff --git a/Cargo.toml b/Cargo.toml
index 0e72c8c..04be09d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "petgraph"
-version = "0.4.11"
+version = "0.4.13"
license = "MIT/Apache-2.0"
authors = [
"bluss",
@@ -27,20 +27,20 @@
[dependencies]
fixedbitset = { version = "0.1.4" }
-quickcheck = { optional = true, version = "0.4", default-features = false }
-ordermap = { version = "0.3.0", optional = true }
+quickcheck = { optional = true, version = "0.7.1", default-features = false }
+indexmap = { version = "1.0.2", optional = true }
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
[dev-dependencies]
-rand = "0.3"
+rand = "0.5.5"
odds = { version = "0.2.19" }
defmac = "0.1"
itertools = { version = "0.7.0", default-features = false }
[features]
default = ["graphmap", "stable_graph"]
-graphmap = ["ordermap"]
+graphmap = ["indexmap"]
serde-1 = ["serde", "serde_derive"]
stable_graph = []
diff --git a/README.rst b/README.rst
index 3ccd7ca..383a709 100644
--- a/README.rst
+++ b/README.rst
@@ -26,6 +26,17 @@
Recent Changes
--------------
+- 0.4.13
+
+ - Fix clippy warnings by @jonasbb
+ - Add docs for ``Csr`` by @ksadorf
+ - Fix conflict with new stable method ``find_map`` in new Rust
+
+- 0.4.12
+
+ - Newtype ``Time`` now also implements ``Hash``
+ - Documentation updates for ``Frozen``.
+
- 0.4.11
- Fix ``petgraph::graph::NodeReferences`` to be publically visible
@@ -187,7 +198,7 @@
- ``GraphMap`` can now have directed edges. ``GraphMap::new`` is now generic
in the edge type. ``DiGraphMap`` and ``UnGraphMap`` are new type aliases.
- Add type aliases ``DiGraph, UnGraph, StableDiGraph, StableUnGraph``
- - ``GraphMap`` is based on the ordermap crate. Deterministic iteration
+ - ``GraphMap`` is based on the indexmap crate. Deterministic iteration
order, faster iteration, no side tables needed to convert to ``Graph``.
- Improved docs for a lot of types and functions.
- Add graph visitor ``DfsPostOrder``
@@ -297,7 +308,7 @@
- Add Graph::capacity(), GraphMap::capacity()
- Fix bug in Graph::reverse()
- Graph and GraphMap have `quickcheck::Arbitrary` implementations,
- if optional feature `quickcheck` is enabled.
+ if optional feature `check` is enabled.
- 0.1.16
diff --git a/serialization-tests/Cargo.toml b/serialization-tests/Cargo.toml
index 8837c0d..063fd44 100644
--- a/serialization-tests/Cargo.toml
+++ b/serialization-tests/Cargo.toml
@@ -19,6 +19,6 @@
[dev-dependencies]
serde_json = "1.0"
-quickcheck = { version = "0.4", default-features = false }
-bincode = { version = "0.9" }
+quickcheck = { version = "0.7.1", default-features = false }
+bincode = "1.0.1"
defmac = "0.1"
diff --git a/serialization-tests/tests/serialization.rs b/serialization-tests/tests/serialization.rs
index 069304f..a82ff88 100644
--- a/serialization-tests/tests/serialization.rs
+++ b/serialization-tests/tests/serialization.rs
@@ -352,7 +352,7 @@
// bincode macros
-defmac!(encode ref g => bincode::serialize(g, bincode::Infinite).unwrap());
+defmac!(encode ref g => bincode::serialize(g).unwrap());
defmac!(decode ref data => bincode::deserialize(data).unwrap());
defmac!(recode ref g => decode!(encode!(g)));
diff --git a/src/algo/dominators.rs b/src/algo/dominators.rs
index 6d08f61..80380af 100644
--- a/src/algo/dominators.rs
+++ b/src/algo/dominators.rs
@@ -213,7 +213,7 @@
.map(|p| *node_to_post_order_idx.get(&p).unwrap())
.collect()
})
- .unwrap_or(vec![])
+ .unwrap_or_else(Vec::new)
})
.collect()
}
diff --git a/src/algo/mod.rs b/src/algo/mod.rs
index 4fc05c2..b64f536 100644
--- a/src/algo/mod.rs
+++ b/src/algo/mod.rs
@@ -35,6 +35,7 @@
IndexType,
};
use visit::{Data, NodeRef, IntoNodeReferences};
+use visit::Walker;
use data::{
Element,
};
@@ -49,6 +50,41 @@
/// [Generic] Return the number of connected components of the graph.
///
/// For a directed graph, this is the *weakly* connected components.
+/// # Example
+/// ```rust
+/// use petgraph::Graph;
+/// use petgraph::algo::connected_components;
+/// use petgraph::prelude::*;
+///
+/// let mut graph : Graph<(),(),Directed>= Graph::new();
+/// let a = graph.add_node(()); // node with no weight
+/// let b = graph.add_node(());
+/// let c = graph.add_node(());
+/// let d = graph.add_node(());
+/// let e = graph.add_node(());
+/// let f = graph.add_node(());
+/// let g = graph.add_node(());
+/// let h = graph.add_node(());
+///
+/// graph.extend_with_edges(&[
+/// (a, b),
+/// (b, c),
+/// (c, d),
+/// (d, a),
+/// (e, f),
+/// (f, g),
+/// (g, h),
+/// (h, e)
+/// ]);
+/// // a ----> b e ----> f
+/// // ^ | ^ |
+/// // | v | v
+/// // d <---- c h <---- g
+///
+/// assert_eq!(connected_components(&graph),2);
+/// graph.add_edge(b,e,());
+/// assert_eq!(connected_components(&graph),1);
+/// ```
pub fn connected_components<G>(g: G) -> usize
where G: NodeCompactIndexable + IntoEdgeReferences,
{
@@ -122,7 +158,7 @@
}
if !dfs.discovered.is_visited(&succ) {
dfs.stack.push(succ);
- }
+ }
}
} else {
dfs.stack.pop();
@@ -229,12 +265,7 @@
with_dfs(g, space, |dfs| {
dfs.reset(g);
dfs.move_to(from);
- while let Some(x) = dfs.next(g) {
- if x == to {
- return true;
- }
- }
- false
+ dfs.iter(g).any(|x| x == to)
})
}
@@ -321,7 +352,7 @@
#[derive(Debug)]
struct Data<'a, G>
- where G: NodeIndexable,
+ where G: NodeIndexable,
G::NodeId: 'a
{
index: usize,
@@ -330,7 +361,7 @@
sccs: &'a mut Vec<Vec<G::NodeId>>,
}
- fn scc_visit<G>(v: G::NodeId, g: G, data: &mut Data<G>)
+ fn scc_visit<G>(v: G::NodeId, g: G, data: &mut Data<G>)
where G: IntoNeighbors + NodeIndexable
{
macro_rules! node {
@@ -402,6 +433,82 @@
///
/// If `make_acyclic` is true, self-loops and multi edges are ignored, guaranteeing that
/// the output is acyclic.
+/// # Example
+/// ```rust
+/// use petgraph::Graph;
+/// use petgraph::algo::condensation;
+/// use petgraph::prelude::*;
+///
+/// let mut graph : Graph<(),(),Directed> = Graph::new();
+/// let a = graph.add_node(()); // node with no weight
+/// let b = graph.add_node(());
+/// let c = graph.add_node(());
+/// let d = graph.add_node(());
+/// let e = graph.add_node(());
+/// let f = graph.add_node(());
+/// let g = graph.add_node(());
+/// let h = graph.add_node(());
+///
+/// graph.extend_with_edges(&[
+/// (a, b),
+/// (b, c),
+/// (c, d),
+/// (d, a),
+/// (b, e),
+/// (e, f),
+/// (f, g),
+/// (g, h),
+/// (h, e)
+/// ]);
+///
+/// // a ----> b ----> e ----> f
+/// // ^ | ^ |
+/// // | v | v
+/// // d <---- c h <---- g
+///
+/// let condensed_graph = condensation(graph,false);
+/// let A = NodeIndex::new(0);
+/// let B = NodeIndex::new(1);
+/// assert_eq!(condensed_graph.node_count(), 2);
+/// assert_eq!(condensed_graph.edge_count(), 9);
+/// assert_eq!(condensed_graph.neighbors(A).collect::<Vec<_>>(), vec![A, A, A, A]);
+/// assert_eq!(condensed_graph.neighbors(B).collect::<Vec<_>>(), vec![A, B, B, B, B]);
+/// ```
+/// If `make_acyclic` is true, self-loops and multi edges are ignored:
+///
+/// ```rust
+/// # use petgraph::Graph;
+/// # use petgraph::algo::condensation;
+/// # use petgraph::prelude::*;
+/// #
+/// # let mut graph : Graph<(),(),Directed> = Graph::new();
+/// # let a = graph.add_node(()); // node with no weight
+/// # let b = graph.add_node(());
+/// # let c = graph.add_node(());
+/// # let d = graph.add_node(());
+/// # let e = graph.add_node(());
+/// # let f = graph.add_node(());
+/// # let g = graph.add_node(());
+/// # let h = graph.add_node(());
+/// #
+/// # graph.extend_with_edges(&[
+/// # (a, b),
+/// # (b, c),
+/// # (c, d),
+/// # (d, a),
+/// # (b, e),
+/// # (e, f),
+/// # (f, g),
+/// # (g, h),
+/// # (h, e)
+/// # ]);
+/// let acyclic_condensed_graph = condensation(graph, true);
+/// let A = NodeIndex::new(0);
+/// let B = NodeIndex::new(1);
+/// assert_eq!(acyclic_condensed_graph.node_count(), 2);
+/// assert_eq!(acyclic_condensed_graph.edge_count(), 1);
+/// assert_eq!(acyclic_condensed_graph.neighbors(B).collect::<Vec<_>>(), vec![A]);
+/// ```
pub fn condensation<N, E, Ty, Ix>(g: Graph<N, E, Ty, Ix>, make_acyclic: bool) -> Graph<Vec<N>, E, Ty, Ix>
where Ty: EdgeType,
Ix: IndexType,
@@ -551,6 +658,60 @@
/// are indexed by the graph's node indices.
///
/// [bf]: https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
+///
+/// # Example
+/// ```rust
+/// use petgraph::Graph;
+/// use petgraph::algo::bellman_ford;
+/// use petgraph::prelude::*;
+///
+/// let mut g = Graph::new();
+/// let a = g.add_node(()); // node with no weight
+/// let b = g.add_node(());
+/// let c = g.add_node(());
+/// let d = g.add_node(());
+/// let e = g.add_node(());
+/// let f = g.add_node(());
+/// g.extend_with_edges(&[
+/// (0, 1, 2.0),
+/// (0, 3, 4.0),
+/// (1, 2, 1.0),
+/// (1, 5, 7.0),
+/// (2, 4, 5.0),
+/// (4, 5, 1.0),
+/// (3, 4, 1.0),
+/// ]);
+///
+/// // Graph represented with the weight of each edge
+/// //
+/// // 2 1
+/// // a ----- b ----- c
+/// // | 4 | 7 |
+/// // d f | 5
+/// // | 1 | 1 |
+/// // \------ e ------/
+///
+/// let path = bellman_ford(&g, a);
+/// assert_eq!(path, Ok((vec![0.0 , 2.0, 3.0, 4.0, 5.0, 6.0],
+/// vec![None, Some(a),Some(b),Some(a), Some(d), Some(e)]
+/// ))
+/// );
+/// // Node f (indice 5) can be reach from a with a path costing 6.
+/// // Predecessor of f is Some(e) which predecessor is Some(d) which predecessor is Some(a).
+/// // Thus the path from a to f is a <-> d <-> e <-> f
+///
+/// let graph_with_neg_cycle = Graph::<(), f32, Undirected>::from_edges(&[
+/// (0, 1, -2.0),
+/// (0, 3, -4.0),
+/// (1, 2, -1.0),
+/// (1, 5, -25.0),
+/// (2, 4, -5.0),
+/// (4, 5, -25.0),
+/// (3, 4, -1.0),
+/// ]);
+///
+/// assert!(bellman_ford(&graph_with_neg_cycle, NodeIndex::new(0)).is_err());
+/// ```
pub fn bellman_ford<G>(g: G, source: G::NodeId)
-> Result<(Vec<G::EdgeWeight>, Vec<Option<G::NodeId>>), NegativeCycle>
where G: NodeCount + IntoNodeIdentifiers + IntoEdges + NodeIndexable,
@@ -623,4 +784,3 @@
fn zero() -> Self { 0. }
fn infinite() -> Self { 1./0. }
}
-
diff --git a/src/astar.rs b/src/astar.rs
index 565953d..0ebd4fe 100644
--- a/src/astar.rs
+++ b/src/astar.rs
@@ -37,6 +37,7 @@
///
/// The graph should be `Visitable` and implement `IntoEdges`.
///
+/// # Example
/// ```
/// use petgraph::Graph;
/// use petgraph::algo::astar;
@@ -58,6 +59,16 @@
/// (d, e, 1),
/// ]);
///
+/// // Graph represented with the weight of each edge
+/// // Edges with '*' are part of the optimal path.
+/// //
+/// // 2 1
+/// // a ----- b ----- c
+/// // | 4* | 7 |
+/// // d f | 5
+/// // | 1* | 1* |
+/// // \------ e ------/
+///
/// let path = astar(&g, a, |finish| finish == f, |e| *e.weight(), |_| 0);
/// assert_eq!(path, Some((6, vec![a, d, e, f])));
/// ```
@@ -157,13 +168,9 @@
let mut path = vec![last];
let mut current = last;
- loop {
- if let Some(&previous) = self.came_from.get(¤t) {
- path.push(previous);
- current = previous;
- } else {
- break
- }
+ while let Some(&previous) = self.came_from.get(¤t) {
+ path.push(previous);
+ current = previous;
}
path.reverse();
diff --git a/src/csr.rs b/src/csr.rs
index 1a237bb..1ce87cc 100644
--- a/src/csr.rs
+++ b/src/csr.rs
@@ -341,7 +341,7 @@
fn neighbors_range(&self, a: NodeIndex<Ix>) -> Range<usize> {
let index = self.row[a.index()];
- let end = self.row.get(a.index() + 1).cloned().unwrap_or(self.column.len());
+ let end = self.row.get(a.index() + 1).cloned().unwrap_or_else(|| self.column.len());
index..end
}
diff --git a/src/dijkstra.rs b/src/dijkstra.rs
index fe9c401..e7b6feb 100644
--- a/src/dijkstra.rs
+++ b/src/dijkstra.rs
@@ -31,6 +31,55 @@
/// cost is calculated.
///
/// Returns a `HashMap` that maps `NodeId` to path cost.
+/// # Example
+/// ```rust
+/// use petgraph::Graph;
+/// use petgraph::algo::dijkstra;
+/// use petgraph::prelude::*;
+/// use std::collections::HashMap;
+///
+/// let mut graph : Graph<(),(),Directed>= Graph::new();
+/// let a = graph.add_node(()); // node with no weight
+/// let b = graph.add_node(());
+/// let c = graph.add_node(());
+/// let d = graph.add_node(());
+/// let e = graph.add_node(());
+/// let f = graph.add_node(());
+/// let g = graph.add_node(());
+/// let h = graph.add_node(());
+/// // z will be in another connected component
+/// let z = graph.add_node(());
+///
+/// graph.extend_with_edges(&[
+/// (a, b),
+/// (b, c),
+/// (c, d),
+/// (d, a),
+/// (e, f),
+/// (b, e),
+/// (f, g),
+/// (g, h),
+/// (h, e)
+/// ]);
+/// // a ----> b ----> e ----> f
+/// // ^ | ^ |
+/// // | v | v
+/// // d <---- c h <---- g
+///
+/// let expected_res: HashMap<NodeIndex, usize> = [
+/// (a, 3),
+/// (b, 0),
+/// (c, 1),
+/// (d, 2),
+/// (e, 1),
+/// (f, 2),
+/// (g, 3),
+/// (h, 4)
+/// ].iter().cloned().collect();
+/// let res = dijkstra(&graph,b,None, |_| 1);
+/// assert_eq!(res, expected_res);
+/// // z is not inside res because there is not path from b to z.
+/// ```
pub fn dijkstra<G, F, K>(graph: G, start: G::NodeId, goal: Option<G::NodeId>,
mut edge_cost: F)
-> HashMap<G::NodeId, K>
diff --git a/src/dot.rs b/src/dot.rs
index fbd78fb..09bf26b 100644
--- a/src/dot.rs
+++ b/src/dot.rs
@@ -29,7 +29,7 @@
/// println!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel]));
///
/// // In this case the output looks like this:
-/// //
+/// //
/// // digraph {
/// // 0 [label="\"A\""]
/// // 1 [label="\"B\""]
@@ -171,10 +171,10 @@
fn write_char(&mut self, c: char) -> fmt::Result {
match c {
- '"' => try!(self.0.write_char('\\')),
+ '"' | '\\' => try!(self.0.write_char('\\')),
// \l is for left justified linebreak
- '\n' => return self.0.write_str(r#"\l"#),
- _ => { }
+ '\n' => return self.0.write_str("\\l"),
+ _ => {}
}
self.0.write_char(c)
}
@@ -188,7 +188,7 @@
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if f.alternate() {
- write!(&mut Escaper(f), "{:#}\\l", &self.0)
+ write!(&mut Escaper(f), "{:#}\n", &self.0)
} else {
write!(&mut Escaper(f), "{}", &self.0)
}
@@ -205,3 +205,13 @@
self.0.fmt(f)
}
}
+
+#[test]
+fn test_escape() {
+ let mut buff = String::new();
+ {
+ let mut e = Escaper(&mut buff);
+ let _ = e.write_str("\" \\ \n");
+ }
+ assert_eq!(buff, "\\\" \\\\ \\l");
+}
diff --git a/src/graph_impl/stable_graph/mod.rs b/src/graph_impl/stable_graph/mod.rs
index fda91c1..20582a5 100644
--- a/src/graph_impl/stable_graph/mod.rs
+++ b/src/graph_impl/stable_graph/mod.rs
@@ -685,6 +685,22 @@
}
}
+ /// Return an iterator over either the nodes without edges to them
+ /// (`Incoming`) or from them (`Outgoing`).
+ ///
+ /// An *internal* node has both incoming and outgoing edges.
+ /// The nodes in `.externals(Incoming)` are the source nodes and
+ /// `.externals(Outgoing)` are the sinks of the graph.
+ ///
+ /// For a graph with undirected edges, both the sinks and the sources are
+ /// just the nodes without edges.
+ ///
+ /// The whole iteration computes in **O(|V|)** time.
+ pub fn externals(&self, dir: Direction) -> Externals<N, Ty, Ix>
+ {
+ Externals { iter: self.raw_nodes().iter().enumerate(), dir: dir, ty: PhantomData }
+ }
+
/// Index the `StableGraph` by two indices, any combination of
/// node or edge indices is fine.
///
@@ -1168,7 +1184,7 @@
type Item = (NodeIndex<Ix>, &'a N);
fn next(&mut self) -> Option<Self::Item> {
- self.iter.find_map(|(i, node)| {
+ self.iter.ex_find_map(|(i, node)| {
node.weight.as_ref().map(move |w| (node_index(i), w))
})
}
@@ -1183,7 +1199,7 @@
where Ix: IndexType
{
fn next_back(&mut self) -> Option<Self::Item> {
- self.iter.rfind_map(|(i, node)| {
+ self.iter.ex_rfind_map(|(i, node)| {
node.weight.as_ref().map(move |w| (node_index(i), w))
})
}
@@ -1360,7 +1376,7 @@
type Item = EdgeReference<'a, E, Ix>;
fn next(&mut self) -> Option<Self::Item> {
- self.iter.find_map(|(i, edge)|
+ self.iter.ex_find_map(|(i, edge)|
edge.weight.as_ref().map(move |weight| {
EdgeReference {
index: edge_index(i),
@@ -1375,7 +1391,7 @@
where Ix: IndexType
{
fn next_back(&mut self) -> Option<Self::Item> {
- self.iter.rfind_map(|(i, edge)|
+ self.iter.ex_rfind_map(|(i, edge)|
edge.weight.as_ref().map(move |weight| {
EdgeReference {
index: edge_index(i),
@@ -1386,6 +1402,38 @@
}
}
+/// An iterator over either the nodes without edges to them or from them.
+pub struct Externals<'a, N: 'a, Ty, Ix: IndexType = DefaultIx> {
+ iter: iter::Enumerate<slice::Iter<'a, Node<Option<N>, Ix>>>,
+ dir: Direction,
+ ty: PhantomData<Ty>,
+}
+
+impl<'a, N: 'a, Ty, Ix> Iterator for Externals<'a, N, Ty, Ix> where
+ Ty: EdgeType,
+ Ix: IndexType,
+{
+ type Item = NodeIndex<Ix>;
+ fn next(&mut self) -> Option<NodeIndex<Ix>>
+ {
+ let k = self.dir.index();
+ loop {
+ match self.iter.next() {
+ None => return None,
+ Some((index, node)) => {
+ if node.weight.is_some() &&
+ node.next[k] == EdgeIndex::end() &&
+ (Ty::is_directed() ||
+ node.next[1-k] == EdgeIndex::end()) {
+ return Some(NodeIndex::new(index))
+ } else {
+ continue
+ }
+ },
+ }
+ }
+ }
+}
/// Iterator over the neighbors of a node.
///
@@ -1524,7 +1572,7 @@
type Item = NodeIndex<Ix>;
fn next(&mut self) -> Option<Self::Item> {
- self.iter.find_map(|(i, node)| {
+ self.iter.ex_find_map(|(i, node)| {
if node.weight.is_some() {
Some(node_index(i))
} else { None }
@@ -1539,7 +1587,7 @@
impl<'a, N, Ix: IndexType> DoubleEndedIterator for NodeIndices<'a, N, Ix> {
fn next_back(&mut self) -> Option<Self::Item> {
- self.iter.rfind_map(|(i, node)| {
+ self.iter.ex_rfind_map(|(i, node)| {
if node.weight.is_some() {
Some(node_index(i))
} else { None }
@@ -1570,7 +1618,7 @@
type Item = EdgeIndex<Ix>;
fn next(&mut self) -> Option<Self::Item> {
- self.iter.find_map(|(i, node)| {
+ self.iter.ex_find_map(|(i, node)| {
if node.weight.is_some() {
Some(edge_index(i))
} else { None }
@@ -1585,7 +1633,7 @@
impl<'a, E, Ix: IndexType> DoubleEndedIterator for EdgeIndices<'a, E, Ix> {
fn next_back(&mut self) -> Option<Self::Item> {
- self.iter.rfind_map(|(i, node)| {
+ self.iter.ex_rfind_map(|(i, node)| {
if node.weight.is_some() {
Some(edge_index(i))
} else { None }
diff --git a/src/graphmap.rs b/src/graphmap.rs
index 945abe0..6b6cab4 100644
--- a/src/graphmap.rs
+++ b/src/graphmap.rs
@@ -14,11 +14,11 @@
use std::ops::{Index, IndexMut, Deref};
use std::iter::FromIterator;
use std::marker::PhantomData;
-use ordermap::OrderMap;
-use ordermap::{
- Iter as OrderMapIter, IterMut as OrderMapIterMut
+use indexmap::IndexMap;
+use indexmap::map::{
+ Iter as IndexMapIter, IterMut as IndexMapIterMut
};
-use ordermap::Keys;
+use indexmap::map::Keys;
use {
EdgeType,
@@ -72,8 +72,8 @@
/// Depends on crate feature `graphmap` (default).
#[derive(Clone)]
pub struct GraphMap<N, E, Ty> {
- nodes: OrderMap<N, Vec<(N, CompactDirection)>>,
- edges: OrderMap<(N, N), E>,
+ nodes: IndexMap<N, Vec<(N, CompactDirection)>>,
+ edges: IndexMap<(N, N), E>,
ty: PhantomData<Ty>,
}
@@ -121,8 +121,8 @@
/// Create a new `GraphMap` with estimated capacity.
pub fn with_capacity(nodes: usize, edges: usize) -> Self {
GraphMap {
- nodes: OrderMap::with_capacity(nodes),
- edges: OrderMap::with_capacity(edges),
+ nodes: IndexMap::with_capacity(nodes),
+ edges: IndexMap::with_capacity(edges),
ty: PhantomData,
}
}
@@ -559,7 +559,7 @@
Ty: EdgeType
{
from: N,
- edges: &'a OrderMap<(N, N), E>,
+ edges: &'a IndexMap<(N, N), E>,
iter: Neighbors<'a, N, Ty>,
}
@@ -596,7 +596,7 @@
}
pub struct AllEdges<'a, N, E: 'a, Ty> where N: 'a + NodeTrait {
- inner: OrderMapIter<'a, (N, N), E>,
+ inner: IndexMapIter<'a, (N, N), E>,
ty: PhantomData<Ty>,
}
@@ -640,7 +640,7 @@
}
pub struct AllEdgesMut<'a, N, E: 'a, Ty> where N: 'a + NodeTrait {
- inner: OrderMapIterMut<'a, (N, N), E>,
+ inner: IndexMapIterMut<'a, (N, N), E>,
ty: PhantomData<Ty>,
}
@@ -815,7 +815,7 @@
}
pub struct NodeIdentifiers<'a, N, E: 'a, Ty> where N: 'a + NodeTrait {
- iter: OrderMapIter<'a, N, Vec<(N, CompactDirection)>>,
+ iter: IndexMapIter<'a, N, Vec<(N, CompactDirection)>>,
ty: PhantomData<Ty>,
edge_ty: PhantomData<E>,
}
@@ -847,7 +847,7 @@
}
pub struct NodeReferences<'a, N, E: 'a, Ty> where N: 'a + NodeTrait {
- iter: OrderMapIter<'a, N, Vec<(N, CompactDirection)>>,
+ iter: IndexMapIter<'a, N, Vec<(N, CompactDirection)>>,
ty: PhantomData<Ty>,
edge_ty: PhantomData<E>,
}
diff --git a/src/iter_utils.rs b/src/iter_utils.rs
index 62a0183..30db2d5 100644
--- a/src/iter_utils.rs
+++ b/src/iter_utils.rs
@@ -2,10 +2,10 @@
pub trait IterUtilsExt : Iterator {
/// Return the first element that maps to `Some(_)`, or None if the iterator
/// was exhausted.
- fn find_map<F, R>(&mut self, mut f: F) -> Option<R>
+ fn ex_find_map<F, R>(&mut self, mut f: F) -> Option<R>
where F: FnMut(Self::Item) -> Option<R>
{
- while let Some(elt) = self.next() {
+ for elt in self {
if let result @ Some(_) = f(elt) {
return result;
}
@@ -15,7 +15,7 @@
/// Return the last element from the back that maps to `Some(_)`, or
/// None if the iterator was exhausted.
- fn rfind_map<F, R>(&mut self, mut f: F) -> Option<R>
+ fn ex_rfind_map<F, R>(&mut self, mut f: F) -> Option<R>
where F: FnMut(Self::Item) -> Option<R>,
Self: DoubleEndedIterator,
{
diff --git a/src/lib.rs b/src/lib.rs
index 6523894..7554774 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,7 +19,7 @@
extern crate fixedbitset;
#[cfg(feature = "graphmap")]
-extern crate ordermap;
+extern crate indexmap;
#[cfg(feature = "serde-1")]
extern crate serde;
diff --git a/src/quickcheck.rs b/src/quickcheck.rs
index 18d40c7..bda1594 100644
--- a/src/quickcheck.rs
+++ b/src/quickcheck.rs
@@ -1,5 +1,4 @@
extern crate quickcheck;
-
use self::quickcheck::{Gen, Arbitrary};
use {
@@ -20,6 +19,15 @@
};
use visit::NodeIndexable;
+/// Return a random float in the range [0, 1.)
+fn random_01<G: Gen>(g: &mut G) -> f64 {
+ // from rand
+ let bits = 53;
+ let scale = 1. / ((1u64 << bits) as f64);
+ let x = g.next_u64();
+ (x >> (64 - bits)) as f64 * scale
+}
+
/// `Arbitrary` for `Graph` creates a graph by selecting a node count
/// and a probability for each possible edge to exist.
///
@@ -41,7 +49,7 @@
return Graph::with_capacity(0, 0);
}
// use X² for edge probability (bias towards lower)
- let edge_prob = g.gen_range(0., 1.) * g.gen_range(0., 1.);
+ let edge_prob = random_01(g) * random_01(g);
let edges = ((nodes as f64).powi(2) * edge_prob) as usize;
let mut gr = Graph::with_capacity(nodes, edges);
for _ in 0..nodes {
@@ -52,7 +60,7 @@
if !gr.is_directed() && i > j {
continue;
}
- let p: f64 = g.gen();
+ let p: f64 = random_01(g);
if p <= edge_prob {
gr.add_edge(i, j, E::arbitrary(g));
}
@@ -107,7 +115,7 @@
return StableGraph::with_capacity(0, 0);
}
// use X² for edge probability (bias towards lower)
- let edge_prob = g.gen_range(0., 1.) * g.gen_range(0., 1.);
+ let edge_prob = random_01(g) * random_01(g);
let edges = ((nodes as f64).powi(2) * edge_prob) as usize;
let mut gr = StableGraph::with_capacity(nodes, edges);
for _ in 0..nodes {
@@ -120,7 +128,7 @@
if !gr.is_directed() && i > j {
continue;
}
- let p: f64 = g.gen();
+ let p: f64 = random_01(g);
if p <= edge_prob {
gr.add_edge(i, j, E::arbitrary(g));
}
@@ -188,7 +196,7 @@
nodes.dedup();
// use X² for edge probability (bias towards lower)
- let edge_prob = g.gen_range(0., 1.) * g.gen_range(0., 1.);
+ let edge_prob = random_01(g) * random_01(g);
let edges = ((nodes.len() as f64).powi(2) * edge_prob) as usize;
let mut gr = GraphMap::with_capacity(nodes.len(), edges);
for &node in &nodes {
@@ -197,7 +205,7 @@
for (index, &i) in nodes.iter().enumerate() {
let js = if Ty::is_directed() { &nodes[..] } else { &nodes[index..] };
for &j in js {
- let p: f64 = g.gen();
+ let p: f64 = random_01(g);
if p <= edge_prob {
gr.add_edge(i, j, E::arbitrary(g));
}
diff --git a/src/visit/dfsvisit.rs b/src/visit/dfsvisit.rs
index 91ea1b2..e0ba22e 100644
--- a/src/visit/dfsvisit.rs
+++ b/src/visit/dfsvisit.rs
@@ -20,26 +20,35 @@
/// For an edge *(u, v)*, if the discover time of *v* is greater than *u*,
/// then it is a forward edge, else a cross edge.
CrossForwardEdge(N, N),
+ /// All edges from a node have been reported.
Finish(N, Time),
}
-/// Return if the expression is a break value.
+/// Return if the expression is a break value, execute the provided statement
+/// if it is a prune value.
macro_rules! try_control {
- ($e:expr) => {
+ ($e:expr, $p:stmt) => {
match $e {
x => if x.should_break() {
return x;
+ } else if x.should_prune() {
+ $p;
}
}
}
}
-/// Control flow for callbacks.
-///
-/// `Break` can carry a value.
+/// Control flow for `depth_first_search` callbacks.
#[derive(Copy, Clone, Debug)]
pub enum Control<B> {
+ /// Continue the DFS traversal as normal.
Continue,
+ /// Prune the current node from the DFS traversal. No more edges from this
+ /// node will be reported to the callback. A `DfsEvent::Finish` for this
+ /// node will still be reported. This can be returned in response to any
+ /// `DfsEvent`, except `Finish`, which will panic.
+ Prune,
+ /// Stop the DFS traversal and return the provided value.
Break(B),
}
@@ -48,7 +57,7 @@
/// Get the value in `Control::Break(_)`, if present.
pub fn break_value(self) -> Option<B> {
match self {
- Control::Continue => None,
+ Control::Continue | Control::Prune => None,
Control::Break(b) => Some(b),
}
}
@@ -60,12 +69,15 @@
pub trait ControlFlow {
fn continuing() -> Self;
fn should_break(&self) -> bool;
+ fn should_prune(&self) -> bool;
}
impl ControlFlow for () {
fn continuing() { }
#[inline]
fn should_break(&self) -> bool { false }
+ #[inline]
+ fn should_prune(&self) -> bool { false }
}
impl<B> ControlFlow for Control<B> {
@@ -73,12 +85,21 @@
fn should_break(&self) -> bool {
if let Control::Break(_) = *self { true } else { false }
}
+ fn should_prune(&self) -> bool {
+ match *self {
+ Control::Prune => true,
+ Control::Continue | Control::Break(_) => false,
+ }
+ }
}
-impl<E> ControlFlow for Result<(), E> {
- fn continuing() -> Self { Ok(()) }
+impl<C: ControlFlow, E> ControlFlow for Result<C, E> {
+ fn continuing() -> Self { Ok(C::continuing()) }
fn should_break(&self) -> bool {
- if let Err(_) = *self { true } else { false }
+ if let Ok(ref c) = *self { c.should_break() } else { true }
+ }
+ fn should_prune(&self) -> bool {
+ if let Ok(ref c) = *self { c.should_prune() } else { false }
}
}
@@ -98,8 +119,12 @@
///
/// If the return value of the visitor is simply `()`, the visit runs until it
/// is finished. If the return value is a `Control<B>`, it can be used to
-/// break the visit early, and the last control value is returned by the
-/// function.
+/// change the control flow of the search. `Control::Break` will stop the
+/// the visit early, returning the contained value from the function.
+/// `Control::Prune` will stop traversing any additional edges from the current
+/// node and proceed immediately to the `Finish` event.
+///
+/// ***Panics** if you attempt to prune a node from its `Finish` event.
///
/// [de]: enum.DfsEvent.html
///
@@ -156,7 +181,8 @@
let finished = &mut graph.visit_map();
for start in starts {
- try_control!(dfs_visitor(graph, start, &mut visitor, discovered, finished, time));
+ try_control!(dfs_visitor(graph, start, &mut visitor, discovered, finished, time),
+ unreachable!());
}
C::continuing()
}
@@ -171,20 +197,27 @@
if !discovered.visit(u) {
return C::continuing();
}
- try_control!(visitor(DfsEvent::Discover(u, time_post_inc(time))));
- for v in graph.neighbors(u) {
- if !discovered.is_visited(&v) {
- try_control!(visitor(DfsEvent::TreeEdge(u, v)));
- try_control!(dfs_visitor(graph, v, visitor, discovered, finished, time));
- } else if !finished.is_visited(&v) {
- try_control!(visitor(DfsEvent::BackEdge(u, v)));
- } else {
- try_control!(visitor(DfsEvent::CrossForwardEdge(u, v)));
+
+ 'prune: loop {
+ try_control!(visitor(DfsEvent::Discover(u, time_post_inc(time))), break 'prune);
+ for v in graph.neighbors(u) {
+ if !discovered.is_visited(&v) {
+ try_control!(visitor(DfsEvent::TreeEdge(u, v)), continue);
+ try_control!(dfs_visitor(graph, v, visitor, discovered, finished, time),
+ unreachable!());
+ } else if !finished.is_visited(&v) {
+ try_control!(visitor(DfsEvent::BackEdge(u, v)), continue);
+ } else {
+ try_control!(visitor(DfsEvent::CrossForwardEdge(u, v)), continue);
+ }
}
+
+ break;
}
let first_finish = finished.visit(u);
debug_assert!(first_finish);
- try_control!(visitor(DfsEvent::Finish(u, time_post_inc(time))));
+ try_control!(visitor(DfsEvent::Finish(u, time_post_inc(time))),
+ panic!("Pruning on the `DfsEvent::Finish` is not supported!"));
C::continuing()
}
diff --git a/src/visit/filter.rs b/src/visit/filter.rs
index 07b3ae9..cb64c79 100644
--- a/src/visit/filter.rs
+++ b/src/visit/filter.rs
@@ -10,6 +10,7 @@
GraphProp,
IntoEdgeReferences,
IntoEdges,
+ IntoEdgesDirected,
IntoNeighbors,
IntoNeighborsDirected,
IntoNodeIdentifiers,
@@ -337,6 +338,21 @@
}
}
+impl<'a, G, F> IntoNeighborsDirected for &'a EdgeFiltered<G, F>
+where
+ G: IntoEdgesDirected,
+ F: FilterEdge<G::EdgeRef>,
+{
+ type NeighborsDirected = EdgeFilteredNeighborsDirected<'a, G, F>;
+ fn neighbors_directed(self, n: G::NodeId, dir: Direction) -> Self::NeighborsDirected {
+ EdgeFilteredNeighborsDirected {
+ iter: self.0.edges_directed(n, dir),
+ f: &self.1,
+ from: n,
+ }
+ }
+}
+
/// A filtered neighbors iterator.
pub struct EdgeFilteredNeighbors<'a, G, F: 'a>
where G: IntoEdges,
@@ -409,6 +425,41 @@
}
}
+/// A filtered neighbors-directed iterator.
+pub struct EdgeFilteredNeighborsDirected<'a, G, F: 'a>
+where
+ G: IntoEdgesDirected,
+{
+ iter: G::EdgesDirected,
+ f: &'a F,
+ from: G::NodeId,
+}
+
+impl<'a, G, F> Iterator for EdgeFilteredNeighborsDirected<'a, G, F>
+where
+ F: FilterEdge<G::EdgeRef>,
+ G: IntoEdgesDirected,
+{
+ type Item = G::NodeId;
+ fn next(&mut self) -> Option<Self::Item> {
+ let f = self.f;
+ let from = self.from;
+ (&mut self.iter)
+ .filter_map(move |edge| {
+ if f.include_edge(edge) {
+ if edge.source() != from {
+ Some(edge.source())
+ } else {
+ Some(edge.target()) // includes case where from == source == target
+ }
+ } else {
+ None
+ }
+ })
+ .next()
+ }
+}
+
Data!{delegate_impl [[G, F], G, EdgeFiltered<G, F>, access0]}
GraphProp!{delegate_impl [[G, F], G, EdgeFiltered<G, F>, access0]}
IntoNodeIdentifiers!{delegate_impl [['a, G, F], G, &'a EdgeFiltered<G, F>, access0]}
diff --git a/src/visit/traversal.rs b/src/visit/traversal.rs
index 9aa6f93..d48d083 100644
--- a/src/visit/traversal.rs
+++ b/src/visit/traversal.rs
@@ -95,7 +95,7 @@
pub fn next<G>(&mut self, graph: G) -> Option<N>
where G: IntoNeighbors<NodeId=N>,
{
- while let Some(node) = self.stack.pop() {
+ if let Some(node) = self.stack.pop() {
for succ in graph.neighbors(node) {
if self.discovered.visit(succ) {
self.stack.push(succ);
@@ -251,7 +251,7 @@
pub fn next<G>(&mut self, graph: G) -> Option<N>
where G: IntoNeighbors<NodeId=N>
{
- while let Some(node) = self.stack.pop_front() {
+ if let Some(node) = self.stack.pop_front() {
for succ in graph.neighbors(node) {
if self.discovered.visit(succ) {
self.stack.push_back(succ);
@@ -403,6 +403,15 @@
}
}
+impl<'a, C, W: ?Sized> Walker<C> for &'a mut W
+ where W: Walker<C>,
+{
+ type Item = W::Item;
+ fn walk_next(&mut self, context: C) -> Option<Self::Item> {
+ (**self).walk_next(context)
+ }
+}
+
impl<G> Walker<G> for Dfs<G::NodeId, G::Map>
where G: IntoNeighbors + Visitable
{
diff --git a/tests/graph.rs b/tests/graph.rs
index 773a993..78a49ea 100644
--- a/tests/graph.rs
+++ b/tests/graph.rs
@@ -1420,12 +1420,13 @@
b: &'static str,
};
let mut gr = Graph::new();
- let a = gr.add_node(Record { a: 1, b: "abc" });
+ let a = gr.add_node(Record { a: 1, b: r"abc\" });
gr.add_edge(a, a, (1, 2));
let dot_output = format!("{:#?}", Dot::new(&gr));
assert_eq!(dot_output,
+ // The single \ turns into four \\\\ because of Debug which turns it to \\ and then escaping each \ to \\.
r#"digraph {
- 0 [label="Record {\l a: 1,\l b: \"abc\"\l}\l"]
+ 0 [label="Record {\l a: 1,\l b: \"abc\\\\\"\l}\l"]
0 -> 0 [label="(\l 1,\l 2\l)\l"]
}
"#);
@@ -1462,7 +1463,145 @@
assert_eq!(set(po), set(g.node_identifiers().filter(|n| (filt.1)(*n))));
}
+#[test]
+fn filtered_edge_reverse() {
+ use petgraph::visit::EdgeFiltered;
+ #[derive(Eq, PartialEq)]
+ enum E {
+ A,
+ B,
+ }
+ // Start with single node graph with loop
+ let mut g = Graph::new();
+ let a = g.add_node("A");
+ g.add_edge(a, a, E::A);
+ let ef_a = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::A);
+ let mut po = Vec::new();
+ let mut dfs = Dfs::new(&ef_a, a);
+ while let Some(next_n_ix) = dfs.next(&ef_a) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![a]));
+
+ // Check in reverse
+ let mut po = Vec::new();
+ let mut dfs = Dfs::new(&Reversed(&ef_a), a);
+ while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![a]));
+
+
+ let mut g = Graph::new();
+ let a = g.add_node("A");
+ let b = g.add_node("B");
+ let c = g.add_node("C");
+ let d = g.add_node("D");
+ let e = g.add_node("E");
+ let f = g.add_node("F");
+ let h = g.add_node("H");
+ let i = g.add_node("I");
+ let j = g.add_node("J");
+
+ g.add_edge(a, b, E::A);
+ g.add_edge(b, c, E::A);
+ g.add_edge(c, d, E::B);
+ g.add_edge(d, e, E::A);
+ g.add_edge(e, f, E::A);
+ g.add_edge(e, h, E::A);
+ g.add_edge(e, i, E::A);
+ g.add_edge(i, j, E::A);
+
+ let ef_a = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::A);
+ let ef_b = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::B);
+
+ // DFS down from a, filtered by E::A.
+ let mut po = Vec::new();
+ let mut dfs = Dfs::new(&ef_a, a);
+ while let Some(next_n_ix) = dfs.next(&ef_a) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![a, b, c]));
+
+ // Reversed DFS from f, filtered by E::A.
+ let mut dfs = Dfs::new(&Reversed(&ef_a), f);
+ let mut po = Vec::new();
+ while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![d, e, f]));
+
+ // Reversed DFS from j, filtered by E::A.
+ let mut dfs = Dfs::new(&Reversed(&ef_a), j);
+ let mut po = Vec::new();
+ while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![d, e, i, j]));
+
+ // Reversed DFS from c, filtered by E::A.
+ let mut dfs = Dfs::new(&Reversed(&ef_a), c);
+ let mut po = Vec::new();
+ while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![a, b, c]));
+
+ // Reversed DFS from c, filtered by E::B.
+ let mut dfs = Dfs::new(&Reversed(&ef_b), c);
+ let mut po = Vec::new();
+ while let Some(next_n_ix) = dfs.next(&Reversed(&ef_b)) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![c]));
+
+ // Reversed DFS from d, filtered by E::B.
+ let mut dfs = Dfs::new(&Reversed(&ef_b), d);
+ let mut po = Vec::new();
+ while let Some(next_n_ix) = dfs.next(&Reversed(&ef_b)) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![c, d]));
+
+ // Now let's test the same graph but undirected
+
+ let mut g = Graph::new_undirected();
+ let a = g.add_node("A");
+ let b = g.add_node("B");
+ let c = g.add_node("C");
+ let d = g.add_node("D");
+ let e = g.add_node("E");
+ let f = g.add_node("F");
+ let h = g.add_node("H");
+ let i = g.add_node("I");
+ let j = g.add_node("J");
+
+ g.add_edge(a, b, E::A);
+ g.add_edge(b, c, E::A);
+ g.add_edge(c, d, E::B);
+ g.add_edge(d, e, E::A);
+ g.add_edge(e, f, E::A);
+ g.add_edge(e, h, E::A);
+ g.add_edge(e, i, E::A);
+ g.add_edge(i, j, E::A);
+
+ let ef_a = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::A);
+ let ef_b = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::B);
+ let mut po = Vec::new();
+ let mut dfs = Dfs::new(&Reversed(&ef_b), d);
+ while let Some(next_n_ix) = dfs.next(&Reversed(&ef_b)) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![c, d]));
+
+ let mut po = Vec::new();
+ let mut dfs = Dfs::new(&Reversed(&ef_a), h);
+ while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
+ po.push(next_n_ix);
+ }
+ assert_eq!(set(po), set(vec![d, e, f, h, i, j]));
+}
#[test]
fn dfs_visit() {
@@ -1539,6 +1678,24 @@
}
path.reverse();
assert_eq!(&path, &[n(0), n(2), n(4)]);
+
+ // check that if we prune 2, we never see 4.
+ let start = n(0);
+ let prune = n(2);
+ let nongoal = n(4);
+ let ret = depth_first_search(&gr, Some(start), |event| {
+ if let Discover(n, _) = event {
+ if n == prune {
+ return Control::Prune;
+ }
+ } else if let TreeEdge(u, v) = event {
+ if v == nongoal {
+ return Control::Break(u);
+ }
+ }
+ Control::Continue
+ });
+ assert!(ret.break_value().is_none());
}
@@ -1711,7 +1868,7 @@
// http://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf.
let mut graph = DiGraph::<_, _>::new();
-
+
let r = graph.add_node("r");
let a = graph.add_node("a");
let b = graph.add_node("b");
diff --git a/tests/unionfind.rs b/tests/unionfind.rs
index f5e2bcf..ba3bdc5 100644
--- a/tests/unionfind.rs
+++ b/tests/unionfind.rs
@@ -1,7 +1,7 @@
extern crate rand;
extern crate petgraph;
-use rand::{Rng, thread_rng, ChaChaRng};
+use rand::{Rng, thread_rng, ChaChaRng, SeedableRng};
use std::collections::HashSet;
use petgraph::unionfind::UnionFind;
@@ -36,7 +36,7 @@
#[test]
fn uf_rand() {
let n = 1 << 14;
- let mut rng: ChaChaRng = thread_rng().gen();
+ let mut rng = ChaChaRng::from_rng(thread_rng()).unwrap();
let mut u = UnionFind::new(n);
for _ in 0..100 {
let a = rng.gen_range(0, n);
@@ -50,7 +50,7 @@
#[test]
fn uf_u8() {
let n = 256;
- let mut rng: ChaChaRng = thread_rng().gen();
+ let mut rng = ChaChaRng::from_rng(thread_rng()).unwrap();
let mut u = UnionFind::<u8>::new(n);
for _ in 0..(n * 8) {
let a = rng.gen();