// Copyright 2007 Google Inc.
// All Rights Reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materials provided with the
//    distribution.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. 

/**
 * @fileoverview Datastructure: AvlTree
 *
 * This file provides the implementation of an AVL-Tree datastructure. The tree
 * maintains a unique values in a sorted order. The values can be accessed
 * efficiently in their sorted order since the tree enforces a O(logn)
 * maximum height.
 *
 * 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>
 *
 */


goog.provide('goog.structs.AvlTree');

goog.require('goog.structs');


/**
 * 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
 */
goog.structs.AvlTree = function(opt_comparator) {
  this.comparator_ = opt_comparator ||
                     goog.structs.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.
 * @private
 */
goog.structs.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 {goog.structs.AvlTree.Node}
 * @private
 */
goog.structs.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 {goog.structs.AvlTree.Node}
 * @private
 */
goog.structs.AvlTree.prototype.comparator_ = null;


/**
 * Pointer to the node with the smallest value in the tree.
 *
 * @type {goog.structs.AvlTree.Node}
 * @private
 */
goog.structs.AvlTree.prototype.minNode_ = null;


/**
 * Pointer to the node with the largest value in the tree.
 *
 * @type {goog.structs.AvlTree.Node}
 * @private
 */
goog.structs.AvlTree.prototype.maxNode_ = null;


/**
 * Keeps track of the number of nodes in the tree.
 *
 * @type {number?}
 * @private
 */
goog.structs.AvlTree.prototype.count_ = null;


/**
 * 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.
 */
goog.structs.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 goog.structs.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 goog.structs.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 goog.structs.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.
 */
goog.structs.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) {
    this.count_ -= 1;
  }

  // Return the value that was removed, null if the value was not in the tree
  return retValue;
};


/**
 * Removes all nodes from the tree.
 */
goog.structs.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} true if the tree contains a node with the specified value,
 *    false otherwise.
 */
goog.structs.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.
 */
goog.structs.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 {Object} The minimum value contained in the tree.
 */
goog.structs.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 {number} The maximum value contained in the tree.
 */
goog.structs.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.
 */
goog.structs.AvlTree.prototype.getHeight = function() {
    return this.root_ ? this.root_.height : 0;
};


/**
 * Inserts the value 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.
 */
goog.structs.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 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 maximum
 * 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 smallest value >= opt_startValue.
 */
goog.structs.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 an reverse-order traversal of the tree and calls 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
 * 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.
 */
goog.structs.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;
    }
  }
};


/**
 * Calls traversalFunc with each node it returns until 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 {goog.structs.AvlTree.Node} opt_startNode Begin traversal on this
 *     node.
 * @param {goog.structs.AvlTree.Node} opt_endNode   End traversal on this node.
 * @private
 */
goog.structs.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 not,
 * this method will perform 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 {goog.structs.AvlTree.Node} node Node to begin balance from.
 * @private
 */
goog.structs.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 {goog.structs.AvlTree.Node} node Pivot node to rotate from.
 * @private
 */
goog.structs.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 {goog.structs.AvlTree.Node} node Pivot node to rotate from.
 * @private
 */
goog.structs.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 ensure the tree still
 * maintains the AVL-tree balance.
 *
 * @param {goog.structs.AvlTree.Node} node The node to be removed.
 * @private
 */
goog.structs.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
 * opt_rootNode.
 *
 * @param {goog.structs.AvlTree.Node} opt_rootNode Optional root node.
 * @return {goog.structs.AvlTree.Node} the node with the smallest value in
 *     the tree.
 * @private
 */
goog.structs.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 {goog.structs.AvlTree.Node} opt_rootNode Optional root node.
 * @return {goog.structs.AvlTree.Node} the node with the largest value in
 *     the tree.
 * @private
 */
goog.structs.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 {goog.structs.AvlTree.Node} opt_parent Optional parent node.
 * @constructor
 */
goog.structs.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 {goog.structs.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 {goog.structs.AvlTree.Node}
 */
goog.structs.AvlTree.Node.prototype.left = null;


/**
 * The node's right child. Null if the node does not have a right child.
 *
 * @type {goog.structs.AvlTree.Node}
 */
goog.structs.AvlTree.Node.prototype.right = null;


/**
 * The height of the tree rooted at this node.
 *
 * @type {number}
 */
goog.structs.AvlTree.Node.prototype.height = 1;


/**
 * Returns true if the specified node has a parent and is the right child of
 * its parent, returns false otherwise.
 *
 * @return {boolean} true if the specified node has a parent and is the right
 *    child of its parent, returns false otherwise.
 */
goog.structs.AvlTree.Node.prototype.isRightChild = function() {
  return this.parent && this.parent.right == this;
};


/**
 * Returns true if the specified node has a parent and is the left child of
 * its parent, returns false otherwise.
 *
 * @return {boolean} true if the specified node has a parent and is the left
 *    child of its parent, returns false otherwise.
 */
goog.structs.AvlTree.Node.prototype.isLeftChild = function() {
  return this.parent && this.parent.left == this;
};

