luka 0.4.0

Library for working with graphs
Documentation
use crate::{Graph, Vertex};
use std::collections::VecDeque;
use crate::error::GraphError;
use crate::algorithms::definitions::Parents;

/// Breadth-first search (BFS) is one of the simplest graph traversal algorithms and is the basis for many important graph algorithms.
/// Algorithmic complexity - O(|V| + |E|), where |V| is the number of vertexs in the graph and |E| is the number of edges in the graph.
/// ```
/// use luka::Graph;
/// use luka::algorithms;
/// use luka::utils;
///
/// let mut graph = Graph::new(100);
/// graph.add_edge(1, 2, 0.0).unwrap();
/// graph.add_edge(2, 3, 0.0).unwrap();
/// graph.add_edge(2, 4, 0.0).unwrap();
/// graph.add_edge(2, 5, 0.0).unwrap();
/// graph.add_edge(4, 8, 0.0).unwrap();
/// graph.add_edge(8, 17, 0.0).unwrap();
///
/// let start = graph.get_vertex(1).unwrap();
/// let target = graph.get_vertex(17).unwrap();
/// let parents = algorithms::bfs(&graph, &start).unwrap();
/// match utils::find_path(&graph, &target, &parents).unwrap() {
///     Some(path) => {
///         assert_eq!(path.iter().map(|vertex|vertex.id()).collect::<Vec<usize>>(), vec![1, 2, 4, 8, 17]);
///     }
///     None => {
///        println!("Path not found !!!")
///     }
/// }
/// ```

pub fn bfs<'a, T>(graph: &'a Graph<T>, from: &'a Vertex<T>) -> Result<Parents<'a, T>, GraphError> {
    let mut queue = VecDeque::new();
    let mut parents = vec![None; graph.size()];
    let mut visited = vec![false; graph.size()];
    let from = from.id();
    queue.push_back(from);
    visited[from] = true;
    while let Some(id) = queue.pop_front(){
        for edge in &graph.adj[id].edges {
            if !visited[edge.to] {
                parents[edge.to] = Some(&graph.adj[id]);
                queue.push_back(edge.to);
                visited[edge.to] = true;
            }
        }
    }
    Ok(parents)
}

#[derive(PartialEq)]
pub enum VisitorBFSAction {
    Nothing,
    Break,
    Error(GraphError)
}

/// Allows BFS algorithm to be extended with callbacks on specific events
pub trait VisitorBFS<T> {
    /// Callback when the next vertex is retrieved from the BFS queue
    #[allow(unused_variables)]
    fn queue_extract_event(&mut self, vertex: &Vertex<T>) -> Result<VisitorBFSAction, GraphError> {
        Ok(VisitorBFSAction::Nothing)
    }
    /// Callback when a vertex is placed in the BFS queue
    #[allow(unused_variables)]
    fn place_in_queue_event(&mut self, vertex: &Vertex<T>, parents: &[Option<&Vertex<T>>]) -> Result<VisitorBFSAction, GraphError> {
        Ok(VisitorBFSAction::Nothing)
    }
    /// Callback when a vertex is considered visited
    #[allow(unused_variables)]
    fn vertex_visited_event(&mut self, vertex: &Vertex<T>, parents: &[Option<&Vertex<T>>]) -> Result<VisitorBFSAction, GraphError> {
        Ok(VisitorBFSAction::Nothing)
    }
}

/// Breadth-first search (BFS) with visitors is one of the simplest graph traversal algorithms and is the basis for many important graph algorithms.
/// Algorithmic complexity - O(|V| + |E|), where |V| is the number of vertexs in the graph and |E| is the number of edges in the graph.
/// Used by a visitor you can intercept certain events of the algorithm
/// ```
/// use luka::Graph;
/// use luka::Vertex;
/// use luka::algorithms;
/// use luka::algorithms::{VisitorBFS, VisitorBFSAction};
/// use luka::utils;
/// use luka::error::GraphError;
///
/// struct CustomVisitor {
///     visiting_order: Vec<usize>
/// }
///
/// impl <T> VisitorBFS<T> for CustomVisitor {
///     fn queue_extract_event(&mut self, vertex: &Vertex<T>) -> Result<VisitorBFSAction, GraphError> {
///         self.visiting_order.push(vertex.id());
///         Ok(VisitorBFSAction::Nothing)
///     }
/// }
///
/// let mut graph = Graph::new(100);
/// graph.add_edge(1, 2, 0.0).unwrap();
/// graph.add_edge(2, 3, 0.0).unwrap();
/// graph.add_edge(2, 4, 0.0).unwrap();
/// graph.add_edge(2, 5, 0.0).unwrap();
/// graph.add_edge(4, 8, 0.0).unwrap();
/// graph.add_edge(8, 17, 0.0).unwrap();
///
/// let start = graph.get_vertex(1).unwrap();
/// let mut visitor = CustomVisitor{ visiting_order: vec![] };
/// algorithms::bfs_with_visitor(&graph, &start, &mut visitor).unwrap();
///
/// assert_eq!(vec![1, 2, 3, 4, 5, 8, 17], visitor.visiting_order);
/// ```

