ipld-eth-server/vendor/gopkg.in/karalabe/cookiejar.v2/graph/dfs/dfs.go
Matt K 293dd2e848 Add vendor dir (#16) (#4)
* Add vendor dir so builds dont require dep

* Pin specific version go-eth version
2018-01-29 13:44:18 -06:00

127 lines
3.2 KiB
Go
Executable File

// CookieJar - A contestant's algorithm toolbox
// Copyright 2013 Peter Szilagyi. All rights reserved.
//
// CookieJar is dual licensed: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// The toolbox is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// Alternatively, the CookieJar toolbox may be used in accordance with the terms
// and conditions contained in a signed written agreement between you and the
// author(s).
// Package dfs implements the depth-first-search algorithm for the graphs.
//
// The DFS is implemented using on demand calculations, meaning that only that
// part of the search space will be expanded as requested, iteratively expanding
// it if needed.
//
// Neighbor traversal order currently is random due to the graph implementation.
// Specific order may be added in the future.
package dfs
import (
"gopkg.in/karalabe/cookiejar.v2/collections/stack"
"gopkg.in/karalabe/cookiejar.v2/graph"
)
// Depth-first-search algorithm data structure.
type Dfs struct {
graph *graph.Graph
source int
visited []bool
parents []int
order []int
pending *stack.Stack
builder *stack.Stack
}
// Creates a new random-order dfs structure.
func New(g *graph.Graph, src int) *Dfs {
d := new(Dfs)
d.graph = g
d.source = src
d.visited = make([]bool, g.Vertices())
d.parents = make([]int, g.Vertices())
d.order = make([]int, 0, g.Vertices())
d.pending = stack.New()
d.pending.Push(src)
d.builder = stack.New()
return d
}
// Generates the path from the source node to the destination.
func (d *Dfs) Path(dst int) []int {
// If not found yet, but processing's not done, search
if !d.visited[dst] && !d.pending.Empty() {
d.search(dst)
}
// If done but still not found return a nil slice
if !d.visited[dst] {
return nil
}
// Generate the path and return
for dst != d.source {
d.builder.Push(dst)
dst = d.parents[dst]
}
d.builder.Push(dst)
path := make([]int, d.builder.Size())
for i := 0; i < len(path); i++ {
path[i] = d.builder.Pop().(int)
}
return path
}
// Checks whether a given vertex is reachable from the source.
func (d *Dfs) Reachable(dst int) bool {
if !d.visited[dst] && !d.pending.Empty() {
d.search(dst)
}
return d.visited[dst]
}
// Generates the full order in which nodes were traversed.
func (d *Dfs) Order() []int {
// Force dfs termination
if !d.pending.Empty() {
d.search(-1)
}
return d.order
}
// Continues the DFS search from the last yield point, looking for dst.
func (d *Dfs) search(dst int) {
for !d.pending.Empty() {
// Fetch the next node, and visit if new
src := d.pending.Pop().(int)
if !d.visited[src] {
d.visited[src] = true
d.order = append(d.order, src)
d.graph.Do(src, func(peer interface{}) {
if p := peer.(int); !d.visited[p] {
d.parents[p] = src
d.pending.Push(p)
}
})
}
// If we found the destination, yield
if dst == src {
return
}
}
}