From 66bc41ce46054faea02a0ad7b42a6ebcf57dca07 Mon Sep 17 00:00:00 2001
From: Kirill Smelkov <kirr@nexedi.com>
Date: Wed, 23 Jun 2021 17:35:51 +0300
Subject: [PATCH] X Fix bug in PPTreeSubSet.Difference  - it was always leaving
 root node alive
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

So that even A \ A was not 酶.
---
 wcfs/internal/xbtree/pptreesubset.go         | 18 ++++++---
 wcfs/internal/xbtree/pptreesubset_test.go    | 41 ++++++++++++++++----
 wcfs/internal/xbtree/treediff.go             |  2 +-
 "wcfs/internal/xbtree/\316\264btail_test.go" | 23 ++++-------
 4 files changed, 55 insertions(+), 29 deletions(-)

diff --git a/wcfs/internal/xbtree/pptreesubset.go b/wcfs/internal/xbtree/pptreesubset.go
index 97bfe079..7cdcd8ee 100644
--- a/wcfs/internal/xbtree/pptreesubset.go
+++ b/wcfs/internal/xbtree/pptreesubset.go
@@ -27,6 +27,7 @@ import (
 )
 
 const tracePPSet = false
+const debugPPSet = false
 
 // PPTreeSubSet represents PP-connected subset of tree node objects.
 //
