From dcb2e9fb10eee65b355f76181e90fc2c64b9a81a Mon Sep 17 00:00:00 2001 From: Danny Yoo Date: Mon, 7 Nov 2011 15:46:22 -0500 Subject: [PATCH] ripping out the avltree stuff with the llrbtree implementation, which we'll use to get immutable hash tables. --- Makefile | 2 +- cs019/deviations.txt | 2 - js-assembler/get-runtime.rkt | 2 +- js-assembler/runtime-src/avltree.js | 776 --------------------------- js-assembler/runtime-src/llrbtree.js | 539 +++++++++++++++++++ version.rkt | 2 +- 6 files changed, 542 insertions(+), 781 deletions(-) delete mode 100644 js-assembler/runtime-src/avltree.js create mode 100644 js-assembler/runtime-src/llrbtree.js diff --git a/Makefile b/Makefile index 2468181..f5ef195 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ test-earley: test-conform: racket tests/test-conform.rkt -test-more: +test-more: bump-version racket tests/run-more-tests.rkt doc: diff --git a/cs019/deviations.txt b/cs019/deviations.txt index 5840838..9c10882 100644 --- a/cs019/deviations.txt +++ b/cs019/deviations.txt @@ -1,7 +1,5 @@ Misc === -current-milliseconds -current-seconds string-copy exit sleep diff --git a/js-assembler/get-runtime.rkt b/js-assembler/get-runtime.rkt index 897c8bd..1cf5a4a 100644 --- a/js-assembler/get-runtime.rkt +++ b/js-assembler/get-runtime.rkt @@ -59,7 +59,7 @@ hashes-header.js jshashtable-2.1_src.js - avltree.js + llrbtree.js baselib-hashes.js hashes-footer.js diff --git a/js-assembler/runtime-src/avltree.js b/js-assembler/runtime-src/avltree.js deleted file mode 100644 index 40a597d..0000000 --- a/js-assembler/runtime-src/avltree.js +++ /dev/null @@ -1,776 +0,0 @@ -// ---------------------------------------------------------------------- -// dyoo: the following code comes from the Google Closure Library. I've done -// edits to flatten the namespace from goog.structs to just -// AvlTree, commented out inorderTraverse and reverseOrderTraverse. -// -// ---------------------------------------------------------------------- -// Original license follows: -// ---------------------------------------------------------------------- - - -// Copyright 2007 The Closure Library Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * @fileoverview Datastructure: AvlTree. - * - * - * This file provides the implementation of an AVL-Tree datastructure. The tree - * maintains a set of unique values in a sorted order. The values can be - * accessed efficiently in their sorted order since the tree enforces an O(logn) - * maximum height. See http://en.wikipedia.org/wiki/Avl_tree for more detail. - * - * The big-O notation for all operations are below: - *
- *   Method                 big-O
- * ----------------------------------------------------------------------------
- * - add                    O(logn)
- * - remove                 O(logn)
- * - clear                  O(1)
- * - contains               O(logn)
- * - getCount               O(1)
- * - getMinimum             O(1), or O(logn) when optional root is specified
- * - getMaximum             O(1), or O(logn) when optional root is specified
- * - getHeight              O(1)
- * - getValues              O(n)
- * - inOrderTraverse        O(logn + k), where k is number of traversed nodes
- * - reverseOrderTraverse   O(logn + k), where k is number of traversed nodes
- * 
- */ - - - -/** - * Constructs an AVL-Tree, which uses the specified comparator to order its - * values. The values can be accessed efficiently in their sorted order since - * the tree enforces a O(logn) maximum height. - * - * @param {Function=} opt_comparator Function used to order the tree's nodes. - * @constructor - * @implements {Collection} - */ -var AvlTree = function(opt_comparator) { - this.comparator_ = opt_comparator || - AvlTree.DEFAULT_COMPARATOR_; -}; - - -/** - * String comparison function used to compare values in the tree. This function - * is used by default if no comparator is specified in the tree's constructor. - * - * @param {string} a The first string. - * @param {string} b The second string. - * @return {number} -1 if a < b, 1 if a > b, 0 if a = b. - * @private - */ -AvlTree.DEFAULT_COMPARATOR_ = function(a, b) { - if (String(a) < String(b)) { - return -1; - } else if (String(a) > String(b)) { - return 1; - } - return 0; -}; - - -/** - * Pointer to the root node of the tree. - * - * @type {AvlTree.Node} - * @private - */ -AvlTree.prototype.root_ = null; - - -/** - * Comparison function used to compare values in the tree. This function should - * take two values, a and b, and return x where: - *
- *  x < 0 if a < b,
- *  x > 0 if a > b,
- *  x = 0 otherwise
- * 
- * - * @type {Function} - * @private - */ -AvlTree.prototype.comparator_ = null; - - -/** - * Pointer to the node with the smallest value in the tree. - * - * @type {AvlTree.Node} - * @private - */ -AvlTree.prototype.minNode_ = null; - - -/** - * Pointer to the node with the largest value in the tree. - * - * @type {AvlTree.Node} - * @private - */ -AvlTree.prototype.maxNode_ = null; - - -/** - * Keeps track of the number of nodes in the tree. - * - * @type {number} - * @private - */ -AvlTree.prototype.count_ = 0; - - -/** - * Inserts a node into the tree with the specified value if the tree does - * not already contain a node with the specified value. If the value is - * inserted, the tree is balanced to enforce the AVL-Tree height property. - * - * @param {*} value Value to insert into the tree. - * @return {boolean} Whether value was inserted into the tree. - */ -AvlTree.prototype.add = function(value) { - // If the tree is empty, create a root node with the specified value - if (this.root_ == null) { - this.root_ = new AvlTree.Node(value); - this.minNode_ = this.root_; - this.maxNode_ = this.root_; - this.count_ = 1; - return true; - } - - // Assume a node is not added and change status when one is - var retStatus = false; - - // Depth traverse the tree and insert the value if we reach a null node - this.traverse_(function(node) { - var retNode = null; - if (this.comparator_(node.value, value) > 0) { - retNode = node.left; - if (node.left == null) { - var newNode = new AvlTree.Node(value, node); - node.left = newNode; - if (node == this.minNode_) { - this.minNode_ = newNode; - } - retStatus = true; // Value was added to tree - this.balance_(node); // Maintain the AVL-tree balance - } - } else if (this.comparator_(node.value, value) < 0) { - retNode = node.right; - if (node.right == null) { - var newNode = new AvlTree.Node(value, node); - node.right = newNode; - if (node == this.maxNode_) { - this.maxNode_ = newNode; - } - retStatus = true; // Value was added to tree - this.balance_(node); // Maintain the AVL-tree balance - } - } - return retNode; // If null, we'll stop traversing the tree - }); - - // If a node was added, increment count - if (retStatus) { - this.count_ += 1; - } - - // Return true if a node was added, false otherwise - return retStatus; -}; - - -/** - * Removes a node from the tree with the specified value if the tree contains a - * node with this value. If a node is removed the tree is balanced to enforce - * the AVL-Tree height property. The value of the removed node is returned. - * - * @param {*} value Value to find and remove from the tree. - * @return {*} The value of the removed node or null if the value was not in - * the tree. - */ -AvlTree.prototype.remove = function(value) { - // Assume the value is not removed and set the value when it is removed - var retValue = null; - - // Depth traverse the tree and remove the value if we find it - this.traverse_(function(node) { - var retNode = null; - if (this.comparator_(node.value, value) > 0) { - retNode = node.left; - } else if (this.comparator_(node.value, value) < 0) { - retNode = node.right; - } else { - retValue = node.value; - this.removeNode_(node); - } - return retNode; // If null, we'll stop traversing the tree - }); - - // If a node was removed, decrement count. - if (retValue) { - // Had traverse_() cleared the tree, set to 0. - this.count_ = this.root_ ? this.count_ - 1 : 0; - } - - // Return the value that was removed, null if the value was not in the tree - return retValue; -}; - - -/** - * Removes all nodes from the tree. - */ -AvlTree.prototype.clear = function() { - this.root_ = null; - this.minNode_ = null; - this.maxNode_ = null; - this.count_ = 0; -}; - - -/** - * Returns true if the tree contains a node with the specified value, false - * otherwise. - * - * @param {*} value Value to find in the tree. - * @return {boolean} Whether the tree contains a node with the specified value. - */ -AvlTree.prototype.contains = function(value) { - // Assume the value is not in the tree and set this value if it is found - var isContained = false; - - // Depth traverse the tree and set isContained if we find the node - this.traverse_(function(node) { - var retNode = null; - if (this.comparator_(node.value, value) > 0) { - retNode = node.left; - } else if (this.comparator_(node.value, value) < 0) { - retNode = node.right; - } else { - isContained = true; - } - return retNode; // If null, we'll stop traversing the tree - }); - - // Return true if the value is contained in the tree, false otherwise - return isContained; -}; - - -/** - * Returns the number of values stored in the tree. - * - * @return {number} The number of values stored in the tree. - */ -AvlTree.prototype.getCount = function() { - return this.count_; -}; - - -/** - * Returns the value u, such that u is contained in the tree and u < v, for all - * values v in the tree where v != u. - * - * @return {*} The minimum value contained in the tree. - */ -AvlTree.prototype.getMinimum = function() { - return this.getMinNode_().value; -}; - - -/** - * Returns the value u, such that u is contained in the tree and u > v, for all - * values v in the tree where v != u. - * - * @return {*} The maximum value contained in the tree. - */ -AvlTree.prototype.getMaximum = function() { - return this.getMaxNode_().value; -}; - - -/** - * Returns the height of the tree (the maximum depth). This height should - * always be <= 1.4405*(Math.log(n+2)/Math.log(2))-1.3277, where n is the - * number of nodes in the tree. - * - * @return {number} The height of the tree. - */ -AvlTree.prototype.getHeight = function() { - return this.root_ ? this.root_.height : 0; -}; - - -/** - * Inserts the values stored in the tree into a new Array and returns the Array. - * - * @return {Array} An array containing all of the trees values in sorted order. - */ -AvlTree.prototype.getValues = function() { - var ret = []; - this.inOrderTraverse(function(value) { - ret.push(value); - }); - return ret; -}; - - -/** - * Performs an in-order traversal of the tree and calls {@code func} with each - * traversed node, optionally starting from the smallest node with a value >= to - * the specified start value. The traversal ends after traversing the tree's - * maximum node or when {@code func} returns a value that evaluates to true. - * - * @param {Function} func Function to call on each traversed node. - * @param {Object=} opt_startValue If specified, traversal will begin on the - * node with the smallest value >= opt_startValue. - */ -AvlTree.prototype.inOrderTraverse = - function(func, opt_startValue) { - // If our tree is empty, return immediately - if (!this.root_) { - return; - } - - // Depth traverse the tree to find node to begin in-order traversal from - var startNode; - if (opt_startValue) { - this.traverse_(function(node) { - var retNode = null; - if (this.comparator_(node.value, opt_startValue) > 0) { - retNode = node.left; - startNode = node; - } else if (this.comparator_(node.value, opt_startValue) < 0) { - retNode = node.right; - } else { - startNode = node; - } - return retNode; // If null, we'll stop traversing the tree - }); - } else { - startNode = this.getMinNode_(); - } - - // Traverse the tree and call func on each traversed node's value - var node = startNode, prev = startNode.left ? startNode.left : startNode; - while (node != null) { - if (node.left != null && node.left != prev && node.right != prev) { - node = node.left; - } else { - if (node.right != prev) { - if (func(node.value)) { - return; - } - } - var temp = node; - node = node.right != null && node.right != prev ? - node.right : - node.parent; - prev = temp; - } - } - }; - - -// /** -// * Performs a reverse-order traversal of the tree and calls {@code func} with -// * each traversed node, optionally starting from the largest node with a value -// * <= to the specified start value. The traversal ends after traversing the -// * tree's minimum node or when func returns a value that evaluates to true. -// * -// * @param {Function} func Function to call on each traversed node. -// * @param {Object=} opt_startValue If specified, traversal will begin on the -// * node with the largest value <= opt_startValue. -// */ -// AvlTree.prototype.reverseOrderTraverse = -// function(func, opt_startValue) { -// // If our tree is empty, return immediately -// if (!this.root_) { -// return; -// } - -// // Depth traverse the tree to find node to begin reverse-order traversal from -// var startNode; -// if (opt_startValue) { -// this.traverse_(goog.bind(function(node) { -// var retNode = null; -// if (this.comparator_(node.value, opt_startValue) > 0) { -// retNode = node.left; -// } else if (this.comparator_(node.value, opt_startValue) < 0) { -// retNode = node.right; -// startNode = node; -// } else { -// startNode = node; -// } -// return retNode; // If null, we'll stop traversing the tree -// }, this)); -// } else { -// startNode = this.getMaxNode_(); -// } - -// // Traverse the tree and call func on each traversed node's value -// var node = startNode, prev = startNode.right ? startNode.right : startNode; -// while (node != null) { -// if (node.right != null && node.right != prev && node.left != prev) { -// node = node.right; -// } else { -// if (node.left != prev) { -// if (func(node.value)) { -// return; -// } -// } -// var temp = node; -// node = node.left != null && node.left != prev ? -// node.left : -// node.parent; -// prev = temp; -// } -// } -// }; - - -/** - * Performs a traversal defined by the supplied {@code traversalFunc}. The first - * call to {@code traversalFunc} is passed the root or the optionally specified - * startNode. After that, calls {@code traversalFunc} with the node returned - * by the previous call to {@code traversalFunc} until {@code traversalFunc} - * returns null or the optionally specified endNode. The first call to - * traversalFunc is passed the root or the optionally specified startNode. - * - * @param {Function} traversalFunc Function used to traverse the tree. Takes a - * node as a parameter and returns a node. - * @param {AvlTree.Node=} opt_startNode The node at which the - * traversal begins. - * @param {AvlTree.Node=} opt_endNode The node at which the - * traversal ends. - * @private - */ -AvlTree.prototype.traverse_ = - function(traversalFunc, opt_startNode, opt_endNode) { - var node = opt_startNode ? opt_startNode : this.root_; - var endNode = opt_endNode ? opt_endNode : null; - while (node && node != endNode) { - node = traversalFunc.call(this, node); - } - }; - - - -/** - * Ensures that the specified node and all its ancestors are balanced. If they - * are not, performs left and right tree rotations to achieve a balanced - * tree. This method assumes that at most 2 rotations are necessary to balance - * the tree (which is true for AVL-trees that are balanced after each node is - * added or removed). - * - * @param {AvlTree.Node} node Node to begin balance from. - * @private - */ -AvlTree.prototype.balance_ = function(node) { - - this.traverse_(function(node) { - // Calculate the left and right node's heights - var lh = node.left ? node.left.height : 0; - var rh = node.right ? node.right.height : 0; - - // Rotate tree rooted at this node if it is not AVL-tree balanced - if (lh - rh > 1) { - if (node.left.right && (!node.left.left || - node.left.left.height < node.left.right.height)) { - this.leftRotate_(node.left); - } - this.rightRotate_(node); - } else if (rh - lh > 1) { - if (node.right.left && (!node.right.right || - node.right.right.height < node.right.left.height)) { - this.rightRotate_(node.right); - } - this.leftRotate_(node); - } - - // Recalculate the left and right node's heights - lh = node.left ? node.left.height : 0; - rh = node.right ? node.right.height : 0; - - // Set this node's height - node.height = Math.max(lh, rh) + 1; - - // Traverse up tree and balance parent - return node.parent; - }, node); - -}; - - -/** - * Performs a left tree rotation on the specified node. - * - * @param {AvlTree.Node} node Pivot node to rotate from. - * @private - */ -AvlTree.prototype.leftRotate_ = function(node) { - // Re-assign parent-child references for the parent of the node being removed - if (node.isLeftChild()) { - node.parent.left = node.right; - node.right.parent = node.parent; - } else if (node.isRightChild()) { - node.parent.right = node.right; - node.right.parent = node.parent; - } else { - this.root_ = node.right; - this.root_.parent = null; - } - - // Re-assign parent-child references for the child of the node being removed - var temp = node.right; - node.right = node.right.left; - if (node.right != null) node.right.parent = node; - temp.left = node; - node.parent = temp; -}; - - -/** - * Performs a right tree rotation on the specified node. - * - * @param {AvlTree.Node} node Pivot node to rotate from. - * @private - */ -AvlTree.prototype.rightRotate_ = function(node) { - // Re-assign parent-child references for the parent of the node being removed - if (node.isLeftChild()) { - node.parent.left = node.left; - node.left.parent = node.parent; - } else if (node.isRightChild()) { - node.parent.right = node.left; - node.left.parent = node.parent; - } else { - this.root_ = node.left; - this.root_.parent = null; - } - - // Re-assign parent-child references for the child of the node being removed - var temp = node.left; - node.left = node.left.right; - if (node.left != null) node.left.parent = node; - temp.right = node; - node.parent = temp; -}; - - -/** - * Removes the specified node from the tree and ensures the tree still - * maintains the AVL-tree balance. - * - * @param {AvlTree.Node} node The node to be removed. - * @private - */ -AvlTree.prototype.removeNode_ = function(node) { - // Perform normal binary tree node removal, but balance the tree, starting - // from where we removed the node - if (node.left != null || node.right != null) { - var b = null; // Node to begin balance from - var r; // Node to replace the node being removed - if (node.left != null) { - r = this.getMaxNode_(node.left); - if (r != node.left) { - r.parent.right = r.left; - if (r.left) r.left.parent = r.parent; - r.left = node.left; - r.left.parent = r; - b = r.parent; - } - r.parent = node.parent; - r.right = node.right; - if (r.right) r.right.parent = r; - if (node == this.maxNode_) this.maxNode_ = r; - } else { - r = this.getMinNode_(node.right); - if (r != node.right) { - r.parent.left = r.right; - if (r.right) r.right.parent = r.parent; - r.right = node.right; - r.right.parent = r; - b = r.parent; - } - r.parent = node.parent; - r.left = node.left; - if (r.left) r.left.parent = r; - if (node == this.minNode_) this.minNode_ = r; - } - - // Update the parent of the node being removed to point to its replace - if (node.isLeftChild()) { - node.parent.left = r; - } else if (node.isRightChild()) { - node.parent.right = r; - } else { - this.root_ = r; - } - - // Balance the tree - this.balance_(b ? b : r); - } else { - // If the node is a leaf, remove it and balance starting from its parent - if (node.isLeftChild()) { - this.special = 1; - node.parent.left = null; - if (node == this.minNode_) this.minNode_ = node.parent; - this.balance_(node.parent); - } else if (node.isRightChild()) { - node.parent.right = null; - if (node == this.maxNode_) this.maxNode_ = node.parent; - this.balance_(node.parent); - } else { - this.clear(); - } - } -}; - - -/** - * Returns the node with the smallest value in tree, optionally rooted at - * {@code opt_rootNode}. - * - * @param {AvlTree.Node=} opt_rootNode Optional root node. - * @return {AvlTree.Node} The node with the smallest value in - * the tree. - * @private - */ -AvlTree.prototype.getMinNode_ = function(opt_rootNode) { - if (!opt_rootNode) { - return this.minNode_; - } - - var minNode = opt_rootNode; - this.traverse_(function(node) { - var retNode = null; - if (node.left) { - minNode = node.left; - retNode = node.left; - } - return retNode; // If null, we'll stop traversing the tree - }, opt_rootNode); - - return minNode; -}; - - -/** - * Returns the node with the largest value in tree, optionally rooted at - * opt_rootNode. - * - * @param {AvlTree.Node=} opt_rootNode Optional root node. - * @return {AvlTree.Node} The node with the largest value in - * the tree. - * @private - */ -AvlTree.prototype.getMaxNode_ = function(opt_rootNode) { - if (!opt_rootNode) { - return this.maxNode_; - } - - var maxNode = opt_rootNode; - this.traverse_(function(node) { - var retNode = null; - if (node.right) { - maxNode = node.right; - retNode = node.right; - } - return retNode; // If null, we'll stop traversing the tree - }, opt_rootNode); - - return maxNode; -}; - - - -/** - * Constructs an AVL-Tree node with the specified value. If no parent is - * specified, the node's parent is assumed to be null. The node's height - * defaults to 1 and its children default to null. - * - * @param {*} value Value to store in the node. - * @param {AvlTree.Node=} opt_parent Optional parent node. - * @constructor - */ -AvlTree.Node = function(value, opt_parent) { - /** - * The value stored by the node. - * - * @type {*} - */ - this.value = value; - - /** - * The node's parent. Null if the node is the root. - * - * @type {AvlTree.Node} - */ - this.parent = opt_parent ? opt_parent : null; -}; - - -/** - * The node's left child. Null if the node does not have a left child. - * - * @type {AvlTree.Node?} - */ -AvlTree.Node.prototype.left = null; - - -/** - * The node's right child. Null if the node does not have a right child. - * - * @type {AvlTree.Node?} - */ -AvlTree.Node.prototype.right = null; - - -/** - * The height of the tree rooted at this node. - * - * @type {number} - */ -AvlTree.Node.prototype.height = 1; - - -/** - * Returns true iff the specified node has a parent and is the right child of - * its parent. - * - * @return {boolean} Whether the specified node has a parent and is the right - * child of its parent. - */ -AvlTree.Node.prototype.isRightChild = function() { - return !!this.parent && this.parent.right == this; -}; - - -/** - * Returns true iff the specified node has a parent and is the left child of - * its parent. - * - * @return {boolean} Whether the specified node has a parent and is the left - * child of its parent. - */ -AvlTree.Node.prototype.isLeftChild = function() { - return !!this.parent && this.parent.left == this; -}; \ No newline at end of file diff --git a/js-assembler/runtime-src/llrbtree.js b/js-assembler/runtime-src/llrbtree.js new file mode 100644 index 0000000..065f946 --- /dev/null +++ b/js-assembler/runtime-src/llrbtree.js @@ -0,0 +1,539 @@ +/*jslint plusplus: true, vars: true, white: true, nomen: true, maxerr: 50, indent: 4 */ + +var LLRBTree = {}; + +// The code basically follows the structure of +// https://github.com/kazu-yamamoto/llrbtree +// +// Mostly comes from the code in: +// +// https://github.com/kazu-yamamoto/llrbtree/blob/master/Data/RBTree/LL.hs +// +// as well as: +// +// https://github.com/kazu-yamamoto/llrbtree/blob/master/Data/RBTree/Internal.hs + +(function() { + 'use strict'; + + // function declarations + var turnR, turnB; + var insert_, balanceL, balanceR, replaceX, remove_; + var removeLT, removeGT, removeEQ; + var isRed, isBlack, isBlackLeftBlack, isBlackLeftRed; + var hardMin; + var minimum, removeMin_; + + + // red and black colors. + var R = "R", B = "B"; + + // An rbtree is either a Leaf or a Node. + + var Node = function(c, //h, + l, x, r) { + this.c = c; // color: (U R B) + //this.h = h; // height: int + this.l = l; // left: rbtree + this.x = x; // x : element + this.r = r; // right: rbtree + }; + + + + var Leaf = function() {}; + // WARNING: DO NOT CONSTRUCT ANY OTHER INSTANCES OF LEAF, OR BAD + // THINGS WILL HAPPEN. + var EMPTY = new Leaf(); + + + + + + + var items_ = function(tree, elts) { + if (tree === EMPTY) { return; } + items_(tree.l, elts); + elts.push(tree.x); + items_(tree.r, elts); + }; + + var items = function(tree) { + var elts = []; + items_(tree, elts); + return elts; + }; + + + + // Either returns the element, or undefined if we hit a leaf. + var find = function(tree, x, cmp) { + while (true) { + if (tree === EMPTY) { return undefined; } + else { + var cmpval = cmp(x, tree.x); + if (cmpval < 0) { + tree = tree.l; + } else if (cmpval > 0) { + tree = tree.r; + } else { + return tree.x; + } + } + } + }; + + var contains = function(tree, x, cmp) { + while (true) { + if (tree === EMPTY) { return false; } + else { + var cmpval = cmp(x, tree.x); + if (cmpval < 0) { + tree = tree.l; + } else if (cmpval > 0) { + tree = tree.r; + } else { + return true; + } + } + } + }; + + + var insert = function(tree, x, cmp) { + return turnB(insert_(tree, x, cmp)); + }; + + insert_ = function(tree, x, cmp) { + var cmpval; + if (tree === EMPTY) { + return new Node(R, //1, + EMPTY, x, EMPTY); + } else { + cmpval = cmp(x, tree.x); + if (cmpval < 0) { + return balanceL(tree.c,// tree.h, + insert_(tree.l, x, cmp), tree.x, tree.r); + } else if (cmpval > 0) { + return balanceR(tree.c,// tree.h, + tree.l, tree.x, insert_(tree.r, x, cmp)); + } else { + return replaceX(tree, x); + } + } + }; + + balanceL = function(c,// h, + l, x, r) { + if (c === B && + l !== EMPTY && l.c === R + && l.l !== EMPTY && l.l.c === R) { + return new Node(R,// h+1, + turnB(l.l), l.x, new Node(B, //h, + l.r, x, r)); + } else { + return new Node(c,// h, + l, x, r); + } + }; + + balanceR = function(c,// h, + l, x, r) { + if (c === B && + l !== EMPTY && l.c === R && + r !== EMPTY && r.c === R) { + return new Node(R,// h+1, + turnB(l), x, turnB(r)); + } else if (r !== EMPTY && + r.c === R) { + return new Node(c,// h, + new Node(R,// r.h, + l, x, r.l), r.x, r.r); + } else { + return new Node(c,// h, + l, x, r); + } + }; + + + var remove = function(tree, x, cmp) { + var removed; + if (tree === EMPTY) { + return tree; + } else { + removed = remove_(turnR(tree), x, cmp); + if (removed === EMPTY) { + return removed; + } else { + return turnB(removed); + } + } + }; + + remove_ = function(tree, x, cmp) { + var cmpval; + if (tree === EMPTY) { + return tree; + } else { + cmpval = cmp(x, tree.x); + if (cmpval < 0) { + return removeLT(x, tree.c,// tree.h, + tree.l, tree.x, tree.r, cmp); + } else if (cmpval > 0) { + return removeGT(x, tree.c,// tree.h, + tree.l, tree.x, tree.r, cmp); + } else { + return removeEQ(x, tree.c,// tree.h, + tree.l, tree.x, tree.r, cmp); + } + } + }; + + removeLT = function(kx, c,// h, + l, x, r, cmp) { + var isBB; + var isBR; + if (c === R) { + isBB = isBlackLeftBlack(l); + isBR = isBlackLeftRed(r); + if (isBB && isBR) { + return new Node(R, + //h, + new Node(B,// r.h, + remove_(turnR(l), kx, cmp), x, r.l.l), + r.l.x, + new Node(B,// r.h, + r.l.r, r.x, r.r)); + } else if (isBB) { + return balanceR(B,// h-1, + remove_(turnR(l), kx, cmp), x, turnR(r)); + } + } + return new Node(c,// h, + remove_(l, kx, cmp), x, r); + }; + + + removeGT = function(kx, c,// h, + l, x, r, cmp) { + var isBB, isBR; + if (l !== EMPTY && l.c === R) { + return balanceR(c,// h, + l.l, l.x, remove_(new Node(R,// h, + l.r, x, r), kx, cmp)); + } + if (c === R) { + isBB = isBlackLeftBlack(r); + isBR = isBlackLeftRed(l); + if (isBB && isBR) { + return new Node(R, + //h, + turnB(l.l), + l.x, + balanceR(B,// l.h, + l.r, x, remove_(turnR(r), kx, cmp))); + } + if (isBB) { + return balanceR(B,// h-1, + turnR(l), x, remove_(turnR(r), kx, cmp)); + } + } + if (c === R) { + return new Node(R,// h, + l, x, remove_(r, kx, cmp)); + } + throw new Error("removeGT"); + }; + + removeEQ = function(kx, c,// h, + l, x, r, cmp) { + var isBB, isBR, m; + if (c === R && l === EMPTY && r === EMPTY) { + return EMPTY; + } + if (l !== EMPTY && l.c === R) { + return balanceR(c,// h, + l.l, l.x, remove_(new Node(R,// h, + l.r, x, r), kx, cmp)); + } + if (c === R) { + isBB = isBlackLeftBlack(r); + isBR = isBlackLeftRed(l); + if (isBB && isBR) { + m = minimum(r); + return balanceR(R,// h, + turnB(l.l), l.x, balanceR(B,// l.h, + l.r, m, removeMin_(turnR(r)))); + } + if (isBB) { + m = minimum(r); + return balanceR(B,// h-1, + turnR(l), m, removeMin_(turnR(r))); + } + } + if (c === R && + r !== EMPTY && r.c === B) { + m = minimum(r); + return new Node(R,// h, + l, m, new Node(B,// r.h, + removeMin_(r.l), r.x, r.r)); + } + throw new Error("removeEQ"); + }; + + + removeMin_ = function(t) { + // var h; + var l, x, r, isBB, isBR; + if (t !== EMPTY && t.c === R && + t.l === EMPTY && t.r === EMPTY) { + return EMPTY; + } + if (t !== EMPTY && t.c === R) { + //h = t.h; + l = t.l; x = t.x; r = t.r; + isBB = isBlackLeftBlack(l); + isBR = isBlackLeftRed(r); + if (isRed(l)) { + return new Node(R,// h, + removeMin_(l), x, r); + } else if (isBB && isBR) { + return hardMin(t); + } else if (isBB) { + return balanceR(B,// h-1, + removeMin_(turnR(l)), x, turnR(r)); + } else { + return new Node(R,// h, + new Node(B,// l.h, + removeMin_(l.l), l.x, l.r), x, r); + } + } + throw new Error("removeMin"); + }; + + + hardMin = function(t) { + if (t !== EMPTY && t.c === R && + t.r !== EMPTY && t.r.c === B && + t.r.l !== EMPTY && t.r.l.c === R) { + return new Node(R, + //t.h, + new Node(B,// t.r.h, + removeMin_(turnR(t.l)), t.x, t.r.l.l), + t.r.l.x, + new Node(B,// t.r.h, + t.r.l.r, t.r.x, t.r.r)); + } + throw new Error("hardMin"); + }; + + + + ////////////////////////////////////////////////////////////////////// + + // turnB: llrbtree -> llrbtree + turnB = function(tree) { + if (tree === EMPTY) { throw new Error("turnB"); } + return new Node(B, //tree.h, + tree.l, tree.x, tree.r); + }; + + // turnR: llrbtree -> llrbtree + turnR = function(tree) { + if (tree === EMPTY) { throw new Error("turnR"); } + return new Node(R, //tree.h, + tree.l, tree.x, tree.r); + }; + + // turnR: llrbtree x -> llrbtree + replaceX = function(tree, x) { + if (tree === EMPTY) { throw new Error("replaceElt"); } + return new Node(tree.c, //tree.h, + tree.l, x, tree.r); + }; + + // isBlack: llrbtree -> boolean + isBlack = function(tree) { + if (tree === EMPTY) { return true; } + return tree.c === B; + }; + + // isRed: llrbtree -> boolean + isRed = function(tree) { + if (tree === EMPTY) { return false; } + return tree.c === R; + }; + + // isBlackLeftBlack: llrbtree -> boolean + isBlackLeftBlack = function(tree) { + if (tree !== EMPTY) { + if (tree.c === B) { + if (tree.l === EMPTY) { + return true; + } else { + return tree.l.c === B; + } + } else { + return false; + } + } else { + return false; + } + }; + + + // isBlackLeftRed: llrbtree -> boolean + isBlackLeftRed = function(tree) { + if (tree !== EMPTY) { + if (tree.c === B) { + if (tree.l !== EMPTY) { + return tree.l.c === R; + } else { + return false; + } + } else { + return false; + } + } else { + return false; + } + }; + + + // minimum: llrbtree -> X + // Returns the minimum element in the tree. + minimum = function(tree) { + if (tree === EMPTY) { throw new Error("minimum"); } + while(true) { + if (tree.l === EMPTY) { + return tree.x; + } + tree = tree.l; + } + }; + + + + ////////////////////////////////////////////////////////////////////// + // This Map makes it easier to use the llrbtree as an associative array. + // The nodes on the tree are key/value pairs, the comparator of which + // focuses only on the key portion of the pair. + + var Map = function(cmp, tree) { + this.cmp = cmp; + this.tree = tree; + }; + + var makeMap = function(keycmp) { + keycmp = keycmp || function(x, y) { var sx = String(x), sy = String(y); + if (sx < sy) { return -1; } + if (sx > sy) { return 1; } + return 0; }; + return new Map( + function(n1, n2) { + return keycmp(n1[0], n2[0]); + }, + EMPTY); + }; + + Map.prototype.put = function(key, val) { + return new Map(this.cmp, + insert(this.tree, [key, val], this.cmp)); + }; + + Map.prototype.contains = function(key) { + return contains(this.tree, [key, undefined], this.cmp); + }; + + var defaultOnFail = function() { + throw new Error("lookup failed"); + }; + + Map.prototype.get = function(key, onFail) { + var x; + onFail = onFail || defaultOnFail; + x = find(this.tree, [key, undefined], this.cmp); + if (x === undefined) { return onFail(); } + return x[1]; + }; + + Map.prototype.remove = function(key) { + return new Map(this.cmp, + remove(this.tree, [key, undefined], this.cmp)); + }; + + Map.prototype.isEmpty = function() { + return this.tree === EMPTY; + }; + + Map.prototype.keys = function() { + var result = items(this.tree), i; + for (i = 0; i < result.length; i++) { + result[i] = result[i][0]; + } + return result; + }; + + Map.prototype.values = function() { + var result = items(this.tree), i; + for (i = 0; i < result.length; i++) { + result[i] = result[i][1]; + } + return result; + }; + + Map.prototype.items = function() { + var result = items(this.tree), i; + for (i = 0; i < result.length; i++) { + // Make sure to copy so that you can't damage the internal + // key/value pairs. + result[i] = result[i].slice(0); + } + return result; + }; + + // Return the color at the tree. + Map.prototype.color = function() { + if (this.tree === EMPTY) { return B; } + return this.tree.c; + }; + + // Navigate left + Map.prototype.left = function() { + if (this.tree === EMPTY) { throw new Error("left"); } + return new Map(this.cmp, this.tree.l); + }; + + // Navigate right + Map.prototype.right = function() { + if (this.tree === EMPTY) { throw new Error("right"); } + return new Map(this.cmp, this.tree.r); + }; + + // Get the key at the tree + Map.prototype.key = function() { + if (this.tree === EMPTY) { throw new Error("key"); } + return this.tree.x[0]; + }; + + // Get the value at the tree. + Map.prototype.val = function() { + if (this.tree === EMPTY) { throw new Error("val"); } + return this.tree.x[1]; + }; + + + + + + ////////////////////////////////////////////////////////////////////// + LLRBTree.EMPTY = EMPTY; + LLRBTree.insert = insert; + LLRBTree.contains = contains; + LLRBTree.find = find; + LLRBTree.remove = remove; + LLRBTree.items = items; + + + LLRBTree.makeMap = makeMap; +}()); \ No newline at end of file diff --git a/version.rkt b/version.rkt index e35c42b..492c66b 100644 --- a/version.rkt +++ b/version.rkt @@ -6,4 +6,4 @@ (provide version) (: version String) -(define version "1.56") +(define version "1.59")