"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isCircular = exports.countWords = exports.countNodes = exports.findNode = exports.has = exports.createTriFromList = exports.createRoot = exports.iteratorTrieWords = exports.iterateTrie = exports.walk = exports.orderTrie = exports.isWordTerminationNode = exports.insert = void 0; const gensequence_1 = require("gensequence"); const TrieNode_1 = require("./TrieNode"); const walker_1 = require("./walker"); function insert(text, node = {}) { if (text.length) { const head = text[0]; const tail = text.slice(1); node.c = node.c || new TrieNode_1.ChildMap(); node.c.set(head, insert(tail, node.c.get(head))); } else { node.f = (node.f || 0) | TrieNode_1.FLAG_WORD; } return node; } exports.insert = insert; function isWordTerminationNode(node) { return ((node.f || 0) & TrieNode_1.FLAG_WORD) === TrieNode_1.FLAG_WORD; } exports.isWordTerminationNode = isWordTerminationNode; /** * Sorts the nodes in a trie in place. */ function orderTrie(node) { if (!node.c) return; const nodes = [...node.c].sort(([a], [b]) => (a < b ? -1 : 1)); node.c = new Map(nodes); for (const n of node.c) { orderTrie(n[1]); } } exports.orderTrie = orderTrie; /** * Generator an iterator that will walk the Trie parent then children in a depth first fashion that preserves sorted order. */ function walk(node) { return gensequence_1.genSequence(walker_1.walker(node)); } exports.walk = walk; exports.iterateTrie = walk; /** * Generate a Iterator that can walk a Trie and yield the words. */ function iteratorTrieWords(node) { return walk(node) .filter((r) => isWordTerminationNode(r.node)) .map((r) => r.text); } exports.iteratorTrieWords = iteratorTrieWords; function createRoot() { return {}; } exports.createRoot = createRoot; function createTriFromList(words) { const root = createRoot(); for (const word of words) { if (word.length) { insert(word, root); } } return root; } exports.createTriFromList = createTriFromList; function has(node, word) { let h = word.slice(0, 1); let t = word.slice(1); while (node.c && node.c.has(h)) { node = node.c.get(h); h = t.slice(0, 1); t = t.slice(1); } return !h.length && !!((node.f || 0) & TrieNode_1.FLAG_WORD); } exports.has = has; function findNode(node, prefix) { let h = prefix.slice(0, 1); let t = prefix.slice(1); let n = node; while (h.length && n && n.c) { n = n.c.get(h); h = t.slice(0, 1); t = t.slice(1); } return n; } exports.findNode = findNode; function countNodes(root) { const seen = new Set(); function walk(n) { if (seen.has(n)) return; seen.add(n); if (n.c) { [...n.c.values()].forEach((n) => walk(n)); } } walk(root); return seen.size; } exports.countNodes = countNodes; function countWords(root) { const visited = new Map(); function walk(n) { if (visited.has(n)) { return visited.get(n); } let cnt = n.f ? 1 : 0; // add the node to the set to avoid getting stuck on circular references. visited.set(n, cnt); if (!n.c) { return cnt; } for (const c of n.c.values()) { cnt += walk(c); } visited.set(n, cnt); return cnt; } return walk(root); } exports.countWords = countWords; function isCircular(root) { const seen = new Set(); const inStack = new Set(); function walk(n) { if (seen.has(n)) return { isCircular: false, allSeen: true }; if (inStack.has(n)) return { isCircular: true, allSeen: false }; inStack.add(n); let r = { isCircular: false, allSeen: true }; if (n.c) { r = [...n.c.values()].reduce((acc, n) => { if (acc.isCircular) return acc; const r = walk(n); r.allSeen = r.allSeen && acc.allSeen; return r; }, r); } if (r.allSeen) { seen.add(n); } inStack.delete(n); return r; } return walk(root).isCircular; } exports.isCircular = isCircular; //# sourceMappingURL=util.js.map