use data::NodeId;
use data::WebOfTrust;
use rayon::prelude::*;
use std::collections::HashSet;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct WotDistanceParameters {
pub node: NodeId,
pub sentry_requirement: u32,
pub step_max: u32,
pub x_percent: f64,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct WotDistance {
pub sentries: u32,
pub success: u32,
pub success_at_border: u32,
pub reached: u32,
pub reached_at_border: u32,
pub outdistanced: bool,
}
pub trait DistanceCalculator<T: WebOfTrust> {
fn compute_distance(&self, wot: &T, params: WotDistanceParameters) -> Option<WotDistance>;
fn is_outdistanced(&self, wot: &T, params: WotDistanceParameters) -> Option<bool>;
}
#[derive(Debug, Clone, Copy)]
pub struct RustyDistanceCalculator;
impl<T: WebOfTrust + Sync> DistanceCalculator<T> for RustyDistanceCalculator {
fn compute_distance(&self, wot: &T, params: WotDistanceParameters) -> Option<WotDistance> {
let WotDistanceParameters {
node,
sentry_requirement,
step_max,
x_percent,
} = params;
if node.0 >= wot.size() {
return None;
}
let mut area = HashSet::new();
area.insert(node);
let mut border = HashSet::new();
border.insert(node);
for _ in 0..step_max {
border = border
.par_iter()
.map(|&id| {
wot.get_links_source(id)
.unwrap()
.iter()
.filter(|source| !area.contains(source))
.cloned()
.collect::<HashSet<_>>()
})
.reduce(HashSet::new, |mut acc, sources| {
for source in sources {
acc.insert(source);
}
acc
});
area.extend(border.iter());
}
let sentries: Vec<_> = wot.get_sentries(sentry_requirement as usize);
let mut success = area.iter().filter(|n| sentries.contains(n)).count() as u32;
let success_at_border = border.iter().filter(|n| sentries.contains(n)).count() as u32;
let mut sentries = sentries.len() as u32;
if wot.is_sentry(node, sentry_requirement as usize).unwrap() {
sentries -= 1;
success -= 1;
}
Some(WotDistance {
sentries,
reached: area.len() as u32 - 1,
reached_at_border: border.len() as u32,
success,
success_at_border,
outdistanced: f64::from(success) < x_percent * f64::from(sentries),
})
}
fn is_outdistanced(&self, wot: &T, params: WotDistanceParameters) -> Option<bool> {
Self::compute_distance(&self, wot, params).map(|result| result.outdistanced)
}
}