ripping out the avltree stuff with the llrbtree implementation, which we'll use to get immutable hash tables.

This commit is contained in:
Danny Yoo 2011-11-07 15:46:22 -05:00
parent 6e9733afec
commit dcb2e9fb10
6 changed files with 542 additions and 781 deletions

View File

@ -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:

View File

@ -1,7 +1,5 @@
Misc
===
current-milliseconds
current-seconds
string-copy
exit
sleep

View File

@ -59,7 +59,7 @@
hashes-header.js
jshashtable-2.1_src.js
avltree.js
llrbtree.js
baselib-hashes.js
hashes-footer.js

View File

@ -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:
* <pre>
* 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
* </pre>
*/
/**
* 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:
* <pre>
* x < 0 if a < b,
* x > 0 if a > b,
* x = 0 otherwise
* </pre>
*
* @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;
};

View File

@ -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;
}());

View File

@ -6,4 +6,4 @@
(provide version)
(: version String)
(define version "1.56")
(define version "1.59")