diff --git a/actions/action.go b/actions/action.go index 39133415..2a3136ac 100644 --- a/actions/action.go +++ b/actions/action.go @@ -2,81 +2,46 @@ package actions import ( "fmt" - "net/http" - "net/url" + "log" "os" - "path/filepath" - "regexp" - "strings" - "github.com/nektos/act/common" - log "github.com/sirupsen/logrus" + "github.com/actions/workflow-parser/model" + "github.com/howeyc/gopass" ) -// imageURL is the directory where a `Dockerfile` should exist -func parseImageLocal(workingDir string, contextDir string) (contextDirOut string, tag string, ok bool) { - if !strings.HasPrefix(contextDir, "./") { - return "", "", false - } - contextDir = filepath.Join(workingDir, contextDir) - if _, err := os.Stat(filepath.Join(contextDir, "Dockerfile")); os.IsNotExist(err) { - log.Debugf("Ignoring missing Dockerfile '%s/Dockerfile'", contextDir) - return "", "", false - } +var secretCache map[string]string - sha, _, err := common.FindGitRevision(contextDir) - if err != nil { - log.Warnf("Unable to determine git revision: %v", err) - sha = "latest" - } - return contextDir, fmt.Sprintf("%s:%s", filepath.Base(contextDir), sha), true +type actionEnvironmentApplier struct { + *model.Action } -// imageURL is the URL for a docker repo -func parseImageReference(image string) (ref string, ok bool) { - imageURL, err := url.Parse(image) - if err != nil { - log.Debugf("Unable to parse image as url: %v", err) - return "", false - } - if imageURL.Scheme != "docker" { - log.Debugf("Ignoring non-docker ref '%s'", imageURL.String()) - return "", false - } - - return fmt.Sprintf("%s%s", imageURL.Host, imageURL.Path), true +func newActionEnvironmentApplier(action *model.Action) environmentApplier { + return &actionEnvironmentApplier{action} } -// imageURL is the directory where a `Dockerfile` should exist -func parseImageGithub(image string) (cloneURL *url.URL, ref string, path string, ok bool) { - re := regexp.MustCompile("^([^/@]+)/([^/@]+)(/([^@]*))?(@(.*))?$") - matches := re.FindStringSubmatch(image) - - if matches == nil { - return nil, "", "", false +func (action *actionEnvironmentApplier) applyEnvironment(env map[string]string) { + for envKey, envValue := range action.Env { + env[envKey] = envValue } - cloneURL, err := url.Parse(fmt.Sprintf("https://github.com/%s/%s", matches[1], matches[2])) - if err != nil { - log.Debugf("Unable to parse as URL: %v", err) - return nil, "", "", false - } + for _, secret := range action.Secrets { + if secretVal, ok := os.LookupEnv(secret); ok { + env[secret] = secretVal + } else { + if secretCache == nil { + secretCache = make(map[string]string) + } - resp, err := http.Head(cloneURL.String()) - if resp.StatusCode >= 400 || err != nil { - log.Debugf("Unable to HEAD URL %s status=%v err=%v", cloneURL.String(), resp.StatusCode, err) - return nil, "", "", false - } + if _, ok := secretCache[secret]; !ok { + fmt.Printf("Provide value for '%s': ", secret) + val, err := gopass.GetPasswdMasked() + if err != nil { + log.Fatal("abort") + } - ref = matches[6] - if ref == "" { - ref = "master" + secretCache[secret] = string(val) + } + env[secret] = secretCache[secret] + } } - - path = matches[4] - if path == "" { - path = "." - } - - return cloneURL, ref, path, true } diff --git a/actions/graph.go b/actions/graph.go new file mode 100644 index 00000000..1e1bbc95 --- /dev/null +++ b/actions/graph.go @@ -0,0 +1,64 @@ +package actions + +import ( + "log" + + "github.com/actions/workflow-parser/model" +) + +// return a pipeline that is run in series. pipeline is a list of steps to run in parallel +func newExecutionGraph(workflowConfig *model.Configuration, actionNames ...string) [][]string { + // first, build a list of all the necessary actions to run, and their dependencies + actionDependencies := make(map[string][]string) + for len(actionNames) > 0 { + newActionNames := make([]string, 0) + for _, aName := range actionNames { + // make sure we haven't visited this action yet + if _, ok := actionDependencies[aName]; !ok { + action := workflowConfig.GetAction(aName) + if action != nil { + actionDependencies[aName] = action.Needs + newActionNames = append(newActionNames, action.Needs...) + } + } + } + actionNames = newActionNames + } + + // next, build an execution graph + graph := make([][]string, 0) + for len(actionDependencies) > 0 { + stage := make([]string, 0) + for aName, aDeps := range actionDependencies { + // make sure all deps are in the graph already + if listInLists(aDeps, graph...) { + stage = append(stage, aName) + delete(actionDependencies, aName) + } + } + if len(stage) == 0 { + log.Fatalf("Unable to build dependency graph!") + } + graph = append(graph, stage) + } + + return graph +} + +// return true iff all strings in srcList exist in at least one of the searchLists +func listInLists(srcList []string, searchLists ...[]string) bool { + for _, src := range srcList { + found := false + for _, searchList := range searchLists { + for _, search := range searchList { + if src == search { + found = true + } + } + } + if !found { + return false + } + } + return true +} diff --git a/actions/model.go b/actions/model.go deleted file mode 100644 index bf720d8f..00000000 --- a/actions/model.go +++ /dev/null @@ -1,129 +0,0 @@ -package actions - -import ( - "fmt" - "log" - "os" - - "github.com/howeyc/gopass" -) - -type workflowModel struct { - On string - Resolves []string -} - -type actionModel struct { - Needs []string - Uses string - Runs []string - Args []string - Env map[string]string - Secrets []string -} - -type workflowsFile struct { - Workflow map[string]workflowModel - Action map[string]actionModel -} - -func (wFile *workflowsFile) getWorkflow(eventName string) (*workflowModel, string, error) { - var rtn workflowModel - for wName, w := range wFile.Workflow { - if w.On == eventName { - rtn = w - return &rtn, wName, nil - } - } - return nil, "", fmt.Errorf("unsupported event: %v", eventName) -} - -func (wFile *workflowsFile) getAction(actionName string) (*actionModel, error) { - if a, ok := wFile.Action[actionName]; ok { - return &a, nil - } - return nil, fmt.Errorf("unsupported action: %v", actionName) -} - -// return a pipeline that is run in series. pipeline is a list of steps to run in parallel -func (wFile *workflowsFile) newExecutionGraph(actionNames ...string) [][]string { - // first, build a list of all the necessary actions to run, and their dependencies - actionDependencies := make(map[string][]string) - for len(actionNames) > 0 { - newActionNames := make([]string, 0) - for _, aName := range actionNames { - // make sure we haven't visited this action yet - if _, ok := actionDependencies[aName]; !ok { - actionDependencies[aName] = wFile.Action[aName].Needs - newActionNames = append(newActionNames, wFile.Action[aName].Needs...) - } - } - actionNames = newActionNames - } - - // next, build an execution graph - graph := make([][]string, 0) - for len(actionDependencies) > 0 { - stage := make([]string, 0) - for aName, aDeps := range actionDependencies { - // make sure all deps are in the graph already - if listInLists(aDeps, graph...) { - stage = append(stage, aName) - delete(actionDependencies, aName) - } - } - if len(stage) == 0 { - log.Fatalf("Unable to build dependency graph!") - } - graph = append(graph, stage) - } - - return graph -} - -// return true iff all strings in srcList exist in at least one of the searchLists -func listInLists(srcList []string, searchLists ...[]string) bool { - for _, src := range srcList { - found := false - for _, searchList := range searchLists { - for _, search := range searchList { - if src == search { - found = true - } - } - } - if !found { - return false - } - } - return true -} - -var secretCache map[string]string - -func (action *actionModel) applyEnvironment(env map[string]string) { - for envKey, envValue := range action.Env { - env[envKey] = envValue - } - - for _, secret := range action.Secrets { - if secretVal, ok := os.LookupEnv(secret); ok { - env[secret] = secretVal - } else { - if secretCache == nil { - secretCache = make(map[string]string) - } - - if _, ok := secretCache[secret]; !ok { - fmt.Printf("Provide value for '%s': ", secret) - val, err := gopass.GetPasswdMasked() - if err != nil { - log.Fatal("abort") - } - - secretCache[secret] = string(val) - } - env[secret] = secretCache[secret] - } - } -} diff --git a/actions/parser.go b/actions/parser.go deleted file mode 100644 index 2497a4bc..00000000 --- a/actions/parser.go +++ /dev/null @@ -1,140 +0,0 @@ -package actions - -import ( - "bytes" - "errors" - "fmt" - "io" - "strings" - - "github.com/hashicorp/hcl" - "github.com/hashicorp/hcl/hcl/ast" - "github.com/hashicorp/hcl/hcl/token" - log "github.com/sirupsen/logrus" -) - -func parseWorkflowsFile(workflowReader io.Reader) (*workflowsFile, error) { - // TODO: add validation logic - // - check for circular dependencies - // - check for valid local path refs - // - check for valid dependencies - - buf := new(bytes.Buffer) - _, err := buf.ReadFrom(workflowReader) - if err != nil { - log.Error(err) - } - - workflows := new(workflowsFile) - - astFile, err := hcl.ParseBytes(buf.Bytes()) - if err != nil { - return nil, err - } - rootNode := ast.Walk(astFile.Node, cleanWorkflowsAST) - err = hcl.DecodeObject(workflows, rootNode) - if err != nil { - return nil, err - } - - return workflows, nil -} - -func cleanWorkflowsAST(node ast.Node) (ast.Node, bool) { - if objectItem, ok := node.(*ast.ObjectItem); ok { - key := objectItem.Keys[0].Token.Value() - - // handle condition where value is a string but should be a list - switch key { - case "args", "runs": - if literalType, ok := objectItem.Val.(*ast.LiteralType); ok { - listType := new(ast.ListType) - parts, err := parseCommand(literalType.Token.Value().(string)) - if err != nil { - return nil, false - } - quote := literalType.Token.Text[0] - for _, part := range parts { - part = fmt.Sprintf("%c%s%c", quote, strings.Replace(part, "\\", "\\\\", -1), quote) - listType.Add(&ast.LiteralType{ - Token: token.Token{ - Type: token.STRING, - Text: part, - }, - }) - } - objectItem.Val = listType - - } - case "resolves", "needs": - if literalType, ok := objectItem.Val.(*ast.LiteralType); ok { - listType := new(ast.ListType) - listType.Add(literalType) - objectItem.Val = listType - - } - } - } - return node, true -} - -// reused from: https://github.com/laurent22/massren/blob/ae4c57da1e09a95d9383f7eb645a9f69790dec6c/main.go#L172 -// nolint: gocyclo -func parseCommand(cmd string) ([]string, error) { - var args []string - state := "start" - current := "" - quote := "\"" - for i := 0; i < len(cmd); i++ { - c := cmd[i] - - if state == "quotes" { - if string(c) != quote { - current += string(c) - } else { - args = append(args, current) - current = "" - state = "start" - } - continue - } - - if c == '"' || c == '\'' { - state = "quotes" - quote = string(c) - continue - } - - if state == "arg" { - if c == ' ' || c == '\t' { - args = append(args, current) - current = "" - state = "start" - } else { - current += string(c) - } - continue - } - - if c != ' ' && c != '\t' { - state = "arg" - current += string(c) - } - } - - if state == "quotes" { - return []string{}, fmt.Errorf("unclosed quote in command line: %s", cmd) - } - - if current != "" { - args = append(args, current) - } - - if len(args) == 0 { - return []string{}, errors.New("empty command line") - } - - log.Debugf("Parsed literal %+q to list %+q", cmd, args) - - return args, nil -} diff --git a/actions/parser_test.go b/actions/parser_test.go deleted file mode 100644 index 9826e21f..00000000 --- a/actions/parser_test.go +++ /dev/null @@ -1,161 +0,0 @@ -package actions - -import ( - "strings" - "testing" - - log "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" -) - -func TestParseWorkflowsFile(t *testing.T) { - log.SetLevel(log.DebugLevel) - - conf := ` - workflow "build-and-deploy" { - on = "push" - resolves = ["deploy"] - } - - action "build" { - uses = "./action1" - args = "echo 'build'" - } - - action "test" { - uses = "docker://ubuntu:18.04" - runs = "echo 'test'" - needs = ["build"] - } - - action "deploy" { - uses = "./action2" - args = ["echo","deploy"] - needs = ["test"] - } - - action "docker-login" { - uses = "docker://docker" - runs = ["sh", "-c", "echo $DOCKER_AUTH | docker login --username $REGISTRY_USER --password-stdin"] - secrets = ["DOCKER_AUTH"] - env = { - REGISTRY_USER = "username" - } - } - - action "unit-tests" { - uses = "./scripts/github_actions" - runs = "yarn test:ci-unittest || echo \"Unit tests failed, but running danger to present the results!\" 2>&1" - } - - action "regex-in-args" { - uses = "actions/bin/filter@master" - args = "tag v?[0-9]+\\.[0-9]+\\.[0-9]+" - } - - action "regex-in-args-array" { - uses = "actions/bin/filter@master" - args = ["tag","v?[0-9]+\\.[0-9]+\\.[0-9]+"] - } - ` - - workflows, err := parseWorkflowsFile(strings.NewReader(conf)) - - assert.Nil(t, err) - assert.Equal(t, 1, len(workflows.Workflow)) - - w, wName, _ := workflows.getWorkflow("push") - assert.Equal(t, "build-and-deploy", wName) - assert.ElementsMatch(t, []string{"deploy"}, w.Resolves) - - actions := []struct { - name string - uses string - needs []string - runs []string - args []string - secrets []string - }{ - {"build", - "./action1", - nil, - nil, - []string{"echo", "build"}, - nil, - }, - {"test", - "docker://ubuntu:18.04", - []string{"build"}, - []string{"echo", "test"}, - nil, - nil, - }, - {"deploy", - "./action2", - []string{"test"}, - nil, - []string{"echo", "deploy"}, - nil, - }, - {"docker-login", - "docker://docker", - nil, - []string{"sh", "-c", "echo $DOCKER_AUTH | docker login --username $REGISTRY_USER --password-stdin"}, - nil, - []string{"DOCKER_AUTH"}, - }, - {"unit-tests", - "./scripts/github_actions", - nil, - []string{"yarn", "test:ci-unittest", "||", "echo", "Unit tests failed, but running danger to present the results!", "2>&1"}, - nil, - nil, - }, - {"regex-in-args", - "actions/bin/filter@master", - nil, - nil, - []string{"tag", `v?[0-9]+\.[0-9]+\.[0-9]+`}, - nil, - }, - {"regex-in-args-array", - "actions/bin/filter@master", - nil, - nil, - []string{"tag", `v?[0-9]+\.[0-9]+\.[0-9]+`}, - nil, - }, - } - - for _, exp := range actions { - act, _ := workflows.getAction(exp.name) - assert.Equal(t, exp.uses, act.Uses, "[%s] Uses", exp.name) - if exp.needs == nil { - assert.Nil(t, act.Needs, "[%s] Needs", exp.name) - } else { - assert.ElementsMatch(t, exp.needs, act.Needs, "[%s] Needs", exp.name) - } - if exp.runs == nil { - assert.Nil(t, act.Runs, "[%s] Runs", exp.name) - } else { - assert.ElementsMatch(t, exp.runs, act.Runs, "[%s] Runs", exp.name) - } - if exp.args == nil { - assert.Nil(t, act.Args, "[%s] Args", exp.name) - } else { - assert.ElementsMatch(t, exp.args, act.Args, "[%s] Args", exp.name) - } - /* - if exp.env == nil { - assert.Nil(t, act.Env, "[%s] Env", exp.name) - } else { - assert.ElementsMatch(t, exp.env, act.Env, "[%s] Env", exp.name) - } - */ - if exp.secrets == nil { - assert.Nil(t, act.Secrets, "[%s] Secrets", exp.name) - } else { - assert.ElementsMatch(t, exp.secrets, act.Secrets, "[%s] Secrets", exp.name) - } - } -} diff --git a/actions/runner.go b/actions/runner.go index fa92d955..9683128f 100644 --- a/actions/runner.go +++ b/actions/runner.go @@ -1,20 +1,23 @@ package actions import ( + "fmt" "io/ioutil" "os" "path/filepath" "sort" + "github.com/actions/workflow-parser/model" + "github.com/actions/workflow-parser/parser" "github.com/nektos/act/common" log "github.com/sirupsen/logrus" ) type runnerImpl struct { - config *RunnerConfig - workflows *workflowsFile - tempDir string - eventJSON string + config *RunnerConfig + workflowConfig *model.Configuration + tempDir string + eventJSON string } // NewRunner Creates a new Runner @@ -56,7 +59,13 @@ func (runner *runnerImpl) setupWorkflows() error { defer workflowReader.Close() - runner.workflows, err = parseWorkflowsFile(workflowReader) + runner.workflowConfig, err = parser.Parse(workflowReader) + if err != nil { + parserError := err.(*parser.ParserError) + for _, e := range parserError.Errors { + fmt.Fprintln(os.Stderr, e) + } + } return err } @@ -88,7 +97,7 @@ func (runner *runnerImpl) resolvePath(path string) string { func (runner *runnerImpl) ListEvents() []string { log.Debugf("Listing all events") events := make([]string, 0) - for _, w := range runner.workflows.Workflow { + for _, w := range runner.workflowConfig.Workflows { events = append(events, w.On) } @@ -103,17 +112,14 @@ func (runner *runnerImpl) ListEvents() []string { // GraphEvent builds an execution path func (runner *runnerImpl) GraphEvent(eventName string) ([][]string, error) { log.Debugf("Listing actions for event '%s'", eventName) - workflow, _, err := runner.workflows.getWorkflow(eventName) - if err != nil { - return nil, err - } - return runner.workflows.newExecutionGraph(workflow.Resolves...), nil + resolves := runner.resolveEvent(runner.config.EventName) + return newExecutionGraph(runner.workflowConfig, resolves...), nil } // RunAction runs a set of actions in parallel, and their dependencies func (runner *runnerImpl) RunActions(actionNames ...string) error { log.Debugf("Running actions %+q", actionNames) - graph := runner.workflows.newExecutionGraph(actionNames...) + graph := newExecutionGraph(runner.workflowConfig, actionNames...) pipeline := make([]common.Executor, 0) for _, actions := range graph { @@ -131,15 +137,32 @@ func (runner *runnerImpl) RunActions(actionNames ...string) error { // RunEvent runs the actions for a single event func (runner *runnerImpl) RunEvent() error { log.Debugf("Running event '%s'", runner.config.EventName) - workflow, _, err := runner.workflows.getWorkflow(runner.config.EventName) - if err != nil { - return err - } - - log.Debugf("Running actions %s -> %s", runner.config.EventName, workflow.Resolves) - return runner.RunActions(workflow.Resolves...) + resolves := runner.resolveEvent(runner.config.EventName) + log.Debugf("Running actions %s -> %s", runner.config.EventName, resolves) + return runner.RunActions(resolves...) } func (runner *runnerImpl) Close() error { return os.RemoveAll(runner.tempDir) } + +// get list of resolves for an event +func (runner *runnerImpl) resolveEvent(eventName string) []string { + workflows := runner.workflowConfig.GetWorkflows(runner.config.EventName) + resolves := make([]string, 0) + for _, workflow := range workflows { + for _, resolve := range workflow.Resolves { + found := false + for _, r := range resolves { + if r == resolve { + found = true + break + } + } + if !found { + resolves = append(resolves, resolve) + } + } + } + return resolves +} diff --git a/actions/runner_exec.go b/actions/runner_exec.go index 14a00d06..3be4af87 100644 --- a/actions/runner_exec.go +++ b/actions/runner_exec.go @@ -9,19 +9,20 @@ import ( "path/filepath" "regexp" + "github.com/actions/workflow-parser/model" "github.com/nektos/act/common" "github.com/nektos/act/container" log "github.com/sirupsen/logrus" ) func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor { - action, err := runner.workflows.getAction(actionName) - if err != nil { - return common.NewErrorExecutor(err) + action := runner.workflowConfig.GetAction(actionName) + if action == nil { + return common.NewErrorExecutor(fmt.Errorf("Unable to find action named '%s'", actionName)) } env := make(map[string]string) - for _, applier := range []environmentApplier{action, runner} { + for _, applier := range []environmentApplier{newActionEnvironmentApplier(action), runner} { applier.applyEnvironment(env) } env["GITHUB_ACTION"] = actionName @@ -37,39 +38,51 @@ func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor { var image string executors := make([]common.Executor, 0) - if imageRef, ok := parseImageReference(action.Uses); ok { + switch uses := action.Uses.(type) { + + case *model.UsesDockerImage: + image = uses.Image executors = append(executors, container.NewDockerPullExecutor(container.NewDockerPullExecutorInput{ DockerExecutorInput: in, - Image: imageRef, + Image: image, })) - image = imageRef - } else if contextDir, imageTag, ok := parseImageLocal(runner.config.WorkingDir, action.Uses); ok { + + case *model.UsesPath: + contextDir := filepath.Join(runner.config.WorkingDir, uses.String()) + sha, _, err := common.FindGitRevision(contextDir) + if err != nil { + log.Warnf("Unable to determine git revision: %v", err) + sha = "latest" + } + image = fmt.Sprintf("%s:%s", filepath.Base(contextDir), sha) + executors = append(executors, container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{ DockerExecutorInput: in, ContextDir: contextDir, - ImageTag: imageTag, + ImageTag: image, })) - image = imageTag - } else if cloneURL, ref, path, ok := parseImageGithub(action.Uses); ok { - cloneDir := filepath.Join(os.TempDir(), "act", action.Uses) + + case *model.UsesRepository: + image = fmt.Sprintf("%s:%s", filepath.Base(uses.Repository), uses.Ref) + cloneURL := fmt.Sprintf("https://github.com/%s", uses.Repository) + + cloneDir := filepath.Join(os.TempDir(), "act", action.Uses.String()) executors = append(executors, common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{ URL: cloneURL, - Ref: ref, + Ref: uses.Ref, Dir: cloneDir, Logger: logger, Dryrun: runner.config.Dryrun, })) - contextDir := filepath.Join(cloneDir, path) - imageTag := fmt.Sprintf("%s:%s", filepath.Base(cloneURL.Path), ref) - + contextDir := filepath.Join(cloneDir, uses.Path) executors = append(executors, container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{ DockerExecutorInput: in, ContextDir: contextDir, - ImageTag: imageTag, + ImageTag: image, })) - image = imageTag - } else { + + default: return common.NewErrorExecutor(fmt.Errorf("unable to determine executor type for image '%s'", action.Uses)) } @@ -84,8 +97,8 @@ func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor { } executors = append(executors, container.NewDockerRunExecutor(container.NewDockerRunExecutorInput{ DockerExecutorInput: in, - Cmd: action.Args, - Entrypoint: action.Runs, + Cmd: action.Args.Parsed, + Entrypoint: action.Runs.Parsed, Image: image, WorkingDir: "/github/workspace", Env: envList, @@ -105,7 +118,11 @@ func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor { func (runner *runnerImpl) applyEnvironment(env map[string]string) { repoPath := runner.config.WorkingDir - _, workflowName, _ := runner.workflows.getWorkflow(runner.config.EventName) + workflows := runner.workflowConfig.GetWorkflows(runner.config.EventName) + if len(workflows) == 0 { + return + } + workflowName := workflows[0].Identifier env["HOME"] = "/github/home" env["GITHUB_ACTOR"] = "nektos/act" diff --git a/actions/runner_test.go b/actions/runner_test.go deleted file mode 100644 index bd1d31ec..00000000 --- a/actions/runner_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package actions - -import ( - "fmt" - "path/filepath" - "testing" - - "github.com/nektos/act/common" - log "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" -) - -func TestParseImageReference(t *testing.T) { - log.SetLevel(log.DebugLevel) - tables := []struct { - refIn string - refOut string - ok bool - }{ - {"docker://myhost.com/foo/bar", "myhost.com/foo/bar", true}, - {"docker://ubuntu", "ubuntu", true}, - {"docker://ubuntu:18.04", "ubuntu:18.04", true}, - {"docker://cibuilds/hugo:0.53", "cibuilds/hugo:0.53", true}, - {"http://google.com:8080", "", false}, - {"./foo", "", false}, - } - - for _, table := range tables { - refOut, ok := parseImageReference(table.refIn) - assert.Equal(t, table.refOut, refOut) - assert.Equal(t, table.ok, ok) - } - -} - -func TestParseImageLocal(t *testing.T) { - log.SetLevel(log.DebugLevel) - tables := []struct { - pathIn string - contextDir string - refTag string - ok bool - }{ - {"docker://myhost.com/foo/bar", "", "", false}, - {"http://google.com:8080", "", "", false}, - {"example/action1", "", "", false}, - {"./example/action1", "/example/action1", "action1:", true}, - } - - revision, _, err := common.FindGitRevision(".") - assert.Nil(t, err) - basedir, err := filepath.Abs("..") - assert.Nil(t, err) - for _, table := range tables { - contextDir, refTag, ok := parseImageLocal(basedir, table.pathIn) - assert.Equal(t, table.ok, ok, "ok match for %s", table.pathIn) - if ok { - assert.Equal(t, fmt.Sprintf("%s%s", basedir, table.contextDir), contextDir, "context dir doesn't match for %s", table.pathIn) - assert.Equal(t, fmt.Sprintf("%s%s", table.refTag, revision), refTag) - } - } - -} -func TestParseImageGithub(t *testing.T) { - log.SetLevel(log.DebugLevel) - tables := []struct { - image string - cloneURL string - ref string - path string - ok bool - }{ - {"nektos/act", "https://github.com/nektos/act", "master", ".", true}, - {"nektos/act/foo", "https://github.com/nektos/act", "master", "foo", true}, - {"nektos/act@xxxxx", "https://github.com/nektos/act", "xxxxx", ".", true}, - {"nektos/act/bar/baz@zzzzz", "https://github.com/nektos/act", "zzzzz", "bar/baz", true}, - {"assimovt/actions-github-deploy/github-deploy@deployment-status-metadata", "https://github.com/assimovt/actions-github-deploy", "deployment-status-metadata", "github-deploy", true}, - {"nektos/zzzzundefinedzzzz", "", "", "", false}, - } - - for _, table := range tables { - cloneURL, ref, path, ok := parseImageGithub(table.image) - assert.Equal(t, table.ok, ok, "ok match for %s", table.image) - if ok { - assert.Equal(t, table.cloneURL, cloneURL.String()) - assert.Equal(t, table.ref, ref) - assert.Equal(t, table.path, path) - } - } - -} diff --git a/common/git.go b/common/git.go index 4d2995b6..13ba8191 100644 --- a/common/git.go +++ b/common/git.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io/ioutil" - "net/url" "os" "path" "path/filepath" @@ -185,7 +184,7 @@ func findGitDirectory(fromFile string) (string, error) { // NewGitCloneExecutorInput the input for the NewGitCloneExecutor type NewGitCloneExecutorInput struct { - URL *url.URL + URL string Ref string Dir string Logger *log.Entry @@ -195,8 +194,8 @@ type NewGitCloneExecutorInput struct { // NewGitCloneExecutor creates an executor to clone git repos func NewGitCloneExecutor(input NewGitCloneExecutorInput) Executor { return func() error { - input.Logger.Infof("git clone '%s'", input.URL.String()) - input.Logger.Debugf(" cloning %s to %s", input.URL.String(), input.Dir) + input.Logger.Infof("git clone '%s'", input.URL) + input.Logger.Debugf(" cloning %s to %s", input.URL, input.Dir) if input.Dryrun { return nil @@ -210,7 +209,7 @@ func NewGitCloneExecutor(input NewGitCloneExecutorInput) Executor { r, err := git.PlainOpen(input.Dir) if err != nil { r, err = git.PlainClone(input.Dir, false, &git.CloneOptions{ - URL: input.URL.String(), + URL: input.URL, Progress: input.Logger.WriterLevel(log.DebugLevel), ReferenceName: refName, }) @@ -231,7 +230,7 @@ func NewGitCloneExecutor(input NewGitCloneExecutorInput) Executor { if err != nil && err.Error() != "already up-to-date" { input.Logger.Errorf("Unable to pull %s: %v", refName, err) } - input.Logger.Debugf("Cloned %s to %s", input.URL.String(), input.Dir) + input.Logger.Debugf("Cloned %s to %s", input.URL, input.Dir) err = w.Checkout(&git.CheckoutOptions{ Branch: refName, diff --git a/go.mod b/go.mod index 69bb338e..5b1ba2c3 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Microsoft/go-winio v0.4.11 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/actions/workflow-parser v0.0.0-20190130154146-aac54e2ba131 github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect github.com/docker/distribution v2.7.0+incompatible // indirect github.com/docker/docker v1.13.1 @@ -14,7 +15,7 @@ require ( github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/mux v1.6.2 // indirect - github.com/hashicorp/hcl v1.0.0 + github.com/hashicorp/hcl v1.0.0 // indirect github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jtolds/gls v4.2.1+incompatible // indirect @@ -25,6 +26,7 @@ require ( github.com/sirupsen/logrus v1.3.0 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect + github.com/soniakeys/graph v0.0.0 // indirect github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 // indirect github.com/stretchr/testify v1.3.0 diff --git a/go.sum b/go.sum index 2b495e3d..1d786057 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6 github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/actions/workflow-parser v0.0.0-20190130154146-aac54e2ba131 h1:f81A6l1wPc9LdSZ85yJb+YuZxHiweb6D3p1l/bKDbJg= +github.com/actions/workflow-parser v0.0.0-20190130154146-aac54e2ba131/go.mod h1:jz9ZVl8zUIcjMfDQearQjvUHIBhx9l1ys4keDd6be34= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= @@ -94,6 +96,10 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/soniakeys/bits v1.0.0 h1:Rune9VFefdJvLE0Q5iRCVGiKdSu2iDihs2I6SCm7evw= +github.com/soniakeys/bits v1.0.0/go.mod h1:7yJHB//UizrUr64VFneewK6SX5oeCf0SMbDYe2ey1JA= +github.com/soniakeys/graph v0.0.0 h1:C/Rr8rv9wbhZIsYHcWJFoI84pkipJocMYdRteE+/PQA= +github.com/soniakeys/graph v0.0.0/go.mod h1:lxpIbor/bIzWUAqvt1Dx92Hr63uWeyuEAbPnsjYbVwM= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= diff --git a/vendor/github.com/actions/workflow-parser/LICENSE b/vendor/github.com/actions/workflow-parser/LICENSE new file mode 100644 index 00000000..331b241b --- /dev/null +++ b/vendor/github.com/actions/workflow-parser/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/actions/workflow-parser/model/configuration.go b/vendor/github.com/actions/workflow-parser/model/configuration.go new file mode 100644 index 00000000..65d33ffa --- /dev/null +++ b/vendor/github.com/actions/workflow-parser/model/configuration.go @@ -0,0 +1,73 @@ +package model + +// Configuration is a parsed main.workflow file +type Configuration struct { + Actions []*Action + Workflows []*Workflow +} + +// Action represents a single "action" stanza in a .workflow file. +type Action struct { + Identifier string + Uses Uses + Runs, Args ActionCommand + Needs []string + Env map[string]string + Secrets []string +} + +// ActionCommand represents the optional "runs" and "args" attributes. +// Each one takes one of two forms: +// - runs="entrypoint arg1 arg2 ..." +// - runs=[ "entrypoint", "arg1", "arg2", ... ] +// If the user uses the string form, "Raw" contains that value, and +// "Parsed" contains an array of the string value split at whitespace. +// If the user uses the array form, "Raw" is empty, and "Parsed" contains +// the array. +type ActionCommand struct { + Raw string + Parsed []string +} + +// Workflow represents a single "workflow" stanza in a .workflow file. +type Workflow struct { + Identifier string + On string + Resolves []string +} + +// GetAction looks up action by identifier. +// +// If the action is not found, nil is returned. +func (c *Configuration) GetAction(id string) *Action { + for _, action := range c.Actions { + if action.Identifier == id { + return action + } + } + return nil +} + +// GetWorkflow looks up a workflow by identifier. +// +// If the workflow is not found, nil is returned. +func (c *Configuration) GetWorkflow(id string) *Workflow { + for _, workflow := range c.Workflows { + if workflow.Identifier == id { + return workflow + } + } + return nil +} + +// GetWorkflows gets all Workflow structures that match a given type of event. +// e.g., GetWorkflows("push") +func (c *Configuration) GetWorkflows(eventType string) []*Workflow { + var ret []*Workflow + for _, workflow := range c.Workflows { + if IsMatchingEventType(workflow.On, eventType) { + ret = append(ret, workflow) + } + } + return ret +} diff --git a/vendor/github.com/actions/workflow-parser/model/eventtypes.go b/vendor/github.com/actions/workflow-parser/model/eventtypes.go new file mode 100644 index 00000000..1c6bdc75 --- /dev/null +++ b/vendor/github.com/actions/workflow-parser/model/eventtypes.go @@ -0,0 +1,55 @@ +package model + +import ( + "strings" +) + +// IsAllowedEventType returns true if the event type is supported. +func IsAllowedEventType(eventType string) bool { + _, ok := eventTypeWhitelist[strings.ToLower(eventType)] + return ok +} + +// IsMatchingEventType checks to see if the "flowOn" string from a flow's on attribute matches the incoming webhook of type eventType. +func IsMatchingEventType(flowOn, eventType string) bool { + return strings.ToLower(flowOn) == strings.ToLower(eventType) +} + +// https://developer.github.com/actions/creating-workflows/workflow-configuration-options/#events-supported-in-workflow-files +var eventTypeWhitelist = map[string]struct{}{ + "check_run": {}, + "check_suite": {}, + "commit_comment": {}, + "create": {}, + "delete": {}, + "deployment": {}, + "deployment_status": {}, + "fork": {}, + "gollum": {}, + "issue_comment": {}, + "issues": {}, + "label": {}, + "member": {}, + "milestone": {}, + "page_build": {}, + "project_card": {}, + "project_column": {}, + "project": {}, + "public": {}, + "pull_request_review_comment": {}, + "pull_request_review": {}, + "pull_request": {}, + "push": {}, + "release": {}, + "repository_dispatch": {}, + "status": {}, + "watch": {}, +} + +func AddAllowedEventType(s string) { + eventTypeWhitelist[s] = struct{}{} +} + +func RemoveAllowedEventType(s string) { + delete(eventTypeWhitelist, s) +} diff --git a/vendor/github.com/actions/workflow-parser/model/uses.go b/vendor/github.com/actions/workflow-parser/model/uses.go new file mode 100644 index 00000000..54de82f6 --- /dev/null +++ b/vendor/github.com/actions/workflow-parser/model/uses.go @@ -0,0 +1,57 @@ +package model + +import ( + "fmt" +) + +type Uses interface { + fmt.Stringer + isUses() +} + +// UsesDockerImage represents `uses = "docker://"` +type UsesDockerImage struct { + Image string +} + +// UsesRepository represents `uses = "/[/]@"` +type UsesRepository struct { + Repository string + Path string + Ref string +} + +// UsesPath represents `uses = "./"` +type UsesPath struct { + Path string +} + +// UsesInvalid represents any invalid `uses = ""` value +type UsesInvalid struct { + Raw string +} + +func (u *UsesDockerImage) isUses() {} +func (u *UsesRepository) isUses() {} +func (u *UsesPath) isUses() {} +func (u *UsesInvalid) isUses() {} + +func (u *UsesDockerImage) String() string { + return fmt.Sprintf("docker://%s", u.Image) +} + +func (u *UsesRepository) String() string { + if u.Path == "" { + return fmt.Sprintf("%s@%s", u.Repository, u.Ref) + } + + return fmt.Sprintf("%s/%s@%s", u.Repository, u.Path, u.Ref) +} + +func (u *UsesPath) String() string { + return fmt.Sprintf("./%s", u.Path) +} + +func (u *UsesInvalid) String() string { + return u.Raw +} diff --git a/vendor/github.com/actions/workflow-parser/parser/errors.go b/vendor/github.com/actions/workflow-parser/parser/errors.go new file mode 100644 index 00000000..f47fc98e --- /dev/null +++ b/vendor/github.com/actions/workflow-parser/parser/errors.go @@ -0,0 +1,129 @@ +package parser + +import ( + "fmt" + "sort" + "strconv" + "strings" + + "github.com/actions/workflow-parser/model" +) + +type ParserError struct { + message string + Errors ErrorList + Actions []*model.Action + Workflows []*model.Workflow +} + +func (p *ParserError) Error() string { + return p.message +} + +// Error represents an error identified by the parser, either syntactic +// (HCL) or semantic (.workflow) in nature. There are fields for location +// (File, Line, Column), severity, and base error string. The `Error()` +// function on this type concatenates whatever bits of the location are +// available with the message. The severity is only used for filtering. +type Error struct { + message string + Pos ErrorPos + Severity Severity +} + +// ErrorPos represents the location of an error in a user's workflow +// file(s). +type ErrorPos struct { + File string + Line int + Column int +} + +// newFatal creates a new error at the FATAL level, indicating that the +// file is so broken it should not be displayed. +func newFatal(pos ErrorPos, format string, a ...interface{}) *Error { + return &Error{ + message: fmt.Sprintf(format, a...), + Pos: pos, + Severity: FATAL, + } +} + +// newError creates a new error at the ERROR level, indicating that the +// file can be displayed but cannot be run. +func newError(pos ErrorPos, format string, a ...interface{}) *Error { + return &Error{ + message: fmt.Sprintf(format, a...), + Pos: pos, + Severity: ERROR, + } +} + +// newWarning creates a new error at the WARNING level, indicating that +// the file might be runnable but might not execute as intended. +func newWarning(pos ErrorPos, format string, a ...interface{}) *Error { + return &Error{ + message: fmt.Sprintf(format, a...), + Pos: pos, + Severity: WARNING, + } +} + +func (e *Error) Error() string { + var sb strings.Builder + if e.Pos.Line != 0 { + sb.WriteString("Line ") // nolint: errcheck + sb.WriteString(strconv.Itoa(e.Pos.Line)) // nolint: errcheck + sb.WriteString(": ") // nolint: errcheck + } + if sb.Len() > 0 { + sb.WriteString(e.message) // nolint: errcheck + return sb.String() + } + return e.message +} + +const ( + _ = iota + + // WARNING indicates a mistake that might affect correctness + WARNING + + // ERROR indicates a mistake that prevents execution of any workflows in the file + ERROR + + // FATAL indicates a mistake that prevents even drawing the file + FATAL +) + +// Severity represents the level of an error encountered while parsing a +// workflow file. See the comments for WARNING, ERROR, and FATAL, above. +type Severity int + +// FirstError searches a Configuration for the first error at or above a +// given severity level. Checking the return value against nil is a good +// way to see if the file has any errors at or above the given severity. +// A caller intending to execute the file might check for +// `errors.FirstError(parser.WARNING)`, while a caller intending to +// display the file might check for `errors.FirstError(parser.FATAL)`. +func (errors ErrorList) FirstError(severity Severity) error { + for _, e := range errors { + if e.Severity >= severity { + return e + } + } + return nil +} + +type ErrorList []*Error + +func (a ErrorList) Len() int { return len(a) } +func (a ErrorList) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ErrorList) Less(i, j int) bool { return a[i].Pos.Line < a[j].Pos.Line } + +// sortErrors sorts the errors reported by the parser. Do this after +// parsing is complete. The sort is stable, so order is preserved within +// a single line: left to right, syntax errors before validation errors. +func (errors ErrorList) sort() { + sort.Stable(errors) +} diff --git a/vendor/github.com/actions/workflow-parser/parser/opts.go b/vendor/github.com/actions/workflow-parser/parser/opts.go new file mode 100644 index 00000000..4db39ba1 --- /dev/null +++ b/vendor/github.com/actions/workflow-parser/parser/opts.go @@ -0,0 +1,15 @@ +package parser + +type OptionFunc func(*parseState) + +func WithSuppressWarnings() OptionFunc { + return func(ps *parseState) { + ps.suppressSeverity = WARNING + } +} + +func WithSuppressErrors() OptionFunc { + return func(ps *parseState) { + ps.suppressSeverity = ERROR + } +} diff --git a/vendor/github.com/actions/workflow-parser/parser/parser.go b/vendor/github.com/actions/workflow-parser/parser/parser.go new file mode 100644 index 00000000..307092e4 --- /dev/null +++ b/vendor/github.com/actions/workflow-parser/parser/parser.go @@ -0,0 +1,807 @@ +package parser + +import ( + "fmt" + "io" + "io/ioutil" + "regexp" + "strings" + + "github.com/actions/workflow-parser/model" + "github.com/hashicorp/hcl" + "github.com/hashicorp/hcl/hcl/ast" + hclparser "github.com/hashicorp/hcl/hcl/parser" + "github.com/hashicorp/hcl/hcl/token" + "github.com/soniakeys/graph" +) + +const minVersion = 0 +const maxVersion = 0 +const maxSecrets = 100 + +type parseState struct { + Version int + Actions []*model.Action + Workflows []*model.Workflow + Errors ErrorList + + posMap map[interface{}]ast.Node + suppressSeverity Severity +} + +// Parse parses a .workflow file and return the actions and global variables found within. +func Parse(reader io.Reader, options ...OptionFunc) (*model.Configuration, error) { + // FIXME - check context for deadline? + b, err := ioutil.ReadAll(reader) + if err != nil { + return nil, err + } + + root, err := hcl.ParseBytes(b) + if err != nil { + if pe, ok := err.(*hclparser.PosError); ok { + pos := ErrorPos{File: pe.Pos.Filename, Line: pe.Pos.Line, Column: pe.Pos.Column} + errors := ErrorList{newFatal(pos, pe.Err.Error())} + return nil, &ParserError{ + message: pe.Err.Error(), + Errors: errors, + } + } + return nil, err + } + + ps := parseAndValidate(root.Node, options...) + if len(ps.Errors) > 0 { + return nil, &ParserError{ + message: "unable to parse and validate", + Errors: ps.Errors, + Actions: ps.Actions, + Workflows: ps.Workflows, + } + } + + return &model.Configuration{ + Actions: ps.Actions, + Workflows: ps.Workflows, + }, nil +} + +// parseAndValidate converts a HCL AST into a parseState and validates +// high-level structure. +// Parameters: +// - root - the contents of a .workflow file, as AST +// Returns: +// - a parseState structure containing actions and workflow definitions +func parseAndValidate(root ast.Node, options ...OptionFunc) *parseState { + ps := &parseState{ + posMap: make(map[interface{}]ast.Node), + } + + for _, option := range options { + option(ps) + } + + ps.parseRoot(root) + ps.validate() + ps.Errors.sort() + + return ps +} + +func (ps *parseState) validate() { + ps.analyzeDependencies() + ps.checkCircularDependencies() + ps.checkActions() + ps.checkFlows() +} + +func uniqStrings(items []string) []string { + seen := make(map[string]bool) + ret := make([]string, 0, len(items)) + for _, item := range items { + if !seen[item] { + seen[item] = true + ret = append(ret, item) + } + } + return ret +} + +// checkCircularDependencies finds loops in the action graph. +// It emits a fatal error for each cycle it finds, in the order (top to +// bottom, left to right) they appear in the .workflow file. +func (ps *parseState) checkCircularDependencies() { + // make a map from action name to node ID, which is the index in the ps.Actions array + // That is, ps.Actions[actionmap[X]].Identifier == X + actionmap := make(map[string]graph.NI) + for i, action := range ps.Actions { + actionmap[action.Identifier] = graph.NI(i) + } + + // make an adjacency list representation of the action dependency graph + adjList := make(graph.AdjacencyList, len(ps.Actions)) + for i, action := range ps.Actions { + adjList[i] = make([]graph.NI, 0, len(action.Needs)) + for _, depName := range action.Needs { + if depIdx, ok := actionmap[depName]; ok { + adjList[i] = append(adjList[i], depIdx) + } + } + } + + // find cycles, and print a fatal error for each one + g := graph.Directed{AdjacencyList: adjList} + g.Cycles(func(cycle []graph.NI) bool { + node := ps.posMap[&ps.Actions[cycle[len(cycle)-1]].Needs] + ps.addFatal(node, "Circular dependency on `%s'", ps.Actions[cycle[0]].Identifier) + return true + }) +} + +// checkActions returns error if any actions are syntactically correct but +// have structural errors +func (ps *parseState) checkActions() { + secrets := make(map[string]bool) + for _, t := range ps.Actions { + // Ensure the Action has a `uses` attribute + if t.Uses == nil { + ps.addError(ps.posMap[t], "Action `%s' must have a `uses' attribute", t.Identifier) + // continue, checking other actions + } + + // Ensure there aren't too many secrets + for _, str := range t.Secrets { + if !secrets[str] { + secrets[str] = true + if len(secrets) == maxSecrets+1 { + ps.addError(ps.posMap[&t.Secrets], "All actions combined must not have more than %d unique secrets", maxSecrets) + } + } + } + + // Ensure that no environment variable or secret begins with + // "GITHUB_", unless it's "GITHUB_TOKEN". + // Also ensure that all environment variable names come from the legal + // form for environment variable names. + // Finally, ensure that the same key name isn't used more than once + // between env and secrets, combined. + for k := range t.Env { + ps.checkEnvironmentVariable(k, ps.posMap[&t.Env]) + } + secretVars := make(map[string]bool) + for _, k := range t.Secrets { + ps.checkEnvironmentVariable(k, ps.posMap[&t.Secrets]) + if _, found := t.Env[k]; found { + ps.addError(ps.posMap[&t.Secrets], "Secret `%s' conflicts with an environment variable with the same name", k) + } + if secretVars[k] { + ps.addWarning(ps.posMap[&t.Secrets], "Secret `%s' redefined", k) + } + secretVars[k] = true + } + } +} + +var envVarChecker = regexp.MustCompile(`\A[A-Za-z_][A-Za-z_0-9]*\z`) + +func (ps *parseState) checkEnvironmentVariable(key string, node ast.Node) { + if key != "GITHUB_TOKEN" && strings.HasPrefix(key, "GITHUB_") { + ps.addWarning(node, "Environment variables and secrets beginning with `GITHUB_' are reserved") + } + if !envVarChecker.MatchString(key) { + ps.addWarning(node, "Environment variables and secrets must contain only A-Z, a-z, 0-9, and _ characters, got `%s'", key) + } +} + +// checkFlows appends an error if any workflows are syntactically correct but +// have structural errors +func (ps *parseState) checkFlows() { + actionmap := makeActionMap(ps.Actions) + for _, f := range ps.Workflows { + // make sure there's an `on` attribute + if f.On == "" { + ps.addError(ps.posMap[f], "Workflow `%s' must have an `on' attribute", f.Identifier) + // continue, checking other workflows + } else if !model.IsAllowedEventType(f.On) { + ps.addError(ps.posMap[&f.On], "Workflow `%s' has unknown `on' value `%s'", f.Identifier, f.On) + // continue, checking other workflows + } + + // make sure that the actions that are resolved all exist + for _, actionID := range f.Resolves { + _, ok := actionmap[actionID] + if !ok { + ps.addError(ps.posMap[&f.Resolves], "Workflow `%s' resolves unknown action `%s'", f.Identifier, actionID) + // continue, checking other workflows + } + } + } +} + +func makeActionMap(actions []*model.Action) map[string]*model.Action { + actionmap := make(map[string]*model.Action) + for _, action := range actions { + actionmap[action.Identifier] = action + } + return actionmap +} + +// Fill in Action dependencies for all actions based on explicit dependencies +// declarations. +// +// ps.Actions is an array of Action objects, as parsed. The Action objects in +// this array are mutated, by setting Action.dependencies for each. +func (ps *parseState) analyzeDependencies() { + actionmap := makeActionMap(ps.Actions) + for _, action := range ps.Actions { + // analyze explicit dependencies for each "needs" keyword + ps.analyzeNeeds(action, actionmap) + } + + // uniq all the dependencies lists + for _, action := range ps.Actions { + if len(action.Needs) >= 2 { + action.Needs = uniqStrings(action.Needs) + } + } +} + +func (ps *parseState) analyzeNeeds(action *model.Action, actionmap map[string]*model.Action) { + for _, need := range action.Needs { + _, ok := actionmap[need] + if !ok { + ps.addError(ps.posMap[&action.Needs], "Action `%s' needs nonexistent action `%s'", action.Identifier, need) + // continue, checking other actions + } + } +} + +// literalToStringMap converts a object value from the AST to a +// map[string]string. For example, the HCL `{ a="b" c="d" }` becomes the +// Go expression map[string]string{ "a": "b", "c": "d" }. +// If the value doesn't adhere to that format -- e.g., +// if it's not an object, or it has non-assignment attributes, or if any +// of its values are anything other than a string, the function appends an +// appropriate error. +func (ps *parseState) literalToStringMap(node ast.Node) map[string]string { + obj, ok := node.(*ast.ObjectType) + + if !ok { + ps.addError(node, "Expected object, got %s", typename(node)) + return nil + } + + ps.checkAssignmentsOnly(obj.List, "") + + ret := make(map[string]string) + for _, item := range obj.List.Items { + if !isAssignment(item) { + continue + } + str, ok := ps.literalToString(item.Val) + if ok { + key := ps.identString(item.Keys[0].Token) + if key != "" { + if _, found := ret[key]; found { + ps.addWarning(node, "Environment variable `%s' redefined", key) + } + ret[key] = str + } + } + } + + return ret +} + +func (ps *parseState) identString(t token.Token) string { + switch t.Type { + case token.STRING: + return t.Value().(string) + case token.IDENT: + return t.Text + default: + ps.addErrorFromToken(t, + "Each identifier should be a string, got %s", + strings.ToLower(t.Type.String())) + return "" + } +} + +// literalToStringArray converts a list value from the AST to a []string. +// For example, the HCL `[ "a", "b", "c" ]` becomes the Go expression +// []string{ "a", "b", "c" }. +// If the value doesn't adhere to that format -- it's not a list, or it +// contains anything other than strings, the function appends an +// appropriate error. +// If promoteScalars is true, then values that are scalar strings are +// promoted to a single-entry string array. E.g., "foo" becomes the Go +// expression []string{ "foo" }. +func (ps *parseState) literalToStringArray(node ast.Node, promoteScalars bool) ([]string, bool) { + literal, ok := node.(*ast.LiteralType) + if ok { + if promoteScalars && literal.Token.Type == token.STRING { + return []string{literal.Token.Value().(string)}, true + } + ps.addError(node, "Expected list, got %s", typename(node)) + return nil, false + } + + list, ok := node.(*ast.ListType) + if !ok { + ps.addError(node, "Expected list, got %s", typename(node)) + return nil, false + } + + ret := make([]string, 0, len(list.List)) + for _, literal := range list.List { + str, ok := ps.literalToString(literal) + if ok { + ret = append(ret, str) + } + } + + return ret, true +} + +// literalToString converts a literal value from the AST into a string. +// If the value isn't a scalar or isn't a string, the function appends an +// appropriate error and returns "", false. +func (ps *parseState) literalToString(node ast.Node) (string, bool) { + val := ps.literalCast(node, token.STRING) + if val == nil { + return "", false + } + return val.(string), true +} + +// literalToInt converts a literal value from the AST into an int64. +// Supported number formats are: 123, 0x123, and 0123. +// Exponents (1e6) and floats (123.456) generate errors. +// If the value isn't a scalar or isn't a number, the function appends an +// appropriate error and returns 0, false. +func (ps *parseState) literalToInt(node ast.Node) (int64, bool) { + val := ps.literalCast(node, token.NUMBER) + if val == nil { + return 0, false + } + return val.(int64), true +} + +func (ps *parseState) literalCast(node ast.Node, t token.Type) interface{} { + literal, ok := node.(*ast.LiteralType) + if !ok { + ps.addError(node, "Expected %s, got %s", strings.ToLower(t.String()), typename(node)) + return nil + } + + if literal.Token.Type != t { + ps.addError(node, "Expected %s, got %s", strings.ToLower(t.String()), typename(node)) + return nil + } + + return literal.Token.Value() +} + +// parseRoot parses the root of the AST, filling in ps.Version, ps.Actions, +// and ps.Workflows. +func (ps *parseState) parseRoot(node ast.Node) { + objectList, ok := node.(*ast.ObjectList) + if !ok { + // It should be impossible for HCL to return anything other than an + // ObjectList as the root node. This error should never happen. + ps.addError(node, "Internal error: root node must be an ObjectList") + return + } + + ps.Actions = make([]*model.Action, 0, len(objectList.Items)) + ps.Workflows = make([]*model.Workflow, 0, len(objectList.Items)) + identifiers := make(map[string]bool) + for idx, item := range objectList.Items { + if item.Assign.IsValid() { + ps.parseVersion(idx, item) + continue + } + ps.parseBlock(item, identifiers) + } +} + +// parseBlock parses a single, top-level "action" or "workflow" block, +// appending it to ps.Actions or ps.Workflows as appropriate. +func (ps *parseState) parseBlock(item *ast.ObjectItem, identifiers map[string]bool) { + if len(item.Keys) != 2 { + ps.addError(item, "Invalid toplevel declaration") + return + } + + cmd := ps.identString(item.Keys[0].Token) + var id string + + switch cmd { + case "action": + action := ps.actionifyItem(item) + if action != nil { + id = action.Identifier + ps.Actions = append(ps.Actions, action) + } + case "workflow": + workflow := ps.workflowifyItem(item) + if workflow != nil { + id = workflow.Identifier + ps.Workflows = append(ps.Workflows, workflow) + } + default: + ps.addError(item, "Invalid toplevel keyword, `%s'", cmd) + return + } + + if identifiers[id] { + ps.addError(item, "Identifier `%s' redefined", id) + } + + identifiers[id] = true +} + +// parseVersion parses a top-level `version=N` statement, filling in +// ps.Version. +func (ps *parseState) parseVersion(idx int, item *ast.ObjectItem) { + if len(item.Keys) != 1 || ps.identString(item.Keys[0].Token) != "version" { + // not a valid `version` declaration + ps.addError(item.Val, "Toplevel declarations cannot be assignments") + return + } + if idx != 0 { + ps.addError(item.Val, "`version` must be the first declaration") + return + } + version, ok := ps.literalToInt(item.Val) + if !ok { + return + } + if version < minVersion || version > maxVersion { + ps.addError(item.Val, "`version = %d` is not supported", version) + return + } + ps.Version = int(version) +} + +// parseIdentifier parses the double-quoted identifier (name) for a +// "workflow" or "action" block. +func (ps *parseState) parseIdentifier(key *ast.ObjectKey) string { + id := key.Token.Text + if len(id) < 3 || id[0] != '"' || id[len(id)-1] != '"' { + ps.addError(key, "Invalid format for identifier `%s'", id) + return "" + } + return id[1 : len(id)-1] +} + +// parseRequiredString parses a string value, setting its value into the +// out-parameter `value` and returning true if successful. +func (ps *parseState) parseRequiredString(value *string, val ast.Node, nodeType, name, id string) bool { + if *value != "" { + ps.addWarning(val, "`%s' redefined in %s `%s'", name, nodeType, id) + // continue, allowing the redefinition + } + + newVal, ok := ps.literalToString(val) + if !ok { + ps.addError(val, "Invalid format for `%s' in %s `%s', expected string", name, nodeType, id) + return false + } + + if newVal == "" { + ps.addError(val, "`%s' value in %s `%s' cannot be blank", name, nodeType, id) + return false + } + + *value = newVal + return true +} + +// parseBlockPreamble parses the beginning of a "workflow" or "action" +// block. +func (ps *parseState) parseBlockPreamble(item *ast.ObjectItem, nodeType string) (string, *ast.ObjectType) { + id := ps.parseIdentifier(item.Keys[1]) + if id == "" { + return "", nil + } + + node := item.Val + obj, ok := node.(*ast.ObjectType) + if !ok { + ps.addError(node, "Each %s must have an { ... } block", nodeType) + return "", nil + } + + ps.checkAssignmentsOnly(obj.List, id) + + return id, obj +} + +// actionifyItem converts an AST block to an Action object. +func (ps *parseState) actionifyItem(item *ast.ObjectItem) *model.Action { + id, obj := ps.parseBlockPreamble(item, "action") + if obj == nil { + return nil + } + + action := &model.Action{ + Identifier: id, + } + ps.posMap[action] = item + + for _, item := range obj.List.Items { + ps.parseActionAttribute(ps.identString(item.Keys[0].Token), action, item.Val) + } + + return action +} + +// parseActionAttribute parses a single key-value pair from an "action" +// block. This function rejects any unknown keys and enforces formatting +// requirements on all values. +// It also has higher-than-normal cyclomatic complexity, so we ask the +// gocyclo linter to ignore it. +// nolint: gocyclo +func (ps *parseState) parseActionAttribute(name string, action *model.Action, val ast.Node) { + switch name { + case "uses": + ps.parseUses(action, val) + case "needs": + needs, ok := ps.literalToStringArray(val, true) + if ok { + action.Needs = needs + ps.posMap[&action.Needs] = val + } + case "runs": + ps.parseCommand(action, &action.Runs, name, val, false) + case "args": + ps.parseCommand(action, &action.Args, name, val, true) + case "env": + env := ps.literalToStringMap(val) + if env != nil { + action.Env = env + } + ps.posMap[&action.Env] = val + case "secrets": + secrets, ok := ps.literalToStringArray(val, false) + if ok { + action.Secrets = secrets + ps.posMap[&action.Secrets] = val + } + default: + ps.addWarning(val, "Unknown action attribute `%s'", name) + } +} + +// parseUses sets the action.Uses value based on the contents of the AST +// node. This function enforces formatting requirements on the value. +func (ps *parseState) parseUses(action *model.Action, node ast.Node) { + if action.Uses != nil { + ps.addWarning(node, "`uses' redefined in action `%s'", action.Identifier) + // continue, allowing the redefinition + } + strVal, ok := ps.literalToString(node) + if !ok { + return + } + + if strVal == "" { + action.Uses = &model.UsesInvalid{} + ps.addError(node, "`uses' value in action `%s' cannot be blank", action.Identifier) + return + } + if strings.HasPrefix(strVal, "./") { + action.Uses = &model.UsesPath{Path: strings.TrimPrefix(strVal, "./")} + return + } + + if strings.HasPrefix(strVal, "docker://") { + action.Uses = &model.UsesDockerImage{Image: strings.TrimPrefix(strVal, "docker://")} + return + } + + tok := strings.Split(strVal, "@") + if len(tok) != 2 { + action.Uses = &model.UsesInvalid{Raw: strVal} + ps.addError(node, "The `uses' attribute must be a path, a Docker image, or owner/repo@ref") + return + } + ref := tok[1] + tok = strings.SplitN(tok[0], "/", 3) + if len(tok) < 2 { + action.Uses = &model.UsesInvalid{Raw: strVal} + ps.addError(node, "The `uses' attribute must be a path, a Docker image, or owner/repo@ref") + return + } + usesRepo := &model.UsesRepository{Repository: tok[0] + "/" + tok[1], Ref: ref} + action.Uses = usesRepo + if len(tok) == 3 { + usesRepo.Path = tok[2] + } +} + +// parseUses sets the action.Runs or action.Command value based on the +// contents of the AST node. This function enforces formatting +// requirements on the value. +func (ps *parseState) parseCommand(action *model.Action, dest *model.ActionCommand, name string, node ast.Node, allowBlank bool) { + if len(dest.Parsed) > 0 { + ps.addWarning(node, "`%s' redefined in action `%s'", name, action.Identifier) + // continue, allowing the redefinition + } + + // Is it a list? + if _, ok := node.(*ast.ListType); ok { + if parsed, ok := ps.literalToStringArray(node, false); ok { + dest.Parsed = parsed + } + return + } + + // If not, parse a whitespace-separated string into a list. + var raw string + var ok bool + if raw, ok = ps.literalToString(node); !ok { + ps.addError(node, "The `%s' attribute must be a string or a list", name) + return + } + if raw == "" && !allowBlank { + ps.addError(node, "`%s' value in action `%s' cannot be blank", name, action.Identifier) + return + } + dest.Raw = raw + dest.Parsed = strings.Fields(raw) +} + +func typename(val interface{}) string { + switch cast := val.(type) { + case *ast.ListType: + return "list" + case *ast.LiteralType: + return strings.ToLower(cast.Token.Type.String()) + case *ast.ObjectType: + return "object" + default: + return fmt.Sprintf("%T", val) + } +} + +// workflowifyItem converts an AST block to a Workflow object. +func (ps *parseState) workflowifyItem(item *ast.ObjectItem) *model.Workflow { + id, obj := ps.parseBlockPreamble(item, "workflow") + if obj == nil { + return nil + } + + var ok bool + workflow := &model.Workflow{Identifier: id} + for _, item := range obj.List.Items { + name := ps.identString(item.Keys[0].Token) + + switch name { + case "on": + ok = ps.parseRequiredString(&workflow.On, item.Val, "workflow", name, id) + if ok { + ps.posMap[&workflow.On] = item + } + case "resolves": + if workflow.Resolves != nil { + ps.addWarning(item.Val, "`resolves' redefined in workflow `%s'", id) + // continue, allowing the redefinition + } + workflow.Resolves, ok = ps.literalToStringArray(item.Val, true) + ps.posMap[&workflow.Resolves] = item + if !ok { + ps.addError(item.Val, "Invalid format for `resolves' in workflow `%s', expected list of strings", id) + // continue, allowing workflow with no `resolves` + } + default: + ps.addWarning(item.Val, "Unknown workflow attribute `%s'", name) + // continue, treat as no-op + } + } + + ps.posMap[workflow] = item + return workflow +} + +func isAssignment(item *ast.ObjectItem) bool { + return len(item.Keys) == 1 && item.Assign.IsValid() +} + +// checkAssignmentsOnly ensures that all elements in the object are "key = +// value" pairs. +func (ps *parseState) checkAssignmentsOnly(objectList *ast.ObjectList, actionID string) { + for _, item := range objectList.Items { + if !isAssignment(item) { + var desc string + if actionID == "" { + desc = "the object" + } else { + desc = fmt.Sprintf("action `%s'", actionID) + } + ps.addErrorFromObjectItem(item, "Each attribute of %s must be an assignment", desc) + continue + } + + child, ok := item.Val.(*ast.ObjectType) + if ok { + ps.checkAssignmentsOnly(child.List, actionID) + } + } +} + +func (ps *parseState) addWarning(node ast.Node, format string, a ...interface{}) { + if ps.suppressSeverity < WARNING { + ps.Errors = append(ps.Errors, newWarning(posFromNode(node), format, a...)) + } +} + +func (ps *parseState) addError(node ast.Node, format string, a ...interface{}) { + if ps.suppressSeverity < ERROR { + ps.Errors = append(ps.Errors, newError(posFromNode(node), format, a...)) + } +} + +func (ps *parseState) addErrorFromToken(t token.Token, format string, a ...interface{}) { + if ps.suppressSeverity < ERROR { + ps.Errors = append(ps.Errors, newError(posFromToken(t), format, a...)) + } +} + +func (ps *parseState) addErrorFromObjectItem(objectItem *ast.ObjectItem, format string, a ...interface{}) { + if ps.suppressSeverity < ERROR { + ps.Errors = append(ps.Errors, newError(posFromObjectItem(objectItem), format, a...)) + } +} + +func (ps *parseState) addFatal(node ast.Node, format string, a ...interface{}) { + if ps.suppressSeverity < FATAL { + ps.Errors = append(ps.Errors, newFatal(posFromNode(node), format, a...)) + } +} + +// posFromNode returns an ErrorPos (file, line, and column) from an AST +// node, so we can report specific locations for each parse error. +func posFromNode(node ast.Node) ErrorPos { + var pos *token.Pos + switch cast := node.(type) { + case *ast.ObjectList: + if len(cast.Items) > 0 { + if len(cast.Items[0].Keys) > 0 { + pos = &cast.Items[0].Keys[0].Token.Pos + } + } + case *ast.ObjectItem: + return posFromNode(cast.Val) + case *ast.ObjectType: + pos = &cast.Lbrace + case *ast.LiteralType: + pos = &cast.Token.Pos + case *ast.ListType: + pos = &cast.Lbrack + case *ast.ObjectKey: + pos = &cast.Token.Pos + } + + if pos == nil { + return ErrorPos{} + } + return ErrorPos{File: pos.Filename, Line: pos.Line, Column: pos.Column} +} + +// posFromObjectItem returns an ErrorPos from an ObjectItem. This is for +// cases where posFromNode(item) would fail because the item has no Val +// set. +func posFromObjectItem(item *ast.ObjectItem) ErrorPos { + if len(item.Keys) > 0 { + return posFromNode(item.Keys[0]) + } + return ErrorPos{} +} + +// posFromToken returns an ErrorPos from a Token. We can't use +// posFromNode here because Tokens aren't Nodes. +func posFromToken(token token.Token) ErrorPos { + return ErrorPos{File: token.Pos.Filename, Line: token.Pos.Line, Column: token.Pos.Column} +} diff --git a/vendor/github.com/soniakeys/bits/.travis.yml b/vendor/github.com/soniakeys/bits/.travis.yml new file mode 100644 index 00000000..2df1f242 --- /dev/null +++ b/vendor/github.com/soniakeys/bits/.travis.yml @@ -0,0 +1,9 @@ +sudo: false +language: go +go: master +before_script: + - go vet + - go get github.com/client9/misspell/cmd/misspell + - misspell -error * + - go get github.com/soniakeys/vetc + - vetc diff --git a/vendor/github.com/soniakeys/bits/bits.go b/vendor/github.com/soniakeys/bits/bits.go new file mode 100644 index 00000000..416b0d89 --- /dev/null +++ b/vendor/github.com/soniakeys/bits/bits.go @@ -0,0 +1,463 @@ +// Copyright 2017 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +// Bits implements methods on a bit array type. +// +// The Bits type holds a fixed size array of bits, numbered consecutively +// from zero. Some set-like operations are possible, but the API is more +// array-like or register-like. +package bits + +import ( + "fmt" + mb "math/bits" +) + +// Bits holds a fixed number of bits. +// +// Bit number 0 is stored in the LSB, or bit 0, of the word indexed at 0. +// +// When Num is not a multiple of 64, the last element of Bits will hold some +// bits beyond Num. These bits are undefined. They are not required to be +// zero but do not have any meaning. Bits methods are not required to leave +// them undisturbed. +type Bits struct { + Num int // number of bits + Bits []uint64 +} + +// New constructs a Bits value with the given number of bits. +// +// It panics if num is negative. +func New(num int) Bits { + if num < 0 { + panic("negative number of bits") + } + return Bits{num, make([]uint64, (num+63)>>6)} +} + +// NewGivens constructs a Bits value with the given bits nums set to 1. +// +// The number of bits will be just enough to hold the largest bit value +// listed. That is, the number of bits will be the max bit number plus one. +// +// It panics if any bit number is negative. +func NewGivens(nums ...int) Bits { + max := -1 + for _, p := range nums { + if p > max { + max = p + } + } + b := New(max + 1) + for _, p := range nums { + b.SetBit(p, 1) + } + return b +} + +// AllOnes returns true if all Num bits are 1. +func (b Bits) AllOnes() bool { + last := len(b.Bits) - 1 + for _, w := range b.Bits[:last] { + if w != ^uint64(0) { + return false + } + } + return ^b.Bits[last]<= b.Num { + panic("bit number out of range") + } + return int(b.Bits[n>>6] >> uint(n&63) & 1) +} + +// ClearAll sets all bits to 0. +func (b Bits) ClearAll() { + for i := range b.Bits { + b.Bits[i] = 0 + } +} + +// ClearBits sets the given bits to 0 in receiver b. +// +// Other bits of b are left unchanged. +// +// It panics if any bit number is out of range. +// That is, negative or >= the number of bits. +func (b Bits) ClearBits(nums ...int) { + for _, p := range nums { + b.SetBit(p, 0) + } +} + +// Equal returns true if all Num bits of a and b are equal. +// +// It panics if a and b have different Num. +func (a Bits) Equal(b Bits) bool { + if a.Num != b.Num { + panic("receiver and argument have different number of bits") + } + if a.Num == 0 { + return true + } + last := len(a.Bits) - 1 + for i, w := range a.Bits[:last] { + if w != b.Bits[i] { + return false + } + } + return (a.Bits[last]^b.Bits[last])<= b.Num { + return true + } + if !v(x<<6 | i) { + return false + } + w >>= uint(t + 1) + if w == 0 { + break + } + t = mb.TrailingZeros64(w) + i += 1 + t + } + } + } + return true +} + +// IterateZeros calls visitor function v for each bit with a value of 0, +// in order from lowest bit to highest bit. +// +// Iteration continues to the highest bit as long as v returns true. +// It stops if v returns false. +// +// IterateZeros returns true normally. It returns false if v returns false. +// +// IterateZeros may not be sensitive to changes if bits are changed during +// iteration, by the vistor function for example. +// See ZeroFrom for an iteration method sensitive to changes during iteration. +func (b Bits) IterateZeros(v func(int) bool) bool { + for x, w := range b.Bits { + w = ^w + if w != 0 { + t := mb.TrailingZeros64(w) + i := t // index in w of next 1 bit + for { + n := x<<6 | i + if n >= b.Num { + return true + } + if !v(x<<6 | i) { + return false + } + w >>= uint(t + 1) + if w == 0 { + break + } + t = mb.TrailingZeros64(w) + i += 1 + t + } + } + } + return true +} + +// Not sets receiver z to the complement of b. +func (z *Bits) Not(b Bits) { + if z.Num != b.Num { + *z = New(b.Num) + } + for i, w := range b.Bits { + z.Bits[i] = ^w + } +} + +// OneFrom returns the number of the first 1 bit at or after (from) bit num. +// +// It returns -1 if there is no one bit at or after num. +// +// This provides one way to iterate over one bits. +// To iterate over the one bits, call OneFrom with n = 0 to get the the first +// one bit, then call with the result + 1 to get successive one bits. +// Unlike the Iterate method, this technique is stateless and so allows +// bits to be changed between successive calls. +// +// There is no panic for calling OneFrom with an argument >= b.Num. +// In this case OneFrom simply returns -1. +// +// See also Iterate. +func (b Bits) OneFrom(num int) int { + if num >= b.Num { + return -1 + } + x := num >> 6 + // test for 1 in this word at or after n + if wx := b.Bits[x] >> uint(num&63); wx != 0 { + num += mb.TrailingZeros64(wx) + if num >= b.Num { + return -1 + } + return num + } + x++ + for y, wy := range b.Bits[x:] { + if wy != 0 { + num = (x+y)<<6 | mb.TrailingZeros64(wy) + if num >= b.Num { + return -1 + } + return num + } + } + return -1 +} + +// Or sets z = x | y. +// +// It panics if x and y do not have the same Num. +func (z *Bits) Or(x, y Bits) { + if x.Num != y.Num { + panic("arguments have different number of bits") + } + if z.Num != x.Num { + *z = New(x.Num) + } + for i, w := range y.Bits { + z.Bits[i] = x.Bits[i] | w + } +} + +// OnesCount returns the number of 1 bits. +func (b Bits) OnesCount() (c int) { + if b.Num == 0 { + return 0 + } + last := len(b.Bits) - 1 + for _, w := range b.Bits[:last] { + c += mb.OnesCount64(w) + } + c += mb.OnesCount64(b.Bits[last] << uint(len(b.Bits)*64-b.Num)) + return +} + +// Set sets the bits of z to the bits of x. +func (z *Bits) Set(b Bits) { + if z.Num != b.Num { + *z = New(b.Num) + } + copy(z.Bits, b.Bits) +} + +// SetAll sets z to have all 1 bits. +func (b Bits) SetAll() { + for i := range b.Bits { + b.Bits[i] = ^uint64(0) + } +} + +// SetBit sets the n'th bit to x, where x is a 0 or 1. +// +// It panics if n is out of range +func (b Bits) SetBit(n, x int) { + if n < 0 || n >= b.Num { + panic("bit number out of range") + } + if x == 0 { + b.Bits[n>>6] &^= 1 << uint(n&63) + } else { + b.Bits[n>>6] |= 1 << uint(n&63) + } +} + +// SetBits sets the given bits to 1 in receiver b. +// +// Other bits of b are left unchanged. +// +// It panics if any bit number is out of range, negative or >= the number +// of bits. +func (b Bits) SetBits(nums ...int) { + for _, p := range nums { + b.SetBit(p, 1) + } +} + +// Single returns true if b has exactly one 1 bit. +func (b Bits) Single() bool { + // like OnesCount, but stop as soon as two are found + if b.Num == 0 { + return false + } + c := 0 + last := len(b.Bits) - 1 + for _, w := range b.Bits[:last] { + c += mb.OnesCount64(w) + if c > 1 { + return false + } + } + c += mb.OnesCount64(b.Bits[last] << uint(len(b.Bits)*64-b.Num)) + return c == 1 +} + +// Slice returns a slice with the bit numbers of each 1 bit. +func (b Bits) Slice() (s []int) { + for x, w := range b.Bits { + if w == 0 { + continue + } + t := mb.TrailingZeros64(w) + i := t // index in w of next 1 bit + for { + n := x<<6 | i + if n >= b.Num { + break + } + s = append(s, n) + w >>= uint(t + 1) + if w == 0 { + break + } + t = mb.TrailingZeros64(w) + i += 1 + t + } + } + return +} + +// String returns a readable representation. +// +// The returned string is big-endian, with the highest number bit first. +// +// If Num is 0, an empty string is returned. +func (b Bits) String() (s string) { + if b.Num == 0 { + return "" + } + last := len(b.Bits) - 1 + for _, w := range b.Bits[:last] { + s = fmt.Sprintf("%064b", w) + s + } + lb := b.Num - 64*last + return fmt.Sprintf("%0*b", lb, + b.Bits[last]&(^uint64(0)>>uint(64-lb))) + s +} + +// Xor sets z = x ^ y. +func (z *Bits) Xor(x, y Bits) { + if x.Num != y.Num { + panic("arguments have different number of bits") + } + if z.Num != x.Num { + *z = New(x.Num) + } + for i, w := range y.Bits { + z.Bits[i] = x.Bits[i] ^ w + } +} + +// ZeroFrom returns the number of the first 0 bit at or after (from) bit num. +// +// It returns -1 if there is no zero bit at or after num. +// +// This provides one way to iterate over zero bits. +// To iterate over the zero bits, call ZeroFrom with n = 0 to get the the first +// zero bit, then call with the result + 1 to get successive zero bits. +// Unlike the IterateZeros method, this technique is stateless and so allows +// bits to be changed between successive calls. +// +// There is no panic for calling ZeroFrom with an argument >= b.Num. +// In this case ZeroFrom simply returns -1. +// +// See also IterateZeros. +func (b Bits) ZeroFrom(num int) int { + // code much like OneFrom except words are negated before testing + if num >= b.Num { + return -1 + } + x := num >> 6 + // negate word to test for 0 at or after n + if wx := ^b.Bits[x] >> uint(num&63); wx != 0 { + num += mb.TrailingZeros64(wx) + if num >= b.Num { + return -1 + } + return num + } + x++ + for y, wy := range b.Bits[x:] { + wy = ^wy + if wy != 0 { + num = (x+y)<<6 | mb.TrailingZeros64(wy) + if num >= b.Num { + return -1 + } + return num + } + } + return -1 +} diff --git a/vendor/github.com/soniakeys/bits/go.mod b/vendor/github.com/soniakeys/bits/go.mod new file mode 100644 index 00000000..a2464ef9 --- /dev/null +++ b/vendor/github.com/soniakeys/bits/go.mod @@ -0,0 +1 @@ +module "github.com/soniakeys/bits" diff --git a/vendor/github.com/soniakeys/bits/readme.adoc b/vendor/github.com/soniakeys/bits/readme.adoc new file mode 100644 index 00000000..d39b1aec --- /dev/null +++ b/vendor/github.com/soniakeys/bits/readme.adoc @@ -0,0 +1,38 @@ += Bits + +Bits provides methods on a bit array type. + +The Bits type holds a fixed size array of bits, numbered consecutively +from zero. Some set-like operations are possible, but the API is more +array-like or register-like. + +image:https://godoc.org/github.com/soniakeys/bits?status.svg[link=https://godoc.org/github.com/soniakeys/bits] image:https://travis-ci.org/soniakeys/bits.svg[link=https://travis-ci.org/soniakeys/bits] + +== Motivation and history + +This package evolved from needs of my library of +https://github.com/soniakeys/graph[graph algorithms]. For graph algorithms +a common need is to store a single bit of information per node in a way that +is both fast and memory efficient. I began by using `big.Int` from the standard +library, then wrapped big.Int in a type. From time to time I considered +other publicly available bit array or bit set packages, such as Will +Fitzgerald's popular https://github.com/willf/bitset[bitset], but there were +always little reasons I preferred my own type and methods. My type that +wrapped `big.Int` met my needs until some simple benchmarks indicated it +might be causing performance problems. Some further experiments supported +this hypothesis so I ran further tests with a prototype bit array written +from scratch. Then satisfied that my custom bit array was solving the graph +performance problems, I decided to move it to a separate package with the +idea it might have more general utility. For the initial version of this +package I did the following: + +- implemented a few tests to demonstrate fundamental correctness +- brought over most methods of my type that wrapped big.Int +- changed the index type from the graph-specific node index to a general `int` +- replaced some custom bit-twiddling with use of the new `math/bits` package + in the standard library +- renamed a few methods for clarity +- added a few methods for symmetry +- added a few new methods I had seen a need for in my graph library +- added doc, examples, tests, and more tests for 100% coverage +- added this readme diff --git a/vendor/github.com/soniakeys/graph/.gitignore b/vendor/github.com/soniakeys/graph/.gitignore new file mode 100644 index 00000000..a93b25f4 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/.gitignore @@ -0,0 +1,2 @@ +*.dot +anecdote/anecdote diff --git a/vendor/github.com/soniakeys/graph/.travis.yml b/vendor/github.com/soniakeys/graph/.travis.yml new file mode 100644 index 00000000..ffc16a19 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/.travis.yml @@ -0,0 +1,11 @@ +sudo: false +language: go +go: + - "1.9.x" + - master +before_script: + - go tool vet -composites=false -printf=false -shift=false . + - go get github.com/client9/misspell/cmd/misspell + - go get github.com/soniakeys/vetc + - misspell -error * */* */*/* + - vetc diff --git a/vendor/github.com/soniakeys/graph/adj.go b/vendor/github.com/soniakeys/graph/adj.go new file mode 100644 index 00000000..dbae4814 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/adj.go @@ -0,0 +1,406 @@ +// Copyright 2014 Sonia Keys +// License MIT: https://opensource.org/licenses/MIT + +package graph + +// adj.go contains methods on AdjacencyList and LabeledAdjacencyList. +// +// AdjacencyList methods are placed first and are alphabetized. +// LabeledAdjacencyList methods follow, also alphabetized. +// Only exported methods need be alphabetized; non-exported methods can +// be left near their use. + +import ( + "sort" + + "github.com/soniakeys/bits" +) + +// NI is a "node int" +// +// It is a node number or node ID. NIs are used extensively as slice indexes. +// NIs typically account for a significant fraction of the memory footprint of +// a graph. +type NI int32 + +// AnyParallel identifies if a graph contains parallel arcs, multiple arcs +// that lead from a node to the same node. +// +// If the graph has parallel arcs, the results fr and to represent an example +// where there are parallel arcs from node `fr` to node `to`. +// +// If there are no parallel arcs, the method returns false -1 -1. +// +// Multiple loops on a node count as parallel arcs. +// +// See also alt.AnyParallelMap, which can perform better for some large +// or dense graphs. +func (g AdjacencyList) AnyParallel() (has bool, fr, to NI) { + var t []NI + for n, to := range g { + if len(to) == 0 { + continue + } + // different code in the labeled version, so no code gen. + t = append(t[:0], to...) + sort.Slice(t, func(i, j int) bool { return t[i] < t[j] }) + t0 := t[0] + for _, to := range t[1:] { + if to == t0 { + return true, NI(n), t0 + } + t0 = to + } + } + return false, -1, -1 +} + +// Complement returns the arc-complement of a simple graph. +// +// The result will have an arc for every pair of distinct nodes where there +// is not an arc in g. The complement is valid for both directed and +// undirected graphs. If g is undirected, the complement will be undirected. +// The result will always be a simple graph, having no loops or parallel arcs. +func (g AdjacencyList) Complement() AdjacencyList { + c := make(AdjacencyList, len(g)) + b := bits.New(len(g)) + for n, to := range g { + b.ClearAll() + for _, to := range to { + b.SetBit(int(to), 1) + } + b.SetBit(n, 1) + ct := make([]NI, len(g)-b.OnesCount()) + i := 0 + b.IterateZeros(func(to int) bool { + ct[i] = NI(to) + i++ + return true + }) + c[n] = ct + } + return c +} + +// IsUndirected returns true if g represents an undirected graph. +// +// Returns true when all non-loop arcs are paired in reciprocal pairs. +// Otherwise returns false and an example unpaired arc. +func (g AdjacencyList) IsUndirected() (u bool, from, to NI) { + // similar code in dot/writeUndirected + unpaired := make(AdjacencyList, len(g)) + for fr, to := range g { + arc: // for each arc in g + for _, to := range to { + if to == NI(fr) { + continue // loop + } + // search unpaired arcs + ut := unpaired[to] + for i, u := range ut { + if u == NI(fr) { // found reciprocal + last := len(ut) - 1 + ut[i] = ut[last] + unpaired[to] = ut[:last] + continue arc + } + } + // reciprocal not found + unpaired[fr] = append(unpaired[fr], to) + } + } + for fr, to := range unpaired { + if len(to) > 0 { + return false, NI(fr), to[0] + } + } + return true, -1, -1 +} + +// SortArcLists sorts the arc lists of each node of receiver g. +// +// Nodes are not relabeled and the graph remains equivalent. +func (g AdjacencyList) SortArcLists() { + for _, to := range g { + sort.Slice(to, func(i, j int) bool { return to[i] < to[j] }) + } +} + +// ------- Labeled methods below ------- + +// ArcsAsEdges constructs an edge list with an edge for each arc, including +// reciprocals. +// +// This is a simple way to construct an edge list for algorithms that allow +// the duplication represented by the reciprocal arcs. (e.g. Kruskal) +// +// See also LabeledUndirected.Edges for the edge list without this duplication. +func (g LabeledAdjacencyList) ArcsAsEdges() (el []LabeledEdge) { + for fr, to := range g { + for _, to := range to { + el = append(el, LabeledEdge{Edge{NI(fr), to.To}, to.Label}) + } + } + return +} + +// DistanceMatrix constructs a distance matrix corresponding to the arcs +// of graph g and weight function w. +// +// An arc from f to t with WeightFunc return w is represented by d[f][t] == w. +// In case of parallel arcs, the lowest weight is stored. The distance from +// any node to itself d[n][n] is 0, unless the node has a loop with a negative +// weight. If g has no arc from f to distinct t, +Inf is stored for d[f][t]. +// +// The returned DistanceMatrix is suitable for DistanceMatrix.FloydWarshall. +func (g LabeledAdjacencyList) DistanceMatrix(w WeightFunc) (d DistanceMatrix) { + d = newDM(len(g)) + for fr, to := range g { + for _, to := range to { + // < to pick min of parallel arcs (also nicely ignores NaN) + if wt := w(to.Label); wt < d[fr][to.To] { + d[fr][to.To] = wt + } + } + } + return +} + +// HasArcLabel returns true if g has any arc from node `fr` to node `to` +// with label `l`. +// +// Also returned is the index within the slice of arcs from node `fr`. +// If no arc from `fr` to `to` with label `l` is present, HasArcLabel returns +// false, -1. +func (g LabeledAdjacencyList) HasArcLabel(fr, to NI, l LI) (bool, int) { + t := Half{to, l} + for x, h := range g[fr] { + if h == t { + return true, x + } + } + return false, -1 +} + +// AnyParallel identifies if a graph contains parallel arcs, multiple arcs +// that lead from a node to the same node. +// +// If the graph has parallel arcs, the results fr and to represent an example +// where there are parallel arcs from node `fr` to node `to`. +// +// If there are no parallel arcs, the method returns -1 -1. +// +// Multiple loops on a node count as parallel arcs. +// +// See also alt.AnyParallelMap, which can perform better for some large +// or dense graphs. +func (g LabeledAdjacencyList) AnyParallel() (has bool, fr, to NI) { + var t []NI + for n, to := range g { + if len(to) == 0 { + continue + } + // slightly different code needed here compared to AdjacencyList + t = t[:0] + for _, to := range to { + t = append(t, to.To) + } + sort.Slice(t, func(i, j int) bool { return t[i] < t[j] }) + t0 := t[0] + for _, to := range t[1:] { + if to == t0 { + return true, NI(n), t0 + } + t0 = to + } + } + return false, -1, -1 +} + +// AnyParallelLabel identifies if a graph contains parallel arcs with the same +// label. +// +// If the graph has parallel arcs with the same label, the results fr and to +// represent an example where there are parallel arcs from node `fr` +// to node `to`. +// +// If there are no parallel arcs, the method returns false -1 Half{}. +// +// Multiple loops on a node count as parallel arcs. +func (g LabeledAdjacencyList) AnyParallelLabel() (has bool, fr NI, to Half) { + var t []Half + for n, to := range g { + if len(to) == 0 { + continue + } + // slightly different code needed here compared to AdjacencyList + t = t[:0] + for _, to := range to { + t = append(t, to) + } + sort.Slice(t, func(i, j int) bool { + return t[i].To < t[j].To || + t[i].To == t[j].To && t[i].Label < t[j].Label + }) + t0 := t[0] + for _, to := range t[1:] { + if to == t0 { + return true, NI(n), t0 + } + t0 = to + } + } + return false, -1, Half{} +} + +// IsUndirected returns true if g represents an undirected graph. +// +// Returns true when all non-loop arcs are paired in reciprocal pairs with +// matching labels. Otherwise returns false and an example unpaired arc. +// +// Note the requirement that reciprocal pairs have matching labels is +// an additional test not present in the otherwise equivalent unlabeled version +// of IsUndirected. +func (g LabeledAdjacencyList) IsUndirected() (u bool, from NI, to Half) { + // similar code in LabeledAdjacencyList.Edges + unpaired := make(LabeledAdjacencyList, len(g)) + for fr, to := range g { + arc: // for each arc in g + for _, to := range to { + if to.To == NI(fr) { + continue // loop + } + // search unpaired arcs + ut := unpaired[to.To] + for i, u := range ut { + if u.To == NI(fr) && u.Label == to.Label { // found reciprocal + last := len(ut) - 1 + ut[i] = ut[last] + unpaired[to.To] = ut[:last] + continue arc + } + } + // reciprocal not found + unpaired[fr] = append(unpaired[fr], to) + } + } + for fr, to := range unpaired { + if len(to) > 0 { + return false, NI(fr), to[0] + } + } + return true, -1, to +} + +// ArcLabels constructs the multiset of LIs present in g. +func (g LabeledAdjacencyList) ArcLabels() map[LI]int { + s := map[LI]int{} + for _, to := range g { + for _, to := range to { + s[to.Label]++ + } + } + return s +} + +// NegativeArc returns true if the receiver graph contains a negative arc. +func (g LabeledAdjacencyList) NegativeArc(w WeightFunc) bool { + for _, nbs := range g { + for _, nb := range nbs { + if w(nb.Label) < 0 { + return true + } + } + } + return false +} + +// ParallelArcsLabel identifies all arcs from node `fr` to node `to` with label `l`. +// +// The returned slice contains an element for each arc from node `fr` to node `to` +// with label `l`. The element value is the index within the slice of arcs from node +// `fr`. +// +// See also the method HasArcLabel, which stops after finding a single arc. +func (g LabeledAdjacencyList) ParallelArcsLabel(fr, to NI, l LI) (p []int) { + t := Half{to, l} + for x, h := range g[fr] { + if h == t { + p = append(p, x) + } + } + return +} + +// Unlabeled constructs the unlabeled graph corresponding to g. +func (g LabeledAdjacencyList) Unlabeled() AdjacencyList { + a := make(AdjacencyList, len(g)) + for n, nbs := range g { + to := make([]NI, len(nbs)) + for i, nb := range nbs { + to[i] = nb.To + } + a[n] = to + } + return a +} + +// WeightedArcsAsEdges constructs a WeightedEdgeList object from the receiver. +// +// Internally it calls g.ArcsAsEdges() to obtain the Edges member. +// See LabeledAdjacencyList.ArcsAsEdges(). +func (g LabeledAdjacencyList) WeightedArcsAsEdges(w WeightFunc) *WeightedEdgeList { + return &WeightedEdgeList{ + Order: g.Order(), + WeightFunc: w, + Edges: g.ArcsAsEdges(), + } +} + +// WeightedInDegree computes the weighted in-degree of each node in g +// for a given weight function w. +// +// The weighted in-degree of a node is the sum of weights of arcs going to +// the node. +// +// A weighted degree of a node is often termed the "strength" of a node. +// +// See note for undirected graphs at LabeledAdjacencyList.WeightedOutDegree. +func (g LabeledAdjacencyList) WeightedInDegree(w WeightFunc) []float64 { + ind := make([]float64, len(g)) + for _, to := range g { + for _, to := range to { + ind[to.To] += w(to.Label) + } + } + return ind +} + +// WeightedOutDegree computes the weighted out-degree of the specified node +// for a given weight function w. +// +// The weighted out-degree of a node is the sum of weights of arcs going from +// the node. +// +// A weighted degree of a node is often termed the "strength" of a node. +// +// Note for undirected graphs, the WeightedOutDegree result for a node will +// equal the WeightedInDegree for the node. You can use WeightedInDegree if +// you have need for the weighted degrees of all nodes or use WeightedOutDegree +// to compute the weighted degrees of individual nodes. In either case loops +// are counted just once, unlike the (unweighted) UndirectedDegree methods. +func (g LabeledAdjacencyList) WeightedOutDegree(n NI, w WeightFunc) (d float64) { + for _, to := range g[n] { + d += w(to.Label) + } + return +} + +// More about loops and strength: I didn't see consensus on this especially +// in the case of undirected graphs. Some sources said to add in-degree and +// out-degree, which would seemingly double both loops and non-loops. +// Some said to double loops. Some said sum the edge weights and had no +// comment on loops. R of course makes everything an option. The meaning +// of "strength" where loops exist is unclear. So while I could write an +// UndirectedWeighted degree function that doubles loops but not edges, +// I'm going to just leave this for now. diff --git a/vendor/github.com/soniakeys/graph/adj_RO.go b/vendor/github.com/soniakeys/graph/adj_RO.go new file mode 100644 index 00000000..17df67c5 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/adj_RO.go @@ -0,0 +1,417 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +// adj_RO.go is code generated from adj_cg.go by directives in graph.go. +// Editing adj_cg.go is okay. +// DO NOT EDIT adj_RO.go. The RO is for Read Only. + +import ( + "errors" + "fmt" + "math/rand" + + "github.com/soniakeys/bits" +) + +// ArcDensity returns density for an simple directed graph. +// +// See also ArcDensity function. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) ArcDensity() float64 { + return ArcDensity(len(g), g.ArcSize()) +} + +// ArcSize returns the number of arcs in g. +// +// Note that for an undirected graph without loops, the number of undirected +// edges -- the traditional meaning of graph size -- will be ArcSize()/2. +// On the other hand, if g is an undirected graph that has or may have loops, +// g.ArcSize()/2 is not a meaningful quantity. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) ArcSize() int { + m := 0 + for _, to := range g { + m += len(to) + } + return m +} + +// BoundsOk validates that all arcs in g stay within the slice bounds of g. +// +// BoundsOk returns true when no arcs point outside the bounds of g. +// Otherwise it returns false and an example arc that points outside of g. +// +// Most methods of this package assume the BoundsOk condition and may +// panic when they encounter an arc pointing outside of the graph. This +// function can be used to validate a graph when the BoundsOk condition +// is unknown. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) BoundsOk() (ok bool, fr NI, to NI) { + for fr, to := range g { + for _, to := range to { + if to < 0 || to >= NI(len(g)) { + return false, NI(fr), to + } + } + } + return true, -1, to +} + +// BreadthFirst traverses a directed or undirected graph in breadth +// first order. +// +// Traversal starts at node start and visits the nodes reachable from +// start. The function visit is called for each node visited. Nodes +// not reachable from start are not visited. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also alt.BreadthFirst, a variant with more options, and +// alt.BreadthFirst2, a direction optimizing variant. +func (g AdjacencyList) BreadthFirst(start NI, visit func(NI)) { + v := bits.New(len(g)) + v.SetBit(int(start), 1) + visit(start) + var next []NI + for frontier := []NI{start}; len(frontier) > 0; { + for _, n := range frontier { + for _, nb := range g[n] { + if v.Bit(int(nb)) == 0 { + v.SetBit(int(nb), 1) + visit(nb) + next = append(next, nb) + } + } + } + frontier, next = next, frontier[:0] + } +} + +// Copy makes a deep copy of g. +// Copy also computes the arc size ma, the number of arcs. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) Copy() (c AdjacencyList, ma int) { + c = make(AdjacencyList, len(g)) + for n, to := range g { + c[n] = append([]NI{}, to...) + ma += len(to) + } + return +} + +// DepthFirst traverses a directed or undirected graph in depth +// first order. +// +// Traversal starts at node start and visits the nodes reachable from +// start. The function visit is called for each node visited. Nodes +// not reachable from start are not visited. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also alt.DepthFirst, a variant with more options. +func (g AdjacencyList) DepthFirst(start NI, visit func(NI)) { + v := bits.New(len(g)) + var f func(NI) + f = func(n NI) { + visit(n) + v.SetBit(int(n), 1) + for _, to := range g[n] { + if v.Bit(int(to)) == 0 { + f(to) + } + } + } + f(start) +} + +// HasArc returns true if g has any arc from node `fr` to node `to`. +// +// Also returned is the index within the slice of arcs from node `fr`. +// If no arc from `fr` to `to` is present, HasArc returns false, -1. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also the method ParallelArcs, which finds all parallel arcs from +// `fr` to `to`. +func (g AdjacencyList) HasArc(fr, to NI) (bool, int) { + for x, h := range g[fr] { + if h == to { + return true, x + } + } + return false, -1 +} + +// AnyLoop identifies if a graph contains a loop, an arc that leads from a +// a node back to the same node. +// +// If g contains a loop, the method returns true and an example of a node +// with a loop. If there are no loops in g, the method returns false, -1. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) AnyLoop() (bool, NI) { + for fr, to := range g { + for _, to := range to { + if NI(fr) == to { + return true, to + } + } + } + return false, -1 +} + +// AddNode maps a node in a supergraph to a subgraph node. +// +// Argument p must be an NI in supergraph s.Super. AddNode panics if +// p is not a valid node index of s.Super. +// +// AddNode is idempotent in that it does not add a new node to the subgraph if +// a subgraph node already exists mapped to supergraph node p. +// +// The mapped subgraph NI is returned. +func (s *Subgraph) AddNode(p NI) (b NI) { + if int(p) < 0 || int(p) >= s.Super.Order() { + panic(fmt.Sprint("AddNode: NI ", p, " not in supergraph")) + } + if b, ok := s.SubNI[p]; ok { + return b + } + a := s.AdjacencyList + b = NI(len(a)) + s.AdjacencyList = append(a, nil) + s.SuperNI = append(s.SuperNI, p) + s.SubNI[p] = b + return +} + +// AddArc adds an arc to a subgraph. +// +// Arguments fr, to must be NIs in supergraph s.Super. As with AddNode, +// AddArc panics if fr and to are not valid node indexes of s.Super. +// +// The arc specfied by fr, to must exist in s.Super. Further, the number of +// parallel arcs in the subgraph cannot exceed the number of corresponding +// parallel arcs in the supergraph. That is, each arc already added to the +// subgraph counts against the arcs available in the supergraph. If a matching +// arc is not available, AddArc returns an error. +// +// If a matching arc is available, subgraph nodes are added as needed, the +// subgraph arc is added, and the method returns nil. +func (s *Subgraph) AddArc(fr NI, to NI) error { + // verify supergraph NIs first, but without adding subgraph nodes just yet. + if int(fr) < 0 || int(fr) >= s.Super.Order() { + panic(fmt.Sprint("AddArc: NI ", fr, " not in supergraph")) + } + if int(to) < 0 || int(to) >= s.Super.Order() { + panic(fmt.Sprint("AddArc: NI ", to, " not in supergraph")) + } + // count existing matching arcs in subgraph + n := 0 + a := s.AdjacencyList + if bf, ok := s.SubNI[fr]; ok { + if bt, ok := s.SubNI[to]; ok { + // both NIs already exist in subgraph, need to count arcs + bTo := to + bTo = bt + for _, t := range a[bf] { + if t == bTo { + n++ + } + } + } + } + // verify matching arcs are available in supergraph + for _, t := range (*s.Super)[fr] { + if t == to { + if n > 0 { + n-- // match existing arc + continue + } + // no more existing arcs need to be matched. nodes can finally + // be added as needed and then the arc can be added. + bf := s.AddNode(fr) + to = s.AddNode(to) + s.AdjacencyList[bf] = append(s.AdjacencyList[bf], to) + return nil // success + } + } + return errors.New("arc not available in supergraph") +} + +func (super AdjacencyList) induceArcs(sub map[NI]NI, sup []NI) AdjacencyList { + s := make(AdjacencyList, len(sup)) + for b, p := range sup { + var a []NI + for _, to := range super[p] { + if bt, ok := sub[to]; ok { + to = bt + a = append(a, to) + } + } + s[b] = a + } + return s +} + +// InduceList constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument l must be a list of +// NIs in receiver graph g. Receiver g becomes the supergraph of the induced +// subgraph. +// +// Duplicate NIs are allowed in list l. The duplicates are effectively removed +// and only a single corresponding node is created in the subgraph. Subgraph +// NIs are mapped in the order of list l, execpt for ignoring duplicates. +// NIs in l that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *AdjacencyList) InduceList(l []NI) *Subgraph { + sub, sup := mapList(l) + return &Subgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + + AdjacencyList: g.induceArcs(sub, sup)} +} + +// InduceBits constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument t must be a bitmap +// representing NIs in receiver graph g. Receiver g becomes the supergraph +// of the induced subgraph. NIs in t that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *AdjacencyList) InduceBits(t bits.Bits) *Subgraph { + sub, sup := mapBits(t) + return &Subgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + + AdjacencyList: g.induceArcs(sub, sup)} +} + +// IsSimple checks for loops and parallel arcs. +// +// A graph is "simple" if it has no loops or parallel arcs. +// +// IsSimple returns true, -1 for simple graphs. If a loop or parallel arc is +// found, simple returns false and a node that represents a counterexample +// to the graph being simple. +// +// See also separate methods AnyLoop and AnyParallel. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) IsSimple() (ok bool, n NI) { + if lp, n := g.AnyLoop(); lp { + return false, n + } + if pa, n, _ := g.AnyParallel(); pa { + return false, n + } + return true, -1 +} + +// IsolatedNodes returns a bitmap of isolated nodes in receiver graph g. +// +// An isolated node is one with no arcs going to or from it. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) IsolatedNodes() (i bits.Bits) { + i = bits.New(len(g)) + i.SetAll() + for fr, to := range g { + if len(to) > 0 { + i.SetBit(fr, 0) + for _, to := range to { + i.SetBit(int(to), 0) + } + } + } + return +} + +// Order is the number of nodes in receiver g. +// +// It is simply a wrapper method for the Go builtin len(). +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) Order() int { + // Why a wrapper for len()? Mostly for Directed and Undirected. + // u.Order() is a little nicer than len(u.LabeledAdjacencyList). + return len(g) +} + +// ParallelArcs identifies all arcs from node `fr` to node `to`. +// +// The returned slice contains an element for each arc from node `fr` to node `to`. +// The element value is the index within the slice of arcs from node `fr`. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also the method HasArc, which stops after finding a single arc. +func (g AdjacencyList) ParallelArcs(fr, to NI) (p []int) { + for x, h := range g[fr] { + if h == to { + p = append(p, x) + } + } + return +} + +// Permute permutes the node labeling of receiver g. +// +// Argument p must be a permutation of the node numbers of the graph, +// 0 through len(g)-1. A permutation returned by rand.Perm(len(g)) for +// example is acceptable. +// +// The graph is permuted in place. The graph keeps the same underlying +// memory but values of the graph representation are permuted to produce +// an isomorphic graph. The node previously labeled 0 becomes p[0] and so on. +// See example (or the code) for clarification. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) Permute(p []int) { + old := append(AdjacencyList{}, g...) // shallow copy + for fr, arcs := range old { + for i, to := range arcs { + arcs[i] = NI(p[to]) + } + g[p[fr]] = arcs + } +} + +// ShuffleArcLists shuffles the arc lists of each node of receiver g. +// +// For example a node with arcs leading to nodes 3 and 7 might have an +// arc list of either [3 7] or [7 3] after calling this method. The +// connectivity of the graph is not changed. The resulting graph stays +// equivalent but a traversal will encounter arcs in a different +// order. +// +// If Rand r is nil, the rand package default shared source is used. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g AdjacencyList) ShuffleArcLists(r *rand.Rand) { + ri := rand.Intn + if r != nil { + ri = r.Intn + } + // Knuth-Fisher-Yates + for _, to := range g { + for i := len(to); i > 1; { + j := ri(i) + i-- + to[i], to[j] = to[j], to[i] + } + } +} diff --git a/vendor/github.com/soniakeys/graph/adj_cg.go b/vendor/github.com/soniakeys/graph/adj_cg.go new file mode 100644 index 00000000..796ced65 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/adj_cg.go @@ -0,0 +1,417 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +// adj_RO.go is code generated from adj_cg.go by directives in graph.go. +// Editing adj_cg.go is okay. +// DO NOT EDIT adj_RO.go. The RO is for Read Only. + +import ( + "errors" + "fmt" + "math/rand" + + "github.com/soniakeys/bits" +) + +// ArcDensity returns density for an simple directed graph. +// +// See also ArcDensity function. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) ArcDensity() float64 { + return ArcDensity(len(g), g.ArcSize()) +} + +// ArcSize returns the number of arcs in g. +// +// Note that for an undirected graph without loops, the number of undirected +// edges -- the traditional meaning of graph size -- will be ArcSize()/2. +// On the other hand, if g is an undirected graph that has or may have loops, +// g.ArcSize()/2 is not a meaningful quantity. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) ArcSize() int { + m := 0 + for _, to := range g { + m += len(to) + } + return m +} + +// BoundsOk validates that all arcs in g stay within the slice bounds of g. +// +// BoundsOk returns true when no arcs point outside the bounds of g. +// Otherwise it returns false and an example arc that points outside of g. +// +// Most methods of this package assume the BoundsOk condition and may +// panic when they encounter an arc pointing outside of the graph. This +// function can be used to validate a graph when the BoundsOk condition +// is unknown. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) BoundsOk() (ok bool, fr NI, to Half) { + for fr, to := range g { + for _, to := range to { + if to.To < 0 || to.To >= NI(len(g)) { + return false, NI(fr), to + } + } + } + return true, -1, to +} + +// BreadthFirst traverses a directed or undirected graph in breadth +// first order. +// +// Traversal starts at node start and visits the nodes reachable from +// start. The function visit is called for each node visited. Nodes +// not reachable from start are not visited. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also alt.BreadthFirst, a variant with more options, and +// alt.BreadthFirst2, a direction optimizing variant. +func (g LabeledAdjacencyList) BreadthFirst(start NI, visit func(NI)) { + v := bits.New(len(g)) + v.SetBit(int(start), 1) + visit(start) + var next []NI + for frontier := []NI{start}; len(frontier) > 0; { + for _, n := range frontier { + for _, nb := range g[n] { + if v.Bit(int(nb.To)) == 0 { + v.SetBit(int(nb.To), 1) + visit(nb.To) + next = append(next, nb.To) + } + } + } + frontier, next = next, frontier[:0] + } +} + +// Copy makes a deep copy of g. +// Copy also computes the arc size ma, the number of arcs. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) Copy() (c LabeledAdjacencyList, ma int) { + c = make(LabeledAdjacencyList, len(g)) + for n, to := range g { + c[n] = append([]Half{}, to...) + ma += len(to) + } + return +} + +// DepthFirst traverses a directed or undirected graph in depth +// first order. +// +// Traversal starts at node start and visits the nodes reachable from +// start. The function visit is called for each node visited. Nodes +// not reachable from start are not visited. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also alt.DepthFirst, a variant with more options. +func (g LabeledAdjacencyList) DepthFirst(start NI, visit func(NI)) { + v := bits.New(len(g)) + var f func(NI) + f = func(n NI) { + visit(n) + v.SetBit(int(n), 1) + for _, to := range g[n] { + if v.Bit(int(to.To)) == 0 { + f(to.To) + } + } + } + f(start) +} + +// HasArc returns true if g has any arc from node `fr` to node `to`. +// +// Also returned is the index within the slice of arcs from node `fr`. +// If no arc from `fr` to `to` is present, HasArc returns false, -1. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also the method ParallelArcs, which finds all parallel arcs from +// `fr` to `to`. +func (g LabeledAdjacencyList) HasArc(fr, to NI) (bool, int) { + for x, h := range g[fr] { + if h.To == to { + return true, x + } + } + return false, -1 +} + +// AnyLoop identifies if a graph contains a loop, an arc that leads from a +// a node back to the same node. +// +// If g contains a loop, the method returns true and an example of a node +// with a loop. If there are no loops in g, the method returns false, -1. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) AnyLoop() (bool, NI) { + for fr, to := range g { + for _, to := range to { + if NI(fr) == to.To { + return true, to.To + } + } + } + return false, -1 +} + +// AddNode maps a node in a supergraph to a subgraph node. +// +// Argument p must be an NI in supergraph s.Super. AddNode panics if +// p is not a valid node index of s.Super. +// +// AddNode is idempotent in that it does not add a new node to the subgraph if +// a subgraph node already exists mapped to supergraph node p. +// +// The mapped subgraph NI is returned. +func (s *LabeledSubgraph) AddNode(p NI) (b NI) { + if int(p) < 0 || int(p) >= s.Super.Order() { + panic(fmt.Sprint("AddNode: NI ", p, " not in supergraph")) + } + if b, ok := s.SubNI[p]; ok { + return b + } + a := s.LabeledAdjacencyList + b = NI(len(a)) + s.LabeledAdjacencyList = append(a, nil) + s.SuperNI = append(s.SuperNI, p) + s.SubNI[p] = b + return +} + +// AddArc adds an arc to a subgraph. +// +// Arguments fr, to must be NIs in supergraph s.Super. As with AddNode, +// AddArc panics if fr and to are not valid node indexes of s.Super. +// +// The arc specfied by fr, to must exist in s.Super. Further, the number of +// parallel arcs in the subgraph cannot exceed the number of corresponding +// parallel arcs in the supergraph. That is, each arc already added to the +// subgraph counts against the arcs available in the supergraph. If a matching +// arc is not available, AddArc returns an error. +// +// If a matching arc is available, subgraph nodes are added as needed, the +// subgraph arc is added, and the method returns nil. +func (s *LabeledSubgraph) AddArc(fr NI, to Half) error { + // verify supergraph NIs first, but without adding subgraph nodes just yet. + if int(fr) < 0 || int(fr) >= s.Super.Order() { + panic(fmt.Sprint("AddArc: NI ", fr, " not in supergraph")) + } + if int(to.To) < 0 || int(to.To) >= s.Super.Order() { + panic(fmt.Sprint("AddArc: NI ", to.To, " not in supergraph")) + } + // count existing matching arcs in subgraph + n := 0 + a := s.LabeledAdjacencyList + if bf, ok := s.SubNI[fr]; ok { + if bt, ok := s.SubNI[to.To]; ok { + // both NIs already exist in subgraph, need to count arcs + bTo := to + bTo.To = bt + for _, t := range a[bf] { + if t == bTo { + n++ + } + } + } + } + // verify matching arcs are available in supergraph + for _, t := range (*s.Super)[fr] { + if t == to { + if n > 0 { + n-- // match existing arc + continue + } + // no more existing arcs need to be matched. nodes can finally + // be added as needed and then the arc can be added. + bf := s.AddNode(fr) + to.To = s.AddNode(to.To) + s.LabeledAdjacencyList[bf] = append(s.LabeledAdjacencyList[bf], to) + return nil // success + } + } + return errors.New("arc not available in supergraph") +} + +func (super LabeledAdjacencyList) induceArcs(sub map[NI]NI, sup []NI) LabeledAdjacencyList { + s := make(LabeledAdjacencyList, len(sup)) + for b, p := range sup { + var a []Half + for _, to := range super[p] { + if bt, ok := sub[to.To]; ok { + to.To = bt + a = append(a, to) + } + } + s[b] = a + } + return s +} + +// InduceList constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument l must be a list of +// NIs in receiver graph g. Receiver g becomes the supergraph of the induced +// subgraph. +// +// Duplicate NIs are allowed in list l. The duplicates are effectively removed +// and only a single corresponding node is created in the subgraph. Subgraph +// NIs are mapped in the order of list l, execpt for ignoring duplicates. +// NIs in l that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *LabeledAdjacencyList) InduceList(l []NI) *LabeledSubgraph { + sub, sup := mapList(l) + return &LabeledSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + + LabeledAdjacencyList: g.induceArcs(sub, sup)} +} + +// InduceBits constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument t must be a bitmap +// representing NIs in receiver graph g. Receiver g becomes the supergraph +// of the induced subgraph. NIs in t that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *LabeledAdjacencyList) InduceBits(t bits.Bits) *LabeledSubgraph { + sub, sup := mapBits(t) + return &LabeledSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + + LabeledAdjacencyList: g.induceArcs(sub, sup)} +} + +// IsSimple checks for loops and parallel arcs. +// +// A graph is "simple" if it has no loops or parallel arcs. +// +// IsSimple returns true, -1 for simple graphs. If a loop or parallel arc is +// found, simple returns false and a node that represents a counterexample +// to the graph being simple. +// +// See also separate methods AnyLoop and AnyParallel. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) IsSimple() (ok bool, n NI) { + if lp, n := g.AnyLoop(); lp { + return false, n + } + if pa, n, _ := g.AnyParallel(); pa { + return false, n + } + return true, -1 +} + +// IsolatedNodes returns a bitmap of isolated nodes in receiver graph g. +// +// An isolated node is one with no arcs going to or from it. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) IsolatedNodes() (i bits.Bits) { + i = bits.New(len(g)) + i.SetAll() + for fr, to := range g { + if len(to) > 0 { + i.SetBit(fr, 0) + for _, to := range to { + i.SetBit(int(to.To), 0) + } + } + } + return +} + +// Order is the number of nodes in receiver g. +// +// It is simply a wrapper method for the Go builtin len(). +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) Order() int { + // Why a wrapper for len()? Mostly for Directed and Undirected. + // u.Order() is a little nicer than len(u.LabeledAdjacencyList). + return len(g) +} + +// ParallelArcs identifies all arcs from node `fr` to node `to`. +// +// The returned slice contains an element for each arc from node `fr` to node `to`. +// The element value is the index within the slice of arcs from node `fr`. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also the method HasArc, which stops after finding a single arc. +func (g LabeledAdjacencyList) ParallelArcs(fr, to NI) (p []int) { + for x, h := range g[fr] { + if h.To == to { + p = append(p, x) + } + } + return +} + +// Permute permutes the node labeling of receiver g. +// +// Argument p must be a permutation of the node numbers of the graph, +// 0 through len(g)-1. A permutation returned by rand.Perm(len(g)) for +// example is acceptable. +// +// The graph is permuted in place. The graph keeps the same underlying +// memory but values of the graph representation are permuted to produce +// an isomorphic graph. The node previously labeled 0 becomes p[0] and so on. +// See example (or the code) for clarification. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) Permute(p []int) { + old := append(LabeledAdjacencyList{}, g...) // shallow copy + for fr, arcs := range old { + for i, to := range arcs { + arcs[i].To = NI(p[to.To]) + } + g[p[fr]] = arcs + } +} + +// ShuffleArcLists shuffles the arc lists of each node of receiver g. +// +// For example a node with arcs leading to nodes 3 and 7 might have an +// arc list of either [3 7] or [7 3] after calling this method. The +// connectivity of the graph is not changed. The resulting graph stays +// equivalent but a traversal will encounter arcs in a different +// order. +// +// If Rand r is nil, the rand package default shared source is used. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledAdjacencyList) ShuffleArcLists(r *rand.Rand) { + ri := rand.Intn + if r != nil { + ri = r.Intn + } + // Knuth-Fisher-Yates + for _, to := range g { + for i := len(to); i > 1; { + j := ri(i) + i-- + to[i], to[j] = to[j], to[i] + } + } +} diff --git a/vendor/github.com/soniakeys/graph/dir.go b/vendor/github.com/soniakeys/graph/dir.go new file mode 100644 index 00000000..a9ce5357 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/dir.go @@ -0,0 +1,1059 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +import ( + "math" +) + +// dir.go has methods specific to directed graphs, types Directed and +// LabeledDirected, also Dominators. +// +// Methods on Directed are first, with exported methods alphabetized. +// Dominators type and methods are at the end. +//---------------------------- + +// Cycles emits all elementary cycles in a directed graph. +// +// The algorithm here is Johnson's. See also the equivalent but generally +// slower alt.TarjanCycles. +func (g Directed) Cycles(emit func([]NI) bool) { + // Johnsons "Finding all the elementary circuits of a directed graph", + // SIAM J. Comput. Vol. 4, No. 1, March 1975. + a := g.AdjacencyList + k := make(AdjacencyList, len(a)) + B := make([]map[NI]bool, len(a)) + blocked := make([]bool, len(a)) + for i := range a { + blocked[i] = true + B[i] = map[NI]bool{} + } + var s NI + var stack []NI + var unblock func(NI) + unblock = func(u NI) { + blocked[u] = false + for w := range B[u] { + delete(B[u], w) + if blocked[w] { + unblock(w) + } + } + } + var circuit func(NI) (bool, bool) + circuit = func(v NI) (found, ok bool) { + f := false + stack = append(stack, v) + blocked[v] = true + for _, w := range k[v] { + if w == s { + if !emit(stack) { + return + } + f = true + } else if !blocked[w] { + switch found, ok = circuit(w); { + case !ok: + return + case found: + f = true + } + } + } + if f { + unblock(v) + } else { + for _, w := range k[v] { + B[w][v] = true + } + } + stack = stack[:len(stack)-1] + return f, true + } + for s = 0; int(s) < len(a); s++ { + // (so there's a little extra n^2 component introduced here that + // comes from not making a proper subgraph but just removing arcs + // and leaving isolated nodes. Iterating over the isolated nodes + // should be very fast though. It seems like it would be a net win + // over creating a subgraph.) + // shallow subgraph + for z := NI(0); z < s; z++ { + k[z] = nil + } + for z := int(s); z < len(a); z++ { + k[z] = a[z] + } + // find scc in k with s + var scc []NI + Directed{k}.StronglyConnectedComponents(func(c []NI) bool { + for _, n := range c { + if n == s { // this is it + scc = c + return false // stop scc search + } + } + return true // keep looking + }) + // clear k + for n := range k { + k[n] = nil + } + // map component + for _, n := range scc { + blocked[n] = false + } + // copy component to k + for _, fr := range scc { + var kt []NI + for _, to := range a[fr] { + if !blocked[to] { + kt = append(kt, to) + } + } + k[fr] = kt + } + if _, ok := circuit(s); !ok { + return + } + // reblock component + for _, n := range scc { + blocked[n] = true + } + } +} + +// DAGMaxLenPath finds a maximum length path in a directed acyclic graph. +// +// Argument ordering must be a topological ordering of g. +func (g Directed) DAGMaxLenPath(ordering []NI) (path []NI) { + // dynamic programming. visit nodes in reverse order. for each, compute + // longest path as one plus longest of 'to' nodes. + // Visits each arc once. O(m). + // + // Similar code in label.go + var n NI + mlp := make([][]NI, g.Order()) // index by node number + for i := len(ordering) - 1; i >= 0; i-- { + fr := ordering[i] // node number + to := g.AdjacencyList[fr] + if len(to) == 0 { + continue + } + mt := to[0] + for _, to := range to[1:] { + if len(mlp[to]) > len(mlp[mt]) { + mt = to + } + } + p := append([]NI{mt}, mlp[mt]...) + mlp[fr] = p + if len(p) > len(path) { + n = fr + path = p + } + } + return append([]NI{n}, path...) +} + +// FromList creates a spanning forest of a graph. +// +// The method populates the From members in f.Paths and returns the FromList. +// Also returned is a bool, true if the receiver is found to be a simple graph +// representing a tree or forest. Loops, or any case of multiple arcs going to +// a node will cause simpleForest to be false. +// +// The FromList return value f will always be a spanning forest of the entire +// graph. The bool return value simpleForest tells if the receiver graph g +// was a simple forest to begin with. +// +// Other members of the FromList are left as zero values. +// Use FromList.RecalcLen and FromList.RecalcLeaves as needed. +func (g Directed) FromList() (f *FromList, simpleForest bool) { + paths := make([]PathEnd, g.Order()) + for i := range paths { + paths[i].From = -1 + } + simpleForest = true + for fr, to := range g.AdjacencyList { + for _, to := range to { + if int(to) == fr || paths[to].From >= 0 { + simpleForest = false + } else { + paths[to].From = NI(fr) + } + } + } + return &FromList{Paths: paths}, simpleForest +} + +// SpanTree builds a tree spanning nodes reachable from the given root. +// +// The component is spanned by breadth-first search from root. +// The resulting spanning tree in stored a FromList. +// +// If FromList.Paths is not the same length as g, it is allocated and +// initialized. This allows a zero value FromList to be passed as f. +// If FromList.Paths is the same length as g, it is used as is and is not +// reinitialized. This allows multiple trees to be spanned in the same +// FromList with successive calls. +// +// For nodes spanned, the Path member of the returned FromList is populated +// with both From and Len values. The MaxLen member will be updated but +// not Leaves. +// +// Returned is the number of nodes spanned, which will be the number of nodes +// reachable from root, and a bool indicating if these nodes were found to be +// a simply connected tree in the receiver graph g. Any cycles, loops, +// or parallel arcs in the component will cause simpleTree to be false, but +// FromList f will still be populated with a valid spanning tree. +func (g Directed) SpanTree(root NI, f *FromList) (nSpanned int, simpleTree bool) { + a := g.AdjacencyList + p := f.Paths + if len(p) != len(a) { + p = make([]PathEnd, len(a)) + for i := range p { + p[i].From = -1 + } + f.Paths = p + } + simpleTree = true + p[root].Len = 1 + type arc struct { + from, to NI + } + var next []arc + frontier := []arc{{-1, root}} + for len(frontier) > 0 { + for _, fa := range frontier { // fa frontier arc + nSpanned++ + l := p[fa.to].Len + 1 + for _, to := range a[fa.to] { + if p[to].Len > 0 { + simpleTree = false + continue + } + p[to] = PathEnd{From: fa.to, Len: l} + if l > f.MaxLen { + f.MaxLen = l + } + next = append(next, arc{fa.to, to}) + } + } + frontier, next = next, frontier[:0] + } + return +} + +// Undirected returns copy of g augmented as needed to make it undirected. +func (g Directed) Undirected() Undirected { + c, _ := g.AdjacencyList.Copy() // start with a copy + rw := make(AdjacencyList, g.Order()) // "reciprocals wanted" + for fr, to := range g.AdjacencyList { + arc: // for each arc in g + for _, to := range to { + if to == NI(fr) { + continue // loop + } + // search wanted arcs + wf := rw[fr] + for i, w := range wf { + if w == to { // found, remove + last := len(wf) - 1 + wf[i] = wf[last] + rw[fr] = wf[:last] + continue arc + } + } + // arc not found, add to reciprocal to wanted list + rw[to] = append(rw[to], NI(fr)) + } + } + // add missing reciprocals + for fr, to := range rw { + c[fr] = append(c[fr], to...) + } + return Undirected{c} +} + +// Transpose constructs a new adjacency list with all arcs reversed. +// +// For every arc from->to of g, the result will have an arc to->from. +// Transpose also counts arcs as it traverses and returns ma the number of arcs +// in g (equal to the number of arcs in the result.) +func (g Directed) Transpose() (t Directed, ma int) { + ta := make(AdjacencyList, g.Order()) + for n, nbs := range g.AdjacencyList { + for _, nb := range nbs { + ta[nb] = append(ta[nb], NI(n)) + ma++ + } + } + return Directed{ta}, ma +} + +// Cycles emits all elementary cycles in a directed graph. +// +// The algorithm here is Johnson's. See also the equivalent but generally +// slower alt.TarjanCycles. +func (g LabeledDirected) Cycles(emit func([]Half) bool) { + a := g.LabeledAdjacencyList + k := make(LabeledAdjacencyList, len(a)) + B := make([]map[NI]bool, len(a)) + blocked := make([]bool, len(a)) + for i := range a { + blocked[i] = true + B[i] = map[NI]bool{} + } + var s NI + var stack []Half + var unblock func(NI) + unblock = func(u NI) { + blocked[u] = false + for w := range B[u] { + delete(B[u], w) + if blocked[w] { + unblock(w) + } + } + } + var circuit func(NI) (bool, bool) + circuit = func(v NI) (found, ok bool) { + f := false + blocked[v] = true + for _, w := range k[v] { + if w.To == s { + if !emit(append(stack, w)) { + return + } + f = true + } else if !blocked[w.To] { + stack = append(stack, w) + switch found, ok = circuit(w.To); { + case !ok: + return + case found: + f = true + } + stack = stack[:len(stack)-1] + } + } + if f { + unblock(v) + } else { + for _, w := range k[v] { + B[w.To][v] = true + } + } + return f, true + } + for s = 0; int(s) < len(a); s++ { + for z := NI(0); z < s; z++ { + k[z] = nil + } + for z := int(s); z < len(a); z++ { + k[z] = a[z] + } + var scc []NI + LabeledDirected{k}.StronglyConnectedComponents(func(c []NI) bool { + for _, n := range c { + if n == s { + scc = c + return false + } + } + return true + }) + for n := range k { + k[n] = nil + } + for _, n := range scc { + blocked[n] = false + } + for _, fr := range scc { + var kt []Half + for _, to := range a[fr] { + if !blocked[to.To] { + kt = append(kt, to) + } + } + k[fr] = kt + } + if _, ok := circuit(s); !ok { + return + } + for _, n := range scc { + blocked[n] = true + } + } +} + +// DAGMaxLenPath finds a maximum length path in a directed acyclic graph. +// +// Length here means number of nodes or arcs, not a sum of arc weights. +// +// Argument ordering must be a topological ordering of g. +// +// Returned is a node beginning a maximum length path, and a path of arcs +// starting from that node. +func (g LabeledDirected) DAGMaxLenPath(ordering []NI) (n NI, path []Half) { + // dynamic programming. visit nodes in reverse order. for each, compute + // longest path as one plus longest of 'to' nodes. + // Visits each arc once. Time complexity O(m). + // + // Similar code in dir.go. + mlp := make([][]Half, g.Order()) // index by node number + for i := len(ordering) - 1; i >= 0; i-- { + fr := ordering[i] // node number + to := g.LabeledAdjacencyList[fr] + if len(to) == 0 { + continue + } + mt := to[0] + for _, to := range to[1:] { + if len(mlp[to.To]) > len(mlp[mt.To]) { + mt = to + } + } + p := append([]Half{mt}, mlp[mt.To]...) + mlp[fr] = p + if len(p) > len(path) { + n = fr + path = p + } + } + return +} + +// FromList creates a spanning forest of a graph. +// +// The method populates the From members in f.Paths and returns the FromList. +// Also returned is a list of labels corresponding to the from arcs, and a +// bool, true if the receiver is found to be a simple graph representing +// a tree or forest. Loops, or any case of multiple arcs going to a node +// will cause simpleForest to be false. +// +// The FromList return value f will always be a spanning forest of the entire +// graph. The bool return value simpleForest tells if the receiver graph g +// was a simple forest to begin with. +// +// Other members of the FromList are left as zero values. +// Use FromList.RecalcLen and FromList.RecalcLeaves as needed. +func (g LabeledDirected) FromList() (f *FromList, labels []LI, simpleForest bool) { + labels = make([]LI, g.Order()) + paths := make([]PathEnd, g.Order()) + for i := range paths { + paths[i].From = -1 + } + simpleForest = true + for fr, to := range g.LabeledAdjacencyList { + for _, to := range to { + if int(to.To) == fr || paths[to.To].From >= 0 { + simpleForest = false + } else { + paths[to.To].From = NI(fr) + labels[to.To] = to.Label + } + } + } + return &FromList{Paths: paths}, labels, simpleForest +} + +// NegativeCycles emits all cycles with negative cycle distance. +// +// The emit function is called for each cycle found. Emit must return true +// to continue cycle enumeration. If emit returns false, NegativeCycles +// stops and returns immediately. +// +// The method mutates receiver g while it runs. Access to g before +// NegativeCycles returns, such as during the emit callback, will find +// g altered. G is completely restored when NegativeCycles returns however, +// even if terminated early with a false return from emit. +// +// If mutations on g are unacceptable, use g.Copy and run NegativeCycles on +// the copy. +// +// See also: +// +// * NegativeCycle, which finds a single example of a negative cycle if +// one exists. +// +// * HasNegativeCycle, which detects if a negative cycle exists. +// +// * BellmanFord, which also detects negative cycles. +// +// * Cycles, from which negative cycles can be filtered. +// +// * alt.NegativeCycles, which uses less memory but is generally slower. +func (g LabeledDirected) NegativeCycles(w WeightFunc, emit func([]Half) bool) { + // Implementation of "Finding all the negative cycles in a directed graph" + // by Takeo Yamada and Harunobu Kinoshita, Discrete Applied Mathematics + // 118 (2002) 279–291. + newNegCyc(g, w, emit).all_nc(LabeledPath{}) +} + +type negCyc struct { + g LabeledDirected + w WeightFunc + emit func([]Half) bool + a LabeledAdjacencyList + tr AdjacencyList + d0, d1 []float64 + dc []float64 + bt [][]fromHalf + btLast []int +} + +func newNegCyc(g LabeledDirected, w WeightFunc, emit func([]Half) bool) *negCyc { + nc := &negCyc{g: g, w: w, emit: emit} + nc.a = g.LabeledAdjacencyList + // transpose to make it easier to find from-arcs. + lt, _ := g.UnlabeledTranspose() + nc.tr = lt.AdjacencyList + nc.d0 = make([]float64, len(nc.a)) + nc.d1 = make([]float64, len(nc.a)) + nc.dc = make([]float64, len(nc.a)) + nc.btLast = make([]int, len(nc.a)) + nc.bt = make([][]fromHalf, len(nc.a)) + for i := range nc.bt { + nc.bt[i] = make([]fromHalf, len(nc.a)) + } + return nc +} + +func (nc *negCyc) all_nc(F LabeledPath) bool { + var C []Half + var R LabeledPath + // Step 1 + if len(F.Path) != 0 { + return nc.step2(F) + } + C = nc.g.NegativeCycle(nc.w) + if len(C) == 0 { + return true + } + // prep step 4 with no F: + F.Start = C[len(C)-1].To + R = LabeledPath{F.Start, C} + return nc.step4(C, F, R) +} + +func (nc *negCyc) step2(F LabeledPath) bool { + fEnd := F.Path[len(F.Path)-1].To + wF := F.Distance(nc.w) + dL, πL := nc.zL(F, fEnd, wF) + if !(dL < 0) { + return true + } + if len(πL) > 0 { + C := append(F.Path, πL...) + R := LabeledPath{fEnd, πL} + return nc.step4(C, F, R) + } + return nc.step3(F, fEnd, wF) +} + +func (nc *negCyc) step3(F LabeledPath, fEnd NI, wF float64) bool { + πΓ := nc.zΓ(F, wF) + if len(πΓ) > 0 { + // prep for step 4 + C := append(F.Path, πΓ...) + R := LabeledPath{fEnd, πΓ} + return nc.step4(C, F, R) + } + return nc.step5(F, fEnd) +} + +func (nc *negCyc) step4(C []Half, F, R LabeledPath) (ok bool) { + // C is a new cycle. + // F is fixed path to be extended and is a prefix of C. + // R is the remainder of C + if ok = nc.emit(C); !ok { + return + } + // for each arc in R, if not the first arc, + // extend F by the arc of the previous iteration. + // remove arc from g, + // Then make the recursive call, then put the arc back in g. + // + // after loop, replace arcs from the two stacks. + type frto struct { + fr NI + to []Half + } + var frStack [][]arc + var toStack []frto + var fr0 NI + var to0 Half + for i, h := range R.Path { + if i > 0 { + // extend F by arc {fr0 to0}, the arc of the previous iteration. + // Remove arcs to to0.To and save on stack. + // Remove arcs from arc0.fr and save on stack. + F.Path = append(F.Path, to0) + frStack = append(frStack, nc.cutTo(to0.To)) + toStack = append(toStack, frto{fr0, nc.a[fr0]}) + nc.a[fr0] = nil + } + toList := nc.a[R.Start] + for j, to := range toList { + if to == h { + last := len(toList) - 1 + toList[j], toList[last] = toList[last], toList[j] + nc.a[R.Start] = toList[:last] + ok = nc.all_nc(F) // return value + toList[last], toList[j] = toList[j], toList[last] + nc.a[R.Start] = toList + break + } + } + if !ok { + break + } + fr0 = R.Start + to0 = h + R.Start = h.To + } + for i := len(frStack) - 1; i >= 0; i-- { + nc.a[toStack[i].fr] = toStack[i].to + nc.restore(frStack[i]) + } + return +} + +func (nc *negCyc) step5(F LabeledPath, fEnd NI) (ok bool) { + // Step 5 (uncertain case) + // + // For each arc from end of F, search each case of extending + // F by that arc. + // + // before loop: save arcs from current path end, + // replace them with room for a single arc. + // extend F by room for one more arc, + ok = true + save := nc.a[fEnd] + nc.a[fEnd] = []Half{{}} + last := len(F.Path) + F.Path = append(F.Path, Half{}) + for _, h := range save { + // in each iteration, set the final arc in F, and the single + // outgoing arc, and save and clear all inbound arcs to the + // new end node. make recursive call, then restore saved + // inbound arcs for the node. + F.Path[last] = h + nc.a[fEnd][0] = h + save := nc.cutTo(h.To) + ok = nc.all_nc(F) + nc.restore(save) + if !ok { + break + } + } + // after loop, restore saved outgoing arcs in g. + nc.a[fEnd] = save + return +} + +type arc struct { + n NI // node that had an arc cut from its toList + x int // index of arc that was swapped to the end of the list +} + +// modify a cutting all arcs to node n. return list of cut arcs than +// can be processed in reverse order to restore changes to a +func (nc *negCyc) cutTo(n NI) (c []arc) { + for _, fr := range nc.tr[n] { + toList := nc.a[fr] + for x := 0; x < len(toList); { + to := toList[x] + if to.To == n { + c = append(c, arc{fr, x}) + last := len(toList) - 1 + toList[x], toList[last] = toList[last], toList[x] + toList = toList[:last] + } else { + x++ + } + } + nc.a[fr] = toList + } + return +} + +func (nc *negCyc) restore(c []arc) { + for i := len(c) - 1; i >= 0; i-- { + r := c[i] + toList := nc.a[r.n] + last := len(toList) + toList = toList[:last+1] + toList[r.x], toList[last] = toList[last], toList[r.x] + nc.a[r.n] = toList + } +} + +func (nc *negCyc) zL(F LabeledPath, fEnd NI, wp float64) (float64, []Half) { + π, c, d := nc.πj(len(nc.a)-len(F.Path), F.Start, fEnd) + if c < 0 { + return d + wp, π + } + j := len(nc.a) - len(F.Path) - 1 + // G1: cut arcs going to c + saveFr := nc.cutTo(c) + for k := 1; k <= j; k++ { + nc.dc[k] = nc.dj(k, F.Start, c) + } + // G0: also cut arcs coming from c + saveTo := nc.a[c] + nc.a[c] = nil + min := nc.dj(j, F.Start, fEnd) + // G2: restore arcs going to c + nc.restore(saveFr) + for k := 1; k <= j; k++ { + d1 := nc.dc[k] + nc.dj(j+1-k, c, fEnd) + if d1 < min { + min = d1 + } + } + nc.a[c] = saveTo + return min + wp, nil +} + +func (nc *negCyc) dj(j int, v, v0 NI) float64 { + for i := range nc.d0 { + nc.d0[i] = math.Inf(1) + } + nc.d0[v0] = 0 + for ; j > 0; j-- { + for i, d := range nc.d0 { + nc.d1[i] = d + } + for vʹ, d0vʹ := range nc.d0 { + if d0vʹ < math.Inf(1) { + for _, to := range nc.a[vʹ] { + if sum := d0vʹ + nc.w(to.Label); sum < nc.d1[to.To] { + nc.d1[to.To] = sum + } + } + } + } + nc.d0, nc.d1 = nc.d1, nc.d0 + } + return nc.d0[v] +} + +func (nc *negCyc) πj(j int, v, v0 NI) ([]Half, NI, float64) { + for i := range nc.d0 { + nc.d0[i] = math.Inf(1) + nc.btLast[i] = -1 + } + nc.d0[v0] = 0 + for k := 0; k < j; k++ { + for i, d := range nc.d0 { + nc.d1[i] = d + } + btk := nc.bt[k] + for vʹ, d0vʹ := range nc.d0 { + if d0vʹ < math.Inf(1) { + for _, to := range nc.a[vʹ] { + if sum := d0vʹ + nc.w(to.Label); sum < nc.d1[to.To] { + nc.d1[to.To] = sum + btk[to.To] = fromHalf{NI(vʹ), to.Label} + nc.btLast[to.To] = k + } + } + } + } + nc.d0, nc.d1 = nc.d1, nc.d0 + } + p := make([]Half, nc.btLast[v]+1) + m := map[NI]bool{} + c := NI(-1) + to := v + for k := nc.btLast[v]; k >= 0; k-- { + fh := nc.bt[k][to] + p[k] = Half{to, fh.Label} + to = fh.From + if c < 0 { + if m[to] { + c = to + } else { + m[to] = true + } + } + } + return p, c, nc.d0[v] +} + +func (nc *negCyc) zΓ(F LabeledPath, wp float64) []Half { + p, d := nc.a.DijkstraPath(F.Path[len(F.Path)-1].To, F.Start, nc.w) + if !(wp+d < 0) { + return nil + } + return p.Path +} + +// SpanTree builds a tree spanning nodes reachable from the given root. +// +// The component is spanned by breadth-first search from root. +// The resulting spanning tree in stored a FromList, and arc labels optionally +// stored in a slice. +// +// If FromList.Paths is not the same length as g, it is allocated and +// initialized. This allows a zero value FromList to be passed as f. +// If FromList.Paths is the same length as g, it is used as is and is not +// reinitialized. This allows multiple trees to be spanned in the same +// FromList with successive calls. +// +// For nodes spanned, the Path member of the returned FromList is populated +// with both From and Len values. The MaxLen member will be updated but +// not Leaves. +// +// The labels slice will be populated only if it is same length as g. +// Nil can be passed for example if labels are not needed. +// +// Returned is the number of nodes spanned, which will be the number of nodes +// reachable from root, and a bool indicating if these nodes were found to be +// a simply connected tree in the receiver graph g. Any cycles, loops, +// or parallel arcs in the component will cause simpleTree to be false, but +// FromList f will still be populated with a valid spanning tree. +func (g LabeledDirected) SpanTree(root NI, f *FromList, labels []LI) (nSpanned int, simpleTree bool) { + a := g.LabeledAdjacencyList + p := f.Paths + if len(p) != len(a) { + p = make([]PathEnd, len(a)) + for i := range p { + p[i].From = -1 + } + f.Paths = p + } + simpleTree = true + p[root].Len = 1 + type arc struct { + from NI + half Half + } + var next []arc + frontier := []arc{{-1, Half{root, -1}}} + for len(frontier) > 0 { + for _, fa := range frontier { // fa frontier arc + nSpanned++ + l := p[fa.half.To].Len + 1 + for _, to := range a[fa.half.To] { + if p[to.To].Len > 0 { + simpleTree = false + continue + } + p[to.To] = PathEnd{From: fa.half.To, Len: l} + if len(labels) == len(p) { + labels[to.To] = to.Label + } + if l > f.MaxLen { + f.MaxLen = l + } + next = append(next, arc{fa.half.To, to}) + } + } + frontier, next = next, frontier[:0] + } + return +} + +// Transpose constructs a new adjacency list that is the transpose of g. +// +// For every arc from->to of g, the result will have an arc to->from. +// Transpose also counts arcs as it traverses and returns ma the number of +// arcs in g (equal to the number of arcs in the result.) +func (g LabeledDirected) Transpose() (t LabeledDirected, ma int) { + ta := make(LabeledAdjacencyList, g.Order()) + for n, nbs := range g.LabeledAdjacencyList { + for _, nb := range nbs { + ta[nb.To] = append(ta[nb.To], Half{To: NI(n), Label: nb.Label}) + ma++ + } + } + return LabeledDirected{ta}, ma +} + +// Undirected returns a new undirected graph derived from g, augmented as +// needed to make it undirected, with reciprocal arcs having matching labels. +func (g LabeledDirected) Undirected() LabeledUndirected { + c, _ := g.LabeledAdjacencyList.Copy() // start with a copy + // "reciprocals wanted" + rw := make(LabeledAdjacencyList, g.Order()) + for fr, to := range g.LabeledAdjacencyList { + arc: // for each arc in g + for _, to := range to { + if to.To == NI(fr) { + continue // arc is a loop + } + // search wanted arcs + wf := rw[fr] + for i, w := range wf { + if w == to { // found, remove + last := len(wf) - 1 + wf[i] = wf[last] + rw[fr] = wf[:last] + continue arc + } + } + // arc not found, add to reciprocal to wanted list + rw[to.To] = append(rw[to.To], Half{To: NI(fr), Label: to.Label}) + } + } + // add missing reciprocals + for fr, to := range rw { + c[fr] = append(c[fr], to...) + } + return LabeledUndirected{c} +} + +// Unlabeled constructs the unlabeled directed graph corresponding to g. +func (g LabeledDirected) Unlabeled() Directed { + return Directed{g.LabeledAdjacencyList.Unlabeled()} +} + +// UnlabeledTranspose constructs a new adjacency list that is the unlabeled +// transpose of g. +// +// For every arc from->to of g, the result will have an arc to->from. +// Transpose also counts arcs as it traverses and returns ma, the number of +// arcs in g (equal to the number of arcs in the result.) +// +// It is equivalent to g.Unlabeled().Transpose() but constructs the result +// directly. +func (g LabeledDirected) UnlabeledTranspose() (t Directed, ma int) { + ta := make(AdjacencyList, g.Order()) + for n, nbs := range g.LabeledAdjacencyList { + for _, nb := range nbs { + ta[nb.To] = append(ta[nb.To], NI(n)) + ma++ + } + } + return Directed{ta}, ma +} + +// DominanceFrontiers holds dominance frontiers for all nodes in some graph. +// The frontier for a given node is a set of nodes, represented here as a map. +type DominanceFrontiers []map[NI]struct{} + +// Frontier computes the dominance frontier for a node set. +func (d DominanceFrontiers) Frontier(s map[NI]struct{}) map[NI]struct{} { + fs := map[NI]struct{}{} + for n := range s { + for f := range d[n] { + fs[f] = struct{}{} + } + } + return fs +} + +// Closure computes the closure, or iterated dominance frontier for a node set. +func (d DominanceFrontiers) Closure(s map[NI]struct{}) map[NI]struct{} { + c := map[NI]struct{}{} + e := map[NI]struct{}{} + w := map[NI]struct{}{} + var n NI + for n = range s { + e[n] = struct{}{} + w[n] = struct{}{} + } + for len(w) > 0 { + for n = range w { + break + } + delete(w, n) + for f := range d[n] { + if _, ok := c[f]; !ok { + c[f] = struct{}{} + if _, ok := e[f]; !ok { + e[f] = struct{}{} + w[f] = struct{}{} + } + } + } + } + return c +} + +// Dominators holds immediate dominators. +// +// Dominators is a return type from methods Dominators, PostDominators, and +// Doms. See those methods for construction examples. +// +// The list of immediate dominators represents the "dominator tree" +// (in the same way a FromList represents a tree, but somewhat lighter weight.) +// +// In addition to the exported immediate dominators, the type also retains +// the transpose graph that was used to compute the dominators. +// See PostDominators and Doms for a caution about modifying the transpose +// graph. +type Dominators struct { + Immediate []NI + from interface { // either Directed or LabeledDirected + domFrontiers(Dominators) DominanceFrontiers + } +} + +// Frontiers constructs the dominator frontier for each node. +// +// The frontier for a node is a set of nodes, represented as a map. The +// returned slice has the length of d.Immediate, which is the length of +// the original graph. The frontier is valid however only for nodes of the +// reachable subgraph. Nodes not in the reachable subgraph, those with a +// d.Immediate value of -1, will have a nil map. +func (d Dominators) Frontiers() DominanceFrontiers { + return d.from.domFrontiers(d) +} + +// Set constructs the dominator set for a given node. +// +// The dominator set for a node always includes the node itself as the first +// node in the returned slice, as long as the node was in the subgraph +// reachable from the start node used to construct the dominators. +// If the argument n is a node not in the subgraph, Set returns nil. +func (d Dominators) Set(n NI) []NI { + im := d.Immediate + if im[n] < 0 { + return nil + } + for s := []NI{n}; ; { + if p := im[n]; p < 0 || p == n { + return s + } else { + s = append(s, p) + n = p + } + } +} + +// starting at the node on the top of the stack, follow arcs until stuck. +// mark nodes visited, push nodes on stack, remove arcs from g. +func (e *eulerian) push() { + for u := e.top(); ; { + e.uv.SetBit(int(u), 0) // reset unvisited bit + arcs := e.g[u] + if len(arcs) == 0 { + return // stuck + } + w := arcs[0] // follow first arc + e.s++ // push followed node on stack + e.p[e.s] = w + e.g[u] = arcs[1:] // consume arc + u = w + } +} + +func (e *labEulerian) push() { + for u := e.top().To; ; { + e.uv.SetBit(int(u), 0) // reset unvisited bit + arcs := e.g[u] + if len(arcs) == 0 { + return // stuck + } + w := arcs[0] // follow first arc + e.s++ // push followed node on stack + e.p[e.s] = w + e.g[u] = arcs[1:] // consume arc + u = w.To + } +} diff --git a/vendor/github.com/soniakeys/graph/dir_RO.go b/vendor/github.com/soniakeys/graph/dir_RO.go new file mode 100644 index 00000000..6d43d12a --- /dev/null +++ b/vendor/github.com/soniakeys/graph/dir_RO.go @@ -0,0 +1,1091 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +import ( + "errors" + "fmt" + + "github.com/soniakeys/bits" +) + +// dir_RO.go is code generated from dir_cg.go by directives in graph.go. +// Editing dir_cg.go is okay. It is the code generation source. +// DO NOT EDIT dir_RO.go. +// The RO means read only and it is upper case RO to slow you down a bit +// in case you start to edit the file. + +// Balanced returns true if for every node in g, in-degree equals out-degree. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) Balanced() bool { + for n, in := range g.InDegree() { + if in != len(g.AdjacencyList[n]) { + return false + } + } + return true +} + +// Copy makes a deep copy of g. +// Copy also computes the arc size ma, the number of arcs. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) Copy() (c Directed, ma int) { + l, s := g.AdjacencyList.Copy() + return Directed{l}, s +} + +// Cyclic determines if g contains a cycle, a non-empty path from a node +// back to itself. +// +// Cyclic returns true if g contains at least one cycle. It also returns +// an example of an arc involved in a cycle. +// Cyclic returns false if g is acyclic. +// +// Also see Topological, which detects cycles. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) Cyclic() (cyclic bool, fr NI, to NI) { + a := g.AdjacencyList + fr, to = -1, -1 + temp := bits.New(len(a)) + perm := bits.New(len(a)) + var df func(int) + df = func(n int) { + switch { + case temp.Bit(n) == 1: + cyclic = true + return + case perm.Bit(n) == 1: + return + } + temp.SetBit(n, 1) + for _, nb := range a[n] { + df(int(nb)) + if cyclic { + if fr < 0 { + fr, to = NI(n), nb + } + return + } + } + temp.SetBit(n, 0) + perm.SetBit(n, 1) + } + for n := range a { + if perm.Bit(n) == 1 { + continue + } + if df(n); cyclic { // short circuit as soon as a cycle is found + break + } + } + return +} + +// DegreeCentralization returns out-degree centralization. +// +// Out-degree of a node is one measure of node centrality and is directly +// available from the adjacency list representation. This allows degree +// centralization for the graph to be very efficiently computed. +// +// The value returned is from 0 to 1 inclusive for simple directed graphs of +// two or more nodes. As a special case, 0 is returned for graphs of 0 or 1 +// nodes. The value returned can be > 1 for graphs with loops or parallel +// edges. +// +// In-degree centralization can be computed as DegreeCentralization of the +// transpose. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) DegreeCentralization() float64 { + a := g.AdjacencyList + if len(a) <= 1 { + return 0 + } + var max, sum int + for _, to := range a { + if len(to) > max { + max = len(to) + } + sum += len(to) + } + l1 := len(a) - 1 + return float64(len(a)*max-sum) / float64(l1*l1) +} + +// Dominators computes the immediate dominator for each node reachable from +// start. +// +// The slice returned as Dominators.Immediate will have the length of +// g.AdjacencyList. Nodes without a path to end will have a value of -1. +// +// See also the method Doms. Internally Dominators must construct the +// transpose of g and also compute a postordering of a spanning tree of the +// subgraph reachable from start. If you happen to have either of these +// computed anyway, it can be more efficient to call Doms directly. +func (g Directed) Dominators(start NI) Dominators { + a := g.AdjacencyList + l := len(a) + // ExampleDoms shows traditional depth-first postorder, but it works to + // generate a reverse preorder. Also breadth-first works instead of + // depth-first and may allow Doms to run a little faster by presenting + // a shallower tree. + post := make([]NI, l) + a.BreadthFirst(start, func(n NI) { + l-- + post[l] = n + }) + tr, _ := g.Transpose() + return g.Doms(tr, post[l:]) +} + +// Doms computes either immediate dominators or postdominators. +// +// The slice returned as Dominators.Immediate will have the length of +// g.AdjacencyList. Nodes without a path to end will have a value of -1. +// +// But see also the simpler methods Dominators and PostDominators. +// +// Doms requires argument tr to be the transpose graph of receiver g, +// and requres argument post to be a post ordering of receiver g. More +// specifically a post ordering of a spanning tree of the subgraph reachable +// from some start node in g. The start node will always be the last node in +// this postordering so it does not need to passed as a separate argument. +// +// Doms can be used to construct either dominators or postdominators. +// To construct dominators on a graph f, generate a postordering p on f +// and call f.Doms(f.Transpose(), p). To construct postdominators, generate +// the transpose t first, then a postordering p on t (not f), and call +// t.Doms(f, p). +// +// Caution: The argument tr is retained in the returned Dominators object +// and is used by the method Dominators.Frontier. It is not deep-copied +// so it is invalid to call Doms, modify the tr graph, and then call Frontier. +func (g Directed) Doms(tr Directed, post []NI) Dominators { + a := g.AdjacencyList + dom := make([]NI, len(a)) + pi := make([]int, len(a)) + for i, n := range post { + pi[n] = i + } + intersect := func(b1, b2 NI) NI { + for b1 != b2 { + for pi[b1] < pi[b2] { + b1 = dom[b1] + } + for pi[b2] < pi[b1] { + b2 = dom[b2] + } + } + return b1 + } + for n := range dom { + dom[n] = -1 + } + start := post[len(post)-1] + dom[start] = start + for changed := false; ; changed = false { + for i := len(post) - 2; i >= 0; i-- { + b := post[i] + var im NI + fr := tr.AdjacencyList[b] + var j int + var fp NI + for j, fp = range fr { + if dom[fp] >= 0 { + im = fp + break + } + } + for _, p := range fr[j:] { + if dom[p] >= 0 { + im = intersect(im, p) + } + } + if dom[b] != im { + dom[b] = im + changed = true + } + } + if !changed { + return Dominators{dom, tr} + } + } +} + +// PostDominators computes the immediate postdominator for each node that can +// reach node end. +// +// The slice returned as Dominators.Immediate will have the length of +// g.AdjacencyList. Nodes without a path to end will have a value of -1. +// +// See also the method Doms. Internally Dominators must construct the +// transpose of g and also compute a postordering of a spanning tree of the +// subgraph of the transpose reachable from end. If you happen to have either +// of these computed anyway, it can be more efficient to call Doms directly. +// +// See the method Doms anyway for the caution note. PostDominators calls +// Doms internally, passing receiver g as Doms argument tr. The caution means +// that it is invalid to call PostDominators, modify the graph g, then call +// Frontier. +func (g Directed) PostDominators(end NI) Dominators { + tr, _ := g.Transpose() + a := tr.AdjacencyList + l := len(a) + post := make([]NI, l) + a.BreadthFirst(end, func(n NI) { + l-- + post[l] = n + }) + return tr.Doms(g, post[l:]) +} + +// called from Dominators.Frontier via interface +func (from Directed) domFrontiers(d Dominators) DominanceFrontiers { + im := d.Immediate + f := make(DominanceFrontiers, len(im)) + for i := range f { + if im[i] >= 0 { + f[i] = map[NI]struct{}{} + } + } + for b, fr := range from.AdjacencyList { + if len(fr) < 2 { + continue + } + imb := im[b] + for _, p := range fr { + for runner := p; runner != imb; runner = im[runner] { + f[runner][NI(b)] = struct{}{} + } + } + } + return f +} + +// Eulerian scans a directed graph to determine if it is Eulerian. +// +// If the graph represents an Eulerian cycle, it returns -1, -1, nil. +// +// If the graph does not represent an Eulerian cycle but does represent an +// Eulerian path, it returns the start and end nodes of the path, and nil. +// +// Otherwise it returns an error indicating a reason the graph is non-Eulerian. +// Also in this case it returns a relevant node in either start or end. +// +// See also method EulerianStart, which short-circuits when it finds a start +// node whereas this method completely validates a graph as Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) Eulerian() (start, end NI, err error) { + ind := g.InDegree() + start = -1 + end = -1 + for n, to := range g.AdjacencyList { + switch { + case len(to) > ind[n]: + if start >= 0 { + return NI(n), -1, errors.New("multiple start candidates") + } + if len(to) > ind[n]+1 { + return NI(n), -1, errors.New("excessive out-degree") + } + start = NI(n) + case ind[n] > len(to): + if end >= 0 { + return -1, NI(n), errors.New("multiple end candidates") + } + if ind[n] > len(to)+1 { + return -1, NI(n), errors.New("excessive in-degree") + } + end = NI(n) + } + } + return start, end, nil +} + +// EulerianCycle finds an Eulerian cycle in a directed multigraph. +// +// * If g has no nodes, result is nil, nil. +// +// * If g is Eulerian, result is an Eulerian cycle with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the cycle. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// Internally, EulerianCycle copies the entire graph g. +// See EulerianCycleD for a more space efficient version. +// +// There are nearly equivalent labeled and unlabeled versions of this method. +// In the labeled version the first element of of the +func (g Directed) EulerianCycle() ([]NI, error) { + c, m := g.Copy() + return c.EulerianCycleD(m) +} + +// EulerianCycleD finds an Eulerian cycle in a directed multigraph. +// +// EulerianCycleD is destructive on its receiver g. See EulerianCycle for +// a non-destructive version. +// +// Argument ma must be the correct arc size, or number of arcs in g. +// +// * If g has no nodes, result is nil, nil. +// +// * If g is Eulerian, result is an Eulerian cycle with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the cycle. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) EulerianCycleD(ma int) ([]NI, error) { + // algorithm adapted from "Sketch of Eulerian Circuit Algorithm" by + // Carl Lee, accessed at http://www.ms.uky.edu/~lee/ma515fa10/euler.pdf. + if g.Order() == 0 { + return nil, nil + } + e := newEulerian(g.AdjacencyList, ma) + e.p[0] = 0 + for e.s >= 0 { + v := e.top() // v is node that starts cycle + e.push() + // if Eulerian, we'll always come back to starting node + if e.top() != v { + return nil, errors.New("not Eulerian") + } + e.keep() + } + if !e.uv.AllZeros() { + return nil, errors.New("not strongly connected") + } + return e.p, nil +} + +// EulerianPath finds an Eulerian path in a directed multigraph. +// +// * If g has no nodes, result is nil, nil. +// +// * If g has an Eulerian path, result is an Eulerian path with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the path. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// Internally, EulerianPath copies the entire graph g. +// See EulerianPathD for a more space efficient version. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) EulerianPath() ([]NI, error) { + c, m := g.Copy() + start, err := c.EulerianStart() + if err != nil { + return nil, err + } + if start < 0 { + start = 0 + } + return c.EulerianPathD(m, start) +} + +// EulerianPathD finds an Eulerian path in a directed multigraph. +// +// EulerianPathD is destructive on its receiver g. See EulerianPath for +// a non-destructive version. +// +// Argument ma must be the correct arc size, or number of arcs in g. +// Argument start must be a valid start node for the path. +// +// * If g has no nodes, result is nil, nil. +// +// * If g has an Eulerian path starting at start, result is an Eulerian path +// with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the path. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) EulerianPathD(ma int, start NI) ([]NI, error) { + if g.Order() == 0 { + return nil, nil + } + e := newEulerian(g.AdjacencyList, ma) + e.p[0] = start + // unlike EulerianCycle, the first path doesn't have to be a cycle. + e.push() + e.keep() + for e.s >= 0 { + start = e.top() + e.push() + // paths after the first must be cycles though + // (as long as there are nodes on the stack) + if e.top() != start { + return nil, errors.New("no Eulerian path") + } + e.keep() + } + if !e.uv.AllZeros() { + return nil, errors.New("no Eulerian path") + } + return e.p, nil +} + +// EulerianStart finds a candidate start node for an Eulerian path. +// +// A candidate start node in the directed case has out-degree one greater then +// in-degree. EulerianStart scans the graph returning immediately with the +// node (and err == nil) when it finds such a candidate. +// +// EulerianStart also returns immediately with an error if it finds the graph +// cannot contain an Eulerian path. In this case it also returns a relevant +// node. +// +// If the scan completes without finding a candidate start node, the graph +// represents an Eulerian cycle. In this case it returns -1, nil, and any +// node can be chosen as a start node for an eulerian path. +// +// See also method Eulerian, which completely validates a graph as Eulerian +// whereas this method short-curcuits when it finds a start node. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) EulerianStart() (start NI, err error) { + ind := g.InDegree() + end := -1 + for n, to := range g.AdjacencyList { + switch { + case len(to) > ind[n]: + if len(to) == ind[n]+1 { + return NI(n), nil // candidate start + } + return -1, errors.New("excessive out-degree") + case ind[n] > len(to): + if end >= 0 { + return NI(n), errors.New("multiple end candidates") + } + if ind[n] > len(to)+1 { + return NI(n), errors.New("excessive in-degree") + } + end = n + } + } + return -1, nil // cycle +} + +type eulerian struct { + g AdjacencyList // working copy of graph, it gets consumed + m int // number of arcs in g, updated as g is consumed + uv bits.Bits // unvisited + // low end of p is stack of unfinished nodes + // high end is finished path + p []NI // stack + path + s int // stack pointer +} + +func newEulerian(g AdjacencyList, m int) *eulerian { + e := &eulerian{ + g: g, + m: m, + uv: bits.New(len(g)), + p: make([]NI, m+1), + } + e.uv.SetAll() + return e +} + +// starting with the node on top of the stack, move nodes with no arcs. +func (e *eulerian) keep() { + for e.s >= 0 { + n := e.top() + if len(e.g[n]) > 0 { + break + } + e.p[e.m] = n + e.s-- + e.m-- + } +} + +func (e *eulerian) top() NI { + return e.p[e.s] +} + +// MaximalNonBranchingPaths finds all paths in a directed graph that are +// "maximal" and "non-branching". +// +// A non-branching path is one where path nodes other than the first and last +// have exactly one arc leading to the node and one arc leading from the node, +// thus there is no possibility to branch away to a different path. +// +// A maximal non-branching path cannot be extended to a longer non-branching +// path by including another node at either end. +// +// In the case of a cyclic non-branching path, the first and last nodes +// of the path will be the same node, indicating an isolated cycle. +// +// The method calls the emit argument for each path or isolated cycle in g, +// as long as emit returns true. If emit returns false, +// MaximalNonBranchingPaths returns immediately. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) MaximalNonBranchingPaths(emit func([]NI) bool) { + a := g.AdjacencyList + ind := g.InDegree() + uv := bits.New(g.Order()) + uv.SetAll() + for v, vTo := range a { + if !(ind[v] == 1 && len(vTo) == 1) { + for _, w := range vTo { + n := []NI{NI(v), w} + uv.SetBit(v, 0) + uv.SetBit(int(w), 0) + wTo := a[w] + for ind[w] == 1 && len(wTo) == 1 { + u := wTo[0] + n = append(n, u) + uv.SetBit(int(u), 0) + w = u + wTo = a[w] + } + if !emit(n) { // n is a path + return + } + } + } + } + // use uv.From rather than uv.Iterate. + // Iterate doesn't work here because we're modifying uv + for b := uv.OneFrom(0); b >= 0; b = uv.OneFrom(b + 1) { + v := NI(b) + n := []NI{v} + for w := v; ; { + w = a[w][0] + uv.SetBit(int(w), 0) + n = append(n, w) + if w == v { + break + } + } + if !emit(n) { // n is an isolated cycle + return + } + } +} + +// InDegree computes the in-degree of each node in g +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) InDegree() []int { + ind := make([]int, g.Order()) + for _, nbs := range g.AdjacencyList { + for _, nb := range nbs { + ind[nb]++ + } + } + return ind +} + +// AddNode maps a node in a supergraph to a subgraph node. +// +// Argument p must be an NI in supergraph s.Super. AddNode panics if +// p is not a valid node index of s.Super. +// +// AddNode is idempotent in that it does not add a new node to the subgraph if +// a subgraph node already exists mapped to supergraph node p. +// +// The mapped subgraph NI is returned. +func (s *DirectedSubgraph) AddNode(p NI) (b NI) { + if int(p) < 0 || int(p) >= s.Super.Order() { + panic(fmt.Sprint("AddNode: NI ", p, " not in supergraph")) + } + if b, ok := s.SubNI[p]; ok { + return b + } + a := s.Directed.AdjacencyList + b = NI(len(a)) + s.Directed.AdjacencyList = append(a, nil) + s.SuperNI = append(s.SuperNI, p) + s.SubNI[p] = b + return +} + +// AddArc adds an arc to a subgraph. +// +// Arguments fr, to must be NIs in supergraph s.Super. As with AddNode, +// AddArc panics if fr and to are not valid node indexes of s.Super. +// +// The arc specfied by fr, to must exist in s.Super. Further, the number of +// parallel arcs in the subgraph cannot exceed the number of corresponding +// parallel arcs in the supergraph. That is, each arc already added to the +// subgraph counts against the arcs available in the supergraph. If a matching +// arc is not available, AddArc returns an error. +// +// If a matching arc is available, subgraph nodes are added as needed, the +// subgraph arc is added, and the method returns nil. +func (s *DirectedSubgraph) AddArc(fr NI, to NI) error { + // verify supergraph NIs first, but without adding subgraph nodes just yet. + if int(fr) < 0 || int(fr) >= s.Super.Order() { + panic(fmt.Sprint("AddArc: NI ", fr, " not in supergraph")) + } + if int(to) < 0 || int(to) >= s.Super.Order() { + panic(fmt.Sprint("AddArc: NI ", to, " not in supergraph")) + } + // count existing matching arcs in subgraph + n := 0 + a := s.Directed.AdjacencyList + if bf, ok := s.SubNI[fr]; ok { + if bt, ok := s.SubNI[to]; ok { + // both NIs already exist in subgraph, need to count arcs + bTo := to + bTo = bt + for _, t := range a[bf] { + if t == bTo { + n++ + } + } + } + } + // verify matching arcs are available in supergraph + for _, t := range (*s.Super).AdjacencyList[fr] { + if t == to { + if n > 0 { + n-- // match existing arc + continue + } + // no more existing arcs need to be matched. nodes can finally + // be added as needed and then the arc can be added. + bf := s.AddNode(fr) + to = s.AddNode(to) + s.Directed.AdjacencyList[bf] = + append(s.Directed.AdjacencyList[bf], to) + return nil // success + } + } + return errors.New("arc not available in supergraph") +} + +// InduceList constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument l must be a list of +// NIs in receiver graph g. Receiver g becomes the supergraph of the induced +// subgraph. +// +// Duplicate NIs are allowed in list l. The duplicates are effectively removed +// and only a single corresponding node is created in the subgraph. Subgraph +// NIs are mapped in the order of list l, execpt for ignoring duplicates. +// NIs in l that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *Directed) InduceList(l []NI) *DirectedSubgraph { + sub, sup := mapList(l) + return &DirectedSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + Directed: Directed{ + g.AdjacencyList.induceArcs(sub, sup), + }} +} + +// InduceBits constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument t must be a bitmap +// representing NIs in receiver graph g. Receiver g becomes the supergraph +// of the induced subgraph. NIs in t that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *Directed) InduceBits(t bits.Bits) *DirectedSubgraph { + sub, sup := mapBits(t) + return &DirectedSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + Directed: Directed{ + g.AdjacencyList.induceArcs(sub, sup), + }} +} + +// IsTree identifies trees in directed graphs. +// +// Return value isTree is true if the subgraph reachable from root is a tree. +// Further, return value allTree is true if the entire graph g is reachable +// from root. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) IsTree(root NI) (isTree, allTree bool) { + a := g.AdjacencyList + v := bits.New(len(a)) + v.SetAll() + var df func(NI) bool + df = func(n NI) bool { + if v.Bit(int(n)) == 0 { + return false + } + v.SetBit(int(n), 0) + for _, to := range a[n] { + if !df(to) { + return false + } + } + return true + } + isTree = df(root) + return isTree, isTree && v.AllZeros() +} + +// PageRank computes a significance score for each node of a graph. +// +// The algorithm is credited to Google founders Brin and Lawrence. +// +// Argument d is a damping factor. Reportedly a value of .85 works well. +// Argument n is a number of iterations. Reportedly values of 20 to 50 +// work well. +// +// Returned is the PageRank score for each node of g. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) PageRank(d float64, n int) []float64 { + // Following "PageRank Explained" by Ian Rogers, accessed at + // http://www.cs.princeton.edu/~chazelle/courses/BIB/pagerank.htm + a := g.AdjacencyList + p0 := make([]float64, len(a)) + p1 := make([]float64, len(a)) + for i := range p0 { + p0[i] = 1 + } + d1 := 1 - d + for ; n > 0; n-- { + for i := range p1 { + p1[i] = d1 + } + for fr, to := range a { + f := d / float64(len(to)) + for _, to := range to { + p1[to] += p0[fr] * f + } + } + p0, p1 = p1, p0 + } + return p0 +} + +// StronglyConnectedComponents identifies strongly connected components in +// a directed graph. +// +// The method calls the emit function for each component identified. The +// argument to emit is the node list of a component. The emit function must +// return true for the method to continue identifying components. If emit +// returns false, the method returns immediately. +// +// Note well: The backing slice for the node list passed to emit is reused +// across emit calls. If you need to retain the node list you must copy it. +// +// The components emitted represent a partition of the nodes in g. +// So for example, if the first component emitted has the same length as g +// then it will be the only component and it means the entire graph g is +// strongly connected. +// +// See also Condensation which returns a condensation graph in addition +// to the strongly connected components. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// The algorithm here is by David Pearce. See also alt.SCCPathBased and +// alt.SCCTarjan. +func (g Directed) StronglyConnectedComponents(emit func([]NI) bool) { + // See Algorithm 3 PEA FIND SCC2(V,E) in "An Improved Algorithm for + // Finding the Strongly Connected Components of a Directed Graph" + // by David J. Pearce. + a := g.AdjacencyList + rindex := make([]int, len(a)) + var S, scc []NI + index := 1 + c := len(a) - 1 + var visit func(NI) bool + visit = func(v NI) bool { + root := true + rindex[v] = index + index++ + for _, w := range a[v] { + if rindex[w] == 0 { + if !visit(w) { + return false + } + } + if rindex[w] < rindex[v] { + rindex[v] = rindex[w] + root = false + } + } + if !root { + S = append(S, v) + return true + } + scc = scc[:0] + index-- + for last := len(S) - 1; last >= 0; last-- { + w := S[last] + if rindex[v] > rindex[w] { + break + } + S = S[:last] + rindex[w] = c + scc = append(scc, w) + index-- + } + rindex[v] = c + c-- + return emit(append(scc, v)) + } + for v := range a { + if rindex[v] == 0 && !visit(NI(v)) { + break + } + } +} + +// Condensation returns strongly connected components and their +// condensation graph. +// +// A condensation represents a directed acyclic graph. +// Components are ordered in a reverse topological ordering. +// +// See also StronglyConnectedComponents, which returns the components only. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) Condensation() (scc [][]NI, cd AdjacencyList) { + a := g.AdjacencyList + b := make([]NI, len(a)) // backing slice for scc + g.StronglyConnectedComponents(func(c []NI) bool { + n := copy(b, c) + scc = append(scc, b[:n]) + b = b[n:] + return true + }) + cd = make(AdjacencyList, len(scc)) // return value + cond := make([]NI, len(a)) // mapping from g node to cd node + for cn, c := range scc { + for _, n := range c { + cond[n] = NI(cn) // map g node to cd node + } + var tos []NI // list of 'to' nodes + m := bits.New(len(cd)) // tos map + m.SetBit(cn, 1) + for _, n := range c { + for _, to := range a[n] { + if ct := cond[to]; m.Bit(int(ct)) == 0 { + m.SetBit(int(ct), 1) + tos = append(tos, ct) + } + } + } + cd[cn] = tos + } + return +} + +// Topological computes a topological ordering of a directed acyclic graph. +// +// For an acyclic graph, return value ordering is a permutation of node numbers +// in topologically sorted order and cycle will be nil. If the graph is found +// to be cyclic, ordering will be nil and cycle will be the path of a found +// cycle. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) Topological() (ordering, cycle []NI) { + i := -1 + return g.dfTopo(func() NI { + i++ + if i < g.Order() { + return NI(i) + } + return -1 + }) +} + +func (g Directed) dfTopo(f func() NI) (ordering, cycle []NI) { + a := g.AdjacencyList + ordering = make([]NI, len(a)) + i := len(ordering) + temp := bits.New(len(a)) + perm := bits.New(len(a)) + var cycleFound bool + var cycleStart NI + var df func(NI) + df = func(n NI) { + switch { + case temp.Bit(int(n)) == 1: + cycleFound = true + cycleStart = n + return + case perm.Bit(int(n)) == 1: + return + } + temp.SetBit(int(n), 1) + for _, nb := range a[n] { + df(nb) + if cycleFound { + if cycleStart >= 0 { + // a little hack: orderng won't be needed so repurpose the + // slice as cycle. this is read out in reverse order + // as the recursion unwinds. + x := len(ordering) - 1 - len(cycle) + ordering[x] = n + cycle = ordering[x:] + if n == cycleStart { + cycleStart = -1 + } + } + return + } + } + temp.SetBit(int(n), 0) + perm.SetBit(int(n), 1) + i-- + ordering[i] = n + } + for { + n := f() + if n < 0 { + return ordering[i:], nil + } + if perm.Bit(int(n)) == 1 { + continue + } + df(n) + if cycleFound { + return nil, cycle + } + } +} + +// TopologicalKahn computes a topological ordering of a directed acyclic graph. +// +// For an acyclic graph, return value ordering is a permutation of node numbers +// in topologically sorted order and cycle will be nil. If the graph is found +// to be cyclic, ordering will be nil and cycle will be the path of a found +// cycle. +// +// This function is based on the algorithm by Arthur Kahn and requires the +// transpose of g be passed as the argument. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) TopologicalKahn(tr Directed) (ordering, cycle []NI) { + // code follows Wikipedia pseudocode. + var L, S []NI + // rem for "remaining edges," this function makes a local copy of the + // in-degrees and consumes that instead of consuming an input. + rem := make([]int, g.Order()) + for n, fr := range tr.AdjacencyList { + if len(fr) == 0 { + // accumulate "set of all nodes with no incoming edges" + S = append(S, NI(n)) + } else { + // initialize rem from in-degree + rem[n] = len(fr) + } + } + for len(S) > 0 { + last := len(S) - 1 // "remove a node n from S" + n := S[last] + S = S[:last] + L = append(L, n) // "add n to tail of L" + for _, m := range g.AdjacencyList[n] { + // WP pseudo code reads "for each node m..." but it means for each + // node m *remaining in the graph.* We consume rem rather than + // the graph, so "remaining in the graph" for us means rem[m] > 0. + if rem[m] > 0 { + rem[m]-- // "remove edge from the graph" + if rem[m] == 0 { // if "m has no other incoming edges" + S = append(S, m) // "insert m into S" + } + } + } + } + // "If graph has edges," for us means a value in rem is > 0. + for c, in := range rem { + if in > 0 { + // recover cyclic nodes + for _, nb := range g.AdjacencyList[c] { + if rem[nb] > 0 { + cycle = append(cycle, NI(c)) + break + } + } + } + } + if len(cycle) > 0 { + return nil, cycle + } + return L, nil +} + +// TopologicalSubgraph computes a topological ordering of a subgraph of a +// directed acyclic graph. +// +// The subgraph considered is that reachable from the specified node list. +// +// For an acyclic subgraph, return value ordering is a permutation of reachable +// node numbers in topologically sorted order and cycle will be nil. If the +// subgraph is found to be cyclic, ordering will be nil and cycle will be +// the path of a found cycle. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Directed) TopologicalSubgraph(nodes []NI) (ordering, cycle []NI) { + i := -1 + return g.dfTopo(func() NI { + i++ + if i < len(nodes) { + return nodes[i] + } + return -1 + }) +} + +// TransitiveClosure returns the transitive closure of directed graph g. +// +// The algorithm is Warren's, which works most naturally with an adjacency +// matrix representation. The returned transitive closure is left in this +// adjacency matrix representation. For a graph g of order n, matrix tc +// is returned as a length n slice of length n bits.Bits values, where +// tc[from].Bit(to) == 1 represents an arc of the transitive closure. +func (g Directed) TransitiveClosure() []bits.Bits { + // construct adjacency matrix + a := g.AdjacencyList + t := make([]bits.Bits, len(a)) + for n := range t { + tn := bits.New(len(a)) + for _, to := range a[n] { + tn.SetBit(int(to), 1) + } + t[n] = tn + } + // above diagonal + for i := 1; i < len(a); i++ { + ti := t[i] + for k := 0; k < i; k++ { + if ti.Bit(k) == 1 { + ti.Or(ti, t[k]) + } + } + } + // below diagonal + for i, ti := range t[:len(a)-1] { + for k := i + 1; k < len(a); k++ { + if ti.Bit(k) == 1 { + ti.Or(ti, t[k]) + } + } + } + return t +} diff --git a/vendor/github.com/soniakeys/graph/dir_cg.go b/vendor/github.com/soniakeys/graph/dir_cg.go new file mode 100644 index 00000000..07cc9655 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/dir_cg.go @@ -0,0 +1,1091 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +import ( + "errors" + "fmt" + + "github.com/soniakeys/bits" +) + +// dir_RO.go is code generated from dir_cg.go by directives in graph.go. +// Editing dir_cg.go is okay. It is the code generation source. +// DO NOT EDIT dir_RO.go. +// The RO means read only and it is upper case RO to slow you down a bit +// in case you start to edit the file. + +// Balanced returns true if for every node in g, in-degree equals out-degree. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) Balanced() bool { + for n, in := range g.InDegree() { + if in != len(g.LabeledAdjacencyList[n]) { + return false + } + } + return true +} + +// Copy makes a deep copy of g. +// Copy also computes the arc size ma, the number of arcs. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) Copy() (c LabeledDirected, ma int) { + l, s := g.LabeledAdjacencyList.Copy() + return LabeledDirected{l}, s +} + +// Cyclic determines if g contains a cycle, a non-empty path from a node +// back to itself. +// +// Cyclic returns true if g contains at least one cycle. It also returns +// an example of an arc involved in a cycle. +// Cyclic returns false if g is acyclic. +// +// Also see Topological, which detects cycles. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) Cyclic() (cyclic bool, fr NI, to Half) { + a := g.LabeledAdjacencyList + fr, to.To = -1, -1 + temp := bits.New(len(a)) + perm := bits.New(len(a)) + var df func(int) + df = func(n int) { + switch { + case temp.Bit(n) == 1: + cyclic = true + return + case perm.Bit(n) == 1: + return + } + temp.SetBit(n, 1) + for _, nb := range a[n] { + df(int(nb.To)) + if cyclic { + if fr < 0 { + fr, to = NI(n), nb + } + return + } + } + temp.SetBit(n, 0) + perm.SetBit(n, 1) + } + for n := range a { + if perm.Bit(n) == 1 { + continue + } + if df(n); cyclic { // short circuit as soon as a cycle is found + break + } + } + return +} + +// DegreeCentralization returns out-degree centralization. +// +// Out-degree of a node is one measure of node centrality and is directly +// available from the adjacency list representation. This allows degree +// centralization for the graph to be very efficiently computed. +// +// The value returned is from 0 to 1 inclusive for simple directed graphs of +// two or more nodes. As a special case, 0 is returned for graphs of 0 or 1 +// nodes. The value returned can be > 1 for graphs with loops or parallel +// edges. +// +// In-degree centralization can be computed as DegreeCentralization of the +// transpose. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) DegreeCentralization() float64 { + a := g.LabeledAdjacencyList + if len(a) <= 1 { + return 0 + } + var max, sum int + for _, to := range a { + if len(to) > max { + max = len(to) + } + sum += len(to) + } + l1 := len(a) - 1 + return float64(len(a)*max-sum) / float64(l1*l1) +} + +// Dominators computes the immediate dominator for each node reachable from +// start. +// +// The slice returned as Dominators.Immediate will have the length of +// g.AdjacencyList. Nodes without a path to end will have a value of -1. +// +// See also the method Doms. Internally Dominators must construct the +// transpose of g and also compute a postordering of a spanning tree of the +// subgraph reachable from start. If you happen to have either of these +// computed anyway, it can be more efficient to call Doms directly. +func (g LabeledDirected) Dominators(start NI) Dominators { + a := g.LabeledAdjacencyList + l := len(a) + // ExampleDoms shows traditional depth-first postorder, but it works to + // generate a reverse preorder. Also breadth-first works instead of + // depth-first and may allow Doms to run a little faster by presenting + // a shallower tree. + post := make([]NI, l) + a.BreadthFirst(start, func(n NI) { + l-- + post[l] = n + }) + tr, _ := g.Transpose() + return g.Doms(tr, post[l:]) +} + +// Doms computes either immediate dominators or postdominators. +// +// The slice returned as Dominators.Immediate will have the length of +// g.AdjacencyList. Nodes without a path to end will have a value of -1. +// +// But see also the simpler methods Dominators and PostDominators. +// +// Doms requires argument tr to be the transpose graph of receiver g, +// and requres argument post to be a post ordering of receiver g. More +// specifically a post ordering of a spanning tree of the subgraph reachable +// from some start node in g. The start node will always be the last node in +// this postordering so it does not need to passed as a separate argument. +// +// Doms can be used to construct either dominators or postdominators. +// To construct dominators on a graph f, generate a postordering p on f +// and call f.Doms(f.Transpose(), p). To construct postdominators, generate +// the transpose t first, then a postordering p on t (not f), and call +// t.Doms(f, p). +// +// Caution: The argument tr is retained in the returned Dominators object +// and is used by the method Dominators.Frontier. It is not deep-copied +// so it is invalid to call Doms, modify the tr graph, and then call Frontier. +func (g LabeledDirected) Doms(tr LabeledDirected, post []NI) Dominators { + a := g.LabeledAdjacencyList + dom := make([]NI, len(a)) + pi := make([]int, len(a)) + for i, n := range post { + pi[n] = i + } + intersect := func(b1, b2 NI) NI { + for b1 != b2 { + for pi[b1] < pi[b2] { + b1 = dom[b1] + } + for pi[b2] < pi[b1] { + b2 = dom[b2] + } + } + return b1 + } + for n := range dom { + dom[n] = -1 + } + start := post[len(post)-1] + dom[start] = start + for changed := false; ; changed = false { + for i := len(post) - 2; i >= 0; i-- { + b := post[i] + var im NI + fr := tr.LabeledAdjacencyList[b] + var j int + var fp Half + for j, fp = range fr { + if dom[fp.To] >= 0 { + im = fp.To + break + } + } + for _, p := range fr[j:] { + if dom[p.To] >= 0 { + im = intersect(im, p.To) + } + } + if dom[b] != im { + dom[b] = im + changed = true + } + } + if !changed { + return Dominators{dom, tr} + } + } +} + +// PostDominators computes the immediate postdominator for each node that can +// reach node end. +// +// The slice returned as Dominators.Immediate will have the length of +// g.AdjacencyList. Nodes without a path to end will have a value of -1. +// +// See also the method Doms. Internally Dominators must construct the +// transpose of g and also compute a postordering of a spanning tree of the +// subgraph of the transpose reachable from end. If you happen to have either +// of these computed anyway, it can be more efficient to call Doms directly. +// +// See the method Doms anyway for the caution note. PostDominators calls +// Doms internally, passing receiver g as Doms argument tr. The caution means +// that it is invalid to call PostDominators, modify the graph g, then call +// Frontier. +func (g LabeledDirected) PostDominators(end NI) Dominators { + tr, _ := g.Transpose() + a := tr.LabeledAdjacencyList + l := len(a) + post := make([]NI, l) + a.BreadthFirst(end, func(n NI) { + l-- + post[l] = n + }) + return tr.Doms(g, post[l:]) +} + +// called from Dominators.Frontier via interface +func (from LabeledDirected) domFrontiers(d Dominators) DominanceFrontiers { + im := d.Immediate + f := make(DominanceFrontiers, len(im)) + for i := range f { + if im[i] >= 0 { + f[i] = map[NI]struct{}{} + } + } + for b, fr := range from.LabeledAdjacencyList { + if len(fr) < 2 { + continue + } + imb := im[b] + for _, p := range fr { + for runner := p.To; runner != imb; runner = im[runner] { + f[runner][NI(b)] = struct{}{} + } + } + } + return f +} + +// Eulerian scans a directed graph to determine if it is Eulerian. +// +// If the graph represents an Eulerian cycle, it returns -1, -1, nil. +// +// If the graph does not represent an Eulerian cycle but does represent an +// Eulerian path, it returns the start and end nodes of the path, and nil. +// +// Otherwise it returns an error indicating a reason the graph is non-Eulerian. +// Also in this case it returns a relevant node in either start or end. +// +// See also method EulerianStart, which short-circuits when it finds a start +// node whereas this method completely validates a graph as Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) Eulerian() (start, end NI, err error) { + ind := g.InDegree() + start = -1 + end = -1 + for n, to := range g.LabeledAdjacencyList { + switch { + case len(to) > ind[n]: + if start >= 0 { + return NI(n), -1, errors.New("multiple start candidates") + } + if len(to) > ind[n]+1 { + return NI(n), -1, errors.New("excessive out-degree") + } + start = NI(n) + case ind[n] > len(to): + if end >= 0 { + return -1, NI(n), errors.New("multiple end candidates") + } + if ind[n] > len(to)+1 { + return -1, NI(n), errors.New("excessive in-degree") + } + end = NI(n) + } + } + return start, end, nil +} + +// EulerianCycle finds an Eulerian cycle in a directed multigraph. +// +// * If g has no nodes, result is nil, nil. +// +// * If g is Eulerian, result is an Eulerian cycle with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the cycle. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// Internally, EulerianCycle copies the entire graph g. +// See EulerianCycleD for a more space efficient version. +// +// There are nearly equivalent labeled and unlabeled versions of this method. +// In the labeled version the first element of of the +func (g LabeledDirected) EulerianCycle() ([]Half, error) { + c, m := g.Copy() + return c.EulerianCycleD(m) +} + +// EulerianCycleD finds an Eulerian cycle in a directed multigraph. +// +// EulerianCycleD is destructive on its receiver g. See EulerianCycle for +// a non-destructive version. +// +// Argument ma must be the correct arc size, or number of arcs in g. +// +// * If g has no nodes, result is nil, nil. +// +// * If g is Eulerian, result is an Eulerian cycle with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the cycle. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) EulerianCycleD(ma int) ([]Half, error) { + // algorithm adapted from "Sketch of Eulerian Circuit Algorithm" by + // Carl Lee, accessed at http://www.ms.uky.edu/~lee/ma515fa10/euler.pdf. + if g.Order() == 0 { + return nil, nil + } + e := newLabEulerian(g.LabeledAdjacencyList, ma) + e.p[0] = Half{0, -1} + for e.s >= 0 { + v := e.top() // v is node that starts cycle + e.push() + // if Eulerian, we'll always come back to starting node + if e.top().To != v.To { + return nil, errors.New("not Eulerian") + } + e.keep() + } + if !e.uv.AllZeros() { + return nil, errors.New("not strongly connected") + } + return e.p, nil +} + +// EulerianPath finds an Eulerian path in a directed multigraph. +// +// * If g has no nodes, result is nil, nil. +// +// * If g has an Eulerian path, result is an Eulerian path with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the path. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// Internally, EulerianPath copies the entire graph g. +// See EulerianPathD for a more space efficient version. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) EulerianPath() ([]Half, error) { + c, m := g.Copy() + start, err := c.EulerianStart() + if err != nil { + return nil, err + } + if start < 0 { + start = 0 + } + return c.EulerianPathD(m, start) +} + +// EulerianPathD finds an Eulerian path in a directed multigraph. +// +// EulerianPathD is destructive on its receiver g. See EulerianPath for +// a non-destructive version. +// +// Argument ma must be the correct arc size, or number of arcs in g. +// Argument start must be a valid start node for the path. +// +// * If g has no nodes, result is nil, nil. +// +// * If g has an Eulerian path starting at start, result is an Eulerian path +// with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the path. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) EulerianPathD(ma int, start NI) ([]Half, error) { + if g.Order() == 0 { + return nil, nil + } + e := newLabEulerian(g.LabeledAdjacencyList, ma) + e.p[0] = Half{start, -1} + // unlike EulerianCycle, the first path doesn't have to be a cycle. + e.push() + e.keep() + for e.s >= 0 { + start = e.top().To + e.push() + // paths after the first must be cycles though + // (as long as there are nodes on the stack) + if e.top().To != start { + return nil, errors.New("no Eulerian path") + } + e.keep() + } + if !e.uv.AllZeros() { + return nil, errors.New("no Eulerian path") + } + return e.p, nil +} + +// EulerianStart finds a candidate start node for an Eulerian path. +// +// A candidate start node in the directed case has out-degree one greater then +// in-degree. EulerianStart scans the graph returning immediately with the +// node (and err == nil) when it finds such a candidate. +// +// EulerianStart also returns immediately with an error if it finds the graph +// cannot contain an Eulerian path. In this case it also returns a relevant +// node. +// +// If the scan completes without finding a candidate start node, the graph +// represents an Eulerian cycle. In this case it returns -1, nil, and any +// node can be chosen as a start node for an eulerian path. +// +// See also method Eulerian, which completely validates a graph as Eulerian +// whereas this method short-curcuits when it finds a start node. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) EulerianStart() (start NI, err error) { + ind := g.InDegree() + end := -1 + for n, to := range g.LabeledAdjacencyList { + switch { + case len(to) > ind[n]: + if len(to) == ind[n]+1 { + return NI(n), nil // candidate start + } + return -1, errors.New("excessive out-degree") + case ind[n] > len(to): + if end >= 0 { + return NI(n), errors.New("multiple end candidates") + } + if ind[n] > len(to)+1 { + return NI(n), errors.New("excessive in-degree") + } + end = n + } + } + return -1, nil // cycle +} + +type labEulerian struct { + g LabeledAdjacencyList // working copy of graph, it gets consumed + m int // number of arcs in g, updated as g is consumed + uv bits.Bits // unvisited + // low end of p is stack of unfinished nodes + // high end is finished path + p []Half // stack + path + s int // stack pointer +} + +func newLabEulerian(g LabeledAdjacencyList, m int) *labEulerian { + e := &labEulerian{ + g: g, + m: m, + uv: bits.New(len(g)), + p: make([]Half, m+1), + } + e.uv.SetAll() + return e +} + +// starting with the node on top of the stack, move nodes with no arcs. +func (e *labEulerian) keep() { + for e.s >= 0 { + n := e.top() + if len(e.g[n.To]) > 0 { + break + } + e.p[e.m] = n + e.s-- + e.m-- + } +} + +func (e *labEulerian) top() Half { + return e.p[e.s] +} + +// MaximalNonBranchingPaths finds all paths in a directed graph that are +// "maximal" and "non-branching". +// +// A non-branching path is one where path nodes other than the first and last +// have exactly one arc leading to the node and one arc leading from the node, +// thus there is no possibility to branch away to a different path. +// +// A maximal non-branching path cannot be extended to a longer non-branching +// path by including another node at either end. +// +// In the case of a cyclic non-branching path, the first and last nodes +// of the path will be the same node, indicating an isolated cycle. +// +// The method calls the emit argument for each path or isolated cycle in g, +// as long as emit returns true. If emit returns false, +// MaximalNonBranchingPaths returns immediately. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) MaximalNonBranchingPaths(emit func([]Half) bool) { + a := g.LabeledAdjacencyList + ind := g.InDegree() + uv := bits.New(g.Order()) + uv.SetAll() + for v, vTo := range a { + if !(ind[v] == 1 && len(vTo) == 1) { + for _, w := range vTo { + n := []Half{Half{NI(v), -1}, w} + uv.SetBit(v, 0) + uv.SetBit(int(w.To), 0) + wTo := a[w.To] + for ind[w.To] == 1 && len(wTo) == 1 { + u := wTo[0] + n = append(n, u) + uv.SetBit(int(u.To), 0) + w = u + wTo = a[w.To] + } + if !emit(n) { // n is a path + return + } + } + } + } + // use uv.From rather than uv.Iterate. + // Iterate doesn't work here because we're modifying uv + for b := uv.OneFrom(0); b >= 0; b = uv.OneFrom(b + 1) { + v := Half{NI(b), -1} + n := []Half{v} + for w := v; ; { + w = a[w.To][0] + uv.SetBit(int(w.To), 0) + n = append(n, w) + if w.To == v.To { + break + } + } + if !emit(n) { // n is an isolated cycle + return + } + } +} + +// InDegree computes the in-degree of each node in g +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) InDegree() []int { + ind := make([]int, g.Order()) + for _, nbs := range g.LabeledAdjacencyList { + for _, nb := range nbs { + ind[nb.To]++ + } + } + return ind +} + +// AddNode maps a node in a supergraph to a subgraph node. +// +// Argument p must be an NI in supergraph s.Super. AddNode panics if +// p is not a valid node index of s.Super. +// +// AddNode is idempotent in that it does not add a new node to the subgraph if +// a subgraph node already exists mapped to supergraph node p. +// +// The mapped subgraph NI is returned. +func (s *LabeledDirectedSubgraph) AddNode(p NI) (b NI) { + if int(p) < 0 || int(p) >= s.Super.Order() { + panic(fmt.Sprint("AddNode: NI ", p, " not in supergraph")) + } + if b, ok := s.SubNI[p]; ok { + return b + } + a := s.LabeledDirected.LabeledAdjacencyList + b = NI(len(a)) + s.LabeledDirected.LabeledAdjacencyList = append(a, nil) + s.SuperNI = append(s.SuperNI, p) + s.SubNI[p] = b + return +} + +// AddArc adds an arc to a subgraph. +// +// Arguments fr, to must be NIs in supergraph s.Super. As with AddNode, +// AddArc panics if fr and to are not valid node indexes of s.Super. +// +// The arc specfied by fr, to must exist in s.Super. Further, the number of +// parallel arcs in the subgraph cannot exceed the number of corresponding +// parallel arcs in the supergraph. That is, each arc already added to the +// subgraph counts against the arcs available in the supergraph. If a matching +// arc is not available, AddArc returns an error. +// +// If a matching arc is available, subgraph nodes are added as needed, the +// subgraph arc is added, and the method returns nil. +func (s *LabeledDirectedSubgraph) AddArc(fr NI, to Half) error { + // verify supergraph NIs first, but without adding subgraph nodes just yet. + if int(fr) < 0 || int(fr) >= s.Super.Order() { + panic(fmt.Sprint("AddArc: NI ", fr, " not in supergraph")) + } + if int(to.To) < 0 || int(to.To) >= s.Super.Order() { + panic(fmt.Sprint("AddArc: NI ", to.To, " not in supergraph")) + } + // count existing matching arcs in subgraph + n := 0 + a := s.LabeledDirected.LabeledAdjacencyList + if bf, ok := s.SubNI[fr]; ok { + if bt, ok := s.SubNI[to.To]; ok { + // both NIs already exist in subgraph, need to count arcs + bTo := to + bTo.To = bt + for _, t := range a[bf] { + if t == bTo { + n++ + } + } + } + } + // verify matching arcs are available in supergraph + for _, t := range (*s.Super).LabeledAdjacencyList[fr] { + if t == to { + if n > 0 { + n-- // match existing arc + continue + } + // no more existing arcs need to be matched. nodes can finally + // be added as needed and then the arc can be added. + bf := s.AddNode(fr) + to.To = s.AddNode(to.To) + s.LabeledDirected.LabeledAdjacencyList[bf] = + append(s.LabeledDirected.LabeledAdjacencyList[bf], to) + return nil // success + } + } + return errors.New("arc not available in supergraph") +} + +// InduceList constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument l must be a list of +// NIs in receiver graph g. Receiver g becomes the supergraph of the induced +// subgraph. +// +// Duplicate NIs are allowed in list l. The duplicates are effectively removed +// and only a single corresponding node is created in the subgraph. Subgraph +// NIs are mapped in the order of list l, execpt for ignoring duplicates. +// NIs in l that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *LabeledDirected) InduceList(l []NI) *LabeledDirectedSubgraph { + sub, sup := mapList(l) + return &LabeledDirectedSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + LabeledDirected: LabeledDirected{ + g.LabeledAdjacencyList.induceArcs(sub, sup), + }} +} + +// InduceBits constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument t must be a bitmap +// representing NIs in receiver graph g. Receiver g becomes the supergraph +// of the induced subgraph. NIs in t that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *LabeledDirected) InduceBits(t bits.Bits) *LabeledDirectedSubgraph { + sub, sup := mapBits(t) + return &LabeledDirectedSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + LabeledDirected: LabeledDirected{ + g.LabeledAdjacencyList.induceArcs(sub, sup), + }} +} + +// IsTree identifies trees in directed graphs. +// +// Return value isTree is true if the subgraph reachable from root is a tree. +// Further, return value allTree is true if the entire graph g is reachable +// from root. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) IsTree(root NI) (isTree, allTree bool) { + a := g.LabeledAdjacencyList + v := bits.New(len(a)) + v.SetAll() + var df func(NI) bool + df = func(n NI) bool { + if v.Bit(int(n)) == 0 { + return false + } + v.SetBit(int(n), 0) + for _, to := range a[n] { + if !df(to.To) { + return false + } + } + return true + } + isTree = df(root) + return isTree, isTree && v.AllZeros() +} + +// PageRank computes a significance score for each node of a graph. +// +// The algorithm is credited to Google founders Brin and Lawrence. +// +// Argument d is a damping factor. Reportedly a value of .85 works well. +// Argument n is a number of iterations. Reportedly values of 20 to 50 +// work well. +// +// Returned is the PageRank score for each node of g. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) PageRank(d float64, n int) []float64 { + // Following "PageRank Explained" by Ian Rogers, accessed at + // http://www.cs.princeton.edu/~chazelle/courses/BIB/pagerank.htm + a := g.LabeledAdjacencyList + p0 := make([]float64, len(a)) + p1 := make([]float64, len(a)) + for i := range p0 { + p0[i] = 1 + } + d1 := 1 - d + for ; n > 0; n-- { + for i := range p1 { + p1[i] = d1 + } + for fr, to := range a { + f := d / float64(len(to)) + for _, to := range to { + p1[to.To] += p0[fr] * f + } + } + p0, p1 = p1, p0 + } + return p0 +} + +// StronglyConnectedComponents identifies strongly connected components in +// a directed graph. +// +// The method calls the emit function for each component identified. The +// argument to emit is the node list of a component. The emit function must +// return true for the method to continue identifying components. If emit +// returns false, the method returns immediately. +// +// Note well: The backing slice for the node list passed to emit is reused +// across emit calls. If you need to retain the node list you must copy it. +// +// The components emitted represent a partition of the nodes in g. +// So for example, if the first component emitted has the same length as g +// then it will be the only component and it means the entire graph g is +// strongly connected. +// +// See also Condensation which returns a condensation graph in addition +// to the strongly connected components. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// The algorithm here is by David Pearce. See also alt.SCCPathBased and +// alt.SCCTarjan. +func (g LabeledDirected) StronglyConnectedComponents(emit func([]NI) bool) { + // See Algorithm 3 PEA FIND SCC2(V,E) in "An Improved Algorithm for + // Finding the Strongly Connected Components of a Directed Graph" + // by David J. Pearce. + a := g.LabeledAdjacencyList + rindex := make([]int, len(a)) + var S, scc []NI + index := 1 + c := len(a) - 1 + var visit func(NI) bool + visit = func(v NI) bool { + root := true + rindex[v] = index + index++ + for _, w := range a[v] { + if rindex[w.To] == 0 { + if !visit(w.To) { + return false + } + } + if rindex[w.To] < rindex[v] { + rindex[v] = rindex[w.To] + root = false + } + } + if !root { + S = append(S, v) + return true + } + scc = scc[:0] + index-- + for last := len(S) - 1; last >= 0; last-- { + w := S[last] + if rindex[v] > rindex[w] { + break + } + S = S[:last] + rindex[w] = c + scc = append(scc, w) + index-- + } + rindex[v] = c + c-- + return emit(append(scc, v)) + } + for v := range a { + if rindex[v] == 0 && !visit(NI(v)) { + break + } + } +} + +// Condensation returns strongly connected components and their +// condensation graph. +// +// A condensation represents a directed acyclic graph. +// Components are ordered in a reverse topological ordering. +// +// See also StronglyConnectedComponents, which returns the components only. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) Condensation() (scc [][]NI, cd AdjacencyList) { + a := g.LabeledAdjacencyList + b := make([]NI, len(a)) // backing slice for scc + g.StronglyConnectedComponents(func(c []NI) bool { + n := copy(b, c) + scc = append(scc, b[:n]) + b = b[n:] + return true + }) + cd = make(AdjacencyList, len(scc)) // return value + cond := make([]NI, len(a)) // mapping from g node to cd node + for cn, c := range scc { + for _, n := range c { + cond[n] = NI(cn) // map g node to cd node + } + var tos []NI // list of 'to' nodes + m := bits.New(len(cd)) // tos map + m.SetBit(cn, 1) + for _, n := range c { + for _, to := range a[n] { + if ct := cond[to.To]; m.Bit(int(ct)) == 0 { + m.SetBit(int(ct), 1) + tos = append(tos, ct) + } + } + } + cd[cn] = tos + } + return +} + +// Topological computes a topological ordering of a directed acyclic graph. +// +// For an acyclic graph, return value ordering is a permutation of node numbers +// in topologically sorted order and cycle will be nil. If the graph is found +// to be cyclic, ordering will be nil and cycle will be the path of a found +// cycle. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) Topological() (ordering, cycle []NI) { + i := -1 + return g.dfTopo(func() NI { + i++ + if i < g.Order() { + return NI(i) + } + return -1 + }) +} + +func (g LabeledDirected) dfTopo(f func() NI) (ordering, cycle []NI) { + a := g.LabeledAdjacencyList + ordering = make([]NI, len(a)) + i := len(ordering) + temp := bits.New(len(a)) + perm := bits.New(len(a)) + var cycleFound bool + var cycleStart NI + var df func(NI) + df = func(n NI) { + switch { + case temp.Bit(int(n)) == 1: + cycleFound = true + cycleStart = n + return + case perm.Bit(int(n)) == 1: + return + } + temp.SetBit(int(n), 1) + for _, nb := range a[n] { + df(nb.To) + if cycleFound { + if cycleStart >= 0 { + // a little hack: orderng won't be needed so repurpose the + // slice as cycle. this is read out in reverse order + // as the recursion unwinds. + x := len(ordering) - 1 - len(cycle) + ordering[x] = n + cycle = ordering[x:] + if n == cycleStart { + cycleStart = -1 + } + } + return + } + } + temp.SetBit(int(n), 0) + perm.SetBit(int(n), 1) + i-- + ordering[i] = n + } + for { + n := f() + if n < 0 { + return ordering[i:], nil + } + if perm.Bit(int(n)) == 1 { + continue + } + df(n) + if cycleFound { + return nil, cycle + } + } +} + +// TopologicalKahn computes a topological ordering of a directed acyclic graph. +// +// For an acyclic graph, return value ordering is a permutation of node numbers +// in topologically sorted order and cycle will be nil. If the graph is found +// to be cyclic, ordering will be nil and cycle will be the path of a found +// cycle. +// +// This function is based on the algorithm by Arthur Kahn and requires the +// transpose of g be passed as the argument. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) TopologicalKahn(tr Directed) (ordering, cycle []NI) { + // code follows Wikipedia pseudocode. + var L, S []NI + // rem for "remaining edges," this function makes a local copy of the + // in-degrees and consumes that instead of consuming an input. + rem := make([]int, g.Order()) + for n, fr := range tr.AdjacencyList { + if len(fr) == 0 { + // accumulate "set of all nodes with no incoming edges" + S = append(S, NI(n)) + } else { + // initialize rem from in-degree + rem[n] = len(fr) + } + } + for len(S) > 0 { + last := len(S) - 1 // "remove a node n from S" + n := S[last] + S = S[:last] + L = append(L, n) // "add n to tail of L" + for _, m := range g.LabeledAdjacencyList[n] { + // WP pseudo code reads "for each node m..." but it means for each + // node m *remaining in the graph.* We consume rem rather than + // the graph, so "remaining in the graph" for us means rem[m] > 0. + if rem[m.To] > 0 { + rem[m.To]-- // "remove edge from the graph" + if rem[m.To] == 0 { // if "m has no other incoming edges" + S = append(S, m.To) // "insert m into S" + } + } + } + } + // "If graph has edges," for us means a value in rem is > 0. + for c, in := range rem { + if in > 0 { + // recover cyclic nodes + for _, nb := range g.LabeledAdjacencyList[c] { + if rem[nb.To] > 0 { + cycle = append(cycle, NI(c)) + break + } + } + } + } + if len(cycle) > 0 { + return nil, cycle + } + return L, nil +} + +// TopologicalSubgraph computes a topological ordering of a subgraph of a +// directed acyclic graph. +// +// The subgraph considered is that reachable from the specified node list. +// +// For an acyclic subgraph, return value ordering is a permutation of reachable +// node numbers in topologically sorted order and cycle will be nil. If the +// subgraph is found to be cyclic, ordering will be nil and cycle will be +// the path of a found cycle. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledDirected) TopologicalSubgraph(nodes []NI) (ordering, cycle []NI) { + i := -1 + return g.dfTopo(func() NI { + i++ + if i < len(nodes) { + return nodes[i] + } + return -1 + }) +} + +// TransitiveClosure returns the transitive closure of directed graph g. +// +// The algorithm is Warren's, which works most naturally with an adjacency +// matrix representation. The returned transitive closure is left in this +// adjacency matrix representation. For a graph g of order n, matrix tc +// is returned as a length n slice of length n bits.Bits values, where +// tc[from].Bit(to) == 1 represents an arc of the transitive closure. +func (g LabeledDirected) TransitiveClosure() []bits.Bits { + // construct adjacency matrix + a := g.LabeledAdjacencyList + t := make([]bits.Bits, len(a)) + for n := range t { + tn := bits.New(len(a)) + for _, to := range a[n] { + tn.SetBit(int(to.To), 1) + } + t[n] = tn + } + // above diagonal + for i := 1; i < len(a); i++ { + ti := t[i] + for k := 0; k < i; k++ { + if ti.Bit(k) == 1 { + ti.Or(ti, t[k]) + } + } + } + // below diagonal + for i, ti := range t[:len(a)-1] { + for k := i + 1; k < len(a); k++ { + if ti.Bit(k) == 1 { + ti.Or(ti, t[k]) + } + } + } + return t +} diff --git a/vendor/github.com/soniakeys/graph/doc.go b/vendor/github.com/soniakeys/graph/doc.go new file mode 100644 index 00000000..a30b5897 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/doc.go @@ -0,0 +1,122 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +// Graph algorithms: Dijkstra, A*, Bellman Ford, Floyd Warshall; +// Kruskal and Prim minimal spanning tree; topological sort and DAG longest +// and shortest paths; Eulerian cycle and path; degeneracy and k-cores; +// Bron Kerbosch clique finding; connected components; dominance; and others. +// +// This is a graph library of integer indexes. To use it with application +// data, you associate data with integer indexes, perform searches or other +// operations with the library, and then use the integer index results to refer +// back to your application data. +// +// Thus it does not store application data, pointers to application data, +// or require you to implement an interface on your application data. +// The idea is to keep the library methods fast and lean. +// +// Representation overview +// +// The package defines a type for a node index (NI) which is just an integer +// type. It defines types for a number of number graph representations using +// NI. The fundamental graph type is AdjacencyList, which is the +// common "list of lists" graph representation. It is a list as a slice +// with one element for each node of the graph. Each element is a list +// itself, a list of neighbor nodes, implemented as an NI slice. Methods +// on an AdjacencyList generally work on any representable graph, including +// directed or undirected graphs, simple graphs or multigraphs. +// +// The type Undirected embeds an AdjacencyList adding methods specific to +// undirected graphs. Similarly the type Directed adds methods meaningful +// for directed graphs. +// +// Similar to NI, the type LI is a "label index" which labels a +// node-to-neighbor "arc" or edge. Just as an NI can index arbitrary node +// data, an LI can index arbitrary arc or edge data. A number of algorithms +// use a "weight" associated with an arc. This package does not represent +// weighted arcs explicitly, but instead uses the LI as a more general +// mechanism allowing not only weights but arbitrary data to be associated +// with arcs. While AdjacencyList represents an arc with simply an NI, +// the type LabeledAdjacencyList uses a type that pairs an NI with an LI. +// This type is named Half, for half-arc. (A full arc would represent +// both ends.) Types LabeledDirected and LabeledUndirected embed a +// LabeledAdjacencyList. +// +// In contrast to Half, the type Edge represents both ends of an edge (but +// no label.) The type LabeledEdge adds the label. The type WeightedEdgeList +// bundles a list of LabeledEdges with a WeightFunc. (WeightedEdgeList has +// few methods. It exists primarily to support the Kruskal algorithm.) +// +// FromList is a compact rooted tree (or forest) respresentation. Like +// AdjacencyList and LabeledAdjacencyList, it is a list with one element for +// each node of the graph. Each element contains only a single neighbor +// however, its parent in the tree, the "from" node. +// +// Code generation +// +// A number of methods on AdjacencyList, Directed, and Undirected are +// applicable to LabeledAdjacencyList, LabeledDirected, and LabeledUndirected +// simply by ignoring the label. In these cases code generation provides +// methods on both types from a single source implementation. These methods +// are documented with the sentence "There are equivalent labeled and unlabeled +// versions of this method." +// +// Terminology +// +// This package uses the term "node" rather than "vertex." It uses "arc" +// to mean a directed edge, and uses "from" and "to" to refer to the ends +// of an arc. It uses "start" and "end" to refer to endpoints of a search +// or traversal. +// +// The usage of "to" and "from" is perhaps most strange. In common speech +// they are prepositions, but throughout this package they are used as +// adjectives, for example to refer to the "from node" of an arc or the +// "to node". The type "FromList" is named to indicate it stores a list of +// "from" values. +// +// A "half arc" refers to just one end of an arc, either the to or from end. +// +// Two arcs are "reciprocal" if they connect two distinct nodes n1 and n2, +// one arc leading from n1 to n2 and the other arc leading from n2 to n1. +// Undirected graphs are represented with reciprocal arcs. +// +// A node with an arc to itself represents a "loop." Duplicate arcs, where +// a node has multiple arcs to another node, are termed "parallel arcs." +// A graph with no loops or parallel arcs is "simple." A graph that allows +// parallel arcs is a "multigraph" +// +// The "size" of a graph traditionally means the number of undirected edges. +// This package uses "arc size" to mean the number of arcs in a graph. For an +// undirected graph without loops, arc size is 2 * size. +// +// The "order" of a graph is the number of nodes. An "ordering" though means +// an ordered list of nodes. +// +// A number of graph search algorithms use a concept of arc "weights." +// The sum of arc weights along a path is a "distance." In contrast, the +// number of nodes in a path, including start and end nodes, is the path's +// "length." (Yes, mixing weights and lengths would be nonsense physically, +// but the terms used here are just distinct terms for abstract values. +// The actual meaning to an application is likely to be something else +// entirely and is not relevant within this package.) +// +// Finally, this package documentation takes back the word "object" in some +// places to refer to a Go value, especially a value of a type with methods. +// +// Shortest path searches +// +// This package implements a number of shortest path searches. Most work +// with weighted graphs that are directed or undirected, and with graphs +// that may have loops or parallel arcs. For weighted graphs, "shortest" +// is defined as the path distance (sum of arc weights) with path length +// (number of nodes) breaking ties. If multiple paths have the same minimum +// distance with the same minimum length, search methods are free to return +// any of them. +// +// Algorithm Description +// Dijkstra Non-negative arc weights, single or all paths. +// AStar Non-negative arc weights, heuristic guided, single path. +// BellmanFord Negative arc weights allowed, no negative cycles, all paths. +// DAGPath O(n) algorithm for DAGs, arc weights of any sign. +// FloydWarshall all pairs distances, no negative cycles. +package graph diff --git a/vendor/github.com/soniakeys/graph/fromlist.go b/vendor/github.com/soniakeys/graph/fromlist.go new file mode 100644 index 00000000..ce8c27f4 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/fromlist.go @@ -0,0 +1,498 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +import "github.com/soniakeys/bits" + +// FromList represents a rooted tree (or forest) where each node is associated +// with a half arc identifying an arc "from" another node. +// +// Other terms for this data structure include "parent list", +// "predecessor list", "in-tree", "inverse arborescence", and +// "spaghetti stack." +// +// The Paths member represents the tree structure. Leaves and MaxLen are +// not always needed. Where Leaves is used it serves as a bitmap where +// Leaves.Bit(n) == 1 for each leaf n of the tree. Where MaxLen is used it is +// provided primarily as a convenience for functions that might want to +// anticipate the maximum path length that would be encountered traversing +// the tree. +// +// Various graph search methods use a FromList to returns search results. +// For a start node of a search, From will be -1 and Len will be 1. For other +// nodes reached by the search, From represents a half arc in a path back to +// start and Len represents the number of nodes in the path. For nodes not +// reached by the search, From will be -1 and Len will be 0. +// +// A single FromList can also represent a forest. In this case paths from +// all leaves do not return to a single root node, but multiple root nodes. +// +// While a FromList generally encodes a tree or forest, it is technically +// possible to encode a cyclic graph. A number of FromList methods require +// the receiver to be acyclic. Graph methods documented to return a tree or +// forest will never return a cyclic FromList. In other cases however, +// where a FromList is not known to by cyclic, the Cyclic method can be +// useful to validate the acyclic property. +type FromList struct { + Paths []PathEnd // tree representation + Leaves bits.Bits // leaves of tree + MaxLen int // length of longest path, max of all PathEnd.Len values +} + +// PathEnd associates a half arc and a path length. +// +// A PathEnd list is an element type of FromList. +type PathEnd struct { + From NI // a "from" half arc, the node the arc comes from + Len int // number of nodes in path from start +} + +/* NewFromList could be confusing now with bits also needing allocation. +maybe best to not have this function. Maybe a more useful new would be +one that took a PathEnd slice and intitialized everything including roots +and max len. Maybe its time for a separate []PathEnd type when that's +all that's needed. (and reconsider the name PathEnd) +*/ + +// NewFromList creates a FromList object of given order. +// +// The Paths member is allocated to the specified order n but other members +// are left as zero values. +func NewFromList(n int) FromList { + return FromList{Paths: make([]PathEnd, n)} +} + +// BoundsOk validates the "from" values in the list. +// +// Negative values are allowed as they indicate root nodes. +// +// BoundsOk returns true when all from values are less than len(t). +// Otherwise it returns false and a node with a from value >= len(t). +func (f FromList) BoundsOk() (ok bool, n NI) { + for n, e := range f.Paths { + if int(e.From) >= len(f.Paths) { + return false, NI(n) + } + } + return true, -1 +} + +// CommonStart returns the common start node of minimal paths to a and b. +// +// It returns -1 if a and b cannot be traced back to a common node. +// +// The method relies on populated PathEnd.Len members. Use RecalcLen if +// the Len members are not known to be present and correct. +func (f FromList) CommonStart(a, b NI) NI { + p := f.Paths + if p[a].Len < p[b].Len { + a, b = b, a + } + for bl := p[b].Len; p[a].Len > bl; { + a = p[a].From + if a < 0 { + return -1 + } + } + for a != b { + a = p[a].From + if a < 0 { + return -1 + } + b = p[b].From + } + return a +} + +// Cyclic determines if f contains a cycle, a non-empty path from a node +// back to itself. +// +// Cyclic returns true if g contains at least one cycle. It also returns +// an example of a node involved in a cycle. +// +// Cyclic returns (false, -1) in the normal case where f is acyclic. +// Note that the bool is not an "ok" return. A cyclic FromList is usually +// not okay. +func (f FromList) Cyclic() (cyclic bool, n NI) { + p := f.Paths + vis := bits.New(len(p)) + for i := range p { + path := bits.New(len(p)) + for n := i; vis.Bit(n) == 0; { + vis.SetBit(n, 1) + path.SetBit(n, 1) + if n = int(p[n].From); n < 0 { + break + } + if path.Bit(n) == 1 { + return true, NI(n) + } + } + } + return false, -1 +} + +// IsolatedNodeBits returns a bitmap of isolated nodes in receiver graph f. +// +// An isolated node is one with no arcs going to or from it. +func (f FromList) IsolatedNodes() (iso bits.Bits) { + p := f.Paths + iso = bits.New(len(p)) + iso.SetAll() + for n, e := range p { + if e.From >= 0 { + iso.SetBit(n, 0) + iso.SetBit(int(e.From), 0) + } + } + return +} + +// PathTo decodes a FromList, recovering a single path. +// +// The path is returned as a list of nodes where the first element will be +// a root node and the last element will be the specified end node. +// +// Only the Paths member of the receiver is used. Other members of the +// FromList do not need to be valid, however the MaxLen member can be useful +// for allocating argument p. +// +// Argument p can provide the result slice. If p has capacity for the result +// it will be used, otherwise a new slice is created for the result. +// +// See also function PathTo. +func (f FromList) PathTo(end NI, p []NI) []NI { + return PathTo(f.Paths, end, p) +} + +// PathTo decodes a single path from a PathEnd list. +// +// A PathEnd list is the main data representation in a FromList. See FromList. +// +// PathTo returns a list of nodes where the first element will be +// a root node and the last element will be the specified end node. +// +// Argument p can provide the result slice. If p has capacity for the result +// it will be used, otherwise a new slice is created for the result. +// +// See also method FromList.PathTo. +func PathTo(paths []PathEnd, end NI, p []NI) []NI { + n := paths[end].Len + if n == 0 { + return p[:0] + } + if cap(p) >= n { + p = p[:n] + } else { + p = make([]NI, n) + } + for { + n-- + p[n] = end + if n == 0 { + return p + } + end = paths[end].From + } +} + +// PathToLabeled decodes a FromList, recovering a single path. +// +// The start of the returned path will be a root node of the FromList. +// +// Only the Paths member of the receiver is used. Other members of the +// FromList do not need to be valid, however the MaxLen member can be useful +// for allocating argument p. +// +// Argument p can provide the result slice. If p has capacity for the result +// it will be used, otherwise a new slice is created for the result. +// +// See also function PathTo. +func (f FromList) PathToLabeled(end NI, labels []LI, p []Half) LabeledPath { + n := f.Paths[end].Len - 1 + if n <= 0 { + return LabeledPath{end, p[:0]} + } + if cap(p) >= n { + p = p[:n] + } else { + p = make([]Half, n) + } + for { + n-- + p[n] = Half{To: end, Label: labels[end]} + end = f.Paths[end].From + if n == 0 { + return LabeledPath{end, p} + } + } +} + +// Preorder traverses a FromList in preorder. +// +// Nodes are visited in order such that for any node n with from node fr, +// fr is visited before n. Where f represents a tree, the visit ordering +// corresponds to a preordering, or depth first traversal of the tree. +// Where f represents a forest, the preorderings of the trees can be +// intermingled. +// +// Leaves must be set correctly first. Use RecalcLeaves if leaves are not +// known to be set correctly. FromList f cannot be cyclic. +// +// Traversal continues while visitor function v returns true. It terminates +// if v returns false. Preorder returns true if it completes without v +// returning false. Preorder returns false if traversal is terminated by v +// returning false. +func (f FromList) Preorder(v func(NI) bool) bool { + p := f.Paths + done := bits.New(len(p)) + var df func(NI) bool + df = func(n NI) bool { + done.SetBit(int(n), 1) + if fr := p[n].From; fr >= 0 && done.Bit(int(fr)) == 0 { + df(fr) + } + return v(n) + } + for n := range f.Paths { + p[n].Len = 0 + } + return f.Leaves.IterateOnes(func(n int) bool { + return df(NI(n)) + }) +} + +// RecalcLeaves recomputes the Leaves member of f. +func (f *FromList) RecalcLeaves() { + p := f.Paths + lv := &f.Leaves + if lv.Num != len(p) { + *lv = bits.New(len(p)) + } + lv.SetAll() + for n := range f.Paths { + if fr := p[n].From; fr >= 0 { + lv.SetBit(int(fr), 0) + } + } +} + +// RecalcLen recomputes Len for each path end, and recomputes MaxLen. +// +// RecalcLen relies on the Leaves member being valid. If it is not known +// to be valid, call RecalcLeaves before calling RecalcLen. +// +// RecalcLen will panic if the FromList is cyclic. Use the Cyclic method +// if needed to verify that the FromList is acyclic. +func (f *FromList) RecalcLen() { + p := f.Paths + var setLen func(NI) int + setLen = func(n NI) int { + switch { + case p[n].Len > 0: + return p[n].Len + case p[n].From < 0: + p[n].Len = 1 + return 1 + } + l := 1 + setLen(p[n].From) + p[n].Len = l + return l + } + for n := range f.Paths { + p[n].Len = 0 + } + f.MaxLen = 0 + f.Leaves.IterateOnes(func(n int) bool { + if l := setLen(NI(n)); l > f.MaxLen { + f.MaxLen = l + } + return true + }) +} + +// ReRoot reorients the tree containing n to make n the root node. +// +// It keeps the tree connected by "reversing" the path from n to the old root. +// +// After ReRoot, the Leaves and Len members are invalid. +// Call RecalcLeaves or RecalcLen as needed. +func (f *FromList) ReRoot(n NI) { + p := f.Paths + fr := p[n].From + if fr < 0 { + return + } + p[n].From = -1 + for { + ff := p[fr].From + p[fr].From = n + if ff < 0 { + return + } + n = fr + fr = ff + } +} + +// Root finds the root of a node in a FromList. +func (f FromList) Root(n NI) NI { + for p := f.Paths; ; { + fr := p[n].From + if fr < 0 { + return n + } + n = fr + } +} + +// Transpose constructs the directed graph corresponding to FromList f +// but with arcs in the opposite direction. That is, from roots toward leaves. +// +// If non-nil argrument roots is passed, Transpose populates it as roots of +// the resulting forest and returns nRoots as a count of the roots. +// +// The method relies only on the From member of f.Paths. Other members of +// the FromList are not used. +func (f FromList) Transpose(roots *bits.Bits) (forest Directed, nRoots int) { + p := f.Paths + g := make(AdjacencyList, len(p)) + if roots != nil { + nRoots = len(p) + if roots.Num != nRoots { + *roots = bits.New(nRoots) + } + roots.SetAll() + } + for i, e := range p { + if e.From == -1 { + continue + } + g[e.From] = append(g[e.From], NI(i)) + if roots != nil && roots.Bit(i) == 1 { + roots.SetBit(i, 0) + nRoots-- + } + } + return Directed{g}, nRoots +} + +// TransposeLabeled constructs the labeled directed graph corresponding +// to FromList f but with arcs in the opposite direction. That is, from +// roots toward leaves. +// +// The argument labels can be nil. In this case labels are generated matching +// the path indexes. This corresponds to the "to", or child node. +// +// If labels is non-nil, it must be the same length as t.Paths and is used +// to look up label numbers by the path index. +// +// If non-nil argrument roots is passed, Transpose populates it as roots of +// the resulting forest and returns nRoots as a count of the roots. +// +// The method relies only on the From member of f.Paths. Other members of +// the FromList are not used. +func (f FromList) TransposeLabeled(labels []LI, roots *bits.Bits) (forest LabeledDirected, nRoots int) { + p := f.Paths + g := make(LabeledAdjacencyList, len(p)) + if roots != nil { + nRoots = len(p) + if roots.Num != nRoots { + *roots = bits.New(nRoots) + } + roots.SetAll() + } + for i, p := range f.Paths { + if p.From == -1 { + continue + } + l := LI(i) + if labels != nil { + l = labels[i] + } + g[p.From] = append(g[p.From], Half{NI(i), l}) + if roots != nil && roots.Bit(i) == 1 { + roots.SetBit(i, 0) + nRoots-- + } + } + return LabeledDirected{g}, nRoots +} + +// Undirected constructs the undirected graph corresponding to FromList f. +// +// The resulting graph will be a tree or forest. +// +// If non-nil argrument roots is passed, Transpose populates it as roots of +// the resulting forest and returns nRoots as a count of the roots. +// +// The method relies only on the From member of f.Paths. Other members of +// the FromList are not used. +func (f FromList) Undirected(roots *bits.Bits) (forest Undirected, nRoots int) { + p := f.Paths + g := make(AdjacencyList, len(p)) + if roots != nil { + nRoots = len(p) + if roots.Num != nRoots { + *roots = bits.New(nRoots) + } + roots.SetAll() + } + for i, e := range p { + if e.From == -1 { + continue + } + g[i] = append(g[i], e.From) + g[e.From] = append(g[e.From], NI(i)) + if roots != nil && roots.Bit(i) == 1 { + roots.SetBit(i, 0) + nRoots-- + } + } + return Undirected{g}, nRoots +} + +// LabeledUndirected constructs the labeled undirected graph corresponding +// to FromList f. +// +// The resulting graph will be a tree or forest. +// +// The argument labels can be nil. In this case labels are generated matching +// the path indexes. This corresponds to the "to", or child node. +// +// If labels is non-nil, it must be the same length as t.Paths and is used +// to look up label numbers by the path index. +// +// If non-nil argrument roots is passed, LabeledUndirected populates it as +// roots of the resulting forest and returns nRoots as a count of the roots. +// +// The method relies only on the From member of f.Paths. Other members of +// the FromList are not used. +func (f FromList) LabeledUndirected(labels []LI, roots *bits.Bits) (forest LabeledUndirected, nRoots int) { + p := f.Paths + g := make(LabeledAdjacencyList, len(p)) + if roots != nil { + nRoots = len(p) + if roots.Num != nRoots { + *roots = bits.New(nRoots) + } + roots.SetAll() + } + for i, p := range f.Paths { + if p.From == -1 { + continue + } + l := LI(i) + if labels != nil { + l = labels[i] + } + g[i] = append(g[i], Half{p.From, l}) + g[p.From] = append(g[p.From], Half{NI(i), l}) + if roots != nil && roots.Bit(i) == 1 { + roots.SetBit(i, 0) + nRoots-- + } + } + return LabeledUndirected{g}, nRoots +} diff --git a/vendor/github.com/soniakeys/graph/go.mod b/vendor/github.com/soniakeys/graph/go.mod new file mode 100644 index 00000000..67bf8e02 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/go.mod @@ -0,0 +1,3 @@ +module "github.com/soniakeys/graph" + +require "github.com/soniakeys/bits" v1.0.0 diff --git a/vendor/github.com/soniakeys/graph/graph.go b/vendor/github.com/soniakeys/graph/graph.go new file mode 100644 index 00000000..c21e48a4 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/graph.go @@ -0,0 +1,767 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +import ( + "bytes" + "errors" + "fmt" + "math" + "reflect" + "text/template" + + "github.com/soniakeys/bits" +) + +// graph.go contains type definitions for all graph types and components. +// Also, go generate directives for source transformations. +// +// For readability, the types are defined in a dependency order: +// +// NI +// AdjacencyList +// Directed +// Undirected +// Bipartite +// Subgraph +// DirectedSubgraph +// UndirectedSubgraph +// LI +// Half +// fromHalf +// LabeledAdjacencyList +// LabeledDirected +// LabeledUndirected +// LabeledBipartite +// LabeledSubgraph +// LabeledDirectedSubgraph +// LabeledUndirectedSubgraph +// Edge +// LabeledEdge +// LabeledPath +// WeightFunc +// WeightedEdgeList +// TraverseOption + +//go:generate cp adj_cg.go adj_RO.go +//go:generate gofmt -r "LabeledAdjacencyList -> AdjacencyList" -w adj_RO.go +//go:generate gofmt -r "n.To -> n" -w adj_RO.go +//go:generate gofmt -r "Half -> NI" -w adj_RO.go +//go:generate gofmt -r "LabeledSubgraph -> Subgraph" -w adj_RO.go + +//go:generate cp dir_cg.go dir_RO.go +//go:generate gofmt -r "LabeledDirected -> Directed" -w dir_RO.go +//go:generate gofmt -r "LabeledDirectedSubgraph -> DirectedSubgraph" -w dir_RO.go +//go:generate gofmt -r "LabeledAdjacencyList -> AdjacencyList" -w dir_RO.go +//go:generate gofmt -r "labEulerian -> eulerian" -w dir_RO.go +//go:generate gofmt -r "newLabEulerian -> newEulerian" -w dir_RO.go +//go:generate gofmt -r "Half{n, -1} -> n" -w dir_RO.go +//go:generate gofmt -r "n.To -> n" -w dir_RO.go +//go:generate gofmt -r "Half -> NI" -w dir_RO.go + +//go:generate cp undir_cg.go undir_RO.go +//go:generate gofmt -r "LabeledUndirected -> Undirected" -w undir_RO.go +//go:generate gofmt -r "LabeledBipartite -> Bipartite" -w undir_RO.go +//go:generate gofmt -r "LabeledUndirectedSubgraph -> UndirectedSubgraph" -w undir_RO.go +//go:generate gofmt -r "LabeledAdjacencyList -> AdjacencyList" -w undir_RO.go +//go:generate gofmt -r "newLabEulerian -> newEulerian" -w undir_RO.go +//go:generate gofmt -r "Half{n, -1} -> n" -w undir_RO.go +//go:generate gofmt -r "n.To -> n" -w undir_RO.go +//go:generate gofmt -r "Half -> NI" -w undir_RO.go + +// An AdjacencyList represents a graph as a list of neighbors for each node. +// The "node ID" of a node is simply it's slice index in the AdjacencyList. +// For an AdjacencyList g, g[n] represents arcs going from node n to nodes +// g[n]. +// +// Adjacency lists are inherently directed but can be used to represent +// directed or undirected graphs. See types Directed and Undirected. +type AdjacencyList [][]NI + +// Directed represents a directed graph. +// +// Directed methods generally rely on the graph being directed, specifically +// that arcs do not have reciprocals. +type Directed struct { + AdjacencyList // embedded to include AdjacencyList methods +} + +// Undirected represents an undirected graph. +// +// In an undirected graph, for each arc between distinct nodes there is also +// a reciprocal arc, an arc in the opposite direction. Loops do not have +// reciprocals. +// +// Undirected methods generally rely on the graph being undirected, +// specifically that every arc between distinct nodes has a reciprocal. +type Undirected struct { + AdjacencyList // embedded to include AdjacencyList methods +} + +// Bipartite represents a bipartite graph. +// +// In a bipartite graph, nodes are partitioned into two sets, or +// "colors," such that every edge in the graph goes from one set to the +// other. +// +// Member Color represents the partition with a bitmap of length the same +// as the number of nodes in the graph. For convenience N0 stores the number +// of zero bits in Color. +// +// To construct a Bipartite object, if you can easily or efficiently use +// available information to construct the Color member, then you should do +// this and construct a Bipartite object with a Go struct literal. +// +// If partition information is not readily available, see the constructor +// Undirected.Bipartite. +// +// Alternatively, in some cases where the graph may have multiple connected +// components, the lower level Undirected.BipartiteComponent can be used to +// control color assignment by component. +type Bipartite struct { + Undirected + Color bits.Bits + N0 int +} + +// Subgraph represents a subgraph mapped to a supergraph. +// +// The subgraph is the embedded AdjacencyList and so the Subgraph type inherits +// all methods of Adjacency list. +// +// The embedded subgraph mapped relative to a specific supergraph, member +// Super. A subgraph may have fewer nodes than its supergraph. +// Each node of the subgraph must map to a distinct node of the supergraph. +// +// The mapping giving the supergraph node for a given subgraph node is +// represented by member SuperNI, a slice parallel to the the subgraph. +// +// The mapping in the other direction, giving a subgraph NI for a given +// supergraph NI, is represented with map SubNI. +// +// Multiple Subgraphs can be created relative to a single supergraph. +// The Subgraph type represents a mapping to only a single supergraph however. +// +// See graph methods InduceList and InduceBits for construction of +// node-induced subgraphs. +// +// Alternatively an empty subgraph can be constructed with InduceList(nil). +// Arbitrary subgraphs can then be built up with methods AddNode and AddArc. +type Subgraph struct { + AdjacencyList // the subgraph + Super *AdjacencyList // the supergraph + SubNI map[NI]NI // subgraph NIs, indexed by supergraph NIs + SuperNI []NI // supergraph NIs indexed by subgraph NIs +} + +// DirectedSubgraph represents a subgraph mapped to a supergraph. +// +// See additional doc at Subgraph type. +type DirectedSubgraph struct { + Directed + Super *Directed + SubNI map[NI]NI + SuperNI []NI +} + +// UndirectedSubgraph represents a subgraph mapped to a supergraph. +// +// See additional doc at Subgraph type. +type UndirectedSubgraph struct { + Undirected + Super *Undirected + SubNI map[NI]NI + SuperNI []NI +} + +// LI is a label integer, used for associating labels with arcs. +type LI int32 + +// Half is a half arc, representing a labeled arc and the "neighbor" node +// that the arc leads to. +// +// Halfs can be composed to form a labeled adjacency list. +type Half struct { + To NI // node ID, usable as a slice index + Label LI // half-arc ID for application data, often a weight +} + +// fromHalf is a half arc, representing a labeled arc and the "neighbor" node +// that the arc originates from. +// +// This used internally in a couple of places. It used to be exported but is +// not currently needed anwhere in the API. +type fromHalf struct { + From NI + Label LI +} + +// A LabeledAdjacencyList represents a graph as a list of neighbors for each +// node, connected by labeled arcs. +// +// Arc labels are not necessarily unique arc IDs. Different arcs can have +// the same label. +// +// Arc labels are commonly used to assocate a weight with an arc. Arc labels +// are general purpose however and can be used to associate arbitrary +// information with an arc. +// +// Methods implementing weighted graph algorithms will commonly take a +// weight function that turns a label int into a float64 weight. +// +// If only a small amount of information -- such as an integer weight or +// a single printable character -- needs to be associated, it can sometimes +// be possible to encode the information directly into the label int. For +// more generality, some lookup scheme will be needed. +// +// In an undirected labeled graph, reciprocal arcs must have identical labels. +// Note this does not preclude parallel arcs with different labels. +type LabeledAdjacencyList [][]Half + +// LabeledDirected represents a directed labeled graph. +// +// This is the labeled version of Directed. See types LabeledAdjacencyList +// and Directed. +type LabeledDirected struct { + LabeledAdjacencyList // embedded to include LabeledAdjacencyList methods +} + +// LabeledUndirected represents an undirected labeled graph. +// +// This is the labeled version of Undirected. See types LabeledAdjacencyList +// and Undirected. +type LabeledUndirected struct { + LabeledAdjacencyList // embedded to include LabeledAdjacencyList methods +} + +// LabeledBipartite represents a bipartite graph. +// +// In a bipartite graph, nodes are partitioned into two sets, or +// "colors," such that every edge in the graph goes from one set to the +// other. +// +// Member Color represents the partition with a bitmap of length the same +// as the number of nodes in the graph. For convenience N0 stores the number +// of zero bits in Color. +// +// To construct a LabeledBipartite object, if you can easily or efficiently use +// available information to construct the Color member, then you should do +// this and construct a LabeledBipartite object with a Go struct literal. +// +// If partition information is not readily available, see the constructor +// Undirected.LabeledBipartite. +// +// Alternatively, in some cases where the graph may have multiple connected +// components, the lower level LabeledUndirected.BipartiteComponent can be used +// to control color assignment by component. +type LabeledBipartite struct { + LabeledUndirected + Color bits.Bits + N0 int +} + +// LabeledSubgraph represents a subgraph mapped to a supergraph. +// +// See additional doc at Subgraph type. +type LabeledSubgraph struct { + LabeledAdjacencyList + Super *LabeledAdjacencyList + SubNI map[NI]NI + SuperNI []NI +} + +// LabeledDirectedSubgraph represents a subgraph mapped to a supergraph. +// +// See additional doc at Subgraph type. +type LabeledDirectedSubgraph struct { + LabeledDirected + Super *LabeledDirected + SubNI map[NI]NI + SuperNI []NI +} + +// LabeledUndirectedSubgraph represents a subgraph mapped to a supergraph. +// +// See additional doc at Subgraph type. +type LabeledUndirectedSubgraph struct { + LabeledUndirected + Super *LabeledUndirected + SubNI map[NI]NI + SuperNI []NI +} + +// Edge is an undirected edge between nodes N1 and N2. +type Edge struct{ N1, N2 NI } + +// LabeledEdge is an undirected edge with an associated label. +type LabeledEdge struct { + Edge + LI +} + +// LabeledPath is a start node and a path of half arcs leading from start. +type LabeledPath struct { + Start NI + Path []Half +} + +// Distance returns total path distance given WeightFunc w. +func (p LabeledPath) Distance(w WeightFunc) float64 { + d := 0. + for _, h := range p.Path { + d += w(h.Label) + } + return d +} + +// WeightFunc returns a weight for a given label. +// +// WeightFunc is a parameter type for various search functions. The intent +// is to return a weight corresponding to an arc label. The name "weight" +// is an abstract term. An arc "weight" will typically have some application +// specific meaning other than physical weight. +type WeightFunc func(label LI) (weight float64) + +// WeightedEdgeList is a graph representation. +// +// It is a labeled edge list, with an associated weight function to return +// a weight given an edge label. +// +// Also associated is the order, or number of nodes of the graph. +// All nodes occurring in the edge list must be strictly less than Order. +// +// WeigtedEdgeList sorts by weight, obtained by calling the weight function. +// If weight computation is expensive, consider supplying a cached or +// memoized version. +type WeightedEdgeList struct { + Order int + WeightFunc + Edges []LabeledEdge +} + +// DistanceMatrix constructs a distance matrix corresponding to the weighted +// edges of l. +// +// An edge n1, n2 with WeightFunc return w is represented by both +// d[n1][n2] == w and d[n2][n1] = w. In case of parallel edges, the lowest +// weight is stored. The distance from any node to itself d[n][n] is 0, unless +// the node has a loop with a negative weight. If g has no edge between n1 and +// distinct n2, +Inf is stored for d[n1][n2] and d[n2][n1]. +// +// The returned DistanceMatrix is suitable for DistanceMatrix.FloydWarshall. +func (l WeightedEdgeList) DistanceMatrix() (d DistanceMatrix) { + d = newDM(l.Order) + for _, e := range l.Edges { + n1 := e.Edge.N1 + n2 := e.Edge.N2 + wt := l.WeightFunc(e.LI) + // < to pick min of parallel arcs (also nicely ignores NaN) + if wt < d[n1][n2] { + d[n1][n2] = wt + d[n2][n1] = wt + } + } + return +} + +// A DistanceMatrix is a square matrix representing some distance between +// nodes of a graph. If the graph is directected, d[from][to] represents +// some distance from node 'from' to node 'to'. Depending on context, the +// distance may be an arc weight or path distance. A value of +Inf typically +// means no arc or no path between the nodes. +type DistanceMatrix [][]float64 + +// little helper function, makes a blank distance matrix for FloydWarshall. +// could be exported? +func newDM(n int) DistanceMatrix { + inf := math.Inf(1) + d := make(DistanceMatrix, n) + for i := range d { + di := make([]float64, n) + for j := range di { + di[j] = inf + } + di[i] = 0 + d[i] = di + } + return d +} + +// FloydWarshall finds all pairs shortest distances for a weighted graph +// without negative cycles. +// +// It operates on a distance matrix representing arcs of a graph and +// destructively replaces arc weights with shortest path distances. +// +// In receiver d, d[fr][to] will be the shortest distance from node +// 'fr' to node 'to'. An element value of +Inf means no path exists. +// Any diagonal element < 0 indicates a negative cycle exists. +// +// See DistanceMatrix constructor methods of LabeledAdjacencyList and +// WeightedEdgeList for suitable inputs. +func (d DistanceMatrix) FloydWarshall() { + for k, dk := range d { + for _, di := range d { + dik := di[k] + for j := range d { + if d2 := dik + dk[j]; d2 < di[j] { + di[j] = d2 + } + } + } + } +} + +// PathMatrix is a return type for FloydWarshallPaths. +// +// It encodes all pairs shortest paths. +type PathMatrix [][]NI + +// Path returns a shortest path from node start to end. +// +// Argument p is truncated, appended to, and returned as the result. +// Thus the underlying allocation is reused if possible. +// If there is no path from start to end, p is returned truncated to +// zero length. +// +// If receiver m is not a valid populated PathMatrix as returned by +// FloydWarshallPaths, behavior is undefined and a panic is likely. +func (m PathMatrix) Path(start, end NI, p []NI) []NI { + p = p[:0] + for { + p = append(p, start) + if start == end { + return p + } + start = m[start][end] + if start < 0 { + return p[:0] + } + } +} + +// FloydWarshallPaths finds all pairs shortest paths for a weighted graph +// without negative cycles. +// +// It operates on a distance matrix representing arcs of a graph and +// destructively replaces arc weights with shortest path distances. +// +// In receiver d, d[fr][to] will be the shortest distance from node +// 'fr' to node 'to'. An element value of +Inf means no path exists. +// Any diagonal element < 0 indicates a negative cycle exists. +// +// The return value encodes the paths. See PathMatrix.Path. +// +// See DistanceMatrix constructor methods of LabeledAdjacencyList and +// WeightedEdgeList for suitable inputs. +// +// See also similar method FloydWarshallFromLists which has a richer +// return value. +func (d DistanceMatrix) FloydWarshallPaths() PathMatrix { + m := make(PathMatrix, len(d)) + inf := math.Inf(1) + for i, di := range d { + mi := make([]NI, len(d)) + for j, dij := range di { + if dij == inf { + mi[j] = -1 + } else { + mi[j] = NI(j) + } + } + m[i] = mi + } + for k, dk := range d { + for i, di := range d { + mi := m[i] + dik := di[k] + for j := range d { + if d2 := dik + dk[j]; d2 < di[j] { + di[j] = d2 + mi[j] = mi[k] + } + } + } + } + return m +} + +// FloydWarshallFromLists finds all pairs shortest paths for a weighted +// graph without negative cycles. +// +// It operates on a distance matrix representing arcs of a graph and +// destructively replaces arc weights with shortest path distances. +// +// In receiver d, d[fr][to] will be the shortest distance from node +// 'fr' to node 'to'. An element value of +Inf means no path exists. +// Any diagonal element < 0 indicates a negative cycle exists. +// +// The return value encodes the paths. The FromLists are fully populated +// with Leaves and Len values. See for example FromList.PathTo for +// extracting paths. Note though that for i'th FromList of the return +// value, PathTo(j) will return the path from j's root, which will not +// be i in the case that there is no path from i to j. You must check +// the first node of the path to see if it is i. If not, there is no +// path from i to j. See example. +// +// See DistanceMatrix constructor methods of LabeledAdjacencyList and +// WeightedEdgeList for suitable inputs. +// +// See also similar method FloydWarshallPaths, which has a lighter +// weight return value. +func (d DistanceMatrix) FloydWarshallFromLists() []FromList { + l := make([]FromList, len(d)) + inf := math.Inf(1) + for i, di := range d { + li := NewFromList(len(d)) + p := li.Paths + for j, dij := range di { + if i == j || dij == inf { + p[j] = PathEnd{From: -1} + } else { + p[j] = PathEnd{From: NI(i)} + } + } + l[i] = li + } + for k, dk := range d { + pk := l[k].Paths + for i, di := range d { + dik := di[k] + pi := l[i].Paths + for j := range d { + if d2 := dik + dk[j]; d2 < di[j] { + di[j] = d2 + pi[j] = pk[j] + } + } + } + } + for _, li := range l { + li.RecalcLeaves() + li.RecalcLen() + } + return l +} + +// AddEdge adds an edge to a subgraph. +// +// For argument e, e.N1 and e.N2 must be NIs in supergraph s.Super. As with +// AddNode, AddEdge panics if e.N1 and e.N2 are not valid node indexes of +// s.Super. +// +// Edge e must exist in s.Super. Further, the number of +// parallel edges in the subgraph cannot exceed the number of corresponding +// parallel edges in the supergraph. That is, each edge already added to the +// subgraph counts against the edges available in the supergraph. If a matching +// edge is not available, AddEdge returns an error. +// +// If a matching edge is available, subgraph nodes are added as needed, the +// subgraph edge is added, and the method returns nil. +func (s *UndirectedSubgraph) AddEdge(n1, n2 NI) error { + // verify supergraph NIs first, but without adding subgraph nodes just yet. + if int(n1) < 0 || int(n1) >= s.Super.Order() { + panic(fmt.Sprint("AddEdge: NI ", n1, " not in supergraph")) + } + if int(n2) < 0 || int(n2) >= s.Super.Order() { + panic(fmt.Sprint("AddEdge: NI ", n2, " not in supergraph")) + } + // count existing matching edges in subgraph + n := 0 + a := s.Undirected.AdjacencyList + if b1, ok := s.SubNI[n1]; ok { + if b2, ok := s.SubNI[n2]; ok { + // both NIs already exist in subgraph, need to count edges + for _, t := range a[b1] { + if t == b2 { + n++ + } + } + if b1 != b2 { + // verify reciprocal arcs exist + r := 0 + for _, t := range a[b2] { + if t == b1 { + r++ + } + } + if r < n { + n = r + } + } + } + } + // verify matching edges are available in supergraph + m := 0 + for _, t := range (*s.Super).AdjacencyList[n1] { + if t == n2 { + if m == n { + goto r // arc match after all existing arcs matched + } + m++ + } + } + return errors.New("edge not available in supergraph") +r: + if n1 != n2 { + // verify reciprocal arcs + m = 0 + for _, t := range (*s.Super).AdjacencyList[n2] { + if t == n1 { + if m == n { + goto good + } + m++ + } + } + return errors.New("edge not available in supergraph") + } +good: + // matched enough edges. nodes can finally + // be added as needed and then the edge can be added. + b1 := s.AddNode(n1) + b2 := s.AddNode(n2) + s.Undirected.AddEdge(b1, b2) + return nil // success +} + +// AddEdge adds an edge to a subgraph. +// +// For argument e, e.N1 and e.N2 must be NIs in supergraph s.Super. As with +// AddNode, AddEdge panics if e.N1 and e.N2 are not valid node indexes of +// s.Super. +// +// Edge e must exist in s.Super with label l. Further, the number of +// parallel edges in the subgraph cannot exceed the number of corresponding +// parallel edges in the supergraph. That is, each edge already added to the +// subgraph counts against the edges available in the supergraph. If a matching +// edge is not available, AddEdge returns an error. +// +// If a matching edge is available, subgraph nodes are added as needed, the +// subgraph edge is added, and the method returns nil. +func (s *LabeledUndirectedSubgraph) AddEdge(e Edge, l LI) error { + // verify supergraph NIs first, but without adding subgraph nodes just yet. + if int(e.N1) < 0 || int(e.N1) >= s.Super.Order() { + panic(fmt.Sprint("AddEdge: NI ", e.N1, " not in supergraph")) + } + if int(e.N2) < 0 || int(e.N2) >= s.Super.Order() { + panic(fmt.Sprint("AddEdge: NI ", e.N2, " not in supergraph")) + } + // count existing matching edges in subgraph + n := 0 + a := s.LabeledUndirected.LabeledAdjacencyList + if b1, ok := s.SubNI[e.N1]; ok { + if b2, ok := s.SubNI[e.N2]; ok { + // both NIs already exist in subgraph, need to count edges + h := Half{b2, l} + for _, t := range a[b1] { + if t == h { + n++ + } + } + if b1 != b2 { + // verify reciprocal arcs exist + r := 0 + h.To = b1 + for _, t := range a[b2] { + if t == h { + r++ + } + } + if r < n { + n = r + } + } + } + } + // verify matching edges are available in supergraph + m := 0 + h := Half{e.N2, l} + for _, t := range (*s.Super).LabeledAdjacencyList[e.N1] { + if t == h { + if m == n { + goto r // arc match after all existing arcs matched + } + m++ + } + } + return errors.New("edge not available in supergraph") +r: + if e.N1 != e.N2 { + // verify reciprocal arcs + m = 0 + h.To = e.N1 + for _, t := range (*s.Super).LabeledAdjacencyList[e.N2] { + if t == h { + if m == n { + goto good + } + m++ + } + } + return errors.New("edge not available in supergraph") + } +good: + // matched enough edges. nodes can finally + // be added as needed and then the edge can be added. + n1 := s.AddNode(e.N1) + n2 := s.AddNode(e.N2) + s.LabeledUndirected.AddEdge(Edge{n1, n2}, l) + return nil // success +} + +// utility function called from all of the InduceList methods. +func mapList(l []NI) (sub map[NI]NI, sup []NI) { + sub = map[NI]NI{} + // one pass to collect unique NIs + for _, p := range l { + sub[NI(p)] = -1 + } + if len(sub) == len(l) { // NIs in l are unique + sup = append([]NI{}, l...) // just copy them + for b, p := range l { + sub[p] = NI(b) // and fill in map + } + } else { // NIs in l not unique + sup = make([]NI, 0, len(sub)) + for _, p := range l { // preserve ordering of first occurrences in l + if sub[p] < 0 { + sub[p] = NI(len(sup)) + sup = append(sup, p) + } + } + } + return +} + +// utility function called from all of the InduceBits methods. +func mapBits(t bits.Bits) (sub map[NI]NI, sup []NI) { + sup = make([]NI, 0, t.OnesCount()) + sub = make(map[NI]NI, cap(sup)) + t.IterateOnes(func(n int) bool { + sub[NI(n)] = NI(len(sup)) + sup = append(sup, NI(n)) + return true + }) + return +} + +// OrderMap formats maps for testable examples. +// +// OrderMap provides simple, no-frills formatting of maps in sorted order, +// convenient in some cases for output of testable examples. +func OrderMap(m interface{}) string { + // in particular exclude slices, which template would happily accept but + // which would probably represent a coding mistake + if reflect.TypeOf(m).Kind() != reflect.Map { + panic("not a map") + } + t := template.Must(template.New("").Parse( + `map[{{range $k, $v := .}}{{$k}}:{{$v}} {{end}}]`)) + var b bytes.Buffer + if err := t.Execute(&b, m); err != nil { + panic(err) + } + return b.String() +} diff --git a/vendor/github.com/soniakeys/graph/hacking.adoc b/vendor/github.com/soniakeys/graph/hacking.adoc new file mode 100644 index 00000000..876d33ff --- /dev/null +++ b/vendor/github.com/soniakeys/graph/hacking.adoc @@ -0,0 +1,135 @@ += Hacking + +== Get, install +Basic use of the package is just go get, or git clone; go install. There are +no dependencies outside the standard library. + +== Build +CI is currently on travis-ci.org. + +The build runs go vet with a few exceptions for things I'm not a big fan of. + +https://github.com/client9/misspell has been valuable. + +Also I wrote https://github.com/soniakeys/vetc to validate that each source +file has copyright/license statement. + +Then, it’s not in the ci script, but I wrote https://github.com/soniakeys/rcv +to put coverage stats in the readme. Maybe it could be commit hook or +something but for now I’ll try just running it manually now and then. + +Go fmt is not in the ci script, but I have at least one editor set up to run +it on save, so code should stay formatted pretty well. + +== Examples with random output +The math/rand generators with constant seeds used to give consistent numbers +across Go versions and so some examples relied on this. Sometime after Go 1.9 +though the numbers changed. The technique for now is to go ahead and write +those examples, get them working, then change the `// Output:` line to +`// Random output:`. This keeps them showing in go doc but keeps them from +being run by go test. This works for now. It might be revisited at some +point. + +== Plans +The primary to-do list is the issue tracker on Github. + +== Direction, focus, features +The project started with no real goal or purpose, just as a place for some code +that might be useful. Here are some elements that characterize the direction. + +* The focus has been on algorithms on adjacency lists. That is, adjacency list + is the fundamental representation for most implemented algorithms. There are + many other interesting representations, many reasons to use them, but + adjacency list is common in literature and practice. It has been useful to + focus on this data representation, at first anyway. + +* The focus has been on single threaded algorithms. Again, there is much new + and interesting work being done with concurrent, parallel, and distributed + graph algorithms, and Go might be an excellent language to implement some of + these algorithms. But as a preliminary step, more traditional + single-threaded algorithms are implemented. + +* The focus has been on static finite graphs. Again there is much interesting + work in online algorithms, dynamic graphs, and infinite graphs, but these + are not generally considered here. + +* Algorithms selected for implementation are generally ones commonly appearing + in beginning graph theory discussions and in general purpose graph libraries + in other programming languages. With these as drivers, there's a big risk + developing a library of curiosities and academic exercises rather than a + library of practical utility. But well, it's a start. The hope is that + there are some practical drivers behind graph theory and behind other graph + libraries. + +* There is active current research going on in graph algorithm development. + One goal for this library is to implement newer and faster algorithms. + In some cases where it seems not too much work, older/classic/traditional + algorithms may be implemented for comparison. These generally go in the + alt subdirectory. + +== General principles +* The API is rather low level. + +* Slices instead of maps. Maps are pretty efficient, and the property of + unique keys can be useful, But slices are still faster and more efficient, + and the unique key property is not always needed or wanted. The Adjacency + list implementation of this library is all done in slices. Slices are used + in algorithms where possible, in preference to maps. Maps are still used in + some cases where uniqueness is needed. + +* Interfaces not generally used. Algorithms are implemented directly on + concrete data types and not on interfaces describing the capabilities of + the data types. The abstraction of interfaces is a nice match to graph + theory and the convenience of running graph algorithms on any type that + implements an interface is appealing, but the costs seem too high to me. + Slices are rich with capababilites that get hidden behind interfaces and + direct slice manipulation is always faster than going through interfaces. + An impedance for programs using the library is that they will generally + have to implement a mapping from slice indexes to their application data, + often including for example, some other form of node ID. This seems fair + to push this burden outside the graph library; the library cannot know + the needs of this mapping. + +* Bitsets are widely used, particularly to store one bit of information per + node of a graph. I used math/big at first but then moved to a dense bitset + of my own. Yes, I considered other third-party bitsets but had my own + feature set I wanted. A slice of bools is another alternative. Bools will + be faster in almost all cases but the bitset will use less memory. I'm + chosing size over speed for now. + +* Code generation is used to provide methods that work on both labeled and + unlabeled graphs. Code is written to labeled types, then transformations + generate the unlabled equivalents. + +* Methods are named for what they return rather than what they do, where + reasonable anyway. + +* Consistency in method signature and behavior across corresponding methods, + for example directed/undirected, labeled/unlabeled, again, as long as it's + reasonable. + +* Sometimes in tension with the consistency principle, methods are lazy about + datatypes of parameters and return values. Sometimes a vale might have + different reasonable representations, a set might be a bitset, map, slice + of bools, or slice of set members for example. Methods will take and return + whatever is convenient for them and not convert the form just for consistency + or to try to guess what a caller would prefer. + +* Methods return multiple results for whatever the algorithm produces that + might be of interest. Sometimes an algorithm will have a primary result but + then some secondary values that also might be of interest. If they are + already computed as a byproduct of the algorithm, or can be computed at + negligible cost, return them. + +* Sometimes in conflict with the multiple result principle, methods will not + speculatively compute secondary results if there is any significant cost + and if the secondary result can be just as easily computed later. + +== Code Maintenance +There are tons of cut and paste variants. There's the basic AdjacencyList, +then Directed and Undirected variants, then Labeled variants of each of those. +Code gen helps avoid some cut and paste but there's a bunch that doesn't +code gen very well and so is duplicated with cut and paste. In particular +the testable examples in the _test files don't cg well and so are pretty much +all duplicated by hand. If you change code, think about where there should +be variants and go look to see if the variants need similar changes. diff --git a/vendor/github.com/soniakeys/graph/mst.go b/vendor/github.com/soniakeys/graph/mst.go new file mode 100644 index 00000000..92a9175c --- /dev/null +++ b/vendor/github.com/soniakeys/graph/mst.go @@ -0,0 +1,254 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +import ( + "container/heap" + "sort" + + "github.com/soniakeys/bits" +) + +type dsElement struct { + from NI + rank int +} + +type disjointSet struct { + set []dsElement +} + +func newDisjointSet(n int) disjointSet { + set := make([]dsElement, n) + for i := range set { + set[i].from = -1 + } + return disjointSet{set} +} + +// return true if disjoint trees were combined. +// false if x and y were already in the same tree. +func (ds disjointSet) union(x, y NI) bool { + xr := ds.find(x) + yr := ds.find(y) + if xr == yr { + return false + } + switch xe, ye := &ds.set[xr], &ds.set[yr]; { + case xe.rank < ye.rank: + xe.from = yr + case xe.rank == ye.rank: + xe.rank++ + fallthrough + default: + ye.from = xr + } + return true +} + +func (ds disjointSet) find(n NI) NI { + // fast paths for n == root or from root. + // no updates need in these cases. + s := ds.set + fr := s[n].from + if fr < 0 { // n is root + return n + } + n, fr = fr, s[fr].from + if fr < 0 { // n is from root + return n + } + // otherwise updates needed. + // two iterative passes (rather than recursion or stack) + // pass 1: find root + r := fr + for { + f := s[r].from + if f < 0 { + break + } + r = f + } + // pass 2: update froms + for { + s[n].from = r + if fr == r { + return r + } + n = fr + fr = s[n].from + } +} + +// Kruskal implements Kruskal's algorithm for constructing a minimum spanning +// forest on an undirected graph. +// +// The forest is returned as an undirected graph. +// +// Also returned is a total distance for the returned forest. +// +// This method is a convenience wrapper for LabeledEdgeList.Kruskal. +// If you have no need for the input graph as a LabeledUndirected, it may be +// more efficient to construct a LabeledEdgeList directly. +func (g LabeledUndirected) Kruskal(w WeightFunc) (spanningForest LabeledUndirected, dist float64) { + return g.WeightedArcsAsEdges(w).Kruskal() +} + +// Kruskal implements Kruskal's algorithm for constructing a minimum spanning +// forest on an undirected graph. +// +// The algorithm allows parallel edges, thus it is acceptable to construct +// the receiver with LabeledUndirected.WeightedArcsAsEdges. It may be more +// efficient though, if you can construct the receiver WeightedEdgeList +// directly without parallel edges. +// +// The forest is returned as an undirected graph. +// +// Also returned is a total distance for the returned forest. +// +// The edge list of the receiver is sorted in place as a side effect of this +// method. See KruskalSorted for a version that relies on the edge list being +// already sorted. This method is a wrapper for KruskalSorted. If you can +// generate the input graph sorted as required for KruskalSorted, you can +// call that method directly and avoid the overhead of the sort. +func (l WeightedEdgeList) Kruskal() (g LabeledUndirected, dist float64) { + e := l.Edges + w := l.WeightFunc + sort.Slice(e, func(i, j int) bool { return w(e[i].LI) < w(e[j].LI) }) + return l.KruskalSorted() +} + +// KruskalSorted implements Kruskal's algorithm for constructing a minimum +// spanning tree on an undirected graph. +// +// When called, the edge list of the receiver must be already sorted by weight. +// See the Kruskal method for a version that accepts an unsorted edge list. +// As with Kruskal, parallel edges are allowed. +// +// The forest is returned as an undirected graph. +// +// Also returned is a total distance for the returned forest. +func (l WeightedEdgeList) KruskalSorted() (g LabeledUndirected, dist float64) { + ds := newDisjointSet(l.Order) + g.LabeledAdjacencyList = make(LabeledAdjacencyList, l.Order) + for _, e := range l.Edges { + if ds.union(e.N1, e.N2) { + g.AddEdge(Edge{e.N1, e.N2}, e.LI) + dist += l.WeightFunc(e.LI) + } + } + return +} + +// Prim implements the Jarník-Prim-Dijkstra algorithm for constructing +// a minimum spanning tree on an undirected graph. +// +// Prim computes a minimal spanning tree on the connected component containing +// the given start node. The tree is returned in FromList f. Argument f +// cannot be a nil pointer although it can point to a zero value FromList. +// +// If the passed FromList.Paths has the len of g though, it will be reused. +// In the case of a graph with multiple connected components, this allows a +// spanning forest to be accumulated by calling Prim successively on +// representative nodes of the components. In this case if leaves for +// individual trees are of interest, pass a non-nil zero-value for the argument +// componentLeaves and it will be populated with leaves for the single tree +// spanned by the call. +// +// If argument labels is non-nil, it must have the same length as g and will +// be populated with labels corresponding to the edges of the tree. +// +// Returned are the number of nodes spanned for the single tree (which will be +// the order of the connected component) and the total spanned distance for the +// single tree. +func (g LabeledUndirected) Prim(start NI, w WeightFunc, f *FromList, labels []LI, componentLeaves *bits.Bits) (numSpanned int, dist float64) { + al := g.LabeledAdjacencyList + if len(f.Paths) != len(al) { + *f = NewFromList(len(al)) + } + if f.Leaves.Num != len(al) { + f.Leaves = bits.New(len(al)) + } + b := make([]prNode, len(al)) // "best" + for n := range b { + b[n].nx = NI(n) + b[n].fx = -1 + } + rp := f.Paths + var frontier prHeap + rp[start] = PathEnd{From: -1, Len: 1} + numSpanned = 1 + fLeaves := &f.Leaves + fLeaves.SetBit(int(start), 1) + if componentLeaves != nil { + if componentLeaves.Num != len(al) { + *componentLeaves = bits.New(len(al)) + } + componentLeaves.SetBit(int(start), 1) + } + for a := start; ; { + for _, nb := range al[a] { + if rp[nb.To].Len > 0 { + continue // already in MST, no action + } + switch bp := &b[nb.To]; { + case bp.fx == -1: // new node for frontier + bp.from = fromHalf{From: a, Label: nb.Label} + bp.wt = w(nb.Label) + heap.Push(&frontier, bp) + case w(nb.Label) < bp.wt: // better arc + bp.from = fromHalf{From: a, Label: nb.Label} + bp.wt = w(nb.Label) + heap.Fix(&frontier, bp.fx) + } + } + if len(frontier) == 0 { + break // done + } + bp := heap.Pop(&frontier).(*prNode) + a = bp.nx + rp[a].Len = rp[bp.from.From].Len + 1 + rp[a].From = bp.from.From + if len(labels) != 0 { + labels[a] = bp.from.Label + } + dist += bp.wt + fLeaves.SetBit(int(bp.from.From), 0) + fLeaves.SetBit(int(a), 1) + if componentLeaves != nil { + componentLeaves.SetBit(int(bp.from.From), 0) + componentLeaves.SetBit(int(a), 1) + } + numSpanned++ + } + return +} + +type prNode struct { + nx NI + from fromHalf + wt float64 // p.Weight(from.Label) + fx int +} + +type prHeap []*prNode + +func (h prHeap) Len() int { return len(h) } +func (h prHeap) Less(i, j int) bool { return h[i].wt < h[j].wt } +func (h prHeap) Swap(i, j int) { + h[i], h[j] = h[j], h[i] + h[i].fx = i + h[j].fx = j +} +func (p *prHeap) Push(x interface{}) { + nd := x.(*prNode) + nd.fx = len(*p) + *p = append(*p, nd) +} +func (p *prHeap) Pop() interface{} { + r := *p + last := len(r) - 1 + *p = r[:last] + return r[last] +} diff --git a/vendor/github.com/soniakeys/graph/random.go b/vendor/github.com/soniakeys/graph/random.go new file mode 100644 index 00000000..e4bbe30c --- /dev/null +++ b/vendor/github.com/soniakeys/graph/random.go @@ -0,0 +1,708 @@ +// Copyright 2016 Sonia Keys +// License MIT: https://opensource.org/licenses/MIT + +package graph + +import ( + "errors" + "math" + "math/rand" + + "github.com/soniakeys/bits" +) + +// ChungLu constructs a random simple undirected graph. +// +// The Chung Lu model is similar to a "configuration model" where each +// node has a specified degree. In the Chung Lu model the degree specified +// for each node is taken as an expected degree, not an exact degree. +// +// Argument w is "weight," the expected degree for each node. +// The values of w must be given in decreasing order. +// +// The constructed graph will have node 0 with expected degree w[0] and so on +// so degree will decrease with node number. To randomize degree across +// node numbers, consider using the Permute method with a rand.Perm. +// +// Also returned is the actual size m of constructed graph g. +// +// If Rand r is nil, the rand package default shared source is used. +func ChungLu(w []float64, rr *rand.Rand) (g Undirected, m int) { + // Ref: "Efficient Generation of Networks with Given Expected Degrees" + // Joel C. Miller and Aric Hagberg + // accessed at http://aric.hagberg.org/papers/miller-2011-efficient.pdf + rf := rand.Float64 + if rr != nil { + rf = rr.Float64 + } + a := make(AdjacencyList, len(w)) + S := 0. + for i := len(w) - 1; i >= 0; i-- { + S += w[i] + } + for u := 0; u < len(w)-1; u++ { + v := u + 1 + p := w[u] * w[v] / S + if p > 1 { + p = 1 + } + for v < len(w) && p > 0 { + if p != 1 { + v += int(math.Log(rf()) / math.Log(1-p)) + } + if v < len(w) { + q := w[u] * w[v] / S + if q > 1 { + q = 1 + } + if rf() < q/p { + a[u] = append(a[u], NI(v)) + a[v] = append(a[v], NI(u)) + m++ + } + p = q + v++ + } + } + } + return Undirected{a}, m +} + +// Euclidean generates a random simple graph on the Euclidean plane. +// +// Nodes are associated with coordinates uniformly distributed on a unit +// square. Arcs are added between random nodes with a bias toward connecting +// nearer nodes. +// +// Unfortunately the function has a few "knobs". +// The returned graph will have order nNodes and arc size nArcs. The affinity +// argument controls the bias toward connecting nearer nodes. The function +// selects random pairs of nodes as a candidate arc then rejects the candidate +// if the nodes fail an affinity test. Also parallel arcs are rejected. +// As more affine or denser graphs are requested, rejections increase, +// increasing run time. The patience argument controls the number of arc +// rejections allowed before the function gives up and returns an error. +// Note that higher affinity will require more patience and that some +// combinations of nNodes and nArcs cannot be achieved with any amount of +// patience given that the returned graph must be simple. +// +// If Rand r is nil, the rand package default shared source is used. +// +// Returned is a directed simple graph and associated positions indexed by +// node number. In the arc list for each node, to-nodes are in random +// order. +// +// See also LabeledEuclidean. +func Euclidean(nNodes, nArcs int, affinity float64, patience int, rr *rand.Rand) (g Directed, pos []struct{ X, Y float64 }, err error) { + a := make(AdjacencyList, nNodes) // graph + ri, rf, re := rand.Intn, rand.Float64, rand.ExpFloat64 + if rr != nil { + ri, rf, re = rr.Intn, rr.Float64, rr.ExpFloat64 + } + // generate random positions + pos = make([]struct{ X, Y float64 }, nNodes) + for i := range pos { + pos[i].X = rf() + pos[i].Y = rf() + } + // arcs + var tooFar, dup int +arc: + for i := 0; i < nArcs; { + if tooFar == nArcs*patience { + err = errors.New("affinity not found") + return + } + if dup == nArcs*patience { + err = errors.New("overcrowding") + return + } + n1 := NI(ri(nNodes)) + var n2 NI + for { + n2 = NI(ri(nNodes)) + if n2 != n1 { // no graph loops + break + } + } + c1 := &pos[n1] + c2 := &pos[n2] + dist := math.Hypot(c2.X-c1.X, c2.Y-c1.Y) + if dist*affinity > re() { // favor near nodes + tooFar++ + continue + } + for _, nb := range a[n1] { + if nb == n2 { // no parallel arcs + dup++ + continue arc + } + } + a[n1] = append(a[n1], n2) + i++ + } + g = Directed{a} + return +} + +// LabeledEuclidean generates a random simple graph on the Euclidean plane. +// +// Arc label values in the returned graph g are indexes into the return value +// wt. Wt is the Euclidean distance between the from and to nodes of the arc. +// +// Otherwise the function arguments and return values are the same as for +// function Euclidean. See Euclidean. +func LabeledEuclidean(nNodes, nArcs int, affinity float64, patience int, rr *rand.Rand) (g LabeledDirected, pos []struct{ X, Y float64 }, wt []float64, err error) { + a := make(LabeledAdjacencyList, nNodes) // graph + wt = make([]float64, nArcs) // arc weights + ri, rf, re := rand.Intn, rand.Float64, rand.ExpFloat64 + if rr != nil { + ri, rf, re = rr.Intn, rr.Float64, rr.ExpFloat64 + } + // generate random positions + pos = make([]struct{ X, Y float64 }, nNodes) + for i := range pos { + pos[i].X = rf() + pos[i].Y = rf() + } + // arcs + var tooFar, dup int +arc: + for i := 0; i < nArcs; { + if tooFar == nArcs*patience { + err = errors.New("affinity not found") + return + } + if dup == nArcs*patience { + err = errors.New("overcrowding") + return + } + n1 := NI(ri(nNodes)) + var n2 NI + for { + n2 = NI(ri(nNodes)) + if n2 != n1 { // no graph loops + break + } + } + c1 := &pos[n1] + c2 := &pos[n2] + dist := math.Hypot(c2.X-c1.X, c2.Y-c1.Y) + if dist*affinity > re() { // favor near nodes + tooFar++ + continue + } + for _, nb := range a[n1] { + if nb.To == n2 { // no parallel arcs + dup++ + continue arc + } + } + wt[i] = dist + a[n1] = append(a[n1], Half{n2, LI(i)}) + i++ + } + g = LabeledDirected{a} + return +} + +// Geometric generates a random geometric graph (RGG) on the Euclidean plane. +// +// An RGG is an undirected simple graph. Nodes are associated with coordinates +// uniformly distributed on a unit square. Edges are added between all nodes +// falling within a specified distance or radius of each other. +// +// The resulting number of edges is somewhat random but asymptotically +// approaches m = πr²n²/2. The method accumulates and returns the actual +// number of edges constructed. In the arc list for each node, to-nodes are +// ordered. Consider using ShuffleArcLists if random order is important. +// +// If Rand r is nil, the rand package default shared source is used. +// +// See also LabeledGeometric. +func Geometric(nNodes int, radius float64, rr *rand.Rand) (g Undirected, pos []struct{ X, Y float64 }, m int) { + // Expected degree is approximately nπr². + a := make(AdjacencyList, nNodes) + rf := rand.Float64 + if rr != nil { + rf = rr.Float64 + } + pos = make([]struct{ X, Y float64 }, nNodes) + for i := range pos { + pos[i].X = rf() + pos[i].Y = rf() + } + for u, up := range pos { + for v := u + 1; v < len(pos); v++ { + vp := pos[v] + dx := math.Abs(up.X - vp.X) + if dx >= radius { + continue + } + dy := math.Abs(up.Y - vp.Y) + if dy >= radius { + continue + } + if math.Hypot(dx, dy) < radius { + a[u] = append(a[u], NI(v)) + a[v] = append(a[v], NI(u)) + m++ + } + } + } + g = Undirected{a} + return +} + +// LabeledGeometric generates a random geometric graph (RGG) on the Euclidean +// plane. +// +// Edge label values in the returned graph g are indexes into the return value +// wt. Wt is the Euclidean distance between nodes of the edge. The graph +// size m is len(wt). +// +// See Geometric for additional description. +func LabeledGeometric(nNodes int, radius float64, rr *rand.Rand) (g LabeledUndirected, pos []struct{ X, Y float64 }, wt []float64) { + a := make(LabeledAdjacencyList, nNodes) + rf := rand.Float64 + if rr != nil { + rf = rr.Float64 + } + pos = make([]struct{ X, Y float64 }, nNodes) + for i := range pos { + pos[i].X = rf() + pos[i].Y = rf() + } + for u, up := range pos { + for v := u + 1; v < len(pos); v++ { + vp := pos[v] + if w := math.Hypot(up.X-vp.X, up.Y-vp.Y); w < radius { + a[u] = append(a[u], Half{NI(v), LI(len(wt))}) + a[v] = append(a[v], Half{NI(u), LI(len(wt))}) + wt = append(wt, w) + } + } + } + g = LabeledUndirected{a} + return +} + +// GnmUndirected constructs a random simple undirected graph. +// +// Construction is by the Erdős–Rényi model where the specified number of +// distinct edges is selected from all possible edges with equal probability. +// +// Argument n is number of nodes, m is number of edges and must be <= n(n-1)/2. +// +// If Rand r is nil, the rand package default shared source is used. +// +// In the generated arc list for each node, to-nodes are ordered. +// Consider using ShuffleArcLists if random order is important. +// +// See also Gnm3Undirected, a method producing a statistically equivalent +// result, but by an algorithm with somewhat different performance properties. +// Performance of the two methods is expected to be similar in most cases but +// it may be worth trying both with your data to see if one has a clear +// advantage. +func GnmUndirected(n, m int, rr *rand.Rand) Undirected { + // based on Alg. 2 from "Efficient Generation of Large Random Networks", + // Vladimir Batagelj and Ulrik Brandes. + // accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf + ri := rand.Intn + if rr != nil { + ri = rr.Intn + } + re := n * (n - 1) / 2 + ml := m + if m*2 > re { + ml = re - m + } + e := map[int]struct{}{} + for len(e) < ml { + e[ri(re)] = struct{}{} + } + a := make(AdjacencyList, n) + if m*2 > re { + i := 0 + for v := 1; v < n; v++ { + for w := 0; w < v; w++ { + if _, ok := e[i]; !ok { + a[v] = append(a[v], NI(w)) + a[w] = append(a[w], NI(v)) + } + i++ + } + } + } else { + for i := range e { + v := 1 + int(math.Sqrt(.25+float64(2*i))-.5) + w := i - (v * (v - 1) / 2) + a[v] = append(a[v], NI(w)) + a[w] = append(a[w], NI(v)) + } + } + return Undirected{a} +} + +// GnmDirected constructs a random simple directed graph. +// +// Construction is by the Erdős–Rényi model where the specified number of +// distinct arcs is selected from all possible arcs with equal probability. +// +// Argument n is number of nodes, ma is number of arcs and must be <= n(n-1). +// +// If Rand r is nil, the rand package default shared source is used. +// +// In the generated arc list for each node, to-nodes are ordered. +// Consider using ShuffleArcLists if random order is important. +// +// See also Gnm3Directed, a method producing a statistically equivalent +// result, but by +// an algorithm with somewhat different performance properties. Performance +// of the two methods is expected to be similar in most cases but it may be +// worth trying both with your data to see if one has a clear advantage. +func GnmDirected(n, ma int, rr *rand.Rand) Directed { + // based on Alg. 2 from "Efficient Generation of Large Random Networks", + // Vladimir Batagelj and Ulrik Brandes. + // accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf + ri := rand.Intn + if rr != nil { + ri = rr.Intn + } + re := n * (n - 1) + ml := ma + if ma*2 > re { + ml = re - ma + } + e := map[int]struct{}{} + for len(e) < ml { + e[ri(re)] = struct{}{} + } + a := make(AdjacencyList, n) + if ma*2 > re { + i := 0 + for v := 0; v < n; v++ { + for w := 0; w < n; w++ { + if w == v { + continue + } + if _, ok := e[i]; !ok { + a[v] = append(a[v], NI(w)) + } + i++ + } + } + } else { + for i := range e { + v := i / (n - 1) + w := i % (n - 1) + if w >= v { + w++ + } + a[v] = append(a[v], NI(w)) + } + } + return Directed{a} +} + +// Gnm3Undirected constructs a random simple undirected graph. +// +// Construction is by the Erdős–Rényi model where the specified number of +// distinct edges is selected from all possible edges with equal probability. +// +// Argument n is number of nodes, m is number of edges and must be <= n(n-1)/2. +// +// If Rand r is nil, the rand package default shared source is used. +// +// In the generated arc list for each node, to-nodes are ordered. +// Consider using ShuffleArcLists if random order is important. +// +// See also GnmUndirected, a method producing a statistically equivalent +// result, but by an algorithm with somewhat different performance properties. +// Performance of the two methods is expected to be similar in most cases but +// it may be worth trying both with your data to see if one has a clear +// advantage. +func Gnm3Undirected(n, m int, rr *rand.Rand) Undirected { + // based on Alg. 3 from "Efficient Generation of Large Random Networks", + // Vladimir Batagelj and Ulrik Brandes. + // accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf + // + // I like this algorithm for its elegance. Pitty it tends to run a + // a little slower than the retry algorithm of Gnm. + ri := rand.Intn + if rr != nil { + ri = rr.Intn + } + a := make(AdjacencyList, n) + re := n * (n - 1) / 2 + rm := map[int]int{} + for i := 0; i < m; i++ { + er := i + ri(re-i) + eNew := er + if rp, ok := rm[er]; ok { + eNew = rp + } + if rp, ok := rm[i]; !ok { + rm[er] = i + } else { + rm[er] = rp + } + v := 1 + int(math.Sqrt(.25+float64(2*eNew))-.5) + w := eNew - (v * (v - 1) / 2) + a[v] = append(a[v], NI(w)) + a[w] = append(a[w], NI(v)) + } + return Undirected{a} +} + +// Gnm3Directed constructs a random simple directed graph. +// +// Construction is by the Erdős–Rényi model where the specified number of +// distinct arcs is selected from all possible arcs with equal probability. +// +// Argument n is number of nodes, ma is number of arcs and must be <= n(n-1). +// +// If Rand r is nil, the rand package default shared source is used. +// +// In the generated arc list for each node, to-nodes are ordered. +// Consider using ShuffleArcLists if random order is important. +// +// See also GnmDirected, a method producing a statistically equivalent result, +// but by an algorithm with somewhat different performance properties. +// Performance of the two methods is expected to be similar in most cases +// but it may be worth trying both with your data to see if one has a clear +// advantage. +func Gnm3Directed(n, ma int, rr *rand.Rand) Directed { + // based on Alg. 3 from "Efficient Generation of Large Random Networks", + // Vladimir Batagelj and Ulrik Brandes. + // accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf + ri := rand.Intn + if rr != nil { + ri = rr.Intn + } + a := make(AdjacencyList, n) + re := n * (n - 1) + rm := map[int]int{} + for i := 0; i < ma; i++ { + er := i + ri(re-i) + eNew := er + if rp, ok := rm[er]; ok { + eNew = rp + } + if rp, ok := rm[i]; !ok { + rm[er] = i + } else { + rm[er] = rp + } + v := eNew / (n - 1) + w := eNew % (n - 1) + if w >= v { + w++ + } + a[v] = append(a[v], NI(w)) + } + return Directed{a} +} + +// GnpUndirected constructs a random simple undirected graph. +// +// Construction is by the Gilbert model, an Erdős–Rényi like model where +// distinct edges are independently selected from all possible edges with +// the specified probability. +// +// Argument n is number of nodes, p is probability for selecting an edge. +// +// If Rand r is nil, the rand package default shared source is used. +// +// In the generated arc list for each node, to-nodes are ordered. +// Consider using ShuffleArcLists if random order is important. +// +// Also returned is the actual size m of constructed graph g. +func GnpUndirected(n int, p float64, rr *rand.Rand) (g Undirected, m int) { + a := make(AdjacencyList, n) + if n < 2 { + return Undirected{a}, 0 + } + rf := rand.Float64 + if rr != nil { + rf = rr.Float64 + } + // based on Alg. 1 from "Efficient Generation of Large Random Networks", + // Vladimir Batagelj and Ulrik Brandes. + // accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf + var v, w NI = 1, -1 +g: + for c := 1 / math.Log(1-p); ; { + w += 1 + NI(c*math.Log(1-rf())) + for { + if w < v { + a[v] = append(a[v], w) + a[w] = append(a[w], v) + m++ + continue g + } + w -= v + v++ + if v == NI(n) { + break g + } + } + } + return Undirected{a}, m +} + +// GnpDirected constructs a random simple directed graph. +// +// Construction is by the Gilbert model, an Erdős–Rényi like model where +// distinct arcs are independently selected from all possible arcs with +// the specified probability. +// +// Argument n is number of nodes, p is probability for selecting an arc. +// +// If Rand r is nil, the rand package default shared source is used. +// +// In the generated arc list for each node, to-nodes are ordered. +// Consider using ShuffleArcLists if random order is important. +// +// Also returned is the actual arc size m of constructed graph g. +func GnpDirected(n int, p float64, rr *rand.Rand) (g Directed, ma int) { + a := make(AdjacencyList, n) + if n < 2 { + return Directed{a}, 0 + } + rf := rand.Float64 + if rr != nil { + rf = rr.Float64 + } + // based on Alg. 1 from "Efficient Generation of Large Random Networks", + // Vladimir Batagelj and Ulrik Brandes. + // accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf + var v, w NI = 0, -1 +g: + for c := 1 / math.Log(1-p); ; { + w += 1 + NI(c*math.Log(1-rf())) + for ; ; w -= NI(n) { + if w == v { + w++ + } + if w < NI(n) { + a[v] = append(a[v], w) + ma++ + continue g + } + v++ + if v == NI(n) { + break g + } + } + } + return Directed{a}, ma +} + +// KroneckerDirected generates a Kronecker-like random directed graph. +// +// The returned graph g is simple and has no isolated nodes but is not +// necessarily fully connected. The number of of nodes will be <= 2^scale, +// and will be near 2^scale for typical values of arcFactor, >= 2. +// ArcFactor * 2^scale arcs are generated, although loops and duplicate arcs +// are rejected. In the arc list for each node, to-nodes are in random +// order. +// +// If Rand r is nil, the rand package default shared source is used. +// +// Return value ma is the number of arcs retained in the result graph. +func KroneckerDirected(scale uint, arcFactor float64, rr *rand.Rand) (g Directed, ma int) { + a, m := kronecker(scale, arcFactor, true, rr) + return Directed{a}, m +} + +// KroneckerUndirected generates a Kronecker-like random undirected graph. +// +// The returned graph g is simple and has no isolated nodes but is not +// necessarily fully connected. The number of of nodes will be <= 2^scale, +// and will be near 2^scale for typical values of edgeFactor, >= 2. +// EdgeFactor * 2^scale edges are generated, although loops and duplicate edges +// are rejected. In the arc list for each node, to-nodes are in random +// order. +// +// If Rand r is nil, the rand package default shared source is used. +// +// Return value m is the true number of edges--not arcs--retained in the result +// graph. +func KroneckerUndirected(scale uint, edgeFactor float64, rr *rand.Rand) (g Undirected, m int) { + al, s := kronecker(scale, edgeFactor, false, rr) + return Undirected{al}, s +} + +// Styled after the Graph500 example code. Not well tested currently. +// Graph500 example generates undirected only. No idea if the directed variant +// here is meaningful or not. +// +// note mma returns arc size ma for dir=true, but returns size m for dir=false +func kronecker(scale uint, edgeFactor float64, dir bool, rr *rand.Rand) (g AdjacencyList, mma int) { + rf, ri, rp := rand.Float64, rand.Intn, rand.Perm + if rr != nil { + rf, ri, rp = rr.Float64, rr.Intn, rr.Perm + } + N := 1 << scale // node extent + M := int(edgeFactor*float64(N) + .5) // number of arcs/edges to generate + a, b, c := 0.57, 0.19, 0.19 // initiator probabilities + ab := a + b + cNorm := c / (1 - ab) + aNorm := a / ab + ij := make([][2]NI, M) + bm := bits.New(N) + var nNodes int + for k := range ij { + var i, j int + for b := 1; b < N; b <<= 1 { + if rf() > ab { + i |= b + if rf() > cNorm { + j |= b + } + } else if rf() > aNorm { + j |= b + } + } + if bm.Bit(i) == 0 { + bm.SetBit(i, 1) + nNodes++ + } + if bm.Bit(j) == 0 { + bm.SetBit(j, 1) + nNodes++ + } + r := ri(k + 1) // shuffle edges as they are generated + ij[k] = ij[r] + ij[r] = [2]NI{NI(i), NI(j)} + } + p := rp(nNodes) // mapping to shuffle IDs of non-isolated nodes + px := 0 + rn := make([]NI, N) + for i := range rn { + if bm.Bit(i) == 1 { + rn[i] = NI(p[px]) // fill lookup table + px++ + } + } + g = make(AdjacencyList, nNodes) +ij: + for _, e := range ij { + if e[0] == e[1] { + continue // skip loops + } + ri, rj := rn[e[0]], rn[e[1]] + for _, nb := range g[ri] { + if nb == rj { + continue ij // skip parallel edges + } + } + g[ri] = append(g[ri], rj) + mma++ + if !dir { + g[rj] = append(g[rj], ri) + } + } + return +} diff --git a/vendor/github.com/soniakeys/graph/readme.adoc b/vendor/github.com/soniakeys/graph/readme.adoc new file mode 100644 index 00000000..df771a5b --- /dev/null +++ b/vendor/github.com/soniakeys/graph/readme.adoc @@ -0,0 +1,50 @@ += Graph + +A graph library with goals of speed and simplicity, Graph implements +graph algorithms on graphs of zero-based integer node IDs. + +image:https://godoc.org/github.com/soniakeys/graph?status.svg[link=https://godoc.org/github.com/soniakeys/graph] +image:http://gowalker.org/api/v1/badge[link=https://gowalker.org/github.com/soniakeys/graph] +image:http://go-search.org/badge?id=github.com%2Fsoniakeys%2Fgraph[link=http://go-search.org/view?id=github.com%2Fsoniakeys%2Fgraph] +image:https://travis-ci.org/soniakeys/graph.svg?branch=master[link=https://travis-ci.org/soniakeys/graph] + +The library provides efficient graph representations and many methods on +graph types. It can be imported and used directly in many applications that +require or can benefit from graph algorithms. + +The library should also be considered as library of source code that can serve +as starting material for coding variant or more complex algorithms. + +== Ancillary material of interest + +The directory link:tutorials[tutorials] is a work in progress - there are only +a few tutorials there yet - but the concept is to provide some topical +walk-throughs to supplement godoc. The source-based godoc documentation +remains the primary documentation. + +The directory link:anecdote[anecdote] contains a stand-alone program that +performs single runs of a number of methods, collecting one-off or "anecdotal" +timings. It currently runs only a small fraction of the library methods but +may still be of interest for giving a general idea of how fast some methods +run. + +The directory link:bench[bench] is another work in progress. The concept is +to present some plots showing benchmark performance approaching some +theoretical asymptote. + +link:hacking.adoc[hacking.adoc] has some information about how the library is +developed, built, and tested. It might be of interest if for example you +plan to fork or contribute to the the repository. + +== Test coverage +1 Jul 2017 +.... +graph 93.7% +graph/alt 88.0% +graph/dot 77.7% +graph/treevis 79.4% +.... + +== License +All files in the repository are licensed with the MIT License, +https://opensource.org/licenses/MIT. diff --git a/vendor/github.com/soniakeys/graph/sssp.go b/vendor/github.com/soniakeys/graph/sssp.go new file mode 100644 index 00000000..88e9d517 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/sssp.go @@ -0,0 +1,761 @@ +// Copyright 2013 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +import ( + "container/heap" + "fmt" + "math" + + "github.com/soniakeys/bits" +) + +// rNode holds data for a "reached" node +type rNode struct { + nx NI + state int8 // state constants defined below + f float64 // "g+h", path dist + heuristic estimate + fx int // heap.Fix index +} + +// for rNode.state +const ( + unreached = 0 + reached = 1 + open = 1 + closed = 2 +) + +type openHeap []*rNode + +// A Heuristic is defined on a specific end node. The function +// returns an estimate of the path distance from node argument +// "from" to the end node. Two subclasses of heuristics are "admissible" +// and "monotonic." +// +// Admissible means the value returned is guaranteed to be less than or +// equal to the actual shortest path distance from the node to end. +// +// An admissible estimate may further be monotonic. +// Monotonic means that for any neighboring nodes A and B with half arc aB +// leading from A to B, and for heuristic h defined on some end node, then +// h(A) <= aB.ArcWeight + h(B). +// +// See AStarA for additional notes on implementing heuristic functions for +// AStar search methods. +type Heuristic func(from NI) float64 + +// Admissible returns true if heuristic h is admissible on graph g relative to +// the given end node. +// +// If h is inadmissible, the string result describes a counter example. +func (h Heuristic) Admissible(g LabeledAdjacencyList, w WeightFunc, end NI) (bool, string) { + // invert graph + inv := make(LabeledAdjacencyList, len(g)) + for from, nbs := range g { + for _, nb := range nbs { + inv[nb.To] = append(inv[nb.To], + Half{To: NI(from), Label: nb.Label}) + } + } + // run dijkstra + // Dijkstra.AllPaths takes a start node but after inverting the graph + // argument end now represents the start node of the inverted graph. + f, _, dist, _ := inv.Dijkstra(end, -1, w) + // compare h to found shortest paths + for n := range inv { + if f.Paths[n].Len == 0 { + continue // no path, any heuristic estimate is fine. + } + if !(h(NI(n)) <= dist[n]) { + return false, fmt.Sprintf("h(%d) = %g, "+ + "required to be <= found shortest path (%g)", + n, h(NI(n)), dist[n]) + } + } + return true, "" +} + +// Monotonic returns true if heuristic h is monotonic on weighted graph g. +// +// If h is non-monotonic, the string result describes a counter example. +func (h Heuristic) Monotonic(g LabeledAdjacencyList, w WeightFunc) (bool, string) { + // precompute + hv := make([]float64, len(g)) + for n := range g { + hv[n] = h(NI(n)) + } + // iterate over all edges + for from, nbs := range g { + for _, nb := range nbs { + arcWeight := w(nb.Label) + if !(hv[from] <= arcWeight+hv[nb.To]) { + return false, fmt.Sprintf("h(%d) = %g, "+ + "required to be <= arc weight + h(%d) (= %g + %g = %g)", + from, hv[from], + nb.To, arcWeight, hv[nb.To], arcWeight+hv[nb.To]) + } + } + } + return true, "" +} + +// AStarA finds a path between two nodes. +// +// AStarA implements both algorithm A and algorithm A*. The difference in the +// two algorithms is strictly in the heuristic estimate returned by argument h. +// If h is an "admissible" heuristic estimate, then the algorithm is termed A*, +// otherwise it is algorithm A. +// +// Like Dijkstra's algorithm, AStarA with an admissible heuristic finds the +// shortest path between start and end. AStarA generally runs faster than +// Dijkstra though, by using the heuristic distance estimate. +// +// AStarA with an inadmissible heuristic becomes algorithm A. Algorithm A +// will find a path, but it is not guaranteed to be the shortest path. +// The heuristic still guides the search however, so a nearly admissible +// heuristic is likely to find a very good path, if not the best. Quality +// of the path returned degrades gracefully with the quality of the heuristic. +// +// The heuristic function h should ideally be fairly inexpensive. AStarA +// may call it more than once for the same node, especially as graph density +// increases. In some cases it may be worth the effort to memoize or +// precompute values. +// +// Argument g is the graph to be searched, with arc weights returned by w. +// As usual for AStar, arc weights must be non-negative. +// Graphs may be directed or undirected. +// +// If AStarA finds a path it returns a FromList encoding the path, the arc +// labels for path nodes, the total path distance, and ok = true. +// Otherwise it returns ok = false. +func (g LabeledAdjacencyList) AStarA(w WeightFunc, start, end NI, h Heuristic) (f FromList, labels []LI, dist float64, ok bool) { + // NOTE: AStarM is largely duplicate code. + + f = NewFromList(len(g)) + labels = make([]LI, len(g)) + d := make([]float64, len(g)) + r := make([]rNode, len(g)) + for i := range r { + r[i].nx = NI(i) + } + // start node is reached initially + cr := &r[start] + cr.state = reached + cr.f = h(start) // total path estimate is estimate from start + rp := f.Paths + rp[start] = PathEnd{Len: 1, From: -1} // path length at start is 1 node + // oh is a heap of nodes "open" for exploration. nodes go on the heap + // when they get an initial or new "g" path distance, and therefore a + // new "f" which serves as priority for exploration. + oh := openHeap{cr} + for len(oh) > 0 { + bestPath := heap.Pop(&oh).(*rNode) + bestNode := bestPath.nx + if bestNode == end { + return f, labels, d[end], true + } + bp := &rp[bestNode] + nextLen := bp.Len + 1 + for _, nb := range g[bestNode] { + alt := &r[nb.To] + ap := &rp[alt.nx] + // "g" path distance from start + g := d[bestNode] + w(nb.Label) + if alt.state == reached { + if g > d[nb.To] { + // candidate path to nb is longer than some alternate path + continue + } + if g == d[nb.To] && nextLen >= ap.Len { + // candidate path has identical length of some alternate + // path but it takes no fewer hops. + continue + } + // cool, we found a better way to get to this node. + // record new path data for this node and + // update alt with new data and make sure it's on the heap. + *ap = PathEnd{From: bestNode, Len: nextLen} + labels[nb.To] = nb.Label + d[nb.To] = g + alt.f = g + h(nb.To) + if alt.fx < 0 { + heap.Push(&oh, alt) + } else { + heap.Fix(&oh, alt.fx) + } + } else { + // bestNode being reached for the first time. + *ap = PathEnd{From: bestNode, Len: nextLen} + labels[nb.To] = nb.Label + d[nb.To] = g + alt.f = g + h(nb.To) + alt.state = reached + heap.Push(&oh, alt) // and it's now open for exploration + } + } + } + return // no path +} + +// AStarAPath finds a shortest path using the AStarA algorithm. +// +// This is a convenience method with a simpler result than the AStarA method. +// See documentation on the AStarA method. +// +// If a path is found, the non-nil node path is returned with the total path +// distance. Otherwise the returned path will be nil. +func (g LabeledAdjacencyList) AStarAPath(start, end NI, h Heuristic, w WeightFunc) (LabeledPath, float64) { + f, labels, d, _ := g.AStarA(w, start, end, h) + return f.PathToLabeled(end, labels, nil), d +} + +// AStarM is AStarA optimized for monotonic heuristic estimates. +// +// Note that this function requires a monotonic heuristic. Results will +// not be meaningful if argument h is non-monotonic. +// +// See AStarA for general usage. See Heuristic for notes on monotonicity. +func (g LabeledAdjacencyList) AStarM(w WeightFunc, start, end NI, h Heuristic) (f FromList, labels []LI, dist float64, ok bool) { + // NOTE: AStarM is largely code duplicated from AStarA. + // Differences are noted in comments in this method. + + f = NewFromList(len(g)) + labels = make([]LI, len(g)) + d := make([]float64, len(g)) + r := make([]rNode, len(g)) + for i := range r { + r[i].nx = NI(i) + } + cr := &r[start] + + // difference from AStarA: + // instead of a bit to mark a reached node, there are two states, + // open and closed. open marks nodes "open" for exploration. + // nodes are marked open as they are reached, then marked + // closed as they are found to be on the best path. + cr.state = open + + cr.f = h(start) + rp := f.Paths + rp[start] = PathEnd{Len: 1, From: -1} + oh := openHeap{cr} + for len(oh) > 0 { + bestPath := heap.Pop(&oh).(*rNode) + bestNode := bestPath.nx + if bestNode == end { + return f, labels, d[end], true + } + + // difference from AStarA: + // move nodes to closed list as they are found to be best so far. + bestPath.state = closed + + bp := &rp[bestNode] + nextLen := bp.Len + 1 + for _, nb := range g[bestNode] { + alt := &r[nb.To] + + // difference from AStarA: + // Monotonicity means that f cannot be improved. + if alt.state == closed { + continue + } + + ap := &rp[alt.nx] + g := d[bestNode] + w(nb.Label) + + // difference from AStarA: + // test for open state, not just reached + if alt.state == open { + + if g > d[nb.To] { + continue + } + if g == d[nb.To] && nextLen >= ap.Len { + continue + } + *ap = PathEnd{From: bestNode, Len: nextLen} + labels[nb.To] = nb.Label + d[nb.To] = g + alt.f = g + h(nb.To) + + // difference from AStarA: + // we know alt was on the heap because we found it marked open + heap.Fix(&oh, alt.fx) + } else { + *ap = PathEnd{From: bestNode, Len: nextLen} + labels[nb.To] = nb.Label + d[nb.To] = g + alt.f = g + h(nb.To) + + // difference from AStarA: + // nodes are opened when first reached + alt.state = open + heap.Push(&oh, alt) + } + } + } + return +} + +// AStarMPath finds a shortest path using the AStarM algorithm. +// +// This is a convenience method with a simpler result than the AStarM method. +// See documentation on the AStarM and AStarA methods. +// +// If a path is found, the non-nil node path is returned with the total path +// distance. Otherwise the returned path will be nil. +func (g LabeledAdjacencyList) AStarMPath(start, end NI, h Heuristic, w WeightFunc) (LabeledPath, float64) { + f, labels, d, _ := g.AStarM(w, start, end, h) + return f.PathToLabeled(end, labels, nil), d +} + +// implement container/heap +func (h openHeap) Len() int { return len(h) } +func (h openHeap) Less(i, j int) bool { return h[i].f < h[j].f } +func (h openHeap) Swap(i, j int) { + h[i], h[j] = h[j], h[i] + h[i].fx = i + h[j].fx = j +} +func (p *openHeap) Push(x interface{}) { + h := *p + fx := len(h) + h = append(h, x.(*rNode)) + h[fx].fx = fx + *p = h +} + +func (p *openHeap) Pop() interface{} { + h := *p + last := len(h) - 1 + *p = h[:last] + h[last].fx = -1 + return h[last] +} + +// BellmanFord finds shortest paths from a start node in a weighted directed +// graph using the Bellman-Ford-Moore algorithm. +// +// WeightFunc w must translate arc labels to arc weights. +// Negative arc weights are allowed but not negative cycles. +// Loops and parallel arcs are allowed. +// +// If the algorithm completes without encountering a negative cycle the method +// returns shortest paths encoded in a FromList, labels and path distances +// indexed by node, and return value end = -1. +// +// If it encounters a negative cycle reachable from start it returns end >= 0. +// In this case the cycle can be obtained by calling f.BellmanFordCycle(end). +// +// Negative cycles are only detected when reachable from start. A negative +// cycle not reachable from start will not prevent the algorithm from finding +// shortest paths from start. +// +// See also NegativeCycle to find a cycle anywhere in the graph, see +// NegativeCycles for enumerating all negative cycles, and see +// HasNegativeCycle for lighter-weight negative cycle detection, +func (g LabeledDirected) BellmanFord(w WeightFunc, start NI) (f FromList, labels []LI, dist []float64, end NI) { + a := g.LabeledAdjacencyList + f = NewFromList(len(a)) + labels = make([]LI, len(a)) + dist = make([]float64, len(a)) + inf := math.Inf(1) + for i := range dist { + dist[i] = inf + } + rp := f.Paths + rp[start] = PathEnd{Len: 1, From: -1} + dist[start] = 0 + for _ = range a[1:] { + imp := false + for from, nbs := range a { + fp := &rp[from] + d1 := dist[from] + for _, nb := range nbs { + d2 := d1 + w(nb.Label) + to := &rp[nb.To] + // TODO improve to break ties + if fp.Len > 0 && d2 < dist[nb.To] { + *to = PathEnd{From: NI(from), Len: fp.Len + 1} + labels[nb.To] = nb.Label + dist[nb.To] = d2 + imp = true + } + } + } + if !imp { + break + } + } + for from, nbs := range a { + d1 := dist[from] + for _, nb := range nbs { + if d1+w(nb.Label) < dist[nb.To] { + // return nb as end of a path with negative cycle at root + return f, labels, dist, NI(from) + } + } + } + return f, labels, dist, -1 +} + +// BellmanFordCycle decodes a negative cycle detected by BellmanFord. +// +// Receiver f and argument end must be results returned from BellmanFord. +func (f FromList) BellmanFordCycle(end NI) (c []NI) { + p := f.Paths + b := bits.New(len(p)) + for b.Bit(int(end)) == 0 { + b.SetBit(int(end), 1) + end = p[end].From + } + for b.Bit(int(end)) == 1 { + c = append(c, end) + b.SetBit(int(end), 0) + end = p[end].From + } + for i, j := 0, len(c)-1; i < j; i, j = i+1, j-1 { + c[i], c[j] = c[j], c[i] + } + return +} + +// HasNegativeCycle returns true if the graph contains any negative cycle. +// +// HasNegativeCycle uses a Bellman-Ford-like algorithm, but finds negative +// cycles anywhere in the graph. Also path information is not computed, +// reducing memory use somewhat compared to BellmanFord. +// +// See also NegativeCycle to obtain the cycle, see NegativeCycles for +// enumerating all negative cycles, and see BellmanFord for single source +// shortest path searches with negative cycle detection. +func (g LabeledDirected) HasNegativeCycle(w WeightFunc) bool { + a := g.LabeledAdjacencyList + dist := make([]float64, len(a)) + for _ = range a[1:] { + imp := false + for from, nbs := range a { + d1 := dist[from] + for _, nb := range nbs { + d2 := d1 + w(nb.Label) + if d2 < dist[nb.To] { + dist[nb.To] = d2 + imp = true + } + } + } + if !imp { + break + } + } + for from, nbs := range a { + d1 := dist[from] + for _, nb := range nbs { + if d1+w(nb.Label) < dist[nb.To] { + return true // negative cycle + } + } + } + return false +} + +// NegativeCycle finds a negative cycle if one exists. +// +// NegativeCycle uses a Bellman-Ford-like algorithm, but finds negative +// cycles anywhere in the graph. If a negative cycle exists, one will be +// returned. The result is nil if no negative cycle exists. +// +// See also NegativeCycles for enumerating all negative cycles, see +// HasNegativeCycle for lighter-weight cycle detection, and see +// BellmanFord for single source shortest paths, also with negative cycle +// detection. +func (g LabeledDirected) NegativeCycle(w WeightFunc) (c []Half) { + a := g.LabeledAdjacencyList + f := NewFromList(len(a)) + p := f.Paths + for n := range p { + p[n] = PathEnd{From: -1, Len: 1} + } + labels := make([]LI, len(a)) + dist := make([]float64, len(a)) + for _ = range a { + imp := false + for from, nbs := range a { + fp := &p[from] + d1 := dist[from] + for _, nb := range nbs { + d2 := d1 + w(nb.Label) + to := &p[nb.To] + if fp.Len > 0 && d2 < dist[nb.To] { + *to = PathEnd{From: NI(from), Len: fp.Len + 1} + labels[nb.To] = nb.Label + dist[nb.To] = d2 + imp = true + } + } + } + if !imp { + return nil + } + } + vis := bits.New(len(a)) +a: + for n := range a { + end := n + b := bits.New(len(a)) + for b.Bit(end) == 0 { + if vis.Bit(end) == 1 { + continue a + } + vis.SetBit(end, 1) + b.SetBit(end, 1) + end = int(p[end].From) + if end < 0 { + continue a + } + } + for b.Bit(end) == 1 { + c = append(c, Half{NI(end), labels[end]}) + b.SetBit(end, 0) + end = int(p[end].From) + } + for i, j := 0, len(c)-1; i < j; i, j = i+1, j-1 { + c[i], c[j] = c[j], c[i] + } + return c + } + return nil // no negative cycle +} + +// DAGMinDistPath finds a single shortest path. +// +// Shortest means minimum sum of arc weights. +// +// Returned is the path and distance as returned by FromList.PathTo. +// +// This is a convenience method. See DAGOptimalPaths for more options. +func (g LabeledDirected) DAGMinDistPath(start, end NI, w WeightFunc) (LabeledPath, float64, error) { + return g.dagPath(start, end, w, false) +} + +// DAGMaxDistPath finds a single longest path. +// +// Longest means maximum sum of arc weights. +// +// Returned is the path and distance as returned by FromList.PathTo. +// +// This is a convenience method. See DAGOptimalPaths for more options. +func (g LabeledDirected) DAGMaxDistPath(start, end NI, w WeightFunc) (LabeledPath, float64, error) { + return g.dagPath(start, end, w, true) +} + +func (g LabeledDirected) dagPath(start, end NI, w WeightFunc, longest bool) (LabeledPath, float64, error) { + o, _ := g.Topological() + if o == nil { + return LabeledPath{}, 0, fmt.Errorf("not a DAG") + } + f, labels, dist, _ := g.DAGOptimalPaths(start, end, o, w, longest) + if f.Paths[end].Len == 0 { + return LabeledPath{}, 0, fmt.Errorf("no path from %d to %d", start, end) + } + return f.PathToLabeled(end, labels, nil), dist[end], nil +} + +// DAGOptimalPaths finds either longest or shortest distance paths in a +// directed acyclic graph. +// +// Path distance is the sum of arc weights on the path. +// Negative arc weights are allowed. +// Where multiple paths exist with the same distance, the path length +// (number of nodes) is used as a tie breaker. +// +// Receiver g must be a directed acyclic graph. Argument o must be either nil +// or a topological ordering of g. If nil, a topologcal ordering is +// computed internally. If longest is true, an optimal path is a longest +// distance path. Otherwise it is a shortest distance path. +// +// Argument start is the start node for paths, end is the end node. If end +// is a valid node number, the method returns as soon as the optimal path +// to end is found. If end is -1, all optimal paths from start are found. +// +// Paths and path distances are encoded in the returned FromList, labels, +// and dist slices. The number of nodes reached is returned as nReached. +func (g LabeledDirected) DAGOptimalPaths(start, end NI, ordering []NI, w WeightFunc, longest bool) (f FromList, labels []LI, dist []float64, nReached int) { + a := g.LabeledAdjacencyList + f = NewFromList(len(a)) + f.Leaves = bits.New(len(a)) + labels = make([]LI, len(a)) + dist = make([]float64, len(a)) + if ordering == nil { + ordering, _ = g.Topological() + } + // search ordering for start + o := 0 + for ordering[o] != start { + o++ + } + var fBetter func(cand, ext float64) bool + var iBetter func(cand, ext int) bool + if longest { + fBetter = func(cand, ext float64) bool { return cand > ext } + iBetter = func(cand, ext int) bool { return cand > ext } + } else { + fBetter = func(cand, ext float64) bool { return cand < ext } + iBetter = func(cand, ext int) bool { return cand < ext } + } + p := f.Paths + p[start] = PathEnd{From: -1, Len: 1} + f.MaxLen = 1 + leaves := &f.Leaves + leaves.SetBit(int(start), 1) + nReached = 1 + for n := start; n != end; n = ordering[o] { + if p[n].Len > 0 && len(a[n]) > 0 { + nDist := dist[n] + candLen := p[n].Len + 1 // len for any candidate arc followed from n + for _, to := range a[n] { + leaves.SetBit(int(to.To), 1) + candDist := nDist + w(to.Label) + switch { + case p[to.To].Len == 0: // first path to node to.To + nReached++ + case fBetter(candDist, dist[to.To]): // better distance + case candDist == dist[to.To] && iBetter(candLen, p[to.To].Len): // same distance but better path length + default: + continue + } + dist[to.To] = candDist + p[to.To] = PathEnd{From: n, Len: candLen} + labels[to.To] = to.Label + if candLen > f.MaxLen { + f.MaxLen = candLen + } + } + leaves.SetBit(int(n), 0) + } + o++ + if o == len(ordering) { + break + } + } + return +} + +// Dijkstra finds shortest paths by Dijkstra's algorithm. +// +// Shortest means shortest distance where distance is the +// sum of arc weights. Where multiple paths exist with the same distance, +// a path with the minimum number of nodes is returned. +// +// As usual for Dijkstra's algorithm, arc weights must be non-negative. +// Graphs may be directed or undirected. Loops and parallel arcs are +// allowed. +// +// Paths and path distances are encoded in the returned FromList and dist +// slice. Returned labels are the labels of arcs followed to each node. +// The number of nodes reached is returned as nReached. +func (g LabeledAdjacencyList) Dijkstra(start, end NI, w WeightFunc) (f FromList, labels []LI, dist []float64, nReached int) { + r := make([]tentResult, len(g)) + for i := range r { + r[i].nx = NI(i) + } + f = NewFromList(len(g)) + labels = make([]LI, len(g)) + dist = make([]float64, len(g)) + current := start + rp := f.Paths + rp[current] = PathEnd{Len: 1, From: -1} // path length at start is 1 node + cr := &r[current] + cr.dist = 0 // distance at start is 0. + cr.done = true // mark start done. it skips the heap. + nDone := 1 // accumulated for a return value + var t tent + for current != end { + nextLen := rp[current].Len + 1 + for _, nb := range g[current] { + // d.arcVis++ + hr := &r[nb.To] + if hr.done { + continue // skip nodes already done + } + dist := cr.dist + w(nb.Label) + vl := rp[nb.To].Len + visited := vl > 0 + if visited { + if dist > hr.dist { + continue // distance is worse + } + // tie breaker is a nice touch and doesn't seem to + // impact performance much. + if dist == hr.dist && nextLen >= vl { + continue // distance same, but number of nodes is no better + } + } + // the path through current to this node is shortest so far. + // record new path data for this node and update tentative set. + hr.dist = dist + rp[nb.To].Len = nextLen + rp[nb.To].From = current + labels[nb.To] = nb.Label + if visited { + heap.Fix(&t, hr.fx) + } else { + heap.Push(&t, hr) + } + } + //d.ndVis++ + if len(t) == 0 { + // no more reachable nodes. AllPaths normal return + return f, labels, dist, nDone + } + // new current is node with smallest tentative distance + cr = heap.Pop(&t).(*tentResult) + cr.done = true + nDone++ + current = cr.nx + dist[current] = cr.dist // store final distance + } + // normal return for single shortest path search + return f, labels, dist, -1 +} + +// DijkstraPath finds a single shortest path. +// +// Returned is the path as returned by FromList.LabeledPathTo and the total +// path distance. +func (g LabeledAdjacencyList) DijkstraPath(start, end NI, w WeightFunc) (LabeledPath, float64) { + f, labels, dist, _ := g.Dijkstra(start, end, w) + return f.PathToLabeled(end, labels, nil), dist[end] +} + +// tent implements container/heap +func (t tent) Len() int { return len(t) } +func (t tent) Less(i, j int) bool { return t[i].dist < t[j].dist } +func (t tent) Swap(i, j int) { + t[i], t[j] = t[j], t[i] + t[i].fx = i + t[j].fx = j +} +func (s *tent) Push(x interface{}) { + nd := x.(*tentResult) + nd.fx = len(*s) + *s = append(*s, nd) +} +func (s *tent) Pop() interface{} { + t := *s + last := len(t) - 1 + *s = t[:last] + return t[last] +} + +type tentResult struct { + dist float64 // tentative distance, sum of arc weights + nx NI // slice index, "node id" + fx int // heap.Fix index + done bool +} + +type tent []*tentResult diff --git a/vendor/github.com/soniakeys/graph/undir.go b/vendor/github.com/soniakeys/graph/undir.go new file mode 100644 index 00000000..21ef7b59 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/undir.go @@ -0,0 +1,817 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +// undir.go has methods specific to undirected graphs, Undirected and +// LabeledUndirected. + +import ( + "fmt" + + "github.com/soniakeys/bits" +) + +// AddEdge adds an edge to a graph. +// +// It can be useful for constructing undirected graphs. +// +// When n1 and n2 are distinct, it adds the arc n1->n2 and the reciprocal +// n2->n1. When n1 and n2 are the same, it adds a single arc loop. +// +// The pointer receiver allows the method to expand the graph as needed +// to include the values n1 and n2. If n1 or n2 happen to be greater than +// len(*p) the method does not panic, but simply expands the graph. +// +// If you know or can compute the final graph order however, consider +// preallocating to avoid any overhead of expanding the graph. +// See second example, "More". +func (p *Undirected) AddEdge(n1, n2 NI) { + // Similar code in LabeledAdjacencyList.AddEdge. + + // determine max of the two end points + max := n1 + if n2 > max { + max = n2 + } + // expand graph if needed, to include both + g := p.AdjacencyList + if int(max) >= len(g) { + p.AdjacencyList = make(AdjacencyList, max+1) + copy(p.AdjacencyList, g) + g = p.AdjacencyList + } + // create one half-arc, + g[n1] = append(g[n1], n2) + // and except for loops, create the reciprocal + if n1 != n2 { + g[n2] = append(g[n2], n1) + } +} + +// RemoveEdge removes a single edge between nodes n1 and n2. +// +// It removes reciprocal arcs in the case of distinct n1 and n2 or removes +// a single arc loop in the case of n1 == n2. +// +// Returns true if the specified edge is found and successfully removed, +// false if the edge does not exist. +func (g Undirected) RemoveEdge(n1, n2 NI) (ok bool) { + ok, x1, x2 := g.HasEdge(n1, n2) + if !ok { + return + } + a := g.AdjacencyList + to := a[n1] + last := len(to) - 1 + to[x1] = to[last] + a[n1] = to[:last] + if n1 == n2 { + return + } + to = a[n2] + last = len(to) - 1 + to[x2] = to[last] + a[n2] = to[:last] + return +} + +// ArcDensity returns density for a simple directed graph. +// +// Parameter n is order, or number of nodes of a simple directed graph. +// Parameter a is the arc size, or number of directed arcs. +// +// Returned density is the fraction `a` over the total possible number of arcs +// or a / (n * (n-1)). +// +// See also Density for density of a simple undirected graph. +// +// See also the corresponding methods AdjacencyList.ArcDensity and +// LabeledAdjacencyList.ArcDensity. +func ArcDensity(n, a int) float64 { + return float64(a) / (float64(n) * float64(n-1)) +} + +// Density returns density for a simple undirected graph. +// +// Parameter n is order, or number of nodes of a simple undirected graph. +// Parameter m is the size, or number of undirected edges. +// +// Returned density is the fraction m over the total possible number of edges +// or m / ((n * (n-1))/2). +// +// See also ArcDensity for simple directed graphs. +// +// See also the corresponding methods AdjacencyList.Density and +// LabeledAdjacencyList.Density. +func Density(n, m int) float64 { + return float64(m) * 2 / (float64(n) * float64(n-1)) +} + +// An EdgeVisitor is an argument to some traversal methods. +// +// Traversal methods call the visitor function for each edge visited. +// Argument e is the edge being visited. +type EdgeVisitor func(e Edge) + +// Edges iterates over the edges of an undirected graph. +// +// Edge visitor v is called for each edge of the graph. That is, it is called +// once for each reciprocal arc pair and once for each loop. +// +// See also LabeledUndirected.Edges for a labeled version. +// See also Undirected.SimpleEdges for a version that emits only the simple +// subgraph. +func (g Undirected) Edges(v EdgeVisitor) { + a := g.AdjacencyList + unpaired := make(AdjacencyList, len(a)) + for fr, to := range a { + arc: // for each arc in a + for _, to := range to { + if to == NI(fr) { + v(Edge{NI(fr), to}) // output loop + continue + } + // search unpaired arcs + ut := unpaired[to] + for i, u := range ut { + if u == NI(fr) { // found reciprocal + v(Edge{u, to}) // output edge + last := len(ut) - 1 + ut[i] = ut[last] + unpaired[to] = ut[:last] + continue arc + } + } + // reciprocal not found + unpaired[fr] = append(unpaired[fr], to) + } + } + // undefined behavior is that unpaired arcs are silently ignored. +} + +// FromList builds a forest with a tree spanning each connected component. +// +// For each component a root is chosen and spanning is done with the method +// Undirected.SpanTree, and so is breadth-first. Returned is a FromList with +// all spanned trees, a list of roots chosen, and a bool indicating if the +// receiver graph g was found to be a simple graph connected as a forest. +// Any cycles, loops, or parallel edges in any component will cause +// simpleForest to be false, but FromList f will still be populated with +// a valid and complete spanning forest. +func (g Undirected) FromList() (f FromList, roots []NI, simpleForest bool) { + p := make([]PathEnd, g.Order()) + for i := range p { + p[i].From = -1 + } + f.Paths = p + simpleForest = true + ts := 0 + for n := range g.AdjacencyList { + if p[n].From >= 0 { + continue + } + roots = append(roots, NI(n)) + ns, st := g.SpanTree(NI(n), &f) + if !st { + simpleForest = false + } + ts += ns + if ts == len(p) { + break + } + } + return +} + +// HasEdge returns true if g has any edge between nodes n1 and n2. +// +// Also returned are indexes x1 and x2 such that g[n1][x1] == n2 +// and g[n2][x2] == n1. If no edge between n1 and n2 is present HasArc +// returns `has` == false. +// +// See also HasArc. If you are interested only in the boolean result and +// g is a well formed (passes IsUndirected) then HasArc is an adequate test. +func (g Undirected) HasEdge(n1, n2 NI) (has bool, x1, x2 int) { + if has, x1 = g.HasArc(n1, n2); !has { + return has, x1, x1 + } + has, x2 = g.HasArc(n2, n1) + return +} + +// SimpleEdges iterates over the edges of the simple subgraph of an undirected +// graph. +// +// Edge visitor v is called for each pair of distinct nodes that is connected +// with an edge. That is, loops are ignored and parallel edges are reduced to +// a single edge. +// +// See also Undirected.Edges for a version that emits all edges. +func (g Undirected) SimpleEdges(v EdgeVisitor) { + for fr, to := range g.AdjacencyList { + e := bits.New(len(g.AdjacencyList)) + for _, to := range to { + if to > NI(fr) && e.Bit(int(to)) == 0 { + e.SetBit(int(to), 1) + v(Edge{NI(fr), to}) + } + } + } + // undefined behavior is that unpaired arcs may or may not be emitted. +} + +// SpanTree builds a tree spanning a connected component. +// +// The component is spanned by breadth-first search from the given root. +// The resulting spanning tree in stored a FromList. +// +// If FromList.Paths is not the same length as g, it is allocated and +// initialized. This allows a zero value FromList to be passed as f. +// If FromList.Paths is the same length as g, it is used as is and is not +// reinitialized. This allows multiple trees to be spanned in the same +// FromList with successive calls. +// +// For nodes spanned, the Path member of the returned FromList is populated +// with both From and Len values. The MaxLen member will be updated but +// not Leaves. +// +// Returned is the number of nodes spanned, which will be the number of nodes +// in the component, and a bool indicating if the component was found to be a +// simply connected unrooted tree in the receiver graph g. Any cycles, loops, +// or parallel edges in the component will cause simpleTree to be false, but +// FromList f will still be populated with a valid and complete spanning tree. +func (g Undirected) SpanTree(root NI, f *FromList) (nSpanned int, simpleTree bool) { + a := g.AdjacencyList + p := f.Paths + if len(p) != len(a) { + p = make([]PathEnd, len(a)) + for i := range p { + p[i].From = -1 + } + f.Paths = p + } + simpleTree = true + p[root] = PathEnd{From: -1, Len: 1} + type arc struct { + from NI + half NI + } + var next []arc + frontier := []arc{{-1, root}} + for len(frontier) > 0 { + for _, fa := range frontier { // fa frontier arc + nSpanned++ + l := p[fa.half].Len + 1 + for _, to := range a[fa.half] { + if to == fa.from { + continue + } + if p[to].Len > 0 { + simpleTree = false + continue + } + p[to] = PathEnd{From: fa.half, Len: l} + if l > f.MaxLen { + f.MaxLen = l + } + next = append(next, arc{fa.half, to}) + } + } + frontier, next = next, frontier[:0] + } + return +} + +// TarjanBiconnectedComponents decomposes a graph into maximal biconnected +// components, components for which if any node were removed the component +// would remain connected. +// +// The receiver g must be a simple graph. The method calls the emit argument +// for each component identified, as long as emit returns true. If emit +// returns false, TarjanBiconnectedComponents returns immediately. +// +// See also the eqivalent labeled TarjanBiconnectedComponents. +func (g Undirected) TarjanBiconnectedComponents(emit func([]Edge) bool) { + // Implemented closely to pseudocode in "Depth-first search and linear + // graph algorithms", Robert Tarjan, SIAM J. Comput. Vol. 1, No. 2, + // June 1972. + // + // Note Tarjan's "adjacency structure" is graph.AdjacencyList, + // His "adjacency list" is an element of a graph.AdjacencyList, also + // termed a "to-list", "neighbor list", or "child list." + a := g.AdjacencyList + number := make([]int, len(a)) + lowpt := make([]int, len(a)) + var stack []Edge + var i int + var biconnect func(NI, NI) bool + biconnect = func(v, u NI) bool { + i++ + number[v] = i + lowpt[v] = i + for _, w := range a[v] { + if number[w] == 0 { + stack = append(stack, Edge{v, w}) + if !biconnect(w, v) { + return false + } + if lowpt[w] < lowpt[v] { + lowpt[v] = lowpt[w] + } + if lowpt[w] >= number[v] { + var bcc []Edge + top := len(stack) - 1 + for number[stack[top].N1] >= number[w] { + bcc = append(bcc, stack[top]) + stack = stack[:top] + top-- + } + bcc = append(bcc, stack[top]) + stack = stack[:top] + top-- + if !emit(bcc) { + return false + } + } + } else if number[w] < number[v] && w != u { + stack = append(stack, Edge{v, w}) + if number[w] < lowpt[v] { + lowpt[v] = number[w] + } + } + } + return true + } + for w := range a { + if number[w] == 0 && !biconnect(NI(w), -1) { + return + } + } +} + +func (g Undirected) BlockCut(block func([]Edge) bool, cut func(NI) bool, isolated func(NI) bool) { + a := g.AdjacencyList + number := make([]int, len(a)) + lowpt := make([]int, len(a)) + var stack []Edge + var i, rc int + var biconnect func(NI, NI) bool + biconnect = func(v, u NI) bool { + i++ + number[v] = i + lowpt[v] = i + for _, w := range a[v] { + if number[w] == 0 { + if u < 0 { + rc++ + } + stack = append(stack, Edge{v, w}) + if !biconnect(w, v) { + return false + } + if lowpt[w] < lowpt[v] { + lowpt[v] = lowpt[w] + } + if lowpt[w] >= number[v] { + if u >= 0 && !cut(v) { + return false + } + var bcc []Edge + top := len(stack) - 1 + for number[stack[top].N1] >= number[w] { + bcc = append(bcc, stack[top]) + stack = stack[:top] + top-- + } + bcc = append(bcc, stack[top]) + stack = stack[:top] + top-- + if !block(bcc) { + return false + } + } + } else if number[w] < number[v] && w != u { + stack = append(stack, Edge{v, w}) + if number[w] < lowpt[v] { + lowpt[v] = number[w] + } + } + } + if u < 0 && rc > 1 { + return cut(v) + } + return true + } + for w := range a { + if number[w] > 0 { + continue + } + if len(a[w]) == 0 { + if !isolated(NI(w)) { + return + } + continue + } + rc = 0 + if !biconnect(NI(w), -1) { + return + } + } +} + +// AddEdge adds an edge to a labeled graph. +// +// It can be useful for constructing undirected graphs. +// +// When n1 and n2 are distinct, it adds the arc n1->n2 and the reciprocal +// n2->n1. When n1 and n2 are the same, it adds a single arc loop. +// +// If the edge already exists in *p, a parallel edge is added. +// +// The pointer receiver allows the method to expand the graph as needed +// to include the values n1 and n2. If n1 or n2 happen to be greater than +// len(*p) the method does not panic, but simply expands the graph. +func (p *LabeledUndirected) AddEdge(e Edge, l LI) { + // Similar code in AdjacencyList.AddEdge. + + // determine max of the two end points + max := e.N1 + if e.N2 > max { + max = e.N2 + } + // expand graph if needed, to include both + g := p.LabeledAdjacencyList + if max >= NI(len(g)) { + p.LabeledAdjacencyList = make(LabeledAdjacencyList, max+1) + copy(p.LabeledAdjacencyList, g) + g = p.LabeledAdjacencyList + } + // create one half-arc, + g[e.N1] = append(g[e.N1], Half{To: e.N2, Label: l}) + // and except for loops, create the reciprocal + if e.N1 != e.N2 { + g[e.N2] = append(g[e.N2], Half{To: e.N1, Label: l}) + } +} + +// A LabeledEdgeVisitor is an argument to some traversal methods. +// +// Traversal methods call the visitor function for each edge visited. +// Argument e is the edge being visited. +type LabeledEdgeVisitor func(e LabeledEdge) + +// Edges iterates over the edges of a labeled undirected graph. +// +// Edge visitor v is called for each edge of the graph. That is, it is called +// once for each reciprocal arc pair and once for each loop. +// +// See also Undirected.Edges for an unlabeled version. +// See also the more simplistic LabeledAdjacencyList.ArcsAsEdges. +func (g LabeledUndirected) Edges(v LabeledEdgeVisitor) { + // similar code in LabeledAdjacencyList.InUndirected + a := g.LabeledAdjacencyList + unpaired := make(LabeledAdjacencyList, len(a)) + for fr, to := range a { + arc: // for each arc in a + for _, to := range to { + if to.To == NI(fr) { + v(LabeledEdge{Edge{NI(fr), to.To}, to.Label}) // output loop + continue + } + // search unpaired arcs + ut := unpaired[to.To] + for i, u := range ut { + if u.To == NI(fr) && u.Label == to.Label { // found reciprocal + v(LabeledEdge{Edge{NI(fr), to.To}, to.Label}) // output edge + last := len(ut) - 1 + ut[i] = ut[last] + unpaired[to.To] = ut[:last] + continue arc + } + } + // reciprocal not found + unpaired[fr] = append(unpaired[fr], to) + } + } +} + +// FromList builds a forest with a tree spanning each connected component in g. +// +// A root is chosen and spanning is done with the LabeledUndirected.SpanTree +// method, and so is breadth-first. Returned is a FromList with all spanned +// trees, labels corresponding to arcs in f, +// a list of roots chosen, and a bool indicating if the receiver graph g was +// found to be a simple graph connected as a forest. Any cycles, loops, or +// parallel edges in any component will cause simpleForest to be false, but +// FromList f will still be populated with a valid and complete spanning forest. + +// FromList builds a forest with a tree spanning each connected component. +// +// For each component a root is chosen and spanning is done with the method +// Undirected.SpanTree, and so is breadth-first. Returned is a FromList with +// all spanned trees, labels corresponding to arcs in f, a list of roots +// chosen, and a bool indicating if the receiver graph g was found to be a +// simple graph connected as a forest. Any cycles, loops, or parallel edges +// in any component will cause simpleForest to be false, but FromList f will +// still be populated with a valid and complete spanning forest. +func (g LabeledUndirected) FromList() (f FromList, labels []LI, roots []NI, simpleForest bool) { + p := make([]PathEnd, g.Order()) + for i := range p { + p[i].From = -1 + } + f.Paths = p + labels = make([]LI, len(p)) + simpleForest = true + ts := 0 + for n := range g.LabeledAdjacencyList { + if p[n].From >= 0 { + continue + } + roots = append(roots, NI(n)) + ns, st := g.SpanTree(NI(n), &f, labels) + if !st { + simpleForest = false + } + ts += ns + if ts == len(p) { + break + } + } + return +} + +// SpanTree builds a tree spanning a connected component. +// +// The component is spanned by breadth-first search from the given root. +// The resulting spanning tree in stored a FromList, and arc labels optionally +// stored in a slice. +// +// If FromList.Paths is not the same length as g, it is allocated and +// initialized. This allows a zero value FromList to be passed as f. +// If FromList.Paths is the same length as g, it is used as is and is not +// reinitialized. This allows multiple trees to be spanned in the same +// FromList with successive calls. +// +// For nodes spanned, the Path member of returned FromList f is populated +// populated with both From and Len values. The MaxLen member will be +// updated but not Leaves. +// +// The labels slice will be populated only if it is same length as g. +// Nil can be passed for example if labels are not needed. +// +// Returned is the number of nodes spanned, which will be the number of nodes +// in the component, and a bool indicating if the component was found to be a +// simply connected unrooted tree in the receiver graph g. Any cycles, loops, +// or parallel edges in the component will cause simpleTree to be false, but +// FromList f will still be populated with a valid and complete spanning tree. +func (g LabeledUndirected) SpanTree(root NI, f *FromList, labels []LI) (nSpanned int, simple bool) { + a := g.LabeledAdjacencyList + p := f.Paths + if len(p) != len(a) { + p = make([]PathEnd, len(a)) + for i := range p { + p[i].From = -1 + } + f.Paths = p + } + simple = true + p[root].Len = 1 + type arc struct { + from NI + half Half + } + var next []arc + frontier := []arc{{-1, Half{root, -1}}} + for len(frontier) > 0 { + for _, fa := range frontier { // fa frontier arc + nSpanned++ + l := p[fa.half.To].Len + 1 + for _, to := range a[fa.half.To] { + if to.To == fa.from && to.Label == fa.half.Label { + continue + } + if p[to.To].Len > 0 { + simple = false + continue + } + p[to.To] = PathEnd{From: fa.half.To, Len: l} + if len(labels) == len(p) { + labels[to.To] = to.Label + } + if l > f.MaxLen { + f.MaxLen = l + } + next = append(next, arc{fa.half.To, to}) + } + } + frontier, next = next, frontier[:0] + } + return +} + +// HasEdge returns true if g has any edge between nodes n1 and n2. +// +// Also returned are indexes x1 and x2 such that g[n1][x1] == Half{n2, l} +// and g[n2][x2] == {n1, l} for some label l. If no edge between n1 and n2 +// exists, HasArc returns `has` == false. +// +// See also HasArc. If you are only interested in the boolean result then +// HasArc is an adequate test. +func (g LabeledUndirected) HasEdge(n1, n2 NI) (has bool, x1, x2 int) { + if has, x1 = g.HasArc(n1, n2); !has { + return has, x1, x1 + } + has, x2 = g.HasArcLabel(n2, n1, g.LabeledAdjacencyList[n1][x1].Label) + return +} + +// HasEdgeLabel returns true if g has any edge between nodes n1 and n2 with +// label l. +// +// Also returned are indexes x1 and x2 such that g[n1][x1] == Half{n2, l} +// and g[n2][x2] == Half{n1, l}. If no edge between n1 and n2 with label l +// is present HasArc returns `has` == false. +func (g LabeledUndirected) HasEdgeLabel(n1, n2 NI, l LI) (has bool, x1, x2 int) { + if has, x1 = g.HasArcLabel(n1, n2, l); !has { + return has, x1, x1 + } + has, x2 = g.HasArcLabel(n2, n1, l) + return +} + +// RemoveEdge removes a single edge between nodes n1 and n2. +// +// It removes reciprocal arcs in the case of distinct n1 and n2 or removes +// a single arc loop in the case of n1 == n2. +// +// If the specified edge is found and successfully removed, RemoveEdge returns +// true and the label of the edge removed. If no edge exists between n1 and n2, +// RemoveEdge returns false, 0. +func (g LabeledUndirected) RemoveEdge(n1, n2 NI) (ok bool, label LI) { + ok, x1, x2 := g.HasEdge(n1, n2) + if !ok { + return + } + a := g.LabeledAdjacencyList + to := a[n1] + label = to[x1].Label // return value + last := len(to) - 1 + to[x1] = to[last] + a[n1] = to[:last] + if n1 == n2 { + return + } + to = a[n2] + last = len(to) - 1 + to[x2] = to[last] + a[n2] = to[:last] + return +} + +// RemoveEdgeLabel removes a single edge between nodes n1 and n2 with label l. +// +// It removes reciprocal arcs in the case of distinct n1 and n2 or removes +// a single arc loop in the case of n1 == n2. +// +// Returns true if the specified edge is found and successfully removed, +// false if the edge does not exist. +func (g LabeledUndirected) RemoveEdgeLabel(n1, n2 NI, l LI) (ok bool) { + ok, x1, x2 := g.HasEdgeLabel(n1, n2, l) + if !ok { + return + } + a := g.LabeledAdjacencyList + to := a[n1] + last := len(to) - 1 + to[x1] = to[last] + a[n1] = to[:last] + if n1 == n2 { + return + } + to = a[n2] + last = len(to) - 1 + to[x2] = to[last] + a[n2] = to[:last] + return +} + +// TarjanBiconnectedComponents decomposes a graph into maximal biconnected +// components, components for which if any node were removed the component +// would remain connected. +// +// The receiver g must be a simple graph. The method calls the emit argument +// for each component identified, as long as emit returns true. If emit +// returns false, TarjanBiconnectedComponents returns immediately. +// +// See also the eqivalent unlabeled TarjanBiconnectedComponents. +func (g LabeledUndirected) TarjanBiconnectedComponents(emit func([]LabeledEdge) bool) { + // Code nearly identical to unlabled version. + number := make([]int, g.Order()) + lowpt := make([]int, g.Order()) + var stack []LabeledEdge + var i int + var biconnect func(NI, NI) bool + biconnect = func(v, u NI) bool { + i++ + number[v] = i + lowpt[v] = i + for _, w := range g.LabeledAdjacencyList[v] { + if number[w.To] == 0 { + stack = append(stack, LabeledEdge{Edge{v, w.To}, w.Label}) + if !biconnect(w.To, v) { + return false + } + if lowpt[w.To] < lowpt[v] { + lowpt[v] = lowpt[w.To] + } + if lowpt[w.To] >= number[v] { + var bcc []LabeledEdge + top := len(stack) - 1 + for number[stack[top].N1] >= number[w.To] { + bcc = append(bcc, stack[top]) + stack = stack[:top] + top-- + } + bcc = append(bcc, stack[top]) + stack = stack[:top] + top-- + if !emit(bcc) { + return false + } + } + } else if number[w.To] < number[v] && w.To != u { + stack = append(stack, LabeledEdge{Edge{v, w.To}, w.Label}) + if number[w.To] < lowpt[v] { + lowpt[v] = number[w.To] + } + } + } + return true + } + for w := range g.LabeledAdjacencyList { + if number[w] == 0 && !biconnect(NI(w), -1) { + return + } + } +} + +func (e *eulerian) pushUndir() error { + for u := e.top(); ; { + e.uv.SetBit(int(u), 0) + arcs := e.g[u] + if len(arcs) == 0 { + return nil + } + w := arcs[0] + e.s++ + e.p[e.s] = w + e.g[u] = arcs[1:] // consume arc + // difference from directed counterpart in dir.go: + // as long as it's not a loop, consume reciprocal arc as well + if w != u { + a2 := e.g[w] + for x, rx := range a2 { + if rx == u { // here it is + last := len(a2) - 1 + a2[x] = a2[last] // someone else gets the seat + e.g[w] = a2[:last] // and it's gone. + goto l + } + } + return fmt.Errorf("graph not undirected. %d -> %d reciprocal not found", u, w) + } + l: + u = w + } +} + +func (e *labEulerian) pushUndir() error { + for u := e.top(); ; { + e.uv.SetBit(int(u.To), 0) + arcs := e.g[u.To] + if len(arcs) == 0 { + return nil + } + w := arcs[0] + e.s++ + e.p[e.s] = w + e.g[u.To] = arcs[1:] // consume arc + // difference from directed counterpart in dir.go: + // as long as it's not a loop, consume reciprocal arc as well + if w.To != u.To { + a2 := e.g[w.To] + for x, rx := range a2 { + if rx.To == u.To && rx.Label == w.Label { // here it is + last := len(a2) - 1 + a2[x] = a2[last] // someone else can have the seat + e.g[w.To] = a2[:last] // and it's gone. + goto l + } + } + return fmt.Errorf("graph not undirected. %d -> %v reciprocal not found", u.To, w) + } + l: + u = w + } +} diff --git a/vendor/github.com/soniakeys/graph/undir_RO.go b/vendor/github.com/soniakeys/graph/undir_RO.go new file mode 100644 index 00000000..7707daf1 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/undir_RO.go @@ -0,0 +1,1138 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +import ( + "errors" + "fmt" + + "github.com/soniakeys/bits" +) + +// undir_RO.go is code generated from undir_cg.go by directives in graph.go. +// Editing undir_cg.go is okay. It is the code generation source. +// DO NOT EDIT undir_RO.go. +// The RO means read only and it is upper case RO to slow you down a bit +// in case you start to edit the file. +//------------------- + +// Bipartite constructs an object indexing the bipartite structure of a graph. +// +// In a bipartite component, nodes can be partitioned into two sets, or +// "colors," such that every edge in the component goes from one set to the +// other. +// +// If the graph is bipartite, the method constructs and returns a new +// Bipartite object as b and returns ok = true. +// +// If the component is not bipartite, a representative odd cycle as oc and +// returns ok = false. +// +// In the case of a graph with mulitiple connected components, this method +// provides no control over the color orientation by component. See +// Undirected.BipartiteComponent if this control is needed. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) Bipartite() (b *Bipartite, oc []NI, ok bool) { + c1 := bits.New(g.Order()) + c2 := bits.New(g.Order()) + r, _, _ := g.ConnectedComponentReps() + // accumulate n2 number of zero bits in c2 as number of one bits in n1 + var n, n2 int + for _, r := range r { + ok, n, _, oc = g.BipartiteComponent(r, c1, c2) + if !ok { + return + } + n2 += n + } + return &Bipartite{g, c2, n2}, nil, true +} + +// BipartiteComponent analyzes the bipartite structure of a connected component +// of an undirected graph. +// +// In a bipartite component, nodes can be partitioned into two sets, or +// "colors," such that every edge in the component goes from one set to the +// other. +// +// Argument n can be any representative node of the component to be analyzed. +// Arguments c1 and c2 must be separate bits.Bits objects constructed to be +// of length of the number of nodes of g. These bitmaps are used in the +// component traversal and the bits of the component must be zero when the +// method is called. +// +// If the component is bipartite, BipartiteComponent populates bitmaps +// c1 and c2 with the two-coloring of the component, always assigning the set +// with representative node n to bitmap c1. It returns b = true, +// and also returns the number of bits set in c1 and c2 as n1 and n2 +// respectively. +// +// If the component is not bipartite, BipartiteComponent returns b = false +// and a representative odd cycle as oc. +// +// See also method Bipartite. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) BipartiteComponent(n NI, c1, c2 bits.Bits) (b bool, n1, n2 int, oc []NI) { + a := g.AdjacencyList + b = true + var open bool + var df func(n NI, c1, c2 *bits.Bits, n1, n2 *int) + df = func(n NI, c1, c2 *bits.Bits, n1, n2 *int) { + c1.SetBit(int(n), 1) + *n1++ + for _, nb := range a[n] { + if c1.Bit(int(nb)) == 1 { + b = false + oc = []NI{nb, n} + open = true + return + } + if c2.Bit(int(nb)) == 1 { + continue + } + df(nb, c2, c1, n2, n1) + if b { + continue + } + switch { + case !open: + case n == oc[0]: + open = false + default: + oc = append(oc, n) + } + return + } + } + df(n, &c1, &c2, &n1, &n2) + if b { + return b, n1, n2, nil + } + return b, 0, 0, oc +} + +// BronKerbosch1 finds maximal cliques in an undirected graph. +// +// The graph must not contain parallel edges or loops. +// +// See https://en.wikipedia.org/wiki/Clique_(graph_theory) and +// https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm for background. +// +// This method implements the BronKerbosch1 algorithm of WP; that is, +// the original algorithm without improvements. +// +// The method calls the emit argument for each maximal clique in g, as long +// as emit returns true. If emit returns false, BronKerbosch1 returns +// immediately. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also more sophisticated variants BronKerbosch2 and BronKerbosch3. +func (g Undirected) BronKerbosch1(emit func(bits.Bits) bool) { + a := g.AdjacencyList + var f func(R, P, X bits.Bits) bool + f = func(R, P, X bits.Bits) bool { + switch { + case !P.AllZeros(): + r2 := bits.New(len(a)) + p2 := bits.New(len(a)) + x2 := bits.New(len(a)) + pf := func(n int) bool { + r2.Set(R) + r2.SetBit(n, 1) + p2.ClearAll() + x2.ClearAll() + for _, to := range a[n] { + if P.Bit(int(to)) == 1 { + p2.SetBit(int(to), 1) + } + if X.Bit(int(to)) == 1 { + x2.SetBit(int(to), 1) + } + } + if !f(r2, p2, x2) { + return false + } + P.SetBit(n, 0) + X.SetBit(n, 1) + return true + } + if !P.IterateOnes(pf) { + return false + } + case X.AllZeros(): + return emit(R) + } + return true + } + var R, P, X bits.Bits + R = bits.New(len(a)) + P = bits.New(len(a)) + X = bits.New(len(a)) + P.SetAll() + f(R, P, X) +} + +// BKPivotMaxDegree is a strategy for BronKerbosch methods. +// +// To use it, take the method value (see golang.org/ref/spec#Method_values) +// and pass it as the argument to BronKerbosch2 or 3. +// +// The strategy is to pick the node from P or X with the maximum degree +// (number of edges) in g. Note this is a shortcut from evaluating degrees +// in P. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) BKPivotMaxDegree(P, X bits.Bits) (p NI) { + // choose pivot u as highest degree node from P or X + a := g.AdjacencyList + maxDeg := -1 + P.IterateOnes(func(n int) bool { // scan P + if d := len(a[n]); d > maxDeg { + p = NI(n) + maxDeg = d + } + return true + }) + X.IterateOnes(func(n int) bool { // scan X + if d := len(a[n]); d > maxDeg { + p = NI(n) + maxDeg = d + } + return true + }) + return +} + +// BKPivotMinP is a strategy for BronKerbosch methods. +// +// To use it, take the method value (see golang.org/ref/spec#Method_values) +// and pass it as the argument to BronKerbosch2 or 3. +// +// The strategy is to simply pick the first node in P. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) BKPivotMinP(P, X bits.Bits) NI { + return NI(P.OneFrom(0)) +} + +// BronKerbosch2 finds maximal cliques in an undirected graph. +// +// The graph must not contain parallel edges or loops. +// +// See https://en.wikipedia.org/wiki/Clique_(graph_theory) and +// https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm for background. +// +// This method implements the BronKerbosch2 algorithm of WP; that is, +// the original algorithm plus pivoting. +// +// The argument is a pivot function that must return a node of P or X. +// P is guaranteed to contain at least one node. X is not. +// For example see BKPivotMaxDegree. +// +// The method calls the emit argument for each maximal clique in g, as long +// as emit returns true. If emit returns false, BronKerbosch1 returns +// immediately. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also simpler variant BronKerbosch1 and more sophisticated variant +// BronKerbosch3. +func (g Undirected) BronKerbosch2(pivot func(P, X bits.Bits) NI, emit func(bits.Bits) bool) { + a := g.AdjacencyList + var f func(R, P, X bits.Bits) bool + f = func(R, P, X bits.Bits) bool { + switch { + case !P.AllZeros(): + r2 := bits.New(len(a)) + p2 := bits.New(len(a)) + x2 := bits.New(len(a)) + pnu := bits.New(len(a)) + // compute P \ N(u). next 5 lines are only difference from BK1 + pnu.Set(P) + for _, to := range a[pivot(P, X)] { + pnu.SetBit(int(to), 0) + } + // remaining code like BK1 + pf := func(n int) bool { + r2.Set(R) + r2.SetBit(n, 1) + p2.ClearAll() + x2.ClearAll() + for _, to := range a[n] { + if P.Bit(int(to)) == 1 { + p2.SetBit(int(to), 1) + } + if X.Bit(int(to)) == 1 { + x2.SetBit(int(to), 1) + } + } + if !f(r2, p2, x2) { + return false + } + P.SetBit(n, 0) + X.SetBit(n, 1) + return true + } + if !pnu.IterateOnes(pf) { + return false + } + case X.AllZeros(): + return emit(R) + } + return true + } + R := bits.New(len(a)) + P := bits.New(len(a)) + X := bits.New(len(a)) + P.SetAll() + f(R, P, X) +} + +// BronKerbosch3 finds maximal cliques in an undirected graph. +// +// The graph must not contain parallel edges or loops. +// +// See https://en.wikipedia.org/wiki/Clique_(graph_theory) and +// https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm for background. +// +// This method implements the BronKerbosch3 algorithm of WP; that is, +// the original algorithm with pivoting and degeneracy ordering. +// +// The argument is a pivot function that must return a node of P or X. +// P is guaranteed to contain at least one node. X is not. +// For example see BKPivotMaxDegree. +// +// The method calls the emit argument for each maximal clique in g, as long +// as emit returns true. If emit returns false, BronKerbosch1 returns +// immediately. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also simpler variants BronKerbosch1 and BronKerbosch2. +func (g Undirected) BronKerbosch3(pivot func(P, X bits.Bits) NI, emit func(bits.Bits) bool) { + a := g.AdjacencyList + var f func(R, P, X bits.Bits) bool + f = func(R, P, X bits.Bits) bool { + switch { + case !P.AllZeros(): + r2 := bits.New(len(a)) + p2 := bits.New(len(a)) + x2 := bits.New(len(a)) + pnu := bits.New(len(a)) + // compute P \ N(u). next lines are only difference from BK1 + pnu.Set(P) + for _, to := range a[pivot(P, X)] { + pnu.SetBit(int(to), 0) + } + // remaining code like BK2 + pf := func(n int) bool { + r2.Set(R) + r2.SetBit(n, 1) + p2.ClearAll() + x2.ClearAll() + for _, to := range a[n] { + if P.Bit(int(to)) == 1 { + p2.SetBit(int(to), 1) + } + if X.Bit(int(to)) == 1 { + x2.SetBit(int(to), 1) + } + } + if !f(r2, p2, x2) { + return false + } + P.SetBit(n, 0) + X.SetBit(n, 1) + return true + } + if !pnu.IterateOnes(pf) { + return false + } + case X.AllZeros(): + return emit(R) + } + return true + } + R := bits.New(len(a)) + P := bits.New(len(a)) + X := bits.New(len(a)) + P.SetAll() + // code above same as BK2 + // code below new to BK3 + ord, _ := g.DegeneracyOrdering() + p2 := bits.New(len(a)) + x2 := bits.New(len(a)) + for _, n := range ord { + R.SetBit(int(n), 1) + p2.ClearAll() + x2.ClearAll() + for _, to := range a[n] { + if P.Bit(int(to)) == 1 { + p2.SetBit(int(to), 1) + } + if X.Bit(int(to)) == 1 { + x2.SetBit(int(to), 1) + } + } + if !f(R, p2, x2) { + return + } + R.SetBit(int(n), 0) + P.SetBit(int(n), 0) + X.SetBit(int(n), 1) + } +} + +// ConnectedComponentBits returns a function that iterates over connected +// components of g, returning a member bitmap for each. +// +// Each call of the returned function returns the order, arc size, +// and bits of a connected component. The underlying bits allocation is +// the same for each call and is overwritten on subsequent calls. Use or +// save the bits before calling the function again. The function returns +// zeros after returning all connected components. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentInts, ConnectedComponentReps, and +// ConnectedComponentReps. +func (g Undirected) ConnectedComponentBits() func() (order, arcSize int, bits bits.Bits) { + a := g.AdjacencyList + vg := bits.New(len(a)) // nodes visited in graph + vc := bits.New(len(a)) // nodes visited in current component + var order, arcSize int + var df func(NI) + df = func(n NI) { + vg.SetBit(int(n), 1) + vc.SetBit(int(n), 1) + order++ + arcSize += len(a[n]) + for _, nb := range a[n] { + if vg.Bit(int(nb)) == 0 { + df(nb) + } + } + return + } + var n int + return func() (o, ma int, b bits.Bits) { + for ; n < len(a); n++ { + if vg.Bit(n) == 0 { + vc.ClearAll() + order, arcSize = 0, 0 + df(NI(n)) + return order, arcSize, vc + } + } + return // return zeros signalling no more components + } +} + +// ConnectedComponenInts returns a list of component numbers (ints) for each +// node of graph g. +// +// The method assigns numbers to components 1-based, 1 through the number of +// components. Return value ci contains the component number for each node. +// Return value nc is the number of components. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentBits, ConnectedComponentLists, and +// ConnectedComponentReps. +func (g Undirected) ConnectedComponentInts() (ci []int, nc int) { + a := g.AdjacencyList + ci = make([]int, len(a)) + var df func(NI) + df = func(nd NI) { + ci[nd] = nc + for _, to := range a[nd] { + if ci[to] == 0 { + df(to) + } + } + return + } + for nd := range a { + if ci[nd] == 0 { + nc++ + df(NI(nd)) + } + } + return +} + +// ConnectedComponentLists returns a function that iterates over connected +// components of g, returning the member list of each. +// +// Each call of the returned function returns a node list of a connected +// component and the arc size of the component. The returned function returns +// nil, 0 after returning all connected components. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentBits, ConnectedComponentInts, and +// ConnectedComponentReps. +func (g Undirected) ConnectedComponentLists() func() (nodes []NI, arcSize int) { + a := g.AdjacencyList + vg := bits.New(len(a)) // nodes visited in graph + var l []NI // accumulated node list of current component + var ma int // accumulated arc size of current component + var df func(NI) + df = func(n NI) { + vg.SetBit(int(n), 1) + l = append(l, n) + ma += len(a[n]) + for _, nb := range a[n] { + if vg.Bit(int(nb)) == 0 { + df(nb) + } + } + return + } + var n int + return func() ([]NI, int) { + for ; n < len(a); n++ { + if vg.Bit(n) == 0 { + l, ma = nil, 0 + df(NI(n)) + return l, ma + } + } + return nil, 0 + } +} + +// ConnectedComponentReps returns a representative node from each connected +// component of g. +// +// Returned is a slice with a single representative node from each connected +// component and also parallel slices with the orders and arc sizes +// in the corresponding components. +// +// This is fairly minimal information describing connected components. +// From a representative node, other nodes in the component can be reached +// by depth first traversal for example. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentBits and ConnectedComponentLists which can +// collect component members in a single traversal, and IsConnected which +// is an even simpler boolean test. +func (g Undirected) ConnectedComponentReps() (reps []NI, orders, arcSizes []int) { + a := g.AdjacencyList + c := bits.New(len(a)) + var o, ma int + var df func(NI) + df = func(n NI) { + c.SetBit(int(n), 1) + o++ + ma += len(a[n]) + for _, nb := range a[n] { + if c.Bit(int(nb)) == 0 { + df(nb) + } + } + return + } + for n := range a { + if c.Bit(n) == 0 { + o, ma = 0, 0 + df(NI(n)) + reps = append(reps, NI(n)) + orders = append(orders, o) + arcSizes = append(arcSizes, ma) + } + } + return +} + +// Copy makes a deep copy of g. +// Copy also computes the arc size ma, the number of arcs. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) Copy() (c Undirected, ma int) { + l, s := g.AdjacencyList.Copy() + return Undirected{l}, s +} + +// Degeneracy is a measure of dense subgraphs within a graph. +// +// See Wikipedia https://en.wikipedia.org/wiki/Degeneracy_(graph_theory) +// +// See also method DegeneracyOrdering which returns a degeneracy node +// ordering and k-core breaks. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) Degeneracy() (k int) { + a := g.AdjacencyList + // WP algorithm, attributed to Matula and Beck. + L := bits.New(len(a)) + d := make([]int, len(a)) + var D [][]NI + for v, nb := range a { + dv := len(nb) + d[v] = dv + for len(D) <= dv { + D = append(D, nil) + } + D[dv] = append(D[dv], NI(v)) + } + for range a { + // find a non-empty D + i := 0 + for len(D[i]) == 0 { + i++ + } + // k is max(i, k) + if i > k { + k = i + } + // select from D[i] + Di := D[i] + last := len(Di) - 1 + v := Di[last] + // Add v to ordering, remove from Di + L.SetBit(int(v), 1) + D[i] = Di[:last] + // move neighbors + for _, nb := range a[v] { + if L.Bit(int(nb)) == 1 { + continue + } + dn := d[nb] // old number of neighbors of nb + Ddn := D[dn] // nb is in this list + // remove it from the list + for wx, w := range Ddn { + if w == nb { + last := len(Ddn) - 1 + Ddn[wx], Ddn[last] = Ddn[last], Ddn[wx] + D[dn] = Ddn[:last] + } + } + dn-- // new number of neighbors + d[nb] = dn + // re--add it to it's new list + D[dn] = append(D[dn], nb) + } + } + return +} + +// DegeneracyOrdering computes degeneracy node ordering and k-core breaks. +// +// See Wikipedia https://en.wikipedia.org/wiki/Degeneracy_(graph_theory) +// +// In return value ordering, nodes are ordered by their "coreness" as +// defined at https://en.wikipedia.org/wiki/Degeneracy_(graph_theory)#k-Cores. +// +// Return value kbreaks indexes ordering by coreness number. len(kbreaks) +// will be one more than the graph degeneracy as returned by the Degeneracy +// method. If degeneracy is d, d = len(kbreaks) - 1, kbreaks[d] is the last +// value in kbreaks and ordering[:kbreaks[d]] contains nodes of the d-cores +// of the graph. kbreaks[0] is always the number of nodes in g as all nodes +// are in in a 0-core. +// +// Note that definitions of "k-core" differ on whether a k-core must be a +// single connected component. This method does not resolve individual +// connected components. +// +// See also method Degeneracy which returns just the degeneracy number. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) DegeneracyOrdering() (ordering []NI, kbreaks []int) { + a := g.AdjacencyList + // WP algorithm + k := 0 + ordering = make([]NI, len(a)) + kbreaks = []int{len(a)} + L := bits.New(len(a)) + d := make([]int, len(a)) + var D [][]NI + for v, nb := range a { + dv := len(nb) + d[v] = dv + for len(D) <= dv { + D = append(D, nil) + } + D[dv] = append(D[dv], NI(v)) + } + for ox := len(a) - 1; ox >= 0; ox-- { + // find a non-empty D + i := 0 + for len(D[i]) == 0 { + i++ + } + // k is max(i, k) + if i > k { + for len(kbreaks) <= i { + kbreaks = append(kbreaks, ox+1) + } + k = i + } + // select from D[i] + Di := D[i] + last := len(Di) - 1 + v := Di[last] + // Add v to ordering, remove from Di + ordering[ox] = v + L.SetBit(int(v), 1) + D[i] = Di[:last] + // move neighbors + for _, nb := range a[v] { + if L.Bit(int(nb)) == 1 { + continue + } + dn := d[nb] // old number of neighbors of nb + Ddn := D[dn] // nb is in this list + // remove it from the list + for wx, w := range Ddn { + if w == nb { + last := len(Ddn) - 1 + Ddn[wx], Ddn[last] = Ddn[last], Ddn[wx] + D[dn] = Ddn[:last] + } + } + dn-- // new number of neighbors + d[nb] = dn + // re--add it to it's new list + D[dn] = append(D[dn], nb) + } + } + //for i, j := 0, k; i < j; i, j = i+1, j-1 { + // kbreaks[i], kbreaks[j] = kbreaks[j], kbreaks[i] + //} + return +} + +// Degree for undirected graphs, returns the degree of a node. +// +// The degree of a node in an undirected graph is the number of incident +// edges, where loops count twice. +// +// If g is known to be loop-free, the result is simply equivalent to len(g[n]). +// See handshaking lemma example at AdjacencyList.ArcSize. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) Degree(n NI) int { + to := g.AdjacencyList[n] + d := len(to) // just "out" degree, + for _, to := range to { + if to == n { + d++ // except loops count twice + } + } + return d +} + +// DegreeCentralization returns the degree centralization metric of a graph. +// +// Degree of a node is one measure of node centrality and is directly +// available from the adjacency list representation. This allows degree +// centralization for the graph to be very efficiently computed. +// +// The value returned is from 0 to 1 inclusive for simple graphs of three or +// more nodes. As a special case, 0 is returned for graphs of two or fewer +// nodes. The value returned can be > 1 for graphs with loops or parallel +// edges. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) DegreeCentralization() float64 { + a := g.AdjacencyList + if len(a) <= 2 { + return 0 + } + var max, sum int + for _, to := range a { + if len(to) > max { + max = len(to) + } + sum += len(to) + } + return float64(len(a)*max-sum) / float64((len(a)-1)*(len(a)-2)) +} + +// Density returns density for a simple graph. +// +// See also Density function. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) Density() float64 { + return Density(g.Order(), g.Size()) +} + +// Eulerian scans an undirected graph to determine if it is Eulerian. +// +// If the graph represents an Eulerian cycle, it returns -1, -1, nil. +// +// If the graph does not represent an Eulerian cycle but does represent an +// Eulerian path, it returns the two end nodes of the path, and nil. +// +// Otherwise it returns an error. +// +// See also method EulerianStart, which short-circuits as soon as it finds +// a node that must be a start or end node of an Eulerian path. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) Eulerian() (end1, end2 NI, err error) { + end1 = -1 + end2 = -1 + for n := range g.AdjacencyList { + switch { + case g.Degree(NI(n))%2 == 0: + case end1 < 0: + end1 = NI(n) + case end2 < 0: + end2 = NI(n) + default: + err = errors.New("non-Eulerian") + return + } + } + return +} + +// EulerianCycle finds an Eulerian cycle in an undirected multigraph. +// +// * If g has no nodes, result is nil, nil. +// +// * If g is Eulerian, result is an Eulerian cycle with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the cycle. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// Internally, EulerianCycle copies the entire graph g. +// See EulerianCycleD for a more space efficient version. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) EulerianCycle() ([]NI, error) { + c, _ := g.Copy() + return c.EulerianCycleD(c.Size()) +} + +// EulerianCycleD finds an Eulerian cycle in an undirected multigraph. +// +// EulerianCycleD is destructive on its receiver g. See EulerianCycle for +// a non-destructive version. +// +// Parameter m must be the size of the undirected graph -- the +// number of edges. Use Undirected.Size if the size is unknown. +// +// * If g has no nodes, result is nil, nil. +// +// * If g is Eulerian, result is an Eulerian cycle with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the cycle. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) EulerianCycleD(m int) ([]NI, error) { + if g.Order() == 0 { + return nil, nil + } + e := newEulerian(g.AdjacencyList, m) + e.p[0] = 0 + for e.s >= 0 { + v := e.top() + if err := e.pushUndir(); err != nil { + return nil, err + } + if e.top() != v { + return nil, errors.New("not Eulerian") + } + e.keep() + } + if !e.uv.AllZeros() { + return nil, errors.New("not strongly connected") + } + return e.p, nil +} + +// EulerianPath finds an Eulerian path in an undirected multigraph. +// +// * If g has no nodes, result is nil, nil. +// +// * If g has an Eulerian path, result is an Eulerian path with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the path. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// Internally, EulerianPath copies the entire graph g. +// See EulerianPathD for a more space efficient version. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) EulerianPath() ([]NI, error) { + c, _ := g.Copy() + start := c.EulerianStart() + if start < 0 { + start = 0 + } + return c.EulerianPathD(c.Size(), start) +} + +// EulerianPathD finds an Eulerian path in a undirected multigraph. +// +// EulerianPathD is destructive on its receiver g. See EulerianPath for +// a non-destructive version. +// +// Argument m must be the correct size, or number of edges in g. +// Argument start must be a valid start node for the path. +// +// * If g has no nodes, result is nil, nil. +// +// * If g has an Eulerian path starting at start, result is an Eulerian path +// with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the path. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) EulerianPathD(m int, start NI) ([]NI, error) { + if g.Order() == 0 { + return nil, nil + } + e := newEulerian(g.AdjacencyList, m) + e.p[0] = start + // unlike EulerianCycle, the first path doesn't have to be a cycle. + if err := e.pushUndir(); err != nil { + return nil, err + } + e.keep() + for e.s >= 0 { + start = e.top() + e.push() + // paths after the first must be cycles though + // (as long as there are nodes on the stack) + if e.top() != start { + return nil, errors.New("no Eulerian path") + } + e.keep() + } + if !e.uv.AllZeros() { + return nil, errors.New("no Eulerian path") + } + return e.p, nil +} + +// EulerianStart finds a candidate start node for an Eulerian path. +// +// A graph representing an Eulerian path can have two nodes with odd degree. +// If it does, these must be the end nodes of the path. EulerianEnd scans +// for a node with an odd degree, returning immediately with the first one +// it finds. +// +// If the scan completes without finding a node with odd degree the method +// returns -1. +// +// See also method Eulerian, which completely validates a graph as representing +// an Eulerian path. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) EulerianStart() NI { + for n := range g.AdjacencyList { + if g.Degree(NI(n))%2 != 0 { + return NI(n) + } + } + return -1 +} + +// AddNode maps a node in a supergraph to a subgraph node. +// +// Argument p must be an NI in supergraph s.Super. AddNode panics if +// p is not a valid node index of s.Super. +// +// AddNode is idempotent in that it does not add a new node to the subgraph if +// a subgraph node already exists mapped to supergraph node p. +// +// The mapped subgraph NI is returned. +func (s *UndirectedSubgraph) AddNode(p NI) (b NI) { + if int(p) < 0 || int(p) >= s.Super.Order() { + panic(fmt.Sprint("AddNode: NI ", p, " not in supergraph")) + } + if b, ok := s.SubNI[p]; ok { + return b + } + a := s.Undirected.AdjacencyList + b = NI(len(a)) + s.Undirected.AdjacencyList = append(a, nil) + s.SuperNI = append(s.SuperNI, p) + s.SubNI[p] = b + return +} + +// InduceList constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument l must be a list of +// NIs in receiver graph g. Receiver g becomes the supergraph of the induced +// subgraph. +// +// Duplicate NIs are allowed in list l. The duplicates are effectively removed +// and only a single corresponding node is created in the subgraph. Subgraph +// NIs are mapped in the order of list l, execpt for ignoring duplicates. +// NIs in l that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *Undirected) InduceList(l []NI) *UndirectedSubgraph { + sub, sup := mapList(l) + return &UndirectedSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + Undirected: Undirected{ + g.AdjacencyList.induceArcs(sub, sup), + }} +} + +// InduceBits constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument t must be a bitmap +// representing NIs in receiver graph g. Receiver g becomes the supergraph +// of the induced subgraph. NIs in t that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *Undirected) InduceBits(t bits.Bits) *UndirectedSubgraph { + sub, sup := mapBits(t) + return &UndirectedSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + Undirected: Undirected{ + g.AdjacencyList.induceArcs(sub, sup), + }} +} + +// IsConnected tests if an undirected graph is a single connected component. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentReps for a method returning more information. +func (g Undirected) IsConnected() bool { + a := g.AdjacencyList + if len(a) == 0 { + return true + } + b := bits.New(len(a)) + var df func(NI) + df = func(n NI) { + b.SetBit(int(n), 1) + for _, to := range a[n] { + if b.Bit(int(to)) == 0 { + df(to) + } + } + } + df(0) + return b.AllOnes() +} + +// IsTree identifies trees in undirected graphs. +// +// Return value isTree is true if the connected component reachable from root +// is a tree. Further, return value allTree is true if the entire graph g is +// connected. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g Undirected) IsTree(root NI) (isTree, allTree bool) { + a := g.AdjacencyList + v := bits.New(len(a)) + v.SetAll() + var df func(NI, NI) bool + df = func(fr, n NI) bool { + if v.Bit(int(n)) == 0 { + return false + } + v.SetBit(int(n), 0) + for _, to := range a[n] { + if to != fr && !df(n, to) { + return false + } + } + return true + } + v.SetBit(int(root), 0) + for _, to := range a[root] { + if !df(root, to) { + return false, false + } + } + return true, v.AllZeros() +} + +// Size returns the number of edges in g. +// +// See also ArcSize and AnyLoop. +func (g Undirected) Size() int { + m2 := 0 + for fr, to := range g.AdjacencyList { + m2 += len(to) + for _, to := range to { + if to == NI(fr) { + m2++ + } + } + } + return m2 / 2 +} + +// Density returns edge density of a bipartite graph. +// +// Edge density is number of edges over maximum possible number of edges. +// Maximum possible number of edges in a bipartite graph is number of +// nodes of one color times number of nodes of the other color. +func (g Bipartite) Density() float64 { + a := g.Undirected.AdjacencyList + s := 0 + g.Color.IterateOnes(func(n int) bool { + s += len(a[n]) + return true + }) + return float64(s) / float64(g.N0*(len(a)-g.N0)) +} + +// PermuteBiadjacency permutes a bipartite graph in place so that a prefix +// of the adjacency list encodes a biadjacency matrix. +// +// The permutation applied is returned. This would be helpful in referencing +// any externally stored node information. +// +// The biadjacency matrix is encoded as the prefix AdjacencyList[:g.N0]. +// Note though that this slice does not represent a valid complete +// AdjacencyList. BoundsOk would return false, for example. +// +// In adjacency list terms, the result of the permutation is that nodes of +// the prefix only have arcs to the suffix and nodes of the suffix only have +// arcs to the prefix. +func (g Bipartite) PermuteBiadjacency() []int { + p := make([]int, g.Order()) + i := 0 + g.Color.IterateZeros(func(n int) bool { + p[n] = i + i++ + return true + }) + g.Color.IterateOnes(func(n int) bool { + p[n] = i + i++ + return true + }) + g.Permute(p) + g.Color.ClearAll() + for i := g.N0; i < g.Order(); i++ { + g.Color.SetBit(i, 1) + } + return p +} diff --git a/vendor/github.com/soniakeys/graph/undir_cg.go b/vendor/github.com/soniakeys/graph/undir_cg.go new file mode 100644 index 00000000..ed932241 --- /dev/null +++ b/vendor/github.com/soniakeys/graph/undir_cg.go @@ -0,0 +1,1138 @@ +// Copyright 2014 Sonia Keys +// License MIT: http://opensource.org/licenses/MIT + +package graph + +import ( + "errors" + "fmt" + + "github.com/soniakeys/bits" +) + +// undir_RO.go is code generated from undir_cg.go by directives in graph.go. +// Editing undir_cg.go is okay. It is the code generation source. +// DO NOT EDIT undir_RO.go. +// The RO means read only and it is upper case RO to slow you down a bit +// in case you start to edit the file. +//------------------- + +// Bipartite constructs an object indexing the bipartite structure of a graph. +// +// In a bipartite component, nodes can be partitioned into two sets, or +// "colors," such that every edge in the component goes from one set to the +// other. +// +// If the graph is bipartite, the method constructs and returns a new +// Bipartite object as b and returns ok = true. +// +// If the component is not bipartite, a representative odd cycle as oc and +// returns ok = false. +// +// In the case of a graph with mulitiple connected components, this method +// provides no control over the color orientation by component. See +// Undirected.BipartiteComponent if this control is needed. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) Bipartite() (b *LabeledBipartite, oc []NI, ok bool) { + c1 := bits.New(g.Order()) + c2 := bits.New(g.Order()) + r, _, _ := g.ConnectedComponentReps() + // accumulate n2 number of zero bits in c2 as number of one bits in n1 + var n, n2 int + for _, r := range r { + ok, n, _, oc = g.BipartiteComponent(r, c1, c2) + if !ok { + return + } + n2 += n + } + return &LabeledBipartite{g, c2, n2}, nil, true +} + +// BipartiteComponent analyzes the bipartite structure of a connected component +// of an undirected graph. +// +// In a bipartite component, nodes can be partitioned into two sets, or +// "colors," such that every edge in the component goes from one set to the +// other. +// +// Argument n can be any representative node of the component to be analyzed. +// Arguments c1 and c2 must be separate bits.Bits objects constructed to be +// of length of the number of nodes of g. These bitmaps are used in the +// component traversal and the bits of the component must be zero when the +// method is called. +// +// If the component is bipartite, BipartiteComponent populates bitmaps +// c1 and c2 with the two-coloring of the component, always assigning the set +// with representative node n to bitmap c1. It returns b = true, +// and also returns the number of bits set in c1 and c2 as n1 and n2 +// respectively. +// +// If the component is not bipartite, BipartiteComponent returns b = false +// and a representative odd cycle as oc. +// +// See also method Bipartite. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) BipartiteComponent(n NI, c1, c2 bits.Bits) (b bool, n1, n2 int, oc []NI) { + a := g.LabeledAdjacencyList + b = true + var open bool + var df func(n NI, c1, c2 *bits.Bits, n1, n2 *int) + df = func(n NI, c1, c2 *bits.Bits, n1, n2 *int) { + c1.SetBit(int(n), 1) + *n1++ + for _, nb := range a[n] { + if c1.Bit(int(nb.To)) == 1 { + b = false + oc = []NI{nb.To, n} + open = true + return + } + if c2.Bit(int(nb.To)) == 1 { + continue + } + df(nb.To, c2, c1, n2, n1) + if b { + continue + } + switch { + case !open: + case n == oc[0]: + open = false + default: + oc = append(oc, n) + } + return + } + } + df(n, &c1, &c2, &n1, &n2) + if b { + return b, n1, n2, nil + } + return b, 0, 0, oc +} + +// BronKerbosch1 finds maximal cliques in an undirected graph. +// +// The graph must not contain parallel edges or loops. +// +// See https://en.wikipedia.org/wiki/Clique_(graph_theory) and +// https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm for background. +// +// This method implements the BronKerbosch1 algorithm of WP; that is, +// the original algorithm without improvements. +// +// The method calls the emit argument for each maximal clique in g, as long +// as emit returns true. If emit returns false, BronKerbosch1 returns +// immediately. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also more sophisticated variants BronKerbosch2 and BronKerbosch3. +func (g LabeledUndirected) BronKerbosch1(emit func(bits.Bits) bool) { + a := g.LabeledAdjacencyList + var f func(R, P, X bits.Bits) bool + f = func(R, P, X bits.Bits) bool { + switch { + case !P.AllZeros(): + r2 := bits.New(len(a)) + p2 := bits.New(len(a)) + x2 := bits.New(len(a)) + pf := func(n int) bool { + r2.Set(R) + r2.SetBit(n, 1) + p2.ClearAll() + x2.ClearAll() + for _, to := range a[n] { + if P.Bit(int(to.To)) == 1 { + p2.SetBit(int(to.To), 1) + } + if X.Bit(int(to.To)) == 1 { + x2.SetBit(int(to.To), 1) + } + } + if !f(r2, p2, x2) { + return false + } + P.SetBit(n, 0) + X.SetBit(n, 1) + return true + } + if !P.IterateOnes(pf) { + return false + } + case X.AllZeros(): + return emit(R) + } + return true + } + var R, P, X bits.Bits + R = bits.New(len(a)) + P = bits.New(len(a)) + X = bits.New(len(a)) + P.SetAll() + f(R, P, X) +} + +// BKPivotMaxDegree is a strategy for BronKerbosch methods. +// +// To use it, take the method value (see golang.org/ref/spec#Method_values) +// and pass it as the argument to BronKerbosch2 or 3. +// +// The strategy is to pick the node from P or X with the maximum degree +// (number of edges) in g. Note this is a shortcut from evaluating degrees +// in P. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) BKPivotMaxDegree(P, X bits.Bits) (p NI) { + // choose pivot u as highest degree node from P or X + a := g.LabeledAdjacencyList + maxDeg := -1 + P.IterateOnes(func(n int) bool { // scan P + if d := len(a[n]); d > maxDeg { + p = NI(n) + maxDeg = d + } + return true + }) + X.IterateOnes(func(n int) bool { // scan X + if d := len(a[n]); d > maxDeg { + p = NI(n) + maxDeg = d + } + return true + }) + return +} + +// BKPivotMinP is a strategy for BronKerbosch methods. +// +// To use it, take the method value (see golang.org/ref/spec#Method_values) +// and pass it as the argument to BronKerbosch2 or 3. +// +// The strategy is to simply pick the first node in P. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) BKPivotMinP(P, X bits.Bits) NI { + return NI(P.OneFrom(0)) +} + +// BronKerbosch2 finds maximal cliques in an undirected graph. +// +// The graph must not contain parallel edges or loops. +// +// See https://en.wikipedia.org/wiki/Clique_(graph_theory) and +// https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm for background. +// +// This method implements the BronKerbosch2 algorithm of WP; that is, +// the original algorithm plus pivoting. +// +// The argument is a pivot function that must return a node of P or X. +// P is guaranteed to contain at least one node. X is not. +// For example see BKPivotMaxDegree. +// +// The method calls the emit argument for each maximal clique in g, as long +// as emit returns true. If emit returns false, BronKerbosch1 returns +// immediately. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also simpler variant BronKerbosch1 and more sophisticated variant +// BronKerbosch3. +func (g LabeledUndirected) BronKerbosch2(pivot func(P, X bits.Bits) NI, emit func(bits.Bits) bool) { + a := g.LabeledAdjacencyList + var f func(R, P, X bits.Bits) bool + f = func(R, P, X bits.Bits) bool { + switch { + case !P.AllZeros(): + r2 := bits.New(len(a)) + p2 := bits.New(len(a)) + x2 := bits.New(len(a)) + pnu := bits.New(len(a)) + // compute P \ N(u). next 5 lines are only difference from BK1 + pnu.Set(P) + for _, to := range a[pivot(P, X)] { + pnu.SetBit(int(to.To), 0) + } + // remaining code like BK1 + pf := func(n int) bool { + r2.Set(R) + r2.SetBit(n, 1) + p2.ClearAll() + x2.ClearAll() + for _, to := range a[n] { + if P.Bit(int(to.To)) == 1 { + p2.SetBit(int(to.To), 1) + } + if X.Bit(int(to.To)) == 1 { + x2.SetBit(int(to.To), 1) + } + } + if !f(r2, p2, x2) { + return false + } + P.SetBit(n, 0) + X.SetBit(n, 1) + return true + } + if !pnu.IterateOnes(pf) { + return false + } + case X.AllZeros(): + return emit(R) + } + return true + } + R := bits.New(len(a)) + P := bits.New(len(a)) + X := bits.New(len(a)) + P.SetAll() + f(R, P, X) +} + +// BronKerbosch3 finds maximal cliques in an undirected graph. +// +// The graph must not contain parallel edges or loops. +// +// See https://en.wikipedia.org/wiki/Clique_(graph_theory) and +// https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm for background. +// +// This method implements the BronKerbosch3 algorithm of WP; that is, +// the original algorithm with pivoting and degeneracy ordering. +// +// The argument is a pivot function that must return a node of P or X. +// P is guaranteed to contain at least one node. X is not. +// For example see BKPivotMaxDegree. +// +// The method calls the emit argument for each maximal clique in g, as long +// as emit returns true. If emit returns false, BronKerbosch1 returns +// immediately. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also simpler variants BronKerbosch1 and BronKerbosch2. +func (g LabeledUndirected) BronKerbosch3(pivot func(P, X bits.Bits) NI, emit func(bits.Bits) bool) { + a := g.LabeledAdjacencyList + var f func(R, P, X bits.Bits) bool + f = func(R, P, X bits.Bits) bool { + switch { + case !P.AllZeros(): + r2 := bits.New(len(a)) + p2 := bits.New(len(a)) + x2 := bits.New(len(a)) + pnu := bits.New(len(a)) + // compute P \ N(u). next lines are only difference from BK1 + pnu.Set(P) + for _, to := range a[pivot(P, X)] { + pnu.SetBit(int(to.To), 0) + } + // remaining code like BK2 + pf := func(n int) bool { + r2.Set(R) + r2.SetBit(n, 1) + p2.ClearAll() + x2.ClearAll() + for _, to := range a[n] { + if P.Bit(int(to.To)) == 1 { + p2.SetBit(int(to.To), 1) + } + if X.Bit(int(to.To)) == 1 { + x2.SetBit(int(to.To), 1) + } + } + if !f(r2, p2, x2) { + return false + } + P.SetBit(n, 0) + X.SetBit(n, 1) + return true + } + if !pnu.IterateOnes(pf) { + return false + } + case X.AllZeros(): + return emit(R) + } + return true + } + R := bits.New(len(a)) + P := bits.New(len(a)) + X := bits.New(len(a)) + P.SetAll() + // code above same as BK2 + // code below new to BK3 + ord, _ := g.DegeneracyOrdering() + p2 := bits.New(len(a)) + x2 := bits.New(len(a)) + for _, n := range ord { + R.SetBit(int(n), 1) + p2.ClearAll() + x2.ClearAll() + for _, to := range a[n] { + if P.Bit(int(to.To)) == 1 { + p2.SetBit(int(to.To), 1) + } + if X.Bit(int(to.To)) == 1 { + x2.SetBit(int(to.To), 1) + } + } + if !f(R, p2, x2) { + return + } + R.SetBit(int(n), 0) + P.SetBit(int(n), 0) + X.SetBit(int(n), 1) + } +} + +// ConnectedComponentBits returns a function that iterates over connected +// components of g, returning a member bitmap for each. +// +// Each call of the returned function returns the order, arc size, +// and bits of a connected component. The underlying bits allocation is +// the same for each call and is overwritten on subsequent calls. Use or +// save the bits before calling the function again. The function returns +// zeros after returning all connected components. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentInts, ConnectedComponentReps, and +// ConnectedComponentReps. +func (g LabeledUndirected) ConnectedComponentBits() func() (order, arcSize int, bits bits.Bits) { + a := g.LabeledAdjacencyList + vg := bits.New(len(a)) // nodes visited in graph + vc := bits.New(len(a)) // nodes visited in current component + var order, arcSize int + var df func(NI) + df = func(n NI) { + vg.SetBit(int(n), 1) + vc.SetBit(int(n), 1) + order++ + arcSize += len(a[n]) + for _, nb := range a[n] { + if vg.Bit(int(nb.To)) == 0 { + df(nb.To) + } + } + return + } + var n int + return func() (o, ma int, b bits.Bits) { + for ; n < len(a); n++ { + if vg.Bit(n) == 0 { + vc.ClearAll() + order, arcSize = 0, 0 + df(NI(n)) + return order, arcSize, vc + } + } + return // return zeros signalling no more components + } +} + +// ConnectedComponenInts returns a list of component numbers (ints) for each +// node of graph g. +// +// The method assigns numbers to components 1-based, 1 through the number of +// components. Return value ci contains the component number for each node. +// Return value nc is the number of components. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentBits, ConnectedComponentLists, and +// ConnectedComponentReps. +func (g LabeledUndirected) ConnectedComponentInts() (ci []int, nc int) { + a := g.LabeledAdjacencyList + ci = make([]int, len(a)) + var df func(NI) + df = func(nd NI) { + ci[nd] = nc + for _, to := range a[nd] { + if ci[to.To] == 0 { + df(to.To) + } + } + return + } + for nd := range a { + if ci[nd] == 0 { + nc++ + df(NI(nd)) + } + } + return +} + +// ConnectedComponentLists returns a function that iterates over connected +// components of g, returning the member list of each. +// +// Each call of the returned function returns a node list of a connected +// component and the arc size of the component. The returned function returns +// nil, 0 after returning all connected components. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentBits, ConnectedComponentInts, and +// ConnectedComponentReps. +func (g LabeledUndirected) ConnectedComponentLists() func() (nodes []NI, arcSize int) { + a := g.LabeledAdjacencyList + vg := bits.New(len(a)) // nodes visited in graph + var l []NI // accumulated node list of current component + var ma int // accumulated arc size of current component + var df func(NI) + df = func(n NI) { + vg.SetBit(int(n), 1) + l = append(l, n) + ma += len(a[n]) + for _, nb := range a[n] { + if vg.Bit(int(nb.To)) == 0 { + df(nb.To) + } + } + return + } + var n int + return func() ([]NI, int) { + for ; n < len(a); n++ { + if vg.Bit(n) == 0 { + l, ma = nil, 0 + df(NI(n)) + return l, ma + } + } + return nil, 0 + } +} + +// ConnectedComponentReps returns a representative node from each connected +// component of g. +// +// Returned is a slice with a single representative node from each connected +// component and also parallel slices with the orders and arc sizes +// in the corresponding components. +// +// This is fairly minimal information describing connected components. +// From a representative node, other nodes in the component can be reached +// by depth first traversal for example. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentBits and ConnectedComponentLists which can +// collect component members in a single traversal, and IsConnected which +// is an even simpler boolean test. +func (g LabeledUndirected) ConnectedComponentReps() (reps []NI, orders, arcSizes []int) { + a := g.LabeledAdjacencyList + c := bits.New(len(a)) + var o, ma int + var df func(NI) + df = func(n NI) { + c.SetBit(int(n), 1) + o++ + ma += len(a[n]) + for _, nb := range a[n] { + if c.Bit(int(nb.To)) == 0 { + df(nb.To) + } + } + return + } + for n := range a { + if c.Bit(n) == 0 { + o, ma = 0, 0 + df(NI(n)) + reps = append(reps, NI(n)) + orders = append(orders, o) + arcSizes = append(arcSizes, ma) + } + } + return +} + +// Copy makes a deep copy of g. +// Copy also computes the arc size ma, the number of arcs. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) Copy() (c LabeledUndirected, ma int) { + l, s := g.LabeledAdjacencyList.Copy() + return LabeledUndirected{l}, s +} + +// Degeneracy is a measure of dense subgraphs within a graph. +// +// See Wikipedia https://en.wikipedia.org/wiki/Degeneracy_(graph_theory) +// +// See also method DegeneracyOrdering which returns a degeneracy node +// ordering and k-core breaks. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) Degeneracy() (k int) { + a := g.LabeledAdjacencyList + // WP algorithm, attributed to Matula and Beck. + L := bits.New(len(a)) + d := make([]int, len(a)) + var D [][]NI + for v, nb := range a { + dv := len(nb) + d[v] = dv + for len(D) <= dv { + D = append(D, nil) + } + D[dv] = append(D[dv], NI(v)) + } + for range a { + // find a non-empty D + i := 0 + for len(D[i]) == 0 { + i++ + } + // k is max(i, k) + if i > k { + k = i + } + // select from D[i] + Di := D[i] + last := len(Di) - 1 + v := Di[last] + // Add v to ordering, remove from Di + L.SetBit(int(v), 1) + D[i] = Di[:last] + // move neighbors + for _, nb := range a[v] { + if L.Bit(int(nb.To)) == 1 { + continue + } + dn := d[nb.To] // old number of neighbors of nb + Ddn := D[dn] // nb is in this list + // remove it from the list + for wx, w := range Ddn { + if w == nb.To { + last := len(Ddn) - 1 + Ddn[wx], Ddn[last] = Ddn[last], Ddn[wx] + D[dn] = Ddn[:last] + } + } + dn-- // new number of neighbors + d[nb.To] = dn + // re--add it to it's new list + D[dn] = append(D[dn], nb.To) + } + } + return +} + +// DegeneracyOrdering computes degeneracy node ordering and k-core breaks. +// +// See Wikipedia https://en.wikipedia.org/wiki/Degeneracy_(graph_theory) +// +// In return value ordering, nodes are ordered by their "coreness" as +// defined at https://en.wikipedia.org/wiki/Degeneracy_(graph_theory)#k-Cores. +// +// Return value kbreaks indexes ordering by coreness number. len(kbreaks) +// will be one more than the graph degeneracy as returned by the Degeneracy +// method. If degeneracy is d, d = len(kbreaks) - 1, kbreaks[d] is the last +// value in kbreaks and ordering[:kbreaks[d]] contains nodes of the d-cores +// of the graph. kbreaks[0] is always the number of nodes in g as all nodes +// are in in a 0-core. +// +// Note that definitions of "k-core" differ on whether a k-core must be a +// single connected component. This method does not resolve individual +// connected components. +// +// See also method Degeneracy which returns just the degeneracy number. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) DegeneracyOrdering() (ordering []NI, kbreaks []int) { + a := g.LabeledAdjacencyList + // WP algorithm + k := 0 + ordering = make([]NI, len(a)) + kbreaks = []int{len(a)} + L := bits.New(len(a)) + d := make([]int, len(a)) + var D [][]NI + for v, nb := range a { + dv := len(nb) + d[v] = dv + for len(D) <= dv { + D = append(D, nil) + } + D[dv] = append(D[dv], NI(v)) + } + for ox := len(a) - 1; ox >= 0; ox-- { + // find a non-empty D + i := 0 + for len(D[i]) == 0 { + i++ + } + // k is max(i, k) + if i > k { + for len(kbreaks) <= i { + kbreaks = append(kbreaks, ox+1) + } + k = i + } + // select from D[i] + Di := D[i] + last := len(Di) - 1 + v := Di[last] + // Add v to ordering, remove from Di + ordering[ox] = v + L.SetBit(int(v), 1) + D[i] = Di[:last] + // move neighbors + for _, nb := range a[v] { + if L.Bit(int(nb.To)) == 1 { + continue + } + dn := d[nb.To] // old number of neighbors of nb + Ddn := D[dn] // nb is in this list + // remove it from the list + for wx, w := range Ddn { + if w == nb.To { + last := len(Ddn) - 1 + Ddn[wx], Ddn[last] = Ddn[last], Ddn[wx] + D[dn] = Ddn[:last] + } + } + dn-- // new number of neighbors + d[nb.To] = dn + // re--add it to it's new list + D[dn] = append(D[dn], nb.To) + } + } + //for i, j := 0, k; i < j; i, j = i+1, j-1 { + // kbreaks[i], kbreaks[j] = kbreaks[j], kbreaks[i] + //} + return +} + +// Degree for undirected graphs, returns the degree of a node. +// +// The degree of a node in an undirected graph is the number of incident +// edges, where loops count twice. +// +// If g is known to be loop-free, the result is simply equivalent to len(g[n]). +// See handshaking lemma example at AdjacencyList.ArcSize. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) Degree(n NI) int { + to := g.LabeledAdjacencyList[n] + d := len(to) // just "out" degree, + for _, to := range to { + if to.To == n { + d++ // except loops count twice + } + } + return d +} + +// DegreeCentralization returns the degree centralization metric of a graph. +// +// Degree of a node is one measure of node centrality and is directly +// available from the adjacency list representation. This allows degree +// centralization for the graph to be very efficiently computed. +// +// The value returned is from 0 to 1 inclusive for simple graphs of three or +// more nodes. As a special case, 0 is returned for graphs of two or fewer +// nodes. The value returned can be > 1 for graphs with loops or parallel +// edges. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) DegreeCentralization() float64 { + a := g.LabeledAdjacencyList + if len(a) <= 2 { + return 0 + } + var max, sum int + for _, to := range a { + if len(to) > max { + max = len(to) + } + sum += len(to) + } + return float64(len(a)*max-sum) / float64((len(a)-1)*(len(a)-2)) +} + +// Density returns density for a simple graph. +// +// See also Density function. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) Density() float64 { + return Density(g.Order(), g.Size()) +} + +// Eulerian scans an undirected graph to determine if it is Eulerian. +// +// If the graph represents an Eulerian cycle, it returns -1, -1, nil. +// +// If the graph does not represent an Eulerian cycle but does represent an +// Eulerian path, it returns the two end nodes of the path, and nil. +// +// Otherwise it returns an error. +// +// See also method EulerianStart, which short-circuits as soon as it finds +// a node that must be a start or end node of an Eulerian path. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) Eulerian() (end1, end2 NI, err error) { + end1 = -1 + end2 = -1 + for n := range g.LabeledAdjacencyList { + switch { + case g.Degree(NI(n))%2 == 0: + case end1 < 0: + end1 = NI(n) + case end2 < 0: + end2 = NI(n) + default: + err = errors.New("non-Eulerian") + return + } + } + return +} + +// EulerianCycle finds an Eulerian cycle in an undirected multigraph. +// +// * If g has no nodes, result is nil, nil. +// +// * If g is Eulerian, result is an Eulerian cycle with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the cycle. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// Internally, EulerianCycle copies the entire graph g. +// See EulerianCycleD for a more space efficient version. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) EulerianCycle() ([]Half, error) { + c, _ := g.Copy() + return c.EulerianCycleD(c.Size()) +} + +// EulerianCycleD finds an Eulerian cycle in an undirected multigraph. +// +// EulerianCycleD is destructive on its receiver g. See EulerianCycle for +// a non-destructive version. +// +// Parameter m must be the size of the undirected graph -- the +// number of edges. Use Undirected.Size if the size is unknown. +// +// * If g has no nodes, result is nil, nil. +// +// * If g is Eulerian, result is an Eulerian cycle with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the cycle. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) EulerianCycleD(m int) ([]Half, error) { + if g.Order() == 0 { + return nil, nil + } + e := newLabEulerian(g.LabeledAdjacencyList, m) + e.p[0] = Half{0, -1} + for e.s >= 0 { + v := e.top() + if err := e.pushUndir(); err != nil { + return nil, err + } + if e.top().To != v.To { + return nil, errors.New("not Eulerian") + } + e.keep() + } + if !e.uv.AllZeros() { + return nil, errors.New("not strongly connected") + } + return e.p, nil +} + +// EulerianPath finds an Eulerian path in an undirected multigraph. +// +// * If g has no nodes, result is nil, nil. +// +// * If g has an Eulerian path, result is an Eulerian path with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the path. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// Internally, EulerianPath copies the entire graph g. +// See EulerianPathD for a more space efficient version. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) EulerianPath() ([]Half, error) { + c, _ := g.Copy() + start := c.EulerianStart() + if start < 0 { + start = 0 + } + return c.EulerianPathD(c.Size(), start) +} + +// EulerianPathD finds an Eulerian path in a undirected multigraph. +// +// EulerianPathD is destructive on its receiver g. See EulerianPath for +// a non-destructive version. +// +// Argument m must be the correct size, or number of edges in g. +// Argument start must be a valid start node for the path. +// +// * If g has no nodes, result is nil, nil. +// +// * If g has an Eulerian path starting at start, result is an Eulerian path +// with err = nil. +// The first element of the result represents only a start node. +// The remaining elements represent the half arcs of the path. +// +// * Otherwise, result is nil, with a non-nil error giving a reason the graph +// is not Eulerian. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) EulerianPathD(m int, start NI) ([]Half, error) { + if g.Order() == 0 { + return nil, nil + } + e := newLabEulerian(g.LabeledAdjacencyList, m) + e.p[0] = Half{start, -1} + // unlike EulerianCycle, the first path doesn't have to be a cycle. + if err := e.pushUndir(); err != nil { + return nil, err + } + e.keep() + for e.s >= 0 { + start = e.top().To + e.push() + // paths after the first must be cycles though + // (as long as there are nodes on the stack) + if e.top().To != start { + return nil, errors.New("no Eulerian path") + } + e.keep() + } + if !e.uv.AllZeros() { + return nil, errors.New("no Eulerian path") + } + return e.p, nil +} + +// EulerianStart finds a candidate start node for an Eulerian path. +// +// A graph representing an Eulerian path can have two nodes with odd degree. +// If it does, these must be the end nodes of the path. EulerianEnd scans +// for a node with an odd degree, returning immediately with the first one +// it finds. +// +// If the scan completes without finding a node with odd degree the method +// returns -1. +// +// See also method Eulerian, which completely validates a graph as representing +// an Eulerian path. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) EulerianStart() NI { + for n := range g.LabeledAdjacencyList { + if g.Degree(NI(n))%2 != 0 { + return NI(n) + } + } + return -1 +} + +// AddNode maps a node in a supergraph to a subgraph node. +// +// Argument p must be an NI in supergraph s.Super. AddNode panics if +// p is not a valid node index of s.Super. +// +// AddNode is idempotent in that it does not add a new node to the subgraph if +// a subgraph node already exists mapped to supergraph node p. +// +// The mapped subgraph NI is returned. +func (s *LabeledUndirectedSubgraph) AddNode(p NI) (b NI) { + if int(p) < 0 || int(p) >= s.Super.Order() { + panic(fmt.Sprint("AddNode: NI ", p, " not in supergraph")) + } + if b, ok := s.SubNI[p]; ok { + return b + } + a := s.LabeledUndirected.LabeledAdjacencyList + b = NI(len(a)) + s.LabeledUndirected.LabeledAdjacencyList = append(a, nil) + s.SuperNI = append(s.SuperNI, p) + s.SubNI[p] = b + return +} + +// InduceList constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument l must be a list of +// NIs in receiver graph g. Receiver g becomes the supergraph of the induced +// subgraph. +// +// Duplicate NIs are allowed in list l. The duplicates are effectively removed +// and only a single corresponding node is created in the subgraph. Subgraph +// NIs are mapped in the order of list l, execpt for ignoring duplicates. +// NIs in l that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *LabeledUndirected) InduceList(l []NI) *LabeledUndirectedSubgraph { + sub, sup := mapList(l) + return &LabeledUndirectedSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + LabeledUndirected: LabeledUndirected{ + g.LabeledAdjacencyList.induceArcs(sub, sup), + }} +} + +// InduceBits constructs a node-induced subgraph. +// +// The subgraph is induced on receiver graph g. Argument t must be a bitmap +// representing NIs in receiver graph g. Receiver g becomes the supergraph +// of the induced subgraph. NIs in t that are not in g will panic. +// +// Returned is the constructed Subgraph object containing the induced subgraph +// and the mappings to the supergraph. +func (g *LabeledUndirected) InduceBits(t bits.Bits) *LabeledUndirectedSubgraph { + sub, sup := mapBits(t) + return &LabeledUndirectedSubgraph{ + Super: g, + SubNI: sub, + SuperNI: sup, + LabeledUndirected: LabeledUndirected{ + g.LabeledAdjacencyList.induceArcs(sub, sup), + }} +} + +// IsConnected tests if an undirected graph is a single connected component. +// +// There are equivalent labeled and unlabeled versions of this method. +// +// See also ConnectedComponentReps for a method returning more information. +func (g LabeledUndirected) IsConnected() bool { + a := g.LabeledAdjacencyList + if len(a) == 0 { + return true + } + b := bits.New(len(a)) + var df func(NI) + df = func(n NI) { + b.SetBit(int(n), 1) + for _, to := range a[n] { + if b.Bit(int(to.To)) == 0 { + df(to.To) + } + } + } + df(0) + return b.AllOnes() +} + +// IsTree identifies trees in undirected graphs. +// +// Return value isTree is true if the connected component reachable from root +// is a tree. Further, return value allTree is true if the entire graph g is +// connected. +// +// There are equivalent labeled and unlabeled versions of this method. +func (g LabeledUndirected) IsTree(root NI) (isTree, allTree bool) { + a := g.LabeledAdjacencyList + v := bits.New(len(a)) + v.SetAll() + var df func(NI, NI) bool + df = func(fr, n NI) bool { + if v.Bit(int(n)) == 0 { + return false + } + v.SetBit(int(n), 0) + for _, to := range a[n] { + if to.To != fr && !df(n, to.To) { + return false + } + } + return true + } + v.SetBit(int(root), 0) + for _, to := range a[root] { + if !df(root, to.To) { + return false, false + } + } + return true, v.AllZeros() +} + +// Size returns the number of edges in g. +// +// See also ArcSize and AnyLoop. +func (g LabeledUndirected) Size() int { + m2 := 0 + for fr, to := range g.LabeledAdjacencyList { + m2 += len(to) + for _, to := range to { + if to.To == NI(fr) { + m2++ + } + } + } + return m2 / 2 +} + +// Density returns edge density of a bipartite graph. +// +// Edge density is number of edges over maximum possible number of edges. +// Maximum possible number of edges in a bipartite graph is number of +// nodes of one color times number of nodes of the other color. +func (g LabeledBipartite) Density() float64 { + a := g.LabeledUndirected.LabeledAdjacencyList + s := 0 + g.Color.IterateOnes(func(n int) bool { + s += len(a[n]) + return true + }) + return float64(s) / float64(g.N0*(len(a)-g.N0)) +} + +// PermuteBiadjacency permutes a bipartite graph in place so that a prefix +// of the adjacency list encodes a biadjacency matrix. +// +// The permutation applied is returned. This would be helpful in referencing +// any externally stored node information. +// +// The biadjacency matrix is encoded as the prefix AdjacencyList[:g.N0]. +// Note though that this slice does not represent a valid complete +// AdjacencyList. BoundsOk would return false, for example. +// +// In adjacency list terms, the result of the permutation is that nodes of +// the prefix only have arcs to the suffix and nodes of the suffix only have +// arcs to the prefix. +func (g LabeledBipartite) PermuteBiadjacency() []int { + p := make([]int, g.Order()) + i := 0 + g.Color.IterateZeros(func(n int) bool { + p[n] = i + i++ + return true + }) + g.Color.IterateOnes(func(n int) bool { + p[n] = i + i++ + return true + }) + g.Permute(p) + g.Color.ClearAll() + for i := g.N0; i < g.Order(); i++ { + g.Color.SetBit(i, 1) + } + return p +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 325975b1..9f2d16d5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,5 +1,8 @@ # github.com/Microsoft/go-winio v0.4.11 github.com/Microsoft/go-winio +# github.com/actions/workflow-parser v0.0.0-20190130154146-aac54e2ba131 +github.com/actions/workflow-parser/model +github.com/actions/workflow-parser/parser # github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 github.com/containerd/continuity/pathdriver # github.com/davecgh/go-spew v1.1.1 @@ -55,11 +58,11 @@ github.com/gogo/protobuf/proto # github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl github.com/hashicorp/hcl/hcl/ast -github.com/hashicorp/hcl/hcl/token github.com/hashicorp/hcl/hcl/parser +github.com/hashicorp/hcl/hcl/token github.com/hashicorp/hcl/json/parser -github.com/hashicorp/hcl/hcl/strconv github.com/hashicorp/hcl/hcl/scanner +github.com/hashicorp/hcl/hcl/strconv github.com/hashicorp/hcl/json/scanner github.com/hashicorp/hcl/json/token # github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c @@ -92,6 +95,10 @@ github.com/pmezard/go-difflib/difflib github.com/sergi/go-diff/diffmatchpatch # github.com/sirupsen/logrus v1.3.0 github.com/sirupsen/logrus +# github.com/soniakeys/bits v1.0.0 +github.com/soniakeys/bits +# github.com/soniakeys/graph v0.0.0 +github.com/soniakeys/graph # github.com/spf13/cobra v0.0.3 github.com/spf13/cobra # github.com/spf13/pflag v1.0.3