├── LICENSE ├── README.markdown ├── examples └── todot │ ├── Makefile │ ├── directed.dgr │ ├── mixed.mgr │ ├── todot.go │ └── undirected.ugr └── src └── graph ├── DirectedGraph_test.go ├── DirectedMap.go ├── Makefile ├── MixedGraph_test.go ├── MixedMap.go ├── MixedMatrix.go ├── UndirectedGraph_test.go ├── UndirectedMap.go ├── UndirectedMatrix.go ├── algorithms.go ├── algorithms_test.go ├── comparators.go ├── comparators_test.go ├── filters.go ├── filters_test.go ├── graph.go ├── input.go ├── iterators.go ├── iterators_test.go ├── neighbours_extractor.go ├── output.go ├── output_test.go ├── search.go ├── search_test.go ├── stuff.go ├── stuff_test.go └── testgraphs_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Michael Hoisie 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Decription 2 | -------- 3 | Graph library for Go/Golang language. 4 | 5 | Install 6 | -------- 7 | First way (with makefile): 8 | 9 | $ git clone git://github.com/StepLg/go-graph.git 10 | $ cd go-graph/src/graph 11 | $ make 12 | $ make install 13 | 14 | Second way (with goinstall): 15 | 16 | $ goinstall github.com/StepLg/go-graph/src/graph 17 | 18 | To update run: 19 | 20 | $ goinstall -u=true github.com/StepLg/go-graph/src/graph 21 | -------------------------------------------------------------------------------- /examples/todot/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.$(GOARCH) 2 | 3 | TARG=todot 4 | 5 | GOFILES= \ 6 | todot.go 7 | 8 | include $(GOROOT)/src/Make.cmd 9 | -------------------------------------------------------------------------------- /examples/todot/directed.dgr: -------------------------------------------------------------------------------- 1 | # simple undirected graph 2 | 1 # dangle vertex 3 | 2 3 # arc between new vertexes 4 | 4>5 # or we can specify arc with hyphen instead of space 5 | 3 5 # arc between existing vertexes 6 | 2 4 6 8 # more than one arc in one line -------------------------------------------------------------------------------- /examples/todot/mixed.mgr: -------------------------------------------------------------------------------- 1 | # simple undirected graph 2 | 1 # dangle vertex 3 | 2-3 # edge between new vertexes 4 | 9>10 # arc between new vertexes 5 | 3>4-5-6>7>8 # more than one connection in one line 6 | 6>4-8-10 # another connections line 7 | -------------------------------------------------------------------------------- /examples/todot/todot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "../../src/graph/_obj/graph" 5 | 6 | "fmt" 7 | "flag" 8 | "os" 9 | "path" 10 | 11 | "github.com/StepLg/go-erx/src/erx" 12 | ) 13 | 14 | func main() { 15 | defer func() { 16 | if err := recover(); err != nil { 17 | if errErx, ok := err.(erx.Error); ok { 18 | formatter := erx.NewStringFormatter(" ") 19 | fmt.Println(formatter.Format(errErx)) 20 | } 21 | } 22 | }() 23 | 24 | flag_help := flag.Bool("help", false, "Display this help.") 25 | flag_inputFile := flag.String("in", "", 26 | `Input file with messages. If file extension is .ugr, .dgr or .mgr then 27 | graph type automaticly set to undirected, directed or mixed respectively. 28 | If flag doesn't set, then read from stdin.`) 29 | flag_outputFile := flag.String("out", "", 30 | `Output file. If it isn't set and flag -autoname isn't set too, then 31 | output to stdout.`) 32 | flag_autoname := flag.Bool("autoname", false, 33 | `Generate output file name automaticly from input file name with 34 | replacing it's extension to ".dot."`) 35 | flag_type := flag.String("type", "", 36 | `[u|d|m] -- Graph type: undirected, directed or mixed respectively.`) 37 | 38 | flag.Parse() 39 | 40 | if *flag_help { 41 | fmt.Println(`Read graph from text representation and output it in graphviz format.`) 42 | fmt.Println() 43 | flag.PrintDefaults() 44 | return 45 | } 46 | 47 | infile := os.Stdin 48 | if *flag_inputFile!="" { 49 | var err os.Error 50 | infile, err = os.Open(*flag_inputFile, os.O_RDONLY, 0000) 51 | if err!=nil { 52 | erxErr := erx.NewSequent("Can't open input file.", err) 53 | erxErr.AddV("file name", *flag_inputFile) 54 | panic(erxErr) 55 | } 56 | 57 | infileExt := path.Ext(*flag_inputFile) 58 | 59 | if *flag_autoname { 60 | // generating autoname for output file 61 | fpath, fname := path.Split(*flag_inputFile) 62 | baseFileName := fname 63 | baseFileName = fname[0:len(fname)-len(infileExt)] 64 | 65 | *flag_outputFile = path.Join(fpath, baseFileName + ".dot") 66 | } 67 | 68 | // initializing graph type from known file extensions. 69 | switch infileExt { 70 | case ".ugr": 71 | *flag_type = "u" 72 | case ".dgr": 73 | *flag_type = "d" 74 | case ".mgr": 75 | *flag_type = "m" 76 | } 77 | } 78 | 79 | outfile := os.Stdout 80 | if *flag_outputFile!="" { 81 | var err os.Error 82 | outfile, err = os.Open(*flag_outputFile, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0644) 83 | if err!=nil { 84 | erxErr := erx.NewSequent("Can't open output file.", err) 85 | erxErr.AddV("file name", *flag_outputFile) 86 | panic(erxErr) 87 | } 88 | } 89 | 90 | switch *flag_type { 91 | case "u": 92 | gr := graph.NewUndirectedMap() 93 | graph.ReadUgraphFile(infile, gr) 94 | graph.PlotUgraphToDot(gr, outfile, nil, nil) 95 | case "d": 96 | gr := graph.NewDirectedMap() 97 | graph.ReadDgraphFile(infile, gr) 98 | graph.PlotDgraphToDot(gr, outfile, nil, nil) 99 | case "m": 100 | gr := graph.NewMixedMap() 101 | graph.ReadMgraphFile(infile, gr) 102 | graph.PlotMgraphToDot(gr, outfile, nil, nil) 103 | default: 104 | err := erx.NewError("Unknown type flag.") 105 | err.AddV("flag value", *flag_type) 106 | panic(err) 107 | } 108 | 109 | outfile.Close() 110 | return 111 | } 112 | -------------------------------------------------------------------------------- /examples/todot/undirected.ugr: -------------------------------------------------------------------------------- 1 | # simple undirected graph 2 | 1 # dangle vertex 3 | 2 3 # edge between new vertexes 4 | 4-5 # or we can specify edge with hyphen instead of space 5 | 3 5 # edge between existing vertexes 6 | 2 4 6 8 # more than one edge in one line -------------------------------------------------------------------------------- /src/graph/DirectedGraph_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "testing" 5 | "runtime" 6 | "strings" 7 | "github.com/StepLg/go-erx/src/erx" 8 | "github.com/orfjackal/gospec/src/gospec" 9 | . "github.com/orfjackal/gospec/src/gospec" 10 | ) 11 | 12 | func init() { 13 | // adding to erx directory prefix to cut from file names 14 | _, file, _, _ := runtime.Caller(0) 15 | dirName := file[0:strings.LastIndex(file, "/")] 16 | prevDirName := dirName[0:strings.LastIndex(dirName, "/")+1] 17 | erx.AddPathCut(prevDirName) 18 | } 19 | 20 | func DirectedGraphSpec(c gospec.Context, graphCreator func() DirectedGraph) { 21 | gr := graphCreator() 22 | 23 | c.Specify("Empty directed graph", func() { 24 | c.Specify("contain no nodes", func() { 25 | c.Expect(gr.Order(), Equals, 0) 26 | }) 27 | c.Specify("contain no edges", func() { 28 | c.Expect(gr.ArcsCnt(), Equals, 0) 29 | }) 30 | }) 31 | 32 | c.Specify("New node in empty graph", func() { 33 | VertexId := VertexId(1) 34 | gr.AddNode(VertexId) 35 | 36 | c.Specify("changing nodes count", func() { 37 | c.Expect(gr.Order(), Equals, 1) 38 | }) 39 | 40 | c.Specify("doesn't change arrows count", func() { 41 | c.Expect(gr.ArcsCnt(), Equals, 0) 42 | }) 43 | 44 | c.Specify("no accessors", func() { 45 | accessors := CollectVertexes(gr.GetAccessors(VertexId)) 46 | c.Expect(len(accessors), Equals, 0) 47 | }) 48 | 49 | c.Specify("no predecessors", func() { 50 | c.Expect(len(CollectVertexes(gr.GetPredecessors(VertexId))), Equals, 0) 51 | }) 52 | 53 | c.Specify("node becomes a source", func() { 54 | sources := CollectVertexes(gr.GetSources()) 55 | c.Expect(sources, ContainsExactly, Values(VertexId)) 56 | }) 57 | 58 | c.Specify("node becomes a sink", func() { 59 | sinks := CollectVertexes(gr.GetSinks()) 60 | c.Expect(sinks, ContainsExactly, Values(VertexId)) 61 | }) 62 | 63 | }) 64 | 65 | c.Specify("New arrow in empty graph", func() { 66 | vertexId := VertexId(1) 67 | anotherVertexId := VertexId(2) 68 | gr.AddArc(vertexId, anotherVertexId) 69 | c.Expect(gr.CheckArc(vertexId, anotherVertexId), Equals, true) 70 | 71 | c.Specify("changing nodes count", func() { 72 | c.Expect(gr.Order(), Equals, 2) 73 | }) 74 | 75 | c.Specify("changing arrows count", func() { 76 | c.Expect(gr.ArcsCnt(), Equals, 1) 77 | }) 78 | 79 | c.Specify("correct accessors in arrow start", func() { 80 | c.Expect(CollectVertexes(gr.GetAccessors(vertexId)), ContainsExactly, Values(anotherVertexId)) 81 | }) 82 | 83 | c.Specify("correct predecessors in arrow start", func() { 84 | c.Expect(len(CollectVertexes(gr.GetPredecessors(vertexId))), Equals, 0) 85 | }) 86 | 87 | c.Specify("correct accessors in arrow end", func() { 88 | c.Expect(len(CollectVertexes(gr.GetAccessors(anotherVertexId))), Equals, 0) 89 | }) 90 | 91 | c.Specify("correct predecessors in arrow end", func() { 92 | c.Expect(CollectVertexes(gr.GetPredecessors(anotherVertexId)), ContainsExactly, Values(vertexId)) 93 | }) 94 | 95 | c.Specify("arrow start becomes a source", func() { 96 | c.Expect(CollectVertexes(gr.GetSources()), ContainsExactly, Values(vertexId)) 97 | }) 98 | 99 | c.Specify("arrow end becomes a sink", func() { 100 | c.Expect(CollectVertexes(gr.GetSinks()), ContainsExactly, Values(anotherVertexId)) 101 | }) 102 | }) 103 | 104 | c.Specify("A bit more complex example", func() { 105 | gr.AddArc(1, 2) 106 | gr.AddArc(2, 3) 107 | gr.AddArc(3, 1) 108 | gr.AddArc(1, 5) 109 | gr.AddArc(4, 5) 110 | gr.AddArc(6, 2) 111 | gr.AddArc(1, 7) 112 | 113 | c.Specify("checking nodes count", func() { 114 | c.Expect(gr.Order(), Equals, 7) 115 | }) 116 | 117 | c.Specify("checking arrows count", func() { 118 | c.Expect(gr.ArcsCnt(), Equals, 7) 119 | }) 120 | 121 | c.Specify("checking sources", func() { 122 | sources := CollectVertexes(gr.GetSources()) 123 | c.Expect(sources, ContainsExactly, Values(VertexId(4), VertexId(6))) 124 | 125 | c.Specify("every source hasn't any predecessors", func() { 126 | for _, vertexId := range sources { 127 | c.Expect(len(CollectVertexes(gr.GetPredecessors(vertexId))), Equals, 0) 128 | } 129 | }) 130 | }) 131 | 132 | c.Specify("checking sinks", func() { 133 | sinks := CollectVertexes(gr.GetSinks()) 134 | c.Expect(sinks, ContainsExactly, Values(VertexId(5), VertexId(7))) 135 | 136 | c.Specify("every sink hasn't any accessors", func() { 137 | for _, vertexId := range sinks { 138 | c.Expect(len(CollectVertexes(gr.GetAccessors(vertexId))), Equals, 0) 139 | } 140 | }) 141 | }) 142 | 143 | c.Specify("checking accessors in intermediate node", func() { 144 | accessors := CollectVertexes(gr.GetAccessors(1)) 145 | c.Expect(accessors, ContainsExactly, Values(VertexId(2), VertexId(5), VertexId(7))) 146 | 147 | c.Specify("every accessor has this node in predecessors", func() { 148 | for _, vertexId := range accessors { 149 | c.Expect(CollectVertexes(gr.GetPredecessors(vertexId)), Contains, VertexId(1)) 150 | } 151 | }) 152 | }) 153 | 154 | c.Specify("checking predecessors in intermediate node", func() { 155 | predecessors := CollectVertexes(gr.GetPredecessors(5)) 156 | c.Expect(predecessors, ContainsExactly, Values(VertexId(1), VertexId(4))) 157 | 158 | c.Specify("every predecessor has this node in accessors", func() { 159 | for _, vertexId := range predecessors { 160 | c.Expect(CollectVertexes(gr.GetAccessors(vertexId)), Contains, VertexId(5)) 161 | } 162 | }) 163 | }) 164 | 165 | }) 166 | } 167 | 168 | func TestDirectedGraphSpec(t *testing.T) { 169 | r := gospec.NewRunner() 170 | 171 | // paramenerized test creator 172 | cr := func(graphCreator func() DirectedGraph) func (c gospec.Context) { 173 | return func(c gospec.Context){ 174 | DirectedGraphSpec(c, graphCreator) 175 | } 176 | } 177 | 178 | r.AddNamedSpec("DirectedGraph(DirectedMap)", cr(func() DirectedGraph { 179 | return DirectedGraph(NewDirectedMap()) 180 | })) 181 | r.AddNamedSpec("DirectedGraph(MixedMatrix)", cr(func() DirectedGraph { 182 | return DirectedGraph(NewMixedMatrix(10)) 183 | })) 184 | r.AddNamedSpec("DirectedGraph(MixedMap)", cr(func() DirectedGraph { 185 | return DirectedGraph(NewMixedMap()) 186 | })) 187 | gospec.MainGoTest(r, t) 188 | } 189 | -------------------------------------------------------------------------------- /src/graph/DirectedMap.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "github.com/StepLg/go-erx/src/erx" 5 | ) 6 | 7 | type DirectedMap struct { 8 | directArcs map[VertexId]map[VertexId]bool 9 | reversedArcs map[VertexId]map[VertexId]bool 10 | arcsCnt int 11 | } 12 | 13 | func NewDirectedMap() *DirectedMap { 14 | g := new(DirectedMap) 15 | g.directArcs = make(map[VertexId]map[VertexId]bool) 16 | g.reversedArcs = make(map[VertexId]map[VertexId]bool) 17 | g.arcsCnt = 0 18 | return g 19 | } 20 | 21 | /////////////////////////////////////////////////////////////////////////////// 22 | // ConnectionsIterable 23 | 24 | func (g *DirectedMap) ConnectionsIter() <-chan Connection { 25 | return g.ArcsIter() 26 | } 27 | 28 | /////////////////////////////////////////////////////////////////////////////// 29 | // VertexesIterable 30 | 31 | func (g *DirectedMap) VertexesIter() <-chan VertexId { 32 | ch := make(chan VertexId) 33 | go func() { 34 | for from, _ := range g.directArcs { 35 | ch <- from 36 | } 37 | 38 | for to, _ := range g.reversedArcs { 39 | // need to prevent duplicating node ids 40 | if _, ok := g.directArcs[to]; !ok { 41 | ch <- to 42 | } 43 | } 44 | close(ch) 45 | }() 46 | return ch 47 | } 48 | 49 | /////////////////////////////////////////////////////////////////////////////// 50 | // VertexesChecker 51 | 52 | func (g *DirectedMap) CheckNode(node VertexId) (exists bool) { 53 | _, exists = g.directArcs[node] 54 | return 55 | } 56 | 57 | /////////////////////////////////////////////////////////////////////////////// 58 | // GraphVertexesWriter 59 | 60 | // Adding single node to graph 61 | func (g *DirectedMap) AddNode(node VertexId) { 62 | makeError := func(err interface{}) (res erx.Error) { 63 | res = erx.NewSequentLevel("Add node to graph.", err, 1) 64 | res.AddV("node id", node) 65 | return 66 | } 67 | 68 | if _, ok := g.directArcs[node]; ok { 69 | panic(makeError(erx.NewError("Node already exists."))) 70 | } 71 | 72 | g.directArcs[node] = make(map[VertexId]bool) 73 | g.reversedArcs[node] = make(map[VertexId]bool) 74 | 75 | return 76 | } 77 | 78 | /////////////////////////////////////////////////////////////////////////////// 79 | // GraphVertexesRemover 80 | 81 | func (g *DirectedMap) RemoveNode(node VertexId) { 82 | makeError := func(err interface{}) (res erx.Error) { 83 | res = erx.NewSequentLevel("Remove node from graph.", err, 1) 84 | res.AddV("node id", node) 85 | return 86 | } 87 | 88 | _, okDirect := g.directArcs[node] 89 | _, okReversed := g.reversedArcs[node] 90 | if !okDirect && !okReversed { 91 | panic(makeError(erx.NewError("Node doesn't exist."))) 92 | } 93 | 94 | g.directArcs[node] = nil, false 95 | g.reversedArcs[node] = nil, false 96 | for _, connectedVertexes := range g.directArcs { 97 | connectedVertexes[node] = false, false 98 | } 99 | for _, connectedVertexes := range g.reversedArcs { 100 | connectedVertexes[node] = false, false 101 | } 102 | return 103 | } 104 | 105 | /////////////////////////////////////////////////////////////////////////////// 106 | // DirectedGraphArcsWriter 107 | 108 | func (g *DirectedMap) touchNode(node VertexId) { 109 | if _, ok := g.directArcs[node]; !ok { 110 | g.directArcs[node] = make(map[VertexId]bool) 111 | g.reversedArcs[node] = make(map[VertexId]bool) 112 | } 113 | } 114 | 115 | // Adding arrow to graph. 116 | func (g *DirectedMap) AddArc(from, to VertexId) { 117 | makeError := func(err interface{}) (res erx.Error) { 118 | res = erx.NewSequentLevel("Add arc to graph.", err, 1) 119 | res.AddV("tail", from) 120 | res.AddV("head", to) 121 | return 122 | } 123 | 124 | g.touchNode(from) 125 | g.touchNode(to) 126 | 127 | if direction, ok := g.directArcs[from][to]; ok && direction { 128 | panic(makeError(erx.NewError("Duplicate arrow."))) 129 | } 130 | 131 | g.directArcs[from][to] = true 132 | g.reversedArcs[to][from] = true 133 | g.arcsCnt++ 134 | return 135 | } 136 | 137 | /////////////////////////////////////////////////////////////////////////////// 138 | // DirectedGraphArcsRemover 139 | 140 | // Removing arrow 'from' and 'to' nodes 141 | func (g *DirectedMap) RemoveArc(from, to VertexId) { 142 | makeError := func(err interface{}) (res erx.Error) { 143 | res = erx.NewSequentLevel("Remove arc from graph.", err, 1) 144 | res.AddV("tail", from) 145 | res.AddV("head", to) 146 | return 147 | } 148 | 149 | connectedVertexes, ok := g.directArcs[from] 150 | if !ok { 151 | panic(makeError(erx.NewError("Tail node doesn't exist."))) 152 | } 153 | 154 | if _, ok = connectedVertexes[to]; ok { 155 | panic(makeError(erx.NewError("Head node doesn't exist."))) 156 | } 157 | 158 | g.directArcs[from][to] = false, false 159 | g.reversedArcs[to][from] = false, false 160 | g.arcsCnt-- 161 | 162 | return 163 | } 164 | 165 | /////////////////////////////////////////////////////////////////////////////// 166 | // DirectedGraphReader 167 | 168 | func (g *DirectedMap) Order() int { 169 | return len(g.directArcs) 170 | } 171 | 172 | func (g *DirectedMap) ArcsCnt() int { 173 | return g.arcsCnt 174 | } 175 | 176 | // Getting all graph sources. 177 | func (g *DirectedMap) GetSources() VertexesIterable { 178 | iterator := func() <-chan VertexId { 179 | ch := make(chan VertexId) 180 | go func() { 181 | for VertexId, predecessors := range g.reversedArcs { 182 | if len(predecessors)==0 { 183 | ch <- VertexId 184 | } 185 | } 186 | close(ch) 187 | }() 188 | return ch 189 | } 190 | 191 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 192 | } 193 | 194 | // Getting all graph sinks. 195 | func (g *DirectedMap) GetSinks() VertexesIterable { 196 | iterator := func() <-chan VertexId { 197 | ch := make(chan VertexId) 198 | go func() { 199 | for VertexId, accessors := range g.directArcs { 200 | if len(accessors)==0 { 201 | ch <- VertexId 202 | } 203 | } 204 | close(ch) 205 | }() 206 | return ch 207 | } 208 | 209 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 210 | } 211 | 212 | // Getting node accessors 213 | func (g *DirectedMap) GetAccessors(node VertexId) VertexesIterable { 214 | iterator := func() <-chan VertexId { 215 | ch := make(chan VertexId) 216 | 217 | go func() { 218 | defer func() { 219 | if e := recover(); e!=nil { 220 | err := erx.NewSequent("Get node accessors in mixed graph.", e) 221 | err.AddV("node", node) 222 | panic(err) 223 | } 224 | }() 225 | accessorsMap, ok := g.directArcs[node] 226 | if !ok { 227 | panic(erx.NewError("Node doesn't exists.")) 228 | } 229 | 230 | for VertexId, _ := range accessorsMap { 231 | ch <- VertexId 232 | } 233 | close(ch) 234 | }() 235 | 236 | return ch 237 | } 238 | 239 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 240 | } 241 | 242 | // Getting node predecessors 243 | func (g *DirectedMap) GetPredecessors(node VertexId) VertexesIterable { 244 | iterator := func() <-chan VertexId { 245 | ch := make(chan VertexId) 246 | 247 | go func() { 248 | defer func() { 249 | if e := recover(); e!=nil { 250 | err := erx.NewSequent("Get node accessors in mixed graph.", e) 251 | err.AddV("node", node) 252 | panic(err) 253 | } 254 | }() 255 | accessorsMap, ok := g.reversedArcs[node] 256 | if !ok { 257 | panic(erx.NewError("Node doesn't exists.")) 258 | } 259 | 260 | for VertexId, _ := range accessorsMap { 261 | ch <- VertexId 262 | } 263 | close(ch) 264 | }() 265 | 266 | return ch 267 | } 268 | 269 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 270 | } 271 | 272 | func (g *DirectedMap) CheckArc(from, to VertexId) (isExist bool) { 273 | makeError := func(err interface{}) (res erx.Error) { 274 | res = erx.NewSequentLevel("Checking arc existance in graph.", err, 1) 275 | res.AddV("tail", from) 276 | res.AddV("head", to) 277 | return 278 | } 279 | 280 | connectedVertexes, ok := g.directArcs[from] 281 | if !ok { 282 | panic(makeError(erx.NewError("From node doesn't exist."))) 283 | } 284 | 285 | if _, ok = g.reversedArcs[to]; !ok { 286 | panic(makeError(erx.NewError("To node doesn't exist."))) 287 | } 288 | 289 | _, isExist = connectedVertexes[to] 290 | 291 | return 292 | } 293 | 294 | func (g *DirectedMap) ArcsIter() <-chan Connection { 295 | ch := make(chan Connection) 296 | go func() { 297 | for from, connectedVertexes := range g.directArcs { 298 | for to, _ := range connectedVertexes { 299 | ch <- Connection{from, to} 300 | } 301 | } 302 | close(ch) 303 | }() 304 | return ch 305 | } 306 | -------------------------------------------------------------------------------- /src/graph/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.$(GOARCH) 2 | 3 | TARG=graph 4 | GOFILES= \ 5 | algorithms.go \ 6 | comparators.go \ 7 | DirectedMap.go \ 8 | filters.go \ 9 | graph.go \ 10 | input.go \ 11 | iterators.go \ 12 | MixedMap.go \ 13 | MixedMatrix.go \ 14 | neighbours_extractor.go \ 15 | output.go \ 16 | search.go \ 17 | stuff.go \ 18 | UndirectedMap.go \ 19 | UndirectedMatrix.go 20 | 21 | include $(GOROOT)/src/Make.pkg 22 | -------------------------------------------------------------------------------- /src/graph/MixedGraph_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "testing" 5 | "github.com/orfjackal/gospec/src/gospec" 6 | . "github.com/orfjackal/gospec/src/gospec" 7 | ) 8 | 9 | func MixedGraphSpec(c gospec.Context, graphCreator func() MixedGraph) { 10 | gr := graphCreator() 11 | c.Specify("After adding new edge", func() { 12 | tail := VertexId(1) 13 | head := VertexId(2) 14 | gr.AddEdge(tail, head) 15 | c.Specify("contain exactly two nodes", func() { 16 | c.Expect(gr.Order(), Equals, 2) 17 | }) 18 | c.Specify("contain single edge", func() { 19 | c.Expect(gr.EdgesCnt(), Equals, 1) 20 | }) 21 | c.Specify("contain no arcs", func() { 22 | c.Expect(gr.ArcsCnt(), Equals, 0) 23 | }) 24 | c.Specify("has one connection with type 'undirected'", func() { 25 | c.Expect(gr.CheckEdge(tail, head), IsTrue) 26 | c.Expect(gr.CheckEdge(head, tail), IsTrue) 27 | c.Expect(gr.CheckArc(tail, head), IsFalse) 28 | c.Expect(gr.CheckArc(head, tail), IsFalse) 29 | c.Expect(gr.CheckEdgeType(tail, head), Equals, CT_UNDIRECTED) 30 | c.Expect(gr.CheckEdgeType(head, tail), Equals, CT_UNDIRECTED) 31 | }) 32 | }) 33 | c.Specify("After adding new arc", func() { 34 | tail := VertexId(1) 35 | head := VertexId(2) 36 | gr.AddArc(tail, head) 37 | c.Specify("contain exactly two nodes", func() { 38 | c.Expect(gr.Order(), Equals, 2) 39 | }) 40 | c.Specify("contain no edges", func() { 41 | c.Expect(gr.EdgesCnt(), Equals, 0) 42 | }) 43 | c.Specify("contain single arc", func() { 44 | c.Expect(gr.ArcsCnt(), Equals, 1) 45 | }) 46 | c.Specify("has one connection with type 'directed'", func() { 47 | c.Expect(gr.CheckEdge(tail, head), IsFalse) 48 | c.Expect(gr.CheckEdge(head, tail), IsFalse) 49 | c.Expect(gr.CheckArc(tail, head), IsTrue) 50 | c.Expect(gr.CheckArc(head, tail), IsFalse) 51 | c.Expect(gr.CheckEdgeType(tail, head), Equals, CT_DIRECTED) 52 | c.Expect(gr.CheckEdgeType(head, tail), Equals, CT_DIRECTED_REVERSED) 53 | }) 54 | }) 55 | } 56 | 57 | func TestMixedGraphSpec(t *testing.T) { 58 | r := gospec.NewRunner() 59 | 60 | // paramenerized test creator 61 | cr := func(graphCreator func() MixedGraph) func (c gospec.Context) { 62 | return func(c gospec.Context){ 63 | MixedGraphSpec(c, graphCreator) 64 | } 65 | } 66 | 67 | r.AddNamedSpec("MixedGraph(MixedMap)", cr(func() MixedGraph { 68 | return MixedGraph(NewMixedMap()) 69 | })) 70 | r.AddNamedSpec("MixedGraph(MixedMatrix)", cr(func() MixedGraph { 71 | return MixedGraph(NewMixedMatrix(10)) 72 | })) 73 | 74 | gospec.MainGoTest(r, t) 75 | } 76 | -------------------------------------------------------------------------------- /src/graph/MixedMap.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "github.com/StepLg/go-erx/src/erx" 5 | ) 6 | 7 | // Mixed graph with map as a internal representation. 8 | // 9 | // Doesn't allow duplicate edges and arcs. 10 | type MixedMap struct { 11 | connections map[VertexId]map[VertexId]MixedConnectionType 12 | arcsCnt int 13 | edgesCnt int 14 | } 15 | 16 | func NewMixedMap() *MixedMap { 17 | g := &MixedMap { 18 | connections: make(map[VertexId]map[VertexId]MixedConnectionType), 19 | arcsCnt: 0, 20 | edgesCnt: 0, 21 | } 22 | return g 23 | } 24 | 25 | /////////////////////////////////////////////////////////////////////////////// 26 | // ConnectionsIterable 27 | 28 | func (g *MixedMap) ConnectionsIter() <-chan Connection { 29 | ch := make(chan Connection) 30 | panic(erx.NewError("Function doesn't implemented yet")) 31 | return ch 32 | } 33 | 34 | /////////////////////////////////////////////////////////////////////////////// 35 | // VertexesIterable 36 | 37 | func (g *MixedMap) VertexesIter() <-chan VertexId { 38 | ch := make(chan VertexId) 39 | go func() { 40 | for from, _ := range g.connections { 41 | ch <- from 42 | } 43 | close(ch) 44 | }() 45 | return ch 46 | } 47 | 48 | /////////////////////////////////////////////////////////////////////////////// 49 | // VertexesChecker 50 | 51 | func (g *MixedMap) CheckNode(node VertexId) (exists bool) { 52 | _, exists = g.connections[node] 53 | return 54 | } 55 | 56 | /////////////////////////////////////////////////////////////////////////////// 57 | // GraphVertexesWriter 58 | 59 | // Adding single node to graph 60 | func (g *MixedMap) AddNode(node VertexId) { 61 | defer func() { 62 | if e:=recover(); e!=nil { 63 | err := erx.NewSequent("Add node to graph.", e) 64 | err.AddV("node id", node) 65 | panic(err) 66 | } 67 | }() 68 | 69 | if _, ok := g.connections[node]; ok { 70 | panic(erx.NewError("Node already exists.")) 71 | } 72 | 73 | g.connections[node] = make(map[VertexId]MixedConnectionType) 74 | 75 | return 76 | } 77 | 78 | /////////////////////////////////////////////////////////////////////////////// 79 | // GraphVertexesRemover 80 | 81 | func (g *MixedMap) RemoveNode(node VertexId) { 82 | defer func() { 83 | if e:=recover(); e!=nil { 84 | err := erx.NewSequent("Remove node from graph.", e) 85 | err.AddV("node id", node) 86 | panic(err) 87 | } 88 | }() 89 | 90 | _, ok := g.connections[node] 91 | if !ok { 92 | panic(erx.NewError("Node doesn't exist.")) 93 | } 94 | 95 | g.connections[node] = nil, false 96 | for _, connectedVertexes := range g.connections { 97 | connectedVertexes[node] = CT_NONE, false 98 | } 99 | return 100 | } 101 | 102 | /////////////////////////////////////////////////////////////////////////////// 103 | // DirectedGraphArcsWriter 104 | 105 | func (g *MixedMap) touchNode(node VertexId) { 106 | if _, ok := g.connections[node]; !ok { 107 | g.connections[node] = make(map[VertexId]MixedConnectionType) 108 | } 109 | } 110 | 111 | // Adding arrow to graph. 112 | func (g *MixedMap) AddArc(from, to VertexId) { 113 | defer func() { 114 | if e:=recover(); e!=nil { 115 | err := erx.NewSequent("Add arc to graph.", e) 116 | err.AddV("tail", from) 117 | err.AddV("head", to) 118 | panic(err) 119 | } 120 | }() 121 | 122 | g.touchNode(from) 123 | g.touchNode(to) 124 | 125 | if direction, ok := g.connections[from][to]; ok { 126 | err := erx.NewError("Duplicate connection.") 127 | err.AddV("connection type", direction) 128 | panic(err) 129 | } 130 | 131 | g.connections[from][to] = CT_DIRECTED 132 | g.connections[to][from] = CT_DIRECTED_REVERSED 133 | g.arcsCnt++ 134 | return 135 | } 136 | 137 | /////////////////////////////////////////////////////////////////////////////// 138 | // DirectedGraphArcsRemover 139 | 140 | // Removing arrow 'from' and 'to' nodes 141 | func (g *MixedMap) RemoveArc(from, to VertexId) { 142 | defer func() { 143 | if e:=recover(); e!=nil { 144 | err := erx.NewSequent("Remove arc from graph.", e) 145 | err.AddV("tail", from) 146 | err.AddV("head", to) 147 | panic(err) 148 | } 149 | }() 150 | 151 | if _, ok := g.connections[from]; ok { 152 | panic(erx.NewError("Tail node doesn't exist.")) 153 | } 154 | 155 | if _, ok := g.connections[to]; ok { 156 | panic(erx.NewError("Head node doesn't exist.")) 157 | } 158 | 159 | if dir, ok := g.connections[from][to]; !ok || dir!=CT_DIRECTED { 160 | panic(erx.NewError("Arc doesn't exist.")) 161 | } 162 | 163 | g.connections[from][to] = CT_NONE, false 164 | g.connections[to][from] = CT_NONE, false 165 | g.arcsCnt-- 166 | 167 | return 168 | } 169 | 170 | /////////////////////////////////////////////////////////////////////////////// 171 | // DirectedGraphReader 172 | 173 | func (g *MixedMap) Order() int { 174 | return len(g.connections) 175 | } 176 | 177 | func (g *MixedMap) ArcsCnt() int { 178 | return g.arcsCnt 179 | } 180 | 181 | // Getting all graph sources. 182 | func (g *MixedMap) GetSources() VertexesIterable { 183 | iterator := func() <-chan VertexId { 184 | ch := make(chan VertexId) 185 | 186 | go func() { 187 | for VertexId, connections := range g.connections { 188 | isSource := true 189 | for _, connType := range connections { 190 | if connType==CT_DIRECTED_REVERSED { 191 | isSource = false 192 | break 193 | } 194 | } 195 | if isSource { 196 | ch <- VertexId 197 | } 198 | } 199 | 200 | close(ch) 201 | }() 202 | 203 | return ch 204 | } 205 | 206 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 207 | } 208 | 209 | // Getting all graph sinks. 210 | func (g *MixedMap) GetSinks() VertexesIterable { 211 | iterator := func() <-chan VertexId { 212 | ch := make(chan VertexId) 213 | 214 | go func() { 215 | for VertexId, connections := range g.connections { 216 | isSink := true 217 | for _, connType := range connections { 218 | if connType==CT_DIRECTED { 219 | isSink = false 220 | break 221 | } 222 | } 223 | if isSink { 224 | ch <- VertexId 225 | } 226 | } 227 | 228 | close(ch) 229 | }() 230 | 231 | return ch 232 | } 233 | 234 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 235 | } 236 | 237 | // Getting node accessors 238 | func (g *MixedMap) GetAccessors(node VertexId) VertexesIterable { 239 | iterator := func() <-chan VertexId { 240 | ch := make(chan VertexId) 241 | 242 | go func() { 243 | defer func() { 244 | if e := recover(); e!=nil { 245 | err := erx.NewSequent("Getting node accessors.", e) 246 | err.AddV("node id", node) 247 | panic(err) 248 | } 249 | }() 250 | 251 | accessorsMap, ok := g.connections[node] 252 | if !ok { 253 | panic(erx.NewError("Node doesn't exists.")) 254 | } 255 | 256 | for VertexId, connType := range accessorsMap { 257 | if connType==CT_DIRECTED { 258 | ch <- VertexId 259 | } 260 | } 261 | 262 | close(ch) 263 | }() 264 | 265 | return ch 266 | } 267 | 268 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 269 | } 270 | 271 | // Getting node predecessors 272 | func (g *MixedMap) GetPredecessors(node VertexId) VertexesIterable { 273 | iterator := func() <-chan VertexId { 274 | ch := make(chan VertexId) 275 | 276 | go func() { 277 | defer func() { 278 | if e := recover(); e!=nil { 279 | err := erx.NewSequent("Getting node predecessors.", e) 280 | err.AddV("node id", node) 281 | panic(err) 282 | } 283 | }() 284 | 285 | accessorsMap, ok := g.connections[node] 286 | if !ok { 287 | panic(erx.NewError("Node doesn't exists.")) 288 | } 289 | 290 | for VertexId, connType := range accessorsMap { 291 | if connType==CT_DIRECTED_REVERSED { 292 | ch <- VertexId 293 | } 294 | } 295 | 296 | close(ch) 297 | }() 298 | 299 | return ch 300 | } 301 | 302 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 303 | } 304 | 305 | func (g *MixedMap) CheckArc(from, to VertexId) (isExist bool) { 306 | defer func() { 307 | if e:=recover(); e!=nil { 308 | err := erx.NewSequent("Checking arc existance in graph.", e) 309 | err.AddV("tail", from) 310 | err.AddV("head", to) 311 | panic(err) 312 | } 313 | }() 314 | 315 | connectedVertexes, ok := g.connections[from] 316 | if !ok { 317 | panic(erx.NewError("Tail node doesn't exist.")) 318 | } 319 | 320 | if _, ok = g.connections[to]; !ok { 321 | panic(erx.NewError("Head node doesn't exist.")) 322 | } 323 | 324 | connType, ok := connectedVertexes[to] 325 | 326 | return ok && connType==CT_DIRECTED 327 | } 328 | 329 | func (g *MixedMap) ArcsIter() <-chan Connection { 330 | ch := make(chan Connection) 331 | go func() { 332 | for from, connectedVertexes := range g.connections { 333 | for to, connType := range connectedVertexes { 334 | if connType!=CT_UNDIRECTED { 335 | ch <- Connection{from, to} 336 | } 337 | } 338 | } 339 | close(ch) 340 | }() 341 | return ch 342 | } 343 | 344 | /////////////////////////////////////////////////////////////////////////////// 345 | // UndirectedGraphEdgesWriter 346 | 347 | // Adding edge to graph. 348 | func (g *MixedMap) AddEdge(from, to VertexId) { 349 | defer func() { 350 | if e:=recover(); e!=nil { 351 | err := erx.NewSequent("Add edge to graph.", e) 352 | err.AddV("node 1", from) 353 | err.AddV("node 2", to) 354 | panic(err) 355 | } 356 | }() 357 | 358 | g.touchNode(from) 359 | g.touchNode(to) 360 | 361 | if direction, ok := g.connections[from][to]; ok { 362 | err := erx.NewError("Duplicate connection.") 363 | err.AddV("connection type", direction) 364 | panic(err) 365 | } 366 | 367 | g.connections[from][to] = CT_UNDIRECTED 368 | g.connections[to][from] = CT_UNDIRECTED 369 | g.edgesCnt++ 370 | 371 | return 372 | } 373 | 374 | /////////////////////////////////////////////////////////////////////////////// 375 | // UndirectedGraphEdgesRemover 376 | 377 | // Removing arrow 'from' and 'to' nodes 378 | func (g *MixedMap) RemoveEdge(from, to VertexId) { 379 | defer func() { 380 | if e:=recover(); e!=nil { 381 | err := erx.NewSequent("Removing edge from graph.", e) 382 | err.AddV("node 1", from) 383 | err.AddV("node 2", to) 384 | panic(err) 385 | } 386 | }() 387 | 388 | if _, ok := g.connections[from]; !ok { 389 | panic(erx.NewError("First node doesn't exists")) 390 | } 391 | 392 | if _, ok := g.connections[to]; !ok { 393 | panic(erx.NewError("Second node doesn't exists")) 394 | } 395 | 396 | g.connections[from][to] = CT_NONE, false 397 | g.connections[to][from] = CT_NONE, false 398 | g.edgesCnt-- 399 | 400 | return 401 | } 402 | 403 | /////////////////////////////////////////////////////////////////////////////// 404 | // UndirectedGraphReader 405 | 406 | func (g *MixedMap) EdgesCnt() int { 407 | return g.edgesCnt 408 | } 409 | 410 | // Getting node predecessors 411 | func (g *MixedMap) GetNeighbours(node VertexId) VertexesIterable { 412 | iterator := func() <-chan VertexId { 413 | ch := make(chan VertexId) 414 | 415 | go func() { 416 | defer func() { 417 | if e:=recover(); e!=nil { 418 | err := erx.NewSequent("Get node neighbours.", e) 419 | err.AddV("node id", node) 420 | panic(err) 421 | } 422 | }() 423 | 424 | if connectedMap, ok := g.connections[node]; ok { 425 | for VertexId, connType := range connectedMap { 426 | if connType==CT_UNDIRECTED { 427 | ch <- VertexId 428 | } 429 | } 430 | } else { 431 | panic(erx.NewError("Node doesn't exists.")) 432 | } 433 | 434 | close(ch) 435 | }() 436 | 437 | return ch 438 | } 439 | 440 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 441 | } 442 | 443 | func (g *MixedMap) CheckEdge(from, to VertexId) bool { 444 | defer func() { 445 | if e:=recover(); e!=nil { 446 | err := erx.NewSequent("Check edge existance in graph.", e) 447 | err.AddV("node 1", from) 448 | err.AddV("node 2", to) 449 | panic(err) 450 | } 451 | }() 452 | 453 | connectedVertexes, ok := g.connections[from] 454 | if !ok { 455 | panic(erx.NewError("Fist node doesn't exist.")) 456 | } 457 | 458 | if _, ok = g.connections[to]; !ok { 459 | panic(erx.NewError("Second node doesn't exist.")) 460 | } 461 | 462 | direction, ok := connectedVertexes[to] 463 | 464 | return ok && direction==CT_UNDIRECTED 465 | } 466 | 467 | func (g *MixedMap) EdgesIter() <-chan Connection { 468 | ch := make(chan Connection) 469 | go func() { 470 | for from, connectedVertexes := range g.connections { 471 | for to, connType := range connectedVertexes { 472 | if from=to { 79 | continue 80 | } 81 | 82 | conn := gr.getConnectionId(from, to, false) 83 | if gr.nodes[conn]!=CT_NONE { 84 | ch <- Connection{from, to} 85 | } 86 | } 87 | } 88 | close(ch) 89 | }() 90 | return ch 91 | } 92 | 93 | /////////////////////////////////////////////////////////////////////////////// 94 | // VertexesIterable 95 | func (gr *MixedMatrix) VertexesIter() <-chan VertexId { 96 | ch := make(chan VertexId) 97 | go func() { 98 | for VertexId, _ := range gr.VertexIds { 99 | ch <- VertexId 100 | } 101 | close(ch) 102 | }() 103 | return ch 104 | } 105 | 106 | /////////////////////////////////////////////////////////////////////////////// 107 | // VertexesChecker 108 | 109 | func (g *MixedMatrix) CheckNode(node VertexId) (exists bool) { 110 | _, exists = g.VertexIds[node] 111 | return 112 | } 113 | 114 | /////////////////////////////////////////////////////////////////////////////// 115 | // UndirectedGraphEdgesWriter 116 | 117 | // Adding new edge to graph 118 | func (gr *MixedMatrix) AddEdge(node1, node2 VertexId) { 119 | defer func() { 120 | if e := recover(); e!=nil { 121 | err := erx.NewSequent("Add edge to mixed graph.", e) 122 | err.AddV("node 1", node1) 123 | err.AddV("node 2", node2) 124 | panic(err) 125 | } 126 | }() 127 | 128 | conn := gr.getConnectionId(node1, node2, true) 129 | if gr.nodes[conn]!=CT_NONE { 130 | err := erx.NewError("Duplicate edge.") 131 | err.AddV("connection id", conn) 132 | err.AddV("type", gr.nodes[conn]) 133 | panic(err) 134 | } 135 | 136 | gr.nodes[conn] = CT_UNDIRECTED 137 | gr.edgesCnt++ 138 | } 139 | 140 | /////////////////////////////////////////////////////////////////////////////// 141 | // UndirectedGraphEdgesRemover 142 | 143 | // Removing edge, connecting node1 and node2 144 | func (gr *MixedMatrix) RemoveEdge(node1, node2 VertexId) { 145 | defer func() { 146 | if e := recover(); e!=nil { 147 | err := erx.NewSequent("Remove edge from mixed graph.", e) 148 | err.AddV("node 1", node1) 149 | err.AddV("node 2", node2) 150 | panic(err) 151 | } 152 | }() 153 | 154 | conn := gr.getConnectionId(node1, node2, true) 155 | if gr.nodes[conn]!=CT_UNDIRECTED { 156 | err := erx.NewError("Edge doesn't exists.") 157 | err.AddV("connection id", conn) 158 | err.AddV("type", gr.nodes[conn]) 159 | panic(err) 160 | } 161 | 162 | gr.nodes[conn] = CT_NONE 163 | gr.edgesCnt-- 164 | } 165 | 166 | /////////////////////////////////////////////////////////////////////////////// 167 | // UndirectedGraphReader 168 | 169 | // Edges count in graph 170 | func (gr *MixedMatrix) EdgesCnt() int { 171 | return gr.edgesCnt 172 | } 173 | 174 | // Checking edge existance between node1 and node2 175 | // 176 | // node1 and node2 must exist in graph or error will be returned 177 | func (gr *MixedMatrix) CheckEdge(node1, node2 VertexId) bool { 178 | defer func() { 179 | if e := recover(); e!=nil { 180 | err := erx.NewSequent("Check edge in mixed graph.", e) 181 | err.AddV("node 1", node1) 182 | err.AddV("node 2", node2) 183 | panic(err) 184 | } 185 | }() 186 | 187 | if node1==node2 { 188 | return false 189 | } 190 | 191 | return gr.nodes[gr.getConnectionId(node1, node2, false)]==CT_UNDIRECTED 192 | } 193 | 194 | // Getting all nodes, connected to given one 195 | func (gr *MixedMatrix) GetNeighbours(node VertexId) VertexesIterable { 196 | iterator := func() <-chan VertexId { 197 | ch := make(chan VertexId) 198 | 199 | go func() { 200 | defer func() { 201 | if e := recover(); e!=nil { 202 | err := erx.NewSequent("Get node neighbours in mixed graph.", e) 203 | err.AddV("node", node) 204 | panic(err) 205 | } 206 | }() 207 | 208 | for neighbour, _ := range gr.VertexIds { 209 | if node==neighbour { 210 | // skipping loops 211 | continue 212 | } 213 | 214 | connId := gr.getConnectionId(node, neighbour, false) 215 | if gr.nodes[connId]==CT_UNDIRECTED { 216 | ch <- neighbour 217 | } 218 | } 219 | close(ch) 220 | }() 221 | 222 | return ch 223 | } 224 | 225 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 226 | } 227 | 228 | /////////////////////////////////////////////////////////////////////////////// 229 | // DirectedGraphArcsWriter 230 | 231 | // Adding directed arc to graph 232 | func (gr *MixedMatrix) AddArc(tail, head VertexId) { 233 | defer func() { 234 | if e := recover(); e!=nil { 235 | err := erx.NewSequent("Add arc to mixed graph.", e) 236 | err.AddV("tail", tail) 237 | err.AddV("head", head) 238 | panic(err) 239 | } 240 | }() 241 | 242 | conn := gr.getConnectionId(tail, head, true) 243 | if gr.nodes[conn]!=CT_NONE { 244 | err := erx.NewError("Duplicate edge.") 245 | err.AddV("connection id", conn) 246 | err.AddV("type", gr.nodes[conn]) 247 | panic(err) 248 | } 249 | 250 | if tail=to { 493 | continue 494 | } 495 | 496 | if gr.nodes[gr.getConnectionId(from, to, false)]==CT_UNDIRECTED { 497 | ch <- Connection{from, to} 498 | } 499 | } 500 | } 501 | close(ch) 502 | }() 503 | return ch 504 | } 505 | 506 | // Iterate over only directed arcs 507 | func (gr *MixedMatrix) ArcsIter() <-chan Connection { 508 | ch := make(chan Connection) 509 | go func() { 510 | for from, _ := range gr.VertexIds { 511 | for to, _ := range gr.VertexIds { 512 | if from>=to { 513 | continue 514 | } 515 | 516 | conn := gr.getConnectionId(from, to, false) 517 | if gr.nodes[conn]==CT_DIRECTED { 518 | ch <- Connection{from, to} 519 | } 520 | if gr.nodes[conn]==CT_DIRECTED_REVERSED { 521 | ch <- Connection{to, from} 522 | } 523 | } 524 | } 525 | close(ch) 526 | }() 527 | return ch 528 | } 529 | 530 | func (gr *MixedMatrix) CheckEdgeType(tail VertexId, head VertexId) MixedConnectionType { 531 | defer func() { 532 | if e := recover(); e!=nil { 533 | err := erx.NewSequent("Check edge type in mixed graph.", e) 534 | err.AddV("tail", tail) 535 | err.AddV("head", head) 536 | panic(err) 537 | } 538 | }() 539 | 540 | conn := gr.getConnectionId(tail, head, false) 541 | connType := gr.nodes[conn] 542 | if connType==CT_DIRECTED && tail>head { 543 | return CT_DIRECTED_REVERSED 544 | } 545 | return connType 546 | } 547 | 548 | func (g *MixedMatrix) ConnectionsCnt() int { 549 | return g.arcsCnt + g.edgesCnt 550 | } 551 | 552 | func (gr *MixedMatrix) TypedConnectionsIter() <-chan TypedConnection { 553 | ch := make(chan TypedConnection) 554 | go func() { 555 | for from, _ := range gr.VertexIds { 556 | for to, _ := range gr.VertexIds { 557 | if from>=to { 558 | continue 559 | } 560 | 561 | conn := gr.getConnectionId(from, to, false) 562 | switch gr.nodes[conn] { 563 | case CT_NONE: 564 | case CT_UNDIRECTED: 565 | ch <- TypedConnection{Connection:Connection{Tail: from, Head:to}, Type:CT_UNDIRECTED} 566 | case CT_DIRECTED: 567 | ch <- TypedConnection{Connection:Connection{Tail: from, Head:to}, Type:CT_DIRECTED} 568 | case CT_DIRECTED_REVERSED: 569 | ch <- TypedConnection{Connection:Connection{Tail: to, Head:from}, Type:CT_DIRECTED} 570 | default: 571 | err := erx.NewError("Internal error: wrong connection type in mixed graph matrix") 572 | err.AddV("connection type", gr.nodes[conn]) 573 | err.AddV("connection id", conn) 574 | err.AddV("tail node", from) 575 | err.AddV("head node", to) 576 | panic(err) 577 | } 578 | } 579 | } 580 | close(ch) 581 | }() 582 | return ch 583 | } 584 | 585 | func (gr *MixedMatrix) getConnectionId(node1, node2 VertexId, create bool) int { 586 | defer func() { 587 | if e := recover(); e!=nil { 588 | err := erx.NewSequent("Calculating connection id.", e) 589 | err.AddV("node 1", node1) 590 | err.AddV("node 2", node2) 591 | panic(err) 592 | } 593 | }() 594 | 595 | var id1, id2 int 596 | node1Exist := false 597 | node2Exist := false 598 | id1, node1Exist = gr.VertexIds[node1] 599 | id2, node2Exist = gr.VertexIds[node2] 600 | 601 | // checking for errors 602 | { 603 | if node1==node2 { 604 | panic(erx.NewError("Equal nodes.")) 605 | } 606 | if !create { 607 | if !node1Exist { 608 | panic(erx.NewError("First node doesn't exist in graph")) 609 | } 610 | if !node2Exist { 611 | panic(erx.NewError("Second node doesn't exist in graph")) 612 | } 613 | } else if !node1Exist || !node2Exist { 614 | if node1Exist && node2Exist { 615 | if gr.size - len(gr.VertexIds) < 2 { 616 | panic(erx.NewError("Not enough space to create two new nodes.")) 617 | } 618 | } else { 619 | if gr.size - len(gr.VertexIds) < 1 { 620 | panic(erx.NewError("Not enough space to create new node.")) 621 | } 622 | } 623 | } 624 | } 625 | 626 | if !node1Exist { 627 | id1 = int(len(gr.VertexIds)) 628 | gr.VertexIds[node1] = id1 629 | } 630 | 631 | if !node2Exist { 632 | id2 = int(len(gr.VertexIds)) 633 | gr.VertexIds[node2] = id2 634 | } 635 | 636 | // switching id1, id2 in order to id1 < id2 637 | if id1>id2 { 638 | id1, id2 = id2, id1 639 | } 640 | 641 | // id from upper triangle matrix, stored in vector 642 | connId := id1*(gr.size-1) + id2 - 1 - id1*(id1+1)/2 643 | return connId 644 | } 645 | -------------------------------------------------------------------------------- /src/graph/UndirectedGraph_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "testing" 5 | "github.com/orfjackal/gospec/src/gospec" 6 | . "github.com/orfjackal/gospec/src/gospec" 7 | ) 8 | 9 | func UndirectedGraphSpec(c gospec.Context, graphCreator func() UndirectedGraph) { 10 | gr := graphCreator() 11 | 12 | c.Specify("Empty undirected graph", func() { 13 | c.Specify("contain no nodes", func() { 14 | c.Expect(gr.Order(), Equals, 0) 15 | }) 16 | c.Specify("contain no edges", func() { 17 | c.Expect(gr.EdgesCnt(), Equals, 0) 18 | }) 19 | }) 20 | 21 | 22 | c.Specify("New node in empty graph", func() { 23 | VertexId := VertexId(1) 24 | gr.AddNode(VertexId) 25 | 26 | c.Specify("changing nodes count", func() { 27 | c.Expect(gr.Order(), Equals, 1) 28 | }) 29 | 30 | c.Specify("doesn't change edges count", func() { 31 | c.Expect(gr.EdgesCnt(), Equals, 0) 32 | }) 33 | 34 | c.Specify("no neighbours", func() { 35 | c.Expect(len(CollectVertexes(gr.GetNeighbours(VertexId))), Equals, 0) 36 | }) 37 | }) 38 | 39 | c.Specify("New edge in empty graph", func() { 40 | n1 := VertexId(1) 41 | n2 := VertexId(2) 42 | gr.AddEdge(n1, n2) 43 | 44 | c.Specify("changing nodes count", func() { 45 | c.Expect(gr.Order(), Equals, 2) 46 | }) 47 | 48 | c.Specify("changing edges count", func() { 49 | c.Expect(gr.EdgesCnt(), Equals, 1) 50 | }) 51 | 52 | c.Specify("neighbours", func() { 53 | c.Expect(CollectVertexes(gr.GetNeighbours(n1)), ContainsExactly, Values(n2)) 54 | c.Expect(CollectVertexes(gr.GetNeighbours(n2)), ContainsExactly, Values(n1)) 55 | }) 56 | }) 57 | } 58 | 59 | func TestUndirectedGraphSpec(t *testing.T) { 60 | r := gospec.NewRunner() 61 | 62 | // paramenerized test creator 63 | cr := func(graphCreator func() UndirectedGraph) func (c gospec.Context) { 64 | return func(c gospec.Context){ 65 | UndirectedGraphSpec(c, graphCreator) 66 | } 67 | } 68 | 69 | r.AddNamedSpec("UndirectedGraph(Map)", cr(func() UndirectedGraph { 70 | return UndirectedGraph(NewUndirectedMap()) 71 | })) 72 | r.AddNamedSpec("UndirectedGraph(Matrix)", cr(func() UndirectedGraph { 73 | return UndirectedGraph(NewUndirectedMatrix(10)) 74 | })) 75 | r.AddNamedSpec("UndirectedGraph(MixedMatrix)", cr(func() UndirectedGraph { 76 | return UndirectedGraph(NewMixedMatrix(10)) 77 | })) 78 | r.AddNamedSpec("UndirectedGraph(MixedMap)", cr(func() UndirectedGraph { 79 | return UndirectedGraph(NewMixedMap()) 80 | })) 81 | gospec.MainGoTest(r, t) 82 | } 83 | -------------------------------------------------------------------------------- /src/graph/UndirectedMap.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "github.com/StepLg/go-erx/src/erx" 5 | ) 6 | 7 | type UndirectedMap struct { 8 | edges map[VertexId]map[VertexId]bool 9 | edgesCnt int 10 | } 11 | 12 | func NewUndirectedMap() *UndirectedMap { 13 | g := new(UndirectedMap) 14 | g.edges = make(map[VertexId]map[VertexId]bool) 15 | g.edgesCnt = 0 16 | return g 17 | } 18 | 19 | /////////////////////////////////////////////////////////////////////////////// 20 | // ConnectionsIterable 21 | 22 | func (g *UndirectedMap) ConnectionsIter() <-chan Connection { 23 | return g.EdgesIter() 24 | } 25 | 26 | /////////////////////////////////////////////////////////////////////////////// 27 | // VertexesIterable 28 | 29 | func (g *UndirectedMap) VertexesIter() <-chan VertexId { 30 | ch := make(chan VertexId) 31 | go func() { 32 | for from, _ := range g.edges { 33 | ch <- from 34 | } 35 | close(ch) 36 | }() 37 | return ch 38 | } 39 | 40 | /////////////////////////////////////////////////////////////////////////////// 41 | // VertexesChecker 42 | 43 | func (g *UndirectedMap) CheckNode(node VertexId) (exists bool) { 44 | _, exists = g.edges[node] 45 | return 46 | } 47 | 48 | /////////////////////////////////////////////////////////////////////////////// 49 | // UndirectedGraphVertexesWriter 50 | 51 | // Adding single node to graph 52 | func (g *UndirectedMap) AddNode(node VertexId) { 53 | makeError := func(err interface{}) (res erx.Error) { 54 | res = erx.NewSequentLevel("Add node to graph.", err, 1) 55 | res.AddV("node id", node) 56 | return 57 | } 58 | 59 | if _, ok := g.edges[node]; ok { 60 | panic(makeError(erx.NewError("Node already exists."))) 61 | } 62 | 63 | g.edges[node] = make(map[VertexId]bool) 64 | 65 | return 66 | } 67 | 68 | /////////////////////////////////////////////////////////////////////////////// 69 | // GraphVertexesRemover 70 | 71 | func (g *UndirectedMap) RemoveNode(node VertexId) { 72 | makeError := func(err interface{}) (res erx.Error) { 73 | res = erx.NewSequentLevel("Remove node from graph.", err, 1) 74 | res.AddV("node id", node) 75 | return 76 | } 77 | 78 | if _, ok := g.edges[node]; !ok { 79 | panic(makeError(erx.NewError("Node doesn't exist."))) 80 | } 81 | 82 | g.edges[node] = nil, false 83 | for _, connectedVertexes := range g.edges { 84 | connectedVertexes[node] = false, false 85 | } 86 | 87 | return 88 | } 89 | 90 | /////////////////////////////////////////////////////////////////////////////// 91 | // UndirectedGraphEdgesWriter 92 | 93 | func (g *UndirectedMap) touchNode(node VertexId) { 94 | if _, ok := g.edges[node]; !ok { 95 | g.edges[node] = make(map[VertexId]bool) 96 | } 97 | } 98 | 99 | // Adding arrow to graph. 100 | func (g *UndirectedMap) AddEdge(from, to VertexId) { 101 | makeError := func(err interface{}) (res erx.Error) { 102 | res = erx.NewSequentLevel("Add edge to graph.", err, 1) 103 | res.AddV("node 1", from) 104 | res.AddV("node 2", to) 105 | return 106 | } 107 | 108 | g.touchNode(from) 109 | g.touchNode(to) 110 | 111 | if direction, ok := g.edges[from][to]; ok && direction { 112 | panic(makeError(erx.NewError("Duplicate arrow."))) 113 | } 114 | 115 | g.edges[from][to] = true 116 | g.edges[to][from] = true 117 | g.edgesCnt++ 118 | 119 | return 120 | } 121 | 122 | /////////////////////////////////////////////////////////////////////////////// 123 | // UndirectedGraphEdgesRemover 124 | 125 | // Removing arrow 'from' and 'to' nodes 126 | func (g *UndirectedMap) RemoveEdge(from, to VertexId) { 127 | makeError := func(err interface{}) (res erx.Error) { 128 | res = erx.NewSequentLevel("Remove edge from graph.", err, 1) 129 | res.AddV("node 1", from) 130 | res.AddV("node 2", to) 131 | return 132 | } 133 | connectedVertexes, ok := g.edges[from] 134 | if !ok { 135 | panic(makeError(erx.NewError("First node doesn't exists"))) 136 | } 137 | 138 | if _, ok = connectedVertexes[to]; ok { 139 | panic(makeError(erx.NewError("Second node doesn't exists"))) 140 | } 141 | 142 | g.edges[from][to] = false, false 143 | g.edges[to][from] = false, false 144 | g.edgesCnt-- 145 | 146 | return 147 | } 148 | 149 | /////////////////////////////////////////////////////////////////////////////// 150 | // UndirectedGraphReader 151 | 152 | func (g *UndirectedMap) Order() int { 153 | return len(g.edges) 154 | } 155 | 156 | func (g *UndirectedMap) EdgesCnt() int { 157 | return g.edgesCnt 158 | } 159 | 160 | // Getting node predecessors 161 | func (g *UndirectedMap) GetNeighbours(node VertexId) VertexesIterable { 162 | iterator := func() <-chan VertexId { 163 | ch := make(chan VertexId) 164 | go func() { 165 | if connectedMap, ok := g.edges[node]; ok { 166 | for VertexId, _ := range connectedMap { 167 | ch <- VertexId 168 | } 169 | } else { 170 | panic(erx.NewError("Node doesn't exists.")) 171 | } 172 | close(ch) 173 | }() 174 | return ch 175 | } 176 | 177 | return VertexesIterable(&nodesIterableLambdaHelper{iterFunc:iterator}) 178 | } 179 | 180 | func (g *UndirectedMap) CheckEdge(from, to VertexId) (isExist bool) { 181 | makeError := func(err interface{}) (res erx.Error) { 182 | res = erx.NewSequentLevel("Check edge existance in graph.", err, 1) 183 | res.AddV("node 1", from) 184 | res.AddV("node 2", to) 185 | return 186 | } 187 | 188 | connectedVertexes, ok := g.edges[from] 189 | if !ok { 190 | panic(makeError(erx.NewError("Fist node doesn't exist."))) 191 | } 192 | 193 | if _, ok = g.edges[to]; !ok { 194 | panic(makeError(erx.NewError("Second node doesn't exist."))) 195 | } 196 | 197 | _, isExist = connectedVertexes[to] 198 | 199 | return 200 | } 201 | 202 | func (g *UndirectedMap) EdgesIter() <-chan Connection { 203 | ch := make(chan Connection) 204 | go func() { 205 | for from, connectedVertexes := range g.edges { 206 | for to, _ := range connectedVertexes { 207 | if fromid2 { 302 | id1, id2 = id2, id1 303 | } 304 | 305 | // id from upper triangle matrix, stored in vector 306 | connId := id1*(g.size-1) + id2 - 1 - id1*(id1+1)/2 307 | return connId 308 | } 309 | -------------------------------------------------------------------------------- /src/graph/algorithms.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "github.com/StepLg/go-erx/src/erx" 5 | ) 6 | 7 | // Copy graph og to rg except args i->j, where exists non direct path i->...->j 8 | // 9 | // Graph rg contains all vertexes from original graph gr and arcs i->j, if there 10 | // doesn't exist path in original graph from i to j, which contains at least 11 | // 3 vertexes 12 | func ReduceDirectPaths(og DirectedGraphReader, rg DirectedGraphArcsWriter, stopFunc func(from, to VertexId, weight float64) bool) { 13 | var checkStopFunc StopFunc 14 | for conn := range og.ArcsIter() { 15 | filteredGraph := NewDirectedGraphArcFilter(og, conn.Tail, conn.Head) 16 | if stopFunc!=nil { 17 | checkStopFunc = func(node VertexId, weight float64) bool { 18 | return stopFunc(conn.Tail, node, weight) 19 | } 20 | } else { 21 | checkStopFunc = nil 22 | } 23 | if !CheckDirectedPathDijkstra(filteredGraph, conn.Tail, conn.Head, checkStopFunc, SimpleWeightFunc) { 24 | rg.AddArc(conn.Tail, conn.Head) 25 | } 26 | } 27 | } 28 | 29 | // Topological sort of directed graph 30 | // 31 | // Return nodes in topological order. If graph has cycles, then hasCycles==true 32 | // and nodes==nil in function result. 33 | func TopologicalSort(gr DirectedGraphReader) (nodes []VertexId, hasCycles bool) { 34 | hasCycles = false 35 | nodes = make([]VertexId, gr.Order()) 36 | pos := len(nodes) 37 | // map of node status. If node doesn't present in map - white color, 38 | // node in map with false value - grey color, and with true value - black color 39 | status := make(map[VertexId]bool) 40 | for source := range gr.GetSources().VertexesIter() { 41 | pos, hasCycles = topologicalSortHelper(gr, source, nodes[0:pos], status) 42 | if hasCycles { 43 | nodes = nil 44 | return 45 | } 46 | } 47 | if pos!=0 { 48 | // cycle without path from any source to this cycle 49 | nodes = nil 50 | hasCycles = true 51 | } 52 | return 53 | } 54 | 55 | func topologicalSortHelper(gr DirectedGraphReader, curNode VertexId, nodes []VertexId, status map[VertexId]bool) (pos int, hasCycles bool) { 56 | if isBlack, ok := status[curNode]; ok { 57 | err := erx.NewError("Internal error in topological sort: node already in status map") 58 | err.AddV("node id", curNode) 59 | err.AddV("status in map", isBlack) 60 | panic(err) 61 | } 62 | hasCycles = false 63 | status[curNode] = false 64 | pos = len(nodes) 65 | for accessor := range gr.GetAccessors(curNode).VertexesIter() { 66 | if isBlack, ok := status[accessor]; ok { 67 | if !isBlack { 68 | // cycle detected! 69 | hasCycles = true 70 | return 71 | } else { 72 | // we have already visited this node 73 | continue 74 | } 75 | } 76 | pos, hasCycles = topologicalSortHelper(gr, accessor, nodes[0:pos], status) 77 | if hasCycles { 78 | return 79 | } 80 | } 81 | status[curNode] = true 82 | pos-- 83 | nodes[pos] = curNode 84 | return 85 | } 86 | 87 | // Split mixed graph to independed subraphs. 88 | // 89 | // Each result subgraph contain only those vertexes, which are connected, and 90 | // which are disjoint with any vertex from any other subgraph. 91 | // 92 | // @todo: Add creator function to control type of new created graphs. 93 | // @todo: Here could be done lot's of optimisation: recolor the smallest color 94 | // and parallelisation. 95 | func SplitGraphToIndependentSubgraphs_mixed(gr MixedGraphReader) []MixedGraph { 96 | // vertexes color map. Two vertexes have same color if and only if there exist 97 | // any path (which could contain both arcs and edges) from one to another 98 | nodesColor := make(map[VertexId]int) 99 | curColor := 0 100 | 101 | // coloring vertexes 102 | for curNode := range gr.GetSources().VertexesIter() { 103 | if _, ok := nodesColor[curNode]; ok { 104 | // node already visited 105 | continue 106 | } 107 | splitGraphToIndependentSubgraphs_helper(curNode, curColor, NewMgraphOutNeighboursExtractor(gr), nodesColor) 108 | curColor++ 109 | } 110 | 111 | // get total vertexes number of each subgraph 112 | colors := make(map[int]int) 113 | for _, color := range nodesColor { 114 | if _, ok := colors[color]; !ok { 115 | colors[color] = 0 116 | } 117 | colors[color]++ 118 | } 119 | 120 | // making subgraphs map with color as a key 121 | result := make(map[int]MixedGraph, len(colors)) 122 | for color, nodesCnt := range colors { 123 | result[color] = NewMixedMatrix(nodesCnt) 124 | } 125 | 126 | // copying nodes to subgraphs 127 | for node := range gr.VertexesIter() { 128 | result[nodesColor[node]].AddNode(node) 129 | } 130 | 131 | // copying arcs to subgraphs 132 | for arc := range gr.ArcsIter() { 133 | result[nodesColor[arc.Tail]].AddArc(arc.Tail, arc.Head) 134 | } 135 | 136 | // copying edges to subgraphs 137 | for edge := range gr.EdgesIter() { 138 | result[nodesColor[edge.Tail]].AddEdge(edge.Tail, edge.Head) 139 | } 140 | 141 | // making result slice 142 | resultSlice := make([]MixedGraph, len(result)) 143 | i := 0 144 | for _, subgraph := range result { 145 | resultSlice[i] = subgraph 146 | i++ 147 | } 148 | return resultSlice 149 | } 150 | 151 | // Split directed graph to independed subraphs. 152 | // 153 | // Each result subgraph contain only those vertexes, which are connected, and 154 | // which are disjoint with any vertex from any other subgraph. 155 | // 156 | // @todo: Add creator function to control type of new created graphs 157 | // @todo: Here could be done lot's of optimisation: recolor the smallest color 158 | // and parallelisation. 159 | func SplitGraphToIndependentSubgraphs_directed(gr DirectedGraphReader) []DirectedGraph { 160 | // vertexes color map. Two vertexes have same color if and only if there exist 161 | // any path (which could contain both arcs and edges) from one to another 162 | nodesColor := make(map[VertexId]int) 163 | curColor := 0 164 | 165 | // coloring vertexes 166 | for curNode := range gr.GetSources().VertexesIter() { 167 | splitGraphToIndependentSubgraphs_helper(curNode, curColor, NewDgraphOutNeighboursExtractor(gr), nodesColor) 168 | curColor++ 169 | } 170 | 171 | // copying nodes to subgraphs 172 | result := make(map[int]DirectedGraph) 173 | for node := range gr.VertexesIter() { 174 | var subgr DirectedGraph 175 | var ok bool 176 | if subgr, ok = result[nodesColor[node]]; !ok { 177 | subgr = NewDirectedMap() 178 | result[nodesColor[node]] = subgr 179 | } 180 | subgr.AddNode(node) 181 | } 182 | 183 | // copying arcs to subgraphs 184 | for arc := range gr.ArcsIter() { 185 | result[nodesColor[arc.Tail]].AddArc(arc.Tail, arc.Head) 186 | } 187 | 188 | // making result slice 189 | resultSlice := make([]DirectedGraph, len(result)) 190 | i := 0 191 | for _, subgraph := range result { 192 | resultSlice[i] = subgraph 193 | i++ 194 | } 195 | return resultSlice 196 | } 197 | 198 | // Split undirected graph to independed subraphs. 199 | // 200 | // Each result subgraph contain only those vertexes, which are connected, and 201 | // which are disjoint with any vertex from any other subgraph. 202 | // 203 | // @todo: Add creator function to control type of new created graphs 204 | // @todo: Here could be done lot's of optimisation: recolor the smallest color 205 | // and parallelisation. 206 | func SplitGraphToIndependentSubgraphs_undirected(gr UndirectedGraphReader) []UndirectedGraph { 207 | // vertexes color map. Two vertexes have same color if and only if there exist 208 | // any path (which could contain both arcs and edges) from one to another 209 | nodesColor := make(map[VertexId]int) 210 | curColor := 0 211 | 212 | // coloring vertexes 213 | for curNode := range gr.VertexesIter() { 214 | if _, ok := nodesColor[curNode]; ok { 215 | // node already visited 216 | continue 217 | } 218 | splitGraphToIndependentSubgraphs_helper(curNode, curColor, NewUgraphOutNeighboursExtractor(gr), nodesColor) 219 | curColor++ 220 | } 221 | 222 | // copying nodes to subgraphs 223 | result := make(map[int]UndirectedGraph) 224 | for node := range gr.VertexesIter() { 225 | var subgr UndirectedGraph 226 | var ok bool 227 | if subgr, ok = result[nodesColor[node]]; !ok { 228 | subgr = NewUndirectedMap() 229 | result[nodesColor[node]] = subgr 230 | } 231 | subgr.AddNode(node) 232 | } 233 | 234 | // copying edges to subgraphs 235 | for edge := range gr.EdgesIter() { 236 | result[nodesColor[edge.Tail]].AddEdge(edge.Tail, edge.Head) 237 | } 238 | 239 | // making result slice 240 | resultSlice := make([]UndirectedGraph, len(result)) 241 | i := 0 242 | for _, subgraph := range result { 243 | resultSlice[i] = subgraph 244 | i++ 245 | } 246 | return resultSlice 247 | } 248 | 249 | // Helper function for splitting graph to independent subgraphs. 250 | // 251 | // Function assign color to each vertex, accessible from given one. If some 252 | // vertex already has different color, then all vertexes with this calor are 253 | // changing color to new. 254 | func splitGraphToIndependentSubgraphs_helper(node VertexId, color int, gr OutNeighboursExtractor, nodesColor map[VertexId]int) { 255 | nodesColor[node] = color 256 | for next := range gr.GetOutNeighbours(node).VertexesIter() { 257 | if nextColor, ok := nodesColor[next]; ok { 258 | if nextColor != color { 259 | // change all 'nextColor' nodes to 'color' nodes 260 | for k, v := range nodesColor { 261 | if v==nextColor { 262 | nodesColor[k] = color 263 | } 264 | } 265 | } 266 | } else { 267 | splitGraphToIndependentSubgraphs_helper(next, color, gr, nodesColor) 268 | } 269 | } 270 | return 271 | } 272 | -------------------------------------------------------------------------------- /src/graph/algorithms_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "testing" 5 | "github.com/orfjackal/gospec/src/gospec" 6 | . "github.com/orfjackal/gospec/src/gospec" 7 | ) 8 | 9 | func ReduceDirectPathsSpec(c gospec.Context) { 10 | c.Specify("Reduced triangle", func() { 11 | gr := NewDirectedMap() 12 | gr.AddArc(1, 2) 13 | gr.AddArc(2, 3) 14 | gr.AddArc(1, 3) 15 | 16 | rgr := NewDirectedMap() 17 | ReduceDirectPaths(gr, rgr, nil) 18 | 19 | expectedGraph := NewDirectedMap() 20 | expectedGraph.AddArc(1, 2) 21 | expectedGraph.AddArc(2, 3) 22 | c.Expect(rgr, DirectedGraphEquals, expectedGraph) 23 | }) 24 | 25 | c.Specify("A bit more complex example", func() { 26 | gr := NewDirectedMap() 27 | gr.AddArc(1, 2) 28 | gr.AddArc(2, 3) 29 | gr.AddArc(3, 4) 30 | gr.AddArc(2, 4) 31 | gr.AddArc(4, 5) 32 | gr.AddArc(1, 6) 33 | gr.AddArc(2, 6) 34 | 35 | rgr := NewDirectedMap() 36 | ReduceDirectPaths(gr, rgr, nil) 37 | 38 | expectedGraph := NewDirectedMap() 39 | expectedGraph.AddArc(1, 2) 40 | expectedGraph.AddArc(2, 3) 41 | expectedGraph.AddArc(3, 4) 42 | expectedGraph.AddArc(4, 5) 43 | expectedGraph.AddArc(2, 6) 44 | c.Expect(rgr, DirectedGraphEquals, expectedGraph) 45 | }) 46 | } 47 | 48 | func TopologicalSortSpec(c gospec.Context) { 49 | gr := NewDirectedMap() 50 | c.Specify("Single node graph", func() { 51 | gr.AddNode(VertexId(1)) 52 | nodes, hasCycle := TopologicalSort(gr) 53 | c.Expect(hasCycle, IsFalse) 54 | c.Expect(nodes, ContainsExactly, Values(VertexId(1))) 55 | }) 56 | 57 | c.Specify("Simple two nodes graph", func() { 58 | gr.AddArc(1, 2) 59 | nodes, hasCycle := TopologicalSort(gr) 60 | c.Expect(hasCycle, IsFalse) 61 | c.Expect(nodes, ContainsExactly, Values(VertexId(1), VertexId(2))) 62 | }) 63 | 64 | c.Specify("Pseudo loops", func() { 65 | gr.AddArc(1, 2) 66 | gr.AddArc(2, 3) 67 | gr.AddArc(1, 4) 68 | gr.AddArc(4, 3) 69 | 70 | _, hasCycle := TopologicalSort(gr) 71 | c.Expect(hasCycle, IsFalse) 72 | }) 73 | } 74 | 75 | func SplitGraphToIndependentSubgraphs_mixedSpec(c gospec.Context) { 76 | c.Specify("Single node graph", func() { 77 | subgr1 := NewMixedMatrix(3) 78 | subgr1.AddArc(1, 2) 79 | subgr1.AddEdge(1, 3) 80 | subgr2 := NewMixedMatrix(3) 81 | subgr2.AddArc(4, 5) 82 | subgr2.AddArc(5, 6) 83 | subgr2.AddArc(4, 6) 84 | 85 | gr := NewMixedMatrix(6) 86 | CopyMixedGraph(subgr1, gr) 87 | CopyMixedGraph(subgr2, gr) 88 | 89 | subgraphs := SplitGraphToIndependentSubgraphs_mixed(gr) 90 | c.Expect(len(subgraphs), Equals, 2) 91 | // @todo: Add ContainsGraph comparator to check if slice contains a graph 92 | c.Expect(MixedGraphsEquals(subgr1, subgraphs[1]), IsTrue) 93 | c.Expect(MixedGraphsEquals(subgr2, subgraphs[0]), IsTrue) 94 | }) 95 | } 96 | 97 | func SplitGraphToIndependentSubgraphs_directedSpec(c gospec.Context) { 98 | c.Specify("Directed graph with 2 independent parts", func() { 99 | gr1, gr2, gr_merged := genDgr2IndependentSubGr() 100 | subgraphs := SplitGraphToIndependentSubgraphs_directed(gr_merged) 101 | c.Expect(len(subgraphs), Equals, 2) 102 | if subgraphs[0].CheckNode(VertexId(1)) { 103 | c.Expect(DirectedGraphsEquals(subgraphs[0], gr1), IsTrue) 104 | c.Expect(DirectedGraphsEquals(subgraphs[1], gr2), IsTrue) 105 | } else { 106 | c.Expect(DirectedGraphsEquals(subgraphs[0], gr2), IsTrue) 107 | c.Expect(DirectedGraphsEquals(subgraphs[1], gr1), IsTrue) 108 | } 109 | }) 110 | } 111 | 112 | func SplitGraphToIndependentSubgraphs_undirectedSpec(c gospec.Context) { 113 | c.Specify("Undirected graph with 2 independent parts", func() { 114 | gr1, gr2, gr_merged := genUgr2IndependentSubGr() 115 | subgraphs := SplitGraphToIndependentSubgraphs_undirected(gr_merged) 116 | c.Expect(len(subgraphs), Equals, 2) 117 | if subgraphs[0].CheckNode(VertexId(1)) { 118 | c.Expect(UndirectedGraphsEquals(subgraphs[0], gr1), IsTrue) 119 | c.Expect(UndirectedGraphsEquals(subgraphs[1], gr2), IsTrue) 120 | } else { 121 | c.Expect(UndirectedGraphsEquals(subgraphs[0], gr2), IsTrue) 122 | c.Expect(UndirectedGraphsEquals(subgraphs[1], gr1), IsTrue) 123 | } 124 | }) 125 | } 126 | 127 | 128 | func TestAlgorithms(t *testing.T) { 129 | r := gospec.NewRunner() 130 | r.AddSpec(ReduceDirectPathsSpec) 131 | r.AddSpec(TopologicalSortSpec) 132 | r.AddSpec(SplitGraphToIndependentSubgraphs_mixedSpec) 133 | r.AddSpec(SplitGraphToIndependentSubgraphs_directedSpec) 134 | r.AddSpec(SplitGraphToIndependentSubgraphs_undirectedSpec) 135 | gospec.MainGoTest(r, t) 136 | } 137 | -------------------------------------------------------------------------------- /src/graph/comparators.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "github.com/StepLg/go-erx/src/erx" 5 | ) 6 | 7 | // Check two mixed graph equality 8 | func MixedGraphsEquals(gr1, gr2 MixedGraphReader) bool { 9 | // checking nodes equality 10 | if !GraphIncludeVertexes(gr1, gr2) || !GraphIncludeVertexes(gr2, gr1) { 11 | return false 12 | } 13 | 14 | // checking connections equality 15 | if !MixedGraphIncludeConnections(gr1, gr2) || !MixedGraphIncludeConnections(gr2, gr1) { 16 | return false 17 | } 18 | 19 | return true 20 | } 21 | 22 | // Check if graph gr include all connections 23 | // 24 | // Warning!!! Due to channels issue 296: http://code.google.com/p/go/issues/detail?id=296 25 | // goroutine will block if not all connections exists in graph 26 | func MixedGraphIncludeConnections(gr MixedGraphReader, connections TypedConnectionsIterable) bool { 27 | for conn := range connections.TypedConnectionsIter() { 28 | switch conn.Type { 29 | case CT_UNDIRECTED: 30 | if !gr.CheckEdge(conn.Tail, conn.Head) { 31 | return false 32 | } 33 | case CT_DIRECTED: 34 | if !gr.CheckArc(conn.Tail, conn.Head) { 35 | return false 36 | } 37 | default: 38 | err := erx.NewError("Internal error: unknown connection type") 39 | panic(err) 40 | } 41 | } 42 | return true 43 | } 44 | 45 | // Check if graph gr include all nodes from nodesToCheck 46 | // 47 | // Warning!!! Due to channels issue 296: http://code.google.com/p/go/issues/detail?id=296 48 | // goroutine will block if not all nodes exists in graph 49 | func GraphIncludeVertexes(gr VertexesChecker, nodesToCheck VertexesIterable) bool { 50 | for node := range nodesToCheck.VertexesIter() { 51 | if !gr.CheckNode(node) { 52 | return false 53 | } 54 | } 55 | return true 56 | } 57 | 58 | // Check if graph gr include all edges from edgesToCheck 59 | // 60 | // Warning!!! Due to channels issue 296: http://code.google.com/p/go/issues/detail?id=296 61 | // goroutine will block if function result is false 62 | func GraphIncludeEdges(gr UndirectedGraphReader, edgesToCheck EdgesIterable) bool { 63 | for conn := range edgesToCheck.EdgesIter() { 64 | if !gr.CheckEdge(conn.Tail, conn.Head) { 65 | return false 66 | } 67 | } 68 | return true 69 | } 70 | 71 | // Check if graph gr include all arcs from edgesToCheck 72 | // 73 | // Warning!!! Due to channels issue 296: http://code.google.com/p/go/issues/detail?id=296 74 | // goroutine will block if function result is false 75 | func GraphIncludeArcs(gr DirectedGraphReader, arcsToCheck ArcsIterable) bool { 76 | for conn := range arcsToCheck.ArcsIter() { 77 | if !gr.CheckArc(conn.Tail, conn.Head) { 78 | return false 79 | } 80 | } 81 | return true 82 | } 83 | 84 | // Check if graph gr include all arcs and all nodes from gr2 85 | // 86 | // Warning!!! Due to channels issue 296: http://code.google.com/p/go/issues/detail?id=296 87 | // goroutine will block if function result is false 88 | func DirectedGraphInclude(gr1, gr2 DirectedGraphReader) bool { 89 | if !GraphIncludeVertexes(gr1, gr2) { 90 | return false 91 | } 92 | 93 | if !GraphIncludeArcs(gr1, gr2) { 94 | return false 95 | } 96 | 97 | return true 98 | } 99 | 100 | // Check if graph gr include all edges and all nodes from gr2 101 | // 102 | // Warning!!! Due to channels issue 296: http://code.google.com/p/go/issues/detail?id=296 103 | // goroutine will block if function result is false 104 | func UndirectedGraphInclude(gr1, gr2 DirectedGraphReader) bool { 105 | if !GraphIncludeVertexes(gr1, gr2) { 106 | return false 107 | } 108 | 109 | if !GraphIncludeArcs(gr1, gr2) { 110 | return false 111 | } 112 | 113 | return true 114 | } 115 | 116 | // Check if two directed grahps are equal 117 | // 118 | // Warning!!! Due to channels issue 296: http://code.google.com/p/go/issues/detail?id=296 119 | // goroutine will block if function result is false 120 | func DirectedGraphsEquals(gr1, gr2 DirectedGraphReader) bool { 121 | if !GraphIncludeVertexes(gr1, gr2) || !GraphIncludeVertexes(gr2, gr1) { 122 | return false 123 | } 124 | 125 | if !GraphIncludeArcs(gr1, gr2) || !GraphIncludeArcs(gr2, gr1) { 126 | return false 127 | } 128 | 129 | return true 130 | } 131 | 132 | // Check if two undirected grahps are equal 133 | // 134 | // Warning!!! Due to channels issue 296: http://code.google.com/p/go/issues/detail?id=296 135 | // goroutine will block if function result is false 136 | func UndirectedGraphsEquals(gr1, gr2 UndirectedGraphReader) bool { 137 | if !GraphIncludeVertexes(gr1, gr2) || !GraphIncludeVertexes(gr2, gr1) { 138 | return false 139 | } 140 | 141 | if !GraphIncludeEdges(gr1, gr2) || !GraphIncludeEdges(gr2, gr1) { 142 | return false 143 | } 144 | 145 | return true 146 | } 147 | 148 | // Interface for ContainPath function. 149 | type NodeAndConnectionChecker interface { 150 | // Check if node exist in graph. 151 | CheckNode(VertexId) bool 152 | // Check if there is connection between from and to nodes in graph. 153 | CheckConnection(from, to VertexId) bool 154 | } 155 | 156 | // Generic function to check if graph contain specific path. 157 | // 158 | // First argument gr is an interface with two functions to check node existance and 159 | // connection existance between two nodes in graph. 160 | // 161 | // unexistNodePanic flag is used to point wether or not to panic if we figure out that 162 | // one of the nodes in path doesn't exist in graph. If unexistNodePanic is false, then 163 | // result of the function will be false. 164 | func ContainPath(gr NodeAndConnectionChecker, path []VertexId, unexistNodePanic bool) bool { 165 | defer func() { 166 | if e:=recover(); e!=nil { 167 | err := erx.NewSequent("Checking if graph contain path", e) 168 | err.AddV("path", path) 169 | err.AddV("panic if node doesn't exist in graph", unexistNodePanic) 170 | panic(err) 171 | } 172 | }() 173 | if len(path)==0 { 174 | // emty path always exists 175 | return true 176 | } 177 | 178 | prev := path[0] 179 | if !gr.CheckNode(prev) { 180 | if unexistNodePanic { 181 | err := erx.NewError("Node doesn't exist in graph.") 182 | err.AddV("node", prev) 183 | panic(err) 184 | } 185 | return false 186 | } 187 | 188 | if len(path)==1 { 189 | return true 190 | } 191 | 192 | for i:=1; ifilter.edges[i].Head { 134 | filter.edges[i].Tail, filter.edges[i].Head = filter.edges[i].Head, filter.edges[i].Tail 135 | } 136 | } 137 | return filter 138 | } 139 | 140 | // Create arcs filter with single arc 141 | func NewUndirectedGraphEdgeFilter(g UndirectedGraphEdgesReader, tail, head VertexId) *UndirectedGraphEdgesFilter { 142 | filter := &UndirectedGraphEdgesFilter{ 143 | UndirectedGraphEdgesReader: g, 144 | edges: make([]Connection, 1), 145 | } 146 | 147 | // tail must be not greater than head 148 | if tail") 122 | } 123 | 124 | func ReadMgraphLine(gr MixedGraphWriter, line string) { 125 | defer func() { 126 | if e:=recover(); e!=nil { 127 | err := erx.NewSequent("Parsing graph arcs and edges from line.", e) 128 | err.AddV("line", line) 129 | panic(err) 130 | } 131 | }() 132 | 133 | line = strings.Trim(line, " \t\n") 134 | if commentPos := strings.Index(line, "#"); commentPos!=-1 { 135 | // truncate comments 136 | line = strings.Trim(line[0:commentPos], " \t\n") 137 | } 138 | 139 | if line=="" { 140 | // skip empty lines 141 | return 142 | } 143 | 144 | if isMatch, err := regexp.MatchString("^[0-9]+$", line); isMatch && err==nil { 145 | // only one number - it's vertex id 146 | vertexId, err := strconv.Atoi(line) 147 | if err!=nil { 148 | panic(err) 149 | } 150 | 151 | gr.AddNode(VertexId(vertexId)) 152 | } else if err!=nil { 153 | panic(err) 154 | } 155 | 156 | var prevVertexId VertexId 157 | hasPrev := false 158 | for _, nodeAsStr := range strings.Split(line, "-", -1) { 159 | nodeAsStr = strings.Trim(nodeAsStr, " \t\n") 160 | 161 | if strings.Index(nodeAsStr, ">")!=-1 { 162 | for index, nodeAsStr1 := range strings.Split(nodeAsStr, ">", -1) { 163 | nodeAsStr1 = strings.Trim(nodeAsStr1, " \t\n") 164 | nodeAsInt, err := strconv.Atoi(nodeAsStr1) 165 | if err!=nil { 166 | errErx := erx.NewSequent("Can't parse node id.", err) 167 | errErx.AddV("chunk", nodeAsStr1) 168 | panic(errErx) 169 | } 170 | 171 | VertexId := VertexId(nodeAsInt) 172 | if hasPrev { 173 | if index!=0 { 174 | gr.AddArc(prevVertexId, VertexId) 175 | } else { 176 | gr.AddEdge(prevVertexId, VertexId) 177 | } 178 | } else { 179 | hasPrev = true 180 | } 181 | prevVertexId = VertexId 182 | } 183 | } else { 184 | nodeAsInt, err := strconv.Atoi(nodeAsStr) 185 | if err!=nil { 186 | errErx := erx.NewSequent("Can't parse node id.", err) 187 | errErx.AddV("chunk", nodeAsStr) 188 | panic(errErx) 189 | } 190 | 191 | VertexId := VertexId(nodeAsInt) 192 | if hasPrev { 193 | gr.AddEdge(prevVertexId, VertexId) 194 | } else { 195 | hasPrev = true 196 | } 197 | prevVertexId = VertexId 198 | } 199 | } 200 | 201 | } 202 | 203 | func readGraphFile(f io.Reader, lineParser func(string)) { 204 | reader := bufio.NewReader(f) 205 | var err os.Error 206 | var line string 207 | line, err = reader.ReadString('\n'); 208 | for err==nil || err==os.EOF { 209 | lineParser(line) 210 | if err==os.EOF { 211 | break 212 | } 213 | line, err = reader.ReadString('\n'); 214 | } 215 | if err!=nil && err!=os.EOF { 216 | erxErr := erx.NewSequent("Error while reading file.", err) 217 | panic(erxErr) 218 | } 219 | } 220 | 221 | func ReadUgraphFile(f io.Reader, gr UndirectedGraphWriter) { 222 | readGraphFile(f, func(line string) { 223 | ReadUgraphLine(gr, line) 224 | }) 225 | } 226 | 227 | func ReadDgraphFile(f io.Reader, gr DirectedGraphWriter) { 228 | readGraphFile(f, func(line string) { 229 | ReadDgraphLine(gr, line) 230 | }) 231 | } 232 | 233 | func ReadMgraphFile(f io.Reader, gr MixedGraphWriter) { 234 | readGraphFile(f, func(line string) { 235 | ReadMgraphLine(gr, line) 236 | }) 237 | } 238 | -------------------------------------------------------------------------------- /src/graph/iterators.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | . "exp/iterable" 5 | 6 | "github.com/StepLg/go-erx/src/erx" 7 | ) 8 | 9 | type connectionsIterableHelper struct { 10 | connIter ConnectionsIterable 11 | } 12 | 13 | func (helper *connectionsIterableHelper) Iter() <-chan interface{} { 14 | ch := make(chan interface{}) 15 | go func() { 16 | for arr := range helper.connIter.ConnectionsIter() { 17 | ch <- arr 18 | } 19 | close(ch) 20 | }() 21 | return ch 22 | } 23 | 24 | type connectionsGenericIterableHelper struct { 25 | iter Iterable 26 | } 27 | 28 | func (helper *connectionsGenericIterableHelper) ConnectionsIter() <-chan Connection { 29 | ch := make(chan Connection) 30 | go func() { 31 | for arr := range helper.iter.Iter() { 32 | ch <- arr.(Connection) 33 | } 34 | close(ch) 35 | }() 36 | return ch 37 | } 38 | 39 | // Transform connections iterable to generic iterable object. 40 | func ConnectionsToGenericIter(connIter ConnectionsIterable) Iterable { 41 | return Iterable(&connectionsIterableHelper{connIter:connIter}) 42 | } 43 | 44 | // Transform generic iterable to connections iterable object. 45 | func GenericToConnectionsIter(iter Iterable) ConnectionsIterable { 46 | return ConnectionsIterable(&connectionsGenericIterableHelper{iter:iter}) 47 | } 48 | 49 | type nodesIterableHelper struct { 50 | nodesIter VertexesIterable 51 | } 52 | 53 | func (helper *nodesIterableHelper) Iter() <-chan interface{} { 54 | ch := make(chan interface{}) 55 | go func() { 56 | for node := range helper.nodesIter.VertexesIter() { 57 | ch <- node 58 | } 59 | close(ch) 60 | }() 61 | return ch 62 | } 63 | 64 | type nodesGenericIterableHelper struct { 65 | iter Iterable 66 | } 67 | 68 | func (helper *nodesGenericIterableHelper) VertexesIter() <-chan VertexId { 69 | ch := make(chan VertexId) 70 | go func() { 71 | for node := range helper.iter.Iter() { 72 | ch <- node.(VertexId) 73 | } 74 | close(ch) 75 | }() 76 | return ch 77 | } 78 | 79 | // Transform vertexes iterable to generic iterable object. 80 | func VertexesToGenericIter(nodesIter VertexesIterable) Iterable { 81 | return Iterable(&nodesIterableHelper{nodesIter:nodesIter}) 82 | } 83 | 84 | // Transform generic iterator to vertexes iterable 85 | func GenericToVertexesIter(iter Iterable) VertexesIterable { 86 | return VertexesIterable(&nodesGenericIterableHelper{iter:iter}) 87 | } 88 | 89 | // Collect all vertexes from iterator to slice. 90 | func CollectVertexes(iter VertexesIterable) []VertexId { 91 | res := make([]VertexId, 10) 92 | i := 0 93 | for node := range iter.VertexesIter() { 94 | if i==len(res) { 95 | tmp := make([]VertexId, 2*i) 96 | copy(tmp, res) 97 | res = tmp 98 | } 99 | res[i] = node 100 | i++ 101 | } 102 | 103 | return res[0:i] 104 | } 105 | 106 | // Build directed graph from connecection iterator with order function 107 | // 108 | // For all connections from iterator check isCorrectOrder function 109 | // and add to directed graph connection in correct order 110 | func BuildDirectedGraph(gr DirectedGraph, connIterable ConnectionsIterable , isCorrectOrder func(Connection) bool) { 111 | for arr := range connIterable.ConnectionsIter() { 112 | if isCorrectOrder(arr) { 113 | gr.AddArc(arr.Tail, arr.Head) 114 | } else { 115 | gr.AddArc(arr.Head, arr.Tail) 116 | } 117 | } 118 | } 119 | 120 | // Copy all arcs from iterator to directed graph 121 | // 122 | // todo: merge with CopyUndirectedGraph 123 | func CopyDirectedGraph(connIter ConnectionsIterable, gr DirectedGraphArcsWriter) { 124 | // wheel := erx.NewError("Can't copy directed graph") 125 | for arrow := range connIter.ConnectionsIter() { 126 | gr.AddArc(arrow.Tail, arrow.Head) 127 | } 128 | return 129 | } 130 | 131 | // Copy all arcs from iterator to directed graph 132 | // 133 | // todo: add VertexesIterable interface and copy all nodes before copying arcs 134 | func CopyUndirectedGraph(connIter ConnectionsIterable, gr UndirectedGraphEdgesWriter) { 135 | // wheel := erx.NewError("Can't copy directed graph") 136 | for arrow := range connIter.ConnectionsIter() { 137 | gr.AddEdge(arrow.Tail, arrow.Head) 138 | } 139 | return 140 | } 141 | 142 | // Copy all connections from iterator to mixed graph 143 | // 144 | // todo: merge with CopyDirectedGraph 145 | func CopyMixedGraph(from TypedConnectionsIterable, to MixedGraphWriter) { 146 | for conn := range from.TypedConnectionsIter() { 147 | switch conn.Type { 148 | case CT_UNDIRECTED: 149 | to.AddEdge(conn.Tail, conn.Head) 150 | case CT_DIRECTED: 151 | to.AddArc(conn.Tail, conn.Head) 152 | default: 153 | err := erx.NewError("Internal error: unknown connection type") 154 | panic(err) 155 | } 156 | } 157 | } 158 | 159 | // helper struct for ArcsToConnIterable 160 | type arcsToConnIterable_helper struct { 161 | gr DirectedGraphArcsReader 162 | } 163 | 164 | func (helper *arcsToConnIterable_helper) ConnectionsIter() <-chan Connection { 165 | return helper.gr.ArcsIter() 166 | } 167 | 168 | // Convert arcs iterator to connections iterator. 169 | func ArcsToConnIterable(gr DirectedGraphArcsReader) ConnectionsIterable { 170 | return &arcsToConnIterable_helper{gr} 171 | } 172 | 173 | // helper struct for EdgesToConnIterable 174 | type edgesToConnIterable_helper struct { 175 | gr UndirectedGraphEdgesReader 176 | } 177 | 178 | func (helper *edgesToConnIterable_helper) ConnectionsIter() <-chan Connection { 179 | return helper.gr.EdgesIter() 180 | } 181 | 182 | // Convert edges iterator to connections iterator. 183 | func EdgesToConnIterable(gr UndirectedGraphEdgesReader) ConnectionsIterable { 184 | return &edgesToConnIterable_helper{gr} 185 | } 186 | 187 | // helper struct for ArcsToTypedConnIterable 188 | type arcsToTypedConnIterable_helper struct { 189 | gr DirectedGraphArcsReader 190 | } 191 | 192 | func (helper *arcsToTypedConnIterable_helper) TypedConnectionsIter() <-chan TypedConnection { 193 | ch := make(chan TypedConnection) 194 | go func() { 195 | for conn := range helper.gr.ArcsIter() { 196 | ch <- TypedConnection{Connection: conn, Type: CT_DIRECTED} 197 | } 198 | close(ch) 199 | }() 200 | return ch 201 | } 202 | 203 | // Convert arcs iterator to typed connections iterator. 204 | func ArcsToTypedConnIterable(gr DirectedGraphArcsReader) TypedConnectionsIterable { 205 | return &arcsToTypedConnIterable_helper{gr} 206 | } 207 | 208 | // helper struct for EdgesToTypedConnIterable 209 | type edgesToTypedConnIterable_helper struct { 210 | gr UndirectedGraphEdgesReader 211 | } 212 | 213 | func (helper *edgesToTypedConnIterable_helper) TypedConnectionsIter() <-chan TypedConnection { 214 | ch := make(chan TypedConnection) 215 | go func() { 216 | for conn := range helper.gr.EdgesIter() { 217 | ch <- TypedConnection{Connection: conn, Type: CT_UNDIRECTED} 218 | } 219 | close(ch) 220 | }() 221 | return ch 222 | } 223 | 224 | // Convert edges iterator to typed connections iterator. 225 | func EdgesToTypedConnIterable(gr UndirectedGraphEdgesReader) TypedConnectionsIterable { 226 | return &edgesToTypedConnIterable_helper{gr} 227 | } 228 | 229 | 230 | // Vertexes iterable object for function, which returns VertexId channel. 231 | // 232 | // Internal use only at this moment. Don't know what for it could be used 233 | // outside. 234 | type nodesIterableLambdaHelper struct { 235 | iterFunc func() <-chan VertexId 236 | } 237 | 238 | func (helper *nodesIterableLambdaHelper) VertexesIter() <-chan VertexId { 239 | return helper.iterFunc() 240 | } 241 | -------------------------------------------------------------------------------- /src/graph/iterators_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | "github.com/orfjackal/gospec/src/gospec" 8 | . "github.com/orfjackal/gospec/src/gospec" 9 | ) 10 | 11 | func DirectedGraphEquals(actual interface{}, expected interface{}) (match bool, pos os.Error, neg os.Error, err os.Error) { 12 | match = false 13 | neg = Errorf("Didn't expect that directed graphs are equal.") 14 | if aGr, ok := actual.(DirectedGraph); ok { 15 | if eGr, ok1 := expected.(DirectedGraph); ok1 { 16 | match = true 17 | missed := "" 18 | for arrow := range eGr.ConnectionsIter() { 19 | isExist := aGr.CheckArc(arrow.Tail, arrow.Head) 20 | if !isExist { 21 | match = false 22 | if missed != "" { 23 | missed += ", " 24 | } 25 | missed += string(arrow.Tail) + "->" + string(arrow.Head) 26 | } 27 | } 28 | 29 | phantom := "" 30 | for arrow := range aGr.ConnectionsIter() { 31 | isExist := eGr.CheckArc(arrow.Tail, arrow.Head) 32 | if !isExist { 33 | match = false 34 | if phantom!="" { 35 | phantom += ", " 36 | } 37 | phantom += fmt.Sprintf("%v->%v", arrow.Tail, arrow.Head) 38 | } 39 | } 40 | 41 | errorText := "Actual graph" 42 | if missed!="" { 43 | errorText += " miss " + missed + " arrows" 44 | } 45 | if missed!="" && phantom!="" { 46 | errorText += " and" 47 | } 48 | if phantom!="" { 49 | errorText += " contain " + phantom + " phantom arrows" 50 | } 51 | errorText += "." 52 | pos = Errorf(errorText) 53 | } else { 54 | err = Errorf("Expected DirectedGraph in actual, but was '%v' of type '%T'", expected, expected) 55 | } 56 | } else { 57 | err = Errorf("Expected DirectedGraph in actual, but was '%v' of type '%T'", actual, actual) 58 | } 59 | 60 | return 61 | } 62 | 63 | func ArrowsIteratorSpec(c gospec.Context) { 64 | gr := DirectedGraph(NewDirectedMap()) 65 | 66 | c.Specify("Copy empty graph", func() { 67 | gr1 := NewDirectedMap() 68 | CopyDirectedGraph(gr, gr1) 69 | c.Expect(gr1, DirectedGraphEquals, gr) 70 | }) 71 | 72 | c.Specify("Copy simple directed graph", func() { 73 | gr1 := NewDirectedMap() 74 | gr.AddArc(1, 2) 75 | gr.AddArc(2, 3) 76 | gr.AddArc(1, 4) 77 | gr.AddArc(5, 1) 78 | 79 | CopyDirectedGraph(gr, gr1) 80 | c.Expect(gr1, DirectedGraphEquals, gr) 81 | }) 82 | } 83 | 84 | func TestArrowsIteratorSpec(t *testing.T) { 85 | r := gospec.NewRunner() 86 | r.AddSpec(ArrowsIteratorSpec) 87 | gospec.MainGoTest(r, t) 88 | } 89 | -------------------------------------------------------------------------------- /src/graph/neighbours_extractor.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | . "exp/iterable" 5 | ) 6 | 7 | // Extract all vertexes, which are accessible from given node. 8 | type OutNeighboursExtractor interface { 9 | GetOutNeighbours(node VertexId) VertexesIterable 10 | } 11 | 12 | type dgraphOutNeighboursExtractor struct { 13 | dgraph DirectedGraphArcsReader 14 | } 15 | 16 | func (e *dgraphOutNeighboursExtractor) GetOutNeighbours(node VertexId) VertexesIterable { 17 | return e.dgraph.GetAccessors(node) 18 | } 19 | 20 | // Extract all vertexes, accessible from given node in directed graph. 21 | // 22 | // In fact, interface function is a synonym to DirectedGraphArcsReader.GetAccessors() function 23 | func NewDgraphOutNeighboursExtractor(gr DirectedGraphArcsReader) OutNeighboursExtractor { 24 | return OutNeighboursExtractor(&dgraphOutNeighboursExtractor{dgraph:gr}) 25 | } 26 | 27 | type ugraphOutNeighboursExtractor struct { 28 | ugraph UndirectedGraphEdgesReader 29 | } 30 | 31 | func (e *ugraphOutNeighboursExtractor) GetOutNeighbours(node VertexId) VertexesIterable { 32 | return e.ugraph.GetNeighbours(node) 33 | } 34 | 35 | // Extract all vertexes, accessible from given node in undirected graph. 36 | // 37 | // In fact, interface function is a synonym to UndirectedGraphEdgesReader.GetNeighbours() function 38 | func NewUgraphOutNeighboursExtractor(gr UndirectedGraphEdgesReader) OutNeighboursExtractor { 39 | return OutNeighboursExtractor(&ugraphOutNeighboursExtractor{ugraph:gr}) 40 | } 41 | 42 | type mgraphOutNeighboursExtractor struct { 43 | mgraph MixedGraphConnectionsReader 44 | } 45 | 46 | func (e *mgraphOutNeighboursExtractor) GetOutNeighbours(node VertexId) VertexesIterable { 47 | return GenericToVertexesIter(Chain(&[...]Iterable{ 48 | VertexesToGenericIter(e.mgraph.GetAccessors(node)), 49 | VertexesToGenericIter(e.mgraph.GetNeighbours(node)), 50 | })) 51 | } 52 | 53 | // Extract all vertexes, accessible from given node in mixed graph. 54 | // 55 | // In fact, interface function is chain of MixedGraphConnectionsReader.GetAccessors() 56 | // and MixedGraphConnectionsReader.GetNeighbours() functions. 57 | func NewMgraphOutNeighboursExtractor(gr MixedGraphConnectionsReader) OutNeighboursExtractor { 58 | return OutNeighboursExtractor(&mgraphOutNeighboursExtractor{mgraph:gr}) 59 | } 60 | 61 | //////////////////////////////////////////////////////////////////////////////// 62 | 63 | 64 | // Extract all vertexes, from which accessibe given node. 65 | type InNeighboursExtractor interface { 66 | GetInNeighbours(node VertexId) VertexesIterable 67 | } 68 | 69 | type dgraphInNeighboursExtractor struct { 70 | dgraph DirectedGraphArcsReader 71 | } 72 | 73 | // Extract all vertexes, from which accessible given node in directed graph. 74 | // 75 | // In fact, interface function is a synonym to DirectedGraphArcsReader.GetPredecessors() function 76 | func (e *dgraphInNeighboursExtractor) GetInNeighbours(node VertexId) VertexesIterable { 77 | return e.dgraph.GetPredecessors(node) 78 | } 79 | 80 | func NewDgraphInNeighboursExtractor(gr DirectedGraphArcsReader) InNeighboursExtractor { 81 | return InNeighboursExtractor(&dgraphInNeighboursExtractor{dgraph:gr}) 82 | } 83 | 84 | type ugraphInNeighboursExtractor struct { 85 | ugraph UndirectedGraphEdgesReader 86 | } 87 | 88 | func (e *ugraphInNeighboursExtractor) GetInNeighbours(node VertexId) VertexesIterable { 89 | return e.ugraph.GetNeighbours(node) 90 | } 91 | 92 | // Extract all vertexes, from which accessible given node in undirected graph. 93 | // 94 | // In fact, interface function is a synonym to UndirectedGraphEdgesReader.GetNeighbours() function 95 | func NewUgraphInNeighboursExtractor(gr UndirectedGraphEdgesReader) InNeighboursExtractor { 96 | return InNeighboursExtractor(&ugraphInNeighboursExtractor{ugraph:gr}) 97 | } 98 | 99 | type mgraphInNeighboursExtractor struct { 100 | mgraph MixedGraphConnectionsReader 101 | } 102 | 103 | func (e *mgraphInNeighboursExtractor) GetInNeighbours(node VertexId) VertexesIterable { 104 | return GenericToVertexesIter(Chain(&[...]Iterable{ 105 | VertexesToGenericIter(e.mgraph.GetPredecessors(node)), 106 | VertexesToGenericIter(e.mgraph.GetNeighbours(node)), 107 | })) 108 | } 109 | 110 | // Extract all vertexes, accessible from given node in mixed graph. 111 | // 112 | // In fact, interface function is chain of MixedGraphConnectionsReader.GetPredecessors() 113 | // and MixedGraphConnectionsReader.GetNeighbours() functions. 114 | func NewMgraphInNeighboursExtractor(gr MixedGraphConnectionsReader) InNeighboursExtractor { 115 | return InNeighboursExtractor(&mgraphInNeighboursExtractor{mgraph:gr}) 116 | } 117 | -------------------------------------------------------------------------------- /src/graph/output.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | "strconv" 8 | ) 9 | 10 | func (node VertexId) String() string { 11 | return strconv.Itoa(int(uint(node))) 12 | } 13 | 14 | func (conn Connection) String() string { 15 | return fmt.Sprintf("%v->%v", conn.Tail, conn.Head) 16 | } 17 | 18 | // Typed connection as a string. 19 | // 20 | // Format: 21 | // * 1--2 for undirected connection 22 | // * 1->2 for directed connection 23 | // * 1<-2 for reversed directed connection 24 | // * 1><2 if there is no connection between 1 and 2 25 | // * 1!!2 for unexpected error (if function can't recognize connection type) 26 | func (conn TypedConnection) String() string { 27 | switch conn.Type { 28 | case CT_UNDIRECTED: 29 | return fmt.Sprintf("%v--%v", conn.Tail, conn.Head) 30 | case CT_DIRECTED: 31 | return fmt.Sprintf("%v->%v", conn.Tail, conn.Head) 32 | case CT_DIRECTED_REVERSED: 33 | return fmt.Sprintf("%v<-%v", conn.Tail, conn.Head) 34 | case CT_NONE: 35 | return fmt.Sprintf("%v><%v", conn.Tail, conn.Head) 36 | } 37 | return fmt.Sprintf("%v!!%v", conn.Tail, conn.Head) 38 | } 39 | 40 | func styleMapToString(style map[string]string) string { 41 | chunks := make([]string, len(style)) 42 | i := 0 43 | for k, v := range style { 44 | chunks[i] = fmt.Sprintf("%v=\"%v\"", k, v) 45 | i++ 46 | } 47 | return "[" + strings.Join(chunks, ",") + "]" 48 | } 49 | 50 | type DotNodeStyleFunc func(node VertexId) map[string]string 51 | type DotConnectionStyleFunc func(conn TypedConnection) map[string]string 52 | 53 | // Basic dot style function for vertexes. 54 | // 55 | // Generates only one style property: vertex label, which match vertex id. 56 | func SimpleNodeStyle(node VertexId) map[string]string { 57 | style := make(map[string]string) 58 | style["label"] = node.String() 59 | return style 60 | } 61 | 62 | // Basic dot style function for connections. 63 | // 64 | // Empty style, except undirected connection. For undirected connection 65 | // "dir=both" style is set. 66 | func SimpleConnectionStyle(conn TypedConnection) map[string]string { 67 | res := make(map[string]string) 68 | if conn.Type==CT_UNDIRECTED { 69 | // for undirected connection by default set "both" direction 70 | res["dir"] = "both" 71 | } 72 | return res 73 | } 74 | 75 | // Plot graph vertexes to dot format. 76 | // 77 | // nodesIter -- iterable object over graph vertexes 78 | // wr -- writer interface 79 | // styleFunc -- style function for vertexes. For example, see SimpleNodeStyle() 80 | func PlotVertexesToDot(nodesIter VertexesIterable, wr io.Writer, styleFunc DotNodeStyleFunc) { 81 | if styleFunc==nil { 82 | styleFunc = SimpleNodeStyle 83 | } 84 | for node := range nodesIter.VertexesIter() { 85 | wr.Write([]byte("n" + node.String() + styleMapToString(styleFunc(node)) + ";\n")) 86 | } 87 | } 88 | 89 | // Plot graph connections to dot format. 90 | // 91 | // connIter -- iterable object over graph connections 92 | // separator -- string betweet two vertexes. "->" for digraph and "--" for graph in graphviz file format 93 | // wr -- writer interface 94 | // styleFunc -- style function for typed connections. For example, see SimpleConnectionStyle() 95 | func PlotConnectionsToDot(connIter TypedConnectionsIterable, separator string, wr io.Writer, styleFunc DotConnectionStyleFunc) { 96 | if styleFunc==nil { 97 | styleFunc = SimpleConnectionStyle 98 | } 99 | for conn := range connIter.TypedConnectionsIter() { 100 | wr.Write([]byte(fmt.Sprintf("n%v" + separator + "n%v%v;\n", 101 | conn.Tail.String(), 102 | conn.Head.String(), 103 | styleMapToString(styleFunc(conn))))) 104 | } 105 | } 106 | 107 | // Plot directed graph to dot format. 108 | // 109 | // gr -- directed graph interface 110 | // wr -- writer interface 111 | // nodeStyleFunc -- style function for vertexes. For example, see SimpleNodeStyle() 112 | // connStyleFunc -- style function for typed connections. For example, see SimpleConnectionStyle() 113 | func PlotDgraphToDot(gr DirectedGraphReader, wr io.Writer, nodeStyleFunc DotNodeStyleFunc, connStyleFunc DotConnectionStyleFunc) { 114 | wr.Write([]byte("digraph messages {\n")) 115 | PlotVertexesToDot(gr, wr, nodeStyleFunc) 116 | PlotConnectionsToDot(ArcsToTypedConnIterable(gr), "->", wr, connStyleFunc) 117 | wr.Write([]byte("}\n")) 118 | } 119 | 120 | // Plot mixed graph to dot format. 121 | // 122 | // gr -- directed graph interface 123 | // wr -- writer interface 124 | // nodeStyleFunc -- style function for vertexes. For example, see SimpleNodeStyle() 125 | // connStyleFunc -- style function for typed connections. For example, see SimpleConnectionStyle() 126 | func PlotMgraphToDot(gr MixedGraphReader, wr io.Writer, nodeStyleFunc DotNodeStyleFunc, connStyleFunc DotConnectionStyleFunc) { 127 | wr.Write([]byte("digraph messages {\n")) 128 | PlotVertexesToDot(gr, wr, nodeStyleFunc) 129 | PlotConnectionsToDot(gr, "->", wr, connStyleFunc) 130 | wr.Write([]byte("}\n")) 131 | } 132 | 133 | // Plot undirected graph to dot format. 134 | // 135 | // gr -- directed graph interface 136 | // wr -- writer interface 137 | // nodeStyleFunc -- style function for vertexes. For example, see SimpleNodeStyle() 138 | // connStyleFunc -- style function for typed connections. For example, see SimpleConnectionStyle() 139 | func PlotUgraphToDot(gr UndirectedGraphReader, wr io.Writer, nodeStyleFunc DotNodeStyleFunc, connStyleFunc DotConnectionStyleFunc) { 140 | wr.Write([]byte("graph messages {\n")) 141 | PlotVertexesToDot(gr, wr, nodeStyleFunc) 142 | PlotConnectionsToDot(EdgesToTypedConnIterable(gr), "--", wr, connStyleFunc) 143 | wr.Write([]byte("}\n")) 144 | } 145 | -------------------------------------------------------------------------------- /src/graph/output_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | /** 4 | import ( 5 | "testing" 6 | "fmt" 7 | 8 | "github.com/orfjackal/gospec/src/gospec" 9 | // . "github.com/orfjackal/gospec/src/gospec" 10 | ) 11 | 12 | func PlotDirectedGraphToDotSpec(c gospec.Context) { 13 | gr := NewDirectedMap() 14 | gr.AddArc(1, 2) 15 | gr.AddArc(2, 3) 16 | gr.AddArc(3, 4) 17 | gr.AddArc(2, 4) 18 | gr.AddArc(4, 5) 19 | gr.AddArc(1, 6) 20 | gr.AddArc(2, 6) 21 | 22 | writer := &StringWriter{} 23 | PlotDirectedGraphToDot(gr, writer, SimpleNodeStyle, SimpleArcStyle) 24 | fmt.Println(writer.Str) 25 | } 26 | 27 | func TestOutput(t *testing.T) { 28 | r := gospec.NewRunner() 29 | r.AddSpec(PlotDirectedGraphToDotSpec) 30 | gospec.MainGoTest(r, t) 31 | } 32 | */ 33 | -------------------------------------------------------------------------------- /src/graph/search.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/StepLg/go-erx/src/erx" 7 | ) 8 | 9 | // Path mark, set by some of search algorithms. 10 | type VertexPathMark struct { 11 | Weight float64 // Weight from one of source nodes to current node. 12 | PrevVertex VertexId // Previous node in path. 13 | } 14 | 15 | // Path weight from one of sources to node in map. 16 | // 17 | // Used as a result by some of minimal path search algorithms. 18 | // To get real path from this marks map use PathFromMarks function. 19 | type PathMarks map[VertexId]*VertexPathMark 20 | 21 | type ConnectionWeightFunc func(head, tail VertexId) float64 22 | 23 | type StopFunc func(node VertexId, sumWeight float64) bool 24 | 25 | func SimpleWeightFunc(head, tail VertexId) float64 { 26 | return float64(1.0) 27 | } 28 | 29 | // Generic check path algorithm for all graph types 30 | // 31 | // Checking path between from and to nodes, using getNeighbours function 32 | // to figure out connected nodes on each step of algorithm. 33 | // 34 | // stopFunc is used to cut bad paths using user-defined criteria 35 | // 36 | // weightFunction calculates total path weight 37 | // 38 | // As a result CheckPathDijkstra returns total weight of path, if it exists. 39 | func CheckPathDijkstra(neighboursExtractor OutNeighboursExtractor, from, to VertexId, stopFunc StopFunc, weightFunction ConnectionWeightFunc) (float64, bool) { 40 | defer func() { 41 | if e:=recover(); e!=nil { 42 | err := erx.NewSequent("Check path graph with Dijkstra algorithm", e) 43 | err.AddV("from", from) 44 | err.AddV("to", to) 45 | panic(err) 46 | } 47 | }() 48 | 49 | if from==to { 50 | return 0.0, true 51 | } 52 | 53 | q := newPriorityQueueSimple(10) 54 | q.Add(from, 0.0) 55 | 56 | for !q.Empty() { 57 | curNode, curWeight := q.Next() 58 | curWeight = -curWeight // because we inverse weight in priority queue 59 | 60 | for nextNode := range neighboursExtractor.GetOutNeighbours(curNode).VertexesIter() { 61 | arcWeight := weightFunction(curNode, nextNode) 62 | if arcWeight < 0 { 63 | err := erx.NewError("Negative weight detected") 64 | err.AddV("head", curNode) 65 | err.AddV("tail", nextNode) 66 | err.AddV("weight", arcWeight) 67 | panic(err) 68 | } 69 | nextWeight := curWeight + arcWeight 70 | if nextNode==to { 71 | return nextWeight, true 72 | } 73 | if stopFunc==nil || !stopFunc(nextNode, nextWeight) { 74 | q.Add(nextNode, -nextWeight) 75 | } 76 | } 77 | } 78 | 79 | return -1.0, false 80 | } 81 | 82 | type CheckDirectedPath func(gr DirectedGraphArcsReader, from, to VertexId, stopFunc StopFunc, weightFunction ConnectionWeightFunc) bool 83 | 84 | func CheckDirectedPathDijkstra(gr DirectedGraphArcsReader, from, to VertexId, stopFunc StopFunc, weightFunction ConnectionWeightFunc) bool { 85 | _, pathExists := CheckPathDijkstra(NewDgraphOutNeighboursExtractor(gr), from, to, stopFunc, weightFunction) 86 | return pathExists 87 | } 88 | 89 | type CheckUndirectedPath func(gr UndirectedGraphEdgesReader, from, to VertexId, stopFunc StopFunc, weightFunction ConnectionWeightFunc) bool 90 | 91 | func CheckUndirectedPathDijkstra(gr UndirectedGraphEdgesReader, from, to VertexId, stopFunc StopFunc, weightFunction ConnectionWeightFunc) bool { 92 | _, pathExists := CheckPathDijkstra(NewUgraphOutNeighboursExtractor(gr), from, to, stopFunc, weightFunction) 93 | return pathExists 94 | } 95 | 96 | type CheckMixedPath func(gr MixedGraphConnectionsReader, from, to VertexId, stopFunc StopFunc, weightFunction ConnectionWeightFunc) bool 97 | 98 | func CheckMixedPathDijkstra(gr MixedGraphConnectionsReader, from, to VertexId, stopFunc StopFunc, weightFunction ConnectionWeightFunc) bool { 99 | _, pathExists := CheckPathDijkstra(NewMgraphOutNeighboursExtractor(gr), from, to, stopFunc, weightFunction) 100 | return pathExists 101 | } 102 | 103 | // Get all paths from one node to another 104 | // 105 | // This algorithms doesn't take any loops into paths. 106 | func GetAllPaths(neighboursExtractor OutNeighboursExtractor, from, to VertexId) <-chan []VertexId { 107 | curPath := make([]VertexId, 10) 108 | nodesStatus := make(map[VertexId]bool) 109 | ch := make(chan []VertexId) 110 | go getAllPaths_helper(neighboursExtractor, from, to, curPath, 0, nodesStatus, ch, true) 111 | return ch 112 | } 113 | 114 | func getAllPaths_helper(neighboursExtractor OutNeighboursExtractor, from, to VertexId, curPath []VertexId, pathPos int, nodesStatus map[VertexId]bool, ch chan []VertexId, closeChannel bool) { 115 | if _, ok := nodesStatus[from]; ok { 116 | return 117 | } 118 | if pathPos==len(curPath) { 119 | // reallocate curPath slice to add new elements 120 | tmp := make([]VertexId, 2*pathPos) 121 | copy(tmp, curPath) 122 | curPath = tmp 123 | } 124 | 125 | curPath[pathPos] = from 126 | 127 | if from==to { 128 | if pathPos>0 { 129 | pathCopy := make([]VertexId, pathPos+1) 130 | copy(pathCopy, curPath[0:pathPos+1]) 131 | ch <- pathCopy 132 | } 133 | return 134 | } 135 | nodesStatus[from] = true 136 | 137 | for nextNode := range neighboursExtractor.GetOutNeighbours(from).VertexesIter() { 138 | getAllPaths_helper(neighboursExtractor, nextNode, to, curPath, pathPos+1, nodesStatus, ch, false) 139 | } 140 | 141 | nodesStatus[from] = false, false 142 | 143 | if closeChannel { 144 | close(ch) 145 | } 146 | return 147 | } 148 | 149 | func GetAllDirectedPaths(gr DirectedGraphArcsReader, from, to VertexId) <-chan []VertexId { 150 | return GetAllPaths(NewDgraphOutNeighboursExtractor(gr), from, to) 151 | } 152 | 153 | func GetAllUndirectedPaths(gr UndirectedGraphEdgesReader, from, to VertexId) <-chan []VertexId { 154 | return GetAllPaths(NewUgraphOutNeighboursExtractor(gr), from, to) 155 | } 156 | 157 | func GetAllMixedPaths(gr MixedGraphConnectionsReader, from, to VertexId) <-chan []VertexId { 158 | return GetAllPaths(NewMgraphOutNeighboursExtractor(gr), from, to) 159 | } 160 | 161 | // Retrieving path from path marks. 162 | func PathFromMarks(marks PathMarks, destination VertexId) Vertexes { 163 | defer func() { 164 | if e:=recover(); e!=nil { 165 | err := erx.NewSequent("Retrieving path from path marks.", e) 166 | err.AddV("marks", marks) 167 | err.AddV("destination", destination) 168 | panic(err) 169 | } 170 | }() 171 | destInfo, ok := marks[destination] 172 | if !ok || destInfo.Weight==math.MaxFloat64 { 173 | // no path from any source to destination 174 | return nil 175 | } 176 | 177 | curVertexInfo := destInfo 178 | path := make(Vertexes, 10) 179 | curPathPos := 0 180 | path[curPathPos] = destination 181 | curPathPos++ 182 | for curVertexInfo.Weight > 0.0 { 183 | if len(path)==curPathPos { 184 | // reallocate memory for path 185 | tmp := make(Vertexes, 2*curPathPos) 186 | copy(tmp, path) 187 | path = tmp 188 | } 189 | path[curPathPos] = curVertexInfo.PrevVertex 190 | curPathPos++ 191 | var ok bool 192 | curVertexInfo, ok = marks[curVertexInfo.PrevVertex] 193 | if !ok { 194 | err := erx.NewError("Can't find path mark info for vertex in path.") 195 | err.AddV("vertex", curVertexInfo.PrevVertex) 196 | err.AddV("cur path", path) 197 | panic(err) 198 | } 199 | } 200 | 201 | path = path[0:curPathPos] 202 | 203 | // reversing path 204 | pathLen := len(path) 205 | for i:=0; i possibleWeight { 234 | marks[conn.Head].PrevVertex = conn.Tail 235 | marks[conn.Head].Weight = possibleWeight 236 | } 237 | } 238 | } 239 | 240 | for conn := range gr.ArcsIter() { 241 | if marks[conn.Head].Weight > marks[conn.Tail].Weight + weightFunc(conn.Tail, conn.Head) { 242 | return nil 243 | } 244 | } 245 | 246 | return marks 247 | } 248 | 249 | func BellmanFordSingleSource(gr DirectedGraphReader, source VertexId, weightFunc ConnectionWeightFunc) PathMarks { 250 | return BellmanFordMultiSource(gr, Vertexes{source}, weightFunc) 251 | } 252 | 253 | // Compute multi-source shortest paths with Bellman-Ford algorithm 254 | // 255 | // Returs map, contains all accessiable vertexes from sources vertexes with 256 | // minimal path weight. 257 | // 258 | // Returns nil if there are negative cycles. 259 | func BellmanFordLightMultiSource(gr OutNeighboursExtractor, sources Vertexes, weightFunc ConnectionWeightFunc) PathMarks { 260 | marks := make(PathMarks) 261 | for _, vertex := range sources { 262 | marks[vertex] = &VertexPathMark{Weight: 0.0, PrevVertex: 0} 263 | } 264 | 265 | for i:=0; i possibleWeight { 270 | marks[vertex] = &VertexPathMark{Weight: possibleWeight, PrevVertex: vertex} 271 | } 272 | } 273 | } 274 | } 275 | 276 | // checking for negative cycles 277 | for vertex, vertexInfo := range marks { 278 | for nextVertex := range gr.GetOutNeighbours(vertex).VertexesIter() { 279 | if marks[nextVertex].Weight > vertexInfo.Weight + weightFunc(vertex, nextVertex) { 280 | return nil 281 | } 282 | } 283 | } 284 | 285 | return marks 286 | } 287 | 288 | func BellmanFordLightSingleSource(gr OutNeighboursExtractor, source VertexId, weightFunc ConnectionWeightFunc) PathMarks { 289 | return BellmanFordLightMultiSource(gr, Vertexes{source}, weightFunc) 290 | } 291 | -------------------------------------------------------------------------------- /src/graph/search_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "testing" 5 | "github.com/orfjackal/gospec/src/gospec" 6 | . "github.com/orfjackal/gospec/src/gospec" 7 | ) 8 | 9 | func CheckDirectedPathSpec(c gospec.Context, checkPathFunction CheckDirectedPath) { 10 | gr := generateDirectedGraph1() 11 | 12 | c.Specify("Check path to self", func() { 13 | c.Expect(checkPathFunction(gr, 1, 1, nil, SimpleWeightFunc), IsTrue) 14 | c.Expect(checkPathFunction(gr, 6, 6, nil, SimpleWeightFunc), IsTrue) 15 | }) 16 | 17 | c.Specify("Check neighbours path", func() { 18 | c.Expect(checkPathFunction(gr, 1, 2, nil, SimpleWeightFunc), IsTrue) 19 | c.Expect(checkPathFunction(gr, 2, 4, nil, SimpleWeightFunc), IsTrue) 20 | c.Expect(checkPathFunction(gr, 1, 6, nil, SimpleWeightFunc), IsTrue) 21 | }) 22 | 23 | c.Specify("Check reversed neighbours", func() { 24 | c.Expect(checkPathFunction(gr, 6, 1, nil, SimpleWeightFunc), IsFalse) 25 | c.Expect(checkPathFunction(gr, 4, 3, nil, SimpleWeightFunc), IsFalse) 26 | c.Expect(checkPathFunction(gr, 5, 4, nil, SimpleWeightFunc), IsFalse) 27 | }) 28 | 29 | c.Specify("Check long path", func() { 30 | c.Expect(checkPathFunction(gr, 1, 6, nil, SimpleWeightFunc), IsTrue) 31 | c.Expect(checkPathFunction(gr, 1, 5, nil, SimpleWeightFunc), IsTrue) 32 | }) 33 | 34 | c.Specify("Check weight limit", func() { 35 | c.Expect(checkPathFunction(gr, 1, 5, func(node VertexId, weight float64) bool { 36 | return weight < 2.0 37 | }, SimpleWeightFunc), IsFalse) 38 | }) 39 | } 40 | 41 | func CheckMixedPathSpec(c gospec.Context, checkPathFunction CheckMixedPath) { 42 | gr := generateMixedGraph1() 43 | 44 | c.Specify("Check path to self", func() { 45 | c.Expect(checkPathFunction(gr, 1, 1, nil, SimpleWeightFunc), IsTrue) 46 | c.Expect(checkPathFunction(gr, 6, 6, nil, SimpleWeightFunc), IsTrue) 47 | c.Expect(checkPathFunction(gr, 4, 4, nil, SimpleWeightFunc), IsTrue) 48 | }) 49 | 50 | c.Specify("Check directed neighbours path", func() { 51 | c.Expect(checkPathFunction(gr, 1, 2, nil, SimpleWeightFunc), IsTrue) 52 | c.Expect(checkPathFunction(gr, 2, 4, nil, SimpleWeightFunc), IsTrue) 53 | c.Expect(checkPathFunction(gr, 1, 6, nil, SimpleWeightFunc), IsTrue) 54 | }) 55 | 56 | c.Specify("Check undirected neighbours path", func() { 57 | c.Expect(checkPathFunction(gr, 4, 6, nil, SimpleWeightFunc), IsTrue) 58 | c.Expect(checkPathFunction(gr, 6, 4, nil, SimpleWeightFunc), IsTrue) 59 | }) 60 | 61 | c.Specify("Check reversed directed neighbours", func() { 62 | c.Expect(checkPathFunction(gr, 6, 1, nil, SimpleWeightFunc), IsFalse) 63 | c.Expect(checkPathFunction(gr, 4, 3, nil, SimpleWeightFunc), IsFalse) 64 | c.Expect(checkPathFunction(gr, 5, 4, nil, SimpleWeightFunc), IsFalse) 65 | }) 66 | 67 | c.Specify("Check long path", func() { 68 | c.Expect(checkPathFunction(gr, 1, 6, nil, SimpleWeightFunc), IsTrue) 69 | c.Expect(checkPathFunction(gr, 1, 5, nil, SimpleWeightFunc), IsTrue) 70 | c.Expect(checkPathFunction(gr, 6, 5, nil, SimpleWeightFunc), IsTrue) 71 | c.Expect(checkPathFunction(gr, 3, 6, nil, SimpleWeightFunc), IsTrue) 72 | 73 | c.Expect(checkPathFunction(gr, 6, 3, nil, SimpleWeightFunc), IsFalse) 74 | }) 75 | 76 | c.Specify("Check weight limit", func() { 77 | c.Expect(checkPathFunction(gr, 1, 5, func(node VertexId, weight float64) bool { 78 | return weight < 2.0 79 | }, SimpleWeightFunc), IsFalse) 80 | }) 81 | } 82 | 83 | func GetAllMixedPathsSpec(c gospec.Context) { 84 | gr := generateMixedGraph1() 85 | /* 86 | [1 2 4 6] 87 | [1 2 6] 88 | [1 2 3 4 6] 89 | [1 6] 90 | */ 91 | 92 | pathsCnt := 0 93 | 94 | for path := range GetAllMixedPaths(gr, 1, 6) { 95 | pathsCnt++ 96 | c.Expect(ContainMixedPath(gr, path, true), IsTrue) 97 | } 98 | 99 | c.Expect(pathsCnt, Equals, 4) 100 | } 101 | 102 | func BellmanFordSingleSourceSpec(c gospec.Context) { 103 | gr := generateDirectedGraph1() 104 | 105 | marks := BellmanFordSingleSource(gr, VertexId(2), SimpleWeightFunc) 106 | c.Expect(len(marks), Equals, gr.Order()) 107 | 108 | c.Expect(PathFromMarks(marks, VertexId(6)), ContainsExactly, Values(VertexId(2), VertexId(6))) 109 | c.Expect(PathFromMarks(marks, VertexId(5)), ContainsExactly, Values(VertexId(2), VertexId(4), VertexId(5))) 110 | c.Expect(PathFromMarks(marks, VertexId(1)), ContainsExactly, Values()) 111 | } 112 | 113 | func TestSearch(t *testing.T) { 114 | r := gospec.NewRunner() 115 | 116 | { 117 | // paramenerized test creator 118 | cr := func(checkPathFunction CheckDirectedPath) func (c gospec.Context) { 119 | return func(c gospec.Context){ 120 | CheckDirectedPathSpec(c, checkPathFunction) 121 | } 122 | } 123 | r.AddNamedSpec("CheckDirectedPath(Dijkstra)", cr(CheckDirectedPathDijkstra)) 124 | } 125 | { 126 | // paramenerized test creator 127 | cr := func(checkPathFunction CheckMixedPath) func (c gospec.Context) { 128 | return func(c gospec.Context){ 129 | CheckMixedPathSpec(c, checkPathFunction) 130 | } 131 | } 132 | r.AddNamedSpec("CheckMixedPath(Dijkstra)", cr(CheckMixedPathDijkstra)) 133 | } 134 | 135 | r.AddSpec(GetAllMixedPathsSpec) 136 | r.AddSpec(BellmanFordSingleSourceSpec) 137 | 138 | 139 | gospec.MainGoTest(r, t) 140 | } 141 | -------------------------------------------------------------------------------- /src/graph/stuff.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "github.com/StepLg/go-erx/src/erx" 5 | ) 6 | 7 | // Connection type. 8 | // 9 | // May be undirected, directed, reversed directed (if instead of tail->head 10 | // connection goes from head to tail) and none (uninitialized value) 11 | type MixedConnectionType uint8 12 | 13 | const ( 14 | CT_NONE MixedConnectionType = iota // there is no connection 15 | CT_UNDIRECTED // edge (undirected connection) 16 | CT_DIRECTED // arc (directed connection): from tail to head 17 | CT_DIRECTED_REVERSED // arc (reversed directed connection): from head to tail 18 | ) 19 | 20 | func (t MixedConnectionType) String() string { 21 | switch t { 22 | case CT_NONE : return "none" 23 | case CT_UNDIRECTED : return "undirected" 24 | case CT_DIRECTED : return "directed" 25 | case CT_DIRECTED_REVERSED : return "reversed" 26 | } 27 | 28 | return "unknown" 29 | } 30 | 31 | // Create undirected connection (edge). 32 | // 33 | // By agreement, edge tail is the vertex with smallest id, and head is the 34 | // vertex with largest id. 35 | func NewUndirectedConnection(n1, n2 VertexId) TypedConnection { 36 | if n1>n2 { 37 | n1, n2 = n2, n1 38 | } 39 | return TypedConnection { 40 | Connection: Connection { 41 | Tail: n1, 42 | Head: n2, 43 | }, 44 | Type: CT_UNDIRECTED, 45 | } 46 | } 47 | 48 | // Create directed connection (arc). 49 | func NewDirectedConnection(tail, head VertexId) TypedConnection { 50 | return TypedConnection { 51 | Connection: Connection { 52 | Tail: tail, 53 | Head: head, 54 | }, 55 | Type: CT_DIRECTED, 56 | } 57 | } 58 | 59 | // internal struct to store node with it's priority for priority queue 60 | type priority_data_t struct { 61 | Node VertexId 62 | Priority float64 63 | } 64 | 65 | type nodesPriority []priority_data_t 66 | 67 | func (d nodesPriority) Less(i, j int) bool { 68 | return d[i].Priority < d[j].Priority 69 | } 70 | 71 | func (d nodesPriority) Swap(i, j int) { 72 | d[i], d[j] = d[j], d[i] 73 | } 74 | 75 | func (d nodesPriority) Len() int { 76 | return len(d) 77 | } 78 | 79 | // Vertexes priority queue 80 | // 81 | // Note: internal use only! while there is lack of standart containers in golang. 82 | type nodesPriorityQueue interface { 83 | // Add new item to queue 84 | Add(node VertexId, priority float64) 85 | // Get item with max priority and remove it from the queue 86 | // 87 | // Panic if queue is empty 88 | Next() (VertexId, float64) 89 | // Get item with max priority without removing it from the queue 90 | // 91 | // Panic if queue is empty 92 | Pick() (VertexId, float64) 93 | // Total queue size 94 | Size() int 95 | // Check if queue is empty 96 | Empty() bool 97 | } 98 | 99 | // Very simple nodes priority queue 100 | // 101 | // Warning! It's very UNEFFICIENT!!! 102 | type nodesPriorityQueueSimple struct { 103 | data nodesPriority 104 | nodesIndex map[VertexId]int 105 | size int 106 | } 107 | 108 | // Create new simple nodes priority queue 109 | // 110 | // size is maximum number of nodes, which queue can store simultaneously 111 | func newPriorityQueueSimple(initialSize int) *nodesPriorityQueueSimple { 112 | if initialSize<=0 { 113 | err := erx.NewError("Can't create priority queue with non-positive size.") 114 | err.AddV("size", initialSize) 115 | panic(err) 116 | } 117 | 118 | q := &nodesPriorityQueueSimple { 119 | data: make(nodesPriority, initialSize), 120 | nodesIndex: make(map[VertexId]int), 121 | size: 0, 122 | } 123 | return q 124 | } 125 | 126 | // Add new item to queue 127 | func (q *nodesPriorityQueueSimple) Add(node VertexId, priority float64) { 128 | defer func() { 129 | if e := recover(); e!=nil { 130 | err := erx.NewSequent("", e) 131 | err.AddV("node", node) 132 | err.AddV("priority", priority) 133 | panic(err) 134 | } 135 | }() 136 | 137 | found := false 138 | if id, ok := q.nodesIndex[node]; ok { 139 | if priority > q.data[id].Priority { 140 | q.data[id].Priority = priority 141 | // changing position 142 | newId := id+1 143 | for q.data[newId].Priority id+1 { 148 | // need to move 149 | copy(q.data[id:newId-1], q.data[id+1:newId]) 150 | q.data[newId-1].Node = node 151 | q.data[newId-1].Priority = priority 152 | } 153 | } 154 | found = true 155 | } 156 | 157 | if !found { 158 | if q.size==len(q.data) { 159 | // resize 160 | // 2 is just a magic number 161 | newData := make(nodesPriority, 2*len(q.data)) 162 | copy(newData, q.data) 163 | q.data = newData 164 | } 165 | id := 0 166 | for q.data[id].Priorityid2 { 278 | id1, id2 = id2, id1 279 | } 280 | 281 | // id from upper triangle matrix, stored in vector 282 | connId := id1*(size-1) + id2 - 1 - id1*(id1+1)/2 283 | return connId 284 | } 285 | -------------------------------------------------------------------------------- /src/graph/stuff_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "testing" 5 | "github.com/orfjackal/gospec/src/gospec" 6 | . "github.com/orfjackal/gospec/src/gospec" 7 | ) 8 | 9 | func VertexesPriorityQueueSpec(c gospec.Context) { 10 | q := newPriorityQueueSimple(5) 11 | 12 | c.Specify("Empty queue", func() { 13 | c.Specify("is empty", func() { 14 | c.Expect(q.Empty(), IsTrue) 15 | c.Expect(q.Size(), Equals, 0) 16 | }) 17 | 18 | c.Specify("after add", func() { 19 | node := VertexId(1) 20 | priority := float64(0.5) 21 | q.Add(node, priority) 22 | 23 | c.Specify("no longer empty", func() { 24 | c.Expect(q.Empty(), IsFalse) 25 | c.Expect(q.Size(), Equals, 1) 26 | }) 27 | 28 | c.Specify("can make pick", func() { 29 | pickNode, pickPriority := q.Pick() 30 | c.Expect(pickNode, Equals, node) 31 | c.Expect(pickPriority, Equals, priority) 32 | c.Specify("not empty", func() { 33 | c.Expect(q.Empty(), IsFalse) 34 | c.Expect(q.Size(), Equals, 1) 35 | }) 36 | }) 37 | 38 | c.Specify("empty after next", func() { 39 | pickNode, pickPriority := q.Next() 40 | c.Expect(pickNode, Equals, node) 41 | c.Expect(pickPriority, Equals, priority) 42 | c.Expect(q.Empty(), IsTrue) 43 | c.Expect(q.Size(), Equals, 0) 44 | }) 45 | }) 46 | }) 47 | 48 | c.Specify("Several items with priorities", func() { 49 | n1 := VertexId(1) 50 | p1 := float64(1.0) 51 | n2 := VertexId(2) 52 | p2 := float64(2.0) 53 | n3 := VertexId(3) 54 | p3 := float64(0.5) 55 | n4 := VertexId(4) 56 | p4 := float64(1.5) 57 | 58 | q.Add(n1, p1) 59 | q.Add(n2, p2) 60 | q.Add(n3, p3) 61 | q.Add(n4, p4) 62 | 63 | c.Expect(q.Size(), Equals, 4) 64 | node, prior := q.Next(); 65 | c.Expect(node, Equals, n2) 66 | c.Expect(prior, Equals, p2) 67 | node, prior = q.Next(); 68 | c.Expect(node, Equals, n4) 69 | c.Expect(prior, Equals, p4) 70 | node, prior = q.Next(); 71 | c.Expect(node, Equals, n1) 72 | c.Expect(prior, Equals, p1) 73 | node, prior = q.Next(); 74 | c.Expect(node, Equals, n3) 75 | c.Expect(prior, Equals, p3) 76 | }) 77 | 78 | c.Specify("Manipulating items priority", func() { 79 | n1 := VertexId(1) 80 | p1 := float64(1.0) 81 | n2 := VertexId(2) 82 | p2 := float64(2.0) 83 | n3 := VertexId(3) 84 | p3 := float64(0.5) 85 | n4 := VertexId(4) 86 | p4 := float64(1.5) 87 | 88 | q.Add(n1, p1) 89 | q.Add(n2, p2) 90 | q.Add(n3, p3) 91 | q.Add(n4, p4) 92 | 93 | c.Specify("Do not decrease priority", func() { 94 | q.Add(n4, p4 - 1.0) 95 | 96 | c.Expect(q.Size(), Equals, 4) 97 | node, prior := q.Next(); 98 | c.Expect(node, Equals, n2) 99 | c.Expect(prior, Equals, p2) 100 | node, prior = q.Next(); 101 | c.Expect(node, Equals, n4) 102 | c.Expect(prior, Equals, p4) 103 | node, prior = q.Next(); 104 | c.Expect(node, Equals, n1) 105 | c.Expect(prior, Equals, p1) 106 | node, prior = q.Next(); 107 | c.Expect(node, Equals, n3) 108 | c.Expect(prior, Equals, p3) 109 | }) 110 | 111 | c.Specify("Change middle to top", func() { 112 | p4 = float64(3.0) 113 | q.Add(n4, p4) 114 | 115 | c.Expect(q.Size(), Equals, 4) 116 | node, prior := q.Next(); 117 | c.Expect(node, Equals, n4) 118 | c.Expect(prior, Equals, p4) 119 | node, prior = q.Next(); 120 | c.Expect(node, Equals, n2) 121 | c.Expect(prior, Equals, p2) 122 | node, prior = q.Next(); 123 | c.Expect(node, Equals, n1) 124 | c.Expect(prior, Equals, p1) 125 | node, prior = q.Next(); 126 | c.Expect(node, Equals, n3) 127 | c.Expect(prior, Equals, p3) 128 | }) 129 | }) 130 | 131 | c.Specify("Push more items than initial size", func() { 132 | n1 := VertexId(1) 133 | p1 := float64(1.0) 134 | n2 := VertexId(2) 135 | p2 := float64(2.0) 136 | n3 := VertexId(3) 137 | p3 := float64(0.5) 138 | n4 := VertexId(4) 139 | p4 := float64(1.5) 140 | n5 := VertexId(6) 141 | p5 := float64(1.6) 142 | n6 := VertexId(7) 143 | p6 := float64(1.7) 144 | 145 | 146 | q.Add(n1, p1) 147 | q.Add(n2, p2) 148 | q.Add(n3, p3) 149 | q.Add(n4, p4) 150 | q.Add(n5, p5) 151 | q.Add(n6, p6) 152 | 153 | c.Expect(q.Size(), Equals, 6) 154 | node, prior := q.Next(); 155 | c.Expect(node, Equals, n2) 156 | c.Expect(prior, Equals, p2) 157 | node, prior = q.Next(); 158 | c.Expect(node, Equals, n6) 159 | c.Expect(prior, Equals, p6) 160 | node, prior = q.Next(); 161 | c.Expect(node, Equals, n5) 162 | c.Expect(prior, Equals, p5) 163 | node, prior = q.Next(); 164 | c.Expect(node, Equals, n4) 165 | c.Expect(prior, Equals, p4) 166 | node, prior = q.Next(); 167 | c.Expect(node, Equals, n1) 168 | c.Expect(prior, Equals, p1) 169 | node, prior = q.Next(); 170 | c.Expect(node, Equals, n3) 171 | c.Expect(prior, Equals, p3) 172 | }) 173 | } 174 | 175 | func MatrixIndexerSpec(c gospec.Context) { 176 | size := 100 177 | usedIds := make(map[int]bool) 178 | nodesIds := make(map[VertexId]int) 179 | for i:=0; i2>3>4") 33 | ReadDgraphLine(gr1, "2>6") 34 | ReadDgraphLine(gr1, "5>4>2") 35 | 36 | gr2 := NewDirectedMap() 37 | ReadDgraphLine(gr2, "10>11>12") 38 | ReadDgraphLine(gr2, "14>13>11") 39 | ReadDgraphLine(gr2, "15>12>16>17") 40 | 41 | gr_merged := NewDirectedMap() 42 | CopyDirectedGraph(gr1, gr_merged) 43 | CopyDirectedGraph(gr2, gr_merged) 44 | 45 | return gr1, gr2, gr_merged 46 | } 47 | 48 | func genUgr2IndependentSubGr() (UndirectedGraphReader, UndirectedGraphReader, UndirectedGraphReader) { 49 | gr1 := NewUndirectedMap() 50 | ReadUgraphLine(gr1, "1-2-3-4") 51 | ReadUgraphLine(gr1, "2-6") 52 | ReadUgraphLine(gr1, "5-4-2") 53 | 54 | gr2 := NewUndirectedMap() 55 | ReadUgraphLine(gr2, "10-11-12") 56 | ReadUgraphLine(gr2, "14-13-11") 57 | ReadUgraphLine(gr2, "15-12-16-17") 58 | 59 | gr_merged := NewUndirectedMap() 60 | CopyUndirectedGraph(gr1, gr_merged) 61 | CopyUndirectedGraph(gr2, gr_merged) 62 | 63 | return gr1, gr2, gr_merged 64 | } 65 | --------------------------------------------------------------------------------