@tensorflow/tfjs-converter
Version:
Tensorflow model converter for javascript
142 lines • 18.6 kB
JavaScript
/**
* @license
* Copyright 2019 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
import { parseNodeName } from '../operations/executors/utils';
/**
* Given graph inputs and desired outputs, find the minimal set of nodes
* to execute in order to compute the outputs. In addition return other useful
* info such:
* - Missing inputs needed to compute the output.
* - Whether the subgraph contains dynamic ops (control flow, dynamic shape).
* - Alternative inputs in order to avoid async (dynamic op) execution.
*/
export function getExecutionSubgraph(inputs, outputs, weightMap, initNodes) {
const usedNodes = new Set();
const missingInputs = [];
let dynamicNode = null;
let syncInputs = null;
// Start with the outputs, going backwards and find all the nodes that are
// needed to compute those outputs.
const seen = new Set();
const inputNodeNames = Object.keys(inputs).map(name => parseNodeName(name)[0]);
let initNodeNames = [];
if (initNodes != null) {
initNodeNames = initNodes.map(node => parseNodeName(node.name)[0]);
}
const frontier = [...outputs];
while (frontier.length > 0) {
const node = frontier.pop();
if (isControlFlow(node) || isDynamicShape(node) || isHashTable(node)) {
if (dynamicNode == null) {
dynamicNode = node;
syncInputs = dynamicNode.children.map(child => child.name)
.filter(name => usedNodes.has(name));
}
}
usedNodes.add(node.name);
// Weights are dead end since we already have their values.
if (weightMap[node.name] != null) {
continue;
}
// This node is a dead end since it's one of the user-provided inputs.
if (inputNodeNames.indexOf(node.name) !== -1) {
continue;
}
// This node is a dead end since it doesn't have any inputs.
if (initNodeNames.indexOf(node.name) !== -1) {
continue;
}
if (node.inputs.length === 0) {
missingInputs.push(node.name);
continue;
}
node.inputs.forEach(input => {
// Don't add to the frontier if it is already there.
if (seen.has(input.name)) {
return;
}
seen.add(input.name);
frontier.push(input);
});
}
return { inputs, outputs, usedNodes, missingInputs, dynamicNode, syncInputs };
}
/**
* Given the execution info, return a list of nodes in topological order that
* need to be executed to compute the output.
*/
export function getNodesInTopologicalOrder(graph, weightMap, executionInfo) {
const { usedNodes, inputs } = executionInfo;
const frontier = [];
const inputNodes = Object.keys(inputs)
.map(name => parseNodeName(name)[0])
.map(name => graph.nodes[name]);
const initNodes = graph.initNodes;
inputNodes.forEach(input => {
if (usedNodes.has(input.name)) {
frontier.push(input);
}
});
graph.weights.forEach(weight => {
if (usedNodes.has(weight.name)) {
frontier.push(weight);
}
});
if (initNodes != null) {
initNodes.forEach(node => {
if (usedNodes.has(node.name)) {
frontier.push(node);
}
});
}
const seen = new Set();
const orderedNodes = [];
while (frontier.length > 0) {
const node = frontier.pop();
seen.add(node.name);
if (!weightMap[node.name]) {
orderedNodes.push(node);
}
node.children.forEach(child => {
if (!seen.has(child.name) && usedNodes.has(child.name) &&
child.inputs.every(input => seen.has(input.name))) {
frontier.push(child);
}
});
}
return orderedNodes;
}
const CONTROL_FLOW_OPS = [
'Switch', 'Merge', 'Enter', 'Exit', 'NextIteration', 'StatelessIf',
'StatelessWhile', 'if', 'While'
];
const DYNAMIC_SHAPE_OPS = [
'NonMaxSuppressionV2', 'NonMaxSuppressionV3', 'NonMaxSuppressionV5', 'Where'
];
const HASH_TABLE_OPS = [
'HashTable', 'HashTableV2', 'LookupTableImport', 'LookupTableImportV2',
'LookupTableFind', 'LookupTableFindV2', 'LookupTableSize', 'LookupTableSizeV2'
];
export function isControlFlow(node) {
return CONTROL_FLOW_OPS.indexOf(node.op) >= 0;
}
export function isDynamicShape(node) {
return DYNAMIC_SHAPE_OPS.indexOf(node.op) >= 0;
}
export function isHashTable(node) {
return HASH_TABLE_OPS.indexOf(node.op) >= 0;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWxfYW5hbHlzaXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi90ZmpzLWNvbnZlcnRlci9zcmMvZXhlY3V0b3IvbW9kZWxfYW5hbHlzaXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBS0gsT0FBTyxFQUFDLGFBQWEsRUFBQyxNQUFNLCtCQUErQixDQUFDO0FBWTVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsb0JBQW9CLENBQ2hDLE1BQXNCLEVBQUUsT0FBZSxFQUFFLFNBQTBCLEVBQ25FLFNBQWtCO0lBQ3BCLE1BQU0sU0FBUyxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDcEMsTUFBTSxhQUFhLEdBQWEsRUFBRSxDQUFDO0lBQ25DLElBQUksV0FBVyxHQUFTLElBQUksQ0FBQztJQUM3QixJQUFJLFVBQVUsR0FBYSxJQUFJLENBQUM7SUFFaEMsMEVBQTBFO0lBQzFFLG1DQUFtQztJQUNuQyxNQUFNLElBQUksR0FBRyxJQUFJLEdBQUcsRUFBVSxDQUFDO0lBQy9CLE1BQU0sY0FBYyxHQUNoQixNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRTVELElBQUksYUFBYSxHQUFhLEVBQUUsQ0FBQztJQUNqQyxJQUFJLFNBQVMsSUFBSSxJQUFJLEVBQUU7UUFDckIsYUFBYSxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDcEU7SUFFRCxNQUFNLFFBQVEsR0FBRyxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUM7SUFDOUIsT0FBTyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMs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O2FBQ3RCO1FBQ0gsQ0FBQyxDQUFDLENBQUM7S0FDSjtJQUNELE9BQU8sWUFBWSxDQUFDO0FBQ3RCLENBQUM7QUFFRCxNQUFNLGdCQUFnQixHQUFHO0lBQ3ZCLFFBQVEsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxlQUFlLEVBQUUsYUFBYTtJQUNsRSxnQkFBZ0IsRUFBRSxJQUFJLEVBQUUsT0FBTztDQUNoQyxDQUFDO0FBQ0YsTUFBTSxpQkFBaUIsR0FBRztJQUN4QixxQkFBcUIsRUFBRSxxQkFBcUIsRUFBRSxxQkFBcUIsRUFBRSxPQUFPO0NBQzdFLENBQUM7QUFDRixNQUFNLGNBQWMsR0FBRztJQUNyQixXQUFXLEVBQUUsYUFBYSxFQUFFLG1CQUFtQixFQUFFLHFCQUFxQjtJQUN0RSxpQkFBaUIsRUFBRSxtQkFBbUIsRUFBRSxpQkFBaUIsRUFBRSxtQkFBbUI7Q0FDL0UsQ0FBQztBQUVGLE1BQU0sVUFBVSxhQUFhLENBQUMsSUFBVTtJQUN0QyxPQUFPLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ2hELENBQUM7QUFFRCxNQUFNLFVBQVUsY0FBYyxDQUFDLElBQVU7SUFDdkMsT0FBTyxpQkFBaUIsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUNqRCxDQUFDO0FBRUQsTUFBTSxVQUFVLFdBQVcsQ0FBQyxJQUFVO0lBQ3BDLE9BQU8sY0FBYyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQzlDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAxOSBHb29nbGUgTExDLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbmltcG9ydCB7TmFtZWRUZW5zb3JNYXB9IGZyb20gJ0B0ZW5zb3JmbG93L3RmanMtY29yZSc7XG5cbmltcG9ydCB7TmFtZWRUZW5zb3JzTWFwfSBmcm9tICcuLi9kYXRhL3R5cGVzJztcbmltcG9ydCB7cGFyc2VOb2RlTmFtZX0gZnJvbSAnLi4vb3BlcmF0aW9ucy9leGVjdXRvcnMvdXRpbHMnO1xuaW1wb3J0IHtHcmFwaCwgTm9kZX0gZnJvbSAnLi4vb3BlcmF0aW9ucy90eXBlcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRXhlY3V0aW9uSW5mbyB7XG4gIGlucHV0czogTmFtZWRUZW5zb3JNYXA7XG4gIG91dHB1dHM6IE5vZGVbXTtcbiAgdXNlZE5vZGVzOiBTZXQ8c3RyaW5nPjtcbiAgbWlzc2luZ0lucHV0czogc3RyaW5nW107XG4gIGR5bmFtaWNOb2RlOiBOb2RlO1xuICBzeW5jSW5wdXRzOiBzdHJpbmdbXTtcbn1cblxuLyoqXG4gKiBHaXZlbiBncmFwaCBpbnB1dHMgYW5kIGRlc2lyZWQgb3V0cHV0cywgZmluZCB0aGUgbWluaW1hbCBzZXQgb2Ygbm9kZXNcbiAqIHRvIGV4ZWN1dGUgaW4gb3JkZXIgdG8gY29tcHV0ZSB0aGUgb3V0cHV0cy4gSW4gYWRkaXRpb24gcmV0dXJuIG90aGVyIHVzZWZ1bFxuICogaW5mbyBzdWNoOlxuICogLSBNaXNzaW5nIGlucHV0cyBuZWVkZWQgdG8gY29tcHV0ZSB0aGUgb3V0cHV0LlxuICogLSBXaGV0aGVyIHRoZSBzdWJncmFwaCBjb250YWlucyBkeW5hbWljIG9wcyAoY29udHJvbCBmbG93LCBkeW5hbWljIHNoYXBlKS5cbiAqIC0gQWx0ZXJuYXRpdmUgaW5wdXRzIGluIG9yZGVyIHRvIGF2b2lkIGFzeW5jIChkeW5hbWljIG9wKSBleGVjdXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRFeGVjdXRpb25TdWJncmFwaChcbiAgICBpbnB1dHM6IE5hbWVkVGVuc29yTWFwLCBvdXRwdXRzOiBOb2RlW10sIHdlaWdodE1hcDogTmFtZWRUZW5zb3JzTWFwLFxuICAgIGluaXROb2Rlcz86IE5vZGVbXSk6IEV4ZWN1dGlvbkluZm8ge1xuICBjb25zdCB1c2VkTm9kZXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgY29uc3QgbWlzc2luZ0lucHV0czogc3RyaW5nW10gPSBbXTtcbiAgbGV0IGR5bmFtaWNOb2RlOiBOb2RlID0gbnVsbDtcbiAgbGV0IHN5bmNJbnB1dHM6IHN0cmluZ1tdID0gbnVsbDtcblxuICAvLyBTdGFydCB3aXRoIHRoZSBvdXRwdXRzLCBnb2luZyBiYWNrd2FyZHMgYW5kIGZpbmQgYWxsIHRoZSBub2RlcyB0aGF0IGFyZVxuICAvLyBuZWVkZWQgdG8gY29tcHV0ZSB0aG9zZSBvdXRwdXRzLlxuICBjb25zdCBzZWVuID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gIGNvbnN0IGlucHV0Tm9kZU5hbWVzID1cbiAgICAgIE9iamVjdC5rZXlzKGlucHV0cykubWFwKG5hbWUgPT4gcGFyc2VOb2RlTmFtZShuYW1lKVswXSk7XG5cbiAgbGV0IGluaXROb2RlTmFtZXM6IHN0cmluZ1tdID0gW107XG4gIGlmIChpbml0Tm9kZXMgIT0gbnVsbCkge1xuICAgIGluaXROb2RlTmFtZXMgPSBpbml0Tm9kZXMubWFwKG5vZGUgPT4gcGFyc2VOb2RlTmFtZShub2RlLm5hbWUpWzBdKTtcbiAgfVxuXG4gIGNvbnN0IGZyb250aWVyID0gWy4uLm91dHB1dHNdO1xuICB3aGlsZSAoZnJvbnRpZXIubGVuZ3RoID4gMCkge1xuICAgIGNvbnN0IG5vZGUgPSBmcm9udGllci5wb3AoKTtcbiAgICBpZiAoaXNDb250cm9sRmxvdyhub2RlKSB8fCBpc0R5bmFtaWNTaGFwZShub2RlKSB8fCBpc0hhc2hUYWJsZShub2RlKSkge1xuICAgICAgaWYgKGR5bmFtaWNOb2RlID09IG51bGwpIHtcbiAgICAgICAgZHluYW1pY05vZGUgPSBub2RlO1xuICAgICAgICBzeW5jSW5wdXRzID0gZHluYW1pY05vZGUuY2hpbGRyZW4ubWFwKGNoaWxkID0+IGNoaWxkLm5hbWUpXG4gICAgICAgICAgICAgICAgICAgICAgICAgLmZpbHRlcihuYW1lID0+IHVzZWROb2Rlcy5oYXMobmFtZSkpO1xuICAgICAgfVxuICAgIH1cbiAgICB1c2VkTm9kZXMuYWRkKG5vZGUubmFtZSk7XG5cbiAgICAvLyBXZWlnaHRzIGFyZSBkZWFkIGVuZCBzaW5jZSB3ZSBhbHJlYWR5IGhhdmUgdGhlaXIgdmFsdWVzLlxuICAgIGlmICh3ZWlnaHRNYXBbbm9kZS5uYW1lXSAhPSBudWxsKSB7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgLy8gVGhpcyBub2RlIGlzIGEgZGVhZCBlbmQgc2luY2UgaXQncyBvbmUgb2YgdGhlIHVzZXItcHJvdmlkZWQgaW5wdXRzLlxuICAgIGlmIChpbnB1dE5vZGVOYW1lcy5pbmRleE9mKG5vZGUubmFtZSkgIT09IC0xKSB7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgLy8gVGhpcyBub2RlIGlzIGEgZGVhZCBlbmQgc2luY2UgaXQgZG9lc24ndCBoYXZlIGFueSBpbnB1dHMuXG4gICAgaWYgKGluaXROb2RlTmFtZXMuaW5kZXhPZihub2RlLm5hbWUpICE9PSAtMSkge1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIGlmIChub2RlLmlucHV0cy5sZW5ndGggPT09IDApIHtcbiAgICAgIG1pc3NpbmdJbnB1dHMucHVzaChub2RlLm5hbWUpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIG5vZGUuaW5wdXRzLmZvckVhY2goaW5wdXQgPT4ge1xuICAgICAgLy8gRG9uJ3QgYWRkIHRvIHRoZSBmcm9udGllciBpZiBpdCBpcyBhbHJlYWR5IHRoZXJlLlxuICAgICAgaWYgKHNlZW4uaGFzKGlucHV0Lm5hbWUpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHNlZW4uYWRkKGlucHV0Lm5hbWUpO1xuICAgICAgZnJvbnRpZXIucHVzaChpbnB1dCk7XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIHtpbnB1dHMsIG91dHB1dHMsIHVzZWROb2RlcywgbWlzc2luZ0lucHV0cywgZHluYW1pY05vZGUsIHN5bmNJbnB1dHN9O1xufVxuXG4vKipcbiAqIEdpdmVuIHRoZSBleGVjdXRpb24gaW5mbywgcmV0dXJuIGEgbGlzdCBvZiBub2RlcyBpbiB0b3BvbG9naWNhbCBvcmRlciB0aGF0XG4gKiBuZWVkIHRvIGJlIGV4ZWN1dGVkIHRvIGNvbXB1dGUgdGhlIG91dHB1dC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldE5vZGVzSW5Ub3BvbG9naWNhbE9yZGVyKFxuICAgIGdyYXBoOiBHcmFwaCwgd2VpZ2h0TWFwOiBOYW1lZFRlbnNvcnNNYXAsXG4gICAgZXhlY3V0aW9uSW5mbzogRXhlY3V0aW9uSW5mbyk6IE5vZGVbXSB7XG4gIGNvbnN0IHt1c2VkTm9kZXMsIGlucHV0c30gPSBleGVjdXRpb25JbmZvO1xuICBjb25zdCBmcm9udGllcjogTm9kZVtdID0gW107XG4gIGNvbnN0IGlucHV0Tm9kZXMgPSBPYmplY3Qua2V5cyhpbnB1dHMpXG4gICAgICAgICAgICAgICAgICAgICAgICAgLm1hcChuYW1lID0+IHBhcnNlTm9kZU5hbWUobmFtZSlbMF0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgLm1hcChuYW1lID0+IGdyYXBoLm5vZGVzW25hbWVdKTtcbiAgY29uc3QgaW5pdE5vZGVzID0gZ3JhcGguaW5pdE5vZGVzO1xuXG4gIGlucHV0Tm9kZXMuZm9yRWFjaChpbnB1dCA9PiB7XG4gICAgaWYgKHVzZWROb2Rlcy5oYXMoaW5wdXQubmFtZSkpIHtcbiAgICAgIGZyb250aWVyLnB1c2goaW5wdXQpO1xuICAgIH1cbiAgfSk7XG4gIGdyYXBoLndlaWdodHMuZm9yRWFjaCh3ZWlnaHQgPT4ge1xuICAgIGlmICh1c2VkTm9kZXMuaGFzKHdlaWdodC5uYW1lKSkge1xuICAgICAgZnJvbnRpZXIucHVzaCh3ZWlnaHQpO1xuICAgIH1cbiAgfSk7XG4gIGlmIChpbml0Tm9kZXMgIT0gbnVsbCkge1xuICAgIGluaXROb2Rlcy5mb3JFYWNoKG5vZGUgPT4ge1xuICAgICAgaWYgKHVzZWROb2Rlcy5oYXMobm9kZS5uYW1lKSkge1xuICAgICAgICBmcm9udGllci5wdXNoKG5vZGUpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIGNvbnN0IHNlZW4gPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgY29uc3Qgb3JkZXJlZE5vZGVzOiBOb2RlW10gPSBbXTtcbiAgd2hpbGUgKGZyb250aWVyLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBub2RlID0gZnJvbnRpZXIucG9wKCk7XG4gICAgc2Vlbi5hZGQobm9kZS5uYW1lKTtcbiAgICBpZiAoIXdlaWdodE1hcFtub2RlLm5hbWVdKSB7XG4gICAgICBvcmRlcmVkTm9kZXMucHVzaChub2RlKTtcbiAgICB9XG4gICAgbm9kZS5jaGlsZHJlbi5mb3JFYWNoKGNoaWxkID0+IHtcbiAgICAgIGlmICghc2Vlbi5oYXMoY2hpbGQubmFtZSkgJiYgdXNlZE5vZGVzLmhhcyhjaGlsZC5uYW1lKSAmJlxuICAgICAgICAgIGNoaWxkLmlucHV0cy5ldmVyeShpbnB1dCA9PiBzZWVuLmhhcyhpbnB1dC5uYW1lKSkpIHtcbiAgICAgICAgZnJvbnRpZXIucHVzaChjaGlsZCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIG9yZGVyZWROb2Rlcztcbn1cblxuY29uc3QgQ09OVFJPTF9GTE9XX09QUyA9IFtcbiAgJ1N3aXRjaCcsICdNZXJnZScsICdFbnRlcicsICdFeGl0JywgJ05leHRJdGVyYXRpb24nLCAnU3RhdGVsZXNzSWYnLFxuICAnU3RhdGVsZXNzV2hpbGUnLCAnaWYnLCAnV2hpbGUnXG5dO1xuY29uc3QgRFlOQU1JQ19TSEFQRV9PUFMgPSBbXG4gICdOb25NYXhTdXBwcmVzc2lvblYyJywgJ05vbk1heFN1cHByZXNzaW9uVjMnLCAnTm9uTWF4U3VwcHJlc3Npb25WNScsICdXaGVyZSdcbl07XG5jb25zdCBIQVNIX1RBQkxFX09QUyA9IFtcbiAgJ0hhc2hUYWJsZScsICdIYXNoVGFibGVWMicsICdMb29rdXBUYWJsZUltcG9ydCcsICdMb29rdXBUYWJsZUltcG9ydFYyJyxcbiAgJ0xvb2t1cFRhYmxlRmluZCcsICdMb29rdXBUYWJsZUZpbmRWMicsICdMb29rdXBUYWJsZVNpemUnLCAnTG9va3VwVGFibGVTaXplVjInXG5dO1xuXG5leHBvcnQgZnVuY3Rpb24gaXNDb250cm9sRmxvdyhub2RlOiBOb2RlKSB7XG4gIHJldHVybiBDT05UUk9MX0ZMT1dfT1BTLmluZGV4T2Yobm9kZS5vcCkgPj0gMDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzRHluYW1pY1NoYXBlKG5vZGU6IE5vZGUpIHtcbiAgcmV0dXJuIERZTkFNSUNfU0hBUEVfT1BTLmluZGV4T2Yobm9kZS5vcCkgPj0gMDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzSGFzaFRhYmxlKG5vZGU6IE5vZGUpIHtcbiAgcmV0dXJuIEhBU0hfVEFCTEVfT1BTLmluZGV4T2Yobm9kZS5vcCkgPj0gMDtcbn1cbiJdfQ==