return r
}
-// collapsepath marks i1 and i2 as equal and collapses as equal all
-// nodes across all paths between i1 and i2. If a strict edge is
+// collapsepath marks n1 and n2 as equal and collapses as equal all
+// nodes across all paths between n1 and n2. If a strict edge is
// found, the function does not modify the DAG and returns false.
+// Complexity is O(n).
func (po *poset) collapsepath(n1, n2 *Value) bool {
i1, i2 := po.values[n1.ID], po.values[n2.ID]
if po.reaches(i1, i2, true) {
return false
}
- // TODO: for now, only handle the simple case of i2 being child of i1
- l, r := po.children(i1)
- if l.Target() == i2 || r.Target() == i2 {
- i2s := newBitset(int(po.lastidx) + 1)
- i2s.Set(i2)
- po.aliasnodes(n1, i2s)
- po.addchild(i1, i2, false)
- return true
- }
+ // Find all the paths from i1 to i2
+ paths := po.findpaths(i1, i2)
+ // Mark all nodes in all the paths as aliases of n1
+ // (excluding n1 itself)
+ paths.Clear(i1)
+ po.aliasnodes(n1, paths)
return true
}
+// findpaths is a recursive function that calculates all paths from cur to dst
+// and return them as a bitset (the index of a node is set in the bitset if
+// that node is on at least one path from cur to dst).
+// We do a DFS from cur (stopping going deep any time we reach dst, if ever),
+// and mark as part of the paths any node that has a children which is already
+// part of the path (or is dst itself).
+func (po *poset) findpaths(cur, dst uint32) bitset {
+ seen := newBitset(int(po.lastidx + 1))
+ path := newBitset(int(po.lastidx + 1))
+ path.Set(dst)
+ po.findpaths1(cur, dst, seen, path)
+ return path
+}
+
+func (po *poset) findpaths1(cur, dst uint32, seen bitset, path bitset) {
+ if cur == dst {
+ return
+ }
+ seen.Set(cur)
+ l, r := po.chl(cur), po.chr(cur)
+ if !seen.Test(l) {
+ po.findpaths1(l, dst, seen, path)
+ }
+ if !seen.Test(r) {
+ po.findpaths1(r, dst, seen, path)
+ }
+ if path.Test(l) || path.Test(r) {
+ path.Set(cur)
+ }
+}
+
// Check whether it is recorded that i1!=i2
func (po *poset) isnoneq(i1, i2 uint32) bool {
if i1 == i2 {
})
}
-func TestSetEqual(t *testing.T) {
+func TestPosetCollapse(t *testing.T) {
+ testPosetOps(t, false, []posetTestOp{
+ {Checkpoint, 0, 0},
+ // Create a complex graph of <= relations among nodes between 10 and 25.
+ {SetOrderOrEqual, 10, 15},
+ {SetOrderOrEqual, 15, 20},
+ {SetOrderOrEqual, 20, vconst(20)},
+ {SetOrderOrEqual, vconst(20), 25},
+ {SetOrderOrEqual, 10, 12},
+ {SetOrderOrEqual, 12, 16},
+ {SetOrderOrEqual, 16, vconst(20)},
+ {SetOrderOrEqual, 10, 17},
+ {SetOrderOrEqual, 17, 25},
+ {SetOrderOrEqual, 15, 18},
+ {SetOrderOrEqual, 18, vconst(20)},
+ {SetOrderOrEqual, 15, 19},
+ {SetOrderOrEqual, 19, 25},
+
+ // These are other paths not part of the main collapsing path
+ {SetOrderOrEqual, 10, 11},
+ {SetOrderOrEqual, 11, 26},
+ {SetOrderOrEqual, 13, 25},
+ {SetOrderOrEqual, 100, 25},
+ {SetOrderOrEqual, 101, 15},
+ {SetOrderOrEqual, 102, 10},
+ {SetOrderOrEqual, 25, 103},
+ {SetOrderOrEqual, 20, 104},
+
+ {Checkpoint, 0, 0},
+ // Collapse everything by setting 10 >= 25: this should make everything equal
+ {SetOrderOrEqual, 25, 10},
+
+ // Check that all nodes are pairwise equal now
+ {Equal, 10, 12},
+ {Equal, 10, 15},
+ {Equal, 10, 16},
+ {Equal, 10, 17},
+ {Equal, 10, 18},
+ {Equal, 10, 19},
+ {Equal, 10, vconst(20)},
+ {Equal, 10, vconst2(20)},
+ {Equal, 10, 25},
+
+ {Equal, 12, 15},
+ {Equal, 12, 16},
+ {Equal, 12, 17},
+ {Equal, 12, 18},
+ {Equal, 12, 19},
+ {Equal, 12, vconst(20)},
+ {Equal, 12, vconst2(20)},
+ {Equal, 12, 25},
+
+ {Equal, 15, 16},
+ {Equal, 15, 17},
+ {Equal, 15, 18},
+ {Equal, 15, 19},
+ {Equal, 15, vconst(20)},
+ {Equal, 15, vconst2(20)},
+ {Equal, 15, 25},
+
+ {Equal, 16, 17},
+ {Equal, 16, 18},
+ {Equal, 16, 19},
+ {Equal, 16, vconst(20)},
+ {Equal, 16, vconst2(20)},
+ {Equal, 16, 25},
+
+ {Equal, 17, 18},
+ {Equal, 17, 19},
+ {Equal, 17, vconst(20)},
+ {Equal, 17, vconst2(20)},
+ {Equal, 17, 25},
+
+ {Equal, 18, 19},
+ {Equal, 18, vconst(20)},
+ {Equal, 18, vconst2(20)},
+ {Equal, 18, 25},
+
+ {Equal, 19, vconst(20)},
+ {Equal, 19, vconst2(20)},
+ {Equal, 19, 25},
+
+ {Equal, vconst(20), vconst2(20)},
+ {Equal, vconst(20), 25},
+
+ {Equal, vconst2(20), 25},
+
+ // ... but not 11/26/100/101/102, which were on a different path
+ {Equal_Fail, 10, 11},
+ {Equal_Fail, 10, 26},
+ {Equal_Fail, 10, 100},
+ {Equal_Fail, 10, 101},
+ {Equal_Fail, 10, 102},
+ {OrderedOrEqual, 10, 26},
+ {OrderedOrEqual, 25, 26},
+ {OrderedOrEqual, 13, 25},
+ {OrderedOrEqual, 13, 10},
+
+ {Undo, 0, 0},
+ {OrderedOrEqual, 10, 25},
+ {Equal_Fail, 10, 12},
+ {Equal_Fail, 10, 15},
+ {Equal_Fail, 10, 25},
+
+ {Undo, 0, 0},
+ })
+
+ testPosetOps(t, false, []posetTestOp{
+ {Checkpoint, 0, 0},
+ {SetOrderOrEqual, 10, 15},
+ {SetOrderOrEqual, 15, 20},
+ {SetOrderOrEqual, 20, 25},
+ {SetOrder, 10, 16},
+ {SetOrderOrEqual, 16, 20},
+ // Check that we cannot collapse here because of the strict relation 10<16
+ {SetOrderOrEqual_Fail, 20, 10},
+ {Undo, 0, 0},
+ })
+}
+
+func TestPosetSetEqual(t *testing.T) {
testPosetOps(t, false, []posetTestOp{
// 10<=20<=30<40, 20<=100<110
{Checkpoint, 0, 0},