浏览代码

Add support for merging files

Vadim Markovtsev 6 年之前
父节点
当前提交
7155406383
共有 4 个文件被更改,包括 418 次插入121 次删除
  1. 4 3
      OCTOPUS.md
  2. 84 7
      internal/burndown/file.go
  3. 184 0
      internal/burndown/file_test.go
  4. 146 111
      internal/rbtree/rbtree.go

+ 4 - 3
OCTOPUS.md

@@ -7,16 +7,17 @@ thinking how to include them into the analysis.
 
 
 ### Plan
 ### Plan
 
 
-* Commits must sorted by time.
+* Commits must be sorted by time.
 * When a fork is hit, clone the pipeline. Assign the old instance to the main branch and new
 * When a fork is hit, clone the pipeline. Assign the old instance to the main branch and new
 instances to the sprouts. BurndownAnalysis should share the same counters for efficiency
 instances to the sprouts. BurndownAnalysis should share the same counters for efficiency
 and simplicity, but the files must be copied.
 and simplicity, but the files must be copied.
 * Follow each branch independently. Clone side pipelines as needed.
 * Follow each branch independently. Clone side pipelines as needed.
 * Join pipelines on merge commits. Side pipelines are killed, the main instance survives.
 * Join pipelines on merge commits. Side pipelines are killed, the main instance survives.
 This will be tricky for Burndown because we need to join the files together while preserving
 This will be tricky for Burndown because we need to join the files together while preserving
-the line annotations.
+the line annotations. The plan is to calculate the separate line annotations for each branch and blend them,
+the oldest timestamp winning.
 * Merge commits should have diffs which correspond to CGit diffs. So far they represent the diff
 * Merge commits should have diffs which correspond to CGit diffs. So far they represent the diff
-with the previous commits in the main branch.
+with the previous commit in the main branch.
 * The sequence of commits must be the analysis scenario: it must inform when to fork and to merge,
 * The sequence of commits must be the analysis scenario: it must inform when to fork and to merge,
 which pipeline instance to apply.
 which pipeline instance to apply.
 
 

+ 84 - 7
internal/burndown/file.go

@@ -36,9 +36,18 @@ func NewStatus(data interface{}, update func(interface{}, int, int, int)) Status
 }
 }
 
 
 // TreeEnd denotes the value of the last leaf in the tree.
 // TreeEnd denotes the value of the last leaf in the tree.
