flatten-js
Version:
Javascript library for 2d geometry
1 lines • 22.8 kB
JSON
{"dependencies":[{"name":"C:\\Users\\alexbol\\WebstormProjects\\flatten-js\\package.json","includedInParent":true,"mtime":1520238055570},{"name":"C:\\Users\\alexbol\\WebstormProjects\\flatten-js\\node_modules\\flatten-interval-tree\\package.json","includedInParent":true,"mtime":1520238156082},{"name":"./classes/node","loc":{"line":6,"column":19}},{"name":"./utils/constants","loc":{"line":7,"column":55}}],"generated":{"js":"/**\r\n * Created by Alex Bol on 3/31/2017.\r\n */\r\n'use strict';\r\n\r\nlet Node = require('./classes/node');\r\nlet {RB_TREE_COLOR_RED, RB_TREE_COLOR_BLACK} = require('./utils/constants');\r\n\r\nlet nil_node = new Node();\r\n\r\n/**\r\n * Implementation of interval binary search tree <br/>\r\n * Interval tree may store items which are couples of {key:interval, value: value} <br/>\r\n * Interval is an object with high and low properties or simply array of numeric [low,high] values <br />\r\n * If interval is an object, it should implement and expose methods less_than, equals_to, intersect and others,\r\n * see documentation {link}\r\n * @type {IntervalTree}\r\n */\r\nlet IntervalTree = class IntervalTree {\r\n /**\r\n * Construct new empty instance of IntervalTree\r\n */\r\n constructor() {\r\n this.root = null;\r\n }\r\n\r\n /**\r\n * Returns number of items stored in the interval tree\r\n * @returns {number}\r\n */\r\n get size() {\r\n let count = 0;\r\n this.tree_walk(this.root, () => count++);\r\n return count;\r\n }\r\n\r\n get keys() {\r\n let res = [];\r\n this.tree_walk(this.root, (node) => res.push(node.item.key.output()));\r\n return res;\r\n }\r\n\r\n get isEmpty() {\r\n return (this.root == null || this.root == nil_node);\r\n }\r\n\r\n /**\r\n * Insert new item into interval tree\r\n * @param key - interval object or array of two numbers [low, high]\r\n * @param value - value representing any object (optional)\r\n * @returns {Node} - returns reference to inserted node as an object {key:interval, value: value}\r\n */\r\n insert(key, value = key) {\r\n if (key === undefined) return;\r\n let insert_node = new Node(key, value, nil_node, nil_node, null, RB_TREE_COLOR_RED);\r\n this.tree_insert(insert_node);\r\n this.recalc_max(insert_node);\r\n return insert_node;\r\n }\r\n\r\n /**\r\n * Returns true if item {key,value} exist in the tree\r\n * @param key - interval correspondent to keys stored in the tree\r\n * @param value - value object to be checked\r\n * @returns {boolean} - true if item {key, value} exist in the tree, false otherwise\r\n */\r\n exist(key, value) {\r\n let search_node = new Node(key, value);\r\n return this.tree_search(this.root, search_node) ? true : false;\r\n }\r\n\r\n /**\r\n * Remove entry {key, value} from the tree\r\n * @param key - interval correspondent to keys stored in the tree\r\n * @param value - - value object\r\n * @returns {boolean} - true if item {key, value} deleted, false if not found\r\n */\r\n remove(key, value) {\r\n let search_node = new Node(key, value);\r\n let delete_node = this.tree_search(this.root, search_node);\r\n if (delete_node) {\r\n this.tree_delete(delete_node);\r\n }\r\n return delete_node;\r\n }\r\n\r\n /**\r\n * Returns array of entry values which keys intersect with given interval <br/>\r\n * If no values stored in the tree, returns array of keys which intersect given interval\r\n * @param interval - search interval, or array [low, high]\r\n * @returns {Array}\r\n */\r\n search(interval) {\r\n let search_node = new Node(interval);\r\n let resp_nodes = [];\r\n this.tree_search_interval(this.root, search_node, resp_nodes);\r\n let resp = [];\r\n resp_nodes.forEach((node) => {\r\n if (node.item.value) { // if there are values, return only values\r\n resp.push(node.item.value);\r\n }\r\n else { // otherwise, return keys\r\n resp.push(node.item.key.output());\r\n }\r\n }, []);\r\n return resp;\r\n }\r\n\r\n /**\r\n * Tree visitor. For each node implement a callback function. <br/>\r\n * Method calls a callback function with two parameters (key, value)\r\n * @param visitor(key,value) - function to be called for each tree item\r\n */\r\n forEach(visitor) {\r\n this.tree_walk(this.root, (node) => visitor(node.item.key, node.item.value));\r\n };\r\n\r\n recalc_max(node) {\r\n let node_current = node;\r\n while (node_current.parent != null) {\r\n node_current.parent.update_max();\r\n node_current = node_current.parent;\r\n }\r\n }\r\n\r\n tree_insert(insert_node) {\r\n let current_node = this.root;\r\n let parent_node = null;\r\n\r\n if (this.root == null || this.root == nil_node) {\r\n this.root = insert_node;\r\n }\r\n else {\r\n while (current_node != nil_node) {\r\n parent_node = current_node;\r\n if (insert_node.less_than(current_node)) {\r\n current_node = current_node.left;\r\n }\r\n else {\r\n current_node = current_node.right;\r\n }\r\n }\r\n\r\n insert_node.parent = parent_node;\r\n\r\n if (insert_node.less_than(parent_node)) {\r\n parent_node.left = insert_node;\r\n }\r\n else {\r\n parent_node.right = insert_node;\r\n }\r\n }\r\n\r\n this.insert_fixup(insert_node);\r\n }\r\n\r\n// After insertion insert_node may have red-colored parent, and this is a single possible violation\r\n// Go upwords to the root and re-color until violation will be resolved\r\n insert_fixup(insert_node) {\r\n let current_node;\r\n let uncle_node;\r\n\r\n current_node = insert_node;\r\n while (current_node != this.root && current_node.parent.color == RB_TREE_COLOR_RED) {\r\n if (current_node.parent == current_node.parent.parent.left) { // parent is left child of grandfather\r\n uncle_node = current_node.parent.parent.right; // right brother of parent\r\n if (uncle_node.color == RB_TREE_COLOR_RED) { // Case 1. Uncle is red\r\n // re-color father and uncle into black\r\n current_node.parent.color = RB_TREE_COLOR_BLACK;\r\n uncle_node.color = RB_TREE_COLOR_BLACK;\r\n current_node.parent.parent.color = RB_TREE_COLOR_RED;\r\n current_node = current_node.parent.parent;\r\n }\r\n else { // Case 2 & 3. Uncle is black\r\n if (current_node == current_node.parent.right) { // Case 2. Current if right child\r\n // This case is transformed into Case 3.\r\n current_node = current_node.parent;\r\n this.rotate_left(current_node);\r\n }\r\n current_node.parent.color = RB_TREE_COLOR_BLACK; // Case 3. Current is left child.\r\n // Re-color father and grandfather, rotate grandfather right\r\n current_node.parent.parent.color = RB_TREE_COLOR_RED;\r\n this.rotate_right(current_node.parent.parent);\r\n }\r\n }\r\n else { // parent is right child of grandfather\r\n uncle_node = current_node.parent.parent.left; // left brother of parent\r\n if (uncle_node.color == RB_TREE_COLOR_RED) { // Case 4. Uncle is red\r\n // re-color father and uncle into black\r\n current_node.parent.color = RB_TREE_COLOR_BLACK;\r\n uncle_node.color = RB_TREE_COLOR_BLACK;\r\n current_node.parent.parent.color = RB_TREE_COLOR_RED;\r\n current_node = current_node.parent.parent;\r\n }\r\n else {\r\n if (current_node == current_node.parent.left) { // Case 5. Current is left child\r\n // Transform into case 6\r\n current_node = current_node.parent;\r\n this.rotate_right(current_node);\r\n }\r\n current_node.parent.color = RB_TREE_COLOR_BLACK; // Case 6. Current is right child.\r\n // Re-color father and grandfather, rotate grandfather left\r\n current_node.parent.parent.color = RB_TREE_COLOR_RED;\r\n this.rotate_left(current_node.parent.parent);\r\n }\r\n }\r\n }\r\n\r\n this.root.color = RB_TREE_COLOR_BLACK;\r\n }\r\n\r\n tree_delete(delete_node) {\r\n let cut_node; // node to be cut - either delete_node or successor_node (\"y\" from 14.4)\r\n let fix_node; // node to fix rb tree property (\"x\" from 14.4)\r\n\r\n if (delete_node.left == nil_node || delete_node.right == nil_node) { // delete_node has less then 2 children\r\n cut_node = delete_node;\r\n }\r\n else { // delete_node has 2 children\r\n cut_node = this.tree_successor(delete_node);\r\n }\r\n\r\n // fix_node if single child of cut_node\r\n if (cut_node.left != nil_node) {\r\n fix_node = cut_node.left;\r\n }\r\n else {\r\n fix_node = cut_node.right;\r\n }\r\n\r\n // remove cut_node from parent\r\n if (fix_node != nil_node) {\r\n fix_node.parent = cut_node.parent;\r\n }\r\n\r\n if (cut_node == this.root) {\r\n this.root = fix_node;\r\n }\r\n else {\r\n if (cut_node == cut_node.parent.left) {\r\n cut_node.parent.left = fix_node;\r\n }\r\n else {\r\n cut_node.parent.right = fix_node;\r\n }\r\n cut_node.parent.update_max(); // update max property of the parent\r\n }\r\n\r\n this.recalc_max(fix_node); // update max property upward from fix_node to root\r\n\r\n // COPY DATA !!!\r\n // Delete_node becomes cut_node, it means that we cannot hold reference\r\n // to node in outer structure and we will have to delete by key, additional search need\r\n if (cut_node != delete_node) {\r\n delete_node.copy_data(cut_node);\r\n delete_node.update_max(); // update max property of the cut node at the new place\r\n this.recalc_max(delete_node); // update max property upward from delete_node to root\r\n }\r\n\r\n if (fix_node != nil_node && cut_node.color == RB_TREE_COLOR_BLACK) {\r\n this.delete_fixup(fix_node);\r\n }\r\n }\r\n\r\n delete_fixup(fix_node) {\r\n let current_node = fix_node;\r\n let brother_node;\r\n\r\n while (current_node != this.root && current_node.parent != null && current_node.color == RB_TREE_COLOR_BLACK) {\r\n if (current_node == current_node.parent.left) { // fix node is left child\r\n brother_node = current_node.parent.right;\r\n if (brother_node.color == RB_TREE_COLOR_RED) { // Case 1. Brother is red\r\n brother_node.color = RB_TREE_COLOR_BLACK; // re-color brother\r\n current_node.parent.color = RB_TREE_COLOR_RED; // re-color father\r\n this.rotate_left(current_node.parent);\r\n brother_node = current_node.parent.right; // update brother\r\n }\r\n // Derive to cases 2..4: brother is black\r\n if (brother_node.left.color == RB_TREE_COLOR_BLACK &&\r\n brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 2: both nephews black\r\n brother_node.color = RB_TREE_COLOR_RED; // re-color brother\r\n current_node = current_node.parent; // continue iteration\r\n }\r\n else {\r\n if (brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 3: left nephew red, right nephew black\r\n brother_node.color = RB_TREE_COLOR_RED; // re-color brother\r\n brother_node.left.color = RB_TREE_COLOR_BLACK; // re-color nephew\r\n this.rotate_right(brother_node);\r\n brother_node = current_node.parent.right; // update brother\r\n // Derive to case 4: left nephew black, right nephew red\r\n }\r\n // case 4: left nephew black, right nephew red\r\n brother_node.color = current_node.parent.color;\r\n current_node.parent.color = RB_TREE_COLOR_BLACK;\r\n brother_node.right.color = RB_TREE_COLOR_BLACK;\r\n this.rotate_left(current_node.parent);\r\n current_node = this.root; // exit from loop\r\n }\r\n }\r\n else { // fix node is right child\r\n brother_node = current_node.parent.left;\r\n if (brother_node.color == RB_TREE_COLOR_RED) { // Case 1. Brother is red\r\n brother_node.color = RB_TREE_COLOR_BLACK; // re-color brother\r\n current_node.parent.color = RB_TREE_COLOR_RED; // re-color father\r\n this.rotate_right(current_node.parent);\r\n brother_node = current_node.parent.left; // update brother\r\n }\r\n // Go to cases 2..4\r\n if (brother_node.left.color == RB_TREE_COLOR_BLACK &&\r\n brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 2\r\n brother_node.color = RB_TREE_COLOR_RED; // re-color brother\r\n current_node = current_node.parent; // continue iteration\r\n }\r\n else {\r\n if (brother_node.left.color == RB_TREE_COLOR_BLACK) { // case 3: right nephew red, left nephew black\r\n brother_node.color = RB_TREE_COLOR_RED; // re-color brother\r\n brother_node.right.color = RB_TREE_COLOR_BLACK; // re-color nephew\r\n this.rotate_left(brother_node);\r\n brother_node = current_node.parent.left; // update brother\r\n // Derive to case 4: right nephew black, left nephew red\r\n }\r\n // case 4: right nephew black, left nephew red\r\n brother_node.color = current_node.parent.color;\r\n current_node.parent.color = RB_TREE_COLOR_BLACK;\r\n brother_node.left.color = RB_TREE_COLOR_BLACK;\r\n this.rotate_right(current_node.parent);\r\n current_node = this.root; // force exit from loop\r\n }\r\n }\r\n }\r\n\r\n current_node.color = RB_TREE_COLOR_BLACK;\r\n }\r\n\r\n tree_search(node, search_node) {\r\n if (node == null || node == nil_node)\r\n return undefined;\r\n\r\n if (search_node.equal_to(node)) {\r\n return node;\r\n }\r\n if (search_node.less_than(node)) {\r\n return this.tree_search(node.left, search_node);\r\n }\r\n else {\r\n return this.tree_search(node.right, search_node);\r\n }\r\n }\r\n\r\n // Original search_interval method; container res support push() insertion\r\n // Search all intervals intersecting given one\r\n tree_search_interval(node, search_node, res) {\r\n if (node != null && node != nil_node) {\r\n // if (node->left != nil_node && node->left->max >= low) {\r\n if (node.left != nil_node && !node.not_intersect_left_subtree(search_node)) {\r\n this.tree_search_interval(node.left, search_node, res);\r\n }\r\n // if (low <= node->high && node->low <= high) {\r\n if (node.intersect(search_node)) {\r\n res.push(node);\r\n }\r\n // if (node->right != nil_node && node->low <= high) {\r\n if (node.right != nil_node && !node.not_intersect_right_subtree(search_node)) {\r\n this.tree_search_interval(node.right, search_node, res);\r\n }\r\n }\r\n }\r\n\r\n local_minimum(node) {\r\n let node_min = node;\r\n while (node_min.left != null && node_min.left != nil_node) {\r\n node_min = node_min.left;\r\n }\r\n return node_min;\r\n }\r\n\r\n // not in use\r\n local_maximum(node) {\r\n let node_max = node;\r\n while (node_max.right != null && node_max.right != nil_node) {\r\n node_max = node_max.right;\r\n }\r\n return node_max;\r\n }\r\n\r\n tree_successor(node) {\r\n let node_successor;\r\n let current_node;\r\n let parent_node;\r\n\r\n if (node.right != nil_node) {\r\n node_successor = this.local_minimum(node.right);\r\n }\r\n else {\r\n current_node = node;\r\n parent_node = node.parent;\r\n while (parent_node != null && parent_node.right == current_node) {\r\n current_node = parent_node;\r\n parent_node = parent_node.parent;\r\n }\r\n node_successor = parent_node;\r\n }\r\n return node_successor;\r\n }\r\n\r\n // | right-rotate(T,y) |\r\n // y ---------------. x\r\n // / \\ / \\\r\n // x c left-rotate(T,x) a y\r\n // / \\ <--------------- / \\\r\n // a b b c\r\n\r\n rotate_left(x) {\r\n let y = x.right;\r\n\r\n x.right = y.left; // b goes to x.right\r\n\r\n if (y.left != nil_node) {\r\n y.left.parent = x; // x becomes parent of b\r\n }\r\n y.parent = x.parent; // move parent\r\n\r\n if (x == this.root) {\r\n this.root = y; // y becomes root\r\n }\r\n else { // y becomes child of x.parent\r\n if (x == x.parent.left) {\r\n x.parent.left = y;\r\n }\r\n else {\r\n x.parent.right = y;\r\n }\r\n }\r\n y.left = x; // x becomes left child of y\r\n x.parent = y; // and y becomes parent of x\r\n\r\n if (x != null && x != nil_node) {\r\n x.update_max();\r\n }\r\n\r\n y = x.parent;\r\n if (y != null && y != nil_node) {\r\n y.update_max();\r\n }\r\n }\r\n\r\n rotate_right(y) {\r\n let x = y.left;\r\n\r\n y.left = x.right; // b goes to y.left\r\n\r\n if (x.right != nil_node) {\r\n x.right.parent = y; // y becomes parent of b\r\n }\r\n x.parent = y.parent; // move parent\r\n\r\n if (y == this.root) { // x becomes root\r\n this.root = x;\r\n }\r\n else { // y becomes child of x.parent\r\n if (y == y.parent.left) {\r\n y.parent.left = x;\r\n }\r\n else {\r\n y.parent.right = x;\r\n }\r\n }\r\n x.right = y; // y becomes right child of x\r\n y.parent = x; // and x becomes parent of y\r\n\r\n if (y != null && y != nil_node) {\r\n y.update_max();\r\n }\r\n\r\n x = y.parent;\r\n if (x != null && x != nil_node) {\r\n x.update_max();\r\n }\r\n }\r\n\r\n tree_walk(node, action) {\r\n if (node != null && node != nil_node) {\r\n this.tree_walk(node.left, action);\r\n // arr.push(node.toArray());\r\n action(node);\r\n this.tree_walk(node.right, action);\r\n }\r\n }\r\n\r\n /* Return true if all red nodes have exactly two black child nodes */\r\n testRedBlackProperty() {\r\n let res = true;\r\n this.tree_walk(this.root, function (node) {\r\n if (node.color == RB_TREE_COLOR_RED) {\r\n if (!(node.left.color == RB_TREE_COLOR_BLACK && node.right.color == RB_TREE_COLOR_BLACK)) {\r\n res = false;\r\n }\r\n }\r\n });\r\n return res;\r\n }\r\n\r\n /* Throw error if not every path from root to bottom has same black height */\r\n testBlackHeightProperty(node) {\r\n let height = 0;\r\n let heightLeft = 0;\r\n let heightRight = 0;\r\n if (node.color == RB_TREE_COLOR_BLACK) {\r\n height++;\r\n }\r\n if (node.left != nil_node) {\r\n heightLeft = this.testBlackHeightProperty(node.left);\r\n }\r\n else {\r\n heightLeft = 1;\r\n }\r\n if (node.right != nil_node) {\r\n heightRight = this.testBlackHeightProperty(node.right);\r\n }\r\n else {\r\n heightRight = 1;\r\n }\r\n if (heightLeft != heightRight) {\r\n throw new Error('Red-black height property violated');\r\n }\r\n height += heightLeft;\r\n return height;\r\n };\r\n};\r\n\r\nmodule.exports = IntervalTree;\r\n"},"hash":"0f0a81c7e12a319ba12e3a269e99caef","cacheData":{"env":{}}}