11cb0ef41Sopenharmony_ci// Copyright 2009 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Redistribution and use in source and binary forms, with or without
31cb0ef41Sopenharmony_ci// modification, are permitted provided that the following conditions are
41cb0ef41Sopenharmony_ci// met:
51cb0ef41Sopenharmony_ci//
61cb0ef41Sopenharmony_ci//     * Redistributions of source code must retain the above copyright
71cb0ef41Sopenharmony_ci//       notice, this list of conditions and the following disclaimer.
81cb0ef41Sopenharmony_ci//     * Redistributions in binary form must reproduce the above
91cb0ef41Sopenharmony_ci//       copyright notice, this list of conditions and the following
101cb0ef41Sopenharmony_ci//       disclaimer in the documentation and/or other materials provided
111cb0ef41Sopenharmony_ci//       with the distribution.
121cb0ef41Sopenharmony_ci//     * Neither the name of Google Inc. nor the names of its
131cb0ef41Sopenharmony_ci//       contributors may be used to endorse or promote products derived
141cb0ef41Sopenharmony_ci//       from this software without specific prior written permission.
151cb0ef41Sopenharmony_ci//
161cb0ef41Sopenharmony_ci// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
171cb0ef41Sopenharmony_ci// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
181cb0ef41Sopenharmony_ci// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
191cb0ef41Sopenharmony_ci// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
201cb0ef41Sopenharmony_ci// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
211cb0ef41Sopenharmony_ci// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
221cb0ef41Sopenharmony_ci// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
231cb0ef41Sopenharmony_ci// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
241cb0ef41Sopenharmony_ci// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
251cb0ef41Sopenharmony_ci// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
261cb0ef41Sopenharmony_ci// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ci/**
301cb0ef41Sopenharmony_ci * Constructs a Splay tree.  A splay tree is a self-balancing binary
311cb0ef41Sopenharmony_ci * search tree with the additional property that recently accessed
321cb0ef41Sopenharmony_ci * elements are quick to access again. It performs basic operations
331cb0ef41Sopenharmony_ci * such as insertion, look-up and removal in O(log(n)) amortized time.
341cb0ef41Sopenharmony_ci *
351cb0ef41Sopenharmony_ci * @constructor
361cb0ef41Sopenharmony_ci */
371cb0ef41Sopenharmony_ciexport class SplayTree {
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci  /**
401cb0ef41Sopenharmony_ci   * Pointer to the root node of the tree.
411cb0ef41Sopenharmony_ci   *
421cb0ef41Sopenharmony_ci   * @type {SplayTreeNode}
431cb0ef41Sopenharmony_ci   * @private
441cb0ef41Sopenharmony_ci   */
451cb0ef41Sopenharmony_ci  root_ = null;
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci  /**
491cb0ef41Sopenharmony_ci   * @return {boolean} Whether the tree is empty.
501cb0ef41Sopenharmony_ci   */
511cb0ef41Sopenharmony_ci  isEmpty() {
521cb0ef41Sopenharmony_ci    return this.root_ === null;
531cb0ef41Sopenharmony_ci  }
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci  /**
561cb0ef41Sopenharmony_ci   * Inserts a node into the tree with the specified key and value if
571cb0ef41Sopenharmony_ci   * the tree does not already contain a node with the specified key. If
581cb0ef41Sopenharmony_ci   * the value is inserted, it becomes the root of the tree.
591cb0ef41Sopenharmony_ci   *
601cb0ef41Sopenharmony_ci   * @param {number} key Key to insert into the tree.
611cb0ef41Sopenharmony_ci   * @param {*} value Value to insert into the tree.
621cb0ef41Sopenharmony_ci   */
631cb0ef41Sopenharmony_ci  insert(key, value) {
641cb0ef41Sopenharmony_ci    if (this.isEmpty()) {
651cb0ef41Sopenharmony_ci      this.root_ = new SplayTreeNode(key, value);
661cb0ef41Sopenharmony_ci      return;
671cb0ef41Sopenharmony_ci    }
681cb0ef41Sopenharmony_ci    // Splay on the key to move the last node on the search path for
691cb0ef41Sopenharmony_ci    // the key to the root of the tree.
701cb0ef41Sopenharmony_ci    this.splay_(key);
711cb0ef41Sopenharmony_ci    if (this.root_.key == key) return;
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci    const node = new SplayTreeNode(key, value);
741cb0ef41Sopenharmony_ci    if (key > this.root_.key) {
751cb0ef41Sopenharmony_ci      node.left = this.root_;
761cb0ef41Sopenharmony_ci      node.right = this.root_.right;
771cb0ef41Sopenharmony_ci      this.root_.right = null;
781cb0ef41Sopenharmony_ci    } else {
791cb0ef41Sopenharmony_ci      node.right = this.root_;
801cb0ef41Sopenharmony_ci      node.left = this.root_.left;
811cb0ef41Sopenharmony_ci      this.root_.left = null;
821cb0ef41Sopenharmony_ci    }
831cb0ef41Sopenharmony_ci    this.root_ = node;
841cb0ef41Sopenharmony_ci  }
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci  /**
871cb0ef41Sopenharmony_ci   * Removes a node with the specified key from the tree if the tree
881cb0ef41Sopenharmony_ci   * contains a node with this key. The removed node is returned. If the
891cb0ef41Sopenharmony_ci   * key is not found, an exception is thrown.
901cb0ef41Sopenharmony_ci   *
911cb0ef41Sopenharmony_ci   * @param {number} key Key to find and remove from the tree.
921cb0ef41Sopenharmony_ci   * @return {SplayTreeNode} The removed node.
931cb0ef41Sopenharmony_ci   */
941cb0ef41Sopenharmony_ci  remove(key) {
951cb0ef41Sopenharmony_ci    if (this.isEmpty()) {
961cb0ef41Sopenharmony_ci      throw Error(`Key not found: ${key}`);
971cb0ef41Sopenharmony_ci    }
981cb0ef41Sopenharmony_ci    this.splay_(key);
991cb0ef41Sopenharmony_ci    if (this.root_.key != key) {
1001cb0ef41Sopenharmony_ci      throw Error(`Key not found: ${key}`);
1011cb0ef41Sopenharmony_ci    }
1021cb0ef41Sopenharmony_ci    const removed = this.root_;
1031cb0ef41Sopenharmony_ci    if (this.root_.left === null) {
1041cb0ef41Sopenharmony_ci      this.root_ = this.root_.right;
1051cb0ef41Sopenharmony_ci    } else {
1061cb0ef41Sopenharmony_ci      const { right } = this.root_;
1071cb0ef41Sopenharmony_ci      this.root_ = this.root_.left;
1081cb0ef41Sopenharmony_ci      // Splay to make sure that the new root has an empty right child.
1091cb0ef41Sopenharmony_ci      this.splay_(key);
1101cb0ef41Sopenharmony_ci      // Insert the original right child as the right child of the new
1111cb0ef41Sopenharmony_ci      // root.
1121cb0ef41Sopenharmony_ci      this.root_.right = right;
1131cb0ef41Sopenharmony_ci    }
1141cb0ef41Sopenharmony_ci    return removed;
1151cb0ef41Sopenharmony_ci  }
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci  /**
1181cb0ef41Sopenharmony_ci   * Returns the node having the specified key or null if the tree doesn't contain
1191cb0ef41Sopenharmony_ci   * a node with the specified key.
1201cb0ef41Sopenharmony_ci   *
1211cb0ef41Sopenharmony_ci   * @param {number} key Key to find in the tree.
1221cb0ef41Sopenharmony_ci   * @return {SplayTreeNode} Node having the specified key.
1231cb0ef41Sopenharmony_ci   */
1241cb0ef41Sopenharmony_ci  find(key) {
1251cb0ef41Sopenharmony_ci    if (this.isEmpty()) return null;
1261cb0ef41Sopenharmony_ci    this.splay_(key);
1271cb0ef41Sopenharmony_ci    return this.root_.key == key ? this.root_ : null;
1281cb0ef41Sopenharmony_ci  }
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci  /**
1311cb0ef41Sopenharmony_ci   * @return {SplayTreeNode} Node having the minimum key value.
1321cb0ef41Sopenharmony_ci   */
1331cb0ef41Sopenharmony_ci  findMin() {
1341cb0ef41Sopenharmony_ci    if (this.isEmpty()) return null;
1351cb0ef41Sopenharmony_ci    let current = this.root_;
1361cb0ef41Sopenharmony_ci    while (current.left !== null) {
1371cb0ef41Sopenharmony_ci      current = current.left;
1381cb0ef41Sopenharmony_ci    }
1391cb0ef41Sopenharmony_ci    return current;
1401cb0ef41Sopenharmony_ci  }
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci  /**
1431cb0ef41Sopenharmony_ci   * @return {SplayTreeNode} Node having the maximum key value.
1441cb0ef41Sopenharmony_ci   */
1451cb0ef41Sopenharmony_ci  findMax(opt_startNode) {
1461cb0ef41Sopenharmony_ci    if (this.isEmpty()) return null;
1471cb0ef41Sopenharmony_ci    let current = opt_startNode || this.root_;
1481cb0ef41Sopenharmony_ci    while (current.right !== null) {
1491cb0ef41Sopenharmony_ci      current = current.right;
1501cb0ef41Sopenharmony_ci    }
1511cb0ef41Sopenharmony_ci    return current;
1521cb0ef41Sopenharmony_ci  }
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_ci  /**
1551cb0ef41Sopenharmony_ci   * @return {SplayTreeNode} Node having the maximum key value that
1561cb0ef41Sopenharmony_ci   *     is less or equal to the specified key value.
1571cb0ef41Sopenharmony_ci   */
1581cb0ef41Sopenharmony_ci  findGreatestLessThan(key) {
1591cb0ef41Sopenharmony_ci    if (this.isEmpty()) return null;
1601cb0ef41Sopenharmony_ci    // Splay on the key to move the node with the given key or the last
1611cb0ef41Sopenharmony_ci    // node on the search path to the top of the tree.
1621cb0ef41Sopenharmony_ci    this.splay_(key);
1631cb0ef41Sopenharmony_ci    // Now the result is either the root node or the greatest node in
1641cb0ef41Sopenharmony_ci    // the left subtree.
1651cb0ef41Sopenharmony_ci    if (this.root_.key <= key) {
1661cb0ef41Sopenharmony_ci      return this.root_;
1671cb0ef41Sopenharmony_ci    } else if (this.root_.left !== null) {
1681cb0ef41Sopenharmony_ci      return this.findMax(this.root_.left);
1691cb0ef41Sopenharmony_ci    } else {
1701cb0ef41Sopenharmony_ci      return null;
1711cb0ef41Sopenharmony_ci    }
1721cb0ef41Sopenharmony_ci  }
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci  /**
1751cb0ef41Sopenharmony_ci   * @return {Array<*>} An array containing all the values of tree's nodes paired
1761cb0ef41Sopenharmony_ci   *     with keys.
1771cb0ef41Sopenharmony_ci   */
1781cb0ef41Sopenharmony_ci  exportKeysAndValues() {
1791cb0ef41Sopenharmony_ci    const result = [];
1801cb0ef41Sopenharmony_ci    this.traverse_(function(node) { result.push([node.key, node.value]); });
1811cb0ef41Sopenharmony_ci    return result;
1821cb0ef41Sopenharmony_ci  }
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_ci  /**
1851cb0ef41Sopenharmony_ci   * @return {Array<*>} An array containing all the values of tree's nodes.
1861cb0ef41Sopenharmony_ci   */
1871cb0ef41Sopenharmony_ci  exportValues() {
1881cb0ef41Sopenharmony_ci    const result = [];
1891cb0ef41Sopenharmony_ci    this.traverse_(function(node) { result.push(node.value) });
1901cb0ef41Sopenharmony_ci    return result;
1911cb0ef41Sopenharmony_ci  }
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci  /**
1941cb0ef41Sopenharmony_ci   * Perform the splay operation for the given key. Moves the node with
1951cb0ef41Sopenharmony_ci   * the given key to the top of the tree.  If no node has the given
1961cb0ef41Sopenharmony_ci   * key, the last node on the search path is moved to the top of the
1971cb0ef41Sopenharmony_ci   * tree. This is the simplified top-down splaying algorithm from:
1981cb0ef41Sopenharmony_ci   * "Self-adjusting Binary Search Trees" by Sleator and Tarjan
1991cb0ef41Sopenharmony_ci   *
2001cb0ef41Sopenharmony_ci   * @param {number} key Key to splay the tree on.
2011cb0ef41Sopenharmony_ci   * @private
2021cb0ef41Sopenharmony_ci   */
2031cb0ef41Sopenharmony_ci  splay_(key) {
2041cb0ef41Sopenharmony_ci    if (this.isEmpty()) return;
2051cb0ef41Sopenharmony_ci    // Create a dummy node.  The use of the dummy node is a bit
2061cb0ef41Sopenharmony_ci    // counter-intuitive: The right child of the dummy node will hold
2071cb0ef41Sopenharmony_ci    // the L tree of the algorithm.  The left child of the dummy node
2081cb0ef41Sopenharmony_ci    // will hold the R tree of the algorithm.  Using a dummy node, left
2091cb0ef41Sopenharmony_ci    // and right will always be nodes and we avoid special cases.
2101cb0ef41Sopenharmony_ci    let dummy, left, right;
2111cb0ef41Sopenharmony_ci    dummy = left = right = new SplayTreeNode(null, null);
2121cb0ef41Sopenharmony_ci    let current = this.root_;
2131cb0ef41Sopenharmony_ci    while (true) {
2141cb0ef41Sopenharmony_ci      if (key < current.key) {
2151cb0ef41Sopenharmony_ci        if (current.left === null) break;
2161cb0ef41Sopenharmony_ci        if (key < current.left.key) {
2171cb0ef41Sopenharmony_ci          // Rotate right.
2181cb0ef41Sopenharmony_ci          const tmp = current.left;
2191cb0ef41Sopenharmony_ci          current.left = tmp.right;
2201cb0ef41Sopenharmony_ci          tmp.right = current;
2211cb0ef41Sopenharmony_ci          current = tmp;
2221cb0ef41Sopenharmony_ci          if (current.left === null) break;
2231cb0ef41Sopenharmony_ci        }
2241cb0ef41Sopenharmony_ci        // Link right.
2251cb0ef41Sopenharmony_ci        right.left = current;
2261cb0ef41Sopenharmony_ci        right = current;
2271cb0ef41Sopenharmony_ci        current = current.left;
2281cb0ef41Sopenharmony_ci      } else if (key > current.key) {
2291cb0ef41Sopenharmony_ci        if (current.right === null) break;
2301cb0ef41Sopenharmony_ci        if (key > current.right.key) {
2311cb0ef41Sopenharmony_ci          // Rotate left.
2321cb0ef41Sopenharmony_ci          const tmp = current.right;
2331cb0ef41Sopenharmony_ci          current.right = tmp.left;
2341cb0ef41Sopenharmony_ci          tmp.left = current;
2351cb0ef41Sopenharmony_ci          current = tmp;
2361cb0ef41Sopenharmony_ci          if (current.right === null) break;
2371cb0ef41Sopenharmony_ci        }
2381cb0ef41Sopenharmony_ci        // Link left.
2391cb0ef41Sopenharmony_ci        left.right = current;
2401cb0ef41Sopenharmony_ci        left = current;
2411cb0ef41Sopenharmony_ci        current = current.right;
2421cb0ef41Sopenharmony_ci      } else {
2431cb0ef41Sopenharmony_ci        break;
2441cb0ef41Sopenharmony_ci      }
2451cb0ef41Sopenharmony_ci    }
2461cb0ef41Sopenharmony_ci    // Assemble.
2471cb0ef41Sopenharmony_ci    left.right = current.left;
2481cb0ef41Sopenharmony_ci    right.left = current.right;
2491cb0ef41Sopenharmony_ci    current.left = dummy.right;
2501cb0ef41Sopenharmony_ci    current.right = dummy.left;
2511cb0ef41Sopenharmony_ci    this.root_ = current;
2521cb0ef41Sopenharmony_ci  }
2531cb0ef41Sopenharmony_ci
2541cb0ef41Sopenharmony_ci  /**
2551cb0ef41Sopenharmony_ci   * Performs a preorder traversal of the tree.
2561cb0ef41Sopenharmony_ci   *
2571cb0ef41Sopenharmony_ci   * @param {function(SplayTreeNode)} f Visitor function.
2581cb0ef41Sopenharmony_ci   * @private
2591cb0ef41Sopenharmony_ci   */
2601cb0ef41Sopenharmony_ci  traverse_(f) {
2611cb0ef41Sopenharmony_ci    const nodesToVisit = [this.root_];
2621cb0ef41Sopenharmony_ci    while (nodesToVisit.length > 0) {
2631cb0ef41Sopenharmony_ci      const node = nodesToVisit.shift();
2641cb0ef41Sopenharmony_ci      if (node === null) continue;
2651cb0ef41Sopenharmony_ci      f(node);
2661cb0ef41Sopenharmony_ci      nodesToVisit.push(node.left);
2671cb0ef41Sopenharmony_ci      nodesToVisit.push(node.right);
2681cb0ef41Sopenharmony_ci    }
2691cb0ef41Sopenharmony_ci  }
2701cb0ef41Sopenharmony_ci}
2711cb0ef41Sopenharmony_ci
2721cb0ef41Sopenharmony_ci/**
2731cb0ef41Sopenharmony_ci * Constructs a Splay tree node.
2741cb0ef41Sopenharmony_ci *
2751cb0ef41Sopenharmony_ci * @param {number} key Key.
2761cb0ef41Sopenharmony_ci * @param {*} value Value.
2771cb0ef41Sopenharmony_ci */
2781cb0ef41Sopenharmony_ciclass SplayTreeNode {
2791cb0ef41Sopenharmony_ci  constructor(key, value) {
2801cb0ef41Sopenharmony_ci    this.key = key;
2811cb0ef41Sopenharmony_ci    this.value = value;
2821cb0ef41Sopenharmony_ci    /**
2831cb0ef41Sopenharmony_ci     * @type {SplayTreeNode}
2841cb0ef41Sopenharmony_ci     */
2851cb0ef41Sopenharmony_ci    this.left = null;
2861cb0ef41Sopenharmony_ci    /**
2871cb0ef41Sopenharmony_ci     * @type {SplayTreeNode}
2881cb0ef41Sopenharmony_ci     */
2891cb0ef41Sopenharmony_ci    this.right = null;
2901cb0ef41Sopenharmony_ci  }
2911cb0ef41Sopenharmony_ci};
292