// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
//! Provide a trait and implementations to compute distances.
use crate::data::WebOfTrust;
use crate::data::WotId;
use rayon::prelude::*;
use std::collections::HashSet;
/// Paramters for `WoT` distance calculations
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct WotDistanceParameters {
/// Node from where distances are calculated.
pub node: WotId,
/// Links count received AND issued to be a sentry.
pub sentry_requirement: u32,
/// Currency parameter.
pub step_max: u32,
/// Currency parameter.
pub x_percent: f64,
}
/// Results of `WebOfTrust::compute_distance`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct WotDistance {
/// Sentries count
pub sentries: u32,
/// Success count
pub success: u32,
/// Succes at border count
pub success_at_border: u32,
/// Reached count
pub reached: u32,
/// Reached at border count
pub reached_at_border: u32,
/// Is the node outdistanced ?
pub outdistanced: bool,
}
/// Compute distance between nodes of a `WebOfTrust`.
pub trait DistanceCalculator {
/// Compute distance between a node and the network.
/// Returns `None` if this node doesn't exist.
fn compute_distance(&self, wot: &T, params: WotDistanceParameters) -> Option;
/// Compute distances of all members
fn compute_distances(
&self,
wot: &T,
sentry_requirement: u32,
step_max: u32,
x_percent: f64,
) -> (usize, Vec, usize, Vec);
/// Test if a node is outdistanced in the network.
/// Returns `Node` if this node doesn't exist.
fn is_outdistanced(&self, wot: &T, params: WotDistanceParameters) -> Option;
}
/// Calculate distances between 2 members in a `WebOfTrust`.
#[derive(Debug, Clone, Copy)]
pub struct RustyDistanceCalculator;
impl DistanceCalculator for RustyDistanceCalculator {
fn compute_distance(&self, wot: &T, params: WotDistanceParameters) -> Option {
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)
.expect("get_links_source must return a value")
.iter()
.filter(|source| !area.contains(source))
.cloned()
.collect::>()
})
.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)
.expect("is_sentry must return a value")
{
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 {
Self::compute_distance(&self, wot, params).map(|result| result.outdistanced)
}
fn compute_distances(
&self,
wot: &T,
sentry_requirement: u32,
step_max: u32,
x_percent: f64,
) -> (usize, Vec, usize, Vec) {
let members_count = wot.get_enabled().len();
let mut distances = Vec::new();
let mut average_distance: usize = 0;
let mut connectivities = Vec::new();
let mut average_connectivity: usize = 0;
for i in 0..wot.size() {
let distance_datas: WotDistance = Self::compute_distance(
&self,
wot,
WotDistanceParameters {
node: WotId(i),
sentry_requirement,
step_max,
x_percent,
},
)
.expect("Fatal Error: compute_distance return None !");
let distance = ((f64::from(distance_datas.success)
/ (x_percent * f64::from(distance_datas.sentries)))
* 100.0) as usize;
distances.push(distance);
average_distance += distance;
let connectivity =
((f64::from(distance_datas.success - distance_datas.success_at_border)
/ (x_percent * f64::from(distance_datas.sentries)))
* 100.0) as usize;
connectivities.push(connectivity);
average_connectivity += connectivity;
}
average_distance /= members_count;
average_connectivity /= members_count;
(
average_distance,
distances,
average_connectivity,
connectivities,
)
}
}