use alloc::{vec, vec::Vec};
use num_traits::AsPrimitive;
use crate::traits::UndirectedMonopartiteMonoplexGraph;
pub(super) struct UndirectedMotifContext<NodeId> {
pub(super) nodes: Vec<NodeId>,
pub(super) rank: Vec<usize>,
pub(super) in_cover: Vec<bool>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MotifCountOrdering {
Natural,
DecreasingDegree,
IncreasingDegree,
}
pub(super) fn build_undirected_motif_context<G>(
graph: &G,
ordering: MotifCountOrdering,
) -> UndirectedMotifContext<G::NodeId>
where
G: UndirectedMonopartiteMonoplexGraph,
{
let n = graph.number_of_nodes().as_();
let nodes: Vec<G::NodeId> = graph.node_ids().collect();
debug_assert_eq!(nodes.len(), n);
debug_assert!(nodes.iter().enumerate().all(|(i, node)| (*node).as_() == i));
let degrees: Vec<usize> = nodes.iter().map(|&node| graph.degree(node).as_()).collect();
let rank = build_rank(°rees, ordering);
let mut in_cover = vec![false; n];
match ordering {
MotifCountOrdering::Natural => {
for (node_index, °ree) in degrees.iter().enumerate() {
if degree > 0 {
in_cover[node_index] = true;
}
}
}
MotifCountOrdering::DecreasingDegree | MotifCountOrdering::IncreasingDegree => {
for &node in &nodes {
let node_index = node.as_();
for neighbor in graph.neighbors(node) {
let neighbor_index = neighbor.as_();
if node_index > neighbor_index {
continue;
}
let source_index = if rank[node_index] <= rank[neighbor_index] {
node_index
} else {
neighbor_index
};
in_cover[source_index] = true;
}
}
}
}
UndirectedMotifContext { nodes, rank, in_cover }
}
fn build_rank(degrees: &[usize], ordering: MotifCountOrdering) -> Vec<usize> {
let n = degrees.len();
let mut order: Vec<usize> = (0..n).collect();
match ordering {
MotifCountOrdering::Natural => {}
MotifCountOrdering::DecreasingDegree => {
order.sort_unstable_by_key(|&node_index| {
(core::cmp::Reverse(degrees[node_index]), node_index)
});
}
MotifCountOrdering::IncreasingDegree => {
order.sort_unstable_by_key(|&node_index| (degrees[node_index], node_index));
}
}
let mut rank = vec![0usize; n];
for (position, node_index) in order.into_iter().enumerate() {
rank[node_index] = position;
}
rank
}