function smaller (a, b) { return a[0] < b[0] || (a[0] === b[0] && a[1] < b[1]); } class N { // A created node is always red! constructor (val) { this.val = val; this.color = true; this._left = null; this._right = null; this._parent = null; if (val.id === null) { throw new Error("You must define id!"); } } isRed () { return this.color; } isBlack () { return !this.color; } redden () { this.color = true; return this; } blacken () { this.color = false; return this; } get grandparent () { return this.parent.parent; } get parent () { return this._parent; } get sibling () { return (this === this.parent.left) ? this.parent.right : this.parent.left; } get left () { return this._left; } get right () { return this._right; } set left (n) { if (n !== null) { n._parent = this; } this._left = n; } set right (n) { if (n !== null) { n._parent = this; } this._right = n; } rotateLeft (tree) { var parent = this.parent; var newParent = this.right; var newRight = this.right.left; newParent.left = this; this.right = newRight; if (parent === null) { tree.root = newParent; newParent._parent = null; } else if (parent.left === this) { parent.left = newParent; } else if (parent.right === this) { parent.right = newParent; } else { throw new Error("The elements are wrongly connected!"); } } next () { if ( this.right !== null ) { // search the most left node in the right tree var o = this.right; while (o.left !== null) { o = o.left; } return o; } else { var p = this; while (p.parent !== null && p !== p.parent.left) { p = p.parent; } return p.parent; } } rotateRight (tree) { var parent = this.parent; var newParent = this.left; var newLeft = this.left.right; newParent.right = this; this.left = newLeft; if (parent === null) { tree.root = newParent; newParent._parent = null; } else if (parent.left === this) { parent.left = newParent; } else if (parent.right === this) { parent.right = newParent; } else { throw new Error("The elements are wrongly connected!"); } } getUncle () { // we can assume that grandparent exists when this is called! if (this.parent === this.parent.parent.left) { return this.parent.parent.right; } else { return this.parent.parent.left; } } } class RBTree { //eslint-disable-line no-unused-vars constructor () { this.root = null; this.length = 0; } findNodeWithLowerBound (from) { if (from === void 0) { throw new Error("You must define from!"); } var o = this.root; if (o === null) { return false; } else { while (true) { if ((from === null || smaller(from, o.val.id)) && o.left !== null) { // o is included in the bound // try to find an element that is closer to the bound o = o.left; } else if (from !== null && smaller(o.val.id, from)) { // o is not within the bound, maybe one of the right elements is.. if (o.right !== null) { o = o.right; } else { // there is no right element. Search for the next bigger element, // this should be within the bounds return o.next(); } } else { return o; } } } } iterate (from, to, f) { var o = this.findNodeWithLowerBound(from); while ( o !== null && (to === null || smaller(o.val.id, to) || compareIds(o.val.id, to)) ) { f(o.val); o = o.next(); } return true; } find (id) { return this.findNode(id).val; } findNode (id) { if (id == null || id.constructor !== Array) { throw new Error("Expect id to be an array!"); } var o = this.root; if (o === null) { return false; } else { while (true) { if (o === null) { return false; } if (smaller(id, o.val.id)) { o = o.left; } else if (smaller(o.val.id, id)) { o = o.right; } else { return o; } } } } delete (id) { if (id == null || id.constructor !== Array) { throw new Error("id is expected to be an Array!"); } var d = this.findNode(id); if (d == null) { throw new Error("Element does not exist!"); } this.length--; if (d.left !== null && d.right !== null) { // switch d with the greates element in the left subtree. // o should have at most one child. var o = d.left; // find while (o.right !== null) { o = o.right; } // switch d.val = o.val; d = o; } // d has at most one child // let n be the node that replaces d var isFakeChild; var child = d.left || d.right; if ( child === null) { isFakeChild = true; child = new N({id: 0}); child.blacken(); d.right = child; } else { isFakeChild = false; } if (d.parent === null) { if (!isFakeChild) { this.root = child; child.blacken(); child._parent = null; } else { this.root = null; } return; } else if (d.parent.left === d) { d.parent.left = child; } else if (d.parent.right === d) { d.parent.right = child; } else { throw new Error("Impossible!"); } if ( d.isBlack() ) { if ( child.isRed() ) { child.blacken(); } else { this._fixDelete(child); } } this.root.blacken(); if (isFakeChild) { if (child.parent.left === child) { child.parent.left = null; } else if (child.parent.right === child) { child.parent.right = null; } else { throw new Error("Impossible #3"); } } } _fixDelete (n) { function isBlack (node) { return node !== null ? node.isBlack() : true; } function isRed(node) { return node !== null ? node.isRed() : false; } if (n.parent === null) { // this can only be called after the first iteration of fixDelete. return; } // d was already replaced by the child // d is not the root // d and child are black var sibling = n.sibling; if (isRed(sibling)) { // make sibling the grandfather n.parent.redden(); sibling.blacken(); if (n === n.parent.left) { n.parent.rotateLeft(this); } else if (n === n.parent.right) { n.parent.rotateRight(this); } else { throw new Error("Impossible #2"); } sibling = n.sibling; } // parent, sibling, and children of n are black if ( n.parent.isBlack() && sibling.isBlack() && isBlack(sibling.left) && isBlack(sibling.right) ) { sibling.redden(); this._fixDelete(n.parent); } else if ( n.parent.isRed() && sibling.isBlack() && isBlack(sibling.left) && isBlack(sibling.right) ) { sibling.redden(); n.parent.blacken(); } else { if ( n === n.parent.left && sibling.isBlack() && isRed(sibling.left) && isBlack(sibling.right) ) { sibling.redden(); sibling.left.blacken(); sibling.rotateRight(this); sibling = n.sibling; } else if ( n === n.parent.right && sibling.isBlack() && isRed(sibling.right) && isBlack(sibling.left) ) { sibling.redden(); sibling.right.blacken(); sibling.rotateLeft(this); sibling = n.sibling; } sibling.color = n.parent.color; n.parent.blacken(); if (n === n.parent.left) { sibling.right.blacken(); n.parent.rotateLeft(this); } else { sibling.left.blacken(); n.parent.rotateRight(this); } } } add (v) { if (v == null || v.id == null || v.id.constructor !== Array) { throw new Error("v is expected to have an id property which is an Array!"); } var node = new N(v); if (this.root !== null) { var p = this.root; // p abbrev. parent while (true) { if (smaller(node.val.id, p.val.id)) { if (p.left === null) { p.left = node; break; } else { p = p.left; } } else if (smaller(p.val.id, node.val.id)) { if (p.right === null) { p.right = node; break; } else { p = p.right; } } else { return false; } } this._fixInsert(node); } else { this.root = node; } this.length++; this.root.blacken(); } _fixInsert (n) { if (n.parent === null) { n.blacken(); return; } else if (n.parent.isBlack()) { return; } var uncle = n.getUncle(); if (uncle !== null && uncle.isRed()) { // Note: parent: red, uncle: red n.parent.blacken(); uncle.blacken(); n.grandparent.redden(); this._fixInsert(n.grandparent); } else { // Note: parent: red, uncle: black or null // Now we transform the tree in such a way that // either of these holds: // 1) grandparent.left.isRed // and grandparent.left.left.isRed // 2) grandparent.right.isRed // and grandparent.right.right.isRed if (n === n.parent.right && n.parent === n.grandparent.left) { n.parent.rotateLeft(this); // Since we rotated and want to use the previous // cases, we need to set n in such a way that // n.parent.isRed again n = n.left; } else if (n === n.parent.left && n.parent === n.grandparent.right) { n.parent.rotateRight(this); // see above n = n.right; } // Case 1) or 2) hold from here on. // Now traverse grandparent, make parent a black node // on the highest level which holds two red nodes. n.parent.blacken(); n.grandparent.redden(); if (n === n.parent.left) { // Case 1 n.grandparent.rotateRight(this); } else { // Case 2 n.grandparent.rotateLeft(this); } } } }