use std::collections::VecDeque;
use crate::containment::ContainmentEdge;
use crate::id::{GraphEdgeId, GraphNodeId, index_of, position};
use super::error::GraphAnalysisError;
use super::index::{AnalysisContext, SelectedAdjacency, SelectedIndexes};
use super::limits::GraphAnalysisLimits;
const NEITHER: &[SelectedAdjacency] = &[];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GraphDirection {
Outgoing,
Incoming,
Both,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct GraphNeighbor {
node: GraphNodeId,
distance: u32,
}
impl GraphNeighbor {
pub fn node(self) -> GraphNodeId {
self.node
}
pub fn distance(self) -> u32 {
self.distance
}
}
#[derive(Debug)]
pub struct GraphPath {
nodes: Box<[GraphNodeId]>,
edges: Box<[GraphEdgeId]>,
}
impl GraphPath {
pub fn nodes(&self) -> &[GraphNodeId] {
&self.nodes
}
pub fn edges(&self) -> &[GraphEdgeId] {
&self.edges
}
}
#[derive(Debug)]
pub struct GraphSubgraph {
nodes: Box<[GraphNodeId]>,
edges: Box<[GraphEdgeId]>,
containment: Box<[ContainmentEdge]>,
}
impl GraphSubgraph {
pub fn nodes(&self) -> &[GraphNodeId] {
&self.nodes
}
pub fn edges(&self) -> &[GraphEdgeId] {
&self.edges
}
pub fn containment(&self) -> &[ContainmentEdge] {
&self.containment
}
}
struct Distances {
measured: Box<[Option<u32>]>,
}
impl Distances {
fn of(&self, node: GraphNodeId) -> Option<u32> {
self.measured.get(index_of(node.index())).copied().flatten()
}
fn reached(&self) -> Box<[GraphNodeId]> {
self.measured
.iter()
.enumerate()
.filter(|(_, distance)| distance.is_some())
.map(|(at, _)| GraphNodeId::new(position(at)))
.collect()
}
fn ordered(&self) -> Box<[GraphNeighbor]> {
let mut found: Vec<GraphNeighbor> = self
.measured
.iter()
.enumerate()
.filter_map(|(at, distance)| beyond_seed(at, *distance))
.collect();
found.sort_unstable_by_key(|neighbor| (neighbor.distance, neighbor.node));
found.into_boxed_slice()
}
}
fn beyond_seed(at: usize, distance: Option<u32>) -> Option<GraphNeighbor> {
distance
.filter(|steps| *steps > 0)
.map(|steps| GraphNeighbor {
node: GraphNodeId::new(position(at)),
distance: steps,
})
}
pub(crate) fn neighbors(
analysis: &AnalysisContext<'_>,
node: GraphNodeId,
depth: u32,
direction: GraphDirection,
) -> Result<Box<[GraphNeighbor]>, GraphAnalysisError> {
let indexes = analysis.indexes();
admitted_walk(indexes, analysis.limits(), node, depth)?;
Ok(walk(indexes, node, depth, direction).ordered())
}
pub(crate) fn path(
analysis: &AnalysisContext<'_>,
route: (GraphNodeId, GraphNodeId),
) -> Result<Option<GraphPath>, GraphAnalysisError> {
let (_, target) = route;
let indexes = analysis.indexes();
admitted_route(indexes, route)?;
let remaining = walk(indexes, target, u32::MAX, GraphDirection::Incoming);
Ok(least_route(indexes, &remaining, route))
}
pub(crate) fn subgraph(
analysis: &AnalysisContext<'_>,
seed: GraphNodeId,
depth: u32,
direction: GraphDirection,
) -> Result<GraphSubgraph, GraphAnalysisError> {
let indexes = analysis.indexes();
admitted_walk(indexes, analysis.limits(), seed, depth)?;
Ok(induced(indexes, &walk(indexes, seed, depth, direction)))
}
fn known_node(indexes: &SelectedIndexes, node: GraphNodeId) -> Result<(), GraphAnalysisError> {
match indexes.holds(node) {
true => Ok(()),
false => Err(GraphAnalysisError::UnknownNode { node }),
}
}
pub(crate) fn admitted_walk(
indexes: &SelectedIndexes,
limits: GraphAnalysisLimits,
node: GraphNodeId,
depth: u32,
) -> Result<(), GraphAnalysisError> {
known_node(indexes, node)?;
admitted_depth(limits, depth)
}
pub(crate) fn admitted_route(
indexes: &SelectedIndexes,
route: (GraphNodeId, GraphNodeId),
) -> Result<(), GraphAnalysisError> {
let (source, target) = route;
known_node(indexes, source)?;
known_node(indexes, target)
}
fn admitted_depth(limits: GraphAnalysisLimits, depth: u32) -> Result<(), GraphAnalysisError> {
match depth <= limits.max_depth() {
true => Ok(()),
false => Err(GraphAnalysisError::DepthLimitExceeded {
requested: depth,
limit: limits.max_depth(),
}),
}
}
fn walk(
indexes: &SelectedIndexes,
seed: GraphNodeId,
depth: u32,
direction: GraphDirection,
) -> Distances {
let mut measured: Vec<Option<u32>> = vec![None; indexes.node_count()];
let mut queue: VecDeque<GraphNeighbor> = VecDeque::new();
admit(
&mut measured,
&mut queue,
GraphNeighbor {
node: seed,
distance: 0,
},
);
while let Some(reached) = queue.pop_front() {
if reached.distance < depth {
extend(indexes, (&mut measured, &mut queue), reached, direction);
}
}
Distances {
measured: measured.into_boxed_slice(),
}
}
fn extend(
indexes: &SelectedIndexes,
state: (&mut [Option<u32>], &mut VecDeque<GraphNeighbor>),
from: GraphNeighbor,
direction: GraphDirection,
) {
let (measured, queue) = state;
let distance = from.distance + 1;
for entry in adjacent(indexes, from.node, direction) {
admit(
measured,
queue,
GraphNeighbor {
node: entry.node(),
distance,
},
);
}
}
fn admit(
measured: &mut [Option<u32>],
queue: &mut VecDeque<GraphNeighbor>,
reached: GraphNeighbor,
) {
match measured.get_mut(index_of(reached.node.index())) {
Some(slot) if slot.is_none() => {
*slot = Some(reached.distance);
queue.push_back(reached);
}
_ => (),
}
}
fn adjacent(
indexes: &SelectedIndexes,
node: GraphNodeId,
direction: GraphDirection,
) -> impl Iterator<Item = SelectedAdjacency> + '_ {
let (first, second) = match direction {
GraphDirection::Outgoing => (indexes.outgoing(node), NEITHER),
GraphDirection::Incoming => (indexes.incoming(node), NEITHER),
GraphDirection::Both => (indexes.outgoing(node), indexes.incoming(node)),
};
first.iter().chain(second).copied()
}
fn least_route(
indexes: &SelectedIndexes,
remaining: &Distances,
route: (GraphNodeId, GraphNodeId),
) -> Option<GraphPath> {
let (source, _) = route;
let mut steps = remaining.of(source)?;
let mut nodes: Vec<GraphNodeId> = vec![source];
let mut edges: Vec<GraphEdgeId> = Vec::new();
let mut current = source;
while steps > 0 {
let step = least_step(indexes, remaining, current, steps)?;
nodes.push(step.node());
edges.push(step.edge());
current = step.node();
steps -= 1;
}
Some(GraphPath {
nodes: nodes.into_boxed_slice(),
edges: edges.into_boxed_slice(),
})
}
fn least_step(
indexes: &SelectedIndexes,
remaining: &Distances,
from: GraphNodeId,
steps: u32,
) -> Option<SelectedAdjacency> {
indexes
.outgoing(from)
.iter()
.filter(|entry| remaining.of(entry.node()) == Some(steps - 1))
.min_by_key(|entry| entry.edge())
.copied()
}
fn induced(indexes: &SelectedIndexes, distances: &Distances) -> GraphSubgraph {
let held = distances.reached();
GraphSubgraph {
edges: internal_edges(indexes, &held),
containment: internal_containment(indexes, &held),
nodes: held,
}
}
fn internal_edges(indexes: &SelectedIndexes, held: &[GraphNodeId]) -> Box<[GraphEdgeId]> {
let mut found: Vec<GraphEdgeId> = held
.iter()
.flat_map(|node| leaving(indexes, *node, held))
.collect();
found.sort_unstable();
found.into_boxed_slice()
}
fn leaving<'index>(
indexes: &'index SelectedIndexes,
node: GraphNodeId,
held: &'index [GraphNodeId],
) -> impl Iterator<Item = GraphEdgeId> + 'index {
indexes
.outgoing(node)
.iter()
.filter(move |entry| held.binary_search(&entry.node()).is_ok())
.map(|entry| entry.edge())
}
fn internal_containment(indexes: &SelectedIndexes, held: &[GraphNodeId]) -> Box<[ContainmentEdge]> {
held.iter()
.filter_map(|child| owned_pair(indexes, *child, held))
.collect()
}
fn owned_pair(
indexes: &SelectedIndexes,
child: GraphNodeId,
held: &[GraphNodeId],
) -> Option<ContainmentEdge> {
indexes
.parent(child)
.filter(|parent| held.binary_search(parent).is_ok())
.map(|parent| ContainmentEdge::new(parent, child))
}