use alloc::vec::Vec;
use core::{iter::FusedIterator, marker::PhantomData};
use oxgraph_topology::{
ContainsElement, ElementId, ElementIndex, ElementPredecessors, ElementSuccessors,
};
use crate::bfs::{
BfsError,
core::{Bfs, EpochCounter, Forward, Reverse, SeededWorkspaceFrontier, ValidatedStart},
};
#[derive(Clone, Debug)]
pub struct BfsWorkspace<G>
where
G: ContainsElement + ElementIndex,
{
marks: Vec<u32>,
queue: Vec<ElementId<G>>,
epoch: EpochCounter,
_graph: PhantomData<fn() -> G>,
}
impl<G> Default for BfsWorkspace<G>
where
G: ContainsElement + ElementIndex,
{
fn default() -> Self {
Self::new()
}
}
impl<G> BfsWorkspace<G>
where
G: ContainsElement + ElementIndex,
{
#[must_use]
pub const fn new() -> Self {
Self {
marks: Vec::new(),
queue: Vec::new(),
epoch: EpochCounter::new(),
_graph: PhantomData,
}
}
#[must_use]
pub fn for_graph(graph: &G) -> Self {
Self::with_element_bound(graph.element_bound())
}
#[must_use]
pub fn with_element_bound(element_bound: usize) -> Self {
Self {
marks: alloc::vec![0; element_bound],
queue: Vec::with_capacity(element_bound),
epoch: EpochCounter::new(),
_graph: PhantomData,
}
}
#[must_use]
pub const fn element_bound_capacity(&self) -> usize {
self.marks.len()
}
fn ensure_element_bound(&mut self, element_bound: usize) {
if self.marks.len() < element_bound {
self.marks.resize(element_bound, 0);
}
if self.queue.capacity() < element_bound {
self.queue.reserve(element_bound - self.queue.capacity());
}
}
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct BreadthFirstSearchWorkspace<'graph, 'workspace, G>
where
G: ContainsElement + ElementSuccessors + ElementIndex,
{
inner: Bfs<'graph, G, SeededWorkspaceFrontier<'workspace, G>, Forward>,
}
impl<G> Iterator for BreadthFirstSearchWorkspace<'_, '_, G>
where
G: ContainsElement + ElementSuccessors + ElementIndex,
{
type Item = ElementId<G>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl<G> FusedIterator for BreadthFirstSearchWorkspace<'_, '_, G> where
G: ContainsElement + ElementSuccessors + ElementIndex
{
}
pub fn breadth_first_search_with_workspace<'graph, 'workspace, G>(
graph: &'graph G,
start: ElementId<G>,
workspace: &'workspace mut BfsWorkspace<G>,
) -> Result<BreadthFirstSearchWorkspace<'graph, 'workspace, G>, BfsError>
where
G: ContainsElement + ElementSuccessors + ElementIndex,
{
let witness = ValidatedStart::new(graph, start)?;
let bound = witness.bound();
workspace.ensure_element_bound(bound);
let epoch = workspace.epoch.advance(&mut workspace.marks);
let frontier = SeededWorkspaceFrontier::new(
&mut workspace.marks,
&mut workspace.queue,
epoch,
start,
witness,
);
Ok(BreadthFirstSearchWorkspace {
inner: Bfs::new(graph, frontier),
})
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ReverseBreadthFirstSearchWorkspace<'graph, 'workspace, G>
where
G: ContainsElement + ElementPredecessors + ElementIndex,
{
inner: Bfs<'graph, G, SeededWorkspaceFrontier<'workspace, G>, Reverse>,
}
impl<G> Iterator for ReverseBreadthFirstSearchWorkspace<'_, '_, G>
where
G: ContainsElement + ElementPredecessors + ElementIndex,
{
type Item = ElementId<G>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl<G> FusedIterator for ReverseBreadthFirstSearchWorkspace<'_, '_, G> where
G: ContainsElement + ElementPredecessors + ElementIndex
{
}
pub fn reverse_breadth_first_search_with_workspace<'graph, 'workspace, G>(
graph: &'graph G,
start: ElementId<G>,
workspace: &'workspace mut BfsWorkspace<G>,
) -> Result<ReverseBreadthFirstSearchWorkspace<'graph, 'workspace, G>, BfsError>
where
G: ContainsElement + ElementPredecessors + ElementIndex,
{
let witness = ValidatedStart::new(graph, start)?;
let bound = witness.bound();
workspace.ensure_element_bound(bound);
let epoch = workspace.epoch.advance(&mut workspace.marks);
let frontier = SeededWorkspaceFrontier::new(
&mut workspace.marks,
&mut workspace.queue,
epoch,
start,
witness,
);
Ok(ReverseBreadthFirstSearchWorkspace {
inner: Bfs::new(graph, frontier),
})
}