masonic
Version:
<hr> <div align="center"> <h1 align="center"> 🧱 masonic </h1>
405 lines (346 loc) • 8.28 kB
text/typescript
type Color = 0 | 1 | 2;
const RED = 0;
const BLACK = 1;
const NIL = 2;
const DELETE = 0;
const KEEP = 1;
type ListNode = {
index: number;
high: number;
next: ListNode | null;
};
interface TreeNode {
max: number;
low: number;
high: number;
// color
C: Color;
// P
P: TreeNode;
// right
R: TreeNode;
// left
L: TreeNode;
list: ListNode;
}
interface Tree {
root: TreeNode;
size: number;
}
function addInterval(treeNode: TreeNode, high: number, index: number): boolean {
let node: ListNode | null = treeNode.list;
let prevNode: ListNode | undefined;
while (node) {
if (node.index === index) return false;
if (high > node.high) break;
prevNode = node;
node = node.next;
}
if (!prevNode) treeNode.list = { index, high, next: node };
if (prevNode) prevNode.next = { index, high, next: prevNode.next };
return true;
}
function removeInterval(treeNode: TreeNode, index: number) {
let node: ListNode | null = treeNode.list;
if (node.index === index) {
if (node.next === null) return DELETE;
treeNode.list = node.next;
return KEEP;
}
let prevNode: ListNode | undefined = node;
node = node.next;
while (node !== null) {
if (node.index === index) {
prevNode.next = node.next;
return KEEP;
}
prevNode = node;
node = node.next;
}
}
const NULL_NODE: TreeNode = {
low: 0,
max: 0,
high: 0,
C: NIL,
// @ts-expect-error
P: undefined,
// @ts-expect-error
R: undefined,
// @ts-expect-error
L: undefined,
// @ts-expect-error
list: undefined,
};
NULL_NODE.P = NULL_NODE;
NULL_NODE.L = NULL_NODE;
NULL_NODE.R = NULL_NODE;
function updateMax(node: TreeNode) {
const max = node.high;
if (node.L === NULL_NODE && node.R === NULL_NODE) node.max = max;
else if (node.L === NULL_NODE) node.max = Math.max(node.R.max, max);
else if (node.R === NULL_NODE) node.max = Math.max(node.L.max, max);
else node.max = Math.max(Math.max(node.L.max, node.R.max), max);
}
function updateMaxUp(node: TreeNode) {
let x = node;
while (x.P !== NULL_NODE) {
updateMax(x.P);
x = x.P;
}
}
function rotateLeft(tree: Tree, x: TreeNode) {
if (x.R === NULL_NODE) return;
const y = x.R;
x.R = y.L;
if (y.L !== NULL_NODE) y.L.P = x;
y.P = x.P;
if (x.P === NULL_NODE) tree.root = y;
else if (x === x.P.L) x.P.L = y;
else x.P.R = y;
y.L = x;
x.P = y;
updateMax(x);
updateMax(y);
}
function rotateRight(tree: Tree, x: TreeNode) {
if (x.L === NULL_NODE) return;
const y = x.L;
x.L = y.R;
if (y.R !== NULL_NODE) y.R.P = x;
y.P = x.P;
if (x.P === NULL_NODE) tree.root = y;
else if (x === x.P.R) x.P.R = y;
else x.P.L = y;
y.R = x;
x.P = y;
updateMax(x);
updateMax(y);
}
function replaceNode(tree: Tree, x: TreeNode, y: TreeNode) {
if (x.P === NULL_NODE) tree.root = y;
else if (x === x.P.L) x.P.L = y;
else x.P.R = y;
y.P = x.P;
}
function fixRemove(tree: Tree, x: TreeNode) {
let w;
while (x !== NULL_NODE && x.C === BLACK) {
if (x === x.P.L) {
w = x.P.R;
if (w.C === RED) {
w.C = BLACK;
x.P.C = RED;
rotateLeft(tree, x.P);
w = x.P.R;
}
if (w.L.C === BLACK && w.R.C === BLACK) {
w.C = RED;
x = x.P;
} else {
if (w.R.C === BLACK) {
w.L.C = BLACK;
w.C = RED;
rotateRight(tree, w);
w = x.P.R;
}
w.C = x.P.C;
x.P.C = BLACK;
w.R.C = BLACK;
rotateLeft(tree, x.P);
x = tree.root;
}
} else {
w = x.P.L;
if (w.C === RED) {
w.C = BLACK;
x.P.C = RED;
rotateRight(tree, x.P);
w = x.P.L;
}
if (w.R.C === BLACK && w.L.C === BLACK) {
w.C = RED;
x = x.P;
} else {
if (w.L.C === BLACK) {
w.R.C = BLACK;
w.C = RED;
rotateLeft(tree, w);
w = x.P.L;
}
w.C = x.P.C;
x.P.C = BLACK;
w.L.C = BLACK;
rotateRight(tree, x.P);
x = tree.root;
}
}
}
x.C = BLACK;
}
function minimumTree(x: TreeNode) {
while (x.L !== NULL_NODE) x = x.L;
return x;
}
function fixInsert(tree: Tree, z: TreeNode) {
let y: TreeNode;
while (z.P.C === RED) {
if (z.P === z.P.P.L) {
y = z.P.P.R;
if (y.C === RED) {
z.P.C = BLACK;
y.C = BLACK;
z.P.P.C = RED;
z = z.P.P;
} else {
if (z === z.P.R) {
z = z.P;
rotateLeft(tree, z);
}
z.P.C = BLACK;
z.P.P.C = RED;
rotateRight(tree, z.P.P);
}
} else {
y = z.P.P.L;
if (y.C === RED) {
z.P.C = BLACK;
y.C = BLACK;
z.P.P.C = RED;
z = z.P.P;
} else {
if (z === z.P.L) {
z = z.P;
rotateRight(tree, z);
}
z.P.C = BLACK;
z.P.P.C = RED;
rotateLeft(tree, z.P.P);
}
}
}
tree.root.C = BLACK;
}
export interface IIntervalTree {
insert(low: number, high: number, index: number): void;
remove(index: number): void;
search(
low: number,
high: number,
callback: (index: number, low: number) => any
): void;
size: number;
}
export function createIntervalTree(): IIntervalTree {
const tree = {
root: NULL_NODE,
size: 0,
};
// we know these indexes are a consistent, safe way to make look ups
// for our case so it's a solid O(1) alternative to
// the O(log n) searchNode() in typical interval trees
const indexMap: Record<number, TreeNode> = {};
return {
insert(low, high, index) {
let x: TreeNode = tree.root;
let y: TreeNode = NULL_NODE;
while (x !== NULL_NODE) {
y = x;
if (low === y.low) break;
if (low < x.low) x = x.L;
else x = x.R;
}
if (low === y.low && y !== NULL_NODE) {
if (!addInterval(y, high, index)) return;
y.high = Math.max(y.high, high);
updateMax(y);
updateMaxUp(y);
indexMap[index] = y;
tree.size++;
return;
}
const z: TreeNode = {
low,
high,
max: high,
C: RED,
P: y,
L: NULL_NODE,
R: NULL_NODE,
list: { index, high, next: null },
};
if (y === NULL_NODE) {
tree.root = z;
} else {
if (z.low < y.low) y.L = z;
else y.R = z;
updateMaxUp(z);
}
fixInsert(tree, z);
indexMap[index] = z;
tree.size++;
},
remove(index) {
const z = indexMap[index];
if (z === void 0) return;
delete indexMap[index];
const intervalResult = removeInterval(z, index);
if (intervalResult === void 0) return;
if (intervalResult === KEEP) {
z.high = z.list.high;
updateMax(z);
updateMaxUp(z);
tree.size--;
return;
}
let y = z;
let originalYColor = y.C;
let x: TreeNode;
if (z.L === NULL_NODE) {
x = z.R;
replaceNode(tree, z, z.R);
} else if (z.R === NULL_NODE) {
x = z.L;
replaceNode(tree, z, z.L);
} else {
y = minimumTree(z.R);
originalYColor = y.C;
x = y.R;
if (y.P === z) {
x.P = y;
} else {
replaceNode(tree, y, y.R);
y.R = z.R;
y.R.P = y;
}
replaceNode(tree, z, y);
y.L = z.L;
y.L.P = y;
y.C = z.C;
}
updateMax(x);
updateMaxUp(x);
if (originalYColor === BLACK) fixRemove(tree, x);
tree.size--;
},
search(low, high, callback) {
const stack = [tree.root];
while (stack.length !== 0) {
const node = stack.pop() as TreeNode;
if (node === NULL_NODE || low > node.max) continue;
if (node.L !== NULL_NODE) stack.push(node.L);
if (node.R !== NULL_NODE) stack.push(node.R);
if (node.low <= high && node.high >= low) {
let curr: ListNode | null = node.list;
while (curr !== null) {
if (curr.high >= low) callback(curr.index, node.low);
curr = curr.next;
}
}
}
},
get size() {
return tree.size;
},
};
}