diff --git a/vendor/intervaltree/core.js b/vendor/intervaltree/core.js new file mode 100644 index 0000000000..bcc0f52a9b --- /dev/null +++ b/vendor/intervaltree/core.js @@ -0,0 +1,278 @@ +/** + * intervaltree + * + * @author Andrey Antukh , 2016 + * @license MIT License + */ +"use strict"; + +goog.provide("intervaltree.core"); +goog.provide("intervaltree.core.IntervalTree"); + +goog.require("goog.asserts"); +goog.require("goog.array"); + +goog.scope(function() { + const assert = goog.asserts.assert; + const every = goog.array.every; + + // --- Types Declaration + + class Interval { + constructor(start, end, data) { + this.start = start; + this.end = end; + this.data = data || null; + } + + toString() { + return `v=${this.start},${this.end} d=${this.data}`; + } + } + + class Node { + constructor(interval, parent) { + this.interval = interval; + this.parent = parent || null + this.left = null; + this.right = null; + this.max = -Infinity; + } + } + + class IntervalTree { + constructor() { + this.root = null; + } + + add(item) { + // Coerce to interval + const interval = makeInterval(item); + + // Set root if the current tree is empty + if (this.root === null) { + this.root = new Node(interval, null); + } else { + insertInterval(this.root, interval); + } + + return this; + } + + contains(point) { + assert(goog.isNumber(point)); + assert(this.root !== null); + return contains(this.root, point); + } + + search(item) { + assert(this.root !== null); + const interval = makeInterval(item); + return search(this.root, interval); + } + } + + // --- Private Api (Implementation) + + function insertInterval(root, interval) { + if (root.interval.start > interval.start) { + if (root.left !== null) { + insertInterval(root.left, interval); + } else { + root.left = new Node(interval, root); + recalculateMax(root.left); + } + } else { + if (root.right !== null) { + insertInterval(root.right, interval); + } else { + root.right = new Node(interval, root); + recalculateMax(root.right); + } + } + } + + function recalculateMax(node) { + const interval = node.interval; + const parent = node.parent; + + if (parent.max < interval.end) { + while (node) { + if (node.max < interval.end) { + node.max = interval.end; + } + node = node.parent; + } + } + } + + function contains(root, point) { + if (root.interval.start <= point && + root.interval.end >= point) { + return true; + } else { + let result = false; + + if (root.left && root.left.max >= point) { + result = result || contains(root.left, point); + } + + if (root.right && root.right.max >= point) { + result = result || contains(root.right, point); + } + + return result; + } + } + + function isIntervalIntersect(a, b) { + return ((a.start <= b.start && a.end >= b.start) || + (a.start <= b.end && a.end >= b.end) || + (b.start <= a.start && b.end >= a.start) || + (b.start <= a.end && b.end >= a.end)); + } + + function search(root, interval) { + console.log("1111"); + if (isIntervalIntersect(root.interval, interval)) { + return root.interval; + } else { + let result = null; + + if (root.left && root.left.max >= interval.start) { + result = result || search(root.left, interval); + } + + if (root.right && root.right.max >= interval.start) { + result = result || search(root.right, interval); + } + + return result; + } + } + + // --- Public Api + + function makeInterval(value) { + if (value instanceof Interval) { + return value + } else if (goog.isArray(value)) { + if (value.length === 1 && + goog.isNumber(value[0])) { + return new Interval(value[0], value[0]); + } else if (value.length === 2 && goog.isNumber(value[0])) { + if (goog.isNumber(value[1])) { + return new Interval(value[0], value[1]); + } else { + return new Interval(value[0], value[0], value[1]); + } + } else if (value.length === 3 && + goog.isNumber(value[0]) && + goog.isNumber(value[1])) { + return new Interval(value[0], value[1], value[2]); + } + } else if (arguments.length === 1 && goog.isNumber(value)) { + return new Interval(value, value); + } else if (arguments.length >= 2 && goog.isNumber(value)) { + if (goog.isNumber(arguments[1])) { + return new Interval(value, arguments[1], arguments[2]); + } else { + return new Interval(value, value, arguments[1]); + } + } else { + throw new Error("Unexpected input."); + } + }; + + function makeTree(items) { + const tree = new IntervalTree(); + + if (goog.isArrayLike(items)) { + for(let item of items) { + tree.add(item); + } + } + + return tree; + } + + const module = intervaltree.core; + + // Types + module.IntervalTree = IntervalTree; + module.Interval = Interval; + module.Node = Node; + + // Constructors + module.interval = makeInterval; + module.create = makeTree; + + // function getRandom(min, max) { + // var crypto = require("crypto"); + // var MAX_UINT32 = 0xFFFFFFFF; + // var range = max - min; + + // if (!(range <= MAX_UINT32)) { + // throw new Error( + // "Range of " + range + " covering " + min + " to " + max + " is > " + + // MAX_UINT32 + "."); + // } else if (min === max) { + // return min; + // } else if (!(max > min)) { + // throw new Error("max (" + max + ") must be >= min (" + min + ")."); + // } + + // // We need to cut off values greater than this to avoid bias in distribution + // // over the range. + // var maxUnbiased = MAX_UINT32 - ((MAX_UINT32 + 1) % (range + 1)); + + // var rand; + // do { + // rand = crypto.randomBytes(4).readUInt32LE(); + // // rand = crypto.randomBytes(new Uint32Array(1))[0]; + // } while (rand > maxUnbiased); + + // var offset = rand % (range + 1); + // return min + offset; + // } + + // function* randomSeq(n) { + // for(let i=0; i