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