├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitpod.yml ├── LICENSE ├── README.md ├── cmd ├── basic │ └── main.go ├── terraform │ └── main.go └── timing │ └── main.go ├── dag.go ├── dag_test.go ├── example_basic_test.go ├── example_descandentsFlow_test.go ├── example_idinterface_test.go ├── go.mod ├── go.sum ├── marshal.go ├── marshal_test.go ├── options.go ├── options_test.go ├── storage.go ├── storage_test.go ├── visitor.go └── visitor_test.go /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | name: Run Tests 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Install Go 15 | uses: actions/setup-go@v3 16 | with: 17 | go-version: '1.20' 18 | - name: Checkout Code 19 | uses: actions/checkout@v3 20 | - name: Run Linters 21 | uses: golangci/golangci-lint-action@v3 22 | with: 23 | version: v1.52.2 24 | 25 | test: 26 | strategy: 27 | matrix: 28 | go-version: ['1.20', '1.21', '1.22'] 29 | platform: [ubuntu-latest, macos-latest, windows-latest] 30 | runs-on: ${{ matrix.platform }} 31 | steps: 32 | - name: Install Go 33 | if: success() 34 | uses: actions/setup-go@v2 35 | with: 36 | go-version: ${{ matrix.go-version }} 37 | - name: Checkout Code 38 | uses: actions/checkout@v2 39 | - name: Run Tests 40 | run: go test -v -covermode=count 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | *~ 3 | /coverage.txt 4 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: go get && go build ./... && go test ./... 3 | command: go run 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Sebastian Bogan 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dag 2 | 3 | [![run tests](https://github.com/heimdalr/dag/workflows/Run%20Tests/badge.svg?branch=master)](https://github.com/heimdalr/dag/actions?query=branch%3Amaster) 4 | [![PkgGoDev](https://pkg.go.dev/badge/github.com/heimdalr/dag)](https://pkg.go.dev/github.com/heimdalr/dag) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/heimdalr/dag)](https://goreportcard.com/report/github.com/heimdalr/dag) 6 | [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/heimdalr/dag) 7 | [![CodeQL](https://github.com/heimdalr/dag/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/heimdalr/dag/actions/workflows/codeql-analysis.yml) 8 | 9 | [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6402/badge)](https://bestpractices.coreinfrastructure.org/projects/6402) 10 | 11 | 12 | Implementation of directed acyclic graphs (DAGs). 13 | 14 | The implementation is fast and thread-safe. It prevents adding cycles or 15 | duplicates and thereby always maintains a valid DAG. The implementation caches 16 | descendants and ancestors to speed up subsequent calls. 17 | 18 | 38 | 39 | 40 | 41 | 42 | ## Quickstart 43 | 44 | Running: 45 | 46 | ``` go 47 | package main 48 | 49 | import ( 50 | "fmt" 51 | "github.com/heimdalr/dag" 52 | ) 53 | 54 | func main() { 55 | 56 | // initialize a new graph 57 | d := NewDAG() 58 | 59 | // init three vertices 60 | v1, _ := d.AddVertex(1) 61 | v2, _ := d.AddVertex(2) 62 | v3, _ := d.AddVertex(struct{a string; b string}{a: "foo", b: "bar"}) 63 | 64 | // add the above vertices and connect them with two edges 65 | _ = d.AddEdge(v1, v2) 66 | _ = d.AddEdge(v1, v3) 67 | 68 | // describe the graph 69 | fmt.Print(d.String()) 70 | } 71 | ``` 72 | 73 | will result in something like: 74 | 75 | ``` 76 | DAG Vertices: 3 - Edges: 2 77 | Vertices: 78 | 1 79 | 2 80 | {foo bar} 81 | Edges: 82 | 1 -> 2 83 | 1 -> {foo bar} 84 | ``` 85 | -------------------------------------------------------------------------------- /cmd/basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/heimdalr/dag" 6 | ) 7 | 8 | func main() { 9 | 10 | // initialize a new graph 11 | d := dag.NewDAG() 12 | 13 | // init three vertices 14 | v1, _ := d.AddVertex(1) 15 | v2, _ := d.AddVertex(2) 16 | v3, _ := d.AddVertex(3) 17 | 18 | // add the above vertices and connect them with two edges 19 | _ = d.AddEdge(v1, v2) 20 | _ = d.AddEdge(v1, v3) 21 | 22 | // describe the graph 23 | fmt.Print(d.String()) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/terraform/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | import ( 5 | "fmt" 6 | "github.com/hashicorp/terraform/dag" 7 | "math" 8 | "time" 9 | ) 10 | 11 | type largeVertex struct { 12 | value int 13 | } 14 | 15 | // implement the Vertex's interface method String() 16 | func (v largeVertex) String() string { 17 | return fmt.Sprintf("%d", v.value) 18 | } 19 | 20 | // implement the Vertex's interface method ID() 21 | func (v largeVertex) ID() string { 22 | return fmt.Sprintf("%d", v.value) 23 | } 24 | */ 25 | 26 | func main() { 27 | /* 28 | var d dag.AcyclicGraph 29 | 30 | root := d.Add(1) 31 | levels := 7 32 | branches := 9 33 | var start, end time.Time 34 | 35 | start = time.Now() 36 | largeAux(d, levels, branches, root) 37 | _ = d.Validate() 38 | end = time.Now() 39 | fmt.Printf("%fs to add %d vertices and %d edges\n", end.Sub(start).Seconds(), len(d.Vertices()), len(d.Edges())) 40 | expectedVertexCount := sum(0, levels-1, branches, pow) 41 | vertexCount := len(d.Vertices()) 42 | if vertexCount != expectedVertexCount { 43 | panic(fmt.Sprintf("GetVertices() = %d, want %d", vertexCount, expectedVertexCount)) 44 | } 45 | 46 | start = time.Now() 47 | descendants, _ := d.Descendents(root) 48 | end = time.Now() 49 | fmt.Printf("%fs to get descendants\n", end.Sub(start).Seconds()) 50 | descendantsCount := descendants.Len() 51 | expectedDescendantsCount := vertexCount - 1 52 | if descendantsCount != expectedDescendantsCount { 53 | panic(fmt.Sprintf("GetDescendants(root) = %d, want %d", descendantsCount, expectedDescendantsCount)) 54 | } 55 | 56 | start = time.Now() 57 | _, _ = d.Descendents(root) 58 | end = time.Now() 59 | fmt.Printf("%fs to get descendants 2nd time\n", end.Sub(start).Seconds()) 60 | 61 | start = time.Now() 62 | d.TransitiveReduction() 63 | end = time.Now() 64 | fmt.Printf("%fs to transitively reduce the graph\n", end.Sub(start).Seconds()) 65 | */ 66 | } 67 | 68 | /* 69 | func largeAux(d dag.AcyclicGraph, level int, branches int, parent dag.Vertex) { 70 | if level > 1 { 71 | if branches < 1 || branches > 9 { 72 | panic("number of branches must be between 1 and 9") 73 | } 74 | for i := 1; i <= branches; i++ { 75 | value := parent.(int)*10 + i 76 | child := d.Add(value) 77 | d.Connect(dag.BasicEdge(child, parent)) 78 | largeAux(d, level-1, branches, child) 79 | } 80 | } 81 | } 82 | 83 | func sum(x, y, branches int, fn interface{}) int { 84 | if x > y { 85 | return 0 86 | } 87 | f, ok := fn.(func(int, int) int) 88 | if !ok { 89 | panic("function no of correct tpye") 90 | } 91 | current := f(branches, x) 92 | rest := sum(x+1, y, branches, f) 93 | return current + rest 94 | } 95 | 96 | func pow(base int, exp int) int { 97 | pow := math.Pow(float64(base), float64(exp)) 98 | return int(pow) 99 | }*/ 100 | -------------------------------------------------------------------------------- /cmd/timing/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/heimdalr/dag" 6 | "math" 7 | "time" 8 | ) 9 | 10 | type largeVertex struct { 11 | value int 12 | } 13 | 14 | // ID implement the interface{}'s interface method Id() 15 | func (v largeVertex) ID() string { 16 | return fmt.Sprintf("%d", v.value) 17 | } 18 | 19 | func main() { 20 | d := dag.NewDAG() 21 | root := largeVertex{1} 22 | key, _ := d.AddVertex(root) 23 | levels := 7 24 | branches := 9 25 | var start, end time.Time 26 | 27 | start = time.Now() 28 | largeAux(d, levels, branches, root) 29 | end = time.Now() 30 | fmt.Printf("%fs to add %d vertices and %d edges\n", end.Sub(start).Seconds(), d.GetOrder(), d.GetSize()) 31 | expectedVertexCount := sum(0, levels-1, branches, pow) 32 | vertexCount := len(d.GetVertices()) 33 | if vertexCount != expectedVertexCount { 34 | panic(fmt.Sprintf("GetVertices() = %d, want %d", vertexCount, expectedVertexCount)) 35 | } 36 | 37 | start = time.Now() 38 | descendants, _ := d.GetDescendants(key) 39 | end = time.Now() 40 | fmt.Printf("%fs to get descendants\n", end.Sub(start).Seconds()) 41 | descendantsCount := len(descendants) 42 | expectedDescendantsCount := vertexCount - 1 43 | if descendantsCount != expectedDescendantsCount { 44 | panic(fmt.Sprintf("GetDescendants(root) = %d, want %d", descendantsCount, expectedDescendantsCount)) 45 | } 46 | 47 | start = time.Now() 48 | _, _ = d.GetDescendants(key) 49 | end = time.Now() 50 | fmt.Printf("%fs to get descendants 2nd time\n", end.Sub(start).Seconds()) 51 | 52 | start = time.Now() 53 | descendantsOrdered, _ := d.GetOrderedDescendants(key) 54 | end = time.Now() 55 | fmt.Printf("%fs to get descendants ordered\n", end.Sub(start).Seconds()) 56 | descendantsOrderedCount := len(descendantsOrdered) 57 | if descendantsOrderedCount != expectedDescendantsCount { 58 | panic(fmt.Sprintf("GetOrderedDescendants(root) = %d, want %d", descendantsOrderedCount, expectedDescendantsCount)) 59 | } 60 | 61 | start = time.Now() 62 | children, _ := d.GetChildren(key) 63 | end = time.Now() 64 | fmt.Printf("%fs to get children\n", end.Sub(start).Seconds()) 65 | childrenCount := len(children) 66 | expectedChildrenCount := branches 67 | if childrenCount != expectedChildrenCount { 68 | panic(fmt.Sprintf("GetChildren(root) = %d, want %d", childrenCount, expectedChildrenCount)) 69 | } 70 | 71 | _, _ = d.GetDescendants(key) 72 | edgeCountBefore := d.GetSize() 73 | start = time.Now() 74 | d.ReduceTransitively() 75 | end = time.Now() 76 | fmt.Printf("%fs to transitively reduce the graph with caches poupulated\n", end.Sub(start).Seconds()) 77 | if edgeCountBefore != d.GetSize() { 78 | panic(fmt.Sprintf("GetSize() = %d, want %d", d.GetSize(), edgeCountBefore)) 79 | } 80 | 81 | d.FlushCaches() 82 | start = time.Now() 83 | d.ReduceTransitively() 84 | end = time.Now() 85 | fmt.Printf("%fs to transitively reduce the graph without caches poupulated\n", end.Sub(start).Seconds()) 86 | 87 | var childList []string 88 | for x := range children { 89 | childList = append(childList, x) 90 | break 91 | } 92 | start = time.Now() 93 | if len(childList) > 0 { 94 | _ = d.DeleteEdge(key, childList[0]) 95 | } 96 | end = time.Now() 97 | fmt.Printf("%fs to delete an edge from the root\n", end.Sub(start).Seconds()) 98 | 99 | } 100 | 101 | func largeAux(d *dag.DAG, level int, branches int, parent largeVertex) (int, int) { 102 | var vertexCount int 103 | var edgeCount int 104 | if level > 1 { 105 | if branches < 1 || branches > 9 { 106 | panic("number of branches must be between 1 and 9") 107 | } 108 | for i := 1; i <= branches; i++ { 109 | value := parent.value*10 + i 110 | child := largeVertex{value} 111 | childId, _ := d.AddVertex(child) 112 | vertexCount++ 113 | err := d.AddEdge(parent.ID(), childId) 114 | edgeCount++ 115 | if err != nil { 116 | panic(err) 117 | } 118 | childVertexCount, childEdgeCount := largeAux(d, level-1, branches, child) 119 | vertexCount += childVertexCount 120 | edgeCount += childEdgeCount 121 | } 122 | } 123 | return vertexCount, edgeCount 124 | } 125 | 126 | func sum(x, y, branches int, fn interface{}) int { 127 | if x > y { 128 | return 0 129 | } 130 | f, ok := fn.(func(int, int) int) 131 | if !ok { 132 | panic("function no of correct tpye") 133 | } 134 | current := f(branches, x) 135 | rest := sum(x+1, y, branches, f) 136 | return current + rest 137 | } 138 | 139 | func pow(base int, exp int) int { 140 | pow := math.Pow(float64(base), float64(exp)) 141 | return int(pow) 142 | } 143 | -------------------------------------------------------------------------------- /dag.go: -------------------------------------------------------------------------------- 1 | // Package dag implements directed acyclic graphs (DAGs). 2 | package dag 3 | 4 | import ( 5 | "fmt" 6 | "sync" 7 | 8 | "github.com/google/uuid" 9 | ) 10 | 11 | // IDInterface describes the interface a type must implement in order to 12 | // explicitly specify vertex id. 13 | // 14 | // Objects of types not implementing this interface will receive automatically 15 | // generated ids (as of adding them to the graph). 16 | type IDInterface interface { 17 | ID() string 18 | } 19 | 20 | // DAG implements the data structure of the DAG. 21 | type DAG struct { 22 | muDAG sync.RWMutex 23 | vertices map[interface{}]string 24 | vertexIds map[string]interface{} 25 | inboundEdge map[interface{}]map[interface{}]struct{} 26 | outboundEdge map[interface{}]map[interface{}]struct{} 27 | muCache sync.RWMutex 28 | verticesLocked *dMutex 29 | ancestorsCache map[interface{}]map[interface{}]struct{} 30 | descendantsCache map[interface{}]map[interface{}]struct{} 31 | options Options 32 | } 33 | 34 | // NewDAG creates / initializes a new DAG. 35 | func NewDAG() *DAG { 36 | return &DAG{ 37 | vertices: make(map[interface{}]string), 38 | vertexIds: make(map[string]interface{}), 39 | inboundEdge: make(map[interface{}]map[interface{}]struct{}), 40 | outboundEdge: make(map[interface{}]map[interface{}]struct{}), 41 | verticesLocked: newDMutex(), 42 | ancestorsCache: make(map[interface{}]map[interface{}]struct{}), 43 | descendantsCache: make(map[interface{}]map[interface{}]struct{}), 44 | options: defaultOptions(), 45 | } 46 | } 47 | 48 | // AddVertex adds the vertex v to the DAG. AddVertex returns an error, if v is 49 | // nil, v is already part of the graph, or the id of v is already part of the 50 | // graph. 51 | func (d *DAG) AddVertex(v interface{}) (string, error) { 52 | 53 | d.muDAG.Lock() 54 | defer d.muDAG.Unlock() 55 | 56 | return d.addVertex(v) 57 | } 58 | 59 | func (d *DAG) addVertex(v interface{}) (string, error) { 60 | 61 | var id string 62 | if i, ok := v.(IDInterface); ok { 63 | id = i.ID() 64 | } else { 65 | id = uuid.New().String() 66 | } 67 | 68 | err := d.addVertexByID(id, v) 69 | return id, err 70 | } 71 | 72 | // AddVertexByID adds the vertex v and the specified id to the DAG. 73 | // AddVertexByID returns an error, if v is nil, v is already part of the graph, 74 | // or the specified id is already part of the graph. 75 | func (d *DAG) AddVertexByID(id string, v interface{}) error { 76 | 77 | d.muDAG.Lock() 78 | defer d.muDAG.Unlock() 79 | 80 | return d.addVertexByID(id, v) 81 | } 82 | 83 | func (d *DAG) addVertexByID(id string, v interface{}) error { 84 | vHash := d.hashVertex(v) 85 | 86 | // sanity checking 87 | if v == nil { 88 | return VertexNilError{} 89 | } 90 | if _, exists := d.vertices[vHash]; exists { 91 | return VertexDuplicateError{v} 92 | } 93 | 94 | if _, exists := d.vertexIds[id]; exists { 95 | return IDDuplicateError{id} 96 | } 97 | 98 | d.vertices[vHash] = id 99 | d.vertexIds[id] = v 100 | 101 | return nil 102 | } 103 | 104 | // GetVertex returns a vertex by its id. GetVertex returns an error, if id is 105 | // the empty string or unknown. 106 | func (d *DAG) GetVertex(id string) (interface{}, error) { 107 | d.muDAG.RLock() 108 | defer d.muDAG.RUnlock() 109 | 110 | if id == "" { 111 | return nil, IDEmptyError{} 112 | } 113 | 114 | v, exists := d.vertexIds[id] 115 | if !exists { 116 | return nil, IDUnknownError{id} 117 | } 118 | return v, nil 119 | } 120 | 121 | // DeleteVertex deletes the vertex with the given id. DeleteVertex also 122 | // deletes all attached edges (inbound and outbound). DeleteVertex returns 123 | // an error, if id is empty or unknown. 124 | func (d *DAG) DeleteVertex(id string) error { 125 | 126 | d.muDAG.Lock() 127 | defer d.muDAG.Unlock() 128 | 129 | if err := d.saneID(id); err != nil { 130 | return err 131 | } 132 | 133 | v := d.vertexIds[id] 134 | vHash := d.hashVertex(v) 135 | 136 | // get descendents and ancestors as they are now 137 | descendants := copyMap(d.getDescendants(vHash)) 138 | ancestors := copyMap(d.getAncestors(vHash)) 139 | 140 | // delete v in outbound edges of parents 141 | if _, exists := d.inboundEdge[vHash]; exists { 142 | for parent := range d.inboundEdge[vHash] { 143 | delete(d.outboundEdge[parent], vHash) 144 | } 145 | } 146 | 147 | // delete v in inbound edges of children 148 | if _, exists := d.outboundEdge[vHash]; exists { 149 | for child := range d.outboundEdge[vHash] { 150 | delete(d.inboundEdge[child], vHash) 151 | } 152 | } 153 | 154 | // delete in- and outbound of v itself 155 | delete(d.inboundEdge, vHash) 156 | delete(d.outboundEdge, vHash) 157 | 158 | // for v and all its descendants delete cached ancestors 159 | for descendant := range descendants { 160 | delete(d.ancestorsCache, descendant) 161 | } 162 | delete(d.ancestorsCache, vHash) 163 | 164 | // for v and all its ancestors delete cached descendants 165 | for ancestor := range ancestors { 166 | delete(d.descendantsCache, ancestor) 167 | } 168 | delete(d.descendantsCache, vHash) 169 | 170 | // delete v itself 171 | delete(d.vertices, vHash) 172 | delete(d.vertexIds, id) 173 | 174 | return nil 175 | } 176 | 177 | // AddEdge adds an edge between srcID and dstID. AddEdge returns an 178 | // error, if srcID or dstID are empty strings or unknown, if the edge 179 | // already exists, or if the new edge would create a loop. 180 | func (d *DAG) AddEdge(srcID, dstID string) error { 181 | 182 | d.muDAG.Lock() 183 | defer d.muDAG.Unlock() 184 | 185 | if err := d.saneID(srcID); err != nil { 186 | return err 187 | } 188 | 189 | if err := d.saneID(dstID); err != nil { 190 | return err 191 | } 192 | 193 | if srcID == dstID { 194 | return SrcDstEqualError{srcID, dstID} 195 | } 196 | 197 | src := d.vertexIds[srcID] 198 | srcHash := d.hashVertex(src) 199 | dst := d.vertexIds[dstID] 200 | dstHash := d.hashVertex(dst) 201 | 202 | // if the edge is already known, there is nothing else to do 203 | if d.isEdge(srcHash, dstHash) { 204 | return EdgeDuplicateError{srcID, dstID} 205 | } 206 | 207 | // get descendents and ancestors as they are now 208 | descendants := copyMap(d.getDescendants(dstHash)) 209 | ancestors := copyMap(d.getAncestors(srcHash)) 210 | 211 | if _, exists := descendants[srcHash]; exists { 212 | return EdgeLoopError{srcID, dstID} 213 | } 214 | 215 | // prepare d.outbound[src], iff needed 216 | if _, exists := d.outboundEdge[srcHash]; !exists { 217 | d.outboundEdge[srcHash] = make(map[interface{}]struct{}) 218 | } 219 | 220 | // dst is a child of src 221 | d.outboundEdge[srcHash][dstHash] = struct{}{} 222 | 223 | // prepare d.inboundEdge[dst], iff needed 224 | if _, exists := d.inboundEdge[dstHash]; !exists { 225 | d.inboundEdge[dstHash] = make(map[interface{}]struct{}) 226 | } 227 | 228 | // src is a parent of dst 229 | d.inboundEdge[dstHash][srcHash] = struct{}{} 230 | 231 | // for dst and all its descendants delete cached ancestors 232 | for descendant := range descendants { 233 | delete(d.ancestorsCache, descendant) 234 | } 235 | delete(d.ancestorsCache, dstHash) 236 | 237 | // for src and all its ancestors delete cached descendants 238 | for ancestor := range ancestors { 239 | delete(d.descendantsCache, ancestor) 240 | } 241 | delete(d.descendantsCache, srcHash) 242 | 243 | return nil 244 | } 245 | 246 | // IsEdge returns true, if there exists an edge between srcID and dstID. 247 | // IsEdge returns false, if there is no such edge. IsEdge returns an error, 248 | // if srcID or dstID are empty, unknown, or the same. 249 | func (d *DAG) IsEdge(srcID, dstID string) (bool, error) { 250 | d.muDAG.RLock() 251 | defer d.muDAG.RUnlock() 252 | 253 | if err := d.saneID(srcID); err != nil { 254 | return false, err 255 | } 256 | if err := d.saneID(dstID); err != nil { 257 | return false, err 258 | } 259 | if srcID == dstID { 260 | return false, SrcDstEqualError{srcID, dstID} 261 | } 262 | 263 | src := d.vertexIds[srcID] 264 | dst := d.vertexIds[dstID] 265 | return d.isEdge(d.hashVertex(src), d.hashVertex(dst)), nil 266 | } 267 | 268 | func (d *DAG) isEdge(srcHash, dstHash interface{}) bool { 269 | 270 | if _, exists := d.outboundEdge[srcHash]; !exists { 271 | return false 272 | } 273 | if _, exists := d.outboundEdge[srcHash][dstHash]; !exists { 274 | return false 275 | } 276 | if _, exists := d.inboundEdge[dstHash]; !exists { 277 | return false 278 | } 279 | if _, exists := d.inboundEdge[dstHash][srcHash]; !exists { 280 | return false 281 | } 282 | return true 283 | } 284 | 285 | // DeleteEdge deletes the edge between srcID and dstID. DeleteEdge 286 | // returns an error, if srcID or dstID are empty or unknown, or if, 287 | // there is no edge between srcID and dstID. 288 | func (d *DAG) DeleteEdge(srcID, dstID string) error { 289 | 290 | d.muDAG.Lock() 291 | defer d.muDAG.Unlock() 292 | 293 | if err := d.saneID(srcID); err != nil { 294 | return err 295 | } 296 | if err := d.saneID(dstID); err != nil { 297 | return err 298 | } 299 | if srcID == dstID { 300 | return SrcDstEqualError{srcID, dstID} 301 | } 302 | 303 | src := d.vertexIds[srcID] 304 | srcHash := d.hashVertex(src) 305 | dst := d.vertexIds[dstID] 306 | dstHash := d.hashVertex(dst) 307 | 308 | if !d.isEdge(srcHash, dstHash) { 309 | return EdgeUnknownError{srcID, dstID} 310 | } 311 | 312 | // get descendents and ancestors as they are now 313 | descendants := copyMap(d.getDescendants(srcHash)) 314 | ancestors := copyMap(d.getAncestors(dstHash)) 315 | 316 | // delete outbound and inbound 317 | delete(d.outboundEdge[srcHash], dstHash) 318 | delete(d.inboundEdge[dstHash], srcHash) 319 | 320 | // for src and all its descendants delete cached ancestors 321 | for descendant := range descendants { 322 | delete(d.ancestorsCache, descendant) 323 | } 324 | delete(d.ancestorsCache, srcHash) 325 | 326 | // for dst and all its ancestors delete cached descendants 327 | for ancestor := range ancestors { 328 | delete(d.descendantsCache, ancestor) 329 | } 330 | delete(d.descendantsCache, dstHash) 331 | 332 | return nil 333 | } 334 | 335 | // GetOrder returns the number of vertices in the graph. 336 | func (d *DAG) GetOrder() int { 337 | d.muDAG.RLock() 338 | defer d.muDAG.RUnlock() 339 | return d.getOrder() 340 | } 341 | 342 | func (d *DAG) getOrder() int { 343 | return len(d.vertices) 344 | } 345 | 346 | // GetSize returns the number of edges in the graph. 347 | func (d *DAG) GetSize() int { 348 | d.muDAG.RLock() 349 | defer d.muDAG.RUnlock() 350 | return d.getSize() 351 | } 352 | 353 | func (d *DAG) getSize() int { 354 | count := 0 355 | for _, value := range d.outboundEdge { 356 | count += len(value) 357 | } 358 | return count 359 | } 360 | 361 | // GetLeaves returns all vertices without children. 362 | func (d *DAG) GetLeaves() map[string]interface{} { 363 | d.muDAG.RLock() 364 | defer d.muDAG.RUnlock() 365 | return d.getLeaves() 366 | } 367 | 368 | func (d *DAG) getLeaves() map[string]interface{} { 369 | leaves := make(map[string]interface{}) 370 | for v := range d.vertices { 371 | dstIDs, ok := d.outboundEdge[v] 372 | if !ok || len(dstIDs) == 0 { 373 | id := d.vertices[v] 374 | leaves[id] = v 375 | } 376 | } 377 | return leaves 378 | } 379 | 380 | // IsLeaf returns true, if the vertex with the given id has no children. IsLeaf 381 | // returns an error, if id is empty or unknown. 382 | func (d *DAG) IsLeaf(id string) (bool, error) { 383 | d.muDAG.RLock() 384 | defer d.muDAG.RUnlock() 385 | if err := d.saneID(id); err != nil { 386 | return false, err 387 | } 388 | return d.isLeaf(id), nil 389 | } 390 | 391 | func (d *DAG) isLeaf(id string) bool { 392 | v := d.vertexIds[id] 393 | vHash := d.hashVertex(v) 394 | dstIDs, ok := d.outboundEdge[vHash] 395 | if !ok || len(dstIDs) == 0 { 396 | return true 397 | } 398 | return false 399 | } 400 | 401 | // GetRoots returns all vertices without parents. 402 | func (d *DAG) GetRoots() map[string]interface{} { 403 | d.muDAG.RLock() 404 | defer d.muDAG.RUnlock() 405 | return d.getRoots() 406 | } 407 | 408 | func (d *DAG) getRoots() map[string]interface{} { 409 | roots := make(map[string]interface{}) 410 | for vHash := range d.vertices { 411 | srcIDs, ok := d.inboundEdge[vHash] 412 | if !ok || len(srcIDs) == 0 { 413 | id := d.vertices[vHash] 414 | roots[id] = vHash 415 | } 416 | } 417 | return roots 418 | } 419 | 420 | // IsRoot returns true, if the vertex with the given id has no parents. IsRoot 421 | // returns an error, if id is empty or unknown. 422 | func (d *DAG) IsRoot(id string) (bool, error) { 423 | d.muDAG.RLock() 424 | defer d.muDAG.RUnlock() 425 | if err := d.saneID(id); err != nil { 426 | return false, err 427 | } 428 | return d.isRoot(id), nil 429 | } 430 | 431 | func (d *DAG) isRoot(id string) bool { 432 | v := d.vertexIds[id] 433 | vHash := d.hashVertex(v) 434 | srcIDs, ok := d.inboundEdge[vHash] 435 | if !ok || len(srcIDs) == 0 { 436 | return true 437 | } 438 | return false 439 | } 440 | 441 | // GetVertices returns all vertices. 442 | func (d *DAG) GetVertices() map[string]interface{} { 443 | d.muDAG.RLock() 444 | defer d.muDAG.RUnlock() 445 | out := make(map[string]interface{}) 446 | for id, value := range d.vertexIds { 447 | out[id] = value 448 | } 449 | return out 450 | } 451 | 452 | // GetParents returns the all parents of the vertex with the id 453 | // id. GetParents returns an error, if id is empty or unknown. 454 | func (d *DAG) GetParents(id string) (map[string]interface{}, error) { 455 | d.muDAG.RLock() 456 | defer d.muDAG.RUnlock() 457 | if err := d.saneID(id); err != nil { 458 | return nil, err 459 | } 460 | v := d.vertexIds[id] 461 | vHash := d.hashVertex(v) 462 | parents := make(map[string]interface{}) 463 | for pv := range d.inboundEdge[vHash] { 464 | pid := d.vertices[pv] 465 | parents[pid] = pv 466 | } 467 | return parents, nil 468 | } 469 | 470 | // GetChildren returns all children of the vertex with the id 471 | // id. GetChildren returns an error, if id is empty or unknown. 472 | func (d *DAG) GetChildren(id string) (map[string]interface{}, error) { 473 | d.muDAG.RLock() 474 | defer d.muDAG.RUnlock() 475 | return d.getChildren(id) 476 | } 477 | 478 | func (d *DAG) getChildren(id string) (map[string]interface{}, error) { 479 | if err := d.saneID(id); err != nil { 480 | return nil, err 481 | } 482 | v := d.vertexIds[id] 483 | vHash := d.hashVertex(v) 484 | children := make(map[string]interface{}) 485 | for cv := range d.outboundEdge[vHash] { 486 | cid := d.vertices[cv] 487 | children[cid] = cv 488 | } 489 | return children, nil 490 | } 491 | 492 | // GetAncestors return all ancestors of the vertex with the id id. GetAncestors 493 | // returns an error, if id is empty or unknown. 494 | // 495 | // Note, in order to get the ancestors, GetAncestors populates the ancestor- 496 | // cache as needed. Depending on order and size of the sub-graph of the vertex 497 | // with id id this may take a long time and consume a lot of memory. 498 | func (d *DAG) GetAncestors(id string) (map[string]interface{}, error) { 499 | d.muDAG.RLock() 500 | defer d.muDAG.RUnlock() 501 | if err := d.saneID(id); err != nil { 502 | return nil, err 503 | } 504 | v := d.vertexIds[id] 505 | vHash := d.hashVertex(v) 506 | ancestors := make(map[string]interface{}) 507 | for av := range d.getAncestors(vHash) { 508 | aid := d.vertices[av] 509 | ancestors[aid] = av 510 | } 511 | return ancestors, nil 512 | } 513 | 514 | func (d *DAG) getAncestors(vHash interface{}) map[interface{}]struct{} { 515 | 516 | // in the best case we have already a populated cache 517 | d.muCache.RLock() 518 | cache, exists := d.ancestorsCache[vHash] 519 | d.muCache.RUnlock() 520 | if exists { 521 | return cache 522 | } 523 | 524 | // lock this vertex to work on it exclusively 525 | d.verticesLocked.lock(vHash) 526 | defer d.verticesLocked.unlock(vHash) 527 | 528 | // now as we have locked this vertex, check (again) that no one has 529 | // meanwhile populated the cache 530 | d.muCache.RLock() 531 | cache, exists = d.ancestorsCache[vHash] 532 | d.muCache.RUnlock() 533 | if exists { 534 | return cache 535 | } 536 | 537 | // as there is no cache, we start from scratch and collect all ancestors locally 538 | cache = make(map[interface{}]struct{}) 539 | var mu sync.Mutex 540 | if parents, ok := d.inboundEdge[vHash]; ok { 541 | 542 | // for each parent collect its ancestors 543 | for parent := range parents { 544 | parentAncestors := d.getAncestors(parent) 545 | mu.Lock() 546 | for ancestor := range parentAncestors { 547 | cache[ancestor] = struct{}{} 548 | } 549 | cache[parent] = struct{}{} 550 | mu.Unlock() 551 | } 552 | } 553 | 554 | // remember the collected descendents 555 | d.muCache.Lock() 556 | d.ancestorsCache[vHash] = cache 557 | d.muCache.Unlock() 558 | return cache 559 | } 560 | 561 | // GetOrderedAncestors returns all ancestors of the vertex with id id 562 | // in a breath-first order. Only the first occurrence of each vertex is 563 | // returned. GetOrderedAncestors returns an error, if id is empty or 564 | // unknown. 565 | // 566 | // Note, there is no order between sibling vertices. Two consecutive runs of 567 | // GetOrderedAncestors may return different results. 568 | func (d *DAG) GetOrderedAncestors(id string) ([]string, error) { 569 | d.muDAG.RLock() 570 | defer d.muDAG.RUnlock() 571 | ids, _, err := d.AncestorsWalker(id) 572 | if err != nil { 573 | return nil, err 574 | } 575 | var ancestors []string 576 | for aid := range ids { 577 | ancestors = append(ancestors, aid) 578 | } 579 | return ancestors, nil 580 | } 581 | 582 | // AncestorsWalker returns a channel and subsequently returns / walks all 583 | // ancestors of the vertex with id id in a breath first order. The second 584 | // channel returned may be used to stop further walking. AncestorsWalker 585 | // returns an error, if id is empty or unknown. 586 | // 587 | // Note, there is no order between sibling vertices. Two consecutive runs of 588 | // AncestorsWalker may return different results. 589 | func (d *DAG) AncestorsWalker(id string) (chan string, chan bool, error) { 590 | d.muDAG.RLock() 591 | defer d.muDAG.RUnlock() 592 | if err := d.saneID(id); err != nil { 593 | return nil, nil, err 594 | } 595 | ids := make(chan string) 596 | signal := make(chan bool, 1) 597 | go func() { 598 | d.muDAG.RLock() 599 | v := d.vertexIds[id] 600 | vHash := d.hashVertex(v) 601 | d.walkAncestors(vHash, ids, signal) 602 | d.muDAG.RUnlock() 603 | close(ids) 604 | close(signal) 605 | }() 606 | return ids, signal, nil 607 | } 608 | 609 | func (d *DAG) walkAncestors(vHash interface{}, ids chan string, signal chan bool) { 610 | 611 | var fifo []interface{} 612 | visited := make(map[interface{}]struct{}) 613 | for parent := range d.inboundEdge[vHash] { 614 | visited[parent] = struct{}{} 615 | fifo = append(fifo, parent) 616 | } 617 | for { 618 | if len(fifo) == 0 { 619 | return 620 | } 621 | top := fifo[0] 622 | fifo = fifo[1:] 623 | for parent := range d.inboundEdge[top] { 624 | if _, exists := visited[parent]; !exists { 625 | visited[parent] = struct{}{} 626 | fifo = append(fifo, parent) 627 | } 628 | } 629 | select { 630 | case <-signal: 631 | return 632 | default: 633 | ids <- d.vertices[top] 634 | } 635 | } 636 | } 637 | 638 | // GetDescendants return all descendants of the vertex with id id. 639 | // GetDescendants returns an error, if id is empty or unknown. 640 | // 641 | // Note, in order to get the descendants, GetDescendants populates the 642 | // descendants-cache as needed. Depending on order and size of the sub-graph 643 | // of the vertex with id id this may take a long time and consume a lot 644 | // of memory. 645 | func (d *DAG) GetDescendants(id string) (map[string]interface{}, error) { 646 | d.muDAG.RLock() 647 | defer d.muDAG.RUnlock() 648 | 649 | if err := d.saneID(id); err != nil { 650 | return nil, err 651 | } 652 | v := d.vertexIds[id] 653 | vHash := d.hashVertex(v) 654 | 655 | descendants := make(map[string]interface{}) 656 | for dv := range d.getDescendants(vHash) { 657 | did := d.vertices[dv] 658 | descendants[did] = dv 659 | } 660 | return descendants, nil 661 | } 662 | 663 | func (d *DAG) getDescendants(vHash interface{}) map[interface{}]struct{} { 664 | 665 | // in the best case we have already a populated cache 666 | d.muCache.RLock() 667 | cache, exists := d.descendantsCache[vHash] 668 | d.muCache.RUnlock() 669 | if exists { 670 | return cache 671 | } 672 | 673 | // lock this vertex to work on it exclusively 674 | d.verticesLocked.lock(vHash) 675 | defer d.verticesLocked.unlock(vHash) 676 | 677 | // now as we have locked this vertex, check (again) that no one has 678 | // meanwhile populated the cache 679 | d.muCache.RLock() 680 | cache, exists = d.descendantsCache[vHash] 681 | d.muCache.RUnlock() 682 | if exists { 683 | return cache 684 | } 685 | 686 | // as there is no cache, we start from scratch and collect all descendants 687 | // locally 688 | cache = make(map[interface{}]struct{}) 689 | var mu sync.Mutex 690 | if children, ok := d.outboundEdge[vHash]; ok { 691 | 692 | // for each child use a goroutine to collect its descendants 693 | //var waitGroup sync.WaitGroup 694 | //waitGroup.Add(len(children)) 695 | for child := range children { 696 | //go func(child interface{}, mu *sync.Mutex, cache map[interface{}]bool) { 697 | childDescendants := d.getDescendants(child) 698 | mu.Lock() 699 | for descendant := range childDescendants { 700 | cache[descendant] = struct{}{} 701 | } 702 | cache[child] = struct{}{} 703 | mu.Unlock() 704 | //waitGroup.Done() 705 | //}(child, &mu, cache) 706 | } 707 | //waitGroup.Wait() 708 | } 709 | 710 | // remember the collected descendents 711 | d.muCache.Lock() 712 | d.descendantsCache[vHash] = cache 713 | d.muCache.Unlock() 714 | return cache 715 | } 716 | 717 | // GetOrderedDescendants returns all descendants of the vertex with id id 718 | // in a breath-first order. Only the first occurrence of each vertex is 719 | // returned. GetOrderedDescendants returns an error, if id is empty or 720 | // unknown. 721 | // 722 | // Note, there is no order between sibling vertices. Two consecutive runs of 723 | // GetOrderedDescendants may return different results. 724 | func (d *DAG) GetOrderedDescendants(id string) ([]string, error) { 725 | d.muDAG.RLock() 726 | defer d.muDAG.RUnlock() 727 | ids, _, err := d.DescendantsWalker(id) 728 | if err != nil { 729 | return nil, err 730 | } 731 | var descendants []string 732 | for did := range ids { 733 | descendants = append(descendants, did) 734 | } 735 | return descendants, nil 736 | } 737 | 738 | // GetDescendantsGraph returns a new DAG consisting of the vertex with id id and 739 | // all its descendants (i.e. the subgraph). GetDescendantsGraph also returns the 740 | // id of the (copy of the) given vertex within the new graph (i.e. the id of the 741 | // single root of the new graph). GetDescendantsGraph returns an error, if id is 742 | // empty or unknown. 743 | // 744 | // Note, the new graph is a copy of the relevant part of the original graph. 745 | func (d *DAG) GetDescendantsGraph(id string) (*DAG, string, error) { 746 | 747 | // recursively add the current vertex and all its descendants 748 | return d.getRelativesGraph(id, false) 749 | } 750 | 751 | // GetAncestorsGraph returns a new DAG consisting of the vertex with id id and 752 | // all its ancestors (i.e. the subgraph). GetAncestorsGraph also returns the id 753 | // of the (copy of the) given vertex within the new graph (i.e. the id of the 754 | // single leaf of the new graph). GetAncestorsGraph returns an error, if id is 755 | // empty or unknown. 756 | // 757 | // Note, the new graph is a copy of the relevant part of the original graph. 758 | func (d *DAG) GetAncestorsGraph(id string) (*DAG, string, error) { 759 | 760 | // recursively add the current vertex and all its ancestors 761 | return d.getRelativesGraph(id, true) 762 | } 763 | 764 | func (d *DAG) getRelativesGraph(id string, asc bool) (*DAG, string, error) { 765 | // sanity checking 766 | if id == "" { 767 | return nil, "", IDEmptyError{} 768 | } 769 | v, exists := d.vertexIds[id] 770 | vHash := d.hashVertex(v) 771 | if !exists { 772 | return nil, "", IDUnknownError{id} 773 | } 774 | 775 | // create a new dag 776 | newDAG := NewDAG() 777 | 778 | // protect the graph from modification 779 | d.muDAG.RLock() 780 | defer d.muDAG.RUnlock() 781 | 782 | // recursively add the current vertex and all its relatives 783 | newId, err := d.getRelativesGraphRec(vHash, newDAG, make(map[interface{}]string), asc) 784 | return newDAG, newId, err 785 | } 786 | 787 | func (d *DAG) getRelativesGraphRec(vHash interface{}, newDAG *DAG, visited map[interface{}]string, asc bool) (newId string, err error) { 788 | 789 | // copy this vertex to the new graph 790 | if newId, err = newDAG.AddVertex(vHash); err != nil { 791 | return 792 | } 793 | 794 | // mark this vertex as visited 795 | visited[vHash] = newId 796 | 797 | // get the direct relatives (depending on the direction either parents or children) 798 | var relatives map[interface{}]struct{} 799 | var ok bool 800 | if asc { 801 | relatives, ok = d.inboundEdge[vHash] 802 | } else { 803 | relatives, ok = d.outboundEdge[vHash] 804 | } 805 | 806 | // for all direct relatives in the original graph 807 | if ok { 808 | for relative := range relatives { 809 | 810 | // if we haven't seen this relative 811 | relativeId, exists := visited[relative] 812 | if !exists { 813 | 814 | // recursively add this relative 815 | if relativeId, err = d.getRelativesGraphRec(relative, newDAG, visited, asc); err != nil { 816 | return 817 | } 818 | } 819 | 820 | // add edge to this relative (depending on the direction) 821 | var srcID, dstID string 822 | if asc { 823 | srcID, dstID = relativeId, newId 824 | 825 | } else { 826 | srcID, dstID = newId, relativeId 827 | } 828 | if err = newDAG.AddEdge(srcID, dstID); err != nil { 829 | return 830 | } 831 | } 832 | } 833 | return 834 | } 835 | 836 | // DescendantsWalker returns a channel and subsequently returns / walks all 837 | // descendants of the vertex with id in a breath first order. The second 838 | // channel returned may be used to stop further walking. DescendantsWalker 839 | // returns an error, if id is empty or unknown. 840 | // 841 | // Note, there is no order between sibling vertices. Two consecutive runs of 842 | // DescendantsWalker may return different results. 843 | func (d *DAG) DescendantsWalker(id string) (chan string, chan bool, error) { 844 | d.muDAG.RLock() 845 | defer d.muDAG.RUnlock() 846 | if err := d.saneID(id); err != nil { 847 | return nil, nil, err 848 | } 849 | ids := make(chan string) 850 | signal := make(chan bool, 1) 851 | go func() { 852 | d.muDAG.RLock() 853 | v := d.vertexIds[id] 854 | vHash := d.hashVertex(v) 855 | d.walkDescendants(vHash, ids, signal) 856 | d.muDAG.RUnlock() 857 | close(ids) 858 | close(signal) 859 | }() 860 | return ids, signal, nil 861 | } 862 | 863 | func (d *DAG) walkDescendants(vHash interface{}, ids chan string, signal chan bool) { 864 | var fifo []interface{} 865 | visited := make(map[interface{}]struct{}) 866 | for child := range d.outboundEdge[vHash] { 867 | visited[child] = struct{}{} 868 | fifo = append(fifo, child) 869 | } 870 | for { 871 | if len(fifo) == 0 { 872 | return 873 | } 874 | top := fifo[0] 875 | fifo = fifo[1:] 876 | for child := range d.outboundEdge[top] { 877 | if _, exists := visited[child]; !exists { 878 | visited[child] = struct{}{} 879 | fifo = append(fifo, child) 880 | } 881 | } 882 | select { 883 | case <-signal: 884 | return 885 | default: 886 | ids <- d.vertices[top] 887 | } 888 | } 889 | } 890 | 891 | // FlowResult describes the data to be passed between vertices in a DescendantsFlow. 892 | type FlowResult struct { 893 | 894 | // The id of the vertex that produced this result. 895 | ID string 896 | 897 | // The actual result. 898 | Result interface{} 899 | 900 | // Any error. Note, DescendantsFlow does not care about this error. It is up to 901 | // the FlowCallback of downstream vertices to handle the error as needed - if 902 | // needed. 903 | Error error 904 | } 905 | 906 | // FlowCallback is the signature of the (callback-) function to call for each 907 | // vertex within a DescendantsFlow, after all its parents have finished their 908 | // work. The parameters of the function are the (complete) DAG, the current 909 | // vertex ID, and the results of all its parents. An instance of FlowCallback 910 | // should return a result or an error. 911 | type FlowCallback func(d *DAG, id string, parentResults []FlowResult) (interface{}, error) 912 | 913 | // DescendantsFlow traverses descendants of the vertex with the ID startID. For 914 | // the vertex itself and each of its descendant it executes the given (callback-) 915 | // function providing it the results of its respective parents. The (callback-) 916 | // function is only executed after all parents have finished their work. 917 | func (d *DAG) DescendantsFlow(startID string, inputs []FlowResult, callback FlowCallback) ([]FlowResult, error) { 918 | d.muDAG.RLock() 919 | defer d.muDAG.RUnlock() 920 | 921 | // Get IDs of all descendant vertices. 922 | flowIDs, errDes := d.GetDescendants(startID) 923 | if errDes != nil { 924 | return []FlowResult{}, errDes 925 | } 926 | 927 | // inputChannels provides for input channels for each of the descendant vertices (+ the start-vertex). 928 | inputChannels := make(map[string]chan FlowResult, len(flowIDs)+1) 929 | 930 | // Iterate vertex IDs and create an input channel for each of them and a single 931 | // output channel for leaves. Note, this "pre-flight" is needed to ensure we 932 | // really have an input channel regardless of how we traverse the tree and spawn 933 | // workers. 934 | leafCount := 0 935 | if len(flowIDs) == 0 { 936 | leafCount = 1 937 | } 938 | for id := range flowIDs { 939 | 940 | // Get all parents of this vertex. 941 | parents, errPar := d.GetParents(id) 942 | if errPar != nil { 943 | return []FlowResult{}, errPar 944 | } 945 | 946 | // Create a buffered input channel that has capacity for all parent results. 947 | inputChannels[id] = make(chan FlowResult, len(parents)) 948 | 949 | if d.isLeaf(id) { 950 | leafCount += 1 951 | } 952 | } 953 | 954 | // outputChannel caries the results of leaf vertices. 955 | outputChannel := make(chan FlowResult, leafCount) 956 | 957 | // To also process the start vertex and to have its results being passed to its 958 | // children, add it to the vertex IDs. Also add an input channel for the start 959 | // vertex and feed the inputs to this channel. 960 | flowIDs[startID] = struct{}{} 961 | inputChannels[startID] = make(chan FlowResult, len(inputs)) 962 | for _, i := range inputs { 963 | inputChannels[startID] <- i 964 | } 965 | 966 | wg := sync.WaitGroup{} 967 | 968 | // Iterate all vertex IDs (now incl. start vertex) and handle each worker (incl. 969 | // inputs and outputs) in a separate goroutine. 970 | for id := range flowIDs { 971 | 972 | // Get all children of this vertex that later need to be notified. Note, we 973 | // collect all children before the goroutine to be able to release the read 974 | // lock as early as possible. 975 | children, errChildren := d.GetChildren(id) 976 | if errChildren != nil { 977 | return []FlowResult{}, errChildren 978 | } 979 | 980 | // Remember to wait for this goroutine. 981 | wg.Add(1) 982 | 983 | go func(id string) { 984 | 985 | // Get this vertex's input channel. 986 | // Note, only concurrent read here, which is fine. 987 | c := inputChannels[id] 988 | 989 | // Await all parent inputs and stuff them into a slice. 990 | parentCount := cap(c) 991 | parentResults := make([]FlowResult, parentCount) 992 | for i := 0; i < parentCount; i++ { 993 | parentResults[i] = <-c 994 | } 995 | 996 | // Execute the worker. 997 | result, errWorker := callback(d, id, parentResults) 998 | 999 | // Wrap the worker's result into a FlowResult. 1000 | flowResult := FlowResult{ 1001 | ID: id, 1002 | Result: result, 1003 | Error: errWorker, 1004 | } 1005 | 1006 | // Send this worker's FlowResult onto all children's input channels or, if it is 1007 | // a leaf (i.e. no children), send the result onto the output channel. 1008 | if len(children) > 0 { 1009 | for child := range children { 1010 | inputChannels[child] <- flowResult 1011 | } 1012 | } else { 1013 | outputChannel <- flowResult 1014 | } 1015 | 1016 | // "Sign off". 1017 | wg.Done() 1018 | 1019 | }(id) 1020 | } 1021 | 1022 | // Wait for all go routines to finish. 1023 | wg.Wait() 1024 | 1025 | // Await all leaf vertex results and stuff them into a slice. 1026 | resultCount := cap(outputChannel) 1027 | results := make([]FlowResult, resultCount) 1028 | for i := 0; i < resultCount; i++ { 1029 | results[i] = <-outputChannel 1030 | } 1031 | 1032 | return results, nil 1033 | } 1034 | 1035 | // ReduceTransitively transitively reduce the graph. 1036 | // 1037 | // Note, in order to do the reduction the descendant-cache of all vertices is 1038 | // populated (i.e. the transitive closure). Depending on order and size of DAG 1039 | // this may take a long time and consume a lot of memory. 1040 | func (d *DAG) ReduceTransitively() { 1041 | 1042 | d.muDAG.Lock() 1043 | defer d.muDAG.Unlock() 1044 | 1045 | graphChanged := false 1046 | 1047 | // populate the descendents cache for all roots (i.e. the whole graph) 1048 | for _, root := range d.getRoots() { 1049 | _ = d.getDescendants(root) 1050 | } 1051 | 1052 | // for each vertex 1053 | for vHash := range d.vertices { 1054 | 1055 | // map of descendants of the children of v 1056 | descendentsOfChildrenOfV := make(map[interface{}]struct{}) 1057 | 1058 | // for each child of v 1059 | for childOfV := range d.outboundEdge[vHash] { 1060 | 1061 | // collect child descendants 1062 | for descendent := range d.descendantsCache[childOfV] { 1063 | descendentsOfChildrenOfV[descendent] = struct{}{} 1064 | } 1065 | } 1066 | 1067 | // for each child of v 1068 | for childOfV := range d.outboundEdge[vHash] { 1069 | 1070 | // remove the edge between v and child, iff child is a 1071 | // descendant of any of the children of v 1072 | if _, exists := descendentsOfChildrenOfV[childOfV]; exists { 1073 | delete(d.outboundEdge[vHash], childOfV) 1074 | delete(d.inboundEdge[childOfV], vHash) 1075 | graphChanged = true 1076 | } 1077 | } 1078 | } 1079 | 1080 | // flush the descendants- and ancestor cache if the graph has changed 1081 | if graphChanged { 1082 | d.flushCaches() 1083 | } 1084 | } 1085 | 1086 | // FlushCaches completely flushes the descendants- and ancestor cache. 1087 | // 1088 | // Note, the only reason to call this method is to free up memory. 1089 | // Normally the caches are automatically maintained. 1090 | func (d *DAG) FlushCaches() { 1091 | d.muDAG.Lock() 1092 | defer d.muDAG.Unlock() 1093 | d.flushCaches() 1094 | } 1095 | 1096 | func (d *DAG) flushCaches() { 1097 | d.ancestorsCache = make(map[interface{}]map[interface{}]struct{}) 1098 | d.descendantsCache = make(map[interface{}]map[interface{}]struct{}) 1099 | } 1100 | 1101 | // Copy returns a copy of the DAG. 1102 | func (d *DAG) Copy() (newDAG *DAG, err error) { 1103 | 1104 | // create a new dag 1105 | newDAG = NewDAG() 1106 | 1107 | // create a map of visited vertices 1108 | visited := make(map[interface{}]string) 1109 | 1110 | // protect the graph from modification 1111 | d.muDAG.RLock() 1112 | defer d.muDAG.RUnlock() 1113 | 1114 | // add all roots and their descendants to the new DAG 1115 | for _, root := range d.GetRoots() { 1116 | if _, err = d.getRelativesGraphRec(root, newDAG, visited, false); err != nil { 1117 | return 1118 | } 1119 | } 1120 | return 1121 | } 1122 | 1123 | // String returns a textual representation of the graph. 1124 | func (d *DAG) String() string { 1125 | result := fmt.Sprintf("DAG Vertices: %d - Edges: %d\n", d.GetOrder(), d.GetSize()) 1126 | result += "Vertices:\n" 1127 | d.muDAG.RLock() 1128 | for k := range d.vertices { 1129 | result += fmt.Sprintf(" %v\n", k) 1130 | } 1131 | result += "Edges:\n" 1132 | for v, children := range d.outboundEdge { 1133 | for child := range children { 1134 | result += fmt.Sprintf(" %v -> %v\n", v, child) 1135 | } 1136 | } 1137 | d.muDAG.RUnlock() 1138 | return result 1139 | } 1140 | 1141 | func (d *DAG) saneID(id string) error { 1142 | // sanity checking 1143 | if id == "" { 1144 | return IDEmptyError{} 1145 | } 1146 | _, exists := d.vertexIds[id] 1147 | if !exists { 1148 | return IDUnknownError{id} 1149 | } 1150 | return nil 1151 | } 1152 | 1153 | func (d *DAG) hashVertex(v interface{}) interface{} { 1154 | return d.options.VertexHashFunc(v) 1155 | } 1156 | 1157 | func copyMap(in map[interface{}]struct{}) map[interface{}]struct{} { 1158 | out := make(map[interface{}]struct{}) 1159 | for id, value := range in { 1160 | out[id] = value 1161 | } 1162 | return out 1163 | } 1164 | 1165 | /*************************** 1166 | ********** Errors ********** 1167 | ****************************/ 1168 | 1169 | // VertexNilError is the error type to describe the situation, that a nil is 1170 | // given instead of a vertex. 1171 | type VertexNilError struct{} 1172 | 1173 | // Implements the error interface. 1174 | func (e VertexNilError) Error() string { 1175 | return "don't know what to do with 'nil'" 1176 | } 1177 | 1178 | // VertexDuplicateError is the error type to describe the situation, that a 1179 | // given vertex already exists in the graph. 1180 | type VertexDuplicateError struct { 1181 | v interface{} 1182 | } 1183 | 1184 | // Implements the error interface. 1185 | func (e VertexDuplicateError) Error() string { 1186 | return fmt.Sprintf("'%v' is already known", e.v) 1187 | } 1188 | 1189 | // IDDuplicateError is the error type to describe the situation, that a given 1190 | // vertex id already exists in the graph. 1191 | type IDDuplicateError struct { 1192 | id string 1193 | } 1194 | 1195 | // Implements the error interface. 1196 | func (e IDDuplicateError) Error() string { 1197 | return fmt.Sprintf("the id '%s' is already known", e.id) 1198 | } 1199 | 1200 | // IDEmptyError is the error type to describe the situation, that an empty 1201 | // string is given instead of a valid id. 1202 | type IDEmptyError struct{} 1203 | 1204 | // Implements the error interface. 1205 | func (e IDEmptyError) Error() string { 1206 | return "don't know what to do with \"\"" 1207 | } 1208 | 1209 | // IDUnknownError is the error type to describe the situation, that a given 1210 | // vertex does not exit in the graph. 1211 | type IDUnknownError struct { 1212 | id string 1213 | } 1214 | 1215 | // Implements the error interface. 1216 | func (e IDUnknownError) Error() string { 1217 | return fmt.Sprintf("'%s' is unknown", e.id) 1218 | } 1219 | 1220 | // EdgeDuplicateError is the error type to describe the situation, that an edge 1221 | // already exists in the graph. 1222 | type EdgeDuplicateError struct { 1223 | src string 1224 | dst string 1225 | } 1226 | 1227 | // Implements the error interface. 1228 | func (e EdgeDuplicateError) Error() string { 1229 | return fmt.Sprintf("edge between '%s' and '%s' is already known", e.src, e.dst) 1230 | } 1231 | 1232 | // EdgeUnknownError is the error type to describe the situation, that a given 1233 | // edge does not exit in the graph. 1234 | type EdgeUnknownError struct { 1235 | src string 1236 | dst string 1237 | } 1238 | 1239 | // Implements the error interface. 1240 | func (e EdgeUnknownError) Error() string { 1241 | return fmt.Sprintf("edge between '%s' and '%s' is unknown", e.src, e.dst) 1242 | } 1243 | 1244 | // EdgeLoopError is the error type to describe loop errors (i.e. errors that 1245 | // where raised to prevent establishing loops in the graph). 1246 | type EdgeLoopError struct { 1247 | src string 1248 | dst string 1249 | } 1250 | 1251 | // Implements the error interface. 1252 | func (e EdgeLoopError) Error() string { 1253 | return fmt.Sprintf("edge between '%s' and '%s' would create a loop", e.src, e.dst) 1254 | } 1255 | 1256 | // SrcDstEqualError is the error type to describe the situation, that src and 1257 | // dst are equal. 1258 | type SrcDstEqualError struct { 1259 | src string 1260 | dst string 1261 | } 1262 | 1263 | // Implements the error interface. 1264 | func (e SrcDstEqualError) Error() string { 1265 | return fmt.Sprintf("src ('%s') and dst ('%s') equal", e.src, e.dst) 1266 | } 1267 | 1268 | /*************************** 1269 | ********** dMutex ********** 1270 | ****************************/ 1271 | 1272 | type cMutex struct { 1273 | mutex sync.Mutex 1274 | count int 1275 | } 1276 | 1277 | // Structure for dynamic mutexes. 1278 | type dMutex struct { 1279 | mutexes map[interface{}]*cMutex 1280 | globalMutex sync.Mutex 1281 | } 1282 | 1283 | // Initialize a new dynamic mutex structure. 1284 | func newDMutex() *dMutex { 1285 | return &dMutex{ 1286 | mutexes: make(map[interface{}]*cMutex), 1287 | } 1288 | } 1289 | 1290 | // Get a lock for instance i 1291 | func (d *dMutex) lock(i interface{}) { 1292 | 1293 | // acquire global lock 1294 | d.globalMutex.Lock() 1295 | 1296 | // if there is no cMutex for i, create it 1297 | if _, ok := d.mutexes[i]; !ok { 1298 | d.mutexes[i] = new(cMutex) 1299 | } 1300 | 1301 | // increase the count in order to show, that we are interested in this 1302 | // instance mutex (thus now one deletes it) 1303 | d.mutexes[i].count++ 1304 | 1305 | // remember the mutex for later 1306 | mutex := &d.mutexes[i].mutex 1307 | 1308 | // as the cMutex is there, we have increased the count, and we know the 1309 | // instance mutex, we can release the global lock 1310 | d.globalMutex.Unlock() 1311 | 1312 | // and wait on the instance mutex 1313 | (*mutex).Lock() 1314 | } 1315 | 1316 | // Release the lock for instance i. 1317 | func (d *dMutex) unlock(i interface{}) { 1318 | 1319 | // acquire global lock 1320 | d.globalMutex.Lock() 1321 | 1322 | // unlock instance mutex 1323 | d.mutexes[i].mutex.Unlock() 1324 | 1325 | // decrease the count, as we are no longer interested in this instance 1326 | // mutex 1327 | d.mutexes[i].count-- 1328 | 1329 | // if we are the last one interested in this instance mutex delete the 1330 | // cMutex 1331 | if d.mutexes[i].count == 0 { 1332 | delete(d.mutexes, i) 1333 | } 1334 | 1335 | // release the global lock 1336 | d.globalMutex.Unlock() 1337 | } 1338 | -------------------------------------------------------------------------------- /dag_test.go: -------------------------------------------------------------------------------- 1 | package dag 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-test/deep" 6 | "sort" 7 | "strconv" 8 | "testing" 9 | ) 10 | 11 | type iVertex struct{ value int } 12 | 13 | func (v iVertex) ID() string { return fmt.Sprintf("%d", v.value) } 14 | 15 | type foobar struct { 16 | A string 17 | B string 18 | } 19 | type foobarKey struct { 20 | A string 21 | B string 22 | MyID string 23 | } 24 | 25 | func (o foobarKey) ID() string { return o.MyID } 26 | 27 | func TestNewDAG(t *testing.T) { 28 | dag := NewDAG() 29 | if order := dag.GetOrder(); order != 0 { 30 | t.Errorf("GetOrder() = %d, want 0", order) 31 | } 32 | if size := dag.GetSize(); size != 0 { 33 | t.Errorf("GetSize() = %d, want 0", size) 34 | } 35 | } 36 | 37 | func TestDAG_AddVertex(t *testing.T) { 38 | dag := NewDAG() 39 | 40 | // add a single vertex and inspect the graph 41 | v := iVertex{1} 42 | id, _ := dag.AddVertex(v) 43 | if id != v.ID() { 44 | t.Errorf("GetOrder().ID() = %s, want %s", id, v.ID()) 45 | } 46 | if order := dag.GetOrder(); order != 1 { 47 | t.Errorf("GetOrder() = %d, want 1", order) 48 | } 49 | if size := dag.GetSize(); size != 0 { 50 | t.Errorf("GetSize() = %d, want 0", size) 51 | } 52 | if leaves := len(dag.GetLeaves()); leaves != 1 { 53 | t.Errorf("GetLeaves() = %d, want 1", leaves) 54 | } 55 | if roots := len(dag.GetRoots()); roots != 1 { 56 | t.Errorf("GetLeaves() = %d, want 1", roots) 57 | } 58 | vertices := dag.GetVertices() 59 | if vertices := len(vertices); vertices != 1 { 60 | t.Errorf("GetVertices() = %d, want 1", vertices) 61 | } 62 | 63 | if _, exists := vertices[id]; !exists { 64 | t.Errorf("GetVertices()[id] = false, want true") 65 | } 66 | 67 | // duplicate 68 | _, errDuplicate := dag.AddVertex(v) 69 | if errDuplicate == nil { 70 | t.Errorf("AddVertex(v) = nil, want %T", VertexDuplicateError{v}) 71 | } 72 | if _, ok := errDuplicate.(VertexDuplicateError); !ok { 73 | t.Errorf("AddVertex(v) expected VertexDuplicateError, got %T", errDuplicate) 74 | } 75 | 76 | // duplicate 77 | _, errIDDuplicate := dag.AddVertex(foobarKey{MyID: "1"}) 78 | if errIDDuplicate == nil { 79 | t.Errorf("AddVertex(foobarKey{MyID: \"1\"}) = nil, want %T", IDDuplicateError{"1"}) 80 | } 81 | if _, ok := errIDDuplicate.(IDDuplicateError); !ok { 82 | t.Errorf("AddVertex(foobarKey{MyID: \"1\"}) expected IDDuplicateError, got %T", errIDDuplicate) 83 | } 84 | 85 | // nil 86 | _, errNil := dag.AddVertex(nil) 87 | if errNil == nil { 88 | t.Errorf("AddVertex(nil) = nil, want %T", VertexNilError{}) 89 | } 90 | if _, ok := errNil.(VertexNilError); !ok { 91 | t.Errorf("AddVertex(nil) expected VertexNilError, got %T", errNil) 92 | } 93 | 94 | } 95 | 96 | func TestDAG_AddVertex2(t *testing.T) { 97 | dag := NewDAG() 98 | type testType struct{ value string } 99 | 100 | v := testType{"1"} 101 | id, _ := dag.AddVertex(v) 102 | vNew, _ := dag.GetVertex(id) 103 | vNewCasted, _ := vNew.(testType) 104 | if v != vNew { 105 | t.Errorf("want %v, got %v", v, vNewCasted) 106 | } 107 | if &v == &vNewCasted { 108 | t.Errorf("pointers shouldn't be equal but %p == %p", &v, &vNewCasted) 109 | } 110 | id, _ = dag.AddVertex(&v) 111 | vNew, _ = dag.GetVertex(id) 112 | vNewPtr, _ := vNew.(*testType) 113 | if v != *vNewPtr { 114 | t.Errorf("want %v, got %v", v, *vNewPtr) 115 | } 116 | if &v != vNewPtr { 117 | t.Errorf("pointers should be equal but %p != %p", &v, vNewPtr) 118 | } 119 | v.value = "20" 120 | if vNewPtr.value != v.value { 121 | t.Errorf("values not updated. want %s, got %s", v.value, vNewPtr.value) 122 | } 123 | } 124 | 125 | func TestDAG_AddVertexByID(t *testing.T) { 126 | dag := NewDAG() 127 | 128 | // add a single vertex and inspect the graph 129 | v := iVertex{1} 130 | id := "1" 131 | _ = dag.AddVertexByID(id, v) 132 | if id != v.ID() { 133 | t.Errorf("GetOrder().ID() = %s, want %s", id, v.ID()) 134 | } 135 | vertices := dag.GetVertices() 136 | if vertices := len(vertices); vertices != 1 { 137 | t.Errorf("GetVertices() = %d, want 1", vertices) 138 | } 139 | 140 | if _, exists := vertices[id]; !exists { 141 | t.Errorf("GetVertices()[id] = false, want true") 142 | } 143 | 144 | // duplicate 145 | errDuplicate := dag.AddVertexByID(id, v) 146 | if errDuplicate == nil { 147 | t.Errorf("AddVertexByID(id, v) = nil, want %T", VertexDuplicateError{v}) 148 | } 149 | if _, ok := errDuplicate.(VertexDuplicateError); !ok { 150 | t.Errorf("AddVertexByID(id, v) expected VertexDuplicateError, got %T", errDuplicate) 151 | } 152 | 153 | // duplicate 154 | _, errIDDuplicate := dag.AddVertex(foobarKey{MyID: "1"}) 155 | if errIDDuplicate == nil { 156 | t.Errorf("AddVertex(foobarKey{MyID: \"1\"}) = nil, want %T", IDDuplicateError{"1"}) 157 | } 158 | if _, ok := errIDDuplicate.(IDDuplicateError); !ok { 159 | t.Errorf("AddVertex(foobarKey{MyID: \"1\"}) expected IDDuplicateError, got %T", errIDDuplicate) 160 | } 161 | 162 | // nil 163 | errNil := dag.AddVertexByID("2", nil) 164 | if errNil == nil { 165 | t.Errorf(`AddVertexByID("2", nil) = nil, want %T`, VertexNilError{}) 166 | } 167 | if _, ok := errNil.(VertexNilError); !ok { 168 | t.Errorf(`AddVertexByID("2", nil) expected VertexNilError, got %T`, errNil) 169 | } 170 | } 171 | 172 | func TestDAG_GetVertex(t *testing.T) { 173 | dag := NewDAG() 174 | v1 := iVertex{1} 175 | id, _ := dag.AddVertex(v1) 176 | if v, _ := dag.GetVertex(id); v != v1 { 177 | t.Errorf("GetVertex() = %v, want %v", v, v1) 178 | } 179 | 180 | // "complex" document without key 181 | v2 := foobar{A: "foo", B: "bar"} 182 | k2, _ := dag.AddVertex(v2) 183 | v3i, _ := dag.GetVertex(k2) 184 | v3, ok3 := v3i.(foobar) 185 | if !ok3 { 186 | t.Error("Casting GetVertex() to original type failed") 187 | } 188 | if deep.Equal(v2, v3) != nil { 189 | t.Errorf("GetVertex() = %v, want %v", v3, v2) 190 | } 191 | 192 | // "complex" document with key 193 | idF := "fancy key" 194 | v4 := foobarKey{A: "foo", B: "bar", MyID: idF} 195 | var v5 foobarKey 196 | k4, _ := dag.AddVertex(v4) 197 | if k4 != idF { 198 | t.Errorf("AddVertex({..., MyID: \"%s\") = %v, want %v", idF, k4, idF) 199 | } 200 | v5i, _ := dag.GetVertex(k4) 201 | v5, ok5 := v5i.(foobarKey) 202 | if !ok5 { 203 | t.Error("Casting GetVertex() to original type failed") 204 | } 205 | if deep.Equal(v4, v5) != nil { 206 | t.Errorf("GetVertex() = %v, want %v", v5, v4) 207 | } 208 | 209 | // unknown 210 | _, errUnknown := dag.GetVertex("foo") 211 | if errUnknown == nil { 212 | t.Errorf("DeleteVertex(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 213 | } 214 | if _, ok := errUnknown.(IDUnknownError); !ok { 215 | t.Errorf("DeleteVertex(\"foo\") expected IDUnknownError, got %T", errUnknown) 216 | } 217 | 218 | // nil 219 | _, errNil := dag.GetVertex("") 220 | if errNil == nil { 221 | t.Errorf("DeleteVertex(\"\") = nil, want %T", IDEmptyError{}) 222 | } 223 | if _, ok := errNil.(IDEmptyError); !ok { 224 | t.Errorf("DeleteVertex(\"\") expected IDEmptyError, got %T", errNil) 225 | } 226 | } 227 | 228 | func TestDAG_DeleteVertex(t *testing.T) { 229 | dag := NewDAG() 230 | v1, _ := dag.AddVertex(iVertex{1}) 231 | 232 | // delete a single vertex and inspect the graph 233 | err := dag.DeleteVertex(v1) 234 | if err != nil { 235 | t.Error(err) 236 | } 237 | if order := dag.GetOrder(); order != 0 { 238 | t.Errorf("GetOrder() = %d, want 0", order) 239 | } 240 | if size := dag.GetSize(); size != 0 { 241 | t.Errorf("GetSize() = %d, want 0", size) 242 | } 243 | if leaves := len(dag.GetLeaves()); leaves != 0 { 244 | t.Errorf("GetLeaves() = %d, want 0", leaves) 245 | } 246 | if roots := len(dag.GetRoots()); roots != 0 { 247 | t.Errorf("GetLeaves() = %d, want 0", roots) 248 | } 249 | vertices := dag.GetVertices() 250 | l := len(vertices) 251 | if l != 0 { 252 | t.Errorf("GetVertices() = %d, want 0", l) 253 | } 254 | 255 | v1, _ = dag.AddVertex(1) 256 | v2, _ := dag.AddVertex(2) 257 | v3, _ := dag.AddVertex(3) 258 | _ = dag.AddEdge(v1, v2) 259 | _ = dag.AddEdge(v2, v3) 260 | if order := dag.GetOrder(); order != 3 { 261 | t.Errorf("GetOrder() = %d, want 3", order) 262 | } 263 | if size := dag.GetSize(); size != 2 { 264 | t.Errorf("GetSize() = %d, want 2", size) 265 | } 266 | if leaves := len(dag.GetLeaves()); leaves != 1 { 267 | t.Errorf("GetLeaves() = %d, want 1", leaves) 268 | } 269 | if roots := len(dag.GetRoots()); roots != 1 { 270 | t.Errorf("GetLeaves() = %d, want 1", roots) 271 | } 272 | if vertices := len(dag.GetVertices()); vertices != 3 { 273 | t.Errorf("GetVertices() = %d, want 3", vertices) 274 | } 275 | if vertices, _ := dag.GetDescendants(v1); len(vertices) != 2 { 276 | t.Errorf("GetDescendants(v1) = %d, want 2", len(vertices)) 277 | } 278 | if vertices, _ := dag.GetAncestors(v3); len(vertices) != 2 { 279 | t.Errorf("GetAncestors(v3) = %d, want 2", len(vertices)) 280 | } 281 | 282 | _ = dag.DeleteVertex(v2) 283 | if order := dag.GetOrder(); order != 2 { 284 | t.Errorf("GetOrder() = %d, want 2", order) 285 | } 286 | if size := dag.GetSize(); size != 0 { 287 | t.Errorf("GetSize() = %d, want 0", size) 288 | } 289 | if leaves := len(dag.GetLeaves()); leaves != 2 { 290 | t.Errorf("GetLeaves() = %d, want 2", leaves) 291 | } 292 | if roots := len(dag.GetRoots()); roots != 2 { 293 | t.Errorf("GetLeaves() = %d, want 2", roots) 294 | } 295 | if vertices := len(dag.GetVertices()); vertices != 2 { 296 | t.Errorf("GetVertices() = %d, want 2", vertices) 297 | } 298 | if vertices, _ := dag.GetDescendants(v1); len(vertices) != 0 { 299 | t.Errorf("GetDescendants(v1) = %d, want 0", len(vertices)) 300 | } 301 | if vertices, _ := dag.GetAncestors(v3); len(vertices) != 0 { 302 | t.Errorf("GetAncestors(v3) = %d, want 0", len(vertices)) 303 | } 304 | 305 | // unknown 306 | errUnknown := dag.DeleteVertex("foo") 307 | if errUnknown == nil { 308 | t.Errorf("DeleteVertex(foo) = nil, want %T", IDUnknownError{"foo"}) 309 | } 310 | if _, ok := errUnknown.(IDUnknownError); !ok { 311 | t.Errorf("DeleteVertex(foo) expected IDUnknownError, got %T", errUnknown) 312 | } 313 | 314 | // nil 315 | errNil := dag.DeleteVertex("") 316 | if errNil == nil { 317 | t.Errorf("DeleteVertex(nil) = nil, want %T", IDEmptyError{}) 318 | } 319 | if _, ok := errNil.(IDEmptyError); !ok { 320 | t.Errorf("DeleteVertex(nil) expected IDEmptyError, got %T", errNil) 321 | } 322 | } 323 | 324 | func TestDAG_AddEdge(t *testing.T) { 325 | dag := NewDAG() 326 | v0, _ := dag.AddVertex("0") 327 | v1, _ := dag.AddVertex("1") 328 | v2, _ := dag.AddVertex("2") 329 | v3, _ := dag.AddVertex("3") 330 | 331 | // add a single edge and inspect the graph 332 | errUnexpected := dag.AddEdge(v1, v2) 333 | if errUnexpected != nil { 334 | t.Error(errUnexpected) 335 | } 336 | if children, _ := dag.GetChildren(v1); len(children) != 1 { 337 | t.Errorf("GetChildren(v1) = %d, want 1", len(children)) 338 | } 339 | if parents, _ := dag.GetParents(v2); len(parents) != 1 { 340 | t.Errorf("GetParents(v2) = %d, want 1", len(parents)) 341 | } 342 | if leaves := len(dag.GetLeaves()); leaves != 3 { 343 | t.Errorf("GetLeaves() = %d, want 1", leaves) 344 | } 345 | if roots := len(dag.GetRoots()); roots != 3 { 346 | t.Errorf("GetLeaves() = %d, want 1", roots) 347 | } 348 | if vertices, _ := dag.GetDescendants(v1); len(vertices) != 1 { 349 | t.Errorf("GetDescendants(v1) = %d, want 1", len(vertices)) 350 | } 351 | if vertices, _ := dag.GetAncestors(v2); len(vertices) != 1 { 352 | t.Errorf("GetAncestors(v2) = %d, want 1", len(vertices)) 353 | } 354 | 355 | err := dag.AddEdge(v2, v3) 356 | if err != nil { 357 | t.Fatal(err) 358 | } 359 | if vertices, _ := dag.GetDescendants(v1); len(vertices) != 2 { 360 | t.Errorf("GetDescendants(v1) = %d, want 2", len(vertices)) 361 | } 362 | if vertices, _ := dag.GetAncestors(v3); len(vertices) != 2 { 363 | t.Errorf("GetAncestors(v3) = %d, want 2", len(vertices)) 364 | } 365 | 366 | _ = dag.AddEdge(v0, v1) 367 | if vertices, _ := dag.GetDescendants(v0); len(vertices) != 3 { 368 | t.Errorf("GetDescendants(v0) = %d, want 3", len(vertices)) 369 | } 370 | if vertices, _ := dag.GetAncestors(v3); len(vertices) != 3 { 371 | t.Errorf("GetAncestors(v3) = %d, want 3", len(vertices)) 372 | } 373 | 374 | // loop 375 | errLoopSrcSrc := dag.AddEdge(v1, v1) 376 | if errLoopSrcSrc == nil { 377 | t.Errorf("AddEdge(v1, v1) = nil, want %T", SrcDstEqualError{v1, v1}) 378 | } 379 | if _, ok := errLoopSrcSrc.(SrcDstEqualError); !ok { 380 | t.Errorf("AddEdge(v1, v1) expected SrcDstEqualError, got %T", errLoopSrcSrc) 381 | } 382 | errLoopDstSrc := dag.AddEdge(v2, v1) 383 | if errLoopDstSrc == nil { 384 | t.Errorf("AddEdge(v2, v1) = nil, want %T", EdgeLoopError{v2, v1}) 385 | } 386 | if _, ok := errLoopDstSrc.(EdgeLoopError); !ok { 387 | t.Errorf("AddEdge(v2, v1) expected EdgeLoopError, got %T", errLoopDstSrc) 388 | } 389 | 390 | // duplicate 391 | errDuplicate := dag.AddEdge(v1, v2) 392 | if errDuplicate == nil { 393 | t.Errorf("AddEdge(v1, v2) = nil, want %T", EdgeDuplicateError{v1, v2}) 394 | } 395 | if _, ok := errDuplicate.(EdgeDuplicateError); !ok { 396 | t.Errorf("AddEdge(v1, v2) expected EdgeDuplicateError, got %T", errDuplicate) 397 | } 398 | 399 | // nil 400 | errNilSrc := dag.AddEdge("", v2) 401 | if errNilSrc == nil { 402 | t.Errorf("AddEdge(nil, v2) = nil, want %T", IDEmptyError{}) 403 | } 404 | if _, ok := errNilSrc.(IDEmptyError); !ok { 405 | t.Errorf("AddEdge(nil, v2) expected IDEmptyError, got %T", errNilSrc) 406 | } 407 | errNilDst := dag.AddEdge(v1, "") 408 | if errNilDst == nil { 409 | t.Errorf("AddEdge(v1, nil) = nil, want %T", IDEmptyError{}) 410 | } 411 | if _, ok := errNilDst.(IDEmptyError); !ok { 412 | t.Errorf("AddEdge(v1, nil) expected IDEmptyError, got %T", errNilDst) 413 | } 414 | } 415 | 416 | func TestDAG_DeleteEdge(t *testing.T) { 417 | dag := NewDAG() 418 | v0, _ := dag.AddVertex(iVertex{0}) 419 | v1, _ := dag.AddVertex("1") 420 | _ = dag.AddEdge(v0, v1) 421 | if size := dag.GetSize(); size != 1 { 422 | t.Errorf("GetSize() = %d, want 1", size) 423 | } 424 | _ = dag.DeleteEdge(v0, v1) 425 | if size := dag.GetSize(); size != 0 { 426 | t.Errorf("GetSize() = %d, want 0", size) 427 | } 428 | 429 | // unknown 430 | errUnknown := dag.DeleteEdge(v0, v1) 431 | if errUnknown == nil { 432 | t.Errorf("DeleteEdge(v0, v1) = nil, want %T", EdgeUnknownError{}) 433 | } 434 | if _, ok := errUnknown.(EdgeUnknownError); !ok { 435 | t.Errorf("DeleteEdge(v0, v1) expected EdgeUnknownError, got %T", errUnknown) 436 | } 437 | 438 | // nil 439 | errNilSrc := dag.DeleteEdge("", v1) 440 | if errNilSrc == nil { 441 | t.Errorf("DeleteEdge(\"\", v1) = nil, want %T", IDEmptyError{}) 442 | } 443 | if _, ok := errNilSrc.(IDEmptyError); !ok { 444 | t.Errorf("DeleteEdge(\"\", v1) expected IDEmptyError, got %T", errNilSrc) 445 | } 446 | errNilDst := dag.DeleteEdge(v0, "") 447 | if errNilDst == nil { 448 | t.Errorf("DeleteEdge(v0, \"\") = nil, want %T", IDEmptyError{}) 449 | } 450 | if _, ok := errNilDst.(IDEmptyError); !ok { 451 | t.Errorf("DeleteEdge(v0, \"\") expected IDEmptyError, got %T", errNilDst) 452 | } 453 | 454 | // unknown 455 | errUnknownSrc := dag.DeleteEdge("foo", v1) 456 | if errUnknownSrc == nil { 457 | t.Errorf("DeleteEdge(foo, v1) = nil, want %T", IDUnknownError{}) 458 | } 459 | if _, ok := errUnknownSrc.(IDUnknownError); !ok { 460 | t.Errorf("DeleteEdge(foo, v1) expected IDUnknownError, got %T", errUnknownSrc) 461 | } 462 | errUnknownDst := dag.DeleteEdge(v0, "foo") 463 | if errUnknownDst == nil { 464 | t.Errorf("DeleteEdge(v0, \"foo\") = nil, want %T", IDUnknownError{}) 465 | } 466 | if _, ok := errUnknownDst.(IDUnknownError); !ok { 467 | t.Errorf("DeleteEdge(v0, \"foo\") expected IDUnknownError, got %T", errUnknownDst) 468 | } 469 | } 470 | 471 | func TestDAG_IsLeaf(t *testing.T) { 472 | dag := NewDAG() 473 | v1, _ := dag.AddVertex("1") 474 | v2, _ := dag.AddVertex("2") 475 | v3, _ := dag.AddVertex("3") 476 | 477 | _ = dag.AddEdge(v1, v2) 478 | _ = dag.AddEdge(v1, v3) 479 | if isLeaf, _ := dag.IsLeaf(v1); isLeaf { 480 | t.Errorf("IsLeaf(v1) = true, want false") 481 | } 482 | if isLeaf, _ := dag.IsLeaf(v2); !isLeaf { 483 | t.Errorf("IsLeaf(v2) = false, want true") 484 | } 485 | if isLeaf, _ := dag.IsLeaf(v3); !isLeaf { 486 | t.Errorf("IsLeaf(v3) = false, want true") 487 | } 488 | if _, err := dag.IsLeaf("foo"); err == nil { 489 | t.Errorf("IsLeaf(foo) = nil, want %T", IDUnknownError{}) 490 | } 491 | if _, err := dag.IsLeaf(""); err == nil { 492 | t.Errorf("IsLeaf(\"\") = nil, want %T", IDEmptyError{}) 493 | } 494 | } 495 | 496 | func TestDAG_IsRoot(t *testing.T) { 497 | dag := NewDAG() 498 | v1, _ := dag.AddVertex("1") 499 | v2, _ := dag.AddVertex("2") 500 | v3, _ := dag.AddVertex("3") 501 | 502 | _ = dag.AddEdge(v1, v2) 503 | _ = dag.AddEdge(v1, v3) 504 | if isRoot, _ := dag.IsRoot(v1); !isRoot { 505 | t.Errorf("IsRoot(v1) = false, want true") 506 | } 507 | if isRoot, _ := dag.IsRoot(v2); isRoot { 508 | t.Errorf("IsRoot(v2) = true, want false") 509 | } 510 | if isRoot, _ := dag.IsRoot(v3); isRoot { 511 | t.Errorf("IsRoot(v3) = true, want false") 512 | } 513 | if _, err := dag.IsRoot("foo"); err == nil { 514 | t.Errorf("IsRoot(foo) = nil, want %T", IDUnknownError{}) 515 | } 516 | if _, err := dag.IsRoot(""); err == nil { 517 | t.Errorf("IsRoot(\"\") = nil, want %T", IDEmptyError{}) 518 | } 519 | } 520 | 521 | func TestDAG_GetChildren(t *testing.T) { 522 | dag := NewDAG() 523 | v1, _ := dag.AddVertex("1") 524 | v2, _ := dag.AddVertex("2") 525 | v3, _ := dag.AddVertex("3") 526 | 527 | _ = dag.AddEdge(v1, v2) 528 | _ = dag.AddEdge(v1, v3) 529 | 530 | children, _ := dag.GetChildren(v1) 531 | if length := len(children); length != 2 { 532 | t.Errorf("GetChildren() = %d, want 2", length) 533 | } 534 | if _, exists := children[v2]; !exists { 535 | t.Errorf("GetChildren()[v2] = %t, want true", exists) 536 | } 537 | if _, exists := children[v3]; !exists { 538 | t.Errorf("GetChildren()[v3] = %t, want true", exists) 539 | } 540 | 541 | // nil 542 | _, errNil := dag.GetChildren("") 543 | if errNil == nil { 544 | t.Errorf("GetChildren(\"\") = nil, want %T", IDEmptyError{}) 545 | } 546 | if _, ok := errNil.(IDEmptyError); !ok { 547 | t.Errorf("GetChildren(\"\") expected IDEmptyError, got %T", errNil) 548 | } 549 | 550 | // unknown 551 | _, errUnknown := dag.GetChildren("foo") 552 | if errUnknown == nil { 553 | t.Errorf("GetChildren(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 554 | } 555 | if _, ok := errUnknown.(IDUnknownError); !ok { 556 | t.Errorf("GetChildren(\"foo\") expected IDUnknownError, got %T", errUnknown) 557 | } 558 | } 559 | 560 | func TestDAG_GetParents(t *testing.T) { 561 | dag := NewDAG() 562 | v1, _ := dag.addVertex("1") 563 | v2, _ := dag.addVertex("2") 564 | v3, _ := dag.addVertex("3") 565 | _ = dag.AddEdge(v1, v3) 566 | _ = dag.AddEdge(v2, v3) 567 | 568 | parents, _ := dag.GetParents(v3) 569 | if length := len(parents); length != 2 { 570 | t.Errorf("GetParents(v3) = %d, want 2", length) 571 | } 572 | if _, exists := parents[v1]; !exists { 573 | t.Errorf("GetParents(v3)[v1] = %t, want true", exists) 574 | } 575 | if _, exists := parents[v2]; !exists { 576 | t.Errorf("GetParents(v3)[v2] = %t, want true", exists) 577 | } 578 | 579 | // nil 580 | _, errNil := dag.GetParents("") 581 | if errNil == nil { 582 | t.Errorf("GetParents(\"\") = nil, want %T", IDEmptyError{}) 583 | } 584 | if _, ok := errNil.(IDEmptyError); !ok { 585 | t.Errorf("GetParents(\"\") expected IDEmptyError, got %T", errNil) 586 | } 587 | 588 | // unknown 589 | _, errUnknown := dag.GetParents("foo") 590 | if errUnknown == nil { 591 | t.Errorf("GetParents(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 592 | } 593 | if _, ok := errUnknown.(IDUnknownError); !ok { 594 | t.Errorf("GetParents(\"foo\") expected IDUnknownError, got %T", errUnknown) 595 | } 596 | 597 | } 598 | 599 | func TestDAG_GetDescendants(t *testing.T) { 600 | dag := NewDAG() 601 | v1, _ := dag.AddVertex("1") 602 | v2, _ := dag.AddVertex("2") 603 | v3, _ := dag.AddVertex("3") 604 | v4, _ := dag.AddVertex("4") 605 | 606 | _ = dag.AddEdge(v1, v2) 607 | _ = dag.AddEdge(v2, v3) 608 | _ = dag.AddEdge(v2, v4) 609 | 610 | if desc, _ := dag.GetDescendants(v1); len(desc) != 3 { 611 | t.Errorf("GetDescendants(v1) = %d, want 3", len(desc)) 612 | } 613 | if desc, _ := dag.GetDescendants(v2); len(desc) != 2 { 614 | t.Errorf("GetDescendants(v2) = %d, want 2", len(desc)) 615 | } 616 | if desc, _ := dag.GetDescendants(v3); len(desc) != 0 { 617 | t.Errorf("GetDescendants(v4) = %d, want 0", len(desc)) 618 | } 619 | if desc, _ := dag.GetDescendants(v4); len(desc) != 0 { 620 | t.Errorf("GetDescendants(v4) = %d, want 0", len(desc)) 621 | } 622 | 623 | // nil 624 | _, errNil := dag.GetDescendants("") 625 | if errNil == nil { 626 | t.Errorf("GetDescendants(\"\") = nil, want %T", IDEmptyError{}) 627 | } 628 | if _, ok := errNil.(IDEmptyError); !ok { 629 | t.Errorf("GetDescendants(\"\") expected IDEmptyError, got %T", errNil) 630 | } 631 | 632 | // unknown 633 | _, errUnknown := dag.GetDescendants("foo") 634 | if errUnknown == nil { 635 | t.Errorf("GetDescendants(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 636 | } 637 | if _, ok := errUnknown.(IDUnknownError); !ok { 638 | t.Errorf("GetDescendants(\"foo\") expected IDUnknownError, got %T", errUnknown) 639 | } 640 | } 641 | 642 | func equal(a, b []string) bool { 643 | if len(a) != len(b) { 644 | return false 645 | } 646 | for i, v := range a { 647 | if v != b[i] { 648 | return false 649 | } 650 | } 651 | return true 652 | } 653 | 654 | func TestDAG_GetOrderedDescendants(t *testing.T) { 655 | dag := NewDAG() 656 | v1, _ := dag.AddVertex("1") 657 | v2, _ := dag.AddVertex("2") 658 | v3, _ := dag.AddVertex("3") 659 | v4, _ := dag.AddVertex("4") 660 | 661 | _ = dag.AddEdge(v1, v2) 662 | _ = dag.AddEdge(v2, v3) 663 | _ = dag.AddEdge(v2, v4) 664 | 665 | if desc, _ := dag.GetOrderedDescendants(v1); len(desc) != 3 { 666 | t.Errorf("len(GetOrderedDescendants(v1)) = %d, want 3", len(desc)) 667 | } 668 | if desc, _ := dag.GetOrderedDescendants(v2); len(desc) != 2 { 669 | t.Errorf("len(GetOrderedDescendants(v2)) = %d, want 2", len(desc)) 670 | } 671 | if desc, _ := dag.GetOrderedDescendants(v3); len(desc) != 0 { 672 | t.Errorf("len(GetOrderedDescendants(v4)) = %d, want 0", len(desc)) 673 | } 674 | if desc, _ := dag.GetOrderedDescendants(v4); len(desc) != 0 { 675 | t.Errorf("GetOrderedDescendants(v4) = %d, want 0", len(desc)) 676 | } 677 | if desc, _ := dag.GetOrderedDescendants(v1); !equal(desc, []string{v2, v3, v4}) && !equal(desc, []string{v2, v4, v3}) { 678 | t.Errorf("GetOrderedDescendants(v4) = %v, want %v or %v", desc, []string{v2, v3, v4}, []string{v2, v4, v3}) 679 | } 680 | 681 | // nil 682 | _, errNil := dag.GetOrderedDescendants("") 683 | if errNil == nil { 684 | t.Errorf("GetOrderedDescendants(\"\") = nil, want %T", IDEmptyError{}) 685 | } 686 | if _, ok := errNil.(IDEmptyError); !ok { 687 | t.Errorf("GetOrderedDescendants(\"\") expected IDEmptyError, got %T", errNil) 688 | } 689 | 690 | // unknown 691 | _, errUnknown := dag.GetOrderedDescendants("foo") 692 | if errUnknown == nil { 693 | t.Errorf("GetOrderedDescendants(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 694 | } 695 | if _, ok := errUnknown.(IDUnknownError); !ok { 696 | t.Errorf("GetOrderedDescendants(\"foo\") expected IDUnknownError, got %T", errUnknown) 697 | } 698 | } 699 | 700 | func TestDAG_GetDescendantsGraph(t *testing.T) { 701 | d0 := NewDAG() 702 | 703 | v1 := iVertex{1} 704 | v1ID, _ := d0.AddVertex(v1) 705 | _, _ = d0.AddVertex(iVertex{2}) 706 | _, _ = d0.AddVertex(iVertex{3}) 707 | _, _ = d0.AddVertex(iVertex{4}) 708 | _, _ = d0.AddVertex(iVertex{5}) 709 | v6 := iVertex{6} 710 | v6ID, _ := d0.AddVertex(v6) 711 | _, _ = d0.AddVertex(iVertex{7}) 712 | _, _ = d0.AddVertex(iVertex{8}) 713 | v9ID, _ := d0.AddVertex(iVertex{9}) 714 | 715 | _ = d0.AddEdge("1", "2") 716 | _ = d0.AddEdge("2", "3") 717 | _ = d0.AddEdge("2", "4") 718 | _ = d0.AddEdge("3", "5") 719 | _ = d0.AddEdge("4", "5") 720 | _ = d0.AddEdge("5", "6") 721 | _ = d0.AddEdge("6", "7") 722 | _ = d0.AddEdge("6", "8") 723 | 724 | // basic tests -- 2 children 725 | d, newId, err := d0.GetDescendantsGraph(v6ID) 726 | if err != nil { 727 | t.Error(err) 728 | } 729 | if d == nil { 730 | t.Error("GetDescendantsGraph(v6ID) returned nil") 731 | } 732 | if newId == "" { 733 | t.Error("GetDescendantsGraph(v6ID) returned empty new id") 734 | } 735 | if newId != v6ID { 736 | t.Errorf("GetDescendantsGraph(v6ID) returned new id %s, want %s", newId, v6ID) 737 | } 738 | if d.GetOrder() != 3 { 739 | t.Errorf("GetOrder() = %d, want 3", d.GetOrder()) 740 | } 741 | if d.GetSize() != 2 { 742 | t.Errorf("GetSize() = %d, want 2", d.GetSize()) 743 | } 744 | roots := d.GetRoots() 745 | if len(roots) != 1 { 746 | t.Errorf("len(GetRoots()) = %d, want 1", len(roots)) 747 | } 748 | if _, exists := roots[newId]; !exists { 749 | t.Errorf("%s is not the root of the new graph", newId) 750 | } 751 | if v6 != roots[newId] { 752 | t.Errorf("wrong root got = %v, want %v", v6, roots[newId]) 753 | } 754 | 755 | // test duplicates 756 | d2, newId2, err2 := d0.GetDescendantsGraph(v1ID) 757 | if err2 != nil { 758 | t.Error(err2) 759 | } 760 | if d2 == nil { 761 | t.Error("GetDescendantsGraph(v1ID) returned nil") 762 | } 763 | if newId2 == "" { 764 | t.Error("GetDescendantsGraph(v1ID) returned empty new id") 765 | } 766 | if newId2 != v1ID { 767 | t.Errorf("GetDescendantsGraph(v1ID) returned new id %s, want %s", newId2, v1ID) 768 | } 769 | newVertex, _ := d2.GetVertex(newId2) 770 | if v1 != newVertex { 771 | t.Errorf("want = %v, got %v", v1, newVertex) 772 | } 773 | if d2.GetOrder() != 8 { 774 | t.Errorf("GetOrder() = %d, want 3", d2.GetOrder()) 775 | } 776 | if d2.GetSize() != 8 { 777 | t.Errorf("GetSize() = %d, want 8", d2.GetSize()) 778 | } 779 | roots2 := d2.GetRoots() 780 | if len(roots2) != 1 { 781 | t.Errorf("len(GetRoots()) = %d, want 1", len(roots2)) 782 | } 783 | if _, exists2 := roots2[newId2]; !exists2 { 784 | t.Errorf("%s is not the root of the new graph", newId2) 785 | } 786 | if v1 != roots2[newId2] { 787 | t.Errorf("wrong root got = %v, want %v", v1, roots2[newId2]) 788 | } 789 | _, errGetUnknown := d2.GetVertex(v9ID) 790 | if errGetUnknown == nil { 791 | t.Errorf("GetVertex(v9ID) = nil, want %T", IDUnknownError{v9ID}) 792 | } 793 | if _, ok := errGetUnknown.(IDUnknownError); !ok { 794 | t.Errorf("GetVertex(v9ID) expected IDUnknownError, got %T", errGetUnknown) 795 | } 796 | 797 | // nil 798 | _, _, errNil := d0.GetDescendantsGraph("") 799 | if errNil == nil { 800 | t.Errorf("GetDescendantsGraph(\"\") = nil, want %T", IDEmptyError{}) 801 | } 802 | if _, ok := errNil.(IDEmptyError); !ok { 803 | t.Errorf("GetDescendantsGraph(\"\") expected IDEmptyError, got %T", errNil) 804 | } 805 | 806 | // unknown 807 | _, _, errUnknown := d0.GetDescendantsGraph("foo") 808 | if errUnknown == nil { 809 | t.Errorf("GetDescendantsGraph(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 810 | } 811 | if _, ok := errUnknown.(IDUnknownError); !ok { 812 | t.Errorf("GetDescendantsGraph(\"foo\") expected IDUnknownError, got %T", errUnknown) 813 | } 814 | } 815 | 816 | func TestDAG_GetAncestorsGraph(t *testing.T) { 817 | d0 := NewDAG() 818 | 819 | _, _ = d0.AddVertex(iVertex{1}) 820 | _, _ = d0.AddVertex(iVertex{2}) 821 | _, _ = d0.AddVertex(iVertex{3}) 822 | _, _ = d0.AddVertex(iVertex{4}) 823 | v5 := iVertex{5} 824 | v5ID, _ := d0.AddVertex(v5) 825 | _, _ = d0.AddVertex(iVertex{6}) 826 | _, _ = d0.AddVertex(iVertex{7}) 827 | _, _ = d0.AddVertex(iVertex{8}) 828 | v9ID, _ := d0.AddVertex(iVertex{9}) 829 | 830 | _ = d0.AddEdge("1", "2") 831 | _ = d0.AddEdge("2", "3") 832 | _ = d0.AddEdge("2", "4") 833 | _ = d0.AddEdge("3", "5") 834 | _ = d0.AddEdge("4", "5") 835 | _ = d0.AddEdge("5", "6") 836 | _ = d0.AddEdge("6", "7") 837 | _ = d0.AddEdge("6", "8") 838 | 839 | // basic tests -- 2 children 840 | d, newId, err := d0.GetAncestorsGraph(v5ID) 841 | if err != nil { 842 | t.Error(err) 843 | } 844 | if d == nil { 845 | t.Error("GetAncestorsGraph(v5ID) returned nil") 846 | } 847 | if newId == "" { 848 | t.Error("GetAncestorsGraph(v5ID) returned empty new id") 849 | } 850 | if newId != v5ID { 851 | t.Errorf("GetAncestorsGraph(v5ID) returned new id %s, want %s", newId, v5ID) 852 | } 853 | if d.GetOrder() != 5 { 854 | t.Errorf("GetOrder() = %d, want 5", d.GetOrder()) 855 | } 856 | if d.GetSize() != 5 { 857 | t.Errorf("GetSize() = %d, want 5", d.GetSize()) 858 | } 859 | roots := d.GetRoots() 860 | if len(roots) != 1 { 861 | t.Errorf("len(GetRoots()) = %d, want 1", len(roots)) 862 | } 863 | leaves := d.GetLeaves() 864 | if len(leaves) != 1 { 865 | t.Errorf("len(GetRoots()) = %d, want 1", len(leaves)) 866 | } 867 | if _, exists := leaves[newId]; !exists { 868 | t.Errorf("%s is not the leaves of the new graph", newId) 869 | } 870 | if v5 != leaves[newId] { 871 | t.Errorf("wrong leaf got = %v, want %v", v5, leaves[newId]) 872 | } 873 | 874 | _, errGetUnknown := d.GetVertex(v9ID) 875 | if errGetUnknown == nil { 876 | t.Errorf("GetVertex(v9ID) = nil, want %T", IDUnknownError{v9ID}) 877 | } 878 | if _, ok := errGetUnknown.(IDUnknownError); !ok { 879 | t.Errorf("GetVertex(v9ID) expected IDUnknownError, got %T", errGetUnknown) 880 | } 881 | 882 | // nil 883 | _, _, errNil := d0.GetAncestorsGraph("") 884 | if errNil == nil { 885 | t.Errorf("GetDescendantsGraph(\"\") = nil, want %T", IDEmptyError{}) 886 | } 887 | if _, ok := errNil.(IDEmptyError); !ok { 888 | t.Errorf("GetDescendantsGraph(\"\") expected IDEmptyError, got %T", errNil) 889 | } 890 | 891 | // unknown 892 | _, _, errUnknown := d0.GetAncestorsGraph("foo") 893 | if errUnknown == nil { 894 | t.Errorf("GetDescendantsGraph(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 895 | } 896 | if _, ok := errUnknown.(IDUnknownError); !ok { 897 | t.Errorf("GetDescendantsGraph(\"foo\") expected IDUnknownError, got %T", errUnknown) 898 | } 899 | } 900 | 901 | func TestDAG_GetAncestors(t *testing.T) { 902 | dag := NewDAG() 903 | v0, _ := dag.AddVertex("0") 904 | v1, _ := dag.AddVertex("1") 905 | v2, _ := dag.AddVertex("2") 906 | v3, _ := dag.AddVertex("3") 907 | v4, _ := dag.AddVertex("4") 908 | v5, _ := dag.AddVertex("5") 909 | v6, _ := dag.AddVertex("6") 910 | v7, _ := dag.AddVertex("7") 911 | 912 | _ = dag.AddEdge(v1, v2) 913 | _ = dag.AddEdge(v2, v3) 914 | _ = dag.AddEdge(v2, v4) 915 | 916 | if ancestors, _ := dag.GetAncestors(v4); len(ancestors) != 2 { 917 | t.Errorf("GetAncestors(v4) = %d, want 2", len(ancestors)) 918 | } 919 | if ancestors, _ := dag.GetAncestors(v3); len(ancestors) != 2 { 920 | t.Errorf("GetAncestors(v3) = %d, want 2", len(ancestors)) 921 | } 922 | if ancestors, _ := dag.GetAncestors(v2); len(ancestors) != 1 { 923 | t.Errorf("GetAncestors(v2) = %d, want 1", len(ancestors)) 924 | } 925 | if ancestors, _ := dag.GetAncestors(v1); len(ancestors) != 0 { 926 | t.Errorf("GetAncestors(v1) = %d, want 0", len(ancestors)) 927 | } 928 | 929 | _ = dag.AddEdge(v3, v5) 930 | _ = dag.AddEdge(v4, v6) 931 | 932 | if ancestors, _ := dag.GetAncestors(v4); len(ancestors) != 2 { 933 | t.Errorf("GetAncestors(v4) = %d, want 2", len(ancestors)) 934 | } 935 | if ancestors, _ := dag.GetAncestors(v7); len(ancestors) != 0 { 936 | t.Errorf("GetAncestors(v4) = %d, want 7", len(ancestors)) 937 | } 938 | _ = dag.AddEdge(v5, v7) 939 | if ancestors, _ := dag.GetAncestors(v7); len(ancestors) != 4 { 940 | t.Errorf("GetAncestors(v7) = %d, want 4", len(ancestors)) 941 | } 942 | _ = dag.AddEdge(v0, v1) 943 | if ancestors, _ := dag.GetAncestors(v7); len(ancestors) != 5 { 944 | t.Errorf("GetAncestors(v7) = %d, want 5", len(ancestors)) 945 | } 946 | 947 | // nil 948 | _, errNil := dag.GetAncestors("") 949 | if errNil == nil { 950 | t.Errorf("GetAncestors(\"\") = nil, want %T", IDEmptyError{}) 951 | } 952 | if _, ok := errNil.(IDEmptyError); !ok { 953 | t.Errorf("GetAncestors(\"\") expected IDEmptyError, got %T", errNil) 954 | } 955 | 956 | // unknown 957 | _, errUnknown := dag.GetAncestors("foo") 958 | if errUnknown == nil { 959 | t.Errorf("GetAncestors(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 960 | } 961 | if _, ok := errUnknown.(IDUnknownError); !ok { 962 | t.Errorf("GetAncestors(\"foo\") expected IDUnknownError, got %T", errUnknown) 963 | } 964 | 965 | } 966 | 967 | func TestDAG_GetOrderedAncestors(t *testing.T) { 968 | dag := NewDAG() 969 | v1, _ := dag.addVertex("1") 970 | v2, _ := dag.addVertex("2") 971 | v3, _ := dag.addVertex("3") 972 | v4, _ := dag.addVertex("4") 973 | _ = dag.AddEdge(v1, v2) 974 | _ = dag.AddEdge(v2, v3) 975 | _ = dag.AddEdge(v2, v4) 976 | 977 | if desc, _ := dag.GetOrderedAncestors(v4); len(desc) != 2 { 978 | t.Errorf("GetOrderedAncestors(v4) = %d, want 2", len(desc)) 979 | } 980 | if desc, _ := dag.GetOrderedAncestors(v2); len(desc) != 1 { 981 | t.Errorf("GetOrderedAncestors(v2) = %d, want 1", len(desc)) 982 | } 983 | if desc, _ := dag.GetOrderedAncestors(v1); len(desc) != 0 { 984 | t.Errorf("GetOrderedAncestors(v1) = %d, want 0", len(desc)) 985 | } 986 | if desc, _ := dag.GetOrderedAncestors(v4); !equal(desc, []string{v2, v1}) { 987 | t.Errorf("GetOrderedAncestors(v4) = %v, want %v", desc, []interface{}{v2, v1}) 988 | } 989 | 990 | // nil 991 | _, errNil := dag.GetOrderedAncestors("") 992 | if errNil == nil { 993 | t.Errorf("GetOrderedAncestors(\"\") = nil, want %T", IDEmptyError{}) 994 | } 995 | if _, ok := errNil.(IDEmptyError); !ok { 996 | t.Errorf("GetOrderedAncestors(\"\") expected IDEmptyError, got %T", errNil) 997 | } 998 | 999 | // unknown 1000 | _, errUnknown := dag.GetOrderedAncestors("foo") 1001 | if errUnknown == nil { 1002 | t.Errorf("GetOrderedAncestors(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 1003 | } 1004 | if _, ok := errUnknown.(IDUnknownError); !ok { 1005 | t.Errorf("GetOrderedAncestors(\"foo\") expected IDUnknownError, got %T", errUnknown) 1006 | } 1007 | } 1008 | 1009 | func TestDAG_AncestorsWalker(t *testing.T) { 1010 | dag := NewDAG() 1011 | v1, _ := dag.AddVertex("1") 1012 | v2, _ := dag.AddVertex("2") 1013 | v3, _ := dag.AddVertex("3") 1014 | v4, _ := dag.AddVertex("4") 1015 | v5, _ := dag.AddVertex("5") 1016 | v6, _ := dag.AddVertex("6") 1017 | v7, _ := dag.AddVertex("7") 1018 | v8, _ := dag.AddVertex("8") 1019 | v9, _ := dag.AddVertex("9") 1020 | v10, _ := dag.AddVertex("101") 1021 | 1022 | _ = dag.AddEdge(v1, v2) 1023 | _ = dag.AddEdge(v1, v3) 1024 | _ = dag.AddEdge(v2, v4) 1025 | _ = dag.AddEdge(v2, v5) 1026 | _ = dag.AddEdge(v4, v6) 1027 | _ = dag.AddEdge(v5, v6) 1028 | _ = dag.AddEdge(v6, v7) 1029 | _ = dag.AddEdge(v7, v8) 1030 | _ = dag.AddEdge(v7, v9) 1031 | _ = dag.AddEdge(v8, v10) 1032 | _ = dag.AddEdge(v9, v10) 1033 | 1034 | vertices, _, _ := dag.AncestorsWalker(v10) 1035 | var ancestors []string 1036 | for v := range vertices { 1037 | ancestors = append(ancestors, v) 1038 | } 1039 | exp1 := []string{v9, v8, v7, v6, v4, v5, v2, v1} 1040 | exp2 := []string{v8, v9, v7, v6, v4, v5, v2, v1} 1041 | exp3 := []string{v9, v8, v7, v6, v5, v4, v2, v1} 1042 | exp4 := []string{v8, v9, v7, v6, v5, v4, v2, v1} 1043 | if !(equal(ancestors, exp1) || equal(ancestors, exp2) || equal(ancestors, exp3) || equal(ancestors, exp4)) { 1044 | t.Errorf("AncestorsWalker(v10) = %v, want %v, %v, %v, or %v ", ancestors, exp1, exp2, exp3, exp4) 1045 | } 1046 | 1047 | // nil 1048 | _, _, errNil := dag.AncestorsWalker("") 1049 | if errNil == nil { 1050 | t.Errorf("AncestorsWalker(\"\") = nil, want %T", IDEmptyError{}) 1051 | } 1052 | if _, ok := errNil.(IDEmptyError); !ok { 1053 | t.Errorf("AncestorsWalker(\"\") expected IDEmptyError, got %T", errNil) 1054 | } 1055 | 1056 | // unknown 1057 | _, _, errUnknown := dag.AncestorsWalker("foo") 1058 | if errUnknown == nil { 1059 | t.Errorf("AncestorsWalker(\"foo\") = nil, want %T", IDUnknownError{"foo"}) 1060 | } 1061 | if _, ok := errUnknown.(IDUnknownError); !ok { 1062 | t.Errorf("AncestorsWalker(\"foo\") expected IDUnknownError, got %T", errUnknown) 1063 | } 1064 | } 1065 | 1066 | func TestDAG_AncestorsWalkerSignal(t *testing.T) { 1067 | dag := NewDAG() 1068 | 1069 | v1, _ := dag.AddVertex("1") 1070 | v2, _ := dag.AddVertex("2") 1071 | v3, _ := dag.AddVertex("3") 1072 | v4, _ := dag.AddVertex("4") 1073 | v5, _ := dag.AddVertex("5") 1074 | _ = dag.AddEdge(v1, v2) 1075 | _ = dag.AddEdge(v2, v3) 1076 | _ = dag.AddEdge(v2, v4) 1077 | _ = dag.AddEdge(v4, v5) 1078 | 1079 | var ancestors []string 1080 | vertices, signal, _ := dag.AncestorsWalker(v5) 1081 | for v := range vertices { 1082 | ancestors = append(ancestors, v) 1083 | if v == v2 { 1084 | signal <- true 1085 | break 1086 | } 1087 | } 1088 | if !equal(ancestors, []string{v4, v2}) { 1089 | t.Errorf("AncestorsWalker(v4) = %v, want %v", ancestors, []string{v4, v2}) 1090 | } 1091 | 1092 | } 1093 | 1094 | func TestDAG_ReduceTransitively(t *testing.T) { 1095 | dag := NewDAG() 1096 | accountCreate, _ := dag.AddVertex("AccountCreate") 1097 | projectCreate, _ := dag.AddVertex("ProjectCreate") 1098 | networkCreate, _ := dag.AddVertex("NetworkCreate") 1099 | contactCreate, _ := dag.AddVertex("ContactCreate") 1100 | authCreate, _ := dag.AddVertex("AuthCreate") 1101 | mailSend, _ := dag.AddVertex("MailSend") 1102 | 1103 | _ = dag.AddEdge(accountCreate, projectCreate) 1104 | _ = dag.AddEdge(accountCreate, networkCreate) 1105 | _ = dag.AddEdge(accountCreate, contactCreate) 1106 | _ = dag.AddEdge(accountCreate, authCreate) 1107 | _ = dag.AddEdge(accountCreate, mailSend) 1108 | 1109 | _ = dag.AddEdge(projectCreate, mailSend) 1110 | _ = dag.AddEdge(networkCreate, mailSend) 1111 | _ = dag.AddEdge(contactCreate, mailSend) 1112 | _ = dag.AddEdge(authCreate, mailSend) 1113 | 1114 | if order := dag.GetOrder(); order != 6 { 1115 | t.Errorf("GetOrder() = %d, want 6", order) 1116 | } 1117 | if size := dag.GetSize(); size != 9 { 1118 | t.Errorf("GetSize() = %d, want 9", size) 1119 | } 1120 | if isEdge, _ := dag.IsEdge(accountCreate, mailSend); !isEdge { 1121 | t.Errorf("IsEdge(accountCreate, mailSend) = %t, want %t", isEdge, true) 1122 | } 1123 | 1124 | dag.ReduceTransitively() 1125 | 1126 | if order := dag.GetOrder(); order != 6 { 1127 | t.Errorf("GetOrder() = %d, want 6", order) 1128 | } 1129 | if size := dag.GetSize(); size != 8 { 1130 | t.Errorf("GetSize() = %d, want 8", size) 1131 | } 1132 | if isEdge, _ := dag.IsEdge(accountCreate, mailSend); isEdge { 1133 | t.Errorf("IsEdge(accountCreate, mailSend) = %t, want %t", isEdge, false) 1134 | } 1135 | 1136 | ordered, _ := dag.GetOrderedDescendants(accountCreate) 1137 | length := len(ordered) 1138 | if length != 5 { 1139 | t.Errorf("length(ordered) = %d, want 5", length) 1140 | } 1141 | last := ordered[length-1] 1142 | if last != mailSend { 1143 | t.Errorf("ordered[length-1]) = %v, want %v", last, mailSend) 1144 | } 1145 | } 1146 | 1147 | func TestDAG_Copy(t *testing.T) { 1148 | d0 := NewDAG() 1149 | 1150 | _, _ = d0.AddVertex(iVertex{1}) 1151 | _, _ = d0.AddVertex(iVertex{2}) 1152 | _, _ = d0.AddVertex(iVertex{3}) 1153 | _, _ = d0.AddVertex(iVertex{4}) 1154 | _, _ = d0.AddVertex(iVertex{5}) 1155 | _, _ = d0.AddVertex(iVertex{6}) 1156 | _, _ = d0.AddVertex(iVertex{7}) 1157 | _, _ = d0.AddVertex(iVertex{8}) 1158 | _, _ = d0.AddVertex(iVertex{9}) 1159 | 1160 | _ = d0.AddEdge("1", "2") 1161 | _ = d0.AddEdge("2", "3") 1162 | _ = d0.AddEdge("2", "4") 1163 | _ = d0.AddEdge("3", "5") 1164 | _ = d0.AddEdge("4", "5") 1165 | _ = d0.AddEdge("5", "6") 1166 | _ = d0.AddEdge("6", "7") 1167 | _ = d0.AddEdge("6", "8") 1168 | 1169 | d1, err := d0.Copy() 1170 | if err != nil { 1171 | t.Error(err) 1172 | } 1173 | if d1.GetOrder() != d0.GetOrder() { 1174 | t.Errorf("got %d, want %d", d1.GetOrder(), d0.GetOrder()) 1175 | } 1176 | if d1.GetSize() != d0.GetSize() { 1177 | t.Errorf("got %d, want %d", d1.GetSize(), d0.GetSize()) 1178 | } 1179 | if len(d1.GetRoots()) != len(d0.GetRoots()) { 1180 | t.Errorf("got %d, want %d", len(d1.GetRoots()), len(d0.GetRoots())) 1181 | } 1182 | if len(d1.GetLeaves()) != len(d0.GetLeaves()) { 1183 | t.Errorf("got %d, want %d", len(d1.GetLeaves()), len(d0.GetLeaves())) 1184 | } 1185 | for i := 1; i < 9; i++ { 1186 | v1, errGet1 := d1.GetVertex(strconv.Itoa(i)) 1187 | if errGet1 != nil { 1188 | t.Error(errGet1) 1189 | } 1190 | v2, errGet2 := d1.GetVertex(strconv.Itoa(i)) 1191 | if errGet2 != nil { 1192 | t.Error(errGet2) 1193 | } 1194 | if v2 != v1 { 1195 | t.Errorf("got %v, want %v", v2, v1) 1196 | } 1197 | } 1198 | } 1199 | 1200 | func TestDAG_String(t *testing.T) { 1201 | dag := NewDAG() 1202 | v1, _ := dag.AddVertex("1") 1203 | v2, _ := dag.AddVertex("2") 1204 | v3, _ := dag.AddVertex("3") 1205 | v4, _ := dag.AddVertex("4") 1206 | _ = dag.AddEdge(v1, v2) 1207 | _ = dag.AddEdge(v2, v3) 1208 | _ = dag.AddEdge(v2, v4) 1209 | expected := "DAG Vertices: 4 - Edges: 3" 1210 | s := dag.String() 1211 | if s[:len(expected)] != expected { 1212 | t.Errorf("String() = \"%s\", want \"%s\"", s, expected) 1213 | } 1214 | } 1215 | 1216 | func TestErrors(t *testing.T) { 1217 | 1218 | tests := []struct { 1219 | want string 1220 | err error 1221 | }{ 1222 | {"don't know what to do with \"\"", IDEmptyError{}}, 1223 | {"'1' is already known", VertexDuplicateError{"1"}}, 1224 | {"'1' is unknown", IDUnknownError{"1"}}, 1225 | {"edge between '1' and '2' is already known", EdgeDuplicateError{"1", "2"}}, 1226 | {"edge between '1' and '2' is unknown", EdgeUnknownError{"1", "2"}}, 1227 | {"edge between '1' and '2' would create a loop", EdgeLoopError{"1", "2"}}, 1228 | } 1229 | for _, tt := range tests { 1230 | t.Run(fmt.Sprintf("%T", tt.err), func(t *testing.T) { 1231 | if got := tt.err.Error(); got != tt.want { 1232 | t.Errorf("Error() = %v, want %v", got, tt.want) 1233 | } 1234 | }) 1235 | } 1236 | } 1237 | 1238 | func ExampleDAG_AncestorsWalker() { 1239 | dag := NewDAG() 1240 | 1241 | v1, _ := dag.AddVertex(iVertex{1}) 1242 | v2, _ := dag.AddVertex(iVertex{2}) 1243 | v3, _ := dag.AddVertex(iVertex{3}) 1244 | v4, _ := dag.AddVertex(iVertex{4}) 1245 | v5, _ := dag.AddVertex(iVertex{5}) 1246 | _ = dag.AddEdge(v1, v2) 1247 | _ = dag.AddEdge(v2, v3) 1248 | _ = dag.AddEdge(v2, v4) 1249 | _ = dag.AddEdge(v4, v5) 1250 | 1251 | var ancestors []interface{} 1252 | vertices, signal, _ := dag.AncestorsWalker(v5) 1253 | for v := range vertices { 1254 | ancestors = append(ancestors, v) 1255 | if v == v2 { 1256 | signal <- true 1257 | break 1258 | } 1259 | } 1260 | fmt.Printf("%v", ancestors) 1261 | 1262 | // Output: 1263 | // [4 2] 1264 | } 1265 | 1266 | func TestLarge(t *testing.T) { 1267 | d := NewDAG() 1268 | root := iVertex{1} 1269 | id, _ := d.addVertex(root) 1270 | levels := 7 1271 | branches := 8 1272 | 1273 | expectedVertexCount, _ := largeAux(d, levels, branches, root) 1274 | expectedVertexCount++ 1275 | vertexCount := len(d.GetVertices()) 1276 | if vertexCount != expectedVertexCount { 1277 | t.Errorf("GetVertices() = %d, want %d", vertexCount, expectedVertexCount) 1278 | } 1279 | 1280 | descendants, _ := d.GetDescendants(id) 1281 | descendantsCount := len(descendants) 1282 | expectedDescendantsCount := vertexCount - 1 1283 | if descendantsCount != expectedDescendantsCount { 1284 | t.Errorf("GetDescendants(root) = %d, want %d", descendantsCount, expectedDescendantsCount) 1285 | } 1286 | 1287 | _, _ = d.GetDescendants(id) 1288 | 1289 | children, _ := d.GetChildren(id) 1290 | childrenCount := len(children) 1291 | expectedChildrenCount := branches 1292 | if childrenCount != expectedChildrenCount { 1293 | t.Errorf("GetChildren(root) = %d, want %d", childrenCount, expectedChildrenCount) 1294 | } 1295 | 1296 | /* 1297 | var childList []interface{} 1298 | for x := range children { 1299 | childList = append(childList, x) 1300 | } 1301 | _ = d.DeleteEdge(root, childList[0]) 1302 | */ 1303 | } 1304 | 1305 | func TestDAG_DescendantsFlowOneNode(t *testing.T) { 1306 | // Initialize a new graph. 1307 | d := NewDAG() 1308 | 1309 | // Init vertices. 1310 | v0, _ := d.AddVertex(0) 1311 | 1312 | // The callback function adds its own value (ID) to the sum of parent results. 1313 | flowCallback := func(d *DAG, id string, parentResults []FlowResult) (interface{}, error) { 1314 | 1315 | v, _ := d.GetVertex(id) 1316 | result, _ := v.(int) 1317 | var parents []int 1318 | for _, r := range parentResults { 1319 | p, _ := d.GetVertex(r.ID) 1320 | parents = append(parents, p.(int)) 1321 | result += r.Result.(int) 1322 | } 1323 | sort.Ints(parents) 1324 | fmt.Printf("%v based on: %+v returns: %d\n", v, parents, result) 1325 | return result, nil 1326 | } 1327 | 1328 | res, _ := d.DescendantsFlow(v0, nil, flowCallback) 1329 | if len(res) != 1 { 1330 | t.Errorf("DescendantsFlow() = %d, want 1", len(res)) 1331 | } 1332 | } 1333 | 1334 | func largeAux(d *DAG, level int, branches int, parent iVertex) (int, int) { 1335 | var vertexCount int 1336 | var edgeCount int 1337 | if level > 1 { 1338 | if branches < 1 || branches > 9 { 1339 | panic("number of branches must be between 1 and 9") 1340 | } 1341 | for i := 1; i <= branches; i++ { 1342 | value := parent.value*10 + i 1343 | child := iVertex{value} 1344 | childID, _ := d.AddVertex(child) 1345 | vertexCount++ 1346 | err := d.AddEdge(parent.ID(), childID) 1347 | edgeCount++ 1348 | if err != nil { 1349 | panic(err) 1350 | } 1351 | childVertexCount, childEdgeCount := largeAux(d, level-1, branches, child) 1352 | vertexCount += childVertexCount 1353 | edgeCount += childEdgeCount 1354 | } 1355 | } 1356 | return vertexCount, edgeCount 1357 | } 1358 | -------------------------------------------------------------------------------- /example_basic_test.go: -------------------------------------------------------------------------------- 1 | package dag_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/heimdalr/dag" 6 | ) 7 | 8 | type foobar struct { 9 | a string 10 | b string 11 | } 12 | 13 | func Example() { 14 | 15 | // initialize a new graph 16 | d := dag.NewDAG() 17 | 18 | // init three vertices 19 | v1, _ := d.AddVertex(1) 20 | v2, _ := d.AddVertex(2) 21 | v3, _ := d.AddVertex(foobar{a: "foo", b: "bar"}) 22 | 23 | // add the above vertices and connect them with two edges 24 | _ = d.AddEdge(v1, v2) 25 | _ = d.AddEdge(v1, v3) 26 | 27 | // describe the graph 28 | fmt.Print(d.String()) 29 | 30 | // Unordered output: 31 | // DAG Vertices: 3 - Edges: 2 32 | // Vertices: 33 | // 1 34 | // 2 35 | // {foo bar} 36 | // Edges: 37 | // 1 -> 2 38 | // 1 -> {foo bar} 39 | } 40 | -------------------------------------------------------------------------------- /example_descandentsFlow_test.go: -------------------------------------------------------------------------------- 1 | package dag_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/heimdalr/dag" 6 | "sort" 7 | ) 8 | 9 | func ExampleDAG_DescendantsFlow() { 10 | // Initialize a new graph. 11 | d := dag.NewDAG() 12 | 13 | // Init vertices. 14 | v0, _ := d.AddVertex(0) 15 | v1, _ := d.AddVertex(1) 16 | v2, _ := d.AddVertex(2) 17 | v3, _ := d.AddVertex(3) 18 | v4, _ := d.AddVertex(4) 19 | 20 | // Add the above vertices and connect them. 21 | _ = d.AddEdge(v0, v1) 22 | _ = d.AddEdge(v0, v3) 23 | _ = d.AddEdge(v1, v2) 24 | _ = d.AddEdge(v2, v4) 25 | _ = d.AddEdge(v3, v4) 26 | 27 | // 0 28 | // ┌─┴─┐ 29 | // 1 │ 30 | // │ 3 31 | // 2 │ 32 | // └─┬─┘ 33 | // 4 34 | 35 | // The callback function adds its own value (ID) to the sum of parent results. 36 | flowCallback := func(d *dag.DAG, id string, parentResults []dag.FlowResult) (interface{}, error) { 37 | 38 | v, _ := d.GetVertex(id) 39 | result, _ := v.(int) 40 | var parents []int 41 | for _, r := range parentResults { 42 | p, _ := d.GetVertex(r.ID) 43 | parents = append(parents, p.(int)) 44 | result += r.Result.(int) 45 | } 46 | sort.Ints(parents) 47 | fmt.Printf("%v based on: %+v returns: %d\n", v, parents, result) 48 | return result, nil 49 | } 50 | 51 | _, _ = d.DescendantsFlow(v0, nil, flowCallback) 52 | 53 | // Unordered output: 54 | // 0 based on: [] returns: 0 55 | // 1 based on: [0] returns: 1 56 | // 3 based on: [0] returns: 3 57 | // 2 based on: [1] returns: 3 58 | // 4 based on: [2 3] returns: 10 59 | } 60 | -------------------------------------------------------------------------------- /example_idinterface_test.go: -------------------------------------------------------------------------------- 1 | package dag_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/heimdalr/dag" 6 | ) 7 | 8 | type idVertex struct { 9 | id string 10 | msg string 11 | } 12 | 13 | func (v idVertex) ID() string { 14 | return v.id 15 | } 16 | 17 | func ExampleIDInterface() { 18 | 19 | // initialize a new graph 20 | d := dag.NewDAG() 21 | 22 | // init three vertices 23 | id, _ := d.AddVertex(idVertex{id: "1", msg: "foo"}) 24 | fmt.Printf("id of vertex is %s\n", id) 25 | v, _ := d.GetVertex(id) 26 | fmt.Printf("%s", v) 27 | 28 | // Output: 29 | // id of vertex is 1 30 | // {1 foo} 31 | } 32 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/heimdalr/dag 2 | 3 | go 1.12 4 | 5 | // require github.com/hashicorp/terraform v0.12.20 6 | 7 | require ( 8 | github.com/emirpasic/gods v1.18.1 9 | github.com/go-test/deep v1.1.0 10 | github.com/google/uuid v1.3.0 11 | ) 12 | 13 | retract [v1.4.1, v1.4.11] -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 2 | github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 3 | github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= 4 | github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= 5 | github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= 6 | github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 7 | github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= 8 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 9 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 10 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 11 | -------------------------------------------------------------------------------- /marshal.go: -------------------------------------------------------------------------------- 1 | package dag 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | // MarshalJSON returns the JSON encoding of DAG. 9 | // 10 | // It traverses the DAG using the Depth-First-Search algorithm 11 | // and uses an internal structure to store vertices and edges. 12 | func (d *DAG) MarshalJSON() ([]byte, error) { 13 | mv := newMarshalVisitor(d) 14 | d.DFSWalk(mv) 15 | return json.Marshal(mv.storableDAG) 16 | } 17 | 18 | // UnmarshalJSON is an informative method. See the UnmarshalJSON function below. 19 | func (d *DAG) UnmarshalJSON(_ []byte) error { 20 | return errors.New("this method is not supported, request function UnmarshalJSON instead") 21 | } 22 | 23 | // UnmarshalJSON parses the JSON-encoded data that defined by StorableDAG. 24 | // It returns a new DAG defined by the vertices and edges of wd. 25 | // If the internal structure of data and wd do not match, 26 | // then deserialization will fail and return json error. 27 | // 28 | // Because the vertex data passed in by the user is an interface{}, 29 | // it does not indicate a specific structure, so it cannot be deserialized. 30 | // And this function needs to pass in a clear DAG structure. 31 | // 32 | // Example: 33 | // dag := NewDAG() 34 | // data, err := json.Marshal(d) 35 | // if err != nil { 36 | // panic(err) 37 | // } 38 | // var wd YourStorableDAG 39 | // restoredDag, err := UnmarshalJSON(data, &wd) 40 | // if err != nil { 41 | // panic(err) 42 | // } 43 | // 44 | // For more specific information please read the test code. 45 | func UnmarshalJSON(data []byte, wd StorableDAG, options Options) (*DAG, error) { 46 | err := json.Unmarshal(data, &wd) 47 | if err != nil { 48 | return nil, err 49 | } 50 | dag := NewDAG() 51 | dag.Options(options) 52 | for _, v := range wd.Vertices() { 53 | errVertex := dag.AddVertexByID(v.Vertex()) 54 | if errVertex != nil { 55 | return nil, errVertex 56 | } 57 | } 58 | for _, e := range wd.Edges() { 59 | errEdge := dag.AddEdge(e.Edge()) 60 | if errEdge != nil { 61 | return nil, errEdge 62 | } 63 | } 64 | return dag, nil 65 | } 66 | 67 | type marshalVisitor struct { 68 | d *DAG 69 | storableDAG 70 | } 71 | 72 | func newMarshalVisitor(d *DAG) *marshalVisitor { 73 | return &marshalVisitor{d: d} 74 | } 75 | 76 | func (mv *marshalVisitor) Visit(v Vertexer) { 77 | mv.StorableVertices = append(mv.StorableVertices, v) 78 | 79 | srcID, _ := v.Vertex() 80 | // Why not use Mutex here? 81 | // Because at the time of Walk, 82 | // the read lock has been used to protect the dag. 83 | children, _ := mv.d.getChildren(srcID) 84 | ids := vertexIDs(children) 85 | for _, dstID := range ids { 86 | e := storableEdge{SrcID: srcID, DstID: dstID} 87 | mv.StorableEdges = append(mv.StorableEdges, e) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /marshal_test.go: -------------------------------------------------------------------------------- 1 | package dag 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | "github.com/go-test/deep" 8 | ) 9 | 10 | func TestMarshalUnmarshalJSON(t *testing.T) { 11 | cases := []struct { 12 | dag *DAG 13 | expected string 14 | }{ 15 | { 16 | dag: getTestWalkDAG(), 17 | expected: `{"vs":[{"i":"1","v":"v1"},{"i":"2","v":"v2"},{"i":"3","v":"v3"},{"i":"4","v":"v4"},{"i":"5","v":"v5"}],"es":[{"s":"1","d":"2"},{"s":"2","d":"3"},{"s":"2","d":"4"},{"s":"4","d":"5"}]}`, 18 | }, 19 | { 20 | dag: getTestWalkDAG2(), 21 | expected: `{"vs":[{"i":"1","v":"v1"},{"i":"3","v":"v3"},{"i":"5","v":"v5"},{"i":"2","v":"v2"},{"i":"4","v":"v4"}],"es":[{"s":"1","d":"3"},{"s":"3","d":"5"},{"s":"2","d":"3"},{"s":"4","d":"5"}]}`, 22 | }, 23 | { 24 | dag: getTestWalkDAG3(), 25 | expected: `{"vs":[{"i":"1","v":"v1"},{"i":"3","v":"v3"},{"i":"2","v":"v2"},{"i":"4","v":"v4"},{"i":"5","v":"v5"}],"es":[{"s":"1","d":"3"},{"s":"2","d":"3"},{"s":"4","d":"5"}]}`, 26 | }, 27 | { 28 | dag: getTestWalkDAG4(), 29 | expected: `{"vs":[{"i":"1","v":"v1"},{"i":"2","v":"v2"},{"i":"3","v":"v3"},{"i":"5","v":"v5"},{"i":"4","v":"v4"}],"es":[{"s":"1","d":"2"},{"s":"2","d":"3"},{"s":"2","d":"4"},{"s":"3","d":"5"}]}`, 30 | }, 31 | } 32 | 33 | for _, c := range cases { 34 | testMarshalUnmarshalJSON(t, c.dag, c.expected) 35 | } 36 | } 37 | 38 | func testMarshalUnmarshalJSON(t *testing.T, d *DAG, expected string) { 39 | data, err := json.Marshal(d) 40 | if err != nil { 41 | t.Error(err) 42 | } 43 | 44 | actual := string(data) 45 | if deep.Equal(expected, actual) != nil { 46 | t.Errorf("Marshal() = %v, want %v", actual, expected) 47 | } 48 | 49 | d1 := &DAG{} 50 | errNotSupported := json.Unmarshal(data, d1) 51 | if errNotSupported == nil { 52 | t.Errorf("UnmarshalJSON() = nil, want %v", "This method is not supported") 53 | } 54 | 55 | var wd testStorableDAG 56 | dag, err := UnmarshalJSON(data, &wd, defaultOptions()) 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | if deep.Equal(d, dag) != nil { 61 | t.Errorf("UnmarshalJSON() = %v, want %v", dag.String(), d.String()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | package dag 2 | 3 | // Options is the configuration for the DAG. 4 | type Options struct { 5 | // VertexHashFunc is the function that calculates the hash value of a vertex. 6 | // This can be useful when the vertex contains not comparable types such as maps. 7 | // If VertexHashFunc is nil, the defaultVertexHashFunc is used. 8 | VertexHashFunc func(v interface{}) interface{} 9 | } 10 | 11 | // Options sets the options for the DAG. 12 | // Options must be called before any other method of the DAG is called. 13 | func (d *DAG) Options(options Options) { 14 | d.muDAG.Lock() 15 | defer d.muDAG.Unlock() 16 | d.options = options 17 | } 18 | 19 | func defaultOptions() Options { 20 | return Options{ 21 | VertexHashFunc: defaultVertexHashFunc, 22 | } 23 | } 24 | 25 | func defaultVertexHashFunc(v interface{}) interface{} { 26 | return v 27 | } 28 | -------------------------------------------------------------------------------- /options_test.go: -------------------------------------------------------------------------------- 1 | package dag 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | type testNonComparableVertexType struct { 9 | ID string `json:"i"` 10 | NotComparableField map[string]string `json:"v"` 11 | } 12 | 13 | func TestOverrideVertexHashFunOption(t *testing.T) { 14 | dag := NewDAG() 15 | /* 1 4 16 | * |\ / 17 | * | 2 18 | * |/ 19 | * 3 20 | */ 21 | 22 | dag.Options(Options{ 23 | VertexHashFunc: func(v interface{}) interface{} { 24 | return v.(testNonComparableVertexType).ID 25 | }}) 26 | 27 | testVertex1 := testNonComparableVertexType{ 28 | ID: "1", 29 | NotComparableField: map[string]string{"not": "comparable"}, 30 | } 31 | vertexId1, err := dag.addVertex(testVertex1) 32 | if err != nil { 33 | t.Errorf("Should create a vertex with a not comparable field when a correct VertexHashFunc option is set") 34 | } 35 | 36 | testVertex2 := testNonComparableVertexType{ 37 | ID: "2", 38 | NotComparableField: map[string]string{"stillNot": "comparable"}, 39 | } 40 | vertexId2, err := dag.addVertex(testVertex2) 41 | if err != nil { 42 | t.Errorf("Should create a vertex with a not comparable field when a correct VertexHashFunc option is set") 43 | } 44 | err = dag.AddEdge(vertexId1, vertexId2) 45 | if err != nil { 46 | t.Errorf("Should create an edge between vertices with not comparable fields when a correct VertexHashFunc option is set") 47 | } 48 | 49 | testVertex3 := testNonComparableVertexType{ 50 | ID: "3", 51 | NotComparableField: map[string]string{"stillNot": "comparable"}, 52 | } 53 | vertexId3, err := dag.addVertex(testVertex3) 54 | if err != nil { 55 | t.Errorf("Should create a vertex with a not comparable field when a correct VertexHashFunc option is set") 56 | } 57 | 58 | err = dag.AddEdge(vertexId1, vertexId3) 59 | if err != nil { 60 | t.Errorf("Should create an edge between vertices with not comparable fields when a correct VertexHashFunc option is set") 61 | } 62 | err = dag.AddEdge(vertexId2, vertexId3) 63 | if err != nil { 64 | t.Errorf("Should create an edge between vertices with not comparable fields when a correct VertexHashFunc option is set") 65 | } 66 | 67 | testVertex4 := testNonComparableVertexType{ 68 | ID: "4", 69 | NotComparableField: map[string]string{"stillNot": "comparable"}, 70 | } 71 | vertexId4, err := dag.addVertex(testVertex4) 72 | if err != nil { 73 | t.Errorf("Should create a vertex with a not comparable field when a correct VertexHashFunc option is set") 74 | } 75 | 76 | err = dag.AddEdge(vertexId4, vertexId2) 77 | if err != nil { 78 | t.Errorf("Should create an edge between vertices with not comparable fields when a correct VertexHashFunc option is set") 79 | } 80 | 81 | isEdge, err := dag.IsEdge(vertexId1, vertexId2) 82 | if !isEdge || err != nil { 83 | t.Errorf("Should return true for edge between vertices with not comparable fields when a correct VertexHashFunc option is set") 84 | } 85 | 86 | err = dag.DeleteEdge(vertexId1, vertexId3) 87 | if err != nil { 88 | t.Errorf("Should delete an edge between vertices with not comparable fields when a correct VertexHashFunc option is set") 89 | } 90 | 91 | isEdge, err = dag.IsEdge(vertexId1, vertexId3) 92 | if isEdge || err != nil { 93 | t.Errorf("Should return false for edge between vertices with not comparable fields when a correct VertexHashFunc option is set") 94 | } 95 | 96 | err = dag.DeleteVertex(vertexId2) 97 | if err != nil { 98 | t.Errorf("Should delete a vertex with not comparable fields when a correct VertexHashFunc option is set") 99 | } 100 | 101 | vertexId2, err = dag.addVertex(testVertex2) 102 | if err != nil { 103 | t.Errorf("Should create a vertex with a not comparable field when a correct VertexHashFunc option is set") 104 | } 105 | 106 | _ = dag.AddEdge(vertexId1, vertexId2) 107 | _ = dag.AddEdge(vertexId2, vertexId3) 108 | err = dag.AddEdge(vertexId4, vertexId2) 109 | if err != nil { 110 | t.Errorf("Should create an edge between vertices with not comparable fields when a correct VertexHashFunc option is set") 111 | } 112 | 113 | roots := dag.GetRoots() 114 | if len(roots) != 2 { 115 | t.Errorf("Should return 2 roots") 116 | } 117 | for rootId := range roots { 118 | if isRoot, err := dag.IsRoot(rootId); !isRoot || err != nil { 119 | t.Errorf("Should return true for root") 120 | } 121 | } 122 | 123 | leaves := dag.GetLeaves() 124 | if len(leaves) != 1 { 125 | t.Errorf("Should return 1 leaf") 126 | } 127 | for leafId := range leaves { 128 | if isLeaf, err := dag.IsLeaf(leafId); !isLeaf || err != nil { 129 | t.Errorf("Should return true for leaf") 130 | } 131 | } 132 | 133 | vertex2Parents, err := dag.GetParents(vertexId2) 134 | if len(vertex2Parents) != 2 || err != nil { 135 | t.Errorf("Should return 2 parents for vertex 2") 136 | } 137 | 138 | vertex2Children, err := dag.GetChildren(vertexId2) 139 | if len(vertex2Children) != 1 || err != nil { 140 | t.Errorf("Should return 1 child for vertex 2") 141 | } 142 | 143 | vertex3Ancestors, err := dag.GetAncestors(vertexId3) 144 | if len(vertex3Ancestors) != 3 || err != nil { 145 | t.Errorf("Should return 3 ancestors for vertex 3, received %d", len(vertex3Ancestors)) 146 | } 147 | 148 | vertex3OrderedAncestors, err := dag.GetOrderedAncestors(vertexId3) 149 | if len(vertex3OrderedAncestors) != 3 || err != nil { 150 | t.Errorf("Should return 3 ancestors for vertex 3, received %d", len(vertex3OrderedAncestors)) 151 | } 152 | 153 | vertex4Descendants, err := dag.GetDescendants(vertexId4) 154 | if len(vertex4Descendants) != 2 || err != nil { 155 | t.Errorf("Should return 2 descendants for vertex 4, received %d", len(vertex4Descendants)) 156 | } 157 | 158 | vertex4OrderedDescendants, err := dag.GetOrderedDescendants(vertexId4) 159 | if len(vertex4OrderedDescendants) != 2 || err != nil { 160 | t.Errorf("Should return 2 descendants for vertex 4, received %d", len(vertex4OrderedDescendants)) 161 | } 162 | 163 | _, _, err = dag.GetDescendantsGraph(vertexId1) 164 | if err != nil { 165 | t.Errorf("Should return a string representation of the descendants graph") 166 | } 167 | 168 | _, _, err = dag.GetAncestorsGraph(vertexId1) 169 | if err != nil { 170 | t.Errorf("Should return a string representation of the ancestors graph") 171 | } 172 | 173 | _, err = dag.Copy() 174 | if err != nil { 175 | t.Errorf("Should return a copy of the DAG") 176 | } 177 | 178 | dagString := dag.String() 179 | if dagString == "" { 180 | t.Errorf("Should return a string representation of the DAG") 181 | } 182 | 183 | dag.ReduceTransitively() 184 | dag.FlushCaches() 185 | dag.DescendantsWalker(vertexId1) // nolint:errcheck 186 | 187 | mv := newMarshalVisitor(dag) 188 | dag.DFSWalk(mv) 189 | dag.BFSWalk(mv) 190 | dag.OrderedWalk(mv) 191 | 192 | _, err = dag.MarshalJSON() 193 | if err != nil { 194 | t.Error(err) 195 | } 196 | 197 | data, err := json.Marshal(dag) 198 | if err != nil { 199 | t.Error(err) 200 | } 201 | 202 | var wd testNonComparableStorableDAG 203 | _, err = UnmarshalJSON(data, &wd, dag.options) 204 | if err != nil { 205 | t.Fatal(err) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /storage.go: -------------------------------------------------------------------------------- 1 | package dag 2 | 3 | var ( 4 | _ Vertexer = (*storableVertex)(nil) 5 | _ Edger = (*storableEdge)(nil) 6 | _ StorableDAG = (*storableDAG)(nil) 7 | _ IDInterface = (*storableVertex)(nil) 8 | ) 9 | 10 | // Vertexer is the interface that wraps the basic Vertex method. 11 | // Vertex returns an id that identifies this vertex and the value of this vertex. 12 | // 13 | // The reason for defining this new structure is that the vertex id may be 14 | // automatically generated when the caller adds a vertex. At this time, the 15 | // vertex structure added by the user does not contain id information. 16 | type Vertexer interface { 17 | Vertex() (id string, value interface{}) 18 | } 19 | 20 | // Edger is the interface that wraps the basic Edge method. 21 | // Edge returns the ids of two vertices that connect an edge. 22 | type Edger interface { 23 | Edge() (srcID, dstID string) 24 | } 25 | 26 | // StorableDAG is the interface that defines a DAG that can be stored. 27 | // It provides methods to get all vertices and all edges of a DAG. 28 | type StorableDAG interface { 29 | Vertices() []Vertexer 30 | Edges() []Edger 31 | } 32 | 33 | // storableVertex implements the Vertexer interface. 34 | // It is implemented as a storable structure. 35 | // And it uses short json tag to reduce the number of bytes after serialization. 36 | type storableVertex struct { 37 | WrappedID string `json:"i"` 38 | Value interface{} `json:"v"` 39 | } 40 | 41 | func (v storableVertex) Vertex() (id string, value interface{}) { 42 | return v.WrappedID, v.Value 43 | } 44 | 45 | func (v storableVertex) ID() string { 46 | return v.WrappedID 47 | } 48 | 49 | // storableEdge implements the Edger interface. 50 | // It is implemented as a storable structure. 51 | // And it uses short json tag to reduce the number of bytes after serialization. 52 | type storableEdge struct { 53 | SrcID string `json:"s"` 54 | DstID string `json:"d"` 55 | } 56 | 57 | func (e storableEdge) Edge() (srcID, dstID string) { 58 | return e.SrcID, e.DstID 59 | } 60 | 61 | // storableDAG implements the StorableDAG interface. 62 | // It acts as a serializable operable structure. 63 | // And it uses short json tag to reduce the number of bytes after serialization. 64 | type storableDAG struct { 65 | StorableVertices []Vertexer `json:"vs"` 66 | StorableEdges []Edger `json:"es"` 67 | } 68 | 69 | func (g storableDAG) Vertices() []Vertexer { 70 | return g.StorableVertices 71 | } 72 | 73 | func (g storableDAG) Edges() []Edger { 74 | return g.StorableEdges 75 | } 76 | -------------------------------------------------------------------------------- /storage_test.go: -------------------------------------------------------------------------------- 1 | package dag 2 | 3 | type testVertex struct { 4 | WID string `json:"i"` 5 | Val string `json:"v"` 6 | } 7 | 8 | func (tv testVertex) ID() string { 9 | return tv.WID 10 | } 11 | 12 | func (tv testVertex) Vertex() (id string, value interface{}) { 13 | return tv.WID, tv.Val 14 | } 15 | 16 | type testStorableDAG struct { 17 | StorableVertices []testVertex `json:"vs"` 18 | StorableEdges []storableEdge `json:"es"` 19 | } 20 | 21 | func (g testStorableDAG) Vertices() []Vertexer { 22 | l := make([]Vertexer, 0, len(g.StorableVertices)) 23 | for _, v := range g.StorableVertices { 24 | l = append(l, v) 25 | } 26 | return l 27 | } 28 | 29 | func (g testStorableDAG) Edges() []Edger { 30 | l := make([]Edger, 0, len(g.StorableEdges)) 31 | for _, v := range g.StorableEdges { 32 | l = append(l, v) 33 | } 34 | return l 35 | } 36 | 37 | type testNonComparableStorableVertex struct { 38 | Id string `json:"i"` 39 | NotComparableVertex testNonComparableVertexType `json:"v"` 40 | } 41 | 42 | func (tv testNonComparableStorableVertex) Vertex() (id string, value interface{}) { 43 | return tv.Id, tv.NotComparableVertex 44 | } 45 | 46 | type testNonComparableStorableDAG struct { 47 | StorableVertices []testNonComparableStorableVertex `json:"vs"` 48 | StorableEdges []storableEdge `json:"es"` 49 | } 50 | 51 | func (g testNonComparableStorableDAG) Vertices() []Vertexer { 52 | l := make([]Vertexer, 0, len(g.StorableVertices)) 53 | for _, v := range g.StorableVertices { 54 | l = append(l, v) 55 | } 56 | return l 57 | } 58 | 59 | func (g testNonComparableStorableDAG) Edges() []Edger { 60 | l := make([]Edger, 0, len(g.StorableEdges)) 61 | for _, v := range g.StorableEdges { 62 | l = append(l, v) 63 | } 64 | return l 65 | } 66 | -------------------------------------------------------------------------------- /visitor.go: -------------------------------------------------------------------------------- 1 | package dag 2 | 3 | import ( 4 | "sort" 5 | 6 | llq "github.com/emirpasic/gods/queues/linkedlistqueue" 7 | lls "github.com/emirpasic/gods/stacks/linkedliststack" 8 | ) 9 | 10 | // Visitor is the interface that wraps the basic Visit method. 11 | // It can use the Visitor and XXXWalk functions together to traverse the entire DAG. 12 | // And access per-vertex information when traversing. 13 | type Visitor interface { 14 | Visit(Vertexer) 15 | } 16 | 17 | // DFSWalk implements the Depth-First-Search algorithm to traverse the entire DAG. 18 | // The algorithm starts at the root node and explores as far as possible 19 | // along each branch before backtracking. 20 | func (d *DAG) DFSWalk(visitor Visitor) { 21 | d.muDAG.RLock() 22 | defer d.muDAG.RUnlock() 23 | 24 | stack := lls.New() 25 | 26 | vertices := d.getRoots() 27 | for _, id := range reversedVertexIDs(vertices) { 28 | v := d.vertexIds[id] 29 | sv := storableVertex{WrappedID: id, Value: v} 30 | stack.Push(sv) 31 | } 32 | 33 | visited := make(map[string]bool, d.getSize()) 34 | 35 | for !stack.Empty() { 36 | v, _ := stack.Pop() 37 | sv := v.(storableVertex) 38 | 39 | if !visited[sv.WrappedID] { 40 | visited[sv.WrappedID] = true 41 | visitor.Visit(sv) 42 | } 43 | 44 | vertices, _ := d.getChildren(sv.WrappedID) 45 | for _, id := range reversedVertexIDs(vertices) { 46 | v := d.vertexIds[id] 47 | sv := storableVertex{WrappedID: id, Value: v} 48 | stack.Push(sv) 49 | } 50 | } 51 | } 52 | 53 | // BFSWalk implements the Breadth-First-Search algorithm to traverse the entire DAG. 54 | // It starts at the tree root and explores all nodes at the present depth prior 55 | // to moving on to the nodes at the next depth level. 56 | func (d *DAG) BFSWalk(visitor Visitor) { 57 | d.muDAG.RLock() 58 | defer d.muDAG.RUnlock() 59 | 60 | queue := llq.New() 61 | 62 | vertices := d.getRoots() 63 | for _, id := range vertexIDs(vertices) { 64 | v := vertices[id] 65 | sv := storableVertex{WrappedID: id, Value: v} 66 | queue.Enqueue(sv) 67 | } 68 | 69 | visited := make(map[string]bool, d.getOrder()) 70 | 71 | for !queue.Empty() { 72 | v, _ := queue.Dequeue() 73 | sv := v.(storableVertex) 74 | 75 | if !visited[sv.WrappedID] { 76 | visited[sv.WrappedID] = true 77 | visitor.Visit(sv) 78 | } 79 | 80 | vertices, _ := d.getChildren(sv.WrappedID) 81 | for _, id := range vertexIDs(vertices) { 82 | v := vertices[id] 83 | sv := storableVertex{WrappedID: id, Value: v} 84 | queue.Enqueue(sv) 85 | } 86 | } 87 | } 88 | 89 | func vertexIDs(vertices map[string]interface{}) []string { 90 | ids := make([]string, 0, len(vertices)) 91 | for id := range vertices { 92 | ids = append(ids, id) 93 | } 94 | sort.Strings(ids) 95 | return ids 96 | } 97 | 98 | func reversedVertexIDs(vertices map[string]interface{}) []string { 99 | ids := vertexIDs(vertices) 100 | i, j := 0, len(ids)-1 101 | for i < j { 102 | ids[i], ids[j] = ids[j], ids[i] 103 | i++ 104 | j-- 105 | } 106 | return ids 107 | } 108 | 109 | // OrderedWalk implements the Topological Sort algorithm to traverse the entire DAG. 110 | // This means that for any edge a -> b, node a will be visited before node b. 111 | func (d *DAG) OrderedWalk(visitor Visitor) { 112 | 113 | d.muDAG.RLock() 114 | defer d.muDAG.RUnlock() 115 | 116 | queue := llq.New() 117 | vertices := d.getRoots() 118 | for _, id := range vertexIDs(vertices) { 119 | v := vertices[id] 120 | sv := storableVertex{WrappedID: id, Value: v} 121 | queue.Enqueue(sv) 122 | } 123 | 124 | visited := make(map[string]bool, d.getOrder()) 125 | 126 | Main: 127 | for !queue.Empty() { 128 | v, _ := queue.Dequeue() 129 | sv := v.(storableVertex) 130 | 131 | if visited[sv.WrappedID] { 132 | continue 133 | } 134 | 135 | // if the current vertex has any parent that hasn't been visited yet, 136 | // put it back into the queue, and work on the next element 137 | parents, _ := d.GetParents(sv.WrappedID) 138 | for parent := range parents { 139 | if !visited[parent] { 140 | queue.Enqueue(sv) 141 | continue Main 142 | } 143 | } 144 | 145 | if !visited[sv.WrappedID] { 146 | visited[sv.WrappedID] = true 147 | visitor.Visit(sv) 148 | } 149 | 150 | vertices, _ := d.getChildren(sv.WrappedID) 151 | for _, id := range vertexIDs(vertices) { 152 | v := vertices[id] 153 | sv := storableVertex{WrappedID: id, Value: v} 154 | queue.Enqueue(sv) 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /visitor_test.go: -------------------------------------------------------------------------------- 1 | package dag 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-test/deep" 7 | ) 8 | 9 | type testVisitor struct { 10 | Values []string 11 | } 12 | 13 | func (pv *testVisitor) Visit(v Vertexer) { 14 | _, value := v.Vertex() 15 | pv.Values = append(pv.Values, value.(string)) 16 | } 17 | 18 | // schematic diagram: 19 | // 20 | // v5 21 | // ^ 22 | // | 23 | // v4 24 | // ^ 25 | // | 26 | // v2 --> v3 27 | // ^ 28 | // | 29 | // v1 30 | func getTestWalkDAG() *DAG { 31 | dag := NewDAG() 32 | 33 | v1, v2, v3, v4, v5 := "1", "2", "3", "4", "5" 34 | _ = dag.AddVertexByID(v1, "v1") 35 | _ = dag.AddVertexByID(v2, "v2") 36 | _ = dag.AddVertexByID(v3, "v3") 37 | _ = dag.AddVertexByID(v4, "v4") 38 | _ = dag.AddVertexByID(v5, "v5") 39 | _ = dag.AddEdge(v1, v2) 40 | _ = dag.AddEdge(v2, v3) 41 | _ = dag.AddEdge(v2, v4) 42 | _ = dag.AddEdge(v4, v5) 43 | 44 | return dag 45 | } 46 | 47 | // schematic diagram: 48 | // 49 | // v4 --> v5 50 | // ^ 51 | // | 52 | // v1 --> v3 53 | // ^ 54 | // | 55 | // v2 56 | func getTestWalkDAG2() *DAG { 57 | dag := NewDAG() 58 | 59 | v1, v2, v3, v4, v5 := "1", "2", "3", "4", "5" 60 | _ = dag.AddVertexByID(v1, "v1") 61 | _ = dag.AddVertexByID(v2, "v2") 62 | _ = dag.AddVertexByID(v3, "v3") 63 | _ = dag.AddVertexByID(v4, "v4") 64 | _ = dag.AddVertexByID(v5, "v5") 65 | _ = dag.AddEdge(v1, v3) 66 | _ = dag.AddEdge(v2, v3) 67 | _ = dag.AddEdge(v3, v5) 68 | _ = dag.AddEdge(v4, v5) 69 | 70 | return dag 71 | } 72 | 73 | // schematic diagram: 74 | // 75 | // v4 --> v5 76 | // 77 | // 78 | // v1 --> v3 79 | // ^ 80 | // | 81 | // v2 82 | func getTestWalkDAG3() *DAG { 83 | dag := NewDAG() 84 | 85 | v1, v2, v3, v4, v5 := "1", "2", "3", "4", "5" 86 | _ = dag.AddVertexByID(v1, "v1") 87 | _ = dag.AddVertexByID(v2, "v2") 88 | _ = dag.AddVertexByID(v3, "v3") 89 | _ = dag.AddVertexByID(v4, "v4") 90 | _ = dag.AddVertexByID(v5, "v5") 91 | _ = dag.AddEdge(v1, v3) 92 | _ = dag.AddEdge(v2, v3) 93 | _ = dag.AddEdge(v4, v5) 94 | 95 | return dag 96 | } 97 | 98 | // schematic diagram: 99 | // 100 | // v4 v5 101 | // ^ ^ 102 | // | | 103 | // v2 --> v3 104 | // ^ 105 | // | 106 | // v1 107 | func getTestWalkDAG4() *DAG { 108 | dag := NewDAG() 109 | 110 | v1, v2, v3, v4, v5 := "1", "2", "3", "4", "5" 111 | _ = dag.AddVertexByID(v1, "v1") 112 | _ = dag.AddVertexByID(v2, "v2") 113 | _ = dag.AddVertexByID(v3, "v3") 114 | _ = dag.AddVertexByID(v4, "v4") 115 | _ = dag.AddVertexByID(v5, "v5") 116 | _ = dag.AddEdge(v1, v2) 117 | _ = dag.AddEdge(v2, v3) 118 | _ = dag.AddEdge(v3, v5) 119 | _ = dag.AddEdge(v2, v4) 120 | 121 | return dag 122 | } 123 | 124 | // schematic diagram: 125 | // 126 | // v5 127 | // ^ 128 | // | 129 | // v3 <-- v4 130 | // ^ ^ 131 | // | | 132 | // v1 v2 133 | func getTestWalkDAG5() *DAG { 134 | dag := NewDAG() 135 | 136 | v1, v2, v3, v4, v5 := "1", "2", "3", "4", "5" 137 | _ = dag.AddVertexByID(v1, "v1") 138 | _ = dag.AddVertexByID(v2, "v2") 139 | _ = dag.AddVertexByID(v3, "v3") 140 | _ = dag.AddVertexByID(v4, "v4") 141 | _ = dag.AddVertexByID(v5, "v5") 142 | _ = dag.AddEdge(v1, v3) 143 | _ = dag.AddEdge(v2, v4) 144 | _ = dag.AddEdge(v4, v3) 145 | _ = dag.AddEdge(v3, v5) 146 | 147 | return dag 148 | } 149 | 150 | func TestDFSWalk(t *testing.T) { 151 | cases := []struct { 152 | dag *DAG 153 | expected []string 154 | }{ 155 | { 156 | dag: getTestWalkDAG(), 157 | expected: []string{"v1", "v2", "v3", "v4", "v5"}, 158 | }, 159 | { 160 | dag: getTestWalkDAG2(), 161 | expected: []string{"v1", "v3", "v5", "v2", "v4"}, 162 | }, 163 | { 164 | dag: getTestWalkDAG3(), 165 | expected: []string{"v1", "v3", "v2", "v4", "v5"}, 166 | }, 167 | { 168 | dag: getTestWalkDAG4(), 169 | expected: []string{"v1", "v2", "v3", "v5", "v4"}, 170 | }, 171 | { 172 | dag: getTestWalkDAG5(), 173 | expected: []string{"v1", "v3", "v5", "v2", "v4"}, 174 | }, 175 | } 176 | 177 | for _, c := range cases { 178 | pv := &testVisitor{} 179 | c.dag.DFSWalk(pv) 180 | 181 | expected := c.expected 182 | actual := pv.Values 183 | if deep.Equal(expected, actual) != nil { 184 | t.Errorf("DFSWalk() = %v, want %v", actual, expected) 185 | } 186 | } 187 | } 188 | 189 | func TestBFSWalk(t *testing.T) { 190 | cases := []struct { 191 | dag *DAG 192 | expected []string 193 | }{ 194 | { 195 | dag: getTestWalkDAG(), 196 | expected: []string{"v1", "v2", "v3", "v4", "v5"}, 197 | }, 198 | { 199 | dag: getTestWalkDAG2(), 200 | expected: []string{"v1", "v2", "v4", "v3", "v5"}, 201 | }, 202 | { 203 | dag: getTestWalkDAG3(), 204 | expected: []string{"v1", "v2", "v4", "v3", "v5"}, 205 | }, 206 | { 207 | dag: getTestWalkDAG4(), 208 | expected: []string{"v1", "v2", "v3", "v4", "v5"}, 209 | }, 210 | { 211 | dag: getTestWalkDAG5(), 212 | expected: []string{"v1", "v2", "v3", "v4", "v5"}, 213 | }, 214 | } 215 | 216 | for _, c := range cases { 217 | pv := &testVisitor{} 218 | c.dag.BFSWalk(pv) 219 | 220 | expected := c.expected 221 | actual := pv.Values 222 | if deep.Equal(expected, actual) != nil { 223 | t.Errorf("BFSWalk() = %v, want %v", actual, expected) 224 | } 225 | } 226 | } 227 | 228 | func TestOrderedWalk(t *testing.T) { 229 | cases := []struct { 230 | dag *DAG 231 | expected []string 232 | }{ 233 | { 234 | dag: getTestWalkDAG(), 235 | expected: []string{"v1", "v2", "v3", "v4", "v5"}, 236 | }, 237 | { 238 | dag: getTestWalkDAG2(), 239 | expected: []string{"v1", "v2", "v4", "v3", "v5"}, 240 | }, 241 | { 242 | dag: getTestWalkDAG3(), 243 | expected: []string{"v1", "v2", "v4", "v3", "v5"}, 244 | }, 245 | { 246 | dag: getTestWalkDAG4(), 247 | expected: []string{"v1", "v2", "v3", "v4", "v5"}, 248 | }, 249 | { 250 | dag: getTestWalkDAG5(), 251 | expected: []string{"v1", "v2", "v4", "v3", "v5"}, 252 | }, 253 | } 254 | 255 | for _, c := range cases { 256 | pv := &testVisitor{} 257 | c.dag.OrderedWalk(pv) 258 | 259 | expected := c.expected 260 | actual := pv.Values 261 | if deep.Equal(expected, actual) != nil { 262 | t.Errorf("OrderedWalk() = %v, want %v", actual, expected) 263 | } 264 | } 265 | } 266 | --------------------------------------------------------------------------------