498 lines
14 KiB
Go
498 lines
14 KiB
Go
// 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
|
|
}
|