use std::mem;
const NONE: u32 = u32::MAX;
const PARANOID_N: usize = 64;
pub(crate) struct Input {
pub(crate) n: usize,
pub(crate) adj: Vec<Vec<u32>>,
pub(crate) elems: Vec<Vec<u32>>,
pub(crate) excluded: Vec<bool>,
pub(crate) aggressive: bool,
pub(crate) previous_degree: bool,
pub(crate) column_order: bool,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Kind {
Var,
Elem,
Gone,
}
#[allow(clippy::struct_excessive_bools)] struct Graph {
n: usize,
list: Vec<Vec<u32>>,
elen: Vec<usize>,
kind: Vec<Kind>,
nv: Vec<usize>,
deg: Vec<usize>,
parent: Vec<u32>,
is_pivot: Vec<bool>,
head: Vec<u32>,
next: Vec<u32>,
prev: Vec<u32>,
mindeg: usize,
nel: usize,
in_lp: Vec<bool>,
w: Vec<u32>,
wflg: u32,
vw: Vec<usize>,
lp_buf: Vec<u32>,
mark: Vec<usize>,
mark_cur: usize,
hhead: Vec<u32>,
hnext: Vec<u32>,
hof: Vec<usize>,
aggressive: bool,
previous_degree: bool,
column_order: bool,
pivots: Vec<u32>,
paranoid: bool,
}
pub(crate) fn order(input: Input) -> Vec<u32> {
let mut g = Graph::new(input);
while g.nel < g.n {
let p = g.select();
g.eliminate(p);
if g.paranoid {
debug_assert_eq!(g.check(), Ok(()));
}
}
if g.column_order {
g.pivot_order()
} else {
g.postorder()
}
}
#[inline]
fn u(i: u32) -> usize {
i as usize
}
#[inline]
fn idx32(i: usize) -> u32 {
u32::try_from(i).expect("index exceeds u32::MAX")
}
impl Graph {
#[allow(clippy::too_many_lines)] fn new(input: Input) -> Self {
let Input {
n,
adj,
elems,
excluded,
aggressive,
previous_degree,
column_order,
} = input;
debug_assert_eq!(adj.len(), n);
debug_assert_eq!(excluded.len(), n);
let ne = elems.len();
let mut w_dead = Vec::new();
let total = n + ne;
let mut einc: Vec<Vec<u32>> = vec![Vec::new(); n];
let mut deg = vec![0_usize; total];
let mut kind = vec![Kind::Var; total];
let mut list: Vec<Vec<u32>> = Vec::with_capacity(total);
for (e, l) in elems.iter().enumerate().rev() {
for &v in l {
debug_assert!(!excluded[u(v)], "excluded variable in an element");
einc[u(v)].push(idx32(n + e));
}
}
let mut elen = vec![0_usize; n];
for (i, mut a) in adj.into_iter().enumerate() {
elen[i] = einc[i].len();
a.append(&mut einc[i]);
list.push(a);
}
for (e, l) in elems.into_iter().enumerate() {
deg[n + e] = l.len();
kind[n + e] = if l.is_empty() { Kind::Gone } else { Kind::Elem };
list.push(l);
}
for (e, k) in kind.iter().enumerate().skip(n) {
if *k == Kind::Gone {
w_dead.push(e);
}
}
let mut g = Self {
n,
list,
elen,
kind,
nv: vec![1; n],
deg,
parent: vec![NONE; total],
is_pivot: vec![false; n],
head: vec![NONE; n + 1],
next: vec![NONE; n],
prev: vec![NONE; n],
mindeg: 0,
nel: 0,
in_lp: vec![false; n],
w: vec![1; total],
wflg: 0,
vw: vec![1; n],
lp_buf: Vec::new(),
mark: vec![0; total],
mark_cur: 0,
hhead: vec![NONE; n.max(1)],
hnext: vec![NONE; n],
hof: vec![0; n],
aggressive,
previous_degree,
column_order,
pivots: Vec::new(),
paranoid: cfg!(debug_assertions) && n <= PARANOID_N,
};
for e in w_dead {
g.w[e] = 0;
}
for (i, &ex) in excluded.iter().enumerate() {
if ex {
g.kind[i] = Kind::Gone;
g.nv[i] = 0;
g.vw[i] = 0;
g.nel += 1;
continue;
}
let ei = g.elen[i];
let from_elems: usize = g
.elems(i)
.iter()
.map(|&e| g.deg[u(e)].saturating_sub(1))
.sum();
let d = g.list[i].len() - ei + from_elems;
g.deg[i] = d.min(n - 1);
}
let insert = |g: &mut Self, i: usize| {
if g.kind[i] == Kind::Var {
g.list_insert(i, g.deg[i]);
}
};
if column_order {
for i in (0..n).rev() {
insert(&mut g, i);
}
} else {
for i in 0..n {
insert(&mut g, i);
}
}
g.mindeg = 0;
if g.paranoid {
debug_assert_eq!(g.check(), Ok(()));
}
g
}
#[inline]
fn elems(&self, i: usize) -> &[u32] {
let l = &self.list[i];
&l[l.len() - self.elen[i]..]
}
#[inline]
fn vars(&self, i: usize) -> &[u32] {
let l = &self.list[i];
&l[..l.len() - self.elen[i]]
}
fn list_insert(&mut self, i: usize, d: usize) {
let h = self.head[d];
self.next[i] = h;
self.prev[i] = NONE;
if h != NONE {
self.prev[u(h)] = idx32(i);
}
self.head[d] = idx32(i);
}
fn list_remove(&mut self, i: usize) {
let (p, x) = (self.prev[i], self.next[i]);
if p == NONE {
self.head[self.deg[i]] = x;
} else {
self.next[u(p)] = x;
}
if x != NONE {
self.prev[u(x)] = p;
}
self.next[i] = NONE;
self.prev[i] = NONE;
}
fn select(&mut self) -> usize {
while self.head[self.mindeg] == NONE {
self.mindeg += 1;
}
let p = u(self.head[self.mindeg]);
self.list_remove(p);
self.nel += self.nv[p];
p
}
fn eliminate(&mut self, p: usize) {
self.is_pivot[p] = true;
self.pivots.push(idx32(p));
self.in_lp[p] = true;
self.vw[p] = 0;
let (lp, mut dk) = self.new_element(p);
self.set_differences(&lp);
let mut nvp = self.nv[p];
for &i in &lp {
if let Some(w) = self.update_degree(u(i), p) {
dk -= w;
nvp += w;
self.nel += w;
}
}
self.deg[p] = dk;
self.supervariables(&lp);
let mut kept = Vec::with_capacity(lp.len());
for &i in &lp {
let i = u(i);
self.in_lp[i] = false;
if self.kind[i] == Kind::Var && self.nv[i] > 0 {
let d = (self.deg[i] + dk - self.nv[i]).min(self.n - self.nel - self.nv[i]);
self.deg[i] = d;
self.list_insert(i, d);
self.mindeg = self.mindeg.min(d);
self.vw[i] = self.nv[i];
kept.push(idx32(i));
}
}
self.in_lp[p] = false;
self.nv[p] = nvp;
if kept.is_empty() {
self.kind[p] = Kind::Gone;
self.w[p] = 0;
}
self.list[p] = kept;
self.lp_buf = lp;
}
fn new_element(&mut self, p: usize) -> (Vec<u32>, usize) {
let lst = mem::take(&mut self.list[p]);
let split = lst.len() - self.elen[p];
let (vars, elems) = lst.split_at(split);
let mut lp = mem::take(&mut self.lp_buf);
lp.clear();
let mut dk = 0;
let n = self.n;
let mut absorb = |g: &mut Self, e: u32, lp: &mut Vec<u32>| {
let e = u(e);
if g.kind[e] != Kind::Elem {
return;
}
let le = mem::take(&mut g.list[e]);
for &j in &le {
dk += g.add_to_lp(u(j), lp);
}
g.kind[e] = Kind::Gone;
g.w[e] = 0;
g.parent[e] = idx32(p);
};
if self.column_order {
for &e in elems.iter().rev().filter(|&&x| u(x) >= n) {
absorb(self, e, &mut lp);
}
for &e in elems.iter().filter(|&&x| u(x) < n) {
absorb(self, e, &mut lp);
}
} else {
for &e in elems.iter().rev() {
absorb(self, e, &mut lp);
}
}
for &j in vars {
dk += self.add_to_lp(u(j), &mut lp);
}
self.kind[p] = Kind::Elem;
self.elen[p] = 0;
(lp, dk)
}
fn add_to_lp(&mut self, j: usize, lp: &mut Vec<u32>) -> usize {
if j >= self.n || self.vw[j] == 0 {
return 0;
}
debug_assert!(self.kind[j] == Kind::Var && self.nv[j] > 0 && !self.in_lp[j]);
self.in_lp[j] = true;
self.vw[j] = 0;
lp.push(idx32(j));
self.list_remove(j);
self.nv[j]
}
fn set_differences(&mut self, lp: &[u32]) {
let stride = idx32(self.n + 1);
if self.wflg >= u32::MAX - 2 * stride {
for x in &mut self.w {
if *x != 0 {
*x = 1;
}
}
self.wflg = 1;
}
self.wflg += stride;
let wflg = self.wflg;
for &i in lp {
let i = u(i);
let nv = self.nv[i];
let l = &self.list[i];
for &e in &l[l.len() - self.elen[i]..] {
let e = u(e);
let mut we = self.w[e];
if we == 0 {
continue; }
debug_assert_eq!(self.kind[e], Kind::Elem);
if we < wflg {
we = idx32(self.deg[e]) + wflg;
}
self.w[e] = we.saturating_sub(idx32(nv)).max(wflg);
}
}
}
fn update_degree(&mut self, i: usize, p: usize) -> Option<usize> {
let mut l = mem::take(&mut self.list[i]);
let split = l.len() - self.elen[i];
let mut d = 0_usize;
let mut h = 0_usize;
let mut w = 0;
for r in 0..split {
let j = l[r];
let ju = u(j);
debug_assert!(ju < self.n, "A_i holds variables only");
let wj = self.vw[ju];
debug_assert_eq!(
wj > 0,
self.kind[ju] == Kind::Var && self.nv[ju] > 0 && !self.in_lp[ju]
);
if wj > 0 {
d += wj;
l[w] = j;
w += 1;
h = h.wrapping_add(ju);
}
}
let n_vars = w;
for r in split..l.len() {
let e = l[r];
let eu = u(e);
let we = self.w[eu];
if we < self.wflg {
debug_assert_ne!(self.kind[eu], Kind::Elem);
continue;
}
let dext = u(we - self.wflg);
if dext > 0 || !self.aggressive {
d += dext;
l[w] = e;
w += 1;
h = h.wrapping_add(eu);
} else {
self.kind[eu] = Kind::Gone;
self.w[eu] = 0;
self.parent[eu] = idx32(p);
}
}
l.truncate(w);
l.push(idx32(p));
if l.len() == 1 {
let w = self.nv[i];
self.kind[i] = Kind::Gone;
self.parent[i] = idx32(p);
self.nv[i] = 0;
return Some(w);
}
self.deg[i] = if self.previous_degree {
self.deg[i].min(d)
} else {
d
};
self.elen[i] = l.len() - n_vars;
self.list[i] = l;
let b = h % self.n;
self.hof[i] = b;
self.hnext[i] = self.hhead[b];
self.hhead[b] = idx32(i);
None
}
fn supervariables(&mut self, lp: &[u32]) {
for &i in lp {
let i = u(i);
if self.kind[i] != Kind::Var || self.nv[i] == 0 {
continue;
}
let b = self.hof[i];
let mut x = self.hhead[b];
if x == NONE {
continue;
}
self.hhead[b] = NONE;
while x != NONE && self.hnext[u(x)] != NONE {
let xu = u(x);
self.mark_cur += 1;
let lx = &self.list[xu];
for &v in &lx[..lx.len() - 1] {
self.mark[u(v)] = self.mark_cur;
}
let mut last = xu;
let mut y = self.hnext[xu];
while y != NONE {
let yu = u(y);
let ly = &self.list[yu];
let same = ly.len() == self.list[xu].len()
&& self.elen[yu] == self.elen[xu]
&& ly[..ly.len() - 1]
.iter()
.all(|&v| self.mark[u(v)] == self.mark_cur);
let after = self.hnext[yu];
if same {
self.nv[xu] += self.nv[yu];
self.nv[yu] = 0;
self.kind[yu] = Kind::Gone;
self.parent[yu] = x;
self.list[yu] = Vec::new();
self.hnext[last] = after;
} else {
last = yu;
}
y = after;
}
x = self.hnext[xu];
}
}
}
fn pivot_order(&self) -> Vec<u32> {
let n = self.n;
let mut time = vec![usize::MAX; n];
for (t, &p) in self.pivots.iter().enumerate() {
time[u(p)] = t;
}
let mut keyed: Vec<(usize, bool, u32)> = Vec::with_capacity(n);
for i in 0..n {
if !self.is_pivot[i] && self.parent[i] == NONE {
continue; }
let mut a = i;
while !self.is_pivot[a] {
a = u(self.parent[a]);
}
keyed.push((time[a], !self.is_pivot[i], idx32(i)));
}
keyed.sort_unstable();
keyed.into_iter().map(|k| k.2).collect()
}
fn postorder(&self) -> Vec<u32> {
let n = self.n;
let mut first = vec![NONE; n];
let mut sibling = vec![NONE; n];
let mut push = |c: usize, p: u32, first: &mut Vec<u32>| {
sibling[c] = first[u(p)];
first[u(p)] = idx32(c);
};
for c in (0..n).rev() {
if !self.is_pivot[c] && self.parent[c] != NONE {
push(c, self.parent[c], &mut first);
}
}
for c in (0..n).rev() {
if self.is_pivot[c] && self.parent[c] != NONE {
push(c, self.parent[c], &mut first);
}
}
let mut out = Vec::with_capacity(n);
let mut stack = Vec::new();
for r in 0..n {
if !self.is_pivot[r] || self.parent[r] != NONE {
continue;
}
stack.push(r);
while let Some(&x) = stack.last() {
let c = first[x];
if c == NONE {
stack.pop();
out.push(idx32(x));
} else {
first[x] = sibling[u(c)];
stack.push(u(c));
}
}
}
out
}
fn check(&self) -> Result<(), String> {
let n = self.n;
let mut listed = vec![false; n];
for (d, &h) in self.head.iter().enumerate() {
let mut x = h;
while x != NONE {
let xu = u(x);
if listed[xu] || self.kind[xu] != Kind::Var || self.nv[xu] == 0 {
return Err(format!("(Q1): node {xu} wrongly listed"));
}
if self.deg[xu] != d {
return Err(format!(
"(Q1): node {xu} in list {d}, degree {}",
self.deg[xu]
));
}
listed[xu] = true;
x = self.next[xu];
}
}
let mut live = 0;
for (i, &is_listed) in listed.iter().enumerate() {
let principal = self.kind[i] == Kind::Var && self.nv[i] > 0;
if principal != is_listed {
return Err(format!(
"(Q1): node {i} principal {principal}, listed {is_listed}"
));
}
if principal {
live += self.nv[i];
}
}
if live + self.nel != n {
return Err(format!("(Q3): {live} live + {} eliminated ≠ {n}", self.nel));
}
let alive = |j: usize| j < n && self.kind[j] == Kind::Var && self.nv[j] > 0;
if self.aggressive {
for e in 0..self.kind.len() {
if self.kind[e] == Kind::Elem {
let w: usize = self.list[e]
.iter()
.filter(|&&j| alive(u(j)))
.map(|&j| self.nv[u(j)])
.sum();
if w != self.deg[e] {
return Err(format!("(Q2): element {e} weight {} ≠ {w}", self.deg[e]));
}
}
}
}
let mut seen = vec![false; n];
for i in (0..n).filter(|&i| alive(i)) {
seen.fill(false);
seen[i] = true;
let mut exact = 0;
let mut visit = |j: usize, seen: &mut Vec<bool>| {
if alive(j) && !seen[j] {
seen[j] = true;
exact += self.nv[j];
}
};
for &e in self.elems(i) {
if self.kind[u(e)] == Kind::Elem {
for &j in &self.list[u(e)] {
visit(u(j), &mut seen);
}
}
}
for &j in self.vars(i) {
visit(u(j), &mut seen);
}
if self.deg[i] < exact {
return Err(format!("(Q4): d̄_{i} = {} < exact {exact}", self.deg[i]));
}
}
Ok(())
}
}