#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct DirectedClassId(pub u64);
pub(crate) fn directed_pairs(k: usize) -> Vec<(usize, usize)> {
(0..k)
.flat_map(|i| (0..k).filter(move |&j| j != i).map(move |j| (i, j)))
.collect()
}
fn mask_of(k: usize, perm: &[usize], has_arc: &impl Fn(usize, usize) -> bool) -> u64 {
let mut m = 0u64;
let mut bit = 0u32;
for i in 0..k {
for j in 0..k {
if i == j {
continue;
}
if has_arc(perm[i], perm[j]) {
m |= 1 << bit;
}
bit += 1;
}
}
m
}
pub(crate) fn mask_of_by(k: usize, perm: &[usize], has_arc: impl Fn(usize, usize) -> bool) -> u64 {
mask_of(k, perm, &has_arc)
}
pub(crate) fn canonical_by(
k: usize,
ps: &[Vec<usize>],
has_arc: impl Fn(usize, usize) -> bool,
) -> u64 {
ps.iter().map(|p| mask_of(k, p, &has_arc)).min().unwrap()
}
pub(crate) fn canonical_arg_by(
k: usize,
ps: &[Vec<usize>],
has_arc: impl Fn(usize, usize) -> bool,
) -> (u64, Vec<usize>) {
let mut best = u64::MAX;
let mut arg = ps[0].clone();
for p in ps {
let m = mask_of(k, p, &has_arc);
if m < best {
best = m;
arg.clone_from(p);
}
}
(best, arg)
}
pub(crate) fn class_to_arcs(mask: u64, k: usize) -> Vec<Vec<usize>> {
let mut out = vec![Vec::new(); k];
for (b, &(i, j)) in directed_pairs(k).iter().enumerate() {
if mask & (1 << b) != 0 {
out[i].push(j);
}
}
out
}
pub(crate) fn weakly_connected(out: &[Vec<usize>]) -> bool {
let k = out.len();
let mut und = vec![Vec::new(); k];
for (i, row) in out.iter().enumerate() {
for &j in row {
und[i].push(j);
und[j].push(i);
}
}
crate::canonical::connected(&und)
}
pub(crate) fn all_weakly_connected_classes(k: usize) -> Vec<u64> {
use crate::canonical::perms;
use std::collections::HashSet;
let ps = perms(k);
let np = directed_pairs(k).len();
let mut classes = HashSet::new();
for mask in 0u64..(1u64 << np) {
let out = class_to_arcs(mask, k);
if !weakly_connected(&out) {
continue;
}
classes.insert(canonical_by(k, &ps, |i, j| out[i].contains(&j)));
}
let mut v: Vec<u64> = classes.into_iter().collect();
v.sort_unstable();
v
}