use std::collections::{HashMap, VecDeque};
pub fn betweenness_centrality(adjacency: &HashMap<i64, Vec<(i64, f64)>>) -> HashMap<i64, f64> {
let mut all_nodes: Vec<i64> = Vec::new();
for (&src, neighbors) in adjacency {
all_nodes.push(src);
for &(dst, _) in neighbors {
all_nodes.push(dst);
}
}
all_nodes.sort_unstable();
all_nodes.dedup();
let n = all_nodes.len();
let id_to_idx: HashMap<i64, usize> = all_nodes
.iter()
.enumerate()
.map(|(i, &id)| (id, i))
.collect();
let mut adj_idx: Vec<Vec<usize>> = vec![Vec::new(); n];
for (&src, neighbors) in adjacency {
let si = id_to_idx[&src];
for &(dst, _) in neighbors {
let di = id_to_idx[&dst];
adj_idx[si].push(di);
}
}
let mut centrality = vec![0.0_f64; n];
for s in 0..n {
let mut stack: Vec<usize> = Vec::new();
let mut predecessors: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut sigma = vec![0.0_f64; n]; sigma[s] = 1.0;
let mut dist = vec![-1_i64; n];
dist[s] = 0;
let mut queue = VecDeque::new();
queue.push_back(s);
while let Some(v) = queue.pop_front() {
stack.push(v);
for &w in &adj_idx[v] {
if dist[w] < 0 {
dist[w] = dist[v] + 1;
queue.push_back(w);
}
if dist[w] == dist[v] + 1 {
sigma[w] += sigma[v];
predecessors[w].push(v);
}
}
}
let mut delta = vec![0.0_f64; n];
while let Some(w) = stack.pop() {
for &v in &predecessors[w] {
delta[v] += (sigma[v] / sigma[w]) * (1.0 + delta[w]);
}
if w != s {
centrality[w] += delta[w];
}
}
}
all_nodes
.iter()
.enumerate()
.map(|(i, &id)| (id, centrality[i]))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph() {
let adj = HashMap::new();
let result = betweenness_centrality(&adj);
assert!(result.is_empty());
}
#[test]
fn single_node() {
let mut adj = HashMap::new();
adj.insert(1, vec![]);
let result = betweenness_centrality(&adj);
assert_eq!(result.len(), 1);
assert!((result[&1] - 0.0).abs() < 1e-10);
}
#[test]
fn linear_chain() {
let mut adj = HashMap::new();
adj.insert(1, vec![(2, 1.0)]);
adj.insert(2, vec![(3, 1.0)]);
adj.insert(3, vec![(4, 1.0)]);
adj.insert(4, vec![]);
let result = betweenness_centrality(&adj);
assert!(result[&2] > 0.0);
assert!(result[&3] > 0.0);
assert!((result[&1] - 0.0).abs() < 1e-10);
}
#[test]
fn star_graph_undirected() {
let mut adj = HashMap::new();
adj.insert(1, vec![(2, 1.0), (3, 1.0), (4, 1.0), (5, 1.0)]);
adj.insert(2, vec![(1, 1.0)]);
adj.insert(3, vec![(1, 1.0)]);
adj.insert(4, vec![(1, 1.0)]);
adj.insert(5, vec![(1, 1.0)]);
let result = betweenness_centrality(&adj);
assert!(result[&1] > result[&2]);
assert!(result[&1] > result[&3]);
}
#[test]
fn triangle_undirected() {
let mut adj = HashMap::new();
adj.insert(1, vec![(2, 1.0), (3, 1.0)]);
adj.insert(2, vec![(1, 1.0), (3, 1.0)]);
adj.insert(3, vec![(1, 1.0), (2, 1.0)]);
let result = betweenness_centrality(&adj);
assert!((result[&1] - result[&2]).abs() < 1e-10);
assert!((result[&2] - result[&3]).abs() < 1e-10);
}
}