summit/frontend/node_modules/splaytree/index.js

607 lines
13 KiB
JavaScript

function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; }
export default class SplayTree {
constructor(compare = DEFAULT_COMPARE, noDuplicates = false) {
this._compare = compare;
this._root = null;
this._size = 0;
this._noDuplicates = !!noDuplicates;
}
rotateLeft(x) {
var y = x.right;
if (y) {
x.right = y.left;
if (y.left) y.left.parent = x;
y.parent = x.parent;
}
if (!x.parent) this._root = y;
else if (x === x.parent.left) x.parent.left = y;
else x.parent.right = y;
if (y) y.left = x;
x.parent = y;
}
rotateRight(x) {
var y = x.left;
if (y) {
x.left = y.right;
if (y.right) y.right.parent = x;
y.parent = x.parent;
}
if (!x.parent) this._root = y;
else if(x === x.parent.left) x.parent.left = y;
else x.parent.right = y;
if (y) y.right = x;
x.parent = y;
}
_splay(x) {
while (x.parent) {
var p = x.parent;
if (!p.parent) {
if (p.left === x) this.rotateRight(p);
else this.rotateLeft(p);
} else if (p.left === x && p.parent.left === p) {
this.rotateRight(p.parent);
this.rotateRight(p);
} else if (p.right === x && p.parent.right === p) {
this.rotateLeft(p.parent);
this.rotateLeft(p);
} else if (p.left === x && p.parent.right === p) {
this.rotateRight(p);
this.rotateLeft(p);
} else {
this.rotateLeft(p);
this.rotateRight(p);
}
}
}
splay(x) {
var p, gp, ggp, l, r;
while (x.parent) {
p = x.parent;
gp = p.parent;
if (gp && gp.parent) {
ggp = gp.parent;
if (ggp.left === gp) ggp.left = x;
else ggp.right = x;
x.parent = ggp;
} else {
x.parent = null;
this._root = x;
}
l = x.left; r = x.right;
if (x === p.left) { // left
if (gp) {
if (gp.left === p) {
/* zig-zig */
if (p.right) {
gp.left = p.right;
gp.left.parent = gp;
} else gp.left = null;
p.right = gp;
gp.parent = p;
} else {
/* zig-zag */
if (l) {
gp.right = l;
l.parent = gp;
} else gp.right = null;
x.left = gp;
gp.parent = x;
}
}
if (r) {
p.left = r;
r.parent = p;
} else p.left = null;
x.right = p;
p.parent = x;
} else { // right
if (gp) {
if (gp.right === p) {
/* zig-zig */
if (p.left) {
gp.right = p.left;
gp.right.parent = gp;
} else gp.right = null;
p.left = gp;
gp.parent = p;
} else {
/* zig-zag */
if (r) {
gp.left = r;
r.parent = gp;
} else gp.left = null;
x.right = gp;
gp.parent = x;
}
}
if (l) {
p.right = l;
l.parent = p;
} else p.right = null;
x.left = p;
p.parent = x;
}
}
}
replace(u, v) {
if (!u.parent) this._root = v;
else if (u === u.parent.left) u.parent.left = v;
else u.parent.right = v;
if (v) v.parent = u.parent;
}
minNode(u = this._root) {
if (u) while (u.left) u = u.left;
return u;
}
maxNode(u = this._root) {
if (u) while (u.right) u = u.right;
return u;
}
insert(key, data) {
var z = this._root;
var p = null;
var comp = this._compare;
var cmp;
if (this._noDuplicates) {
while (z) {
p = z;
cmp = comp(z.key, key);
if (cmp === 0) return;
else if (comp(z.key, key) < 0) z = z.right;
else z = z.left;
}
} else {
while (z) {
p = z;
if (comp(z.key, key) < 0) z = z.right;
else z = z.left;
}
}
z = { key, data, left: null, right: null, parent: p };
if (!p) this._root = z;
else if (comp(p.key, z.key) < 0) p.right = z;
else p.left = z;
this.splay(z);
this._size++;
return z;
}
find (key) {
var z = this._root;
var comp = this._compare;
while (z) {
var cmp = comp(z.key, key);
if (cmp < 0) z = z.right;
else if (cmp > 0) z = z.left;
else return z;
}
return null;
}
/**
* Whether the tree contains a node with the given key
* @param {Key} key
* @return {boolean} true/false
*/
contains (key) {
var node = this._root;
var comparator = this._compare;
while (node) {
var cmp = comparator(key, node.key);
if (cmp === 0) return true;
else if (cmp < 0) node = node.left;
else node = node.right;
}
return false;
}
remove (key) {
var z = this.find(key);
if (!z) return false;
this.splay(z);
if (!z.left) this.replace(z, z.right);
else if (!z.right) this.replace(z, z.left);
else {
var y = this.minNode(z.right);
if (y.parent !== z) {
this.replace(y, y.right);
y.right = z.right;
y.right.parent = y;
}
this.replace(z, y);
y.left = z.left;
y.left.parent = y;
}
this._size--;
return true;
}
removeNode(z) {
if (!z) return false;
this.splay(z);
if (!z.left) this.replace(z, z.right);
else if (!z.right) this.replace(z, z.left);
else {
var y = this.minNode(z.right);
if (y.parent !== z) {
this.replace(y, y.right);
y.right = z.right;
y.right.parent = y;
}
this.replace(z, y);
y.left = z.left;
y.left.parent = y;
}
this._size--;
return true;
}
erase (key) {
var z = this.find(key);
if (!z) return;
this.splay(z);
var s = z.left;
var t = z.right;
var sMax = null;
if (s) {
s.parent = null;
sMax = this.maxNode(s);
this.splay(sMax);
this._root = sMax;
}
if (t) {
if (s) sMax.right = t;
else this._root = t;
t.parent = sMax;
}
this._size--;
}
/**
* Removes and returns the node with smallest key
* @return {?Node}
*/
pop () {
var node = this._root, returnValue = null;
if (node) {
while (node.left) node = node.left;
returnValue = { key: node.key, data: node.data };
this.remove(node.key);
}
return returnValue;
}
/* eslint-disable class-methods-use-this */
/**
* Successor node
* @param {Node} node
* @return {?Node}
*/
next (node) {
var successor = node;
if (successor) {
if (successor.right) {
successor = successor.right;
while (successor && successor.left) successor = successor.left;
} else {
successor = node.parent;
while (successor && successor.right === node) {
node = successor; successor = successor.parent;
}
}
}
return successor;
}
/**
* Predecessor node
* @param {Node} node
* @return {?Node}
*/
prev (node) {
var predecessor = node;
if (predecessor) {
if (predecessor.left) {
predecessor = predecessor.left;
while (predecessor && predecessor.right) predecessor = predecessor.right;
} else {
predecessor = node.parent;
while (predecessor && predecessor.left === node) {
node = predecessor;
predecessor = predecessor.parent;
}
}
}
return predecessor;
}
/* eslint-enable class-methods-use-this */
/**
* @param {forEachCallback} callback
* @return {SplayTree}
*/
forEach(callback) {
var current = this._root;
var s = [], done = false, i = 0;
while (!done) {
// Reach the left most Node of the current Node
if (current) {
// Place pointer to a tree node on the stack
// before traversing the node's left subtree
s.push(current);
current = current.left;
} else {
// BackTrack from the empty subtree and visit the Node
// at the top of the stack; however, if the stack is
// empty you are done
if (s.length > 0) {
current = s.pop();
callback(current, i++);
// We have visited the node and its left
// subtree. Now, it's right subtree's turn
current = current.right;
} else done = true;
}
}
return this;
}
/**
* Walk key range from `low` to `high`. Stops if `fn` returns a value.
* @param {Key} low
* @param {Key} high
* @param {Function} fn
* @param {*?} ctx
* @return {SplayTree}
*/
range(low, high, fn, ctx) {
const Q = [];
const compare = this._compare;
let node = this._root, cmp;
while (Q.length !== 0 || node) {
if (node) {
Q.push(node);
node = node.left;
} else {
node = Q.pop();
cmp = compare(node.key, high);
if (cmp > 0) {
break;
} else if (compare(node.key, low) >= 0) {
if (fn.call(ctx, node)) return this; // stop if smth is returned
}
node = node.right;
}
}
return this;
}
/**
* Returns all keys in order
* @return {Array<Key>}
*/
keys () {
var current = this._root;
var s = [], r = [], done = false;
while (!done) {
if (current) {
s.push(current);
current = current.left;
} else {
if (s.length > 0) {
current = s.pop();
r.push(current.key);
current = current.right;
} else done = true;
}
}
return r;
}
/**
* Returns `data` fields of all nodes in order.
* @return {Array<Value>}
*/
values () {
var current = this._root;
var s = [], r = [], done = false;
while (!done) {
if (current) {
s.push(current);
current = current.left;
} else {
if (s.length > 0) {
current = s.pop();
r.push(current.data);
current = current.right;
} else done = true;
}
}
return r;
}
/**
* Returns node at given index
* @param {number} index
* @return {?Node}
*/
at (index) {
// removed after a consideration, more misleading than useful
// index = index % this.size;
// if (index < 0) index = this.size - index;
var current = this._root;
var s = [], done = false, i = 0;
while (!done) {
if (current) {
s.push(current);
current = current.left;
} else {
if (s.length > 0) {
current = s.pop();
if (i === index) return current;
i++;
current = current.right;
} else done = true;
}
}
return null;
}
/**
* Bulk-load items. Both array have to be same size
* @param {Array<Key>} keys
* @param {Array<Value>} [values]
* @param {Boolean} [presort=false] Pre-sort keys and values, using
* tree's comparator. Sorting is done
* in-place
* @return {AVLTree}
*/
load(keys = [], values = [], presort = false) {
if (this._size !== 0) throw new Error('bulk-load: tree is not empty');
const size = keys.length;
if (presort) sort(keys, values, 0, size - 1, this._compare);
this._root = loadRecursive(null, keys, values, 0, size);
this._size = size;
return this;
}
min() {
var node = this.minNode(this._root);
if (node) return node.key;
else return null;
}
max() {
var node = this.maxNode(this._root);
if (node) return node.key;
else return null;
}
isEmpty() { return this._root === null; }
get size() { return this._size; }
/**
* Create a tree and load it with items
* @param {Array<Key>} keys
* @param {Array<Value>?} [values]
* @param {Function?} [comparator]
* @param {Boolean?} [presort=false] Pre-sort keys and values, using
* tree's comparator. Sorting is done
* in-place
* @param {Boolean?} [noDuplicates=false] Allow duplicates
* @return {SplayTree}
*/
static createTree(keys, values, comparator, presort, noDuplicates) {
return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
}
}
function loadRecursive (parent, keys, values, start, end) {
const size = end - start;
if (size > 0) {
const middle = start + Math.floor(size / 2);
const key = keys[middle];
const data = values[middle];
const node = { key, data, parent };
node.left = loadRecursive(node, keys, values, start, middle);
node.right = loadRecursive(node, keys, values, middle + 1, end);
return node;
}
return null;
}
function sort(keys, values, left, right, compare) {
if (left >= right) return;
const pivot = keys[(left + right) >> 1];
let i = left - 1;
let j = right + 1;
while (true) {
do i++; while (compare(keys[i], pivot) < 0);
do j--; while (compare(keys[j], pivot) > 0);
if (i >= j) break;
let tmp = keys[i];
keys[i] = keys[j];
keys[j] = tmp;
tmp = values[i];
values[i] = values[j];
values[j] = tmp;
}
sort(keys, values, left, j, compare);
sort(keys, values, j + 1, right, compare);
}