use std::marker::PhantomData;
use anyhow::Result;
use crate::{DFSVisitor, DepthFirstSearch, EdgeID, NodeID, Nodes, VisitableGraph};
pub trait PathExists {
type Graph: VisitableGraph;
fn path_exists_opt(&self, from: impl AsRef<NodeID>, to: impl AsRef<NodeID>, excluded_edge: Option<impl AsRef<EdgeID>>) -> Result<bool>;
fn path_exists(&self, from: impl AsRef<NodeID>, to: impl AsRef<NodeID>) -> Result<bool> {
self.path_exists_opt(from, to, None::<EdgeID>)
}
fn can_add_dag_edge(&self, from: impl AsRef<NodeID>, to: impl AsRef<NodeID>, excluded_edge: Option<impl AsRef<EdgeID>>) -> Result<bool> {
Ok(!self.path_exists_opt(to, from, excluded_edge)?)
}
fn can_move_dag_edge(&self, edge: impl AsRef<EdgeID>, new_source: impl AsRef<NodeID>, new_target: impl AsRef<NodeID>) -> Result<bool> {
self.can_add_dag_edge(new_source, new_target, Some(edge))
}
}
impl<G> PathExists for G
where
G: DepthFirstSearch<G, bool> + VisitableGraph,
{
type Graph = G;
fn path_exists_opt(&self, from: impl AsRef<NodeID>, to: impl AsRef<NodeID>, excluded_edge: Option<impl AsRef<EdgeID>>) -> Result<bool> {
let from = from.as_ref();
let to = to.as_ref();
let mut visitor = PathExistsVisitor::new(from.clone(), to.clone());
self.depth_first_search_opt(&mut visitor, &Nodes::from([from.clone()]), false, excluded_edge)
}
}
pub struct PathExistsVisitor<Graph> {
from: NodeID,
to: NodeID,
graph: PhantomData<Graph>,
}
impl<Graph> PathExistsVisitor<Graph>
where
Graph: VisitableGraph,
{
pub fn new(from: NodeID, to: NodeID) -> Self {
Self {
from,
to,
graph: PhantomData,
}
}
}
impl<Graph> DFSVisitor for PathExistsVisitor<Graph>
where
Graph: VisitableGraph,
{
type Graph = Graph;
type Output = bool;
fn discover_node(&mut self, _graph: &Self::Graph, node: &NodeID) -> Result<Option<bool>> {
Ok(if node == &self.to { Some(true) } else { None })
}
fn finish_node(&mut self, _graph: &Self::Graph, node: &NodeID) -> Result<Option<bool>> {
Ok(if node == &self.from { Some(false) } else { None })
}
fn finish(&mut self, _graph: &Self::Graph) -> Result<bool> {
Ok(false)
}
}