UNPKG

@rx-angular/cdk

Version:

@rx-angular/cdk is a Component Development Kit for ergonomic and highly performant angular applications. It helps to to build Large scale applications, UI libs, state management, rendering systems and much more. Furthermore the unique way of mixing reacti

1 lines 213 kB
{"version":3,"file":"cdk-template.mjs","sources":["../tmp-esm2022/template/lib/list-reconciliation.js","../tmp-esm2022/template/lib/utils.js","../tmp-esm2022/template/lib/list-view-handler.js","../tmp-esm2022/template/lib/render-error.js","../tmp-esm2022/template/lib/list-template-manager.js","../tmp-esm2022/template/lib/list-view-context.js","../tmp-esm2022/template/lib/model.js","../tmp-esm2022/template/lib/rx-live-collection.js","../tmp-esm2022/template/lib/template-manager.js","../tmp-esm2022/template/cdk-template.js"],"sourcesContent":["// copied from https://github.com/angular/angular/blob/main/packages/core/src/render3/list_reconciliation.ts\n/**\n * A type representing the live collection to be reconciled with any new (incoming) collection. This\n * is an adapter class that makes it possible to work with different internal data structures,\n * regardless of the actual values of the incoming collection.\n */\nexport class LiveCollection {\n destroy(item) {\n // noop by default\n }\n updateValue(index, value) {\n // noop by default\n }\n // operations below could be implemented on top of the operations defined so far, but having\n // them explicitly allow clear expression of intent and potentially more performant\n // implementations\n swap(index1, index2) {\n const startIdx = Math.min(index1, index2);\n const endIdx = Math.max(index1, index2);\n const endItem = this.detach(endIdx);\n if (endIdx - startIdx > 1) {\n const startItem = this.detach(startIdx);\n this.attach(startIdx, endItem);\n this.attach(endIdx, startItem);\n }\n else {\n this.attach(startIdx, endItem);\n }\n }\n move(prevIndex, newIdx) {\n this.attach(newIdx, this.detach(prevIndex));\n }\n}\nfunction valuesMatching(liveIdx, liveValue, newIdx, newValue, trackBy) {\n if (liveIdx === newIdx && Object.is(liveValue, newValue)) {\n // matching and no value identity to update\n return 1;\n }\n else if (Object.is(trackBy(liveIdx, liveValue), trackBy(newIdx, newValue))) {\n // matching but requires value identity update\n return -1;\n }\n return 0;\n}\nfunction recordDuplicateKeys(keyToIdx, key, idx) {\n const idxSoFar = keyToIdx.get(key);\n if (idxSoFar !== undefined) {\n idxSoFar.add(idx);\n }\n else {\n keyToIdx.set(key, new Set([idx]));\n }\n}\n/**\n * The live collection reconciliation algorithm that perform various in-place operations, so it\n * reflects the content of the new (incoming) collection.\n *\n * The reconciliation algorithm has 2 code paths:\n * - \"fast\" path that don't require any memory allocation;\n * - \"slow\" path that requires additional memory allocation for intermediate data structures used to\n * collect additional information about the live collection.\n * It might happen that the algorithm switches between the two modes in question in a single\n * reconciliation path - generally it tries to stay on the \"fast\" path as much as possible.\n *\n * The overall complexity of the algorithm is O(n + m) for speed and O(n) for memory (where n is the\n * length of the live collection and m is the length of the incoming collection). Given the problem\n * at hand the complexity / performance constraints makes it impossible to perform the absolute\n * minimum of operation to reconcile the 2 collections. The algorithm makes different tradeoffs to\n * stay within reasonable performance bounds and may apply sub-optimal number of operations in\n * certain situations.\n *\n * @param liveCollection the current, live collection;\n * @param newCollection the new, incoming collection;\n * @param trackByFn key generation function that determines equality between items in the life and\n * incoming collection;\n */\nexport function reconcile(liveCollection, newCollection, trackByFn) {\n let detachedItems = undefined;\n let liveKeysInTheFuture = undefined;\n let liveStartIdx = 0;\n let liveEndIdx = liveCollection.length - 1;\n const duplicateKeys = ngDevMode ? new Map() : undefined;\n if (Array.isArray(newCollection)) {\n let newEndIdx = newCollection.length - 1;\n while (liveStartIdx <= liveEndIdx && liveStartIdx <= newEndIdx) {\n // compare from the beginning\n const liveStartValue = liveCollection.at(liveStartIdx);\n const newStartValue = newCollection[liveStartIdx];\n if (ngDevMode) {\n recordDuplicateKeys(duplicateKeys, trackByFn(liveStartIdx, newStartValue), liveStartIdx);\n }\n const isStartMatching = valuesMatching(liveStartIdx, liveStartValue, liveStartIdx, newStartValue, trackByFn);\n if (isStartMatching !== 0) {\n if (isStartMatching < 0) {\n liveCollection.updateValue(liveStartIdx, newStartValue);\n }\n liveStartIdx++;\n continue;\n }\n // compare from the end\n // TODO(perf): do _all_ the matching from the end\n const liveEndValue = liveCollection.at(liveEndIdx);\n const newEndValue = newCollection[newEndIdx];\n if (ngDevMode) {\n recordDuplicateKeys(duplicateKeys, trackByFn(newEndIdx, newEndValue), newEndIdx);\n }\n const isEndMatching = valuesMatching(liveEndIdx, liveEndValue, newEndIdx, newEndValue, trackByFn);\n if (isEndMatching !== 0) {\n if (isEndMatching < 0) {\n liveCollection.updateValue(liveEndIdx, newEndValue);\n }\n liveEndIdx--;\n newEndIdx--;\n continue;\n }\n // Detect swap and moves:\n const liveStartKey = trackByFn(liveStartIdx, liveStartValue);\n const liveEndKey = trackByFn(liveEndIdx, liveEndValue);\n const newStartKey = trackByFn(liveStartIdx, newStartValue);\n if (Object.is(newStartKey, liveEndKey)) {\n const newEndKey = trackByFn(newEndIdx, newEndValue);\n // detect swap on both ends;\n if (Object.is(newEndKey, liveStartKey)) {\n liveCollection.swap(liveStartIdx, liveEndIdx);\n liveCollection.updateValue(liveEndIdx, newEndValue);\n newEndIdx--;\n liveEndIdx--;\n }\n else {\n // the new item is the same as the live item with the end pointer - this is a move forward\n // to an earlier index;\n liveCollection.move(liveEndIdx, liveStartIdx);\n }\n liveCollection.updateValue(liveStartIdx, newStartValue);\n liveStartIdx++;\n continue;\n }\n // Fallback to the slow path: we need to learn more about the content of the live and new\n // collections.\n detachedItems ??= new UniqueValueMultiKeyMap();\n liveKeysInTheFuture ??= initLiveItemsInTheFuture(liveCollection, liveStartIdx, liveEndIdx, trackByFn);\n // Check if I'm inserting a previously detached item: if so, attach it here\n if (attachPreviouslyDetached(liveCollection, detachedItems, liveStartIdx, newStartKey)) {\n liveCollection.updateValue(liveStartIdx, newStartValue);\n liveStartIdx++;\n liveEndIdx++;\n }\n else if (!liveKeysInTheFuture.has(newStartKey)) {\n // Check if we seen a new item that doesn't exist in the old collection and must be INSERTED\n const newItem = liveCollection.create(liveStartIdx, newCollection[liveStartIdx]);\n liveCollection.attach(liveStartIdx, newItem);\n liveStartIdx++;\n liveEndIdx++;\n }\n else {\n // We know that the new item exists later on in old collection but we don't know its index\n // and as the consequence can't move it (don't know where to find it). Detach the old item,\n // hoping that it unlocks the fast path again.\n detachedItems.set(liveStartKey, liveCollection.detach(liveStartIdx));\n liveEndIdx--;\n }\n }\n // Final cleanup steps:\n // - more items in the new collection => insert\n while (liveStartIdx <= newEndIdx) {\n createOrAttach(liveCollection, detachedItems, trackByFn, liveStartIdx, newCollection[liveStartIdx]);\n liveStartIdx++;\n }\n }\n else if (newCollection != null) {\n // iterable - immediately fallback to the slow path\n const newCollectionIterator = newCollection[Symbol.iterator]();\n let newIterationResult = newCollectionIterator.next();\n while (!newIterationResult.done && liveStartIdx <= liveEndIdx) {\n const liveValue = liveCollection.at(liveStartIdx);\n const newValue = newIterationResult.value;\n if (ngDevMode) {\n recordDuplicateKeys(duplicateKeys, trackByFn(liveStartIdx, newValue), liveStartIdx);\n }\n const isStartMatching = valuesMatching(liveStartIdx, liveValue, liveStartIdx, newValue, trackByFn);\n if (isStartMatching !== 0) {\n // found a match - move on, but update value\n if (isStartMatching < 0) {\n liveCollection.updateValue(liveStartIdx, newValue);\n }\n liveStartIdx++;\n newIterationResult = newCollectionIterator.next();\n }\n else {\n detachedItems ??= new UniqueValueMultiKeyMap();\n liveKeysInTheFuture ??= initLiveItemsInTheFuture(liveCollection, liveStartIdx, liveEndIdx, trackByFn);\n // Check if I'm inserting a previously detached item: if so, attach it here\n const newKey = trackByFn(liveStartIdx, newValue);\n if (attachPreviouslyDetached(liveCollection, detachedItems, liveStartIdx, newKey)) {\n liveCollection.updateValue(liveStartIdx, newValue);\n liveStartIdx++;\n liveEndIdx++;\n newIterationResult = newCollectionIterator.next();\n }\n else if (!liveKeysInTheFuture.has(newKey)) {\n liveCollection.attach(liveStartIdx, liveCollection.create(liveStartIdx, newValue));\n liveStartIdx++;\n liveEndIdx++;\n newIterationResult = newCollectionIterator.next();\n }\n else {\n // it is a move forward - detach the current item without advancing in collections\n const liveKey = trackByFn(liveStartIdx, liveValue);\n detachedItems.set(liveKey, liveCollection.detach(liveStartIdx));\n liveEndIdx--;\n }\n }\n }\n // this is a new item as we run out of the items in the old collection - create or attach a\n // previously detached one\n while (!newIterationResult.done) {\n createOrAttach(liveCollection, detachedItems, trackByFn, liveCollection.length, newIterationResult.value);\n newIterationResult = newCollectionIterator.next();\n }\n }\n // Cleanups common to the array and iterable:\n // - more items in the live collection => delete starting from the end;\n while (liveStartIdx <= liveEndIdx) {\n liveCollection.destroy(liveCollection.detach(liveEndIdx--));\n }\n // - destroy items that were detached but never attached again.\n detachedItems?.forEach((item) => {\n liveCollection.destroy(item);\n });\n // report duplicate keys (dev mode only)\n if (ngDevMode) {\n const duplicatedKeysMsg = [];\n for (const [key, idxSet] of duplicateKeys) {\n if (idxSet.size > 1) {\n const idx = [...idxSet].sort((a, b) => a - b);\n for (let i = 1; i < idx.length; i++) {\n duplicatedKeysMsg.push(`key \"${key}\" at index \"${idx[i - 1]}\" and \"${idx[i]}\"`);\n }\n }\n }\n if (duplicatedKeysMsg.length > 0) {\n const message = 'The provided track expression resulted in duplicated keys for a given collection. ' +\n 'Adjust the tracking expression such that it uniquely identifies all the items in the collection. ' +\n 'Duplicated keys were: \\n' +\n duplicatedKeysMsg.join(', \\n') +\n '.';\n // tslint:disable-next-line:no-console\n console.warn(message);\n }\n }\n}\nfunction attachPreviouslyDetached(prevCollection, detachedItems, index, key) {\n if (detachedItems !== undefined && detachedItems.has(key)) {\n prevCollection.attach(index, detachedItems.get(key));\n detachedItems.delete(key);\n return true;\n }\n return false;\n}\nfunction createOrAttach(liveCollection, detachedItems, trackByFn, index, value) {\n if (!attachPreviouslyDetached(liveCollection, detachedItems, index, trackByFn(index, value))) {\n const newItem = liveCollection.create(index, value);\n liveCollection.attach(index, newItem);\n }\n else {\n liveCollection.updateValue(index, value);\n }\n}\nfunction initLiveItemsInTheFuture(liveCollection, start, end, trackByFn) {\n const keys = new Set();\n for (let i = start; i <= end; i++) {\n keys.add(trackByFn(i, liveCollection.at(i)));\n }\n return keys;\n}\n/**\n * A specific, partial implementation of the Map interface with the following characteristics:\n * - allows multiple values for a given key;\n * - maintain FIFO order for multiple values corresponding to a given key;\n * - assumes that all values are unique.\n *\n * The implementation aims at having the minimal overhead for cases where keys are _not_ duplicated\n * (the most common case in the list reconciliation algorithm). To achieve this, the first value for\n * a given key is stored in a regular map. Then, when more values are set for a given key, we\n * maintain a form of linked list in a separate map. To maintain this linked list we assume that all\n * values (in the entire collection) are unique.\n */\nexport class UniqueValueMultiKeyMap {\n constructor() {\n // A map from a key to the first value corresponding to this key.\n this.kvMap = new Map();\n // A map that acts as a linked list of values - each value maps to the next value in this \"linked\n // list\" (this only works if values are unique). Allocated lazily to avoid memory consumption when\n // there are no duplicated values.\n this._vMap = undefined;\n }\n has(key) {\n return this.kvMap.has(key);\n }\n delete(key) {\n if (!this.has(key))\n return false;\n const value = this.kvMap.get(key);\n if (this._vMap !== undefined && this._vMap.has(value)) {\n this.kvMap.set(key, this._vMap.get(value));\n this._vMap.delete(value);\n }\n else {\n this.kvMap.delete(key);\n }\n return true;\n }\n get(key) {\n return this.kvMap.get(key);\n }\n set(key, value) {\n if (this.kvMap.has(key)) {\n let prevValue = this.kvMap.get(key);\n // Note: we don't use `assertNotSame`, because the value needs to be stringified even if\n // there is no error which can freeze the browser for large values (see #58509).\n /*if (ngDevMode && prevValue === value) {\n throw new Error(\n `Detected a duplicated value ${value} for the key ${key}`,\n );\n }*/\n if (this._vMap === undefined) {\n this._vMap = new Map();\n }\n const vMap = this._vMap;\n while (vMap.has(prevValue)) {\n prevValue = vMap.get(prevValue);\n }\n vMap.set(prevValue, value);\n }\n else {\n this.kvMap.set(key, value);\n }\n }\n forEach(cb) {\n // eslint-disable-next-line prefer-const\n for (let [key, value] of this.kvMap) {\n cb(value, key);\n if (this._vMap !== undefined) {\n const vMap = this._vMap;\n while (vMap.has(value)) {\n value = vMap.get(value);\n cb(value, key);\n }\n }\n }\n }\n}\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdC1yZWNvbmNpbGlhdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMvY2RrL3RlbXBsYXRlL3NyYy9saWIvbGlzdC1yZWNvbmNpbGlhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSw0R0FBNEc7QUFVNUc7Ozs7R0FJRztBQUNILE1BQU0sT0FBZ0IsY0FBYztJQU1sQyxPQUFPLENBQUMsSUFBTztRQUNiLGtCQUFrQjtJQUNwQixDQUFDO0lBQ0QsV0FBVyxDQUFDLEtBQWEsRUFBRSxLQUFRO1FBQ2pDLGtCQUFrQjtJQUNwQixDQUFDO0lBRUQsNEZBQTRGO0lBQzVGLG1GQUFtRjtJQUNuRixrQkFBa0I7SUFDbEIsSUFBSSxDQUFDLE1BQWMsRUFBRSxNQUFjO1FBQ2pDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQzFDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3hDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDcEMsSUFBSSxNQUFNLEdBQUcsUUFBUSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQzFCLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDeEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDL0IsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDakMsQ0FBQzthQUFNLENBQUM7WUFDTixJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNqQyxDQUFDO0lBQ0gsQ0FBQztJQUNELElBQUksQ0FBQyxTQUFpQixFQUFFLE1BQWM7UUFDcEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO0lBQzlDLENBQUM7Q0FDRjtBQUVELFNBQVMsY0FBYyxDQUNyQixPQUFlLEVBQ2YsU0FBWSxFQUNaLE1BQWMsRUFDZCxRQUFXLEVBQ1gsT0FBMkI7SUFFM0IsSUFBSSxPQUFPLEtBQUssTUFBTSxJQUFJLE1BQU0sQ0FBQyxFQUFFLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxFQUFFLENBQUM7UUFDekQsMkNBQTJDO1FBQzNDLE9BQU8sQ0FBQyxDQUFDO0lBQ1gsQ0FBQztTQUFNLElBQ0wsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUMsRUFDakUsQ0FBQztRQUNELDhDQUE4QztRQUM5QyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ1osQ0FBQztJQUVELE9BQU8sQ0FBQyxDQUFDO0FBQ1gsQ0FBQztBQUVELFNBQVMsbUJBQW1CLENBQzFCLFFBQW1DLEVBQ25DLEdBQVksRUFDWixHQUFXO0lBRVgsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUVuQyxJQUFJLFFBQVEsS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUMzQixRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3BCLENBQUM7U0FBTSxDQUFDO1FBQ04sUUFBUSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDcEMsQ0FBQztBQUNILENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNCRztBQUNILE1BQU0sVUFBVSxTQUFTLENBQ3ZCLGNBQW9DLEVBQ3BDLGFBQTZDLEVBQzdDLFNBQTZCO0lBRTdCLElBQUksYUFBYSxHQUFtRCxTQUFTLENBQUM7SUFDOUUsSUFBSSxtQkFBbUIsR0FBNkIsU0FBUyxDQUFDO0lBRTlELElBQUksWUFBWSxHQUFHLENBQUMsQ0FBQztJQUNyQixJQUFJLFVBQVUsR0FBRyxjQUFjLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUUzQyxNQUFNLGFBQWEsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxFQUF3QixDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFFOUUsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUM7UUFDakMsSUFBSSxTQUFTLEdBQUcsYUFBYSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7UUFFekMsT0FBTyxZQUFZLElBQUksVUFBVSxJQUFJLFlBQVksSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUMvRCw2QkFBNkI7WUFDN0IsTUFBTSxjQUFjLEdBQUcsY0FBYyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUN2RCxNQUFNLGFBQWEsR0FBRyxhQUFhLENBQUMsWUFBWSxDQUFDLENBQUM7WUFFbEQsSUFBSSxTQUFTLEVBQUUsQ0FBQztnQkFDZCxtQkFBbUIsQ0FDakIsYUFBYyxFQUNkLFNBQVMsQ0FBQyxZQUFZLEVBQUUsYUFBYSxDQUFDLEVBQ3RDLFlBQVksQ0FDYixDQUFDO1lBQ0osQ0FBQztZQUVELE1BQU0sZUFBZSxHQUFHLGNBQWMsQ0FDcEMsWUFBWSxFQUNaLGNBQWMsRUFDZCxZQUFZLEVBQ1osYUFBYSxFQUNiLFNBQVMsQ0FDVixDQUFDO1lBQ0YsSUFBSSxlQUFlLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQzFCLElBQUksZUFBZSxHQUFHLENBQUMsRUFBRSxDQUFDO29CQUN4QixjQUFjLENBQUMsV0FBVyxDQUFDLFlBQVksRUFBRSxhQUFhLENBQUMsQ0FBQztnQkFDMUQsQ0FBQztnQkFDRCxZQUFZLEVBQUUsQ0FBQztnQkFDZixTQUFTO1lBQ1gsQ0FBQztZQUVELHVCQUF1QjtZQUN2QixpREFBaUQ7WUFDakQsTUFBTSxZQUFZLEdBQUcsY0FBYyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUNuRCxNQUFNLFdBQVcsR0FBRyxhQUFhLENBQUMsU0FBUyxDQUFDLENBQUM7WUFFN0MsSUFBSSxTQUFTLEVBQUUsQ0FBQztnQkFDZCxtQkFBbUIsQ0FDakIsYUFBYyxFQUNkLFNBQVMsQ0FBQyxTQUFTLEVBQUUsV0FBVyxDQUFDLEVBQ2pDLFNBQVMsQ0FDVixDQUFDO1lBQ0osQ0FBQztZQUVELE1BQU0sYUFBYSxHQUFHLGNBQWMsQ0FDbEMsVUFBVSxFQUNWLFlBQVksRUFDWixTQUFTLEVBQ1QsV0FBVyxFQUNYLFNBQVMsQ0FDVixDQUFDO1lBQ0YsSUFBSSxhQUFhLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ3hCLElBQUksYUFBYSxHQUFHLENBQUMsRUFBRSxDQUFDO29CQUN0QixjQUFjLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxXQUFXLENBQUMsQ0FBQztnQkFDdEQsQ0FBQztnQkFDRCxVQUFVLEVBQUUsQ0FBQztnQkFDYixTQUFTLEVBQUUsQ0FBQztnQkFDWixTQUFTO1lBQ1gsQ0FBQztZQUVELHlCQUF5QjtZQUN6QixNQUFNLFlBQVksR0FBRyxTQUFTLENBQUMsWUFBWSxFQUFFLGNBQWMsQ0FBQyxDQUFDO1lBQzdELE1BQU0sVUFBVSxHQUFHLFNBQVMsQ0FBQyxVQUFVLEVBQUUsWUFBWSxDQUFDLENBQUM7WUFDdkQsTUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLFlBQVksRUFBRSxhQUFhLENBQUMsQ0FBQztZQUMzRCxJQUFJLE1BQU0sQ0FBQyxFQUFFLENBQUMsV0FBVyxFQUFFLFVBQVUsQ0FBQyxFQUFFLENBQUM7Z0JBQ3ZDLE1BQU0sU0FBUyxHQUFHLFNBQVMsQ0FBQyxTQUFTLEVBQUUsV0FBVyxDQUFDLENBQUM7Z0JBQ3BELDRCQUE0QjtnQkFDNUIsSUFBSSxNQUFNLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxZQUFZLENBQUMsRUFBRSxDQUFDO29CQUN2QyxjQUFjLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxVQUFVLENBQUMsQ0FBQztvQkFDOUMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsV0FBVyxDQUFDLENBQUM7b0JBQ3BELFNBQVMsRUFBRSxDQUFDO29CQUNaLFVBQVUsRUFBRSxDQUFDO2dCQUNmLENBQUM7cUJBQU0sQ0FBQztvQkFDTiwwRkFBMEY7b0JBQzFGLHVCQUF1QjtvQkFDdkIsY0FBYyxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsWUFBWSxDQUFDLENBQUM7Z0JBQ2hELENBQUM7Z0JBQ0QsY0FBYyxDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsYUFBYSxDQUFDLENBQUM7Z0JBQ3hELFlBQVksRUFBRSxDQUFDO2dCQUNmLFNBQVM7WUFDWCxDQUFDO1lBRUQseUZBQXlGO1lBQ3pGLGVBQWU7WUFDZixhQUFhLEtBQUssSUFBSSxzQkFBc0IsRUFBRSxDQUFDO1lBQy9DLG1CQUFtQixLQUFLLHdCQUF3QixDQUM5QyxjQUFjLEVBQ2QsWUFBWSxFQUNaLFVBQVUsRUFDVixTQUFTLENBQ1YsQ0FBQztZQUVGLDJFQUEyRTtZQUMzRSxJQUNFLHdCQUF3QixDQUN0QixjQUFjLEVBQ2QsYUFBYSxFQUNiLFlBQVksRUFDWixXQUFXLENBQ1osRUFDRCxDQUFDO2dCQUNELGNBQWMsQ0FBQyxXQUFXLENBQUMsWUFBWSxFQUFFLGFBQWEsQ0FBQyxDQUFDO2dCQUN4RCxZQUFZLEVBQUUsQ0FBQztnQkFDZixVQUFVLEVBQUUsQ0FBQztZQUNmLENBQUM7aUJBQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDO2dCQUNqRCw0RkFBNEY7Z0JBQzVGLE1BQU0sT0FBTyxHQUFHLGNBQWMsQ0FBQyxNQUFNLENBQ25DLFlBQVksRUFDWixhQUFhLENBQUMsWUFBWSxDQUFDLENBQzVCLENBQUM7Z0JBQ0YsY0FBYyxDQUFDLE1BQU0sQ0FBQyxZQUFZLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQzdDLFlBQVksRUFBRSxDQUFDO2dCQUNmLFVBQVUsRUFBRSxDQUFDO1lBQ2YsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLDBGQUEwRjtnQkFDMUYsMkZBQTJGO2dCQUMzRiw4Q0FBOEM7Z0JBQzlDLGFBQWEsQ0FBQyxHQUFHLENBQUMsWUFBWSxFQUFFLGNBQWMsQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQztnQkFDckUsVUFBVSxFQUFFLENBQUM7WUFDZixDQUFDO1FBQ0gsQ0FBQztRQUVELHVCQUF1QjtRQUN2QiwrQ0FBK0M7UUFDL0MsT0FBTyxZQUFZLElBQUksU0FBUyxFQUFFLENBQUM7WUFDakMsY0FBYyxDQUNaLGNBQWMsRUFDZCxhQUFhLEVBQ2IsU0FBUyxFQUNULFlBQVksRUFDWixhQUFhLENBQUMsWUFBWSxDQUFDLENBQzVCLENBQUM7WUFDRixZQUFZLEVBQUUsQ0FBQztRQUNqQixDQUFDO0lBQ0gsQ0FBQztTQUFNLElBQUksYUFBYSxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ2pDLG1EQUFtRDtRQUNuRCxNQUFNLHFCQUFxQixHQUFHLGFBQWEsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUMvRCxJQUFJLGtCQUFrQixHQUFHLHFCQUFxQixDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3RELE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLElBQUksWUFBWSxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQzlELE1BQU0sU0FBUyxHQUFHLGNBQWMsQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLENBQUM7WUFDbEQsTUFBTSxRQUFRLEdBQUcsa0JBQWtCLENBQUMsS0FBSyxDQUFDO1lBRTFDLElBQUksU0FBUyxFQUFFLENBQUM7Z0JBQ2QsbUJBQW1CLENBQ2pCLGFBQWMsRUFDZCxTQUFTLENBQUMsWUFBWSxFQUFFLFFBQVEsQ0FBQyxFQUNqQyxZQUFZLENBQ2IsQ0FBQztZQUNKLENBQUM7WUFFRCxNQUFNLGVBQWUsR0FBRyxjQUFjLENBQ3BDLFlBQVksRUFDWixTQUFTLEVBQ1QsWUFBWSxFQUNaLFFBQVEsRUFDUixTQUFTLENBQ1YsQ0FBQztZQUNGLElBQUksZUFBZSxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUMxQiw0Q0FBNEM7Z0JBQzVDLElBQUksZUFBZSxHQUFHLENBQUMsRUFBRSxDQUFDO29CQUN4QixjQUFjLENBQUMsV0FBVyxDQUFDLFlBQVksRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDckQsQ0FBQztnQkFDRCxZQUFZLEVBQUUsQ0FBQztnQkFDZixrQkFBa0IsR0FBRyxxQkFBcUIsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNwRCxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sYUFBYSxLQUFLLElBQUksc0JBQXNCLEVBQUUsQ0FBQztnQkFDL0MsbUJBQW1CLEtBQUssd0JBQXdCLENBQzlDLGNBQWMsRUFDZCxZQUFZLEVBQ1osVUFBVSxFQUNWLFNBQVMsQ0FDVixDQUFDO2dCQUVGLDJFQUEyRTtnQkFDM0UsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLFlBQVksRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDakQsSUFDRSx3QkFBd0IsQ0FDdEIsY0FBYyxFQUNkLGFBQWEsRUFDYixZQUFZLEVBQ1osTUFBTSxDQUNQLEVBQ0QsQ0FBQztvQkFDRCxjQUFjLENBQUMsV0FBVyxDQUFDLFlBQVksRUFBRSxRQUFRLENBQUMsQ0FBQztvQkFDbkQsWUFBWSxFQUFFLENBQUM7b0JBQ2YsVUFBVSxFQUFFLENBQUM7b0JBQ2Isa0JBQWtCLEdBQUcscUJBQXFCLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBQ3BELENBQUM7cUJBQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO29CQUM1QyxjQUFjLENBQUMsTUFBTSxDQUNuQixZQUFZLEVBQ1osY0FBYyxDQUFDLE1BQU0sQ0FBQyxZQUFZLEVBQUUsUUFBUSxDQUFDLENBQzlDLENBQUM7b0JBQ0YsWUFBWSxFQUFFLENBQUM7b0JBQ2YsVUFBVSxFQUFFLENBQUM7b0JBQ2Isa0JBQWtCLEdBQUcscUJBQXFCLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBQ3BELENBQUM7cUJBQU0sQ0FBQztvQkFDTixrRkFBa0Y7b0JBQ2xGLE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxZQUFZLEVBQUUsU0FBUyxDQUFDLENBQUM7b0JBQ25ELGFBQWEsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGNBQWMsQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQztvQkFDaEUsVUFBVSxFQUFFLENBQUM7Z0JBQ2YsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBRUQsMkZBQTJGO1FBQzNGLDBCQUEwQjtRQUMxQixPQUFPLENBQUMsa0JBQWtCLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDaEMsY0FBYyxDQUNaLGNBQWMsRUFDZCxhQUFhLEVBQ2IsU0FBUyxFQUNULGNBQWMsQ0FBQyxNQUFNLEVBQ3JCLGtCQUFrQixDQUFDLEtBQUssQ0FDekIsQ0FBQztZQUNGLGtCQUFrQixHQUFHLHFCQUFxQixDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3BELENBQUM7SUFDSCxDQUFDO0lBRUQsNkNBQTZDO0lBQzdDLHVFQUF1RTtJQUN2RSxPQUFPLFlBQVksSUFBSSxVQUFVLEVBQUUsQ0FBQztRQUNsQyxjQUFjLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzlELENBQUM7SUFFRCwrREFBK0Q7SUFDL0QsYUFBYSxFQUFFLE9BQU8sQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFO1FBQzlCLGNBQWMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDL0IsQ0FBQyxDQUFDLENBQUM7SUFFSCx3Q0FBd0M7SUFDeEMsSUFBSSxTQUFTLEVBQUUsQ0FBQztRQUNkLE1BQU0saUJBQWlCLEdBQUcsRUFBRSxDQUFDO1FBQzdCLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUMsSUFBSSxhQUFjLEVBQUUsQ0FBQztZQUMzQyxJQUFJLE1BQU0sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3BCLE1BQU0sR0FBRyxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQzlDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxHQUFHLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7b0JBQ3BDLGlCQUFpQixDQUFDLElBQUksQ0FDcEIsUUFBUSxHQUFHLGVBQWUsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FDeEQsQ0FBQztnQkFDSixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFFRCxJQUFJLGlCQUFpQixDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNqQyxNQUFNLE9BQU8sR0FDWCxvRkFBb0Y7Z0JBQ3BGLG1HQUFtRztnQkFDbkcsMEJBQTBCO2dCQUMxQixpQkFBaUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO2dCQUM5QixHQUFHLENBQUM7WUFFTixzQ0FBc0M7WUFDdEMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN4QixDQUFDO0lBQ0gsQ0FBQztBQUNILENBQUM7QUFFRCxTQUFTLHdCQUF3QixDQUMvQixjQUFvQyxFQUNwQyxhQUE2RCxFQUM3RCxLQUFhLEVBQ2IsR0FBWTtJQUVaLElBQUksYUFBYSxLQUFLLFNBQVMsSUFBSSxhQUFhLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDMUQsY0FBYyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsYUFBYSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUUsQ0FBQyxDQUFDO1FBQ3RELGFBQWEsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDMUIsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQ0QsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDO0FBRUQsU0FBUyxjQUFjLENBQ3JCLGNBQW9DLEVBQ3BDLGFBQTZELEVBQzdELFNBQW1DLEVBQ25DLEtBQWEsRUFDYixLQUFRO0lBRVIsSUFDRSxDQUFDLHdCQUF3QixDQUN2QixjQUFjLEVBQ2QsYUFBYSxFQUNiLEtBQUssRUFDTCxTQUFTLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUN4QixFQUNELENBQUM7UUFDRCxNQUFNLE9BQU8sR0FBRyxjQUFjLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNwRCxjQUFjLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN4QyxDQUFDO1NBQU0sQ0FBQztRQUNOLGNBQWMsQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzNDLENBQUM7QUFDSCxDQUFDO0FBRUQsU0FBUyx3QkFBd0IsQ0FDL0IsY0FBZ0QsRUFDaEQsS0FBYSxFQUNiLEdBQVcsRUFDWCxTQUFtQztJQUVuQyxNQUFNLElBQUksR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQ3ZCLEtBQUssSUFBSSxDQUFDLEdBQUcsS0FBSyxFQUFFLENBQUMsSUFBSSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNsQyxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsY0FBYyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxPQUFPLHNCQUFzQjtJQUFuQztRQUNFLGlFQUFpRTtRQUN6RCxVQUFLLEdBQUcsSUFBSSxHQUFHLEVBQVEsQ0FBQztRQUNoQyxpR0FBaUc7UUFDakcsa0dBQWtHO1FBQ2xHLGtDQUFrQztRQUMxQixVQUFLLEdBQTBCLFNBQVMsQ0FBQztJQStEbkQsQ0FBQztJQTdEQyxHQUFHLENBQUMsR0FBTTtRQUNSLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVELE1BQU0sQ0FBQyxHQUFNO1FBQ1gsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDO1lBQUUsT0FBTyxLQUFLLENBQUM7UUFFakMsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFFLENBQUM7UUFDbkMsSUFBSSxJQUFJLENBQUMsS0FBSyxLQUFLLFNBQVMsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ3RELElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUUsQ0FBQyxDQUFDO1lBQzVDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzNCLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDekIsQ0FBQztRQUVELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELEdBQUcsQ0FBQyxHQUFNO1FBQ1IsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM3QixDQUFDO0lBRUQsR0FBRyxDQUFDLEdBQU0sRUFBRSxLQUFRO1FBQ2xCLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN4QixJQUFJLFNBQVMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUUsQ0FBQztZQUVyQyx3RkFBd0Y7WUFDeEYsZ0ZBQWdGO1lBQ2hGOzs7O2VBSUc7WUFFSCxJQUFJLElBQUksQ0FBQyxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQzdCLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztZQUN6QixDQUFDO1lBRUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztZQUN4QixPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztnQkFDM0IsU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFFLENBQUM7WUFDbkMsQ0FBQztZQUNELElBQUksQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzdCLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzdCLENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTyxDQUFDLEVBQXdCO1FBQzlCLHdDQUF3QztRQUN4QyxLQUFLLElBQUksQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3BDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDZixJQUFJLElBQUksQ0FBQyxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQzdCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7Z0JBQ3hCLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO29CQUN2QixLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUUsQ0FBQztvQkFDekIsRUFBRSxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQztnQkFDakIsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gY29waWVkIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL2FuZ3VsYXIvYW5ndWxhci9ibG9iL21haW4vcGFja2FnZXMvY29yZS9zcmMvcmVuZGVyMy9saXN0X3JlY29uY2lsaWF0aW9uLnRzXG5cbmltcG9ydCB7IFRyYWNrQnlGdW5jdGlvbiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG4vKipcbiAqIEBkZXNjcmlwdGlvbiBXaWxsIGJlIHByb3ZpZGVkIHRocm91Z2ggVGVyc2VyIGdsb2JhbCBkZWZpbml0aW9ucyBieSBBbmd1bGFyIENMSVxuICogZHVyaW5nIHRoZSBwcm9kdWN0aW9uIGJ1aWxkLlxuICovXG5kZWNsYXJlIGNvbnN0IG5nRGV2TW9kZTogYm9vbGVhbjtcblxuLyoqXG4gKiBBIHR5cGUgcmVwcmVzZW50aW5nIHRoZSBsaXZlIGNvbGxlY3Rpb24gdG8gYmUgcmVjb25jaWxlZCB3aXRoIGFueSBuZXcgKGluY29taW5nKSBjb2xsZWN0aW9uLiBUaGlzXG4gKiBpcyBhbiBhZGFwdGVyIGNsYXNzIHRoYXQgbWFrZXMgaXQgcG9zc2libGUgdG8gd29yayB3aXRoIGRpZmZlcmVudCBpbnRlcm5hbCBkYXRhIHN0cnVjdHVyZXMsXG4gKiByZWdhcmRsZXNzIG9mIHRoZSBhY3R1YWwgdmFsdWVzIG9mIHRoZSBpbmNvbWluZyBjb2xsZWN0aW9uLlxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTGl2ZUNvbGxlY3Rpb248VCwgVj4ge1xuICBhYnN0cmFjdCBnZXQgbGVuZ3RoKCk6IG51bWJlcjtcbiAgYWJzdHJhY3QgYXQoaW5kZXg6IG51bWJlcik6IFY7XG4gIGFic3RyYWN0IGF0dGFjaChpbmRleDogbnVtYmVyLCBpdGVtOiBUKTogdm9pZDtcbiAgYWJzdHJhY3QgZGV0YWNoKGluZGV4OiBudW1iZXIpOiBUO1xuICBhYnN0cmFjdCBjcmVhdGUoaW5kZXg6IG51bWJlciwgdmFsdWU6IFYpOiBUO1xuICBkZXN0cm95KGl0ZW06IFQpOiB2b2lkIHtcbiAgICAvLyBub29wIGJ5IGRlZmF1bHRcbiAgfVxuICB1cGRhdGVWYWx1ZShpbmRleDogbnVtYmVyLCB2YWx1ZTogVik6IHZvaWQge1xuICAgIC8vIG5vb3AgYnkgZGVmYXVsdFxuICB9XG5cbiAgLy8gb3BlcmF0aW9ucyBiZWxvdyBjb3VsZCBiZSBpbXBsZW1lbnRlZCBvbiB0b3Agb2YgdGhlIG9wZXJhdGlvbnMgZGVmaW5lZCBzbyBmYXIsIGJ1dCBoYXZpbmdcbiAgLy8gdGhlbSBleHBsaWNpdGx5IGFsbG93IGNsZWFyIGV4cHJlc3Npb24gb2YgaW50ZW50IGFuZCBwb3RlbnRpYWxseSBtb3JlIHBlcmZvcm1hbnRcbiAgLy8gaW1wbGVtZW50YXRpb25zXG4gIHN3YXAoaW5kZXgxOiBudW1iZXIsIGluZGV4MjogbnVtYmVyKTogdm9pZCB7XG4gICAgY29uc3Qgc3RhcnRJZHggPSBNYXRoLm1pbihpbmRleDEsIGluZGV4Mik7XG4gICAgY29uc3QgZW5kSWR4ID0gTWF0aC5tYXgoaW5kZXgxLCBpbmRleDIpO1xuICAgIGNvbnN0IGVuZEl0ZW0gPSB0aGlzLmRldGFjaChlbmRJZHgpO1xuICAgIGlmIChlbmRJZHggLSBzdGFydElkeCA+IDEpIHtcbiAgICAgIGNvbnN0IHN0YXJ0SXRlbSA9IHRoaXMuZGV0YWNoKHN0YXJ0SWR4KTtcbiAgICAgIHRoaXMuYXR0YWNoKHN0YXJ0SWR4LCBlbmRJdGVtKTtcbiAgICAgIHRoaXMuYXR0YWNoKGVuZElkeCwgc3RhcnRJdGVtKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5hdHRhY2goc3RhcnRJZHgsIGVuZEl0ZW0pO1xuICAgIH1cbiAgfVxuICBtb3ZlKHByZXZJbmRleDogbnVtYmVyLCBuZXdJZHg6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMuYXR0YWNoKG5ld0lkeCwgdGhpcy5kZXRhY2gocHJldkluZGV4KSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gdmFsdWVzTWF0Y2hpbmc8Vj4oXG4gIGxpdmVJZHg6IG51bWJlcixcbiAgbGl2ZVZhbHVlOiBWLFxuICBuZXdJZHg6IG51bWJlcixcbiAgbmV3VmFsdWU6IFYsXG4gIHRyYWNrQnk6IFRyYWNrQnlGdW5jdGlvbjxWPixcbik6IG51bWJlciB7XG4gIGlmIChsaXZlSWR4ID09PSBuZXdJZHggJiYgT2JqZWN0LmlzKGxpdmVWYWx1ZSwgbmV3VmFsdWUpKSB7XG4gICAgLy8gbWF0Y2hpbmcgYW5kIG5vIHZhbHVlIGlkZW50aXR5IHRvIHVwZGF0ZVxuICAgIHJldHVybiAxO1xuICB9IGVsc2UgaWYgKFxuICAgIE9iamVjdC5pcyh0cmFja0J5KGxpdmVJZHgsIGxpdmVWYWx1ZSksIHRyYWNrQnkobmV3SWR4LCBuZXdWYWx1ZSkpXG4gICkge1xuICAgIC8vIG1hdGNoaW5nIGJ1dCByZXF1aXJlcyB2YWx1ZSBpZGVudGl0eSB1cGRhdGVcbiAgICByZXR1cm4gLTE7XG4gIH1cblxuICByZXR1cm4gMDtcbn1cblxuZnVuY3Rpb24gcmVjb3JkRHVwbGljYXRlS2V5cyhcbiAga2V5VG9JZHg6IE1hcDx1bmtub3duLCBTZXQ8bnVtYmVyPj4sXG4gIGtleTogdW5rbm93bixcbiAgaWR4OiBudW1iZXIsXG4pOiB2b2lkIHtcbiAgY29uc3QgaWR4U29GYXIgPSBrZXlUb0lkeC5nZXQoa2V5KTtcblxuICBpZiAoaWR4U29GYXIgIT09IHVuZGVmaW5lZCkge1xuICAgIGlkeFNvRmFyLmFkZChpZHgpO1xuICB9IGVsc2Uge1xuICAgIGtleVRvSWR4LnNldChrZXksIG5ldyBTZXQoW2lkeF0pKTtcbiAgfVxufVxuXG4vKipcbiAqIFRoZSBsaXZlIGNvbGxlY3Rpb24gcmVjb25jaWxpYXRpb24gYWxnb3JpdGhtIHRoYXQgcGVyZm9ybSB2YXJpb3VzIGluLXBsYWNlIG9wZXJhdGlvbnMsIHNvIGl0XG4gKiByZWZsZWN0cyB0aGUgY29udGVudCBvZiB0aGUgbmV3IChpbmNvbWluZykgY29sbGVjdGlvbi5cbiAqXG4gKiBUaGUgcmVjb25jaWxpYXRpb24gYWxnb3JpdGhtIGhhcyAyIGNvZGUgcGF0aHM6XG4gKiAtIFwiZmFzdFwiIHBhdGggdGhhdCBkb24ndCByZXF1aXJlIGFueSBtZW1vcnkgYWxsb2NhdGlvbjtcbiAqIC0gXCJzbG93XCIgcGF0aCB0aGF0IHJlcXVpcmVzIGFkZGl0aW9uYWwgbWVtb3J5IGFsbG9jYXRpb24gZm9yIGludGVybWVkaWF0ZSBkYXRhIHN0cnVjdHVyZXMgdXNlZCB0b1xuICogY29sbGVjdCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IHRoZSBsaXZlIGNvbGxlY3Rpb24uXG4gKiBJdCBtaWdodCBoYXBwZW4gdGhhdCB0aGUgYWxnb3JpdGhtIHN3aXRjaGVzIGJldHdlZW4gdGhlIHR3byBtb2RlcyBpbiBxdWVzdGlvbiBpbiBhIHNpbmdsZVxuICogcmVjb25jaWxpYXRpb24gcGF0aCAtIGdlbmVyYWxseSBpdCB0cmllcyB0byBzdGF5IG9uIHRoZSBcImZhc3RcIiBwYXRoIGFzIG11Y2ggYXMgcG9zc2libGUuXG4gKlxuICogVGhlIG92ZXJhbGwgY29tcGxleGl0eSBvZiB0aGUgYWxnb3JpdGhtIGlzIE8obiArIG0pIGZvciBzcGVlZCBhbmQgTyhuKSBmb3IgbWVtb3J5ICh3aGVyZSBuIGlzIHRoZVxuICogbGVuZ3RoIG9mIHRoZSBsaXZlIGNvbGxlY3Rpb24gYW5kIG0gaXMgdGhlIGxlbmd0aCBvZiB0aGUgaW5jb21pbmcgY29sbGVjdGlvbikuIEdpdmVuIHRoZSBwcm9ibGVtXG4gKiBhdCBoYW5kIHRoZSBjb21wbGV4aXR5IC8gcGVyZm9ybWFuY2UgY29uc3RyYWludHMgbWFrZXMgaXQgaW1wb3NzaWJsZSB0byBwZXJmb3JtIHRoZSBhYnNvbHV0ZVxuICogbWluaW11bSBvZiBvcGVyYXRpb24gdG8gcmVjb25jaWxlIHRoZSAyIGNvbGxlY3Rpb25zLiBUaGUgYWxnb3JpdGhtIG1ha2VzIGRpZmZlcmVudCB0cmFkZW9mZnMgdG9cbiAqIHN0YXkgd2l0aGluIHJlYXNvbmFibGUgcGVyZm9ybWFuY2UgYm91bmRzIGFuZCBtYXkgYXBwbHkgc3ViLW9wdGltYWwgbnVtYmVyIG9mIG9wZXJhdGlvbnMgaW5cbiAqIGNlcnRhaW4gc2l0dWF0aW9ucy5cbiAqXG4gKiBAcGFyYW0gbGl2ZUNvbGxlY3Rpb24gdGhlIGN1cnJlbnQsIGxpdmUgY29sbGVjdGlvbjtcbiAqIEBwYXJhbSBuZXdDb2xsZWN0aW9uIHRoZSBuZXcsIGluY29taW5nIGNvbGxlY3Rpb247XG4gKiBAcGFyYW0gdHJhY2tCeUZuIGtleSBnZW5lcmF0aW9uIGZ1bmN0aW9uIHRoYXQgZGV0ZXJtaW5lcyBlcXVhbGl0eSBiZXR3ZWVuIGl0ZW1zIGluIHRoZSBsaWZlIGFuZFxuICogICAgIGluY29taW5nIGNvbGxlY3Rpb247XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWNvbmNpbGU8VCwgVj4oXG4gIGxpdmVDb2xsZWN0aW9uOiBMaXZlQ29sbGVjdGlvbjxULCBWPixcbiAgbmV3Q29sbGVjdGlvbjogSXRlcmFibGU8Vj4gfCB1bmRlZmluZWQgfCBudWxsLFxuICB0cmFja0J5Rm46IFRyYWNrQnlGdW5jdGlvbjxWPixcbik6IHZvaWQge1xuICBsZXQgZGV0YWNoZWRJdGVtczogVW5pcXVlVmFsdWVNdWx0aUtleU1hcDx1bmtub3duLCBUPiB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcbiAgbGV0IGxpdmVLZXlzSW5UaGVGdXR1cmU6IFNldDx1bmtub3duPiB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcblxuICBsZXQgbGl2ZVN0YXJ0SWR4ID0gMDtcbiAgbGV0IGxpdmVFbmRJZHggPSBsaXZlQ29sbGVjdGlvbi5sZW5ndGggLSAxO1xuXG4gIGNvbnN0IGR1cGxpY2F0ZUtleXMgPSBuZ0Rldk1vZGUgPyBuZXcgTWFwPHVua25vd24sIFNldDxudW1iZXI+PigpIDogdW5kZWZpbmVkO1xuXG4gIGlmIChBcnJheS5pc0FycmF5KG5ld0NvbGxlY3Rpb24pKSB7XG4gICAgbGV0IG5ld0VuZElkeCA9IG5ld0NvbGxlY3Rpb24ubGVuZ3RoIC0gMTtcblxuICAgIHdoaWxlIChsaXZlU3RhcnRJZHggPD0gbGl2ZUVuZElkeCAmJiBsaXZlU3RhcnRJZHggPD0gbmV3RW5kSWR4KSB7XG4gICAgICAvLyBjb21wYXJlIGZyb20gdGhlIGJlZ2lubmluZ1xuICAgICAgY29uc3QgbGl2ZVN0YXJ0VmFsdWUgPSBsaXZlQ29sbGVjdGlvbi5hdChsaXZlU3RhcnRJZHgpO1xuICAgICAgY29uc3QgbmV3U3RhcnRWYWx1ZSA9IG5ld0NvbGxlY3Rpb25bbGl2ZVN0YXJ0SWR4XTtcblxuICAgICAgaWYgKG5nRGV2TW9kZSkge1xuICAgICAgICByZWNvcmREdXBsaWNhdGVLZXlzKFxuICAgICAgICAgIGR1cGxpY2F0ZUtleXMhLFxuICAgICAgICAgIHRyYWNrQnlGbihsaXZlU3RhcnRJZHgsIG5ld1N0YXJ0VmFsdWUpLFxuICAgICAgICAgIGxpdmVTdGFydElkeCxcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgaXNTdGFydE1hdGNoaW5nID0gdmFsdWVzTWF0Y2hpbmcoXG4gICAgICAgIGxpdmVTdGFydElkeCxcbiAgICAgICAgbGl2ZVN0YXJ0VmFsdWUsXG4gICAgICAgIGxpdmVTdGFydElkeCxcbiAgICAgICAgbmV3U3RhcnRWYWx1ZSxcbiAgICAgICAgdHJhY2tCeUZuLFxuICAgICAgKTtcbiAgICAgIGlmIChpc1N0YXJ0TWF0Y2hpbmcgIT09IDApIHtcbiAgICAgICAgaWYgKGlzU3RhcnRNYXRjaGluZyA8IDApIHtcbiAgICAgICAgICBsaXZlQ29sbGVjdGlvbi51cGRhdGVWYWx1ZShsaXZlU3RhcnRJZHgsIG5ld1N0YXJ0VmFsdWUpO1xuICAgICAgICB9XG4gICAgICAgIGxpdmVTdGFydElkeCsrO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gY29tcGFyZSBmcm9tIHRoZSBlbmRcbiAgICAgIC8vIFRPRE8ocGVyZik6IGRvIF9hbGxfIHRoZSBtYXRjaGluZyBmcm9tIHRoZSBlbmRcbiAgICAgIGNvbnN0IGxpdmVFbmRWYWx1ZSA9IGxpdmVDb2xsZWN0aW9uLmF0KGxpdmVFbmRJZHgpO1xuICAgICAgY29uc3QgbmV3RW5kVmFsdWUgPSBuZXdDb2xsZWN0aW9uW25ld0VuZElkeF07XG5cbiAgICAgIGlmIChuZ0Rldk1vZGUpIHtcbiAgICAgICAgcmVjb3JkRHVwbGljYXRlS2V5cyhcbiAgICAgICAgICBkdXBsaWNhdGVLZXlzISxcbiAgICAgICAgICB0cmFja0J5Rm4obmV3RW5kSWR4LCBuZXdFbmRWYWx1ZSksXG4gICAgICAgICAgbmV3RW5kSWR4LFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBpc0VuZE1hdGNoaW5nID0gdmFsdWVzTWF0Y2hpbmcoXG4gICAgICAgIGxpdmVFbmRJZHgsXG4gICAgICAgIGxpdmVFbmRWYWx1ZSxcbiAgICAgICAgbmV3RW5kSWR4LFxuICAgICAgICBuZXdFbmRWYWx1ZSxcbiAgICAgICAgdHJhY2tCeUZuLFxuICAgICAgKTtcbiAgICAgIGlmIChpc0VuZE1hdGNoaW5nICE9PSAwKSB7XG4gICAgICAgIGlmIChpc0VuZE1hdGNoaW5nIDwgMCkge1xuICAgICAgICAgIGxpdmVDb2xsZWN0aW9uLnVwZGF0ZVZhbHVlKGxpdmVFbmRJZHgsIG5ld0VuZFZhbHVlKTtcbiAgICAgICAgfVxuICAgICAgICBsaXZlRW5kSWR4LS07XG4gICAgICAgIG5ld0VuZElkeC0tO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gRGV0ZWN0IHN3YXAgYW5kIG1vdmVzOlxuICAgICAgY29uc3QgbGl2ZVN0YXJ0S2V5ID0gdHJhY2tCeUZuKGxpdmVTdGFydElkeCwgbGl2ZVN0YXJ0VmFsdWUpO1xuICAgICAgY29uc3QgbGl2ZUVuZEtleSA9IHRyYWNrQnlGbihsaXZlRW5kSWR4LCBsaXZlRW5kVmFsdWUpO1xuICAgICAgY29uc3QgbmV3U3RhcnRLZXkgPSB0cmFja0J5Rm4obGl2ZVN0YXJ0SWR4LCBuZXdTdGFydFZhbHVlKTtcbiAgICAgIGlmIChPYmplY3QuaXMobmV3U3RhcnRLZXksIGxpdmVFbmRLZXkpKSB7XG4gICAgICAgIGNvbnN0IG5ld0VuZEtleSA9IHRyYWNrQnlGbihuZXdFbmRJZHgsIG5ld0VuZFZhbHVlKTtcbiAgICAgICAgLy8gZGV0ZWN0IHN3YXAgb24gYm90aCBlbmRzO1xuICAgICAgICBpZiAoT2JqZWN0LmlzKG5ld0VuZEtleSwgbGl2ZVN0YXJ0S2V5KSkge1xuICAgICAgICAgIGxpdmVDb2xsZWN0aW9uLnN3YXAobGl2ZVN0YXJ0SWR4LCBsaXZlRW5kSWR4KTtcbiAgICAgICAgICBsaXZlQ29sbGVjdGlvbi51cGRhdGVWYWx1ZShsaXZlRW5kSWR4LCBuZXdFbmRWYWx1ZSk7XG4gICAgICAgICAgbmV3RW5kSWR4LS07XG4gICAgICAgICAgbGl2ZUVuZElkeC0tO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIHRoZSBuZXcgaXRlbSBpcyB0aGUgc2FtZSBhcyB0aGUgbGl2ZSBpdGVtIHdpdGggdGhlIGVuZCBwb2ludGVyIC0gdGhpcyBpcyBhIG1vdmUgZm9yd2FyZFxuICAgICAgICAgIC8vIHRvIGFuIGVhcmxpZXIgaW5kZXg7XG4gICAgICAgICAgbGl2ZUNvbGxlY3Rpb24ubW92ZShsaXZlRW5kSWR4LCBsaXZlU3RhcnRJZHgpO1xuICAgICAgICB9XG4gICAgICAgIGxpdmVDb2xsZWN0aW9uLnVwZGF0ZVZhbHVlKGxpdmVTdGFydElkeCwgbmV3U3RhcnRWYWx1ZSk7XG4gICAgICAgIGxpdmVTdGFydElkeCsrO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gRmFsbGJhY2sgdG8gdGhlIHNsb3cgcGF0aDogd2UgbmVlZCB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBjb250ZW50IG9mIHRoZSBsaXZlIGFuZCBuZXdcbiAgICAgIC8vIGNvbGxlY3Rpb25zLlxuICAgICAgZGV0YWNoZWRJdGVtcyA/Pz0gbmV3IFVuaXF1ZVZhbHVlTXVsdGlLZXlNYXAoKTtcbiAgICAgIGxpdmVLZXlzSW5UaGVGdXR1cmUgPz89IGluaXRMaXZlSXRlbXNJblRoZUZ1dHVyZShcbiAgICAgICAgbGl2ZUNvbGxlY3Rpb24sXG4gICAgICAgIGxpdmVTdGFydElkeCxcbiAgICAgICAgbGl2ZUVuZElkeCxcbiAgICAgICAgdHJhY2tCeUZuLFxuICAgICAgKTtcblxuICAgICAgLy8gQ2hlY2sgaWYgSSdtIGluc2VydGluZyBhIHByZXZpb3VzbHkgZGV0YWNoZWQgaXRlbTogaWYgc28sIGF0dGFjaCBpdCBoZXJlXG4gICAgICBpZiAoXG4gICAgICAgIGF0dGFjaFByZXZpb3VzbHlEZXRhY2hlZChcbiAgICAgICAgICBsaXZlQ29sbGVjdGlvbixcbiAgICAgICAgICBkZXRhY2hlZEl0ZW1zLFxuICAgICAgICAgIGxpdmVTdGFydElkeCxcbiAgICAgICAgICBuZXdTdGFydEtleSxcbiAgICAgICAgKVxuICAgICAgKSB7XG4gICAgICAgIGxpdmVDb2xsZWN0aW9uLnVwZGF0ZVZhbHVlKGxpdmVTdGFydElkeCwgbmV3U3RhcnRWYWx1ZSk7XG4gICAgICAgIGxpdmVTdGFydElkeCsrO1xuICAgICAgICBsaXZlRW5kSWR4Kys7XG4gICAgICB9IGVsc2UgaWYgKCFsaXZlS2V5c0luVGhlRnV0dXJlLmhhcyhuZXdTdGFydEtleSkpIHtcbiAgICAgICAgLy8gQ2hlY2sgaWYgd2Ugc2VlbiBhIG5ldyBpdGVtIHRoYXQgZG9lc24ndCBleGlzdCBpbiB0aGUgb2xkIGNvbGxlY3Rpb24gYW5kIG11c3QgYmUgSU5TRVJURURcbiAgICAgICAgY29uc3QgbmV3SXRlbSA9IGxpdmVDb2xsZWN0aW9uLmNyZWF0ZShcbiAgICAgICAgICBsaXZlU3RhcnRJZHgsXG4gICAgICAgICAgbmV3Q29sbGVjdGlvbltsaXZlU3RhcnRJZHhdLFxuICAgICAgICApO1xuICAgICAgICBsaXZlQ29sbGVjdGlvbi5hdHRhY2gobGl2ZVN0YXJ0SWR4LCBuZXdJdGVtKTtcbiAgICAgICAgbGl2ZVN0YXJ0SWR4Kys7XG4gICAgICAgIGxpdmVFbmRJZHgrKztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFdlIGtub3cgdGhhdCB0aGUgbmV3IGl0ZW0gZXhpc3RzIGxhdGVyIG9uIGluIG9sZCBjb2xsZWN0aW9uIGJ1dCB3ZSBkb24ndCBrbm93IGl0cyBpbmRleFxuICAgICAgICAvLyBhbmQgYXMgdGhlIGNvbnNlcXVlbmNlIGNhbid0IG1vdmUgaXQgKGRvbid0IGtub3cgd2hlcmUgdG8gZmluZCBpdCkuIERldGFjaCB0aGUgb2xkIGl0ZW0sXG4gICAgICAgIC8vIGhvcGluZyB0aGF0IGl0IHVubG9ja3MgdGhlIGZhc3QgcGF0aCBhZ2Fpbi5cbiAgICAgICAgZGV0YWNoZWRJdGVtcy5zZXQobGl2ZVN0YXJ0S2V5LCBsaXZlQ29sbGVjdGlvbi5kZXRhY2gobGl2ZVN0YXJ0SWR4KSk7XG4gICAgICAgIGxpdmVFbmRJZHgtLTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBGaW5hbCBjbGVhbnVwIHN0ZXBzOlxuICAgIC8vIC0gbW9yZSBpdGVtcyBpbiB0aGUgbmV3IGNvbGxlY3Rpb24gPT4gaW5zZXJ0XG4gICAgd2hpbGUgKGxpdmVTdGFydElkeCA8PSBuZXdFbmRJZHgpIHtcbiAgICAgIGNyZWF0ZU9yQXR0YWNoKFxuICAgICAgICBsaXZlQ29sbGVjdGlvbixcbiAgICAgICAgZGV0YWNoZWRJdGVtcyxcbiAgICAgICAgdHJhY2tCeUZuLFxuICAgICAgICBsaXZlU3RhcnRJZHgsXG4gICAgICAgIG5ld0NvbGxlY3Rpb25bbGl2ZVN0YXJ0SWR4XSxcbiAgICAgICk7XG4gICAgICBsaXZlU3RhcnRJZHgrKztcbiAgICB9XG4gIH0gZWxzZSBpZiAobmV3Q29sbGVjdGlvbiAhPSBudWxsKSB7XG4gICAgLy8gaXRlcmFibGUgLSBpbW1lZGlhdGVseSBmYWxsYmFjayB0byB0aGUgc2xvdyBwYXRoXG4gICAgY29uc3QgbmV3Q29sbGVjdGlvbkl0ZXJhdG9yID0gbmV3Q29sbGVjdGlvbltTeW1ib2wuaXRlcmF0b3JdKCk7XG4gICAgbGV0IG5ld0l0ZXJhdGlvblJlc3VsdCA9IG5ld0NvbGxlY3Rpb25JdGVyYXRvci5uZXh0KCk7XG4gICAgd2hpbGUgKCFuZXdJdGVyYXRpb25SZXN1bHQuZG9uZSAmJiBsaXZlU3RhcnRJZHggPD0gbGl2ZUVuZElkeCkge1xuICAgICAgY29uc3QgbGl2ZVZhbHVlID0gbGl2ZUNvbGxlY3Rpb24uYXQobGl2ZVN0YXJ0SWR4KTtcbiAgICAgIGNvbnN0IG5ld1ZhbHVlID0gbmV3SXRlcmF0aW9uUmVzdWx0LnZhbHVlO1xuXG4gICAgICBpZiAobmdEZXZNb2RlKSB7XG4gICAgICAgIHJlY29yZER1cGxpY2F0ZUtleXMoXG4gICAgICAgICAgZHVwbGljYXRlS2V5cyEsXG4gICAgICAgICAgdHJhY2tCeUZuKGxpdmVTdGFydElkeCwgbmV3VmFsdWUpLFxuICAgICAgICAgIGxpdmVTdGFydElkeCxcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgaXNTdGFydE1hdGNoaW5nID0gdmFsdWVzTWF0Y2hpbmcoXG4gICAgICAgIGxpdmVTdGFydElkeCxcbiAgICAgICAgbGl2ZVZhbHVlLFxuICAgICAgICBsaXZlU3RhcnRJZHgsXG4gICAgICAgIG5ld1ZhbHVlLFxuICAgICAgICB0cmFja0J5Rm4sXG4gICAgICApO1xuICAgICAgaWYgKGlzU3RhcnRNYXRjaGluZyAhPT0gMCkge1xuICAgICAgICAvLyBmb3VuZCBhIG1hdGNoIC0gbW92ZSBvbiwgYnV0IHVwZGF0ZSB2YWx1ZVxuICAgICAgICBpZiAoaXNTdGFydE1hdGNoaW5nIDwgMCkge1xuICAgICAgICAgIGxpdmVDb2xsZWN0aW9uLnVwZGF0ZVZhbHVlKGxpdmVTdGFydElkeCwgbmV3VmFsdWUpO1xuICAgICAgICB9XG4gICAgICAgIGxpdmVTdGFydElkeCsrO1xuICAgICAgICBuZXdJdGVyYXRpb25SZXN1bHQgPSBuZXdDb2xsZWN0aW9uSXRlcmF0b3IubmV4dCgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZGV0YWNoZWRJdGVtcyA/Pz0gbmV3IFVuaXF1ZVZhbHVlTXVsdGlLZXlNYXAoKTtcbiAgICAgICAgbGl2ZUtleXNJblRoZUZ1dHVyZSA/Pz0gaW5pdExpdmVJdGVtc0luVGhlRnV0dXJlKFxuICAgICAgICAgIGxpdmVDb2xsZWN0aW9uLFxuICAgICAgICAgIGxpdmVTdGFydElkeCxcbiAgICAgICAgICBsaXZlRW5kSWR4LFxuICAgICAgICAgIHRyYWNrQnlGbixcbiAgICAgICAgKTtcblxuICAgICAgICAvLyBDaGVjayBpZiBJJ20gaW5zZXJ0aW5nIGEgcHJldmlvdXNseSBkZXRhY2hlZCBpdGVtOiBpZiBzbywgYXR0YWNoIGl0IGhlcmVcbiAgICAgICAgY29uc3QgbmV3S2V5ID0gdHJhY2tCeUZuKGxpdmVTdGFydElkeCwgbmV3VmFsdWUpO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgYXR0YWNoUHJldmlvdXNseURldGFjaGVkKFxuICAgICAgICAgICAgbGl2ZUNvbGxlY3Rpb24sXG4gICAgICAgICAgICBkZXRhY2hlZEl0ZW1zLFxuICAgICAgICAgICAgbGl2ZVN0YXJ0SWR4LFxuICAgICAgICAgICAgbmV3S2V5LFxuICAgICAgICAgIClcbiAgICAgICAgKSB7XG4gICAgICAgICAgbGl2ZUNvbGxlY3Rpb24udXBkYXRlVmFsdWUobGl2ZVN0YXJ0SWR4LCBuZXdWYWx1ZSk7XG4gICAgICAgICAgbGl2ZVN0YXJ0SWR4Kys7XG4gICAgICAgICAgbGl2ZUVuZElkeCsrO1xuICAgICAgICAgIG5ld0l0ZXJhdGlvblJlc3VsdCA9IG5ld0NvbGxlY3Rpb25JdGVyYXRvci5uZXh0KCk7XG4gICAgICAgIH0gZWxzZSBpZiAoIWxpdmVLZXlzSW5UaGVGdXR1cmUuaGFzKG5ld0tleSkpIHtcbiAgICAgICAgICBsaXZlQ29sbGVjdGlvbi5hdHRhY2goXG4gICAgICAgICAgICBsaXZlU3RhcnRJZHgsXG4gICAgICAgICAgICBsaXZlQ29sbGVjdGlvbi5jcmVhdGUobGl2ZVN0YXJ0SWR4LCBuZXdWYWx1ZSksXG4gICAgICAgICAgKTtcbiAgICAgICAgICBsaXZlU3RhcnRJZHgrKztcbiAgICAgICAgICBsaXZlRW5kSWR4Kys7XG4gICAgICAgICAgbmV3SXRlcmF0aW9uUmVzdWx0ID0gbmV3Q29sbGVjdGlvbkl0ZXJhdG9yLm5leHQoKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBpdCBpcyBhIG1vdmUgZm9yd2FyZCAtIGRldGFjaCB0aGUgY3VycmVudCBpdGVtIHdpdGhvdXQgYWR2YW5jaW5nIGluIGNvbGxlY3Rpb25zXG4gICAgICAgICAgY29uc3QgbGl2ZUtleSA9IHRyYWNrQnlGbihsaXZlU3RhcnRJZHgsIGxpdmVWYWx1ZSk7XG4gICAgICAgICAgZGV0YWNoZWRJdGVtcy5zZXQobGl2ZUtleSwgbGl2ZUNvbGxlY3Rpb24uZGV0YWNoKGxpdmVTdGFydElkeCkpO1xuICAgICAgICAgIGxpdmVFbmRJZHgtLTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHRoaXMgaXMgYSBuZXcgaXRlbSBhcyB3ZSBydW4gb3V0IG9mIHRoZSBpdGVtcyBpbiB0aGUgb2xkIGNvbGxlY3Rpb24gLSBjcmVhdGUgb3IgYXR0YWNoIGFcbiAgICAvLyBwcmV2aW91c2x5IGRldGFjaGVkIG9uZVxuICAgIHdoaWxlICghbmV3SXRlcmF0aW9uUmVzdWx0LmRvbmUpIHtcbiAgICAgIGNyZWF0ZU9yQXR0YWNoKFxuICAgICAgICBsaXZlQ29sbGVjdGlvbixcbiAgICAgICAgZGV0YWNoZWRJdGVtcyxcbiAgICAgICAgdHJhY2tCeUZuLFxuICAgICAgICBsaXZlQ29sbGVjdGlvbi5sZW5ndGgsXG4gICAgICAgIG5ld0l0ZXJhdGlvblJlc3VsdC52YWx1ZSxcbiAgICAgICk7XG4gICAgICBuZXdJdGVyYXRpb25SZXN1bHQgPSBuZXdDb2xsZWN0aW9uSXRlcmF0b3IubmV4dCgpO1xuICAgIH1cbiAgfVxuXG4gIC8vIENsZWFudXBzIGNvbW1vbiB0byB0aGUgYXJyYXkgYW5kIGl0ZXJhYmxlOlxuICAvLyAtIG1vcmUgaXRlbXMgaW4gdGhlIGxpdmUgY29sbGVjdGlvbiA9PiBkZWxldGUgc3RhcnRpbmcgZnJvbSB0aGUgZW5kO1xuICB3aGlsZSAobGl2ZVN0YXJ0SWR4IDw9IGxpdmVFbmRJZHgpIHtcbiAgICBsaXZlQ29sbGVjdGlvbi5kZXN0cm95KGxpdmVDb2xsZWN0aW9uLmRldGFjaChsaXZlRW5kSWR4LS0pKTtcbiAgfVxuXG4gIC8vIC0gZGVzdHJveSBpdGVtcyB0aGF0IHdlcmUgZGV0YWNoZWQgYnV0IG5ldmVyIGF0dGFjaGVkIGFnYWluLlxuICBkZXRhY2hlZEl0ZW1zPy5mb3JFYWNoKChpdGVtKSA9PiB7XG4gICAgbGl2ZUNvbGxlY3Rpb24uZGVzdHJveShpdGVtKTtcbiAgfSk7XG5cbiAgLy8gcmVwb3J0IGR1cGxpY2F0ZSBrZXlzIChkZXYgbW9kZSBvbmx5KVxuICBpZiAobmdEZXZNb2RlKSB7XG4gICAgY29uc3QgZHVwbGljYXRlZEtleXNNc2cgPSBbXTtcbiAgICBmb3IgKGNvbnN0IFtrZXksIGlkeFNldF0gb2YgZHVwbGljYXRlS2V5cyEpIHtcbiAgICAgIGlmIChpZHhTZXQuc2l6ZSA+IDEpIHtcbiAgICAgICAgY29uc3QgaWR4ID0gWy4uLmlkeFNldF0uc29ydCgoYSwgYikgPT4gYSAtIGIpO1xuICAgICAgICBmb3IgKGxldCBpID0gMTsgaSA8IGlkeC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIGR1cGxpY2F0ZWRLZXlzTXNnLnB1c2goXG4gICAgICAgICAgICBga2V5IFwiJHtrZXl9XCIgYXQgaW5kZXggXCIke2lkeFtpIC0gMV19XCIgYW5kIFwiJHtpZHhbaV19XCJgLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoZHVwbGljYXRlZEtleXNNc2cubGVuZ3RoID4gMCkge1xuICAgICAgY29uc3QgbWVzc2FnZSA9XG4gICAgICAgICdUaGUgcHJvdmlkZWQgdHJhY2sgZXhwcmVzc2lvbiByZXN1bHRlZCBpbiBkdXBsaWNhdGVkIGtleXMgZm9yIGEgZ2l2ZW4gY29sbGVjdGlvbi4gJyArXG4gICAgICAgICdBZGp1c3QgdGhlIHRyYWNraW5nIGV4cHJlc3Npb24gc3VjaCB0aGF0IGl0IHVuaXF1ZWx5IGlkZW50aWZpZXMgYWxsIHRoZSBpdGVtcyBpbiB0aGUgY29sbGVjdGlvbi4gJyArXG4gICAgICAgICdEdXBsaWNhdGVkIGtleXMgd2VyZTogXFxuJyArXG4gICAgICAgIGR1cGxpY2F0ZWRLZXlzTXNnLmpvaW4oJywgXFxuJykgK1xuICAgICAgICAnLic7XG5cbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1jb25zb2xlXG4gICAgICBjb25zb2xlLndhcm4obWVzc2FnZSk7XG4gICAgfVxuICB9XG59XG5cbmZ1bmN0aW9uIGF0dGFjaFByZXZpb3VzbHlEZXRhY2hlZDxULCBWPihcbiAgcHJldkNvbGxlY3Rpb246IExpdmVDb2xsZWN0aW9uPFQsIFY+LFxuICBkZXRhY2hlZEl0ZW1zOiBVbmlxdWVWYWx1ZU11bHRpS2V5TWFwPHVua25vd24sIFQ+IHwgdW5kZWZpbmVkLFxuICBpbmRleDogbnVtYmVyLFxuICBrZXk6IHVua25vd24sXG4pOiBib29sZWFuIHtcbiAgaWYgKGRldGFjaGVkSXRlbXMgIT09IHVuZGVmaW5lZCAmJiBkZXRhY2hlZEl0ZW1zLmhhcyhrZXkpKSB7XG4gICAgcHJldkNvbGxlY3Rpb24uYXR0YWNoKGluZGV4LCBkZXRhY2hlZEl0ZW1zLmdldChrZXkpISk7XG4gICAgZGV0YWNoZWRJdGVtcy5kZWxldGUoa2V5KTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZU9yQXR0YWNoPFQsIFY+KFxuICBsaXZlQ29sbGVjdGlvbjogTGl2ZUNvbGxlY3Rpb248VCwgVj4sXG4gIGRldGFjaGVkSXRlbXM6IFVuaXF1ZVZhbHVlTXVsdGlLZXlNYXA8dW5rbm93biwgVD4gfCB1bmRlZmluZWQsXG4gIHRyYWNrQnlGbjogVHJhY2tCeUZ1bmN0aW9uPHVua25vd24+LFxuICBpbmRleDogbnVtYmVyLFxuICB2YWx1ZTogVixcbikge1xuICBpZiAoXG4gICAgIWF0dGFjaFByZXZpb3VzbHlEZXRhY2hlZChcbiAgICAgIGxpdmVDb2xsZWN0aW9uLFxuICAgICAgZGV0YWNoZWRJdGVtcyxcbiAgICAgIGluZGV4LFxuICAgICAgdHJhY2tCeUZuKGluZGV4LCB2YWx1ZSksXG4gICAgKVxuICApIHtcbiAgICBjb25zdCBuZXdJdGVtID0gbGl2ZUNvbGxlY3Rpb24uY3JlYXRlKGluZGV4LCB2YWx1ZSk7XG4gICAgbGl2ZUNvbGxlY3Rpb24uYXR0YWNoKGluZGV4LCBuZXdJdGVtKTtcbiAgfSBlbHNlIHtcbiAgICBsaXZlQ29sbGVjdGlvbi51cGRhdGVWYWx1ZShpbmRleCwgdmFsdWUpO1xuICB9XG59XG5cbmZ1bmN0aW9uIGluaXRMaXZlSXRlbXNJblRoZUZ1dHVyZTxUPihcbiAgbGl2ZUNvbGxlY3Rpb246IExpdmVDb2xsZWN0aW9uPHVua25vd24sIHVua25vd24+LFxuICBzdGFydDogbnVtYmVyLFxuICBlbmQ6IG51bWJlcixcbiAgdHJhY2tCeUZuOiBUcmFja0J5RnVuY3Rpb248dW5rbm93bj4sXG4pOiBTZXQ8dW5rbm93bj4ge1xuICBjb25zdCBrZXlzID0gbmV3IFNldCgpO1xuICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPD0gZW5kOyBpKyspIHtcbiAgICBrZXlzLmFkZCh0cmFja0J5Rm4oaSwgbGl2ZUNvbGxlY3Rpb24uYXQoaSkpKTtcbiAgfVxuICByZXR1cm4ga2V5cztcbn1cblxuLyoqXG4gKiBBIHNwZWNpZmljLCBwYXJ0aWFsIGltcGxlbWVudGF0aW9uIG9mIHRoZSBNYXAgaW50ZXJmYWNlIHdpdGggdGhlIGZvbGxvd2luZyBjaGFyYWN0ZXJpc3RpY3M6XG4gKiAtIGFsbG93cyBtdWx0aXBsZSB2YWx1ZXMgZm9yIGEgZ2l2ZW4ga2V5O1xuI