@tato30/vue-pdf
Version:
PDF component for Vue 3
804 lines • 1.4 MB
JavaScript
var Gl = Object.defineProperty;
var Nl = (b, W, Z) => W in b ? Gl(b, W, { enumerable: !0, configurable: !0, writable: !0, value: Z }) : b[W] = Z;
var q = (b, W, Z) => (Nl(b, typeof W != "symbol" ? W + "" : W, Z), Z);
import { defineComponent as v, ref as U, watch as k, onMounted as e, openBlock as K, createElementBlock as A, toRaw as g, computed as f, onUnmounted as Xl, createBlock as _, mergeProps as r, createCommentVNode as $, createVNode as Yl, withDirectives as Ml, createElementVNode as dl, renderSlot as Tl, vShow as Vl, shallowRef as P, isRef as yl } from "vue";
import * as F from "pdfjs-dist";
const B = "internal-link", hl = "link", Ll = "file-attachment", zl = "form-text", sl = "form-select", Il = "form-checkbox", ul = "form-radio", pl = "form-button", ll = ["click", "dblclick", "mouseover", "input", "change"];
function E(b, W, Z) {
const l = [];
if (Z)
for (const d of Z)
d[b] === W && l.push(d);
return l;
}
function j(b, W) {
return { type: b, data: W };
}
function n(b, W) {
switch (b.type) {
case "textarea":
case "text":
return j(zl, {
fieldName: b.name,
value: b.value
});
case "select-one":
case "select-multiple":
const Z = [];
for (const d of b.options)
Z.push({
value: d.value,
label: d.label
});
const l = [];
for (const d of b.selectedOptions)
l.push({
value: d.value,
label: d.label
});
return j(sl, {
fieldName: b.name,
value: l,
options: Z
});
case "checkbox":
return j(Il, {
fieldName: b.name,
checked: b.checked
});
case "radio":
return j(ul, {
fieldName: b.name,
...W
});
case "button":
return j(pl, {
fieldName: b.name,
...W
});
}
}
function il(b) {
return j(Ll, b.file);
}
async function al(b, W) {
if (b.dest) {
let Z;
if (typeof b.dest == "string" ? Z = await W.getDestination(b.dest) : Z = b.dest, !Array.isArray(Z))
return console.warn(`Destination "${Z}" is not a valid destination (dest="${b.dest}")`), j(B, {
referencedPage: null,
offset: null
});
let l = null;
Z.length === 5 && (l = {
left: b.dest[2],
bottom: b.dest[3]
});
const [d] = Z;
if (Number.isInteger(d))
return j(B, {
referencedPage: Number(d) + 1,
offset: l
});
if (typeof d == "object") {
const c = await W.getPageIndex(d);
return j(B, {
referencedPage: c + 1,
offset: l
});
} else
return console.warn(
`Destination "${d}" is not a valid destination (dest="${b.dest}")`
), j(B, {
referencedPage: null,
offset: null
});
} else if (b.url)
return j(hl, {
url: b.url,
unsafeUrl: b.unsafeUrl
});
}
function Zl(b) {
for (const W of b.getElementsByTagName("span")) {
let Z = W.textContent;
const l = JSON.parse(W.dataset.l10nArgs ?? "{}");
if (Z)
for (const d in l)
Z = Z.replace(`{{${d}}}`, l[d]);
W.textContent = Z;
}
}
function Rl(b, W, Z) {
var d;
let l = b.target.parentNode;
if (l.tagName === "DIV" && (l = l.firstChild), l.className === "linkAnnotation" && b.type === "click") {
const c = (d = l.dataset) == null ? void 0 : d.annotationId;
if (c)
return al(E("id", c, Z)[0], W);
} else if (l.className.includes("popupAnnotation") || l.className.includes("textAnnotation"))
Zl(l);
else if (l.className.includes("fileAttachmentAnnotation")) {
Zl(l);
const c = l.dataset.annotationId;
if (c && b.type === "dblclick")
return il(E("id", c, Z)[0]);
} else if (l.className.includes("textWidgetAnnotation") && b.type === "input") {
let c = l.getElementsByTagName("input")[0];
return c || (c = l.getElementsByTagName("textarea")[0]), n(c);
} else {
if (l.className.includes("choiceWidgetAnnotation") && b.type === "input")
return n(l.getElementsByTagName("select")[0]);
if (l.className.includes("buttonWidgetAnnotation checkBox") && b.type === "change")
return n(l.getElementsByTagName("input")[0]);
if (l.className.includes("buttonWidgetAnnotation radioButton") && b.type === "change") {
const c = l.dataset.annotationId;
if (c) {
const m = E("id", c, Z)[0], z = [];
for (const h of E("fieldName", m.fieldName, Z))
h.buttonValue && z.push(h.buttonValue);
return n(l.getElementsByTagName("input")[0], {
value: m.buttonValue,
defaultValue: m.fieldValue,
options: z
});
}
} else if (l.className.includes("buttonWidgetAnnotation pushButton") && b.type === "click") {
const c = l.dataset.annotationId;
if (c) {
const m = E("id", c, Z)[0];
return m.resetForm ? n(
{ name: m.fieldName, type: "button" },
{ actions: m.actions, reset: !0 }
) : n(
{ name: m.fieldName, type: "button" },
{ actions: m.actions, reset: !1 }
);
}
}
}
}
class cl {
constructor() {
q(this, "externalLinkEnabled");
this.externalLinkEnabled = !0;
}
/**
* @type {number}
*/
get pagesCount() {
return 0;
}
/**
* @type {number}
*/
get page() {
return 0;
}
/**
* @param {number} _value
*/
set page(W) {
}
/**
* @type {number}
*/
get rotation() {
return 0;
}
/**
* @param {number} _value
*/
set rotation(W) {
}
/**
* @type {boolean}
*/
get isInPresentationMode() {
return !1;
}
/**
* @param {string|Array} _dest - The named, or explicit, PDF destination.
*/
async goToDestination(W) {
}
/**
* @param {number|string} _val - The page number, or page label.
*/
goToPage(W) {
}
/**
* @param {HTMLAnchorElement} link
* @param {string} url
* @param {boolean} [_newWindow]
*/
addLinkAttributes(W, Z, l = !1) {
}
/**
* @param _dest - The PDF destination object.
* @returns {string} The hyperlink to the PDF object.
*/
getDestinationHash(W) {
return "#";
}
/**
* @param _hash - The PDF parameters/hash.
* @returns {string} The hyperlink to the PDF object.
*/
getAnchorUrl(W) {
return "#";
}
/**
* @param {string} _hash
*/
setHash(W) {
}
/**
* @param {string} _action
*/
executeNamedAction(W) {
}
/**
* @param {Object} _action
*/
executeSetOCGState(W) {
}
/**
* @param {number} _pageNum - page number.
* @param {Object} _pageRef - reference to the page.
*/
cachePageRef(W, Z) {
}
}
const wl = /* @__PURE__ */ v({
__name: "AnnotationLayer",
props: {
page: {},
viewport: {},
document: {},
annotationsFilter: {},
annotationsMap: {},
imageResourcesPath: {},
hideForms: { type: Boolean },
enableScripting: { type: Boolean },
intent: {}
},
emits: ["annotation", "annotationLoaded"],
setup(b, { emit: W }) {
const Z = b, l = W, d = U(), c = U();
function m(I) {
const Y = Rl(I, Z.document, c.value);
Promise.resolve(Y).then((V) => {
V && l("annotation", V);
});
}
async function z() {
var Y;
return await ((Y = g(Z.document)) == null ? void 0 : Y.getFieldObjects());
}
async function h() {
var Y;
return await ((Y = g(Z.document)) == null ? void 0 : Y.hasJSActions());
}
async function T() {
const I = Z.page;
let Y = await (I == null ? void 0 : I.getAnnotations({ intent: Z.intent }));
if (Z.annotationsFilter) {
const V = Z.annotationsFilter;
Y = Y.filter((N) => {
const G = N.subtype, L = N.fieldType ? `${G}.${N.fieldType}` : null;
return (V == null ? void 0 : V.includes(G)) || L !== null && (V == null ? void 0 : V.includes(L));
});
}
return Y;
}
async function u() {
var R, a;
(a = (R = d.value).replaceChildren) == null || a.call(R);
for (const i of ll)
d.value.removeEventListener(i, m);
const I = g(Z.document), Y = Z.page, V = Z.viewport;
c.value = await T();
const N = /* @__PURE__ */ new Map([]);
for (const i of c.value)
if (i.subtype === "Widget" && i.fieldType === "Btn" && i.pushButton) {
const x = i.rect[2] - i.rect[0], J = i.rect[3] - i.rect[1], X = document.createElement("canvas");
X.setAttribute("width", (x * V.scale).toString()), X.setAttribute("height", (J * V.scale).toString()), N.set(i.id, X);
}
const G = I.annotationStorage;
if (Z.annotationsMap)
for (const [i, x] of Object.entries(Z.annotationsMap))
G.setValue(i, x);
const L = {
accessibilityManager: void 0,
annotationCanvasMap: N,
div: d.value,
page: Y,
viewport: V.clone({ dontFlip: !0 }),
annotationEditorUIManager: null,
l10n: null,
structTreeLayer: null
}, p = {
annotations: c.value,
viewport: V.clone({ dontFlip: !0 }),
linkService: new cl(),
annotationCanvasMap: N,
div: d.value,
annotationStorage: G,
renderForms: !Z.hideForms,
page: Y,
enableScripting: !1,
hasJSActions: await h(),
fieldObjects: await z(),
downloadManager: null,
imageResourcesPath: Z.imageResourcesPath
};
new F.AnnotationLayer(L).render(p).then(async () => {
l("annotationLoaded", await T());
});
for (const i of ll)
d.value.addEventListener(i, m);
}
return k(() => Z.viewport, () => {
Z.page && Z.viewport && d.value && u();
}), e(() => {
Z.page && Z.viewport && d.value && u();
}), (I, Y) => (K(), A("div", {
ref_key: "layer",
ref: d,
class: "annotationLayer",
style: { display: "block" }
}, null, 512));
}
});
function Sl(b, W, Z) {
const l = [];
for (const u of b.items)
l.push(u.str), u.hasEOL && l.push(`
`);
let d = l.join("");
d = d.replace(new RegExp("(?<=\\p{Ideographic}|[\\u3040-\\u30FF])\\n(\\p{Ideographic}|[\\u3040-\\u30FF])", "gmu"), "$1"), d = d.replace(new RegExp("(?<=\\S)-\\n", "gmu"), ""), d = d.replace(/\n/g, " ");
const c = ["g"];
Z.ignoreCase && c.push("i");
let m = W.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
Z.completeWords && (m = `\\b${m}\\b`);
const z = new RegExp(m, c.join("")), h = [];
let T;
for (; (T = z.exec(d)) !== null; )
h.push([T.index, T[0].length, T[0]]);
return h;
}
function jl(b, W) {
function Z(h, T, u) {
if (h.hasEOL && h.str.length === 0) {
const I = T == null ? void 0 : T.str[T.str.length - 1], Y = u == null ? void 0 : u.str[0], V = new RegExp(/(\p{Ideographic}|[\u3040-\u30FF])/u);
if (I && V.test(I) && Y && V.test(Y))
return 0;
}
return h.hasEOL ? h.str.endsWith("-") ? -1 : 1 : 0;
}
let l = 0, d = 0;
const c = W.items, m = c.length - 1, z = [];
for (let h = 0; h < b.length; h++) {
let T = b[h][0];
for (; l !== m && T >= d + c[l].str.length; ) {
const Y = c[l];
d += Y.str.length + Z(
Y,
l - 1 < 0 ? null : c[l - 1],
l + 1 >= c.length ? null : c[l + 1]
), l++;
}
const u = {
idx: l,
offset: T - d
};
for (T += b[h][1]; l !== m && T > d + c[l].str.length; ) {
const Y = c[l];
d += Y.str.length + Z(
Y,
l - 1 < 0 ? null : c[l - 1],
l + 1 >= c.length ? null : c[l + 1]
), l++;
}
const I = {
idx: l,
offset: T - d
};
z.push({
start: u,
end: I,
str: b[h][2],
oindex: b[h][0]
});
}
return z;
}
function xl(b, W, Z) {
function l(d, c = -1, m = -1) {
const z = W.items[d], h = [];
let T = "", u = "", I = "", Y = Z[d];
if (!Y)
return;
if (Y.nodeType === Node.TEXT_NODE) {
const G = document.createElement("span");
Y.before(G), G.append(Y), Z[d] = G, Y = G;
}
c >= 0 && m >= 0 ? T = z.str.substring(c, m) : c < 0 && m < 0 ? T = z.str : c >= 0 ? T = z.str.substring(c) : m >= 0 && (T = z.str.substring(0, m));
const V = document.createTextNode(T), N = document.createElement("span");
if (N.className = "highlight appended", N.append(V), h.push(N), c > 0)
if (Y.childNodes.length === 1 && Y.childNodes[0].nodeType === Node.TEXT_NODE) {
u = z.str.substring(0, c);
const G = document.createTextNode(u);
h.unshift(G);
} else {
let G = 0;
const L = [];
for (const p of Y.childNodes) {
const s = p.nodeType === Node.TEXT_NODE ? p.nodeValue : p.firstChild.nodeValue;
G += s.length, G <= c ? L.push(p) : c >= G - s.length && m <= G && L.push(document.createTextNode(s.substring(0, c - (G - s.length))));
}
h.unshift(...L);
}
if (m > 0) {
I = z.str.substring(m);
const G = document.createTextNode(I);
h.push(G);
}
Y.replaceChildren(...h);
}
for (const d of b.sort((c, m) => c.oindex - m.oindex))
if (d.start.idx === d.end.idx)
l(d.start.idx, d.start.offset, d.end.offset);
else
for (let c = d.start.idx, m = d.end.idx; c <= m; c++)
c === d.start.idx ? l(c, d.start.offset) : c === d.end.idx ? l(c, -1, d.end.offset) : l(c);
}
function Jl(b, W) {
const Z = b.items.map((l) => l.str);
for (let l = 0; l < W.length; l++) {
const d = W[l];
if (d && d.nodeType !== Node.TEXT_NODE) {
const c = document.createTextNode(Z[l]);
d.replaceChildren(c);
}
}
}
function Dl(b, W, Z) {
const l = [];
for (const d of b) {
const c = Sl(W, d, Z);
l.push(...jl(c, W));
}
return l;
}
const tl = /* @__PURE__ */ v({
__name: "TextLayer",
props: {
page: {},
viewport: {},
highlightText: {},
highlightOptions: {},
highlightPages: {}
},
emits: ["highlight", "textLoaded"],
setup(b, { emit: W }) {
const Z = b, l = W, d = U(), c = U();
let m = [], z = null;
function h() {
return Object.assign(
{},
{
ignoreCase: !0,
completeWords: !1
},
Z.highlightOptions
);
}
async function T(N = !1) {
const G = Z.page, L = await (G == null ? void 0 : G.getTextContent());
if (L && (N && Jl(L, m), Z.highlightText && (!Z.highlightPages || Z.highlightPages.includes(G.pageNumber)))) {
const p = typeof Z.highlightText == "string" ? [Z.highlightText] : Z.highlightText, s = Dl(
p,
L,
h()
);
xl(s, L, m), l("highlight", {
matches: s,
textContent: L,
textDivs: m,
page: (G == null ? void 0 : G.pageNumber) || 1
});
}
}
async function u() {
var R, a;
z == null || z.cancel(), (a = (R = d.value).replaceChildren) == null || a.call(R);
const N = Z.page, G = Z.viewport, L = N == null ? void 0 : N.streamTextContent({
includeMarkedContent: !0,
disableNormalization: !0
}), p = new F.TextLayer({
container: d.value,
textContentSource: L,
viewport: G
});
z = p, await p.render(), m = p.textDivs;
const s = await (N == null ? void 0 : N.getTextContent());
l("textLoaded", { textDivs: m, textContent: s }), I(), T();
}
function I() {
var G;
const N = document.createElement("div");
N.className = "endOfContent", (G = d.value) == null || G.appendChild(N), c.value = N;
}
function Y() {
c.value && c.value.classList.add("active");
}
function V() {
c.value && c.value.classList.remove("active");
}
return k(
() => Z.viewport,
(N) => {
Z.page && Z.viewport && d.value && u();
}
), k(
() => [Z.highlightText, Z.highlightOptions],
(N) => {
T(!0);
},
{ deep: !0 }
), e(() => {
Z.page && Z.viewport && d.value && u();
}), (N, G) => (K(), A("div", {
ref_key: "layer",
ref: d,
class: "textLayer",
style: { display: "block" },
onMousedown: Y,
onMouseup: V
}, null, 544));
}
}), Ul = /* @__PURE__ */ v({
__name: "XFALayer",
props: {
page: {},
document: {},
viewport: {}
},
emits: ["xfaLoaded"],
setup(b, { emit: W }) {
const Z = b, l = W, d = U();
async function c() {
var T, u;
(u = (T = d.value).replaceChildren) == null || u.call(T);
const m = g(Z.document), z = Z.page, h = Z.viewport;
if (m.isPureXfa) {
const I = await z.getXfa(), Y = {
div: d.value,
viewport: h.clone({ dontFlip: !0 }),
linkService: new cl(),
annotationStorage: m == null ? void 0 : m.annotationStorage,
xfaHtml: I
};
F.XfaLayer.render(Y), l("xfaLoaded");
}
}
return k(() => Z.viewport, (m) => {
Z.page && Z.viewport && d.value && c();
}), e(() => {
Z.page && Z.viewport && d.value && c();
}), (m, z) => (K(), A("div", {
ref_key: "layer",
ref: d,
style: { display: "block" }
}, null, 512));
}
});
const Fl = /* @__PURE__ */ dl("canvas", {
dir: "ltr",
style: { display: "block" },
role: "main"
}, null, -1), bl = /* @__PURE__ */ v({
__name: "VuePDF",
props: {
pdf: {},
page: { default: 1 },
scale: { default: 1 },
rotation: {},
fitParent: { type: Boolean },
width: {},
height: {},
textLayer: { type: Boolean },
autoDestroy: { type: Boolean, default: !1 },
imageResourcesPath: {},
hideForms: { type: Boolean },
intent: { default: "display" },
annotationLayer: { type: Boolean },
annotationsFilter: {},
annotationsMap: {},
watermarkText: {},
watermarkOptions: {},
highlightText: {},
highlightOptions: {},
highlightPages: {}
},
emits: ["annotation", "highlight", "loaded", "textLoaded", "annotationLoaded", "xfaLoaded"],
setup(b, { expose: W, emit: Z }) {
const l = b, d = Z, c = U(), m = U(), z = U(!1);
let h;
const T = f(() => ({
viewport: void 0,
document: void 0,
page: void 0
})), u = f(() => ({
annotationsMap: l.annotationsMap,
annotationsFilter: l.annotationsFilter,
imageResourcesPath: l.imageResourcesPath,
hideForms: l.hideForms,
intent: l.intent
})), I = f(() => ({
highlightText: l.highlightText,
highlightOptions: l.highlightOptions,
highlightPages: l.highlightPages
}));
function Y() {
return Object.assign(
{},
{
columns: 4,
rows: 4,
rotation: 45,
fontSize: 18,
color: "rgba(211, 210, 211, 0.4)"
},
l.watermarkOptions
);
}
function V(X) {
if (!(typeof X == "number" && X % 90 === 0))
return 0;
const M = X / 90;
return M > 4 ? V(X - 360) : M < 0 ? V(X + 360) : X;
}
function N(X) {
let M = l.scale;
if (l.fitParent) {
const y = c.value.parentNode.clientWidth, S = X.getViewport({ scale: 1 }).width;
M = y / S;
} else if (l.width) {
const y = X.getViewport({ scale: 1 }).width;
M = l.width / y;
} else if (l.height) {
const y = X.getViewport({ scale: 1 }).height;
M = l.height / y;
}
return M;
}
function G(X = 1) {
if (!l.watermarkText)
return;
const M = L();
if (!M)
return;
const y = M.getContext("2d");
if (!y)
return;
const S = Y(), O = l.watermarkText, w = S.columns, D = S.rows, t = w * D, o = S.rotation, Q = S.fontSize * X;
y.font = `${Q}px Trebuchet MS`, y.fillStyle = S.color;
for (let C = 0; C < t; C++) {
const H = C % w * (M.width / w) + M.width / (w * 2), Wl = Math.floor(C / w) * (M.height / D) + M.height / (D * 2), ml = y.measureText(O).width;
y.save(), y.translate(H, Wl), y.rotate(-o * (Math.PI / 180)), y.fillText(O, -ml / 2, Q / 2), y.restore();
}
}
function L() {
var M;
let X = null;
return (M = c.value) == null || M.childNodes.forEach((y) => {
y.tagName === "CANVAS" && (X = y);
}), X;
}
function p(X) {
var O, w, D;
let M;
const y = L();
y && (y == null ? void 0 : y.getAttribute("role")) === "main" ? M = y : (M = document.createElement("canvas"), M.style.display = "block", M.setAttribute("dir", "ltr"));
const S = window.devicePixelRatio || 1;
return M.width = Math.floor(X.width * S), M.height = Math.floor(X.height * S), M.style.width = `${Math.floor(X.width)}px`, M.style.height = `${Math.floor(X.height)}px`, (O = c.value) == null || O.style.setProperty("--scale-factor", `${X.scale}`), (w = c.value) == null || w.style.setProperty("--user-unit", `${X.userUnit}`), (D = c.value) == null || D.style.setProperty("--total-scale-factor", "calc(var(--scale-factor) * var(--user-unit))"), m.value.style.width = `${Math.floor(X.width)}px`, m.value.style.height = `${Math.floor(X.height)}px`, m.value.style.top = "0", m.value.style.left = "0", z.value = !0, M;
}
function s() {
h && h.cancel();
}
function R(X) {
var M;
(M = g(T.value.document)) == null || M.getPage(X).then((y) => {
var H;
s();
const S = y.getViewport(), O = {
scale: N(y),
rotation: V((l.rotation || 0) + S.rotation)
}, w = y.getViewport(O), D = L(), t = p(w), o = window.devicePixelRatio || 1, Q = o !== 1 ? [o, 0, 0, o, 0, 0] : void 0, C = {
canvasContext: t.getContext("2d"),
viewport: w,
annotationMode: l.hideForms ? F.AnnotationMode.ENABLE : F.AnnotationMode.ENABLE_FORMS,
transform: Q,
intent: l.intent
};
(t == null ? void 0 : t.getAttribute("role")) !== "main" ? D && ((H = c.value) == null || H.replaceChild(t, D)) : t.removeAttribute("role"), T.value.page = y, T.value.viewport = w, h = y.render(C), h.promise.then(() => {
z.value = !1, G(w.scale), d("loaded", T.value.viewport);
}).catch(() => {
});
});
}
function a(X) {
X.promise.then(async (M) => {
T.value.document = M, R(l.page);
});
}
k(
() => l.pdf,
(X) => {
X !== void 0 && a(X);
}
), k(
() => [
l.scale,
l.width,
l.height,
l.rotation,
l.page,
l.hideForms,
l.intent
],
() => {
R(l.page);
}
), e(() => {
l.pdf !== void 0 && a(l.pdf);
}), Xl(() => {
var X;
l.autoDestroy && ((X = l.pdf) == null || X.destroy());
});
function i() {
var X;
(X = l.pdf) == null || X.destroy();
}
function x() {
R(l.page);
}
function J() {
s();
}
return W({
reload: x,
cancel: J,
destroy: i
}), (X, M) => (K(), A("div", {
ref_key: "container",
ref: c,
style: { position: "relative", display: "block" }
}, [
Fl,
X.annotationLayer ? (K(), _(wl, r({ key: 0 }, { ...T.value, ...u.value }, {
onAnnotation: M[0] || (M[0] = (y) => d("annotation", y)),
onAnnotationLoaded: M[1] || (M[1] = (y) => d("annotationLoaded", y))
}), null, 16)) : $("", !0),
X.textLayer ? (K(), _(tl, r({ key: 1 }, { ...T.value, ...I.value }, {
onHighlight: M[2] || (M[2] = (y) => d("highlight", y)),
onTextLoaded: M[3] || (M[3] = (y) => d("textLoaded", y))
}), null, 16)) : $("", !0),
Yl(Ul, r({ ...T.value }, {
onXfaLoaded: M[4] || (M[4] = (y) => d("xfaLoaded"))
}), null, 16),
Ml(dl("div", {
ref_key: "loadingLayer",
ref: m,
style: { position: "absolute" }
}, [
Tl(X.$slots, "default")
], 512), [
[Vl, z.value]
])
], 512));
}
}), Ol = "data:application/javascript;base64,LyoqCiAqIEBsaWNzdGFydCBUaGUgZm9sbG93aW5nIGlzIHRoZSBlbnRpcmUgbGljZW5zZSBub3RpY2UgZm9yIHRoZQogKiBKYXZhU2NyaXB0IGNvZGUgaW4gdGhpcyBwYWdlCiAqCiAqIENvcHlyaWdodCAyMDI0IE1vemlsbGEgRm91bmRhdGlvbgogKgogKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLgogKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXQKICoKICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMAogKgogKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICJBUyBJUyIgQkFTSVMsCiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLgogKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLgogKgogKiBAbGljZW5kIFRoZSBhYm92ZSBpcyB0aGUgZW50aXJlIGxpY2Vuc2Ugbm90aWNlIGZvciB0aGUKICogSmF2YVNjcmlwdCBjb2RlIGluIHRoaXMgcGFnZQogKi8KLyoqCiAqIHBkZmpzVmVyc2lvbiA9IDUuMy4zMQogKiBwZGZqc0J1aWxkID0gNDdhZDgyMGQ5CiAqLwpjb25zdCBlPSEoIm9iamVjdCIhPXR5cGVvZiBwcm9jZXNzfHxwcm9jZXNzKyIiIT0iW29iamVjdCBwcm9jZXNzXSJ8fHByb2Nlc3MudmVyc2lvbnMubnd8fHByb2Nlc3MudmVyc2lvbnMuZWxlY3Ryb24mJnByb2Nlc3MudHlwZSYmImJyb3dzZXIiIT09cHJvY2Vzcy50eXBlKSx0PVsuMDAxLDAsMCwuMDAxLDAsMF0sYT0xLjM1LHI9LjM1LGk9LjI1OTI1OTI1OTI1OTI1OTI0LG49MSxzPTIsbz00LGM9OCxsPTE2LGg9NjQsdT0xMjgsZD0yNTYsZj0icGRmanNfaW50ZXJuYWxfZWRpdG9yXyIsZz0zLHA9OSxtPTEzLGI9MTUseT0xMDEsdz17UFJJTlQ6NCxNT0RJRllfQ09OVEVOVFM6OCxDT1BZOjE2LE1PRElGWV9BTk5PVEFUSU9OUzozMixGSUxMX0lOVEVSQUNUSVZFX0ZPUk1TOjI1NixDT1BZX0ZPUl9BQ0NFU1NJQklMSVRZOjUxMixBU1NFTUJMRToxMDI0LFBSSU5UX0hJR0hfUVVBTElUWToyMDQ4fSxTPTAseD00LGs9MSxDPTIsdj0zLEY9MSxPPTIsVD0zLE09NCxEPTUsUj02LE49NyxFPTgsTD05LGo9MTAsXz0xMSxVPTEyLFg9MTMscT0xNCxIPTE1LFc9MTYsej0xNywkPTIwLEc9Ikdyb3VwIixWPSJSIixLPTEsSj0yLFk9NCxaPTE2LFE9MzIsZWU9MTI4LHRlPTUxMixhZT0xLHJlPTIsaWU9NDA5NixuZT04MTkyLHNlPTMyNzY4LG9lPTY1NTM2LGNlPTEzMTA3MixsZT0xMDQ4NTc2LGhlPTIwOTcxNTIsdWU9ODM4ODYwOCxkZT0xNjc3NzIxNixmZT0xLGdlPTIscGU9MyxtZT00LGJlPTUseWU9e0U6Ik1vdXNlIEVudGVyIixYOiJNb3VzZSBFeGl0IixEOiJNb3VzZSBEb3duIixVOiJNb3VzZSBVcCIsRm86IkZvY3VzIixCbDoiQmx1ciIsUE86IlBhZ2VPcGVuIixQQzoiUGFnZUNsb3NlIixQVjoiUGFnZVZpc2libGUiLFBJOiJQYWdlSW52aXNpYmxlIixLOiJLZXlzdHJva2UiLEY6IkZvcm1hdCIsVjoiVmFsaWRhdGUiLEM6IkNhbGN1bGF0ZSJ9LHdlPXtXQzoiV2lsbENsb3NlIixXUzoiV2lsbFNhdmUiLERTOiJEaWRTYXZlIixXUDoiV2lsbFByaW50IixEUDoiRGlkUHJpbnQifSxTZT17TzoiUGFnZU9wZW4iLEM6IlBhZ2VDbG9zZSJ9LHhlPTEsQWU9NSxrZT0xLENlPTIsdmU9MyxGZT00LEllPTUsT2U9NixUZT03LE1lPTgsRGU9OSxCZT0xMCxSZT0xMSxOZT0xMixFZT0xMyxQZT0xNCxMZT0xNSxqZT0xNixfZT0xNyxVZT0xOCxYZT0xOSxxZT0yMCxIZT0yMSxXZT0yMix6ZT0yMywkZT0yNCxHZT0yNSxWZT0yNixLZT0yNyxKZT0yOCxZZT0yOSxaZT0zMCxRZT0zMSxldD0zMix0dD0zMyxhdD0zNCxydD0zNSxpdD0zNixudD0zNyxzdD0zOCxvdD0zOSxjdD00MCxsdD00MSxodD00Mix1dD00MyxkdD00NCxmdD00NSxndD00NixwdD00NyxtdD00OCxidD00OSx5dD01MCx3dD01MSxTdD01Mix4dD01MyxBdD01NCxrdD01NSxDdD01Nix2dD01NyxGdD01OCxJdD01OSxPdD02MCxUdD02MSxNdD02MixEdD02MyxCdD02NCxSdD02NSxOdD02NixFdD02NyxQdD02OCxMdD02OSxqdD03MCxfdD03MSxVdD03MixYdD03MyxxdD03NCxIdD03NSxXdD03Nix6dD03NywkdD04MCxHdD04MSxWdD04MyxLdD04NCxKdD04NSxZdD04NixadD04NyxRdD04OCxlYT04OSx0YT05MCxhYT05MSxyYT05MixpYT05MyxuYT05NCxzYT0wLG9hPTEsY2E9MixsYT0zLGhhPTEsdWE9MjtsZXQgZGE9eGU7ZnVuY3Rpb24gZ2V0VmVyYm9zaXR5TGV2ZWwoKXtyZXR1cm4gZGF9ZnVuY3Rpb24gaW5mbyhlKXtkYT49QWUmJmNvbnNvbGUubG9nKGBJbmZvOiAke2V9YCl9ZnVuY3Rpb24gd2FybihlKXtkYT49eGUmJmNvbnNvbGUubG9nKGBXYXJuaW5nOiAke2V9YCl9ZnVuY3Rpb24gdW5yZWFjaGFibGUoZSl7dGhyb3cgbmV3IEVycm9yKGUpfWZ1bmN0aW9uIGFzc2VydChlLHQpe2V8fHVucmVhY2hhYmxlKHQpfWZ1bmN0aW9uIGNyZWF0ZVZhbGlkQWJzb2x1dGVVcmwoZSx0PW51bGwsYT1udWxsKXtpZighZSlyZXR1cm4gbnVsbDtpZihhJiYic3RyaW5nIj09dHlwZW9mIGUpe2lmKGEuYWRkRGVmYXVsdFByb3RvY29sJiZlLnN0YXJ0c1dpdGgoInd3dy4iKSl7Y29uc3QgdD1lLm1hdGNoKC9cLi9nKTt0Py5sZW5ndGg+PTImJihlPWBodHRwOi8vJHtlfWApfWlmKGEudHJ5Q29udmVydEVuY29kaW5nKXRyeXtlPXN0cmluZ1RvVVRGOFN0cmluZyhlKX1jYXRjaHt9fWNvbnN0IHI9dD9VUkwucGFyc2UoZSx0KTpVUkwucGFyc2UoZSk7cmV0dXJuIGZ1bmN0aW9uIF9pc1ZhbGlkUHJvdG9jb2woZSl7c3dpdGNoKGU/LnByb3RvY29sKXtjYXNlImh0dHA6IjpjYXNlImh0dHBzOiI6Y2FzZSJmdHA6IjpjYXNlIm1haWx0bzoiOmNhc2UidGVsOiI6cmV0dXJuITA7ZGVmYXVsdDpyZXR1cm4hMX19KHIpP3I6bnVsbH1mdW5jdGlvbiBzaGFkb3coZSx0LGEscj0hMSl7T2JqZWN0LmRlZmluZVByb3BlcnR5KGUsdCx7dmFsdWU6YSxlbnVtZXJhYmxlOiFyLGNvbmZpZ3VyYWJsZTohMCx3cml0YWJsZTohMX0pO3JldHVybiBhfWNvbnN0IGZhPWZ1bmN0aW9uIEJhc2VFeGNlcHRpb25DbG9zdXJlKCl7ZnVuY3Rpb24gQmFzZUV4Y2VwdGlvbihlLHQpe3RoaXMubWVzc2FnZT1lO3RoaXMubmFtZT10fUJhc2VFeGNlcHRpb24ucHJvdG90eXBlPW5ldyBFcnJvcjtCYXNlRXhjZXB0aW9uLmNvbnN0cnVjdG9yPUJhc2VFeGNlcHRpb247cmV0dXJuIEJhc2VFeGNlcHRpb259KCk7Y2xhc3MgUGFzc3dvcmRFeGNlcHRpb24gZXh0ZW5kcyBmYXtjb25zdHJ1Y3RvcihlLHQpe3N1cGVyKGUsIlBhc3N3b3JkRXhjZXB0aW9uIik7dGhpcy5jb2RlPXR9fWNsYXNzIFVua25vd25FcnJvckV4Y2VwdGlvbiBleHRlbmRzIGZhe2NvbnN0cnVjdG9yKGUsdCl7c3VwZXIoZSwiVW5rbm93bkVycm9yRXhjZXB0aW9uIik7dGhpcy5kZXRhaWxzPXR9fWNsYXNzIEludmFsaWRQREZFeGNlcHRpb24gZXh0ZW5kcyBmYXtjb25zdHJ1Y3RvcihlKXtzdXBlcihlLCJJbnZhbGlkUERGRXhjZXB0aW9uIil9fWNsYXNzIFJlc3BvbnNlRXhjZXB0aW9uIGV4dGVuZHMgZmF7Y29uc3RydWN0b3IoZSx0LGEpe3N1cGVyKGUsIlJlc3BvbnNlRXhjZXB0aW9uIik7dGhpcy5zdGF0dXM9dDt0aGlzLm1pc3Npbmc9YX19Y2xhc3MgRm9ybWF0RXJyb3IgZXh0ZW5kcyBmYXtjb25zdHJ1Y3RvcihlKXtzdXBlcihlLCJGb3JtYXRFcnJvciIpfX1jbGFzcyBBYm9ydEV4Y2VwdGlvbiBleHRlbmRzIGZhe2NvbnN0cnVjdG9yKGUpe3N1cGVyKGUsIkFib3J0RXhjZXB0aW9uIil9fWZ1bmN0aW9uIGJ5dGVzVG9TdHJpbmcoZSl7Im9iamVjdCI9PXR5cGVvZiBlJiZ2b2lkIDAhPT1lPy5sZW5ndGh8fHVucmVhY2hhYmxlKCJJbnZhbGlkIGFyZ3VtZW50IGZvciBieXRlc1RvU3RyaW5nIik7Y29uc3QgdD1lLmxlbmd0aCxhPTgxOTI7aWYodDxhKXJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsZSk7Y29uc3Qgcj1bXTtmb3IobGV0IGk9MDtpPHQ7aSs9YSl7Y29uc3Qgbj1NYXRoLm1pbihpK2EsdCkscz1lLnN1YmFycmF5KGksbik7ci5wdXNoKFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkobnVsbCxzKSl9cmV0dXJuIHIuam9pbigiIil9ZnVuY3Rpb24gc3RyaW5nVG9CeXRlcyhlKXsic3RyaW5nIiE9dHlwZW9mIGUmJnVucmVhY2hhYmxlKCJJbnZhbGlkIGFyZ3VtZW50IGZvciBzdHJpbmdUb0J5dGVzIik7Y29uc3QgdD1lLmxlbmd0aCxhPW5ldyBVaW50OEFycmF5KHQpO2ZvcihsZXQgcj0wO3I8dDsrK3IpYVtyXT0yNTUmZS5jaGFyQ29kZUF0KHIpO3JldHVybiBhfWZ1bmN0aW9uIHN0cmluZzMyKGUpe3JldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlKGU+PjI0JjI1NSxlPj4xNiYyNTUsZT4+OCYyNTUsMjU1JmUpfWZ1bmN0aW9uIG9iamVjdFNpemUoZSl7cmV0dXJuIE9iamVjdC5rZXlzKGUpLmxlbmd0aH1jbGFzcyBGZWF0dXJlVGVzdHtzdGF0aWMgZ2V0IGlzTGl0dGxlRW5kaWFuKCl7cmV0dXJuIHNoYWRvdyh0aGlzLCJpc0xpdHRsZUVuZGlhbiIsZnVuY3Rpb24gaXNMaXR0bGVFbmRpYW4oKXtjb25zdCBlPW5ldyBVaW50OEFycmF5KDQpO2VbMF09MTtyZXR1cm4gMT09PW5ldyBVaW50MzJBcnJheShlLmJ1ZmZlciwwLDEpWzBdfSgpKX1zdGF0aWMgZ2V0IGlzRXZhbFN1cHBvcnRlZCgpe3JldHVybiBzaGFkb3codGhpcywiaXNFdmFsU3VwcG9ydGVkIixmdW5jdGlvbiBpc0V2YWxTdXBwb3J0ZWQoKXt0cnl7bmV3IEZ1bmN0aW9uKCIiKTtyZXR1cm4hMH1jYXRjaHtyZXR1cm4hMX19KCkpfXN0YXRpYyBnZXQgaXNPZmZzY3JlZW5DYW52YXNTdXBwb3J0ZWQoKXtyZXR1cm4gc2hhZG93KHRoaXMsImlzT2Zmc2NyZWVuQ2FudmFzU3VwcG9ydGVkIiwidW5kZWZpbmVkIiE9dHlwZW9mIE9mZnNjcmVlbkNhbnZhcyl9c3RhdGljIGdldCBpc0ltYWdlRGVjb2RlclN1cHBvcnRlZCgpe3JldHVybiBzaGFkb3codGhpcywiaXNJbWFnZURlY29kZXJTdXBwb3J0ZWQiLCJ1bmRlZmluZWQiIT10eXBlb2YgSW1hZ2VEZWNvZGVyKX1zdGF0aWMgZ2V0IHBsYXRmb3JtKCl7Y29uc3R7cGxhdGZvcm06ZSx1c2VyQWdlbnQ6dH09bmF2aWdhdG9yO3JldHVybiBzaGFkb3codGhpcywicGxhdGZvcm0iLHtpc0FuZHJvaWQ6dC5pbmNsdWRlcygiQW5kcm9pZCIpLGlzTGludXg6ZS5pbmNsdWRlcygiTGludXgiKSxpc01hYzplLmluY2x1ZGVzKCJNYWMiKSxpc1dpbmRvd3M6ZS5pbmNsdWRlcygiV2luIiksaXNGaXJlZm94OnQuaW5jbHVkZXMoIkZpcmVmb3giKX0pfXN0YXRpYyBnZXQgaXNDU1NSb3VuZFN1cHBvcnRlZCgpe3JldHVybiBzaGFkb3codGhpcywiaXNDU1NSb3VuZFN1cHBvcnRlZCIsZ2xvYmFsVGhpcy5DU1M/LnN1cHBvcnRzPy4oIndpZHRoOiByb3VuZCgxLjVweCwgMXB4KSIpKX19Y29uc3QgZ2E9QXJyYXkuZnJvbShBcnJheSgyNTYpLmtleXMoKSwoZT0+ZS50b1N0cmluZygxNikucGFkU3RhcnQoMiwiMCIpKSk7Y2xhc3MgVXRpbHtzdGF0aWMgbWFrZUhleENvbG9yKGUsdCxhKXtyZXR1cm5gIyR7Z2FbZV19JHtnYVt0XX0ke2dhW2FdfWB9c3RhdGljIHNjYWxlTWluTWF4KGUsdCl7bGV0IGE7aWYoZVswXSl7aWYoZVswXTwwKXthPXRbMF07dFswXT10WzJdO3RbMl09YX10WzBdKj1lWzBdO3RbMl0qPWVbMF07aWYoZVszXTwwKXthPXRbMV07dFsxXT10WzNdO3RbM109YX10WzFdKj1lWzNdO3RbM10qPWVbM119ZWxzZXthPXRbMF07dFswXT10WzFdO3RbMV09YTthPXRbMl07dFsyXT10WzNdO3RbM109YTtpZihlWzFdPDApe2E9dFsxXTt0WzFdPXRbM107dFszXT1hfXRbMV0qPWVbMV07dFszXSo9ZVsxXTtpZihlWzJdPDApe2E9dFswXTt0WzBdPXRbMl07dFsyXT1hfXRbMF0qPWVbMl07dFsyXSo9ZVsyXX10WzBdKz1lWzRdO3RbMV0rPWVbNV07dFsyXSs9ZVs0XTt0WzNdKz1lWzVdfXN0YXRpYyB0cmFuc2Zvcm0oZSx0KXtyZXR1cm5bZVswXSp0WzBdK2VbMl0qdFsxXSxlWzFdKnRbMF0rZVszXSp0WzFdLGVbMF0qdFsyXStlWzJdKnRbM10sZVsxXSp0WzJdK2VbM10qdFszXSxlWzBdKnRbNF0rZVsyXSp0WzVdK2VbNF0sZVsxXSp0WzRdK2VbM10qdFs1XStlWzVdXX1zdGF0aWMgYXBwbHlUcmFuc2Zvcm0oZSx0LGE9MCl7Y29uc3Qgcj1lW2FdLGk9ZVthKzFdO2VbYV09cip0WzBdK2kqdFsyXSt0WzRdO2VbYSsxXT1yKnRbMV0raSp0WzNdK3RbNV19c3RhdGljIGFwcGx5VHJhbnNmb3JtVG9CZXppZXIoZSx0LGE9MCl7Y29uc3Qgcj10WzBdLGk9dFsxXSxuPXRbMl0scz10WzNdLG89dFs0XSxjPXRbNV07Zm9yKGxldCB0PTA7dDw2O3QrPTIpe2NvbnN0IGw9ZVthK3RdLGg9ZVthK3QrMV07ZVthK3RdPWwqcitoKm4rbztlW2ErdCsxXT1sKmkraCpzK2N9fXN0YXRpYyBhcHBseUludmVyc2VUcmFuc2Zvcm0oZSx0KXtjb25zdCBhPWVbMF0scj1lWzFdLGk9dFswXSp0WzNdLXRbMV0qdFsyXTtlWzBdPShhKnRbM10tcip0WzJdK3RbMl0qdFs1XS10WzRdKnRbM10pL2k7ZVsxXT0oLWEqdFsxXStyKnRbMF0rdFs0XSp0WzFdLXRbNV0qdFswXSkvaX1zdGF0aWMgYXhpYWxBbGlnbmVkQm91bmRpbmdCb3goZSx0LGEpe2NvbnN0IHI9dFswXSxpPXRbMV0sbj10WzJdLHM9dFszXSxvPXRbNF0sYz10WzVdLGw9ZVswXSxoPWVbMV0sdT1lWzJdLGQ9ZVszXTtsZXQgZj1yKmwrbyxnPWYscD1yKnUrbyxtPXAsYj1zKmgrYyx5PWIsdz1zKmQrYyxTPXc7aWYoMCE9PWl8fDAhPT1uKXtjb25zdCBlPWkqbCx0PWkqdSxhPW4qaCxyPW4qZDtmKz1hO20rPWE7cCs9cjtnKz1yO2IrPWU7Uys9ZTt3Kz10O3krPXR9YVswXT1NYXRoLm1pbihhWzBdLGYscCxnLG0pO2FbMV09TWF0aC5taW4oYVsxXSxiLHcseSxTKTthWzJdPU1hdGgubWF4KGFbMl0sZixwLGcsbSk7YVszXT1NYXRoLm1heChhWzNdLGIsdyx5LFMpfXN0YXRpYyBpbnZlcnNlVHJhbnNmb3JtKGUpe2NvbnN0IHQ9ZVswXSplWzNdLWVbMV0qZVsyXTtyZXR1cm5bZVszXS90LC1lWzFdL3QsLWVbMl0vdCxlWzBdL3QsKGVbMl0qZVs1XS1lWzRdKmVbM10pL3QsKGVbNF0qZVsxXS1lWzVdKmVbMF0pL3RdfXN0YXRpYyBzaW5ndWxhclZhbHVlRGVjb21wb3NlMmRTY2FsZShlLHQpe2NvbnN0IGE9ZVswXSxyPWVbMV0saT1lWzJdLG49ZVszXSxzPWEqKjIrcioqMixvPWEqaStyKm4sYz1pKioyK24qKjIsbD0ocytjKS8yLGg9TWF0aC5zcXJ0KGwqKjItKHMqYy1vKioyKSk7dFswXT1NYXRoLnNxcnQobCtofHwxKTt0WzFdPU1hdGguc3FydChsLWh8fDEpfXN0YXRpYyBub3JtYWxpemVSZWN0KGUpe2NvbnN0IHQ9ZS5zbGljZSgwKTtpZihlWzBdPmVbMl0pe3RbMF09ZVsyXTt0WzJdPWVbMF19aWYoZVsxXT5lWzNdKXt0WzFdPWVbM107dFszXT1lWzFdfXJldHVybiB0fXN0YXRpYyBpbnRlcnNlY3QoZSx0KXtjb25zdCBhPU1hdGgubWF4KE1hdGgubWluKGVbMF0sZVsyXSksTWF0aC5taW4odFswXSx0WzJdKSkscj1NYXRoLm1pbihNYXRoLm1heChlWzBdLGVbMl0pLE1hdGgubWF4KHRbMF0sdFsyXSkpO2lmKGE+cilyZXR1cm4gbnVsbDtjb25zdCBpPU1hdGgubWF4KE1hdGgubWluKGVbMV0sZVszXSksTWF0aC5taW4odFsxXSx0WzNdKSksbj1NYXRoLm1pbihNYXRoLm1heChlWzFdLGVbM10pLE1hdGgubWF4KHRbMV0sdFszXSkpO3JldHVybiBpPm4/bnVsbDpbYSxpLHIsbl19c3RhdGljIHBvaW50Qm91bmRpbmdCb3goZSx0LGEpe2FbMF09TWF0aC5taW4oYVswXSxlKTthWzFdPU1hdGgubWluKGFbMV0sdCk7YVsyXT1NYXRoLm1heChhWzJdLGUpO2FbM109TWF0aC5tYXgoYVszXSx0KX1zdGF0aWMgcmVjdEJvdW5kaW5nQm94KGUsdCxhLHIsaSl7aVswXT1NYXRoLm1pbihpWzBdLGUsYSk7aVsxXT1NYXRoLm1pbihpWzFdLHQscik7aVsyXT1NYXRoLm1heChpWzJdLGUsYSk7aVszXT1NYXRoLm1heChpWzNdLHQscil9c3RhdGljI2UoZSx0LGEscixpLG4scyxvLGMsbCl7aWYoYzw9MHx8Yz49MSlyZXR1cm47Y29uc3QgaD0xLWMsdT1jKmMsZD11KmMsZj1oKihoKihoKmUrMypjKnQpKzMqdSphKStkKnIsZz1oKihoKihoKmkrMypjKm4pKzMqdSpzKStkKm87bFswXT1NYXRoLm1pbihsWzBdLGYpO2xbMV09TWF0aC5taW4obFsxXSxnKTtsWzJdPU1hdGgubWF4KGxbMl0sZik7bFszXT1NYXRoLm1heChsWzNdLGcpfXN0YXRpYyN0KGUsdCxhLHIsaSxuLHMsbyxjLGwsaCx1KXtpZihNYXRoLmFicyhjKTwxZS0xMil7TWF0aC5hYnMobCk+PTFlLTEyJiZ0aGlzLiNlKGUsdCxhLHIsaSxuLHMsbywtaC9sLHUpO3JldHVybn1jb25zdCBkPWwqKjItNCpoKmM7aWYoZDwwKXJldHVybjtjb25zdCBmPU1hdGguc3FydChkKSxnPTIqYzt0aGlzLiNlKGUsdCxhLHIsaSxuLHMsbywoLWwrZikvZyx1KTt0aGlzLiNlKGUsdCxhLHIsaSxuLHMsbywoLWwtZikvZyx1KX1zdGF0aWMgYmV6aWVyQm91bmRpbmdCb3goZSx0LGEscixpLG4scyxvLGMpe2NbMF09TWF0aC5taW4oY1swXSxlLHMpO2NbMV09TWF0aC5taW4oY1sxXSx0LG8pO2NbMl09TWF0aC5tYXgoY1syXSxlLHMpO2NbM109TWF0aC5tYXgoY1szXSx0LG8pO3RoaXMuI3QoZSxhLGkscyx0LHIsbixvLDMqKDMqKGEtaSktZStzKSw2KihlLTIqYStpKSwzKihhLWUpLGMpO3RoaXMuI3QoZSxhLGkscyx0LHIsbixvLDMqKDMqKHItbiktdCtvKSw2Kih0LTIqcituKSwzKihyLXQpLGMpfX1jb25zdCBwYT1bMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsNzI4LDcxMSw3MTAsNzI5LDczMyw3MzEsNzMwLDczMiwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCw4MjI2LDgyMjQsODIyNSw4MjMwLDgyMTIsODIxMSw0MDIsODI2MCw4MjQ5LDgyNTAsODcyMiw4MjQwLDgyMjIsODIyMCw4MjIxLDgyMTYsODIxNyw4MjE4LDg0ODIsNjQyNTcsNjQyNTgsMzIxLDMzOCwzNTIsMzc2LDM4MSwzMDUsMzIyLDMzOSwzNTMsMzgyLDAsODM2NF07ZnVuY3Rpb24gc3RyaW5nVG9QREZTdHJpbmcoZSx0PSExKXtpZihlWzBdPj0iw68iKXtsZXQgYTtpZigiw74iPT09ZVswXSYmIsO/Ij09PWVbMV0pe2E9InV0Zi0xNmJlIjtlLmxlbmd0aCUyPT0xJiYoZT1lLnNsaWNlKDAsLTEpKX1lbHNlIGlmKCLDvyI9PT1lWzBdJiYiw74iPT09ZVsxXSl7YT0idXRmLTE2bGUiO2UubGVuZ3RoJTI9PTEmJihlPWUuc2xpY2UoMCwtMSkpfWVsc2Uiw68iPT09ZVswXSYmIsK7Ij09PWVbMV0mJiLCvyI9PT1lWzJdJiYoYT0idXRmLTgiKTtpZihhKXRyeXtjb25zdCByPW5ldyBUZXh0RGVjb2RlcihhLHtmYXRhbDohMH0pLGk9c3RyaW5nVG9CeXRlcyhlKSxuPXIuZGVjb2RlKGkpO3JldHVybiB0fHwhbi5pbmNsdWRlcygiGyIpP246bi5yZXBsYWNlQWxsKC9ceDFiW15ceDFiXSooPzpceDFifCQpL2csIiIpfWNhdGNoKGUpe3dhcm4oYHN0cmluZ1RvUERGU3RyaW5nOiAiJHtlfSIuYCl9fWNvbnN0IGE9W107Zm9yKGxldCByPTAsaT1lLmxlbmd0aDtyPGk7cisrKXtjb25zdCBuPWUuY2hhckNvZGVBdChyKTtpZighdCYmMjc9PT1uKXtmb3IoOysrcjxpJiYyNyE9PWUuY2hhckNvZGVBdChyKTspO2NvbnRpbnVlfWNvbnN0IHM9cGFbbl07YS5wdXNoKHM/U3RyaW5nLmZyb21DaGFyQ29kZShzKTplLmNoYXJBdChyKSl9cmV0dXJuIGEuam9pbigiIil9ZnVuY3Rpb24gc3RyaW5nVG9VVEY4U3RyaW5nKGUpe3JldHVybiBkZWNvZGVVUklDb21wb25lbnQoZXNjYXBlKGUpKX1mdW5jdGlvbiB1dGY4U3RyaW5nVG9TdHJpbmcoZSl7cmV0dXJuIHVuZXNjYXBlKGVuY29kZVVSSUNvbXBvbmVudChlKSl9ZnVuY3Rpb24gaXNBcnJheUVxdWFsKGUsdCl7aWYoZS5sZW5ndGghPT10Lmxlbmd0aClyZXR1cm4hMTtmb3IobGV0IGE9MCxyPWUubGVuZ3RoO2E8cjthKyspaWYoZVthXSE9PXRbYV0pcmV0dXJuITE7cmV0dXJuITB9ZnVuY3Rpb24gZ2V0TW9kaWZpY2F0aW9uRGF0ZShlPW5ldyBEYXRlKXtyZXR1cm5bZS5nZXRVVENGdWxsWWVhcigpLnRvU3RyaW5nKCksKGUuZ2V0VVRDTW9udGgoKSsxKS50b1N0cmluZygpLnBhZFN0YXJ0KDIsIjAiKSxlLmdldFVUQ0RhdGUoKS50b1N0cmluZygpLnBhZFN0YXJ0KDIsIjAiKSxlLmdldFVUQ0hvdXJzKCkudG9TdHJpbmcoKS5wYWRTdGFydCgyLCIwIiksZS5nZXRVVENNaW51dGVzKCkudG9TdHJpbmcoKS5wYWRTdGFydCgyLCIwIiksZS5nZXRVVENTZWNvbmRzKCkudG9TdHJpbmcoKS5wYWRTdGFydCgyLCIwIildLmpvaW4oIiIpfWxldCBtYT1udWxsLGJhPW51bGw7ZnVuY3Rpb24gTWF0aENsYW1wKGUsdCxhKXtyZXR1cm4gTWF0aC5taW4oTWF0aC5tYXgoZSx0KSxhKX1mdW5jdGlvbiB0b0hleFV0aWwoZSl7cmV0dXJuIFVpbnQ4QXJyYXkucHJvdG90eXBlLnRvSGV4P2UudG9IZXgoKTpBcnJheS5mcm9tKGUsKGU9PmdhW2VdKSkuam9pbigiIil9ImZ1bmN0aW9uIiE9dHlwZW9mIFByb21pc2UudHJ5JiYoUHJvbWlzZS50cnk9ZnVuY3Rpb24oZSwuLi50KXtyZXR1cm4gbmV3IFByb21pc2UoKGE9PnthKGUoLi4udCkpfSkpfSk7ImZ1bmN0aW9uIiE9dHlwZW9mIE1hdGguc3VtUHJlY2lzZSYmKE1hdGguc3VtUHJlY2lzZT1mdW5jdGlvbihlKXtyZXR1cm4gZS5yZWR1Y2UoKChlLHQpPT5lK3QpLDApfSk7Y29uc3QgeWE9U3ltYm9sKCJDSVJDVUxBUl9SRUYiKSx3YT1TeW1ib2woIkVPRiIpO2xldCBTYT1PYmplY3QuY3JlYXRlKG51bGwpLHhhPU9iamVjdC5jcmVhdGUobnVsbCksQWE9T2JqZWN0LmNyZWF0ZShudWxsKTtjbGFzcyBOYW1le2NvbnN0cnVjdG9yKGUpe3RoaXMubmFtZT1lfXN0YXRpYyBnZXQoZSl7cmV0dXJuIHhhW2VdfHw9bmV3IE5hbWUoZSl9fWNsYXNzIENtZHtjb25zdHJ1Y3RvcihlKXt0aGlzLmNtZD1lfXN0YXRpYyBnZXQoZSl7cmV0dXJuIFNhW2VdfHw9bmV3IENtZChlKX19Y29uc3Qga2E9ZnVuY3Rpb24gbm9uU2VyaWFsaXphYmxlQ2xvc3VyZSgpe3JldHVybiBrYX07Y2xhc3MgRGljdHtjb25zdHJ1Y3RvcihlPW51bGwpe3RoaXMuX21hcD1uZXcgTWFwO3RoaXMueHJlZj1lO3RoaXMub2JqSWQ9bnVsbDt0aGlzLnN1cHByZXNzRW5jcnlwdGlvbj0hMTt0aGlzLl9fbm9uU2VyaWFsaXphYmxlX189a2F9YXNzaWduWHJlZihlKXt0aGlzLnhyZWY9ZX1nZXQgc2l6ZSgpe3JldHVybiB0aGlzLl9tYXAuc2l6ZX1nZXQoZSx0LGEpe2xldCByPXRoaXMuX21hcC5nZXQoZSk7aWYodm9pZCAwPT09ciYmdm9pZCAwIT09dCl7cj10aGlzLl9tYXAuZ2V0KHQpO3ZvaWQgMD09PXImJnZvaWQgMCE9PWEmJihyPXRoaXMuX21hcC5nZXQoYSkpfXJldHVybiByIGluc3RhbmNlb2YgUmVmJiZ0aGlzLnhyZWY/dGhpcy54cmVmLmZldGNoKHIsdGhpcy5zdXBwcmVzc0VuY3J5cHRpb24pOnJ9YXN5bmMgZ2V0QXN5bmMoZSx0LGEpe2xldCByPXRoaXMuX21hcC5nZXQoZSk7aWYodm9pZCAwPT09ciYmdm9pZCAwIT09dCl7cj10aGlzLl9tYXAuZ2V0KHQpO3ZvaWQgMD09PXImJnZvaWQgMCE9PWEmJihyPXRoaXMuX21hcC5nZXQoYSkpfXJldHVybiByIGluc3RhbmNlb2YgUmVmJiZ0aGlzLnhyZWY/dGhpcy54cmVmLmZldGNoQXN5bmMocix0aGlzLnN1cHByZXNzRW5jcnlwdGlvbik6cn1nZXRBcnJheShlLHQsYSl7bGV0IHI9dGhpcy5fbWFwLmdldChlKTtpZih2b2lkIDA9PT1yJiZ2b2lkIDAhPT10KXtyPXRoaXMuX21hcC5nZXQodCk7dm9pZCAwPT09ciYmdm9pZCAwIT09YSYmKHI9dGhpcy5fbWFwLmdldChhKSl9ciBpbnN0YW5jZW9mIFJlZiYmdGhpcy54cmVmJiYocj10aGlzLnhyZWYuZmV0Y2gocix0aGlzLnN1cHByZXNzRW5jcnlwdGlvbikpO2lmKEFycmF5LmlzQXJyYXkocikpe3I9ci5zbGljZSgpO2ZvcihsZXQgZT0wLHQ9ci5sZW5ndGg7ZTx0O2UrKylyW2VdaW5zdGFuY2VvZiBSZWYmJnRoaXMueHJlZiYmKHJbZV09dGhpcy54cmVmLmZldGNoKHJbZV0sdGhpcy5zdXBwcmVzc0VuY3J5cHRpb24pKX1yZXR1cm4gcn1nZXRSYXcoZSl7cmV0dXJuIHRoaXMuX21hcC5nZXQoZSl9Z2V0S2V5cygpe3JldHVyblsuLi50aGlzLl9tYXAua2V5cygpXX1nZXRSYXdWYWx1ZXMoKXtyZXR1cm5bLi4udGhpcy5fbWFwLnZhbHVlcygpXX1zZXQoZSx0KXt0aGlzLl9tYXAuc2V0KGUsdCl9aGFzKGUpe3JldHVybiB0aGlzLl9tYXAuaGFzKGUpfSpbU3ltYm9sLml0ZXJhdG9yXSgpe2Zvcihjb25zdFtlLHRdb2YgdGhpcy5fbWFwKXlpZWxkW2UsdCBpbnN0YW5jZW9mIFJlZiYmdGhpcy54cmVmP3RoaXMueHJlZi5mZXRjaCh0LHRoaXMuc3VwcHJlc3NFbmNyeXB0aW9uKTp0XX1zdGF0aWMgZ2V0IGVtcHR5KCl7Y29uc3QgZT1uZXcgRGljdChudWxsKTtlLnNldD0oZSx0KT0+e3VucmVhY2hhYmxlKCJTaG91bGQgbm90IGNhbGwgYHNldGAgb24gdGhlIGVtcHR5IGRpY3Rpb25hcnkuIil9O3JldHVybiBzaGFkb3codGhpcywiZW1wdHkiLGUpfXN0YXRpYyBtZXJnZSh7eHJlZjplLGRpY3RBcnJheTp0LG1lcmdlU3ViRGljdHM6YT0hMX0pe2NvbnN0IHI9bmV3IERpY3QoZSksaT1uZXcgTWFwO2Zvcihjb25zdCBlIG9mIHQpaWYoZSBpbnN0YW5jZW9mIERpY3QpZm9yKGNvbnN0W3Qscl1vZiBlLl9tYXApe2xldCBlPWkuZ2V0KHQpO2lmKHZvaWQgMD09PWUpe2U9W107aS5zZXQodCxlKX1lbHNlIGlmKCEoYSYmciBpbnN0YW5jZW9mIERpY3QpKWNvbnRpbnVlO2UucHVzaChyKX1mb3IoY29uc3RbdCxhXW9mIGkpe2lmKDE9PT1hLmxlbmd0aHx8IShhWzBdaW5zdGFuY2VvZiBEaWN0KSl7ci5fbWFwLnNldCh0LGFbMF0pO2NvbnRpbnVlfWNvbnN0IGk9bmV3IERpY3QoZSk7Zm9yKGNvbnN0IGUgb2YgYSlmb3IoY29uc3RbdCxhXW9mIGUuX21hcClpLl9tYXAuaGFzKHQpfHxpLl9tYXAuc2V0KHQsYSk7aS5zaXplPjAmJnIuX21hcC5zZXQodCxpKX1pLmNsZWFyKCk7cmV0dXJuIHIuc2l6ZT4wP3I6RGljdC5lbXB0eX1jbG9uZSgpe2NvbnN0IGU9bmV3IERpY3QodGhpcy54cmVmKTtmb3IoY29uc3QgdCBvZiB0aGlzLmdldEtleXMoKSllLnNldCh0LHRoaXMuZ2V0UmF3KHQpKTtyZXR1cm4gZX1kZWxldGUoZSl7ZGVsZXRlIHRoaXMuX21hcFtlXX19Y2xhc3MgUmVme2NvbnN0cnVjdG9yKGUsdCl7dGhpcy5udW09ZTt0aGlzLmdlbj10fXRvU3RyaW5nKCl7cmV0dXJuIDA9PT10aGlzLmdlbj9gJHt0aGlzLm51bX1SYDpgJHt0aGlzLm51bX1SJHt0aGlzLmdlbn1gfXN0YXRpYyBmcm9tU3RyaW5nKGUpe2NvbnN0IHQ9QWFbZV07aWYodClyZXR1cm4gdDtjb25zdCBhPS9eKFxkKylSKFxkKikkLy5leGVjKGUpO3JldHVybiBhJiYiMCIhPT1hWzFdP0FhW2VdPW5ldyBSZWYocGFyc2VJbnQoYVsxXSksYVsyXT9wYXJzZUludChhWzJdKTowKTpudWxsfXN0YXRpYyBnZXQoZSx0KXtjb25zdCBhPTA9PT10P2Ake2V9UmA6YCR7ZX1SJHt0fWA7cmV0dXJuIEFhW2FdfHw9bmV3IFJlZihlLHQpfX1jbGFzcyBSZWZTZXR7Y29uc3RydWN0b3IoZT1udWxsKXt0aGlzLl9zZXQ9bmV3IFNldChlPy5fc2V0KX1oYXMoZSl7cmV0dXJuIHRoaXMuX3NldC5oYXMoZS50b1N0cmluZygpKX1wdXQoZSl7dGhpcy5fc2V0LmFkZChlLnRvU3RyaW5nKCkpfXJlbW92ZShlKXt0aGlzLl9zZXQuZGVsZXRlKGUudG9TdHJpbmcoKSl9W1N5bWJvbC5pdGVyYXRvcl0oKXtyZXR1cm4gdGhpcy5fc2V0LnZhbHVlcygpfWNsZWFyKCl7dGhpcy5fc2V0LmNsZWFyKCl9fWNsYXNzIFJlZlNldENhY2hle2NvbnN0cnVjdG9yKCl7dGhpcy5fbWFwPW5ldyBNYXB9Z2V0IHNpemUoKXtyZXR1cm4gdGhpcy5fbWFwLnNpemV9Z2V0KGUpe3JldHVybiB0aGlzLl9tYXAuZ2V0KGUudG9TdHJpbmcoKSl9aGFzKGUpe3JldHVybiB0aGlzLl9tYXAuaGFzKGUudG9TdHJpbmcoKSl9cHV0KGUsdCl7dGhpcy5fbWFwLnNldChlLnRvU3RyaW5nKCksdCl9cHV0QWxpYXMoZSx0KXt0aGlzLl9tYXAuc2V0KGUudG9TdHJpbmcoKSx0aGlzLmdldCh0KSl9W1N5bWJvbC5pdGVyYXRvcl0oKXtyZXR1cm4gdGhpcy5fbWFwLnZhbHVlcygpfWNsZWFyKCl7dGhpcy5fbWFwLmNsZWFyKCl9KnZhbHVlcygpe3lpZWxkKnRoaXMuX21hcC52YWx1ZXMoKX0qaXRlbXMoKXtmb3IoY29uc3RbZSx0XW9mIHRoaXMuX21hcCl5aWVsZFtSZWYuZnJvbVN0cmluZyhlKSx0XX19ZnVuY3Rpb24gaXNOYW1lKGUsdCl7cmV0dXJuIGUgaW5zdGFuY2VvZiBOYW1lJiYodm9pZCAwPT09dHx8ZS5uYW1lPT09dCl9ZnVuY3Rpb24gaXNDbWQoZSx0KXtyZXR1cm4gZSBpbnN0YW5jZW9mIENtZCYmKHZvaWQgMD09PXR8fGUuY21kPT09dCl9ZnVuY3Rpb24gaXNEaWN0KGUsdCl7cmV0dXJuIGUgaW5zdGFuY2VvZiBEaWN0JiYodm9pZCAwPT09dHx8aXNOYW1lKGUuZ2V0KCJUeXBlIiksdCkpfWZ1bmN0aW9uIGlzUmVmc0VxdWFsKGUsdCl7cmV0dXJuIGUubnVtPT09dC5udW0mJmUuZ2VuPT09dC5nZW59Y2xhc3MgQmFzZVN0cmVhbXtnZXQgbGVuZ3RoKCl7dW5yZWFjaGFibGUoIkFic3RyYWN0IGdldHRlciBgbGVuZ3RoYCBhY2Nlc3NlZCIpfWdldCBpc0VtcHR5KCl7dW5yZWFjaGFibGUoIkFic3RyYWN0IGdldHRlciBgaXNFbXB0eWAgYWNjZXNzZWQiKX1nZXQgaXNEYXRhTG9hZGVkKCl7cmV0dXJuIHNoYWRvdyh0aGlzLCJpc0RhdGFMb2FkZWQiLCEwKX1nZXRCeXRlKCl7dW5yZWFjaGFibGUoIkFic3RyYWN0IG1ldGhvZCBgZ2V0Qnl0ZWAgY2FsbGVkIil9Z2V0Qnl0ZXMoZSl7dW5yZWFjaGFibGUoIkFic3RyYWN0IG1ldGhvZCBgZ2V0Qnl0ZXNgIGNhbGxlZCIpfWFzeW5jIGdldEltYWdlRGF0YShlLHQpe3JldHVybiB0aGlzLmdldEJ5dGVzKGUsdCl9YXN5bmMgYXN5bmNHZXRCeXRlcygpe3VucmVhY2hhYmxlKCJBYnN0cmFjdCBtZXRob2QgYGFzeW5jR2V0Qnl0ZXNgIGNhbGxlZCIpfWdldCBpc0FzeW5jKCl7cmV0dXJuITF9Z2V0IGlzQXN5bmNEZWNvZGVyKCl7cmV0dXJuITF9Z2V0IGNhbkFzeW5jRGVjb2RlSW1hZ2VGcm9tQnVmZmVyKCl7cmV0dXJuITF9YXN5bmMgZ2V0VHJhbnNmZXJhYmxlSW1hZ2UoKXtyZXR1cm4gbnVsbH1wZWVrQnl0ZSgpe2NvbnN0IGU9dGhpcy5nZXRCeXRlKCk7LTEhPT1lJiZ0aGlzLnBvcy0tO3JldHVybiBlfXBlZWtCeXRlcyhlKXtjb25zdCB0PXRoaXMuZ2V0Qnl0ZXMoZSk7dGhpcy5wb3MtPXQubGVuZ3RoO3JldHVybiB0fWdldFVpbnQxNigpe2NvbnN0IGU9dGhpcy5nZXRCeXRlKCksdD10aGlzLmdldEJ5dGUoKTtyZXR1cm4tMT09PWV8fC0xPT09dD8tMTooZTw8OCkrdH1nZXRJbnQzMigpe3JldHVybih0aGlzLmdldEJ5dGUoKTw8MjQpKyh0aGlzLmdldEJ5dGUoKTw8MTYpKyh0aGlzLmdldEJ5dGUoKTw8OCkrdGhpcy5nZXRCeXRlKCl9Z2V0Qnl0ZVJhbmdlKGUsdCl7dW5yZWFjaGFibGUoIkFic3RyYWN0IG1ldGhvZCBgZ2V0Qnl0ZVJhbmdlYCBjYWxsZWQiKX1nZXRTdHJpbmcoZSl7cmV0dXJuIGJ5dGVzVG9TdHJpbmcodGhpcy5nZXRCeXRlcyhlKSl9c2tpcChlKXt0aGlzLnBvcys9ZXx8MX1yZXNldCgpe3VucmVhY2hhYmxlKCJBYnN0cmFjdCBtZXRob2QgYHJlc2V0YCBjYWxsZWQiKX1tb3ZlU3RhcnQoKXt1bnJlYWNoYWJsZSgiQWJzdHJhY3QgbWV0aG9kIGBtb3ZlU3RhcnRgIGNhbGxlZCIpfW1ha2VTdWJTdHJlYW0oZSx0LGE9bnVsbCl7dW5yZWFjaGFibGUoIkFic3RyYWN0IG1ldGhvZCBgbWFrZVN1YlN0cmVhbWAgY2FsbGVkIil9Z2V0QmFzZVN0cmVhbXMoKXtyZXR1cm4gbnVsbH19Y29uc3QgQ2E9L15bMS05XVwuXGQkLyx2YT0yKiozMS0xLEZhPVsxLDAsMCwxLDAsMF0sSWE9WyJDb2xvclNwYWNlIiwiRXh0R1N0YXRlIiwiRm9udCIsIlBhdHRlcm4iLCJQcm9wZXJ0aWVzIiwiU2hhZGluZyIsIlhPYmplY3QiXSxPYT1bIkV4dEdTdGF0ZSIsIkZvbnQiLCJQcm9wZXJ0aWVzIiwiWE9iamVjdCJdO2Z1bmN0aW9uIGdldExvb2t1cFRhYmxlRmFjdG9yeShlKXtsZXQgdDtyZXR1cm4gZnVuY3Rpb24oKXtpZihlKXt0PU9iamVjdC5jcmVhdGUobnVsbCk7ZSh0KTtlPW51bGx9cmV0dXJuIHR9fWNsYXNzIE1pc3NpbmdEYXRhRXhjZXB0aW9uIGV4dGVuZHMgZmF7Y29uc3RydWN0b3IoZSx0KXtzdXBlcihgTWlzc2luZyBkYXRhIFske2V9LCAke3R9KWAsIk1pc3NpbmdEYXRhRXhjZXB0aW9uIik7dGhpcy5iZWdpbj1lO3RoaXMuZW5kPXR9fWNsYXNzIFBhcnNlckVPRkV4Y2VwdGlvbiBleHRlbmRzIGZhe2NvbnN0cnVjdG9yKGUpe3N1cGVyKGUsIlBhcnNlckVPRkV4Y2VwdGlvbiIpfX1jbGFzcyBYUmVmRW50cnlFeGNlcHRpb24gZXh0ZW5kcyBmYXtjb25zdHJ1Y3RvcihlKXtzdXBlcihlLCJYUmVmRW50cnlFeGNlcHRpb24iKX19Y2xhc3MgWFJlZlBhcnNlRXhjZXB0aW9uIGV4dGVuZHMgZmF7Y29uc3RydWN0b3IoZSl7c3VwZXIoZSwiWFJlZlBhcnNlRXhjZXB0aW9uIil9fWZ1bmN0aW9uIGFycmF5QnVmZmVyc1RvQnl0ZXMoZSl7Y29uc3QgdD1lLmxlbmd0aDtpZigwPT09dClyZXR1cm4gbmV3IFVpbnQ4QXJyYXkoMCk7aWYoMT09PXQpcmV0dXJuIG5ldyBVaW50OEFycmF5KGVbMF0pO2xldCBhPTA7Zm9yKGxldCByPTA7cjx0O3IrKylhKz1lW3JdLmJ5dGVMZW5ndGg7Y29uc3Qgcj1uZXcgVWludDhBcnJheShhKTtsZXQgaT0wO2ZvcihsZXQgYT0wO2E8dDthKyspe2NvbnN0IHQ9bmV3IFVpbnQ4QXJyYXkoZVthXSk7ci5zZXQodCxpKTtpKz10LmJ5dGVMZW5ndGh9cmV0dXJuIHJ9YXN5bmMgZnVuY3Rpb24gZmV0Y2hCaW5hcnlEYXRhKGUpe2NvbnN0IHQ9YXdhaXQgZmV0Y2goZSk7aWYoIXQub2spdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZmV0Y2ggZmlsZSAiJHtlfSIgd2l0aCAiJHt0LnN0YXR1c1RleHR9Ii5gKTtyZXR1cm4gbmV3IFVpbnQ4QXJyYXkoYXdhaXQgdC5hcnJheUJ1ZmZlcigpKX1mdW5jdGlvbiBnZXRJbmhlcml0YWJsZVByb3BlcnR5KHtkaWN0OmUsa2V5OnQsZ2V0QXJyYXk6YT0hMSxzdG9wV2hlbkZvdW5kOnI9ITB9KXtsZXQgaTtjb25zdCBuPW5ldyBSZWZTZXQ7Zm9yKDtlIGluc3RhbmNlb2YgRGljdCYmKCFlLm9iaklkfHwhbi5oYXMoZS5vYmpJZCkpOyl7ZS5vYmpJZCYmbi5wdXQoZS5vYmpJZCk7Y29uc3Qgcz1hP2UuZ2V0QXJyYXkodCk6ZS5nZXQodCk7aWYodm9pZCAwIT09cyl7aWYocilyZXR1cm4gczsoaXx8PVtdKS5wdXNoKHMpfWU9ZS5nZXQoIlBhcmVudCIpfXJldHVybiBpfWNvbnN0IFRhPVsiIiwiQyIsIkNDIiwiQ0NDIiwiQ0QiLCJEIiwiREMiLCJEQ0MiLCJEQ0NDIiwiQ00iLCIiLCJYIiwiWFgiLCJYWFgiLCJYTCIsIkwiLCJMWCIsIkxYWCIsIkxYWFgiLCJYQyIsIiIsIkkiLCJJSSIsIklJSSIsIklWIiwiViIsIlZJIiwiVklJIiwiVklJSSIsIklYIl07ZnVuY3Rpb24gdG9Sb21hbk51bWVyYWxzKGUsdD0hMSl7YXNzZXJ0KE51bWJlci5pc0ludGVnZXIoZSkmJmU+MCwiVGhlIG51bWJlciBzaG91bGQgYmUgYSBwb3NpdGl2ZSBpbnRlZ2VyLiIpO2NvbnN0IGE9Ik0iLnJlcGVhdChlLzFlM3wwKStUYVtlJTFlMy8xMDB8MF0rVGFbMTArKGUlMTAwLzEwfDApXStUYVsyMCtlJTEwXTtyZXR1cm4gdD9hLnRvTG93ZXJDYXNlKCk6YX1mdW5jdGlvbiBsb2cyKGUpe3JldHVybiBlPjA/TWF0aC5jZWlsKE1hdGgubG9nMihlKSk6MH1mdW5jdGlvbiByZWFkSW50OChlLHQpe3JldHVybiBlW3RdPDwyND4+MjR9ZnVuY3Rpb24gcmVhZEludDE2KGUsdCl7cmV0dXJuKGVbdF08PDI0fGVbdCsxXTw8MTYpPj4xNn1mdW5jdGlvbiByZWFkVWludDE2KGUsdCl7cmV0dXJuIGVbdF08PDh8ZVt0KzFdfWZ1bmN0aW9uIHJlYWRVaW50MzIoZSx0KXtyZXR1cm4oZVt0XTw8MjR8ZVt0KzFdPDwxNnxlW3QrMl08PDh8ZVt0KzNdKT4+PjB9ZnVuY3Rpb24gaXNXaGl0ZVNwYWNlKGUpe3JldHVybiAzMj09PWV8fDk9PT1lfHwxMz09PWV8fDEwPT09ZX1mdW5jdGlvbiBpc051bWJlckFycmF5KGUsdCl7cmV0dXJuIEFycmF5LmlzQXJyYXkoZSk/KG51bGw9PT10fHxlLmxlbmd0aD09PXQpJiZlLmV2ZXJ5KChlPT4ibnVtYmVyIj09dHlwZW9mIGUpKTpBcnJheUJ1ZmZlci5pc1ZpZXcoZSkmJiEoZSBpbnN0YW5jZW9mIEJpZ0ludDY0QXJyYXl8fGUgaW5zdGFuY2VvZiBCaWdVaW50NjRBcnJheSkmJihudWxsPT09dHx8ZS5sZW5ndGg9PT10KX1mdW5jdGlvbiBsb29rdXBNYXRyaXgoZSx0KXtyZXR1cm4gaXNOdW1iZXJBcnJheShlLDYpP2U6dH1mdW5jdGlvbiBsb29rdXBSZWN0KGUsdCl7cmV0dXJuIGlzTnVtYmVyQXJyYXkoZSw0KT9lOnR9ZnVuY3Rpb24gbG9va3VwTm9ybWFsUmVjdChlLHQpe3JldHVybiBpc051bWJlckFycmF5KGUsNCk/VXRpbC5ub3JtYWxpemVSZWN0KGUpOnR9ZnVuY3Rpb24gcGFyc2VYRkFQYXRoKGUpe2NvbnN0IHQ9LyguKylcWyhcZCspXF0kLztyZXR1cm4gZS5zcGxpdCgiLiIpLm1hcCgoZT0+e2NvbnN0IGE9ZS5tYXRjaCh0KTtyZXR1cm4gYT97bmFtZTphWzFdLHBvczpwYXJzZUludChhWzJdLDEwKX06e25hbWU6ZSxwb3M6MH19KSl9ZnVuY3Rpb24gZXNjYXBlUERGTmFtZShlKXtjb25zdCB0PVtdO2xldCBhPTA7Zm9yKGxldCByPTAsaT1lLmxlbmd0aDtyPGk7cisrKXtjb25zdCBpPWUuY2hhckNvZGVBdChyKTtpZihpPDMzfHxpPjEyNnx8MzU9PT1pfHw0MD09PWl8fDQxPT09aXx8NjA9PT1pfHw2Mj09PWl8fDkxPT09aXx8OTM9PT1pfHwxMjM9PT1pfHwxMjU9PT1pfHw0Nz09PWl8fDM3PT09aSl7YTxyJiZ0LnB1c2goZS5zdWJzdHJpbmcoYSxyKSk7dC5wdXNoKGAjJHtpLnRvU3RyaW5nKDE2KX1gKTthPXIrMX19aWYoMD09PXQubGVuZ3RoKXJldHVybiBlO2E8ZS5sZW5ndGgmJnQucHVzaChlLnN1YnN0cmluZyhhLGUubGVuZ3RoKSk7cmV0dXJuIHQuam9pbigiIil9ZnVuY3Rpb24gZXNjYXBlU3RyaW5nKGUpe3JldHVybiBlLnJlcGxhY2VBbGwoLyhbKClcXFxuXHJdKS9nLChlPT4iXG4iPT09ZT8iXFxuIjoiXHIiPT09ZT8iXFxyIjpgXFwke2V9YCkpfWZ1bmN0aW9uIF9jb2xsZWN0SlMoZSx0LGEscil7aWYoIWUpcmV0dXJuO2xldCBpPW51bGw7aWYoZSBpbnN0YW5jZW9mIFJlZil7aWYoci5oYXMoZSkpcmV0dXJuO2k9ZTtyLnB1dChpKTtlPXQuZmV0Y2goZSl9aWYoQXJyYXkuaXNBcnJheShlKSlmb3IoY29uc3QgaSBvZiBlKV9jb2xsZWN0SlMoaSx0LGEscik7ZWxzZSBpZihlIGluc3RhbmNlb2YgRGljdCl7aWYoaXNOYW1lKGUuZ2V0KCJTIiksIkphdmFTY3JpcHQiKSl7Y29uc3QgdD1lLmdldCgiSlMiKTtsZXQgcjt0IGluc3RhbmNlb2YgQmFzZVN0cmVhbT9yPXQuZ2V0U3RyaW5nKCk6InN0cmluZyI9PXR5cGVvZiB0JiYocj10KTtyJiY9c3RyaW5nVG9QREZTdHJpbmcociwhMCkucmVwbGFjZUFsbCgiXDAiLCIiKTtyJiZhLnB1c2gocil9X2NvbGxlY3RKUyhlLmdldFJhdygiTmV4dCIpLHQsYSxy