#[cfg(feature = "parallel")]
pub mod parallel;
pub mod sequential;
#[derive(Debug)]
pub enum Graph<NodeId: U16orU32 = u16> {
Sequential(sequential::SeqGraph<NodeId>),
#[cfg(feature = "parallel")]
Parallel(parallel::ParaGraph<NodeId>),
}
impl<NodeId: U16orU32> Graph<NodeId> {
#[inline]
pub fn builder(nodes_len: usize) -> GraphBuilder<NodeId> {
assert!(
nodes_len <= NodeId::MAX_NODES,
"Number of nodes exceeds the limit; Specify `u32` as the NodeId type, like `Graph::<u32>::builder(100_000)`"
);
GraphBuilder::new(nodes_len)
}
pub fn into_builder(self) -> GraphBuilder<NodeId> {
let nodes_len = match &self {
Graph::Sequential(ref builder) => builder.nodes_len(),
#[cfg(feature = "parallel")]
Graph::Parallel(ref builder) => builder.nodes_len(),
};
let inner = match self {
Graph::Sequential(graph) => GraphBuilderEnum::Sequential(graph.into_builder()),
#[cfg(feature = "parallel")]
Graph::Parallel(graph) => GraphBuilderEnum::Parallel(graph.into_builder()),
};
let multi_threaded = match inner {
GraphBuilderEnum::Sequential(_) => Some(false),
#[cfg(feature = "parallel")]
GraphBuilderEnum::Parallel(_) => Some(true),
GraphBuilderEnum::None => unreachable!(),
};
GraphBuilder {
inner,
multi_threaded,
nodes_len,
}
}
#[inline]
pub fn neighbor_to(&self, curr: NodeId, dest: NodeId) -> Option<NodeId> {
self.neighbors_to(curr, dest).next()
}
#[inline]
pub fn neighbor_to_with(
&self,
curr: NodeId,
dest: NodeId,
f: impl Fn(NodeId) -> bool,
) -> Option<NodeId> {
self.neighbors_to(curr, dest).find(|&n| f(n))
}
#[inline]
pub fn neighbors_to(&self, curr: NodeId, dest: NodeId) -> NeighborsToIter<'_, NodeId> {
match self {
Graph::Sequential(graph) => NeighborsToIter::Sequential(graph.neighbors_to(curr, dest)),
#[cfg(feature = "parallel")]
Graph::Parallel(graph) => NeighborsToIter::Parallel(graph.neighbors_to(curr, dest)),
}
}
#[inline]
pub fn path_to(&self, curr: NodeId, dest: NodeId) -> PathIter<'_, NodeId> {
match self {
Graph::Sequential(graph) => PathIter::Sequential(graph.path_to(curr, dest)),
#[cfg(feature = "parallel")]
Graph::Parallel(graph) => PathIter::Parallel(graph.path_to(curr, dest)),
}
}
#[inline]
pub fn path_exists(&self, curr: NodeId, dest: NodeId) -> bool {
match self {
Graph::Sequential(graph) => graph.path_exists(curr, dest),
#[cfg(feature = "parallel")]
Graph::Parallel(graph) => graph.path_exists(curr, dest),
}
}
#[inline]
pub fn neighbors(&self, node: NodeId) -> &[NodeId] {
match self {
Graph::Sequential(graph) => graph.neighbors(node),
#[cfg(feature = "parallel")]
Graph::Parallel(graph) => graph.neighbors(node),
}
}
#[inline]
pub fn nodes_len(&self) -> usize {
match self {
Graph::Sequential(graph) => graph.nodes_len(),
#[cfg(feature = "parallel")]
Graph::Parallel(graph) => graph.nodes_len(),
}
}
#[inline]
pub fn edges_len(&self) -> usize {
match self {
Graph::Sequential(graph) => graph.edges_len(),
#[cfg(feature = "parallel")]
Graph::Parallel(graph) => graph.edges_len(),
}
}
}
#[derive(Debug)]
pub enum PathIter<'a, NodeId: U16orU32> {
Sequential(sequential::PathIter<'a, NodeId>),
#[cfg(feature = "parallel")]
Parallel(parallel::PathIter<'a, NodeId>),
}
impl<NodeId: U16orU32> Iterator for PathIter<'_, NodeId> {
type Item = NodeId;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self {
PathIter::Sequential(iter) => iter.next(),
#[cfg(feature = "parallel")]
PathIter::Parallel(iter) => iter.next(),
}
}
}
#[derive(Debug)]
pub enum NeighborsToIter<'a, NodeId: U16orU32> {
Sequential(sequential::NeighborsToIter<'a, NodeId>),
#[cfg(feature = "parallel")]
Parallel(parallel::NeighborsToIter<'a, NodeId>),
}
impl<NodeId: U16orU32> Iterator for NeighborsToIter<'_, NodeId> {
type Item = NodeId;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self {
NeighborsToIter::Sequential(iter) => iter.next(),
#[cfg(feature = "parallel")]
NeighborsToIter::Parallel(iter) => iter.next(),
}
}
}
#[derive(Debug)]
pub struct GraphBuilder<NodeId: U16orU32 = u16> {
inner: GraphBuilderEnum<NodeId>,
multi_threaded: Option<bool>,
nodes_len: usize,
}
#[derive(Debug)]
enum GraphBuilderEnum<NodeId: U16orU32> {
Sequential(sequential::SeqGraphBuilder<NodeId>),
#[cfg(feature = "parallel")]
Parallel(parallel::ParaGraphBuilder<NodeId>),
None,
}
impl<NodeId: U16orU32> GraphBuilderEnum<NodeId> {
#[inline]
fn is_none(&self) -> bool {
matches!(self, GraphBuilderEnum::None)
}
#[allow(unused_variables)]
fn set_builder(&mut self, nodes_len: usize, multi_threaded: Option<bool>) {
#[cfg(feature = "parallel")]
let builder = {
let multi_threaded = multi_threaded.unwrap_or_else(|| {
let available_parallelism = std::thread::available_parallelism()
.map(|e| e.get())
.unwrap_or(1);
available_parallelism > 1
});
if multi_threaded {
GraphBuilderEnum::Parallel(parallel::ParaGraphBuilder::new(nodes_len))
} else {
GraphBuilderEnum::Sequential(sequential::SeqGraphBuilder::new(nodes_len))
}
};
#[cfg(not(feature = "parallel"))]
let builder = GraphBuilderEnum::Sequential(sequential::SeqGraphBuilder::new(nodes_len));
*self = builder;
}
}
impl<NodeId: U16orU32> GraphBuilder<NodeId> {
#[inline]
pub fn new(nodes_len: usize) -> Self {
GraphBuilder {
inner: GraphBuilderEnum::None,
multi_threaded: None,
nodes_len,
}
}
#[cfg(feature = "parallel")]
#[inline]
pub fn multi_threaded(mut self, multi_threaded: bool) -> Self {
self.multi_threaded = Some(multi_threaded);
self
}
pub fn resize(&mut self, nodes_len: usize) {
if self.inner.is_none() {
self.inner.set_builder(self.nodes_len, self.multi_threaded);
}
match &mut self.inner {
GraphBuilderEnum::Sequential(builder) => builder.resize(nodes_len),
#[cfg(feature = "parallel")]
GraphBuilderEnum::Parallel(builder) => builder.resize(nodes_len),
GraphBuilderEnum::None => unreachable!(),
}
}
#[inline]
pub fn connect(&mut self, a: NodeId, b: NodeId) {
if self.inner.is_none() {
self.inner.set_builder(self.nodes_len, self.multi_threaded);
}
match &mut self.inner {
GraphBuilderEnum::Sequential(builder) => builder.connect(a, b),
#[cfg(feature = "parallel")]
GraphBuilderEnum::Parallel(builder) => builder.connect(a, b),
GraphBuilderEnum::None => unreachable!(),
}
}
#[inline]
pub fn disconnect(&mut self, a: NodeId, b: NodeId) {
if self.inner.is_none() {
self.inner.set_builder(self.nodes_len, self.multi_threaded);
}
match &mut self.inner {
GraphBuilderEnum::Sequential(builder) => builder.disconnect(a, b),
#[cfg(feature = "parallel")]
GraphBuilderEnum::Parallel(builder) => builder.disconnect(a, b),
GraphBuilderEnum::None => unreachable!(),
}
}
#[inline]
pub fn build(self) -> Graph<NodeId> {
let mut builder = self.inner;
if builder.is_none() {
builder.set_builder(self.nodes_len, self.multi_threaded);
}
match builder {
GraphBuilderEnum::Sequential(builder) => Graph::Sequential(builder.build()),
#[cfg(feature = "parallel")]
GraphBuilderEnum::Parallel(builder) => Graph::Parallel(builder.build()),
GraphBuilderEnum::None => unreachable!(),
}
}
#[inline]
pub fn nodes_len(&self) -> usize {
match self {
GraphBuilder {
inner: GraphBuilderEnum::Sequential(builder),
..
} => builder.nodes_len(),
#[cfg(feature = "parallel")]
GraphBuilder {
inner: GraphBuilderEnum::Parallel(builder),
..
} => builder.nodes_len(),
GraphBuilder {
inner: GraphBuilderEnum::None,
nodes_len,
..
} => *nodes_len,
}
}
#[inline]
pub fn edges_len(&self) -> usize {
match self {
GraphBuilder {
inner: GraphBuilderEnum::Sequential(builder),
..
} => builder.edges_len(),
#[cfg(feature = "parallel")]
GraphBuilder {
inner: GraphBuilderEnum::Parallel(builder),
..
} => builder.edges_len(),
GraphBuilder {
inner: GraphBuilderEnum::None,
..
} => 0,
}
}
#[inline]
pub fn neighbors(&self, node: NodeId) -> &[NodeId] {
match self {
GraphBuilder {
inner: GraphBuilderEnum::Sequential(builder),
..
} => builder.neighbors(node),
#[cfg(feature = "parallel")]
GraphBuilder {
inner: GraphBuilderEnum::Parallel(builder),
..
} => builder.neighbors(node),
GraphBuilder {
inner: GraphBuilderEnum::None,
..
} => &[],
}
}
}
pub trait U16orU32: sealed::Sealed {
const MAX_NODES: usize;
fn as_usize(self) -> usize;
fn from_usize(value: usize) -> Self;
}
mod sealed {
use std::fmt;
use super::*;
pub trait Sealed:
Ord + Eq + Clone + Copy + std::hash::Hash + Send + Sync + fmt::Display + fmt::Debug
{
}
impl Sealed for u16 {}
impl Sealed for u32 {}
impl U16orU32 for u16 {
const MAX_NODES: usize = 1 << 16;
#[inline]
fn as_usize(self) -> usize {
self as usize
}
#[inline]
fn from_usize(value: usize) -> Self {
value as u16
}
}
impl U16orU32 for u32 {
const MAX_NODES: usize = 1 << 32;
#[inline]
fn as_usize(self) -> usize {
self as usize
}
#[inline]
fn from_usize(value: usize) -> Self {
value as u32
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[ignore]
#[test]
fn test_graph() {
let mut builder = Graph::builder(10000);
for y in 0..100u16 {
for x in 0..100 {
let node = y * 100 + x;
if x < 99 {
builder.connect(node, node + 1);
}
if y < 99 {
builder.connect(node, node + 100);
}
}
}
let graph = builder.build();
let mut curr = 0;
let dest = 9900;
let mut hops = 0;
while curr != dest {
let prev = curr;
curr = graph.neighbor_to(curr, dest).unwrap();
println!("{prev} -> {curr}");
hops += 1;
if curr == dest {
println!("we've reached node '{dest}' in {hops} hops!");
break;
}
}
}
}