/// <summary>
        /// Dijkstras algorithm that traverses the graph to find shortest path
        /// </summary>
        /// <param name="graph"> <see cref="Graph"/> instance. </param>
        /// <param name="startVertex"> The start <see cref="Vertex"/> of path to calculate minimum distance for. </param>
        /// <returns> The shortest (minimum cost) path from starting point to all other. </returns>
        public static PathwayCollection <T> Dijkstra <T>(this DirectedGraph <T> graph, Vertex <T> startVertex) where T : IEquatable <T>
        {
            var distances = graph.Vertices.ToDictionary(key => key, value => long.MaxValue);

            distances[startVertex] = 0;
            var distancesMinimumHeap = new BinaryHeap <VertexDistancePair <T> >(
                BinaryHeapType.MinimumHeap,
                graph.Vertices.Count,
                new VertexDistancePairComparer <T>());

            distancesMinimumHeap.Add(new VertexDistancePair <T>(startVertex, 0));

            var pathVertices = new Dictionary <Vertex <T>, Vertex <T> >();

            while (distancesMinimumHeap.Count > 0)
            {
                var shortestDistanceVertexPair = distancesMinimumHeap.Remove();
                var shortestDistanceVertex     = shortestDistanceVertexPair.Vertex;

                foreach (var outboundEdge in shortestDistanceVertex.OutboundEdges)
                {
                    var  outboundEndVertex   = outboundEdge.EndVertex;
                    long alternativeDistance = distances[shortestDistanceVertex] + outboundEdge.Weight;

                    if (alternativeDistance < distances[outboundEndVertex])
                    {
                        distances[outboundEndVertex]    = alternativeDistance;
                        pathVertices[outboundEndVertex] = shortestDistanceVertex;
                        distancesMinimumHeap.Add(new VertexDistancePair <T>(outboundEndVertex, alternativeDistance));
                    }
                }
            }

            return(new PathwayCollection <T>(pathVertices, distances, startVertex));
        }
        /// <summary>
        /// Prim's algorithm to find the minimum span tree on graph
        /// </summary>
        /// <param name="graph"> <see cref="Graph"/> instance. </param>
        /// <returns> Minimum span tree containing edges and minimum distance. </returns>
        public static MinimumSpanTree <T> PrimsMinimumSpanningTree <T>(this DirectedGraph <T> graph) where T : IEquatable <T>
        {
            if (graph.Vertices.Count == 0 || graph.Edges.Count == 0)
            {
                return(new MinimumSpanTree <T>(Enumerable.Empty <Edge <T> >().ToList(), 0));
            }

            var currentVertex   = graph.Vertices.First();
            int minimumDistance = 0;
            var minimumSpanTree = new List <Edge <T> >();
            var edgesToVisit    = new BinaryHeap <Edge <T> >(BinaryHeapType.MinimumHeap, currentVertex.OutboundEdges.Count, new EdgeComparer <T>());
            var verticesVisited = new HashSet <Vertex <T> >();

            while (minimumSpanTree.Count < graph.Vertices.Count - 1)
            {
                foreach (var edge in currentVertex.OutboundEdges)
                {
                    edgesToVisit.Add(edge);
                }

                verticesVisited.Add(currentVertex);
                Edge <T> minimumEdge = null;

                while (edgesToVisit.Count > 0)
                {
                    minimumEdge = edgesToVisit.Remove();
                    if (verticesVisited.Contains(minimumEdge.StartVertex) != verticesVisited.Contains(minimumEdge.EndVertex))
                    {
                        break;
                    }
                }

                if (minimumEdge == null)
                {
                    throw new MultipleMinimumSpanningTreesException();
                }

                minimumSpanTree.Add(minimumEdge);
                minimumDistance += minimumEdge.Weight;
                currentVertex    = verticesVisited.Contains(minimumEdge.EndVertex)
                                      ? minimumEdge.StartVertex
                                      : minimumEdge.EndVertex;
            }

            return(new MinimumSpanTree <T>(minimumSpanTree, minimumDistance));
        }
        /// <summary>
        /// Kruskal's algorithm to find the minimum span tree on graph
        /// </summary>
        /// <param name="graph"> <see cref="Graph"/> instance. </param>
        /// <returns> Minimum span tree containing edges and minimum distance. </returns>
        public static MinimumSpanTree <T> KruskalsMinimumSpanningTree <T>(this DirectedGraph <T> graph) where T : IEquatable <T>
        {
            if (graph.Vertices.Count == 0 || graph.Edges.Count == 0)
            {
                return(new MinimumSpanTree <T>(Enumerable.Empty <Edge <T> >().ToList(), 0));
            }

            var minimumSpanTree = new List <Edge <T> >();
            var minimumDistance = 0;
            var unionFind       = new DisjointSet <Vertex <T> >(graph.Vertices);
            var edgesToVisit    = new BinaryHeap <Edge <T> >(BinaryHeapType.MinimumHeap, graph.Edges.Count, new EdgeComparer <T>());

            foreach (var edge in graph.Edges)
            {
                edgesToVisit.Add(edge);
            }

            while (minimumSpanTree.Count < graph.Vertices.Count - 1)
            {
                Edge <T> minimumEdge = null;

                while (edgesToVisit.Count > 0)
                {
                    minimumEdge = edgesToVisit.Remove();
                    if (unionFind.Find(minimumEdge.StartVertex) != unionFind.Find(minimumEdge.EndVertex))
                    {
                        break;
                    }
                }

                if (minimumEdge == null)
                {
                    throw new MultipleMinimumSpanningTreesException();
                }

                minimumSpanTree.Add(minimumEdge);
                minimumDistance += minimumEdge.Weight;
                unionFind.Union(minimumEdge.StartVertex, minimumEdge.EndVertex);
            }

            return(new MinimumSpanTree <T>(minimumSpanTree, minimumDistance));
        }