func isValidID(id int64) bool {
	return id == int64(int(id)) && id >= 0
}
isValidID returns whether id is a valid ID for a community,
multiplexCommunity or node. These are all graph.Node types
stored in []T with a mapping between their index and their ID
so IDs must be positive and fit within the int type.
package community
import (
	"fmt"
	"math/rand"
	"sort"
	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/internal/set"
)
func Q(g graph.Graph, communities [][]graph.Node, resolution float64) float64 {
	switch g := g.(type) {
	case graph.Undirected:
		return qUndirected(g, communities, resolution)
	case graph.Directed:
		return qDirected(g, communities, resolution)
	default:
		panic(fmt.Sprintf("community: invalid graph type: %T", g))
	}
}
type ReducedGraph interface {
	graph.Graph
	Communities() [][]graph.Node
	Structure() [][]graph.Node
	Expanded() ReducedGraph
}
func Modularize(g graph.Graph, resolution float64, src *rand.Rand) ReducedGraph {
	switch g := g.(type) {
	case graph.Undirected:
		return louvainUndirected(g, resolution, src)
	case graph.Directed:
		return louvainDirected(g, resolution, src)
	default:
		panic(fmt.Sprintf("community: invalid graph type: %T", g))
	}
}
type Multiplex interface {
	Nodes() []graph.Node
	Depth() int
}
func QMultiplex(g Multiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
	if weights != nil && len(weights) != g.Depth() {
		panic("community: weights vector length mismatch")
	}
	if resolutions != nil && len(resolutions) != 1 && len(resolutions) != g.Depth() {
		panic("community: resolutions vector length mismatch")
	}
	switch g := g.(type) {
	case UndirectedMultiplex:
		return qUndirectedMultiplex(g, communities, weights, resolutions)
	case DirectedMultiplex:
		return qDirectedMultiplex(g, communities, weights, resolutions)
	default:
		panic(fmt.Sprintf("community: invalid graph type: %T", g))
	}
}
type ReducedMultiplex interface {
	Multiplex
	Communities() [][]graph.Node
	Structure() [][]graph.Node
	Expanded() ReducedMultiplex
}
func ModularizeMultiplex(g Multiplex, weights, resolutions []float64, all bool, src *rand.Rand) ReducedMultiplex {
	if weights != nil && len(weights) != g.Depth() {
		panic("community: weights vector length mismatch")
	}
	if resolutions != nil && len(resolutions) != 1 && len(resolutions) != g.Depth() {
		panic("community: resolutions vector length mismatch")
	}
	switch g := g.(type) {
	case UndirectedMultiplex:
		return louvainUndirectedMultiplex(g, weights, resolutions, all, src)
	case DirectedMultiplex:
		return louvainDirectedMultiplex(g, weights, resolutions, all, src)
	default:
		panic(fmt.Sprintf("community: invalid graph type: %T", g))
	}
}
type undirectedEdges struct {
	edges   [][]int
	weights map[[2]int]float64
}
type directedEdges struct {
	edgesFrom [][]int
	edgesTo   [][]int
	weights   map[[2]int]float64
}