use crate::array::*;
use crate::category::*;
use crate::finite_function::*;
use crate::indexed_coproduct::*;
use crate::open_hypergraph::*;
use num_traits::{One, Zero};
pub fn layer<K: ArrayKind, O, A>(f: &OpenHypergraph<K, O, A>) -> (FiniteFunction<K>, K::Type<K::I>)
where
K::Type<A>: Array<K, A>,
K::Type<K::I>: NaturalArray<K>,
{
let a = operation_adjacency(f);
let (ordering, completed) = kahn(&a);
(
FiniteFunction::new(ordering, f.h.x.0.len()).unwrap(),
completed,
)
}
pub fn layered_operations<K: ArrayKind, O, A>(
f: &OpenHypergraph<K, O, A>,
) -> (Vec<K::Index>, K::Index)
where
K::Type<A>: Array<K, A>,
K::Type<K::I>: NaturalArray<K>,
K::I: Into<usize>,
{
let (order, unvisited) = layer(f);
(converse_iter(order).collect(), unvisited.into())
}
fn kahn<K: ArrayKind>(
adjacency: &IndexedCoproduct<K, FiniteFunction<K>>,
) -> (K::Index, K::Type<K::I>)
where
K::Type<K::I>: NaturalArray<K>,
{
let mut order: K::Type<K::I> = K::Type::<K::I>::fill(K::I::zero(), adjacency.len());
let mut unvisited: K::Type<K::I> = K::Type::<K::I>::fill(K::I::one(), adjacency.len());
let mut indegree = indegree(adjacency);
let mut frontier: K::Index = zero(&indegree);
let mut depth = K::I::zero();
while !frontier.is_empty() && depth <= adjacency.len() {
unvisited.scatter_assign_constant(&frontier, K::I::zero());
order.scatter_assign_constant(&frontier, depth.clone());
let (reachable_ix, reachable_count) = sparse_relative_indegree(
adjacency,
&FiniteFunction::new(frontier, adjacency.len()).unwrap(),
);
indegree
.table
.as_mut()
.scatter_sub_assign(&reachable_ix.table, &reachable_count.table);
frontier = {
let reachable_ix_indegree_zero_ix = indegree
.table
.gather(reachable_ix.table.get_range(..))
.zero();
reachable_ix
.table
.gather(reachable_ix_indegree_zero_ix.get_range(..))
};
frontier = filter::<K>(
&frontier,
&unvisited.as_ref().gather(frontier.get_range(..)),
);
depth = depth + K::I::one();
}
(order.into(), unvisited)
}
fn filter<K: ArrayKind>(values: &K::Index, predicate: &K::Index) -> K::Index {
predicate.repeat(values.get_range(..))
}
#[allow(dead_code)]
fn filter_by_dense<K: ArrayKind>(values: &K::Index, predicate: &K::Index) -> K::Index
where
K::Type<K::I>: NaturalArray<K>,
{
predicate
.gather(values.get_range(..))
.repeat(values.get_range(..))
}
fn sparse_relative_indegree<K: ArrayKind>(
a: &IndexedCoproduct<K, FiniteFunction<K>>,
f: &FiniteFunction<K>,
) -> (FiniteFunction<K>, FiniteFunction<K>)
where
K::Type<K::I>: NaturalArray<K>,
{
assert_eq!(a.len(), f.target());
let g = a.indexed_values(f).unwrap();
let (i, c) = g.table.sparse_bincount();
let target = a.len() + K::I::one();
(
FiniteFunction::new(i, a.len()).unwrap(),
FiniteFunction::new(c, target).unwrap(),
)
}
fn dense_relative_indegree<K: ArrayKind>(
adjacency: &IndexedCoproduct<K, FiniteFunction<K>>,
f: &FiniteFunction<K>,
) -> FiniteFunction<K>
where
K::Type<K::I>: NaturalArray<K>,
{
assert_eq!(adjacency.len(), f.target());
let reached = adjacency.indexed_values(f).unwrap();
let target = adjacency.len() + K::I::one();
let table = (reached.table.as_ref() as &K::Type<K::I>).bincount(adjacency.len());
FiniteFunction::new(table, target).unwrap()
}
pub fn indegree<K: ArrayKind>(
adjacency: &IndexedCoproduct<K, FiniteFunction<K>>,
) -> FiniteFunction<K>
where
K::Type<K::I>: NaturalArray<K>,
{
dense_relative_indegree(adjacency, &FiniteFunction::<K>::identity(adjacency.len()))
}
pub fn operation_adjacency<K: ArrayKind, O, A>(
f: &OpenHypergraph<K, O, A>,
) -> IndexedCoproduct<K, FiniteFunction<K>>
where
K::Type<K::I>: NaturalArray<K>,
{
f.h.t.flatmap(&converse(&f.h.s))
}
pub fn converse<K: ArrayKind>(
r: &IndexedCoproduct<K, FiniteFunction<K>>,
) -> IndexedCoproduct<K, FiniteFunction<K>>
where
K::Type<K::I>: NaturalArray<K>,
{
let values_table = {
let arange = K::Index::arange(&K::I::zero(), &r.sources.len());
let unsorted_values = r.sources.table.repeat(arange.get_range(..));
unsorted_values.sort_by(&r.values.table)
};
let sources_table =
(r.values.table.as_ref() as &K::Type<K::I>).bincount(r.values.target.clone());
let sources = FiniteFunction::new(sources_table, r.values.table.len() + K::I::one()).unwrap();
let values = FiniteFunction::new(values_table, r.len()).unwrap();
IndexedCoproduct::new(sources, values).unwrap()
}
pub fn converse_iter<K: ArrayKind>(order: FiniteFunction<K>) -> impl Iterator<Item = K::Index>
where
K::Type<K::I>: NaturalArray<K>,
K::I: Into<usize>,
{
let c = converse(&IndexedCoproduct::elements(order));
c.into_iter().map(|x| x.table)
}
fn zero<K: ArrayKind>(f: &FiniteFunction<K>) -> K::Index
where
K::Type<K::I>: NaturalArray<K>,
{
(f.table.as_ref() as &K::Type<K::I>).zero()
}