/* global compareIds, copyObject */ 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 } } prev () { if (this.left !== null) { // search the most right node in the left tree var o = this.left while (o.right !== null) { o = o.right } return o } else { var p = this while (p.parent !== null && p !== p.parent.right) { 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 null } 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 } } } } findNodeWithUpperBound (to) { if (to === void 0) { throw new Error('You must define from!') } var o = this.root if (o === null) { return null } else { while (true) { if ((to === null || smaller(o.val.id, to)) && o.right !== null) { // o is included in the bound // try to find an element that is closer to the bound o = o.right } else if (to !== null && smaller(to, o.val.id)) { // o is not within the bound, maybe one of the left elements is.. if (o.left !== null) { o = o.left } else { // there is no left element. Search for the prev smaller element, // this should be within the bounds return o.prev() } } 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 } logTable (from = null, to = null) { var os = [] this.iterate(from, to, function (o) { var o_ = copyObject(o) var id = o_.id delete o_.id o_['id[0]'] = id[0] o_['id[1]'] = id[1] os.push(o_) }) console.table(os) } 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 null } } this._fixInsert(node) } else { this.root = node } this.length++ this.root.blacken() return node } _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) } } } }