@@ -291,10 +292,16 @@ func (A PPTreeSubSet) fixup(未nchild map[zodb.Oid]int) {
 	A.xfixup(+1, 未nchild)
 }
 func (A PPTreeSubSet) xfixup(sign int, 未nchild map[zodb.Oid]int) {
-	//fmt.Printf("\nfixup:\n")
-	//fmt.Printf("    路: %s\n", A)
-	//fmt.Printf("    未: %v\n", 未nchild)
-	//defer fmt.Printf("  ->路: %s\n\n", A)
+	if debugPPSet {
+		ssign := "+"
+		if sign < 0 {
+			ssign = "-"
+		}
+		fmt.Printf("\n  fixup:\n")
+		fmt.Printf("      路: %s\n", A)
+		fmt.Printf("     %s未: %v\n", ssign, 未nchild)
+		defer fmt.Printf("    ->路: %s\n\n", A)
+	}
 
 	gcq := []zodb.Oid{}
 	for oid, 未nc := range 未nchild {
@@ -326,7 +333,8 @@ func (S PPTreeSubSet) gc1(oid zodb.Oid) {
 	for oid != zodb.InvalidOid {
 		t := S[oid]
 		t.nchild--
-		if t.nchild > 0 || /* root node */t.parent == zodb.InvalidOid {
+//		if t.nchild > 0 || /* root node */t.parent == zodb.InvalidOid {
+		if t.nchild > 0 {
 			break
 		}
 		delete(S, oid)
diff --git a/wcfs/internal/xbtree/pptreesubset_test.go b/wcfs/internal/xbtree/pptreesubset_test.go
index aaf46dcb..58e8506c 100644
--- a/wcfs/internal/xbtree/pptreesubset_test.go
+++ b/wcfs/internal/xbtree/pptreesubset_test.go
@@ -20,6 +20,7 @@
 package xbtree
 
 import (
+	"strings"
 	"testing"
 
 	"lab.nexedi.com/kirr/neo/go/zodb"
@@ -79,18 +80,42 @@ func TestPPTreeSubSetOps(t *testing.T) {
 			S{a:{酶,1}, b:{a,0}},	// B
 				S{a:{酶,1}, b:{a,0}},	// U
 				S{}),			// D
+
+		E(
+			S{a:{酶,1}, b:{a,1}, c:{b,0}},	// A
+			S{a:{酶,1}, b:{a,1}, c:{b,0}},	// B (=A)
+				S{a:{酶,1}, b:{a,1}, c:{b,0}},	// U (=A)
+				S{}),				// D
+	}
+
+	assert1 := func(op string, A, B, res, resOK S) {
+		t.Helper()
+		if res.Equal(resOK) {
+			return
+		}
+		op1 := op[0:1]
+		t.Errorf("%s:\n  A:   %s\n  B:   %s\n  ->%s: %s\n  ok%s: %s\n",
+			strings.Title(op), A, B, op1, res, strings.ToUpper(op1), resOK)
 	}
 
 	for _, tt := range testv {
-		U := tt.A.Union(tt.B)
-		D := tt.A.Difference(tt.B)
+		Uab := tt.A.Union(tt.B)
+		Uba := tt.B.Union(tt.A)
+		Dab := tt.A.Difference(tt.B)
 
-		if !U.Equal(tt.Union) {
-			t.Errorf("Union:\n  A:   %s\n  B:   %s\n  ->u: %s\n  okU: %s\n", tt.A, tt.B, U, tt.Union)
-		}
-		if !D.Equal(tt.Difference) {
-			t.Errorf("Difference:\n  A:   %s\n  B:   %s\n  ->d: %s\n  okD: %s\n", tt.A, tt.B, D, tt.Difference)
-		}
+		assert1("union",      tt.A, tt.B, Uab, tt.Union)
+		assert1("union",      tt.B, tt.A, Uba, tt.Union)
+		assert1("difference", tt.A, tt.B, Dab, tt.Difference)
+
+		Uaa := tt.A.Union(tt.A)
+		Ubb := tt.B.Union(tt.B)
+		Daa := tt.A.Difference(tt.A)
+		Dbb := tt.B.Difference(tt.B)
+
+		assert1("union",      tt.A, tt.A, Uaa, tt.A)
+		assert1("union",      tt.B, tt.B, Ubb, tt.B)
+		assert1("difference", tt.A, tt.A, Daa, S{})
+		assert1("difference", tt.B, tt.B, Dbb, S{})
 
 		// XXX also verify U/D properties like (A+B)\B + (A+B)\A + (A^B) == (A+B) ?
 	}
diff --git a/wcfs/internal/xbtree/treediff.go b/wcfs/internal/xbtree/treediff.go
index ce1fead2..142b24b9 100644
--- a/wcfs/internal/xbtree/treediff.go
+++ b/wcfs/internal/xbtree/treediff.go
@@ -159,7 +159,7 @@ func 未ZConnectTracked(未Zv []zodb.Oid, T PPTreeSubSet) (未ZTC SetOid, 未topsByR
 // nodeInRange represents a Node coming under [lo, hi_] key range in its tree.
 type nodeInRange struct {
 	prefix   []zodb.Oid // path to this node goes via this objects
-	lo, hi_  Key  // [lo, hi_]	NOTE _not_ hi) not to overflow at 鈭�	XXX -> Range
+	lo, hi_  Key  // [lo, hi_]	NOTE _not_ hi) not to overflow at 鈭�	XXX -> keycov KeyRange?
 	node     Node
 	done     bool // whether this node was already taken into account while computing diff
 }
diff --git "a/wcfs/internal/xbtree/\316\264btail_test.go" "b/wcfs/internal/xbtree/\316\264btail_test.go"
index 8e21f929..2f47aa02 100644
--- "a/wcfs/internal/xbtree/\316\264btail_test.go"
+++ "b/wcfs/internal/xbtree/\316\264btail_test.go"
@@ -1042,11 +1042,7 @@ func xverify螖BTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
 						}
 					}
 
-//					// tracked keys1 becomes tracked keys1_2 after Update(t1->t2)
-////					keys1_2 := kadj12.Map(keys1)
-//					keys1_2 := kadj12.Map(kadj01.Map(keys1_0))
 					xverify螖BTail_rebuild_U(t, 未btail, db, treeRoot, t1, t2, xat,
-//						/*trackSet=*/t2.xkv.trackSet(keys1_2),
 						/*trackSet=*/t2.xkv.trackSet(keys1R2),
 						/*v未T=*/ 未kv1_k1R2, 未kv2_k1R2)
 
@@ -1070,23 +1066,22 @@ func xverify螖BTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
 							keys12R2 = keys12R2_
 						}
 
-//						// 未kv1_2 = t1.未xkv / kadj10(kadj21(kadj12(keys1) | keys2))
-//						// 未kv2_2 = t2.未xkv / kadj10(kadj21(kadj12(keys1) | keys2))
-////						keys12_2 := keys1_2.Union(keys2)
-////						keys12_0 := kadj10.Map(kadj21.Map(keys12_2))
-
-/*
+///*
 						fmt.Printf("\n\n\nKKK\nkeys1=%s  keys2=%s\n", keys1, keys2)
-						fmt.Printf("keys12R2=%s\n", keys12R2)
+						fmt.Printf("keys1R2:  %s\n", keys1R2)
+						fmt.Printf("keys12R2: %s\n", keys12R2)
 
 						fmt.Printf("t0.xkv: %v\n", t0.xkv)
 						fmt.Printf("t1.xkv: %v\n", t1.xkv)
 						fmt.Printf("t2.xkv: %v\n", t2.xkv)
 						fmt.Printf("kadj21: %v\n", kadj21)
 						fmt.Printf("kadj12: %v\n", kadj12)
-						fmt.Printf("t2.xkv.trackSet(%s) -> %s", keys12R2, t2.xkv.trackSet(keys12R2))
+						fmt.Printf("t2.xkv.trackSet(keys2)   -> %s\n", t2.xkv.trackSet(keys2))
+						fmt.Printf("t2.xkv.trackSet(keys1R2) -> %s\n", t2.xkv.trackSet(keys1R2))
+						fmt.Printf("t2.xkv.trackSet(keys2) \\ t2.xkv.trackSet(keys1R2)  -> %s\n",
+							t2.xkv.trackSet(keys2).Difference(t2.xkv.trackSet(keys1R2)))
 						fmt.Printf("\n\n\n")
-*/
+//*/
 
 
 						// 未kvX_k12R2 = tX.未xkv / keys12R2
@@ -1109,12 +1104,10 @@ func xverify螖BTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
 							xverify螖BTail_rebuild_TR(t, db, 未btail_, t2, treeRoot, xat,
 								// after Track(keys2)
 								keys2,
-//								/*trackSet*/ t2.xkv.trackSet(keys1_2),
 								/*trackSet*/ t2.xkv.trackSet(keys1R2),
 								/*trackNew*/ t2.xkv.trackSet(keys2).Difference(
 									// trackNew should not cover ranges that are
 									// already in trackSet
-//									t2.xkv.trackSet(keys1_2)),
 									t2.xkv.trackSet(keys1R2)),
 
 								// after rebuild
-- 
2.30.9