///|
/// What every algorithm needs from a graph.
///
/// `neighbors` and `incident` are parallel: position `i` of each describes one
/// edge leaving `node`, so a walk that only wants nodes allocates nothing and a
/// walk that wants weights reads both. In an undirected graph an edge appears at
/// both of its endpoints, and a self-loop appears once.
///
/// A third party's storage joins the family by implementing this trait: no
/// algorithm in this library names a concrete graph type.
pub(open) trait Topology {
/// How many nodes the graph has. Node numbers run from `0` to this minus one,
/// except in `StableGraph`, where a deleted node leaves a vacant number and is
/// not counted.
fn node_count(Self) -> Int32
/// How many edges the graph has. Edge numbers need not be below this: a graph
/// that deleted an edge keeps the number vacant.
fn edge_count(Self) -> Int32
/// Whether the graph is directed.
fn directed(Self) -> Bool
/// The nodes reachable from `node` along the edges leaving it, one entry per
/// edge, in the same order as `incident`. Parallel edges therefore repeat the
/// neighbour.
fn neighbors(Self, Int32) -> ArrayView[Int32]
/// The edges leaving `node`, one entry per edge, in the same order as
/// `neighbors`.
fn incident(Self, Int32) -> ArrayView[Int32]
/// The two endpoints of an edge, source first in a directed graph.
fn endpoints(Self, Int32) -> (Int32, Int32)
}
///|
/// A graph that can also walk its edges backwards.
///
/// `Graph`, `StableGraph` and `Csr` keep both directions, so starting such a
/// walk costs O(1) and allocates nothing. An implementation that keeps only the
/// forward direction can still satisfy the trait by building the list on demand,
/// at O(d) per call.
pub(open) trait Inbound {
/// The nodes with an edge into `node`, in the same order as `in_incident`.
fn in_neighbors(Self, Int32) -> ArrayView[Int32]
/// The edges into `node`.
fn in_incident(Self, Int32) -> ArrayView[Int32]
}