implement gg.Graph methods Union, DelValueIn, and Values
This commit is contained in:
parent
9b577c0df8
commit
0b2b53ef56
137
lang/gg/gg.go
137
lang/gg/gg.go
@ -8,6 +8,8 @@ import (
|
|||||||
"hash"
|
"hash"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO rename half-edge to open-edge
|
||||||
|
|
||||||
// Identifier is implemented by any value which can return a unique string for
|
// Identifier is implemented by any value which can return a unique string for
|
||||||
// itself via an Identify method
|
// itself via an Identify method
|
||||||
type Identifier interface {
|
type Identifier interface {
|
||||||
@ -121,6 +123,18 @@ func (v vertex) hasHalfEdge(he HalfEdge) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v vertex) cpAndDelHalfEdge(he HalfEdge) (vertex, bool) {
|
||||||
|
heID := identify(he)
|
||||||
|
for i, in := range v.in {
|
||||||
|
if identify(in) == heID {
|
||||||
|
v = v.cp()
|
||||||
|
v.in = append(v.in[:i], v.in[i+1:]...)
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
|
||||||
// Graph is a wrapper around a set of connected Vertices
|
// Graph is a wrapper around a set of connected Vertices
|
||||||
type Graph struct {
|
type Graph struct {
|
||||||
vM map[string]vertex // only contains value vertices
|
vM map[string]vertex // only contains value vertices
|
||||||
@ -182,11 +196,11 @@ func JunctionOut(in []HalfEdge, edgeVal Identifier) HalfEdge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValueIn takes a HalfEdge and connects it to the Value Vertex containing val,
|
// AddValueIn takes a HalfEdge and connects it to the Value Vertex containing
|
||||||
// and returns the new Graph which reflects that connection. Any Vertices
|
// val, returning the new Graph which reflects that connection. Any Vertices
|
||||||
// referenced within the HalfEdge which do not yet exist in the Graph will also
|
// referenced within the HalfEdge which do not yet exist in the Graph will also
|
||||||
// be created in this step.
|
// be created in this step.
|
||||||
func (g *Graph) ValueIn(he HalfEdge, val Identifier) *Graph {
|
func (g *Graph) AddValueIn(he HalfEdge, val Identifier) *Graph {
|
||||||
to := vertex{
|
to := vertex{
|
||||||
VertexType: Value,
|
VertexType: Value,
|
||||||
val: val,
|
val: val,
|
||||||
@ -217,18 +231,121 @@ func (g *Graph) ValueIn(he HalfEdge, val Identifier) *Graph {
|
|||||||
if _, ok := g.vM[vID]; !ok {
|
if _, ok := g.vM[vID]; !ok {
|
||||||
g.vM[vID] = v
|
g.vM[vID] = v
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
for _, e := range v.in {
|
for _, e := range v.in {
|
||||||
persist(e.fromV)
|
persist(e.fromV)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
delete(g.vM, toID)
|
delete(g.vM, toID)
|
||||||
persist(to)
|
persist(to)
|
||||||
|
for _, e := range to.in {
|
||||||
|
persist(e.fromV)
|
||||||
|
}
|
||||||
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Merge
|
// DelValueIn takes a HalfEdge and disconnects it from the Value Vertex
|
||||||
|
// containing val, returning the new Graph which reflects the disconnection. If
|
||||||
|
// the Value Vertex doesn't exist within the graph, or it doesn't have the given
|
||||||
|
// HalfEdge, no changes are made. Any vertices referenced by the HalfEdge for
|
||||||
|
// which that edge is their only outgoing edge will be removed from the Graph.
|
||||||
|
func (g *Graph) DelValueIn(he HalfEdge, val Identifier) *Graph {
|
||||||
|
to := vertex{
|
||||||
|
VertexType: Value,
|
||||||
|
val: val,
|
||||||
|
}
|
||||||
|
toID := identify(to)
|
||||||
|
|
||||||
|
// pull to out of the graph. if it's not there then bail
|
||||||
|
var ok bool
|
||||||
|
if to, ok = g.vM[toID]; !ok {
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// get new copy of to without the half-edge, or return if the half-edge
|
||||||
|
// wasn't even in to
|
||||||
|
to, ok = to.cpAndDelHalfEdge(he)
|
||||||
|
if !ok {
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
g = g.cp()
|
||||||
|
g.vM[toID] = to
|
||||||
|
|
||||||
|
// connectedTo returns whether the vertex has any connections with the
|
||||||
|
// vertex of the given id, descending recursively
|
||||||
|
var connectedTo func(string, vertex) bool
|
||||||
|
connectedTo = func(vID string, curr vertex) bool {
|
||||||
|
for _, in := range curr.in {
|
||||||
|
if in.fromV.VertexType == Value && identify(in.fromV) == vID {
|
||||||
|
return true
|
||||||
|
} else if in.fromV.VertexType == Junction && connectedTo(vID, in.fromV) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isOrphaned returns whether the given vertex has any connections to other
|
||||||
|
// nodes in the graph
|
||||||
|
isOrphaned := func(v vertex) bool {
|
||||||
|
vID := identify(v)
|
||||||
|
if v, ok := g.vM[vID]; ok && len(v.in) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for vID2, v2 := range g.vM {
|
||||||
|
if vID2 == vID {
|
||||||
|
continue
|
||||||
|
} else if connectedTo(vID, v2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// if to is orphaned get rid of it
|
||||||
|
if isOrphaned(to) {
|
||||||
|
delete(g.vM, toID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rmOrphaned descends down the given HalfEdge and removes any Value
|
||||||
|
// Vertices referenced in it which are now orphaned
|
||||||
|
var rmOrphaned func(HalfEdge)
|
||||||
|
rmOrphaned = func(he HalfEdge) {
|
||||||
|
if he.fromV.VertexType == Value && isOrphaned(he.fromV) {
|
||||||
|
delete(g.vM, identify(he.fromV))
|
||||||
|
} else if he.fromV.VertexType == Junction {
|
||||||
|
for _, juncHe := range he.fromV.in {
|
||||||
|
rmOrphaned(juncHe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rmOrphaned(he)
|
||||||
|
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union takes in another Graph and returns a new one which is the union of the
|
||||||
|
// two. Value vertices which are shared between the two will be merged so that
|
||||||
|
// the new vertex has the input edges of both.
|
||||||
|
func (g *Graph) Union(g2 *Graph) *Graph {
|
||||||
|
g = g.cp()
|
||||||
|
for vID, v2 := range g2.vM {
|
||||||
|
v, ok := g.vM[vID]
|
||||||
|
if !ok {
|
||||||
|
v = v2
|
||||||
|
} else {
|
||||||
|
for _, v2e := range v2.in {
|
||||||
|
if !v.hasHalfEdge(v2e) {
|
||||||
|
v.in = append(v.in, v2e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.vM[vID] = v
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Graph traversal
|
// Graph traversal
|
||||||
@ -287,6 +404,16 @@ func (g *Graph) Value(val Identifier) *Vertex {
|
|||||||
return g.view[identify(val)]
|
return g.view[identify(val)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Values returns all Value Vertices in the Graph
|
||||||
|
func (g *Graph) Values() []*Vertex {
|
||||||
|
g.makeView()
|
||||||
|
vv := make([]*Vertex, 0, len(g.view))
|
||||||
|
for _, v := range g.view {
|
||||||
|
vv = append(vv, v)
|
||||||
|
}
|
||||||
|
return vv
|
||||||
|
}
|
||||||
|
|
||||||
// Equal returns whether or not the two Graphs are equivalent in value
|
// Equal returns whether or not the two Graphs are equivalent in value
|
||||||
func Equal(g1, g2 *Graph) bool {
|
func Equal(g1, g2 *Graph) bool {
|
||||||
if len(g1.vM) != len(g2.vM) {
|
if len(g1.vM) != len(g2.vM) {
|
||||||
|
@ -83,7 +83,7 @@ func TestGraph(t *T) {
|
|||||||
mkTest(
|
mkTest(
|
||||||
"values-basic",
|
"values-basic",
|
||||||
func() *Graph {
|
func() *Graph {
|
||||||
return Null.ValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
return Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
},
|
},
|
||||||
value("v0"),
|
value("v0"),
|
||||||
value("v1", edge("e0", value("v0"))),
|
value("v1", edge("e0", value("v0"))),
|
||||||
@ -92,8 +92,8 @@ func TestGraph(t *T) {
|
|||||||
mkTest(
|
mkTest(
|
||||||
"values-2edges",
|
"values-2edges",
|
||||||
func() *Graph {
|
func() *Graph {
|
||||||
g0 := Null.ValueIn(ValueOut(id("v0"), id("e0")), id("v2"))
|
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v2"))
|
||||||
return g0.ValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
return g0.AddValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
||||||
},
|
},
|
||||||
value("v0"),
|
value("v0"),
|
||||||
value("v1"),
|
value("v1"),
|
||||||
@ -106,8 +106,8 @@ func TestGraph(t *T) {
|
|||||||
mkTest(
|
mkTest(
|
||||||
"values-separate",
|
"values-separate",
|
||||||
func() *Graph {
|
func() *Graph {
|
||||||
g0 := Null.ValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
return g0.ValueIn(ValueOut(id("v2"), id("e2")), id("v3"))
|
return g0.AddValueIn(ValueOut(id("v2"), id("e2")), id("v3"))
|
||||||
},
|
},
|
||||||
value("v0"),
|
value("v0"),
|
||||||
value("v1", edge("e0", value("v0"))),
|
value("v1", edge("e0", value("v0"))),
|
||||||
@ -118,7 +118,7 @@ func TestGraph(t *T) {
|
|||||||
mkTest(
|
mkTest(
|
||||||
"values-circular",
|
"values-circular",
|
||||||
func() *Graph {
|
func() *Graph {
|
||||||
return Null.ValueIn(ValueOut(id("v0"), id("e")), id("v0"))
|
return Null.AddValueIn(ValueOut(id("v0"), id("e")), id("v0"))
|
||||||
},
|
},
|
||||||
value("v0", edge("e", value("v0"))),
|
value("v0", edge("e", value("v0"))),
|
||||||
),
|
),
|
||||||
@ -126,8 +126,8 @@ func TestGraph(t *T) {
|
|||||||
mkTest(
|
mkTest(
|
||||||
"values-circular2",
|
"values-circular2",
|
||||||
func() *Graph {
|
func() *Graph {
|
||||||
g0 := Null.ValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
return g0.ValueIn(ValueOut(id("v1"), id("e1")), id("v0"))
|
return g0.AddValueIn(ValueOut(id("v1"), id("e1")), id("v0"))
|
||||||
},
|
},
|
||||||
value("v0", edge("e1", value("v1", edge("e0", value("v0"))))),
|
value("v0", edge("e1", value("v1", edge("e0", value("v0"))))),
|
||||||
value("v1", edge("e0", value("v0", edge("e1", value("v1"))))),
|
value("v1", edge("e0", value("v0", edge("e1", value("v1"))))),
|
||||||
@ -136,9 +136,9 @@ func TestGraph(t *T) {
|
|||||||
mkTest(
|
mkTest(
|
||||||
"values-circular3",
|
"values-circular3",
|
||||||
func() *Graph {
|
func() *Graph {
|
||||||
g0 := Null.ValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
g1 := g0.ValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
g1 := g0.AddValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
||||||
return g1.ValueIn(ValueOut(id("v2"), id("e2")), id("v1"))
|
return g1.AddValueIn(ValueOut(id("v2"), id("e2")), id("v1"))
|
||||||
},
|
},
|
||||||
value("v0"),
|
value("v0"),
|
||||||
value("v1",
|
value("v1",
|
||||||
@ -157,7 +157,7 @@ func TestGraph(t *T) {
|
|||||||
e0 := ValueOut(id("v0"), id("e0"))
|
e0 := ValueOut(id("v0"), id("e0"))
|
||||||
e1 := ValueOut(id("v1"), id("e1"))
|
e1 := ValueOut(id("v1"), id("e1"))
|
||||||
ej0 := JunctionOut([]HalfEdge{e0, e1}, id("ej0"))
|
ej0 := JunctionOut([]HalfEdge{e0, e1}, id("ej0"))
|
||||||
return Null.ValueIn(ej0, id("v2"))
|
return Null.AddValueIn(ej0, id("v2"))
|
||||||
},
|
},
|
||||||
value("v0"), value("v1"),
|
value("v0"), value("v1"),
|
||||||
value("v2", junction("ej0",
|
value("v2", junction("ej0",
|
||||||
@ -176,7 +176,7 @@ func TestGraph(t *T) {
|
|||||||
e11 := ValueOut(id("v1"), id("e11"))
|
e11 := ValueOut(id("v1"), id("e11"))
|
||||||
ej1 := JunctionOut([]HalfEdge{e01, e11}, id("ej1"))
|
ej1 := JunctionOut([]HalfEdge{e01, e11}, id("ej1"))
|
||||||
ej2 := JunctionOut([]HalfEdge{ej0, ej1}, id("ej2"))
|
ej2 := JunctionOut([]HalfEdge{ej0, ej1}, id("ej2"))
|
||||||
return Null.ValueIn(ej2, id("v2"))
|
return Null.AddValueIn(ej2, id("v2"))
|
||||||
},
|
},
|
||||||
value("v0"), value("v1"),
|
value("v0"), value("v1"),
|
||||||
value("v2", junction("ej2",
|
value("v2", junction("ej2",
|
||||||
@ -197,11 +197,11 @@ func TestGraph(t *T) {
|
|||||||
e0 := ValueOut(id("v0"), id("e0"))
|
e0 := ValueOut(id("v0"), id("e0"))
|
||||||
e1 := ValueOut(id("v1"), id("e1"))
|
e1 := ValueOut(id("v1"), id("e1"))
|
||||||
ej0 := JunctionOut([]HalfEdge{e0, e1}, id("ej0"))
|
ej0 := JunctionOut([]HalfEdge{e0, e1}, id("ej0"))
|
||||||
g0 := Null.ValueIn(ej0, id("v2"))
|
g0 := Null.AddValueIn(ej0, id("v2"))
|
||||||
e20 := ValueOut(id("v2"), id("e20"))
|
e20 := ValueOut(id("v2"), id("e20"))
|
||||||
g1 := g0.ValueIn(e20, id("v0"))
|
g1 := g0.AddValueIn(e20, id("v0"))
|
||||||
e21 := ValueOut(id("v2"), id("e21"))
|
e21 := ValueOut(id("v2"), id("e21"))
|
||||||
return g1.ValueIn(e21, id("v1"))
|
return g1.AddValueIn(e21, id("v1"))
|
||||||
},
|
},
|
||||||
value("v0", edge("e20", value("v2", junction("ej0",
|
value("v0", edge("e20", value("v2", junction("ej0",
|
||||||
edge("e0", value("v0")),
|
edge("e0", value("v0")),
|
||||||
@ -239,7 +239,7 @@ func TestGraph(t *T) {
|
|||||||
|
|
||||||
func TestGraphImmutability(t *T) {
|
func TestGraphImmutability(t *T) {
|
||||||
e0 := ValueOut(id("v0"), id("e0"))
|
e0 := ValueOut(id("v0"), id("e0"))
|
||||||
g0 := Null.ValueIn(e0, id("v1"))
|
g0 := Null.AddValueIn(e0, id("v1"))
|
||||||
assert.Nil(t, Null.Value(id("v0")))
|
assert.Nil(t, Null.Value(id("v0")))
|
||||||
assert.Nil(t, Null.Value(id("v1")))
|
assert.Nil(t, Null.Value(id("v1")))
|
||||||
assert.NotNil(t, g0.Value(id("v0")))
|
assert.NotNil(t, g0.Value(id("v0")))
|
||||||
@ -247,21 +247,219 @@ func TestGraphImmutability(t *T) {
|
|||||||
|
|
||||||
// half-edges should be re-usable
|
// half-edges should be re-usable
|
||||||
e1 := ValueOut(id("v2"), id("e1"))
|
e1 := ValueOut(id("v2"), id("e1"))
|
||||||
g1a := g0.ValueIn(e1, id("v3a"))
|
g1a := g0.AddValueIn(e1, id("v3a"))
|
||||||
g1b := g0.ValueIn(e1, id("v3b"))
|
g1b := g0.AddValueIn(e1, id("v3b"))
|
||||||
assertVertexEqual(t, value("v3a", edge("e1", value("v2"))), g1a.Value(id("v3a")))
|
assertVertexEqual(t, value("v3a", edge("e1", value("v2"))), g1a.Value(id("v3a")))
|
||||||
assert.Nil(t, g1a.Value(id("v3b")))
|
assert.Nil(t, g1a.Value(id("v3b")))
|
||||||
assertVertexEqual(t, value("v3b", edge("e1", value("v2"))), g1b.Value(id("v3b")))
|
assertVertexEqual(t, value("v3b", edge("e1", value("v2"))), g1b.Value(id("v3b")))
|
||||||
assert.Nil(t, g1b.Value(id("v3a")))
|
assert.Nil(t, g1b.Value(id("v3a")))
|
||||||
|
|
||||||
// ... even re-usable twice in succession
|
// ... even re-usable twice in succession
|
||||||
g2 := g0.ValueIn(e1, id("v3")).ValueIn(e1, id("v4"))
|
g2 := g0.AddValueIn(e1, id("v3")).AddValueIn(e1, id("v4"))
|
||||||
assert.Nil(t, g2.Value(id("v3b")))
|
assert.Nil(t, g2.Value(id("v3b")))
|
||||||
assert.Nil(t, g2.Value(id("v3a")))
|
assert.Nil(t, g2.Value(id("v3a")))
|
||||||
assertVertexEqual(t, value("v3", edge("e1", value("v2"))), g2.Value(id("v3")))
|
assertVertexEqual(t, value("v3", edge("e1", value("v2"))), g2.Value(id("v3")))
|
||||||
assertVertexEqual(t, value("v4", edge("e1", value("v2"))), g2.Value(id("v4")))
|
assertVertexEqual(t, value("v4", edge("e1", value("v2"))), g2.Value(id("v4")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGraphDelValueIn(t *T) {
|
||||||
|
{ // removing from null
|
||||||
|
g := Null.DelValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
|
assert.True(t, Equal(Null, g))
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // removing edge from vertex which doesn't have that edge
|
||||||
|
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
|
g1 := g0.DelValueIn(ValueOut(id("v0"), id("e1")), id("v1"))
|
||||||
|
assert.True(t, Equal(g0, g1))
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // removing only edge
|
||||||
|
he := ValueOut(id("v0"), id("e0"))
|
||||||
|
g0 := Null.AddValueIn(he, id("v1"))
|
||||||
|
g1 := g0.DelValueIn(he, id("v1"))
|
||||||
|
assert.True(t, Equal(Null, g1))
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // removing only edge (junction)
|
||||||
|
he := JunctionOut([]HalfEdge{
|
||||||
|
ValueOut(id("v0"), id("e0")),
|
||||||
|
ValueOut(id("v1"), id("e1")),
|
||||||
|
}, id("ej0"))
|
||||||
|
g0 := Null.AddValueIn(he, id("v2"))
|
||||||
|
g1 := g0.DelValueIn(he, id("v2"))
|
||||||
|
assert.True(t, Equal(Null, g1))
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // removing one of two edges
|
||||||
|
he := ValueOut(id("v1"), id("e0"))
|
||||||
|
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v2"))
|
||||||
|
g1 := g0.AddValueIn(he, id("v2"))
|
||||||
|
g2 := g1.DelValueIn(he, id("v2"))
|
||||||
|
assert.True(t, Equal(g0, g2))
|
||||||
|
assert.NotNil(t, g2.Value(id("v0")))
|
||||||
|
assert.Nil(t, g2.Value(id("v1")))
|
||||||
|
assert.NotNil(t, g2.Value(id("v2")))
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // removing one of two edges (junction)
|
||||||
|
e0 := ValueOut(id("v0"), id("e0"))
|
||||||
|
e1 := ValueOut(id("v1"), id("e1"))
|
||||||
|
e2 := ValueOut(id("v2"), id("e2"))
|
||||||
|
heA := JunctionOut([]HalfEdge{e0, e1}, id("heA"))
|
||||||
|
heB := JunctionOut([]HalfEdge{e1, e2}, id("heB"))
|
||||||
|
g0a := Null.AddValueIn(heA, id("v3"))
|
||||||
|
g0b := Null.AddValueIn(heB, id("v3"))
|
||||||
|
g1 := g0a.Union(g0b).DelValueIn(heA, id("v3"))
|
||||||
|
assert.True(t, Equal(g1, g0b))
|
||||||
|
assert.Nil(t, g1.Value(id("v0")))
|
||||||
|
assert.NotNil(t, g1.Value(id("v1")))
|
||||||
|
assert.NotNil(t, g1.Value(id("v2")))
|
||||||
|
assert.NotNil(t, g1.Value(id("v3")))
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // removing one of two edges in circular graph
|
||||||
|
e0 := ValueOut(id("v0"), id("e0"))
|
||||||
|
e1 := ValueOut(id("v1"), id("e1"))
|
||||||
|
g0 := Null.AddValueIn(e0, id("v1")).AddValueIn(e1, id("v0"))
|
||||||
|
g1 := g0.DelValueIn(e0, id("v1"))
|
||||||
|
assert.True(t, Equal(Null.AddValueIn(e1, id("v0")), g1))
|
||||||
|
assert.NotNil(t, g1.Value(id("v0")))
|
||||||
|
assert.NotNil(t, g1.Value(id("v1")))
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // removing to's only edge, sub-nodes have edge to each other
|
||||||
|
ej := JunctionOut([]HalfEdge{
|
||||||
|
ValueOut(id("v0"), id("ej0")),
|
||||||
|
ValueOut(id("v1"), id("ej0")),
|
||||||
|
}, id("ej"))
|
||||||
|
g0 := Null.AddValueIn(ej, id("v2"))
|
||||||
|
e0 := ValueOut(id("v0"), id("e0"))
|
||||||
|
g1 := g0.AddValueIn(e0, id("v1"))
|
||||||
|
g2 := g1.DelValueIn(ej, id("v2"))
|
||||||
|
assert.True(t, Equal(Null.AddValueIn(e0, id("v1")), g2))
|
||||||
|
assert.NotNil(t, g2.Value(id("v0")))
|
||||||
|
assert.NotNil(t, g2.Value(id("v1")))
|
||||||
|
assert.Nil(t, g2.Value(id("v2")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGraphUnion(t *T) {
|
||||||
|
assertUnion := func(g1, g2 *Graph) *Graph {
|
||||||
|
ga := g1.Union(g2)
|
||||||
|
gb := g2.Union(g1)
|
||||||
|
assert.True(t, Equal(ga, gb))
|
||||||
|
return ga
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Union with Null
|
||||||
|
assert.True(t, Equal(Null, Null.Union(Null)))
|
||||||
|
|
||||||
|
g := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
|
assert.True(t, Equal(g, assertUnion(g, Null)))
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Two disparate graphs union'd
|
||||||
|
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
|
g1 := Null.AddValueIn(ValueOut(id("v2"), id("e1")), id("v3"))
|
||||||
|
g := assertUnion(g0, g1)
|
||||||
|
assertVertexEqual(t, value("v0"), g.Value(id("v0")))
|
||||||
|
assertVertexEqual(t, value("v1", edge("e0", value("v0"))), g.Value(id("v1")))
|
||||||
|
assertVertexEqual(t, value("v2"), g.Value(id("v2")))
|
||||||
|
assertVertexEqual(t, value("v3", edge("e1", value("v2"))), g.Value(id("v3")))
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Two disparate graphs with junctions
|
||||||
|
ga := Null.AddValueIn(JunctionOut([]HalfEdge{
|
||||||
|
ValueOut(id("va0"), id("ea0")),
|
||||||
|
ValueOut(id("va1"), id("ea1")),
|
||||||
|
}, id("eaj")), id("va2"))
|
||||||
|
gb := Null.AddValueIn(JunctionOut([]HalfEdge{
|
||||||
|
ValueOut(id("vb0"), id("eb0")),
|
||||||
|
ValueOut(id("vb1"), id("eb1")),
|
||||||
|
}, id("ebj")), id("vb2"))
|
||||||
|
g := assertUnion(ga, gb)
|
||||||
|
assertVertexEqual(t, value("va0"), g.Value(id("va0")))
|
||||||
|
assertVertexEqual(t, value("va1"), g.Value(id("va1")))
|
||||||
|
assertVertexEqual(t,
|
||||||
|
value("va2", junction("eaj",
|
||||||
|
edge("ea0", value("va0")),
|
||||||
|
edge("ea1", value("va1")))),
|
||||||
|
g.Value(id("va2")),
|
||||||
|
)
|
||||||
|
assertVertexEqual(t, value("vb0"), g.Value(id("vb0")))
|
||||||
|
assertVertexEqual(t, value("vb1"), g.Value(id("vb1")))
|
||||||
|
assertVertexEqual(t,
|
||||||
|
value("vb2", junction("ebj",
|
||||||
|
edge("eb0", value("vb0")),
|
||||||
|
edge("eb1", value("vb1")))),
|
||||||
|
g.Value(id("vb2")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Two partially overlapping graphs
|
||||||
|
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v2"))
|
||||||
|
g1 := Null.AddValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
||||||
|
g := assertUnion(g0, g1)
|
||||||
|
assertVertexEqual(t, value("v0"), g.Value(id("v0")))
|
||||||
|
assertVertexEqual(t, value("v1"), g.Value(id("v1")))
|
||||||
|
assertVertexEqual(t,
|
||||||
|
value("v2",
|
||||||
|
edge("e0", value("v0")),
|
||||||
|
edge("e1", value("v1")),
|
||||||
|
),
|
||||||
|
g.Value(id("v2")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // two partially overlapping graphs with junctions
|
||||||
|
g0 := Null.AddValueIn(JunctionOut([]HalfEdge{
|
||||||
|
ValueOut(id("v0"), id("e0")),
|
||||||
|
ValueOut(id("v1"), id("e1")),
|
||||||
|
}, id("ej0")), id("v2"))
|
||||||
|
g1 := Null.AddValueIn(JunctionOut([]HalfEdge{
|
||||||
|
ValueOut(id("v0"), id("e0")),
|
||||||
|
ValueOut(id("v1"), id("e1")),
|
||||||
|
}, id("ej1")), id("v2"))
|
||||||
|
g := assertUnion(g0, g1)
|
||||||
|
assertVertexEqual(t, value("v0"), g.Value(id("v0")))
|
||||||
|
assertVertexEqual(t, value("v1"), g.Value(id("v1")))
|
||||||
|
assertVertexEqual(t,
|
||||||
|
value("v2",
|
||||||
|
junction("ej0", edge("e0", value("v0")), edge("e1", value("v1"))),
|
||||||
|
junction("ej1", edge("e0", value("v0")), edge("e1", value("v1"))),
|
||||||
|
),
|
||||||
|
g.Value(id("v2")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Two equal graphs
|
||||||
|
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
|
g := assertUnion(g0, g0)
|
||||||
|
assertVertexEqual(t, value("v0"), g.Value(id("v0")))
|
||||||
|
assertVertexEqual(t,
|
||||||
|
value("v1", edge("e0", value("v0"))),
|
||||||
|
g.Value(id("v1")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Two equal graphs with junctions
|
||||||
|
g0 := Null.AddValueIn(JunctionOut([]HalfEdge{
|
||||||
|
ValueOut(id("v0"), id("e0")),
|
||||||
|
ValueOut(id("v1"), id("e1")),
|
||||||
|
}, id("ej0")), id("v2"))
|
||||||
|
g := assertUnion(g0, g0)
|
||||||
|
assertVertexEqual(t, value("v0"), g.Value(id("v0")))
|
||||||
|
assertVertexEqual(t, value("v1"), g.Value(id("v1")))
|
||||||
|
assertVertexEqual(t,
|
||||||
|
value("v2",
|
||||||
|
junction("ej0", edge("e0", value("v0")), edge("e1", value("v1"))),
|
||||||
|
),
|
||||||
|
g.Value(id("v2")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGraphEqual(t *T) {
|
func TestGraphEqual(t *T) {
|
||||||
assertEqual := func(g1, g2 *Graph) {
|
assertEqual := func(g1, g2 *Graph) {
|
||||||
assert.True(t, Equal(g1, g2))
|
assert.True(t, Equal(g1, g2))
|
||||||
@ -278,32 +476,32 @@ func TestGraphEqual(t *T) {
|
|||||||
{
|
{
|
||||||
// graph is equal to itself, not to null
|
// graph is equal to itself, not to null
|
||||||
e0 := ValueOut(id("v0"), id("e0"))
|
e0 := ValueOut(id("v0"), id("e0"))
|
||||||
g0 := Null.ValueIn(e0, id("v1"))
|
g0 := Null.AddValueIn(e0, id("v1"))
|
||||||
assertNotEqual(g0, Null)
|
assertNotEqual(g0, Null)
|
||||||
assertEqual(g0, g0)
|
assertEqual(g0, g0)
|
||||||
|
|
||||||
// adding the an existing edge again shouldn't do anything
|
// adding the an existing edge again shouldn't do anything
|
||||||
assertEqual(g0, g0.ValueIn(e0, id("v1")))
|
assertEqual(g0, g0.AddValueIn(e0, id("v1")))
|
||||||
|
|
||||||
// g1a and g1b have the same vertices, but the edges are different
|
// g1a and g1b have the same vertices, but the edges are different
|
||||||
g1a := g0.ValueIn(ValueOut(id("v0"), id("e1a")), id("v2"))
|
g1a := g0.AddValueIn(ValueOut(id("v0"), id("e1a")), id("v2"))
|
||||||
g1b := g0.ValueIn(ValueOut(id("v0"), id("e1b")), id("v2"))
|
g1b := g0.AddValueIn(ValueOut(id("v0"), id("e1b")), id("v2"))
|
||||||
assertNotEqual(g1a, g1b)
|
assertNotEqual(g1a, g1b)
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // equal construction should yield equality, even if out of order
|
{ // equal construction should yield equality, even if out of order
|
||||||
ga := Null.ValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
ga := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
ga = ga.ValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
ga = ga.AddValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
||||||
gb := Null.ValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
gb := Null.AddValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
||||||
gb = gb.ValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
gb = gb.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||||
assertEqual(ga, gb)
|
assertEqual(ga, gb)
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // junction basic test
|
{ // junction basic test
|
||||||
e0 := ValueOut(id("v0"), id("e0"))
|
e0 := ValueOut(id("v0"), id("e0"))
|
||||||
e1 := ValueOut(id("v1"), id("e1"))
|
e1 := ValueOut(id("v1"), id("e1"))
|
||||||
ga := Null.ValueIn(JunctionOut([]HalfEdge{e0, e1}, id("ej")), id("v2"))
|
ga := Null.AddValueIn(JunctionOut([]HalfEdge{e0, e1}, id("ej")), id("v2"))
|
||||||
gb := Null.ValueIn(JunctionOut([]HalfEdge{e1, e0}, id("ej")), id("v2"))
|
gb := Null.AddValueIn(JunctionOut([]HalfEdge{e1, e0}, id("ej")), id("v2"))
|
||||||
assertEqual(ga, ga)
|
assertEqual(ga, ga)
|
||||||
assertNotEqual(ga, gb)
|
assertNotEqual(ga, gb)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user