Files
turftracker/frontend/node_modules/splaytree/src/index.ts
2026-04-09 13:19:47 -05:00

685 lines
15 KiB
TypeScript

import Node from "./node";
import { Key, Value } from "./types";
export type Comparator<Key> = (a: Key, b: Key) => number;
export type Visitor<Key, Value> = (
node: Node<Key, Value>
) => undefined | boolean | void;
export type NodePrinter<Key, Value> = (node: Node<Key, Value>) => string;
/* follows "An implementation of top-down splaying"
* by D. Sleator <sleator@cs.cmu.edu> March 1992
*/
function DEFAULT_COMPARE(a: Key, b: Key): number {
return a > b ? 1 : a < b ? -1 : 0;
}
type TreeNodeList<Key, Value> = { head: Node<Key, Value> | null };
/**
* Simple top down splay, not requiring i to be in the tree t.
*/
function splay(
i: Key,
t: Node<Key, Value>,
comparator: Comparator<Key>
): Node<Key, Value> {
const N = new Node(null, null);
let l = N;
let r = N;
while (true) {
const cmp = comparator(i, t!.key);
//if (i < t.key) {
if (cmp < 0) {
if (t!.left === null) break;
//if (i < t.left.key) {
if (comparator(i, t.left.key) < 0) {
const y = t.left; /* rotate right */
t.left = y.right;
y.right = t;
t = y;
if (t.left === null) break;
}
r.left = t; /* link right */
r = t;
t = t.left;
//} else if (i > t.key) {
} else if (cmp > 0) {
if (t.right === null) break;
//if (i > t.right.key) {
if (comparator(i, t.right.key) > 0) {
const y = t.right; /* rotate left */
t.right = y.left;
y.left = t;
t = y;
if (t.right === null) break;
}
l.right = t; /* link left */
l = t;
t = t.right;
} else break;
}
/* assemble */
l.right = t.left;
r.left = t.right;
t.left = N.right;
t.right = N.left;
return t;
}
function insert(
i: Key,
data: Value,
t: Node<Key, Value>,
comparator: Comparator<Key>
): Node<Key, Value> {
const node = new Node(i, data);
if (t === null) {
node.left = node.right = null;
return node;
}
t = splay(i, t, comparator);
const cmp = comparator(i, t.key);
if (cmp < 0) {
node.left = t.left;
node.right = t;
t.left = null;
} else if (cmp >= 0) {
node.right = t.right;
node.left = t;
t.right = null;
}
return node;
}
function split(
key: Key,
v: Node<Key, Value>,
comparator: Comparator<Key>
): {
left: Node<Key, Value> | null;
right: Node<Key, Value> | null;
} {
let left = null;
let right = null;
if (v) {
v = splay(key, v, comparator);
const cmp = comparator(v.key, key);
if (cmp === 0) {
left = v.left;
right = v.right;
} else if (cmp < 0) {
right = v.right;
v.right = null;
left = v;
} else {
left = v.left;
v.left = null;
right = v;
}
}
return { left, right };
}
function merge(
left: Node<Key, Value> | null,
right: Node<Key, Value> | null,
comparator: Comparator<Key>
) {
if (right === null) return left;
if (left === null) return right;
right = splay(left.key, right, comparator);
right.left = left;
return right;
}
type StringCollector = (s: string) => void;
/**
* Prints level of the tree
*/
function printRow(
root: Node<Key, Value>,
prefix: string,
isTail: boolean,
out: StringCollector,
printNode: NodePrinter<Key, Value>
) {
if (root) {
out(`${prefix}${isTail ? "└── " : "├── "}${printNode(root)}\n`);
const indent = prefix + (isTail ? " " : "│ ");
if (root.left) printRow(root.left, indent, false, out, printNode);
if (root.right) printRow(root.right, indent, true, out, printNode);
}
}
export default class Tree<Key = number, Value = any> {
private _comparator: Comparator<Key>;
private _root: Node<Key, Value> | null = null;
private _size: number = 0;
constructor(comparator = DEFAULT_COMPARE) {
this._comparator = comparator;
}
/**
* Inserts a key, allows duplicates
*/
public insert(key: Key, data?: Value): Node<Key, Value> {
this._size++;
return (this._root = insert(key, data, this._root!, this._comparator));
}
/**
* Adds a key, if it is not present in the tree
*/
public add(key: Key, data?: Value): Node<Key, Value> {
const node = new Node(key, data);
if (this._root === null) {
node.left = node.right = null;
this._size++;
this._root = node;
}
const comparator = this._comparator;
const t = splay(key, this._root, comparator);
const cmp = comparator(key, t.key);
if (cmp === 0) this._root = t;
else {
if (cmp < 0) {
node.left = t.left;
node.right = t;
t.left = null;
} else if (cmp > 0) {
node.right = t.right;
node.left = t;
t.right = null;
}
this._size++;
this._root = node;
}
return this._root;
}
/**
* @param {Key} key
* @return {Node|null}
*/
public remove(key: Key): void {
this._root = this._remove(key, this._root!, this._comparator);
}
/**
* Deletes i from the tree if it's there
*/
private _remove(i: Key, t: Node<Key, Value>, comparator: Comparator<Key>) {
let x;
if (t === null) return null;
t = splay(i, t, comparator);
const cmp = comparator(i, t.key);
if (cmp === 0) {
/* found it */
if (t.left === null) {
x = t.right!;
} else {
x = splay(i, t.left, comparator);
x.right = t.right;
}
this._size--;
return x;
}
return t; /* It wasn't there */
}
/**
* Removes and returns the node with smallest key
*/
public pop(): { key: Key; data: Value } | null {
let node = this._root;
if (node) {
while (node.left) node = node.left;
this._root = splay(node.key, this._root!, this._comparator);
this._root = this._remove(node.key, this._root, this._comparator);
return { key: node.key, data: node.data };
}
return null;
}
/**
* Find without splaying
*/
public findStatic(key: Key): Node<Key, Value> | null {
let current = this._root;
const compare = this._comparator;
while (current) {
const cmp = compare(key, current.key);
if (cmp === 0) return current;
else if (cmp < 0) current = current.left;
else current = current.right;
}
return null;
}
public find(key: Key): Node<Key, Value> | null {
if (this._root) {
this._root = splay(key, this._root, this._comparator);
if (this._comparator(key, this._root.key) !== 0) return null;
}
return this._root;
}
public contains(key: Key): boolean {
let current = this._root;
const compare = this._comparator;
while (current) {
const cmp = compare(key, current.key);
if (cmp === 0) return true;
else if (cmp < 0) current = current.left;
else current = current.right;
}
return false;
}
public forEach(visitor: Visitor<Key, Value>, ctx?: any): Tree<Key, Value> {
let current = this._root;
const Q = []; /* Initialize stack s */
let done = false;
while (!done) {
if (current !== null) {
Q.push(current);
current = current.left;
} else {
if (Q.length !== 0) {
current = Q.pop()!;
visitor.call(ctx, current);
current = current.right;
} else done = true;
}
}
return this;
}
/**
* Walk key range from `low` to `high`. Stops if `fn` returns a value.
*/
public range(
low: Key,
high: Key,
fn: Visitor<Key, Value>,
ctx?: any
): Tree<Key, Value> {
const Q = [];
const compare = this._comparator;
let node = this._root;
let 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 array of keys
*/
public keys(): Key[] {
const keys: Key[] = [];
this.forEach(({ key }) => {
keys.push(key);
});
return keys;
}
/**
* Returns array of all the data in the nodes
*/
public values(): Value[] {
const values: Value[] = [];
this.forEach(({ data }) => {
values.push(data);
});
return values;
}
public min(): Key | null {
if (this._root) return this.minNode(this._root)!.key;
return null;
}
public max(): Key | null {
if (this._root) return this.maxNode(this._root)!.key;
return null;
}
public minNode(t = this._root): Node<Key, Value> | null {
if (t) while (t.left) t = t.left;
return t;
}
public maxNode(t = this._root): Node<Key, Value> | null {
if (t) while (t.right) t = t.right;
return t;
}
/**
* Returns node at given index
*/
public at(index: number): Node<Key, Value> | null {
let current = this._root;
let done = false;
let i = 0;
const Q = [];
while (!done) {
if (current) {
Q.push(current);
current = current.left;
} else {
if (Q.length > 0) {
current = Q.pop()!;
if (i === index) return current;
i++;
current = current.right!;
} else done = true;
}
}
return null;
}
public next(d: Node<Key, Value>): Node<Key, Value> | null {
let root = this._root;
let successor = null;
if (d.right) {
successor = d.right;
while (successor.left) successor = successor.left;
return successor;
}
const comparator = this._comparator;
while (root) {
const cmp = comparator(d.key, root.key);
if (cmp === 0) break;
else if (cmp < 0) {
successor = root;
root = root.left;
} else root = root.right;
}
return successor;
}
public prev(d: Node<Key, Value>): Node<Key, Value> | null {
let root = this._root;
let predecessor = null;
if (d.left !== null) {
predecessor = d.left;
while (predecessor.right) predecessor = predecessor.right;
return predecessor;
}
const comparator = this._comparator;
while (root) {
const cmp = comparator(d.key, root.key);
if (cmp === 0) break;
else if (cmp < 0) root = root.left;
else {
predecessor = root;
root = root.right;
}
}
return predecessor;
}
public clear(): Tree<Key, Value> {
this._root = null;
this._size = 0;
return this;
}
public toList() {
return toList(this._root!);
}
/**
* Bulk-load items. Both array have to be same size
*/
public load(keys: Key[], values: Value[] = [], presort: boolean = false) {
let size = keys.length;
const comparator = this._comparator;
// sort if needed
if (presort) sort(keys, values, 0, size - 1, comparator);
if (this._root === null) {
// empty tree
this._root = loadRecursive(keys, values, 0, size);
this._size = size;
} else {
// that re-builds the whole tree from two in-order traversals
const mergedList = mergeLists(
this.toList(),
createList(keys, values),
comparator
);
size = this._size + size;
this._root = sortedListToBST({ head: mergedList }, 0, size);
}
return this;
}
public isEmpty(): boolean {
return this._root === null;
}
get size(): number {
return this._size;
}
get root(): Node<Key, Value> | null {
return this._root;
}
public toString(
printNode: NodePrinter<Key, Value> = (n) => String(n.key)
): string {
const out: string[] = [];
printRow(this._root!, "", true, (v) => out.push(v), printNode);
return out.join("");
}
public update(key: Key, newKey: Key, newData?: Value): void {
const comparator = this._comparator;
let { left, right } = split(key, this._root!, comparator);
if (comparator(key, newKey) < 0) {
right = insert(newKey, newData, right!, comparator);
} else {
left = insert(newKey, newData, left!, comparator);
}
this._root = merge(left, right, comparator);
}
public split(key: Key) {
return split(key, this._root!, this._comparator);
}
*[Symbol.iterator]() {
let current = this._root;
const Q: Node<Key, Value>[] = []; /* Initialize stack s */
let done = false;
while (!done) {
if (current !== null) {
Q.push(current);
current = current.left;
} else {
if (Q.length !== 0) {
current = Q.pop()!;
yield current;
current = current.right;
} else done = true;
}
}
}
}
function loadRecursive(
keys: Key[],
values: Value[],
start: number,
end: number
): Node<Key, Value> | null {
const size = end - start;
if (size > 0) {
const middle = start + Math.floor(size / 2);
const key = keys[middle];
const data = values[middle];
const node = new Node(key, data);
node.left = loadRecursive(keys, values, start, middle);
node.right = loadRecursive(keys, values, middle + 1, end);
return node;
}
return null;
}
function createList(keys: Key[], values: Value[]): Node<Key, Value> {
const head = new Node<Key, Value>(null, null);
let p: Node<Key, Value> = head;
for (let i = 0; i < keys.length; i++) {
p = p.next = new Node(keys[i], values[i]);
}
p.next = null;
return head.next!;
}
function toList(root: Node<Key, Value>): Node<Key, Value> {
let current = root;
const Q = [];
let done = false;
const head = new Node<Key, Value>(null, null);
let p = head;
while (!done) {
if (current) {
Q.push(current);
current = current.left!;
} else {
if (Q.length > 0) {
current = p = p.next = Q.pop()!;
current = current.right!;
} else done = true;
}
}
p.next = null; // that'll work even if the tree was empty
return head.next!;
}
function sortedListToBST(
list: TreeNodeList<Key, Value>,
start: number,
end: number
) {
const size = end - start;
if (size > 0) {
const middle = start + Math.floor(size / 2);
const left = sortedListToBST(list, start, middle);
const root = list.head;
root!.left = left;
list.head = list.head!.next;
root!.right = sortedListToBST(list, middle + 1, end);
return root!;
}
return null;
}
function mergeLists<Key, Value>(
l1: Node<Key, Value>,
l2: Node<Key, Value>,
compare: Comparator<Key>
): Node<Key, Value> {
const head: Node<Key, Value> = new Node<Key, Value>(null as Key, null); // dummy
let p = head;
let p1: Node<Key, Value> = l1;
let p2: Node<Key, Value> = l2;
while (p1 !== null && p2 !== null) {
if (compare(p1.key, p2.key) < 0) {
p.next = p1;
p1 = p1.next!;
} else {
p.next = p2;
p2 = p2.next!;
}
p = p.next;
}
if (p1 !== null) {
p.next = p1;
} else if (p2 !== null) {
p.next = p2;
}
return head.next!;
}
function sort(
keys: Key[],
values: Value[],
left: number,
right: number,
compare: Comparator<Key>
) {
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);
}