Skip to content

Commit cdabf13

Browse files
committed
Rewritten BFSOrder so it returns node, parent, distance, and root
1 parent c1de7da commit cdabf13

File tree

1 file changed

+80
-44
lines changed
  • algo/src/visits/breadth_first

1 file changed

+80
-44
lines changed

algo/src/visits/breadth_first/seq.rs

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ impl<'a, G: RandomAccessGraph> Seq<'a, G> {
119119
/// * `graph`: an immutable reference to the graph to visit.
120120
pub fn new(graph: &'a G) -> Self {
121121
let num_nodes = graph.num_nodes();
122+
assert_ne!(
123+
num_nodes,
124+
usize::MAX,
125+
"The BFS Seq visit cannot be used on graphs with usize::MAX nodes."
126+
);
122127
Self {
123128
graph,
124129
visited: BitVec::new(num_nodes),
@@ -257,7 +262,7 @@ impl<'a, G: RandomAccessGraph> Sequential<EventPred> for Seq<'a, G> {
257262
}
258263

259264
impl<'a, 'b, G: RandomAccessGraph> IntoIterator for &'a mut Seq<'b, G> {
260-
type Item = IterItem;
265+
type Item = IterEvent;
261266
type IntoIter = BfsOrder<'a, 'b, G>;
262267

263268
fn into_iter(self) -> Self::IntoIter {
@@ -268,17 +273,15 @@ impl<'a, 'b, G: RandomAccessGraph> IntoIterator for &'a mut Seq<'b, G> {
268273
/// Iterator on **all nodes** of the graph in a BFS order
269274
pub struct BfsOrder<'a, 'b, G: RandomAccessGraph> {
270275
visit: &'a mut Seq<'b, G>,
271-
/// If the queue is empty, resume the BFS from that node.
272-
///
273-
/// This allows initializing the BFS from all orphan nodes without reading
274-
/// the reverse graph.
275-
start: usize,
276-
/// The current node being visited.
277-
current_node: usize,
276+
/// The root of the current visit.
277+
root: usize,
278+
/// The current node being enumerated, i.e. the parent of the nodes returned
279+
/// by `succ`
280+
parent: usize,
278281
/// The current distance from the root.
279282
distance: usize,
280-
/// The successors of the current node, this is done to be able to return
281-
/// also the predecessor.
283+
/// The successors of the `parent` node, this is done to be able to return
284+
/// also the parent.
282285
succ: <<G as RandomAccessLabeling>::Labels<'a> as IntoIterator>::IntoIter,
283286
/// Number of visited nodes, used to compute the length of the iterator.
284287
visited_nodes: usize,
@@ -290,81 +293,114 @@ impl<'a, 'b, G: RandomAccessGraph> BfsOrder<'a, 'b, G> {
290293
let succ = visit.graph.successors(0).into_iter();
291294
BfsOrder {
292295
visit,
293-
start: 0,
294-
current_node: 0,
296+
root: 0,
297+
parent: 0,
295298
distance: 0,
296299
succ,
297300
visited_nodes: 0,
298301
}
299302
}
300303
}
301304

302-
pub struct IterItem {
305+
/// An event returned by the BFS iterator [`BfsOrder`].
306+
pub struct IterEvent {
307+
/// The root of the current visit
308+
pub root: usize,
309+
/// The parent of the current node
303310
pub parent: usize,
311+
/// The current node being visited
304312
pub node: usize,
313+
/// The distance of the current node from the root
305314
pub distance: usize,
306315
}
307316

308317
impl<'a, 'b, G: RandomAccessGraph> Iterator for BfsOrder<'a, 'b, G> {
309-
type Item = IterItem;
318+
type Item = IterEvent;
310319

311320
fn next(&mut self) -> Option<Self::Item> {
321+
// handle the first node separately, as we need to pre-fill the succ
322+
// iterator to be able to implement `new`
323+
if self.visited_nodes == 0 {
324+
self.visited_nodes += 1;
325+
self.visit.visited.set(self.root, true);
326+
self.visit.queue.push_back(None);
327+
return Some(IterEvent {
328+
root: self.root,
329+
parent: self.root,
330+
node: self.root,
331+
distance: 0,
332+
});
333+
}
312334
loop {
313335
// fast path, if the successors iterator is not exhausted, we can just return the next node
314-
if let Some(succ) = self.succ.next() {
336+
for succ in &mut self.succ {
315337
if self.visit.visited[succ] {
316338
continue; // skip already visited nodes
317339
}
340+
318341
// if it's a new node, we visit it and add it to the queue
319342
// of nodes whose successors we will visit
320-
self.visit.queue.push_back(Some(
321-
NonMaxUsize::new(succ).expect("node index should never be usize::MAX"),
322-
));
343+
let node = NonMaxUsize::new(succ);
344+
debug_assert!(node.is_some(), "Node index should never be usize::MAX");
345+
let node = unsafe { node.unwrap_unchecked() };
346+
self.visit.queue.push_back(Some(node));
347+
323348
self.visit.visited.set(succ as _, true);
324349
self.visited_nodes += 1;
325-
return Some(IterItem {
326-
parent: self.current_node,
350+
return Some(IterEvent {
351+
root: self.root,
352+
parent: self.parent,
327353
node: succ,
328354
distance: self.distance,
329355
});
330356
}
331357

332358
// the successors are exhausted, so we need to move to the next node
333-
self.current_node = loop {
334-
match self.visit.queue.pop_front() {
359+
loop {
360+
match self.visit.queue.pop_front().expect(
361+
"Queue should never be empty here, as we always add a level separator after the first node.",
362+
) {
335363
// if we have a node, we can continue visiting its successors
336-
Some(Some(node)) => break node.into(),
364+
Some(node) => {
365+
self.parent = node.into();
366+
// reset the successors iterator for the new current node
367+
self.succ = self.visit.graph.successors(self.parent).into_iter();
368+
break;
369+
}
337370
// new level separator, so we increment the distance
338-
Some(None) => {
339-
self.distance += 1;
371+
None => {
340372
// if the queue is not empty, we need to add a new level separator
341373
if !self.visit.queue.is_empty() {
374+
self.distance += 1;
342375
self.visit.queue.push_back(None);
376+
continue;
343377
}
344-
continue;
345-
}
346-
// if the queue is empty, we need to find the next unvisited node
347-
None => {
348-
while self.visit.visited[self.start] {
349-
self.start += 1;
350-
if self.start >= self.visit.graph.num_nodes() {
378+
self.distance = 0; // new visits, new distance
379+
380+
// the queue is empty, we need to find the next unvisited node
381+
while self.visit.visited[self.root] {
382+
self.root += 1;
383+
if self.root >= self.visit.graph.num_nodes() {
351384
return None;
352385
}
353386
}
354-
self.visit.visited.set(self.start, true);
355-
self.distance = 0; // new visits, new distance
356-
break self.start
387+
388+
self.visited_nodes += 1;
389+
self.visit.visited.set(self.root, true);
390+
self.visit.queue.push_back(None);
391+
392+
self.parent = self.root;
393+
self.succ = self.visit.graph.successors(self.root).into_iter();
394+
395+
return Some(IterEvent {
396+
root: self.root,
397+
parent: self.root,
398+
node: self.root,
399+
distance: self.distance,
400+
});
357401
}
358402
}
359-
};
360-
// reset the successors iterator for the new current node
361-
self.succ = self.visit.graph.successors(self.current_node).into_iter();
362-
self.visited_nodes += 1;
363-
return Some(IterItem {
364-
parent: self.current_node,
365-
node: self.current_node,
366-
distance: self.distance,
367-
});
403+
}
368404
}
369405
}
370406
}

0 commit comments

Comments
 (0)