#![cfg(feature = "alloc")]
use alloc::vec::Vec;
use super::{XorShift64, builder_utils::build_symmetric};
use crate::impls::{CSR2D, SymmetricCSR2D};
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub fn barabasi_albert(
seed: u64,
n: usize,
m: usize,
) -> SymmetricCSR2D<CSR2D<usize, usize, usize>> {
assert!(m >= 1, "m must be at least 1");
let initial_clique = m + 1;
if n <= 1 {
return build_symmetric(n, Vec::new());
}
if n <= initial_clique {
let mut edges = Vec::new();
for u in 0..n {
for v in (u + 1)..n {
edges.push((u, v));
}
}
return build_symmetric(n, edges);
}
let mut rng = XorShift64::from(XorShift64::normalize_seed(seed));
let clique_edges = initial_clique * (initial_clique - 1) / 2;
let total_edges_estimate = clique_edges + (n - initial_clique) * m;
let mut stubs: Vec<usize> = Vec::with_capacity(total_edges_estimate * 2);
let mut edges: Vec<(usize, usize)> = Vec::with_capacity(total_edges_estimate);
for u in 0..initial_clique {
for v in (u + 1)..initial_clique {
edges.push((u, v));
stubs.push(u);
stubs.push(v);
}
}
for v in initial_clique..n {
let mut targets: Vec<usize> = Vec::with_capacity(m);
while targets.len() < m {
let idx = (rng.next().unwrap() as usize) % stubs.len();
let target = stubs[idx];
if !targets.contains(&target) {
targets.push(target);
}
}
for &t in &targets {
let (u, w) = if t < v { (t, v) } else { (v, t) };
edges.push((u, w));
stubs.push(v);
stubs.push(t);
}
}
edges.sort_unstable();
edges.dedup();
build_symmetric(n, edges)
}