-const TreeEnd int = -1
+const TreeEnd = -1
+// TreeMaxBinPower is the binary power value which corresponds to the maximum day which
+// can be stored in the tree.
+const TreeMaxBinPower = 14
+// TreeMergeMark is the special day which disables the status updates and is used in File.Merge().
+const TreeMergeMark = (1 << TreeMaxBinPower) - 1
 
 
 func (file *File) updateTime(currentTime int, previousTime int, delta int) {
 func (file *File) updateTime(currentTime int, previousTime int, delta int) {
+	if currentTime & TreeMergeMark == TreeMergeMark {
+		// merge mode
+		return
+	}
 	for _, status := range file.statuses {
 	for _, status := range file.statuses {
 		status.update(status.data, currentTime, previousTime, delta)
 		status.update(status.data, currentTime, previousTime, delta)
 	}
 	}
@@ -53,9 +62,7 @@ func (file *File) updateTime(currentTime int, previousTime int, delta int) {
 //
 //
 // statuses are the attached interval length mappings.
 // statuses are the attached interval length mappings.
 func NewFile(time int, length int, statuses ...Status) *File {
 func NewFile(time int, length int, statuses ...Status) *File {
-	file := new(File)
-	file.statuses = statuses
-	file.tree = new(rbtree.RBTree)
+	file := &File{tree: new(rbtree.RBTree), statuses: statuses}
 	if length > 0 {
 	if length > 0 {
 		file.updateTime(time, time, length)
 		file.updateTime(time, time, length)
 		file.tree.Insert(rbtree.Item{Key: 0, Value: time})
 		file.tree.Insert(rbtree.Item{Key: 0, Value: time})
@@ -73,9 +80,7 @@ func NewFile(time int, length int, statuses ...Status) *File {
 //
 //
 // statuses are the attached interval length mappings.
 // statuses are the attached interval length mappings.
 func NewFileFromTree(keys []int, vals []int, statuses ...Status) *File {
 func NewFileFromTree(keys []int, vals []int, statuses ...Status) *File {
-	file := new(File)
-	file.statuses = statuses
-	file.tree = new(rbtree.RBTree)
+	file := &File{tree: new(rbtree.RBTree), statuses: statuses}
 	if len(keys) != len(vals) {
 	if len(keys) != len(vals) {
 		panic("keys and vals must be of equal length")
 		panic("keys and vals must be of equal length")
 	}
 	}
@@ -86,6 +91,20 @@ func NewFileFromTree(keys []int, vals []int, statuses ...Status) *File {
 	return file
 	return file
 }
 }
 
 
+// Clone copies the file. It performs a deep copy of the tree;
+// depending on `clearStatuses` the original statuses are removed or not.
+// Any new `statuses` are appended.
+func (file *File) Clone(clearStatuses bool, statuses ...Status) *File {
+	clone := &File{tree: file.tree.Clone(), statuses: file.statuses}
+	if clearStatuses {
+		clone.statuses = []Status{}
+	}
+	for _, status := range statuses {
+		clone.statuses = append(clone.statuses, status)
+	}
+	return clone
+}
+
 // Len returns the File's size - that is, the maximum key in the tree of line
 // Len returns the File's size - that is, the maximum key in the tree of line
 // intervals.
 // intervals.
 func (file *File) Len() int {
 func (file *File) Len() int {
@@ -218,6 +237,51 @@ func (file *File) Update(time int, pos int, insLength int, delLength int) {
 	}
 	}
 }
 }
 
 
+func (file *File) Merge(day int, others... *File) {
+	myself := file.flatten()
+	for _, other := range others {
+		lines := other.flatten()
+		if len(myself) != len(lines) {
+			panic("file corruption, lines number mismatch during merge")
+		}
+		for i, l := range myself {
+			ol := lines[i]
+			if ol & TreeMergeMark == TreeMergeMark {
+				continue
+			}
+			if l & TreeMergeMark == TreeMergeMark {
+				myself[i] = ol
+			} else if l != ol {
+				// the same line introduced in different branches
+				// consider the oldest version as the ground truth
+				if l > ol {
+					myself[i] = ol
+					// subtract from the newer day l
+					file.updateTime(ol, l, -1)
+				} else {
+					// subtract from the newer day ol
+					file.updateTime(l, ol, -1)
+				}
+			}
+		}
+	}
+	for i, l := range myself {
+		if l & TreeMergeMark == TreeMergeMark {
+			myself[i] = day
+			file.updateTime(day, day, 1)
+		}
+	}
+	// now we need to reconstruct the tree from the discrete values
+	tree := &rbtree.RBTree{}
+	for i, v := range myself {
+		if i == 0 || v != myself[i - 1] {
+			tree.Insert(rbtree.Item{Key: i, Value: v})
+		}
+	}
+	tree.Insert(rbtree.Item{Key: len(myself), Value: TreeEnd})
+	file.tree = tree
+}
+
 // Status returns the bound status object by the specified index.
 // Status returns the bound status object by the specified index.
 func (file *File) Status(index int) interface{} {
 func (file *File) Status(index int) interface{} {
 	if index < 0 || index >= len(file.statuses) {
 	if index < 0 || index >= len(file.statuses) {
@@ -263,3 +327,16 @@ func (file *File) Validate() {
 		prevKey = node.Key
 		prevKey = node.Key
 	}
 	}
 }
 }
+
+// flatten represents the file as a slice of lines, each line's value being the corresponding day.
+func (file *File) flatten() []int {
+	lines := make([]int, 0, file.Len())
+	val := -1
+	for iter := file.tree.Min(); !iter.Limit(); iter = iter.Next() {
+		for i := len(lines); i < iter.Item().Key; i++ {
+			lines = append(lines, val)
+		}
+		val = iter.Item().Value
+	}
+	return lines
+}

+ 184 - 0
internal/burndown/file_test.go

@@ -5,6 +5,7 @@ import (
 
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 	"gopkg.in/src-d/hercules.v4/internal/rbtree"
 	"gopkg.in/src-d/hercules.v4/internal/rbtree"
+	"fmt"
 )
 )
 
 
 func updateStatusFile(
 func updateStatusFile(
@@ -54,6 +55,97 @@ func TestBullshitFile(t *testing.T) {
 	assert.Equal(t, int64(0), status[1])
 	assert.Equal(t, int64(0), status[1])
 }
 }
 
 
+func TestCloneFile(t *testing.T) {
+	file, status := fixtureFile()
+	// 0 0 | 100 -1                             [0]: 100
+	file.Update(1, 20, 30, 0)
+	// 0 0 | 20 1 | 50 0 | 130 -1               [0]: 100, [1]: 30
+	file.Update(2, 20, 0, 5)
+	// 0 0 | 20 1 | 45 0 | 125 -1               [0]: 100, [1]: 25
+	file.Update(3, 20, 0, 5)
+	// 0 0 | 20 1 | 40 0 | 120 -1               [0]: 100, [1]: 20
+	file.Update(4, 20, 10, 0)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 130 -1        [0]: 100, [1]: 20, [4]: 10
+	clone := file.Clone(false)
+	clone.Update(5, 45, 0, 10)
+	// 0 0 | 20 4 | 30 1 | 45 0 | 120 -1        [0]: 95, [1]: 15, [4]: 10
+	clone.Update(6, 45, 5, 0)
+	// 0 0 | 20 4 | 30 1 | 45 6 | 50 0 | 125 -1 [0]: 95, [1]: 15, [4]: 10, [6]: 5
+	assert.Equal(t, int64(95), status[0])
+	assert.Equal(t, int64(15), status[1])
+	assert.Equal(t, int64(0), status[2])
+	assert.Equal(t, int64(0), status[3])
+	assert.Equal(t, int64(10), status[4])
+	assert.Equal(t, int64(0), status[5])
+	assert.Equal(t, int64(5), status[6])
+	dump := file.Dump()
+	// Output:
+	// 0 0
+	// 20 4
+	// 30 1
+	// 50 0
+	// 130 -1
+	assert.Equal(t, "0 0\n20 4\n30 1\n50 0\n130 -1\n", dump)
+	dump = clone.Dump()
+	// Output:
+	// 0 0
+	// 20 4
+	// 30 1
+	// 45 6
+	// 50 0
+	// 125 -1
+	assert.Equal(t, "0 0\n20 4\n30 1\n45 6\n50 0\n125 -1\n", dump)
+
+}
+
+func TestCloneFileClearStatus(t *testing.T) {
+	file, status := fixtureFile()
+	// 0 0 | 100 -1                             [0]: 100
+	file.Update(1, 20, 30, 0)
+	// 0 0 | 20 1 | 50 0 | 130 -1               [0]: 100, [1]: 30
+	file.Update(2, 20, 0, 5)
+	// 0 0 | 20 1 | 45 0 | 125 -1               [0]: 100, [1]: 25
+	file.Update(3, 20, 0, 5)
+	// 0 0 | 20 1 | 40 0 | 120 -1               [0]: 100, [1]: 20
+	file.Update(4, 20, 10, 0)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 130 -1        [0]: 100, [1]: 20, [4]: 10
+	newStatus := map[int]int64{}
+	clone := file.Clone(true, NewStatus(newStatus, updateStatusFile))
+	clone.Update(5, 45, 0, 10)
+	// 0 0 | 20 4 | 30 1 | 45 0 | 120 -1        [0]: -5, [1]: -5
+	clone.Update(6, 45, 5, 0)
+	// 0 0 | 20 4 | 30 1 | 45 6 | 50 0 | 125 -1 [0]: -5, [1]: -5, [6]: 5
+	assert.Equal(t, int64(100), status[0])
+	assert.Equal(t, int64(20), status[1])
+	assert.Equal(t, int64(0), status[2])
+	assert.Equal(t, int64(0), status[3])
+	assert.Equal(t, int64(10), status[4])
+	assert.Equal(t, int64(-5), newStatus[0])
+	assert.Equal(t, int64(-5), newStatus[1])
+	assert.Equal(t, int64(0), newStatus[2])
+	assert.Equal(t, int64(0), newStatus[3])
+	assert.Equal(t, int64(0), newStatus[4])
+	assert.Equal(t, int64(0), newStatus[5])
+	assert.Equal(t, int64(5), newStatus[6])
+	dump := file.Dump()
+	// Output:
+	// 0 0
+	// 20 4
+	// 30 1
+	// 50 0
+	// 130 -1
+	assert.Equal(t, "0 0\n20 4\n30 1\n50 0\n130 -1\n", dump)
+	dump = clone.Dump()
+	// Output:
+	// 0 0
+	// 20 4
+	// 30 1
+	// 45 6
+	// 50 0
+	// 125 -1
+	assert.Equal(t, "0 0\n20 4\n30 1\n45 6\n50 0\n125 -1\n", dump)
+}
+
 func TestLenFile(t *testing.T) {
 func TestLenFile(t *testing.T) {
 	file, _ := fixtureFile()
 	file, _ := fixtureFile()
 	assert.Equal(t, 100, file.Len())
 	assert.Equal(t, 100, file.Len())
@@ -418,3 +510,95 @@ func TestFileValidate(t *testing.T) {
 	file.tree.FindGE(2).Item().Key = 1
 	file.tree.FindGE(2).Item().Key = 1
 	assert.Panics(t, func() { file.Validate() })
 	assert.Panics(t, func() { file.Validate() })
 }
 }
+
+func TestFileFlatten(t *testing.T) {
+	file, _ := fixtureFile()
+	// 0 0 | 100 -1                             [0]: 100
+	file.Update(1, 20, 30, 0)
+	// 0 0 | 20 1 | 50 0 | 130 -1               [0]: 100, [1]: 30
+	file.Update(2, 20, 0, 5)
+	// 0 0 | 20 1 | 45 0 | 125 -1               [0]: 100, [1]: 25
+	file.Update(3, 20, 0, 5)
+	// 0 0 | 20 1 | 40 0 | 120 -1               [0]: 100, [1]: 20
+	file.Update(4, 20, 10, 0)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 130 -1        [0]: 100, [1]: 20, [4]: 10
+	lines := file.flatten()
+	for i := 0; i < 20; i++ {
+		assert.Equal(t, 0, lines[i], fmt.Sprintf("line %d", i))
+	}
+	for i := 20; i < 30; i++ {
+		assert.Equal(t, 4, lines[i], fmt.Sprintf("line %d", i))
+	}
+	for i := 30; i < 50; i++ {
+		assert.Equal(t, 1, lines[i], fmt.Sprintf("line %d", i))
+	}
+	for i := 50; i < 130; i++ {
+		assert.Equal(t, 0, lines[i], fmt.Sprintf("line %d", i))
+	}
+	assert.Len(t, lines, 130)
+}
+
+func TestFileMergeMark(t *testing.T) {
+	file, status := fixtureFile()
+	// 0 0 | 100 -1                             [0]: 100
+	file.Update(1, 20, 30, 0)
+	// 0 0 | 20 1 | 50 0 | 130 -1               [0]: 100, [1]: 30
+	file.Update(2, 20, 0, 5)
+	// 0 0 | 20 1 | 45 0 | 125 -1               [0]: 100, [1]: 25
+	file.Update(3, 20, 0, 5)
+	// 0 0 | 20 1 | 40 0 | 120 -1               [0]: 100, [1]: 20
+	file.Update(4, 20, 10, 0)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 130 -1        [0]: 100, [1]: 20, [4]: 10
+	file.Update(TreeMergeMark, 60, 20, 20)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 60 M | 80 0 | 130 -1
+	// [0]: 100, [1]: 20, [4]: 10
+	dump := file.Dump()
+	assert.Equal(t, "0 0\n20 4\n30 1\n50 0\n60 16383\n80 0\n130 -1\n", dump)
+	assert.Contains(t, status, 0)
+	assert.Equal(t, int64(100), status[0])
+	assert.Equal(t, int64(20), status[1])
+	assert.Equal(t, int64(0), status[2])
+	assert.Equal(t, int64(0), status[3])
+	assert.Equal(t, int64(10), status[4])
+	assert.NotContains(t, status, TreeMergeMark)
+}
+
+
+func TestFileMerge(t *testing.T) {
+	file1, status := fixtureFile()
+	// 0 0 | 100 -1                             [0]: 100
+	file1.Update(1, 20, 30, 0)
+	// 0 0 | 20 1 | 50 0 | 130 -1               [0]: 100, [1]: 30
+	file1.Update(2, 20, 0, 5)
+	// 0 0 | 20 1 | 45 0 | 125 -1               [0]: 100, [1]: 25
+	file1.Update(3, 20, 0, 5)
+	// 0 0 | 20 1 | 40 0 | 120 -1               [0]: 100, [1]: 20
+	file1.Update(4, 20, 10, 0)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 130 -1        [0]: 100, [1]: 20, [4]: 10
+	file2 := file1.Clone(false)
+	file1.Update(TreeMergeMark, 60, 30, 30)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 60 M | 90 0 | 130 -1
+	// [0]: 70, [1]: 20, [4]: 10
+	file2.Update(5, 60, 20, 20)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 60 5 | 80 0 | 130 -1
+	// [0]: 80, [1]: 20, [4]: 10, [5]: 20
+	file2.Update(TreeMergeMark, 80, 10, 10)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 60 5 | 80 M | 90 0 | 130 -1
+	// [0]: 70, [1]: 20, [4]: 10, [5]: 20
+	file2.Update(6, 0, 10, 10)
+	// 0 6 | 10 0 | 20 4 | 30 1 | 50 0 | 60 5 | 80 M | 90 0 | 130 -1
+	// [0]: 60, [1]: 20, [4]: 10, [5]: 20, [6]: 10
+	file1.Merge(7, file2)
+	// 0 0 | 20 4 | 30 1 | 50 0 | 60 5 | 80 7 | 90 0 | 130 -1
+	// [0]: 70, [1]: 20, [4]: 10, [5]: 20, [6]: 0, [7]: 10
+	dump := file1.Dump()
+	assert.Equal(t, "0 0\n20 4\n30 1\n50 0\n60 5\n80 7\n90 0\n130 -1\n", dump)
+	assert.Equal(t, int64(70), status[0])
+	assert.Equal(t, int64(20), status[1])
+	assert.Equal(t, int64(0), status[2])
+	assert.Equal(t, int64(0), status[3])
+	assert.Equal(t, int64(10), status[4])
+	assert.Equal(t, int64(20), status[5])
+	assert.Equal(t, int64(0), status[6])
+	assert.Equal(t, int64(10), status[7])
+}

+ 146 - 111
internal/rbtree/rbtree.go

@@ -22,7 +22,7 @@ type RBTree struct {
 	// Root of the tree
 	// Root of the tree
 	root *node
 	root *node
 
 
-	// The minimum and maximum nodes under the root.
+	// The minimum and maximum nodes under the tree.
 	minNode, maxNode *node
 	minNode, maxNode *node
 
 
 	// Number of nodes under root, including the root
 	// Number of nodes under root, including the root
@@ -30,14 +30,49 @@ type RBTree struct {
 }
 }
 
 
 // Len returns the number of elements in the tree.
 // Len returns the number of elements in the tree.
-func (root *RBTree) Len() int {
-	return root.count
+func (tree *RBTree) Len() int {
+	return tree.count
+}
+
+// Clone performs a deep copy of the tree.
+func (tree *RBTree) Clone() *RBTree {
+	clone := &RBTree{}
+	clone.count = tree.count
+	nodeMap := map[*node]*node{}
+	queue := []*node{tree.root}
+	for len(queue) > 0 {
+		head := queue[len(queue)-1]
+		queue = queue[:len(queue)-1]
+		headCopy := *head
+		nodeMap[head] = &headCopy
+		if head.left != nil {
+			queue = append(queue, head.left)
+		}
+		if head.right != nil {
+			queue = append(queue, head.right)
+		}
+	}
+	for _, mapped := range nodeMap {
+		if mapped.parent != nil {
+			mapped.parent = nodeMap[mapped.parent]
+		}
+		if mapped.left != nil {
+			mapped.left = nodeMap[mapped.left]
+		}
+		if mapped.right != nil {
+			mapped.right = nodeMap[mapped.right]
+		}
+	}
+	clone.root = nodeMap[tree.root]
+	clone.minNode = nodeMap[tree.minNode]
+	clone.maxNode = nodeMap[tree.maxNode]
+	return clone
 }
 }
 
 
 // Get is a convenience function for finding an element equal to Key. Returns
 // Get is a convenience function for finding an element equal to Key. Returns
 // nil if not found.
 // nil if not found.
-func (root *RBTree) Get(key int) *int {
-	n, exact := root.findGE(key)
+func (tree *RBTree) Get(key int) *int {
+	n, exact := tree.findGE(key)
 	if exact {
 	if exact {
 		return &n.item.Value
 		return &n.item.Value
 	}
 	}
@@ -46,60 +81,60 @@ func (root *RBTree) Get(key int) *int {
 
 
 // Min creates an iterator that points to the minimum item in the tree.
 // Min creates an iterator that points to the minimum item in the tree.
 // If the tree is empty, returns Limit()
 // If the tree is empty, returns Limit()
-func (root *RBTree) Min() Iterator {
-	return Iterator{root, root.minNode}
+func (tree *RBTree) Min() Iterator {
+	return Iterator{tree, tree.minNode}
 }
 }
 
 
 // Max creates an iterator that points at the maximum item in the tree.
 // Max creates an iterator that points at the maximum item in the tree.
 //
 //
 // If the tree is empty, returns NegativeLimit().
 // If the tree is empty, returns NegativeLimit().
-func (root *RBTree) Max() Iterator {
-	if root.maxNode == nil {
-		return Iterator{root, negativeLimitNode}
+func (tree *RBTree) Max() Iterator {
+	if tree.maxNode == nil {
+		return Iterator{tree, negativeLimitNode}
 	}
 	}
-	return Iterator{root, root.maxNode}
+	return Iterator{tree, tree.maxNode}
 }
 }
 
 
 // Limit creates an iterator that points beyond the maximum item in the tree.
 // Limit creates an iterator that points beyond the maximum item in the tree.
-func (root *RBTree) Limit() Iterator {
-	return Iterator{root, nil}
+func (tree *RBTree) Limit() Iterator {
+	return Iterator{tree, nil}
 }
 }
 
 
 // NegativeLimit creates an iterator that points before the minimum item in the tree.
 // NegativeLimit creates an iterator that points before the minimum item in the tree.
-func (root *RBTree) NegativeLimit() Iterator {
-	return Iterator{root, negativeLimitNode}
+func (tree *RBTree) NegativeLimit() Iterator {
+	return Iterator{tree, negativeLimitNode}
 }
 }
 
 
 // FindGE finds the smallest element N such that N >= Key, and returns the
 // FindGE finds the smallest element N such that N >= Key, and returns the
 // iterator pointing to the element. If no such element is found,
 // iterator pointing to the element. If no such element is found,
-// returns root.Limit().
-func (root *RBTree) FindGE(key int) Iterator {
-	n, _ := root.findGE(key)
-	return Iterator{root, n}
+// returns tree.Limit().
+func (tree *RBTree) FindGE(key int) Iterator {
+	n, _ := tree.findGE(key)
+	return Iterator{tree, n}
 }
 }
 
 
 // FindLE finds the largest element N such that N <= Key, and returns the
 // FindLE finds the largest element N such that N <= Key, and returns the
 // iterator pointing to the element. If no such element is found,
 // iterator pointing to the element. If no such element is found,
 // returns iter.NegativeLimit().
 // returns iter.NegativeLimit().
-func (root *RBTree) FindLE(key int) Iterator {
-	n, exact := root.findGE(key)
+func (tree *RBTree) FindLE(key int) Iterator {
+	n, exact := tree.findGE(key)
 	if exact {
 	if exact {
-		return Iterator{root, n}
+		return Iterator{tree, n}
 	}
 	}
 	if n != nil {
 	if n != nil {
-		return Iterator{root, n.doPrev()}
+		return Iterator{tree, n.doPrev()}
 	}
 	}
-	if root.maxNode == nil {
-		return Iterator{root, negativeLimitNode}
+	if tree.maxNode == nil {
+		return Iterator{tree, negativeLimitNode}
 	}
 	}
-	return Iterator{root, root.maxNode}
+	return Iterator{tree, tree.maxNode}
 }
 }
 
 
 // Insert an item. If the item is already in the tree, do nothing and
 // Insert an item. If the item is already in the tree, do nothing and
 // return false. Else return true.
 // return false. Else return true.
-func (root *RBTree) Insert(item Item) (bool, Iterator) {
+func (tree *RBTree) Insert(item Item) (bool, Iterator) {
 	// TODO: delay creating n until it is found to be inserted
 	// TODO: delay creating n until it is found to be inserted
-	n := root.doInsert(item)
+	n := tree.doInsert(item)
 	if n == nil {
 	if n == nil {
 		return false, Iterator{}
 		return false, Iterator{}
 	}
 	}
@@ -139,12 +174,12 @@ func (root *RBTree) Insert(item Item) (bool, Iterator) {
 
 
 		// Case 4: parent is red, uncle is black (1)
 		// Case 4: parent is red, uncle is black (1)
 		if n.isRightChild() && n.parent.isLeftChild() {
 		if n.isRightChild() && n.parent.isLeftChild() {
-			root.rotateLeft(n.parent)
+			tree.rotateLeft(n.parent)
 			n = n.left
 			n = n.left
 			continue
 			continue
 		}
 		}
 		if n.isLeftChild() && n.parent.isRightChild() {
 		if n.isLeftChild() && n.parent.isRightChild() {
-			root.rotateRight(n.parent)
+			tree.rotateRight(n.parent)
 			n = n.right
 			n = n.right
 			continue
 			continue
 		}
 		}
@@ -153,21 +188,21 @@ func (root *RBTree) Insert(item Item) (bool, Iterator) {
 		n.parent.color = black
 		n.parent.color = black
 		grandparent.color = red
 		grandparent.color = red
 		if n.isLeftChild() {
 		if n.isLeftChild() {
-			root.rotateRight(grandparent)
+			tree.rotateRight(grandparent)
 		} else {
 		} else {
-			root.rotateLeft(grandparent)
+			tree.rotateLeft(grandparent)
 		}
 		}
 		break
 		break
 	}
 	}
-	return true, Iterator{root, insN}
+	return true, Iterator{tree, insN}
 }
 }
 
 
 // DeleteWithKey deletes an item with the given Key. Returns true iff the item was
 // DeleteWithKey deletes an item with the given Key. Returns true iff the item was
 // found.
 // found.
-func (root *RBTree) DeleteWithKey(key int) bool {
-	iter := root.FindGE(key)
+func (tree *RBTree) DeleteWithKey(key int) bool {
+	iter := tree.FindGE(key)
 	if iter.node != nil {
 	if iter.node != nil {
-		root.DeleteWithIterator(iter)
+		tree.DeleteWithIterator(iter)
 		return true
 		return true
 	}
 	}
 	return false
 	return false
@@ -176,9 +211,9 @@ func (root *RBTree) DeleteWithKey(key int) bool {
 // DeleteWithIterator deletes the current item.
 // DeleteWithIterator deletes the current item.
 //
 //
 // REQUIRES: !iter.Limit() && !iter.NegativeLimit()
 // REQUIRES: !iter.Limit() && !iter.NegativeLimit()
-func (root *RBTree) DeleteWithIterator(iter Iterator) {
+func (tree *RBTree) DeleteWithIterator(iter Iterator) {
 	doAssert(!iter.Limit() && !iter.NegativeLimit())
 	doAssert(!iter.Limit() && !iter.NegativeLimit())
-	root.doDelete(iter.node)
+	tree.doDelete(iter.node)
 }
 }
 
 
 // Iterator allows scanning tree elements in sort order.
 // Iterator allows scanning tree elements in sort order.
@@ -188,7 +223,7 @@ func (root *RBTree) DeleteWithIterator(iter Iterator) {
 // iterator becomes invalid. For other operation types, the iterator
 // iterator becomes invalid. For other operation types, the iterator
 // remains valid.
 // remains valid.
 type Iterator struct {
 type Iterator struct {
-	root *RBTree
+	tree *RBTree
 	node *node
 	node *node
 }
 }
 
 
@@ -204,12 +239,12 @@ func (iter Iterator) Limit() bool {
 
 
 // Min checks if the iterator points to the minimum element in the tree.
 // Min checks if the iterator points to the minimum element in the tree.
 func (iter Iterator) Min() bool {
 func (iter Iterator) Min() bool {
-	return iter.node == iter.root.minNode
+	return iter.node == iter.tree.minNode
 }
 }
 
 
 // Max checks if the iterator points to the maximum element in the tree.
 // Max checks if the iterator points to the maximum element in the tree.
 func (iter Iterator) Max() bool {
 func (iter Iterator) Max() bool {
-	return iter.node == iter.root.maxNode
+	return iter.node == iter.tree.maxNode
 }
 }
 
 
 // NegativeLimit checks if the iterator points before the minimum element in the tree.
 // NegativeLimit checks if the iterator points before the minimum element in the tree.
@@ -231,9 +266,9 @@ func (iter Iterator) Item() *Item {
 func (iter Iterator) Next() Iterator {
 func (iter Iterator) Next() Iterator {
 	doAssert(!iter.Limit())
 	doAssert(!iter.Limit())
 	if iter.NegativeLimit() {
 	if iter.NegativeLimit() {
-		return Iterator{iter.root, iter.root.minNode}
+		return Iterator{iter.tree, iter.tree.minNode}
 	}
 	}
-	return Iterator{iter.root, iter.node.doNext()}
+	return Iterator{iter.tree, iter.node.doNext()}
 }
 }
 
 
 // Prev creates a new iterator that points to the predecessor of the current
 // Prev creates a new iterator that points to the predecessor of the current
@@ -243,12 +278,12 @@ func (iter Iterator) Next() Iterator {
 func (iter Iterator) Prev() Iterator {
 func (iter Iterator) Prev() Iterator {
 	doAssert(!iter.NegativeLimit())
 	doAssert(!iter.NegativeLimit())
 	if !iter.Limit() {
 	if !iter.Limit() {
-		return Iterator{iter.root, iter.node.doPrev()}
+		return Iterator{iter.tree, iter.node.doPrev()}
 	}
 	}
-	if iter.root.maxNode == nil {
-		return Iterator{iter.root, negativeLimitNode}
+	if iter.tree.maxNode == nil {
+		return Iterator{iter.tree, negativeLimitNode}
 	}
 	}
-	return Iterator{iter.root, iter.root.maxNode}
+	return Iterator{iter.tree, iter.tree.maxNode}
 }
 }
 
 
 func doAssert(b bool) {
 func doAssert(b bool) {
@@ -356,54 +391,54 @@ func maxPredecessor(n *node) *node {
 // Private methods
 // Private methods
 //
 //
 
 
-func (root *RBTree) recomputeMinNode() {
-	root.minNode = root.root
-	if root.minNode != nil {
-		for root.minNode.left != nil {
-			root.minNode = root.minNode.left
+func (tree *RBTree) recomputeMinNode() {
+	tree.minNode = tree.root
+	if tree.minNode != nil {
+		for tree.minNode.left != nil {
+			tree.minNode = tree.minNode.left
 		}
 		}
 	}
 	}
 }
 }
 
 
-func (root *RBTree) recomputeMaxNode() {
-	root.maxNode = root.root
-	if root.maxNode != nil {
-		for root.maxNode.right != nil {
-			root.maxNode = root.maxNode.right
+func (tree *RBTree) recomputeMaxNode() {
+	tree.maxNode = tree.root
+	if tree.maxNode != nil {
+		for tree.maxNode.right != nil {
+			tree.maxNode = tree.maxNode.right
 		}
 		}
 	}
 	}
 }
 }
 
 
-func (root *RBTree) maybeSetMinNode(n *node) {
-	if root.minNode == nil {
-		root.minNode = n
-		root.maxNode = n
-	} else if n.item.Key < root.minNode.item.Key {
-		root.minNode = n
+func (tree *RBTree) maybeSetMinNode(n *node) {
+	if tree.minNode == nil {
+		tree.minNode = n
+		tree.maxNode = n
+	} else if n.item.Key < tree.minNode.item.Key {
+		tree.minNode = n
 	}
 	}
 }
 }
 
 
-func (root *RBTree) maybeSetMaxNode(n *node) {
-	if root.maxNode == nil {
-		root.minNode = n
-		root.maxNode = n
-	} else if n.item.Key > root.maxNode.item.Key {
-		root.maxNode = n
+func (tree *RBTree) maybeSetMaxNode(n *node) {
+	if tree.maxNode == nil {
+		tree.minNode = n
+		tree.maxNode = n
+	} else if n.item.Key > tree.maxNode.item.Key {
+		tree.maxNode = n
 	}
 	}
 }
 }
 
 
 // Try inserting "item" into the tree. Return nil if the item is
 // Try inserting "item" into the tree. Return nil if the item is
 // already in the tree. Otherwise return a new (leaf) node.
 // already in the tree. Otherwise return a new (leaf) node.
-func (root *RBTree) doInsert(item Item) *node {
-	if root.root == nil {
+func (tree *RBTree) doInsert(item Item) *node {
+	if tree.root == nil {
 		n := &node{item: item}
 		n := &node{item: item}
-		root.root = n
-		root.minNode = n
-		root.maxNode = n
-		root.count++
+		tree.root = n
+		tree.minNode = n
+		tree.maxNode = n
+		tree.count++
 		return n
 		return n
 	}
 	}
-	parent := root.root
+	parent := tree.root
 	for true {
 	for true {
 		comp := item.Key - parent.item.Key
 		comp := item.Key - parent.item.Key
 		if comp == 0 {
 		if comp == 0 {
@@ -412,8 +447,8 @@ func (root *RBTree) doInsert(item Item) *node {
 			if parent.left == nil {
 			if parent.left == nil {
 				n := &node{item: item, parent: parent}
 				n := &node{item: item, parent: parent}
 				parent.left = n
 				parent.left = n
-				root.count++
-				root.maybeSetMinNode(n)
+				tree.count++
+				tree.maybeSetMinNode(n)
 				return n
 				return n
 			}
 			}
 			parent = parent.left
 			parent = parent.left
@@ -421,8 +456,8 @@ func (root *RBTree) doInsert(item Item) *node {
 			if parent.right == nil {
 			if parent.right == nil {
 				n := &node{item: item, parent: parent}
 				n := &node{item: item, parent: parent}
 				parent.right = n
 				parent.right = n
-				root.count++
-				root.maybeSetMaxNode(n)
+				tree.count++
+				tree.maybeSetMaxNode(n)
 				return n
 				return n
 			}
 			}
 			parent = parent.right
 			parent = parent.right
@@ -434,8 +469,8 @@ func (root *RBTree) doInsert(item Item) *node {
 // Find a node whose item >= Key. The 2nd return Value is true iff the
 // Find a node whose item >= Key. The 2nd return Value is true iff the
 // node.item==Key. Returns (nil, false) if all nodes in the tree are <
 // node.item==Key. Returns (nil, false) if all nodes in the tree are <
 // Key.
 // Key.
-func (root *RBTree) findGE(key int) (*node, bool) {
-	n := root.root
+func (tree *RBTree) findGE(key int) (*node, bool) {
+	n := tree.root
 	for true {
 	for true {
 		if n == nil {
 		if n == nil {
 			return nil, false
 			return nil, false
@@ -465,10 +500,10 @@ func (root *RBTree) findGE(key int) (*node, bool) {
 }
 }
 
 
 // Delete N from the tree.
 // Delete N from the tree.
-func (root *RBTree) doDelete(n *node) {
+func (tree *RBTree) doDelete(n *node) {
 	if n.left != nil && n.right != nil {
 	if n.left != nil && n.right != nil {
 		pred := maxPredecessor(n)
 		pred := maxPredecessor(n)
-		root.swapNodes(n, pred)
+		tree.swapNodes(n, pred)
 	}
 	}
 
 
 	doAssert(n.left == nil || n.right == nil)
 	doAssert(n.left == nil || n.right == nil)
@@ -478,33 +513,33 @@ func (root *RBTree) doDelete(n *node) {
 	}
 	}
 	if n.color == black {
 	if n.color == black {
 		n.color = getColor(child)
 		n.color = getColor(child)
-		root.deleteCase1(n)
+		tree.deleteCase1(n)
 	}
 	}
-	root.replaceNode(n, child)
+	tree.replaceNode(n, child)
 	if n.parent == nil && child != nil {
 	if n.parent == nil && child != nil {
 		child.color = black
 		child.color = black
 	}
 	}
-	root.count--
-	if root.count == 0 {
-		root.minNode = nil
-		root.maxNode = nil
+	tree.count--
+	if tree.count == 0 {
+		tree.minNode = nil
+		tree.maxNode = nil
 	} else {
 	} else {
-		if root.minNode == n {
-			root.recomputeMinNode()
+		if tree.minNode == n {
+			tree.recomputeMinNode()
 		}
 		}
-		if root.maxNode == n {
-			root.recomputeMaxNode()
+		if tree.maxNode == n {
+			tree.recomputeMaxNode()
 		}
 		}
 	}
 	}
 }
 }
 
 
 // Move n to the pred's place, and vice versa
 // Move n to the pred's place, and vice versa
 //
 //
-func (root *RBTree) swapNodes(n, pred *node) {
+func (tree *RBTree) swapNodes(n, pred *node) {
 	doAssert(pred != n)
 	doAssert(pred != n)
 	isLeft := pred.isLeftChild()
 	isLeft := pred.isLeftChild()
 	tmp := *pred
 	tmp := *pred
-	root.replaceNode(n, pred)
+	tree.replaceNode(n, pred)
 	pred.color = n.color
 	pred.color = n.color
 
 
 	if tmp.parent == n {
 	if tmp.parent == n {
@@ -561,16 +596,16 @@ func (root *RBTree) swapNodes(n, pred *node) {
 	n.color = tmp.color
 	n.color = tmp.color
 }
 }
 
 
-func (root *RBTree) deleteCase1(n *node) {
+func (tree *RBTree) deleteCase1(n *node) {
 	for true {
 	for true {
 		if n.parent != nil {
 		if n.parent != nil {
 			if getColor(n.sibling()) == red {
 			if getColor(n.sibling()) == red {
 				n.parent.color = red
 				n.parent.color = red
 				n.sibling().color = black
 				n.sibling().color = black
 				if n == n.parent.left {
 				if n == n.parent.left {
-					root.rotateLeft(n.parent)
+					tree.rotateLeft(n.parent)
 				} else {
 				} else {
-					root.rotateRight(n.parent)
+					tree.rotateRight(n.parent)
 				}
 				}
 			}
 			}
 			if getColor(n.parent) == black &&
 			if getColor(n.parent) == black &&
@@ -589,7 +624,7 @@ func (root *RBTree) deleteCase1(n *node) {
 					n.sibling().color = red
 					n.sibling().color = red
 					n.parent.color = black
 					n.parent.color = black
 				} else {
 				} else {
-					root.deleteCase5(n)
+					tree.deleteCase5(n)
 				}
 				}
 			}
 			}
 		}
 		}
@@ -597,21 +632,21 @@ func (root *RBTree) deleteCase1(n *node) {
 	}
 	}
 }
 }
 
 
-func (root *RBTree) deleteCase5(n *node) {
+func (tree *RBTree) deleteCase5(n *node) {
 	if n == n.parent.left &&
 	if n == n.parent.left &&
 		getColor(n.sibling()) == black &&
 		getColor(n.sibling()) == black &&
 		getColor(n.sibling().left) == red &&
 		getColor(n.sibling().left) == red &&
 		getColor(n.sibling().right) == black {
 		getColor(n.sibling().right) == black {
 		n.sibling().color = red
 		n.sibling().color = red
 		n.sibling().left.color = black
 		n.sibling().left.color = black
-		root.rotateRight(n.sibling())
+		tree.rotateRight(n.sibling())
 	} else if n == n.parent.right &&
 	} else if n == n.parent.right &&
 		getColor(n.sibling()) == black &&
 		getColor(n.sibling()) == black &&
 		getColor(n.sibling().right) == red &&
 		getColor(n.sibling().right) == red &&
 		getColor(n.sibling().left) == black {
 		getColor(n.sibling().left) == black {
 		n.sibling().color = red
 		n.sibling().color = red
 		n.sibling().right.color = black
 		n.sibling().right.color = black
-		root.rotateLeft(n.sibling())
+		tree.rotateLeft(n.sibling())
 	}
 	}
 
 
 	// case 6
 	// case 6
@@ -620,17 +655,17 @@ func (root *RBTree) deleteCase5(n *node) {
 	if n == n.parent.left {
 	if n == n.parent.left {
 		doAssert(getColor(n.sibling().right) == red)
 		doAssert(getColor(n.sibling().right) == red)
 		n.sibling().right.color = black
 		n.sibling().right.color = black
-		root.rotateLeft(n.parent)
+		tree.rotateLeft(n.parent)
 	} else {
 	} else {
 		doAssert(getColor(n.sibling().left) == red)
 		doAssert(getColor(n.sibling().left) == red)
 		n.sibling().left.color = black
 		n.sibling().left.color = black
-		root.rotateRight(n.parent)
+		tree.rotateRight(n.parent)
 	}
 	}
 }
 }
 
 
-func (root *RBTree) replaceNode(oldn, newn *node) {
+func (tree *RBTree) replaceNode(oldn, newn *node) {
 	if oldn.parent == nil {
 	if oldn.parent == nil {
-		root.root = newn
+		tree.root = newn
 	} else {
 	} else {
 		if oldn == oldn.parent.left {
 		if oldn == oldn.parent.left {
 			oldn.parent.left = newn
 			oldn.parent.left = newn
@@ -648,7 +683,7 @@ func (root *RBTree) replaceNode(oldn, newn *node) {
   A   Y	    =>     X   C
   A   Y	    =>     X   C
      B C 	  A B
      B C 	  A B
 */
 */
-func (root *RBTree) rotateLeft(x *node) {
+func (tree *RBTree) rotateLeft(x *node) {
 	y := x.right
 	y := x.right
 	x.right = y.left
 	x.right = y.left
 	if y.left != nil {
 	if y.left != nil {
@@ -656,7 +691,7 @@ func (root *RBTree) rotateLeft(x *node) {
 	}
 	}
 	y.parent = x.parent
 	y.parent = x.parent
 	if x.parent == nil {
 	if x.parent == nil {
-		root.root = y
+		tree.root = y
 	} else {
 	} else {
 		if x.isLeftChild() {
 		if x.isLeftChild() {
 			x.parent.left = y
 			x.parent.left = y
@@ -673,7 +708,7 @@ func (root *RBTree) rotateLeft(x *node) {
    X   C  =>   A   Y
    X   C  =>   A   Y
   A B             B C
   A B             B C
 */
 */
-func (root *RBTree) rotateRight(y *node) {
+func (tree *RBTree) rotateRight(y *node) {
 	x := y.left
 	x := y.left
 
 
 	// Move "B"
 	// Move "B"
@@ -684,7 +719,7 @@ func (root *RBTree) rotateRight(y *node) {
 
 
 	x.parent = y.parent
 	x.parent = y.parent
 	if y.parent == nil {
 	if y.parent == nil {
-		root.root = x
+		tree.root = x
 	} else {
 	} else {
 		if y.isLeftChild() {
 		if y.isLeftChild() {
 			y.parent.left = x
 			y.parent.left = x