use std::collections::{HashMap, HashSet};
pub const DEFAULT_DAMPING: f64 = 0.85;
pub const DEFAULT_ITERATIONS: u32 = 20;
pub fn personalized_page_rank(
nodes: &[String],
fwd_adj: &HashMap<String, HashSet<String>>,
seeds: &[String],
damping: f64,
iterations: u32,
) -> HashMap<String, f64> {
let n = nodes.len();
if n == 0 {
return HashMap::new();
}
let idx: HashMap<&str, usize> = nodes
.iter()
.enumerate()
.map(|(i, s)| (s.as_str(), i))
.collect();
let mut teleport = vec![0f64; n];
let mut wsum = 0f64;
for s in seeds {
if let Some(&i) = idx.get(s.as_str()) {
teleport[i] = 1.0;
wsum += 1.0;
}
}
if wsum == 0.0 {
let v = 1.0 / n as f64;
for t in teleport.iter_mut() {
*t = v;
}
} else {
for t in teleport.iter_mut() {
*t /= wsum;
}
}
let out_lists: Vec<Vec<usize>> = nodes
.iter()
.map(|id| match fwd_adj.get(id) {
None => Vec::new(),
Some(s) => s
.iter()
.filter_map(|t| idx.get(t.as_str()).copied())
.collect(),
})
.collect();
let mut rank = teleport.clone();
for _ in 0..iterations {
let mut next = vec![0f64; n];
for i in 0..n {
let r = rank[i];
if r == 0.0 {
continue;
}
let outs = &out_lists[i];
if outs.is_empty() {
for k in 0..n {
next[k] += damping * r * teleport[k]; }
continue;
}
let share = (damping * r) / outs.len() as f64;
for &j in outs {
next[j] += share;
}
}
for k in 0..n {
next[k] += (1.0 - damping) * teleport[k];
}
rank = next;
}
let mut result = HashMap::with_capacity(n);
for (i, id) in nodes.iter().enumerate() {
result.insert(id.clone(), rank[i]);
}
result
}