export class Tree { /** @internal */ _root: TreeNode; constructor(root: TreeNode) { this._root = root; } get root(): T { return this._root.value; } parent(t: T): T | null { const p = this.pathFromRoot(t); return p.length > 1 ? p[p.length - 2] : null; } children(t: T): T[] { const n = findNode(t, this._root); return n ? n.children.map(t => t.value) : []; } firstChild(t: T): T | null { const n = findNode(t, this._root); return n && n.children.length > 0 ? n.children[0].value : null; } pathFromRoot(t: T): T[] { return findPath(t, this._root, []).map(s => s.value); } contains(tree: Tree): boolean { return contains(this._root, tree._root); } } export function rootNode(tree: Tree): TreeNode { return tree._root; } function findNode(expected: T, c: TreeNode): TreeNode | null { if (expected === c.value) return c; for (let cc of c.children) { const r = findNode(expected, cc); if (r) return r; } return null; } function findPath(expected: T, c: TreeNode, collected: TreeNode[]): TreeNode[] { collected.push(c); if (expected === c.value) return collected; for (let cc of c.children) { const cloned = collected.slice(0); const r = findPath(expected, cc, cloned); if (r) return r; } return []; } function contains(tree: TreeNode, subtree: TreeNode): boolean { if (tree.value !== subtree.value) return false; for (let subtreeNode of subtree.children) { const s = tree.children.filter(child => child.value === subtreeNode.value); if (s.length === 0) return false; if (!contains(s[0], subtreeNode)) return false; } return true; } export class TreeNode { constructor(public value: T, public children: TreeNode[]) {} }