pub fn bfs_with_visitor<'a, 'b, T>(graph: &'a Graph<T>, from: &'a Vertex<T>, visitor: &'b mut dyn VisitorBFS<T>) -> Result<Parents<'a, T>, GraphError> {
    let mut queue = VecDeque::new();
    let mut parents = vec![None; graph.size()];
    let mut visited = vec![false; graph.size()];
    let from = from.id();
    queue.push_back(from);
    match visitor.place_in_queue_event(&graph.adj[from], &parents)? {
        VisitorBFSAction::Nothing => {}
        VisitorBFSAction::Break => {
            return Ok(parents);
        }
        VisitorBFSAction::Error(err) => {
            return Err(err);
        }
    }
    visited[from] = true;
    match visitor.vertex_visited_event(&graph.adj[from], &parents)? {
        VisitorBFSAction::Nothing => {}
        VisitorBFSAction::Break => {
            return Ok(parents);
        }
        VisitorBFSAction::Error(err) => {
            return Err(err);
        }
    }
    while let Some(from) = queue.pop_front(){
        match visitor.queue_extract_event(&graph.adj[from])? {
            VisitorBFSAction::Nothing => {}
            VisitorBFSAction::Break => {
                break;
            }
            VisitorBFSAction::Error(err) => {
                return Err(err);
            }
        }
        for edge in &graph.adj[from].edges {
            if !visited[edge.to] {
                parents[edge.to] = Some(&graph.adj[from]);
                queue.push_back(edge.to);
                match visitor.place_in_queue_event(&graph.adj[edge.to], &parents)? {
                    VisitorBFSAction::Nothing => {}
                    VisitorBFSAction::Break => {
                        return Ok(parents);
                    }
                    VisitorBFSAction::Error(err) => {
                        return Err(err);
                    }
                }
                visited[edge.to] = true;
                visited[from] = true;
                match visitor.vertex_visited_event(&graph.adj[edge.to], &parents)? {
                    VisitorBFSAction::Nothing => {}
                    VisitorBFSAction::Break => {
                        return Ok(parents);
                    }
                    VisitorBFSAction::Error(err) => {
                        return Err(err);
                    }
                }
            }
        }
    }
    Ok(parents)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_bfs_algorithm() {
        use crate::utils;

        let mut graph = Graph::new(100);
        graph.add_edge(1, 2, 0.0).unwrap();
        graph.add_edge(2, 3, 0.0).unwrap();
        graph.add_edge(2, 4, 0.0).unwrap();
        graph.add_edge(2, 5, 0.0).unwrap();
        graph.add_edge(4, 8, 0.0).unwrap();
        graph.add_edge(8, 17, 0.0).unwrap();

        let start = graph.get_vertex(1).unwrap();
        let target = graph.get_vertex(5).unwrap();
        let parents = bfs(&graph, &start).unwrap();
        match utils::find_path(&graph, &target, &parents).unwrap() {
            Some(path) => {
                assert_eq!(path.iter().map(|vertex|vertex.id()).collect::<Vec<usize>>(), vec![1, 2, 5]);
            }
            None => {
                println!("Path not found !!!")
            }
        }

        let target = graph.get_vertex(17).unwrap();
        let path = utils::find_path(&graph, &target, &parents).unwrap().unwrap();
        assert_eq!(path.iter().map(|vertex|vertex.id()).collect::<Vec<usize>>(), vec![1, 2, 4, 8, 17]);
    }

    #[test]
    #[should_panic]
    fn test_bfs_algorithm_panic() {
        let mut graph = Graph::new(100);
        graph.add_edge(1, 2, 0.0).unwrap();
        let start = graph.get_vertex(122).unwrap();
        bfs(&graph, &start).unwrap();
    }

    #[test]
    fn test_bfs_with_visitor_algorithm() {
        struct CustomVisitor {
            visiting_order: Vec<usize>
        }

        impl <T> VisitorBFS<T> for CustomVisitor {
            fn queue_extract_event(&mut self, vertex: &Vertex<T>) -> Result<VisitorBFSAction, GraphError> {
                self.visiting_order.push(vertex.id());
                Ok(VisitorBFSAction::Nothing)
            }
        }

        let mut graph = Graph::new(100);
        graph.add_edge(1, 2, 0.0).unwrap();
        graph.add_edge(2, 3, 0.0).unwrap();
        graph.add_edge(2, 4, 0.0).unwrap();
        graph.add_edge(2, 5, 0.0).unwrap();
        graph.add_edge(4, 8, 0.0).unwrap();
        graph.add_edge(8, 17, 0.0).unwrap();

        let mut visitor = CustomVisitor{ visiting_order: vec![] };
        let start = graph.get_vertex(1).unwrap();
        bfs_with_visitor(&graph, &start, &mut visitor).unwrap();
        assert_eq!(vec![1, 2, 3, 4, 5, 8, 17], visitor.visiting_order);
    }
}