use super::relation::converse;
use crate::array::{Array, ArrayKind, NaturalArray};
use crate::category::Arrow;
use crate::finite_function::FiniteFunction;
use crate::indexed_coproduct::IndexedCoproduct;
use crate::strict::hypergraph::Hypergraph;
use num_traits::{One, Zero};
pub(crate) fn operation_adjacency<K: ArrayKind, O, A>(
h: &Hypergraph<K, O, A>,
) -> IndexedCoproduct<K, FiniteFunction<K>>
where
K::Type<K::I>: NaturalArray<K>,
{
h.t.flatmap(&converse(&h.s))
}
pub(crate) fn node_adjacency<K: ArrayKind, O, A>(
h: &Hypergraph<K, O, A>,
) -> IndexedCoproduct<K, FiniteFunction<K>>
where
K::Type<K::I>: NaturalArray<K>,
{
node_adjacency_from_incidence(&h.s, &h.t)
}
pub(crate) fn node_adjacency_from_incidence<K: ArrayKind>(
s: &IndexedCoproduct<K, FiniteFunction<K>>,
t: &IndexedCoproduct<K, FiniteFunction<K>>,
) -> IndexedCoproduct<K, FiniteFunction<K>>
where
K::Type<K::I>: NaturalArray<K>,
{
converse(s).flatmap(t)
}
pub(crate) 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)
}
pub(crate) 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(crate) 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 = reached.table.len() + K::I::one();
let table = (reached.table.as_ref() as &K::Type<K::I>).bincount(adjacency.len());
FiniteFunction::new(table, target).unwrap()
}
pub(crate) 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 = g.table.len() + K::I::one();
(
FiniteFunction::new(i, a.len()).unwrap(),
FiniteFunction::new(c, target).unwrap(),
)
}
pub(crate) 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(..))
}
pub(crate) 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)
}
pub(crate) 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()
}