use zrx_graph::{Topology, Traversal};
use crate::scheduler::value::{Value, Values};
use super::storage::Storage;
use super::Node;
#[derive(Debug)]
pub struct Frontier {
traversal: Traversal,
dependents: Vec<u8>,
storage: Storage,
visitable: usize,
}
impl Frontier {
pub fn new<I>(topology: &Topology, initial: I) -> Self
where
I: IntoIterator<Item = usize>,
{
let visitable = initial.into_iter().collect::<Vec<_>>();
let traversal = Traversal::new(topology, visitable.clone());
let outgoing = traversal.topology().outgoing();
let distance = traversal.topology().distance();
let mut dependents = outgoing.degrees().to_vec();
for node in outgoing {
if !visitable.iter().any(|&n| distance[n][node] != u8::MAX) {
let incoming = topology.incoming();
for &dependency in &incoming[node] {
dependents[dependency] -= 1;
}
}
}
Self {
traversal,
dependents,
storage: Storage::new(),
visitable: 0,
}
}
#[inline]
#[must_use]
pub fn take(&mut self) -> Option<usize> {
self.traversal.take().inspect(|_| self.visitable += 1)
}
pub fn complete(
&mut self, node: Node<Option<Box<dyn Value>>>,
) -> Result<(), Option<Box<dyn Value>>> {
match self.traversal.complete(node.id) {
Ok(()) => {}
Err(_) => return Err(node.data),
}
self.visitable -= 1;
if self.dependents[node.id] != 0 {
if let Some(data) = node.data {
self.storage.append(node.id, data);
}
}
let incoming = self.traversal.topology().incoming();
for &dependency in &incoming[node.id] {
self.dependents[dependency] -= 1;
if self.dependents[dependency] == 0 {
self.storage.remove(dependency);
}
}
Ok(())
}
pub fn select(&mut self, id: usize) -> Node<Values<'_>> {
let incoming = self.traversal.topology().incoming();
let view = self.storage.select(&incoming[id]);
Node { id, data: Values::View(view) }
}
}
#[allow(clippy::must_use_candidate)]
impl Frontier {
#[inline]
pub fn len(&self) -> usize {
self.visitable + self.traversal.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}