WXL
4 天以前 3bd962a6d7f61239c020e2dbbeb7341e5b842dd1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*
    MIT License http://www.opensource.org/licenses/mit-license.php
    Author Tobias Koppers @sokra
*/
 
"use strict";
 
/**
 * Nested map structure used to index tuple prefixes until the final tuple
 * element can be stored in a `Set`.
 * @template K
 * @template V
 * @typedef {Map<K, InnerMap<K, V> | Set<V>>} InnerMap
 */
 
/**
 * Stores tuples of arbitrary length while preserving efficient prefix lookups
 * through a tree of maps that ends in a set of final values.
 * @template T
 * @template V
 */
class TupleSet {
    /**
     * Seeds the tuple set with an optional iterable of tuples.
     * @param {Iterable<[T, V, ...EXPECTED_ANY]>=} init init
     */
    constructor(init) {
        /** @type {InnerMap<T, V>} */
        this._map = new Map();
        this.size = 0;
        if (init) {
            for (const tuple of init) {
                this.add(...tuple);
            }
        }
    }
 
    /**
     * Adds a tuple to the set, creating any missing prefix maps along the way.
     * @param {[T, V, ...EXPECTED_ANY]} args tuple
     * @returns {void}
     */
    add(...args) {
        let map = this._map;
        for (let i = 0; i < args.length - 2; i++) {
            const arg = args[i];
            const innerMap = map.get(arg);
            if (innerMap === undefined) {
                map.set(arg, (map = new Map()));
            } else {
                map = /** @type {InnerMap<T, V>} */ (innerMap);
            }
        }
 
        const beforeLast = args[args.length - 2];
        let set = /** @type {Set<V>} */ (map.get(beforeLast));
        if (set === undefined) {
            map.set(beforeLast, (set = new Set()));
        }
 
        const last = args[args.length - 1];
        this.size -= set.size;
        set.add(last);
        this.size += set.size;
    }
 
    /**
     * Checks whether the exact tuple is already present in the set.
     * @param {[T, V, ...EXPECTED_ANY]} args tuple
     * @returns {boolean} true, if the tuple is in the Set
     */
    has(...args) {
        let map = this._map;
        for (let i = 0; i < args.length - 2; i++) {
            const arg = args[i];
            map = /** @type {InnerMap<T, V>} */ (map.get(arg));
            if (map === undefined) {
                return false;
            }
        }
 
        const beforeLast = args[args.length - 2];
        const set = map.get(beforeLast);
        if (set === undefined) {
            return false;
        }
 
        const last = args[args.length - 1];
        return set.has(last);
    }
 
    /**
     * Removes a tuple from the set when it is present.
     * @param {[T, V, ...EXPECTED_ANY]} args tuple
     * @returns {void}
     */
    delete(...args) {
        let map = this._map;
        for (let i = 0; i < args.length - 2; i++) {
            const arg = args[i];
            map = /** @type {InnerMap<T, V>} */ (map.get(arg));
            if (map === undefined) {
                return;
            }
        }
 
        const beforeLast = args[args.length - 2];
        const set = map.get(beforeLast);
        if (set === undefined) {
            return;
        }
 
        const last = args[args.length - 1];
        this.size -= set.size;
        set.delete(last);
        this.size += set.size;
    }
 
    /**
     * Iterates over every stored tuple by walking the nested map structure and
     * yielding each complete prefix plus its terminal set value.
     * @returns {Iterator<[T, V, ...EXPECTED_ANY]>} iterator
     */
    [Symbol.iterator]() {
        /**
         * Iterator type used while traversing nested tuple-prefix maps.
         * @template T, V
         * @typedef {MapIterator<[T, InnerMap<T, V> | Set<V>]>} IteratorStack
         */
 
        // This is difficult to type because we can have a map inside a map inside a map, etc. where the end is a set (each key is an argument)
        // But in basic use we only have 2 arguments in our methods, so we have `Map<K, Set<V>>`
        /** @type {IteratorStack<T, V>[]} */
        const iteratorStack = [];
        /** @type {[T?, V?, ...EXPECTED_ANY]} */
        const tuple = [];
        /** @type {SetIterator<V> | undefined} */
        let currentSetIterator;
 
        /**
         * Advances through nested maps until a terminal value set is reached or
         * every remaining branch has been exhausted.
         * @param {IteratorStack<T, V>} it iterator
         * @returns {boolean} result
         */
        const next = (it) => {
            const result = it.next();
            if (result.done) {
                if (iteratorStack.length === 0) return false;
                tuple.pop();
                return next(
                    /** @type {IteratorStack<T, V>} */
                    (iteratorStack.pop())
                );
            }
            const [key, value] = result.value;
            iteratorStack.push(it);
            tuple.push(key);
            if (value instanceof Set) {
                currentSetIterator = value[Symbol.iterator]();
                return true;
            }
            return next(value[Symbol.iterator]());
        };
 
        next(this._map[Symbol.iterator]());
 
        return {
            next() {
                while (currentSetIterator) {
                    const result = currentSetIterator.next();
                    if (result.done) {
                        tuple.pop();
                        if (
                            !next(
                                /** @type {IteratorStack<T, V>} */
                                (iteratorStack.pop())
                            )
                        ) {
                            currentSetIterator = undefined;
                        }
                    } else {
                        return {
                            done: false,
                            value:
                                /* eslint-disable unicorn/prefer-spread */
                                /** @type {[T, V, ...EXPECTED_ANY]} */
                                (tuple.concat(result.value))
                        };
                    }
                }
                return { done: true, value: undefined };
            }
        };
    }
}
 
module.exports = TupleSet;