graph: make Edge into an interface
This commit is contained in:
parent
a5b3b7acd0
commit
16f2b1bbde
@ -37,12 +37,30 @@ func NewValue(V interface{}) Value {
|
||||
|
||||
// Edge is a directional edge connecting two values in a Graph, the Tail and the
|
||||
// Head.
|
||||
type Edge struct {
|
||||
Tail, Head Value
|
||||
type Edge interface {
|
||||
Tail() Value // The Value the Edge is coming from
|
||||
Head() Value // The Value the Edge is going to
|
||||
}
|
||||
|
||||
func (e Edge) id() string {
|
||||
return fmt.Sprintf("%q->%q", e.Tail, e.Head)
|
||||
func edgeID(e Edge) string {
|
||||
return fmt.Sprintf("%q->%q", e.Tail().ID, e.Head().ID)
|
||||
}
|
||||
|
||||
type edge struct {
|
||||
tail, head Value
|
||||
}
|
||||
|
||||
// NewEdge constructs and returns an Edge running from tail to head.
|
||||
func NewEdge(tail, head Value) Edge {
|
||||
return edge{tail, head}
|
||||
}
|
||||
|
||||
func (e edge) Tail() Value {
|
||||
return e.tail
|
||||
}
|
||||
|
||||
func (e edge) Head() Value {
|
||||
return e.head
|
||||
}
|
||||
|
||||
// an edgeIndex maps valueIDs to a set of edgeIDs. Graph keeps two edgeIndex's,
|
||||
@ -125,7 +143,7 @@ func (g Graph) String() string {
|
||||
// Add returns a new Graph instance with the given Edge added to it. If the
|
||||
// original Graph already had that Edge this returns the original Graph.
|
||||
func (g Graph) Add(e Edge) Graph {
|
||||
id := e.id()
|
||||
id := edgeID(e)
|
||||
if _, ok := g.m[id]; ok {
|
||||
return g
|
||||
}
|
||||
@ -137,8 +155,8 @@ func (g Graph) Add(e Edge) Graph {
|
||||
|
||||
func (g Graph) addDirty(edgeID string, e Edge) {
|
||||
g.m[edgeID] = e
|
||||
g.vIns.add(e.Head.ID, edgeID)
|
||||
g.vOuts.add(e.Tail.ID, edgeID)
|
||||
g.vIns.add(e.Head().ID, edgeID)
|
||||
g.vOuts.add(e.Tail().ID, edgeID)
|
||||
}
|
||||
|
||||
func (g Graph) estSize() int {
|
||||
@ -153,15 +171,15 @@ func (g Graph) estSize() int {
|
||||
// Del returns a new Graph instance without the given Edge in it. If the
|
||||
// original Graph didn't have that Edge this returns the original Graph.
|
||||
func (g Graph) Del(e Edge) Graph {
|
||||
id := e.id()
|
||||
id := edgeID(e)
|
||||
if _, ok := g.m[id]; !ok {
|
||||
return g
|
||||
}
|
||||
|
||||
g2 := g.cp()
|
||||
delete(g2.m, id)
|
||||
g2.vIns.del(e.Head.ID, id)
|
||||
g2.vOuts.del(e.Tail.ID, id)
|
||||
g2.vIns.del(e.Head().ID, id)
|
||||
g2.vOuts.del(e.Tail().ID, id)
|
||||
return g2
|
||||
}
|
||||
|
||||
@ -170,8 +188,8 @@ func (g Graph) Del(e Edge) Graph {
|
||||
func (g Graph) Disjoin() []Graph {
|
||||
valM := make(map[string]*Graph, len(g.vOuts))
|
||||
graphForEdge := func(edge Edge) *Graph {
|
||||
headGraph := valM[edge.Head.ID]
|
||||
tailGraph := valM[edge.Tail.ID]
|
||||
headGraph := valM[edge.Head().ID]
|
||||
tailGraph := valM[edge.Tail().ID]
|
||||
if headGraph == nil && tailGraph == nil {
|
||||
newGraph := Graph{}.cp() // cp also initializes
|
||||
return &newGraph
|
||||
@ -203,8 +221,8 @@ func (g Graph) Disjoin() []Graph {
|
||||
for edgeID, edge := range g.m {
|
||||
graph := graphForEdge(edge)
|
||||
(*graph).addDirty(edgeID, edge)
|
||||
valM[edge.Head.ID] = graph
|
||||
valM[edge.Tail.ID] = graph
|
||||
valM[edge.Head().ID] = graph
|
||||
valM[edge.Tail().ID] = graph
|
||||
}
|
||||
|
||||
found := map[*Graph]bool{}
|
||||
@ -276,14 +294,16 @@ func (g Graph) Nodes() map[string]Node {
|
||||
// if head and tail are modified at the same time it messes up the case
|
||||
// where they are the same node
|
||||
{
|
||||
head := nodesM[edge.Head.ID]
|
||||
head.Value = edge.Head
|
||||
headV := edge.Head()
|
||||
head := nodesM[headV.ID]
|
||||
head.Value = headV
|
||||
head.Ins = append(head.Ins, edge)
|
||||
nodesM[head.ID] = head
|
||||
}
|
||||
{
|
||||
tail := nodesM[edge.Tail.ID]
|
||||
tail.Value = edge.Tail
|
||||
tailV := edge.Tail()
|
||||
tail := nodesM[tailV.ID]
|
||||
tail.Value = tailV
|
||||
tail.Outs = append(tail.Outs, edge)
|
||||
nodesM[tail.ID] = tail
|
||||
}
|
||||
@ -359,10 +379,11 @@ func (g Graph) VisitBreadth(start Value, callback func(n Node) bool) {
|
||||
}
|
||||
visited[val.ID] = true
|
||||
for _, edge := range node.Outs {
|
||||
if visited[edge.Head.ID] {
|
||||
headV := edge.Head()
|
||||
if visited[headV.ID] {
|
||||
continue
|
||||
}
|
||||
toVisit = append(toVisit, edge.Head)
|
||||
toVisit = append(toVisit, headV)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,10 +421,10 @@ func (g Graph) VisitDepth(start Value, callback func(n Node) bool) {
|
||||
}
|
||||
visited[val.ID] = true
|
||||
for _, edge := range node.Outs {
|
||||
if visited[edge.Head.ID] {
|
||||
if visited[edge.Head().ID] {
|
||||
continue
|
||||
}
|
||||
toVisit = append(toVisit, edge.Head)
|
||||
toVisit = append(toVisit, edge.Head())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ func TestGraph(t *T) {
|
||||
Next: func(ss mchk.State) mchk.Action {
|
||||
s := ss.(state)
|
||||
var p params
|
||||
if i := mrand.Intn(10); i == 0 {
|
||||
if i := mrand.Intn(10); i == 0 && len(s.m) > 0 {
|
||||
// add edge which is already there
|
||||
for _, e := range s.m {
|
||||
p.add = e
|
||||
@ -44,24 +44,24 @@ func TestGraph(t *T) {
|
||||
}
|
||||
} else if i == 1 {
|
||||
// delete edge which isn't there
|
||||
p.del = Edge{Tail: strV("z"), Head: strV("z")}
|
||||
p.del = NewEdge(strV("z"), strV("z"))
|
||||
} else if i <= 5 {
|
||||
// add probably new edge
|
||||
p.add = Edge{Tail: strV(mrand.Hex(1)), Head: strV(mrand.Hex(1))}
|
||||
p.add = NewEdge(strV(mrand.Hex(1)), strV(mrand.Hex(1)))
|
||||
} else {
|
||||
// probably del edge
|
||||
p.del = Edge{Tail: strV(mrand.Hex(1)), Head: strV(mrand.Hex(1))}
|
||||
p.del = NewEdge(strV(mrand.Hex(1)), strV(mrand.Hex(1)))
|
||||
}
|
||||
return mchk.Action{Params: p}
|
||||
},
|
||||
Apply: func(ss mchk.State, a mchk.Action) (mchk.State, error) {
|
||||
s, p := ss.(state), a.Params.(params)
|
||||
if p.add != (Edge{}) {
|
||||
if p.add != nil {
|
||||
s.Graph = s.Graph.Add(p.add)
|
||||
s.m[p.add.id()] = p.add
|
||||
s.m[edgeID(p.add)] = p.add
|
||||
} else {
|
||||
s.Graph = s.Graph.Del(p.del)
|
||||
delete(s.m, p.del.id())
|
||||
delete(s.m, edgeID(p.del))
|
||||
}
|
||||
|
||||
{ // test Nodes and Edges methods
|
||||
@ -72,14 +72,14 @@ func TestGraph(t *T) {
|
||||
ins, outs := map[string]int{}, map[string]int{}
|
||||
for _, e := range s.m {
|
||||
aa = append(aa, massert.Has(edges, e))
|
||||
aa = append(aa, massert.HasKey(nodes, e.Head.ID))
|
||||
aa = append(aa, massert.Has(nodes[e.Head.ID].Ins, e))
|
||||
aa = append(aa, massert.HasKey(nodes, e.Tail.ID))
|
||||
aa = append(aa, massert.Has(nodes[e.Tail.ID].Outs, e))
|
||||
vals[e.Head.ID] = true
|
||||
vals[e.Tail.ID] = true
|
||||
ins[e.Head.ID]++
|
||||
outs[e.Tail.ID]++
|
||||
aa = append(aa, massert.HasKey(nodes, e.Head().ID))
|
||||
aa = append(aa, massert.Has(nodes[e.Head().ID].Ins, e))
|
||||
aa = append(aa, massert.HasKey(nodes, e.Tail().ID))
|
||||
aa = append(aa, massert.Has(nodes[e.Tail().ID].Outs, e))
|
||||
vals[e.Head().ID] = true
|
||||
vals[e.Tail().ID] = true
|
||||
ins[e.Head().ID]++
|
||||
outs[e.Tail().ID]++
|
||||
}
|
||||
aa = append(aa, massert.Len(edges, len(s.m)))
|
||||
aa = append(aa, massert.Len(nodes, len(vals)))
|
||||
@ -145,7 +145,7 @@ func TestSubGraphAndEqual(t *T) {
|
||||
Next: func(ss mchk.State) mchk.Action {
|
||||
i := mrand.Intn(10)
|
||||
p := params{
|
||||
e: Edge{Tail: strV(mrand.Hex(4)), Head: strV(mrand.Hex(4))},
|
||||
e: NewEdge(strV(mrand.Hex(4)), strV(mrand.Hex(4))),
|
||||
add1: i != 0,
|
||||
add2: i != 1,
|
||||
}
|
||||
@ -206,20 +206,20 @@ func TestDisjoinUnion(t *T) {
|
||||
prefix := mrand.Hex(1)
|
||||
var edge Edge
|
||||
if vals := s.valM[prefix]; len(vals) == 0 {
|
||||
edge = Edge{
|
||||
Tail: strV(prefix + mrand.Hex(1)),
|
||||
Head: strV(prefix + mrand.Hex(1)),
|
||||
}
|
||||
edge = NewEdge(
|
||||
strV(prefix+mrand.Hex(1)),
|
||||
strV(prefix+mrand.Hex(1)),
|
||||
)
|
||||
} else if mrand.Intn(2) == 0 {
|
||||
edge = Edge{
|
||||
Tail: mrand.Element(vals, nil).(Value),
|
||||
Head: strV(prefix + mrand.Hex(1)),
|
||||
}
|
||||
edge = NewEdge(
|
||||
mrand.Element(vals, nil).(Value),
|
||||
strV(prefix+mrand.Hex(1)),
|
||||
)
|
||||
} else {
|
||||
edge = Edge{
|
||||
Tail: strV(prefix + mrand.Hex(1)),
|
||||
Head: mrand.Element(vals, nil).(Value),
|
||||
}
|
||||
edge = NewEdge(
|
||||
strV(prefix+mrand.Hex(1)),
|
||||
mrand.Element(vals, nil).(Value),
|
||||
)
|
||||
}
|
||||
|
||||
return mchk.Action{Params: params{prefix: prefix, e: edge}}
|
||||
@ -227,7 +227,7 @@ func TestDisjoinUnion(t *T) {
|
||||
Apply: func(ss mchk.State, a mchk.Action) (mchk.State, error) {
|
||||
s, p := ss.(state), a.Params.(params)
|
||||
s.g = s.g.Add(p.e)
|
||||
s.valM[p.prefix] = append(s.valM[p.prefix], p.e.Head, p.e.Tail)
|
||||
s.valM[p.prefix] = append(s.valM[p.prefix], p.e.Head(), p.e.Tail())
|
||||
s.disjM[p.prefix] = s.disjM[p.prefix].Add(p.e)
|
||||
|
||||
var aa []massert.Assertion
|
||||
@ -314,11 +314,15 @@ func TestVisitBreadth(t *T) {
|
||||
var p params
|
||||
p.newRank = len(thisRank(s)) > 0 && mrand.Intn(10) == 0
|
||||
if p.newRank {
|
||||
p.e.Head = randNew(s)
|
||||
p.e.Tail = randFromRank(s, thisRank)
|
||||
p.e = NewEdge(
|
||||
randFromRank(s, thisRank),
|
||||
randNew(s),
|
||||
)
|
||||
} else {
|
||||
p.e.Head = strV(mrand.Hex(2))
|
||||
p.e.Tail = randFromRank(s, prevRank)
|
||||
p.e = NewEdge(
|
||||
randFromRank(s, prevRank),
|
||||
strV(mrand.Hex(2)),
|
||||
)
|
||||
}
|
||||
return mchk.Action{Params: p}
|
||||
},
|
||||
@ -327,8 +331,8 @@ func TestVisitBreadth(t *T) {
|
||||
if p.newRank {
|
||||
s.ranks = append(s.ranks, map[string]bool{})
|
||||
}
|
||||
if !s.g.Has(p.e.Head) {
|
||||
thisRank(s)[p.e.Head.ID] = true
|
||||
if !s.g.Has(p.e.Head()) {
|
||||
thisRank(s)[p.e.Head().ID] = true
|
||||
}
|
||||
s.g = s.g.Add(p.e)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user