|  | "use strict" | 
|  |  | 
|  | module.exports = createRBTree | 
|  |  | 
|  | var RED   = 0 | 
|  | var BLACK = 1 | 
|  |  | 
|  | function RBNode(color, key, value, left, right, count) { | 
|  | this._color = color | 
|  | this.key = key | 
|  | this.value = value | 
|  | this.left = left | 
|  | this.right = right | 
|  | this._count = count | 
|  | } | 
|  |  | 
|  | function cloneNode(node) { | 
|  | return new RBNode(node._color, node.key, node.value, node.left, node.right, node._count) | 
|  | } | 
|  |  | 
|  | function repaint(color, node) { | 
|  | return new RBNode(color, node.key, node.value, node.left, node.right, node._count) | 
|  | } | 
|  |  | 
|  | function recount(node) { | 
|  | node._count = 1 + (node.left ? node.left._count : 0) + (node.right ? node.right._count : 0) | 
|  | } | 
|  |  | 
|  | function RedBlackTree(compare, root) { | 
|  | this._compare = compare | 
|  | this.root = root | 
|  | } | 
|  |  | 
|  | var proto = RedBlackTree.prototype | 
|  |  | 
|  | Object.defineProperty(proto, "keys", { | 
|  | get: function() { | 
|  | var result = [] | 
|  | this.forEach(function(k,v) { | 
|  | result.push(k) | 
|  | }) | 
|  | return result | 
|  | } | 
|  | }) | 
|  |  | 
|  | Object.defineProperty(proto, "values", { | 
|  | get: function() { | 
|  | var result = [] | 
|  | this.forEach(function(k,v) { | 
|  | result.push(v) | 
|  | }) | 
|  | return result | 
|  | } | 
|  | }) | 
|  |  | 
|  | //Returns the number of nodes in the tree | 
|  | Object.defineProperty(proto, "length", { | 
|  | get: function() { | 
|  | if(this.root) { | 
|  | return this.root._count | 
|  | } | 
|  | return 0 | 
|  | } | 
|  | }) | 
|  |  | 
|  | //Insert a new item into the tree | 
|  | proto.insert = function(key, value) { | 
|  | var cmp = this._compare | 
|  | //Find point to insert new node at | 
|  | var n = this.root | 
|  | var n_stack = [] | 
|  | var d_stack = [] | 
|  | while(n) { | 
|  | var d = cmp(key, n.key) | 
|  | n_stack.push(n) | 
|  | d_stack.push(d) | 
|  | if(d <= 0) { | 
|  | n = n.left | 
|  | } else { | 
|  | n = n.right | 
|  | } | 
|  | } | 
|  | //Rebuild path to leaf node | 
|  | n_stack.push(new RBNode(RED, key, value, null, null, 1)) | 
|  | for(var s=n_stack.length-2; s>=0; --s) { | 
|  | var n = n_stack[s] | 
|  | if(d_stack[s] <= 0) { | 
|  | n_stack[s] = new RBNode(n._color, n.key, n.value, n_stack[s+1], n.right, n._count+1) | 
|  | } else { | 
|  | n_stack[s] = new RBNode(n._color, n.key, n.value, n.left, n_stack[s+1], n._count+1) | 
|  | } | 
|  | } | 
|  | //Rebalance tree using rotations | 
|  | //console.log("start insert", key, d_stack) | 
|  | for(var s=n_stack.length-1; s>1; --s) { | 
|  | var p = n_stack[s-1] | 
|  | var n = n_stack[s] | 
|  | if(p._color === BLACK || n._color === BLACK) { | 
|  | break | 
|  | } | 
|  | var pp = n_stack[s-2] | 
|  | if(pp.left === p) { | 
|  | if(p.left === n) { | 
|  | var y = pp.right | 
|  | if(y && y._color === RED) { | 
|  | //console.log("LLr") | 
|  | p._color = BLACK | 
|  | pp.right = repaint(BLACK, y) | 
|  | pp._color = RED | 
|  | s -= 1 | 
|  | } else { | 
|  | //console.log("LLb") | 
|  | pp._color = RED | 
|  | pp.left = p.right | 
|  | p._color = BLACK | 
|  | p.right = pp | 
|  | n_stack[s-2] = p | 
|  | n_stack[s-1] = n | 
|  | recount(pp) | 
|  | recount(p) | 
|  | if(s >= 3) { | 
|  | var ppp = n_stack[s-3] | 
|  | if(ppp.left === pp) { | 
|  | ppp.left = p | 
|  | } else { | 
|  | ppp.right = p | 
|  | } | 
|  | } | 
|  | break | 
|  | } | 
|  | } else { | 
|  | var y = pp.right | 
|  | if(y && y._color === RED) { | 
|  | //console.log("LRr") | 
|  | p._color = BLACK | 
|  | pp.right = repaint(BLACK, y) | 
|  | pp._color = RED | 
|  | s -= 1 | 
|  | } else { | 
|  | //console.log("LRb") | 
|  | p.right = n.left | 
|  | pp._color = RED | 
|  | pp.left = n.right | 
|  | n._color = BLACK | 
|  | n.left = p | 
|  | n.right = pp | 
|  | n_stack[s-2] = n | 
|  | n_stack[s-1] = p | 
|  | recount(pp) | 
|  | recount(p) | 
|  | recount(n) | 
|  | if(s >= 3) { | 
|  | var ppp = n_stack[s-3] | 
|  | if(ppp.left === pp) { | 
|  | ppp.left = n | 
|  | } else { | 
|  | ppp.right = n | 
|  | } | 
|  | } | 
|  | break | 
|  | } | 
|  | } | 
|  | } else { | 
|  | if(p.right === n) { | 
|  | var y = pp.left | 
|  | if(y && y._color === RED) { | 
|  | //console.log("RRr", y.key) | 
|  | p._color = BLACK | 
|  | pp.left = repaint(BLACK, y) | 
|  | pp._color = RED | 
|  | s -= 1 | 
|  | } else { | 
|  | //console.log("RRb") | 
|  | pp._color = RED | 
|  | pp.right = p.left | 
|  | p._color = BLACK | 
|  | p.left = pp | 
|  | n_stack[s-2] = p | 
|  | n_stack[s-1] = n | 
|  | recount(pp) | 
|  | recount(p) | 
|  | if(s >= 3) { | 
|  | var ppp = n_stack[s-3] | 
|  | if(ppp.right === pp) { | 
|  | ppp.right = p | 
|  | } else { | 
|  | ppp.left = p | 
|  | } | 
|  | } | 
|  | break | 
|  | } | 
|  | } else { | 
|  | var y = pp.left | 
|  | if(y && y._color === RED) { | 
|  | //console.log("RLr") | 
|  | p._color = BLACK | 
|  | pp.left = repaint(BLACK, y) | 
|  | pp._color = RED | 
|  | s -= 1 | 
|  | } else { | 
|  | //console.log("RLb") | 
|  | p.left = n.right | 
|  | pp._color = RED | 
|  | pp.right = n.left | 
|  | n._color = BLACK | 
|  | n.right = p | 
|  | n.left = pp | 
|  | n_stack[s-2] = n | 
|  | n_stack[s-1] = p | 
|  | recount(pp) | 
|  | recount(p) | 
|  | recount(n) | 
|  | if(s >= 3) { | 
|  | var ppp = n_stack[s-3] | 
|  | if(ppp.right === pp) { | 
|  | ppp.right = n | 
|  | } else { | 
|  | ppp.left = n | 
|  | } | 
|  | } | 
|  | break | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | //Return new tree | 
|  | n_stack[0]._color = BLACK | 
|  | return new RedBlackTree(cmp, n_stack[0]) | 
|  | } | 
|  |  | 
|  |  | 
|  | //Visit all nodes inorder | 
|  | function doVisitFull(visit, node) { | 
|  | if(node.left) { | 
|  | var v = doVisitFull(visit, node.left) | 
|  | if(v) { return v } | 
|  | } | 
|  | var v = visit(node.key, node.value) | 
|  | if(v) { return v } | 
|  | if(node.right) { | 
|  | return doVisitFull(visit, node.right) | 
|  | } | 
|  | } | 
|  |  | 
|  | //Visit half nodes in order | 
|  | function doVisitHalf(lo, compare, visit, node) { | 
|  | var l = compare(lo, node.key) | 
|  | if(l <= 0) { | 
|  | if(node.left) { | 
|  | var v = doVisitHalf(lo, compare, visit, node.left) | 
|  | if(v) { return v } | 
|  | } | 
|  | var v = visit(node.key, node.value) | 
|  | if(v) { return v } | 
|  | } | 
|  | if(node.right) { | 
|  | return doVisitHalf(lo, compare, visit, node.right) | 
|  | } | 
|  | } | 
|  |  | 
|  | //Visit all nodes within a range | 
|  | function doVisit(lo, hi, compare, visit, node) { | 
|  | var l = compare(lo, node.key) | 
|  | var h = compare(hi, node.key) | 
|  | var v | 
|  | if(l <= 0) { | 
|  | if(node.left) { | 
|  | v = doVisit(lo, hi, compare, visit, node.left) | 
|  | if(v) { return v } | 
|  | } | 
|  | if(h > 0) { | 
|  | v = visit(node.key, node.value) | 
|  | if(v) { return v } | 
|  | } | 
|  | } | 
|  | if(h > 0 && node.right) { | 
|  | return doVisit(lo, hi, compare, visit, node.right) | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | proto.forEach = function rbTreeForEach(visit, lo, hi) { | 
|  | if(!this.root) { | 
|  | return | 
|  | } | 
|  | switch(arguments.length) { | 
|  | case 1: | 
|  | return doVisitFull(visit, this.root) | 
|  | break | 
|  |  | 
|  | case 2: | 
|  | return doVisitHalf(lo, this._compare, visit, this.root) | 
|  | break | 
|  |  | 
|  | case 3: | 
|  | if(this._compare(lo, hi) >= 0) { | 
|  | return | 
|  | } | 
|  | return doVisit(lo, hi, this._compare, visit, this.root) | 
|  | break | 
|  | } | 
|  | } | 
|  |  | 
|  | //First item in list | 
|  | Object.defineProperty(proto, "begin", { | 
|  | get: function() { | 
|  | var stack = [] | 
|  | var n = this.root | 
|  | while(n) { | 
|  | stack.push(n) | 
|  | n = n.left | 
|  | } | 
|  | return new RedBlackTreeIterator(this, stack) | 
|  | } | 
|  | }) | 
|  |  | 
|  | //Last item in list | 
|  | Object.defineProperty(proto, "end", { | 
|  | get: function() { | 
|  | var stack = [] | 
|  | var n = this.root | 
|  | while(n) { | 
|  | stack.push(n) | 
|  | n = n.right | 
|  | } | 
|  | return new RedBlackTreeIterator(this, stack) | 
|  | } | 
|  | }) | 
|  |  | 
|  | //Find the ith item in the tree | 
|  | proto.at = function(idx) { | 
|  | if(idx < 0) { | 
|  | return new RedBlackTreeIterator(this, []) | 
|  | } | 
|  | var n = this.root | 
|  | var stack = [] | 
|  | while(true) { | 
|  | stack.push(n) | 
|  | if(n.left) { | 
|  | if(idx < n.left._count) { | 
|  | n = n.left | 
|  | continue | 
|  | } | 
|  | idx -= n.left._count | 
|  | } | 
|  | if(!idx) { | 
|  | return new RedBlackTreeIterator(this, stack) | 
|  | } | 
|  | idx -= 1 | 
|  | if(n.right) { | 
|  | if(idx >= n.right._count) { | 
|  | break | 
|  | } | 
|  | n = n.right | 
|  | } else { | 
|  | break | 
|  | } | 
|  | } | 
|  | return new RedBlackTreeIterator(this, []) | 
|  | } | 
|  |  | 
|  | proto.ge = function(key) { | 
|  | var cmp = this._compare | 
|  | var n = this.root | 
|  | var stack = [] | 
|  | var last_ptr = 0 | 
|  | while(n) { | 
|  | var d = cmp(key, n.key) | 
|  | stack.push(n) | 
|  | if(d <= 0) { | 
|  | last_ptr = stack.length | 
|  | } | 
|  | if(d <= 0) { | 
|  | n = n.left | 
|  | } else { | 
|  | n = n.right | 
|  | } | 
|  | } | 
|  | stack.length = last_ptr | 
|  | return new RedBlackTreeIterator(this, stack) | 
|  | } | 
|  |  | 
|  | proto.gt = function(key) { | 
|  | var cmp = this._compare | 
|  | var n = this.root | 
|  | var stack = [] | 
|  | var last_ptr = 0 | 
|  | while(n) { | 
|  | var d = cmp(key, n.key) | 
|  | stack.push(n) | 
|  | if(d < 0) { | 
|  | last_ptr = stack.length | 
|  | } | 
|  | if(d < 0) { | 
|  | n = n.left | 
|  | } else { | 
|  | n = n.right | 
|  | } | 
|  | } | 
|  | stack.length = last_ptr | 
|  | return new RedBlackTreeIterator(this, stack) | 
|  | } | 
|  |  | 
|  | proto.lt = function(key) { | 
|  | var cmp = this._compare | 
|  | var n = this.root | 
|  | var stack = [] | 
|  | var last_ptr = 0 | 
|  | while(n) { | 
|  | var d = cmp(key, n.key) | 
|  | stack.push(n) | 
|  | if(d > 0) { | 
|  | last_ptr = stack.length | 
|  | } | 
|  | if(d <= 0) { | 
|  | n = n.left | 
|  | } else { | 
|  | n = n.right | 
|  | } | 
|  | } | 
|  | stack.length = last_ptr | 
|  | return new RedBlackTreeIterator(this, stack) | 
|  | } | 
|  |  | 
|  | proto.le = function(key) { | 
|  | var cmp = this._compare | 
|  | var n = this.root | 
|  | var stack = [] | 
|  | var last_ptr = 0 | 
|  | while(n) { | 
|  | var d = cmp(key, n.key) | 
|  | stack.push(n) | 
|  | if(d >= 0) { | 
|  | last_ptr = stack.length | 
|  | } | 
|  | if(d < 0) { | 
|  | n = n.left | 
|  | } else { | 
|  | n = n.right | 
|  | } | 
|  | } | 
|  | stack.length = last_ptr | 
|  | return new RedBlackTreeIterator(this, stack) | 
|  | } | 
|  |  | 
|  | //Finds the item with key if it exists | 
|  | proto.find = function(key) { | 
|  | var cmp = this._compare | 
|  | var n = this.root | 
|  | var stack = [] | 
|  | while(n) { | 
|  | var d = cmp(key, n.key) | 
|  | stack.push(n) | 
|  | if(d === 0) { | 
|  | return new RedBlackTreeIterator(this, stack) | 
|  | } | 
|  | if(d <= 0) { | 
|  | n = n.left | 
|  | } else { | 
|  | n = n.right | 
|  | } | 
|  | } | 
|  | return new RedBlackTreeIterator(this, []) | 
|  | } | 
|  |  | 
|  | //Removes item with key from tree | 
|  | proto.remove = function(key) { | 
|  | var iter = this.find(key) | 
|  | if(iter) { | 
|  | return iter.remove() | 
|  | } | 
|  | return this | 
|  | } | 
|  |  | 
|  | //Returns the item at `key` | 
|  | proto.get = function(key) { | 
|  | var cmp = this._compare | 
|  | var n = this.root | 
|  | while(n) { | 
|  | var d = cmp(key, n.key) | 
|  | if(d === 0) { | 
|  | return n.value | 
|  | } | 
|  | if(d <= 0) { | 
|  | n = n.left | 
|  | } else { | 
|  | n = n.right | 
|  | } | 
|  | } | 
|  | return | 
|  | } | 
|  |  | 
|  | //Iterator for red black tree | 
|  | function RedBlackTreeIterator(tree, stack) { | 
|  | this.tree = tree | 
|  | this._stack = stack | 
|  | } | 
|  |  | 
|  | var iproto = RedBlackTreeIterator.prototype | 
|  |  | 
|  | //Test if iterator is valid | 
|  | Object.defineProperty(iproto, "valid", { | 
|  | get: function() { | 
|  | return this._stack.length > 0 | 
|  | } | 
|  | }) | 
|  |  | 
|  | //Node of the iterator | 
|  | Object.defineProperty(iproto, "node", { | 
|  | get: function() { | 
|  | if(this._stack.length > 0) { | 
|  | return this._stack[this._stack.length-1] | 
|  | } | 
|  | return null | 
|  | }, | 
|  | enumerable: true | 
|  | }) | 
|  |  | 
|  | //Makes a copy of an iterator | 
|  | iproto.clone = function() { | 
|  | return new RedBlackTreeIterator(this.tree, this._stack.slice()) | 
|  | } | 
|  |  | 
|  | //Swaps two nodes | 
|  | function swapNode(n, v) { | 
|  | n.key = v.key | 
|  | n.value = v.value | 
|  | n.left = v.left | 
|  | n.right = v.right | 
|  | n._color = v._color | 
|  | n._count = v._count | 
|  | } | 
|  |  | 
|  | //Fix up a double black node in a tree | 
|  | function fixDoubleBlack(stack) { | 
|  | var n, p, s, z | 
|  | for(var i=stack.length-1; i>=0; --i) { | 
|  | n = stack[i] | 
|  | if(i === 0) { | 
|  | n._color = BLACK | 
|  | return | 
|  | } | 
|  | //console.log("visit node:", n.key, i, stack[i].key, stack[i-1].key) | 
|  | p = stack[i-1] | 
|  | if(p.left === n) { | 
|  | //console.log("left child") | 
|  | s = p.right | 
|  | if(s.right && s.right._color === RED) { | 
|  | //console.log("case 1: right sibling child red") | 
|  | s = p.right = cloneNode(s) | 
|  | z = s.right = cloneNode(s.right) | 
|  | p.right = s.left | 
|  | s.left = p | 
|  | s.right = z | 
|  | s._color = p._color | 
|  | n._color = BLACK | 
|  | p._color = BLACK | 
|  | z._color = BLACK | 
|  | recount(p) | 
|  | recount(s) | 
|  | if(i > 1) { | 
|  | var pp = stack[i-2] | 
|  | if(pp.left === p) { | 
|  | pp.left = s | 
|  | } else { | 
|  | pp.right = s | 
|  | } | 
|  | } | 
|  | stack[i-1] = s | 
|  | return | 
|  | } else if(s.left && s.left._color === RED) { | 
|  | //console.log("case 1: left sibling child red") | 
|  | s = p.right = cloneNode(s) | 
|  | z = s.left = cloneNode(s.left) | 
|  | p.right = z.left | 
|  | s.left = z.right | 
|  | z.left = p | 
|  | z.right = s | 
|  | z._color = p._color | 
|  | p._color = BLACK | 
|  | s._color = BLACK | 
|  | n._color = BLACK | 
|  | recount(p) | 
|  | recount(s) | 
|  | recount(z) | 
|  | if(i > 1) { | 
|  | var pp = stack[i-2] | 
|  | if(pp.left === p) { | 
|  | pp.left = z | 
|  | } else { | 
|  | pp.right = z | 
|  | } | 
|  | } | 
|  | stack[i-1] = z | 
|  | return | 
|  | } | 
|  | if(s._color === BLACK) { | 
|  | if(p._color === RED) { | 
|  | //console.log("case 2: black sibling, red parent", p.right.value) | 
|  | p._color = BLACK | 
|  | p.right = repaint(RED, s) | 
|  | return | 
|  | } else { | 
|  | //console.log("case 2: black sibling, black parent", p.right.value) | 
|  | p.right = repaint(RED, s) | 
|  | continue | 
|  | } | 
|  | } else { | 
|  | //console.log("case 3: red sibling") | 
|  | s = cloneNode(s) | 
|  | p.right = s.left | 
|  | s.left = p | 
|  | s._color = p._color | 
|  | p._color = RED | 
|  | recount(p) | 
|  | recount(s) | 
|  | if(i > 1) { | 
|  | var pp = stack[i-2] | 
|  | if(pp.left === p) { | 
|  | pp.left = s | 
|  | } else { | 
|  | pp.right = s | 
|  | } | 
|  | } | 
|  | stack[i-1] = s | 
|  | stack[i] = p | 
|  | if(i+1 < stack.length) { | 
|  | stack[i+1] = n | 
|  | } else { | 
|  | stack.push(n) | 
|  | } | 
|  | i = i+2 | 
|  | } | 
|  | } else { | 
|  | //console.log("right child") | 
|  | s = p.left | 
|  | if(s.left && s.left._color === RED) { | 
|  | //console.log("case 1: left sibling child red", p.value, p._color) | 
|  | s = p.left = cloneNode(s) | 
|  | z = s.left = cloneNode(s.left) | 
|  | p.left = s.right | 
|  | s.right = p | 
|  | s.left = z | 
|  | s._color = p._color | 
|  | n._color = BLACK | 
|  | p._color = BLACK | 
|  | z._color = BLACK | 
|  | recount(p) | 
|  | recount(s) | 
|  | if(i > 1) { | 
|  | var pp = stack[i-2] | 
|  | if(pp.right === p) { | 
|  | pp.right = s | 
|  | } else { | 
|  | pp.left = s | 
|  | } | 
|  | } | 
|  | stack[i-1] = s | 
|  | return | 
|  | } else if(s.right && s.right._color === RED) { | 
|  | //console.log("case 1: right sibling child red") | 
|  | s = p.left = cloneNode(s) | 
|  | z = s.right = cloneNode(s.right) | 
|  | p.left = z.right | 
|  | s.right = z.left | 
|  | z.right = p | 
|  | z.left = s | 
|  | z._color = p._color | 
|  | p._color = BLACK | 
|  | s._color = BLACK | 
|  | n._color = BLACK | 
|  | recount(p) | 
|  | recount(s) | 
|  | recount(z) | 
|  | if(i > 1) { | 
|  | var pp = stack[i-2] | 
|  | if(pp.right === p) { | 
|  | pp.right = z | 
|  | } else { | 
|  | pp.left = z | 
|  | } | 
|  | } | 
|  | stack[i-1] = z | 
|  | return | 
|  | } | 
|  | if(s._color === BLACK) { | 
|  | if(p._color === RED) { | 
|  | //console.log("case 2: black sibling, red parent") | 
|  | p._color = BLACK | 
|  | p.left = repaint(RED, s) | 
|  | return | 
|  | } else { | 
|  | //console.log("case 2: black sibling, black parent") | 
|  | p.left = repaint(RED, s) | 
|  | continue | 
|  | } | 
|  | } else { | 
|  | //console.log("case 3: red sibling") | 
|  | s = cloneNode(s) | 
|  | p.left = s.right | 
|  | s.right = p | 
|  | s._color = p._color | 
|  | p._color = RED | 
|  | recount(p) | 
|  | recount(s) | 
|  | if(i > 1) { | 
|  | var pp = stack[i-2] | 
|  | if(pp.right === p) { | 
|  | pp.right = s | 
|  | } else { | 
|  | pp.left = s | 
|  | } | 
|  | } | 
|  | stack[i-1] = s | 
|  | stack[i] = p | 
|  | if(i+1 < stack.length) { | 
|  | stack[i+1] = n | 
|  | } else { | 
|  | stack.push(n) | 
|  | } | 
|  | i = i+2 | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //Removes item at iterator from tree | 
|  | iproto.remove = function() { | 
|  | var stack = this._stack | 
|  | if(stack.length === 0) { | 
|  | return this.tree | 
|  | } | 
|  | //First copy path to node | 
|  | var cstack = new Array(stack.length) | 
|  | var n = stack[stack.length-1] | 
|  | cstack[cstack.length-1] = new RBNode(n._color, n.key, n.value, n.left, n.right, n._count) | 
|  | for(var i=stack.length-2; i>=0; --i) { | 
|  | var n = stack[i] | 
|  | if(n.left === stack[i+1]) { | 
|  | cstack[i] = new RBNode(n._color, n.key, n.value, cstack[i+1], n.right, n._count) | 
|  | } else { | 
|  | cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count) | 
|  | } | 
|  | } | 
|  |  | 
|  | //Get node | 
|  | n = cstack[cstack.length-1] | 
|  | //console.log("start remove: ", n.value) | 
|  |  | 
|  | //If not leaf, then swap with previous node | 
|  | if(n.left && n.right) { | 
|  | //console.log("moving to leaf") | 
|  |  | 
|  | //First walk to previous leaf | 
|  | var split = cstack.length | 
|  | n = n.left | 
|  | while(n.right) { | 
|  | cstack.push(n) | 
|  | n = n.right | 
|  | } | 
|  | //Copy path to leaf | 
|  | var v = cstack[split-1] | 
|  | cstack.push(new RBNode(n._color, v.key, v.value, n.left, n.right, n._count)) | 
|  | cstack[split-1].key = n.key | 
|  | cstack[split-1].value = n.value | 
|  |  | 
|  | //Fix up stack | 
|  | for(var i=cstack.length-2; i>=split; --i) { | 
|  | n = cstack[i] | 
|  | cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count) | 
|  | } | 
|  | cstack[split-1].left = cstack[split] | 
|  | } | 
|  | //console.log("stack=", cstack.map(function(v) { return v.value })) | 
|  |  | 
|  | //Remove leaf node | 
|  | n = cstack[cstack.length-1] | 
|  | if(n._color === RED) { | 
|  | //Easy case: removing red leaf | 
|  | //console.log("RED leaf") | 
|  | var p = cstack[cstack.length-2] | 
|  | if(p.left === n) { | 
|  | p.left = null | 
|  | } else if(p.right === n) { | 
|  | p.right = null | 
|  | } | 
|  | cstack.pop() | 
|  | for(var i=0; i<cstack.length; ++i) { | 
|  | cstack[i]._count-- | 
|  | } | 
|  | return new RedBlackTree(this.tree._compare, cstack[0]) | 
|  | } else { | 
|  | if(n.left || n.right) { | 
|  | //Second easy case:  Single child black parent | 
|  | //console.log("BLACK single child") | 
|  | if(n.left) { | 
|  | swapNode(n, n.left) | 
|  | } else if(n.right) { | 
|  | swapNode(n, n.right) | 
|  | } | 
|  | //Child must be red, so repaint it black to balance color | 
|  | n._color = BLACK | 
|  | for(var i=0; i<cstack.length-1; ++i) { | 
|  | cstack[i]._count-- | 
|  | } | 
|  | return new RedBlackTree(this.tree._compare, cstack[0]) | 
|  | } else if(cstack.length === 1) { | 
|  | //Third easy case: root | 
|  | //console.log("ROOT") | 
|  | return new RedBlackTree(this.tree._compare, null) | 
|  | } else { | 
|  | //Hard case: Repaint n, and then do some nasty stuff | 
|  | //console.log("BLACK leaf no children") | 
|  | for(var i=0; i<cstack.length; ++i) { | 
|  | cstack[i]._count-- | 
|  | } | 
|  | var parent = cstack[cstack.length-2] | 
|  | fixDoubleBlack(cstack) | 
|  | //Fix up links | 
|  | if(parent.left === n) { | 
|  | parent.left = null | 
|  | } else { | 
|  | parent.right = null | 
|  | } | 
|  | } | 
|  | } | 
|  | return new RedBlackTree(this.tree._compare, cstack[0]) | 
|  | } | 
|  |  | 
|  | //Returns key | 
|  | Object.defineProperty(iproto, "key", { | 
|  | get: function() { | 
|  | if(this._stack.length > 0) { | 
|  | return this._stack[this._stack.length-1].key | 
|  | } | 
|  | return | 
|  | }, | 
|  | enumerable: true | 
|  | }) | 
|  |  | 
|  | //Returns value | 
|  | Object.defineProperty(iproto, "value", { | 
|  | get: function() { | 
|  | if(this._stack.length > 0) { | 
|  | return this._stack[this._stack.length-1].value | 
|  | } | 
|  | return | 
|  | }, | 
|  | enumerable: true | 
|  | }) | 
|  |  | 
|  |  | 
|  | //Returns the position of this iterator in the sorted list | 
|  | Object.defineProperty(iproto, "index", { | 
|  | get: function() { | 
|  | var idx = 0 | 
|  | var stack = this._stack | 
|  | if(stack.length === 0) { | 
|  | var r = this.tree.root | 
|  | if(r) { | 
|  | return r._count | 
|  | } | 
|  | return 0 | 
|  | } else if(stack[stack.length-1].left) { | 
|  | idx = stack[stack.length-1].left._count | 
|  | } | 
|  | for(var s=stack.length-2; s>=0; --s) { | 
|  | if(stack[s+1] === stack[s].right) { | 
|  | ++idx | 
|  | if(stack[s].left) { | 
|  | idx += stack[s].left._count | 
|  | } | 
|  | } | 
|  | } | 
|  | return idx | 
|  | }, | 
|  | enumerable: true | 
|  | }) | 
|  |  | 
|  | //Advances iterator to next element in list | 
|  | iproto.next = function() { | 
|  | var stack = this._stack | 
|  | if(stack.length === 0) { | 
|  | return | 
|  | } | 
|  | var n = stack[stack.length-1] | 
|  | if(n.right) { | 
|  | n = n.right | 
|  | while(n) { | 
|  | stack.push(n) | 
|  | n = n.left | 
|  | } | 
|  | } else { | 
|  | stack.pop() | 
|  | while(stack.length > 0 && stack[stack.length-1].right === n) { | 
|  | n = stack[stack.length-1] | 
|  | stack.pop() | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //Checks if iterator is at end of tree | 
|  | Object.defineProperty(iproto, "hasNext", { | 
|  | get: function() { | 
|  | var stack = this._stack | 
|  | if(stack.length === 0) { | 
|  | return false | 
|  | } | 
|  | if(stack[stack.length-1].right) { | 
|  | return true | 
|  | } | 
|  | for(var s=stack.length-1; s>0; --s) { | 
|  | if(stack[s-1].left === stack[s]) { | 
|  | return true | 
|  | } | 
|  | } | 
|  | return false | 
|  | } | 
|  | }) | 
|  |  | 
|  | //Update value | 
|  | iproto.update = function(value) { | 
|  | var stack = this._stack | 
|  | if(stack.length === 0) { | 
|  | throw new Error("Can't update empty node!") | 
|  | } | 
|  | var cstack = new Array(stack.length) | 
|  | var n = stack[stack.length-1] | 
|  | cstack[cstack.length-1] = new RBNode(n._color, n.key, value, n.left, n.right, n._count) | 
|  | for(var i=stack.length-2; i>=0; --i) { | 
|  | n = stack[i] | 
|  | if(n.left === stack[i+1]) { | 
|  | cstack[i] = new RBNode(n._color, n.key, n.value, cstack[i+1], n.right, n._count) | 
|  | } else { | 
|  | cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count) | 
|  | } | 
|  | } | 
|  | return new RedBlackTree(this.tree._compare, cstack[0]) | 
|  | } | 
|  |  | 
|  | //Moves iterator backward one element | 
|  | iproto.prev = function() { | 
|  | var stack = this._stack | 
|  | if(stack.length === 0) { | 
|  | return | 
|  | } | 
|  | var n = stack[stack.length-1] | 
|  | if(n.left) { | 
|  | n = n.left | 
|  | while(n) { | 
|  | stack.push(n) | 
|  | n = n.right | 
|  | } | 
|  | } else { | 
|  | stack.pop() | 
|  | while(stack.length > 0 && stack[stack.length-1].left === n) { | 
|  | n = stack[stack.length-1] | 
|  | stack.pop() | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //Checks if iterator is at start of tree | 
|  | Object.defineProperty(iproto, "hasPrev", { | 
|  | get: function() { | 
|  | var stack = this._stack | 
|  | if(stack.length === 0) { | 
|  | return false | 
|  | } | 
|  | if(stack[stack.length-1].left) { | 
|  | return true | 
|  | } | 
|  | for(var s=stack.length-1; s>0; --s) { | 
|  | if(stack[s-1].right === stack[s]) { | 
|  | return true | 
|  | } | 
|  | } | 
|  | return false | 
|  | } | 
|  | }) | 
|  |  | 
|  | //Default comparison function | 
|  | function defaultCompare(a, b) { | 
|  | if(a < b) { | 
|  | return -1 | 
|  | } | 
|  | if(a > b) { | 
|  | return 1 | 
|  | } | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | //Build a tree | 
|  | function createRBTree(compare) { | 
|  | return new RedBlackTree(compare || defaultCompare, null) | 
|  | } |