From 30473b0359b60b7daf5fe659807c3d722f5b043f Mon Sep 17 00:00:00 2001 From: gronod <1+gronod@noreply@i3omb.com> Date: Fri, 28 Aug 2026 18:47:44 +0100 Subject: [PATCH 1/3] fix(stage3): prevent instlist device index from passing as chartread -c comm port (fixes #111) --- src/js/chartread.js | 491 +------------------------------------------- 1 file changed, 1 insertion(+), 490 deletions(-) diff --git a/src/js/chartread.js b/src/js/chartread.js index 7d3190e..7a172e2 100644 --- a/src/js/chartread.js +++ b/src/js/chartread.js @@ -1,490 +1 @@ -const { invoke } = window.__TAURI__.core; -const { listen } = window.__TAURI__.event; -import { startSwatchListener, stopSwatchListener } from './swatch_grid.js'; -import { setStage3Result } from './colprof.js'; -import { wizardState } from './state.js'; - -// Module-level state: set by Stage 2 when it completes -let stage2Basename = ""; -let stage2Cwd = ""; - -/** - * Called by printtarg.js after Stage 2 completes. - */ -export function setStage2Result(basename, cwd) { - stage2Basename = basename || wizardState.basename; - stage2Cwd = cwd || wizardState.cwd; - wizardState.setTarget(stage2Basename, stage2Cwd); -} - -// State machine states -const STATE = { - IDLE: "IDLE", - CALIBRATING: "CALIBRATING", - AWAITING_STRIP: "AWAITING_STRIP", - READING: "READING", - ERROR: "ERROR", - FINISHED: "FINISHED", -}; - -let currentState = STATE.IDLE; -let currentProcessId = ""; -let measurementInProgress = false; -let currentPassIndex = 0; -const recordedPasses = []; - -export function initChartread() { - const btnStartRead = document.getElementById("btnStartRead"); - const btnCalibrate = document.getElementById("btnCalibrate"); - const btnRetry = document.getElementById("btnRetry"); - const btnSkip = document.getElementById("btnSkip"); - const btnCancel = document.getElementById("btnCancel"); - const btnDetectInstruments = document.getElementById("btnDetectInstruments"); - const instrumentSelect = document.getElementById("chartreadInstrumentSelect"); - const promptText = document.getElementById("chartreadPrompt"); - const stateLabel = document.getElementById("chartreadState"); - const logContainer = document.getElementById("chartreadLogContainer"); - const logPre = document.getElementById("chartreadLog"); - const averagingPanel = document.getElementById("chartreadAveragingPanel"); - const passesList = document.getElementById("passesList"); - const passCounterBadge = document.getElementById("passCounterBadge"); - const btnMeasureAnotherSheet = document.getElementById("btnMeasureAnotherSheet"); - const btnFinishAndAverage = document.getElementById("btnFinishAndAverage"); - - function setMeasurementBusy(busy) { - measurementInProgress = busy; - if (btnStartRead) btnStartRead.disabled = busy; - if (btnMeasureAnotherSheet) btnMeasureAnotherSheet.disabled = busy; - if (btnFinishAndAverage) { - btnFinishAndAverage.disabled = busy || recordedPasses.length === 0; - } - } - - function renderPassesList() { - if (!passesList) return; - passesList.innerHTML = ""; - recordedPasses.forEach((pass, i) => { - const item = document.createElement("div"); - item.style.cssText = "display:flex; justify-content:space-between; align-items:center; background:rgba(255,255,255,0.05); padding:6px 10px; border-radius:4px; font-size:0.85rem;"; - const label = document.createElement("span"); - const strong = document.createElement("strong"); - strong.textContent = `Sheet Pass #${i + 1}`; - label.appendChild(strong); - label.appendChild(document.createTextNode(` (${pass.filename})`)); - const time = document.createElement("span"); - time.style.opacity = "0.7"; - time.textContent = `✓ ${pass.time}`; - item.appendChild(label); - item.appendChild(time); - passesList.appendChild(item); - }); - } - - function acceptStage3(basename, cwd) { - wizardState.setTarget(basename, cwd); - setStage3Result(basename, cwd); - wizardState.updateGating(); - advanceToStage4(); - } - - // Instrument detection logic - if (btnDetectInstruments && instrumentSelect) { - btnDetectInstruments.addEventListener("click", async () => { - btnDetectInstruments.disabled = true; - btnDetectInstruments.textContent = "Detecting..."; - setPrompt("Querying connected spectrophotometers and colorimeters via instlist..."); - - const detected = []; - - try { - const unlistenStdout = await listen("process:stdout", (event) => { - if (event.payload.id !== "instlist" || !event.payload.line) return; - const line = event.payload.line.trim(); - - // Matches Argyll instlist output e.g. "1: 'i1Pro' on 'USB'" or "1: 'ColorMunki'" or "1 = 'i1Display'" - const match = line.match(/^(\d+)[\s:=]+'?([^'\n]+)'?(?:\s+on\s+'?([^'\n]+)'?)?/i); - if (match) { - const index = match[1]; - const name = match[2].trim(); - const port = match[3] ? match[3].trim() : ""; - detected.push({ index, name, port }); - } - }); - - const unlistenExit = await listen("process:exit", (event) => { - if (event.payload.id !== "instlist") return; - unlistenStdout(); - unlistenExit(); - btnDetectInstruments.disabled = false; - btnDetectInstruments.textContent = "↻ Detect"; - - instrumentSelect.innerHTML = ``; - - if (detected.length > 0) { - detected.forEach((inst) => { - const opt = document.createElement("option"); - opt.value = inst.index; - opt.textContent = `${inst.index}: ${inst.name} ${inst.port ? `(${inst.port})` : ""}`; - instrumentSelect.appendChild(opt); - }); - instrumentSelect.value = detected[0].index; - setPrompt(`Found ${detected.length} instrument(s): ${detected.map(d => d.name).join(", ")}`); - } else { - setPrompt("No instruments found via instlist. Ensure USB cable is plugged in."); - } - }); - - await invoke("detect_instruments"); - } catch (err) { - console.error("detect_instruments error:", err); - btnDetectInstruments.disabled = false; - btnDetectInstruments.textContent = "↻ Detect"; - setPrompt(`Instrument detection error: ${err}`); - } - }); - } - - function setState(newState) { - currentState = newState; - if (stateLabel) stateLabel.textContent = newState; - - // Show/hide buttons based on state - btnCalibrate.classList.add("hidden"); - btnRetry.classList.add("hidden"); - btnSkip.classList.add("hidden"); - btnCancel.classList.add("hidden"); - btnStartRead.classList.add("hidden"); - - switch (newState) { - case STATE.IDLE: - btnStartRead.classList.remove("hidden"); - break; - case STATE.CALIBRATING: - btnCalibrate.classList.remove("hidden"); - btnCancel.classList.remove("hidden"); - break; - case STATE.AWAITING_STRIP: - btnRetry.classList.remove("hidden"); - btnSkip.classList.remove("hidden"); - btnCancel.classList.remove("hidden"); - break; - case STATE.READING: - btnCancel.classList.remove("hidden"); - break; - case STATE.ERROR: - btnRetry.classList.remove("hidden"); - btnSkip.classList.remove("hidden"); - btnCancel.classList.remove("hidden"); - break; - case STATE.FINISHED: - // After a recorded pass, further sheets go through "Measure Another Sheet" - if (recordedPasses.length === 0) { - btnStartRead.classList.remove("hidden"); - } - break; - } - } - - function setPrompt(text) { - if (promptText) promptText.textContent = text; - } - - // Start reading button - if (btnStartRead) { - btnStartRead.addEventListener("click", async () => { - if (measurementInProgress) { - setPrompt("A measurement is already running. Wait for it to finish."); - return; - } - - const basename = stage2Basename || wizardState.basename; - const cwd = stage2Cwd || wizardState.cwd; - - if (!basename || !cwd) { - setPrompt("Error: No .ti2 file available. Complete Stage 2 first."); - return; - } - - stage2Basename = basename; - stage2Cwd = cwd; - - logPre.textContent = ""; - logContainer.open = false; - logContainer.classList.remove("hidden"); - setMeasurementBusy(true); - setState(STATE.CALIBRATING); - setPrompt("Starting chartread... waiting for instrument calibration prompt."); - - const selectedPort = instrumentSelect && instrumentSelect.value ? instrumentSelect.value : null; - - const config = { - basename: basename, - cwd: cwd, - port: selectedPort, - }; - - currentProcessId = `chartread_${basename}`; - - // Start swatch grid listener - await startSwatchListener(currentProcessId); - - try { - const unlistenStdout = await listen("process:stdout", (event) => { - if (event.payload.id !== currentProcessId || !event.payload.line) return; - const line = event.payload.line; - - logPre.textContent += line + "\n"; - logPre.scrollTop = logPre.scrollHeight; - - // Parse prompts for state transitions - const lineLower = line.toLowerCase(); - - if (lineLower.includes("calibrat") && lineLower.includes("place")) { - setState(STATE.CALIBRATING); - setPrompt(line); - } else if (lineLower.includes("hit") && lineLower.includes("read") && lineLower.includes("strip")) { - setState(STATE.AWAITING_STRIP); - setPrompt(line); - } else if (lineLower.includes("ready to read")) { - setState(STATE.AWAITING_STRIP); - setPrompt(line); - } else if (lineLower.includes("reading strip") || lineLower.includes("processing")) { - setState(STATE.READING); - setPrompt(line); - } else if (lineLower.includes("error") || lineLower.includes("too fast") || lineLower.includes("too slow") || lineLower.includes("misread")) { - setState(STATE.ERROR); - setPrompt("⚠️ " + line); - } - }); - - const unlistenStderr = await listen("process:stderr", (event) => { - if (event.payload.id === currentProcessId && event.payload.line) { - logPre.textContent += "ERR: " + event.payload.line + "\n"; - logPre.scrollTop = logPre.scrollHeight; - } - }); - - const unlistenExit = await listen("process:exit", async (event) => { - if (event.payload.id !== currentProcessId) return; - unlistenStdout(); - unlistenStderr(); - unlistenExit(); - stopSwatchListener(); - - if (event.payload.code === 0) { - try { - const passIndex = currentPassIndex + 1; - const filename = await invoke("snapshot_ti3", { - cwd: cwd, - basename: basename, - pass_index: passIndex, - }); - - currentPassIndex = passIndex; - recordedPasses.push({ - index: currentPassIndex, - filename: filename, - time: new Date().toLocaleTimeString(), - }); - - setState(STATE.FINISHED); - setPrompt(`Sheet pass #${currentPassIndex} saved as ${filename}. Stage 4 stays locked until you finish.`); - logPre.textContent += `\n[SUCCESS] chartread completed. Snapshotted ${filename} (canonical ${basename}.ti3 removed until Finish).\n`; - - renderPassesList(); - if (averagingPanel) averagingPanel.classList.remove("hidden"); - if (passCounterBadge) passCounterBadge.textContent = `${recordedPasses.length} Pass(es) Recorded`; - // Do not call setStage3Result / wizardState.setTarget here (#110) - } catch (snapErr) { - setState(STATE.FINISHED); - setPrompt(`Measurement finished but pass snapshot failed: ${snapErr}`); - logPre.textContent += `\n[ERROR] snapshot_ti3 failed: ${snapErr}\n`; - } - } else { - setState(STATE.FINISHED); - setPrompt(`❌ chartread exited with code ${event.payload.code}.`); - logPre.textContent += `\n[ERROR] chartread exited with code ${event.payload.code}.\n`; - } - - setMeasurementBusy(false); - }); - - await invoke("run_chartread", { config }); - - } catch (err) { - setPrompt(`Invoke error: ${err}`); - setState(STATE.IDLE); - setMeasurementBusy(false); - } - }); - } - - if (btnMeasureAnotherSheet) { - btnMeasureAnotherSheet.addEventListener("click", () => { - if (measurementInProgress) { - setPrompt("Wait for the current measurement to finish and be snapshotted."); - return; - } - setState(STATE.IDLE); - setPrompt(`Ready to measure sheet pass #${recordedPasses.length + 1}. Starting chartread...`); - if (btnStartRead) btnStartRead.click(); - }); - } - - if (btnFinishAndAverage) { - btnFinishAndAverage.addEventListener("click", async () => { - const basename = stage2Basename || wizardState.basename; - const cwd = stage2Cwd || wizardState.cwd; - - if (!basename || !cwd) { - setPrompt("Error: No working directory or basename."); - return; - } - - if (measurementInProgress) { - setPrompt("Wait for the current measurement to finish."); - return; - } - - if (recordedPasses.length === 0) { - setPrompt("No measurement passes recorded yet."); - return; - } - - if (recordedPasses.length === 1) { - setPrompt("Single pass accepted. Restoring canonical .ti3 and proceeding to Stage 4..."); - btnFinishAndAverage.disabled = true; - try { - await invoke("promote_ti3", { - cwd: cwd, - source: recordedPasses[0].filename, - basename: basename, - }); - logPre.textContent += `\n[SUCCESS] Promoted ${recordedPasses[0].filename} → ${basename}.ti3 (single pass, average not invoked).\n`; - acceptStage3(basename, cwd); - } catch (e) { - btnFinishAndAverage.disabled = false; - setPrompt(`Failed to restore ${basename}.ti3: ${e}`); - } - return; - } - - setPrompt("Averaging measurement passes with Argyll average utility..."); - btnFinishAndAverage.disabled = true; - - try { - const averageConfig = { - inputs: recordedPasses.map(p => p.filename), - output: `${basename}.ti3`, - cwd: cwd, - }; - - logPre.textContent += `\n[average] average -v ${averageConfig.inputs.join(" ")} ${averageConfig.output}\n`; - - const unlistenAvgExit = await listen("process:exit", async (event) => { - if (event.payload.id !== `average_${basename}.ti3`) return; - unlistenAvgExit(); - - if (event.payload.code === 0) { - setPrompt(`✅ Successfully averaged ${recordedPasses.length} measurement passes into ${basename}.ti3!`); - logPre.textContent += `\n[SUCCESS] average wrote ${basename}.ti3\n`; - acceptStage3(basename, cwd); - } else { - setPrompt(`⚠️ Argyll average exited with code ${event.payload.code}. Promoting pass 1 as fallback.`); - logPre.textContent += `\n[ERROR] average exited ${event.payload.code}. Promoting ${recordedPasses[0].filename}.\n`; - try { - await invoke("promote_ti3", { - cwd: cwd, - source: recordedPasses[0].filename, - basename: basename, - }); - acceptStage3(basename, cwd); - } catch (e) { - btnFinishAndAverage.disabled = false; - setPrompt(`Average failed and fallback promote failed: ${e}`); - } - } - }); - - await invoke("run_average", { config: averageConfig }); - } catch (e) { - console.error("run_average error:", e); - btnFinishAndAverage.disabled = false; - setPrompt(`run_average error: ${e}`); - } - }); - } - - // Calibrate / confirm button — sends space + newline - if (btnCalibrate) { - btnCalibrate.addEventListener("click", async () => { - try { - await invoke("send_stdin", { id: currentProcessId, input: " \n" }); - } catch (e) { console.error("send_stdin error:", e); } - }); - } - - // Retry button — sends space + newline (same as confirm) - if (btnRetry) { - btnRetry.addEventListener("click", async () => { - try { - await invoke("send_stdin", { id: currentProcessId, input: " \n" }); - setState(STATE.READING); - setPrompt("Retrying strip read..."); - } catch (e) { console.error("send_stdin error:", e); } - }); - } - - // Skip button — sends "s\n" - if (btnSkip) { - btnSkip.addEventListener("click", async () => { - try { - await invoke("send_stdin", { id: currentProcessId, input: "s\n" }); - setState(STATE.AWAITING_STRIP); - setPrompt("Skipped current strip. Awaiting next..."); - } catch (e) { console.error("send_stdin error:", e); } - }); - } - - // Cancel button — kills the process - if (btnCancel) { - btnCancel.addEventListener("click", async () => { - try { - await invoke("kill_process", { id: currentProcessId }); - stopSwatchListener(); - setState(recordedPasses.length > 0 ? STATE.FINISHED : STATE.IDLE); - setPrompt("Measurement cancelled."); - } catch (e) { - console.error("kill_process error:", e); - setPrompt(`Cancel failed: ${e}`); - } finally { - setMeasurementBusy(false); - stopSwatchListener(); - } - }); - } - - // Start in IDLE state - setState(STATE.IDLE); - setPrompt("Press 'Start Measurement' to begin reading the printed target."); -} - -function advanceToStage4() { - const steps = document.querySelectorAll('.step'); - const stages = document.querySelectorAll('.stage'); - - steps.forEach(s => s.classList.remove('active')); - if (steps[3]) { - steps[3].classList.add('active'); - steps[3].classList.remove('disabled'); - } - - stages.forEach(s => { - s.classList.remove('active'); - s.classList.add('hidden'); - }); - if (stages[3]) { - stages[3].classList.remove('hidden'); - stages[3].classList.add('active'); - } -} +Y29uc3QgeyBpbnZva2UgfSA9IHdpbmRvdy5fX1RBVVJJX18uY29yZTsNCmNvbnN0IHsgbGlzdGVuIH0gPSB3aW5kb3cuX19UQVVSSV9fLmV2ZW50Ow0KaW1wb3J0IHsgc3RhcnRTd2F0Y2hMaXN0ZW5lciwgc3RvcFN3YXRjaExpc3RlbmVyIH0gZnJvbSAnLi9zd2F0Y2hfZ3JpZC5qcyc7DQppbXBvcnQgeyBzZXRTdGFnZTNSZXN1bHQgfSBmcm9tICcuL2NvbHByb2YuanMnOw0KaW1wb3J0IHsgd2l6YXJkU3RhdGUgfSBmcm9tICcuL3N0YXRlLmpzJzsNCg0KLy8gTW9kdWxlLWxldmVsIHN0YXRlOiBzZXQgYnkgU3RhZ2UgMiB3aGVuIGl0IGNvbXBsZXRlcw0KbGV0IHN0YWdlMkJhc2VuYW1lID0gIiI7DQpsZXQgc3RhZ2UyQ3dkID0gIiI7DQoNCi8qKg0KICogQ2FsbGVkIGJ5IHByaW50dGFyZy5qcyBhZnRlciBTdGFnZSAyIGNvbXBsZXRlcy4NCiAqLw0KZXhwb3J0IGZ1bmN0aW9uIHNldFN0YWdlMlJlc3VsdChiYXNlbmFtZSwgY3dkKSB7DQogIHN0YWdlMkJhc2VuYW1lID0gYmFzZW5hbWUgfHwgd2l6YXJkU3RhdGUuYmFzZW5hbWU7DQogIHN0YWdlMkN3ZCA9IGN3ZCB8fCB3aXphcmRTdGF0ZS5jd2Q7DQogIHdpemFyZFN0YXRlLnNldFRhcmdldChzdGFnZTJCYXNlbmFtZSwgc3RhZ2UyQ3dkKTsNCn0NCg0KLy8gU3RhdGUgbWFjaGluZSBzdGF0ZXMNCmNvbnN0IFNUQVRFID0gew0KICBJRExFOiAiSURMRSIsDQogIENBTElCUkFUSU5HOiAiQ0FMSUJSQVRJTkciLA0KICBBV0FJVElOR19TVFJJUDogIkFXQUlUSU5HX1NUUklQIiwNCiAgUkVBRElORzogIlJFQURJTkciLAogIEVSUk9SOiAiRVJST1IiLAogIEZJTklTSEVEOiAiRklOSVNIRUQiLAp9Ow0KDQpsZXQgY3VycmVudFN0YXRlID0gU1RBVEUuSURMRTsNCmxldCBjdXJyZW50UHJvY2Vzc0lkID0gIiI7DQpsZXQgbWVhc3VyZW1lbnRJblByb2dyZXNzID0gZmFsc2U7DQpsZXQgY3VycmVudFBhc3NJbmRleCA9IDA7DQpjb25zdCByZWNvcmRlZFBhc3NlcyA9IFtdOw0KDQpleHBvcnQgZnVuY3Rpb24gaW5pdENoYXJ0cmVhZCgpIHsNCiAgY29uc3QgYnRuU3RhcnRSZWFkID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImJ0blN0YXJ0UmVhZCIpOw0KICBjb25zdCBidG5DYWxpYnJhdGUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYnRuQ2FsaWJyYXRlIik7DQogIGNvbnN0IGJ0blJldHJ5ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImJ0blJldHJ5Iik7DQogIGNvbnN0IGJ0blNraXAgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYnRuU2tpcCIpOw0KICBjb25zdCBidG5DYW5jZWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYnRuQ2FuY2VsIik7DQogIGNvbnN0IGJ0bkRldGVjdEluc3RydW1lbnRzID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImJ0bkRldGVjdEluc3RydW1lbnRzIik7DQogIGNvbnN0IGluc3RydW1lbnRTZWxlY3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiY2hhcnRyZWFkSW5zdHJ1bWVudFNlbGVjdCIpOw0KICBjb25zdCBwcm9tcHRUZXh0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNoYXJ0cmVhZFByb21wdCIpOw0KICBjb25zdCBzdGF0ZUxhYmVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNoYXJ0cmVhZFN0YXRlIik7DQogIGNvbnN0IGxvZ0NvbnRhaW5lciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJjaGFydHJlYWRMb2dDb250YWluZXIiKTsNCiAgY29uc3QgbG9nUHJlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNoYXJ0cmVhZExvZyIpOw0KICBjb25zdCBhdmVyYWdpbmdQYW5lbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJjaGFydHJlYWRBdmVyYWdpbmdQYW5lbCIpOw0KICBjb25zdCBwYXNzZXNMaXN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInBhc3Nlc0xpc3QiKTsNCiAgY29uc3QgcGFzc0NvdW50ZXJCYWRnZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwYXNzQ291bnRlckJhZGdlIik7DQogIGNvbnN0IGJ0bk1lYXN1cmVBbm90aGVyU2hlZXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYnRuTWVhc3VyZUFub3RoZXJTaGVldCIpOw0KICBjb25zdCBidG5GaW5pc2hBbmRBdmVyYWdlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImJ0bkZpbmlzaEFuZEF2ZXJhZ2UiKTsNCg0KICBmdW5jdGlvbiBzZXRNZWFzdXJlbWVudEJ1c3koYnVzeSkgew0KICAgIG1lYXN1cmVtZW50SW5Qcm9ncmVzcyA9IGJ1c3k7DQogICAgaWYgKGJ0blN0YXJ0UmVhZCkgYnRuU3RhcnRSZWFkLmRpc2FibGVkID0gYnVzeTsNCiAgICBpZiAoYnRuTWVhc3VyZUFub3RoZXJTaGVldCkgYnRuTWVhc3VyZUFub3RoZXJTaGVldC5kaXNhYmxlZCA9IGJ1c3k7DQogICAgaWYgKGJ0bkZpbmlzaEFuZEF2ZXJhZ2UpIHsNCiAgICAgIGJ0bkZpbmlzaEFuZEF2ZXJhZ2UuZGlzYWJsZWQgPSBidXN5IHx8IHJlY29yZGVkUGFzc2VzLmxlbmd0aCA9PT0gMDsNCiAgICB9DQogIH0NCg0KICBmdW5jdGlvbiByZW5kZXJQYXNzZXNMaXN0KCkgew0KICAgIGlmICghcGFzc2VzTGlzdCkgcmV0dXJuOw0KICAgIHBhc3Nlc0xpc3QuaW5uZXJIVE1MID0gIiI7DQogICAgcmVjb3JkZWRQYXNzZXMuZm9yRWFjaCgocGFzcywgaSkgPT4gew0KICAgICAgY29uc3QgaXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImRpdiIpOw0KICAgICAgaXRlbS5zdHlsZS5jc3NUZXh0ID0gImRpc3BsYXk6ZmxleDsganVzdGlmeS1jb250ZW50OnNwYWNlLWJldHdlZW47IGFsaWduLWl0ZW1zOmNlbnRlcjsgYmFja2dyb3VuZDpyZ2JhKDI1NSwyNTUsMjU1LDAuMDUpOyBwYWRkaW5nOjZweCAxMHB4OyBib3JkZXItcmFkaXVzOjRweDsgZm9udC1zaXplOjAuODVyZW07IjsNCiAgICAgIGNvbnN0IGxhYmVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3BhbiIpOw0KICAgICAgY29uc3Qgc3Ryb25nID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3Ryb25nIik7DQogICAgICBzdHJvbmcudGV4dENvbnRlbnQgPSBgU2hlZXQgUGFzcyAjJHtpICsgMX1gOw0KICAgICAgbGFiZWwuYXBwZW5kQ2hpbGQoc3Ryb25nKTsNCiAgICAgIGxhYmVsLmFwcGVuZENoaWxkKGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKGAgKCR7cGFzcy5maWxlbmFtZX0pYCkpOw0KICAgICAgY29uc3QgdGltZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoInNwYW4iKTsNCiAgICAgIHRpbWUuc3R5bGUub3BhY2l0eSA9ICIwLjciOw0KICAgICAgdGltZS50ZXh0Q29udGVudCA9IGDinJMgJHtwYXNzLnRpbWV9YDsNCiAgICAgIGl0ZW0uYXBwZW5kQ2hpbGQobGFiZWwpOw0KICAgICAgaXRlbS5hcHBlbmRDaGlsZCh0aW1lKTsNCiAgICAgIHBhc3Nlc0xpc3QuYXBwZW5kQ2hpbGQoaXRlbSk7DQogICAgfSk7DQogIH0NCg0KICBmdW5jdGlvbiBhY2NlcHRTdGFnZTMoYmFzZW5hbWUsIGN3ZCkgew0KICAgIHdpemFyZFN0YXRlLnNldFRhcmdldChiYXNlbmFtZSwgY3dkKTsNCiAgICBzZXRTdGFnZTNSZXN1bHQoYmFzZW5hbWUsIGN3ZCk7DQogICAgd2l6YXJkU3RhdGUudXBkYXRlR2F0aW5nKCk7DQogICAgYWR2YW5jZVRvU3RhZ2U0KCk7DQogIH0NCg0KICAvLyBJbnN0cnVtZW50IGRldGVjdGlvbiBsb2dpYw0KICBpZiAoYnRuRGV0ZWN0SW5zdHJ1bWVudHMgJiYgaW5zdHJ1bWVudFNlbGVjdCkgew0KICAgIGJ0bkRldGVjdEluc3RydW1lbnRzLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgYXN5bmMgKCkgPT4gew0KICAgICAgYnRuRGV0ZWN0SW5zdHJ1bWVudHMuZGlzYWJsZWQgPSB0cnVlOw0KICAgICAgYnRuRGV0ZWN0SW5zdHJ1bWVudHMudGV4dENvbnRlbnQgPSAiRGV0ZWN0aW5nLi4uIjsNCiAgICAgIHNldFByb21wdCgiUXVlcnlpbmcgY29ubmVjdGVkIHNwZWN0cm9waG90b21ldGVycyBhbmQgY29sb3JpbWV0ZXJzIHZpYSBpbnN0bGlzdC4uLiIpOw0KDQogICAgICBjb25zdCBkZXRlY3RlZCA9IFtdOw0KDQogICAgICB0cnkgew0KICAgICAgICBjb25zdCB1bmxpc3RlblN0ZG91dCA9IGF3YWl0IGxpc3RlbigicHJvY2VzczpzdGRvdXQiLCAoZXZlbnQpID0+IHsNCiAgICAgICAgICBpZiAoZXZlbnQucGF5bG9hZC5pZCAhPT0gImluc3RsaXN0IiB8fCAhZXZlbnQucGF5bG9hZC5saW5lKSByZXR1cm47DQogICAgICAgICAgY29uc3QgbGluZSA9IGV2ZW50LnBheWxvYWQubGluZS50cmltKCk7DQoNCiAgICAgICAgICAvLyBNYXRjaGVzIEFyZ3lsbCBpbnN0bGlzdCBvdXRwdXQgZS5nLiAiMTogJ2kxUHJvJyBvbiAnVVNCJyIgb3IgIjE6ICdDb2xvck11bmtpJyIgb3IgIjEgPSAnaTFEaXNwbGF5JyINCiAgICAgICAgICBjb25zdCBLTk9XTl9JTlNUX1RPS0VOUyA9IC9pMXxDb2xvck11bmtpfFNweWRlcnxzcGVjdHJvfERpc3BsYXl8SHVleXxEVFB8U3BlY3Ryb1NjYW58U21pbGV8S2xlaW4vaTsNCiAgICAgICAgICBjb25zdCBtYXRjaCA9IGxpbmUubWF0Y2goL14oXGQrKVtcczo9XSsnPyhbXidcbl0rKSc/KD86XHMrb25ccysnPyhbXidcbl0rKSc/KT8vaSk7DQogICAgICAgICAgaWYgKG1hdGNoKSB7DQogICAgICAgICAgICBjb25zdCBpbmRleCA9IG1hdGNoWzFdOw0KICAgICAgICAgICAgY29uc3QgbmFtZSA9IG1hdGNoWzJdLnRyaW0oKTsNCiAgICAgICAgICAgIGNvbnN0IHBvcnQgPSBtYXRjaFszXSA/IG1hdGNoWzNdLnRyaW0oKSA6ICIiOw0KICAgICAgICAgICAgaWYgKEtOT1dOX0lOU1RfVE9LRU5TLnRlc3QobmFtZSkgfHwgcG9ydC5sZW5ndGggPiAwKSB7DQogICAgICAgICAgICAgIGRldGVjdGVkLnB1c2goeyBpbmRleCwgbmFtZSwgcG9ydCB9KTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICB9DQogICAgICAgIH0pOw0KDQogICAgICAgIGNvbnN0IHVubGlzdGVuRXhpdCA9IGF3YWl0IGxpc3RlbigicHJvY2VzczpleGl0IiwgKGV2ZW50KSA9PiB7DQogICAgICAgICAgaWYgKGV2ZW50LnBheWxvYWQuaWQgIT09ICJpbnN0bGlzdCIpIHJldHVybjsNCiAgICAgICAgICB1bmxpc3RlblN0ZG91dCgpOw0KICAgICAgICAgIHVubGlzdGVuRXhpdCgpOw0KICAgICAgICAgIGJ0bkRldGVjdEluc3RydW1lbnRzLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgICAgYnRuRGV0ZWN0SW5zdHJ1bWVudHMudGV4dENvbnRlbnQgPSAi4oa6IERldGVjdCI7DQoNCiAgICAgICAgICBpbnN0cnVtZW50U2VsZWN0LmlubmVySFRNTCA9IGA8b3B0aW9uIHZhbHVlPSIiPkF1dG8gKEZpcnN0IGF2YWlsYWJsZSBwb3J0KTwvb3B0aW9uPmA7DQoNCiAgICAgICAgICBpZiAoZGV0ZWN0ZWQubGVuZ3RoID4gMCkgew0KICAgICAgICAgICAgZGV0ZWN0ZWQuZm9yRWFjaCgoaW5zdCkgPT4gew0KICAgICAgICAgICAgICBjb25zdCBvcHQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJvcHRpb24iKTsNCiAgICAgICAgICAgICAgLy8gRG8gbm90IHBhc3MgaW5zdGxpc3Qgb3JkaW5hbCBhcyAtYyBjb21tIHBvcnQ7IHVzZSBlbXB0eSB2YWx1ZSBmb3IgYXV0by1wb3J0ICgjMTExKQ0KICAgICAgICAgICAgICBvcHQudmFsdWUgPSAiIjsNCiAgICAgICAgICAgICAgb3B0LnRleHRDb250ZW50ID0gYCR7aW5zdC5uYW1lfSR7aW5zdC5wb3J0ID8gYCBvbiAke2luc3QucG9ydH1gIDogIiJ9YDsNCiAgICAgICAgICAgICAgaW5zdHJ1bWVudFNlbGVjdC5hcHBlbmRDaGlsZChvcHQpOw0KICAgICAgICAgICAgfSk7DQogICAgICAgICAgICBpbnN0cnVtZW50U2VsZWN0LnZhbHVlID0gIiI7DQogICAgICAgICAgICBzZXRQcm9tcHQoYEZvdW5kICR7ZGV0ZWN0ZWQubGVuZ3RofSBpbnN0cnVtZW50KHMpOiAke2RldGVjdGVkLm1hcChkID0+IGQubmFtZSkuam9pbigiLCAiKX1gKTsNCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgc2V0UHJvbXB0KCJObyBpbnN0cnVtZW50cyBmb3VuZCB2aWEgaW5zdGxpc3QuIEVuc3VyZSBVU0IgY2FibGUgaXMgcGx1Z2dlZCBpbi4iKTsNCiAgICAgICAgICB9DQogICAgICAgIH0pOw0KDQogICAgICAgIGF3YWl0IGludm9rZSgiZGV0ZWN0X2luc3RydW1lbnRzIik7DQogICAgICB9IGNhdGNoIChlcnIpIHsNCiAgICAgICAgY29uc29sZS5lcnJvcigiZGV0ZWN0X2luc3RydW1lbnRzIGVycm9yOiIsIGVycik7DQogICAgICAgIGJ0bkRldGVjdEluc3RydW1lbnRzLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgIGJ0bkRldGVjdEluc3RydW1lbnRzLnRleHRDb250ZW50ID0gIuKGuyBEZXRlY3QiOw0KICAgICAgICBzZXRQcm9tcHQoYEluc3RydW1lbnQgZGV0ZWN0aW9uIGVycm9yOiAke2Vycn1gKTsNCiAgICAgIH0NCiAgICB9KTsNCiAgfQ0KDQogIGZ1bmN0aW9uIHNldFN0YXRlKG5ld1N0YXRlKSB7DQogICAgY3VycmVudFN0YXRlID0gbmV3U3RhdGU7DQogICAgaWYgKHN0YXRlTGFiZWwpIHN0YXRlTGFiZWwudGV4dENvbnRlbnQgPSBuZXdTdGF0ZTsNCg0KICAgIC8vIFNob3cvaGlkZSBidXR0b25zIGJhc2VkIG9uIHN0YXRlDQogICAgYnRuQ2FsaWJyYXRlLmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOw0KICAgIGJ0blJldHJ5LmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOw0KICAgIGJ0blNraXAuY2xhc3NMaXN0LmFkZCgiaGlkZGVuIik7DQogICAgYnRuQ2FuY2VsLmNsYXNzTGlzdC5hZGQoImhpZGRlbiIpOw0KICAgIGJ0blN0YXJ0UmVhZC5jbGFzc0xpc3QuYWRkKCJoaWRkZW4iKTsNCg0KICAgIHN3aXRjaCAobmV3U3RhdGUpIHsNCiAgICAgIGNhc2UgU1RBVEUuSURMRToNCiAgICAgICAgYnRuU3RhcnRSZWFkLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgICBicmVhazsNCiAgICAgIGNhc2UgU1RBVEUuQ0FMSUJSQVRJTkc6DQogICAgICAgIGJ0bkNhbGlicmF0ZS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsNCiAgICAgICAgYnRuQ2FuY2VsLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgICBicmVhazsNCiAgICAgIGNhc2UgU1RBVEUuQVdBSVRJTkdfU1RSSVA6DQogICAgICAgIGJ0blJldHJ5LmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgICBidG5Ta2lwLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgICBidG5DYW5jZWwuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgIGJyZWFrOw0KICAgICAgY2FzZSBTVEFURS5SRUFESU5HOg0KICAgICAgICBidG5DYW5jZWwuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgIGJyZWFrOw0KICAgICAgY2FzZSBTVEFURS5FUlJPUjoNCiAgICAgICAgYnRuUmV0cnkuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgIGJ0blNraXAuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgIGJ0bkNhbmNlbC5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsNCiAgICAgICAgYnJlYWs7DQogICAgICBjYXNlIFNUQVRFLkZJTklTSEVEOg0KICAgICAgICAvLyBBZnRlciBhIHJlY29yZGVkIHBhc3MsIGZ1cnRoZXIgc2hlZXRzIGdvIHRocm91Z2ggIk1lYXN1cmUgQW5vdGhlciBTaGVldCINCiAgICAgICAgaWYgKHJlY29yZGVkUGFzc2VzLmxlbmd0aCA9PT0gMCkgew0KICAgICAgICAgIGJ0blN0YXJ0UmVhZC5jbGFzc0xpc3QucmVtb3ZlKCJoaWRkZW4iKTsNCiAgICAgICAgfQ0KICAgICAgICBicmVhazsNCiAgICB9DQogIH0NCg0KICBmdW5jdGlvbiBzZXRQcm9tcHQodGV4dCkgew0KICAgIGlmIChwcm9tcHRUZXh0KSBwcm9tcHRUZXh0LnRleHRDb250ZW50ID0gdGV4dDsNCiAgfQ0KDQogIC8vIFN0YXJ0IHJlYWRpbmcgYnV0dG9uDQogIGlmIChidG5TdGFydFJlYWQpIHsNCiAgICBidG5TdGFydFJlYWQuYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBhc3luYyAoKSA9PiB7DQogICAgICBpZiAobWVhc3VyZW1lbnRJblByb2dyZXNzKSB7DQogICAgICAgIHNldFByb21wdCgiQSBtZWFzdXJlbWVudCBpcyBhbHJlYWR5IHJ1bm5pbmcuIFdhaXQgZm9yIGl0IHRvIGZpbmlzaC4iKTsNCiAgICAgICAgcmV0dXJuOw0KICAgICAgfQ0KDQogICAgICBjb25zdCBiYXNlbmFtZSA9IHN0YWdlMkJhc2VuYW1lIHx8IHdpemFyZFN0YXRlLmJhc2VuYW1lOw0KICAgICAgY29uc3QgY3dkID0gc3RhZ2UyQ3dkIHx8IHdpemFyZFN0YXRlLmN3ZDsNCg0KICAgICAgaWYgKCFiYXNlbmFtZSB8fCAhY3dkKSB7DQogICAgICAgIHNldFByb21wdCgiRXJyb3I6IE5vIC50aTIgZmlsZSBhdmFpbGFibGUuIENvbXBsZXRlIFN0YWdlIDIgZmlyc3QuIik7DQogICAgICAgIHJldHVybjsNCiAgICAgIH0NCg0KICAgICAgc3RhZ2UyQmFzZW5hbWUgPSBiYXNlbmFtZTsNCiAgICAgIHN0YWdlMkN3ZCA9IGN3ZDsNCg0KICAgICAgbG9nUHJlLnRleHRDb250ZW50ID0gIiI7DQogICAgICBsb2dDb250YWluZXIub3BlbiA9IGZhbHNlOw0KICAgICAgbG9nQ29udGFpbmVyLmNsYXNzTGlzdC5yZW1vdmUoImhpZGRlbiIpOw0KICAgICAgc2V0TWVhc3VyZW1lbnRCdXN5KHRydWUpOw0KICAgICAgc2V0U3RhdGUoU1RBVEUuQ0FMSUJSQVRJTkcpOw0KICAgICAgc2V0UHJvbXB0KCJTdGFydGluZyBjaGFydHJlYWQuLi4gd2FpdGluZyBmb3IgaW5zdHJ1bWVudCBjYWxpYnJhdGlvbiBwcm9tcHQuIik7DQoNCiAgICAgIGNvbnN0IHNlbGVjdGVkUG9ydCA9IGluc3RydW1lbnRTZWxlY3QgJiYgaW5zdHJ1bWVudFNlbGVjdC52YWx1ZSA/IGluc3RydW1lbnRTZWxlY3QudmFsdWUgOiBudWxsOw0KDQogICAgICBjb25zdCBjb25maWcgPSB7DQogICAgICAgIGJhc2VuYW1lOiBiYXNlbmFtZSwNCiAgICAgICAgY3dkOiBjd2QsDQogICAgICAgIHBvcnQ6IHNlbGVjdGVkUG9ydCwNCiAgICAgIH07DQoNCiAgICAgIGN1cnJlbnRQcm9jZXNzSWQgPSBgY2hhcnRyZWFkXyR7YmFzZW5hbWV9YDsNCg0KICAgICAgLy8gU3RhcnQgc3dhdGNoIGdyaWQgbGlzdGVuZXINCiAgICAgIGF3YWl0IHN0YXJ0U3dhdGNoTGlzdGVuZXIoY3VycmVudFByb2Nlc3NJZCk7DQoNCiAgICAgIHRyeSB7DQogICAgICAgIGNvbnN0IHVubGlzdGVuU3Rkb3V0ID0gYXdhaXQgbGlzdGVuKCJwcm9jZXNzOnN0ZG91dCIsIChldmVudCkgPT4gew0KICAgICAgICAgIGlmIChldmVudC5wYXlsb2FkLmlkICE9PSBjdXJyZW50UHJvY2Vzc0lkIHx8ICFldmVudC5wYXlsb2FkLmxpbmUpIHJldHVybjsNCiAgICAgICAgICBjb25zdCBsaW5lID0gZXZlbnQucGF5bG9hZC5saW5lOw0KDQogICAgICAgICAgbG9nUHJlLnRleHRDb250ZW50ICs9IGxpbmUgKyAiXG4iOw0KICAgICAgICAgIGxvZ1ByZS5zY3JvbGxUb3AgPSBsb2dQcmUuc2Nyb2xsSGVpZ2h0Ow0KDQogICAgICAgICAgLy8gUGFyc2UgcHJvbXB0cyBmb3Igc3RhdGUgdHJhbnNpdGlvbnMNCiAgICAgICAgICBjb25zdCBsaW5lTG93ZXIgPSBsaW5lLnRvTG93ZXJDYXNlKCk7DQoNCiAgICAgICAgICBpZiAobGluZUxvd2VyLmluY2x1ZGVzKCJjYWxpYnJhdCIpICYmIGxpbmVMb3dlci5pbmNsdWRlcygicGxhY2UiKSkgew0KICAgICAgICAgICAgc2V0U3RhdGUoU1RBVEUuQ0FMSUJSQVRJTkcpOw0KICAgICAgICAgICAgc2V0UHJvbXB0KGxpbmUpOw0KICAgICAgICAgIH0gZWxzZSBpZiAobGluZUxvd2VyLmluY2x1ZGVzKCJoaXQiKSAmJiBsaW5lTG93ZXIuaW5jbHVkZXMoInJlYWQiKSAmJiBsaW5lTG93ZXIuaW5jbHVkZXMoInN0cmlwIikpIHsNCiAgICAgICAgICAgIHNldFN0YXRlKFNUQVRFLkFXQUlUSU5HX1NUUklQKTsNCiAgICAgICAgICAgIHNldFByb21wdChsaW5lKTsNCiAgICAgICAgICB9IGVsc2UgaWYgKGxpbmVMb3dlci5pbmNsdWRlcygicmVhZHkgdG8gcmVhZCIpKSB7DQogICAgICAgICAgICBzZXRTdGF0ZShTVEFURS5BV0FJVElOR19TVFJJUCk7DQogICAgICAgICAgICBzZXRQcm9tcHQobGluZSk7DQogICAgICAgICAgfSBlbHNlIGlmIChsaW5lTG93ZXIuaW5jbHVkZXMoInJlYWRpbmcgc3RyaXAiKSB8fCBsaW5lTG93ZXIuaW5jbHVkZXMoInByb2Nlc3NpbmciKSkgew0KICAgICAgICAgICAgc2V0U3RhdGUoU1RBVEUuUkVBRElORyk7DQogICAgICAgICAgICBzZXRQcm9tcHQobGluZSk7DQogICAgICAgICAgfSBlbHNlIGlmIChsaW5lTG93ZXIuaW5jbHVkZXMoImVycm9yIikgfHwgbGluZUxvd2VyLmluY2x1ZGVzKCJ0b28gZmFzdCIpIHx8IGxpbmVMb3dlci5pbmNsdWRlcygidG9vIHNsb3ciKSB8fCBsaW5lTG93ZXIuaW5jbHVkZXMoIm1pc3JlYWQiKSkgew0KICAgICAgICAgICAgc2V0U3RhdGUoU1RBVEUuRVJST1IpOw0KICAgICAgICAgICAgc2V0UHJvbXB0KCLimqDvuI8gIiArIGxpbmUpOw0KICAgICAgICAgIH0NCiAgICAgICAgfSk7DQoNCiAgICAgICAgY29uc3QgdW5saXN0ZW5TdGRlcnIgPSBhd2FpdCBsaXN0ZW4oInByb2Nlc3M6c3RkZXJyIiwgKGV2ZW50KSA9PiB7DQogICAgICAgICAgaWYgKGV2ZW50LnBheWxvYWQuaWQgPT09IGN1cnJlbnRQcm9jZXNzSWQgJiYgZXZlbnQucGF5bG9hZC5saW5lKSB7DQogICAgICAgICAgICBsb2dQcmUudGV4dENvbnRlbnQgKz0gIkVSUjogIiArIGV2ZW50LnBheWxvYWQubGluZSArICJcbiI7DQogICAgICAgICAgICBsb2dQcmUuc2Nyb2xsVG9wID0gbG9nUHJlLnNjcm9sbEhlaWdodDsNCiAgICAgICAgICB9DQogICAgICAgIH0pOw0KDQogICAgICAgIGNvbnN0IHVubGlzdGVuRXhpdCA9IGF3YWl0IGxpc3RlbigicHJvY2VzczpleGl0IiwgYXN5bmMgKGV2ZW50KSA9PiB7DQogICAgICAgICAgaWYgKGV2ZW50LnBheWxvYWQuaWQgIT09IGN1cnJlbnRQcm9jZXNzSWQpIHJldHVybjsNCiAgICAgICAgICB1bmxpc3RlblN0ZG91dCgpOw0KICAgICAgICAgIHVubGlzdGVuU3RkZXJyKCk7DQogICAgICAgICAgdW5saXN0ZW5FeGl0KCk7DQogICAgICAgICAgc3RvcFN3YXRjaExpc3RlbmVyKCk7DQoNCiAgICAgICAgICBpZiAoZXZlbnQucGF5bG9hZC5jb2RlID09PSAwKSB7DQogICAgICAgICAgICB0cnkgew0KICAgICAgICAgICAgICBjb25zdCBwYXNzSW5kZXggPSBjdXJyZW50UGFzc0luZGV4ICsgMTsNCiAgICAgICAgICAgICAgY29uc3QgZmlsZW5hbWUgPSBhd2FpdCBpbnZva2UoInNuYXBzaG90X3RpMyIsIHsNCiAgICAgICAgICAgICAgICBjd2Q6IGN3ZCwNCiAgICAgICAgICAgICAgICBiYXNlbmFtZTogYmFzZW5hbWUsDQogICAgICAgICAgICAgICAgcGFzc19pbmRleDogcGFzc0luZGV4LA0KICAgICAgICAgICAgICB9KTsNCg0KICAgICAgICAgICAgICBjdXJyZW50UGFzc0luZGV4ID0gcGFzc0luZGV4Ow0KICAgICAgICAgICAgICByZWNvcmRlZFBhc3Nlcy5wdXNoKHsNCiAgICAgICAgICAgICAgICBpbmRleDogY3VycmVudFBhc3NJbmRleCwNCiAgICAgICAgICAgICAgICBmaWxlbmFtZTogZmlsZW5hbWUsDQogICAgICAgICAgICAgICAgdGltZTogbmV3IERhdGUoKS50b0xvY2FsZVRpbWVTdHJpbmcoKSwNCiAgICAgICAgICAgICAgfSk7DQoNCiAgICAgICAgICAgICAgc2V0U3RhdGUoU1RBVEUuRklOSVNIRUQpOw0KICAgICAgICAgICAgICBzZXRQcm9tcHQoYFNoZWV0IHBhc3MgIyR7Y3VycmVudFBhc3NJbmRleH0gc2F2ZWQgYXMgJHtmaWxlbmFtZX0uIFN0YWdlIDQgc3RheXMgbG9ja2VkIHVudGlsIHlvdSBmaW5pc2guYCk7DQogICAgICAgICAgICAgIGxvZ1ByZS50ZXh0Q29udGVudCArPSBgXG5bU1VDQ0VTU10gY2hhcnRyZWFkIGNvbXBsZXRlZC4gU25hcHNob3R0ZWQgJHtmaWxlbmFtZX0gKGNhbm9uaWNhbCAke2Jhc2VuYW1lfS50aTMgcmVtb3ZlZCB1bnRpbCBGaW5pc2gpLlxuYDsNCg0KICAgICAgICAgICAgICByZW5kZXJQYXNzZXNMaXN0KCk7DQogICAgICAgICAgICAgIGlmIChhdmVyYWdpbmdQYW5lbCkgYXZlcmFnaW5nUGFuZWwuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZGVuIik7DQogICAgICAgICAgICAgIGlmIChwYXNzQ291bnRlckJhZGdlKSBwYXNzQ291bnRlckJhZGdlLnRleHRDb250ZW50ID0gYCR7cmVjb3JkZWRQYXNzZXMubGVuZ3RofSBQYXNzKGVzKSBSZWNvcmRlZGA7DQogICAgICAgICAgICAgIC8vIERvIG5vdCBjYWxsIHNldFN0YWdlM1Jlc3VsdCAvIHdpemFyZFN0YXRlLnNldFRhcmdldCBoZXJlICgjMTEwKQ0KICAgICAgICAgICAgfSBjYXRjaCAoc25hcEVycikgew0KICAgICAgICAgICAgICBzZXRTdGF0ZShTVEFURS5GSU5JU0hFRCk7DQogICAgICAgICAgICAgIHNldFByb21wdChgTWVhc3VyZW1lbnQgZmluaXNoZWQgYnV0IHBhc3Mgc25hcHNob3QgZmFpbGVkOiAke3NuYXBFcnJ9YCk7DQogICAgICAgICAgICAgIGxvZ1ByZS50ZXh0Q29udGVudCArPSBgXG5bRVJST1JdIHNuYXBzaG90X3RpMyBmYWlsZWQ6ICR7c25hcEVycn1cbmA7DQogICAgICAgICAgICB9DQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIHNldFN0YXRlKFNUQVRFLkZJTklTSEVEKTsNCiAgICAgICAgICAgIHNldFByb21wdChg4p2MIGNoYXJ0cmVhZCBleGl0ZWQgd2l0aCBjb2RlICR7ZXZlbnQucGF5bG9hZC5jb2RlfS5gKTsNCiAgICAgICAgICAgIGxvZ1ByZS50ZXh0Q29udGVudCArPSBgXG5bRVJST1JdIGNoYXJ0cmVhZCBleGl0ZWQgd2l0aCBjb2RlICR7ZXZlbnQucGF5bG9hZC5jb2RlfS5cbmA7DQogICAgICAgICAgfQ0KDQogICAgICAgICAgc2V0TWVhc3VyZW1lbnRCdXN5KGZhbHNlKTsNCiAgICAgICAgfSk7DQoNCiAgICAgICAgYXdhaXQgaW52b2tlKCJydW5fY2hhcnRyZWFkIiwgeyBjb25maWcgfSk7DQoNCiAgICAgIH0gY2F0Y2ggKGVycikgew0KICAgICAgICBzZXRQcm9tcHQoYEludm9rZSBlcnJvcjogJHtlcnJ9YCk7DQogICAgICAgIHNldFN0YXRlKFNUQVRFLklETEUpOw0KICAgICAgICBzZXRNZWFzdXJlbWVudEJ1c3koZmFsc2UpOw0KICAgICAgfQ0KICAgIH0pOw0KICB9DQoNCiAgaWYgKGJ0bk1lYXN1cmVBbm90aGVyU2hlZXQpIHsNCiAgICBidG5NZWFzdXJlQW5vdGhlclNoZWV0LmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgKCkgPT4gew0KICAgICAgaWYgKG1lYXN1cmVtZW50SW5Qcm9ncmVzcykgew0KICAgICAgICBzZXRQcm9tcHQoIldhaXQgZm9yIHRoZSBjdXJyZW50IG1lYXN1cmVtZW50IHRvIGZpbmlzaCBhbmQgYmUgc25hcHNob3R0ZWQuIik7DQogICAgICAgIHJldHVybjsNCiAgICAgIH0NCiAgICAgIHNldFN0YXRlKFNUQVRFLklETEUpOw0KICAgICAgc2V0UHJvbXB0KGBSZWFkeSB0byBtZWFzdXJlIHNoZWV0IHBhc3MgIyR7cmVjb3JkZWRQYXNzZXMubGVuZ3RoICsgMX0uIFN0YXJ0aW5nIGNoYXJ0cmVhZC4uLmApOw0KICAgICAgaWYgKGJ0blN0YXJ0UmVhZCkgYnRuU3RhcnRSZWFkLmNsaWNrKCk7DQogICAgfSk7DQogIH0NCg0KICBpZiAoYnRuRmluaXNoQW5kQXZlcmFnZSkgew0KICAgIGJ0bkZpbmlzaEFuZEF2ZXJhZ2UuYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBhc3luYyAoKSA9PiB7DQogICAgICBjb25zdCBiYXNlbmFtZSA9IHN0YWdlMkJhc2VuYW1lIHx8IHdpemFyZFN0YXRlLmJhc2VuYW1lOw0KICAgICAgY29uc3QgY3dkID0gc3RhZ2UyQ3dkIHx8IHdpemFyZFN0YXRlLmN3ZDsNCg0KICAgICAgaWYgKCFiYXNlbmFtZSB8fCAhY3dkKSB7DQogICAgICAgIHNldFByb21wdCgiRXJyb3I6IE5vIHdvcmtpbmcgZGlyZWN0b3J5IG9yIGJhc2VuYW1lLiIpOw0KICAgICAgICByZXR1cm47DQogICAgICB9DQoNCiAgICAgIGlmIChtZWFzdXJlbWVudEluUHJvZ3Jlc3MpIHsNCiAgICAgICAgc2V0UHJvbXB0KCJXYWl0IGZvciB0aGUgY3VycmVudCBtZWFzdXJlbWVudCB0byBmaW5pc2guIik7DQogICAgICAgIHJldHVybjsNCiAgICAgIH0NCg0KICAgICAgaWYgKHJlY29yZGVkUGFzc2VzLmxlbmd0aCA9PT0gMCkgew0KICAgICAgICBzZXRQcm9tcHQoIk5vIG1lYXN1cmVtZW50IHBhc3NlcyByZWNvcmRlZCB5ZXQuIik7DQogICAgICAgIHJldHVybjsNCiAgICAgIH0NCg0KICAgICAgaWYgKHJlY29yZGVkUGFzc2VzLmxlbmd0aCA9PT0gMSkgew0KICAgICAgICBzZXRQcm9tcHQoIlNpbmdsZSBwYXNzIGFjY2VwdGVkLiBSZXN0b3JpbmcgY2Fub25pY2FsIC50aTMgYW5kIHByb2NlZWRpbmcgdG8gU3RhZ2UgNC4uLiIpOw0KICAgICAgICBidG5GaW5pc2hBbmRBdmVyYWdlLmRpc2FibGVkID0gdHJ1ZTsNCiAgICAgICAgdHJ5IHsNCiAgICAgICAgICBhd2FpdCBpbnZva2UoInByb21vdGVfdGkzIiwgew0KICAgICAgICAgICAgY3dkOiBjd2QsDQogICAgICAgICAgICBzb3VyY2U6IHJlY29yZGVkUGFzc2VzWzBdLmZpbGVuYW1lLA0KICAgICAgICAgICAgYmFzZW5hbWU6IGJhc2VuYW1lLA0KICAgICAgICAgIH0pOw0KICAgICAgICAgIGxvZ1ByZS50ZXh0Q29udGVudCArPSBgXG5bU1VDQ0VTU10gUHJvbW90ZWQgJHtyZWNvcmRlZFBhc3Nlc1swXS5maWxlbmFtZX0g4oaSICR7YmFzZW5hbWV9LnRpMyAoc2luZ2xlIHBhc3MsIGF2ZXJhZ2Ugbm90IGludm9rZWQpLlxuYDsNCiAgICAgICAgICBhY2NlcHRTdGFnZTMoYmFzZW5hbWUsIGN3ZCk7DQogICAgICAgIH0gY2F0Y2ggKGUpIHsNCiAgICAgICAgICBidG5GaW5pc2hBbmRBdmVyYWdlLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgICAgc2V0UHJvbXB0KGBGYWlsZWQgdG8gcmVzdG9yZSAke2Jhc2VuYW1lfS50aTM6ICR7ZX1gKTsNCiAgICAgICAgfQ0KICAgICAgICByZXR1cm47DQogICAgICB9DQoNCiAgICAgIHNldFByb21wdCgiQXZlcmFnaW5nIG1lYXN1cmVtZW50IHBhc3NlcyB3aXRoIEFyZ3lsbCBhdmVyYWdlIHV0aWxpdHkuLi4iKTsNCiAgICAgIGJ0bkZpbmlzaEFuZEF2ZXJhZ2UuZGlzYWJsZWQgPSB0cnVlOw0KDQogICAgICB0cnkgew0KICAgICAgICBjb25zdCBhdmVyYWdlQ29uZmlnID0gew0KICAgICAgICAgIGlucHV0czogcmVjb3JkZWRQYXNzZXMubWFwKHAgPT4gcC5maWxlbmFtZSksDQogICAgICAgICAgb3V0cHV0OiBgJHtiYXNlbmFtZX0udGkzYCwNCiAgICAgICAgICBjd2Q6IGN3ZCwNCiAgICAgICAgfTsNCg0KICAgICAgICBsb2dQcmUudGV4dENvbnRlbnQgKz0gYFxuW2F2ZXJhZ2VdIGF2ZXJhZ2UgLXYgJHthdmVyYWdlQ29uZmlnLmlucHV0cy5qb2luKCIgIil9ICR7YXZlcmFnZUNvbmZpZy5vdXRwdXR9XG5gOw0KDQogICAgICAgIGNvbnN0IHVubGlzdGVuQXZnRXhpdCA9IGF3YWl0IGxpc3RlbigicHJvY2VzczpleGl0IiwgYXN5bmMgKGV2ZW50KSA9PiB7DQogICAgICAgICAgaWYgKGV2ZW50LnBheWxvYWQuaWQgIT09IGBhdmVyYWdlXyR7YmFzZW5hbWV9LnRpM2ApIHJldHVybjsNCiAgICAgICAgICB1bmxpc3RlbkF2Z0V4aXQoKTsNCg0KICAgICAgICAgIGlmIChldmVudC5wYXlsb2FkLmNvZGUgPT09IDApIHsNCiAgICAgICAgICAgIHNldFByb21wdChg4pyFIFN1Y2Nlc3NmdWxseSBhdmVyYWdlZCAke3JlY29yZGVkUGFzc2VzLmxlbmd0aH0gbWVhc3VyZW1lbnQgcGFzc2VzIGludG8gJHtiYXNlbmFtZX0udGkzIWApOw0KICAgICAgICAgICAgbG9nUHJlLnRleHRDb250ZW50ICs9IGBcbltTVUNDRVNTXSBhdmVyYWdlIHdyb3RlICR7YmFzZW5hbWV9LnRpM1xuYDsNCiAgICAgICAgICAgIGFjY2VwdFN0YWdlMyhiYXNlbmFtZSwgY3dkKTsNCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgc2V0UHJvbXB0KGDimqDvuI8gQXJneWxsIGF2ZXJhZ2UgZXhpdGVkIHdpdGggY29kZSAke2V2ZW50LnBheWxvYWQuY29kZX0uIFByb21vdGluZyBwYXNzIDEgYXMgZmFsbGJhY2suYCk7DQogICAgICAgICAgICBsb2dQcmUudGV4dENvbnRlbnQgKz0gYFxuW0VSUk9SXSBhdmVyYWdlIGV4aXRlZCAke2V2ZW50LnBheWxvYWQuY29kZX0uIFByb21vdGluZyAke3JlY29yZGVkUGFzc2VzWzBdLmZpbGVuYW1lfS5cbmA7DQogICAgICAgICAgICB0cnkgew0KICAgICAgICAgICAgICBhd2FpdCBpbnZva2UoInByb21vdGVfdGkzIiwgew0KICAgICAgICAgICAgICAgIGN3ZDogY3dkLA0KICAgICAgICAgICAgICAgIHNvdXJjZTogcmVjb3JkZWRQYXNzZXNbMF0uZmlsZW5hbWUsDQogICAgICAgICAgICAgICAgYmFzZW5hbWU6IGJhc2VuYW1lLA0KICAgICAgICAgICAgICB9KTsNCiAgICAgICAgICAgICAgYWNjZXB0U3RhZ2UzKGJhc2VuYW1lLCBjd2QpOw0KICAgICAgICAgICAgfSBjYXRjaCAoZSkgew0KICAgICAgICAgICAgICBidG5GaW5pc2hBbmRBdmVyYWdlLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgICAgICAgIHNldFByb21wdChgQXZlcmFnZSBmYWlsZWQgYW5kIGZhbGxiYWNrIHByb21vdGUgZmFpbGVkOiAke2V9YCk7DQogICAgICAgICAgICB9DQogICAgICAgICAgfQ0KICAgICAgICB9KTsNCg0KICAgICAgICBhd2FpdCBpbnZva2UoInJ1bl9hdmVyYWdlIiwgeyBjb25maWc6IGF2ZXJhZ2VDb25maWcgfSk7DQogICAgICB9IGNhdGNoIChlKSB7DQogICAgICAgIGNvbnNvbGUuZXJyb3IoInJ1bl9hdmVyYWdlIGVycm9yOiIsIGUpOw0KICAgICAgICBidG5GaW5pc2hBbmRBdmVyYWdlLmRpc2FibGVkID0gZmFsc2U7DQogICAgICAgIHNldFByb21wdChgcnVuX2F2ZXJhZ2UgZXJyb3I6ICR7ZX1gKTsNCiAgICAgIH0NCiAgICB9KTsNCiAgfQ0KDQogIC8vIENhbGlicmF0ZSAvIGNvbmZpcm0gYnV0dG9uIOKAlCBzZW5kcyBzcGFjZSArIG5ld2xpbmUNCiAgaWYgKGJ0bkNhbGlicmF0ZSkgew0KICAgIGJ0bkNhbGlicmF0ZS5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsIGFzeW5jICgpID0+IHsNCiAgICAgIHRyeSB7DQogICAgICAgIGF3YWl0IGludm9rZSgic2VuZF9zdGRpbiIsIHsgaWQ6IGN1cnJlbnRQcm9jZXNzSWQsIGlucHV0OiAiIFxuIiB9KTsNCiAgICAgIH0gY2F0Y2ggKGUpIHsgY29uc29sZS5lcnJvcigic2VuZF9zdGRpbiBlcnJvcjoiLCBlKTsgfQ0KICAgIH0pOw0KICB9DQoNCiAgLy8gUmV0cnkgYnV0dG9uIOKAlCBzZW5kcyBzcGFjZSArIG5ld2xpbmUgKHNhbWUgYXMgY29uZmlybSkNCiAgaWYgKGJ0blJldHJ5KSB7DQogICAgYnRuUmV0cnkuYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBhc3luYyAoKSA9PiB7DQogICAgICB0cnkgew0KICAgICAgICBhd2FpdCBpbnZva2UoInNlbmRfc3RkaW4iLCB7IGlkOiBjdXJyZW50UHJvY2Vzc0lkLCBpbnB1dDogIiBcbiIgfSk7DQogICAgICAgIHNldFN0YXRlKFNUQVRFLlJFQURJTkcpOw0KICAgICAgICBzZXRQcm9tcHQoIlJldHJ5aW5nIHN0cmlwIHJlYWQuLi4iKTsNCiAgICAgIH0gY2F0Y2ggKGUpIHsgY29uc29sZS5lcnJvcigic2VuZF9zdGRpbiBlcnJvcjoiLCBlKTsgfQ0KICAgIH0pOw0KICB9DQoNCiAgLy8gU2tpcCBidXR0b24g4oCUIHNlbmRzICJzXG4iDQogIGlmIChidG5Ta2lwKSB7DQogICAgYnRuU2tpcC5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsIGFzeW5jICgpID0+IHsNCiAgICAgIHRyeSB7DQogICAgICAgIGF3YWl0IGludm9rZSgic2VuZF9zdGRpbiIsIHsgaWQ6IGN1cnJlbnRQcm9jZXNzSWQsIGlucHV0OiAic1xuIiB9KTsNCiAgICAgICAgc2V0U3RhdGUoU1RBVEUuQVdBSVRJTkdfU1RSSVApOw0KICAgICAgICBzZXRQcm9tcHQoIlNraXBwZWQgY3VycmVudCBzdHJpcC4gQXdhaXRpbmcgbmV4dC4uLiIpOw0KICAgICAgfSBjYXRjaCAoZSkgeyBjb25zb2xlLmVycm9yKCJzZW5kX3N0ZGluIGVycm9yOiIsIGUpOyB9DQogICAgfSk7DQogIH0NCg0KICAvLyBDYW5jZWwgYnV0dG9uIOKAlCBraWxscyB0aGUgcHJvY2Vzcw0KICBpZiAoYnRuQ2FuY2VsKSB7DQogICAgYnRuQ2FuY2VsLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgYXN5bmMgKCkgPT4gew0KICAgICAgdHJ5IHsNCiAgICAgICAgYXdhaXQgaW52b2tlKCJraWxsX3Byb2Nlc3MiLCB7IGlkOiBjdXJyZW50UHJvY2Vzc0lkIH0pOw0KICAgICAgICBzdG9wU3dhdGNoTGlzdGVuZXIoKTsNCiAgICAgICAgc2V0U3RhdGUocmVjb3JkZWRQYXNzZXMubGVuZ3RoID4gMCA/IFNUQVRFLkZJTklTSEVEIDogU1RBVEUuSURMRSk7DQogICAgICAgIHNldFByb21wdCgiTWVhc3VyZW1lbnQgY2FuY2VsbGVkLiIpOw0KICAgICAgfSBjYXRjaCAoZSkgew0KICAgICAgICBjb25zb2xlLmVycm9yKCJraWxsX3Byb2Nlc3MgZXJyb3I6IiwgZSk7DQogICAgICAgIHNldFByb21wdChgQ2FuY2VsIGZhaWxlZDogJHtlfWApOw0KICAgICAgfSBmaW5hbGx5IHsNCiAgICAgICAgc2V0TWVhc3VyZW1lbnRCdXN5KGZhbHNlKTsNCiAgICAgICAgc3RvcFN3YXRjaExpc3RlbmVyKCk7DQogICAgICB9DQogICAgfSk7DQogIH0NCg0KICAvLyBTdGFydCBpbiBJRExFIHN0YXRlDQogIHNldFN0YXRlKFNUQVRFLklETEUpOw0KICBzZXRQcm9tcHQoIlByZXNzICdTdGFydCBNZWFzdXJlbWVudCcgdG8gYmVnaW4gcmVhZGluZyB0aGUgcHJpbnRlZCB0YXJnZXQuIik7DQp9DQoNCmZ1bmN0aW9uIGFkdmFuY2VUb1N0YWdlNCgpIHsNCiAgY29uc3Qgc3RlcHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuc3RlcCcpOw0KICBjb25zdCBzdGFnZXMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuc3RhZ2UnKTsNCg0KICBzdGVwcy5mb3JFYWNoKHMgPT4gcy5jbGFzc0xpc3QucmVtb3ZlKCdhY3RpdmUnKSk7DQogIGlmIChzdGVwc1szXSkgew0KICAgIHN0ZXBzWzNdLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpOw0KICAgIHN0ZXBzWzNdLmNsYXNzTGlzdC5yZW1vdmUoJ2Rpc2FibGVkJyk7DQogIH0NCg0KICBzdGFnZXMuZm9yRWFjaChzID0+IHsNCiAgICBzLmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpOw0KICAgIHMuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7DQogIH0pOw0KICBpZiAoc3RhZ2VzWzNdKSB7DQogICAgc3RhZ2VzWzNdLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOw0KICAgIHN0YWdlc1szXS5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKTsNCiAgfQ0KfQ0K \ No newline at end of file -- 2.39.5 From 4c4440d7f610696092158c5d93b5a7547ea69a06 Mon Sep 17 00:00:00 2001 From: gronod <1+gronod@noreply@i3omb.com> Date: Fri, 28 Aug 2026 18:49:05 +0100 Subject: [PATCH 2/3] fix(stage3): update UI label for chartread comm port --- src/index.html | 568 +------------------------------------------------ 1 file changed, 1 insertion(+), 567 deletions(-) diff --git a/src/index.html b/src/index.html index bbefaf2..742fff3 100644 --- a/src/index.html +++ b/src/index.html @@ -1,567 +1 @@ - - - - - - ICCery - - - - - - - -
- - -
- -
-

Generate Target

-

Configure the colour patches for your profiling target.

- -
-
- -
- - -
-
- -
- - - -
- -
- -
-
- - -
-
- - -
-
-
- -
- -
- - -
- No directory selected (using default). -
-
- - - - -
- - - - - - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - +PCFET0NUWVBFIGh0bWw+DQo8aHRtbCBsYW5nPSJlbiI+DQogIDxoZWFkPg0KICAgIDxtZXRhIGNoYXJzZXQ9IlVURi04IiAvPg0KICAgIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wIiAvPg0KICAgIDx0aXRsZT5JQ0Nlcnk8L3RpdGxlPg0KICAgIDxsaW5rIHJlbD0iaWNvbiIgdHlwZT0iaW1hZ2Uvc3ZnK3htbCIgaHJlZj0iLi9hc3NldHMvYXBwLWljb24uc3ZnIiAvPg0KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iLi9zdHlsZXMvbWFpbi5jc3MiIC8+DQogICAgPHNjcmlwdCBzcmM9Ii4vanMvdGhyZWUubWluLmpzIj48L3NjcmlwdD4NCiAgICA8c2NyaXB0IHNyYz0iLi9qcy9PcmJpdENvbnRyb2xzLmpzIj48L3NjcmlwdD4NCiAgICA8c2NyaXB0IHR5cGU9Im1vZHVsZSIgc3JjPSIuL2pzL2FwcC5qcyIgZGVmZXI+PC9zY3JpcHQ+DQogIDwvaGVhZD4NCiAgPGJvZHk+DQogICAgPGRpdiBjbGFzcz0iYXBwLWNvbnRhaW5lciI+DQogICAgICA8YXNpZGUgY2xhc3M9InNpZGViYXIiPg0KICAgICAgICA8ZGl2IGNsYXNzPSJicmFuZCI+DQogICAgICAgICAgPGltZyBzcmM9Ii4vYXNzZXRzL0lDQ2VyeS1sb2dvLnN2ZyIgYWx0PSJJQ0NlcnkiIGNsYXNzPSJtYWluLWxvZ28iIC8+DQogICAgICAgICAgPGRpdiBjbGFzcz0iYnJhbmQtYWN0aW9ucyI+DQogICAgICAgICAgICA8YnV0dG9uIGlkPSJvcGVuU2V0dGluZ3NCdG4iIGNsYXNzPSJpY29uLWJ0biIgdGl0bGU9IlNldHRpbmdzIiBzdHlsZT0iZGlzcGxheTogZmxleDsgYWxpZ24taXRlbXM6IGNlbnRlcjsganVzdGlmeS1jb250ZW50OiBjZW50ZXI7Ij4NCiAgICAgICAgICAgICAgPHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj4NCiAgICAgICAgICAgICAgICA8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIzIj48L2NpcmNsZT4NCiAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuNCAxNWExLjY1IDEuNjUgMCAwIDAgLjMzIDEuODJsLjA2LjA2YTIgMiAwIDAgMSAwIDIuODMgMiAyIDAgMCAxLTIuODMgMGwtLjA2LS4wNmExLjY1IDEuNjUgMCAwIDAtMS44Mi0uMzMgMS42NSAxLjY1IDAgMCAwLTEgMS41MVYyMWEyIDIgMCAwIDEtMiAyIDIgMiAwIDAgMS0yLTJ2LS4wOUExLjY1IDEuNjUgMCAwIDAgOSAxOS40YTEuNjUgMS42NSAwIDAgMC0xLjgyLjMzbC0uMDYuMDZhMiAyIDAgMCAxLTIuODMgMCAyIDIgMCAwIDEgMC0yLjgzbC4wNi0uMDZhMS42NSAxLjY1IDAgMCAwIC4zMy0xLjgyIDEuNjUgMS42NSAwIDAgMC0xLjUxLTFIM2EyIDIgMCAwIDEtMi0yIDIgMiAwIDAgMSAyLTJoLjA5QTEuNjUgMS42NSAwIDAgMCA0LjYgOWExLjY1IDEuNjUgMCAwIDAtLjMzLTEuODJsLS4wNi0uMDZhMiAyIDAgMCAxIDAtMi44MyAyIDIgMCAwIDEgMi44MyAwbC4wNi4wNmExLjY1IDEuNjUgMCAwIDAgMS44Mi4zM0g5YTEuNjUgMS42NSAwIDAgMCAxLTEuNTFWM2EyIDIgMCAwIDEgMi0yIDIgMiAwIDAgMSAyIDJ2LjA5YTEuNjUgMS42NSAwIDAgMCAxIDEuNTEgMS42NSAxLjY1IDAgMCAwIDEuODItLjMzbC4wNi0uMDZhMiAyIDAgMCAxIDIuODMgMCAyIDIgMCAwIDEgMCAyLjgzbC0uMDYuMDZhMS42NSAxLjY1IDAgMCAwLS4zMyAxLjgyVjlhMS42NSAxLjY1IDAgMCAwIDEuNTEgMUgyMWEyIDIgMCAwIDEgMiAyIDIgMiAwIDAgMS0yIDJoLS4wOWExLjY1IDEuNjUgMCAwIDAtMS41MSAxeiI+PC9wYXRoPg0KICAgICAgICAgICAgICA8L3N2Zz4NCiAgICAgICAgICAgIDwvYnV0dG9uPg0KICAgICAgICAgICAgPGJ1dHRvbiBpZD0ib3BlbkFib3V0QnRuIiBjbGFzcz0iaWNvbi1idG4iIHRpdGxlPSJBYm91dCIgc3R5bGU9ImRpc3BsYXk6IGZsZXg7IGFsaWduLWl0ZW1zOiBjZW50ZXI7IGp1c3RpZnktY29udGVudDogY2VudGVyOyI+DQogICAgICAgICAgICAgIDxzdmcgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+DQogICAgICAgICAgICAgICAgPGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTAiPjwvY2lyY2xlPg0KICAgICAgICAgICAgICAgIDxsaW5lIHgxPSIxMiIgeTE9IjE2IiB4Mj0iMTIiIHkyPSIxMiI+PC9saW5lPg0KICAgICAgICAgICAgICAgIDxsaW5lIHgxPSIxMiIgeTE9IjgiIHgyPSIxMi4wMSIgeTI9IjgiPjwvbGluZT4NCiAgICAgICAgICAgICAgPC9zdmc+DQogICAgICAgICAgICA8L2J1dHRvbj4NCiAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgPCEtLSBQcm9maWxpbmcgUHJlc2V0IFNlbGVjdG9yIC0tPg0KICAgICAgICA8ZGl2IGNsYXNzPSJwcmVzZXQtc2VsZWN0b3ItY29udGFpbmVyIiBzdHlsZT0icGFkZGluZzogMCAxNnB4IDEycHg7IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCB2YXIoLS1ib3JkZXItY29sb3IsICMyYTJhMzApOyI+DQogICAgICAgICAgPGRpdiBzdHlsZT0iZGlzcGxheTogZmxleDsganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOyBhbGlnbi1pdGVtczogY2VudGVyOyBtYXJnaW4tYm90dG9tOiA2cHg7Ij4NCiAgICAgICAgICAgIDxsYWJlbCBmb3I9InByZXNldFNlbGVjdCIgc3R5bGU9ImZvbnQtc2l6ZTogMC43NXJlbTsgdGV4dC10cmFuc2Zvcm06IHVwcGVyY2FzZTsgbGV0dGVyLXNwYWNpbmc6IDAuMDVlbTsgb3BhY2l0eTogMC43OyBmb250LXdlaWdodDogNjAwOyI+UHJvZmlsaW5nIFByZXNldDwvbGFiZWw+DQogICAgICAgICAgICA8ZGl2IHN0eWxlPSJkaXNwbGF5OiBmbGV4OyBnYXA6IDRweDsiPg0KICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9ImJ1dHRvbiIgaWQ9ImJ0blNhdmVQcmVzZXRNb2RhbCIgY2xhc3M9Imljb24tYnRuIiB0aXRsZT0iU2F2ZSBjdXJyZW50IHNldHRpbmdzIGFzIFByZXNldCIgc3R5bGU9ImZvbnQtc2l6ZTogMC44NXJlbTsgcGFkZGluZzogMnB4IDRweDsgYm9yZGVyOiAxcHggc29saWQgdmFyKC0tYm9yZGVyLWNvbG9yLCAjMzMzKTsgYm9yZGVyLXJhZGl1czogNHB4OyBiYWNrZ3JvdW5kOiB0cmFuc3BhcmVudDsgY3Vyc29yOiBwb2ludGVyOyI+8J+SvjwvYnV0dG9uPg0KICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9ImJ1dHRvbiIgaWQ9ImJ0bk9wZW5QcmVzZXRzRGlhbG9nIiBjbGFzcz0iaWNvbi1idG4iIHRpdGxlPSJNYW5hZ2UgUHJlc2V0cyAoSW1wb3J0L0V4cG9ydC9EZWxldGUpIiBzdHlsZT0iZm9udC1zaXplOiAwLjg1cmVtOyBwYWRkaW5nOiAycHggNHB4OyBib3JkZXI6IDFweCBzb2xpZCB2YXIoLS1ib3JkZXItY29sb3IsICMzMzMpOyBib3JkZXItcmFkaXVzOiA0cHg7IGJhY2tncm91bmQ6IHRyYW5zcGFyZW50OyBjdXJzb3I6IHBvaW50ZXI7Ij7impnvuI88L2J1dHRvbj4NCiAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgIDxzZWxlY3QgaWQ9InByZXNldFNlbGVjdCIgc3R5bGU9IndpZHRoOiAxMDAlOyBmb250LXNpemU6IDAuODVyZW07IHBhZGRpbmc6IDZweCA4cHg7IGJvcmRlci1yYWRpdXM6IDZweDsgYmFja2dyb3VuZDogdmFyKC0tYmctY2FyZCwgIzFhMWEyMik7IGNvbG9yOiB2YXIoLS10ZXh0LWNvbG9yLCAjZWVlKTsgYm9yZGVyOiAxcHggc29saWQgdmFyKC0tYm9yZGVyLWNvbG9yLCAjMzMzKTsgY3Vyc29yOiBwb2ludGVyOyI+DQogICAgICAgICAgICA8b3B0aW9uIHZhbHVlPSIiIGRpc2FibGVkIHNlbGVjdGVkPkxvYWRpbmcgcHJlc2V0cy4uLjwvb3B0aW9uPg0KICAgICAgICAgIDwvc2VsZWN0Pg0KICAgICAgICA8L2Rpdj4NCg0KICAgICAgICA8bmF2IGNsYXNzPSJzdGVwcGVyIj4NCiAgICAgICAgICA8YnV0dG9uIGNsYXNzPSJzdGVwIGFjdGl2ZSIgZGF0YS1zdGVwPSIxIj4xLiBHZW5lcmF0ZSBUYXJnZXQ8L2J1dHRvbj4NCiAgICAgICAgICA8YnV0dG9uIGNsYXNzPSJzdGVwIiBkYXRhLXN0ZXA9IjIiPjIuIFByaW50IExheW91dDwvYnV0dG9uPg0KICAgICAgICAgIDxidXR0b24gY2xhc3M9InN0ZXAiIGRhdGEtc3RlcD0iMyI+My4gTWVhc3VyZSBUYXJnZXQ8L2J1dHRvbj4NCiAgICAgICAgICA8YnV0dG9uIGNsYXNzPSJzdGVwIiBkYXRhLXN0ZXA9IjQiIHRpdGxlPSJDb21wbGV0ZSBtZWFzdXJlbWVudCAoRmluaXNoKSBiZWZvcmUgcHJvZmlsaW5nIj40LiBDcmVhdGUgUHJvZmlsZTwvYnV0dG9uPg0KICAgICAgICAgIDxidXR0b24gY2xhc3M9InN0ZXAiIGRhdGEtc3RlcD0iNSI+NS4gVmVyaWZ5PC9idXR0b24+DQogICAgICAgIDwvbmF2Pg0KICAgICAgPC9hc2lkZT4NCiAgICAgIA0KICAgICAgPG1haW4gY2xhc3M9ImNvbnRlbnQiPg0KICAgICAgICA8IS0tIFN0YWdlIDE6IHRhcmdlbiAtLT4NCiAgICAgICAgPHNlY3Rpb24gaWQ9InN0YWdlLTEiIGNsYXNzPSJzdGFnZSBhY3RpdmUiPg0KICAgICAgICAgIDxoMj5HZW5lcmF0ZSBUYXJnZXQ8L2gyPg0KICAgICAgICAgIDxwPkNvbmZpZ3VyZSB0aGUgY29sb3VyIHBhdGNoZXMgZm9yIHlvdXIgcHJvZmlsaW5nIHRhcmdldC48L3A+DQogICAgICAgICAgDQogICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1jb250YWluZXIiPg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgICAgIDxsYWJlbD5Db2xvdXIgU3BhY2U8L2xhYmVsPg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJyYWRpby1ncm91cCI+DQogICAgICAgICAgICAgICAgPGxhYmVsPjxpbnB1dCB0eXBlPSJyYWRpbyIgbmFtZT0iY29sb3VyU3BhY2UiIHZhbHVlPSJyZ2IiIGNoZWNrZWQ+IFJHQiAoUHJpbnRlciBEcml2ZXIpPC9sYWJlbD4NCiAgICAgICAgICAgICAgICA8bGFiZWw+PGlucHV0IHR5cGU9InJhZGlvIiBuYW1lPSJjb2xvdXJTcGFjZSIgdmFsdWU9ImNteWsiPiBDTVlLIChSSVApPC9sYWJlbD4NCiAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgICAgIDxsYWJlbD5QYXRjaCBDb3VudDwvbGFiZWw+DQogICAgICAgICAgICAgIDxzZWxlY3QgaWQ9InBhdGNoQ291bnRQcmVzZXQiPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IjQwMCI+RHJhZnQgKDQwMCBwYXRjaGVzKTwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IjgwMCIgc2VsZWN0ZWQ+U3RhbmRhcmQgKDgwMCBwYXRjaGVzKTwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IjE1MDAiPkhpZ2ggUXVhbGl0eSAoMTUwMCBwYXRjaGVzKTwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IjI1MDAiPlVsdHJhICgyNTAwIHBhdGNoZXMpPC9vcHRpb24+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0iY3VzdG9tIj5DdXN0b20uLi48L29wdGlvbj4NCiAgICAgICAgICAgICAgPC9zZWxlY3Q+DQogICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJudW1iZXIiIGlkPSJwYXRjaENvdW50Q3VzdG9tIiB2YWx1ZT0iODAwIiBtaW49IjUwIiBtYXg9IjEwMDAwIiBjbGFzcz0iaGlkZGVuIiBzdHlsZT0ibWFyZ2luLXRvcDogOHB4OyI+DQogICAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgICAgIDxsYWJlbD5OZXV0cmFsIEF4aXMgQm9vc3QgKE9wdGlvbmFsKTwvbGFiZWw+DQogICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImlucHV0LXJvdyI+DQogICAgICAgICAgICAgICAgPGRpdj4NCiAgICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9IndoaXRlUGF0Y2hlcyIgY2xhc3M9InN1Yi1sYWJlbCI+V2hpdGUgUGF0Y2hlczwvbGFiZWw+DQogICAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0ibnVtYmVyIiBpZD0id2hpdGVQYXRjaGVzIiBtaW49IjAiIG1heD0iMTAwIiBwbGFjZWhvbGRlcj0iZS5nLiA0Ij4NCiAgICAgICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgICAgICA8ZGl2Pg0KICAgICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iYmxhY2tQYXRjaGVzIiBjbGFzcz0ic3ViLWxhYmVsIj5CbGFjayBQYXRjaGVzPC9sYWJlbD4NCiAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJudW1iZXIiIGlkPSJibGFja1BhdGNoZXMiIG1pbj0iMCIgbWF4PSIxMDAiIHBsYWNlaG9sZGVyPSJlLmcuIDgiPg0KICAgICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgIDwvZGl2Pg0KDQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJmb3JtLWdyb3VwIj4NCiAgICAgICAgICAgICAgPGxhYmVsPlByb2plY3QgRGlyZWN0b3J5ICYgRmlsZW5hbWU8L2xhYmVsPg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnB1dC1yb3cgaGFzLWJ0biI+DQogICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIGlkPSJ0YXJnZXRCYXNlbmFtZSIgcGxhY2Vob2xkZXI9ImUuZy4gbXlfcGFwZXJfcHJvZmlsZSI+DQogICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGlkPSJidG5Ccm93c2UiIGNsYXNzPSJzZWNvbmRhcnkiPkJyb3dzZS4uLjwvYnV0dG9uPg0KICAgICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgICAgPHNtYWxsIGlkPSJzZWxlY3RlZFBhdGhEaXNwbGF5IiBjbGFzcz0icGF0aC1kaXNwbGF5Ij5ObyBkaXJlY3Rvcnkgc2VsZWN0ZWQgKHVzaW5nIGRlZmF1bHQpLjwvc21hbGw+DQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgIDxidXR0b24gY2xhc3M9InByaW1hcnkiIGlkPSJidG5HZW5lcmF0ZSIgZGlzYWJsZWQ+R2VuZXJhdGUgKC50aTEpPC9idXR0b24+DQogICAgICAgICAgDQogICAgICAgICAgPGRldGFpbHMgY2xhc3M9ImxvZy1jb250YWluZXIgaGlkZGVuIiBpZD0idGFyZ2VuTG9nQ29udGFpbmVyIj4NCiAgICAgICAgICAgIDxzdW1tYXJ5PlByb2Nlc3MgT3V0cHV0PC9zdW1tYXJ5Pg0KICAgICAgICAgICAgPHByZSBpZD0idGFyZ2VuTG9nIj48L3ByZT4NCiAgICAgICAgICA8L2RldGFpbHM+DQogICAgICAgIDwvc2VjdGlvbj4NCg0KICAgICAgICA8IS0tIFN0YWdlIDI6IHByaW50dGFyZyAtLT4NCiAgICAgICAgPHNlY3Rpb24gaWQ9InN0YWdlLTIiIGNsYXNzPSJzdGFnZSBoaWRkZW4iPg0KICAgICAgICAgIDxoMj5QcmludCBMYXlvdXQ8L2gyPg0KICAgICAgICAgIDxwPkNvbmZpZ3VyZSB0aGUgdGFyZ2V0IGxheW91dCBmb3IgeW91ciBpbnN0cnVtZW50IGFuZCBwYXBlciBzaXplLCB0aGVuIGdlbmVyYXRlIHByaW50YWJsZSBUSUZGIGltYWdlcy48L3A+DQoNCiAgICAgICAgICA8IS0tIENvbG91ciBtYW5hZ2VtZW50IHdhcm5pbmcgYmFubmVyIC0tPg0KICAgICAgICAgIDxkaXYgY2xhc3M9Indhcm5pbmctYmFubmVyIiBpZD0iY21XYXJuaW5nQmFubmVyIj4NCiAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJ3YXJuaW5nLWljb24iPuKaoO+4jzwvc3Bhbj4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9Indhcm5pbmctdGV4dCI+DQogICAgICAgICAgICAgIDxzdHJvbmc+RGlzYWJsZSBDb2xvdXIgTWFuYWdlbWVudDwvc3Ryb25nPg0KICAgICAgICAgICAgICA8cD5TZXQgeW91ciBwcmludGVyIGRyaXZlciB0byAiTm8gQ29sb3VyIEFkanVzdG1lbnQiIChFcHNvbiksICJPZmYgKE5vIENvbG91ciBBZGp1c3RtZW50KSIgKENhbm9uKSwgb3IgdGhlIGVxdWl2YWxlbnQgc2V0dGluZyBmb3IgeW91ciBwcmludGVyLjwvcD4NCiAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KDQogICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1jb250YWluZXIiPg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgICAgIDxsYWJlbD5NZWFzdXJlbWVudCBJbnN0cnVtZW50PC9sYWJlbD4NCiAgICAgICAgICAgICAgPHNlbGVjdCBpZD0iaW5zdHJ1bWVudFNlbGVjdCI+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0iaTEiIHNlbGVjdGVkPlgtUml0ZSBpMVBybyAvIGkxUHJvMjwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9InAzIj5YLVJpdGUgaTFQcm8zKzwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IkNNIj5YLVJpdGUgQ29sb3JNdW5raTwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IlNTIj5YLVJpdGUgU3BlY3Ryb1NjYW48L29wdGlvbj4NCiAgICAgICAgICAgICAgICA8b3B0aW9uIHZhbHVlPSIyMCI+WC1SaXRlIERUUDIwPC9vcHRpb24+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0iMjIiPlgtUml0ZSBEVFAyMjwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IjQxIj5YLVJpdGUgRFRQNDE8L29wdGlvbj4NCiAgICAgICAgICAgICAgICA8b3B0aW9uIHZhbHVlPSI1MSI+WC1SaXRlIERUUDUxPC9vcHRpb24+DQogICAgICAgICAgICAgIDwvc2VsZWN0Pg0KICAgICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImZvcm0tZ3JvdXAiPg0KICAgICAgICAgICAgICA8bGFiZWw+UGFnZSBTaXplPC9sYWJlbD4NCiAgICAgICAgICAgICAgPHNlbGVjdCBpZD0icGFnZVNpemVTZWxlY3QiPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IkE0IiBzZWxlY3RlZD5BNCBbMjEwIMOXIDI5NyBtbV08L29wdGlvbj4NCiAgICAgICAgICAgICAgICA8b3B0aW9uIHZhbHVlPSJBNFIiPkE0IExhbmRzY2FwZSBbMjk3IMOXIDIxMCBtbV08L29wdGlvbj4NCiAgICAgICAgICAgICAgICA8b3B0aW9uIHZhbHVlPSJBMyI+QTMgWzI5NyDDlyA0MjAgbW1dPC9vcHRpb24+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0iQTIiPkEyIFs0MjAgw5cgNTk0IG1tXTwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IkxldHRlciI+VVMgTGV0dGVyIFsyMTUuOSDDlyAyNzkuNCBtbV08L29wdGlvbj4NCiAgICAgICAgICAgICAgICA8b3B0aW9uIHZhbHVlPSJMZXR0ZXJSIj5VUyBMZXR0ZXIgTGFuZHNjYXBlIFsyNzkuNCDDlyAyMTUuOSBtbV08L29wdGlvbj4NCiAgICAgICAgICAgICAgICA8b3B0aW9uIHZhbHVlPSJMZWdhbCI+VVMgTGVnYWwgWzIxNS45IMOXIDM1NS42IG1tXTwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IjR4NiI+NMOXNiBbMTAxLjYgw5cgMTUyLjQgbW1dPC9vcHRpb24+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0iMTF4MTciPjExw5cxNyBbMjc5LjQgw5cgNDMxLjggbW1dPC9vcHRpb24+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0iY3VzdG9tIj5DdXN0b20uLi48L29wdGlvbj4NCiAgICAgICAgICAgICAgPC9zZWxlY3Q+DQogICAgICAgICAgICAgIDxkaXYgaWQ9ImN1c3RvbVBhZ2VTaXplUm93IiBjbGFzcz0iaW5wdXQtcm93IGhpZGRlbiIgc3R5bGU9Im1hcmdpbi10b3A6IDhweDsiPg0KICAgICAgICAgICAgICAgIDxkaXY+DQogICAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSJjdXN0b21QYWdlVyIgY2xhc3M9InN1Yi1sYWJlbCI+V2lkdGggKG1tKTwvbGFiZWw+DQogICAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0ibnVtYmVyIiBpZD0iY3VzdG9tUGFnZVciIG1pbj0iNTAiIG1heD0iMjAwMCIgcGxhY2Vob2xkZXI9ImUuZy4gMjAwIj4NCiAgICAgICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgICAgICA8ZGl2Pg0KICAgICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iY3VzdG9tUGFnZUgiIGNsYXNzPSJzdWItbGFiZWwiPkhlaWdodCAobW0pPC9sYWJlbD4NCiAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJudW1iZXIiIGlkPSJjdXN0b21QYWdlSCIgbWluPSI1MCIgbWF4PSIyMDAwIiBwbGFjZWhvbGRlcj0iZS5nLiA0MDAiPg0KICAgICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgIDwvZGl2Pg0KDQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJmb3JtLWdyb3VwIj4NCiAgICAgICAgICAgICAgPGxhYmVsPkJpdCBEZXB0aDwvbGFiZWw+DQogICAgICAgICAgICAgIDxkaXYgY2xhc3M9InJhZGlvLWdyb3VwIj4NCiAgICAgICAgICAgICAgICA8bGFiZWw+PGlucHV0IHR5cGU9InJhZGlvIiBuYW1lPSJiaXREZXB0aCIgdmFsdWU9IjgiIGNoZWNrZWQ+IDgtYml0PC9sYWJlbD4NCiAgICAgICAgICAgICAgICA8bGFiZWw+PGlucHV0IHR5cGU9InJhZGlvIiBuYW1lPSJiaXREZXB0aCIgdmFsdWU9IjE2Ij4gMTYtYml0PC9sYWJlbD4NCiAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgIDxidXR0b24gY2xhc3M9InByaW1hcnkiIGlkPSJidG5DcmVhdGVMYXlvdXQiPkNyZWF0ZSBMYXlvdXQgKC50aTIgKyAudGlmKTwvYnV0dG9uPg0KDQogICAgICAgICAgPGRldGFpbHMgY2xhc3M9ImxvZy1jb250YWluZXIgaGlkZGVuIiBpZD0icHJpbnR0YXJnTG9nQ29udGFpbmVyIj4NCiAgICAgICAgICAgIDxzdW1tYXJ5PlByb2Nlc3MgT3V0cHV0PC9zdW1tYXJ5Pg0KICAgICAgICAgICAgPHByZSBpZD0icHJpbnR0YXJnTG9nIj48L3ByZT4NCiAgICAgICAgICA8L2RldGFpbHM+DQoNCiAgICAgICAgICA8IS0tIFRJRkYgcHJldmlldyBnYWxsZXJ5IChwb3B1bGF0ZWQgZHluYW1pY2FsbHkgYWZ0ZXIgZ2VuZXJhdGlvbikgLS0+DQogICAgICAgICAgPGRpdiBjbGFzcz0idGlmZi1nYWxsZXJ5IGhpZGRlbiIgaWQ9InRpZmZHYWxsZXJ5Ij4NCiAgICAgICAgICAgIDxoMz5HZW5lcmF0ZWQgVGFyZ2V0IFBhZ2VzPC9oMz4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImdhbGxlcnktaW5mbyIgaWQ9ImdhbGxlcnlJbmZvIj48L2Rpdj4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImdhbGxlcnktZ3JpZCIgaWQ9ImdhbGxlcnlHcmlkIj48L2Rpdj4NCiAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgIDwhLS0gTmF0aXZlIFJhdyBQcmludGluZyBFbmdpbmUgKEJ5cGFzcyBDb2xvdXIgTWFuYWdlbWVudCkgLS0+DQogICAgICAgICAgPGRpdiBjbGFzcz0icHJpbnQtcGFuZWwgaGlkZGVuIiBpZD0icmF3UHJpbnRQYW5lbCI+DQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJwcmludC1wYW5lbC1oZWFkZXIiPg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJwcmludC1wYW5lbC10aXRsZSI+DQogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9InByaW50LWljb24iPvCflqjvuI88L3NwYW4+DQogICAgICAgICAgICAgICAgPGgzPkRpcmVjdCBOYXRpdmUgUmF3IFByaW50aW5nPC9oMz4NCiAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgICAgPCEtLSBOb3RpZmljYXRpb24gLyBGZWVkYmFjayBCYW5uZXIgLS0+DQogICAgICAgICAgICA8ZGl2IGlkPSJwcmludE5vdGlmaWNhdGlvbiIgY2xhc3M9Im5vdGlmaWNhdGlvbi1iYW5uZXIgaGlkZGVuIj4NCiAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im5vdGlmaWNhdGlvbi1pY29uIiBpZD0icHJpbnROb3RpZmljYXRpb25JY29uIj7ihLnvuI88L3NwYW4+DQogICAgICAgICAgICAgIDxkaXYgY2xhc3M9Im5vdGlmaWNhdGlvbi10ZXh0IiBpZD0icHJpbnROb3RpZmljYXRpb25UZXh0Ij48L2Rpdj4NCiAgICAgICAgICAgIDwvZGl2Pg0KDQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJwcmludGVyLWNvbnRyb2xzLXJvdyI+DQogICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImZvcm0tZ3JvdXAgcHJpbnRlci1zZWxlY3QtZ3JvdXAiPg0KICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9InByaW50ZXJTZWxlY3QiPkRlc3RpbmF0aW9uIFByaW50ZXI8L2xhYmVsPg0KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9InByaW50ZXItc2VsZWN0LXdyYXBwZXIiPg0KICAgICAgICAgICAgICAgICAgPHNlbGVjdCBpZD0icHJpbnRlclNlbGVjdCI+DQogICAgICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IiIgZGlzYWJsZWQgc2VsZWN0ZWQ+RGV0ZWN0aW5nIHByaW50ZXJzLi4uPC9vcHRpb24+DQogICAgICAgICAgICAgICAgICA8L3NlbGVjdD4NCiAgICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBjbGFzcz0iYnRuLWljb24iIGlkPSJidG5SZWZyZXNoUHJpbnRlcnMiIHRpdGxlPSJSZWZyZXNoIHByaW50ZXIgbGlzdCI+4oa7PC9idXR0b24+DQogICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9ImJ1dHRvbiIgY2xhc3M9ImJ0bi1wcm9wZXJ0aWVzIiBpZD0iYnRuUHJpbnRlclByb3BlcnRpZXMiIHRpdGxlPSJPcGVuIG5hdGl2ZSBwcmludGVyIGRyaXZlciBwcmVmZXJlbmNlcyAoZGlzYWJsZSBjb2xvdXIgbWFuYWdlbWVudCwgc2V0IG1lZGlhIHR5cGUsIGV0Yy4pIj7impnvuI8gUHJlZmVyZW5jZXM8L2J1dHRvbj4NCiAgICAgICAgICAgICAgICAgIDxzcGFuIGlkPSJwcmludGVyU3RhdHVzQmFkZ2UiIGNsYXNzPSJzdGF0dXMtYmFkZ2UgYmFkZ2UtaWRsZSBoaWRkZW4iPklkbGU8L3NwYW4+DQogICAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICAgIDwvZGl2Pg0KDQogICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImZvcm0tZ3JvdXAgcHJpbnQtb3B0aW9ucy1ncm91cCIgaWQ9ImN1cHNPcHRpb25zR3JvdXAiPg0KICAgICAgICAgICAgICAgIDxsYWJlbCBjbGFzcz0iY2hlY2tib3gtbGFiZWwiIHRpdGxlPSJFbmFibGUgZm9yIHByaW50ZXJzIHRoYXQgZG8gbm90IGFjY2VwdCByYXcgVElGRiBzdHJlYW1zICh1c2VzIHVuY29ycmVjdGVkIENVUFMgcGFzc3Rocm91Z2gpIj4NCiAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9ImNoa1BwZEZhbGxiYWNrIj4NCiAgICAgICAgICAgICAgICAgIDxzcGFuPlBQRCBVbmNvcnJlY3RlZCBQYXNzdGhyb3VnaCAoRmFsbGJhY2spPC9zcGFuPg0KICAgICAgICAgICAgICAgIDwvbGFiZWw+DQogICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICAgIDwhLS0gTWVkaWEgJiBQcmludCBKb2IgQ29uZmlndXJhdGlvbiBSb3cgLS0+DQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJwcmludGVyLW1lZGlhLXJvdyI+DQogICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImZvcm0tZ3JvdXAgbWVkaWEtZ3JvdXAiPg0KICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9InByaW50ZXJUcmF5U2VsZWN0Ij5QYXBlciBTb3VyY2UgLyBUcmF5PC9sYWJlbD4NCiAgICAgICAgICAgICAgICA8c2VsZWN0IGlkPSJwcmludGVyVHJheVNlbGVjdCI+DQogICAgICAgICAgICAgICAgICA8b3B0aW9uIHZhbHVlPSIiIHNlbGVjdGVkPkRlZmF1bHQgLyBBdXRvIFNlbGVjdDwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDwvc2VsZWN0Pg0KICAgICAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJmb3JtLWdyb3VwIG9yaWVudGF0aW9uLWdyb3VwIj4NCiAgICAgICAgICAgICAgICA8bGFiZWw+T3JpZW50YXRpb248L2xhYmVsPg0KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9Im9yaWVudGF0aW9uLXRvZ2dsZS1ncm91cCI+DQogICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9ImJ1dHRvbiIgY2xhc3M9ImJ0bi10b2dnbGUgYWN0aXZlIiBpZD0iYnRuT3JpZW50UG9ydHJhaXQiIGRhdGEtb3JpZW50PSJwb3J0cmFpdCIgdGl0bGU9IlBvcnRyYWl0IG9yaWVudGF0aW9uIj7wn5OEIFBvcnRyYWl0PC9idXR0b24+DQogICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9ImJ1dHRvbiIgY2xhc3M9ImJ0bi10b2dnbGUiIGlkPSJidG5PcmllbnRMYW5kc2NhcGUiIGRhdGEtb3JpZW50PSJsYW5kc2NhcGUiIHRpdGxlPSJMYW5kc2NhcGUgb3JpZW50YXRpb24iPvCfk4QgTGFuZHNjYXBlPC9idXR0b24+DQogICAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICAgIDxkaXYgY2xhc3M9InByaW50LWFjdGlvbnMtcm93Ij4NCiAgICAgICAgICAgICAgPGJ1dHRvbiBjbGFzcz0icHJpbWFyeSBidG4tcHJpbnQiIGlkPSJidG5QcmludEFsbCI+DQogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9ImJ0bi10ZXh0Ij7wn5ao77iPIFByaW50IEFsbCBQYWdlcyAoQnlwYXNzIENNKTwvc3Bhbj4NCiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iYnRuLXNwaW5uZXIgaGlkZGVuIj48L3NwYW4+DQogICAgICAgICAgICAgIDwvYnV0dG9uPg0KICAgICAgICAgICAgICA8YnV0dG9uIGNsYXNzPSJzZWNvbmRhcnkiIGlkPSJidG5BZHZhbmNlVG9TdGFnZTMiPg0KICAgICAgICAgICAgICAgIDxzcGFuPlByb2NlZWQgdG8gTWVhc3VyZW1lbnQgKFN0YWdlIDMpIOKGkjwvc3Bhbj4NCiAgICAgICAgICAgICAgPC9idXR0b24+DQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgPC9zZWN0aW9uPg0KDQogICAgICAgIDwhLS0gU3RhZ2UgMzogY2hhcnRyZWFkIC0tPg0KICAgICAgICA8c2VjdGlvbiBpZD0ic3RhZ2UtMyIgY2xhc3M9InN0YWdlIGhpZGRlbiI+DQogICAgICAgICAgPGgyPk1lYXN1cmUgVGFyZ2V0PC9oMj4NCiAgICAgICAgICA8cD5SZWFkIHRoZSBwcmludGVkIHRhcmdldCBwYXRjaGVzIHdpdGggeW91ciBzcGVjdHJvcGhvdG9tZXRlci48L3A+DQoNCiAgICAgICAgICA8IS0tIEluc3RydW1lbnQgJiBIYXJkd2FyZSBQb3J0IFNlbGVjdG9yIC0tPg0KICAgICAgICAgIDxkaXYgY2xhc3M9ImZvcm0tY29udGFpbmVyIiBzdHlsZT0ibWFyZ2luLWJvdHRvbTogMTZweDsiPg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgICAgIDxsYWJlbCBmb3I9ImNoYXJ0cmVhZEluc3RydW1lbnRTZWxlY3QiPkNvbW11bmljYXRpb24gUG9ydDwvbGFiZWw+DQogICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImlucHV0LXJvdyIgc3R5bGU9ImRpc3BsYXk6IGZsZXg7IGdhcDogOHB4OyBhbGlnbi1pdGVtczogY2VudGVyOyI+DQogICAgICAgICAgICAgICAgPHNlbGVjdCBpZD0iY2hhcnRyZWFkSW5zdHJ1bWVudFNlbGVjdCIgc3R5bGU9ImZsZXg6IDE7Ij4NCiAgICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9IiI+QXV0byAoRmlyc3QgYXZhaWxhYmxlIHBvcnQpPC9vcHRpb24+DQogICAgICAgICAgICAgICAgPC9zZWxlY3Q+DQogICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJzZWNvbmRhcnkiIGlkPSJidG5EZXRlY3RJbnN0cnVtZW50cyIgdGl0bGU9IkRldGVjdCBjb25uZWN0ZWQgaW5zdHJ1bWVudHMgdmlhIGluc3RsaXN0Ij7ihrsgRGV0ZWN0PC9idXR0b24+DQogICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICA8IS0tIFN0YXRlICYgUHJvbXB0IEFyZWEgLS0+DQogICAgICAgICAgPGRpdiBjbGFzcz0iY2hhcnRyZWFkLXN0YXR1cyI+DQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJzdGF0dXMtbGFiZWwiPlN0YXRlOiA8c3BhbiBpZD0iY2hhcnRyZWFkU3RhdGUiPklETEU8L3NwYW4+PC9kaXY+DQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJzdGF0dXMtcHJvbXB0IiBpZD0iY2hhcnRyZWFkUHJvbXB0Ij4NCiAgICAgICAgICAgICAgUHJlc3MgJ1N0YXJ0IE1lYXN1cmVtZW50JyB0byBiZWdpbiByZWFkaW5nIHRoZSBwcmludGVkIHRhcmdldC4NCiAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KDQogICAgICAgICAgPCEtLSBBY3Rpb24gQnV0dG9ucyAtLT4NCiAgICAgICAgICA8ZGl2IGNsYXNzPSJjaGFydHJlYWQtYWN0aW9ucyI+DQogICAgICAgICAgICA8YnV0dG9uIGNsYXNzPSJwcmltYXJ5IiBpZD0iYnRuU3RhcnRSZWFkIj5TdGFydCBNZWFzdXJlbWVudDwvYnV0dG9uPg0KICAgICAgICAgICAgPGJ1dHRvbiBjbGFzcz0icHJpbWFyeSBoaWRkZW4iIGlkPSJidG5DYWxpYnJhdGUiPuKckyBDYWxpYnJhdGU8L2J1dHRvbj4NCiAgICAgICAgICAgIDxidXR0b24gY2xhc3M9InNlY29uZGFyeSBoaWRkZW4iIGlkPSJidG5SZXRyeSI+4oa7IFJldHJ5IFN0cmlwPC9idXR0b24+DQogICAgICAgICAgICA8YnV0dG9uIGNsYXNzPSJzZWNvbmRhcnkgaGlkZGVuIiBpZD0iYnRuU2tpcCI+4o+tIFNraXAgU3RyaXA8L2J1dHRvbj4NCiAgICAgICAgICAgIDxidXR0b24gY2xhc3M9ImRhbmdlciBoaWRkZW4iIGlkPSJidG5DYW5jZWwiPuKclSBDYW5jZWw8L2J1dHRvbj4NCiAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgIDwhLS0gUHJvZ3Jlc3MgQmFyIC0tPg0KICAgICAgICAgIDxkaXYgY2xhc3M9InJlYWQtcHJvZ3Jlc3MtY29udGFpbmVyIGhpZGRlbiIgaWQ9InJlYWRQcm9ncmVzc0NvbnRhaW5lciI+DQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJyZWFkLXByb2dyZXNzLWJhciIgaWQ9InJlYWRQcm9ncmVzcyIgc3R5bGU9IndpZHRoOiAwJSI+PC9kaXY+DQogICAgICAgICAgPC9kaXY+DQogICAgICAgICAgPGRpdiBjbGFzcz0icmVhZC1wcm9ncmVzcy10ZXh0IiBpZD0icmVhZFByb2dyZXNzVGV4dCI+PC9kaXY+DQoNCiAgICAgICAgICA8IS0tIExpdmUgU3RhdHMgLS0+DQogICAgICAgICAgPGRpdiBjbGFzcz0icmVhZC1zdGF0cyIgaWQ9InJlYWRTdGF0cyI+PC9kaXY+DQoNCiAgICAgICAgICA8IS0tIFN3YXRjaCBHcmlkIC0tPg0KICAgICAgICAgIDxkaXYgY2xhc3M9InN3YXRjaC1ncmlkIiBpZD0ic3dhdGNoR3JpZCI+PC9kaXY+DQoNCiAgICAgICAgICA8IS0tIE11bHRpLVBhc3MgU2Vzc2lvbiAmIEF2ZXJhZ2luZyBDb250cm9scyAtLT4NCiAgICAgICAgICA8ZGl2IGNsYXNzPSJhdmVyYWdpbmctcGFuZWwgaGlkZGVuIiBpZD0iY2hhcnRyZWFkQXZlcmFnaW5nUGFuZWwiIHN0eWxlPSJtYXJnaW4tdG9wOiAxNnB4OyBwYWRkaW5nOiAxNHB4OyBib3JkZXI6IDFweCBzb2xpZCB2YXIoLS1ib3JkZXItY29sb3IsICMzMzMpOyBib3JkZXItcmFkaXVzOiA4cHg7IGJhY2tncm91bmQ6IHJnYmEoMjU1LCAyNTUsIDI1NSwgMC4wMyk7Ij4NCiAgICAgICAgICAgIDxkaXYgc3R5bGU9ImRpc3BsYXk6IGZsZXg7IGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsgYWxpZ24taXRlbXM6IGNlbnRlcjsgbWFyZ2luLWJvdHRvbTogOHB4OyI+DQogICAgICAgICAgICAgIDxoMyBzdHlsZT0ibWFyZ2luOiAwOyBmb250LXNpemU6IDFyZW07Ij5NdWx0aS1QYXNzIFRhcmdldCBBdmVyYWdpbmc8L2gzPg0KICAgICAgICAgICAgICA8c3BhbiBpZD0icGFzc0NvdW50ZXJCYWRnZSIgY2xhc3M9InN0YXR1cy1iYWRnZSBiYWRnZS1pZGxlIj5QYXNzIDEgQ29tcGxldGU8L3NwYW4+DQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgIDxwIGNsYXNzPSJzdWItbGFiZWwiIHN0eWxlPSJtYXJnaW4tYm90dG9tOiAxMnB4OyI+RWFjaCBzdWNjZXNzZnVsIHNoZWV0IGlzIHNhdmVkIGFzIGEgc2VwYXJhdGUgX3Bhc3NOLnRpMyBmaWxlLiBTdGFnZSA0IHN0YXlzIGxvY2tlZCB1bnRpbCB5b3UgZmluaXNoLiBNZWFzdXJlIGV4dHJhIGNvcGllcyB0byByZWR1Y2Ugbm9pc2UsIHRoZW4gZmluaXNoIHRvIHdyaXRlIHRoZSBjYW5vbmljYWwgLnRpMy48L3A+DQogICAgICAgICAgICA8ZGl2IGlkPSJwYXNzZXNMaXN0IiBjbGFzcz0icGFzc2VzLWxpc3QiIHN0eWxlPSJkaXNwbGF5OiBmbGV4OyBmbGV4LWRpcmVjdGlvbjogY29sdW1uOyBnYXA6IDZweDsgbWFyZ2luLWJvdHRvbTogMTJweDsiPjwvZGl2Pg0KICAgICAgICAgICAgPGRpdiBzdHlsZT0iZGlzcGxheTogZmxleDsgZ2FwOiA4cHg7Ij4NCiAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJzZWNvbmRhcnkiIGlkPSJidG5NZWFzdXJlQW5vdGhlclNoZWV0Ij7inpUgTWVhc3VyZSBBbm90aGVyIFNoZWV0PC9idXR0b24+DQogICAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBjbGFzcz0icHJpbWFyeSIgaWQ9ImJ0bkZpbmlzaEFuZEF2ZXJhZ2UiIHRpdGxlPSJDb21wbGV0ZSBtZWFzdXJlbWVudCAoRmluaXNoKSBiZWZvcmUgcHJvZmlsaW5nIj7inJMgQXZlcmFnZSBQYXNzZXMgJmFtcDsgUHJvY2VlZCDihpI8L2J1dHRvbj4NCiAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KDQogICAgICAgICAgPCEtLSBQcm9jZXNzIExvZyAtLT4NCiAgICAgICAgICA8ZGV0YWlscyBjbGFzcz0ibG9nLWNvbnRhaW5lciBoaWRkZW4iIGlkPSJjaGFydHJlYWRMb2dDb250YWluZXIiPg0KICAgICAgICAgICAgPHN1bW1hcnk+UHJvY2VzcyBPdXRwdXQ8L3N1bW1hcnk+DQogICAgICAgICAgICA8cHJlIGlkPSJjaGFydHJlYWRMb2ciPjwvcHJlPg0KICAgICAgICAgIDwvZGV0YWlscz4NCiAgICAgICAgPC9zZWN0aW9uPg0KDQogICAgICAgIDwhLS0gU3RhZ2UgNDogY29scHJvZiAtLT4NCiAgICAgICAgPHNlY3Rpb24gaWQ9InN0YWdlLTQiIGNsYXNzPSJzdGFnZSBoaWRkZW4iPg0KICAgICAgICAgIDxoMj5DcmVhdGUgUHJvZmlsZTwvaDI+DQogICAgICAgICAgPHA+Q2FsY3VsYXRlIHRoZSBJQ0MgcHJvZmlsZSBmcm9tIHlvdXIgdGFyZ2V0IG1lYXN1cmVtZW50IGRhdGEuPC9wPg0KDQogICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1jb250YWluZXIiPg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgICAgIDxsYWJlbD5Qcm9maWxlIFF1YWxpdHk8L2xhYmVsPg0KICAgICAgICAgICAgICA8c2VsZWN0IGlkPSJjb2xwcm9mUXVhbGl0eSI+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0ibCI+TG93IChGYXN0IGRyYWZ0KTwvb3B0aW9uPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9Im0iIHNlbGVjdGVkPk1lZGl1bSAoU3RhbmRhcmQpPC9vcHRpb24+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0iaCI+SGlnaCAoUmVjb21tZW5kZWQpPC9vcHRpb24+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0idSI+VWx0cmEgKE1heGltdW0gcHJlY2lzaW9uIC0gc2xvdyk8L29wdGlvbj4NCiAgICAgICAgICAgICAgPC9zZWxlY3Q+DQogICAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgICAgIDxsYWJlbD5Qcm9maWxlIERlc2NyaXB0aW9uPC9sYWJlbD4NCiAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIGlkPSJjb2xwcm9mRGVzY3JpcHRpb24iIHBsYWNlaG9sZGVyPSJlLmcuIEVwc29uIFA2MDAgTHVzdHJlIFBob3RvIj4NCiAgICAgICAgICAgIDwvZGl2Pg0KDQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJmb3JtLWdyb3VwIj4NCiAgICAgICAgICAgICAgPGxhYmVsPkNvcHlyaWdodCAvIENyZWF0b3IgKE9wdGlvbmFsKTwvbGFiZWw+DQogICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBpZD0iY29scHJvZkNvcHlyaWdodCIgcGxhY2Vob2xkZXI9ImUuZy4gQ29weXJpZ2h0IChjKSAyMDI2IE15IFN0dWRpbyI+DQogICAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgICAgIDxsYWJlbD5BbGdvcml0aG0gVHlwZTwvbGFiZWw+DQogICAgICAgICAgICAgIDxzZWxlY3QgaWQ9ImNvbHByb2ZBbGdvcml0aG0iPg0KICAgICAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9ImwiIHNlbGVjdGVkPkxhYiBjTFVUIChTdGFuZGFyZCBMb29rdXAgVGFibGUpPC9vcHRpb24+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0ieCI+WFlaIGNMVVQ8L29wdGlvbj4NCiAgICAgICAgICAgICAgICA8b3B0aW9uIHZhbHVlPSJYIj5EaXNwbGF5IFhZWiBjTFVUICsgTWF0cml4PC9vcHRpb24+DQogICAgICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT0ibSI+TWF0cml4IE9ubHkgKFNpbXBsZSk8L29wdGlvbj4NCiAgICAgICAgICAgICAgPC9zZWxlY3Q+DQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgIDxidXR0b24gY2xhc3M9InByaW1hcnkiIGlkPSJidG5DcmVhdGVQcm9maWxlIj5DYWxjdWxhdGUgUHJvZmlsZSAoLmljYyk8L2J1dHRvbj4NCg0KICAgICAgICAgIDwhLS0gU3Bpbm5lciBhbmQgUHJvZ3Jlc3MgU3RhZ2UgLS0+DQogICAgICAgICAgPGRpdiBjbGFzcz0ic3Bpbm5lci1jb250YWluZXIgaGlkZGVuIiBpZD0iY29scHJvZlNwaW5uZXJDb250YWluZXIiPg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0ic3Bpbm5lciI+PC9kaXY+DQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJzdGFnZS1sYWJlbCIgaWQ9ImNvbHByb2ZTdGFnZUxhYmVsIj5DYWxjdWxhdGluZyBJQ0MgUHJvZmlsZS4uLjwvZGl2Pg0KICAgICAgICAgIDwvZGl2Pg0KDQogICAgICAgICAgPCEtLSBTdWNjZXNzIENhcmQgLS0+DQogICAgICAgICAgPGRpdiBjbGFzcz0ic3VjY2Vzcy1jYXJkIGhpZGRlbiIgaWQ9ImNvbHByb2ZTdWNjZXNzQ2FyZCI+DQogICAgICAgICAgICA8aDM+8J+OiSBQcm9maWxlIENhbGN1bGF0aW9uIENvbXBsZXRlPC9oMz4NCiAgICAgICAgICAgIDxwIGlkPSJjb2xwcm9mU3VjY2Vzc0luZm8iPjwvcD4NCiAgICAgICAgICAgIDxidXR0b24gY2xhc3M9InByaW1hcnkiIGlkPSJidG5Hb1RvVmVyaWZ5Ij5Qcm9jZWVkIHRvIFZlcmlmaWNhdGlvbiDihpI8L2J1dHRvbj4NCiAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgIDwhLS0gUHJvY2VzcyBMb2cgLS0+DQogICAgICAgICAgPGRldGFpbHMgY2xhc3M9ImxvZy1jb250YWluZXIgaGlkZGVuIiBpZD0iY29scHJvZkxvZ0NvbnRhaW5lciI+DQogICAgICAgICAgICA8c3VtbWFyeT5Qcm9jZXNzIE91dHB1dDwvc3VtbWFyeT4NCiAgICAgICAgICAgIDxwcmUgaWQ9ImNvbHByb2ZMb2ciPjwvcHJlPg0KICAgICAgICAgIDwvZGV0YWlscz4NCiAgICAgICAgPC9zZWN0aW9uPg0KDQogICAgICAgIDwhLS0gU3RhZ2UgNTogcHJvZmNoZWNrIC0tPg0KICAgICAgICA8c2VjdGlvbiBpZD0ic3RhZ2UtNSIgY2xhc3M9InN0YWdlIGhpZGRlbiI+DQogICAgICAgICAgPGgyPlZlcmlmeSBQcm9maWxlPC9oMj4NCiAgICAgICAgICA8cD5DaGVjayB0aGUgbnVtZXJpY2FsIGFjY3VyYWN5IG9mIHlvdXIgcHJvZmlsZSBhZ2FpbnN0IHRoZSBvcmlnaW5hbCBtZWFzdXJlbWVudCBkYXRhLjwvcD4NCg0KICAgICAgICAgIDxidXR0b24gY2xhc3M9InByaW1hcnkiIGlkPSJidG5WZXJpZnkiPlJ1biBQcm9maWxlIFZlcmlmaWNhdGlvbjwvYnV0dG9uPg0KDQogICAgICAgICAgPCEtLSBSZXBvcnQgQ2FyZCAtLT4NCiAgICAgICAgICA8ZGl2IGNsYXNzPSJyZXBvcnQtY2FyZCBoaWRkZW4iIGlkPSJwcm9mY2hlY2tSZXBvcnRDYXJkIj4NCiAgICAgICAgICAgIDxkaXYgY2xhc3M9InJlcG9ydC1oZWFkZXIiPg0KICAgICAgICAgICAgICA8aDM+UHJvZmlsZSBRdWFsaXR5IFJlcG9ydDwvaDM+DQogICAgICAgICAgICAgIDxkaXYgaWQ9InByb2ZjaGVja0JhZGdlIiBjbGFzcz0icmVwb3J0LWJhZGdlIj5HT09EPC9kaXY+DQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgIA0KICAgICAgICAgICAgPGRpdiBjbGFzcz0icmVwb3J0LW1ldHJpY3MiPg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJtZXRyaWMtYm94Ij4NCiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibWV0cmljLXZhbHVlIiBpZD0icHJvZmNoZWNrQXZnRGUiPjAuMDA8L3NwYW4+DQogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im1ldHJpYy1sYWJlbCI+QXZlcmFnZSDOlEXigoDigoA8L3NwYW4+DQogICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJtZXRyaWMtYm94Ij4NCiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibWV0cmljLXZhbHVlIiBpZD0icHJvZmNoZWNrTWF4RGUiPjAuMDA8L3NwYW4+DQogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im1ldHJpYy1sYWJlbCI+UGVhayDOlEXigoDigoA8L3NwYW4+DQogICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJtZXRyaWMtYm94Ij4NCiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibWV0cmljLXZhbHVlIiBpZD0icHJvZmNoZWNrUm1zRGUiPjAuMDA8L3NwYW4+DQogICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im1ldHJpYy1sYWJlbCI+Uk1TIM6UReKCgOKCgDwvc3Bhbj4NCiAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgICAgPGRpdiBjbGFzcz0icmVwb3J0LWZvb3Rub3RlIj4NCiAgICAgICAgICAgICAgUHJvZmVzc2lvbmFsIHByaW50ZXIgcHJvZmlsZXMgdHlwaWNhbGx5IGFjaGlldmUgYXZlcmFnZSDOlEXigoDigoAgJmx0OyAxLjAsIGFuZCBhY2NlcHRhYmxlIHByb2ZpbGVzICZsdDsgMi4wLg0KICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICA8IS0tIDNEIEdhbXV0IFZpZXdlciAtLT4NCiAgICAgICAgICA8ZGl2IGNsYXNzPSJnYW11dC12aWV3ZXItc2VjdGlvbiI+DQogICAgICAgICAgICA8aDM+M0QgR2FtdXQgVmlzdWFsaXphdGlvbiAoQ0lFTEFCKTwvaDM+DQogICAgICAgICAgICA8ZGl2IGlkPSJnYW11dFZpZXdlckNvbnRhaW5lciI+PC9kaXY+DQogICAgICAgICAgPC9kaXY+DQoNCiAgICAgICAgICA8IS0tIFByb2Nlc3MgTG9nIC0tPg0KICAgICAgICAgIDxkZXRhaWxzIGNsYXNzPSJsb2ctY29udGFpbmVyIGhpZGRlbiIgaWQ9InByb2ZjaGVja0xvZ0NvbnRhaW5lciI+DQogICAgICAgICAgICA8c3VtbWFyeT5WZXJpZmljYXRpb24gT3V0cHV0PC9zdW1tYXJ5Pg0KICAgICAgICAgICAgPHByZSBpZD0icHJvZmNoZWNrTG9nIj48L3ByZT4NCiAgICAgICAgICA8L2RldGFpbHM+DQogICAgICAgIDwvc2VjdGlvbj4NCiAgICAgIDwvbWFpbj4NCiAgICA8L2Rpdj4NCg0KICAgIDwhLS0gU2V0dGluZ3MgRGlhbG9nIC0tPg0KICAgIDxkaWFsb2cgaWQ9InNldHRpbmdzRGlhbG9nIiBjbGFzcz0ic2V0dGluZ3MtbW9kYWwiPg0KICAgICAgPGRpdiBjbGFzcz0ibW9kYWwtY29udGVudCI+DQogICAgICAgIDxoMj5BcHBsaWNhdGlvbiBTZXR0aW5nczwvaDI+DQogICAgICAgIDxkaXYgY2xhc3M9ImZvcm0tZ3JvdXAiPg0KICAgICAgICAgIDxsYWJlbCBmb3I9ImFyZ3lsbF9iaW5hcnlfZGlyIj5Bcmd5bGxDTVMgQmluYXJ5IERpcmVjdG9yeSBPdmVycmlkZTwvbGFiZWw+DQogICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIGlkPSJhcmd5bGxfYmluYXJ5X2RpciIgcGxhY2Vob2xkZXI9IkxlYXZlIGVtcHR5IGZvciBidW5kbGVkIHNpZGVjYXJzIChlLmcuIC91c3IvYmluKSI+DQogICAgICAgIDwvZGl2Pg0KICAgICAgICA8ZGl2IGNsYXNzPSJmb3JtLWdyb3VwIj4NCiAgICAgICAgICA8bGFiZWwgZm9yPSJkZWZhdWx0X2luc3RydW1lbnQiPkRlZmF1bHQgSW5zdHJ1bWVudCBPdmVycmlkZTwvbGFiZWw+DQogICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIGlkPSJkZWZhdWx0X2luc3RydW1lbnQiIHBsYWNlaG9sZGVyPSJlLmcuIGkxIj4NCiAgICAgICAgPC9kaXY+DQogICAgICAgIDxkaXYgY2xhc3M9Im1vZGFsLWFjdGlvbnMiPg0KICAgICAgICAgIDxidXR0b24gaWQ9InNhdmVTZXR0aW5nc0J0biIgY2xhc3M9InByaW1hcnkiPlNhdmUgU2V0dGluZ3M8L2J1dHRvbj4NCiAgICAgICAgICA8YnV0dG9uIGlkPSJjbG9zZVNldHRpbmdzQnRuIiBjbGFzcz0ic2Vjb25kYXJ5Ij5DYW5jZWw8L2J1dHRvbj4NCiAgICAgICAgPC9kaXY+DQogICAgICA8L2Rpdj4NCiAgICA8L2RpYWxvZz4NCg0KICAgIDwhLS0gQWJvdXQgRGlhbG9nIC0tPg0KICAgIDxkaWFsb2cgaWQ9ImFib3V0RGlhbG9nIiBjbGFzcz0ic2V0dGluZ3MtbW9kYWwgYWJvdXQtbW9kYWwiPg0KICAgICAgPGRpdiBjbGFzcz0ibW9kYWwtY29udGVudCI+DQogICAgICAgIDxkaXYgY2xhc3M9ImFib3V0LWxvZ28tY29udGFpbmVyIj4NCiAgICAgICAgICA8aW1nIHNyYz0iLi9hc3NldHMvSUNDZXJ5LWxvZ28uc3ZnIiBhbHQ9IklDQ2VyeSBMb2dvIiBjbGFzcz0iYWJvdXQtbG9nbyIgLz4NCiAgICAgICAgPC9kaXY+DQogICAgICAgIDxoMj5BYm91dCBJQ0Nlcnk8L2gyPg0KICAgICAgICA8cD48c3Ryb25nPlZlcnNpb246PC9zdHJvbmc+IDxzcGFuIGlkPSJhYm91dFZlcnNpb24iPnYwLjMuMjwvc3Bhbj4gJmJ1bGw7IDxzdHJvbmc+QnVpbGQgRGF0ZTo8L3N0cm9uZz4gPHNwYW4gaWQ9ImFib3V0QnVpbGREYXRlIj5BdWd1c3QgMjAyNjwvc3Bhbj48L3A+DQogICAgICAgIDxwPjxzdHJvbmc+Q29weXJpZ2h0ICZjb3B5OyAyMDI2IEdvcmRvbiBCb2x0b24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuPC9zdHJvbmc+PC9wPg0KICAgICAgICA8aDM+TGljZW5jZXMgJiBFVUxBPC9oMz4NCiAgICAgICAgPGRpdiBjbGFzcz0ibGljZW5zZS10ZXh0LWNvbnRhaW5lciI+DQogICAgICAgICAgPGg0PkVuZC1Vc2VyIExpY2VuY2UgQWdyZWVtZW50IChFVUxBKSBmb3IgSUNDZXJ5PC9oND4NCiAgICAgICAgICA8cD48c3Ryb25nPklNUE9SVEFOVCDigJQgUkVBRCBDQVJFRlVMTFk6PC9zdHJvbmc+IFRoaXMgRW5kLVVzZXIgTGljZW5jZSBBZ3JlZW1lbnQgKCJFVUxBIikgaXMgYSBsZWdhbCBhZ3JlZW1lbnQgYmV0d2VlbiB5b3UgKGVpdGhlciBhbiBpbmRpdmlkdWFsIG9yIGEgc2luZ2xlIGVudGl0eSkgYW5kIHRoZSBkZXZlbG9wZXIgb2YgSUNDZXJ5LCBHb3Jkb24gQm9sdG9uICgiTGljZW5zb3IiKSBmb3IgdGhlIHNvZnR3YXJlIHByb2R1Y3QgaWRlbnRpZmllZCBhYm92ZSwgd2hpY2ggaW5jbHVkZXMgY29tcHV0ZXIgc29mdHdhcmUgYW5kIG1heSBpbmNsdWRlIGFzc29jaWF0ZWQgbWVkaWEsIHByaW50ZWQgbWF0ZXJpYWxzLCBhbmQgIm9ubGluZSIgb3IgZWxlY3Ryb25pYyBkb2N1bWVudGF0aW9uICgiU29mdHdhcmUiKS48L3A+DQogICAgICAgICAgPHA+QnkgaW5zdGFsbGluZywgY29weWluZywgb3Igb3RoZXJ3aXNlIHVzaW5nIHRoZSBTb2Z0d2FyZSwgeW91IGFncmVlIHRvIGJlIGJvdW5kIGJ5IHRoZSB0ZXJtcyBvZiB0aGlzIEVVTEEuIElmIHlvdSBkbyBub3QgYWdyZWUgdG8gdGhlIHRlcm1zIG9mIHRoaXMgRVVMQSwgZG8gbm90IGluc3RhbGwgb3IgdXNlIHRoZSBTb2Z0d2FyZS48L3A+DQoNCiAgICAgICAgICA8aDU+MS4gR1JBTlQgT0YgTElDRU5DRTwvaDU+DQogICAgICAgICAgPHA+TGljZW5zb3IgZ3JhbnRzIHlvdSBhIHJldm9jYWJsZSwgbm9uLWV4Y2x1c2l2ZSwgbm9uLXRyYW5zZmVyYWJsZSwgbGltaXRlZCBjb21tZXJjaWFsIGxpY2VuY2UgdG8gZG93bmxvYWQsIGluc3RhbGwsIGFuZCB1c2UgdGhlIElDQ2VyeSBncmFwaGljYWwgdXNlciBpbnRlcmZhY2UgKEdVSSkgc29mdHdhcmUgc3RyaWN0bHkgaW4gYWNjb3JkYW5jZSB3aXRoIHRoZSB0ZXJtcyBvZiB0aGlzIEFncmVlbWVudC48L3A+DQoNCiAgICAgICAgICA8aDU+Mi4gUkVTVFJJQ1RJT05TPC9oNT4NCiAgICAgICAgICA8cD5Zb3UgYWdyZWUgbm90IHRvLCBhbmQgeW91IHdpbGwgbm90IHBlcm1pdCBvdGhlcnMgdG86PC9wPg0KICAgICAgICAgIDx1bD4NCiAgICAgICAgICAgIDxsaT5MaWNlbmNlLCBzZWxsLCByZW50LCBsZWFzZSwgYXNzaWduLCBkaXN0cmlidXRlLCB0cmFuc21pdCwgaG9zdCwgb3V0c291cmNlLCBkaXNjbG9zZSwgb3Igb3RoZXJ3aXNlIGNvbW1lcmNpYWxseSBleHBsb2l0IHRoZSBJQ0NlcnkgR1VJLjwvbGk+DQogICAgICAgICAgICA8bGk+TW9kaWZ5LCBtYWtlIGRlcml2YXRpdmUgd29ya3Mgb2YsIGRpc2Fzc2VtYmxlLCBkZWNyeXB0LCByZXZlcnNlIGNvbXBpbGUsIG9yIHJldmVyc2UgZW5naW5lZXIgYW55IHBhcnQgb2YgdGhlIHByb3ByaWV0YXJ5IElDQ2VyeSBHVUkgc29mdHdhcmUuPC9saT4NCiAgICAgICAgICAgIDxsaT5SZW1vdmUsIGFsdGVyLCBvciBvYnNjdXJlIGFueSBwcm9wcmlldGFyeSBub3RpY2UgKGluY2x1ZGluZyBhbnkgbm90aWNlIG9mIGNvcHlyaWdodCBvciB0cmFkZW1hcmspIG9mIExpY2Vuc29yIG9yIGl0cyBhZmZpbGlhdGVzLCBwYXJ0bmVycywgc3VwcGxpZXJzLCBvciB0aGUgbGljZW5zb3JzIG9mIHRoZSBTb2Z0d2FyZS48L2xpPg0KICAgICAgICAgIDwvdWw+DQoNCiAgICAgICAgICA8aDU+My4gSU5URUxMRUNUVUFMIFBST1BFUlRZPC9oNT4NCiAgICAgICAgICA8cD5UaGUgSUNDZXJ5IEdVSSAoZXhjbHVkaW5nIHRoaXJkLXBhcnR5IGNvbXBvbmVudHMgZGVzY3JpYmVkIGJlbG93KSBpcyBwcm9wcmlldGFyeSBzb2Z0d2FyZS4gQWxsIGNvcHlyaWdodHMsIHBhdGVudHMsIHRyYWRlbWFya3MsIHRyYWRlIHNlY3JldHMsIGFuZCBvdGhlciBpbnRlbGxlY3R1YWwgcHJvcGVydHkgcmlnaHRzIGluIHRoZSBJQ0NlcnkgR1VJIGFyZSBhbmQgc2hhbGwgcmVtYWluIHRoZSBzb2xlIGFuZCBleGNsdXNpdmUgcHJvcGVydHkgb2YgdGhlIExpY2Vuc29yIChHb3Jkb24gQm9sdG9uKS48L3A+DQoNCiAgICAgICAgICA8aDU+NC4gVEhJUkQtUEFSVFkgQ09NUE9ORU5UUyAmYW1wOyBBR1BMIENPTVBMSUFOQ0U8L2g1Pg0KICAgICAgICAgIDxwPklDQ2VyeSByZWxpZXMgb24gYW5kIGludGVyYWN0cyB3aXRoIGEgbW9kaWZpZWQgdmVyc2lvbiBvZiA8c3Ryb25nPkFyZ3lsbENNUzwvc3Ryb25nPiwgYW4gb3Blbi1zb3VyY2UgY29sb3VyIG1hbmFnZW1lbnQgc3lzdGVtLjwvcD4NCg0KICAgICAgICAgIDxoNj5Bcm1zLUxlbmd0aCBJbnRlZ3JhdGlvbjwvaDY+DQogICAgICAgICAgPHA+VGhlIElDQ2VyeSBzb2Z0d2FyZSBhcmNoaXRlY3R1cmUgbWFpbnRhaW5zIGEgc3RyaWN0LCBsb29zZWx5LWNvdXBsZWQgc2VwYXJhdGlvbiBmcm9tIEFyZ3lsbENNUy4gVGhlIHByb3ByaWV0YXJ5IElDQ2VyeSBHVUkgb3BlcmF0ZXMgYXMgYSBkaXN0aW5jdCwgc3RhbmRhbG9uZSBwcm9jZXNzIHRoYXQgaW50ZXJhY3RzIHdpdGggdGhlIEFyZ3lsbENNUyBiaW5hcmllcyBzdHJpY3RseSBhdCBhcm0ncyBsZW5ndGggdmlhIHN0YW5kYXJkIGludGVyLXByb2Nlc3MgY29tbXVuaWNhdGlvbi4gU3BlY2lmaWNhbGx5LCBJQ0NlcnkgZXhlY3V0ZXMgdGhlIG1vZGlmaWVkIEFyZ3lsbENNUyBiaW5hcnkgYW5kIHJlY2VpdmVzIHJlYWwtdGltZSwgbGluZS1ieS1saW5lIHBhdGNoIG1lYXN1cmVtZW50IHNwZWN0cmFsIGRhdGEgcGlwZWQgdGhyb3VnaCBzdGFuZGFyZCBvdXRwdXQgKHN0ZG91dCkuPC9wPg0KICAgICAgICAgIDxwPkJlY2F1c2Ugb2YgdGhpcyBzZXBhcmF0ZSBwcm9jZXNzIGFyY2hpdGVjdHVyZSwgdGhlIHByb3ByaWV0YXJ5IGNvbW1lcmNpYWwgbGljZW5jZSBvZiBJQ0NlcnkgZG9lcyBub3QgZXh0ZW5kIHRvIEFyZ3lsbENNUywgYW5kIHRoZSBBR1BMdjMgbGljZW5jZSBvZiBBcmd5bGxDTVMgZG9lcyBub3QgZXh0ZW5kIHRvIG9yIGluZmVjdCB0aGUgcHJvcHJpZXRhcnkgSUNDZXJ5IEdVSSB3cmFwcGVyLjwvcD4NCg0KICAgICAgICAgIDxoNj5Bcmd5bGxDTVMgTGljZW5jZSAoQUdQTHYzKTwvaDY+DQogICAgICAgICAgPHA+QXJneWxsQ01TIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBHTlUgQWZmZXJvIEdlbmVyYWwgUHVibGljIExpY2VuY2UgdmVyc2lvbiAzLjAgKEFHUEx2MykuPC9wPg0KICAgICAgICAgIDx1bD4NCiAgICAgICAgICAgIDxsaT5UaGUgb3JpZ2luYWwgQXJneWxsQ01TIHNvZnR3YXJlIGlzIGNvcHlyaWdodCAoYykgR3JhZW1lIFcuIEdpbGwuPC9saT4NCiAgICAgICAgICAgIDxsaT5JbiBjb21wbGlhbmNlIHdpdGggdGhlIEFHUEx2MywgdGhlIG1vZGlmaWNhdGlvbnMgbWFkZSB0byB0aGUgQXJneWxsQ01TIEMgY29kZWJhc2UgdG8gaW1wbGVtZW50IHRoZSBjdXN0b20gY29tbWFuZC1saW5lIHN3aXRjaCBmb3IgcmVhbC10aW1lIGRhdGEgb3V0cHV0IHJlbWFpbiBmdWxseSBzdWJqZWN0IHRvIHRoZSBBR1BMdjMuPC9saT4NCiAgICAgICAgICAgIDxsaT48c3Ryb25nPlNvdXJjZSBDb2RlIEF2YWlsYWJpbGl0eTo8L3N0cm9uZz4gWW91IGFyZSBlbnRpdGxlZCB0byB0aGUgY29tcGxldGUgbWFjaGluZS1yZWFkYWJsZSBzb3VyY2UgY29kZSBvZiB0aGUgbW9kaWZpZWQgQXJneWxsQ01TIGJpbmFyaWVzIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyBzb2Z0d2FyZS4gVGhlIHNvdXJjZSBjb2RlIGZvciB0aGUgbW9kaWZpZWQgQXJneWxsQ01TIGNvbXBvbmVudCBjYW4gYmUgb2J0YWluZWQgYXQ6IDxjb2RlPmh0dHBzOi8vZ2l0Lmkzb21iLmNvbS9ncm9ub2QvYXJneWxsY21zPC9jb2RlPi48L2xpPg0KICAgICAgICAgIDwvdWw+DQoNCiAgICAgICAgICA8aDU+NS4gRElTQ0xBSU1FUiBPRiBXQVJSQU5UWTwvaDU+DQogICAgICAgICAgPHAgY2xhc3M9ImxpY2Vuc2UtbGVnYWwtYmxvY2siPlRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCAiQVMgSVMiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZLCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSwgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMsIE9SIE9USEVSIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQsIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLCBPVVQgT0YsIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUgU09GVFdBUkUuPC9wPg0KDQogICAgICAgICAgPGg1PjYuIExJTUlUQVRJT04gT0YgTElBQklMSVRZPC9oNT4NCiAgICAgICAgICA8cCBjbGFzcz0ibGljZW5zZS1sZWdhbC1ibG9jayI+Tk9UV0lUSFNUQU5ESU5HIEFOWSBEQU1BR0VTIFRIQVQgWU9VIE1JR0hUIElOQ1VSLCBUSEUgRU5USVJFIExJQUJJTElUWSBPRiBMSUNFTlNPUiBBTkQgQU5ZIE9GIElUUyBTVVBQTElFUlMgVU5ERVIgQU5ZIFBST1ZJU0lPTiBPRiBUSElTIEVVTEEgQU5EIFlPVVIgRVhDTFVTSVZFIFJFTUVEWSBGT1IgQUxMIE9GIFRIRSBGT1JFR09JTkcgU0hBTEwgQkUgTElNSVRFRCBUTyBUSEUgQU1PVU5UIEFDVFVBTExZIFBBSUQgQlkgWU9VIEZPUiBUSEUgU09GVFdBUkUuPC9wPg0KDQogICAgICAgICAgPGhyIGNsYXNzPSJsaWNlbnNlLWRpdmlkZXIiIC8+DQoNCiAgICAgICAgICA8aDQ+VGhpcmQtUGFydHkgTGljZW5zZXM8L2g0Pg0KICAgICAgICAgIDxwPlRoZSBJQ0Nlcnkgc29mdHdhcmUsIENvcHlyaWdodCAoYykgMjAyNiBHb3Jkb24gQm9sdG9uLCB1c2VzIHRoZSBmb2xsb3dpbmcgdGhpcmQtcGFydHkgY29tcG9uZW50czo8L3A+DQoNCiAgICAgICAgICA8aDU+VGhyZWUuanM8L2g1Pg0KICAgICAgICAgIDxwPkNvcHlyaWdodCAoYykgMjAxMC0yMDIzIHRocmVlLmpzIGF1dGhvcnM8L3A+DQogICAgICAgICAgPHAgY2xhc3M9ImxpY2Vuc2UtbGVnYWwtYmxvY2siPlBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHkgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgIlNvZnR3YXJlIiksIHRvIGRlYWwgaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcyBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOjxici8+PGJyLz5UaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpbiBhbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS48YnIvPjxici8+VEhFIFNPRlRXQVJFIElTIFBST1ZJREVEICJBUyBJUyIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1IgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRSBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTLCBPUiBPVEhFUiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLCBPVVQgT0YsIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUgU09GVFdBUkUuPC9wPg0KDQogICAgICAgICAgPGg1PkRlbGF1bmF0b3I8L2g1Pg0KICAgICAgICAgIDxwPkNvcHlyaWdodCAoYykgMjAxNywgTWFwYm94PC9wPg0KICAgICAgICAgIDxwIGNsYXNzPSJsaWNlbnNlLWxlZ2FsLWJsb2NrIj5QZXJtaXNzaW9uIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBhbmQvb3IgZGlzdHJpYnV0ZSB0aGlzIHNvZnR3YXJlIGZvciBhbnkgcHVycG9zZSB3aXRoIG9yIHdpdGhvdXQgZmVlIGlzIGhlcmVieSBncmFudGVkLCBwcm92aWRlZCB0aGF0IHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIGFwcGVhciBpbiBhbGwgY29waWVzLjxici8+PGJyLz5USEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiBBTkQgVEhFIEFVVEhPUiBESVNDTEFJTVMgQUxMIFdBUlJBTlRJRVMgV0lUSCBSRUdBUkQgVE8gVEhJUyBTT0ZUV0FSRSBJTkNMVURJTkcgQUxMIElNUExJRUQgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFkgQU5EIEZJVE5FU1MuIElOIE5PIEVWRU5UIFNIQUxMIFRIRSBBVVRIT1IgQkUgTElBQkxFIEZPUiBBTlkgU1BFQ0lBTCwgRElSRUNULCBJTkRJUkVDVCwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIE9SIEFOWSBEQU1BR0VTIFdIQVRTT0VWRVIgUkVTVUxUSU5HIEZST00gTE9TUyBPRiBVU0UsIERBVEEgT1IgUFJPRklUUywgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIE5FR0xJR0VOQ0UgT1IgT1RIRVIgVE9SVElPVVMgQUNUSU9OLCBBUklTSU5HIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFVTRSBPUiBQRVJGT1JNQU5DRSBPRiBUSElTIFNPRlRXQVJFLjwvcD4NCg0KICAgICAgICAgIDxoNT5UYXVyaSBGcmFtZXdvcmsgJmFtcDsgUnVzdCBEZXBlbmRlbmNpZXM8L2g1Pg0KICAgICAgICAgIDxwPkNvcHlyaWdodCAoYykgMjAxOS1wcmVzZW50LCBUYXVyaSBQcm9ncmFtbWUgd2l0aGluIFRoZSBDb21tb25zIENvbnNlcnZhbmN5LjwvcD4NCiAgICAgICAgICA8cD5UaGlzIHNvZnR3YXJlIGFsc28gdXNlcyB2YXJpb3VzIGxpYnJhcmllcyBmcm9tIHRoZSBSdXN0IGVjb3N5c3RlbSAoaW5jbHVkaW5nIFNlcmRlLCBUb2tpbykgdGhhdCBhcmUgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlIG9yIEFwYWNoZSBMaWNlbnNlIFZlcnNpb24gMi4wLjwvcD4NCiAgICAgICAgPC9kaXY+DQogICAgICAgIDxkaXYgY2xhc3M9Im1vZGFsLWFjdGlvbnMiPg0KICAgICAgICAgIDxidXR0b24gaWQ9ImNsb3NlQWJvdXRCdG4iIGNsYXNzPSJwcmltYXJ5Ij5DbG9zZTwvYnV0dG9uPg0KICAgICAgICA8L2Rpdj4NCiAgICAgIDwvZGl2Pg0KICAgIDwvZGlhbG9nPg0KDQogICAgPCEtLSBTYXZlIFByZXNldCBEaWFsb2cgLS0+DQogICAgPGRpYWxvZyBpZD0ic2F2ZVByZXNldERpYWxvZyIgY2xhc3M9InNldHRpbmdzLW1vZGFsIj4NCiAgICAgIDxkaXYgY2xhc3M9Im1vZGFsLWNvbnRlbnQiPg0KICAgICAgICA8aDI+U2F2ZSBQcm9maWxpbmcgUHJlc2V0PC9oMj4NCiAgICAgICAgPHAgc3R5bGU9ImZvbnQtc2l6ZTogMC45cmVtOyBvcGFjaXR5OiAwLjg7IG1hcmdpbi1ib3R0b206IDE2cHg7Ij5TYXZlIHRoZSBjdXJyZW50IHNldHRpbmdzIGZyb20gU3RhZ2UgMSwgMiwgYW5kIDQgYXMgYSByZXVzYWJsZSBwcm9maWxpbmcgcHJlc2V0LjwvcD4NCiAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgPGxhYmVsIGZvYj0ic2F2ZVByZXNldE5hbWUiPlByZXNldCBOYW1lPC9sYWJlbD4NCiAgICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgaWQ9InNhdmVQcmVzZXROYW1lIiBwbGFjZWhvbGRlcj0iZS5nLiBNeSBDdXN0b20gRmluZUFydCBQcmVzZXQiPg0KICAgICAgICA8L2Rpdj4NCiAgICAgICAgPGRpdiBjbGFzcz0iZm9ybS1ncm91cCI+DQogICAgICAgICAgPGxhYmVsIGZvYj0ic2F2ZVByZXNldERlc2MiPkRlc2NyaXB0aW9uIChPcHRpb25hbCk8L2xhYmVsPg0KICAgICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBpZD0ic2F2ZVByZXNldERlc2MiIHBsYWNlaG9sZGVyPSJlLmcuIE9wdGltaXplZCBmb3IgMzAwZ3NtIENvdHRvbiBSYWcgd2l0aCAxNTAwIHBhdGNoZXMiPg0KICAgICAgICA8L2Rpdj4NCiAgICAgICAgPGRpdiBjbGFzcz0ibW9kYWwtYWN0aW9ucyI+DQogICAgICAgICAgPGJ1dHRvbiBpZD0iYnRuQ29uZmlybVNhdmVQcmVzZXQiIGNsYXNzPSJwcmltYXJ5Ij5TYXZlIFByZXNldDwvYnV0dG9uPg0KICAgICAgICAgIDxidXR0b24gaWQ9ImJ0bkNsb3NlU2F2ZVByZXNldERpYWxvZyIgY2xhc3M9InNlY29uZGFyeSI+Q2FuY2VsPC9idXR0b24+DQogICAgICAgIDwvZGl2Pg0KICAgICAgPC9kaXY+DQogICAgPC9kaWFsb2c+DQoNCiAgICA8IS0tIE1hbmFnZSBQcmVzZXRzIERpYWxvZyAtLT4NCiAgICA8ZGlhbG9nIGlkPSJtYW5hZ2VQcmVzZXRzRGlhbG9nIiBjbGFzcz0ic2V0dGluZ3MtbW9kYWwiIHN0eWxlPSJtYXgtd2lkdGg6IDYwMHB4OyI+DQogICAgICA8ZGl2IGNsYXNzPSJtb2RhbC1jb250ZW50Ij4NCiAgICAgICAgPGgyPk1hbmFnZSBQcm9maWxpbmcgUHJlc2V0czwvaDI+DQogICAgICAgIDxwIHN0eWxlPSJmb250LXNpemU6IDAuOXJlbTsgb3BhY2l0eTogMC44OyBtYXJnaW4tYm90dG9tOiAxNHB4OyI+VmlldyBidWlsdC1pbiBhbmQgY3VzdG9tIHByZXNldHMsIG9yIGltcG9ydCBhbmQgZXhwb3J0IHByZXNldCBjb25maWd1cmF0aW9ucyBpbiBKU09OIGZvcm1hdC48L3A+DQogICAgICAgIA0KICAgICAgICA8ZGl2IGlkPSJtYW5hZ2VQcmVzZXRzTGlzdCIgc3R5bGU9ImRpc3BsYXk6IGZsZXg7IGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47IGdhcDogOHB4OyBtYXgtaGVpZ2h0OiAyNDBweDsgb3ZlcmZsb3cteTogYXV0bzsgbWFyZ2luLWJvdHRvbTogMTZweDsgcGFkZGluZzogNHB4OyI+PC9kaXY+DQoNCiAgICAgICAgPGRpdiBzdHlsZT0iZGlzcGxheTogZmxleDsgZ2FwOiA4cHg7IGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsgYm9yZGVyLXRvcDogMXB4IHNvbGlkIHZhcigtLWJvcmRlci1jb2xvciwgIzMzMyk7IHBhZGRpbmctdG9wOiAxNHB4OyI+DQogICAgICAgICAgPGRpdiBzdHlsZT0iZGlzcGxheTogZmxleDsgZ2FwOiA4cHg7Ij4NCiAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0iYnRuRXhwb3J0QWN0aXZlUHJlc2V0IiBjbGFzcz0ic2Vjb25kYXJ5IiB0aXRsZT0iRXhwb3J0IGN1cnJlbnRseSBzZWxlY3RlZCBwcmVzZXQgdG8gSlNPTiBmaWxlIj7wn5OkIEV4cG9ydCBBY3RpdmU8L2J1dHRvbj4NCiAgICAgICAgICAgIDxidXR0b24gdHlwZT0iYnV0dG9uIiBpZD0iYnRuSW1wb3J0UHJlc2V0IiBjbGFzcz0ic2Vjb25kYXJ5IiB0aXRsZT0iSW1wb3J0IHByZXNldCBmcm9tIEpTT04gZmlsZSI+8J+TlSBJbXBvcnQgUHJlc2V0PC9idXR0b24+DQogICAgICAgICAgPC9kaXY+DQogICAgICAgICAgPGJ1dHRvbiBpZD0iYnRuQ2xvc2VNYW5hZ2VQcmVzZXRzRGlhbG9nIiBjbGFzcz0icHJpbWFyeSI+RG9uZTwvYnV0dG9uPg0KICAgICAgICA8L2Rpdj4NCiAgICAgIDwvZGl2Pg0KICAgIDwvZGlhbG9nPg0KICA8L2JvZHk+DQo8L2h0bWw+DQo= \ No newline at end of file -- 2.39.5 From a196d7cdf8eeeb43da30ea34a501f09ffc63a609 Mon Sep 17 00:00:00 2001 From: gronod <1+gronod@noreply@i3omb.com> Date: Fri, 28 Aug 2026 18:51:18 +0100 Subject: [PATCH 3/3] test(commands): add unit tests for build_chartread_args empty/auto port --- src-tauri/src/commands.rs | 980 +------------------------------------- 1 file changed, 1 insertion(+), 979 deletions(-) diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 26ccb22..d8128ae 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1,979 +1 @@ -use tauri::{AppHandle, Manager, State}; -use serde::{Deserialize, Serialize}; -use crate::process_manager::ProcessManager; - -#[tauri::command] -pub async fn spawn_process( - app: AppHandle, - state: State<'_, ProcessManager>, - id: String, - binary: String, - args: Vec, -) -> Result<(), String> { - state.spawn(app, id, binary, args, None).await -} - -#[tauri::command] -pub async fn send_stdin( - state: State<'_, ProcessManager>, - id: String, - input: String, -) -> Result<(), String> { - state.send_stdin(&id, &input).await -} - -#[tauri::command] -pub async fn kill_process( - state: State<'_, ProcessManager>, - id: String, -) -> Result<(), String> { - state.kill(&id).await -} - -pub fn get_binary_candidates(binary_name: &str) -> Vec { - if cfg!(windows) && !binary_name.to_lowercase().ends_with(".exe") { - vec![format!("{}.exe", binary_name), binary_name.to_string()] - } else { - vec![binary_name.to_string()] - } -} - -#[tauri::command] -pub async fn resolve_binary(app: AppHandle, binary_name: String) -> Result { - let settings = crate::settings::load_settings(app.clone()).unwrap_or_default(); - let candidates = get_binary_candidates(&binary_name); - - if let Some(dir) = settings.argyll_binary_dir { - if !dir.trim().is_empty() { - let base_dir = std::path::Path::new(&dir); - for name in &candidates { - let custom_path = base_dir.join(name); - if custom_path.exists() { - return Ok(custom_path.to_string_lossy().to_string()); - } - } - } - } - - let platform = match (std::env::consts::OS, std::env::consts::ARCH) { - ("linux", "x86_64") => "linux-x86_64", - ("windows", "x86_64") => "windows-x86_64", - ("macos", "aarch64") => "macos-aarch64", - ("macos", "x86_64") => "macos-x86_64", - _ => "linux-x86_64", - }; - - for name in &candidates { - if let Ok(resource_path) = app.path().resolve( - format!("argyll/{}/{}", platform, name), - tauri::path::BaseDirectory::Resource, - ) { - if resource_path.exists() { - return Ok(resource_path.to_string_lossy().to_string()); - } - } - } - - let primary_name = &candidates[0]; - let resource_path = app - .path() - .resolve( - format!("argyll/{}/{}", platform, primary_name), - tauri::path::BaseDirectory::Resource, - ) - .map_err(|e| e.to_string())?; - - Ok(resource_path.to_string_lossy().to_string()) -} - -#[derive(Serialize)] -pub struct AppInfo { - pub version: String, - pub build_date: String, -} - -#[tauri::command] -pub fn get_app_info() -> AppInfo { - AppInfo { - version: env!("CARGO_PKG_VERSION").to_string(), - build_date: env!("BUILD_DATE").to_string(), - } -} - -pub fn resolve_safe_cwd(app: &AppHandle, cwd_input: &str) -> Result { - if !cwd_input.trim().is_empty() { - let p = std::path::Path::new(cwd_input.trim()); - if p.is_dir() { - return Ok(p.to_string_lossy().to_string()); - } - } - - app.path() - .document_dir() - .or_else(|_| app.path().home_dir()) - .or_else(|_| app.path().app_data_dir()) - .map(|p| p.to_string_lossy().to_string()) - .map_err(|e| format!("Failed to resolve default working directory: {}", e)) -} - -#[tauri::command] -pub fn get_default_working_dir(app: AppHandle) -> Result { - resolve_safe_cwd(&app, "") -} - -#[tauri::command] -pub async fn select_target_file( - app: AppHandle, - default_dir: Option, - default_name: Option, -) -> Result, String> { - use tauri_plugin_dialog::DialogExt; - - let mut builder = app.dialog().file().add_filter("ArgyllCMS Target", &["ti1"]); - if let Some(ref dir) = default_dir { - if !dir.trim().is_empty() { - builder = builder.set_directory(std::path::PathBuf::from(dir)); - } - } - if let Some(ref name) = default_name { - if !name.trim().is_empty() { - let filename = if name.to_lowercase().ends_with(".ti1") { - name.to_string() - } else { - format!("{}.ti1", name) - }; - builder = builder.set_file_name(filename); - } - } - - let (tx, rx) = tokio::sync::oneshot::channel(); - builder.save_file(move |file_path| { - let res = file_path.map(|p| p.to_string()); - let _ = tx.send(res); - }); - - rx.await.map_err(|e| format!("Dialog channel error: {}", e)) -} - -#[tauri::command] -pub async fn select_directory( - app: AppHandle, - default_dir: Option, -) -> Result, String> { - use tauri_plugin_dialog::DialogExt; - - let mut builder = app.dialog().file(); - if let Some(ref dir) = default_dir { - if !dir.trim().is_empty() { - builder = builder.set_directory(std::path::PathBuf::from(dir)); - } - } - - let (tx, rx) = tokio::sync::oneshot::channel(); - builder.pick_folder(move |folder_path| { - let res = folder_path.map(|p| p.to_string()); - let _ = tx.send(res); - }); - - rx.await.map_err(|e| format!("Dialog channel error: {}", e)) -} - -#[tauri::command] -pub async fn detect_instruments(app: AppHandle, state: State<'_, ProcessManager>) -> Result<(), String> { - let binary = resolve_binary(app.clone(), "instlist".to_string()).await?; - state.spawn(app, "instlist".to_string(), binary, vec![], None).await -} - -pub fn resolve_profile_extension(cwd: &str, basename: &str) -> (String, std::path::PathBuf) { - let p_icc = std::path::Path::new(cwd).join(format!("{}.icc", basename)); - let p_icm = std::path::Path::new(cwd).join(format!("{}.icm", basename)); - - if p_icm.exists() && !p_icc.exists() { - ("icm".to_string(), p_icm) - } else if p_icc.exists() { - ("icc".to_string(), p_icc) - } else { - #[cfg(windows)] - { - ("icm".to_string(), p_icm) - } - #[cfg(not(windows))] - { - ("icc".to_string(), p_icc) - } - } -} - -#[derive(Serialize)] -pub struct StageStatus { - pub stage1_complete: bool, - pub stage2_complete: bool, - pub stage3_complete: bool, - pub stage4_complete: bool, - pub profile_path: Option, -} - -#[tauri::command] -pub fn verify_stage_artefacts(cwd: String, basename: String) -> StageStatus { - if cwd.trim().is_empty() || basename.trim().is_empty() { - return StageStatus { - stage1_complete: false, - stage2_complete: false, - stage3_complete: false, - stage4_complete: false, - profile_path: None, - }; - } - - let base_path = std::path::Path::new(&cwd); - let ti1 = base_path.join(format!("{}.ti1", basename)).exists(); - let ti2 = base_path.join(format!("{}.ti2", basename)).exists(); - let ti3 = base_path.join(format!("{}.ti3", basename)).exists(); - let (_, prof_p) = resolve_profile_extension(&cwd, &basename); - let prof_exists = prof_p.exists(); - - StageStatus { - stage1_complete: ti1, - stage2_complete: ti2, - stage3_complete: ti3, - stage4_complete: prof_exists, - profile_path: if prof_exists { Some(prof_p.to_string_lossy().to_string()) } else { None }, - } -} - -#[tauri::command] -pub fn get_profile_path(cwd: String, basename: String) -> String { - let (_, path) = resolve_profile_extension(&cwd, &basename); - path.to_string_lossy().to_string() -} - -#[tauri::command] -pub async fn extract_gamut( - app: AppHandle, - state: State<'_, ProcessManager>, - icc_path: String, -) -> Result<(), String> { - let binary = resolve_binary(app.clone(), "iccgamut".to_string()).await?; - let path = std::path::Path::new(&icc_path); - let basename = path.file_stem().map(|s| s.to_string_lossy().to_string()).unwrap_or_else(|| "profile".to_string()); - let parent_dir = path.parent().map(|p| p.to_string_lossy().to_string()).unwrap_or_default(); - - // Auto-detect .icc vs .icm if the specified path does not exist directly - let resolved_path = if path.exists() { - icc_path.clone() - } else { - let alt = if icc_path.ends_with(".icc") { - icc_path.replace(".icc", ".icm") - } else if icc_path.ends_with(".icm") { - icc_path.replace(".icm", ".icc") - } else { - icc_path.clone() - }; - if std::path::Path::new(&alt).exists() { - alt - } else { - icc_path.clone() - } - }; - - let args = vec![ - "-v".to_string(), - "-d".to_string(), - "50.0".to_string(), - resolved_path, - ]; - let cwd = if parent_dir.is_empty() { - resolve_safe_cwd(&app, "").ok() - } else { - Some(parent_dir) - }; - - let id = format!("iccgamut_{}", basename); - state.spawn(app, id, binary, args, cwd).await -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct TargenConfig { - pub colour_space: String, - #[serde(default)] - pub patch_count: u32, - pub white_patches: Option, - pub black_patches: Option, - pub total_patches: Option, - pub basename: String, - pub cwd: String, -} - -fn default_dpi() -> u32 { - 300 -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct PrinttargConfig { - pub instrument: String, // One of: "i1", "p3", "CM", "SS", "20", "22", "41", "51" - pub page_size: String, // One of: "A4", "A4R", "A3", "A2", "Letter", "LetterR", "Legal", "4x6", "11x17", or "WWWxHHH" - pub bit_depth: u8, // 8 or 16 - #[serde(default = "default_dpi")] - pub dpi: u32, // TIFF resolution, defaults to 300 DPI - pub basename: String, // Must match the .ti1 basename from Stage 1 - pub cwd: String, // Working directory where the .ti1 file resides -} - -pub fn build_targen_args(config: &TargenConfig) -> Vec { - let mut args = vec![ - "-v".to_string(), - "-d".to_string(), - ]; - - if config.colour_space.to_lowercase() == "cmyk" { - args.push("4".to_string()); - } else { - args.push("2".to_string()); // Default to RGB - } - - let patches = if config.patch_count > 0 { - config.patch_count - } else { - config.total_patches.unwrap_or(0) - }; - - if patches > 0 { - args.push("-f".to_string()); - args.push(patches.to_string()); - } - - if let Some(white) = config.white_patches { - args.push("-e".to_string()); - args.push(white.to_string()); - } - - if let Some(black) = config.black_patches { - args.push("-B".to_string()); - args.push(black.to_string()); - } - - args.push(config.basename.clone()); - args -} - -pub fn build_printtarg_args(config: &PrinttargConfig) -> Vec { - let mut args = vec![ - "-v".to_string(), - "-u".to_string(), - "-i".to_string(), - config.instrument.clone(), - "-p".to_string(), - config.page_size.clone(), - ]; - - if config.bit_depth == 16 { - args.push("-T".to_string()); - } else { - args.push("-t".to_string()); - } - args.push(config.dpi.to_string()); - - args.push(config.basename.clone()); - args -} - -#[tauri::command] -pub async fn run_targen( - app: AppHandle, - state: State<'_, ProcessManager>, - config: TargenConfig, -) -> Result<(), String> { - let binary = resolve_binary(app.clone(), "targen".to_string()).await?; - let args = build_targen_args(&config); - let id = format!("targen_{}", config.basename); - let cwd = Some(resolve_safe_cwd(&app, &config.cwd)?); - - state.spawn(app, id, binary, args, cwd).await -} - -#[tauri::command] -pub async fn run_printtarg( - app: AppHandle, - state: State<'_, ProcessManager>, - config: PrinttargConfig, -) -> Result<(), String> { - let binary = resolve_binary(app.clone(), "printtarg".to_string()).await?; - let args = build_printtarg_args(&config); - let id = format!("printtarg_{}", config.basename); - let cwd = Some(resolve_safe_cwd(&app, &config.cwd)?); - - state.spawn(app, id, binary, args, cwd).await -} - -#[tauri::command] -pub async fn read_file_base64(path: String) -> Result { - use std::fs; - let bytes = fs::read(&path).map_err(|e| format!("Failed to read {}: {}", path, e))?; - use base64::Engine; - Ok(base64::engine::general_purpose::STANDARD.encode(&bytes)) -} - -#[tauri::command] -pub async fn read_tiff_preview_png(path: String) -> Result { - use std::fs; - use std::io::Cursor; - use base64::Engine; - use image::ImageFormat; - - let file_bytes = fs::read(&path).map_err(|e| format!("Failed to read {}: {}", path, e))?; - let img = image::load_from_memory_with_format(&file_bytes, ImageFormat::Tiff) - .map_err(|e| format!("Failed to decode TIFF {}: {}", path, e))?; - - let max_dim = std::cmp::max(img.width(), img.height()); - let processed_img = if max_dim > 1200 { - img.resize(1200, 1200, image::imageops::FilterType::Lanczos3) - } else { - img - }; - - let mut png_bytes = Cursor::new(Vec::new()); - processed_img - .write_to(&mut png_bytes, ImageFormat::Png) - .map_err(|e| format!("Failed to encode PNG for {}: {}", path, e))?; - - Ok(base64::engine::general_purpose::STANDARD.encode(png_bytes.into_inner())) -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ChartreadConfig { - pub basename: String, - pub cwd: String, - pub port: Option, -} - -pub fn build_chartread_args(config: &ChartreadConfig) -> Vec { - let mut args = vec![ - "-v".to_string(), - "-u".to_string(), - ]; - - if let Some(ref port) = config.port { - if !port.trim().is_empty() { - args.push("-c".to_string()); - args.push(port.trim().to_string()); - } - } - - args.push(config.basename.clone()); - args -} - -#[tauri::command] -pub async fn run_chartread( - app: AppHandle, - state: State<'_, ProcessManager>, - config: ChartreadConfig, -) -> Result<(), String> { - let binary = resolve_binary(app.clone(), "chartread".to_string()).await?; - let args = build_chartread_args(&config); - let id = format!("chartread_{}", config.basename); - let cwd = Some(resolve_safe_cwd(&app, &config.cwd)?); - - state.spawn(app, id, binary, args, cwd).await -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct AverageConfig { - pub inputs: Vec, - pub output: String, - pub cwd: String, -} - -pub fn build_average_args(config: &AverageConfig) -> Vec { - let mut args = vec!["-v".to_string()]; - for input in &config.inputs { - args.push(input.clone()); - } - args.push(config.output.clone()); - args -} - -#[tauri::command] -pub async fn run_average( - app: AppHandle, - state: State<'_, ProcessManager>, - config: AverageConfig, -) -> Result<(), String> { - let binary = resolve_binary(app.clone(), "average".to_string()).await?; - let args = build_average_args(&config); - let id = format!("average_{}", config.output); - let cwd = Some(resolve_safe_cwd(&app, &config.cwd)?); - - state.spawn(app, id, binary, args, cwd).await -} - -fn sanitize_basename(basename: &str) -> Result { - let name = basename.trim(); - if name.is_empty() { - return Err("basename is empty".to_string()); - } - if name.contains('/') || name.contains('\\') || name.contains("..") { - return Err("basename must not contain path separators".to_string()); - } - Ok(name.to_string()) -} - -pub fn snapshot_ti3_filename(basename: &str, pass_index: u32) -> Result { - if pass_index == 0 { - return Err("pass_index must be 1-based".to_string()); - } - let name = sanitize_basename(basename)?; - Ok(format!("{}_pass{}.ti3", name, pass_index)) -} - -/// Copy `{cwd}/{basename}.ti3` to `{cwd}/{basename}_pass{N}.ti3` and remove the -/// canonical file so Stage 4 stays locked until Finish (#109 / #110). -#[tauri::command] -pub fn snapshot_ti3(cwd: String, basename: String, pass_index: u32) -> Result { - let dest_name = snapshot_ti3_filename(&basename, pass_index)?; - let name = sanitize_basename(&basename)?; - let dir = std::path::Path::new(&cwd); - if !dir.is_dir() { - return Err(format!("working directory does not exist: {}", cwd)); - } - let src = dir.join(format!("{}.ti3", name)); - if !src.is_file() { - return Err(format!("measurement file not found: {}", src.display())); - } - let dest = dir.join(&dest_name); - std::fs::copy(&src, &dest).map_err(|e| format!("failed to snapshot .ti3: {}", e))?; - std::fs::remove_file(&src) - .map_err(|e| format!("failed to remove canonical .ti3 after snapshot: {}", e))?; - Ok(dest_name) -} - -/// Copy a pass file (relative name) back to `{cwd}/{basename}.ti3`. -#[tauri::command] -pub fn promote_ti3(cwd: String, source: String, basename: String) -> Result<(), String> { - let name = sanitize_basename(&basename)?; - let src_name = source.trim(); - if src_name.is_empty() - || src_name.contains('/') - || src_name.contains('\\') - || src_name.contains("..") - { - return Err("source filename is invalid".to_string()); - } - let dir = std::path::Path::new(&cwd); - let src = dir.join(src_name); - if !src.is_file() { - return Err(format!("source measurement file not found: {}", src.display())); - } - let dest = dir.join(format!("{}.ti3", name)); - std::fs::copy(&src, &dest).map_err(|e| format!("failed to promote .ti3: {}", e))?; - Ok(()) -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ColprofConfig { - pub algorithm: String, - pub quality: String, - pub intent: Option, - pub copyright: Option, - pub description: Option, - pub basename: String, - pub cwd: String, -} - -pub fn build_colprof_args(config: &ColprofConfig) -> Vec { - let mut args = vec![ - "-v".to_string(), - "-a".to_string(), - config.algorithm.clone(), - "-q".to_string(), - config.quality.clone(), - ]; - - if let Some(ref intent) = config.intent { - if !intent.trim().is_empty() { - args.push("-p".to_string()); - args.push(intent.clone()); - } - } - - if let Some(ref cr) = config.copyright { - if !cr.trim().is_empty() { - args.push("-C".to_string()); - args.push(cr.clone()); - } - } - - if let Some(ref desc) = config.description { - if !desc.trim().is_empty() { - args.push("-D".to_string()); - args.push(desc.clone()); - } - } - - args.push(config.basename.clone()); - args -} - -#[tauri::command] -pub async fn run_colprof( - app: AppHandle, - state: State<'_, ProcessManager>, - config: ColprofConfig, -) -> Result<(), String> { - let binary = resolve_binary(app.clone(), "colprof".to_string()).await?; - let args = build_colprof_args(&config); - let id = format!("colprof_{}", config.basename); - let cwd = Some(resolve_safe_cwd(&app, &config.cwd)?); - - state.spawn(app, id, binary, args, cwd).await -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ProfcheckConfig { - pub ti3_path: String, - pub icc_path: String, - pub cwd: String, -} - -pub fn build_profcheck_args(config: &ProfcheckConfig) -> Vec { - vec![ - "-v".to_string(), - "-k".to_string(), - "-s".to_string(), - config.ti3_path.clone(), - config.icc_path.clone(), - ] -} - -#[tauri::command] -pub async fn run_profcheck( - app: AppHandle, - state: State<'_, ProcessManager>, - mut config: ProfcheckConfig, -) -> Result<(), String> { - let binary = resolve_binary(app.clone(), "profcheck".to_string()).await?; - - // Auto-detect .icc vs .icm if the specified icc_path does not exist directly - if !std::path::Path::new(&config.icc_path).exists() { - let alt = if config.icc_path.ends_with(".icc") { - config.icc_path.replace(".icc", ".icm") - } else if config.icc_path.ends_with(".icm") { - config.icc_path.replace(".icm", ".icc") - } else { - config.icc_path.clone() - }; - if std::path::Path::new(&alt).exists() { - config.icc_path = alt; - } - } - - let args = build_profcheck_args(&config); - let id = format!("profcheck_{}", config.ti3_path); - let cwd = Some(resolve_safe_cwd(&app, &config.cwd)?); - - state.spawn(app, id, binary, args, cwd).await -} - -#[tauri::command] -pub async fn get_printers() -> Result, String> { - #[cfg(windows)] - { - crate::print::windows::get_printers() - } - #[cfg(unix)] - { - crate::print::unix::get_printers() - } - #[cfg(not(any(windows, unix)))] - { - Err("Native printer enumeration is not supported on this platform.".to_string()) - } -} - -#[tauri::command] -pub async fn get_printer_capabilities( - printer_name: String, -) -> Result { - #[cfg(windows)] - { - crate::print::windows::get_printer_capabilities(&printer_name) - } - #[cfg(unix)] - { - crate::print::unix::get_printer_capabilities(&printer_name) - } - #[cfg(not(any(windows, unix)))] - { - let _ = printer_name; - Err("Printer capabilities query is not supported on this platform.".to_string()) - } -} - -#[tauri::command] -pub async fn show_printer_properties( - state: State<'_, crate::print::PrinterDevModeStore>, - printer_name: String, -) -> Result<(), String> { - #[cfg(windows)] - { - crate::print::windows::show_printer_properties(&printer_name, &state) - } - #[cfg(unix)] - { - let _ = (state, printer_name); - Ok(()) - } - #[cfg(not(any(windows, unix)))] - { - let _ = (state, printer_name); - Err("Printer properties dialog is not supported on this platform.".to_string()) - } -} - -#[tauri::command] -pub async fn print_target_native( - state: State<'_, crate::print::PrinterDevModeStore>, - printer_name: String, - tiff_path: String, - options: Option, -) -> Result<(), String> { - #[cfg(windows)] - { - crate::print::windows::print_target( - &printer_name, - &tiff_path, - options.as_ref(), - Some(&state), - ) - } - #[cfg(unix)] - { - let _ = state; - crate::print::unix::print_target( - &printer_name, - &tiff_path, - options.as_ref(), - ) - } - #[cfg(not(any(windows, unix)))] - { - let _ = (state, printer_name, tiff_path, options); - Err("Native raw printing is not supported on this platform.".to_string()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_build_targen_args_rgb() { - let config = TargenConfig { - colour_space: "rgb".to_string(), - patch_count: 800, - white_patches: Some(4), - black_patches: None, - total_patches: None, - basename: "my_profile".to_string(), - cwd: "/tmp".to_string(), - }; - let args = build_targen_args(&config); - assert_eq!(args, vec!["-v", "-d", "2", "-f", "800", "-e", "4", "my_profile"]); - } - - #[test] - fn test_build_targen_args_cmyk() { - let config = TargenConfig { - colour_space: "cmyk".to_string(), - patch_count: 1500, - white_patches: None, - black_patches: Some(8), - total_patches: None, - basename: "cmyk_profile".to_string(), - cwd: "/tmp".to_string(), - }; - let args = build_targen_args(&config); - assert_eq!(args, vec!["-v", "-d", "4", "-f", "1500", "-B", "8", "cmyk_profile"]); - } - - #[test] - fn test_build_targen_args_total_patches_fallback() { - let config = TargenConfig { - colour_space: "rgb".to_string(), - patch_count: 0, - white_patches: None, - black_patches: None, - total_patches: Some(400), - basename: "draft_profile".to_string(), - cwd: "/tmp".to_string(), - }; - let args = build_targen_args(&config); - assert_eq!(args, vec!["-v", "-d", "2", "-f", "400", "draft_profile"]); - } - - #[test] - fn test_build_printtarg_args_i1_a4_8bit() { - let config = PrinttargConfig { - instrument: "i1".to_string(), - page_size: "A4".to_string(), - bit_depth: 8, - dpi: 100, - basename: "my_profile".to_string(), - cwd: "/tmp".to_string(), - }; - let args = build_printtarg_args(&config); - assert_eq!(args, vec!["-v", "-u", "-i", "i1", "-p", "A4", "-t", "100", "my_profile"]); - } - - #[test] - fn test_build_printtarg_args_cm_letter_16bit() { - let config = PrinttargConfig { - instrument: "CM".to_string(), - page_size: "Letter".to_string(), - bit_depth: 16, - dpi: 300, - basename: "cmyk_profile".to_string(), - cwd: "/home/user".to_string(), - }; - let args = build_printtarg_args(&config); - assert_eq!(args, vec!["-v", "-u", "-i", "CM", "-p", "Letter", "-T", "300", "cmyk_profile"]); - } - - #[test] - fn test_build_printtarg_args_custom_page_size() { - let config = PrinttargConfig { - instrument: "SS".to_string(), - page_size: "200x400".to_string(), - bit_depth: 8, - dpi: 150, - basename: "custom_target".to_string(), - cwd: "/tmp".to_string(), - }; - let args = build_printtarg_args(&config); - assert_eq!(args, vec!["-v", "-u", "-i", "SS", "-p", "200x400", "-t", "150", "custom_target"]); - } - - #[test] - fn test_build_chartread_args() { - let config = ChartreadConfig { - basename: "my_profile".to_string(), - cwd: "/home/user".to_string(), - port: None, - }; - let args = build_chartread_args(&config); - assert_eq!(args, vec!["-v", "-u", "my_profile"]); - } - - #[test] - fn test_build_chartread_args_with_port() { - let config = ChartreadConfig { - basename: "my_profile".to_string(), - cwd: "/home/user".to_string(), - port: Some("1".to_string()), - }; - let args = build_chartread_args(&config); - assert_eq!(args, vec!["-v", "-u", "-c", "1", "my_profile"]); - } - - #[test] - fn test_build_average_args() { - let config = AverageConfig { - inputs: vec!["pass1.ti3".to_string(), "pass2.ti3".to_string()], - output: "avg.ti3".to_string(), - cwd: "/home/user".to_string(), - }; - let args = build_average_args(&config); - assert_eq!(args, vec!["-v", "pass1.ti3", "pass2.ti3", "avg.ti3"]); - } - - #[test] - fn test_build_average_args_pass_files() { - let config = AverageConfig { - inputs: vec!["job_pass1.ti3".to_string(), "job_pass2.ti3".to_string()], - output: "job.ti3".to_string(), - cwd: "/home/user".to_string(), - }; - let args = build_average_args(&config); - assert_eq!(args, vec!["-v", "job_pass1.ti3", "job_pass2.ti3", "job.ti3"]); - } - - #[test] - fn test_snapshot_ti3_filename() { - assert_eq!(snapshot_ti3_filename("foo", 1).unwrap(), "foo_pass1.ti3"); - assert_eq!(snapshot_ti3_filename("foo", 2).unwrap(), "foo_pass2.ti3"); - assert!(snapshot_ti3_filename("foo", 0).is_err()); - assert!(snapshot_ti3_filename("../x", 1).is_err()); - assert!(snapshot_ti3_filename("a/b", 1).is_err()); - } - - #[test] - fn test_snapshot_and_promote_ti3_roundtrip() { - let dir = std::env::temp_dir().join(format!( - "iccery_ti3_{}_{}", - std::process::id(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_nanos() - )); - std::fs::create_dir_all(&dir).unwrap(); - let canonical = dir.join("job.ti3"); - std::fs::write(&canonical, b"PASS1").unwrap(); - let cwd = dir.to_string_lossy().to_string(); - let name = snapshot_ti3(cwd.clone(), "job".to_string(), 1).unwrap(); - assert_eq!(name, "job_pass1.ti3"); - assert!(!canonical.exists(), "canonical .ti3 must be removed after snapshot"); - assert_eq!(std::fs::read(dir.join("job_pass1.ti3")).unwrap(), b"PASS1"); - - std::fs::write(dir.join("job_pass2.ti3"), b"PASS2").unwrap(); - let name2 = snapshot_ti3_filename("job", 2).unwrap(); - assert_eq!(name2, "job_pass2.ti3"); - assert!(dir.join("job_pass1.ti3").exists(), "pass 1 must survive pass 2"); - - promote_ti3(cwd, name, "job".to_string()).unwrap(); - assert_eq!(std::fs::read(&canonical).unwrap(), b"PASS1"); - let _ = std::fs::remove_dir_all(&dir); - } - - #[test] - fn test_build_colprof_args() { - let config = ColprofConfig { - quality: "h".to_string(), - algorithm: "l".to_string(), - intent: None, - description: Some("My Profile".to_string()), - copyright: Some("2026 ACME".to_string()), - basename: "my_profile".to_string(), - cwd: "/home/user".to_string(), - }; - let args = build_colprof_args(&config); - assert_eq!( - args, - vec!["-v", "-a", "l", "-q", "h", "-C", "2026 ACME", "-D", "My Profile", "my_profile"] - ); - } - - #[test] - fn test_build_profcheck_args() { - let config = ProfcheckConfig { - ti3_path: "my_profile.ti3".to_string(), - icc_path: "my_profile.icc".to_string(), - cwd: "/home/user".to_string(), - }; - let args = build_profcheck_args(&config); - assert_eq!(args, vec!["-v", "-k", "-s", "my_profile.ti3", "my_profile.icc"]); - } - - #[test] - fn test_get_binary_candidates() { - let candidates = get_binary_candidates("targen"); - if cfg!(windows) { - assert_eq!(candidates, vec!["targen.exe", "targen"]); - } else { - assert_eq!(candidates, vec!["targen"]); - } - - let candidates_exe = get_binary_candidates("targen.exe"); - assert_eq!(candidates_exe, vec!["targen.exe"]); - } -} +dXNlIHRhdXJpOjp7QXBwSGFuZGxlLCBNYW5hZ2VyLCBTdGF0ZX07DQp1c2Ugc2VyZGU6OntEZXNlcmlhbGl6ZSwgU2VyaWFsaXplfTsNCnVzZSBjcmF0ZTo6cHJvY2Vzc19tYW5hZ2VyOjpQcm9jZXNzTWFuYWdlcjsNCg0KI1t0YXVyaTo6Y29tbWFuZF0NCnB1YiBhc3luYyBmbiBzcGF3bl9wcm9jZXNzKA0KICAgIGFwcDogQXBwSGFuZGxlLA0KICAgIHN0YXRlOiBTdGF0ZTwnXywgUHJvY2Vzc01hbmFnZXI+LA0KICAgIGlkOiBTdHJpbmcsDQogICAgYmluYXJ5OiBTdHJpbmcsDQogICAgYXJnczogVmVjPFN0cmluZz4sDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgc3RhdGUuc3Bhd24oYXBwLCBpZCwgYmluYXJ5LCBhcmdzLCBOb25lKS5hd2FpdA0KfQ0KDQojW3RhdXJpOjpjb21tYW5kXQ0KcHViIGFzeW5jIGZuIHNlbmRfc3RkaW4oDQogICAgc3RhdGU6IFN0YXRlPCdfLCBQcm9jZXNzTWFuYWdlcj4sDQogICAgaWQ6IFN0cmluZywNCiAgICBpbnB1dDogU3RyaW5nLA0KKSAtPiBSZXN1bHQ8KCksIFN0cmluZz4gew0KICAgIHN0YXRlLnNlbmRfc3RkaW4oJmlkLCAmaW5wdXQpLmF3YWl0DQp9DQoNCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgYXN5bmMgZm4ga2lsbF9wcm9jZXNzKA0KICAgIHN0YXRlOiBTdGF0ZTwnXywgUHJvY2Vzc01hbmFnZXI+LA0KICAgIGlkOiBTdHJpbmcsDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgc3RhdGUua2lsbCgmaWQpLmF3YWl0DQp9DQoNCnB1YiBmbiBnZXRfYmluYXJ5X2NhbmRpZGF0ZXMoYmluYXJ5X25hbWU6ICZzdHIpIC0+IFZlYzxTdHJpbmc+IHsNCiAgICBpZiBjZmchKHdpbmRvd3MpICYmICFiaW5hcnlfbmFtZS50b19sb3dlcmNhc2UoKS5lbmRzX3dpdGgoIi5leGUiKSB7DQogICAgICAgIHZlYyFbZm9ybWF0ISgie30uZXhlIiwgYmluYXJ5X25hbWUpLCBiaW5hcnlfbmFtZS50b19zdHJpbmcoKV0NCiAgICB9IGVsc2Ugew0KICAgICAgICB2ZWMhW2JpbmFyeV9uYW1lLnRvX3N0cmluZygpXQ0KICAgIH0NCn0NCg0KI1t0YXVyaTo6Y29tbWFuZF0NCnB1YiBhc3luYyBmbiByZXNvbHZlX2JpbmFyeShhcHA6IEFwcEhhbmRsZSwgYmluYXJ5X25hbWU6IFN0cmluZykgLT4gUmVzdWx0PFN0cmluZywgU3RyaW5nPiB7DQogICAgbGV0IHNldHRpbmdzID0gY3JhdGU6OnNldHRpbmdzOjpsb2FkX3NldHRpbmdzKGFwcC5jbG9uZSgpKS51bndyYXBfb3JfZGVmYXVsdCgpOw0KICAgIGxldCBjYW5kaWRhdGVzID0gZ2V0X2JpbmFyeV9jYW5kaWRhdGVzKCZiaW5hcnlfbmFtZSk7DQoNCiAgICBpZiBsZXQgU29tZShkaXIpID0gc2V0dGluZ3MuYXJneWxsX2JpbmFyeV9kaXIgew0KICAgICAgICBpZiAhZGlyLnRyaW0oKS5pc19lbXB0eSgpIHsNCiAgICAgICAgICAgIGxldCBiYXNlX2RpciA9IHN0ZDo6cGF0aDo6UGF0aDo6bmV3KCZkaXIpOw0KICAgICAgICAgICAgZm9yIG5hbWUgaW4gJmNhbmRpZGF0ZXMgew0KICAgICAgICAgICAgICAgIGxldCBjdXN0b21fcGF0aCA9IGJhc2VfZGlyLmpvaW4obmFtZSk7DQogICAgICAgICAgICAgICAgaWYgY3VzdG9tX3BhdGguZXhpc3RzKCkgew0KICAgICAgICAgICAgICAgICAgICByZXR1cm4gT2soY3VzdG9tX3BhdGgudG9fc3RyaW5nX2xvc3N5KCkudG9fc3RyaW5nKCkpOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgIH0NCg0KICAgIGxldCBwbGF0Zm9ybSA9IG1hdGNoIChzdGQ6OmVudjo6Y29uc3RzOjpPUywgc3RkOjplbnY6OmNvbnN0czo6QVJDSCkgew0KICAgICAgICAoImxpbnV4IiwgIng4Nl82NCIpID0+ICJsaW51eC14ODZfNjQiLA0KICAgICAgICAoIndpbmRvd3MiLCAieDg2XzY0IikgPT4gIndpbmRvd3MteDg2XzY0IiwNCiAgICAgICAgKCJtYWNvcyIsICJhYXJjaDY0IikgPT4gIm1hY29zLWFhcmNoNjQiLA0KICAgICAgICAoIm1hY29zIiwgIng4Nl82NCIpID0+ICJtYWNvcy14ODZfNjQiLA0KICAgICAgICBfID0+ICJsaW51eC14ODZfNjQiLA0KICAgIH07DQoNCiAgICBmb3IgbmFtZSBpbiAmY2FuZGlkYXRlcyB7DQogICAgICAgIGlmIGxldCBPayhyZXNvdXJjZV9wYXRoKSA9IGFwcC5wYXRoKCkucmVzb2x2ZSgNCiAgICAgICAgICAgIGZvcm1hdCEoImFyZ3lsbC97fS97fSIsIHBsYXRmb3JtLCBuYW1lKSwNCiAgICAgICAgICAgIHRhdXJpOjpwYXRoOjpCYXNlRGlyZWN0b3J5OjpSZXNvdXJjZSwNCiAgICAgICAgKSB7DQogICAgICAgICAgICBpZiByZXNvdXJjZV9wYXRoLmV4aXN0cygpIHsNCiAgICAgICAgICAgICAgICByZXR1cm4gT2socmVzb3VyY2VfcGF0aC50b19zdHJpbmdfbG9zc3koKS50b19zdHJpbmcoKSk7DQogICAgICAgICAgICB9DQogICAgICAgIH0NCiAgICB9DQoNCiAgICBsZXQgcHJpbWFyeV9uYW1lID0gJmNhbmRpZGF0ZXNbMF07DQogICAgbGV0IHJlc291cmNlX3BhdGggPSBhcHANCiAgICAgICAgLnBhdGgoKQ0KICAgICAgICAucmVzb2x2ZSgNCiAgICAgICAgICAgIGZvcm1hdCEoImFyZ3lsbC97fS97fSIsIHBsYXRmb3JtLCBwcmltYXJ5X25hbWUpLA0KICAgICAgICAgICAgdGF1cmk6OnBhdGg6OkJhc2VEaXJlY3Rvcnk6OlJlc291cmNlLA0KICAgICAgICApDQogICAgICAgIC5tYXBfZXJyKHxlfCBlLnRvX3N0cmluZygpKT87DQoNCiAgICBPayhyZXNvdXJjZV9wYXRoLnRvX3N0cmluZygpKQ0KfQ0KDQojW2Rlcml2ZShTZXJpYWxpemUpXQ0KcHViIHN0cnVjdCBBcHBJbmZvIHsNCiAgICBwdWIgdmVyc2lvbjogU3RyaW5nLA0KICAgIHB1YiBidWlsZF9kYXRlOiBTdHJpbmcsDQp9DQoNCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgZm4gZ2V0X2FwcF9pbmZvKCkgLT4gQXBwSW5mbyB7DQogICAgQXBwSW5mbyB7DQogICAgICAgIHZlcnNpb246IGVudiEoIkNBUkdPX1BLR19WRVJTSU9OIikudG9fc3RyaW5nKCksDQogICAgICAgIGJ1aWxkX2RhdGU6IGVudiEoIkJVSUxEX0RBVEUiKS50b19zdHJpbmcoKSwNCiAgICB9DQp9DQoNCnB1YiBmbiByZXNvbHZlX3NhZmVfY3dkKGFwcDogJkFwcEhhbmRsZSwgY3dkX2lucHV0OiAmc3RyKSAtPiBSZXN1bHQ8U3RyaW5nLCBTdHJpbmc+IHsNCiAgICBpZiAhY3dkX2lucHV0LnRyaW0oKS5pc19lbXB0eSgpIHsNCiAgICAgICAgbGV0IHAgPSBzdGQ6OnBhdGg6OlBhdGg6Om5ldyhjd2RfaW5wdXQudHJpbSgpKTsNCiAgICAgICAgaWYgcC5pc19kaXIoKSB7DQogICAgICAgICAgICByZXR1cm4gT2socC50b19zdHJpbmdfbG9zc3koKS50b19zdHJpbmcoKSk7DQogICAgICAgIH0NCiAgICB9DQoNCiAgICBhcHAucGF0aCgpDQogICAgICAgIC5kb2N1bWVudF9kaXIoKQ0KICAgICAgICAub3JfZWxzZSh8X3wgYXBwLnBhdGgoKS5ob21lX2RpcigpKQ0KICAgICAgICAub3JfZWxzZSh8X3wgYXBwLnBhdGgoKS5hcHBfZGF0YV9kaXIoKSkNCiAgICAgICAgLm1hcCh8cHwgcC50b19zdHJpbmdfbG9zc3koKS50b19zdHJpbmcoKSkNCiAgICAgICAgLm1hcF9lcnIofGV8IGZvcm1hdCEoIkZhaWxlZCB0byByZXNvbHZlIGRlZmF1bHQgd29ya2luZyBkaXJlY3Rvcnk6IHt9IiwgZSkpDQp9DQoNCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgZm4gZ2V0X2RlZmF1bHRfd29ya2luZ19kaXIoYXBwOiBBcHBIYW5kbGUpIC0+IFJlc3VsdDxTdHJpbmcsIFN0cmluZz4gew0KICAgIHJlc29sdmVfc2FmZV9jd2QoJmFwcCwgIiIpDQp9DQoNCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgYXN5bmMgZm4gc2VsZWN0X3RhcmdldF9maWxlKA0KICAgIGFwcDogQXBwSGFuZGxlLA0KICAgIGRlZmF1bHRfZGlyOiBPcHRpb248U3RyaW5nPiwNCiAgICBkZWZhdWx0X25hbWU6IE9wdGlvbjxTdHJpbmc+LA0KKSAtPiBSZXN1bHQ8T3B0aW9uPFN0cmluZz4sIFN0cmluZz4gew0KICAgIHVzZSB0YXVyaV9wbHVnaW5fZGlhbG9nOjpEaWFsb2dFeHQ7DQoNCiAgICBsZXQgbXV0IGJ1aWxkZXIgPSBhcHAuZGlhbG9nKCkuZmlsZSgpLmFkZF9maWx0ZXIoIkFyZ3lsbENNUyBUYXJnZXQiLCAmWyJ0aTEiXSk7DQogICAgaWYgbGV0IFNvbWUocmVmIGRpcikgPSBkZWZhdWx0X2RpciB7DQogICAgICAgIGlmICFkaXIudHJpbSgpLmlzX2VtcHR5KCkgew0KICAgICAgICAgICAgYnVpbGRlciA9IGJ1aWxkZXIuc2V0X2RpcmVjdG9yeShzdGQ6OnBhdGg6OlBhdGhCdWY6OmZyb20oZGlyKSk7DQogICAgICAgIH0NCiAgICB9DQogICAgaWYgbGV0IFNvbWUocmVmIG5hbWUpID0gZGVmYXVsdF9uYW1lIHsNCiAgICAgICAgaWYgIW5hbWUudHJpbSgpLmlzX2VtcHR5KCkgew0KICAgICAgICAgICAgbGV0IGZpbGVuYW1lID0gaWYgbmFtZS50b19sb3dlcmNhc2UoKS5lbmRzX3dpdGgoIi50aTEiKSB7DQogICAgICAgICAgICAgICAgbmFtZS50b19zdHJpbmcoKQ0KICAgICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgICAgICBmb3JtYXQhKCJ7fS50aTEiLCBuYW1lKQ0KICAgICAgICAgICAgfTsNCiAgICAgICAgICAgIGJ1aWxkZXIgPSBidWlsZGVyLnNldF9maWxlX25hbWUoZmlsZW5hbWUpOw0KICAgICAgICB9DQogICAgfQ0KDQogICAgbGV0ICh0eCwgcngpID0gdG9raW86OnN5bmM6Om9uZXNob3Q6OmNoYW5uZWwoKTsNCiAgICBidWlsZGVyLnNhdmVfZmlsZShtb3ZlIHxmaWxlX3BhdGh8IHsNCiAgICAgICAgbGV0IHJlcyA9IGZpbGVfcGF0aC5tYXAofHB8IHAudG9fc3RyaW5nKCkpOw0KICAgICAgICBsZXQgXyA9IHR4LnNlbmQocmVzKTsNCiAgICB9KTsNCg0KICAgIHJ4LmF3YWl0Lm1hcF9lcnIofGV8IGZvcm1hdCEoIkRpYWxvZyBjaGFubmVsIGVycm9yOiB7fSIsIGUpKQ0KfQ0KDQojW3RhdXJpOjpjb21tYW5kXQ0KcHViIGFzeW5jIGZuIHNlbGVjdF9kaXJlY3RvcnkoDQogICAgYXBwOiBBcHBIYW5kbGUsDQogICAgZGVmYXVsdF9kaXI6IE9wdGlvbjxTdHJpbmc+LA0KKSAtPiBSZXN1bHQ8T3B0aW9uPFN0cmluZz4sIFN0cmluZz4gew0KICAgIHVzZSB0YXVyaV9wbHVnaW5fZGlhbG9nOjpEaWFsb2dFeHQ7DQoNCiAgICBsZXQgbXV0IGJ1aWxkZXIgPSBhcHAuZGlhbG9nKCkuZmlsZSgpOw0KICAgIGlmIGxldCBTb21lKHJlZiBkaXIpID0gZGVmYXVsdF9kaXIgew0KICAgICAgICBpZiAhZGlyLnRyaW0oKS5pc19lbXB0eSgpIHsNCiAgICAgICAgICAgIGJ1aWxkZXIgPSBidWlsZGVyLnNldF9kaXJlY3Rvcnkoc3RkOjpwYXRoOjpQYXRoQnVmOjpmcm9tKGRpcikpOw0KICAgICAgICB9DQogICAgfQ0KDQogICAgbGV0ICh0eCwgcngpID0gdG9raW86OnN5bmM6Om9uZXNob3Q6OmNoYW5uZWwoKTsNCiAgICBidWlsZGVyLnBpY2tfZm9sZGVyKG1vdmUgfGZvbGRlcl9wYXRofCB7DQogICAgICAgIGxldCByZXMgPSBmb2xkZXJfcGF0aC5tYXAofHB8IHAudG9fc3RyaW5nKCkpOw0KICAgICAgICBsZXQgXyA9IHR4LnNlbmQocmVzKTsNCiAgICB9KTsNCg0KICAgIHJ4LmF3YWl0Lm1hcF9lcnIofGV8IGZvcm1hdCEoIkRpYWxvZyBjaGFubmVsIGVycm9yOiB7fSIsIGUpKQ0KfQ0KDQojW3RhdXJpOjpjb21tYW5kXQ0KcHViIGFzeW5jIGZuIGRldGVjdF9pbnN0cnVtZW50cyhhcHA6IEFwcEhhbmRsZSwgc3RhdGU6IFN0YXRlPCdfLCBQcm9jZXNzTWFuYWdlcj4pIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgbGV0IGJpbmFyeSA9IHJlc29sdmVfYmluYXJ5KGFwcC5jbG9uZSgpLCAiaW5zdGxpc3QiLnRvX3N0cmluZygpKS5hd2FpdD87DQogICAgc3RhdGUuc3Bhd24oYXBwLCAiaW5zdGxpc3QiLnRvX3N0cmluZygpLCBiaW5hcnksIHZlYyFbXSwgTm9uZSkuYXdhaXQNCn0NCg0KcHViIGZuIHJlc29sdmVfcHJvZmlsZV9leHRlbnNpb24oY3dkOiAmc3RyLCBiYXNlbmFtZTogJnN0cikgLT4gKFN0cmluZywgc3RkOjpwYXRoOjpQYXRoQnVmKSB7DQogICAgbGV0IHBfaWNjID0gc3RkOjpwYXRoOjpQYXRoOjpuZXcoY3dkKS5qb2luKGZvcm1hdCEoInt9LmljYyIsIGJhc2VuYW1lKSk7DQogICAgbGV0IHBfaWNtID0gc3RkOjpwYXRoOjpQYXRoOjpuZXcoY3dkKS5qb2luKGZvcm1hdCEoInt9LmljbSIsIGJhc2VuYW1lKSk7DQoNCiAgICBpZiBwX2ljbS5leGlzdHMoKSAmJiAhcF9pY2MuZXhpc3RzKCkgew0KICAgICAgICAoImljbSIudG9fc3RyaW5nKCksIHBfaWNtKQ0KICAgIH0gZWxzZSBpZiBwX2ljYy5leGlzdHMoKSB7DQogICAgICAgICgiaWNjIi50b19zdHJpbmcoKSwgcF9pY2MpDQogICAgfSBlbHNlIHsNCiAgICAgICAgI1tjZmcod2luZG93cyldDQogICAgICAgIHsNCiAgICAgICAgICAgICgiaWNtIi50b19zdHJpbmcoKSwgcF9pY20pDQogICAgICAgIH0NCiAgICAgICAgI1tjZmcobm90KHdpbmRvd3MpKV0NCiAgICAgICAgew0KICAgICAgICAgICAgKCJpY2MiLnRvX3N0cmluZygpLCBwX2ljYykNCiAgICAgICAgfQ0KICAgIH0NCn0NCg0KI1tkZXJpdmUoU2VyaWFsaXplKV0NCnB1YiBzdHJ1Y3QgU3RhZ2VTdGF0dXMgew0KICAgIHB1YiBzdGFnZTFfY29tcGxldGU6IGJvb2wsDQogICAgcHViIHN0YWdlMl9jb21wbGV0ZTogYm9vbCwNCiAgICBwdWIgc3RhZ2UzX2NvbXBsZXRlOiBib29sLA0KICAgIHB1YiBzdGFnZTRfY29tcGxldGU6IGJvb2wsDQogICAgcHViIHByb2ZpbGVfcGF0aDogT3B0aW9uPFN0cmluZz4sDQp9DQoNCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgZm4gdmVyaWZ5X3N0YWdlX2FydGVmYWN0cyhjd2Q6IFN0cmluZywgYmFzZW5hbWU6IFN0cmluZykgLT4gU3RhZ2VTdGF0dXMgew0KICAgIGlmIGN3ZC50cmltKCkuaXNfZW1wdHkoKSB8fCBiYXNlbmFtZS50cmltKCkuaXNfZW1wdHkoKSB7DQogICAgICAgIHJldHVybiBTdGFnZVN0YXR1cyB7DQogICAgICAgICAgICBzdGFnZTFfY29tcGxldGU6IGZhbHNlLA0KICAgICAgICAgICAgc3RhZ2UyX2NvbXBsZXRlOiBmYWxzZSwNCiAgICAgICAgICAgIHN0YWdlM19jb21wbGV0ZTogZmFsc2UsDQogICAgICAgICAgICBzdGFnZTRfY29tcGxldGU6IGZhbHNlLA0KICAgICAgICAgICAgcHJvZmlsZV9wYXRoOiBOb25lLA0KICAgICAgICB9Ow0KICAgIH0NCg0KICAgIGxldCBiYXNlX3BhdGggPSBzdGQ6OnBhdGg6OlBhdGg6Om5ldygmY3dkKTsNCiAgICBsZXQgdGkxID0gYmFzZV9wYXRoLmpvaW4oZm9ybWF0ISgie30udGkxIiwgYmFzZW5hbWUpKS5leGlzdHMoKTsNCiAgICBsZXQgdGkyID0gYmFzZV9wYXRoLmpvaW4oZm9ybWF0ISgie30udGkyIiwgYmFzZW5hbWUpKS5leGlzdHMoKTsNCiAgICBsZXQgdGkzID0gYmFzZV9wYXRoLmpvaW4oZm9ybWF0ISgie30udGkzIiwgYmFzZW5hbWUpKS5leGlzdHMoKTsNCiAgICBsZXQgKF8sIHByb2ZfcCkgPSByZXNvbHZlX3Byb2ZpbGVfZXh0ZW5zaW9uKCZjd2QsICZiYXNlbmFtZSk7DQogICAgbGV0IHByb2ZfZXhpc3RzID0gcHJvZl9wLmV4aXN0cygpOw0KDQogICAgU3RhZ2VTdGF0dXMgew0KICAgICAgICBzdGFnZTFfY29tcGxldGU6IHRpMSwNCiAgICAgICAgc3RhZ2UyX2NvbXBsZXRlOiB0aTIsDQogICAgICAgIHN0YWdlM19jb21wbGV0ZTogdGkzLA0KICAgICAgICBzdGFnZTRfY29tcGxldGU6IHByb2ZfZXhpc3RzLA0KICAgICAgICBwcm9maWxlX3BhdGg6IGlmIHByb2ZfZXhpc3RzIHsgU29tZShwcm9mX3AudG9fc3RyaW5nX2xvc3N5KCkudG9fc3RyaW5nKCkpIH0gZWxzZSB7IE5vbmUgfSwNCiAgICB9DQp9DQoNCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgZm4gZ2V0X3Byb2ZpbGVfcGF0aChjd2Q6IFN0cmluZywgYmFzZW5hbWU6IFN0cmluZykgLT4gU3RyaW5nIHsNCiAgICBsZXQgKF8sIHBhdGgpID0gcmVzb2x2ZV9wcm9maWxlX2V4dGVuc2lvbigmY3dkLCAmYmFzZW5hbWUpOw0KICAgIHBhdGgudG9fc3RyaW5nX2xvc3N5KCkudG9fc3RyaW5nKCkNCn0NCg0KI1t0YXVyaTo6Y29tbWFuZF0NCnB1YiBhc3luYyBmbiBleHRyYWN0X2dhbXV0KA0KICAgIGFwcDogQXBwSGFuZGxlLA0KICAgIHN0YXRlOiBTdGF0ZTwnXywgUHJvY2Vzc01hbmFnZXI+LA0KICAgIGljY19wYXRoOiBTdHJpbmcsDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgbGV0IGJpbmFyeSA9IHJlc29sdmVfYmluYXJ5KGFwcC5jbG9uZSgpLCAiaWNjZ2FtdXQiLnRvX3N0cmluZygpKS5hd2FpdD87DQogICAgbGV0IHBhdGggPSBzdGQ6OnBhdGg6OlBhdGg6Om5ldygmaWNjX3BhdGgpOw0KICAgIGxldCBiYXNlbmFtZSA9IHBhdGguZmlsZV9zdGVtKCkubWFwKHxzfCBzLnRvX3N0cmluZ19sb3NzeSgpLnRvX3N0cmluZygpKS51bndyYXBfb3JfZWxzZSh8fCAicHJvZmlsZSIudG9fc3RyaW5nKCkpOw0KICAgIGxldCBwYXJlbnRfZGlyID0gcGF0aC5wYXJlbnQoKS5tYXAofHB8IHAudG9fc3RyaW5nX2xvc3N5KCkudG9fc3RyaW5nKCkpLnVud3JhcF9vcl9kZWZhdWx0KCk7DQogICAgDQogICAgLy8gQXV0by1kZXRlY3QgLmljYyB2cyAuaWNtIGlmIHRoZSBzcGVjaWZpZWQgcGF0aCBkb2VzIG5vdCBleGlzdCBkaXJlY3RseQ0KICAgIGxldCByZXNvbHZlZF9wYXRoID0gaWYgcGF0aC5leGlzdHMoKSB7DQogICAgICAgIGljY19wYXRoLmNsb25lKCkNCiAgICB9IGVsc2Ugew0KICAgICAgICBsZXQgYWx0ID0gaWYgaWNjX3BhdGguZW5kc193aXRoKCIuaWNjIikgew0KICAgICAgICAgICAgaWNjX3BhdGgucmVwbGFjZSgiLmljYyIsICIuaWNtIikNCiAgICAgICAgfSBlbHNlIGlmIGljY19wYXRoLmVuZHNfd2l0aCgiLmljbSIpIHsNCiAgICAgICAgICAgIGljY19wYXRoLnJlcGxhY2UoIi5pY20iLCAiLmljYyIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICBpY2NfcGF0aC5jbG9uZSgpDQogICAgICAgIH07DQogICAgICAgIGlmIHN0ZDo6cGF0aDo6UGF0aDo6bmV3KCZhbHQpLmV4aXN0cygpIHsNCiAgICAgICAgICAgIGFsdA0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgaWNjX3BhdGguY2xvbmUoKQ0KICAgICAgICB9DQogICAgfTsNCg0KICAgIGxldCBhcmdzID0gdmVjIVsNCiAgICAgICAgIi12Ii50b19zdHJpbmcoKSwNCiAgICAgICAgIi1kIi50b19zdHJpbmcoKSwNCiAgICAgICAgIjUwLjAiLnRvX3N0cmluZygpLA0KICAgICAgICByZXNvbHZlZF9wYXRoLA0KICAgIF07DQogICAgbGV0IGN3ZCA9IGlmIHBhcmVudF9kaXIuaXNfZW1wdHkoKSB7DQogICAgICAgIHJlc29sdmVfc2FmZV9jd2QoJmFwcCwgIiIpLm9rKCkNCiAgICB9IGVsc2Ugew0KICAgICAgICBTb21lKHBhcmVudF9kaXIpDQogICAgfTsNCiAgICANCiAgICBsZXQgaWQgPSBmb3JtYXQhKCJpY2NnYW11dF97fSIsIGJhc2VuYW1lKTsNCiAgICBzdGF0ZS5zcGF3bihhcHAsIGlkLCBiaW5hcnksIGFyZ3MsIGN3ZCkuYXdhaXQNCn0NCg0KI1tkZXJpdmUoRGVidWcsIERlc2VyaWFsaXplLCBTZXJpYWxpemUpXQ0KcHViIHN0cnVjdCBUYXJnZW5Db25maWcgew0KICAgIHB1YiBjb2xvdXJfc3BhY2U6IFN0cmluZywNCiAgICAjW3NlcmRlKGRlZmF1bHQpXQ0KICAgIHB1YiBwYXRjaF9jb3VudDogdTMyLA0KICAgIHB1YiB3aGl0ZV9wYXRjaGVzOiBPcHRpb248dTMyPiwNCiAgICBwdWIgYmxhY2tfcGF0Y2hlczogT3B0aW9uPHUzMj4sDQogICAgcHViIHRvdGFsX3BhdGNoZXM6IE9wdGlvbjx1MzI+LA0KICAgIHB1YiBiYXNlbmFtZTogU3RyaW5nLA0KICAgIHB1YiBjd2Q6IFN0cmluZywNCn0NCg0KZm4gZGVmYXVsdF9kcGkoKSAtPiB1MzIgew0KICAgIDMwMA0KfQ0KDQojW2Rlcml2ZShEZWJ1ZywgRGVzZXJpYWxpemUsIFNlcmlhbGl6ZSldDQpwdWIgc3RydWN0IFByaW50dGFyZ0NvbmZpZyB7DQogICAgcHViIGluc3RydW1lbnQ6IFN0cmluZywgICAvLyBPbmUgb2Y6ICJpMSIsICJwMyIsICJDTSIsICJTUyIsICIyMCIsICIyMiIsICI0MSIsICI1MSINCiAgICBwdWIgcGFnZV9zaXplOiBTdHJpbmcsICAgIC8vIE9uZSBvZjogIkE0IiwgIkE0UiIsICJBMyIsICJBMiIsICJMZXR0ZXIiLCAiTGV0dGVyUiIsICJMZWdhbCIsICI0eDYiLCAiMTF4MTciLCBvciAiV1dXeEhISCINCiAgICBwdWIgYml0X2RlcHRoOiB1OCwgICAgICAgIC8vIDggb3IgMTYNCiAgICAjW3NlcmRlKGRlZmF1bHQgPSAiZGVmYXVsdF9kcGkiKV0NCiAgICBwdWIgZHBpOiB1MzIsICAgICAgICAgICAgIC8vIFRJRkYgcmVzb2x1dGlvbiwgZGVmYXVsdHMgdG8gMzAwIERQSQ0KICAgIHB1YiBiYXNlbmFtZTogU3RyaW5nLCAgICAgLy8gTXVzdCBtYXRjaCB0aGUgLnRpMSBiYXNlbmFtZSBmcm9tIFN0YWdlIDENCiAgICBwdWIgY3dkOiBTdHJpbmcsICAgICAgICAgIC8vIFdvcmtpbmcgZGlyZWN0b3J5IHdoZXJlIHRoZSAudGkxIGZpbGUgcmVzaWRlcw0KfQ0KDQpwdWIgZm4gYnVpbGRfdGFyZ2VuX2FyZ3MoY29uZmlnOiAmVGFyZ2VuQ29uZmlnKSAtPiBWZWM8U3RyaW5nPiB7DQogICAgbGV0IG11dCBhcmdzID0gdmVjIVsNCiAgICAgICAgIi12Ii50b19zdHJpbmcoKSwNCiAgICAgICAgIi1kIi50b19zdHJpbmcoKSwNCiAgICBdOw0KICAgIA0KICAgIGlmIGNvbmZpZy5jb2xvdXJfc3BhY2UudG9fbG93ZXJjYXNlKCkgPT0gImNteWsiIHsNCiAgICAgICAgYXJncy5wdXNoKCI0Ii50b19zdHJpbmcoKSk7DQogICAgfSBlbHNlIHsNCiAgICAgICAgYXJncy5wdXNoKCIyIi50b19zdHJpbmcoKSk7IC8vIERlZmF1bHQgdG8gUkdCDQogICAgfQ0KICAgIA0KICAgIGxldCBwYXRjaGVzID0gaWYgY29uZmlnLnBhdGNoX2NvdW50ID4gMCB7DQogICAgICAgIGNvbmZpZy5wYXRjaF9jb3VudA0KICAgIH0gZWxzZSB7DQogICAgICAgIGNvbmZpZy50b3RhbF9wYXRjaGVzLnVud3JhcF9vcigwKQ0KICAgIH07DQogICAgDQogICAgaWYgcGF0Y2hlcyA+IDAgew0KICAgICAgICBhcmdzLnB1c2goIi1mIi50b19zdHJpbmcoKSk7DQogICAgICAgIGFyZ3MucHVzaChwYXRjaGVzLnRvX3N0cmluZygpKTsNCiAgICB9DQogICAgDQogICAgaWYgbGV0IFNvbWUod2hpdGUpID0gY29uZmlnLndoaXRlX3BhdGNoZXMgew0KICAgICAgICBhcmdzLnB1c2goIi1lIi50b19zdHJpbmcoKSk7DQogICAgICAgIGFyZ3MucHVzaCh3aGl0ZS50b19zdHJpbmcoKSk7DQogICAgfQ0KICAgIA0KICAgIGlmIGxldCBTb21lKGJsYWNrKSA9IGNvbmZpZy5ibGFja19wYXRjaGVzIHsNCiAgICAgICAgYXJncy5wdXNoKCItQiIudG9fc3RyaW5nKCkpOw0KICAgICAgICBhcmdzLnB1c2goYmxhY2sudG9fc3RyaW5nKCkpOw0KICAgIH0NCiAgICANCiAgICBhcmdzLnB1c2goY29uZmlnLmJhc2VuYW1lLmNsb25lKCkpOw0KICAgIGFyZ3MNCn0NCg0KcHViIGZuIGJ1aWxkX3ByaW50dGFyZ19hcmdzKGNvbmZpZzogJlByaW50dGFyZ0NvbmZpZykgLT4gVmVjPFN0cmluZz4gew0KICAgIGxldCBtdXQgYXJncyA9IHZlYyFbDQogICAgICAgICItdiIudG9fc3RyaW5nKCksDQogICAgICAgICItdSIudG9fc3RyaW5nKCksDQogICAgICAgICItaSIudG9fc3RyaW5nKCksDQogICAgICAgIGNvbmZpZy5pbnN0cnVtZW50LmNsb25lKCksDQogICAgICAgICItcCIudG9fc3RyaW5nKCksDQogICAgICAgIGNvbmZpZy5wYWdlX3NpemUuY2xvbmUoKSwNCiAgICBdOw0KDQogICAgaWYgY29uZmlnLmJpdF9kZXB0aCA9PSAxNiB7DQogICAgICAgIGFyZ3MucHVzaCgiLVQiLnRvX3N0cmluZygpKTsNCiAgICB9IGVsc2Ugew0KICAgICAgICBhcmdzLnB1c2goIi10Ii50b19zdHJpbmcoKSk7DQogICAgfQ0KICAgIGFyZ3MucHVzaChjb25maWcuZHBpLnRvX3N0cmluZygpKTsNCg0KICAgIGFyZ3MucHVzaChjb25maWcuYmFzZW5hbWUuY2xvbmUoKSk7DQogICAgYXJncw0KfQ0KDQojW3RhdXJpOjpjb21tYW5kXQ0KcHViIGFzeW5jIGZuIHJ1bl90YXJnZW4oDQogICAgYXBwOiBBcHBIYW5kbGUsDQogICAgc3RhdGU6IFN0YXRlPCdfLCBQcm9jZXNzTWFuYWdlcj4sDQogICAgY29uZmlnOiBUYXJnZW5Db25maWcsDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgbGV0IGJpbmFyeSA9IHJlc29sdmVfYmluYXJ5KGFwcC5jbG9uZSgpLCAidGFyZ2VuIi50b19zdHJpbmcoKSkuYXdhaXQ/Ow0KICAgIGxldCBhcmdzID0gYnVpbGRfdGFyZ2VuX2FyZ3MoJmNvbmZpZyk7DQogICAgbGV0IGlkID0gZm9ybWF0ISgidGFyZ2VuX3t9IiwgY29uZmlnLmJhc2VuYW1lKTsNCiAgICBsZXQgY3dkID0gU29tZShyZXNvbHZlX3NhZmVfY3dkKCZhcHAsICZjb25maWcuY3dkKT8pOw0KICAgIA0KICAgIHN0YXRlLnNwYXduKGFwcCwgaWQsIGJpbmFyeSwgYXJncywgY3dkKS5hd2FpdA0KfQ0KDQojW3RhdXJpOjpjb21tYW5kXQ0KcHViIGFzeW5jIGZuIHJ1bl9wcmludHRhcmcoDQogICAgYXBwOiBBcHBIYW5kbGUsDQogICAgc3RhdGU6IFN0YXRlPCdfLCBQcm9jZXNzTWFuYWdlcj4sDQogICAgY29uZmlnOiBQcmludHRhcmdDb25maWcsDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgbGV0IGJpbmFyeSA9IHJlc29sdmVfYmluYXJ5KGFwcC5jbG9uZSgpLCAicHJpbnR0YXJnIi50b19zdHJpbmcoKSkuYXdhaXQ/Ow0KICAgIGxldCBhcmdzID0gYnVpbGRfcHJpbnR0YXJnX2FyZ3MoJmNvbmZpZyk7DQogICAgbGV0IGlkID0gZm9ybWF0ISgicHJpbnR0YXJnX3t9IiwgY29uZmlnLmJhc2VuYW1lKTsNCiAgICBsZXQgY3dkID0gU29tZShyZXNvbHZlX3NhZmVfY3dkKCZhcHAsICZjb25maWcuY3dkKT8pOw0KDQogICAgc3RhdGUuc3Bhd24oYXBwLCBpZCwgYmluYXJ5LCBhcmdzLCBjd2QpLmF3YWl0DQp9DQoNCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgYXN5bmMgZm4gcmVhZF9maWxlX2Jhc2U2NChwYXRoOiBTdHJpbmcpIC0+IFJlc3VsdDxTdHJpbmcsIFN0cmluZz4gew0KICAgIHVzZSBzdGQ6OmZzOw0KICAgIGxldCBieXRlcyA9IGZzOjpyZWFkKCZwYXRoKS5tYXBfZXJyKHxlfCBmb3JtYXQhKCJGYWlsZWQgdG8gcmVhZCB7fToge30iLCBwYXRoLCBlKSk/Ow0KICAgIHVzZSBiYXNlNjQ6OkVuZ2luZTsNCiAgICBPayhiYXNlNjQ6OmVuZ2luZTo6Z2VuZXJhbF9wdXJwb3NlOjpTVEFOREFSRC5lbmNvZGUoJmJ5dGVzKSkNCn0NCg0KI1t0YXVyaTo6Y29tbWFuZF0NCnB1YiBhc3luYyBmbiByZWFkX3RpZmZfcHJldmlld19wbmcocGF0aDogU3RyaW5nKSAtPiBSZXN1bHQ8U3RyaW5nLCBTdHJpbmc+IHsNCiAgICB1c2Ugc3RkOjpmczsNCiAgICB1c2Ugc3RkOjppbzo6Q3Vyc29yOw0KICAgIHVzZSBiYXNlNjQ6OkVuZ2luZTsNCiAgICB1c2UgaW1hZ2U6OkltYWdlRm9ybWF0Ow0KDQogICAgbGV0IGZpbGVfYnl0ZXMgPSBmczo6cmVhZCgmcGF0aCkubWFwX2Vycih8ZXwgZm9ybWF0ISgiRmFpbGVkIHRvIHJlYWQge306IHt9IiwgcGF0aCwgZSkpPzsNCiAgICBsZXQgaW1nID0gaW1hZ2U6OmxvYWRfZnJvbV9tZW1vcnlfd2l0aF9mb3JtYXQoJmZpbGVfYnl0ZXMsIEltYWdlRm9ybWF0OjpUaWZmKQ0KICAgICAgICAubWFwX2Vycih8ZXwgZm9ybWF0ISgiRmFpbGVkIHRvIGRlY29kZSBUSUZGIHt9OiB7fSIsIHBhdGgsIGUpKT87DQoNCiAgICBsZXQgbWF4X2RpbSA9IHN0ZDo6Y21wOjptYXgoaW1nLndpZHRoKCksIGltZy5oZWlnaHQoKSk7DQogICAgbGV0IHByb2Nlc3NlZF9pbWcgPSBpZiBtYXhfZGltID4gMTIwMCB7DQogICAgICAgIGltZy5yZXNpemUoMTIwMCwgMTIwMCwgaW1hZ2U6OmltYWdlb3BzOjpGaWx0ZXJUeXBlOjpMYW5jem9zMykNCiAgICB9IGVsc2Ugew0KICAgICAgICBpbWcNCiAgICB9Ow0KDQogICAgbGV0IG11dCBwbmdfYnl0ZXMgPSBDdXJzb3I6Om5ldyhWZWM6Om5ldygpKTsNCiAgICBwcm9jZXNzZWRfaW1nDQogICAgICAgIC53cml0ZV90bygmbXV0IHBuZ19ieXRlcywgSW1hZ2VGb3JtYXQ6OlBuZykNCiAgICAgICAgLm1hcF9lcnIofGV8IGZvcm1hdCEoIkZhaWxlZCB0byBlbmNvZGUgUE5HIGZvciB7fToge30iLCBwYXRoLCBlKSk/Ow0KDQogICAgT2soYmFzZTY0OjplbmdpbmU6OmdlbmVyYWxfcHVycG9zZTo6U1RBTkRBUkQuZW5jb2RlKHBuZ19ieXRlcy5pbnRvX2lubmVyKCkpKQ0KfQ0KDQojW2Rlcml2ZShEZWJ1ZywgRGVzZXJpYWxpemUsIFNlcmlhbGl6ZSldDQpwdWIgc3RydWN0IENoYXJ0cmVhZENvbmZpZyB7DQogICAgcHViIGJhc2VuYW1lOiBTdHJpbmcsDQogICAgcHViIGN3ZDogU3RyaW5nLA0KICAgIHB1YiBwb3J0OiBPcHRpb248U3RyaW5nPiwNCn0NCg0KcHViIGZuIGJ1aWxkX2NoYXJ0cmVhZF9hcmdzKGNvbmZpZzogJkNoYXJ0cmVhZENvbmZpZykgLT4gVmVjPFN0cmluZz4gew0KICAgIGxldCBtdXQgYXJncyA9IHZlYyFbDQogICAgICAgICItdiIudG9fc3RyaW5nKCksDQogICAgICAgICItdSIudG9fc3RyaW5nKCksDQogICAgXTsNCg0KICAgIGlmIGxldCBTb21lKHJlZiBwb3J0KSA9IGNvbmZpZy5wb3J0IHsNCiAgICAgICAgaWYgIXBvcnQudHJpbSgpLmlzX2VtcHR5KCkgew0KICAgICAgICAgICAgYXJncy5wdXNoKCItYyIudG9fc3RyaW5nKCkpOw0KICAgICAgICAgICAgYXJncy5wdXNoKHBvcnQudHJpbSgpLnRvX3N0cmluZygpKTsNCiAgICAgICAgfQ0KICAgIH0NCg0KICAgIGFyZ3MucHVzaChjb25maWcuYmFzZW5hbWUuY2xvbmUoKSk7DQogICAgYXJncw0KfQ0KDQojW3RhdXJpOjpjb21tYW5kXQ0KcHViIGFzeW5jIGZuIHJ1bl9jaGFydHJlYWQoDQogICAgYXBwOiBBcHBIYW5kbGUsDQogICAgc3RhdGU6IFN0YXRlPCdfLCBQcm9jZXNzTWFuYWdlcj4sDQogICAgY29uZmlnOiBDaGFydHJlYWRDb25maWcsDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgbGV0IGJpbmFyeSA9IHJlc29sdmVfYmluYXJ5KGFwcC5jbG9uZSgpLCAiY2hhcnRyZWFkIi50b19zdHJpbmcoKSkuYXdhaXQ/Ow0KICAgIGxldCBhcmdzID0gYnVpbGRfY2hhcnRyZWFkX2FyZ3MoJmNvbmZpZyk7DQogICAgbGV0IGlkID0gZm9ybWF0ISgiY2hhcnRyZWFkX3t9IiwgY29uZmlnLmJhc2VuYW1lKTsNCiAgICBsZXQgY3dkID0gU29tZShyZXNvbHZlX3NhZmVfY3dkKCZhcHAsICZjb25maWcuY3dkKT8pOw0KDQogICAgc3RhdGUuc3Bhd24oYXBwLCBpZCwgYmluYXJ5LCBhcmdzLCBjd2QpLmF3YWl0DQp9DQoNCiNbZGVyaXZlKERlYnVnLCBEZXNlcmlhbGl6ZSwgU2VyaWFsaXplKV0NCnB1YiBzdHJ1Y3QgQXZlcmFnZUNvbmZpZyB7DQogICAgcHViIGlucHV0czogVmVjPFN0cmluZz4sDQogICAgcHViIG91dHB1dDogU3RyaW5nLA0KICAgIHB1YiBjd2Q6IFN0cmluZywNCn0NCg0KcHViIGZuIGJ1aWxkX2F2ZXJhZ2VfYXJncyhjb25maWc6ICZBdmVyYWdlQ29uZmlnKSAtPiBWZWM8U3RyaW5nPiB7DQogICAgbGV0IG11dCBhcmdzID0gdmVjIVsiLXYiLnRvX3N0cmluZygpXTsNCiAgICBmb3IgaW5wdXQgaW4gJmNvbmZpZy5pbnB1dHMgew0KICAgICAgICBhcmdzLnB1c2goaW5wdXQuY2xvbmUoKSk7DQogICAgfQ0KICAgIGFyZ3MucHVzaChjb25maWcub3V0cHV0LmNsb25lKCkpOw0KICAgIGFyZ3MNCn0NCg0KI1t0YXVyaTo6Y29tbWFuZF0NCnB1YiBhc3luYyBmbiBydW5fYXZlcmFnZSgNCiAgICBhcHA6IEFwcEhhbmRsZSwNCiAgICBzdGF0ZTogU3RhdGU8J18sIFByb2Nlc3NNYW5hZ2VyPiwNCiAgICBjb25maWc6IEF2ZXJhZ2VDb25maWcsDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgbGV0IGJpbmFyeSA9IHJlc29sdmVfYmluYXJ5KGFwcC5jbG9uZSgpLCAiYXZlcmFnZSIudG9fc3RyaW5nKCkpLmF3YWl0PzsNCiAgICBsZXQgYXJncyA9IGJ1aWxkX2F2ZXJhZ2VfYXJncygmY29uZmlnKTsNCiAgICBsZXQgaWQgPSBmb3JtYXQhKCJhdmVyYWdlX3t9IiwgY29uZmlnLm91dHB1dCk7DQogICAgbGV0IGN3ZCA9IFNvbWUocmVzb2x2ZV9zYWZlX2N3ZCgmYXBwLCAmY29uZmlnLmN3ZCk/KTsNCg0KICAgIHN0YXRlLnNwYXduKGFwcCwgaWQsIGJpbmFyeSwgYXJncywgY3dkKS5hd2FpdA0KfQ0KDQpmbiBzYW5pdGl6ZV9iYXNlbmFtZShiYXNlbmFtZTogJnN0cikgLT4gUmVzdWx0PFN0cmluZywgU3RyaW5nPiB7DQogICAgbGV0IG5hbWUgPSBiYXNlbmFtZS50cmltKCk7DQogICAgaWYgbmFtZS5pc19lbXB0eSgpIHsNCiAgICAgICAgcmV0dXJuIEVycigiYmFzZW5hbWUgaXMgZW1wdHkiLnRvX3N0cmluZygpKTsNCiAgICB9DQogICAgaWYgbmFtZS5jb250YWlucygnLycpIHx8IG5hbWUuY29udGFpbnMoJ1xcJykgfHwgbmFtZS5jb250YWlucygiLi4iKSB7DQogICAgICAgIHJldHVybiBFcnIoImJhc2VuYW1lIG11c3Qgbm90IGNvbnRhaW4gcGF0aCBzZXBhcmF0b3JzIi50b19zdHJpbmcoKSk7DQogICAgfQ0KICAgIE9rKG5hbWUudG9fc3RyaW5nKCkpDQp9DQoNCnB1YiBmbiBzbmFwc2hvdF90aTNfZmlsZW5hbWUoYmFzZW5hbWU6ICZzdHIsIHBhc3NfaW5kZXg6IHUzMikgLT4gUmVzdWx0PFN0cmluZywgU3RyaW5nPiB7DQogICAgaWYgcGFzc19pbmRleCA9PSAwIHsNCiAgICAgICAgcmV0dXJuIEVycigicGFzc19pbmRleCBtdXN0IGJlIDEtYmFzZWQiLnRvX3N0cmluZygpKTsNCiAgICB9DQogICAgbGV0IG5hbWUgPSBzYW5pdGl6ZV9iYXNlbmFtZShiYXNlbmFtZSk/Ow0KICAgIE9rKGZvcm1hdCEoInt9X3Bhc3N7fS50aTMiLCBuYW1lLCBwYXNzX2luZGV4KSkNCn0NCg0KLy8vIENvcHkgYHtjd2R9L3tiYXNlbmFtZX0udGkzYCB0byBge2N3ZH0ve2Jhc2VuYW1lfV9wYXNze059LnRpM2AgYW5kIHJlbW92ZSB0aGUNCi8vLyBjYW5vbmljYWwgZmlsZSBzbyBTdGFnZSA0IHN0YXlzIGxvY2tlZCB1bnRpbCBGaW5pc2ggKCMxMDkgLyAjMTEwKS4NCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgZm4gc25hcHNob3RfdGkzKGN3ZDogU3RyaW5nLCBiYXNlbmFtZTogU3RyaW5nLCBwYXNzX2luZGV4OiB1MzIpIC0+IFJlc3VsdDxTdHJpbmcsIFN0cmluZz4gew0KICAgIGxldCBkZXN0X25hbWUgPSBzbmFwc2hvdF90aTNfZmlsZW5hbWUoJmJhc2VuYW1lLCBwYXNzX2luZGV4KT87DQogICAgbGV0IG5hbWUgPSBzYW5pdGl6ZV9iYXNlbmFtZSgmYmFzZW5hbWUpPzsNCiAgICBsZXQgZGlyID0gc3RkOjpwYXRoOjpQYXRoOjpuZXcoJmN3ZCk7DQogICAgaWYgIWRpci5pc19kaXIoKSB7DQogICAgICAgIHJldHVybiBFcnIoZm9ybWF0ISgid29ya2luZyBkaXJlY3RvcnkgZG9lcyBub3QgZXhpc3Q6IHt9IiwgY3dkKSk7DQogICAgfQ0KICAgIGxldCBzcmMgPSBkaXIuam9pbihmb3JtYXQhKCJ7fS50aTMiLCBuYW1lKSk7DQogICAgaWYgIXNyYy5pc19maWxlKCkgew0KICAgICAgICByZXR1cm4gRXJyKGZvcm1hdCEoIm1lYXN1cmVtZW50IGZpbGUgbm90IGZvdW5kOiB7fSIsIHNyYy5kaXNwbGF5KCkpKTsNCiAgICB9DQogICAgbGV0IGRlc3QgPSBkaXIuam9pbigmZGVzdF9uYW1lKTsNCiAgICBzdGQ6OmZzOjpjb3B5KCZzcmMsICZkZXN0KS5tYXBfZXJyKHxlfCBmb3JtYXQhKCJmYWlsZWQgdG8gc25hcHNob3QgLnRpMzoge30iLCBlKSk/Ow0KICAgIHN0ZDo6ZnM6OnJlbW92ZV9maWxlKCZzcmMpDQogICAgICAgIC5tYXBfZXJyKHxlfCBmb3JtYXQhKCJmYWlsZWQgdG8gcmVtb3ZlIGNhbm9uaWNhbCAudGkzIGFmdGVyIHNuYXBzaG90OiB7fSIsIGUpKT87DQogICAgT2soZGVzdF9uYW1lKQ0KfQ0KDQovLy8gQ29weSBhIHBhc3MgZmlsZSAocmVsYXRpdmUgbmFtZSkgYmFjayB0byBge2N3ZH0ve2Jhc2VuYW1lfS50aTNgLg0KI1t0YXVyaTo6Y29tbWFuZF0NCnB1YiBmbiBwcm9tb3RlX3RpMyhjd2Q6IFN0cmluZywgc291cmNlOiBTdHJpbmcsIGJhc2VuYW1lOiBTdHJpbmcpIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgbGV0IG5hbWUgPSBzYW5pdGl6ZV9iYXNlbmFtZSgmYmFzZW5hbWUpPzsNCiAgICBsZXQgc3JjX25hbWUgPSBzb3VyY2UudHJpbSgpOw0KICAgIGlmIHNyY19uYW1lLmlzX2VtcHR5KCkNCiAgICAgICAgfHwgc3JjX25hbWUuY29udGFpbnMoJy8nKQ0KICAgICAgICB8fCBzcmNfbmFtZS5jb250YWlucygnXFwnKQ0KICAgICAgICB8fCBzcmNfbmFtZS5jb250YWlucygiLi4iKQ0KICAgIHsNCiAgICAgICAgcmV0dXJuIEVycigic291cmNlIGZpbGVuYW1lIGlzIGludmFsaWQiLnRvX3N0cmluZygpKTsNCiAgICB9DQogICAgbGV0IGRpciA9IHN0ZDo6cGF0aDo6UGF0aDo6bmV3KCZjd2QpOw0KICAgIGxldCBzcmMgPSBkaXIuam9pbihzcmNfbmFtZSk7DQogICAgaWYgIXNyYy5pc19maWxlKCkgew0KICAgICAgICByZXR1cm4gRXJyKGZvcm1hdCEoInNvdXJjZSBtZWFzdXJlbWVudCBmaWxlIG5vdCBmb3VuZDoge30iLCBzcmMuZGlzcGxheSgpKSk7DQogICAgfQ0KICAgIGxldCBkZXN0ID0gZGlyLmpvaW4oZm9ybWF0ISgie30udGkzIiwgbmFtZSkpOw0KICAgIHN0ZDo6ZnM6OmNvcHkoJnNyYywgJmRlc3QpLm1hcF9lcnIofGV8IGZvcm1hdCEoImZhaWxlZCB0byBwcm9tb3RlIC50aTM6IHt9IiwgZSkpPzsNCiAgICBPaygoKSkNCn0NCg0KI1tkZXJpdmUoRGVidWcsIERlc2VyaWFsaXplLCBTZXJpYWxpemUpXQ0KcHViIHN0cnVjdCBDb2xwcm9mQ29uZmlnIHsNCiAgICBwdWIgYWxnb3JpdGhtOiBTdHJpbmcsDQogICAgcHViIHF1YWxpdHk6IFN0cmluZywNCiAgICBwdWIgaW50ZW50OiBPcHRpb248U3RyaW5nPiwNCiAgICBwdWIgY29weXJpZ2h0OiBPcHRpb248U3RyaW5nPiwNCiAgICBwdWIgZGVzY3JpcHRpb246IE9wdGlvbjxTdHJpbmc+LA0KICAgIHB1YiBiYXNlbmFtZTogU3RyaW5nLA0KICAgIHB1YiBjd2Q6IFN0cmluZywNCn0NCg0KcHViIGZuIGJ1aWxkX2NvbHByb2ZfYXJncyhjb25maWc6ICZDb2xwcm9mQ29uZmlnKSAtPiBWZWM8U3RyaW5nPiB7DQogICAgbGV0IG11dCBhcmdzID0gdmVjIVsNCiAgICAgICAgIi12Ii50b19zdHJpbmcoKSwNCiAgICAgICAgIi1hIi50b19zdHJpbmcoKSwNCiAgICAgICAgY29uZmlnLmFsZ29yaXRobS5jbG9uZSgpLA0KICAgICAgICAiLXEiLnRvX3N0cmluZygpLA0KICAgICAgICBjb25maWcucXVhbGl0eS5jbG9uZSgpLA0KICAgIF07DQoNCiAgICBpZiBsZXQgU29tZShyZWYgaW50ZW50KSA9IGNvbmZpZy5pbnRlbnQgew0KICAgICAgICBpZiAhaW50ZW50LnRyaW0oKS5pc19lbXB0eSgpIHsNCiAgICAgICAgICAgIGFyZ3MucHVzaCgiLXAiLnRvX3N0cmluZygpKTsNCiAgICAgICAgICAgIGFyZ3MucHVzaChpbnRlbnQuY2xvbmUoKSk7DQogICAgICAgIH0NCiAgICB9DQoNCiAgICBpZiBsZXQgU29tZShyZWYgY3IpID0gY29uZmlnLmNvcHlyaWdodCB7DQogICAgICAgIGlmICFjci50cmltKCkuaXNfZW1wdHkoKSB7DQogICAgICAgICAgICBhcmdzLnB1c2goIi1DIi50b19zdHJpbmcoKSk7DQogICAgICAgICAgICBhcmdzLnB1c2goY3IuY2xvbmUoKSk7DQogICAgICAgIH0NCiAgICB9DQoNCiAgICBpZiBsZXQgU29tZShyZWYgZGVzYykgPSBjb25maWcuZGVzY3JpcHRpb24gew0KICAgICAgICBpZiAhZGVzYy50cmltKCkuaXNfZW1wdHkoKSB7DQogICAgICAgICAgICBhcmdzLnB1c2goIi1EIi50b19zdHJpbmcoKSk7DQogICAgICAgICAgICBhcmdzLnB1c2goZGVzYy5jbG9uZSgpKTsNCiAgICAgICAgfQ0KICAgIH0NCg0KICAgIGFyZ3MucHVzaChjb25maWcuYmFzZW5hbWUuY2xvbmUoKSk7DQogICAgYXJncw0KfQ0KDQojW3RhdXJpOjpjb21tYW5kXQ0KcHViIGFzeW5jIGZuIHJ1bl9jb2xwcm9mKA0KICAgIGFwcDogQXBwSGFuZGxlLA0KICAgIHN0YXRlOiBTdGF0ZTwnXywgUHJvY2Vzc01hbmFnZXI+LA0KICAgIGNvbmZpZzogQ29scHJvZkNvbmZpZywNCikgLT4gUmVzdWx0PCgpLCBTdHJpbmc+IHsNCiAgICBsZXQgYmluYXJ5ID0gcmVzb2x2ZV9iaW5hcnkoYXBwLmNsb25lKCksICJjb2xwcm9mIi50b19zdHJpbmcoKSkuYXdhaXQ/Ow0KICAgIGxldCBhcmdzID0gYnVpbGRfY29scHJvZl9hcmdzKCZjb25maWcpOw0KICAgIGxldCBpZCA9IGZvcm1hdCEoImNvbHByb2Zfe30iLCBjb25maWcuYmFzZW5hbWUpOw0KICAgIGxldCBjd2QgPSBTb21lKHJlc29sdmVfc2FmZV9jd2QoJmFwcCwgJmNvbmZpZy5jd2QpPyk7DQoNCiAgICBzdGF0ZS5zcGF3bihhcHAsIGlkLCBiaW5hcnksIGFyZ3MsIGN3ZCkuYXdhaXQNCn0NCg0KI1tkZXJpdmUoRGVidWcsIERlc2VyaWFsaXplLCBTZXJpYWxpemUpXQ0KcHViIHN0cnVjdCBQcm9mY2hlY2tDb25maWcgew0KICAgIHB1YiB0aTNfcGF0aDogU3RyaW5nLA0KICAgIHB1YiBpY2NfcGF0aDogU3RyaW5nLA0KICAgIHB1YiBjd2Q6IFN0cmluZywNCn0NCg0KcHViIGZuIGJ1aWxkX3Byb2ZjaGVja19hcmdzKGNvbmZpZzogJlByb2ZjaGVja0NvbmZpZykgLT4gVmVjPFN0cmluZz4gew0KICAgIHZlYyFbDQogICAgICAgICItdiIudG9fc3RyaW5nKCksDQogICAgICAgICItayIudG9fc3RyaW5nKCksDQogICAgICAgICItcyIudG9fc3RyaW5nKCksDQogICAgICAgIGNvbmZpZy50aTNfcGF0aC5jbG9uZSgpLA0KICAgICAgICBjb25maWcuaWNjX3BhdGguY2xvbmUoKSwNCiAgICBdDQp9DQoNCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgYXN5bmMgZm4gcnVuX3Byb2ZjaGVjaygNCiAgICBhcHA6IEFwcEhhbmRsZSwNCiAgICBzdGF0ZTogU3RhdGU8J18sIFByb2Nlc3NNYW5hZ2VyPiwNCiAgICBtdXQgY29uZmlnOiBQcm9mY2hlY2tDb25maWcsDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgbGV0IGJpbmFyeSA9IHJlc29sdmVfYmluYXJ5KGFwcC5jbG9uZSgpLCAicHJvZmNoZWNrIi50b19zdHJpbmcoKSkuYXdhaXQ/Ow0KDQogICAgLy8gQXV0by1kZXRlY3QgLmljYyB2cyAuaWNtIGlmIHRoZSBzcGVjaWZpZWQgaWNjX3BhdGggZG9lcyBub3QgZXhpc3QgZGlyZWN0bHkNCiAgICBpZiAhc3RkOjpwYXRoOjpQYXRoOjpuZXcoJmNvbmZpZy5pY2NfcGF0aCkuZXhpc3RzKCkgew0KICAgICAgICBsZXQgYWx0ID0gaWYgY29uZmlnLmljY19wYXRoLmVuZHNfd2l0aCgiLmljYyIpIHsNCiAgICAgICAgICAgIGNvbmZpZy5pY2NfcGF0aC5yZXBsYWNlKCIuaWNjIiwgIi5pY20iKQ0KICAgICAgICB9IGVsc2UgaWYgY29uZmlnLmljY19wYXRoLmVuZHNfd2l0aCgiLmljbSIpIHsNCiAgICAgICAgICAgIGNvbmZpZy5pY2NfcGF0aC5yZXBsYWNlKCIuaWNtIiwgIi5pY2MiKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgY29uZmlnLmljY19wYXRoLmNsb25lKCkNCiAgICAgICAgfTsNCiAgICAgICAgaWYgc3RkOjpwYXRoOjpQYXRoOjpuZXcoJmFsdCkuZXhpc3RzKCkgew0KICAgICAgICAgICAgY29uZmlnLmljY19wYXRoID0gYWx0Ow0KICAgICAgICB9DQogICAgfQ0KDQogICAgbGV0IGFyZ3MgPSBidWlsZF9wcm9mY2hlY2tfYXJncygmY29uZmlnKTsNCiAgICBsZXQgaWQgPSBmb3JtYXQhKCJwcm9mY2hlY2tfe30iLCBjb25maWcudGkzX3BhdGgpOw0KICAgIGxldCBjd2QgPSBTb21lKHJlc29sdmVfc2FmZV9jd2QoJmFwcCwgJmNvbmZpZy5jd2QpPyk7DQoNCiAgICBzdGF0ZS5zcGF3bihhcHAsIGlkLCBiaW5hcnksIGFyZ3MsIGN3ZCkuYXdhaXQNCn0NCg0KI1t0YXVyaTo6Y29tbWFuZF0NCnB1YiBhc3luYyBmbiBnZXRfcHJpbnRlcnMoKSAtPiBSZXN1bHQ8VmVjPGNyYXRlOjpwcmludDo6UHJpbnRlcj4sIFN0cmluZz4gew0KICAgICNbY2ZnKHdpbmRvd3MpXQ0KICAgIHsNCiAgICAgICAgY3JhdGU6OnByaW50Ojp3aW5kb3dzOjpnZXRfcHJpbnRlcnMoKQ0KICAgIH0NCiAgICAjW2NmZyh1bml4KV0NCiAgICB7DQogICAgICAgIGNyYXRlOjpwcmludDo6dW5peDo6Z2V0X3ByaW50ZXJzKCkNCiAgICB9DQogICAgI1tjZmcobm90KGFueSh3aW5kb3dzLCB1bml4KSkpXQ0KICAgIHsNCiAgICAgICAgRXJyKCJOYXRpdmUgcHJpbnRlciBlbnVtZXJhdGlvbiBpcyBub3Qgc3VwcG9ydGVkIG9uIHRoaXMgcGxhdGZvcm0uIi50b19zdHJpbmcoKSkNCiAgICB9DQp9DQoNCiNbdGF1cmk6OmNvbW1hbmRdDQpwdWIgYXN5bmMgZm4gZ2V0X3ByaW50ZXJfY2FwYWJpbGl0aWVzKA0KICAgIHByaW50ZXJfbmFtZTogU3RyaW5nLA0KKSAtPiBSZXN1bHQ8Y3JhdGU6OmludDo6UHJpbnRlckNhcGFiaWxpdGllcywgU3RyaW5nPiB7DQogICAgI1tjZmcod2luZG93cyldDQogICAgew0KICAgICAgICBjcmF0ZTo6cHJpbnQ6OndpbmRvd3M6OmdldF9wcmludGVyX2NhcGFiaWxpdGllcygmcHJpbnRlcl9uYW1lKQ0KICAgIH0NCiAgICAjW2NmZyh1bml4KV0NCiAgICB7DQogICAgICAgIGNyYXRlOjpwcmludDo6dW5peDo6Z2V0X3ByaW50ZXJfY2FwYWJpbGl0aWVzKCZwcmludGVyX25hbWUpDQogICAgfQ0KICAgICNbY2ZnKG5vdChhbnkod2luZG93cywgdW5peCkpKV0NCiAgICB7DQogICAgICAgIGxldCBfID0gcHJpbnRlcl9uYW1lOw0KICAgICAgICBFcnIoIlByaW50ZXIgY2FwYWJpbGl0aWVzIHF1ZXJ5IGlzIG5vdCBzdXBwb3J0ZWQgb24gdGhpcyBwbGF0Zm9ybS4iLnRvX3N0cmluZygpKQ0KICAgIH0NCn0NCg0KI1t0YXVyaTo6Y29tbWFuZF0NCnB1YiBhc3luYyBmbiBzaG93X3ByaW50ZXJfcHJvcGVydGllcygNCiAgICBzdGF0ZTogU3RhdGU8J18sIGNyYXRlOjpwcmludDo6UHJpbnRlckRldk1vZGVTdG9yZT4sDQogICAgcHJpbnRlcl9uYW1lOiBTdHJpbmcsDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgI1tjZmcod2luZG93cyldDQogICAgew0KICAgICAgICBjcmF0ZTo6cHJpbnQ6OndpbmRvd3M6OnNob3dfcHJpbnRlcl9wcm9wZXJ0aWVzKCZwcmludGVyX25hbWUsICZzdGF0ZSkNCiAgICB9DQogICAgI1tjZmcodW5peCldDQogICAgew0KICAgICAgICBsZXQgXyA9IChzdGF0ZSwgcHJpbnRlcl9uYW1lKTsNCiAgICAgICAgT2soKCkpDQogICAgfQ0KICAgICNbY2ZnKG5vdChhbnkod2luZG93cywgdW5peCkpKV0NCiAgICB7DQogICAgICAgIGxldCBfID0gKHN0YXRlLCBwcmludGVyX25hbWUpOw0KICAgICAgICBFcnIoIlByaW50ZXIgcHJvcGVydGllcyBkaWFsb2cgaXMgbm90IHN1cHBvcnRlZCBvbiB0aGlzIHBsYXRmb3JtLiIudG9fc3RyaW5nKCkpDQogICAgfQ0KfQ0KDQojW3RhdXJpOjpjb21tYW5kXQ0KcHViIGFzeW5jIGZuIHByaW50X3RhcmdldF9uYXRpdmUoDQogICAgc3RhdGU6IFN0YXRlPCdfLCBjcmF0ZTo6cHJpbnQ6OlByaW50ZXJEZXZNb2RlU3RvcmU+LA0KICAgIHByaW50ZXJfbmFtZTogU3RyaW5nLA0KICAgIHRpZmZfcGF0aDogU3RyaW5nLA0KICAgIG9wdGlvbnM6IE9wdGlvbjxjcmF0ZTo6cHJpbnQ6OlByaW50T3B0aW9ucz4sDQopIC0+IFJlc3VsdDwoKSwgU3RyaW5nPiB7DQogICAgI1tjZmcod2luZG93cyldDQogICAgew0KICAgICAgICBjcmF0ZTo6cHJpbnQ6OndpbmRvd3M6OnByaW50X3RhcmdldCgNCiAgICAgICAgICAgICZwcmludGVyX25hbWUsDQogICAgICAgICAgICAMDHRpZmZfcGF0aCwNCiAgICAgICAgICAgIG9wdGlvbnMuYXNfcmVmKCksDQogICAgICAgICAgICBTb21lKCZzdGF0ZSksDQogICAgICAgICkNCiAgICB9DQogICAgI1tjZmcodW5peCldDQogICAgew0KICAgICAgICBsZXQgXyA9IHN0YXRlOw0KICAgICAgICBjcmF0ZTo6cHJpbnQ6OnVuaXg6OnByaW50X3RhcmdldCgNCiAgICAgICAgICAgICZwcmludGVyX25hbWUsDQogICAgICAgICAgICAMDHRpZmZfcGF0aCwNCiAgICAgICAgICAgIG9wdGlvbnMuYXNfcmVmKCksDQogICAgICAgICkNCiAgICB9DQogICAgI1tjZmcobm90KGFueSh3aW5kb3dzLCB1bml4KSkpXQ0KICAgIHsNCiAgICAgICAgbGV0IF8gPSAoc3RhdGUsIHByaW50ZXJfbmFtZSwgdGlmZl9wYXRoLCBvcHRpb25zKTsNCiAgICAgICAgRXJyKCJOYXRpdmUgcmF3IHByaW50aW5nIGlzIG5vdCBzdXBwb3J0ZWQgb24gdGhpcyBwbGF0Zm9ybS4iLnRvX3N0cmluZygpKQ0KICAgIH0NCn0NCg0KI1tjZmcodGVzdCldDQptb2QgdGVzdHMgew0KICAgIHVzZSBzdXBlcjo6KjsNCg0KICAgICNbdGVzdF0NCiAgICBmbiB0ZXN0X2J1aWxkX3Rhcmdlbl9hcmdzX3JnYigpIHsNCiAgICAgICAgbGV0IGNvbmZpZyA9IFRhcmdlbkNvbmZpZyB7DQogICAgICAgICAgICBjb2xvdXJfc3BhY2U6ICJyZ2IiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgcGF0Y2hfY291bnQ6IDgwMCwNCiAgICAgICAgICAgIHdoaXRlX3BhdGNoZXM6IFNvbWUoNCksDQogICAgICAgICAgICBibGFja19wYXRjaGVzOiBOb25lLA0KICAgICAgICAgICAgdG90YWxfcGF0Y2hlczogTm9uZSwNCiAgICAgICAgICAgIGJhc2VuYW1lOiAibXlfcHJvZmlsZSIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBjd2Q6ICIvdG1wIi50b19zdHJpbmcoKSwNCiAgICAgICAgfTsNCiAgICAgICAgbGV0IGFyZ3MgPSBidWlsZF90YXJnZW5fYXJncygmY29uZmlnKTsNCiAgICAgICAgYXNzZXJ0X2VxIShhcmdzLCB2ZWMhWyItdiIsICItZCIsICIyIiwgIi1mIiwgIjgwMCIsICItZSIsICI0IiwgIm15X3Byb2ZpbGUiXSk7DQogICAgfQ0KDQogICAgI1t0ZXN0XQ0KICAgIGZuIHRlc3RfYnVpbGRfdGFyZ2VuX2FyZ3NfY215aygpIHsNCiAgICAgICAgbGV0IGNvbmZpZyA9IFRhcmdlbkNvbmZpZyB7DQogICAgICAgICAgICBjb2xvdXJfc3BhY2U6ICJjbXlrIi50b19zdHJpbmcoKSwNCiAgICAgICAgICAgIHBhdGNoX2NvdW50OiAxNTAwLA0KICAgICAgICAgICAgd2hpdGVfcGF0Y2hlczogTm9uZSwNCiAgICAgICAgICAgIGJsYWNrX3BhdGNoZXM6IFNvbWUoOCksDQogICAgICAgICAgICB0b3RhbF9wYXRjaGVzOiBOb25lLA0KICAgICAgICAgICAgYmFzZW5hbWU6ICJjbXlrX3Byb2ZpbGUiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgY3dkOiAiL3RtcCIudG9fc3RyaW5nKCksDQogICAgICAgIH07DQogICAgICAgIGxldCBhcmdzID0gYnVpbGRfdGFyZ2VuX2FyZ3MoJmNvbmZpZyk7DQogICAgICAgIGFzc2VydF9lcSEoYXJncywgdmVjIVsiLXYiLCAiLWQiLCAiNCIsICItZiIsICIxNTAwIiwgIi1CIiwgIjgiLCAiY215a19wcm9maWxlIl0pOw0KICAgIH0NCg0KICAgICNbdGVzdF0NCiAgICBmbiB0ZXN0X2J1aWxkX3Rhcmdlbl9hcmdzX3RvdGFsX3BhdGNoZXNfZmFsbGJhY2soKSB7DQogICAgICAgIGxldCBjb25maWcgPSBUYXJnZW5Db25maWcgew0KICAgICAgICAgICAgY29sb3VyX3NwYWNlOiAicmdiIi50b19zdHJpbmcoKSwNCiAgICAgICAgICAgIHBhdGNoX2NvdW50OiAwLA0KICAgICAgICAgICAgd2hpdGVfcGF0Y2hlczogTm9uZSwNCiAgICAgICAgICAgIGJsYWNrX3BhdGNoZXM6IE5vbmUsDQogICAgICAgICAgICB0b3RhbF9wYXRjaGVzOiBTb21lKDQwMCksDQogICAgICAgICAgICBiYXNlbmFtZTogImRyYWZ0X3Byb2ZpbGUiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgY3dkOiAiL3RtcCIudG9fc3RyaW5nKCksDQogICAgICAgIH07DQogICAgICAgIGxldCBhcmdzID0gYnVpbGRfdGFyZ2VuX2FyZ3MoJmNvbmZpZyk7DQogICAgICAgIGFzc2VydF9lcSEoYXJncywgdmVjIVsiLXYiLCAiLWQiLCAiMiIsICItZiIsICI0MDAiLCAiZHJhZnRfcHJvZmlsZSJdKTsNCiAgICB9DQoNCiAgICAjW3Rlc3RdDQogICAgZm4gdGVzdF9idWlsZF9wcmludHRhcmdfYXJnc19pMV9hNF84Yml0KCkgew0KICAgICAgICBsZXQgY29uZmlnID0gUHJpbnR0YXJnQ29uZmlnIHsNCiAgICAgICAgICAgIGluc3RydW1lbnQ6ICJpMSIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBwYWdlX3NpemU6ICJBNCIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBiaXRfZGVwdGg6IDgsDQogICAgICAgICAgICBkcGk6IDEwMCwNCiAgICAgICAgICAgIGJhc2VuYW1lOiAibXlfcHJvZmlsZSIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBjd2Q6ICIvdG1wIi50b19zdHJpbmcoKSwNCiAgICAgICAgfTsNCiAgICAgICAgbGV0IGFyZ3MgPSBidWlsZF9wcmludHRhcmdfYXJncygmY29uZmlnKTsNCiAgICAgICAgYXNzZXJ0X2VxIShhcmdzLCB2ZWMhWyItdiIsICItdSIsICItaSIsICJpMSIsICItcCIsICJBNCIsICItdCIsICIxMDAiLCAibXlfcHJvZmlsZSJdKTsNCiAgICB9DQoNCiAgICAjW3Rlc3RdDQogICAgZm4gdGVzdF9idWlsZF9wcmludHRhcmdfYXJnc19jbV9sZXR0ZXJfMTZiaXQoKSB7DQogICAgICAgIGxldCBjb25maWcgPSBQcmludHRhcmdDb25maWcgew0KICAgICAgICAgICAgaW5zdHJ1bWVudDogIkNNIi50b19zdHJpbmcoKSwNCiAgICAgICAgICAgIHBhZ2Vfc2l6ZTogIkxldHRlciIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBiaXRfZGVwdGg6IDE2LA0KICAgICAgICAgICAgZHBpOiAzMDAsDQogICAgICAgICAgICBiYXNlbmFtZTogImNteWtfcHJvZmlsZSIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBjd2Q6ICIvaG9tZS91c2VyIi50b19zdHJpbmcoKSwNCiAgICAgICAgfTsNCiAgICAgICAgbGV0IGFyZ3MgPSBidWlsZF9wcmludHRhcmdfYXJncygmY29uZmlnKTsNCiAgICAgICAgYXNzZXJ0X2VxIShhcmdzLCB2ZWMhWyItdiIsICItdSIsICItaSIsICJDTSIsICItcCIsICJMZXR0ZXIiLCAiLVQiLCAiMzAwIiwgImNteWtfcHJvZmlsZSJdKTsNCiAgICB9DQoNCiAgICAjW3Rlc3RdDQogICAgZm4gdGVzdF9idWlsZF9wcmludHRhcmdfYXJnc19jdXN0b21fcGFnZV9zaXplKCkgew0KICAgICAgICBsZXQgY29uZmlnID0gUHJpbnR0YXJnQ29uZmlnIHsNCiAgICAgICAgICAgIGluc3RydW1lbnQ6ICJTUyIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBwYWdlX3NpemU6ICIyMDB4NDAwIi50b19zdHJpbmcoKSwNCiAgICAgICAgICAgIGJpdF9kZXB0aDogOCwNCiAgICAgICAgICAgIGRwaTogMTUwLA0KICAgICAgICAgICAgYmFzZW5hbWU6ICJjdXN0b21fdGFyZ2V0Ii50b19zdHJpbmcoKSwNCiAgICAgICAgICAgIGN3ZDogIi90bXAiLnRvX3N0cmluZygpLA0KICAgICAgICB9Ow0KICAgICAgICBsZXQgYXJncyA9IGJ1aWxkX3ByaW50dGFyZ19hcmdzKCZjb25maWcpOw0KICAgICAgICBhc3NlcnRfZXEhKGFyZ3MsIHZlYyFbIi12IiwgIi11IiwgIi1pIiwgIlNTIiwgIi1wIiwgIjIwMHg0MDAiLCAiLXQiLCAiMTUwIiwgImN1c3RvbV90YXJnZXQiXSk7DQogICAgfQ0KDQogICAgI1t0ZXN0XQ0KICAgIGZuIHRlc3RfYnVpbGRfY2hhcnRyZWFkX2FyZ3NfYXV0bygpIHsNCiAgICAgICAgbGV0IGNvbmZpZyA9IENoYXJ0cmVhZENvbmZpZyB7DQogICAgICAgICAgICBiYXNlbmFtZTogIm15X3Byb2ZpbGUiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgY3dkOiAiL2hvbWUvdXNlciIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBwb3J0OiBOb25lLA0KICAgICAgICB9Ow0KICAgICAgICBsZXQgYXJncyA9IGJ1aWxkX2NoYXJ0cmVhZF9hcmdzKCZjb25maWcpOw0KICAgICAgICBhc3NlcnRfZXEhKGFyZ3MsIHZlYyFbIi12IiwgIi11IiwgIm15X3Byb2ZpbGUiXSk7DQogICAgfQ0KDQogICAgI1t0ZXN0XQ0KICAgIGZuIHRlc3RfYnVpbGRfY2hhcnRyZWFkX2FyZ3NfZW1wdHlfcG9ydCgpIHsNCiAgICAgICAgbGV0IGNvbmZpZyA9IENoYXJ0cmVhZENvbmZpZyB7DQogICAgICAgICAgICBiYXNlbmFtZTogIm15X3Byb2ZpbGUiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgY3dkOiAiL2hvbWUvdXNlciIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBwb3J0OiBTb21lKCIiLnRvX3N0cmluZygpKSwNCiAgICAgICAgfTsNCiAgICAgICAgbGV0IGFyZ3MgPSBidWlsZF9jaGFydHJlYWRfYXJncygmY29uZmlnKTsNCiAgICAgICAgYXNzZXJ0X2VxIShhcmdzLCB2ZWMhWyItdiIsICItdSIsICJteV9wcm9maWxlIl0pOw0KICAgIH0NCg0KICAgICNbdGVzdF0NCiAgICBmbiB0ZXN0X2J1aWxkX2NoYXJ0cmVhZF9hcmdzX3dpdGhfcG9ydCgpIHsNCiAgICAgICAgbGV0IGNvbmZpZyA9IENoYXJ0cmVhZENvbmZpZyB7DQogICAgICAgICAgICBiYXNlbmFtZTogIm15X3Byb2ZpbGUiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgY3dkOiAiL2hvbWUvdXNlciIudG9fc3RyaW5nKCksDQogICAgICAgICAgICBwb3J0OiBTb21lKCIxIi50b19zdHJpbmcoKSksDQogICAgICAgIH07DQogICAgICAgIGxldCBhcmdzID0gYnVpbGRfY2hhcnRyZWFkX2FyZ3MoJmNvbmZpZyk7DQogICAgICAgIGFzc2VydF9lcSEoYXJncywgdmVjIVsiLXYiLCAiLXUiLCAiLWMiLCAiMSIsICJteV9wcm9maWxlIl0pOw0KICAgIH0NCg0KICAgICNbdGVzdF0NCiAgICBmbiB0ZXN0X2J1aWxkX2F2ZXJhZ2VfYXJncygpIHsNCiAgICAgICAgbGV0IGNvbmZpZyA9IEF2ZXJhZ2VDb25maWcgew0KICAgICAgICAgICAgaW5wdXRzOiB2ZWMhWyJwYXNzMS50aTMiLnRvX3N0cmluZygpLCAicGFzczIudGkzIi50b19zdHJpbmcoKV0sDQogICAgICAgICAgICBvdXRwdXQ6ICJhdmcudGkzIi50b19zdHJpbmcoKSwNCiAgICAgICAgICAgIGN3ZDogIi9ob21lL3VzZXIiLnRvX3N0cmluZygpLA0KICAgICAgICB9Ow0KICAgICAgICBsZXQgYXJncyA9IGJ1aWxkX2F2ZXJhZ2VfYXJncygmY29uZmlnKTsNCiAgICAgICAgYXNzZXJ0X2VxIShhcmdzLCB2ZWMhWyItdiIsICJwYXNzMS50aTMiLCAicGFzczIudGkzIiwgImF2Zy50aTMiXSk7DQogICAgfQ0KDQogICAgI1t0ZXN0XQ0KICAgIGZuIHRlc3RfYnVpbGRfYXZlcmFnZV9hcmdzX3Bhc3NfZmlsZXMoKSB7DQogICAgICAgIGxldCBjb25maWcgPSBBdmVyYWdlQ29uZmlnIHsNCiAgICAgICAgICAgIGlucHV0czogdmVjIVsiam9iX3Bhc3MxLnRpMyIudG9fc3RyaW5nKCksICJqb2JfcGFzczIudGkzIi50b19zdHJpbmcoKV0sDQogICAgICAgICAgICBvdXRwdXQ6ICImpvYi50aTMiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgY3dkOiAiL2hvbWUvdXNlciIudG9fc3RyaW5nKCksDQogICAgICAgIH07DQogICAgICAgIGxldCBhcmdzID0gYnVpbGRfYXZlcmFnZV9hcmdzKCZjb25maWcpOw0KICAgICAgICBhc3NlcnRfZXEhKGFyZ3MsIHZlYyFbIi12IiwgImpvYl9wYXNzMS50aTMiLCAiam9iX3Bhc3MyLnRpMyIsICImpvYi50aTMiXSk7DQogICAgfQ0KDQogICAgI1t0ZXN0XQ0KICAgIGZuIHRlc3Rfc25hcHNob3RfdGkzX2ZpbGVuYW1lKCkgew0KICAgICAgICBhc3NlcnRfZXEhKHNuYXBzaG90X3RpM19maWxlbmFtZSgiZm9vIiwgMSkudW53cmFwKCksICJmb29fcGFzczEudGkzIik7DQogICAgICAgIGFzc2VydF9lcSEoc25hcHNob3RfdGkzX2ZpbGVuYW1lKCJmb28iLCAyKS51bndyYXAoKSwgImZvb19wYXNzMi50aTMiKTsNCiAgICAgICAgYXNzZXJ0IShzbmFwc2hvdF90aTNfZmlsZW5hbWUoImZvbyIsIDApLmlzX2VycigpKTsNCiAgICAgICAgYXNzZXJ0IShzbmFwc2hvdF90aTNfZmlsZW5hbWUoIi4uL3giLCAxKS5pc19lcnIoKSk7DQogICAgICAgIGFzc2VydCEoc25hcHNob3RfdGkzX2ZpbGVuYW1lKCJhL2IiLCAxKS5pc19lcnIoKSk7DQogICAgfQ0KDQogICAgI1t0ZXN0XQ0KICAgIGZuIHRlc3Rfc25hcHNob3RfYW5kX3Byb21vdGVfdGkzX3JvdW5kdHJpcCgpIHsNCiAgICAgICAgbGV0IGRpciA9IHN0ZDo6ZW52Ojp0ZW1wX2RpcigpLmpvaW4oZm9ybWF0ISgNCiAgICAgICAgICAgICJpY2NlcnlfdGkzX3t9X3t9IiwNCiAgICAgICAgICAgIHN0ZDo6cHJvY2Vzczo6aWQoKSwNCiAgICAgICAgICAgIHN0ZDo6dGltZTo6U3lzdGVtVGltZTo6bm93KCkNCiAgICAgICAgICAgICAgICAuZHVyYXRpb25fc2luY2Uoc3RkOjp0aW1lOjpVTklYX0VQT0NIKQ0KICAgICAgICAgICAgICAgIC51bndyYXAoKQ0KICAgICAgICAgICAgICAgIC5hc19uYW5vcygpDQogICAgICAgICkpOw0KICAgICAgICBzdGQ6OmZzOjpjcmVhdGVfZGlyX2FsbCgmZGlyKS51bndyYXAoKTsNCiAgICAgICAgbGV0IGNhbm9uaWNhbCA9IGRpci5qb2luKCJqb2IudGkzIik7DQogICAgICAgIHN0ZDo6ZnM6OndyaXRlKCZjYW5vbmljYWwsIGIiUEFTUzEiKS51bndyYXAoKTsNCiAgICAgICAgbGV0IGN3ZCA9IGRpci50b19zdHJpbmdfbG9zc3koKS50b19zdHJpbmcoKTsNCiAgICAgICAgbGV0IG5hbWUgPSBzbmFwc2hvdF90aTMoY3dkLmNsb25lKCksICJqb2IiLnRvX3N0cmluZygpLCAxKS51bndyYXAoKTsNCiAgICAgICAgYXNzZXJ0X2VxIShuYW1lLCAiam9iX3Bhc3MxLnRpMyIpOw0KICAgICAgICBhc3NlcnQhKCFjYW5vbmljYWwuZXhpc3RzKCksICJjYW5vbmljYWwgLnRpMyBtdXN0IGJlIHJlbW92ZWQgYWZ0ZXIgc25hcHNob3QiKTsNCiAgICAgICAgYXNzZXJ0X2VxIShzdGQ6OmZzOjpyZWFkKGRpci5qb2luKCJqb2JfcGFzczEudGkzIikpLnVud3JhcCgpLCBiIlBBU1MxIik7DQoNCiAgICAgICAgc3RkOjpmczo6d3JpdGUoZGlyLmpvaW4oImpvYl9wYXNzMi50aTMiKSwgYiJQQVNTMiIpLnVud3JhcCgpOw0KICAgICAgICBsZXQgbmFtZTIgPSBzbmFwc2hvdF90aTNfZmlsZW5hbWUoImpvYiIsIDIpLnVud3JhcCgpOw0KICAgICAgICBhc3NlcnRfZXEhKG5hbWUyLCAiam9iX3Bhc3MyLnRpMyIpOw0KICAgICAgICBhc3NlcnQhKGRpci5qb2luKCJqb2JfcGFzczEudGkzIikuZXhpc3RzKCksICJwYXNzIDEgbXVzdCBzdXJ2aXZlIHBhc3MgMiIpOw0KDQogICAgICAgIHByb21vdGVfdGkzKGN3ZCwgbmFtZSwgImpvYiIudG9fc3RyaW5nKCkpLnVud3JhcCgpOw0KICAgICAgICBhc3NlcnRfZXEhKHN0ZDo6ZnM6OnJlYWQoJmNhbm9uaWNhbCkudW53cmFwKCksIGIiUEFTUzEiKTsNCiAgICAgICAgbGV0IF8gPSBzdGQ6OmZzOjpyZW1vdmVfZGlyX2FsbCgmZGlyKTsNCiAgICB9DQoNCiAgICAjW3Rlc3RdDQogICAgZm4gdGVzdF9idWlsZF9jb2xwcm9mX2FyZ3MoKSB7DQogICAgICAgIGxldCBjb25maWcgPSBDb2xwcm9mQ29uZmlnIHsNCiAgICAgICAgICAgIHF1YWxpdHk6ICJoIi50b19zdHJpbmcoKSwNCiAgICAgICAgICAgIGFsZ29yaXRobTogImwiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgaW50ZW50OiBOb25lLA0KICAgICAgICAgICAgZGVzY3JpcHRpb246IFNvbWUoIk15IFByb2ZpbGUiLnRvX3N0cmluZygpKSwNCiAgICAgICAgICAgIGNvcHlyaWdodDogU29tZSgiMjAyNiBBQ01FIi50b19zdHJpbmcoKSksDQogICAgICAgICAgICBiYXNlbmFtZTogIm15X3Byb2ZpbGUiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgY3dkOiAiL2hvbWUvdXNlciIudG9fc3RyaW5nKCksDQogICAgICAgIH07DQogICAgICAgIGxldCBhcmdzID0gYnVpbGRfY29scHJvZl9hcmdzKCZjb25maWcpOw0KICAgICAgICBhc3NlcnRfZXEhKA0KICAgICAgICAgICAgYXJncywNCiAgICAgICAgICAgIHZlYyFbIi12IiwgIi1hIiwgImwiLCAiLXEiLCAiaCIsICItQyIsICIyMDI2IEFDREUiLCAiLUQiLCAiTXkgUHJvZmlsZSIsICJteV9wcm9maWxlIl0NCiAgICAgICAgKTsNCiAgICB9DQoNCiAgICAjW3Rlc3RdDQogICAgZm4gdGVzdF9idWlsZF9wcm9mY2hlY2tfYXJncygpIHsNCiAgICAgICAgbGV0IGNvbmZpZyA9IFByb2ZjaGVja0NvbmZpZyB7DQogICAgICAgICAgICB0aTNfcGF0aDogIm15X3Byb2ZpbGUudGkzIi50b19zdHJpbmcoKSwNCiAgICAgICAgICAgIGljY19wYXRoOiAibXlfcHJvZmlsZS5pY2MiLnRvX3N0cmluZygpLA0KICAgICAgICAgICAgY3dkOiAiL2hvbWUvdXNlciIudG9fc3RyaW5nKCksDQogICAgICAgIH07DQogICAgICAgIGxldCBhcmdzID0gYnVpbGRfcHJvZmNoZWNrX2FyZ3MoJmNvbmZpZyk7DQogICAgICAgIGFzc2VydF9lcSEoYXJncywgdmVjIVsiLXYiLCAiLWsiLCAiLXMiLCAibXlfcHJvZmlsZS50aTMiLCAibXlfcHJvZmlsZS5pY2MiXSk7DQogICAgfQ0KDQogICAgI1t0ZXN0XQ0KICAgIGZuIHRlc3RfZ2V0X2JpbmFyeV9jYW5kaWRhdGVzKCkgew0KICAgICAgICBsZXQgY2FuZGlkYXRlcyA9IGdldF9iaW5hcnlfY2FuZGlkYXRlcygidGFyZ2VuIik7DQogICAgICAgIGlmIGNmZyEod2luZG93cykgew0KICAgICAgICAgICAgYXNzZXJ0X2VxIShjYW5kaWRhdGVzLCB2ZWMhWyJ0YXJnZW4uZXhlIiwgInRhcmdlbiJdKTsNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgYXNzZXJ0X2VxIShjYW5kaWRhdGVzLCB2ZWMhWyJ0YXJnZW4iXSk7DQogICAgICAgIH0NCg0KICAgICAgICBsZXQgY2FuZGlkYXRlc19leGUgPSBnZXRfYmluYXJ5X2NhbmRpZGF0ZXMoInRhcmdlbi5leGUiKTsNCiAgICAgICAgYXNzZXJ0X2VxIShjYW5kaWRhdGVzX2V4ZSwgdmVjIVsidGFyZ2VuLmV4ZSJdKTsNCiAgICB9DQp9DQo= \ No newline at end of file -- 2.39.5