use crate::{cn, Network};
use indexmap::map::Entry;
use rand::prelude::{IteratorRandom, Rng};
#[derive(Debug, Clone)]
pub struct Observers {
true_source: usize,
true_time: usize,
observers: cn::IndexMap<usize, usize>,
}
impl Observers {
pub fn true_source(&self) -> usize {
self.true_source
}
pub fn true_time(&self) -> usize {
self.true_time
}
pub fn as_map(&self) -> &cn::IndexMap<usize, usize> {
&self.observers
}
pub fn by_time(&mut self) -> &mut Self {
self.observers.sort_by(|_, t1, _, t2| t1.cmp(t2));
self
}
pub fn keep(&mut self, keep: &[usize]) -> &mut Self {
self.observers.retain(|index, _| keep.contains(index));
self
}
pub fn keep_random(&mut self, amount: usize, rng: &mut impl Rng) -> &mut Self {
if amount >= self.observers.len() {
self.observers.clear();
} else if amount == 0 {
return self;
} else {
let to_keep = self
.observers
.keys()
.copied()
.choose_multiple(rng, amount);
self.keep(&to_keep);
}
self
}
pub fn keep_portion(&mut self, portion: f64, rng: &mut impl Rng) -> &mut Self {
self.keep_random((portion * self.as_map().len() as f64).round() as usize, rng)
}
pub fn adjust_to(&mut self, net: &Network) -> &mut Self {
self.keep(&net.nodes().collect::<Vec<usize>>())
}
}
pub fn spread(net: &Network, source: usize, steps: usize) -> cn::Result<Observers> {
let mut infected = cn::IndexMap::default();
infected.insert(source, 0);
for step in 1..steps + 1 {
let inf: Vec<usize> = infected.keys().copied().collect();
for i in inf {
for (neighbor, weight) in net.links_of(i)? {
if let Entry::Vacant(entry) = infected.entry(*neighbor) {
if net.rng_lock().unwrap().gen::<f64>() < *weight {
entry.insert(step);
}
}
}
}
}
Ok(Observers {
true_source: source,
true_time: 0,
observers: infected,
})
}
pub fn spread_random(net: &Network, steps: usize) -> Observers {
let root = net.nodes().choose(&mut *net.rng_lock().unwrap()).unwrap();
spread(net, root, steps).unwrap()
}