1use std::collections::VecDeque;
13
14use crate::containment::ContainmentEdge;
15use crate::id::{GraphEdgeId, GraphNodeId, index_of, position};
16
17use super::error::GraphAnalysisError;
18use super::index::{AnalysisContext, SelectedAdjacency, SelectedIndexes};
19use super::limits::GraphAnalysisLimits;
20
21const NEITHER: &[SelectedAdjacency] = &[];
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub enum GraphDirection {
31 Outgoing,
33 Incoming,
35 Both,
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
42pub struct GraphNeighbor {
43 node: GraphNodeId,
44 distance: u32,
45}
46
47impl GraphNeighbor {
48 pub fn node(self) -> GraphNodeId {
50 self.node
51 }
52
53 pub fn distance(self) -> u32 {
55 self.distance
56 }
57}
58
59#[derive(Debug)]
64pub struct GraphPath {
65 nodes: Box<[GraphNodeId]>,
66 edges: Box<[GraphEdgeId]>,
67}
68
69impl GraphPath {
70 pub fn nodes(&self) -> &[GraphNodeId] {
72 &self.nodes
73 }
74
75 pub fn edges(&self) -> &[GraphEdgeId] {
77 &self.edges
78 }
79}
80
81#[derive(Debug)]
87pub struct GraphSubgraph {
88 nodes: Box<[GraphNodeId]>,
89 edges: Box<[GraphEdgeId]>,
90 containment: Box<[ContainmentEdge]>,
91}
92
93impl GraphSubgraph {
94 pub fn nodes(&self) -> &[GraphNodeId] {
96 &self.nodes
97 }
98
99 pub fn edges(&self) -> &[GraphEdgeId] {
102 &self.edges
103 }
104
105 pub fn containment(&self) -> &[ContainmentEdge] {
108 &self.containment
109 }
110}
111
112struct Distances {
114 measured: Box<[Option<u32>]>,
115}
116
117impl Distances {
118 fn of(&self, node: GraphNodeId) -> Option<u32> {
120 self.measured.get(index_of(node.index())).copied().flatten()
121 }
122
123 fn reached(&self) -> Box<[GraphNodeId]> {
125 self.measured
126 .iter()
127 .enumerate()
128 .filter(|(_, distance)| distance.is_some())
129 .map(|(at, _)| GraphNodeId::new(position(at)))
130 .collect()
131 }
132
133 fn ordered(&self) -> Box<[GraphNeighbor]> {
136 let mut found: Vec<GraphNeighbor> = self
137 .measured
138 .iter()
139 .enumerate()
140 .filter_map(|(at, distance)| beyond_seed(at, *distance))
141 .collect();
142 found.sort_unstable_by_key(|neighbor| (neighbor.distance, neighbor.node));
143 found.into_boxed_slice()
144 }
145}
146
147fn beyond_seed(at: usize, distance: Option<u32>) -> Option<GraphNeighbor> {
149 distance
150 .filter(|steps| *steps > 0)
151 .map(|steps| GraphNeighbor {
152 node: GraphNodeId::new(position(at)),
153 distance: steps,
154 })
155}
156
157pub(crate) fn neighbors(
160 analysis: &AnalysisContext<'_>,
161 node: GraphNodeId,
162 depth: u32,
163 direction: GraphDirection,
164) -> Result<Box<[GraphNeighbor]>, GraphAnalysisError> {
165 let indexes = analysis.indexes();
166 admitted_walk(indexes, analysis.limits(), node, depth)?;
167 Ok(walk(indexes, node, depth, direction).ordered())
168}
169
170pub(crate) fn path(
180 analysis: &AnalysisContext<'_>,
181 route: (GraphNodeId, GraphNodeId),
182) -> Result<Option<GraphPath>, GraphAnalysisError> {
183 let (_, target) = route;
184 let indexes = analysis.indexes();
185 admitted_route(indexes, route)?;
186 let remaining = walk(indexes, target, u32::MAX, GraphDirection::Incoming);
187 Ok(least_route(indexes, &remaining, route))
188}
189
190pub(crate) fn subgraph(
193 analysis: &AnalysisContext<'_>,
194 seed: GraphNodeId,
195 depth: u32,
196 direction: GraphDirection,
197) -> Result<GraphSubgraph, GraphAnalysisError> {
198 let indexes = analysis.indexes();
199 admitted_walk(indexes, analysis.limits(), seed, depth)?;
200 Ok(induced(indexes, &walk(indexes, seed, depth, direction)))
201}
202
203fn known_node(indexes: &SelectedIndexes, node: GraphNodeId) -> Result<(), GraphAnalysisError> {
205 match indexes.holds(node) {
206 true => Ok(()),
207 false => Err(GraphAnalysisError::UnknownNode { node }),
208 }
209}
210
211pub(crate) fn admitted_walk(
220 indexes: &SelectedIndexes,
221 limits: GraphAnalysisLimits,
222 node: GraphNodeId,
223 depth: u32,
224) -> Result<(), GraphAnalysisError> {
225 known_node(indexes, node)?;
226 admitted_depth(limits, depth)
227}
228
229pub(crate) fn admitted_route(
238 indexes: &SelectedIndexes,
239 route: (GraphNodeId, GraphNodeId),
240) -> Result<(), GraphAnalysisError> {
241 let (source, target) = route;
242 known_node(indexes, source)?;
243 known_node(indexes, target)
244}
245
246fn admitted_depth(limits: GraphAnalysisLimits, depth: u32) -> Result<(), GraphAnalysisError> {
249 match depth <= limits.max_depth() {
250 true => Ok(()),
251 false => Err(GraphAnalysisError::DepthLimitExceeded {
252 requested: depth,
253 limit: limits.max_depth(),
254 }),
255 }
256}
257
258fn walk(
263 indexes: &SelectedIndexes,
264 seed: GraphNodeId,
265 depth: u32,
266 direction: GraphDirection,
267) -> Distances {
268 let mut measured: Vec<Option<u32>> = vec![None; indexes.node_count()];
269 let mut queue: VecDeque<GraphNeighbor> = VecDeque::new();
270 admit(
271 &mut measured,
272 &mut queue,
273 GraphNeighbor {
274 node: seed,
275 distance: 0,
276 },
277 );
278 while let Some(reached) = queue.pop_front() {
279 if reached.distance < depth {
280 extend(indexes, (&mut measured, &mut queue), reached, direction);
281 }
282 }
283 Distances {
284 measured: measured.into_boxed_slice(),
285 }
286}
287
288fn extend(
290 indexes: &SelectedIndexes,
291 state: (&mut [Option<u32>], &mut VecDeque<GraphNeighbor>),
292 from: GraphNeighbor,
293 direction: GraphDirection,
294) {
295 let (measured, queue) = state;
296 let distance = from.distance + 1;
297 for entry in adjacent(indexes, from.node, direction) {
298 admit(
299 measured,
300 queue,
301 GraphNeighbor {
302 node: entry.node(),
303 distance,
304 },
305 );
306 }
307}
308
309fn admit(
311 measured: &mut [Option<u32>],
312 queue: &mut VecDeque<GraphNeighbor>,
313 reached: GraphNeighbor,
314) {
315 match measured.get_mut(index_of(reached.node.index())) {
316 Some(slot) if slot.is_none() => {
317 *slot = Some(reached.distance);
318 queue.push_back(reached);
319 }
320 _ => (),
321 }
322}
323
324fn adjacent(
326 indexes: &SelectedIndexes,
327 node: GraphNodeId,
328 direction: GraphDirection,
329) -> impl Iterator<Item = SelectedAdjacency> + '_ {
330 let (first, second) = match direction {
331 GraphDirection::Outgoing => (indexes.outgoing(node), NEITHER),
332 GraphDirection::Incoming => (indexes.incoming(node), NEITHER),
333 GraphDirection::Both => (indexes.outgoing(node), indexes.incoming(node)),
334 };
335 first.iter().chain(second).copied()
336}
337
338fn least_route(
340 indexes: &SelectedIndexes,
341 remaining: &Distances,
342 route: (GraphNodeId, GraphNodeId),
343) -> Option<GraphPath> {
344 let (source, _) = route;
345 let mut steps = remaining.of(source)?;
346 let mut nodes: Vec<GraphNodeId> = vec![source];
347 let mut edges: Vec<GraphEdgeId> = Vec::new();
348 let mut current = source;
349 while steps > 0 {
350 let step = least_step(indexes, remaining, current, steps)?;
351 nodes.push(step.node());
352 edges.push(step.edge());
353 current = step.node();
354 steps -= 1;
355 }
356 Some(GraphPath {
357 nodes: nodes.into_boxed_slice(),
358 edges: edges.into_boxed_slice(),
359 })
360}
361
362fn least_step(
365 indexes: &SelectedIndexes,
366 remaining: &Distances,
367 from: GraphNodeId,
368 steps: u32,
369) -> Option<SelectedAdjacency> {
370 indexes
371 .outgoing(from)
372 .iter()
373 .filter(|entry| remaining.of(entry.node()) == Some(steps - 1))
374 .min_by_key(|entry| entry.edge())
375 .copied()
376}
377
378fn induced(indexes: &SelectedIndexes, distances: &Distances) -> GraphSubgraph {
380 let held = distances.reached();
381 GraphSubgraph {
382 edges: internal_edges(indexes, &held),
383 containment: internal_containment(indexes, &held),
384 nodes: held,
385 }
386}
387
388fn internal_edges(indexes: &SelectedIndexes, held: &[GraphNodeId]) -> Box<[GraphEdgeId]> {
391 let mut found: Vec<GraphEdgeId> = held
392 .iter()
393 .flat_map(|node| leaving(indexes, *node, held))
394 .collect();
395 found.sort_unstable();
396 found.into_boxed_slice()
397}
398
399fn leaving<'index>(
401 indexes: &'index SelectedIndexes,
402 node: GraphNodeId,
403 held: &'index [GraphNodeId],
404) -> impl Iterator<Item = GraphEdgeId> + 'index {
405 indexes
406 .outgoing(node)
407 .iter()
408 .filter(move |entry| held.binary_search(&entry.node()).is_ok())
409 .map(|entry| entry.edge())
410}
411
412fn internal_containment(indexes: &SelectedIndexes, held: &[GraphNodeId]) -> Box<[ContainmentEdge]> {
415 held.iter()
416 .filter_map(|child| owned_pair(indexes, *child, held))
417 .collect()
418}
419
420fn owned_pair(
423 indexes: &SelectedIndexes,
424 child: GraphNodeId,
425 held: &[GraphNodeId],
426) -> Option<ContainmentEdge> {
427 indexes
428 .parent(child)
429 .filter(|parent| held.binary_search(parent).is_ok())
430 .map(|parent| ContainmentEdge::new(parent, child))
431}