func (l *directedLocalMover) localMovingHeuristic(rnd func(int) int) (done bool) {
	for {
		l.shuffle(rnd)
		for _, n := range l.nodes {
			dQ, dst, src := l.deltaQ(n)
			if dQ <= 0 {
				continue
			}
			l.move(dst, src)
		}
		if !l.moved {
			return !l.changed
		}
	}
}
localMovingHeuristic performs the Louvain local moving heuristic until
no further moves can be made. It returns a boolean indicating that the
directedLocalMover has not made any improvement to the community structure and
so the Louvain algorithm is done.
package community
import (
	"math"
	"math/rand"
	"sort"
	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/internal/ordered"
	"gonum.org/v1/gonum/graph/internal/set"
)
func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64) float64 {
	nodes := g.Nodes()
	weight := positiveWeightFuncFor(g)
	var m float64
	k := make(map[int64]directedWeights, len(nodes))
	for _, n := range nodes {
		var wOut float64
		u := n
		for _, v := range g.From(u) {
			wOut += weight(u, v)
		}
		var wIn float64
		v := n
		for _, u := range g.To(v) {
			wIn += weight(u, v)
		}
		w := weight(n, n)
		m += w + wOut  
		k[n.ID()] = directedWeights{out: w + wOut, in: w + wIn}
	}
	if communities == nil {
		var q float64
		for _, u := range nodes {
			kU := k[u.ID()]
			q += weight(u, u) - resolution*kU.out*kU.in/m
		}
		return q / m
	}
	var q float64
	for _, c := range communities {
		for _, u := range c {
			kU := k[u.ID()]
			for _, v := range c {
				kV := k[v.ID()]
				q += weight(u, v) - resolution*kU.out*kV.in/m
			}
		}
	}
	return q / m
}
func louvainDirected(g graph.Directed, resolution float64, src *rand.Rand) ReducedGraph {
	c := reduceDirected(g, nil)
	rnd := rand.Intn
	if src != nil {
		rnd = src.Intn
	}
	for {
		l := newDirectedLocalMover(c, c.communities, resolution)
		if l == nil {
			return c
		}
		if done := l.localMovingHeuristic(rnd); done {
			return c
		}
		c = reduceDirected(c, l.communities)
	}
}
type ReducedDirected struct {
	nodes []community
	directedEdges
	communities [][]graph.Node
	parent *ReducedDirected
}
var (
	reducedDirected = (*ReducedDirected)(nil)
	_ graph.WeightedDirected = reducedDirected
	_ ReducedGraph           = reducedDirected
)
func (g *ReducedDirected) Communities() [][]graph.Node {
	communities := make([][]graph.Node, len(g.communities))
	if g.parent == nil {
		for i, members := range g.communities {
			comm := make([]graph.Node, len(members))
			for j, n := range members {
				nodes := g.nodes[n.ID()].nodes
				if len(nodes) != 1 {
					panic("community: unexpected number of nodes in base graph community")
				}
				comm[j] = nodes[0]
			}
			communities[i] = comm
		}
		return communities
	}
	sub := g.parent.Communities()
	for i, members := range g.communities {
		var comm []graph.Node
		for _, n := range members {
			comm = append(comm, sub[n.ID()]...)
		}
		communities[i] = comm
	}
	return communities
}
func (g *ReducedDirected) Structure() [][]graph.Node {
	return g.communities
}
func (g *ReducedDirected) Expanded() ReducedGraph {
	return g.parent
}
func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirected {
	if communities == nil {
		if r, ok := g.(*ReducedDirected); ok {
			return r
		}
		nodes := g.Nodes()
		sort.Sort(ordered.ByID(nodes))
		communities = make([][]graph.Node, len(nodes))
		for i := range nodes {
			communities[i] = []graph.Node{node(i)}
		}
		weight := positiveWeightFuncFor(g)
		r := ReducedDirected{
			nodes: make([]community, len(nodes)),
			directedEdges: directedEdges{
				edgesFrom: make([][]int, len(nodes)),
				edgesTo:   make([][]int, len(nodes)),
				weights:   make(map[[2]int]float64),
			},
			communities: communities,
		}
		communityOf := make(map[int64]int, len(nodes))
		for i, n := range nodes {
			r.nodes[i] = community{id: i, nodes: []graph.Node{n}}
			communityOf[n.ID()] = i
		}
		for _, n := range nodes {
			id := communityOf[n.ID()]
			var out []int
			u := n
			for _, v := range g.From(u) {
				vid := communityOf[v.ID()]
				if vid != id {
					out = append(out, vid)
				}
				r.weights[[2]int{id, vid}] = weight(u, v)
			}
			r.edgesFrom[id] = out
			var in []int
			v := n
			for _, u := range g.To(v) {
				uid := communityOf[u.ID()]
				if uid != id {
					in = append(in, uid)
				}
				r.weights[[2]int{uid, id}] = weight(u, v)
			}
			r.edgesTo[id] = in
		}
		return &r
	}
	var commNodes int
	for i := 0; i < len(communities); {
		comm := communities[i]
		if len(comm) == 0 {
			communities[i] = communities[len(communities)-1]
			communities[len(communities)-1] = nil
			communities = communities[:len(communities)-1]
		} else {
			commNodes += len(comm)
			i++
		}
	}
	r := ReducedDirected{
		nodes: make([]community, len(communities)),
		directedEdges: directedEdges{
			edgesFrom: make([][]int, len(communities)),
			edgesTo:   make([][]int, len(communities)),
			weights:   make(map[[2]int]float64),
		},
	}
	r.communities = make([][]graph.Node, len(communities))
	for i := range r.communities {
		r.communities[i] = []graph.Node{node(i)}
	}
	if g, ok := g.(*ReducedDirected); ok {
		g.communities = communities
		r.parent = g
	}
	weight := positiveWeightFuncFor(g)
	communityOf := make(map[int64]int, commNodes)
	for i, comm := range communities {
		r.nodes[i] = community{id: i, nodes: comm}
		for _, n := range comm {
			communityOf[n.ID()] = i
		}
	}
	for id, comm := range communities {
		var out, in []int
		for _, n := range comm {
			u := n
			for _, v := range comm {
				r.nodes[id].weight += weight(u, v)
			}
			for _, v := range g.From(u) {
				vid := communityOf[v.ID()]
				found := false
				for _, e := range out {
					if e == vid {
						found = true
						break
					}
				}
				if !found && vid != id {
					out = append(out, vid)
				}
				r.weights[[2]int{id, vid}] += weight(u, v) / 2
			}
			v := n
			for _, u := range g.To(v) {
				uid := communityOf[u.ID()]
				found := false
				for _, e := range in {
					if e == uid {
						found = true
						break
					}
				}
				if !found && uid != id {
					in = append(in, uid)
				}
				r.weights[[2]int{uid, id}] += weight(u, v) / 2
			}
		}
		r.edgesFrom[id] = out
		r.edgesTo[id] = in
	}
	return &r
}
func (g *ReducedDirected) Has(n graph.Node) bool {
	id := n.ID()
	return 0 <= id && id < int64(len(g.nodes))
}
func (g *ReducedDirected) Nodes() []graph.Node {
	nodes := make([]graph.Node, len(g.nodes))
	for i := range g.nodes {
		nodes[i] = node(i)
	}
	return nodes
}
func (g *ReducedDirected) From(u graph.Node) []graph.Node {
	out := g.edgesFrom[u.ID()]
	nodes := make([]graph.Node, len(out))
	for i, vid := range out {
		nodes[i] = g.nodes[vid]
	}
	return nodes
}
func (g *ReducedDirected) To(v graph.Node) []graph.Node {
	in := g.edgesTo[v.ID()]
	nodes := make([]graph.Node, len(in))
	for i, uid := range in {
		nodes[i] = g.nodes[uid]
	}
	return nodes
}
func (g *ReducedDirected) HasEdgeBetween(x, y graph.Node) bool {
	xid := x.ID()
	yid := y.ID()
	if xid == yid || !isValidID(xid) || !isValidID(yid) {
		return false
	}
	_, ok := g.weights[[2]int{int(xid), int(yid)}]
	if ok {
		return true
	}
	_, ok = g.weights[[2]int{int(yid), int(xid)}]
	return ok
}
func (g *ReducedDirected) HasEdgeFromTo(u, v graph.Node) bool {
	uid := u.ID()
	vid := v.ID()
	if uid == vid || !isValidID(uid) || !isValidID(vid) {
		return false
	}
	_, ok := g.weights[[2]int{int(uid), int(vid)}]
	return ok
}
func (g *ReducedDirected) Edge(u, v graph.Node) graph.Edge {
	return g.WeightedEdge(u, v)
}
func (g *ReducedDirected) WeightedEdge(u, v graph.Node) graph.WeightedEdge {
	uid := u.ID()
	vid := v.ID()
	if uid == vid || !isValidID(uid) || !isValidID(vid) {
		return nil
	}
	w, ok := g.weights[[2]int{int(uid), int(vid)}]
	if !ok {
		return nil
	}
	return edge{from: g.nodes[uid], to: g.nodes[vid], weight: w}
}
func (g *ReducedDirected) Weight(x, y graph.Node) (w float64, ok bool) {
	xid := x.ID()
	yid := y.ID()
	if !isValidID(xid) || !isValidID(yid) {
		return 0, false
	}
	if xid == yid {
		return g.nodes[xid].weight, true
	}
	w, ok = g.weights[[2]int{int(xid), int(yid)}]
	return w, ok
}
type directedLocalMover struct {
	g *ReducedDirected
	nodes []graph.Node
	edgeWeightsOf []directedWeights
	m float64
	weight func(x, y graph.Node) float64
	communities [][]graph.Node
	memberships []int
	resolution float64
	moved bool
	changed bool
}
type directedWeights struct {
	out, in float64
}
func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resolution float64) *directedLocalMover {
	nodes := g.Nodes()
	l := directedLocalMover{
		g:             g,
		nodes:         nodes,
		edgeWeightsOf: make([]directedWeights, len(nodes)),
		communities:   communities,
		memberships:   make([]int, len(nodes)),
		resolution:    resolution,
		weight:        positiveWeightFuncFor(g),
	}
	for _, n := range l.nodes {
		u := n
		var wOut float64
		for _, v := range g.From(u) {
			wOut += l.weight(u, v)
		}
		v := n
		var wIn float64
		for _, u := range g.To(v) {
			wIn += l.weight(u, v)
		}
		w := l.weight(n, n)
		l.edgeWeightsOf[n.ID()] = directedWeights{out: w + wOut, in: w + wIn}
		l.m += w + wOut
	}
	for i, c := range communities {
		for _, n := range c {
			l.memberships[n.ID()] = i
		}
	}
	return &l
}