use crate::traits::{RandomAccessGraph, RandomAccessLabeling};
use crate::visits::{
Sequential,
breadth_first::{EventPred, FilterArgsPred},
};
use anyhow::Result;
use nonmax::NonMaxUsize;
use std::{collections::VecDeque, ops::ControlFlow, ops::ControlFlow::Continue};
use sux::bits::BitVec;
use sux::traits::BitVecOpsMut;
pub struct Seq<'a, G: RandomAccessGraph> {
graph: &'a G,
visited: BitVec,
queue: VecDeque<Option<NonMaxUsize>>,
}
impl<'a, G: RandomAccessGraph> Seq<'a, G> {
pub fn new(graph: &'a G) -> Self {
let num_nodes = graph.num_nodes();
assert_ne!(
num_nodes,
usize::MAX,
"The BFS Seq visit cannot be used on graphs with usize::MAX nodes."
);
Self {
graph,
visited: BitVec::new(num_nodes),
queue: VecDeque::new(),
}
}
pub fn iter_from_roots(
&mut self,
roots: impl IntoIterator<Item = usize>,
) -> Result<BfsOrderFromRoots<'_, 'a, G>> {
BfsOrderFromRoots::new(self, roots)
}
}
impl<'a, G: RandomAccessGraph> Sequential<EventPred> for Seq<'a, G> {
fn visit_filtered_with<
R: IntoIterator<Item = usize>,
T,
E,
C: FnMut(&mut T, EventPred) -> ControlFlow<E, ()>,
F: FnMut(&mut T, FilterArgsPred) -> bool,
>(
&mut self,
roots: R,
mut init: T,
mut callback: C,
mut filter: F,
) -> ControlFlow<E, ()> {
self.queue.clear();
for root in roots {
if self.visited[root]
|| !filter(
&mut init,
FilterArgsPred {
node: root,
pred: root,
distance: 0,
},
)
{
continue;
}
if self.queue.is_empty() {
callback(&mut init, EventPred::Init {})?;
}
self.visited.set(root, true);
self.queue.push_back(Some(
NonMaxUsize::new(root).expect("node index should never be usize::MAX"),
));
callback(
&mut init,
EventPred::Visit {
node: root,
pred: root,
distance: 0,
},
)?;
}
if self.queue.is_empty() {
return Continue(());
}
callback(
&mut init,
EventPred::FrontierSize {
distance: 0,
size: self.queue.len(),
},
)?;
self.queue.push_back(None);
let mut distance = 1;
while let Some(current_node) = self.queue.pop_front() {
match current_node {
Some(node) => {
let node = node.into();
for succ in self.graph.successors(node) {
let (node, pred) = (succ, node);
if !self.visited[succ] {
if filter(
&mut init,
FilterArgsPred {
node,
pred,
distance,
},
) {
self.visited.set(succ, true);
callback(
&mut init,
EventPred::Visit {
node,
pred,
distance,
},
)?;
self.queue.push_back(Some(
NonMaxUsize::new(succ)
.expect("node index should never be usize::MAX"),
))
}
} else {
callback(&mut init, EventPred::Revisit { node, pred })?;
}
}
}
None => {
if !self.queue.is_empty() {
callback(
&mut init,
EventPred::FrontierSize {
distance,
size: self.queue.len(),
},
)?;
distance += 1;
self.queue.push_back(None);
}
}
}
}
callback(&mut init, EventPred::Done {})
}
fn reset(&mut self) {
self.queue.clear();
self.visited.fill(false);
}
}
impl<'a, 'b, G: RandomAccessGraph> IntoIterator for &'a mut Seq<'b, G> {
type Item = IterEvent;
type IntoIter = BfsOrder<'a, 'b, G>;
fn into_iter(self) -> Self::IntoIter {
BfsOrder::new(self)
}
}
pub struct BfsOrder<'a, 'b, G: RandomAccessGraph> {
visit: &'a mut Seq<'b, G>,
root: usize,
parent: usize,
distance: usize,
succ: <<G as RandomAccessLabeling>::Labels<'a> as IntoIterator>::IntoIter,
visited_nodes: usize,
}
impl<'a, 'b, G: RandomAccessGraph> BfsOrder<'a, 'b, G> {
pub fn new(visit: &'a mut Seq<'b, G>) -> BfsOrder<'a, 'b, G> {
assert!(
visit.graph.num_nodes() > 0,
"BfsOrder requires a non-empty graph"
);
visit.reset(); let succ = visit.graph.successors(0).into_iter();
BfsOrder {
visit,
root: 0,
parent: 0,
distance: 0,
succ,
visited_nodes: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IterEvent {
pub root: usize,
pub parent: usize,
pub node: usize,
pub distance: usize,
}
impl<'a, 'b, G: RandomAccessGraph> Iterator for BfsOrder<'a, 'b, G> {
type Item = IterEvent;
fn next(&mut self) -> Option<Self::Item> {
if self.visited_nodes == 0 {
self.visited_nodes += 1;
self.visit.visited.set(self.root, true);
self.visit.queue.push_back(None);
self.distance = 1;
return Some(IterEvent {
root: self.root,
parent: self.root,
node: self.root,
distance: 0,
});
}
loop {
for succ in &mut self.succ {
if self.visit.visited[succ] {
continue; }
let node = NonMaxUsize::new(succ);
debug_assert!(node.is_some(), "Node index should never be usize::MAX");
let node = unsafe { node.unwrap_unchecked() };
self.visit.queue.push_back(Some(node));
self.visit.visited.set(succ as _, true);
self.visited_nodes += 1;
return Some(IterEvent {
root: self.root,
parent: self.parent,
node: succ,
distance: self.distance,
});
}
loop {
match self.visit.queue.pop_front().expect(
"Queue should never be empty here, as we always add a level separator after the first node.",
) {
Some(node) => {
self.parent = node.into();
self.succ = self.visit.graph.successors(self.parent).into_iter();
break;
}
None => {
if !self.visit.queue.is_empty() {
self.distance += 1;
self.visit.queue.push_back(None);
continue;
}
while self.visit.visited[self.root] {
self.root += 1;
if self.root >= self.visit.graph.num_nodes() {
return None;
}
}
self.visited_nodes += 1;
self.visit.visited.set(self.root, true);
self.visit.queue.push_back(None);
self.distance = 1;
self.parent = self.root;
self.succ = self.visit.graph.successors(self.root).into_iter();
return Some(IterEvent {
root: self.root,
parent: self.root,
node: self.root,
distance: 0,
});
}
}
}
}
}
}
impl<'a, 'b, G: RandomAccessGraph> ExactSizeIterator for BfsOrder<'a, 'b, G> {
fn len(&self) -> usize {
self.visit.graph.num_nodes() - self.visited_nodes
}
}
pub struct BfsOrderFromRoots<'a, 'b, G: RandomAccessGraph> {
visit: &'a mut Seq<'b, G>,
parent: usize,
distance: usize,
succ: <<G as RandomAccessLabeling>::Labels<'a> as IntoIterator>::IntoIter,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IterFromRootsEvent {
pub parent: usize,
pub node: usize,
pub distance: usize,
}
impl<'a, 'b, G: RandomAccessGraph> BfsOrderFromRoots<'a, 'b, G> {
pub fn new(
visit: &'a mut Seq<'b, G>,
roots: impl IntoIterator<Item = usize>,
) -> Result<BfsOrderFromRoots<'a, 'b, G>> {
visit.reset(); visit.queue.extend(
roots
.into_iter()
.map(|root| Some(NonMaxUsize::new(root).unwrap())),
);
anyhow::ensure!(
!visit.queue.is_empty(),
"Cannot create BfsOrderFromRoots with empty roots"
);
visit.queue.push_back(None);
let first_root: usize = visit.queue[0].unwrap().into();
let succ = visit.graph.successors(first_root).into_iter();
Ok(BfsOrderFromRoots {
visit,
parent: first_root,
distance: 0,
succ,
})
}
}
impl<'a, 'b, G: RandomAccessGraph> Iterator for BfsOrderFromRoots<'a, 'b, G> {
type Item = IterFromRootsEvent;
fn next(&mut self) -> Option<Self::Item> {
if self.distance == 0 {
let element = self.visit.queue.pop_front().unwrap();
if let Some(node) = element {
let node = node.into();
self.visit.visited.set(node, true);
self.visit.queue.push_back(element);
return Some(IterFromRootsEvent {
parent: node,
node,
distance: 0,
});
} else {
self.distance = 1;
self.visit.queue.push_back(None);
}
}
loop {
for succ in &mut self.succ {
if self.visit.visited[succ] {
continue;
}
let node = NonMaxUsize::new(succ);
debug_assert!(node.is_some(), "Node index should never be usize::MAX");
let node = unsafe { node.unwrap_unchecked() };
self.visit.queue.push_back(Some(node));
self.visit.visited.set(succ as _, true);
return Some(IterFromRootsEvent {
parent: self.parent,
node: succ,
distance: self.distance,
});
}
'inner: loop {
match self.visit.queue.pop_front().expect(
"Queue should never be empty here, as we always add a level separator after the first node.",
) {
Some(node) => {
self.parent = node.into();
self.succ = self.visit.graph.successors(self.parent).into_iter();
break 'inner;
}
None => {
if self.visit.queue.is_empty() {
return None;
}
self.visit.queue.push_back(None);
self.distance += 1;
continue 'inner;
}
}
}
}
}
}