"use strict" var bounds = require("binary-search-bounds") var NOT_FOUND = 0 var SUCCESS = 1 var EMPTY = 2 module.exports = createWrapper function IntervalTreeNode(mid, left, right, leftPoints, rightPoints) { this.mid = mid this.left = left this.right = right this.leftPoints = leftPoints this.rightPoints = rightPoints this.count = (left ? left.count : 0) + (right ? right.count : 0) + leftPoints.length } var proto = IntervalTreeNode.prototype function copy(a, b) { a.mid = b.mid a.left = b.left a.right = b.right a.leftPoints = b.leftPoints a.rightPoints = b.rightPoints a.count = b.count } function rebuild(node, intervals) { var ntree = createIntervalTree(intervals) node.mid = ntree.mid node.left = ntree.left node.right = ntree.right node.leftPoints = ntree.leftPoints node.rightPoints = ntree.rightPoints node.count = ntree.count } function rebuildWithInterval(node, interval) { var intervals = node.intervals([]) intervals.push(interval) rebuild(node, intervals) } function rebuildWithoutInterval(node, interval) { var intervals = node.intervals([]) var idx = intervals.indexOf(interval) if(idx < 0) { return NOT_FOUND } intervals.splice(idx, 1) rebuild(node, intervals) return SUCCESS } proto.intervals = function(result) { result.push.apply(result, this.leftPoints) if(this.left) { this.left.intervals(result) } if(this.right) { this.right.intervals(result) } return result } proto.insert = function(interval) { var weight = this.count - this.leftPoints.length this.count += 1 if(interval[1] < this.mid) { if(this.left) { if(4*(this.left.count+1) > 3*(weight+1)) { rebuildWithInterval(this, interval) } else { this.left.insert(interval) } } else { this.left = createIntervalTree([interval]) } } else if(interval[0] > this.mid) { if(this.right) { if(4*(this.right.count+1) > 3*(weight+1)) { rebuildWithInterval(this, interval) } else { this.right.insert(interval) } } else { this.right = createIntervalTree([interval]) } } else { var l = bounds.ge(this.leftPoints, interval, compareBegin) var r = bounds.ge(this.rightPoints, interval, compareEnd) this.leftPoints.splice(l, 0, interval) this.rightPoints.splice(r, 0, interval) } } proto.remove = function(interval) { var weight = this.count - this.leftPoints if(interval[1] < this.mid) { if(!this.left) { return NOT_FOUND } var rw = this.right ? this.right.count : 0 if(4 * rw > 3 * (weight-1)) { return rebuildWithoutInterval(this, interval) } var r = this.left.remove(interval) if(r === EMPTY) { this.left = null this.count -= 1 return SUCCESS } else if(r === SUCCESS) { this.count -= 1 } return r } else if(interval[0] > this.mid) { if(!this.right) { return NOT_FOUND } var lw = this.left ? this.left.count : 0 if(4 * lw > 3 * (weight-1)) { return rebuildWithoutInterval(this, interval) } var r = this.right.remove(interval) if(r === EMPTY) { this.right = null this.count -= 1 return SUCCESS } else if(r === SUCCESS) { this.count -= 1 } return r } else { if(this.count === 1) { if(this.leftPoints[0] === interval) { return EMPTY } else { return NOT_FOUND } } if(this.leftPoints.length === 1 && this.leftPoints[0] === interval) { if(this.left && this.right) { var p = this var n = this.left while(n.right) { p = n n = n.right } if(p === this) { n.right = this.right } else { var l = this.left var r = this.right p.count -= n.count p.right = n.left n.left = l n.right = r } copy(this, n) this.count = (this.left?this.left.count:0) + (this.right?this.right.count:0) + this.leftPoints.length } else if(this.left) { copy(this, this.left) } else { copy(this, this.right) } return SUCCESS } for(var l = bounds.ge(this.leftPoints, interval, compareBegin); l=0 && arr[i][1] >= lo; --i) { var r = cb(arr[i]) if(r) { return r } } } function reportRange(arr, cb) { for(var i=0; i this.mid) { if(this.right) { var r = this.right.queryPoint(x, cb) if(r) { return r } } return reportRightRange(this.rightPoints, x, cb) } else { return reportRange(this.leftPoints, cb) } } proto.queryInterval = function(lo, hi, cb) { if(lo < this.mid && this.left) { var r = this.left.queryInterval(lo, hi, cb) if(r) { return r } } if(hi > this.mid && this.right) { var r = this.right.queryInterval(lo, hi, cb) if(r) { return r } } if(hi < this.mid) { return reportLeftRange(this.leftPoints, hi, cb) } else if(lo > this.mid) { return reportRightRange(this.rightPoints, lo, cb) } else { return reportRange(this.leftPoints, cb) } } function compareNumbers(a, b) { return a - b } function compareBegin(a, b) { var d = a[0] - b[0] if(d) { return d } return a[1] - b[1] } function compareEnd(a, b) { var d = a[1] - b[1] if(d) { return d } return a[0] - b[0] } function createIntervalTree(intervals) { if(intervals.length === 0) { return null } var pts = [] for(var i=0; i>1] var leftIntervals = [] var rightIntervals = [] var centerIntervals = [] for(var i=0; i