function buildUsersTrie(users) {
    const rootNode = new trie();
    users.forEach(([id, name]) => {
        rootNode.insert(name.toLowerCase(), [id, name]);
    });
    return JSON.stringify(rootNode.serialize());
}
class trie_node {
    constructor() {
        this.terminal = false;
        this.children = new Map();
    }
    serialize() {
        const { terminal, value, children } = this;
        let mapped = {};
        let numChildren = 0;
        Object.keys(Object.fromEntries(children)).forEach((childKey) => {
            numChildren += 1;
            mapped[childKey] = children.get(childKey).serialize();
        });
        return {
            t: this.terminal ? 1 : 0,
            v: value,
            c: numChildren > 0 ? mapped : undefined,
        };
    }
}
class trie {
    constructor() {
        this.root = new trie_node();
        this.elements = 0;
    }
    serialize() {
        return this.root.serialize();
    }
    get length() {
        return this.elements;
    }
    get(key) {
        const node = this.getNode(key);
        if (node) {
            return node.value;
        }
        return null;
    }
    contains(key) {
        const node = this.getNode(key);
        return !!node;
    }
    insert(key, value) {
        let node = this.root;
        let remaining = key;
        while (remaining.length > 0) {
            let child = null;
            for (const childKey of node.children.keys()) {
                const prefix = this.commonPrefix(remaining, childKey);
                if (!prefix.length) {
                    continue;
                }
                if (prefix.length === childKey.length) {
                    // enter child node
                    child = node.children.get(childKey);
                    remaining = remaining.slice(childKey.length);
                    break;
                }
                else {
                    // split the child
                    child = new trie_node();
                    child.children.set(childKey.slice(prefix.length), node.children.get(childKey));
                    node.children.delete(childKey);
                    node.children.set(prefix, child);
                    remaining = remaining.slice(prefix.length);
                    break;
                }
            }
            if (!child && remaining.length) {
                child = new trie_node();
                node.children.set(remaining, child);
                remaining = "";
            }
            node = child;
        }
        if (!node.terminal) {
            node.terminal = true;
            this.elements += 1;
        }
        node.value = value;
    }
    remove(key) {
        const node = this.getNode(key);
        if (node) {
            node.terminal = false;
            this.elements -= 1;
        }
    }
    map(prefix, func) {
        const mapped = [];
        const node = this.getNode(prefix);
        const stack = [];
        if (node) {
            stack.push([prefix, node]);
        }
        while (stack.length) {
            const [key, node] = stack.pop();
            if (node.terminal) {
                mapped.push(func(key, node.value));
            }
            for (const c of node.children.keys()) {
                stack.push([key + c, node.children.get(c)]);
            }
        }
        return mapped;
    }
    getNode(key) {
        let node = this.root;
        let remaining = key;
        while (node && remaining.length > 0) {
            let child = null;
            for (let i = 1; i <= remaining.length; i += 1) {
                child = node.children.get(remaining.slice(0, i));
                if (child) {
                    remaining = remaining.slice(i);
                    break;
                }
            }
            node = child;
        }
        return remaining.length === 0 && node && node.terminal ? node : null;
    }
    commonPrefix(a, b) {
        const shortest = Math.min(a.length, b.length);
        let i = 0;
        for (; i < shortest; i += 1) {
            if (a[i] !== b[i]) {
                break;
            }
        }
        return a.slice(0, i);
    }
}
