github.com/gopherd/gonum@v0.0.4/graph/topo/tarjan_test.go (about) 1 // Copyright ©2014 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package topo 6 7 import ( 8 "reflect" 9 "testing" 10 11 "github.com/gopherd/gonum/graph" 12 "github.com/gopherd/gonum/graph/internal/ordered" 13 "github.com/gopherd/gonum/graph/simple" 14 ) 15 16 type interval struct{ start, end int } 17 18 var tarjanTests = []struct { 19 g []intset 20 21 ambiguousOrder []interval 22 want [][]int64 23 24 sortedLength int 25 unorderableLength int 26 sortable bool 27 }{ 28 { 29 g: []intset{ 30 0: linksTo(1), 31 1: linksTo(2, 7), 32 2: linksTo(3, 6), 33 3: linksTo(4), 34 4: linksTo(2, 5), 35 6: linksTo(3, 5), 36 7: linksTo(0, 6), 37 }, 38 39 want: [][]int64{ 40 {5}, 41 {2, 3, 4, 6}, 42 {0, 1, 7}, 43 }, 44 45 sortedLength: 1, 46 unorderableLength: 2, 47 sortable: false, 48 }, 49 { 50 g: []intset{ 51 0: linksTo(1, 2, 3), 52 1: linksTo(2), 53 2: linksTo(3), 54 3: linksTo(1), 55 }, 56 57 want: [][]int64{ 58 {1, 2, 3}, 59 {0}, 60 }, 61 62 sortedLength: 1, 63 unorderableLength: 1, 64 sortable: false, 65 }, 66 { 67 g: []intset{ 68 0: linksTo(1), 69 1: linksTo(0, 2), 70 2: linksTo(1), 71 }, 72 73 want: [][]int64{ 74 {0, 1, 2}, 75 }, 76 77 sortedLength: 0, 78 unorderableLength: 1, 79 sortable: false, 80 }, 81 { 82 g: []intset{ 83 0: linksTo(1), 84 1: linksTo(2, 3), 85 2: linksTo(4, 5), 86 3: linksTo(4, 5), 87 4: linksTo(6), 88 5: nil, 89 6: nil, 90 }, 91 92 // Node pairs (2, 3) and (4, 5) are not 93 // relatively orderable within each pair. 94 ambiguousOrder: []interval{ 95 {0, 3}, // This includes node 6 since it only needs to be before 4 in topo sort. 96 {3, 5}, 97 }, 98 want: [][]int64{ 99 {6}, {5}, {4}, {3}, {2}, {1}, {0}, 100 }, 101 102 sortedLength: 7, 103 sortable: true, 104 }, 105 { 106 g: []intset{ 107 0: linksTo(1), 108 1: linksTo(2, 3, 4), 109 2: linksTo(0, 3), 110 3: linksTo(4), 111 4: linksTo(3), 112 }, 113 114 // SCCs are not relatively ordable. 115 ambiguousOrder: []interval{ 116 {0, 2}, 117 }, 118 want: [][]int64{ 119 {0, 1, 2}, 120 {3, 4}, 121 }, 122 123 sortedLength: 0, 124 unorderableLength: 2, 125 sortable: false, 126 }, 127 } 128 129 func TestSort(t *testing.T) { 130 for i, test := range tarjanTests { 131 g := simple.NewDirectedGraph() 132 for u, e := range test.g { 133 // Add nodes that are not defined by an edge. 134 if g.Node(int64(u)) == nil { 135 g.AddNode(simple.Node(u)) 136 } 137 for v := range e { 138 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 139 } 140 } 141 sorted, err := Sort(g) 142 var gotSortedLen int 143 for _, n := range sorted { 144 if n != nil { 145 gotSortedLen++ 146 } 147 } 148 if gotSortedLen != test.sortedLength { 149 t.Errorf("unexpected number of sortable nodes for test %d: got:%d want:%d", i, gotSortedLen, test.sortedLength) 150 } 151 if err == nil != test.sortable { 152 t.Errorf("unexpected sortability for test %d: got error: %v want: nil-error=%t", i, err, test.sortable) 153 } 154 if err != nil && len(err.(Unorderable)) != test.unorderableLength { 155 t.Errorf("unexpected number of unorderable nodes for test %d: got:%d want:%d", i, len(err.(Unorderable)), test.unorderableLength) 156 } 157 } 158 } 159 160 func TestTarjanSCC(t *testing.T) { 161 for i, test := range tarjanTests { 162 g := simple.NewDirectedGraph() 163 for u, e := range test.g { 164 // Add nodes that are not defined by an edge. 165 if g.Node(int64(u)) == nil { 166 g.AddNode(simple.Node(u)) 167 } 168 for v := range e { 169 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 170 } 171 } 172 gotSCCs := TarjanSCC(g) 173 // tarjan.strongconnect does range iteration over maps, 174 // so sort SCC members to ensure consistent ordering. 175 gotIDs := make([][]int64, len(gotSCCs)) 176 for i, scc := range gotSCCs { 177 gotIDs[i] = make([]int64, len(scc)) 178 for j, id := range scc { 179 gotIDs[i][j] = id.ID() 180 } 181 ordered.Int64s(gotIDs[i]) 182 } 183 for _, iv := range test.ambiguousOrder { 184 ordered.BySliceValues(test.want[iv.start:iv.end]) 185 ordered.BySliceValues(gotIDs[iv.start:iv.end]) 186 } 187 if !reflect.DeepEqual(gotIDs, test.want) { 188 t.Errorf("unexpected Tarjan scc result for %d:\n\tgot:%v\n\twant:%v", i, gotIDs, test.want) 189 } 190 } 191 } 192 193 var stabilizedSortTests = []struct { 194 g []intset 195 196 want []graph.Node 197 err error 198 }{ 199 { 200 g: []intset{ 201 0: linksTo(1), 202 1: linksTo(2, 7), 203 2: linksTo(3, 6), 204 3: linksTo(4), 205 4: linksTo(2, 5), 206 6: linksTo(3, 5), 207 7: linksTo(0, 6), 208 }, 209 210 want: []graph.Node{nil, nil, simple.Node(5)}, 211 err: Unorderable{ 212 {simple.Node(0), simple.Node(1), simple.Node(7)}, 213 {simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(6)}, 214 }, 215 }, 216 { 217 g: []intset{ 218 0: linksTo(1, 2, 3), 219 1: linksTo(2), 220 2: linksTo(3), 221 3: linksTo(1), 222 }, 223 224 want: []graph.Node{simple.Node(0), nil}, 225 err: Unorderable{ 226 {simple.Node(1), simple.Node(2), simple.Node(3)}, 227 }, 228 }, 229 { 230 g: []intset{ 231 0: linksTo(1), 232 1: linksTo(0, 2), 233 2: linksTo(1), 234 }, 235 236 want: []graph.Node{nil}, 237 err: Unorderable{ 238 {simple.Node(0), simple.Node(1), simple.Node(2)}, 239 }, 240 }, 241 { 242 g: []intset{ 243 0: linksTo(1), 244 1: linksTo(2, 3), 245 2: linksTo(4, 5), 246 3: linksTo(4, 5), 247 4: linksTo(6), 248 5: nil, 249 6: nil, 250 }, 251 252 want: []graph.Node{simple.Node(0), simple.Node(1), simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(5), simple.Node(6)}, 253 err: nil, 254 }, 255 { 256 g: []intset{ 257 0: linksTo(1), 258 1: linksTo(2, 3, 4), 259 2: linksTo(0, 3), 260 3: linksTo(4), 261 4: linksTo(3), 262 }, 263 264 want: []graph.Node{nil, nil}, 265 err: Unorderable{ 266 {simple.Node(0), simple.Node(1), simple.Node(2)}, 267 {simple.Node(3), simple.Node(4)}, 268 }, 269 }, 270 { 271 g: []intset{ 272 0: linksTo(1, 2, 3, 4, 5, 6), 273 1: linksTo(7), 274 2: linksTo(7), 275 3: linksTo(7), 276 4: linksTo(7), 277 5: linksTo(7), 278 6: linksTo(7), 279 7: nil, 280 }, 281 282 want: []graph.Node{simple.Node(0), simple.Node(1), simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(5), simple.Node(6), simple.Node(7)}, 283 err: nil, 284 }, 285 } 286 287 func TestSortStabilized(t *testing.T) { 288 for i, test := range stabilizedSortTests { 289 g := simple.NewDirectedGraph() 290 for u, e := range test.g { 291 // Add nodes that are not defined by an edge. 292 if g.Node(int64(u)) == nil { 293 g.AddNode(simple.Node(u)) 294 } 295 for v := range e { 296 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 297 } 298 } 299 got, err := SortStabilized(g, nil) 300 if !reflect.DeepEqual(got, test.want) { 301 t.Errorf("unexpected sort result for test %d: got:%d want:%d", i, got, test.want) 302 } 303 if !reflect.DeepEqual(err, test.err) { 304 t.Errorf("unexpected sort error for test %d: got:%v want:%v", i, err, test.want) 305 } 306 } 307 }