// 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 find paths between nodes.
use crate::data::WebOfTrust;
use crate::data::WotId;
use std::collections::HashSet;
/// Find paths between 2 nodes of a `WebOfTrust`.
pub trait PathFinder {
/// Get paths from one node to the other.
fn find_paths(&self, wot: &T, from: WotId, to: WotId, k_max: u32) -> Vec>;
}
/// A new "rusty-er" implementation of `WoT` path finding.
#[derive(Debug, Clone, Copy)]
pub struct RustyPathFinder;
impl PathFinder for RustyPathFinder {
fn find_paths(&self, wot: &T, from: WotId, to: WotId, k_max: u32) -> Vec> {
if from.0 >= wot.size() || to.0 >= wot.size() {
return vec![];
}
// 1. We explore the k_max area around `to`, and only remember backward
// links of the smallest distance.
// Stores for each node its distance to `to` node and its backward links.
// By default all nodes are out of range (`k_max + 1`) and links are known.
let mut graph: Vec<(u32, Vec)> =
(0..wot.size()).map(|_| (k_max + 1, vec![])).collect();
// `to` node is at distance 0, and have no backward links.
graph[to.0] = (0, vec![]);
// Explored zone border.
let mut border = HashSet::new();
border.insert(to);
for distance in 1..=k_max {
let mut next_border = HashSet::new();
for node in border {
for source in &wot
.get_links_source(node)
.expect("links source must not be None")
{
match graph[source.0].0 {
path_distance if path_distance > distance => {
// shorter path, we replace
graph[source.0] = (distance, vec![node]);
next_border.insert(*source);
}
path_distance if path_distance == distance => {
// same length, we combine
graph[source.0].1.push(node);
next_border.insert(*source);
}
_ => unreachable!(),
}
}
}
border = next_border;
}
// 2. If `from` is found, we follow the backward links and build paths.
// For each path, we look at the last element sources and build new paths with them.
let mut paths = vec![vec![from]];
for _ in 1..=k_max {
let mut new_paths = vec![];
for path in &paths {
let node = path.last().expect("path should not be empty");
if node == &to {
// If path is complete, we keep it.
new_paths.push(path.clone())
} else {
// If not complete we comlete paths
let sources = &graph[node.0];
for source in &sources.1 {
let mut new_path = path.clone();
new_path.push(*source);
new_paths.push(new_path);
}
}
}
paths = new_paths;
}
paths
}
}