< Summary

Information
Class: MoreStructures.Graphs.ShortestPath.BfsBasedShortestPathFinder
Assembly: MoreStructures
File(s): /home/runner/work/MoreStructures/MoreStructures/MoreStructures/Graphs/ShortestPath/BfsBasedShortestPathFinder.cs
Line coverage
100%
Covered lines: 29
Uncovered lines: 0
Coverable lines: 29
Total lines: 121
Line coverage: 100%
Branch coverage
100%
Covered branches: 6
Total branches: 6
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
get_VisitStrategyBuilder()100%1100%
.ctor(...)100%1100%
Find(...)100%2100%
BuildShortestPath(...)100%4100%

File(s)

/home/runner/work/MoreStructures/MoreStructures/MoreStructures/Graphs/ShortestPath/BfsBasedShortestPathFinder.cs

#LineLine coverage
 1using MoreLinq.Extensions;
 2using MoreStructures.Graphs.Visitor;
 3
 4namespace MoreStructures.Graphs.ShortestPath;
 5
 6/// <summary>
 7/// An implementation of <see cref="IShortestPathFinder"/> which performs a BFS traversal, starting from the start
 8/// vertex, to find the shortest path to the end vertex.
 9/// </summary>
 10/// <remarks>
 11///     <para id="algorithm">
 12///     ALGORITHM
 13///     <br/>
 14///     - A <see cref="IVisitStrategy"/> is instantiated via the <see cref="VisitStrategyBuilder"/> provided in the
 15///       constructor.
 16///       <br/>
 17///     - The <see cref="IVisitStrategy"/> instance is instrumented, by adding an event handler H to the
 18///       <see cref="IVisitStrategy.VisitingVertex"/>.
 19///       <br/>
 20///     - The event handler H maps each visited vertex to its previous vertex (i.e. the vertex whose visit has trigger
 21///       the visit of this vertex), as stored in <see cref="VisitEventArgs.PreviousVertex"/> passed by the
 22///       <see cref="IVisitStrategy.VisitingVertex"/> event, into a <see cref="IDictionary{TKey, TValue}"/> D.
 23///       <br/>
 24///     - A Breadth First Search from the start vertex is executed, running
 25///       <see cref="IVisitStrategy.BreadthFirstSearchFromVertex"/>. The result of the visit is consumed (i.e. the
 26///       visit is done) until either the end vertex is encountered, or when there are no more vertices reachable from
 27///       the start vertex, which haven't been visited yet.
 28///       <br/>
 29///     - If the end vertex hasn't been discovered during the visit, it is not reachable. Therefore, a length of path
 30///       equal to <see cref="int.MaxValue"/> and an empty path are returned.
 31///       <br/>
 32///     - If the end vertex has been reached, the end vertex has been visited and it is contained in D.
 33///       <br/>
 34///     - D is traversed backwards, from the end to the start vertex, rebuilding the (reversed) path.
 35///       <br/>
 36///     - Finally, the path is reversed and returned as result.
 37///       <br/>
 38///     - Its length minus 1 represents the length of the path between start and end vertex.
 39///     </para>
 40///     <para id="complexity">
 41///     COMPLEXITY
 42///     <br/>
 43///     - While building the <see cref="IVisitStrategy"/> instance has a complexity that depends on the user-provided
 44///       <see cref="VisitStrategyBuilder"/>, it doesn't generally depends on the size of the graph to be visited, and
 45///       can be considered O(1) work.
 46///       <br/>
 47///     - Instrumenting the <see cref="IVisitStrategy"/> instance and running the handler for every visited vertex are
 48///       also constant-time operations.
 49///       <br/>
 50///     - The complexity of the BFS visit depends on the actual <see cref="IVisitStrategy"/> implementation used.
 51///       <br/>
 52///     - Rebuilding and reversing the path takes time and space proportional to the length of the path, which in the
 53///       worst case can be O(v) where v is the number of vertices of the graph.
 54///       <br/>
 55///     - Therefore, Time Complexity is O(Tbfs + v) and Space Complexity is O(Sbfs + v).
 56///       <br/>
 57///     - If <see cref="FullyIterativeHashSetBasedGraphVisit"/> or <see cref="FullyRecursiveHashSetBasedGraphVisit"/>
 58///       visits are used, with an <see cref="IGraph"/> retrieving neighbors in constant time, Time and Space
 59///       Complexity become O(v + e).
 60///     </para>
 61/// </remarks>
 62public class BfsBasedShortestPathFinder : IShortestPathFinder
 63{
 64    /// <summary>
 65    /// A building function able to instantiate the <see cref="IVisitStrategy"/> to be used to find the shortest path,
 66    /// by running a Breadth First Searches from the start vertex via
 67    /// <see cref="IVisitStrategy.BreadthFirstSearchFromVertex(IGraph, int)"/>.
 68    /// </summary>
 869    public Func<IVisitStrategy> VisitStrategyBuilder { get; }
 70
 71    /// <summary>
 72    ///     <inheritdoc cref="BfsBasedShortestPathFinder"/>
 73    /// </summary>
 74    /// <param name="visitStrategyBuilder">
 75    ///     <inheritdoc cref="VisitStrategyBuilder" path="/summary"/>
 76    /// </param>
 77    /// <remarks>
 78    ///     <inheritdoc cref="BfsBasedShortestPathFinder"/>
 79    /// </remarks>
 880    public BfsBasedShortestPathFinder(Func<IVisitStrategy> visitStrategyBuilder)
 881    {
 882        VisitStrategyBuilder = visitStrategyBuilder;
 883    }
 84
 85    /// <inheritdoc path="//*[not(self::remarks)]"/>
 86    /// <remarks>
 87    ///     <inheritdoc cref="BfsBasedShortestPathFinder"/>
 88    /// </remarks>
 89    public (int, IList<int>) Find(IGraph graph, int start, int end)
 890    {
 891        var visitor = VisitStrategyBuilder();
 92
 893        var previousVertices = new Dictionary<int, int?>();
 3294        visitor.VisitingVertex += (o, e) => previousVertices[e.Vertex] = e.PreviousVertex;
 95
 896        visitor
 897            .BreadthFirstSearchFromVertex(graph, start)
 2498            .TakeWhile(vertex => vertex != end)
 899            .Consume();
 100
 8101        if (!previousVertices.ContainsKey(end))
 2102            return (int.MaxValue, new List<int> { });
 103
 6104        var path = BuildShortestPath(end, previousVertices);
 105
 6106        return (path.Count - 1, path);
 8107    }
 108
 109    private static IList<int> BuildShortestPath(int end, Dictionary<int, int?> previousVertices)
 6110    {
 6111        var path = new List<int>();
 6112        int? maybeCurrentVertex = end;
 21113        while (maybeCurrentVertex is int currentVertex)
 15114        {
 15115            path.Add(currentVertex);
 15116            maybeCurrentVertex = previousVertices[currentVertex];
 15117        }
 6118        path.Reverse();
 6119        return path;
 6120    }
 121}