/// <summary>Prim algorithm.</summary>
        /// <param name="graph">Undirected weighted graph</param>
        /// <param name="source">Starting vertex</param>
        /// <returns>Minimal spanning tree</returns>
        public static IUndirectedGraph <TVertexId, TVertexProperty, TEdgeProperty> Prim <TVertexId, TVertexProperty, TEdgeProperty>(
            IUndirectedGraph <TVertexId, TVertexProperty, TEdgeProperty> graph, Vertex <TVertexId> source)
            where TEdgeProperty : IWeighted
        {
            var mst = new UndirectedSimpleGraph <TVertexId, TVertexProperty, TEdgeProperty>(
                graph.Vertices.Select(v => v.Id).ToArray());
            var visited = new HashSet <Vertex <TVertexId> >();
            var heap    = new Heap <(Edge <TVertexId>, Vertex <TVertexId>)>(
                (pair1, pair2) => graph.Properties[pair1.Item1].Weight.CompareTo(
                    graph.Properties[pair2.Item1].Weight)
                );

            visited.Add(source);

            foreach (Edge <TVertexId> adjacentEdge in graph.GetAdjacentEdges(source))
            {
                Vertex <TVertexId> neighbour = adjacentEdge.GetNeighbour(source);

                if (neighbour != source)
                {
                    heap.Push((adjacentEdge, neighbour));
                }
            }

            while (heap.Count > 0)
            {
                (Edge <TVertexId> edge, Vertex <TVertexId> vertex) = heap.Pop();

                if (!visited.Contains(vertex))
                {
                    visited.Add(vertex);
                    mst.AddEdge(edge, graph.Properties[edge]);

                    foreach (Edge <TVertexId> adjacentEdge in graph.GetAdjacentEdges(vertex))
                    {
                        Vertex <TVertexId> neighbour = adjacentEdge.GetNeighbour(vertex);

                        if (!visited.Contains(neighbour))
                        {
                            heap.Push((adjacentEdge, neighbour));
                        }
                    }
                }
            }

            return(mst);
        }