#![expect(
clippy::missing_const_for_fn,
reason = "RNG methods mutate internal state"
)]
#![expect(
clippy::cast_possible_truncation,
reason = "benchmark node counts are small enough for usize on CI targets"
)]
pub struct Rng(u64);
impl Rng {
#[must_use]
pub const fn new(seed: u64) -> Self {
Self(if seed == 0 { 1 } else { seed })
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.0 = x;
x
}
#[inline]
pub fn next_u32(&mut self) -> u32 {
u32::try_from(self.next_u64() >> 16).unwrap_or(u32::MAX)
}
#[inline]
pub fn next_bounded(&mut self, n: u32) -> u32 {
u32::try_from((u64::from(self.next_u32()) * u64::from(n)) >> 32).unwrap_or(0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RawEdge {
pub source: u32,
pub target: u32,
}
#[derive(Clone, Debug)]
pub struct GeneratedBenchmarkGraph {
pub node_count: u32,
pub raw_edges: Vec<RawEdge>,
}
pub const BENCH_SEED: u64 = 42;
pub const BENCH_NODE_COUNT: u32 = 10_000;
pub const BENCH_AVG_DEGREE: u32 = 3;
pub const BENCH_LABEL_10K: &str = "10k";
#[must_use]
pub fn build_benchmark_fixture(
node_count: u32,
avg_degree: u32,
seed: u64,
) -> GeneratedBenchmarkGraph {
let mut rng = Rng::new(seed);
let total_edges = (u64::from(node_count) * u64::from(avg_degree)) as usize;
let mut raw_edges = Vec::with_capacity(total_edges.saturating_mul(2));
let mut edge_list: Vec<u32> = (0..node_count).collect();
for _ in 0..total_edges {
let source = rng.next_bounded(node_count);
let target_idx = (rng.next_u64() % edge_list.len() as u64) as usize;
let target = edge_list[target_idx];
if source == target {
continue;
}
raw_edges.push(RawEdge { source, target });
raw_edges.push(RawEdge {
source: target,
target: source,
});
edge_list.push(source);
edge_list.push(target);
}
GeneratedBenchmarkGraph {
node_count,
raw_edges,
}
}
#[must_use]
pub fn out_degrees(fixture: &GeneratedBenchmarkGraph) -> Vec<u32> {
let mut degree = vec![0_u32; fixture.node_count as usize];
for edge in &fixture.raw_edges {
let source = edge.source as usize;
if source < degree.len() {
degree[source] = degree[source].saturating_add(1);
}
}
degree
}
#[must_use]
pub fn find_supernode(fixture: &GeneratedBenchmarkGraph) -> u32 {
let mut best_idx = 0_u32;
let mut best_degree = 0_u32;
for (idx, °) in out_degrees(fixture).iter().enumerate() {
if deg > best_degree {
best_degree = deg;
best_idx = u32::try_from(idx).unwrap_or(0);
}
}
best_idx
}