package dag
import "fmt"
type Node interface {
NodeID() string
}
type Edge struct{ From, To Node }
type Graph struct {
nodeByID map[string]Node
nodes nodeSet
outEdges map[Node]nodeSet
inEdges map[Node]nodeSet
}
type nodeSet map[Node]struct{}
func (ns nodeSet) Add(n Node) { ns[n] = struct{}{} }
func (ns nodeSet) Remove(n Node) { delete(ns, n) }
func (ns nodeSet) Has(n Node) bool {
_, ok := ns[n]
return ok
}
func (ns nodeSet) Clone() nodeSet {
newSet := make(nodeSet, len(ns))
for node := range ns {
newSet[node] = struct{}{}
}
return newSet
}
func (g *Graph) init() {
if g.nodeByID == nil {
g.nodeByID = make(map[string]Node)
}
if g.nodes == nil {
g.nodes = make(nodeSet)
}
if g.outEdges == nil {
g.outEdges = make(map[Node]nodeSet)
}
if g.inEdges == nil {
g.inEdges = make(map[Node]nodeSet)
}
}
func (g *Graph) Add(n Node) {
g.init()
if other, ok := g.nodeByID[n.NodeID()]; ok && other != n {
panic(fmt.Sprintf("Graph.Add: Node ID %q is already in use by another Node", n.NodeID()))
}
g.nodes.Add(n)
g.nodeByID[n.NodeID()] = n
}
func (g *Graph) GetByID(id string) Node { return g.nodeByID[id] }
func (g *Graph) Remove(n Node) {
if !g.nodes.Has(n) {
return
}
delete(g.nodeByID, n.NodeID())
g.nodes.Remove(n)
delete(g.outEdges, n)
for _, ns := range g.inEdges {
ns.Remove(n)
}
}
func (g *Graph) AddEdge(e Edge) {
g.init()
if !g.nodes.Has(e.From) || !g.nodes.Has(e.To) {
panic("AddEdge called with a node that doesn't exist in graph")
}
inSet, ok := g.inEdges[e.To]
if !ok {
inSet = make(nodeSet)
g.inEdges[e.To] = inSet
}
inSet.Add(e.From)
outSet, ok := g.outEdges[e.From]
if !ok {
outSet = make(nodeSet)
g.outEdges[e.From] = outSet
}
outSet.Add(e.To)
}
func (g *Graph) RemoveEdge(e Edge) {
inSet, ok := g.inEdges[e.To]
if ok {
delete(inSet, e.From)
}
outSet, ok := g.outEdges[e.From]
if ok {
delete(outSet, e.To)
}
}
func (g *Graph) Nodes() []Node {
nodes := make([]Node, 0, len(g.nodes))
for n := range g.nodes {
nodes = append(nodes, n)
}
return nodes
}
func (g *Graph) Edges() []Edge {
var edges []Edge
for from, tos := range g.outEdges {
for to := range tos {
edges = append(edges, Edge{From: from, To: to})
}
}
return edges
}
func (g *Graph) Dependants(n Node) []Node {
sourceDependants := g.inEdges[n]
dependants := make([]Node, 0, len(sourceDependants))
for dep := range sourceDependants {
dependants = append(dependants, dep)
}
return dependants
}
func (g *Graph) Dependencies(n Node) []Node {
sourceDependencies := g.outEdges[n]
dependencies := make([]Node, 0, len(sourceDependencies))
for dep := range sourceDependencies {
dependencies = append(dependencies, dep)
}
return dependencies
}
func (g *Graph) Roots() []Node {
var res []Node
for n := range g.nodes {
if len(g.inEdges[n]) == 0 {
res = append(res, n)
}
}
return res
}
func (g *Graph) Leaves() []Node {
var res []Node
for n := range g.nodes {
if len(g.outEdges[n]) == 0 {
res = append(res, n)
}
}
return res
}
func (g *Graph) Clone() *Graph {
newGraph := &Graph{
nodes: g.nodes.Clone(),
nodeByID: make(map[string]Node, len(g.nodeByID)),
outEdges: make(map[Node]nodeSet, len(g.outEdges)),
inEdges: make(map[Node]nodeSet, len(g.outEdges)),
}
for key, value := range g.nodeByID {
newGraph.nodeByID[key] = value
}
for node, set := range g.outEdges {
newGraph.outEdges[node] = set.Clone()
}
for node, set := range g.inEdges {
newGraph.inEdges[node] = set.Clone()
}
return newGraph
}