use std::cmp;
use std::marker::PhantomData;
#[cfg(feature = "serde-1")]
use serde::{Deserialize, Serialize};
use petgraph::data::{Build, DataMap, DataMapMut};
use petgraph::visit::{
Data, EdgeCount, EdgeRef, GraphBase, GraphProp, IntoEdges, IntoEdgesDirected, NodeCount,
};
use petgraph::{
graph::{DefaultIx, EdgeIndex, IndexType, NodeIndex},
stable_graph::StableGraph,
Directed, Direction, EdgeType, Graph, IntoWeightedEdge, Undirected,
};
use crate::{BoundedGraphError, BoundedNode};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde-1", derive(Serialize, Deserialize))]
pub struct BoundedGraph<N, E, G = Graph<N, E, Directed, DefaultIx>> {
pub(crate) graph: G,
pub(crate) _phantom: PhantomData<(N, E)>,
}
pub type BoundedDiGraph<N, E> = BoundedGraph<N, E, Graph<N, E, Directed, DefaultIx>>;
pub type BoundedUnGraph<N, E> = BoundedGraph<N, E, Graph<N, E, Undirected, DefaultIx>>;
pub type BoundedStableDiGraph<N, E> = BoundedGraph<N, E, StableGraph<N, E, Directed, DefaultIx>>;
pub type BoundedStableUnGraph<N, E> = BoundedGraph<N, E, StableGraph<N, E, Undirected, DefaultIx>>;
impl<N, E, Ty, Ix, G> BoundedGraph<N, E, G>
where
Ty: EdgeType,
Ix: IndexType,
N: BoundedNode<Ix>,
E: Clone,
G: GraphBase<NodeId = NodeIndex<Ix>, EdgeId = EdgeIndex<Ix>>
+ Data<NodeWeight = N, EdgeWeight = E>
+ GraphProp<EdgeType = Ty>
+ DataMap
+ DataMapMut
+ Build
+ NodeCount
+ EdgeCount,
{
pub fn new() -> Self
where
G: Default,
{
Self {
graph: G::default(),
_phantom: PhantomData,
}
}
#[must_use = "checking edge capacity is only useful if the result is used"]
pub fn can_add_edge(&self, node: NodeIndex<Ix>, other: NodeIndex<Ix>) -> bool
where
for<'a> &'a G: GraphBase<NodeId = NodeIndex<Ix>> + IntoEdgesDirected,
{
let (o_weight, i_weight) =
match (self.graph.node_weight(node), self.graph.node_weight(other)) {
(Some(o), Some(i)) => (o, i),
_ => return false,
};
let outgoing_count = (&self.graph)
.edges_directed(node, Direction::Outgoing)
.count();
let incoming_count = (&self.graph)
.edges_directed(other, Direction::Incoming)
.count();
o_weight.can_add_edge(Direction::Outgoing, outgoing_count, i_weight)
&& i_weight.can_add_edge(Direction::Incoming, incoming_count, o_weight)
}
#[must_use = "the node index is needed to reference the added node"]
pub fn add_node(&mut self, node: N) -> NodeIndex<Ix> {
self.graph.add_node(node)
}
#[must_use = "edge addition may fail due to capacity constraints"]
pub fn add_edge(
&mut self,
source: NodeIndex<Ix>,
target: NodeIndex<Ix>,
edge: E,
) -> Result<EdgeIndex<Ix>, BoundedGraphError<Ix>>
where
for<'a> &'a G: GraphBase<NodeId = NodeIndex<Ix>> + IntoEdgesDirected,
{
let (source_weight, target_weight) = match (
self.graph.node_weight(source),
self.graph.node_weight(target),
) {
(Some(s), Some(t)) => (s, t),
(None, None) => return Err(BoundedGraphError::not_found(None, None)),
(None, Some(_)) => return Err(BoundedGraphError::not_found(None, Some(target))),
(Some(_), None) => return Err(BoundedGraphError::not_found(Some(source), None)),
};
let outgoing_count = (&self.graph)
.edges_directed(source, Direction::Outgoing)
.count();
let incoming_count = (&self.graph)
.edges_directed(target, Direction::Incoming)
.count();
let source_has_space =
source_weight.can_add_edge(Direction::Outgoing, outgoing_count, target_weight);
let target_has_space =
target_weight.can_add_edge(Direction::Incoming, incoming_count, source_weight);
match (source_has_space, target_has_space) {
(true, true) => {
<G as petgraph::data::Build>::add_edge(&mut self.graph, source, target, edge)
.ok_or_else(|| BoundedGraphError::not_found(Some(source), Some(target)))
}
(false, true) => Err(BoundedGraphError::source_rejected(source, target)),
(true, false) => Err(BoundedGraphError::target_rejected(source, target)),
(false, false) => Err(BoundedGraphError::both_rejected(source, target)),
}
}
#[must_use = "edge update may fail due to capacity constraints"]
pub fn update_edge(
&mut self,
a: NodeIndex<Ix>,
b: NodeIndex<Ix>,
weight: E,
) -> Result<EdgeIndex<Ix>, BoundedGraphError<Ix>>
where
for<'a> &'a G: GraphBase<NodeId = NodeIndex<Ix>> + IntoEdges + IntoEdgesDirected,
{
let edge_exists = (&self.graph).edges(a).any(|e| e.target() == b);
if edge_exists {
Ok(self.graph.update_edge(a, b, weight))
} else {
self.add_edge(a, b, weight)
}
}
pub fn extend_with_edges<I>(&mut self, iterable: I)
where
I: IntoIterator,
I::Item: IntoWeightedEdge<E>,
<I::Item as IntoWeightedEdge<E>>::NodeId: Into<NodeIndex<Ix>>,
N: Default,
for<'a> &'a G: GraphBase<NodeId = NodeIndex<Ix>> + IntoEdgesDirected,
{
let iter = iterable.into_iter();
let (_low, _) = iter.size_hint();
for elt in iter {
let (source, target, weight) = elt.into_weighted_edge();
let (source, target) = (source.into(), target.into());
let nx = cmp::max(source, target);
while nx.index() >= self.graph.node_count() {
let _ = self.add_node(N::default());
}
let _ = self.add_edge(source, target, weight);
}
}
#[must_use = "returns edge indices and potential errors that should be checked"]
pub fn try_extend_with_edges<I>(
&mut self,
iterable: I,
) -> Result<Vec<EdgeIndex<Ix>>, (Vec<EdgeIndex<Ix>>, BoundedGraphError<Ix>)>
where
I: IntoIterator,
I::Item: IntoWeightedEdge<E>,
<I::Item as IntoWeightedEdge<E>>::NodeId: Into<NodeIndex<Ix>>,
N: Default,
for<'a> &'a G: GraphBase<NodeId = NodeIndex<Ix>> + IntoEdgesDirected,
{
let iter = iterable.into_iter();
let (low, _) = iter.size_hint();
let mut added_edges = Vec::with_capacity(low);
for elt in iter {
let (source, target, weight) = elt.into_weighted_edge();
let (source, target) = (source.into(), target.into());
let nx = cmp::max(source, target);
while nx.index() >= self.graph.node_count() {
let _ = self.add_node(N::default());
}
match self.add_edge(source, target, weight) {
Ok(edge_idx) => added_edges.push(edge_idx),
Err(err) => return Err((added_edges, err)),
}
}
Ok(added_edges)
}
#[must_use = "creates a new graph that should be used"]
pub fn from_edges<I>(iterable: I) -> Self
where
I: IntoIterator,
I::Item: IntoWeightedEdge<E>,
<I::Item as IntoWeightedEdge<E>>::NodeId: Into<NodeIndex<Ix>>,
N: Default,
G: Default,
for<'a> &'a G: GraphBase<NodeId = NodeIndex<Ix>> + IntoEdgesDirected,
{
let mut g = Self::new();
g.extend_with_edges(iterable);
g
}
pub fn node_count(&self) -> usize {
self.graph.node_count()
}
pub fn edge_count(&self) -> usize {
self.graph.edge_count()
}
#[must_use]
pub fn inner(&self) -> &G {
&self.graph
}
#[must_use = "consumes self and returns the inner graph"]
pub fn into_inner(self) -> G {
self.graph
}
}
impl<N, E, G> Default for BoundedGraph<N, E, G>
where
G: Default,
{
fn default() -> Self {
Self {
graph: G::default(),
_phantom: PhantomData,
}
}
}
impl<N, E, Ty, Ix> BoundedGraph<N, E, Graph<N, E, Ty, Ix>>
where
Ty: EdgeType,
Ix: IndexType,
N: BoundedNode<Ix>,
E: Clone,
{
#[must_use = "creates a new graph that should be used"]
pub fn with_capacity(nodes: usize, edges: usize) -> Self {
Self {
graph: Graph::with_capacity(nodes, edges),
_phantom: PhantomData,
}
}
}
impl<N, E, Ty, Ix> BoundedGraph<N, E, StableGraph<N, E, Ty, Ix>>
where
Ty: EdgeType,
Ix: IndexType,
N: BoundedNode<Ix>,
E: Clone,
{
#[must_use = "creates a new graph that should be used"]
pub fn with_capacity(nodes: usize, edges: usize) -> Self {
Self {
graph: StableGraph::with_capacity(nodes, edges),
_phantom: PhantomData,
}
}
}
impl<N, E, Ty, Ix> TryFrom<Graph<N, E, Ty, Ix>> for BoundedGraph<N, E, Graph<N, E, Ty, Ix>>
where
Ty: EdgeType,
Ix: IndexType,
N: BoundedNode<Ix>,
E: Clone,
{
type Error = BoundedGraphError<Ix>;
fn try_from(graph: Graph<N, E, Ty, Ix>) -> Result<Self, Self::Error> {
for node in graph.node_indices() {
let node_weight = graph
.node_weight(node)
.ok_or_else(|| BoundedGraphError::not_found(Some(node), None))?;
let outgoing_count = graph.edges_directed(node, Direction::Outgoing).count();
let incoming_count = graph.edges_directed(node, Direction::Incoming).count();
for edge in graph.edges_directed(node, Direction::Outgoing) {
let target = edge.target();
let target_weight = graph
.node_weight(target)
.ok_or_else(|| BoundedGraphError::not_found(None, Some(target)))?;
if !node_weight.can_add_edge(Direction::Outgoing, outgoing_count - 1, target_weight)
{
return Err(BoundedGraphError::source_rejected(node, target));
}
}
for edge in graph.edges_directed(node, Direction::Incoming) {
let source = edge.source();
let source_weight = graph
.node_weight(source)
.ok_or_else(|| BoundedGraphError::not_found(Some(source), None))?;
if !node_weight.can_add_edge(Direction::Incoming, incoming_count - 1, source_weight)
{
return Err(BoundedGraphError::target_rejected(source, node));
}
}
}
Ok(BoundedGraph {
graph,
_phantom: PhantomData,
})
}
}
impl<N, E, Ty, Ix> TryFrom<StableGraph<N, E, Ty, Ix>>
for BoundedGraph<N, E, StableGraph<N, E, Ty, Ix>>
where
Ty: EdgeType,
Ix: IndexType,
N: BoundedNode<Ix>,
E: Clone,
{
type Error = BoundedGraphError<Ix>;
fn try_from(graph: StableGraph<N, E, Ty, Ix>) -> Result<Self, Self::Error> {
for node in graph.node_indices() {
let node_weight = graph
.node_weight(node)
.ok_or_else(|| BoundedGraphError::not_found(Some(node), None))?;
let outgoing_count = graph.edges_directed(node, Direction::Outgoing).count();
let incoming_count = graph.edges_directed(node, Direction::Incoming).count();
for edge in graph.edges_directed(node, Direction::Outgoing) {
let target = edge.target();
let target_weight = graph
.node_weight(target)
.ok_or_else(|| BoundedGraphError::not_found(None, Some(target)))?;
if !node_weight.can_add_edge(Direction::Outgoing, outgoing_count - 1, target_weight)
{
return Err(BoundedGraphError::source_rejected(node, target));
}
}
for edge in graph.edges_directed(node, Direction::Incoming) {
let source = edge.source();
let source_weight = graph
.node_weight(source)
.ok_or_else(|| BoundedGraphError::not_found(Some(source), None))?;
if !node_weight.can_add_edge(Direction::Incoming, incoming_count - 1, source_weight)
{
return Err(BoundedGraphError::target_rejected(source, node));
}
}
}
Ok(BoundedGraph {
graph,
_phantom: PhantomData,
})
}
}