UNPKG

hack-emulator-js

Version:

a hack emulator for the nand2tetris project

828 lines (706 loc) 53.3 kB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ /** * As the hack computer is quite small * Everything is placed in this file * CPU, RAM, ROM, Screen, Keyboard */ var assembler = require('hack-assembler') function Hack() { this.ROM = new Array(32767 + 1) // 0x0000 to 0x8000 this.RAM = new Array(24576 + 1) // 0x0000 to 0x6000 this.RAM.fill(0) this.KBD = 24576 // Keyboard position in 0x6000 this.PC = 0 this.DRegister = 0 this.ARegister = 0 this.comp = { // '0101010': '0', '0101010': () => { return 0 }, // '0111111': '1', '0111111': () => { return 1 }, // '0111010': '-1', '0111010': () => { return -1 }, // '0001100': 'D', '0001100': () => { return this.DRegister }, // '0110000': 'A', '0110000': () => { return this.ARegister }, // '0001101': '!D', '0001101': () => { return ~this.DRegister }, // '0110001': '!A', '0110001': () => { return ~this.ARegister }, // '0001111': '-D', '0001111': () => { return -this.DRegister }, // '0110011': '-A', '0110011': () => { return -this.ARegister }, // '0011111': 'D+1', '0011111': () => { return this.DRegister + 1 }, // '0110111': 'A+1', '0110111': () => { return this.ARegister + 1 }, // '0001110': 'D-1', '0001110': () => { return this.DRegister - 1 }, // '0110010': 'A-1', '0110010': () => { return this.ARegister - 1 }, // '0000010': 'D+A', '0000010': () => { return this.DRegister + this.ARegister }, // '0010011': 'D-A', '0010011': () => { return this.DRegister - this.ARegister }, // '0000111': 'A-D', '0000111': () => { return this.ARegister - this.DRegister }, // '0000000': 'D&A', '0000000': () => { return this.DRegister & this.ARegister }, // '0010101': 'D|A', '0010101': () => { return this.DRegister | this.ARegister }, // '1110000': 'M', '1110000': () => { return this.RAM[this.ARegister] }, // '1110001': '!M', '1110001': () => { return ~this.RAM[this.ARegister] }, // '1110011': '-M', '1110011': () => { return -this.RAM[this.ARegister] }, // '1110111': 'M+1', '1110111': () => { return this.RAM[this.ARegister] + 1 }, // '1110010': 'M-1', '1110010': () => { return this.RAM[this.ARegister] - 1 }, // '1000010': 'D+M', '1000010': () => { return this.DRegister + this.RAM[this.ARegister] }, // '1010011': 'D-M', '1010011': () => { return this.DRegister - this.RAM[this.ARegister] }, // '1000111': 'M-D', '1000111': () => { return this.RAM[this.ARegister] - this.DRegister }, // '1000000': 'D&M', '1000000': () => { return this.DRegister & this.RAM[this.ARegister] }, // '1010101': 'D|M' '1010101': () => { return this.DRegister | this.RAM[this.ARegister] } } // Destination this.dest = { // '000': '0', '000': (val) => { // Do nothing }, // '001': 'M', '001': (val) => { this.setRAM(val) }, // '010': 'D', '010': (val) => { this.DRegister = val }, // '011': 'MD', '011': (val) => { this.DRegister = val this.setRAM(val) }, // '100': 'A', '100': (val) => { this.ARegister = val }, // '101': 'AM', '101': (val) => { this.setRAM(val) this.ARegister = val }, // '110': 'AD', '110': (val) => { this.DRegister = val this.ARegister = val }, // '111': 'AMD' '111': (val) => { this.DRegister = val this.setRAM(val) this.ARegister = val } } this.jump = { // '000': '0', '000': (val) => { return false }, // '001': 'JGT', '001': (val) => { if (val > 0) { return true } }, // '010': 'JEQ', '010': (val) => { if (val == 0) { return true } }, // '011': 'JGE', '011': (val) => { if (val >= 0) { return true } }, // '100': 'JLT', '100': (val) => { if (val < 0) { return true } }, // '101': 'JNE', '101': (val) => { if (val != 0) { return true } }, // '110': 'JLE', '110': (val) => { if (val <= 0) { return true } }, // '111': 'JMP' '111': (val) => { return true } } // Screen this.SIZE_BITS = 512 * 256 this.SIZE_WORDS = this.SIZE_BITS / 16 this.SCREEN_RAM = 16384 this.CANVAS = null this.CANVAS_CTX = null this.CANVAS_DATA = null function getBinVal(i) { var bin = i.toString(2) while (bin.length < 32) { bin = "0" + bin } return bin } // Used for the screen // Screen operates bit patterns // 1 is on 0 is off function dec2bin(dec) { var bin = (dec >>> 0).toString(2) var bit32 = getBinVal(bin) return bit32.substring(16, 32) } // Get X and Y position of the word on the image // According to the position in RAM // RAM[16384 + r*32 + c%16] this.getImageRowColumn = function () { var numWord = this.ARegister - this.SCREEN_RAM var y = Math.floor(numWord * 16 / 512) var x = numWord * 16 % 512 var xy = { x: x, y: y } return xy } // Draw pixel on canvas this.drawPixel = function (x, y, r, g, b, a) { var index = (x + y * this.CANVAS.width) * 4; this.CANVAS_DATA.data[index + 0] = r; this.CANVAS_DATA.data[index + 1] = g; this.CANVAS_DATA.data[index + 2] = b; this.CANVAS_DATA.data[index + 3] = a; } this.updateImageData = function (val) { // get bin val var rowColumn = this.getImageRowColumn() var x = rowColumn.x var y = rowColumn.y var binVal = dec2bin(val) var binAry = binVal.split('') binAry.forEach((elem, i) => { if (elem == 1) { this.drawPixel(x + 16 - i, y, 0, 0, 0, 255) } else { this.drawPixel(x + 16 - i, y, 255, 255, 255, 0) } }) } this.updateCanvas = function () { this.CANVAS_CTX.putImageData(this.CANVAS_DATA, 0, 0); } // Just in order to turn off screen this.screen = 1 // Set screen RAM for fast access // As screen is updated often this.setRAM = function (val) { this.RAM[this.ARegister] = val if (this.ARegister >= this.SCREEN_RAM && this.ARegister < this.KBD) { if (this.screen) { this.updateImageData(val) } } } // Instruction: ixxaccccccdddjjj // Get opcode // 0 = C instruction // 1 = A instruction this.getOpcode = function (ins) { return ins.substring(0, 1) } this.getComp = function (ins) { return ins.substring(3, 10) } this.getDest = function (ins) { return ins.substring(10, 13) } this.getJump = function (ins) { return ins.substring(13, 16) } this.debugCycle = function (ins) { if (!this.debug) { return } var opcode = this.getOpcode(ins) console.log('Opcode ', opcode) if (opcode == 0) { console.log('At ', parseInt(ins, 2)) console.log('At (value) ', this.RAM[parseInt(ins, 2)]) } console.log('Ins ', ins) console.log('PC ', this.PC) console.log('After Parse') console.log('ALUOut', this.ALUOut) console.log('AReg ', this.ARegister) console.log('DReg ', this.DRegister) console.log(this.RAM.slice(0, 16)) console.log('---') } this.cyclesDone = 0 this.debug = 0 this.cycle = function () { this.currentPC = this.PC if (typeof this.ROM[this.PC] == 'undefined') { return } var ins = this.ROM[this.PC] var opcode = this.getOpcode(ins) if (opcode == 1) { this.cycleC(ins) } else { this.cycleA(ins) } this.cyclesDone++ if (this.cyclesDone % 100000 == 0) { if (this.debug) { console.log(this.cyclesDone) } } } this.cycleC = function (ins) { var comp = this.getComp(ins) var dest = this.getDest(ins) var jump = this.getJump(ins) var jumped = false var ALUOut = this.comp[comp]() this.ALUOut = ALUOut if (dest != '000') { this.dest[dest](ALUOut) } if (jump != '000') { if (this.jump[jump](ALUOut)) { this.PC = this.ARegister jumped = true } } if (!jumped) { this.PC++ } this.debugCycle(ins) } this.cycleA = function (ins) { this.ARegister = parseInt(ins, 2) this.PC++ this.debugCycle(ins) } this.loadROM = function (str) { var ass = new assembler(str) var bin = ass.getAssembledCode() var program = bin.split('\n') for (i = 0; i < program.length; i++) { this.ROM[i] = program[i]; } } } module.exports = Hack },{"hack-assembler":3}],2:[function(require,module,exports){ var symbolTable = { 'R0': 0, 'R1': 1, 'R2': 2, 'R3': 3, 'R4': 4, 'R5': 5, 'R6': 6, 'R7': 7, 'R8': 8, 'R9': 9, 'R10': 10, 'R11': 11, 'R12': 12, 'R13': 13, 'R14': 14, 'R15': 15, 'SCREEN': 16384, 'KBD': 24576, 'SP': 0, 'LCL': 1, 'ARG': 2, 'THIS': 3, 'THAT': 4 } module.exports.symbolTable = symbolTable var dest = { '0': '000', 'M': '001', 'D': '010', 'MD': '011', 'A': '100', 'AM': '101', 'AD': '110', 'AMD': '111' } module.exports.dest = dest var jump = { '0': '000', 'JGT': '001', 'JEQ': '010', 'JGE': '011', 'JLT': '100', 'JNE': '101', 'JLE': '110', 'JMP': '111' } module.exports.jump = jump var comp = { '0': '0101010', '1': '0111111', '-1': '0111010', 'D': '0001100', 'A': '0110000', '!D': '0001101', '!A': '0110001', '-D': '0001111', '-A': '0110011', 'D+1': '0011111', 'A+1': '0110111', 'D-1': '0001110', 'A-1': '0110010', 'D+A': '0000010', 'D-A': '0010011', 'A-D': '0000111', 'D&A': '0000000', 'D|A': '0010101', 'M': '1110000', '!M': '1110001', '-M': '1110011', 'M+1': '1110111', 'M-1': '1110010', 'D+M': '1000010', 'D-M': '1010011', 'M-D': '1000111', 'D&M': '1000000', 'D|M': '1010101' } module.exports.comp = comp },{}],3:[function(require,module,exports){ var {symbolTable, dest, jump, comp} = require('./constants') var removeComments = require('remove-comments-regex') function assembler(str) { // Trim comments var str = removeComments(str) // Split code array var codeAry = str.split('\n') var code = {} // Move to object with trimmed values codeAry.forEach((element, i) => { var line = element.trim() if (line) { code[i] = element.trim() } }); var final = {} var labels = {} var i = 0; // Get LOOPS, e.g. (LOOP) and line number var regExp = /\(([^)]+)\)/ for (key in code) { var matches = regExp.exec(code[key]); if (!matches) { final[i++] = code[key]; } else { labels[matches[1]] = parseInt(i) } } // Move labels and code to this this.labels = labels this.code = final // Sustitute labels with line number this.subLabel = function () { for (key in this.code) { let line = this.code[key] line = line.replace('@', '') if (this.labels.hasOwnProperty(line)) { this.code[key] = '@' + this.labels[line] } } } // Memory start after last register this.currentM = 16 // Add other symbols to symbol table this.addSymbolsTotable = function () { for (key in this.code) { if (this.getOpcode(this.code[key]) === 0) { let line = this.code[key] line = line.replace('@', '') if (!symbolTable.hasOwnProperty(line) && isNaN(line)) { symbolTable[line] = this.currentM this.currentM++ } } } } // Substitute all symbols this.subSymbols = function () { for (key in this.code) { let line = this.code[key] line = line.replace('@', '') if (symbolTable.hasOwnProperty(line)) { this.code[key] = '@' + symbolTable[line] } } } // Get opcode this.getOpcode = function (line) { if (line.startsWith('@')) { return 0 } return 1 } // Parse C opcode // ixxaccccccdddjjj this.parseOpcodeC = function (line) { var parts = {} // Assignment var ary = line.split('=') if (ary.length > 1) { parts['d'] = dest[ary[0]].toString() parts['c'] = comp[ary[1]].toString() parts['j'] = '000' } var ary = line.split(';') if (ary.length > 1) { parts['c'] = comp[ary[0]].toString() parts['d'] = '000' //dest[ary[0]].toString() parts['j'] = jump[ary[1]].toString() } var instruction = '111' + parts['c'] + parts['d'] + parts['j'] return instruction } // Parse A opcode this.parseOpcodeA = function (line) { line = line.replace('@', '') var i = parseInt(line) var bin = i.toString(2) while(bin.length < 16) { bin = "0" + bin } return bin } // Assemble this.getAssembledCode = function () { this.subLabel() this.addSymbolsTotable() this.subSymbols() var instructions = '' for (key in this.code) { let opcode = this.getOpcode(this.code[key]) if (opcode == 1) { instructions +=this.parseOpcodeC(this.code[key]) + '\n' } else { instructions += this.parseOpcodeA(this.code[key]) + '\n' } } return instructions.trim() } this.assemble = function () { var instructions = this.getAssembledCode() console.log(instructions) } } module.exports = assembler },{"./constants":2,"remove-comments-regex":4}],4:[function(require,module,exports){ function trimComments(str) { var uncommented = str.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') // Trim top and bottom return uncommented.replace(/^\s+|\s+$/g, '') } module.exports = trimComments },{}],5:[function(require,module,exports){ var Hack = require('./index') window.onload = () => { // Hack special keys compared to ascii var keys = { 13: 128, 8: 129, 37: 130, 38: 131, 39: 132, 40: 133, 36: 134, 35: 135, 33: 136, 24: 137, 45: 138, 46: 139, 27: 140, 112: 141, 113: 142, 114: 143, 115: 144, 116: 145, 117: 146, 118: 147, 119: 148, 120: 149, 121: 150, 122: 151, 123: 152 } function handkeKeyDown(e) { var keyCode = parseInt(e.keyCode) if (keyCode in keys) { keyCode = keys[keyCode] } hack.RAM[24576] = keyCode } function handkeKeyUp() { hack.RAM[24576] = 0 } var hack var intervalID var running = false var opcodes = parseInt(document.getElementById('opcodes').value) var milli = parseInt(document.getElementById('milli').value) document.getElementById('opcodes').addEventListener('input', function (e) { opcodes = this.value }) document.getElementById('milli').addEventListener('input', function (e) { milli = this.value }) document.getElementById("run").addEventListener("click", () => { if (running) { window.removeEventListener("keydown", handkeKeyDown, true) window.removeEventListener("keyup", handkeKeyUp, true) stopAnimation() clearInterval(intervalID) running = false } var asm = document.getElementById('asm').value setupHack(asm) }) document.getElementById("stop").addEventListener("click", () => { if (running) { window.removeEventListener("keydown", handkeKeyDown, true) window.removeEventListener("keyup", handkeKeyUp, true) stopAnimation() clearInterval(intervalID) hack = null running = false } }) function setupHack(asm) { if (running) { return } hack = new Hack() hack.CANVAS = document.getElementById("screen") hack.CANVAS_CTX = hack.CANVAS.getContext("2d") hack.CANVAS_DATA = hack.CANVAS_CTX.getImageData(0, 0, hack.CANVAS.width, hack.CANVAS.height) hack.loadROM(asm) hack.debug = 0 intervalID = setInterval(function () { for (var i = 0; i < opcodes; i++) { hack.cycle() } if (hack.cyclesDone % 100000 == 0) { document.getElementById('numOpcodes').innerHTML = hack.cyclesDone } }, milli) window.addEventListener("keydown", handkeKeyDown) window.addEventListener("keyup", handkeKeyUp) startAnmation() running = true } var requestId function loopAnimation(time) { requestId = undefined hack.updateCanvas() startAnmation(); } function startAnmation() { if (!requestId) { requestId = requestAnimationFrame(loopAnimation); } } function stopAnimation() { if (requestId) { cancelAnimationFrame(requestId); requestId = undefined; } } } },{"./index":1}]},{},[5]) //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Vzci9saWIvbm9kZV9tb2R1bGVzL3dhdGNoaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJpbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9oYWNrLWFzc2VtYmxlci9jb25zdGFudHMuanMiLCJub2RlX21vZHVsZXMvaGFjay1hc3NlbWJsZXIvaW5kZXguanMiLCJub2RlX21vZHVsZXMvcmVtb3ZlLWNvbW1lbnRzLXJlZ2V4L2luZGV4LmpzIiwidGVzdC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDamJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzSkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qKlxuICogQXMgdGhlIGhhY2sgY29tcHV0ZXIgaXMgcXVpdGUgc21hbGxcbiAqIEV2ZXJ5dGhpbmcgaXMgcGxhY2VkIGluIHRoaXMgZmlsZVxuICogQ1BVLCBSQU0sIFJPTSwgU2NyZWVuLCBLZXlib2FyZFxuICovXG5cbnZhciBhc3NlbWJsZXIgPSByZXF1aXJlKCdoYWNrLWFzc2VtYmxlcicpXG5cbmZ1bmN0aW9uIEhhY2soKSB7XG5cbiAgICB0aGlzLlJPTSA9IG5ldyBBcnJheSgzMjc2NyArIDEpIC8vIDB4MDAwMCB0byAweDgwMDBcbiAgICB0aGlzLlJBTSA9IG5ldyBBcnJheSgyNDU3NiArIDEpIC8vIDB4MDAwMCB0byAweDYwMDBcbiAgICB0aGlzLlJBTS5maWxsKDApXG5cbiAgICB0aGlzLktCRCA9IDI0NTc2ICAgIC8vIEtleWJvYXJkIHBvc2l0aW9uIGluIDB4NjAwMFxuICAgIHRoaXMuUEMgPSAwXG4gICAgdGhpcy5EUmVnaXN0ZXIgPSAwXG4gICAgdGhpcy5BUmVnaXN0ZXIgPSAwXG5cbiAgICB0aGlzLmNvbXAgPSB7XG4gICAgICAgIC8vICcwMTAxMDEwJzogJzAnLFxuICAgICAgICAnMDEwMTAxMCc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiAwXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcwMTExMTExJzogJzEnLFxuICAgICAgICAnMDExMTExMSc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiAxXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcwMTExMDEwJzogJy0xJyxcbiAgICAgICAgJzAxMTEwMTAnOiAoKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gLTFcbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzAwMDExMDAnOiAnRCcsXG4gICAgICAgICcwMDAxMTAwJzogKCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuRFJlZ2lzdGVyXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcwMTEwMDAwJzogJ0EnLFxuICAgICAgICAnMDExMDAwMCc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLkFSZWdpc3RlclxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDAwMTEwMSc6ICchRCcsXG4gICAgICAgICcwMDAxMTAxJzogKCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIH50aGlzLkRSZWdpc3RlclxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDExMDAwMSc6ICchQScsXG4gICAgICAgICcwMTEwMDAxJzogKCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIH50aGlzLkFSZWdpc3RlclxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDAwMTExMSc6ICctRCcsXG4gICAgICAgICcwMDAxMTExJzogKCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIC10aGlzLkRSZWdpc3RlclxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDExMDAxMSc6ICctQScsXG4gICAgICAgICcwMTEwMDExJzogKCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIC10aGlzLkFSZWdpc3RlclxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDAxMTExMSc6ICdEKzEnLFxuICAgICAgICAnMDAxMTExMSc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLkRSZWdpc3RlciArIDFcbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzAxMTAxMTEnOiAnQSsxJyxcbiAgICAgICAgJzAxMTAxMTEnOiAoKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5BUmVnaXN0ZXIgKyAxXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcwMDAxMTEwJzogJ0QtMScsXG4gICAgICAgICcwMDAxMTEwJzogKCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuRFJlZ2lzdGVyIC0gMVxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDExMDAxMCc6ICdBLTEnLFxuICAgICAgICAnMDExMDAxMCc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLkFSZWdpc3RlciAtIDFcbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzAwMDAwMTAnOiAnRCtBJyxcbiAgICAgICAgJzAwMDAwMTAnOiAoKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5EUmVnaXN0ZXIgKyB0aGlzLkFSZWdpc3RlclxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDAxMDAxMSc6ICdELUEnLFxuICAgICAgICAnMDAxMDAxMSc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLkRSZWdpc3RlciAtIHRoaXMuQVJlZ2lzdGVyXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcwMDAwMTExJzogJ0EtRCcsXG4gICAgICAgICcwMDAwMTExJzogKCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuQVJlZ2lzdGVyIC0gdGhpcy5EUmVnaXN0ZXJcbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzAwMDAwMDAnOiAnRCZBJyxcbiAgICAgICAgJzAwMDAwMDAnOiAoKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5EUmVnaXN0ZXIgJiB0aGlzLkFSZWdpc3RlclxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDAxMDEwMSc6ICdEfEEnLFxuICAgICAgICAnMDAxMDEwMSc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLkRSZWdpc3RlciB8IHRoaXMuQVJlZ2lzdGVyXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcxMTEwMDAwJzogJ00nLFxuICAgICAgICAnMTExMDAwMCc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLlJBTVt0aGlzLkFSZWdpc3Rlcl1cbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzExMTAwMDEnOiAnIU0nLFxuICAgICAgICAnMTExMDAwMSc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB+dGhpcy5SQU1bdGhpcy5BUmVnaXN0ZXJdXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcxMTEwMDExJzogJy1NJyxcbiAgICAgICAgJzExMTAwMTEnOiAoKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gLXRoaXMuUkFNW3RoaXMuQVJlZ2lzdGVyXVxuICAgICAgICB9LFxuICAgICAgICAvLyAnMTExMDExMSc6ICdNKzEnLFxuICAgICAgICAnMTExMDExMSc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLlJBTVt0aGlzLkFSZWdpc3Rlcl0gKyAxXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcxMTEwMDEwJzogJ00tMScsXG4gICAgICAgICcxMTEwMDEwJzogKCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuUkFNW3RoaXMuQVJlZ2lzdGVyXSAtIDFcbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzEwMDAwMTAnOiAnRCtNJyxcbiAgICAgICAgJzEwMDAwMTAnOiAoKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5EUmVnaXN0ZXIgKyB0aGlzLlJBTVt0aGlzLkFSZWdpc3Rlcl1cbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzEwMTAwMTEnOiAnRC1NJyxcbiAgICAgICAgJzEwMTAwMTEnOiAoKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5EUmVnaXN0ZXIgLSB0aGlzLlJBTVt0aGlzLkFSZWdpc3Rlcl1cbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzEwMDAxMTEnOiAnTS1EJyxcbiAgICAgICAgJzEwMDAxMTEnOiAoKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5SQU1bdGhpcy5BUmVnaXN0ZXJdIC0gdGhpcy5EUmVnaXN0ZXJcbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzEwMDAwMDAnOiAnRCZNJyxcbiAgICAgICAgJzEwMDAwMDAnOiAoKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5EUmVnaXN0ZXIgJiB0aGlzLlJBTVt0aGlzLkFSZWdpc3Rlcl1cbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzEwMTAxMDEnOiAnRHxNJ1xuICAgICAgICAnMTAxMDEwMSc6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLkRSZWdpc3RlciB8IHRoaXMuUkFNW3RoaXMuQVJlZ2lzdGVyXVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gRGVzdGluYXRpb25cbiAgICB0aGlzLmRlc3QgPSB7XG4gICAgICAgIC8vICcwMDAnOiAnMCcsXG4gICAgICAgICcwMDAnOiAodmFsKSA9PiB7XG4gICAgICAgICAgICAvLyBEbyBub3RoaW5nXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcwMDEnOiAnTScsXG4gICAgICAgICcwMDEnOiAodmFsKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNldFJBTSh2YWwpXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcwMTAnOiAnRCcsXG4gICAgICAgICcwMTAnOiAodmFsKSA9PiB7XG4gICAgICAgICAgICB0aGlzLkRSZWdpc3RlciA9IHZhbFxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDExJzogJ01EJyxcbiAgICAgICAgJzAxMSc6ICh2YWwpID0+IHtcbiAgICAgICAgICAgIHRoaXMuRFJlZ2lzdGVyID0gdmFsXG4gICAgICAgICAgICB0aGlzLnNldFJBTSh2YWwpXG4gICAgICAgIH0sXG4gICAgICAgIC8vICcxMDAnOiAnQScsXG4gICAgICAgICcxMDAnOiAodmFsKSA9PiB7XG4gICAgICAgICAgICB0aGlzLkFSZWdpc3RlciA9IHZhbFxuICAgICAgICB9LFxuICAgICAgICAvLyAnMTAxJzogJ0FNJyxcbiAgICAgICAgJzEwMSc6ICh2YWwpID0+IHtcblxuICAgICAgICAgICAgdGhpcy5zZXRSQU0odmFsKVxuICAgICAgICAgICAgdGhpcy5BUmVnaXN0ZXIgPSB2YWxcbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzExMCc6ICdBRCcsXG4gICAgICAgICcxMTAnOiAodmFsKSA9PiB7XG5cbiAgICAgICAgICAgIHRoaXMuRFJlZ2lzdGVyID0gdmFsXG4gICAgICAgICAgICB0aGlzLkFSZWdpc3RlciA9IHZhbFxuICAgICAgICB9LFxuICAgICAgICAvLyAnMTExJzogJ0FNRCdcbiAgICAgICAgJzExMSc6ICh2YWwpID0+IHtcblxuICAgICAgICAgICAgdGhpcy5EUmVnaXN0ZXIgPSB2YWxcbiAgICAgICAgICAgIHRoaXMuc2V0UkFNKHZhbClcbiAgICAgICAgICAgIHRoaXMuQVJlZ2lzdGVyID0gdmFsXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLmp1bXAgPSB7XG4gICAgICAgIC8vICcwMDAnOiAnMCcsXG4gICAgICAgICcwMDAnOiAodmFsKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgfSxcbiAgICAgICAgLy8gJzAwMSc6ICdKR1QnLFxuICAgICAgICAnMDAxJzogKHZhbCkgPT4ge1xuICAgICAgICAgICAgaWYgKHZhbCA+IDApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDEwJzogJ0pFUScsXG4gICAgICAgICcwMTAnOiAodmFsKSA9PiB7XG4gICAgICAgICAgICBpZiAodmFsID09IDApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICAvLyAnMDExJzogJ0pHRScsXG4gICAgICAgICcwMTEnOiAodmFsKSA9PiB7XG4gICAgICAgICAgICBpZiAodmFsID49IDApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICAvLyAnMTAwJzogJ0pMVCcsXG4gICAgICAgICcxMDAnOiAodmFsKSA9PiB7XG4gICAgICAgICAgICBpZiAodmFsIDwgMCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIC8vICcxMDEnOiAnSk5FJyxcbiAgICAgICAgJzEwMSc6ICh2YWwpID0+IHtcbiAgICAgICAgICAgIGlmICh2YWwgIT0gMCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIC8vICcxMTAnOiAnSkxFJyxcbiAgICAgICAgJzExMCc6ICh2YWwpID0+IHtcbiAgICAgICAgICAgIGlmICh2YWwgPD0gMCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIC8vICcxMTEnOiAnSk1QJ1xuICAgICAgICAnMTExJzogKHZhbCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgICAgfVxuICAgIH1cblxuXG4gICAgLy8gU2NyZWVuXG4gICAgdGhpcy5TSVpFX0JJVFMgPSA1MTIgKiAyNTZcbiAgICB0aGlzLlNJWkVfV09SRFMgPSB0aGlzLlNJWkVfQklUUyAvIDE2XG4gICAgdGhpcy5TQ1JFRU5fUkFNID0gMTYzODRcblxuICAgIHRoaXMuQ0FOVkFTID0gbnVsbFxuICAgIHRoaXMuQ0FOVkFTX0NUWCA9IG51bGxcbiAgICB0aGlzLkNBTlZBU19EQVRBID0gbnVsbFxuXG4gICAgZnVuY3Rpb24gZ2V0QmluVmFsKGkpIHtcbiAgICAgICAgdmFyIGJpbiA9IGkudG9TdHJpbmcoMilcbiAgICAgICAgd2hpbGUgKGJpbi5sZW5ndGggPCAzMikge1xuICAgICAgICAgICAgYmluID0gXCIwXCIgKyBiaW5cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYmluXG4gICAgfVxuXG4gICAgLy8gVXNlZCBmb3IgdGhlIHNjcmVlblxuICAgIC8vIFNjcmVlbiBvcGVyYXRlcyBiaXQgcGF0dGVybnNcbiAgICAvLyAxIGlzIG9uIDAgaXMgb2ZmXG4gICAgZnVuY3Rpb24gZGVjMmJpbihkZWMpIHtcbiAgICAgICAgdmFyIGJpbiA9IChkZWMgPj4+IDApLnRvU3RyaW5nKDIpXG4gICAgICAgIHZhciBiaXQzMiA9IGdldEJpblZhbChiaW4pXG4gICAgICAgIHJldHVybiBiaXQzMi5zdWJzdHJpbmcoMTYsIDMyKVxuXG4gICAgfVxuXG4gICAgLy8gR2V0IFggYW5kIFkgcG9zaXRpb24gb2YgdGhlIHdvcmQgb24gdGhlIGltYWdlXG4gICAgLy8gQWNjb3JkaW5nIHRvIHRoZSBwb3NpdGlvbiBpbiBSQU1cbiAgICAvLyBSQU1bMTYzODQgKyByKjMyICsgYyUxNl1cbiAgICB0aGlzLmdldEltYWdlUm93Q29sdW1uID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgbnVtV29yZCA9IHRoaXMuQVJlZ2lzdGVyIC0gdGhpcy5TQ1JFRU5fUkFNXG5cbiAgICAgICAgdmFyIHkgPSBNYXRoLmZsb29yKG51bVdvcmQgKiAxNiAvIDUxMilcbiAgICAgICAgdmFyIHggPSBudW1Xb3JkICogMTYgJSA1MTJcblxuICAgICAgICB2YXIgeHkgPSB7IHg6IHgsIHk6IHkgfVxuXG4gICAgICAgIHJldHVybiB4eVxuICAgIH1cblxuICAgIC8vIERyYXcgcGl4ZWwgb24gY2FudmFzXG4gICAgdGhpcy5kcmF3UGl4ZWwgPSBmdW5jdGlvbiAoeCwgeSwgciwgZywgYiwgYSkge1xuXG4gICAgICAgIHZhciBpbmRleCA9ICh4ICsgeSAqIHRoaXMuQ0FOVkFTLndpZHRoKSAqIDQ7XG5cbiAgICAgICAgdGhpcy5DQU5WQVNfREFUQS5kYXRhW2luZGV4ICsgMF0gPSByO1xuICAgICAgICB0aGlzLkNBTlZBU19EQVRBLmRhdGFbaW5kZXggKyAxXSA9IGc7XG4gICAgICAgIHRoaXMuQ0FOVkFTX0RBVEEuZGF0YVtpbmRleCArIDJdID0gYjtcbiAgICAgICAgdGhpcy5DQU5WQVNfREFUQS5kYXRhW2luZGV4ICsgM10gPSBhO1xuICAgIH1cblxuICAgIHRoaXMudXBkYXRlSW1hZ2VEYXRhID0gZnVuY3Rpb24gKHZhbCkge1xuXG4gICAgICAgIC8vIGdldCBiaW4gdmFsXG4gICAgICAgIHZhciByb3dDb2x1bW4gPSB0aGlzLmdldEltYWdlUm93Q29sdW1uKClcbiAgICAgICAgdmFyIHggPSByb3dDb2x1bW4ueFxuICAgICAgICB2YXIgeSA9IHJvd0NvbHVtbi55XG5cbiAgICAgICAgdmFyIGJpblZhbCA9IGRlYzJiaW4odmFsKVxuICAgICAgICB2YXIgYmluQXJ5ID0gYmluVmFsLnNwbGl0KCcnKVxuXG4gICAgICAgIGJpbkFyeS5mb3JFYWNoKChlbGVtLCBpKSA9PiB7XG4gICAgICAgICAgICBpZiAoZWxlbSA9PSAxKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5kcmF3UGl4ZWwoeCArIDE2IC0gaSwgeSwgMCwgMCwgMCwgMjU1KVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmRyYXdQaXhlbCh4ICsgMTYgLSBpLCB5LCAyNTUsIDI1NSwgMjU1LCAwKVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIHRoaXMudXBkYXRlQ2FudmFzID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB0aGlzLkNBTlZBU19DVFgucHV0SW1hZ2VEYXRhKHRoaXMuQ0FOVkFTX0RBVEEsIDAsIDApO1xuICAgIH1cblxuICAgIC8vIEp1c3QgaW4gb3JkZXIgdG8gdHVybiBvZmYgc2NyZWVuXG4gICAgdGhpcy5zY3JlZW4gPSAxXG5cbiAgICAvLyBTZXQgc2NyZWVuIFJBTSBmb3IgZmFzdCBhY2Nlc3NcbiAgICAvLyBBcyBzY3JlZW4gaXMgdXBkYXRlZCBvZnRlblxuICAgIHRoaXMuc2V0UkFNID0gZnVuY3Rpb24gKHZhbCkge1xuICAgICAgICB0aGlzLlJBTVt0aGlzLkFSZWdpc3Rlcl0gPSB2YWxcbiAgICAgICAgaWYgKHRoaXMuQVJlZ2lzdGVyID49IHRoaXMuU0NSRUVOX1JBTSAmJiB0aGlzLkFSZWdpc3RlciA8IHRoaXMuS0JEKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5zY3JlZW4pIHtcbiAgICAgICAgICAgICAgICB0aGlzLnVwZGF0ZUltYWdlRGF0YSh2YWwpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBJbnN0cnVjdGlvbjogaXh4YWNjY2NjY2RkZGpqalxuICAgIC8vIEdldCBvcGNvZGVcbiAgICAvLyAwID0gQyBpbnN0cnVjdGlvbiBcbiAgICAvLyAxID0gQSBpbnN0cnVjdGlvblxuICAgIHRoaXMuZ2V0T3Bjb2RlID0gZnVuY3Rpb24gKGlucykge1xuICAgICAgICByZXR1cm4gaW5zLnN1YnN0cmluZygwLCAxKVxuICAgIH1cblxuICAgIHRoaXMuZ2V0Q29tcCA9IGZ1bmN0aW9uIChpbnMpIHtcbiAgICAgICAgcmV0dXJuIGlucy5zdWJzdHJpbmcoMywgMTApXG4gICAgfVxuXG4gICAgdGhpcy5nZXREZXN0ID0gZnVuY3Rpb24gKGlucykge1xuICAgICAgICByZXR1cm4gaW5zLnN1YnN0cmluZygxMCwgMTMpXG4gICAgfVxuXG4gICAgdGhpcy5nZXRKdW1wID0gZnVuY3Rpb24gKGlucykge1xuICAgICAgICByZXR1cm4gaW5zLnN1YnN0cmluZygxMywgMTYpXG4gICAgfVxuXG4gICAgdGhpcy5kZWJ1Z0N5Y2xlID0gZnVuY3Rpb24gKGlucykge1xuXG4gICAgICAgIGlmICghdGhpcy5kZWJ1Zykge1xuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgdmFyIG9wY29kZSA9IHRoaXMuZ2V0T3Bjb2RlKGlucylcbiAgICAgICAgY29uc29sZS5sb2coJ09wY29kZSAnLCBvcGNvZGUpXG4gICAgICAgIGlmIChvcGNvZGUgPT0gMCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ0F0ICcsIHBhcnNlSW50KGlucywgMikpXG4gICAgICAgICAgICBjb25zb2xlLmxvZygnQXQgKHZhbHVlKSAnLCB0aGlzLlJBTVtwYXJzZUludChpbnMsIDIpXSlcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnNvbGUubG9nKCdJbnMgJywgaW5zKVxuICAgICAgICBjb25zb2xlLmxvZygnUEMgJywgdGhpcy5QQylcbiAgICAgICAgY29uc29sZS5sb2coJ0FmdGVyIFBhcnNlJylcblxuICAgICAgICBjb25zb2xlLmxvZygnQUxVT3V0JywgdGhpcy5BTFVPdXQpXG4gICAgICAgIGNvbnNvbGUubG9nKCdBUmVnICcsIHRoaXMuQVJlZ2lzdGVyKVxuICAgICAgICBjb25zb2xlLmxvZygnRFJlZyAnLCB0aGlzLkRSZWdpc3RlcilcblxuICAgICAgICBjb25zb2xlLmxvZyh0aGlzLlJBTS5zbGljZSgwLCAxNikpXG4gICAgICAgIGNvbnNvbGUubG9nKCctLS0nKVxuICAgIH1cblxuICAgIHRoaXMuY3ljbGVzRG9uZSA9IDBcbiAgICB0aGlzLmRlYnVnID0gMFxuXG4gICAgdGhpcy5jeWNsZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdGhpcy5jdXJyZW50UEMgPSB0aGlzLlBDXG5cbiAgICAgICAgaWYgKHR5cGVvZiB0aGlzLlJPTVt0aGlzLlBDXSA9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICB2YXIgaW5zID0gdGhpcy5ST01bdGhpcy5QQ11cblxuICAgICAgICB2YXIgb3Bjb2RlID0gdGhpcy5nZXRPcGNvZGUoaW5zKVxuICAgICAgICBpZiAob3Bjb2RlID09IDEpIHtcbiAgICAgICAgICAgIHRoaXMuY3ljbGVDKGlucylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuY3ljbGVBKGlucylcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuY3ljbGVzRG9uZSsrXG4gICAgICAgIGlmICh0aGlzLmN5Y2xlc0RvbmUgJSAxMDAwMDAgPT0gMCkge1xuICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyh0aGlzLmN5Y2xlc0RvbmUpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLmN5Y2xlQyA9IGZ1bmN0aW9uIChpbnMpIHtcblxuICAgICAgICB2YXIgY29tcCA9IHRoaXMuZ2V0Q29tcChpbnMpXG4gICAgICAgIHZhciBkZXN0ID0gdGhpcy5nZXREZXN0KGlucylcbiAgICAgICAgdmFyIGp1bXAgPSB0aGlzLmdldEp1bXAoaW5zKVxuICAgICAgICB2YXIganVtcGVkID0gZmFsc2VcblxuICAgICAgICB2YXIgQUxVT3V0ID0gdGhpcy5jb21wW2NvbXBdKClcbiAgICAgICAgdGhpcy5BTFVPdXQgPSBBTFVPdXRcblxuICAgICAgICBpZiAoZGVzdCAhPSAnMDAwJykge1xuICAgICAgICAgICAgdGhpcy5kZXN0W2Rlc3RdKEFMVU91dClcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChqdW1wICE9ICcwMDAnKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5qdW1wW2p1bXBdKEFMVU91dCkpIHtcbiAgICAgICAgICAgICAgICB0aGlzLlBDID0gdGhpcy5BUmVnaXN0ZXJcbiAgICAgICAgICAgICAgICBqdW1wZWQgPSB0cnVlXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIWp1bXBlZCkge1xuICAgICAgICAgICAgdGhpcy5QQysrXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmRlYnVnQ3ljbGUoaW5zKVxuXG4gICAgfVxuXG4gICAgdGhpcy5jeWNsZUEgPSBmdW5jdGlvbiAoaW5zKSB7XG4gICAgICAgIHRoaXMuQVJlZ2lzdGVyID0gcGFyc2VJbnQoaW5zLCAyKVxuICAgICAgICB0aGlzLlBDKytcbiAgICAgICAgdGhpcy5kZWJ1Z0N5Y2xlKGlucylcbiAgICB9XG5cbiAgICB0aGlzLmxvYWRST00gPSBmdW5jdGlvbiAoc3RyKSB7XG4gICAgICAgIHZhciBhc3MgPSBuZXcgYXNzZW1ibGVyKHN0cilcbiAgICAgICAgdmFyIGJpbiA9IGFzcy5nZXRBc3NlbWJsZWRDb2RlKClcblxuICAgICAgICB2YXIgcHJvZ3JhbSA9IGJpbi5zcGxpdCgnXFxuJylcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHByb2dyYW0ubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHRoaXMuUk9NW2ldID0gcHJvZ3JhbVtpXTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBIYWNrXG4iLCJ2YXIgc3ltYm9sVGFibGUgPSB7XG4gICAgJ1IwJzogMCxcbiAgICAnUjEnOiAxLFxuICAgICdSMic6IDIsXG4gICAgJ1IzJzogMyxcbiAgICAnUjQnOiA0LFxuICAgICdSNSc6IDUsXG4gICAgJ1I2JzogNixcbiAgICAnUjcnOiA3LFxuICAgICdSOCc6IDgsXG4gICAgJ1I5JzogOSxcbiAgICAnUjEwJzogMTAsXG4gICAgJ1IxMSc6IDExLFxuICAgICdSMTInOiAxMixcbiAgICAnUjEzJzogMTMsXG4gICAgJ1IxNCc6IDE0LFxuICAgICdSMTUnOiAxNSxcbiAgICAnU0NSRUVOJzogMTYzODQsXG4gICAgJ0tCRCc6IDI0NTc2LFxuICAgICdTUCc6IDAsXG4gICAgJ0xDTCc6IDEsXG4gICAgJ0FSRyc6IDIsXG4gICAgJ1RISVMnOiAzLFxuICAgICdUSEFUJzogNFxufVxuXG5tb2R1bGUuZXhwb3J0cy5zeW1ib2xUYWJsZSA9IHN5bWJvbFRhYmxlXG5cbnZhciBkZXN0ID0ge1xuICAgICcwJzogICAgJzAwMCcsXG4gICAgJ00nOiAgICAnMDAxJyxcbiAgICAnRCc6ICAgICcwMTAnLFxuICAgICdNRCc6ICAgJzAxMScsXG4gICAgJ0EnOiAgICAnMTAwJyxcbiAgICAnQU0nOiAgICcxMDEnLFxuICAgICdBRCc6ICAgJzExMCcsXG4gICAgJ0FNRCc6ICAnMTExJ1xufVxuXG5tb2R1bGUuZXhwb3J0cy5kZXN0ID0gZGVzdFxuXG52YXIganVtcCA9IHtcbiAgICAnMCc6ICAgICcwMDAnLFxuICAgICdKR1QnOiAgJzAwMScsXG4gICAgJ0pFUSc6ICAnMDEwJyxcbiAgICAnSkdFJzogICcwMTEnLFxuICAgICdKTFQnOiAgJzEwMCcsXG4gICAgJ0pORSc6ICAnMTAxJyxcbiAgICAnSkxFJzogICcxMTAnLFxuICAgICdKTVAnOiAgJzExMSdcbn1cblxubW9kdWxlLmV4cG9ydHMuanVtcCA9IGp1bXBcblxudmFyIGNvbXAgPSB7XG4gICAgJzAnOiAgICAnMDEwMTAxMCcsXG4gICAgJzEnOiAgICAnMDExMTExMScsXG4gICAgJy0xJzogICAnMDExMTAxMCcsXG4gICAgJ0QnOiAgICAnMDAwMTEwMCcsXG4gICAgJ0EnOiAgICAnMDExMDAwMCcsXG4gICAgJyFEJzogICAnMDAwMTEwMScsXG4gICAgJyFBJzogICAnMDExMDAwMScsXG4gICAgJy1EJzogICAnMDAwMTExMScsXG4gICAgJy1BJzogICAnMDExMDAxMScsXG4gICAgJ0QrMSc6ICAnMDAxMTExMScsXG4gICAgJ0ErMSc6ICAnMDExMDExMScsXG4gICAgJ0QtMSc6ICAnMDAwMTExMCcsXG4gICAgJ0EtMSc6ICAnMDExMDAxMCcsXG4gICAgJ0QrQSc6ICAnMDAwMDAxMCcsXG4gICAgJ0QtQSc6ICAnMDAxMDAxMScsXG4gICAgJ0EtRCc6ICAnMDAwMDExMScsXG4gICAgJ0QmQSc6ICAnMDAwMDAwMCcsXG4gICAgJ0R8QSc6ICAnMDAxMDEwMScsXG4gICAgJ00nOiAgICAnMTExMDAwMCcsXG4gICAgJyFNJzogICAnMTExMDAwMScsXG4gICAgJy1NJzogICAnMTExMDAxMScsXG4gICAgJ00rMSc6ICAnMTExMDExMScsXG4gICAgJ00tMSc6ICAnMTExMDAxMCcsXG4gICAgJ0QrTSc6ICAnMTAwMDAxMCcsXG4gICAgJ0QtTSc6ICAnMTAxMDAxMScsXG4gICAgJ00tRCc6ICAnMTAwMDExMScsXG4gICAgJ0QmTSc6ICAnMTAwMDAwMCcsXG4gICAgJ0R8TSc6ICAnMTAxMDEwMSdcbn1cblxubW9kdWxlLmV4cG9ydHMuY29tcCA9IGNvbXAiLCJ2YXIge3N5bWJvbFRhYmxlLCBkZXN0LCBqdW1wLCBjb21wfSA9IHJlcXVpcmUoJy4vY29uc3RhbnRzJylcbnZhciByZW1vdmVDb21tZW50cyA9IHJlcXVpcmUoJ3JlbW92ZS1jb21tZW50cy1yZWdleCcpXG5cbmZ1bmN0aW9uIGFzc2VtYmxlcihzdHIpIHtcblxuICAgIC8vIFRyaW0gY29tbWVudHNcbiAgICB2YXIgc3RyID0gcmVtb3ZlQ29tbWVudHMoc3RyKVxuXG4gICAgLy8gU3BsaXQgY29kZSBhcnJheVxuICAgIHZhciBjb2RlQXJ5ID0gc3RyLnNwbGl0KCdcXG4nKVxuICAgIHZhciBjb2RlID0ge31cblxuICAgIC8vIE1vdmUgdG8gb2JqZWN0ICB3aXRoIHRyaW1tZWQgdmFsdWVzXG4gICAgY29kZUFyeS5mb3JFYWNoKChlbGVtZW50LCBpKSA9PiB7XG4gICAgICAgIHZhciBsaW5lID0gZWxlbWVudC50cmltKClcbiAgICAgICAgaWYgKGxpbmUpIHtcbiAgICAgICAgICAgIGNvZGVbaV0gPSBlbGVtZW50LnRyaW0oKVxuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICB2YXIgZmluYWwgPSB7fVxuICAgIHZhciBsYWJlbHMgPSB7fVxuICAgIHZhciBpID0gMDtcbiAgICBcbiAgICAvLyBHZXQgTE9PUFMsIGUuZy4gKExPT1ApIGFuZCBsaW5lIG51bWJlclxuICAgIHZhciByZWdFeHAgPSAvXFwoKFteKV0rKVxcKS9cblxuICAgIGZvciAoa2V5IGluIGNvZGUpIHtcbiAgICAgICAgdmFyIG1hdGNoZXMgPSByZWdFeHAuZXhlYyhjb2RlW2tleV0pO1xuICAgICAgICBpZiAoIW1hdGNoZXMpIHtcbiAgICAgICAgICAgIGZpbmFsW2krK10gPSBjb2RlW2tleV07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsYWJlbHNbbWF0Y2hlc1sxXV0gPSBwYXJzZUludChpKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gTW92ZSBsYWJlbHMgYW5kIGNvZGUgdG8gdGhpc1xuICAgIHRoaXMubGFiZWxzID0gbGFiZWxzXG4gICAgdGhpcy5jb2RlID0gZmluYWxcbiAgIFxuICAgIC8vIFN1c3RpdHV0ZSBsYWJlbHMgd2l0aCBsaW5lIG51bWJlclxuICAgIHRoaXMuc3ViTGFiZWwgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGZvciAoa2V5IGluIHRoaXMuY29kZSkge1xuICAgICAgICAgICAgbGV0IGxpbmUgPSB0aGlzLmNvZGVba2V5XVxuICAgICAgICAgICAgbGluZSA9IGxpbmUucmVwbGFjZSgnQCcsICcnKVxuICAgICAgICAgICAgaWYgKHRoaXMubGFiZWxzLmhhc093blByb3BlcnR5KGxpbmUpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jb2RlW2tleV0gPSAnQCcgKyB0aGlzLmxhYmVsc1tsaW5lXVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gTWVtb3J5IHN0YXJ0IGFmdGVyIGxhc3QgcmVnaXN0ZXJcbiAgICB0aGlzLmN1cnJlbnRNID0gMTZcblxuICAgIC8vIEFkZCBvdGhlciBzeW1ib2xzIHRvIHN5bWJvbCB0YWJsZVxuICAgIHRoaXMuYWRkU3ltYm9sc1RvdGFibGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGZvciAoa2V5IGluIHRoaXMuY29kZSkge1xuXG4gICAgICAgICAgICBpZiAodGhpcy5nZXRPcGNvZGUodGhpcy5jb2RlW2tleV0pID09PSAwKSB7XG4gICAgICAgICAgICAgICAgbGV0IGxpbmUgPSB0aGlzLmNvZGVba2V5XVxuICAgICAgICAgICAgICAgIGxpbmUgPSBsaW5lLnJlcGxhY2UoJ0AnLCAnJylcblxuICAgICAgICAgICAgICAgIGlmICghc3ltYm9sVGFibGUuaGFzT3duUHJvcGVydHkobGluZSkgJiYgaXNOYU4obGluZSkpIHtcbiAgICAgICAgICAgICAgICAgICAgc3ltYm9sVGFibGVbbGluZV0gPSB0aGlzLmN1cnJlbnRNXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY3VycmVudE0rK1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIFN1YnN0aXR1dGUgYWxsIHN5bWJvbHNcbiAgICB0aGlzLnN1YlN5bWJvbHMgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGZvciAoa2V5IGluIHRoaXMuY29kZSkge1xuICAgICAgICAgICAgbGV0IGxpbmUgPSB0aGlzLmNvZGVba2V5XVxuICAgICAgICAgICAgbGluZSA9IGxpbmUucmVwbGFjZSgnQCcsICcnKVxuICAgICAgICAgICAgaWYgKHN5bWJvbFRhYmxlLmhhc093blByb3BlcnR5KGxpbmUpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jb2RlW2tleV0gPSAnQCcgKyBzeW1ib2xUYWJsZVtsaW5lXVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gR2V0IG9wY29kZVxuICAgIHRoaXMuZ2V0T3Bjb2RlID0gZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgaWYgKGxpbmUuc3RhcnRzV2l0aCgnQCcpKSB7XG4gICAgICAgICAgICByZXR1cm4gMFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiAxXG4gICAgfVxuXG4gICAgLy8gUGFyc2UgQyBvcGNvZGVcbiAgICAvLyBpeHhhY2NjY2NjZGRkampqXG4gICAgdGhpcy5wYXJzZU9wY29kZUMgPSBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICBcbiAgICAgICAgdmFyIHBhcnRzID0ge31cblxuICAgICAgICAvLyBBc3NpZ25tZW50XG4gICAgICAgIHZhciBhcnkgPSBsaW5lLnNwbGl0KCc9JylcbiAgICAgICAgaWYgKGFyeS5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICBwYXJ0c1snZCddID0gZGVzdFthcnlbMF1dLnRvU3RyaW5nKClcbiAgICAgICAgICAgIHBhcnRzWydjJ10gPSBjb21wW2FyeVsxXV0udG9TdHJpbmcoKVxuICAgICAgICAgICAgcGFydHNbJ2onXSA9ICcwMDAnXG4gICAgICAgIH0gXG5cbiAgICAgICAgdmFyIGFyeSA9IGxpbmUuc3BsaXQoJzsnKVxuICAgICAgICBpZiAoYXJ5Lmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgIHBhcnRzWydjJ10gPSBjb21wW2FyeVswXV0udG9TdHJpbmcoKVxuICAgICAgICAgICAgcGFydHNbJ2QnXSA9ICcwMDAnIC8vZGVzdFthcnlbMF1dLnRvU3RyaW5nKClcbiAgICAgICAgICAgIHBhcnRzWydqJ10gPSBqdW1wW2FyeVsxXV0udG9TdHJpbmcoKVxuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGluc3RydWN0aW9uID0gJzExMScgKyBwYXJ0c1snYyddICsgcGFydHNbJ2QnXSArIHBhcnRzWydqJ11cbiAgICAgICAgcmV0dXJuIGluc3RydWN0aW9uXG5cbiAgICB9XG5cbiAgICAvLyBQYXJzZSBBIG9wY29kZVxuICAgIHRoaXMucGFyc2VPcGNvZGVBID0gZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgbGluZSA9IGxpbmUucmVwbGFjZSgnQCcsICcnKVxuXG4gICAgICAgIHZhciBpID0gcGFyc2VJbnQobGluZSlcbiAgICAgICAgdmFyIGJpbiA9IGkudG9TdHJpbmcoMilcblxuICAgICAgICB3aGlsZShiaW4ubGVuZ3RoIDwgMTYpIHtcbiAgICAgICAgICAgIGJpbiA9IFwiMFwiICsgYmluXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGJpblxuICAgIH1cblxuICAgIC8vIEFzc2VtYmxlXG4gICAgdGhpcy5nZXRBc3NlbWJsZWRDb2RlID0gZnVuY3Rpb24gKCkge1xuXG4gICAgICAgIHRoaXMuc3ViTGFiZWwoKVxuICAgICAgICB0aGlzLmFkZFN5bWJvbHNUb3RhYmxlKClcbiAgICAgICAgdGhpcy5zdWJTeW1ib2xzKClcblxuICAgICAgICB2YXIgaW5zdHJ1Y3Rpb25zID0gJydcbiAgICAgICAgZm9yIChrZXkgaW4gdGhpcy5jb2RlKSB7XG4gICAgICAgICAgICBsZXQgb3Bjb2RlID0gdGhpcy5nZXRPcGNvZGUodGhpcy5jb2RlW2tleV0pXG4gICAgICAgICAgICBpZiAob3Bjb2RlID09IDEpIHtcbiAgICAgICAgICAgICAgICBpbnN0cnVjdGlvbnMgKz10aGlzLnBhcnNlT3Bjb2RlQyh0aGlzLmNvZGVba2V5XSkgKyAnXFxuJ1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpbnN0cnVjdGlvbnMgKz0gdGhpcy5wYXJzZU9wY29kZUEodGhpcy5jb2RlW2tleV0pICsgJ1xcbidcbiAgICAgICAgICAgIH0gICAgXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGluc3RydWN0aW9ucy50cmltKClcbiAgICAgICAgXG4gICAgfVxuXG4gICAgdGhpcy5hc3NlbWJsZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIGluc3RydWN0aW9ucyA9IHRoaXMuZ2V0QXNzZW1ibGVkQ29kZSgpXG4gICAgICAgIGNvbnNvbGUubG9nKGluc3RydWN0aW9ucylcbiAgICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYXNzZW1ibGVyXG4iLCJmdW5jdGlvbiB0cmltQ29tbWVudHMoc3RyKSB7XG4gICAgXG4gICAgdmFyIHVuY29tbWVudGVkID0gc3RyLnJlcGxhY2UoL1xcL1xcKltcXHNcXFNdKj9cXCpcXC98KFteXFxcXDpdfF4pXFwvXFwvLiokL2dtLCAnJDEnKVxuICAgIC8vIFRyaW0gdG9wIGFuZCBib3R0b21cbiAgICByZXR1cm4gdW5jb21tZW50ZWQucmVwbGFjZSgvXlxccyt8XFxzKyQvZywgJycpXG59XG5cbm1vZHVsZS5leHBvcnRzID0gdHJpbUNvbW1lbnRzXG4iLCJ2YXIgSGFjayA9IHJlcXVpcmUoJy4vaW5kZXgnKVxuXG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuXG4gICAgLy8gSGFjayBzcGVjaWFsIGtleXMgY29tcGFyZWQgdG8gYXNjaWlcbiAgICB2YXIga2V5cyA9IHtcbiAgICAgICAgMTM6IDEyOCxcbiAgICAgICAgODogMTI5LFxuICAgICAgICAzNzogMTMwLFxuICAgICAgICAzODogMTMxLFxuICAgICAgICAzOTogMTMyLFxuICAgICAgICA0MDogMTMzLFxuICAgICAgICAzNjogMTM0LFxuICAgICAgICAzNTogMTM1LFxuICAgICAgICAzMzogMTM2LFxuICAgICAgICAyNDogMTM3LFxuICAgICAgICA0NTogMTM4LFxuICAgICAgICA0NjogMTM5LFxuICAgICAgICAyNzogMTQwLFxuICAgICAgICAxMTI6IDE0MSxcbiAgICAgICAgMTEzOiAxNDIsXG4gICAgICAgIDExNDogMTQzLFxuICAgICAgICAxMTU6IDE0NCxcbiAgICAgICAgMTE2OiAxNDUsXG4gICAgICAgIDExNzogMTQ2LFxuICAgICAgICAxMTg6IDE0NyxcbiAgICAgICAgMTE5OiAxNDgsXG4gICAgICAgIDEyMDogMTQ5LFxuICAgICAgICAxMjE6IDE1MCxcbiAgICAgICAgMTIyOiAxNTEsXG4gICAgICAgIDEyMzogMTUyXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaGFuZGtlS2V5RG93bihlKSB7XG4gICAgICAgIHZhciBrZXlDb2RlID0gcGFyc2VJbnQoZS5rZXlDb2RlKVxuICAgICAgICBpZiAoa2V5Q29kZSBpbiBrZXlzKSB7XG4gICAgICAgICAgICBrZXlDb2RlID0ga2V5c1trZXlDb2RlXVxuICAgICAgICB9XG4gICAgICAgIGhhY2suUkFNWzI0NTc2XSA9IGtleUNvZGVcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBoYW5ka2VLZXlVcCgpIHtcbiAgICAgICAgaGFjay5SQU1bMjQ1NzZdID0gMFxuICAgIH1cblxuICAgIHZhciBoYWNrXG4gICAgdmFyIGludGVydmFsSURcbiAgICB2YXIgcnVubmluZyA9IGZhbHNlXG4gICAgdmFyIG9wY29kZXMgPSBwYXJzZUludChkb2N1bWVudC5nZXRFbGV