refacctor gg to use Value instead of Identifier, which will make serializing more straightforward, and reduces some complexity of the code besides
This commit is contained in:
parent
754b75407a
commit
e52befb7ed
181
gg/gg.go
181
gg/gg.go
@ -2,63 +2,62 @@
|
||||
package gg
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TODO instead of Identifier being public, make it encoding.TextMarshaler
|
||||
|
||||
// Identifier is implemented by any value which can return a unique string for
|
||||
// itself via an Identify method
|
||||
type Identifier interface {
|
||||
Identify(hash.Hash)
|
||||
// Value wraps a go value in a way such that it will be uniquely identified
|
||||
// within any Graph and between Graphs. Use NewValue to create a Value instance.
|
||||
// You can create an instance manually as long as ID is globally unique.
|
||||
type Value struct {
|
||||
ID string
|
||||
V interface{}
|
||||
}
|
||||
|
||||
func identify(i Identifier) string {
|
||||
h := md5.New()
|
||||
i.Identify(h)
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
// NewValue returns a Value instance wrapping any go value. The Value returned
|
||||
// will be independent of the passed in go value. So if the same go value is
|
||||
// passed in twice then the two returned Value instances will be treated as
|
||||
// being different values by Graph.
|
||||
func NewValue(V interface{}) Value {
|
||||
b := make([]byte, 8)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return Value{
|
||||
ID: hex.EncodeToString(b),
|
||||
V: V,
|
||||
}
|
||||
|
||||
// Str is an Identifier identified by its string value
|
||||
type Str string
|
||||
|
||||
// Identify implements the Identifier interface
|
||||
func (s Str) Identify(h hash.Hash) {
|
||||
fmt.Fprintf(h, "%q", s)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// VertexType enumerates the different possible vertex types
|
||||
type VertexType string
|
||||
|
||||
const (
|
||||
// Value is a Vertex which contains exactly one value and has at least one
|
||||
// edge (either input or output)
|
||||
Value VertexType = "value"
|
||||
// ValueVertex is a Vertex which contains exactly one value and has at least
|
||||
// one edge (either input or output)
|
||||
ValueVertex VertexType = "value"
|
||||
|
||||
// Junction is a Vertex which contains two or more in edges and exactly one
|
||||
// out edge
|
||||
Junction VertexType = "junction"
|
||||
// JunctionVertex is a Vertex which contains two or more in edges and
|
||||
// exactly one out edge
|
||||
JunctionVertex VertexType = "junction"
|
||||
)
|
||||
|
||||
// Edge is a uni-directional connection between two vertices with an attribute
|
||||
// value
|
||||
type Edge struct {
|
||||
From *Vertex
|
||||
Value Identifier
|
||||
Value Value
|
||||
To *Vertex
|
||||
}
|
||||
|
||||
// Vertex is a vertex in a Graph. No fields should be modified directly, only
|
||||
// through method calls
|
||||
type Vertex struct {
|
||||
ID string
|
||||
VertexType
|
||||
ID string // identifier of the vertex, unique within the graph
|
||||
Value Identifier // Value is valid if-and-only-if VertexType is Value
|
||||
Value Value // Value is valid if-and-only-if VertexType is ValueVertex
|
||||
In, Out []Edge
|
||||
}
|
||||
|
||||
@ -72,14 +71,11 @@ type OpenEdge struct {
|
||||
// already that will need to be taken into account when persisting into the
|
||||
// graph
|
||||
fromV vertex
|
||||
val Identifier
|
||||
val Value
|
||||
}
|
||||
|
||||
// Identify implements the Identifier interface
|
||||
func (oe OpenEdge) Identify(h hash.Hash) {
|
||||
fmt.Fprintln(h, "openEdge")
|
||||
oe.fromV.Identify(h)
|
||||
oe.val.Identify(h)
|
||||
func (oe OpenEdge) id() string {
|
||||
return fmt.Sprintf("(%s,%s)", oe.fromV.id, oe.val.ID)
|
||||
}
|
||||
|
||||
// vertex is a representation of a vertex in the graph. Each Graph contains a
|
||||
@ -95,28 +91,12 @@ func (oe OpenEdge) Identify(h hash.Hash) {
|
||||
// When a view is constructed in makeView these Value instances are deduplicated
|
||||
// and the top-level one's in value is used to properly connect it.
|
||||
type vertex struct {
|
||||
id string
|
||||
VertexType
|
||||
val Identifier
|
||||
val Value
|
||||
in []OpenEdge
|
||||
}
|
||||
|
||||
// A Value vertex is unique by the value it contains
|
||||
// A Junction vertex is unique by its input edges
|
||||
func (v vertex) Identify(h hash.Hash) {
|
||||
switch v.VertexType {
|
||||
case Value:
|
||||
fmt.Fprintln(h, "value")
|
||||
v.val.Identify(h)
|
||||
case Junction:
|
||||
fmt.Fprintf(h, "junction:%d\n", len(v.in))
|
||||
for _, in := range v.in {
|
||||
in.Identify(h)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid VertexType:%#v", v))
|
||||
}
|
||||
}
|
||||
|
||||
func (v vertex) cp() vertex {
|
||||
cp := v
|
||||
cp.in = make([]OpenEdge, len(v.in))
|
||||
@ -125,9 +105,9 @@ func (v vertex) cp() vertex {
|
||||
}
|
||||
|
||||
func (v vertex) hasOpenEdge(oe OpenEdge) bool {
|
||||
oeID := identify(oe)
|
||||
oeID := oe.id()
|
||||
for _, in := range v.in {
|
||||
if identify(in) == oeID {
|
||||
if in.id() == oeID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -135,9 +115,9 @@ func (v vertex) hasOpenEdge(oe OpenEdge) bool {
|
||||
}
|
||||
|
||||
func (v vertex) cpAndDelOpenEdge(oe OpenEdge) (vertex, bool) {
|
||||
oeID := identify(oe)
|
||||
oeID := oe.id()
|
||||
for i, in := range v.in {
|
||||
if identify(in) == oeID {
|
||||
if in.id() == oeID {
|
||||
v = v.cp()
|
||||
v.in = append(v.in[:i], v.in[i+1:]...)
|
||||
return v, true
|
||||
@ -168,8 +148,8 @@ func (g *Graph) cp() *Graph {
|
||||
cp := &Graph{
|
||||
vM: make(map[string]vertex, len(g.vM)),
|
||||
}
|
||||
for id, v := range g.vM {
|
||||
cp.vM[id] = v
|
||||
for vID, v := range g.vM {
|
||||
cp.vM[vID] = v
|
||||
}
|
||||
return cp
|
||||
}
|
||||
@ -178,16 +158,17 @@ func (g *Graph) cp() *Graph {
|
||||
// Graph creation
|
||||
|
||||
// ValueOut creates a OpenEdge which, when used to construct a Graph, represents
|
||||
// an edge (with edgeVal attached to it) leaving the Value Vertex containing
|
||||
// an edge (with edgeVal attached to it) coming from the ValueVertex containing
|
||||
// val.
|
||||
//
|
||||
// When constructing Graphs Value vertices are de-duplicated on their value. So
|
||||
// When constructing Graphs, Value vertices are de-duplicated on their Value. So
|
||||
// multiple ValueOut OpenEdges constructed with the same val will be leaving the
|
||||
// same Vertex instance in the constructed Graph.
|
||||
func ValueOut(val, edgeVal Identifier) OpenEdge {
|
||||
func ValueOut(val, edgeVal Value) OpenEdge {
|
||||
return OpenEdge{
|
||||
fromV: vertex{
|
||||
VertexType: Value,
|
||||
id: val.ID,
|
||||
VertexType: ValueVertex,
|
||||
val: val,
|
||||
},
|
||||
val: edgeVal,
|
||||
@ -195,16 +176,21 @@ func ValueOut(val, edgeVal Identifier) OpenEdge {
|
||||
}
|
||||
|
||||
// JunctionOut creates a OpenEdge which, when used to construct a Graph,
|
||||
// represents an edge (with edgeVal attached to it) leaving the Junction Vertex
|
||||
// comprised of the given ordered-set of input edges.
|
||||
// represents an edge (with edgeVal attached to it) coming from the
|
||||
// JunctionVertex comprised of the given ordered-set of input edges.
|
||||
//
|
||||
// When constructing Graphs Junction vertices are de-duplicated on their input
|
||||
// edges. So multiple Junction OpenEdges constructed with the same set of input
|
||||
// edges will be leaving the same Junction instance in the constructed Graph.
|
||||
func JunctionOut(in []OpenEdge, edgeVal Identifier) OpenEdge {
|
||||
func JunctionOut(in []OpenEdge, edgeVal Value) OpenEdge {
|
||||
inIDs := make([]string, len(in))
|
||||
for i := range in {
|
||||
inIDs[i] = in[i].id()
|
||||
}
|
||||
return OpenEdge{
|
||||
fromV: vertex{
|
||||
VertexType: Junction,
|
||||
id: "[" + strings.Join(inIDs, ",") + "]",
|
||||
VertexType: JunctionVertex,
|
||||
in: in,
|
||||
},
|
||||
val: edgeVal,
|
||||
@ -215,12 +201,13 @@ func JunctionOut(in []OpenEdge, edgeVal Identifier) OpenEdge {
|
||||
// val, returning the new Graph which reflects that connection. Any Vertices
|
||||
// referenced within toe OpenEdge which do not yet exist in the Graph will also
|
||||
// be created in this step.
|
||||
func (g *Graph) AddValueIn(oe OpenEdge, val Identifier) *Graph {
|
||||
func (g *Graph) AddValueIn(oe OpenEdge, val Value) *Graph {
|
||||
to := vertex{
|
||||
VertexType: Value,
|
||||
id: val.ID,
|
||||
VertexType: ValueVertex,
|
||||
val: val,
|
||||
}
|
||||
toID := identify(to)
|
||||
toID := to.id
|
||||
|
||||
// if to is already in the graph, pull it out, as it might have existing in
|
||||
// edges we want to keep
|
||||
@ -241,8 +228,8 @@ func (g *Graph) AddValueIn(oe OpenEdge, val Identifier) *Graph {
|
||||
// recursively add in any vertices which aren't already there
|
||||
var persist func(vertex)
|
||||
persist = func(v vertex) {
|
||||
vID := identify(v)
|
||||
if v.VertexType == Value {
|
||||
if v.VertexType == ValueVertex {
|
||||
vID := v.id
|
||||
if _, ok := g.vM[vID]; !ok {
|
||||
g.vM[vID] = v
|
||||
}
|
||||
@ -266,12 +253,13 @@ func (g *Graph) AddValueIn(oe OpenEdge, val Identifier) *Graph {
|
||||
// the Value Vertex doesn't exist within the graph, or it doesn't have the given
|
||||
// OpenEdge, no changes are made. Any vertices referenced by toe OpenEdge for
|
||||
// which that edge is their only outgoing edge will be removed from the Graph.
|
||||
func (g *Graph) DelValueIn(oe OpenEdge, val Identifier) *Graph {
|
||||
func (g *Graph) DelValueIn(oe OpenEdge, val Value) *Graph {
|
||||
to := vertex{
|
||||
VertexType: Value,
|
||||
id: val.ID,
|
||||
VertexType: ValueVertex,
|
||||
val: val,
|
||||
}
|
||||
toID := identify(to)
|
||||
toID := to.id
|
||||
|
||||
// pull to out of the graph. if it's not there then bail
|
||||
var ok bool
|
||||
@ -293,9 +281,9 @@ func (g *Graph) DelValueIn(oe OpenEdge, val Identifier) *Graph {
|
||||
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 {
|
||||
if in.fromV.VertexType == ValueVertex && in.fromV.id == vID {
|
||||
return true
|
||||
} else if in.fromV.VertexType == Junction && connectedTo(vID, in.fromV) {
|
||||
} else if in.fromV.VertexType == JunctionVertex && connectedTo(vID, in.fromV) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -305,7 +293,7 @@ func (g *Graph) DelValueIn(oe OpenEdge, val Identifier) *Graph {
|
||||
// isOrphaned returns whether the given vertex has any connections to other
|
||||
// nodes in the graph
|
||||
isOrphaned := func(v vertex) bool {
|
||||
vID := identify(v)
|
||||
vID := v.id
|
||||
if v, ok := g.vM[vID]; ok && len(v.in) > 0 {
|
||||
return false
|
||||
}
|
||||
@ -328,11 +316,11 @@ func (g *Graph) DelValueIn(oe OpenEdge, val Identifier) *Graph {
|
||||
// Vertices referenced in it which are now orphaned
|
||||
var rmOrphaned func(OpenEdge)
|
||||
rmOrphaned = func(oe OpenEdge) {
|
||||
if oe.fromV.VertexType == Value && isOrphaned(oe.fromV) {
|
||||
delete(g.vM, identify(oe.fromV))
|
||||
} else if oe.fromV.VertexType == Junction {
|
||||
for _, juncHe := range oe.fromV.in {
|
||||
rmOrphaned(juncHe)
|
||||
if oe.fromV.VertexType == ValueVertex && isOrphaned(oe.fromV) {
|
||||
delete(g.vM, oe.fromV.id)
|
||||
} else if oe.fromV.VertexType == JunctionVertex {
|
||||
for _, juncOe := range oe.fromV.in {
|
||||
rmOrphaned(juncOe)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -377,18 +365,17 @@ func (g *Graph) makeView() {
|
||||
|
||||
var getV func(vertex, bool) *Vertex
|
||||
getV = func(v vertex, top bool) *Vertex {
|
||||
vID := identify(v)
|
||||
V, ok := g.all[vID]
|
||||
V, ok := g.all[v.id]
|
||||
if !ok {
|
||||
V = &Vertex{VertexType: v.VertexType, ID: vID, Value: v.val}
|
||||
g.all[vID] = V
|
||||
V = &Vertex{ID: v.id, VertexType: v.VertexType, Value: v.val}
|
||||
g.all[v.id] = V
|
||||
}
|
||||
|
||||
// we can be sure all Value vertices will be called with top==true at
|
||||
// some point, so we only need to descend into the input edges if:
|
||||
// * top is true
|
||||
// * this is a junction's first time being gotten
|
||||
if !top && (ok || v.VertexType != Junction) {
|
||||
if !top && (ok || v.VertexType != JunctionVertex) {
|
||||
return V
|
||||
}
|
||||
|
||||
@ -400,8 +387,8 @@ func (g *Graph) makeView() {
|
||||
V.In = append(V.In, e)
|
||||
}
|
||||
|
||||
if v.VertexType == Value {
|
||||
g.byVal[identify(v.val)] = V
|
||||
if v.VertexType == ValueVertex {
|
||||
g.byVal[v.val.ID] = V
|
||||
}
|
||||
|
||||
return V
|
||||
@ -412,15 +399,15 @@ func (g *Graph) makeView() {
|
||||
}
|
||||
}
|
||||
|
||||
// Value returns the Value Vertex for the given value. If the Graph doesn't
|
||||
// contain a vertex for the value then nil is returned
|
||||
func (g *Graph) Value(val Identifier) *Vertex {
|
||||
// ValueVertex returns the Value Vertex for the given value. If the Graph
|
||||
// doesn't contain a vertex for the value then nil is returned
|
||||
func (g *Graph) ValueVertex(val Value) *Vertex {
|
||||
g.makeView()
|
||||
return g.byVal[identify(val)]
|
||||
return g.byVal[val.ID]
|
||||
}
|
||||
|
||||
// Values returns all Value Vertices in the Graph
|
||||
func (g *Graph) Values() []*Vertex {
|
||||
// ValueVertices returns all Value Vertices in the Graph
|
||||
func (g *Graph) ValueVertices() []*Vertex {
|
||||
g.makeView()
|
||||
vv := make([]*Vertex, 0, len(g.byVal))
|
||||
for _, v := range g.byVal {
|
||||
|
513
gg/gg_test.go
513
gg/gg_test.go
@ -1,44 +1,30 @@
|
||||
package gg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash"
|
||||
. "testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type idAny struct {
|
||||
i interface{}
|
||||
func edge(val Value, from *Vertex) Edge {
|
||||
return Edge{Value: val, From: from}
|
||||
}
|
||||
|
||||
func (i idAny) Identify(h hash.Hash) {
|
||||
fmt.Fprintln(h, i)
|
||||
}
|
||||
|
||||
func id(i interface{}) Identifier {
|
||||
return idAny{i: i}
|
||||
}
|
||||
|
||||
func edge(val string, from *Vertex) Edge {
|
||||
return Edge{Value: id(val), From: from}
|
||||
}
|
||||
|
||||
func value(val string, in ...Edge) *Vertex {
|
||||
func value(val Value, in ...Edge) *Vertex {
|
||||
return &Vertex{
|
||||
VertexType: Value,
|
||||
Value: id(val),
|
||||
VertexType: ValueVertex,
|
||||
Value: val,
|
||||
In: in,
|
||||
}
|
||||
}
|
||||
|
||||
func junction(val string, in ...Edge) Edge {
|
||||
func junction(val Value, in ...Edge) Edge {
|
||||
return Edge{
|
||||
From: &Vertex{
|
||||
VertexType: Junction,
|
||||
VertexType: JunctionVertex,
|
||||
In: in,
|
||||
},
|
||||
Value: id(val),
|
||||
Value: val,
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +60,7 @@ func assertWalk(t *T, expVals, expJuncs int, g *Graph, msgAndArgs ...interface{}
|
||||
g.Walk(nil, func(v *Vertex) bool {
|
||||
assert.NotContains(t, seen, v, msgAndArgs...)
|
||||
seen[v] = true
|
||||
if v.VertexType == Value {
|
||||
if v.VertexType == ValueVertex {
|
||||
gotVals++
|
||||
} else {
|
||||
gotJuncs++
|
||||
@ -102,122 +88,140 @@ func mkTest(name string, out func() *Graph, numVals, numJuncs int, exp ...*Verte
|
||||
}
|
||||
|
||||
func TestGraph(t *T) {
|
||||
var (
|
||||
v0 = NewValue("v0")
|
||||
v1 = NewValue("v1")
|
||||
v2 = NewValue("v2")
|
||||
v3 = NewValue("v3")
|
||||
e0 = NewValue("e0")
|
||||
e00 = NewValue("e00")
|
||||
e01 = NewValue("e01")
|
||||
e1 = NewValue("e1")
|
||||
e10 = NewValue("e10")
|
||||
e11 = NewValue("e11")
|
||||
e2 = NewValue("e2")
|
||||
e20 = NewValue("e20")
|
||||
e21 = NewValue("e21")
|
||||
ej0 = NewValue("ej0")
|
||||
ej1 = NewValue("ej1")
|
||||
ej2 = NewValue("ej2")
|
||||
)
|
||||
tests := []graphTest{
|
||||
mkTest(
|
||||
"values-basic",
|
||||
func() *Graph {
|
||||
return Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||
return Null.AddValueIn(ValueOut(v0, e0), v1)
|
||||
},
|
||||
2, 0,
|
||||
value("v0"),
|
||||
value("v1", edge("e0", value("v0"))),
|
||||
value(v0),
|
||||
value(v1, edge(e0, value(v0))),
|
||||
),
|
||||
|
||||
mkTest(
|
||||
"values-2edges",
|
||||
func() *Graph {
|
||||
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v2"))
|
||||
return g0.AddValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
||||
g0 := Null.AddValueIn(ValueOut(v0, e0), v2)
|
||||
return g0.AddValueIn(ValueOut(v1, e1), v2)
|
||||
},
|
||||
3, 0,
|
||||
value("v0"),
|
||||
value("v1"),
|
||||
value("v2",
|
||||
edge("e0", value("v0")),
|
||||
edge("e1", value("v1")),
|
||||
value(v0),
|
||||
value(v1),
|
||||
value(v2,
|
||||
edge(e0, value(v0)),
|
||||
edge(e1, value(v1)),
|
||||
),
|
||||
),
|
||||
|
||||
mkTest(
|
||||
"values-separate",
|
||||
func() *Graph {
|
||||
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||
return g0.AddValueIn(ValueOut(id("v2"), id("e2")), id("v3"))
|
||||
g0 := Null.AddValueIn(ValueOut(v0, e0), v1)
|
||||
return g0.AddValueIn(ValueOut(v2, e2), v3)
|
||||
},
|
||||
4, 0,
|
||||
value("v0"),
|
||||
value("v1", edge("e0", value("v0"))),
|
||||
value("v2"),
|
||||
value("v3", edge("e2", value("v2"))),
|
||||
value(v0),
|
||||
value(v1, edge(e0, value(v0))),
|
||||
value(v2),
|
||||
value(v3, edge(e2, value(v2))),
|
||||
),
|
||||
|
||||
mkTest(
|
||||
"values-circular",
|
||||
func() *Graph {
|
||||
return Null.AddValueIn(ValueOut(id("v0"), id("e")), id("v0"))
|
||||
return Null.AddValueIn(ValueOut(v0, e0), v0)
|
||||
},
|
||||
1, 0,
|
||||
value("v0", edge("e", value("v0"))),
|
||||
value(v0, edge(e0, value(v0))),
|
||||
),
|
||||
|
||||
mkTest(
|
||||
"values-circular2",
|
||||
func() *Graph {
|
||||
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||
return g0.AddValueIn(ValueOut(id("v1"), id("e1")), id("v0"))
|
||||
g0 := Null.AddValueIn(ValueOut(v0, e0), v1)
|
||||
return g0.AddValueIn(ValueOut(v1, e1), v0)
|
||||
},
|
||||
2, 0,
|
||||
value("v0", edge("e1", value("v1", edge("e0", value("v0"))))),
|
||||
value("v1", edge("e0", value("v0", edge("e1", value("v1"))))),
|
||||
value(v0, edge(e1, value(v1, edge(e0, value(v0))))),
|
||||
value(v1, edge(e0, value(v0, edge(e1, value(v1))))),
|
||||
),
|
||||
|
||||
mkTest(
|
||||
"values-circular3",
|
||||
func() *Graph {
|
||||
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||
g1 := g0.AddValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
||||
return g1.AddValueIn(ValueOut(id("v2"), id("e2")), id("v1"))
|
||||
g0 := Null.AddValueIn(ValueOut(v0, e0), v1)
|
||||
g1 := g0.AddValueIn(ValueOut(v1, e1), v2)
|
||||
return g1.AddValueIn(ValueOut(v2, e2), v1)
|
||||
},
|
||||
3, 0,
|
||||
value("v0"),
|
||||
value("v1",
|
||||
edge("e0", value("v0")),
|
||||
edge("e2", value("v2", edge("e1", value("v1")))),
|
||||
value(v0),
|
||||
value(v1,
|
||||
edge(e0, value(v0)),
|
||||
edge(e2, value(v2, edge(e1, value(v1)))),
|
||||
),
|
||||
value("v2", edge("e1", value("v1",
|
||||
edge("e0", value("v0")),
|
||||
edge("e2", value("v2")),
|
||||
value(v2, edge(e1, value(v1,
|
||||
edge(e0, value(v0)),
|
||||
edge(e2, value(v2)),
|
||||
))),
|
||||
),
|
||||
|
||||
mkTest(
|
||||
"junction-basic",
|
||||
func() *Graph {
|
||||
e0 := ValueOut(id("v0"), id("e0"))
|
||||
e1 := ValueOut(id("v1"), id("e1"))
|
||||
ej0 := JunctionOut([]OpenEdge{e0, e1}, id("ej0"))
|
||||
return Null.AddValueIn(ej0, id("v2"))
|
||||
e0 := ValueOut(v0, e0)
|
||||
e1 := ValueOut(v1, e1)
|
||||
ej0 := JunctionOut([]OpenEdge{e0, e1}, ej0)
|
||||
return Null.AddValueIn(ej0, v2)
|
||||
},
|
||||
3, 1,
|
||||
value("v0"), value("v1"),
|
||||
value("v2", junction("ej0",
|
||||
edge("e0", value("v0")),
|
||||
edge("e1", value("v1")),
|
||||
value(v0), value(v1),
|
||||
value(v2, junction(ej0,
|
||||
edge(e0, value(v0)),
|
||||
edge(e1, value(v1)),
|
||||
)),
|
||||
),
|
||||
|
||||
mkTest(
|
||||
"junction-basic2",
|
||||
func() *Graph {
|
||||
e00 := ValueOut(id("v0"), id("e00"))
|
||||
e10 := ValueOut(id("v1"), id("e10"))
|
||||
ej0 := JunctionOut([]OpenEdge{e00, e10}, id("ej0"))
|
||||
e01 := ValueOut(id("v0"), id("e01"))
|
||||
e11 := ValueOut(id("v1"), id("e11"))
|
||||
ej1 := JunctionOut([]OpenEdge{e01, e11}, id("ej1"))
|
||||
ej2 := JunctionOut([]OpenEdge{ej0, ej1}, id("ej2"))
|
||||
return Null.AddValueIn(ej2, id("v2"))
|
||||
e00 := ValueOut(v0, e00)
|
||||
e10 := ValueOut(v1, e10)
|
||||
ej0 := JunctionOut([]OpenEdge{e00, e10}, ej0)
|
||||
e01 := ValueOut(v0, e01)
|
||||
e11 := ValueOut(v1, e11)
|
||||
ej1 := JunctionOut([]OpenEdge{e01, e11}, ej1)
|
||||
ej2 := JunctionOut([]OpenEdge{ej0, ej1}, ej2)
|
||||
return Null.AddValueIn(ej2, v2)
|
||||
},
|
||||
3, 3,
|
||||
value("v0"), value("v1"),
|
||||
value("v2", junction("ej2",
|
||||
junction("ej0",
|
||||
edge("e00", value("v0")),
|
||||
edge("e10", value("v1")),
|
||||
value(v0), value(v1),
|
||||
value(v2, junction(ej2,
|
||||
junction(ej0,
|
||||
edge(e00, value(v0)),
|
||||
edge(e10, value(v1)),
|
||||
),
|
||||
junction("ej1",
|
||||
edge("e01", value("v0")),
|
||||
edge("e11", value("v1")),
|
||||
junction(ej1,
|
||||
edge(e01, value(v0)),
|
||||
edge(e11, value(v1)),
|
||||
),
|
||||
)),
|
||||
),
|
||||
@ -225,27 +229,27 @@ func TestGraph(t *T) {
|
||||
mkTest(
|
||||
"junction-circular",
|
||||
func() *Graph {
|
||||
e0 := ValueOut(id("v0"), id("e0"))
|
||||
e1 := ValueOut(id("v1"), id("e1"))
|
||||
ej0 := JunctionOut([]OpenEdge{e0, e1}, id("ej0"))
|
||||
g0 := Null.AddValueIn(ej0, id("v2"))
|
||||
e20 := ValueOut(id("v2"), id("e20"))
|
||||
g1 := g0.AddValueIn(e20, id("v0"))
|
||||
e21 := ValueOut(id("v2"), id("e21"))
|
||||
return g1.AddValueIn(e21, id("v1"))
|
||||
e0 := ValueOut(v0, e0)
|
||||
e1 := ValueOut(v1, e1)
|
||||
ej0 := JunctionOut([]OpenEdge{e0, e1}, ej0)
|
||||
g0 := Null.AddValueIn(ej0, v2)
|
||||
e20 := ValueOut(v2, e20)
|
||||
g1 := g0.AddValueIn(e20, v0)
|
||||
e21 := ValueOut(v2, e21)
|
||||
return g1.AddValueIn(e21, v1)
|
||||
},
|
||||
3, 1,
|
||||
value("v0", edge("e20", value("v2", junction("ej0",
|
||||
edge("e0", value("v0")),
|
||||
edge("e1", value("v1", edge("e21", value("v2")))),
|
||||
value(v0, edge(e20, value(v2, junction(ej0,
|
||||
edge(e0, value(v0)),
|
||||
edge(e1, value(v1, edge(e21, value(v2)))),
|
||||
)))),
|
||||
value("v1", edge("e21", value("v2", junction("ej0",
|
||||
edge("e0", value("v0", edge("e20", value("v2")))),
|
||||
edge("e1", value("v1")),
|
||||
value(v1, edge(e21, value(v2, junction(ej0,
|
||||
edge(e0, value(v0, edge(e20, value(v2)))),
|
||||
edge(e1, value(v1)),
|
||||
)))),
|
||||
value("v2", junction("ej0",
|
||||
edge("e0", value("v0", edge("e20", value("v2")))),
|
||||
edge("e1", value("v1", edge("e21", value("v2")))),
|
||||
value(v2, junction(ej0,
|
||||
edge(e0, value(v0, edge(e20, value(v2)))),
|
||||
edge(e1, value(v1, edge(e21, value(v2)))),
|
||||
)),
|
||||
),
|
||||
}
|
||||
@ -256,9 +260,9 @@ func TestGraph(t *T) {
|
||||
for j, exp := range tests[i].exp {
|
||||
msgAndArgs := []interface{}{
|
||||
"tests[%d].name:%q exp[%d].val:%q",
|
||||
i, tests[i].name, j, exp.Value.(idAny).i,
|
||||
i, tests[i].name, j, exp.Value.V.(string),
|
||||
}
|
||||
v := out.Value(exp.Value)
|
||||
v := out.ValueVertex(exp.Value)
|
||||
if !assert.NotNil(t, v, msgAndArgs...) {
|
||||
continue
|
||||
}
|
||||
@ -279,109 +283,127 @@ func TestGraph(t *T) {
|
||||
}
|
||||
|
||||
func TestGraphImmutability(t *T) {
|
||||
e0 := ValueOut(id("v0"), id("e0"))
|
||||
g0 := Null.AddValueIn(e0, id("v1"))
|
||||
assert.Nil(t, Null.Value(id("v0")))
|
||||
assert.Nil(t, Null.Value(id("v1")))
|
||||
assert.NotNil(t, g0.Value(id("v0")))
|
||||
assert.NotNil(t, g0.Value(id("v1")))
|
||||
v0 := NewValue("v0")
|
||||
v1 := NewValue("v1")
|
||||
e0 := NewValue("e0")
|
||||
oe0 := ValueOut(v0, e0)
|
||||
g0 := Null.AddValueIn(oe0, v1)
|
||||
assert.Nil(t, Null.ValueVertex(v0))
|
||||
assert.Nil(t, Null.ValueVertex(v1))
|
||||
assert.NotNil(t, g0.ValueVertex(v0))
|
||||
assert.NotNil(t, g0.ValueVertex(v1))
|
||||
|
||||
// half-edges should be re-usable
|
||||
e1 := ValueOut(id("v2"), id("e1"))
|
||||
g1a := g0.AddValueIn(e1, id("v3a"))
|
||||
g1b := g0.AddValueIn(e1, id("v3b"))
|
||||
assertVertexEqual(t, value("v3a", edge("e1", value("v2"))), g1a.Value(id("v3a")))
|
||||
assert.Nil(t, g1a.Value(id("v3b")))
|
||||
assertVertexEqual(t, value("v3b", edge("e1", value("v2"))), g1b.Value(id("v3b")))
|
||||
assert.Nil(t, g1b.Value(id("v3a")))
|
||||
v2 := NewValue("v2")
|
||||
v3a, v3b := NewValue("v3a"), NewValue("v3b")
|
||||
e1 := NewValue("e1")
|
||||
oe1 := ValueOut(v2, e1)
|
||||
g1a := g0.AddValueIn(oe1, v3a)
|
||||
g1b := g0.AddValueIn(oe1, v3b)
|
||||
assertVertexEqual(t, value(v3a, edge(e1, value(v2))), g1a.ValueVertex(v3a))
|
||||
assert.Nil(t, g1a.ValueVertex(v3b))
|
||||
assertVertexEqual(t, value(v3b, edge(e1, value(v2))), g1b.ValueVertex(v3b))
|
||||
assert.Nil(t, g1b.ValueVertex(v3a))
|
||||
|
||||
// ... even re-usable twice in succession
|
||||
g2 := g0.AddValueIn(e1, id("v3")).AddValueIn(e1, id("v4"))
|
||||
assert.Nil(t, g2.Value(id("v3b")))
|
||||
assert.Nil(t, g2.Value(id("v3a")))
|
||||
assertVertexEqual(t, value("v3", edge("e1", value("v2"))), g2.Value(id("v3")))
|
||||
assertVertexEqual(t, value("v4", edge("e1", value("v2"))), g2.Value(id("v4")))
|
||||
v3 := NewValue("v3")
|
||||
v4 := NewValue("v4")
|
||||
g2 := g0.AddValueIn(oe1, v3).AddValueIn(oe1, v4)
|
||||
assert.Nil(t, g2.ValueVertex(v3b))
|
||||
assert.Nil(t, g2.ValueVertex(v3a))
|
||||
assertVertexEqual(t, value(v3, edge(e1, value(v2))), g2.ValueVertex(v3))
|
||||
assertVertexEqual(t, value(v4, edge(e1, value(v2))), g2.ValueVertex(v4))
|
||||
}
|
||||
|
||||
func TestGraphDelValueIn(t *T) {
|
||||
v0 := NewValue("v0")
|
||||
v1 := NewValue("v1")
|
||||
e0 := NewValue("e0")
|
||||
{ // removing from null
|
||||
g := Null.DelValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||
g := Null.DelValueIn(ValueOut(v0, e0), v1)
|
||||
assert.True(t, Equal(Null, g))
|
||||
}
|
||||
|
||||
e1 := NewValue("e1")
|
||||
{ // 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"))
|
||||
g0 := Null.AddValueIn(ValueOut(v0, e0), v1)
|
||||
g1 := g0.DelValueIn(ValueOut(v0, e1), v1)
|
||||
assert.True(t, Equal(g0, g1))
|
||||
}
|
||||
|
||||
{ // removing only edge
|
||||
oe := ValueOut(id("v0"), id("e0"))
|
||||
g0 := Null.AddValueIn(oe, id("v1"))
|
||||
g1 := g0.DelValueIn(oe, id("v1"))
|
||||
oe := ValueOut(v0, e0)
|
||||
g0 := Null.AddValueIn(oe, v1)
|
||||
g1 := g0.DelValueIn(oe, v1)
|
||||
assert.True(t, Equal(Null, g1))
|
||||
}
|
||||
|
||||
ej0 := NewValue("ej0")
|
||||
v2 := NewValue("v2")
|
||||
{ // removing only edge (junction)
|
||||
oe := JunctionOut([]OpenEdge{
|
||||
ValueOut(id("v0"), id("e0")),
|
||||
ValueOut(id("v1"), id("e1")),
|
||||
}, id("ej0"))
|
||||
g0 := Null.AddValueIn(oe, id("v2"))
|
||||
g1 := g0.DelValueIn(oe, id("v2"))
|
||||
ValueOut(v0, e0),
|
||||
ValueOut(v1, e1),
|
||||
}, ej0)
|
||||
g0 := Null.AddValueIn(oe, v2)
|
||||
g1 := g0.DelValueIn(oe, v2)
|
||||
assert.True(t, Equal(Null, g1))
|
||||
}
|
||||
|
||||
{ // removing one of two edges
|
||||
oe := ValueOut(id("v1"), id("e0"))
|
||||
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v2"))
|
||||
g1 := g0.AddValueIn(oe, id("v2"))
|
||||
g2 := g1.DelValueIn(oe, id("v2"))
|
||||
oe := ValueOut(v1, e0)
|
||||
g0 := Null.AddValueIn(ValueOut(v0, e0), v2)
|
||||
g1 := g0.AddValueIn(oe, v2)
|
||||
g2 := g1.DelValueIn(oe, 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")))
|
||||
assert.NotNil(t, g2.ValueVertex(v0))
|
||||
assert.Nil(t, g2.ValueVertex(v1))
|
||||
assert.NotNil(t, g2.ValueVertex(v2))
|
||||
}
|
||||
|
||||
e2 := NewValue("e2")
|
||||
eja, ejb := NewValue("eja"), NewValue("ejb")
|
||||
v3 := NewValue("v3")
|
||||
{ // removing one of two edges (junction)
|
||||
e0 := ValueOut(id("v0"), id("e0"))
|
||||
e1 := ValueOut(id("v1"), id("e1"))
|
||||
e2 := ValueOut(id("v2"), id("e2"))
|
||||
oeA := JunctionOut([]OpenEdge{e0, e1}, id("oeA"))
|
||||
oeB := JunctionOut([]OpenEdge{e1, e2}, id("oeB"))
|
||||
g0a := Null.AddValueIn(oeA, id("v3"))
|
||||
g0b := Null.AddValueIn(oeB, id("v3"))
|
||||
g1 := g0a.Union(g0b).DelValueIn(oeA, id("v3"))
|
||||
e0 := ValueOut(v0, e0)
|
||||
e1 := ValueOut(v1, e1)
|
||||
e2 := ValueOut(v2, e2)
|
||||
oeA := JunctionOut([]OpenEdge{e0, e1}, eja)
|
||||
oeB := JunctionOut([]OpenEdge{e1, e2}, ejb)
|
||||
g0a := Null.AddValueIn(oeA, v3)
|
||||
g0b := Null.AddValueIn(oeB, v3)
|
||||
g1 := g0a.Union(g0b).DelValueIn(oeA, 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")))
|
||||
assert.Nil(t, g1.ValueVertex(v0))
|
||||
assert.NotNil(t, g1.ValueVertex(v1))
|
||||
assert.NotNil(t, g1.ValueVertex(v2))
|
||||
assert.NotNil(t, g1.ValueVertex(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")))
|
||||
e0 := ValueOut(v0, e0)
|
||||
e1 := ValueOut(v1, e1)
|
||||
g0 := Null.AddValueIn(e0, v1).AddValueIn(e1, v0)
|
||||
g1 := g0.DelValueIn(e0, v1)
|
||||
assert.True(t, Equal(Null.AddValueIn(e1, v0), g1))
|
||||
assert.NotNil(t, g1.ValueVertex(v0))
|
||||
assert.NotNil(t, g1.ValueVertex(v1))
|
||||
}
|
||||
|
||||
ej := NewValue("ej")
|
||||
{ // removing to's only edge, sub-nodes have edge to each other
|
||||
ej := JunctionOut([]OpenEdge{
|
||||
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")))
|
||||
oej := JunctionOut([]OpenEdge{
|
||||
ValueOut(v0, ej0),
|
||||
ValueOut(v1, ej0),
|
||||
}, ej)
|
||||
g0 := Null.AddValueIn(oej, v2)
|
||||
e0 := ValueOut(v0, e0)
|
||||
g1 := g0.AddValueIn(e0, v1)
|
||||
g2 := g1.DelValueIn(oej, v2)
|
||||
assert.True(t, Equal(Null.AddValueIn(e0, v1), g2))
|
||||
assert.NotNil(t, g2.ValueVertex(v0))
|
||||
assert.NotNil(t, g2.ValueVertex(v1))
|
||||
assert.Nil(t, g2.ValueVertex(v2))
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,110 +415,124 @@ func TestGraphUnion(t *T) {
|
||||
return ga
|
||||
}
|
||||
|
||||
v0 := NewValue("v0")
|
||||
v1 := NewValue("v1")
|
||||
e0 := NewValue("e0")
|
||||
{ // Union with Null
|
||||
assert.True(t, Equal(Null, Null.Union(Null)))
|
||||
|
||||
g := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||
g := Null.AddValueIn(ValueOut(v0, e0), v1)
|
||||
assert.True(t, Equal(g, assertUnion(g, Null)))
|
||||
}
|
||||
|
||||
v2 := NewValue("v2")
|
||||
v3 := NewValue("v3")
|
||||
e1 := NewValue("e1")
|
||||
{ // 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"))
|
||||
g0 := Null.AddValueIn(ValueOut(v0, e0), v1)
|
||||
g1 := Null.AddValueIn(ValueOut(v2, e1), 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")))
|
||||
assertVertexEqual(t, value(v0), g.ValueVertex(v0))
|
||||
assertVertexEqual(t, value(v1, edge(e0, value(v0))), g.ValueVertex(v1))
|
||||
assertVertexEqual(t, value(v2), g.ValueVertex(v2))
|
||||
assertVertexEqual(t, value(v3, edge(e1, value(v2))), g.ValueVertex(v3))
|
||||
}
|
||||
|
||||
va0, vb0 := NewValue("va0"), NewValue("vb0")
|
||||
va1, vb1 := NewValue("va1"), NewValue("vb1")
|
||||
va2, vb2 := NewValue("va2"), NewValue("vb2")
|
||||
ea0, eb0 := NewValue("ea0"), NewValue("eb0")
|
||||
ea1, eb1 := NewValue("ea1"), NewValue("eb1")
|
||||
eaj, ebj := NewValue("eaj"), NewValue("ebj")
|
||||
{ // Two disparate graphs with junctions
|
||||
ga := Null.AddValueIn(JunctionOut([]OpenEdge{
|
||||
ValueOut(id("va0"), id("ea0")),
|
||||
ValueOut(id("va1"), id("ea1")),
|
||||
}, id("eaj")), id("va2"))
|
||||
ValueOut(va0, ea0),
|
||||
ValueOut(va1, ea1),
|
||||
}, eaj), va2)
|
||||
gb := Null.AddValueIn(JunctionOut([]OpenEdge{
|
||||
ValueOut(id("vb0"), id("eb0")),
|
||||
ValueOut(id("vb1"), id("eb1")),
|
||||
}, id("ebj")), id("vb2"))
|
||||
ValueOut(vb0, eb0),
|
||||
ValueOut(vb1, eb1),
|
||||
}, ebj), vb2)
|
||||
g := assertUnion(ga, gb)
|
||||
assertVertexEqual(t, value("va0"), g.Value(id("va0")))
|
||||
assertVertexEqual(t, value("va1"), g.Value(id("va1")))
|
||||
assertVertexEqual(t, value(va0), g.ValueVertex(va0))
|
||||
assertVertexEqual(t, value(va1), g.ValueVertex(va1))
|
||||
assertVertexEqual(t,
|
||||
value("va2", junction("eaj",
|
||||
edge("ea0", value("va0")),
|
||||
edge("ea1", value("va1")))),
|
||||
g.Value(id("va2")),
|
||||
value(va2, junction(eaj,
|
||||
edge(ea0, value(va0)),
|
||||
edge(ea1, value(va1)))),
|
||||
g.ValueVertex(va2),
|
||||
)
|
||||
assertVertexEqual(t, value("vb0"), g.Value(id("vb0")))
|
||||
assertVertexEqual(t, value("vb1"), g.Value(id("vb1")))
|
||||
assertVertexEqual(t, value(vb0), g.ValueVertex(vb0))
|
||||
assertVertexEqual(t, value(vb1), g.ValueVertex(vb1))
|
||||
assertVertexEqual(t,
|
||||
value("vb2", junction("ebj",
|
||||
edge("eb0", value("vb0")),
|
||||
edge("eb1", value("vb1")))),
|
||||
g.Value(id("vb2")),
|
||||
value(vb2, junction(ebj,
|
||||
edge(eb0, value(vb0)),
|
||||
edge(eb1, value(vb1)))),
|
||||
g.ValueVertex(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"))
|
||||
g0 := Null.AddValueIn(ValueOut(v0, e0), v2)
|
||||
g1 := Null.AddValueIn(ValueOut(v1, e1), v2)
|
||||
g := assertUnion(g0, g1)
|
||||
assertVertexEqual(t, value("v0"), g.Value(id("v0")))
|
||||
assertVertexEqual(t, value("v1"), g.Value(id("v1")))
|
||||
assertVertexEqual(t, value(v0), g.ValueVertex(v0))
|
||||
assertVertexEqual(t, value(v1), g.ValueVertex(v1))
|
||||
assertVertexEqual(t,
|
||||
value("v2",
|
||||
edge("e0", value("v0")),
|
||||
edge("e1", value("v1")),
|
||||
value(v2,
|
||||
edge(e0, value(v0)),
|
||||
edge(e1, value(v1)),
|
||||
),
|
||||
g.Value(id("v2")),
|
||||
g.ValueVertex(v2),
|
||||
)
|
||||
}
|
||||
|
||||
ej0 := NewValue("ej0")
|
||||
ej1 := NewValue("ej1")
|
||||
{ // two partially overlapping graphs with junctions
|
||||
g0 := Null.AddValueIn(JunctionOut([]OpenEdge{
|
||||
ValueOut(id("v0"), id("e0")),
|
||||
ValueOut(id("v1"), id("e1")),
|
||||
}, id("ej0")), id("v2"))
|
||||
ValueOut(v0, e0),
|
||||
ValueOut(v1, e1),
|
||||
}, ej0), v2)
|
||||
g1 := Null.AddValueIn(JunctionOut([]OpenEdge{
|
||||
ValueOut(id("v0"), id("e0")),
|
||||
ValueOut(id("v1"), id("e1")),
|
||||
}, id("ej1")), id("v2"))
|
||||
ValueOut(v0, e0),
|
||||
ValueOut(v1, e1),
|
||||
}, ej1), v2)
|
||||
g := assertUnion(g0, g1)
|
||||
assertVertexEqual(t, value("v0"), g.Value(id("v0")))
|
||||
assertVertexEqual(t, value("v1"), g.Value(id("v1")))
|
||||
assertVertexEqual(t, value(v0), g.ValueVertex(v0))
|
||||
assertVertexEqual(t, value(v1), g.ValueVertex(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"))),
|
||||
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")),
|
||||
g.ValueVertex(v2),
|
||||
)
|
||||
}
|
||||
|
||||
{ // Two equal graphs
|
||||
g0 := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||
g0 := Null.AddValueIn(ValueOut(v0, e0), v1)
|
||||
g := assertUnion(g0, g0)
|
||||
assertVertexEqual(t, value("v0"), g.Value(id("v0")))
|
||||
assertVertexEqual(t, value(v0), g.ValueVertex(v0))
|
||||
assertVertexEqual(t,
|
||||
value("v1", edge("e0", value("v0"))),
|
||||
g.Value(id("v1")),
|
||||
value(v1, edge(e0, value(v0))),
|
||||
g.ValueVertex(v1),
|
||||
)
|
||||
}
|
||||
|
||||
{ // Two equal graphs with junctions
|
||||
g0 := Null.AddValueIn(JunctionOut([]OpenEdge{
|
||||
ValueOut(id("v0"), id("e0")),
|
||||
ValueOut(id("v1"), id("e1")),
|
||||
}, id("ej0")), id("v2"))
|
||||
ValueOut(v0, e0),
|
||||
ValueOut(v1, e1),
|
||||
}, ej0), v2)
|
||||
g := assertUnion(g0, g0)
|
||||
assertVertexEqual(t, value("v0"), g.Value(id("v0")))
|
||||
assertVertexEqual(t, value("v1"), g.Value(id("v1")))
|
||||
assertVertexEqual(t, value(v0), g.ValueVertex(v0))
|
||||
assertVertexEqual(t, value(v1), g.ValueVertex(v1))
|
||||
assertVertexEqual(t,
|
||||
value("v2",
|
||||
junction("ej0", edge("e0", value("v0")), edge("e1", value("v1"))),
|
||||
value(v2,
|
||||
junction(ej0, edge(e0, value(v0)), edge(e1, value(v1))),
|
||||
),
|
||||
g.Value(id("v2")),
|
||||
g.ValueVertex(v2),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -514,35 +550,42 @@ func TestGraphEqual(t *T) {
|
||||
|
||||
assertEqual(Null, Null) // duh
|
||||
|
||||
v0 := NewValue("v0")
|
||||
v1 := NewValue("v1")
|
||||
v2 := NewValue("v2")
|
||||
e0 := NewValue("e0")
|
||||
e1 := NewValue("e1")
|
||||
e1a, e1b := NewValue("e1a"), NewValue("e1b")
|
||||
{
|
||||
// graph is equal to itself, not to null
|
||||
e0 := ValueOut(id("v0"), id("e0"))
|
||||
g0 := Null.AddValueIn(e0, id("v1"))
|
||||
e0 := ValueOut(v0, e0)
|
||||
g0 := Null.AddValueIn(e0, v1)
|
||||
assertNotEqual(g0, Null)
|
||||
assertEqual(g0, g0)
|
||||
|
||||
// adding the an existing edge again shouldn't do anything
|
||||
assertEqual(g0, g0.AddValueIn(e0, id("v1")))
|
||||
assertEqual(g0, g0.AddValueIn(e0, v1))
|
||||
|
||||
// g1a and g1b have the same vertices, but the edges are different
|
||||
g1a := g0.AddValueIn(ValueOut(id("v0"), id("e1a")), id("v2"))
|
||||
g1b := g0.AddValueIn(ValueOut(id("v0"), id("e1b")), id("v2"))
|
||||
g1a := g0.AddValueIn(ValueOut(v0, e1a), v2)
|
||||
g1b := g0.AddValueIn(ValueOut(v0, e1b), v2)
|
||||
assertNotEqual(g1a, g1b)
|
||||
}
|
||||
|
||||
{ // equal construction should yield equality, even if out of order
|
||||
ga := Null.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||
ga = ga.AddValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
||||
gb := Null.AddValueIn(ValueOut(id("v1"), id("e1")), id("v2"))
|
||||
gb = gb.AddValueIn(ValueOut(id("v0"), id("e0")), id("v1"))
|
||||
ga := Null.AddValueIn(ValueOut(v0, e0), v1)
|
||||
ga = ga.AddValueIn(ValueOut(v1, e1), v2)
|
||||
gb := Null.AddValueIn(ValueOut(v1, e1), v2)
|
||||
gb = gb.AddValueIn(ValueOut(v0, e0), v1)
|
||||
assertEqual(ga, gb)
|
||||
}
|
||||
|
||||
ej := NewValue("ej")
|
||||
{ // junction basic test
|
||||
e0 := ValueOut(id("v0"), id("e0"))
|
||||
e1 := ValueOut(id("v1"), id("e1"))
|
||||
ga := Null.AddValueIn(JunctionOut([]OpenEdge{e0, e1}, id("ej")), id("v2"))
|
||||
gb := Null.AddValueIn(JunctionOut([]OpenEdge{e1, e0}, id("ej")), id("v2"))
|
||||
e0 := ValueOut(v0, e0)
|
||||
e1 := ValueOut(v1, e1)
|
||||
ga := Null.AddValueIn(JunctionOut([]OpenEdge{e0, e1}, ej), v2)
|
||||
gb := Null.AddValueIn(JunctionOut([]OpenEdge{e1, e0}, ej), v2)
|
||||
assertEqual(ga, ga)
|
||||
assertNotEqual(ga, gb)
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ func boxFromVertex(v *gg.Vertex, flowDir geo.XY) box {
|
||||
numIn: len(v.In),
|
||||
numOut: len(v.Out),
|
||||
}
|
||||
if v.VertexType == gg.Value {
|
||||
b.body = string(v.Value.(gg.Str))
|
||||
if v.VertexType == gg.ValueVertex {
|
||||
b.body = v.Value.V.(string)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
@ -17,28 +17,38 @@ type Constraint struct {
|
||||
LT string
|
||||
}
|
||||
|
||||
const ltEdge = gg.Str("lt")
|
||||
var ltEdge = gg.NewValue("lt")
|
||||
|
||||
// Engine processes sets of constraints to generate an output
|
||||
type Engine struct {
|
||||
g *gg.Graph
|
||||
vals map[string]gg.Value
|
||||
}
|
||||
|
||||
// NewEngine initializes and returns an empty Engine
|
||||
func NewEngine() *Engine {
|
||||
return &Engine{g: gg.Null}
|
||||
return &Engine{g: gg.Null, vals: map[string]gg.Value{}}
|
||||
}
|
||||
|
||||
func (e *Engine) getVal(elem string) gg.Value {
|
||||
if val, ok := e.vals[elem]; ok {
|
||||
return val
|
||||
}
|
||||
val := gg.NewValue(elem)
|
||||
e.vals[elem] = val
|
||||
return val
|
||||
}
|
||||
|
||||
// AddConstraint adds the given constraint to the engine's set, returns false if
|
||||
// the constraint couldn't be added due to a conflict with a previous constraint
|
||||
func (e *Engine) AddConstraint(c Constraint) bool {
|
||||
elem := gg.Str(c.Elem)
|
||||
g := e.g.AddValueIn(gg.ValueOut(elem, ltEdge), gg.Str(c.LT))
|
||||
elem := e.getVal(c.Elem)
|
||||
g := e.g.AddValueIn(gg.ValueOut(elem, ltEdge), e.getVal(c.LT))
|
||||
|
||||
// Check for loops in g starting at c.Elem, bail if there are any
|
||||
{
|
||||
seen := map[*gg.Vertex]bool{}
|
||||
start := g.Value(elem)
|
||||
start := g.ValueVertex(elem)
|
||||
var hasLoop func(v *gg.Vertex) bool
|
||||
hasLoop = func(v *gg.Vertex) bool {
|
||||
if seen[v] {
|
||||
@ -66,12 +76,12 @@ func (e *Engine) AddConstraint(c Constraint) bool {
|
||||
// engine and whose value is known to be zero.
|
||||
func (e *Engine) Solve() map[string]int {
|
||||
m := map[string]int{}
|
||||
if len(e.g.Values()) == 0 {
|
||||
if len(e.g.ValueVertices()) == 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
vElem := func(v *gg.Vertex) string {
|
||||
return string(v.Value.(gg.Str))
|
||||
return v.Value.V.(string)
|
||||
}
|
||||
|
||||
// first the roots are determined to be the elements with no In edges, which
|
||||
|
48
gim/main.go
48
gim/main.go
@ -35,25 +35,36 @@ func debugf(str string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, str, args...)
|
||||
}
|
||||
|
||||
func mkGraph() *gg.Graph {
|
||||
aE0 := gg.ValueOut(gg.Str("a"), gg.Str("aE0"))
|
||||
aE1 := gg.ValueOut(gg.Str("a"), gg.Str("aE1"))
|
||||
aE2 := gg.ValueOut(gg.Str("a"), gg.Str("aE2"))
|
||||
aE3 := gg.ValueOut(gg.Str("a"), gg.Str("aE3"))
|
||||
func mkGraph() (*gg.Graph, gg.Value) {
|
||||
a := gg.NewValue("a")
|
||||
aE0 := gg.NewValue("aE0")
|
||||
aE1 := gg.NewValue("aE1")
|
||||
aE2 := gg.NewValue("aE2")
|
||||
aE3 := gg.NewValue("aE3")
|
||||
b0 := gg.NewValue("b0")
|
||||
b1 := gg.NewValue("b1")
|
||||
b2 := gg.NewValue("b2")
|
||||
b3 := gg.NewValue("b3")
|
||||
oaE0 := gg.ValueOut(a, aE0)
|
||||
oaE1 := gg.ValueOut(a, aE1)
|
||||
oaE2 := gg.ValueOut(a, aE2)
|
||||
oaE3 := gg.ValueOut(a, aE3)
|
||||
g := gg.Null
|
||||
g = g.AddValueIn(aE0, gg.Str("b0"))
|
||||
g = g.AddValueIn(aE1, gg.Str("b1"))
|
||||
g = g.AddValueIn(aE2, gg.Str("b2"))
|
||||
g = g.AddValueIn(aE3, gg.Str("b3"))
|
||||
g = g.AddValueIn(oaE0, b0)
|
||||
g = g.AddValueIn(oaE1, b1)
|
||||
g = g.AddValueIn(oaE2, b2)
|
||||
g = g.AddValueIn(oaE3, b3)
|
||||
|
||||
c := gg.NewValue("c")
|
||||
empty := gg.NewValue("")
|
||||
jE := gg.JunctionOut([]gg.OpenEdge{
|
||||
gg.ValueOut(gg.Str("b0"), gg.Str("")),
|
||||
gg.ValueOut(gg.Str("b1"), gg.Str("")),
|
||||
gg.ValueOut(gg.Str("b2"), gg.Str("")),
|
||||
gg.ValueOut(gg.Str("b3"), gg.Str("")),
|
||||
}, gg.Str("jE"))
|
||||
g = g.AddValueIn(jE, gg.Str("c"))
|
||||
return g
|
||||
gg.ValueOut(b0, empty),
|
||||
gg.ValueOut(b1, empty),
|
||||
gg.ValueOut(b2, empty),
|
||||
gg.ValueOut(b3, empty),
|
||||
}, gg.NewValue("jE"))
|
||||
g = g.AddValueIn(jE, c)
|
||||
return g, c
|
||||
}
|
||||
|
||||
//func mkGraph() *gg.Graph {
|
||||
@ -68,11 +79,12 @@ func main() {
|
||||
term.Reset()
|
||||
term.HideCursor()
|
||||
|
||||
g, start := mkGraph()
|
||||
v := view{
|
||||
g: mkGraph(),
|
||||
g: g,
|
||||
primFlowDir: geo.Right,
|
||||
secFlowDir: geo.Down,
|
||||
start: gg.Str("c"),
|
||||
start: start,
|
||||
center: geo.Zero.Midpoint(term.WindowSize(), rounder),
|
||||
}
|
||||
|
||||
|
@ -24,12 +24,12 @@ func posSolve(g *gg.Graph) [][]*gg.Vertex {
|
||||
secEng := constraint.NewEngine()
|
||||
|
||||
strM := g.ByID()
|
||||
for vID, v := range strM {
|
||||
for _, v := range strM {
|
||||
var prevIn *gg.Vertex
|
||||
for _, e := range v.In {
|
||||
primEng.AddConstraint(constraint.Constraint{
|
||||
Elem: e.From.ID,
|
||||
LT: vID,
|
||||
LT: v.ID,
|
||||
})
|
||||
if prevIn != nil {
|
||||
secEng.AddConstraint(constraint.Constraint{
|
||||
@ -103,7 +103,7 @@ func centerBoxes(boxes []*box, around geo.XY) {
|
||||
type view struct {
|
||||
g *gg.Graph
|
||||
primFlowDir, secFlowDir geo.XY
|
||||
start gg.Str
|
||||
start gg.Value
|
||||
center geo.XY
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user