@openfisca/json-model
Version:
Library to handle informations extracted in JSON or YAML format from OpenFisca parameters, variables, etc
397 lines (392 loc) • 65.4 kB
JavaScript
/// Structure and functions of editable parameters
/// Note: raw parameters have no structure.
export let ParameterClass = /*#__PURE__*/function (ParameterClass) {
ParameterClass["Node"] = "Node";
ParameterClass["Scale"] = "Scale";
ParameterClass["Value"] = "Value";
return ParameterClass;
}({});
export let ScaleType = /*#__PURE__*/function (ScaleType) {
ScaleType["LinearAverageRate"] = "linear_average_rate";
ScaleType["MarginalAmount"] = "marginal_amount";
ScaleType["MarginalRate"] = "marginal_rate";
ScaleType["SingleAmount"] = "single_amount";
return ScaleType;
}({});
export let ValueType = /*#__PURE__*/function (ValueType) {
ValueType["Boolean"] = "boolean";
ValueType["Number"] = "number";
ValueType["StringArray"] = "string_array";
ValueType["StringByString"] = "string_by_string";
return ValueType;
}({});
export const revaluationTypes = ["income_tax_usual_revaluation", "legal_revaluation", "legal_revaluation_following_income_tax_usual_revaluation"];
export function accessParameterFromIds(parameter, ids) {
for (const id of ids) {
if (parameter.class !== ParameterClass.Node) {
return [parameter, `Can't access to child "${id}" in parameter ${parameter.name}, because it is not a node`];
}
const child = parameter.children?.[id];
if (child === undefined) {
return [parameter, `Parameter ${parameter.name} has no child named "${id}"`];
}
parameter = child;
}
return [parameter, null];
}
/// Convert a dictionary of brackets by instant to a list of brackets
/// containing amounts, bases, rates & thresholds by instant.
export function bracketsFromScaleByInstant(scaleByInstant) {
const brackets = [];
for (const key of ["amount", "base", "rate", "threshold"]) {
let latestValueByBracketIndex = {};
for (const [instant, scaleAtInstant] of Object.entries(scaleByInstant).sort(([instant1], [instant2]) => instant1.localeCompare(instant2))) {
for (const [bracketIndex, bracketAtInstant] of scaleAtInstant.entries()) {
if (brackets.length <= bracketIndex) {
brackets.push({});
}
const value = bracketAtInstant[key];
if (value !== undefined) {
const bracket = brackets[bracketIndex];
const latestValue = latestValueByBracketIndex[bracketIndex];
const valueByInstant = bracket[key] ??= {};
if (value === "expected") {
if (value !== latestValue) {
latestValueByBracketIndex[bracketIndex] = valueByInstant[instant] = value;
}
} else if (latestValue === undefined || latestValue === "expected" || value.value !== latestValue.value) {
latestValueByBracketIndex[bracketIndex] = valueByInstant[instant] = value;
} else if (value.reference !== undefined) {
let latestReferences = latestValue.reference;
if (latestReferences === undefined) {
latestReferences = latestValue.reference = [];
}
for (const reference of value.reference) {
if (!latestReferences.some(latestReference => latestReference.href === reference.href && latestReference.title === reference.title)) {
latestReferences.push(reference);
}
}
}
}
}
const latestValueByBracketIndexLength = Object.keys(latestValueByBracketIndex).length;
for (let bracketIndex = scaleAtInstant.length; bracketIndex < latestValueByBracketIndexLength; bracketIndex++) {
const bracket = brackets[bracketIndex];
const valueByInstant = bracket[key] ??= {};
valueByInstant[instant] = {
value: null
};
delete latestValueByBracketIndex[bracketIndex];
}
}
}
return brackets;
}
export function improveParameter(parameter, parent = undefined, ids = []) {
const id = ids[ids.length - 1];
if (id !== undefined) {
parameter.id = id;
}
if (parameter.name === undefined) {
parameter.name = ids.join(".");
}
if (parent != null) {
parameter.parent = parent;
}
const title = parameter.short_label ?? parameter.description ?? id?.replace(/_/g, " ").replace(/^\w/, c => c.toUpperCase());
if (title !== undefined) {
parameter.title = title;
}
parameter.titles = (title === undefined ? parent?.titles ?? [] : [...(parent?.titles ?? []), title]).filter(Boolean);
switch (parameter.class) {
case ParameterClass.Node:
if (parameter.children !== undefined) {
for (const [childId, child] of Object.entries(parameter.children)) {
improveParameter(child, parameter, [...ids, childId]);
}
}
break;
default:
}
}
export function isAmountScaleParameter(parameter) {
return [ScaleType.MarginalAmount, ScaleType.SingleAmount].includes(parameter.type);
}
export function isRateScaleParameter(parameter) {
return ![ScaleType.MarginalAmount, ScaleType.SingleAmount].includes(parameter.type);
}
export function isVectorialNodeParameter(node) {
if (node.children === undefined || Object.keys(node.children).length <= 1) {
return false;
}
const children = Object.values(node.children);
if (children.some(child => child.class !== ParameterClass.Value)) {
return false;
}
const commonType = children[0].type;
if (children.some(child => child.type !== commonType)) {
return false;
}
const commonFilePath = children[0].file_path;
if (children.some(child => child.file_path !== commonFilePath)) {
return false;
}
return true;
}
export function* iterParameterAncestors(parameter) {
if (parameter == null || !parameter.id) {
return;
}
yield* iterParameterAncestors(parameter.parent);
yield parameter;
}
function mergeDuplicateParameters(existingParameter, parameter) {
if (existingParameter.class === ParameterClass.Node) {
const existingChidren = existingParameter.children;
for (const child of Object.values(parameter.children)) {
const existingChild = existingChidren[child.id ?? ""];
if (existingChild === undefined) {
existingChidren[child.id ?? ""] = child;
child.parent = existingParameter;
} else {
mergeDuplicateParameters(existingChild, child);
}
}
}
}
export function mergeParameters(parameters) {
const rootParameterById = {};
for (const parameter of parameters) {
// Create a copy of the parameters tree, containing only this parameter.
let rootParameter = {
...parameter
};
for (let ancestor = rootParameter.parent; ancestor !== undefined && ancestor.id; rootParameter = ancestor, ancestor = rootParameter.parent) {
ancestor = {
...ancestor,
children: {
[rootParameter.id]: rootParameter
}
};
rootParameter.parent = ancestor;
}
// Merge this simplified parameters tree with the others.
const existingRootParameter = rootParameterById[rootParameter.id ?? ""];
if (existingRootParameter === undefined) {
rootParameterById[rootParameter.id ?? ""] = rootParameter;
} else {
mergeDuplicateParameters(existingRootParameter, rootParameter);
}
}
return rootParameterById;
}
export function mergeReferences(references1, references2) {
if (references1 == null) {
return references2 ?? undefined;
}
if (references2 == null) {
return references1 ?? undefined;
}
const references = [...references1];
for (const reference2 of references2) {
let found = false;
if (reference2.href !== undefined) {
const index = references.findIndex(reference => reference.href === reference2.href);
if (index >= 0) {
found = true;
let reference = references[index];
let changed = false;
if (reference.note === undefined && reference2.note !== undefined) {
if (!changed) {
changed = true;
reference = references[index] = {
...reference
};
}
reference.note = reference2.note;
}
if (reference.title === undefined && reference2.title !== undefined) {
if (!changed) {
changed = true;
reference = references[index] = {
...reference
};
}
reference.title = reference2.title;
}
}
} else if (reference2.title !== undefined) {
const index = references.findIndex(reference => reference.title === reference2.title);
if (index >= 0) {
found = true;
let reference = references[index];
if (reference.note === undefined && reference2.note !== undefined) {
reference = references[index] = {
...reference
};
reference.note = reference2.note;
}
}
} else {
// assert.notStrictEqual(reference2.note, undefined)
const index = references.findIndex(reference => reference.note === reference2.note);
if (index >= 0) {
found = true;
}
}
if (!found) {
references.push(reference2);
}
}
return references;
}
export function newParameterRepositoryUrl(metadata, parameter) {
if (parameter.file_path === undefined) {
return undefined;
}
const packageName = parameter.file_path.split("/")[0];
for (const packageMetadata of metadata.packages) {
if (packageMetadata.name === packageName) {
return `${packageMetadata.repository_url}/blob/${packageMetadata.version}/${parameter.file_path}`;
}
}
return undefined;
}
export function parameterLastReviewOrChange(parameter) {
switch (parameter.class) {
case ParameterClass.Node:
return parameter.last_value_still_valid_on;
case ParameterClass.Scale:
{
const latestInstant = [parameter.last_value_still_valid_on, ...Object.keys(scaleByInstantFromBrackets(parameter.brackets))].filter(instant => instant !== undefined).sort((instant1, instant2) => instant2.localeCompare(instant1))[0];
return latestInstant;
}
case ParameterClass.Value:
{
const latestInstant = [parameter.last_value_still_valid_on, ...Object.keys(parameter.values)].filter(instant => instant !== undefined).sort((instant1, instant2) => instant2.localeCompare(instant1))[0];
return latestInstant;
}
}
}
export function parameterWithoutChildren(parameter) {
if (parameter.class === ParameterClass.Node) {
parameter = {
...parameter
};
delete parameter.children;
}
return parameter;
}
/// Apply changes of a reform to (root) parameter.
export function patchParameter(parameter, patch) {
if (Object.keys(patch).length === 0) {
return parameter;
}
const patchedParameter = {
...parameter
};
for (const [key, value] of Object.entries(patch)) {
if (value === null) {
delete patchedParameter[key];
} else if (patchedParameter[key] === undefined) {
;
patchedParameter[key] = value;
} else if (key === "children") {
const patchedChildren = patchedParameter.children = {
...patchedParameter.children
};
for (const [childId, childPatch] of Object.entries(value)) {
if (childPatch === null) {
delete patchedChildren[childId];
} else if (patchedChildren[childId] === undefined) {
patchedChildren[childId] = childPatch;
} else {
patchedChildren[childId] = patchParameter(patchedChildren[childId], childPatch);
}
}
} else {
;
patchedParameter[key] = value;
}
}
return patchedParameter;
}
/// Convert a list of brackets containing amounts, bases, rates & thresholds by instant
/// to a dictionary of brackets by instant.
/// For heuristic, see function `build_api_scale` of
/// https://github.com/openfisca/openfisca-core/blob/master/openfisca_web_api/loader/parameters.py
export function scaleByInstantFromBrackets(brackets) {
const instants = new Set();
for (const bracket of brackets) {
for (const instant of Object.keys(bracket.amount ?? {})) {
instants.add(instant);
}
for (const instant of Object.keys(bracket.base ?? {})) {
instants.add(instant);
}
for (const instant of Object.keys(bracket.rate ?? {})) {
instants.add(instant);
}
for (const instant of Object.keys(bracket.threshold)) {
instants.add(instant);
}
}
const scaleAtInstantByInstant = {};
for (const instant of instants) {
const scaleAtInstant = [];
for (const bracket of brackets) {
const bracketAtInstant = {};
for (const key of ["amount", "base", "rate", "threshold"]) {
const valueByInstant = bracket?.[key];
if (valueByInstant === undefined) {
continue;
}
let bestInstant = undefined;
for (const valueInstant of Object.keys(valueByInstant)) {
if ((bestInstant === undefined || bestInstant.localeCompare(valueInstant) < 0) && valueInstant.localeCompare(instant) <= 0) {
bestInstant = valueInstant;
}
}
if (bestInstant === undefined) {
continue;
}
bracketAtInstant[key] = valueByInstant[bestInstant];
}
if (Object.keys(bracketAtInstant).length === 0 || Object.values(bracketAtInstant).every(value => value !== "expected" && value.value === null)) {
continue;
}
scaleAtInstant.push(bracketAtInstant);
}
// Add the scaleAtInstant, even when it is empty.
scaleAtInstantByInstant[instant] = scaleAtInstant;
}
return scaleAtInstantByInstant;
}
export function scaleParameterUsesBase(parameter) {
return isRateScaleParameter(parameter) && parameter.brackets.some(bracket => bracket.base !== undefined);
}
export function* walkParameters(parameter, {
depthFirst,
withNodes
} = {}) {
if (!depthFirst && (parameter.class !== ParameterClass.Node || withNodes)) {
yield parameter;
}
switch (parameter.class) {
case ParameterClass.Node:
if (parameter.children !== undefined) {
for (const child of Object.values(parameter.children)) {
yield* walkParameters(child, {
depthFirst,
withNodes
});
}
}
break;
default:
}
if (depthFirst && (parameter.class !== ParameterClass.Node || withNodes)) {
yield parameter;
}
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJQYXJhbWV0ZXJDbGFzcyIsIlNjYWxlVHlwZSIsIlZhbHVlVHlwZSIsInJldmFsdWF0aW9uVHlwZXMiLCJhY2Nlc3NQYXJhbWV0ZXJGcm9tSWRzIiwicGFyYW1ldGVyIiwiaWRzIiwiaWQiLCJjbGFzcyIsIk5vZGUiLCJuYW1lIiwiY2hpbGQiLCJjaGlsZHJlbiIsInVuZGVmaW5lZCIsImJyYWNrZXRzRnJvbVNjYWxlQnlJbnN0YW50Iiwic2NhbGVCeUluc3RhbnQiLCJicmFja2V0cyIsImtleSIsImxhdGVzdFZhbHVlQnlCcmFja2V0SW5kZXgiLCJpbnN0YW50Iiwic2NhbGVBdEluc3RhbnQiLCJPYmplY3QiLCJlbnRyaWVzIiwic29ydCIsImluc3RhbnQxIiwiaW5zdGFudDIiLCJsb2NhbGVDb21wYXJlIiwiYnJhY2tldEluZGV4IiwiYnJhY2tldEF0SW5zdGFudCIsImxlbmd0aCIsInB1c2giLCJ2YWx1ZSIsImJyYWNrZXQiLCJsYXRlc3RWYWx1ZSIsInZhbHVlQnlJbnN0YW50IiwicmVmZXJlbmNlIiwibGF0ZXN0UmVmZXJlbmNlcyIsInNvbWUiLCJsYXRlc3RSZWZlcmVuY2UiLCJocmVmIiwidGl0bGUiLCJsYXRlc3RWYWx1ZUJ5QnJhY2tldEluZGV4TGVuZ3RoIiwia2V5cyIsImltcHJvdmVQYXJhbWV0ZXIiLCJwYXJlbnQiLCJqb2luIiwic2hvcnRfbGFiZWwiLCJkZXNjcmlwdGlvbiIsInJlcGxhY2UiLCJjIiwidG9VcHBlckNhc2UiLCJ0aXRsZXMiLCJmaWx0ZXIiLCJCb29sZWFuIiwiY2hpbGRJZCIsImlzQW1vdW50U2NhbGVQYXJhbWV0ZXIiLCJNYXJnaW5hbEFtb3VudCIsIlNpbmdsZUFtb3VudCIsImluY2x1ZGVzIiwidHlwZSIsImlzUmF0ZVNjYWxlUGFyYW1ldGVyIiwiaXNWZWN0b3JpYWxOb2RlUGFyYW1ldGVyIiwibm9kZSIsInZhbHVlcyIsIlZhbHVlIiwiY29tbW9uVHlwZSIsImNvbW1vbkZpbGVQYXRoIiwiZmlsZV9wYXRoIiwiaXRlclBhcmFtZXRlckFuY2VzdG9ycyIsIm1lcmdlRHVwbGljYXRlUGFyYW1ldGVycyIsImV4aXN0aW5nUGFyYW1ldGVyIiwiZXhpc3RpbmdDaGlkcmVuIiwiZXhpc3RpbmdDaGlsZCIsIm1lcmdlUGFyYW1ldGVycyIsInBhcmFtZXRlcnMiLCJyb290UGFyYW1ldGVyQnlJZCIsInJvb3RQYXJhbWV0ZXIiLCJhbmNlc3RvciIsImV4aXN0aW5nUm9vdFBhcmFtZXRlciIsIm1lcmdlUmVmZXJlbmNlcyIsInJlZmVyZW5jZXMxIiwicmVmZXJlbmNlczIiLCJyZWZlcmVuY2VzIiwicmVmZXJlbmNlMiIsImZvdW5kIiwiaW5kZXgiLCJmaW5kSW5kZXgiLCJjaGFuZ2VkIiwibm90ZSIsIm5ld1BhcmFtZXRlclJlcG9zaXRvcnlVcmwiLCJtZXRhZGF0YSIsInBhY2thZ2VOYW1lIiwic3BsaXQiLCJwYWNrYWdlTWV0YWRhdGEiLCJwYWNrYWdlcyIsInJlcG9zaXRvcnlfdXJsIiwidmVyc2lvbiIsInBhcmFtZXRlckxhc3RSZXZpZXdPckNoYW5nZSIsImxhc3RfdmFsdWVfc3RpbGxfdmFsaWRfb24iLCJTY2FsZSIsImxhdGVzdEluc3RhbnQiLCJzY2FsZUJ5SW5zdGFudEZyb21CcmFja2V0cyIsInBhcmFtZXRlcldpdGhvdXRDaGlsZHJlbiIsInBhdGNoUGFyYW1ldGVyIiwicGF0Y2giLCJwYXRjaGVkUGFyYW1ldGVyIiwicGF0Y2hlZENoaWxkcmVuIiwiY2hpbGRQYXRjaCIsImluc3RhbnRzIiwiU2V0IiwiYW1vdW50IiwiYWRkIiwiYmFzZSIsInJhdGUiLCJ0aHJlc2hvbGQiLCJzY2FsZUF0SW5zdGFudEJ5SW5zdGFudCIsImJlc3RJbnN0YW50IiwidmFsdWVJbnN0YW50IiwiZXZlcnkiLCJzY2FsZVBhcmFtZXRlclVzZXNCYXNlIiwid2Fsa1BhcmFtZXRlcnMiLCJkZXB0aEZpcnN0Iiwid2l0aE5vZGVzIl0sInNvdXJjZXMiOlsiLi4vc3JjL3BhcmFtZXRlcnMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8vIFN0cnVjdHVyZSBhbmQgZnVuY3Rpb25zIG9mIGVkaXRhYmxlIHBhcmFtZXRlcnNcbi8vLyBOb3RlOiByYXcgcGFyYW1ldGVycyBoYXZlIG5vIHN0cnVjdHVyZS5cblxuaW1wb3J0IHR5cGUgeyBNZXRhZGF0YSB9IGZyb20gXCIuL21ldGFkYXRhXCJcbmltcG9ydCB0eXBlIHsgUmVmZXJlbmNlLCBSZWZlcmVuY2VzQnlJbnN0YW50IH0gZnJvbSBcIi4vcmVmZXJlbmNlc1wiXG5cbmV4cG9ydCBpbnRlcmZhY2UgQW1vdW50QnJhY2tldCBleHRlbmRzIEJyYWNrZXRCYXNlIHtcbiAgYW1vdW50OiB7IFtpbnN0YW50OiBzdHJpbmddOiBOdW1iZXJWYWx1ZSB8IFwiZXhwZWN0ZWRcIiB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQW1vdW50QnJhY2tldEF0SW5zdGFudCBleHRlbmRzIEJyYWNrZXRBdEluc3RhbnRCYXNlIHtcbiAgYW1vdW50OiBOdW1iZXJWYWx1ZSB8IFwiZXhwZWN0ZWRcIlxufVxuXG5leHBvcnQgaW50ZXJmYWNlIEJvb2xlYW5QYXJhbWV0ZXIgZXh0ZW5kcyBWYWx1ZVBhcmFtZXRlckJhc2Uge1xuICB0eXBlOiBWYWx1ZVR5cGUuQm9vbGVhblxuICB2YWx1ZXM6IHsgW2luc3RhbnQ6IHN0cmluZ106IE1heWJlQm9vbGVhblZhbHVlIHwgXCJleHBlY3RlZFwiIH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBCb29sZWFuVmFsdWUgZXh0ZW5kcyBWYWx1ZUJhc2Uge1xuICB2YWx1ZTogYm9vbGVhblxufVxuXG5leHBvcnQgdHlwZSBCcmFja2V0ID0gQW1vdW50QnJhY2tldCB8IFJhdGVCcmFja2V0XG5cbmV4cG9ydCB0eXBlIEJyYWNrZXRBdEluc3RhbnQgPSBBbW91bnRCcmFja2V0QXRJbnN0YW50IHwgUmF0ZUJyYWNrZXRBdEluc3RhbnRcblxuZXhwb3J0IGludGVyZmFjZSBCcmFja2V0QXRJbnN0YW50QmFzZSB7XG4gIHRocmVzaG9sZDogTWF5YmVOdW1iZXJWYWx1ZSB8IFwiZXhwZWN0ZWRcIlxufVxuXG5leHBvcnQgaW50ZXJmYWNlIEJyYWNrZXRCYXNlIHtcbiAgdGhyZXNob2xkOiB7IFtpbnN0YW50OiBzdHJpbmddOiBNYXliZU51bWJlclZhbHVlIHwgXCJleHBlY3RlZFwiIH1cbn1cblxuZXhwb3J0IHR5cGUgQnJhY2tldFZhbHVlQXRJbnN0YW50ID0gTWF5YmVOdW1iZXJWYWx1ZSB8IE51bWJlclZhbHVlIHwgXCJleHBlY3RlZFwiXG5cbmV4cG9ydCBpbnRlcmZhY2UgTGluZWFyQXZlcmFnZVJhdGVTY2FsZVBhcmFtZXRlciBleHRlbmRzIFNjYWxlUGFyYW1ldGVyQmFzZSB7XG4gIGJyYWNrZXRzOiBSYXRlQnJhY2tldFtdXG4gIHJhdGVfdW5pdD86IHN0cmluZ1xuICB0eXBlOiBTY2FsZVR5cGUuTGluZWFyQXZlcmFnZVJhdGVcbn1cblxuZXhwb3J0IGludGVyZmFjZSBNYXJnaW5hbEFtb3VudFNjYWxlUGFyYW1ldGVyIGV4dGVuZHMgU2NhbGVQYXJhbWV0ZXJCYXNlIHtcbiAgYW1vdW50X3VuaXQ/OiBzdHJpbmdcbiAgYnJhY2tldHM6IEFtb3VudEJyYWNrZXRbXVxuICB0eXBlOiBTY2FsZVR5cGUuTWFyZ2luYWxBbW91bnRcbn1cblxuZXhwb3J0IGludGVyZmFjZSBNYXJnaW5hbFJhdGVTY2FsZVBhcmFtZXRlciBleHRlbmRzIFNjYWxlUGFyYW1ldGVyQmFzZSB7XG4gIGJyYWNrZXRzOiBSYXRlQnJhY2tldFtdXG4gIHJhdGVfdW5pdD86IHN0cmluZ1xuICB0eXBlOiBTY2FsZVR5cGUuTWFyZ2luYWxSYXRlXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTWF5YmVCb29sZWFuVmFsdWUgZXh0ZW5kcyBWYWx1ZUJhc2Uge1xuICB2YWx1ZTogYm9vbGVhbiB8IG51bGxcbn1cblxuZXhwb3J0IGludGVyZmFjZSBNYXliZU51bWJlclZhbHVlIGV4dGVuZHMgVmFsdWVCYXNlIHtcbiAgdmFsdWU6IG51bWJlciB8IG51bGxcbn1cblxuZXhwb3J0IGludGVyZmFjZSBNYXliZVN0cmluZ0FycmF5VmFsdWUgZXh0ZW5kcyBWYWx1ZUJhc2Uge1xuICB2YWx1ZTogQXJyYXk8c3RyaW5nIHwgbnVsbD4gfCBudWxsXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTWF5YmVTdHJpbmdCeVN0cmluZ1ZhbHVlIGV4dGVuZHMgVmFsdWVCYXNlIHtcbiAgdmFsdWU6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVsbCB9IHwgbnVsbFxufVxuXG5leHBvcnQgaW50ZXJmYWNlIE5vZGVQYXJhbWV0ZXIgZXh0ZW5kcyBQYXJhbWV0ZXJCYXNlIHtcbiAgLy8gQ2hpbGRyZW4gYXJlIHByZXNlbnQgaW4gZXZlcnkgUGFyYW1ldGVyTm9kZSwgYnV0IGFyZSByZW1vdmVkIGZyb20gYW5jZXN0b3JzLlxuICBjaGlsZHJlbj86IHsgW2lkOiBzdHJpbmddOiBQYXJhbWV0ZXIgfVxuICBjbGFzczogUGFyYW1ldGVyQ2xhc3MuTm9kZVxuICBvcmRlcj86IHN0cmluZ1tdXG4gIHVuaXQ/OiBzdHJpbmdcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOdW1iZXJWYWx1ZSBleHRlbmRzIFZhbHVlQmFzZSB7XG4gIHZhbHVlOiBudW1iZXJcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOdW1iZXJQYXJhbWV0ZXIgZXh0ZW5kcyBWYWx1ZVBhcmFtZXRlckJhc2Uge1xuICB0eXBlOiBWYWx1ZVR5cGUuTnVtYmVyXG4gIHZhbHVlczogeyBbaW5zdGFudDogc3RyaW5nXTogTWF5YmVOdW1iZXJWYWx1ZSB8IFwiZXhwZWN0ZWRcIiB9XG59XG5cbmV4cG9ydCB0eXBlIE9mZmljaWFsSm91cm5hbERhdGVzQnlJbnN0YW50ID0geyBbaW5zdGFudDogc3RyaW5nXTogc3RyaW5nIHwgbnVsbCB9XG5cbmV4cG9ydCB0eXBlIFBhcmFtZXRlciA9IE5vZGVQYXJhbWV0ZXIgfCBTY2FsZVBhcmFtZXRlciB8IFZhbHVlUGFyYW1ldGVyXG5cbmV4cG9ydCBpbnRlcmZhY2UgUGFyYW1ldGVyQmFzZSB7XG4gIGNsYXNzOiBQYXJhbWV0ZXJDbGFzc1xuICBkZXNjcmlwdGlvbj86IHN0cmluZ1xuICBsYWJlbF9lbj86IHN0cmluZ1xuICBkb2N1bWVudGF0aW9uPzogc3RyaW5nXG4gIGRvY3VtZW50YXRpb25fc3RhcnQ/OiBib29sZWFuXG4gIGZpbGVfcGF0aD86IHN0cmluZ1xuICBpZD86IHN0cmluZyAvLyBHZW5lcmF0ZWQgYXR0cmlidXRlIChsYXN0IHBhcnQgb2YgbmFtZSlcbiAgLy8vIE5hbWUgb2YgcGFyYW1ldGVyIHRoYXQgaXMgdXNlZCB0byBhdXRvbWF0aWNhbGx5IHJldmFsdWF0ZSB0aGlzIHBhcmFtZXRlciB3aXRoIGluZmxhdGlvblxuICBpbmZsYXRvcj86IHN0cmluZ1xuICBpbmZsYXRvcl9yZWZlcmVuY2U/OiBSZWZlcmVuY2VzQnlJbnN0YW50XG4gIC8vIFdoZW4gdHJ1ZSB0aGUgaW5zdGFudHMgb2YgdGhpcyBwYXJhbWV0ZXIgYW5kIGl0cyBkZXNjZW5kYW50cyBhcmUgb2Zmc2V0IGJ5IG9uZSB0b1xuICAvLyB0aGUgcHJlY2VkaW5nIHllYXIuIFVzZWQgZm9yIEZyZW5jaCBpbmNvbWUgdGF4ZXMuXG4gIGluY29tZV90YXhfeWVhcj86IGJvb2xlYW5cbiAgbGFzdF92YWx1ZV9zdGlsbF92YWxpZF9vbj86IHN0cmluZ1xuICBuYW1lPzogc3RyaW5nXG4gIG5vdGVzPzogUmVmZXJlbmNlc0J5SW5zdGFudFxuICBvZmZpY2lhbF9qb3VybmFsX2RhdGU/OiBPZmZpY2lhbEpvdXJuYWxEYXRlc0J5SW5zdGFudFxuICBwYXJlbnQ/OiBOb2RlUGFyYW1ldGVyIC8vIEdlbmVyYXRlZCBhdHRyaWJ1dGVcbiAgcmVmZXJlbmNlPzogUmVmZXJlbmNlc0J5SW5zdGFudFxuICByZWZlcnJpbmdfdmFyaWFibGVzPzogc3RyaW5nW11cbiAgLy8gUmVmZXJlbmNlIGFubm91bmNpbmcgdGhhdCByZXZhbG9yaXphdGlvbiBpcyBzeXN0ZW1hdGljXG4gIHJldmFsdWF0aW9uX3JlZmVyZW5jZT86IFJlZmVyZW5jZXNCeUluc3RhbnRcbiAgLy8gVHlwZSBvZiBzeXN0ZW1hdGljIHJldmFsdWF0aW9uXG4gIHJldmFsdWF0aW9uX3R5cGU/OiBSZXZhbHVhdGlvblR5cGVcbiAgc2hvcnRfbGFiZWw/OiBzdHJpbmdcbiAgc2hvcnRfbGFiZWxfZW4/OiBzdHJpbmdcbiAgdGl0bGU/OiBzdHJpbmcgLy8gR2VuZXJhdGVkIGF0dHJpYnV0ZSBjb25zdHJ1Y3RlZCB1c2luZyBzaG9ydF9sYWJlbCBvciBkZXNjcmlwdGlvbiBvciBuYW1lXG4gIHRpdGxlcz86IHN0cmluZ1tdIC8vIEdlbmVyYXRlZCBhdHRyaWJ1dGUgYWdncmVnYXRpbmcgdGhlIHRpdGxlcyBvZiBldmVyeSBhbmNlc3RvcnNcbn1cblxuZXhwb3J0IGVudW0gUGFyYW1ldGVyQ2xhc3Mge1xuICBOb2RlID0gXCJOb2RlXCIsXG4gIFNjYWxlID0gXCJTY2FsZVwiLFxuICBWYWx1ZSA9IFwiVmFsdWVcIixcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQYXJhbWV0ZXJDdXN0b21pemF0aW9uIHtcbiAgLy8vIE5hbWUgb2YgcGFyYW1ldGVyIHRoYXQgaXMgdXNlZCB0byBhdXRvbWF0aWNhbGx5IHJldmFsdWF0ZSB0aGlzIHBhcmFtZXRlciB3aXRoIGluZmxhdGlvblxuICBpbmZsYXRvcj86IHN0cmluZ1xuICBpbmZsYXRvcl9yZWZlcmVuY2U/OiBSZWZlcmVuY2VzQnlJbnN0YW50XG4gIC8vIFJlZmVyZW5jZSBhbm5vdW5jaW5nIHRoYXQgcmV2YWxvcml6YXRpb24gaXMgc3lzdGVtYXRpY1xuICByZXZhbHVhdGlvbl9yZWZlcmVuY2U/OiBSZWZlcmVuY2VzQnlJbnN0YW50XG4gIC8vIFR5cGUgb2Ygc3lzdGVtYXRpYyByZXZhbHVhdGlvblxuICByZXZhbHVhdGlvbl90eXBlPzogUmV2YWx1YXRpb25UeXBlXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGFyYW1ldGVyV2l0aEFuY2VzdG9ycyB7XG4gIHBhcmFtZXRlcjogUGFyYW1ldGVyXG4gIGFuY2VzdG9yczogTm9kZVBhcmFtZXRlcltdXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmF0ZUJyYWNrZXQgZXh0ZW5kcyBCcmFja2V0QmFzZSB7XG4gIGJhc2U/OiB7IFtpbnN0YW50OiBzdHJpbmddOiBOdW1iZXJWYWx1ZSB8IFwiZXhwZWN0ZWRcIiB9XG4gIHJhdGU6IHsgW2luc3RhbnQ6IHN0cmluZ106IE1heWJlTnVtYmVyVmFsdWUgfCBcImV4cGVjdGVkXCIgfVxufVxuXG5leHBvcnQgdHlwZSBSZXZhbHVhdGlvblR5cGUgPSAodHlwZW9mIHJldmFsdWF0aW9uVHlwZXMpW251bWJlcl1cblxuZXhwb3J0IGludGVyZmFjZSBSYXRlQnJhY2tldEF0SW5zdGFudCBleHRlbmRzIEJyYWNrZXRBdEluc3RhbnRCYXNlIHtcbiAgYmFzZT86IE51bWJlclZhbHVlIHwgXCJleHBlY3RlZFwiXG4gIHJhdGU6IE1heWJlTnVtYmVyVmFsdWUgfCBcImV4cGVjdGVkXCJcbn1cblxuZXhwb3J0IHR5cGUgU2NhbGVBdEluc3RhbnQgPSBCcmFja2V0QXRJbnN0YW50W11cblxuZXhwb3J0IHR5cGUgU2NhbGVQYXJhbWV0ZXIgPVxuICB8IExpbmVhckF2ZXJhZ2VSYXRlU2NhbGVQYXJhbWV0ZXJcbiAgfCBNYXJnaW5hbEFtb3VudFNjYWxlUGFyYW1ldGVyXG4gIHwgTWFyZ2luYWxSYXRlU2NhbGVQYXJhbWV0ZXJcbiAgfCBTaW5nbGVBbW91bnRTY2FsZVBhcmFtZXRlclxuXG5leHBvcnQgaW50ZXJmYWNlIFNjYWxlUGFyYW1ldGVyQmFzZSBleHRlbmRzIFBhcmFtZXRlckJhc2Uge1xuICBicmFja2V0czogQW1vdW50QnJhY2tldFtdIHwgUmF0ZUJyYWNrZXRbXVxuICBjbGFzczogUGFyYW1ldGVyQ2xhc3MuU2NhbGVcbiAgaXBwX2Nzdl9pZD86IHN0cmluZyB8IHsgW2JyYWNrZXROYW1lOiBzdHJpbmddOiBzdHJpbmcgfVxuICB0aHJlc2hvbGRfdW5pdD86IHN0cmluZ1xuICB0eXBlOiBTY2FsZVR5cGVcbn1cblxuZXhwb3J0IGVudW0gU2NhbGVUeXBlIHtcbiAgTGluZWFyQXZlcmFnZVJhdGUgPSBcImxpbmVhcl9hdmVyYWdlX3JhdGVcIixcbiAgTWFyZ2luYWxBbW91bnQgPSBcIm1hcmdpbmFsX2Ftb3VudFwiLFxuICBNYXJnaW5hbFJhdGUgPSBcIm1hcmdpbmFsX3JhdGVcIixcbiAgU2luZ2xlQW1vdW50ID0gXCJzaW5nbGVfYW1vdW50XCIsXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2luZ2xlQW1vdW50U2NhbGVQYXJhbWV0ZXIgZXh0ZW5kcyBTY2FsZVBhcmFtZXRlckJhc2Uge1xuICBhbW91bnRfdW5pdD86IHN0cmluZ1xuICBicmFja2V0czogQW1vdW50QnJhY2tldFtdXG4gIHR5cGU6IFNjYWxlVHlwZS5TaW5nbGVBbW91bnRcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTdHJpbmdBcnJheVBhcmFtZXRlciBleHRlbmRzIFZhbHVlUGFyYW1ldGVyQmFzZSB7XG4gIHR5cGU6IFZhbHVlVHlwZS5TdHJpbmdBcnJheVxuICB2YWx1ZXM6IHsgW2luc3RhbnQ6IHN0cmluZ106IE1heWJlU3RyaW5nQXJyYXlWYWx1ZSB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3RyaW5nQnlTdHJpbmdQYXJhbWV0ZXIgZXh0ZW5kcyBWYWx1ZVBhcmFtZXRlckJhc2Uge1xuICB0eXBlOiBWYWx1ZVR5cGUuU3RyaW5nQnlTdHJpbmdcbiAgdmFsdWVzOiB7IFtpbnN0YW50OiBzdHJpbmddOiBNYXliZVN0cmluZ0J5U3RyaW5nVmFsdWUgfVxufVxuXG5leHBvcnQgdHlwZSBWYWx1ZUF0SW5zdGFudCA9XG4gIHwgQm9vbGVhblZhbHVlXG4gIHwgTWF5YmVCb29sZWFuVmFsdWVcbiAgfCBNYXliZU51bWJlclZhbHVlXG4gIHwgTWF5YmVTdHJpbmdBcnJheVZhbHVlXG4gIHwgTWF5YmVTdHJpbmdCeVN0cmluZ1ZhbHVlXG4gIHwgTnVtYmVyVmFsdWVcbiAgfCBcImV4cGVjdGVkXCJcblxuZXhwb3J0IGludGVyZmFjZSBWYWx1ZUJhc2Uge1xuICByZWZlcmVuY2U/OiBSZWZlcmVuY2VbXVxuICB1bml0Pzogc3RyaW5nIC8vIE5vdGU6IEEgYnJhY2tldCB2YWx1ZSBoYXMgbm8gdW5pdC5cbiAgdmFsdWU/OiB1bmtub3duXG59XG5cbmV4cG9ydCB0eXBlIFZhbHVlUGFyYW1ldGVyID1cbiAgfCBCb29sZWFuUGFyYW1ldGVyXG4gIHwgTnVtYmVyUGFyYW1ldGVyXG4gIHwgU3RyaW5nQXJyYXlQYXJhbWV0ZXJcbiAgfCBTdHJpbmdCeVN0cmluZ1BhcmFtZXRlclxuXG5leHBvcnQgaW50ZXJmYWNlIFZhbHVlUGFyYW1ldGVyQmFzZSBleHRlbmRzIFBhcmFtZXRlckJhc2Uge1xuICBjbGFzczogUGFyYW1ldGVyQ2xhc3MuVmFsdWVcbiAgaXBwX2Nzdl9pZD86IHN0cmluZ1xuICB0eXBlOiBWYWx1ZVR5cGVcbiAgdW5pdD86IHN0cmluZ1xuICB2YWx1ZXM6IHsgW2luc3RhbnQ6IHN0cmluZ106IFZhbHVlQXRJbnN0YW50IH1cbn1cblxuZXhwb3J0IGVudW0gVmFsdWVUeXBlIHtcbiAgQm9vbGVhbiA9IFwiYm9vbGVhblwiLFxuICBOdW1iZXIgPSBcIm51bWJlclwiLFxuICBTdHJpbmdBcnJheSA9IFwic3RyaW5nX2FycmF5XCIsXG4gIFN0cmluZ0J5U3RyaW5nID0gXCJzdHJpbmdfYnlfc3RyaW5nXCIsXG59XG5cbmV4cG9ydCBjb25zdCByZXZhbHVhdGlvblR5cGVzID0gW1xuICBcImluY29tZV90YXhfdXN1YWxfcmV2YWx1YXRpb25cIixcbiAgXCJsZWdhbF9yZXZhbHVhdGlvblwiLFxuICBcImxlZ2FsX3JldmFsdWF0aW9uX2ZvbGxvd2luZ19pbmNvbWVfdGF4X3VzdWFsX3JldmFsdWF0aW9uXCIsXG5dIGFzIGNvbnN0XG5cbmV4cG9ydCBmdW5jdGlvbiBhY2Nlc3NQYXJhbWV0ZXJGcm9tSWRzKFxuICBwYXJhbWV0ZXI6IFBhcmFtZXRlcixcbiAgaWRzOiBzdHJpbmdbXSxcbik6IFtQYXJhbWV0ZXIsIHVua25vd25dIHtcbiAgZm9yIChjb25zdCBpZCBvZiBpZHMpIHtcbiAgICBpZiAocGFyYW1ldGVyLmNsYXNzICE9PSBQYXJhbWV0ZXJDbGFzcy5Ob2RlKSB7XG4gICAgICByZXR1cm4gW1xuICAgICAgICBwYXJhbWV0ZXIsXG4gICAgICAgIGBDYW4ndCBhY2Nlc3MgdG8gY2hpbGQgXCIke2lkfVwiIGluIHBhcmFtZXRlciAke3BhcmFtZXRlci5uYW1lfSwgYmVjYXVzZSBpdCBpcyBub3QgYSBub2RlYCxcbiAgICAgIF1cbiAgICB9XG4gICAgY29uc3QgY2hpbGQgPSBwYXJhbWV0ZXIuY2hpbGRyZW4/LltpZF1cbiAgICBpZiAoY2hpbGQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIFtcbiAgICAgICAgcGFyYW1ldGVyLFxuICAgICAgICBgUGFyYW1ldGVyICR7cGFyYW1ldGVyLm5hbWV9IGhhcyBubyBjaGlsZCBuYW1lZCBcIiR7aWR9XCJgLFxuICAgICAgXVxuICAgIH1cbiAgICBwYXJhbWV0ZXIgPSBjaGlsZFxuICB9XG4gIHJldHVybiBbcGFyYW1ldGVyLCBudWxsXVxufVxuXG4vLy8gQ29udmVydCBhIGRpY3Rpb25hcnkgb2YgYnJhY2tldHMgYnkgaW5zdGFudCB0byBhIGxpc3Qgb2YgYnJhY2tldHNcbi8vLyBjb250YWluaW5nIGFtb3VudHMsIGJhc2VzLCByYXRlcyAmIHRocmVzaG9sZHMgYnkgaW5zdGFudC5cbmV4cG9ydCBmdW5jdGlvbiBicmFja2V0c0Zyb21TY2FsZUJ5SW5zdGFudChzY2FsZUJ5SW5zdGFudDoge1xuICBbaW5zdGFudDogc3RyaW5nXTogU2NhbGVBdEluc3RhbnRcbn0pOiBBbW91bnRCcmFja2V0W10gfCBSYXRlQnJhY2tldFtdIHtcbiAgY29uc3QgYnJhY2tldHM6IFBhcnRpYWw8QnJhY2tldD5bXSA9IFtdXG4gIGZvciAoY29uc3Qga2V5IG9mIFtcImFtb3VudFwiLCBcImJhc2VcIiwgXCJyYXRlXCIsIFwidGhyZXNob2xkXCJdKSB7XG4gICAgbGV0IGxhdGVzdFZhbHVlQnlCcmFja2V0SW5kZXg6IHtcbiAgICAgIFtpbmRleDogc3RyaW5nXTogTWF5YmVOdW1iZXJWYWx1ZSB8IFwiZXhwZWN0ZWRcIiB8IHVuZGVmaW5lZFxuICAgIH0gPSB7fVxuICAgIGZvciAoY29uc3QgW2luc3RhbnQsIHNjYWxlQXRJbnN0YW50XSBvZiBPYmplY3QuZW50cmllcyhzY2FsZUJ5SW5zdGFudCkuc29ydChcbiAgICAgIChbaW5zdGFudDFdLCBbaW5zdGFudDJdKSA9PiBpbnN0YW50MS5sb2NhbGVDb21wYXJlKGluc3RhbnQyKSxcbiAgICApKSB7XG4gICAgICBmb3IgKGNvbnN0IFticmFja2V0SW5kZXgsIGJyYWNrZXRBdEluc3RhbnRdIG9mIHNjYWxlQXRJbnN0YW50LmVudHJpZXMoKSkge1xuICAgICAgICBpZiAoYnJhY2tldHMubGVuZ3RoIDw9IGJyYWNrZXRJbmRleCkge1xuICAgICAgICAgIGJyYWNrZXRzLnB1c2goe30pXG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdmFsdWUgPSBicmFja2V0QXRJbnN0YW50W2tleSBhcyBrZXlvZiBCcmFja2V0QXRJbnN0YW50XVxuICAgICAgICBpZiAodmFsdWUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIGNvbnN0IGJyYWNrZXQgPSBicmFja2V0c1ticmFja2V0SW5kZXhdXG4gICAgICAgICAgY29uc3QgbGF0ZXN0VmFsdWUgPSBsYXRlc3RWYWx1ZUJ5QnJhY2tldEluZGV4W2JyYWNrZXRJbmRleF1cbiAgICAgICAgICBjb25zdCB2YWx1ZUJ5SW5zdGFudCA9IChicmFja2V0W2tleSBhcyBrZXlvZiBCcmFja2V0XSA/Pz0ge30pXG4gICAgICAgICAgaWYgKHZhbHVlID09PSBcImV4cGVjdGVkXCIpIHtcbiAgICAgICAgICAgIGlmICh2YWx1ZSAhPT0gbGF0ZXN0VmFsdWUpIHtcbiAgICAgICAgICAgICAgbGF0ZXN0VmFsdWVCeUJyYWNrZXRJbmRleFticmFja2V0SW5kZXhdID0gdmFsdWVCeUluc3RhbnRbXG4gICAgICAgICAgICAgICAgaW5zdGFudFxuICAgICAgICAgICAgICBdID0gdmFsdWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKFxuICAgICAgICAgICAgbGF0ZXN0VmFsdWUgPT09IHVuZGVmaW5lZCB8fFxuICAgICAgICAgICAgbGF0ZXN0VmFsdWUgPT09IFwiZXhwZWN0ZWRcIiB8fFxuICAgICAgICAgICAgdmFsdWUudmFsdWUgIT09IGxhdGVzdFZhbHVlLnZhbHVlXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICBsYXRlc3RWYWx1ZUJ5QnJhY2tldEluZGV4W2JyYWNrZXRJbmRleF0gPSB2YWx1ZUJ5SW5zdGFudFtpbnN0YW50XSA9XG4gICAgICAgICAgICAgIHZhbHVlXG4gICAgICAgICAgfSBlbHNlIGlmICh2YWx1ZS5yZWZlcmVuY2UgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGV0IGxhdGVzdFJlZmVyZW5jZXMgPSBsYXRlc3RWYWx1ZS5yZWZlcmVuY2VcbiAgICAgICAgICAgIGlmIChsYXRlc3RSZWZlcmVuY2VzID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgbGF0ZXN0UmVmZXJlbmNlcyA9IGxhdGVzdFZhbHVlLnJlZmVyZW5jZSA9IFtdXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmb3IgKGNvbnN0IHJlZmVyZW5jZSBvZiB2YWx1ZS5yZWZlcmVuY2UpIHtcbiAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICFsYXRlc3RSZWZlcmVuY2VzLnNvbWUoXG4gICAgICAgICAgICAgICAgICAobGF0ZXN0UmVmZXJlbmNlKSA9PlxuICAgICAgICAgICAgICAgICAgICBsYXRlc3RSZWZlcmVuY2UuaHJlZiA9PT0gcmVmZXJlbmNlLmhyZWYgJiZcbiAgICAgICAgICAgICAgICAgICAgbGF0ZXN0UmVmZXJlbmNlLnRpdGxlID09PSByZWZlcmVuY2UudGl0bGUsXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICBsYXRlc3RSZWZlcmVuY2VzLnB1c2gocmVmZXJlbmNlKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGxhdGVzdFZhbHVlQnlCcmFja2V0SW5kZXhMZW5ndGggPSBPYmplY3Qua2V5cyhcbiAgICAgICAgbGF0ZXN0VmFsdWVCeUJyYWNrZXRJbmRleCxcbiAgICAgICkubGVuZ3RoXG4gICAgICBmb3IgKFxuICAgICAgICBsZXQgYnJhY2tldEluZGV4ID0gc2NhbGVBdEluc3RhbnQubGVuZ3RoO1xuICAgICAgICBicmFja2V0SW5kZXggPCBsYXRlc3RWYWx1ZUJ5QnJhY2tldEluZGV4TGVuZ3RoO1xuICAgICAgICBicmFja2V0SW5kZXgrK1xuICAgICAgKSB7XG4gICAgICAgIGNvbnN0IGJyYWNrZXQgPSBicmFja2V0c1ticmFja2V0SW5kZXhdXG4gICAgICAgIGNvbnN0IHZhbHVlQnlJbnN0YW50ID0gKGJyYWNrZXRba2V5IGFzIGtleW9mIEJyYWNrZXRdID8/PSB7fSlcbiAgICAgICAgdmFsdWVCeUluc3RhbnRbaW5zdGFudF0gPSB7IHZhbHVlOiBudWxsIH1cbiAgICAgICAgZGVsZXRlIGxhdGVzdFZhbHVlQnlCcmFja2V0SW5kZXhbYnJhY2tldEluZGV4XVxuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gYnJhY2tldHMgYXMgQW1vdW50QnJhY2tldFtdIHwgUmF0ZUJyYWNrZXRbXVxufVxuXG5leHBvcnQgZnVuY3Rpb24gaW1wcm92ZVBhcmFtZXRlcihcbiAgcGFyYW1ldGVyOiBQYXJhbWV0ZXIsXG4gIHBhcmVudDogTm9kZVBhcmFtZXRlciB8IHVuZGVmaW5lZCB8IG51bGwgPSB1bmRlZmluZWQsXG4gIGlkczogc3RyaW5nW10gPSBbXSxcbik6IHZvaWQge1xuICBjb25zdCBpZCA9IGlkc1tpZHMubGVuZ3RoIC0gMV1cbiAgaWYgKGlkICE9PSB1bmRlZmluZWQpIHtcbiAgICBwYXJhbWV0ZXIuaWQgPSBpZFxuICB9XG4gIGlmIChwYXJhbWV0ZXIubmFtZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcGFyYW1ldGVyLm5hbWUgPSBpZHMuam9pbihcIi5cIilcbiAgfVxuICBpZiAocGFyZW50ICE9IG51bGwpIHtcbiAgICBwYXJhbWV0ZXIucGFyZW50ID0gcGFyZW50XG4gIH1cbiAgY29uc3QgdGl0bGUgPVxuICAgIHBhcmFtZXRlci5zaG9ydF9sYWJlbCA/P1xuICAgIHBhcmFtZXRlci5kZXNjcmlwdGlvbiA/P1xuICAgIGlkPy5yZXBsYWNlKC9fL2csIFwiIFwiKS5yZXBsYWNlKC9eXFx3LywgKGMpID0+IGMudG9VcHBlckNhc2UoKSlcbiAgaWYgKHRpdGxlICE9PSB1bmRlZmluZWQpIHtcbiAgICBwYXJhbWV0ZXIudGl0bGUgPSB0aXRsZVxuICB9XG4gIHBhcmFtZXRlci50aXRsZXMgPSAoXG4gICAgdGl0bGUgPT09IHVuZGVmaW5lZFxuICAgICAgPyAocGFyZW50Py50aXRsZXMgPz8gW10pXG4gICAgICA6IFsuLi4ocGFyZW50Py50aXRsZXMgPz8gW10pLCB0aXRsZV1cbiAgKS5maWx0ZXIoQm9vbGVhbilcblxuICBzd2l0Y2ggKHBhcmFtZXRlci5jbGFzcykge1xuICAgIGNhc2UgUGFyYW1ldGVyQ2xhc3MuTm9kZTpcbiAgICAgIGlmIChwYXJhbWV0ZXIuY2hpbGRyZW4gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBmb3IgKGNvbnN0IFtjaGlsZElkLCBjaGlsZF0gb2YgT2JqZWN0LmVudHJpZXMocGFyYW1ldGVyLmNoaWxkcmVuKSkge1xuICAgICAgICAgIGltcHJvdmVQYXJhbWV0ZXIoY2hpbGQsIHBhcmFtZXRlciwgWy4uLmlkcywgY2hpbGRJZF0pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGJyZWFrXG4gICAgZGVmYXVsdDpcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNBbW91bnRTY2FsZVBhcmFtZXRlcihwYXJhbWV0ZXI6IFNjYWxlUGFyYW1ldGVyKSB7XG4gIHJldHVybiBbU2NhbGVUeXBlLk1hcmdpbmFsQW1vdW50LCBTY2FsZVR5cGUuU2luZ2xlQW1vdW50XS5pbmNsdWRlcyhcbiAgICBwYXJhbWV0ZXIudHlwZSxcbiAgKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNSYXRlU2NhbGVQYXJhbWV0ZXIocGFyYW1ldGVyOiBTY2FsZVBhcmFtZXRlcikge1xuICByZXR1cm4gIVtTY2FsZVR5cGUuTWFyZ2luYWxBbW91bnQsIFNjYWxlVHlwZS5TaW5nbGVBbW91bnRdLmluY2x1ZGVzKFxuICAgIHBhcmFtZXRlci50eXBlLFxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1ZlY3RvcmlhbE5vZGVQYXJhbWV0ZXIobm9kZTogTm9kZVBhcmFtZXRlcik6IGJvb2xlYW4ge1xuICBpZiAobm9kZS5jaGlsZHJlbiA9PT0gdW5kZWZpbmVkIHx8IE9iamVjdC5rZXlzKG5vZGUuY2hpbGRyZW4pLmxlbmd0aCA8PSAxKSB7XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cbiAgY29uc3QgY2hpbGRyZW4gPSBPYmplY3QudmFsdWVzKG5vZGUuY2hpbGRyZW4pIGFzIFZhbHVlUGFyYW1ldGVyW11cbiAgaWYgKGNoaWxkcmVuLnNvbWUoKGNoaWxkKSA9PiBjaGlsZC5jbGFzcyAhPT0gUGFyYW1ldGVyQ2xhc3MuVmFsdWUpKSB7XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cbiAgY29uc3QgY29tbW9uVHlwZSA9IGNoaWxkcmVuWzBdLnR5cGVcbiAgaWYgKGNoaWxkcmVuLnNvbWUoKGNoaWxkKSA9PiBjaGlsZC50eXBlICE9PSBjb21tb25UeXBlKSkge1xuICAgIHJldHVybiBmYWxzZVxuICB9XG4gIGNvbnN0IGNvbW1vbkZpbGVQYXRoID0gY2hpbGRyZW5bMF0uZmlsZV9wYXRoXG4gIGlmIChjaGlsZHJlbi5zb21lKChjaGlsZCkgPT4gY2hpbGQuZmlsZV9wYXRoICE9PSBjb21tb25GaWxlUGF0aCkpIHtcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuICByZXR1cm4gdHJ1ZVxufVxuXG5leHBvcnQgZnVuY3Rpb24qIGl0ZXJQYXJhbWV0ZXJBbmNlc3RvcnMoXG4gIHBhcmFtZXRlcj86IFBhcmFtZXRlciB8IHVuZGVmaW5lZCB8IG51bGwsXG4pOiBHZW5lcmF0b3I8UGFyYW1ldGVyLCB2b2lkLCB1bmtub3duPiB7XG4gIGlmIChwYXJhbWV0ZXIgPT0gbnVsbCB8fCAhcGFyYW1ldGVyLmlkKSB7XG4gICAgcmV0dXJuXG4gIH1cbiAgeWllbGQqIGl0ZXJQYXJhbWV0ZXJBbmNlc3RvcnMocGFyYW1ldGVyLnBhcmVudClcbiAgeWllbGQgcGFyYW1ldGVyXG59XG5cbmZ1bmN0aW9uIG1lcmdlRHVwbGljYXRlUGFyYW1ldGVycyhcbiAgZXhpc3RpbmdQYXJhbWV0ZXI6IFBhcmFtZXRlcixcbiAgcGFyYW1ldGVyOiBQYXJhbWV0ZXIsXG4pOiB2b2lkIHtcbiAgaWYgKGV4aXN0aW5nUGFyYW1ldGVyLmNsYXNzID09PSBQYXJhbWV0ZXJDbGFzcy5Ob2RlKSB7XG4gICAgY29uc3QgZXhpc3RpbmdDaGlkcmVuID0gZXhpc3RpbmdQYXJhbWV0ZXIuY2hpbGRyZW4gYXMge1xuICAgICAgW2lkOiBzdHJpbmddOiBQYXJhbWV0ZXJcbiAgICB9XG4gICAgZm9yIChjb25zdCBjaGlsZCBvZiBPYmplY3QudmFsdWVzKFxuICAgICAgKHBhcmFtZXRlciBhcyBOb2RlUGFyYW1ldGVyKS5jaGlsZHJlbiBhcyB7XG4gICAgICAgIFtpZDogc3RyaW5nXTogUGFyYW1ldGVyXG4gICAgICB9LFxuICAgICkpIHtcbiAgICAgIGNvbnN0IGV4aXN0aW5nQ2hpbGQgPSBleGlzdGluZ0NoaWRyZW5bY2hpbGQuaWQgPz8gXCJcIl1cbiAgICAgIGlmIChleGlzdGluZ0NoaWxkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgZXhpc3RpbmdDaGlkcmVuW2NoaWxkLmlkID8/IFwiXCJdID0gY2hpbGRcbiAgICAgICAgY2hpbGQucGFyZW50ID0gZXhpc3RpbmdQYXJhbWV0ZXJcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG1lcmdlRHVwbGljYXRlUGFyYW1ldGVycyhleGlzdGluZ0NoaWxkLCBjaGlsZClcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlUGFyYW1ldGVycyhwYXJhbWV0ZXJzOiBQYXJhbWV0ZXJbXSk6IHtcbiAgW2lkOiBzdHJpbmddOiBQYXJhbWV0ZXJcbn0ge1xuICBjb25zdCByb290UGFyYW1ldGVyQnlJZDogeyBbaWQ6IHN0cmluZ106IFBhcmFtZXRlciB9ID0ge31cbiAgZm9yIChjb25zdCBwYXJhbWV0ZXIgb2YgcGFyYW1ldGVycykge1xuICAgIC8vIENyZWF0ZSBhIGNvcHkgb2YgdGhlIHBhcmFtZXRlcnMgdHJlZSwgY29udGFpbmluZyBvbmx5IHRoaXMgcGFyYW1ldGVyLlxuICAgIGxldCByb290UGFyYW1ldGVyID0geyAuLi5wYXJhbWV0ZXIgfVxuICAgIGZvciAoXG4gICAgICBsZXQgYW5jZXN0b3I6IE5vZGVQYXJhbWV0ZXIgfCB1bmRlZmluZWQgPSByb290UGFyYW1ldGVyLnBhcmVudDtcbiAgICAgIGFuY2VzdG9yICE9PSB1bmRlZmluZWQgJiYgYW5jZXN0b3IuaWQ7XG4gICAgICByb290UGFyYW1ldGVyID0gYW5jZXN0b3IhLCBhbmNlc3RvciA9IHJvb3RQYXJhbWV0ZXIucGFyZW50XG4gICAgKSB7XG4gICAgICBhbmNlc3RvciA9IHtcbiAgICAgICAgLi4uYW5jZXN0b3IsXG4gICAgICAgIGNoaWxkcmVuOiB7IFtyb290UGFyYW1ldGVyLmlkIV06IHJvb3RQYXJhbWV0ZXIgfSxcbiAgICAgIH1cbiAgICAgIHJvb3RQYXJhbWV0ZXIucGFyZW50ID0gYW5jZXN0b3JcbiAgICB9XG5cbiAgICAvLyBNZXJnZSB0aGlzIHNpbXBsaWZpZWQgcGFyYW1ldGVycyB0cmVlIHdpdGggdGhlIG90aGVycy5cbiAgICBjb25zdCBleGlzdGluZ1Jvb3RQYXJhbWV0ZXIgPSByb290UGFyYW1ldGVyQnlJZFtyb290UGFyYW1ldGVyLmlkID8/IFwiXCJdXG4gICAgaWYgKGV4aXN0aW5nUm9vdFBhcmFtZXRlciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByb290UGFyYW1ldGVyQnlJZFtyb290UGFyYW1ldGVyLmlkID8/IFwiXCJdID0gcm9vdFBhcmFtZXRlclxuICAgIH0gZWxzZSB7XG4gICAgICBtZXJnZUR1cGxpY2F0ZVBhcmFtZXRlcnMoZXhpc3RpbmdSb290UGFyYW1ldGVyLCByb290UGFyYW1ldGVyKVxuICAgIH1cbiAgfVxuICByZXR1cm4gcm9vdFBhcmFtZXRlckJ5SWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlUmVmZXJlbmNlcyhcbiAgcmVmZXJlbmNlczE6IFJlZmVyZW5jZVtdIHwgdW5kZWZpbmVkIHwgbnVsbCxcbiAgcmVmZXJlbmNlczI6IFJlZmVyZW5jZVtdIHwgdW5kZWZpbmVkIHwgbnVsbCxcbik6IFJlZmVyZW5jZVtdIHwgdW5kZWZpbmVkIHtcbiAgaWYgKHJlZmVyZW5jZXMxID09IG51bGwpIHtcbiAgICByZXR1cm4gcmVmZXJlbmNlczIgPz8gdW5kZWZpbmVkXG4gIH1cbiAgaWYgKHJlZmVyZW5jZXMyID09IG51bGwpIHtcbiAgICByZXR1cm4gcmVmZXJlbmNlczEgPz8gdW5kZWZpbmVkXG4gIH1cbiAgY29uc3QgcmVmZXJlbmNlcyA9IFsuLi5yZWZlcmVuY2VzMV1cbiAgZm9yIChjb25zdCByZWZlcmVuY2UyIG9mIHJlZmVyZW5jZXMyKSB7XG4gICAgbGV0IGZvdW5kID0gZmFsc2VcbiAgICBpZiAocmVmZXJlbmNlMi5ocmVmICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IGluZGV4ID0gcmVmZXJlbmNlcy5maW5kSW5kZXgoXG4gICAgICAgIChyZWZlcmVuY2UpID0+IHJlZmVyZW5jZS5ocmVmID09PSByZWZlcmVuY2UyLmhyZWYsXG4gICAgICApXG4gICAgICBpZiAoaW5kZXggPj0gMCkge1xuICAgICAgICBmb3VuZCA9IHRydWVcbiAgICAgICAgbGV0IHJlZmVyZW5jZSA9IHJlZmVyZW5jZXNbaW5kZXhdXG4gICAgICAgIGxldCBjaGFuZ2VkID0gZmFsc2VcbiAgICAgICAgaWYgKHJlZmVyZW5jZS5ub3RlID09PSB1bmRlZmluZWQgJiYgcmVmZXJlbmNlMi5ub3RlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBpZiAoIWNoYW5nZWQpIHtcbiAgICAgICAgICAgIGNoYW5nZWQgPSB0cnVlXG4gICAgICAgICAgICByZWZlcmVuY2UgPSByZWZlcmVuY2VzW2luZGV4XSA9IHsgLi4ucmVmZXJlbmNlIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgcmVmZXJlbmNlLm5vdGUgPSByZWZlcmVuY2UyLm5vdGVcbiAgICAgICAgfVxuICAgICAgICBpZiAocmVmZXJlbmNlLnRpdGxlID09PSB1bmRlZmluZWQgJiYgcmVmZXJlbmNlMi50aXRsZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgaWYgKCFjaGFuZ2VkKSB7XG4gICAgICAgICAgICBjaGFuZ2VkID0gdHJ1ZVxuICAgICAgICAgICAgcmVmZXJlbmNlID0gcmVmZXJlbmNlc1tpbmRleF0gPSB7IC4uLnJlZmVyZW5jZSB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHJlZmVyZW5jZS50aXRsZSA9IHJlZmVyZW5jZTIudGl0bGVcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAocmVmZXJlbmNlMi50aXRsZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjb25zdCBpbmRleCA9IHJlZmVyZW5jZXMuZmluZEluZGV4KFxuICAgICAgICAocmVmZXJlbmNlKSA9PiByZWZlcmVuY2UudGl0bGUgPT09IHJlZmVyZW5jZTIudGl0bGUsXG4gICAgICApXG4gICAgICBpZiAoaW5kZXggPj0gMCkge1xuICAgICAgICBmb3VuZCA9IHRydWVcbiAgICAgICAgbGV0IHJlZmVyZW5jZSA9IHJlZmVyZW5jZXNbaW5kZXhdXG4gICAgICAgIGlmIChyZWZlcmVuY2Uubm90ZSA9PT0gdW5kZWZpbmVkICYmIHJlZmVyZW5jZTIubm90ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcmVmZXJlbmNlID0gcmVmZXJlbmNlc1tpbmRleF0gPSB7IC4uLnJlZmVyZW5jZSB9XG4gICAgICAgICAgcmVmZXJlbmNlLm5vdGUgPSByZWZlcmVuY2UyLm5vdGVcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBhc3NlcnQubm90U3RyaWN0RXF1YWwocmVmZXJlbmNlMi5ub3RlLCB1bmRlZmluZWQpXG4gICAgICBjb25zdCBpbmRleCA9IHJlZmVyZW5jZXMuZmluZEluZGV4KFxuICAgICAgICAocmVmZXJlbmNlKSA9PiByZWZlcmVuY2Uubm90ZSA9PT0gcmVmZXJlbmNlMi5ub3RlLFxuICAgICAgKVxuICAgICAgaWYgKGluZGV4ID49IDApIHtcbiAgICAgICAgZm91bmQgPSB0cnVlXG4gICAgICB9XG4gICAgfVxuICAgIGlmICghZm91bmQpIHtcbiAgICAgIHJlZmVyZW5jZXMucHVzaChyZWZlcmVuY2UyKVxuICAgIH1cbiAgfVxuICByZXR1cm4gcmVmZXJlbmNlc1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbmV3UGFyYW1ldGVyUmVwb3NpdG9yeVVybChcbiAgbWV0YWRhdGE6IE1ldGFkYXRhLFxuICBwYXJhbWV0ZXI6IFBhcmFtZXRlcixcbik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGlmIChwYXJhbWV0ZXIuZmlsZV9wYXRoID09PSB1bmRlZmluZWQpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkXG4gIH1cbiAgY29uc3QgcGFja2FnZU5hbWUgPSBwYXJhbWV0ZXIuZmlsZV9wYXRoLnNwbGl0KFwiL1wiKVswXVxuICBmb3IgKGNvbnN0IHBhY2thZ2VNZXRhZGF0YSBvZiBtZXRhZGF0YS5wYWNrYWdlcykge1xuICAgIGlmIChwYWNrYWdlTWV0YWRhdGEubmFtZSA9PT0gcGFja2FnZU5hbWUpIHtcbiAgICAgIHJldHVybiBgJHtwYWNrYWdlTWV0YWRhdGEucmVwb3NpdG9yeV91cmx9L2Jsb2IvJHtwYWNrYWdlTWV0YWRhdGEudmVyc2lvbn0vJHtwYXJhbWV0ZXIuZmlsZV9wYXRofWBcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gcGFyYW1ldGVyTGFzdFJldmlld09yQ2hhbmdlKFxuICBwYXJhbWV0ZXI6IFBhcmFtZXRlcixcbik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIHN3aXRjaCAocGFyYW1ldGVyLmNsYXNzKSB7XG4gICAgY2FzZSBQYXJhbWV0ZXJDbGFzcy5Ob2RlOlxuICAgICAgcmV0dXJuIHBhcmFtZXRlci5sYXN0X3ZhbHVlX3N0aWxsX3ZhbGlkX29uXG5cbiAgICBjYXNlIFBhcmFtZXRlckNsYXNzLlNjYWxlOiB7XG4gICAgICBjb25zdCBsYXRlc3RJbnN0YW50ID0gW1xuICAgICAgICBwYXJhbWV0ZXIubGFzdF92YWx1ZV9zdGlsbF92YWxpZF9vbixcbiAgICAgICAgLi4uT2JqZWN0LmtleXMoc2NhbGVCeUluc3RhbnRGcm9tQnJhY2tldHMocGFyYW1ldGVyLmJyYWNrZXRzKSksXG4gICAgICBdXG4gICAgICAgIC5maWx0ZXIoKGluc3RhbnQpID0+IGluc3RhbnQgIT09IHVuZGVmaW5lZClcbiAgICAgICAgLnNvcnQoKGluc3RhbnQxLCBpbnN0YW50MikgPT4gaW5zdGFudDIhLmxvY2FsZUNvbXBhcmUoaW5zdGFudDEhKSlbMF1cbiAgICAgIHJldHVybiBsYXRlc3RJbnN0YW50XG4gICAgfVxuXG4gICAgY2FzZSBQYXJhbWV0ZXJDbGFzcy5WYWx1ZToge1xuICAgICAgY29uc3QgbGF0ZXN0SW5zdGFudCA9IFtcbiAgICAgICAgcGFyYW1ldGVyLmxhc3RfdmFsdWVfc3RpbGxfdmFsaWRfb24sXG4gICAgICAgIC4uLk9iamVjdC5rZXlzKHBhcmFtZXRlci52YWx1ZXMpLFxuICAgICAgXVxuICAgICAgICAuZmlsdGVyKChpbnN0YW50KSA9PiBpbnN0YW50ICE9PSB1bmRlZmluZWQpXG4gICAgICAgIC5zb3J0KChpbnN0YW50MSwgaW5zdGFudDIpID0+IGluc3RhbnQyIS5sb2NhbGVDb21wYXJlKGluc3RhbnQxISkpWzBdXG4gICAgICByZXR1cm4gbGF0ZXN0SW5zdGFudFxuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcGFyYW1ldGVyV2l0aG91dENoaWxkcmVuKHBhcmFtZXRlcjogUGFyYW1ldGVyKTogUGFyYW1ldGVyIHtcbiAgaWYgKHBhcmFtZXRlci5jbGFzcyA9PT0gUGFyYW1ldGVyQ2xhc3MuTm9kZSkge1xuICAgIHBhcmFtZXRlciA9IHsgLi4ucGFyYW1ldGVyIH1cbiAgICBkZWxldGUgcGFyYW1ldGVyLmNoaWxkcmVuXG4gIH1cbiAgcmV0dXJuIHBhcmFtZXRlclxufVxuXG4vLy8gQXBwbHkgY2hhbmdlcyBvZiBhIHJlZm9ybSB0byAocm9vdCkgcGFyYW1ldGVyLlxuZXhwb3J0IGZ1bmN0aW9uIHBhdGNoUGFyYW1ldGVyPFBhcmFtZXRlclR5cGUgZXh0ZW5kcyBQYXJhbWV0ZXI+KFxuICBwYXJhbWV0ZXI6IFBhcmFtZXRlclR5cGUsXG4gIHBhdGNoOiB7IFtrZXk6IHN0cmluZ106IHVua25vd24gfSxcbik6IFBhcmFtZXRlclR5cGUge1xuICBpZiAoT2JqZWN0LmtleXMocGF0Y2gpLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBwYXJhbWV0ZXJcbiAgfVxuICBjb25zdCBwYXRjaGVkUGFyYW1ldGVyID0geyAuLi5wYXJhbWV0ZXIgfVxuICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwYXRjaCkpIHtcbiAgICBpZiAodmFsdWUgPT09IG51bGwpIHtcbiAgICAgIGRlbGV0ZSAocGF0Y2hlZFBhcmFtZXRlciBhcyB1bmtub3duIGFzIHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9KVtrZXldXG4gICAgfSBlbHNlIGlmIChcbiAgICAgIChwYXRjaGVkUGFyYW1ldGVyIGFzIHVua25vd24gYXMgeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0pW2tleV0gPT09XG4gICAgICB1bmRlZmluZWRcbiAgICApIHtcbiAgICAgIDsocGF0Y2hlZFBhcmFtZXRlciBhcyB1bmtub3duIGFzIHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9KVtrZXldID0gdmFsdWVcbiAgICB9IGVsc2UgaWYgKGtleSA9PT0gXCJjaGlsZHJlblwiKSB7XG4gICAgICBjb25zdCBwYXRjaGVkQ2hpbGRyZW4gPSAoKHBhdGNoZWRQYXJhbWV0ZXIgYXMgTm9kZVBhcmFtZXRlcikuY2hpbGRyZW4gPSB7XG4gICAgICAgIC4uLihwYXRjaGVkUGFyYW1ldGVyIGFzIE5vZGVQYXJhbWV0ZXIpLmNoaWxkcmVuLFxuICAgICAgfSlcbiAgICAgIGZvciAoY29uc3QgW2NoaWxkSWQsIGNoaWxkUGF0Y2hdIG9mIE9iamVjdC5lbnRyaWVzKFxuICAgICAgICB2YWx1ZSBhcyB7IFtjaGlsZElkOiBzdHJpbmddOiB1bmtub3duIH0sXG4gICAgICApKSB7XG4gICAgICAgIGlmIChjaGlsZFBhdGNoID09PSBudWxsKSB7XG4gICAgICAgICAgZGVsZXRlIHBhdGNoZWRDaGlsZHJlbltjaGlsZElkXVxuICAgICAgICB9IGVsc2UgaWYgKHBhdGNoZWRDaGlsZHJlbltjaGlsZElkXSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcGF0Y2hlZENoaWxkcmVuW2NoaWxkSWRdID0gY2hpbGRQYXRjaCBhcyBQYXJhbWV0ZXJcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBwYXRjaGVkQ2hpbGRyZW5bY2hpbGRJZF0gPSBwYXRjaFBhcmFtZXRlcihcbiAgICAgICAgICAgIHBhdGNoZWRDaGlsZHJlbltjaGlsZElkXSxcbiAgICAgICAgICAgIGNoaWxkUGF0Y2ggYXMgeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0sXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIDsocGF0Y2hlZFBhcmFtZXRlciBhcyB1bmtub3duIGFzIHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9KVtrZXldID0gdmFsdWVcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHBhdGNoZWRQYXJhbWV0ZXJcbn1cblxuLy8vIENvbnZlcnQgYSBsaXN0IG9mIGJyYWNrZXRzIGNvbnRhaW5pbmcgYW1vdW50cywgYmFzZXMsIHJhdGVzICYgdGhyZXNob2xkcyBieSBpbnN0YW50XG4vLy8gdG8gYSBkaWN0aW9uYXJ5IG9mIGJyYWNrZXRzIGJ5IGluc3RhbnQuXG4vLy8gRm9yIGhldXJpc3RpYywgc2VlIGZ1bmN0aW9uIGBidWlsZF9hcGlfc2NhbGVgIG9mXG4vLy8gaHR0cHM6Ly9naXRodWIuY29tL29wZW5maXNjYS9vcGVuZmlzY2EtY29yZS9ibG9iL21hc3Rlci9vcGVuZmlzY2Ffd2ViX2FwaS9sb2FkZXIvcGFyYW1ldGVycy5weVxuZXhwb3J0IGZ1bmN0aW9uIHNjYWxlQnlJbnN0YW50RnJvbUJyYWNrZXRzKGJyYWNrZXRzOiBCcmFja2V0W10pOiB7XG4gIFtpbnN0YW50OiBzdHJpbmddOiBTY2FsZUF0SW5zdGFudFxufSB7XG4gIGNvbnN0IGluc3RhbnRzID0gbmV3IFNldDxzdHJpbmc+KClcbiAgZm9yIChjb25zdCBicmFja2V0IG9mIGJyYWNrZXRzKSB7XG4gICAgZm9yIChjb25zdCBpbnN0YW50IG9mIE9iamVjdC5rZXlzKFxuICAgICAgKGJyYWNrZXQgYXMgQW1vdW50QnJhY2tldCkuYW1vdW50ID8/IHt9LFxuICAgICkpIHtcbiAgICAgIGluc3RhbnRzLmFkZChpbnN0YW50KVxuICAgIH1cbiAgICBmb3IgKGNvbnN0IGluc3RhbnQgb2YgT2JqZWN0LmtleXMoKGJyYWNrZXQgYXMgUmF0ZUJyYWNrZXQpLmJhc2UgPz8ge30pKSB7XG4gICAgICBpbnN0YW50cy5hZGQoaW5zdGFudClcbiAgICB9XG4gICAgZm9yIChjb25zdCBpbnN0YW50IG9mIE9iamVjdC5rZXlzKChicmFja2V0IGFzIFJhdGVCcmFja2V0KS5yYXRlID8/IHt9KSkge1xuICAgICAgaW5zdGFudHMuYWRkKGluc3RhbnQpXG4gICAgfVxuICAgIGZvciAoY29uc3QgaW5zdGFudCBvZiBPYmplY3Qua2V5cyhicmFja2V0LnRocmVzaG9sZCkpIHtcbiAgICAgIGluc3RhbnRzLmFkZChpbnN0YW50KVxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHNjYWxlQXRJbnN0YW50QnlJbnN0YW50OiB7XG4gICAgW2luc3RhbnQ6IHN0cmluZ106IFNjYWxlQXRJbnN0YW50XG4gIH0gPSB7fVxuICBmb3IgKGNvbnN0IGluc3RhbnQgb2YgaW5zdGFudHMpIHtcbiAgICBjb25zdCBzY2FsZUF0SW5zdGFudDogU2NhbGVBdEluc3RhbnQgPSBbXVxuICAgIGZvciAoY29uc3QgYnJhY2tldCBvZiBicmFja2V0cykge1xuICAgICAgY29uc3QgYnJhY2tldEF0SW5zdGFudCA9IHt9IGFzIHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9XG4gICAgICBmb3IgKGNvbnN0IGtleSBvZiBbXCJhbW91bnRcIiwgXCJiYXNlXCIsIFwicmF0ZVwiLCBcInRocmVzaG9sZFwiXSkge1xuICAgICAgICBjb25zdCB2YWx1ZUJ5SW5zdGFudCA9IChcbiAgICAgICAgICBicmFja2V0IGFzIHVua25vd24gYXMge1xuICAgICAgICAgICAgW2tleTogc3RyaW5nXTogeyBbaW5zdGFudDogc3RyaW5nXTogdW5rbm93biB9XG4gICAgICAgICAgfVxuICAgICAgICApPy5ba2V5XVxuICAgICAgICBpZiAodmFsdWVCeUluc3RhbnQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH1cbiAgICAgICAgbGV0IGJlc3RJbnN0YW50OiBzdHJpbmcgfCB1bmRlZmluZWQgPSB1bmRlZmluZWRcbiAgICAgICAgZm9yIChjb25zdCB2YWx1ZUluc3RhbnQgb2YgT2JqZWN0LmtleXModmFsdWVCeUluc3RhbnQpKSB7XG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgKGJlc3RJbnN0YW50ID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgICAgICAgYmVzdEluc3RhbnQubG9jYWxlQ29tcGFyZSh2YWx1ZUluc3RhbnQpIDwgMCkgJiZcbiAgICAgICAgICAgIHZhbHVlSW5zdGFudC5sb2NhbGVDb21wYXJlKGluc3RhbnQpIDw9IDBcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIGJlc3RJbnN0YW50ID0gdmFsdWVJbnN0YW50XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChiZXN0SW5zdGFudCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgY29udGludWVcbiAgICAgICAgfVxuICAgICAgICBicmFja2V0QXRJbnN0YW50W2tleV0gPSB2YWx1ZUJ5SW5zdGFudFtiZXN0SW5zdGFudF1cbiAgICAgIH1cbiAgICAgIGlmIChcbiAgICAgICAgT2JqZWN0LmtleXMoYnJhY2tldEF0SW5zdGFudCkubGVuZ3RoID09PSAwIHx8XG4gICAgICAgIE9iamVjdC52YWx1ZXMoYnJhY2tldEF0SW5zdGFudCkuZXZlcnkoXG4gICAgICAgICAgKHZhbHVlKSA9PlxuICAgICAgICAgICAgdmFsdWUgIT09IFwiZXhwZWN0ZWRcIiAmJlxuICAgICAgICAgICAgKHZhbHVlIGFzIHsgdmFsdWU/OiB1bmtub3duIH0pLnZhbHVlID09PSBudWxsLFxuICAgICAgICApXG4gICAgICApIHtcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cbiAgICAgIHNjYWxlQXRJbnN0YW50LnB1c2goYnJhY2tldEF0SW5zdGFudCBhcyB1bmtub3duIGFzIEJyYWNrZXRBdEluc3RhbnQpXG4gICAgfVxuICAgIC8vIEFkZCB0aGUgc2NhbGVBdEluc3RhbnQsIGV2ZW4gd2hlbiBpdCBpcyBlbXB0eS5cbiAgICBzY2FsZUF0SW5zdGFudEJ5SW5zdGFudFtpbnN0YW50XSA9IHNjYWxlQXRJbnN0YW50XG4gIH1cbiAgcmV0dXJuIHNjYWxlQXRJbnN0YW50QnlJbnN0YW50XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzY2FsZVBhcmFtZXRlclVzZXNCYXNlKHBhcmFtZXRlcjogU2NhbGVQYXJhbWV0ZXIpOiBib29sZWFuIHtcbiAgcmV0dXJuIChcbiAgICBpc1JhdGVTY2FsZVBhcmFtZXRlcihwYXJhbWV0ZXIpICYmXG4gICAgKHBhcmFtZXRlci5icmFja2V0cyBhcyBSYXRlQnJhY2tldFtdKS5zb21lKFxuICAgICAgKGJyYWNrZXQ6IFJhdGVCcmFja2V0KSA9PiBicmFja2V0LmJhc2UgIT09IHVuZGVmaW5lZCxcbiAgICApXG4gIClcbn1cblxuZXhwb3J0IGZ1bmN0aW9uKiB3YWxrUGFyYW1ldGVycyhcbiAgcGFyYW1ldGVyOiBQYXJhbWV0ZXIsXG4gIHsgZGVwdGhGaXJzdCwgd2l0aE5vZGVzIH06IHsgZGVwdGhGaXJzdD86IGJvb2xlYW47IHdpdGhOb2Rlcz86IGJvb2xlYW4gfSA9IHt9LFxuKTogR2VuZXJhdG9yPFBhcmFtZXRlciwgdm9pZCwgdW5rbm93bj4ge1xuICBpZiAoIWRlcHRoRmlyc3QgJiYgKHBhcmFtZXRlci5jbGFzcyAhPT0gUGFyYW1ldGVyQ2xhc3MuTm9kZSB8fCB3aXRoTm9kZXMpKSB7XG4gICAgeWllbGQgcGFyYW1ldGVyXG4gIH1cbiAgc3dpdGNoIChwYXJhbWV0ZXIuY2xhc3MpIHtcbiAgICBjYXNlIFBhcmFtZXRlckNsYXNzLk5vZGU6XG4gICAgICBpZiAocGFyYW1ldGVyLmNoaWxkcmVuICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgZm9yIChjb25zdCBjaGlsZCBvZiBPYmplY3QudmFsdWVzKHBhcmFtZXRlci5jaGlsZHJlbikpIHtcbiAgICAgICAgICB5aWVsZCogd2Fsa1BhcmFtZXRlcnMoY2hpbGQsIHsgZGVwdGhGaXJzdCwgd2l0aE5vZGVzIH0pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGJyZWFrXG4gICAgZGVmYXVsdDpcbiAgfVxuICBpZiAoZGVwdGhGaXJzdCAmJiAocGFyYW1ldGVyLmNsYXNzICE9PSBQYXJhbWV0ZXJDbGFzcy5Ob2RlIHx8IHdpdGhOb2RlcykpIHtcbiAgICB5aWVsZCBwYXJhbWV0ZXJcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBOztBQTBIQSxXQUFZQSxjQUFjLDBCQUFkQSxjQUFjO0VBQWRBLGNBQWM7RUFBZEEsY0FBYztFQUFkQSxjQUFjO0VBQUEsT0FBZEEsY0FBYztBQUFBO0FBaUQxQixXQUFZQyxTQUFTLDBCQUFUQSxTQUFTO0VBQVRBLFNBQVM7RUFB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