Skip to content

Commit

Permalink
Port the implementation of SetsClosure from Java.
Browse files Browse the repository at this point in the history
  • Loading branch information
inspirer committed Sep 10, 2018
1 parent 3414cd8 commit 78fc54e
Show file tree
Hide file tree
Showing 3 changed files with 430 additions and 4 deletions.
163 changes: 159 additions & 4 deletions tm-go/grammar/closure.go
Expand Up @@ -54,8 +54,9 @@ func (c *Closure) Complement(set *FutureSet, origin status.SourceNode) *FutureSe
// Returns an error (ClosureError) if there exists a complement set transitively depending on
// itself.
func (c *Closure) Compute() error {
// TODO implement
return nil
t := tarjan{buf: c.buf, interner: c.interner, nodes: c.nodes}
t.run()
return t.err.err()
}

type op uint8
Expand All @@ -82,7 +83,7 @@ func (s *FutureSet) Include(sets ...*FutureSet) {
log.Fatal("cannot expand a non-union FutureSet")
}
if s.edges == nil {
s.edges = make([]int, len(sets))
s.edges = make([]int, 0, len(sets))
}
for _, set := range sets {
s.edges = append(s.edges, set.index)
Expand All @@ -92,7 +93,161 @@ func (s *FutureSet) Include(sets ...*FutureSet) {
// ClosureError enumerates all offending complement sets.
type ClosureError []*FutureSet

func (c ClosureError) err() error {
if len(c) == 0 {
return nil
}
return c
}

// Error implements error
func (c *ClosureError) Error() string {
func (c ClosureError) Error() string {
return "Set complement cannot be part of a dependency cycle."
}

type tarjan struct {
buf []int // for reuse
interner *container.IntSliceMap
nodes []*FutureSet

stack []int
index []int
lowLink []int
onStack container.BitSet
curr int
err ClosureError
}

func (t *tarjan) run() {
size := len(t.nodes)
if size < 2 {
return
}

t.stack = nil
t.index = make([]int, size)
for i := range t.index {
t.index[i] = -1
}
t.lowLink = make([]int, size)
t.onStack = container.NewBitSet(size)

t.curr = 0
for i := 0; i < size; i++ {
if t.index[i] == -1 {
t.strongConnect(i)
}
}
}

func (t *tarjan) strongConnect(v int) {
base := len(t.stack)
t.index[v] = t.curr
t.lowLink[v] = t.curr
t.curr++
t.stack = append(t.stack, v)
t.onStack.Set(v)

for _, w := range t.nodes[v].edges {
if t.index[w] == -1 {
t.strongConnect(w)
if t.lowLink[w] < t.lowLink[v] {
t.lowLink[v] = t.lowLink[w]
}
} else if t.onStack.Get(w) && t.index[w] < t.lowLink[v] {
t.lowLink[v] = t.index[w]
}
}

if t.lowLink[v] == t.index[v] {
t.closure(t.stack[base:])
for _, v := range t.stack[base:] {
t.onStack.Clear(v)
}
t.stack = t.stack[:base]
}
}

func (t *tarjan) closure(component []int) {
for _, q := range component {
if t.nodes[q].op == intersection {
t.slowClosure(component)
return
}
}

// Simple union (no intersections).
var res container.IntSet
for _, v := range component {
fs := t.nodes[v]
res = t.intern(container.Merge(res, fs.IntSet, t.buf))
for _, w := range t.nodes[v].edges {
set := t.nodes[w].IntSet
if fs.op == complement {
set = set.Complement()
if t.onStack.Get(w) {
// Complements cannot be part of a dependency cycle.
t.err = append(t.err, fs)
continue
}
}
if !t.onStack.Get(w) {
res = t.intern(container.Merge(res, set, t.buf))
}
}
}

if len(t.err) != 0 {
return
}

for _, v := range component {
t.nodes[v].IntSet = res
}
}

func (t *tarjan) slowClosure(component []int) {
for {
var dirty bool
for _, v := range component {
fs := t.nodes[v]
var res container.IntSet
switch fs.op {
case intersection:
res.Inverse = true
for _, w := range t.nodes[v].edges {
res = container.Intersect(res, t.nodes[w].IntSet, t.buf)
}
case union:
res = fs.IntSet
for _, w := range t.nodes[v].edges {
res = t.intern(container.Merge(res, t.nodes[w].IntSet, t.buf))
}
case complement:
if len(t.nodes[v].edges) != 1 {
panic("broken invariant")
}
w := t.nodes[v].edges[0]
if t.onStack.Get(w) {
// Complements cannot be part of a dependency cycle.
t.err = append(t.err, fs)
continue
}
res = t.nodes[w].IntSet.Complement()
}
res = t.intern(res)
if !res.Equals(fs.IntSet) {
fs.IntSet = res
dirty = true
}
}
if !dirty {
break
}
}
}

func (t *tarjan) intern(set container.IntSet) container.IntSet {
s := t.interner.Get(set.Set).([]int)
return container.IntSet{Set: s, Inverse: set.Inverse}
}

0 comments on commit 78fc54e

Please sign in to comment.