func (l *directedLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst int, src commIdx) {
	id := n.ID()
	a_aa := l.weight(n, n)
	k_a := l.edgeWeightsOf[id]
	m := l.m
	gamma := l.resolution
	connected := make(set.Ints)
	for _, vid := range l.g.edgesFrom[id] {
		connected.Add(l.memberships[vid])
	}
	for _, vid := range l.g.edgesTo[id] {
		connected.Add(l.memberships[vid])
	}
	connected.Add(l.memberships[id])
	candidates := make([]int, 0, len(connected))
	for i := range connected {
		candidates = append(candidates, i)
	}
	sort.Ints(candidates)
	var dQremove float64
	dQadd, dst, src := math.Inf(-1), -1, commIdx{-1, -1}
	for _, i := range candidates {
		c := l.communities[i]
		var k_aC, sigma_totC directedWeights  
		var removal bool
		for j, u := range c {
			uid := u.ID()
			if uid == id {
				if src.community != -1 {
					panic("community: multiple sources")
				}
				src = commIdx{i, j}
				removal = true
			}
			k_aC.in += l.weight(u, n)
			k_aC.out += l.weight(n, u)
			w := l.edgeWeightsOf[uid]
			sigma_totC.in += w.in
			sigma_totC.out += w.out
		}
		switch {
		case removal:
			dQremove = (k_aC.in   - a_aa) + (k_aC.out   - a_aa) -
				gamma*(k_a.in*(sigma_totC.out   -k_a.out)+k_a.out*(sigma_totC.in   -k_a.in))/m
		default:
			dQ := k_aC.in   + k_aC.out   -
				gamma*(k_a.in*sigma_totC.out   +k_a.out*sigma_totC.in  )/m
			if dQ > dQadd {
				dQadd = dQ
				dst = i
			}
		}
	}
	return (dQadd - dQremove) / m, dst, src
}
deltaQ returns the highest gain in modularity attainable by moving
n from its current community to another connected community and
the index of the chosen destination. The index into the directedLocalMover's
communities field is returned in src if n is in communities.
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
}
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
		}
	}
}
func (l *directedLocalMover) shuffle(rnd func(n int) int) {
	l.moved = false
	for i := range l.nodes[:len(l.nodes)-1] {
		j := i + rnd(len(l.nodes)-i)
		l.nodes[i], l.nodes[j] = l.nodes[j], l.nodes[i]
	}
}
func (l *directedLocalMover) move(dst int, src commIdx) {
	l.moved = true
	l.changed = true
	srcComm := l.communities[src.community]
	n := srcComm[src.node]
	l.memberships[n.ID()] = dst
	l.communities[dst] = append(l.communities[dst], n)
	srcComm[src.node], srcComm[len(srcComm)-1] = srcComm[len(srcComm)-1], nil
	l.communities[src.community] = srcComm[:len(srcComm)-1]
}