gonum.org/v1/gonum@v0.14.0/graph/path/shortest.go (about) 1 // Copyright ©2015 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 path 6 7 import ( 8 "math" 9 10 "golang.org/x/exp/rand" 11 12 "gonum.org/v1/gonum/floats/scalar" 13 "gonum.org/v1/gonum/graph" 14 "gonum.org/v1/gonum/graph/internal/ordered" 15 "gonum.org/v1/gonum/graph/internal/set" 16 "gonum.org/v1/gonum/mat" 17 ) 18 19 // Shortest is a shortest-path tree created by the BellmanFordFrom, DijkstraFrom 20 // or AStar single-source shortest path functions. 21 type Shortest struct { 22 // from holds the source node given to 23 // the function that returned the 24 // Shortest value. 25 from graph.Node 26 27 // nodes hold the nodes of the analysed 28 // graph. 29 nodes []graph.Node 30 // indexOf contains a mapping between 31 // the id-dense representation of the 32 // graph and the potentially id-sparse 33 // nodes held in nodes. 34 indexOf map[int64]int 35 36 // dist and next represent the shortest 37 // paths between nodes. 38 // 39 // Indices into dist and next are 40 // mapped through indexOf. 41 // 42 // dist contains the distances 43 // from the from node for each 44 // node in the graph. 45 dist []float64 46 // next contains the shortest-path 47 // tree of the graph. The index is a 48 // linear mapping of to-dense-id. 49 next []int 50 51 // hasNegativeCycle indicates 52 // whether the Shortest includes 53 // a negative cycle. This should 54 // be set by the function that 55 // returned the Shortest value. 56 hasNegativeCycle bool 57 58 // negCosts holds negative costs 59 // between pairs of nodes to report 60 // negative cycles. 61 // negCosts must be initialised by 62 // routines that can handle negative 63 // edge weights. 64 negCosts map[negEdge]float64 65 } 66 67 // newShortestFrom returns a shortest path tree for paths from u 68 // initialised with the given nodes. The nodes held by the returned 69 // Shortest may be lazily added. 70 func newShortestFrom(u graph.Node, nodes []graph.Node) Shortest { 71 indexOf := make(map[int64]int, len(nodes)) 72 uid := u.ID() 73 for i, n := range nodes { 74 indexOf[n.ID()] = i 75 if n.ID() == uid { 76 u = n 77 } 78 } 79 80 p := Shortest{ 81 from: u, 82 83 nodes: nodes, 84 indexOf: indexOf, 85 86 dist: make([]float64, len(nodes)), 87 next: make([]int, len(nodes)), 88 } 89 for i := range nodes { 90 p.dist[i] = math.Inf(1) 91 p.next[i] = -1 92 } 93 p.dist[indexOf[uid]] = 0 94 95 return p 96 } 97 98 // add adds a node to the Shortest, initialising its stored index and returning, and 99 // setting the distance and position as unconnected. add will panic if the node is 100 // already present. 101 func (p *Shortest) add(u graph.Node) int { 102 uid := u.ID() 103 if _, exists := p.indexOf[uid]; exists { 104 panic("shortest: adding existing node") 105 } 106 idx := len(p.nodes) 107 p.indexOf[uid] = idx 108 p.nodes = append(p.nodes, u) 109 p.dist = append(p.dist, math.Inf(1)) 110 p.next = append(p.next, -1) 111 return idx 112 } 113 114 // set sets the weight of the path from the node in p.nodes indexed by mid to the node 115 // indexed by to. 116 func (p Shortest) set(to int, weight float64, mid int) { 117 p.dist[to] = weight 118 p.next[to] = mid 119 if weight < 0 { 120 e := negEdge{from: mid, to: to} 121 c, ok := p.negCosts[e] 122 if !ok { 123 p.negCosts[e] = weight 124 } else if weight < c { 125 // The only ways that we can have a new weight that is 126 // lower than the previous weight is if either the edge 127 // has already been traversed in a negative cycle, or 128 // the edge is reachable from a negative cycle. 129 // Either way the reported path is returned with a 130 // negative infinite path weight. 131 p.negCosts[e] = math.Inf(-1) 132 } 133 } 134 } 135 136 // From returns the starting node of the paths held by the Shortest. 137 func (p Shortest) From() graph.Node { return p.from } 138 139 // WeightTo returns the weight of the minimum path to v. If the path to v includes 140 // a negative cycle, the returned weight will not reflect the true path weight. 141 func (p Shortest) WeightTo(vid int64) float64 { 142 to, toOK := p.indexOf[vid] 143 if !toOK { 144 return math.Inf(1) 145 } 146 return p.dist[to] 147 } 148 149 // To returns a shortest path to v and the weight of the path. If the path 150 // to v includes a negative cycle, one pass through the cycle will be included 151 // in path, but any path leading into the negative cycle will be lost, and 152 // weight will be returned as -Inf. 153 func (p Shortest) To(vid int64) (path []graph.Node, weight float64) { 154 to, toOK := p.indexOf[vid] 155 if !toOK || math.IsInf(p.dist[to], 1) { 156 return nil, math.Inf(1) 157 } 158 from := p.indexOf[p.from.ID()] 159 path = []graph.Node{p.nodes[to]} 160 weight = math.Inf(1) 161 if p.hasNegativeCycle { 162 seen := make(set.Ints) 163 seen.Add(from) 164 for to != from { 165 next := p.next[to] 166 if math.IsInf(p.negCosts[negEdge{from: next, to: to}], -1) { 167 weight = math.Inf(-1) 168 } 169 if seen.Has(to) { 170 break 171 } 172 seen.Add(to) 173 path = append(path, p.nodes[next]) 174 to = next 175 } 176 } else { 177 n := len(p.nodes) 178 for to != from { 179 to = p.next[to] 180 path = append(path, p.nodes[to]) 181 if n < 0 { 182 panic("path: unexpected negative cycle") 183 } 184 n-- 185 } 186 } 187 ordered.Reverse(path) 188 return path, math.Min(weight, p.dist[p.indexOf[vid]]) 189 } 190 191 // ShortestAlts is a shortest-path tree created by the BellmanFordAllFrom or DijkstraAllFrom 192 // single-source shortest path functions. 193 type ShortestAlts struct { 194 // from holds the source node given to 195 // the function that returned the 196 // ShortestAlts value. 197 from graph.Node 198 199 // nodes hold the nodes of the analysed 200 // graph. 201 nodes []graph.Node 202 // indexOf contains a mapping between 203 // the id-dense representation of the 204 // graph and the potentially id-sparse 205 // nodes held in nodes. 206 indexOf map[int64]int 207 208 // dist and next represent the shortest 209 // paths between nodes. 210 // 211 // Indices into dist and next are 212 // mapped through indexOf. 213 // 214 // dist contains the distances 215 // from the from node for each 216 // node in the graph. 217 dist []float64 218 // next contains the shortest-path 219 // tree of the graph. The index is a 220 // linear mapping of to-dense-id. 221 next [][]int 222 223 // hasNegativeCycle indicates 224 // whether the ShortestAlts includes 225 // a negative cycle. This should 226 // be set by the function that 227 // returned the ShortestAlts value. 228 hasNegativeCycle bool 229 230 // negCosts holds negative costs 231 // between pairs of nodes to report 232 // negative cycles. 233 // negCosts must be initialised by 234 // routines that can handle negative 235 // edge weights. 236 negCosts map[negEdge]float64 237 } 238 239 // newShortestAltsFrom returns a shortest path tree for all paths from u 240 // initialised with the given nodes. The nodes held by the returned 241 // Shortest may be lazily added. 242 func newShortestAltsFrom(u graph.Node, nodes []graph.Node) ShortestAlts { 243 indexOf := make(map[int64]int, len(nodes)) 244 uid := u.ID() 245 for i, n := range nodes { 246 indexOf[n.ID()] = i 247 if n.ID() == uid { 248 u = n 249 } 250 } 251 252 p := ShortestAlts{ 253 from: u, 254 255 nodes: nodes, 256 indexOf: indexOf, 257 258 dist: make([]float64, len(nodes)), 259 next: make([][]int, len(nodes)), 260 } 261 for i := range nodes { 262 p.dist[i] = math.Inf(1) 263 p.next[i] = nil 264 } 265 p.dist[indexOf[uid]] = 0 266 267 return p 268 } 269 270 // add adds a node to the ShortestAlts, initialising its stored index and returning, and 271 // setting the distance and position as unconnected. add will panic if the node is 272 // already present. 273 func (p *ShortestAlts) add(u graph.Node) int { 274 uid := u.ID() 275 if _, exists := p.indexOf[uid]; exists { 276 panic("shortest: adding existing node") 277 } 278 idx := len(p.nodes) 279 p.indexOf[uid] = idx 280 p.nodes = append(p.nodes, u) 281 p.dist = append(p.dist, math.Inf(1)) 282 p.next = append(p.next, nil) 283 return idx 284 } 285 286 // set sets the weight of the path from the node in p.nodes indexed by mid to the node 287 // indexed by to. 288 func (p ShortestAlts) set(to int, weight float64, mid int) { 289 p.dist[to] = weight 290 p.next[to] = []int{mid} 291 if weight < 0 { 292 e := negEdge{from: mid, to: to} 293 c, ok := p.negCosts[e] 294 if !ok { 295 p.negCosts[e] = weight 296 } else if weight < c { 297 // The only ways that we can have a new weight that is 298 // lower than the previous weight is if either the edge 299 // has already been traversed in a negative cycle, or 300 // the edge is reachable from a negative cycle. 301 // Either way the reported path is returned with a 302 // negative infinite path weight. 303 p.negCosts[e] = math.Inf(-1) 304 } 305 } 306 } 307 308 // addPath adds a new path from the node in p.nodes indexed by mid to the node indexed 309 // by to. The weight of the path is expected to be the same as already existing paths 310 // between these nodes, but no check is made for this. 311 func (p ShortestAlts) addPath(to, mid int) { 312 // These are likely to be rare, so just loop over collisions. 313 for _, v := range p.next[to] { 314 if mid == v { 315 return 316 } 317 } 318 p.next[to] = append(p.next[to], mid) 319 } 320 321 // From returns the starting node of the paths held by the ShortestAlts. 322 func (p ShortestAlts) From() graph.Node { return p.from } 323 324 // WeightTo returns the weight of the minimum path to v. If the path to v includes 325 // a negative cycle, the returned weight will not reflect the true path weight. 326 func (p ShortestAlts) WeightTo(vid int64) float64 { 327 to, toOK := p.indexOf[vid] 328 if !toOK { 329 return math.Inf(1) 330 } 331 return p.dist[to] 332 } 333 334 // To returns a shortest path to v and the weight of the path. If more than 335 // one shortest path exists between u and v, a randomly chosen path will be 336 // returned and unique is returned false. If a cycle with zero weight exists 337 // in the path, it will not be included, but unique will be returned false. 338 // If the path to v includes a negative cycle, one pass through the cycle will 339 // be included in path, but any path leading into the negative cycle will be 340 // lost, and weight will be returned as -Inf. 341 func (p ShortestAlts) To(vid int64) (path []graph.Node, weight float64, unique bool) { 342 to, toOK := p.indexOf[vid] 343 if !toOK || math.IsInf(p.dist[to], 1) { 344 return nil, math.Inf(1), false 345 } 346 from := p.indexOf[p.from.ID()] 347 unique = true 348 path = []graph.Node{p.nodes[to]} 349 if p.hasNegativeCycle { 350 weight = math.Inf(1) 351 seen := make(set.Ints) 352 seen.Add(from) 353 for to != from { 354 c := p.next[to] 355 var next int 356 if len(c) != 1 { 357 unique = false 358 next = c[rand.Intn(len(c))] 359 } else { 360 next = c[0] 361 } 362 if math.IsInf(p.negCosts[negEdge{from: next, to: to}], -1) { 363 weight = math.Inf(-1) 364 unique = false 365 } 366 if seen.Has(to) { 367 break 368 } 369 seen.Add(to) 370 path = append(path, p.nodes[next]) 371 to = next 372 } 373 weight = math.Min(weight, p.dist[p.indexOf[vid]]) 374 } else { 375 seen := make([]int, len(p.nodes)) 376 for i := range seen { 377 seen[i] = -1 378 } 379 seen[to] = 0 380 381 var next int 382 for from != to { 383 c := p.next[to] 384 if len(c) != 1 { 385 unique = false 386 next = c[rand.Intn(len(c))] 387 } else { 388 next = c[0] 389 } 390 if seen[next] >= 0 { 391 path = path[:seen[next]] 392 } 393 seen[next] = len(path) 394 path = append(path, p.nodes[next]) 395 to = next 396 } 397 weight = p.dist[p.indexOf[vid]] 398 } 399 400 ordered.Reverse(path) 401 return path, weight, unique 402 } 403 404 // AllTo returns all shortest paths to v and the weight of the paths. Paths 405 // containing zero-weight cycles are not returned. If a negative cycle exists between 406 // u and v, paths is returned nil and weight is returned as -Inf. 407 func (p ShortestAlts) AllTo(vid int64) (paths [][]graph.Node, weight float64) { 408 from := p.indexOf[p.from.ID()] 409 to, toOK := p.indexOf[vid] 410 if !toOK || len(p.next[to]) == 0 { 411 if p.from.ID() == vid { 412 return [][]graph.Node{{p.nodes[from]}}, 0 413 } 414 return nil, math.Inf(1) 415 } 416 417 _, weight, unique := p.To(vid) 418 if math.IsInf(weight, -1) && !unique { 419 return nil, math.Inf(-1) 420 } 421 422 seen := make([]bool, len(p.nodes)) 423 p.allTo(from, to, seen, []graph.Node{p.nodes[to]}, func(path []graph.Node) { 424 paths = append(paths, append([]graph.Node(nil), path...)) 425 }) 426 weight = p.dist[to] 427 428 return paths, weight 429 } 430 431 // AllToFunc calls fn on all shortest paths to v. Paths containing zero-weight 432 // cycles are not considered. If a negative cycle exists between u and v, no 433 // path is considered. The fn closure must not retain the path parameter. 434 func (p ShortestAlts) AllToFunc(vid int64, fn func(path []graph.Node)) { 435 from := p.indexOf[p.from.ID()] 436 to, toOK := p.indexOf[vid] 437 if !toOK || len(p.next[to]) == 0 { 438 if p.from.ID() == vid { 439 fn([]graph.Node{p.nodes[from]}) 440 } 441 return 442 } 443 444 _, weight, unique := p.To(vid) 445 if math.IsInf(weight, -1) && !unique { 446 return 447 } 448 449 seen := make([]bool, len(p.nodes)) 450 p.allTo(from, to, seen, []graph.Node{p.nodes[to]}, fn) 451 } 452 453 // allTo recursively constructs a slice of paths extending from the node 454 // indexed into p.nodes by from to the node indexed by to. len(seen) must match 455 // the number of nodes held by the receiver. The path parameter is the current 456 // working path and the results passed to fn. 457 func (p ShortestAlts) allTo(from, to int, seen []bool, path []graph.Node, fn func(path []graph.Node)) { 458 seen[to] = true 459 if from == to { 460 if path == nil { 461 return 462 } 463 ordered.Reverse(path) 464 fn(path) 465 ordered.Reverse(path) 466 return 467 } 468 first := true 469 var seenWork []bool 470 for _, to := range p.next[to] { 471 if seen[to] { 472 continue 473 } 474 if first { 475 p := make([]graph.Node, len(path), len(path)+1) 476 copy(p, path) 477 path = p 478 seenWork = make([]bool, len(seen)) 479 first = false 480 } 481 copy(seenWork, seen) 482 p.allTo(from, to, seenWork, append(path, p.nodes[to]), fn) 483 } 484 } 485 486 // negEdge is a key into the negative costs map used by Shortest and ShortestAlts. 487 type negEdge struct{ from, to int } 488 489 // AllShortest is a shortest-path tree created by the DijkstraAllPaths, FloydWarshall 490 // or JohnsonAllPaths all-pairs shortest paths functions. 491 type AllShortest struct { 492 // nodes hold the nodes of the analysed 493 // graph. 494 nodes []graph.Node 495 // indexOf contains a mapping between 496 // the id-dense representation of the 497 // graph and the potentially id-sparse 498 // nodes held in nodes. 499 indexOf map[int64]int 500 501 // dist, next and forward represent 502 // the shortest paths between nodes. 503 // 504 // Indices into dist and next are 505 // mapped through indexOf. 506 // 507 // dist contains the pairwise 508 // distances between nodes. 509 // 510 // Internally, edges on negative 511 // cycles are given a special NaN 512 // weight, NaN(0xdefaced). 513 // This is returned to the user as 514 // -Inf. This approach allows -Inf 515 // weight edges on simple paths to be 516 // distinguished from -Inf weight 517 // paths that contain negative cycles. 518 // The distinction is visible to the 519 // user through whether then path 520 // returned with a -Inf weight is 521 // nil or contains a set of nodes. 522 dist *mat.Dense 523 // next contains the shortest-path 524 // tree of the graph. The first index 525 // is a linear mapping of from-dense-id 526 // and to-dense-id, to-major with a 527 // stride equal to len(nodes); the 528 // slice indexed to is the list of 529 // intermediates leading from the 'from' 530 // node to the 'to' node represented 531 // by dense id. 532 // The interpretation of next is 533 // dependent on the state of forward. 534 next [][]int 535 // forward indicates the direction of 536 // path reconstruction. Forward 537 // reconstruction is used for Floyd- 538 // Warshall and reverse is used for 539 // Dijkstra. 540 forward bool 541 } 542 543 var ( 544 // defaced is NaN(0xdefaced) used as a marker for -Inf weight edges 545 // within paths containing negative cycles. Routines marking these 546 // edges should use this value. 547 defaced = scalar.NaNWith(0xdefaced) 548 // defacedBits is the bit pattern we look for in AllShortest to 549 // identify the edges. 550 defacedBits = math.Float64bits(defaced) 551 ) 552 553 // newAllShortest returns an all-pairs shortest path forest for paths with the 554 // given nodes. The forward flag indicates whether the path reconstruction is 555 // performed in the forward (Floyd-Warshall) or reverse (Dijkstra/Johnson's) order. 556 func newAllShortest(nodes []graph.Node, forward bool) AllShortest { 557 if len(nodes) == 0 { 558 return AllShortest{} 559 } 560 indexOf := make(map[int64]int, len(nodes)) 561 for i, n := range nodes { 562 indexOf[n.ID()] = i 563 } 564 dist := make([]float64, len(nodes)*len(nodes)) 565 for i := range dist { 566 dist[i] = math.Inf(1) 567 } 568 return AllShortest{ 569 nodes: nodes, 570 indexOf: indexOf, 571 572 dist: mat.NewDense(len(nodes), len(nodes), dist), 573 next: make([][]int, len(nodes)*len(nodes)), 574 forward: forward, 575 } 576 } 577 578 // at returns a slice of node indexes into p.nodes for nodes that are mid points 579 // between nodes indexed by from and to. 580 func (p AllShortest) at(from, to int) (mid []int) { 581 return p.next[from+to*len(p.nodes)] 582 } 583 584 // set sets the weights of paths between node indexes into p.nodes for from and to 585 // passing through the nodes indexed by mid. 586 func (p AllShortest) set(from, to int, weight float64, mid ...int) { 587 p.dist.Set(from, to, weight) 588 p.next[from+to*len(p.nodes)] = append(p.next[from+to*len(p.nodes)][:0], mid...) 589 } 590 591 // add adds paths between node indexed in p.nodes by from and to passing through 592 // the nodes indexed by mid. 593 func (p AllShortest) add(from, to int, mid ...int) { 594 loop: // These are likely to be rare, so just loop over collisions. 595 for _, k := range mid { 596 for _, v := range p.next[from+to*len(p.nodes)] { 597 if k == v { 598 continue loop 599 } 600 } 601 p.next[from+to*len(p.nodes)] = append(p.next[from+to*len(p.nodes)], k) 602 } 603 } 604 605 // Weight returns the weight of the minimum path between u and v. 606 func (p AllShortest) Weight(uid, vid int64) float64 { 607 from, fromOK := p.indexOf[uid] 608 to, toOK := p.indexOf[vid] 609 if !fromOK || !toOK { 610 return math.Inf(1) 611 } 612 w := p.dist.At(from, to) 613 if math.Float64bits(w) == defacedBits { 614 return math.Inf(-1) 615 } 616 return w 617 } 618 619 // Between returns a shortest path from u to v and the weight of the path. If more than 620 // one shortest path exists between u and v, a randomly chosen path will be returned and 621 // unique is returned false. If a cycle with zero weight exists in the path, it will not 622 // be included, but unique will be returned false. If a negative cycle exists on the path 623 // from u to v, path will be returned nil, weight will be -Inf and unique will be false. 624 func (p AllShortest) Between(uid, vid int64) (path []graph.Node, weight float64, unique bool) { 625 from, fromOK := p.indexOf[uid] 626 to, toOK := p.indexOf[vid] 627 if !fromOK || !toOK || len(p.at(from, to)) == 0 { 628 if uid == vid { 629 if !fromOK { 630 return []graph.Node{node(uid)}, 0, true 631 } 632 return []graph.Node{p.nodes[from]}, 0, true 633 } 634 return nil, math.Inf(1), false 635 } 636 637 weight = p.dist.At(from, to) 638 if math.Float64bits(weight) == defacedBits { 639 return nil, math.Inf(-1), false 640 } 641 642 seen := make([]int, len(p.nodes)) 643 for i := range seen { 644 seen[i] = -1 645 } 646 var n graph.Node 647 if p.forward { 648 n = p.nodes[from] 649 seen[from] = 0 650 } else { 651 n = p.nodes[to] 652 seen[to] = 0 653 } 654 655 path = []graph.Node{n} 656 unique = true 657 658 var next int 659 for from != to { 660 c := p.at(from, to) 661 if len(c) != 1 { 662 unique = false 663 next = c[rand.Intn(len(c))] 664 } else { 665 next = c[0] 666 } 667 if seen[next] >= 0 { 668 path = path[:seen[next]] 669 } 670 seen[next] = len(path) 671 path = append(path, p.nodes[next]) 672 if p.forward { 673 from = next 674 } else { 675 to = next 676 } 677 } 678 if !p.forward { 679 ordered.Reverse(path) 680 } 681 682 return path, weight, unique 683 } 684 685 // AllBetween returns all shortest paths from u to v and the weight of the paths. Paths 686 // containing zero-weight cycles are not returned. If a negative cycle exists between 687 // u and v, paths is returned nil and weight is returned as -Inf. 688 func (p AllShortest) AllBetween(uid, vid int64) (paths [][]graph.Node, weight float64) { 689 from, fromOK := p.indexOf[uid] 690 to, toOK := p.indexOf[vid] 691 if !fromOK || !toOK || len(p.at(from, to)) == 0 { 692 if uid == vid { 693 if !fromOK { 694 return [][]graph.Node{{node(uid)}}, 0 695 } 696 return [][]graph.Node{{p.nodes[from]}}, 0 697 } 698 return nil, math.Inf(1) 699 } 700 701 weight = p.dist.At(from, to) 702 if math.Float64bits(weight) == defacedBits { 703 return nil, math.Inf(-1) 704 } 705 706 var n graph.Node 707 if p.forward { 708 n = p.nodes[from] 709 } else { 710 n = p.nodes[to] 711 } 712 seen := make([]bool, len(p.nodes)) 713 p.allBetween(from, to, seen, []graph.Node{n}, func(path []graph.Node) { 714 paths = append(paths, append([]graph.Node(nil), path...)) 715 }) 716 717 return paths, weight 718 } 719 720 // AllBetweenFunc calls fn on all shortest paths from u to v. Paths containing 721 // zero-weight cycles are not considered. If a negative cycle exists between u 722 // and v, no path is considered. The fn closure must not retain the path 723 // parameter. 724 func (p AllShortest) AllBetweenFunc(uid, vid int64, fn func(path []graph.Node)) { 725 from, fromOK := p.indexOf[uid] 726 to, toOK := p.indexOf[vid] 727 if !fromOK || !toOK || len(p.at(from, to)) == 0 { 728 if uid == vid { 729 if !fromOK { 730 fn([]graph.Node{node(uid)}) 731 return 732 } 733 fn([]graph.Node{p.nodes[from]}) 734 return 735 } 736 return 737 } 738 739 if math.Float64bits(p.dist.At(from, to)) == defacedBits { 740 return 741 } 742 743 var n graph.Node 744 if p.forward { 745 n = p.nodes[from] 746 } else { 747 n = p.nodes[to] 748 } 749 seen := make([]bool, len(p.nodes)) 750 p.allBetween(from, to, seen, []graph.Node{n}, fn) 751 } 752 753 // allBetween recursively constructs a set of paths extending from the node 754 // indexed into p.nodes by from to the node indexed by to. len(seen) must match 755 // the number of nodes held by the receiver. The path parameter is the current 756 // working path and the results passed to fn. 757 func (p AllShortest) allBetween(from, to int, seen []bool, path []graph.Node, fn func([]graph.Node)) { 758 if p.forward { 759 seen[from] = true 760 } else { 761 seen[to] = true 762 } 763 if from == to { 764 if path == nil { 765 return 766 } 767 if !p.forward { 768 ordered.Reverse(path) 769 } 770 fn(path) 771 if !p.forward { 772 ordered.Reverse(path) 773 } 774 return 775 } 776 first := true 777 var seenWork []bool 778 for _, n := range p.at(from, to) { 779 if seen[n] { 780 continue 781 } 782 if first { 783 p := make([]graph.Node, len(path), len(path)+1) 784 copy(p, path) 785 path = p 786 seenWork = make([]bool, len(seen)) 787 first = false 788 } 789 if p.forward { 790 from = n 791 } else { 792 to = n 793 } 794 copy(seenWork, seen) 795 p.allBetween(from, to, seenWork, append(path, p.nodes[n]), fn) 796 } 797 } 798 799 type node int64 800 801 func (n node) ID() int64 { return int64(n) }