UNPKG

@everwhen/temporal

Version:
216 lines (215 loc) 6.16 kB
import { max } from "../fn/max.js"; import {} from "../interval.js"; export const Color = { RED: 'RED', BLACK: 'BLACK', }; class IntervalNode { interval; max; data; color = Color.RED; left = null; right = null; parent = null; constructor(interval, item) { this.interval = interval; this.max = interval.end; this.data = new Map([[item.id, item]]); } } export class IntervalTree { #root = null; constructor(entries) { if (entries) { for (const [interval, data] of entries) { this.insert(interval, data); } } } insert(interval, item) { let parent = null; let current = this.#root; while (current) { parent = current; current.max = max(current.max, interval.end); if (this.#compare(interval, current.interval) === 0) { current.data.set(item.id, item); return; } const cmpStart = interval.start.compare(current.interval.start); current = cmpStart < 0 ? current.left : current.right; } const node = new IntervalNode(interval, item); node.parent = parent; if (parent === null) { this.#root = node; this.#root.color = Color.BLACK; return; } if (interval.start.compare(parent.interval.start) < 0) { parent.left = node; } else { parent.right = node; } this.#insertFixup(node); } #compare(a, b) { const startComparison = a.start.compare(b.start); if (startComparison !== 0) { return startComparison; } return a.end.compare(b.end); } search(query, predicate, mapper = (args) => args) { const results = []; for (const result of this.#traverse(this.#root, query)) { if (predicate(result.interval)) { results.push(mapper(result)); } } return results; } /** * Returns number of items stored in the interval tree */ size() { let count = 0; for (const result of this) { count = count + result.data.size; } return count; } /** * Returns true if tree is empty */ isEmpty() { return this.#root === null; } /** * Clear tree */ clear() { this.#root = null; } *[Symbol.iterator]() { yield* this.#walk(this.#root); } *walk(query) { if (query) { yield* this.#traverse(this.#root, query); } else { yield* this.#walk(this.#root); } } *#walk(node) { if (!node) return; yield* this.#walk(node.left); yield { interval: node.interval, data: node.data }; yield* this.#walk(node.right); } *#traverse(node, query) { if (!node) return; if (node.left && node.left.max.compare(query.start) >= 0) { yield* this.#traverse(node.left, query); } yield { interval: node.interval, data: node.data }; if (node.right && query.end.compare(node.interval.start) >= 0) { yield* this.#traverse(node.right, query); } } #insertFixup(node) { while (node.parent?.color === Color.RED) { const parent = node.parent; const grandparent = parent.parent; if (parent === grandparent.left) { const uncle = grandparent.right; if (uncle?.color === Color.RED) { parent.color = Color.BLACK; uncle.color = Color.BLACK; grandparent.color = Color.RED; node = grandparent; } else { if (node === parent.right) { node = parent; this.#rotateLeft(node); } node.parent.color = Color.BLACK; node.parent.parent.color = Color.RED; this.#rotateRight(node.parent.parent); } } else { const uncle = grandparent.left; if (uncle?.color === Color.RED) { parent.color = Color.BLACK; uncle.color = Color.BLACK; grandparent.color = Color.RED; node = grandparent; } else { if (node === parent.left) { node = parent; this.#rotateRight(node); } node.parent.color = Color.BLACK; node.parent.parent.color = Color.RED; this.#rotateLeft(node.parent.parent); } } } this.#root.color = Color.BLACK; } #rotateLeft(x) { const y = x.right; 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; } y.left = x; x.parent = y; this.#updateMax(x); this.#updateMax(y); } #rotateRight(y) { const x = y.left; y.left = x.right; if (x.right) x.right.parent = y; x.parent = y.parent; if (!y.parent) { this.#root = x; } else if (y === y.parent.right) { y.parent.right = x; } else { y.parent.left = x; } x.right = y; y.parent = x; this.#updateMax(y); this.#updateMax(x); } #updateMax(node) { node.max = node.interval.end; if (node.left) node.max = max(node.max, node.left.max); if (node.right) node.max = max(node.max, node.right.max); } }