use ahash::HashSet;
use std::collections::VecDeque;
use std::mem;
use super::topology::{Topology, Transitive};
use super::Graph;
mod error;
mod into_iter;
pub use error::{Error, Result};
pub use into_iter::IntoIter;
#[derive(Clone, Debug)]
pub struct Traversal {
topology: Topology<Transitive>,
dependencies: Vec<u8>,
initial: Vec<usize>,
visitable: VecDeque<usize>,
}
impl Traversal {
#[must_use]
pub fn new<I>(topology: &Topology<Transitive>, initial: I) -> Self
where
I: AsRef<[usize]>,
{
let mut visitable: VecDeque<_> =
unique(initial.as_ref().iter()).collect();
let incoming = topology.incoming();
let mut dependencies = incoming.degrees().to_vec();
for node in incoming {
for &dependency in &incoming[node] {
let mut iter = initial.as_ref().iter();
if !iter.any(|&n| topology.has_path(n, dependency)) {
dependencies[node] -= 1;
}
}
}
visitable.retain(|&n| dependencies[n] == 0);
Self {
topology: topology.clone(),
dependencies,
initial: visitable.iter().copied().collect(),
visitable,
}
}
#[inline]
#[must_use]
pub fn take(&mut self) -> Option<usize> {
self.visitable.pop_front()
}
pub fn complete(&mut self, node: usize) -> Result {
if self.dependencies[node] == u8::MAX {
return Err(Error::Completed(node));
}
if self.dependencies[node] != 0 {
return Err(Error::Converged);
}
self.dependencies[node] = u8::MAX;
let outgoing = self.topology.outgoing();
for &dependent in &outgoing[node] {
self.dependencies[dependent] -= 1;
if self.dependencies[dependent] == 0 {
self.visitable.push_back(dependent);
}
}
Ok(())
}
pub fn converge(&mut self, other: Self) -> Result {
if self.topology != other.topology {
return Err(Error::Mismatch);
}
let iter = self.initial.iter().chain(&other.initial);
let initial: Vec<_> = unique(iter).collect();
let graph = Graph {
data: (0..self.topology.incoming().len()).collect(),
topology: self.topology.clone(),
};
let mut iter = graph.common_descendants(&initial);
let Some(common) = iter.next() else {
return Err(Error::Disjoint(other));
};
let prior = mem::replace(self, Self::new(&self.topology, initial));
let mut visitable = VecDeque::new();
while let Some(node) = self.take() {
let p = prior.dependencies[node];
let o = other.dependencies[node];
if (p == u8::MAX || o == u8::MAX) && !common.contains(&node) {
self.complete(node)?;
} else {
visitable.push_back(node);
}
}
self.visitable = visitable;
Ok(())
}
}
#[allow(clippy::must_use_candidate)]
impl Traversal {
#[inline]
pub fn topology(&self) -> &Topology<Transitive> {
&self.topology
}
#[inline]
pub fn initial(&self) -> &[usize] {
&self.initial
}
#[inline]
pub fn len(&self) -> usize {
self.visitable.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.visitable.is_empty()
}
}
#[inline]
fn unique<'a, I>(iter: I) -> impl Iterator<Item = usize>
where
I: IntoIterator<Item = &'a usize>,
{
let mut nodes = HashSet::default();
iter.into_iter() .copied()
.filter(move |&node| nodes.insert(node))
}
#[cfg(test)]
mod tests {
mod complete {
use crate::graph;
#[test]
fn handles_graph() {
let graph = graph! {
transitive;
"a" => "b", "a" => "c",
"b" => "d", "b" => "e",
"c" => "f",
"d" => "g",
"e" => "g", "e" => "h",
"f" => "h",
"g" => "i",
"h" => "i",
};
for (node, mut descendants) in [
(0, vec![0, 1, 2, 3, 4, 5, 6, 7, 8]),
(1, vec![1, 3, 4, 6, 7, 8]),
(2, vec![2, 5, 7, 8]),
(3, vec![3, 6, 8]),
(4, vec![4, 6, 7, 8]),
(5, vec![5, 7, 8]),
(6, vec![6, 8]),
(7, vec![7, 8]),
(8, vec![8]),
] {
let mut traversal = graph.traverse([node]);
while let Some(node) = traversal.take() {
assert_eq!(node, descendants.remove(0));
assert!(traversal.complete(node).is_ok());
}
}
}
#[test]
fn handles_multi_graph() {
let graph = graph! {
transitive;
"a" => "b", "a" => "c", "a" => "c",
"b" => "d", "b" => "e",
"c" => "f",
"d" => "g",
"e" => "g", "e" => "h",
"f" => "h",
"g" => "i",
"h" => "i",
};
for (node, mut descendants) in [
(0, vec![0, 1, 2, 3, 4, 5, 6, 7, 8]),
(1, vec![1, 3, 4, 6, 7, 8]),
(2, vec![2, 5, 7, 8]),
(3, vec![3, 6, 8]),
(4, vec![4, 6, 7, 8]),
(5, vec![5, 7, 8]),
(6, vec![6, 8]),
(7, vec![7, 8]),
(8, vec![8]),
] {
let mut traversal = graph.traverse([node]);
while let Some(node) = traversal.take() {
assert_eq!(node, descendants.remove(0));
assert!(traversal.complete(node).is_ok());
}
}
}
}
mod converge {
use crate::graph;
#[test]
fn handles_graph() {
let graph = graph! {
transitive;
"a" => "b", "a" => "c",
"b" => "d", "b" => "e",
"c" => "f",
"d" => "g",
"e" => "g", "e" => "h",
"f" => "h",
"g" => "i",
"h" => "i",
};
for (i, j, descendants) in [
(vec![0], vec![0], vec![0, 1, 2, 3, 4, 5, 6, 7, 8]),
(vec![1], vec![0], vec![0, 1, 2, 3, 4, 5, 6, 7, 8]),
(vec![8], vec![0], vec![0, 1, 2, 3, 4, 5, 6, 7, 8]),
(vec![1], vec![1], vec![1, 3, 4, 6, 7, 8]),
(vec![1], vec![2], vec![1, 2, 3, 4, 5, 6, 7, 8]),
(vec![2], vec![4], vec![2, 4, 5, 6, 7, 8]),
(vec![4], vec![2], vec![4, 2, 6, 5, 7, 8]),
(vec![3], vec![5], vec![3, 5, 6, 7, 8]),
(vec![6], vec![7], vec![6, 7, 8]),
(vec![8], vec![8], vec![8]),
] {
let mut traversal = graph.traverse(i);
assert!(traversal.converge(graph.traverse(j)).is_ok());
assert_eq!(
traversal.into_iter().collect::<Vec<_>>(), descendants
);
}
}
#[test]
fn handles_multi_graph() {
let graph = graph! {
transitive;
"a" => "b", "a" => "c", "a" => "c",
"b" => "d", "b" => "e",
"c" => "f",
"d" => "g",
"e" => "g", "e" => "h",
"f" => "h",
"g" => "i",
"h" => "i",
};
for (i, j, descendants) in [
(vec![0], vec![0], vec![0, 1, 2, 3, 4, 5, 6, 7, 8]),
(vec![1], vec![0], vec![0, 1, 2, 3, 4, 5, 6, 7, 8]),
(vec![8], vec![0], vec![0, 1, 2, 3, 4, 5, 6, 7, 8]),
(vec![1], vec![1], vec![1, 3, 4, 6, 7, 8]),
(vec![1], vec![2], vec![1, 2, 3, 4, 5, 6, 7, 8]),
(vec![2], vec![4], vec![2, 4, 5, 6, 7, 8]),
(vec![4], vec![2], vec![4, 2, 6, 5, 7, 8]),
(vec![3], vec![5], vec![3, 5, 6, 7, 8]),
(vec![6], vec![7], vec![6, 7, 8]),
(vec![8], vec![8], vec![8]),
] {
let mut traversal = graph.traverse(i);
assert!(traversal.converge(graph.traverse(j)).is_ok());
assert_eq!(
traversal.into_iter().collect::<Vec<_>>(), descendants
);
}
}
}
}