"""
	kruskal_mst(g, distmx=weights(g); minimize=true)
	kruskal_mst(g, weight_vector; minimize=true)

Return a vector of edges representing the minimum (by default) spanning tree of a connected, 
undirected graph `g` with optional distance matrix `distmx` using [Kruskal's algorithm](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm).

Alternative to the distance matrix `distmx`, one can pass a `weight_vector` with weights ordered as `edges(g)`.

### Optional Arguments
- `minimize=true`: if set to `false`, calculate the maximum spanning tree.
"""
function kruskal_mst end
# see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax
@traitfn function kruskal_mst(
    g::AG::(!IsDirected), distmx::AbstractMatrix{T}=weights(g); minimize=true
) where {T<:Number,U,AG<:AbstractGraph{U}}
    weight_vector = Vector{T}()
    sizehint!(weight_vector, ne(g))
    for e in edges(g)
        push!(weight_vector, distmx[src(e), dst(e)])
    end
    return kruskal_mst(g, weight_vector; minimize=minimize)
end

@traitfn function kruskal_mst(
    g::AG::(!IsDirected), weight_vector::AbstractVector{T}; minimize=true
) where {T<:Number,U,AG<:AbstractGraph{U}}
    connected_vs = IntDisjointSet(nv(g))

    mst = Vector{edgetype(g)}()
    nv(g) <= 1 && return mst
    sizehint!(mst, nv(g) - 1)

    edge_list = collect(edges(g))
    for e in edge_list[sortperm(weight_vector; rev=(!minimize))]
        if !in_same_set(connected_vs, src(e), dst(e))
            union!(connected_vs, src(e), dst(e))
            push!(mst, e)
            (length(mst) >= nv(g) - 1) && break
        end
    end

    return mst
end
