@ -7,7 +7,9 @@ import (
"strings"
)
// Value is any value which can be stored within a Graph.
// Value is any value which can be stored within a Graph. Values should be
// considered immutable, ie once used with the graph package their internal
// value does not change.
type Value interface {
Equal ( Value ) bool
String ( ) string
@ -17,12 +19,12 @@ type Value interface {
// constructing graphs. It has no meaning on its own.
type OpenEdge [ V Value ] struct {
val * V
tup [ ] OpenEdge [ V ]
tup [ ] * OpenEdge [ V ]
edgeVal V
}
func ( oe OpenEdge [ V ] ) equal ( oe2 OpenEdge [ V ] ) bool {
func ( oe * OpenEdge [ V ] ) equal ( oe2 * OpenEdge [ V ] ) bool {
if ! oe . edgeVal . Equal ( oe2 . edgeVal ) {
return false
}
@ -44,7 +46,7 @@ func (oe OpenEdge[V]) equal(oe2 OpenEdge[V]) bool {
return true
}
func ( oe OpenEdge [ V ] ) String ( ) string {
func ( oe * OpenEdge [ V ] ) String ( ) string {
vertexType := "tup"
@ -73,9 +75,10 @@ func (oe OpenEdge[V]) String() string {
// the previous edge value.
//
// NOTE I _think_ this can be factored out once Graph is genericized.
func ( oe OpenEdge [ V ] ) WithEdgeValue ( val V ) OpenEdge [ V ] {
oe . edgeVal = val
return oe
func ( oe * OpenEdge [ V ] ) WithEdgeValue ( val V ) * OpenEdge [ V ] {
oeCp := * oe
oeCp . edgeVal = val
return & oeCp
}
// EdgeValue returns the Value which lies on the edge itself.
@ -96,7 +99,7 @@ func (oe OpenEdge[V]) FromValue() (V, bool) {
// FromTuple returns the tuple of OpenEdges from which the OpenEdge was created
// via TupleOut, or false if it wasn't created via TupleOut.
func ( oe OpenEdge [ V ] ) FromTuple ( ) ( [ ] OpenEdge [ V ] , bool ) {
func ( oe OpenEdge [ V ] ) FromTuple ( ) ( [ ] * OpenEdge [ V ] , bool ) {
if oe . val != nil {
return nil , false
}
@ -105,37 +108,37 @@ func (oe OpenEdge[V]) FromTuple() ([]OpenEdge[V], bool) {
}
// ValueOut creates a OpenEdge which, when used to construct a Graph, represents
// an edge (with edgeVal attached to it) coming from the ValueVertex containing
// val.
func ValueOut [ V Value ] ( val , edgeVal V ) OpenEdge [ V ] {
return OpenEdge [ V ] { val : & val , edgeVal : edgeVal }
// an edge (with edgeVal attached to it) coming from the vertex containing val.
func ValueOut [ V Value ] ( val , edgeVal V ) * OpenEdge [ V ] {
return & OpenEdge [ V ] {
val : & val ,
edgeVal : edgeVal ,
}
}
// TupleOut creates an OpenEdge which, when used to construct a Graph,
// represents an edge (with edgeVal attached to it) coming from the
// TupleVertex comprised of the given ordered-set of input edges.
//
// If len(ins) == 1 && edgeVal.IsZero(), then that single OpenEdge is
// returned as-is.
func TupleOut [ V Value ] ( ins [ ] OpenEdge [ V ] , edgeVal V ) OpenEdge [ V ] {
// represents an edge (with edgeVal attached to it) coming from the vertex
// comprised of the given ordered-set of input edges.
func TupleOut [ V Value ] ( ins [ ] * OpenEdge [ V ] , edgeVal V ) * OpenEdge [ V ] {
if len ( ins ) == 1 {
in := ins [ 0 ]
var zero V
var (
zero V
in = ins [ 0 ]
)
if edgeVal . Equal ( zero ) {
return in
}
if in . edgeVal . Equal ( zero ) {
in . edgeVal = edgeVal
return in
return in . WithEdgeValue ( edgeVal )
}
}
return OpenEdge [ V ] {
return & OpenEdge [ V ] {
tup : ins ,
edgeVal : edgeVal ,
}
@ -143,39 +146,11 @@ func TupleOut[V Value](ins []OpenEdge[V], edgeVal V) OpenEdge[V] {
type graphValueIn [ V Value ] struct {
val V
edges [ ] OpenEdge [ V ]
}
func ( valIn graphValueIn [ V ] ) cp ( ) graphValueIn [ V ] {
cp := valIn
cp . edges = make ( [ ] OpenEdge [ V ] , len ( valIn . edges ) )
copy ( cp . edges , valIn . edges )
return valIn
edge * OpenEdge [ V ]
}
func ( valIn graphValueIn [ V ] ) equal ( valIn2 graphValueIn [ V ] ) bool {
if ! valIn . val . Equal ( valIn2 . val ) {
return false
}
if len ( valIn . edges ) != len ( valIn2 . edges ) {
return false
}
outer :
for _ , edge := range valIn . edges {
for _ , edge2 := range valIn2 . edges {
if edge . equal ( edge2 ) {
continue outer
}
}
return false
}
return true
return valIn . val . Equal ( valIn2 . val ) && valIn . edge . equal ( valIn2 . edge )
}
// Graph is an immutable container of a set of vertices. The Graph keeps track
@ -186,13 +161,16 @@ outer:
// lots of O(N) operations, unnecessary copying on changes, and duplicate data
// in memory.
type Graph [ V Value ] struct {
edges [ ] * OpenEdge [ V ]
valIns [ ] graphValueIn [ V ]
}
func ( g * Graph [ V ] ) cp ( ) * Graph [ V ] {
cp := & Graph [ V ] {
edges : make ( [ ] * OpenEdge [ V ] , len ( g . edges ) ) ,
valIns : make ( [ ] graphValueIn [ V ] , len ( g . valIns ) ) ,
}
copy ( cp . edges , g . edges )
copy ( cp . valIns , g . valIns )
return cp
}
@ -202,55 +180,82 @@ func (g *Graph[V]) String() string {
var strs [ ] string
for _ , valIn := range g . valIns {
for _ , oe := range valIn . edges {
strs = append (
strs ,
fmt . Sprintf ( "valIn(%s, %s)" , oe . String ( ) , valIn . val . String ( ) ) ,
)
}
strs = append (
strs ,
fmt . Sprintf ( "valIn(%s, %s)" , valIn . edge . String ( ) , valIn . val . String ( ) ) ,
)
}
return fmt . Sprintf ( "graph(%s)" , strings . Join ( strs , ", " ) )
}
// NOTE this method is used more for its functionality than for any performance
// reasons... it's incredibly inefficient in how it deduplicates edges, but by
// doing the deduplication we enable the graph map operation to work correctly.
func ( g * Graph [ V ] ) dedupeEdge ( edge * OpenEdge [ V ] ) * OpenEdge [ V ] {
// check if there's an existing edge which is fully equivalent in the graph
// already, and if so return that.
for i := range g . edges {
if g . edges [ i ] . equal ( edge ) {
return g . edges [ i ]
}
}
// if this edge is a value edge then there's nothing else to do, return it.
if _ , ok := edge . FromValue ( ) ; ok {
return edge
}
// this edge is a tuple edge, it's possible that one of its sub-edges is
// already in the graph. dedupe each sub-edge individually.
tupEdges := make ( [ ] * OpenEdge [ V ] , len ( edge . tup ) )
for i := range edge . tup {
tupEdges [ i ] = g . dedupeEdge ( edge . tup [ i ] )
}
return TupleOut ( tupEdges , edge . EdgeValue ( ) )
}
// ValueIns returns, if any, all OpenEdges which lead to the given Value in the
// Graph (ie, all those added via AddValueIn).
func ( g * Graph [ V ] ) ValueIns ( val Value ) [ ] OpenEdge [ V ] {
//
// The returned slice should not be modified.
func ( g * Graph [ V ] ) ValueIns ( val Value ) [ ] * OpenEdge [ V ] {
var edges [ ] * OpenEdge [ V ]
for _ , valIn := range g . valIns {
if valIn . val . Equal ( val ) {
return valIn . cp ( ) . edges
edges = append ( edges , valIn . edge )
}
}
return nil
return edges
}
// AddValueIn takes a OpenEdge and connects it to the Value vertex containing
// val, returning the new Graph which reflects that connection.
func ( g * Graph [ V ] ) AddValueIn ( oe OpenEdge [ V ] , val V ) * Graph [ V ] {
func ( g * Graph [ V ] ) AddValueIn ( oe * OpenEdge [ V ] , val V ) * Graph [ V ] {
edges := g . ValueIns ( val )
valIn := graphValueIn [ V ] {
val : val ,
edge : oe ,
}
for _ , existingOE := range edges {
if existingOE . equal ( oe ) {
for i := range g . valIn s {
if g . valIns [ i ] . equal ( valIn ) {
return g
}
}
// ValueIns returns a copy of edges, so we're ok to modify it.
edges = append ( edges , oe )
valIn := graphValueIn [ V ] { val : val , edges : edges }
valIn . edge = g . dedupeEdge ( valIn . edge )
g = g . cp ( )
for i , existingValIn := range g . valIns {
if existingValIn . val . Equal ( val ) {
g . valIns [ i ] = valIn
return g
}
}
g . valIns = append ( g . valIns , valIn )
return g
}