Files
mesh-delivery/modules/mesh-delivery-assigner/internal/assigngraph/cycle.go
T

74 lines
2.2 KiB
Go

package assigngraph
import (
"fmt"
"maps"
"slices"
"strings"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshwants"
)
// Cycles are the cycles among the waits within one step (design 47 G §272, ADR 0656): an entry waits for its acting parts in the step an
// edge names, so two acting parts acting on each other in one step wait for each other for ever. Each is `<step>: a -> b -> a`, by
// node and module, sorted. Edges of two steps never close a cycle together: the indexer taking in the checker and the checker checking
// the indexer are none, and a holder's own inputs are no edge here (the graph gives an acting edge only from a part to the entries it
// acts on, never to what the part is built from).
func Cycles(entries []meshwants.AssignmentEntry) []string {
type key struct{ step, at string }
next := map[key][]string{}
steps := map[string]bool{}
for _, e := range entries {
for _, edge := range e.Edges {
if edge.Kind != EdgeActing {
continue
}
k := key{edge.Step, e.Node + "/" + e.Module}
next[k] = append(next[k], edge.Node+"/"+edge.Module)
steps[edge.Step] = true
}
}
var out []string
for _, step := range slices.Sorted(maps.Keys(steps)) {
const (
open = iota + 1
done
)
state := map[string]int{}
var path []string
var walk func(at string)
walk = func(at string) {
state[at] = open
path = append(path, at)
for _, to := range next[key{step, at}] {
switch state[to] {
case 0:
walk(to)
case open:
from := slices.Index(path, to)
cycle := slices.Clone(path[from:])
// start at the smallest member so one cycle is said once, whichever member is met first
lowest := slices.Index(cycle, slices.Min(cycle))
cycle = append(cycle[lowest:], cycle[:lowest]...)
out = append(out, fmt.Sprintf("%s: %s -> %s", step, strings.Join(cycle, " -> "), cycle[0]))
}
}
path = path[:len(path)-1]
state[at] = done
}
starts := map[string]bool{}
for k := range next {
if k.step == step {
starts[k.at] = true
}
}
for _, at := range slices.Sorted(maps.Keys(starts)) {
if state[at] == 0 {
walk(at)
}
}
}
slices.Sort(out)
return slices.Compact(out)
}