use std::cmp::Reverse;
use std::collections::{BTreeMap, BinaryHeap};
use super::execution::{DEADLINE_CHECK_STRIDE, ElimExit, ElimSink, ElimStop, exceeds_width_bound};
use super::graph::EliminationGraph;
use crate::rng::Xorshift64;
macro_rules! ord_by_key {
($t:ty) => {
impl Ord for $t {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.key.cmp(&other.key)
}
}
impl PartialOrd for $t {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
};
}
mod deterministic;
mod min_degree;
mod min_fill;
mod sampling;
#[cfg(test)]
mod tests;
pub(super) use min_degree::eliminate_min_degree;
pub(super) use min_fill::eliminate_min_fill;
pub(super) use sampling::{eliminate_sampled_min_degree, eliminate_sampled_min_fill};
pub(super) const CHEAP_MODE_MAX_ACTIVE: usize = 512;
struct FillScratch {
marker: Vec<u16>,
stamp: u16,
}
impl FillScratch {
fn new(n: usize) -> Self {
FillScratch {
marker: vec![0; n],
stamp: 0,
}
}
#[inline]
fn bump_stamp(&mut self) {
self.stamp = self.stamp.wrapping_add(1);
if self.stamp == 0 {
self.marker.fill(0);
self.stamp = 1;
}
}
fn fill_count_of(&mut self, graph: &EliminationGraph, v: u32) -> u64 {
if graph.bitset_words > 0 {
crate::meter::charge(
(graph.degree(v) as u64).saturating_mul(graph.bitset_words as u64),
);
return graph.fill_count_of_bs(v);
}
let nbrs_v = graph.adj[v as usize].as_slice();
let k = nbrs_v.len();
if crate::meter::is_armed() {
let sigma: u64 = nbrs_v
.iter()
.map(|&u| graph.adj[u as usize].len() as u64)
.sum();
crate::meter::charge(k as u64 + sigma);
}
if k < 2 {
return 0;
}
self.bump_stamp();
let s = self.stamp;
for &u in nbrs_v {
self.marker[u as usize] = s;
}
let mut doubled = 0u64;
let marker = self.marker.as_ptr();
for &u in nbrs_v {
let adj_u = unsafe { graph.adj.get_unchecked(u as usize) };
for &w in adj_u.iter() {
let m = unsafe { *marker.add(w as usize) };
doubled += (m == s) as u64;
}
}
let edge_count = doubled / 2;
let total_pairs = (k as u64) * (k as u64 - 1) / 2;
total_pairs - edge_count
}
}
fn take_bag(graph: &EliminationGraph, v: u32, nbrs_buf: &mut Vec<u32>) -> Vec<u32> {
nbrs_buf.clear();
graph.collect_live_nbrs_into(v, nbrs_buf);
let mut bag = Vec::with_capacity(nbrs_buf.len() + 1);
bag.push(v);
bag.extend_from_slice(nbrs_buf);
bag
}
fn drain_clique_tail<E: Ord + ElimEntry>(
graph: &mut EliminationGraph,
sink: &mut ElimSink<'_>,
heap: &mut BinaryHeap<E>,
nbrs_buf: &mut Vec<u32>,
) {
while let Some(entry) = heap.pop() {
let v = entry.vertex();
let vi = v as usize;
if !graph.active[vi] {
continue;
}
let bag = take_bag(graph, v, nbrs_buf);
graph.remove_without_fill_nbrs(v, nbrs_buf);
sink.record(v, bag);
}
}
trait ElimEntry {
fn vertex(&self) -> u32;
fn snapshot(&self) -> u64;
}
#[derive(Clone)]
pub(super) struct BucketMap {
buckets: BTreeMap<u64, Vec<u32>>,
position: Vec<Option<(u64, usize)>>,
}
impl BucketMap {
fn with_capacity(n: usize) -> Self {
BucketMap {
buckets: BTreeMap::new(),
position: vec![None; n],
}
}
fn insert(&mut self, v: u32, key: u64) {
let bucket = self.buckets.entry(key).or_default();
let idx = bucket.len();
bucket.push(v);
self.position[v as usize] = Some((key, idx));
}
fn remove_vertex(&mut self, v: u32) {
if let Some((key, idx)) = self.position[v as usize].take() {
let bucket = self.buckets.get_mut(&key).expect("bucket missing");
let last_idx = bucket.len() - 1;
if idx != last_idx {
let moved = bucket[last_idx];
bucket[idx] = moved;
self.position[moved as usize] = Some((key, idx));
}
bucket.pop();
if bucket.is_empty() {
self.buckets.remove(&key);
}
}
}
fn update(&mut self, v: u32, new_key: u64) {
if let Some((cur_key, _)) = self.position[v as usize] {
if cur_key == new_key {
return;
}
self.remove_vertex(v);
}
self.insert(v, new_key);
}
fn min_bucket(&self) -> Option<(u64, &[u32])> {
self.buckets
.iter()
.next()
.map(|(key, vertices)| (*key, vertices.as_slice()))
}
fn key_of(&self, v: u32) -> Option<u64> {
self.position[v as usize].map(|(key, _)| key)
}
}
pub(super) fn compute_initial_fill(graph: &EliminationGraph) -> Vec<u64> {
let n = graph.len();
let mut scratch = FillScratch::new(n);
(0..n)
.map(|v| {
if graph.active[v] {
scratch.fill_count_of(graph, v as u32)
} else {
0
}
})
.collect()
}
#[inline]
fn sampling_mass(earlier_first_weight: u32) -> u64 {
u64::from(u32::MAX - earlier_first_weight) + 1
}
fn sample_tie_set(tie_set: &[u32], weights: &[u32], rng: &mut Xorshift64) -> u32 {
debug_assert!(!tie_set.is_empty());
if tie_set.len() == 1 {
return tie_set[0];
}
let mut total: u64 = 0;
for &v in tie_set {
total += sampling_mass(weights[v as usize]);
}
let hi = rng.next_u32() as u64;
let lo = rng.next_u32() as u64;
let r = ((hi << 32) | lo) % total;
let mut acc: u64 = 0;
let mut pick = tie_set.len() - 1;
for (i, &v) in tie_set.iter().enumerate() {
acc += sampling_mass(weights[v as usize]);
if r < acc {
pick = i;
break;
}
}
crate::meter::charge(tie_set.len() as u64 + pick as u64 + 1);
tie_set[pick]
}